#include <math.h>
#include <stdlib.h>
#if !defined(__APPLE__)
#include <malloc.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <string.h>

#include <fftw3.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_erf.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_sf_legendre.h>
#include <gsl/gsl_sf_bessel.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_sf_expint.h>
#include <gsl/gsl_deriv.h>

#include "../cosmolike_core/theory/basics.c"
#include "../cosmolike_core/theory/structs.c"
#include "../cosmolike_core/theory/parameters.c"
#include "../cosmolike_core/emu17/P_cb/emu.c"
#include "../cosmolike_core/theory/recompute.c"
#include "../cosmolike_core/theory/cosmo3D.c"
#include "../cosmolike_core/theory/redshift.c"
#include "../cosmolike_core/theory/halo.c"
#include "../cosmolike_core/theory/HOD.c"
#include "../cosmolike_core/theory/cosmo2D_fourier.c"
#include "../cosmolike_core/theory/IA.c"
#include "../cosmolike_core/theory/cluster.c"
#include "../cosmolike_core/theory/BAO.c"
#include "../cosmolike_core/theory/external_prior.c"
#include "../cosmolike_core/theory/covariances_3D.c"
#include "../cosmolike_core/theory/covariances_fourier.c"
#include "../cosmolike_core/theory/covariances_cluster.c"
#include "init_SRD.c"

double C_shear_tomo_sys(double ell,int z1,int z2);
double C_cgl_tomo_sys(double ell_Cluster,int zl,int nN, int zs);
double C_gl_tomo_sys(double ell,int zl,int zs);
void set_data_shear(int Ncl, double *ell, double *data, int start);
void set_data_ggl(int Ncl, double *ell, double *data, int start);
void set_data_clustering(int Ncl, double *ell, double *data, int start);
void set_data_cluster_N(double *data, int start);
void set_data_cgl(double *ell_Cluster, double *data, int start);
void compute_data_vector(char *details, double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu, double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8, double B9, double B10, double SP1, double SP2, double SP3, double SP4, double SP5, double SP6, double SP7, double SP8, double SP9, double SP10, double SPS1, double CP1, double CP2, double CP3, double CP4, double CP5, double CP6, double CP7, double CP8, double CP9, double CP10, double CPS1, double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10, double A_ia, double beta_ia, double eta_ia, double eta_ia_highz, double LF_alpha, double LF_P, double LF_Q, double LF_red_alpha, double LF_red_P, double LF_red_Q, double mass_obs_norm, double mass_obs_slope, double mass_z_slope, double mass_obs_scatter_norm, double mass_obs_scatter_mass_slope, double mass_obs_scatter_z_slope);
double log_multi_like(double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu, double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8, double B9, double B10, double SP1, double SP2, double SP3, double SP4, double SP5, double SP6, double SP7, double SP8, double SP9, double SP10, double SPS1, double CP1, double CP2, double CP3, double CP4, double CP5, double CP6, double CP7, double CP8, double CP9, double CP10, double CPS1, double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10, double A_ia, double beta_ia, double eta_ia, double eta_ia_highz, double LF_alpha, double LF_P, double LF_Q, double LF_red_alpha, double LF_red_P, double LF_red_Q, double mass_obs_norm, double mass_obs_slope, double mass_z_slope, double mass_obs_scatter_norm, double mass_obs_scatter_mass_slope, double mass_obs_scatter_z_slope);
double write_vector_wrapper(char *details, input_cosmo_params ic, input_nuisance_params in);
double log_like_wrapper(input_cosmo_params ic, input_nuisance_params in);
int get_N_tomo_shear(void);
int get_N_tomo_clustering(void);
int get_N_ggl(void);
int get_N_ell(void);

int get_N_tomo_shear(void){
  return tomo.shear_Nbin;
}
int get_N_tomo_clustering(void){
  return tomo.clustering_Nbin;
}
int get_N_ggl(void){
  return tomo.ggl_Npowerspectra;
}
int get_N_ell(void){
  return like.Ncl;
}

double C_shear_tomo_sys(double ell, int z1, int z2)
{
  double C;
  // C= C_shear_tomo_nointerp(ell,z1,z2);
  // if(like.IA==1) C+=C_II_nointerp(ell,z1,z2)+C_GI_nointerp(ell,z1,z2);

  if(like.IA!=1) C= C_shear_tomo_nointerp(ell,z1,z2);
  if(like.IA==1) C= C_shear_shear_IA(ell,z1,z2);
  //if(like.IA==1) C = C_shear_tomo_nointerp(ell,z1,z2)+C_II_nointerp(ell,z1,z2)+C_GI_nointerp(ell,z1,z2);
  if(like.IA==2) C += C_II_lin_nointerp(ell,z1,z2)+C_GI_lin_nointerp(ell,z1,z2);
  if(like.shearcalib==1) C *=(1.0+nuisance.shear_calibration_m[z1])*(1.0+nuisance.shear_calibration_m[z2]);
  //printf("%le %d %d %le\n",ell,z1,z2,C_shear_tomo_nointerp(ell,z1,z2)+C_II_JB_nointerp(ell,z1,z2)+C_GI_JB_nointerp(ell,z1,z2));
return C;
}

double C_gl_tomo_sys(double ell,int zl,int zs)
{
  double C;
  // C=C_gl_tomo_nointerp(ell,zl,zs);
  // if(like.IA==1) C += C_gI_nointerp(ell,zl,zs);

  if(like.IA!=1) C=C_gl_tomo_nointerp(ell,zl,zs);
  if(like.IA==1) C = C_ggl_IA(ell,zl,zs);
  if(like.IA==2) C += C_gI_lin_nointerp(ell,zl,zs);
  if(like.shearcalib==1) C *=(1.0+nuisance.shear_calibration_m[zs]);
return C;
}

double C_cgl_tomo_sys(double ell_Cluster, int zl,int nN, int zs)
{
  double C;
  C=C_cgl_tomo_nointerp(ell_Cluster,zl,nN,zs);
  //if(like.IA!=0) C +=
  if(like.shearcalib==1) C *=(1.0+nuisance.shear_calibration_m[zs]);
return C;
}

void set_data_shear(int Ncl, double *ell, double *data, int start)
{
  int i,z1,z2,nz;
  double a;
  for (nz = 0; nz < tomo.shear_Npowerspectra; nz++){
    z1 = Z1(nz); z2 = Z2(nz);
    for (i = 0; i < Ncl; i++){
      if (ell[i]<like.lmax_shear){ data[Ncl*nz+i] = C_shear_tomo_sys(ell[i],z1,z2);}
      else {data[Ncl*nz+i] = 0.;}
    }
  }
}

void set_data_ggl(int Ncl, double *ell, double *data, int start)
{
  int i, zl,zs,nz;
  for (nz = 0; nz < tomo.ggl_Npowerspectra; nz++){
    zl = ZL(nz); zs = ZS(nz);
    for (i = 0; i < Ncl; i++){
      if (test_kmax(ell[i],zl)){
        data[start+(Ncl*nz)+i] = C_gl_tomo_sys(ell[i],zl,zs);
      }
      else{
        data[start+(Ncl*nz)+i] = 0.;
      }
    }
  }
}

void set_data_clustering(int Ncl, double *ell, double *data, int start){
  int i, nz;
  for (nz = 0; nz < tomo.clustering_Npowerspectra; nz++){
    //printf("%d %e %e\n",nz, gbias.b[nz][1],pf_photoz(gbias.b[nz][1],nz));
    for (i = 0; i < Ncl; i++){
      if (test_kmax(ell[i],nz)){data[start+(Ncl*nz)+i] = C_cl_tomo_nointerp(ell[i],nz,nz);}
      else{data[start+(Ncl*nz)+i] = 0.;}
      //printf("%d %d %le %le\n",nz,nz,ell[i],data[Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra + nz)+i]);
    }
  }
}

void set_data_cluster_N(double *data, int start){
  int nN, nz;
  for (nz = 0; nz < tomo.cluster_Nbin; nz++){
    for (nN = 0; nN < Cluster.N200_Nbin; nN++){
      data[start+Cluster.N200_Nbin*nz+nN] = N_N200(nz, nN);
    }
  }
}

void set_data_cgl(double *ell_Cluster, double *data, int start)
{
  int zl,zs,nN,nz,i,j;
  for(nN = 0; nN < Cluster.N200_Nbin; nN++){
    for (nz = 0; nz < tomo.cgl_Npowerspectra; nz++){
      zl = ZC(nz); zs = ZSC(nz);
      for (i = 0; i < Cluster.lbin; i++){
        j = start;
        j += (nz*Cluster.N200_Nbin+nN)*Cluster.lbin +i;
        data[j] = C_cgl_tomo_sys(ell_Cluster[i],zl,nN,zs);
      }
    }
  }
}

int set_cosmology_params(double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu)
{
  cosmology.Omega_m=OMM;
  cosmology.Omega_v= 1.0-cosmology.Omega_m;
  cosmology.sigma_8=S8;
  cosmology.n_spec= NS;
  cosmology.w0=W0;
  cosmology.wa=WA;
  cosmology.omb=OMB;
  cosmology.h0=H0;
  cosmology.MGSigma=MGSigma;
  cosmology.MGmu=MGmu;

  if (cosmology.Omega_m < 0.05 || cosmology.Omega_m > 0.6) return 0;
  if (cosmology.omb < 0.04 || cosmology.omb > 0.055) return 0;
  if (cosmology.sigma_8 < 0.5 || cosmology.sigma_8 > 1.1) return 0;
  if (cosmology.n_spec < 0.84 || cosmology.n_spec > 1.06) return 0;
  if (cosmology.w0 < -2.1 || cosmology.w0 > -0.0) return 0;
  if (cosmology.wa < -2.6 || cosmology.wa > 2.6) return 0;
  if (cosmology.h0 < 0.4 || cosmology.h0 > 0.9) return 0;
  return 1;
}

void set_nuisance_shear_calib(double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10)
{
  nuisance.shear_calibration_m[0] = M1;
  nuisance.shear_calibration_m[1] = M2;
  nuisance.shear_calibration_m[2] = M3;
  nuisance.shear_calibration_m[3] = M4;
  nuisance.shear_calibration_m[4] = M5;
  nuisance.shear_calibration_m[5] = M6;
  nuisance.shear_calibration_m[6] = M7;
  nuisance.shear_calibration_m[7] = M8;
  nuisance.shear_calibration_m[8] = M9;
  nuisance.shear_calibration_m[9] = M10;
}

int set_nuisance_shear_photoz(double SP1,double SP2,double SP3,double SP4,double SP5,double SP6,double SP7,double SP8,double SP9,double SP10,double SPS1)
{
  int i;
  nuisance.bias_zphot_shear[0]=SP1;
  nuisance.bias_zphot_shear[1]=SP2;
  nuisance.bias_zphot_shear[2]=SP3;
  nuisance.bias_zphot_shear[3]=SP4;
  nuisance.bias_zphot_shear[4]=SP5;
  nuisance.bias_zphot_shear[5]=SP6;
  nuisance.bias_zphot_shear[6]=SP7;
  nuisance.bias_zphot_shear[7]=SP8;
  nuisance.bias_zphot_shear[8]=SP9;
  nuisance.bias_zphot_shear[9]=SP10;

  for (i=0;i<tomo.shear_Nbin; i++){
    nuisance.sigma_zphot_shear[i]=SPS1;
    if (nuisance.sigma_zphot_shear[i]<0.001) return 0;
  }
  return 1;
}

int set_nuisance_clustering_photoz(double CP1,double CP2,double CP3,double CP4,double CP5,double CP6,double CP7,double CP8,double CP9,double CP10,double CPS1)
{
  int i;
  nuisance.bias_zphot_clustering[0]=CP1;
  nuisance.bias_zphot_clustering[1]=CP2;
  nuisance.bias_zphot_clustering[2]=CP3;
  nuisance.bias_zphot_clustering[3]=CP4;
  nuisance.bias_zphot_clustering[4]=CP5;
  nuisance.bias_zphot_clustering[5]=CP6;
  nuisance.bias_zphot_clustering[6]=CP7;
  nuisance.bias_zphot_clustering[7]=CP8;
  nuisance.bias_zphot_clustering[8]=CP9;
  nuisance.bias_zphot_clustering[9]=CP10;

  for (i=0;i<tomo.clustering_Nbin; i++){
    nuisance.sigma_zphot_clustering[i]=CPS1;
    if (nuisance.sigma_zphot_clustering[i]<0.001) return 0;
  }
  return 1;
}

int set_nuisance_ia(double A_ia, double beta_ia, double eta_ia, double eta_ia_highz, double LF_alpha, double LF_P, double LF_Q, double LF_red_alpha, double LF_red_P, double LF_red_Q)
{

  nuisance.A_ia=A_ia;
  nuisance.beta_ia=beta_ia;
  nuisance.eta_ia=eta_ia;
  nuisance.eta_ia_highz=eta_ia_highz;
  nuisance.LF_alpha=LF_alpha;
  nuisance.LF_P=LF_P;
  nuisance.LF_Q=LF_Q;
  nuisance.LF_red_alpha=LF_red_alpha;
  nuisance.LF_red_P=LF_red_P;
  nuisance.LF_red_Q=LF_red_Q;

  if (nuisance.A_ia < 0.0 || nuisance.A_ia > 10.0) return 0;
  if (nuisance.beta_ia < -1.0 || nuisance.beta_ia > 3.0) return 0;
  if (nuisance.eta_ia < -3.0 || nuisance.eta_ia> 3.0) return 0;
  if (nuisance.eta_ia_highz < -1.0 || nuisance.eta_ia_highz> 1.0) return 0;
  // printf("%le %le %le %le %le %le %le %le %le %le\n",nuisance.A_ia,nuisance.beta_ia,nuisance.eta_ia,nuisance.eta_ia_highz,nuisance.LF_alpha,nuisance.LF_P, nuisance.LF_Q,nuisance.LF_red_alpha,nuisance.LF_red_P,nuisance.LF_red_Q);
  // if(like.IA!=0){
  //  if (check_LF()) return 0;
  // }
return 1;
}

