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

#include <ndlinear/gsl_multifit_ndlinear.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_sf_laguerre.h>
#include <gsl/gsl_sf_legendre.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multifit.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_bspline.h>
#include <gsl/gsl_statistics.h>

/* dimension of fit */
#define N_DIM            3

/* number of basis functions for each variable */
#define N_SUM_R          10
#define N_SUM_THETA      10
#define N_SUM_PHI        9

#define R_MAX            3.0

double
psi_real_exact(int k, int l, int m, double r, double theta, double phi)
{
  double R, T, P;

  R = pow(r, (double) l) *
      exp(-r*r) *
      gsl_sf_laguerre_n(k, l + 0.5, 2 * r * r);
  T = gsl_sf_legendre_sphPlm(l, m, cos(theta));
  P = cos(m * phi);

  return (R * T * P);
}

/* basis functions for each variable */

int
basis_r(double r, double y[], void *params)
{
  gsl_bspline_workspace *bw = params;
  gsl_vector_view v = gsl_vector_view_array(y, N_SUM_R);
  int s;

  /* use B-splines for r dependence */
  s = gsl_bspline_eval(r, &v.vector, bw);

  return s;
}

int
basis_theta(double theta, double y[], void *params)
{
  size_t i;

  /* use Legendre polynomials for theta dependence */
  for (i = 0; i < N_SUM_THETA; ++i)
    y[i] = gsl_sf_legendre_Pl(i, cos(theta));

  return GSL_SUCCESS;
}

int
basis_phi(double phi, double y[], void *params)
{
  size_t i;

  /* use standard Fourier basis (sin/cos) for phi dependence */
  for (i = 0; i < N_SUM_PHI; ++i)
    {
      if ((i % 2) == 0)
        y[i] = cos((double)(i/2) * phi);
      else
        y[i] = sin((double)((i+1)/2) * phi);
    }

  return GSL_SUCCESS;
}

int
main(int argc, char *argv[])
{
  const size_t ndim = N_DIM; /* dimension of fit */
  const size_t ndata = 3000; /* number of data points to fit */
  size_t N[N_DIM];           /* upper bounds on model sums */
  int (*u[N_DIM])(double x, double y[], void *params);
  size_t i;                  /* looping */
  int k, l, m;               /* quantum numbers */
  gsl_rng *rng_p;
  gsl_bspline_workspace *bspline_p;
  gsl_multifit_linear_workspace *multifit_p;
  gsl_multifit_ndlinear_workspace *ndlinear_p;
  gsl_vector *data;          /* psi data */
  gsl_matrix *vars;          /* parameters corresponding to psi data */
  gsl_matrix *X;             /* matrix for least squares fit */
  gsl_vector *coeffs;        /* fit coefficients */
  gsl_matrix *cov;           /* covariance matrix */
  double chisq;              /* chi^2 */
  double Rsq;                /* R^2 */
  size_t ncoeffs;            /* total number of fit coefficients */

  gsl_rng_env_setup();

  k = 5;
  l = 4;
  m = 2;

  N[0] = N_SUM_R;
  N[1] = N_SUM_THETA;
  N[2] = N_SUM_PHI;

  u[0] = &basis_r;
  u[1] = &basis_theta;
  u[2] = &basis_phi;

  rng_p = gsl_rng_alloc(gsl_rng_default);
  bspline_p = gsl_bspline_alloc(4, N_SUM_R - 2);
  ndlinear_p = gsl_multifit_ndlinear_alloc(ndim, N, u, bspline_p);

  ncoeffs = gsl_multifit_ndlinear_ncoeffs(ndlinear_p);

  multifit_p = gsl_multifit_linear_alloc(ndata, ncoeffs);
  data = gsl_vector_alloc(ndata);
  vars = gsl_matrix_alloc(ndata, ndim);
  X = gsl_matrix_alloc(ndata, ncoeffs);
  coeffs = gsl_vector_alloc(ncoeffs);
  cov = gsl_matrix_alloc(ncoeffs, ncoeffs);

  gsl_bspline_knots_uniform(0.0, R_MAX, bspline_p);

  /* this is the data to be fitted */

  for (i = 0; i < ndata; ++i)
    {
      double r = gsl_rng_uniform(rng_p) * R_MAX;
      double theta = gsl_rng_uniform(rng_p) * M_PI;
      double phi = gsl_rng_uniform(rng_p) * 2.0 * M_PI;
      double psi = psi_real_exact(k, l, m, r, theta, phi);
      double dpsi = gsl_ran_gaussian(rng_p, 0.05 * psi);

      /* keep track of (r, theta, phi) points */
      gsl_matrix_set(vars, i, 0, r);
      gsl_matrix_set(vars, i, 1, theta);
      gsl_matrix_set(vars, i, 2, phi);

      /* fill in RHS data vector */
      gsl_vector_set(data, i, psi + dpsi);
    }

  /* construct the design matrix X */
  gsl_multifit_ndlinear_design(vars, X, ndlinear_p);

  /* now do the actual least squares fit */
  gsl_multifit_linear(X, data, coeffs, cov, &chisq, multifit_p);

  /* compute R^2 */
  Rsq = 1.0 - chisq / gsl_stats_tss(data->data, 1, data->size);

  fprintf(stderr, "chisq = %e, Rsq = %f\n", chisq, Rsq);

  /* now print out the model and the exact solution and compute rms error */
  {
    double eps_rms = 0.0;
    double volume = 0.0;
    double r, theta, phi;
    double dr = 0.05;
    double dtheta = 5.0 * M_PI / 180.0;
    double dphi = 5.0 * M_PI / 180.0;
    double x[N_DIM];
    gsl_vector_view xv = gsl_vector_view_array(x, N_DIM);
    double psi;
    double psi_model;
    double err;

    for (r = 0.01; r < R_MAX; r += dr)
      {
        for (theta = 0.0; theta < M_PI; theta += dtheta)
          {
            for (phi = 0.0; phi < 2.0 * M_PI; phi += dphi)
              {
                double dV = r * r * sin(theta) * dr * dtheta * dphi;

                x[0] = r;
                x[1] = theta;
                x[2] = phi;

                /* compute model value for this (r, theta, phi) */
                psi_model = gsl_multifit_ndlinear_calc(&xv.vector,
                                                       coeffs,
                                                       ndlinear_p);

                /* compute exact value for this (r, theta, phi) */
                psi = psi_real_exact(k, l, m, r, theta, phi);

                err = psi_model - psi;
                eps_rms += err * err * dV;
                volume += dV;

                if (phi == 0.0)
                  printf("%e %e %e %e\n", r, theta, psi, psi_model);
              }
          }
        printf("\n");
      }

    eps_rms /= volume;
    eps_rms = sqrt(eps_rms);
    fprintf(stderr, "rms error over all parameter space = %e\n", eps_rms);
  }

  gsl_rng_free(rng_p);
  gsl_bspline_free(bspline_p);
  gsl_multifit_ndlinear_free(ndlinear_p);
  gsl_multifit_linear_free(multifit_p);
  gsl_vector_free(data);
  gsl_matrix_free(vars);
  gsl_matrix_free(X);
  gsl_vector_free(coeffs);
  gsl_matrix_free(cov);

  return 0;
} /* main() */


#include <stdio.h>
#include <gsl/gsl_blas.h>
#include "../../include/timing.h"

double* default_matrix;
double** results;

int max(int a, int b){
  if(a > b) return a;
  return b;
}

void save_res_to_csv(double** results, int max_size, int stride, int trials, char* file_name){
  FILE* csv = fopen(file_name, "w");
  if(csv == NULL) perror("Error creating file!");

  // header of the .csv
  fprintf(csv, "size,result\n" );
  // measurement results
  for(int i = 0; i < max_size; i++){
    for (int j = 0; j < trials; j++) {
      fprintf(csv, "%d,%lf\n", i * stride, results[i][j]);
    }
  }
  fclose(csv);

}

