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

void read_MCMC_state(MCMC_info *MCMC) {
    char          filename_output_dir[SID_MAX_FILENAME_LENGTH];
    char          filename_chain_dir[SID_MAX_FILENAME_LENGTH];
    char          filename_results_dir[SID_MAX_FILENAME_LENGTH];
    char          filename_plots_dir[SID_MAX_FILENAME_LENGTH];
    char          filename_run[SID_MAX_FILENAME_LENGTH];
    char          filename_chain[SID_MAX_FILENAME_LENGTH];
    char          filename_chain_config[SID_MAX_FILENAME_LENGTH];
    char          filename_stats[SID_MAX_FILENAME_LENGTH];
    char          filename_coverage[SID_MAX_FILENAME_LENGTH];
    char          filename_chain_covariance[SID_MAX_FILENAME_LENGTH];
    char          filename_covariance[SID_MAX_FILENAME_LENGTH];
    char          filename_histograms[SID_MAX_FILENAME_LENGTH];
    char          filename_results[SID_MAX_FILENAME_LENGTH];
    char          filename_stop[SID_MAX_FILENAME_LENGTH];
    char          format_string[32];
    int           my_chain;
    int           i_P, i_DS, i_M, i_array;
    double *      V_read;
    FILE *        fp_run;
    FILE *        fp_chain;
    FILE *        fp_chain_config;
    FILE *        fp_stats;
    FILE *        fp_coverage;
    FILE *        fp_chain_covariance;
    FILE *        fp_covariance;
    FILE *        fp_histograms;
    FILE *        fp_results;
    FILE *        fp_stop;
    MCMC_DS_info *current_DS;

    set_MCMC_mode(MCMC, MCMC_MODE_DEFAULT);
    my_chain = MCMC->my_chain;

    SID_log("Reading MCMC state from {%s}...", SID_LOG_OPEN, MCMC->filename_output_dir);

    // Set directories
    sprintf(filename_output_dir, "%s/", MCMC->filename_output_dir);
    sprintf(filename_chain_dir, "%s/chains/", MCMC->filename_output_dir);
    sprintf(filename_results_dir, "%s/results/", MCMC->filename_output_dir);
    sprintf(filename_plots_dir, "%s/plots/", MCMC->filename_output_dir);
    // Set filenames
    sprintf(filename_run, "%s/run.dat", MCMC->filename_output_dir);
    sprintf(filename_chain, "%s/chain_trace_%06d.dat", filename_chain_dir, my_chain);
    sprintf(filename_chain_config, "%s/chain_config_%06d.dat", filename_chain_dir, my_chain);
    sprintf(filename_chain_covariance, "%s/chain_covariance_%06d.dat", filename_chain_dir, my_chain);
    sprintf(filename_stats, "%s/chain_stats_%06d.dat", filename_chain_dir, my_chain);
    sprintf(filename_coverage, "%s/coverage.dat", filename_results_dir);
    sprintf(filename_histograms, "%s/histograms.dat", filename_results_dir);
    sprintf(filename_covariance, "%s/covariance.dat", filename_results_dir);

    MCMC->map_P_to_M                 = NULL;
    MCMC->compute_MCMC_ln_likelihood = compute_MCMC_ln_likelihood_default;
    MCMC->params                     = NULL;
    MCMC->temperature                = 1.0;
    MCMC->n_P                        = 0;
    MCMC->n_thin                     = 1;
    MCMC->n_DS                       = 0;
    MCMC->n_M_total                  = 0;
    MCMC->n_arrays                   = 0;
    MCMC->n_M                        = NULL;
    MCMC->array                      = NULL;
    MCMC->V                          = NULL;
    MCMC->m                          = NULL;
    MCMC->b                          = NULL;
    MCMC->RNG                        = NULL;
    MCMC->flag_integrate_on          = GBP_TRUE;
    MCMC->flag_analysis_on           = GBP_TRUE;
    MCMC->first_map_call             = GBP_TRUE;
    MCMC->mode                       = MCMC_MODE_DEFAULT;
    MCMC->DS                         = NULL;
    MCMC->last                       = NULL;

    // Read/Write Header file
    if((fp_run = fopen(filename_run, "rb")) != NULL) {
        fp_run = fopen(filename_run, "rb");
        SID_fread_verify(MCMC->problem_name, sizeof(char), MCMC_NAME_SIZE, fp_run);
        SID_fread_verify(&(MCMC->n_chains), sizeof(int), 1, fp_run);
        SID_fread_verify(&(MCMC->n_avg), sizeof(int), 1, fp_run);
        SID_fread_verify(&(MCMC->flag_autocor_on), sizeof(int), 1, fp_run);
        SID_fread_verify(&(MCMC->flag_no_map_write), sizeof(int), 1, fp_run);
        SID_fread_verify(&(MCMC->n_P), sizeof(int), 1, fp_run);
        SID_log("Problem name    ={%s}", SID_LOG_COMMENT, MCMC->problem_name);
        SID_log("n_avg           ={%d}", SID_LOG_COMMENT, MCMC->n_avg);
        SID_log("flag_autocor_on ={%d}", SID_LOG_COMMENT, MCMC->flag_autocor_on);
        MCMC->P_names     = (char **)SID_malloc(sizeof(char *) * MCMC->n_P);
        MCMC->P_init      = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_new       = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_last      = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_chain     = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_limit_min = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        MCMC->P_limit_max = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
        for(i_P = 0; i_P < MCMC->n_P; i_P++)
            MCMC->P_limit_min[i_P] = -DBL_MAX * 1e-3;
        for(i_P = 0; i_P < MCMC->n_P; i_P++)
            MCMC->P_limit_max[i_P] = DBL_MAX * 1e-3;

        SID_log("Parameters (name, initial_value,limit min,limit max):", SID_LOG_OPEN);
        MCMC->P_name_length = 0;
        for(i_P = 0; i_P < MCMC->n_P; i_P++) {
            MCMC->P_names[i_P] = (char *)SID_malloc(sizeof(char) * MCMC_NAME_SIZE);
            SID_fread_verify(MCMC->P_names[i_P], sizeof(char), MCMC_NAME_SIZE, fp_run);
            SID_fread_verify(&(MCMC->P_init[i_P]), sizeof(double), 1, fp_run);
            SID_fread_verify(&(MCMC->P_limit_min[i_P]), sizeof(double), 1, fp_run);
            SID_fread_verify(&(MCMC->P_limit_max[i_P]), sizeof(double), 1, fp_run);
            MCMC->P_name_length = GBP_MAX((size_t)(MCMC->P_name_length), strlen(MCMC->P_names[i_P]));
        }
        sprintf(MCMC->P_name_format, "%%-%ds", MCMC->P_name_length);
        sprintf(format_string, "%s %%13.6le %%13.6le %%13.6le", MCMC->P_name_format);
        for(i_P = 0; i_P < MCMC->n_P; i_P++)
            SID_log(format_string, SID_LOG_COMMENT, MCMC->P_names[i_P], MCMC->P_init[i_P], MCMC->P_limit_min[i_P], MCMC->P_limit_max[i_P]);
        SID_log(NULL, SID_LOG_CLOSE | SID_LOG_NOPRINT);
        SID_fread_verify(&(MCMC->n_arrays), sizeof(int), 1, fp_run);
        SID_log("n_arrays=%d", SID_LOG_OPEN, MCMC->n_arrays);
        MCMC->array      = (double **)SID_malloc(sizeof(double *) * MCMC->n_arrays);
        MCMC->array_name = (char **)SID_malloc(sizeof(char *) * MCMC->n_arrays);
        for(i_array = 0; i_array < MCMC->n_arrays; i_array++) {
            MCMC->array[i_array]      = (double *)SID_malloc(sizeof(double) * MCMC->n_P);
            MCMC->array_name[i_array] = (char *)SID_malloc(sizeof(char) * MCMC_NAME_SIZE);
            SID_fread_verify(MCMC->array_name[i_array], sizeof(char), MCMC_NAME_SIZE, fp_run);
            SID_fread_verify(MCMC->array[i_array], sizeof(double), MCMC->n_P, fp_run);
            SID_log("array #%03d name ={%s}", SID_LOG_COMMENT, i_array, MCMC->array_name[i_array]);
        }
        SID_log(NULL, SID_LOG_CLOSE | SID_LOG_NOPRINT);
        SID_fread_verify(&(MCMC->n_DS), sizeof(int), 1, fp_run);
        SID_log("Reading %d datasets...", SID_LOG_OPEN, MCMC->n_DS);
        for(i_DS = 0; i_DS < MCMC->n_DS; i_DS++) {
            SID_log("Dataset #%03d:", SID_LOG_OPEN, i_DS);
            current_DS = (MCMC_DS_info *)SID_malloc(sizeof(MCMC_DS_info));
            SID_fread_verify(current_DS->name, sizeof(char), MCMC_NAME_SIZE, fp_run);
            SID_fread_verify(&(current_DS->n_M), sizeof(int), 1, fp_run);
            MCMC->n_M_total += current_DS->n_M;
            SID_log("name    ={%s}", SID_LOG_COMMENT, current_DS->name);
            SID_log("n_M     =%d", SID_LOG_COMMENT, current_DS->n_M);
            current_DS->M_target  = (double *)SID_malloc(sizeof(double) * current_DS->n_M);
            current_DS->dM_target = (double *)SID_malloc(sizeof(double) * current_DS->n_M);
            current_DS->params    = NULL;
            SID_fread_verify(current_DS->M_target, sizeof(double), current_DS->n_M, fp_run);
            SID_fread_verify(current_DS->dM_target, sizeof(double), current_DS->n_M, fp_run);
            SID_fread_verify(&(current_DS->n_arrays), sizeof(int), 1, fp_run);
            SID_log("n_arrays=%d", SID_LOG_OPEN, current_DS->n_arrays);
            current_DS->array      = (double **)SID_malloc(sizeof(double *) * current_DS->n_arrays);
            current_DS->array_name = (char **)SID_malloc(sizeof(char *) * current_DS->n_arrays);
            for(i_array = 0; i_array < current_DS->n_arrays; i_array++) {
                current_DS->array[i_array]      = (double *)SID_malloc(sizeof(double) * current_DS->n_M);
                current_DS->array_name[i_array] = (char *)SID_malloc(sizeof(char) * MCMC_NAME_SIZE);
                SID_fread_verify(current_DS->array_name[i_array], sizeof(char), MCMC_NAME_SIZE, fp_run);
                SID_fread_verify(current_DS->array[i_array], sizeof(double), current_DS->n_M, fp_run);
                SID_log("array #%03d name={%s}", SID_LOG_COMMENT, i_array, current_DS->array_name[i_array]);
            }
            SID_log(NULL, SID_LOG_CLOSE | SID_LOG_NOPRINT);
            current_DS->next = NULL;
            if(MCMC->DS == NULL)
                MCMC->DS = current_DS;
            else
                MCMC->last->next = current_DS;
            MCMC->last = current_DS;
            SID_log(NULL, SID_LOG_CLOSE | SID_LOG_NOPRINT);
        }
        SID_log("Done.", SID_LOG_CLOSE);
        fclose(fp_run);
    }

    // ... fetch the number of intervals that have already been computed ...
    fp_chain_config = fopen(filename_chain_config, "rb");
    V_read          = (double *)SID_malloc(sizeof(double) * MCMC->n_P * MCMC->n_P);
    SID_fread_verify(&(MCMC->n_iterations), sizeof(int), 1, fp_chain_config);
    SID_fread_verify(&(MCMC->n_iterations_burn), sizeof(int), 1, fp_chain_config);

    // ... fetch the temperature and covariance matrix that was being used
    SID_fread_verify(&(MCMC->temperature), sizeof(double), 1, fp_chain_config);
    SID_fread_verify(V_read, sizeof(double), MCMC->n_P * MCMC->n_P, fp_chain_config);
    set_MCMC_covariance(MCMC, V_read);
    SID_free(SID_FARG V_read);

    // Initialize dataset arrays
    init_MCMC_arrays(MCMC);

    SID_log("# burn  iterations = %d", SID_LOG_COMMENT, MCMC->n_iterations_burn);
    SID_log("# total iterations = %d", SID_LOG_COMMENT, MCMC->n_iterations);
    SID_log("Temperature        = %le", SID_LOG_COMMENT, MCMC->temperature);
    fclose(fp_chain_config);

    SID_log("Done.", SID_LOG_CLOSE);
}


/*
 * BRAINS
 * (B)LR (R)everberation-mapping (A)nalysis (I)n AGNs with (N)ested (S)ampling
 * Yan-Rong Li, liyanrong@ihep.ac.cn
 * Thu, Aug 4, 2016
 */

#ifdef SpecAstro

#include <math.h>
#include <stddef.h>
#include <string.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <fftw3.h>

#include "brains.h"

/*
 * calculate SA phase and 2D TF
 */
