#ifndef MKL_TEMPLATE
#define MKL_TEMPLATE

//#include <cblas.h>
#include <stddef.h>
//#include <blas.h>
#ifdef small
#undef small
#endif
//#include <lapack.h>
#include <cblas_defvar.h>

#ifdef NEW_MATLAB
   typedef ptrdiff_t INTT;
#else
   typedef int INTT;
#endif

/// a few static variables for lapack
static char low='l';
static char lower='L';
static char nonUnit='n';
static char upper='u';
static INTT info=0;
static char incr='I';
static char decr='D';
static char all='A';
static char no='N';
static char reduced='S';
static char allV='V';

#ifdef REMOVE_
#define dnrm2_ dnrm2
#define snrm2_ snrm2
#define dcopy_ dcopy
#define scopy_ scopy
#define daxpy_ daxpy
#define saxpy_ saxpy
#define dscal_ dscal
#define sscal_ sscal
#define dasum_ dasum
#define sasum_ sasum
#define ddot_ ddot
#define sdot_ sdot
#define dgemv_ dgemv
#define sgemv_ sgemv
#define dger_ dger
#define sger_ sger
#define dtrmv_ dtrmv
#define strmv_ strmv
#define dsyr_ dsyr
#define ssyr_ ssyr
#define dsymv_ dsymv
#define ssymv_ ssymv
#define dgemm_ dgemm
#define sgemm_ sgemm
#define dsyrk_ dsyrk
#define ssyrk_ ssyrk
#define dtrmm_ dtrmm
#define strmm_ strmm
#define dtrtri_ dtrtri
#define strtri_ strtri
#define idamax_ idamax
#define isamax_ isamax
#define dsytrf_ dsytrf
#define ssytrf_ ssytrf
#define dsytri_ dsytri
#define ssytri_ ssytri
#define dlasrt_ dlasrt
#define slasrt_ slasrt
#define dgesvd_ dgesvd
#define sgesvd_ sgesvd
#define dsyev_ dsyev
#define ssyev_ ssyev
#endif

/// external functions
#ifdef HAVE_MKL   // obsolete
extern "C" {
#endif
   size_t cblas_idamin( int n,  double* X,  int incX);
   size_t cblas_isamin( int n,  float* X,  int incX);
#ifdef HAVE_MKL
};
#endif

#ifdef HAVE_MKL
extern "C" {
   void vdSqr( int n,  double* vecIn, double* vecOut);
   void vsSqr( int n,  float* vecIn, float* vecOut);
   void vdSqrt( int n,  double* vecIn, double* vecOut);
   void vsSqrt( int n,  float* vecIn, float* vecOut);
   void vdInvSqrt( int n,  double* vecIn, double* vecOut);
   void vsInvSqrt( int n,  float* vecIn, float* vecOut);
   void vdSub( int n,  double* vecIn,  double* vecIn2, double* vecOut);
   void vsSub( int n,  float* vecIn,  float* vecIn2, float* vecOut);
   void vdDiv( int n,  double* vecIn,  double* vecIn2, double* vecOut);
   void vsDiv( int n,  float* vecIn,  float* vecIn2, float* vecOut);
   void vdExp( int n,  double* vecIn, double* vecOut);
   void vsExp( int n,  float* vecIn, float* vecOut);
   void vdInv( int n,  double* vecIn, double* vecOut);
   void vsInv( int n,  float* vecIn, float* vecOut);
   void vdAdd( int n,  double* vecIn,  double* vecIn2, double* vecOut);
   void vsAdd( int n,  float* vecIn,  float* vecIn2, float* vecOut);
   void vdMul( int n,  double* vecIn,  double* vecIn2, double* vecOut);
   void vsMul( int n,  float* vecIn,  float* vecIn2, float* vecOut);
   void vdAbs( int n,  double* vecIn, double* vecOut);
   void vsAbs( int n,  float* vecIn, float* vecOut);
}
#endif

// INTTerfaces to a few BLAS function, Level 1
/// INTTerface to cblas_*nrm2
template <typename T> T cblas_nrm2( INTT n,  T* X,  INTT incX);
/// INTTerface to cblas_*copy
template <typename T> void cblas_copy( INTT n,  T* X,  INTT incX,
      T* Y,  INTT incY);
/// INTTerface to cblas_*axpy
template <typename T> void cblas_axpy( INTT n,  T a,  T* X,
       INTT incX, T* Y,  INTT incY);
/// INTTerface to cblas_*scal
template <typename T> void cblas_scal( INTT n,  T a, T* X,
       INTT incX);
/// INTTerface to cblas_*asum
template <typename T> T cblas_asum( INTT n,  T* X,  INTT incX);
/// INTTerface to cblas_*adot
template <typename T> T cblas_dot( INTT n,  T* X,  INTT incX,
       T* Y, INTT incY);
/// interface to cblas_i*amin
template <typename T> int cblas_iamin( INTT n,  T* X,  INTT incX);
/// interface to cblas_i*amax
template <typename T> int cblas_iamax( INTT n,  T* X,  INTT incX);

// INTTerfaces to a few BLAS function, Level 2

/// INTTerface to cblas_*gemv
template <typename T> void cblas_gemv( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  INTT M,
       INTT N,  T alpha,  T *A,  INTT lda,  T *X,
       INTT incX,  T beta,T *Y,   INTT incY);
/// INTTerface to cblas_*trmv
template <typename T> void inline cblas_trmv( CBLAS_ORDER order,  CBLAS_UPLO Uplo,
       CBLAS_TRANSPOSE TransA,  CBLAS_DIAG Diag,  INTT N,
       T *A,  INTT lda, T *X,  INTT incX);
/// INTTerface to cblas_*syr
template <typename T> void inline cblas_syr( CBLAS_ORDER order,
        CBLAS_UPLO Uplo,  INTT N,  T alpha,
       T *X,  INTT incX, T *A,  INTT lda);

/// INTTerface to cblas_*symv
template <typename T> inline void cblas_symv( CBLAS_ORDER order,
       CBLAS_UPLO Uplo,  INTT N,
       T alpha,  T *A,  INTT lda,  T *X,
       INTT incX,  T beta,T *Y,   INTT incY);

// INTTerfaces to a few BLAS function, Level 3
/// INTTerface to cblas_*gemm
template <typename T> void cblas_gemm( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  CBLAS_TRANSPOSE TransB,
       INTT M,  INTT N,  INTT K,  T alpha,
       T *A,  INTT lda,  T *B,  INTT ldb,
       T beta, T *C,  INTT ldc);
/// INTTerface to cblas_*syrk
template <typename T> void cblas_syrk( CBLAS_ORDER order,
       CBLAS_UPLO Uplo,  CBLAS_TRANSPOSE Trans,  INTT N,  INTT K,
       T alpha,  T *A,  INTT lda,
       T beta, T*C,  INTT ldc);
/// INTTerface to cblas_*ger
template <typename T> void cblas_ger( CBLAS_ORDER order,
       INTT M,  INTT N,  T alpha,  T *X,  INTT incX,
       T* Y,  INTT incY, T*A,  INTT lda);
/// INTTerface to cblas_*trmm
template <typename T> void cblas_trmm( CBLAS_ORDER order,
       CBLAS_SIDE Side,  CBLAS_UPLO Uplo,
       CBLAS_TRANSPOSE TransA,  CBLAS_DIAG Diag,
       INTT M,  INTT N,  T alpha,
       T*A,  INTT lda,T *B,  INTT ldb);

// interfaces to a few functions from the intel Vector Mathematical Library
/// interface to v*Sqr
template <typename T> void vSqrt( int n,  T* vecIn, T* vecOut);
/// interface to v*Sqr
template <typename T> void vInvSqrt( int n,  T* vecIn, T* vecOut);
/// interface to v*Sqr
template <typename T> void vSqr( int n,  T* vecIn, T* vecOut);
/// interface to v*Sub
template <typename T> void vSub( int n,  T* vecIn,  T* vecIn2, T* vecOut);
/// interface to v*Div
template <typename T> void vDiv( int n,  T* vecIn,  T* vecIn2, T* vecOut);
/// interface to v*Exp
template <typename T> void vExp( int n,  T* vecIn, T* vecOut);
/// interface to v*Inv
template <typename T> void vInv( int n,  T* vecIn, T* vecOut);
/// interface to v*Add
template <typename T> void vAdd( int n,  T* vecIn,  T* vecIn2, T* vecOut);
/// interface to v*Mul
template <typename T> void vMul( int n,  T* vecIn,  T* vecIn2, T* vecOut);
/// interface to v*Abs
template <typename T> void vAbs( int n,  T* vecIn, T* vecOut);

// interfaces to a few LAPACK functions
/// interface to *trtri
template <typename T> void trtri(char& uplo, char& diag,
      INTT n, T * a, INTT lda);
/// interface to *sytri  // call sytrf
template <typename T> void sytri(char& uplo, INTT n, T* a, INTT lda);
//, INTT* ipiv,
//      T* work);
/// interaface to *lasrt
template <typename T> void lasrt(char& id,  INTT n, T *d);
//template <typename T> void lasrt2(char& id,  INTT& n, T *d, int* key);
template <typename T> void gesvd( char& jobu, char& jobvt, INTT m,
      INTT n, T* a, INTT lda, T* s,
      T* u, INTT ldu, T* vt, INTT ldvt);
template <typename T> void syev( char& jobz, char& uplo, INTT n,
         T* a, INTT lda, T* w);

/* ******************
 * Implementations
 * *****************/

extern "C" {
   double dnrm2_(INTT *n,double *x,INTT *incX);
   float snrm2_(INTT *n,float *x,INTT *incX);
   void dcopy_(INTT *n,double *x,INTT *incX, double *y,INTT *incY);
   void scopy_(INTT *n,float *x,INTT *incX, float *y,INTT *incY);
   void daxpy_(INTT *n,double* a, double *x,INTT *incX, double *y,INTT *incY);
   void saxpy_(INTT *n,float* a, float *x,INTT *incX, float *y,INTT *incY);
   void dscal_(INTT *n,double* a, double *x,INTT *incX);
   void sscal_(INTT *n,float* a, float *x,INTT *incX);
   double dasum_(INTT *n,double *x,INTT *incX);
   float sasum_(INTT *n,float *x,INTT *incX);
   double ddot_(INTT *n,double *x,INTT *incX, double *y,INTT *incY);
   float sdot_(INTT *n,float *x,INTT *incX, float *y,INTT *incY);
   void dgemv_(char   *trans, INTT *m, INTT *n, double *alpha, double *a,
         INTT *lda, double *x, INTT *incx, double *beta, double *y,INTT *incy);
   void sgemv_(char   *trans, INTT *m, INTT *n, float *alpha, float *a,
         INTT *lda, float *x, INTT *incx, float *beta, float *y,INTT *incy);
   void dger_(INTT *m, INTT *n, double *alpha, double *x, INTT *incx,
         double *y, INTT *incy, double *a, INTT *lda);
   void sger_(INTT *m, INTT *n, float *alpha, float *x, INTT *incx,
         float *y, INTT *incy, float *a, INTT *lda);
   void dtrmv_(char *uplo, char *trans, char   *diag, INTT *n, double *a,
         INTT *lda, double *x, INTT *incx);
   void strmv_(char *uplo, char *trans, char   *diag, INTT *n, float *a,
         INTT *lda, float *x, INTT *incx);
   void dsyr_(char *uplo, INTT *n, double *alpha, double *x, INTT *incx,
         double *a, INTT *lda);
   void ssyr_(char *uplo, INTT *n, float *alpha, float *x, INTT *incx,
         float *a, INTT *lda);
   void dsymv_(char   *uplo, INTT *n, double *alpha, double *a, INTT *lda,
         double *x, INTT *incx, double *beta, double *y, INTT *incy);
   void ssymv_(char   *uplo, INTT *n, float *alpha, float *a, INTT *lda,
         float *x, INTT *incx, float *beta, float *y, INTT *incy);
   void dgemm_(char *transa, char *transb, INTT *m, INTT *n, INTT *k,
         double *alpha, double *a, INTT *lda, double *b, INTT *ldb, double *beta,
         double *c, INTT *ldc);
   void sgemm_(char *transa, char *transb, INTT *m, INTT *n, INTT *k,
         float *alpha, float *a, INTT *lda, float *b, INTT *ldb, float *beta,
         float *c, INTT *ldc);
   void dsyrk_(char *uplo, char *trans, INTT *n, INTT *k, double *alpha,
         double *a, INTT *lda, double *beta, double *c, INTT *ldc);
   void ssyrk_(char *uplo, char *trans, INTT *n, INTT *k, float *alpha,
         float *a, INTT *lda, float *beta, float *c, INTT *ldc);
   void dtrmm_(char *side,char *uplo,char *transa, char *diag, INTT *m,
         INTT *n, double *alpha, double *a, INTT *lda, double *b,
         INTT *ldb);
   void strmm_(char *side,char *uplo,char *transa, char *diag, INTT *m,
         INTT *n, float *alpha, float *a, INTT *lda, float *b,
         INTT *ldb);
   INTT idamax_(INTT *n, double *dx, INTT *incx);
   INTT isamax_(INTT *n, float *dx, INTT *incx);
   void dtrtri_(char* uplo, char* diag, INTT* n, double * a, INTT* lda,
         INTT* info);
   void strtri_(char* uplo, char* diag, INTT* n, float * a, INTT* lda,
         INTT* info);
   void dsytrf_(char* uplo, INTT* n, double* a, INTT* lda, INTT* ipiv,
         double* work, INTT* lwork, INTT* info);
   void ssytrf_(char* uplo, INTT* n, float* a, INTT* lda, INTT* ipiv,
         float* work, INTT* lwork, INTT* info);
   void dsytri_(char* uplo, INTT* n, double* a, INTT* lda, INTT* ipiv,
         double* work, INTT* info);
   void ssytri_(char* uplo, INTT* n, float* a, INTT* lda, INTT* ipiv,
         float* work, INTT* info);
   void dlasrt_(char* id,  INTT* n, double *d, INTT* info);
   void slasrt_(char* id,  INTT* n, float*d, INTT* info);
   void dgesvd_(char*jobu, char *jobvt, INTT *m, INTT *n, double *a,
         INTT *lda, double *s, double *u, INTT *ldu, double *vt,
         INTT *ldvt, double *work, INTT *lwork, INTT *info);
   void sgesvd_(char*jobu, char *jobvt, INTT *m, INTT *n, float *a,
         INTT *lda, float *s, float *u, INTT *ldu, float *vt,
         INTT *ldvt, float *work, INTT *lwork, INTT *info);
   void dsyev_(char *jobz, char *uplo, INTT *n, double *a, INTT *lda,
         double *w, double *work, INTT *lwork, INTT *info);
   void ssyev_(char *jobz, char *uplo, INTT *n, float *a, INTT *lda,
         float *w, float *work, INTT *lwork, INTT *info);
}

