// 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>.

#ifndef _GENINDICES_H_
#define _GENINDICES_H_

#include <iostream>
#include <memory>

#ifndef __GXX_EXPERIMENTAL_CXX0X__
#if (__cplusplus <= 199711L )
//#warning compiler may be too old !
#include <tr1/memory>
namespace std
{
  using tr1::shared_ptr;
}
#endif
#else
//#warning compiler may be too old !
#endif


// computes pow(N,order) at compile time !
template <int N,int order> struct Power
{
  enum 
  {
    value = N * Power<N,order-1>::value
  };
};

template <int N> struct Power<N,0>
{
  enum { value = 1 };
};

template <int N> struct Power<N,-10> //avoid never ending loops if reasonnably negative...
{
  enum { value = 0 };
};


template<int order, int N> struct SwapIndex;

// helper class to manipulate multiindices.
// allows simple loops over an arbitrary number of indices.
template<int order, int N> struct genIndices 
{
  friend class SwapIndex<order,N>;
  static const int Nele=Power<N,order>::value;
  static const int Order=order;
  static const int Dim=N;
  genIndices() {Clear();}
  genIndices(const int ndx_[order]) {for (int i=0;i<order;++i) ndx[i]=ndx_[i];}
  genIndices(int index) {getIndices(index);} // convert from linear index (0 -> N^order-1)
  template<int N1> genIndices(const genIndices<order,N1> &ndx1) {copyIndices(ndx1);}
  template<int N1> void copyIndices(const genIndices<order,N1> &ndx1) {for (int i=0;i<order;++i) ndx[i]=ndx1[i];}
  inline int operator[](int i) const { return ndx[i]; }
  inline int & operator[](int i) { return ndx[i]; }
  inline bool Overflow() {return (ndx[0]==-1);}
  inline bool Clear() {for (int i=0;i<order;++i) ndx[i]=0;return false;}
  inline bool Swap()
  {
    for (int i=0,j=order-1;i<(order>>1);++i,--j)
    {
      int tmp=ndx[i];
      ndx[i]=ndx[j];
      ndx[j]=tmp;
    }
    return false;
  }
  genIndices<order,N>& operator++() // pre increment
  {
    int carry=1;
    for (int i=order-1;i>=0;--i)
    {
      if (carry)
      {
        if (++ndx[i]==N) { ndx[i]=0; carry=1;}
        else { carry=0;break;}
      } else break;
    }
    if (carry==1) ndx[0]=-1; // overflow
    return *this;
  }

  genIndices<order,N> operator++(int) // post increment
  {
    genIndices<order,N> tmp(*this); // copy
    operator++(); // pre-increment
    return tmp;   // return old value
  }

  genIndices<order,N>& operator--() // pre decrement
  {
    int carry=1;
    for (int i=order-1;i>=0;--i)
    {
      if (carry)
      {
        if (--ndx[i]==-1) { ndx[i]=N-1; carry=1; }
        else {carry =0;break;}
      } else break;
    }
    if (carry==1) ndx[0]=-1; //decremented 0...
    return *this;
  }

  genIndices<order,N> operator--(int) // post decrement
  {
    genIndices<order,N> tmp(*this); // copy
    operator--(); // pre-derement
    return tmp;   // return old value
  }
  void getIndices(int index);
  int getIndex(void) const;
private:
  int ndx[order];
};

template<int order,int N> void genIndices<order,N>::getIndices(int index)
{
 for (int i=order-1;i>=0;--i) // inverse loop because the fastest incrementing index is the last one (like in e.g. in getindex(i,j)) and tab[i][j]
 {
   ndx[i]=index % N;
   index/=N;
 }
}

template<int order,int N> int genIndices<order,N>::getIndex(void) const
{
  int val=0;
  int pow=1;
  for (int i=order-1;i>=0;--i) // inverse loop because the fastest incrementing index is the last one (like in e.g. in getindex(i,j)) and tab[i][j]
  {
    val+=ndx[i]*pow;
    pow*=N;
  }
  return val;
}

template <int order,int N> std::ostream & operator<< (std::ostream &output,const genIndices<order,N> &ndx)
{
  output << "[";
  for (int i=0;i<order;++i)
  {
    output << ndx[i];
    if (i<order-1) std::cout << ",";
  }
  output << "]";
  return output;
}



struct Swap 
{
  static Swap *NoSwap;
  inline int operator[](int i) const { return i; }
};

template<int order, int N=3> struct SwapIndex : public Swap
{
  static const int Nele=Power<N,order>::value;
  static const int Order=order;
  static const int Dim=N;
  int ndx[Nele];
  SwapIndex(const int ndx_[order]) // new index order
  {
    for (int i=0;i<Nele;++i)
    {
      genIndices<order,N> ind(i);
      genIndices<order,N> ind2;
      for (int j=0;j<order;++j)
        ind2.ndx[ndx_[j]]=ind.ndx[j];
      int l=ind2.getIndex();
      ndx[i]=l;
    }
  }
  SwapIndex() // swap all
  {
    for (int i=0;i<Nele;++i)
    {
      genIndices<order,N> ind(i);
      ind.Swap();
      int l=ind.getIndex();
      ndx[i]=l;
    }
  }
  inline int operator[](int i) const { return ndx[i]; }
};


#endif // _GENINDICES_H_
