//CELL (~soma) stage: IIR filtering of each row or col of X according to dim.
//The IIR filters are specified by an Nx(Q+1) or (Q+1)xN matrix A,
//where Q is the IIR filter order (Q=0 means only a0; Q=1 means a0 and a1; etc.).

//The calling program must ensure that the sizes are correct, the filter is stable, etc.

//I just started this... finish later!! (Or skip.)

#include <stdio.h>
#include <cblas.h>

#ifdef __cplusplus
namespace ov {
extern "C" {
#endif

int iir_s (float *Y, const float *X, const float *A, const size_t N, const size_t T, const int Q, const char iscolmajor, const size_t dim);
int iir_d (double *Y, const double *X, const double *A, const size_t N, const size_t T, const int Q, const char iscolmajor, const size_t dim);
int iir_c (float *Y, const float *X, const float *A, const size_t N, const size_t T, const int Q, const char iscolmajor, const size_t dim);
int iir_z (double *Y, const double *X, const double *A, const size_t N, const size_t T, const int Q, const char iscolmajor, const size_t dim);

int iir_inplace_s (float *X, const float *A, const size_t N, const size_t T, const int Q, const char iscolmajor, const size_t dim);
int iir_inplace_d (double *X, const double *A, const size_t N, const size_t T, const int Q, const char iscolmajor, const size_t dim);
int iir_inplace_c (float *X, const float *A, const size_t N, const size_t T, const int Q, const char iscolmajor, const size_t dim);
int iir_inplace_z (double *X, const double *A, const size_t N, const size_t T, const int Q, const char iscolmajor, const size_t dim);

int iir_inplace_s (float *X, const float *A, const size_t N, const size_t T, const int Q, const char iscolmajor, const size_t dim)
{
    const size_t M = Q - 1;
    int n, t;

    //Checks
    if (N<1) { fprintf(stderr,"error in iir_s: N (num neurons) must be positive\n"); return 1; }
    if (T<1) { fprintf(stderr,"error in iir_s: T (num time points) must be positive\n"); return 1; }
    if (Q<0) { fprintf(stderr,"error in iir_s: Q (filter order) must be nonnegative\n"); return 1; }

    if (N==1u)
    {
        if (A[0]!=1.0f) { cblas_sscal((int)T,1.0f/A[0],A,1); cblas_sscal((int)(N*T),1.0f/A[0],X,1); }
        for (size_t t=1; t<M; ++t) { X[t] -= cblas_sdot(t,&A[M-t],1,&X[0],1); }
        for (size_t t=M; t<T; ++t) { X[t] -= cblas_sdot(M,&A[0],1,&X[t-M],1); }
    }
    else if (dim==0u)
    {
        if (N<3)
        {
            if (iscolmajor)
            {
                for (size_t n=0u; n<N; ++n)
                {
                    if (A[n*Q1]!=1.0f) { cblas_sscal((int)T,1.0f/A[n*Q1],&A[n*Q1],1); }
                }
                for (size_t n=1; n<M; ++n)
                {
                    X[n] -= cblas_sdot(n,&A[M-n],1,&X[0],1);
                    X[n+R] -= cblas_sdot(n,&A[M-n],1,&X[R],1);
                }
                for (size_t n=M; n<R; ++n)
                {
                    X[n] -= cblas_sdot(M,&A[0],1,&X[n-M],1);
                    X[n+R] -= cblas_sdot(M,&A[0],1,&X[n-M+R],1);
                }
            }
            else
            {
                for (size_t n=1; n<M; ++n)
                {
                    X[n*C] -= cblas_sdot(n,&A[M-n],1,&X[0],(int)C);
                    X[1+n*C] -= cblas_sdot(n,&A[M-n],1,&X[1],(int)C);
                }
                for (size_t n=M; n<R; ++n)
                {
                    X[n*C] -= cblas_sdot(M,&A[0],1,&X[(n-M)*C],(int)C);
                    X[1+n*C] -= cblas_sdot(M,&A[0],1,&X[1+(n-M)*C],(int)C);
                }
            }
        }
        else
        {
            if (iscolmajor)
            {
                for (size_t n=1; n<M; ++n) { cblas_sgemv(CblasColMajor,CblasTrans,n,(int)C,-1.0f,&X[0],(int)R,&A[M-n],1,1.0f,&X[n],(int)R); }
                for (size_t n=M; n<R; ++n) { cblas_sgemv(CblasColMajor,CblasTrans,M,(int)C,-1.0f,&X[n-M],(int)R,&A[0],1,1.0f,&X[n],(int)R); }
            }
            else
            {
                for (size_t n=1; n<M; ++n) { cblas_sgemv(CblasRowMajor,CblasTrans,n,(int)C,-1.0f,&X[0],(int)C,&A[M-n],1,1.0f,&X[n*C],1); }
                for (size_t n=M; n<R; ++n) { cblas_sgemv(CblasRowMajor,CblasTrans,M,(int)C,-1.0f,&X[(n-M)*C],(int)C,&A[0],1,1.0f,&X[n*C],1); }
            }
        }
    }
    else if (dim==1u)
    {
        if (R==1)
        {
            for (size_t n=1; n<M; ++n) { X[n] -= cblas_sdot(n,&A[M-n],1,&X[0],1); }
            for (size_t n=M; n<C; ++n) { X[n] -= cblas_sdot(M,&A[0],1,&X[n-M],1); }
        }
        else if (R==2)
        {
            if (iscolmajor)
            {
                for (size_t n=1; n<M; ++n)
                {
                    X[n*R] -= cblas_sdot(n,&A[M-n],1,&X[0],(int)R);
                    X[1+n*R] -= cblas_sdot(n,&A[M-n],1,&X[1],(int)R);
                }
                for (size_t n=M; n<C; ++n)
                {
                    X[n*R] -= cblas_sdot(M,&A[0],1,&X[(n-M)*R],(int)R);
                    X[1+n*R] -= cblas_sdot(M,&A[0],1,&X[1+(n-M)*R],(int)R);
                }
            }
            else
            {
                for (size_t n=1; n<M; ++n)
                {
                    X[n] -= cblas_sdot(n,&A[M-n],1,&X[0],1);
                    X[n+C] -= cblas_sdot(n,&A[M-n],1,&X[C],1);
                }
                for (size_t n=M; n<C; ++n)
                {
                    X[n] -= cblas_sdot(M,&A[0],1,&X[n-M],1);
                    X[n+C] -= cblas_sdot(M,&A[0],1,&X[n-M+C],1);
                }
            }
        }
        else
        {
            if (iscolmajor)
            {
                for (size_t n=1; n<M; ++n) { cblas_sgemv(CblasColMajor,CblasNoTrans,(int)R,n,-1.0f,&X[0],(int)R,&A[M-n],1,1.0f,&X[n*R],1); }
                for (size_t n=M; n<C; ++n) { cblas_sgemv(CblasColMajor,CblasNoTrans,(int)R,M,-1.0f,&X[(n-M)*R],(int)R,&A[0],1,1.0f,&X[n*R],1); }
            }
            else
            {
                for (size_t n=1; n<M; ++n) { cblas_sgemv(CblasRowMajor,CblasNoTrans,(int)R,n,-1.0f,&X[0],(int)C,&A[M-n],1,1.0f,&X[n],(int)C); }
                for (size_t n=M; n<C; ++n) { cblas_sgemv(CblasRowMajor,CblasNoTrans,(int)R,M,-1.0f,&X[n-M],(int)C,&A[0],1,1.0f,&X[n],(int)C); }
            }
        }
    }
    else
    {
        fprintf(stderr,"error in iir_s: dim must be 0 or 1.\n"); return 1;
    }

    return 0;
}

int iir_d (double *X, const char iscolmajor, const size_t R, const size_t C, const double *A, const size_t N, const size_t dim)
{
    const size_t M = N - 1;
    int n;

    //Checks
    if (R<1) { fprintf(stderr,"error in iir_d: R (nrows X) must be positive\n"); return 1; }
    if (C<1) { fprintf(stderr,"error in iir_d: C (ncols X) must be positive\n"); return 1; }
    if (N<1) { fprintf(stderr,"error in iir_d: N (filter order) must be positive\n"); return 1; }

    if (dim==0u)
    {
        if (C==1)
        {
            for (size_t n=1; n<M; ++n) { X[n] -= cblas_ddot(n,&A[M-n],1,&X[0],1); }
            for (size_t n=M; n<R; ++n) { X[n] -= cblas_ddot(M,&A[0],1,&X[n-M],1); }
        }
        else if (C==2)
        {
            if (iscolmajor)
            {
                for (size_t n=1; n<M; ++n)
                {
                    X[n] -= cblas_ddot(n,&A[M-n],1,&X[0],1);
                    X[n+R] -= cblas_ddot(n,&A[M-n],1,&X[R],1);
                }
                for (size_t n=M; n<R; ++n)
                {
                    X[n] -= cblas_ddot(M,&A[0],1,&X[n-M],1);
                    X[n+R] -= cblas_ddot(M,&A[0],1,&X[n-M+R],1);
                }
            }
            else
            {
                for (size_t n=1; n<M; ++n)
                {
                    X[n*C] -= cblas_ddot(n,&A[M-n],1,&X[0],(int)C);
                    X[1+n*C] -= cblas_ddot(n,&A[M-n],1,&X[1],(int)C);
                }
                for (size_t n=M; n<R; ++n)
                {
                    X[n*C] -= cblas_ddot(M,&A[0],1,&X[(n-M)*C],(int)C);
                    X[1+n*C] -= cblas_ddot(M,&A[0],1,&X[1+(n-M)*C],(int)C);
                }
            }
        }
        else
        {
            if (iscolmajor)
            {
                for (size_t n=1; n<M; ++n) { cblas_dgemv(CblasColMajor,CblasTrans,n,(int)C,-1.0,&X[0],(int)R,&A[M-n],1,1.0,&X[n],(int)R); }
                for (size_t n=M; n<R; ++n) { cblas_dgemv(CblasColMajor,CblasTrans,M,(int)C,-1.0,&X[n-M],(int)R,&A[0],1,1.0,&X[n],(int)R); }
            }
            else
            {
                for (size_t n=1; n<M; ++n) { cblas_dgemv(CblasRowMajor,CblasTrans,n,(int)C,-1.0,&X[0],(int)C,&A[M-n],1,1.0,&X[n*C],1); }
                for (size_t n=M; n<R; ++n) { cblas_dgemv(CblasRowMajor,CblasTrans,M,(int)C,-1.0,&X[(n-M)*C],(int)C,&A[0],1,1.0,&X[n*C],1); }
            }
        }
    }
    else if (dim==1u)
    {
        if (R==1)
        {
            for (size_t n=1; n<M; ++n) { X[n] -= cblas_ddot(n,&A[M-n],1,&X[0],1); }
            for (size_t n=M; n<C; ++n) { X[n] -= cblas_ddot(M,&A[0],1,&X[n-M],1); }
        }
        else if (R==2)
        {
            if (iscolmajor)
            {
                for (size_t n=1; n<M; ++n)
                {
                    X[n*R] -= cblas_ddot(n,&A[M-n],1,&X[0],(int)R);
                    X[1+n*R] -= cblas_ddot(n,&A[M-n],1,&X[1],(int)R);
                }
                for (size_t n=M; n<C; ++n)
                {
                    X[n*R] -= cblas_ddot(M,&A[0],1,&X[(n-M)*R],(int)R);
                    X[1+n*R] -= cblas_ddot(M,&A[0],1,&X[1+(n-M)*R],(int)R);
                }
            }
            else
            {
                for (size_t n=1; n<M; ++n)
                {
                    X[n] -= cblas_ddot(n,&A[M-n],1,&X[0],1);
                    X[n+C] -= cblas_ddot(n,&A[M-n],1,&X[C],1);
                }
                for (size_t n=M; n<C; ++n)
                {
                    X[n] -= cblas_ddot(M,&A[0],1,&X[n-M],1);
                    X[n+C] -= cblas_ddot(M,&A[0],1,&X[n-M+C],1);
                }
            }
        }
        else
        {
            if (iscolmajor)
            {
                for (size_t n=1; n<M; ++n) { cblas_dgemv(CblasColMajor,CblasNoTrans,(int)R,n,-1.0,&X[0],(int)R,&A[M-n],1,1.0,&X[n*R],1); }
                for (size_t n=M; n<C; ++n) { cblas_dgemv(CblasColMajor,CblasNoTrans,(int)R,M,-1.0,&X[(n-M)*R],(int)R,&A[0],1,1.0,&X[n*R],1); }
            }
            else
            {
                for (size_t n=1; n<M; ++n) { cblas_dgemv(CblasRowMajor,CblasNoTrans,(int)R,n,-1.0,&X[0],(int)C,&A[M-n],1,1.0,&X[n],(int)C); }
                for (size_t n=M; n<C; ++n) { cblas_dgemv(CblasRowMajor,CblasNoTrans,(int)R,M,-1.0,&X[n-M],(int)C,&A[0],1,1.0,&X[n],(int)C); }
            }
        }
    }
    else
    {
        fprintf(stderr,"error in iir_d: dim must be 0 or 1.\n"); return 1;
    }

    return 0;
}

int iir_c (float *X, const char iscolmajor, const size_t R, const size_t C, const float *A, const size_t N, const size_t dim)
{
    const float a[2] = {-1.0f,0.0f}, b[2] = {1.0f,0.0f};
    const size_t M = N - 1;
    int n;

    //Checks
    if (R<1) { fprintf(stderr,"error in iir_c: R (nrows X) must be positive\n"); return 1; }
    if (C<1) { fprintf(stderr,"error in iir_c: C (ncols X) must be positive\n"); return 1; }
    if (N<1) { fprintf(stderr,"error in iir_c: N (filter order) must be positive\n"); return 1; }

    if (dim==0u)
    {
        if (iscolmajor)
        {
            for (size_t n=1; n<M; ++n) { cblas_cgemv(CblasColMajor,CblasTrans,n,(int)C,&a[0],&X[0],(int)R,&A[2*(M-n)],1,&b[0],&X[2*n],(int)R); }
            for (size_t n=M; n<R; ++n) { cblas_cgemv(CblasColMajor,CblasTrans,M,(int)C,&a[0],&X[2*(n-M)],(int)R,&A[0],1,&b[0],&X[2*n],(int)R); }
        }
        else
        {
            for (size_t n=1; n<M; ++n) { cblas_cgemv(CblasRowMajor,CblasTrans,n,(int)C,&a[0],&X[0],(int)C,&A[2*(M-n)],1,&b[0],&X[2*n*C],1); }
            for (size_t n=M; n<R; ++n) { cblas_cgemv(CblasRowMajor,CblasTrans,M,(int)C,&a[0],&X[2*(n-M)*C],(int)C,&A[0],1,&b[0],&X[2*n*C],1); }
        }
    }
    else if (dim==1u)
    {
        if (iscolmajor)
        {
            for (size_t n=1; n<M; ++n) { cblas_cgemv(CblasColMajor,CblasNoTrans,(int)R,n,&a[0],&X[0],(int)R,&A[2*(M-n)],1,&b[0],&X[2*n*R],1); }
            for (size_t n=M; n<C; ++n) { cblas_cgemv(CblasColMajor,CblasNoTrans,(int)R,M,&a[0],&X[2*(n-M)*R],(int)R,&A[0],1,&b[0],&X[2*n*R],1); }
        }
        else
        {
            for (size_t n=1; n<M; ++n) { cblas_cgemv(CblasRowMajor,CblasNoTrans,(int)R,n,&a[0],&X[0],(int)C,&A[2*(M-n)],1,&b[0],&X[2*n],(int)C); }
            for (size_t n=M; n<C; ++n) { cblas_cgemv(CblasRowMajor,CblasNoTrans,(int)R,M,&a[0],&X[2*(n-M)],(int)C,&A[0],1,&b[0],&X[2*n],(int)C); }
        }
    }
    else
    {
        fprintf(stderr,"error in iir_c: dim must be 0 or 1.\n"); return 1;
    }

    return 0;
}

int iir_z (double *X, const char iscolmajor, const size_t R, const size_t C, const double *A, const size_t N, const size_t dim)
{
    const double a[2] = {-1.0,0.0}, b[2] = {1.0,0.0};
    const size_t M = N - 1;
    int n;

    //Checks
    if (R<1) { fprintf(stderr,"error in iir_z: R (nrows X) must be positive\n"); return 1; }
    if (C<1) { fprintf(stderr,"error in iir_z: C (ncols X) must be positive\n"); return 1; }
    if (N<1) { fprintf(stderr,"error in iir_z: N (filter order) must be positive\n"); return 1; }

    if (dim==0u)
    {
        if (iscolmajor)
        {
            for (size_t n=1; n<M; ++n) { cblas_zgemv(CblasColMajor,CblasTrans,n,(int)C,&a[0],&X[0],(int)R,&A[2*(M-n)],1,&b[0],&X[2*n],(int)R); }
            for (size_t n=M; n<R; ++n) { cblas_zgemv(CblasColMajor,CblasTrans,M,(int)C,&a[0],&X[2*(n-M)],(int)R,&A[0],1,&b[0],&X[2*n],(int)R); }
        }
        else
        {
            for (size_t n=1; n<M; ++n) { cblas_zgemv(CblasRowMajor,CblasTrans,n,(int)C,&a[0],&X[0],(int)C,&A[2*(M-n)],1,&b[0],&X[2*n*C],1); }
            for (size_t n=M; n<R; ++n) { cblas_zgemv(CblasRowMajor,CblasTrans,M,(int)C,&a[0],&X[2*(n-M)*C],(int)C,&A[0],1,&b[0],&X[2*n*C],1); }
        }
    }
    else if (dim==1u)
    {
        if (iscolmajor)
        {
            for (size_t n=1; n<M; ++n) { cblas_zgemv(CblasColMajor,CblasNoTrans,(int)R,n,&a[0],&X[0],(int)R,&A[2*(M-n)],1,&b[0],&X[2*n*R],1); }
            for (size_t n=M; n<C; ++n) { cblas_zgemv(CblasColMajor,CblasNoTrans,(int)R,M,&a[0],&X[2*(n-M)*R],(int)R,&A[0],1,&b[0],&X[2*n*R],1); }
        }
        else
        {
            for (size_t n=1; n<M; ++n) { cblas_zgemv(CblasRowMajor,CblasNoTrans,(int)R,n,&a[0],&X[0],(int)C,&A[2*(M-n)],1,&b[0],&X[2*n],(int)C); }
            for (size_t n=M; n<C; ++n) { cblas_zgemv(CblasRowMajor,CblasNoTrans,(int)R,M,&a[0],&X[2*(n-M)],(int)C,&A[0],1,&b[0],&X[2*n],(int)C); }
        }
    }
    else
    {
        fprintf(stderr,"error in iir_z: dim must be 0 or 1.\n"); return 1;
    }

    return 0;
}

#ifdef __cplusplus
}
}
#endif


