/* specfunc/legendre_poly.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* Author:  G. Jungman */

#include <config.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include "gsl_sf_bessel.h"
#include "gsl_sf_exp.h"
#include "gsl_sf_gamma.h"
#include "gsl_sf_log.h"
#include "gsl_sf_pow_int.h"
#include "gsl_sf_legendre.h"

#include "error.h"

/*-*-*-*-*-*-*-*-*-*-*-* Functions with Error Codes *-*-*-*-*-*-*-*-*-*-*-*/

int
gsl_sf_legendre_P1_e(double x, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  {
    result->val = x;
    result->err = 0.0;
    return GSL_SUCCESS;
  }
}

int
gsl_sf_legendre_P2_e(double x, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  {
    result->val = 0.5*(3.0*x*x - 1.0);
    result->err = GSL_DBL_EPSILON * (fabs(3.0*x*x) + 1.0);
    return GSL_SUCCESS;
  }
}

int
gsl_sf_legendre_P3_e(double x, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  {
    result->val = 0.5*x*(5.0*x*x - 3.0);
    result->err = GSL_DBL_EPSILON * (fabs(result->val) + 0.5 * fabs(x) * (fabs(5.0*x*x) + 3.0));
    return GSL_SUCCESS;
  }
}

int
gsl_sf_legendre_Pl_e(const int l, const double x, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  if(l < 0 || x < -1.0 || x > 1.0) {
    DOMAIN_ERROR(result);
  }
  else if(l == 0) {
    result->val = 1.0;
    result->err = 0.0;
    return GSL_SUCCESS;
  }
  else if(l == 1) {
    result->val = x;
    result->err = 0.0;
    return GSL_SUCCESS;
  }
  else if(l == 2) {
    result->val = 0.5 * (3.0*x*x - 1.0);
    result->err = 3.0 * GSL_DBL_EPSILON * fabs(result->val);
    return GSL_SUCCESS;
  }
  else if(x == 1.0) {
    result->val = 1.0;
    result->err = 0.0;
    return GSL_SUCCESS;
  }
  else if(x == -1.0) {
    result->val = ( GSL_IS_ODD(l) ? -1.0 : 1.0 );
    result->err = 0.0;
    return GSL_SUCCESS;
  }
  else if(l < 100000) {
    /* Compute by upward recurrence on l.
     */
    double p_mm   = 1.0;     /* P_0(x) */
    double p_mmp1 = x;	    /* P_1(x) */
    double p_ell = p_mmp1;
    int ell;

    for(ell=2; ell <= l; ell++){
      p_ell = (x*(2*ell-1)*p_mmp1 - (ell-1)*p_mm) / ell;
      p_mm = p_mmp1;
      p_mmp1 = p_ell;
    }

    result->val = p_ell;
    result->err = (0.5 * ell + 1.0) * GSL_DBL_EPSILON * fabs(p_ell);
    return GSL_SUCCESS;
  }
  else {
    /* Asymptotic expansion.
     * [Olver, p. 473]
     */
    double u  = l + 0.5;
    double th = acos(x);
    gsl_sf_result J0;
    gsl_sf_result Jm1;
    int stat_J0  = gsl_sf_bessel_J0_e(u*th, &J0);
    int stat_Jm1 = gsl_sf_bessel_Jn_e(-1, u*th, &Jm1);
    double pre;
    double B00;
    double c1;

    /* B00 = 1/8 (1 - th cot(th) / th^2
     * pre = sqrt(th/sin(th))
     */
    if(th < GSL_ROOT4_DBL_EPSILON) {
      B00 = (1.0 + th*th/15.0)/24.0;
      pre = 1.0 + th*th/12.0;
    }
    else {
      double sin_th = sqrt(1.0 - x*x);
      double cot_th = x / sin_th;
      B00 = 1.0/8.0 * (1.0 - th * cot_th) / (th*th);
      pre = sqrt(th/sin_th);
    }

    c1 = th/u * B00;

    result->val  = pre * (J0.val + c1 * Jm1.val);
    result->err  = pre * (J0.err + fabs(c1) * Jm1.err);
    result->err += GSL_SQRT_DBL_EPSILON * fabs(result->val);

    return GSL_ERROR_SELECT_2(stat_J0, stat_Jm1);
  }
}

int
gsl_sf_legendre_Pl_array(const int lmax, const double x, double * result_array)
{
  /* CHECK_POINTER(result_array) */

  if(lmax < 0 || x < -1.0 || x > 1.0) {
    GSL_ERROR ("domain error", GSL_EDOM);
  }
  else if(lmax == 0) {
    result_array[0] = 1.0;
    return GSL_SUCCESS;
  }
  else if(lmax == 1) {
    result_array[0] = 1.0;
    result_array[1] = x;
    return GSL_SUCCESS;
  }
  else {
    double p_mm   = 1.0;    /* P_0(x) */
    double p_mmp1 = x;	    /* P_1(x) */
    double p_ell = p_mmp1;
    int ell;

    result_array[0] = 1.0;
    result_array[1] = x;

    for(ell=2; ell <= lmax; ell++){
      p_ell = (x*(2*ell-1)*p_mmp1 - (ell-1)*p_mm) / ell;
      p_mm = p_mmp1;
      p_mmp1 = p_ell;
      result_array[ell] = p_ell;
    }

    return GSL_SUCCESS;
  }
}

int
gsl_sf_legendre_Plm_e(const int l, const int m, const double x, gsl_sf_result * result)
{
  /* If l is large and m is large, then we have to worry
   * about overflow. Calculate an approximate exponent which
   * measures the normalization of this thing.
   */
  double dif = l-m;
  double sum = l+m;
  double exp_check = 0.5 * log(2.0*l+1.0)
                   + 0.5 * dif * (log(dif)-1.0)
                   - 0.5 * sum * (log(sum)-1.0);

  /* CHECK_POINTER(result) */

  if(m < 0 || l < m || x < -1.0 || x > 1.0) {
    DOMAIN_ERROR(result);
  }
  else if(exp_check < GSL_LOG_DBL_MIN + 10.0){
    /* Bail out. */
    OVERFLOW_ERROR(result);
  }
  else {
    /* Account for the error due to the
     * representation of 1-x.
     */
    const double err_amp = 1.0 / (GSL_DBL_EPSILON + fabs(1.0-fabs(x)));

    double p_mm;     /* P_m^m(x) */
    double p_mmp1;   /* P_{m+1}^m(x) */

    /* Calculate P_m^m from the analytic result:
     *          P_m^m(x) = (-1)^m (2m-1)!! (1-x^2)^(m/2) , m > 0
     */
    p_mm = 1.0;
    if(m > 0){
      double root_factor = sqrt(1.0-x)*sqrt(1.0+x);
      double fact_coeff = 1.0;
      int i;
      for(i=1; i<=m; i++) {
        p_mm *= -fact_coeff * root_factor;
        fact_coeff += 2.0;
      }
    }

    /* Calculate P_{m+1}^m. */
    p_mmp1 = x * (2*m + 1) * p_mm;

    if(l == m){
      result->val = p_mm;
      result->err = err_amp * 2.0 * GSL_DBL_EPSILON * fabs(p_mm);
      return GSL_SUCCESS;
    }
    else if(l == m + 1) {
      result->val = p_mmp1;
      result->err = err_amp * 2.0 * GSL_DBL_EPSILON * fabs(p_mmp1);
      return GSL_SUCCESS;
    }
    else{
      double p_ell = 0.0;
      int ell;

      /* Compute P_l^m, l > m+1 by upward recurrence on l. */
      for(ell=m+2; ell <= l; ell++){
        p_ell = (x*(2*ell-1)*p_mmp1 - (ell+m-1)*p_mm) / (ell-m);
        p_mm = p_mmp1;
        p_mmp1 = p_ell;
      }

      result->val = p_ell;
      result->err = err_amp * (0.5*(l-m) + 1.0) * GSL_DBL_EPSILON * fabs(p_ell);

      return GSL_SUCCESS;
    }
  }
}

int
gsl_sf_legendre_Plm_array(const int lmax, const int m, const double x, double * result_array)
{
  /* If l is large and m is large, then we have to worry
   * about overflow. Calculate an approximate exponent which
   * measures the normalization of this thing.
   */
  double dif = lmax-m;
  double sum = lmax+m;
  double exp_check = 0.5 * log(2.0*lmax+1.0)
                     + 0.5 * dif * (log(dif)-1.0)
                     - 0.5 * sum * (log(sum)-1.0);

  /* CHECK_POINTER(result_array) */

  if(m < 0 || lmax < m || x < -1.0 || x > 1.0) {
    GSL_ERROR ("error", GSL_EDOM);
  }
  else if(m > 0 && (x == 1.0 || x == -1.0)) {
    int ell;
    for(ell=m; ell<=lmax; ell++) result_array[ell-m] = 0.0;
    return GSL_SUCCESS;
  }
  else if(exp_check < GSL_LOG_DBL_MIN + 10.0){
    /* Bail out.
     */
    GSL_ERROR ("error", GSL_EOVRFLW);
  }
  else {
    double p_mm;                 /* P_m^m(x)     */
    double p_mmp1;               /* P_{m+1}^m(x) */

    /* Calculate P_m^m from the analytic result:
     *          P_m^m(x) = (-1)^m (2m-1)!! (1-x^2)^(m/2) , m > 0
     */
    p_mm = 1.0;
    if(m > 0){
      double root_factor = sqrt(1.0-x)*sqrt(1.0+x);
      double fact_coeff = 1.0;
      int i;
      for(i=1; i<=m; i++){
        p_mm *= -fact_coeff * root_factor;
        fact_coeff += 2.0;
      }
    }

    /* Calculate P_{m+1}^m. */
    p_mmp1 = x * (2*m + 1) * p_mm;

    if(lmax == m){
      result_array[0] = p_mm;
      return GSL_SUCCESS;
    }
    else if(lmax == m + 1) {
      result_array[0] = p_mm;
      result_array[1] = p_mmp1;
      return GSL_SUCCESS;
    }
    else{
      double p_ell;
      int ell;

      result_array[0] = p_mm;
      result_array[1] = p_mmp1;

      /* Compute P_l^m, l >= m+2, by upward recursion on l. */
      for(ell=m+2; ell <= lmax; ell++){
        p_ell = (x*(2*ell-1)*p_mmp1 - (ell+m-1)*p_mm) / (ell-m);
        p_mm = p_mmp1;
        p_mmp1 = p_ell;
        result_array[ell-m] = p_ell;
      }

      return GSL_SUCCESS;
    }
  }
}

int
gsl_sf_legendre_sphPlm_e(const int l, int m, const double x, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  if(m < 0 || l < m || x < -1.0 || x > 1.0) {
    DOMAIN_ERROR(result);
  }
  else if(m == 0) {
    gsl_sf_result P;
    int stat_P = gsl_sf_legendre_Pl_e(l, x, &P);
    double pre = sqrt((2.0*l + 1.0)/(4.0*M_PI));
    result->val  = pre * P.val;
    result->err  = pre * P.err;
    result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val);
    return stat_P;
  }
  else if(x == 1.0 || x == -1.0) {
    /* m > 0 here */
    result->val = 0.0;
    result->err = 0.0;
    return GSL_SUCCESS;
  }
  else {
    /* m > 0 and |x| < 1 here */

    /* Starting value for recursion.
     * Y_m^m(x) = sqrt( (2m+1)/(4pi m) gamma(m+1/2)/gamma(m) ) (-1)^m (1-x^2)^(m/2) / pi^(1/4)
     */
    gsl_sf_result lncirc;
    gsl_sf_result lnpoch;
    double lnpre_val;
    double lnpre_err;
    gsl_sf_result ex_pre;
    double sr;
    const double sgn = ( GSL_IS_ODD(m) ? -1.0 : 1.0);
    const double y_mmp1_factor = x * sqrt(2.0*m + 3.0);
    double y_mm, y_mm_err;
    double y_mmp1;
    gsl_sf_log_1plusx_e(-x*x, &lncirc);
    gsl_sf_lnpoch_e(m, 0.5, &lnpoch);  /* Gamma(m+1/2)/Gamma(m) */
    lnpre_val = -0.25*M_LNPI + 0.5 * (lnpoch.val + m*lncirc.val);
    lnpre_err = 0.25*M_LNPI*GSL_DBL_EPSILON + 0.5 * (lnpoch.err + fabs(m)*lncirc.err);
    gsl_sf_exp_err_e(lnpre_val, lnpre_err, &ex_pre);
    sr    = sqrt((2.0+1.0/m)/(4.0*M_PI));
    y_mm   = sgn * sr * ex_pre.val;
    y_mmp1 = y_mmp1_factor * y_mm;
    y_mm_err  = 2.0 * GSL_DBL_EPSILON * fabs(y_mm) + sr * ex_pre.err;
    y_mm_err *= 1.0 + 1.0/(GSL_DBL_EPSILON + fabs(1.0-x));

    if(l == m){
      result->val  = y_mm;
      result->err  = y_mm_err;
      result->err += 2.0 * GSL_DBL_EPSILON * fabs(y_mm);
      return GSL_SUCCESS;
    }
    else if(l == m + 1) {
      result->val  = y_mmp1;
      result->err  = fabs(y_mmp1_factor) * y_mm_err;
      result->err += 2.0 * GSL_DBL_EPSILON * fabs(y_mmp1);
      return GSL_SUCCESS;
    }
    else{
      double y_ell = 0.0;
      int ell;

      /* Compute Y_l^m, l > m+1, upward recursion on l. */
      for(ell=m+2; ell <= l; ell++){
        const double rat1 = (double)(ell-m)/(double)(ell+m);
	const double rat2 = (ell-m-1.0)/(ell+m-1.0);
        const double factor1 = sqrt(rat1*(2*ell+1)*(2*ell-1));
        const double factor2 = sqrt(rat1*rat2*(2*ell+1)/(2*ell-3));
        y_ell = (x*y_mmp1*factor1 - (ell+m-1)*y_mm*factor2) / (ell-m);
        y_mm   = y_mmp1;
        y_mmp1 = y_ell;
      }

      result->val  = y_ell;
      result->err  = (0.5*(l-m) + 1.0) * GSL_DBL_EPSILON * fabs(y_ell);
      result->err += fabs(y_mm_err/y_mm) * fabs(y_ell);

      return GSL_SUCCESS;
    }
  }
}

