/* popGenTools.h
/
/
/ Andrew Kern
/
/
*/
#ifndef PGTOOLS_INC
#define PGTOOLS_INC

//#define MAXSNPS 1000000
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_min.h>
#include "mpi.h"

struct my_F_params{
	int i, n, ascSize, ascFreq, derivedFlag;
	gsl_integration_workspace * w;
};

struct trans_params{
	int xi, i, n, ascSize, ascFreq, derivedFlag;
	double t, q, p, tau,v;
	gsl_integration_workspace * w;
};

struct prf_params{
	int xi, i, n, ascSize, ascFreq, derivedFlag;
};

struct my_snpProb_params{
  int i, n, ascSize;
  double denom;
	gsl_integration_workspace * w;
};

struct my_f_params{
  int i,n, ascSize;
  double beta;
};

struct prfLik_params{
  int snpNumber, maxSampleSize, ascSize, runMode;
  gsl_vector *weights, *sampleSizeVector, *betas;
  struct snp *data;
  gsl_matrix *sfsCounts;
};

struct my_lik_params{
  int snpNumber, maxSampleSize, ascSize, runMode;
  gsl_vector *weights, *sampleSizeVector, *betas;
  gsl_integration_workspace * w;
  struct snp *data;
  gsl_matrix *sfsBools;
  gsl_min_fminimizer *min;

};
struct my_likCI_params{
  int snpNumber, maxSampleSize;
  gsl_vector *weights, *sampleSizeVector;
  struct snp *data;
  gsl_matrix *sfsBools;
  double lMax, logUnits, beta_hat;
};

struct my_jointLik_params{
  int snpNumber, maxSampleSize, ascSize, nstates;
  gsl_vector *weights, *sampleSizeVector, *betas, *startingPoint;
  struct snp *data;
  gsl_matrix *sfsBools, *posts;

};

double my_f(double q, void * params);
double my_F(double beta, void * params);
double my_F2(double beta, void * p);
double my_FAdapt(double beta, void * p);
double my_lik(double beta, void * params);
double bustProb(double beta, double theta, void *p);
double sfsLikBetaVector(gsl_vector *beta, void * p);
double prfLik(double beta, void * p);
double weightedLik(double beta, void * params);
double weightedLikLook(double beta, void * params);
double weightedLikLookCI(double beta, void * params);
double likWrap(double beta);
double snpProb(double beta, void * params);
double snpProbDenomLookup(double beta, void * params);
double snpProbDenom(double beta, int sampleSize);
gsl_vector *makeSnpProbDenomVector(double beta, int maxSampleSize, gsl_vector *sampleSizeVec);
double ml_est(double * lik_beta_hat);
double ml_prf(double * lik_beta_hat, void * p);
double weighted_ml_est(double * lik_beta_hat, void * p);
double weighted_ml_est_lookup(double * lik_beta_hat, void * p);
double weighted_ml_est_lookup_errReport(double * lik_beta_hat, void * p);
double weighted_ml_CILower_lookup(double * lik_beta_hat, void * p);
double weighted_ml_CIUpper_lookup(double * lik_beta_hat, void * p);
double wlikWrap(double beta, void * p);
double  wlikWrapLook(double beta, void * p);
double  wlikCIWrapLook(double beta, void * p);
double  prfLikWrap(double beta, void * p);
gsl_vector *sampleSizeVector(struct snp *data, int snpNumber, int maxSampleSize);
void sampleSizeVectorFill(struct snp *data, int snpNumber, int maxSampleSize, gsl_vector *bools);
gsl_matrix *summarizeSFS(int maxSampleSize, struct snp *data, int snpNumber);
gsl_matrix *summarizeSFSBool(int max, struct snp *data, int snpNumber);
void summarizeSFSBoolFill(gsl_matrix *bool, struct snp *data, int snpNumber);
void summarizeSFSFill(gsl_matrix *counts, struct snp *data, int snpNumber);
int summarizeSFSFillFromTo(gsl_matrix *counts, struct snp *data, int snpNumber, int from, int to, int startPos);
gsl_matrix *snpProbMatrix(double beta,  int maxSampleSize, gsl_vector *sampleSizeVector, gsl_matrix *sfsBools);
gsl_matrix *snpProbMatrixNotLog(double beta,  int maxSampleSize, gsl_vector *sampleSizeVector, gsl_matrix *sfsBools);
gsl_matrix *snpProbMatrixNotLogFull(double beta,  int maxSampleSize,gsl_vector *sampleSizeVector);
gsl_vector *snpProbVectorNotLog(double beta,  int sampleSize);
int simulateSiteFreq(int sampleSize, double alpha, void *r);
double simpleAscertain(double sampleFreq, void *p);
double probAscertainmentGivenModel(double beta, void *p);
gsl_matrix *snpAscMatrix(int maxSampleSize, gsl_vector *sampleSizeVector, gsl_matrix *sfsBools, int ascSize);
gsl_matrix *estimateAscSFS(int maxSampleSize, gsl_vector *sampleSizeVector,  gsl_matrix *sfsBools, int ascSize, gsl_matrix *sfsSummary);
double weightedLikLookAsc(double beta, void * p);
double  wlikWrapLookAsc(double beta, void * p);
double weighted_ml_est_lookup_asc(double * lik_beta_hat, void * p);
double probAscertainmentGivenModelLookup(double beta, int sampSize, gsl_matrix *snpProbs, gsl_matrix *ascProbs);
double probAscertainmentGivenModelHemiLookup(double beta, int sampSize, gsl_vector *snpProbs, gsl_matrix *ascProbs);
double outgroupAscertain(double sampleFreq, void * p);
gsl_matrix *snpOutgroupAscMatrix(int maxSampleSize, gsl_vector *sampleSizeVector, int ascSize, int derived, int runMode);
double weightedLikLookOutgroupAsc(double beta, void * p);
double  wlikWrapLookOutgroupAsc(double beta, void * p);
double weighted_ml_est_lookup_outgroup_asc(double * lik_beta_hat, void * p);
double probOutgroupAscertainmentGivenModel(double beta, void *p);
double sfsLikBetaVectorOutgroupAsc(gsl_vector *betas, void * p);
double sfsLikBetaVectorOutgroupAscPre(gsl_vector *betas, gsl_matrix *ascProbsAncestral,gsl_matrix *ascProbsDerived, gsl_vector *snpProbs, void * p);
//pre-alloced versions
gsl_matrix *snpOutgroupAscMatrixPre(int maxSampleSize, gsl_vector *sampleSizeVector, int ascSize, int derived, \
	int runMode, gsl_matrix *probs);
gsl_vector *snpProbVectorNotLogPre(double beta,  int sampleSize, gsl_vector *probs);
//MPI stuff
double sfsLikBetaVectorMPI(gsl_vector *betas, void * p, int myrank, int mprocs, MPI_Comm comm);
double sfsLikBetaVectorOutgroupAscPreMPI(gsl_vector *betas, gsl_matrix *ascProbsAncestral,gsl_matrix *ascProbsDerived, \
	gsl_vector *snpProbs, void * p,int myrank, int mprocs, MPI_Comm comm);

double weightedLikLookFolded(double beta, void * p);
double weighted_ml_est_lookup_folded(double * lik_beta_hat, void * p);
double weighted_ml_CILower_lookupFolded(double * lik_beta_hat, void * p);
double weighted_ml_CIUpper_lookupFolded(double * lik_beta_hat, void * p);
double  wlikWrapLookFolded(double beta, void * p);

int segSites(struct snp *data, int sites);
double theta_pi(struct snp *data, int sites);
double a1f(int n);
double theta_w(int segSites, int n);
double a2f(int);
double b1f(int);
double b2f(int);
double c1f(double, double);
double c2f(int, double, double, double);
double e1f(double, double);
double e2f(double, double, double);
double tajdSNP(int, int, double) ;

#endif


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

#include <cblas.h>

#include "LibShalom.h"

static double gtod_ref_time_sec = 0.0;

double dclock()
{
        double the_time, norm_sec;
        struct timeval tv;

        gettimeofday( &tv, NULL );

        if ( gtod_ref_time_sec == 0.0 )
                gtod_ref_time_sec = ( double ) tv.tv_sec;

        norm_sec = ( double ) tv.tv_sec - gtod_ref_time_sec;

        the_time = norm_sec + tv.tv_usec * 1.0e-6;

        return the_time;
}

void random_matrix( int m, int n, float *a)
{
  double drand48();
  int i,j;

  for ( i=0; i<m; i++ )
    for ( j=0; j<n; j++ )
		a[i*n+j]= 2.0 * (float)drand48( ) - 1.0 ;
}