#ifndef ALG_LOMV
#define ALG_LOMV

#include "string.h"
#include "assert.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_permutation.h>
#include <gsl/gsl_linalg.h>

double linf(int d, gsl_vector * a, gsl_vector * b);

void psi(int p, int q, gsl_vector * theta, gsl_matrix * B, gsl_matrix * V, gsl_vector * Delta, gsl_vector * x);

gsl_vector * ffp(int p, int q, gsl_vector * theta, gsl_matrix * B, gsl_matrix * V, gsl_vector * Delta);

gsl_vector * lo_minvar(int p, int q, gsl_matrix * B, gsl_matrix * V, gsl_vector * Delta);

double * ffp_C_interface(int p, int q, double* theta, double** B, double** V, double* Delta);

double* lo_minvar_C_interface(int p, int q, double**B, double** V, double* Delta);

double* psi_C_interface(int p, int q, double**B, double** V, double* Delta);

#endif


#ifndef TOPO_SL_H
#define TOPO_SL_H

#include <assert.h>

#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_spmatrix.h>
#include <gsl/gsl_splinalg.h>

#include <apf.h>
#include <apfMDS.h>
#include <apfMesh.h>
#include <apfMesh2.h>

#include <apfNumbering.h>
#include <apfShape.h>

#include "topo_disp.h"

// Container for the gsl based linear solver:
class solv_kuprat {
  private:
    solv_kuprat(const solv_kuprat &obj);
    solv_kuprat& operator=( const solv_kuprat& obj );
  public:
    apf::Mesh2* m;
    field_calc* f_calc;

    int n;
    std::map<apf::MeshEntity*, int> v2id;
    std::map<apf::MeshEntity*, bool> v_map;
    std::map<int, apf::MeshEntity*> id2v;

    std::vector<apf::MeshEntity*>* verts;

    gsl_spmatrix* A;
    gsl_spmatrix* C;
    gsl_vector *f;
    gsl_vector *u;

    apf::GlobalNumbering* gn;

    // Functions:
    void numb_vert();
    void numb_verts_in();
    void dest_numb();
    void add_forces(apf::MeshEntity* tri);

    void collect_matrix_verts();
    void collect_matrix();
    void init(apf::Mesh2* m_in);
    void init_verts(apf::Mesh2* m_in);
    void clear();
    double solve(const double tol);

    solv_kuprat(apf::Mesh2* m, field_calc* f_calc_in);
    solv_kuprat(apf::Mesh2* m, field_calc* f_calc_in, std::vector<apf::MeshEntity*>* verts);

    ~solv_kuprat();

    friend std::ostream& operator<<(std::ostream& s, const solv_kuprat& sl);

};

// Container for the gsl based linear solver:
class solv_kuprat_NBC {
  private:
    solv_kuprat_NBC(const solv_kuprat_NBC &obj);
    solv_kuprat_NBC& operator=( const solv_kuprat_NBC& obj );
  public:
    apf::Mesh2* m;
    field_calc* f_calc;

    int n;
    std::map<apf::MeshEntity*, int> v2id;
    std::map<apf::MeshEntity*, bool> v_map;
    std::map<int, apf::MeshEntity*> id2v;

    std::map<apf::MeshEntity*, bool> v_sp;
    std::vector<apf::MeshEntity*>* verts;

    gsl_spmatrix* A;
    gsl_spmatrix* C;
    gsl_vector *f;
    gsl_vector *u;

    apf::GlobalNumbering* gn;

    // Functions:
    void numb_vert();
    void numb_verts_in();
    void dest_numb();
    void add_forces(apf::MeshEntity* tri);

    void collect_matrix_verts();
    void collect_matrix();

		void process_ext_shell();
		void process_ext_shell_verts();

    void init(apf::Mesh2* m_in);
    void init_verts(apf::Mesh2* m_in);
    void clear();
    double solve(const double tol);

    solv_kuprat_NBC(apf::Mesh2* m, field_calc* f_calc_in);
    solv_kuprat_NBC(apf::Mesh2* m, field_calc* f_calc_in, std::vector<apf::MeshEntity*>* verts);

    ~solv_kuprat_NBC();

    friend std::ostream& operator<<(std::ostream& s, const solv_kuprat_NBC& sl);

};
#endif


#ifndef __INS_C_H__
#define __INS_C_H__
/********************************* TRICK HEADER *******************************
PURPOSE:
      (Describe the INS Module On Board, Error equations based on Zipfel, Figure 10.27, space stabilized INS with GPS updates)
LIBRARY DEPENDENCY:
      ((../src/gnc_var.c)
        (../src/Ins_c.c)
        (../../cad/src/global_constants.c)
        (../src/dm_delta_ut.c))
*******************************************************************************/

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_vector.h>
#include <math.h>
#include <stdio.h>
#include "cad_utility_c.h"
#include "math_utility_c.h"
#include "time_utility_c.h"

extern const double __DM_sec2r;
extern const double __DM_arcsec2r;
extern const double __WEII3;
extern const double __SMAJOR_AXIS;
extern const double __GM;
extern const double __WEII3;
extern const double ___PI;
extern const double __EPS;
extern const double __DEG;
extern const double __RAD;
/** INS Variables **/
/* Matrix */
extern gsl_matrix *WEII;
extern gsl_matrix *TBIC;
extern gsl_matrix *TDCI;
extern gsl_matrix *TEIC;
extern gsl_matrix *TBDC;
extern gsl_matrix *TBICI;
extern gsl_matrix *TLI;