int
gsl_sf_legendre_sphPlm_array(const int lmax, int m, const double x, double * result_array)
{
  /* CHECK_POINTER(result_array) */

  if(m < 0 || lmax < m || x < -1.0 || x > 1.0) {
    GSL_ERROR ("error", GSL_EDOM);
  }
  else if(m > 0 && (x == 1.0 || x == -1.0)) {
    int ell;
    for(ell=m; ell<=lmax; ell++) result_array[ell-m] = 0.0;
    return GSL_SUCCESS;
  }
  else {
    double y_mm;
    double y_mmp1;

    if(m == 0) {
      y_mm   = 0.5/M_SQRTPI;          /* Y00 = 1/sqrt(4pi) */
      y_mmp1 = x * M_SQRT3 * y_mm;
    }
    else {
      /* |x| < 1 here */

      gsl_sf_result lncirc;
      gsl_sf_result lnpoch;
      double lnpre;
      const double sgn = ( GSL_IS_ODD(m) ? -1.0 : 1.0);
      gsl_sf_log_1plusx_e(-x*x, &lncirc);
      gsl_sf_lnpoch_e(m, 0.5, &lnpoch);  /* Gamma(m+1/2)/Gamma(m) */
      lnpre = -0.25*M_LNPI + 0.5 * (lnpoch.val + m*lncirc.val);
      y_mm   = sqrt((2.0+1.0/m)/(4.0*M_PI)) * sgn * exp(lnpre);
      y_mmp1 = x * sqrt(2.0*m + 3.0) * y_mm;
    }

    if(lmax == m){
      result_array[0] = y_mm;
      return GSL_SUCCESS;
    }
    else if(lmax == m + 1) {
      result_array[0] = y_mm;
      result_array[1] = y_mmp1;
      return GSL_SUCCESS;
    }
    else{
      double y_ell;
      int ell;

      result_array[0] = y_mm;
      result_array[1] = y_mmp1;

      /* Compute Y_l^m, l > m+1, upward recursion on l. */
      for(ell=m+2; ell <= lmax; ell++){
        const double rat1 = (double)(ell-m)/(double)(ell+m);
	const double rat2 = (ell-m-1.0)/(ell+m-1.0);
        const double factor1 = sqrt(rat1*(2*ell+1)*(2*ell-1));
        const double factor2 = sqrt(rat1*rat2*(2*ell+1)/(2*ell-3));
        y_ell = (x*y_mmp1*factor1 - (ell+m-1)*y_mm*factor2) / (ell-m);
        y_mm   = y_mmp1;
        y_mmp1 = y_ell;
	result_array[ell-m] = y_ell;
      }
    }

    return GSL_SUCCESS;
  }
}

#ifndef HIDE_INLINE_STATIC
int
gsl_sf_legendre_array_size(const int lmax, const int m)
{
  return lmax-m+1;
}
#endif

/*-*-*-*-*-*-*-*-*-* Functions w/ Natural Prototypes *-*-*-*-*-*-*-*-*-*-*/

#include "eval.h"

double gsl_sf_legendre_P1(const double x)
{
  EVAL_RESULT(gsl_sf_legendre_P1_e(x, &result));
}

double gsl_sf_legendre_P2(const double x)
{
  EVAL_RESULT(gsl_sf_legendre_P2_e(x, &result));
}

double gsl_sf_legendre_P3(const double x)
{
  EVAL_RESULT(gsl_sf_legendre_P3_e(x, &result));
}

double gsl_sf_legendre_Pl(const int l, const double x)
{
  EVAL_RESULT(gsl_sf_legendre_Pl_e(l, x, &result));
}

double gsl_sf_legendre_Plm(const int l, const int m, const double x)
{
  EVAL_RESULT(gsl_sf_legendre_Plm_e(l, m, x, &result));
}

double gsl_sf_legendre_sphPlm(const int l, const int m, const double x)
{
  EVAL_RESULT(gsl_sf_legendre_sphPlm_e(l, m, x, &result));
}


#ifndef alphas_H_INCLUDED
#define alphas_H_INCLUDED

//for fixed alpha
//#define g2 (4.6)

#include <gsl/gsl_spline.h>

//http://pdg.lbl.gov/2010/reviews/rpp2010-rev-qcd.pdf

#define Mz 91.1876
#define LamQCD 0.217
//fixednf = 0 for nf(mu)
#define fixednf 3
//extrapolates below mu0*LamQCD to value of (mu0-1.0)*LamQCD at mu=0
#define mu0 4.
#define pi 3.14159265359
#define Nc (3.)
#define Cf ((Nc*Nc-1.)/(2.*Nc))

void TabulateAlpha();
double alpha(double mu);

double nf(double mu);
double alpha_b0(double nf);
double alpha_b1(double nf);
double alpha_b2(double nf);
double alpha_s0(double mu);
double alpha_s1(double mu);
double alpha_s2(double mu);

gsl_interp_accel *accAlpha;
gsl_spline *Alpha;

#endif


#ifndef __CCL_CONSTANTS_H_INCLUDED__
#define __CCL_CONSTANTS_H_INCLUDED__

#include <gsl/gsl_const_mksa.h>

//Spline types
#define A_SPLINE_TYPE gsl_interp_akima
#define K_SPLINE_TYPE gsl_interp_akima
#define L_SPLINE_TYPE gsl_interp_akima
#define M_SPLINE_TYPE gsl_interp_akima
#define D_SPLINE_TYPE gsl_interp_akima
#define PNL_SPLINE_TYPE gsl_interp2d_bicubic
#define PLIN_SPLINE_TYPE gsl_interp2d_bicubic
#define CORR_SPLINE_TYPE gsl_interp_akima

/** @file */

#ifndef M_PI
/**
 *  PI (in case it's not defined from math.h)
*/
#define M_PI 3.14159265358979323846
#endif

/**
 *  k pivot. These are in units of Mpc (no factor of h)
*/
#define K_PIVOT 0.05

/**
 * Lightspeed / H0 in units of Mpc/h (from CODATA 2014)
 */
#define CLIGHT_HMPC 2997.92458 //H0^-1 in Mpc/h

/**
 * Newton's gravitational constant in units of m^3/Kg/s^2
 */
//#define GNEWT 6.6738e-11  /(from PDG 2013) in m^3/Kg/s^2
//#define GNEWT 6.67428e-11 // CLASS VALUE
#define GNEWT 6.67408e-11 // from CODATA 2014

/**
 * Solar mass in units of kg (from GSL)
 */
//#define SOLAR_MASS GSL_CONST_MKSA_SOLAR_MASS
//#define SOLAR_MASS 1.9885e30 //(from PDG 2015) in Kg
#define SOLAR_MASS 1.9884754153381438e+30 //from IAU 2015
/**
 * Mpc to meters (from PDG 2013)
 */
#define MPC_TO_METER 3.08567758149e22

/**
 * pc to meters (from PDG 2013)
 */
#define PC_TO_METER 3.08567758149e16

/**
 * Rho critical in units of M_sun/h / (Mpc/h)^3
 */
#define RHO_CRITICAL ((3*100*100)/(8*M_PI*GNEWT)) * (1000*1000*MPC_TO_METER/SOLAR_MASS)

/**
 * Boltzmann constant in units of J/K
*/
//#define KBOLTZ  GSL_CONST_MKSA_BOLTZMANN
#define KBOLTZ 1.38064852e-23 //from CODATA 2014

/**
 * Stefan-Boltzmann constant in units of kg/s^3 / K^4
 */
//#define STBOLTZ GSL_CONST_MKSA_STEFAN_BOLTZMANN_CONSTANT
#define STBOLTZ 5.670367e-8 //from CODATA 2014
/**
 * Planck's constant in units kg m^2 / s
 */
//#define HPLANCK  GSL_CONST_MKSA_PLANCKS_CONSTANT_H
#define HPLANCK 6.626070040e-34 //from CODATA 2014

/**
 * The speed of light in m/s
 */
//#define CLIGHT   GSL_CONST_MKSA_SPEED_OF_LIGHT
#define CLIGHT 299792458.0 //from CODATA 2014

/**
 * Electron volt to Joules convestion
 */
//#define EV_IN_J  GSL_CONST_MKSA_ELECTRON_VOLT
#define EV_IN_J 1.6021766208e-19  //from CODATA 2014

/**
 * Temperature of the CMB in K
 */
#define TCMB 2.725
//#define TCMB 2.7255 // CLASS value

/**
 * T_ncdm, as taken from CLASS, explanatory.ini
 */
#define TNCDM 0.71611

/**
 * neutrino mass splitting differences
 * See Lesgourgues and Pastor, 2012 for these values.
 * Adv. High Energy Phys. 2012 (2012) 608515,
 * arXiv:1212.6154, page 13
*/
#define DELTAM12_sq 7.62E-5
#define DELTAM13_sq_pos 2.55E-3
#define DELTAM13_sq_neg -2.43E-3

//Precision parameters
/**
 * Default relative precision if not otherwise specified
 */
#define GSL_EPSREL 1E-4

/**
 * Default number of iterations for integration and root-finding if not otherwise
 * specified
 */
#define GSL_N_ITERATION 1000

/**
 * Default number of Gauss-Kronrod points in QAG integration if not otherwise
 * specified
 */
#define GSL_INTEGRATION_GAUSS_KRONROD_POINTS GSL_INTEG_GAUSS41

/**
 * Absolute precision in neutrino root finding
 */
#define GSL_EPSABS_NU 1E-7

/**
 * Relative precision in neutrino root finding
 */
#define GSL_EPSREL_NU 1E-7

/**
 * Number of iterations for neutrino root finding
 */
#define GSL_N_ITERATION_NU 1000

/**
 * Relative precision in sigma_R calculations
 */
#define GSL_EPSREL_SIGMAR 1E-5

/**
 * Relative precision in distance calculations
 */
#define GSL_EPSREL_DIST 1E-6

/**
 * Relative precision in growth calculations
 */
#define GSL_EPSREL_GROWTH 1E-6

/**
 * Relative precision in dNdz calculations
 */
#define GSL_EPSREL_DNDZ 1E-6

/**
 * Absolute precision in growth calculations
 */
#define EPS_SCALEFAC_GROWTH 1E-6

#endif


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

/* Author:  G. Jungman */

#ifndef __GSL_SF_FERMI_DIRAC_H__
#define __GSL_SF_FERMI_DIRAC_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <gsl/gsl_sf_result.h>

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

__BEGIN_DECLS

/* Complete Fermi-Dirac Integrals:
 *
 *  F_j(x)   := 1/Gamma[j+1] Integral[ t^j /(Exp[t-x] + 1), {t,0,Infinity}]
 *
 *
 * Incomplete Fermi-Dirac Integrals:
 *
 *  F_j(x,b) := 1/Gamma[j+1] Integral[ t^j /(Exp[t-x] + 1), {t,b,Infinity}]
 */

/* Complete integral F_{-1}(x) = e^x / (1 + e^x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_FUN int     gsl_sf_fermi_dirac_m1_e(const double x, gsl_sf_result * result);
GSL_FUN double     gsl_sf_fermi_dirac_m1(const double x);

/* Complete integral F_0(x) = ln(1 + e^x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_FUN int     gsl_sf_fermi_dirac_0_e(const double x, gsl_sf_result * result);
GSL_FUN double     gsl_sf_fermi_dirac_0(const double x);

/* Complete integral F_1(x)
 *
 * exceptions: GSL_EUNDRFLW, GSL_EOVRFLW
 */
GSL_FUN int     gsl_sf_fermi_dirac_1_e(const double x, gsl_sf_result * result);
GSL_FUN double     gsl_sf_fermi_dirac_1(const double x);

/* Complete integral F_2(x)
 *
 * exceptions: GSL_EUNDRFLW, GSL_EOVRFLW
 */
GSL_FUN int     gsl_sf_fermi_dirac_2_e(const double x, gsl_sf_result * result);
GSL_FUN double     gsl_sf_fermi_dirac_2(const double x);