int set_nuisance_cluster_Mobs(double cluster_Mobs_lgN0,  double cluster_Mobs_alpha, double cluster_Mobs_beta, double cluster_Mobs_sigma0, double cluster_Mobs_sigma_qm, double cluster_Mobs_sigma_qz)
{
  //  nuisance.cluster_Mobs_lgM0 = mass_obs_norm;  //fiducial : 1.72+log(1.e+14*0.7); could use e.g. sigma = 0.2 Gaussian prior
  //  nuisance.cluster_Mobs_alpha = mass_obs_slope; //fiducial: 1.08; e.g. sigma = 0.1 Gaussian prior
  //  nuisance.cluster_Mobs_beta = mass_z_slope; //fiducial: 0.0; e.g. sigma = 0.1 Gaussian prior
  //  nuisance.cluster_Mobs_sigma = mass_obs_scatter; //fiducial 0.25; e.g. sigma = 0.05 Gaussian prior

  // fiducial values and priors from Murata et al. (2018) except for redshift-related parameters
  nuisance.cluster_Mobs_lgN0 = cluster_Mobs_lgN0; //fiducial: 3.207, flat prior [0.5, 5.0]
  nuisance.cluster_Mobs_alpha = cluster_Mobs_alpha; //fiducial: 0.993, flat prior [0.0, 2.0]
  nuisance.cluster_Mobs_beta = cluster_Mobs_beta; //fiducial: 0.0, flat prior [-1.5, 1.5]
  nuisance.cluster_Mobs_sigma0 = cluster_Mobs_sigma0; //fiducial: 0.456, flat prior [0.0, 1.5]
  nuisance.cluster_Mobs_sigma_qm = cluster_Mobs_sigma_qm; //fiducial: -0.169, flat prior [-1.5, 1.5]
  nuisance.cluster_Mobs_sigma_qz = cluster_Mobs_sigma_qz; //fiducial: 0.0, flat prior [-1.5, 1.5]

  if (nuisance.cluster_Mobs_lgN0 < 0.5 || nuisance.cluster_Mobs_lgN0 > 5.0) return 0;
  if (nuisance.cluster_Mobs_alpha < 0.0 || nuisance.cluster_Mobs_alpha > 2.0) return 0;
  if (nuisance.cluster_Mobs_beta < -1.5 || nuisance.cluster_Mobs_beta > 1.5) return 0;
  if (nuisance.cluster_Mobs_sigma0 < 0.0|| nuisance.cluster_Mobs_sigma0 > 1.5) return 0;
  if (nuisance.cluster_Mobs_sigma_qm < -1.5 && nuisance.cluster_Mobs_sigma_qm > 1.5) return 0;
  if (nuisance.cluster_Mobs_sigma_qz < -1.5 && nuisance.cluster_Mobs_sigma_qz > 1.5)return 0;

return 1;
}

int set_nuisance_gbias(double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8,double B9, double B10)
{
  int i;
  gbias.b[0] = B1;
  gbias.b[1] = B2;
  gbias.b[2] = B3;
  gbias.b[3] = B4;
  gbias.b[4] = B5;
  gbias.b[5] = B6;
  gbias.b[6] = B7;
  gbias.b[7] = B8;
  gbias.b[8] = B9;
  gbias.b[9] = B10;
  if(like.bias==1){
    for (i = 0; i < 10; i++){
      if (gbias.b[i] < 0.8 || gbias.b[i] > 3.0) return 0;
    }
  }
  return 1;
}

double log_L_SRD_SN_Y1_RENEE() // computed from /Users/teifler/Dropbox/cosmolike_store/LSSTawakens/ReneeSNChains/lsst_y1_jan29_nostarts_varyM_oldomb_rand.txt
{
  double log_L = 0.;
  double param_diff[4];
  //double inv[4][4]={{3.79965832e+03,-1.67422234e+01, 1.49107053e+03, 2.03605752e+02},{ -1.67422234e+01, 4.73633438e+02,-2.97547647e+00,-2.21008677e-01},{1.49107053e+03,-2.97547647e+00, 7.06767724e+02, 7.78147349e+01},{2.03605752e+02,-2.21008677e-01, 7.78147349e+01, 1.20866157e+01}};

  // double inv[4][4]={{2.54176679e+03,2.52990975e+01,8.90935426e+02,1.36402504e+02},{2.52990975e+01,4.81470268e+02,1.01904094e+01,6.95186662e-01},{8.90935426e+02,1.01904094e+01,4.02864434e+02,4.60275519e+01},{1.36402504e+02,6.95186662e-01,4.60275519e+01,8.41721305e+00}};

  double inv[4][4]={{2.92724944e+03,-2.18583441e+01, 1.31758412e+03, 1.57579361e+02},
 {-2.18583441e+01, 4.63427974e+02,-1.59603150e+01,-8.00922379e-01},
 {1.31758412e+03,-1.59603150e+01, 7.22127857e+02, 6.67792329e+01},
 {1.57579361e+02,-8.00922379e-01, 6.67792329e+01, 9.71021170e+00}};
  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.h0-0.6727;
  param_diff[2] = cosmology.w0+1.0;
  param_diff[3] = cosmology.wa;

  log_L = -0.5*do_matrix_mult_invcov(4,inv,param_diff);
  return log_L;
 }

double log_L_SRD_SN_Y10_RENEE() // computed from /Users/teifler/Dropbox/cosmolike_store/LSSTawakens/ReneeSNChains/lsst_y10_jan29_nostarts_varyM_oldomb_rand.txt
{
  double log_L = 0.;
  double param_diff[4];

double inv[4][4]={{1.13549508e+04,-7.56474778e+01,7.30717358e+03,5.90184798e+02},
 { -7.56474778e+01,4.91930625e+02,-4.39988764e+01,-7.28574193e+00},
 {7.30717358e+03,-4.39988764e+01,5.38324084e+03,3.24899498e+02},
 {5.90184798e+02,-7.28574193e+00,3.24899498e+02,3.84274454e+01}};

  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.h0-0.6727;
  param_diff[2] = cosmology.w0+1.0;
  param_diff[3] = cosmology.wa;

  log_L = -0.5*do_matrix_mult_invcov(4,inv, param_diff);
  return log_L;
 }

double log_L_SRD_SL_Y1_TOM() // computed from /Users/teifler/Dropbox/cosmolike_store/LSSTawakens/TomSLChains/SL_LSSTY1.txt
{
  double log_L = 0.;
  double param_diff[4];
  double inv[4][4]={{9.57953440e+01,1.90114212e+01,1.13561665e+00,2.33866782e+02},{1.90114212e+01,1.31631828e+01,-1.88534652e-02,1.45671419e+02},{1.13561665e+00,-1.88534652e-02,6.29296427e-01,-6.10433226e+00},{2.33866782e+02,1.45671419e+02,-6.10433226e+00,2.71769694e+03}};

  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.w0+1.0;
  param_diff[2] = cosmology.wa;
  param_diff[3] = cosmology.h0-0.6727;

  log_L = -0.5*do_matrix_mult_invcov(4,inv, param_diff);
  return log_L;
 }

double log_L_SRD_SL_Y10_TOM() // computed from /Users/teifler/Dropbox/cosmolike_store/LSSTawakens/TomSLChains/SL_LSSTY10.txt
{
  double log_L = 0.;
  double param_diff[4];
  double inv[4][4]={{2.44407663e+02,1.31525702e+02,1.62494864e+01,1.03889766e+03},{1.31525702e+02,1.90748959e+02,8.03881315e+00,1.99392208e+03},{1.62494864e+01,8.03881315e+00,2.23917868e+00,-6.64709076e+00},{1.03889766e+03,1.99392208e+03,-6.64709076e+00,3.31389425e+04}};

  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.w0+1.0;
  param_diff[2] = cosmology.wa;
  param_diff[3] = cosmology.h0-0.6727;

  log_L = -0.5*do_matrix_mult_invcov(4,inv, param_diff);
  return log_L;
 }

double log_L_SRD_LSST_Y1_clusters() // // including MOR for Y1, see fisher.py
{
  double log_L = 0.;
  double param_diff[7];
  double inv[7][7]={{3.858101e+04, 3.588867e+04, 2.231543e+03, -9.737292e+02, -2.076847e+02, -1.567836e+03, 5.165868e+02},{3.588867e+04, 4.046241e+04, 2.204910e+03, -4.306084e+02, -1.921012e+02, -1.153062e+03, 4.146891e+02},{2.231543e+03, 2.204910e+03, 4.640472e+02, -3.849041e+01, -1.164098e+01, -2.997845e+02, 8.606974e+01},{-9.737292e+02, -4.306084e+02, -3.849041e+01, 1.057829e+02, 1.234460e+01, 3.159550e+01, -1.021792e+01},{-2.076847e+02, -1.921012e+02, -1.164098e+01, 1.234460e+01, 3.965104e+00, 6.461167e+00, -2.324549e+00},{-1.567836e+03, -1.153062e+03, -2.997845e+02, 3.159550e+01, 6.461167e+00, 9.845550e+04, -2.104350e+02},{5.165868e+02, 4.146891e+02, 8.606974e+01, -1.021792e+01, -2.324549e+00, -2.104350e+02, 2.121782e+02}};

  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.sigma_8-0.831;
  param_diff[2] = cosmology.n_spec-0.9645;
  param_diff[3] = cosmology.w0+1.0;
  param_diff[4] = cosmology.wa;
  param_diff[5] = cosmology.omb-0.0491685;
  param_diff[6] = cosmology.h0-0.6727;

  log_L = -0.5*do_matrix_mult_invcov(7,inv, param_diff);
  return log_L;
 }

double log_L_SRD_LSST_Y10_clusters() // including MOR for Y10, see fisher.py
{
  double log_L = 0.;
  double param_diff[7];
  double inv[7][7]={{8.011883e+04, 7.501415e+04, 4.972257e+03, -1.009514e+03, -2.332928e+02, -4.035026e+03, 1.280726e+03},{7.501415e+04, 9.212235e+04, 5.029562e+03, 1.256229e+03, -8.904458e+00, -3.884022e+03, 1.236633e+03},{4.972257e+03, 5.029562e+03, 8.996244e+02, -1.335280e+01, -1.271912e+01, -9.151661e+02, 2.624954e+02},{-1.009514e+03, 1.256229e+03, -1.335280e+01, 3.943470e+02, 5.659304e+01, -3.776163e+01, 7.168167e+00},{-2.332928e+02, -8.904458e+00, -1.271912e+01, 5.659304e+01, 1.167193e+01, 4.161086e+00, -1.872393e+00},{-4.035026e+03, -3.884022e+03, -9.151661e+02, -3.776163e+01, 4.161086e+00, 1.000908e+05, -6.424232e+02},{1.280726e+03, 1.236633e+03, 2.624954e+02, 7.168167e+00, -1.872393e+00, -6.424232e+02, 3.273443e+02}};

  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.sigma_8-0.831;
  param_diff[2] = cosmology.n_spec-0.9645;
  param_diff[3] = cosmology.w0+1.0;
  param_diff[4] = cosmology.wa;
  param_diff[5] = cosmology.omb-0.0491685;
  param_diff[6] = cosmology.h0-0.6727;

  log_L = -0.5*do_matrix_mult_invcov(7,inv, param_diff);
  return log_L;
 }

double log_L_SRD_stage3_exceptw0wa()
{
  double log_L = 0.;
  double param_diff[7];
  double inv[7][7]={{8.52388933e+03,-2.38254882e-03,4.32405053e+00,0.00000000e+00,0.00000000e+00,1.22554260e+02,5.48470403e+01},{-2.38254882e-03,3.34465215e+01,3.86664237e-01,0.00000000e+00,0.00000000e+00,-8.71498515e+01,-7.09315084e+00},{4.32405053e+00,3.86664237e-01,2.47843770e+02,0.00000000e+00,0.00000000e+00,-1.21079947e+02,4.07696833e+00},{0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00},{0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00},{1.22554260e+02,-8.71498515e+01,-1.21079947e+02,-0.00000000e+00,-0.00000000e+00,7.17126110e+04,3.07891510e+03},{5.48470403e+01,-7.09315084e+00,4.07696833e+00,0.00000000e+00,0.00000000e+00,3.07891510e+03,5.31816639e+02}};

  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.sigma_8-0.831;
  param_diff[2] = cosmology.n_spec-0.9645;
  param_diff[3] = cosmology.w0+1.0;
  param_diff[4] = cosmology.wa;
  param_diff[5] = cosmology.omb-0.0491685;
  param_diff[6] = cosmology.h0-0.6727;

  log_L = -0.5*do_matrix_mult_invcov(7,inv, param_diff);
  return log_L;
 }