// Implementations of the INTTerfaces, BLAS Level 1
/// Implementation of the INTTerface for cblas_dnrm2
template <> inline double cblas_nrm2<double>( INTT n,  double* X,
       INTT incX) {
   //return cblas_dnrm2(n,X,incX);
   return dnrm2_(&n,X,&incX);
};
/// Implementation of the INTTerface for cblas_snrm2
template <> inline float cblas_nrm2<float>( INTT n,  float* X,
       INTT incX) {
   //return cblas_snrm2(n,X,incX);
   return snrm2_(&n,X,&incX);
};
/// Implementation of the INTTerface for cblas_dcopy
template <> inline void cblas_copy<double>( INTT n,  double* X,
       INTT incX, double* Y,  INTT incY) {
   //cblas_dcopy(n,X,incX,Y,incY);
   dcopy_(&n,X,&incX,Y,&incY);
};
/// Implementation of the INTTerface for cblas_scopy
template <> inline void cblas_copy<float>( INTT n,  float* X,  INTT incX,
      float* Y,  INTT incY) {
   //cblas_scopy(n,X,incX,Y,incY);
   scopy_(&n,X,&incX,Y,&incY);
};
/// Implementation of the INTTerface for cblas_scopy
template <> inline void cblas_copy<int>( INTT n,  int* X,  INTT incX,
      int* Y,  INTT incY) {
   for (int i = 0; i<n; ++i)
      Y[incY*i]=X[incX*i];
};
/// Implementation of the INTTerface for cblas_scopy
template <> inline void cblas_copy<bool>( INTT n,  bool* X,  INTT incX,
      bool* Y,  INTT incY) {
   for (int i = 0; i<n; ++i)
      Y[incY*i]=X[incX*i];
};

/// Implementation of the INTTerface for cblas_daxpy
template <> inline void cblas_axpy<double>( INTT n,  double a,  double* X,
       INTT incX, double* Y,  INTT incY) {
   //cblas_daxpy(n,a,X,incX,Y,incY);
   daxpy_(&n,&a,X,&incX,Y,&incY);
};
/// Implementation of the INTTerface for cblas_saxpy
template <> inline void cblas_axpy<float>( INTT n,  float a,  float* X,
       INTT incX, float* Y,  INTT incY) {
   //cblas_saxpy(n,a,X,incX,Y,incY);
   saxpy_(&n,&a,X,&incX,Y,&incY);
};

/// Implementation of the INTTerface for cblas_saxpy
template <> inline void cblas_axpy<int>( INTT n,  int a,  int* X,
       INTT incX, int* Y,  INTT incY) {
   for (int i = 0; i<n; ++i)
      Y[i] += a*X[i];
};

/// Implementation of the INTTerface for cblas_saxpy
template <> inline void cblas_axpy<bool>( INTT n,  bool a,  bool* X,
       INTT incX, bool* Y,  INTT incY) {
   for (int i = 0; i<n; ++i)
      Y[i] = a*X[i];
};

/// Implementation of the INTTerface for cblas_dscal
template <> inline void cblas_scal<double>( INTT n,  double a, double* X,
       INTT incX) {
   //cblas_dscal(n,a,X,incX);
   dscal_(&n,&a,X,&incX);
};
/// Implementation of the INTTerface for cblas_sscal
template <> inline void cblas_scal<float>( INTT n,  float a, float* X,
       INTT incX) {
   //cblas_sscal(n,a,X,incX);
   sscal_(&n,&a,X,&incX);
};
/// Implementation of the INTTerface for cblas_sscal
template <> inline void cblas_scal<int>( INTT n,  int a, int* X,
       INTT incX) {
   for (int i = 0; i<n; ++i) X[i*incX]*=a;
};
/// Implementation of the INTTerface for cblas_sscal
template <> inline void cblas_scal<bool>( INTT n,  bool a, bool* X,
       INTT incX) {
   /// not implemented
};

/// Implementation of the INTTerface for cblas_dasum
template <> inline double cblas_asum<double>( INTT n,  double* X,  INTT incX) {
   //return cblas_dasum(n,X,1);
   return dasum_(&n,X,&incX);
};
/// Implementation of the INTTerface for cblas_sasum
template <> inline float cblas_asum<float>( INTT n,  float* X,  INTT incX) {
   //return cblas_sasum(n,X,1);
   return sasum_(&n,X,&incX);
};
/// Implementation of the INTTerface for cblas_ddot
template <> inline double cblas_dot<double>( INTT n,  double* X,
       INTT incX,  double* Y, INTT incY) {
   //return cblas_ddot(n,X,incX,Y,incY);
   return ddot_(&n,X,&incX,Y,&incY);
};
/// Implementation of the INTTerface for cblas_sdot
template <> inline float cblas_dot<float>( INTT n,  float* X,
       INTT incX,  float* Y, INTT incY) {
   //return cblas_sdot(n,X,incX,Y,incY);
   return sdot_(&n,X,&incX,Y,&incY);
};
template <> inline int cblas_dot<int>( INTT n,  int* X,
       INTT incX,  int* Y, INTT incY) {
   int total=0;
   int i,j;
   j=0;
   for (i = 0; i<n; ++i) {
      total+=X[i*incX]*Y[j];
      //j+=incY;
      j+=(int)incY;
   }
   return total;
};
/// Implementation of the INTTerface for cblas_sdot
template <> inline bool cblas_dot<bool>( INTT n,  bool* X,
       INTT incX,  bool* Y, INTT incY) {
   /// not implemented
   return true;
};

// Implementations of the INTTerfaces, BLAS Level 2
///  Implementation of the INTTerface for cblas_dgemv
template <> inline void cblas_gemv<double>( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  INTT M,  INTT N,
       double alpha,  double *A,  INTT lda,
       double *X,  INTT incX,  double beta,
      double *Y,  INTT incY) {
   //cblas_dgemv(order,TransA,M,N,alpha,A,lda,X,incX,beta,Y,incY);
   dgemv_(cblas_transpose(TransA),&M,&N,&alpha,A,&lda,X,&incX,&beta,Y,&incY);
};
///  Implementation of the INTTerface for cblas_sgemv
template <> inline void cblas_gemv<float>( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  INTT M,  INTT N,
       float alpha,  float *A,  INTT lda,
       float *X,  INTT incX,  float beta,
      float *Y,  INTT incY) {
   //cblas_sgemv(order,TransA,M,N,alpha,A,lda,X,incX,beta,Y,incY);
   sgemv_(cblas_transpose(TransA),&M,&N,&alpha,A,&lda,X,&incX,&beta,Y,&incY);
};
///  Implementation of the INTTerface for cblas_sgemv
template <> inline void cblas_gemv<int>( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  INTT M,  INTT N,
       int alpha,  int *A,  INTT lda,
       int *X,  INTT incX,  int beta,
      int *Y,  INTT incY) {
   ///  not implemented
};
///  Implementation of the INTTerface for cblas_sgemv
template <> inline void cblas_gemv<bool>( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  INTT M,  INTT N,
       bool alpha,  bool *A,  INTT lda,
       bool *X,  INTT incX,  bool beta,
      bool *Y,  INTT incY) {
   /// not implemented
};

///  Implementation of the INTTerface for cblas_dger
template <> inline void cblas_ger<double>( CBLAS_ORDER order,
       INTT M,  INTT N,  double alpha,  double *X,  INTT incX,
       double* Y,  INTT incY, double *A,  INTT lda) {
   //cblas_dger(order,M,N,alpha,X,incX,Y,incY,A,lda);
   dger_(&M,&N,&alpha,X,&incX,Y,&incY,A,&lda);
};
///  Implementation of the INTTerface for cblas_sger
template <> inline void cblas_ger<float>( CBLAS_ORDER order,
       INTT M,  INTT N,  float alpha,  float *X,  INTT incX,
       float* Y,  INTT incY, float *A,  INTT lda) {
   //cblas_sger(order,M,N,alpha,X,incX,Y,incY,A,lda);
   sger_(&M,&N,&alpha,X,&incX,Y,&incY,A,&lda);
};
///  Implementation of the INTTerface for cblas_dtrmv
template <> inline void cblas_trmv<double>( CBLAS_ORDER order,  CBLAS_UPLO Uplo,
       CBLAS_TRANSPOSE TransA,  CBLAS_DIAG Diag,  INTT N,
       double *A,  INTT lda, double *X,  INTT incX) {
   //cblas_dtrmv(order,Uplo,TransA,Diag,N,A,lda,X,incX);
   dtrmv_(cblas_uplo(Uplo),cblas_transpose(TransA),cblas_diag(Diag),&N,A,&lda,X,&incX);
};
///  Implementation of the INTTerface for cblas_strmv
template <> inline void cblas_trmv<float>( CBLAS_ORDER order,  CBLAS_UPLO Uplo,
       CBLAS_TRANSPOSE TransA,  CBLAS_DIAG Diag,  INTT N,
       float *A,  INTT lda, float *X,  INTT incX) {
   //cblas_strmv(order,Uplo,TransA,Diag,N,A,lda,X,incX);
   strmv_(cblas_uplo(Uplo),cblas_transpose(TransA),cblas_diag(Diag),&N,A,&lda,X,&incX);
};
/// Implementation of cblas_dsyr
template <> inline void cblas_syr( CBLAS_ORDER order,
        CBLAS_UPLO Uplo,
       INTT N,  double alpha,  double*X,
       INTT incX, double *A,  INTT lda) {
   //cblas_dsyr(order,Uplo,N,alpha,X,incX,A,lda);
   dsyr_(cblas_uplo(Uplo),&N,&alpha,X,&incX,A,&lda);
};
/// Implementation of cblas_ssyr
template <> inline void cblas_syr( CBLAS_ORDER order,
        CBLAS_UPLO Uplo,
       INTT N,  float alpha,  float*X,
       INTT incX, float *A,  INTT lda) {
   //cblas_ssyr(order,Uplo,N,alpha,X,incX,A,lda);
   ssyr_(cblas_uplo(Uplo),&N,&alpha,X,&incX,A,&lda);
};
/// Implementation of cblas_ssymv
template <> inline void cblas_symv( CBLAS_ORDER order,
       CBLAS_UPLO Uplo,  INTT N,
       float alpha,  float *A,  INTT lda,  float *X,
       INTT incX,  float beta,float *Y,   INTT incY) {
   //cblas_ssymv(order,Uplo,N,alpha,A,lda,X,incX,beta,Y,incY);
   ssymv_(cblas_uplo(Uplo),&N,&alpha,A,&lda,X,&incX,&beta,Y,&incY);
}
/// Implementation of cblas_dsymv
template <> inline void cblas_symv( CBLAS_ORDER order,
       CBLAS_UPLO Uplo,  INTT N,
       double alpha,  double *A,  INTT lda,  double *X,
       INTT incX,  double beta,double *Y,   INTT incY) {
   //cblas_dsymv(order,Uplo,N,alpha,A,lda,X,incX,beta,Y,incY);
   dsymv_(cblas_uplo(Uplo),&N,&alpha,A,&lda,X,&incX,&beta,Y,&incY);
}