/* Complete integral F_j(x)
 * for integer j
 *
 * exceptions: GSL_EUNDRFLW, GSL_EOVRFLW
 */
GSL_FUN int     gsl_sf_fermi_dirac_int_e(const int j, const double x, gsl_sf_result * result);
GSL_FUN double     gsl_sf_fermi_dirac_int(const int j, const double x);

/* Complete integral F_{-1/2}(x)
 *
 * exceptions: GSL_EUNDRFLW, GSL_EOVRFLW
 */
GSL_FUN int     gsl_sf_fermi_dirac_mhalf_e(const double x, gsl_sf_result * result);
GSL_FUN double     gsl_sf_fermi_dirac_mhalf(const double x);

/* Complete integral F_{1/2}(x)
 *
 * exceptions: GSL_EUNDRFLW, GSL_EOVRFLW
 */
GSL_FUN int     gsl_sf_fermi_dirac_half_e(const double x, gsl_sf_result * result);
GSL_FUN double     gsl_sf_fermi_dirac_half(const double x);

/* Complete integral F_{3/2}(x)
 *
 * exceptions: GSL_EUNDRFLW, GSL_EOVRFLW
 */
GSL_FUN int     gsl_sf_fermi_dirac_3half_e(const double x, gsl_sf_result * result);
GSL_FUN double     gsl_sf_fermi_dirac_3half(const double x);

/* Incomplete integral F_0(x,b) = ln(1 + e^(b-x)) - (b-x)
 *
 * exceptions: GSL_EUNDRFLW, GSL_EDOM
 */
GSL_FUN int     gsl_sf_fermi_dirac_inc_0_e(const double x, const double b, gsl_sf_result * result);
GSL_FUN double     gsl_sf_fermi_dirac_inc_0(const double x, const double b);

__END_DECLS

#endif /* __GSL_SF_FERMI_DIRAC_H__ */


/**
 * @file tde.h
 * @brief Time delay estimation
 * @author Kenichi Kumatani
 */

#ifndef TDE_H
#define TDE_H

#include <stdio.h>
#include <assert.h>
#include <float.h>

#include <gsl/gsl_block.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_fft_real.h>
#include <gsl/gsl_fft_complex.h>
#include <common/refcount.h>
#include "common/jexception.h"

#include "stream/stream.h"
#include "feature/feature.h"
#include "modulated/modulated.h"
#include "btk.h"

// ----- definition for class `CCTDE' -----
//
/**
   @class find the time difference which provides the maximum correlation between tow signals.
   @usage
   1. constrct sample feature objects :
       samp1 =
       samp2 =
   2. construct this object and feed the sample features into it:
*/
class CCTDE : public VectorFeatureStream {
public:
  /**
     @brief
     @param
     @param
     @param
     @param
   */
  CCTDE( SampleFeaturePtr& samp1, SampleFeaturePtr& samp2, int fftLen=512, unsigned nHeldMaxCC=1, int freqLowerLimit=-1, int freqUpperLimit=-1, const String& nm= "CCTDE" );
  ~CCTDE();

  virtual const gsl_vector* next(int frame_no = -5 );
  virtual const gsl_vector* nextX( unsigned chanX = 0, int frame_no = -5);
  virtual void  reset();
  void  allsamples( int fftLen = -1 );
  void set_target_frequency_range( int freqLowerLimit, int freqUpperLimit ){
    freq_lower_limit_ = freqLowerLimit;
    freq_upper_limit_ = freqUpperLimit;
  }
  const unsigned *sample_delays() const { return sample_delays_; }
  const double *cc_values() const {return cc_values_; }

#ifdef ENABLE_LEGACY_BTK_API
  void setTargetFrequencyRange( int freqLowerLimit, int freqUpperLimit ){ set_target_frequency_range(freqLowerLimit, freqUpperLimit); }
  const unsigned *getSampleDelays(){ return sample_delays(); }
  const double *getCCValues(){ return cc_values(); }
#endif

private:
  const gsl_vector* detect_cc_peaks_( double **samples, size_t stride );

  typedef list<SampleFeaturePtr>	ChannelList_;
  typedef ChannelList_::iterator	ChannelIterator_;
  ChannelList_	channelL_;   // must be 2.
  unsigned	*sample_delays_; // sample delays
  double	*cc_values_;     // cross-correlation (CC) values
  unsigned	nHeldMaxCC_;    // how many CC values are held
  unsigned	fftLen_;
  int		samplerate_;
  gsl_vector	*window_;
  int		freq_lower_limit_;
  int		freq_upper_limit_;
  vector<unsigned > _frameCounter;
};

typedef Inherit<CCTDE, VectorFeatureStreamPtr> CCTDEPtr;

#endif


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <complex.h>
#include <math.h>

#include <fftw3.h>

#include <gsl/gsl_sf_legendre.h>

#include "fsht.h"
#include "util.h"

int fshtinit (shdata *dat, int deg, int ntheta, int nphi, int measfft) {
	int ierr;
	complex double *fftbuf;

	/* This is actually one more than the maximum degree. */
	dat->deg = deg;

	/* The number of theta samples must at least equal the SH degree. */
	if (ntheta < deg) dat->ntheta = deg;
	else dat->ntheta = ntheta;

	/* At least this many phi values are required for fast FFT evaluation. */
	if (2 * deg - 1 > nphi) dat->nphi = 2 * deg - 1;
	else dat->nphi = nphi;

	/* Allocate the theta points and weights. */
	dat->theta = calloc (2 * dat->ntheta, sizeof(double));
	dat->weights = dat->theta + dat->ntheta;

	/* Find the Legendre-Gauss quadrature points. */
	gauleg (dat->ntheta, dat->theta, dat->weights);

	/* Temporarily allocate an FFT data buffer for planning. */
	fftbuf = fftw_malloc (dat->ntheta * dat->nphi * sizeof(complex double));

	/* Plan the forward and inverse transforms. */
	dat->fplan = fftw_plan_many_dft (1, &(dat->nphi), dat->ntheta, fftbuf,
			&(dat->nphi), 1, dat->nphi, fftbuf, &(dat->nphi), 1,
			dat->nphi, FFTW_FORWARD, measfft ? FFTW_MEASURE : FFTW_ESTIMATE);

	dat->bplan = fftw_plan_many_dft (1, &(dat->nphi), dat->ntheta, fftbuf,
			&(dat->nphi), 1, dat->nphi, fftbuf, &(dat->nphi), 1,
			dat->nphi, FFTW_BACKWARD, measfft ? FFTW_MEASURE : FFTW_ESTIMATE);

	/* The FFT buffer is no longer necessary, and will be reallocated
	 * on-the-fly when it is needed later. */
	fftw_free (fftbuf);

	return 0;
}

int fshtfree (shdata *dat) {
	if (dat->theta) free (dat->theta);

	fftw_destroy_plan (dat->fplan);
	fftw_destroy_plan (dat->bplan);

	dat->ntheta = dat->nphi = dat->deg = 0;

	return 0;
}

/* Scale the components of the spherical harmonic coefficients to relate the
 * far-field signature to the spherical harmonic expansion. The default (for
 * non-negative sgn) properly scales SH coefficients AFTER a forward
 * transform (angular to spherical). If sgn is negative, it scales the SH
 * coefficients BEFORE an inverse transform (spherical to angular). */
int shscale (complex double *samp, shdata *dat, int sgn) {
	complex double cscale[4] = { I, -1.0, -I, 1.0 };
	int i, j, off, idx;

	/* For negative sgn, flip the signs of the imaginary multipliers. */
	if (sgn < 0) {
		cscale[0] = -I;
		cscale[2] = I;
	}

	for (i = 0; i < dat->deg; ++i) {
		idx = i % 4;
		off = i * dat->nphi;

		/* Scale the zero order for all degrees. */
		samp[off] *= cscale[idx];

		/* Scale the nonzero orders for all degrees. */
		for (j = 1; j <= i; ++j) {
			samp[off + j] *= cscale[idx];
			samp[off + dat->nphi - j] *= cscale[idx];
		}
	}

	return 0;
}

/* Forward spherical harmonic transform: take samples of the function in theta
 * and phi into SH coefficients. */
int ffsht (complex double *samp, shdata *dat, int maxdeg) {
	int i, j, k, aoff, npk, dm1, deg = maxdeg;
	long lgn, lgi;
	double pc, scale, *lgvals;
	complex double *beta, *fftbuf;

	/* Copy the samples to the FFT buffer and perform the FFT. */
	fftbuf = fftw_malloc (dat->ntheta * dat->nphi * sizeof(complex double));
	memcpy (fftbuf, samp, dat->ntheta * dat->nphi * sizeof(complex double));

	/* Perform an in-place FFT of the buffer. */
	fftw_execute_dft (dat->fplan, fftbuf, fftbuf);

	/* Zero out the input samples, to prepare for storage of coefficients. */
	memset (samp, 0, dat->ntheta * dat->nphi * sizeof(complex double));

	beta = fftbuf;

	/* If no maximum degree is specified, use the default. */
	if (maxdeg < 1) deg = dat->deg;

	dm1 = deg - 1;

	/* Create storage for all Legendre polynomials. */
	lgn = gsl_sf_legendre_array_n (dm1);
	lgvals = malloc (lgn * sizeof(double));

	for (i = 0; i < dat->ntheta; ++i) {
		/* The scale factor that will be required. */
		scale = 2 * M_PI * dat->weights[i] / dat->nphi;

		/* Build the Legendre polynomials that we need.
		 * Don't use Condon-Shortley phase factor. */
		gsl_sf_legendre_array_e (GSL_SF_LEGENDRE_SPHARM, dm1, dat->theta[i], 1., lgvals);

		/* Handle m = 0 for all degrees. */
		for (j = 0; j < deg; ++j) {
			lgi = gsl_sf_legendre_array_index(j, 0);
			samp[j * dat->nphi] += scale * beta[0] * lgvals[lgi];
		}

		/* Handle nonzero orders for all relevant degrees. */
		for (k = 1; k < deg; ++k) {
			npk = dat->nphi - k;
			for (j = k; j < deg; ++j) {
				aoff = j * dat->nphi;
				lgi = gsl_sf_legendre_array_index(j, k);
				pc = scale * lgvals[lgi];
				/* The positive-order coefficient. */
				samp[aoff + k] += pc * beta[k];
				/* The negative-order coefficient. */
				samp[aoff + npk] += pc * beta[npk];
			}
		}

		beta += dat->nphi;
	}

	/* The FFT buffer is no longer required. */
	fftw_free (fftbuf);

	return deg;
}

/* Inverse spherical harmonic transform: take SH coefficients to sample of the
 * function in theta and phi. */
int ifsht (complex double *samp, shdata *dat, int maxdeg) {
	int i, j, k, aoff, npk, dm1, n, deg = maxdeg;
	long lgn, lgi;
	double *lgvals;
	complex double *beta, *fftbuf;

	/* The non-polar samples. */
	n = dat->ntheta * dat->nphi;

	/* Zero out the FFT buffer to prepare for evaluation of function. */
	fftbuf = fftw_malloc (n * sizeof(complex double));
	memset (fftbuf, 0, n * sizeof(complex double));

	beta = fftbuf;

	/* Use the default degree if no maximum is specified. */
	if (maxdeg < 1) deg = dat->deg;

	dm1 = deg - 1;

	/* Create storage for all Legendre polynomials. */
	lgn = gsl_sf_legendre_array_n(dm1);
	lgvals = malloc (lgn * sizeof(double));

	for (i = 0; i < dat->ntheta; ++i) {
		/* Build the Legendre polynomials that we need. */
		/* Don't use the Condon-Shortley phase factor. */
		gsl_sf_legendre_array_e (GSL_SF_LEGENDRE_SPHARM, dm1, dat->theta[i], 1., lgvals);

		for (j = 0; j < deg; ++j) {
			lgi = gsl_sf_legendre_array_index(j, 0);
			beta[0] += samp[j * dat->nphi] * lgvals[lgi];
		}

		for (k = 1; k < deg; ++k) {
			npk = dat->nphi - k;
			for (j = k; j < deg; ++j) {
				aoff = j * dat->nphi;
				lgi = gsl_sf_legendre_array_index(j, k);
				/* The positive-order coefficient. */
				beta[k] += samp[aoff + k] * lgvals[lgi];
				/* The negative-order coefficient. */
				beta[npk] += samp[aoff + npk] * lgvals[lgi];
			}
		}

		/* Move the next theta value in the FFT array. */
		beta += dat->nphi;
	}

	/* Perform the inverse FFT and copy the values to the storage area. */
	fftw_execute_dft (dat->bplan, fftbuf, fftbuf);
	memcpy (samp, fftbuf, n * sizeof(complex double));

	/* Eliminate the FFT buffer. */
	fftw_free (fftbuf);
	free (lgvals);

	return deg;
}


#include <stdio.h>
#include <math.h>
#include <unistd.h> /* for getopt */
#include <string.h>

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

#include "defaults.h"
#include "h2ocalc.h"
//#include"h2ocalc_test.h"

/* gcc h2ocalc.c -o h2ocalc -lgsl -lgslcblas -lm -Wall  */

