/*
 -----------------------------------------------------------------
 ** Top contributors:
 **   Shiqi Wang and Suman Jana
 ** This file is part of the ReluVal project.
 ** Copyright (c) 2018-2019 by the authors listed in the file LICENSE
 ** and their institutional affiliations.
 ** All rights reserved.
 -----------------------------------------------------------------
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <cblas.h>

#ifndef MATRIX_H
#define MATRIX_H

/* Define the structure of Matrix */
struct Matrix
{
    float* data;
    int row, col;
};

/* add the constant to matrix */
void add_constant(struct Matrix* A, float alpha);

/*matrix multiplication with factors */
void matmul_with_factor(struct Matrix* A,\
                        struct Matrix* B,\
                        struct Matrix* C,\
                        float alpha,\
                        float beta);

/* matrix multiplication */
void matmul(struct Matrix* A,\
            struct Matrix* B,\
            struct Matrix* C);

/* matrix multiplication with bias */
void matmul_with_bias(struct Matrix* A,\
                      struct Matrix* B,\
                      struct Matrix* C);

/* element-wise multiplication */
void multiply(struct Matrix* A, struct Matrix* B);

/* print matrix */
void printMatrix(struct Matrix* A);

/* print matrix to the file */
void fprintMatrix(FILE *fp, struct Matrix* A);

/* takes the relu of the matrix */
void relu(struct Matrix* A);

#endif


static char help[] =
"ODE system solver example using TS, but with Jacobian.  Sets TS type to\n"
"implicit Crank-Nicolson.  Compare ode.c.\n\n";

#include <petsc.h>

extern PetscErrorCode ExactSolution(PetscReal, Vec);
extern PetscErrorCode FormRHSFunction(TS, PetscReal, Vec, Vec, void*);
extern PetscErrorCode FormRHSJacobian(TS, PetscReal, Vec, Mat, Mat, void*);

int main(int argc,char **argv) {
  PetscErrorCode ierr;
  PetscInt   steps;
  PetscReal  t0 = 0.0, tf = 20.0, dt = 0.1, err;
  Vec        y, yexact;
  Mat        J;
  TS         ts;

  ierr = PetscInitialize(&argc,&argv,NULL,help); if (ierr) return ierr;

  ierr = VecCreate(PETSC_COMM_WORLD,&y); CHKERRQ(ierr);
  ierr = VecSetSizes(y,PETSC_DECIDE,2); CHKERRQ(ierr);
  ierr = VecSetFromOptions(y); CHKERRQ(ierr);
  ierr = VecDuplicate(y,&yexact); CHKERRQ(ierr);

  ierr = TSCreate(PETSC_COMM_WORLD,&ts); CHKERRQ(ierr);
  ierr = TSSetProblemType(ts,TS_NONLINEAR); CHKERRQ(ierr);
  ierr = TSSetRHSFunction(ts,NULL,FormRHSFunction,NULL); CHKERRQ(ierr);

//STARTMATJ
  ierr = MatCreate(PETSC_COMM_WORLD,&J); CHKERRQ(ierr);
  ierr = MatSetSizes(J,PETSC_DECIDE,PETSC_DECIDE,2,2); CHKERRQ(ierr);
  ierr = MatSetFromOptions(J); CHKERRQ(ierr);
  ierr = MatSetUp(J); CHKERRQ(ierr);
  ierr = TSSetRHSJacobian(ts,J,J,FormRHSJacobian,NULL); CHKERRQ(ierr);
  ierr = TSSetType(ts,TSCN); CHKERRQ(ierr);
//ENDMATJ

  // set time axis
  ierr = TSSetTime(ts,t0); CHKERRQ(ierr);
  ierr = TSSetMaxTime(ts,tf); CHKERRQ(ierr);
  ierr = TSSetTimeStep(ts,dt); CHKERRQ(ierr);
  ierr = TSSetExactFinalTime(ts,TS_EXACTFINALTIME_MATCHSTEP); CHKERRQ(ierr);
  ierr = TSSetFromOptions(ts); CHKERRQ(ierr);

  // set initial values and solve
  ierr = TSGetTime(ts,&t0); CHKERRQ(ierr);
  ierr = ExactSolution(t0,y); CHKERRQ(ierr);
  ierr = TSSolve(ts,y); CHKERRQ(ierr);

  // compute error and report
  ierr = TSGetStepNumber(ts,&steps); CHKERRQ(ierr);
  ierr = TSGetTime(ts,&tf); CHKERRQ(ierr);
  ierr = ExactSolution(tf,yexact); CHKERRQ(ierr);
  ierr = VecAXPY(y,-1.0,yexact); CHKERRQ(ierr);    // y <- y - yexact
  ierr = VecNorm(y,NORM_INFINITY,&err); CHKERRQ(ierr);
  ierr = PetscPrintf(PETSC_COMM_WORLD,
              "error at tf = %.3f with %d steps:  |y-y_exact|_inf = %g\n",
              tf,steps,err); CHKERRQ(ierr);

  MatDestroy(&J);
  VecDestroy(&y);  VecDestroy(&yexact);  TSDestroy(&ts);
  return PetscFinalize();
}

PetscErrorCode ExactSolution(PetscReal t, Vec y) {
    PetscReal *ay;
    VecGetArray(y,&ay);
    ay[0] = t - PetscSinReal(t);
    ay[1] = 1.0 - PetscCosReal(t);
    VecRestoreArray(y,&ay);
    return 0;
}

PetscErrorCode FormRHSFunction(TS ts, PetscReal t, Vec y, Vec g,
                               void *ptr) {
    const PetscReal *ay;
    PetscReal       *ag;
    VecGetArrayRead(y,&ay);
    VecGetArray(g,&ag);
    ag[0] = ay[1];            // = g_1(t,y)
    ag[1] = - ay[0] + t;      // = g_2(t,y)
    VecRestoreArrayRead(y,&ay);
    VecRestoreArray(g,&ag);
    return 0;
}

//STARTJACOBIAN
PetscErrorCode FormRHSJacobian(TS ts, PetscReal t, Vec y, Mat J, Mat P,
                               void *ptr) {
    PetscErrorCode ierr;
    PetscInt   row[2] = {0, 1},  col[2] = {0, 1};
    PetscReal  v[4] = { 0.0, 1.0,
                       -1.0, 0.0};
    ierr = MatSetValues(P,2,row,2,col,v,INSERT_VALUES); CHKERRQ(ierr);
    ierr = MatAssemblyBegin(P,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
    ierr = MatAssemblyEnd(P,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
    if (J != P) {
        ierr = MatAssemblyBegin(J,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
        ierr = MatAssemblyEnd(J,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
    }
    return 0;
}
//ENDJACOBIAN


#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

/*
Adapted from C code from Darren Wilkinson's blog
https://darrenjw.wordpress.com/2011/07/16/gibbs-sampler-in-various-languages-revisited/
To build:
gcc -O3 gibbs_gsl.c -I/usr/local/include -L/usr/local/lib -lgsl -lgslcblas -lm -o gibbs_gsl
To run:
time ./gibbs_gsl
*/

/* Arrays xa and ya are passed by reference */
void gibbs(int N, int thin, double **xa, double **ya)
{
  int i,j;
  gsl_rng *r = gsl_rng_alloc(gsl_rng_mt19937);
  double x=0;
  double y=0;
  for (i=0;i<N;i++) {
    for (j=0;j<thin;j++) {
      x=gsl_ran_gamma(r,3.0,1.0/(y*y+4));
      y=1.0/(x+1)+gsl_ran_gaussian(r,1.0/sqrt(2*x+2));
    }
    (*xa)[i]=x; /* Note how the arrays are dereferenced */
    (*ya)[i]=y;
  }
  free(r);
}

int main()
{
  int N=50000;
  int thin=1000;
  double *xa,*ya;
  xa = (double *)malloc(sizeof(double)*N);
  ya = (double *)malloc(sizeof(double)*N);
  gibbs(N,thin,&xa,&ya);
  return(0);
}


#include <stdio.h>      //printf
#include <string.h>     //memset
#include <time.h>       //time
#include <cblas.h>      //cblas_scopy, cblas_sscal, cblas_sgemm
#include <sys/times.h>  //times
#include <unistd.h>     //sysconf
#include <math.h>       //fabsf
#include <xmmintrin.h>  //_mm_malloc
#define  ALIGN 16

float* gen_matrix(int order, float range){
    float* m = _mm_malloc(order * order * sizeof(float), ALIGN);
    srand(time(NULL));
    for(int i = 0; i < order * order; ++i)
        m[i] = ((float)rand() / (float)(RAND_MAX)) * range;
    return m;
}

void transpose_matrix(float* m, int order){
    for(int i = 0; i < order; ++i)
        for(int j = i; j < order; ++j){
            float tmp = m[i * order + j];
            m[i * order + j] = m[j * order + i];
            m[j * order + i] = tmp;
        }
}

void print_matrix(float* m, int order, FILE* out){
    fprintf(out, "Order: %d\n", order);
    for(int i = 0; i < order; ++i){
        for(int j = 0; j < order; ++j)
            fprintf(out, "%.4f ", m[i * order + j]);
        fprintf(out, "\n");
    }
    fprintf(out, "\n");
}

void get_matrix_norms(float* m, int order, float* l1_norm, float* max_norm){
    *l1_norm = 0;
    *max_norm = 0;
    for(int i = 0; i < order; ++i){
        float row_sum = 0;
        float col_sum = 0;
        for(int j = 0; j < order; ++j){
            row_sum += fabs(m[i * order + j]);
            col_sum += fabs(m[j * order + i]);
        }
        if(*l1_norm < col_sum)
            *l1_norm = col_sum;
        if(*max_norm < row_sum)
            *max_norm = row_sum;
    }
}

float* get_identity_matrix(int order){
    float* m = _mm_malloc(order * order * sizeof(float), ALIGN);
    memset(m, 0, order * order * sizeof(float));
    for(int i = 0; i < order; ++i)
        m[i * order + i] = 1;
    return m;
}

float* invert_matrix(float* A, int order, int iter_number){
    float* B = _mm_malloc(order * order * sizeof(float), ALIGN);
    cblas_scopy(order * order, A, 1, B, 1);
    transpose_matrix(B, order);
    float l1_norm, max_norm;
    get_matrix_norms(A, order, &l1_norm, &max_norm);
    cblas_sscal(order * order, 1.0 / (l1_norm * max_norm), B, 1);
    float* R = get_identity_matrix(order);
    cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, order, order, order, -1, B, order, A, order, 1, R, order);
    float* R_1_deg = _mm_malloc(order * order * sizeof(float), ALIGN);
    cblas_scopy(order * order, R, 1, R_1_deg, 1);
    float* tmp = NULL;
    float* inv_A = get_identity_matrix(order);
    for(int i = 0; i < iter_number; ++i){
        tmp = _mm_malloc(order * order * sizeof(float), ALIGN);
        cblas_saxpy(order * order, 1, R, 1, inv_A, 1);
        cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, order, order, order, 1, R, order, R_1_deg, order, 0, tmp,
                    order);
        _mm_free(R);
        R = tmp;
    }
    cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, order, order, order, 1, inv_A, order, B, order, 0.0, tmp,
            order);
    _mm_free(inv_A);
    inv_A = tmp;
    _mm_free(B);
    _mm_free(R_1_deg);
    return inv_A;
}

int main(){
    FILE* input = fopen("input.txt", "r");
    FILE* output = fopen("output.txt", "w");
    int N = 0, M = 0;
    fscanf(input, "%d%d", &N, &M);
    float* A = gen_matrix(N, 5.0);
    time_t start_real = time(NULL);
    float* inv_A = invert_matrix(A, N, M);
    time_t finish_real = time(NULL);
    float* rez = _mm_malloc(N * N * sizeof(float), ALIGN);
    cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, N, N, N, 1, inv_A, N, A, N, 0, rez, N);
    print_matrix(rez, N, output);
    printf("Total real time: %ld sec.\n", finish_real - start_real);
    _mm_free(A);
    _mm_free(inv_A);
    _mm_free(rez);
    fclose(input);
    fclose(output);
    return 0;
}

The 2nd course/Computers and devices/laboratory work #7/with_cblas/cblas.c


/* specfunc/gsl_sf_coupling.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
 *
 * 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* Author:  G. Jungman */

#ifndef __GSL_SF_COUPLING_H__
#define __GSL_SF_COUPLING_H__

#include <gsl/gsl_sf_result.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

/* 3j Symbols:  / ja jb jc \
 *              \ ma mb mc /
 *
 * exceptions: GSL_EDOM, GSL_EOVRFLW
 */
int gsl_sf_coupling_3j_e(int two_ja, int two_jb, int two_jc,
                            int two_ma, int two_mb, int two_mc,
			    gsl_sf_result * result
			    );
double gsl_sf_coupling_3j(int two_ja, int two_jb, int two_jc,
                          int two_ma, int two_mb, int two_mc
                          );

/* 6j Symbols:  / ja jb jc \
 *              \ jd je jf /
 *
 * exceptions: GSL_EDOM, GSL_EOVRFLW
 */
int gsl_sf_coupling_6j_e(int two_ja, int two_jb, int two_jc,
                            int two_jd, int two_je, int two_jf,
			    gsl_sf_result * result
			    );
double gsl_sf_coupling_6j(int two_ja, int two_jb, int two_jc,
                          int two_jd, int two_je, int two_jf
                          );

/* 9j Symbols:  / ja jb jc \
 *              | jd je jf |
 *              \ jg jh ji /
 *
 * exceptions: GSL_EDOM, GSL_EOVRFLW
 */
int gsl_sf_coupling_9j_e(int two_ja, int two_jb, int two_jc,
                            int two_jd, int two_je, int two_jf,
			    int two_jg, int two_jh, int two_ji,
			    gsl_sf_result * result
			    );
double gsl_sf_coupling_9j(int two_ja, int two_jb, int two_jc,
                          int two_jd, int two_je, int two_jf,
                          int two_jg, int two_jh, int two_ji
                          );

__END_DECLS

