#ifndef amdmsFit_H
#define amdmsFit_H

#include "amdms.h"

#define amdmsMAX_COEFF 32


typedef amdmsCOMPL (*amdmsFIT_FUNC)(void *env, int n, double *x, double *y, double *ye);
typedef double (*amdmsFIT_EVAL)(void *env, double x);
typedef void (*amdmsFIT_LINEAR_BASE)(double x, int np, double *p);
typedef void (*amdmsFIT_NONLINEAR_BASE)(double x, int na, double *a, double *y, double *dyda);
typedef void (*amdmsFIT_NONLINEAR_INIT)(void *henv, int n, double *x, double *y, double *ye);


/******************************************************************************/
/* environment for a simple fit                                               */
/******************************************************************************/
typedef struct {
  amdmsFIT_FUNC  func;              /* do a fit                               */
  amdmsFIT_EVAL  eval;              /* evaluate the fit function              */
  int            nCoefficients;     /* number of coefficients                 */
  int            nDataPoints;       /* number of data points                  */
  double         chi2;              /* chi squared                            */
  double         absDist2;          /* sum of absolute squared distances      */
  double         relDist2;          /* sum of relative squared distances      */
  double         fitLowerLimit;     /* lower limit for fit with given quality */
  double         fitUpperLimit;     /* upper limit for fit with given quality */
  double         a[amdmsMAX_COEFF];  /* vector of all coefficients            */
  double         ae[amdmsMAX_COEFF]; /* vector of all coefficient errors      */
  int            allocated;         /* flag if structure is allocated         */
} amdmsFIT_ENV;
/******************************************************************************/


/**********************************************************************/
/* environment for a linear model fit                                 */
/**********************************************************************/
typedef struct {
  amdmsFIT_ENV           env;       /* environment for a simple fit   */
  amdmsFIT_LINEAR_BASE   base;      /* base function for linear model */
  double               **matU;      /* ???                            */
  double               **matV;      /* ???                            */
  double                *vecW;      /* ???                            */
  double                *vecB;      /* ???                            */
  double               **matCvm;    /* ???                            */
  int                    allocated; /* flag if structure is allocated */
} amdmsFIT_LINEAR_ENV;
/**********************************************************************/


/***************************************************************************/
/* environment for a nonlinear model fit                                   */
/***************************************************************************/
typedef struct {
  amdmsFIT_ENV             env;       /* environment for a simple fit      */
  amdmsFIT_NONLINEAR_INIT  init;      /* initializer function              */
  amdmsFIT_NONLINEAR_BASE  base;      /* base function for nonlinear model */
  int                     *ivecIndxc; /* ???                               */
  int                     *ivecIndxr; /* ???                               */
  int                     *ivecIpiv;  /* ???                               */
  double                  *vecDyda;   /* ???                               */
  double                   ochi2;     /* ???                               */
  double                  *vecAtry;   /* ???                               */
  double                  *vecBeta;   /* ???                               */
  double                  *vecDa;     /* ???                               */
  double                 **matOneda;  /* ???                               */
  int                      mfit;      /* ???                               */
  int                     *ivecIa;    /* ???                               */
  double                   alamda;    /* ???                               */
  double                 **matCvm;    /* ???                               */
  double                 **matAlpha;  /* ???                               */
  int                      allocated; /* flag if structure is allocated    */
} amdmsFIT_NONLINEAR_ENV;
/***************************************************************************/


/*****************************************************************************/
/* environment for a fit process which recognizes outliers and saturation    */
/*****************************************************************************/
typedef struct {
  amdmsFIT_ENV  env;        /* environment for a simple fit                  */
  amdmsFIT_ENV *fit;        /* fit environment of real fit model             */
  double       *valX;       /* vector with x values                          */
  double       *valY;       /* vector with y values                          */
  double       *valYErr;    /* vector with stderr (y) values                 */
  int          *useFlags;   /* flags if values are used                      */
  double       *usedX;      /* used x values                                 */
  double       *usedY;      /* used y values                                 */
  double       *usedYErr;   /* used stderr (y) values                        */
  int           satFlag;    /* flag if saturation should be recognized       */
  double        satWidth;   /* relative width of saturation box              */
  double        satHeight;  /* relative height of saturation box             */
  int           satDP;      /* index of first data point at saturation level */
  int           firstDP;    /* index of first used data point                */
  int           lastDP;     /* index of last used data point                 */
  int           nDelStart;  /* number of possible outliers from beginning    */
  int           nDelMiddle; /* number of possible outliers in the middle     */
  int           nDelEnd;    /* number of possible outliers at end            */
  double        minStep;    /* minimal required enhancement                  */
  double        maxRelDist; /* maximum allowed relative distance             */
  int           allocated;  /* flag if structure is allocated                */
} amdmsFIT_DATA_ENV;
/*****************************************************************************/