/* struct ODE_param{ */
/*   int a[NEQ];  /\* dont worry let just run with global vars for now *\/ */
/*   float b[NEQ]; */
/* }; */

int nbprint(double x, double y[], int n) {
    int i;
    double sum = 0;
    printf("%6.4f   ", x);
    for (i = 0; i < n; i++) {
        printf("%6.2f ", y[i]);
        sum += y[i];
    }
    printf("  %6.2f\n",sum);
    return 0;
}

int nbeprint(double x, double y[], int n) {
    int i;
    double sum = 0;

    printf("%6.4e   ",x);
    for (i = 0; i < n; i++) {
        printf("%6.2e ", y[i]);
        sum += y[i];
    }
    printf("  %6.2e\n", sum);
    return 0;
}

int func (double t, const double y[],double f[], void *params) {
    // struct ODE_param *p = (struct ODE_params *) params;
    int i,j,k;
    double rate[NEQ];

    //printf("func\n");

    /* for each species ... */
    for (k = 0; k < NSPECIES; k++) {
        //printf("SPECIES %i\n",k);

        f[k] = 0;

        /* ...find all contributing and removing things */

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

            if (nmatrix[j][k] != 0) {

                rate[j] = nmatrix [j][k]; /* build the final equation */
                //printf("%3i ",nmatrix[j][k]);

                /* add generate I equations */
                rate[j] *= rconst[j]; /* get rate for reaction I_j */

                //printf(" * k%i ",j);
                for (i = 0; i < NSPECIES; i++) {
                    if (nmatrix[j][i] == -1 ) {
                        rate[j] *= y[i]; /* first order kinetics */
                        //printf("* y[%i] ",i);
                    }
                    if (nmatrix[j][i] == -2 ) {
                        rate[j] *= y[i]*y[i]; // second order kinetics
                        //printf("* y[%i]^2 ",i);
                    }
                }

                f[k] += rate[j];
                //printf(" + \n");
            }
        }

        //printf(" ----  k f[k]: %i     %e ---- \n",k,f[k]);

        //    nbeprint(t,y,NSPECIES);
        //    if (k ==8)
        //      exit(0);
    }

    //nbeprint(t,y,NSPECIES);
    //nbeprint(t,f,NSPECIES);

    /* add function for radiation, assumed in last bin */
    //f[NEQ-1] = 0;

    return GSL_SUCCESS;
}