#endif /* __GSL_SF_COUPLING_H__ */


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

double m[] = {
  3, 1, 3,
  1, 5, 9,
  2, 6, 5
};

double x[] = {
  -1, 3, -3
};

#ifdef __cplusplus
extern "C" {
#endif

     void dgesv_(int *n, int *nrhs,  double *a,  int  *lda,
           int *ipivot, double *b, int *ldb, int *info);

#ifdef __cplusplus
}
#endif

int main(void) {
  int i;
  // blas:
  double A[6] = {1.0, 2.0, 1.0, -3.0, 4.0, -1.0};
  double B[6] = {1.0, 2.0, 1.0, -3.0, 4.0, -1.0};
  double C[9] = {.5, .5, .5, .5, .5, .5, .5, .5, .5};
  cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans,
              3, 3, 2, 1, A, 3, B, 3, 2, C, 3);
  for (i = 0; i < 9; i++)
    printf("%f\n", C[i]);

  // lapack:
  int ipiv[3];
  int j;
  int info;
  int n = 1;
  int nrhs = 1;
  int lda = 3;
  int ldb = 3;
  dgesv_(&n,&nrhs, &m[0], &lda, ipiv, &x[0], &ldb, &info);
  for (i=0; i<3; ++i)
    printf("%5.1f\n", x[i]);

  return 0;
}


/* roots/newton.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Reid Priedhorsky, 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* newton.c -- newton root finding algorithm

   This is the classical Newton-Raphson iteration.

   x[i+1] = x[i] - f(x[i])/f'(x[i])

*/

#include <config.h>

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <float.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>

#include "roots.h"

typedef struct
  {
    double f, df;
  }
newton_state_t;

static int newton_init (void * vstate, gsl_function_fdf * fdf, double * root);
static int newton_iterate (void * vstate, gsl_function_fdf * fdf, double * root);

static int
newton_init (void * vstate, gsl_function_fdf * fdf, double * root)
{
  newton_state_t * state = (newton_state_t *) vstate;

  const double x = *root ;

  state->f = GSL_FN_FDF_EVAL_F (fdf, x);
  state->df = GSL_FN_FDF_EVAL_DF (fdf, x) ;

  return GSL_SUCCESS;

}

static int
newton_iterate (void * vstate, gsl_function_fdf * fdf, double * root)
{
  newton_state_t * state = (newton_state_t *) vstate;

  double root_new, f_new, df_new;

  if (state->df == 0.0)
    {
      GSL_ERROR("derivative is zero", GSL_EZERODIV);
    }

  root_new = *root - (state->f / state->df);

  *root = root_new ;

  GSL_FN_FDF_EVAL_F_DF(fdf, root_new, &f_new, &df_new);

  state->f = f_new ;
  state->df = df_new ;

  if (!finite(f_new))
    {
      GSL_ERROR ("function not continuous", GSL_EBADFUNC);
    }

  if (!finite (df_new))
    {
      GSL_ERROR ("function not differentiable", GSL_EBADFUNC);
    }

  return GSL_SUCCESS;
}

static const gsl_root_fdfsolver_type newton_type =
{"newton",				/* name */
 sizeof (newton_state_t),
 &newton_init,
 &newton_iterate};

const gsl_root_fdfsolver_type  * gsl_root_fdfsolver_newton = &newton_type;


/******************************************************************************
**                   Copyright (C) 2014 by CEA
*******************************************************************************
**
**    UNIT
**
**    Version: 1.0
**
**    Author: Fred Ngole
**
**    Date:  24/09/2014
**
**    File:  sr_util.h
**
*******************************************************************************
**
**    DESCRIPTION
**    -----------
**    Tools used in sprite.cc and useful beyond the scope of super-resolution
**
**
******************************************************************************/
#ifndef SR_UTIL_H
#define SR_UTIL_H
#include <stdio.h>
#include <stdlib.h>
#include "TempArray.h"
#include <cmath>
#include "IM_Obj.h"
#include "IM_IO.h"
#include "MR_Obj.h"
#include "MR_Sigma.h"
#include "CoaddCorrel.h"
#include "IM_Deconv.h"
#include "OptMedian.h"
//#include <random>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

const double Pi = 3.14159265358979323846;
const int LR_DENOISING = 0;
const int MR_SUPP_DENOISING=1;
const int ONE_STEP_ANALYSIS=1;
const int ACCURATE_ANALYSIS=0;
const int MODEL_SINC_INTERP=0;
const int MODEL_NON_LIN_INTERP=1;
const int MODEL_GAUSS_FIT=2;
const int FISTA = 1;
const int GEN_FWBW = 0;
//gsl_rng *rng;
//const gsl_rng_type * T;
//T =  gsl_rng_default;
//rng = gsl_rng_alloc (T);

typedef struct{
  type_transform transf;
  int nb_sc;
  int nb_usc;
  sb_type_norm Norm;
  type_undec_filter U_Filter;
  type_sb_filter SB_Filter;
  type_border Bord;
  FilterAnaSynt *FAS ;
}mr_opt;
int compute_centroid(Ifloat *img,double sig,double *cent,int niter=10);
int compute_centroid_arr(fltarray *data,double sig,double **cent,int niter=10);
double gauss2d(double amp,double x,double y,double theta,double sigx,double sigy);
void gauss2darray(double amp,double theta,double sigx,double sigy, double xcen,double ycen,Ifloat &im_out);
void gauss2D_fitting_first_guess(Ifloat im,double*sigx,double*sigy,double*xcen,double*ycen,double*amp,int smooth_ker);
void gauss2d_fit(Ifloat im,double *xcen,double *ycen,double* amp,double sig0,double *sig,int nb_iter_alter=20,int nb_subiter=10,double *mse=NULL,int max_test=10);
void gauss2d_fit_2(Ifloat im,double *xcen,double *ycen,double *amp,double *sigx,double *sigy,int nb_iter_alter=20,int nb_subiter=10,double *mse=NULL,int max_test=10);
double gauss2d_der1sig(Ifloat res,Ifloat gauss_approx,Ifloat distances_map,double sig);
double gauss2d_der1sigx(Ifloat res, Ifloat gauss_approx, double sigx, double xcen);
double gauss2d_der1sigy(Ifloat res, Ifloat gauss_approx, double sigy, double ycen);
double gauss2d_der2sig(Ifloat res,Ifloat gauss_approx,Ifloat distances_map,double sig);
double gauss2d_der2sigx(Ifloat res, Ifloat gauss_approx, double sigx, double xcen);
double gauss2d_der2sigy(Ifloat res, Ifloat gauss_approx, double sigy, double ycen);
double gauss2d_der1xcen(Ifloat res, Ifloat gauss_approx, double sig, double xcen);
double gauss2d_der1ycen(Ifloat res, Ifloat gauss_approx, double sig, double ycen);
double gauss2d_der2xcen(Ifloat res, Ifloat gauss_approx, double sig,double xcen);
double gauss2d_der2ycen(Ifloat res, Ifloat gauss_approx, double sig,double ycen);
double gauss2d_der2xcenycen(Ifloat res, Ifloat gauss_approx, double sig, double xcen, double ycen);
void dist_map(Ifloat &map,double xcen,double ycen);
void thresholding(Ifloat *data_in,Ifloat *data_out,double thresh,bool thresh_type=0);
void thresholding(Ifloat *data,Ifloat *data_out,Ifloat *thresh,bool thresh_type=0);
void thresholding(fltarray *data_in,fltarray *data_out,fltarray *thresh,bool thresh_type=0);
void circ_thresh(Ifloat *data_in,Ifloat *data_out,double r,double *cen);
void circ_thresh(fltarray *data_in,fltarray *data_out,double r,double **cen);
int sign_num (double a);
int wl_trans(Ifloat *Dat,mr_opt opt,MultiResol* MR_Data);
void wl_thresholding(MultiResol*wav_coeff,fltarray *thresh,bool thresh_type=0);
void wl_filter(Ifloat *img,Ifloat*img_filt,mr_opt opt,double nsig=4,bool thresh_type=0,fltarray* noise_map=NULL,fltarray *coeff_thresh=NULL,Ifloat *coarse_scale=NULL);
void wl_filter_analysis(Ifloat img,Ifloat &img_filt,mr_opt opt,double nsig,bool thresh_type,fltarray std_map,MultiResol &coeff_init,int nb_iter=100,double mu=0.5,double *mse=NULL);
int wl_gnoise_est(MultiResol* wav_coeff,fltarray* noise_arr);
mr_opt mr_opt_init(int Nx,type_transform transf = TO_UNDECIMATED_MALLAT,int nb_sc=-1,int nb_usc=-1,sb_type_norm Norm=NORM_L1,type_undec_filter U_Filter=DEF_UNDER_FILTER,type_sb_filter SB_Filter=F_MALLAT_7_9,type_border Bord=I_CONT,Bool Verbose =False);
int noise_est(fltarray* data,fltarray* noise_arr);
void wl_arr_filter(fltarray *data,fltarray*data_filt,mr_opt opt,double nsig=4,bool thresh_type=0);
void coadd_frames(fltarray Data, Ifloat &Ima,Bool GetMedian=False);
void coadd_frames(fltarray Dat, Ifloat &Ima,float Zoom,
		      Bool GetMedian=True,int MaxDist=4,
		      int Surface=10, type_interp_corr TypeInterp=ICF_TANH,Bool OptNoOffset=False,Bool MeanSub=False,Bool Verbose=False);
void flux_est(Ifloat *data, double r,double *cent,double*flux);
void flux_est(fltarray *data, double r,double **cent,double*flux,int ref_im_ind=0);
void shift_est(fltarray *data, double sig_gfit, double *sig_vec,double **cent,double **shift,int ref_im_ind=0,double nsig=4);
double sinc(double x);
void lanczos(double u,double*mask,int mask_rad=10);
void lanczos(double u[],double**mask,int mask_rad=10);
void lanczos_upsamp(Ifloat &im,int upfact,Ifloat &im_out, double *u=NULL,int kern_rad=5);
void lanczos_stacking(fltarray &data,Ifloat &im_upsamp,int upfact,double **u,int kern_rad=5,Bool mean_en=False);
void im_stacking(fltarray &data,Ifloat &im_stack,Bool mean_en=False);
void decim(Ifloat *img,Ifloat *img_filt,Ifloat *img_dec,int D,Ifloat* mask,Bool filt_en=True,Bool fft_en=True );
void rotate(Ifloat *input,Ifloat *output,int dir);
void transpose_decim(Ifloat*im_in,Ifloat*im_out,int D);
void power_meth(void(*opname)(double*,double*),int size_input,int *nb_iter,double *spec_rad,int nb_iter_max=100,double tol=0.01);
double norm2 (double * x,int siz);
void scale (double * x,double siz,double a);
double dot(Ifloat Im1,Ifloat Im2);
void hadamard(Ifloat Im1,Ifloat Im2,Ifloat &Im_out);
void ineq_cons(Ifloat &A, Ifloat B,double b=1);
void reverse(double*u,int length,double*ur);
void holes(double*h,int sc_nb,int length,double*hout);
void convol1D(double*in,double*out,double*h,int length,int filt_length);
void convol1D_cent(double*in,double*out,double*h,int length,int filt_length);
void correl2D(Ifloat im1,Ifloat im2, Ifloat &im_correl);
void sep_filt2d(Ifloat* im_in,Ifloat*im_out,double *h,double *g,int lh,int lg);
void transp_sep_filt2d(Ifloat* im_in,Ifloat*im_out,double *h,double *g,int lh,int lg);
void grad(Ifloat im,Ifloat &im_gradx,Ifloat &im_grady,double* hx,double* hy,int lhx,int lhy);
void grad_cons_noise_est(Ifloat im_in,Ifloat& im_out,Ifloat &res,double * sig,double gamma,int nb_iter=5,double * mse_iter=NULL,double mu=0.5, double *hx=NULL,double *hy=NULL,int lhx=-1,int lhy=-1);
void mad_res_noise_est(Ifloat im_in,Ifloat& im_out,Ifloat &res,double * sig,int nb_iter=5,double * mse_iter=NULL,double*sig_iter=NULL,double mu=1, int nsig=5,Bool sig_up=True,int thresh_type=0);
//void randomn(double *x,double sig=1,int length=1,double mean=0);
void randomngsl(double *x,double sig,int length,double mean=0);
//void randomn(Ifloat *mat,double sig=1,double mean=0);
void randomngsl(Ifloat *mat,double sig=1,double mean=0);
//void randomn(fltarray *mat,double *sig,double mean=0);
void randomngsl(fltarray *mat,double *sig,double mean=0);
//void randomn(fltarray *mat,double *sig,double*mean);
void randomngsl(fltarray *mat,double *sig,double*mean);
double var_iter(double x,int n,double *mean,double*M);
void check_ineq(Ifloat dat,double thresh,Bool abs_en,Ifloat &flag);
void mr_support_filt(Ifloat img,Ifloat &img_filt, mr_opt opt,double nsig=5,int nb_iter=1,double*mse=NULL,Bool Pos_coeff=True,double lambda=1,Bool coarse_cons=False,Bool pos_cons=True, Bool drop_coarse=True,Bool iso_cons=False, double sig=89.108911);

inline void mod_denoise_usage(void)
{
    fprintf(OUTMAN, "         [-m type_of_model_denoising]\n");

    fprintf(OUTMAN, "              %d: %s \n",0,
                                            "Wavelet denoising of LR images before coaddition");
    fprintf(OUTMAN, "              %d: %s \n",1,
                                            "Iterative multiresolution support denoising after coaddition");
    fprintf(OUTMAN, "             default is %s.\n",  "Iterative multiresolution support denoising after coaddition");
}

inline void mod_comp_usage(void)
{
    fprintf(OUTMAN, "         [-M model_computing_method]\n");

    fprintf(OUTMAN, "              %d: %s \n",0,
                                            "Sinc Interpolation");
    fprintf(OUTMAN, "              %d: %s \n",1,
                                            "Non linear interpolation");
    fprintf(OUTMAN, "              %d: %s \n",2,
                                            "Gaussian fitting after sinc interpolation (no denoising)");
    fprintf(OUTMAN, "             default is %s.\n",  "Sinc Interpolation");
}

