// GenFem - A high-level finite element library
// Copyright (C) 2010-2026 Eric Bechet
//
// See the LICENSE file for license information and contributions.
// Please report all bugs and problems to <bechet@cadxfem.org>.
//
// Initial design: Frederic Duboeuf and Eric Bechet (rev.576)


#ifndef _GEN_TENSORIAL_TRAITS__H_
#define _GEN_TENSORIAL_TRAITS__H_

#include <ostream>
#include <iomanip>
#include <complex>

#include "genTensors.h"

//--------------------------------------------------------------------------
// Tensorial Traits
//--------------------------------------------------------------------------

template<class T> struct TensorialTraits
{
};

template<class scalar> struct ScalarTraits
{
  typedef scalar RealType;
  static const int nr=1;
};

template<class scalar> struct ScalarTraits<std::complex<scalar> >
{
  typedef scalar RealType;
  static const int nr=2;
};


// new genTensor
template<int order,class scalar,int N> struct TensorialTraits<genTensor<order,scalar,N> >
{
  typedef scalar Scalar;
  static const int dim=N;
  typedef genTensor<0,scalar,N> ScalarType;
  typedef genTensor<order,scalar,N> ValType;
  typedef genTensor<order,typename ScalarTraits<scalar>::RealType,N> RealValType;
  typedef genTensor<order+1,scalar,N> GradType;
  typedef genTensor<order+2,scalar,N> HessType;
};

template<class scalar,int N> struct TensorialTraits<genTensor0<scalar,N> >
{
  typedef scalar Scalar;
  static const int dim=N;
  typedef genTensor0<scalar,N> ScalarType;
  typedef genTensor0<scalar,N> ValType;
  typedef genTensor0<typename ScalarTraits<scalar>::RealType,N> RealValType;
  typedef genTensor1<scalar,N> GradType;
  typedef genTensor2<scalar,N> HessType;
};

template<class scalar,int N> struct TensorialTraits<genTensor1<scalar,N> >
{
  typedef scalar Scalar;
  static const int dim=N;
  typedef genTensor0<scalar,N> ScalarType;
  typedef genTensor1<scalar,N> ValType;
  typedef genTensor1<typename ScalarTraits<scalar>::RealType,N> RealValType;
  typedef genTensor2<scalar,N> GradType;
  typedef genTensor3<scalar,N> HessType;
};


template<class scalar,int N> struct TensorialTraits<genTensor2<scalar,N> >
{
  typedef scalar Scalar;
  static const int dim=N;
  typedef genTensor0<scalar,N> ScalarType;
  typedef genTensor2<scalar,N> ValType;
  typedef genTensor2<typename ScalarTraits<scalar>::RealType,N> RealValType;
  typedef genTensor3<scalar,N> GradType;
  typedef genTensor4<scalar,N> HessType;
};

template<class scalar,int N> struct TensorialTraits<genTensor3<scalar,N> >
{
  typedef scalar Scalar;
  static const int dim=N;
  typedef genTensor0<scalar,N> ScalarType;
  typedef genTensor3<scalar,N> ValType;
  typedef genTensor3<typename ScalarTraits<scalar>::RealType,N> RealValType;
  typedef genTensor4<scalar,N> GradType;
//typedef genTensor5<scalar,N> HessType; // will fail
};

template<class scalar,int N> struct TensorialTraits<genTensor4<scalar,N> >
{
  typedef scalar Scalar;
  static const int dim=N;
  typedef genTensor0<scalar,N> ScalarType;
  typedef genTensor4<scalar,N> ValType;
  typedef genTensor4<typename ScalarTraits<scalar>::RealType,N> RealValType;
//typedef genTensor5<scalar,N> GradType; // will fail
//typedef genTensor6<scalar,N> HessType; // will fail
};


template<class T1,class T2> struct TensorialTraitsBinary
{
};

// new genTensor


template<int order1,int order2,class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor<order1,scalar,N1>,genTensor<order2,scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor<order1+order2,scalar,(N1>N2)?N1:N2> TensProdType;
  typedef genTensor<(order1+order2-2)>=0?order1+order2-2:0,scalar,(N1>N2)?N1:N2> ContrProdType;
  typedef genTensor<(order1-order2)>=0?order1-order2:order2-order1,scalar,(N1>N2)?N1:N2> FullContrProdType;
};


template<class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor0<scalar,N1>,genTensor0<scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor0<scalar,(N1>N2)?N1:N2> TensProdType;
  typedef genTensor0<scalar,(N1>N2)?N1:N2> ContrProdType;
  typedef genTensor0<scalar,(N1>N2)?N1:N2> FullContrProdType;
};