void random_matrix1( int m, int n, float *a)
{
  double drand48();
  int i,j;

  for ( i=0; i<m; i++ )
    for ( j=0; j<n; j++ )
		a[i*n+j]= 1.0 ;
}

void transpose( int m, int n, float *a)
{
	float *temp_a = ( float * ) malloc( m* n * sizeof( float ) );

	int i, j;

	for( i =0 ;i< m; i++)
	{
		for( j= 0;j < n; j++)
		{
			temp_a[j * m + i] = a[i*n + j];
		}
	}

	for( i =0 ; i< n; i++)
	{
		for( j =0; j< m; j++)
		{
			a[ i * m+j] = temp_a[i*m+j];
		}
	}
}

int main()
{

	LibShalom_set_thread_nums(64);
	int i,j,k;
	int loop= 5 ;
	long M, N, K;
	float *C1;
	double start, cost;
	double gflops;
	int flag =0 ;
	char transa = 'N', transb = 'N';
	int pc;

	FILE *fp;

  	if( (fp=fopen("NSMM_small.txt","w")) == NULL )
  	{
    	puts("Fail to open file!");
    	exit(0);
  	}

    for( j =8; j < 9; j = j + 8)
    {

        M = 256 ;
        N = 4096;
        K = 5000;
        //M= N = K = j;
    	double ops = M *N *K * 1.0e-09 * 2;
    	fprintf(fp, "%d %d %d", M,N,K);

    	for(pc =0 ;pc < 1; pc++)
    	{
    		int flag =0 ;
    		int lda = K;
			int ldb = K;
			int ldc = N;

	    	float *A = ( float * ) malloc( K* M * sizeof( float ) );
	    	float *B = ( float * ) malloc( K* N * sizeof( float ) );
	    	float *C = ( float * ) malloc( M* N * sizeof( float ) );
	    	float *C1 = ( float * ) malloc( M* N * sizeof( float ) );
	    	float *SB= ( float * ) malloc( K* N * sizeof( float ) );

	    	random_matrix(M,K,A);
	    	random_matrix(K,N,B);

	    	transpose(K, N, B);

	    	for( i =0 ;i< 2; i++)
			cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, M, N, K, 1.0, A, lda, B, ldb, 0.0, C1, ldc);

			start = dclock();
		    for(i= 0; i<loop ;i++)
		    {

		     	cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, M, N, K, 1.0, A, lda, B, ldb, 0.0, C1, ldc);
		    }

		    for( i =0 ;i< 5; i++)
		    	LibShalom_sgemm_mp(NoTrans, Trans, C, A, B, M, N, K);

			start = dclock();
			for( i= 0; i< loop ;i++)
				LibShalom_sgemm_mp(NoTrans, Trans, C, A, B, M, N, K);
			cost =(dclock()-start)/loop;

			ops = M * N  * K * 1.0e-09 * 2;
			printf("N_SMM:  M= %d N=%d K=%d flops = %lf effic= %.3lf %\n", M, N, K,
					ops/cost, ops/cost/17.6 * 100/ 64);
			fprintf(fp, " %.3f", ops/cost);

		for( i= 0; i< M; i++)
		{
			for( j= 0 ;j<N;j++)
			{
				if((C[i*N+j]- C1[i*N+j] > 0.0001 || C[i*N+j]- C1[i*N+j] < -0.0001) )
				{
					printf("i = %d, j= %d\n",i ,j );
					printf("C= %lf , C1= %lf\n", C[i*N+j], C1[i*N+j]);
					flag =1;
					printf("error\n");
					break;
				}
			}
			if(flag ==1)
				break;
		}

		if(flag == 0)
			printf("结果正确\n");

		    free(A);
		    free(B);
		    free(C);
		    free(SB);
		}

		fprintf(fp, "\n");
	}

	fclose(fp);
    return 0;
}

/*

int main()
{

	LibShalom_thread_nums(32);
//	printf("%d\n", vec.size());
	int i,j,k;
	int loop= 100 ;
	long M, N, K;
	float *C1;
	double start, cost;
	double gflops;
	int flag =0 ;
	char transa = 'N', transb = 'N';
	int pc;

	FILE *fp;

	long Me[5]={5,13,13,23,26},
		Ne[5]={5,5,13,23,26},
		Ke[5]={5,13,13,23,13};

  	if( (fp=fopen("NSMM_small.txt","w")) == NULL )
  	{
    	puts("Fail to open file!");
    	exit(0);
  	}

    for( j = 0; j < 5; j++)
    {

        M = Me[j];
        N = Ne[j];
        K = Ke[j];
        //M= N = K = j;
    	double ops = M *N *K * 1.0e-09 * 2;
    	fprintf(fp, "%d %d %d", M,N,K);

    	for(pc =0 ;pc < 5; pc++)
    	{
	    	double *A = ( double * ) malloc( K* M * sizeof( double ) );
	    	double *B = ( double * ) malloc( K* N * sizeof( double ) );
	    	double *C = ( double * ) malloc( M* N * sizeof( double ) );
	    	double *SB= ( double * ) malloc( K* N * sizeof( double ) );

		    for( i =0 ;i< 5; i++)
		    	LibShalom_dgemm(NoTrans, NoTrans, C, A, B, M, N, K);

			start = dclock();
			for( i= 0; i< loop ;i++)
				LibShalom_dgemm(NoTrans, NoTrans, C, A, B, M, N, K);
			cost =(dclock()-start)/loop;

			ops = M * N  * K * 1.0e-09 * 2;
			printf("N_SMM:  M= %d N=%d K=%d flops = %lf effic= %.3lf %\n", M, N, K,
					ops/cost, ops/cost/8.8 * 100);
			fprintf(fp, " %.3f", ops/cost);

		    free(A);
		    free(B);
		    free(C);
		    free(SB);
		}

		fprintf(fp, "\n");
	}

	fclose(fp);
    return 0;
}
*/


#ifndef HYPER_CONST
#define HYPER_CONST

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_blas.h>

class Hyper_Const
{
public :
    static const int dim0 = 120;           // max_clause
    static const int dim1 = 20;            // max_var
    static const int dim2 = 2;             // nc
    static const int nact = 40;            // nact
    static const float c_act;     	       // c_act is a hyperparameter for MCTS (decide the level of exploration)

    static const gsl_rng *r;              // random generater
    static const double alpha[nact];      // alpha parameter
    static void generate_dirichlet(double*);     // function used to generate dirichlet noise
    static const int MCTS_size_lim; // the size of MCT we want to achieve.
};

#endif


#ifdef __cplusplus
extern "C" {
#endif

#ifndef GSL_GAUSS
#define GLS_GAUSS

#include <math.h>
#include <gsl_math.h>
#include <gsl_cdf.h>

double gsl_cdf_ugaussian_P (const double x);
double gsl_cdf_ugaussian_Q (const double x);
double gsl_cdf_gaussian_P (const double x, const double sigma);
double gsl_cdf_gaussian_Q (const double x, const double sigma);

#endif // GLS_GAUSS

#ifdef __cplusplus
}
#endif


/**
 * \author Sylvain Marsat, University of Maryland - NASA GSFC
 *
 * \brief C header for functions windowing and computing FFT/IFFT of time/frequency series.
 *
 */

#ifndef __GENERATETDIFD_H__
#define __GENERATETDIFD_H__ 1

#ifdef __GNUC__
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif

#define _XOPEN_SOURCE 500

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <complex.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_bspline.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_min.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_complex.h>

#include "constants.h"
#include "struct.h"
#include "waveform.h"
#include "fft.h"
#include "LISAgeometry.h"
#include "LISAFDresponse.h"
#include "LISAnoise.h"

/********************************** Structures ******************************************/

typedef enum GenTDIFDtag {
  TDIhlm,
  TDIFD,
  TDIFFT,
  h22FFT,
  yslrFFT
} GenTDIFDtag;