// Implementations of the INTTerfaces, BLAS Level 3
///  Implementation of the INTTerface for cblas_dgemm
template <> inline void cblas_gemm<double>( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  CBLAS_TRANSPOSE TransB,
       INTT M,  INTT N,  INTT K,  double alpha,
       double *A,  INTT lda,  double *B,  INTT ldb,
       double beta, double *C,  INTT ldc) {
   //cblas_dgemm(Order,TransA,TransB,M,N,K,alpha,A,lda,B,ldb,beta,C,ldc);
   dgemm_(cblas_transpose(TransA),cblas_transpose(TransB),&M,&N,&K,&alpha,A,&lda,B,&ldb,&beta,C,&ldc);
};
///  Implementation of the INTTerface for cblas_sgemm
template <> inline void cblas_gemm<float>( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  CBLAS_TRANSPOSE TransB,
       INTT M,  INTT N,  INTT K,  float alpha,
       float *A,  INTT lda,  float *B,  INTT ldb,
       float beta, float *C,  INTT ldc) {
   //cblas_sgemm(Order,TransA,TransB,M,N,K,alpha,A,lda,B,ldb,beta,C,ldc);
   sgemm_(cblas_transpose(TransA),cblas_transpose(TransB),&M,&N,&K,&alpha,A,&lda,B,&ldb,&beta,C,&ldc);
};
template <> inline void cblas_gemm<int>( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  CBLAS_TRANSPOSE TransB,
       INTT M,  INTT N,  INTT K,  int alpha,
       int *A,  INTT lda,  int *B,  INTT ldb,
       int beta, int *C,  INTT ldc) {
   /// not implemented
};
///  Implementation of the INTTerface for cblas_sgemm
template <> inline void cblas_gemm<bool>( CBLAS_ORDER order,
       CBLAS_TRANSPOSE TransA,  CBLAS_TRANSPOSE TransB,
       INTT M,  INTT N,  INTT K,  bool alpha,
       bool *A,  INTT lda,  bool *B,  INTT ldb,
       bool beta, bool *C,  INTT ldc) {
   /// not implemented
};

///  Implementation of the INTTerface for cblas_dsyrk
template <> inline void cblas_syrk<double>( CBLAS_ORDER order,
       CBLAS_UPLO Uplo,  CBLAS_TRANSPOSE Trans,  INTT N,  INTT K,
       double alpha,  double *A,  INTT lda,
       double beta, double *C,  INTT ldc) {
   //cblas_dsyrk(Order,Uplo,Trans,N,K,alpha,A,lda,beta,C,ldc);
   dsyrk_(cblas_uplo(Uplo),cblas_transpose(Trans),&N,&K,&alpha,A,&lda,&beta,C,&ldc);
};
///  Implementation of the INTTerface for cblas_ssyrk
template <> inline void cblas_syrk<float>( CBLAS_ORDER order,
       CBLAS_UPLO Uplo,  CBLAS_TRANSPOSE Trans,  INTT N,  INTT K,
       float alpha,  float *A,  INTT lda,
       float beta, float *C,  INTT ldc) {
   //cblas_ssyrk(Order,Uplo,Trans,N,K,alpha,A,lda,beta,C,ldc);
   ssyrk_(cblas_uplo(Uplo),cblas_transpose(Trans),&N,&K,&alpha,A,&lda,&beta,C,&ldc);
};
///  Implementation of the INTTerface for cblas_ssyrk
template <> inline void cblas_syrk<int>( CBLAS_ORDER order,
       CBLAS_UPLO Uplo,  CBLAS_TRANSPOSE Trans,  INTT N,  INTT K,
       int alpha,  int *A,  INTT lda,
       int beta, int *C,  INTT ldc) {
   /// not implemented
};
///  Implementation of the INTTerface for cblas_ssyrk
template <> inline void cblas_syrk<bool>( CBLAS_ORDER order,
       CBLAS_UPLO Uplo,  CBLAS_TRANSPOSE Trans,  INTT N,  INTT K,
       bool alpha,  bool *A,  INTT lda,
       bool beta, bool *C,  INTT ldc) {
   /// not implemented
};

///  Implementation of the INTTerface for cblas_dtrmm
template <> inline void cblas_trmm<double>( CBLAS_ORDER order,
       CBLAS_SIDE Side,  CBLAS_UPLO Uplo,
       CBLAS_TRANSPOSE TransA,  CBLAS_DIAG Diag,
       INTT M,  INTT N,  double alpha,
       double *A,  INTT lda,double *B,  INTT ldb) {
   //cblas_dtrmm(Order,Side,Uplo,TransA,Diag,M,N,alpha,A,lda,B,ldb);
   dtrmm_(cblas_side(Side),cblas_uplo(Uplo),cblas_transpose(TransA),cblas_diag(Diag),&M,&N,&alpha,A,&lda,B,&ldb);
};
///  Implementation of the INTTerface for cblas_strmm
template <> inline void cblas_trmm<float>( CBLAS_ORDER order,
       CBLAS_SIDE Side,  CBLAS_UPLO Uplo,
       CBLAS_TRANSPOSE TransA,  CBLAS_DIAG Diag,
       INTT M,  INTT N,  float alpha,
       float *A,  INTT lda,float *B,  INTT ldb) {
   //cblas_strmm(Order,Side,Uplo,TransA,Diag,M,N,alpha,A,lda,B,ldb);
   strmm_(cblas_side(Side),cblas_uplo(Uplo),cblas_transpose(TransA),cblas_diag(Diag),&M,&N,&alpha,A,&lda,B,&ldb);
};
///  Implementation of the interface for cblas_idamax
template <> inline int cblas_iamax<double>( INTT n,  double* X,
       INTT incX) {
   //return cblas_idamax(n,X,incX);
   return static_cast<int>(idamax_(&n,X,&incX)-1);
};
///  Implementation of the interface for cblas_isamax
template <> inline int cblas_iamax<float>( INTT n,  float* X,
       INTT incX) {
   //return cblas_isamax(n,X,incX);
   return static_cast<int>(isamax_(&n,X,&incX)-1);
};

// Implementations of the interfaces, LAPACK
/// Implemenation of the interface for dtrtri
template <> inline void trtri<double>(char& uplo, char& diag,
      INTT n, double * a, INTT lda) {
   //dtrtri_(&uplo,&diag,&n,a,&lda,&info);
   dtrtri_(&uplo,&diag,&n,a,&lda,&info);
};
/// Implemenation of the interface for strtri
template <> inline void trtri<float>(char& uplo, char& diag,
      INTT n, float* a, INTT lda) {
   //strtri_(&uplo,&diag,&n,a,&lda,&info);
   strtri_(&uplo,&diag,&n,a,&lda,&info);
};

/// Implemenation of the interface for dsytri
template <> inline void sytri<double>(char& uplo, INTT n, double* a, INTT lda) {
//, INTT* ipiv, double* work) {
   //dsytri_(&uplo,&n,a,&lda,ipiv,work,&info);
   INTT lwork=-1;
   INTT* ipiv= new INTT[n];
   double* query, *work;
   query = new double[1];
   dsytrf_(&uplo,&n,a,&lda,ipiv,query,&lwork,&info);
   lwork=static_cast<INTT>(*query);
   delete[](query);
   work = new double[static_cast<int>(lwork)];
   dsytrf_(&uplo,&n,a,&lda,ipiv,work,&lwork,&info);
   delete[](work);
   work = new double[static_cast<int>(2*n)];
   dsytri_(&uplo,&n,a,&lda,ipiv,work,&info);
   delete[](work);
   delete[](ipiv);
};
/// Implemenation of the interface for ssytri
template <> inline void sytri<float>(char& uplo, INTT n, float* a, INTT lda) {
   INTT lwork=-1;
   INTT* ipiv= new INTT[n];
   float* query, *work;
   query = new float[1];
   ssytrf_(&uplo,&n,a,&lda,ipiv,query,&lwork,&info);
   lwork=static_cast<INTT>(*query);
   delete[](query);
   work = new float[static_cast<int>(lwork)];
   ssytrf_(&uplo,&n,a,&lda,ipiv,work,&lwork,&info);
   delete[](work);
   work = new float[static_cast<int>(2*n)];
   ssytri_(&uplo,&n,a,&lda,ipiv,work,&info);
   delete[](work);
   delete[](ipiv);
};
/// interaface to *lasrt
template <> inline void lasrt(char& id, INTT n, double *d) {
   //dlasrt_(&id,const_cast<int*>(&n),d,&info);
   dlasrt_(&id,&n,d,&info);
};
/// interaface to *lasrt
template <> inline void lasrt(char& id, INTT n, float *d) {
   //slasrt_(&id,const_cast<int*>(&n),d,&info);
   slasrt_(&id,&n,d,&info);
};
//template <> inline void lasrt2(char& id, INTT& n, double *d,int* key) {
//   //dlasrt2_(&id,const_cast<int*>(&n),d,key,&info);
//   dlasrt2(&id,&n,d,key,&info);
//};
///// interaface to *lasrt
//template <> inline void lasrt2(char& id, INTT& n, float *d, int* key) {
//   //slasrt2_(&id,const_cast<int*>(&n),d,key,&info);
//   slasrt2(&id,&n,d,key,&info);
//};
template <> void inline gesvd( char& jobu, char& jobvt, INTT m,
      INTT n, double* a, INTT lda, double* s,
      double* u, INTT ldu, double* vt, INTT ldvt) {
   double* query = new double[1];
   INTT lwork=-1;
   dgesvd_(&jobu, &jobvt, &m, &n, a, &lda, s, u, &ldu, vt, &ldvt,
         query, &lwork, &info );
   lwork=static_cast<INTT>(*query);
   delete[](query);
   double* work = new double[static_cast<int>(lwork)];
   dgesvd_(&jobu, &jobvt, &m, &n, a, &lda, s, u, &ldu, vt, &ldvt,
         work, &lwork, &info );
   delete[](work);
}
template <> void inline gesvd( char& jobu, char& jobvt, INTT m,
      INTT n, float* a, INTT lda, float* s,
      float* u, INTT ldu, float* vt, INTT ldvt) {
   float* query = new float[1];
   INTT lwork=-1;
   sgesvd_(&jobu, &jobvt, &m, &n, a, &lda, s, u, &ldu, vt, &ldvt,
         query, &lwork, &info );
   lwork=static_cast<INTT>(*query);
   delete[](query);
   float* work = new float[static_cast<int>(lwork)];
   sgesvd_(&jobu, &jobvt, &m, &n, a, &lda, s, u, &ldu, vt, &ldvt,
         work, &lwork, &info );
   delete[](work);
}

template <> void inline syev( char& jobz, char& uplo, INTT n,
         float* a, INTT lda, float* w) {
   float* query = new float[1];
   INTT lwork=-1;
   ssyev_(&jobz,&uplo,&n,a,&lda,w,query,&lwork,&info);
   lwork=static_cast<INTT>(*query);
   delete[](query);
   float* work = new float[static_cast<int>(lwork)];
   ssyev_(&jobz,&uplo,&n,a,&lda,w,work,&lwork,&info);
   delete[](work);
};

template <> void inline syev( char& jobz, char& uplo, INTT n,
         double* a, INTT lda, double* w) {
   double* query = new double[1];
   INTT lwork=-1;
   dsyev_(&jobz,&uplo,&n,a,&lda,w,query,&lwork,&info);
   lwork=static_cast<INTT>(*query);
   delete[](query);
   double* work = new double[static_cast<int>(lwork)];
   dsyev_(&jobz,&uplo,&n,a,&lda,w,work,&lwork,&info);
   delete[](work);
};

/// If the MKL is not present, a slow implementation is used instead.
#ifdef HAVE_MKL
/// Implemenation of the interface for vdSqr
template <> inline void vSqr<double>( int n,  double* vecIn,
      double* vecOut) {
   vdSqr(n,vecIn,vecOut);
};
/// Implemenation of the interface for vsSqr
template <> inline void vSqr<float>( int n,  float* vecIn,
      float* vecOut) {
   vsSqr(n,vecIn,vecOut);
};
template <> inline void vSqrt<double>( int n,  double* vecIn,
      double* vecOut) {
   vdSqrt(n,vecIn,vecOut);
};
/// Implemenation of the interface for vsSqr
template <> inline void vSqrt<float>( int n,  float* vecIn,
      float* vecOut) {
   vsSqrt(n,vecIn,vecOut);
};
template <> inline void vInvSqrt<double>( int n,  double* vecIn,
      double* vecOut) {
   vdInvSqrt(n,vecIn,vecOut);
};
/// Implemenation of the interface for vsSqr
template <> inline void vInvSqrt<float>( int n,  float* vecIn,
      float* vecOut) {
   vsInvSqrt(n,vecIn,vecOut);
};