inline void min_id_usage(void)
{
    fprintf(OUTMAN, "         [-A Minimisation algorithm]\n");

    fprintf(OUTMAN, "              %d: %s \n",0,
                                            "Generalized forward-backard (analysis+positivity)");
    fprintf(OUTMAN, "              %d: %s \n",1,
                                            "FISTA");
    fprintf(OUTMAN, "             default is %s.\n",  "Generalized forward-backard (analysis+positivity)");
}

inline void sparse_const_type_usage(void)
{
    fprintf(OUTMAN, "         [-D sparse_constraint_implementation (if FISTA is chosen for the minimisation)]\n");

    fprintf(OUTMAN, "              %d: %s \n",0,
                                            "Accurate analysis constraint with subiteration");
    fprintf(OUTMAN, "              %d: %s \n",1,
                                            "One step approximation of analysis constraint implementation");
    fprintf(OUTMAN, "             default is %s.\n",  "Accurate analysis constraint with subiteration");
}

inline void noise_est_usage(void)
{
    fprintf(OUTMAN, "         [-e Noise_est_meth]\n");

    fprintf(OUTMAN, "              %d: %s \n",0,
                                            "Estimation on backprojected residual using a MAD");
    fprintf(OUTMAN, "              %d: %s \n",1,
                                            "Noise simulation");
    //fprintf(OUTMAN, "              %d: %s \n",2,
    //                                      "Direct calculation of noise standard deviation per pixel (option under development)");

    fprintf(OUTMAN, "             default is %s.\n",  "Estimation on backprojected residual using a MAD");
}

inline void thresh_type_usage(void)
{
    fprintf(OUTMAN, "         [-c Thresh_type]\n");

    fprintf(OUTMAN, "              %d: %s \n",0,
                                            "Hard Thresholding");
    fprintf(OUTMAN, "              %d: %s \n",1,
                                            "Soft Thresholding");
    //fprintf(OUTMAN, "              %d: %s \n",2,
    //                                      "Direct calculation of noise standard deviation per pixel (option under development)");

    fprintf(OUTMAN, "             default is %s.\n",  "Soft Thresholding");
}

void decim_conv_mat(Ifloat conv_mat,int im_siz[],int decim_fact,Ifloat &output,double flux=1,double sig=1);
void convmask_shift_extend(Ifloat mask,int im_siz[],int shift[],Ifloat &output);
void noise_map_comp(double **shifts, double *sig, double *flux,int nb_im,int lancrad, int im_siz[],mr_opt opt, int decim_fact,fltarray &output);
int renewSeed();
void mr_norm(mr_opt opt,double*nrm,int nb_band,int Nx,int Ny=-1);
void wl_gnoise_est_dat(Ifloat data,mr_opt opt,fltarray* noise_arr);
void shell(unsigned long n,double *a);
double median(double *arr,int n);
#endif


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_sf_expint.h>
#include "ccl.h"

//maths helper function for NFW profile
static double helper_fx(double x){
    double f;
    f = log(1.+x)-x/(1.+x);
    return f;
}

//cosmo: ccl cosmology object containing cosmological parameters
//c: halo concentration, needs to be consistent with halo size definition
//halomass: halo mass
//massdef_delta: mass definition, overdensity relative to mean matter density
//a: scale factor
//r: radii at which to calculate output
//nr: number of radii for calculation
//rho_r: stores densities at r
//returns void
void ccl_halo_profile_nfw(ccl_cosmology *cosmo, double c, double halomass,
                          double massdef_delta_m, double a, double *r, int nr,
                          double *rho_r, int *status) {

    //haloradius: halo radius for mass definition
    //rs: scale radius
    double haloradius, rs;

    haloradius = r_delta(cosmo, halomass, a, massdef_delta_m, status);
    rs = haloradius/c;

    //rhos: NFW density parameter
    double rhos;

    rhos = halomass/(4.*M_PI*(rs*rs*rs)*helper_fx(c));

    int i;
    double x;
    for(i=0; i < nr; i++) {
        x = r[i]/rs;
        rho_r[i] = rhos/(x*(1.+x)*(1.+x));
    }
    return;
}

//cosmo: ccl cosmology object containing cosmological parameters
//c: halo concentration, needs to be consistent with halo size definition
//halomass: halo mass
//massdef_delta: mass definition, overdensity relative to mean matter density
//a: scale factor
//rp: radius at which to calculate output
//nr: number of radii for calculation
//sigma_r: stores surface mass density (integrated along line of sight) at given projected rp
//returns void
void ccl_projected_halo_profile_nfw(ccl_cosmology *cosmo, double c,
                                    double halomass, double massdef_delta_m,
                                    double a, double *rp, int nr, double *sigma_r,
                                    int *status) {

    //haloradius: halo radius for mass definition
    //rs: scale radius
    double haloradius, rs;

    haloradius = r_delta(cosmo, halomass, a, massdef_delta_m, status);
    rs = haloradius/c;

    //rhos: NFW density parameter
    double rhos;

    rhos = halomass/(4.*M_PI*(rs*rs*rs)*helper_fx(c));

    double x;

    int i;
    for(i=0; i < nr; i++){

        x = rp[i]/rs;
        if (x==1.){
            sigma_r[i] = 2.*rs*rhos/3.;
        }
        else if (x<1.){
            sigma_r[i] = 2.*rs*rhos*(1.-2.*atanh(sqrt(fabs((1.-x)/(1.+x))))/sqrt(fabs(1.-x*x)))/(x*x-1.);
        }
        else {
            sigma_r[i] = 2.*rs*rhos*(1.-2.*atan(sqrt(fabs((1.-x)/(1.+x))))/sqrt(fabs(1.-x*x)))/(x*x-1.);
        }
    }
    return;

}

//maths helper function assuming NFW approximation
static double helper_solve_cvir(double c, void *rhs_pointer){
    double rhs = *(double*)rhs_pointer;
    return (log(1.+c)-c/(1.+c))/(c*c*c) - rhs;
}

//solve for cvir from different mass definition iteratively, assuming NFW profile.
static double solve_cvir(ccl_cosmology *cosmo, double rhs, double initial_guess, int *status){

    int rootstatus;
    int iter = 0, max_iter = 100;
    const gsl_root_fsolver_type *T;
    gsl_root_fsolver *s;
    double cvir;
    double c_lo = 0.1, c_hi = initial_guess*100.;
    gsl_function F;

    F.function = &helper_solve_cvir;
    F.params = &rhs;

    T = gsl_root_fsolver_brent;
    s = gsl_root_fsolver_alloc (T);
    if (s != NULL) {
      gsl_root_fsolver_set (s, &F, c_lo, c_hi);

      do
        {
          iter++;
          gsl_root_fsolver_iterate (s);
          cvir = gsl_root_fsolver_root (s);
          c_lo = gsl_root_fsolver_x_lower (s);
          c_hi = gsl_root_fsolver_x_upper (s);
          rootstatus = gsl_root_test_interval (c_lo, c_hi, 0, 0.0001);
        }
      while (rootstatus == GSL_CONTINUE && iter < max_iter);

      gsl_root_fsolver_free(s);

      // Check for errors
      if (rootstatus != GSL_SUCCESS) {
          ccl_raise_gsl_warning(rootstatus, "ccl_haloprofile.c: solve_cvir():");
          *status = CCL_ERROR_PROFILE_ROOT;
          ccl_cosmology_set_status_message(cosmo, "ccl_haloprofile.c: solve_cvir(): Root finding failure\n");
          return NAN;
      } else {
          return cvir;
      }
    }
    else {
      *status = CCL_ERROR_MEMORY;
      return NAN;
    }
}

// Structure to hold parameters of integrand_einasto
typedef struct{
  ccl_cosmology *cosmo;
  double alpha, rs;
} Int_Einasto_Par;

static double integrand_einasto(double r, void *params){

    Int_Einasto_Par *p = (Int_Einasto_Par *)params;
    double alpha = p->alpha;
    double rs = p->rs;

    return 4.*M_PI*r*r*exp(-2.*(pow(r/rs,alpha)-1)/alpha);
}

//integrate einasto profile to get normalization of rhos
static double integrate_einasto(ccl_cosmology *cosmo, double R, double alpha, double rs, int *status){
    int qagstatus;
    double result = 0, eresult;
    Int_Einasto_Par ipar;
    gsl_function F;
    gsl_integration_workspace *w = NULL;

    w = gsl_integration_workspace_alloc(1000);

    if (w == NULL) {
      *status = CCL_ERROR_MEMORY;
    }

    if (*status == 0) {
      // Structure required for the gsl integration
      ipar.cosmo = cosmo;
      ipar.alpha = alpha;
      ipar.rs = rs;
      F.function = &integrand_einasto;
      F.params = &ipar;

      // Actually does the integration
      qagstatus = gsl_integration_qag(
        &F, 0, R, 0, 0.0001, 1000, 3, w,
        &result, &eresult);

      // Check for errors
      if (qagstatus != GSL_SUCCESS) {
        ccl_raise_gsl_warning(qagstatus, "ccl_haloprofile.c: integrate_einasto():");
        *status = CCL_ERROR_PROFILE_INT;
        ccl_cosmology_set_status_message(cosmo, "ccl_haloprofile.c: integrate_einasto(): Integration failure\n");
        result = NAN;
      }
    }

  // Clean up
  gsl_integration_workspace_free(w);

  return result;
}

//cosmo: ccl cosmology object containing cosmological parameters
//c: halo concentration, needs to be consistent with halo size definition
//halomass: halo mass
//massdef_delta: mass definition, overdensity relative to mean matter density
//a: scale factor
//r: radii at which to calculate output
//nr: number of radii for calculation
//rho_r: stores densities at r
//returns void
void ccl_halo_profile_einasto(ccl_cosmology *cosmo, double c, double halomass,
                              double massdef_delta_m, double a, double *r, int nr,
                              double *rho_r, int *status) {

    //haloradius: halo radius for mass definition
    //rs: scale radius
    double haloradius, rs;

    haloradius = r_delta(cosmo, halomass, a, massdef_delta_m, status);
    rs = haloradius/c;

    //nu: peak height, https://arxiv.org/pdf/1401.1216.pdf eqn1
    //alpha: Einasto parameter, https://arxiv.org/pdf/1401.1216.pdf eqn5
    double nu;
    double alpha;  //calibrated relation with nu, with virial mass
    double Mvir;
    double Delta_v;

    Delta_v = Dv_BryanNorman(cosmo, a, status); //virial definition, if odelta is this, the definition is virial.

    if (massdef_delta_m<Delta_v+1 && massdef_delta_m>Delta_v-1){   //allow rounding of virial definition
        Mvir = halomass;
    }
    else{
        double rhs; //NFW equation for cvir: f(Rvir/Rs)/f(c)=(Rvir^3*Delta_v)/(R^3*massdef) -> f(cvir)/cvir^3 = rhs
        rhs = (helper_fx(c)*(rs*rs*rs)*Delta_v)/((haloradius*haloradius*haloradius)*massdef_delta_m);
        Mvir = halomass*helper_fx(solve_cvir(cosmo, rhs, c, status))/helper_fx(c);
    }

    nu = 1.686/ccl_sigmaM(cosmo, log10(Mvir), a, status); //delta_c_Tinker
    alpha = 0.155 + 0.0095*nu*nu;

    //rhos: scale density
    double rhos;

    rhos = halomass/integrate_einasto(cosmo, haloradius, alpha, rs, status); //normalize

    int i;
    for(i=0; i < nr; i++) {
        rho_r[i] = rhos*exp(-2.*(pow(r[i]/rs,alpha)-1.)/alpha);
    }

    return;

}

// Structure to hold parameters of integrand_hernquist
typedef struct{
  ccl_cosmology *cosmo;
  double rs;
} Int_Hernquist_Par;

static double integrand_hernquist(double r, void *params){

    Int_Hernquist_Par *p = (Int_Hernquist_Par *)params;
    double rs = p->rs;

    return 4.*M_PI*r*r/((r/rs)*(1.+r/rs)*(1.+r/rs)*(1.+r/rs));
}

//integrate hernquist profile to get normalization of rhos
static double integrate_hernquist(ccl_cosmology *cosmo, double R, double rs, int *status){
    int qagstatus;
    double result = 0, eresult;
    Int_Hernquist_Par ipar;
    gsl_function F;
    gsl_integration_workspace *w = NULL;

    w = gsl_integration_workspace_alloc(1000);

    if (w == NULL) {
      *status = CCL_ERROR_MEMORY;
    }

    if (*status == 0) {
      // Structure required for the gsl integration
      ipar.cosmo = cosmo;
      ipar.rs = rs;
      F.function = &integrand_hernquist;
      F.params = &ipar;

      // Actually does the integration
      qagstatus = gsl_integration_qag(
        &F, 0, R, 0, 0.0001, 1000, 3, w,
        &result, &eresult);

      // Check for errors
      if (qagstatus != GSL_SUCCESS) {
        ccl_raise_gsl_warning(qagstatus, "ccl_haloprofile.c: integrate_hernquist():");
        *status = CCL_ERROR_PROFILE_INT;
        ccl_cosmology_set_status_message(cosmo, "ccl_haloprofile.c: integrate_hernquist(): Integration failure\n");
        result = NAN;
      }
    }

    // Clean up
    gsl_integration_workspace_free(w);

    return result;
}

//cosmo: ccl cosmology object containing cosmological parameters
//c: halo concentration, needs to be consistent with halo size definition
//halomass: halo mass
//massdef_delta: mass definition, overdensity relative to mean matter density
//a: scale factor
//r: radii at which to calculate output
//nr: number of radii for calculation
//rho_r: stores densities at r
//returns void
void ccl_halo_profile_hernquist(ccl_cosmology *cosmo, double c, double halomass,
                                double massdef_delta_m, double a, double *r, int nr,
                                double *rho_r, int *status) {

    //haloradius: halo radius for mass definition
    //rs: scale radius
    double haloradius, rs;

    haloradius = r_delta(cosmo, halomass, a, massdef_delta_m, status);
    rs = haloradius/c;

    //rhos: scale density
    double rhos;

    rhos = halomass/integrate_hernquist(cosmo, haloradius, rs, status); //normalize

    int i;
    double x;
    for(i=0; i < nr; i++) {
        x = r[i]/rs;
        rho_r[i] = rhos/(x*pow((1.+x),3));
    }

    return;

}