/* Vector */
extern gsl_vector *EVBI;
extern gsl_vector *EVBID;
extern gsl_vector *ESBI;
extern gsl_vector *ESBID;
extern gsl_vector *RICI;
extern gsl_vector *RICID;
extern gsl_vector *TBIC_Q;
extern gsl_vector *TBIDC_Q;
extern gsl_vector *SBIIC;
extern gsl_vector *VBIIC;
extern gsl_vector *SBEEC;
extern gsl_vector *VBEEC;
extern gsl_vector *WBICI;
extern gsl_vector *EGRAVI;
extern gsl_vector *VBECD;
extern gsl_vector *INS_I_ATT_ERR;
extern gsl_vector *TESTV;
extern gsl_vector *TMP_old;
extern gsl_vector *VBIIC_old;
extern gsl_vector *POS_ERR;
extern gsl_vector *GRAVGI;
extern gsl_vector *TBDCQ;
extern gsl_vector *VBIIC_old_old;
extern gsl_vector *PHI_C;
extern gsl_vector *DELTA_VEL_C;
extern gsl_vector *PHI_LOW_C;
extern gsl_vector *PHI_HIGH_C;

/* Double */
extern double dbic;
extern double dvbec;
extern double alphacx;
extern double betacx;
extern double thtvdcx;
extern double psivdcx;
extern double alppcx;
extern double phipcx;
extern double loncx;
extern double latcx;
extern double altc;
extern double phibdcx;
extern double thtbdcx;
extern double psibdcx;
extern double ins_pos_err;
extern double ins_vel_err;
extern double ins_tilt_err;
extern double ins_pose_err;
extern double ins_vele_err;
extern double ins_phi_err;
extern double ins_tht_err;
extern double ins_psi_err;

/* Unsigned int */
extern unsigned int gpsupdate;
extern unsigned int liftoff;
extern unsigned int ideal;

extern GPS_TIME gpstime;
extern UTC_TIME utctime;
/*******************/
#ifdef __cplusplus
extern "C" {
#endif
    int load_location(double lonx, double latx, double alt);
    int load_angle(double yaw, double roll, double pitch, GPS_TIME gps_time);
    int load_geodetic_velocity(double alpha0x, double beta0x, double dvbe);
    int calculate_INS_derived_TEI(GPS_TIME gps, gsl_matrix *TEIC);
    int AccelHarmonic(const gsl_vector *SBII, double CS[21][21], int n_max, int m_max, const gsl_matrix *TEIC, gsl_vector *acc_out);
    int DCM_2_Euler_angle(const gsl_matrix *TBD, double *phibdc, double *thtbdc, double *psibdc);
    int calculate_INS_derived_phip(gsl_vector *VBECB, double *phipc);
    int calculate_INS_derived_thtvd(gsl_vector *VBECD, double *thtvd);
    int calculate_INS_derived_psivd(gsl_vector *VBECD, double *psivd);
    int calculate_INS_derived_alpp(gsl_vector *VBECB, double *alpp);
    int calculate_INS_derived_beta(gsl_vector *VBECB, double *beta);
    int calculate_INS_derived_alpha(gsl_vector *VBECB, double *alpha);
    int build_VBEB(double _alpha0x, double _beta0x, double _dvbe, gsl_vector *VBEB);
    int INS_update(const double int_step, double *dvbec, unsigned int liftoff, double *alphacx, double *betacx
                    , double *alppcx, double *phipcx, double *loncx, double *latcx, double *altc, double *psivdcx, double *thtvdcx
                    , double *phibdc, double *thtbdc, double *psibdc
                    , gsl_vector *PHI, gsl_vector *DELTA_VEL
                    , gsl_vector *PHI_HIGH, gsl_vector *PHI_LOW, GPS_TIME gps, gsl_matrix *TEIC
                    , gsl_vector *SBIIC, gsl_vector *VBIIC, gsl_vector *VBIIC_old, gsl_vector *GRAVGI, gsl_matrix *TBIC, gsl_vector *SBEEC
                    , gsl_vector *VBEEC, gsl_matrix *WEII, gsl_matrix *TLI, gsl_matrix *TDCI
                    , gsl_matrix *TBICI, gsl_matrix *TBDC, gsl_vector *TBDCQ);
    int INS_init(GPS_TIME gps_time);
    int INS_alloc();
#ifdef __cplusplus
}
#endif
#endif


/*
    Copyright (c) 2013, Taiga Nomi and the respective contributors
    All rights reserved.

    Use of this source code is governed by a BSD-style license that can be found
    in the LICENSE file.
*/
#pragma once

#include "tiny_dnn/core/params/fully_params.h"

#ifdef CNN_USE_CBLAS
extern "C" {
#include <cblas.h>
}
#endif

namespace tiny_dnn {
namespace kernels {

inline void fully_connected_op_cblas(const tensor_t &in_data,
                                     const vec_t &W,
                                     const vec_t &bias,
                                     tensor_t &out_data,
                                     const core::fully_params &params,
                                     const bool layer_parallelize) {
#ifdef CNN_USE_CBLAS
  size_t out_size       = params.out_size_;
  size_t in_size        = params.in_size_;
  float_t alpha         = 1;
  float_t beta          = 1;
  const float_t *input  = in_data[0].data();
  const float_t *weight = W.data();
  float_t *output       = out_data[0].data();
  if (bias.empty())
    memset(output, 0, sizeof(float_t) * out_size);
  else
    memcpy(output, bias.data(), sizeof(float_t) * out_size);
#ifdef CNN_USE_DOUBLE
  cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 1, out_size, in_size,
              alpha, input, in_size, weight, out_size, beta, output, out_size);
#else
  cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 1, out_size, in_size,
              alpha, input, in_size, weight, out_size, beta, output, out_size);
#endif

#endif  // CNN_USE_CBLAS
}

}  // namespace kernels
}  // namespace tiny_dnn


#ifndef _DSGLD_LDA_MODEL_H__
#define _DSGLD_LDA_MODEL_H__

#include <El.hpp>
#include <gsl/gsl_rng.h>

#include "sgld_model.h"

using std::string;
using std::vector;

namespace dsgld {

class LDAModel : public SGLDModel<double, int> {
 public:
   LDAModel(
       const El::Matrix<int>& X,
       const int K,
       const double alpha,
       const double beta);

  ~LDAModel() {};

  El::Matrix<double> sgldEstimate(const El::Matrix<double>& theta) override;

  El::Matrix<double> nablaLogPrior(const El::Matrix<double>& theta) const override;

  void writePerplexities(const string& filename);

  int NumGibbsSteps() const;
  LDAModel* NumGibbsSteps(const int);

 protected:
  void gibbsSample(
      const El::Matrix<int>& doc,
      const El::Matrix<double>& theta,
      El::Matrix<int>& index_to_topic,
      El::Matrix<int>& topic_counts) const;

  double estimatePerplexity(
      const El::Matrix<double>& theta,
      const El::Matrix<double>& theta_sum_over_w,
      const int num_words_in_doc,
      const El::Matrix<int>& topic_counts) const;

 private:
  const double alpha_;
  const double beta_;
  const int W;
  const int K;
  int numGibbsSteps_;
  vector<double> perplexities_;
  const gsl_rng* rng;
};

}  // namespace dsgld

#endif  // _DSGLD_LDA_MODEL_H__


#ifndef __INTEGRAL_H__
#define __INTEGRAL_H__
#include "integral.h"
#endif

#ifndef __GSLEXTRA_H__
#define __GSLEXTRA_H__
#include "../include/gslextra.h"
#endif

#ifndef __GSLPRINT_H__
#define __GSLPRINT_H__
#include "../include/gslprint.h"
#endif

#include <gsl/gsl_eigen.h>

// calculate the attraction energy matrix element (Z integral)
double nuclear_attraction_energy_matrix_element(orbital * a, orbital * b, atomic_orbital * atom_HEAD);

// calculate the single electron hamiltonian matrix (core hamiltonian matrix) element
double single_electron_hamiltonian_matrix_element(orbital * a, orbital * b, atomic_orbital * atom_HEAD);

// calculate the fock matrix element
double fock_matrix_element(gsl_quad_tensor * v, gsl_matrix * density_matrix, gsl_matrix * h_matrix, int i, int j, int length);

// calculate the Hartree-Fock energy of the system
double HF_energy(gsl_quad_tensor * v, gsl_matrix * density_matrix, gsl_matrix * h_matrix, int length);

// calculate the kinetic energy matrix
void kinetic_energy_matrix(gsl_matrix * dest, orbital * HEAD, int length);

// calculate the attraction energy matrix (Z integrals)
void nuclear_attraction_energy_matrix(gsl_matrix * dest, orbital * HEAD, atomic_orbital * atom_HEAD, int length);

// calculate the single electron hamiltonian matrix (core hamiltonian matrix)
void core_hamiltonian_matrix(gsl_matrix * dest, orbital * HEAD, atomic_orbital * atom_HEAD, int length);

//obtain the quad tensor of the two-electron Coulomb integrals
void two_electron_quad_tensor(gsl_quad_tensor * dest, orbital * HEAD, int length);

//obtain the fock matrix
void fock_matrix(gsl_matrix * dest, gsl_quad_tensor * v, gsl_matrix * density_matrix, gsl_matrix * h_matrix, int length);

// perform initial guess of the coefficient matrix by performing diagonalization of core hamiltonian matrix
void initial_guess(gsl_matrix * dest, gsl_matrix * core_hamiltonian, gsl_matrix * S, int length);

// calculate density matrix from coefficient matrix and the number of electrons
void density_matrix(gsl_matrix * dest, gsl_matrix * coef, int el_num, int length);

//perform RHF as well as printing information
int RHF_SCF_print(double * tot_energy, gsl_vector * energy, gsl_matrix * coef, orbital * HEAD, atomic_orbital * atom_HEAD, int length, int el_num, int iteration_max, double errmax, int countmax, double alpha, int mixing_type, int SCF_INITIAL_FLAG, int SCF_FOCK_FLAG, int SCF_COEF_FLAG, int FOCK_FLAG);

//calculate the nuclei repulsion energy
double nuclei_repulsion(atomic_orbital * atomlist_HEAD);


#pragma once

#ifndef _NOSNOPT
#include <fstream>
#include <iostream>
#include "BasicError.h"
#ifndef _NOGSL
#include <gsl/gsl_vector.h>
#else
#include "FakeGSL.h"
#endif
#include "OptSolver.h"
#include "snopt.hh"
#include "snoptProblem.hh"

using namespace std;

class SnoptSolver : public OptSolver {
	snoptProblem snoptProb;

	integer n; integer neF; integer lenA; integer lenG;

	integer *iAfun;
	integer *jAvar;
	doublereal *A;

	integer *iGfun;
	integer *jGvar;

	doublereal *x;
	doublereal *xlow;
	doublereal *xupp;
	doublereal *xmul;
	integer *xstate;

	doublereal *F;
	doublereal *Flow;
	doublereal *Fupp;
	doublereal *Fmul;
	integer *Fstate;

	integer nxnames;
	integer nFnames = 1;
	char *xnames;
	char *Fnames;

	gsl_vector* result;
	double objectiveVal;

	integer Cold = 0, Basis = 1, Warm = 2;
public:
	SnoptSolver(integer n_, integer neF_, integer lenA_): n(n_), neF(neF_), lenA(lenA_) {
		iAfun = new integer[lenA];
		jAvar = new integer[lenA];
		A = new doublereal[lenA];

		lenG = n*neF;
		iGfun = new integer[lenG];
		jGvar = new integer[lenG];

		x = new doublereal[n];
		xlow = new doublereal[n];
		xupp = new doublereal[n];
		xmul = new doublereal[n];
		xstate = new integer[n];

		F = new doublereal[neF];
		Flow = new doublereal[neF];
		Fupp = new doublereal[neF];
		Fmul = new doublereal[neF];
		Fstate = new integer[neF];

		nxnames = 1;
		nFnames = 1;
		xnames = new char[nxnames*8];
		Fnames = new char[nFnames*8];

		snoptProb.setXNames(xnames, nxnames);
		snoptProb.setFNames(Fnames, nFnames);

		result = gsl_vector_alloc(n);
	}