double log_L_SRD_stage3()
{
  double log_L = 0.;
  double param_diff[7];
  double inv[7][7]={{8.52889541e+03,9.34266060e-03,2.21513407e+00,1.85008170e+01,6.12185662e+00,1.17720010e+02,5.30670581e+01},{9.34266060e-03,3.34475078e+01,3.79135616e-01,-8.02192934e-02,5.88736315e-02,-8.69202865e+01,-7.08029577e+00},{2.21513407e+00,3.79135616e-01,2.48739187e+02,-7.46022981e+00,-2.69922217e+00,-1.19693897e+02,4.78085262e+00},{1.85008170e+01,-8.02192934e-02,-7.46022981e+00,8.42937127e+01,1.68856256e+01,-4.89063661e+01,-8.77194357e+00},{6.12185662e+00,5.88736315e-02,-2.69922217e+00,1.68856256e+01,9.55489400e+00,5.27704214e+00,-1.38597499e+00},{1.17720010e+02,-8.69202865e+01,-1.19693897e+02,-4.89063661e+01,5.27704214e+00,7.17777988e+04,3.08491105e+03},{5.30670581e+01,-7.08029577e+00,4.78085262e+00,-8.77194357e+00,-1.38597499e+00,3.08491105e+03,5.32751808e+02}};

  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.sigma_8-0.831;
  param_diff[2] = cosmology.n_spec-0.9645;
  param_diff[3] = cosmology.w0+1.0;
  param_diff[4] = cosmology.wa;
  param_diff[5] = cosmology.omb-0.0491685;
  param_diff[6] = cosmology.h0-0.6727;

  log_L = -0.5*do_matrix_mult_invcov(7,inv, param_diff);
  return log_L;
 }

double log_L_SRD_LSST_Y1_3x2pt()
{
  double log_L = 0.;
  double param_diff[7];
  double inv[7][7]={{6.461019e+05, 4.105098e+05, 9.063469e+04, -2.968463e+04, -6.656297e+03, -3.266829e+05, 7.947019e+04},{4.105098e+05, 3.102396e+05, 4.061214e+04, -2.051833e+04, -5.069455e+03, -1.156574e+05, 2.911602e+04},{9.063469e+04, 4.061214e+04, 3.652491e+04, -1.033492e+03, -1.571074e+02, -9.436089e+04, 2.396154e+04},{-2.968463e+04, -2.051833e+04, -1.033492e+03, 2.081364e+03, 4.721662e+02, 7.414854e+03, -1.864708e+03},{-6.656297e+03, -5.069455e+03, -1.571074e+02, 4.721662e+02, 1.200287e+02, 1.340576e+03, -3.293614e+02},{-3.266829e+05, -1.156574e+05, -9.436089e+04, 7.414854e+03, 1.340576e+03, 5.388793e+05, -9.884641e+04},{7.947019e+04, 2.911602e+04, 2.396154e+04, -1.864708e+03, -3.293614e+02, -9.884641e+04, 2.373400e+04}};

  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.sigma_8-0.831;
  param_diff[2] = cosmology.n_spec-0.9645;
  param_diff[3] = cosmology.w0+1.0;
  param_diff[4] = cosmology.wa;
  param_diff[5] = cosmology.omb-0.0491685;
  param_diff[6] = cosmology.h0-0.6727;

  log_L = -0.5*do_matrix_mult_invcov(7,inv, param_diff);
  return log_L;
 }

double log_L_SRD_LSST_Y10_3x2pt()
{
  double log_L = 0.;
  double param_diff[7];
  double inv[7][7]={{1.732125e+06, 1.363116e+06, 2.420885e+05, -6.384395e+04, -1.991510e+04, -7.567872e+05, 1.858361e+05},{1.363116e+06, 1.196537e+06, 1.469326e+05, -5.148233e+04, -1.796618e+04, -4.526364e+05, 1.126267e+05},{2.420885e+05, 1.469326e+05, 1.011204e+05, -1.370858e+03, -4.391526e+02, -2.001808e+05, 5.228884e+04},{-6.384395e+04, -5.148233e+04, -1.370858e+03, 4.153843e+03, 1.137490e+03, 1.286287e+04, -3.253747e+03},{-1.991510e+04, -1.796618e+04, -4.391526e+02, 1.137490e+03, 3.720224e+02, 4.198170e+03, -1.020253e+03},{-7.567872e+05, -4.526364e+05, -2.001808e+05, 1.286287e+04, 4.198170e+03, 8.507285e+05, -1.685201e+05},{1.858361e+05, 1.126267e+05, 5.228884e+04, -3.253747e+03, -1.020253e+03, -1.685201e+05, 4.064231e+04}};

  param_diff[0] = cosmology.Omega_m-0.3156;
  param_diff[1] = cosmology.sigma_8-0.831;
  param_diff[2] = cosmology.n_spec-0.9645;
  param_diff[3] = cosmology.w0+1.0;
  param_diff[4] = cosmology.wa;
  param_diff[5] = cosmology.omb-0.0491685;
  param_diff[6] = cosmology.h0-0.6727;

  log_L = -0.5*do_matrix_mult_invcov(7,inv, param_diff);
  return log_L;
 }

double log_L_DESI()
{
  double log_L = 0.;
  double param_diff[2];
  double inv[2][2]={{1411.40888852,449.46652105},{449.46652105,   291.94320759}};

  param_diff[0] = cosmology.w0+1.0;
  param_diff[1] = cosmology.wa;

  log_L = -0.5*do_matrix_mult_invcov(2,inv, param_diff);
  return log_L;
 }

double scatter(double N200, double z)
{
  double lnM, M, a;
  lnM = 1./nuisance.cluster_Mobs_alpha*(log(N200)-nuisance.cluster_Mobs_lgN0-nuisance.cluster_Mobs_beta*log(1.+z));
  M = exp(lnM)*3.e14;
  a = 1./(1.+z);
  return scatter_lgN200_model_mz(M, a);
}

double scatter_fid(double N200, double z)
{
  double cluster_Mobs_lgN0 = 3.207;
  double cluster_Mobs_alpha = 0.993;
  double cluster_Mobs_beta = 0.0;
  double cluster_Mobs_sigma0 = 0.456;
  double cluster_Mobs_sigma_qm = 0.0; // SRD suggestion from Eduardo
//  double cluster_Mobs_sigma_qm = -0.169;
  double cluster_Mobs_sigma_qz = 0.0;

  double lnM, M, a;
  lnM = 1./cluster_Mobs_alpha*(log(N200)-cluster_Mobs_lgN0-cluster_Mobs_beta*log(1.+z));
  M = exp(lnM)*3.e14;
  a = 1./(1.+z);
  return cluster_Mobs_sigma0 + log(M/(3.e+14))*cluster_Mobs_sigma_qm + log(1./a)*cluster_Mobs_sigma_qz;
}

double log_L_SRD_LSST_CL_EDUARDO() // values for scatter function are "arbitrary" but based on some experience (Hironao/Eduarod)
{
  double log_L = 0.;
  double s_100_0p2 = scatter(100., 0.2);
  double s_30_0p2 = scatter(30., 0.2);
  double s_100_0p8 = scatter(100., 0.8); // remove if redshift dependent scatter is excluded in analysis
  double f_100_0p2 = scatter_fid(100., 0.2);
  double f_30_0p2 = scatter_fid(30., 0.2);
  double f_100_0p8 = scatter_fid(100., 0.8); // remove if redshift dependent scatter is excluded in analysis
  double sig = 0.1; // 0.05 weighting the giants

  log_L = -0.5*(exp(pow((s_100_0p2-f_100_0p2)/sig, 2.0))+ exp(pow((s_30_0p2-f_30_0p2)/sig, 2.0)) + exp(pow((s_100_0p8-f_100_0p8)/sig, 2.0)));

  return log_L;
}

double log_multi_like(double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu, double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8, double B9, double B10, double SP1, double SP2, double SP3, double SP4, double SP5, double SP6, double SP7, double SP8, double SP9, double SP10, double SPS1, double CP1, double CP2, double CP3, double CP4, double CP5, double CP6, double CP7, double CP8, double CP9, double CP10, double CPS1, double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10, double A_ia, double beta_ia, double eta_ia, double eta_ia_highz, double LF_alpha, double LF_P, double LF_Q, double LF_red_alpha, double LF_red_P, double LF_red_Q, double mass_obs_norm, double mass_obs_slope, double mass_z_slope, double mass_obs_scatter_norm, double mass_obs_scatter_mass_slope, double mass_obs_scatter_z_slope)
{
  int i,j,k,m=0,l;
  static double *pred;
  static double *ell;
  static double *ell_Cluster;
  static double darg;
  double chisqr,a,log_L_prior=0.0, log_L_GRS=0.0;

  if(ell==0){
    pred= create_double_vector(0, like.Ndata-1);
    ell= create_double_vector(0, like.Ncl-1);
    darg=(log(like.lmax)-log(like.lmin))/like.Ncl;
    for (l=0;l<like.Ncl;l++){
      ell[l]=exp(log(like.lmin)+(l+0.5)*darg);
    }
    ell_Cluster= create_double_vector(0, Cluster.lbin-1);
    darg=(log(Cluster.l_max)-log(Cluster.l_min))/Cluster.lbin;
    for (l=0;l<Cluster.lbin;l++){
      ell_Cluster[l]=exp(log(Cluster.l_min)+(l+0.5)*darg);
    }
  }
  if (set_cosmology_params(OMM,S8,NS,W0,WA,OMB,H0,MGSigma,MGmu)==0){
    printf("Cosmology out of bounds\n");
    return -1.0e8;
  }
  set_nuisance_shear_calib(M1,M2,M3,M4,M5,M6,M7,M8,M9,M10);
  if (set_nuisance_shear_photoz(SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,SP9,SP10,SPS1)==0){
    printf("Shear photo-z sigma too small\n");
    return -1.0e8;
  }
  if (set_nuisance_clustering_photoz(CP1,CP2,CP3,CP4,CP5,CP6,CP7,CP8,CP9,CP10,CPS1)==0){
    printf("Clustering photo-z sigma too small\n");
    return -1.0e8;
  }
  if (set_nuisance_ia(A_ia,beta_ia,eta_ia,eta_ia_highz,LF_alpha,LF_P,LF_Q,LF_red_alpha,LF_red_P,LF_red_Q)==0){
    printf("IA parameters out of bounds\n");
    return -1.0e8;
  }
  if (set_nuisance_gbias(B1,B2,B3,B4,B5,B6,B7,B8,B9,B10)==0){
    printf("Bias out of bounds\n");
    return -1.0e8;
  }
  if (set_nuisance_cluster_Mobs(mass_obs_norm, mass_obs_slope, mass_z_slope, mass_obs_scatter_norm, mass_obs_scatter_mass_slope, mass_obs_scatter_z_slope)==0){
    printf("Mobs out of bounds\n");
    return -1.0e8;
  }

  //printf("like %le %le %le %le %le %le %le %le\n",cosmology.Omega_m, cosmology.Omega_v,cosmology.sigma_8,cosmology.n_spec,cosmology.w0,cosmology.wa,cosmology.omb,cosmology.h0);
  // printf("like %le %le %le %le\n",gbias.b[0][0], gbias.b[1][0], gbias.b[2][0], gbias.b[3][0]);
  // for (i=0; i<10; i++){
  //   printf("nuisance %le %le %le\n",nuisance.shear_calibration_m[i],nuisance.bias_zphot_shear[i],nuisance.sigma_zphot_shear[i]);
  // }

  log_L_prior=0.0;
  // if(like.Aubourg_Planck_BAO_SN==1) log_L_prior+=log_L_Planck_BAO_SN();
  // if(like.SN==1) log_L_prior+=log_L_SN();
  // if(like.BAO==1) log_L_prior+=log_L_BAO();
  // if(like.Planck==1) log_L_prior+=log_L_Planck();
  // if(like.IA!=0) log_L_prior+=log_L_ia();
  // if(like.IA!=0) log_L_prior+=log_like_f_red();
  // if(like.wlphotoz!=0) log_L_prior+=log_L_wlphotoz();
  // if(like.clphotoz!=0) log_L_prior+=log_L_clphotoz();
  // if(like.shearcalib==1) log_L_prior+=log_L_shear_calib();
  // if(like.clusterMobs==1) log_L_prior+=log_L_clusterMobs();

  // if(like.SRD_SN_Y10==1) log_L_prior+=log_L_SRD_SN_Y10_RENEE();
  // if(like.SRD_SN_Y1==1) log_L_prior+=log_L_SRD_SN_Y1_RENEE();
  // if(like.SRD_SL_Y10==1) log_L_prior+=log_L_SRD_SL_Y10_TOM();
  // if(like.SRD_SL_Y1==1) log_L_prior+=log_L_SRD_SL_Y1_TOM();

  // log_L_prior+=log_L_DESI();

  if(like.SRD==1 || like.SRD==6 || like.SRD==11) log_L_prior+=log_L_SRD_stage3();
  if(like.SRD==2 || like.SRD==6) log_L_prior+=1/(1+0.7*0.7)*log_L_SRD_SN_Y10_RENEE();
  if(like.SRD==3 || like.SRD==6) log_L_prior+=1/(1+0.62*0.62)*log_L_SRD_SL_Y10_TOM();
  if(like.SRD==4 || like.SRD==6) log_L_prior+=14202.63/18000.0*1/(1+0.62*0.62)*log_L_SRD_LSST_Y10_3x2pt();
  if(like.SRD==5 || like.SRD==6) log_L_prior+=14202.63/18000.0*1/(1+0.62*0.62)*log_L_SRD_LSST_Y10_clusters();

  if(like.SRD==7 || like.SRD==11) log_L_prior+=1/(1+0.7*0.7)*log_L_SRD_SN_Y1_RENEE();
  if(like.SRD==8 || like.SRD==11) log_L_prior+=1/(1+0.62*0.62)*log_L_SRD_SL_Y1_TOM();
  if(like.SRD==9 || like.SRD==11) log_L_prior+=11808.28/18000.0*1/(1+0.62*0.62)*log_L_SRD_LSST_Y1_3x2pt();
  if(like.SRD==10 || like.SRD==11) log_L_prior+=11808.28/18000.0*1/(1+0.62*0.62)*log_L_SRD_LSST_Y1_clusters();

  // printf("%d %d %d %d\n",like.BAO,like.wlphotoz,like.clphotoz,like.shearcalib);
  // printf("logl %le %le %le %le\n",log_L_shear_calib(),log_L_wlphotoz(),log_L_clphotoz(),log_L_clusterMobs());
  // int start=0;

  // if(like.shear_shear==1) {
  //   set_data_shear(like.Ncl, ell, pred, start);
  //   start=start+like.Ncl*tomo.shear_Npowerspectra;
  // }
  // if(like.shear_pos==1){
  //   set_data_ggl(like.Ncl, ell, pred, start);
  //   start=start+like.Ncl*tomo.ggl_Npowerspectra;
  // }
  // if(like.pos_pos==1){
  //   set_data_clustering(like.Ncl,ell,pred, start);
  //   start=start+like.Ncl*tomo.clustering_Npowerspectra;
  // }
  // if(like.clusterN==1){
  //   set_data_cluster_N(pred,start);
  //   start=start+tomo.cluster_Nbin*Cluster.N200_Nbin;
  // }
  // if(like.clusterWL==1){
  //   set_data_cgl(ell_Cluster,pred, start);
  // }
  chisqr=0.0;
  // for (i=0; i<like.Ndata; i++){
  //   for (j=0; j<like.Ndata; j++){
  //     a=(pred[i]-data_read(1,i))*invcov_read(1,i,j)*(pred[j]-data_read(1,j));
  //     chisqr=chisqr+a;
  //   }
  //  //  if (fabs(data_read(1,i)/pred[i]-1.0) >1.e-4){
  //  // printf("%d %le %le %le\n",i,data_read(1,i),pred[i],data_read(1,i)/pred[i]);
  //  //  }
  // }
  // if (chisqr<0.0){
  //   printf("errror: chisqr < 0\n");
  // }
  // if (chisqr<-1.0) exit(EXIT_FAILURE);

  return -0.5*chisqr+log_L_prior;
}

