/* randist/test.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 <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_test.h>
#include <gsl/gsl_ieee_utils.h>

#define N 100000
void testMoments (double (*f) (void), const char *name,
		   double a, double b, double p);
void testPDF (double (*f) (void), double (*pdf)(double), const char *name);
void testDiscretePDF (double (*f) (void), double (*pdf)(unsigned int),
			const char *name);

void test_shuffle (void);
void test_choose (void);
double test_beta (void);
double test_beta_pdf (double x);
double test_bernoulli (void);
double test_bernoulli_pdf (unsigned int n);
double test_binomial (void);
double test_binomial_pdf (unsigned int n);
double test_binomial_large (void);
double test_binomial_large_pdf (unsigned int n);
double test_cauchy (void);
double test_cauchy_pdf (double x);
double test_chisq (void);
double test_chisq_pdf (double x);
double test_discrete1 (void);
double test_discrete1_pdf (unsigned int n);
double test_discrete2 (void);
double test_discrete2_pdf (unsigned int n);
double test_erlang (void);
double test_erlang_pdf (double x);
double test_exponential (void);
double test_exponential_pdf (double x);
double test_exppow0 (void);
double test_exppow0_pdf (double x);
double test_exppow1 (void);
double test_exppow1_pdf (double x);
double test_exppow1a (void);
double test_exppow1a_pdf (double x);
double test_exppow2 (void);
double test_exppow2_pdf (double x);
double test_exppow2a (void);
double test_exppow2a_pdf (double x);
double test_fdist (void);
double test_fdist_pdf (double x);
double test_flat (void);
double test_flat_pdf (double x);
double test_gamma (void);
double test_gamma_pdf (double x);
double test_gamma1 (void);
double test_gamma1_pdf (double x);
double test_gamma_int (void);
double test_gamma_int_pdf (double x);
double test_gamma_large (void);
double test_gamma_large_pdf (double x);
double test_gaussian (void);
double test_gaussian_pdf (double x);
double test_gaussian_ratio_method (void);
double test_gaussian_ratio_method_pdf (double x);
double test_gaussian_tail (void);
double test_gaussian_tail_pdf (double x);
double test_gaussian_tail1 (void);
double test_gaussian_tail1_pdf (double x);
double test_gaussian_tail2 (void);
double test_gaussian_tail2_pdf (double x);
double test_ugaussian (void);
double test_ugaussian_pdf (double x);
double test_ugaussian_ratio_method (void);
double test_ugaussian_ratio_method_pdf (double x);
double test_ugaussian_tail (void);
double test_ugaussian_tail_pdf (double x);
double test_bivariate_gaussian1 (void);
double test_bivariate_gaussian1_pdf (double x);
double test_bivariate_gaussian2 (void);
double test_bivariate_gaussian2_pdf (double x);
double test_bivariate_gaussian3 (void);
double test_bivariate_gaussian3_pdf (double x);
double test_bivariate_gaussian4 (void);
double test_bivariate_gaussian4_pdf (double x);
double test_gumbel1 (void);
double test_gumbel1_pdf (double x);
double test_gumbel2 (void);
double test_gumbel2_pdf (double x);
double test_geometric (void);
double test_geometric_pdf (unsigned int x);
double test_geometric1 (void);
double test_geometric1_pdf (unsigned int x);
double test_hypergeometric1 (void);
double test_hypergeometric1_pdf (unsigned int x);
double test_hypergeometric2 (void);
double test_hypergeometric2_pdf (unsigned int x);
double test_hypergeometric3 (void);
double test_hypergeometric3_pdf (unsigned int x);
double test_hypergeometric4 (void);
double test_hypergeometric4_pdf (unsigned int x);
double test_hypergeometric5 (void);
double test_hypergeometric5_pdf (unsigned int x);
double test_hypergeometric6 (void);
double test_hypergeometric6_pdf (unsigned int x);
double test_landau (void);
double test_landau_pdf (double x);
double test_levy1 (void);
double test_levy1_pdf (double x);
double test_levy2 (void);
double test_levy2_pdf (double x);
double test_levy1a (void);
double test_levy1a_pdf (double x);
double test_levy2a (void);
double test_levy2a_pdf (double x);
double test_levy_skew1 (void);
double test_levy_skew1_pdf (double x);
double test_levy_skew2 (void);
double test_levy_skew2_pdf (double x);
double test_levy_skew1a (void);
double test_levy_skew1a_pdf (double x);
double test_levy_skew2a (void);
double test_levy_skew2a_pdf (double x);
double test_levy_skew1b (void);
double test_levy_skew1b_pdf (double x);
double test_levy_skew2b (void);
double test_levy_skew2b_pdf (double x);
double test_logistic (void);
double test_logistic_pdf (double x);
double test_lognormal (void);
double test_lognormal_pdf (double x);
double test_logarithmic (void);
double test_logarithmic_pdf (unsigned int n);
double test_negative_binomial (void);
double test_negative_binomial_pdf (unsigned int n);
double test_pascal (void);
double test_pascal_pdf (unsigned int n);
double test_pareto (void);
double test_pareto_pdf (double x);
double test_poisson (void);
double test_poisson_pdf (unsigned int x);
double test_poisson_large (void);
double test_poisson_large_pdf (unsigned int x);
double test_dir2d (void);
double test_dir2d_pdf (double x);
double test_dir2d_trig_method (void);
double test_dir2d_trig_method_pdf (double x);
double test_dir3dxy (void);
double test_dir3dxy_pdf (double x);
double test_dir3dyz (void);
double test_dir3dyz_pdf (double x);
double test_dir3dzx (void);
double test_dir3dzx_pdf (double x);
double test_rayleigh (void);
double test_rayleigh_pdf (double x);
double test_rayleigh_tail (void);
double test_rayleigh_tail_pdf (double x);
double test_tdist1 (void);
double test_tdist1_pdf (double x);
double test_tdist2 (void);
double test_tdist2_pdf (double x);
double test_laplace (void);
double test_laplace_pdf (double x);
double test_weibull (void);
double test_weibull_pdf (double x);
double test_weibull1 (void);
double test_weibull1_pdf (double x);

gsl_rng *r_global;

int
main (void)
{
  gsl_ieee_env_setup ();

  gsl_rng_env_setup() ;
  r_global = gsl_rng_alloc (gsl_rng_default);

#define FUNC(x)  test_ ## x,                     "test gsl_ran_" #x
#define FUNC2(x) test_ ## x, test_ ## x ## _pdf, "test gsl_ran_" #x

  test_shuffle() ;
  test_choose() ;

  testMoments (FUNC (ugaussian), 0.0, 100.0, 0.5);
  testMoments (FUNC (ugaussian), -1.0, 1.0, 0.6826895);
  testMoments (FUNC (ugaussian), 3.0, 3.5, 0.0011172689);
  testMoments (FUNC (ugaussian_tail), 3.0, 3.5, 0.0011172689/0.0013498981);
  testMoments (FUNC (exponential), 0.0, 1.0, 1- exp(-0.5));
  testMoments (FUNC (cauchy), 0.0, 10000.0, 0.5);

  testMoments (FUNC (discrete1), -0.5, 0.5, 0.59 );
  testMoments (FUNC (discrete1), 0.5, 1.5, 0.40 );
  testMoments (FUNC (discrete1), 1.5, 3.5, 0.01 );

  testPDF (FUNC2(beta));
  testPDF (FUNC2(cauchy));
  testPDF (FUNC2(chisq));
  testPDF (FUNC2(erlang));
  testPDF (FUNC2(exponential));

  testPDF (FUNC2(exppow0));
  testPDF (FUNC2(exppow1));
  testPDF (FUNC2(exppow1a));
  testPDF (FUNC2(exppow2));
  testPDF (FUNC2(exppow2a));

  testPDF (FUNC2(fdist));
  testPDF (FUNC2(flat));
  testPDF (FUNC2(gamma));
  testPDF (FUNC2(gamma1));
  testPDF (FUNC2(gamma_int));
  testPDF (FUNC2(gamma_large));
  testPDF (FUNC2(gaussian));
  testPDF (FUNC2(gaussian_ratio_method));
  testPDF (FUNC2(ugaussian));
  testPDF (FUNC2(ugaussian_ratio_method));
  testPDF (FUNC2(gaussian_tail));
  testPDF (FUNC2(gaussian_tail1));
  testPDF (FUNC2(gaussian_tail2));
  testPDF (FUNC2(ugaussian_tail));

  testPDF (FUNC2(bivariate_gaussian1));
  testPDF (FUNC2(bivariate_gaussian2));
  testPDF (FUNC2(bivariate_gaussian3));
  testPDF (FUNC2(bivariate_gaussian4));

  testPDF (FUNC2(gumbel1));
  testPDF (FUNC2(gumbel2));
  testPDF (FUNC2(landau));
  testPDF (FUNC2(levy1));
  testPDF (FUNC2(levy2));
  testPDF (FUNC2(levy1a));
  testPDF (FUNC2(levy2a));
  testPDF (FUNC2(levy_skew1));
  testPDF (FUNC2(levy_skew2));
  testPDF (FUNC2(levy_skew1a));
  testPDF (FUNC2(levy_skew2a));
  testPDF (FUNC2(levy_skew1b));
  testPDF (FUNC2(levy_skew2b));
  testPDF (FUNC2(logistic));
  testPDF (FUNC2(lognormal));
  testPDF (FUNC2(pareto));
  testPDF (FUNC2(rayleigh));
  testPDF (FUNC2(rayleigh_tail));
  testPDF (FUNC2(tdist1));
  testPDF (FUNC2(tdist2));
  testPDF (FUNC2(laplace));
  testPDF (FUNC2(weibull));
  testPDF (FUNC2(weibull1));

  testPDF (FUNC2(dir2d));
  testPDF (FUNC2(dir2d_trig_method));
  testPDF (FUNC2(dir3dxy));
  testPDF (FUNC2(dir3dyz));
  testPDF (FUNC2(dir3dzx));

  testDiscretePDF (FUNC2(discrete1));
  testDiscretePDF (FUNC2(discrete2));
  testDiscretePDF (FUNC2(poisson));
  testDiscretePDF (FUNC2(poisson_large));
  testDiscretePDF (FUNC2(bernoulli));
  testDiscretePDF (FUNC2(binomial));
  testDiscretePDF (FUNC2(binomial_large));
  testDiscretePDF (FUNC2(geometric));
  testDiscretePDF (FUNC2(geometric1));
  testDiscretePDF (FUNC2(hypergeometric1));
  testDiscretePDF (FUNC2(hypergeometric2));
  testDiscretePDF (FUNC2(hypergeometric3));
  testDiscretePDF (FUNC2(hypergeometric4));
  testDiscretePDF (FUNC2(hypergeometric5));
  testDiscretePDF (FUNC2(hypergeometric6));
  testDiscretePDF (FUNC2(logarithmic));
  testDiscretePDF (FUNC2(negative_binomial));
  testDiscretePDF (FUNC2(pascal));

  exit (gsl_test_summary());
}

void
test_shuffle (void)
{
  double count[10][10] ;
  int x[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
  int i, j, status = 0;

  for (i = 0; i < 10; i++)
    {
      for (j = 0; j < 10; j++)
	{
	  count[i][j] = 0 ;
	}
    }

  for (i = 0 ; i < N; i++)
    {
      for (j = 0; j < 10; j++)
	x[j] = j ;

      gsl_ran_shuffle (r_global, x, 10, sizeof(int)) ;

      for (j = 0; j < 10; j++)
	count[x[j]][j] ++ ;
    }

  for (i = 0; i < 10; i++)
    {
      for (j = 0; j < 10; j++)
	{
	  double expected = N / 10.0 ;
	  double d = fabs(count[i][j] - expected);
	  double sigma = d / sqrt(expected) ;
	  if (sigma > 5 && d > 1)
	    {
	      status = 1 ;
	      gsl_test (status,
			"gsl_ran_shuffle %d,%d (%g observed vs %g expected)",
			i, j, count[i][j]/N, 0.1) ;
	    }
	}
    }

  gsl_test (status, "gsl_ran_shuffle on {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}") ;

}

void
test_choose (void)
{
  double count[10] ;
  int x[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
  int y[3] = {0, 1, 2} ;
  int i, j, status = 0;

  for (i = 0; i < 10; i++)
    {
      count[i] = 0 ;
    }

  for (i = 0 ; i < N; i++)
    {
      for (j = 0; j < 10; j++)
	x[j] = j ;

      gsl_ran_choose (r_global, y, 3, x, 10, sizeof(int)) ;

      for (j = 0; j < 3; j++)
	count[y[j]]++ ;
    }

  for (i = 0; i < 10; i++)
    {
      double expected = 3.0 * N / 10.0 ;
      double d = fabs(count[i] - expected);
      double sigma = d / sqrt(expected) ;
      if (sigma > 5 && d > 1)
	{
	  status = 1 ;
	  gsl_test (status,
		    "gsl_ran_choose %d (%g observed vs %g expected)",
		    i, count[i]/N, 0.1) ;
	}
    }

  gsl_test (status, "gsl_ran_choose (3) on {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}") ;

}

void
testMoments (double (*f) (void), const char *name,
	      double a, double b, double p)
{
  int i;
  double count = 0, expected, sigma;
  int status;

  for (i = 0; i < N; i++)
    {
      double r = f ();
      if (r < b && r > a)
	count++;
    }

  expected = p * N;
  sigma = fabs (count - expected) / sqrt (expected);

  status = (sigma > 3);

  gsl_test (status, "%s [%g,%g] (%g observed vs %g expected)",
	    name, a, b, count / N, p);
}

#define BINS 100

void
testPDF (double (*f) (void), double (*pdf)(double), const char *name)
{
  double count[BINS], p[BINS];
  double a = -5.0, b = +5.0 ;
  double dx = (b - a) / BINS ;
  int i,j,status = 0, status_i =0 ;

  for (i = 0; i < BINS; i++)
    count[i] = 0 ;

  for (i = 0; i < N; i++)
    {
      double r = f ();
      if (r < b && r > a)
	{
	  j =  (int)((r - a)/dx) ;
	  count[j]++;
	}
    }

  for (i = 0; i < BINS; i++)
    {
      /* Compute an approximation to the integral of p(x) from x to
         x+dx using Simpson's rule */

      double x = a + i * dx ;