void init_matrix(int max_size, int stride){
  int actual_size = max_size * stride;
  default_matrix = (double*) malloc(actual_size* actual_size * sizeof(double));
  for (int i = 0; i < actual_size; i++) {
    for (int j = 0; j < actual_size; j++) {
      default_matrix[actual_size * i + j] = actual_size * i + j;
    }
  }
}

void init_results(int max_size, int trials){
  results = (double**) malloc(max_size * sizeof(double*));
  for (int i = 0; i < max_size; i++) {
    results[i] = (double*) malloc(trials * sizeof(double));
  }
}

void cleanup_matrix(int max_size, int stride){
  free(default_matrix);
}

void cleanup_results(int max_size){
  for (int i = 0; i < max_size; i++) {
    free(results[i]);
  }
  free(results);
}

double matrix_naive_test(int size){
  gsl_matrix* new_mat = gsl_matrix_alloc(size, size);
  gsl_matrix_view tmp_m = gsl_matrix_view_array(default_matrix, size, size);
  double res;
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
      gsl_matrix_set(new_mat, i, j, 0);
    }
  }
  double start = get_real_time();
  for(int j = 0; j< size; j++){
    for (int k = 0; k < size; k++) {
      for (int i = 0; i < size; i++) {
        double tmp_res = gsl_matrix_get(&tmp_m.matrix, i, k) * gsl_matrix_get(&tmp_m.matrix, k, j);
        gsl_matrix_set(new_mat, i, j, gsl_matrix_get(new_mat, i, j) + tmp_res);

      }
    }
  }
  double end = get_real_time();
  gsl_matrix_free(new_mat);
  return end - start;

}

double matrix_better_test(int size){
  gsl_matrix* new_mat = gsl_matrix_alloc(size, size);
  gsl_matrix_view tmp_m = gsl_matrix_view_array(default_matrix, size, size);
  double res;
  double start = get_real_time();
  for(int i = 0; i< size; i++){
    for (int j = 0; j < size; j++) {
      double tmp_res = 0;
      for (int k = 0; k < size; k++) {
        tmp_res += gsl_matrix_get(&tmp_m.matrix, i, k) * gsl_matrix_get(&tmp_m.matrix, k, j);
      }
      gsl_matrix_set(new_mat, i, j, tmp_res);
    }
  }
  double end = get_real_time();
  gsl_matrix_free(new_mat);
  return end - start;

}

double matrix_blas_test(int size){
  gsl_matrix_view tmp_m = gsl_matrix_view_array(default_matrix, size, size);
  double res;
  double start = get_real_time();
  gsl_blas_dsymm(CblasLeft, CblasUpper, 1, &tmp_m.matrix, &tmp_m.matrix, 0,&tmp_m.matrix);

  double end = get_real_time();
  return end - start;

}

void matrix_naive_analysis(int max_size, int stride, int trials){
  init_matrix(max_size, stride);
  for(int i = 1; i < max_size; i++){
    for (int j = 0; j < trials; j++) {
      results[i][j] = matrix_naive_test(i * stride);
    }
  }
  save_res_to_csv(results, max_size, stride, trials, "naive.csv");
  cleanup_matrix(max_size, stride);
}

void matrix_better_analysis(int max_size, int stride, int trials){
  init_matrix(max_size, stride);
  for(int i = 1; i < max_size; i++){
    for (int j = 0; j < trials; j++) {
      results[i][j] = matrix_better_test(i * stride);
    }
  }
  save_res_to_csv(results, max_size, stride, trials, "better.csv");
  cleanup_matrix(max_size, stride);
}

void matrix_blas_analysis(int max_size, int stride, int trials){
  init_matrix(max_size, stride);
  for(int i = 1; i < max_size; i++){
    for (int j = 0; j < trials; j++) {
      results[i][j] = matrix_blas_test(i * stride);
    }
  }
  save_res_to_csv(results, max_size, stride, trials, "blas.csv");
  cleanup_matrix(max_size, stride);
}

int
main (void)
{
  int max_size = 25;
  int stride = 20;
  int trials = 10;
  int results_size = max_size;
  init_results(results_size, trials);
  printf("Analyzing naive multiplication.\n" );
  matrix_naive_analysis(max_size, stride, trials);
  printf("Analyzing better multiplication.\n" );
  matrix_better_analysis(max_size, stride, trials);
  printf("Analyzing blas multiplication.\n" );
  matrix_blas_analysis(max_size, stride, trials);
  cleanup_results(results_size);

  return 0;
}


/* Author: G. Jungman (+modifications from O. Teytaud olivier.teytaud@inria.fr)
 */
#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_ieee_utils.h>

#include <gsl/gsl_qrng.h>
#include <gsl/gsl_test.h>
#include <math.h>

void test_sobol(void)
{
  int status = 0;
  double v[3];
  /* int i; */

  /* test in dimension 2 */
  gsl_qrng * g = gsl_qrng_alloc(gsl_qrng_sobol, 2);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.25 || v[1] != 0.75 );
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.375 || v[1] != 0.375 );
  gsl_qrng_free(g);

  gsl_test (status, "Sobol d=2");

  status = 0;
  /* test in dimension 3 */
  g = gsl_qrng_alloc(gsl_qrng_sobol, 3);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.25 || v[1] != 0.75 || v[2] != 0.25 );
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.375 || v[1] != 0.375 || v[2] != 0.625 );

  gsl_test (status, "Sobol d=3");

  status = 0;
  gsl_qrng_init(g);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.25 || v[1] != 0.75 || v[2] != 0.25 );
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.375 || v[1] != 0.375 || v[2] != 0.625 );
  gsl_qrng_free(g);

  gsl_test (status, "Sobol d=3 (reinitialized)");
}

void test_halton(void)
{
	int status = 0;
	double v[1229];
	unsigned int i;

	/* test in dimension 1229 */

	gsl_qrng * g = gsl_qrng_alloc(gsl_qrng_halton, 1229);
	for (i=0;i<30;i++)
		gsl_qrng_get(g, v);
	gsl_qrng_free(g);

	gsl_test (status, "Halton d=1229");
	status = 0;

	/* test in dimension 2 */
	/*should be
	 * 0.5 0.333333
	 * 0.25 0.666667
	 * 0.75 0.111111
	 * 0.125 0.444444*/
	g = gsl_qrng_alloc(gsl_qrng_halton, 2);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 3.0/4.0, 1e-3, "halton(2) k=2 v[0]");
        gsl_test_rel (v[1], 1.0/9.0, 1e-3, "halton(2) k=2 v[1]");

	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 1.0/8.0, 1e-3, "halton(2) k=3 v[0]");
        gsl_test_rel (v[1], 4.0/9.0, 1e-3, "halton(2) k=3 v[1]");

	gsl_qrng_free(g);

	/* test in dimension 3 */
	g = gsl_qrng_alloc(gsl_qrng_halton, 3);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 0.75, 1e-3, "halton(3) k=3 v[0]");
        gsl_test_rel (v[1], 1.0/9.0, 1e-3, "halton(3) k=3 v[1]");
        gsl_test_rel (v[2], 0.6, 1e-3, "halton(3) k=3 v[2]");

	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 0.125, 1e-3, "halton(3) k=4 v[0]");
        gsl_test_rel (v[1], 4.0/9.0, 1e-3, "halton(3) k=4 v[1]");
        gsl_test_rel (v[2], 0.8, 1e-3, "halton(3) k=4 v[2]");

	gsl_qrng_init(g);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 0.75, 1e-3, "halton(3) reinitialized k=3 v[0]");
        gsl_test_rel (v[1], 1.0/9.0, 1e-3, "halton(3) reinitialized k=3 v[1]");
        gsl_test_rel (v[2], 0.6, 1e-3, "halton(3) reinitialized k=3 v[2]");

	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 0.125, 1e-3, "halton(3) reinitialized k=4 v[0]");
        gsl_test_rel (v[1], 4.0/9.0, 1e-3, "halton(3) reinitialized k=4 v[1]");
        gsl_test_rel (v[2], 0.8, 1e-3, "halton(3) reinitialized k=4 v[2]");

	gsl_qrng_free(g);
}