void calculate_sa_transfun2d_from_blrmodel(const void *pm, double *transv, double *trans2d, int n_vel, int flag_save)
{
  int i;
  double *sa_model, *rm_model;

  /* share all parameters */
  if(parset.flag_sa_par_mutual == 0)
  {
    gen_cloud_sample(pm, 5, 0);
    transfun_2d_cal_with_sample(transv, trans2d, n_vel);
    calculate_sa_with_sample(pm);
  }
  /* share mass and inclination */
  else if(parset.flag_sa_par_mutual == 1)
  {
    rm_model = (double *)pm;
    sa_model = rm_model + num_params_blr;
    sa_model[idx_sa_par_mutual[0]] = rm_model[idx_rm_par_mutual[0]]; //mbh
    sa_model[idx_sa_par_mutual[1]] = rm_model[idx_rm_par_mutual[1]]; //inc

    gen_cloud_sample(pm, 2, flag_save);
    transfun_2d_cal_with_sample(transv, trans2d, n_vel);

    gen_sa_cloud_sample((void *)sa_model, 3, flag_save);
    calculate_sa_with_sample(pm);
  }
  /* share inclination, opening angle and all dynamical parameters */
  else
  {
    rm_model = (double *)pm;
    sa_model = rm_model + num_params_blr;

    sa_model[idx_sa_par_mutual[1]] = rm_model[idx_rm_par_mutual[1]]; //inc
    sa_model[idx_sa_par_mutual[2]] = rm_model[idx_rm_par_mutual[2]]; //opening angle
    /* dynamical parameters from mass on */
    for(i=idx_sa_par_mutual[0]; i<num_params_sa_blr_model; i++)
    {
      sa_model[i] = rm_model[i]; /* note the BLR models are identical */
    }

    gen_cloud_sample(pm, 2, flag_save);
    transfun_2d_cal_with_sample(transv, trans2d, n_vel);

    gen_sa_cloud_sample((void *)sa_model, 3, flag_save);
    calculate_sa_with_sample(pm);
  }
}

/*
 * calculate SA phase and 1D TF
 */
void calculate_sa_transfun_from_blrmodel(const void *pm, int flag_save)
{
  double *sa_model, *rm_model;

  if(parset.flag_sa_par_mutual == 0)
  {
    gen_cloud_sample(pm, 4, flag_save);
    transfun_1d_cal_with_sample();
    calculate_sa_with_sample(pm);
  }
  else
  {
    rm_model = (double *)pm;
    sa_model = rm_model + num_params_blr;
    sa_model[idx_sa_par_mutual[1]] = rm_model[idx_rm_par_mutual[1]]; // inc
    sa_model[idx_sa_par_mutual[2]] = rm_model[idx_rm_par_mutual[2]]; // opening angle

    gen_cloud_sample(pm, 1, flag_save);
    transfun_1d_cal_with_sample();

    gen_sa_cloud_sample((void *)sa_model, 3, flag_save);
    calculate_sa_with_sample(pm);
  }
}

/*
 * calculate SA phase and line profile from cloud sample
 */
void calculate_sa_with_sample(const void *pm)
{
  int i, j, k, idV;
  double V, V_offset, dV, y, z, alpha, beta, flux_norm, phase, *phase_norm, *alpha_cent, *beta_cent;
  double DA, PA, FA, CO, cos_PA, sin_PA;
  double *pmodel = (double *)pm;

  phase_norm = workspace_phase;
  alpha_cent = phase_norm + n_vel_sa_data;
  beta_cent = alpha_cent + n_vel_sa_data;

  /* angular size distance */
  DA = exp(pmodel[num_params_blr + num_params_sa_blr_model]);
  /* position angle */
  PA = pmodel[num_params_blr + num_params_sa_blr_model + 1]/180.0 * PI;
  /* line flux scaling factor */
  FA = exp(pmodel[num_params_blr + num_params_sa_blr_model+2]);
  /*
   * line center offset: V+dV = (w-(w0+dw0))/(w0+dw0) ==> dV = - dw0/w0
   * equivalent to redshift offset:  dz = -(1+z) dw0/w0
   */
  CO = -pmodel[num_params_blr + num_params_sa_blr_model+3]/parset.sa_linecenter * C_Unit;

  /* North through East (E of N), 180 deg ambiguity */
  cos_PA = cos(PA);
  sin_PA = sin(PA);

  dV = vel_sa_data[1] - vel_sa_data[0];

  for(i=0; i<n_vel_sa_data; i++)
  {
    for(k=0; k<n_base_sa_data; k++)
    {
      phase_sa[k*n_vel_sa_data + i] = 0.0;
    }
    phase_norm[i] = 0.0;
    alpha_cent[i] = 0.0;
    beta_cent[i] = 0.0;
  }

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
    y = clouds_alpha[i];
    z = clouds_beta[i];

    //alpha = y * cos_PA + z * sin_PA;
    //beta = -y * sin_PA + z * cos_PA;
    alpha = y;
    beta = z;

    for(j=0; j< parset.n_vel_per_cloud; j++)
    {
      V = clouds_vel[i*parset.n_vel_per_cloud + j] + CO;
      V_offset = V + bin_offset * dV; /* bin type: center or left edge */
      if(V_offset<vel_sa_data[0] || V_offset >= vel_sa_data[n_vel_sa_data-1]+dV)
        continue;
      idV = (V_offset - vel_sa_data[0])/dV;

      phase_norm[idV] += clouds_weight[i];
      alpha_cent[idV] += alpha * clouds_weight[i];
      beta_cent[idV] += beta * clouds_weight[i];
    }
  }

  /* normalize line spectrum */
  flux_norm = 0.0;
  for(j=0; j<n_vel_sa_data; j++)
  {
    flux_norm += phase_norm[j];
  }
  flux_norm /= (sa_flux_norm * n_vel_sa_data);
  for(j=0; j<n_vel_sa_data; j++)
  {
    Fline_sa[j] = FA * phase_norm[j]/(flux_norm + EPS);
  }

  for(j=0; j<n_vel_sa_data; j++)
  {
    y = alpha_cent[j];
    z = beta_cent[j];

    alpha_cent[j] = y * cos_PA + z * sin_PA;
    beta_cent[j] = -y * sin_PA + z * cos_PA;

    alpha_cent[j] = (alpha_cent[j]/(phase_norm[j]+EPS)) / DA;
    beta_cent[j] =  (beta_cent[j]/(phase_norm[j]+EPS)) / DA;
  }

  /* phi = -2*pi * f_line * B/lambda * X/DA */
  for(k=0; k<n_base_sa_data; k++)
  {
    for(j=0; j<n_vel_sa_data; j++)
    {
      phase = base_sa_data[k*2] * alpha_cent[j] + base_sa_data[k*2 + 1] * beta_cent[j];
      phase_sa[k*n_vel_sa_data + j] = -Fline_sa[j]/(1.0+Fline_sa[j]) * phase;
    }
  }

  /* smooth */
  sa_smooth_run(vel_sa_data, Fline_sa, n_vel_sa_data, phase_sa, n_base_sa_data);

  return;
}

/*
 * calculate SA phase and line profile from cloud sample at simulated mesh.
 */
void calculate_sa_sim_with_sample(const void *pm, double *vel_sa, int n_vel_sa, double *base_sa, int n_base_sa,
                                  double *p_sa, double *F_sa)
{
  int i, j, k, idV;
  double V, V_offset, dV, y, z, alpha, beta, flux_norm, phase, *phase_norm, *alpha_cent, *beta_cent;
  double DA, PA, FA, CO, cos_PA, sin_PA;
  double *pmodel = (double *)pm;

  phase_norm = workspace_phase;
  alpha_cent = phase_norm + n_vel_sa;
  beta_cent = alpha_cent + n_vel_sa;

  /* angular size distance */
  DA = exp(pmodel[num_params_blr + num_params_sa_blr_model]);
  /* position angle */
  PA = pmodel[num_params_blr + num_params_sa_blr_model + 1]/180.0 * PI;
  /* line flux scaling factor */
  FA = exp(pmodel[num_params_blr + num_params_sa_blr_model+2]);
  /*
   * line center offset: V+dV = (w-(w0+dw0))/(w0+dw0) ==> dV = - dw0/w0
   * equivalent to redshift offset:  dz = -(1+z) dw0/w0
   */
  CO = -pmodel[num_params_blr + num_params_sa_blr_model+3]/parset.sa_linecenter * C_Unit;

  /* North through East (E of N), 180 deg ambiguity */
  cos_PA = cos(PA);
  sin_PA = sin(PA);

  dV = vel_sa[1] - vel_sa[0];

  for(i=0; i<n_vel_sa; i++)
  {
    for(k=0; k<n_base_sa; k++)
    {
      p_sa[k*n_vel_sa + i] = 0.0;
    }
    phase_norm[i] = 0.0;
    alpha_cent[i] = 0.0;
    beta_cent[i] = 0.0;
  }

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
    y = clouds_alpha[i];
    z = clouds_beta[i];

    //alpha = y * cos_PA + z * sin_PA;
    //beta = -y * sin_PA + z * cos_PA;
    alpha = y;
    beta = z;

    for(j=0; j< parset.n_vel_per_cloud; j++)
    {
      V = clouds_vel[i*parset.n_vel_per_cloud + j] + CO;
      V_offset = V + bin_offset * dV; /* bin type: center or left edge */
      if(V_offset<vel_sa[0] || V_offset >= vel_sa[n_vel_sa-1]+dV)
        continue;
      idV = (V_offset - vel_sa[0])/dV;

      phase_norm[idV] += clouds_weight[i];
      alpha_cent[idV] += alpha * clouds_weight[i];
      beta_cent[idV] += beta * clouds_weight[i];
    }
  }

  /* normalize line spectrum */
  flux_norm = 0.0;
  for(j=0; j<n_vel_sa; j++)
  {
    flux_norm += phase_norm[j];
  }
  flux_norm /= (sa_flux_norm * n_vel_sa);
  for(j=0; j<n_vel_sa; j++)
  {
    F_sa[j] = FA * phase_norm[j]/(flux_norm + EPS);
  }

  for(j=0; j<n_vel_sa; j++)
  {
    y = alpha_cent[j];
    z = beta_cent[j];

    alpha_cent[j] = y * cos_PA + z * sin_PA;
    beta_cent[j] = -y * sin_PA + z * cos_PA;

    alpha_cent[j] = (alpha_cent[j]/(phase_norm[j]+EPS)) / DA;
    beta_cent[j] =  (beta_cent[j]/(phase_norm[j]+EPS)) / DA;
  }

  /* phi = -2*pi * f_line * B/lambda * X/DA */
  for(k=0; k<n_base_sa; k++)
  {
    for(j=0; j<n_vel_sa; j++)
    {
      phase = base_sa[k*2] * alpha_cent[j] + base_sa[k*2 + 1] * beta_cent[j];
      p_sa[k*n_vel_sa + j] = -F_sa[j]/(1.0+F_sa[j]) * phase;
    }
  }

  /* smooth */
  sa_smooth_run(vel_sa, F_sa, n_vel_sa, p_sa, n_base_sa);

  return;
}

/*
 * calculate SA phase and line profile from a BLR model.
 */
void calculate_sa_from_blrmodel(const void *pm, int flag_save)
{
  gen_sa_cloud_sample(pm, 3, 0);
  calculate_sa_with_sample(pm);
  return;
}

void set_sa_blr_model()
{
  switch(parset.flag_sa_blrmodel)
  {
    case 0:
      num_params_sa_blr_model = num_params_MyBLRmodel2d;
      gen_sa_cloud_sample = gen_cloud_sample_mymodel;
      break;

    case 1:
      num_params_sa_blr_model = sizeof(BLRmodel1)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model1;
      break;

    case 2:
      num_params_sa_blr_model = sizeof(BLRmodel2)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model2;
      break;

    case 3:
      num_params_sa_blr_model = sizeof(BLRmodel3)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model3;
      break;

    case 4:
      num_params_sa_blr_model = sizeof(BLRmodel4)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model4;
      break;

    case 5:
      num_params_sa_blr_model = sizeof(BLRmodel5)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model5;
      break;

    case 6:
      num_params_sa_blr_model = sizeof(BLRmodel6)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model6;
      break;

    case 7:
      num_params_sa_blr_model = sizeof(BLRmodel7)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model7;
      break;

    case 8:
      num_params_sa_blr_model = sizeof(BLRmodel8)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model8;
      break;

    case 9:
      num_params_sa_blr_model = sizeof(BLRmodel9)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model9;
      break;

    default:
      num_params_sa_blr_model = sizeof(BLRmodel1)/sizeof(double);
      gen_sa_cloud_sample = gen_cloud_sample_model1;
      break;
  }

  return;
}

/*
 * set the index of mutual parameters (BH mass and inclination) in RM and SA BLR models.
 */
