#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;
}


#ifndef _H_INTERP2D
#define _H_INTERP2D

#include <gsl/gsl_spline.h>
#include <gsl/gsl_interp.h>

typedef struct Interpolator2D{
  double *x1;
  double *x2;
  double *y;
  int N1;
  int N2;
  double *y1;
  gsl_spline *x1dir_spline;
  gsl_interp_accel *x1dir_accel;
  gsl_spline **x2dir_splines;
  gsl_interp_accel **x2dir_accels;
} Interpolator2D;

typedef double (*interp2d_modifier_function)(double,double,double,void*);

Interpolator2D * init_interp_2d(double *x1, double *x2, double *y, int N1, int N2, const gsl_interp_type *T);
Interpolator2D * init_interp_2d_akima(double *x1, double *x2, double *y, int N1, int N2);
Interpolator2D * init_interp_2d_grid(double *x1, double *x2, double **y, int N1, int N2, const gsl_interp_type *T);
Interpolator2D * init_interp_2d_akima_grid(double *x1, double *x2, double **y, int N1, int N2);

Interpolator2D * init_interp_2d_function(double *x1, double *x2, double *y,
  int N1, int N2, const gsl_interp_type *T, interp2d_modifier_function function, void * args);
Interpolator2D * init_interp_2d_akima_function(double *x1, double *x2, double *y,
  int N1, int N2, interp2d_modifier_function function, void * args);
Interpolator2D * init_interp_2d_grid_function(double *x1, double *x2, double **y,
  int N1, int N2, const gsl_interp_type *T, interp2d_modifier_function function, void * args);
Interpolator2D * init_interp_2d_akima_grid_function(double *x1, double *x2,
  double **y, int N1, int N2, interp2d_modifier_function function, void * args);

void destroy_interp_2d(Interpolator2D * interp2d);
double interp_2d(double x1, double x2, Interpolator2D * interp2d);
Interpolator2D * load_interp_2d_file(const char * filename, int N1, int N2);

double interp_2d_xmin(Interpolator2D * interp2d);
double interp_2d_xmax(Interpolator2D * interp2d);
double interp_2d_ymin(Interpolator2D * interp2d);
double interp_2d_ymax(Interpolator2D * interp2d);

#endif


/** \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__ */


#pragma once

/***************************************************
 *************** Auto-Generated File ***************
 ***************************************************/

#include <cstring>
#include <string>

#include <gsl/gsl_const_mksa.h>
#include <gsl/gsl_const_num.h>

#include "../../Utils/BinarySearch.h"

enum UnitType {
	NONE,
	{UnitTypes}
};

constexpr const int numUnits = {numUnits};
constexpr const char* unitNames[numUnits] = {
	{unitNames}
};
constexpr const int unitIndices[numUnits] = {
	{unitIndices}
};
constexpr const char * unitAbbreviations[numUnits] = {
	{unitAbbreviations}
};
constexpr const UnitType unitTypes[numUnits] = {
	{unitTypes}
};
constexpr const double unitConversions[numUnits] = {
	{unitConversions}
};

/*
Returns index of the unit in the unitNames array.

Uses binary search under the hood to search for the index.

Parameters
----------
name: The name of the unit

Returns
-------
The index or -1 if the provided name is not a unit.
*/
CONSTEXPR_BINARY_SEARCH(getUnitIndex, unitNames, numUnits)

CONSTEXPR_BINARY_SEARCH(getAbbrIndex, unitAbbreviations, numUnits)

constexpr UnitType getUnitType(const char* name){
    int index = getUnitIndex(name);
    if (index != -1){
		return NONE;
	}
	return unitTypes[index];
}
inline UnitType getUnitType(const std::string& name){
    int index = getUnitIndex(name.c_str());
    if (index != -1){
		return NONE;
	}
	return unitTypes[index];
}
inline UnitType getUnitType(int index){
    if (index < 0 || index >= numUnits){
		return NONE;
	}
	return unitTypes[index];
}