void test_reversehalton(void)
{
	int status = 0;
	double v[3];

	/* test in dimension 2 */
	gsl_qrng * g = gsl_qrng_alloc(gsl_qrng_reversehalton, 2);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
	/* should be
	 * 0.5 0.666667
	 * 0.25 0.333333
	 * 0.75 0.222222
	 * 0.125 0.888889*/

        gsl_test_rel (v[0], 3.0/4.0, 1e-3, "reversehalton(2) k=2 v[0]");
        gsl_test_rel (v[1], 2.0/9.0, 1e-3, "reversehalton(2) k=2 v[1]");

	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 1.0/8.0, 1e-3, "reversehalton(2) k=2 v[0]");
        gsl_test_rel (v[1], 8.0/9.0, 1e-3, "reversehalton(2) k=2 v[1]");

	gsl_qrng_free(g);

	/* test in dimension 3 */
	g = gsl_qrng_alloc(gsl_qrng_reversehalton, 3);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 0.75, 1e-3, "reversehalton(3) k=3 v[0]");
        gsl_test_rel (v[1], 2.0/9.0, 1e-3, "reversehalton(3) k=3 v[1]");
        gsl_test_rel (v[2], 0.4, 1e-3, "reversehalton(3) k=3 v[2]");

	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 0.125, 1e-3, "reversehalton(3) k=3 v[0]");
        gsl_test_rel (v[1], 8.0/9.0, 1e-3, "reversehalton(3) k=3 v[1]");
        gsl_test_rel (v[2], 0.2, 1e-3, "reversehalton(3) k=3 v[2]");

	status = 0;
	gsl_qrng_init(g);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 0.75, 1e-3, "reversehalton(3) reinitialized k=3 v[0]");
        gsl_test_rel (v[1], 2.0/9.0, 1e-3, "reversehalton(3) reinitialized k=3 v[1]");
        gsl_test_rel (v[2], 0.4, 1e-3, "reversehalton(3) reinitialized k=3 v[2]");

	gsl_qrng_get(g, v);
        gsl_test_rel (v[0], 0.125, 1e-3, "reversehalton(3) reinitialized k=3 v[0]");
        gsl_test_rel (v[1], 8.0/9.0, 1e-3, "reversehalton(3) reinitialized k=3 v[1]");
        gsl_test_rel (v[2], 0.2, 1e-3, "reversehalton(3) reinitialized k=3 v[2]");

	gsl_qrng_free(g);
}

void test_nied2(void)
{
  int status = 0;
  double v[3];
  /* int i; */

  /* test in dimension 2 */
  gsl_qrng * g = gsl_qrng_alloc(gsl_qrng_niederreiter_2, 2);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.75 || v[1] != 0.25 );
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.25 || v[1] != 0.75 );
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.625 || v[1] != 0.125 );
  gsl_qrng_free(g);

  gsl_test (status, "Niederreiter d=2");

  status = 0;

  /* test in dimension 3 */
  g = gsl_qrng_alloc(gsl_qrng_niederreiter_2, 3);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.75 || v[1] != 0.25 || v[2] != 0.3125 );
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.25 || v[1] != 0.75 || v[2] != 0.5625 );
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.625 || v[1] != 0.125 || v[2] != 0.6875 );

  gsl_test (status, "Niederreiter d=3");

  status = 0;

  gsl_qrng_init(g);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.75 || v[1] != 0.25 || v[2] != 0.3125 );
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.25 || v[1] != 0.75 || v[2] != 0.5625 );
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  gsl_qrng_get(g, v);
  status += ( v[0] != 0.625 || v[1] != 0.125 || v[2] != 0.6875 );
  gsl_qrng_free(g);

  gsl_test (status, "Niederreiter d=3 (reinitialized)");
}

int main()
{

  gsl_ieee_env_setup ();

  test_sobol();
	test_halton();
	test_reversehalton();
  test_nied2();

  exit (gsl_test_summary ());
}


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

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

/*! \file second_order.c
 *  \brief produce actual ICs from special 2nd order lpt ICs
 */

double F1_Omega(double a)
{
  double omega_a;

  omega_a = All.Omega0 / (All.Omega0 + a * (1 - All.Omega0 - All.OmegaLambda) + a * a * a * All.OmegaLambda);

  return pow(omega_a, 5.0 / 9);
}

double F2_Omega(double a)
{
  double omega_a;

  omega_a = All.Omega0 / (All.Omega0 + a * (1 - All.Omega0 - All.OmegaLambda) + a * a * a * All.OmegaLambda);

  return 2.0 * pow(omega_a, 6.0 / 11);
}

void second_order_ics(void)
{
  int i, j;
  double a, hubble, fac1, fac2, fac3;
  double ax, ay, az;

  if(ThisTask == 0)
    {
      printf("\nNow producing ICs based on 2nd-order LPT\n\n");
      fflush(stdout);
    }

  a = All.TimeBegin;

  hubble = hubble_function(a);

  fac1 = 1.0 / (a * a * hubble * F1_Omega(a));	/* this factor converts the Zeldovich peculiar velocity
						   (expressed as  a^2*dX/dt  to comoving displacement */
  fac2 = header.lpt_scalingfactor;

  fac3 = fac2 * a * a * hubble * F2_Omega(a);

  if(ThisTask == 0)
    printf("fac1=%g  fac2=%g  fac3=%g\n", fac1, fac2, fac3);

  for(i = 0; i < NumPart; i++)
    {
      for(j = 0; j < 3; j++)
	P[i].Pos[j] += fac1 * P[i].Vel[j];	/* Zeldovich displacement */

#ifdef PMGRID
      for(j = 0; j < 3; j++)
	P[i].Pos[j] += fac2 * (P[i].GravPM[j] + P[i].g.GravAccel[j]);	/* second order lpt displacement */

      for(j = 0; j < 3; j++)
	P[i].Vel[j] += fac3 * (P[i].GravPM[j] + P[i].g.GravAccel[j]);	/* second order lpt velocity correction */
#else
      for(j = 0; j < 3; j++)
	P[i].Pos[j] += fac2 * (P[i].g.GravAccel[j]);	/* second order lpt displacement */

      for(j = 0; j < 3; j++)
	P[i].Vel[j] += fac3 * (P[i].g.GravAccel[j]);	/* second order lpt velocity correction */
#endif
    }

  /* now set the masses correctly */

  for(i = 0; i < NumPart; i++)
    {
      P[i].Mass = P[i].OldAcc;

      if(All.MassTable[P[i].Type] != 0)
	P[i].Mass = All.MassTable[P[i].Type];
    }

  if(All.ComovingIntegrationOn)
    if(All.PeriodicBoundariesOn == 1)
      check_omega();		/* check again whether we have plausible mass density */

  /* recompute long range force */

  All.DoDynamicUpdate = 1;
  domain_Decomposition();	/* redo domain decomposition because particles may have shifted */

  CPU_Step[CPU_MISC] += measure_time();

#ifdef PMGRID
  long_range_force();
#endif

  CPU_Step[CPU_MESH] += measure_time();

  gravity_tree();

  for(i = 0; i < NumPart; i++)
    {
#ifdef PMGRID
      ax = (P[i].g.GravAccel[0] + P[i].GravPM[0]) / All.G;
      ay = (P[i].g.GravAccel[1] + P[i].GravPM[1]) / All.G;
      az = (P[i].g.GravAccel[2] + P[i].GravPM[2]) / All.G;
#else
      ax = P[i].g.GravAccel[0] / All.G;
      ay = P[i].g.GravAccel[1] / All.G;
      az = P[i].g.GravAccel[2] / All.G;
#endif
      P[i].OldAcc = sqrt(ax * ax + ay * ay + az * az);
    }

  if(All.TypeOfOpeningCriterion == 1)
    {
      All.ErrTolTheta = 0;	/* This will switch to the relative opening criterion for the following force computations */
      gravity_tree();
    }

}