template<class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor0<scalar,N1>,genTensor1<scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor1<scalar,N2> TensProdType;
  typedef genTensor1<scalar,N2> ContrProdType;
  typedef genTensor1<scalar,N2> FullContrProdType;
};

template<class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor1<scalar,N1>,genTensor0<scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor1<scalar,N1> TensProdType;
  typedef genTensor1<scalar,N1> ContrProdType;
  typedef genTensor1<scalar,N1> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor1<scalar,N>,genTensor1<scalar,N> >
{
  typedef scalar Scalar;
  typedef genTensor2<scalar,N> TensProdType;
  typedef genTensor0<scalar,N> ContrProdType;
  typedef genTensor0<scalar,N> FullContrProdType;
};

template<class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor0<scalar,N1>,genTensor2<scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor2<scalar,N2> TensProdType;
  typedef genTensor2<scalar,N2> ContrProdType;
  typedef genTensor2<scalar,N2> FullContrProdType;
};

template<class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor2<scalar,N1>,genTensor0<scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor2<scalar,N1> TensProdType;
  typedef genTensor2<scalar,N1> ContrProdType;
  typedef genTensor2<scalar,N1> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor1<scalar,N>,genTensor2<scalar,N> >
{
  typedef scalar Scalar;
  typedef genTensor3<scalar,N> TensProdType;
  typedef genTensor1<scalar,N> ContrProdType;
  typedef genTensor1<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor2<scalar,N>,genTensor1<scalar,N> >
{
  typedef scalar Scalar;
  typedef genTensor3<scalar,N> TensProdType;
  typedef genTensor1<scalar,N> ContrProdType;
  typedef genTensor1<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor2<scalar,N>,genTensor2<scalar,N> >
{
  typedef scalar Scalar;
  typedef genTensor4<scalar,N> TensProdType;
  typedef genTensor2<scalar,N> ContrProdType;
  typedef genTensor0<scalar,N> FullContrProdType;
};

template<class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor0<scalar,N1>,genTensor3<scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor3<scalar,N2> TensProdType;
  typedef genTensor3<scalar,N2> ContrProdType;
  typedef genTensor3<scalar,N2> FullContrProdType;
};

template<class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor3<scalar,N1>,genTensor0<scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor3<scalar,N1> TensProdType;
  typedef genTensor3<scalar,N1> ContrProdType;
  typedef genTensor3<scalar,N1> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor1<scalar,N>,genTensor3<scalar,N> >
{
  typedef scalar Scalar;
  typedef genTensor4<scalar,N> TensProdType;
  typedef genTensor2<scalar,N> ContrProdType;
  typedef genTensor2<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor3<scalar,N>,genTensor1<scalar,N> >
{
  typedef scalar Scalar;
  typedef genTensor4<scalar,N> TensProdType;
  typedef genTensor2<scalar,N> ContrProdType;
  typedef genTensor2<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor2<scalar,N>,genTensor3<scalar,N> >
{
  typedef scalar Scalar;
//   typedef genTensor5<scalar,N> TensProdType; // will fail
  typedef genTensor3<scalar,N> ContrProdType;
  typedef genTensor1<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor3<scalar,N>,genTensor2<scalar,N> >
{
  typedef scalar Scalar;
//typedef genTensor5<scalar,N> TensProdType; // will fail
  typedef genTensor3<scalar,N> ContrProdType;
  typedef genTensor1<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor3<scalar,N>,genTensor3<scalar,N> >
{
  typedef scalar Scalar;
//typedef genTensor5<scalar,N> TensProdType; // will fail
  typedef genTensor4<scalar,N> ContrProdType;
  typedef genTensor0<scalar,N> FullContrProdType;
};

template<class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor4<scalar,N1>,genTensor0<scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor4<scalar,N1> TensProdType;
  typedef genTensor4<scalar,N1> ContrProdType;
  typedef genTensor4<scalar,N1> FullContrProdType;
};

template<class scalar,int N1,int N2> struct TensorialTraitsBinary<genTensor0<scalar,N1>,genTensor4<scalar,N2> >
{
  typedef scalar Scalar;
  typedef genTensor4<scalar,N2> TensProdType;
  typedef genTensor4<scalar,N2> ContrProdType;
  typedef genTensor4<scalar,N2> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor4<scalar,N>,genTensor1<scalar,N> >
{
  typedef scalar Scalar;
//typedef genTensor5<scalar,N> TensProdType; // will fail
  typedef genTensor3<scalar,N> ContrProdType;
  typedef genTensor3<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor1<scalar,N>,genTensor4<scalar,N> >
{
  typedef scalar Scalar;
//typedef genTensor5<scalar,N> TensProdType; // will fail
  typedef genTensor3<scalar,N> ContrProdType;
  typedef genTensor3<scalar,N> FullContrProdType;
};


template<class scalar,int N> struct TensorialTraitsBinary<genTensor4<scalar,N>,genTensor2<scalar,N> >
{
  typedef scalar Scalar;
//typedef genTensor6<scalar,N> TensProdType; // will fail
  typedef genTensor4<scalar,N> ContrProdType;
  typedef genTensor2<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor2<scalar,N>,genTensor4<scalar,N> >
{
  typedef scalar Scalar;
//typedef genTensor6<scalar,N> TensProdType; // will fail
  typedef genTensor4<scalar,N> ContrProdType;
  typedef genTensor2<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor4<scalar,N>,genTensor3<scalar,N> >
{
  typedef scalar Scalar;
//typedef genTensor7<scalar,N> TensProdType; // will fail
//typedef genTensor5<scalar,N> ContrProdType; // will fail
  typedef genTensor1<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor3<scalar,N>,genTensor4<scalar,N> >
{
  typedef scalar Scalar;
//typedef genTensor7<scalar,N> TensProdType; // will fail
//typedef genTensor5<scalar,N> ContrProdType; // will fail
  typedef genTensor1<scalar,N> FullContrProdType;
};

template<class scalar,int N> struct TensorialTraitsBinary<genTensor4<scalar,N>,genTensor4<scalar,N> >
{
  typedef scalar Scalar;
//typedef genTensor8<scalar,N> TensProdType; // will fail
//typedef genTensor6<scalar,N> ContrProdType; // will fail
  typedef genTensor0<scalar,N> FullContrProdType;
};

// Doit pouvoir être ajouté directement dans TensorialTraits
template<class T,class scalar=double> struct TensorialProperties
{
};

template<class scalar> struct TensorialProperties<scalar>
{
  static const char* Name() {return "Scalar";}
  static const int Length() {return 1;}
  static const int Size() {return 1;}
};

// new genTensor
template<int order,class scalar,int N> struct TensorialProperties<genTensor<order,scalar,N> >
{
  static const char* Name() {return "genTensor";}
  static const int Length() {return order==0?1:order<2?3:9;}
  static const int Size() {return Power<N,order>::value;}
};

template<class scalar,int N> struct TensorialProperties<genTensor0<scalar,N> >
{
  static const char* Name() {return "genTensor0";}
  static const int Length() {return 1;}
  static const int Size() {return 1;}
};

template<class scalar,int N> struct TensorialProperties<genTensor1<scalar,N> >
{
  static const char* Name() {return "genTensor1";}
  static const int Length() {return 3;}
  static const int Size() {return N;}
};

template<class scalar,int N> struct TensorialProperties<genTensor2<scalar,N> >
{
  static const char* Name() {return "genTensor2";}
  static const int Length() {return 3;}
  static const int Size() {return N*N;}
};

template<class scalar,int N> struct TensorialProperties<genTensor3<scalar,N> >
{
  static const char* Name() {return "genTensor3";}
  static const int Length() {return 9;}
  static const int Size() {return N*N*N;}
};

template<class scalar,int N> struct TensorialProperties<genTensor4<scalar,N> >
{
  static const char* Name() {return "genTensor4";}
  static const int Length() {return 9;}
  static const int Size() {return N*N*N*N;}
};

//--------------------------------------------------------------------------
// Tensor Operators
//--------------------------------------------------------------------------
/*
template<typename T,class scalar> inline void PrintOp(const T &a)
{
  printf("PrintOp is not defined");
  assert(0);
}
*/
template<class scalar> inline void PrintOp(const scalar &a)
{
  std::cout << " scalar :" << std::endl <<  a << std::endl;
}

// new genTensor
template<int order,class scalar,int N> inline void PrintOp(const genTensor<order,scalar,N> &a)
{
  char str[10];
  sprintf(str,"%d",order);
  a.print(str);
}

template<class scalar,int N> inline void PrintOp(const genTensor0<scalar,N> &a)
{
  a.print("0");
}

template<class scalar,int N> inline void PrintOp(const genTensor1<scalar,N> &a)
{
  a.print("1");
}

template<class scalar,int N> inline void PrintOp(const genTensor2<scalar,N> &a)
{
  a.print("2");
}

template<class scalar,int N> inline void PrintOp(const genTensor3<scalar,N> &a)
{
  a.print("3");
}

template<class scalar,int N> inline void PrintOp(const genTensor4<scalar,N> &a)
{
  a.print("4");
}
/*
template<typename T> inline void writeTensor(const T &tens, std::ostream &out, int line=0) // inline pour eviter de mettre les spécialisations dans le cpp
{
  printf("writeTensor is not defined");
  assert(0);
}
*/
template<class scalar> inline void writeTensor(const scalar &tens, std::ostream &out, int line)
{
  out << tens << " ";
}

// new genTensor
template<int order,class scalar,int N> inline void writeTensor(const genTensor<order,scalar,N> &tens, std::ostream &out, int line)
{
  out << tens << " "; // need to code that correctly
}


template<class scalar,int N> inline void writeTensor(const genTensor0<scalar,N> &tens, std::ostream &out, int line)
{
  out << tens() << " ";
}

template<class scalar,int N> inline void writeTensor(const genTensor1<scalar,N> &tens, std::ostream &out, int line)
{
  out << tens(line) << " ";
}

template<class scalar,int N> inline void writeTensor(const genTensor2<scalar,N> &tens, std::ostream &out, int line)
{
  for (int j=0; j<N; ++j)
    out << tens(line,j) << " ";
  out << " ";
}

template<class scalar,int N> inline void writeTensor(const genTensor3<scalar,N> &tens, std::ostream &out, int line) // line [0;8]
{
  int i=line/N;
  int j=line%N;
  for (int k=0; k<N; ++k)
    out << tens(i,j,k) << " ";
  out << " ";
}

template<class scalar,int N> inline void writeTensor(const genTensor4<scalar,N> &tens, std::ostream &out, int line) // line [0;8]
{
  int i=line/N;
  int j=line%N;
  for (int k=0; k<N; ++k)
    for (int l=0; l<N; ++l)
      out << tens(i,j,k,l) << " ";
  out << " ";
}


#ifdef USE_FTENSOR
#include "FTensor.hpp"
using namespace FTensor;


namespace FTensor
{
template<class T, int N>
class Scalar0
{  
  T data;
public : 
  Scalar0() : data() {}
  Scalar0(T d) : data(d) {}
  Scalar0(const Scalar0<T, N> &other) : data(other.data) {}
  inline operator T() const {return data;}
//  inline operator T&() {return data;}
  inline T operator ()() const {return data;}
};
}


template<class scalar, int N> struct TensorialTraits<Scalar0<scalar, N> >
{
  typedef scalar Scalar;
  static const int dim=N;
  typedef Scalar0<scalar, N> ScalarType;
  typedef Scalar0<scalar, N> ValType;
  typedef Tensor1<scalar, N> GradType;
  typedef Tensor2<scalar, N,N> HessType;
};

template<class scalar, int N> struct TensorialTraits<Tensor1<scalar, N> >
{
  typedef scalar Scalar;
  static const int dim=N;
  typedef Scalar0<scalar, N> ScalarType;
  typedef Tensor1<scalar, N> ValType;
  typedef Tensor2<scalar, N,N> GradType;
//  typedef Tensor3<scalar, N,N> HessType;
};

template<class scalar, int N> struct TensorialTraits<Tensor2<scalar, N,N> >
{
  typedef scalar Scalar;
  static const int dim=N;
  typedef Scalar0<scalar, N> ScalarType;
  typedef Tensor2<scalar, N,N> ValType;
//  typedef Tensor3<scalar, N,N> GradType;
//  typedef Tensor3<scalar, N,N> HessType;
};
/*
template<class T> struct TensorialTraitsBinary< typename T::Scalar,T >
{
  typedef typename T::scalar Scalar;
  typedef T TensProdType;
  typedef T ContrProdType;
  typedef T FullContrProdType;
};

template<class T> struct TensorialTraitsBinary< T, typename T::Scalar>
{
  typedef typename T::scalar Scalar;
  typedef T TensProdType;
  typedef T ContrProdType;
  typedef T FullContrProdType;
};
*/

template<class scalar, int N> struct TensorialTraitsBinary<Scalar0<scalar, N>,Scalar0<scalar, N> >
{
  typedef scalar Scalar;
  typedef Scalar0<scalar, N> TensProdType;
  typedef Scalar0<scalar, N> ContrProdType;
  typedef Scalar0<scalar, N> FullContrProdType;
};

template<class scalar, int N> struct TensorialTraitsBinary<Scalar0<scalar, N>,Tensor1<scalar, N> >
{
  typedef scalar Scalar;
  typedef Tensor1<scalar, N> TensProdType;
  typedef Tensor1<scalar, N> ContrProdType;
  typedef Tensor1<scalar, N> FullContrProdType;
};
template<class scalar, int N> struct TensorialTraitsBinary<Tensor1<scalar, N>,Scalar0<scalar, N> >
{
  typedef scalar Scalar;
  typedef Tensor1<scalar, N> TensProdType;
  typedef Tensor1<scalar, N> ContrProdType;
  typedef Tensor1<scalar, N> FullContrProdType;
};

template<class scalar, int N> struct TensorialTraitsBinary<Tensor1<scalar, N> ,Tensor1<scalar, N> >
{
  typedef scalar Scalar;
  typedef Tensor2<scalar, N,N> TensProdType;
  typedef Scalar0<scalar, N> ContrProdType;
  typedef Scalar0<scalar, N> FullContrProdType;
};


template<class scalar, int N> struct TensorialTraitsBinary<Scalar0<scalar, N>,Tensor2<scalar, N,N> >
{
  typedef scalar Scalar;
  typedef Tensor2<scalar, N,N> TensProdType;
  typedef Tensor2<scalar, N,N> ContrProdType;
  typedef Tensor2<scalar, N,N> FullContrProdType;
};

template<class scalar, int N> struct TensorialTraitsBinary<Tensor2<scalar, N,N>,Scalar0<scalar, N> >
{
  typedef scalar Scalar;
  typedef Tensor2<scalar, N,N> TensProdType;
  typedef Tensor2<scalar, N,N> ContrProdType;
  typedef Tensor2<scalar, N,N> FullContrProdType;
};
/*
template<class scalar, int N> struct TensorialTraitsBinary<Tensor1<scalar, N>,Tensor2<scalar, N,N> >
{
  typedef scalar Scalar;
  typedef Tensor3<scalar, N,N,N> TensProdType;
  typedef Tensor1<scalar, N> ContrProdType;
  typedef Tensor1<scalar, N> FullContrProdType;
};

template<class scalar, int N> struct TensorialTraitsBinary<Tensor2<scalar, N,N>,Tensor1<scalar, N> >
{
  typedef scalar Scalar;
  typedef Tensor3<scalar, N,N,N> TensProdType;
  typedef Tensor1<scalar, N> ContrProdType;
  typedef Tensor1<scalar, N> FullContrProdType;
};

template<class scalar, int N> struct TensorialTraitsBinary<Tensor2<scalar, N,N> ,Tensor2<scalar, N,N> >
{
  typedef scalar Scalar;
  typedef Tensor4<scalar, N,N,N,N> TensProdType;
  typedef Tensor1<scalar, N> ContrProdType;
  typedef Scalar0<scalar, N> FullContrProdType;
};
*/
template<class scalar, int N> struct TensorialProperties<Scalar0<scalar, N> >
{
  static const char* Name() {return "Scalar0";}
  static const int Length() {return 1;}
};

template<class scalar, int N> struct TensorialProperties<Tensor1<scalar, N> >
{
  static const char* Name() {return "Tensor1";}
  static const int Length() {return 3;}
};

template<class scalar, int N> struct TensorialProperties<Tensor2<scalar, N,N> >
{
  static const char* Name() {return "Tensor2";}
  static const int Length() {return 3;}
};


template<class scalar, int N> inline void PrintOp(const Scalar0<scalar, N> &a)
{
  std::cout << a() << " " ;
}


template<class scalar, int N> inline void PrintOp(const Tensor1<scalar, N> &a)
{
  for (int i=0;i<N;++i) std::cout << a(i) << " " ;
}

template<class scalar, int N> inline void PrintOp(const Tensor2<scalar, N,N> &a)
{
  for (int i=0; i<N; ++i){
    for (int j=0; j<N; ++j)
      std::cout << a(i,j) << " " ;
    std::cout << std::endl; }
}


template<class scalar, int N> inline void writeTensor(const Scalar0<scalar, N> &tens, std::ostream &out, int line)
{
  out << tens() << " ";
}

template<class scalar, int N> inline void writeTensor(const Tensor1<scalar, N> &tens, std::ostream &out, int line)
{
  out << tens(line) << " ";
}

template<class scalar, int N> inline void writeTensor(const Tensor2<scalar, N,N> &tens, std::ostream &out, int line)
{
  for (int j=0; j<N; ++j)
    out << tens(line,j) << " ";
  out << " ";
}

#endif //USE_FTENSOR






#endif //_GEN_TENSORIAL_TRAITS__H_