/* Parameters for the generation of a LISA waveform (in the form of a list of modes) */
typedef struct tagGenTDIFDparams {
  double tRef;               /* reference time (s) - GPS time at the frequency representing coalescence */
  double torb;               /* reference orbital time entering orbital response (s) */
  double phiRef;             /* reference phase (rad) - phase at the frequency representing coalescence (or at fRef if specified) */
  double fRef;               /* reference frequency at which phiRef is set (Hz, default 0 which is interpreted as Mf=0.14) */
  double m1;                 /* mass of companion 1 (solar masses, default 2e6) */
  double m2;                 /* mass of companion 2 (solar masses, default 1e6) */
  double distance;           /* distance of source (Mpc, default 1e3) */
  double inclination;        /* inclination of source (rad, default pi/3) */
  double lambda;             /* first angle for the position in the sky (rad, default 0) */
  double beta;               /* second angle for the position in the sky (rad, default 0) */
  double polarization;       /* polarization angle (rad, default 0) */

  int nbmode;                /* number of modes to generate (starting with 22) - defaults to 5 (all modes) */
  double minf;               /* Minimal frequency (Hz, default=0) - when set to 0, use the lowest frequency where the detector noise model is trusted __LISASimFD_Noise_fLow (set somewhat arbitrarily)*/
  double maxf;               /* Maximal frequency (Hz, default=0) - when set to 0, use the highest frequency where the detector noise model is trusted __LISASimFD_Noise_fHigh (set somewhat arbitrarily)*/
  double deltatobs;          /* Observation duration (years, default=2) */
  int tagextpn;              /* Tag to allow PN extension of the waveform at low frequencies (default=1) */
  double Mfmatch;            /* When PN extension allowed, geometric matching frequency: will use ROM above this value. If <=0, use ROM down to the lowest covered frequency (default=0.) */
  int setphiRefatfRef;       /* Flag for adjusting the FD phase at phiRef at the given fRef, which depends also on tRef - if false, treat phiRef simply as an orbital phase shift (minus an observer phase shift) (default=1) */
  double deltaf;             /* When generating frequency series from the mode contributions, deltaf for the output (0 to set automatically at 1/2*1/(2T)) */
  double twindowbeg;         /* When generating frequency series from file by FFT, twindowbeg (0 to set automatically at 0.05*duration) */
  double twindowend;         /* When generating frequency series from file by FFT, twindowend (0 to set automatically at 0.01*duration) */
  int tagh22fromfile;        /* Tag choosing wether to load h22 FD downsampled Amp/Phase from file (default 0) */
  int tagtdi;                /* Tag selecting the desired output format */
  int frozenLISA;            /* Freeze the orbital configuration to the time of peak of the injection (default 0) */
  ResponseApproxtag responseapprox;    /* Approximation in the GAB and orb response - choices are full (full response, default), lowfL (keep orbital delay frequency-dependence but simplify constellation response) and lowf (simplify constellation and orbital response) - WARNING : at the moment noises are not consistent, and TDI combinations from the GAB are unchanged */
  int taggenwave;            /* Tag selecting the desired output format */
  int restorescaledfactor;   /* If 1, restore the factors that were scaled out of TDI observables */
  int FFTfromtdfile;         /* Option for loading time series and FFTing (default: false) */
  int nsamplesinfile;        /* Number of lines of input file */
  int binaryin;              /* Tag for loading the data in gsl binary form instead of text (default false) */
  int binaryout;             /* Tag for outputting the data in gsl binary form instead of text (default false) */
  char indir[256];           /* Path for the input directory */
  char infile[256];          /* Path for the input file */
  char outdir[256];          /* Path for the output directory */
  char outfileprefix[256];   /* Path for the output file */
} GenTDIFDparams;

#if 0
{ /* so that editors will match succeeding brace */
#elif defined(__cplusplus)
}
#endif

#endif /* _GENERATETDIFD_H */


/* -*- linux-c -*- */
/* fewbody_utils.c

   Copyright (C) 2002-2004 John M. Fregeau

   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>
#include "fewbody.h"

/* allocate a vector */
double *fb_malloc_vector(int n)
{
	return((double *) malloc(n * sizeof(double)));
}

/* allocate a matrix */
double **fb_malloc_matrix(int nr, int nc)
{
	int i;
	double **m;

	m = (double **) malloc(nr * sizeof(double *));

	m[0] = (double *) malloc(nr * nc * sizeof(double));

	for (i=1; i<nr; i++) {
		m[i] = m[i-1] + nc;
	}

	return(m);
}

/* free a vector */
void fb_free_vector(double *v)
{
	free(v);
}

/* free a matrix */
void fb_free_matrix(double **m)
{
	free(m[0]);
	free(m);
}

/* a fast square function */
double fb_sqr(double x)
{
	return(x*x);
}

/* a fast cube function */
double fb_cub(double x)
{
	return(x*x*x);
}

/* the dot product of two vectors */
double fb_dot(double x[3], double y[3])
{
	return(x[0] * y[0] + x[1] * y[1] + x[2] * y[2]);
}

/* the modulus of a vector */
double fb_mod(double x[3])
{
	return(sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]));
}

/* the cross product of two vectors */
int fb_cross(double x[3], double y[3], double z[3])
{
	z[0] = x[1] * y[2] - x[2] * y[1];
	z[1] = x[2] * y[0] - x[0] * y[2];
	z[2] = x[0] * y[1] - x[1] * y[0];

	return(0);
}

/* something to calculate the angular momentum */
int fb_angmom(fb_obj_t *star, int nstar, double L[3])
{
	int i, k;
	double **l;

	l = fb_malloc_matrix(nstar, 3);

	for (k=0; k<3; k++) {
		L[k] = 0.0;
	}

	for (i=0; i<nstar; i++) {
		fb_cross(star[i].x, star[i].v, l[i]);
		for (k=0; k<3; k++) {
			L[k] += star[i].m * l[i][k];
		}
	}

	fb_free_matrix(l);

	return(0);
}

/* something to calculate the internal angular momentum */
void fb_angmomint(fb_obj_t *star, int nstar, double L[3])
{
	int i, k;

	for (k=0; k<3; k++) {
		L[k] = 0.0;
	}

	for (i=0; i<nstar; i++) {
		for (k=0; k<3; k++) {
			L[k] += star[i].Lint[k];
		}
	}
}

/* something to calculate the internal energy */
double fb_einttot(fb_obj_t *star, int nstar)
{
	int i;
	double eint=0.0;

	for (i=0; i<nstar; i++) {
		eint += star[i].Eint;
	}

	return(eint);
}

/* calculates the total potential energy of the system */
double fb_petot(fb_obj_t *star, int nstar)
{
	int i, j, k;
	double pe=0.0, r[3];

	for (i=0; i<nstar; i++) {
		for (j=i+1; j<nstar; j++) {
			for (k=0; k<3; k++) {
				r[k] = star[j].x[k] - star[i].x[k];
			}
			pe += -star[i].m * star[j].m / fb_mod(r);
		}
	}

	return(pe);
}

/* calculates the total kinetic energy of the system */
double fb_ketot(fb_obj_t *star, int nstar)
{
	int i;
	double ke=0.0;

	for (i=0; i<nstar; i++) {
		ke += 0.5 * star[i].m * fb_dot(star[i].v, star[i].v);
	}

	return(ke);
}

/* calculates the potential energy of the bound members of the system */
double fb_outerpetot(fb_obj_t **obj, int nobj)
{
	int i, j, k;
	double pe=0.0, r[3];

	for (i=0; i<nobj; i++) {
		for (j=i+1; j<nobj; j++) {
			for (k=0; k<3; k++) {
				r[k] = obj[j]->x[k] - obj[i]->x[k];
			}
			pe += -obj[i]->m * obj[j]->m / fb_mod(r);
		}
	}

	return(pe);
}

/* calculates the kinetic energy of the bound members of the system */
double fb_outerketot(fb_obj_t **obj, int nobj)
{
	int i;
	double ke=0.0;

	for (i=0; i<nobj; i++) {
		ke += 0.5 * obj[i]->m * fb_dot(obj[i]->v, obj[i]->v);
	}

	return(ke);
}

/* solve the Kepler equation for the eccentric anomaly, given the mean anomaly and eccentricity */
double fb_kepler(double e, double mean_anom)
{
	int status, iter;
	double ecc_anom, params[2];
	gsl_function F;
	const gsl_root_fsolver_type *T;
	gsl_root_fsolver *s;

	/* set up the root solver */
	F.function = &fb_keplerfunc;
	F.params = &params;

	/* set the parameters */
	params[0] = e;
	params[1] = mean_anom;

	T = gsl_root_fsolver_brent;
	s = gsl_root_fsolver_alloc(T);
	gsl_root_fsolver_set(s, &F, 0.0, 2.0*FB_CONST_PI);

	/* get eccentric anomaly by root-finding */
	iter = 0;
	do {
		iter++;
		gsl_root_fsolver_iterate(s);
		status = gsl_root_test_interval(gsl_root_fsolver_x_lower(s), gsl_root_fsolver_x_upper(s), \
						FB_ROOTSOLVER_ABS_ACC, FB_ROOTSOLVER_REL_ACC);
	} while (status == GSL_CONTINUE && iter < FB_ROOTSOLVER_MAX_ITER);

	if (iter >= FB_ROOTSOLVER_MAX_ITER) {
		fprintf(stderr, "Root finder failed to converge.\n");
		exit(1);
	}

	/* we've got the root */
	ecc_anom = gsl_root_fsolver_root(s);

	/* free memory associated with root solver */
	gsl_root_fsolver_free(s);

	return(ecc_anom);
}