/// Implemenation of the interface for vdSub
template <> inline void vSub<double>( int n,  double* vecIn,
       double* vecIn2, double* vecOut) {
   vdSub(n,vecIn,vecIn2,vecOut);
};
/// Implemenation of the interface for vsSub
template <> inline void vSub<float>( int n,  float* vecIn,
       float* vecIn2, float* vecOut) {
   vsSub(n,vecIn,vecIn2,vecOut);
};
/// Implemenation of the interface for vdDiv
template <> inline void vDiv<double>( int n,  double* vecIn,
       double* vecIn2, double* vecOut) {
   vdDiv(n,vecIn,vecIn2,vecOut);
};
/// Implemenation of the interface for vsDiv
template <> inline void vDiv<float>( int n,  float* vecIn,
       float* vecIn2, float* vecOut) {
   vsDiv(n,vecIn,vecIn2,vecOut);
};
/// Implemenation of the interface for vdExp
template <> inline void vExp<double>( int n,  double* vecIn,
      double* vecOut) {
   vdExp(n,vecIn,vecOut);
};
/// Implemenation of the interface for vsExp
template <> inline void vExp<float>( int n,  float* vecIn,
      float* vecOut) {
   vsExp(n,vecIn,vecOut);
};
/// Implemenation of the interface for vdInv
template <> inline void vInv<double>( int n,  double* vecIn,
      double* vecOut) {
   vdInv(n,vecIn,vecOut);
};
/// Implemenation of the interface for vsInv
template <> inline void vInv<float>( int n,  float* vecIn,
      float* vecOut) {
   vsInv(n,vecIn,vecOut);
};
/// Implemenation of the interface for vdAdd
template <> inline void vAdd<double>( int n,  double* vecIn,
       double* vecIn2, double* vecOut) {
   vdAdd(n,vecIn,vecIn2,vecOut);
};
/// Implemenation of the interface for vsAdd
template <> inline void vAdd<float>( int n,  float* vecIn,
       float* vecIn2, float* vecOut) {
   vsAdd(n,vecIn,vecIn2,vecOut);
};
/// Implemenation of the interface for vdMul
template <> inline void vMul<double>( int n,  double* vecIn,
       double* vecIn2, double* vecOut) {
   vdMul(n,vecIn,vecIn2,vecOut);
};
/// Implemenation of the interface for vsMul
template <> inline void vMul<float>( int n,  float* vecIn,
       float* vecIn2, float* vecOut) {
   vsMul(n,vecIn,vecIn2,vecOut);
};

/// interface to vdAbs
template <> inline void vAbs( int n,  double* vecIn,
      double* vecOut) {
   vdAbs(n,vecIn,vecOut);
};
/// interface to vdAbs
template <> inline void vAbs( int n,  float* vecIn,
      float* vecOut) {
   vsAbs(n,vecIn,vecOut);
};

/// implemenation of the interface of the non-offical blas, level 1 function
/// cblas_idamin
template <> inline int cblas_iamin<double>( int n,  double* x,
       int incx) {
   return (int) cblas_idamin(n,x,incx);
};
/// implemenation of the interface of the non-offical blas, level 1 function
/// cblas_isamin
template <> inline int cblas_iamin<float>( int n,  float* x,
       int incx) {
   return (int) cblas_isamin(n,x,incx);
};
/// slow alternative implementation of some MKL function
#else
/// Slow implementation of vdSqr and vsSqr
template <typename T> inline void vSqr( int n,  T* vecIn, T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=vecIn[i]*vecIn[i];
};
template <typename T> inline void vSqrt( int n,  T* vecIn, T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=sqr<T>(vecIn[i]);
};
template <typename T> inline void vInvSqrt( int n,  T* vecIn, T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=T(1.0)/sqr<T>(vecIn[i]);
};

/// Slow implementation of vdSub and vsSub
template <typename T> inline void vSub( int n,  T* vecIn1,
       T* vecIn2, T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=vecIn1[i]-vecIn2[i];
};
/// Slow implementation of vdInv and vsInv
template <typename T> inline void vInv( int n,  T* vecIn, T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=1.0/vecIn[i];
};
/// Slow implementation of vdExp and vsExp
template <typename T> inline void vExp( int n,  T* vecIn, T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=exp(vecIn[i]);
};
/// Slow implementation of vdAdd and vsAdd
template <typename T> inline void vAdd( int n,  T* vecIn1,
       T* vecIn2, T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=vecIn1[i]+vecIn2[i];
};
/// Slow implementation of vdMul and vsMul
template <typename T> inline void vMul( int n,  T* vecIn1,
       T* vecIn2, T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=vecIn1[i]*vecIn2[i];
};
/// Slow implementation of vdDiv and vsDiv
template <typename T> inline void vDiv( int n,  T* vecIn1,
       T* vecIn2, T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=vecIn1[i]/vecIn2[i];
};
/// Slow implementation of vAbs
template <typename T> inline void vAbs( int n,  T* vecIn,
      T* vecOut) {
   for (int i = 0; i<n; ++i) vecOut[i]=abs<T>(vecIn[i]);
};

/// Slow implementation of cblas_idamin and cblas_isamin
template <typename T> int inline cblas_iamin(INTT n, T* X, INTT incX) {
   int imin=0;
   double min=fabs(X[0]);
   for (int j = 1; j<n; j+=incX) {
      double cur = fabs(X[j]);
      if (cur < min) {
         imin=j;
         min = cur;
      }
   }
   return imin;
}
#endif

#endif


#include <gsl/span>

namespace engine
{
	template <class ElementType, std::ptrdiff_t Extent = gsl::dynamic_extent>
	using span = gsl::span<ElementType, Extent>;
}


#pragma once

#include <gsl\gsl>
#include <winrt\Windows.Foundation.h>
#include <d3d11.h>
#include <map>
#include "DrawableGameComponent.h"
#include "FullScreenRenderTarget.h"
#include "FullScreenQuad.h"

namespace Library
{
	class Texture2D;
}

namespace Rendering
{
	enum class DistortionMaps
	{
		Glass,
		Text,
		NoDistortion,
		End
	};

	class DiffuseLightingDemo;

	class DistortionMappingDemo final : public Library::DrawableGameComponent
	{
	public:
		DistortionMappingDemo(Library::Game& game, const std::shared_ptr<Library::Camera>& camera);
		DistortionMappingDemo(const DistortionMappingDemo&) = delete;
		DistortionMappingDemo(DistortionMappingDemo&&) = default;
		DistortionMappingDemo& operator=(const DistortionMappingDemo&) = default;
		DistortionMappingDemo& operator=(DistortionMappingDemo&&) = default;
		~DistortionMappingDemo();

		std::shared_ptr<DiffuseLightingDemo> DiffuseLighting() const;

		float DisplacementScale() const;
		void SetDisplacementScale(float displacementScale);

		DistortionMaps DistortionMap() const;
		const std::string& DistortionMapString() const;
		void SetDistortionMap(DistortionMaps distortionMap);

		virtual void Initialize() override;
		virtual void Update(const Library::GameTime& gameTime) override;
		virtual void Draw(const Library::GameTime& gameTime) override;

	private:
		static const std::map<DistortionMaps, std::string> DistortionMapNames;

		struct PixelCBufferPerObject
		{
			float DisplacementScale{ 1.0f };
			DirectX::XMFLOAT3 Padding{ 0.0f, 0.0f, 0.0f };
		};

		std::shared_ptr<DiffuseLightingDemo> mDiffuseLightingDemo;
		Library::FullScreenRenderTarget mRenderTarget;
		Library::FullScreenQuad mFullScreenQuad;
		winrt::com_ptr<ID3D11Buffer> mPixelCBufferPerObject;
		PixelCBufferPerObject mPixelCBufferPerObjectData;
		std::map<DistortionMaps, std::shared_ptr<Library::Texture2D>> mDistortionMaps;
		DistortionMaps mActiveDistortionMap{ DistortionMaps::Glass };
	};
}


// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the LICENSE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#pragma once

#include <boost/multiprecision/cpp_int.hpp>
#include <gsl/gsl>
#include <spdlog/spdlog.h>

using uint256_t = boost::multiprecision::checked_uint256_t;
using byte = std::byte;

#include "inc/exception.h"
#include "inc/log.h"
#include "inc/string.h"

#define BETTER_ENUMS_DEFAULT_CONSTRUCTOR(Enum)                                 \
public:                                                                        \
  Enum() = default;

#define ENUM BETTER_ENUM


