#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_blas.h>

#define RESOLUTION 28  // bmp image 28*28
#define LINE_LEN (RESOLUTION * RESOLUTION * 4) // All the pixel data stored within a line
#define DATA_NUM_WITHOUT_LABEL (RESOLUTION * RESOLUTION) // Data length without label
#define DATA_NUM_WITH_LABEL ((RESOLUTION * RESOLUTION) + 1) // For training, the first number is label, so +1
#define HIDDEN_NODE 250 // node number in hidden layer
#define OUTPUT_NODE 10  // output node number
#define LEARN_RATE 0.1
#define TRAIN_SET_SIZE 5000
#define TEST_SET_SIZE 1000
#define EPOCH 8

#define SIGMOID(x) (1.0 / (1.0 + (pow(M_E, -x))))
#define NOR_DIS_DEV(x) (pow(x, -0.5))  // For generating init weight

FILE* openfile(char* file_name);
void read_parse(FILE* fp, int input_data[], int* label, char* mode);
gsl_matrix* scale_input(int input_data[]);
gsl_matrix* vectorize_target(int label);
gsl_matrix* init_weight(int row, int col);
double get_output_error(gsl_matrix* target, gsl_matrix* output, gsl_matrix* output_error);
void training(gsl_matrix* better_input, gsl_matrix* target, gsl_matrix* input_weight, gsl_matrix* hidden_weight, gsl_matrix* hidden_x, gsl_matrix* hidden_o, gsl_matrix* output_o, gsl_matrix* output_x, gsl_matrix* output_error, gsl_matrix* hidden_error, double* error);
void store_weight(gsl_matrix* input_weight, gsl_matrix* hidden_weight);
void load_weight(gsl_matrix* input_weight, gsl_matrix* hidden_weight);
int recognize(gsl_matrix* better_input, gsl_matrix* input_weight, gsl_matrix* hidden_weight, gsl_matrix* hidden_x, gsl_matrix* hidden_o, gsl_matrix* output_x, gsl_matrix* output_o);

int main(int argc, char **argv)
{
	int label;
	double error;
	double error_acc = 0.0;
	char* file_name;
	char* mode;
	FILE* fp;
	int input_data[DATA_NUM_WITHOUT_LABEL];
	gsl_matrix* better_input;
	gsl_matrix* target;
	gsl_matrix* output_error = gsl_matrix_alloc(OUTPUT_NODE, 1);
	gsl_matrix* hidden_error = gsl_matrix_alloc(HIDDEN_NODE, 1);
	gsl_matrix* output_x = gsl_matrix_alloc(OUTPUT_NODE, 1);
	gsl_matrix* output_o = gsl_matrix_alloc(OUTPUT_NODE, 1);
    gsl_matrix* hidden_x = gsl_matrix_alloc(HIDDEN_NODE, 1);
    gsl_matrix* hidden_o = gsl_matrix_alloc(HIDDEN_NODE, 1);
	gsl_matrix* input_weight;
	gsl_matrix* hidden_weight;

	if(argc < 3){
		fprintf(stderr, "Usage: %s [-t(raining), -r(cognizing), -e(poch)] [file_name]\n", argv[0]);
		exit(EXIT_FAILURE);
	}else{
		mode = argv[1];
		file_name = argv[2];
	}

	fp = openfile(file_name);

	if(!strcmp(mode, "-t")){
		//training
		input_weight = init_weight(HIDDEN_NODE, DATA_NUM_WITHOUT_LABEL); // initiate the input-hidden weight
    	hidden_weight = init_weight(OUTPUT_NODE, HIDDEN_NODE);  // initiate the hidden-output weight

    	int training_times = 0;
    	puts("Training started\n");
        for (int i = 0; i < EPOCH; i++){

			do{
				read_parse(fp, input_data, &label, mode);
				better_input = scale_input(input_data);
				target = vectorize_target(label);
				training(better_input, target, input_weight, hidden_weight, hidden_x, hidden_o, output_o, output_x, output_error, hidden_error, &error);
				training_times++;
                error_acc += error;

			}while(training_times < TRAIN_SET_SIZE);

			rewind(fp);  // back to the start of the input file, start epoch
			if(i == 0){
				printf("In the first training, Accumulative Error = %f\n", error_acc);
				puts("========Start Epoch=========");
			}else{
				printf("Epoch %d, Accumulative Error = %f\n", i, error_acc);
			}
			error_acc = 0.0;
            training_times = 0;

        }

        gsl_matrix_free(better_input), gsl_matrix_free(target), gsl_matrix_free(output_error), gsl_matrix_free(hidden_error), gsl_matrix_free(output_x), gsl_matrix_free(output_o);
        gsl_matrix_free(hidden_x), gsl_matrix_free(hidden_o);

        store_weight(input_weight, hidden_weight);
        puts("Training weight has been stored!");
        gsl_matrix_free(input_weight), gsl_matrix_free(hidden_weight);

	}

	if(!strcmp(mode, "-r")){
		input_weight = gsl_matrix_alloc(HIDDEN_NODE, DATA_NUM_WITHOUT_LABEL);
		hidden_weight = gsl_matrix_alloc(OUTPUT_NODE, HIDDEN_NODE);

		load_weight(input_weight, hidden_weight);

		int test_times = 0;
		int wrong_times = 0;
		int anwser;

		do{
            read_parse(fp, input_data, &label, mode);
			better_input = scale_input(input_data);
			anwser = recognize(better_input, input_weight, hidden_weight, hidden_x, hidden_o, output_x, output_o);

			printf("Label: %d, Guess: %d\n", label, anwser);

			if(anwser != label) wrong_times++;

			test_times++;

		}while(test_times < TEST_SET_SIZE);
		printf("Performance: %2f\n", (1 - ((float)wrong_times/(float)test_times)) * 100);
		gsl_matrix_free(better_input), gsl_matrix_free(target), gsl_matrix_free(output_error), gsl_matrix_free(hidden_error), gsl_matrix_free(output_x), gsl_matrix_free(output_o);
		gsl_matrix_free(hidden_x), gsl_matrix_free(hidden_o);
		gsl_matrix_free(input_weight), gsl_matrix_free(hidden_weight);

	}
        fclose(fp);
		return 0;
}

FILE* openfile(char* file_name)
{
	FILE* fp;
	if(!(fp = fopen(file_name, "r")))
		fprintf(stderr, "Can't open the file\n");
	return fp;
}

void read_parse(FILE* fp, int input_data[], int* label, char* mode)
{
	char line[LINE_LEN];
	char temp;
	int line_c = 0;
	int input_c = 0;
	char* token;

	while((temp = fgetc(fp)) != '\n'){
        if(temp == EOF) break;
        line[line_c++] = temp;
	}
    line[line_c] = '\0';
    line_c = 0;
	if(!strcmp(mode, "-t")){
		token = strtok(line, ",");
		*label = atoi(token);
		while(token != NULL){
			token = strtok(NULL, ",");
			if(token == NULL) break;
			input_data[input_c++] = atoi(token);
		}

	}
	if(!strcmp(mode, "-r")){
		token = strtok(line, ",");
		*label = atoi(token);
		while(token != NULL){
			token = strtok(NULL, ",");
			if(token == NULL) break;
			input_data[input_c++] = atoi(token);
		}
	}
}

gsl_matrix* scale_input(int input_data[])
{
    gsl_matrix* better_input = gsl_matrix_alloc(DATA_NUM_WITHOUT_LABEL, 1);
	for(int i = 0; i < DATA_NUM_WITHOUT_LABEL; i++){
		gsl_matrix_set(better_input, i, 0, ((double)input_data[i]/255.0*0.99)+0.01);
	}
    return better_input;
}

gsl_matrix* vectorize_target(int label)
{
	gsl_matrix* target = gsl_matrix_alloc(10, 1);

	for(int i = 0; i < 10; i++){
		if(i == label){
			gsl_matrix_set(target, i, 0, 0.99);
		}else{
			gsl_matrix_set(target, i, 0, 0.01);
		}
	}

	return target;
}

gsl_matrix* init_weight(int row, int col)
{
    gsl_matrix* input_weight = gsl_matrix_alloc(row, col);

    const gsl_rng_type * T;
    gsl_rng * r;
    double sigma = NOR_DIS_DEV(col);
    r = gsl_rng_alloc (gsl_rng_mt19937);
    for(int i = 0; i < row; i++){

        for(int j = 0; j < col; j++){
            gsl_matrix_set(input_weight, i, j, gsl_ran_gaussian(r, sigma));
        }
    }
    gsl_rng_free(r);
    return input_weight;
}

double get_output_error(gsl_matrix* target, gsl_matrix* output, gsl_matrix* output_error)
{
	double error = 0.0;
	for (int i = 0; i < OUTPUT_NODE; i++){
		gsl_matrix_set(output_error, i, 0, (gsl_matrix_get(target, i, 0) - gsl_matrix_get(output, i, 0)));
	}

	for (int i = 0; i < OUTPUT_NODE; i++){
		error += pow(gsl_matrix_get(output_error, i, 0), 2);
	}

	return error;
}

void training(gsl_matrix* better_input, gsl_matrix* target, gsl_matrix* input_weight, gsl_matrix* hidden_weight, gsl_matrix* hidden_x, gsl_matrix* hidden_o, gsl_matrix* output_o, gsl_matrix* output_x, gsl_matrix* output_error, gsl_matrix* hidden_error, double* error)
{
	gsl_matrix* delta_hidden_weight = gsl_matrix_alloc(OUTPUT_NODE, HIDDEN_NODE);
	gsl_matrix* delta_input_weight = gsl_matrix_alloc(HIDDEN_NODE, DATA_NUM_WITHOUT_LABEL);
	gsl_matrix* temp_hidden = gsl_matrix_alloc(OUTPUT_NODE, 1);
	gsl_matrix* temp_input = gsl_matrix_alloc(HIDDEN_NODE, 1);

	 gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, input_weight, better_input, 0.0, hidden_x);  // X = W*I
	 for(int i = 0; i < HIDDEN_NODE; i++){
	 	gsl_matrix_set(hidden_o, i, 0, SIGMOID(gsl_matrix_get(hidden_x, i, 0)));
	 }

	 gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, hidden_weight, hidden_o, 0.0, output_x);
	 for(int i = 0; i < OUTPUT_NODE; i++){
	 	gsl_matrix_set(output_o, i, 0, SIGMOID(gsl_matrix_get(output_x, i, 0)));
	 }

	 *error = get_output_error(target, output_o, output_error);
	 gsl_blas_dgemm(CblasTrans, CblasNoTrans, 1.0, hidden_weight, output_error, 0.0, hidden_error); // Error hidden

	 for(int i = 0; i < OUTPUT_NODE; i++){
	 	gsl_matrix_set(temp_hidden, i, 0, (LEARN_RATE * gsl_matrix_get(output_error, i, 0) * gsl_matrix_get(output_o, i, 0) * (1.0 - gsl_matrix_get(output_o, i, 0))));
	 }
	 gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0, temp_hidden, hidden_o, 0.0, delta_hidden_weight);

	 for(int i = 0; i < HIDDEN_NODE; i++){
	 	gsl_matrix_set(temp_input, i, 0, (LEARN_RATE * gsl_matrix_get(hidden_error, i, 0) * gsl_matrix_get(hidden_o, i, 0) * (1.0 - gsl_matrix_get(hidden_error, i, 0))));
	 }
	 gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0, temp_input, better_input, 0.0, delta_input_weight);

	 gsl_matrix_add(hidden_weight, delta_hidden_weight);
	 gsl_matrix_add(input_weight, delta_input_weight);

	 gsl_matrix_free(delta_input_weight);
	 gsl_matrix_free(delta_hidden_weight);
	 gsl_matrix_free(temp_input);
	 gsl_matrix_free(temp_hidden);

}

void store_weight(gsl_matrix* input_weight, gsl_matrix* hidden_weight)
{
	FILE* fp = fopen("Good_Weight", "w+");

	for(int i = 0; i < HIDDEN_NODE; i++){
		for(int j = 0; j < DATA_NUM_WITHOUT_LABEL; j++){

			fprintf(fp, "%f,", gsl_matrix_get(input_weight, i, j));
		}
	}

	fseek(fp, -1, SEEK_CUR);
	fputc('\n', fp);

	for(int i = 0; i < OUTPUT_NODE; i++){
		for(int j = 0; j < HIDDEN_NODE; j++){

			fprintf(fp, "%f,", gsl_matrix_get(hidden_weight, i, j));
		}
	}
	fseek(fp, -1, SEEK_CUR);
	fputc('\n', fp);

	fclose(fp);
}