/***************************************************************************/
/* environment for a fit process which recognizes outliers of smooth data  */
/***************************************************************************/
typedef struct {
  amdmsFIT_DATA_ENV  env;                /* environment for a data fit     */
  double             slopeLimit;         /* limit for outlier detection    */
  double            *slopes;             /* all slopes                     */
  double            *slopeDifferences;   /* all slope differences          */
  double             avgSlope;           /* average slope                  */
  double             avgSlopeDifference; /* average slope difference       */
  int                allocated;          /* flag if structure is allocated */
} amdmsFIT_SMOOTH_DATA_ENV;
/***************************************************************************/



#ifdef __cplusplus
extern "C" {
#endif

  amdmsCOMPL amdmsCreateFit(amdmsFIT_ENV **env,
			    amdmsFIT_FUNC func,
			    amdmsFIT_EVAL eval,
			    int nCoefficients);
  amdmsCOMPL amdmsDestroyFit(amdmsFIT_ENV **env);
  
  amdmsCOMPL amdmsCreateLinearFit(amdmsFIT_LINEAR_ENV **env,
				  amdmsFIT_LINEAR_BASE base,
				  int nCoefficients);
  amdmsCOMPL amdmsDestroyLinearFit(amdmsFIT_LINEAR_ENV **env);
  
  amdmsCOMPL amdmsCreateNonLinearFit(amdmsFIT_NONLINEAR_ENV **env,
				     amdmsFIT_NONLINEAR_INIT init,
				     amdmsFIT_NONLINEAR_BASE base,
				     int nCoefficients);
  amdmsCOMPL amdmsDestroyNonLinearFit(amdmsFIT_NONLINEAR_ENV **env);
  
  amdmsCOMPL amdmsCreateDataFit(amdmsFIT_DATA_ENV **env,
				amdmsFIT_ENV *fit);
  amdmsCOMPL amdmsDestroyDataFit(amdmsFIT_DATA_ENV **env);
  
  amdmsCOMPL amdmsCreateSmoothDataFit(amdmsFIT_SMOOTH_DATA_ENV **env,
				      amdmsFIT_ENV *fit);
  amdmsCOMPL amdmsDestroySmoothDataFit(amdmsFIT_SMOOTH_DATA_ENV **env);
  
  amdmsCOMPL amdmsCreateStraightLineFit(amdmsFIT_ENV **env);
  amdmsCOMPL amdmsCreateExponentialFit(amdmsFIT_ENV **env);
  amdmsCOMPL amdmsCreateLogarithmicFit(amdmsFIT_ENV **env);
  amdmsCOMPL amdmsCreatePolynomialFit(amdmsFIT_LINEAR_ENV **env,
				      int degree);
  amdmsCOMPL amdmsCreateRayFit(amdmsFIT_LINEAR_ENV **env);
  amdmsCOMPL amdmsCreateNonLinearPixelFit(amdmsFIT_NONLINEAR_ENV **env);
  amdmsCOMPL amdmsCreateGeneralExpFit(amdmsFIT_NONLINEAR_ENV **env);
  amdmsCOMPL amdmsCreateElectronicBiasFit(amdmsFIT_NONLINEAR_ENV **env);
  amdmsCOMPL amdmsCreateConversionFactorFit(amdmsFIT_NONLINEAR_ENV **env);
  
  amdmsCOMPL amdmsDoFit(amdmsFIT_ENV *env,
			int n,
			double *x,
			double *y,
			double *ye);
  
  amdmsCOMPL amdmsCalcFitLimits(amdmsFIT_ENV *env,
				int n,
				double *x,
				double *y,
				double *ye,
				double relLimit);
  
  double amdmsEvalFit(amdmsFIT_ENV *env,
		      double x);
  
  amdmsCOMPL amdmsSmoothDataByFiniteDiff1(double *y,
					  double *z,
					  double lambda,
					  int m);
  amdmsCOMPL amdmsSmoothDataByFiniteDiff1W(double *w,
					   double *y,
					   double *z,
					   double lambda,
					   int m);
  amdmsCOMPL amdmsSmoothDataByFiniteDiff2(double *y,
					  double *z,
					  double lambda,
					  int m);
  amdmsCOMPL amdmsSmoothDataByFiniteDiff2W(double *w,
					   double *y,
					   double *z,
					   double lambda,
					   int m);

#ifdef __cplusplus
}
#endif

#endif /* !amdmsFit_H */