#include <math.h>
#include <stdio.h>
#include <mpfr.h>
#include <petsc.h>
#include <stdlib.h>
#include <string.h>

#include "ellipsoid/ellipsoid.h"

int testCoordinateTransform()
{
  //Verify that the ellipsoidal to Cartesian transformation is a bijection
  //the tolerance is 10 significant figures
  EllipsoidalSystem e;
  initEllipsoidalSystem(&e, 3.0, 2.0, 1.0, 32);

  double relErrorX, relErrorY, relErrorZ;

  //loop over octants
  for(int sx=-1; sx<=1; sx+=2) { //sx = [-1,1]
    for(int sy=-1; sy<=1; sy+=2) { //sy = [-1,1]
      for(int sz=-1; sz<=1; sz+=2) { //sz = [-1,1]
	//loop over brick in cartesian space
	for(double x=4; x<5+tol; x+=1.5) {
	  for(double y=4; y<5+tol; y+=1.5) {
	    for(double z=4; z<5+tol; z+=1.5) {
	      Point temp = { .x1 = sx*x, .x2 = sy*y, .x3 = sz*z };
	      cartesianToEllipsoidal(&e, &temp);
	      ellipsoidToCartesian(&e, &temp);
	      relErrorX = fabs(sx*x - temp.x1)/x;
	      relErrorY = fabs(sy*y - temp.x2)/y;
	      relErrorZ = fabs(sz*z - temp.x3)/z;
	      //check error
	      if( relErrorX > tol ||
		  relErrorY > tol ||
		  relErrorZ > tol) {
		printf("testCoordinateTransform failed\n");
		printf("relative error(x): %16.16f\n", relErrorX);
		printf("relative error(y): %16.16f\n", relErrorY);
		printf("relative error(z): %16.16f\n", relErrorZ);
		return 0;
	      }
	    }
	  }
	}
      }
    }
  }
  return 1;
}

int testLameType()
{

  static char formula1[18] = "K^0_1 L^0_1 M^0_1";
  static char formula2[30] = "K^0_2 K^1_2 L^0_2 M^0_2 N^0_2";
  static char formula3[66] = "K^0_5 K^1_5 K^2_5 L^0_5 L^1_5 L^2_5 M^0_5 M^1_5 M^2_5 N^0_5 N^1_5";

  char **checks = (char**) malloc(sizeof(char*)*3);
  checks[0] = (char*) malloc(sizeof(char)*17);
  checks[1] = (char*) malloc(sizeof(char)*29);
  checks[2] = (char*) malloc(sizeof(char)*65);

  EllipsoidalSystem e;
  initEllipsoidalSystem(&e, 3.0, 2.0, 1.0, 32);
  int index[3] = { 1, 2, 5 };
  int k;
  int n;
  for(int i=0; i<3; ++i) {
    n = index[i];
    for(int p=0; p < 2*n + 1; ++p) {
      k = p*6;
      checks[i][k]   = getLameTypeT(n, p);
      checks[i][k+1] = '^';
      checks[i][k+2] = getLameTypeTp(n, p) + '0';
      checks[i][k+3] = '_';
      checks[i][k+4] = n + '0';
      if( p != 2*n )
	checks[i][k+5] = ' ';
    }
  }
  //check first string
  for(int i=0; i < 17; ++i) {
    if(checks[0][i] != formula1[i]) {
      return 0;
    }
  }
  //check second string
  for(int i=0; i < 29; ++i) {
    if(checks[1][i] != formula2[i]) {
      return 0;
    }
  }
  //check first string
  for(int i=0; i < 65; ++i) {
    if(checks[2][i] != formula3[i]) {
      return 0;
    }
  }
  free(checks[0]);
  free(checks[1]);
  free(checks[2]);
  free(checks);
  return 1;
}

int testI()
{
  //Using results from Dassios, test the computation of I^p_n for n = 0, 1, 2
  EllipsoidalSystem e;
  initEllipsoidalSystem(&e, 3.0, 2.0, 1.0, 32);
  double a = e.a;
  double b = e.b;
  double c = e.c;
  double h = e.h;
  double k = e.k;
  double l = a;
  double rootSum = sqrt((a*a*a*a - b*b*c*c) + (b*b*b*b - a*a*c*c) + (c*c*c*c - a*a*b*b));
  double DassiosLambda = (1/3.)*(a*a + b*b + c*c) + (1/3.)*rootSum;
  double DassiosLambdaPrime = (1/3.)*(a*a + b*b + c*c) - (1/3.)*rootSum;

  double errors[18];
  int derror[18] = { 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 9, 10, 11, 12, 13 };
  int ecount = 0;
  //Dassios D1 error
  double lhs = 3 * (DassiosLambda + DassiosLambdaPrime);
  double rhs = 2 * (a*a + b*b + c*c);
  errors[ecount] = fabs(lhs - rhs); ecount++;
  //Dassios D2 error
  lhs = 3 * DassiosLambda * DassiosLambdaPrime;
  rhs = a*a*b*b + a*a*c*c + b*b*c*c;
  errors[ecount] = fabs(lhs - rhs); ecount ++;
  //Dassios D3 error
  lhs = ((-1)*(b*b - c*c)*(DassiosLambda - a*a)) + (k*k*(DassiosLambda - b*b)) + (-h*h*(DassiosLambda - c*c));
  rhs = ((-1)*(b*b - c*c)*(DassiosLambdaPrime - a*a)) + (k*k*(DassiosLambdaPrime - b*b)) + (-h*h*(DassiosLambdaPrime - c*c));
  errors[ecount] = fabs(lhs-0); ecount++;
  errors[ecount ] = fabs(rhs-0); ecount++;
  //Dassios D4 error
  lhs = (-a*a*(b*b - c*c)*(DassiosLambda - a*a)) + (b*b*k*k*(DassiosLambda - b*b)) + (-c*c*h*h*(DassiosLambda - c*c));
  rhs = (-a*a*(b*b - c*c)*(DassiosLambdaPrime - a*a)) + (b*b*k*k*(DassiosLambdaPrime - b*b)) + (-c*c*h*h*(DassiosLambdaPrime - c*c));
  double analytical = h*h*k*k*(b*b - c*c);
  errors[ecount] = fabs(lhs - analytical); ecount++;
  errors[ecount] = fabs(rhs - analytical); ecount++;
  //Dassios D5 error
  double alpha[3] = { a, b, c };
  lhs = 0;
  rhs = 0;
  for(int i=0; i<3; ++i) {
    lhs += alpha[i]*alpha[i]/(alpha[i]*alpha[i] - DassiosLambda);
    rhs += alpha[i]*alpha[i]/(alpha[i]*alpha[i] - DassiosLambdaPrime);
  }
  errors[ecount] = fabs(lhs - 3); ecount++;
  errors[ecount] = fabs(rhs - 3); ecount++;
  //Dassios D6 error
  double anal1 = h*h*k*k*(b*b - c*c);
  double num1  = 3*(b*b - c*c)*(DassiosLambda - a*a)*(DassiosLambdaPrime - a*a);
  double anal2 = -h*h*k*k*(b*b - c*c);
  double num2  = 3*k*k*(DassiosLambda - b*b)*(DassiosLambdaPrime - b*b);
  double anal3 = h*h*k*k*(b*b - c*c);
  double num3  = 3*h*h*(DassiosLambda - c*c)*(DassiosLambdaPrime - c*c);
  errors[ecount] = fabs(num1 - anal1); ecount++;
  errors[ecount] = fabs(num2 - anal2); ecount++;
  errors[ecount] = fabs(num3 - anal3); ecount++;

  //Calculate I^p_n
  int nmax = 2;
  int Isize = 0;
  for(int n=0; n < nmax+1; ++n) {
    for(int p=0; p < 2*n+1; ++p) {
      Isize++;
    }
  }
  double *Ival = (double*) malloc(sizeof(double)*Isize);
  int i=0;
  for(int n=0; n < nmax+1; ++n) {
    for(int p=0; p < 2*n+1; ++p) {
      calcI(&e, n, p, l, 1, 1, Ival+i); //Ival[i] = calcI(&e, n, p, l, 1, 1);
      i++;
    }
  }

  //Dassios D7: their h_3 = our h and their h_2 = our k
  double analyticalSumTest1 = 1.0/(l*sqrt(l*l - h*h)*sqrt(l*l - k*k));
  double numericalSumTest1 = 0;
  for(int k=1; k<4; ++k) {
    numericalSumTest1 += Ival[k];
  }
  errors[ecount] = fabs(analyticalSumTest1 - numericalSumTest1); ecount++;

  //Dassios D8: their alpha1 = our a, their alpha2 = our b, their alpha3 = our c
  double analyticalSumTest2 = Ival[0] - (l*l - a*a)/(l*sqrt(l*l - h*h)*sqrt(l*l - k*k));
  double numericalSumTest2 = a*a*Ival[1] + b*b*Ival[2] + c*c*Ival[3];
  errors[ecount] = fabs(analyticalSumTest2 - numericalSumTest2); ecount++;
  //Dassios D9: their Lambda = our DassiosLambda, their LambdaPrime = our DassiosLambdaPrime
  double analyticalSumTest3 = 1.0/(2.0*(DassiosLambda - a*a + l*l)*l*sqrt(l*l - h*h)*sqrt(l*l - k*k)) - (1.0/2.0)*(Ival[1]/(DassiosLambda - a*a) + Ival[2]/(DassiosLambda - b*b) + Ival[3]/(DassiosLambda - c*c));
  double numericalSumTest3 = Ival[4];
  errors[ecount] = fabs(analyticalSumTest3 - numericalSumTest3); ecount++;
  //Dassios D10: their Lambda = our DassiosLambda, their LambdaPrime = our DassiosLambdaPrime
  double analyticalSumTest4 = 1.0/(2.0*(DassiosLambdaPrime - a*a + l*l)*l*sqrt(l*l - h*h)*sqrt(l*l - k*k)) - (1.0/2.0)*(Ival[1]/(DassiosLambdaPrime - a*a) + Ival[2]/(DassiosLambdaPrime - b*b) + Ival[3]/(DassiosLambdaPrime - c*c));
  double numericalSumTest4 = Ival[5];
  errors[ecount] = fabs(analyticalSumTest4 - numericalSumTest4); ecount++;
  //Dassios D11: their h1*h1 = our (b*b - c*c)
  double analyticalSumTest5 = (1.0/(h*h)) * (Ival[2] - Ival[1]);
  double numericalSumTest5 = Ival[6];
  errors[ecount] = fabs(analyticalSumTest5 - numericalSumTest5); ecount++;
  //Dassios D12: their h1*h1 = our (b*b - c*c)
  double analyticalSumTest6 = (1.0/(k*k)) * (Ival[3] - Ival[1]);
  double numericalSumTest6 = Ival[7];
  errors[ecount] = fabs(analyticalSumTest6 - numericalSumTest6); ecount++;
  //Dassios D13: their h1*h1 = our (b*b - c*c)
  double analyticalSumTest7 = (1.0/(b*b - c*c)) * (Ival[3] - Ival[2]);
  double numericalSumTest7 = Ival[8];
  errors[ecount] = fabs(analyticalSumTest7 - numericalSumTest7); ecount++;

  //checking errors
  for(int i=0; i < 18; ++i) {
    if(errors[i] > tol) {
      printf("Dassios D%d failed with error %8.8e\n", derror[i], errors[i]);
      free(Ival);
      return 0;
    }
  }

  free(Ival);
  return 1;
}

int testNormalization()
{
  //Check calculated \gamma^p_n against analytic results for n = 0, 1, 2
  double a = 3.0;
  double b = 2.0;
  double c = 1.0;
  EllipsoidalSystem e;
  initEllipsoidalSystem(&e, a, b, c, 32);
  //Dassios (B14)
  double firstTerm = (a*a + b*b + c*c)/3.0;
  double secondTerm = sqrt((a*a*a*a - b*b*c*c) + (b*b*b*b - a*a*c*c) + (c*c*c*c - a*a*b*b))/3.0;
  double LambdaD = firstTerm + secondTerm;
  double LambdaDprime = firstTerm - secondTerm;
  //Dassios (B16)-(B20)
  double hx = sqrt(b*b - c*c);
  double hy = e.k;
  double hz = e.h;
  double analytic[9] = { 4*PETSC_PI,
			 4*PETSC_PI/3 * hy*hy*hz*hz,
			 4*PETSC_PI/3 * hx*hx*hz*hz,
			 4*PETSC_PI/3 * hx*hx*hy*hy,
			 -8*PETSC_PI/5 * (LambdaD - LambdaDprime)*(LambdaD - a*a)*(LambdaD - b*b)*(LambdaD - c*c),
			 8*PETSC_PI/5 * (LambdaD - LambdaDprime)*(LambdaDprime - a*a)*(LambdaDprime - b*b)*(LambdaDprime - c*c),
			 4*PETSC_PI/15 * hx*hx*hy*hy*hz*hz*hz*hz,
			 4*PETSC_PI/15 * hx*hx*hy*hy*hy*hy*hz*hz,
			 4*PETSC_PI/15 * hx*hx*hx*hx*hy*hy*hz*hz };
  printf("analytic[0] = %4.4e\n", analytic[0]);
  printf("analytic[1] = %4.4e\n", analytic[1]);
  double value, estimate, error;
  int counter = 0;
  for(int n=0; n<3; ++n) {
    for(int p=0; p<2*n+1; ++p) {
      value = analytic[counter];
      calcNormalization(&e, n, p, &estimate);
      error = estimate - value;
      if(fabs(error) > tol) {
	printf("testnormalization failed iteration %d at (n,p) = (%d,%d) with error %8.8e\n", counter, n, p, fabs(error));
	return 0;
      }
      counter++;
    }
  }
  return 1;

}