#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <sys/resource.h>
#include <string.h>

#include <gsl/gsl_linalg.h>
#include <gsl/gsl_blas.h>

double drand() {
    return (double) rand() / RAND_MAX;
}

void ptime(struct rusage *ru0, struct rusage *ru1) {

    double utime = 0, stime = 0, ttime = 0;

    utime = (double) ru1->ru_utime.tv_sec
            + 1.e-6 * (double) ru1->ru_utime.tv_usec
            - ru0->ru_utime.tv_sec
            - 1.e-6 * (double) ru0->ru_utime.tv_usec;
    stime = (double) ru1->ru_stime.tv_sec
            + 1.e-6 * (double) ru1->ru_stime.tv_usec
            - ru0->ru_stime.tv_sec
            - 1.e-6 * (double) ru0->ru_stime.tv_usec;
    ttime = stime + utime;

    printf("%3f\n", ttime);
}

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

    if (argc < 2) {
        errno = EINVAL;
        perror("");
        return errno;
    }
    int n = strtol(argv[1], NULL, 10);
    if (0 != errno) {
        perror("");
        return errno;
    }

    srand(time(NULL));

    int els = n * n;
    double *A_data = (double *) malloc(els * sizeof(double));
    for (int i = 0; i < els; i++) {
        A_data[i] = drand();
    }
    double *b_data = (double *) malloc(n * sizeof(double));
    for (int i = 0; i < n; i++) {
        b_data[i] = drand();
    }

    gsl_matrix_view A
            = gsl_matrix_view_array(A_data, n, n);
    gsl_matrix *A_copy = gsl_matrix_alloc(n, n);
    gsl_matrix_memcpy(A_copy, &A.matrix);
    fprintf(stderr, "A = [\n");
    gsl_matrix_fprintf(stderr, &A.matrix, "%g");
    fprintf(stderr, "]\n");

    gsl_vector_view b
            = gsl_vector_view_array(b_data, n);
    fprintf(stderr, "b = [\n");
    gsl_vector_fprintf(stderr, &b.vector, "%g");
    fprintf(stderr, "]\n");

    gsl_vector *x = gsl_vector_alloc(n);

    struct rusage t0, t1, t2;
    int s;

    gsl_permutation *p = gsl_permutation_alloc(n);

    getrusage(RUSAGE_SELF, &t0);
    gsl_linalg_LU_decomp(&A.matrix, p, &s);
    getrusage(RUSAGE_SELF, &t1);
    gsl_linalg_LU_solve(&A.matrix, p, &b.vector, x);
    getrusage(RUSAGE_SELF, &t2);

    fprintf(stderr, "x = [\n");
    gsl_vector_fprintf(stderr, x, "%g");
    fprintf(stderr, "]\n");

    fprintf(stderr, "%s \n", "decomposition time:");
    ptime(&t0, &t1);
    fprintf(stderr, "%s \n", "solve time:");
    ptime(&t1, &t2);
    fprintf(stderr, "%s \n", "together time:");
    ptime(&t0, &t2);

    gsl_vector *y = gsl_vector_alloc(n);
    gsl_blas_dgemv(CblasNoTrans, 1.0, A_copy, x, 0, y);
    fprintf(stderr, "y = [\n");
    gsl_vector_fprintf(stderr, y, "%g");
    fprintf(stderr, "]\n");

    gsl_permutation_free(p);
    gsl_vector_free(x);
    return EXIT_SUCCESS;
}


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gbpLib.h>
#include <gbpMath.h>
#include <gbpCosmo_core.h>
#include <gbpCosmo_NFW_etc.h>
#include <gsl/gsl_sf_expint.h>

double rho_NFW(double r, double M_vir, double z, int mode, cosmo_info **cosmo) {
    double c_vir;
    double R_vir;
    double g_c;
    double rho_o;
    double x;
    set_NFW_params(M_vir, z, mode, cosmo, &c_vir, &R_vir);
    g_c   = 1. / (log(1. + c_vir) - c_vir / (1. + c_vir));
    rho_o = M_vir * g_c / (FOUR_PI * pow(R_vir / c_vir, 3.));
    x     = r * c_vir / R_vir;
    return (rho_o / (x * pow(1. + x, 2.)));
}


/* integration/tests.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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>
#include <math.h>
#include <gsl/gsl_math.h>

#include "tests.h"

/* These are the test functions from table 4.1 of the QUADPACK book */

/* f1(x) = x^alpha * log(1/x) */
/* integ(f1,x,0,1) = 1/(alpha + 1)^2 */

double f1 (double x, void * params) {
  double alpha = *(double *) params ;
  return pow(x,alpha) * log(1/x) ;
}

/* f2(x) = 4^-alpha / ((x-pi/4)^2 + 16^-alpha) */
/* integ(f2,x,0,1) = arctan((4-pi)4^(alpha-1)) + arctan(pi 4^(alpha-1)) */

double f2 (double x, void * params) {
  double alpha = *(double *) params ;
  return pow(4.0,-alpha) / (pow((x-M_PI/4.0),2.0) + pow(16.0,-alpha)) ;
}

/* f3(x) = cos(2^alpha * sin(x)) */
/* integ(f3,x,0,pi) = pi J_0(2^alpha) */

double f3 (double x, void * params) {
  double alpha = *(double *) params ;
  return cos(pow(2.0,alpha) * sin(x)) ;
}

/* Functions 4, 5 and 6 are duplicates of functions  1, 2 and 3 */
/* ....                                                         */

/* f7(x) = |x - 1/3|^alpha */
/* integ(f7,x,0,1) = ((2/3)^(alpha+1) + (1/3)^(alpha+1))/(alpha + 1) */

double f7 (double x, void * params) {
  double alpha = *(double *) params ;
  return pow(fabs(x - (1.0/3.0)),alpha) ;
}

/* f8(x) = |x - pi/4|^alpha */
/* integ(f8,x,0,1) =
   ((1 - pi/4)^(alpha+1) + (pi/4)^(alpha+1))/(alpha + 1) */

double f8 (double x, void * params) {
  double alpha = *(double *) params ;
  return pow(fabs(x - (M_PI/4.0)),alpha) ;
}

/* f9(x) = sqrt(1 - x^2) / (x + 1 + 2^-alpha) */
/* integ(f9,x,-1,1) = pi/sqrt((1+2^-alpha)^2-1) */

double f9 (double x, void * params) {
  double alpha = *(double *) params ;
  return 1 / ((x + 1 + pow(2.0,-alpha)) * sqrt(1-x*x)) ;
}

/* f10(x) = sin(x)^(alpha - 1) */
/* integ(f10,x,0,pi/2) = 2^(alpha-2) ((Gamma(alpha/2))^2)/Gamma(alpha) */

double f10 (double x, void * params) {
  double alpha = *(double *) params ;
  return pow(sin(x), alpha-1) ;
}

/* f11(x) = log(1/x)^(alpha - 1) */
/* integ(f11,x,0,1) = Gamma(alpha) */

double f11 (double x, void * params) {
  double alpha = *(double *) params ;
  return pow(log(1/x), alpha-1) ;
}

/* f12(x) = exp(20*(x-1)) * sin(2^alpha * x) */
/* integ(f12,x,0,1) =
   (20 sin(2^alpha) - 2^alpha cos(2^alpha) + 2^alpha exp(-20))
   /(400 + 4^alpha) */