int main(int argc, char **argv) {
    //  struct ODE_param *p = (struct ODE_params *) params;

    double dummy_param = 0;
    gsl_odeiv2_system sys = {func, NULL, NSPECIES, &dummy_param};
    gsl_odeiv2_driver *d = gsl_odeiv2_driver_alloc_y_new(&sys,
                                                         gsl_odeiv2_step_rk8pd,
                                                         1e-6,
                                                         1e-6,
                                                         0.0);
    int i,j,status;
    double t = NSTART;
    //  double t1 = NSTOP;
    //double foobar; /* trash parameter */
    double ti;
    double y[NSPECIES];

    double freq = FREQ;
    double doser = DOSER;
    double simtime = SIMTIME;
    double resol = RESOL;
    double period = 0;
    double dpulse = 0;
    double tick = 0;
    double ta = 1e-3; /* plotting clocks*/
    int pulse_left = 0;
    int pulse_counter = 0;
    signed long int tick_counter = 0;
    char *fname;
    int c;
    int flagp = 0, flagl = 0, flagc = 1; /* default: print each tick */

    /* parse options */
    opterr = 0;
    while ((c = getopt(argc, argv, "f:d:o:r:t:")) != -1) {
        switch(c) {
        case 'l':
            flagl = 1;
            flagp = 0;
            flagc = 0;
            break;
        case 'p':
            flagl = 0;
            flagp = 1;
            flagc = 0;
            break;
        case 'c':
            flagl = 0;
            flagp = 0;
            flagc = 1;
            break;
        case 'f':
            sscanf(optarg, "%lf", &freq);
            break;
        case 'd':
            sscanf(optarg, "%lf", &doser);
            break;
        case 'o':
            fname = optarg; /* not implemented */
            break;
        case 'r':
            sscanf(optarg, "%lf", &resol);
            break;
        case 't':
            sscanf(optarg, "%lf", &simtime);
            break;
        case '?':
            printf("Options:\n");
            printf("  -p output per pulse\n");
            printf("  -c output per tick\n");
            printf("  -l only end of output\n");
            printf("  -f frequency [Hz]\n");
            printf("  -d dose rate [Gy/min]\n");
            printf("  -o output file\n");
            printf("  -r tick resolution [sec]\n");
            printf("  -t simulation time [sec]\n");
            printf("\n");
            exit(0);
            break;
        default:
            printf ("?? no handle ?? %i\n", c);
            exit(-1);
        }
    }

    period = 1 / freq;
    dpulse = (doser / 60.0) / freq * EVJ; // in eV per pulse per liter

    if (period < resol) {
        printf(" *** Error: ");
        printf("Period must be larger than tick resolution.\n");
        exit(-1);
    }

    /* find ticksize closest to the requested one */
    tick = period/(round(period/ resol));

    /* number of pulses to be simulated */
    pulse_left = (int) ((simtime-RSTART) * freq);

    /* print a header */
    printf("# Frequency    : %.3e Hz \n", freq);
    printf("# Period       : %.3e sec\n", period);
    printf("#\n");
    printf("# Tick size    : %.3e sec\n", tick);
    printf("# Time for sim : %.3e sec\n", simtime);
    printf("#\n");
    printf("# Dose rate    : %.3e Gy/min\n", doser );
    printf("# Pulse size   : %.3e eV/l/pulse\n", dpulse );
    printf("# Pulse count  : %i pulses\n", pulse_left);
    printf("#\n");
    printf("# Total delivered dose for this simulation\n");
    printf("#              : %.6e Gy \n", pulse_left * dpulse / EVJ);
    printf("#\n");

    /* copy start conditions into working array */
    for (i = 0; i < NSPECIES; i++)
        y[i] = ystart[i];

    /* Print first line with starting conditions. */
    nbeprint(t, y, NSPECIES);

    /* loop over all pulses */
    while (pulse_left != 0) {

        /* check if we are at a pulse time step */
        if (t >= ((pulse_counter * period) + RSTART)) {
            if (flagp) /* print per pulse (pre pulse) */
                nbeprint(t, y, NSPECIES);

            printf("# Pulse! %i \n", pulse_left);
            for (j=0; j < NSPECIES; j++)
                y[j] += dpulse * gval[j] * 0.01 / NA;
            pulse_left--;
            pulse_counter++;
        }

        tick_counter++;
        ti = tick_counter * tick;

        status = gsl_odeiv2_driver_apply(d, &t, ti, y);

        if (status != GSL_SUCCESS) {
            printf("error, return value = %d\n", status);
            break;
        }

        if (flagc) /* print per tick */
            nbeprint(t, y, NSPECIES);

    } /* end pulses left iterator */

    if (flagl) /* print last */
        nbeprint(t, y, NSPECIES);

    gsl_odeiv2_driver_free(d);
    return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_rng.h>

#define N 65536 //the final number of nodes
#define I 2 // the initial number of nodes

#define MI 0 //the initial trial number
#define MF 33 //(the final trial number) - 1

#define RS 35382 //seed of the random number generator

int main()
{
	FILE *fp;

	double delta=sqrt(1.5);
	int tout[]={512,1024,2048,4096,8192,16384,32768,65536,131072};

	int i,j,k,l,m,t,tt;
	int tmp,flag;

	int **nb; //neighbors of each node

	int x; //new node
	int vdx; //virtual degree of the node x
	int *tmparr;

	int z;
	int nzsize;
	int *nz;

	int *deg; //degree of each node

	double degav; //average degree
	double *knn; //average degree of the nearest neighbors of each node
	double *cc; //local clustering coefficient of each node
	double ccav; //average local clustering coefficient

	char filename[100];

	const gsl_rng_type * TYPE;
	gsl_rng * ran;

	gsl_rng_env_setup();
	TYPE=gsl_rng_default;
	ran=gsl_rng_alloc(TYPE);
	gsl_rng_set(ran,RS);

	tmparr=malloc(sizeof(int)*N);
	nz=malloc(sizeof(int)*N);

	deg=malloc(sizeof(int)*N);
	knn=malloc(sizeof(double)*N);
	cc=malloc(sizeof(double)*N);

	nb=malloc(sizeof(int*)*N);
	for (i=0;i<N;i++){
		nb[i]=malloc(sizeof(int)*N);
	}

	for (m=MI;m<MF;m++){

		//generation of the initial network
		for (i=0;i<I;i++){
			deg[i]=0;
			for (j=0;j<I;j++){
				if (j!=i){
					nb[i][deg[i]]=j;
					deg[i]++;
				}
			}
		}

		tt=0;
		for (t=I;t<N;t++){
			//adding (t+1)-th node: copying degree step
			x=(int)(gsl_rng_uniform(ran)*t);
			vdx=1+(int)(gsl_rng_uniform(ran)*ceil(delta*deg[x]));
			deg[t]=0;

			/*//new OPA pre-adjunction rule
			j=0;
			for (i=0;i<t;i++){
				if (vdx<=ceil(delta*deg[i])){
					tmparr[j]=i;
					j++;
				}
			}
			z=tmparr[(int)(gsl_rng_uniform(ran)*j)];
			nzsize=deg[z]+1;
			for (i=0;i<nzsize-1;i++){
				nz[i]=nb[z][i];
			}
			nz[nzsize-1]=z;
			*/
			/*//OPA pre-adjunction rule
			nzsize=0;
			for (i=0;i<t;i++){
				if (vdx<=ceil(delta*deg[i])){
					nz[nzsize]=i;
					nzsize++;
				}
			}
			*/

			//OPA adjunction rule
			tmp=t;
			for (i=0;i<t;i++){
				if (vdx<=ceil(delta*deg[i]) && deg[i]<tmp)tmp=deg[i];
			}
			nzsize=0;
			for (i=0;i<t;i++){
				if (deg[i]==tmp){
					nz[nzsize]=i;
					nzsize++;
				}
			}

			//choosing the targets of the new node x
			if (vdx>=nzsize){
				for (i=0;i<nzsize;i++){
					nb[nz[i]][deg[nz[i]]]=t; deg[nz[i]]++;
					nb[t][deg[t]]=nz[i]; deg[t]++;
				}
			} else{
				for (j=0;j<vdx;j++){
					i=j+(int)(gsl_rng_uniform(ran)*(nzsize-j));

					nb[nz[i]][deg[nz[i]]]=t; deg[nz[i]]++;
					nb[t][deg[t]]=nz[i]; deg[t]++;

					tmp=nz[j];
					nz[j]=nz[i];
					nz[i]=tmp;
				}
			}

			//output
			if (t+1==tout[tt]){
				//degav
				degav=0.0;
				for (i=0;i<t+1;i++){
					degav+=deg[i];
				}
				degav/=(t+1);

				//knn
				for (i=0;i<t+1;i++){
					knn[i]=0.0;
					for (j=0;j<deg[i];j++){
						knn[i]+=deg[nb[i][j]];
					}
					if (deg[i]>0)knn[i]/=deg[i];
				}

				//cc
				ccav=0.0;
				for (i=0;i<t+1;i++){
					tmp=0;
					for (j=0;j<deg[i];j++){
						for (k=j+1;k<deg[i];k++){
							flag=0;
							for (l=0;l<deg[nb[i][j]];l++){
								if (nb[nb[i][j]][l]==nb[i][k]){
									flag=1;
									break;
								}
							}
							if (flag==1)tmp++;
						}
					}

					if (deg[i]>1)cc[i]=2.0*tmp/(deg[i]*(deg[i]-1));
					else cc[i]=0.0;

					ccav+=cc[i];
				}
				ccav/=(t+1);

				sprintf(filename,"id-deg-knn-cc_t%d_trial%d.txt",t+1,m);
				fp=fopen(filename,"w");
				for (i=0;i<t+1;i++){
					fprintf(fp,"%d %d %.15f %.15f\n",i,deg[i],knn[i],cc[i]);
				}
				fclose(fp);

				printf("m=%d t=%d degav=%f ccav=%f\n",m,t+1,degav,ccav);

				tt++;
			}//end of output

		}//end of t-loop

	}//end of m-loop

	free(tmparr);
	free(nz);
	free(deg);
	free(knn);
	free(cc);
	for (i=0;i<N;i++){
		free(nb[i]);
	}
	free(nb);

	return 0;
}


#include <math.h>

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

#include "qdm.h"

#define TAU_RESET_L_SIZE 11
#define TAU_RESET_M_SIZE  8
#define TAU_RESET_H_SIZE 11
#define TAU_RESET_SIZE (TAU_RESET_L_SIZE + TAU_RESET_M_SIZE + TAU_RESET_H_SIZE)

#define TAU_RESET_STEP 0.0001
#define TAU_RESET_GAP  0.001

#define TAU_TABLE_SIZE 1048576

#define TAU_INITIAL_GUESS 0.5
#define TAU_EPS 0.001
#define TAU_ITERATION_MAX 1000

static
void
qdm_tau_reset_setup(
    qdm_tau *t
)
{
  t->reset = gsl_vector_alloc(TAU_RESET_SIZE);

  size_t offset = 0;

  gsl_vector_view view;

  view = gsl_vector_subvector(t->reset, offset, TAU_RESET_L_SIZE);
  qdm_vector_set_seq(&view.vector, t->low, t->low + (TAU_RESET_L_SIZE - 1) * TAU_RESET_STEP);
  offset += TAU_RESET_L_SIZE;

  view = gsl_vector_subvector(t->reset, offset, TAU_RESET_M_SIZE);
  qdm_vector_set_seq(&view.vector, t->low + TAU_RESET_GAP, t->high - TAU_RESET_GAP);
  offset += TAU_RESET_M_SIZE;

  view = gsl_vector_subvector(t->reset, offset, TAU_RESET_H_SIZE);
  qdm_vector_set_seq(&view.vector, t->high - (TAU_RESET_H_SIZE - 1) * TAU_RESET_STEP, t->high);
  offset += TAU_RESET_H_SIZE;
}

static
void
qdm_tau_table_setup(
    qdm_tau *t
)
{
  if (!t->use_table) {
    t->ispline_table = NULL;
    t->mspline_table = NULL;

    return;
  }

  t->ispline_table = gsl_matrix_alloc(TAU_TABLE_SIZE, (t->knots->size - t->spline_df) + 1);
  t->mspline_table = gsl_matrix_alloc(TAU_TABLE_SIZE, (t->knots->size - t->spline_df) + 1);

  double tau = 0;
  gsl_vector_view row;
  for (size_t i = 0; i < TAU_TABLE_SIZE; i++) {
    tau = (double)(i) / TAU_TABLE_SIZE;

    row = gsl_matrix_row(t->ispline_table, i);
    qdm_ispline_vector(
        &row.vector,
        tau,
        t->spline_df,
        t->knots
    );

    row = gsl_matrix_row(t->mspline_table, i);
    qdm_mspline_vector(
        &row.vector,
        tau,
        t->spline_df,
        t->knots
    );
  }
}

qdm_tau *
qdm_tau_alloc(
    int use_table,

    double low,
    double high,

    size_t spline_df,
    const gsl_vector *knots
)
{
  qdm_tau *t = malloc(sizeof(qdm_tau));

  if (use_table == 1) {
    t->use_table = true;
  } else {
    t->use_table = false;
  }

  t->low = low;
  t->high = high;

  t->spline_df = spline_df;
  t->knots = qdm_vector_copy(knots);

  qdm_tau_reset_setup(t);
  qdm_tau_table_setup(t);

  return t;
}

void
qdm_tau_free(
    qdm_tau *t
)
{
  if (t == NULL) {
    return;
  }

  t->use_table = 0;

  t->low = 0;
  t->high = 0;

  t->spline_df = 0;
  t->knots = NULL;

  gsl_vector_free(t->reset);
  t->reset = NULL;

  gsl_matrix_free(t->ispline_table);
  t->ispline_table = NULL;

  gsl_matrix_free(t->mspline_table);
  t->mspline_table = NULL;

  free(t);
}

double
qdm_tau_ispline_mmm(
    const qdm_tau *t,
    const double value,
    const gsl_vector *mmm
)
{
  double result = 0;

  if (t->use_table) {
    size_t i = (size_t)fmin(floor(value * TAU_TABLE_SIZE), TAU_TABLE_SIZE - 1);
    gsl_vector_view ispline = gsl_matrix_row(t->ispline_table, i);

    gsl_blas_ddot(&ispline.vector, mmm, &result);
  } else {
    size_t m = (t->knots->size - t->spline_df) + 1;
    double ispline_data[m];
    gsl_vector_view ispline = gsl_vector_view_array(ispline_data, m);

    qdm_ispline_vector(&ispline.vector, value, t->spline_df, t->knots);
    gsl_blas_ddot(&ispline.vector, mmm, &result);
  }

  return result;
}

double
qdm_tau_mspline_mmm(
    const qdm_tau *t,
    const double value,
    const gsl_vector *mmm
)
{
  double result = 0;

  if (t->use_table) {
    size_t i = (size_t)fmin(floor(value * TAU_TABLE_SIZE), TAU_TABLE_SIZE - 1);
    gsl_vector_view mspline = gsl_matrix_row(t->mspline_table, i);

    gsl_blas_ddot(&mspline.vector, mmm, &result);
  } else {
    size_t m = (t->knots->size - t->spline_df) + 1;
    double mspline_data[m];
    gsl_vector_view mspline = gsl_vector_view_array(mspline_data, m);

    qdm_mspline_vector(&mspline.vector, value, t->spline_df, t->knots);
    gsl_blas_ddot(&mspline.vector, mmm, &result);
  }

  return result;
}

double
qdm_tau_find(
    const qdm_tau *t,
    const double v,
    const gsl_vector *mmm
)
{
  double tau = TAU_INITIAL_GUESS;
  double qi_u = 0;
  double qm_u = 0;

  size_t reset_i = 0;
  for (size_t i = 0; i < TAU_ITERATION_MAX; i++) {
    if (reset_i >= t->reset->size) {
      tau = TAU_INITIAL_GUESS;

      break;
    }

    /* Calculate position... */
    qi_u = qdm_tau_ispline_mmm(t, tau, mmm);

    /* Calculate slope... */
    qm_u = qdm_tau_mspline_mmm(t, tau, mmm);

    /* Check if the update is within our desired interval [0, 1]. */
    double update = tau - (qi_u - v) / qm_u;
    if (update < 0 || update > 1) {
      tau = gsl_vector_get(t->reset, reset_i);
      reset_i++;

      continue;
    } else {
      tau = update;
    }

    if (fabs(qi_u - v) <= TAU_EPS) {
      break;
    }
  }

  return tau;
}


/*
  NAME:
     minmax
  PURPOSE:
     returns the (finite) minimum and the maximum of a vector which is the row/column of a matrix
  CALLING SEQUENCE:
     minmax(gsl_matrix * q, int row, bool isrow, double * min, double * max, bool partial, int partial_indx[3])
  INPUT:
     q            - matrix which holds the vector
     row          - row/column which holds the vector
     isrow        - is it the row or is it the column
  OUTPUT:
     min   - finite minimum
     max   - finite maximum
  REVISION HISTORY:
     2008-09-21 - Written Bovy
*/
#include <gsl/gsl_matrix.h>
#include <float.h>
#include <stdbool.h>
#include <proj_gauss_mixtures.h>

void minmax(gsl_matrix * q, int row, bool isrow, double * min,
	    double * max){
  *max = -DBL_MAX;
  *min = DBL_MAX;
  int dd;
  double temp;
  if (isrow) {
    for (dd = 0; dd != q->size2; ++dd){
	temp  = gsl_matrix_get(q,row,dd);
	if (temp > *max && bovy_isfin(temp))
	  *max = temp;
	if (temp < *min && bovy_isfin(temp))
	  *min = temp;
    }
  }
  else {
    for (dd = 0; dd != q->size1; ++dd){
	temp  = gsl_matrix_get(q,dd,row);
	if (temp > *max && bovy_isfin(temp))
	  *max = temp;
	if (temp < *min && bovy_isfin(temp))
	  *min = temp;
    }
  }

  return ;
}


#include <assert.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <float.h>
#include "nn.h"
#ifdef __APPLE__
#include <Accelerate/Accelerate.h>
#else
#include <cblas.h>
#endif

#define AVERAGE_FISHER 10

/**
 * A function for generating random numbers according to a N(mu, sigma) Gaussian
 * distribution.
 */
double randn (double mu, double sigma)
{
  double U1, U2, W, mult;
  static double X1, X2;
  static int call = 0;

  if (call == 1) {
      call = !call;
      return (mu + sigma * (double) X2);
  }
  do {
      U1 = -1 + ((double) rand () / RAND_MAX) * 2;
      U2 = -1 + ((double) rand () / RAND_MAX) * 2;
      W = pow (U1, 2) + pow (U2, 2);
  } while (W >= 1 || W == 0);

  mult = sqrt ((-2 * log (W)) / W);
  X1 = U1 * mult;
  X2 = U2 * mult;

  call = !call;

  return (mu + sigma * (double) X1);
}

/**
 * Shorthand to generate normally distributed random numbers.
 */
double rand_normal()
{
  return randn(0.0, 1.0);
}

/**
 * @brief Shuffle the index array.
 */
void shuffle_index(size_t num_pattern, size_t random_idx[num_pattern])
{
  size_t p, np, op;
  for (p = 0; p < num_pattern; ++p) {
    random_idx[p] = p;
  }
  for (p = 0; p < num_pattern; ++p) {
    np = p + ((double)rand()/((double)RAND_MAX+1)) * (num_pattern - 1 - p);
    op = random_idx[p];
    random_idx[p] = random_idx[np];
    random_idx[np] = op;
  }
}

/**
 * This function fills the input and target vector with the list of training
 * examples from automaton.
 */
void fill_input_target(size_t size, double* input, uint8_t* target,
                       uint8_t* automaton, int offset, int states)
{
  size_t index;
  int counter;
  int side = 2 * offset + 1;
  int num_input = states * (side * side - 1);
  uint8_t val;

  for (size_t i = 0; i < size; ++i) {
    for (size_t j = 0; j < size; ++j) {
      index = i * size + j;
      counter = 1;

      /* Add bias in the main vector */
      input[index * (num_input + 1)] = 1.0;

      for (int a = -offset; a < offset + 1; ++a) {
        for (int b = -offset; b < offset + 1; ++b) {
          if (a != 0 || b != 0) {  /* Don't take index i,j */
            val = automaton[((i + a + size) % size) * size
                            + ((j + b + size) % size)];
            for (uint8_t s = 0; s < states; ++s) {
              input[index * (num_input + 1) + counter] = (val == s) ? 1.: 0.;
              counter++;
            }
          }
        }
      }

      target[index] = automaton[i * size + j];
    }
  }
}

void init_weights(int num_hidden, int num_input, int num_output,
                  double* delta_w_ih, double* weight_ih,
                  double* delta_w_ho, double* weight_ho)
{
  int i, j, k;
  /* Initialize weights input -> hidden */
  for (j = 0; j < num_hidden; ++j) {
    delta_w_ih[j] = 0.0;
    weight_ih[j] = 0.0;
    for (i = 1; i < num_input + 1; ++i) {
      delta_w_ih[i * num_hidden + j] = 0.0;
      weight_ih[i * num_hidden + j] = rand_normal() *
        sqrt(1 / (double)(num_input));
    }
  }

  /* Initialize weights hidden -> output */
  for (k = 0; k < num_output; ++k) {
    delta_w_ho[k] = 0.0;
    weight_ho[k] = 0.0;
    for (j = 1; j < num_hidden + 1; ++j) {
      delta_w_ho[j * num_output + k] = 0.0;
      weight_ho[j * num_output + k] = rand_normal() *
        sqrt(1 / (double)(num_hidden));
    }
  }
}

void forward(double* input, double* output,
             int num_hidden, int num_pattern,
             int num_input, int num_output,
             double* hidden,
             double* hidden_bias,
             double* weight_ih,
             double* weight_ho)
{
  int j, k, p;
  double max_out, agg_out = 0.0;

  /* Compute hidden activations */
  cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
              num_pattern, num_hidden, num_input + 1, 1.0,
              input, num_input + 1, weight_ih, num_hidden,
              0.0, hidden, num_hidden);

  /* ReLU non-linearity */
  for (p = 0; p < num_pattern; ++p) {
    hidden_bias[p * (num_hidden + 1)] = 1.0;
    for (j = 1; j < num_hidden + 1; ++j) {
      hidden_bias[p * (num_hidden + 1) + j] =
        (hidden[p * num_hidden + j - 1] > 0.0) ?
        hidden[p * num_hidden + j - 1]: 0.0;
    }
  }

  /* Compute output unit activations */
  cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
              num_pattern, num_output, num_hidden + 1, 1.0,
              hidden_bias, num_hidden + 1, weight_ho, num_output,
              0.0, output, num_output);

  /* Compute softmax of output */
  for (p = 0; p < num_pattern; ++p) {
    max_out = output[p * num_output];
    for (k = 1; k < num_output; ++k) {
      if (output[p * num_output + k] > max_out) {
        max_out = output[p * num_output + k];
      }
    }
    agg_out = 0.0;
    for (k = 0; k < num_output; ++k) {
      output[p * num_output + k] = exp(output[p * num_output + k] - max_out);
      agg_out += output[p * num_output + k];
    }
    for (k = 0; k < num_output; ++k) {
      output[p * num_output + k] /= agg_out;
    }
  }
}