void set_idx_par_mutual()
{
  switch(parset.flag_blrmodel)
  {
    case 0:
      idx_rm_par_mutual[0] = offsetof(MyBLRmodel, mbh);
      idx_rm_par_mutual[1] = offsetof(MyBLRmodel, inc);
      idx_rm_par_mutual[2] = offsetof(MyBLRmodel, opn);
      break;

    case 1:
      idx_rm_par_mutual[0] = offsetof(BLRmodel1, mbh);
      idx_rm_par_mutual[1] = offsetof(BLRmodel1, inc);
      idx_rm_par_mutual[2] = offsetof(BLRmodel1, opn);
      break;

    case 2:
      idx_rm_par_mutual[0] = offsetof(BLRmodel2, mbh);
      idx_rm_par_mutual[1] = offsetof(BLRmodel2, inc);
      idx_rm_par_mutual[2] = offsetof(BLRmodel2, opn);
      break;

    case 3:
      idx_rm_par_mutual[0] = offsetof(BLRmodel3, mbh);
      idx_rm_par_mutual[1] = offsetof(BLRmodel3, inc);
      idx_rm_par_mutual[2] = offsetof(BLRmodel3, opn);
      break;

    case 4:
      idx_rm_par_mutual[0] = offsetof(BLRmodel4, mbh);
      idx_rm_par_mutual[1] = offsetof(BLRmodel4, inc);
      idx_rm_par_mutual[2] = offsetof(BLRmodel4, opn);
      break;

    case 5:
      idx_rm_par_mutual[0] = offsetof(BLRmodel5, mbh);
      idx_rm_par_mutual[1] = offsetof(BLRmodel5, inc);
      idx_rm_par_mutual[2] = offsetof(BLRmodel5, opn);
      break;

    case 6:
      idx_rm_par_mutual[0] = offsetof(BLRmodel6, mbh);
      idx_rm_par_mutual[1] = offsetof(BLRmodel6, inc);
      idx_rm_par_mutual[2] = offsetof(BLRmodel6, opn);
      break;

    case 7:
      idx_rm_par_mutual[0] = offsetof(BLRmodel7, mbh);
      idx_rm_par_mutual[1] = offsetof(BLRmodel7, inc);
      idx_rm_par_mutual[2] = offsetof(BLRmodel7, opn);
      break;

    case 8:
      idx_rm_par_mutual[0] = offsetof(BLRmodel8, mbh);
      idx_rm_par_mutual[1] = offsetof(BLRmodel8, inc);
      idx_rm_par_mutual[2] = offsetof(BLRmodel8, theta_min);
      break;

    case 9:
      idx_rm_par_mutual[0] = offsetof(BLRmodel9, mbh);
      idx_rm_par_mutual[1] = offsetof(BLRmodel9, inc);
      idx_rm_par_mutual[2] = offsetof(BLRmodel9, opn);
      break;
  }

  switch(parset.flag_sa_blrmodel)
  {
    case 0:
      idx_sa_par_mutual[0] = offsetof(MyBLRmodel, mbh);
      idx_sa_par_mutual[1] = offsetof(MyBLRmodel, inc);
      idx_sa_par_mutual[2] = offsetof(MyBLRmodel, opn);
      break;

    case 1:
      idx_sa_par_mutual[0] = offsetof(SABLRmodel1, mbh);
      idx_sa_par_mutual[1] = offsetof(SABLRmodel1, inc);
      idx_sa_par_mutual[2] = offsetof(SABLRmodel1, opn);
      break;

    case 2:
      idx_sa_par_mutual[0] = offsetof(SABLRmodel2, mbh);
      idx_sa_par_mutual[1] = offsetof(SABLRmodel2, inc);
      idx_sa_par_mutual[2] = offsetof(SABLRmodel2, opn);
      break;

    case 3:
      idx_sa_par_mutual[0] = offsetof(SABLRmodel3, mbh);
      idx_sa_par_mutual[1] = offsetof(SABLRmodel3, inc);
      idx_sa_par_mutual[2] = offsetof(SABLRmodel3, opn);
      break;

    case 4:
      idx_sa_par_mutual[0] = offsetof(SABLRmodel4, mbh);
      idx_sa_par_mutual[1] = offsetof(SABLRmodel4, inc);
      idx_sa_par_mutual[2] = offsetof(SABLRmodel4, opn);
      break;

    case 5:
      idx_sa_par_mutual[0] = offsetof(SABLRmodel5, mbh);
      idx_sa_par_mutual[1] = offsetof(SABLRmodel5, inc);
      idx_sa_par_mutual[2] = offsetof(SABLRmodel5, opn);
      break;

    case 6:
      idx_sa_par_mutual[0] = offsetof(SABLRmodel6, mbh);
      idx_sa_par_mutual[1] = offsetof(SABLRmodel6, inc);
      idx_sa_par_mutual[2] = offsetof(SABLRmodel6, opn);
      break;

    case 7:
      idx_sa_par_mutual[0] = offsetof(SABLRmodel7, mbh);
      idx_sa_par_mutual[1] = offsetof(SABLRmodel7, inc);
      idx_sa_par_mutual[2] = offsetof(SABLRmodel7, opn);
      break;

    case 8:
      idx_sa_par_mutual[0] = offsetof(SABLRmodel8, mbh);
      idx_sa_par_mutual[1] = offsetof(SABLRmodel8, inc);
      idx_sa_par_mutual[2] = offsetof(SABLRmodel8, theta_min);
      break;

    case 9:
      idx_sa_par_mutual[0] = offsetof(SABLRmodel9, mbh);
      idx_sa_par_mutual[1] = offsetof(SABLRmodel9, inc);
      idx_sa_par_mutual[2] = offsetof(SABLRmodel9, opn);
      break;
  }

  idx_sa_par_mutual[0] /= sizeof(double);
  idx_sa_par_mutual[1] /= sizeof(double);
  idx_sa_par_mutual[2] /= sizeof(double);
  idx_rm_par_mutual[0] /= sizeof(double);
  idx_rm_par_mutual[1] /= sizeof(double);
  idx_rm_par_mutual[2] /= sizeof(double);

  return;
}

/*
 * setup  SA BLR model parameter range.
 */
// model 1
void set_sa_blr_range_model1()
{
  int i;

  i = 0;
  //mu
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //beta
  sa_blr_range_model[i][0] = 0.001;
  sa_blr_range_model[i++][1] = 2.0;
  //F
  sa_blr_range_model[i][0] = 0.001;
  sa_blr_range_model[i++][1] = 0.999;
  //inc
  sa_blr_range_model[i][0] = 0.0;  // in cosine
  sa_blr_range_model[i++][1] = 1.0;
  //opn
  sa_blr_range_model[i][0] = 0.0;  // in rad
  sa_blr_range_model[i++][1] = 90.0;
  //k
  sa_blr_range_model[i][0] = -0.5;
  sa_blr_range_model[i++][1] = 0.5;
  //mbh
  sa_blr_range_model[i][0] = log(mass_range[0]);
  sa_blr_range_model[i++][1] = log(mass_range[1]);
  //lambda
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.5;
  //q
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;

  //sa_blr_range_model[2][1] = fmin(sa_blr_range_model[2][1], log(rcloud_max_set));
  return;
}

// model 2
void set_sa_blr_range_model2()
{
  int i;

  i = 0;
  //mu
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //beta
  sa_blr_range_model[i][0] = 0.001;
  sa_blr_range_model[i++][1] = 2.0;
  //F
  sa_blr_range_model[i][0] = 0.001;
  sa_blr_range_model[i++][1] = 0.999;
  //inc
  sa_blr_range_model[i][0] = 0.0;  // in cosine
  sa_blr_range_model[i++][1] = 1.0;
  //opn
  sa_blr_range_model[i][0] = 0.0;  // in rad
  sa_blr_range_model[i++][1] = 90.0;
  //k
  sa_blr_range_model[i][0] = -0.5;
  sa_blr_range_model[i++][1] = 0.5;
  //mbh
  sa_blr_range_model[i][0] = log(mass_range[0]);
  sa_blr_range_model[i++][1] = log(mass_range[1]);
  //sigr
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //sigtheta
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;

  return;
}

// model 3
void set_sa_blr_range_model3()
{
  int i;

  i = 0;
  //Rin
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //F
  sa_blr_range_model[i][0] = log(1.0);
  sa_blr_range_model[i++][1] = log(1.0e2);
  //alpha
  sa_blr_range_model[i][0] = -3.0;
  sa_blr_range_model[i++][1] = 3.0;
  //inc
  sa_blr_range_model[i][0] = 0.0;  // in cosine
  sa_blr_range_model[i++][1] = 1.0;
  //opn
  sa_blr_range_model[i][0] = 0.0;   // in rad
  sa_blr_range_model[i++][1] = 90.0;
  //k
  sa_blr_range_model[i][0] = -0.5;
  sa_blr_range_model[i++][1] = 0.5;
  //mbh
  sa_blr_range_model[i][0] = log(mass_range[0]);
  sa_blr_range_model[i++][1] = log(mass_range[1]);
  //xi
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //q
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;

  //rcloud_max_set = fmax(rcloud_max_set, exp(sa_blr_range_model[3][1] + sa_blr_range_model[4][1]));
  return;
}

// model 4
void set_sa_blr_range_model4()
{
  int i;

  i = 0;
  //Rin
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //F
  sa_blr_range_model[i][0] = log(1.0);
  sa_blr_range_model[i++][1] = log(1.0e2);
  //alpha
  sa_blr_range_model[i][0] = -3.0;
  sa_blr_range_model[i++][1] = 3.0;
  //inc
  sa_blr_range_model[i][0] = 0.0; // in cosine
  sa_blr_range_model[i++][1] = 1.0;
  //opn
  sa_blr_range_model[i][0] = 0.0;  // in rad
  sa_blr_range_model[i++][1] = 90.0;
  //k
  sa_blr_range_model[i][0] = -0.5;
  sa_blr_range_model[i++][1] = 0.5;
  //mbh
  sa_blr_range_model[i][0] = log(mass_range[0]);
  sa_blr_range_model[i++][1] = log(mass_range[1]);
  //xi
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = sqrt(2.0)/2.0;
  //q
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;

  //rcloud_max_set = fmax(rcloud_max_set, exp(sa_blr_range_model[3][1] + sa_blr_range_model[4][1]));
  return;
}

// model 5
void set_sa_blr_range_model5()
{
  int i;

  i = 0;
  //mu
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //Fin
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //Fout
  sa_blr_range_model[i][0] = log(1.0);
  sa_blr_range_model[i++][1] = log(10.0);
  //alpha
  sa_blr_range_model[i][0] = 1.0;
  sa_blr_range_model[i++][1] = 3.0;
  //inc
  sa_blr_range_model[i][0] = 0.0; // in cosine
  sa_blr_range_model[i++][1] = 1.0;
  //opn
  sa_blr_range_model[i][0] = 0.0;  // in degree
  sa_blr_range_model[i++][1] = 90.0;
  //k
  sa_blr_range_model[i][0] = -0.5;
  sa_blr_range_model[i++][1] = 0.5;
  //beta
  sa_blr_range_model[i][0] = 1.0;
  sa_blr_range_model[i++][1] = 5.0;
  //xi
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //mbh
  sa_blr_range_model[i][0] = log(mass_range[0]);
  sa_blr_range_model[i++][1] = log(mass_range[1]);
  //fellip
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //fflow
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //sigr_circ
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(0.1);
  //sigthe_circ
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(1.0);
  //sigr_rad
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(0.1);
  //sigthe_rad
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(1.0);
  //theta_rot
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 90.0;
  //sig_turb
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(0.1);

  //rcloud_max_set = fmax(rcloud_max_set, exp(sa_blr_range_model[2][1] + sa_blr_range_model[4][1]));

  return;
}

// model 6
void set_sa_blr_range_model6()
{
  int i;

  i = 0;
  //mu
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //beta
  sa_blr_range_model[i][0] = 0.001;
  sa_blr_range_model[i++][1] = 2.0;
  //F
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //inc
  sa_blr_range_model[i][0] = 0.0; //in cosine
  sa_blr_range_model[i++][1] = 1.0;
  //opn
  sa_blr_range_model[i][0] = 0.0;  // in rad
  sa_blr_range_model[i++][1] = 90.0;
  //k
  sa_blr_range_model[i][0] = -0.5;
  sa_blr_range_model[i++][1] = 0.5;
  //gamma
  sa_blr_range_model[i][0] = 1.0;
  sa_blr_range_model[i++][1] = 5.0;
  //xi
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //mbh
  sa_blr_range_model[i][0] = log(mass_range[0]);
  sa_blr_range_model[i++][1] = log(mass_range[1]);
  //fellip
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //fflow
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //sigr_circ
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(0.1);
  //sigthe_circ
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(1.0);
  //sigr_rad
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(0.1);
  //sigthe_rad
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(1.0);
  //theta_rot
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 90.0;
  //sig_turb
  sa_blr_range_model[i][0] = log(0.0001);
  sa_blr_range_model[i++][1] = log(0.1);

  //rcloud_max_set = 44.85;
  //sa_blr_range_model[2][1] = fmin(sa_blr_range_model[2][1], log(rcloud_max_set));
  return;
}

// model 7
void set_sa_blr_range_model7()
{
  int i;

  i = 0;
  //mu
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //beta
  sa_blr_range_model[i][0] = 0.001;
  sa_blr_range_model[i++][1] = 2.0;
  //F
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //inc
  sa_blr_range_model[i][0] = 0.0;  // in cosine
  sa_blr_range_model[i++][1] = 1.0;
  //opn
  sa_blr_range_model[i][0] = 0.0;  // in rad
  sa_blr_range_model[i++][1] = 90.0;
  //k
  sa_blr_range_model[i][0] = -0.5;
  sa_blr_range_model[i++][1] = 0.5;
  //gamma
  sa_blr_range_model[i][0] = 1.0;
  sa_blr_range_model[i++][1] = 5.0;
  //xi
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;

  //fsh
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //mu_un
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //beta_un
  sa_blr_range_model[i][0] = 0.001;
  sa_blr_range_model[i++][1] = 2.0;
  //F_un
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //opn_un
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 90.0;

  //mbh
  sa_blr_range_model[i][0] = log(mass_range[0]);
  sa_blr_range_model[i++][1] = log(mass_range[1]);
  //fellip
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //fflow
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //sigr_circ
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(0.1);
  //sigthe_circ
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(1.0);
  //sigr_rad
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(0.1);
  //sigthe_rad
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(1.0);
  //theta_rot
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 90.0;

  //fellip_un
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //fflow_un
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;

  //sig_turb
  sa_blr_range_model[i][0] = log(0.001);
  sa_blr_range_model[i++][1] = log(0.1);

  return;
}