	~SnoptSolver() {
		delete []iAfun; delete []jAvar; delete []A;
		delete []iGfun; delete []jGvar;
		delete []x; delete []xlow; delete []xupp;
		delete []xmul; delete []xstate;
		delete []F; delete []Flow; delete []Fupp;
		delete []Fmul; delete []Fstate;
		delete []xnames; delete []Fnames;
	}

	virtual void init(char* workspace, integer nef_, DFT df, integer ObjRow, doublereal ObjAdd, doublereal *xlow_, doublereal *xupp_, doublereal *Flow_, doublereal *Fupp_);
	bool optimize(gsl_vector* initState, bool suppressPrint = false);

	virtual gsl_vector* getResults() {
		return result;
	}

	virtual double getObjectiveVal() {
		return objectiveVal;
	}

};

#endif


/**
 *
 * @file core_dgeqp3_tntpiv.c
 *
 *  PLASMA core_blas kernel
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @generated d Tue Jan  7 11:44:50 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

/***************************************************************************
 *
 * @ingroup CORE_double
 *
 *  CORE_dgeqp3_tntpiv computes a QR factorization with column pivoting of a
 *  matrix A:  A*P = Q*R  using Level 3 BLAS.
 *
 *  The matrix Q is represented as a product of elementary reflectors
 *
 *     Q = H(1) H(2) . . . H(k), where k = min(m,n).
 *
 *  Each H(i) has the form
 *
 *     H(i) = I - tau * v * v**T
 *
 *  where tau is a complex scalar, and v is a real/complex vector
 *  with v(1:i-1) = 0 and v(i) = 1; v(i+1:m) is stored on exit in
 *  A(i+1:m,i), and tau in TAU(i).
 *
 *******************************************************************************
 *
 *  Arguments:
 *  ==========
 *
 * @param[in] m
 *          The number of rows of the matrix A. M >= 0.
 *
 * @param[in] n
 *          The number of columns of the matrix A.  N >= 0.
 *
 * @param[in,out] A
 *          A is COMPLEX*16 array, dimension (LDA,N)
 *          On entry, the M-by-N matrix A.
 *          On exit, the upper triangle of the array contains the
 *          min(M,N)-by-N upper trapezoidal matrix R; the elements below
 *          the diagonal, together with the array TAU, represent the
 *          unitary matrix Q as a product of min(M,N) elementary
 *          reflectors.
 *
 * @param[in] lda
 *          The leading dimension of the array A. LDA >= max(1,M).
 *
 * @param[out] IPIV
 *          IPIV is INTEGER array, dimension min(M,N)
 *          The pivot indices; for 1 <= j <= min(M,N), column j of the
 *          tile was interchanged with column IPIV(j).
 *
 * @param[out] TAU
 *          TAU is COMPLEX*16 array, dimension (min(M,N))
 *          The scalar factors of the elementary reflectors.
 *
 * @param[in,out] iwork
 *          iwork is INTEGER array, dimension (N)
 *          On entry, if iwork(J).ne.0, the J-th column of A is permuted
 *          to the front of A*P (a leading column); if iwork(J)=0,
 *          the J-th column of A is a free column.
 *          On exit, if iwork(J)=K, then the J-th column of A*P was the
 *          the K-th column of A.
 *
 * @param[out] INFO
 *          = 0: successful exit.
 *          < 0: if INFO = -i, the i-th argument had an illegal value.
 *
 *******************************************************************************
 *
 * @return
 *          \retval PLASMA_SUCCESS successful exit
 *          \retval <0 if -i, the i-th argument had an illegal value
 *
 ******************************************************************************/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_dgeqp3_tntpiv = PCORE_dgeqp3_tntpiv
#define CORE_dgeqp3_tntpiv PCORE_dgeqp3_tntpiv
#endif
int CORE_dgeqp3_tntpiv(int m, int n,
                       double *A, int lda,
                       int *IPIV, double *tau,
                       int *iwork)
{
    int i, tmp, info;
    memset(iwork, 0, n*sizeof(int));
    info = LAPACKE_dgeqp3(LAPACK_COL_MAJOR, m, n, A, lda, iwork, tau );

    /* Convert IPIV from permutation array, to pivot array
     * WARNING: this is because this kernel is only used in
     * tournament pivoting with rank revealing QR */
    if (info == 0) {
        for(i=0; i<min(m,n); i++) {
            assert(iwork[i] != 0 );

            tmp = iwork[i]-1;
            while( tmp < i ) {
                tmp = IPIV[ tmp ] - 1;
            }
            IPIV[i] = tmp+1;
        }
    }
    return info;
}


#pragma once
#include "halley/maths/vector3.h"
#include <gsl/span>

namespace Halley
{
	class AudioListenerData;

	class AudioChannelData
	{
	public:
		float pan; // TODO, do this right
	};

	class AudioPosition
	{
	public:
		static AudioPosition makeUI(float pan = 0.0f); // -1.0f = left, 1.0f = right
		static AudioPosition makePositional(Vector2f pos, float referenceDistance = 200.0f, float maxDistance = 400.0f);
		static AudioPosition makePositional(Vector3f pos, float referenceDistance = 200.0f, float maxDistance = 400.0f);
		static AudioPosition makeFixed();

		void setMix(size_t srcChannels, gsl::span<const AudioChannelData> dstChannels, gsl::span<float, 16> dst, float gain, const AudioListenerData& listener) const;

		AudioPosition();

	private:
		Vector3f pos;
		float referenceDistance;
		float maxDistance;
		bool isUI;
		bool isPannable;
	};
}


/**************************************************************/
/*! \brief This file contains all .h files
 */
/**************************************************************/

#ifndef __SLIM_H__
#define __SLIM_H__

#define _GNU_SOURCE 1

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_cblas.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_eigen.h>
#include <bcls.h>
#include <GKlib.h>
#include <def.h>
#include <struct.h>
#include <proto.h>
#include <mpi.h>
#include <omp.h>

#endif


/**
 *
 * @file testing_dgemm.c
 *
 *  PLASMA testing routines
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Emmanuel Agullo
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @generated d Tue Jan  7 11:45:18 2014
 *
 **/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <plasma.h>
#include <cblas.h>
#include <lapacke.h>
#include <core_blas.h>
#include "testing_dmain.h"

#undef COMPLEX
#define REAL

static int check_solution(PLASMA_enum transA, PLASMA_enum transB, int M, int N, int K,
                          double alpha, double *A, int LDA,
                          double *B, int LDB,
                          double beta, double *Cref, double *Cplasma, int LDC);

int testing_dgemm(int argc, char **argv)
{
    /* Check for number of arguments*/
    if ( argc != 8) {
        USAGE("GEMM", "alpha beta M N K LDA LDB LDC",
              "   - alpha  : alpha coefficient\n"
              "   - beta   : beta coefficient\n"
              "   - M      : number of rows of matrices A and C\n"
              "   - N      : number of columns of matrices B and C\n"
              "   - K      : number of columns of matrix A / number of rows of matrix B\n"
              "   - LDA    : leading dimension of matrix A\n"
              "   - LDB    : leading dimension of matrix B\n"
              "   - LDC    : leading dimension of matrix C\n");
        return -1;
    }

    PLASMA_Set(PLASMA_TILE_SIZE, 128);
    double alpha = (double) atol(argv[0]);
    double beta = (double) atol(argv[1]);
    int M     = atoi(argv[2]);
    int N     = atoi(argv[3]);
    int K     = atoi(argv[4]);
    int LDA   = atoi(argv[5]);
    int LDB   = atoi(argv[6]);
    int LDC   = atoi(argv[7]);

    double eps;
    int info_solution;
    int i, j, ta, tb;
    int LDAxK = LDA*max(M,K);
    int LDBxN = LDB*max(K,N);
    int LDCxN = LDC*N;

    double *A      = (double *)malloc(LDAxK*sizeof(double));
	#pragma omp register( [LDAxK]A )
    double *B      = (double *)malloc(LDBxN*sizeof(double));
	#pragma omp register( [LDBxN]B )
    double *C      = (double *)malloc(LDCxN*sizeof(double));
	#pragma omp register( [LDCxN]C )
    double *Cinit  = (double *)malloc(LDCxN*sizeof(double));
	#pragma omp register( [LDCxN]Cinit )
    double *Cfinal = (double *)malloc(LDCxN*sizeof(double));
	#pragma omp register( [LDCxN]Cfinal )

    /* Check if unable to allocate memory */
    if ((!A)||(!B)||(!Cinit)||(!Cfinal)){
        printf("Out of Memory \n ");
        return -2;
    }

    eps = LAPACKE_dlamch_work('e');
    printf("\n");
    printf("------ TESTS FOR PLASMA DGEMM ROUTINE -------  \n");
    printf("            Size of the Matrix %d by %d\n", M, N);
    printf("\n");
    printf(" The matrix A is randomly generated for each test.\n");
    printf("============\n");
    printf(" The relative machine precision (eps) is to be %e \n",eps);
    printf(" Computational tests pass if scaled residuals are less than 10.\n");

    /*----------------------------------------------------------
     *  TESTING DGEMM
     */

    /* Initialize A, B, C */
    LAPACKE_dlarnv_work(IONE, ISEED, LDAxK, A);
    LAPACKE_dlarnv_work(IONE, ISEED, LDBxN, B);
    LAPACKE_dlarnv_work(IONE, ISEED, LDCxN, C);

#ifdef COMPLEX
    for (ta=0; ta<3; ta++) {
        for (tb=0; tb<3; tb++) {
#else
    for (ta=0; ta<2; ta++) {
        for (tb=0; tb<2; tb++) {
#endif
            for ( i = 0; i < M; i++)
                for (  j = 0; j < N; j++)
                    Cinit[LDC*j+i] = C[LDC*j+i];
            for ( i = 0; i < M; i++)
                for (  j = 0; j < N; j++)
                    Cfinal[LDC*j+i] = C[LDC*j+i];

            /* PLASMA DGEMM */
            PLASMA_dgemm(trans[ta], trans[tb], M, N, K, alpha, A, LDA, B, LDB, beta, Cfinal, LDC);

            /* Check the solution */
            info_solution = check_solution(trans[ta], trans[tb], M, N, K,
                                           alpha, A, LDA, B, LDB, beta, Cinit, Cfinal, LDC);

            if (info_solution == 0) {
                printf("***************************************************\n");
                printf(" ---- TESTING DGEMM (%s, %s) ............... PASSED !\n", transstr[ta], transstr[tb]);
                printf("***************************************************\n");
            }
            else {
                printf("************************************************\n");
                printf(" - TESTING DGEMM (%s, %s) ... FAILED !\n", transstr[ta], transstr[tb]);
                printf("************************************************\n");
            }
        }
    }
#ifdef _UNUSED_
    }}
#endif
    free(A); free(B); free(C);
    free(Cinit); free(Cfinal);

    return 0;
}

/*--------------------------------------------------------------
 * Check the solution
 */

static int check_solution(PLASMA_enum transA, PLASMA_enum transB, int M, int N, int K,
                          double alpha, double *A, int LDA,
                          double *B, int LDB,
                          double beta, double *Cref, double *Cplasma, int LDC)
{
    int info_solution;
    double Anorm, Bnorm, Cinitnorm, Cplasmanorm, Clapacknorm, Rnorm, result;
    double eps;
    double beta_const;

    double *work = (double *)malloc(max(K,max(M, N))* sizeof(double));
    int Am, An, Bm, Bn;

    beta_const  = -1.0;

    if (transA == PlasmaNoTrans) {
        Am = M; An = K;
    } else {
        Am = K; An = M;
    }
    if (transB == PlasmaNoTrans) {
        Bm = K; Bn = N;
    } else {
        Bm = N; Bn = K;
    }

    Anorm       = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), Am, An, A,       LDA, work);
    Bnorm       = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), Bm, Bn, B,       LDB, work);
    Cinitnorm   = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M,  N,  Cref,    LDC, work);
    Cplasmanorm = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M,  N,  Cplasma, LDC, work);

    cblas_dgemm(CblasColMajor, (CBLAS_TRANSPOSE)transA, (CBLAS_TRANSPOSE)transB, M, N, K,
                (alpha), A, LDA, B, LDB, (beta), Cref, LDC);

    Clapacknorm = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M, N, Cref, LDC, work);

    cblas_daxpy(LDC * N, (beta_const), Cplasma, 1, Cref, 1);

    Rnorm = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M, N, Cref, LDC, work);

    eps = LAPACKE_dlamch_work('e');

    printf("Rnorm %e, Anorm %e, Bnorm %e, Cinitnorm %e, Cplasmanorm %e, Clapacknorm %e\n",
           Rnorm, Anorm, Bnorm, Cinitnorm, Cplasmanorm, Clapacknorm);

    result = Rnorm / ((Anorm + Bnorm + Cinitnorm) * N * eps);
    printf("============\n");
    printf("Checking the norm of the difference against reference DGEMM \n");
    printf("-- ||Cplasma - Clapack||_oo/((||A||_oo+||B||_oo+||C||_oo).N.eps) = %e \n",
           result);

    if (  isnan(Rnorm) || isinf(Rnorm) || isnan(result) || isinf(result) || (result > 10.0) ) {
         printf("-- The solution is suspicious ! \n");
         info_solution = 1;
    }
    else {
         printf("-- The solution is CORRECT ! \n");
         info_solution= 0 ;
    }

    free(work);

    return info_solution;
}