#define STEPS 100
      double sum = 0 ;

      if (fabs(x) < 1e-10) /* hit the origin exactly */
	x = 0.0 ;

      for (j = 1; j < STEPS; j++)
	sum += pdf(x + j * dx / STEPS) ;

      p[i] =  0.5 * (pdf(x) + 2*sum + pdf(x + dx - 1e-7)) * dx / STEPS ;
    }

  for (i = 0; i < BINS; i++)
    {
      double x = a + i * dx ;
      double d = fabs(count[i] - N*p[i]) ;
      if (p[i] != 0)
	{
	  double s = d / sqrt(N*p[i]) ;
	  status_i = (s > 5) && (d > 1) ;
	}
      else
	{
	  status_i = (count[i] != 0) ;
	}
      status |= status_i ;
      if (status_i)
	gsl_test (status_i, "%s [%g,%g) (%g/%d=%g observed vs %g expected)",
		  name, x, x+dx, count[i],N,count[i]/N, p[i]) ;
    }

  if (status == 0)
    gsl_test (status, "%s, sampling against pdf over range [%g,%g) ",
	      name, a, b) ;
}

void
testDiscretePDF (double (*f) (void), double (*pdf)(unsigned int), const char *name)
{
  double count[BINS], p[BINS];
  unsigned int i ;
  int status = 0, status_i =0 ;

  for (i = 0; i < BINS; i++)
    count[i] = 0 ;

  for (i = 0; i < N; i++)
    {
      int r = (int)(f ());
      if (r>= 0 && r < BINS)
	count[r]++;
    }

  for (i = 0; i < BINS; i++)
    p[i] =  pdf(i) ;

  for (i = 0; i < BINS; i++)
    {
      double d = fabs(count[i] - N*p[i]) ;
      if (p[i] != 0)
	{
	  double s = d/sqrt(N*p[i]) ;
	  status_i = (s > 5) && (d > 1);
	}
      else
	{
	  status_i = (count[i] != 0) ;
	}
      status |= status_i ;
      if (status_i)
	gsl_test (status_i, "%s i=%d (%g observed vs %g expected)",
		  name, i, count[i]/N, p[i]) ;
    }

  if (status == 0)
    gsl_test (status, "%s, sampling against pdf over range [%d,%d) ",
	      name, 0, BINS) ;
}

double
test_beta (void)
{
  return gsl_ran_beta (r_global, 2.0, 3.0);
}

double
test_beta_pdf (double x)
{
  return gsl_ran_beta_pdf (x, 2.0, 3.0);
}

double
test_bernoulli (void)
{
  return gsl_ran_bernoulli (r_global, 0.3);
}

double
test_bernoulli_pdf (unsigned int n)
{
  return gsl_ran_bernoulli_pdf (n, 0.3);
}

double
test_binomial (void)
{
  return gsl_ran_binomial (r_global, 0.3, 5);
}

double
test_binomial_pdf (unsigned int n)
{
  return gsl_ran_binomial_pdf (n, 0.3, 5);
}

double
test_binomial_large (void)
{
  return gsl_ran_binomial (r_global, 0.3, 55);
}

double
test_binomial_large_pdf (unsigned int n)
{
  return gsl_ran_binomial_pdf (n, 0.3, 55);
}

double
test_cauchy (void)
{
  return gsl_ran_cauchy (r_global, 2.0);
}

double
test_cauchy_pdf (double x)
{
  return gsl_ran_cauchy_pdf (x, 2.0);
}

double
test_chisq (void)
{
  return gsl_ran_chisq (r_global, 13.0);
}

double
test_chisq_pdf (double x)
{
  return gsl_ran_chisq_pdf (x, 13.0);
}

double
test_dir2d (void)
{
  double x=0, y=0, theta;
  gsl_ran_dir_2d (r_global, &x, &y);
  theta = atan2(x,y);
  return theta;
}

double
test_dir2d_pdf (double x)
{
  if (x > -M_PI && x <= M_PI)
    {
      return 1 / (2 * M_PI) ;
    }
  else
    {
      return 0 ;
    }
}

double
test_dir2d_trig_method (void)
{
  double x=0, y=0, theta;
  gsl_ran_dir_2d_trig_method (r_global, &x, &y);
  theta = atan2(x,y);
  return theta;
}

double
test_dir2d_trig_method_pdf (double x)
{
  if (x > -M_PI && x <= M_PI)
    {
      return 1 / (2 * M_PI) ;
    }
  else
    {
      return 0 ;
    }
}

double
test_dir3dxy (void)
{
  double x=0, y=0, z=0, theta;
  gsl_ran_dir_3d (r_global, &x, &y, &z);
  theta = atan2(x,y);
  return theta;
}

double
test_dir3dxy_pdf (double x)
{
  if (x > -M_PI && x <= M_PI)
    {
      return 1 / (2 * M_PI) ;
    }
  else
    {
      return 0 ;
    }
}

double
test_dir3dyz (void)
{
  double x=0, y=0, z=0, theta;
  gsl_ran_dir_3d (r_global, &x, &y, &z);
  theta = atan2(y,z);
  return theta;
}

double
test_dir3dyz_pdf (double x)
{
  if (x > -M_PI && x <= M_PI)
    {
      return 1 / (2 * M_PI) ;
    }
  else
    {
      return 0 ;
    }
}

double
test_dir3dzx (void)
{
  double x=0, y=0, z=0, theta;
  gsl_ran_dir_3d (r_global, &x, &y, &z);
  theta = atan2(z,x);
  return theta;
}

double
test_dir3dzx_pdf (double x)
{
  if (x > -M_PI && x <= M_PI)
    {
      return 1 / (2 * M_PI) ;
    }
  else
    {
      return 0 ;
    }
}

static gsl_ran_discrete_t *g1 = NULL;
static gsl_ran_discrete_t *g2 = NULL;

double
test_discrete1 (void)
{
    static double P[3]={0.59, 0.4, 0.01};
    if (g1==NULL) {
        g1 = gsl_ran_discrete_preproc(3,P);
    }
    return gsl_ran_discrete(r_global,g1);
}
double test_discrete1_pdf (unsigned int n)
{
    return gsl_ran_discrete_pdf((size_t)n,g1);
}

double
test_discrete2 (void)
{
    static double P[10]={ 1, 9, 3, 4, 5, 8, 6, 7, 2, 0 };
    if (g2==NULL) {
        g2 = gsl_ran_discrete_preproc(10,P);
    }
    return gsl_ran_discrete(r_global,g2);
}
double test_discrete2_pdf (unsigned int n)
{
    return gsl_ran_discrete_pdf((size_t)n,g2);
}

double
test_erlang (void)
{
  return gsl_ran_erlang (r_global, 3.0, 4.0);
}

double
test_erlang_pdf (double x)
{
  return gsl_ran_erlang_pdf (x, 3.0, 4.0);
}

double
test_exponential (void)
{
  return gsl_ran_exponential (r_global, 2.0);
}

double
test_exponential_pdf (double x)
{
  return gsl_ran_exponential_pdf (x, 2.0);
}

double
test_exppow0 (void)
{
  return gsl_ran_exppow (r_global, 3.7, 0.3);
}

double
test_exppow0_pdf (double x)
{
  return gsl_ran_exppow_pdf (x, 3.7, 0.3);
}

double
test_exppow1 (void)
{
  return gsl_ran_exppow (r_global, 3.7, 1.0);
}

double
test_exppow1_pdf (double x)
{
  return gsl_ran_exppow_pdf (x, 3.7, 1.0);
}

double
test_exppow1a (void)
{
  return gsl_ran_exppow (r_global, 3.7, 1.9);
}

double
test_exppow1a_pdf (double x)
{
  return gsl_ran_exppow_pdf (x, 3.7, 1.9);
}

double
test_exppow2 (void)
{
  return gsl_ran_exppow (r_global, 3.7, 2.0);
}

double
test_exppow2_pdf (double x)
{
  return gsl_ran_exppow_pdf (x, 3.7, 2.0);
}

double
test_exppow2a (void)
{
  return gsl_ran_exppow (r_global, 3.7, 7.5);
}

double
test_exppow2a_pdf (double x)
{
  return gsl_ran_exppow_pdf (x, 3.7, 7.5);
}

double
test_fdist (void)
{
  return gsl_ran_fdist (r_global, 3.0, 4.0);
}

double
test_fdist_pdf (double x)
{
  return gsl_ran_fdist_pdf (x, 3.0, 4.0);
}

double
test_flat (void)
{
  return gsl_ran_flat (r_global, 3.0, 4.0);
}

double
test_flat_pdf (double x)
{
  return gsl_ran_flat_pdf (x, 3.0, 4.0);
}

double
test_gamma (void)
{
  return gsl_ran_gamma (r_global, 2.5, 2.17);
}

double
test_gamma_pdf (double x)
{
  return gsl_ran_gamma_pdf (x, 2.5, 2.17);
}

double
test_gamma1 (void)
{
  return gsl_ran_gamma (r_global, 1.0, 2.17);
}

double
test_gamma1_pdf (double x)
{
  return gsl_ran_gamma_pdf (x, 1.0, 2.17);
}

double
test_gamma_int (void)
{
  return gsl_ran_gamma (r_global, 10.0, 2.17);
}

double
test_gamma_int_pdf (double x)
{
  return gsl_ran_gamma_pdf (x, 10.0, 2.17);
}

double
test_gamma_large (void)
{
  return gsl_ran_gamma (r_global, 20.0, 2.17);
}

double
test_gamma_large_pdf (double x)
{
  return gsl_ran_gamma_pdf (x, 20.0, 2.17);
}

double
test_gaussian (void)
{
  return gsl_ran_gaussian (r_global, 3.0);
}

double
test_gaussian_pdf (double x)
{
  return gsl_ran_gaussian_pdf (x, 3.0);
}

double
test_gaussian_ratio_method (void)
{
  return gsl_ran_gaussian_ratio_method (r_global, 3.0);
}

double
test_gaussian_ratio_method_pdf (double x)
{
  return gsl_ran_gaussian_pdf (x, 3.0);
}

double
test_gaussian_tail (void)
{
  return gsl_ran_gaussian_tail (r_global, 1.7, 0.25);
}

double
test_gaussian_tail_pdf (double x)
{
  return gsl_ran_gaussian_tail_pdf (x, 1.7, 0.25) ;
}

double
test_gaussian_tail1 (void)
{
  return gsl_ran_gaussian_tail (r_global, -1.7, 5.0);
}

double
test_gaussian_tail1_pdf (double x)
{
  return gsl_ran_gaussian_tail_pdf (x, -1.7, 5.0) ;
}

double
test_gaussian_tail2 (void)
{
  return gsl_ran_gaussian_tail (r_global, 0.1, 2.0);
}

double
test_gaussian_tail2_pdf (double x)
{
  return gsl_ran_gaussian_tail_pdf (x, 0.1, 2.0) ;
}

double
test_ugaussian (void)
{
  return gsl_ran_ugaussian (r_global);
}

double
test_ugaussian_pdf (double x)
{
  return gsl_ran_ugaussian_pdf (x);
}

double
test_ugaussian_ratio_method (void)
{
  return gsl_ran_ugaussian_ratio_method (r_global);
}