void compute_data_vector(char *details, double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu, double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8, double B9, double B10, double SP1, double SP2, double SP3, double SP4, double SP5, double SP6, double SP7, double SP8, double SP9, double SP10, double SPS1, double CP1, double CP2, double CP3, double CP4, double CP5, double CP6, double CP7, double CP8, double CP9, double CP10, double CPS1, double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10, double A_ia, double beta_ia, double eta_ia, double eta_ia_highz, double LF_alpha, double LF_P, double LF_Q, double LF_red_alpha, double LF_red_P, double LF_red_Q, double mass_obs_norm, double mass_obs_slope, double mass_z_slope, double mass_obs_scatter_norm, double mass_obs_scatter_mass_slope, double mass_obs_scatter_z_slope)
{

  int i,j,k,m=0,l;
  static double *pred;
  static double *ell;
  static double *ell_Cluster;
  static double darg;
  double chisqr,a,log_L_prior=0.0;

  if(ell==0){
    pred= create_double_vector(0, like.Ndata-1);
    ell= create_double_vector(0, like.Ncl-1);
    darg=(log(like.lmax)-log(like.lmin))/like.Ncl;
    for (l=0;l<like.Ncl;l++){
      ell[l]=exp(log(like.lmin)+(l+0.5)*darg);
    }
    ell_Cluster= create_double_vector(0, Cluster.lbin-1);
    darg=(log(Cluster.l_max)-log(Cluster.l_min))/Cluster.lbin;
    for (l=0;l<Cluster.lbin;l++){
      ell_Cluster[l]=exp(log(Cluster.l_min)+(l+0.5)*darg);
    }
  }
// for (l=0;l<like.Ncl;l++){
//   printf("%d %le\n",i,ell[l]);
// }
  set_cosmology_params(OMM,S8,NS,W0,WA,OMB,H0,MGSigma,MGmu);
  set_nuisance_shear_calib(M1,M2,M3,M4,M5,M6,M7,M8,M9,M10);
  set_nuisance_shear_photoz(SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,SP9,SP10,SPS1);
  set_nuisance_clustering_photoz(CP1,CP2,CP3,CP4,CP5,CP6,CP7,CP8,CP9,CP10,CPS1);
  set_nuisance_ia(A_ia,beta_ia,eta_ia,eta_ia_highz,LF_alpha,LF_P,LF_Q,LF_red_alpha,LF_red_P,LF_red_Q);
  set_nuisance_gbias(B1,B2,B3,B4,B5,B6,B7,B8,B9,B10);
  set_nuisance_cluster_Mobs(mass_obs_norm, mass_obs_slope, mass_z_slope, mass_obs_scatter_norm, mass_obs_scatter_mass_slope, mass_obs_scatter_z_slope);

  int start=0;
  if(like.shear_shear==1) {
    set_data_shear(like.Ncl, ell, pred, start);
    start=start+like.Ncl*tomo.shear_Npowerspectra;
  }
  if(like.shear_pos==1){
    //printf("ggl\n");
    set_data_ggl(like.Ncl, ell, pred, start);
    start=start+like.Ncl*tomo.ggl_Npowerspectra;
  }
  if(like.pos_pos==1){
    //printf("clustering\n");
    set_data_clustering(like.Ncl,ell,pred, start);
    start=start+like.Ncl*tomo.clustering_Npowerspectra;
  }

  if(like.clusterN==1){
    set_data_cluster_N(pred,start);
    start= start+tomo.cluster_Nbin*Cluster.N200_Nbin;
  }
  if(like.clusterWL==1){
    set_data_cgl(ell_Cluster,pred, start);
  }
  FILE *F;
  char filename[300];
  if (strstr(details,"FM") != NULL){
    sprintf(filename,"%s",details);
  }
  else {sprintf(filename,"datav/%s_%s",like.probes,details);}
  F=fopen(filename,"w");
  for (i=0;i<like.Ndata; i++){
    fprintf(F,"%d %le\n",i,pred[i]);
    //printf("%d %le\n",i,pred[i]);
  }
  fclose(F);
}

double write_vector_wrapper(char *details, input_cosmo_params ic, input_nuisance_params in)
{
  compute_data_vector(details, ic.omega_m, ic.sigma_8, ic.n_s, ic.w0, ic.wa, ic.omega_b, ic.h0, ic.MGSigma, ic.MGmu,
    in.bias[0], in.bias[1], in.bias[2], in.bias[3],in.bias[4], in.bias[5], in.bias[6], in.bias[7],in.bias[8], in.bias[9],
    in.source_z_bias[0], in.source_z_bias[1], in.source_z_bias[2], in.source_z_bias[3], in.source_z_bias[4],
    in.source_z_bias[5], in.source_z_bias[6], in.source_z_bias[7], in.source_z_bias[8], in.source_z_bias[9],
    in.source_z_s,
    in.lens_z_bias[0], in.lens_z_bias[1], in.lens_z_bias[2], in.lens_z_bias[3], in.lens_z_bias[4],
    in.lens_z_bias[5], in.lens_z_bias[6], in.lens_z_bias[7], in.lens_z_bias[8], in.lens_z_bias[9],
    in.lens_z_s,
    in.shear_m[0], in.shear_m[1], in.shear_m[2], in.shear_m[3], in.shear_m[4],
    in.shear_m[5], in.shear_m[6], in.shear_m[7], in.shear_m[8], in.shear_m[9],
    in.A_ia, in.beta_ia, in.eta_ia, in.eta_ia_highz,
    in.lf[0], in.lf[1], in.lf[2], in.lf[3], in.lf[4], in.lf[5],
    in.m_lambda[0], in.m_lambda[1], in.m_lambda[2], in.m_lambda[3],
    in.m_lambda[4], in.m_lambda[5]);
  return 0;
}

double log_like_wrapper(input_cosmo_params ic, input_nuisance_params in)
{
  double like = log_multi_like(ic.omega_m, ic.sigma_8, ic.n_s, ic.w0, ic.wa, ic.omega_b, ic.h0, ic.MGSigma, ic.MGmu,

    in.bias[0], in.bias[1], in.bias[2], in.bias[3],in.bias[4], in.bias[5], in.bias[6], in.bias[7],in.bias[8], in.bias[9],
    in.source_z_bias[0], in.source_z_bias[1], in.source_z_bias[2], in.source_z_bias[3], in.source_z_bias[4],
    in.source_z_bias[5], in.source_z_bias[6], in.source_z_bias[7], in.source_z_bias[8], in.source_z_bias[9],
    in.source_z_s,
    in.lens_z_bias[0], in.lens_z_bias[1], in.lens_z_bias[2], in.lens_z_bias[3], in.lens_z_bias[4],
    in.lens_z_bias[5], in.lens_z_bias[6], in.lens_z_bias[7], in.lens_z_bias[8], in.lens_z_bias[9],
    in.lens_z_s,
    in.shear_m[0], in.shear_m[1], in.shear_m[2], in.shear_m[3], in.shear_m[4],
    in.shear_m[5], in.shear_m[6], in.shear_m[7], in.shear_m[8], in.shear_m[9],
    in.A_ia, in.beta_ia, in.eta_ia, in.eta_ia_highz,
    in.lf[0], in.lf[1], in.lf[2], in.lf[3], in.lf[4], in.lf[5],
    in.m_lambda[0], in.m_lambda[1], in.m_lambda[2], in.m_lambda[3],
    in.m_lambda[4], in.m_lambda[5]);
  return like;
}

 int main(int argc, char** argv)
{
  int hit=atoi(argv[1]);
  char filename[500],arg1[400],arg2[400];
  int t;
  t=hit;
  double area_table[12]={7500.0,13000.0,16000.0,10000.0,15000.0,20000.0,10000.0,15000.0,20000.0,10000.0,15000.0,20000.0};
  double nsource_table[12]={9.8,12.1,15.1,15.1,18.9,23.5,20.3,23.5,26.9,26.9,30.8,35.0};
  double nlens_table[12]={15.0,20.0,25.0,25.0,32.0,41.0,35.0,41.0,48.0,48.0,57.0,67.0};

  char survey_designation[12][200]={"LSST_Y1","LSST_Y1","LSST_Y1","LSST_Y3","LSST_Y3","LSST_Y3","LSST_Y6","LSST_Y6","LSST_Y6","LSST_Y10","LSST_Y10","LSST_Y10"};
  char source_zfile[12][400]={"WL_zdistri_model0_z0=1.940000e-01_alpha=8.830000e-01","WL_zdistri_model1_z0=1.900000e-01_alpha=8.620000e-01","WL_zdistri_model2_z0=1.860000e-01_alpha=8.410000e-01","WL_zdistri_model3_z0=1.860000e-01_alpha=8.410000e-01","WL_zdistri_model4_z0=1.830000e-01_alpha=8.210000e-01","WL_zdistri_model5_z0=1.790000e-01_alpha=8.000000e-01","WL_zdistri_model6_z0=1.810000e-01_alpha=8.140000e-01","WL_zdistri_model7_z0=1.790000e-01_alpha=8.000000e-01","WL_zdistri_model8_z0=1.760000e-01_alpha=7.860000e-01","WL_zdistri_model9_z0=1.760000e-01_alpha=7.860000e-01","WL_zdistri_model10_z0=1.740000e-01_alpha=7.720000e-01","WL_zdistri_model11_z0=1.710000e-01_alpha=7.590000e-01"};
  char lens_zfile[12][400]={"LSS_zdistri_model0_z0=2.590000e-01_alpha=9.520000e-01","LSS_zdistri_model1_z0=2.610000e-01_alpha=9.370000e-01","LSS_zdistri_model2_z0=2.640000e-01_alpha=9.250000e-01","LSS_zdistri_model3_z0=2.640000e-01_alpha=9.250000e-01","LSS_zdistri_model4_z0=2.680000e-01_alpha=9.150000e-01","LSS_zdistri_model5_z0=2.740000e-01_alpha=9.070000e-01","LSS_zdistri_model6_z0=2.700000e-01_alpha=9.120000e-01","LSS_zdistri_model7_z0=2.740000e-01_alpha=9.070000e-01","LSS_zdistri_model8_z0=2.780000e-01_alpha=9.030000e-01","LSS_zdistri_model9_z0=2.780000e-01_alpha=9.030000e-01","LSS_zdistri_model10_z0=2.830000e-01_alpha=9.000000e-01","LSS_zdistri_model11_z0=2.880000e-01_alpha=8.980000e-01"};
  int Ntomo_lens[12]={5,5,5,7,7,7,9,9,9,10,10,10};

  init_cosmo();
  init_fisher_precision();
  init_binning_fourier(20,20.0,15000.0,3000.0,21.0,5,Ntomo_lens[t]);
  init_survey(survey_designation[t]);
  sprintf(arg1,"zdistris/%s",source_zfile[t]);
  sprintf(arg2,"zdistris/%s",lens_zfile[t]);
  init_galaxies(arg1,arg2,"gaussian","gaussian","SRD");
  init_clusters();
  init_IA("NLA_HF", "GAMA");
  init_probes("3x2pt_clusterN_clusterWL");
  init_priors("none","none","none","none");

  sprintf(filename,"%s_area%le_ng%le_nl%le",survey_designation[t],area_table[t],nsource_table[t],nlens_table[t]);
  if(strstr(survey_designation[t],"LSST_Y1") != NULL) compute_data_vector(filename,0.3156,0.831,0.9645,-1.,0.,0.0491685,0.6727,0.,0.,1.413566e+00,1.567919e+00,1.731037e+00,1.900583e+00,2.074809e+00,1.413566e+00,1.567919e+00,1.731037e+00,1.900583e+00,2.074809e+00,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.05,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.03,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,5.92,1.1,-0.47,0.0,0.0,0.0,0.0,0.0,0.0,0.0,3.207,0.993,0.0,0.456,0.0,0.0);

  if(strstr(survey_designation[t],"LSST_Y3") != NULL) compute_data_vector(filename,0.3156,0.831,0.9645,-1.,0.,0.0491685,0.6727,0.,0.,1.392398e+00,1.500535e+00,1.613747e+00,1.731037e+00,1.851600e+00,1.974761e+00,2.100003e+00,1.731037e+00,1.900583e+00,2.074809e+00,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.05,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.03,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,5.92,1.1,-0.47,0.0,0.0,0.0,0.0,0.0,0.0,0.0,3.207,0.993,0.0,0.456,0.0,0.0);

  if(strstr(survey_designation[t],"LSST_Y6") != NULL) compute_data_vector(filename,0.3156,0.831,0.9645,-1.,0.,0.0491685,0.6727,0.,0.,1.380752e+00,1.463865e+00,1.550281e+00,1.639495e+00,1.731037e+00,1.824565e+00,1.919738e+00,2.016299e+00,2.114025e+00,2.074809e+00,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.05,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.03,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,5.92,1.1,-0.47,0.0,0.0,0.0,0.0,0.0,0.0,0.0,3.207,0.993,0.0,0.456,0.0,0.0);

  if(strstr(survey_designation[t],"LSST_Y10") != NULL) compute_data_vector(filename,0.3156,0.831,0.9645,-1.,0.,0.0491685,0.6727,0.,0.,1.376695e+00,1.451179e+00,1.528404e+00,1.607983e+00,1.689579e+00,1.772899e+00,1.857700e+00,1.943754e+00,2.030887e+00,2.118943e+00,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.05,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.03,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,5.92,1.1,-0.47,0.0,0.0,0.0,0.0,0.0,0.0,0.0,3.207,0.993,0.0,0.456,0.0,0.0);
  return 0;
}


