#if !defined(PS_MINIMIZE_H)
#define PS_MINIMIZE_H

/** \file psMinimize.h
 *  \brief minimization operations
 *  \ingroup MathGroup
 */

/** Functions **************************************************************/
/** \addtogroup MathGroup Math Utilities
 *  \{
 */


/** Specification and return structure for a minimization */
typedef struct {
    const int maxIter;			///< Maximum number of iterations
    const float tol;			///< Tolerance to reach
    float value;			///< Value after minimization
    int iter;				///< Actual number of iterations performed
    float lastDelta;			///< Last change before quitting
} psMinimization;


psMinimization *psMinimizationAlloc(int maxIter, ///< Maximum number of iterations
				    float tol ///< Tolerance to reach
    );

/** Function format for LM minimization: requires derivatives */
typedef float (*psMinimizeLMFunc)(
    psVector *deriv,			///< Derivatives to return
    const psVector *params,		///< Parameters for which to evaluate
    const psArray *coords		///< Array of vectors of coordinates at which to evaluate
    );

/** Minimize a function using the LM method: requires derivatives.  Returns false if there were errors. */
bool
psMinimizeLM(psMinimization *min,	///< Minimization specification, and output
	     psMatrix *covar,		///< Covariance matrix to return
	     psVector *params,		///< Best guess input parameters, and output
	     const psVector *paramMask,	///< Parameter mask: 1 = fit for parameter, 0 = hold constant.
	     const psArray *coords,	///< Coordinates at which to evaluate function
	     psMinimizeLMFunc func	///< Function to minimize
    );

/** Pre-defined functions for LM minimization */
psMinimizeLMFunc psMinimizeLMChi2Gauss1D;
psMinimizeLMFunc psMinimizeLMChi2Gauss2D;

/** Function format for Powell minimization: does not require derivatives */
typedef float (*psMinimizePowellFunc)(
    const psVector *params,		///< Parameters for which to evaluate
    const psArray *coords		///< Array of vectors of coordinates at which to evaluate
    );

/** Minimize a function using Powell: doesn't require derivatives.  Returns false if there were errors. */
bool
psMinimizePowell(psMinimization *min,	///< Minimization specification, and output
		 psVector *params,	///< Best guess input parameters, and output
		 const psVector *paramMask, ///< Parameter mask: 1 = fit for parameter, 0 = hold constant.
		 const psArray *coords, ///< Coordinates at which to evaluate function
		 psMinimizePowellFunc func ///< Function to minimize
    );

/** Model function format for Powell Chi2 minimization */
typedef psVector* (*psMinimizeChi2PowellFunc)(
    const psVector *params,		///< Parameters for which to evaluate
    const psArray *coords		///< Array of vectors of coordinates at which to evaluate
    );

/** Minimize Chi^2 for a data set given a model: using Powell method.  Returns false if there were errors. */
bool
psMinimizeChi2Powell(psMinimization *min,	///< Minimization specification, and output
		     psVector *params,	///< Best guess input parameters, and output
		     const psVector *paramMask, ///< Parameter mask: 1 = fit for parameter, 0 = hold constant.
		     const psArray *coords, ///< Coordinates at which to evaluate function
		     const psVector *value, ///< Measured values at coordinates
		     const psVector *error, ///< Errors in measured values at coordinates
		     psMinimizePowellChi2Func model ///< Model to fit
    );

/************************************************************************************************************/

/** Derive a polynomial fit by chi^2 minimisation --- can be done analytically */
psPolynomial1D *
psVectorFitPolynomial1D(psPolynomial1D *myPoly, ///< Polynomial to fit
			const psVector *x, ///< Ordinates (or NULL to just use the indices)
			const psVector *y, ///< Coordinates
			const psVector *yErr) ///< Errors in coordinates, or NULL
;

psSpline1D *
psVectorFitSpline1D(psSpline1D *mySpline, ///< Spline to fit
		    const psVector *x,	///< Ordinates (or NULL to just use the indices)
		    const psVector *y,	///< Coordinates
		    const psVector *yErr ///< Errors in coordinates, or NULL
    );

/* \} */ // End of MathGroup Functions

#endif
