#ifndef _Polynomial_h_
#define _Polynomial_h_
/* David Weenink, 19990628 */

#define FITTER_PARAMETER_FREE 0
#define FITTER_PARAMETER_FIXED 1

#ifndef _SimpleVector_h_
	#include "SimpleVector.h"
#endif
#ifndef _Function_h_
	#include "Function.h"
#endif
#ifndef _TableOfReal_h_
	#include "TableOfReal.h"
#endif
#ifndef _Graphics_h_
	#include "Graphics.h"
#endif
#ifndef _Minimizers_h_
	#include "Minimizers.h"
#endif
#ifndef _Spectrum_h_
	#include "Spectrum.h"
#endif
#ifndef _RealTier_h_
	#include "RealTier.h"
#endif
#ifndef _SSCP_h_
	#include "SSCP.h"
#endif

#define Spline_MAXIMUM_DEGREE 20

#define FunctionTerms_members Function_members	\
	long numberOfCoefficients;	\
	double *coefficients;
#define FunctionTerms_methods Function_methods	\
	double (*evaluate) (I, double x);	\
	void (*evaluateTerms) (I, double x, double terms[]); \
	void (*getExtrema) (I, double x1, double x2, double *xmin, double *ymin, \
			double *xmax, double *ymax);	\
	long (*getDegree) (I);
class_create (FunctionTerms, Function)	

int FunctionTerms_init (I, double xmin, double xmax, long numberOfCoefficients);

int FunctionTerms_initFromString (I, double xmin, double xmax, char *s, int allowTrailingZeros);

FunctionTerms FunctionTerms_create (double xmin, double xmax, long numberOfCoefficients);

void FunctionTerms_setDomain (I, double xmin, double xmax);

int FunctionTerms_setCoefficient (I, long index, double value);

double FunctionTerms_evaluate (I, double x);

void FunctionTerms_evaluateTerms (I, double x, double terms[]);

void FunctionTerms_getExtrema (I, double x1, double x2, double *xmin, double *ymin,
	double *xmax, double *ymax);

long FunctionTerms_getDegree (I);

double FunctionTerms_getMinimum (I, double x1, double x2);

double FunctionTerms_getXOfMinimum (I, double x1, double x2);

double FunctionTerms_getMaximum (I, double x1, double x2);

double FunctionTerms_getXOfMaximum (I, double x1, double x2);
/*
	Returns minimum and maximum function values (ymin, ymax) in
	interval [x1, x2] and their x-values (xmin, xmax).
	Precondition: [x1, x2] is a (sub)domain 
		my xmin <= x1 < x2 <= my xmax
*/

void FunctionTerms_draw (I, Graphics g, double xmin, double xmax, double ymin, double ymax, 
	int extrapolate, int garnish);
/*
	Extrapolate only for functions whose domain is extendable and that can be extrapolated.
	Polynomials can be extrapolated.
	LegendreSeries and ChebyshevSeries cannot be extrapolated.
*/
void FunctionTerms_drawBasisFunction (I, Graphics g, long index, double xmin, double xmax,
	double ymin, double ymax, int extrapolate, int garnish);

#define Polynomial_members FunctionTerms_members
#define Polynomial_methods FunctionTerms_methods
class_create (Polynomial, FunctionTerms)

#define Roots_members ComplexVector_members
#define Roots_methods ComplexVector_methods
class_create (Roots, ComplexVector)

Polynomial Polynomial_create (double xmin, double xmax, long degree);

Polynomial Polynomial_createFromString (double xmin, double xmax, char *s);

void Polynomial_scaleCoefficients_monic (Polynomial me);
/* Make coefficent of leading term 1.0 */

Polynomial Polynomial_scaleX (Polynomial me, double xmin, double xmax);
/* x' = (x-location) / scale */

void Polynomial_evaluate_z (Polynomial me, double r, double phi,
	double *re, double *im);
/* Evaluate at complex z = r exp (i phi) */

double Polynomial_getArea (Polynomial me, double xmin, double xmax);

Polynomial Polynomial_getDerivative (Polynomial me);

Polynomial Polynomial_getPrimitive (Polynomial me);

void Polynomial_draw (I, Graphics g, double xmin, double xmax, double ymin, double ymax, int garnish);

double Polynomial_evaluate (I, double x);

void Polynomial_evaluateTerms (I, double x, double terms[]);

Polynomial Polynomials_multiply (Polynomial me, Polynomial thee);