/** \file IRLS.h
*
* `IRLS' is a C++ implementation of the IRLS algorithm for GLM
* Copyright (C) 2013 Xioaquan Wen, Timothee Flutre
*
* 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, see <http://www.gnu.org/licenses/>.
*/

#ifndef _IRLS_H_
#define _IRLS_H_

#include <vector>

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

#include "LinkFunc.h"

class IRLS
{
private:
  gsl_vector * y;      // response vector
  gsl_matrix * X;      // design matrix
  gsl_vector * offset; // offset vector
  bool free_data;      // depends on load_data() or set_data()

  size_t n;            // sample size;
  size_t p;            // number of parameters (including intercept)
  size_t rank;         // of X (useful for p-values)

  gsl_vector * bv;     // vector of estimated effect sizes
  gsl_matrix * VB;     // covariance matrix of estimated effect sizes
  double psi;          // dispersion

public:

  LinkFunc * link;

  IRLS(const char * link_type);
  ~IRLS();
  void load_data(const std::vector<double> & yv,
		 const std::vector<std::vector<double> > &Xv,
		 const std::vector<double> & offv);
  void set_data(gsl_vector * yv,
		gsl_matrix * Xv,
		gsl_vector * offv);
  void fit_model();
  std::vector<double> get_coef();
  std::vector<double> get_stderr();
  size_t get_rank_X() { return rank; };
  double get_dispersion() { return psi; };

private:
  void compute_variance(gsl_vector *w);

}; // IRLS

#endif // _IRLS_H_


/* ode-initval/odeiv2.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 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.
 */

/* Author:  G. Jungman */
/* Modified by Tuomo Keskitalo */

#ifndef __GSL_ODEIV2_H__
#define __GSL_ODEIV2_H__

#include <stdio.h>
#include <stdlib.h>
#include <gsl/gsl_types.h>

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

__BEGIN_DECLS
/* Description of a system of ODEs.
 *
 * y' = f(t,y) = dydt(t, y)
 *
 * The system is specified by giving the right-hand-side
 * of the equation and possibly a jacobian function.
 *
 * Some methods require the jacobian function, which calculates
 * the matrix dfdy and the vector dfdt. The matrix dfdy conforms
 * to the GSL standard, being a continuous range of floating point
 * values, in row-order.
 *
 * As with GSL function objects, user-supplied parameter
 * data is also present.
 */
  typedef struct
{
  int (*function) (double t, const double y[], double dydt[], void *params);
  int (*jacobian) (double t, const double y[], double *dfdy, double dfdt[],
                   void *params);
  size_t dimension;
  void *params;
}
gsl_odeiv2_system;

/* Function evaluation macros */

#define GSL_ODEIV_FN_EVAL(S,t,y,f)  (*((S)->function))(t,y,f,(S)->params)
#define GSL_ODEIV_JA_EVAL(S,t,y,dfdy,dfdt)  (*((S)->jacobian))(t,y,dfdy,dfdt,(S)->params)

/* Type definitions */

typedef struct gsl_odeiv2_step_struct gsl_odeiv2_step;
typedef struct gsl_odeiv2_control_struct gsl_odeiv2_control;
typedef struct gsl_odeiv2_evolve_struct gsl_odeiv2_evolve;
typedef struct gsl_odeiv2_driver_struct gsl_odeiv2_driver;

/* Stepper object
 *
 * Opaque object for stepping an ODE system from t to t+h.
 * In general the object has some state which facilitates
 * iterating the stepping operation.
 */

typedef struct
{
  const char *name;
  int can_use_dydt_in;
  int gives_exact_dydt_out;
  void *(*alloc) (size_t dim);
  int (*apply) (void *state, size_t dim, double t, double h, double y[],
                double yerr[], const double dydt_in[], double dydt_out[],
                const gsl_odeiv2_system * dydt);
  int (*set_driver) (void *state, const gsl_odeiv2_driver * d);
  int (*reset) (void *state, size_t dim);
  unsigned int (*order) (void *state);
  void (*free) (void *state);
}
gsl_odeiv2_step_type;

struct gsl_odeiv2_step_struct
{
  const gsl_odeiv2_step_type *type;
  size_t dimension;
  void *state;
};

/* Available stepper types */

GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_rk2;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_rk4;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_rkf45;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_rkck;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_rk8pd;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_rk2imp;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_rk4imp;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_bsimp;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_rk1imp;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_msadams;
GSL_VAR const gsl_odeiv2_step_type *gsl_odeiv2_step_msbdf;

/* Stepper object methods */

gsl_odeiv2_step *gsl_odeiv2_step_alloc (const gsl_odeiv2_step_type * T,
                                        size_t dim);
int gsl_odeiv2_step_reset (gsl_odeiv2_step * s);
void gsl_odeiv2_step_free (gsl_odeiv2_step * s);
const char *gsl_odeiv2_step_name (const gsl_odeiv2_step * s);
unsigned int gsl_odeiv2_step_order (const gsl_odeiv2_step * s);
int gsl_odeiv2_step_apply (gsl_odeiv2_step * s, double t, double h,
                           double y[], double yerr[], const double dydt_in[],
                           double dydt_out[], const gsl_odeiv2_system * dydt);
int gsl_odeiv2_step_set_driver (gsl_odeiv2_step * s,
                                const gsl_odeiv2_driver * d);

/* Step size control object. */

typedef struct
{
  const char *name;
  void *(*alloc) (void);
  int (*init) (void *state, double eps_abs, double eps_rel, double a_y,
               double a_dydt);
  int (*hadjust) (void *state, size_t dim, unsigned int ord, const double y[],
                  const double yerr[], const double yp[], double *h);
  int (*errlevel) (void *state, const double y, const double dydt,
                   const double h, const size_t ind, double *errlev);
  int (*set_driver) (void *state, const gsl_odeiv2_driver * d);
  void (*free) (void *state);
}
gsl_odeiv2_control_type;

struct gsl_odeiv2_control_struct
{
  const gsl_odeiv2_control_type *type;
  void *state;
};

/* Possible return values for an hadjust() evolution method */

#define GSL_ODEIV_HADJ_INC   1  /* step was increased */
#define GSL_ODEIV_HADJ_NIL   0  /* step unchanged     */
#define GSL_ODEIV_HADJ_DEC (-1) /* step decreased     */

/* General step size control methods.
 *
 * The hadjust() method controls the adjustment of
 * step size given the result of a step and the error.
 * Valid hadjust() methods must return one of the codes below.
 * errlevel function calculates the desired error level D0.
 *
 * The general data can be used by specializations
 * to store state and control their heuristics.
 */

gsl_odeiv2_control *gsl_odeiv2_control_alloc (const gsl_odeiv2_control_type *
                                              T);
int gsl_odeiv2_control_init (gsl_odeiv2_control * c, double eps_abs,
                             double eps_rel, double a_y, double a_dydt);
void gsl_odeiv2_control_free (gsl_odeiv2_control * c);
int gsl_odeiv2_control_hadjust (gsl_odeiv2_control * c, gsl_odeiv2_step * s,
                                const double y[], const double yerr[],
                                const double dydt[], double *h);
const char *gsl_odeiv2_control_name (const gsl_odeiv2_control * c);
int gsl_odeiv2_control_errlevel (gsl_odeiv2_control * c, const double y,
                                 const double dydt, const double h,
                                 const size_t ind, double *errlev);
int gsl_odeiv2_control_set_driver (gsl_odeiv2_control * c,
                                   const gsl_odeiv2_driver * d);

/* Available control object constructors.
 *
 * The standard control object is a four parameter heuristic
 * defined as follows:
 *    D0 = eps_abs + eps_rel * (a_y |y| + a_dydt h |y'|)
 *    D1 = |yerr|
 *    q  = consistency order of method (q=4 for 4(5) embedded RK)
 *    S  = safety factor (0.9 say)
 *
 *                      /  (D0/D1)^(1/(q+1))  D0 >= D1
 *    h_NEW = S h_OLD * |
 *                      \  (D0/D1)^(1/q)      D0 < D1
 *
 * This encompasses all the standard error scaling methods.
 *
 * The y method is the standard method with a_y=1, a_dydt=0.
 * The yp method is the standard method with a_y=0, a_dydt=1.
 */

gsl_odeiv2_control *gsl_odeiv2_control_standard_new (double eps_abs,
                                                     double eps_rel,
                                                     double a_y,
                                                     double a_dydt);
gsl_odeiv2_control *gsl_odeiv2_control_y_new (double eps_abs, double eps_rel);
gsl_odeiv2_control *gsl_odeiv2_control_yp_new (double eps_abs,
                                               double eps_rel);

/* This controller computes errors using different absolute errors for
 * each component
 *
 *    D0 = eps_abs * scale_abs[i] + eps_rel * (a_y |y| + a_dydt h |y'|)
 */

gsl_odeiv2_control *gsl_odeiv2_control_scaled_new (double eps_abs,
                                                   double eps_rel, double a_y,
                                                   double a_dydt,
                                                   const double scale_abs[],
                                                   size_t dim);

/* Evolution object */

struct gsl_odeiv2_evolve_struct
{
  size_t dimension;
  double *y0;
  double *yerr;
  double *dydt_in;
  double *dydt_out;
  double last_step;
  unsigned long int count;
  unsigned long int failed_steps;
  const gsl_odeiv2_driver *driver;
};

/* Evolution object methods */

gsl_odeiv2_evolve *gsl_odeiv2_evolve_alloc (size_t dim);
int gsl_odeiv2_evolve_apply (gsl_odeiv2_evolve * e, gsl_odeiv2_control * con,
                             gsl_odeiv2_step * step,
                             const gsl_odeiv2_system * dydt, double *t,
                             double t1, double *h, double y[]);
int gsl_odeiv2_evolve_apply_fixed_step (gsl_odeiv2_evolve * e,
                                        gsl_odeiv2_control * con,
                                        gsl_odeiv2_step * step,
                                        const gsl_odeiv2_system * dydt,
                                        double *t, const double h0,
                                        double y[]);
int gsl_odeiv2_evolve_reset (gsl_odeiv2_evolve * e);
void gsl_odeiv2_evolve_free (gsl_odeiv2_evolve * e);
int gsl_odeiv2_evolve_set_driver (gsl_odeiv2_evolve * e,
                                  const gsl_odeiv2_driver * d);