double
test_ugaussian_ratio_method_pdf (double x)
{
  return gsl_ran_ugaussian_pdf (x);
}

double
test_ugaussian_tail (void)
{
  return gsl_ran_ugaussian_tail (r_global, 3.0);
}

double
test_ugaussian_tail_pdf (double x)
{
  return gsl_ran_ugaussian_tail_pdf (x, 3.0) ;
}

double
test_bivariate_gaussian1 (void)
{
  double x = 0, y = 0;
  gsl_ran_bivariate_gaussian (r_global, 3.0, 2.0, 0.3, &x, &y);
  return x ;
}

double
test_bivariate_gaussian1_pdf (double x)
{
  return gsl_ran_gaussian_pdf (x, 3.0);
}

double
test_bivariate_gaussian2 (void)
{
  double x = 0, y = 0;
  gsl_ran_bivariate_gaussian (r_global, 3.0, 2.0, 0.3, &x, &y);
  return y ;
}

double
test_bivariate_gaussian2_pdf (double y)
{
  int i, n = 10 ;
  double sum = 0 ;
  double a = -10, b = 10, dx = (b - a)/n ;
  for (i = 0; i < n ; i++)
    {
      double x = a + i * dx ;
      sum += gsl_ran_bivariate_gaussian_pdf (x, y, 3.0, 2.0, 0.3) * dx ;
    }
  return sum ;
}

double
test_bivariate_gaussian3 (void)
{
  double x = 0, y = 0;
  gsl_ran_bivariate_gaussian (r_global, 3.0, 2.0, 0.3, &x, &y);
  return x + y ;
}

double
test_bivariate_gaussian3_pdf (double x)
{
  double sx = 3.0, sy = 2.0, r = 0.3;
  double su = (sx+r*sy) ;
  double sv = sy*sqrt(1-r*r) ;
  double sigma = sqrt(su*su + sv*sv) ;

  return gsl_ran_gaussian_pdf (x, sigma);
}

double
test_bivariate_gaussian4 (void)
{
  double x = 0, y = 0;
  gsl_ran_bivariate_gaussian (r_global, 3.0, 2.0, -0.5, &x, &y);
  return x + y ;
}

double
test_bivariate_gaussian4_pdf (double x)
{
  double sx = 3.0, sy = 2.0, r = -0.5;
  double su = (sx+r*sy) ;
  double sv = sy*sqrt(1-r*r) ;
  double sigma = sqrt(su*su + sv*sv) ;

  return gsl_ran_gaussian_pdf (x, sigma);
}

double
test_geometric (void)
{
  return gsl_ran_geometric (r_global, 0.5);
}

double
test_geometric_pdf (unsigned int n)
{
  return gsl_ran_geometric_pdf (n, 0.5);
}

double
test_geometric1 (void)
{
  return gsl_ran_geometric (r_global, 1.0);
}

double
test_geometric1_pdf (unsigned int n)
{
  return gsl_ran_geometric_pdf (n, 1.0);
}

double
test_hypergeometric1 (void)
{
  return gsl_ran_hypergeometric (r_global, 5, 7, 4);
}

double
test_hypergeometric1_pdf (unsigned int n)
{
  return gsl_ran_hypergeometric_pdf (n, 5, 7, 4);
}

double
test_hypergeometric2 (void)
{
  return gsl_ran_hypergeometric (r_global, 5, 7, 11);
}

double
test_hypergeometric2_pdf (unsigned int n)
{
  return gsl_ran_hypergeometric_pdf (n, 5, 7, 11);
}

double
test_hypergeometric3 (void)
{
  return gsl_ran_hypergeometric (r_global, 5, 7, 1);
}

double
test_hypergeometric3_pdf (unsigned int n)
{
  return gsl_ran_hypergeometric_pdf (n, 5, 7, 1);
}

double
test_hypergeometric4 (void)
{
  return gsl_ran_hypergeometric (r_global, 5, 7, 20);
}

double
test_hypergeometric4_pdf (unsigned int n)
{
  return gsl_ran_hypergeometric_pdf (n, 5, 7, 20);
}

double
test_hypergeometric5 (void)
{
  return gsl_ran_hypergeometric (r_global, 2, 7, 5);
}

double
test_hypergeometric5_pdf (unsigned int n)
{
  return gsl_ran_hypergeometric_pdf (n, 2, 7, 5);
}

double
test_hypergeometric6 (void)
{
  return gsl_ran_hypergeometric (r_global, 2, 10, 3);
}

double
test_hypergeometric6_pdf (unsigned int n)
{
  return gsl_ran_hypergeometric_pdf (n, 2, 10, 3);
}

double
test_gumbel1 (void)
{
  return gsl_ran_gumbel1 (r_global, 3.12, 4.56);
}

double
test_gumbel1_pdf (double x)
{
  return gsl_ran_gumbel1_pdf (x, 3.12, 4.56);
}

double
test_gumbel2 (void)
{
  return gsl_ran_gumbel2 (r_global, 3.12, 4.56);
}

double
test_gumbel2_pdf (double x)
{
  return gsl_ran_gumbel2_pdf (x, 3.12, 4.56);
}

double
test_landau (void)
{
  return gsl_ran_landau (r_global);
}

double
test_landau_pdf (double x)
{
  return gsl_ran_landau_pdf (x);
}

double
test_levy1 (void)
{
  return gsl_ran_levy (r_global, 5.0, 1.0);
}

double
test_levy1_pdf (double x)
{
  return gsl_ran_cauchy_pdf (x, 5.0);
}

double
test_levy2 (void)
{
  return gsl_ran_levy (r_global, 5.0, 2.0);
}

double
test_levy2_pdf (double x)
{
  return gsl_ran_gaussian_pdf (x, sqrt(2.0) * 5.0 );
}

double
test_levy1a (void)
{
  return gsl_ran_levy (r_global, 5.0, 1.01);
}

double
test_levy1a_pdf (double x)
{
  return gsl_ran_cauchy_pdf (x, 5.0);
}

double
test_levy2a (void)
{
  return gsl_ran_levy (r_global, 5.0, 1.99);
}

double
test_levy2a_pdf (double x)
{
  return gsl_ran_gaussian_pdf (x, sqrt(2.0) * 5.0 );
}

double
test_levy_skew1 (void)
{
  return gsl_ran_levy_skew (r_global, 5.0, 1.0, 0.0);
}

double
test_levy_skew1_pdf (double x)
{
  return gsl_ran_cauchy_pdf (x, 5.0);
}

double
test_levy_skew2 (void)
{
  return gsl_ran_levy_skew (r_global, 5.0, 2.0, 0.0);
}

double
test_levy_skew2_pdf (double x)
{
  return gsl_ran_gaussian_pdf (x, sqrt(2.0) * 5.0 );
}

double
test_levy_skew1a (void)
{
  return gsl_ran_levy_skew (r_global, 5.0, 1.01, 0.0);
}

double
test_levy_skew1a_pdf (double x)
{
  return gsl_ran_cauchy_pdf (x, 5.0);
}

double
test_levy_skew2a (void)
{
  return gsl_ran_levy_skew (r_global, 5.0, 1.99, 0.0);
}

double
test_levy_skew2a_pdf (double x)
{
  return gsl_ran_gaussian_pdf (x, sqrt(2.0) * 5.0 );
}

double
test_levy_skew1b (void)
{
  return gsl_ran_levy_skew (r_global, 5.0, 1.01, 0.001);
}

double
test_levy_skew1b_pdf (double x)
{
  return gsl_ran_cauchy_pdf (x, 5.0);
}

double
test_levy_skew2b (void)
{
  return gsl_ran_levy_skew (r_global, 5.0, 1.99, 0.001);
}

double
test_levy_skew2b_pdf (double x)
{
  return gsl_ran_gaussian_pdf (x, sqrt(2.0) * 5.0 );
}

double
test_logistic (void)
{
  return gsl_ran_logistic (r_global, 3.1);
}

double
test_logistic_pdf (double x)
{
  return gsl_ran_logistic_pdf (x, 3.1);
}

double
test_logarithmic (void)
{
  return gsl_ran_logarithmic (r_global, 0.4);
}

double
test_logarithmic_pdf (unsigned int n)
{
  return gsl_ran_logarithmic_pdf (n, 0.4);
}

double
test_lognormal (void)
{
  return gsl_ran_lognormal (r_global, 2.7, 1.3);
}

double
test_lognormal_pdf (double x)
{
  return gsl_ran_lognormal_pdf (x, 2.7, 1.3);
}

double
test_negative_binomial (void)
{
  return gsl_ran_negative_binomial (r_global, 0.3, 20.0);
}

double
test_negative_binomial_pdf (unsigned int n)
{
  return gsl_ran_negative_binomial_pdf (n, 0.3, 20.0);
}

double
test_pascal (void)
{
  return gsl_ran_pascal (r_global, 0.8, 3);
}

double
test_pascal_pdf (unsigned int n)
{
  return gsl_ran_pascal_pdf (n, 0.8, 3);
}

double
test_pareto (void)
{
  return gsl_ran_pareto (r_global, 1.9, 2.75);
}

double
test_pareto_pdf (double x)
{
  return gsl_ran_pareto_pdf (x, 1.9, 2.75);
}

double
test_rayleigh (void)
{
  return gsl_ran_rayleigh (r_global, 1.9);
}

double
test_rayleigh_pdf (double x)
{
  return gsl_ran_rayleigh_pdf (x, 1.9);
}

double
test_rayleigh_tail (void)
{
  return gsl_ran_rayleigh_tail (r_global, 2.7, 1.9);
}

double
test_rayleigh_tail_pdf (double x)
{
  return gsl_ran_rayleigh_tail_pdf (x, 2.7, 1.9);
}

double
test_poisson (void)
{
  return gsl_ran_poisson (r_global, 5.0);
}

double
test_poisson_pdf (unsigned int n)
{
  return gsl_ran_poisson_pdf (n, 5.0);
}

double
test_poisson_large (void)
{
  return gsl_ran_poisson (r_global, 30.0);
}

double
test_poisson_large_pdf (unsigned int n)
{
  return gsl_ran_poisson_pdf (n, 30.0);
}

double
test_tdist1 (void)
{
  return gsl_ran_tdist (r_global, 1.75);
}

double
test_tdist1_pdf (double x)
{
  return gsl_ran_tdist_pdf (x, 1.75);
}

double
test_tdist2 (void)
{
  return gsl_ran_tdist (r_global, 12.75);
}

double
test_tdist2_pdf (double x)
{
  return gsl_ran_tdist_pdf (x, 12.75);
}

double
test_laplace (void)
{
  return gsl_ran_laplace (r_global, 2.75);
}

double
test_laplace_pdf (double x)
{
  return gsl_ran_laplace_pdf (x, 2.75);
}

double
test_weibull (void)
{
  return gsl_ran_weibull (r_global, 3.14, 2.75);
}

double
test_weibull_pdf (double x)
{
  return gsl_ran_weibull_pdf (x, 3.14, 2.75);
}

double
test_weibull1 (void)
{
  return gsl_ran_weibull (r_global, 2.97, 1.0);
}

double
test_weibull1_pdf (double x)
{
  return gsl_ran_weibull_pdf (x, 2.97, 1.0);
}


#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <gsl/gsl_math.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_roots.h>

#include "cosmocalc.h"
#include "haloprofs.h"
#include "weaklens.h"

double eps_fun(double x, void *p)
{
  double *v = (double*)p;
  return NFWprof_menc(x,v[0],v[1],v[2]) - v[3];
}

double solve_scale_nfw(gsl_root_fsolver *s, gsl_function F, double *p)
{
  int status;
  int iter,max_iter = 100;
  double x_lo = 0.0,x_hi = 5.0;
  double r;

  iter = 0;
  gsl_root_fsolver_set(s,&F,x_lo,x_hi);
  do
    {
      iter++;
      status = gsl_root_fsolver_iterate(s);
      r = gsl_root_fsolver_root(s);
      x_lo = gsl_root_fsolver_x_lower(s);
      x_hi = gsl_root_fsolver_x_upper(s);
      status = gsl_root_test_interval(x_lo, x_hi,0,0.001);
    }
  while (status == GSL_CONTINUE && iter < max_iter);

  return r;
}