// model 8
void set_sa_blr_range_model8()
{
  int i;

  i = 0;
  //theta_min
  sa_blr_range_model[i][0] = 20.0;     // in degree
  sa_blr_range_model[i++][1] = 90.0;
  //dtheta_max
  sa_blr_range_model[i][0] = 0.0;      // in degree
  sa_blr_range_model[i++][1] = 90.0;
  //r_min
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.25);
  //fr_max
  sa_blr_range_model[i][0] = 1.0;  //
  sa_blr_range_model[i++][1] = 10.0;
  //gamma
  sa_blr_range_model[i][0] = 0.0;  //
  sa_blr_range_model[i++][1] = 3.0;
  //alpha
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 3.0;
  //lambda
  sa_blr_range_model[i][0] = -3.0;
  sa_blr_range_model[i++][1] = 0.0;
  //k
  sa_blr_range_model[i][0] = -0.5;
  sa_blr_range_model[i++][1] = 0.5;
  //xi
  sa_blr_range_model[i][0] = 0.0;
  sa_blr_range_model[i++][1] = 1.0;
  //Rv
  sa_blr_range_model[i][0] = log(10.0);
  sa_blr_range_model[i++][1] = log(50.0);
  //Rblr
  sa_blr_range_model[i][0] = log(10.0);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //inc
  sa_blr_range_model[i][0] = 0.0;   // cos(inc)
  sa_blr_range_model[i++][1] = 1.0;
  //mbh
  sa_blr_range_model[i][0] = log(mass_range[0]);
  sa_blr_range_model[i++][1] = log(mass_range[1]);

  return;
}

// model 9
void set_sa_blr_range_model9()
{
  int i;

  i = 0;
  //mu
  sa_blr_range_model[i][0] = log(0.1);
  sa_blr_range_model[i++][1] = log(rcloud_max_set*0.5);
  //beta
  sa_blr_range_model[i][0] = 0.001;
  sa_blr_range_model[i++][1] = 2.0;
  //F
  sa_blr_range_model[i][0] = 0.001;
  sa_blr_range_model[i++][1] = 0.999;
  //inc
  sa_blr_range_model[i][0] = 0.0;  // in cosine
  sa_blr_range_model[i++][1] = 1.0;
  //opn
  sa_blr_range_model[i][0] = 0.0;  // in degree
  sa_blr_range_model[i++][1] = 90.0;
  //mbh
  sa_blr_range_model[i][0] = log(mass_range[0]);
  sa_blr_range_model[i++][1] = log(mass_range[1]);

  return;
}

/*!
 * This function copes with parameter fixing.\n
 * Only fix BLR model parameters.
 */
void set_par_fix_sa_blrmodel()
{
  int i;
  char *pstr;

  npar_fix = 0;

  if(thistask == roottask)
  {
    pstr = parset.sa_str_par_fix_val;
    // set the default value if not provided.
    for(i=strlen(parset.sa_str_par_fix); i<num_params_sa_blr_model; i++)
      parset.sa_str_par_fix[i] = '0';

    for(i=0; i<num_params_sa_blr_model; i++)
    {
      if(parset.sa_str_par_fix[i] == '0')
      {
        par_fix[num_params_blr + i] = 0;
        par_fix_val[num_params_blr + i] = -DBL_MAX;  /* set to be the smallest value */
      }
      else if(parset.sa_str_par_fix[i] == '1')
      {
        if(pstr == NULL)
        {
          printf("# %d-th SA BLR parameter value is not provided (counting from 0).\n", i);
          exit(0);
        }
        par_fix[num_params_blr + i] = 1;
        sscanf(pstr, "%lf", &par_fix_val[num_params_blr + i]);
        npar_fix++;
        printf("# %d-th SA BLR parameter fixed, value= %f.\n", i, par_fix_val[num_params_blr + i]);
        pstr = strchr(pstr, ':'); /* values are separated by ":" */
        if(pstr!=NULL)
        {
          pstr++;
        }
      }
      else   // default value
      {
        par_fix[num_params_blr + i] = 0;
        par_fix_val[num_params_blr + i] = -DBL_MAX;
      }
    }
  }

  MPI_Bcast(par_fix + num_params_blr, num_params_sa_blr_model, MPI_INT, roottask, MPI_COMM_WORLD);
  MPI_Bcast(par_fix_val + num_params_blr, num_params_sa_blr_model, MPI_DOUBLE, roottask, MPI_COMM_WORLD);

  return;
}

int nd_fft_sa, nd_fft_sa_cal, npad_sa;

fftw_complex *sa_data_fft, *sa_resp_fft, *sa_conv_fft;
fftw_plan sa_pdata, sa_presp, sa_pback;
double *sa_real_data, *sa_real_resp, *sa_real_conv;

/*!
 * This function initiates workspace for FFT.
 */
void sa_smooth_init(int n_v_sa, const double *v_sa, double sigV)
{
  int i;
  double dV;

  npad_sa = fmin(n_v_sa * 0.1, 20);
  nd_fft_sa = n_v_sa+npad_sa*2;
  nd_fft_sa_cal = nd_fft_sa/2+1;

  sa_data_fft = (fftw_complex *) fftw_malloc((nd_fft_sa_cal) * sizeof(fftw_complex));
  sa_resp_fft = (fftw_complex *) fftw_malloc((nd_fft_sa_cal) * sizeof(fftw_complex));
  sa_conv_fft = (fftw_complex *) fftw_malloc((nd_fft_sa_cal) * sizeof(fftw_complex));

  sa_real_data = (double *)fftw_malloc(nd_fft_sa * sizeof(double));
  //real_resp = (double *)fftw_malloc(nd_fft_sa * sizeof(double));
  sa_real_conv = (double *)fftw_malloc(nd_fft_sa * sizeof(double));

  sa_pdata = fftw_plan_dft_r2c_1d(nd_fft_sa, sa_real_data, sa_data_fft, FFTW_PATIENT);
  //presp = fftw_plan_dft_r2c_1d(nd_fft_sa, sa_real_resp, sa_resp_fft, FFTW_PATIENT);
  sa_pback = fftw_plan_dft_c2r_1d(nd_fft_sa, sa_conv_fft, sa_real_conv, FFTW_PATIENT);

  dV = v_sa[1] - v_sa[0];
  for(i=0; i<nd_fft_sa_cal; i++)
  {
    sa_resp_fft[i][0] = exp(-2.0 * PI*PI * sigV/dV*sigV/dV * i*i*1.0/nd_fft_sa/nd_fft_sa)/nd_fft_sa;
    sa_resp_fft[i][1] = 0.0;
  }
  return;
}

/*!
 * This function finalizes FFT.
 */
void sa_smooth_end()
{
  fftw_destroy_plan(sa_pdata);
  fftw_destroy_plan(sa_pback);

  fftw_free(sa_data_fft);
  fftw_free(sa_resp_fft);
  fftw_free(sa_conv_fft);

  fftw_free(sa_real_data);
  //fftw_free(sa_real_resp);
  fftw_free(sa_real_conv);
  return;
}

void sa_smooth_run(double *v_sa, double *F_sa, int n_v_sa, double *p_sa, int n_base_sa)
{
  int i, j;
  /* first profile */
  memcpy(sa_real_data+npad_sa, F_sa, n_v_sa*sizeof(double));
  for(i=0; i<npad_sa; i++)
    sa_real_data[i] = sa_real_data[nd_fft_sa-1-i] = 0.0;

  /* FFT of line */
  fftw_execute(sa_pdata);
  /* complex multiply and inverse FFT
   * note that for FFT of real data, FFTW outputs n/2+1 complex numbers.
   * similarly, for complex to real transform, FFTW needs input of n/2+1 complex numbers.
   */
  for(i=0; i<nd_fft_sa_cal; i++)
  {
    sa_conv_fft[i][0] = sa_data_fft[i][0]*sa_resp_fft[i][0] - sa_data_fft[i][1]*sa_resp_fft[i][1];
    sa_conv_fft[i][1] = sa_data_fft[i][0]*sa_resp_fft[i][1] + sa_data_fft[i][1]*sa_resp_fft[i][0];
  }
  fftw_execute(sa_pback);
  memcpy(F_sa, sa_real_conv+npad_sa, n_v_sa*sizeof(double));

  /* then phase */
  for(j=0; j<n_base_sa; j++)
  {
    memcpy(sa_real_data+npad_sa, &p_sa[j*n_v_sa], n_v_sa*sizeof(double));
    for(i=0; i<npad_sa; i++)
      sa_real_data[i] = sa_real_data[nd_fft_sa-1-i] = 0.0;

    /* FFT of line */
    fftw_execute(sa_pdata);
    /* complex multiply and inverse FFT
     * note that for FFT of real data, FFTW outputs n/2+1 complex numbers.
     * similarly, for complex to real transform, FFTW needs input of n/2+1 complex numbers.
     */
    for(i=0; i<nd_fft_sa_cal; i++)
    {
      sa_conv_fft[i][0] = sa_data_fft[i][0]*sa_resp_fft[i][0] - sa_data_fft[i][1]*sa_resp_fft[i][1];
      sa_conv_fft[i][1] = sa_data_fft[i][0]*sa_resp_fft[i][1] + sa_data_fft[i][1]*sa_resp_fft[i][0];
    }
    fftw_execute(sa_pback);
    memcpy(&p_sa[j*n_v_sa], sa_real_conv+npad_sa, n_v_sa*sizeof(double));
  }
  return;
}
#endif


#include <gsl/gsl_math.h>
#include <gsl/gsl_cblas.h>
#include "cblas.h"
#include "error_cblas_l3.h"

void
cblas_ssymm (const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side,
             const enum CBLAS_UPLO Uplo, const int M, const int N,
             const float alpha, const float *A, const int lda, const float *B,
             const int ldb, const float beta, float *C, const int ldc)
{
#define BASE float
#include "source_symm_r.h"
#undef BASE
}


/* -*- linux-c -*- */
/* fewbody_ks.c

   Copyright (C) 2002-2004 John M. Fregeau

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

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

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

/* the modulus of a K-S vector */
double fb_ks_mod(double x[4])
{
	return(sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3]));
}

/* calculate Q given q */
void fb_calc_Q(double q[4], double Q[4])
{
	double u[4];

	u[0] = sqrt(0.5 * (fb_mod(q) + fabs(q[0])));
	u[1] = 0.5 * q[1] / u[0];
	u[2] = 0.5 * q[2] / u[0];
	u[3] = 0.0;

	if (q[0] > 0.0) {
		Q[0] = u[0];
		Q[1] = u[1];
		Q[2] = u[2];
		Q[3] = u[3];
	} else {
		Q[0] = u[1];
		Q[1] = u[0];
		Q[2] = u[3];
		Q[3] = u[2];
	}
}

/* calculate the K-S matrix */
void fb_calc_ksmat(double Q[4], double Qmat[4][4])
{
	Qmat[0][0] = Q[0];
	Qmat[0][1] = -Q[1];
	Qmat[0][2] = -Q[2];
	Qmat[0][3] = Q[3];

	Qmat[1][0] = Q[1];
	Qmat[1][1] = Q[0];
	Qmat[1][2] = -Q[3];
	Qmat[1][3] = -Q[2];

	Qmat[2][0] = Q[2];
	Qmat[2][1] = Q[3];
	Qmat[2][2] = Q[0];
	Qmat[2][3] = Q[1];

	Qmat[3][0] = Q[3];
	Qmat[3][1] = -Q[2];
	Qmat[3][2] = Q[1];
	Qmat[3][3] = -Q[0];
}

/* calculate the a matrix */
void fb_calc_amat(double **a, int nstar, int kstar)
{
	int i, j, k;

	/* first zero out the matrix */
	for (i=0; i<nstar; i++) {
		for (k=0; k<kstar; k++) {
			a[i][k] = 0.0;
		}
	}

	/* then set the non-zero components */
	k = -1;
	for (i=0; i<nstar-1; i++) {
		for (j=i+1; j<nstar; j++) {
			k++;
			a[i][k] = 1.0;
			a[j][k] = -1.0;
		}
	}
}

/* calculate the T matrix */
void fb_calc_Tmat(double **a, double *m, double **T, int nstar, int kstar)
{
	int u, v, e;

	for (u=0; u<kstar; u++) {
		for (v=0; v<kstar; v++) {
			T[u][v] = 0.0;
			for (e=0; e<nstar; e++) {
				T[u][v] += a[e][u] * a[e][v] / m[e];
			}
			T[u][v] *= 0.5;
		}
	}
}