/*
 * This file is part of the Visual Computing Library (VCL) release under the
 * license.
 *
 * Copyright (c) 2017 Basil Fierz
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#pragma once

// VCL configuration
#include <vcl/config/global.h>

// C++ Standard library
#include <memory>
#include <string>
#include <tuple>
#include <vector>

// GSL
#include <gsl/gsl>

// Windows API
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
extern "C" {
#    include <hidsdi.h>
}

// VCL
#include <vcl/hid/device.h>

namespace Vcl { namespace HID { namespace Windows
{
	//! \note Structure definition taken from
	//! https://zfx.info/viewtopic.php?f=11&t=2977
	//! https://www.codeproject.com/Articles/297312/Minimal-Key-Logger-using-RAWINPUT
	//! https://www.codeproject.com/Articles/185522/Using-the-Raw-Input-API-to-Process-Joystick-Input
	struct Axis
	{
		//! Usage page as defined in the standard (e.g. "generic (0001)")
		USAGE usagePage;

		//! Usage of the axis as defined in the standard (e.g. "slider (0036)")
		USAGE usage;

		//! Index as defined through Hidp_GetData()
		USHORT index;

		//! Minimum value defined by the HID device
		int32_t                logicalMinimum;

		//! Maximum value defined by the HID device
		int32_t                logicalMaximum;

		//! Indicate wheter DirectInput calibration data was applied
		bool                   isCalibrated;

		//! Minimum value after calibration
		int32_t                logicalCalibratedMinimum;

		//! Maximum value after calibration
		int32_t                logicalCalibratedMaximum;

		//! Through calibration defined center value of the axis
		int32_t                logicalCalibratedCenter;

		//! Physical minimum value
		int32_t physicalMinimum;

		//! Physical maxiumum value
		int32_t physicalMaximum;

		//! Name as given by the driver
		std::wstring name;
	};

	struct Button
	{
		//! Usage page as defined in the standard (e.g. "buttons (0009)")
		USAGE usagePage;

		//! Usage of the axis as defined in the standard (e.g. "secondary (0002)")
		USAGE usage;

		//! Index as defined through Hidp_GetData()
		USHORT index;

		//! Name as given by the driver
		std::wstring name;
	};

	//! Normalize the axix value using the device configuration data
	inline float normalizeAxis(ULONG value, const Axis& axis)
	{
		if (static_cast<LONG>(value) < axis.logicalCalibratedCenter)
		{
			float range = static_cast<float>(axis.logicalCalibratedCenter - axis.logicalCalibratedMinimum);
			return (static_cast<LONG>(value) - axis.logicalCalibratedCenter) / range;
		}
		else
		{
			float range = static_cast<float>(axis.logicalCalibratedMaximum - axis.logicalCalibratedCenter);
			return (static_cast<LONG>(value) - axis.logicalCalibratedCenter) / range;
		}
	}

	class GenericHID
	{
	public:
		GenericHID(HANDLE raw_handle);

		//! Access the raw-input API handle
		//! \returns The input device handle
		HANDLE rawHandle() const { return _rawInputHandle; }

		//! Access the windows internal handle
		//! \returns The file handle to the device
		HANDLE fileHandle() const { return _fileHandle; }

		//! Access the vendor ID
		//! \returns The vendor ID
		DWORD vendorId() const { return _vendorId; }

		//! Access the product ID
		//! \returns The product ID
		DWORD productId() const { return _productId; }

		//! Read the device name from the hardware
		//! \returns The vendor defined names (vendor, product)
		auto readDeviceName() const -> std::pair<std::wstring, std::wstring>;

		const std::vector<Axis>& axes() const { return _axes; }
		const std::vector<Button>& buttons() const { return _buttons; }

		const std::vector<HIDP_VALUE_CAPS>& axisCaps() const { return _axesCaps; }
		const std::vector<HIDP_BUTTON_CAPS>& buttonCaps() const { return _buttonCaps; }

	private:
		//! Read the device capabilities
		auto readDeviceCaps() const -> std::tuple<std::vector<HIDP_BUTTON_CAPS>, std::vector<HIDP_VALUE_CAPS>>;

		//! Map buttons and fetch calibration data
		//! \param mapping Direct Input related remapping of buttons
		void readButtonCalibration(gsl::span<struct DirectInputButtonMapping> mapping) const;

		//! Map axes and fetch calibration data
		//! \param axes_caps
		void readAxisCalibration(const std::vector<HIDP_VALUE_CAPS>& axes_caps, gsl::span<struct DirectInputAxisMapping> mapping) const;

		//! Convert and store the button caps
		//! \param button_caps
		//! \param mapping Direct Input related remapping of buttons
		void storeButtons(std::vector<HIDP_BUTTON_CAPS>&& button_caps, gsl::span<struct DirectInputButtonMapping> mapping);

		//! Convert and store the axes caps
		//! \param axes_caps
		void storeAxes(std::vector<HIDP_VALUE_CAPS>&& axes_caps, gsl::span<struct DirectInputAxisMapping> mapping);

	private:
		//! Handle provided by the raw input API
		HANDLE _rawInputHandle{ nullptr };

		//! Handle from the file API
		HANDLE _fileHandle{ nullptr };

		//! Vendor ID
		DWORD _vendorId;

		//! Product ID
		DWORD _productId;

		//! Buttons associated with the device
		std::vector<Button> _buttons;

		//! Axes associated with the device
		std::vector<Axis> _axes;

		//! HID button representation
		std::vector<HIDP_BUTTON_CAPS> _buttonCaps;

		//! HID axis representation
		std::vector<HIDP_VALUE_CAPS> _axesCaps;
	};

	class AbstractHID
	{
	public:
		AbstractHID(std::unique_ptr<GenericHID> device) : _device{ std::move(device) } {}

		const GenericHID* device() const { return _device.get(); }

		virtual bool processInput(HWND window_handle, UINT input_code, PRAWINPUT raw_input) = 0;

	private:
		//! Actual hardware device implementation
		std::unique_ptr<GenericHID> _device;
	};

	template<typename JoystickType>
	class JoystickHID : public AbstractHID, public JoystickType
	{
	public:
		JoystickHID(std::unique_ptr<GenericHID> device);

		bool processInput(HWND window_handle, UINT input_code, PRAWINPUT raw_input) override;
	};

	template<typename GamepadType>
	class GamepadHID : public AbstractHID, public GamepadType
	{
	public:
		GamepadHID(std::unique_ptr<GenericHID> device);

		bool processInput(HWND window_handle, UINT input_code, PRAWINPUT raw_input) override;
	};

	template<typename ControllerType>
	class MultiAxisControllerHID : public AbstractHID, public ControllerType
	{
	public:
		MultiAxisControllerHID(std::unique_ptr<GenericHID> device);

		bool processInput(HWND window_handle, UINT input_code, PRAWINPUT raw_input) override;
	};

	class DeviceManager
	{
	public:
		DeviceManager();

		gsl::span<Device const* const> devices() const;

		//! Register devices with a specific window
		//! \param device_types Types of devices for which input should
		//!                     be processed.
		//! \param window_handle Handle to the window to which devices should be
		//!                      registered.
		void registerDevices(Flags<DeviceType> device_types, HWND window_handle);

		//! Polls all the devices instead of processing only a single device
		//! \param window_handle Handle of the window calling this method
		//! \returns True, if any device was successfully polled.
		//! \note This method is implemented according to the documenation
		//!       on MSDN. However, it seems not possible to call it.
		bool poll(HWND window_handle, UINT input_code);

		//! Process the input of a specific device
		bool processInput(HWND window_handle, UINT message, WPARAM wide_param, LPARAM low_param);

	private:
		//! List of Windows HID
		std::vector<std::unique_ptr<AbstractHID>> _devices;

		//! List of device pointers (links to `_devices`)
		std::vector<Device*> _deviceLinks;
	};
}}}


#pragma once

#include <vector>

#include <gsl/span>

#include "Mouse.h"
#include "Joystick.h"
#include "JoystickButton.h"
#include "Keyboard.h"
#include "RawInput.h"
#include "Xbox360Controller.h"

namespace qvr
{

class JoystickAxisThreshold
{
public:
	JoystickAxisThreshold(
		const qvr::JoystickAxis axis,
		const float threshold,
		const bool trueAboveThreshold)
		: mJoystickAxis(axis)
		, mThreshold(threshold)
		, mTrueAboveThreshold(trueAboveThreshold)
	{}
	JoystickAxisThreshold(
		const Xbox360Controller::Axis axis,
		const float threshold,
		const bool trueAboveThreshold)
		: JoystickAxisThreshold(
			Xbox360Controller::ToJoystickAxis(axis),
			threshold,
			trueAboveThreshold)
	{
	}
	bool IsActive(const qvr::Joystick& joystick) const
	{
		const float v = joystick.GetPosition(mJoystickAxis);
		if (mTrueAboveThreshold)
		{
			return v > mThreshold;
		}
		return v < mThreshold;
	}
	bool JustActive(const qvr::Joystick& joystick) const
	{
		const float current  = joystick.GetPosition(mJoystickAxis);
		const float previous = joystick.GetPreviousPosition(mJoystickAxis);
		if (mTrueAboveThreshold)
		{
			return current > mThreshold && previous < mThreshold;
		}
		return current < mThreshold && previous > mThreshold;
	}
private:
	const qvr::JoystickAxis mJoystickAxis;
	const float mThreshold;
	const bool mTrueAboveThreshold;
};

}

class BinaryInput
{
public:
	enum class Type
	{
		KeyboardKey,
		MouseButton,
		JoystickButton,
		JoystickAxisThreshold
	};

	BinaryInput(qvr::KeyboardKey key)
		: mType(Type::KeyboardKey)
		, mKeyboardKey(key)
	{}

	BinaryInput(qvr::MouseButton mouseButton)
		: mType(Type::MouseButton)
		, mMouseButton(mouseButton)
	{}

	BinaryInput(qvr::JoystickButton joystickButton)
		: mType(Type::JoystickButton)
		, mJoystickButton(joystickButton)
	{}

	BinaryInput(qvr::JoystickAxisThreshold joystickAxisThreshold)
		: mType(Type::JoystickAxisThreshold)
		, mJoystickAxisThreshold(joystickAxisThreshold)
	{}

	bool IsActive(const qvr::RawInputDevices& devices) const
	{
		const auto joystickIndex = qvr::JoystickIndex(0);
		const qvr::Joystick* joystick = devices.GetJoysticks().GetJoystick(joystickIndex);

		switch (mType)
		{
		case Type::KeyboardKey:
			return devices.GetKeyboard().IsDown(mKeyboardKey);
		case Type::MouseButton:
			return devices.GetMouse().IsDown(mMouseButton);
		// TODO: Which joysticks?
		case Type::JoystickButton:
			if (joystick) {
				return joystick->IsDown(mJoystickButton);
			}
			break;
		case Type::JoystickAxisThreshold:
			if (joystick) {
				return mJoystickAxisThreshold.IsActive(*joystick);
			}
			break;
		}
		return false;
	}

	bool JustActive(const qvr::RawInputDevices& devices) const
	{
		const auto joystickIndex = qvr::JoystickIndex(0);
		const qvr::Joystick* joystick = devices.GetJoysticks().GetJoystick(joystickIndex);

		switch (mType)
		{
		case Type::KeyboardKey:
			return devices.GetKeyboard().JustDown(mKeyboardKey);
		case Type::MouseButton:
			return devices.GetMouse().JustDown(mMouseButton);
			// TODO: Which joysticks?
		case Type::JoystickButton:
			if (joystick) {
				return joystick->JustDown(mJoystickButton);
			}
			break;
		case Type::JoystickAxisThreshold:
			if (joystick) {
				return mJoystickAxisThreshold.JustActive(*joystick);
			}
			break;
		}
		return false;
	}

private:
	const Type mType;

	union
	{
		const qvr::KeyboardKey mKeyboardKey;
		const qvr::MouseButton mMouseButton;
		const qvr::JoystickButton mJoystickButton;
		const qvr::JoystickAxisThreshold mJoystickAxisThreshold;
	};
};

inline bool AnyActive(const qvr::RawInputDevices& devices, const gsl::span<const BinaryInput> inputs) {
	for (const BinaryInput input : inputs) {
		if (input.IsActive(devices)) {
			return true;
		}
	}

	return false;
}

inline bool AnyJustActive(const qvr::RawInputDevices& devices, const gsl::span<const BinaryInput> inputs) {
	for (const BinaryInput input : inputs) {
		if (input.JustActive(devices)) {
			return true;
		}
	}

	return false;
}


/*
#
# Copyright (c) 2006-2012      University of Houston. All rights reserved.
# $COPYRIGHT$
#
# Additional copyrights may follow
#
# $HEADER$
#
*/

#ifndef __SL_INTERNAL__
#define __SL_INTERNAL__

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
//#include <gsl/gsl_fit.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <errno.h>
//#include <papi.h>
#ifdef MINGW
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <pwd.h>
#include <sys/utsname.h>
#endif

#include "SL_array.h"

extern fd_set SL_send_fdset;
extern fd_set SL_recv_fdset;

extern int SL_this_procid;
extern int SL_this_procport;
extern int SL_this_listensock;
extern int SL_numprocs;
extern int SL_init_numprocs;
extern int SL_proxy_numprocs;
/* Message header send before any message */
struct SL_msg_header {
    int      cmd; /* what type of message is this */
    int     from; /* id of the src process */
    int       to; /* id of the dest. process */
    int      tag; /* tag of the message */
    int  context; /* context id */
    int      len; /* Message length in bytes */
    int       id; /* Id of the last fragment */
    int loglength;
    int     temp;
};
typedef struct SL_msg_header SL_msg_header;

/* Process structure containing all relevant contact information,
   communication status etc. */
struct SL_proc;
typedef int SL_msg_comm_fnct ( struct SL_proc *dproc, int fd );

struct SL_proc {
    int                         id;
    char                 *hostname;
    int                       port;
    int                       sock;
    int                      state;
    int           connect_attempts; /* number of connect attempts */
    double    connect_start_tstamp; /* time stamp when we started to accept or connect
				       for this proc */
    double                 timeout; /* max time a process should wait before disconnecting */

    struct SL_msgq_head    *squeue; /* Send queue */
    struct SL_msgq_head    *rqueue; /* Recv queue */
    struct SL_msgq_head   *urqueue; /* Unexpected msgs queue */
    struct SL_msgq_head   *scqueue; /* Send complete queue */
    struct SL_msgq_head   *rcqueue; /* Recv complete queue */
    struct SL_qitem   *currecvelem;
    struct SL_qitem   *cursendelem;
    SL_msg_comm_fnct     *recvfunc;
    SL_msg_comm_fnct     *sendfunc;
    struct SL_msg_perf    *msgperf; /*to keep track of time and msglenth for each communication */
    struct SL_msg_perf   *insertpt;
    struct SL_network_perf *netperf;
};

struct SL_msg_perf {
	struct SL_msg_perf  *fwd;
	struct SL_msg_perf  *back;
	int 		   msglen;
	double		     time;
	int 		      pos;
	int 		  msgtype; /*send type(0) or recieve type(1)*/
	int 		   elemid;
        struct SL_proc      *proc;
};
typedef struct SL_msg_perf SL_msg_perf;

struct SL_network_perf {
	struct SL_network_perf	 *fwd;
	struct SL_network_perf	*back;
	double 	              latency;
	double 		    bandwidth;
	int 			  pos;

};
typedef struct SL_network_perf SL_network_perf;

typedef struct SL_proc SL_proc;

#ifdef MINGW
struct iovec {
	char	*iov_base;	/* Base address. */
	size_t	 iov_len;	/* Length. */
};

#define	TCP_MAXSEG		0x02	/* set maximum segment size */
#define 	F_GETFL		3	/* get file->f_flags */
#define 	F_SETFL		4	/* set file->f_flags */
#define 	O_NONBLOCK	 	00004
#endif

/* A  message queue item containing the operation
   it decsribes */
struct SL_msgq_head;
struct SL_qitem {
    int                       id;
    int                   iovpos;
    int                   lenpos;
    int                    error;
    struct iovec          iov[2];
    struct SL_msgq_head *move_to;
    struct SL_msgq_head    *head;
    struct SL_qitem        *next;
    struct SL_qitem        *prev;
    double             starttime;
    double               endtime;
};
typedef struct SL_qitem SL_qitem;

/* A message queue */
struct SL_msgq_head {
    int               count;
    char              *name;
    struct SL_qitem  *first;
    struct SL_qitem   *last;
};
typedef struct SL_msgq_head SL_msgq_head;

/* Request object identifying an ongoing communication */
struct SL_msg_request {
    struct SL_proc        *proc;
    int                    type; /* Send or Recv */
    int                      id;
    struct SL_qitem       *elem;
    struct SL_msgq_head *cqueue; /* completion queue to look for */
};
typedef struct SL_msg_request SL_msg_request;

struct SL_msgq_head     *SL_event_sendq;
struct SL_msgq_head     *SL_event_recvq;
struct SL_msgq_head     *SL_event_sendcq;

/* MACROS */
/*#ifdef PRINTF
  #undef PRINTF
  #define PRINTF(A) printf A
#else
  #define PRINTF(A)
#endif*/

#define FALSE 0
#define TRUE  1

#define SEND 0
#define RECV 1