#define LegendreSeries_members FunctionTerms_members
#define LegendreSeries_methods FunctionTerms_methods
class_create (LegendreSeries, FunctionTerms)

LegendreSeries LegendreSeries_create (double xmin, double xmax, long numberOfPolynomials);

LegendreSeries LegendreSeries_createFromString (double xmin, double xmax, char *s);

LegendreSeries LegendreSeries_getDerivative (LegendreSeries me);

Polynomial LegendreSeries_to_Polynomial (LegendreSeries me);

Roots Roots_create (long numberOfRoots);

dcomplex Roots_evaluate_z (Roots me, dcomplex z);

Roots Polynomial_to_Roots (Polynomial me);

long Roots_getNumberOfRoots (Roots me);

void Roots_draw (Roots me, Graphics g, double rmin, double rmax, double imin, double imax, 
	char *symbol, int fontSize, int garnish);
	
dcomplex Roots_getRoot (Roots me, long index);
int Roots_setRoot (Roots me, long index, double re, double im);

Spectrum Roots_to_Spectrum (Roots me, double nyquistFrequency,
	long numberOfFrequencies, double radius);

Polynomial Roots_to_Polynomial (Roots me);

Polynomial TableOfReal_to_Polynomial (I, long degree, long xcol,
	long ycol, long scol);

LegendreSeries TableOfReal_to_LegendreSeries (I, long numberOfPolynomials, 
	long xcol, long ycol, long scol);
	
Spectrum Polynomial_to_Spectrum (Polynomial me, double nyquistFrequency,
	long numberOfFrequencies, double radius);

/*
	A ChebyshevSeries p(x) on a domain [xmin,xmax] is defined as the
	following linear combination of Chebyshev polynomials T[k](x') of
	degree k-1 and domain [-1, 1]:
		p(x) = sum (k=1..numberOfCoefficients, c[k]*T[k](x')) - c[1] / 2, where
		x' = (2 * x - xmin - xmax) / (xmax - xmin)
	This is equivalent to:
		p(x) = c[1] /2 + sum (k=2..numberOfCoefficients, c[k]*T[k](x'))
*/	
#define ChebyshevSeries_members FunctionTerms_members
#define ChebyshevSeries_methods FunctionTerms_methods
class_create (ChebyshevSeries, FunctionTerms)
	
ChebyshevSeries ChebyshevSeries_create (double xmin, double xmax, long numberOfPolynomials);

ChebyshevSeries ChebyshevSeries_createFromString (double xmin, double xmax, char *s);

Polynomial ChebyshevSeries_to_Polynomial (ChebyshevSeries me);

#define Spline_members FunctionTerms_members	\
	long degree, numberOfKnots;	\
	double *knots;
#define Spline_methods FunctionTerms_methods \
	long (*getOrder) (I);
class_create (Spline, FunctionTerms)

int Spline_init (I, double xmin, double xmax, long degree, long numberOfCoefficients, long numberOfKnots);

long Spline_getOrder (I);

void Spline_drawKnots (I, Graphics g, double xmin, double xmax, double ymin, double ymax, int garnish);

Spline Spline_scaleX (I, double xmin, double xmax);
/* scale domain and knots to new domain */

#define MSpline_members Spline_members
#define MSpline_methods Spline_methods
class_create (MSpline, Spline)

MSpline MSpline_create (double xmin, double xmax, long degree, long numberOfInteriorKnots);

MSpline MSpline_createFromStrings (double xmin, double xmax, long degree, char *coef, char *interiorKnots);

#define ISpline_members Spline_members
#define ISpline_methods Spline_methods
class_create (ISpline, Spline)

ISpline ISpline_create (double xmin, double xmax, long degree, long numberOfInteriorKnots);
ISpline ISpline_createFromStrings (double xmin, double xmax, long degree, char *coef, char *interiorKnots);

/****************** fit **********************************************/

int FunctionTerms_and_RealTier_fit (I, thou, int *freezeCoefficients, double tol, int ic, Covariance *c);

Polynomial RealTier_to_Polynomial (I, long degree, double tol, int ic, Covariance *cvm);

LegendreSeries RealTier_to_LegendreSeries (I, long degree, double tol, int ic, Covariance *cvm);

ChebyshevSeries RealTier_to_ChebyshevSeries (I, long degree, double tol, int ic, Covariance *cvm);

#endif /* _Polynomial_h_ */