void update_weights(int num_input, int num_hidden, int num_output,
                    double eta,
                    double* batch_error, double reg, double alpha,
                    double* weight_ih, double* delta_w_ih,
                    double* delta_w_ih_prev,
                    double* weight_ho, double* delta_w_ho,
                    double* delta_w_ho_prev)
{
  int i, j, k;
  /* Update weights with average gradient */
  for (i = 0; i < num_input + 1; ++i) {
    for (j = 0; j < num_hidden; ++j) {
      if (reg > 0.) {
        *batch_error += 0.5 * reg *
          weight_ih[i * num_hidden + j] * weight_ih[i * num_hidden + j] ;
        weight_ih[i * num_hidden + j] += (1 + alpha) *
          reg * weight_ih[i * num_hidden + j];
      }

      weight_ih[i * num_hidden + j] -= (1 + alpha) *
        (eta * delta_w_ih[i * num_hidden + j]);

      if (alpha > 0.) {
        weight_ih[i * num_hidden + j] += (-alpha) *
          delta_w_ih_prev[i * num_hidden + j];
      }
    }
  }
  for (j = 0; j < num_hidden + 1; ++j) {
    for (k = 0; k < num_output; ++k) {
      if (reg > 0.) {
        *batch_error += 0.5 * reg *
          weight_ho[j * num_output + k] * weight_ho[j * num_output + k];
        weight_ho[j * num_output + k] += (1 + alpha) *
        reg * weight_ho[j * num_output + k];
      }

      weight_ho[j * num_output + k] -= (1 + alpha) *
        (eta * delta_w_ho[j * num_output + k]);

      if (alpha > 0.) {
        weight_ho[j * num_output + k] += (-alpha) *
          delta_w_ho_prev[j * num_output + k];
      }
    }
  }
}

void compute_batch_gradients(int base_index, double alpha,
                             int batch_size, int num_input, int num_output,
                             int num_hidden, size_t* random_idx,
                             double* output,
                             uint8_t* target, double* delta_output,
                             double* delta_w_ho, double* hidden_bias,
                             double* weight_ho, double* delta_h,
                             double* delta_w_ih, double* input,
                             double* delta_w_ih_prev,
                             double* delta_w_ho_prev,
                             network_opts_t* opts)
{
  int i, j, k, p;

  /* Gradient initialization (Nesterov momentum) */
  if (opts->optim_type == NESTEROV) {
    memcpy(delta_w_ih_prev, delta_w_ih,
           sizeof(double) * num_hidden * (num_input + 1));
    for (i = 0; i < num_input + 1; ++i) {
      for (j = 0; j < num_hidden; ++j) {
        delta_w_ih[i * num_hidden + j] *= alpha;
      }
    }

    memcpy(delta_w_ho_prev, delta_w_ho,
           sizeof(double) * num_output * (num_hidden + 1));
    for (j = 0; j < num_hidden + 1; ++j) {
      for (k = 0; k < num_output; ++k) {
        delta_w_ho[j * num_output + k] *= alpha;
      }
    }
  }

  for (int b = 0; b < batch_size; ++b) {
    p = random_idx[(base_index + b)];

    /* Backpropagation */
    for (k = 0; k < num_output; ++k) {
      /* Output gradients */
      delta_output[b * num_output + k] =
        (output[b * num_output + k] - ((k == target[p])? 1.0: 0.0));
      delta_output[b * num_output + k] /= (double) batch_size;
    }
  }

  /* Dot product hidden_bias x delta_output stored in delta_w_ho */
  cblas_dgemm(CblasRowMajor, CblasTrans, CblasNoTrans,
              num_hidden + 1, num_output, batch_size,
              1 , hidden_bias, num_hidden + 1,
              delta_output, num_output,
              (opts->optim_type == NESTEROV)? 1.0: 0.0,
              delta_w_ho, num_output);

  /* Dot product weight_ho x delta_output (we don't count the bias in
     weight_ho) stored in delta_h */
  cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasTrans,
              batch_size, num_hidden, num_output,
              1.0, delta_output, num_output,
              &weight_ho[num_output], num_output,
              0.0, delta_h, num_hidden);

  /* Hidden layer non linearity gradients */
  for (int b = 0; b < batch_size; ++b) {
    for (j = 0; j < num_hidden; ++j) {
      delta_h[b * num_hidden + j] *=
        ((hidden_bias[b * (num_hidden + 1) + j + 1] > 0) ? 1.0: 0.0);
    }
  }

  cblas_dgemm(CblasRowMajor, CblasTrans, CblasNoTrans,
              num_input + 1, num_hidden, batch_size,
              1 , input, num_input + 1,
              delta_h, num_hidden,
              (opts->optim_type == NESTEROV)? 1.0: 0.0,
              delta_w_ih, num_hidden);

}

double compute_loss(int base_index, int batch_size,
                    size_t* random_idx,
                    int num_output, double* output,
                    uint8_t* target)
{
  double batch_error = 0.0;
  int p;
  for (int b = 0; b < batch_size; ++b) {
    p = random_idx[(base_index + b)];
    /* Compute loss */
    batch_error +=
      - log((output[b * num_output + target[p]] > 0) ?
            output[b * num_output + target[p]]: DBL_MIN);
  }
  batch_error /= (double) batch_size;
  return batch_error;
}

double compute_fisher(int states, int neighbors,
                      int num_pattern, int num_output,
                      int num_hidden, int num_input,
                      double* input,
                      double* weight_ih, double* weight_ho)
{
  double fisher_information = 0.0;
  int base, index, delta;

  /* Build the secondary input that will be perturbed */
  double* perturbed_input =
    (double*) malloc(sizeof(double) * num_pattern * (num_input + 1));

  /* Allocate placeholders in the function to make it more
     adaptable to the input dataset. */
  double* output =
    (double*) malloc(sizeof(double) * num_pattern * num_output);
  double* output_pert =
    (double*) malloc(sizeof(double) * num_pattern * num_output);
  double* hidden =
    (double*) malloc(sizeof(double) * num_pattern * num_hidden);
  double* hidden_bias =
    (double*) malloc(sizeof(double) * num_pattern * (num_hidden + 1));

  /* Compute the output of the network on the base dataset */
  forward(input, output, num_hidden, num_pattern, num_input, num_output,
          hidden, hidden_bias, weight_ih, weight_ho);

  for (int n = 0; n < AVERAGE_FISHER; ++n) {
    for (int neighbor_n = 0; neighbor_n < neighbors; ++neighbor_n) {

      /* Perturb second input */
      memcpy(perturbed_input, input,
            sizeof(double) * num_pattern * (num_input + 1));
      for (int i = 0; i < num_pattern; ++i) {
        /* Choose index of the input cell to perturb */
        index = i * (num_input + 1) + neighbor_n * states;

        for (int t = 0; t < states; ++t) {
          base = 1 + (rand() % (states - 1));
          perturbed_input[index + ((base + t) % states)] =
            input[index + t];
        }
      }
      /* Compute the output of the network on the perturbed dataset */
      forward(perturbed_input, output_pert, num_hidden, num_pattern,
              num_input, num_output, hidden, hidden_bias, weight_ih, weight_ho);

      for (int i = 0; i < num_pattern; ++i) {
        for (int j = 0; j < num_output; ++j) {
          if (output_pert[i * num_output + j] > 0
              && output[i * num_output + j] > 0) {
            delta = log(output[i * num_output + j]
                        / output_pert[i * num_output + j]);
            fisher_information += output[i * num_output + j]
              * delta * delta;
          }
        }
      }

    }
  }

  free(output);
  free(hidden);
  free(hidden_bias);
  free(perturbed_input);
  free(output_pert);

  return fisher_information / (AVERAGE_FISHER * num_pattern);
}

double compute_error(int num_pattern, int num_output,
                     int num_hidden, int num_input,
                     double* input, uint8_t* target,
                     double* weight_ih, double* weight_ho)
{
  double test_error = 0.0;
  double val;

  /* Allocate placeholders in the function to make it more
     adaptable to the input dataset. */
  double* output =
    (double*) malloc(sizeof(double) * num_pattern * num_output);
  double* hidden =
    (double*) malloc(sizeof(double) * num_pattern * num_hidden);
  double* hidden_bias =
    (double*) malloc(sizeof(double) * num_pattern * (num_hidden + 1));

  /* Compute the output of the network */
  forward(input, output, num_hidden, num_pattern, num_input, num_output,
          hidden, hidden_bias, weight_ih, weight_ho);

  /* Compute loss */
  for (int p = 0; p < num_pattern; ++p) {
    val = output[p * num_output + target[p]];
    test_error += - log((val > 0) ? val: DBL_MIN);
  }
  test_error /= num_pattern;

  free(output);
  free(hidden);
  free(hidden_bias);

  return test_error;
}

void train_nn_on_automaton(size_t size, int states,
                           uint8_t* train_automaton,
                           uint8_t** test_automata,
                           int n_tests,
                           network_opts_t* opts,
                           network_result_t* res)
{
  if (opts->verbosity >= 1) {
    fprintf(stdout, "\nProcessing with options: h%i r%i e%i\n",
            opts->num_hid, opts->offset, opts->max_epoch);
  }

  size_t num_pattern = size * size;
  int side = 2 * opts->offset + 1;
  int num_input = states * (side * side - 1);
  int num_hidden = opts->num_hid;
  int num_output = states;

  double batch_error, error, eta = 1, alpha = 0.9;
  if (opts->optim_type != NESTEROV) {
    alpha = 0.;
  }

  const int batch_size = 8;
  /* Regression parameter */
  double reg = 0.;

  /* ====== Network and training variables declaration ====== */

  /* num_pattern x (num_input + 1) array that holds all the training set */
  double* base_input =
    (double *) malloc(num_pattern * (num_input + 1) * sizeof(double));
  /* Array that holds the training labels */
  uint8_t* target = (uint8_t *) malloc(num_pattern * sizeof(uint8_t));
  /* Fill those arrays with the automaton's content */
  fill_input_target(size, base_input, target,
                    train_automaton, opts->offset, states);

  /* Arrays for the test data */
  double* test_input =
    (double *) malloc(num_pattern * (num_input + 1) * sizeof(double));
  uint8_t* test_target = (uint8_t *) malloc(num_pattern * sizeof(uint8_t));

  /* Weights of the network */
  double weight_ih[(num_input + 1) * num_hidden];
  double weight_ho[(num_hidden + 1) * num_output];

  /* Gradients of the output and hidden layer placeholders */
  double delta_output[batch_size * num_output];
  double delta_h[batch_size * num_hidden];

  /* Weight gradients and previous gradients for Nesterov momentum */
  double delta_w_ih[(num_input + 1) * num_hidden];
  double delta_w_ho[(num_input + 1) * num_hidden];
  double* delta_w_ih_prev = NULL;
  double* delta_w_ho_prev = NULL;

  /* Allocate only if being used later */
  if (opts->optim_type == NESTEROV) {
    delta_w_ih_prev = malloc(sizeof(double) * (num_input + 1) * num_hidden);
    delta_w_ho_prev =  malloc(sizeof(double) * (num_hidden + 1) * num_output);
  }

  /* Allocate the arrays that will hold data for each batch */
  double input[(batch_size) * (num_input + 1)];
  double hidden[batch_size * num_hidden];
  double hidden_bias[batch_size * (num_hidden + 1)];
  double output[batch_size * num_output];

  int epoch;
  size_t random_idx[num_pattern];
  double test_error = 0.0;
  double test_var = 0.0;

  /* ===================== End declarations ====================== */

  /* Initialize the weights of the network */
  init_weights(num_hidden, num_input, num_output,
               delta_w_ih, weight_ih,
               delta_w_ho, weight_ho);

  for (epoch = 0; epoch < opts->max_epoch; ++epoch) {
    /* Initialize error */
    error = 0.0;

    /* Learning rate decay */
    if (epoch > 0 && epoch%10 == 0 && opts->decay == DECAY) {
      eta -= .5 * eta;
    }

    /* Random ordering of input patterns done at each epoch */
    shuffle_index(num_pattern, random_idx);

    /* Loop through every batch in the dataset */
    for (size_t s = 0; s < num_pattern; s += batch_size) {
      for (int b = 0; b < batch_size; ++b) {
        if (s + b >= num_pattern) {
          break;
        }
        /* Copy batch elements to the input array for processing */
        memcpy(input + b * (num_input + 1),
               base_input + (random_idx[(s + b)] *
                             (num_input + 1)),
               sizeof(double) * (num_input + 1));
      }

      /* Forward pass */
      forward(input, output, num_hidden, batch_size, num_input, num_output,
              hidden, hidden_bias, weight_ih, weight_ho);

      batch_error = compute_loss(s, batch_size, random_idx, num_output,
                                 output, target);

      /* Compute the gradients for each weight matrix */
      compute_batch_gradients(s, alpha, batch_size, num_input, num_output,
                              num_hidden,
                              random_idx, output, target,
                              delta_output, delta_w_ho, hidden_bias, weight_ho,
                              delta_h, delta_w_ih, input,
                              delta_w_ih_prev, delta_w_ho_prev, opts);

      /* Update the weights of the network */
      update_weights(num_input, num_hidden, num_output, eta, &batch_error, reg,
                     alpha, weight_ih, delta_w_ih, delta_w_ih_prev, weight_ho,
                     delta_w_ho, delta_w_ho_prev);

      /* Error is only updated here because it might have been changed by
         `update_weights` */
      error += batch_error * batch_size;
    }
    error /= (double)(num_pattern);

    if (opts-> verbosity >= 1 && epoch%5 == 0) {
      fprintf(stdout, "\nEpoch %d: Error = %f", epoch, error);
    }
  }

  /* Compute error on the training set */
  error = compute_error(num_pattern, num_output, num_hidden,
                        num_input, base_input, target,
                        weight_ih, weight_ho);

  if (opts->verbosity >= 1) {
    fprintf(stdout, "\nTrain error: %f", error);
  }

  /* Compute Fisher information if the flag requires it */
  if (opts->fisher == FISHER) {
    res->fisher_info = compute_fisher(states, (side*side - 1), num_pattern,
                                      num_output, num_hidden, num_input,
                                      base_input, weight_ih, weight_ho);
  }

  /* Was an array of states to test on provided ? */
  if (test_automata != NULL) {
    double test_errors[n_tests];
    for (int i = 0; i < n_tests; ++i) {
      /* Fill the placeholders with test data */
      fill_input_target(size, test_input, test_target,
                        test_automata[i], opts->offset, states);

      /* Compute error on the test set */
      test_errors[i] = compute_error(num_pattern, num_output, num_hidden,
                                     num_input, test_input, test_target,
                                     weight_ih, weight_ho);
      test_error += error / test_errors[i];
    }

    /* Average score across all tested states */
    test_error /= n_tests;

    /* Compute variance of scores */
    for (int i = 0; i < n_tests; ++i) {
      test_var += (error / test_errors[i] - test_error)
        * (error / test_errors[i] - test_error);
    }
    test_var /= n_tests;
    test_var = sqrt(test_var);

    if (opts->verbosity >= 1) {
      fprintf(stdout, "\tTest error: %f\tVar: %f\tRatio: %f",
              error / test_error, test_var, test_error);
    }
  }

  /* Log results */
  if (opts->verbosity >= 1 && opts->fisher== FISHER) {
      fprintf(stdout, "\tFisher: %f\n", res->fisher_info);
  } else if (opts->verbosity >= 1){
      fprintf(stdout, "\n");
  }

  /* Output data */
  res->train_error = error;
  res->test_error = error / test_error;
  res->error_var = test_var;

  /* Cleanup allocated arrays */
  free(target);
  free(base_input);
  free(test_input);
  free(test_target);
  free(delta_w_ih_prev);
  free(delta_w_ho_prev);
}


