
#ifndef INTREPID_FIELDCONTAINER_KOKKOS_HPP
#define INTREPID_FIELDCONTAINER_KOKKOS_HPP

//#ifdef HAVE_INTREPID_KOKKOSCORE
#include "Kokkos_Core.hpp"
#include "Sacado.hpp"
#include <impl/Kokkos_Timer.hpp>
//#endif

#include <random>
#include <time.h>
#include <stdlib.h>
#include <Kokkos_Random.hpp>



namespace Intrepid{
class none{};
class FieldContainer_Kokkos_Ptr;
template <class Scalar,class ScalarPointer=void,class MemoryLayout=void,class ExecutionSpace=void>
class FieldContainer_Kokkos;
template <class Scalar>
class FieldContainer_Kokkos<Scalar,void,void,void>{
size_t dim0=0;
size_t dim1=0;
size_t dim2=0;
size_t dim3=0;
size_t dim4=0;
size_t dim5=0;
size_t dim6=0;
size_t dim7=0;
size_t dim[8]={0};

Scalar* containerMemory;
size_t rankValue=0;
size_t sizeValue=0;
unsigned int count_=1;
public:
FieldContainer_Kokkos()=delete;
FieldContainer_Kokkos(size_t dim_0);
FieldContainer_Kokkos(size_t dim_0,size_t dim_1);
FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2);
FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3);
FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3,size_t dim_4);
FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3,size_t dim_4,size_t dim_5);
FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3,size_t dim_4,size_t dim_5,size_t dim_6);
FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3,size_t dim_4,size_t dim_5,size_t dim_6,size_t dim_7);
FieldContainer_Kokkos(FieldContainer_Kokkos& inContainer);
FieldContainer_Kokkos(const FieldContainer_Kokkos& inContainer);
~FieldContainer_Kokkos();
Scalar& operator() (const size_t i0);

Scalar& operator() (const size_t i0, const size_t i1);

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2);

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3 );

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3 , const size_t i4 );

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3, const size_t i4, const size_t i5);

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3, const size_t i4, const size_t i5,
                          const size_t i6);

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3, const size_t i4, const size_t i5,
                          const size_t i6, const size_t i7);

Scalar& operator() (const size_t i0)const;

Scalar& operator() (const size_t i0, const size_t i1)const;

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2)const;

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3 )const;

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3 , const size_t i4 )const;

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3, const size_t i4, const size_t i5)const;

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3, const size_t i4, const size_t i5,
                          const size_t i6)const;

Scalar& operator() (const size_t i0, const size_t i1, const size_t i2,
                          const size_t i3, const size_t i4, const size_t i5,
                          const size_t i6, const size_t i7)const;
size_t rank(){return rankValue;}

size_t size(){return sizeValue;}

size_t dimension(size_t num)const{return dim[num];}

size_t dimension_0(){return dim0;}
size_t dimension_1(){return dim1;}
size_t dimension_2(){return dim2;}
size_t dimension_3(){return dim3;}
size_t dimension_4(){return dim4;}
size_t dimension_5(){return dim5;}
size_t dimension_6(){return dim6;}
size_t dimension_7(){return dim7;}

size_t dimension_0()const{return dim0;}
size_t dimension_1()const{return dim1;}
size_t dimension_2()const{return dim2;}
size_t dimension_3()const{return dim3;}
size_t dimension_4()const{return dim4;}
size_t dimension_5()const{return dim5;}
size_t dimension_6()const{return dim6;}
size_t dimension_7()const{return dim7;}

};

template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(FieldContainer_Kokkos<Scalar>& inContainer){
rankValue=inContainer.rankValue;
sizeValue=inContainer.sizeValue;
dim[0]=dim0=inContainer.dim0;
dim[1]=dim1=inContainer.dim1;
dim[2]=dim2=inContainer.dim2;
dim[3]=dim3=inContainer.dim3;
dim[4]=dim4=inContainer.dim4;
dim[5]=dim5=inContainer.dim5;
dim[6]=dim6=inContainer.dim6;
dim[7]=dim7=inContainer.dim7;
containerMemory=inContainer.containerMemory;
count_=inContainer.count_;
count_=count_+1;
}