/* the derivatives function for the GSL ODE integrator */
int fb_ks_func(double s, const double *y, double *f, void *params)
{
	int k, l, m, kstar;
	double *M, **amat, **Tmat, Einit;
	double **Q, **P, **p, **A, **TP, **TQ, **UQ, *d;
	double Qmat[4][4], Pmat[4][4], Astar[4], T, U, val, L, H, G, GT, GU;

	/* set parameters */
	kstar = (*(fb_ks_params_t *) params).kstar;
	M = (*(fb_ks_params_t *) params).M;
	amat = (*(fb_ks_params_t *) params).amat;
	Tmat = (*(fb_ks_params_t *) params).Tmat;
	Einit = (*(fb_ks_params_t *) params).Einit;

	/* allocate memory */
	Q = fb_malloc_matrix(kstar, 4);
	P = fb_malloc_matrix(kstar, 4);
	p = fb_malloc_matrix(kstar, 4);
	A = fb_malloc_matrix(kstar, 4);
	TP = fb_malloc_matrix(kstar, 4);
	TQ = fb_malloc_matrix(kstar, 4);
	UQ = fb_malloc_matrix(kstar, 4);
	d = fb_malloc_vector(kstar);

	/* set Q_k, P_k, and p_k */
	for (k=0; k<kstar; k++) {
		/* read Q_k and P_k directly from y[] */
		for (l=0; l<4; l++) {
			Q[k][l] = y[k*8+1+l];
			P[k][l] = y[k*8+4+1+l];
		}

		/* calculate the KS matrix from Q_k */
		fb_calc_ksmat(Q[k], Qmat);

		/* and then calculate p_k */
		for (l=0; l<4; l++) {
			p[k][l] = 0.0;
			for (m=0; m<4; m++) {
				p[k][l] += Qmat[l][m] * P[k][m];
			}
			p[k][l] /= 2.0 * fb_ks_dot(Q[k], Q[k]);
		}
	}

	/* set intermediate variables */
	T = 0.0;
	U = 0.0;
	for (k=0; k<kstar; k++) {
		/* first calculate the K-S matrix for this value of k */
		fb_calc_ksmat(Q[k], Qmat);

		/* calculate A_k */
		for (m=0; m<4; m++) {
			A[k][m] = 0.0;
			for (l=0; l<kstar; l++) {
				A[k][m] += Tmat[k][l] * p[l][m];
			}
		}

		/* then d_k */
		d[k] = fb_ks_dot(A[k], p[k]);

		/* increment the sums for T and U */
		T += d[k];
		U += M[k] / fb_ks_dot(Q[k], Q[k]);

		/* then calculate the first partial derivative of T with respect to P_k */
		for (m=0; m<4; m++) {
			TP[k][m] = 0.0;
			for (l=0; l<4; l++) {
				TP[k][m] += Qmat[l][m] * A[k][l];
			}
			TP[k][m] /= fb_ks_dot(Q[k], Q[k]);
		}

		/* A^*_k */
		Astar[0] = A[k][0];
		Astar[1] = A[k][1];
		Astar[2] = A[k][2];
		Astar[3] = -A[k][3];

		/* calculate the K-S matrix for P_k */
		fb_calc_ksmat(P[k], Pmat);

		/* then calculate the first partial derivatives of T and U */
		for (l=0; l<4; l++) {
			val = 0.0;
			for (m=0; m<4; m++) {
				val += Pmat[m][l] * Astar[m];
			}
			TQ[k][l] = (val - 4.0 * d[k] * Q[k][l]) / fb_ks_dot(Q[k], Q[k]);
			UQ[k][l] = -2.0 * M[k] * Q[k][l] / fb_sqr(fb_ks_dot(Q[k], Q[k]));
		}
	}

	/* set the Lagrangian, Hamiltonian, and the initial energy */
	L = T + U;
	H = T - U;
	G = (H - Einit) / L;
	GT = (1.0 - G) / L;
	GU = -(1.0 + G) / L;

	/* set derivatives */
	f[0] = 1.0 / L;
	for (k=0; k<kstar; k++) {
		for (l=0; l<4; l++) {
			f[k*8+1+l] = GT * TP[k][l];
			f[k*8+4+1+l] = -GT * TQ[k][l] - GU * UQ[k][l];
		}
	}

	/* free memory */
	fb_free_matrix(Q);
	fb_free_matrix(P);
	fb_free_matrix(p);
	fb_free_matrix(A);
	fb_free_matrix(TP);
	fb_free_matrix(TQ);
	fb_free_matrix(UQ);
	fb_free_vector(d);

	/* all done */
	return(GSL_SUCCESS);
}

/* function to calculate the Einit parameter for the integrator */
/* the code here is a verbatim copy from the first part of fb_ks_func */
double fb_ks_Einit(const double *y, fb_ks_params_t params)
{
	int k, l, m;
	double **Q, **P, **p, **A, *d;
	double Qmat[4][4], T, U;

	/* allocate memory */
	Q = fb_malloc_matrix(params.kstar, 4);
	P = fb_malloc_matrix(params.kstar, 4);
	p = fb_malloc_matrix(params.kstar, 4);
	A = fb_malloc_matrix(params.kstar, 4);
	d = fb_malloc_vector(params.kstar);

	/* set Q_k, P_k, and p_k */
	for (k=0; k<params.kstar; k++) {
		/* read Q_k and P_k directly from y[] */
		for (l=0; l<4; l++) {
			Q[k][l] = y[k*8+1+l];
			P[k][l] = y[k*8+4+1+l];
		}

		/* calculate the KS matrix from Q_k */
		fb_calc_ksmat(Q[k], Qmat);

		/* and then calculate p_k */
		for (l=0; l<4; l++) {
			p[k][l] = 0.0;
			for (m=0; m<4; m++) {
				p[k][l] += Qmat[l][m] * P[k][m];
			}
			p[k][l] /= 2.0 * fb_ks_dot(Q[k], Q[k]);
		}
	}

	/* set intermediate variables */
	T = 0.0;
	U = 0.0;
	for (k=0; k<params.kstar; k++) {
		/* first calculate the K-S matrix for this value of k */
		fb_calc_ksmat(Q[k], Qmat);

		/* calculate A_k */
		for (m=0; m<4; m++) {
			A[k][m] = 0.0;
			for (l=0; l<params.kstar; l++) {
				A[k][m] += params.Tmat[k][l] * p[l][m];
			}
		}

		/* then d_k */
		d[k] = fb_ks_dot(A[k], p[k]);

		/* increment the sums for T and U */
		T += d[k];
		U += params.M[k] / fb_ks_dot(Q[k], Q[k]);
	}

	/* free memory */
	fb_free_matrix(Q);
	fb_free_matrix(P);
	fb_free_matrix(p);
	fb_free_matrix(A);
	fb_free_vector(d);

	return(T-U);
}

/* function to convert from Euclidean coordinates to K-S coordinates */
void fb_euclidean_to_ks(fb_obj_t **star, double *y, int nstar, int kstar)
{
	int i, j, k, l, m;
	double **q, **p, Q[4], P[4], Qmat[4][4];

	q = fb_malloc_matrix(kstar, 4);
	p = fb_malloc_matrix(kstar, 4);

	/* then calculate q_k and p_k */
	k = -1;
	for (i=0; i<nstar-1; i++) {
		for (j=i+1; j<nstar; j++) {
			k++;
			for (l=0; l<3; l++) {
				q[k][l] = star[i]->x[l] - star[j]->x[l];
				p[k][l] = (star[i]->m * star[i]->v[l] - star[j]->m * star[j]->v[l])/((double) nstar);
			}
			q[k][3] = 0.0;
			p[k][3] = 0.0;
		}
	}

	/* and then Q_k and P_k */
	for (k=0; k<kstar; k++) {
		/* calculate Q_k */
		fb_calc_Q(q[k], Q);

		/* then P_k */
		fb_calc_ksmat(Q, Qmat);
		for (l=0; l<4; l++) {
			P[l] = 0.0;
			for (m=0; m<4; m++) {
				P[l] += Qmat[m][l] * p[k][m];
			}
			P[l] *= 2.0;

			/* then set y_i */
			y[8*k+l+1] = Q[l];
			y[8*k+l+4+1] = P[l];
		}
	}

	fb_free_matrix(q);
	fb_free_matrix(p);
}

/* function to convert from K-S coordinates to Euclidean coordinates */
void fb_ks_to_euclidean(double *y, fb_obj_t **star, int nstar, int kstar)
{
	int i, j, k, l;
	double *m, mtot, **Q, **P, **q, **p, Qmat[4][4];

	/* allocate memory */
	m = fb_malloc_vector(nstar);
	Q = fb_malloc_matrix(kstar, 4);
	P = fb_malloc_matrix(kstar, 4);
	q = fb_malloc_matrix(kstar, 4);
	p = fb_malloc_matrix(kstar, 4);

	/* set the masses */
	mtot = 0.0;
	for (i=0; i<nstar; i++) {
		m[i] = star[i]->m;
		mtot += m[i];
	}

	/* set Q_k, P_k, and p_k */
	for (k=0; k<kstar; k++) {
		/* read Q_k and P_k directly from y[] */
		for (l=0; l<4; l++) {
			Q[k][l] = y[k*8+1+l];
			P[k][l] = y[k*8+4+1+l];
		}

		/* calculate the KS matrix from Q_k */
		fb_calc_ksmat(Q[k], Qmat);

		/* and then calculate q_k and p_k */
		for (l=0; l<4; l++) {
			q[k][l] = 0.0;
			p[k][l] = 0.0;
			for (j=0; j<4; j++) {
				q[k][l] += Qmat[l][j] * Q[k][j];
				p[k][l] += Qmat[l][j] * P[k][j];
			}
			p[k][l] /= 2.0 * fb_ks_dot(Q[k], Q[k]);
		}
	}

	/* set r_i and v_i */
	for (i=0; i<nstar; i++) {
		for (k=0; k<3; k++) {
			star[i]->x[k] = 0.0;
			star[i]->v[k] = 0.0;
		}

		for (j=i+1; j<nstar; j++) {
			for (k=0; k<3; k++) {
				star[i]->x[k] += m[j] * q[FB_KS_K(i, j, nstar)][k];
				star[i]->v[k] += p[FB_KS_K(i, j, nstar)][k];
			}
		}

		for (j=0; j<i; j++) {
			for (k=0; k<3; k++) {
				star[i]->x[k] += - m[j] * q[FB_KS_K(j, i, nstar)][k];
				star[i]->v[k] += - p[FB_KS_K(j, i, nstar)][k];
			}
		}

		for (k=0; k<3; k++) {
			star[i]->x[k] /= mtot;
			star[i]->v[k] /= m[i];
		}
	}

	/* free memory */
	fb_free_vector(m);
	fb_free_matrix(Q);
	fb_free_matrix(P);
	fb_free_matrix(q);
	fb_free_matrix(p);
}


/* popGenTools.h
/
/
/ Andrew Kern
/
/
*/

#define MAXSNPS 1000000
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>

struct snp{ /*SNP struct definition- meant to hold position, derived freq, sample size */
  int pos, i, n, ascSize;
};

struct my_F_params{
  int i, n, ascSize;
};

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

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

struct my_lik_params{
  int snpNumber, maxSampleSize, ascSize;
  gsl_vector *weights, *sampleSizeVector, *betas;
  struct snp *data;
  gsl_matrix *sfsBools;

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

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

};

double my_f(double q, void * params);
double my_F(double beta, void * params);
double my_F2(double beta, void * p);
double my_lik(double beta, void * params);
double sfsLikBetaVector(gsl_vector *beta, void * p);
double weightedLik(double beta, void * params);
double weightedLikLook(double beta, void * params);
double weightedLikLookCI(double beta, void * params);
double likWrap(double beta);
double snpProb(double beta, void * params);
double snpProbDenomLookup(double beta, void * params);
double snpProbDenom(double beta, int sampleSize);
gsl_vector *makeSnpProbDenomVector(double beta, int maxSampleSize, gsl_vector *sampleSizeVec);
double ml_est(double * lik_beta_hat);
double weighted_ml_est(double * lik_beta_hat, void * p);
double weighted_ml_est_lookup(double * lik_beta_hat, void * p);
double weighted_ml_est_lookup_errReport(double * lik_beta_hat, void * p);
double weighted_ml_CILower_lookup(double * lik_beta_hat, void * p);
double weighted_ml_CIUpper_lookup(double * lik_beta_hat, void * p);
double wlikWrap(double beta, void * p);
double  wlikWrapLook(double beta, void * p);
double  wlikCIWrapLook(double beta, void * p);
gsl_vector *sampleSizeVector(struct snp data[], int snpNumber, int maxSampleSize);
gsl_matrix *summarizeSFS(int maxSampleSize, struct snp data[], int snpNumber);
gsl_matrix *summarizeSFSBool(int maxSampleSize, struct snp data[], int snpNumber);
gsl_matrix *snpProbMatrix(double beta,  int maxSampleSize, gsl_vector *sampleSizeVector, gsl_matrix *sfsBools);
gsl_matrix *snpProbMatrixNotLog(double beta,  int maxSampleSize, gsl_vector *sampleSizeVector, gsl_matrix *sfsBools);
gsl_matrix *snpProbMatrixNotLogFull(double beta,  int maxSampleSize,gsl_vector *sampleSizeVector);
gsl_vector *snpProbVectorNotLog(double beta,  int sampleSize);
int simulateSiteFreq(int sampleSize, double alpha, void *r);
double simpleAscertain(double sampleFreq, void *p);
double probAscertainmentGivenModel(double beta, void *p);
gsl_matrix *snpAscMatrix(int maxSampleSize, gsl_vector *sampleSizeVector, gsl_matrix *sfsBools, int ascSize);
gsl_matrix *estimateAscSFS(int maxSampleSize, gsl_vector *sampleSizeVector,  gsl_matrix *sfsBools, int ascSize, gsl_matrix *sfsSummary);
double weightedLikLookAsc(double beta, void * p);
double  wlikWrapLookAsc(double beta, void * p);
double weighted_ml_est_lookup_asc(double * lik_beta_hat, void * p);
double probAscertainmentGivenModelLookup(double beta, int sampSize, gsl_matrix *snpProbs, gsl_matrix *ascProbs);
double probAscertainmentGivenModelHemiLookup(double beta, int sampSize, gsl_vector *snpProbs, gsl_matrix *ascProbs);
double outgroupAscertain(double sampleFreq, void * p);
gsl_matrix *snpOutgroupAscMatrix(int maxSampleSize, gsl_vector *sampleSizeVector, int ascSize);
double weightedLikLookOutgroupAsc(double beta, void * p);
double  wlikWrapLookOutgroupAsc(double beta, void * p);
double weighted_ml_est_lookup_outgroup_asc(double * lik_beta_hat, void * p);
double probOutgroupAscertainmentGivenModel(double beta, void *p);
double sfsLikBetaVectorOutgroupAsc(gsl_vector *betas, void * p);