constexpr double getUnitConversion(const char* name){
    int index = getUnitIndex(name);
    if (index != -1){
		return NONE;
	}
	return unitConversions[index];
}
inline double getUnitConversion(const std::string& name){
    int index = getUnitIndex(name.c_str());
    if (index != -1){
		return NONE;
	}
	return unitConversions[index];
}
inline double getUnitConversion(int index){
    if (index < 0 || index >= numUnits){
		return NONE;
	}
	return unitConversions[index];
}


//===------------------------------------------------------------*- C++ -*-===//
//
// This file is distributed under MIT License. See LICENSE.txt for details.
//
//===----------------------------------------------------------------------===//
//
// Copyright (c) 2017 University of Kaiserslautern.
//

#pragma once

#include <nlopt.h>

namespace gosat {

class OptConfig {
public:
    OptConfig();

    OptConfig(nlopt_algorithm global_alg, nlopt_algorithm local_alg);

    virtual ~OptConfig() = default;

    int MaxEvalCount;
    int MaxLocalEvalCount;
    double RelTolerance;
    double Bound;
    double StepSize;
    unsigned InitialPopulation;
};

class NLoptOptimizer {
public:
    NLoptOptimizer();

    NLoptOptimizer(nlopt_algorithm global_alg,
                   nlopt_algorithm local_alg = NLOPT_LN_BOBYQA);

    virtual ~NLoptOptimizer() = default;

    int optimize
            (nlopt_func func, unsigned dim, double* x,
             double* min) const noexcept;

    double eval
            (nlopt_func func, unsigned dim, const double* x) const noexcept;

    bool existsRoundingError
            (nlopt_func func,
             unsigned int dim,
             const double* x,
             const double* min) const noexcept;

    void fixRoundingErrorNearZero
            (nlopt_func const func,
             unsigned dim,
             double* x,
             double* min) const noexcept;

    int refineResult(nlopt_func func, unsigned dim, double* x, double* min);

    static bool isSupportedGlobalOptAlg(nlopt_algorithm opt_alg) noexcept;

    static bool isSupportedLocalOptAlg(nlopt_algorithm local_opt_alg) noexcept;

    static bool isRequireLocalOptAlg(nlopt_algorithm opt_alg) noexcept;

    static bool isRequirePopulation(nlopt_algorithm opt_alg) noexcept;

private:
    const nlopt_algorithm m_global_opt_alg;
    const nlopt_algorithm m_local_opt_alg;
public:
    OptConfig Config;
};
}


#ifndef COMMON_H
#define COMMON_H

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

#include <gsl/gsl_errno.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv2.h>
#include <gsl/gsl_sf_bessel.h>
#include <gsl/gsl_statistics.h>

#include <plplot/plplot.h>

#endif


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

#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_test.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_spmatrix.h>

int status = 0;

#define BASE_GSL_COMPLEX_LONG
#include "templates_on.h"
#include "test_complex_source.c"
#include "templates_off.h"
#undef  BASE_GSL_COMPLEX_LONG

#define BASE_GSL_COMPLEX
#include "templates_on.h"
#include "test_complex_source.c"
#include "templates_off.h"
#undef  BASE_GSL_COMPLEX

#define BASE_GSL_COMPLEX_FLOAT
#include "templates_on.h"
#include "test_complex_source.c"
#include "templates_off.h"
#undef  BASE_GSL_COMPLEX_FLOAT

#define BASE_LONG_DOUBLE
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_LONG_DOUBLE

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

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

#define BASE_ULONG
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_ULONG

#define BASE_LONG
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_LONG

#define BASE_UINT
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_UINT

#define BASE_INT
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_INT

#define BASE_USHORT
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_USHORT

#define BASE_SHORT
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_SHORT

#define BASE_UCHAR
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_UCHAR

#define BASE_CHAR
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_CHAR