#define SL_RECONN_MAX      20
#define SL_ACCEPT_MAX_TIME 10
#define SL_READ_MAX_TIME   5
#define SL_ACCEPT_INFINITE_TIME -1
#define SL_BIND_PORTSHIFT 200
#define SL_SLEEP_TIME       1
#define SL_TCP_BUFFER_SIZE  262142
#define SL_MAX_EVENT_HANDLE     1
#define SL_CONSTANT_ID          -32
#define SL_EVENT_MANAGER        -1
#define SL_PROXY_SERVER		-2
#define PERFBUFSIZE	20
#define MTU		(1024L*4L)
#define SL_PROC_ID	-64

int SL_socket ( void );
int SL_bind_static ( int handle, int port );
int SL_bind_dynamic (  int handle, int port );
int SL_socket_close ( int handle );
int SL_open_socket_conn ( int *handle, const char *as_host, int port );
int SL_open_socket_bind ( int *handle, int port );
int SL_open_socket_listen ( int sock );
int SL_open_socket_listen_nb ( int *handle, int port );
int SL_open_socket_conn_nb ( int *handle, const char *as_host, int port );
int SL_socket_read ( int hdl, char *buf, int num, double timeout );
int SL_socket_write ( int hdl, char *buf, int num, double timeout );
int SL_socket_write_nb ( int hdl, char *buf, int num, int *numwritten );

int SL_socket_read_nb ( int hdl, char *buf, int num, int* numread );

void SL_print_socket_options ( int fd );
void SL_configure_socket ( int sd );
void SL_configure_socket_nb ( int sd );
int SL_init_internal();
double SL_papi_time();

/* status object t.b.d */

#endif /* __SL_INTERNALL__ */


//
// Created by ZL on 2019-06-13.
//

#ifndef ZQCNNDEMO_ZQCNN_MTCNN_NCHWC4_H
#define ZQCNNDEMO_ZQCNN_MTCNN_NCHWC4_H

#include "ZQ_CNN_Net_NCHWC.h"
#include "ZQ_CNN_MTCNN_NCHWC.h"
#include <vector>
#include <iostream>
#include "opencv2/opencv.hpp"
#include "ZQ_CNN_CompileConfig.h"
#include <cblas.h>

class MTCNNNCHWC {
public:
    MTCNNNCHWC(const std::string &model_path);

    std::vector<ZQ::ZQ_CNN_BBox> detect(const std::string &img_path);

    std::vector<ZQ::ZQ_CNN_BBox> detectMat(cv::Mat &image0);

private:
    ZQ::ZQ_CNN_MTCNN_NCHWC *mtcnn;
    int thread_num = 1;
};

#endif //ZQCNNDEMO_ZQCNN_MTCNN_NCHWC4_H


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include "arcana/finally_scope.h"
#include "arcana/functional/inplace_function.h"
#include "arcana/utils/algorithm.h"
#include "sorted_vector.h"

#include <gsl/gsl>
#include <iterator>
#include <map>
#include <mutex>
#include <stdint.h>

namespace mira
{
    /*
        A collection that mimics a coat check. You insert something into it and it gives you a ticket.
        Once you're done with the ticket, your item gets removed from the collection.
    */
    template<typename T, typename MutexT = std::mutex>
    class ticketed_collection
    {
        using seed_t = int64_t;

        struct compare
        {
            bool operator()(const std::pair<seed_t, T>& left, const std::pair<seed_t, T>& right) const
            {
                return left.first < right.first;
            }

            bool operator()(const std::pair<seed_t, T>& left, const seed_t& right) const
            {
                return left.first < right;
            }
        };

        using storage_t = sorted_vector<std::pair<seed_t, T>, compare>;

        struct ticket_functor
        {
            ticket_functor(seed_t id, MutexT& mutex, storage_t& items)
                : m_id{ id }
                , m_mutex{ mutex }
                , m_items{ items }
            {}

            void operator()()
            {
                std::lock_guard<MutexT> guard{ m_mutex };

                auto found = m_items.find(
                    m_id, [](const std::pair<seed_t, T>& left, const seed_t& right) { return left.first == right; });

                assert(found != m_items.end() && "ticketed item wasn't found in the collection");

                m_items.erase(found);
            }

            seed_t m_id;
            MutexT& m_mutex;
            storage_t& m_items;
        };

    public:
        struct iterator
        {
            using iter_t = typename storage_t::const_iterator;

            using difference_type = typename std::iterator_traits<iter_t>::difference_type;
            using value_type = T;
            using pointer = const value_type*;
            using reference = const value_type&;
            using iterator_category = std::input_iterator_tag;

            explicit iterator(const iter_t& itr)
                : m_iter{ itr }
            {}

            bool operator==(const iterator& other) const
            {
                return m_iter == other.m_iter;
            }

            bool operator!=(const iterator& other) const
            {
                return !(*this == other);
            }

            iterator& operator++()
            {
                ++m_iter;
                return *this;
            }

            iterator operator++(int)
            {
                iterator pre{ *this };
                ++m_iter;
                return pre;
            }

            const T* operator->() const
            {
                return &m_iter->second;
            }

            const T& operator*() const
            {
                return m_iter->second;
            }

        private:
            typename storage_t::const_iterator m_iter;
        };

        using ticket = gsl::final_action<
            stdext::inplace_function<void(), sizeof(ticket_functor), alignof(ticket_functor)>>;

        using ticket_scope = finally_scope<ticket>;

        ticketed_collection() = default;
        ticketed_collection(const ticketed_collection&) = delete;
        ticketed_collection& operator=(const ticketed_collection&) = delete;

        iterator begin() const
        {
            return iterator{ m_items.cbegin() };
        }

        iterator end() const
        {
            return iterator{ m_items.cend() };
        }

        auto size() const
        {
            return m_items.size();
        }

        auto empty() const
        {
            return m_items.empty();
        }

        //
        // Inserts an element into the collection and returns a ticket
        // that will remove the item on its destruction. The mutex passed
        // in should be already locked when calling this method. That same
        // mutex will get locked when removing the item from the collection.
        //
        template<typename ElementT>
        ticket insert(ElementT&& element, MutexT& mutex)
        {
            seed_t id = m_seed++;
            m_items.insert(std::pair<seed_t, T>{ id, std::forward<ElementT>(element) });

            return ticket{ ticket_functor(id, mutex, m_items) };
        }

    private:
        seed_t m_seed = 0;
        storage_t m_items;
    };
}


#ifndef _EM_UTILS_H_
#define _EM_UTILS_H_ 1

#include <petsc.h>

#include <string>

std::string string_format(const char*, ...);
std::string parse_string(const std::string &s);

class LogEventHelper {
public:
  LogEventHelper(PetscLogEvent ple) : ple_(ple) { PetscLogEventBegin(ple_, 0, 0, 0, 0); }
  ~LogEventHelper() { PetscLogEventEnd(ple_, 0, 0, 0, 0); }

private:
  PetscLogEvent ple_;
};

class LogStageHelper {
public:
  LogStageHelper(const std::string &name) {
    PetscLogStage pls;
    PetscLogStageRegister(name.c_str(), &pls);
    PetscLogStagePush(pls);
  }
  ~LogStageHelper() { PetscLogStagePop(); }
};

#endif


// Implementation of interface described in float_blob.h.

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gstdio.h>

#include <gsl/gsl_math.h>

#include "float_blob.h"
#include "float_image.h"
#include "utilities.h"

FloatBlob *
float_blob_new (gssize size_x, gssize size_y, const char *scratch_dir,
		const char *name)
{
  g_assert (size_x > 0 && size_y > 0);
  g_assert (g_file_test (scratch_dir, G_FILE_TEST_IS_DIR));
  g_assert (g_access (scratch_dir, R_OK | W_OK | X_OK) == 0);

  FloatBlob *self = g_new (FloatBlob, 1);

  self->size_x = size_x;
  self->size_y = size_y;

  self->swapped_bytes = FALSE;

  if ( name != NULL ) {
    self->file = ensure_slash_terminated (g_string_new (scratch_dir));
    g_string_append (self->file, name);
  }
  else {
    self->file = make_unique_tmp_file_name (scratch_dir, "float_blob_tmp_");
    g_assert (!g_file_test (self->file->str, G_FILE_TEST_EXISTS));
  }
  self->is_file_owner = TRUE;

  self->fd = open (self->file->str, O_RDWR | O_CREAT | O_TRUNC,
		   S_IRUSR | S_IWUSR);
  g_assert (self->fd != -1);

  // Out of carefulness we didn't truncate, but we want to make sure
  // we really got a new file.
  g_assert (file_size (self->fd) == 0);

  // Seek to last byte of file.
  off_t last_byte_offset
    = (off_t) self->size_x * (off_t) self->size_y * sizeof (float) - 1;
  off_t file_offset = lseek (self->fd, last_byte_offset, SEEK_SET);
  g_assert (file_offset == last_byte_offset);

  // Write a byte at the end of the file.
  size_t bytes_to_write = 1;
  g_assert (sizeof (unsigned char) == 1);
  unsigned char good_old_zero = 0x0;
  ssize_t bytes_written = write (self->fd, &good_old_zero, bytes_to_write);
  g_assert (bytes_written == bytes_to_write);

  // We should now have reserved all the space needed in this file.
  g_assert (file_size (self->fd) == last_byte_offset + 1);

  self->reference_count = 1;

  return self;
}

FloatBlob *
float_blob_new_using (gssize size_x, gssize size_y, const char *file,
		      gboolean swapped_bytes)
{
  g_assert (size_x > 0 && size_y > 0);
  g_assert (g_file_test (file, G_FILE_TEST_IS_REGULAR));
  g_assert (g_access (file, R_OK | W_OK) == 0);

  FloatBlob *self = g_new (FloatBlob, 1);

  self->fd = open (file, O_RDWR);
  g_assert (self->fd != -1);

  g_assert (file_size (self->fd) >= (off_t) size_x * size_y * sizeof (float));

  self->size_x = size_x;
  self->size_y = size_y;

  self->swapped_bytes = swapped_bytes;

  self->file = g_string_new (file);

  self->is_file_owner = FALSE;

  self->reference_count = 1;

  return self;
}

static void
float_blob_region_transfer (FloatBlob *self, gssize start_x, gssize start_y,
			    gssize w, gssize h, float *buffer,
			    gboolean is_get_transfer)
{
  g_assert (start_x >= 0 && start_x < self->size_x);
  g_assert (start_y >= 0 && start_y < self->size_y);
  g_assert (w > 0 && start_x + w <= self->size_x);
  g_assert (h > 0 && start_y + h <= self->size_y);

  ssize_t (*transfer)(int fd, void *buf, size_t count)
    = is_get_transfer ? read : ((ssize_t (*)(int, void *, size_t)) write);

  size_t jj;
  for ( jj = start_y ; jj < start_y + h ; jj++ ) {
    off_t file_offset
      = lseek (self->fd,
	       sizeof (float) * ((off_t) jj * self->size_x + start_x),
	       SEEK_SET);
    g_assert (file_offset != (off_t) -1);
    size_t transfer_size = w * sizeof (float);
    ssize_t transfer_result
      = transfer (self->fd, buffer + (jj - start_y) * w, transfer_size);
    if ( transfer_result == -1 ) {
      g_error ("I/O error: %s\n", strerror (errno));
    }
    g_assert (transfer_result == transfer_size);
  }
}

void
float_blob_get_region (FloatBlob *self, gssize start_x, gssize start_y,
		       gssize w, gssize h, float *buffer)
{
  float_blob_region_transfer (self, start_x, start_y, w, h, buffer, TRUE);

  if ( self->swapped_bytes ) {
    swap_array_bytes_32 (buffer, w * h);
  }
}

void
float_blob_set_region (FloatBlob *self, gssize start_x, gssize start_y,
		       gssize w, gssize h, float *buffer)
{
  if ( self->swapped_bytes ) {
    swap_array_bytes_32 (buffer, w * h);
  }

  float_blob_region_transfer (self, start_x, start_y, w, h, buffer, FALSE);

  if ( self->swapped_bytes ) {
    swap_array_bytes_32 (buffer, w * h);
  }
}

GString *
float_blob_steal_file (FloatBlob *self)
{
  g_assert (self->is_file_owner == TRUE);

  self->is_file_owner = FALSE;

  return g_string_new (self->file->str);

}

void
float_blob_export_as_jpeg (FloatBlob *self, const char *file, double mask)
{
  // Self as float image.
  FloatImage *safi = float_image_new (self->size_x, self->size_y);

  float *row_buffer = g_new (float, self->size_x);

  // We are about to use swap_32 type function, so we have a quick
  // attack of paranoia and check sizeof float...
  g_assert (sizeof (float) == 4);

  // Seek to the beginning of the file.
  off_t resultant_offset = lseek (self->fd, 0, SEEK_SET);
  if ( resultant_offset == (off_t) -1 ) {
    g_error ("lseek failed: %s\n", strerror (errno));
  }
  g_assert (resultant_offset == 0);

  size_t ii, jj;
  for ( jj = 0 ; jj < self->size_y ; jj++ ) {

    // Read one row of data from the file underlying self.
    ssize_t bytes_to_read = self->size_x * sizeof (float);
    ssize_t read_count = read (self->fd, row_buffer, bytes_to_read);
    g_assert (read_count == bytes_to_read);

    // Swap row bytes if necessary.
    if ( self->swapped_bytes ) {
      swap_array_bytes_32 (row_buffer, self->size_x);
    }

    // Write that row to the FloatImage.
    for ( ii = 0 ; ii < self->size_x ; ii++ ) {
      float_image_set_pixel (safi, ii, jj, row_buffer[ii]);
    }
  }

  g_free (row_buffer);

  float_image_export_as_jpeg (safi, file, GSL_MAX (safi->size_x, safi->size_y),
			      mask);

  float_image_unref (safi);
}