/*
 *  iaf_chxk_2008.h
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST 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 NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef IAF_CHXK_2008_H
#define IAF_CHXK_2008_H

// Generated includes:
#include "config.h"

#ifdef HAVE_GSL

// C includes:
#include <gsl/gsl_errno.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv.h>

// Includes from nestkernel:
#include "archiving_node.h"
#include "connection.h"
#include "event.h"
#include "nest_types.h"
#include "recordables_map.h"
#include "ring_buffer.h"
#include "universal_data_logger.h"

/* BeginDocumentation
Name: iaf_chxk_2008 - Conductance based leaky integrate-and-fire neuron model
                      used in Casti et al 2008.

Description:
iaf_chxk_2008 is an implementation of a spiking neuron using IAF dynamics with
conductance-based synapses [1]. It is modeled after iaf_cond_alpha with the
addition of after hyper-polarization current instead of a membrane potential
reset. Incoming spike events induce a post-synaptic change of conductance
modeled by an alpha function. The alpha function is normalized such that an
event of weight 1.0 results in a peak current of 1 nS at t = tau_syn.

Parameters:
The following parameters can be set in the status dictionary.

V_m        double - Membrane potential in mV
E_L        double - Leak reversal potential in mV.
C_m        double - Capacity of the membrane in pF
V_th       double - Spike threshold in mV.
E_ex       double - Excitatory reversal potential in mV.
E_in       double - Inhibitory reversal potential in mV.
g_L        double - Leak conductance in nS.
tau_ex     double - Rise time of the excitatory synaptic alpha function in ms.
tau_in     double - Rise time of the inhibitory synaptic alpha function in ms.
I_e        double - Constant input current in pA.
tau_ahp    double - Afterhyperpolarization (AHP) time constant in ms.
E_ahp      double - AHP potential in mV.
g_ahp      double - AHP conductance in nS.
ahp_bug    bool   - Defaults to false. If true, behaves like original
                    model implementation.

References:
[1] Casti A, Hayot F, Xiao Y, and Kaplan E (2008) A simple model of retina-LGN
transmission. J Comput Neurosci 24:235-252.

Sends: SpikeEvent

Receives: SpikeEvent, CurrentEvent

Author: Heiberg

SeeAlso: iaf_cond_alpha
*/

namespace nest
{
/**
 * Function computing right-hand side of ODE for GSL solver.
 * @note Must be declared here so we can befriend it in class.
 * @note Must have C-linkage for passing to GSL. Internally, it is
 *       a first-class C++ function, but cannot be a member function
 *       because of the C-linkage.
 * @note No point in declaring it inline, since it is called
 *       through a function pointer.
 * @param void* Pointer to model neuron instance.
 */
extern "C" int iaf_chxk_2008_dynamics( double, const double*, double*, void* );

/**
 * Integrate-and-fire neuron model with two conductance-based synapses.
 */
class iaf_chxk_2008 : public Archiving_Node
{

  // Boilerplate function declarations --------------------------------

public:
  iaf_chxk_2008();
  iaf_chxk_2008( const iaf_chxk_2008& );
  ~iaf_chxk_2008();

  /**
   * Import sets of overloaded virtual functions.
   * @see Technical Issues / Virtual Functions: Overriding, Overloading, and
   * Hiding
   */
  using Node::handle;
  using Node::handles_test_event;

  port send_test_event( Node&, rport, synindex, bool );

  bool
  is_off_grid() const
  {
    return true;
  } // uses off_grid events

  port handles_test_event( SpikeEvent&, rport );
  port handles_test_event( CurrentEvent&, rport );
  port handles_test_event( DataLoggingRequest&, rport );

  void handle( SpikeEvent& );
  void handle( CurrentEvent& );
  void handle( DataLoggingRequest& );

  void get_status( DictionaryDatum& ) const;
  void set_status( const DictionaryDatum& );

private:
  void init_state_( const Node& proto );
  void init_buffers_();
  void calibrate();
  void update( Time const&, const long, const long );

  // END Boilerplate function declarations ----------------------------

  // Friends --------------------------------------------------------

  // make dynamics function quasi-member
  friend int iaf_chxk_2008_dynamics( double, const double*, double*, void* );

  // The next two classes need to be friends to access the State_ class/member
  friend class RecordablesMap< iaf_chxk_2008 >;
  friend class UniversalDataLogger< iaf_chxk_2008 >;

private:
  // Parameters class -------------------------------------------------

  //! Model parameters
  struct Parameters_
  {
    double V_th; //!< Threshold Potential in mV
    double g_L;  //!< Leak Conductance in nS
    double C_m;  //!< Membrane Capacitance in pF
    double E_ex; //!< Excitatory reversal Potential in mV
    double E_in; //!< Inhibitory reversal Potential in mV
    double E_L;  //!< Leak reversal Potential (a.k.a. resting potential) in mV
    double tau_synE; //!< Synaptic Time Constant Excitatory Synapse in ms
    double tau_synI; //!< Synaptic Time Constant for Inhibitory Synapse in ms
    double I_e;      //!< Constant Current in pA
    double tau_ahp;  //!< Afterhyperpolarization (AHP) time constant
    double g_ahp;    //!< AHP conductance
    double E_ahp;    //!< AHP potential
    bool ahp_bug;    //!< If true, discard AHP conductance value from previous
                     //!< spikes
    Parameters_();   //!< Set default parameter values

    void get( DictionaryDatum& ) const; //!< Store current values in dictionary
    void set( const DictionaryDatum& ); //!< Set values from dictionary
  };

  // State variables class --------------------------------------------

  /**
   * State variables of the model.
   *
   * State variables consist of the state vector for the subthreshold
   * dynamics and the refractory count. The state vector must be a
   * C-style array to be compatible with GSL ODE solvers.
   *
   * @note Copy constructor and assignment operator are required because
   *       of the C-style array.
   */
public:
  struct State_
  {

    //! Symbolic indices to the elements of the state vector y
    enum StateVecElems
    {
      V_M = 0,
      DG_EXC,
      G_EXC,
      DG_INH,
      G_INH,
      DG_AHP,
      G_AHP,
      STATE_VEC_SIZE
    };

    //! state vector, must be C-array for GSL solver
    double y[ STATE_VEC_SIZE ];

    //!< number of refractory steps remaining
    int r;

    State_( const Parameters_& ); //!< Default initialization
    State_( const State_& );
    State_& operator=( const State_& );

    void get( DictionaryDatum& ) const; //!< Store current values in dictionary

    /**
     * Set state from values in dictionary.
     * Requires Parameters_ as argument to, e.g., check bounds.'
     */
    void set( const DictionaryDatum&, const Parameters_& );
  };

private:
  // Buffers class --------------------------------------------------------

  /**
   * Buffers of the model.
   * Buffers are on par with state variables in terms of persistence,
   * i.e., initialized only upon first Simulate call after ResetKernel
   * or ResetNetwork, but are implementation details hidden from the user.
   */
  struct Buffers_
  {
    Buffers_( iaf_chxk_2008& );                  //!<Sets buffer pointers to 0
    Buffers_( const Buffers_&, iaf_chxk_2008& ); //!<Sets buffer pointers to 0

    //! Logger for all analog data
    UniversalDataLogger< iaf_chxk_2008 > logger_;

    /** buffers and sums up incoming spikes/currents */
    RingBuffer spike_exc_;
    RingBuffer spike_inh_;
    RingBuffer currents_;

    /* GSL ODE stuff */
    gsl_odeiv_step* s_;    //!< stepping function
    gsl_odeiv_control* c_; //!< adaptive step size control function
    gsl_odeiv_evolve* e_;  //!< evolution function
    gsl_odeiv_system sys_; //!< struct describing system

    // IntergrationStep_ should be reset with the neuron on ResetNetwork,
    // but remain unchanged during calibration. Since it is initialized with
    // step_, and the resolution cannot change after nodes have been created,
    // it is safe to place both here.
    double step_;            //!< step size in ms
    double IntegrationStep_; //!< current integration time step, updated by GSL

    /**
     * Input current injected by CurrentEvent.
     * This variable is used to transport the current applied into the
     * _dynamics function computing the derivative of the state vector.
     * It must be a part of Buffers_, since it is initialized once before
     * the first simulation, but not modified before later Simulate calls.
     */
    double I_stim_;
  };

  // Variables class -------------------------------------------------------

  /**
   * Internal variables of the model.
   * Variables are re-initialized upon each call to Simulate.
   */
  struct Variables_
  {
    /**
     * Impulse to add to DG_EXC on spike arrival to evoke unit-amplitude
     * conductance excursion.
     */
    double PSConInit_E;

    /**
     * Impulse to add to DG_INH on spike arrival to evoke unit-amplitude
     * conductance excursion.
     */
    double PSConInit_I;

    /**
     * Impulse to add to DG_AHP on spike generation to evoke unit-amplitude
     * conductance excursion.
     */
    double PSConInit_AHP;
  };

  // Access functions for UniversalDataLogger -------------------------------

  //! Read out state vector elements, used by UniversalDataLogger
  template < State_::StateVecElems elem >
  double
  get_y_elem_() const
  {
    return S_.y[ elem ];
  }

  //! Read out remaining refractory time, used by UniversalDataLogger
  double
  get_r_() const
  {
    return Time::get_resolution().get_ms() * S_.r;
  }

  double
  get_I_syn_exc_() const
  {
    return S_.y[ State_::G_EXC ] * ( S_.y[ State_::V_M ] - P_.E_ex );
  }
  double
  get_I_syn_inh_() const
  {
    return S_.y[ State_::G_INH ] * ( S_.y[ State_::V_M ] - P_.E_in );
  }
  double
  get_I_ahp_() const
  {
    return S_.y[ State_::G_AHP ] * ( S_.y[ State_::V_M ] - P_.E_ahp );
  }

  // Data members -----------------------------------------------------------

  // keep the order of these lines, seems to give best performance
  Parameters_ P_;
  State_ S_;
  Variables_ V_;
  Buffers_ B_;

  //! Mapping of recordables names to access functions
  static RecordablesMap< iaf_chxk_2008 > recordablesMap_;
};

// Boilerplate inline function definitions ----------------------------------

inline port
iaf_chxk_2008::send_test_event( Node& target,
  rport receptor_type,
  synindex,
  bool )
{
  SpikeEvent e;
  e.set_sender( *this );

  return target.handles_test_event( e, receptor_type );
}

inline port
iaf_chxk_2008::handles_test_event( SpikeEvent&, rport receptor_type )
{
  if ( receptor_type != 0 )
  {
    throw UnknownReceptorType( receptor_type, get_name() );
  }
  return 0;
}

inline port
iaf_chxk_2008::handles_test_event( CurrentEvent&, rport receptor_type )
{
  if ( receptor_type != 0 )
  {
    throw UnknownReceptorType( receptor_type, get_name() );
  }
  return 0;
}

inline port
iaf_chxk_2008::handles_test_event( DataLoggingRequest& dlr,
  rport receptor_type )
{
  if ( receptor_type != 0 )
  {
    throw UnknownReceptorType( receptor_type, get_name() );
  }
  return B_.logger_.connect_logging_device( dlr, recordablesMap_ );
}

inline void
iaf_chxk_2008::get_status( DictionaryDatum& d ) const
{
  P_.get( d );
  S_.get( d );
  Archiving_Node::get_status( d );

  ( *d )[ names::recordables ] = recordablesMap_.get_list();
}

inline void
iaf_chxk_2008::set_status( const DictionaryDatum& d )
{
  Parameters_ ptmp = P_; // temporary copy in case of errors
  ptmp.set( d );         // throws if BadProperty
  State_ stmp = S_;      // temporary copy in case of errors
  stmp.set( d, ptmp );   // throws if BadProperty

  // We now know that (ptmp, stmp) are consistent. We do not
  // write them back to (P_, S_) before we are also sure that
  // the properties to be set in the parent class are internally
  // consistent.
  Archiving_Node::set_status( d );

  // if we get here, temporaries contain consistent set of properties
  P_ = ptmp;
  S_ = stmp;
}

} // namespace

#endif // HAVE_GSL
#endif // IAF_CHXK_2008_H


/*
 * BRAINS
 * (B)LR (R)everberation-mapping (A)nalysis (I)n AGNs with (N)ested (S)ampling
 * Yan-Rong Li, liyanrong@ihep.ac.cn
 * Thu, Aug 4, 2016
 */

/*! \file dnest_sa1d.c
 *  \brief run dnest sampling for sa and 1d rm analysis.
 */

#ifdef SA

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_interp.h>
#include <mpi.h>

#include "brains.h"

DNestFptrSet *fptrset_sa1d;

/*
 * run denst for SA and 1D RM analysis.
 */