void testfunc(mpfr_t *x, mpfr_t *val, FuncInfo4 *ctx)
{
  double a2 = (*ctx).a2;
  double b2 = (*ctx).b2;
  double c2 = (*ctx).c2;
  double botVar = (*ctx).botVar;

  mpfr_t temp, s;
  mpfr_init2(temp, mpfr_get_prec(*x));
  mpfr_init2(s   , mpfr_get_prec(*x));
  //change of variables
  //s = (1-x)/x
  mpfr_d_sub(s, 1.0, *x, MPFR_RNDZ);
  mpfr_div(s, s, *x, MPFR_RNDN);

  mpfr_add_d(temp, s, a2, MPFR_RNDN);
  mpfr_add_d(*val, s, b2, MPFR_RNDN);
  mpfr_mul(*val, *val, temp, MPFR_RNDN);
  mpfr_add_d(temp, s, c2, MPFR_RNDN);
  mpfr_mul(*val, *val, temp, MPFR_RNDN);
  mpfr_sqrt(*val, *val, MPFR_RNDN);

  mpfr_add_d(temp, s, botVar, MPFR_RNDN);
  mpfr_mul(*val, *val, temp, MPFR_RNDN);

  mpfr_d_div(*val, 1.0, *val, MPFR_RNDN);

  //multiply by dt = ds * (1+t)^2
  mpfr_add_d(temp, s, 1.0, MPFR_RNDN);
  mpfr_mul(temp, temp, temp, MPFR_RNDN);
  mpfr_mul(*val, *val, temp, MPFR_RNDN);
  //mpfr_clears(temp, s, NULL);
}

int testSurfaceOperatorEigenvalues()
{
  EllipsoidalSystem e;
  double a = 3.0;
  double b = 2.0;
  double c = 1.0;
  initEllipsoidalSystem(&e, a, b, c, 32);
  double l = a;
  int n = 1;
  double analytic[3];
  mpfr_t integral;

  mpfr_t mpfrzero, mpfrone;
  mpfr_inits(mpfrzero, mpfrone, integral, NULL);
  mpfr_set_d(mpfrzero, 0.0, MPFR_RNDN);
  mpfr_set_d(mpfrone, 1.0, MPFR_RNDN);

  //Integrals are from Ritter, need to transform integrals to finite interval
  FuncInfo4 ctx = { &e, a*a, b*b, c*c, a*a };
  integrateMPFR((PetscErrorCode (*)(mpfr_t*, mpfr_t*, void*)) testfunc, &e, mpfrzero, mpfrone, 14, &integral, &ctx);
  analytic[0] = (a*b*c * mpfr_get_d(integral, MPFR_RNDN) - 1.0)/2.0;
  ctx.botVar = b*b;
  integrateMPFR((PetscErrorCode (*)(mpfr_t*, mpfr_t*, void*)) testfunc, &e, mpfrzero, mpfrone, 14, &integral, &ctx);
  analytic[1] = (a*b*c * mpfr_get_d(integral, MPFR_RNDN) - 1.0)/2.0;
  ctx.botVar = c*c;
  integrateMPFR((PetscErrorCode (*)(mpfr_t*, mpfr_t*, void*)) testfunc, &e, mpfrzero, mpfrone, 14, &integral, &ctx);
  analytic[2] = (a*b*c * mpfr_get_d(integral, MPFR_RNDN) - 1.0)/2.0;

  double *ev = (double*) malloc(sizeof(double)*(3));
  for(int p=0; p < 3; ++p)
    calcSurfaceOperatorEigenvalues(&e, n, p, l, 1, 1, ev+p); //ev[p] = calcSurfaceOperatorEigenvalues(&e, n, p, l, 1, 1);
  for(int p=0; p < 3; ++p) {
    if(fabs(ev[p] - analytic[p]) > tol) {
      printf("analytic (%8.8e) doesn't match numerical (%8.8e), error = (%8.8e) for integral %d\n", analytic[p], ev[p], fabs(ev[p] - analytic[p]), p+1);
      return 0;
    }
  }
  mpfr_clears(mpfrzero, mpfrone, integral, NULL);
  free(ev);
  return 1;

}

int compareIntegration()
{

  //int n, p given in calcNormalization
  int n = 1;
  int p = 1;

  int digits = 32;

  //set up ellipsoidal system
  EllipsoidalSystem e;
  double xA = 3.0;
  double yB = 2.0;
  double zC = 1.0;
  initEllipsoidalSystem(&e, xA, yB, zC, digits);

  mpfr_t bound_a, bound_b;
  mpfr_inits2(4*digits, bound_a, bound_b, NULL);

  FuncInfo2 ctx1 = { .e = &e, .n = n, .p = p, .numeratorType = 0, .denomSign = 1 };

  mpfr_t a, b, c;
  mpfr_inits(a, b, c, NULL);
  double integralMPFR, integralMidpoint;

  integrateMPFR((PetscErrorCode (*)(mpfr_t*, mpfr_t*, void*)) normFunction1, &e, e.hp_h, e.hp_k, 14, &integralMPFR, &ctx1);
  integrateMidpoint((PetscErrorCode (*)(mpfr_t*, mpfr_t*, void*)) normFunction1, e.hp_h, e.hp_k, 10, &integralMidpoint, &ctx1);

  printf("MPFR integral is: %15.15f\n", integralMPFR);
  printf("Midpoint integral is: %15.15f\n", integralMidpoint);
  printf("oh wow\n");

  mpfr_clears(a, b, c, NULL);

  return 0;
}

int main(int argc, char **argv)
{
  mpfr_set_default_prec(4*64);
  /*
  EllipsoidalSystem e;
  initEllipsoidalSystem(&e, 3.0, 2.0, 1.0);
  printf("h: %15.15f\nh2: %15.15f\nk: %15.15f\nk2: %15.15f\n", e.h, e.h2, e.k, e.k2);
  int n = 1;
  int p = 1;
  double l = 1.0;

  //printf("calcLame(1, 1, 1.0) = %15.15f\n", calcLame(&e, 1, 1, 1.0));

  printf("calcNormalization(%d, %d, %15.15f) = %15.15f\n", n, p, l, calcNormalization(&e, n, p));
  */

  if(testCoordinateTransform())
    printf("testCoordinateTransform         passed\n");
  else
    printf("testCoordinateTransform         failed\n");
  if(testLameType())
    printf("testLameType                    passed\n");
  else
    printf("testLameType                    failed\n");
  if(testI())
    printf("testI                           passed\n");
  else
    printf("testI                           failed\n");
  if(testNormalization())
    printf("testNormalization               passed\n");
  else
    printf("testNormalization               failed\n");
  if(testSurfaceOperatorEigenvalues())
    printf("testSurfaceOperatorEigenvalues  passed\n");
  else
    printf("testSurfaceOperatorEigenvalues  failed\n");

  //compareIntegration();

  /*
  double h = .1;
  mpfr_t x, val;
  EllipsoidalSystem e;
  initEllipsoidalSystem(&e, 3.0, 2.0, 1.0);
  FuncInfo2 ctx1 = { .e = &e, .n = 1, .p = 2, .numeratorType = 0, .denomSign = -1 };
  mpfr_inits2(4*16, x, val, NULL);
  for(int k=0; k < 10; ++k) {
    mpfr_set_d(x, k*h, MPFR_RNDN);
    normFunction1(&x, &val, &ctx1);
    printf("normfunction1(%8.8f) = %15.15f\n", k*h, mpfr_get_d(val, MPFR_RNDN));
  }
  */
  return 0;
}


/* poly/gsl_poly.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004, 2007 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_POLY_H__
#define __GSL_POLY_H__

#include <stdlib.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_complex.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

/* Evaluate polynomial
 *
 * c[0] + c[1] x + c[2] x^2 + ... + c[len-1] x^(len-1)
 *
 * exceptions: none
 */

/* real polynomial, real x */
INLINE_DECL double gsl_poly_eval(const double c[], const int len, const double x);

/* real polynomial, complex x */
INLINE_DECL gsl_complex gsl_poly_complex_eval (const double c [], const int len, const gsl_complex z);

/* complex polynomial, complex x */
INLINE_DECL gsl_complex gsl_complex_poly_complex_eval (const gsl_complex c [], const int len, const gsl_complex z);

int gsl_poly_eval_derivs(const double c[], const size_t lenc, const double x, double res[], const size_t lenres);

#ifdef HAVE_INLINE
INLINE_FUN
double
gsl_poly_eval(const double c[], const int len, const double x)
{
  int i;
  double ans = c[len-1];
  for(i=len-1; i>0; i--) ans = c[i-1] + x * ans;
  return ans;
}

INLINE_FUN
gsl_complex
gsl_poly_complex_eval(const double c[], const int len, const gsl_complex z)
{
  int i;
  gsl_complex ans;
  GSL_SET_COMPLEX (&ans, c[len-1], 0.0);
  for(i=len-1; i>0; i--) {
    /* The following three lines are equivalent to
       ans = gsl_complex_add_real (gsl_complex_mul (z, ans), c[i-1]);
       but faster */
    double tmp = c[i-1] + GSL_REAL (z) * GSL_REAL (ans) - GSL_IMAG (z) * GSL_IMAG (ans);
    GSL_SET_IMAG (&ans, GSL_IMAG (z) * GSL_REAL (ans) + GSL_REAL (z) * GSL_IMAG (ans));
    GSL_SET_REAL (&ans, tmp);
  }
  return ans;
}

INLINE_FUN
gsl_complex
gsl_complex_poly_complex_eval(const gsl_complex c[], const int len, const gsl_complex z)
{
  int i;
  gsl_complex ans = c[len-1];
  for(i=len-1; i>0; i--) {
    /* The following three lines are equivalent to
       ans = gsl_complex_add (c[i-1], gsl_complex_mul (x, ans));
       but faster */
    double tmp = GSL_REAL (c[i-1]) + GSL_REAL (z) * GSL_REAL (ans) - GSL_IMAG (z) * GSL_IMAG (ans);
    GSL_SET_IMAG (&ans, GSL_IMAG (c[i-1]) + GSL_IMAG (z) * GSL_REAL (ans) + GSL_REAL (z) * GSL_IMAG (ans));
    GSL_SET_REAL (&ans, tmp);
  }
  return ans;
}
#endif /* HAVE_INLINE */

/* Work with divided-difference polynomials, Abramowitz & Stegun 25.2.26 */

int
gsl_poly_dd_init (double dd[], const double x[], const double y[],
                  size_t size);

INLINE_DECL double
gsl_poly_dd_eval (const double dd[], const double xa[], const size_t size, const double x);

#ifdef HAVE_INLINE
INLINE_FUN
double
gsl_poly_dd_eval(const double dd[], const double xa[], const size_t size, const double x)
{
  size_t i;
  double y = dd[size - 1];
  for (i = size - 1; i--;) y = dd[i] + (x - xa[i]) * y;
  return y;
}
#endif /* HAVE_INLINE */

int
gsl_poly_dd_taylor (double c[], double xp,
                    const double dd[], const double x[], size_t size,
                    double w[]);

/* Solve for real or complex roots of the standard quadratic equation,
 * returning the number of real roots.
 *
 * Roots are returned ordered.
 */
int gsl_poly_solve_quadratic (double a, double b, double c,
                              double * x0, double * x1);

int
gsl_poly_complex_solve_quadratic (double a, double b, double c,
                                  gsl_complex * z0, gsl_complex * z1);

/* Solve for real roots of the cubic equation
 * x^3 + a x^2 + b x + c = 0, returning the
 * number of real roots.
 *
 * Roots are returned ordered.
 */
int gsl_poly_solve_cubic (double a, double b, double c,
                          double * x0, double * x1, double * x2);

int
gsl_poly_complex_solve_cubic (double a, double b, double c,
                              gsl_complex * z0, gsl_complex * z1,
                              gsl_complex * z2);

/* Solve for the complex roots of a general real polynomial */

typedef struct
{
  size_t nc ;
  double * matrix ;
}
gsl_poly_complex_workspace ;

gsl_poly_complex_workspace * gsl_poly_complex_workspace_alloc (size_t n);
void gsl_poly_complex_workspace_free (gsl_poly_complex_workspace * w);

int
gsl_poly_complex_solve (const double * a, size_t n,
                        gsl_poly_complex_workspace * w,
                        gsl_complex_packed_ptr z);

__END_DECLS

#endif /* __GSL_POLY_H__ */


/*This program evaluates the differential equations for a photon's geodesic in Minkowski spacetime.
The equations are written in the form $\frac{d(x or p)^{\alpha}}{d\lambda}=f(x^{\alpha},p^{\alpha})$.
Where $p^{\alpha}={\dot{x}}^{\alpha}$*/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_errno.h>          //GSL error management module

#define C 299792.458     //Speed of light in velocity units
#define DLAMBDA 0.001   //Geodesics parameter step

/*Function for solving the differential equations of Minkowski geodesics.*/
void euler1(double *x0, double *x1, double *x2, double *x3, double *p0, double *p1, double *p2, double *p3, double *lambda)
{
  /*Increment in the variables of the differential equation we want to solve*/
  double dx0, dx1, dx2, dx3, dp0, dp1, dp2, dp3;

  /*Calculation of the increments*/
  dx0 = *p0*DLAMBDA; dx1 = *p1*DLAMBDA; dx2 = *p2*DLAMBDA; dx3 = *p3*DLAMBDA;
  dp0 = 0.0; dp1 = 0.0; dp2 = 0.0; dp3 = 0.0;

  /*New values of the variables of the differential equation. Since we are using pointers, when called the routine the value of variable change.*/
  *x0 = *x0 + dx0; *x1 = *x1 + dx1; *x2 = *x2 + dx2; *x3 = *x3 + dx3;
  *p0 = *p0 + dp0; *p1 = *p1 + dp1; *p2 = *p2 + dp2; *p3 = *p3 + dp3;

  /*Increment of parameter of geodesics*/
  *lambda = *lambda + DLAMBDA;
}