/* Driver object
 *
 * This is a high level wrapper for step, control and
 * evolve objects.
 */

struct gsl_odeiv2_driver_struct
{
  const gsl_odeiv2_system *sys; /* ODE system */
  gsl_odeiv2_step *s;           /* stepper object */
  gsl_odeiv2_control *c;        /* control object */
  gsl_odeiv2_evolve *e;         /* evolve object */
  double h;                     /* step size */
  double hmin;                  /* minimum step size allowed */
  double hmax;                  /* maximum step size allowed */
  unsigned long int n;          /* number of steps taken */
  unsigned long int nmax;       /* Maximum number of steps allowed */
};

/* Driver object methods */

gsl_odeiv2_driver *gsl_odeiv2_driver_alloc_y_new (const gsl_odeiv2_system *
                                                  sys,
                                                  const gsl_odeiv2_step_type *
                                                  T, const double hstart,
                                                  const double epsabs,
                                                  const double epsrel);
gsl_odeiv2_driver *gsl_odeiv2_driver_alloc_yp_new (const gsl_odeiv2_system *
                                                   sys,
                                                   const gsl_odeiv2_step_type
                                                   * T, const double hstart,
                                                   const double epsabs,
                                                   const double epsrel);
gsl_odeiv2_driver *gsl_odeiv2_driver_alloc_scaled_new (const gsl_odeiv2_system
                                                       * sys,
                                                       const
                                                       gsl_odeiv2_step_type *
                                                       T, const double hstart,
                                                       const double epsabs,
                                                       const double epsrel,
                                                       const double a_y,
                                                       const double a_dydt,
                                                       const double
                                                       scale_abs[]);
gsl_odeiv2_driver *gsl_odeiv2_driver_alloc_standard_new (const
                                                         gsl_odeiv2_system *
                                                         sys,
                                                         const
                                                         gsl_odeiv2_step_type
                                                         * T,
                                                         const double hstart,
                                                         const double epsabs,
                                                         const double epsrel,
                                                         const double a_y,
                                                         const double a_dydt);
int gsl_odeiv2_driver_set_hmin (gsl_odeiv2_driver * d, const double hmin);
int gsl_odeiv2_driver_set_hmax (gsl_odeiv2_driver * d, const double hmax);
int gsl_odeiv2_driver_set_nmax (gsl_odeiv2_driver * d,
                                const unsigned long int nmax);
int gsl_odeiv2_driver_apply (gsl_odeiv2_driver * d, double *t,
                             const double t1, double y[]);
int gsl_odeiv2_driver_apply_fixed_step (gsl_odeiv2_driver * d, double *t,
                                        const double h,
                                        const unsigned long int n,
                                        double y[]);
int gsl_odeiv2_driver_reset (gsl_odeiv2_driver * d);
void gsl_odeiv2_driver_free (gsl_odeiv2_driver * state);

__END_DECLS
#endif /* __GSL_ODEIV2_H__ */


/*
  NAME:
     proj_gauss_mixtures
  PURPOSE:
     runs the full projected gaussian mixtures algorithms, with
     regularization and split and merge
  CALLING SEQUENCE:
     proj_gauss_mixtures(struct datapoint * data, int N,
     struct gaussian * gaussians, int K,bool * fixamp, bool * fixmean,
     bool * fixcovar, double * avgloglikedata, double tol,
     long long int maxiter, bool likeonly, double w, int splitnmerge,
     bool keeplog, FILE *logfile, FILE *convlogfile, bool noproj,
     bool diagerrs,noweight)
  INPUT:
     data        - the data
     N           - number of datapoints
     gaussians   - model gaussians (initial conditions)
     K           - number of model gaussians
     fixamp      - fix the amplitude?
     fixmean     - fix the mean?
     fixcovar    - fix the covar?
     tol         - proj_EM convergence limit
     maxiter     - maximum number of iterations in each proj_EM
     likeonly    - only compute the likelihood?
     w           - regularization paramter
     splitnmerge - split 'n' merge depth (how far down the list to go)
     keeplog     - keep a log in a logfile?
     logfile     - pointer to the logfile
     convlogfile - pointer to the convlogfile
     noproj      - don't perform any projections
     diagerrs    - the data->SS errors-squared are diagonal
     noweight    - don't use data-weights
  OUTPUT:
     updated model gaussians
     avgloglikedata - average log likelihood of the data
  REVISION HISTORY:
     2008-08-21 Written Bovy
     2010-03-01 Added noproj option - Bovy
     2010-04-01 Added noweight option - Bovy
*/
#ifdef _OPENMP
#include <omp.h>
#endif
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include "proj_gauss_mixtures.h"

void proj_gauss_mixtures(struct datapoint * data, int N,
			 struct gaussian * gaussians, int K,
			 bool * fixamp, bool * fixmean, bool * fixcovar,
			 double * avgloglikedata, double tol,
			 long long int maxiter, bool likeonly, double w,
			 int splitnmerge, bool keeplog, FILE *logfile,
			 FILE *convlogfile, bool noproj, bool diagerrs,
			 bool noweight){
  //Allocate some memory
  struct gaussian * startgaussians;
  startgaussians = gaussians;
  int d = (gaussians->VV)->size1;//dim of mm
  //Only give copies of the fix* vectors to the EM algorithm
  bool *fixamp_tmp, *fixmean_tmp,*fixcovar_tmp;
  fixamp_tmp = (bool *) malloc(K * sizeof (bool) );
  fixmean_tmp = (bool *) malloc(K * sizeof (bool) );
  fixcovar_tmp = (bool *) malloc(K * sizeof (bool) );
  int kk;
  for (kk = 0; kk != K; ++kk){
    *(fixamp_tmp++) = *(fixamp++);
    *(fixmean_tmp++) = *(fixmean++);
    *(fixcovar_tmp++) = *(fixcovar++);
  }
  fixamp -= K;
  fixamp_tmp -= K;
  fixmean -= K;
  fixmean_tmp -= K;
  fixcovar -= K;
  fixcovar_tmp -= K;
  //allocate the newalpha, newmm and newVV matrices
#ifdef _OPENMP
  nthreads = omp_get_max_threads();
#else
  nthreads = 1;
#endif
  newgaussians = (struct gaussian *) malloc(K * nthreads * sizeof (struct gaussian) );
  startnewgaussians = newgaussians;
  int ll;
  for (kk=0; kk != K*nthreads; ++kk){
    newgaussians->alpha = 0.0;
    newgaussians->mm = gsl_vector_calloc (d);
    newgaussians->VV = gsl_matrix_calloc (d,d);
    ++newgaussians;
  }
  newgaussians= startnewgaussians;
  double oldavgloglikedata;
  //allocate the q_ij matrix
  qij = gsl_matrix_alloc(N,K);
  double * qstarij = (double *) malloc(N * sizeof (double) );
  I = gsl_matrix_alloc(d,d);
  gsl_matrix_set_identity(I);//Unit matrix
  gsl_matrix_scale(I,w);//scaled to w
  //Also take care of the bbij's and the BBij's
  bs = (struct modelbs *) malloc(nthreads * K * sizeof (struct modelbs) );
  for (kk = 0; kk != nthreads*K; ++kk){
    bs->bbij = gsl_vector_alloc (d);
    bs->BBij = gsl_matrix_alloc (d,d);
    ++bs;
  }
  bs -= nthreads*K;
  //splitnmerge
  int maxsnm = K*(K-1)*(K-2)/2;
  int * snmhierarchy = (int *) malloc(maxsnm*3* sizeof (int) );
  int j,k,l;
  struct gaussian * oldgaussians = (struct gaussian *) malloc(K * sizeof (struct gaussian) );
  gsl_matrix * oldqij = gsl_matrix_alloc(N,K);
  for (kk=0; kk != K; ++kk){
    oldgaussians->mm = gsl_vector_calloc (d);
    oldgaussians->VV = gsl_matrix_calloc (d,d);
    ++oldgaussians;
  }
  oldgaussians -= K;

  //create temporary file to hold convergence info
  FILE *tmpconvfile;
  char * tmpfilename= tmpnam(NULL);;
  if (keeplog)
    tmpconvfile= fopen(tmpfilename,"w+");

  //proj_EM
  if (keeplog)
    fprintf(logfile,"#Initial proj_EM\n");
  //printf("Where's the segmentation fault?\n");
  proj_EM(data,N,gaussians,K,fixamp_tmp,fixmean_tmp,fixcovar_tmp,
	  avgloglikedata,tol,maxiter,likeonly,w,
	  keeplog,logfile,convlogfile,noproj,diagerrs,noweight);
  if (keeplog){
    fprintf(logfile,"\n");
    fprintf(convlogfile,"\n");
  }
  //reset fix* vectors
  for (kk = 0; kk != K; ++kk){
    *(fixamp_tmp++) = *(fixamp++);
    *(fixmean_tmp++) = *(fixmean++);
    *(fixcovar_tmp++) = *(fixcovar++);
  }
  fixamp -= K;
  fixamp_tmp -= K;
  fixmean -= K;
  fixmean_tmp -= K;
  fixcovar -= K;
  fixcovar_tmp -= K;

  //Run splitnmerge
  randgen = gsl_rng_alloc(gsl_rng_mt19937);
  bool weretrying = true;
  if (likeonly || splitnmerge == 0 || K < 3)
    ;
  else {
    while (weretrying){
      weretrying = false; /* this is set back to true if an improvement is found */
      //store avgloglike from normal EM and model parameters
      oldavgloglikedata = *avgloglikedata;
      gsl_matrix_memcpy(oldqij,qij);
      for (kk=0; kk != K; ++kk){
	oldgaussians->alpha = gaussians->alpha;
	gsl_vector_memcpy(oldgaussians->mm,gaussians->mm);
	gsl_matrix_memcpy(oldgaussians->VV,gaussians->VV);
	++oldgaussians;
	++gaussians;
      }
      gaussians -= K;
      oldgaussians -= K;
      //Then calculate the splitnmerge hierarchy
      calc_splitnmerge(data,N,gaussians,K,qij,snmhierarchy);
      //Then go through this hierarchy
      kk=0;
      while (kk != splitnmerge && kk != maxsnm){
	//splitnmerge
	j = *(snmhierarchy++);
	k = *(snmhierarchy++);
	l = *(snmhierarchy++);
	splitnmergegauss(gaussians,K,oldqij,j,k,l);
	//partial EM
	//Prepare fixed vectors for partial EM
	for (ll = 0; ll != K; ++ll){
	  *(fixamp_tmp++) = (ll != j && ll != k && ll != l);
	  *(fixmean_tmp++) = (ll != j && ll != k && ll != l);
	  *(fixcovar_tmp++) = (ll != j && ll != k && ll != l);
	}
	fixamp_tmp -= K;
	fixmean_tmp -= K;
	fixcovar_tmp -= K;
	if (keeplog)
	  fprintf(logfile,"#Merging %i and %i, splitting %i\n",j,k,l);
	proj_EM(data,N,gaussians,K,fixamp_tmp,fixmean_tmp,fixcovar_tmp,
		avgloglikedata,tol,maxiter,likeonly,w,keeplog,logfile,
		tmpconvfile,noproj,diagerrs,noweight);
	//reset fix* vectors
	for (ll = 0; ll != K; ++ll){
	    *(fixamp_tmp++) = *(fixamp++);
	    *(fixmean_tmp++) = *(fixmean++);
	    *(fixcovar_tmp++) = *(fixcovar++);
	}
	fixamp -= K;
	fixamp_tmp -= K;
	fixmean -= K;
	fixmean_tmp -= K;
	fixcovar -= K;
	fixcovar_tmp -= K;
	//Full EM
	if (keeplog){
	  fprintf(logfile,"#full EM:\n");
	  fprintf(tmpconvfile,"\n");
	}
	proj_EM(data,N,gaussians,K,fixamp_tmp,fixmean_tmp,fixcovar_tmp,
		avgloglikedata,tol,maxiter,likeonly,w,keeplog,logfile,
		tmpconvfile,noproj,diagerrs,noweight);
	if (keeplog){
	  fprintf(logfile,"\n");
	  fprintf(tmpconvfile,"\n");
	}
	//reset fix* vectors
	for (ll = 0; ll != K; ++ll){
	    *(fixamp_tmp++) = *(fixamp++);
	    *(fixmean_tmp++) = *(fixmean++);
	    *(fixcovar_tmp++) = *(fixcovar++);
	  }
	  fixamp -= K;
	  fixamp_tmp -= K;
	  fixmean -= K;
	  fixmean_tmp -= K;
	  fixcovar -= K;
	  fixcovar_tmp -= K;
	//Better?
	if (*avgloglikedata > oldavgloglikedata){
	  if (keeplog){
	    fprintf(logfile,"#accepted\n");
	    //Copy tmpfile into convfile
	    fseek(tmpconvfile,0,SEEK_SET);
	    while (feof(tmpconvfile) == 0)
	      fputc(fgetc(tmpconvfile),convlogfile);
	    fseek(convlogfile,-1,SEEK_CUR);
	    fclose(tmpconvfile);
	    tmpconvfile= fopen(tmpfilename,"w+");
	  }
	  weretrying = true;
	  ++kk;
	  break;
	}
	else {
	  if (keeplog){
	    fprintf(logfile,"#didn't improve likelihood\n");
	    fclose(tmpconvfile);
	    tmpconvfile= fopen(tmpfilename,"w+");
	  }
	  //revert back to the older solution
	  *avgloglikedata = oldavgloglikedata;
	  /*  gsl_matrix_memcpy(qij,oldqij);*/
	  for (ll=0; ll != K; ++ll){
	    gaussians->alpha = oldgaussians->alpha;
	    gsl_vector_memcpy(gaussians->mm,oldgaussians->mm);
	    gsl_matrix_memcpy(gaussians->VV,oldgaussians->VV);
	    ++oldgaussians;
	    ++gaussians;
	  }
	  gaussians -= K;
	  oldgaussians -= K;
	}
	++kk;
      }
      snmhierarchy -= 3*kk;
    }
  }

  if (keeplog){
    fclose(tmpconvfile);
    fprintf(convlogfile,"\n");
  }

  //Compute some criteria to set the number of Gaussians and print these to the logfile
  int ii;
  int npc,np;
  double pc,aic,mdl;
  if (keeplog){
    //Partition coefficient
    pc=0.;
    for (ii=0; ii != N; ++ii)
      for (kk=0; kk != K; ++kk)
	pc += pow(exp(gsl_matrix_get(qij,ii,kk)),2);
    pc /= N;
    fprintf(logfile,"Partition coefficient \t=\t%f\n",pc);
    //Akaike's information criterion
    npc = 1 + d + d * (d - 1) / 2;
    np = K * npc;
    aic= -2 * ((double) N - 1. - npc - 100)* *avgloglikedata + 3 * np;
    fprintf(logfile,"AIC \t\t\t=\t%f\n",aic);
    //MDL
    mdl = - *avgloglikedata * N + 0.5 * np * log(N);
    fprintf(logfile,"MDL \t\t\t=\t%f\n",mdl);
  }

  //Free memory
  gsl_matrix_free(I);
  gsl_matrix_free(qij);
  for (kk = 0; kk != nthreads*K; ++kk){
    gsl_vector_free(bs->bbij);
    gsl_matrix_free(bs->BBij);
    ++bs;
  }
  bs -= nthreads*K;
  free(bs);
  for (kk=0; kk != K*nthreads; ++kk){
    gsl_vector_free(newgaussians->mm);
    gsl_matrix_free(newgaussians->VV);
    ++newgaussians;
  }
  newgaussians= startnewgaussians;
  free(newgaussians);
  gsl_rng_free(randgen);

  gsl_matrix_free(oldqij);
  for (kk=0; kk != K; ++kk){
    gsl_vector_free(oldgaussians->mm);
    gsl_matrix_free(oldgaussians->VV);
    ++oldgaussians;
  }
  oldgaussians -= K;
  free(oldgaussians);
  free(qstarij);
  free(snmhierarchy);
  free(fixamp_tmp);
  free(fixmean_tmp);
  free(fixcovar_tmp);

  return;
}