FloatBlob *
float_blob_ref (FloatBlob *self)
{
  g_assert (self->reference_count > 0);

  self->reference_count++;

  return self;
}

void
float_blob_unref (FloatBlob *self)
{
  g_assert (self->reference_count > 0);

  self->reference_count--;

  if ( self->reference_count == 0 ) {
    int return_code = close (self->fd);
    g_assert (return_code == 0);

    if ( self->is_file_owner ) {
      return_code = unlink (self->file->str);
      if ( return_code != 0 ) {
	g_error ("unlink of float_blob file '%s' in function %s, (file "
		 __FILE__ ", line %d) failed: %s", self->file->str, __func__,
		 __LINE__, strerror (errno));
      }
      g_assert (return_code == 0);
    }

    g_string_free (self->file, TRUE);

    g_free (self);

    self = NULL;
  }
}


#pragma once
#include <Manager.h>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <limits>
#include <vector>
#include "../types/def.h"
#include <gsl\gsl>
#include "SFML/Window.hpp"
#include <utility>
#include <../_entity/Entity.h>
#include <../_entity/EntityManager.h>

#include "../_component/components/TransformComponent.h"
#include "../_component/components/CollisionComponent.h"

class ChunkManager : public Manager<ChunkManager> {
public:
    friend class CRSP<ChunkManager>;
    using ChunkMap = std::unordered_map<Chunk, EntityIdSet, PairIntIntHash>;
    using EntityMap = std::unordered_map<EntityId, ChunkSet>;
    using PositionCache = std::unordered_map<EntityId, std::pair<sf::Vector2f, sf::Vector2f>>;
    using ChunkLengthType = float;
    using GroupedEntities = std::unordered_map<Chunk, std::set<Entity*>, PairIntIntHash>;
private:
    ChunkManager();
    ~ChunkManager();

public:

    void start_up() override;
    void shut_down() override;

    std::vector<Chunk> get_relevant_chunks() const;

    ChunkSet get_chunks_of(EntityId id) const;

    ChunkSet calculate_chunks(Entity* entity) const;

    Chunk get_chunk_from_position(float x, float y) const;

    bool share_chunks(EntityId a, EntityId b) const;

    void update_entity_chunks();

    void update_single_entity(Entity* entity);

    void clear();

    /*
    There are not const because it might require to generate vectors for new chunks without entities
    */
    void draw_debug_chunks();

    void draw_debug_chunk_configuration();

    const EntityIdSet& get_entities_of_chunk(Chunk chunk);

    const std::set<Entity*>& get_colliding_entities_of_chunk(Chunk chunk);

    /**
    Returns a vector of vectors with the entities grouped by chunks, that is, in each vector
    we have all the entities that are in a particular chunk, entities can be repeated in more
    than one vector.
    */
    template<typename... T>
    GroupedEntities get_grouped_entities_with_components() {

        auto general = GroupedEntities{};

        for (auto & c : m_chunk_map) {

            if (c.second.size() == 0) continue;

            auto& entity_set = c.second;

            auto entities_in_chunk = std::vector<Entity*>{};

            for (auto& e : entity_set) {
                auto entity = EntityManager::get().get_entity(e);

                if (!entity->is_in_relevant_chunk()) continue;

                auto checks = { entity->has_component<T>()... };

                if (std::all_of(checks.begin(), checks.end(), [](bool i) {return i; })) {
                    entities_in_chunk.push_back(entity);
                }
            }

            general[c.first] = (std::move(entities_in_chunk));

        }
        return general;
    }

    /*
    Specific version of this function for the elements with a collision component
    that returns an internal member that we calculate on our "update_entity_chunks" instead
    of calculating it on request like we are doing in the function above.
    */
    template<>
    GroupedEntities get_grouped_entities_with_components<TransformComponent, CollisionComponent>() {
        return m_collision_components_cache;
    }

private:

    /**
    The length of the side of each square chunk
    */
    ChunkLengthType m_chunk_size{ 16 };

    /**
    Number of chunks outside the visible screen that we still consider relevant
    */
    unsigned int m_chunk_threshold{ 1 };

    /**
    This is used in case an entity enters a chunk exactly in the frame that we have to check
    something in that new chunk, we register the entity as being in the chunks that it
    really is plus "m_safe_threshold" more in each direction.
    */
    unsigned int m_safe_threshold{ 1 };

    ChunkMap m_chunk_map{};
    EntityMap m_entity_map{};
    PositionCache m_location_cache{};

    Chunk m_min_relevant_chunk{};
    Chunk m_max_relevant_chunk{};

    GroupedEntities m_collision_components_cache{};

    sf::Vector2f camera_previous_center{ std::numeric_limits<float>::infinity(),std::numeric_limits<float>::infinity() };
    sf::Vector2f camera_previous_size{ std::numeric_limits<float>::infinity(),std::numeric_limits<float>::infinity() };
    bool m_camera_changed{ true };

};


/* block/gsl_block_uint.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_BLOCK_UINT_H__
#define __GSL_BLOCK_UINT_H__

#include <stdlib.h>
#include <gsl/gsl_errno.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

struct gsl_block_uint_struct
{
  size_t size;
  unsigned int *data;
};

typedef struct gsl_block_uint_struct gsl_block_uint;

gsl_block_uint *gsl_block_uint_alloc (const size_t n);
gsl_block_uint *gsl_block_uint_calloc (const size_t n);
void gsl_block_uint_free (gsl_block_uint * b);

int gsl_block_uint_fread (FILE * stream, gsl_block_uint * b);
int gsl_block_uint_fwrite (FILE * stream, const gsl_block_uint * b);
int gsl_block_uint_fscanf (FILE * stream, gsl_block_uint * b);
int gsl_block_uint_fprintf (FILE * stream, const gsl_block_uint * b, const char *format);

int gsl_block_uint_raw_fread (FILE * stream, unsigned int * b, const size_t n, const size_t stride);
int gsl_block_uint_raw_fwrite (FILE * stream, const unsigned int * b, const size_t n, const size_t stride);
int gsl_block_uint_raw_fscanf (FILE * stream, unsigned int * b, const size_t n, const size_t stride);
int gsl_block_uint_raw_fprintf (FILE * stream, const unsigned int * b, const size_t n, const size_t stride, const char *format);

size_t gsl_block_uint_size (const gsl_block_uint * b);
unsigned int * gsl_block_uint_data (const gsl_block_uint * b);

__END_DECLS

#endif /* __GSL_BLOCK_UINT_H__ */


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef image_a41c29de_bd9b_4bbd_9012_6dc956f524b3_h
#define image_a41c29de_bd9b_4bbd_9012_6dc956f524b3_h

#include <gslib/type.h>
#include <gslib/string.h>
#include <ariel/dirty.h>

__ariel_begin__

class image
{
public:
    enum image_format
    {
        fmt_gray,                   /* gray8 */
        fmt_rgba,                   /* rgba8888 */
    };

    friend class imageio;

public:
    image();
    ~image() { destroy(); }
    bool is_valid() const;
    image_format get_format() const { return _format; }
    int get_depth() const { return _depth; }
    bool create(image_format fmt, int w, int h);
    void destroy();
    void init(const color& cr);
    void enable_alpha_channel(bool b) { _is_alpha_channel_valid = b; }
    bool has_alpha() const { return _is_alpha_channel_valid; }
    int get_width() const { return _width; }
    int get_height() const { return _height; }
    int get_bytes_per_line() const { return _bytes_per_line; }
    int get_size() const { return _color_bytes; }
    void set_xdpi(int dpi) { _xdpi = dpi; }
    void set_ydpi(int dpi) { _ydpi = dpi; }
    int get_xdpi() const { return _xdpi; }
    int get_ydpi() const { return _ydpi; }
    byte* get_data(int x, int y) const;
    byte* get_bits() const { return _data; }
    bool load(const string& filepath);
    bool load(const gchar* filepath, int len) { return load(string(filepath, len)); }
    bool save(const string& filepath) const;
    void clear(byte b, const rect* rc = nullptr);
    void clear(const color& cr, const rect* rc = nullptr);
    void copy(const image& img);
    void copy(const image& img, int x, int y, int cx, int cy, int sx, int sy);

protected:
    image_format        _format;
    int                 _width;
    int                 _height;
    int                 _depth;
    int                 _color_bytes;
    int                 _bytes_per_line;
    int                 _xdpi;
    int                 _ydpi;
    byte*               _data;
    bool                _is_alpha_channel_valid;
};

__ariel_end__

#endif


/* bst/gsl_bst_rb.h
 *
 * Copyright (C) 2018 Patrick Alken
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_BST_RB_H__
#define __GSL_BST_RB_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_bst_types.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

#ifndef GSL_BST_RB_MAX_HEIGHT
#define GSL_BST_RB_MAX_HEIGHT 48
#endif

/* red-black node */
struct gsl_bst_rb_node
{
  struct gsl_bst_rb_node *rb_link[2]; /* subtrees */
  void *rb_data;                      /* pointer to data */
  unsigned char rb_color;             /* color */
};

/* red-black tree data structure */
typedef struct
{
  struct gsl_bst_rb_node *rb_root;   /* tree's root */
  gsl_bst_cmp_function *rb_compare;  /* comparison function */
  void *rb_param;                    /* extra argument to |rb_compare| */
  const gsl_bst_allocator *rb_alloc; /* memory allocator */
  size_t rb_count;                   /* number of items in tree */
  unsigned long rb_generation;       /* generation number */
} gsl_bst_rb_table;

/* red-black traverser structure */
typedef struct
{
  const gsl_bst_rb_table *rb_table;  /* tree being traversed */
  struct gsl_bst_rb_node *rb_node;   /* current node in tree */
  struct gsl_bst_rb_node *rb_stack[GSL_BST_RB_MAX_HEIGHT];
                                     /* all the nodes above |rb_node| */
  size_t rb_height;                  /* number of nodes in |rb_parent| */
  unsigned long rb_generation;       /* generation number */
} gsl_bst_rb_traverser;

__END_DECLS

#endif /* __GSL_BST_RB_H__ */


#pragma once

#include <gsl/span>

#include <cstddef>
#include <type_traits>

namespace imageview {
namespace detail {

template <class T, typename Enable = void>
class HasColorTypeTypedef : public std::false_type {};

template <class T>
class HasColorTypeTypedef<T, std::void_t<typename T::color_type>> : public std::true_type {};

template <class T, typename Enable = void>
class HasKBytesPerPixelConstant : public std::false_type {};

template <class T>
class HasKBytesPerPixelConstant<T, std::enable_if_t<std::is_integral_v<decltype(T::kBytesPerPixel)> &&
                                                    std::is_const_v<decltype(T::kBytesPerPixel)>>>
    : public std::true_type {};

template <class T, class Enable = void>
class HasRead : public std::false_type {};

template <class T>
class HasRead<T, std::enable_if_t<std::is_same_v<typename T::color_type,
                                                 decltype(std::declval<const T&>().read(
                                                     std::declval<gsl::span<const std::byte, T::kBytesPerPixel>>()))>>>
    : public std::true_type {};

template <class T, class Enable = void>
class HasWrite : public std::false_type {};

template <class T>
class HasWrite<T, std::enable_if_t<std::is_same_v<void, decltype(std::declval<const T&>().write(
                                                            std::declval<const typename T::color_type&>(),
                                                            std::declval<gsl::span<std::byte, T::kBytesPerPixel>>()))>>>
    : public std::true_type {};

}  // namespace detail

template <class T>
class IsPixelFormat : public std::conjunction<
                          // Has a member typedef 'color_type'.
                          detail::HasColorTypeTypedef<T>,
                          // Has an integral static member constant 'kBytesPerPixel'.
                          detail::HasKBytesPerPixelConstant<T>,
                          // Has a member function read() with the signature equivalent to
                          //   color_type read(gsl::span<const std::byte, kBytesPerPixel>) const;
                          detail::HasRead<T>,
                          // Has a member function write() with the signature equivalent to
                          //   void write(const color_type&, gsl::span<std::byte, kBytesPerPixel>) const;
                          detail::HasWrite<T>> {};

}  // namespace imageview


#pragma once
/*
 * (C) Copyright 2020-2021 UCAR
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 */
/*! \defgroup ioda_cxx_types Type System
 * \brief The data type system
 * \ingroup ioda_cxx_api
 *
 * @{
 * \file Type.h
 * \brief Interfaces for ioda::Type and related classes. Implements the type system.
 */