double f12 (double x, void * params) {
  double alpha = *(double *) params ;
  return exp(20*(x-1)) * sin(pow(2.0,alpha) * x) ;
}

/* f13(x) = cos(2^alpha * x)/sqrt(x(1 - x)) */
/* integ(f13,x,0,1) = pi cos(2^(alpha-1)) J_0(2^(alpha-1))  */

double f13 (double x, void * params) {
  double alpha = *(double *) params ;
  return cos(pow(2.0,alpha)*x)/sqrt(x*(1-x)) ;
}

double f14 (double x, void * params) {
  double alpha = *(double *) params ;
  return exp(-pow(2.0,-alpha)*x)*cos(x)/sqrt(x) ;
}

double f15 (double x, void * params) {
  double alpha = *(double *) params ;
  return x*x * exp(-pow(2.0,-alpha)*x) ;
}

double f16 (double x, void * params) {
  double alpha = *(double *) params ;
  if (x==0 && alpha == 1) return 1 ;  /* make the function continuous in x */
  if (x==0 && alpha > 1) return 0 ;   /* avoid problems with pow(0,1) */
  return pow(x,alpha-1)/pow((1+10*x),2.0) ;
}

double f17 (double x, void * params) {
  double alpha = *(double *) params ;
  return pow(2.0,-alpha)/(((x-1)*(x-1)+pow(4.0,-alpha))*(x-2)) ;
}

/* f454(x) = x^3 log|(x^2-1)(x^2-2)| */
/* integ(f454,x,0,inf) = 61 log(2) + (77/4) log(7) - 27 */

double f454 (double x, void * params) {
  double x2 = x * x;
  double x3 = x * x2;
  params = 0 ;
  return x3 * log(fabs((x2 - 1.0) * (x2 - 2.0))) ;
}

/* f455(x) = log(x)/(1+100*x^2) */
/* integ(f455,x,0,inf) = -log(10)/20 */

double f455 (double x, void * params) {
  params = 0 ;
  return log(x) / (1.0 + 100.0 * x * x) ;
}

/* f456(x) = log(x) */
/* integ(f456*sin(10 pi x),x,0,1) = -(gamma + log(10pi) - Ci(10pi))/(10pi) */

double f456 (double x, void * params) {
  params = 0 ;
  if (x == 0.0)
    {
      return 0;
    }
  return log(x) ;
}

/* f457(x) = 1/sqrt(x) */
/* integ(f457*cos(pi x / 2),x,0,+inf) = 1 */

double f457 (double x, void * params) {
  params = 0 ;
  if (x == 0.0)
    {
      return 0;
    }
  return 1/sqrt(x) ;
}

/* f458(x) = 1/(1 + log(x)^2)^2 */
/* integ(log(x) f458(x),x,0,1) = (Ci(1) sin(1) + (pi/2 - Si(1)) cos(1))/pi
                               = -0.1892752 */

double f458 (double x, void * params) {
  params = 0 ;

  if (x == 0.0)
    {
      return 0;
    }
  else
    {
      double u = log(x);
      double v = 1 + u * u;

      return 1.0 / (v * v) ;
    }
}

/* f459(x) = 1/(5 x^3 + 6) */
/* integ(f459/(x-0),x,-1,5) = log(125/631)/18 */

double f459 (double x, void * params) {
  params = 0 ;
  return 1.0 / (5.0 * x * x * x + 6.0) ;
}

/* myfn1(x) = exp(-x - x^2) */
/* integ(myfn1,x,-inf,inf) = sqrt(pi) exp(-1/4) */

double myfn1 (double x, void * params) {
  params = 0;
  return exp(-x - x*x) ;
}

/* myfn2(x) = exp(alpha*x) */
/* integ(myfn2,x,-inf,b) = exp(alpha*b)/alpha */

double myfn2 (double x, void * params) {
  double alpha = *(double *) params ;
  return exp(alpha*x) ;
}


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

#include <gsl/gsl_math.h>
#include <gsl/gsl_movstat.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_vector.h>

int
main(void)
{
  const size_t N = 1000;                                 /* length of time series */
  const double sigma[] = { 1.0, 5.0, 1.0, 3.0, 5.0 };    /* variances */
  const size_t N_sigma[] = { 200, 450, 600, 850, 1000 }; /* samples where variance changes */
  const size_t K = 41;                                   /* window size */
  gsl_vector *x = gsl_vector_alloc(N);
  gsl_vector *xmedian = gsl_vector_alloc(N);
  gsl_vector *xmad = gsl_vector_alloc(N);
  gsl_vector *xiqr = gsl_vector_alloc(N);
  gsl_vector *xSn = gsl_vector_alloc(N);
  gsl_vector *xQn = gsl_vector_alloc(N);
  gsl_vector *xsd = gsl_vector_alloc(N);
  gsl_rng *r = gsl_rng_alloc(gsl_rng_default);
  gsl_movstat_workspace * w = gsl_movstat_alloc(K);
  size_t idx = 0;
  size_t i;

  for (i = 0; i < N; ++i)
    {
      double gi = gsl_ran_gaussian(r, sigma[idx]);
      double u = gsl_rng_uniform(r);
      double outlier = (u < 0.01) ? 15.0*GSL_SIGN(gi) : 0.0;
      double xi = gi + outlier;

      gsl_vector_set(x, i, xi);

      if (i == N_sigma[idx] - 1)
        ++idx;
    }

  /* compute moving statistics */
  gsl_movstat_mad(GSL_MOVSTAT_END_TRUNCATE, x, xmedian, xmad, w);
  gsl_movstat_qqr(GSL_MOVSTAT_END_TRUNCATE, x, 0.25, xiqr, w);
  gsl_movstat_Sn(GSL_MOVSTAT_END_TRUNCATE, x, xSn, w);
  gsl_movstat_Qn(GSL_MOVSTAT_END_TRUNCATE, x, xQn, w);
  gsl_movstat_sd(GSL_MOVSTAT_END_TRUNCATE, x, xsd, w);

  /* scale IQR by factor to approximate standard deviation */
  gsl_vector_scale(xiqr, 0.7413);

  /* print results */
  idx = 0;
  for (i = 0; i < N; ++i)
    {
      printf("%zu %f %f %f %f %f %f %f\n",
             i,
             gsl_vector_get(x, i),
             sigma[idx],
             gsl_vector_get(xmad, i),
             gsl_vector_get(xiqr, i),
             gsl_vector_get(xSn, i),
             gsl_vector_get(xQn, i),
             gsl_vector_get(xsd, i));

      if (i == N_sigma[idx] - 1)
        ++idx;
    }

  gsl_vector_free(x);
  gsl_vector_free(xmedian);
  gsl_vector_free(xmad);
  gsl_vector_free(xiqr);
  gsl_vector_free(xSn);
  gsl_vector_free(xQn);
  gsl_vector_free(xsd);
  gsl_rng_free(r);
  gsl_movstat_free(w);

  return 0;
}


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

// #include <stdlib.h>
// #include <time.h>

double boxcar_overlap(double rA, double radiiA, double rB, double radiiB)
{

    double F;
    F = pow(rB - rA, 2) / pow(radiiA + radiiB, 2);

    return F;

}