/* the Kepler function for the root finder */
double fb_keplerfunc(double ecc_anom, void *params)
{
	double e, mean_anom;

	e = ((double *)params)[0];
	mean_anom = ((double *)params)[1];

	return(ecc_anom - e * sin(ecc_anom) - mean_anom);
}

/* calculate the relative tidal acceleration */
double fb_reltide(fb_obj_t *bin, fb_obj_t *single, double r)
{
	double arel, atid;

	arel = bin->m / fb_sqr(bin->a * (1.0 + bin->e));
	/* Factor in numerator is (single->m + bin->m) instead of single->m for the case of
	   small single->m, for which we want to at some point resolve "bin" to get the
	   motion of "single".  This will eventually be fixed when we we use full collapsed
	   binary member positions in the integration, and not just the CM of the binary. */
	atid = 2.0 * (single->m + bin->m) / fb_cub(r) * bin->a * (1.0 + bin->e);

	return(atid/arel);
}


#ifndef __MAIN_H__
#define __MAIN_H__ 1

#ifdef PARALLEL
#include "mpi.h"
#endif
#include <stdio.h>
#include <math.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <gsl/gsl_randist.h>
//#include "NNopt.h"
#include "bambi.h"

#ifdef __INTEL_COMPILER 			// if the MultiNest library was compiled with ifort
       #define NESTRUN nested_mp_nestrun_
#elif defined __GNUC__ 				// if the MultiNest library was compiled with gfortran
       #define NESTRUN __nested_MOD_nestrun
#else
       #error Do not know how to link to Fortran libraries, check symbol table for your platform (nm libnest3.a | grep nestrun) & edit example_eggbox_C++/eggbox.cc
#endif

extern float thL[3],logLRange,tol;
extern float *omicron;
extern double *maxsigma;
extern double logZero;
extern int nNN,nNNerr,totpar,loglcalls,ncheck,myid,nproc;
extern char root[100],networkinputs[100];
extern bool likenetinit,converged,lastconverged,netres,firstrun,discardpts;
extern int ignoredbambicalls,counter;
extern size_t nlayers,nnodes[10];
extern int doBAMBI,useNN,whitenin,whitenout,resume;

/***************************************** C++ Interface to MultiNest **************************************************/

namespace nested
{

	// map the Fortran 90 entry points of libnest3.a to C++ functions

	// module nested, function nestRun maps to nested::run

	// the pass-by-reference nature of most of the Fortran is translated away
	// *apart* from the callbacks. The provided call back functions must still accept
	// references rather than values. There is also some confusion as to the type
	// of the first argument of LogLike.
	// Should it be a double * or an farray<double, 1> *? The former seems to
	// work and is simpler.

	// This structure is reverse engineered from looking
	// at gfortran stack traces. It is probably wrong

	template<typename type, int ndims> class farray_traits;

	template<> class farray_traits<double, 1> { public: static const int id = 537; };
	template<> class farray_traits<double, 2> { public: static const int id = 538; };
	template<> class farray_traits<int, 1> { public: static const int id = 265; };
	template<> class farray_traits<int, 2> { public: static const int id = 266; };

	// the extra data for f90 that defines how arrays are arranged.
	template<typename T, int ndim> class farray
	{
		public:
			farray(T *_data, int w, int h = 0) : data(_data), offset(0), type(farray_traits<T, ndim>::id),
			x_stride(1), x_lbound(1), x_ubound(w), y_stride(w), y_lbound(1), y_ubound(h) {};

			T *data;
			int offset;
			int type;
			int x_stride, x_lbound, x_ubound;
			int y_stride, y_lbound, y_ubound;
	};

	extern "C" {
		void NESTRUN(int &mmodal, int &ceff, int &nlive, double &tol, double &efr, int &ndims,
			int &nPar, int &nClsPar, int &maxModes, int &updInt, double &Ztol, char *root, int &seed,
			int *pWrap, int &fb, int &resume, int &outfile, int &initMPI, double &logZero, int &maxiter,
			void (*Loglike)(double *Cube, int &n_dim, int &n_par, double &lnew, void *),
			void (*dumper)(int &, int &, int &, double **, double **, double **, double &, double &, double &, void *),
			void (*bambi)(int &, int &, double **, double &), void *context, int &root_len);
	}

	static void run(bool mmodal, bool ceff, int nlive, double tol, double efr, int ndims, int nPar, int nClsPar, int maxModes,
		int updInt, double Ztol, const std::string & root, int seed, int *pWrap, bool fb, bool resume, bool outfile,
		bool initMPI, double logZero, int maxiter, void (*LogLike)(double *Cube, int &n_dim, int &n_par, double &lnew, void *),
		void (*dumper)(int &, int &, int &, double **, double **, double **, double &, double &, double &, void *),
		void (*bambi)(int &, int &, double **, double &), void *context)
	{
		char t_root[100];
		std::fill(t_root, t_root + 100, ' ');
		snprintf(t_root, 99, "%s", root.c_str());
		int root_len = strlen(t_root);
		t_root[strlen(t_root)] = ' ';

		int t_fb = fb;
		int t_resume = resume;
		int t_outfile = outfile;
		int t_initMPI = initMPI;
		int t_mmodal = mmodal;
		int t_ceff = ceff;

		NESTRUN(t_mmodal, t_ceff, nlive, tol, efr, ndims, nPar, nClsPar, maxModes, updInt, Ztol, t_root, seed, pWrap, t_fb,
		t_resume, t_outfile, t_initMPI, logZero, maxiter, LogLike, dumper, bambi, context, root_len);
	}
}

/***********************************************************************************************************************/

#endif


/**
 * copyright  (C) 2004
 * the icecube collaboration
 * $Id: I3GSLRandomService.h 161127 2018-02-20 14:27:18Z kjmeagher $
 *
 * @brief An implementation of the I3RandomService interface.
 *
 * Uses the gsl library for the random numbers
 *
 * @version $Revision: 161127 $
 * @date $Date: 2018-02-20 07:27:18 -0700 (Tue, 20 Feb 2018) $
 * @author pretz
 */

#ifndef I3GSLRANDOMSERVICE_H
#define I3GSLRANDOMSERVICE_H

#include "phys-services/I3RandomService.h"

#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_test.h>

/**
 * This is (the state for) a shim which allows us to count the calls to the
 * RNG, but otherwise hands all work off to the real GSL implmentations.
 */
typedef struct
{
  unsigned long int seed;
  uint64_t icalls;
  uint64_t dcalls;
  gsl_rng* rng;
} gsl_rng_wrapper_state;

extern const gsl_rng_type gsl_rng_counting_wrapper;

class I3GSLRandomService : public I3RandomService{
 public:
  /**
   * default constructor
   */
  I3GSLRandomService();

  /**
   * constructor
   */
  explicit I3GSLRandomService(unsigned long int seed, bool track_state=true);

  /**
   * destructor
   */
  virtual ~I3GSLRandomService();

  /**
   * a number drawn from a binomial distribution
   */
  virtual int Binomial(int ntot, double prob);

  /**
   * A number from an Exponential distribution
   */
  virtual double Exp(double tau);

  /**
   * An integer drawn uniformly from [0,imax)
   */
  virtual unsigned int Integer(unsigned int imax);

  /**
   * An integer drawn from a Poisson distribution
   */
  virtual int Poisson(double mean);

  /**
   * A number drawn from a Poisson distribution, as a double
   */
  virtual double PoissonD(double mean);

  /**
   * a double drawn from a uniform distribution (0,x1)
   */
  virtual double Uniform(double x1 = 1);

  /**
   * a double drawn from a uniform distribution (x1,x2)
   */
  virtual double Uniform(double x1, double x2);

  /**
   * a double drawn from a Gaussian distribution with given
   * mean and standard deviation
   */
  virtual double Gaus(double mean, double stddev);

  /**
   * get all information necessary to restore the internal
   * state of the generator
   */
  virtual I3FrameObjectPtr GetState() const;

  /**
   * restore the internal state of the generator
   */
  virtual void RestoreState(I3FrameObjectConstPtr state);

 private:

  // private copy constructors and assignment
  I3GSLRandomService(const I3GSLRandomService& );
  I3GSLRandomService operator=(const I3GSLRandomService& );

  /**
   * Helper function which constructs our preferred GSL RNG.
   */
  static void construct(gsl_rng*& r);
  /**
   * Helper function which constructs a GSL RNG wrapped with the counting shim
   */
  static void construct_counted(gsl_rng*& r);