int main(int argc, char **argv)
{
  //init
  cosmoData.cosmoNum = 1;
  cosmoData.OmegaM = 0.27;
  cosmoData.OmegaL = 0.73;
  cosmoData.OmegaB = 0.045;
  cosmoData.OmegaNu = 0.0;
  cosmoData.OmegaK = 0.0;
  cosmoData.h = 0.7;
  cosmoData.w0 = -1.0;
  cosmoData.wa = 0.0;
  cosmoData.SpectralIndex = 0.96;
  cosmoData.Sigma8 = 0.8;
  cosmoData.useSmoothTransFunc = 0;

  //prints mass function in bins to stdout
  double a = atof(argv[1]);
  double mp = atof(argv[2]);
  double np;
  double mmin = 1e1;
  double mmax = 1e18;
  long Nm = 2000;
  double m,dlnm = log(mmax/mmin)/Nm;
  long i;
  double rvir,rs,c;

  cosmoData.delta = 200.0*RHO_CRIT*hubble_noscale(a)*hubble_noscale(a)/(cosmoData.OmegaM*RHO_CRIT/a/a/a);
  //fprintf(stderr,"delta = %f\n",cosmoData.delta);

  gsl_root_fsolver *s;
  gsl_function F;
  double p[4],r1,rN,rN2;

  F.function = &eps_fun;
  F.params = p;
  s = gsl_root_fsolver_alloc(gsl_root_fsolver_brent);

  fprintf(stdout,"# m rs rvir r1 rN rN2\n");
  for(i=0;i<Nm;++i)
    {
      m = exp(dlnm*i)*mmin;
      rvir = pow(m/(cosmoData.delta*RHO_CRIT*cosmoData.OmegaM*4.0/3.0*M_PI),1.0/3.0);
      c = duffy2008_concNFW(m,a);
      //if(c < 4.0)
      //c = 4.0;
      rs = rvir/c;

      np = m/mp;

      if(np >= 1.0)
	{
	  p[0] = m;
	  p[1] = rvir;
	  p[2] = c;
	  p[3] = mp;
	  r1 = solve_scale_nfw(s,F,p);
	}
      else
	r1 = -1.0;

      if(np > 100)
	{
	  p[0] = m;
	  p[1] = rvir;
	  p[2] = c;
	  p[3] = mp*100.0;
	  rN = solve_scale_nfw(s,F,p);
	}
      else
	rN = -1.0;

      if(np > 10)
	{
	  p[0] = m;
	  p[1] = rvir;
	  p[2] = c;
	  p[3] = mp*10.0;
	  rN2 = solve_scale_nfw(s,F,p);
	}
      else
	rN2 = -1.0;

      fprintf(stdout,"%e %e %e %e %e %e\n",m,rs,rvir,r1,rN,rN2);
    }

  gsl_root_fsolver_free(s);

  return 0;
}


// 2019-05-16 by Liya Ding

#include <string>
#include <math.h>
#include <gsl/gsl_poly.h>
#include <algorithm>
#include <stdlib.h>
#include <iostream>
//#include "convolver.h"

static double approxZero(double n);
static double opposite(double theta);
static int getTemplateN(int M);
double* getWeights(int M, double sigma);
void computeBaseTemplates(double* input, int nx, int ny, int M, int borderCondition, double sigma, double** templates);
double pointRespM1(int i, double angle, double* alpha, double** templates);
double pointRespM2(int i, double angle, double* alpha, double** templates);
double pointRespM3(int i, double angle, double* alpha, double** templates);
double pointRespM4(int i, double angle, double* alpha, double** templates);
double pointRespM5(int i, double angle, double* alpha, double** templates);

int getRealRoots(double* z, int nz, double* roots);
void filterM1(double** templates, int nx, int ny, double* alpha, double* response, double* orientation);
void filterM2(double** templates, int nx, int ny, double* alpha, double* response, double* orientation);
void filterM3(double** templates, int nx, int ny, double* alpha, double* response, double* orientation);
void filterM4(double** templates, int nx, int ny, double* alpha, double* response, double* orientation);
void filterM5(double** templates, int nx, int ny, double* alpha, double* response, double* orientation);
int mirror(int x, int nx);
double interp(double* image, int nx, int ny, double x, double y);
void computeNMS(double* response, double* orientation, double* nms, int nx, int ny);
void steerablefilter2Dcore(double * input, long* in_sz, int M, double sigma,double* &response, double* &orientation, double* &nms);


#include <algorithm>
#include <cstdio>
#include <random>
#include <gsl/gsl_multimin.h>

#include "dgp.h"
#include "vec.h"
#include "fobj_sph.h"

typedef struct
{
   double tau;
   double lambda;
   double rho;
   dgp_t *G;
} gsl_params_t;

void gsl_fdf(const gsl_vector *x, void *params, double *f, gsl_vector *g)
{
   gsl_params_t *p = (gsl_params_t *)params;
   *f = fobj_sph(p->lambda, p->tau, *(p->G), x->data, g->data);
}

void gsl_df(const gsl_vector *x, void *params, gsl_vector *g)
{
   gsl_params_t *p = (gsl_params_t *)params;
   fobj_sph(p->lambda, p->tau, *(p->G), x->data, g->data);
}

double gsl_f(const gsl_vector *x, void *params)
{
   gsl_params_t *p = (gsl_params_t *)params;
   return fobj_sph(p->lambda, p->tau, *(p->G), x->data, NULL);
}

double init_tau(dgp_t &G)
{
   std::vector<double> tau_all(G.m_nedges);

   for (int i = 0; i < G.m_nedges; i++)
   {
      tau_all[i] = (G.m_l[i] + G.m_u[i]) / 2.0;
   }

   std::sort(tau_all.begin(), tau_all.end());

   return tau_all[(int)(0.75 * G.m_nedges)];;
}

double gsl_vector_norm(gsl_vector *v)
{
   double v_nrm = 0.0;
   for (size_t i = 0; i < v->size; i++)
   {
      v_nrm += v->data[i] * v->data[i];
   }
   return sqrt(v_nrm);
}

void init_sol(dgp_t &G, double *x)
{
   double dij, lij, uij, *xi, *xj;
   double y[3], y_nrm;
   int *neighs, nneighs, j;
   std::vector<int> s(G.m_nnodes);
   std::vector<bool> b(G.m_nnodes, false);

   // s = [0, 1, 2, ..., nnodes-1]
   for (int k = 0; k < G.m_nnodes; k++)
   {
      s[k] = k;
   }
   std::random_shuffle(s.begin(), s.end());

   // x = zeros(3 * nnodes)
   for (int k = 0; k < (3 * G.m_nnodes); ++k)
   {
      x[k] = 0.0;
   }

   std::default_random_engine rndEngine;
   std::normal_distribution<double> gaussian(0.0, 1.0);

   for (int k = 0; k < G.m_nnodes; k++)
   {
      int i = s[k];
      xi = &(x[3 * i]);
      G.neighs(i, nneighs, &neighs);
      for (int jj = 0; jj < nneighs; jj++)
      {
         j = neighs[jj];
         if (b[j])
         {
            continue;
         }
         G.vals(i, j, NULL, &lij, &uij);
         dij = (lij + uij) / 2.0;
         xj = &(x[3 * j]);
         // y is randomly distributed inside the sphere((0,0,0),1)
         vec_set(y, gaussian(rndEngine), gaussian(rndEngine), gaussian(rndEngine));
         y_nrm = vec_norm(y);
         // xj = dij * (y / y_nrm) + xi  (randomly distributed over sphere(xi, dij))
         vec_axpby(dij / y_nrm, y, 1.0, xi, xj);
      }
   }
}

void sph_print_iter(int i, gsl_params_t &p, double f, double fs, gsl_vector *g, int k)
{
   if ((i % 10) == 0)
   {
      printf(" iter |    tau   |    f     |    fs    |  nrm(g)  | #f_calls \n");
   }
   printf("%5d | %3.2e | %3.2e | %3.2e | %3.2e | %5d\n", i, p.tau, f, fs, gsl_vector_norm(g), k);
}

bool sph(dgp_t &G, double ftol, double &f, double *x, bool verbose)
{
   gsl_params_t gsl_params;
   gsl_params.lambda = 0.5;
   gsl_params.rho = 0.99;
   gsl_params.tau = init_tau(G);
   gsl_params.G = &G;

   gsl_vector *y = gsl_vector_alloc(3 * G.m_nnodes);
   gsl_vector *g = gsl_vector_alloc(3 * G.m_nnodes);

   init_sol(G, y->data);

   const gsl_multimin_fdfminimizer_type *fdfmin_method = gsl_multimin_fdfminimizer_vector_bfgs2;
   gsl_multimin_fdfminimizer *fdfmin;

   gsl_multimin_function_fdf gsl_fobj;
   gsl_fobj.n = 3 * G.m_nnodes;
   gsl_fobj.f = gsl_f;
   gsl_fobj.df = gsl_df;
   gsl_fobj.fdf = gsl_fdf;
   gsl_fobj.params = (void *)(&gsl_params);

   double fs;
   bool solved = false;
   int maxit = 1000, k = 0, status;
   for (int i = 0; i < maxit; i++)
   {
      fs = fobj_sph(gsl_params.lambda, gsl_params.tau, G, y->data, g->data);
      f = fobj_sph(gsl_params.lambda, 1E-16, G, y->data, NULL);

      if (verbose)
      {
         sph_print_iter(i, gsl_params, f, fs, g, k);
      }

      if (f < ftol)
      {
         solved = true;
         break;
      }

      gsl_params.tau *= gsl_params.rho;
      fdfmin = gsl_multimin_fdfminimizer_alloc(fdfmin_method, 3 * G.m_nnodes);
      gsl_multimin_fdfminimizer_set(fdfmin, &gsl_fobj, y, 0.01, 1E-3);

      // call local optimization
      for (k = 0, status = GSL_CONTINUE; k < 1000 && status == GSL_CONTINUE; k++)
      {
         status = gsl_multimin_fdfminimizer_iterate(fdfmin);
         if (status)
         {
            break;
         }
         status = gsl_multimin_test_gradient(fdfmin->gradient, 1E-8);
      }

      // copy from fdfmin to y
      for (int j = 0; j < 3 * G.m_nnodes; j++)
      {
         y->data[j] = fdfmin->x->data[j];
      }

      gsl_multimin_fdfminimizer_free(fdfmin);
   }

   // copy solution from y to x
   for (int i = 0; i < 3 * G.m_nnodes; i++)
   {
      x[i] = y->data[i];
   }

   gsl_vector_free(y);

   return solved;
}


/* histogram/gsl-histogram.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 <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <gsl/gsl_histogram.h>

void
print_help(void)
{
  fprintf (stderr, "Usage: gsl-histogram [-u] xmin xmax [n]\n");
  fprintf (stderr, "Computes a histogram of the data on stdin using n bins from xmin to xmax.\n"
                   "If n is unspecified then bins of integer width are used.\n"
                   "If -u is given, histogram is normalized so that sum of all bins is unity.\n");
}

int
main (int argc, char **argv)
{
  double a = 0.0, b = 1.0;
  size_t n = 10;
  int unit = 0;
  int c;

  while ((c = getopt(argc, argv, "u")) != (-1))
    {
      switch (c)
        {
          case 'u':
            unit = 1;
            break;

          default:
            print_help();
            exit(0);
            break;
        }
    }

  if (argc - optind < 2)
    {
      print_help();
      exit(0);
    }

  a = atof (argv[optind++]);
  b = atof (argv[optind++]);

  if (argc - optind > 0)
    n = atoi (argv[optind++]);
  else
    n = (int)(b - a) ;

  {
    double x;
    gsl_histogram *h = gsl_histogram_alloc (n);

    gsl_histogram_set_ranges_uniform (h, a, b);

    while (fscanf(stdin, "%lg", &x) == 1)
      {
        gsl_histogram_increment(h, x);
      }

#ifdef DISPLAY_STATS
    {
      double mean = gsl_histogram_mean (h);
      double sigma = gsl_histogram_sigma (h);
      fprintf (stdout, "# mean = %g\n", mean);
      fprintf (stdout, "# sigma = %g\n", sigma);
    }
#endif

    /* normalize histogram if needed */
    if (unit)
      {
        double sum = gsl_histogram_sum(h);

        if (sum > 0.0)
          gsl_histogram_scale(h, 1.0 / sum);
      }

    gsl_histogram_fprintf (stdout, h, "%g", "%g");

    gsl_histogram_free (h);
  }

  return 0;
}


/* fft/gsl_fft_complex_float.h
 *
 * 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.
 */

#ifndef __GSL_FFT_COMPLEX_FLOAT_H__
#define __GSL_FFT_COMPLEX_FLOAT_H__

#include <stddef.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_fft.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

/*  Power of 2 routines  */

int gsl_fft_complex_float_radix2_forward (gsl_complex_packed_array_float data,
                                          const size_t stride,
                                          const size_t n);

int gsl_fft_complex_float_radix2_backward (gsl_complex_packed_array_float data,
                                           const size_t stride,
                                           const size_t n);

int gsl_fft_complex_float_radix2_inverse (gsl_complex_packed_array_float data,
                                          const size_t stride,
                                          const size_t n);