template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(const FieldContainer_Kokkos<Scalar>& inContainer){
rankValue=inContainer.rankValue;
sizeValue=inContainer.sizeValue;
dim[0]=dim0=inContainer.dim0;
dim[1]=dim1=inContainer.dim1;
dim[2]=dim2=inContainer.dim2;
dim[3]=dim3=inContainer.dim3;
dim[4]=dim4=inContainer.dim4;
dim[5]=dim5=inContainer.dim5;
dim[6]=dim6=inContainer.dim6;
dim[7]=dim7=inContainer.dim7;
containerMemory=inContainer.containerMemory;
count_=inContainer.count_;
count_=count_+1;
}

template <class Scalar>
FieldContainer_Kokkos<Scalar>::~FieldContainer_Kokkos(){
count_=count_-1;
if(count_==0){delete[] containerMemory;}
}
template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(size_t dim_0){
count_=1;
dim0=dim[0]=dim_0;
rankValue=1;
sizeValue=dim_0;
containerMemory=new Scalar[sizeValue];
}

template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(size_t dim_0,size_t dim_1){
count_=1;
dim0=dim[0]=dim_0;
dim1=dim[1]=dim_1;
rankValue=2;
sizeValue=dim_0*dim_1;
containerMemory=new Scalar[sizeValue];
}
template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2){
count_=1;
dim0=dim[0]=dim_0;
dim1=dim[1]=dim_1;
dim2=dim[2]=dim_2;
rankValue=3;
sizeValue=dim_0*dim_1*dim_2;
containerMemory=new Scalar[sizeValue];
}

template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3){
count_=1;
dim0=dim[0]=dim_0;
dim1=dim[1]=dim_1;
dim2=dim[2]=dim_2;
dim3=dim[3]=dim_3;
rankValue=4;
sizeValue=dim_0*dim_1*dim_2*dim_3;
containerMemory=new Scalar[sizeValue];

}

template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3,size_t dim_4){
count_=1;
dim0=dim[0]=dim_0;
dim1=dim[1]=dim_1;
dim2=dim[2]=dim_2;
dim3=dim[3]=dim_3;
dim4=dim[4]=dim_4;
rankValue=5;
sizeValue=dim_0*dim_1*dim_2*dim_3*dim_4;
containerMemory=new Scalar[sizeValue];
}

template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3,size_t dim_4,size_t dim_5){
count_=1;
dim0=dim[0]=dim_0;
dim1=dim[1]=dim_1;
dim2=dim[2]=dim_2;
dim3=dim[3]=dim_3;
dim4=dim[4]=dim_4;
dim5=dim[5]=dim_5;
rankValue=6;
sizeValue=dim_0*dim_1*dim_2*dim_3*dim_4*dim_5;
containerMemory=new Scalar[sizeValue];
}
template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3,size_t dim_4,size_t dim_5,size_t dim_6){
count_=1;
dim0=dim[0]=dim_0;
dim1=dim[1]=dim_1;
dim2=dim[2]=dim_2;
dim3=dim[3]=dim_3;
dim4=dim[4]=dim_4;
dim5=dim[5]=dim_5;
dim6=dim[6]=dim_6;
rankValue=7;
sizeValue=dim_0*dim_1*dim_2*dim_3*dim_4*dim_5*dim_6;
containerMemory=new Scalar[sizeValue];
}