  gsl_rng* r;
  bool track_state;

  SET_LOGGER("I3GSLRandomService");

  //let gsl_wrapper_set use construct()
  friend void gsl_wrapper_set(void* vstate, unsigned long int s);
};

I3_POINTER_TYPEDEFS(I3GSLRandomService);

#endif //I3GSLRANDOMSERVICE_H


#ifndef __ARNOLDI_Implicit_restart_Arnoldi_H__
#define __ARNOLDI_Implicit_restart_Arnoldi_H__

#include "Macros.h"
#include "timer.h"
#include "cuda_supp.h"
#include "memory_operations.h"
#include "Arnoldi_Driver.h"
#include "Select_Shifts.h"
#include "QR_Shifts.h"
#include "Matrix_Vector_emulator.h"
#include "file_operations.h"
//to set number of threads in openblas
#include <cblas.h>

extern "C" int openblas_get_num_threads(void);
extern "C" void openblas_set_num_threads(int);

real Implicit_restart_Arnoldi_GPU_data(cublasHandle_t handle, bool verbose, int N, user_map_vector Axb, void *user_struct, char which[2], int k, int m, complex real* eigenvaluesA, real tol, int max_iter, real *eigenvectors_real=NULL, real *eigenvectors_imag=NULL, int BLASTreads=1);

real Implicit_restart_Arnoldi_GPU_data_Matrix_Exponent(cublasHandle_t handle, bool verbose, int N,  user_map_vector Axb_exponent_invert, void *user_struct_exponent_invert, user_map_vector Axb, void *user_struct, char which[2], char which_exponent[2], int k, int m, complex real* eigenvaluesA, real tol, int max_iter, bool is_rotated, real *eigenvectors_real_d=NULL, real *eigenvectors_imag_d=NULL, int BLASThreads=1);

#endif


/**
 * Interface definitions for the postscript output routines
 */

#ifndef _SPCE_OUTPUT_H
#define _SPCE_OUTPUT_H
#include "aper_conf.h"
#include <gsl/gsl_matrix.h>

/*
 * Structure to store all information on
 * a stamp image which is going to be
 * created. This information is steps in x
 * start coos in x,y and image dimensions.
 */
typedef struct
{
  double resolution;

  double xstart;
  double ystart;

  long   xsize;
  long   ysize;
}
drzstamp_dim;

/*
 * Quadrangle structure to be filled with the
 * corners of a pixel in the drizzled coo-system.
 * Those corners are then passed to the boxer
 * subroutine to determine the overlap for a
 * particular pixel
 */
typedef struct
{
  double xmax;  // the maximum value in x[]
  double xmin;  // the minimum value in x[]
  double ymax;  // the maximum value in y[]
  double ymin;  // the minimum value in y[]

                // (x[0],y[0]), (x[1],y[1])
  double x[4];  // (x[2],y[2]) and (x[3],y[3])
  double y[4];  // are the corner points of a pixel
                // in the coo-system of the drizzled image
}
quadrangle;

typedef struct
{
  gsl_matrix *counts;
  gsl_matrix *weight;
}
drzstamp;

/*
 * Structure for all stamp images
 * of a beam in 'drzprep'
 */
typedef struct
{
  gsl_matrix *counts;
  gsl_matrix *error;
  gsl_matrix *cont;
  gsl_matrix *model;
  gsl_matrix *vari;
}
drzprep;

extern gsl_vector_int *
get_trace_inds (const ap_pixel * const ap_p);

extern gsl_matrix *
stamp_img (const ap_pixel * const ap_p, float width, d_point *stp_min);

extern drzprep *
stamp_img_drzprep (const int opt_extr, const ap_pixel * const ap_p, const ap_pixel * const se_p,
		   float width, float nullval, int usemode,
		   drzstamp_dim dimension, gsl_matrix *drzcoeffs,
		   double exptime, double sky_cps, double rdnoise,
		   const int bckmode);

extern void
free_drzprep(drzprep *drzprep_stamps);

extern gsl_matrix *
rectified_stamp_img (const ap_pixel * const ap_p, float width, d_point *stp_min);

extern d_point
get_minxy_from_PET(const ap_pixel * const ap_p);

extern d_point
get_maxxy_from_PET(const ap_pixel * const ap_p);

extern drzstamp *
drizzled_stamp_img (const ap_pixel * const ap_p, double width,
		    const double orient, const drzstamp_dim dimension);

extern drzstamp_dim
get_drzprep_dim(const ap_pixel *const ap_p, float width,
		int boxwidth, int boxheight);

extern drzstamp_dim
get_stamp_dim(const ap_pixel * const ap_p, float width,
	      aperture_conf *conf, const int beamID, d_point *stp_min);

extern quadrangle
get_quad_from_pixel(const ap_pixel *cur_p, const double orient, const drzstamp_dim dimension);

extern gsl_matrix *
drizzled_stamp_img_orig (const ap_pixel * const ap_p, float width,
		    aperture_conf *conf);

extern void
interpolate_over_NaN (gsl_matrix *data);

extern void
free_stamp_img(gsl_matrix *stamp);

extern void
free_drzstamp(drzstamp *stamp);
#endif


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

#pragma once

#ifndef linequ_546582d4_5f60_4938_9ce8_1beb1678ba61_h
#define linequ_546582d4_5f60_4938_9ce8_1beb1678ba61_h

#include <gslib/type.h>
#include <gslib/std.h>
#include <gslib/error.h>

__gslib_begin__

class _trait_dimcol {};
class _trait_dimrow {};

template<class _fty, class _mty, class _trdim>
class ledimsel
{
public:
    typedef _fty type;
    typedef _mty matrix;
    typedef ledimsel<_fty, _mty, _trdim> selection;

public:
    ledimsel(matrix& mat, int cr): _mat(mat), _sel(cr) {}
    int index() const { return _sel; }
    int capacity() const { return capacity<_trdim>(); }
    int cols() const { return _mat.cols(); }
    int rows() const { return _mat.rows(); }
    type& get(int i) { return get<_trdim>(i); }
    const type& get_const(int i) const { return get_const<_trdim>(i); }
    void set(int i, const type& t) { get(i) = t; }

protected:
    template<class _trdim>
    int capacity() const;
    template<class _trdim>
    type& get(int i);
    template<class _trdim>
    const type& get_const(int i) const;
    template<>
    int capacity<_trait_dimcol>() const { return rows(); }
    template<>
    int capacity<_trait_dimrow>() const { return cols(); }
    template<>
    type& get<_trait_dimcol>(int i) { return _mat.get(i, _sel); }
    template<>
    type& get<_trait_dimrow>(int i) { return _mat.get(_sel, i); }
    template<>
    const type& get_const<_trait_dimcol>(int i) const { return _mat.get_const(i, _sel); }
    template<>
    const type& get_const<_trait_dimrow>(int i) const { return _mat.get_const(_sel, i); }

protected:
    matrix&         _mat;
    int             _sel;

public:
    template<class _op>
    ledimsel& operator*=(_op t)
    {
        for_each([&t](type& n) { n *= t; });
        return *this;
    }
    template<class _op>
    ledimsel& operator/=(_op t) { return operator*=(t); }
    template<class _opset>
    ledimsel& operator+=(const _opset& rhs)
    {
        assert(capacity() == rhs.capacity());
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            get(i) += rhs.get_const(i);
        return *this;
    }
    template<class _opset>
    ledimsel& operator-=(const _opset& rhs)
    {
        assert(capacity() == rhs.capacity());
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            get(i) -= rhs.get_const(i);
        return *this;
    }

public:
    template<class _lambda>
    void for_each(_lambda lam)
    {
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            lam(get(i));
    }
    template<class _lambda>
    void const_for_each(_lambda lam) const
    {
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            lam(get_const(i));
    }
};

/* optimize for row selector */
template<class _fty, class _mty>
class ledimsel<_fty, _mty, _trait_dimrow>
{
public:
    typedef _fty type;
    typedef _mty matrix;
    typedef ledimsel<_fty, _mty, _trait_dimrow> selection;

public:
    ledimsel(matrix& mat, int cr): _mat(mat), _sel(cr), _data(mat.data() + mat.accpos(cr, 0)) {}
    int index() const { return _sel; }
    int capacity() const { return cols(); }
    int cols() const { return _mat.cols(); }
    int rows() const { return _mat.rows(); }
    type& get(int i) { return _data[i]; }
    const type& get_const(int i) const { return _data[i]; }
    void set(int i, const type& t) { get(i) = t; }

protected:
    matrix&         _mat;
    int             _sel;
    type*           _data;

public:
    template<class _op>
    ledimsel& operator*=(_op t)
    {
        for_each([&t](type& n) { n *= t; });
        return *this;
    }
    template<class _op>
    ledimsel& operator/=(_op t) { return operator*=(t); }
    template<class _opset>
    ledimsel& operator+=(const _opset& rhs)
    {
        assert(capacity() == rhs.capacity());
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            get(i) += rhs.get_const(i);
        return *this;
    }
    template<class _opset>
    ledimsel& operator-=(const _opset& rhs)
    {
        assert(capacity() == rhs.capacity());
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            get(i) -= rhs.get_const(i);
        return *this;
    }

public:
    template<class _lambda>
    void for_each(_lambda lam)
    {
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            lam(get(i));
    }
    template<class _lambda>
    void const_for_each(_lambda lam) const
    {
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            lam(get_const(i));
    }
};