int dnest_sa1d(int argc, char **argv)
{
  int i;
  double logz_sa1d;

  if(parset.flag_sa_par_mutual == 0) /* SA and RM have the same BLR */
  {
    set_blr_model2d();
    num_params_sa_blr_model = 0;
  }
  else /* SA and RM have different BLRs but share the same inc. */
  {
    set_blr_model1d();
    set_sa_blr_model();
  }
  /* RM */
  num_params_blr = num_params_blr_model + 1; /* include line sys err */
  num_params_rm = parset.n_con_recon + num_params_var + num_params_blr;

  /* SA */
  num_params_sa_blr = num_params_sa_blr_model + num_params_sa_extpar;
  num_params_sa = num_params_sa_blr;

  /* total */
  num_params_blr_tot = num_params_blr + num_params_sa_blr;
  num_params = num_params_sa + num_params_rm;
  idx_resp = num_params_blr_tot + num_params_drw + num_params_trend;
  idx_difftrend = idx_resp + num_params_resp;

  par_fix = (int *) malloc(num_params * sizeof(int));
  par_fix_val = (double *) malloc(num_params * sizeof(double));
  par_range_model = malloc( num_params * sizeof(double *));
  par_prior_gaussian = malloc( num_params * sizeof(double *));
  for(i=0; i<num_params; i++)
  {
    par_range_model[i] = malloc(2*sizeof(double));
    par_prior_gaussian[i] = malloc(2*sizeof(double));
  }
  par_prior_model = malloc( num_params * sizeof(int));

  fptrset_sa1d = dnest_malloc_fptrset();
  /* setup functions used for dnest*/
  fptrset_sa1d->from_prior = from_prior_sa1d;
  fptrset_sa1d->print_particle = print_particle_sa1d;
  fptrset_sa1d->restart_action = restart_action_sa;
  fptrset_sa1d->accept_action = accept_action_sa1d;
  fptrset_sa1d->kill_action = kill_action_sa1d;
  fptrset_sa1d->perturb = perturb_sa1d;
  fptrset_sa1d->read_particle = read_particle_sa1d;

  fptrset_sa1d->log_likelihoods_cal_initial = log_likelihoods_cal_initial_sa1d;
  fptrset_sa1d->log_likelihoods_cal_restart = log_likelihoods_cal_restart_sa1d;
  fptrset_sa1d->log_likelihoods_cal = log_likelihoods_cal_sa1d;

  set_par_range_sa1d();
  set_par_fix_blrmodel();
  set_par_fix_sa_blrmodel();

  /* the rest parameters */
  for(i=num_params_blr_model; i<num_params_blr; i++)
  {
    par_fix[i] = 0;
    par_fix_val[i] = -DBL_MAX;
  }
  for(i=num_params_blr + num_params_sa_blr_model; i<num_params; i++)
  {
    par_fix[i] = 0;
    par_fix_val[i] = -DBL_MAX;
  }

  /* fix non-linear response */
  if(parset.flag_nonlinear !=1)
  {
    par_fix[idx_resp+1] = 1;
    par_fix_val[idx_resp+1] = 0.0;
  }

  /* fix systematic error of line */
  if(parset.flag_line_sys_err != 1)
  {
    par_fix[num_params_blr-1] = 1;
    par_fix_val[num_params_blr-1] = log(1.0);
  }

  /* fix inc of SA BLR if SA and RM have different BLR
   * in this case, DA must be fixed.
   */
  if(parset.flag_sa_par_mutual != 0)
  {
    /* get the index for mbh and inclination parameters */
    set_idx_par_mutual();

    /* inc */
    par_fix[num_params_blr + idx_sa_par_mutual[1]] = 1;
    par_fix_val[num_params_blr + idx_sa_par_mutual[1]] = 0.0;

    /* fix DA */
    par_fix[num_params_blr + num_params_sa_blr_model] = 1;
    par_fix_val[num_params_blr + num_params_sa_blr_model] = log(550.0);
  }

  /* fix FA */
  par_fix[num_params_blr + num_params_sa_blr_model+2] = 1;
  par_fix_val[num_params_blr + num_params_sa_blr_model+2] = log(1.0);

  /* fix systematic error of continuum */
  if(parset.flag_con_sys_err != 1)
  {
    par_fix[num_params_blr_tot] = 1;
    par_fix_val[num_params_blr_tot] = log(1.0);
  }

  /* fix continuum variation parameter sigma and tau if flag_fixvar is true */
  if(parset.flag_fixvar == 1)
  {
    par_fix[num_params_blr_tot + 1] = 1;
    par_fix_val[num_params_blr_tot + 1] = var_param[1];
    par_fix[num_params_blr_tot + 2] = 1;
    par_fix_val[num_params_blr_tot + 2] = var_param[2];
  }

  print_par_names_sa1d();

  force_update = parset.flag_force_update;
  if(parset.flag_para_name != 1)
    logz_sa1d = dnest(argc, argv, fptrset_sa1d, num_params, dnest_options_file);

  dnest_free_fptrset(fptrset_sa1d);

  return 0;
}