#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <gbpLib.h>
#include <gbpRNG.h>
#include <gbpMCMC.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_fit.h>
#include <gsl/gsl_interp.h>

int generate_MCMC_chain(MCMC_info *MCMC) {
    int              flag_success;
    int              i_P, i_DS;
    static char      format_string[256];
    FILE *           fp_report_props;
    static char      filename_report_props[SID_MAX_FILENAME_LENGTH];
    static int       n_P;
    static double *  P_new;
    static double ** M_new;
    static double *  P_last;
    static double *  P_chain;
    static double ** M_last;
    static double *  P_best;
    static double *  P_limit_min;
    static double *  P_limit_max;
    static RNG_info *RNG;
    static int       flag_report_props;
    static int       n_DS;
    static int *     n_M;

    // Initialize a few things on the first call
    switch(MCMC->first_chain_call) {
        case GBP_TRUE:
            n_P                    = MCMC->n_P;
            P_new                  = MCMC->P_new;
            M_new                  = MCMC->M_new;
            P_last                 = MCMC->P_last;
            M_last                 = MCMC->M_last;
            P_chain                = MCMC->P_chain;
            P_best                 = MCMC->P_best;
            P_limit_min            = MCMC->P_limit_min;
            P_limit_max            = MCMC->P_limit_max;
            RNG                    = MCMC->RNG;
            n_DS                   = MCMC->n_DS;
            n_M                    = MCMC->n_M;
            flag_report_props      = SID_CHECK_BITFIELD_SWITCH(MCMC->mode, MCMC_MODE_REPORT_PROPS);
            MCMC->first_chain_call = GBP_FALSE;
            sprintf(filename_report_props, "%s/chains/report_props_%06d.dat", MCMC->filename_output_dir, MCMC->my_chain);
            break;
    }

    // We need to compute the likelihood of the initial conditions
    //   if we are just starting a chain
    if(MCMC->flag_init_chain)
        generate_MCMC_proposition(MCMC, GBP_TRUE);

    // Set the last new state to the new last state
    MCMC->ln_likelihood_last = MCMC->ln_likelihood_new;
    MCMC->ln_Pr_last         = MCMC->ln_Pr_new;
    memcpy(P_last, P_new, n_P * sizeof(double));
    for(i_DS = 0; i_DS < n_DS; i_DS++)
        memcpy(M_last[i_DS], M_new[i_DS], n_M[i_DS] * sizeof(double));

    // Keep generating parameter sets until the mapping function is satisfied
    generate_MCMC_proposition(MCMC, GBP_FALSE);

    // Decide if this is a successful proposition or not...
    if(MCMC->ln_likelihood_new > MCMC->ln_likelihood_chain) {
        MCMC->ln_Pr_new = 0.;
        flag_success    = GBP_TRUE;
    } else if(MCMC->my_chain == SID.My_rank) {
        MCMC->ln_Pr_new = MCMC->ln_likelihood_new - MCMC->ln_likelihood_chain;
        if((double)random_number(RNG) <= exp(MCMC->ln_Pr_new))
            flag_success = GBP_TRUE;
        else
            flag_success = GBP_FALSE;
    }
    // fprintf(stderr,"%d %le %d\n",flag_success,MCMC->ln_Pr_new,SID_CHECK_BITFIELD_SWITCH(MCMC->mode,MCMC_MODE_PARALLEL));
    // if(!SID_CHECK_BITFIELD_SWITCH(MCMC->mode,MCMC_MODE_PARALLEL))
    SID_Bcast(&flag_success, 1, SID_INT, SID_MASTER_RANK, MCMC->comm);

    // ... if it is, then update the chain ...
    if(flag_success) {
        memcpy(P_chain, P_new, (size_t)n_P * sizeof(double));
        MCMC->ln_likelihood_chain = MCMC->ln_likelihood_new;
        MCMC->ln_Pr_chain         = MCMC->ln_Pr_new;
        MCMC->n_success++;
    } else
        MCMC->n_fail++;
    MCMC->n_propositions++;

    // Report the proposition if asked to
    if(flag_report_props && MCMC->my_chain == SID.My_rank) {
        fp_report_props = fopen(filename_report_props, "w");
        if(flag_success)
            fprintf(fp_report_props, "Proposal #%09d: SUCCEEDED\n\n", MCMC->n_propositions);
        else
            fprintf(fp_report_props, "Proposal #%09d: REJECTED\n\n", MCMC->n_propositions);
        sprintf(format_string, "   %s = %%13.6le (was %%13.6le) (best is %%13.6le) (flat prior =%%13.6le -> %%13.6le)\n", MCMC->P_name_format);
        for(i_P = 0; i_P < n_P; i_P++)
            fprintf(fp_report_props, format_string, MCMC->P_names[i_P], P_new[i_P], P_last[i_P], P_best[i_P], P_limit_min[i_P], P_limit_max[i_P]);
        fprintf(fp_report_props, "\n");
        fprintf(fp_report_props,
                "ln(likelihood) =%13.6le+constant (was %13.6le+constant) (best is %13.6le+constant)\n",
                MCMC->ln_likelihood_new,
                MCMC->ln_likelihood_last,
                MCMC->ln_likelihood_best);
        fprintf(fp_report_props, "ln(probability)=%13.6le\n", MCMC->ln_Pr_new);
        fprintf(fp_report_props, "n_success      =%09d\n", MCMC->n_success);
        fprintf(fp_report_props, "n_fail         =%09d\n", MCMC->n_fail);
        fprintf(fp_report_props, "success rate   =%.2f %%\n", (double)MCMC->n_success / (double)(MCMC->n_fail + MCMC->n_success) * 100.0);
        fclose(fp_report_props);
    }

    return (flag_success);
}


//=========================================
// Main driver for the proper time integral
//=========================================

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

double* transformT(double t, void* p, int* order)
//Transforms the integral [0,infinity) to [0,1]
//x=(1-t)/t
{
	struct Wparams *params = (struct Wparams *) p;
	double* tfunc;
	double x = (1.0-t)/t;
	int i;

	tfunc = Tfunc(x,p, order);
	//loop over groups of worldlines
	for(i = 0; i < params->ng; i++)
	{
		tfunc[i] = (tfunc[i]/t)/t;

	}
	return tfunc;

}

void testval(double *val, int n, double t)
//used for outputting debugging information
{
  int i;
  double avg = 0.0;
  for(i = 0; i < n; i++)
  {
	if(isnan(val[i])) printf("for t=%f val[%d]=nan\n",t, i);
	avg += val[i];
  }
  avg /= n;

  printf("mean for Tt=%f, %f\n",t,avg);
}

void Tintout(double *val, int n, double rho, int fermion)
//Produces output about the T-integral
{
	int i;
	double avg = 0.0, SEM = 0.0;
  	for(i = 0; i < n; i++)
  	{
		if(isnan(val[i])) printf("for rho=%f val[%d]=nan\n",rho, i);
		avg += val[i];
  	}
  	avg /= n;
	for(i = 0; i < n; i++)
 	{
		SEM += (val[i]-avg)*(val[i]-avg);
  	}
  	SEM = sqrt(SEM/((double)n*((double)n-1.0)));
  	//rho Leff SEM-Leff

	if(fermion == 1)
  		printf("Leffvsrho: %e %e %e\n", rho, avg/(4.0*pi), SEM/(4.0*pi));
	else
  		printf("Leffvsrho: %e %e %e\n", rho, avg/(-2.0*pi), SEM/(2.0*pi));
}

int* shuffleWL(int Nl)
//Returns a vector representing a shuffled order of worldlines
{
  gsl_rng * r;
  int i;

  // select random number generator
  r = gsl_rng_alloc (gsl_rng_mt19937);

  int* order=(int*)malloc(Nl*sizeof(*order));

  for (i = 0; i < Nl; i++)
  {
  	order[i] = i;
  }

  //gsl_ran_shuffle (r, order, Nl, sizeof (int));
  free(r);

  return order;
}

double* integrateT(int n, void * p)
//Performs the proper time integral using Simpson's Method
//n = number of points to use for Simpson's method
//p = Wparams parameters for the integrand
{
        double* val;    //value of the integral for each group
	double* trt;    //Integrands evaluated at three points
	double* trtphalf;
	double* trtp1;
	int* order;     //Stores the order of worldlines
	struct Wparams *params = (struct Wparams *) p;
	int inx, ig, i;
	val = (double *)calloc(params->ng,sizeof(*val));
	double h = 1.0/((double) n);   //
	double t;
	if(!val) printf("integrate(): malloc failed\n");
	//initialize val to zero
	for(ig = 0; ig < params->ng; ig++)
	{
		val[ig] = 0.0;
	}
	//main integration loop
	for(inx = 0; inx < n; inx++)
	{
		printf("T integrate inx=%d\n",inx);
		//shuffle worldlines
		order=shuffleWL(params->Nl);
		t=h*((double)inx+1.0);
		trt=transformT(t,p,order);
		trtphalf=transformT(h*((double)inx + 0.5), p, order);
		trtp1=transformT(h*((double) inx+1.0), p, order);

		//A kludge to ignore possible infinities or nans
		for(ig = 0; ig < params->ng; ig++)
		{
			if(isinf(trt[ig]))
			{
				printf("Warning: trt[%d]=%f.  Setting to zero.\n",ig,trt[ig]);
				trt[ig]=0.0;
			}
			if(isinf(trtphalf[ig]))
			{
				printf("Warning: trtphalf[%d]=%f.  Setting to zero.\n",ig,trtphalf[ig]);
				trtphalf[ig]=0.0;
			}
			if(isinf(trtp1[ig]))
			{
				printf("Warning: trtp1[%d]=%f.  Setting to zero.\n",ig,trtp1[ig]);
				trtp1[ig]=0.0;
			}

        		val[ig]+=h/6.0*(
				trt[ig]
				+ 4.0*trtphalf[ig]
				+trtp1[ig]);
			if(isinf(val[ig])){
				printf("trt[%d]=%e\n",ig, trt[ig]);
				printf("trtphalf[%d]=%e\n",ig,trtphalf[ig]);
				printf("trtp1[%d]=%e\n",ig,trtp1[ig]);
				printf("val[%d]=%e\n",ig,val[ig]);

			}
			//printf("val[%d]=%e\n",ig,val[ig]);
		}
		testval(val, params->ng, t);
		free(order);

	}
	Tintout(val, params->ng, params->xcm.x, params->fermion);
	free(trt);
	free(trtphalf);
	free(trtp1);

        return val;

}


