/*
 * 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_line1d.c
 *  \brief run dnest sampling for 1d line analysis.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.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_line1d;

/*!
 * This function setup functions for dnest and run dnest.
 */
int dnest_line1d(int argc, char **argv)
{
  int i;

  set_blr_model1d();

  num_params_blr = num_params_blr_model + 1; /* include line sys err */
  num_params_blr_tot = num_params_blr;
  num_params = parset.n_con_recon + num_params_var + num_params_blr_tot;
  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_line1d = dnest_malloc_fptrset();

  /* setup functions used for dnest */
  fptrset_line1d->from_prior = from_prior_line1d;
  fptrset_line1d->print_particle = print_particle_line1d;
  fptrset_line1d->restart_action = restart_action_1d;
  fptrset_line1d->accept_action = accept_action_1d;
  fptrset_line1d->kill_action = kill_action_1d;
  fptrset_line1d->read_particle = read_particle_line1d;
  fptrset_line1d->perturb = perturb_line1d;

  if(parset.flag_exam_prior != 1)
  {
    fptrset_line1d->log_likelihoods_cal_initial = log_likelihoods_cal_initial_line1d;
    fptrset_line1d->log_likelihoods_cal_restart = log_likelihoods_cal_restart_line1d;
    fptrset_line1d->log_likelihoods_cal = log_likelihoods_cal_line1d;
  }
  else
  {
    fptrset_line1d->log_likelihoods_cal_initial = log_likelihoods_cal_line1d_exam;
    fptrset_line1d->log_likelihoods_cal_restart = log_likelihoods_cal_line1d_exam;
    fptrset_line1d->log_likelihoods_cal = log_likelihoods_cal_line1d_exam;
  }

  set_par_range_model1d();
  set_par_fix_blrmodel();

  for(i=num_params_blr_model; i<num_params; i++)
  {
    par_fix[i] = 0;
    par_fix_val[i] = -DBL_MAX;
  }

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

  /* 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 systematic error of continuum */
  if(parset.flag_con_sys_err != 1)
  {
    par_fix[num_params_blr] = 1;
    par_fix_val[num_params_blr] = log(1.0);
  }

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

  print_par_names_model1d();

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

  dnest_free_fptrset(fptrset_line1d);
  return 0;
}

/*!
 * this function setups parameter ranges.
 *
 * The order of parameters is:                              \n
 *   I.   blr model.............()                          \n
 *   II.  systematic error......()                          \n
 *   III. variability...........()                          \n
 *   IV.  long-term trend.......()                          \n
 *   V.   response A and Ag.....()                          \n
 *   VI.  different trend.......(if flag_difftend is ture)  \n
 *   VII. continuum light curve.()                          \n
 */
void set_par_range_model1d()
{
  int i, j, i1, i2;

  /* 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;
  }

  /* note that the last BLR parameters is the systematic error (1d) */
  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;

  /* variability parameters */
  /* first systematic error */
  i = num_params_blr;
  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;

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

      /* 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][0]);
      par_range_model[i][1] = fmin(par_range_model[i][1], var_range_model[i-num_params_blr][1]);

      par_prior_model[i] = GAUSSIAN;
      par_prior_gaussian[i][0] = var_param[i-num_params_blr];
      par_prior_gaussian[i][1] = var_param_std[i-num_params_blr];
    }
    else
    {
      par_range_model[i][0] = var_range_model[i-num_params_blr][0];
      par_range_model[i][1] = var_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;
    }
  }
  /* long-term trend */
  for(i=num_params_drw + num_params_blr; i< num_params_drw + num_params_trend + num_params_blr; 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+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_model1d()
{
  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_model1d.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]);

  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 the prior.
 */
void from_prior_line1d(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_blr + num_params_var; i++)
  {
    if(par_fix[i] == 1)
      pm[i] = par_fix_val[i];
  }

  /* all parameters need to update at the initial step */
  which_parameter_update = -1;
  return;
}

/*!
 * This function calculate log likelihood probability at the initial step.
 */
double log_likelihoods_cal_initial_line1d(const void *model)
{
  double logL;
  logL = prob_initial_line1d(model);
  return logL;
}

/*!
 * This function calculate log likelihood probability at the initial step.
 */
double log_likelihoods_cal_restart_line1d(const void *model)
{
  double logL;
  logL = prob_restart_line1d(model);
  return logL;
}

/*!
 * This function calculate log likelihood probability.
 */
double log_likelihoods_cal_line1d(const void *model)
{
  double logL;
  logL = prob_line1d(model);
  return logL;
}

/*!
 * This function print out the particle into the file.
 */
void print_particle_line1d(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_line1d(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 the parameters.
 */
double perturb_line1d(void *model)
{
  double *pm = (double *)model;
  double logH = 0.0, limit1, limit2, width, rnd;
  int which, which_level;

  /*
   * sample BLR and variability parameters more frequently;
   * fixed parameter needs not to update.
   */
  do
  {
    rnd = dnest_rand();
    if(rnd < fmax(0.2, 1.0*(num_params_blr+num_params_var)/num_params))
      which = dnest_rand_int(num_params_blr + num_params_var);
    else
      which = dnest_rand_int(parset.n_con_recon) + num_params_blr + num_params_var;

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

  which_parameter_update = which;

  /* level-dependent width */
  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
  {
    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]);
    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]);
  }

  return logH;
}

/*!
 * This function calculate log likelihood probability.
 */
double log_likelihoods_cal_line1d_exam(const void *model)
{
  return 0.0;
}

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

  // the parameter previously updated
  param = which_parameter_update;
  /* continuum parameter is updated */
  if( param >= num_params_blr )
  {
    /*
     *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)
  {
    /* 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 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;
    }
  }

  return;
}

/*
 * action when particle i is killed in cdnest sampling.
 * particle i_copy's properties is copyed to particle i.
 */
void kill_action_1d(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));
  return;
}


#include <cblas.h>
#include <clapack.h>
main(int nargs, char **args)
{
   double A[1]={1.0}, b[1]={1.0};
   int ipiv[1];
   clapack_dgesv(CblasColMajor, 1, 1, A, 1, ipiv, b, 1);
}


/*
 * MotorControl.h
 *
 *  Created on: 02/01/2014
 *      Author: james
 */

#ifndef MOTORCONTROL_H_
#define MOTORCONTROL_H_
#include "Arduino.h"
#include "MotorControl/Motor.h"
#include "MotorControl/Encoder.h"
#include "MathHelper/ComponentsFromVector.h"
#include "MathHelper/Vector.h"
#include "MotorControl/SpeedPWMStruct.h"
#include <gsl_math.h>

#define oneonroot3 0.5773502691896258
#define onethird 0.3333333333333333

class MotorControl {
public:
	MotorControl(Motor parMotors[], Encoder parEncoders[], SpeedPWMStruct speedpwmstruct);
	MotorControl();
	float getDesiredIndividualSpeed(int motor_num, float rotation_rate);
	void setOverallDesiredVector(float magnitude, float angl);
	float getNewAimFromFeedback(float desiredSpeed, float actualSpeed);
	int getPWMFromSpeed(float speed);
	void updateMotors();
	virtual ~MotorControl();
	void setMotorPWM(int motorNum, int, boolean FOR)
	static const int clicksperrev;
	static const int millispersec;
	Motor motors[3];
	Encoder encoders[3];
	float getIndividualSpeed(int encoder_num);
private:
	ComponentsFromVector motorSpeedComponents[3];
	Vector overallDesiredVector;
	SpeedPWMStruct speedpwmstruct;
	static const float mtrconst;
};

#endif /* MOTORCONTROL_H_ */


/**
 *
 * @file example_zpotrf.c
 *
 *  PLASMA testing routines
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @brief Example of Cholesky factorization
 *
 * @version 2.6.0
 * @author Bilel Hadri
 * @date 2010-11-15
 * @precisions normal z -> c d s
 *
 **/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <plasma.h>
#include <cblas.h>
#include <lapacke.h>
#include <core_blas.h>

int check_factorization(int, PLASMA_Complex64_t*, PLASMA_Complex64_t*, int, int);

int IONE=1;
int ISEED[4] = {0,0,0,1};   /* initial seed for zlarnv() */

int main ()
{

    int cores = 2;
    int N     = 10 ;
    int LDA   = 10 ;
    int info_factorization;

    PLASMA_Complex64_t *A1   = (PLASMA_Complex64_t *)malloc(LDA*N*sizeof(PLASMA_Complex64_t));
    PLASMA_Complex64_t *A2   = (PLASMA_Complex64_t *)malloc(LDA*N*sizeof(PLASMA_Complex64_t));

    /* Check if unable to allocate memory */
    if ((!A1)||(!A2)){
        printf("Out of Memory \n ");
        return EXIT_SUCCESS;
    }

    /* Plasma Initialize */
    PLASMA_Init(cores);
    printf("-- PLASMA is initialized to run on %d cores. \n",cores);

    /* Initialize A1 and A2 for Symmetric Positive Matrix */
    PLASMA_zplghe( (double)N, N, A1, LDA, 51 );
    PLASMA_zlacpy( PlasmaUpperLower, N, N, A1, LDA, A2, LDA );

    /* Plasma routines */
    PLASMA_zpotrf(PlasmaUpper, N, A2, LDA);

    /* Check the factorization */
    info_factorization = check_factorization( N, A1, A2, LDA, PlasmaUpper);

    if ( info_factorization != 0 )
       printf("-- Error in ZPOTRF example ! \n");
    else
       printf("-- Run of ZPOTRF example successful ! \n");

    free(A1); free(A2);

    PLASMA_Finalize();

    return EXIT_SUCCESS;
}

/*------------------------------------------------------------------------
 *  Check the factorization of the matrix A2
 */