template<class _opset1, class _opset2>
static void swap_opset(_opset1& ops1, _opset2& ops2)
{
    assert(ops1.capacity() == ops2.capacity());
    int cap = ops1.capacity();
    for(int i = 0; i < cap; i ++)
        std::swap(ops1.get(i), ops2.get(i));
}

template<class _opset>
static int first_non_zero(const _opset& ops)
{
    int cap = ops.capacity();
    for(int i = 0; i < cap; i ++) {
        if(ops.get_const(i) != 0)
            return i;
    }
    return cap;
}

template<class _opset>
static int last_non_zero(const _opset& ops)
{
    for(int i = ops.capacity() - 1; i >= 0; i --) {
        if(ops.get_const(i) != 0)
            return i;
    }
    return ops.capacity();
}

template<class _fty>
class levector
{
public:
    typedef _fty type;
    typedef levector<_fty> vector;

protected:
    int             _size;
    type*           _data;

public:
    levector() { _data = 0, _size = 0; }
    levector(int s)
    {
        assert(s > 0);
        _data = 0;
        setup(s);
    }
    ~levector()
    {
        if(_data) {
            delete [] _data;
            _data = 0;
        }
    }
    void setup(int s)
    {
        if(_data != 0)
            delete [] _data;
        _size = s;
        _data = new type[s];
    }
    int capacity() const { return _size; }
    type& get(int i) { return _data[i]; }
    const type& get_const(int i) const { return _data[i]; }
    type* data() const { return _data; }
    void set(int i, const type& t) { get(i) = t; }

public:
    template<class _lambda>
    void for_each(_lambda lam)
    {
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            lam(get(i));
    }
    template<class _lambda>
    void const_for_each(_lambda lam) const
    {
        int cap = capacity();
        for(int i = 0; i < cap; i ++)
            lam(get_const(i));
    }
};

template<class _fty>
class lematrix
{
public:
    typedef _fty type;
    typedef lematrix<_fty> matrix;
    typedef levector<_fty> vector;
    typedef ledimsel<_fty, lematrix, _trait_dimrow> rowselector;
    typedef ledimsel<_fty, const lematrix, _trait_dimrow> const_rowselector;
    typedef ledimsel<_fty, lematrix, _trait_dimcol> colselector;
    typedef ledimsel<_fty, const lematrix, _trait_dimcol> const_colselector;

protected:
    int             _rows;
    int             _cols;
    type*           _data;

public:
    lematrix()
    {
        _rows = _cols = 0;
        _data = 0;
    }
    lematrix(int r, int c)
    {
        assert(r > 0 && c > 0);
        _data = 0;
        set_dim(r, c);
    }
    ~lematrix()
    {
        if(_data != 0) {
            delete [] _data;
            _data = 0;
        }
    }
    void set_dim(int r, int c)
    {
        if(_data != 0)
            delete [] _data;
        assert(r > 0 && c > 0);
        _rows = r;
        _cols = c;
        _data = new type[r * c];
    }
    int accpos(int r, int c) const
    {
        assert(r >= 0 && r < _rows && c >= 0 && c < _cols);
        int p = r * _cols + c;
        return p;
    }
    type& get(int r, int c)
    {
        assert(r >= 0 && r < _rows && c >= 0 && c < _cols);
        int p = accpos(r, c);
        return _data[p];
    }
    const type& get_const(int r, int c) const
    {
        assert(r >= 0 && r < _rows && c >= 0 && c < _cols);
        int p = accpos(r, c);
        return _data[p];
    }
    int dump_row(vector& v, int r)
    {
        int s = cols();
        v.setup(s);
        memcpy_s(v.data(), sizeof(type) * s, &get(r, 0), sizeof(type) * s);
        return s;
    }
    int dump_col(vector& v, int c)
    {
        int s = rows();
        v.setup(s);
        auto& sel = select_col(c);
        assert(s == sel.capacity());
        for(int i = 0; i < s; i ++)
            v.set(i, sel.get_const(i));
        return s;
    }
    bool gauss_elim()
    {
        assert(rows() >= 2);
        int cap = rows();
        arrange(0, cap);
        for(int i = 1; i < cap; i ++)
            gauss_elim(i);
        return row_rank() <= col_rank();
    }

public:
    int rows() const { return _rows; }
    int cols() const { return _cols; }
    type* data() const { return _data; }
    colselector select_col(int c) { return colselector(*this, c); }
    const_colselector select_col(int c) const { return const_colselector(*this, c); }
    rowselector select_row(int r) { return rowselector(*this, r); }
    const_rowselector select_row(int r) const { return const_rowselector(*this, r); }
    bool fuzzy_solvable() const { return rows() >= cols() - 1; }
    int row_rank() const { return cols() - first_non_zero(select_row(0)) - 1; }
    int col_rank() const { return last_non_zero(select_col(cols() - 1)) + 1; }

protected:
    void arrange(int start, int end)
    {
        assert(start >= 0 && end <= rows());
        if(end - start <= 1)
            return;
        struct node
        {
            int index, first;
            node(int i, int f): index(i), first(f) {}
        };
        list<node> stlist;
        for(int i = start; i < end; i ++)
            stlist.push_back(node(i, first_non_zero(select_row(i))));
        stlist.sort([](const node& n1, const node& n2)->bool { return n1.first < n2.first; });
        assert(!stlist.empty());
        matrix mt;
        mt.set_dim(end - start, cols());
        auto j = stlist.begin();
        for(int i = 0; j != stlist.end(); ++ i, ++ j) {
            auto& r = mt.select_row(i);
            memcpy_s(&r.get(0), sizeof(type)*cols(), &get_const(j->index, 0), sizeof(type)*cols());
        }
        int cpsize = sizeof(type) * cols() * (end - start);
        memcpy_s(&get(start, 0), cpsize, mt.data(), cpsize);
    }
    void gauss_elim(int i)
    {
        assert(i > 0 && i < rows());
        auto& select1 = select_row(i - 1);
        auto& select2 = select_row(i);
        int first1 = first_non_zero(select1);
        if(select2.get(first1) == 0)
            return;
        int cap = rows();
        for(int j = i; j < cap; j ++) {
            if(!gauss_elim(i - 1, j, first1))
                break;
        }
        arrange(i, cap);
    }
    bool gauss_elim(int i, int j, int first)
    {
        assert(i < j);
        auto& select1 = select_row(i);
        auto& select2 = select_row(j);
        if(select2.get(first) == 0)
            return false;
        select2 *= (select1.get_const(first) / select2.get_const(first));
        select2 -= select1;
        return true;
    }

protected:
    void trace_data() const
    {
        for(int i = 0; i < rows(); i ++) {
            auto& sel = select_row(i);
            for(int j = 0; j < sel.capacity(); j ++)
                trace(_t("%lf    "), sel.get_const(j));
            trace(_t("\r"));
        }
    }
};