void load_weight(gsl_matrix* input_weight, gsl_matrix* hidden_weight)
{
	FILE* fp = fopen("Good_Weight", "r");
	char num_temp[20];
	double array[HIDDEN_NODE*DATA_NUM_WITHOUT_LABEL];
	char c;
	int j = 0, k;

	if(fp == NULL){
		fprintf(stderr, "No Good_Weight here, Please training first\n");
		exit(EXIT_FAILURE);
	}

	while((c = fgetc(fp)) != '\n'){

		if(c == ','){
			k = 0;
			array[j++] = atof(num_temp);
			bzero(num_temp, sizeof(num_temp));
			continue;
		}
		num_temp[k++] = c;
	}

	array[j] = atof(num_temp);

	j = 0;

	for(int n = 0; n < HIDDEN_NODE; n++){
		for(int m = 0; m < DATA_NUM_WITHOUT_LABEL; m++){
			gsl_matrix_set(input_weight, n, m, array[j++]);
		}
	}

	j = 0;
	k = 0;
	while((c = fgetc(fp)) != '\n'){

		if(c == ','){
			k = 0;
			array[j++] = atof(num_temp);
			bzero(num_temp, sizeof(num_temp));
			continue;
		}
		num_temp[k++] = c;
	}

	array[j] = atof(num_temp);

	j = 0;

	for(int n = 0; n < OUTPUT_NODE; n++){
		for(int m = 0; m < HIDDEN_NODE; m++){
			gsl_matrix_set(hidden_weight, n, m, array[j++]);
		}
	}

	fclose(fp);
}

int recognize(gsl_matrix* better_input, gsl_matrix* input_weight, gsl_matrix* hidden_weight, gsl_matrix* hidden_x, gsl_matrix* hidden_o, gsl_matrix* output_x, gsl_matrix* output_o)
{
	size_t row, col;
	gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, input_weight, better_input, 0.0, hidden_x);  // X = W*I
	 for(int i = 0; i < HIDDEN_NODE; i++){
	 	gsl_matrix_set(hidden_o, i, 0, SIGMOID(gsl_matrix_get(hidden_x, i, 0)));
	 }

	gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, hidden_weight, hidden_o, 0.0, output_x);
	 for(int i = 0; i < OUTPUT_NODE; i++){
	 	gsl_matrix_set(output_o, i, 0, SIGMOID(gsl_matrix_get(output_x, i, 0)));
	 }

	 gsl_matrix_max_index(output_o, &row, &col);

	 return (int)row;
}


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

#include <gsl/gsl_math.h>
#include <gsl/gsl_interp2d.h>
#include <gsl/gsl_spline2d.h>

int
main()
{
  const gsl_interp2d_type *T = gsl_interp2d_bilinear;
  const size_t N = 100;             /* number of points to interpolate */
  const double xa[] = { 0.0, 1.0 }; /* define unit square */
  const double ya[] = { 0.0, 1.0 };
  const size_t nx = sizeof(xa) / sizeof(double); /* x grid points */
  const size_t ny = sizeof(ya) / sizeof(double); /* y grid points */
  double *za = malloc(nx * ny * sizeof(double));
  gsl_spline2d *spline = gsl_spline2d_alloc(T, nx, ny);
  gsl_interp_accel *xacc = gsl_interp_accel_alloc();
  gsl_interp_accel *yacc = gsl_interp_accel_alloc();
  size_t i, j;

  /* set z grid values */
  gsl_spline2d_set(spline, za, 0, 0, 0.0);
  gsl_spline2d_set(spline, za, 0, 1, 1.0);
  gsl_spline2d_set(spline, za, 1, 1, 0.5);
  gsl_spline2d_set(spline, za, 1, 0, 1.0);

  /* initialize interpolation */
  gsl_spline2d_init(spline, xa, ya, za, nx, ny);

  /* interpolate N values in x and y and print out grid for plotting */
  for (i = 0; i < N; ++i)
    {
      double xi = i / (N - 1.0);

      for (j = 0; j < N; ++j)
        {
          double yj = j / (N - 1.0);
          double zij = gsl_spline2d_eval(spline, xi, yj, xacc, yacc);

          printf("%f %f %f\n", xi, yj, zij);
        }
      printf("\n");
    }

  gsl_spline2d_free(spline);
  gsl_interp_accel_free(xacc);
  gsl_interp_accel_free(yacc);
  free(za);

  return 0;
}


#include <math.h>
#include <gsl/gsl_sf_gamma.h>
#include <galpy_potentials.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
//PowerSphericalPotentialwCutoff
//3  arguments: amp, alpha, rc
double mass(double r2,double alpha, double rc){
  return 2. * M_PI * pow ( rc , 3. - alpha ) * ( gsl_sf_gamma ( 1.5 - 0.5 * alpha ) - gsl_sf_gamma_inc ( 1.5 - 0.5 * alpha , r2 / rc / rc ) );
}
double PowerSphericalPotentialwCutoffEval(double R,double Z, double phi,
					  double t,
					  struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R+Z*Z;
  double r= sqrt(r2);
  return amp * 2. * M_PI * pow(rc,3.-alpha) / r * ( r / rc * ( gsl_sf_gamma ( 1. - 0.5 * alpha ) - gsl_sf_gamma_inc ( 1. - 0.5 * alpha , r2 / rc / rc ) ) - ( gsl_sf_gamma ( 1.5 - 0.5 * alpha ) - gsl_sf_gamma_inc ( 1.5 - 0.5 * alpha , r2 / rc / rc) ) );
}
double PowerSphericalPotentialwCutoffRforce(double R,double Z, double phi,
					    double t,
					    struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R+Z*Z;
  //Calculate Rforce
  return - amp * mass (r2,alpha,rc) * R / pow(r2,1.5);
}
double PowerSphericalPotentialwCutoffPlanarRforce(double R,double phi,
						  double t,
						  struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R;
  //Calculate Rforce
  return - amp * mass (r2,alpha,rc) / r2;
}
double PowerSphericalPotentialwCutoffzforce(double R,double Z,double phi,
					    double t,
					    struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R+Z*Z;
  //Calculate Rforce
  return - amp * mass (r2,alpha,rc) * Z / pow(r2,1.5);
}
double PowerSphericalPotentialwCutoffPlanarR2deriv(double R,double phi,
						   double t,
						   struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R;
  //Calculate R2deriv
  return amp * ( 4. * M_PI * pow(r2,- 0.5 * alpha) * exp(-r2/rc/rc) - 2. * mass(r2,alpha,rc)/pow(r2,1.5) );
}


/* gsl_sf_hermite.h
 *
 * Copyright (C) 2011-2014 Konrad Griessinger
 *
 * 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.
 */

/*----------------------------------------------------------------------*
 * (konradg(at)gmx.net)                                                 *
 *----------------------------------------------------------------------*/

#ifndef __GSL_SF_HERMITE_H__
#define __GSL_SF_HERMITE_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