int main(void)
{
  int i;            //For array manipulation

  /*Initial conditions*/
  double x0 = 0.0, x1 = 0.0, x2 = 0.0, x3 = 0.0, p1 = 0.1*C, p2 = 0.9*C, p3 = 0.0, lambda = 0.0;
  double p0 = sqrt(pow(p1,2) + pow(p2,2) + pow(p3,2))/C;

  /*Pointer to file where solution of differential equation will be saved.*/
  FILE *geodesic;
  geodesic = fopen("geodesic_solution.dat","w");

  /*Write line of initial values in file*/
  fprintf(geodesic, "%.12lf %.12lf %.12lf %.12lf %.12lf %.12lf %.12lf %.12lf %.12lf\n", lambda, x0, x1, x2, x3, p0, p1, p2, p3);

  /*Solution of the differential equation*/
  for(i=0; i<1000; i++)
    {
      euler1(&x0, &x1, &x2, &x3, &p0, &p1, &p2, &p3, &lambda);
      fprintf(geodesic, "%12lf %12lf %12lf %12lf %12lf %12lf %12lf %12lf %12lf\n", lambda, x0, x1, x2, x3, p0, p1, p2, p3);
    }

  /** Releasing all used space in memory **/
  fclose(geodesic); //Close file storing the results

  /*GNUPLOT*/

  FILE *plot;
  plot = popen("gnuplot -persist","w");
  fprintf(plot, "set terminal x11 0\n");
  fprintf(plot, "set multiplot layout 1,3\n");
  fprintf(plot, "plot 'geodesic_solution.dat' using 1:2 not\n");
  fprintf(plot, "plot 'geodesic_solution.dat' using 1:3 not\n");
  fprintf(plot, "plot 'geodesic_solution.dat' using 1:4 not\n");
  fprintf(plot, "unset multiplot\n");
  fprintf(plot, "reset\n");
  fprintf(plot, "set terminal x11 1\n");
  fprintf(plot, "set multiplot layout 1,3\n");
  fprintf(plot, "plot 'geodesic_solution.dat' using 1:6 not\n");
  fprintf(plot, "plot 'geodesic_solution.dat' using 1:7 not\n");
  fprintf(plot, "plot 'geodesic_solution.dat' using 1:8 not\n");
  fprintf(plot, "unset multiplot\n");
  fprintf(plot, "set terminal x11 2\n");
  fprintf(plot, "splot 'geodesic_solution.dat' using 2:3:1\n");

  pclose(plot);
  //system("rm geodesic_solution.dat");
}


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <string.h>

#ifdef ACCELERATE
#include <Accelerate/Accelerate.h>
#else
#include <cblas.h>
#endif

#include "neural.h"

void
neural_feed_forward(struct neural_net_layer *head, float *x, int batch_size)
{
	/* point "current" layer to head and set input as feature array x */
	struct neural_net_layer *curr = head;
	struct neural_net_layer *next;
	curr->act = x;

	int in_nodes;
	int out_nodes;

	while ((next = curr->next) != NULL) {
		in_nodes = curr->in_nodes;
		out_nodes = curr->out_nodes;

		/* act = X * Theta^T + bias */
		for (int i = 0; i < batch_size; i++) {
			memcpy(next->act + i * out_nodes, curr->bias, out_nodes * sizeof(float));
		}
		cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, batch_size, out_nodes, in_nodes,
			1.0, curr->act, in_nodes, curr->theta, in_nodes, 1.0, next->act, out_nodes);

		/* apply sigmoid function */
		for (int i = 0; i < out_nodes * batch_size; i++) {
			next->act[i] = 1.0 / (1 + expf(-next->act[i]));
		}

		/* transition to next layer */
		curr = next;
	}
}

void
neural_sgd_feed_forward(struct neural_net_layer *head, float *x)
{
	/* point "current" layer to head and set input as feature array x */
	struct neural_net_layer *curr = head;
	struct neural_net_layer *next;
	curr->act = x;

	int rows;
	int cols;

	while ((next = curr->next) != NULL) {
		rows = curr->out_nodes;
		cols = curr->in_nodes;

		/* act = theta * x + act */
		memcpy(next->act, curr->bias, rows * sizeof(float));
		cblas_sgemv(CblasRowMajor, CblasNoTrans, rows, cols, 1.0, curr->theta,
			cols, curr->act, 1, 1.0, next->act, 1);

		/* apply sigmoid function */
		for (int i = 0; i < rows; i++) {
			next->act[i] = 1.0 / (1 + expf(-next->act[i]));
		}

		/* transition to next layer */
		curr = next;
	}
}

void
neural_back_prop(struct neural_net_layer *tail, float *y, const int batch_size,
		const float alpha, const float lambda)
{
	/* get delta from final predictions vs. actual; delta = a - y */
	cblas_scopy(tail->in_nodes * batch_size, tail->act, 1, tail->delta, 1);
	cblas_saxpy(tail->in_nodes * batch_size, -1.0, y, 1, tail->delta, 1);

	struct neural_net_layer *curr;
	struct neural_net_layer *prev;
	int in_nodes;
	int out_nodes;

	for (curr = tail; curr->prev != NULL; curr = prev) {
		prev = curr->prev;
		in_nodes = prev->in_nodes;
		out_nodes = prev->out_nodes;

		/* Delta^(n-1) = Delta^(n) * Theta^(n-1) .* A^(n-1) (1 - A^(n-1)) */
		cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, batch_size, in_nodes, out_nodes,
				1.0, curr->delta, out_nodes, prev->theta, in_nodes, 0.0, prev->delta, in_nodes);
		for (int j = 0; j < in_nodes * batch_size; j++) {
			prev->delta[j] *= (prev->act[j] * (1 - prev->act[j]));
		}
	}

	for (curr = tail; curr->prev != NULL; curr = prev) {
		prev = curr->prev;
		in_nodes = prev->in_nodes;
		out_nodes = prev->out_nodes;

		/* bias -= alpha * delta */
		for (int i = 0; i < batch_size; i++) {
			cblas_saxpy(out_nodes, -alpha, curr->delta + i * out_nodes, 1, prev->bias, 1);
		}

		/* Theta -= alpha * (Delta^T * Act + lambda * Theta) */
		cblas_sgemm(CblasRowMajor, CblasTrans, CblasNoTrans, out_nodes, in_nodes, batch_size,
				-alpha, curr->delta, out_nodes, prev->act, in_nodes, 1 - alpha * lambda,
				prev->theta, in_nodes);
	}
}

void
neural_sgd_back_prop(struct neural_net_layer *tail, float *y, const float alpha,
		const float lambda)
{
	/* get delta from final predictions vs. actual; delta = a - y */
	cblas_scopy(tail->in_nodes, tail->act, 1, tail->delta, 1);
	cblas_saxpy(tail->in_nodes, -1.0, y, 1, tail->delta, 1);

	struct neural_net_layer *curr;
	struct neural_net_layer *prev;
	int rows;
	int cols;

	for (curr = tail; curr->prev != NULL; curr = prev) {
		prev = curr->prev;
		rows = prev->out_nodes;
		cols = prev->in_nodes;

		/* delta^(n-1) = theta^(n-1)T * delta^(n) .* a^(n-1) (1 - a^(n-1)) */
		cblas_sgemv(CblasRowMajor, CblasTrans, rows, cols, 1.0, prev->theta,
				cols, curr->delta, 1, 0.0, prev->delta, 1);
		for (int j = 0; j < cols; j++) {
			prev->delta[j] *= (prev->act[j] * (1 - prev->act[j]));
		}
	}

	for (curr = tail; curr->prev != NULL; curr = prev) {
		prev = curr->prev;
		rows = prev->out_nodes;
		cols = prev->in_nodes;

		/* bias -= alpha * delta */
		cblas_saxpy(rows, -alpha, curr->delta, 1, prev->bias, 1);

		/* theta -= alpha * (delta * actT + lambda * theta) */
		cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, rows, cols, 1, -alpha,
				curr->delta, 1, prev->act, cols, 1 - alpha * lambda, prev->theta, cols);
	}
}

void
neural_train_iteration(struct neural_net_layer *head, struct neural_net_layer *tail,
		float *features, float *labels, const int n_samples, const int batch_size,
		const float alpha, const float lambda)
{
	int n_features = head->in_nodes;
	int n_labels = tail->in_nodes;
	int i;

	if (batch_size == 1) {
		for (i = 0; i < n_samples; i++) {
			neural_sgd_feed_forward(head, features + i * n_features);
			neural_sgd_back_prop(tail, labels + i * n_labels, alpha, lambda);
		}
	} else {
		for (i = 0; i < n_samples / batch_size; i++) {
			neural_feed_forward(head, features + i * n_features * batch_size, batch_size);
			neural_back_prop(tail, labels + i * n_labels * batch_size, batch_size, alpha, lambda);
		}

		/* take care of remaining examples */
		int remainder = n_samples % batch_size;
		if (remainder > 0) {
			neural_feed_forward(head, features + i * n_features * batch_size, remainder);
			neural_back_prop(tail, labels + i * n_labels * batch_size, remainder, alpha, lambda);
		}
	}
}

void
neural_predict_prob(struct neural_net_layer *head, struct neural_net_layer *tail,
		float *features, float *preds, const int n_samples, const int batch_size)
{
	int n_features = head->in_nodes;
	int n_labels = tail->in_nodes;
	int i;

	if (batch_size == 1) {
		for (i = 0; i < n_samples; i++) {
			neural_sgd_feed_forward(head, features + i * n_features);
			memcpy(preds + i * n_labels, tail->act, n_labels * sizeof(float));
		}
	} else {
		for (i = 0; i < n_samples / batch_size; i++) {
			neural_feed_forward(head, features + i * n_features * batch_size, batch_size);
			memcpy(preds + i * n_labels * batch_size, tail->act,
					n_labels * batch_size * sizeof(float));
		}

		/* take care of remaining examples */
		int remainder = n_samples % batch_size;
		if (remainder > 0) {
			neural_feed_forward(head, features + i * n_features * batch_size, remainder);
			memcpy(preds + i * n_labels * batch_size, tail->act,
					n_labels * remainder * sizeof(float));
		}
	}
}


#include "matrix_support.h"
#include <cblas.h>

int main(int argc, char *argv[])
{
    /* enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102}; */
    /* enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113}; */
    /* enum CBLAS_UPLO {CblasUpper=121, CblasLower=122}; */
    /* enum CBLAS_DIAG {CblasNonUnit=131, CblasUnit=132}; */
    /* enum CBLAS_SIDE {CblasLeft=141, CblasRight=142}; */

    CBLAS_ORDER order = CblasRowMajor;
    CBLAS_TRANSPOSE transA = CblasNoTrans;
    CBLAS_TRANSPOSE transB = CblasNoTrans;
    int m = 2;
    int k = 3;
    int n = 4;
    double alpha = 1;
    double beta = 1;
    double *A = create_matrix(m, k);
    double *B = create_matrix(k, n);
    double *C = create_matrix(m, n);
    double *D = create_matrix(n, k);
    double *x = create_vector(k);
    double *d = create_vector(m);
    printf("A\n");
    init_matrix_vector(A, m * k, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0);
    print_matrix(A, m, k);
    printf("\nB\n");
    init_matrix_vector(B, k * n, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
            10.0, 11.0);
    print_matrix(B, k, n);
    printf("\nD\n");
    init_matrix_vector(D, n * k, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
            10.0, 11.0);
    print_matrix(D, n, k);
    printf("\nx\n");
    init_matrix_vector(x, k * 1, -2.0, 2.0, 10.0);
    print_vector(x, k);

    /* void cblas_dgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, */
    /*                  const enum CBLAS_TRANSPOSE TransB, const int M, const int N, */
    /*                  const int K, const double alpha, const double *A, */
    /*                  const int lda, const double *B, const int ldb, */
    /*                  const double beta, double *C, const int ldc); */

    cblas_dgemm(order,
            transA,
            transB,
            m,
            n,
            k,
            alpha,
            A,
            k,
            B,
            n,
            beta,
            C,
            n);
    printf("\nC = A.dot(B)\n");
    print_matrix(C, m, n);

    printf("\nd = A.dot(x)\n");

    /*
     * dasum takes the sum of the absolute values
     */

    /**
     * ddot dot product between two vectors
     */

    /*
     * dnrm2 Eucledian norm of a vector
     */

    /*
     * dscal scales a vector by a constant
     */

    /*
     * daxpy constant times a vector plus a vector. uses unrolled loops for
     * increments equal to one.
     */

    /* void cblas_dgemv(const enum CBLAS_ORDER order, */
    /*              const enum CBLAS_TRANSPOSE TransA, const int M, const int N, */
    /*              const double alpha, const double *A, const int lda, */
    /*              const double *X, const int incX, const double beta, */
    /*              double *Y, const int incY); */
    cblas_dgemv(order,
            transA, // not transposed
            m,   // rows of a
            k,   // cols of a
            alpha, // scalar to multiply A with
            A,   //matrix A
            k, // lda: leading dimension of A
            x, // vector x
            1, // incx
            beta, // beta: scalar to multiply y with (0 having y as output)
            d, // vector d (Y in formula)
            1); // incy

    print_vector(d, m);
    return 0;
}


static char help[] =
"ODE system solver example using TS.  Solves N-dimensional system\n"
"    dy/dt = G(t,y)\n"
"with y(t0) = y0 to compute y(tf).  Serial only.\n"
"Sets TS type to Runge-Kutta.  The implemented example has\n"
"G_0 = y_1, G_1 = - y_0 + t, y_0(0) = 0, y_1(0) = 0.  The exact solution is\n"
"y_0(t) = t - sin(t), y_1(t) = 1 - cos(t).\n\n";

#include <petsc.h>

extern PetscErrorCode ExactSolution(double, Vec);
extern PetscErrorCode FormRHSFunction(TS, double, Vec, Vec, void*);