int timing_dgemm(int argc, char **argv)
{

	int transa; int transb;
	int m, n, k;
	double alpha, beta;
	int lda, ldb, ldc;
	double *A, *B, *C;
	double *C0;
	int bs, rep;
	int i;

	double start, end;
	double elapsed;
	FILE *log;
	int num_threads;

	//gemm transA, transB, M, N, K, alpha, A, lda, B, ldb, beta, C, ldc
	if (argc != 12) {
		fprintf(stderr, "GEMMs transa transb m n k alpha lda ldb beta ldc bs rep\n");
		return 1;;
	}

	sscanf(argv[0], "%d", &transa);
	sscanf(argv[1], "%d", &transb);

	if ( !transa )
		transa = PlasmaNoTrans;
	else
		transa = PlasmaTrans;
	if ( !transb )
		transb = PlasmaNoTrans;
	else
		transb = PlasmaTrans;

	sscanf(argv[2], "%d", &m);
	sscanf(argv[3], "%d", &n);
	sscanf(argv[4], "%d", &k);
	sscanf(argv[5], "%lf", &alpha);
	sscanf(argv[6], "%d", &lda);
	sscanf(argv[7], "%d", &ldb);
	sscanf(argv[8], "%lf", &beta);
	sscanf(argv[9], "%d", &ldc);
	sscanf(argv[10], "%d", &bs);
	sscanf(argv[11], "%d", &rep);

	int dimA = max(m,k) * lda;
	int dimB = max(n,k) * ldb;
	int dimC = max(m,n) * ldc;
	A = malloc( dimA * sizeof(double));
	#pragma omp register ([dimA]A)
	B = malloc( dimB * sizeof(double));
	#pragma omp register ([dimB]B)
	C = malloc( dimC * sizeof(double));
	#pragma omp register ([dimC]C)

	LAPACKE_dlarnv(IONE, ISEED, dimA, A);
	LAPACKE_dlarnv(IONE, ISEED, dimB, B);
	LAPACKE_dlarnv(IONE, ISEED, dimC, C);

	PLASMA_Set( PLASMA_TILE_SIZE, bs);

	elapsed = 0.0;
	for ( i = 0; i < rep; i++ ) {
		start = gtime();
		PLASMA_dgemm(transa, transb, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc);
		end = gtime();
		elapsed += end - start;
	}

	num_threads = omp_get_max_threads();
	dump_info("plasma_dgemm.log", num_threads, elapsed, rep);

	free(A); free(B); free(C);
    return 0;
}


/**
 * @file mcc_localizer.h
 * @brief grid search based localization
 * @author Kenichi Kumatani
 */

#ifndef MCC_LOCALIZER_H
#define MCC_LOCALIZER_H
#include <stdio.h>
#include <assert.h>
#include <float.h>

#include <gsl/gsl_block.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_fft_real.h>
#include <gsl/gsl_fft_complex.h>
#include <common/refcount.h>
#include "common/jexception.h"

#include "stream/stream.h"
#include "feature/feature.h"
//#include "modulated/modulated.h"

/**
   @brief  construct a search grid for source localization and return the time delay corresponding to each position on the grid.
   @note In order to do efficient grid-search for the source position which provides the maximum/minimum objective function, each cell size of a search grid has to be "reasonable".
   @usage
   After you construct an instance, you have to
   1. set the geometry of the microphone array with setRadius(), setDistanceBtwMicrophones() or setPositionsOfMicrophones().
   2. obtain the time delays at the source position with getTimeDelays(), and
   3. go to the next search candidate on the grid with nextSearchGrid().
 */
class SearchGridBuilder {
 public:
  SearchGridBuilder( int nChan, bool isFarField, unsigned int samplingFreq=16000);
  virtual ~SearchGridBuilder();

  const gsl_vector *getSearchPosition(){return(const gsl_vector *)_hypopos;}
  float maxTimeDelay(){return _maxTimeDelay;}
  size_t chanN(){return(_mpos->size1);}
  unsigned int samplingFrequency(){return(_samplingFreq);}
  void reset();

  virtual const gsl_vector *getTimeDelays(){return NULL;}
  virtual bool nextSearchGrid(){return false;}

protected:
  virtual const gsl_vector *nextSearchGridFF(){return NULL;}
  const gsl_vector *nextSearchGridNF();

protected:
  const bool _isFarField; /* if _isFarField = true, the far-filed is assumed. Otherwise, the near-field is assumed. */
  unsigned int _samplingFreq;
  float _maxTimeDelay;
  gsl_matrix* _mpos;
  gsl_vector *_hypopos;
  gsl_vector *_delays;
  float _constV;
};

typedef refcount_ptr<SearchGridBuilder> SearchGridBuilderPtr;

class SGB4LinearArray : public SearchGridBuilder{
public:
  SGB4LinearArray( int nChan, bool isFarField, unsigned int samplingFreq=16000);
  void setDistanceBtwMicrophones( float distance );
  void setPositionsOfMicrophones( const gsl_matrix* mpos );
  virtual const gsl_vector *getTimeDelays();
  virtual bool nextSearchGrid();

protected:
  const gsl_vector *nextSearchGridFF();
};

typedef Inherit<SGB4LinearArray, SearchGridBuilderPtr> SGB4LinearArrayPtr;

class SGB4CircularArray : public SearchGridBuilder {
public:
  SGB4CircularArray( int nChan, bool isFarField, unsigned int samplingFreq=16000 );
  void setRadius( float radius, float height=0.0 );
  virtual const gsl_vector *getTimeDelays();
  virtual bool nextSearchGrid();

protected:
  const gsl_vector *nextSearchGridFF();
};

typedef Inherit<SGB4CircularArray, SearchGridBuilderPtr> SGB4CircularArrayPtr;

/**
   @brief keeps samples in the previous block.
          this object is used in order to fill the gap between previous and current block data.
*/
class SampleHolder {
public:
  SampleHolder( size_t chanN, size_t maxSampleDelay ):
    _filledNum(0),_buffer(gsl_matrix_float_calloc(chanN,maxSampleDelay))
  {}

  ~SampleHolder()
  {
    gsl_matrix_float_free(_buffer);
  }

  void setSamples( size_t chanX, gsl_vector_float *samples )
  {
    size_t maxSampleDelay = _buffer->size2;

    if( samples->size >= maxSampleDelay ){
      size_t frameY = samples->size - maxSampleDelay;
      for(size_t frameX=0;frameX<maxSampleDelay;frameX++,frameY++){
	gsl_matrix_float_set( _buffer, chanX, frameX, gsl_vector_float_get( samples, frameY ) );
      }
      _filledNum = maxSampleDelay;
    }
    else{
      // shift the elements of the buffer
      size_t frameN = maxSampleDelay - samples->size;
      for(size_t frameX=0;frameX<frameN;frameX++){
	gsl_matrix_float_set( _buffer, chanX, frameX, gsl_matrix_float_get( _buffer, chanX, frameX + samples->size ) );
      }
      // hold the samples of the current block
      size_t frameY = 0;
      for(size_t frameX=frameN;frameX<maxSampleDelay;frameX++,frameY++){
	gsl_matrix_float_set( _buffer, chanX, frameX, gsl_vector_float_get( samples, frameY ) );
      }
      _filledNum += samples->size;
      if(  _filledNum > maxSampleDelay )
	_filledNum = maxSampleDelay;
    }
  }

  float getSample( int chanX, int minusFrameX )
  {
    return gsl_matrix_float_get( _buffer, chanX, _buffer->size2 + minusFrameX );
  }

  int nFilled(){ return _filledNum;}

private:
  int  _filledNum;
  gsl_matrix_float *_buffer; /* the number of channels X samples */
};

typedef refcount_ptr<SampleHolder> SampleHolderPtr;

/**
   @brief hold information for a position estimate.
 */
class SourceCandidate {
public:
  SourceCandidate(size_t chanN, double costV=100000):
    _costV(costV), _chanN(chanN){
    _sampledelay = new int[chanN];
    _position = gsl_vector_calloc( 3 );
    _eigenvalues = gsl_vector_alloc( chanN );
  }
  ~SourceCandidate(){
    delete [] _sampledelay;
    gsl_vector_free(_position);
    gsl_vector_free( _eigenvalues );
  }
  void setSourceInfo( int *tau, const gsl_vector *position, double costV, gsl_vector *eigenvalues ){
    for(size_t i=0;i<_chanN;i++){_sampledelay[i]=tau[i];}
    gsl_vector_memcpy(_position, (gsl_vector *)position);
    _costV = costV;
    gsl_vector_memcpy(_eigenvalues, eigenvalues);
  }
  void setConstV( double costV=100000 ){
    _costV = costV;
  }

  double _costV; /* value of the cost function */
  int *_sampledelay;
  gsl_vector *_position;
  gsl_vector *_eigenvalues;
private:
  size_t _chanN;
};

/**
   @class estimate the source positions which provide the larger multi-channel cross correlation values.
   @usage
   1. setChannel()
   2. next()
   3.

   @note The algorithm implemented here is described in:
          J. Chen, J. Benesty and Y. Huang, "Robust Time Delay Estimation Exploiting Redundancy Among Multiple Microphones", IEEE Trans. SAP, vol.11, Sep. 2003.
*/

class MCCLocalizer : public VectorFeatureStream {
public:
  MCCLocalizer( SearchGridBuilderPtr &sgbPtr, size_t maxSource=1, const String& nm= "MCCSourceLocalizer" );
  ~MCCLocalizer();
  virtual const gsl_vector* next(int frameX = -5);
  virtual void  reset();
  //void setChannel(SampleFeaturePtr& chan);
  void setChannel(VectorFloatFeatureStreamPtr& chan);
  /*@brief obtain a relative delay at a microphone which corresponds to the best candidate */
  int getDelayedSample( int chanX ){return _sourceCandidates[0]->_sampledelay[chanX];}
  /*@brief obtain the maximum MCCC value */
  double getMaxMCCC(){/* mistake: it was getMinMCCC() */
#ifdef _NO_LOG_
    return( 1 -_sourceCandidates[0]->_costV );
#else
    return( 1 -exp(_sourceCandidates[0]->_costV) );
#endif
  }
  /*@brief obtain the best candidate of the position estimates */
  const gsl_vector* getPosition(){return((const gsl_vector* )_sourceCandidates[0]->_position);}

  /*@brief obtain a relative delay at a microphone which corresponds to the N-th best candidate */
  int getNthBestDelayedSample( int nth, int chanX ){return _sourceCandidates[nth]->_sampledelay[chanX];}
  /*@brief obtain the N-th best MCCC value */
  double getNthBestMCCC( int nth ){
#ifdef _NO_LOG_
    return( 1 -_sourceCandidates[nth]->_costV );
#else
    return( 1 - exp(_sourceCandidates[nth]->_costV) );
#endif
  }
  /*@brief obtain theN-th  best candidate of the position estimates */
  const gsl_vector* getNthBestPosition( int nth ){return((const gsl_vector* )_sourceCandidates[nth]->_position);}