int gsl_fft_complex_float_radix2_transform (gsl_complex_packed_array_float data,
                                            const size_t stride,
                                            const size_t n,
                                            const gsl_fft_direction sign);

int gsl_fft_complex_float_radix2_dif_forward (gsl_complex_packed_array_float data,
                                              const size_t stride,
                                              const size_t n);

int gsl_fft_complex_float_radix2_dif_backward (gsl_complex_packed_array_float data,
                                               const size_t stride,
                                               const size_t n);

int gsl_fft_complex_float_radix2_dif_inverse (gsl_complex_packed_array_float data,
                                              const size_t stride,
                                              const size_t n);

int gsl_fft_complex_float_radix2_dif_transform (gsl_complex_packed_array_float data,
                                                const size_t stride,
                                                const size_t n,
                                                const gsl_fft_direction sign);

/*  Mixed Radix general-N routines  */

typedef struct
  {
    size_t n;
    size_t nf;
    size_t factor[64];
    gsl_complex_float *twiddle[64];
    gsl_complex_float *trig;
  }
gsl_fft_complex_wavetable_float;

typedef struct
{
    size_t n;
    float *scratch;
}
gsl_fft_complex_workspace_float;

gsl_fft_complex_wavetable_float *gsl_fft_complex_wavetable_float_alloc (size_t n);

void gsl_fft_complex_wavetable_float_free (gsl_fft_complex_wavetable_float * wavetable);

gsl_fft_complex_workspace_float *gsl_fft_complex_workspace_float_alloc (size_t n);

void gsl_fft_complex_workspace_float_free (gsl_fft_complex_workspace_float * workspace);

int gsl_fft_complex_float_memcpy (gsl_fft_complex_wavetable_float * dest,
                               gsl_fft_complex_wavetable_float * src);

int gsl_fft_complex_float_forward (gsl_complex_packed_array_float data,
                                   const size_t stride,
                                   const size_t n,
                                   const gsl_fft_complex_wavetable_float * wavetable,
                                   gsl_fft_complex_workspace_float * work);

int gsl_fft_complex_float_backward (gsl_complex_packed_array_float data,
                                    const size_t stride,
                                    const size_t n,
                                    const gsl_fft_complex_wavetable_float * wavetable,
                                    gsl_fft_complex_workspace_float * work);

int gsl_fft_complex_float_inverse (gsl_complex_packed_array_float data,
                                   const size_t stride,
                                   const size_t n,
                                   const gsl_fft_complex_wavetable_float * wavetable,
                                   gsl_fft_complex_workspace_float * work);

int gsl_fft_complex_float_transform (gsl_complex_packed_array_float data,
                                     const size_t stride, const size_t n,
                                     const gsl_fft_complex_wavetable_float * wavetable,
                                     gsl_fft_complex_workspace_float * work,
                                     const gsl_fft_direction sign);

__END_DECLS

#endif /* __GSL_FFT_COMPLEX_FLOAT_H__ */


#ifndef DATA_PROCESS_H
#define DATA_PROCESS_H

#include <stdio.h>
#include <string.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_statistics.h>
#include <math.h>

void support_degree_generator(gsl_matrix*, double*);
void eigenvec_calc(gsl_vector*, gsl_matrix*, gsl_matrix*);
void principal_comp_calc(gsl_matrix*, gsl_matrix*, gsl_matrix*);
void contri_rate_calc_kth(gsl_vector*, double*);
void major_contri_calc(double*, double*);
void integ_supp_score_calc(double*, gsl_matrix*, gsl_vector*);
void elliminate_incorrect_data(gsl_vector*, int*);
void weight_coeff_calc(gsl_vector*, int*, double*);
double fused_output(double*, double*);

#endif


//This file contains mostly untested code for computing splined
//periodic flux tube configurations

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <builtin_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv2.h>
#include "intT.h"

//extern "C"
//__device__ __host__ float interp(float rho2, float *rho2sp, float4 *coefs);

int func (double t, const double y[], double f[],
           void *params)
{
    double *p = (double *)params;
    double lambda2 = p[0];
    double a = p[1];
    f[0] = 2.0/lambda2*t*exp(-pow(sin(pi*t/a),2)/lambda2);
     return GSL_SUCCESS;
}

int jac(double t, const double y[], double *dfdy, double dfdt[], void *params)
{
    double *p = (double *)params;
    double lambda2 = p[0];
    double a = p[1];

    gsl_matrix_view dfdy_mat
        = gsl_matrix_view_array(dfdy, 1, 1);
    gsl_matrix * m = &dfdy_mat.matrix;

    double der=0.0;

    gsl_matrix_set (m, 0, 0, der);
    dfdt[0] = 2.0/lambda2*exp(-pow(sin(pi*t/a),2)/lambda2)*(1.0-
	2.0*pi/(a*lambda2)*t*sin(pi*t/a)*cos(pi/a*t));
    return GSL_SUCCESS;
}

float4* flspline(double *fli, float *rho2, int N)
//Computes the cubic spline coefficients for f_lambda(rho^2)
//uses Burden and Faires algorithm 3.5 (Clamped Cubic Spline)
//Step numbers refer to this textbook
{
  int i;
  double* h;
  double* l;
  double* mu;
  double* z;
  double* alpha;
  float4* flscoefs;
  h=(double *)malloc(N*sizeof(*h));
  l=(double *)malloc(N*sizeof(*l));
  mu=(double *)malloc(N*sizeof(*mu));
  z=(double *)malloc(N*sizeof(*z));
  alpha = (double *)malloc(N*sizeof(*alpha));
  flscoefs=(float4 *)malloc(N*sizeof(*flscoefs));

//Step 1
  for(i=0;i<N;i++)
  {
	flscoefs[i].x = fli[i];
  }
  for(i=0;i<N-1;i++) h[i]=rho2[i+1]-rho2[i];
//Step 2
  alpha[0]=3.0*(fli[1]-fli[0])/h[0];
  alpha[N-1]=-3.0*(fli[N-1]-fli[N-2])/h[N-2];
//Step 3
  for(i=1;i<N-1;i++)
  {
	//printf("%d \n",i);
	alpha[i]=3.0/h[i]*(fli[i+1]-fli[i])-3.0/h[i-1]*(fli[i]-fli[i-1]);
  }
//Step 4 -
//Steps 4-7 solve a tridiagonal system using Crout factorization (Burden and Faires alg 6.7)
  l[0]=2.0*h[0];
  mu[0]=0.5;
  z[0]=alpha[0]/l[0];
//Step 5
  for(i=1;i<N-1;i++)
  {
	l[i]=2.0*(rho2[i+1]-rho2[i-1])-h[i-1]*mu[i-1];
	mu[i]=h[i]/l[i];
	z[i]=(alpha[i]-h[i-1]*z[i-1])/l[i];
  }
//Step 6
  l[N-1]=h[N-2]*(2.0-mu[N-2]);
  z[N-1]=(alpha[N-1]-h[N-2]*z[N-2])/l[N-1];
  flscoefs[N-1].z = z[N-1];
//Step 7
  for(i=N-2;i>-1;i--)
  {
	flscoefs[i].z = z[i]-mu[i]*flscoefs[i+1].z;
	flscoefs[i].y = (fli[i+1]-fli[i])/h[i] - h[i]*(flscoefs[i+1].z+2.0*flscoefs[i].z)/3.0;
	flscoefs[i].w = (flscoefs[i+1].z-flscoefs[i].z)/(3.0*h[i]);
  }
//Free allocated memory
  free(h);free(l);free(mu);free(z);free(alpha);
//Step 8
  return flscoefs;

}

float4* getspline(float* rho2, double lambda2, double a, int Npoints)
{

    double params[2];
    const gsl_odeiv2_step_type * T = gsl_odeiv2_step_rk8pd;
    float4 *coefs;

    gsl_odeiv2_step * s  = gsl_odeiv2_step_alloc (T, 1);
    gsl_odeiv2_control * c  = gsl_odeiv2_control_y_new (1e-9,1e-7);
    gsl_odeiv2_evolve * e  = gsl_odeiv2_evolve_alloc (1);

    params[0] = lambda2;
    params[1] = a;

    gsl_odeiv2_system sys = {func, jac, 1, params};

    double rho = 0.0;
    double rhof = 20.0;
    double h = rhof/((double)Npoints);
    double y[1] = { 0.00 };
    double *flambda;
    int i=0;

    flambda=(double *)malloc(Npoints*sizeof(*flambda));
    while (i<Npoints)
    {
        int status = gsl_odeiv2_evolve_apply_fixed_step (e, c, s,
                                        &sys,
                                        &rho, h, y);
 	if(status == GSL_FAILURE)
	{
		printf("ERROR:GSL_FAILURE\n");
		break;
	}
        else if (status != GSL_SUCCESS)
            break;
        rho2[i]=rho*rho;
	flambda[i]=y[0];
	i+=1;
	//rho+=h;

    }

    gsl_odeiv2_evolve_free (e);
    gsl_odeiv2_control_free (c);
    gsl_odeiv2_step_free (s);

    coefs=flspline(flambda,rho2,Npoints);
    free(flambda);
    return coefs;
}

double getinterp(float rho2, float *rho2sp, float4 *coefs)
{
  int j;
  int upperi=Nspline-1, loweri=0;
  float rho2diff=0.0;
  double flambda;

    //Discover which interval to look in using a binary search
  if(rho2<rho2sp[Nspline-1] && rho2>rho2sp[0])
//  if(0)
  {
   	while(upperi-loweri > 1)
	{
		if(rho2 >= rho2sp[(upperi+loweri)/2]) loweri=(upperi+loweri)/2;
		else upperi=(upperi+loweri)/2;
  	}
  	//interpolate using the jth interval
  	j=loweri;
	rho2diff=rho2-rho2sp[j];
	//rho2diff=0.0;
  	flambda= coefs[j].x+rho2diff*(coefs[j].y+rho2diff*(coefs[j].z+rho2diff*coefs[j].w));
  }
  else
  {
	j=0;
	rho2diff=0.0f;
	flambda=0.0f;
  }

  //*flambda= coefs[j].x+rho2diff*(coefs[j].y+rho2diff*(coefs[j].z+rho2diff*coefs[j].w));
  //*fprime = coefs[j].y+rho2diff*(2.0f*coefs[j].z + rho2diff*(3.0f*coefs[j].w));
  //*flambda=1.0f-exp(-1.0f*rho2);
  //*fprime=1.0f*exp(-1.0f*rho2);
  return flambda;
}

double testspline(float rho2, double lambda2, double a, int Npoints, float *rho2sp, float4 *flcoefs)
{
    printf("testspline: rho2=%f\n",(double)rho2);
    double flodeiv, flspline;
    double params[2];
    const gsl_odeiv2_step_type * T = gsl_odeiv2_step_rk8pd;
    float4 *coefs;

    gsl_odeiv2_step * s  = gsl_odeiv2_step_alloc (T, 1);
    gsl_odeiv2_control * c  = gsl_odeiv2_control_y_new (1e-9,1e-7);
    gsl_odeiv2_evolve * e  = gsl_odeiv2_evolve_alloc (1);

    params[0] = lambda2;
    params[1] = a;

    gsl_odeiv2_system sys = {func, jac, 1, params};

    double rho = 0.0;
    double rhof = sqrt(rho2);
    double h = rhof/((double)Npoints);
    double y[1] = { 0.00 };
    double *flambda;
    int i=0;
    flambda=(double *)malloc(Npoints*sizeof(*flambda));

    while(rho<rhof)
    {

    	int status = gsl_odeiv2_evolve_apply (e, c, s,
                                        &sys,
                                        &rho, rhof, &h, y);
    	if(status == GSL_FAILURE)
    	{
		printf("ERROR:GSL_FAILURE\n");
    	}
    }
    flodeiv=y[0];
    //rho+=h;
    flspline=(double)interp(rho2, rho2sp, flcoefs);
    return (flodeiv-flspline)/flodeiv;
}


/* specfunc/gsl_sf_trig.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_TRIG_H__
#define __GSL_SF_TRIG_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

/* Sin(x) with GSL semantics. This is actually important
 * because we want to control the error estimate, and trying
 * to guess the error for the standard library implementation
 * every time it is used would be a little goofy.
 */
