/*
 * This file is part of the GIRAFFE Pipeline
 * Copyright (C) 2002-2019 European Southern Observatory
 *
 * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifndef GIMATH_LM_H
#define GIMATH_LM_H

#include <cxtypes.h>

#include <cpl_matrix.h>


#ifdef __cplusplus
extern "C" {
#endif


#define LMRQ_DCHISQ  0.0001
#define LMRQ_ITERMAX 30
#define LMRQ_TESTMAX 7
#define LMRQ_NPARAMS 3

#define DW_DEGREE 3
#define DW_LOG001 2.302585093  /* -log(0.1) */

/* optical model inputs indices */
#define LMI_WLEN  0
#define LMI_XFIB  1
#define LMI_YFIB  2

/* optical model parameters indices */
#define LMP_NX    0
#define LMP_NY    0
#define LMP_PXSIZ 1
#define LMP_FCOLL 2
#define LMP_CFACT 3
#define LMP_THETA 4
#define LMP_ORDER 5
#define LMP_SPACE 6
#define LMP_SOFFX 7
#define LMP_SOFFY 8
#define LMP_SPHI  9

/* line model parameters indices */
#define LMP_AMPL  0
#define LMP_CENT  1
#define LMP_BACK  2
#define LMP_WID1  3
#define LMP_WID2  4

/* locy model parameters indices */
#define LMP_TX  0
#define LMP_TY  1
#define LMP_CX  2
#define LMP_KY  3
#define LMP_TT  4

/* locy model inputs indices */
#define LMI_XCCD  0
#define LMI_NX    1
#define LMI_STRX  2
#define LMI_NCOF  3

/**
 * @typedef fitted_func
 * @brief
 *   Function type definition for mrqnlfit()
 */

typedef void (*fitted_func)(double[], double[], double[], double *, double[], int);

/**
 * @typedef lmrq_params
 * @brief Type definition to handle non linear fit parameters
 */

struct lmrq_params {
    cxint     imax;  /**< max number of iterations        */
    cxint     tmax;  /**< max number of chisq test        */
    cxdouble  dchsq; /**< delta chi square convergence    */
};

typedef struct lmrq_params lmrq_params;

/**
 *  @brief enum definition to handle models number
 */

enum _lmrq_model_id_ {
    LMRQ_GAUSSUM,    /**< summ of gaussian */
    LMRQ_XOPTMOD,    /**< 1st optical model along dispersion direction */
    LMRQ_XOPTMODGS,  /**< 2nd optical model along dispersion direction */
    LMRQ_XOPTMOD2,   /**< 3rd optical model along dispersion direction */
    LMRQ_PSFCOS,     /**< PSF profile using cosinus */
    LMRQ_PSFEXP,     /**< PSF profile using exponential */
    LMRQ_YOPTMOD,    /**< 1st optical model along spectra direction */
    LMRQ_YOPTMOD2,   /**< 2nd optical model along spectra direction */
    LMRQ_LOCYWARP,   /**< model of localization deformation */
    LMRQ_PSFEXP2,    /**< another PSF profile using exponential */
    LMRQ_TEST,       /**< test */
    LMRQ_UNDEFINED   /**< Undefined */
};

typedef enum _lmrq_model_id_ lmrq_model_id;

/**
 *  @brief enum definition to handle model types
 */

enum lmrq_model_type {
    LINE_MODEL, /**< line profile model */
    XOPT_MODEL, /**< optical model along X direction */
    YOPT_MODEL, /**< optical model along Y direction */
    LOCY_MODEL  /**< model of localization deformation */
};

typedef enum lmrq_model_type lmrq_model_type;

/**
 *  @brief struct definition to handle model functions
 */

struct lmrq_model {
    lmrq_model_id   id;         /**< model identification               */
    fitted_func     cfunc;      /**< C function name for the model      */
    cxint           nparams;    /**< number of parameters for the model */
    cxint           ninputs;    /**< number of inputs for the model     */
    cxchar          name[256];  /**< name of the model                  */
    lmrq_model_type type;       /**< type of model                      */
};

typedef struct lmrq_model lmrq_model; // aka FittedModel


cxint mrqnlfit(cpl_matrix *, cpl_matrix *, cpl_matrix *, cxint, cpl_matrix *,
               cxdouble[], cxint[], cxint, cpl_matrix *, cxdouble *,
               lmrq_params, fitted_func);

cxint mymrqmin(cpl_matrix *, cpl_matrix *, cpl_matrix *, cxint, cpl_matrix *,
               cxdouble[], cxint[], cxint, cpl_matrix *, cpl_matrix *,
               cxdouble *, fitted_func, cxdouble *);

cxint mymrqcof(cpl_matrix *, cpl_matrix *, cpl_matrix *, cxint,  cpl_matrix *,
               cxdouble[], cxint[], cxint, cpl_matrix *, cpl_matrix *,
               cxdouble *, fitted_func);

cxdouble r_squared(cxdouble, cpl_matrix *, cxint);



void mrqgaussum(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
                cxdouble[], cxint);

void mrqxoptmod(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
                cxdouble[], cxint);

void mrqxoptmod2(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
                 cxdouble[], cxint);

void mrqyoptmod(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
                cxdouble[], cxint);

void mrqyoptmod2(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
                 cxdouble[], cxint);

void mrqpsfcos(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
               cxdouble[], cxint);

void mrqpsfexp(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
               cxdouble[], cxint);

void mrqpsfexp2(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
                cxdouble[], cxint);

void mrqlocywarp(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
                 cxdouble[], cxint);

void mrqxoptmodGS(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
                  cxdouble[], cxint);

void mrqtest(cxdouble[], cxdouble[], cxdouble[], cxdouble *,
             cxdouble[], cxint);

extern lmrq_model lmrq_models[];
extern cxint nr_lmrq_models;


#ifdef __cplusplus
}
#endif

#endif /* GIMATH_LM_H */