#include <array>
#include <cstring>
#include <functional>
#include <gsl/gsl-lite.hpp>
#include <memory>
#include <string>
#include <typeindex>
#include <typeinfo>
#include <vector>

#include "ioda/Types/Type_Provider.h"
#include "ioda/Exception.h"
#include "ioda/defs.h"

namespace ioda {
class Type;

/// Basic pre-defined types (Python convenience wrappers)
/// \see py_ioda.cpp
/// \note Names here do not match the python equivalents. The
///   Python names match numpy's definitions.
enum class BasicTypes {
  undefined_,  ///< Internal use only
  float_,
  double_,
  ldouble_,
  char_,
  short_,
  ushort_,
  int_,
  uint_,
  lint_,
  ulint_,
  llint_,
  ullint_,
  int32_,
  uint32_,
  int16_,
  uint16_,
  int64_,
  uint64_,
  bool_,
  str_
};

namespace detail {
IODA_DL size_t COMPAT_strncpy_s(char* dest, size_t destSz, const char* src, size_t srcSz);

class Type_Backend;

template <class Type_Implementation = Type>
class Type_Base {
  friend class ::ioda::Type;
  std::shared_ptr<Type_Backend> backend_;

protected:
  ::ioda::detail::Type_Provider* provider_;

  /// @name General Functions
  /// @{

  Type_Base(std::shared_ptr<Type_Backend> b, ::ioda::detail::Type_Provider* p)
      : backend_(b), provider_(p) {}

  /// Get the type provider.
  inline detail::Type_Provider* getTypeProvider() const { return provider_; }

  /*
  /// \brief Convenience function to check a type.
  /// \param DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \returns True if the type matches
  /// \returns False (0) if the type does not match
  /// \throws if an error occurred.
  template <class DataType>
  bool isA() const {
    Type templateType = Types::GetType_Wrapper<DataType>::GetType(getTypeProvider());

    return isA(templateType);
  }
  /// Hand-off to the backend to check equivalence
  virtual bool isA(Type lhs) const;

  /// Python compatability function
  inline bool isA(BasicTypes dataType) const { return isA(Type(dataType, getTypeProvider())); }
  */
public:
  virtual ~Type_Base() {}
  std::shared_ptr<Type_Backend> getBackend() const { return backend_; }
  bool isValid() const { return (backend_.use_count() > 0); }

  /// \brief Get the size of a single element of a type, in bytes.
  /// \details This function is paired with the read and write functions to allow you to
  /// read and write data in a type-agnostic manner.
  /// This size report is a bit complicated when variable-length strings are encountered.
  /// In these cases, the size of the string pointer is returned.
  virtual size_t getSize() const;

  /// @}
};
}  // namespace detail

/// \brief Represents the "type" (i.e. integer, string, float) of a piece of data.
/// \ingroup ioda_cxx_types
///
/// Generally, you do not have to use this class directly. Attributes and Variables have
/// templated functions that convert your type into the type used internally by ioda.
/// \see Types::GetType and Types::GetType_Wrapper for functions that produce these types.
class IODA_DL Type : public detail::Type_Base<> {
public:
  Type();
  Type(std::shared_ptr<detail::Type_Backend> b, std::type_index t);
  Type(BasicTypes, gsl::not_null<::ioda::detail::Type_Provider*> t);

  virtual ~Type();

  /// @name Type-querying functions
  /// @{

  /// @deprecated This function is problematic since we cannot query a type properly
  /// when loading from a file.
  std::type_index getType() const { return as_type_index_; }
  inline std::type_index operator()() const { return getType(); }
  inline std::type_index get() const { return getType(); }

  /// @}
private:
  std::type_index as_type_index_;
};

namespace detail {

/// Backends inherit from this and they provide their own functions.
/// Lots of std::dynamic_cast, unfortunately.
class IODA_DL Type_Backend : public Type_Base<> {
public:
  virtual ~Type_Backend();
  size_t getSize() const override;

protected:
  Type_Backend();
};

}  // namespace detail

/// \brief Defines the type system used for manipulating IODA objects.
namespace Types {

// using namespace ioda::Handles;

/// \brief Convenience struct to determine if a type can represent a string.
/// \ingroup ioda_cxx_types
/// \todo extend to UTF-8 strings, as HDF5 supports these. No support for UTF-16, but conversion
/// functions may be applied.
/// \todo Fix for "const std::string".
template <typename T>
struct is_string : public std::integral_constant<
                     bool, std::is_same<char*, typename std::decay<T>::type>::value
                             || std::is_same<const char*, typename std::decay<T>::type>::value> {};
/// \brief Convenience struct to determine if a type can represent a string.
/// \ingroup ioda_cxx_types
template <>
struct is_string<std::string> : std::true_type {};

/// Useful compile-time definitions.
namespace constants {
/// \note Different than ObsSpace variable-length dimension. This is for a Type.
constexpr size_t _Variable_Length = 0;
// constexpr int _Not_An_Array_type = -3;
}  // namespace constants

/// \brief For fundamental, non-string types.
/// \ingroup ioda_cxx_types
template <class DataType, int Array_Type_Dimensionality = 0>
Type GetType(gsl::not_null<const ::ioda::detail::Type_Provider*> t,
             std::initializer_list<Dimensions_t> Adims                   = {},
             typename std::enable_if<!is_string<DataType>::value>::type* = 0) {
  if (Array_Type_Dimensionality <= 0)
    throw Exception(
      "Bad assertion / unsupported fundamental type at the frontend side "
      "of the ioda type system.", ioda_Here());
  else
    return t->makeArrayType(Adims, typeid(DataType[]), typeid(DataType));
}
/// \brief For fundamental string types. These are either constant or variable length arrays.
/// Separate handling elsewhere.
/// \ingroup ioda_cxx_types
template <class DataType, int String_Type_Length = constants::_Variable_Length>
Type GetType(gsl::not_null<const ::ioda::detail::Type_Provider*> t,
             std::initializer_list<Dimensions_t>                        = {},
             typename std::enable_if<is_string<DataType>::value>::type* = 0) {
  return t->makeStringType(String_Type_Length, typeid(DataType));
}

// This macro just repeats a long definition

/// @def IODA_ADD_FUNDAMENTAL_TYPE
/// Macro that defines a "fundamental type" that needs to be supported
/// by the backend. These match C++11.
/// \ingroup ioda_cxx_types
/// \see https://en.cppreference.com/w/cpp/language/types
/// \since C++11: we use bool, short int, unsigned short int,
///   int, unsigned int, long int, unsigned long int,
///   long long int, unsigned long long int,
///   signed char, unsigned char, char,
///   wchar_t, char16_t, char32_t,
///   float, double, long double.
/// \since C++20: we also add char8_t.
#define IODA_ADD_FUNDAMENTAL_TYPE(x)                                                               \
  template <>                                                                                \
  inline Type GetType<x, 0>(gsl::not_null<const ::ioda::detail::Type_Provider*> t,                 \
                            std::initializer_list<Dimensions_t>, void*) {                          \
    return t->makeFundamentalType(typeid(x));                                                      \
  }

IODA_ADD_FUNDAMENTAL_TYPE(bool);
IODA_ADD_FUNDAMENTAL_TYPE(short int);
IODA_ADD_FUNDAMENTAL_TYPE(unsigned short int);
IODA_ADD_FUNDAMENTAL_TYPE(int);
IODA_ADD_FUNDAMENTAL_TYPE(unsigned int);
IODA_ADD_FUNDAMENTAL_TYPE(long int);
IODA_ADD_FUNDAMENTAL_TYPE(unsigned long int);
IODA_ADD_FUNDAMENTAL_TYPE(long long int);
IODA_ADD_FUNDAMENTAL_TYPE(unsigned long long int);
IODA_ADD_FUNDAMENTAL_TYPE(signed char);
IODA_ADD_FUNDAMENTAL_TYPE(unsigned char);
IODA_ADD_FUNDAMENTAL_TYPE(char);
IODA_ADD_FUNDAMENTAL_TYPE(wchar_t);
IODA_ADD_FUNDAMENTAL_TYPE(char16_t);
IODA_ADD_FUNDAMENTAL_TYPE(char32_t);
// IODA_ADD_FUNDAMENTAL_TYPE(char8_t); // C++20
IODA_ADD_FUNDAMENTAL_TYPE(float);
IODA_ADD_FUNDAMENTAL_TYPE(double);
IODA_ADD_FUNDAMENTAL_TYPE(long double);

#undef IODA_ADD_FUNDAMENTAL_TYPE

/*
/// Used in an example. Incomplete.
/// \todo Pop off std::array as a 1-D object
template<> inline Type GetType<std::array<int,2>, 0>
        (gsl::not_null<const ::ioda::detail::Type_Provider*> t,
        std::initializer_list<Dimensions_t>, void*) {
        return t->makeArrayType({2}, typeid(std::array<int,2>), typeid(int)); }

*/

/*
template <class DataType, int Array_Type_Dimensionality = 0>
Type GetType(
        gsl::not_null<const ::ioda::detail::Type_Provider*> t,
        std::initializer_list<Dimensions_t> Adims = {},
        typename std::enable_if<!is_string<DataType>::value>::type* = 0);
template <class DataType, int String_Type_Length = constants::_Variable_Length>
Type GetType(
        gsl::not_null<const ::ioda::detail::Type_Provider*> t,
        typename std::enable_if<is_string<DataType>::value>::type* = 0);
        */

/// \brief Wrapper struct to call GetType. Needed because of C++ template rules.
/// \ingroup ioda_cxx_types
/// \see ioda::Attribute, ioda::Has_Attributes, ioda::Variable, ioda::Has_Variables
template <class DataType,
          int Length = 0>  //, typename = std::enable_if_t<!is_string<DataType>::value>>
struct GetType_Wrapper {
  static Type GetType(gsl::not_null<const ::ioda::detail::Type_Provider*> t) {
    /// \note Currently breaks array types, but these are not yet used.
    return ::ioda::Types::GetType<DataType, Length>(t, {Length});
  }
};
/// \ingroup ioda_cxx_types
typedef std::function<Type(gsl::not_null<const ::ioda::detail::Type_Provider*>)>
  TypeWrapper_function;
/*
template <class DataType, int Length = 0, typename = std::enable_if_t<is_string<DataType>::value>>
struct GetType_Wrapper {
        Type GetType(gsl::not_null<const ::ioda::detail::Type_Provider*> t) const {
                // string split
                return ::ioda::Types::GetType<DataType, Length>(t);
        }
};
*/

// inline Encapsulated_Handle GetTypeFixedString(Dimensions_t sz);
}  // namespace Types
}  // namespace ioda

/// @}


#pragma once

#include <nextalign/nextalign.h>

#include <gsl/string_span>
#include <string>

#include "../nextalign_private.h"
#include "../utils/to_underlying.h"

using NucleotideSequenceSpan = SequenceSpan<Nucleotide>;

Nucleotide toNucleotide(char nuc);

char nucToChar(Nucleotide nuc);

inline std::ostream& operator<<(std::ostream& os, const Nucleotide& nucleotide) {
  os << std::string{to_underlying(nucleotide)};
  return os;
}


#ifndef _PYGSL_SOLVER_INTERN_H_
#define _PYGSL_SOLVER_INTERN_H_ 1

#define _PyGSL_SOLVER_API_MODULE 1
#include <pygsl/solver.h>
#undef _PyGSL_SOLVER_API_MODULE

#include <pygsl/general_helpers.h>
#include <pygsl/block_helpers.h>
#include <pygsl/function_helpers.h>
#include <setjmp.h>
#include <gsl/gsl_math.h>

/*
 * Collect all element accessor methods which just need a pointer to the struct
 * and return a value.
 */
struct _ElementAccessor{
     int_m_t method;
     const char * name;
};

static PyTypeObject PyGSL_solver_pytype;
#define PyGSL_solver_check(ob) ((ob)->ob_type == &PyGSL_solver_pytype)
#define _PyGSL_solver_n_init PyGSL_solver_n_init

static PyGSL_solver*
_PyGSL_solver_init(const struct _SolverStatic *mstatic);

static int
PyGSL_solver_set_called(PyGSL_solver *self);

#define PyGSL_SOLVER_SET_CALLED(ob) \
        (((ob)->set_called == 1) ? GSL_SUCCESS: PyGSL_solver_set_called((ob)))

static int
PyGSL_solver_func_set(PyGSL_solver *self, PyObject *args, PyObject *f,
		       PyObject *df, PyObject *fdf);

#if 0
static PyObject *
_PyGSL_solver_np_init(PyObject *self, PyObject *args, const solver_alloc_struct * alloc);

static PyObject *
_PyGSL_solver_i_vvdd(PyObject * self, PyObject * args, int_f_vvdd_t func);

#endif
#endif /* _PYGSL_SOLVER_INTERN_H_ */