int
main (void)
{
  const size_t num = 3;
  const size_t M[] = { 53, 40, 30 };
  const size_t N[] = { 107, 20, 30 };
  const double density[] = { 0.3, 0.2, 0.5 };
  gsl_rng * r = gsl_rng_alloc(gsl_rng_default);
  size_t i;

  for (i = 0; i < num; ++i)
    {
      test_all (M[i], N[i], density[i], r);
      test_float_all (M[i], N[i], density[i], r);
      test_long_double_all (M[i], N[i], density[i], r);
      test_ulong_all (M[i], N[i], density[i], r);
      test_long_all (M[i], N[i], density[i], r);
      test_uint_all (M[i], N[i], density[i], r);
      test_int_all (M[i], N[i], density[i], r);
      test_ushort_all (M[i], N[i], density[i], r);
      test_short_all (M[i], N[i], density[i], r);
      test_uchar_all (M[i], N[i], density[i], r);
      test_char_all (M[i], N[i], density[i], r);

      test_complex_all (M[i], N[i], density[i], r);
      test_complex_float_all (M[i], N[i], density[i], r);
      test_complex_long_double_all (M[i], N[i], density[i], r);
    }

  gsl_rng_free(r);

  exit (gsl_test_summary ());
}


#ifndef problems_h
#define problems_h

#include <ceed.h>
#include <petsc.h>
#include "../problems/cl-problems.h"
#include "../problems/neo-hookean.h"
#include "../problems/mooney-rivlin.h"

// Physics options
#define SOLIDS_PROBLEM_REGISTER(list, name, fname, physics) \
  ierr = PetscFunctionListAdd(&list->setupPhysics, name,                          \
                              PhysicsContext_ ## physics); CHKERRQ(ierr);         \
  ierr = PetscFunctionListAdd(&list->setupSmootherPhysics, name,                  \
                              PhysicsSmootherContext_ ## physics); CHKERRQ(ierr); \
  ierr = PetscFunctionListAdd(&list->setupLibceedFineLevel, name,                 \
                              SetupLibceedFineLevel_ ## fname); CHKERRQ(ierr);    \
  ierr = PetscFunctionListAdd(&list->setupLibceedLevel, name,                     \
                              SetupLibceedLevel_ ## fname); CHKERRQ(ierr);        \

typedef struct ProblemFunctions_ *ProblemFunctions;
struct ProblemFunctions_ {
  PetscFunctionList setupPhysics, setupSmootherPhysics, setupLibceedFineLevel,
                    setupLibceedLevel;
};

PetscErrorCode RegisterProblems(ProblemFunctions problem_functions);

#define SOLIDS_PROBLEM(name) \
  PetscErrorCode SetupLibceedFineLevel_ ## name (DM dm, DM dm_energy,           \
    DM dm_diagnostic, Ceed ceed, AppCtx app_ctx, CeedQFunctionContext phys_ctx, \
    PetscInt fine_level, PetscInt num_comp_u, PetscInt U_g_size,                \
    PetscInt U_loc_size, CeedVector force_ceed, CeedVector neumann_ceed,        \
    CeedData *data);                                                            \
  PetscErrorCode SetupLibceedLevel_ ## name (DM dm, Ceed ceed,                  \
    AppCtx app_ctx, PetscInt level, PetscInt num_comp_u, PetscInt U_g_size,     \
    PetscInt u_loc_size, CeedVector fine_mult, CeedData *data);                 \

SOLIDS_PROBLEM(ElasLinear);
SOLIDS_PROBLEM(ElasSSNH);
SOLIDS_PROBLEM(ElasFSCurrentNH1);
SOLIDS_PROBLEM(ElasFSCurrentNH2);
SOLIDS_PROBLEM(ElasFSInitialNH1);
SOLIDS_PROBLEM(ElasFSInitialNH2);
SOLIDS_PROBLEM(ElasFSInitialMR1);

#endif //problems_h