/*==========================================================
 * Multinomial_gsl.c
 *
 * Return multinomially distributed integers based on input probability vector
 *
 *========================================================*/
/*  */

#include "mex.h"
#include "matrix.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> /* For EXIT_FAILURE, EXIT_SUCCESS */
#include "mat.h"
// #include <gsl/gsl_sf_bessel.h>
#include <gsl/gsl_randist.h>
#include <time.h>
#define pi acos(-1.0)

int rlxd_seed(){
    int seed;
    FILE *file_seeds;
    file_seeds=fopen("/dev/urandom","rb"); // /dev/urandom is designed so that each time it is opened it produces a different random seed
    fread(&seed, sizeof(int), 1, file_seeds);
    fclose(file_seeds);

    return abs(seed);
}

void Multinomial(unsigned int *n, double N, double *x, mwSize K)
{

//     Multinomial(n,N,x,K);

    //    /* calculate eigenvalues and eigenvectors */
    //mexCallMATLAB(2, lhs, 1, &x, "eig");

//    printf("N =%f\n",N);

//    printf("K =%d\n",K);

    //Initialisation for using gls multinomial function

    const gsl_rng_type *Type;         //initialise a constant Type which holds type of random number generator
    gsl_rng *r;                              //Initialise pointer to r which is an "instance" of random number generator

    //Set seed for random numbers from current time
//     srand(time(0));
//     srand(rlxd_seed());
    unsigned long int seed = rlxd_seed();         //initialise integer seed and set to value shown
//     unsigned long int seed = random_seed();         //initialise integer seed and set to value shown

    gsl_rng_env_setup();                        //read in environmental variables set at command line

    Type = gsl_rng_default;                        //set random number generator to default, which is mt19937
    r = gsl_rng_alloc (Type);                       //Allocate memory for type T generator and get pointer
    gsl_rng_set (r, seed);                      //initialise random number generator

    unsigned int NN = N;                    //Sample size
    size_t KK =K;
//     double mx[KK];                    //Initialise pointer to mx real positive array of probabilities

//     unsigned int n[KK];           //Initialise output integer array

//     printf ("generator type: %s\n", gsl_rng_name (r));
//     printf ("seed = %d\n", seed);

//     printf("NN=%d\n",NN);
//     printf("KK=%d\n",KK);

    gsl_ran_multinomial(r, KK, NN, x, n);

    gsl_rng_free(r);

//     free(n);
//     free(mx);

 }

/* The gateway function */
void mexFunction( int nlhs, mxArray *plhs[],
                  int nrhs, const mxArray *prhs[])
{

     /* check for proper number of arguments */
    if(nrhs!=3) {
        mexErrMsgIdAndTxt("MyToolbox:Multinomial_gsl:nrhs","Three inputs required.");
    }
    if(nlhs!=1) {
        mexErrMsgIdAndTxt("MyToolbox:Multinomial_gsl:nlhs","One output required.");
    }

    /* get input variables */
    double N=mxGetScalar(prhs[0]);
    double *x = mxGetPr(prhs[1]);
    double K=mxGetScalar(prhs[2]);

    int *n;

    mwSize Ka=K;

//     plhs[0] = mxCreateDoubleMatrix(1, Ka,mxREAL);
    //mxCreateNumericMatrix is used here as we want to create a vector of integers
    plhs[0] = mxCreateNumericMatrix(1, Ka, mxUINT32_CLASS, mxREAL);

   /* get a pointer to the real data in the output matrix */
   //As arrays is not DOUBLE then we can't use mxGetPr
   n = (int*) mxGetData(plhs[0]);

   Multinomial(n,N,x,Ka);

}


/// @file mlr.h
/// @brief multiple linear regression support
/// @author Jeff Perry <jeffsp@gmail.com>
/// @version 1.0
/// @date 2013-06-13

#ifndef MLR_H
#define MLR_H

#include "raster_utils.h"
//#include <lapacke.h>

namespace horny_toad
{

    /// @brief helper function
    template<typename T>
    T matrix_multiply (const T &a, const T &b)
    {
        assert (a.cols () == b.rows ());
        const size_t N = a.rows ();
        const size_t M = a.cols ();
        const size_t P = b.cols ();
        T y (a.rows (), b.cols ());
        for (size_t i = 0; i < N; ++i)
            for (size_t j = 0; j < P; ++j)
                for (size_t k = 0; k < M; ++k)
                    y (i, j) += a (i, k) * b (k, j);
        return y;
    }

    /// @brief multiple linear regression
    ///
    /// @tparam T matrix types
    /// @param y responses
    /// @param x predictors
    ///
    /// @return linear estimates of y=b*x
    template<typename T>
    T mlr_inverse (const T &y, const T &x)
    {
        // add a column of 1's to x on the left
        T xx (x.rows (), x.cols () + 1);
        for (size_t i = 0; i < x.rows (); ++i)
        {
            xx (i, 0) = 1;
            for (size_t j = 0; j < x.cols (); ++j)
            {
                xx (i, j + 1) = x (i, j);
            }
        }
        // b = (x^T * x)^-1 * x^T * y
        T xt = transpose (xx);
        T tmp = matrix_multiply (xt, xx);
        tmp = invert (tmp);
        tmp = matrix_multiply (tmp, xt);
        tmp = matrix_multiply (tmp, y);
        return tmp;
    }

    /// @brief multiple linear regression
    ///
    /// @tparam T matrix types
    /// @param y responses
    /// @param x predictors
    ///
    /// @return linear estimates of y=b*x
    /*
    template<typename T>
    T mlr_lapack (const T &y, const T &x)
    {
        assert (x.cols () <= x.rows ());
        assert (y.rows () == x.rows ());
        // result gets stored in b
        T b (y);
        // add a column of 1's to x on the left
        T xx (x.rows (), x.cols () + 1);
        for (size_t i = 0; i < x.rows (); ++i)
        {
            xx (i, 0) = 1;
            for (size_t j = 0; j < x.cols (); ++j)
            {
                xx (i, j + 1) = x (i, j);
            }
        }
        const size_t M = xx.rows ();
        const size_t N = xx.cols ();
        // solve it
        if (LAPACKE_dgels (LAPACK_ROW_MAJOR, 'N', M, N, 1, &xx[0], N, &b[0], 1) != 0)
            throw std::runtime_error ("LAPACK error: can't solve mlr");
        // copy result into matrix with correct dimensions
        T z (xx.cols (), 1);
        for (size_t i = 0; i < z.rows (); ++i)
            z[i] = b[i];
        return z;
    }
    */

    /// @brief multiple linear regression
    ///
    /// @tparam T matrix types
    /// @param y responses
    /// @param x predictors
    ///
    /// @return linear estimates of y=b*x
    template<typename T>
    T mlr (const T &y, const T &x)
    {
        return mlr_inverse (y, x);
        //return mlr_lapack (y, x);
    }
} // namespace horny_toad

#endif // MLR_H


#ifndef MITRO_GRAPH_SHORTEST_PATH_DIJKSTRA_H
#define MITRO_GRAPH_SHORTEST_PATH_DIJKSTRA_H
#include "Graph.h"
#include <gsl.h>
/////////////////////////////////////////////////////
//  DIJKSTRA'S SHORTEST PATH
/////////////////////////////////////////////////////

std::vector<std::pair<double, const GraphEdge*>> nodeWeightDijkstra(Graph &g, int startNode, int endNode);
std::vector<std::pair<double, const GraphEdge*>> edgeWeightDijkstra(Graph &g, int startNode, int endNode);

namespace graph_algorithm_capture
{
    struct ShortestPathEdgeWeightDijkstraState
    {
        std::vector<std::pair<double, const GraphEdge*>> path;
        std::set<std::pair<double, int>> openNodes; // discovered but not fully processed
        std::set<std::pair<double, int>> closedNodes; //  fully processed
        std::vector<gsl::not_null<const GraphEdge*>> processedEdges; // processed, not part of any of the minimal paths
        int inspectedNode;
        const GraphEdge * inspectedEdge;
        std::string description;

        ShortestPathEdgeWeightDijkstraState(const std::vector<std::pair<double, const GraphEdge*>> &path,
            const std::set<std::pair<double, int>> &openNodes,
            const std::set<std::pair<double, int>> &closedNodes,
            const std::vector<gsl::not_null<const GraphEdge*>> &processedEdges,
            int inspectedNode,
            const GraphEdge* inspectedEdge,
            const std::string &description) :
            path(path),
            openNodes(openNodes),
            closedNodes(closedNodes),
            processedEdges(processedEdges),
            inspectedNode(inspectedNode),
            inspectedEdge(inspectedEdge),
            description(description)
        {}
    };

    std::vector<ShortestPathEdgeWeightDijkstraState>
    edgeWeightDijkstraCaptureStates(Graph &g, int startNode, int endNode);
} // namespace graph_algorithm_capture

#endif // MITRO_GRAPH_SHORTEST_PATH_DIJKSTRA_H


#ifndef __QBLAS_EXTEND__CD
#define __QBLAS_EXTEND__CD
#ifdef HAVE_CBLAS_H
#include <cblas.h>
#elif (defined HAVE_MKL_CBLAS_H)
#include <mkl_cblas.h>
#elif (defined HAVE_GSL_CBLAS_H)
#include <gsl_cblas.h>
#endif

/*
  some convenience functions for
  the case where all vectors are
  continguous in memory
*/
inline void cblas_daxpy(const int N,
                        const double alpha,
                        const double *X,
                        double *Y)
{ cblas_daxpy(N,alpha,X,1,Y,1); }

inline double cblas_dnrm2(const int N,
                          const double *X)
{ return cblas_dnrm2(N,X,1); }

inline void cblas_dcopy(const int N,
                        const double *X,
                        double *Y )
{ cblas_dcopy(N,X,1,Y,1); }

inline void cblas_dscal(const int N,
                        const double alpha,
                        double *X)
{ cblas_dscal(N,alpha,X,1); }

inline double cblas_ddot(const int N,
			 const double *X,
			 const double *Y )
{ return cblas_ddot(N,X,1,Y,1); }

inline double cblas_ddot(const int N, const double *X)
{ return cblas_ddot(N,X,1,X,1); }

inline void cblas_zdotc_sub(const int N, const double *X, const double *Y,
			  double* dot)
{ return cblas_zdotc_sub(N,X,1,Y,1, dot); }

//inline void cblas_cdotc_sub(const int N, const float *X, const float *Y,
//		  float* dot)
//{ return cblas_cdotc_sub(N,X,Y,dot); }

#endif


#include <gsl/gsl_complex.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_fft_real.h>
#include <gsl/gsl_fft_halfcomplex.h>
#include <gsl/gsl_fft_complex.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_poly.h>
#include <gsl/gsl_statistics_double.h>
#include <gsl/gsl_statistics_float.h>

#include <stdio.h>

int vector_float_convolve(int cs, const float* c, int as, const float* a, int rs, float* r)
{
  int h = cs / 2;
  int li, ri;
  int i,j;

  for (i = 0; i < cs; i++) {
    li = i - h;
    ri = i + h;
    r[i] = 0;
    for (j = (li >= 0 ? li : 0); j < (ri < as ? ri : (as - 1)); j++) {
      r[i] += a[j]*c[j+h+1];
    }
  }
  return 0;
}