//STARTMAIN
int main(int argc,char **argv) {
  PetscErrorCode ierr;
  int       steps;
  double    t0 = 0.0, tf = 20.0, dt = 0.1, err;
  Vec       y, yexact;
  TS        ts;

  PetscInitialize(&argc,&argv,(char*)0,help);

  ierr = VecCreate(PETSC_COMM_WORLD,&y); CHKERRQ(ierr);
  ierr = VecSetSizes(y,PETSC_DECIDE,2); CHKERRQ(ierr);
  ierr = VecSetFromOptions(y); CHKERRQ(ierr);
  ierr = VecDuplicate(y,&yexact); CHKERRQ(ierr);

  ierr = TSCreate(PETSC_COMM_WORLD,&ts); CHKERRQ(ierr);
  ierr = TSSetProblemType(ts,TS_NONLINEAR); CHKERRQ(ierr);
  ierr = TSSetRHSFunction(ts,NULL,FormRHSFunction,NULL); CHKERRQ(ierr);
  ierr = TSSetType(ts,TSRK); CHKERRQ(ierr);

  // set time axis
  ierr = TSSetTime(ts,t0); CHKERRQ(ierr);
  ierr = TSSetMaxTime(ts,tf); CHKERRQ(ierr);
  ierr = TSSetTimeStep(ts,dt); CHKERRQ(ierr);
  ierr = TSSetExactFinalTime(ts,TS_EXACTFINALTIME_MATCHSTEP); CHKERRQ(ierr);
  ierr = TSSetFromOptions(ts); CHKERRQ(ierr);

  // set initial values and solve
  ierr = TSGetTime(ts,&t0); CHKERRQ(ierr);
  ierr = ExactSolution(t0,y); CHKERRQ(ierr);
  ierr = TSSolve(ts,y); CHKERRQ(ierr);

  // compute error and report
  ierr = TSGetStepNumber(ts,&steps); CHKERRQ(ierr);
  ierr = TSGetTime(ts,&tf); CHKERRQ(ierr);
  ierr = ExactSolution(tf,yexact); CHKERRQ(ierr);
  ierr = VecAXPY(y,-1.0,yexact); CHKERRQ(ierr);    // y <- y - yexact
  ierr = VecNorm(y,NORM_INFINITY,&err); CHKERRQ(ierr);
  ierr = PetscPrintf(PETSC_COMM_WORLD,
              "error at tf = %.3f with %d steps:  |y-y_exact|_inf = %g\n",
              tf,steps,err); CHKERRQ(ierr);

  VecDestroy(&y);  VecDestroy(&yexact);  TSDestroy(&ts);
  return PetscFinalize();
}
//ENDMAIN

//STARTCALLBACKS
PetscErrorCode ExactSolution(double t, Vec y) {
    double *ay;
    VecGetArray(y,&ay);
    ay[0] = t - sin(t);
    ay[1] = 1.0 - cos(t);
    VecRestoreArray(y,&ay);
    return 0;
}

PetscErrorCode FormRHSFunction(TS ts, double t, Vec y, Vec g, void *ptr) {
    const double *ay;
    double       *ag;
    VecGetArrayRead(y,&ay);
    VecGetArray(g,&ag);
    ag[0] = ay[1];            // = G_1(t,y)
    ag[1] = - ay[0] + t;      // = G_2(t,y)
    VecRestoreArrayRead(y,&ay);
    VecRestoreArray(g,&ag);
    return 0;
}
//ENDCALLBACKS


/*
** t-tests
**
** G.Lohmann, Jan 2017
*/

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

#include <gsl/gsl_cdf.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

#define TINY 1.0e-10
#define ABS(x) ((x) > 0 ? (x) : -(x))
#define SQR(x) ((x)*(x))

/*
** convert t to z values
*/
double t2z(double t,double df)
{
  double p=0,z=0;
  double a,b,x;
  extern double gsl_sf_beta_inc(double,double,double);

  /* t to p */
  x = df/(df+(t*t));
  if (x <= 0 || x > 1) return 0;

  a = 0.5*df;
  b = 0.5;
  p = 0.5 * gsl_sf_beta_inc(a,b,x);

  /* p to z */
  z = gsl_cdf_ugaussian_Qinv(p);
  return z;
}

void avevar(double *data,int n,double *a,double *v)
{
  int j;
  double ave,var,nx,s,u;

  nx = (double)n;
  ave = 0;
  for (j=0; j<n; j++) ave += data[j];
  ave /= nx;

  var = u = 0;
  for (j=0; j<n; j++) {
    s = data[j]-ave;
    u   += s;
    var += s*s;
  }
  var=(var-u*u/nx)/(nx-1);

  *v = var;
  *a = ave;
}

/* welch test, unequal variance */
double welchtest(double *data1,double *data2,int n1,int n2)
{
  double tiny=TINY;
  double ave1,ave2,var1,var2;
  double z=0,t=0,df=0;
  double nx1 = (double)n1;
  double nx2 = (double)n2;

  avevar(data1,n1,&ave1,&var1);
  if (var1 < tiny) return 0;
  avevar(data2,n2,&ave2,&var2);
  if (var2 < tiny) return 0;
  t = (ave1 - ave2)/sqrt(var1/nx1 + var2/nx2);
  df = SQR(var1/nx1+var2/nx2)/(SQR(var1/nx1)/(nx1-1)+SQR(var2/nx2)/(nx2-1));
  z  = t2z((double) t,(double) df);
  if (t < 0) z = -z;
  return z;
}

/* paired twosample test */
double paired_ttest(double *data1,double *data2,int n)
{
  int j;
  double ave,var,tiny=TINY;
  for (j=0; j<n; j++) {
    data1[j] -= data2[j];
  }
  avevar(data1,n,&ave,&var);
  if (var < tiny) return 0;
  double nx = (double)n;
  double df = nx-1.0;
  double t = sqrt(nx) * ave/sqrt(var);
  double z = t2z(t,df);
  if (t < 0) z = -z;
  return z;
}

/* paired twosample test */
double xtest2(double *data1,double *data2,int n)
{
  int j;
  double nx,ave1,ave2,var1,var2,sd,u,df,cov;
  double tiny=TINY;
  double t=0,z=0;

  avevar(data1,n,&ave1,&var1);
  avevar(data2,n,&ave2,&var2);
  if (var1 < tiny || var2 < tiny) return 0;

  nx = (double)n;
  df = nx-1;
  cov = 0;
  for (j=0; j<n; j++)
    cov += (data1[j]-ave1)*(data2[j]-ave2);
  cov /= df;

  t = 0;
  u = (var1+var2-2.0*cov);
  if (u < tiny) return 0;
  sd = sqrt(u/nx);
  if (sd < tiny) return 0;
  t = (double)(ave1-ave2)/sd;
  z = t2z(t,df);
  if (t < 0) z = -z;
  return z;
}

/* twosample t-test, pooled variance  */
double ttest2(double *data1,double *data2,int n1,int n2)
{
  int j;
  double nx1,nx2,ave1,ave2,sd,s1,s2,df;
  double tiny=TINY;
  double t=0,z=0;

  nx1 = (double)n1;
  nx2 = (double)n2;

  ave1 = ave2 = 0;
  for (j=0; j<n1; j++) ave1 += data1[j];
  for (j=0; j<n2; j++) ave2 += data2[j];
  if (ABS(ave1) < tiny) return 0;
  if (ABS(ave2) < tiny) return 0;
  ave1 /= nx1;
  ave2 /= nx2;

  s1 = 0;
  for (j=0; j<n1; j++) s1 += SQR(data1[j]-ave1);

  s2 = 0;
  for (j=0; j<n2; j++) s2 += SQR(data2[j]-ave2);

  df = nx1 + nx2 - 2.0;
  sd = sqrt((1.0/nx1 + 1.0/nx2) * (s1+s2)/df);
  if (sd < tiny) return 0;

  t = (double)(ave1-ave2)/sd;
  z = t2z(t,df);
  if (t < 0) z = -z;
  return z;
}

/* onesample t-test */
double ttest1(double *data,int n)
{
  double ave=0,var=0,tiny=TINY;
  avevar(data,n,&ave,&var);
  if (var < tiny) return 0;
  double nx = (double)n;
  double df = nx-1.0;
  double t  = sqrt(nx) * ave/sqrt(var);
  double z  = t2z(t,df);
  if (t < 0) z = -z;
  return z;
}


#include <stdio.h>
#include <time.h>
#include <cblas.h>// libatlas-dev (3.10.2-7)

#include "include/Matrix.h"
#include "include/HelpFunctions.h"

// file output
char* RESULT_FOLDER_PATH = "results/";
struct stat ST = {0};

int main(int argc, const char **argv) {

    // VARIABLES

    clock_t start, end; // measure time
    bool calc_correct[3] = {false, false, false}; // correct calculations? (cache, avx, blas)
    double overall_times[4] = {0,0,0,0}; // save different overall times in array

    // arguments from command line, a.out = argv[0]
    unsigned int N = (unsigned int) atoi(argv[1]); // dimension (N x N)
    unsigned int BS = (unsigned int) atoi(argv[2]); // block size
    int REPETITIONS = atoi(argv[3]);

    printf("cmd args: %d %d %d\n",N,BS,REPETITIONS);

    // INIT.

    float* m1_f = createRandomizedMatrix_f(N);
    float* m2_f = createRandomizedMatrix_f(N);
    float* stdAlgorithm_F = calloc(N * N, sizeof (float));

    // TEST FOR DIMENSIONS

    if (N % AVX_VECTOR_SIZE != 0 || N < AVX_VECTOR_SIZE) {
        printf("> ERROR! dimension N (%d) has to be: N mod %d == 0\n",N,AVX_VECTOR_SIZE);
        return EXIT_FAILURE;
    }
    else if (BS % 2 != 0 || BS < 2 || BS > N) {
        printf("> ERROR! block size BS (%d) has to be: BS mod 2 == 0\n && <= N",BS);
        return EXIT_FAILURE;
    }
    else if (REPETITIONS <= 0) {
        printf("> ERROR! REPETITIONS (%d) has to be > 0 \n",REPETITIONS);
        return EXIT_FAILURE;
    }
    printHeader(N, BS, REPETITIONS);

    // === CALCULATIONS ==================================================================

    // STANDARD ALGORITHM

    printf("executing standard algorithm multiplications …\n");

    for (int i = 0; i < REPETITIONS; ++i) {
        float* result_f = calloc(N * N, sizeof (float));

        // one multiplication
        start = clock();
        standardMatrixMul_f(m1_f, m2_f, result_f, N);
        end = clock();

        // copy values from other matrix (later: comparison)
        if (i == 0) {
            for (unsigned int j = 0; j < N; ++j) {
                for (unsigned int k = 0; k < N; ++k) {
                    stdAlgorithm_F[(N * j) + k] = result_f[(N * j) + k];
                }
            }
        }

        overall_times[0] += ((double) (end - start)) / CLOCKS_PER_SEC;

        free(result_f);
    }

    // CACHE OPTIMIZED

    printf("executing cache optimized multiplications …\n");

    for (int i = 0; i < REPETITIONS; ++i) {
        float* result_cache_f = calloc(N * N, sizeof (float));

        start = clock();
        optimizedMatrixMul_f(m1_f, m2_f, result_cache_f, N, BS);
        end = clock();

        overall_times[1] += ((double) (end - start)) / CLOCKS_PER_SEC;
        calc_correct[0] = compareResultMatrices_f(stdAlgorithm_F, result_cache_f, N);

        free(result_cache_f);
    }

    // PARALLEL (AVX)

    printf("executing parallel multiplications …\n");

    for (int i = 0; i < REPETITIONS; ++i) {
        float* result_para_f = calloc(N * N, sizeof (float));

        start = clock();
        parallelMatrixMul_AVX_f(m1_f, m2_f, result_para_f, N);
        end = clock();

        overall_times[2] += ((double) (end - start)) / CLOCKS_PER_SEC;
        calc_correct[1] = compareResultMatrices_f(stdAlgorithm_F, result_para_f, N);

        free(result_para_f);
    }

    // BLAS (ATLAS)

    printf("executing multiplications using BLAS …\n");
    float ALPHA = 1;
    float BETA = 0;

    // matrix dimensions
    int LDA=N;
    int LDB=N;
    int LDC=N;

    // Single Precision, General Matrix Multiplication
    for (int i = 0; i < REPETITIONS; ++i) {
        float* result_cblas_f = calloc(N * N, sizeof (float));

        start = clock();
        cblas_sgemm(CblasRowMajor,CblasNoTrans,CblasNoTrans,N,N,N,ALPHA,
                    m1_f,LDA,
                    m2_f,LDB,BETA,
                    result_cblas_f,LDC);
        end = clock();

        overall_times[3] += ((double) (end - start)) / CLOCKS_PER_SEC;
        calc_correct[2] = compareResultMatrices_f(stdAlgorithm_F, result_cblas_f, N);

        free(result_cblas_f);
    }

    // ===== cleanup allocated memory ===================================

    free(m1_f);
    free(m2_f);
    free(stdAlgorithm_F);

    // === FILE OUTPUT ===================================================================

    createResultFolder(RESULT_FOLDER_PATH, ST);
    char* filename =
            createResultFile(RESULT_FOLDER_PATH, N, BS, REPETITIONS, overall_times, calc_correct);
    printf("\nwriting results to %s …\n", filename);
    printf("done.\n");

    free(filename);

    return 0;
}


#ifndef BIO_MATH_H_
#define BIO_MATH_H_

#include "bio/defs.h"
#include "bio/gsl.h"

#include <boost/io/ios_state.hpp>

#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_sf_log.h>
#include <gsl/gsl_sf_exp.h>

#include <numeric>

BIO_NS_START

/** Calculates the log of n choose the observed counts.  */
template < typename ObsIt >
double
calc_ln_n_choose(
	ObsIt obs_begin,
	ObsIt obs_end)
{
	double result = 0.0;

	unsigned n = 0;
	while (obs_end != obs_begin)
	{
		result -= gsl_sf_lnfact(*obs_begin);
		n += *obs_begin;

		++obs_begin;
	}

	result += gsl_sf_lnfact(n);

	return result;
}