template<class _fty>
class linequ
{
public:
    typedef _fty type;
    typedef linequ<_fty> myref;
    typedef levector<_fty> vector;
    typedef lematrix<_fty> matrix;
    static double qnan()
    {
        static const struct nan_struct { union { uint a[2]; double b; }; } qnan_struct = { 0xffffffff, 0x7fffffff };
        return qnan_struct.b;
    }

public:
    void set_variable_count(int c) { _variables.setup(c); }
    int get_variable_count() const { return _variables.capacity(); }
    void set_equation_count(int c) { _matrix.set_dim(c, get_variable_count() + 1); }
    int get_equation_count() const { return _matrix.rows(); }
    const vector& get_results() const { return _variables; }
    type get_variable(int i) const { return _variables.get_const(i); }
    void set_equation(int i, ...)
    {
        va_list ptr;
        va_start(ptr, i);
        auto& selection = _matrix.select_row(i);
        int cap = _matrix.cols();
        for(int j = 0; j < cap; j ++)
            selection.set(j, va_arg(ptr, type));
    }
    void set_equation(int i, const type d[], int s)
    {
        auto& selection = _matrix.select_row(i);
        if(s > selection.capacity())
            s = selection.capacity();
        for(int j = 0; j < s; j ++)
            selection.set(j, d[j]);
    }
    int solve()
    {
        if(!_matrix.fuzzy_solvable() || !_matrix.gauss_elim())
            return 0;
        set_unsolved<_fty>();
        int rkcol = _matrix.col_rank();
        int c = 0;
        for(int i = rkcol - 1; i >= 0; i --, c ++) {
            if(!solve(i))
                break;
        }
        return c;
    }

protected:
    vector          _variables;
    matrix          _matrix;

protected:
    template<class _fty>
    void set_unsolved();
    template<>
    void set_unsolved<double>() { _variables.for_each([](type& v) { v = qnan(); }); }
    template<>
    void set_unsolved<float>()
    {
        float fqnan = static_cast<float>(qnan());
        assert(sizeof(fqnan) == sizeof(int));
        memset(_variables.data(), *(int*)&fqnan, _variables.capacity());
    }
    bool solve(int c)
    {
        auto& selection = _matrix.select_row(c);
        int first = first_non_zero(selection);
        for(int i = get_variable_count() - 1; i > c; i --) {
            if((((type)qnan())) == get_variable(i))
                return false;
        }
        int cap = get_variable_count();
        type s = selection.get_const(cap);
        for(int i = c + 1; i < cap; i ++)
            s += (selection.get_const(i) * get_variable(i));
        _variables.set(c, -s / selection.get_const(c));
        return true;
    }
};

__gslib_end__

#endif


/**
 *
 * @file al4san/codelet_dlacpy.c
 *
 * @copyright 2009-2014 The University of Tennessee and The University of
 *                      Tennessee Research Foundation. All rights reserved.
 * @copyright 2012-2018 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 * @copyright 2017-2018 King Abdullah University of Science and Technology (KAUST).
 *                      All rights reserved.
 * @brief Chameleon dlacpy AL4SAN codelet
 *
 * @version 1.1.0
 * @author Rabab Alomairy
 * @date 2018-09-19
 * @generated d Wed Jan 16 11:13:56 2019
 *
 */
/**
 *
 * @ingroup CORE_CHAMELEON_Complex64_t
 *
 */
#include <lapacke.h>
#include <al4san.h>
#include <runtime/al4san_quark.h>
#include <runtime/al4san_starpu.h>

AL4SAN_TASK_CPU(lacpy1, lacpy_cpu_func1)

void EIG_AL4SAN_CORE_dlacpy1(AL4SAN_option_t *options,
                         int uplo, int m, int n, int nb,
                         const AL4SAN_desc_t *A, int Am, int An, int lda,
                         const AL4SAN_desc_t *B, int Bm, int Bn, int ldb )
{
    (void)nb;

    AL4SAN_Insert_Task(AL4SAN_TASK(lacpy1), (AL4SAN_option_t*)options,
        AL4SAN_VALUE,                       &uplo,                                        sizeof(int),
        AL4SAN_VALUE,                       &m,                                           sizeof(int),
        AL4SAN_VALUE,                       &n,                                           sizeof(int),
        AL4SAN_INPUT,                       AL4SAN_ADDR(A, double, Am, An),  AL4SAN_DEP,
        AL4SAN_VALUE,                       &lda,                                         sizeof(int),
        AL4SAN_OUTPUT,                      AL4SAN_ADDR(B, double, Bm, Bn),  AL4SAN_DEP,
        AL4SAN_VALUE,                       &ldb,                                         sizeof(int),
        AL4SAN_PRIORITY,                    options->priority,                     sizeof(int),
        AL4SAN_LABEL,                       "dlacpy",                                     sizeof(char),
        AL4SAN_COLOR,                       "white",                                      sizeof(char),
        ARG_END);
}

#if !defined(CHAMELEON_SIMULATION)
 void lacpy_cpu_func1(AL4SAN_arg_list *al4san_arg)
{
    int uplo;
    int M;
    int N;
    int displA;
    int displB;
    const double *A;
    int LDA;
    double *B;
    int LDB;

    AL4SAN_Unpack_Arg(al4san_arg, &uplo, &M, &N, &A, &LDA, &B, &LDB);

    CORE_dlacpy(uplo, M, N, A, LDA, B , LDB);
}
#endif /* !defined(CHAMELEON_SIMULATION) */


#include <config.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

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

#include <gsl/gsl_fft_complex.h>
#include <gsl/gsl_fft_complex_float.h>

#define BASE_DOUBLE
#include "templates_on.h"
#include "bitreverse.c"
#include "templates_off.h"
#undef  BASE_DOUBLE

#define BASE_FLOAT
#include "templates_on.h"
#include "bitreverse.c"
#include "templates_off.h"
#undef  BASE_FLOAT

#include "factorize.c"

#define BASE_DOUBLE
#include "templates_on.h"
#include "c_init.c"
#include "c_main.c"
#include "c_pass_2.c"
#include "c_pass_3.c"
#include "c_pass_4.c"
#include "c_pass_5.c"
#include "c_pass_6.c"
#include "c_pass_7.c"
#include "c_pass_n.c"
#include "c_radix2.c"
#include "templates_off.h"
#undef  BASE_DOUBLE

#define BASE_FLOAT
#include "templates_on.h"
#include "c_init.c"
#include "c_main.c"
#include "c_pass_2.c"
#include "c_pass_3.c"
#include "c_pass_4.c"
#include "c_pass_5.c"
#include "c_pass_6.c"
#include "c_pass_7.c"
#include "c_pass_n.c"
#include "c_radix2.c"
#include "templates_off.h"
#undef  BASE_FLOAT

#include <gsl/gsl_fft_halfcomplex.h>
#include <gsl/gsl_fft_halfcomplex_float.h>

#define BASE_DOUBLE
#include "templates_on.h"
#include "hc_init.c"
#include "hc_main.c"
#include "hc_pass_2.c"
#include "hc_pass_3.c"
#include "hc_pass_4.c"
#include "hc_pass_5.c"
#include "hc_pass_n.c"
#include "hc_radix2.c"
#include "hc_unpack.c"
#include "templates_off.h"
#undef  BASE_DOUBLE

#define BASE_FLOAT
#include "templates_on.h"
#include "hc_init.c"
#include "hc_main.c"
#include "hc_pass_2.c"
#include "hc_pass_3.c"
#include "hc_pass_4.c"
#include "hc_pass_5.c"
#include "hc_pass_n.c"
#include "hc_radix2.c"
#include "hc_unpack.c"
#include "templates_off.h"
#undef  BASE_FLOAT

#include <gsl/gsl_fft_real.h>
#include <gsl/gsl_fft_real_float.h>

#define BASE_DOUBLE
#include "templates_on.h"
#include "real_init.c"
#include "real_main.c"
#include "real_pass_2.c"
#include "real_pass_3.c"
#include "real_pass_4.c"
#include "real_pass_5.c"
#include "real_pass_n.c"
#include "real_radix2.c"
#include "real_unpack.c"
#include "templates_off.h"
#undef  BASE_DOUBLE

#define BASE_FLOAT
#include "templates_on.h"
#include "real_init.c"
#include "real_main.c"
#include "real_pass_2.c"
#include "real_pass_3.c"
#include "real_pass_4.c"
#include "real_pass_5.c"
#include "real_pass_n.c"
#include "real_radix2.c"
#include "real_unpack.c"
#include "templates_off.h"
#undef  BASE_FLOAT


/* ieee-utils/print.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 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.
 */

#include <config.h>
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_ieee_utils.h>

/* A table of sign characters, 0=positive, 1=negative. We print a space
   instead of a unary + sign for compatibility with bc */

static char signs[2]={' ','-'} ;

void
gsl_ieee_fprintf_float (FILE * stream, const float * x) {
  gsl_ieee_float_rep r ;
  gsl_ieee_float_to_rep(x, &r) ;

  switch (r.type)
    {
    case GSL_IEEE_TYPE_NAN:
      fprintf(stream, "NaN") ;
      break ;
    case GSL_IEEE_TYPE_INF:
      fprintf(stream, "%cInf", signs[r.sign]) ;
      break ;
    case GSL_IEEE_TYPE_NORMAL:
      fprintf(stream, "%c1.%s*2^%d", signs[r.sign], r.mantissa, r.exponent) ;
      break ;
    case GSL_IEEE_TYPE_DENORMAL:
      fprintf(stream, "%c0.%s*2^%d", signs[r.sign], r.mantissa, r.exponent + 1) ;
      break ;
    case GSL_IEEE_TYPE_ZERO:
      fprintf(stream, "%c0", signs[r.sign]) ;
      break ;
    default:
      fprintf(stream, "[non-standard IEEE float]") ;
    }
}