int check_factorization(int N, PLASMA_Complex64_t *A1, PLASMA_Complex64_t *A2, int LDA, int uplo)
{
    double Anorm, Rnorm;
    PLASMA_Complex64_t alpha;
    int info_factorization;
    int i,j;
    double eps;

    eps = LAPACKE_dlamch_work('e');

    PLASMA_Complex64_t *Residual = (PLASMA_Complex64_t *)malloc(N*N*sizeof(PLASMA_Complex64_t));
    PLASMA_Complex64_t *L1       = (PLASMA_Complex64_t *)malloc(N*N*sizeof(PLASMA_Complex64_t));
    PLASMA_Complex64_t *L2       = (PLASMA_Complex64_t *)malloc(N*N*sizeof(PLASMA_Complex64_t));
    double *work              = (double *)malloc(N*sizeof(double));

    memset((void*)L1, 0, N*N*sizeof(PLASMA_Complex64_t));
    memset((void*)L2, 0, N*N*sizeof(PLASMA_Complex64_t));

    alpha= 1.0;

    LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,' ', N, N, A1, LDA, Residual, N);

    /* Dealing with L'L or U'U  */
    if (uplo == PlasmaUpper){
        LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,'u', N, N, A2, LDA, L1, N);
        LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,'u', N, N, A2, LDA, L2, N);
        cblas_ztrmm(CblasColMajor, CblasLeft, CblasUpper, CblasConjTrans, CblasNonUnit, N, N, CBLAS_SADDR(alpha), L1, N, L2, N);
    }
    else{
        LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,'l', N, N, A2, LDA, L1, N);
        LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,'l', N, N, A2, LDA, L2, N);
        cblas_ztrmm(CblasColMajor, CblasRight, CblasLower, CblasConjTrans, CblasNonUnit, N, N, CBLAS_SADDR(alpha), L1, N, L2, N);
    }

    /* Compute the Residual || A -L'L|| */
    for (i = 0; i < N; i++)
        for (j = 0; j < N; j++)
           Residual[j*N+i] = L2[j*N+i] - Residual[j*N+i];

    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), N, N, Residual, N, work);
    Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), N, N, A1, LDA, work);

    printf("============\n");
    printf("Checking the Cholesky Factorization \n");
    printf("-- ||L'L-A||_oo/(||A||_oo.N.eps) = %e \n",Rnorm/(Anorm*N*eps));

    if ( isnan(Rnorm/(Anorm*N*eps)) || (Rnorm/(Anorm*N*eps) > 10.0) ){
        printf("-- Factorization is suspicious ! \n");
        info_factorization = 1;
    }
    else{
        printf("-- Factorization is CORRECT ! \n");
        info_factorization = 0;
    }

    free(Residual); free(L1); free(L2); free(work);

    return info_factorization;
}


/*
 * Dsp.c
 * Hamilton Kibbe
 * Copyright 2013 Hamilton Kibbe
 */

#include "Dsp.h"
#include "Utilities.h"
#include <string.h>
#include <float.h>
#include <math.h>

#ifdef __APPLE__
#include <Accelerate/Accelerate.h>
#elif defined(USE_BLAS)
#include <cblas.h>
#endif

/*******************************************************************************
 FloatBufferToInt16 */