GSL_EXPORT int gsl_sf_sin_e(double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_sin(const double x);

/* Cos(x) with GSL semantics.
 */
GSL_EXPORT int gsl_sf_cos_e(double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_cos(const double x);

/* Hypot(x,y) with GSL semantics.
 */
GSL_EXPORT int gsl_sf_hypot_e(const double x, const double y, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_hypot(const double x, const double y);

/* Sin(z) for complex z
 *
 * exceptions: GSL_EOVRFLW
 */
GSL_EXPORT int gsl_sf_complex_sin_e(const double zr, const double zi, gsl_sf_result * szr, gsl_sf_result * szi);

/* Cos(z) for complex z
 *
 * exceptions: GSL_EOVRFLW
 */
GSL_EXPORT int gsl_sf_complex_cos_e(const double zr, const double zi, gsl_sf_result * czr, gsl_sf_result * czi);

/* Log(Sin(z)) for complex z
 *
 * exceptions: GSL_EDOM, GSL_ELOSS
 */
GSL_EXPORT int gsl_sf_complex_logsin_e(const double zr, const double zi, gsl_sf_result * lszr, gsl_sf_result * lszi);

/* Sinc(x) = sin(pi x) / (pi x)
 *
 * exceptions: none
 */
GSL_EXPORT int gsl_sf_sinc_e(double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_sinc(const double x);

/* Log(Sinh(x)), x > 0
 *
 * exceptions: GSL_EDOM
 */
GSL_EXPORT int gsl_sf_lnsinh_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_lnsinh(const double x);

/* Log(Cosh(x))
 *
 * exceptions: none
 */
GSL_EXPORT int gsl_sf_lncosh_e(const double x, gsl_sf_result * result);
GSL_EXPORT double gsl_sf_lncosh(const double x);

/* Convert polar to rectlinear coordinates.
 *
 * exceptions: GSL_ELOSS
 */
GSL_EXPORT int gsl_sf_polar_to_rect(const double r, const double theta, gsl_sf_result * x, gsl_sf_result * y);

/* Convert rectilinear to polar coordinates.
 * return argument in range [-pi, pi]
 *
 * exceptions: GSL_EDOM
 */
GSL_EXPORT int gsl_sf_rect_to_polar(const double x, const double y, gsl_sf_result * r, gsl_sf_result * theta);

/* Sin(x) for quantity with an associated error.
 */
GSL_EXPORT int gsl_sf_sin_err_e(const double x, const double dx, gsl_sf_result * result);

/* Cos(x) for quantity with an associated error.
 */
GSL_EXPORT int gsl_sf_cos_err_e(const double x, const double dx, gsl_sf_result * result);

/* Force an angle to lie in the range (-pi,pi].
 *
 * exceptions: GSL_ELOSS
 */
GSL_EXPORT int gsl_sf_angle_restrict_symm_e(double * theta);
GSL_EXPORT double gsl_sf_angle_restrict_symm(const double theta);

/* Force an angle to lie in the range [0, 2pi)
 *
 * exceptions: GSL_ELOSS
 */
GSL_EXPORT int gsl_sf_angle_restrict_pos_e(double * theta);
GSL_EXPORT double gsl_sf_angle_restrict_pos(const double theta);

GSL_EXPORT int gsl_sf_angle_restrict_symm_err_e(const double theta, gsl_sf_result * result);

GSL_EXPORT int gsl_sf_angle_restrict_pos_err_e(const double theta, gsl_sf_result * result);

__END_DECLS

#endif /* __GSL_SF_TRIG_H__ */


#include <stdio.h>

#include <gsl/gsl_monte.h>
#include <gsl/gsl_monte_vegas.h>
#include <gsl/gsl_math.h>
#include "timer.c"
#include "timer.h"
#include "integrand.h"

double dipole_approx (double r);

double gaussian (double *x, int dim);

int main (void)
{
    double res, err;

    size_t dim = 6;
    double x1[] = { 0., 0., 0., 0., 0., 0., };
    double xu[] = { 1., 1., 1., 1., 1., 1., };
    double distmin = 1.001;
    double distmax = 4.;
    double dist;
    int numpoints = 20;
    double nt = (distmax - distmin) / (numpoints - 1);
    double vegas[20], dipole[20], distance[20];
//vegas integration
    gsl_rng *r = gsl_rng_alloc (gsl_rng_taus2);
    unsigned long seed = 1UL;

    gsl_rng_set (r, seed);

    size_t calls = 1000000;

    dist = distmin;

    gsl_monte_function G = { &g, dim, &dist };

    gsl_monte_vegas_state *sv = gsl_monte_vegas_alloc (dim);

    gsl_monte_vegas_init (sv);

    timer_start ();

    for (int i = 0; i < numpoints; i++){
        gsl_monte_vegas_integrate (&G, x1, xu, dim, calls / 5, r, sv, &res,
            &err);
        do
        {
            gsl_monte_vegas_integrate (&G, x1, xu, dim, calls, r, sv, &res,
                &err);
	    fflush(stdout);
        }
        while (fabs (gsl_monte_vegas_chisq (sv) - 1.0) > 0.2);
        dist += nt;
        vegas[i] = res;
    distance[i] = dist;
    dipole[i] = -2. / pow (dist, 3.);
    }
    timer_stop();
    //double vegastime = timer_stop();
     gsl_monte_vegas_free (sv);

//Monte carlo integration
    double sum;
    double x[6];

    long i, j, nn;
    nn = 1000000;

    timer_start ();

    double monte[20];

    dist = distmin;
    for (j = 0; j < numpoints; j++)
    {
        sum = 0.;
        for (i = 0; i < nn; i++)
        {
            for (int k = 0; k < (int) dim; k++)
            {
                x[k] = gsl_rng_uniform (r);
            }
            sum += g (x, dim, &dist);
        }
        res = sum/nn;
	fflush(stdout);
        dist += nt;
	monte[j] = res;
    }
    timer_stop ();
    //double montetime = timer_stop();
    //printf("Vegas time: %f Monte time: %f", vegastime, montetime);

    gsl_rng_free(r);

    double monteerr = 0.0;

    for (int jj = 0; jj < numpoints; jj++)
    {
    monteerr += fabs(monte[jj] - vegas[jj]);
    }
//prints the output of each integration
   printf("#    Dist               Vegas           Monte       Dipolapprox\n");
    for( int l = 0; l < numpoints; l++)
    {
    double dd = distance[l];
    double vv = fabs(vegas[l]);
    double mm = fabs(monte[l]);
    double di = fabs(dipole[l]);
    printf("   %.6f         %.6f       %.6f      %.6f\n", dd, vv, mm, di);
    }

    return 0;
}


/**
 * File: trace_conf.h
 */

#ifndef _TRACE_CONF_H
#define _TRACE_CONF_H

#include <string.h>
#include <math.h>
#include <gsl/gsl_vector.h>
#include "aXe_grism.h"
#include "aXe_errors.h"
#include "spc_cfg.h"
#include "disp_conf.h"

/**
	A structure to contain the coefficient of a 2D
	polynomial that can be used to compute the given
	coefficient of the polynomial dispersion relation
	as at a particular i.j location in the image
*/
typedef struct
{
     gsl_vector *pol;		/* A vector containing the 2D polynomial coefficients */
     d_point offset;		/* X and Y offsets to apply to input coordinates */
     d_point cpoint;		/* The detector location at which this structure was computed */
     int ID;			/* The ID of the beam for which this coefficient is defined */
     char file[MAXCHAR];
}
tracestruct;

extern int
get_beam_trace_norder (char *filename, int beamID);

extern gsl_vector *
get_beam_trace_order (char *filename, int beamID, int order);

extern float
get_trace_coeff_at_pos (char *filename, int beamID, int order, d_point p);

extern gsl_vector *
get_trace_coeffs_at_pos (char *filename, int beamID, d_point p);

extern gsl_vector *
get_beam_trace_xoff (char *filename, int beamID);

extern gsl_vector *
get_beam_trace_yoff (char *filename, int beamID);

extern float
get_trace_xoff_at_pos (char *filename, int beamID, d_point p);

extern float
get_trace_yoff_at_pos (char *filename, int beamID, d_point p);

extern float
eval_trace_off_at_pos (gsl_vector *coeffs, d_point p, int beamID);

extern tracestruct *
get_tracestruct_at_pos (char *filename, int beamID, d_point p);

extern void
tracestruct_fprintf (FILE * file, tracestruct * trace);

extern void
free_tracestruct (tracestruct * trace);
#endif


/*
 * Gsl_vector_3: test gsl_vector
 *
 * Copyright (c) 2012 Jérémie Decock
 *
 * Required: GSL library (libgsl0-dev)
 * Usage: gcc gsl_vector_3.c -lgsl -lgslcblas -lm
 *    or: gcc gsl_vector_3.c $(pkg-config --libs gsl)
 */

#include <gsl/gsl_vector.h>

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

    gsl_vector * v1 = gsl_vector_calloc(10);
    gsl_vector * v2 = gsl_vector_calloc(10);

    gsl_vector_set_all(v1, 2.0);
    gsl_vector_set_all(v2, 6.0);

    gsl_vector_mul(v1, v2);

    gsl_vector_fprintf(stdout, v1, "%.2f");

    gsl_vector_free(v1);
    gsl_vector_free(v2);

    return 0;
}


#ifndef NUFFTW_PLAN_H
#define NUFFTW_PLAN_H

#include <complex>
#include <cmath>
#include <limits>               // st::numeric_limits
#include <algorithm>            // std::max
#include <gsl/gsl_sf_bessel.h>  // Bessel functions
#include <gsl/gsl_sf_lambert.h> // Lambert W functions
#include <fftw3.h>
#include "timer.h"

namespace nufftw
{
    using namespace std::complex_literals;
    typedef std::complex<double> complex;

    struct options
    {
        double tol = std::numeric_limits<double>::epsilon();
        unsigned int fftw_flags = FFTW_ESTIMATE;
    };

    namespace details
    {
        void cheb_vals(int n, int p, const double* x, double* vals);
        void bessel_coeffs(int r, double gam, complex* cfs);
    }

    class plan_base
    {
    	public:
    		plan_base(const int* ns_, complex* in_, complex* out_, const options& opts_ = options()) :
                in(in_), out(out_), opts(opts_)
            {
                n=1;
                for (int i=0; i<N; ++i) {
                    ns[i]=ns_[i];
                    n*=ns[i];
                }
            }

            virtual ~plan_base()
            {
                fftw_destroy_plan(fp);
            }

    		virtual void execute() = 0;

        protected:
            int m;               // Number of nonuniform points
            int ns[N];           // Size of the problem in each dimension
            int n;               // Total size of the problem
            complex* in;         // Input coefficients
            complex* out;        // Output values
            fftw_plan fp;        // FFTW plans
            options opts;        // Options
            double gam;          // Perturbation parameter
            int r;               // Rank parameter
    };

    class plan1 : public plan_base<N>
    {
        public:
            plan1(const int* n_, complex* in_, complex* out_, const double* omega_, const options& opts_ = options()) :
                plan_base<N>(n_, in_, out_, opts_), omega(omega_)
            {}
            virtual ~plan1();
            virtual void execute();
        protected:
            const double* omega;
            plan1<1>* plans;
    };

    class plan1_2d : public plan_base
    {

    };

    class plan1_1d : public plan_base
    {
        public:
            plan1(int n_, complex* in_, complex* out_, const double* omega_, const options& opts_ = options()) :
                plan_base<1>(&n_, in_, out_, opts_), omega(omega_), t(new int[n]),
                er(new double[n]), temp(new double[n])
            {
                timer::tic();
                gam = 0;
                for (int i=0; i<n; ++i) {
                    int s = std::round(omega[i]);
                    t[i] = s%n;
                    er[i] = omega[i]-s;
                    gam = std::max(gam, std::abs(er[i]));
                }
                timer::toc("Compute gamma");

                timer::tic();
                r = 1;
                if (gam>opts.tol) {
                    // Asymptotic approximation to Lambert-W:
                    //   double xi = std::log(std::log(10./opts.tol)/(7.*gam);
                    //   double log_xi=std::log(xi), inv_xi=1./xi;
                    //   double lw = xi-log_xi*(1+inv_xi*(1+inv_xi*(0.5*log_xi-1)));
                    double lw = gsl_sf_lambert_W0(std::log(10./opts.tol)/(7.*gam));
                    r = std::ceil(5.*gam*std::exp(lw));
                }
                timer::toc("Compute r");

                timer::tic();
                // Plan the FFTs
                fft_data = reinterpret_cast<complex*>(fftw_alloc_complex(n*r));
                // int stride = r;
                int stride = 1;
                // int dist = 1;
                int dist = n;
                fp = fftw_plan_many_dft(1, &n, r,
                    reinterpret_cast<fftw_complex*>(fft_data), NULL, stride, dist,
                    reinterpret_cast<fftw_complex*>(fft_data), NULL, stride, dist,
                    FFTW_BACKWARD, opts.fftw_flags
                );
                timer::toc("Plan the FFTs");

                // Construct a low rank approximation, using Chebyshev expansions
                // for A_K = exp(-2*pi*1im*(x[j]-j/N)*k):

                // Construct a low rank approximation to
                //     A_{jk} = exp(-2*pi*1i*(x_j-s_j/N)*k), 0<=j,k<=N-1,
                // where |x_j-j/N|<= gam <=1/2.  See [1].
                u = new complex[n*r];
                v = new double[n*r];

                // Compute u
                timer::tic();
                double* cheb = new double[n*r];
                complex* bess = new complex[r*r];
                timer::tic();
                double fac = 1./gam;
                for (int i=0; i<n; ++i) temp[i]=er[i]*fac;
                details::cheb_vals(n, r, temp, cheb);
                timer::toc("Chebyshev");
                timer::tic();
                details::bessel_coeffs(r, gam, bess);
                timer::toc("Bessel");
                timer::tic();
                for (int i=0; i<n; ++i) {
                    complex scl = std::exp(-1i*M_PI*er[i]);
                    for (int j=0; j<r; ++j) {
                        u[r*i+j]=0;
                        for (int k=0; k<r; ++k) {
                            u[r*i+j]+=cheb[r*i+k]*bess[r*j+k];
                        }
                        u[r*i+j]*=scl;
                    }
                }
                timer::toc("Matrix multiply");
                delete [] bess;
                delete [] cheb;
                timer::toc("Compute u");

                // Compute v
                timer::tic();
                fac = 2./n;
                temp[0]=-1;
                for (int i=1; i<n; ++i) temp[i]=temp[i-1]+fac;
                details::cheb_vals(n, r, temp, v);
                timer::toc("Compute v");
            }

            virtual ~plan1()
            {
                delete [] t;
                delete [] er;
                delete [] u;
                delete [] v;
                delete [] temp;
                fftw_free(reinterpret_cast<fftw_complex*>(fft_data));
            }

            virtual void execute()
            {
                for (int i=0; i<n*r; ++i) fft_data[i]=0;
                for (int i=0; i<n; ++i) {
                    for (int j=0; j<r; ++j) {
                        // fft_data[r*t[i]+j] += std::conj(in[i]*u[r*i+j]);
                        fft_data[n*j+t[i]] += std::conj(in[i]*u[r*i+j]);
                    }
                }

                // Do the FFTs
                timer::tic();
                fftw_execute(fp); // normalize?
                timer::toc("Call FFTW");

                for (int i=0; i<n; ++i) {
                    out[i]=0;
                    for (int j=0; j<r; ++j) {
                        // out[i] += v[r*i+j]*std::conj(fft_data[r*i+j]);
                        out[i] += v[r*i+j]*std::conj(fft_data[n*j+i]);
                    }
                }
            }

        protected:
            const double* omega; // FFT samples
            int* t;              // Closest equispaced FFT samples to omega
            double* er;          // Perturbation error
            complex* u;          // Low rank approximation
            double* v;           // Low rank approximation
            double* temp;        // Temporary storage
            complex* fft_data;   // Data for FFTW
    };

    // template<int N>
    // class plan2 : public plan_base<N>
    // {
    //     public:
    //         plan2();
    //         virtual ~plan2();
    // };

    // template<>
    // class plan2<1> : public plan_base<1>
    // {
    //     public:
    //         plan2();
    //         virtual ~plan2();
    // };

    // template<int N>
    // class plan3 : public plan_base<N>
    // {
    //     public:
    //         plan3();
    //         virtual ~plan3();
    // };

    // template<>
    // class plan3<1> : public plan_base<1>
    // {
    //     public:
    //         plan3();
    //         virtual ~plan3();
    // };

    typedef plan_base* plan;

    // Methods for planning
    template<int N>
    void destroy_plan(plan<N> p)
    {
        if (p) delete p;
    }

    template<int N>
    void execute(const plan<N> p)
    {
        p->execute();
    }

    namespace details
    {
        /** Evaluate Chebyshev polynomials of degree 0,...,p-1 at x.
         *  (row-major order) */
        void cheb_vals(int n, int p, const double* x, double* vals)
        {
            for (int i=0; i<n; ++i) {
                vals[p*i]=1;
                vals[p*i+1]=x[i];
            }
            for (int i=0; i<n; ++i) {
                for (int j=1; j<p-1; ++j) {
                    vals[p*i+(j+1)] = 2.*x[i]*vals[p*i+j] - vals[p*i+(j-1)];
                }
            }
        }

        /** The bivarate Chebyshev coefficients for the function
         *  f(x,y) = exp(-i*x.*y) on the domain [-gam, gam]x[0,2*pi], as given
         *  by Lemma A.2 of Townsend's DPhil thesis. (column-major order) */
        void bessel_coeffs(int r, double gam, complex* cfs)
        {
            double arg = -0.5*gam*M_PI;
            for (int p=0; p<r; ++p) {
                for (int q=p%2; q<r; q+=2) {
                    cfs[r*q+p] = 4.*std::pow(1i,q)*gsl_sf_bessel_Jn((p+q)*0.5,arg)*gsl_sf_bessel_Jn((q-p)*0.5,arg);
                }
            }
            for (int p=0; p<r; ++p) cfs[p]*=0.5;
            for (int q=0; q<r; ++q) cfs[r*q]*=0.5;
        }
    }
}

#endif


#ifndef DERIVATIVES_H_5CHQ89V7
#define DERIVATIVES_H_5CHQ89V7

#include <gsl/gsl>
#include <tuple>
#include <type_traits>

namespace sens_loc::math {

/// Calculate the first derivate with the central differential quotient.
/// \tparam Real precision of the calculation
/// \param y__1 \f$y_{i-1}\f$
/// \param y_1 \f$y_{i+1}\f$
/// \param dx \f$2. * dx\f$
/// \returns first derivative at this point of order \f$\mathcal{O}(dx^2)\f$
template <typename Real>
inline Real first_derivative_central(Real y__1, Real y_1, Real dx) noexcept {
    static_assert(std::is_floating_point_v<Real>);

    Expects(dx > Real(0.));
    // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
    return (y_1 - y__1) / (Real(2.) * dx);
}

/// Calculate the second derivate with the central differential quotient.
/// \tparam Real precision of the calculation
/// \param y__1 \f$y_{i-1}\f$
/// \param y_0 \f$y_{i}\f$
/// \param y_1 \f$y_{i+1}\f$
/// \param dx \f$dx\f$
/// \returns second derivative at this point of order \f$\mathcal{O}(dx^2)\f$
template <typename Real>
inline Real
second_derivative_central(Real y__1, Real y_0, Real y_1, Real dx) noexcept {
    static_assert(std::is_floating_point_v<Real>);

    Expects(dx > Real(0.));
    // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
    return (y_1 + y__1 - Real(2.) * y_0) / (dx * dx);
}

/// Calculate the derivatives for a surface patch.
///
/// Index convention:
/// \f$d\_\_1 == d_{-1}\f$
/// \f$d\_\_0 == d_{0}\f$
/// \f$d\_1   == d_{1}\f$
///
/// Angle convention:
/// \f$\varphi\f$ -> u direction
/// \f$\theta\f$ -> v direction
///
/// \tparam Real precision of the calculation
/// \param d__1__1,d__1__0,d__1_1 neighbours "above" central pixel
/// \param d__0__1,d__0__0,d__0_1 same row as the central pixel
/// \param d_1__1,d_1__0,d_1_1 row "after" the central pixel
/// \param d_phi angle between rays in x direction \f$(u - 1, u + 1)\f$
/// \param d_theta angle between rays in y direction \f$(v - 1, v + 1)\f$
/// \param d_phi_theta angle between rays in diagonal direction
/// \f$(u - 1, v + 1)\f$
/// \returns partial derivatives \f$(f_u, f_v, f_uu, f_vv, f_uv)\f$
/// \pre the depth values shuold be positive, as they encode depth values
/// \pre \p / d_phi, \p d_theta, \p d_phi_theta are all positive angles
// clang-format off
template <typename Real = float>
inline std::tuple<Real, Real, Real, Real, Real>
derivatives(Real d__1__1, Real d__1__0, Real d__1_1,
            Real d__0__1, Real d__0__0, Real d__0_1,
            Real d_1__1, Real d_1__0, Real d_1_1,
            Real d_phi, Real d_theta, Real d_phi_theta) noexcept {
    static_assert(std::is_floating_point_v<Real>);

    Expects(d_phi > 0.);
    Expects(d_theta > 0.);
    Expects(d_phi_theta > 0.);

    (void)d__1__1;
    (void)d__1_1;
    (void)d_1__1;
    (void)d_1_1;

    // clang-format on
    const Real f_u = math::first_derivative_central(d__0__1, d__0_1, d_phi);
    const Real f_v = math::first_derivative_central(d__1__0, d_1__0, d_theta);
    const Real f_uu =
        math::second_derivative_central(d__0__1, d__0__0, d__0_1, d_phi);
    const Real f_vv =
        math::second_derivative_central(d__1__0, d__0__0, d_1__0, d_theta);
    const Real f_uv =
        math::second_derivative_central(d__1__0, d__0__0, d_1__0, d_phi_theta);

    return std::make_tuple(f_u, f_v, f_uu, f_vv, f_uv);
};
// clang-format on
}  // namespace sens_loc::math

#endif /* end of include guard: DERIVATIVES_H_5CHQ89V7 */


/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
**                                                                                **
** This file forms part of the Underworld geophysics modelling application.         **
**                                                                                **
** For full license and copyright information, please refer to the LICENSE.md file  **
** located at the project root, or contact the authors.                             **
**                                                                                **
**~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/
#include <petsc.h>
#include <petscvec.h>
#include <petscmat.h>

#include "common-driver-utils.h"

// Calculate {r} = {F} - [K]{u} - [G]{p}
// Calculate || {r} ||_2
double BSSCR_StokesMomentumResidual( Mat K, Mat G, Vec F, Vec u, Vec p )
{
	Vec                     uStar;
	//      PetscReal       negOne = -1.0;
	PetscReal               f_norm;
	PetscReal               r1_norm;

	VecNorm( F, NORM_2, &f_norm );          // u_norm = || {uStar} ||_2

	VecDuplicate( u, &uStar );
	MatMult( K, u, uStar );                                 // {uStar} = [K]{u}
	MatMultAdd( G, p, uStar, uStar );               // {uStar} = {uStar} + [G]{p}
	VecAYPX( uStar, -1.0, F );                      // {uStar} = {F} - {uStar}
	VecNorm( uStar, NORM_2, &r1_norm );             // r_norm = || {uStar} ||_2
	Stg_VecDestroy(&uStar );
	/*
	printf("%s \n", __func__ );
	printf("\t||f - Ku - Gp||       = %g \n", r1_norm );
	printf("\t||f - Ku - Gp||/||f|| = %g \n", r1_norm/f_norm );
	*/

	//      return ( (double)(r1_norm/f_norm) );
	return ( (double)(r1_norm) );
}

// Calculate {r} = {H} - [G]^T{u} - [C]{p}
// Calculate || {r} ||_2
double BSSCR_StokesContinuityResidual( Mat G, Mat C, Vec H, Vec u, Vec p )
{
	Vec                     pStar;
	//      PetscReal       negOne = -1.0;
	PetscReal               r2_norm;
	PetscReal               u_norm;

	VecNorm( u, NORM_2, &u_norm );          // u_norm = || {uStar} ||_2

	VecDuplicate( H, &pStar );
	MatMultTranspose( G, u, pStar );                // {pStar} = [G]^T{u}
	if( C != PETSC_NULL ) {
		MatMultAdd( C, p, pStar, pStar );	/* {pStar} = {pStar} + [C] {p} */
	}
	VecAYPX( pStar, -1.0, H );                              // {pStar} = {H} - {pStar}

	VecNorm( pStar, NORM_2, &r2_norm );             // norm = || {pStar} ||_2
	Stg_VecDestroy(&pStar );

	/*
	printf("%s \n", __func__ );
	printf("\t||h - Du||       = %g \n", r2_norm );
	printf("\t||h - Du||/||u|| = %g \n", r2_norm/u_norm );
	*/

	//      return ( (double)(r2_norm/u_norm) );
	return ( (double)(r2_norm) );
}


//
// Copyright (c) 2017 Michele Segata <msegata@disi.unitn.it>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
//

#ifndef MATRIX_UTILS_H
#define MATRIX_UTILS_H

#include "Array.hh"
#include <vector>
#ifdef ENABLE_DISCRETIZATION
#include <gsl/gsl_matrix.h>
#endif

/**
 * Copies the values of a matrix over a portion of another
 * @param dst destination matrix
 * @param src source matrix
 * @param row row index of the destination matrix
 * @param col column index of the destination matrix
 */
void set_submatrix(Matrix<double> &dst, const Matrix<double> &src, int row,
                   int col);

/**
 * Copies the values of a vector over a portion of another
 * @param dst destination vector
 * @param src source vector
 * @param pos index of the destination vector
 */
void set_subvector(Vector<double> &dst, const Vector<double> &src, int pos);

/**
 * Generates a diagonal matrix by using a vector as the diagonal
 * @param src vector to use as the diagonal
 * @return the diagonal matrix
 */
Matrix<double> diag(const Vector<double> &src);

/**
 * Returns a diagonal square matrix
 * @param n square matrix size
 * @param v value to put on the diagonal
 * @return the diagonal matrix
 */
Matrix<double> diag(int n, double v = 1);

/**
 * Returns an identity matrix
 * @param n square matrix size
 * @return the identity matrix
 */
Matrix<double> identity(int n);

/**
 * Given a list of comma separated values, returns a vector
 * @param v string of comma separated values
 * @param length number of elements in the string
 * @return the vector including the parsed values
 */
Vector<double> get_vector(const std::string &v, int length);

/**
 * Returns a vector of all zeros
 * @param length length of the vector
 * @return a zero vector of the specified length
 */
Vector<double> zero_vector(int length);

/**
 * Given a string of comma separated values, returns a matrix of the parsed
 * values
 * @param m string of comma separated values. the number of elements must be
 * nrow * ncol
 * @param nrow number of rows
 * @param ncol number of columns
 * @return the matrix of parsed values
 */
Matrix<double> get_matrix(const std::string &m, int nrow, int ncol);

/**
 * Matrix transpose
 * @param m the matrix to be transposed
 * @return the transposed matrix
 */
Matrix<double> transpose(const Matrix<double> &m);

/**
 * Given a matrix of a particular size, changes the size of the matrix
 * without touching the existing values. New matrix values are initialized
 * with zeros
 * @param m matrix to be enlarged in place
 * @param r new number of rows
 * @param c new number of columns
 */
void extend_matrix(Matrix<double> &m, int r, int c);

/**
 * Given a vector of a particular size, changes the size of the vector
 * without touching the existing values. New vector values are initialized
 * with zeros
 * @param v vector to be enlarged in place
 * @param n new size
 */
void extend_vector(Vector<double> &v, int n);

/**
 * Merges two matrices together by appending the rows of the second matrix to
 * the rows of the first
 * @param a first matrix
 * @param b second matrix
 * @return a matrix where a and b are merged by rows
 */
Matrix<double> bind_matrices(const Matrix<double> &a, const Matrix<double> &b);

/**
 * Merges two vectors together
 * @param a first vector
 * @param b second vector
 * @return a vector composed by a and b
 */
Vector<double> bind_vectors(const Vector<double> &a, const Vector<double> &b);

/**
 * Performs the product of two matrices
 * @param a first matrix
 * @param b second matrix
 * @return the matrix product between a and b if the matrices are compatible,
 * an empty matrix otherwise
 */
Matrix<double> multiply(const Matrix<double> &a, const Matrix<double> &b);

/**
 * Performs the product of a matrix and a vector
 * @param a matrix
 * @param b vector
 * @return the product between a and b if the matrices are compatible,
 * an empty matrix otherwise
 */
Matrix<double> multiply(const Matrix<double> &a, const Vector<double> &b);

/**
 * Performs the product of a matrix and a vector
 * @param a matrix
 * @param b vector
 * @return the product between a and b if the matrices are compatible,
 * an empty matrix otherwise
 */
Matrix<double> multiply(const Matrix<double> &a, const double *b);

/**
 * Given a matrix, the function computes the powers of such a matrix and
 * stores it in a vector of matrices, where the index is the exponent. The
 * results is thus a vector v[i] = A^i for i = 0 ... n
 * @param a input matrix
 * @param n maximum exponent
 * @return the vector of matrices A^i
 */
std::vector<Matrix<double> > get_powers(const Matrix<double> &a, int n);

/**
 * Pretty prints a matrix to stdout
 * @param a the matrix to print
 * @param name optional name to print
 */
void pretty_print_matrix(const Matrix<double_t> &a, const std::string &name="");

/**
 * Copies a vector inside another. If the sizes don't match, then the source
 * vector is repeated multiple time in the destination vector. For example,
 * if the source vector is [1, 2, 3] and the destination vector has length 8,
 * then the destination will be filled with [1, 2, 3, 1, 2, 3, 1, 2]
 * @param dst destination vector
 * @param src source vector
 */
void copy_vector(Vector<double>& dst, const Vector<double> &src);

/**
 * Copies a vector inside another. The size of the destination vector must be
 * larger or equal than the one of the source vector
 * @param dst destination vector
 * @param src source vector
 */
void copy_vector(double *dst, const Vector<double> &src);

/**
 * Copies a vector inside another. The size of the destination vector must be
 * larger or equal than the one of the source vector
 * @param dst destination vector
 * @param src source vector
 */
void copy_vector(double *dst, const Matrix<double> &src);

/**
 * Returns a portion of a vector
 * @param src source vector
 * @param from start index
 * @param length elements to copy
 * @return the subvector of elements going from src[from] to src[from+length-1]
 */
Vector<double> subvector(const Vector<double> &src, int from, int length);

#ifdef ENABLE_DISCRETIZATION

/**
 * Converts a gsl matrix into a Matrix<double> object
 * @param m pointer to gsl_matrix
 * @return converted matrix
 */
Matrix<double> from_gsl(const gsl_matrix *m);

/**
 * Converts a Matrix<double> object into a gsl matrix. This function allocates
 * the memory for the gsl matrix and it is a duty of the user to free the
 * memory via the gsl_matrix_free() method
 * @param m matrix object
 * @return converted matrix
 */
gsl_matrix *to_gsl(const Matrix<double> &m);

#endif

#endif //MATRIX_UTILS_H


/***************************************************************************
 *   Copyright (C) 2008 by Mikhail Zaslavskiy   *
 *   mikhail.zaslavskiy@ensmp.fr   *
 *                                                                         *
 *   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.             *
 ***************************************************************************/
#ifndef ALGORITHM_H
#define ALGORITHM_H
#define EPSILON 1e-100
#include "rpc.h"
#include "graph.h"
#include <math.h>
#include "hungarian.h"

#include <gsl/gsl_blas.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_permutation.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_eigen.h>

#include <vector>
#include <iostream>

/**
Class of graph matching results
*/
class match_result
{
public:
	match_result(){gm_P=NULL;gm_P_exact=NULL;salgo="";}
	std::vector<double> vd_trace;
	int inum_iteration;
	double dres;
	gsl_matrix* gm_P;
	gsl_matrix* gm_P_exact;
	double dtime;
	double dfvalue;
	double dfvalue_exact;
	std::string salgo;
	~match_result(){};

};
/**
Parent class for all graph matching algorithms

	@author Mikhail Zaslavskiy <mikhail.zaslavskiy@ensmp.fr>
*/
class algorithm : public rpc
{
public:
     algorithm(std::string );
     algorithm();
     match_result gmatch(graph& g, graph& h,gsl_matrix* gm_P_i=NULL, gsl_matrix* gm_ldh=NULL,double dalpha_ldh=-1);//common stuff,
     virtual match_result match(graph& g, graph& h, gsl_matrix* gm_P_i=NULL, gsl_matrix* gm_ldh=NULL,double dalpha_ldh=-1)=0;//particular method implementation
     double graph_dist(graph &g,graph &h,gsl_matrix* gm_P,char cscore_matrix);
     double graph_dist(graph &g, graph &h,char cscore_matrix);
      ~algorithm();

     const gsl_matrix* get_ldhmatrix(){return gm_ldh;};
     void  set_ldhmatrix(const gsl_matrix* _gm_A);

protected:
    gsl_matrix *gm_ldh;
    double dalpha_ldh;
    void update_C_hungarian(gsl_matrix* gm_C,int isign=1, bool bback=false);
    double f_qcv(gsl_matrix *gm_Ag_d,gsl_matrix *gm_Ah_d,gsl_matrix* gm_P,gsl_matrix * gm_temp,bool bqcv=false);
    char cdesc_matrix,cscore_matrix;
    parameter pdebug,pdebug_f;
    bool bverbose;
    std::string sverbfile;
    std::ofstream fverbose;
    long long N;
    double df_norm;
    bool bnosymm;

};

#endif


/* min/brent.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.
 */

/* brent.c -- brent minimum finding algorithm */

#include <config.h>

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

#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_min.h>

#include "min.h"

typedef struct
  {
    double d, e, v, w;
    double f_v, f_w;
  }
brent_state_t;

static int brent_init (void *vstate, gsl_function * f, double x_minimum, double f_minimum, double x_lower, double f_lower, double x_upper, double f_upper);
static int brent_iterate (void *vstate, gsl_function * f, double *x_minimum, double * f_minimum, double * x_lower, double * f_lower, double * x_upper, double * f_upper);

static int
brent_init (void *vstate, gsl_function * f, double x_minimum, double f_minimum, double x_lower, double f_lower, double x_upper, double f_upper)
{
  brent_state_t *state = (brent_state_t *) vstate;

  const double golden = 0.3819660;      /* golden = (3 - sqrt(5))/2 */

  double v = x_lower + golden * (x_upper - x_lower);
  double w = v;

  double f_vw;

  x_minimum = 0 ;  /* avoid warnings about unused varibles */
  f_minimum = 0 ;
  f_lower = 0 ;
  f_upper = 0 ;

  state->v = v;
  state->w = w;

  state->d = 0;
  state->e = 0;

  SAFE_FUNC_CALL (f, v, &f_vw);

  state->f_v = f_vw;
  state->f_w = f_vw;

  return GSL_SUCCESS;
}

static int
brent_iterate (void *vstate, gsl_function * f, double *x_minimum, double * f_minimum, double * x_lower, double * f_lower, double * x_upper, double * f_upper)
{
  brent_state_t *state = (brent_state_t *) vstate;

  const double x_left = *x_lower;
  const double x_right = *x_upper;

  const double z = *x_minimum;
  double d = state->e;
  double e = state->d;
  double u, f_u;
  const double v = state->v;
  const double w = state->w;
  const double f_v = state->f_v;
  const double f_w = state->f_w;
  const double f_z = *f_minimum;

  const double golden = 0.3819660;      /* golden = (3 - sqrt(5))/2 */

  const double w_lower = (z - x_left);
  const double w_upper = (x_right - z);

  const double tolerance =  GSL_SQRT_DBL_EPSILON * fabs (z);

  double p = 0, q = 0, r = 0;

  const double midpoint = 0.5 * (x_left + x_right);

  if (fabs (e) > tolerance)
    {
      /* fit parabola */

      r = (z - w) * (f_z - f_v);
      q = (z - v) * (f_z - f_w);
      p = (z - v) * q - (z - w) * r;
      q = 2 * (q - r);

      if (q > 0)
        {
          p = -p;
        }
      else
        {
          q = -q;
        }

      r = e;
      e = d;
    }

  if (fabs (p) < fabs (0.5 * q * r) && p < q * w_lower && p < q * w_upper)
    {
      double t2 = 2 * tolerance ;

      d = p / q;
      u = z + d;

      if ((u - x_left) < t2 || (x_right - u) < t2)
        {
          d = (z < midpoint) ? tolerance : -tolerance ;
        }
    }
  else
    {
      e = (z < midpoint) ? x_right - z : -(z - x_left) ;
      d = golden * e;
    }

  if (fabs (d) >= tolerance)
    {
      u = z + d;
    }
  else
    {
      u = z + ((d > 0) ? tolerance : -tolerance) ;
    }

  state->e = e;
  state->d = d;

  SAFE_FUNC_CALL(f, u, &f_u);

  if (f_u <= f_z)
    {
      if (u < z)
        {
          *x_upper = z;
          *f_upper = f_z;
        }
      else
        {
          *x_lower = z;
          *f_lower = f_z;
        }

      state->v = w;
      state->f_v = f_w;
      state->w = z;
      state->f_w = f_z;
      *x_minimum = u;
      *f_minimum = f_u;
      return GSL_SUCCESS;
    }
  else
    {
      if (u < z)
        {
          *x_lower = u;
          *f_lower = f_u;
          return GSL_SUCCESS;
        }
      else
        {
          *x_upper = u;
          *f_upper = f_u;
          return GSL_SUCCESS;
        }

      if (f_u <= f_w || w == z)
        {
          state->v = w;
          state->f_v = f_w;
          state->w = u;
          state->f_w = f_u;
          return GSL_SUCCESS;
        }
      else if (f_u <= f_v || v == z || v == w)
        {
          state->v = u;
          state->f_v = f_u;
          return GSL_SUCCESS;
        }
    }

  return GSL_FAILURE;
}

static const gsl_min_fminimizer_type brent_type =
{"brent",                       /* name */
 sizeof (brent_state_t),
 &brent_init,
 &brent_iterate};

const gsl_min_fminimizer_type *gsl_min_fminimizer_brent = &brent_type;