void
gsl_ieee_printf_float (const float * x)
{
  gsl_ieee_fprintf_float (stdout,x);
}

void
gsl_ieee_fprintf_double (FILE * stream, const double * x) {
  gsl_ieee_double_rep r ;
  gsl_ieee_double_to_rep (x, &r) ;

  switch (r.type)
    {
    case GSL_IEEE_TYPE_NAN:
      fprintf(stream, "NaN") ;
      break ;
    case GSL_IEEE_TYPE_INF:
      fprintf(stream, "%cInf", signs[r.sign]) ;
      break ;
    case GSL_IEEE_TYPE_NORMAL:
      fprintf(stream, "%c1.%s*2^%d", signs[r.sign], r.mantissa, r.exponent) ;
      break ;
    case GSL_IEEE_TYPE_DENORMAL:
      fprintf(stream, "%c0.%s*2^%d", signs[r.sign], r.mantissa, r.exponent + 1) ;
      break ;
    case GSL_IEEE_TYPE_ZERO:
      fprintf(stream, "%c0", signs[r.sign]) ;
      break ;
    default:
      fprintf(stream, "[non-standard IEEE double]") ;
    }
}

void
gsl_ieee_printf_double (const double * x)
{
  gsl_ieee_fprintf_double (stdout,x);
}


/* -*- mode: C; c-basic-offset: 4 -*- */
/* ex: set shiftwidth=4 tabstop=4 expandtab: */
/*
 * Copyright (c) 2013, Georgia Tech Research Corporation
 * All rights reserved.
 *
 * Author(s): Neil T. Dantam <ntd@gatech.edu>
 * Georgia Tech Humanoid Robotics Lab
 * Under Direction of Prof. Mike Stilman <mstilman@cc.gatech.edu>
 *
 *
 * This file is provided under the following "BSD-style" License:
 *
 *
 *   Redistribution and use in source and binary forms, with or
 *   without modification, are permitted provided that the following
 *   conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *   POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <amino.h>
#include <cblas.h>
#include "reflex.h"

void x_fopen( const char *prefix, const char *suffix, size_t n, FILE *f[] ) {
    for( size_t i = 0; i < n; i ++ ) {
        char buf[512];
        sprintf(buf,"%s%lu%s", prefix, i, suffix);
        f[i] = fopen(buf, "w");
    }
}

void x_write( double t, size_t n, double e[], FILE *f[7] ) {
    for( size_t i = 0; i < n; i ++ ) {
        fprintf(f[i], "%f %f\n", t, e[i] );
        //fprintf(stdout, "%f %f\n", t, e[i] );
    }
}

void e_corrupt( double theta_max, double x_max, const double e0[7], double e1[7] )
{
    // rotation
    double aa[4];
    aa_vrand( 4, aa );
    aa_la_normalize(3,aa);
    aa[3] *= theta_max;

    // translation
    double v[3];
    aa_vrand( 3, v );
    for( size_t i = 0; i < 3; i ++ ) {
        v[i] *= x_max;
    }

    // convert
    double ec[7];
    aa_tf_axang2quat(aa, ec+AA_TF_QUTR_Q);
    AA_MEM_CPY(ec+AA_TF_QUTR_V, v, 3 );

    // mul
    aa_tf_qutr_mul( e0, ec, e1 );

}

rfx_tf_dx XX_true;
rfx_tf_dx XX_est;
rfx_tf_dx ZZ;
rfx_tf_dx UU;

double P[16*16] = {0};
double V[16*16] = {0};
double W[8*8] = {0};

double Pa[13*13] = {0};
double Wa[13*13] = {0};
double Va[13*13] = {0};

double Pb[13*13] = {0};
double Wb[7*7] = {0};
double Vb[13*13] = {0};

int main(void)
{

    double dt = .01;

    FILE *fout_z[7];
    FILE *fout_x_true[7];
    FILE *fout_x_est[7];

    FILE *fout_dx_true[6];
    FILE *fout_dx_est[6];

    // state
    memset(&XX_true,0,sizeof(XX_true));
    memset(&XX_est,0,sizeof(XX_est));
    memset(&ZZ,0,sizeof(ZZ));
    memset(&UU,0,sizeof(UU));

    aa_la_diag( 16, P, 1.0 );
    aa_la_diag( 8, W, 1.0 );
    aa_la_diag( 16, V, 1.0e-2 );

    for( size_t i = 0; i < 8; i ++ ) {
        AA_MATREF(V,16, i, 8+i ) = 1e-2;
        AA_MATREF(V,16, 8+i, i ) = 1e-2;
    }

    aa_la_diag( 13, Pb, 1 );
    aa_la_diag( 7, Wb, dt*5e-1 );

    aa_la_diag( 13, Pa, 1.0e-2 );
    aa_la_diag( 13, Wa, 1.0 );
    aa_la_diag( 13, Va, 1.0e2 );

    // files

    x_fopen("xtrue", ".dat", 7,fout_x_true);
    x_fopen("z", ".dat", 7,fout_z);
    x_fopen("xest", ".dat", 7,fout_x_est);
    x_fopen("dx_true", ".dat", 6,fout_dx_true);
    x_fopen("dx_est", ".dat", 6,fout_dx_est);

    memcpy(XX_true.tf.r.data, aa_tf_quat_ident, 4*sizeof(XX_true.tf.r.data[0]));
    memcpy(XX_est.tf.r.data, aa_tf_quat_ident, 4*sizeof(XX_true.tf.r.data[0]));

    for( double t = 0.0; t < 10.0; t += dt ) {
        fprintf(stderr, "t: %f, ", t );
        // pristine velocity
        double dx[6] = {cos(t*M_PI / 1), 0, 0,
                        0, 0, sin(t*M_PI / 1)};
        AA_MEM_CPY(XX_true.dx.data, dx, 6);

        // integrate true
        {
            double e_next[7];
            aa_tf_qutr_svel(XX_true.tf.data, XX_true.dx.data, dt, e_next );
            memcpy( XX_true.tf.data, e_next, sizeof(e_next) );
        }

        // corrupt measurement
        e_corrupt( 10*M_PI/180, 7e-2, XX_true.tf.data, ZZ.tf.data );

        // filter
        aa_tick("filter time: ");
        double S[8];
        double Sz[8];
        double dS_est[8];
        double dS_true[8];
        //double dE_est[7];
        aa_tf_qutr2duqu( XX_est.tf.data, S);
        aa_tf_qutr2duqu( ZZ.tf.data, Sz);
        aa_tf_duqu_vel2diff( S, XX_est.dx.data, dS_est );
        aa_tf_duqu_vel2diff( S, XX_true.dx.data, dS_true );

        //aa_tf_qutr_vel2diff( XX_est.tf.data, XX_est.dx.data, dE_est );

        /* rfx_lqg_duqu_predict( dt, S, dS_est, P, V ); */
        /* rfx_lqg_duqu_correct( 1, */
        /*                       S, dS_est, */
        /*                       Sz, /\*ZZ.dx.data, *\/ */
        /*                       P, W ); */
        /* aa_tf_duqu_diff2vel( S, dS_est, XX_est.dx.data ); */
        /* aa_tf_duqu2qutr( S, XX_est.tf.data); */

        rfx_lqg_qutr_process_noise( dt, 1, 1, XX_est.tf.data, Vb );

        rfx_lqg_qutr_predict( dt, XX_est.tf.data, XX_est.dx.data, Pb, Vb );
        rfx_lqg_qutr_correct( 1,
                              XX_est.tf.data, XX_est.dx.data,
                              ZZ.tf.data,
                              Pb, Wb );
        //aa_tf_qutr_diff2vel( XX_est.tf.data, dE_est, XX_est.dx.data );

        //rfx_tf_filter_update_work( dt, XX_est.tf.data, UU.tf.data, ZZ.tf.data, Pa, Va, Wa );
        aa_tock();
        //printf("--\n");
        //printf("dS_true: "); aa_dump_vec( stdout, dS_true, 8 );
        //printf("dS_est: "); aa_dump_vec(  stdout, dS_est, 8 );
        //aa_dump_mat( stdout, P, 16, 16 );

        //return 0;
        // print
        x_write( t, 7, XX_true.tf.data, fout_x_true );
        x_write( t, 6, XX_true.dx.data, fout_dx_true );

        x_write( t, 7, XX_est.tf.data, fout_x_est );
        x_write( t, 6, XX_est.dx.data, fout_dx_est );

        x_write( t, 7, ZZ.tf.data, fout_z );
    }

}