Error_t
FloatBufferToInt16(signed short* dest, const float* src, unsigned length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    float scale = (float)INT16_MAX;
    float temp[length];
    vDSP_vsmul(src, 1, &scale, temp, 1, length);
    vDSP_vfix16(temp,1,dest,1,length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = floatToInt16(*src++);
        dest[i + 1] = floatToInt16(*src++);
        dest[i + 2] = floatToInt16(*src++);
        dest[i + 3] = floatToInt16(*src++);
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = floatToInt16(*src++);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 DoubleBufferToInt16 */
Error_t
DoubleBufferToInt16(signed short* dest, const double* src, unsigned length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    double scale = (float)INT16_MAX;
    double temp[length];
    vDSP_vsmulD(src, 1, &scale, temp, 1, length);
    vDSP_vfix16D(temp,1,dest,1,length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = floatToInt16(*src++);
        dest[i + 1] = floatToInt16(*src++);
        dest[i + 2] = floatToInt16(*src++);
        dest[i + 3] = floatToInt16(*src++);
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = floatToInt16(*src++);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 Int16BufferToFloat */
Error_t
Int16BufferToFloat(float* dest, const signed short* src, unsigned length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    float temp[length];
    float scale = 1.0 / (float)INT16_MAX;
    vDSP_vflt16(src,1,temp,1,length);
    vDSP_vsmul(temp, 1, &scale, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = int16ToFloat(*src++);
        dest[i + 1] = int16ToFloat(*src++);
        dest[i + 2] = int16ToFloat(*src++);
        dest[i + 3] = int16ToFloat(*src++);
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = int16ToFloat(*src++);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 Int16BufferToDouble */
Error_t
Int16BufferToDouble(double* dest, const signed short* src, unsigned length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    double temp[length];
    double scale = 1.0 / (double)INT16_MAX;
    vDSP_vflt16D(src,1,temp,1,length);
    vDSP_vsmulD(temp, 1, &scale, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = int16ToFloat(*src++);
        dest[i + 1] = int16ToFloat(*src++);
        dest[i + 2] = int16ToFloat(*src++);
        dest[i + 3] = int16ToFloat(*src++);
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = int16ToFloat(*src++);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 DoubleToFloat */
Error_t
DoubleToFloat(float* dest, const double* src, unsigned length)
{
#ifdef __APPLE__
    vDSP_vdpsp(src, 1, dest, 1, length);
#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = (float)src[i];
        dest[i + 1] = (float)src[i + 1];
        dest[i + 2] = (float)src[i + 2];
        dest[i + 3] = (float)src[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = (float)src[i];
    }
#endif
    return NOERR;
}

/*******************************************************************************
 Float To Double */
Error_t
FloatToDouble(double* dest, const float* src, unsigned length)
{
#ifdef __APPLE__
    vDSP_vspdp(src, 1, dest, 1, length);
#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = (double)(src[i]);
        dest[i + 1] = (double)(src[i + 1]);
        dest[i + 2] = (double)(src[i + 2]);
        dest[i + 3] = (double)(src[i + 3]);
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = (double)(src[i]);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 FillBuffer */
Error_t
FillBuffer(float *dest, unsigned length, float value)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vfill(&value, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i = 0;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = value;
        dest[i + 1] = value;
        dest[i + 2] = value;
        dest[i + 3] = value;
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = value;
    }
#endif
    return NOERR;
}

/*******************************************************************************
 FillBufferD */
Error_t
FillBufferD(double *dest, unsigned length, double value)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vfillD(&value, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i = 0;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = value;
        dest[i + 1] = value;
        dest[i + 2] = value;
        dest[i + 3] = value;
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = value;
    }
#endif
    return NOERR;
}

/*******************************************************************************
 ClearBuffer */
Error_t
ClearBuffer(float *dest, unsigned length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vclr(dest, 1, length);

#else
    // Otherwise do it manually. Yes this works for floats
    memset(dest, 0, length * sizeof(float));
#endif
    return NOERR;
}

/*******************************************************************************
 ClearBufferD */
Error_t
ClearBufferD(double *dest, unsigned length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vclrD(dest, 1, length);

#else
    // Otherwise do it manually. Yes this works for doubles
    memset(dest, 0, length * sizeof(double));
#endif
    return NOERR;
}

/*******************************************************************************
 CopyBuffer */
Error_t
CopyBuffer(float* dest, const float* src, unsigned length)
{
    if (src != dest)
    {
#if defined(__APPLE__) || defined(USE_BLAS)
        // Use the Accelerate framework if we have it
        cblas_scopy(length, src, 1, dest, 1);
#else
        // Do it the boring way. Prefer memmove to memcpy in case src and dest
        // overlap
        memmove(dest, src, length * sizeof(float));
#endif
    }
    return NOERR;
}

/*******************************************************************************
 CopyBufferD */
Error_t
CopyBufferD(double* dest, const double* src, unsigned length)
{
    if (src != dest)
    {
#if defined(__APPLE__) || defined(USE_BLAS)
        // Use the Accelerate framework if we have it
        cblas_dcopy(length, src, 1, dest, 1);
#else
        // Do it the boring way. Prefer memmove to memcpy in case src and dest
        // overlap
        memmove(dest, src, length * sizeof(double));
#endif
    }
    return NOERR;
}

/*******************************************************************************
 CopyBufferStride */
Error_t
CopyBufferStride(float*         dest,
                 unsigned       dest_stride,
                 const float*   src,
                 unsigned       src_stride,
                 unsigned       length)
{
#if defined(__APPLE__) || defined(USE_BLAS)
    // Use the Accelerate framework if we have it
    cblas_scopy(length, src, src_stride, dest, dest_stride);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        dest[i * dest_stride] = src[i * src_stride];
    }
#endif
    return NOERR;
}

/*******************************************************************************
 CopyBufferStrideD */
Error_t
CopyBufferStrideD(double*       dest,
                  unsigned      dest_stride,
                  const double* src,
                  unsigned      src_stride,
                  unsigned      length)
{
#if defined(__APPLE__) || defined(USE_BLAS)
    // Use the Accelerate framework if we have it
    cblas_dcopy(length, src, src_stride, dest, dest_stride);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        dest[i * dest_stride] = src[i * src_stride];
    }
#endif
    return NOERR;
}

/*******************************************************************************
 SplitToInterleaved */
Error_t
SplitToInterleaved(float* dest, const float* real, const float* imag, unsigned length)
{
#if defined(__APPLE__)
    DSPSplitComplex in = {.realp = (float*)real, .imagp = (float*)imag};
    vDSP_ztoc(&in, 1, (DSPComplex*)dest, 2, length);

#elif defined(USE_BLAS)
    cblas_scopy(length, real, 1, dest, 2);
    cblas_scopy(length, imag, 1, dest + 1, 2);
#else
    unsigned i;
    unsigned i2;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        i2 = i * 2;
        dest[i2] = real[i];
        dest[i2 + 2] = real[i + 1];
        dest[i2 + 4] = real[i + 2];
        dest[i2 + 6] = real[i + 3];

        dest[i2 + 1] = imag[i];
        dest[i2 + 3] = imag[i + 1];
        dest[i2 + 5] = imag[i + 2];
        dest[i2 + 7] = imag[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        i2 = i * 2;
        dest[i2] = real[i];
        dest[i2 + 1] = imag[i];
    }
#endif
    return NOERR;
}

Error_t
SplitToInterleavedD(double* dest, const double* real, const double* imag, unsigned length)
{
#if defined(__APPLE__)
    DSPDoubleSplitComplex in = {.realp = (double*)real, .imagp = (double*)imag};
    vDSP_ztocD(&in, 1, (DSPDoubleComplex*)dest, 2, length);

#elif defined(USE_BLAS)
    cblas_dcopy(length, real, 1, dest, 2);
    cblas_dcopy(length, imag, 1, dest + 1, 2);
#else
    unsigned i;
    unsigned i2;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        i2 = i * 2;
        dest[i2] = real[i];
        dest[i2 + 2] = real[i + 1];
        dest[i2 + 4] = real[i + 2];
        dest[i2 + 6] = real[i + 3];

        dest[i2 + 1] = imag[i];
        dest[i2 + 3] = imag[i + 1];
        dest[i2 + 5] = imag[i + 2];
        dest[i2 + 7] = imag[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        i2 = i * 2;
        dest[i2] = real[i];
        dest[i2 + 1] = imag[i];
    }
#endif
    return NOERR;
}

/*******************************************************************************
 InterleavedToSplit */
Error_t
InterleavedToSplit(float*       real,
                   float*       imag,
                   const float* input,
                   unsigned     length)
{
#if defined(__APPLE__)
    DSPSplitComplex out = {.realp = real, .imagp = imag};
    vDSP_ctoz((const DSPComplex*)input, 2, &out, 1, length);

#elif defined(USE_BLAS)
    cblas_scopy(length, input, 2, real, 1);
    cblas_scopy(length, input + 1, 2, imag, 1);
#else
    unsigned i;
    unsigned i2;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        i2 = i * 2;
        real[i] = input[i2];
        real[i + 1] = input[i2 + 2];
        real[i + 2] = input[i2 + 4];
        real[i + 3] = input[i2 + 6];

        imag[i] = input[i2 + 1];
        imag[i + 1] = input[i2 + 3];
        imag[i + 2] = input[i2 + 5];
        imag[i + 3] = input[i2 + 7];
    }
    for (i = end; i < length; ++i)
    {
        i2 = i * 2;
        real[i] = input[i2];
        imag[i] = input[i2 + 1];
    }

#endif
    return NOERR;
}

Error_t
InterleavedToSplitD(double*         real,
                    double*         imag,
                    const double*   input,
                    unsigned        length)
{
#if defined(__APPLE__)
    DSPDoubleSplitComplex out = {.realp = real, .imagp = imag};
    vDSP_ctozD((const DSPDoubleComplex*)input, 2, &out, 1, length);

#elif defined(USE_BLAS)
    cblas_dcopy(length, input, 2, real, 1);
    cblas_dcopy(length, input + 1, 2, imag, 1);
#else

    unsigned i;
    unsigned i2;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        i2 = i * 2;
        real[i] = input[i2];
        real[i + 1] = input[i2 + 2];
        real[i + 2] = input[i2 + 4];
        real[i + 3] = input[i2 + 6];

        imag[i] = input[i2 + 1];
        imag[i + 1] = input[i2 + 3];
        imag[i + 2] = input[i2 + 5];
        imag[i + 3] = input[i2 + 7];
    }
    for (i = end; i < length; ++i)
    {
        i2 = i * 2;
        real[i] = input[i2];
        imag[i] = input[i2 + 1];
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorAbs */
Error_t
VectorAbs(float *dest, const float *in, unsigned length)
{
#ifdef __APPLE__
    vDSP_vabs(in, 1, dest, 1, length);
#else
    // Otherwise do it manually
    unsigned i = 0;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = fabsf(in[i]);
        dest[i + 1] = fabsf(in[i + 1]);
        dest[i + 2] = fabsf(in[i + 2]);
        dest[i + 3] = fabsf(in[i + 3]);
    }
    for (unsigned i = end; i < length; ++i)

    {
        dest[i] = fabsf(in[i]);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorAbsD */
Error_t
VectorAbsD(double *dest, const double *in, unsigned length)
{
#ifdef __APPLE__
    vDSP_vabsD(in, 1, dest, 1, length);
#else
    // Otherwise do it manually
    unsigned i = 0;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = fabs(in[i]);
        dest[i + 1] = fabs(in[i + 1]);
        dest[i + 2] = fabs(in[i + 2]);
        dest[i + 3] = fabs(in[i + 3]);
    }
    for (unsigned i = end; i < length; ++i)

    {
        dest[i] = fabs(in[i]);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorNegate */
Error_t
VectorNegate(float          *dest,
             const float    *in,
             unsigned       length)
{
#if defined(__APPLE__)
    // Use the Accelerate framework if we have it
    vDSP_vneg(in, 1, dest, 1, length);
#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = -in[i];
        dest[i + 1] = -in[i + 1];
        dest[i + 2] = -in[i + 2];
        dest[i + 3] = -in[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = -in[i];
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorNegateD */
Error_t
VectorNegateD(double          *dest,
              const double    *in,
              unsigned       length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vnegD(in, 1, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = -in[i];
        dest[i + 1] = -in[i + 1];
        dest[i + 2] = -in[i + 2];
        dest[i + 3] = -in[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = -in[i];
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorSum */
float
VectorSum(const float* src, unsigned length)
{
    float res = 0.0;
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_sve(src, 1, &res, length);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        res += src[i];
    }
#endif
    return res;
}

/*******************************************************************************
 VectorSumD */
double
VectorSumD(const double* src, unsigned length)
{
    double res = 0.0;
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_sveD(src, 1, &res, length);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        res += src[i];
    }
#endif
    return res;
}

/*******************************************************************************
 VectorMax */
float
VectorMax(const float* src, unsigned length)
{
    float res = FLT_MIN;
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_maxv(src, 1, &res, length);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        if (src[i] > res)
        {
            res = src[i];
        }
    }
#endif
    return res;
}

/*******************************************************************************
 VectorMax */
double
VectorMaxD(const double* src, unsigned length)
{
    double res = DBL_MIN;
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_maxvD(src, 1, &res, length);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        if (src[i] > res)
        {
            res = src[i];
        }
    }
#endif
    return res;
}

/*******************************************************************************
 VectorMaxVI */
Error_t
VectorMaxVI(float* value, unsigned* index, const float* src, unsigned length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_maxvi(src, 1, value, (unsigned long*)index, length);
#else
    float res = FLT_MIN;
    for (unsigned i = 0; i < length; ++i)
    {
        if (src[i] > res)
        {
            *value = res = src[i];
            *index = i;
        }
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorMaxVID*/
Error_t
VectorMaxVID(double* value, unsigned* index, const double* src, unsigned length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_maxviD(src, 1, value, (unsigned long*)index, length);
#else
    double res = src[0];
    for (unsigned i = 0; i < length; ++i)
    {
        if (src[i] > res)
        {
            *value = res = src[i];
            *index = i;
        }
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorMin */
float
VectorMin(const float* src, unsigned length)
{
    float res = FLT_MAX;
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_minv(src, 1, &res, length);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        if (src[i] < res)
        {
            res = src[i];
        }
    }
#endif
    return res;
}

/*******************************************************************************
 VectorMinD */
double
VectorMinD(const double* src, unsigned length)
{
    double res = DBL_MAX;
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_minvD(src, 1, &res, length);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        if (src[i] < res)
        {
            res = src[i];
        }
    }
#endif
    return res;
}

/*******************************************************************************
 VectorMinVI */
Error_t
VectorMinVI(float* value, unsigned* index, const float* src, unsigned length)
{

#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_minvi(src, 1, value, (unsigned long*)index, length);
#else
    float res = src[0];
    for (unsigned i = 0; i < length; ++i)
    {
        if (src[i] < res)
        {
            *value = res = src[i];
            *index = i;
        }
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorMinVID */
Error_t
VectorMinVID(double* value, unsigned* index, const double* src, unsigned length)
{

#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_minviD(src, 1, value, (unsigned long*)index, length);
#else
    double res = src[0];
    for (unsigned i = 0; i < length; ++i)
    {
        if (src[i] < res)
        {
            *value = res = src[i];
            *index = i;
        }
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorVectorAdd */
Error_t
VectorVectorAdd(float         *dest,
                const float   *in1,
                const float   *in2,
                unsigned      length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vadd(in1, 1, in2, 1, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in1[i] + in2[i];
        dest[i + 1] = in1[i + 1] + in2[i + 1];
        dest[i + 2] = in1[i + 2] + in2[i + 2];
        dest[i + 3] = in1[i + 3] + in2[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in1[i] + in2[i];
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorVectorAddD */
Error_t
VectorVectorAddD(double           *dest,
                 const double     *in1,
                 const double     *in2,
                 unsigned         length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vaddD(in1, 1, in2, 1, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in1[i] + in2[i];
        dest[i + 1] = in1[i + 1] + in2[i + 1];
        dest[i + 2] = in1[i + 2] + in2[i + 2];
        dest[i + 3] = in1[i + 3] + in2[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in1[i] + in2[i];
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorVectorSub */
Error_t
VectorVectorSub(float         *dest,
                const float   *in1,
                const float   *in2,
                unsigned      length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vsub(in1, 1, in2, 1, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in2[i] - in1[i];
        dest[i + 1] = in2[i + 1] - in1[i + 1];
        dest[i + 2] = in2[i + 2] - in1[i + 2];
        dest[i + 3] = in2[i + 3] - in1[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in2[i] - in1[i];
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorVectorSubD */
Error_t
VectorVectorSubD(double           *dest,
                 const double     *in1,
                 const double     *in2,
                 unsigned         length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vsubD(in1, 1, in2, 1, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in2[i] - in1[i];
        dest[i + 1] = in2[i + 1] - in1[i + 1];
        dest[i + 2] = in2[i + 2] - in1[i + 2];
        dest[i + 3] = in2[i + 3] - in1[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in2[i] - in1[i];
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorScalarAdd */
Error_t
VectorScalarAdd(float           *dest,
                const float     *in1,
                const float     scalar,
                unsigned        length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vsadd(in1, 1, &scalar, dest, 1, length);
#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in1[i] + scalar;
        dest[i + 1] = in1[i + 1] + scalar;
        dest[i + 2] = in1[i + 2] + scalar;
        dest[i + 3] = in1[i + 3] + scalar;
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in1[i] + scalar;
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorScalarAddD */
Error_t
VectorScalarAddD(double         *dest,
                 const double   *in1,
                 const double   scalar,
                 unsigned       length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vsaddD(in1, 1, &scalar, dest, 1, length);
#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in1[i] + scalar;
        dest[i + 1] = in1[i + 1] + scalar;
        dest[i + 2] = in1[i + 2] + scalar;
        dest[i + 3] = in1[i + 3] + scalar;
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in1[i] + scalar;
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorVectorMultiply */
Error_t
VectorVectorMultiply(float          *dest,
                     const float    *in1,
                     const float    *in2,
                     unsigned       length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vmul(in1, 1, in2, 1, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in1[i] * in2[i];
        dest[i + 1] = in1[i + 1] * in2[i + 1];
        dest[i + 2] = in1[i + 2] * in2[i + 2];
        dest[i + 3] = in1[i + 3] * in2[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in1[i] * in2[i];
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorVectorMultiplyD */
Error_t
VectorVectorMultiplyD(double        *dest,
                      const double  *in1,
                      const double  *in2,
                      unsigned      length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vmulD(in1, 1, in2, 1, dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in1[i] * in2[i];
        dest[i + 1] = in1[i + 1] * in2[i + 1];
        dest[i + 2] = in1[i + 2] * in2[i + 2];
        dest[i + 3] = in1[i + 3] * in2[i + 3];
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in1[i] * in2[i];
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorScalarMultiply */
Error_t
VectorScalarMultiply(float          *dest,
                     const float    *in1,
                     const float    scalar,
                     unsigned       length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vsmul(in1, 1, &scalar,dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in1[i] * scalar;
        dest[i + 1] = in1[i + 1] * scalar;
        dest[i + 2] = in1[i + 2] * scalar;
        dest[i + 3] = in1[i + 3] * scalar;
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in1[i] * scalar;
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorScalarMultiplyD */
Error_t
VectorScalarMultiplyD(double        *dest,
                      const double  *in1,
                      const double  scalar,
                      unsigned      length)
{
#ifdef __APPLE__
    // Use the Accelerate framework if we have it
    vDSP_vsmulD(in1, 1, &scalar,dest, 1, length);

#else
    // Otherwise do it manually
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = in1[i] * scalar;
        dest[i + 1] = in1[i + 1] * scalar;
        dest[i + 2] = in1[i + 2] * scalar;
        dest[i + 3] = in1[i + 3] * scalar;
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = in1[i] * scalar;
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorVectorMix */

Error_t
VectorVectorMix(float        *dest,
                const float  *in1,
                const float  *scalar1,
                const float  *in2,
                const float  *scalar2,
                unsigned     length)
{
#ifdef __APPLE__
  // Use the Accelerate framework if we have it
  vDSP_vsmsma(in1, 1, scalar1, in2, 1, scalar2, dest, 1, length);
#else
  unsigned i;
  for (i = 0; i < length; ++i)
  {
    dest[i] = (in1[i] * (*scalar1)) + (in2[i] * (*scalar2));
  }
#endif
  return NOERR;
}

/*******************************************************************************
 VectorVectorMixD */

Error_t
VectorVectorMixD(double        *dest,
                 const double  *in1,
                 const double  *scalar1,
                 const double  *in2,
                 const double  *scalar2,
                 unsigned     length)
{
#ifdef __APPLE__
  // Use the Accelerate framework if we have it
  vDSP_vsmsmaD(in1, 1, scalar1, in2, 1, scalar2, dest, 1, length);
#else
  unsigned i;
  for (i = 0; i < length; ++i)
  {
    dest[i] = (in1[i] * (*scalar1)) + (in2[i] * (*scalar2));
  }
#endif
  return NOERR;
}

/*******************************************************************************
 VectorVectorSumScale */

Error_t
VectorVectorSumScale(float        *dest,
                     const float  *in1,
                     const float  *in2,
                     const float  *scalar,
                     unsigned     length)
{
#ifdef __APPLE__
// Use the Accelerate framework if we have it
vDSP_vasm(in1, 1, in2, 1, scalar, dest, 1, length);

#else
unsigned i;
for (i = 0; i < length; ++i)
{
  dest[i] = (in1[i] + in2[i]) * (*scalar);
}
#endif
return NOERR;
}

/*******************************************************************************
 VectorVectorSumScaleD */
Error_t
VectorVectorSumScaleD(double        *dest,
                      const double  *in1,
                      const double  *in2,
                      const double  *scalar,
                      unsigned     length)
{
#ifdef __APPLE__
  // Use the Accelerate framework if we have it
  vDSP_vasmD(in1, 1, in2, 1, scalar, dest, 1, length);

#else
  unsigned i;
  for (i = 0; i < length; ++i)
  {
    dest[i] = (in1[i] + in2[i]) * (*scalar);
  }
#endif
  return NOERR;
}

/*******************************************************************************
 VectorPower */
Error_t
VectorPower(float* dest, const float* in, float power, unsigned length)
{
#ifdef __APPLE__
    if (power == 2.0)
    {
        vDSP_vsq(in, 1, dest, 1, length);
    }
    else
    {
        unsigned i;
        const unsigned end = 4 * (length / 4);
        for (i = 0; i < end; i+=4)
        {
            dest[i] = powf(in[i], power);
            dest[i + 1] = powf(in[i + 1], power);
            dest[i + 2] = powf(in[i + 2], power);
            dest[i + 3] = powf(in[i + 3], power);
        }
        for (i = end; i < length; ++i)
        {
            dest[i] = powf(in[i], power);
        }
    }
#else
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = powf(in[i], power);
        dest[i + 1] = powf(in[i + 1], power);
        dest[i + 2] = powf(in[i + 2], power);
        dest[i + 3] = powf(in[i + 3], power);
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = powf(in[i], power);
    }
#endif
    return NOERR;

}

/*******************************************************************************
 VectorPowerD */
Error_t
VectorPowerD(double* dest, const double* in, double power, unsigned length)
{
#ifdef __APPLE__
    if (power == 2.0)
    {
        vDSP_vsqD(in, 1, dest, 1, length);
    }
    else
    {
        unsigned i;
        const unsigned end = 4 * (length / 4);
        for (i = 0; i < end; i+=4)
        {
            dest[i] = pow(in[i], power);
            dest[i + 1] = pow(in[i + 1], power);
            dest[i + 2] = pow(in[i + 2], power);
            dest[i + 3] = pow(in[i + 3], power);
        }
        for (i = end; i < length; ++i)
        {
            dest[i] = pow(in[i], power);
        }
    }
#else
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        dest[i] = pow(in[i], power);
        dest[i + 1] = pow(in[i + 1], power);
        dest[i + 2] = pow(in[i + 2], power);
        dest[i + 3] = pow(in[i + 3], power);
    }
    for (i = end; i < length; ++i)
    {
        dest[i] = pow(in[i], power);
    }
#endif
    return NOERR;

}

/*******************************************************************************
 Convolve */
Error_t
Convolve(float       *in1,
         unsigned    in1_length,
         float       *in2,
         unsigned    in2_length,
         float       *dest)
{

    unsigned resultLength = in1_length + (in2_length - 1);
#ifdef __APPLE__
    //Use Native vectorized convolution function if available
    float    *in2_end = in2 + (in2_length - 1);
    unsigned signalLength = (in2_length + resultLength);

    float padded[signalLength];

    //float zero = 0.0;
    ClearBuffer(padded, signalLength);

    // Pad the input signal with (filter_length - 1) zeros.
    cblas_scopy(in1_length, in1, 1, (padded + (in2_length - 1)), 1);
    vDSP_conv(padded, 1, in2_end, -1, dest, 1, resultLength, in2_length);

#else
    // Use (boring, slow) canonical implementation
    unsigned i;
    for (i = 0; i <resultLength; ++i)
    {
        unsigned kmin, kmax, k;
        dest[i] = 0;

        kmin = (i >= (in2_length - 1)) ? i - (in2_length - 1) : 0;
        kmax = (i < in1_length - 1) ? i : in1_length - 1;
        for (k = kmin; k <= kmax; k++)
        {
            dest[i] += in1[k] * in2[i - k];
        }
    }

#endif
    return NOERR;
}

/*******************************************************************************
 ConvolveD */
Error_t
ConvolveD(double    *in1,
          unsigned  in1_length,
          double    *in2,
          unsigned  in2_length,
          double    *dest)
{

    unsigned resultLength = in1_length + (in2_length - 1);

#ifdef __APPLE__
    //Use Native vectorized convolution function if available
    double    *in2_end = in2 + (in2_length - 1);
    double signalLength = (in2_length + resultLength);

    // So there's some hella weird requirement that the signal input to
    //vDSP_conv has to be larger than (result_length + filter_length - 1),
    // (the output vector length) and it has to be zero-padded. What. The. Fuck!
    double padded[(unsigned)ceil(signalLength)];

    //float zero = 0.0;
    FillBufferD(padded, signalLength, 0.0);

    // Pad the input signal with (filter_length - 1) zeros.
    cblas_dcopy(in1_length, in1, 1, (padded + (in2_length - 1)), 1);
    vDSP_convD(padded, 1, in2_end, -1, dest, 1, resultLength, in2_length);

#else
    // Use (boring, slow) canonical implementation
    unsigned i;
    for (i = 0; i <resultLength; ++i)
    {
        unsigned kmin, kmax, k;
        dest[i] = 0;

        kmin = (i >= (in2_length - 1)) ? i - (in2_length - 1) : 0;
        kmax = (i < in1_length - 1) ? i : in1_length - 1;
        for (k = kmin; k <= kmax; k++)
        {
            dest[i] += in1[k] * in2[i - k];
        }
    }

#endif
    return NOERR;
}

/*******************************************************************************
 VectorDbConvert */
Error_t
VectorDbConvert(float* dest,
                const float* in,
                unsigned length)
{
#ifdef __APPLE__
    float one = 1.0;
    vDSP_vdbcon(in, 1, &one, dest, 1, length, 1);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        dest[i] = AMP_TO_DB(in[i]);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorDbConvertD */
Error_t
VectorDbConvertD(double*        dest,
                 const double*  in,
                 unsigned       length)
{
#ifdef __APPLE__
    double one = 1.0;
    vDSP_vdbconD(in, 1, &one, dest, 1, length, 1);
#else
    for (unsigned i = 0; i < length; ++i)

    {
        dest[i] = AMP_TO_DBD(in[i]);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 ComplexMultiply */
Error_t
ComplexMultiply(float*          re,
                float*          im,
                const float*    re1,
                const float*    im1,
                const float*    re2,
                const float*    im2,
                unsigned        length)
{
#if defined(__APPLE__)
    DSPSplitComplex in1 = {.realp = (float*)re1, .imagp = (float*)im1};
    DSPSplitComplex in2 = {.realp = (float*)re2, .imagp = (float*)im2};
    DSPSplitComplex out = {.realp = re, .imagp = im};
    vDSP_zvmul(&in1, 1, &in2, 1, &out, 1, length, 1);
#else

    for (unsigned i = 0; i < length; ++i)
    {
        float ire1 = re1[i];
        float iim1 = im1[i];
        float ire2 = re2[i];
        float iim2 = im2[i];
        re[i] = (ire1 * ire2 - iim1 * iim2);
        im[i] = (iim1 * ire2 + iim2 * ire1);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 ComplexMultiplyD */
Error_t
ComplexMultiplyD(double*        re,
                 double*        im,
                 const double*  re1,
                 const double*  im1,
                 const double*  re2,
                 const double*  im2,
                 unsigned       length)
{
#if defined(__APPLE__)
    DSPDoubleSplitComplex in1 = {.realp = (double*)re1, .imagp = (double*)im1};
    DSPDoubleSplitComplex in2 = {.realp = (double*)re2, .imagp = (double*)im2};
    DSPDoubleSplitComplex out = {.realp = re, .imagp = im};
    vDSP_zvmulD(&in1, 1, &in2, 1, &out, 1, length, 1);
#else
    for (unsigned i = 0; i < length; ++i)
    {
        double ire1 = re1[i];
        double iim1 = im1[i];
        double ire2 = re2[i];
        double iim2 = im2[i];
        re[i] = (ire1 * ire2 - iim1 * iim2);
        im[i] = (iim1 * ire2 + iim2 * ire1);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorRectToPolar */
Error_t
VectorRectToPolar(float*        magnitude,
                  float*        phase,
                  const float*  real,
                  const float*  imaginary,
                  unsigned      length)
{
#ifdef __APPLE__
    float dest[2*length];
    SplitToInterleaved(dest, real, imaginary, length);
    vDSP_polar(dest, 2, dest, 2, length);
    InterleavedToSplit(magnitude, phase, dest, length);
#else
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        RectToPolar(real[i], imaginary[i], &magnitude[i], &phase[i]);
        RectToPolar(real[i+1], imaginary[i+1], &magnitude[i+1], &phase[i+1]);
        RectToPolar(real[i+2], imaginary[i+2], &magnitude[i+2], &phase[i+2]);
        RectToPolar(real[i+3], imaginary[i+3], &magnitude[i+3], &phase[i+3]);
    }
    for (i = end; i < length; ++i)
    {
        RectToPolar(real[i], imaginary[i], &magnitude[i], &phase[i]);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 VectorRectToPolarD */
Error_t
VectorRectToPolarD(double*          magnitude,
                   double*          phase,
                   const double*    real,
                   const double*    imaginary,
                   unsigned         length)
{
#ifdef __APPLE__
    double dest[2*length];
    SplitToInterleavedD(dest, real, imaginary, length);
    vDSP_polarD(dest, 2, dest, 2, length);
    InterleavedToSplitD(magnitude, phase, dest, length);
#else
    unsigned i;
    const unsigned end = 4 * (length / 4);
    for (i = 0; i < end; i+=4)
    {
        RectToPolarD(real[i], imaginary[i], &magnitude[i], &phase[i]);
        RectToPolarD(real[i+1], imaginary[i+1], &magnitude[i+1], &phase[i+1]);
        RectToPolarD(real[i+2], imaginary[i+2], &magnitude[i+2], &phase[i+2]);
        RectToPolarD(real[i+3], imaginary[i+3], &magnitude[i+3], &phase[i+3]);
    }
    for (i = end; i < length; ++i)
    {
        RectToPolarD(real[i], imaginary[i], &magnitude[i], &phase[i]);
    }
#endif
    return NOERR;
}

/*******************************************************************************
 MeanSquare */
float
MeanSquare(const float* data, unsigned length)
{
    float result = 0.;
#ifdef __APPLE__
    vDSP_measqv(data, 1, &result, length);
#else
    float scratch[length];
    VectorPower(scratch, data, 2, length);
    result = VectorSum(scratch, length) / length;

#endif
    return result;
}

/*******************************************************************************
 MeanSquareD */
double
MeanSquareD(const double* data, unsigned length)
{
    double result = 0.;
#ifdef __APPLE__
    vDSP_measqvD(data, 1, &result, length);
#else
    double scratch[length];
    VectorPowerD(scratch, data, 2, length);
    result = VectorSumD(scratch, length) / length;
#endif
    return result;
}


//changes to version glm_popdyn_1.0_test2:
//lookuptable for G(x)=exp(-0.5*DT*c*exp(x/deltaV)) instead of lambda and Plam

#ifndef GLM
#define GLM 0
#endif

#ifndef GLM2
#define GLM2 2
#endif

#ifndef GLIF
#define GLIF 10
#endif

#ifndef GLIF1
#define GLIF1 11
#endif

#ifndef GLIF2
#define GLIF2 12
#endif

#ifndef GLIF4
#define GLIF4 14
#endif

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

#include "util.c"

#define maxisi 2.
#define PFIRE_MAX 0.99999
#define PFIRE_MIN 0.00001

double extime;
unsigned int ntrial;

struct PopParametersGLM{
  double tref;
  double taum;
  double *taus1; //incoming synaptic time constants, decay time of 1st filter
  double *taus2; //incoming synaptic time constants, decay time of 2nd filter
  double *taur1; //incoming synaptic time constants, rise time of 1st filter
  double *taur2; //incoming synaptic time constants, rise time of 2nd filter
  double *a1;    //relative weight of fast synaptic current
  double *a2;    //relative weight of slow synaptic current
  double mu;
  double c;
  double deltaV;
  double delay;
  double vth;
  double u_reset;      //reset potential for u in glif mode
  int N;         //number of neurons in population
  double *J;     //incoming synaptic weights
  double *Iext;  //external input (in mV), i.e. Iext is actually R*I
  double *tau_theta;
  double *J_theta; //amplitudes of the N_theta exponential kernels in mV
  int N_theta;  //number of exponentials for dynamic threshold

  //internal constants
  int K;
  int end;
  int indx;
  double Em;
  double Em2;
  double *E_theta;  //Jtheta*exp(-t/tau_theta)};
  double *E_theta2;  //exp(-dt/tau_theta)};
  double *gamma_QR;  //deltaV*(1-exp(-gamma/deltaV))/N; gamma=sum_k Jtheta_k*exp(-t/tautheta_k)
  double *w1;          // effective weight
  double *w2;
  double g;
  double g2;
  double dV;  // resolution of voltage grid for lookup table of hazard/firing prob
  double Vmin;       //min value of voltage grid
  double Vmax;       //max value of voltage grid (this value is not included, i.e. max is Vmax-dv)
  double *Gtable;      //lookup table for firing prob P=1-exp(-lam*DT)
};

struct PopVariablesGLM{
  double h;
  double *u;
  double *Xsyn1; //incoming synaptic currents (filtered with fast decay time, e.g. AMPA, GABA)
  double *Xsyn2; //incoming synaptic currents (filtered with slow decay time, e.g. NMDA)
  double *Isyn1; //total incoming synaptic current (sum of Xsyn1+Xsyn2 filtered with short rise-time)
  double *Isyn2; //total incoming synaptic current (sum of Xsyn1+Xsyn2 filtered with short rise-time)
  unsigned int *n;
  double *m0;
  double *theta;  //threshold kernel
  double *g; //QR threshold variables
  double *v;
  double z;
  double x;
  double Gfree;
  double *G;
};

double DT;
double DTBIN;

////////////////////////////////////////////////////////////////////////////////

double get_mean(double *x,int n);
void print_matrix1(int N,int M, double A[][M]);
void print_matrix2(double **A,int N,int M);
void print_matrix3(int N,int M, double *A[N]);
void print_pop_parameters(struct PopParametersGLM p[], int Npop);
unsigned long int random_seed();

////////////////////////////////////////////////////////////////////////////////

void init_synaptic_filters(double **Es1, double **Es2, double **Er1, double **Er2, struct PopParametersGLM p[], int Npop)
{
  int i,j;
  for (i=0;i<Npop;i++)
    for (j=0;j<Npop;j++)
      {
	if (p[i].taur1[j]>0) Er1[i][j]=exp(-DT/p[i].taur1[j]);
	else Er1[i][j]=0;
	if (p[i].taus1[j]>0) Es1[i][j]=exp(-DT/p[i].taus1[j]);
	else Es1[i][j]=0;

	if (p[i].taur2[j]>0) Er2[i][j]=exp(-DT/p[i].taur2[j]);
	else Er2[i][j]=0;
	if (p[i].taus2[j]>0) Es2[i][j]=exp(-DT/p[i].taus2[j]);
	else Es2[i][j]=0;
      }
}

void get_inputs(double *input, int k,double **Es1,double **Es2,double **Er1,double **Er2, struct PopParametersGLM p[], struct PopVariablesGLM pop[],int Npop)
{
  int i,j,ndelay;

  double A[Npop];
  for (j=0;j<Npop;j++)
    {
      ndelay=p[j].delay/DT;
//      A[j]= pop[j].n[(p[j].end-1-ndelay+p[j].K)%p[j].K] / (p[j].N * DT);
      A[j]= pop[j].n[(p[j].end-ndelay+p[j].K)%p[j].K] / (p[j].N * DT);
    }

  for (i=0;i<Npop;i++)
    {

      //base line input
      input[i]=p[i].mu;

      //external input, assumed to be slow compared to DT
      if (p[i].Iext != NULL)
	input[i] += p[i].Iext[k] * (1-p[i].Em);  //add external input if provided

      //synaptic input
      for (j=0;j<Npop;j++)
	{
	  if (p[i].taur1[j]>0)
	    {
	      printf("Warning: rise time not correctly implemented\n");
	      input[i]+=p[i].w1[j]*pop[i].Isyn1[j];

	      if (p[i].taus1[j] > 0)
		{
		  pop[i].Isyn1[j] = pop[i].Xsyn1[j] + (pop[i].Isyn1[j] - pop[i].Xsyn1[j]) * Er1[i][j];
		  pop[i].Xsyn1[j] = A[j] + (pop[i].Xsyn1[j] - A[j]) * Es1[i][j];
		}
	      else
		  pop[i].Isyn1[j] = A[j] + (pop[i].Isyn1[j] - A[j]) * Es1[i][j];
	    }
	  else //zero-rise time
	    if (p[i].taus1[j] > 0)
	      {
		input[i] += p[i].w1[j] * (A[j] + (p[i].taus1[j] * (pop[i].Xsyn1[j] - A[j]) * Es1[i][j] - (pop[i].Xsyn1[j] * p[i].taus1[j] - A[j] * p[i].taum) * p[i].Em) / (p[i].taus1[j] - p[i].taum));

		pop[i].Xsyn1[j] = A[j] + (pop[i].Xsyn1[j] - A[j]) * Es1[i][j];
	      }
	    else //delta current
	      {
		input[i]+=p[i].w1[j]*A[j] * (1 - p[i].Em);
	      }

	  //same for 2nd filter
	  if (p[i].taur2[j]>0)
	    {
	      input[i]+=p[i].w2[j]*pop[i].Isyn2[j];

	      if (p[i].taus2[j] > 0)
		{
		  pop[i].Isyn2[j] = pop[i].Xsyn2[j] + (pop[i].Isyn2[j] - pop[i].Xsyn2[j]) * Er2[i][j];
		  pop[i].Xsyn2[j] = A[j] + (pop[i].Xsyn2[j] - A[j]) * Es2[i][j];
		}
	      else
		  pop[i].Isyn2[j] = A[j] + (pop[i].Isyn2[j] - A[j]) * Es2[i][j];
	    }
	  else //zero-rise time
	    if (p[i].taus2[j] > 0)
	      {
		input[i]+=p[i].w2[j]*pop[i].Xsyn2[j];
		pop[i].Xsyn2[j] = A[j] + (pop[i].Xsyn2[j] - A[j]) * Es2[i][j];
	      }
	    else //delta current
	      input[i]+=p[i].w2[j]*A[j];
	}

    }
}

double cond_rate(double x,struct PopParametersGLM *p)
{
  if (p->deltaV > 0) return p->c * exp(x / p->deltaV);
  else //p->D==0, no noise
    {
      if (x < 0) return 0;
      else return 1./DT;
    }
}

static double get_Plam(double V, struct PopParametersGLM *p, double *G_old)
{
  if (V<p->Vmax)
    {
      if (V>=p->Vmin)
	{
	  //int x=(int)((V - p->Vmin) / p->dV);
	  double G = p->Gtable[(int)((V - p->Vmin) / p->dV)];
	  double Plam = 1 - *G_old * G;
	  //printf("V=%g x=%d lam=%g\n",V,x,lam);
	  *G_old = G;
	  return Plam;
	}
      else
	{
	  *G_old=1;
	  return 0.;
	}
    }
  else
    {
      *G_old = 0;
      return 1.;
    }
}

void update(struct PopVariablesGLM *pop, double *nmean, double input,gsl_rng *rng,struct PopParametersGLM *p, int mode)
{

  int i,k,r;
  int kref=(int)(p->tref/DT);
  double W=0, X=0, Y=0, Z=0, theta=p->vth;
  int end=p->end, K=p->K;
  double threshold;

  // first, determine non-refractory spiking prob.
  for (r=0;r < p->N_theta; r++)
    {
      pop->g[r] = pop->g[r]*p->E_theta2[r] + (1 - p->E_theta2[r]) * pop->n[end]/(p->N * DT);
      theta += p->E_theta[r] * pop->g[r];
    }

  double Pfree;
  pop->h = input + (pop->h - p->mu) * p->Em;
  Pfree=get_Plam(pop->h-theta,p,&(pop->Gfree));

  for (k=0; k<K; k++) X += pop->m0[k];

  double ztot,Plam,PLAM, mm;
  theta -= pop->n[end] * p->gamma_QR[0];//substracts effect of n[end] above, added again in 1st loop below, but ensures that threshold theta(t+DT|k=end) is correct

  switch(mode)
    {
    case GLM:
      for (k=end;k<=end-kref+K;k++)
	{
	  i=k%K;
	  threshold = pop->theta[k-end] + theta;
	  theta += pop->n[i] * p->gamma_QR[k-end];
	  Plam=get_Plam(pop->h-threshold,p,pop->G+i);

	  Z += pop->v[i];
	  mm = Plam * pop->m0[i];
	  Y += Plam * pop->v[i];
	  W += mm;
	  pop->v[i] = (1-Plam)*(1-Plam)*pop->v[i] + mm;
	  pop->m0[i] -= mm;
	}
      break;

    case GLIF:
      {
      for (k=end;k<=end-kref+K;k++)
	{
	  i=k%K;
	  threshold = pop->theta[k-end] + theta;
	  theta += pop->n[i] * p->gamma_QR[k-end];
	  pop->u[i] = input + (pop->u[i] - p->mu) * p->Em;//GLIF
	  Plam=get_Plam(pop->u[i]-threshold,p,pop->G+i);

	  Z += pop->v[i];
	  mm = Plam * pop->m0[i];
	  Y += Plam * pop->v[i];
	  W += mm;
	  pop->v[i] = (1-Plam)*(1-Plam)*pop->v[i] + mm;
	  pop->m0[i] -= mm;

	}
      }
      pop->u[end] = p->u_reset;//GLIF
      break;
    }

  ztot = Z + pop->z;
  if (ztot>0) PLAM=(Y+Pfree*pop->z)/ztot;
  else PLAM=0;
  //  printf("W=%g Z=%g z=%g Y=%g y=%g\n",W, Z,pop->z,Y,Pfree*pop->z);

  *nmean = W + Pfree * pop->x + PLAM * (p->N - X - pop->x);
  pop->n[end] = gsl_ran_binomial(rng, *nmean/p->N, p->N);

  pop->z=(1-Pfree)*(1-Pfree)*pop->z+Pfree*pop->x+pop->v[end];
  pop->x=(1-Pfree)*pop->x+pop->m0[end];

  pop->m0[end] = pop->n[end];
  pop->v[end] = 0.;
  pop->G[end] = 1.;

}

void simulate(int Nbin,double **A,double **rate,struct PopVariablesGLM pop[],gsl_rng *rng,struct PopParametersGLM p[],int Npop, int mode, int dispprog)
{
  double input[Npop],normfact[Npop], nmean[Npop], nmean_bin[Npop];
  int n_bin[Npop];
  int L,i;
  int l=0,k=0;
  L=(int)(DTBIN/DT+0.5);
  for (i=0;i<Npop;i++)
    {
      n_bin[i]=0;
      nmean_bin[i]=0;
      normfact[i]=1./(L*DT*p[i].N);
    }

  double **Es1=dmatrix(Npop,Npop);
  double **Es2=dmatrix(Npop,Npop);
  double **Er1=dmatrix(Npop,Npop);
  double **Er2=dmatrix(Npop,Npop);
  init_synaptic_filters(Es1,Es2,Er1,Er2,p,Npop);

  int dispcount=(int)(Nbin/100);

  //  clock_t start=clock();
  while (k<Nbin)
    {
      //      printf("Nbin=%d, k=%d l=%d\n",Nbin,k,l);
      get_inputs(input,k,Es1,Es2,Er1,Er2,p,pop,Npop);

      for (i=0;i<Npop;i++)
	{
	  update(pop+i,nmean+i,input[i],rng,p+i,mode);
      	  n_bin[i]+=pop[i].n[p[i].end];
      	  nmean_bin[i]+=nmean[i];
   	}
      l++;
      //           printf("nmeanbin=%g\n",nmean_bin[0]);
	   //      printf("\n");
      if (l>=L)
      	{
      	  for (i=0;i<Npop;i++)
	    {
	      A[i][k] = n_bin[i] * normfact[i];
	      rate[i][k] = nmean_bin[i] * normfact[i];

	      //if (i==0) printf("k=%d rate=%g\n",k,rate[i][k]);
	      //	      aa[i][k]=a[i];
      	      n_bin[i]=0;
	      nmean_bin[i]=0;
	    }
      	  l=0;
      	  k++;
      	}

      for (i=0;i<Npop;i++) p[i].end=(p[i].end + 1) % p[i].K;

      if (dispprog)
      	if ((k+1)%dispcount==0)
      	  {
      	    printf("%d%%  \r",(int)((k+1) * 100. / Nbin));
      	    fflush(stdout);
      	  }

    }

  //  printf("Execution time of mesoscopic sim: %g seconds, K=%d\n",(double)(clock()-start)/CLOCKS_PER_SEC,p[0].K);

  free_dmatrix(Es1);
  free_dmatrix(Es2);
  free_dmatrix(Er1);
  free_dmatrix(Er2);

}

double threshold_kernel(double t, struct PopParametersGLM *p)
{
  double theta=0;
  int r;
  for (r=0; r<p->N_theta;r++)
    if (t>=0)  theta += p->J_theta[r] / p->tau_theta[r] * exp(-t / p->tau_theta[r]);
  return theta;
}

void init_glm(struct PopParametersGLM p[],struct PopVariablesGLM pop[],int Npop, gsl_rng *rng, int mode)
//memory allocation and initialization of variables,
{
  int i,j,k,r, K;
  for (i=0;i<Npop;i++)
    {
      K=p[i].K;
      //      printf("i=%d K=%d\n",i,K);
      pop[i].n=uivector(K);
      pop[i].m0=dvector(K);
      pop[i].v=dvector(K);
      if (mode>=GLIF) pop[i].u=dvector(K);
      else pop[i].u=NULL;
      pop[i].Isyn1=dvector(Npop);
      pop[i].Isyn2=dvector(Npop);
      pop[i].Xsyn1=dvector(Npop);
      pop[i].Xsyn2=dvector(Npop);

      p[i].Em= exp(-DT / p[i].taum);
      p[i].Em2= exp(-0.5*DT / p[i].taum);
      p[i].end = 0;

      pop[i].theta=dvector(K);
      p[i].gamma_QR=dvector(K);
      pop[i].G=dvector(K);

      for (k=0;k<K;k++)
	{
	  /* if (k<K-1)  */
	  /*   { */
	  /*     pop[i].m0[k]=(double)(p[i].N)/(K-1); //normalization (the K-1 element is set zero below) */
	  /*     pop[i].n[k]=(int)(pop[i].m0[k]+1); //initializing with spike count corresponding to zero hazard */

	  /* pop[i].n[k]=(int)(100*DT*p[i].N+1); //initializing with spike count corresponding to 100 Hz */
	  /* pop[i].m0[k]=exp(-(K-k-1)*DT*100)*pop[i].n[k]; */
	  /* n+=pop[i].m0[k]; */
	  //if (i==0) printf("m0=%g n=%d\n", pop[i].m0[k], pop[i].n[k]);
	  /*   } */

	  pop[i].m0[k]=0;
	  pop[i].n[k]=0;
	  pop[i].v[k]=0;
	  if (mode>=GLIF) pop[i].u[k]=p[i].vth-20*gsl_rng_uniform(rng);

	  pop[i].theta[k]=threshold_kernel( (K-k-1) * DT, p+i);//shifted by DT to get filter at theta(s+DT)
	  p[i].gamma_QR[k] = p[i].deltaV * (1 - exp(-pop[i].theta[k] / p[i].deltaV)) / p[i].N;
	  //	  if (i==0) printf("i=%d k=%d gamma_QR=%g\n",i,k,p[i].gamma_QR[k]);

	  pop[i].G[k]=1.;
	}
      pop[i].Gfree=1.;

      pop[i].n[K-1]=p[i].N;
      pop[i].m0[K-1]=p[i].N;
      pop[i].z=0.;
      pop[i].x=0.;
      //pop[i].x=(p[i].N - n) * DT;
      /* printf("i=%d n=%g x/dt=%g\n",i,n,pop[i].x/DT); */

      for (j=0;j<Npop;j++) pop[i].Isyn1[j]=0;
      for (j=0;j<Npop;j++) pop[i].Isyn2[j]=0;
      for (j=0;j<Npop;j++) pop[i].Xsyn1[j]=0;
      for (j=0;j<Npop;j++) pop[i].Xsyn2[j]=0;

      pop[i].h=p[i].mu;

      //pre-calculating constant J_k*exp(-tau_rel/tau_k)
      pop[i].g=dvector(p[i].N_theta);
      for (r=0; r < p[i].N_theta; r++)
	pop[i].g[r] = pop[i].n[0]/(p[i].N * DT)/p[i].tau_theta[r];

      p[i].E_theta=dvector(p[i].N_theta);
      p[i].E_theta2=dvector(p[i].N_theta);
      //      printf("E_theta= ");
      for (r=0; r < p[i].N_theta; r++)
	{
	  p[i].E_theta[r] = p[i].J_theta[r] * exp(- K * DT / p[i].tau_theta[r]);
	  p[i].E_theta2[r] = exp(- DT / p[i].tau_theta[r]);
	  //	  printf("%g tautheta=%g",p[i].E_theta[r],p[i].tau_theta[r]);
	}
      //      printf("\n");

            //pre-computing firing probability as lookup table
      p[i].dV=p[i].deltaV/100;
      p[i].Vmin = p[i].deltaV * log(-log(1-PFIRE_MIN)/DT / p[i].c);
      p[i].Vmax = p[i].deltaV * log(-log(1-PFIRE_MAX)/DT / p[i].c);
      int L=(p[i].Vmax - p[i].Vmin)/p[i].dV;
      //      printf("Lookup tbl (popul %d): Use L=%d, Vmin=%g Vmax=%g dV=%g\n",i+1,L,p[i].Vmin,p[i].Vmax,p[i].dV);
      p[i].Gtable=dvector(L);
      for (k=0;k<L;k++)
	{
	  double V=p[i].Vmin + k*p[i].dV;
	  p[i].Gtable[k]= exp(-0.5 * p[i].c * exp(V/p[i].deltaV) * DT);
	}
    }
}

void get_history_size(struct PopParametersGLM p[],int Npop)
{
  int i;
  double tmax=20.;//sec

  for (i=0;i<Npop;i++)
    {
      int k=tmax/DT;
      int kmin=5*p[i].taum/DT;
      while ((threshold_kernel(k * DT, p+i) / p[i].deltaV < 0.1) && (k>kmin)) k--;
      //      printf("K=%d\n",k);
      if (k*DT<=p[i].delay)
	k=(int)(p[i].delay/DT)+1;
      //      printf("K=%d\n",k);
      if (k*DT<=p[i].tref)
	k=(int)(p[i].tref/DT)+1;
      p[i].K=k;
      //      p[i].K=20000;
      printf("Use K=%d bins for history of population %d (T=%g sec)\n",p[i].K,i+1,p[i].K*DT);
    }
}

void free_pop(struct PopVariablesGLM pop[],struct PopParametersGLM p[],int Npop, int mode)
{
  int i;
  for (i=0;i<Npop;i++)
    {
      free_uivector(pop[i].n);
      free_dvector(pop[i].m0);
      free_dvector(pop[i].v);
      if (mode>=GLIF) free_dvector(pop[i].u);
      free_dvector(pop[i].Isyn1);
      free_dvector(pop[i].Isyn2);
      free_dvector(pop[i].Xsyn1);
      free_dvector(pop[i].Xsyn2);

      free_dvector(pop[i].g);
      free_dvector(pop[i].theta);
      free_dvector(p[i].E_theta);
      free_dvector(p[i].E_theta2);
      free_dvector(p[i].gamma_QR);
      free_dvector(pop[i].G);
      free_dvector(p[i].Gtable);
    }
}

void get_psd_pop(double **SA,int Nbin,int Ntrials,struct PopParametersGLM neuron[],int Npop, int mode)
{
  gsl_rng *rng=gsl_rng_alloc(gsl_rng_taus2);
  unsigned long int seed;
  seed = random_seed();
  gsl_rng_set(rng,seed);
  //  gsl_rng_set (rng,(long)time(NULL));

  get_history_size(neuron,Npop);

  int i,j,n;

  struct PopVariablesGLM pop[Npop];
  init_glm(neuron,pop,Npop, rng, mode);
  double sum=0;
  for (i=0;i<neuron[0].K;i++)
    {
      sum+=pop[0].m0[i];
    }

  double **A, **rate;
  A=dmatrix(Npop,Nbin);
  rate=dmatrix(Npop,Nbin);

  simulate(Nbin,A,rate,pop,rng,neuron,Npop, mode, 0);

  double complex *AF[Npop];
  for (j=0;j<Npop;j++)
    {
      AF[j]=(double complex *)malloc(sizeof(double complex)*Nbin);
      SA[j]=(double *)malloc(sizeof(double)*(Nbin/2));//psd of A
      for (i=0;i<Nbin/2;i++) SA[j][i]=0;
    }

  fftw_plan plan=fftw_plan_dft_r2c_1d(Nbin,A[0],AF[0],FFTW_ESTIMATE);

  for (n=0;n<Ntrials;n++)
    {
      //      if (n%10==9)
	{
           printf("trial %d ",n+1);
           for (j=0;j<Npop;j++) printf("%g ",get_mean(A[j],Nbin));
           printf("\r");
	   fflush(stdout);
	}
	simulate(Nbin,A,rate,pop,rng,neuron,Npop,mode, 0);

      for (j=0;j<Npop;j++)
	{
	  fftw_execute_dft_r2c(plan,A[j],AF[j]);
	  for (i=1;i<Nbin/2+1;i++) SA[j][i-1]+=creal(AF[j][i]*conj(AF[j][i]))*DTBIN/Nbin;
	}
    }
  printf("trial %d ",n);
  for (j=0;j<Npop;j++) printf("%g ",get_mean(A[j],Nbin));
  printf("\n");

  for (j=0;j<Npop;j++)
    for (i=0;i<Nbin/2;i++)  SA[j][i]/=Ntrials;

  gsl_rng_free (rng);
  free_pop(pop,neuron,Npop, mode);

  free_dmatrix(A);
  free_dmatrix(rate);

  for (j=0;j<Npop;j++) free(AF[j]);
  fftw_destroy_plan(plan);
}

void get_trajectory(double **A,double **rate,int Nbin, int Npop,struct PopParametersGLM neuron[],double dt, double dtbin, int mode, int seed)
{

  gsl_rng *rng=gsl_rng_alloc(gsl_rng_taus2);
  gsl_rng_set (rng,(long)seed);
  /* printf("seed=%d\n",seed); */
  /* printf("rndnr=%g\n",gsl_rng_uniform(rng)); */
  DT=dt;
  DTBIN=dtbin;

  get_history_size(neuron,Npop);

  struct PopVariablesGLM pop[Npop];
  init_glm(neuron,pop,Npop, rng, mode);

  clock_t start=clock();
  simulate(Nbin,A,rate,pop,rng,neuron,Npop, mode, 1);
  double sim_t=(double)(clock()-start)/CLOCKS_PER_SEC;
  printf("Execution time of mesoscopic dynamics: %g seconds, %g s per biosecond\n",sim_t,sim_t/Nbin/DTBIN);

  int i;
  //for (i=0;i<20;i++) printf("rate=%g\n",rate[0][i]);
  for (i=0;i<Npop;i++) printf("mean rate of population %d: %g \n",i+1,get_mean(rate[i],Nbin));
  //  for (i=0;i<Nbin;i++) printf("%g\n",rate[0][i]);

  gsl_rng_free (rng);
  free_pop(pop,neuron,Npop, mode);
}

void init_population(struct PopParametersGLM p[], int Npop,double tref[], double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], double **signal, int N_theta[], double J_ref[], double *J_theta[], double *tau_theta[], double sigma[])
{

  int k,l;
  for (k=0;k<Npop;k++)
    {
      p[k].indx=k;
      p[k].tref=tref[k];
      p[k].taum=taum[k];
      p[k].mu=mu[k];
      p[k].c=c[k];
      p[k].deltaV=deltaV[k];
      p[k].delay=delay[k];
      p[k].vth=vth[k];
      p[k].u_reset = vreset[k];
      p[k].N=N[k];
      p[k].g=sqrt(1-exp(-2*DT/p[k].taum)) * sigma[k];
      p[k].g2=sqrt(1-exp(-DT/p[k].taum)) * sigma[k];
      p[k].Iext=signal[k];
      p[k].taus1=taus1[k];
      p[k].taus2=taus2[k];
      p[k].taur1=taur1[k];
      p[k].taur2=taur2[k];
      if (J_ref[k]==0)
	p[k].N_theta=N_theta[k];
      else
	p[k].N_theta=N_theta[k]+1; //putting exponential refractory kernel into threshold (theta kernel)

      p[k].J_theta=dvector(p[k].N_theta);
      p[k].tau_theta=dvector(p[k].N_theta);
      for (l=0;l<N_theta[k];l++)
	{
	  p[k].J_theta[l]=J_theta[k][l];
	  p[k].tau_theta[l]=tau_theta[k][l];
	  //	  printf("k=%d l=%d J=%g tau=%g\n",k,l,J_theta[k][l],tau_theta[k][l]);
	}
      if (J_ref[k]!=0.)
	{
	  p[k].J_theta[N_theta[k]] = J_ref[k]*taum[k];
	  p[k].tau_theta[N_theta[k]] = taum[k];
	}

      //effective synaptic weights
      p[k].w1=dvector(Npop);
      p[k].w2=dvector(Npop);
      for (l=0;l<Npop;l++)
	{
	  p[k].w1[l] = J[k][l] * p_conn[k][l] * N[l] * taum[k] * a1[k][l] / (a1[k][l]+a2[k][l]);
	  p[k].w2[l] = J[k][l] * p_conn[k][l] * N[l] * taum[k] * a2[k][l] / (a1[k][l]+a2[k][l]);
	  //printf("k=%d l=%d a1=%g w1=%g\n",k,l,a1[k][l],p[k].w1[l]);
	}
    }

}

void clean_population(struct PopParametersGLM p[], int Npop)
{
  int k;
  for (k=0;k<Npop;k++) free_dvector(p[k].w1);
  for (k=0;k<Npop;k++) free_dvector(p[k].w2);
  for (k=0;k<Npop;k++) free_dvector(p[k].J_theta);
  for (k=0;k<Npop;k++) free_dvector(p[k].tau_theta);
}

void get_psd_with_fullparameterlist(double **SA, int Nbin, int Ntrials, int Npop, double *tref, double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], int N_theta[], double Jref[], double *J_theta[], double *tau_theta[], double sigma[], double dt,double dtbin, int mode)
{
  double **signal=dmatrix(Npop,1);
  int i;
  for (i=0;i<Npop;i++)
    signal[i]=NULL;

  DT=dt;
  DTBIN=dtbin;

  struct PopParametersGLM p[Npop];
  init_population(p, Npop, tref, taum, taus1, taus2, taur1, taur2, a1, a2, mu, c, deltaV, delay, vth, vreset, N, J, p_conn, signal, N_theta, Jref, J_theta, tau_theta, sigma);

  get_psd_pop(SA, Nbin, Ntrials, p, Npop, mode);
  free_dmatrix(signal);
  clean_population(p,Npop);

}

 void get_psd_with_2D_arrays(int Nf, double SA[][Nf], int Ntrials, int Npop, double *tref, double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], int N_theta[], double Jref[], double J_theta[], double tau_theta[], double sigma[], double dt,double dtbin, int mode)
{
  //convert 2D array to double**
  double *SA_tmp[Npop], *J_theta_ptr[Npop], *tau_theta_ptr[Npop];
  int i,j;
  int Nbin=2*Nf;

  int indx=0;
  for (i=0;i<Npop;i++)
    {
      J_theta_ptr[i]=&(J_theta[indx]);
      tau_theta_ptr[i]=&(tau_theta[indx]);
      indx+=N_theta[i];
      /* if (N_theta[i]>0) */
      /* 	{ */
      /* 	  J_theta_ptr[i]=&(J_theta[indx]); */
      /* 	  tau_theta_ptr[i]=&(tau_theta[indx]); */
      /* 	  indx+=N_theta[i]; */
      /* 	} */
      /* else */
      /* 	{ */
      /* 	  J_theta_ptr[i]=NULL; */
      /* 	  tau_theta_ptr[i]=NULL; */
      /* 	} */
    }

  get_psd_with_fullparameterlist(SA_tmp, Nbin, Ntrials, Npop, tref, taum, taus1, taus2, taur1, taur2, a1, a2, mu, c, deltaV, delay, vth, vreset, N, J, p_conn, N_theta, Jref, J_theta_ptr, tau_theta_ptr, sigma, dt, dtbin, mode);

  for (j=0;j<Npop;j++)
    for (i=0;i<Nf;i++)
      SA[j][i]=SA_tmp[j][i];

  for (j=0;j<Npop;j++)
    free(SA_tmp[j]);

}

void get_trajectory_with_fullparameterlist(double **A, double **rate, int Nbin, int Npop, double *tref, double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], double **signal, int N_theta[], double Jref[], double *J_theta[], double *tau_theta[], double sigma[], double dt,double dtbin, int mode, int seed)
{
  DT=dt;
  DTBIN=dtbin;

  struct PopParametersGLM p[Npop];
  init_population(p, Npop, tref, taum, taus1, taus2, taur1, taur2, a1, a2, mu, c, deltaV, delay, vth, vreset, N, J, p_conn, signal, N_theta, Jref, J_theta, tau_theta, sigma);
  //  print_pop_parameters(p,Npop);

  get_trajectory(A, rate, Nbin, Npop, p, dt, dtbin, mode, seed);

  clean_population(p,Npop);
}

void get_trajectory_with_2D_arrays(int Nbin, double A[][Nbin], double rate[][Nbin], int Npop, double *tref, double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], double s[][Nbin], int N_theta[], double Jref[], double J_theta[], double tau_theta[], double sigma[], double dt,double dtbin, int mode, int seed)
{

  //convert 2D array to double**
  double *rate_ptr[Npop],*AA[Npop],*signal[Npop], *J_theta_ptr[Npop], *tau_theta_ptr[Npop];
  int i;

  for (i=0;i<Npop;i++)
    AA[i]=A[i];
  for (i=0;i<Npop;i++)
    rate_ptr[i]=rate[i];
  for (i=0;i<Npop;i++)
    signal[i]=s[i];

  int indx=0;
  for (i=0;i<Npop;i++)
    {
      if (N_theta[i]>0)
  	{
  	  J_theta_ptr[i]=&(J_theta[indx]);
  	  tau_theta_ptr[i]=&(tau_theta[indx]);
  	  indx+=N_theta[i];
  	}
      else
  	{
  	  J_theta_ptr[i]=NULL;
  	  tau_theta_ptr[i]=NULL;
  	}
    }

  get_trajectory_with_fullparameterlist(AA, rate_ptr, Nbin, Npop, tref, taum, taus1, taus2, taur1, taur2, a1, a2, mu, c, deltaV, delay, vth, vreset, N, J, p_conn, signal, N_theta, Jref, J_theta_ptr, tau_theta_ptr, sigma, dt, dtbin, mode, seed);

}

//===================================================================
// SRM Model
//===================================================================

void print_pop_parameters(struct PopParametersGLM p[], int Npop)
{

  int k,l;
  printf("\n");
  for (k=0;k<Npop;k++)
    {
      printf("POPULATION %d\n",k+1);
      printf("tref=%g\n",p[k].tref);
      printf("taum=%g\n",p[k].taum);
      printf("mu=%g\n",p[k].mu);
      printf("c=%g\n",p[k].c);
      printf("DeltaV=%g\n",p[k].deltaV);
      printf("delay=%g\n",p[k].delay);
      printf("vth=%g\n",p[k].vth);
      printf("vreset=%g\n",p[k].u_reset);
      printf("N=%d\n",p[k].N);
      printf("Ntheta=%d\n",p[k].N_theta);
      for (l=0;l<p[k].N_theta;l++)
	{
	  printf("Jtheta=%g\n",p[k].J_theta[l]);
	  printf("tautheta=%g\n",p[k].tau_theta[l]);
	}
      for (l=0;l<Npop;l++)
	{
	  printf("pop%d to pop%d:\n",l+1,k+1);
	  printf("   w1=%g\n",p[k].w1[l]);
	  printf("   w2=%g\n",p[k].w2[l]);
	  printf("   taus1=%g taur1=%g\n",p[k].taus1[l],p[k].taur1[l]);
	  printf("   taus2=%g taur2=%g\n",p[k].taus2[l],p[k].taur2[l]);
	}
      printf("\n");
    }
  printf("DT=%g DTBIN=%g\n",DT,DTBIN);
}

double get_mean(double *x,int n)
{
  int i;
  double m=0;
  if (n>200) //discard initial transient
    {
      for (i=200;i<n;i++) m+=x[i];
      return m/(n-200);
    }
  else
    {
      for (i=0;i<n;i++) m+=x[i];
      return m/n;
    }
}

void print_matrix1(int N,int M, double A[][M])
{
  int i,j;
  for (i=0;i<N;i++)
    {
      for (j=0;j<M;j++)
	printf("%g ",A[i][j]);
      printf("\n");
    }
}

void print_matrix2(double **A,int N,int M)
{
  int i,j;
  for (i=0;i<N;i++)
    {
      for (j=0;j<M;j++)
	printf("%g ",A[i][j]);
      printf("\n");
    }
}

void print_matrix3(int N,int M, double *A[N])
{
  int i,j;
  for (i=0;i<N;i++)
    {
      for (j=0;j<M;j++)
	printf("%g ",A[i][j]);
      printf("\n");
    }
}

/* unsigned long int random_seed() */
/* { */

/*  unsigned int seed; */
/*  FILE *devrandom; */

/*  if ((devrandom = fopen("/dev/random","r")) == NULL) { */
/*    fprintf(stderr,"Cannot open /dev/random, setting seed to 0\n"); */
/*    seed = 0; */
/*  } else { */
/*    fread(&seed,sizeof(seed),1,devrandom); */
/*    if(verbose == D_SEED) printf("Got seed %u from /dev/random\n",seed); */
/*    fclose(devrandom); */
/*  } */

/*  return(seed); */

/* } */

#include <sys/time.h>
unsigned long int random_seed()
{

 unsigned int seed;
 struct timeval tv;
 FILE *devrandom;

 if ((devrandom = fopen("/dev/urandom","r")) == NULL) {
   gettimeofday(&tv,0);
   seed = tv.tv_sec + tv.tv_usec;
 } else {
   fread(&seed,sizeof(seed),1,devrandom);
   fclose(devrandom);
 }

 return(seed);

}


/**
 *
 * @file qwrapper_dgemm_tile.c
 *
 *  PLASMA core_blas quark wrapper
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mark Gates
 * @date 2010-11-15
 * @generated d Tue Jan  7 11:44:59 2014
 *
 **/
#include <cblas.h>
#include "common.h"

/***************************************************************************//**
 *
 * Version of dgemm for tile storage, to avoid dependency problem when
 * computations are done within the tile. alpha and beta are passed as
 * pointers so they can depend on runtime values.
 *
 * @param[in] Alock
 *          Pointer to tile owning submatrix A.
 *
 * @param[in] Block
 *          Pointer to tile owning submatrix B.
 *
 * @param[in] Clock
 *          Pointer to tile owning submatrix C.
 *
 **/
void QUARK_CORE_dgemm_tile(Quark *quark, Quark_Task_Flags *task_flags,
                           PLASMA_enum transA, PLASMA_enum transB,
                           int m, int n, int k, int nb,
                           const double *alpha, const double *A, int lda,
                                                            const double *B, int ldb,
                           const double *beta,        double *C, int ldc,
                           const double *Alock,
                           const double *Block,
                           const double *Clock)
{
    DAG_CORE_GEMM;
    QUARK_Insert_Task(quark, CORE_dgemm_tile_quark, task_flags,
        sizeof(PLASMA_enum),              &transA, VALUE,
        sizeof(PLASMA_enum),              &transB, VALUE,
        sizeof(int),                      &m,      VALUE,
        sizeof(int),                      &n,      VALUE,
        sizeof(int),                      &k,      VALUE,
        sizeof(double),       alpha,           INPUT,
        sizeof(double)*nb*nb, A,               NODEP,          /* input; see Alock */
        sizeof(int),                      &lda,    VALUE,
        sizeof(double)*nb*nb, B,               NODEP,          /* input; see Block */
        sizeof(int),                      &ldb,    VALUE,
        sizeof(double),       beta,            INPUT,
        sizeof(double)*nb*nb, C,                       NODEP,  /* inout; see Clock */
        sizeof(int),                      &ldc,    VALUE,
        sizeof(double)*nb*nb, Alock,           INPUT,
        sizeof(double)*nb,    Block,           INPUT,
        sizeof(double)*nb,    Clock,                   INOUT,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_dgemm_tile_quark = PCORE_dgemm_tile_quark
#define CORE_dgemm_tile_quark PCORE_dgemm_tile_quark
#endif
void CORE_dgemm_tile_quark(Quark *quark)
{
    PLASMA_enum transA, transB;
    int m, n, k, lda, ldb, ldc;
    const double *alpha, *beta;
    const double *A, *B;
    double *C;

    quark_unpack_args_13( quark, transA, transB, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc );
    cblas_dgemm(
        CblasColMajor,
        (CBLAS_TRANSPOSE)transA, (CBLAS_TRANSPOSE)transB,
        m, n, k,
        (*alpha), A, lda,
                             B, ldb,
        (*beta),  C, ldc );
}