template <class Scalar>
FieldContainer_Kokkos<Scalar>::FieldContainer_Kokkos(size_t dim_0,size_t dim_1,size_t dim_2,size_t dim_3,size_t dim_4,size_t dim_5,size_t dim_6,size_t dim_7){
count_=1;
dim0=dim[0]=dim_0;
dim1=dim[1]=dim_1;
dim2=dim[2]=dim_2;
dim3=dim[3]=dim_3;
dim4=dim[4]=dim_4;
dim5=dim[5]=dim_5;
dim6=dim[6]=dim_6;
dim7=dim[7]=dim_7;
rankValue=8;
sizeValue=dim_0*dim_1*dim_2*dim_3*dim_4*dim_5*dim_6*dim_7;
containerMemory=new Scalar[sizeValue];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0){
return containerMemory[i0];
}
template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1){
return containerMemory[dim1*i0+i1];
}
template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2){
return containerMemory[(dim1*i0+i1)*dim2+i2];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3){
return containerMemory[((dim1*i0+i1)*dim2+i2)*dim3+i3];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3,const size_t i4){
return containerMemory[(((dim1*i0+i1)*dim2+i2)*dim3+i3)*dim4+i4];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3,const size_t i4,const size_t i5){
return containerMemory[((((dim1*i0+i1)*dim2+i2)*dim3+i3)*dim4+i4)*dim5+i5];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3,const size_t i4,const size_t i5,const size_t i6){
return containerMemory[(((((dim1*i0+i1)*dim2+i2)*dim3+i3)*dim4+i4)*dim5+i5)*dim6+i6];
}
template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3,const size_t i4,const size_t i5,const size_t i6,const size_t i7){
return containerMemory[((((((dim1*i0+i1)*dim2+i2)*dim3+i3)*dim4+i4)*dim5+i5)*dim6+i6)*dim7+i7];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0)const{
return containerMemory[i0];
}
template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1)const{
return containerMemory[dim1*i0+i1];
}
template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2)const{
return containerMemory[(dim1*i0+i1)*dim2+i2];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3)const{
return containerMemory[((dim1*i0+i1)*dim2+i2)*dim3+i3];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3,const size_t i4)const{
return containerMemory[(((dim0*i1+i1)*dim2+i2)*dim3+i3)*dim4+i4];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3,const size_t i4,const size_t i5)const{
return containerMemory[((((dim1*i0+i1)*dim2+i2)*dim3+i3)*dim4+i4)*dim5+i5];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3,const size_t i4,const size_t i5,const size_t i6)const{
return containerMemory[(((((dim1*i0+i1)*dim2+i2)*dim3+i3)*dim4+i4)*dim5+i5)*dim6+i6];
}

template <class Scalar>
inline Scalar& FieldContainer_Kokkos<Scalar>::operator() (const size_t i0,const size_t i1,const size_t i2,const size_t i3,const size_t i4,const size_t i5,const size_t i6,const size_t i7)const{
return containerMemory[((((((dim1*i0+i1)*dim2+i2)*dim3+i3)*dim4+i4)*dim5+i5)*dim6+i6)*dim7+i7];
}


}

template<class FadType, class Layout, class Device, class Scalar>
struct Return_Type< Intrepid::FieldContainer_Kokkos<FadType, Layout, Device>, Scalar>{
      typedef FadType& return_type;
      typedef FadType  const_return_type;
};

template<class FadType, class Layout, class Device, class Scalar>
struct Return_Type<const Intrepid::FieldContainer_Kokkos<FadType, Layout, Device>, Scalar>{
      typedef FadType& return_type;
      typedef FadType  const_return_type;
};



#include "Intrepid_FieldContainer_Kokkos_CUDA_Left.hpp"
#include "Intrepid_FieldContainer_Kokkos_CUDA_Right.hpp"
#include "Intrepid_FieldContainer_Kokkos_OpenMP_Left.hpp"
#include "Intrepid_FieldContainer_Kokkos_OpenMP_Right.hpp"
#include "Intrepid_FieldContainer_Kokkos_PThreads_Left.hpp"
#include "Intrepid_FieldContainer_Kokkos_PThreads_Right.hpp"
#include "Intrepid_FieldContainer_Kokkos_Serial_Left.hpp"
#include "Intrepid_FieldContainer_Kokkos_Serial_Right.hpp"
#endif