void set_par_range_sa1d()
{
  int i, j, i1, i2;

  /* RM BLR parameters first */
  for(i=0; i<num_params_blr_model; i++)
  {
    par_range_model[i][0] = blr_range_model[i][0];
    par_range_model[i][1] = blr_range_model[i][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;
  }

  /* systematic line error */
  i = num_params_blr -1;
  par_range_model[i][0] = sys_err_line_range[0];
  par_range_model[i][1] = sys_err_line_range[1];

  par_prior_model[i] = UNIFORM;
  par_prior_gaussian[i][0] = 0.0;
  par_prior_gaussian[i][1] = 0.0;

  /* SA BLR parameters */
  for(i=num_params_blr; i<num_params_blr + num_params_sa_blr_model; i++)
  {
    par_range_model[i][0] = sa_blr_range_model[i-num_params_blr][0];
    par_range_model[i][1] = sa_blr_range_model[i-num_params_blr][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;
  }
  /* SA extra parameters */
  for(i=num_params_blr + num_params_sa_blr_model; i<num_params_blr_tot; i++)
  {
    par_range_model[i][0] = sa_extpar_range[i-(num_params_blr + num_params_sa_blr_model)][0];
    par_range_model[i][1] = sa_extpar_range[i-(num_params_blr + num_params_sa_blr_model)][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;
  }

  /* variability parameters */
  /* first systematic error */
  i = num_params_blr_tot;
  par_range_model[i][0] = var_range_model[0][0];
  par_range_model[i][1] = var_range_model[0][1];

  par_prior_model[i] = UNIFORM;
  par_prior_gaussian[i][0] = 0.0;
  par_prior_gaussian[i][1] = 0.0;

  /* drw parameters */
  for(i=num_params_blr_tot+1; i<num_params_drw + num_params_blr_tot; i++)
  {
    if(var_param_std[i-num_params_blr_tot] > 0.0)
    {
      par_range_model[i][0] = var_param[i-num_params_blr_tot] - 5.0 * var_param_std[i-num_params_blr_tot];
      par_range_model[i][1] = var_param[i-num_params_blr_tot] + 5.0 * var_param_std[i-num_params_blr_tot];

      /* make sure that the range lies within the initial range */
      par_range_model[i][0] = fmax(par_range_model[i][0], var_range_model[i-num_params_blr_tot][0]);
      par_range_model[i][1] = fmin(par_range_model[i][1], var_range_model[i-num_params_blr_tot][1]);

      par_prior_model[i] = GAUSSIAN;
      par_prior_gaussian[i][0] = var_param[i-num_params_blr_tot];
      par_prior_gaussian[i][1] = var_param_std[i-num_params_blr_tot];
    }
    else
    {
      par_range_model[i][0] = var_range_model[i-num_params_blr_tot][0];
      par_range_model[i][1] = var_range_model[i-num_params_blr_tot][1];

      par_prior_model[i] = UNIFORM;
      par_prior_gaussian[i][0] = 0.0;
      par_prior_gaussian[i][1] = 0.0;
    }
  }
  /* long-term trend */
  for(i=num_params_drw + num_params_blr_tot; i< num_params_drw + num_params_trend + num_params_blr_tot; i++)
  {
    par_range_model[i][0] = var_range_model[3][0];
    par_range_model[i][1] = var_range_model[3][1];

    par_prior_model[i] = GAUSSIAN;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 1.0;
  }

  /* response A and Ag */
  j = 0;
  i1 = idx_resp;
  i2 = idx_resp + num_params_resp;
  for(i=i1; i<i2; i++)
  {
    par_range_model[i][0] = resp_range[j][0];
    par_range_model[i][1] = resp_range[j][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;

    j++;
  }

  /* different trend */
  j = 0;
  i1 = idx_difftrend;
  i2 = idx_difftrend + num_params_difftrend;
  for(i=i1; i< i2; i++)
  {
    par_range_model[i][0] = var_range_model[4 + j][0];
    par_range_model[i][1] = var_range_model[4 + j][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;

    j++;
  }

  /* continuum light curve parameters */
  for(i=num_params_blr_tot+num_params_var; i<num_params; i++)
  {
    par_range_model[i][0] = var_range_model[4+num_params_difftrend][0];
    par_range_model[i][1] = var_range_model[4+num_params_difftrend][1];

    par_prior_model[i] = GAUSSIAN;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 1.0;
  }
  return;
}

/*!
 *  print names and prior ranges for parameters
 *
 */
void print_par_names_sa1d()
{
  if(thistask!= roottask)
    return;

  int i, j;
  FILE *fp;
  char fname[BRAINS_MAX_STR_LENGTH], str_fmt[BRAINS_MAX_STR_LENGTH];

  sprintf(fname, "%s/%s", parset.file_dir, "data/para_names_sa1d.txt");
  fp = fopen(fname, "w");
  if(fp == NULL)
  {
    fprintf(stderr, "# Error: Cannot open file %s.\n", fname);
    exit(0);
  }

  strcpy(str_fmt, "%4d %-15s %10.6f %10.6f %4d %4d %15.6e\n");

  printf("# Print parameter name in %s\n", fname);

  fprintf(fp, "#*************************************************\n");
  fprint_version(fp);
  fprintf(fp, "#*************************************************\n");

  fprintf(fp, "%4s %-15s %10s %10s %4s %4s %15s\n", "#", "Par", "Min", "Max", "Prior", "Fix", "Val");

  i=-1;
  for(j=0; j<num_params_blr_model; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "BLR model", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  i++;
  fprintf(fp, str_fmt, i, "sys_err_line", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  for(j=0; j<num_params_sa_blr_model; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "SA BLR model", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  for(j=0; j<num_params_sa_extpar; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "SA Extra Par", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  i++;
  fprintf(fp, str_fmt, i, "sys_err_con", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  i++;
  fprintf(fp, str_fmt, i, "sigmad", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  i++;
  fprintf(fp, str_fmt, i, "taud", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  for(j=0; j<num_params_trend; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "trend", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  i++;
  fprintf(fp, str_fmt, i, "A", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  i++;
  fprintf(fp, str_fmt, i, "Ag", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  for(j=0; j<num_params_difftrend; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "diff trend", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  for(j=0; j<parset.n_con_recon; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "time series", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  fclose(fp);
}

/*!
 * this function generates a sample from prior.
 */
void from_prior_sa1d(void *model)
{
  int i;
  double *pm = (double *)model;

  for(i=0; i<num_params; i++)
  {
    if(par_prior_model[i] == GAUSSIAN)
    {
      pm[i] = dnest_randn()*par_prior_gaussian[i][1] + par_prior_gaussian[i][0];
      dnest_wrap(&pm[i], par_range_model[i][0], par_range_model[i][1]);
    }
    else
    {
      pm[i] = par_range_model[i][0] + dnest_rand() * (par_range_model[i][1] - par_range_model[i][0]);
    }
  }

  /* cope with fixed parameters */
  for(i=0; i<num_params; i++)
  {
    if(par_fix[i] == 1)
      pm[i] = par_fix_val[i];
  }

  which_parameter_update = -1;

  return;
}

/*!
 * this function calculates likelihood at initial step.
 */
double log_likelihoods_cal_initial_sa1d(const void *model)
{
  double logL;
  logL = prob_initial_sa1d(model);
  return logL;
}

/*!
 * this function calculates likelihood at initial step.
 */
double log_likelihoods_cal_restart_sa1d(const void *model)
{
  double logL;
  logL = prob_initial_sa1d(model);
  return logL;
}

/*!
 * this function calculates likelihood.
 */
double log_likelihoods_cal_sa1d(const void *model)
{
  double logL;
  logL = prob_sa1d(model);
  return logL;
}

/*!
 * this function prints out parameters.
 */
void print_particle_sa1d(FILE *fp, const void *model)
{
  int i;
  double *pm = (double *)model;

  for(i=0; i<num_params; i++)
  {
    fprintf(fp, "%e ", pm[i] );
  }
  fprintf(fp, "\n");
  return;
}

/*!
 * This function read the particle from the file.
 */
void read_particle_sa1d(FILE *fp, void *model)
{
  int j;
  double *psample = (double *)model;

  for(j=0; j < dnest_num_params; j++)
  {
    if(fscanf(fp, "%lf", psample+j) < 1)
    {
      printf("%f\n", *psample);
      fprintf(stderr, "#Error: Cannot read file %s.\n", options.sample_file);
      exit(0);
    }
  }
  return;
}

/*!
 * this function perturbs parameters.
 */
double perturb_sa1d(void *model)
{
  double *pm = (double *)model;
  double logH = 0.0, limit1, limit2, width, rnd;
  int which, which_level;

  /*
   * fixed parameters need not to update
   * perturb important parameters more frequently
   */
  do
  {
    rnd = dnest_rand();
    if(rnd < fmax(0.2, 1.0*(num_params_blr_tot+num_params_var)/num_params))
      which = dnest_rand_int(num_params_blr_tot + num_params_var);
    else
      which = dnest_rand_int(parset.n_con_recon) + num_params_blr_tot + num_params_var;

  }while(par_fix[which] == 1);

  which_parameter_update = which;

  /* level-dependent width */
  count_saves = dnest_get_count_saves();
  which_level_update = dnest_get_which_level_update();
  which_level = which_level_update > (size_levels-10)?(size_levels-10):which_level_update;

  if( which_level > 0)
  {
    limit1 = limits[(which_level-1) * num_params *2 + which *2];
    limit2 = limits[(which_level-1) * num_params *2 + which *2 + 1];
    width = (limit2 - limit1);
  }
  else
  {
    limit1 = par_range_model[which][0];
    limit2 = par_range_model[which][1];
    width = (par_range_model[which][1] - par_range_model[which][0]);
  }
  width /= (2.35);

  if(par_prior_model[which] == GAUSSIAN)
  {
    logH -= (-0.5*pow((pm[which] - par_prior_gaussian[which][0])/par_prior_gaussian[which][1], 2.0) );
    pm[which] += dnest_randh() * width;
    dnest_wrap(&pm[which], par_range_model[which][0], par_range_model[which][1]);
    //dnest_wrap(&pm[which], limit1, limit2);
    logH += (-0.5*pow((pm[which] - par_prior_gaussian[which][0])/par_prior_gaussian[which][1], 2.0) );
  }
  else
  {
    pm[which] += dnest_randh() * width;
    dnest_wrap(&(pm[which]), par_range_model[which][0], par_range_model[which][1]);
    //dnest_wrap(&pm[which], limit1, limit2);
  }

  return logH;
}

/*!
 * this function calculates likelihood.
 */
double log_likelihoods_cal_sa1d_exam(const void *model)
{
  return 0.0;
}

void accept_action_sa1d()
{
  int param;
  double *ptemp;

  // the parameter previously updated
  param = which_parameter_update;

  /* continuum parameter is updated */
  if( param >= num_params_blr_tot )
  {
    /*
     *note that (response) Fline is also changed as long as Fcon is changed.
     *num_params_blr-the parameter is the systematic error of continuum.
     *the change of this parameter also changes continuum reconstruction.
     */

    ptemp = Fcon_rm_particles[which_particle_update];
    Fcon_rm_particles[which_particle_update] = Fcon_rm_particles_perturb[which_particle_update];
    Fcon_rm_particles_perturb[which_particle_update] = ptemp;

    if(force_update != 1)
    {
      ptemp = Fline_at_data_particles[which_particle_update];
      Fline_at_data_particles[which_particle_update] = Fline_at_data_particles_perturb[which_particle_update];
      Fline_at_data_particles_perturb[which_particle_update] = ptemp;
    }
  }
  else if( param != num_params_blr-1 && force_update != 1)
  {
    /* RM and SA BLR parameter is updated
     * Note a) that the (num_par_blr-1)-th parameter is systematic error of line.
     * when this parameter is updated, Trans1D and Fline, phase_sa, and Fline_sa are unchanged.
     *      b) Fline is always changed, except param = num_params_blr-1.
     */

    {
      ptemp = TransTau_particles[which_particle_update];
      TransTau_particles[which_particle_update] = TransTau_particles_perturb[which_particle_update];
      TransTau_particles_perturb[which_particle_update] = ptemp;

      ptemp = Trans1D_particles[which_particle_update];
      Trans1D_particles[which_particle_update] = Trans1D_particles_perturb[which_particle_update];
      Trans1D_particles_perturb[which_particle_update] = ptemp;

      ptemp = Fline_at_data_particles[which_particle_update];
      Fline_at_data_particles[which_particle_update] = Fline_at_data_particles_perturb[which_particle_update];
      Fline_at_data_particles_perturb[which_particle_update] = ptemp;

      prob_sa_particles[which_particle_update] = prob_sa_particles_perturb[which_particle_update];
    }
  }

  return;
}

/*
 * action when particle i is killed in cdnest sampling.
 * particle i_copy's properties is copyed to particle i.
 */
void kill_action_sa1d(int i, int i_copy)
{
  memcpy(Fcon_rm_particles[i], Fcon_rm_particles[i_copy], parset.n_con_recon * sizeof(double));
  memcpy(Fline_at_data_particles[i], Fline_at_data_particles[i_copy], n_line_data * sizeof(double));
  memcpy(TransTau_particles[i], TransTau_particles[i_copy], parset.n_tau*sizeof(double));
  memcpy(Trans1D_particles[i], Trans1D_particles[i_copy], parset.n_tau * sizeof(double));

  prob_sa_particles[i] = prob_sa_particles[i_copy];

  return;
}

#endif


/*
    Code for the article 'Landscape heterogeneity buffers biodiversity of meta-food-webs under global change through rescue and drainage effects'

    Copyright (C) 2016 Christian Guill & Florian D. Schneider
    Copyright (C) 2020 Remo Ryser

    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/>.

    last edit: 01.03.2020 by RR

 */

/*
// ************** Local foodweb code extended into space ***************************

Remo Ryser, Johanna Häussler, Markus Stark

*/

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <fstream>      // for input/output from or to files
//#include <stdlib.h>
#include <iostream>		// for input/output on terminal
#include <sstream>
#include <vector>
#include <cstdlib>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_rng.h>				// random number generator
#include <gsl/gsl_randist.h>			// random number distributions
#include <gsl/gsl_blas.h>				// linear algebra routines
#include <gsl/gsl_linalg.h>				// linear algebra
#include <gsl/gsl_sort_vector.h>		// vector operations
#include <gsl/gsl_odeiv.h>              // ODE solver

#include <cvode/cvode.h>             /* prototypes for CVODE fcts., consts. */
#include <nvector/nvector_serial.h>  /* serial N_Vector types, fcts., macros */
#include <cvode/cvode_dense.h>       /* prototype for CVDense */

double *web_calc(gsl_rng *r);
static void dynamics(double B[], double Bdot[], void *params, double t);
static void pdef_structure(gsl_rng *r,gsl_matrix *Ap, gsl_vector *mass, gsl_matrix *Up, gsl_vector *DmaxPlant);
static void output(gsl_matrix *SW, gsl_vector *mass, gsl_vector *con, double iniB[], double meanB[], double meanB_tot[], double CV[], double CV_tot[], double params[], int paramslength, double Biomass_Temp[], double RGG_params[], double net_growth_Temp[]);
static void getInputSpp();
static void getInputRicker();
static void getInputMass(gsl_vector *mass, gsl_matrix *Up,gsl_vector *DmaxPlant);
static void getInputLandscape();
static void getInputCoords(gsl_vector *coords_in_v);
static void getInputSW(gsl_matrix *SW_input);
static void getInputNutrient(gsl_vector *NS_input);

double get_random_parameter(double mean, double sigma, double low_cutoff, double high_cutoff ,gsl_rng *r);
static void mean_body_mass(double meanB[], gsl_vector *mass, double mean_body_masses[]);

static void set_parameters(gsl_rng *r, gsl_matrix *Ap, gsl_vector *mass, double params[], gsl_matrix *Up);
static void set_spatial_parameters(gsl_rng *r, gsl_matrix *SW, gsl_vector *Loc, gsl_vector *NS, double params[]);
static void initialise_biomass_densities(double B[], double iniB[], double params[], gsl_rng *r);
static void solve_ode(double B[], double meanB[],  double meanB_tot[], double CV[],  double CV_tot[], double params[], gsl_matrix *CovM, double Biomass_Temp[], double net_growth_Temp[]);
static void Extinction(double B[], int Num);
static void Extinct_Species(double B[], int Num, int Pat);
static int Cvode_rates(realtype t, N_Vector y, N_Vector ydot, void *params);

static void net_rates(gsl_vector *TBvec, gsl_vector *Ivec, gsl_vector *Dvec, gsl_vector *Xvec, gsl_vector *Emvec, gsl_vector *N_in, gsl_vector *N_out, gsl_vector *N_up, void *params, double t);

static void Prepare_timeseries_file(FILE *timeseries);
static void Write_timeseries_to_file(double B[], double t, FILE *timeseries);
static void Prepare_netrates_file(FILE *netrates);
static void Write_netrates_to_file(int p, gsl_vector *netvec, gsl_vector *Emvec_p, FILE *netrates);
static void Prepare_disprates_file(FILE *disprates);
static void Write_disprates_to_file(int i, int j, gsl_vector *Imvec_i, FILE *disprates);
static void Write_params_to_file(double params[], int paramslength);
static void Write_landscape_to_file(double params[]);
static void Write_SW_matrix_to_file(gsl_matrix *SW);

static void Generate_RGG_structure(gsl_rng *ran, gsl_vector *mass, gsl_vector *Loc, gsl_matrix *SW, gsl_matrix *SW_input, gsl_vector *con, double params[], double RGG_params[], gsl_vector *DmaxPlant);

static void calc_patch_distances(gsl_matrix *P_Dist, gsl_vector *Loc, double RGG_params[]);
static void calc_spatial_connectance(gsl_matrix *SW, gsl_vector *con, double RGG_params[]);

static void show_matrix(gsl_matrix *A, int Num, int N2);
static void show_vector(gsl_vector *A, int Num);
double calc_sd(gsl_vector *con, int START, int P, int div);
double calc_mean(gsl_vector *con, int START, int P, int div);


/**
 * \author Sylvain Marsat, University of Maryland - NASA GSFC
 *
 * \brief C header for EOBNRv2HM reduced order model (non-spinning version).
 * See CQG 31 195010, 2014, arXiv:1402.4146 for details on the reduced order method.
 * See arXiv:1106.1021 for the EOBNRv2HM model.
 *
 * Borrows from the SEOBNR ROM LAL code written by Michael Puerrer and John Veitch.
 *
 * Put the untared data in the directory designated by the environment variable ROM_DATA_PATH.
 *
 * Parameter range:
 *   q = 1-12 (almost)
 *   No spin
 *   Mtot >= 10Msun for fstart=8Hz
 *
 */

#ifndef _EOBNRV2HMROM_H
#define _EOBNRV2HMROM_H

#define _XOPEN_SOURCE 500

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

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

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

#include "constants.h"
#include "struct.h"
#include "EOBNRv2HMROMstruct.h"

#if defined(__cplusplus)
extern "C" {
#elif 0
} /* so that editors will match preceding brace */
#endif

   /********General definitions********/

#define nk_amp 10  /* number of SVD-modes == number of basis functions for amplitude */
#define nk_phi 20  /* number of SVD-modes == number of basis functions for phase */

/********External array for the list of modes********/
#define nbmodemax 5
extern const int listmode[nbmodemax][2];

/**************************************************/
/**************** Prototypes **********************/

/* Functions to load, initalize and cleanup data */
int EOBNRv2HMROM_Init_DATA(void);
int EOBNRv2HMROM_Init(const char dir[]);

void EOBNRHMROMdata_Init(EOBNRHMROMdata **data);
void EOBNRHMROMdata_interp_Init(EOBNRHMROMdata_interp **data_interp);
void EOBNRHMROMdata_coeff_Init(EOBNRHMROMdata_coeff **data_coeff);

void EOBNRHMROMdata_Cleanup(EOBNRHMROMdata *data);
void EOBNRHMROMdata_interp_Cleanup(EOBNRHMROMdata_interp *data_interp);
void EOBNRHMROMdata_coeff_Cleanup(EOBNRHMROMdata_coeff *data_coeff);

/* Function to read data */
int Read_Data_Mode(const char dir[], const int mode[2], EOBNRHMROMdata *data);

/* Functions to interpolate the data and to evaluate the interpolated data for a given q */

int Evaluate_Spline_Data(
  const double q,                           /* Input: q-value for which projection coefficients should be evaluated */
  const EOBNRHMROMdata_interp* data_interp,  /* Input: data in interpolated form */
  EOBNRHMROMdata_coeff* data_coeff           /* Output: vectors of projection coefficients and shifts in time and phase */
);

int Interpolate_Spline_Data(
  const EOBNRHMROMdata *data,           /* Input: data in vector/matrix form to interpolate */
  EOBNRHMROMdata_interp *data_interp    /* Output: interpolated data */
);

/* Functions for waveform reconstruction */

int EOBNRv2HMROMCore(
  struct tagListmodesCAmpPhaseFrequencySeries **listhlm,
  int nbmode,
  double tRef,
  double phiRef,
  double fRef,
  double Mtot_sec,
  double q,
  double distance,
  int setphiRefattRef);

int SimEOBNRv2HMROM(
  ListmodesCAmpPhaseFrequencySeries **listhlm,  /* Output: list of modes in Frequency-domain amplitude and phase form */
  int nbmode,                                    /* Number of modes to generate (starting with the 22) */
  double tRef,                                   /* Time shift with respect to the 22-fit removed waveform (s) */
  double phiRef,                                 /* Phase at reference frequency */
  double fRef,                                   /* Reference frequency (Hz); 0 defaults to fLow */
  double m1SI,                                   /* Mass of companion 1 (kg) */
  double m2SI,                                   /* Mass of companion 2 (kg) */
  double distance,                               /* Distance of source (m) */
  int setphiRefattRef);                          /* Flag for adjusting the FD phase at phiRef at the given fRef, which depends also on tRef - if false, treat phiRef simply as an orbital phase shift (minus an observer phase shift) */

int SimEOBNRv2HMROMExtTF2(
  ListmodesCAmpPhaseFrequencySeries **listhlm,   /* Output: list of modes in Frequency-domain amplitude and phase form */
  int nbmode,                                    /* Number of modes to generate (starting with the 22) */
  double Mf_match,                               /* Minimum frequency using EOBNRv2HMROM in inverse total mass units*/
  double minf,                                   /* Minimum frequency required */
  int tagexthm,                                  /* Tag to decide whether or not to extend the higher modes as well */
  double deltatRef,                              /* Time shift so that the peak of the 22 mode occurs at deltatRef */
  double phiRef,                                 /* Phase at reference frequency */
  double fRef,                                   /* Reference frequency (Hz); 0 defaults to fLow */
  double m1SI,                                   /* Mass of companion 1 (kg) */
  double m2SI,                                   /* Mass of companion 2 (kg) */
  double distance,                               /* Distance of source (m) */
  int setphiRefattRef);                          /* Flag for adjusting the FD phase at phiRef at the given fRef, which depends also on tRef - if false, treat phiRef simply as an orbital phase shift (minus an observer phase shift) */

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

#endif /* _EOBNRV2HMROM_H */