size_t gen_pts_rsa_1d(double *x,
    size_t npoints, double radius, int step_limit,
    unsigned long randSeed)
{

    // Setup GSL random number generator
    const gsl_rng_type * T;
    gsl_rng * r;
    T = gsl_rng_default;
    r = gsl_rng_alloc (T);

    // Set the seed
    // srand ( time(NULL) );
    // unsigned long randSeed = rand();
    gsl_rng_set(r, randSeed);

    // Set the initial position
    double xn = gsl_rng_uniform (r) * (1 - 2 * radius) + radius;
    x[0] = xn;

    size_t valid_pts;
    double F;
    int k, flag, step;

    step = 0;
    valid_pts = 1;

    while ((valid_pts < npoints) & (step < step_limit))
    {

        xn = gsl_rng_uniform (r) * (1 - 2 * radius) + radius;

        flag = 1;
        for (k = 0; k < valid_pts; k++)
        {

            F = boxcar_overlap(xn, radius, x[k], radius);
            if (F < 1.0)
            {

                flag = 0;
                break;

            }
        }
        if (flag == 1)
        {

           x[valid_pts] = xn;
           valid_pts += 1;

        }

        step += 1;

    }

    gsl_rng_free (r);

    return valid_pts;

}

unsigned int metro_md_1d(double *x,
    double radius, size_t npoints, unsigned int step_limit,
    unsigned long randSeed)
{

    /* Setup GSL random number generator */
    const gsl_rng_type * T;
    gsl_rng * r;
    T = gsl_rng_default;
    r = gsl_rng_alloc (T);

    /* Set the seed */
    // srand ( time(NULL) );
    // unsigned long randSeed = rand();
    gsl_rng_set(r, randSeed);

    double diameter = 2 * radius;

    double dx, xn, F;
    unsigned int step, i, k, flag, success_steps;

    step = 0;
    success_steps = 0;
    while (step < step_limit)
    {

        i = step % npoints;

        /* Generate new position */
        while (1)
        {

            dx = diameter * (gsl_rng_uniform (r) - 0.5);
            xn = x[i] + dx;

            if (((xn > radius) & (xn < 1 - radius)))
            {
               break;
            }

        }

        /* Determine if new position overlaps with other positions */
        flag = 1;
        for (k = 0; k < npoints; k++)
        {

            F = boxcar_overlap(xn, radius, x[k], radius);
            if ((F < 1.0) & (i != k))
            {
                flag = 0;
                break;
            }

        }

        if (flag == 1)
        {

            x[i] = xn;

            success_steps = success_steps + 1;

        }

        step = step + 1;

    }

    gsl_rng_free (r);

    return success_steps;

}

unsigned int metro_pd_1d(double *x,
    double *radius, size_t npoints, int step_limit,
    unsigned long randSeed)
{

    /* Setup GSL random number generator */
    const gsl_rng_type * T;
    gsl_rng * r;
    T = gsl_rng_default;
    r = gsl_rng_alloc (T);

    /* Set the seed */
    // srand ( time(NULL) );
    // unsigned long randSeed = rand();
    gsl_rng_set(r, randSeed);

    double dx, xn, diameter, F;
    unsigned int step, i, k, flag, success_steps;

    step = 0;
    success_steps = 0;
    while (step < step_limit)
    {

        i = step % npoints;

        /* Generate new position */
        while (1)
        {

            diameter = 2 * radius[i];

            dx = diameter * (gsl_rng_uniform (r) - 0.5);
            xn = x[i] + dx;

            if (((xn > radius[i]) & (xn < 1 - radius[i])) )
            {
               break;
            }

        }

        /* Determine if new position overlaps with other positions */
        flag = 1;
        for (k = 0; k < npoints; k++)
        {

            F = boxcar_overlap(xn, radius[i], x[k], radius[k]);
            if ((F < 1.0) & (i != k))
            {
                flag = 0;
                break;
            }

        }

        if (flag == 1)
        {

            x[i] = xn;

            success_steps += 1;

        }

        step = step + 1;

    }

    gsl_rng_free (r);

    return success_steps;

}


/*
 Example usage of MIXMAX with GSL

 *  Copyright Konstantin Savvidy.
 *
 *  Free to use, academic or commercial. Do not redistribute without permission.
 *
 *	G.K.Savvidy and N.G.Ter-Arutyunian,
 *  On the Monte Carlo simulation of physical systems,
 *	J.Comput.Phys. 97, 566 (1991);
 *  Preprint EPI-865-16-86, Yerevan, Jan. 1986
 *
 *  K.Savvidy
 *  The MIXMAX random number generator
 *  Comp. Phys. Commun. 196 (2015), pp 161–165
 *  http://dx.doi.org/10.1016/j.cpc.2015.06.003
 *
 */

// cc -std=c99 -O3 -funroll-loops -Wall -I /usr/local/include/ driver_gsl.c mixmax.o -L/usr/local/lib -DHOOKUP_GSL=1  -o gsl -lgsl
// make gsl; GSL_RNG_SEED=123 GSL_RNG_TYPE=MIXMAX ./gsl

#include <stdio.h>
#include "mixmax.h"
#include <gsl/gsl_rng.h>
#include <gsl/gsl_version.h>

int
main (void)
{
  const gsl_rng_type * T;
  gsl_rng * r;
  unsigned long int seed = 0;

  int i, n = 10;

 // gsl_rng_env_setup();
 // gsl_rng_types_setup ();

  T = gsl_rng_mixmax;

    const char *p = getenv ("GSL_RNG_SEED");
    if (p)
    {
        seed = strtoul (p, 0, 0);
        fprintf (stderr, "GSL_RNG_SEED=%lu\n", seed);
    };

    gsl_rng_default_seed = seed;

    r = gsl_rng_alloc (T);

    printf("generator type: %s\n", gsl_rng_name (r));
    printf("seed = %lu\n", gsl_rng_default_seed);
    printf("gsl_version=%s\n", gsl_version);

  for (i = 0; i < n; i++)
    {
      double u = gsl_rng_uniform (r);
      printf ("%.16F\n", u);
    }

  gsl_rng_free (r);

  return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <sys/time.h>
#include <gsl/gsl_cblas.h>

#define N 5000;

void mm_bruteforce_ijk(double *a, double *b, double *c, int I, int K, int J) {
    for(int i = 0; i < I; i++) {
        for(int j = 0; j < J; j++) {
            for(int k = 0; k < K; k++) {
                c[i * J + j] += a[i * K + k] * b[k * J + j];
            }
        }
    }
}

void mm_bruteforce_ikj(double *a, double *b, double *c, int I, int K, int J) {
    for(int i = 0; i < I; i++) {
        for(int k = 0; k < K; k++) {
            double dv = a[i * K + k];
            for(int j = 0; j < J; j++) {
                c[i * J + j] += dv * b[k * J + j];
            }
        }
    }
}

void mm_omp(double *a, double *b, double *c, int I, int K, int J) {
    #pragma omp parallel for
    for(int i = 0; i < I; i++) {
        for(int k = 0; k < K; k++) {
            register double dv = a[i * K + k];
            for(int j = 0; j < J; j++) {
                c[i * J + j] += dv * b[k * J + j];
            }
        }
    }
}

void mm_cblas_dgemm(double *a, double *b, double *c, int p, int q, int r) {
    int l = p;
    int m = q;
    int n = r;

    int lda = m;
    int ldb = n;
    int ldc = n;

    double alpha = 1.0;
    double beta = 0.0;

    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
            l, n, m, alpha, a, lda, b, ldb, beta, c, ldc);
}

void init_arange(double *mat, int a, int b) {
    for(int i = 0; i < (a*b); i++) {
        mat[i] = i + 1;
    }
}

void init_value(double *mat, int a, int b, double value) {
    for(int i = 0; i < (a*b); i++) {
        mat[i] = value;
    }
}

void init_zero(double *mat, int a, int b) {
    init_value(mat, a, b, 0);
}

void init_one(double *mat, int a, int b) {
    init_value(mat, a, b, 1);
}

void timer_start(struct timeval *pstv) {
    gettimeofday(pstv, NULL);
}

void timer_end(struct timeval *petv) {
    gettimeofday(petv, NULL);
}