/** Calculates a factor involved in the likelihood of a multinomial distribution with a dirichlet prior. */
template < typename ObsIt, typename AlphaIt >
double
calc_ln_gamma_factor(
	ObsIt obs_begin,
	ObsIt obs_end,
	AlphaIt alpha_begin)
{
	double ln_pi_gamma_alpha_obs = 0.0; //work in logarithms
	double ln_pi_gamma_alpha = 0.0; //work in logarithms
	double sum_alpha = 0.0;
	double sum_obs = 0.0;
	while (obs_end != obs_begin)
	{
		sum_alpha += *alpha_begin;
		sum_obs += *obs_begin;
		ln_pi_gamma_alpha += gsl_sf_lngamma(*alpha_begin);
		ln_pi_gamma_alpha_obs += gsl_sf_lngamma(*alpha_begin + *obs_begin);

		++obs_begin;
		++alpha_begin;
	}

	double ln_result =
		gsl_sf_lngamma(sum_alpha)
		- ln_pi_gamma_alpha
		+ ln_pi_gamma_alpha_obs
		- gsl_sf_lngamma(sum_alpha + sum_obs);

	return ln_result;
}

/** Calculates the log of the likelihood of seeing the observed counts under a dirichlet prior. */
template < typename ObsIt, typename AlphaIt >
double
calc_multinomial_ln_likelihood_dirichlet_prior(
	ObsIt obs_begin,
	ObsIt obs_end,
	AlphaIt alpha_begin)
{
	return calc_ln_gamma_factor(obs_begin, obs_end, alpha_begin) + calc_ln_n_choose(obs_begin, obs_end);
}

/** Calculates the log of the likelihood of seeing the observed counts under a uniform multinomial distribution. */
template < typename ObsIt >
double
calc_multinomial_ln_likelihood_uniform_dist(
	ObsIt obs_begin,
	ObsIt obs_end)
{
	const unsigned n = std::accumulate(obs_begin, obs_end, unsigned(0));
	if (0 == n)
	{
		return 0.0;
	}

	const double ln_n_choose = calc_ln_n_choose(obs_begin, obs_end);
	return ln_n_choose - n * gsl_sf_log(double(obs_end - obs_begin));
}

/** Calculates the log of the likelihood of seeing the observed counts under a multinomial distribution with given p's. */
template < typename ObsIt, typename PIt >
double
calc_multinomial_ln_likelihood(
	ObsIt obs_begin,
	ObsIt obs_end,
	PIt p_begin)
{
	const unsigned n = std::accumulate(obs_begin, obs_end, unsigned(0));
	if (0 == n)
	{
		return 0.0;
	}

	double result = calc_ln_n_choose(obs_begin, obs_end);
	while (obs_end != obs_begin)
	{
		result += *obs_begin * gsl_sf_log(*p_begin);

		++obs_begin;
		++p_begin;
	}

	return result;
}

/** Calculates the ratio of evidence in favour of a uniform distribution over one with a dirichlet prior
defined by the alphas. */
template < typename ObsIt, typename AlphaIt >
double
calc_ln_multinomial_uniform_evidence(
	ObsIt obs_begin,
	ObsIt obs_end,
	AlphaIt alpha_begin)
{
	const double ln_likelihood_uniform_dist = calc_multinomial_ln_likelihood_uniform_dist(obs_begin, obs_end);
	const double ln_likelihood_dirichlet_prior = calc_multinomial_ln_likelihood_dirichlet_prior(obs_begin, obs_end, alpha_begin);
	return ln_likelihood_uniform_dist - ln_likelihood_dirichlet_prior;
}

/** Calculates the ratio of evidence in favour of a particular multinomial distribution (defined by the p's)
over one with a dirichlet prior defined by the alpha's. */
template < typename ObsIt, typename AlphaIt, typename PIt >
double
calc_ln_multinomial_evidence(
	ObsIt obs_begin,
	ObsIt obs_end,
	AlphaIt alpha_begin,
	PIt p_begin)
{
	return
		calc_multinomial_ln_likelihood(obs_begin, obs_end, p_begin)
		- calc_multinomial_ln_likelihood_dirichlet_prior(obs_begin, obs_end, alpha_begin);
}

BIO_NS_END

#endif //BIO_MATH_H_


/* randist/weibull.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 James Theiler, 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.
 */

#include <config.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

/* The Weibull distribution has the form,

   p(x) dx = (b/a) (x/a)^(b-1) exp(-(x/a)^b) dx

 */

double
gsl_ran_weibull (const gsl_rng * r, const double a, const double b)
{
  double x = gsl_rng_uniform_pos (r);

  double z = pow (-log (x), 1 / b);

  return a * z;
}

double
gsl_ran_weibull_pdf (const double x, const double a, const double b)
{
  if (x < 0)
    {
      return 0 ;
    }
  else if (x == 0)
    {
      if (b == 1)
        return 1/a ;
      else
        return 0 ;
    }
  else if (b == 1)
    {
      return exp(-x/a)/a ;
    }
  else
    {
      double p = (b/a) * exp (-pow (x/a, b) + (b - 1) * log (x/a));
      return p;
    }
}


#include <stdio.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_bessel.h>

int
main (void)
{
  double x = 5.0;
  gsl_sf_result result;

  double expected = -0.17759677131433830434739701;

  int status = gsl_sf_bessel_J0_e (x, &result);

  printf ("status  = %s\n", gsl_strerror(status));
  printf ("J0(5.0) = %.18f\n"
          "      +/- % .18f\n",
          result.val, result.err);
  printf ("exact   = %.18f\n", expected);
  return status;
}


#include <stdio.h>
#include <gsl/gsl_poly.h>

int main() {
  double a = 1.0, b = -3.0, c = 2.0;
  double x0 = 0.0, x1 = 0.0;
  gsl_poly_solve_quadratic(a, b, c, &x0, &x1);
  printf("%4.1fx^2 + %4.1fx + %4.1f = 0 -> x = %4.1f, %4.1f\n",
      a, b, c, x0, x1);
  return 0;
}


/* randist/multinomial.c
 *
 * Copyright (C) 2002 Gavin E. Crooks <gec@compbio.berkeley.edu>
 *
 * 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 2 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.
 */

#include <config.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_sf_gamma.h>

/* The multinomial distribution has the form

                                      N!           n_1  n_2      n_K
   prob(n_1, n_2, ... n_K) = -------------------- p_1  p_2  ... p_K
                             (n_1! n_2! ... n_K!)

   where n_1, n_2, ... n_K are nonnegative integers, sum_{k=1,K} n_k = N,
   and p = (p_1, p_2, ..., p_K) is a probability distribution.

   Random variates are generated using the conditional binomial method.
   This scales well with N and does not require a setup step.

   Ref:
   C.S. David, The computer generation of multinomial random variates,
   Comp. Stat. Data Anal. 16 (1993) 205-217
*/

void
gsl_ran_multinomial (const gsl_rng * r, const size_t K,
                     const unsigned int N, const double p[], unsigned int n[])
{
  size_t k;
  double norm = 0.0;
  double sum_p = 0.0;

  unsigned int sum_n = 0;

  /* p[k] may contain non-negative weights that do not sum to 1.0.
   * Even a probability distribution will not exactly sum to 1.0
   * due to rounding errors.
   */

  for (k = 0; k < K; k++)
    {
      norm += p[k];
    }

  for (k = 0; k < K; k++)
    {
      if (p[k] > 0.0)
        {
          n[k] = gsl_ran_binomial (r, p[k] / (norm - sum_p), N - sum_n);
        }
      else
        {
          n[k] = 0;
        }

      sum_p += p[k];
      sum_n += n[k];
    }

}

double
gsl_ran_multinomial_pdf (const size_t K,
                         const double p[], const unsigned int n[])
{
  return exp (gsl_ran_multinomial_lnpdf (K, p, n));
}

double
gsl_ran_multinomial_lnpdf (const size_t K,
                           const double p[], const unsigned int n[])
{
  size_t k;
  unsigned int N = 0;
  double log_pdf = 0.0;
  double norm = 0.0;

  for (k = 0; k < K; k++)
    {
      N += n[k];
    }

  for (k = 0; k < K; k++)
    {
      norm += p[k];
    }

  log_pdf = gsl_sf_lnfact (N);

  for (k = 0; k < K; k++)
    {
      log_pdf -= gsl_sf_lnfact (n[k]);
    }

  for (k = 0; k < K; k++)
    {
      log_pdf += log (p[k] / norm) * n[k];
    }

  return log_pdf;
}


#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_sort.h>

#include "cosmocalc.h"

gsl_spline *cosmocalc_aexpn2age_spline = NULL;
gsl_interp_accel *cosmocalc_aexpn2age_acc = NULL;
double AGE = 0;

/* function for integration using gsl integration */
static double age_integ_funct(double a, void *p)
{
  return 1.0/a/hubble_noscale(a);
}

/* init function  - some help from Gadget-2 applied here */
void init_cosmocalc_age_table(void)
{
#define WORKSPACE_NUM 100000
#define ABSERR 0.0
#define RELERR 1e-8

  static int initFlag = 1;
  static int currCosmoNum;

  gsl_integration_workspace *workspace;
  gsl_function F;
  long i;
  double result,abserr,afact;
  double age_table[COSMOCALC_AGE_TABLE_LENGTH];
  double aexpn_table[COSMOCALC_AGE_TABLE_LENGTH];

  if(initFlag == 1 || currCosmoNum != cosmoData.cosmoNum)
    {
      initFlag = 0;
      currCosmoNum = cosmoData.cosmoNum;

      workspace = gsl_integration_workspace_alloc((size_t) WORKSPACE_NUM);

      aexpn_table[0] = 0.0;
      age_table[0] = 0.0;
      for(i=1;i<COSMOCALC_AGE_TABLE_LENGTH;++i)
	{
	  afact = (1.0 - 0.0)/(COSMOCALC_AGE_TABLE_LENGTH-1.0)*((double) i);
	  F.function = &age_integ_funct;
	  gsl_integration_qag(&F,1e-12,afact,ABSERR,RELERR,(size_t) WORKSPACE_NUM,GSL_INTEG_GAUSS51,workspace,&result,&abserr);
	  aexpn_table[i] = afact;
	  age_table[i] = result*TH/cosmoData.h;
	}

      gsl_integration_workspace_free(workspace);
#undef ABSERR
#undef RELERR
#undef WORKSPACE_NUM

      //init the spline and accelerators
      if(cosmocalc_aexpn2age_spline != NULL)
	gsl_spline_free(cosmocalc_aexpn2age_spline);
      cosmocalc_aexpn2age_spline = gsl_spline_alloc(GSL_SPLINE_TYPE,(size_t) (COSMOCALC_AGE_TABLE_LENGTH));
      gsl_spline_init(cosmocalc_aexpn2age_spline,aexpn_table,age_table,(size_t) (COSMOCALC_AGE_TABLE_LENGTH));
      if(cosmocalc_aexpn2age_acc != NULL)
	gsl_interp_accel_reset(cosmocalc_aexpn2age_acc);
      else
	cosmocalc_aexpn2age_acc = gsl_interp_accel_alloc();

      AGE = gsl_spline_eval(cosmocalc_aexpn2age_spline,1.0,cosmocalc_aexpn2age_acc);
    }
}

double age(double a)
{
  static int initFlag = 1;
  static int currCosmoNum;

  if(initFlag == 1 || currCosmoNum != cosmoData.cosmoNum)
    {
      initFlag = 0;
      currCosmoNum = cosmoData.cosmoNum;
      init_cosmocalc_age_table();
    }

  return gsl_spline_eval(cosmocalc_aexpn2age_spline,a,cosmocalc_aexpn2age_acc);
}

//call age first to set AGE
double lookback(double a)
{
  double age_a = age(a);
  return AGE - age_a;
}


#include <stdio.h>
#include <gsl/gsl_statistics.h>
#include <stdlib.h>
#include <float.h>

#ifndef N
#define N 32
#endif

#define K N/2

#include <klee/klee.h>
int
main(void)
{
  double data[K], w[K];
  float input[K];

  // random inputs
  int i;
  for (i = 0; i < K; i++){
    if (i<2) w[i] = 1; else w[i] = 2*w[i-1];
  }

  // symbolic inputs
  klee_make_symbolic(&input, sizeof(input), "input");

  for (i = 0; i < K; i++){
    data[i] = input[i]  / FLT_MAX * 100;
  }

  double wmean;

  wmean     = gsl_stats_wmean(w, 1, data, 1, K);

  printf ("The sample mean is %g\n", wmean);
  return 0;
}


#pragma once

#include <gsl/gsl_sf_zeta.h>
#include <cmath>

double real_trigamma_cmplxarg(double x, double y, int m, int l){
    //compute the real part of the trigamma function at z = x + iy using
    //my accelerated series formula
    //argument should have positive real part (x > 0)
    //m gives the number of zeta function terms to use
    //l gives the number of terms to use in the residual series
    double result = 0;

    //if x is small, the result will be large and thus, inaccurate.  Use the
    //polygamma shift formula to give a larger value of x for the computation
    if(x < 1000){
        return ((x*x - y*y)/(x*x + y*y)/(x*x + y*y)) + real_trigamma_cmplxarg(x + 1, y, m, l);
    }
    else{
        double phase, ypow = 1.0, xpow, denom;
        //compute finite sum of Hurwitz zeta functions
        for (int i = 1; i < m; ++i){
            phase = 2*(i/2) == i ? -1.0 : 1.0;
            result += phase*(2*i - 1)*ypow*gsl_sf_hzeta(2*i, x);
            ypow = ypow*y*y;
        }
        //compute the infinite sum of residuals
        phase = 2*(m/2) == m ? 1.0 : -1.0;
        for (int n = 0; n < l; ++n){
            xpow = pow(x + n, 2*m);
            denom = ((x + n)*(x + n) + y*y)*((x + n)*(x + n) + y*y);
            result += phase*ypow*((2*m - 1)*y*y + (2*m + 1)*(x + n)*(x + n)) / xpow / denom;
        }
        return result;
    }
}