//#####################################################################
// Copyright 2004-2009, Kevin Der, Ronald Fedkiw, Geoffrey Irving, Frank Losasso, Craig Schroeder, Andrew Selle, Tamar Shinar, Eftychios Sifakis, Jerry Talton, Wen Zheng.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class ARRAY_BASE_DEFINITIONS
//#####################################################################
#ifndef __ARRAY_BASE_DEFINITIONS__
#define __ARRAY_BASE_DEFINITIONS__

#include <Core/Arrays/ARRAY_BASE.h>
#include <Core/Arrays/ARRAYS_FORWARD.h>
#include <Core/Data_Structures/ELEMENT_ID.h>
#include <Core/Data_Structures/HASH_REDUCE.h>
#include <Core/Data_Structures/TUPLE.h>
#include <Core/Log/DEBUG_UTILITIES.h>
#include <Core/Math_Tools/exchange.h>
#include <Core/Math_Tools/minmax.h>
#include <Core/Math_Tools/sign.h>
#include <Core/Math_Tools/sqr.h>
#include <Core/Utilities/TYPE_UTILITIES.h>
#include <Core/Vectors/ARITHMETIC_POLICY.h>
#include <Core/Vectors/Dot_Product.h>
#include <Core/Vectors/SCALAR_POLICY.h>
#include <algorithm>
#include <cassert>
#include <iostream>
namespace PhysBAM{

#define RAW_FOREACH(static_n_,expr_,a_,b_,c_,d_,e_)                     \
    int ign0,ign1,ign2,ign3;(void)ign0;(void)ign1;(void)ign2;(void)ign3; \
    if constexpr(static_n_>=0 && static_n_<=size_limit)                 \
    {                                                                   \
        []<int...i>(auto&a_,auto&b_,auto&c_,auto&d_,auto&e_,std::integer_sequence<int,i...>) \
            {((expr_),...);}(a_,b_,c_,d_,e_,std::make_integer_sequence<int,static_n_>{}); \
    }                                                                   \
    else{for(int i=0,m_=Value(a_.Size());i<m_;i++){expr_;}}

#define ARRAY_FOREACH_SKIP(static_n_,expr0_,expr_,a_,b_,c_)             \
    auto& self=Derived();int ign0,ign1,ign2;(void)ign0;(void)ign1;(void)ign2; \
    if constexpr(static_n_==0){expr0_;}                                 \
    else if constexpr(static_n_>0 && static_n_<=size_limit+1)           \
    {                                                                   \
        []<int...i>(auto&self,auto&a_,auto&b_,auto&c_,std::integer_sequence<int,i...>) \
            {((expr_),...);}(self,a_,b_,c_,std::make_integer_sequence<int,static_n_-1>{}); \
    }                                                                   \
    else                                                                \
    {                                                                   \
    int m_=Value(self.Size());                                          \
    if(m_==0){expr0_;}                                                  \
    else for(int i=0;i<m_-1;i++){expr_;}                                \
}

#define RAW_COMBINE(static_n_,op_,init_,expr_,a_,b_,c_,d_,e_)           \
    int ign0,ign1,ign2,ign3;(void)ign0;(void)ign1;(void)ign2;(void)ign3; \
    if constexpr(static_n_==0){return (init_);}                         \
    if constexpr(static_n_>0 && static_n_<=size_limit)                 \
    {                                                                   \
        return []<int...i>(auto&a_,auto&b_,auto&c_,auto&d_,auto&e_,std::integer_sequence<int,i...>) \
            {return (... op_ (expr_));}(a_,b_,c_,d_,e_,std::make_integer_sequence<int,static_n_>{}); \
    }                                                                   \
    else{auto z_(init_);for(int i=0,m_=Value(a_.Size());i<m_;i++){z_=z_ op_ (expr_);}return z_;}

#define ARRAY_FOREACH4(static_n_,expr_,a_,b_,c_,d_) auto& self=Derived();RAW_FOREACH(static_n_,expr_,self,a_,b_,c_,d_)
#define ARRAY_FOREACH2(static_n_,expr_,a_,b_) ARRAY_FOREACH4(static_n_,expr_,a_,b_,ign2,ign3)
#define ARRAY_COMBINE4(static_n_,op_,init_,expr_,a_,b_,c_,d_) auto& self=Derived();RAW_COMBINE(static_n_,op_,init_,expr_,self,a_,b_,c_,d_)
#define ARRAY_COMBINE2(static_n_,op_,init_,expr_,a_,b_) ARRAY_COMBINE4(static_n_,op_,init_,expr_,a_,b_,ign2,ign3)

template<class T,class T_ARRAY,class ID> template<class A> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator=(const A& source)
{
    static_assert(CAN_ASSIGN<T,typename A::ELEMENT>::value);
    T_ARRAY& self=Derived();
    ID m=self.Size();
    assert(m==source.Size());
    if(!T_ARRAY::Same_Array(self,source)) for(ID i(0);i<m;i++) self(i)=source(i);
    return self;
}
template<class T,class T_ARRAY,class ID> template<class A> void
ARRAY_BASE<T,T_ARRAY,ID>::
Assert_Same_Size(const A&v) const
{
    typedef std::remove_reference_t<decltype(v.Derived())> B;
    if constexpr(T_ARRAY::fixed_m>=0 && B::fixed_m>=0)
    {
        static_assert(T_ARRAY::fixed_m==B::fixed_m);
    }
    else{assert(Size()==v.Size());}
}
template<class T,class T_ARRAY,class ID> template<class T_INDICES> INDIRECT_ARRAY<T_ARRAY,T_INDICES&>
ARRAY_BASE<T,T_ARRAY,ID>::
Subset(const T_INDICES& indices)
{
    return INDIRECT_ARRAY<T_ARRAY,T_INDICES&>(Derived(),indices);
}
template<class T,class T_ARRAY,class ID> template<class T_INDICES> INDIRECT_ARRAY<const T_ARRAY,T_INDICES&>
ARRAY_BASE<T,T_ARRAY,ID>::
Subset(const T_INDICES& indices) const
{
    return INDIRECT_ARRAY<const T_ARRAY,T_INDICES&>(Derived(),indices);
}
template<class T,class T_ARRAY,class ID> ARRAY_VIEW<T>
ARRAY_BASE<T,T_ARRAY,ID>::
Prefix(const ID prefix_size)
{
    assert(prefix_size<=Derived().Size());
    return Array_View(ID(),prefix_size);
}
template<class T,class T_ARRAY,class ID> ARRAY_VIEW<const T>
ARRAY_BASE<T,T_ARRAY,ID>::
Prefix(const ID prefix_size) const
{
    assert(prefix_size<=Derived().Size());
    return Array_View(ID(),prefix_size);
}
template<class T,class T_ARRAY,class ID> template<class A,class I> void
ARRAY_BASE<T,T_ARRAY,ID>::
Copy(const ARRAY_BASE<T,A,I>& a,FORCE_IDX)
{
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,self(ID(i))=a(I(i)),a,ign0);
}
template<class T,class T_ARRAY,class ID> template<class A,class I> void
ARRAY_BASE<T,T_ARRAY,ID>::
Copy(const ARRAY_BASE<T,A,I>& a,CAST_DATA)
{
    Copy(a,cast_data,force_idx);
}
template<class T,class T_ARRAY,class ID> template<class A,class I> void
ARRAY_BASE<T,T_ARRAY,ID>::
Copy(const ARRAY_BASE<T,A,I>& a,CAST_DATA,FORCE_IDX)
{
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,self(ID(i))=T(a(I(i))),a,ign0);
}
template<class T,class T_ARRAY,class ID> template<class A,class I> void
ARRAY_BASE<T,T_ARRAY,ID>::
Copy(const ARRAY_BASE<T,A,I>& a,FORCE_IDX,CAST_DATA)
{
    Copy(a,cast_data,force_idx);
}
template<class T,class T_ARRAY,class ID> bool
ARRAY_BASE<T,T_ARRAY,ID>::
Valid_Index(const ID i) const
{
    return (unsigned)Value(i)<Value(Size());
}
template<class T,class T_ARRAY,class ID> ARRAY_VIEW<typename ELEMENT_OF_VECTOR<T>::TYPE>
ARRAY_BASE<T,T_ARRAY,ID>::
Flattened() // valid only for contiguous arrays of VECTOR<T,d>
{
    T_ARRAY& self=Derived();
    return ARRAY_VIEW<typename T::ELEMENT>(self.Get_Array_Pointer()->begin(),T::m*Value(self.Size()));
}
template<class T,class T_ARRAY,class ID> ARRAY_VIEW<const typename ELEMENT_OF_VECTOR<T>::TYPE>
ARRAY_BASE<T,T_ARRAY,ID>::
Flattened() const // valid only for contiguous arrays of VECTOR<T,d>
{
    const T_ARRAY& self=Derived();
    return ARRAY_VIEW<const typename T::ELEMENT>(self.Get_Array_Pointer()->begin(),T::m*Value(self.Size()));
}
template<class T,class T_ARRAY,class ID> template<class ID1> ARRAY_VIEW<const T,ID1>
ARRAY_BASE<T,T_ARRAY,ID>::
Array_View(const ID first,const ID1 length) const
{
    const T_ARRAY& self=Derived();
    assert((unsigned)(Value(first)+Value(length))<=(unsigned)Value(self.Size()) && Value(length)>=0);
    return ARRAY_VIEW<const T,ID1>(self.Get_Array_Pointer()+Value(first),length);
}
template<class T,class T_ARRAY,class ID> template<class ID1> ARRAY_VIEW<T,ID1>
ARRAY_BASE<T,T_ARRAY,ID>::
Array_View(const ID first,const ID1 length)
{
    T_ARRAY& self=Derived();
    assert((unsigned)(Value(first)+Value(length))<=(unsigned)Value(self.Size()) && Value(length)>=0);
    return ARRAY_VIEW<T,ID1>((T*)self.Get_Array_Pointer()+Value(first),length);
}
template<class T,class T_ARRAY,class ID> ARRAY_VIEW<const T,ID>
ARRAY_BASE<T,T_ARRAY,ID>::
Array_View(INTERVAL<ID> I) const
{
    return Array_View(I.min_corner,I.max_corner-I.min_corner);
}
template<class T,class T_ARRAY,class ID> ARRAY_VIEW<T,ID>
ARRAY_BASE<T,T_ARRAY,ID>::
Array_View(INTERVAL<ID> I)
{
    return Array_View(I.min_corner,I.max_corner-I.min_corner);
}
template<class T,class T_ARRAY,class ID> template<class A,class O>
std::enable_if_t<((O::fixed_m>=0||A::fixed_m>=0)&&compat(O::fixed_m,A::fixed_m)),bool>
ARRAY_BASE<T,T_ARRAY,ID>::
operator==(const ARRAY_BASE<T,A,ID>& v) const
{
    const T_ARRAY& self=Derived();
    auto&w=v.Derived();
    constexpr int mm=std::max(T_ARRAY::fixed_m,A::fixed_m);
    if constexpr(T_ARRAY::fixed_m<0)
    {
        if(Value(self.Size())!=mm) return false;
    }
    else if constexpr(A::fixed_m<0)
    {
        if(Value(w.Size())!=mm) return false;
    }
    if constexpr(mm<=size_limit)
    {
        return []<int...i>(auto&self,auto&w,std::integer_sequence<int,i...>)
            {return (... && (self(ID(i))==w(ID(i))));
            }(self,w,std::make_integer_sequence<int,mm>{});
    }
    else
    {
        for(ID i(0),m=self.Size();i<m;i++) if(self(i)!=w(i)) return false;
        return true;
    }
}
template<class T,class T_ARRAY,class ID> template<class A,class O> std::enable_if_t<(O::fixed_m<0&&A::fixed_m<0),bool>
ARRAY_BASE<T,T_ARRAY,ID>::
operator==(const ARRAY_BASE<T,A,ID>& v) const
{
    const T_ARRAY& self=Derived();
    auto&w=v.Derived();
    if(self.Size()!=w.Size()) return false;
    for(ID i(0),m=self.Size();i<m;i++) if(self(i)!=w(i)) return false;
    return true;
}
template<class T,class T_ARRAY,class ID> template<class A> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator+=(const ARRAY_BASE<T,A,ID>& v)
{
    assert(Size()==v.Size());
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,self(ID(i))+=v(ID(i)),v,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator+=(const S& a)
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,self(ID(i))+=a,a,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator+=(const T& a)
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,self(ID(i))+=a,a,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> const T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator+() const
{
    return Derived();
};
template<class T,class T_ARRAY,class ID> template<class A> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator-=(const ARRAY_BASE<T,A,ID>& v)
{
    assert(Size()==v.Size());
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,self(ID(i))-=v(ID(i)),v,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator-=(const S& a)
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,self(ID(i))-=a,a,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator-=(const T& a)
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,self(ID(i))-=a,a,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> template<class A> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator*=(const ARRAY_BASE<T,A,ID>& v)
{
    assert(Size()==v.Size());
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,self(ID(i))*=v(ID(i)),v,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator*=(const S& a)
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,self(ID(i))*=a,a,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator*=(const T& a)
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,self(ID(i))*=a,a,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> template<class A> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator/=(const ARRAY_BASE<T,A,ID>& v)
{
    assert(Size()==v.Size());
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,self(ID(i))/=v(ID(i)),v,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator/=(const S& a)
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,self(ID(i))/=a,a,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> T_ARRAY&
ARRAY_BASE<T,T_ARRAY,ID>::
operator/=(const T& a)
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,self(ID(i))/=a,a,ign0);
    return Derived();
}
template<class T,class T_ARRAY,class ID> template<class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator-() const
{
    R r((INITIAL_SIZE)Value(Size()));
    ARRAY_FOREACH2(T_ARRAY::fixed_m,r(ID(i))=-self(ID(i)),r,ign0);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class A,class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator+(const ARRAY_BASE<T,A,ID>& v) const
{
    assert(Size()==v.Size());
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,r(ID(i))=self(ID(i))+v(ID(i)),v,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator+(const S& a) const
{
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    ARRAY_FOREACH2(T_ARRAY::fixed_m,r(ID(i))=self(ID(i))+a,a,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator+(const T& a) const
{
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    ARRAY_FOREACH2(T_ARRAY::fixed_m,r(ID(i))=self(ID(i))+a,a,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class A,class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator-(const ARRAY_BASE<T,A,ID>& v) const
{
    assert(Size()==v.Size());
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,r(ID(i))=self(ID(i))-v(ID(i)),v,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator-(const S& a) const
{
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    ARRAY_FOREACH2(T_ARRAY::fixed_m,r(ID(i))=self(ID(i))-a,a,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator-(const T& a) const
{
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    ARRAY_FOREACH2(T_ARRAY::fixed_m,r(ID(i))=self(ID(i))-a,a,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class A,class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator*(const ARRAY_BASE<T,A,ID>& v) const
{
    assert(Size()==v.Size());
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,r(ID(i))=self(ID(i))*v(ID(i)),v,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator*(const S& a) const
{
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    ARRAY_FOREACH2(T_ARRAY::fixed_m,r(ID(i))=self(ID(i))*a,a,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator*(const T& a) const
{
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    ARRAY_FOREACH2(T_ARRAY::fixed_m,r(ID(i))=self(ID(i))*a,a,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class A,class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator/(const ARRAY_BASE<T,A,ID>& v) const
{
    assert(Size()==v.Size());
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,r(ID(i))=self(ID(i))/v(ID(i)),v,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator/(const S& a) const
{
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    ARRAY_FOREACH2(T_ARRAY::fixed_m,r(ID(i))=self(ID(i))/a,a,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
operator/(const T& a) const
{
    VEC_TYPE_NZ<T,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    ARRAY_FOREACH2(T_ARRAY::fixed_m,r(ID(i))=self(ID(i))/a,a,r);
    return r;
}
template<class T,class T_ARRAY,class ID> T&
ARRAY_BASE<T,T_ARRAY,ID>::
Last()
{
    T_ARRAY& self=Derived();
    return self(self.Size()-1);
}
template<class T,class T_ARRAY,class ID> const T&
ARRAY_BASE<T,T_ARRAY,ID>::
Last() const
{
    const T_ARRAY& self=Derived();
    return self(self.Size()-1);
}
template<class T,class T_ARRAY,class ID> ID
ARRAY_BASE<T,T_ARRAY,ID>::
Size() const
{
    return Derived().Size();
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Max() const
{
    T result=(*this)(ID(0));
    ARRAY_FOREACH_SKIP(T_ARRAY::fixed_m,{PHYSBAM_FATAL_ERROR();},(result=PhysBAM::max(result,self(ID(i+1)))),result,ign0,ign1);
    return result;
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Min() const
{
    T result=(*this)(ID(0));
    ARRAY_FOREACH_SKIP(T_ARRAY::fixed_m,{PHYSBAM_FATAL_ERROR();},(result=PhysBAM::min(result,self(ID(i+1)))),result,ign0,ign1);
    return result;
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Max_Abs() const
{
    T result{};
    ARRAY_FOREACH2(T_ARRAY::fixed_m,(result=PhysBAM::max(result,abs(self(ID(i))))),result,ign0);
    return result;
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Max_Mag() const
{
    T result{};
    ARRAY_FOREACH2(T_ARRAY::fixed_m,(result=PhysBAM::maxmag(result,self(ID(i)))),result,ign0);
    return result;
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Min_Mag() const
{
    T result=(*this)(ID(0));
    ARRAY_FOREACH_SKIP(T_ARRAY::fixed_m,{PHYSBAM_FATAL_ERROR();},(result=PhysBAM::minmag(result,self(ID(i+1)))),result,ign0,ign1);
    return result;
}
template<class T,class T_ARRAY,class ID> ID
ARRAY_BASE<T,T_ARRAY,ID>::
Arg_Max() const
{
    int best=0;
    ARRAY_FOREACH_SKIP(T_ARRAY::fixed_m,{PHYSBAM_FATAL_ERROR();},(self(ID(i+1))>self(ID(best))?(best=i+1):0),best,ign0,ign1);
    return ID(best);
}
template<class T,class T_ARRAY,class ID> ID
ARRAY_BASE<T,T_ARRAY,ID>::
Arg_Min() const
{
    int best=0;
    ARRAY_FOREACH_SKIP(T_ARRAY::fixed_m,{PHYSBAM_FATAL_ERROR();},(self(ID(i+1))<self(ID(best))?(best=i+1):0),best,ign0,ign1);
    return ID(best);
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Componentwise_Max_Abs() const
{
    T result{};
    ARRAY_FOREACH2(T_ARRAY::fixed_m,(result=result.Componentwise_Max(abs(self(ID(i))))),result,ign0);
    return result;
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Componentwise_Max() const
{
    T result=(*this)(ID(0));
    ARRAY_FOREACH_SKIP(T_ARRAY::fixed_m,{PHYSBAM_FATAL_ERROR();},(result=result.Componentwise_Max(self(ID(i+1)))),result,ign0,ign1);
    return result;
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Componentwise_Min() const
{
    T result=(*this)(ID(0));
    ARRAY_FOREACH_SKIP(T_ARRAY::fixed_m,{PHYSBAM_FATAL_ERROR();},(result=result.Componentwise_Min(self(ID(i+1)))),result,ign0,ign1);
    return result;
}
template<class T,class T_ARRAY,class ID> template<class A,class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
Componentwise_Min(const ARRAY_BASE<T,A,ID>& v) const
{
    VEC_TYPE_NZ<T,A::fixed_m,ID> r((INITIAL_SIZE)v.Size());
    ARRAY_FOREACH2(A::fixed_m,r(ID(i))=min(self(ID(i)),v(ID(i))),v,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class A,class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
Componentwise_Max(const ARRAY_BASE<T,A,ID>& v) const
{
    VEC_TYPE_NZ<T,A::fixed_m,ID> r((INITIAL_SIZE)v.Size());
    ARRAY_FOREACH2(A::fixed_m,r(ID(i))=max(self(ID(i)),v(ID(i))),v,r);
    return r;
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Sum() const
{
    ARRAY_COMBINE2(T_ARRAY::fixed_m,+,T{},self(ID(i)),ign0,ign1);
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Product() const
{
    ARRAY_COMBINE2(T_ARRAY::fixed_m,*,T{1},self(ID(i)),ign0,ign1);
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Sum_Abs() const
{
    ARRAY_COMBINE2(T_ARRAY::fixed_m,+,T{},abs(self(ID(i))),ign0,ign1);
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Average() const
{
    const T_ARRAY& self=Derived();
    return self.Size()?Sum()/self.Size():T();
}
template<class T,class T_ARRAY,class ID> template<class A> SCALAR_TYPE<T>
ARRAY_BASE<T,T_ARRAY,ID>::
Dot(const ARRAY_BASE<T,A,ID>& a) const
{
    assert(Size()==a.Size());
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_COMBINE2(n,+,SCALAR_TYPE<T>{},PhysBAM::Dot_Product(self(ID(i)),a(ID(i))),a,ign0);
}
template<class T,class T_ARRAY,class ID> template<class A> auto
ARRAY_BASE<T,T_ARRAY,ID>::
Dot_Double_Precision(const ARRAY_BASE<T,A,ID>& a) const -> D
{
    assert(Size()==a.Size());
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_COMBINE2(n,+,D{},PhysBAM::Dot_Product(self(ID(i)),a(ID(i))),a,ign0);
}
template<class T,class T_ARRAY,class ID> SCALAR_TYPE<T>
ARRAY_BASE<T,T_ARRAY,ID>::
Magnitude_Squared() const
{
    ARRAY_COMBINE2(T_ARRAY::fixed_m,+,SCALAR_TYPE<T>{},PhysBAM::Magnitude_Squared(self(ID(i))),ign0,ign1);
}
template<class T,class T_ARRAY,class ID> SCALAR_TYPE<T>
ARRAY_BASE<T,T_ARRAY,ID>::
Magnitude() const
{
    return sqrt(Magnitude_Squared());
}
template<class T,class T_ARRAY,class ID> T
ARRAY_BASE<T,T_ARRAY,ID>::
Normalize()
{
    T magnitude=Magnitude();
    if(magnitude) Derived()*=1/magnitude;
    else{Fill(0);
        (*this)(0)=(T)1;
    }return magnitude;
}
template<class T,class T_ARRAY,class ID> SCALAR_TYPE<T>
ARRAY_BASE<T,T_ARRAY,ID>::
Maximum_Magnitude() const
{
    if constexpr(is_same<SCALAR_TYPE<T>,T>::value) return Max_Abs();
    else
    {
        SCALAR_TYPE<T> result{};
        ARRAY_FOREACH2(T_ARRAY::fixed_m,(result=max(result,PhysBAM::Magnitude_Squared(self(ID(i))))),result,ign0);
        return sqrt(result);
    }
}
template<class T,class T_ARRAY,class ID> ID
ARRAY_BASE<T,T_ARRAY,ID>::
Arg_Maximum_Magnitude() const
{
    if constexpr(is_same<SCALAR_TYPE<T>,T>::value)
    {
        int best=0;
        auto best_val=abs((*this)(ID(0))),cur(best_val);
        ARRAY_FOREACH_SKIP(T_ARRAY::fixed_m,{return ID();},((cur=abs(self(ID(i+1))),cur>best_val)?(best_val=cur,best=i+1):0),best,best_val,cur);
        return ID(best);
    }
    else
    {
        int best=0;
        auto best_val=(*this)(ID(0)).Magnitude_Squared(),cur(best_val);
        ARRAY_FOREACH_SKIP(T_ARRAY::fixed_m,{return ID();},((cur=self(ID(i+1)).Magnitude_Squared(),cur>best_val)?(best_val=cur,best=i+1):0),best,best_val,cur);
        return ID(best);
    }
}
template<class T,class T_ARRAY,class ID> template<class A> auto
ARRAY_BASE<T,T_ARRAY,ID>::
Weighted_Sum(const A& weights) const -> ELEMENT
{
    static_assert_SAME(typename A::ELEMENT,SCALAR);
    assert(weights.Size()==Size());
    static_assert(compat(T_ARRAY::fixed_m,A::fixed_m));
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_COMBINE2(n,+,ELEMENT{},weights(ID(i))*self(ID(i)),weights,ign0);
}
template<class T,class T_ARRAY,class ID> T_ARRAY
ARRAY_BASE<T,T_ARRAY,ID>::
Householder_Vector(const int k) const
{
    T_ARRAY v((INITIAL_SIZE)Size());
    T v_dot_v=0;
    for(int i=k;i<Size();i++)
    {
        v(i)=(*this)(i);
        v_dot_v+=sqr(v(i));
    }
    if((*this)(k)>=0) v(k)+=sqrt(v_dot_v);
    else v(k)-=sqrt(v_dot_v);
    return v;
}
template<class T,class T_ARRAY,class ID> template<class A,class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
Householder_Transform(const ARRAY_BASE<T,A>& v) const
{
    T_ARRAY u((INITIAL_SIZE)Value(Size()));
    u.Copy(-2*Dot(v)/v.Dot(v),v.Derived(),Derived());
    return u;
}
template<class T,class T_ARRAY,class ID> void
ARRAY_BASE<T,T_ARRAY,ID>::
Givens_Rotate(const int i,const int j,const T c,const T s)
{
    if(i>j)
    {
        Givens_Rotate(j,i,c,-s);
        return;
    }
    assert(0<=i && i<j && j<Size());
    T u=(*this)(i),v=(*this)(j);
    (*this)(i)=c*u-s*v;
    (*this)(j)=s*u+c*v;
}
template<class T,class T_ARRAY,class ID> template<class A> T
ARRAY_BASE<T,T_ARRAY,ID>::
Angle_Between(const ARRAY_BASE<T,A>& v) const
{
    Assert_Same_Size(v);
    T u2=0,u1=(*this)(0),v1=v(0),uv=0;
    for(int i=1;i<Size();i++)
    {
        T ui=(*this)(i);
        u2+=sqr(ui);
        uv+=ui*v(i);
    }
    u1+=sign_nonzero(u1)*sqrt(u2+sqr(u1));
    T factor=2*(uv+u1*v1)/(u2+sqr(u1)),R01=v1-factor*u1,R11=0;
    for(int i=1;i<Size();i++)
        R11+=sqr(v(i)-factor*(*this)(i));
    return atan2(sqrt(R11),-R01*sign_nonzero(u1));
}
template<class T,class T_ARRAY,class ID> template<class A,class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
Componentwise_Greater_Equal(const ARRAY_BASE<T,A>& v) const
{
    Assert_Same_Size(v);
    VEC_TYPE_NZ<bool,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,(r(ID(i))=self(ID(i))>=v(ID(i))),v,r);
    return r;
}
template<class T,class T_ARRAY,class ID> template<class A,class O,class R> R
ARRAY_BASE<T,T_ARRAY,ID>::
Componentwise_And(const ARRAY_BASE<bool,A>& v) const
{
    Assert_Same_Size(v);
    VEC_TYPE_NZ<bool,T_ARRAY::fixed_m,ID> r((INITIAL_SIZE)Value(Size()));
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,(r(ID(i))=(self(ID(i))&&v(ID(i)))),v,r);
    return r;
}
template<class T,class T_ARRAY,class ID> void
ARRAY_BASE<T,T_ARRAY,ID>::
Reverse()
{
    std::reverse(begin(),end());
}
template<class T,class T_ARRAY,class ID> template<class A> bool
ARRAY_BASE<T,T_ARRAY,ID>::
All_Greater_Equal(const ARRAY_BASE<T,A,ID>& v) const
{
    Assert_Same_Size(v);
    const T_ARRAY& self=Derived();
    const A& vd=v.Derived();
    for(int i=0,n=self.Size();i<n;i++) if(self(i)<vd(i)) return false;
    return true;
}
template<class T,class T_ARRAY,class ID> template<class A> bool
ARRAY_BASE<T,T_ARRAY,ID>::
All_Less(const ARRAY_BASE<T,A,ID>& v) const
{
    Assert_Same_Size(v);
    const T_ARRAY& self=Derived();
    const A& vd=v.Derived();
    for(int i=0,n=self.Size();i<n;i++) if(!(self(i)<vd(i))) return false;
    return true;
}
template<class T,class T_ARRAY,class ID> template<class A> bool
ARRAY_BASE<T,T_ARRAY,ID>::
All_Less_Equal(const ARRAY_BASE<T,A,ID>& v) const
{
    return v.All_Greater_Equal(*this);
}
template<class T,class T_ARRAY,class ID> template<class A> bool
ARRAY_BASE<T,T_ARRAY,ID>::
All_Greater(const ARRAY_BASE<T,A,ID>& v) const
{
    return v.All_Less(*this);
}
template<class T,class T_ARRAY,class ID> ID
ARRAY_BASE<T,T_ARRAY,ID>::
Find(const T& element) const
{
    const T_ARRAY& self=Derived();
    ID m=self.Size();
    for(ID i(0);i<m;i++) if(self(i)==element) return i;
    return -1;
}
template<class T,class T_ARRAY,class ID> bool
ARRAY_BASE<T,T_ARRAY,ID>::
Find(const T& element,ID& index) const
{
    return Find(element,0,index);
}
template<class T,class T_ARRAY,class ID> bool
ARRAY_BASE<T,T_ARRAY,ID>::
Find(const T& element,const ID start_index,ID& index) const
{
    const T_ARRAY& self=Derived();
    ID m=self.Size();
    for(ID i=start_index;i<m;i++)
        if(self(i)==element){
            index=i;
            return true;
        }
    return false;
}
template<class T,class T_ARRAY,class ID> bool
ARRAY_BASE<T,T_ARRAY,ID>::
Contains(const T& element) const
{
    const T_ARRAY& self=Derived();
    ID m=self.Size();
    for(ID i(0);i<m;i++) if(self(i)==element) return true;
    return false;
}
template<class T,class T_ARRAY,class ID> bool
ARRAY_BASE<T,T_ARRAY,ID>::
Contains_Only(const T& element) const
{
    const T_ARRAY& self=Derived();
    ID m=self.Size();
    for(ID i(0);i<m;i++) if(self(i)!=element) return false;
    return true;
}
template<class T,class T_ARRAY,class ID> int
ARRAY_BASE<T,T_ARRAY,ID>::
Count_Matches(const T& value) const
{
    ARRAY_COMBINE2(T_ARRAY::fixed_m,+,0,(self(ID(i))==value),value,ign0);
}
template<class T,class T_ARRAY,class ID> int
ARRAY_BASE<T,T_ARRAY,ID>::
Number_True() const
{
    static_assert_SAME(T,bool);
    return Count_Matches(true);
}
template<class T,class T_ARRAY,class ID> int
ARRAY_BASE<T,T_ARRAY,ID>::
Number_False() const
{
    static_assert_SAME(T,bool);
    return Count_Matches(false);
}
template<class T,class T_ARRAY,class ID> template<class T_COMPARE> void
ARRAY_BASE<T,T_ARRAY,ID>::
Sort(const T_COMPARE comparison)
{
    std::sort(begin(),end(),comparison);
}
template<class T,class T_ARRAY,class ID> template<class T_COMPARE> void
ARRAY_BASE<T,T_ARRAY,ID>::
Stable_Sort(const T_COMPARE& comparison)
{
    std::stable_sort(begin(),end(),comparison);
}
template<class T,class T_ARRAY,class ID> void
ARRAY_BASE<T,T_ARRAY,ID>::
Sort()
{
    Sort(std::less<typename T_ARRAY::ELEMENT>());
}
template<class T,class T_ARRAY,class ID> void
ARRAY_BASE<T,T_ARRAY,ID>::
Stable_Sort()
{
    Stable_Sort(std::less<typename T_ARRAY::ELEMENT>());
}
template<class T,class T_ARRAY,class ID> void
ARRAY_BASE<T,T_ARRAY,ID>::
Fill(T value)
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,(self(ID(i))=value),value,ign0);
}
template<class T,class T_ARRAY,class ID> template<class A> void
ARRAY_BASE<T,T_ARRAY,ID>::
Copy(const A& v1)
{
    *this=v1;
}
template<class T,class T_ARRAY,class ID> template<class A> void
ARRAY_BASE<T,T_ARRAY,ID>::
Copy(const SCALAR c1,const A& v1)
{
    assert(Size()==v1.Size());
    constexpr int n=std::max(T_ARRAY::fixed_m,A::fixed_m);
    ARRAY_FOREACH2(n,(self(ID(i))=c1*v1(ID(i))),c1,v1);
}
template<class T,class T_ARRAY,class ID> template<class A,class B> void
ARRAY_BASE<T,T_ARRAY,ID>::
Copy(const SCALAR c1,const A& v1,const B& v2)
{
    assert(Size()==v1.Size());
    assert(Size()==v2.Size());
    constexpr int n=std::max(std::max(T_ARRAY::fixed_m,A::fixed_m),B::fixed_m);
    ARRAY_FOREACH4(n,(self(ID(i))=c1*v1(ID(i))+v2(ID(i))),c1,v1,v2,ign0);
}
template<class T,class T_ARRAY,class ID> template<class A,class B> void
ARRAY_BASE<T,T_ARRAY,ID>::
Copy(const SCALAR c1,const A& v1,const SCALAR c2,const B& v2)
{
    assert(Size()==v1.Size());
    assert(Size()==v2.Size());
    constexpr int n=std::max(std::max(T_ARRAY::fixed_m,A::fixed_m),B::fixed_m);
    ARRAY_FOREACH4(n,(self(ID(i))=c1*v1(ID(i))+c2*v2(ID(i))),c1,v1,c2,v2);
}
template<class T,class T_ARRAY,class ID> template<class A> void
ARRAY_BASE<T,T_ARRAY,ID>::
Remove_Sorted_Indices_Lazy(const A& index)
{
    static_assert_SAME(ID,typename A::ELEMENT);
    T_ARRAY& self=Derived();
    ID index_m=index.Size();
    if(index_m==0) return;
    ID curr=0;
    for(ID k=index_m-1;k>=ID(0);k--)if(index(k)!=curr){curr=index(k);
            self.Remove_Index_Lazy(curr);
        }
    self.Compact();
}
template<class T,class T_ARRAY,class ID> template<class A,class B> void
ARRAY_BASE<T,T_ARRAY,ID>::
Extract(ARRAY_BASE<T,A,ID>& a,ARRAY_BASE<T,B,ID>& b) const
{
    assert(a.Size()+b.Size()==Size());
    for(ID i(0);i<a.Size();i++) a(i)=(*this)(i);
    for(ID i(0);i<b.Size();i++) b(i)=(*this)(i+a.Size());
}
template<class T,class T_ARRAY,class ID> template<class A,class B,class C> void
ARRAY_BASE<T,T_ARRAY,ID>::
Extract(ARRAY_BASE<T,A,ID>& a,ARRAY_BASE<T,B,ID>& b,ARRAY_BASE<T,C,ID>& c) const
{
    assert(a.Size()+b.Size()+c.Size()==Size());
    for(ID i(0);i<a.Size();i++) a(i)=(*this)(i);
    for(ID i(0);i<b.Size();i++) b(i)=(*this)(i+a.Size());
    for(ID i(0);i<c.Size();i++) c(i)=(*this)(i+a.Size()+b.Size());
}
template<class T,class T_ARRAY,class ID> template<class A,class B> void
ARRAY_BASE<T,T_ARRAY,ID>::
Combine(const ARRAY_BASE<T,A,ID>& a,const ARRAY_BASE<T,B,ID>& b)
{
    assert(a.Size()+b.Size()==Size());
    for(ID i(0);i<a.Size();i++) (*this)(i)=a(i);
    for(ID i(0);i<b.Size();i++) (*this)(i+a.Size())=b(i);
}
template<class T,class T_ARRAY,class ID> template<class A,class B,class C> void
ARRAY_BASE<T,T_ARRAY,ID>::
Combine(const ARRAY_BASE<T,A,ID>& a,const ARRAY_BASE<T,B,ID>& b,const ARRAY_BASE<T,C,ID>& c)
{
    assert(a.Size()+b.Size()+c.Size()==Size());
    for(ID i(0);i<a.Size();i++) (*this)(i)=a(i);
    for(ID i(0);i<b.Size();i++) (*this)(i+a.Size())=b(i);
    for(ID i(0);i<c.Size();i++) (*this)(i+a.Size()+b.Size())=c(i);
}
template<class T,class T_ARRAY,class ID> ID
ARRAY_BASE<T,T_ARRAY,ID>::
Binary_Search(const T& value) const // lower_bound binary search
{
    return ID(std::lower_bound(begin(),end(),value)-begin());
}
template<class T,class T_ARRAY,class ID> template<class T_COMPARE> ID
ARRAY_BASE<T,T_ARRAY,ID>::
Binary_Search(const T& value,const T_COMPARE comparison) const // lower_bound binary search
{
    return ID(std::lower_bound(begin(),end(),value,comparison)-begin());
}
template<class T,class T_ARRAY,class ID> void
ARRAY_BASE<T,T_ARRAY,ID>::
Write_Raw(std::ostream& output) const
{
    ARRAY_FOREACH2(T_ARRAY::fixed_m,(output<<self(ID(i)),(ID(i)<self.Size()-1)?(output<<" ",0):0),output,ign0);
}
template<class T,class A,class ID> VEC_TYPE_NZ<T,A::fixed_m,ID>
operator+(const typename A::S& a,const ARRAY_BASE<T,A,ID>& v)
{
    return v+a;
}
template<class T,class A,class ID> VEC_TYPE_NZ<T,A::fixed_m,ID>
operator+(const ND<T>& a,const ARRAY_BASE<T,A,ID>& v)
{
    return v+a;
}
template<class T,class A,class ID> VEC_TYPE_NZ<T,A::fixed_m,ID>
operator-(const typename A::S& a,const ARRAY_BASE<T,A,ID>& v)
{
    VEC_TYPE_NZ<T,A::fixed_m,ID> r((INITIAL_SIZE)v.Size());
    RAW_FOREACH(A::fixed_m,r(ID(i))=a-v(ID(i)),v,a,r,ign0,ign1);
    return r;
}
template<class T,class A,class ID> VEC_TYPE_NZ<T,A::fixed_m,ID>
operator-(const ND<T>& a,const ARRAY_BASE<T,A,ID>& v)
{
    VEC_TYPE_NZ<T,A::fixed_m,ID> r((INITIAL_SIZE)v.Size());
    RAW_FOREACH(A::fixed_m,r(ID(i))=a-v(ID(i)),v,a,r,ign0,ign1);
    return r;
}
template<class T,class A,class ID> VEC_TYPE_NZ<T,A::fixed_m,ID>
operator*(const typename A::S& a,const ARRAY_BASE<T,A,ID>& v)
{
    return v*a;
}
template<class T,class A,class ID> VEC_TYPE_NZ<T,A::fixed_m,ID>
operator*(const ND<T>& a,const ARRAY_BASE<T,A,ID>& v)
{
    return v*a;
}
template<class T,class A,class ID> VEC_TYPE_NZ<T,A::fixed_m,ID>
operator/(const typename A::S& a,const ARRAY_BASE<T,A,ID>& v)
{
    VEC_TYPE_NZ<T,A::fixed_m,ID> r((INITIAL_SIZE)v.Size());
    RAW_FOREACH(A::fixed_m,r(ID(i))=a/v(ID(i)),v,a,r,ign0,ign1);
    return r;
}
template<class T,class A,class ID> VEC_TYPE_NZ<T,A::fixed_m,ID>
operator/(const ND<T>& a,const ARRAY_BASE<T,A,ID>& v)
{
    VEC_TYPE_NZ<T,A::fixed_m,ID> r((INITIAL_SIZE)v.Size());
    RAW_FOREACH(A::fixed_m,r(ID(i))=a/v(ID(i)),v,a,r,ign0,ign1);
    return r;
}
template<class T,class A,class ID> inline std::ostream&
operator<<(std::ostream& output,const ARRAY_BASE<T,A,ID>& a)
{
    output<<"(";
    a.Write_Raw(output);
    output<<")";
    return output;
}
template<class T,class A,class ID>
static int Hash_Reduce_Array_Helper(const ARRAY_BASE<T,A,ID>& array)
{
    ID i(0);
    int hash=Value(array.Size())%2==0?missing_element_hash:HASH_REDUCE<T>::H(array(i++));
    for(;i<array.Size()-1;i+=2)
        hash=int_hash(hash,HASH_REDUCE<T>::H(array(i)),HASH_REDUCE<T>::H(array(i+1)));
    return Value(array.Size())==1?int_hash(hash):hash;
}
template<class A> struct HASH_REDUCE<A,enable_if_t<(IS_ARRAY<A>::value && !FIXED_SIZE_VECTOR<A>::value)>>
{
    static int H(const A& key)
    {
        return Hash_Reduce_Array_Helper(key);
    }
};
}

#undef RAW_FOREACH
#undef ARRAY_FOREACH_SKIP
#undef RAW_COMBINE
#undef ARRAY_FOREACH4
#undef ARRAY_FOREACH2
#undef ARRAY_COMBINE4
#undef ARRAY_COMBINE2

#endif