void timer_print(struct timeval *pstv, struct timeval *petv) {
    time_t sec;
    suseconds_t usec;
    sec = petv->tv_sec - pstv->tv_sec;
    usec = petv->tv_usec - pstv->tv_usec;
    if(usec < 0) {
        sec--;
        usec += 1000000;
    }
    printf("elapsed time : %ld.%ld\n", sec, usec);
}

typedef void (*fptr_mm)(double *a, double *b, double *c, int I, int K, int J);

void check_etime_mm(double *a, double *b, double *c, int I, int K, int J, fptr_mm mm) {
    struct timeval stv;
    struct timeval etv;
    init_zero(c, I, J);
    timer_start(&stv);
    mm(a, b, c, I, K, J);
    timer_end(&etv);
    timer_print(&stv, &etv);
}

int main(int argc, char *argv[]) {
    int I, K, J;
    I = K = J = N;

    double *a = (double*)malloc(sizeof(double) * I * K);
    double *b = (double*)malloc(sizeof(double) * K * J);
    double *c = (double*)malloc(sizeof(double) * I * J);

    init_arange(a, I, K);
    init_arange(b, K, J);

    check_etime_mm(a, b, c, I, K, J, mm_bruteforce_ijk);
    check_etime_mm(a, b, c, I, K, J, mm_bruteforce_ikj);
    check_etime_mm(a, b, c, I, K, J, mm_omp);
    check_etime_mm(a, b, c, I, K, J, mm_cblas_dgemm);

    return 0;
}


#include <stdio.h>
#include <cblas.h>

#define M 2
#define N 2
#define K 3
#define X 4
#define Y 5

void print_mat(const char *name, int r, int c, float *m)
{
    printf("%s =\n", name);
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            printf("%.2lf ", m[i * c + j]);
        }
        printf("\n");
    }
}

int main()
{
    float A[M * K] = {
        1, 2, 3,
        4, 5, 6,
    };
    float B[K * N] = {
        0.1, 0.2,
        0.3, 0.4,
        0.5, 0.6,
    };
    float C[X * Y] = { 0 };

    // M x K, K x N -> M x N (single precision, 's'gemm)
    // Save A x B in C starting from (1, 1)
    cblas_sgemm(
        CblasRowMajor, CblasNoTrans, CblasNoTrans,
        M, N, K,
        1,
        A, K,
        B, N,
        0,
        C + Y + 1, Y
    );

    print_mat("A", M, K, A);
    print_mat("B", K, N, B);
    print_mat("C", X, Y, C);

    return 0;
}


/* integration/qk21.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 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 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <gsl/gsl_integration.h>

/* Gauss quadrature weights and kronrod quadrature abscissae and
   weights as evaluated with 80 decimal digit arithmetic by
   L. W. Fullerton, Bell Labs, Nov. 1981. */

static const double xgk[11] =   /* abscissae of the 21-point kronrod rule */
{
  0.995657163025808080735527280689003,
  0.973906528517171720077964012084452,
  0.930157491355708226001207180059508,
  0.865063366688984510732096688423493,
  0.780817726586416897063717578345042,
  0.679409568299024406234327365114874,
  0.562757134668604683339000099272694,
  0.433395394129247190799265943165784,
  0.294392862701460198131126603103866,
  0.148874338981631210884826001129720,
  0.000000000000000000000000000000000
};

/* xgk[1], xgk[3], ... abscissae of the 10-point gauss rule.
   xgk[0], xgk[2], ... abscissae to optimally extend the 10-point gauss rule */

static const double wg[5] =     /* weights of the 10-point gauss rule */
{
  0.066671344308688137593568809893332,
  0.149451349150580593145776339657697,
  0.219086362515982043995534934228163,
  0.269266719309996355091226921569469,
  0.295524224714752870173892994651338
};

static const double wgk[11] =   /* weights of the 21-point kronrod rule */
{
  0.011694638867371874278064396062192,
  0.032558162307964727478818972459390,
  0.054755896574351996031381300244580,
  0.075039674810919952767043140916190,
  0.093125454583697605535065465083366,
  0.109387158802297641899210590325805,
  0.123491976262065851077958109831074,
  0.134709217311473325928054001771707,
  0.142775938577060080797094273138717,
  0.147739104901338491374841515972068,
  0.149445554002916905664936468389821
};

void
gsl_integration_qk21 (const gsl_function * f, double a, double b,
                      double *result, double *abserr,
                      double *resabs, double *resasc)
{
  double fv1[11], fv2[11];
  gsl_integration_qk (11, xgk, wg, wgk, fv1, fv2, f, a, b, result, abserr, resabs, resasc);
}


/* specfunc/gsl_sf_bessel.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 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.
 */

/* Author:  G. Jungman */

#ifndef __GSL_SF_BESSEL_H__
#define __GSL_SF_BESSEL_H__

#include <stdlib.h>
#include <gsl/gsl_mode.h>
#include <gsl/gsl_precision.h>
#include <gsl/gsl_sf_result.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

/* Regular Bessel Function J_0(x)
 *
 * exceptions: none
 */