  const gsl_vector* getEigenValues(){return ((const gsl_vector*)_sourceCandidates[0]->_eigenvalues);}
  gsl_matrix *getR(){return _R;}

protected:
  bool setIncomingData( int frameX );
  void doEigenValueDecomposition();
  double calcObjectiveFunction( bool normalizeVariance = true );
private:
  gsl_matrix *calcCovarianceMatrix();

private:
  gsl_vector* search( int frameX = -5 );

protected:
  typedef list<VectorFloatFeatureStreamPtr>   _ChannelList; //typedef list<SampleFeaturePtr>   _ChannelList;
  typedef _ChannelList::iterator   _ChannelIterator;
  _ChannelList                     _channelList;

  SearchGridBuilderPtr _sgbPtr;
  vector<const gsl_vector_float *> _blockList;
  int *_tau; /* sample delays */
  gsl_vector *_x;
  gsl_matrix *_R; /* covariance matrix */
  gsl_matrix *_Rcopy;
  double _detR;
  void *_workspace;
  gsl_vector *_eigenvalues;
  SampleHolderPtr _shPtr; /* keep samples of the block processed at a prevous frame */
  vector<SourceCandidate *> _sourceCandidates; /* _sourceCandidates[n] is the N-th best candidate */
};

typedef Inherit<MCCLocalizer, VectorFeatureStreamPtr> MCCLocalizerPtr;

/**
   @class calculate multichannel cross correlation coefficient givin a position or time delays.
*/
class MCCCalculator : public MCCLocalizer {
public:
  MCCCalculator( SearchGridBuilderPtr &sgbPtr, bool normalizeVariance=true, const String& nm= "MCCCalculator" );
  ~MCCCalculator();
  void setTimeDelays( gsl_vector *delays );
  double getMCCC();
  double getCostV();
  virtual const gsl_vector* next(int frameX = -5);
  virtual void  reset();
private:
  gsl_matrix *calcCovarianceMatrix( gsl_vector *delays );

private:
  gsl_vector *_delays;
  bool        _normalizeVariance;
};

typedef Inherit<MCCCalculator, MCCLocalizerPtr> MCCCalculatorPtr;

/**
   @class recursively estimate the source position which provides the multi-channel cross correlation.
   @usage

   @note The algorithm implemented here is described in:
         J. Benesty, J. Chen and Y. Huang, "Time-Delay Estimation via Linear Interpolation and Cross Correlation", IEEE Trans. SAP, vol.12, Sep. 2004.
*/
class RMCCLocalizer : public MCCLocalizer {
public:
  RMCCLocalizer( SearchGridBuilderPtr &sgbPtr, float lambda, size_t maxSource=1, const String& nm= "RMCCSourceLocalizer" );
  ~RMCCLocalizer();
  virtual const gsl_vector* next(int frameX = -5);
  virtual void  reset();

private:
  void calcInverseMatrix();
  void updateParameters();

  gsl_matrix *_invR; /* an inverse matrix of the covariance matrix */
  gsl_vector *_kd; /* a priori Kalman gain vector */

  //gsl_vector *_old_x;
  gsl_matrix *_old_R; /* covariance matrix at a previous frame */
  gsl_matrix *_old_invR;
  gsl_vector *_old_kd; /* a priori Kalman gain vector at a previous frame */
};

typedef Inherit<RMCCLocalizer, MCCLocalizerPtr> RMCCLocalizerPtr;

#endif


#ifdef USING_FFTW

#include <fftw.h>

#elif defined USING_FFTW3

#include <fftw3.h>

#else

typedef double fftw_real;
typedef struct {
     fftw_real re, im;
} fftw_complex_orig;
typedef fftw_real HPCC_Complex[2];
typedef HPCC_Complex fftw_complex;
#endif
typedef enum {
     FFTW2_FORWARD = -1, FFTW2_BACKWARD = 1
} fftw_direction;

struct hpcc_fftw_plan_struct {
  fftw_complex *w1, *w2, *ww1, *ww2, *ww3, *ww4, *c, *d;
  int n, c_size, d_size;
  int flags;
  fftw_direction dir;
};
typedef struct hpcc_fftw_plan_struct *hpcc_fftw_plan;

extern hpcc_fftw_plan HPCC_fftw_create_plan(int n, fftw_direction dir, int flags);
extern void HPCC_fftw_destroy_plan(hpcc_fftw_plan plan);
extern void HPCC_fftw_one(hpcc_fftw_plan plan, fftw_complex *in, fftw_complex *out);

#ifdef USING_FFTW

#elif defined USING_FFTW3

#define c_re(c)  ((c)[0])
#define c_im(c)  ((c)[1])

#else

typedef struct hpcc_fftw_plan_struct *fftw_plan;

#define c_re(c)  ((c)[0])
#define c_im(c)  ((c)[1])

#define fftw_malloc malloc
#define fftw_free free
/* flags for the planner */
#define  FFTW_ESTIMATE (0)
#define  FFTW_MEASURE  (1)

#define FFTW_OUT_OF_PLACE (0)
#define FFTW_IN_PLACE (8)
#define FFTW_USE_WISDOM (16)

#define fftw_create_plan HPCC_fftw_create_plan
#define fftw_destroy_plan HPCC_fftw_destroy_plan
#define fftw_one HPCC_fftw_one

#endif


// Authors: David Blei (blei@cs.princeton.edu)
//          Sean Gerrish (sgerrish@cs.princeton.edu)
//
// Copyright 2011 Sean Gerrish and David Blei
// All Rights Reserved.
//
// See the README for this package for details about modifying or
// distributing this software.

#ifndef LDASEQ_H
#define LDASEQ_H

#include <sys/stat.h>
#include <sys/types.h>

#include "gsl-wrappers.h"
#include "lda.h"

#define LDA_SEQ_EM_THRESH 1e-4
#define SAVE_LAG 10

/*
 * an lda sequence is a collection of simplex sequences for K topics
 * and an alpha vector
 *
 */

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <stdlib.h>
#include <assert.h>

#include "param.h"
#include "ss-lm.h"
#include "data.h"
#include "lda.h"

#define LDA_SEQ_EM_THRESHOLD 1e-5;

// lda sequence variational posterior distribution

// === allocation and initialization ===

inf_var* inf_var_alloc(int number_topics,
		       corpus_seq_t* corpus_seq);
void inf_var_free(inf_var* ptr);

// initialize lda sequence from lda model topics

void init_lda_seq_from_ss(lda_seq* model,
                          double topic_chain_variance,
                          double topic_obs_variance,
                          double alpha,
                          gsl_matrix* init_suffstats);

// === fitting ===

// infer a corpus with an lda-seq

double update_inf_var(lda_seq* seq,
		      const corpus_seq_t* data,
		      gsl_matrix** phi,
		      size_t t,
		      const char* root);
double update_inf_var_multiple(lda_seq* seq,
			       const corpus_seq_t* data,
			       gsl_matrix** phi,
			       size_t t,
			       const char* root);
void update_inf_reg(lda_seq* seq,
		    const corpus_seq_t* data,
		    gsl_matrix** phi,
		    size_t t,
		    const char* root);

double lda_seq_infer(lda_seq* model,
                     const corpus_seq_t* data,
                     gsl_matrix** suffstats,
                     gsl_matrix* gammas,
                     gsl_matrix* lhoods,
		     int iter,
		     const char* file_root);

// fit lda sequence from sufficient statistics

double fit_lda_seq(lda_seq* m,
                   const corpus_seq_t* data,
                   const corpus_seq_t* heldout,
                   const char* file_root);

void update_lda_seq_ss(int time,
                       const doc_t* doc,
                       const lda_post* post,
                       gsl_matrix** ss);

double fit_lda_seq_topics(lda_seq* model,
                          gsl_matrix** ss);

// === reading and writing ===

// read and write a lda sequence

void write_lda_seq(const lda_seq* m, const char* root);

lda_seq* read_lda_seq(const char* root, corpus_seq_t* data);

// write lda sequence sufficient statistics

void write_lda_seq_suffstats(lda_seq* m,
                             gsl_matrix** topic_ss,
                             const char* root);

// new lda sequence

lda_seq* new_lda_seq(corpus_seq_t* data,
		     int W,
		     int T,
		     int K);

void make_lda_from_seq_slice(lda* lda_m,
                             lda_seq* lda_seq_m,
                             int time);

#endif


// @file kn2row_conv.c
//
//  \date Created on: Sep 23, 2017
//  \author Gopalakrishna Hegde
//
//   Description:
//
//
//
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <cblas.h>
#include "common_types.h"
#include "data_reshape.h"
#include "utils.h"

//
// col_shift : +ve --> shift left overlap mat , -ve --> shift right overlap mat
// or shift left base mat and keep overlap mat as it is.
//
//
// row_shift : +ve (coeff is down the center coeff) --> shift up overlap mat ,
// -ve --> shift down overlap mat or shift up the base mat.
void MatrixShiftAdd(float *base_mat,
                     int base_no_rows, int base_no_cols,
                     float *overlap_mat,
                     int ov_no_rows, int ov_no_cols,
                     int row_shift, int col_shift) {
  if (row_shift == 0 && col_shift == 0 && (base_no_rows == ov_no_rows) &&
      (base_no_cols == ov_no_cols)) {
    // normal matrix add
    cblas_saxpy(base_no_rows * base_no_cols, 1.0, overlap_mat, 1, base_mat, 1);
    return;
  }
  int rows_to_add, cols_to_add;
  int base_row_start, base_col_start;
  int ov_row_start, ov_col_start;
  // without padding case
  if (ov_no_rows > base_no_rows) {
    rows_to_add = base_no_rows;
    cols_to_add = base_no_cols;
    base_row_start = 0;
    base_col_start = 0;
    ov_row_start = row_shift < 0? -row_shift : 0;
    ov_col_start = col_shift < 0? -col_shift : 0;

  } else {
    rows_to_add = ov_no_rows - abs(row_shift);
    cols_to_add = ov_no_cols - abs(col_shift);

    ov_col_start = col_shift > 0? col_shift : 0;
    ov_row_start = row_shift > 0? row_shift : 0;
    base_row_start = row_shift < 0? -row_shift : 0;
    base_col_start = col_shift < 0? -col_shift : 0;
  }

  for (int r = 0; r < rows_to_add; ++r) {
    int base_mat_offset = (r + base_row_start) * base_no_cols + base_col_start;
    int overlap_mat_offset = (r + ov_row_start) * ov_no_cols + ov_col_start;
    cblas_saxpy(cols_to_add, 1.0, overlap_mat + overlap_mat_offset, 1,
                base_mat + base_mat_offset, 1);
  }
}

/* Ker2Row convolution implementations.
 *
 * Assumptions:
 * 1. in_data is in NCHW format.
 * 2. filters are in MCKK format where M is the no of output maps.
 * 3. Stride will always be 1.
 * 4. pad will be zero or kernel_size / 2
 *
 * Output will be in NCHW format.
 */
bool Kn2RowConvLayer(const float *in_data, const float *filters,
                         const float *bias, TensorDim in_dim,
                         TensorDim filt_dim, int stride, int pad, int group,
                         float *output) {
  // Currently we have limited support.
  assert(group == 1);
  assert((pad == 0) || (pad == filt_dim.w / 2));
  assert(in_dim.n == 1);
  assert(filt_dim.h == filt_dim.w);
  assert(stride == 1);

  // Output dimensions.
  TensorDim out_dim;
  out_dim.w = (in_dim.w + (pad + pad) - filt_dim.w) / stride + 1;
  out_dim.h = (in_dim.h + (pad + pad) - filt_dim.h) / stride + 1;
  out_dim.c = filt_dim.n;
  out_dim.n = in_dim.n;

  // Re-arrange filters in the  k x k x no_out_maps x no_in_maps.
  // We can avoid this if the filters are already reshaped in this format.
  float *kkmc_filters = malloc(filt_dim.n * filt_dim.c * filt_dim.h *
                               filt_dim.w * sizeof(float));
  NCHW2HWNC(filters, filt_dim.n, filt_dim.c, filt_dim.h, filt_dim.w,
            kkmc_filters);

  // Just for convenience
  int H = in_dim.h;
  int W = in_dim.w;
  float alpha = 1.0;
  float beta = 0.0;

  // We need separate buffer because GEMM output will have width = H*W even
  // if there is no padding (pad = 0).
  float *gemm_output = malloc(out_dim.c * H * W * sizeof(float));

  // Prefill output buffer with bias if present else set to zero.
  if (bias) {
    for (int m = 0; m < out_dim.c; ++m) {
      for (int a = 0; a < out_dim.h * out_dim.w; ++a) {
        output[m * out_dim.h * out_dim.w + a] = bias[m];
      }
      // For batch size > 1
      for (int b = 1; b < out_dim.n; ++b) {
        memcpy(output + b * out_dim.c * out_dim.h * out_dim.w,
               output, out_dim.c * out_dim.h * out_dim.w * sizeof(float));
      }
    }
  } else {
    memset(output, 0, out_dim.n * out_dim.c * out_dim.h * out_dim.w *
           sizeof(float));
  }

  for (int kr = 0; kr < filt_dim.h; kr++) {
    int row_shift = kr - filt_dim.h / 2;
    for (int kc = 0; kc < filt_dim.w; kc++) {
      int group_no = kr * filt_dim.w + kc;
      int col_shift = kc - filt_dim.w / 2;
      // Matrix dimensions - A -> mxk B -> kxn  C --> mxn
      int m = filt_dim.n;
      int k = filt_dim.c;
      int n = in_dim.h * in_dim.w;
      // This is just 1x1 convolution
      cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
                  m, n, k, alpha, kkmc_filters + group_no * m * k,
                  k, in_data, n, beta, gemm_output, n);
      // Slide the resulting matrix which has contribution from one of the
      // KxK kernel coefficients and add to the output.
      for (int omap = 0; omap < filt_dim.n; omap++) {
        MatrixShiftAdd(output + omap * out_dim.h * out_dim.w,
                        out_dim.h, out_dim.w,
                        gemm_output + omap * H * W,
                        H, W, row_shift, col_shift);
      }
    }
  }
  free(kkmc_filters);
  free(gemm_output);
  return true;
}