#include <stdio.h>
#include <stdlib.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_statistics_double.h>
#include <math.h>
#include "tz_error.h"
#include "tz_constant.h"
#include "tz_voxel_linked_list.h"
#include "tz_stack_sampling.h"
#include "tz_voxel_graphics.h"
#include "tz_neurotrace.h"
#include "tz_stack_math.h"
#include "tz_stack_utils.h"
#include "tz_objdetect.h"
#include "tz_voxeltrans.h"
#include "tz_stack_stat.h"
#include "tz_stack_draw.h"
#include "tz_geo3d_vector.h"
#include "tz_stack_bwmorph.h"
#include "tz_stack_bwdist.h"

INIT_EXCEPTION_MAIN(e)

int main(int argc, char* argv[])
{
#if 1
  Stack *stack = Read_Stack("../data/lobster_neuron_seed.tif");

  Voxel_List *list = Stack_To_Voxel_List(stack);

  //Print_Voxel_List(list);

  Pixel_Array *pa = Pixel_Array_Read("../data/lobster_neuron_seeds.pa");
  //Print_Pixel_Array(pa);

  Voxel *seed;

  int i;
  double *pa_array = (double *) pa->array;

  printf("mean: %g, std: %g\n", gsl_stats_mean(pa_array, 1, pa->size),
	 sqrt(gsl_stats_variance(pa_array, 1, pa->size)));

  double threshold = gsl_stats_mean(pa_array, 1, pa->size) +
    3.0 * sqrt(gsl_stats_variance(pa_array, 1, pa->size));

  double max_r = gsl_stats_max(pa_array, 1, pa->size);

  printf("%g, %g\n", threshold, max_r);

  max_r = 30.0;
  Set_Neuroseg_Max_Radius(max_r);

  dim_type dim[3];
  dim[0] = stack->width;
  dim[1] = stack->height;
  dim[2] = stack->depth;

  IMatrix *chord = Make_IMatrix(dim, 3);

  Stack *code = Make_Stack(GREY16, stack->width, stack->height, stack->depth);

  Kill_Stack(stack);

  stack = Read_Stack("../data/lobster_neuron_single.tif");

  Rgb_Color color;
  Set_Color(&color, 255, 0, 0);

  Stack *signal = Read_Stack("../data/lobster_neuron.tif");

  Stack *canvas = Translate_Stack(signal, COLOR, 0);

  /************** soma detection *************/
#if 0
  Struct_Element *se = Make_Ball_Se(((int) threshold));
  Stack *stack1 = Stack_Erode_Fast(stack, NULL, se);
  Stack *soma = Stack_Dilate(stack1, NULL, se);
  Kill_Stack(stack1);

  Stack_And(stack, soma, soma);

  Stack_Label_Bwc(canvas, soma, color);

  Kill_Stack(soma);

  Write_Stack("../data/test.tif", canvas);
  return 0;
#endif
  /*******************************************/

  Object_3d *obj = NULL;
  int seed_offset = -1;

  Neurochain *chain = NULL;

  double z_scale = 0.488 / 0.588;

  Set_Zscale(z_scale);

  Stack *traced =
    Make_Stack(GREY, signal->width, signal->height, signal->depth);
  One_Stack(traced);

  for (i = 0; i < pa->size; i++) {
    seed = Voxel_Queue_De(&list);
    printf("------------------------------------------> seed: %d\n", i);
    if (*STACK_PIXEL_8(traced, seed->x, seed->y, seed->z, 0) == 0) {
      continue;
    }

    double width = pa_array[i];

    if (width > max_r) {
      continue;
    }

    chain = New_Neurochain();

    Print_Voxel(seed);
    printf("%g\n", width);

    int max_level = (int) (width + 0.5);
    if (max_level < 6) {
      max_level = 6;
    }

    seed_offset = Stack_Util_Offset(seed->x, seed->y, seed->z,
				    stack->width, stack->height, stack->depth);
    Stack_Level_Code_Constraint(stack, code, chord->array, &seed_offset, 1,
				max_level + 1);

    Voxel_t v;
    Voxel_To_Tvoxel(seed, v);

    Print_Tvoxel(v);

    Stack *tmp_stack = Copy_Stack(stack);
    obj = Stack_Grow_Object_Constraint(tmp_stack, 1, v, chord, code,
				       max_level);
    Free_Stack(tmp_stack);

    Print_Object_3d_Info(obj);

    double vec[3];
    Object_3d_Orientation_Zscale(obj, vec, MAJOR_AXIS, z_scale);

    double theta, psi;
    Geo3d_Vector obj_vec;
    Set_Geo3d_Vector(&obj_vec, vec[0], vec[1], vec[2]);

    Geo3d_Vector_Orientation(&obj_vec, &theta, &psi);

    Set_Neuroseg(&(chain->seg), width, width, 10.0,
		 theta, psi);

    double cpos[3];
    cpos[0] = seed->x;
    cpos[1] = seed->y;
    cpos[2] = seed->z;
    cpos[2] *= z_scale;

    double bpos[3];
    Neuroseg_Pos_Center_To_Bottom(&(chain->seg), cpos, bpos);
    Set_Position(chain->position, bpos[0], bpos[1], bpos[2]);

    if (Initialize_Tracing(signal, chain, NULL, NULL) >= MIN_SCORE) {
      if ((chain->seg.r1 < max_r) && (chain->seg.r2 < max_r)) {
	chain = Trace_Neuron(signal, chain, BOTH, traced);

	Print_Neurochain(chain);

	//Stack_Draw_Object_Bwc(canvas, obj, color);

	Neurochain_Erase(traced, Neurochain_Head(chain));
	Neurochain_Label(canvas, Neurochain_Head(chain));
      }
    }

    Free_Neurochain(chain);

    free(seed);

    Kill_Object_3d(obj);
  }

  Write_Stack("../data/lobster_neuron_traced.tif", canvas);

#endif

  return 0;
}


/* -*- c++ -*- */
/*
 * Copyright (C) 2017 Free Software Foundation, Inc.
 *
 * This file is part of GNU Radio
 *
 * GNU Radio 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, or (at your option)
 * any later version.
 *
 * GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <gsl/gsl_integration.h>

#define	NSTEPS		 10	// how many steps of mu are in the generated table
#define	MAX_NSTEPS	256
#define	NTAPS		  8	// # of taps in the interpolator
#define	MAX_NTAPS	128

extern void initpt (double x[], int ntaps);
extern double objective (double x[], int *ntaps);
extern double global_mu;
extern double global_B;
extern gsl_integration_workspace *global_gsl_int_workspace;

// fortran
extern double prax2_ (double (fct)(double x[], int *ntaps),
		      double initv[], int *ntaps, double result[]);

static void
usage (char *name)
{
  fprintf (stderr, "usage: %s [-v] [-n <nsteps>] [-t <ntaps>] [-B <bw>]\n", name);
  exit (1);
}

static void
printline (double x[], int ntaps, int imu, int nsteps)
{
  int	i;

  printf ("  { ");
  for (i = 0; i < ntaps; i++){
    printf ("%12.5e", x[i]);
    if (i != ntaps - 1)
      printf (", ");
    else
      printf (" }, // %3d/%d\n", imu, nsteps);
  }
}

int
main (int argc, char **argv)
{
  double	xx[MAX_NSTEPS+1][MAX_NTAPS];
  int	 	ntaps = NTAPS;
  int		nsteps = NSTEPS;
  int		i, j;
  double 	result;
  double	step_size;
  int		c;
  int		verbose = 0;

  global_B = 0.25;

  while ((c = getopt (argc, argv, "n:t:B:v")) != EOF){
    switch (c){
    case 'n':
      nsteps = strtol (optarg, 0, 0);
      break;

    case 't':
      ntaps = strtol (optarg, 0, 0);
      break;

    case 'B':
      global_B = strtod (optarg, 0);
      break;

    case 'v':
      verbose = 1;
      break;

    default:
      usage (argv[0]);
      break;
    }
  }

  if ((nsteps & 1) != 0){
    fprintf (stderr, "%s: nsteps must be even\n", argv[0]);
    exit (1);
  }

  if (nsteps > MAX_NSTEPS){
    fprintf (stderr, "%s: nsteps must be < %d\n", argv[0], MAX_NSTEPS);
    exit (1);
  }

  if ((ntaps & 1) != 0){
    fprintf (stderr, "%s: ntaps must be even\n", argv[0]);
    exit (1);
  }

  if (nsteps > MAX_NTAPS){
    fprintf (stderr, "%s: ntaps must be < %d\n", argv[0], MAX_NTAPS);
    exit (1);
  }

  if (global_B < 0 || global_B > 0.5){
    fprintf (stderr, "%s: bandwidth must be in the range (0, 0.5)\n", argv[0]);
    exit (1);
  }

  global_gsl_int_workspace = gsl_integration_workspace_alloc(4000);
  if (global_gsl_int_workspace == NULL) {
    fprintf (stderr, "%s: unable to allocate GSL integration work space\n",
             argv[0]);
    exit (1);
  }

  step_size = 1.0/nsteps;

  // compute optimal values for mu <= 1.0

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

    global_mu = j * step_size;		// this determines the MU for which we're computing the taps

    // initialize X to a reasonable starting value

    initpt (&xx[j][0], ntaps);

    // find the value of X that minimizes the value of OBJECTIVE

    result = prax2_ (objective, &xx[j][0], &ntaps, &xx[j][0]);

    if (verbose){
      fprintf (stderr, "Mu: %10.8f\t", global_mu);
      fprintf (stderr, "Objective: %g\n", result);
    }
  }

  gsl_integration_workspace_free(global_gsl_int_workspace);

  // now print out the table

  printf ("\
/*\n\
 * This file was machine generated by gen_interp_differentiator_taps.\n\
 * DO NOT EDIT BY HAND.\n\
 */\n\n");

  printf ("static const int	DNTAPS     = %4d;\n", ntaps);
  printf ("static const int	DNSTEPS    = %4d;\n", nsteps);
  printf ("static const double	DBANDWIDTH = %g;\n\n", global_B);

  printf ("static const float Dtaps[DNSTEPS+1][DNTAPS] = {\n");
  printf ("  //    -4            -3            -2            -1             0             1             2             3                mu\n");

  for (i = 0; i <= nsteps; i++)
    printline (xx[i], ntaps, i, nsteps);

  printf ("};\n\n");

  return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include "parmt_utils.h"
#ifdef PARMT_USE_INTEL
#include <mkl_cblas.h>
#else
#include <cblas.h>
#endif
#include "iscl/array/array.h"

/*!
 * @brief The iloc'th location and iobs'th observation this sets the forward
 *        modeling matrix G s.t. Gm = u
 *
 * @param[in] iobs    observation number
 * @param[in] iloc    location number
 * @param[in] npmax   leading dimension of G
 * @param[in] data    data data structure with Green's functions which will be
 *                    set on G
 *
 * @param[out] G      column major modeling matrix s.t. Gm=u where
 *                    G is packed
 *                     \f$ \{ G_{xx}, G_{yy}, G_{zz},
 *                            G_{xy}, G_{xz}, G_{yz} \} \f$
 *                    and m is in NED ordering [npmax x 6]
 *
 * @result 0 indicates success
 *
 */
int parmt_utils_setDataOnG(const int iobs, const int iloc, const int npmax,
                           const struct parmtData_struct data,
                           double *__restrict__ G)
{
    const char *fcnm = "parmt_utils_setDataOnG\0";
    int k;
    if (iobs < 0 || iobs >= data.nobs)
    {
        printf("%s: Error invalid observation\n", fcnm);
        return -1;
    }
    if (iloc < 0 || iloc >= data.nlocs)
    {
        printf("%s: Error invalid location\n", fcnm);
        return -1;
    }
    k = iobs*data.nlocs + iloc;
    if (npmax > data.sacGxx[k].npts || npmax > data.sacGyy[k].npts ||
        npmax > data.sacGzz[k].npts || npmax > data.sacGxy[k].npts ||
        npmax > data.sacGxz[k].npts || npmax > data.sacGyz[k].npts)
    {
        printf("%s: npmax is too small\n", fcnm);
        return -1;
    }
    if (data.sacGxx[k].data == NULL || data.sacGyy[k].data == NULL ||
        data.sacGzz[k].data == NULL || data.sacGxy[k].data == NULL ||
        data.sacGxz[k].data == NULL || data.sacGyz[k].data == NULL)
    {
        printf("%s: Greens function is null\n", fcnm);
        return -1;
    }
    // Get the Green's functions onto the matrix G
    array_zeros64f_work(6*npmax, G);
    cblas_dcopy(data.sacGxx[k].header.npts,
                data.sacGxx[k].data, 1, &G[0*npmax], 1);
    cblas_dcopy(data.sacGyy[k].header.npts,
                data.sacGyy[k].data, 1, &G[1*npmax], 1);
    cblas_dcopy(data.sacGzz[k].header.npts,
                data.sacGzz[k].data, 1, &G[2*npmax], 1);
    cblas_dcopy(data.sacGxy[k].header.npts,
                data.sacGxy[k].data, 1, &G[3*npmax], 1);
    cblas_dcopy(data.sacGxz[k].header.npts,
                data.sacGxz[k].data, 1, &G[4*npmax], 1);
    cblas_dcopy(data.sacGyz[k].header.npts,
                data.sacGyz[k].data, 1, &G[5*npmax], 1);
    return 0;
}