int vector_double_convolve(int cs, const double* c, int as, const double* a, int rs, double* r)
{
  int h = cs / 2;
  int li, ri;
  int i,j;

  for (i = 0; i < cs; i++) {
    li = i - h;
    ri = i + h;
    r[i] = 0;
    for (j = (li >= 0 ? li : 0); j < (ri < as ? ri : (as - 1)); j++) {
      r[i] += a[j]*c[j+h+1];
    }
  }
  return 0;
}

int vector_complex_convolve(int cs, const gsl_complex* c, int as, const gsl_complex* a, int rs, gsl_complex* r)
{
  int h = cs / 2;
  int li, ri;
  int i,j;

  for (i = 0; i < cs; i++) {
    li = i - h;
    ri = i + h;
    r[i].dat[0] = 0;
    r[i].dat[1] = 0;
    for (j = (li >= 0 ? li : 0); j < (ri < as ? ri : (as - 1)); j++) {
      r[i].dat[0] += a[j].dat[0]*c[j+h+1].dat[0]-a[j].dat[1]*c[j+h+1].dat[1];
      r[i].dat[1] += a[j].dat[0]*c[j+h+1].dat[1]+a[j].dat[1]*c[j+h+1].dat[0];
    }
  }
  return 0;
}

int filter_double(int ls, const double* l, int ks, const double* k, int vs, const double* v, int rs, double* r)
{
  if (ls > vs || ks > vs) return 2000; // BAD_SIZE

  int i,j;

  double L = l[0];
  double K = k[0];

  int N = ls - 1;
  int M = ks - 1;

  for (i = 0; i < vs; i++) {
    r[i] = 0;
    for (j = 0; j < N; j++) {
      if (i - j > 0) r[i] -= (l[j+1])*v[i-j];
    }
    for (j = 0; j < M; j++) {
      if (i - j > 0) r[i] += (k[j+1])*r[i-j];
    }
  }
  return 0;
}

int filter_float(int ls, const float* l, int ks, const float* k, int vs, const float* v, int rs, float* r)
{
  if (ls > vs || ks > vs) return 2000; // BAD_SIZE

  int i,j;

  float L = l[0];
  float K = k[0];

  int N = ls - 1;
  int M = ks - 1;

  for (i = 0; i < vs; i++) {
    r[i] = 0;
    for (j = 0; j < N; j++) {
      if (i - j > 0) r[i] -= (l[j+1])*v[i-j];
    }
    for (j = 0; j < M; j++) {
      if (i - j > 0) r[i] += (k[j+1])*r[i-j];
    }
  }
  return 0;
}

int hilbert(int rs, gsl_complex* r)
{
  int s = rs;

  gsl_fft_complex_wavetable * wavetable = gsl_fft_complex_wavetable_alloc (s);
  gsl_fft_complex_workspace * workspace = gsl_fft_complex_workspace_alloc (s);

  // forward fourier transform
  gsl_fft_complex_forward ((double*)r, 1, s, wavetable, workspace);
  // zero negative coefficients and double positive

  int i;
  int m = s/2;
  for (i = 1; i < s; i++) {
    if (i <= m) {
      r[i].dat[0] *= 2;
      r[i].dat[1] *= 2;
    }
    else if (s % 2 == 0 && i == m+1) {
    }
    else {
      r[i].dat[0] = 0;
      r[i].dat[1] = 0;
    }
  }

  // inverse fourier transform
  gsl_fft_complex_inverse ((double*)r, 1, s, wavetable, workspace);

  gsl_fft_complex_wavetable_free (wavetable);
  gsl_fft_complex_workspace_free (workspace);

  return 0;
}

int pwelch(int w, int vs, const gsl_complex* v, int rs, double* r)
{
  if (w > vs) return 2000; // BAD_SIZE

  int i,j;

  int fs = w;

  int num_windows = vs / fs; // ignore end

  double s[fs];
  for (i = 0; i < fs; i++) s[i] = 0;

  gsl_fft_complex_wavetable * wavetable = gsl_fft_complex_wavetable_alloc (fs);
  gsl_fft_complex_workspace * workspace = gsl_fft_complex_workspace_alloc (fs);

  gsl_complex* f = malloc(sizeof(gsl_complex)*fs);
  gsl_vector_view F = gsl_vector_view_array((double*)f, 2*fs);

  gsl_vector_view X;

  for (i = 0; i < num_windows; i++) {
    X = gsl_vector_view_array((double*)(&v[i*fs]), 2*fs); // v is gsl_complex*
    gsl_blas_dcopy(&X.vector,&F.vector);
    gsl_fft_complex_forward ((double*)f, 1, fs, wavetable, workspace);
    for (j = 0; j < fs; j++) s[j] += f[j].dat[0]*f[j].dat[0] + f[j].dat[1]*f[j].dat[1];
  }
  for (j = 0; j < rs; j++) {
    if (j == 0) r[j] = s[j];
    else if (j == (rs-1)) r[j] = s[j];
    else r[j] = s[j] + s[fs-j+1];

    r[j] /= num_windows;
    r[j] = sqrt(r[j]);
  }
  gsl_fft_complex_wavetable_free (wavetable);
  gsl_fft_complex_workspace_free (workspace);

  free(f);

  return 0;
}

int hamming_double(int rs, double* r)
{
  int i;

  for (i = 0; i < rs; i++) r[i] = 0.54 - 0.46 * cos(2*M_PI*i/rs);

  return 0;
}

int hamming_float(int rs, float* r)
{
  int i;

  for (i = 0; i < rs; i++) r[i] = 0.54 - 0.46 * cos(2*M_PI*i/rs);

  return 0;
}

int real_poly_complex_eval(int cs, const double* c, int zs, const gsl_complex* z, int rs, gsl_complex* r)
{
  int i;

  for (i = 0; i < zs; i++)
    r[i] = gsl_poly_complex_eval(c,cs,z[i]);

  return 0;
}

int complex_power_double(int cs, const gsl_complex* c, int rs, double* r)
{
  if (rs != cs) return 2000; // BAD_SIZE

  int i;

  for (i = 0; i < cs; i++)
    r[i] = c[i].dat[0]*c[i].dat[0] + c[i].dat[1]*c[i].dat[1];

  return 0;
}

int complex_power_float(int cs, const gsl_complex* c, int rs, float* r)
{
  if (rs != cs) return 2000; // BAD_SIZE

  int i;

  for (i = 0; i < cs; i++)
    r[i] = c[i].dat[0]*c[i].dat[0] + c[i].dat[1]*c[i].dat[1];

  return 0;
}

int downsample_double(int n, int xs, const double* x, int rs, double* r)
{
  if (rs != xs/n) return 2000; // BAD_SIZE

  int i;

  for (i = 0; i < rs; i++)
    r[i] = x[i*n];

  return 0;
}

int downsample_float(int n, int xs, const float* x, int rs, float* r)
{
  if (rs != xs/n) return 2000; // BAD_SIZE

  int i;

  for (i = 0; i < rs; i++)
    r[i] = x[i*n];

  return 0;
}

int vector_diff_double(int xs, const double* x, int rs, double* r)
{
  if (rs != xs - 1) return 2000; // BAD_SIZE

  int i;

  for (i = 0; i < rs; i++)
    r[i] = x[i+1] - x[i];

  return 0;
}

int vector_diff_float(int xs, const float* x, int rs, float* r)
{
  if (rs != xs - 1) return 2000; // BAD_SIZE

  int i;

  for (i = 0; i < rs; i++)
    r[i] = x[i+1] - x[i];

  return 0;
}

int unwrap_double(int xs, const double* x, int rs, double* r)
{
  if (rs != xs) return 2000; // BAD_SIZE

  int i;

  r[0] = x[0];

  double c = 0;

  int tmp;

  for (i = 1; i < rs; i++) {
    tmp = x[i-1] - x[i];
    if (tmp > M_PI) {
      r[i] = 2*M_PI;
    }
    else if (tmp < (-M_PI)) {
      r[i] = -2*M_PI;
    }
    else {
      r[i] = 0;
    }
    c += r[i];
    r[i] = c + x[i];
  }

  return 0;
}

int unwrap_float(int xs, const float* x, int rs, float* r)
{
  if (rs != xs) return 2000; // BAD_SIZE

  int i;

  r[0] = x[0];

  double c = 0;

  int tmp;

  for (i = 1; i < rs; i++) {
    tmp = x[i-1] - x[i];
    if (tmp > M_PI) {
      r[i] = 2*M_PI;
    }
    else if (tmp < (-M_PI)) {
      r[i] = -2*M_PI;
    }
    else {
      r[i] = 0;
    }
    c += r[i];
    r[i] = c + x[i];
  }

  return 0;
}

int cross_covariance_double(int max_lag,
			    double* sx,
			    double* sy,
			    int xs, const double* x,
			    int ys, const double* y,
			    int rs, double* r)
{
  if (xs != ys)        return 2000; // BAD_SIZE
  if (rs != 2*max_lag) return 2000; // BAD_SIZE

  double mx = gsl_stats_mean(x,1,xs);
  double my = gsl_stats_mean(y,1,ys);

  *sx = gsl_stats_sd(x,1,xs);
  *sy = gsl_stats_sd(y,1,ys);

  int delay;
  int i, j;

  double sxy;

  for (delay=-max_lag; delay < max_lag; delay++) {
    sxy = 0;
    for (i=0;i<xs;i++) {
      j = i+delay;
      if (j < 0 || j >= xs) sxy += (x[i]-mx)*(-my);
      else sxy += (x[i]-mx)*(y[j]-my);
      /* or should it be:
      if (j < 0 || j >= xs) continue;
      else sxy += (x[i]-mx)*(y[j]-my);
      */
    }
    r[delay+max_lag] = sxy/xs;
  }

  return 0;
}

int cross_covariance_float(int max_lag,
			    float* sx,
			    float* sy,
			    int xs, const float* x,
			    int ys, const float* y,
			    int rs, double* r)
{
  if (xs != ys)        return 2000; // BAD_SIZE
  if (rs != 2*max_lag) return 2000; // BAD_SIZE

  float mx = gsl_stats_float_mean(x,1,xs);
  float my = gsl_stats_float_mean(y,1,ys);

  *sx = gsl_stats_float_sd(x,1,xs);
  *sy = gsl_stats_float_sd(y,1,ys);

  int delay;
  int i, j;

  float sxy;

  for (delay=-max_lag; delay < max_lag; delay++) {
    sxy = 0;
    for (i=0;i<xs;i++) {
      j = i+delay;
      if (j < 0 || j >= xs) sxy += (x[i]-mx)*(-my);
      else sxy += (x[i]-mx)*(y[j]-my);
      /* or should it be:
      if (j < 0 || j >= xs) continue;
      else sxy += (x[i]-mx)*(y[j]-my);
      */
    }
    r[delay+max_lag] = sxy/xs;
  }

  return 0;
}

int cum_sum_double(int xs, const double* x, int rs, double* r)
{
  int i;

  r[0] = x[0];

  for (i=1;i<xs;i++) {
    r[i] = r[i-1]+x[i];
  }

  return 0;
}

int cum_sum_float(int xs, const float* x, int rs, float* r)
{
  int i;

  r[0] = x[0];

  for (i=1;i<xs;i++) {
    r[i] = r[i-1]+x[i];
  }

  return 0;
}


#include <stdio.h>
#include <math.h>
#include <gbpLib.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_deriv.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_min.h>
#include <gbpInterpolate.h>

double interpolate_maximum_function(double x, void *params);
double interpolate_maximum_function(double x, void *params) {
    return (-interpolate((interp_info *)params, x));
}

void interpolate_maximum(interp_info *interp,
                         double       x_lo_in,
                         double       x_guess_in,
                         double       x_hi_in,
                         double       threshold,
                         double *     x_maxima,
                         double *     y_maxima) {
    double                         x_lo;
    double                         x_hi;
    double                         x_guess;
    double                         r_val;
    const gsl_min_fminimizer_type *T;
    gsl_min_fminimizer *           s;
    gsl_function                   F;
    int                            status;
    int                            iter = 0;
    int                            max_iter;

    x_lo       = x_lo_in;
    x_guess    = x_guess_in;
    x_hi       = x_hi_in;
    max_iter   = GBP_MAX(100, interp->n);
    F.function = interpolate_maximum_function;
    F.params   = (void *)interp;
    T          = gsl_min_fminimizer_brent;
    s          = gsl_min_fminimizer_alloc(T);
    gsl_min_fminimizer_set(s, &F, x_guess, x_lo, x_hi);

    do {
        iter++;
        status  = gsl_min_fminimizer_iterate(s);
        x_lo    = gsl_min_fminimizer_x_lower(s);
        x_guess = gsl_min_fminimizer_x_minimum(s);
        x_hi    = gsl_min_fminimizer_x_upper(s);
        status  = gsl_min_test_interval(x_lo, x_hi, 0., threshold);
    } while(status == GSL_CONTINUE && iter < max_iter);
    gsl_min_fminimizer_free(s);
    (*x_maxima) = x_guess;
    (*y_maxima) = interpolate(interp, x_guess);
}