GSL_EXPORT int gsl_sf_bessel_J0_e(const double x,  gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_J0(const double x);

/* Regular Bessel Function J_1(x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_J1_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_J1(const double x);

/* Regular Bessel Function J_n(x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Jn_e(int n, double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Jn(const int n, const double x);

/* Regular Bessel Function J_n(x),  nmin <= n <= nmax
 *
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Jn_array(int nmin, int nmax, double x, double * result_array);

/* Irregular Bessel function Y_0(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Y0_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Y0(const double x);

/* Irregular Bessel function Y_1(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EOVRFLW, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Y1_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Y1(const double x);

/* Irregular Bessel function Y_n(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EOVRFLW, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Yn_e(int n,const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Yn(const int n,const double x);

/* Irregular Bessel function Y_n(x), nmin <= n <= nmax
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EOVRFLW, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Yn_array(const int nmin, const int nmax, const double x, double * result_array);

/* Regular modified Bessel function I_0(x)
 *
 * exceptions: GSL_EOVRFLW
 */
GSL_EXPORT int gsl_sf_bessel_I0_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_I0(const double x);

/* Regular modified Bessel function I_1(x)
 *
 * exceptions: GSL_EOVRFLW, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_I1_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_I1(const double x);

/* Regular modified Bessel function I_n(x)
 *
 * exceptions: GSL_EOVRFLW, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_In_e(const int n, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_In(const int n, const double x);

/* Regular modified Bessel function  I_n(x) for n=nmin,...,nmax
 *
 * nmin >=0, nmax >= nmin
 * exceptions: GSL_EDOM, GSL_EOVRFLW, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_In_array(const int nmin, const int nmax, const double x, double * result_array);

/* Scaled regular modified Bessel function
 *  exp(-|x|) I_0(x)
 *
 * exceptions: none
 */
GSL_EXPORT int gsl_sf_bessel_I0_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_I0_scaled(const double x);

/* Scaled regular modified Bessel function
 *  exp(-|x|) I_1(x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_I1_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_I1_scaled(const double x);

/* Scaled regular modified Bessel function
 *  exp(-|x|) I_n(x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_In_scaled_e(int n, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_In_scaled(const int n, const double x);

/* Scaled regular modified Bessel function
 *  exp(-|x|) I_n(x)  for n=nmin,...,nmax
 *
 * nmin >=0, nmax >= nmin
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_In_scaled_array(const int nmin, const int nmax, const double x, double * result_array);

/* Irregular modified Bessel function K_0(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_K0_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_K0(const double x);

/* Irregular modified Bessel function K_1(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EOVRFLW, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_K1_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_K1(const double x);

/* Irregular modified Bessel function K_n(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EOVRFLW, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Kn_e(const int n, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Kn(const int n, const double x);

/* Irregular modified Bessel function  K_n(x)  for n=nmin,...,nmax
 *
 * x > 0.0, nmin >=0, nmax >= nmin
 * exceptions: GSL_EDOM, GSL_EOVRFLW, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Kn_array(const int nmin, const int nmax, const double x, double * result_array);

/* Scaled irregular modified Bessel function
 *  exp(x) K_0(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM
 */
GSL_EXPORT int gsl_sf_bessel_K0_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_K0_scaled(const double x);

/* Scaled irregular modified Bessel function
 *  exp(x) K_1(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_K1_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_K1_scaled(const double x);

/* Scaled irregular modified Bessel function
 *  exp(x) K_n(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Kn_scaled_e(int n, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Kn_scaled(const int n, const double x);

/* Scaled irregular modified Bessel function  exp(x) K_n(x)  for n=nmin,...,nmax
 *
 * x > 0.0, nmin >=0, nmax >= nmin
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Kn_scaled_array(const int nmin, const int nmax, const double x, double * result_array);

/* Regular spherical Bessel function j_0(x) = sin(x)/x
 *
 * exceptions: none
 */
GSL_EXPORT int gsl_sf_bessel_j0_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_j0(const double x);

/* Regular spherical Bessel function j_1(x) = (sin(x)/x - cos(x))/x
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_j1_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_j1(const double x);

/* Regular spherical Bessel function j_2(x) = ((3/x^2 - 1)sin(x) - 3cos(x)/x)/x
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_j2_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_j2(const double x);

/* Regular spherical Bessel function j_l(x)
 *
 * l >= 0, x >= 0.0
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_jl_e(const int l, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_jl(const int l, const double x);

/* Regular spherical Bessel function j_l(x) for l=0,1,...,lmax
 *
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_jl_array(const int lmax, const double x, double * result_array);

/* Regular spherical Bessel function j_l(x) for l=0,1,...,lmax
 * Uses Steed's method.
 *
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_jl_steed_array(const int lmax, const double x, double * jl_x_array);

/* Irregular spherical Bessel function y_0(x)
 *
 * exceptions: none
 */
GSL_EXPORT int gsl_sf_bessel_y0_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_y0(const double x);

/* Irregular spherical Bessel function y_1(x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_y1_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_y1(const double x);

/* Irregular spherical Bessel function y_2(x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_y2_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_y2(const double x);

/* Irregular spherical Bessel function y_l(x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_yl_e(int l, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_yl(const int l, const double x);

/* Irregular spherical Bessel function y_l(x) for l=0,1,...,lmax
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_yl_array(const int lmax, const double x, double * result_array);

/* Regular scaled modified spherical Bessel function
 *
 * Exp[-|x|] i_0(x)
 *
 * exceptions: none
 */
GSL_EXPORT int gsl_sf_bessel_i0_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_i0_scaled(const double x);

/* Regular scaled modified spherical Bessel function
 *
 * Exp[-|x|] i_1(x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_i1_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_i1_scaled(const double x);

/* Regular scaled modified spherical Bessel function
 *
 * Exp[-|x|] i_2(x)
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_i2_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_i2_scaled(const double x);

/* Regular scaled modified spherical Bessel functions
 *
 * Exp[-|x|] i_l(x)
 *
 * i_l(x) = Sqrt[Pi/(2x)] BesselI[l+1/2,x]
 *
 * l >= 0
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_il_scaled_e(const int l, double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_il_scaled(const int l, const double x);

/* Regular scaled modified spherical Bessel functions
 *
 * Exp[-|x|] i_l(x)
 * for l=0,1,...,lmax
 *
 * exceptions: GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_il_scaled_array(const int lmax, const double x, double * result_array);

/* Irregular scaled modified spherical Bessel function
 * Exp[x] k_0(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_k0_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_k0_scaled(const double x);

/* Irregular modified spherical Bessel function
 * Exp[x] k_1(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EUNDRFLW, GSL_EOVRFLW
 */
GSL_EXPORT int gsl_sf_bessel_k1_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_k1_scaled(const double x);

/* Irregular modified spherical Bessel function
 * Exp[x] k_2(x)
 *
 * x > 0.0
 * exceptions: GSL_EDOM, GSL_EUNDRFLW, GSL_EOVRFLW
 */
GSL_EXPORT int gsl_sf_bessel_k2_scaled_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_k2_scaled(const double x);

/* Irregular modified spherical Bessel function
 * Exp[x] k_l[x]
 *
 * k_l(x) = Sqrt[Pi/(2x)] BesselK[l+1/2,x]
 *
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_kl_scaled_e(int l, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_kl_scaled(const int l, const double x);

/* Irregular scaled modified spherical Bessel function
 * Exp[x] k_l(x)
 *
 * for l=0,1,...,lmax
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_kl_scaled_array(const int lmax, const double x, double * result_array);

/* Regular cylindrical Bessel function J_nu(x)
 *
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Jnu_e(const double nu, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Jnu(const double nu, const double x);

/* Irregular cylindrical Bessel function Y_nu(x)
 *
 * exceptions:
 */
GSL_EXPORT int gsl_sf_bessel_Ynu_e(double nu, double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Ynu(const double nu, const double x);

/* Regular cylindrical Bessel function J_nu(x)
 * evaluated at a series of x values. The array
 * contains the x values. They are assumed to be
 * strictly ordered and positive. The array is
 * over-written with the values of J_nu(x_i).
 *
 * exceptions: GSL_EDOM, GSL_EINVAL
 */
GSL_EXPORT int gsl_sf_bessel_sequence_Jnu_e(double nu, gsl_mode_t mode, size_t size, double * v);

/* Scaled modified cylindrical Bessel functions
 *
 * Exp[-|x|] BesselI[nu, x]
 * x >= 0, nu >= 0
 *
 * exceptions: GSL_EDOM
 */
GSL_EXPORT int gsl_sf_bessel_Inu_scaled_e(double nu, double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Inu_scaled(double nu, double x);

/* Modified cylindrical Bessel functions
 *
 * BesselI[nu, x]
 * x >= 0, nu >= 0
 *
 * exceptions: GSL_EDOM, GSL_EOVRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Inu_e(double nu, double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Inu(double nu, double x);

/* Scaled modified cylindrical Bessel functions
 *
 * Exp[+|x|] BesselK[nu, x]
 * x > 0, nu >= 0
 *
 * exceptions: GSL_EDOM
 */
GSL_EXPORT int gsl_sf_bessel_Knu_scaled_e(const double nu, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Knu_scaled(const double nu, const double x);

/* Modified cylindrical Bessel functions
 *
 * BesselK[nu, x]
 * x > 0, nu >= 0
 *
 * exceptions: GSL_EDOM, GSL_EUNDRFLW
 */
GSL_EXPORT int gsl_sf_bessel_Knu_e(const double nu, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_Knu(const double nu, const double x);

/* Logarithm of modified cylindrical Bessel functions.
 *
 * Log[BesselK[nu, x]]
 * x > 0, nu >= 0
 *
 * exceptions: GSL_EDOM
 */
GSL_EXPORT int gsl_sf_bessel_lnKnu_e(const double nu, const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_lnKnu(const double nu, const double x);

/* s'th positive zero of the Bessel function J_0(x).
 *
 * exceptions:
 */
GSL_EXPORT int gsl_sf_bessel_zero_J0_e(unsigned int s, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_zero_J0(unsigned int s);

/* s'th positive zero of the Bessel function J_1(x).
 *
 * exceptions:
 */
GSL_EXPORT int gsl_sf_bessel_zero_J1_e(unsigned int s, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_zero_J1(unsigned int s);

/* s'th positive zero of the Bessel function J_nu(x).
 *
 * exceptions:
 */
GSL_EXPORT int gsl_sf_bessel_zero_Jnu_e(double nu, unsigned int s, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_bessel_zero_Jnu(double nu, unsigned int s);

__END_DECLS

#endif /* __GSL_SF_BESSEL_H__ */