GSL_FUN int gsl_sf_hermite_prob_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob(const int n, const double x);
GSL_FUN int gsl_sf_hermite_prob_deriv_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob_deriv(const int m, const int n, const double x);
GSL_FUN int gsl_sf_hermite_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite(const int n, const double x);
GSL_FUN int gsl_sf_hermite_deriv_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_deriv(const int m, const int n, const double x);
GSL_FUN int gsl_sf_hermite_func_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func(const int n, const double x);
GSL_FUN int gsl_sf_hermite_func_fast_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func_fast(const int n, const double x);
GSL_FUN int gsl_sf_hermite_prob_array(const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_array_deriv(const int m, const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_deriv_array(const int mmax, const int n, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_series_e(const int n, const double x, const double * a, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob_series(const int n, const double x, const double * a);
GSL_FUN int gsl_sf_hermite_array(const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_array_deriv(const int m, const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_deriv_array(const int mmax, const int n, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_series_e(const int n, const double x, const double * a, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_series(const int n, const double x, const double * a);
GSL_FUN int gsl_sf_hermite_func_array(const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_func_series_e(const int n, const double x, const double * a, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func_series(const int n, const double x, const double * a);
GSL_FUN int gsl_sf_hermite_func_der_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func_der(const int m, const int n, const double x);
GSL_FUN int gsl_sf_hermite_prob_zero_e(const int n, const int s, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob_zero(const int n, const int s);
GSL_FUN int gsl_sf_hermite_zero_e(const int n, const int s, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_zero(const int n, const int s);
GSL_FUN int gsl_sf_hermite_func_zero_e(const int n, const int s, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func_zero(const int n, const int s);

#ifndef GSL_DISABLE_DEPRECATED

GSL_FUN int gsl_sf_hermite_phys_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_phys(const int n, const double x);
GSL_FUN int gsl_sf_hermite_phys_der_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_phys_der(const int m, const int n, const double x);
GSL_FUN int gsl_sf_hermite_phys_array(const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_phys_series_e(const int n, const double x, const double * a, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_phys_series(const int n, const double x, const double * a);
GSL_FUN int gsl_sf_hermite_phys_array_der(const int m, const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_phys_der_array(const int mmax, const int n, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_phys_zero_e(const int n, const int s, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_phys_zero(const int n, const int s);

GSL_FUN int gsl_sf_hermite_prob_array_der(const int m, const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_der_array(const int mmax, const int n, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_der_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob_der(const int m, const int n, const double x);

#endif /* !GSL_DISABLE_DEPRECATED */

__END_DECLS

#endif /* __GSL_SF_HERMITE_H__ */


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <time.h>
#define USAGE "./mcmc_loktavolterra.x n_steps n_burn"

void rg4th(float *resultado, float x, float y, float a, float b, float c, float d, float h);
void x_prime(float *valor, float x, float y, float alpha, float beta);
void y_prime(float *valor, float x, float y, float gamma, float delta);

void my_model(float *mine_presas,float *mine_depredadores,float *tiempo,float ini_presas, float ini_depredadores,float a, float b, float c, float d, int n_puntos);
void likelihood(float *chi,float *presas,float *depredadores,float *mine_presas,float *mine_depredadores,int n_puntos);
void mcmc(int n_steps,float *a,float *b,float *c,float *d,float *l,float *tiempo,float *presas,float *depredadores,int n_puntos,float *mine_presas,float *mine_depredadores, float ini_presas, float ini_depredadores);
void min(float *mine_presas,float *mine_depredadores,float ini_presas,float ini_depredadores,float *tiempo,float *a,float *b,float *c,float *d,float *l,int n_steps,int n_puntos);

void leer(float *tiempo, float *presas, float *depredadores, int n_puntos);
float *reserva(int n_puntos);
void print_array(float *a, float *b, float *c, float *d , float *l, int n_steps, int n_burn);

int main(int argc, char **argv){

	srand48(time(NULL));

	//Valores que entran
	int n_steps=atof(argv[1]);
	int n_burn=atof(argv[2]);
	//Inicializacion de los arrays que guardan los datos
	int n_puntos= 96;
	float *tiempo;
	float *presas;
	float *depredadores;
	tiempo=reserva(n_puntos);
	presas=reserva(n_puntos);
	depredadores=reserva(n_puntos);

	leer(tiempo, presas, depredadores, n_puntos);
	//Condiciones iniciales para resolver las ecuaciones diferenciales
	float ini_presas, ini_depredadores;
	ini_presas=presas[0];
	ini_depredadores=depredadores[0];

	//Array con los resultados del ajuste
	float *mine_presas;
	mine_presas=reserva(n_puntos);
	float *mine_depredadores;
	mine_depredadores=reserva(n_puntos);

	//Arrays que guardan los datos de las variables
	float *a; //alpha
	float *b; //beta
	float *c; //gamma
	float *d; //delta
	float *l;
	a=reserva(n_steps);
	b=reserva(n_steps);
	c=reserva(n_steps);
	d=reserva(n_steps);
	l=reserva(n_steps);
	//Primer paso en la cadena
	a[0]=drand48();
	b[0]=drand48();
	c[0]=drand48();
	d[0]=drand48();
	float chi;
	my_model(mine_presas,mine_depredadores,tiempo,ini_presas,ini_depredadores,a[0],b[0],c[0],d[0],n_puntos);
	likelihood(&chi,presas,depredadores,mine_presas,mine_depredadores,n_puntos);
	l[0]=chi;

	mcmc(n_steps,a,b,c,d,l,tiempo,presas,depredadores,n_puntos,mine_presas,mine_depredadores,ini_presas,ini_depredadores);
	min(mine_presas,mine_depredadores,ini_presas,ini_depredadores,tiempo,a,b,c,d,l,n_steps,n_puntos);

	print_array(a,b,c,d,l,n_steps,n_burn);

	free(tiempo);
	free(presas);
	free(mine_presas);
	free(depredadores);
	free(mine_depredadores);
	free(a);
	free(b);
	free(c);
	free(d);

	return 0;
}

//OBTENCION DEL MEJOR AJUSTE
void min(float *mine_presas,float *mine_depredadores,float ini_presas,float ini_depredadores,float *tiempo,float *a,float *b,float *c,float *d,float *l,int n_steps,int n_puntos){
	int i;
	float temp, a2, b2, c2, d2;
	temp=l[0];
	a2=a[0];
	b2=b[0];
	c2=c[0];
	d2=d[0];
	for(i=1;i<n_steps;i++){
		if(temp > l[i] ){
			temp=l[i];
			a2=a[i];
			b2=b[i];
			c2=c[i];
			d2=d[i];
		}
	}
	my_model(mine_presas,mine_depredadores,tiempo,ini_presas,ini_depredadores,a2,b2,c2,d2,n_puntos);
}

//ITERACIONES
void mcmc(int n_steps,float *a,float *b,float *c,float *d,float *l,float *tiempo,float *presas,float *depredadores,int n_puntos,float *mine_presas,float *mine_depredadores, float ini_presas, float ini_depredadores){
	int i;
	float a2,b2,c2,d2,chi,chi2,alpha,beta;
	const gsl_rng_type * T;
	gsl_rng * r;
	gsl_rng_env_setup();
	T = gsl_rng_default;
	r = gsl_rng_alloc (T);

    for(i=0;i<(n_steps-1);i++){
		a2 = a[i] + gsl_ran_gaussian(r, 0.1);
		b2 = b[i] + gsl_ran_gaussian(r, 0.1);
		c2 = c[i] + gsl_ran_gaussian(r, 0.1);
		d2 = c[i] + gsl_ran_gaussian(r, 0.1);

		float *presas_init;
		float *presas_prime;
		presas_init=reserva(n_puntos);
		presas_prime=reserva(n_puntos);
		float *depredadores_init;
		float *depredadores_prime;
		depredadores_init=reserva(n_puntos);
		depredadores_prime=reserva(n_puntos);

		my_model(presas_init,depredadores_init,tiempo,ini_presas,ini_depredadores,a[i],b[i],c[i],d[i],n_puntos);
		likelihood(&chi,presas,depredadores,presas_init,depredadores_init,n_puntos);

		my_model(presas_prime,depredadores_prime,tiempo,ini_presas,ini_depredadores,a2,b2,c2,d2,n_puntos);
		likelihood(&chi2,presas,depredadores,presas_prime,depredadores_prime,n_puntos);

		alpha = -chi2 + chi;
		if(alpha >= 0.0){
			a[i+1]=a2;
			b[i+1]=b2;
			c[i+1]=c2;
			d[i+1]=d2;
			l[i+1]=chi2;
		}
		else{
			beta = drand48();
			if(alpha < -6000.0 ){
				a[i+1]=a[i];
				b[i+1]=b[i];
				c[i+1]=c[i];
				d[i+1]=d[i];
				l[i+1]=chi;
			}
			else if(beta <= exp(alpha)){
				a[i+1]=a2;
				b[i+1]=b2;
				c[i+1]=c2;
				d[i+1]=d2;
				l[i+1]=chi2;
			}
			else{
				a[i+1]=a[i];
				b[i+1]=b[i];
				c[i+1]=c[i];
				d[i+1]=d[i];
				l[i+1]=chi;
			}
		}

		free(presas_init);
		free(presas_prime);
		free(depredadores_init);
		free(depredadores_prime);
	}

  gsl_rng_free (r);
}

//IMPRESION DE LOS ARRAYS
void print_array(float *a, float *b, float *c, float *d , float *l, int n_steps, int n_burn){
  int i;
  for(i=n_burn;i<n_steps;i++){
    printf("%f %f %f %f %f\n", a[i], b[i], c[i] , d[i], l[i]);
  }
}

//DEFINICION DEL CALCULO DEL AJUSTE
void my_model(float *mine_presas,float *mine_depredadores,float *tiempo,float ini_presas, float ini_depredadores,float a, float b, float c, float d, int n_puntos){

	//Condiciones iniciales
	mine_presas[0] = ini_presas;
	mine_depredadores[0] = ini_depredadores;

	//Recorrido R-K
	int i;
	for(i=1;i<n_puntos;i++){
		float *r;
		float h;
		h = tiempo[i]-tiempo[i-1];
		r=reserva(2);
		rg4th(r, mine_presas[i-1], mine_depredadores[i-1], a, b, c, d, h);
		mine_presas[i]=r[0];
		mine_depredadores[i]=r[1];
		free(r);
	}
}

//RUNGE-KUTTA DE CUARTO ORDEN
void rg4th(float *resultado, float x, float y, float a, float b, float c, float d, float h){
    float k1_x,k1_y;
    x_prime(&k1_x,x,y,a,b);
    y_prime(&k1_y,x,y,c,d);

    //First step
    float x1,y1;
    x1 = x + (h/2.0) * k1_x;
    y1 = y + (h/2.0) * k1_y;

	float k2_x,k2_y;
    x_prime(&k2_x,x1,y1,a,b);
    y_prime(&k2_y,x1,y1,c,d);

    //Second step
    float x2,y2;
    x2 = x + (h/2.0) * k2_x;
    y2 = y + (h/2.0) * k2_y;

	float k3_x,k3_y;
	x_prime(&k3_x,x2,y2,a,b);
    y_prime(&k3_y,x2,y2,c,d);

    //Third step
    float x3,y3;
    x3 = x + h * k3_x;
    y3 = y + h * k3_y;

	float k4_x,k4_y;
	x_prime(&k4_x,x3,y3,a,b);
    y_prime(&k4_y,x3,y3,c,d);

    //Fourth step
    float average_x, average_y;
    average_x = (1.0/6.0)*(k1_x + 2.0*k2_x + 2.0*k3_x + k4_x);
    average_y = (1.0/6.0)*(k1_y + 2.0*k2_y + 2.0*k3_y + k4_y);

    resultado[0] = x + h * average_x;
    resultado[1] = y + h * average_y;
}

//ECUACION DIFERENCIAL DE PRESAS
void x_prime(float *valor, float x, float y, float alpha, float beta){
	*valor= x * (alpha - beta * y);
}

//ECUACION DIFERENCIAL DE DEPREDADORES
void y_prime(float *valor, float x, float y, float gamma, float delta){
	*valor = -1 * y * (gamma - delta * x);
}

//RESERVA DEL AJUSTE
float *reserva(int n_puntos){
  float *array;
  int i;
  if(!(array = malloc(n_puntos * sizeof(float)))){
    printf("Problema en reserva\n");
    exit(1);
  }
  for(i=0;i<n_puntos;i++){
    array[i] = 0.0;
  }
  return array;
}

//CALCULO DE LA VARIACION DEL AJUSTE
void likelihood(float *chi,float *presas,float *depredadores,float *mine_presas,float *mine_depredadores,int n_puntos){
	int i;
	float temp1=0;
	float temp2=0;
	for(i=0;i<n_puntos;i++){
		temp1=temp1+pow(presas[i]-mine_presas[i],2.0);
		temp2=temp2+pow(depredadores[i]-mine_depredadores[i],2.0);
	}
	*chi=(1.0/2.0)*(temp1+temp2);
}

//LECTURA DEL ARCHIVO
void leer(float *tiempo, float *presas, float *depredadores,int n_puntos){

	FILE *in;
	int i;
	in=fopen("archivo.dat","r");
	if(!in){
    printf("problems opening the file %s\n","archivo.dat");
    exit(1);
	}
	for(i=0;i<(n_puntos);i++){

		  fscanf(in, "%f %f %f\n", &tiempo[i], &presas[i], &depredadores[i]);

	}
	fclose(in);
}


#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_integration.h>

#include "allvars.h"
#include "proto.h"

static double logTimeBegin;
static double logTimeMax;

double drift_integ(double a, void *param)
{
  double h;

  h = hubble_function(a);

  return 1 / (h * a * a * a);
}

double gravkick_integ(double a, void *param)
{
  double h;

  h = hubble_function(a);

  return 1 / (h * a * a);
}

double hydrokick_integ(double a, void *param)
{
  double h;

  h = hubble_function(a);

  return 1 / (h * pow(a, 3 * GAMMA_MINUS1) * a);
}

double growthfactor_integ(double a, void *param)
{
  double s;

  s = hubble_function(a) / All.Hubble * sqrt(a * a * a);

  return pow(sqrt(a) / s, 3);
}

void init_drift_table(void)
{
#define WORKSIZE 100000
  int i;
  double result, abserr;

  /*---------- DEBUG ! ------------*/
#ifdef DARKENERGY_DEBUG
  FILE *FdDKfac;
  char mode[2], buf[200];

  strcpy(mode, "w");
  sprintf(buf, "%s%s", All.OutputDir, "driftkickfac.txt");
  FdDKfac = fopen(buf, mode);
  fprintf(FdDKfac, "i a drift GravKick HydroKick\n");
  /*---------- DEBUG ! ------------*/
#endif

  gsl_function F;
  gsl_integration_workspace *workspace;

  logTimeBegin = log(All.TimeBegin);
  logTimeMax = log(All.TimeMax);

  workspace = gsl_integration_workspace_alloc(WORKSIZE);

  for(i = 0; i < DRIFT_TABLE_LENGTH; i++)
    {
      F.function = &drift_integ;
      gsl_integration_qag(&F, exp(logTimeBegin), exp(logTimeBegin + ((logTimeMax - logTimeBegin) / DRIFT_TABLE_LENGTH) * (i + 1)), All.Hubble,	/* note: absolute error just a dummy */
			  1.0e-8, WORKSIZE, GSL_INTEG_GAUSS41, workspace, &result, &abserr);
      DriftTable[i] = result;

      F.function = &gravkick_integ;
      gsl_integration_qag(&F, exp(logTimeBegin), exp(logTimeBegin + ((logTimeMax - logTimeBegin) / DRIFT_TABLE_LENGTH) * (i + 1)), All.Hubble,	/* note: absolute error just a dummy */
			  1.0e-8, WORKSIZE, GSL_INTEG_GAUSS41, workspace, &result, &abserr);
      GravKickTable[i] = result;

      F.function = &hydrokick_integ;
      gsl_integration_qag(&F, exp(logTimeBegin), exp(logTimeBegin + ((logTimeMax - logTimeBegin) / DRIFT_TABLE_LENGTH) * (i + 1)), All.Hubble,	/* note: absolute error just a dummy */
			  1.0e-8, WORKSIZE, GSL_INTEG_GAUSS41, workspace, &result, &abserr);
      HydroKickTable[i] = result;

#ifdef DARKENERGY_DEBUG
  /*---------- DEBUG ! ------------*/
      fprintf(FdDKfac, "%d %e %e %e %e \n", i,
	      exp(logTimeBegin + ((logTimeMax - logTimeBegin) / DRIFT_TABLE_LENGTH) * (i + 1)), DriftTable[i],
	      GravKickTable[i], HydroKickTable[i]);
  /*---------- DEBUG ! ------------*/
#endif
    }

  gsl_integration_workspace_free(workspace);

#ifdef DARKENERGY_DEBUG
  /*---------- DEBUG ! ------------*/
  fclose(FdDKfac);
  /*---------- DEBUG ! ------------*/
#endif
}

/*! This function integrates the cosmological prefactor for a drift
 *   step between time0 and time1. The value returned is
 *  \f[ \int_{a_0}^{a_1} \frac{{\rm d}a}{H(a)}
 *  \f]
 *
 *  A lookup-table is used for reasons of speed.
 */
double get_drift_factor(int time0, int time1)
{
  double a1, a2, df1, df2, u1, u2;
  int i1, i2;
  static int last_time0 = -1, last_time1 = -1;
  static double last_value;

  if(time0 == last_time0 && time1 == last_time1)
    return last_value;

  /* note: will only be called for cosmological integration */

  a1 = logTimeBegin + time0 * All.Timebase_interval;
  a2 = logTimeBegin + time1 * All.Timebase_interval;

  if(logTimeMax > logTimeBegin)
    u1 = (a1 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u1 = 0;
  i1 = (int) u1;
  if(i1 >= DRIFT_TABLE_LENGTH)
    i1 = DRIFT_TABLE_LENGTH - 1;

  if(i1 <= 1)
    df1 = u1 * DriftTable[0];
  else
    df1 = DriftTable[i1 - 1] + (DriftTable[i1] - DriftTable[i1 - 1]) * (u1 - i1);

  if(logTimeMax > logTimeBegin)
    u2 = (a2 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u2 = 0;
  i2 = (int) u2;
  if(i2 >= DRIFT_TABLE_LENGTH)
    i2 = DRIFT_TABLE_LENGTH - 1;

  if(i2 <= 1)
    df2 = u2 * DriftTable[0];
  else
    df2 = DriftTable[i2 - 1] + (DriftTable[i2] - DriftTable[i2 - 1]) * (u2 - i2);

  last_time0 = time0;
  last_time1 = time1;

  return last_value = (df2 - df1);
}

double get_gravkick_factor(int time0, int time1)
{
  double a1, a2, df1, df2, u1, u2;
  int i1, i2;
  static int last_time0 = -1, last_time1 = -1;
  static double last_value;

  if(time0 == last_time0 && time1 == last_time1)
    return last_value;

  /* note: will only be called for cosmological integration */

  a1 = logTimeBegin + time0 * All.Timebase_interval;
  a2 = logTimeBegin + time1 * All.Timebase_interval;

  if(logTimeMax > logTimeBegin)
    u1 = (a1 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u1 = 0;
  i1 = (int) u1;
  if(i1 >= DRIFT_TABLE_LENGTH)
    i1 = DRIFT_TABLE_LENGTH - 1;

  if(i1 <= 1)
    df1 = u1 * GravKickTable[0];
  else
    df1 = GravKickTable[i1 - 1] + (GravKickTable[i1] - GravKickTable[i1 - 1]) * (u1 - i1);

  if(logTimeMax > logTimeBegin)
    u2 = (a2 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u2 = 0;
  i2 = (int) u2;
  if(i2 >= DRIFT_TABLE_LENGTH)
    i2 = DRIFT_TABLE_LENGTH - 1;

  if(i2 <= 1)
    df2 = u2 * GravKickTable[0];
  else
    df2 = GravKickTable[i2 - 1] + (GravKickTable[i2] - GravKickTable[i2 - 1]) * (u2 - i2);

  last_time0 = time0;
  last_time1 = time1;

  return last_value = (df2 - df1);
}

double get_hydrokick_factor(int time0, int time1)
{
  double a1, a2, df1, df2, u1, u2;
  int i1, i2;
  static int last_time0 = -1, last_time1 = -1;
  static double last_value;

  if(time0 == last_time0 && time1 == last_time1)
    return last_value;

  /* note: will only be called for cosmological integration */

  a1 = logTimeBegin + time0 * All.Timebase_interval;
  a2 = logTimeBegin + time1 * All.Timebase_interval;

  if(logTimeMax > logTimeBegin)
    u1 = (a1 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u1 = 0;
  i1 = (int) u1;
  if(i1 >= DRIFT_TABLE_LENGTH)
    i1 = DRIFT_TABLE_LENGTH - 1;

  if(i1 <= 1)
    df1 = u1 * HydroKickTable[0];
  else
    df1 = HydroKickTable[i1 - 1] + (HydroKickTable[i1] - HydroKickTable[i1 - 1]) * (u1 - i1);

  if(logTimeMax > logTimeBegin)
    u2 = (a2 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u2 = 0;
  i2 = (int) u2;
  if(i2 >= DRIFT_TABLE_LENGTH)
    i2 = DRIFT_TABLE_LENGTH - 1;

  if(i2 <= 1)
    df2 = u2 * HydroKickTable[0];
  else
    df2 = HydroKickTable[i2 - 1] + (HydroKickTable[i2] - HydroKickTable[i2 - 1]) * (u2 - i2);

  last_time0 = time0;
  last_time1 = time1;

  return last_value = (df2 - df1);
}


/* randist/binomial.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough
 *
 * 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.
 */

#include <config.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_sf_gamma.h>

/* The binomial distribution has the form,

   prob(k) =  n!/(k!(n-k)!) *  p^k (1-p)^(n-k) for k = 0, 1, ..., n

   This is the algorithm from Knuth */

unsigned int
gsl_ran_binomial (const gsl_rng * r, double p, unsigned int n)
{
  unsigned int i, a, b, k = 0;

  while (n > 10)	/* This parameter is tunable */
    {
      double X;
      a = 1 + (n / 2);
      b = 1 + n - a;

      X = gsl_ran_beta (r, (double) a, (double) b);

      if (X >= p)
	{
	  n = a - 1;
	  p /= X;
	}
      else
	{
	  k += a;
	  n = b - 1;
	  p = (p - X) / (1 - X);
	}
    }

  for (i = 0; i < n; i++)
    {
      double u = gsl_rng_uniform (r);
      if (u < p)
	k++;
    }

  return k;
}

double
gsl_ran_binomial_pdf (const unsigned int k, const double p,
		      const unsigned int n)
{
  if (k > n)
    {
      return 0 ;
    }
  else
    {
      double a = k;
      double b = n - k;
      double P;
      double Cnk = gsl_sf_choose (n, k) ;

      P = Cnk * pow (p, a) * pow (1 - p, b);

      return P;
    }
}


/* cdf/gsl_cdf.h
 *
 * Copyright (C) 2002 Jason H. Stover.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
 */

/* Author:  J. Stover */

#ifndef __GSL_CDF_H__
#define __GSL_CDF_H__

#include <gsl/gsl_types.h>

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

__BEGIN_DECLS

GSL_EXPORT double gsl_cdf_ugaussian_P (const double x);
GSL_EXPORT double gsl_cdf_ugaussian_Q (const double x);

GSL_EXPORT double gsl_cdf_ugaussian_Pinv (const double P);
GSL_EXPORT double gsl_cdf_ugaussian_Qinv (const double Q);

GSL_EXPORT double gsl_cdf_gaussian_P (const double x, const double sigma);
GSL_EXPORT double gsl_cdf_gaussian_Q (const double x, const double sigma);

GSL_EXPORT double gsl_cdf_gaussian_Pinv (const double P, const double sigma);
GSL_EXPORT double gsl_cdf_gaussian_Qinv (const double Q, const double sigma);

GSL_EXPORT double gsl_cdf_gamma_P (const double x, const double a, const double b);
GSL_EXPORT double gsl_cdf_gamma_Q (const double x, const double a, const double b);

GSL_EXPORT double gsl_cdf_gamma_Pinv (const double P, const double a, const double b);
GSL_EXPORT double gsl_cdf_gamma_Qinv (const double Q, const double a, const double b);

GSL_EXPORT double gsl_cdf_cauchy_P (const double x, const double a);
GSL_EXPORT double gsl_cdf_cauchy_Q (const double x, const double a);

GSL_EXPORT double gsl_cdf_cauchy_Pinv (const double P, const double a);
GSL_EXPORT double gsl_cdf_cauchy_Qinv (const double Q, const double a);

GSL_EXPORT double gsl_cdf_laplace_P (const double x, const double a);
GSL_EXPORT double gsl_cdf_laplace_Q (const double x, const double a);

GSL_EXPORT double gsl_cdf_laplace_Pinv (const double P, const double a);
GSL_EXPORT double gsl_cdf_laplace_Qinv (const double Q, const double a);

GSL_EXPORT double gsl_cdf_rayleigh_P (const double x, const double sigma);
GSL_EXPORT double gsl_cdf_rayleigh_Q (const double x, const double sigma);

GSL_EXPORT double gsl_cdf_rayleigh_Pinv (const double P, const double sigma);
GSL_EXPORT double gsl_cdf_rayleigh_Qinv (const double Q, const double sigma);

GSL_EXPORT double gsl_cdf_chisq_P (const double x, const double nu);
GSL_EXPORT double gsl_cdf_chisq_Q (const double x, const double nu);

GSL_EXPORT double gsl_cdf_chisq_Pinv (const double P, const double nu);
GSL_EXPORT double gsl_cdf_chisq_Qinv (const double Q, const double nu);

GSL_EXPORT double gsl_cdf_exponential_P (const double x, const double mu);
GSL_EXPORT double gsl_cdf_exponential_Q (const double x, const double mu);

GSL_EXPORT double gsl_cdf_exponential_Pinv (const double P, const double mu);
GSL_EXPORT double gsl_cdf_exponential_Qinv (const double Q, const double mu);

GSL_EXPORT double gsl_cdf_exppow_P (const double x, const double a, const double b);
GSL_EXPORT double gsl_cdf_exppow_Q (const double x, const double a, const double b);

GSL_EXPORT double gsl_cdf_tdist_P (const double x, const double nu);
GSL_EXPORT double gsl_cdf_tdist_Q (const double x, const double nu);

GSL_EXPORT double gsl_cdf_tdist_Pinv (const double P, const double nu);
GSL_EXPORT double gsl_cdf_tdist_Qinv (const double Q, const double nu);

GSL_EXPORT double gsl_cdf_fdist_P (const double x, const double nu1, const double nu2);
GSL_EXPORT double gsl_cdf_fdist_Q (const double x, const double nu1, const double nu2);

GSL_EXPORT double gsl_cdf_fdist_Pinv (const double P, const double nu1, const double nu2);
GSL_EXPORT double gsl_cdf_fdist_Qinv (const double Q, const double nu1, const double nu2);

GSL_EXPORT double gsl_cdf_beta_P (const double x, const double a, const double b);
GSL_EXPORT double gsl_cdf_beta_Q (const double x, const double a, const double b);

GSL_EXPORT double gsl_cdf_beta_Pinv (const double P, const double a, const double b);
GSL_EXPORT double gsl_cdf_beta_Qinv (const double Q, const double a, const double b);

GSL_EXPORT double gsl_cdf_flat_P (const double x, const double a, const double b);
GSL_EXPORT double gsl_cdf_flat_Q (const double x, const double a, const double b);

GSL_EXPORT double gsl_cdf_flat_Pinv (const double P, const double a, const double b);
GSL_EXPORT double gsl_cdf_flat_Qinv (const double Q, const double a, const double b);

GSL_EXPORT double gsl_cdf_lognormal_P (const double x, const double zeta, const double sigma);
GSL_EXPORT double gsl_cdf_lognormal_Q (const double x, const double zeta, const double sigma);

GSL_EXPORT double gsl_cdf_lognormal_Pinv (const double P, const double zeta, const double sigma);
GSL_EXPORT double gsl_cdf_lognormal_Qinv (const double Q, const double zeta, const double sigma);

GSL_EXPORT double gsl_cdf_gumbel1_P (const double x, const double a, const double b);
GSL_EXPORT double gsl_cdf_gumbel1_Q (const double x, const double a, const double b);

GSL_EXPORT double gsl_cdf_gumbel1_Pinv (const double P, const double a, const double b);
GSL_EXPORT double gsl_cdf_gumbel1_Qinv (const double Q, const double a, const double b);

GSL_EXPORT double gsl_cdf_gumbel2_P (const double x, const double a, const double b);
GSL_EXPORT double gsl_cdf_gumbel2_Q (const double x, const double a, const double b);

GSL_EXPORT double gsl_cdf_gumbel2_Pinv (const double P, const double a, const double b);
GSL_EXPORT double gsl_cdf_gumbel2_Qinv (const double Q, const double a, const double b);

GSL_EXPORT double gsl_cdf_weibull_P (const double x, const double a, const double b);
GSL_EXPORT double gsl_cdf_weibull_Q (const double x, const double a, const double b);

GSL_EXPORT double gsl_cdf_weibull_Pinv (const double P, const double a, const double b);
GSL_EXPORT double gsl_cdf_weibull_Qinv (const double Q, const double a, const double b);

GSL_EXPORT double gsl_cdf_pareto_P (const double x, const double a, const double b);
GSL_EXPORT double gsl_cdf_pareto_Q (const double x, const double a, const double b);

GSL_EXPORT double gsl_cdf_pareto_Pinv (const double P, const double a, const double b);
GSL_EXPORT double gsl_cdf_pareto_Qinv (const double Q, const double a, const double b);

GSL_EXPORT double gsl_cdf_logistic_P (const double x, const double a);
GSL_EXPORT double gsl_cdf_logistic_Q (const double x, const double a);

GSL_EXPORT double gsl_cdf_logistic_Pinv (const double P, const double a);
GSL_EXPORT double gsl_cdf_logistic_Qinv (const double Q, const double a);

GSL_EXPORT double gsl_cdf_binomial_P (const unsigned int k, const double p, const unsigned int n);
GSL_EXPORT double gsl_cdf_binomial_Q (const unsigned int k, const double p, const unsigned int n);

GSL_EXPORT double gsl_cdf_poisson_P (const unsigned int k, const double mu);
GSL_EXPORT double gsl_cdf_poisson_Q (const unsigned int k, const double mu);

GSL_EXPORT double gsl_cdf_geometric_P (const unsigned int k, const double p);
GSL_EXPORT double gsl_cdf_geometric_Q (const unsigned int k, const double p);

GSL_EXPORT double gsl_cdf_negative_binomial_P (const unsigned int k, const double p, const double n);
GSL_EXPORT double gsl_cdf_negative_binomial_Q (const unsigned int k, const double p, const double n);

GSL_EXPORT double gsl_cdf_pascal_P (const unsigned int k, const double p, const unsigned int n);
GSL_EXPORT double gsl_cdf_pascal_Q (const unsigned int k, const double p, const unsigned int n);

GSL_EXPORT double gsl_cdf_hypergeometric_P (const unsigned int k, const unsigned int n1,
                                            const unsigned int n2, const unsigned int t);
GSL_EXPORT double gsl_cdf_hypergeometric_Q (const unsigned int k, const unsigned int n1,
                                            const unsigned int n2, const unsigned int t);

__END_DECLS

#endif /* __GSL_CDF_H__ */


// Alessandro Casalino
//
// Compile with (Mac with default homebrew gsl 2.6) gcc-9 -O2 coupled_quintessence_evolve.c -o coupled_quintessence_evolve.exe -L/usr/local/Cellar/gsl/2.6/lib -I/usr/local/Cellar/gsl/2.6/include -lgsl
// Run with ./coupled_quintessence_evolve.exe

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spline.h>

// PHYSICAL PARAMETERS VALUES

// INITIAL CONDITIONS
// Initial value of the scale factor
double a_init = 1e-14;
// Final value of the scale factor
double a_end = 1.1;

// Values of fractional density for the cosmological matter today
double Omega_rad_0 = 8e-5;
double Omega_b_0 = 0.0486;//0.0490439;
double Omega_Lambda_0 = 0.;//0.6911;
double Omega_cdm_0 = 0.2589;//0.264201;

// Physical constants for output conversions
double _G_ = 6.67428e-11;                 /**< Newton constant in m^3/Kg/s^2 */
double _MPc_over_m_ = 3.085677581282e22;  // Conversion factor from Mpc to m
double _Gyr_over_Mpc_ = 3.06601394e2;     // Conversion factor from Gyr to Mpc
double _c_ = 2.99792458e8;                // Speed of light in m/s
double _H0_ = 67.74;                      // H0 of LCDM in Km/s/Mpc (h:0.6731)

double fourpiG = 0.5;

double T_CONF = 1.;

// COMPUTATION PARAMETERS VALUES

// Number of points used in the computation
int points = (int) 1e6;
// Max particle horizon value
double max_tau = 40.;
// Min particle horizon value
double min_tau = 1e-9;

// Raise this value to make the csv file smaller, but decreasing resolution
// The value inserted is the ratio between the number of values written in a full resolution file / decreased resolution file
int csv_resolution = 10;

// BISECTION PARAMTERS
// Bisection precision
double delta_bisection = 1e-2;

// Bisection target
//  1: mg_field
//  2: mg_field_p
//  3: potential_constant
int bisection_type = 3;

double bisection_min = 0.;
double bisection_max = 20.;

// Initial conditions (considered accordingly to the bisection_type choice)
double mg_field_bk_0 = 1e-20;
double mg_field_p_bk_0 = 1.;

// TEST MODE
// Provides some informations on the terminal and the .csv file during the computation (1: on, others: off)
int TEST_MODE = 1;

// Definition of the POTENTIAL
// For a list of the models see above
double mg_pot_const = 1.;
double mg_pot_exp = -0.5;

double mg_pot(const double mg_field_bk) {

  return mg_pot_const * pow(mg_field_bk, mg_pot_exp);

}

double mg_pot_p(const double mg_field_bk) {

  return mg_pot_exp * mg_pot_const * pow(mg_field_bk, mg_pot_exp - 1.);

}

// Definition of the COUPLING FUNCION
// For a list of the models see above
double mg_coupl_const = 0.1;

double mg_coupl(const double mg_field_bk) {

  // 2 factor to be consistent with hi_class units (8 pi G = 1)
  return mg_coupl_const * mg_field_bk * mg_field_bk;

}

double mg_coupl_p(const double mg_field_bk) {

  return 2.*mg_coupl_const * mg_field_bk;

}

double mg_coupl_pp(const double mg_field_bk) {

  return 2.*mg_coupl_const;

}

// Definitions of DIFFERENTIAL EQUATION system
// Need to have first order equations to use Runge Kutta
//
double a_p_rk4(const double a, const double a_p, const double mg_field_bk, const double mg_field_p_bk)
{
	return a_p;
}
double a_pp_rk4(const double a, const double a_p, const double mg_field_bk, const double mg_field_p_bk)
{
  double a3 = a * a * a;
  double rhoa3 = (Omega_cdm_0 + Omega_b_0) + (Omega_Lambda_0 * a3) + (Omega_rad_0 / a);
  double Pa3 = - (Omega_Lambda_0 * a3) + 1./3. * (Omega_rad_0 / a );

  return fourpiG / 3. * ( (rhoa3 - 3. * Pa3) - a * mg_field_p_bk * mg_field_p_bk * (1. + 3./2./fourpiG * mg_coupl_pp(mg_field_bk)) + 4. * a3 * mg_pot(mg_field_bk) + 3./2./fourpiG * a3 * mg_pot_p(mg_field_bk) * mg_coupl_p(mg_field_bk))/(1. + mg_coupl(mg_field_bk) + 3./4./fourpiG * mg_coupl_p(mg_field_bk) * mg_coupl_p(mg_field_bk));
}
double mg_field_bk_p_rk4(const double a, const double a_p, const double mg_field_bk, const double mg_field_p_bk)
{
	return mg_field_p_bk;
}
double mg_field_bk_pp_rk4(const double a, const double a_p, const double mg_field_bk, const double mg_field_p_bk)
{
  double a2 = a * a;
  double rhoa2 = (Omega_cdm_0 + Omega_b_0) / a + (Omega_Lambda_0 * a2) + (Omega_rad_0 / a / a);
  double Pa2 = - (Omega_Lambda_0 * a2) + 1./3. * (Omega_rad_0 / a / a );

	return - 2. * a_p/a * mg_field_p_bk + ( - mg_pot_p(mg_field_bk) * a2 * ( 1. + mg_coupl(mg_field_bk) ) + ( rhoa2 - 3. * Pa2 + 4. * a2 * mg_pot(mg_field_bk) - mg_field_p_bk * mg_field_p_bk * ( 1. + 3./2./fourpiG * mg_coupl_pp(mg_field_bk) ) ) /2. * mg_coupl_p(mg_field_bk) ) / (1. + mg_coupl(mg_field_bk) + 3./4./fourpiG * mg_coupl_p(mg_field_bk) * mg_coupl_p(mg_field_bk) );
}
double Hconf(const double a, const double mg_field_bk, const double mg_field_p_bk)
{
  double a2 = a * a;
  double rhoa2 = (Omega_cdm_0 + Omega_b_0) / a + (Omega_Lambda_0 * a2) + (Omega_rad_0 / a / a);

  return - mg_field_p_bk * mg_coupl_p(mg_field_bk)/(1. + mg_coupl(mg_field_bk))/2. + sqrt((2. * fourpiG / 3.) * (1. + mg_coupl(mg_field_bk)) * ( rhoa2 + (mg_field_p_bk * mg_field_p_bk / 2.) + (a2 * mg_pot(mg_field_bk)) ) + mg_field_p_bk * mg_field_p_bk/4. * mg_coupl_p(mg_field_bk) * mg_coupl_p(mg_field_bk))/(1. + mg_coupl(mg_field_bk));
}

// Integrand for the particle horizon integral
double particleHorizonIntegrand(double a, double mg_field_bk, double mg_field_p_bk)
{
	return 1. / ( a * Hconf(a, mg_field_bk, mg_field_p_bk) );
}
// Particle horizon integral step
double particleHorizon(const int i, double * a, double * mg_field_bk, double * mg_field_p_bk) {
  double h = a[i]-a[i-1];
  double fa = particleHorizonIntegrand(a[i-1],mg_field_bk[i-1],mg_field_p_bk[i-1]);
  double fb = particleHorizonIntegrand(a[i],mg_field_bk[i],mg_field_p_bk[i]);

  return h*(fa+fb)/2.;
}

int scan_for_a0 (double * a, double ae) {

  int i=0;

  for(i=0; a[i] <= ae && i < points; i++);

  return i;

}

// Function used to print results stored in vectors as a csv file
void csv(double * t, double * a, double * a_p, double * mg_field_bk, double * mg_field_p_bk, double * particleHorizonVec, char * filename) {

    FILE *fp;
    fp = fopen (filename, "w+");
    fprintf(fp, "%s, %s, %s, %s, %s, %s, %s, %s", "t", "a(t)", "H(t)/H0", "H_prime(t)/H0^2", "Omega_df", "Omega_r", "Omega_b", "Omega_cdm");
    fprintf(fp, ", %s", "omega_df");
    fprintf(fp, ", %s", "PH"); // Particle horizon
    fprintf(fp, ", %s", "mg_field");
    fprintf(fp, ", %s", "mg_field_p");
    if(TEST_MODE==1) fprintf(fp, ", %s", "H_check(t)/H0");
    fprintf(fp, "\n");

    // Time conversion factor
    // double tcf = 1./_H0_/(60.*60.*24.*365.*1e9)*_MPc_over_m_/1000.;

    int i = 0;
    double particleHorizonRes = 0.;

    for(i=1;i<points;i++){
      particleHorizonRes += particleHorizon(i,a,mg_field_bk,mg_field_p_bk);
      particleHorizonVec[i] = particleHorizonRes;
    }

    i = 0;

    int i_a0 = scan_for_a0(a,1.);
    double H0 = Hconf(a[i_a0],mg_field_bk[i_a0],mg_field_p_bk[i_a0]);

    int w1=0, w2=0, w3=0;

    while( a[i] <= a_end ){

      double H = Hconf(a[i],mg_field_bk[i],mg_field_p_bk[i]); // this is the Hubble constant with conformal time
      double H_prime = a_pp_rk4(a[i], a_p[i], mg_field_bk[i], mg_field_p_bk[i]) / a[i] - H * H; // this is Hubble prime with conformal time
      double phi_pp = mg_field_bk_pp_rk4(a[i], a_p[i], mg_field_bk[i], mg_field_p_bk[i]);

      double P_df   = - mg_pot(mg_field_bk[i]) + mg_field_p_bk[i] * mg_field_p_bk[i]/2./a[i]/a[i] + 1./(2.*fourpiG)*( mg_coupl(mg_field_bk[i])*(H*H+2.*H_prime) + (H*mg_field_p_bk[i]+phi_pp) * mg_coupl_p(mg_field_bk[i]) + mg_field_p_bk[i] * mg_field_p_bk[i] * mg_coupl_pp(mg_field_bk[i]))/a[i]/a[i];
      double rho_df = mg_pot(mg_field_bk[i]) + mg_field_p_bk[i] * mg_field_p_bk[i]/2./a[i]/a[i] - 3./2./fourpiG * ( H * mg_field_p_bk[i] * mg_coupl_p(mg_field_bk[i]) + H * H * mg_coupl(mg_field_bk[i]))/a[i]/a[i];

      double Omega_df  = 2. * fourpiG / 3. * a[i] * a[i] * rho_df /H /H;
      double Omega_rad = 2. * fourpiG / 3. * Omega_rad_0 /a[i] /a[i] /H /H;
      double Omega_b = 2. * fourpiG / 3. * Omega_b_0 /a[i] /H /H;
      double Omega_cdm = 2. * fourpiG / 3. * Omega_cdm_0 /a[i] /H /H;

      if(rho_df<0 && w1==0 && i>0){
        printf("WARNING: the energy density is negative at a = %e\n", a[i]);
        w1++;
      }
      if( (Omega_df+Omega_rad+Omega_b+Omega_cdm > 1.01 || Omega_df+Omega_rad+Omega_b+Omega_cdm < 0.99) && w2==0){
        printf("WARNING: the fractional density sum is %e at a = %e\n", Omega_df+Omega_rad+Omega_b+Omega_cdm,a[i]);
        w2++;
      }
      if( (Omega_df> 1. || Omega_rad > 1. || Omega_b > 1. || Omega_cdm > 1.) && w3==0){
        printf("WARNING: one fractional density is > 1 at a = %e\n", a[i]);
        w3++;
      }

      double omega_df = P_df/rho_df;

      if(T_CONF==0) H_prime = ( H_prime - H * H )/a[i]/a[i]; // this is Hubble prime with cosmological time
      if(T_CONF==0) H = H / a[i]; // this it the Hubble constant with cosmological time

      fprintf(fp, "%e, %e, %e, %e, %e, %e, %e, %e", t[i], a[i], H / H0, H_prime /H0 /H0, Omega_df, Omega_rad, Omega_b, Omega_cdm );
      fprintf(fp, ", %e", omega_df);

      fprintf(fp, ", %e", particleHorizonVec[i]);

      fprintf(fp, ", %e", mg_field_bk[i]);
      fprintf(fp, ", %e", mg_field_p_bk[i]);

      if(TEST_MODE==1){

        double H_test;

        H_test = a_p[i]/a[i];
        if(T_CONF==0) H_test = H_test / a[i];

        fprintf(fp, ", %e", H_test / sqrt(2. * fourpiG / 3. / (1. + mg_coupl(mg_field_bk[i_a0])) ));

      }

      fprintf(fp, "\n");

      if(a[i+csv_resolution]<= a_end){
        i=i+csv_resolution;
      }
      else{
        i++;
      }

    }

    fclose(fp);

}

// This divides an interval in a logarithmic scale of basis 10, and store results in input vector v
void logscale10 (double * v, double A, double B, int points){

    int i;

    double a = log10(A);
    double b = log10(B);

    double h = (b - a) / (points-1.0);

    for(i=0;i<points;i++){
        v[i] = pow(10.0, a + i * h);
    }

}

// This function is a temporal step of the Runge-Kutta 4 method:
// evolves the system for a time equal to dtau (h in the program)
void mg_rungekutta4bg(double * f, const double dtau)
{

  double a = f[1];
  double a_p = f[2];
  double mg_field_bk = f[3];
  double mg_field_p_bk = f[4];

	double k1a, k2a, k3a, k4a;
	double k1ap, k2ap, k3ap, k4ap;
	double k1f, k2f, k3f, k4f;
	double k1fp, k2fp, k3fp, k4fp;

	k1a 	= a_p_rk4             (a, a_p, mg_field_bk, mg_field_p_bk);
	k1ap 	= a_pp_rk4            (a, a_p, mg_field_bk, mg_field_p_bk);
	k1f 	= mg_field_bk_p_rk4   (a, a_p, mg_field_bk, mg_field_p_bk);
	k1fp 	= mg_field_bk_pp_rk4  (a, a_p, mg_field_bk, mg_field_p_bk);

	k2a 	= a_p_rk4             (a + k1a * dtau / 2., a_p + k1ap * dtau / 2., mg_field_bk + k1f * dtau / 2., mg_field_p_bk + k1fp * dtau / 2.);
	k2ap 	= a_pp_rk4            (a + k1a * dtau / 2., a_p + k1ap * dtau / 2., mg_field_bk + k1f * dtau / 2., mg_field_p_bk + k1fp * dtau / 2.);
	k2f 	= mg_field_bk_p_rk4   (a + k1a * dtau / 2., a_p + k1ap * dtau / 2., mg_field_bk + k1f * dtau / 2., mg_field_p_bk + k1fp * dtau / 2.);
	k2fp 	= mg_field_bk_pp_rk4  (a + k1a * dtau / 2., a_p + k1ap * dtau / 2., mg_field_bk + k1f * dtau / 2., mg_field_p_bk + k1fp * dtau / 2.);

	k3a 	= a_p_rk4             (a + k2a * dtau / 2., a_p + k2ap * dtau / 2., mg_field_bk + k2f * dtau / 2., mg_field_p_bk + k2fp * dtau / 2.);
	k3ap 	= a_pp_rk4            (a + k2a * dtau / 2., a_p + k2ap * dtau / 2., mg_field_bk + k2f * dtau / 2., mg_field_p_bk + k2fp * dtau / 2.);
	k3f 	= mg_field_bk_p_rk4   (a + k2a * dtau / 2., a_p + k2ap * dtau / 2., mg_field_bk + k2f * dtau / 2., mg_field_p_bk + k2fp * dtau / 2.);
	k3fp 	= mg_field_bk_pp_rk4  (a + k2a * dtau / 2., a_p + k2ap * dtau / 2., mg_field_bk + k2f * dtau / 2., mg_field_p_bk + k2fp * dtau / 2.);

	k4a 	= a_p_rk4             (a + k3a * dtau, a_p + k3ap * dtau, mg_field_bk + k3f * dtau, mg_field_p_bk + k3fp * dtau);
	k4ap 	= a_pp_rk4            (a + k3a * dtau, a_p + k3ap * dtau, mg_field_bk + k3f * dtau, mg_field_p_bk + k3fp * dtau);
	k4f 	= mg_field_bk_p_rk4   (a + k3a * dtau, a_p + k3ap * dtau, mg_field_bk + k3f * dtau, mg_field_p_bk + k3fp * dtau);
	k4fp 	= mg_field_bk_pp_rk4  (a + k3a * dtau, a_p + k3ap * dtau, mg_field_bk + k3f * dtau, mg_field_p_bk + k3fp * dtau);

	f[1] += dtau * (k1a + 2. * k2a + 2. * k3a + k4a) / 6.;
	f[2] += dtau * (k1ap + 2. * k2ap + 2. * k3ap + k4ap) / 6.;
	f[3] += dtau * (k1f + 2. * k2f + 2. * k3f + k4f) / 6.;
	f[4] += dtau * (k1fp + 2. * k2fp + 2. * k3fp + k4fp) / 6.;

}

// This function evolves the system with the Runge-Kutta 4 method until t_stop
double rk4(double * t, double * a, double * a_p, double * mg_field_bk, double * mg_field_p_bk, double Omega_f_0) {

    double f[5];
    int i = 0;

    // Call the function for a logarithmic scale of time
    // We don't start from t = 0 to avoid problems with quintessence potentials
    logscale10(t,min_tau,max_tau,points);

    while( i < points - 1 ){

      f[0] = t[i];
      f[1] = a[i];
      f[2] = a_p[i];
      f[3] = mg_field_bk[i];
      f[4] = mg_field_p_bk[i];

      mg_rungekutta4bg(f, t[i+1]-t[i]);

      a[i+1] = f[1];
      a_p[i+1] = f[2];
      mg_field_bk[i+1] = f[3];
      mg_field_p_bk[i+1] = f[4];
      i++;

    }

    i = scan_for_a0(a,1.);

    double H         = Hconf(a[i], mg_field_bk[i], mg_field_p_bk[i]);

    double rho_df = mg_pot(mg_field_bk[i]) + mg_field_p_bk[i] * mg_field_p_bk[i]/2./a[i]/a[i] - 3./2./fourpiG * ( H * mg_field_p_bk[i] * mg_coupl_p(mg_field_bk[i]) + H * H * mg_coupl(mg_field_bk[i]))/a[i]/a[i];
    double Omega_df  = 2. * fourpiG / 3. * a[i] * a[i] * rho_df /H /H;
    return Omega_df  - Omega_f_0;

}

double bisection (double min, double max, double * t, double * a, double * a_p, double * mg_field_bk, double * mg_field_p_bk, const double Omega_f_0, int type) {

  double result = 0.;

  double C = (max+min)/2.;

  if(type==1){

    printf("\t-> Bisection target: mg_field_bk.\n", result);
    if(TEST_MODE==1) printf("\n");

    while(fabs((max-min)/min)>delta_bisection){

      a_p[0] = a_init * Hconf(a[0], min, mg_field_p_bk[0]);
      mg_field_bk[0] = min;
      double rk4_min = rk4(t, a, a_p, mg_field_bk, mg_field_p_bk, Omega_f_0);
      a_p[0] = a_init * Hconf(a[0], C, mg_field_p_bk[0]);
      mg_field_bk[0] = C;
      double rk4_C = rk4(t, a, a_p, mg_field_bk, mg_field_p_bk, Omega_f_0);

      if(rk4_min*rk4_C>=0) {
        min=C;
      }
      else {
        max=C;
      }

      C=(max+min)/2.;

      if (TEST_MODE == 1) printf("TEST_MODE ON - min: %e , max: %e, C: %e, rk4_min: %e , rk4_C: %e \n", min, max, C, rk4_min, rk4_C);

    }

    result = (max+min)/2.;

    if(TEST_MODE==1) printf("\n");
    printf("\t-> Result of bisection method is mg_field_bk: %e (internal units).\n", result);

  }

  else if(type==2){

    printf("\t-> Bisection target: mg_field_p_bk.\n", result);
    if(TEST_MODE==1) printf("\n");

    while(fabs((max-min)/min)>delta_bisection){

      a_p[0] = a_init * Hconf(a[0], mg_field_bk[0], min);
      mg_field_p_bk[0] = min;
      double rk4_min = rk4(t, a, a_p, mg_field_bk, mg_field_p_bk, Omega_f_0);
      a_p[0] = a_init * Hconf(a[0], mg_field_bk[0], C);
      mg_field_p_bk[0] = C;
      double rk4_C = rk4(t, a, a_p, mg_field_bk, mg_field_p_bk, Omega_f_0);

      if(rk4_min*rk4_C>=0) {
        min=C;
      }
      else {
        max=C;
      }

      C=(max+min)/2.;

      if (TEST_MODE == 1) printf("TEST_MODE ON - min: %e , max: %e, C: %e, rk4_min: %e , rk4_C: %e \n", min, max, C, rk4_min, rk4_C);

    }

    result = (max+min)/2.;

    if(TEST_MODE==1) printf("\n");
    printf("\t-> Result of bisection method is mg_field_p_bk: %e (internal units).\n", result);

  }

  else if(type==3){

    printf("\t-> Bisection target: mg_pot_const.\n", result);
    if(TEST_MODE==1) printf("\n");

    while(fabs((max-min)/min)>delta_bisection){

      mg_pot_const = min;
      a_p[0] = a_init * Hconf(a[0], mg_field_bk[0], mg_field_p_bk[0]);
      double rk4_min = rk4(t, a, a_p, mg_field_bk, mg_field_p_bk, Omega_f_0);
      mg_pot_const = C;
      a_p[0] = a_init * Hconf(a[0], mg_field_bk[0], mg_field_p_bk[0]);
      double rk4_C = rk4(t, a, a_p, mg_field_bk, mg_field_p_bk, Omega_f_0);

      if(rk4_min*rk4_C>=0) {
        min=C;
      }
      else {
        max=C;
      }

      C=(max+min)/2.;

      if (TEST_MODE == 1) printf("TEST_MODE ON - min: %e , max: %e, C: %e, rk4_min: %e , rk4_C: %e \n", min, max, C, rk4_min, rk4_C);

    }

    result = (max+min)/2.;

    if(TEST_MODE==1) printf("\n");
    printf("\t-> Result of bisection method is mg_pot_const: %e (internal units).\n", result);

  }

  else{
    result = 0.;
  }

  return result;

}

int main() {

    double Omega_f_0 = 1. - Omega_Lambda_0 - Omega_rad_0 - Omega_b_0 - Omega_cdm_0;

    printf("\n\t\t----------------------------------------\n\n");

    // Definition of the vector needed for the evolution functions
    double * t; double * a; double * a_p; double * mg_field_bk; double * mg_field_p_bk; double * particleHorizonVec;
    t                   = (double *) malloc(sizeof(double) * points);
    a                   = (double *) malloc(sizeof(double) * points);
    a_p                 = (double *) malloc(sizeof(double) * points);
    mg_field_bk         = (double *) malloc(sizeof(double) * points);
    mg_field_p_bk       = (double *) malloc(sizeof(double) * points);
    particleHorizonVec  = (double *) malloc(sizeof(double) * points);

    if(!t||!a||!a_p||!mg_field_bk||!mg_field_p_bk||!particleHorizonVec){
      printf("Error! The memory cannot be allocated. The program will be terminated.\n");
      exit(1);
    }

    // Initial conditions (tau=0)
    a[0] = a_init;

    mg_field_bk[0] = mg_field_bk_0;
    mg_field_p_bk[0] = mg_field_p_bk_0;

    printf("\n Extended quintessence evolution\n");

    printf("\n Parameters used: \n");
    printf("\t- Potential constant: %e\n", mg_pot_const);
    printf("\t- Potential exponent: %e\n", mg_pot_exp);
    printf("\t- Function constant: %e\n", mg_coupl_const);

    printf(" Searching for best initial value for the dark fluid ... \n \n");

    double bisection_result = bisection(bisection_min, bisection_max, t, a, a_p, mg_field_bk, mg_field_p_bk, Omega_f_0,bisection_type);

    if (bisection_type==1) {
      mg_field_bk[0] = bisection_result;
      if(mg_field_bk[0] > bisection_max*(1.-0.05)) printf("\n\n WARNING: it seems that the initial value is too near the bounds! \n\n");
    }
    else if (bisection_type==2) {
      mg_field_p_bk[0] = bisection_result;
      if(mg_field_p_bk[0] > bisection_max*(1.-0.05)) printf("\n\n WARNING: it seems that the initial value is too near the bounds! \n\n");
    }
    else if (bisection_type==3) {
      mg_pot_const = bisection_result;
      if(mg_pot_const > bisection_max*(1.-0.05)) printf("\n\n WARNING: it seems that the initial value is too near the bounds! \n\n");
    }
    else {
      printf("No bisection type chosen. The evolution will be computed with provided initial conditions.");
    }

    a_p[0] = a_init * Hconf(a_init, mg_field_bk[0], mg_field_p_bk[0]);

    printf("\n\n Evolving the system ...\n");

    rk4(t, a, a_p, mg_field_bk, mg_field_p_bk, Omega_f_0);

    int last_int = scan_for_a0(a,a_end);
    int last_int_print = scan_for_a0(a,1.);

    if(last_int >= points-1) printf("\n\n WARNING: the a = a_end value seems not to be reached in the result vectors! \n\n");

    double t_age = 0.;
    int i = 0;
    for(i=2;i<last_int_print;i++){
      t_age += (a[i]+a[i-1])*(t[i]-t[i-1])/2.;
    }

    printf("\n Results:\n");
    printf("\t- H0: %f \n", a_p[last_int_print]/a[last_int_print]/sqrt(2. * fourpiG / 3.) );
    printf("\t- Age of the Universe: %f Gyr\n", sqrt(2. * fourpiG / 3.) * t_age/_H0_/(60.*60.*24.*365.*1e9)*_MPc_over_m_/1000.);//t[last_int_print]/((4*M_PI)/3.)
    if(TEST_MODE==1) printf("\t- Number of points: %d \n", last_int);

    char filename[50];
    sprintf (filename, "coupl_mg_bk.csv");

    csv(t, a, a_p, mg_field_bk, mg_field_p_bk, particleHorizonVec, filename);

    printf("\n The results are saved in '.csv' files.\n");

    if(TEST_MODE==1) printf("\n TEST_MODE ON: check the values of H in .csv file. They must be equal!");

    printf("\n\t\t----------------------------------------\n");

    free(a);free(a_p);free(mg_field_bk);free(mg_field_p_bk);free(particleHorizonVec);

    printf("\n");

    exit(0);

}


/* poly/test.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Brian Gough
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_test.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_poly.h>

int
main (void)
{
  const double eps = 100.0 * GSL_DBL_EPSILON;

  gsl_ieee_env_setup ();

  {
    double x, y;
    double c[3] = { 1.0, 0.5, 0.3 };
    x = 0.5;
    y = gsl_poly_eval (c, 3, x);
    gsl_test_rel (y, 1 + 0.5 * x + 0.3 * x * x, eps,
                  "gsl_poly_eval({1, 0.5, 0.3}, 0.5)");
  }

  {
    double x, y;
    double d[11] = { 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 };
    x = 1.0;
    y = gsl_poly_eval (d, 11, x);
    gsl_test_rel (y, 1.0, eps,
                  "gsl_poly_eval({1,-1, 1, -1, 1, -1, 1, -1, 1, -1, 1}, 1.0)");

  }

  /* Quadratic */

  {
    double x0, x1;

    int n = gsl_poly_solve_quadratic (4.0, -20.0, 26.0, &x0, &x1);

    gsl_test (n != 0, "gsl_poly_solve_quadratic, no roots, (2x - 5)^2 = -1");
  }

  {
    double x0, x1;

    int n = gsl_poly_solve_quadratic (4.0, -20.0, 25.0, &x0, &x1);

    gsl_test (n != 2, "gsl_poly_solve_quadratic, one root, (2x - 5)^2 = 0");
    gsl_test_rel (x0, 2.5, 1e-9, "x0, (2x - 5)^2 = 0");
    gsl_test_rel (x1, 2.5, 1e-9, "x1, (2x - 5)^2 = 0");
    gsl_test (x0 != x1, "x0 == x1, (2x - 5)^2 = 0");
  }

  {
    double x0, x1;

    int n = gsl_poly_solve_quadratic (4.0, -20.0, 21.0, &x0, &x1);

    gsl_test (n != 2, "gsl_poly_solve_quadratic, two roots, (2x - 5)^2 = 4");
    gsl_test_rel (x0, 1.5, 1e-9, "x0, (2x - 5)^2 = 4");
    gsl_test_rel (x1, 3.5, 1e-9, "x1, (2x - 5)^2 = 4");
  }

  {
    double x0, x1;

    int n = gsl_poly_solve_quadratic (4.0, 7.0, 0.0, &x0, &x1);

    gsl_test (n != 2, "gsl_poly_solve_quadratic, two roots, x(4x + 7) = 0");
    gsl_test_rel (x0, -1.75, 1e-9, "x0, x(4x + 7) = 0");
    gsl_test_rel (x1, 0.0, 1e-9, "x1, x(4x + 7) = 0");
  }

  {
    double x0, x1;

    int n = gsl_poly_solve_quadratic (5.0, 0.0, -20.0, &x0, &x1);

    gsl_test (n != 2,
              "gsl_poly_solve_quadratic, two roots b = 0, 5 x^2 = 20");
    gsl_test_rel (x0, -2.0, 1e-9, "x0, 5 x^2 = 20");
    gsl_test_rel (x1, 2.0, 1e-9, "x1, 5 x^2 = 20");
  }

  {
    double x0, x1;

    int n = gsl_poly_solve_quadratic (0.0, 3.0, -21.0, &x0, &x1);

    gsl_test (n != 1,
              "gsl_poly_solve_quadratic, one root (linear) 3 x - 21 = 0");
    gsl_test_rel (x0, 7.0, 1e-9, "x0, 3x - 21 = 0");
  }

  {
    double x0, x1;
    int n = gsl_poly_solve_quadratic (0.0, 0.0, 1.0, &x0, &x1);

    gsl_test (n != 0,
              "gsl_poly_solve_quadratic, no roots 1 = 0");
  }

  /* Cubic */

  {
    double x0, x1, x2;

    int n = gsl_poly_solve_cubic (0.0, 0.0, -27.0, &x0, &x1, &x2);

    gsl_test (n != 1, "gsl_poly_solve_cubic, one root, x^3 = 27");
    gsl_test_rel (x0, 3.0, 1e-9, "x0, x^3 = 27");
  }

  {
    double x0, x1, x2;

    int n = gsl_poly_solve_cubic (-51.0, 867.0, -4913.0, &x0, &x1, &x2);

    gsl_test (n != 3, "gsl_poly_solve_cubic, three roots, (x-17)^3=0");
    gsl_test_rel (x0, 17.0, 1e-9, "x0, (x-17)^3=0");
    gsl_test_rel (x1, 17.0, 1e-9, "x1, (x-17)^3=0");
    gsl_test_rel (x2, 17.0, 1e-9, "x2, (x-17)^3=0");
  }

  {
    double x0, x1, x2;

    int n = gsl_poly_solve_cubic (-57.0, 1071.0, -6647.0, &x0, &x1, &x2);

    gsl_test (n != 3,
              "gsl_poly_solve_cubic, three roots, (x-17)(x-17)(x-23)=0");
    gsl_test_rel (x0, 17.0, 1e-9, "x0, (x-17)(x-17)(x-23)=0");
    gsl_test_rel (x1, 17.0, 1e-9, "x1, (x-17)(x-17)(x-23)=0");
    gsl_test_rel (x2, 23.0, 1e-9, "x2, (x-17)(x-17)(x-23)=0");
  }

  {
    double x0, x1, x2;

    int n = gsl_poly_solve_cubic (-11.0, -493.0, +6647.0, &x0, &x1, &x2);

    gsl_test (n != 3,
              "gsl_poly_solve_cubic, three roots, (x+23)(x-17)(x-17)=0");
    gsl_test_rel (x0, -23.0, 1e-9, "x0, (x+23)(x-17)(x-17)=0");
    gsl_test_rel (x1, 17.0, 1e-9, "x1, (x+23)(x-17)(x-17)=0");
    gsl_test_rel (x2, 17.0, 1e-9, "x2, (x+23)(x-17)(x-17)=0");
  }

  {
    double x0, x1, x2;

    int n = gsl_poly_solve_cubic (-143.0, 5087.0, -50065.0, &x0, &x1, &x2);

    gsl_test (n != 3,
              "gsl_poly_solve_cubic, three roots, (x-17)(x-31)(x-95)=0");
    gsl_test_rel (x0, 17.0, 1e-9, "x0, (x-17)(x-31)(x-95)=0");
    gsl_test_rel (x1, 31.0, 1e-9, "x1, (x-17)(x-31)(x-95)=0");
    gsl_test_rel (x2, 95.0, 1e-9, "x2, (x-17)(x-31)(x-95)=0");
  }

  {
    double x0, x1, x2;

    int n = gsl_poly_solve_cubic (-109.0, 803.0, 50065.0, &x0, &x1, &x2);

    gsl_test (n != 3,
              "gsl_poly_solve_cubic, three roots, (x+17)(x-31)(x-95)=0");
    gsl_test_rel (x0, -17.0, 1e-9, "x0, (x+17)(x-31)(x-95)=0");
    gsl_test_rel (x1, 31.0, 1e-9, "x1, (x+17)(x-31)(x-95)=0");
    gsl_test_rel (x2, 95.0, 1e-9, "x2, (x+17)(x-31)(x-95)=0");
  }

  /* Quadratic with complex roots */

  {
    gsl_complex z0, z1;

    int n = gsl_poly_complex_solve_quadratic (4.0, -20.0, 26.0, &z0, &z1);

    gsl_test (n != 2,
              "gsl_poly_complex_solve_quadratic, 2 roots (2x - 5)^2 = -1");
    gsl_test_rel (GSL_REAL (z0), 2.5, 1e-9, "z0.real, (2x - 5)^2 = -1");
    gsl_test_rel (GSL_IMAG (z0), -0.5, 1e-9, "z0.imag, (2x - 5)^2 = -1");

    gsl_test_rel (GSL_REAL (z1), 2.5, 1e-9, "z1.real, (2x - 5)^2 = -1");
    gsl_test_rel (GSL_IMAG (z1), 0.5, 1e-9, "z1.imag, (2x - 5)^2 = -1");
  }

  {
    gsl_complex z0, z1;

    int n = gsl_poly_complex_solve_quadratic (4.0, -20.0, 25.0, &z0, &z1);

    gsl_test (n != 2,
              "gsl_poly_complex_solve_quadratic, one root, (2x - 5)^2 = 0");
    gsl_test_rel (GSL_REAL (z0), 2.5, 1e-9, "z0.real, (2x - 5)^2 = 0");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag (2x - 5)^2 = 0");
    gsl_test_rel (GSL_REAL (z1), 2.5, 1e-9, "z1.real, (2x - 5)^2 = 0");
    gsl_test_rel (GSL_IMAG (z1), 0.0, 1e-9, "z1.imag (2x - 5)^2 = 0");
    gsl_test (GSL_REAL (z0) != GSL_REAL (z1),
              "z0.real == z1.real, (2x - 5)^2 = 0");
    gsl_test (GSL_IMAG (z0) != GSL_IMAG (z1),
              "z0.imag == z1.imag, (2x - 5)^2 = 0");
  }

  {
    gsl_complex z0, z1;

    int n = gsl_poly_complex_solve_quadratic (4.0, -20.0, 21.0, &z0, &z1);

    gsl_test (n != 2,
              "gsl_poly_complex_solve_quadratic, two roots, (2x - 5)^2 = 4");
    gsl_test_rel (GSL_REAL (z0), 1.5, 1e-9, "z0.real, (2x - 5)^2 = 4");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag, (2x - 5)^2 = 4");
    gsl_test_rel (GSL_REAL (z1), 3.5, 1e-9, "z1.real, (2x - 5)^2 = 4");
    gsl_test_rel (GSL_IMAG (z1), 0.0, 1e-9, "z1.imag, (2x - 5)^2 = 4");
  }

  {
    gsl_complex z0, z1;

    int n = gsl_poly_complex_solve_quadratic (4.0, 7.0, 0.0, &z0, &z1);

    gsl_test (n != 2,
              "gsl_poly_complex_solve_quadratic, two roots, x(4x + 7) = 0");
    gsl_test_rel (GSL_REAL (z0), -1.75, 1e-9, "z0.real, x(4x + 7) = 0");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag, x(4x + 7) = 0");
    gsl_test_rel (GSL_REAL (z1), 0.0, 1e-9, "z1.real, x(4x + 7) = 0");
    gsl_test_rel (GSL_IMAG (z1), 0.0, 1e-9, "z1.imag, x(4x + 7) = 0");
  }

  {
    gsl_complex z0, z1;

    int n = gsl_poly_complex_solve_quadratic (5.0, 0.0, -20.0, &z0, &z1);

    gsl_test (n != 2,
              "gsl_poly_complex_solve_quadratic, two roots b = 0, 5 x^2 = 20");
    gsl_test_rel (GSL_REAL (z0), -2.0, 1e-9, "z0.real, 5 x^2 = 20");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag, 5 x^2 = 20");
    gsl_test_rel (GSL_REAL (z1), 2.0, 1e-9, "z1.real, 5 x^2 = 20");
    gsl_test_rel (GSL_IMAG (z1), 0.0, 1e-9, "z1.imag, 5 x^2 = 20");
  }

  {
    gsl_complex z0, z1;

    int n = gsl_poly_complex_solve_quadratic (5.0, 0.0, 20.0, &z0, &z1);

    gsl_test (n != 2,
              "gsl_poly_complex_solve_quadratic, two roots b = 0, 5 x^2 = -20");
    gsl_test_rel (GSL_REAL (z0), 0.0, 1e-9, "z0.real, 5 x^2 = -20");
    gsl_test_rel (GSL_IMAG (z0), -2.0, 1e-9, "z0.imag, 5 x^2 = -20");
    gsl_test_rel (GSL_REAL (z1), 0.0, 1e-9, "z1.real, 5 x^2 = -20");
    gsl_test_rel (GSL_IMAG (z1), 2.0, 1e-9, "z1.imag, 5 x^2 = -20");
  }

  {
    gsl_complex z0, z1;

    int n = gsl_poly_complex_solve_quadratic (0.0, 3.0, -21.0, &z0, &z1);

    gsl_test (n != 1,
              "gsl_poly_complex_solve_quadratic, one root (linear) 3 x - 21 = 0");

    gsl_test_rel (GSL_REAL (z0), 7.0, 1e-9, "z0.real, 3x - 21 = 0");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag, 3x - 21 = 0");
  }

  {
    gsl_complex z0, z1;

    int n = gsl_poly_complex_solve_quadratic (0.0, 0.0, 1.0, &z0, &z1);
    gsl_test (n != 0,
              "gsl_poly_complex_solve_quadratic, no roots 1 = 0");
  }

  /* Cubic with complex roots */

  {
    gsl_complex z0, z1, z2;

    int n = gsl_poly_complex_solve_cubic (0.0, 0.0, -27.0, &z0, &z1, &z2);

    gsl_test (n != 3, "gsl_poly_complex_solve_cubic, three root, x^3 = 27");
    gsl_test_rel (GSL_REAL (z0), -1.5, 1e-9, "z0.real, x^3 = 27");
    gsl_test_rel (GSL_IMAG (z0), -1.5 * sqrt (3.0), 1e-9,
                  "z0.imag, x^3 = 27");
    gsl_test_rel (GSL_REAL (z1), -1.5, 1e-9, "z1.real, x^3 = 27");
    gsl_test_rel (GSL_IMAG (z1), 1.5 * sqrt (3.0), 1e-9, "z1.imag, x^3 = 27");
    gsl_test_rel (GSL_REAL (z2), 3.0, 1e-9, "z2.real, x^3 = 27");
    gsl_test_rel (GSL_IMAG (z2), 0.0, 1e-9, "z2.imag, x^3 = 27");
  }

  {
    gsl_complex z0, z1, z2;

    int n = gsl_poly_complex_solve_cubic (-1.0, 1.0, 39.0, &z0, &z1, &z2);

    gsl_test (n != 3,
              "gsl_poly_complex_solve_cubic, three root, (x+3)(x^2-4x+13) = 0");
    gsl_test_rel (GSL_REAL (z0), -3.0, 1e-9, "z0.real, (x+3)(x^2+1) = 0");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag, (x+3)(x^2+1) = 0");
    gsl_test_rel (GSL_REAL (z1), 2.0, 1e-9, "z1.real, (x+3)(x^2+1) = 0");
    gsl_test_rel (GSL_IMAG (z1), -3.0, 1e-9, "z1.imag, (x+3)(x^2+1) = 0");
    gsl_test_rel (GSL_REAL (z2), 2.0, 1e-9, "z2.real, (x+3)(x^2+1) = 0");
    gsl_test_rel (GSL_IMAG (z2), 3.0, 1e-9, "z2.imag, (x+3)(x^2+1) = 0");
  }

  {
    gsl_complex z0, z1, z2;

    int n =
      gsl_poly_complex_solve_cubic (-51.0, 867.0, -4913.0, &z0, &z1, &z2);

    gsl_test (n != 3,
              "gsl_poly_complex_solve_cubic, three roots, (x-17)^3=0");
    gsl_test_rel (GSL_REAL (z0), 17.0, 1e-9, "z0.real, (x-17)^3=0");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag, (x-17)^3=0");
    gsl_test_rel (GSL_REAL (z1), 17.0, 1e-9, "z1.real, (x-17)^3=0");
    gsl_test_rel (GSL_IMAG (z1), 0.0, 1e-9, "z1.imag, (x-17)^3=0");
    gsl_test_rel (GSL_REAL (z2), 17.0, 1e-9, "z2.real, (x-17)^3=0");
    gsl_test_rel (GSL_IMAG (z2), 0.0, 1e-9, "z2.imag, (x-17)^3=0");
  }

  {
    gsl_complex z0, z1, z2;

    int n =
      gsl_poly_complex_solve_cubic (-57.0, 1071.0, -6647.0, &z0, &z1, &z2);

    gsl_test (n != 3,
              "gsl_poly_complex_solve_cubic, three roots, (x-17)(x-17)(x-23)=0");
    gsl_test_rel (GSL_REAL (z0), 17.0, 1e-9, "z0.real, (x-17)(x-17)(x-23)=0");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag, (x-17)(x-17)(x-23)=0");
    gsl_test_rel (GSL_REAL (z1), 17.0, 1e-9, "z1.real, (x-17)(x-17)(x-23)=0");
    gsl_test_rel (GSL_IMAG (z1), 0.0, 1e-9, "z1.imag, (x-17)(x-17)(x-23)=0");
    gsl_test_rel (GSL_REAL (z2), 23.0, 1e-9, "z2.real, (x-17)(x-17)(x-23)=0");
    gsl_test_rel (GSL_IMAG (z2), 0.0, 1e-9, "z2.imag, (x-17)(x-17)(x-23)=0");
  }

  {
    gsl_complex z0, z1, z2;

    int n =
      gsl_poly_complex_solve_cubic (-11.0, -493.0, +6647.0, &z0, &z1, &z2);

    gsl_test (n != 3,
              "gsl_poly_complex_solve_cubic, three roots, (x+23)(x-17)(x-17)=0");
    gsl_test_rel (GSL_REAL (z0), -23.0, 1e-9,
                  "z0.real, (x+23)(x-17)(x-17)=0");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag, (x+23)(x-17)(x-17)=0");
    gsl_test_rel (GSL_REAL (z1), 17.0, 1e-9, "z1.real, (x+23)(x-17)(x-17)=0");
    gsl_test_rel (GSL_IMAG (z1), 0.0, 1e-9, "z1.imag, (x+23)(x-17)(x-17)=0");
    gsl_test_rel (GSL_REAL (z2), 17.0, 1e-9, "z2.real, (x+23)(x-17)(x-17)=0");
    gsl_test_rel (GSL_IMAG (z2), 0.0, 1e-9, "z2.imag, (x+23)(x-17)(x-17)=0");
  }

  {
    gsl_complex z0, z1, z2;

    int n =
      gsl_poly_complex_solve_cubic (-143.0, 5087.0, -50065.0, &z0, &z1, &z2);

    gsl_test (n != 3,
              "gsl_poly_complex_solve_cubic, three roots, (x-17)(x-31)(x-95)=0");
    gsl_test_rel (GSL_REAL (z0), 17.0, 1e-9, "z0.real, (x-17)(x-31)(x-95)=0");
    gsl_test_rel (GSL_IMAG (z0), 0.0, 1e-9, "z0.imag, (x-17)(x-31)(x-95)=0");
    gsl_test_rel (GSL_REAL (z1), 31.0, 1e-9, "z1.real, (x-17)(x-31)(x-95)=0");
    gsl_test_rel (GSL_IMAG (z1), 0.0, 1e-9, "z1.imag, (x-17)(x-31)(x-95)=0");
    gsl_test_rel (GSL_REAL (z2), 95.0, 1e-9, "z2.real, (x-17)(x-31)(x-95)=0");
    gsl_test_rel (GSL_IMAG (z2), 0.0, 1e-9, "z2.imag, (x-17)(x-31)(x-95)=0");
  }

  {
    /* Wilkinson polynomial: y = (x-1)(x-2)(x-3)(x-4)(x-5) */

    double a[6] = { -120, 274, -225, 85, -15, 1 };
    double z[6*2];

    gsl_poly_complex_workspace *w = gsl_poly_complex_workspace_alloc (6);

    int status = gsl_poly_complex_solve (a, 6, w, z);

    gsl_poly_complex_workspace_free (w);

    gsl_test (status,
              "gsl_poly_complex_solve, 5th-order Wilkinson polynomial");
    gsl_test_rel (z[0], 1.0, 1e-9, "z0.real, 5th-order polynomial");
    gsl_test_rel (z[1], 0.0, 1e-9, "z0.imag, 5th-order polynomial");
    gsl_test_rel (z[2], 2.0, 1e-9, "z1.real, 5th-order polynomial");
    gsl_test_rel (z[3], 0.0, 1e-9, "z1.imag, 5th-order polynomial");
    gsl_test_rel (z[4], 3.0, 1e-9, "z2.real, 5th-order polynomial");
    gsl_test_rel (z[5], 0.0, 1e-9, "z2.imag, 5th-order polynomial");
    gsl_test_rel (z[6], 4.0, 1e-9, "z3.real, 5th-order polynomial");
    gsl_test_rel (z[7], 0.0, 1e-9, "z3.imag, 5th-order polynomial");
    gsl_test_rel (z[8], 5.0, 1e-9, "z4.real, 5th-order polynomial");
    gsl_test_rel (z[9], 0.0, 1e-9, "z4.imag, 5th-order polynomial");
  }

  {
    /* : 8-th order polynomial y = x^8 + x^4 + 1 */

    double a[9] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
    double z[8*2];

    double C = 0.5;
    double S = sqrt (3.0) / 2.0;

    gsl_poly_complex_workspace *w = gsl_poly_complex_workspace_alloc (9);

    int status = gsl_poly_complex_solve (a, 9, w, z);

    gsl_poly_complex_workspace_free (w);

    gsl_test (status, "gsl_poly_complex_solve, 8th-order polynomial");

    gsl_test_rel (z[0], -S, 1e-9, "z0.real, 8th-order polynomial");
    gsl_test_rel (z[1], C, 1e-9, "z0.imag, 8th-order polynomial");
    gsl_test_rel (z[2], -S, 1e-9, "z1.real, 8th-order polynomial");
    gsl_test_rel (z[3], -C, 1e-9, "z1.imag, 8th-order polynomial");
    gsl_test_rel (z[4], -C, 1e-9, "z2.real, 8th-order polynomial");
    gsl_test_rel (z[5], S, 1e-9, "z2.imag, 8th-order polynomial");
    gsl_test_rel (z[6], -C, 1e-9, "z3.real, 8th-order polynomial");
    gsl_test_rel (z[7], -S, 1e-9, "z3.imag, 8th-order polynomial");
    gsl_test_rel (z[8], C, 1e-9, "z4.real, 8th-order polynomial");
    gsl_test_rel (z[9], S, 1e-9, "z4.imag, 8th-order polynomial");
    gsl_test_rel (z[10], C, 1e-9, "z5.real, 8th-order polynomial");
    gsl_test_rel (z[11], -S, 1e-9, "z5.imag, 8th-order polynomial");
    gsl_test_rel (z[12], S, 1e-9, "z6.real, 8th-order polynomial");
    gsl_test_rel (z[13], C, 1e-9, "z6.imag, 8th-order polynomial");
    gsl_test_rel (z[14], S, 1e-9, "z7.real, 8th-order polynomial");
    gsl_test_rel (z[15], -C, 1e-9, "z7.imag, 8th-order polynomial");

  }

  {
    int i;

    double xa[7] = {0.16, 0.97, 1.94, 2.74, 3.58, 3.73, 4.70 };
    double ya[7] = {0.73, 1.11, 1.49, 1.84, 2.30, 2.41, 3.07 };

    double dd_expected[7] = {  7.30000000000000e-01,
                               4.69135802469136e-01,
                              -4.34737219941284e-02,
                               2.68681098870099e-02,
                              -3.22937056934996e-03,
                               6.12763259971375e-03,
                              -6.45402453527083e-03 };

    double dd[7], coeff[7], work[7];

    gsl_poly_dd_init (dd, xa, ya, 7);

    for (i = 0; i < 7; i++)
      {
        gsl_test_rel (dd[i], dd_expected[i], 1e-10, "divided difference dd[%d]", i);
      }

    for (i = 0; i < 7; i++)
      {
        double y = gsl_poly_dd_eval(dd, xa, 7, xa[i]);
        gsl_test_rel (y, ya[i], 1e-10, "divided difference y[%d]", i);
      }

    gsl_poly_dd_taylor (coeff, 1.5, dd, xa, 7, work);

    for (i = 0; i < 7; i++)
      {
        double y = gsl_poly_eval(coeff, 7, xa[i] - 1.5);
        gsl_test_rel (y, ya[i], 1e-10, "taylor expansion about 1.5 y[%d]", i);
      }
  }

  /* now summarize the results */

  exit (gsl_test_summary ());
}