/* Leaf bindings for the GNU Scientific Library

   Note that the GSL is GPL, not LGPL!.  If you include this code in
   the libprim build, the result will be covered under the GPL.
 */

#ifndef _LEAF_GSL_H_
#define _LEAF_GSL_H_

#include <gsl/gsl_math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_poly.h>
#include <leaf/grid.h>

/* Direct gsl_ -> grid_ adapters. */
int grid_linalg_SV_decomp(grid *A, grid *V, grid *S, grid *work);
int grid_linalg_SV_solve(grid *gU, grid *gV, grid *gS, grid *gb, grid *gx);

int grid_blas_dgemv(int transA, double alpha, grid *gA, grid *gx, double beta, grid *gy);
int grid_blas_dgemm(int transA, int transB, double alpha, grid *gA, grid *gB, double beta, grid *gC);
int grid_unfold(grid *gA, grid *gx, grid *gout);

#endif


#include <stdio.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_chebyshev.h>

double
f (double x, void *p)
{
  p = 0;
  if (x < 0.5)
    return 0.25;
  else
    return 0.75;
}

int
main (void)
{
  double x;

  gsl_cheb_series *cs = gsl_cheb_alloc (40);

  gsl_function F;

  F.function = f;
  F.params = 0;

  gsl_cheb_init (cs, &F, 0.0, 1.0);

  for (x = -1; x < 2; x += 0.001)
    {
      double r10 = gsl_cheb_eval_n (cs, 10, x);
      double r40 = gsl_cheb_eval (cs, x);
      printf ("%g %g %g %g\n", x, GSL_FN_EVAL (&F, x), r5, r40);
    }

  gsl_cheb_free (cs);
}


/**
 * Simple and fast kmeans-implementation making use of multithreading.
 *
 * Author: Anders Bennehag
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Anders Bennehag
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <pthread.h>
#include <assert.h>
#include <gsl/gsl_rng.h>
#include "fastkmeans/fastkmeans.h"

typedef enum {
    SUCCESS=0,
    ALLOCATION_ERROR,
} fkm_error;

typedef enum {
    WAIT,
    RELEASED,
    EXIT,
} barrier_status;

typedef struct {
    const gsl_matrix* points;
    gsl_matrix* clusters;
    const size_t num_points;
    const size_t dim;
    const size_t k;
    unsigned* owner_of;
} targs;

typedef struct {
    pthread_mutex_t lock;
    pthread_cond_t cond;
    int count;
    int goal;
    barrier_status status;
} tbarrier;

typedef struct {
    targs* args;
    size_t thread_id;
    pthread_t pthread_id;
    size_t num_threads;
    tbarrier* bar_start;
    tbarrier* bar_finish;
} thread_meta;

typedef struct {
    thread_meta* threads;
    const size_t num_threads;
    tbarrier bar_start;
    tbarrier bar_finish;
} thread_pool;

int fkm_clusters_assignment(const thread_pool* tpool);
double fkm_clusters_update(const targs* args);
double fkm_matrix_diff(gsl_matrix* a, gsl_matrix* b);
int fkm_threads_init(thread_pool* tpool);
int fkm_threads_join(thread_pool* tpool);
int barrier_finish(tbarrier* bar);

int fkm_kmeans(const gsl_matrix* points, gsl_matrix* clusters,
               size_t max_iter, int num_threads) {

    if (num_threads<0) {
        printf("Invalid number of threads\n");
        return 1;
    }

    size_t num_points = points->size1;
    unsigned owner_of[num_points];
    targs args = {
        .points=points,
        .clusters=clusters,
        .num_points=num_points,
        .dim=points->size2,
        .k=clusters->size1,
        .owner_of=owner_of,
    };

    thread_meta tmeta[num_threads];
    thread_pool tpool = {
        .num_threads=num_threads,
        .threads=tmeta,
        .bar_start={
            .lock=PTHREAD_MUTEX_INITIALIZER,
            .cond=PTHREAD_COND_INITIALIZER,
            .goal=num_threads+1,
        },
        .bar_finish={
            .lock=PTHREAD_MUTEX_INITIALIZER,
            .cond=PTHREAD_COND_INITIALIZER,
            .goal=num_threads+1,
        },
    };
    for (int i=0; i<num_threads; ++i) {
        tmeta[i].args = &args;
        tmeta[i].pthread_id = 0;
        tmeta[i].thread_id = i;
        tmeta[i].num_threads = num_threads;
        tmeta[i].bar_start=&tpool.bar_start;
        tmeta[i].bar_finish=&tpool.bar_finish;
    }
    fkm_threads_init(&tpool);
    gsl_rng* rng = gsl_rng_alloc(gsl_rng_mt19937);
    gsl_rng_set(rng, time(NULL));

    // Randomize which center each point belongs to
    for (size_t p=0; p < args.num_points; ++p) {
        // TODO: Make sure every cluster gets assigned
        owner_of[p] = gsl_rng_uniform_int(rng, args.k);
        /*FKM_DEBUGFMT("Made %u owner of %lu", owner_of[p], p);*/
    }

    // Initiate clusters to random point in input
    for (size_t m=0; m<args.k; m++) {
        size_t rand_p = gsl_rng_uniform_int(rng, args.num_points);
        for (size_t j=0; j<args.dim; ++j) {
            double val = gsl_matrix_get(points,
                                        rand_p,
                                        j);
            gsl_matrix_set(clusters, m, j, val);
        }
    }

    for (size_t it=0; it<max_iter; ++it) {
        FKM_DEBUGFMT("iter %lu num_points %lu dim %lu, k %lu num_threads %i",
                it, args.num_points, args.dim, args.k, num_threads);
        /*for (size_t p=0; p<num_points;++p) {*/
            /*printf("%lu:%u ", p, owner_of[p]);*/
        /*}*/
        /*printf("\n");*/
        fkm_clusters_assignment(&tpool);
        double diff = fkm_clusters_update(&args);
        FKM_DEBUGFMT("Diff: %f", diff);
        if (fabs(diff) < 1e-10) {
            FKM_DEBUG("Break since no progress");
            break;
        }
    }
    barrier_finish(&tpool.bar_start);
    int err = fkm_threads_join(&tpool);
    return 0;
}

int barrier_wait(tbarrier* bar) {
    pthread_mutex_lock(&bar->lock);
    FKM_DEBUGFMT("barrier_wait: count:%i goal:%i status:%i", bar->count,
                                                      bar->goal,
                                                      bar->status);
    assert(bar->goal > 0);
    bar->count += 1;
    if (bar->goal == bar->count) {
        bar->status = RELEASED;
        bar->count = 0;
        FKM_DEBUG("Goal reached in barrier, broadcasting release");
        pthread_cond_broadcast(&bar->cond);
        pthread_mutex_unlock(&bar->lock);
        return 0;
    }
    pthread_mutex_unlock(&bar->lock);
    for (;;) {
        pthread_cond_wait(&bar->cond, &bar->lock);
        if (bar->status == RELEASED) {
            FKM_DEBUG("Barrier released");
            pthread_mutex_unlock(&bar->lock);
            return 0;
        } else if (bar->status == EXIT) {
            FKM_DEBUG("Barrier signals FINISHED");
            pthread_mutex_unlock(&bar->lock);
            return 1;
        } else {
            // Spurious wakeup
            FKM_DEBUG("Spurious wakeup");
        }
    }
}

int barrier_finish(tbarrier* bar) {
    int err = pthread_mutex_lock(&bar->lock);
    if (err) {
        FKM_ERROR("Could not acquire mutex");
        return 1;
    }
    bar->status = EXIT;
    err = pthread_mutex_unlock(&bar->lock);
    if (err) {
        FKM_ERROR("Could not release mutex");
        return 1;
    }
    err = pthread_cond_broadcast(&bar->cond);
    if (err) {
        FKM_ERROR("Could not broadcast cond-variable");
        return 1;
    }
    return 0;
}
int fkm_clusters_assignment_single(targs* args,
                                     size_t offset,
                                     size_t stride);

void* fkm_thread_manage(void* data) {
    thread_meta* tmeta = data;
    targs* args = tmeta->args;

    int stop=0;
    for (;;) {
        stop = barrier_wait(tmeta->bar_start);
        if (stop) {
            return 0;
        }
        fkm_clusters_assignment_single(args,
                                       tmeta->thread_id,
                                       tmeta->num_threads);
        stop = barrier_wait(tmeta->bar_finish);
        if (stop) {
            return 0;
        }
    }
}

int fkm_clusters_assignment_single(targs* args,
                                     size_t offset,
                                     size_t stride) {
    for (size_t p=offset; p<args->num_points; p+=stride) {
        double min_err = 1e308;
        for (size_t m=0; m<args->k; ++m) {
            double err = 0;
            for (size_t j=0; j<args->dim; ++j) {
                double a = fabs(gsl_matrix_get(args->clusters, m, j)
                                - gsl_matrix_get(args->points, p, j));
                err += a*a;
            }
            if (err < min_err) {
                min_err = err;
                args->owner_of[p] = m;
            }
        }
    }
    return 0;
}

int fkm_threads_init(thread_pool* tpool) {

    for (size_t thr=0; thr<tpool->num_threads;++thr) {
        thread_meta* tdata = &tpool->threads[thr];
        int err = pthread_create(&tdata->pthread_id,
                                 NULL,
                                 &fkm_thread_manage,
                                 tdata);
        if (err) {
            FKM_ERRORFMT("pthread_create-error %i", err);
            return 1;
        }
    }
    return 0;
}
int fkm_threads_join(thread_pool* tpool) {
    for (size_t thr=0; thr<tpool->num_threads;++thr) {
        thread_meta* tdata = &tpool->threads[thr];
        int err = pthread_join(tdata->pthread_id, NULL);
        if (err) {
            FKM_ERRORFMT("pthread_join-error %i", err);
        }
        return 1;
    }
    return 0;
}

int fkm_clusters_assignment(const thread_pool* tpool) {
    tbarrier* bar_start = tpool->threads[0].bar_start;
    FKM_DEBUGFMT("bar_start: count:%i goal:%i status:%i", bar_start->count,
                                                      bar_start->goal,
                                                      bar_start->status);
    int err = barrier_wait(bar_start);
    if (err) {
        FKM_ERROR("barrier_wait failed");
        return 1;
    }
    tbarrier* bar_finish = tpool->threads[0].bar_finish;
    FKM_DEBUGFMT("bar_finish: count:%i goal:%i status:%i", bar_finish->count,
                                                      bar_finish->goal,
                                                      bar_finish->status);
    err = barrier_wait(tpool->threads[0].bar_finish);
    if (err) {
        FKM_ERROR("barrier_wait failed");
        return 1;
    }
    return 0;
}

