#ifndef NUMERICS_H
# define NUMERICS_H

#ifdef __cplusplus
#  define EXTERN_C extern "C"
#else
#  define EXTERN_C /**/
#endif


/* Note! For amoeba, if you want a parameter to be fixed, just make sure */
/* it has the same value in all n+1 initial vectors.  That property will be */
/* preserved. */
typedef double (*amoeba_fcn)(const double *p, int n, void *c, int nc);
typedef double (*mp_amoeba_fcn)(const double *p, int n, void *c, int nc, int copy, int no_hist);
typedef double (*projector_fcn)(double *p, int n, void *c, int nc);
double **amoeba(double **p,	/* must have dimensions [n+1][n] */
				int n,		/* number of parameters to vary */
				double ftol,
				amoeba_fcn funk,
				int itmax,
				void *c,
				int nc);
double **amoeba_allfree(double **p,	/* must have dimensions [n+1][n] */
				int n,		/* number of parameters to vary */
				double ftol,
				amoeba_fcn funk,
				int itmax,
				void *c,
				int nc);

double **amoeba_dirset(double **p,	/* must have dimensions [n+1][n] */
				int n,		/* number of parameters to vary */
				double ftol,
				amoeba_fcn funk,
				int itmax,
				void *c,
				int nc);
double **amoeba_dirset_allfree(double **p,	/* must have dimensions [n+1][n] */
				int n,		/* number of parameters to vary */
				double ftol,
				amoeba_fcn funk,
				int itmax,
				void *c,
				int nc);
#ifdef __cplusplus
double **amoeba_x_allfree(double **p,	/* must have dimensions [n+1][n] */
				int n,		/* number of parameters to vary */
				double ftol,
				mp_amoeba_fcn funk,
				int itmax,
				void *c,
				int nc,
				int nprocessors = 1,
				double T = 0,
				float *interest = 0);
#endif

extern int amoeba_anneal_noisy;		/* Set nonzero to get some debugging print statements. */

double *const*amoeba_anneal(double *const*p,
				int n,
				double ftol,
				double (*funk)(const double *p, int n, void *c, int nc),
				int itmax,
				void *c,
				int nc, double T0, double Tfac);

/* Returns 1 if the iteration count (itmax) was reached, 0 if not. */
int amoeba_anneal_allfree(double *const*p,
				int n,
				double ftol,
				double (*funk)(const double *p, int n, void *c, int nc),
				int itmax,
				void *c,
				int nc, double T0, double Tfac);
int amoeba_anneal_allfreex(double *const*p,
				int n,
				double ftol,
				double (*funk)(const double *p, int n, void *c, int nc),
				int itmax,
				void *c,
				int nc, double T0, double Tfac,
				void (*project)(double *p, int n, void *c, int nc)
				);
int amoeba_anneal_allfree2(double *const*p,
				int n, int nvertex,
				double ftol,
				double (*funk)(const double *p, int n, void *c, int nc),
				int itmax,
				void *c,
				int nc, double T0, double Tfac,
				void (*project)(double *p, int n, void *c, int nc)
				);
extern int amoeba_anneal_qq;

/* Picks a point in the center of the n-dimensional polygon
	that amoeba() uses.  Space is allocated by malloc. */
double *amoeba_center(const double *const* p, int n);
/* returns the RMS spread of the polygon along the coordinate axes.
	Space is allocated by malloc. */
double *amoeba_spread(const double *const* p, int n);


typedef double (*root_function)(double x, const void *p);

/* Finds a root over an interval.  If the root_function has the same sign
at both endpoints, it subdivides the region to a depth 'depth', looking
for a change of sign.  If it finds a change of sign, it uses a combination
of a secant method and bisection to guarentee success (except perhaps
at singularities). */
double
root(root_function f, double xl, double xh,
			const void *p, double epsx, double epsf, int depth);
/* Same, but on an open interval.  Operates without evaluating the endpoints. */
double
rootO(root_function f, double xl, double xh,
			const void *p, double epsx, double epsf, int depth);

#ifdef HAS_TEMPLATES
#include <Block.h>

Block<double>
roots(root_function f, double xl, double xf, const void * p,
		double epsx, double epsf, int depth);


struct mmze_out	{
	double x, y;
	mmze_out(double xx, double yy): x(xx), y(yy) {;}
	};

mmze_out
minimize(double (*f)(double x, const void *p), double xl, double xh, const void *p,
			double epsx, double epsf);

#endif


/* Invert a nxn matrix in place.   Numeric recipes with extra
attention to roundoff error. */
int invert(double **m, int n);	/* Really BOOL */



/* Solves a set of linear equations using LAPACK.
Either overdetermined or underdetermined. */
double ** eqn_solve(int m, int n, double **a,
					int nrhs, double **b,
					double *errs);

/* Solves a set of linear equations using LAPACK.
The matrix a is band-diagonal with width bw on either side of the
diagonal (bw==0 corresponds to a diagonal matrix).
Other elements of a are not accessed, and need not exist.
*/
double ** eqn_solve_band(int n, double **a, int bw,
				int nrhs, double **b);

/* Returns eigenvalues, leaves eigenvectors in a. */
/* On return, a*v*a_transpose == the matrix you passed in. */
/* More specifically, input[i][j] = a[i][k]*v[k]*a[j][k]. */
/* That means that the kth eigenvalue of the input is in column k, a[*][k]. */
double * calc_eigenvectors(double **a, int n);

#ifdef __cplusplus
extern "C" {
#endif
typedef double (*f_interpolator_t)(const double *xa, const double *ya, int n, double x, double *pdy);
double ratint(const double *xa, const double *ya, int n, double x, double *pdy);
double polint(const double *xa, const double *ya, int n, double x, double *pdy);
#ifdef __cplusplus
}	/* extern "C" */
#endif
double * monotonic(const double *x, int n, int sign);
double * monotonic_median(const double *x, int n, int sign);

/* The local part of the integral of exp(y), centered on x0 */
EXTERN_C
double expint_local(double xm, double ym,
		double x0, double y0,
		double xp, double yp);

EXTERN_C
double expint_eend(double xm, double ym,
			double x0, double y0,
			double xp, double yp);

struct parab_t	{
	double xctr, yctr, coef;
	double slope, value;
	};

EXTERN_C
struct parab_t expint_parab(double xm, double ym,
				double x0, double y0,
				double xp, double yp);

#endif