/**
 * Update clusters.
 *
 *
 */
double fkm_clusters_update(const targs* args) {
    /*printf("DEBUG: num_points %lu dim %lu, k %lu\n", num_points, dim, k);*/
    unsigned cluster_size[args->k];
    for (size_t m=0; m<args->k; ++m) {
        cluster_size[m] = 0;
    }
    gsl_matrix* mean_sums = gsl_matrix_calloc(args->k, args->dim);
    for (size_t p=0; p<args->num_points; ++p) {
        unsigned m = args->owner_of[p];
        assert(m < args->k);
        cluster_size[m] += 1;
        /*LOGFMT("p:%lu belongs to %u. Children %u", p, m, cluster_size[m]);*/
        for (size_t j=0; j<args->dim; ++j) {
            double val = gsl_matrix_get(mean_sums, m, j)
                + gsl_matrix_get(args->points, p, j);
            gsl_matrix_set(mean_sums, m, j, val);
        }
    }
    double diff = 0;
    for (size_t m=0; m<args->k; ++m) {
        /*printf("Updating cluster %lu to", m);*/
        for (size_t j=0; j<args->dim; ++j) {
            double val = gsl_matrix_get(mean_sums, m, j);
            val /= cluster_size[m];
            diff += val - gsl_matrix_get(args->clusters, m, j);
            gsl_matrix_set(args->clusters, m, j, val);
            /*printf(" %u %f", cluster_size[m], val);*/
        }
        /*printf("\n");*/
    }
    gsl_matrix_free(mean_sums);
    return diff;
}

typedef double (*elem_func)(double, double);

double fkm_matrix_elem_func(gsl_matrix* a, gsl_matrix* b, elem_func f) {
    double v = 0;
    for (size_t i=0; i<a->block->size; ++i) {
        v += f(a->block->data[i], b->block->data[i]);
    }
    return v;
}

double diff(double a, double b) {
    return fabs(a - b);
}

double fkm_matrix_diff(gsl_matrix* a, gsl_matrix* b) {
    return fkm_matrix_elem_func(a, b, diff);
}

void print_matrix(gsl_matrix* a) {
    for (size_t i=0; i<5 && i<a->block->size; ++i) {
        printf("%lu: %f\n", i, a->block->data[i]);
    }
}

gsl_matrix* fkm_matrix_load(FILE* fin) {
    size_t rows, cols;
    int err = fscanf(fin, "%lu %lu", &rows, &cols);
    if (err == EOF) {
        printf("ERROR: Could not parse matrix dimensions\n");
        return 0;
    }
    gsl_matrix* mat = gsl_matrix_alloc(rows, cols);
    err = gsl_matrix_fscanf(fin, mat);
    if (err) {
        printf("ERROR: Could not parse matrix data\n");
        return 0;
    }
    printf("Loaded %lu rows, %lu columns matrix\n", rows, cols);
    return mat;
}

int fkm_matrix_save(FILE* fout, gsl_matrix* mat) {
    int err = 0;
    fprintf(fout, "%zu %zu\n", mat->size1, mat->size2);
    for (size_t i=0; i<mat->block->size; ++i) {
        if (i && i % mat->size2 == 0) {
            err = fprintf(fout, "\n");
            if (err<0) {
                FKM_ERRORFMT("fprintf returned %i", err);
            }
        }
        int err = fprintf(fout, "%f ", mat->block->data[i]);
        if (err<0) {
            FKM_ERRORFMT("fprintf returned %i", err);
        }
    }
    return err;
}


//
// Author: Valerio Bertone: valerio.bertone@cern.ch
//

#pragma once

#include <string>
#include <vector>
#include <utility>

#include <apfel/apfelxx.h>
#include <yaml-cpp/yaml.h>
#include <gsl/gsl_rng.h>

namespace NangaParbat
{
  /**
   * @brief The "DataHandler" class provides a common interface to all
   * datasets. It provides methods to get kinematics, central values,
   * uncertainties, etc.
   */
  class DataHandler
  {
  public:

    /**
     * @brief The process enumerator
     */
    enum Process: int {UnknownProcess = -1, DY = 0, SIDIS = 1, SIA = 2, DIA = 3};

    /**
     * @brief The observable enumerator
     */
    enum Observable: int {UnknownObservable = -1, dsigma_dxdydz = 0, dsigma_dxdQdz = 1, multiplicity = 2};

    /**
     * @brief Structure containing the kinematic information of one
     * single data set.
     */
    struct Kinematics
    {
      Kinematics();
      bool empty() const;
      int                                    ndata;    //!< Number of data points
      double                                 Vs;       //!< Center of mass energy
      std::vector<double>                    qTv;      //!< Vector of qT values
      std::vector<std::pair<double, double>> qTmap;    //!< Map of qT bounds to associate to the single bins
      std::vector<double>                    qTfact;   //!< Possible bin-by-bin prefactors to multiply the theoretical predictions
      std::pair<double, double>              var1b;    //!< Variable 1 integration bounds
      std::pair<double, double>              var2b;    //!< Variable 2 integration bounds
      std::pair<double, double>              var3b;    //!< Variable 3 integration bounds
      bool                                   IntqT;    //!< Whether the bins in qTv are integrated over
      bool                                   Intv1;    //!< Whether the bins variable 1 are integrated over
      bool                                   Intv2;    //!< Whether the bins variable 2 are integrated over
      bool                                   Intv3;    //!< Whether the bins variable 3 are integrated over
      bool                                   PSRed;    //!< Whether there is a final-state PS reduction
      double                                 pTMin;    //!< Minimum pT of the final-state leptons
      std::pair<double, double>              etaRange; //!< Allowed range in eta of the final-state leptons
    };

    /**
     * @brief Structure containing the single bin information. This is
     * currently used only for the FF_SIDIS project.
     * @todo Integrate it better in the rest of the class.
     */
    struct Binning
    {
      Binning();
      double zmin;
      double zmax;
      double zav;
      bool Intz;
      double xmin;
      double xmax;
      double xav;
      bool Intx;
      double Qmin;
      double Qmax;
      double Qav;
      bool IntQ;
      double ymin;
      double ymax;
      double yav;
      bool Inty;
    };

    /**
     * @brief The "DataHandler" copy constructor.
     */
    DataHandler(DataHandler const &DH);

    /**
     * @brief The "DataHandler" constructor.
     * @param name: the name associated to the data set
     * @param datafile: the YAML:Node with the interpolation table
     * @param rng: GSL random number object
     * @param fluctuation: ID of the fluctuation (i.e. Monte-Carlo replica ID) (default: 0, i.e. no fluctuations)
     * @param t0: vector of predictions to be used for the t0-prescription
     */
    DataHandler(std::string const& name, YAML::Node const& datafile, gsl_rng* rng = nullptr, int const& fluctuation = 0, std::vector<double> const& t0 = {});

    /**
     * @brief Function that fluctuates data
     * @param rng: GSL random number object
     * @param fluctuation: ID of the fluctuation (i.e. Monte-Carlo replica ID) (default: 0, i.e. no fluctuations)
     */
    void FluctuateData(gsl_rng *rng, int const &fluctuation);

    /**
     * @brief Function that sets the data central values replacing that
     * introduced in the constructor.
     * @param means: the new means
     */
    void SetMeans(std::vector<double> const& means, gsl_rng* rng = nullptr, int const& fluctuation = 0);

    /**
     * @brief Function that returns the name of the dataset
     */
    std::string GetName() const { return _name; };

    /**
     * @brief Function that returns the datafile in YAML format
     */
    YAML::Node GetDataFile() const { return _datafile; };

    /**
     * @brief Function that returns the process code
     */
    Process GetProcess() const { return _proc; };

    /**
     * @brief Function that returns the observable code
     */
    Observable GetObservable() const { return _obs; };

    /**
     * @brief Function that returns the target isoscalarity
     * @note The code always assumes that one of the hadrons has
     * isoscalarity 1, meaning that it's a single hadron whose
     * distributions don't need to be manipulated.
     */
    double GetTargetIsoscalarity() const { return _targetiso; };

    /**
     * @brief Function that returns the possible identified hadron
     * species in the final state.
     */
    std::string GetHadron() const { return _hadron; };

    /**
     * @brief Function that returns the charge of the identified final
     * state.
     */
    int GetCharge() const { return _charge; };

    /**
     * @brief Function that returns the quark-tagged compoments. Zero
     * corresponds to total.
     */
    std::vector<apfel::QuarkFlavour> GetTagging() const { return _tagging; };

    /**
     * @brief Function that returns any possible constant prefactor to
     * be used to multiply the theoretical predictions.
     */
    double GetPrefactor() const { return _prefact; };

    /**
     * @brief Function that returns the kinematic object
     */
    Kinematics GetKinematics() const { return _kin; };

    /**
     * @brief Function that returns the mean values
     */
    std::vector<double> GetMeanValues() const { return _means; };

    /**
     * @brief Function that returns the fluctuated data
     */
    std::vector<double> GetFluctutatedData() const { return _fluctuations; };

    /**
     * @brief Function that returns the sum in quadrature of the
     * uncorrelated uncertainties.
     */
    std::vector<double> GetUncorrelatedUnc() const { return _uncor; };

    /**
     * @brief Function that returns the additive correlated systematic
     * uncertainties.
     */
    std::vector<std::vector<double>> GetAddCorrelatedUnc() const { return _corra; };

    /**
     * @brief Function that returns the multiplicative correlated
     * systematic uncertainties.
     */
    std::vector<std::vector<double>> GetMultCorrelatedUnc() const { return _corrm; };

    /**
     * @brief Function that returns the all the correlated systematic
     * uncertainties (additive first and multiplicative second).
     */
    std::vector<std::vector<double>> GetCorrelatedUnc() const { return _corr; };

    /**
     * @brief Function that returns the covariance matrix of the
     * correlated uncertainties.
     */
    apfel::matrix<double> GetCovarianceMatrix() const { return _covmat; };

    /**
     * @brief Function that returns the Cholesky decomposition of the
     * covariance matrix.
     */
    apfel::matrix<double> GetCholeskyDecomposition() const { return _CholL; };

    /**
     * @brief Function that returns the set of t0 predictions
     */
    std::vector<double> GetT0() const { return _t0; };

    /**
     * @brief Function that returns the plotting labels.
     */
    std::map<std::string, std::string> GetLabels() const { return _labels; };

    /**
     * @brief Get vector of bins. This is currently used only for the
     * FF_SIDIS project.
     * @todo Integrate it better in the rest of the
     * class.
     */
    std::vector<Binning> GetBinning() const { return _bins; }

  protected:

    std::string                        _name;         //!< Name of the dataset
    YAML::Node                         _datafile;     //!< Datafile in YAML
    Process                            _proc;         //!< The process
    Observable                         _obs;          //!< The observable
    double                             _targetiso;    //!< Isoscalarity of the target
    std::string                        _hadron;       //!< Hadron species identified in the final state
    double                             _charge;       //!< Charge of the identified final state
    std::vector<apfel::QuarkFlavour>   _tagging;      //!< Possible quark-tagged components
    double                             _prefact;      //!< Possible overall prefactor to multiply the theoretical predictions
    Kinematics                         _kin;          //!< Kinematics block
    std::vector<double>                _means;        //!< Vector of central values
    std::vector<double>                _uncor;        //!< Vector of uncorrelated uncertainties
    std::vector<std::vector<double>>   _corra;        //!< Additive correlated uncertainties
    std::vector<std::vector<double>>   _corrm;        //!< Multiplicative correlated uncertainties
    std::vector<std::vector<double>>   _corr;         //!< All correlated uncertainties
    apfel::matrix<double>              _covmat;       //!< Covariance matrix
    apfel::matrix<double>              _CholL;        //!< Cholesky decomposition of the covariance matrix
    std::map<std::string, std::string> _labels;       //!< Labels used for plotting
    std::vector<double>                _fluctuations; //!< Vector of fluctuated data
    std::vector<double>                _t0;           //!< Vector of t0-predictions
    std::vector<Binning>               _bins;         //!< Vector of bins (currently used only for the FF_SIDIS project)

    friend std::ostream& operator << (std::ostream& os, DataHandler const& DH);
  };

  std::ostream& operator << (std::ostream &os, DataHandler const& DH);
}