/* $Id: xsh_detmon.c,v 1.30 2012/01/15 14:14:29 amodigli Exp $
 *
 * This file is part of the irplib package
 * Copyright (C) 2002, 2003 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 02111-1307 USA
 */

/*
 * $Author: amodigli $
 * $Date: 2012/01/15 14:14:29 $
 * $Revision: 1.30 $
 * $Name: xsh-2_3_0 $
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <complex.h>

/*---------------------------------------------------------------------------
                                  Includes
 ---------------------------------------------------------------------------*/

#include <math.h>
#include <string.h>
#include <assert.h>
#include <float.h>

#include <cpl.h>

#include "xsh_detmon.h"
#include "xsh_error.h"

#include "xsh_hist.h"
#include "xsh_irplib_utils.h"


/* Computes the square of an euclidean distance bet. 2 points */
#define pdist(x1,y1,x2,y2) (((x1-x2)*(x1-x2))+((y1-y2)*(y1-y2)))

#define cpl_drand() ((double)rand()/(double)RAND_MAX)

/*--------------------------------------------------------------------------*/

/*
 * @defgroup xsh_detmon        Detector monitoring functions
 */

/*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
                                  Defines
 ---------------------------------------------------------------------------*/


#define HIST_FACT 2.354820045

enum pixeltypes
{
    HOT = 0,
    DEAD = 1,
    NOISY = 2
};

enum stackingtypes
{
    MINMAX = 0,
    MEAN = 1,
    MEDIAN = 2,
    KSIGMA = 3
};

enum readouts
{
    HORIZONTAL = 1,
    VERTICAL = 2
};


static struct
{
    /* Inputs */
    const char             *method;
    const char             *pmethod;
    irplib_ronbias_method   method_bitmask;
    int                     prescan_llx;
    int                     prescan_lly;
    int                     prescan_urx;
    int                     prescan_ury;
    int                     overscan_llx;
    int                     overscan_lly;
    int                     overscan_urx;
    int                     overscan_ury;
    int                     preoverscan_degree;
    int                     random_nsamples;
    int                     random_sizex;
    int                     random_sizey;
    int                     criteria;
    int                     ref_llx;
    int                     ref_lly;
    int                     ref_urx;
    int                     ref_ury;
    const char             *stacking_method;
    int                     stacking_ks_low;
    int                     stacking_ks_high;
    int                     stacking_ks_iter;
    int                     master_shift_x;
    int                     master_shift_y;
    int                     ron_llx;
    int                     ron_lly;
    int                     ron_urx;
    int                     ron_ury;
    int                     exts;
    int                     nb_extensions;
} detmon_ronbias_config;

static struct
{
    int         mode;
    cpl_boolean direction;
    double      speed;
    int         llx;
    int         lly;
    int         urx;
    int         ury;
    int         kappa;
    int         exts;
    int         nb_extensions;
} detmon_pernoise_config;

static struct
{
    const char * ron_method;
    const char * dsnu_method;
    int         exts;
    int         nb_extensions;
    cpl_boolean opt_nir;
} detmon_dark_config;

#define NIR TRUE
#define OPT FALSE

/*---------------------------------------------------------------------------
                                  Private function prototypes
 ---------------------------------------------------------------------------*/

static cpl_error_code
xsh_ksigma_clip_double(const double  * pi,
		       cpl_binary * pm,
		       int               llx,
			  int               lly,
			  int               urx,
			  int               ury,
			  int               nx,
			  double            var_sum,
			  int               npixs,
			  double            kappa,
			  int               nclip,
			  double            tolerance,
			  double          * mean,
			  double          * stdev);

static cpl_error_code
xsh_ksigma_clip_float(const float     * pi,
		      cpl_binary * pm,
			 int               llx,
			 int               lly,
			 int               urx,
			 int               ury,
			 int               nx,
			 double            var_sum,
			 int               npixs,
			 double            kappa,
			 int               nclip,
			 double            tolerance,
			 double          * mean,
			 double          * stdev);

static cpl_error_code
xsh_ksigma_clip_int(const int       * pi,
		    cpl_binary * pm,
		       int               llx,
		       int               lly,
		       int               urx,
		       int               ury,
		       int               nx,
		       double            var_sum,
		       int               npixs,
		       double            kappa,
		       int               nclip,
		       double            tolerance,
		       double          * mean,
		       double          * stdev);


/* Functions for RON/Bias recipe, xsh_detmon_ronbias() */

static cpl_error_code
xsh_detmon_ronbias_retrieve_parlist(const char *,
                                       const char *,
                                       const cpl_parameterlist *,
                                       cpl_boolean);

static cpl_error_code
xsh_detmon_ronbias_random(const cpl_imagelist *,
			     const cpl_image *, cpl_propertylist *);

static cpl_error_code
xsh_detmon_ronbias_histo(const cpl_imagelist *,
			    const cpl_image *, cpl_propertylist *);

static cpl_error_code
xsh_detmon_ronbias_preoverscan(const cpl_imagelist *, /*const cpl_image *,*/
                                   cpl_propertylist *, cpl_image **);

static cpl_error_code
xsh_detmon_ronbias_region(const cpl_imagelist *,
			     const cpl_image *, cpl_propertylist *);

static cpl_image *
xsh_detmon_ronbias_master(const cpl_imagelist *,
			     cpl_mask **, cpl_mask **, cpl_mask **,
                             cpl_propertylist *);

static cpl_error_code
xsh_detmon_ronbias_save(const cpl_parameterlist *,
                           cpl_frameset *,
                           const char *,
                           const char *,
                           const char *,
                           const cpl_propertylist *,
                           const cpl_propertylist *,
                           const cpl_propertylist *,
                           const cpl_propertylist *,
                           const cpl_propertylist *,
                           const cpl_propertylist *,
                           const cpl_propertylist *,
                           const char *,
                           const cpl_image *,
                           const cpl_image *,
			   const cpl_mask *,
			   const cpl_mask *,
			   const cpl_mask *,
                           cpl_propertylist *,
                           const int,
                           const int,
                           cpl_frameset *,
                           int);

int
xsh_detmon_ronbias_dfs_set_groups(cpl_frameset *, const char *);


static cpl_error_code
xsh_detmon_ronbias_dutycycl(const cpl_frameset *, cpl_propertylist *);

cpl_error_code
xsh_detmon_rm_bpixs(cpl_image **,
                       const double,
                       int         ,
                       int         );

/* The following 2 functions are duplicated from cpl_det */



static cpl_bivector    *
irplib_bivector_gen_rect_poisson(const int *r,
                                 const int np,
                                 const int homog);

/* End of duplicated code */


cpl_error_code
xsh_detmon_ronbias_check_defaults(const cpl_frameset *, const int whichext);


/* Functions for Periodic Noise Characterisation, xsh_detmon_pernoise() */

int
xsh_detmon_pernoise_dfs_set_groups(cpl_frameset *,
                                      const char *);

static cpl_error_code
xsh_detmon_pernoise_retrieve_parlist(const char *,
                                       const char *,
                                       const cpl_parameterlist *);

static cpl_error_code
xsh_detmon_pernoise_qc(cpl_propertylist *,
                          cpl_table        *,
                          int);

static cpl_error_code
xsh_detmon_pernoise_save(const cpl_parameterlist *,
                            cpl_frameset *,
                            const char *,
                            const char *,
                            const char *,
                            const char *,
                            cpl_table **,
                            cpl_propertylist **,
                            const int,
                            const int,
                            const cpl_frameset *);

cpl_error_code
xsh_detmon_pernoise_rm_bg(cpl_image *,
                             int,
                             int);

int
xsh_detmon_dark_dfs_set_groups(cpl_frameset *,
                                  const char *);

cpl_error_code
xsh_detmon_dark_dsnu(cpl_frameset *,
                        cpl_imagelist *,
                        cpl_table *,
                        cpl_image *,
                        int pos);


static cpl_error_code
xsh_detmon_dark_save(const cpl_parameterlist *,
                        cpl_frameset *,
                        const char *,
                        const char *,
                        const char *,
                        const char *,
                        const char *,
                        const char *,
                        cpl_imagelist **,
                        cpl_table **,
                        cpl_imagelist **,
                        cpl_propertylist **,
                        const int,
                        const int,
                        const cpl_frameset *);



static cpl_error_code
xsh_detmon_retrieve_dark_params(const char *,
                                   const char *,
                                   const cpl_parameterlist *);

cpl_error_code
xsh_detmon_dark_qc(cpl_propertylist *,
                      cpl_image *);



/*         RONBIAS FILLING PARLIST        */



/*---------------------------------------------------------------------------*/

/*
 * @brief  Fill input parameters with default values
 * @param  parlist      parameters list
 * @param  recipe_name  recipe name
 * @param  pipeline_name pipeline name

 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/

cpl_error_code
xsh_detmon_ronbias_fill_parlist_default(cpl_parameterlist * parlist,
				   const char *recipe_name,
				   const char *pipeline_name)
{
    const cpl_error_code error =
	xsh_detmon_ronbias_fill_parlist(parlist, recipe_name, pipeline_name,
					   "ALL", /* --method */
					   "NORM",/* --pmethod */
					   1,     /* --preoverscan_degree */
					   -1,    /* --random_nsamples */
					   -1,    /* --random_sizex */
					   -1,    /* --random_sizey */
					   0,     /* --criteria     */
					   -1,    /* --ref_llx     */
					   -1,    /* --ref_lly */
					   -1,    /* --ref_urx */
					   -1,    /* --ref_ury */
					   "MEAN",/* --stacking_method */
					   3,     /* --stacking_ks_low */
					   3,     /* --stacking_ks_high */
					   5,     /* --stacking_ks_iter */
					   0,     /* --master_shift_x */
					   0,     /* --master_shift_y */
					   -1,    /* --ron_llx */
					   -1,    /* --ron_lly */
					   -1,    /* --ron_urx */
					   -1,    /* --ron_ury */
					   0,    /* --exts */
					   OPT);
    cpl_ensure_code(!error, error);

    return cpl_error_get_code();
}



/*---------------------------------------------------------------------------*/

/*
 * @brief  Fill input parameters with default values
 * @param  parlist      parameters list
 * @param  recipe_name  recipe name
 * @param  pipeline_name pipeline name

 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/

cpl_error_code
xsh_detmon_darkron_fill_parlist_default(cpl_parameterlist * parlist,
					   const char *recipe_name,
					   const char *pipeline_name)
{
    const cpl_error_code error =
	xsh_detmon_ronbias_fill_parlist(parlist, recipe_name, pipeline_name,
					   "ALL", /* --method */
					   "NORM",/* --pmethod */
					   1,     /* --preoverscan_degree */
					   -1,    /* --random_nsamples */
					   -1,    /* --random_sizex */
					   -1,    /* --random_sizey */
					   0,     /* --criteria     */
					   -1,    /* --ref_llx     */
					   -1,    /* --ref_lly */
					   -1,    /* --ref_urx */
					   -1,    /* --ref_ury */
					   "MEAN",/* --stacking_method */
					   3,     /* --stacking_ks_low */
					   3,     /* --stacking_ks_high */
					   5,     /* --stacking_ks_iter */
					   0,     /* --master_shift_x */
					   0,     /* --master_shift_y */
					   -1,    /* --ron_llx */
					   -1,    /* --ron_lly */
					   -1,    /* --ron_urx */
					   -1,    /* --ron_ury */
					   0,    /* --exts */
					   NIR);
    cpl_ensure_code(!error, error);

    return cpl_error_get_code();
}



/*---------------------------------------------------------------------------*/

/*
 * @brief  Fill input parameters with default values
 * @param  parlist      parameters list
 * @param  recipe_name  recipe name
 * @param  pipeline_name pipeline name
 * @param  method adopted method
 * @param  pmethod adopted pre-method
 * @param  preoverscan_degree degree used ti fit pre-overscan regions
 * @param  random_nsamples number of samples used for random computation
 * @param  random_sizex x size of rectangle area for random computation
 * @param  random_sizey x size of rectangle area for random computation
 * @param  criteria
 * @param  ref_llx  reference region lower left x
 * @param  ref_lly  reference region lower left y
 * @param  ref_urx  reference region upper right x
 * @param  ref_ury  reference region upper right y
 * @param  stacking_method  frame stacking method
 * @param  stacking_ks_low  kappa value to kappa sigma low intensity pixels
 * @param  stacking_ks_high  kappa value to kappa sigma high intensity pixels
 * @param  stacking_ks_iter  kappa value to kappa sigma number of iterations
 * @param  master_shift_x  x shift value applied to master
 * @param  master_shift_y  y shift value applied to master
 * @param  ron_llx  reference region lower left x to compute RON
 * @param  ron_lly  reference region lower left y to compute RON
 * @param  ron_urx  reference region upper right x to compute RON
 * @param  ron_ury  reference region upper right y to compute RON
 * @param  exts  image extension to be reduced
 * @param  opt_nir  switch to specify if in input are OPT or NIR data

 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/

cpl_error_code
xsh_detmon_ronbias_fill_parlist(cpl_parameterlist * parlist,
				   const char *recipe_name,
				   const char *pipeline_name,
				   const char * method,
				   const char * pmethod,
				   const int preoverscan_degree,
				   const int random_nsamples,
				   const int random_sizex,
				   const int random_sizey,
				   const int criteria,
				   const int ref_llx,
				   const int ref_lly,
				   const int ref_urx,
				   const int ref_ury,
				   const char * stacking_method,
				   const int stacking_ks_low,
				   const int stacking_ks_high,
				   const int stacking_ks_iter,
				   const int master_shift_x,
				   const int master_shift_y,
				   const int ron_llx,
				   const int ron_lly,
				   const int ron_urx,
				   const int ron_ury,
				   const int exts,
                                   cpl_boolean opt_nir)
{

    const char * meth_desc_opt =
	"Method to be used when computing bias. Methods appliable: "
	"<RANDOM | HISTO | PREOVERSCAN | REGION | ALL>. By default ALL "
	"methods are applied. More than a method can be chosen; in that "
	"case selected methods must be separated by a single space and put "
	"together between inverted commas (ex. --method=\"HISTO REGION\")."
	"\n RANDOM: Bias is computed as the mean value on a given number "
	"(--random.nsamples) of boxes (dimensions --random.sizex and "
	"--random.sizey) randomly taken  accross the detector.\n HISTO: "
	"An histogram of the pixels of the image is built.\n PREOVERSCAN: "
	"Mean, median and RMS values computed and designated areas. \n "
	"REGION: Mean, median and RMS values on reference region.";

    const char * meth_desc_nir =
	"Method to be used when computing bias. Methods appliable: "
	"<RANDOM | HISTO | REGION | ALL>. By default ALL "
	"methods are applied. More than a method can be chosen; in that "
	"case selected methods must be separated by a single space and put "
	"together between inverted commas (ex. --method=\"HISTO REGION\")."
	"\n RANDOM: Bias is computed as the mean value on a given number "
	"(--random.nsamples) of boxes (dimensions --random.sizex and "
	"--random.sizey) randomly taken  accross the detector.\n HISTO: "
	"An histogram of the pixels of the image is built.\n "
	"REGION: Mean, median and RMS values on reference region.";

    const char * method_desc = opt_nir == OPT ? meth_desc_opt : meth_desc_nir;

    const cpl_error_code error =
	xsh_detmon_fill_parlist(parlist, recipe_name, pipeline_name, 22,
				   "method",
				   method_desc,
				   "CPL_TYPE_STRING", method,

				   "pmethod",
				   "Pre-method for RANDOM, HISTO and REGION."
				   "Difference raw frames or not",
				   "CPL_TYPE_STRING", pmethod,

				   "preoverscan.degree",
				   "Degree used for pre-overscan method",
				   "CPL_TYPE_INT", preoverscan_degree,

				   "random.nsamples",
				   "Number of samples",
				   "CPL_TYPE_INT", random_nsamples,

				   "random.sizex",
				   "X size of the boxes",
				   "CPL_TYPE_INT", random_sizex,

				   "random.sizey",
				   "Y size of the boxes",
				   "CPL_TYPE_INT", random_sizey,

				   "criteria",
				   "Criteria",
				   "CPL_TYPE_INT", criteria,

				   "ref.llx",
				   "x coordinate of the lower-left point "
				   "of the reference region of the frame",
				   "CPL_TYPE_INT", ref_llx,

				   "ref.lly",
				   "y coordinate of the lower-left point "
				   "of the reference region of the frame",
				   "CPL_TYPE_INT", ref_lly,

				   "ref.urx",
				   "x coordinate of the upper-right point "
				   "of the reference region of the frame",
				   "CPL_TYPE_INT", ref_urx,

				   "ref.ury",
				   "y coordinate of the upper-right point "
				   "of the reference region of the frame",
				   "CPL_TYPE_INT", ref_ury,

				   "stacking.method",
				   "Method to be used when stacking the master. Posible values < MINMAX | MEAN | MEDIAN | KSIGMA >",
				   "CPL_TYPE_STRING", stacking_method,

				   "stacking.ks.low",
				   "Low threshold for kappa-sigma clipping",
				   "CPL_TYPE_INT", stacking_ks_low,

				   "stacking.ks.high",
				   "High threshold for kappa-sigma clipping",
				   "CPL_TYPE_INT", stacking_ks_high,

				   "stacking.ks.iter",
				   "Nb of iterations for kappa-sigma clipping",
				   "CPL_TYPE_INT", stacking_ks_iter,

				   "master.shift.x",
				   "Master shift X",
				   "CPL_TYPE_INT", master_shift_x,

				   "master.shift.y",
				   "Master shift Y",
				   "CPL_TYPE_INT", master_shift_y,

				   "ron.llx",
				   "x coordinate of the lower-left point "
				   "of the RON frame",
				   "CPL_TYPE_INT", ron_llx,

				   "ron.lly",
				   "y coordinate of the lower-left point "
				   "of the RON frame",
				   "CPL_TYPE_INT", ron_lly,

				   "ron.urx",
				   "x coordinate of the upper-right point "
				   "of the RON frame",
				   "CPL_TYPE_INT", ron_urx,

				   "ron.ury",
				   "y coordinate of the upper-right point "
				   "of the RON frame", "CPL_TYPE_INT", ron_ury,

                                   "exts",
                                   "Activate the multi-exts option",
                                   "CPL_TYPE_INT", exts);


    cpl_ensure_code(!error, error);

    return cpl_error_get_code();
}




/*---------------------------------------------------------------------------*/

/*
 * @brief  Fill input parameters values
 * @param  parlist      parameters list
 * @param  recipe_name  recipe name
 * @param  pipeline_name pipeline name
 * @param  npars  number of parameters

 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_detmon_fill_parlist(cpl_parameterlist * parlist,
                           const char *recipe_name,
                           const char *pipeline_name,
                           int npars, ...)
{

    va_list                 ap;

    char                   *group_name;

    int                     pars_counter = 0;

    group_name = cpl_sprintf("%s.%s", pipeline_name, recipe_name);
    assert(group_name != NULL);

#define insert_par(PARNAME, PARDESC, PARVALUE, PARTYPE)                      \
    do {                                                                     \
    char * par_name = cpl_sprintf("%s.%s", group_name, PARNAME);          \
    cpl_parameter * p;                                                       \
    assert(par_name != NULL);                                                \
    p = cpl_parameter_new_value(par_name, PARTYPE,                           \
                                PARDESC, group_name, PARVALUE);              \
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, PARNAME);             \
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);                        \
    cpl_parameterlist_append(parlist, p);                                    \
    cpl_free(par_name);                                                      \
    } while(0);


    va_start(ap, npars);

    while(pars_counter < npars) {
        char                   *name = va_arg(ap, char *);
        char                   *desc = va_arg(ap, char *);
        char                   *type = va_arg(ap, char *);

        if(!strcmp(type, "CPL_TYPE_INT")) {
            int                     v1 = va_arg(ap, int);

            insert_par(name, desc, v1, CPL_TYPE_INT);
        } else if(!strcmp(type, "CPL_TYPE_BOOL")) {
            char                   *v2 = va_arg(ap, char *);

            if(!strcmp(v2, "CPL_FALSE"))
                insert_par(name, desc, CPL_FALSE, CPL_TYPE_BOOL);
            if(!strcmp(v2, "CPL_TRUE"))
                insert_par(name, desc, CPL_TRUE, CPL_TYPE_BOOL);
        } else if(!strcmp(type, "CPL_TYPE_STRING")) {
            char                   *v2 = va_arg(ap, char *);

            insert_par(name, desc, v2, CPL_TYPE_STRING);
        } else if(!strcmp(type, "CPL_TYPE_DOUBLE")) {
            double v3 = va_arg(ap, double);
            insert_par(name, desc, v3, CPL_TYPE_DOUBLE);
        }

        pars_counter++;
    }

    va_end(ap);

    cpl_free(group_name);

#undef insert_par
    return 0;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
int
xsh_detmon_retrieve_par_int(const char *parn,
                           const char *pipeline_name,
                           const char *recipe_name,
                           const cpl_parameterlist * parlist)
{
    char                   *par_name;
    cpl_parameter          *par;
    int                     value;

    par_name = cpl_sprintf("%s.%s.%s", pipeline_name, recipe_name, parn);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    value = cpl_parameter_get_int(par);
    cpl_free(par_name);

    return value;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
double
xsh_detmon_retrieve_par_double(const char *parn,
                           const char *pipeline_name,
                           const char *recipe_name,
                           const cpl_parameterlist * parlist)
{
    char                   *par_name;
    cpl_parameter          *par;
    double                     value;

    par_name = cpl_sprintf("%s.%s.%s", pipeline_name, recipe_name, parn);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    value = cpl_parameter_get_double(par);
    cpl_free(par_name);

    return value;
}

/*--------------------------------------------------------------------------*/

/*
 * @brief    Comparison function to identify different settings
 * @param    frame1  First frame
 * @param    frame2  Second frame
 * @return   0 if different, 1 if equal, -1 in error case
 */

/*--------------------------------------------------------------------------*/

int
xsh_detmon_compare_dits(const cpl_frame * frame1, const cpl_frame * frame2)
{
    int                     comparison;
    cpl_propertylist       *plist1;
    cpl_propertylist       *plist2;
    double                  dval1, dval2;

    /* Test entries */
    if(frame1 == NULL || frame2 == NULL)
        return -1;

    /* Get property lists */
    if((plist1 = cpl_propertylist_load(cpl_frame_get_filename(frame1),
                                       0)) == NULL) {
        cpl_msg_error(cpl_func, "getting header from reference frame");
        return -1;
    }
    if((plist2 = cpl_propertylist_load(cpl_frame_get_filename(frame2),
                                       0)) == NULL) {
        cpl_msg_error(cpl_func, "getting header from reference frame");
        cpl_propertylist_delete(plist1);
        return -1;
    }

    /* Test status */
    if(cpl_error_get_code()) {
        cpl_propertylist_delete(plist1);
        cpl_propertylist_delete(plist2);
        return -1;
    }

    /* Compare exposure time */
    comparison = 1;
    dval1 = irplib_pfits_get_exptime(plist1);
    dval2 = irplib_pfits_get_exptime(plist2);
    if(cpl_error_get_code()) {
        cpl_msg_error(cpl_func, "cannot get exposure time");
        cpl_propertylist_delete(plist1);
        cpl_propertylist_delete(plist2);
        return -1;
    }
    if(fabs(dval1 - dval2) > 1e-3)
        comparison = 0;

    /* Free and return */
    cpl_propertylist_delete(plist1);
    cpl_propertylist_delete(plist2);
    return comparison;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve exposure time
 * @param  plist      parameter list
 * @return "EXPTIME" keyword value.
 */

/*---------------------------------------------------------------------------*/

double
irplib_pfits_get_exptime(const cpl_propertylist * plist)
{
    double                  exptime;

    exptime = cpl_propertylist_get_double(plist, "EXPTIME");

    return exptime;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @param  opt_nir  switch to specify if in input are OPT or NIR data
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static                  cpl_error_code
xsh_detmon_ronbias_retrieve_parlist(const char *pipeline_name,
                                      const char *recipe_name,
				       const cpl_parameterlist * parlist,
                                       cpl_boolean opt_nir)
{
    char                   *par_name;
    cpl_parameter          *par;

    char                    m1[20] = "";
    char                    m2[20] = "";
    char                    m3[20] = "";

    /* --method */
    par_name = cpl_sprintf("%s.%s.method", pipeline_name, recipe_name);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    detmon_ronbias_config.method = cpl_parameter_get_string(par);
    cpl_free(par_name);

    detmon_ronbias_config.method_bitmask = 0;

    sscanf(detmon_ronbias_config.method, "%s %s %s", m1, m2, m3);

    if(!strcmp(m1, "RANDOM") || !strcmp(m2, "RANDOM")
       || !strcmp(m3, "RANDOM"))
        detmon_ronbias_config.method_bitmask += RANDOM;

    if(!strcmp(m1, "HISTO") || !strcmp(m2, "HISTO") || !strcmp(m3, "HISTO"))
        detmon_ronbias_config.method_bitmask += HISTO;

    if(!strcmp(m1, "PREOVERSCAN") || !strcmp(m2, "PREOVERSCAN")
       || !strcmp(m3, "PREOVERSCAN")) {
	if (opt_nir == NIR) {
	    /* Just in case some advance user reads himself in the code and
	       tries to trick the interface providing an option no contained
	       in the man-page */
	    cpl_msg_warning(cpl_func, "PREOVERSCAN is not appliable for NIR");
	} else {
	    detmon_ronbias_config.method_bitmask += PREOVERSCAN;
	}
    }
    if(!strcmp(m1, "REGION") || !strcmp(m2, "REGION")
       || !strcmp(m3, "REGION"))
        detmon_ronbias_config.method_bitmask += REGION;

    if(!strcmp(m1, "ALL")) {
	if (opt_nir == OPT) {
	    detmon_ronbias_config.method_bitmask =
		RANDOM | HISTO | PREOVERSCAN | REGION;
	} else {
	    detmon_ronbias_config.method_bitmask =
		RANDOM | HISTO | REGION;
	}
    }

    /* --pmethod */
    par_name = cpl_sprintf("%s.%s.pmethod", pipeline_name, recipe_name);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    detmon_ronbias_config.pmethod = cpl_parameter_get_string(par);
    cpl_free(par_name);

    /* --preoverscan.degree */
    detmon_ronbias_config.preoverscan_degree =
        xsh_detmon_retrieve_par_int("preoverscan.degree", pipeline_name,
                                   recipe_name, parlist);

    /* --nsamples */
    detmon_ronbias_config.random_nsamples =
        xsh_detmon_retrieve_par_int("random.nsamples", pipeline_name,
                                   recipe_name, parlist);

    /* --sizex */
    detmon_ronbias_config.random_sizex =
        xsh_detmon_retrieve_par_int("random.sizex", pipeline_name,
                                   recipe_name, parlist);

    /* --sizey */
    detmon_ronbias_config.random_sizey =
        xsh_detmon_retrieve_par_int("random.sizey", pipeline_name,
                                   recipe_name, parlist);

    /* --criteria */
    detmon_ronbias_config.criteria =
        xsh_detmon_retrieve_par_int("criteria", pipeline_name, recipe_name,
                                   parlist);

    /* --ref.llx */
    detmon_ronbias_config.ref_llx =
        xsh_detmon_retrieve_par_int("ref.llx", pipeline_name, recipe_name,
                                   parlist);
    /* --ref.lly */
    detmon_ronbias_config.ref_lly =
        xsh_detmon_retrieve_par_int("ref.lly", pipeline_name, recipe_name,
                                   parlist);
    /* --ref.urx */
    detmon_ronbias_config.ref_urx =
        xsh_detmon_retrieve_par_int("ref.urx", pipeline_name, recipe_name,
                                   parlist);
    /* --ref.ury */
    detmon_ronbias_config.ref_ury =
        xsh_detmon_retrieve_par_int("ref.ury", pipeline_name, recipe_name,
                                   parlist);

    /* --stacking.method */
    par_name =
        cpl_sprintf("%s.%s.stacking.method", pipeline_name, recipe_name);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    detmon_ronbias_config.stacking_method = cpl_parameter_get_string(par);
    cpl_free(par_name);

    /* --stacking.ks.low */
    detmon_ronbias_config.stacking_ks_low =
        xsh_detmon_retrieve_par_int("stacking.ks.low", pipeline_name,
                                   recipe_name, parlist);
    /* --stacking.ks.high */
    detmon_ronbias_config.stacking_ks_high =
        xsh_detmon_retrieve_par_int("stacking.ks.high", pipeline_name,
                                   recipe_name, parlist);
    /* --stacking.ks.iter */
    detmon_ronbias_config.stacking_ks_iter =
        xsh_detmon_retrieve_par_int("stacking.ks.iter", pipeline_name,
                                   recipe_name, parlist);
    /* --master.shift.x */
    detmon_ronbias_config.master_shift_x =
        xsh_detmon_retrieve_par_int("master.shift.x", pipeline_name,
                                   recipe_name, parlist);
    /* --master.shift.y */
    detmon_ronbias_config.master_shift_y =
        xsh_detmon_retrieve_par_int("master.shift.y", pipeline_name,
                                   recipe_name, parlist);
    /* --ron.llx */
    detmon_ronbias_config.ron_llx =
        xsh_detmon_retrieve_par_int("ron.llx", pipeline_name, recipe_name,
                                   parlist);
    /* --ron.lly */
    detmon_ronbias_config.ron_lly =
        xsh_detmon_retrieve_par_int("ron.lly", pipeline_name, recipe_name,
                                   parlist);
    /* --ron.urx */
    detmon_ronbias_config.ron_urx =
        xsh_detmon_retrieve_par_int("ron.urx", pipeline_name, recipe_name,
                                   parlist);
    /* --ron.ury */
    detmon_ronbias_config.ron_ury =
        xsh_detmon_retrieve_par_int("ron.ury", pipeline_name, recipe_name,
                                   parlist);
    /* --exts */
    detmon_ronbias_config.exts =
        xsh_detmon_retrieve_par_int("exts", pipeline_name, recipe_name,
                                   parlist);

    if(cpl_error_get_code()) {
        cpl_msg_error(cpl_func, "Failed to retrieve the input parameters");
        cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
    }


    return CPL_ERROR_NONE;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Check parameter defauls
 * @param  set        Input set of frames
 * @param  whichext   extension to be reduced
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_detmon_ronbias_check_defaults(const cpl_frameset * set,
                                     const int whichext)
{
    const cpl_frame  * fr        = cpl_frameset_get_first_const(set);

    cpl_propertylist * plist     =
	cpl_propertylist_load(cpl_frame_get_filename(fr), whichext);

    const int naxis1 = cpl_propertylist_get_int(plist, "NAXIS1");
    const int naxis2 = cpl_propertylist_get_int(plist, "NAXIS2");

    if(detmon_ronbias_config.method_bitmask & PREOVERSCAN)
    {
		const int nx = cpl_propertylist_get_int(plist, "ESO DET OUT1 NX");
		const int ny = cpl_propertylist_get_int(plist, "ESO DET OUT1 NY");

		int prscsize;
		int ovscsize;

		if (naxis1 != nx)
		{
			prscsize =
			cpl_propertylist_get_int(plist, "ESO DET OUT1 PRSCX");
			ovscsize =
			cpl_propertylist_get_int(plist, "ESO DET OUT1 OVSCX");

			cpl_error_ensure(cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(), goto cleanup,"error");

			detmon_ronbias_config.prescan_llx  = 1;
			detmon_ronbias_config.prescan_lly  = 1;
			detmon_ronbias_config.prescan_urx  = prscsize;
			detmon_ronbias_config.prescan_ury  = naxis2;
			detmon_ronbias_config.overscan_llx = naxis1 - ovscsize;
			detmon_ronbias_config.overscan_lly = 1;
			detmon_ronbias_config.overscan_urx = naxis1;
			detmon_ronbias_config.overscan_ury = naxis2;
		} else if (naxis2 != ny)
		{
			prscsize =
			cpl_propertylist_get_int(plist, "ESO DET OUT1 PRSCY");
			ovscsize =
			cpl_propertylist_get_int(plist, "ESO DET OUT1 OVSCY");
			cpl_error_ensure(cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(), goto cleanup,"error");

			detmon_ronbias_config.prescan_llx  = 1;
			detmon_ronbias_config.prescan_lly  = 1;
			detmon_ronbias_config.prescan_urx  = naxis1;
			detmon_ronbias_config.prescan_ury  = prscsize;
			detmon_ronbias_config.overscan_llx = 1;
			detmon_ronbias_config.overscan_lly = naxis2 - ovscsize;
			detmon_ronbias_config.overscan_urx = naxis1;
			detmon_ronbias_config.overscan_ury = naxis2;
		} else
		{
			cpl_msg_error(cpl_func,
				  "No PREOVERSCAN areas found");
			cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
			goto cleanup;
		}
    }

    if(detmon_ronbias_config.ref_llx == -1)
        detmon_ronbias_config.ref_llx = naxis1 / 8;
    if(detmon_ronbias_config.ref_lly == -1)
        detmon_ronbias_config.ref_lly = naxis2 / 8;
    if(detmon_ronbias_config.ref_urx == -1)
        detmon_ronbias_config.ref_urx = naxis1 * 7 / 8;
    if(detmon_ronbias_config.ref_ury == -1)
        detmon_ronbias_config.ref_ury = naxis2 * 7 / 8;

    if(detmon_ronbias_config.ron_llx == -1)
        detmon_ronbias_config.ron_llx = 1;
    if(detmon_ronbias_config.ron_lly == -1)
        detmon_ronbias_config.ron_lly = 1;
    if(detmon_ronbias_config.ron_urx == -1)
        detmon_ronbias_config.ron_urx = naxis1;
    if(detmon_ronbias_config.ron_ury == -1)
        detmon_ronbias_config.ron_ury = naxis2;

cleanup:
    cpl_propertylist_delete(plist);
	return cpl_error_get_code();
}


/*---------------------------------------------------------------------------*/
/**
  @brief    Apply kappa-sigma clipping on input image
  @param    img      Input image
  @param    llx      Lower left x position (FITS convention)
  @param    lly      Lower left y position (FITS convention)
  @param    urx      Upper right x position (FITS convention)
  @param    ury      Upper right y position (FITS convention)
  @param    kappa    Kappa value for the clipping
  @param    nclip    Number of clipping iterations
  @param    kmean    Mean after clipping (output)
  @param    kstdev   Stdev after clipping (output)
  @return   CPL_ERROR_NONE or the relevant #_cpl_error_code_ on error

  This function applies an iterative kappa-sigma clipping on the image and
  returns mean and stdev after the clipping.

  The function takes as a starting point the "standard" values of mean and
  stdev from cpl_stats.

  On each iteration, the contribution of pixels outside the range
  [mean - kappa * stdev, mean + kappa * stdev] is removed, the values of
  mean and stdev are updated, and so are the limits of the range to be used
  in the next iteration as well.

  The algorithm stops after nclip iterations or when the variation of the
  range between two consecutive iterations is smaller (absolute value) than
  the tolerance.

  The effectiveness of this function resides on the way the update of the
  values of mean and stdev is done.

  The contribution of a single pixel in variance can be removed as follows:

  \sum_{i=1}^{N-1} (x_i - \overline{x}_{n-1})^2 =
  \sum_{i=1}^ N    (x_i - \overline{x}_n    )^2 -
  \frac{N}{N-1} \,( \, \overline{x}_n - x_{n} )^2

  For further details on the mathematical aspects, please refer to DFS05126.

  Possible #_cpl_error_code_ set in this function:
   - CPL_ERROR_NULL_INPUT if img or kmean is NULL
   - CPL_ERROR_ILLEGAL_INPUT if
       a) the window specification is illegal (llx > urx or lly > ury)
       b) the window specification is outside the image
       c) the tolerance is negative
       d) kappa is <= 1.0
       e) nclip is <= 0.

  The values of kmean and kstdev is undefined on error.
*/
/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_ksigma_clip(const cpl_image * img,
		   int               llx,
		   int               lly,
		   int               urx,
		   int               ury,
		   double            kappa,
		   int               nclip,
		   double            tolerance,
		   double          * kmean,
		   double          * kstdev)
{

    int nx, ny;

    double      mean, stdev;
    int         npixs;
    cpl_image* sub_ima=NULL;
    double kappa2=0;
    double thresh=0;
    double thresh_p=0;
    cpl_binary * pm=0;
    const float* pi=0;

    int pix=0;
    int i=0;
    int j=0;
    int k=0;

    cpl_ensure_code(img != NULL, CPL_ERROR_NULL_INPUT);

    nx = cpl_image_get_size_x(img);
    ny = cpl_image_get_size_y(img);


    cpl_ensure_code(llx > 0 && urx > llx && urx <= nx &&
		    lly > 0 && ury > lly && ury <= ny,
		    CPL_ERROR_ILLEGAL_INPUT);

    cpl_ensure_code(tolerance >= 0.0, CPL_ERROR_ILLEGAL_INPUT);
    cpl_ensure_code(kappa     >  1.0, CPL_ERROR_ILLEGAL_INPUT);
    cpl_ensure_code(nclip     >    0, CPL_ERROR_ILLEGAL_INPUT);
 
    sub_ima=cpl_image_extract(img,llx, lly, urx, ury);
   npixs= nx*ny-cpl_image_count_rejected(sub_ima);
   cpl_image_delete(sub_ima);
   mean    = cpl_image_get_mean_window(img,llx, lly, urx, ury);
   stdev    = cpl_image_get_stdev_window(img,llx, lly, urx, ury);
   pi=cpl_image_get_data_float_const(img);
   pm=cpl_mask_get_data(cpl_image_get_bpm((cpl_image*)img));
 

   kappa2=kappa*kappa;
   thresh_p=-1;
   for(k=0;k<nclip;k++) {
     mean    = cpl_image_get_mean_window(img,llx, lly, urx, ury);
     stdev    = cpl_image_get_stdev_window(img,llx, lly, urx, ury);
     thresh=stdev*stdev*kappa2;
     for(j=lly;j<ury;j++) {
       for(i=llx;i<urx;i++) {
	 pix=i+j*nx;
	 if( 
             (pm[pix] != CPL_BINARY_1) && 
	     ((pi[pix]-mean)*(pi[pix]-mean) > thresh ) ) {
	   pm[pix]=CPL_BINARY_1;
	 } /* end check if pix is outlier */
       } /* loop over i */
     } /* loop over j */
     if((fabs(thresh_p-thresh)) < tolerance) {
       break;
     } else {
       thresh_p=thresh;
     }
   } /* loop over niter */

    *kmean = mean;
    if (kstdev != NULL) *kstdev = stdev; /* Optional */


    return cpl_error_get_code();
}


cpl_error_code
xsh_ksigma_clip_old(const cpl_image * img,
		   int               llx,
		   int               lly,
		   int               urx,
		   int               ury,
		   double            kappa,
		   int               nclip,
		   double            tolerance,
		   double          * kmean,
		   double          * kstdev)
{
    cpl_errorstate inistate = cpl_errorstate_get();

    int nx, ny;

    cpl_stats * stats;
    double      mean, stdev, var_sum;
    int         npixs;

    cpl_ensure_code(img != NULL, CPL_ERROR_NULL_INPUT);

    nx = cpl_image_get_size_x(img);
    ny = cpl_image_get_size_y(img);

    cpl_ensure_code(llx > 0 && urx > llx && urx <= nx &&
		    lly > 0 && ury > lly && ury <= ny,
		    CPL_ERROR_ILLEGAL_INPUT);

    cpl_ensure_code(tolerance >= 0.0, CPL_ERROR_ILLEGAL_INPUT);
    cpl_ensure_code(kappa     >  1.0, CPL_ERROR_ILLEGAL_INPUT);
    cpl_ensure_code(nclip     >    0, CPL_ERROR_ILLEGAL_INPUT);


    skip_if(stats = cpl_stats_new_from_image_window(img,
					    CPL_STATS_MEAN | CPL_STATS_STDEV,
						  llx, lly, urx, ury));

    npixs   = cpl_stats_get_npix(stats); /* Non-bad pixels in window */
    mean    = cpl_stats_get_mean(stats);
    skip_if(stdev   = cpl_stats_get_stdev(stats));
    var_sum = stdev * stdev * (npixs - 1);

    cpl_stats_delete(stats);

    /* img, llx etc. may cause errors: Check and propagate */
    cpl_ensure_code(cpl_errorstate_is_equal(inistate), cpl_error_get_code());

    switch (cpl_image_get_type(img)) {
    case CPL_TYPE_DOUBLE:
      xsh_ksigma_clip_double(cpl_image_get_data_double_const(img),
			     cpl_mask_get_data(cpl_image_get_bpm((cpl_image*)img)),
					  llx, lly, urx, ury, nx, var_sum,
					  npixs, kappa, nclip, tolerance,
					   &mean, &stdev);
	break;
    case CPL_TYPE_FLOAT:
      xsh_ksigma_clip_float(cpl_image_get_data_float_const(img),
			    cpl_mask_get_data(cpl_image_get_bpm((cpl_image*)img)),
					 llx, lly, urx, ury, nx, var_sum,
					 npixs, kappa, nclip, tolerance,
					  &mean, &stdev);
	break;
    case CPL_TYPE_INT:
      xsh_ksigma_clip_int(cpl_image_get_data_int_const(img),
			  cpl_mask_get_data(cpl_image_get_bpm((cpl_image*)img)),
				       llx, lly, urx, ury, nx, var_sum,
				       npixs, kappa, nclip, tolerance,
					&mean, &stdev);
	break;
    default:
	/* It is an error in CPL to reach this point */
	assert( 0 );
    }

    *kmean = mean;
    if (kstdev != NULL) *kstdev = stdev; /* Optional */

    end_skip;
 
    return cpl_error_get_code();
}

#define CONCAT(a,b) a ## _ ## b
#define CONCAT2X(a,b) CONCAT(a,b)

#define CPL_TYPE double
#include "xsh_detmon_body.h"
#undef CPL_TYPE

#define CPL_TYPE float
#include "xsh_detmon_body.h"
#undef CPL_TYPE

#define CPL_TYPE int
#include "xsh_detmon_body.h"
#undef CPL_TYPE

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_detmon_ronbias(cpl_frameset * frameset,
                      const cpl_parameterlist * parlist,
                      const char *tag,
                      const char *recipe_name,
                      const char *pipeline_name,
                      const char *pafregexp,
                      const cpl_propertylist * pro_master,
                      const cpl_propertylist * pro_xstr, /* Unsupported*/
                      const cpl_propertylist * pro_ystr, /* Unsupported*/
                      const cpl_propertylist * pro_synth,
                      const cpl_propertylist * pro_bpmhot,
                      const cpl_propertylist * pro_bpmcold,
                      const cpl_propertylist * pro_bpmdev,
                      const char *package,
                      int (*compare) (const cpl_frame *, const cpl_frame *),
                      cpl_boolean opt_nir)
{

    cpl_size                     nsets;
    int                     i;

    cpl_size                    * selection  = NULL;
    cpl_frameset           * cur_fset   = NULL;
    cpl_propertylist       * qclist     = NULL;
    cpl_image              * synthetic  = NULL;
    cpl_image              * masterbias = NULL;
    cpl_imagelist          * rawbiases  = NULL;
    cpl_mask               * bpmhot     = NULL;
    cpl_mask               * bpmcold    = NULL;
    cpl_mask               * bpmdev     = NULL;

    /* Test entries */
    cpl_ensure_code(frameset        != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(parlist         != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(tag             != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(recipe_name     != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(pipeline_name   != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(pro_master      != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(pro_bpmhot      != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(pro_bpmcold     != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(pro_bpmdev      != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(package         != NULL, CPL_ERROR_NULL_INPUT);

    if(xsh_detmon_ronbias_dfs_set_groups(frameset, tag)) {
        cpl_msg_error(cpl_func, "Cannot identify RAW and CALIB frames");
    }

    /*
     * First of all test the entries.
     * See if the selected method(s) is/are appliable.
     * See if necessary parameters for those selected have been provided.
     */

/*    clreturn_if(xsh_detmon_ronbias_test_entries());
*/
    /*
     * This function reads all inputs parameters from parlist
     * and stores them in a global variable detmon_ronbias_config.
     * Similar to xsh_detmon_lg_retrieve_parlist(). See xsh_detmon.c
     */
    xsh_detmon_ronbias_retrieve_parlist(pipeline_name,
					   recipe_name, parlist, opt_nir);

    /* Extra input check for PREOVERSCAN */
    if(detmon_ronbias_config.method_bitmask & PREOVERSCAN)
	cpl_ensure_code(pro_synth       != NULL, CPL_ERROR_NULL_INPUT);


    /* Labelise all input frames */
    if(compare == NULL)
        nsets = 1;
    else {
        cpl_msg_info(cpl_func, "Identify the different settings");
        selection = cpl_frameset_labelise(frameset, compare, &nsets);
        if(selection == NULL)
            cpl_msg_error(cpl_func, "Cannot labelise input frames");
    }

    /* Extract settings and reduce each of them */
    for(i = 0; i < nsets; i++) {
	int  j;
	int first_ext = 0;
	int last_ext  = 1;

       detmon_ronbias_config.nb_extensions = 1;

	/* Reduce data set nb i */
	cpl_msg_info(cpl_func, "Reduce data set nb %d out of %" 
                     CPL_SIZE_FORMAT "",i + 1, nsets);

	cur_fset = nsets == 1 ?
	    cpl_frameset_duplicate(frameset) :
	    cpl_frameset_extract(frameset, selection, i);
	skip_if(cur_fset == NULL);

       if(detmon_ronbias_config.exts > 0) {
	   first_ext = detmon_ronbias_config.exts;
	   last_ext  = first_ext + 1;
       } else if(detmon_ronbias_config.exts < 0) {
          const cpl_frame        *cur_frame =
             cpl_frameset_get_first_const(cur_fset);
          /* Get the nb of extensions */
          detmon_ronbias_config.nb_extensions =
             cpl_frame_get_nextensions(cur_frame);
	  first_ext = 1;
	  last_ext = detmon_ronbias_config.nb_extensions + 1;
       }

       if (last_ext - first_ext > 1) {
	   skip_if(xsh_detmon_ronbias_save(parlist, frameset,
					      recipe_name,
					      pipeline_name, pafregexp,
					      pro_master, pro_xstr,
					      pro_ystr, pro_synth,
					      pro_bpmhot,
					      pro_bpmcold, pro_bpmdev,
					      package, NULL, NULL, NULL,
					      NULL, NULL, NULL,
					      0, 0, cur_fset, 0));
       }

       for(j = first_ext; j < last_ext; j++) {
	   int whichext;

	   qclist = cpl_propertylist_new();

	   rawbiases
	       = cpl_imagelist_load_frameset(cur_fset,
					     CPL_TYPE_FLOAT, 1, j);
	   skip_if(rawbiases == NULL);

	   skip_if(xsh_detmon_ronbias_check_defaults(cur_fset, j));

	   skip_if(xsh_detmon_ronbias_dutycycl(cur_fset, qclist));

	   masterbias = xsh_detmon_ronbias_master(rawbiases,
						     &bpmhot, &bpmcold,
						     &bpmdev, qclist);
	   skip_if(masterbias == NULL);

	   /*
	    * Following, a function corresponding each of the
	    * possible methods is to be found.
	    */

          if(detmon_ronbias_config.method_bitmask & RANDOM) {
	      skip_if(xsh_detmon_ronbias_random(rawbiases, masterbias,
						   qclist));
          }

          if(detmon_ronbias_config.method_bitmask & HISTO) {
	      skip_if(xsh_detmon_ronbias_histo(rawbiases, masterbias,
						  qclist));
          }

          if(detmon_ronbias_config.method_bitmask & PREOVERSCAN) {
	      skip_if(xsh_detmon_ronbias_preoverscan(rawbiases,
							/*masterbias,*/
							qclist, &synthetic));
          }

          if(detmon_ronbias_config.method_bitmask & REGION) {
	      skip_if(xsh_detmon_ronbias_region(rawbiases, masterbias,
						   qclist));
          }

          /*
           * This function takes the QC list where all the results of the
           * methods applied are stored, and compares them.
           * No action defined yet if comparison reveals important differences.
           */
#if 0
	  xsh_detmon_ronbias_check(qclist);
#endif

	  /* Definition of the extension of the output where to save
	     the products. If input are multiextension but only
	     computation on a single extension is required, it is 0 */
	  whichext = first_ext > 1 ? 0 : j;

          skip_if(xsh_detmon_ronbias_save(parlist, frameset,
					     recipe_name,
					     pipeline_name, pafregexp,
					     pro_master, pro_xstr,
					     pro_ystr, pro_synth,
					     pro_bpmhot,
					     pro_bpmcold, pro_bpmdev,
					     package, masterbias, synthetic,
					     bpmhot, bpmcold, bpmdev,
					     qclist, 0, 0, cur_fset,
					     whichext));

          cpl_image_delete(synthetic);
          cpl_image_delete(masterbias);
          cpl_mask_delete(bpmhot);
          cpl_mask_delete(bpmcold);
          cpl_mask_delete(bpmdev);
          cpl_imagelist_delete(rawbiases);
	  cpl_propertylist_delete(qclist);

	  qclist     = NULL;
	  rawbiases  = NULL;
	  masterbias = NULL;
	  bpmhot     = NULL;
	  bpmcold    = NULL;
	  bpmdev     = NULL;
	  synthetic  = NULL;
       } /* for each extension */

       cpl_frameset_delete(cur_fset);
       cur_fset = NULL;

    } /* for each setting */

    end_skip;

    cpl_free(selection);

    cpl_frameset_delete(cur_fset);

    cpl_image_delete(synthetic);
    cpl_image_delete(masterbias);
    cpl_mask_delete(bpmhot);
    cpl_mask_delete(bpmcold);
    cpl_mask_delete(bpmdev);
    cpl_imagelist_delete(rawbiases);
    cpl_propertylist_delete(qclist);

    return cpl_error_get_code();
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static cpl_error_code
xsh_detmon_ronbias_random(const cpl_imagelist * rawbiases,
			     const cpl_image     * masterbias,
                             cpl_propertylist    * qclist)
{
    int                     nraws = cpl_imagelist_get_size(rawbiases);
    int                     i;
    double                  bias = DBL_MAX;  /* Avoid (false) uninit warning */
    double                  bias_error;

    double                  ron_error;
    double                 *ron =
        (double *) cpl_malloc(sizeof(double) * nraws);

    cpl_vector             *v;
    double stdev = 0;
    cpl_error_code error = CPL_ERROR_NONE;

    /* As we are applying to diff frames instead of raw frames,
       there is one less to compute on */
    if(!strcmp(detmon_ronbias_config.pmethod, "DIF"))
    {
		nraws--;
		/* As we are applying to diff frames instead of raw frames,
		   there is one less to compute on */
		for(i = 0; i < nraws; i++)
		{
			const cpl_image        *c1_raw =
			cpl_imagelist_get_const(rawbiases, i);
			const cpl_image        *c2_raw =
			cpl_imagelist_get_const(rawbiases, i + 1);
			/*FIXME: See if const modifier is necessary */
			const cpl_image        *c_raw = cpl_image_subtract_create(c1_raw,
										  c2_raw);
		   error = cpl_flux_get_noise_window(c_raw, NULL,
						  detmon_ronbias_config.random_sizex / 2,
						  detmon_ronbias_config.random_nsamples,
						  ron + i, &ron_error);
		   cpl_image_delete((cpl_image*)c_raw);
		   if (error != CPL_ERROR_NONE)
		   {
			   break;
		   }
		}
    } else
    {
		for(i = 0; i < nraws; i++)
		{
			const cpl_image *c_raw = cpl_imagelist_get_const(rawbiases, i);
			skip_if(cpl_flux_get_noise_window(c_raw, NULL,
						  detmon_ronbias_config.random_sizex / 2,
						  detmon_ronbias_config.random_nsamples,
						  ron + i, &ron_error));
		}
    }

    /*FIXME: Calls to noise_window could be out from if() and
      coded only once */
    if (error == CPL_ERROR_NONE)
    {
		xsh_flux_get_bias_window(masterbias, NULL,
					detmon_ronbias_config.random_sizex / 2,
					detmon_ronbias_config.random_nsamples,
					&bias, &bias_error);

		v = cpl_vector_wrap(nraws, ron);
		stdev = cpl_vector_get_median_const(v);
		cpl_vector_unwrap(v);


		skip_if(cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_RANDOM_VAL, bias));
		skip_if(cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_RANDOM_VAL,
						 DETMON_QC_BIAS_RANDOM_VAL_C));

		skip_if(cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_RANDOM_RON, stdev));
		skip_if(cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_RANDOM_RON,
						 DETMON_QC_BIAS_RANDOM_RON_C));
    }

    xsh_flux_get_bias_window(masterbias, NULL,
				detmon_ronbias_config.random_sizex / 2,
				detmon_ronbias_config.random_nsamples,
				&bias, &bias_error);

    v = cpl_vector_wrap(nraws, ron);
    if (v)
    {
    	stdev = cpl_vector_get_median_const(v);
		cpl_vector_unwrap(v);
    }

    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_RANDOM_VAL, bias);
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_RANDOM_VAL,
					 DETMON_QC_BIAS_RANDOM_VAL_C);


    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_RANDOM_RON, stdev);
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_RANDOM_RON,
					 DETMON_QC_BIAS_RANDOM_RON_C);

    end_skip;
    if (ron)
		cpl_free(ron);
    return cpl_error_get_code();
}

static cpl_error_code
xsh_detmon_ronbias_histo(const cpl_imagelist * rawbiases,
			    const cpl_image * masterbias,
                            cpl_propertylist * qclist)
{
    int                     nraws = cpl_imagelist_get_size(rawbiases);
    int                     i;

    double mbias = DBL_MAX;
    double mfwhm = DBL_MAX;
    double mmax = DBL_MAX;

    cpl_vector * fwhms;
    cpl_vector * maxs;

    double mean_fwhm = DBL_MAX;

    if(!strcmp(detmon_ronbias_config.pmethod, "DIF")) nraws--;

    fwhms = cpl_vector_new(nraws);
    maxs = cpl_vector_new(nraws);

    for(i = 0; i < nraws; i++) {
	/*FIXME: See if it is necessary to have const */
	const cpl_image * c_raw;
	double bias = DBL_MAX;
	double fwhm = DBL_MAX;
	double max = DBL_MAX;

	if(strcmp(detmon_ronbias_config.pmethod, "DIF")) {
           c_raw = cpl_imagelist_get_const(rawbiases, i);
	} else {
	    const cpl_image        *c1_raw = cpl_imagelist_get_const(rawbiases, i);
	    const cpl_image        * c2_raw = cpl_imagelist_get_const(rawbiases, i+1);
	    c_raw = cpl_image_subtract_create(c1_raw, c2_raw);
	}

	skip_if(xsh_detmon_ronbias_histo_reduce(c_raw, &bias, &fwhm, &max));

	skip_if(bias == DBL_MAX || fwhm == DBL_MAX || max == DBL_MAX);

	if(!strcmp(detmon_ronbias_config.pmethod, "DIF"))
	    cpl_image_delete((cpl_image *)c_raw);

        skip_if(cpl_vector_set(maxs, i, max));
        skip_if(cpl_vector_set(fwhms, i, fwhm));

	/* FIXME: Add properly a hist-saving in debug-mode */
    }

    skip_if(cpl_vector_divide_scalar(fwhms, HIST_FACT));

    xsh_detmon_ronbias_histo_reduce(masterbias, &mbias, &mfwhm, &mmax);

    skip_if(mbias == DBL_MAX || mfwhm == DBL_MAX || mmax == DBL_MAX);

    skip_if(cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_HISTO_VAL,
					   mbias));
    skip_if(cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_HISTO_VAL,
					 DETMON_QC_BIAS_HISTO_VAL_C));
    mean_fwhm = cpl_vector_get_mean(fwhms);

    skip_if(mean_fwhm == DBL_MAX);
    skip_if(cpl_error_get_code());

    skip_if(cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_HISTO_RON,
					   mean_fwhm));
    skip_if(cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_HISTO_RON,
					 DETMON_QC_BIAS_HISTO_RON_C));

    end_skip;

    cpl_vector_delete(fwhms);
    cpl_vector_delete(maxs);

    return cpl_error_get_code();
}

cpl_error_code
xsh_detmon_ronbias_histo_reduce(const cpl_image * c_raw,
				   double * bias,
				   double * fwhm,
				   double * max)
{
    unsigned long           uj;
    xsh_hist            *hist;
    unsigned long           maxwhere = 0;
    unsigned long           maxf;

    double mean, stdev;
    cpl_image * dupi;
    unsigned long           x1a = 1;
    unsigned long           x2a = 1;

    double                  x1 = 0;
    double                  x2 = 0;

    double                  maxwhere_interp;
    double                  max_interp;
    double a, b, c;
    cpl_matrix * coeffs =cpl_matrix_new(3, 3);
    cpl_matrix * rhs    =cpl_matrix_new(3, 1);
    int p, q;
    cpl_matrix * result = NULL;
    cpl_error_code error;

    mean = cpl_image_get_mean(c_raw);
    stdev = cpl_image_get_stdev(c_raw);
    dupi = cpl_image_duplicate(c_raw);

    /* FIXME: Still to decide if it is necessary to remove bad pixels
       in advance or not*/

    hist = xsh_hist_new();
    error = xsh_hist_fill(hist, dupi);
    cpl_ensure_code(!error, error);

    cpl_image_delete(dupi);

    maxf = xsh_hist_get_max(hist, &maxwhere);

    for( p = 0; p< 3; p++){
	unsigned long bi = xsh_hist_get_value(hist, maxwhere-1+p);
	cpl_matrix_set(rhs, p, 0, bi);
	for( q= 0; q< 3; q++) {
	    cpl_matrix_set(coeffs, p,q,pow((maxwhere-1+p),q));
	}
    }

    result = cpl_matrix_solve(coeffs, rhs);

    a = cpl_matrix_get(result, 2, 0);
    b = cpl_matrix_get(result, 1, 0);
    c = cpl_matrix_get(result, 0, 0);

    maxwhere_interp = -0.5 * b / (2 * a);
    max_interp = -1 * b * b / (4 * a) + c;

    cpl_matrix_delete(coeffs);
    cpl_matrix_delete(rhs);
    cpl_matrix_delete(result);

    /* Look for the points of half-maximum */
    for(uj = 0; uj < maxwhere; uj++) {
	if(xsh_hist_get_value(hist, uj)     <= max_interp / 2 &&
	   xsh_hist_get_value(hist, uj + 1) >  max_interp / 2) {
	    x1a = uj;
	}
    }
    for(uj = maxwhere; uj < xsh_hist_get_nbins(hist)-1; uj++) {
	if(xsh_hist_get_value(hist, uj)     >= max_interp / 2 &&
	   xsh_hist_get_value(hist, uj + 1) <  max_interp / 2) {
	    x2a = uj;
	}
    }

    x1 = (max_interp / 2 - xsh_hist_get_value(hist, x1a)) /
	(xsh_hist_get_value(hist, x1a + 1) -
	 xsh_hist_get_value(hist, x1a)) + x1a;
    x2 = (max_interp / 2 - xsh_hist_get_value(hist, x2a)) /
	(xsh_hist_get_value(hist, x2a + 1) -
	 xsh_hist_get_value(hist, x2a)) + x2a;

    *fwhm = (x2 - x1) * xsh_hist_get_bin_size(hist);

    *max = max_interp;

    *bias =  maxwhere_interp * xsh_hist_get_bin_size(hist) +
                           xsh_hist_get_start(hist);

    xsh_hist_delete(hist);

    return cpl_error_get_code();
}
/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static cpl_error_code
xsh_detmon_ronbias_preoverscan(const cpl_imagelist * rawbiases,
                                  cpl_propertylist * qclist,
                                  cpl_image ** synthetic)
{
    int                     i;
    int                     nx, ny;
    int                     nraws;

    cpl_vector             *meanspre;
    cpl_vector             *medspre;
    cpl_vector             *rmsspre;
    cpl_vector             *meansover;
    cpl_vector             *medsover;
    cpl_vector             *rmssover;

    cpl_error_code          error;

    nraws = cpl_imagelist_get_size(rawbiases);
    cpl_ensure_code(nraws != -1, CPL_ERROR_ILLEGAL_INPUT);

    meanspre = cpl_vector_new(nraws);
    medspre = cpl_vector_new(nraws);
    rmsspre = cpl_vector_new(nraws);
    meansover = cpl_vector_new(nraws);
    medsover = cpl_vector_new(nraws);
    rmssover = cpl_vector_new(nraws);

    nx = cpl_image_get_size_x(cpl_imagelist_get_const(rawbiases, 0));
    ny = cpl_image_get_size_y(cpl_imagelist_get_const(rawbiases, 0));
    cpl_ensure_code(nx != -1 && ny != -1, CPL_ERROR_ILLEGAL_INPUT);

    if(nx < detmon_ronbias_config.prescan_urx ||
       nx < detmon_ronbias_config.overscan_urx ||
       ny < detmon_ronbias_config.prescan_ury ||
       ny < detmon_ronbias_config.overscan_ury) {
        cpl_msg_warning(cpl_func, "PREOVERSCAN method not applied. Given "
                        "limits of prescan and overscan area "
                        "exceed image size. Please check and rerun.");
        return CPL_ERROR_NONE;
    }

    for(i = 0; i < nraws; i++) {
        double                  mean = 0;
        double                  stdev = 0;

        cpl_image              *prescan = NULL;
        cpl_image              *overscan = NULL;

        const cpl_image        *c_raw = cpl_imagelist_get_const(rawbiases, i);

        cpl_ensure_code(c_raw != NULL, CPL_ERROR_ILLEGAL_INPUT);

        prescan =
            cpl_image_extract(c_raw,
                              detmon_ronbias_config.prescan_llx,
                              detmon_ronbias_config.prescan_lly,
                              detmon_ronbias_config.prescan_urx,
                              detmon_ronbias_config.prescan_ury);
        cpl_ensure_code(prescan != NULL, CPL_ERROR_ILLEGAL_INPUT);
        overscan =
            cpl_image_extract(c_raw,
                              detmon_ronbias_config.overscan_llx,
                              detmon_ronbias_config.overscan_lly,
                              detmon_ronbias_config.overscan_urx,
                              detmon_ronbias_config.overscan_ury);
        cpl_ensure_code(overscan != NULL, CPL_ERROR_ILLEGAL_INPUT);

        if(i == 0) {
            *synthetic = xsh_detmon_build_synthetic(prescan, overscan);
            cpl_msg_info(cpl_func, "Creating SYNTHETIC frame");
            if(*synthetic == NULL) {
                cpl_msg_error(cpl_func, "Error creating SYNTHETIC frame");
                return CPL_ERROR_UNSPECIFIED;
            }
        }

        error = xsh_ksigma_clip(c_raw,
                                             detmon_ronbias_config.
                                             prescan_llx,
                                             detmon_ronbias_config.
                                             prescan_lly,
                                             detmon_ronbias_config.
                                             prescan_urx,
                                             detmon_ronbias_config.
                                             prescan_ury,
                                             (double) detmon_ronbias_config.
                                             stacking_ks_low,
                                             detmon_ronbias_config.
                                             stacking_ks_iter, 1e-5,
					     &mean, &stdev);
        cpl_ensure_code(!error, error);

        cpl_ensure_code(mean != 0 && stdev != 0, CPL_ERROR_UNSPECIFIED);

        error = cpl_vector_set(medspre, i, cpl_image_get_median(prescan));
        cpl_ensure_code(!error, error);

        error = cpl_vector_set(meanspre, i, mean);
        cpl_ensure_code(!error, error);
        error = cpl_vector_set(rmsspre, i, stdev);
        cpl_ensure_code(!error, error);
        error = xsh_ksigma_clip(c_raw,
                                             detmon_ronbias_config.
                                             overscan_llx,
                                             detmon_ronbias_config.
                                             overscan_lly,
                                             detmon_ronbias_config.
                                             overscan_urx,
                                             detmon_ronbias_config.
                                             overscan_ury,
                                             (double) detmon_ronbias_config.
                                             stacking_ks_low,
                                             detmon_ronbias_config.
                                             stacking_ks_iter, 1e-5,
					     &mean, &stdev);
        cpl_ensure_code(!error, error);

        cpl_ensure_code(mean != 0 && stdev != 0, CPL_ERROR_UNSPECIFIED);

        error = cpl_vector_set(medsover, i, cpl_image_get_median(overscan));
        cpl_ensure_code(!error, error);

        error = cpl_vector_set(meansover, i, mean);
        cpl_ensure_code(!error, error);
        error = cpl_vector_set(rmssover, i, stdev);
        cpl_ensure_code(!error, error);

        cpl_image_delete(prescan);
        cpl_image_delete(overscan);
    }

    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_PRESCAN_MEAN,
                                           cpl_vector_get_mean(meanspre));

    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_PRESCAN_MEAN,
					 DETMON_QC_BIAS_PRESCAN_MEAN_C);

    cpl_ensure_code(!error, error);
    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_PRESCAN_MED,
                                           cpl_vector_get_mean(medspre));
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_PRESCAN_MED,
					 DETMON_QC_BIAS_PRESCAN_MED_C);

    cpl_ensure_code(!error, error);
    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_PRESCAN_RON,
                                           cpl_vector_get_mean(rmsspre));

    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_PRESCAN_RON,
					 DETMON_QC_BIAS_PRESCAN_RON_C);
    cpl_ensure_code(!error, error);

    error =
        cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_OVERSCAN_MEAN,
                                       cpl_vector_get_mean(meansover));
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_OVERSCAN_MEAN,
					 DETMON_QC_BIAS_OVERSCAN_MEAN_C);
    cpl_ensure_code(!error, error);
    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_OVERSCAN_MED,
                                           cpl_vector_get_mean(medsover));
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_OVERSCAN_MED,
					 DETMON_QC_BIAS_OVERSCAN_MED_C);
    cpl_ensure_code(!error, error);
    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_OVERSCAN_RON,
                                           cpl_vector_get_mean(rmssover));
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_OVERSCAN_RON,
					 DETMON_QC_BIAS_OVERSCAN_RON_C);
    cpl_ensure_code(!error, error);

    /* The following seems not to be necessary.
       Pending of revision to be removed */
    /*
    error =
        cpl_propertylist_append_double(qclist,
                                       "ESO QC BIAS PRESCAN MEAN STDEV",
                                       cpl_vector_get_stdev(meanspre));
    cpl_ensure_code(!error, error);
    error =
        cpl_propertylist_append_double(qclist,
                                       "ESO QC BIAS PRESCAN MED STDEV",
                                       cpl_vector_get_stdev(medspre));
    cpl_ensure_code(!error, error);
    error =
        cpl_propertylist_append_double(qclist,
                                       "ESO QC BIAS PRESCAN RMS STDEV",
                                       cpl_vector_get_stdev(rmsspre));
    cpl_ensure_code(!error, error);

    error =
        cpl_propertylist_append_double(qclist,
                                       "ESO QC BIAS OVERSCAN MEAN STDEV",
                                       cpl_vector_get_stdev(meansover));
    cpl_ensure_code(!error, error);
    error =
        cpl_propertylist_append_double(qclist,
                                       "ESO QC BIAS OVERSCAN MED STDEV",
                                       cpl_vector_get_stdev(medsover));
    cpl_ensure_code(!error, error);
    error =
        cpl_propertylist_append_double(qclist,
                                       "ESO QC BIAS OVERSCAN RMS STDEV",
                                       cpl_vector_get_stdev(rmssover));
    cpl_ensure_code(!error, error);
    */

    cpl_vector_delete(meanspre);
    cpl_vector_delete(medspre);
    cpl_vector_delete(rmsspre);
    cpl_vector_delete(meansover);
    cpl_vector_delete(medsover);
    cpl_vector_delete(rmssover);

    return CPL_ERROR_NONE;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static cpl_error_code
xsh_detmon_ronbias_region(const cpl_imagelist * rawbiases,
			     const cpl_image * masterbias,
                             cpl_propertylist * qclist)
{

    int                     nraws = cpl_imagelist_get_size(rawbiases);
    int                     i;

    int                     nx =
        cpl_image_get_size_x(cpl_imagelist_get_const(rawbiases, 0));
    int                     ny =
        cpl_image_get_size_y(cpl_imagelist_get_const(rawbiases, 0));

    cpl_vector *rmssreg;
    cpl_error_code          error;

    const cpl_image * c_raw;
    double median, mbias, mstdev;

    if(!strcmp(detmon_ronbias_config.pmethod, "DIF")) nraws--;

    rmssreg = cpl_vector_new(nraws);

    if(nx < detmon_ronbias_config.ref_urx ||
       ny < detmon_ronbias_config.ref_ury) {
        cpl_msg_warning(cpl_func, "REGION method not applied. Given "
                        "limits of prescan and overscan area "
                        "exceed image size. Please check and rerun.");
        return CPL_ERROR_NONE;
    }

    for(i = 0; i < nraws; i++) {
        double                  mean = 0;
        double                  stdev = 0;
	if(strcmp(detmon_ronbias_config.pmethod, "DIF")) {
	    c_raw = cpl_imagelist_get_const(rawbiases, i);
	} else {
	    const cpl_image        *c1_raw = cpl_imagelist_get_const(rawbiases, i);
	    const cpl_image        * c2_raw = cpl_imagelist_get_const(rawbiases, i+1);
	    c_raw = cpl_image_subtract_create(c1_raw, c2_raw);
	}
        error = xsh_ksigma_clip(c_raw,
                                             detmon_ronbias_config.ref_llx,
                                             detmon_ronbias_config.ref_lly,
                                             detmon_ronbias_config.ref_urx,
                                             detmon_ronbias_config.ref_ury,
                                             (double) detmon_ronbias_config.
                                             stacking_ks_low,
                                             detmon_ronbias_config.
                                             stacking_ks_iter, 1e-5,
					     &mean, &stdev);
        cpl_ensure_code(!error, error);
        /*        cpl_vector_set(rmssreg, i, cpl_image_get_stdev_window(c_raw,
           detmon_ronbias_config.ref_llx,
           detmon_ronbias_config.ref_lly,
           detmon_ronbias_config.ref_urx,
           detmon_ronbias_config.ref_ury));
         */
        error = cpl_vector_set(rmssreg, i, stdev);
        cpl_ensure_code(!error, error);
	if(!strcmp(detmon_ronbias_config.pmethod, "DIF")) cpl_image_delete((cpl_image *)c_raw);
    }

    median =  cpl_image_get_median_window(masterbias,
					  detmon_ronbias_config.ref_llx,
					  detmon_ronbias_config.ref_lly,
					  detmon_ronbias_config.ref_urx,
					  detmon_ronbias_config.ref_ury);
    error = xsh_ksigma_clip(masterbias,
					 detmon_ronbias_config.ref_llx,
					 detmon_ronbias_config.ref_lly,
					 detmon_ronbias_config.ref_urx,
					 detmon_ronbias_config.ref_ury,
					 (double) detmon_ronbias_config.
					 stacking_ks_low,
					 detmon_ronbias_config.
					 stacking_ks_iter, 1e-5,
					 &mbias, &mstdev);

    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_REGION_MED,
					   median);
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_REGION_MED,
					 DETMON_QC_BIAS_REGION_MED_C);
    cpl_ensure_code(!error, error);

    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_REGION_VAL,
					   mbias);
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_REGION_VAL,
					 DETMON_QC_BIAS_REGION_VAL_C);
    cpl_ensure_code(!error, error);
    error = cpl_propertylist_append_double(qclist,DETMON_QC_BIAS_REGION_RON,
					   cpl_vector_get_mean(rmssreg));
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_BIAS_REGION_RON,
					 DETMON_QC_BIAS_REGION_RON_C);
    cpl_ensure_code(!error, error);
    /*
    error =
        cpl_propertylist_append_double(qclist, "ESO QC BIAS REGION RMS STDEV",
                                       cpl_vector_get_stdev(rmssreg));
    cpl_ensure_code(!error, error);
    */
    cpl_vector_delete(rmssreg);

    return cpl_error_get_code();
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static cpl_image *
xsh_detmon_ronbias_master(const cpl_imagelist * rawbiases,
			     cpl_mask ** bpmhot, cpl_mask ** bpmcold,
			     cpl_mask ** bpmdev, cpl_propertylist * qclist)
{
    double                  mean = 0;
    double                  stdev = 0;
    cpl_image              *masterbias = NULL;
    double dark_med, stdev_med,lower, upper;
    int hotpix_nb, coldpix_nb, devpix_nb;
    cpl_image * stdev_im = NULL;

    if(!strcmp(detmon_ronbias_config.stacking_method, "MEAN"))
        masterbias = cpl_imagelist_collapse_create(rawbiases);
    if(!strcmp(detmon_ronbias_config.stacking_method, "MINMAX"))
        masterbias =
            cpl_imagelist_collapse_minmax_create(rawbiases, 0, 10000);
    if(!strcmp(detmon_ronbias_config.stacking_method, "KSIGMA"))
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(5, 1, 0)
        masterbias =
	cpl_imagelist_collapse_sigclip_create(rawbiases, 3.0, 3.0, 0.9,
					      CPL_COLLAPSE_MEAN, NULL);
#else
        masterbias =
	cpl_imagelist_collapse_sigclip_create(rawbiases, 3.0, 0.0, 1.0,
					      1, NULL);
#endif
    if(!strcmp(detmon_ronbias_config.stacking_method, "MEDIAN"))
        masterbias = cpl_imagelist_collapse_median_create(rawbiases);

    skip_if(masterbias == NULL);

    skip_if(xsh_ksigma_clip(masterbias, 1, 1,
                                         cpl_image_get_size_x(masterbias),
                                         cpl_image_get_size_y(masterbias),
                                         (double) detmon_ronbias_config.
                                         stacking_ks_low,
                                         detmon_ronbias_config.
                                         stacking_ks_iter, 1e-5,
					 &mean, &stdev));

    if(irplib_isnan(mean))
	cpl_msg_error(cpl_func, "We have an error in mean");
    if(irplib_isnan(stdev))
	cpl_msg_error(cpl_func, "We have an error in stdev");

    skip_if(cpl_propertylist_append_double(qclist,DETMON_QC_MASTER_MEAN,
					   mean));
    skip_if(cpl_propertylist_set_comment(qclist,DETMON_QC_MASTER_MEAN,
					 DETMON_QC_MASTER_MEAN_C));

    skip_if(cpl_propertylist_append_double(qclist,DETMON_QC_MASTER_RMS,
					   stdev));
    skip_if(cpl_propertylist_set_comment(qclist,DETMON_QC_MASTER_RMS,
					 DETMON_QC_MASTER_RMS_C));

    /* Compute median-rms of the central part of the dark  */
    dark_med = cpl_image_get_median(masterbias);

    lower = dark_med - stdev * detmon_ronbias_config.stacking_ks_low;
    upper = dark_med + stdev * detmon_ronbias_config.stacking_ks_high;

    /* Create the hot pixel map */
    cpl_mask_delete(*bpmhot);
    irplib_check(*bpmhot = cpl_mask_threshold_image_create(masterbias,
							   upper, DBL_MAX),
                 "Cannot compute the hot pixel map");
    hotpix_nb = cpl_mask_count(*bpmhot);
    skip_if (0);

    /* Create the cold pixel map */
    cpl_mask_delete(*bpmcold);
    irplib_check(*bpmcold = cpl_mask_threshold_image_create(masterbias,
							    -FLT_MAX, lower),
                 "Cannot compute the cold pixel map");
    coldpix_nb = cpl_mask_count(*bpmcold);
    skip_if (0);

    /* Create the deviant pixel map */
    stdev_im  = irplib_imagelist_collapse_stdev_create(rawbiases);
    stdev_med = cpl_image_get_median(stdev_im);

    skip_if(xsh_ksigma_clip(stdev_im, 1, 1,
                                         cpl_image_get_size_x(stdev_im),
                                         cpl_image_get_size_y(stdev_im),
                                         (double) detmon_ronbias_config.
                                         stacking_ks_low,
                                         detmon_ronbias_config.
                                         stacking_ks_iter, 1e-5,
					 &mean, &stdev));

    lower = stdev_med - stdev * detmon_ronbias_config.stacking_ks_low;
    upper = stdev_med + stdev * detmon_ronbias_config.stacking_ks_high;

    cpl_mask_delete(*bpmdev);
    irplib_check(*bpmdev = cpl_mask_threshold_image_create(stdev_im,
							   lower, upper),
                 "Cannot compute the cold pixel map");
    cpl_mask_not(*bpmdev);
    devpix_nb = cpl_mask_count(*bpmdev);
    skip_if (0);


    skip_if(cpl_propertylist_append_int(qclist,DETMON_QC_NBCOLDPIX,coldpix_nb));
    skip_if(cpl_propertylist_set_comment(qclist,DETMON_QC_NBCOLDPIX,
					 DETMON_QC_NBCOLDPIX_C));

    skip_if(cpl_propertylist_append_int(qclist,DETMON_QC_NBHOTPIX, hotpix_nb));
    skip_if(cpl_propertylist_set_comment(qclist,DETMON_QC_NBHOTPIX,
					 DETMON_QC_NBHOTPIX_C));

    skip_if(cpl_propertylist_append_int(qclist,DETMON_QC_NBDEVPIX, devpix_nb));
    skip_if(cpl_propertylist_set_comment(qclist,DETMON_QC_NBDEVPIX,
					 DETMON_QC_NBDEVPIX_C));

    end_skip;

    cpl_image_delete(stdev_im);

    if (cpl_error_get_code()) {
	cpl_image_delete(masterbias);
	masterbias = NULL;
    }

    return masterbias;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static                  cpl_error_code
xsh_detmon_ronbias_save(const cpl_parameterlist * parlist,
                           cpl_frameset * frameset,
                           const char *recipe_name,
                           const char *pipeline_name,
			   const char *pafregexp,
			   const cpl_propertylist * pro_master,
			   const cpl_propertylist * pro_xstr, /* Unsupported*/
			   const cpl_propertylist * pro_ystr, /* Unsupported*/
			   const cpl_propertylist * pro_synth,
			   const cpl_propertylist * pro_bpmhot,
			   const cpl_propertylist * pro_bpmcold,
			   const cpl_propertylist * pro_bpmdev,
                           const char *package,
                           const cpl_image * masterbias,
                           const cpl_image * synthetic,
                           const cpl_mask * bpmhot,
                           const cpl_mask * bpmcold,
                           const cpl_mask * bpmdev,
                           cpl_propertylist * qclist,
                           const int flag_sets,
                           const int which_set,
                           cpl_frameset * usedframes,
                           int whichext)
{

    cpl_frame              *ref_frame;
    cpl_propertylist       *plist = NULL;
    char                   *name_o = NULL; /* Avoid (false) uninit warning */

    cpl_propertylist       * paflist   = NULL;
    cpl_propertylist       * mainplist = NULL;
    cpl_propertylist       * xplist    = NULL;
    cpl_image              * image     = NULL;

    cpl_propertylist  * mypro_master     =
	cpl_propertylist_duplicate(pro_master);

    cpl_propertylist  * mypro_synth = NULL;
    cpl_propertylist  * mypro_bpmhot    =
	cpl_propertylist_duplicate(pro_bpmhot);
    cpl_propertylist  * mypro_bpmcold =
	cpl_propertylist_duplicate(pro_bpmcold);
    cpl_propertylist  * mypro_bpmdev =
	cpl_propertylist_duplicate(pro_bpmdev);

    cpl_ensure_code(parlist != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(frameset != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(pafregexp != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(package != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(recipe_name != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(pipeline_name != NULL, CPL_ERROR_NULL_INPUT);
    cpl_ensure_code(usedframes != NULL, CPL_ERROR_NULL_INPUT);

    if (pro_synth)
	mypro_synth = cpl_propertylist_duplicate(pro_synth);

    /* Extra check while XSTR and YSTR are not supported */
    cpl_ensure_code(pro_xstr == NULL && pro_ystr == NULL,
		    CPL_ERROR_UNSUPPORTED_MODE);

    /* Extract extension headers if multi-extension */
    if (detmon_ronbias_config.exts < 0) {
        const char * filename =
            cpl_frame_get_filename(cpl_frameset_get_first(frameset));


            xplist = cpl_propertylist_load_regexp(filename, whichext,
                                                     "ESO DET", 0);
	    skip_if(cpl_propertylist_append(xplist, qclist));
    }

    cpl_msg_info(cpl_func,"dealing with extention %d",whichext);

    /* This is only used later for PAF */
    /* Get FITS header from reference file */
    ref_frame = cpl_frameset_get_first(frameset);
    skip_if(ref_frame == NULL);

    skip_if((mainplist =
	cpl_propertylist_load(cpl_frame_get_filename(ref_frame),
			      0)) == NULL);

    /**************************/
    /*  Write the MASTERBIAS  */
    /**************************/

    /* Set the file name for the table */
    if(!flag_sets) {
       name_o = cpl_sprintf("%s_masterbias.fits", recipe_name);
       assert(name_o != NULL);
    } else {
       name_o =
          cpl_sprintf("%s_masterbias_set%02d.fits", recipe_name,
                      which_set);
       assert(name_o != NULL);
    }
    /* Save the MASTERBIAS image */
    if (whichext == 0) {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
	cpl_propertylist_append(mypro_master, qclist);

	skip_if(cpl_dfs_save_image(frameset, NULL, parlist, usedframes, NULL,
	                           masterbias, CPL_BPP_IEEE_FLOAT, recipe_name,
	                           mypro_master, NULL, package, name_o));
#else
	const char * procatg_master =
	    cpl_propertylist_get_string(mypro_master, CPL_DFS_PRO_CATG);

	cpl_propertylist_append(mypro_master, qclist);

	skip_if(cpl_dfs_save_image(frameset, parlist, usedframes, masterbias,
                             CPL_BPP_IEEE_FLOAT, recipe_name, procatg_master,
			      mypro_master, NULL, package, name_o));
#endif
    } else
	skip_if(cpl_image_save(masterbias,
			       name_o, CPL_BPP_IEEE_FLOAT, xplist,
			       CPL_IO_EXTEND));

    /* Free */
    cpl_free(name_o);
    name_o = NULL;

    /*****************************/
    /*  Write the HOT PIXEL MAP  */
    /*****************************/

    /* Set the file name for the table */
    if(!flag_sets) {
       name_o = cpl_sprintf("%s_hotpixmap.fits", recipe_name);
       assert(name_o != NULL);
    } else {
       name_o =
          cpl_sprintf("%s_hotpixmap_set%02d.fits", recipe_name,
                      which_set);
       assert(name_o != NULL);
    }
    /* Save the HOTBPM image */
    skip_if(0);
    image = cpl_image_new_from_mask(bpmhot);
    cpl_error_reset();

    if (whichext == 0) {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
	cpl_propertylist_append(mypro_bpmhot, qclist);

	skip_if(cpl_dfs_save_image(frameset, NULL, parlist, usedframes, NULL,
	                           image, CPL_BPP_IEEE_FLOAT, recipe_name,
			           mypro_bpmhot, NULL, package, name_o));
#else
	const char * procatg_bpmhot =
	    cpl_propertylist_get_string(mypro_bpmhot, CPL_DFS_PRO_CATG);

	cpl_propertylist_append(mypro_bpmhot, qclist);

	skip_if(cpl_dfs_save_image(frameset, parlist, usedframes, image,
                             CPL_BPP_IEEE_FLOAT, recipe_name, procatg_bpmhot,
			      mypro_bpmhot, NULL, package, name_o));
#endif
    } else
	skip_if(cpl_image_save(image,
			       name_o, CPL_BPP_IEEE_FLOAT, xplist,
			       CPL_IO_EXTEND));

    /* Free */
    cpl_free(name_o);
    cpl_image_delete(image);
    image = NULL;
    name_o = NULL;

    /*****************************/
    /*  Write the COLD PIXEL MAP  */
    /*****************************/

    /* Set the file name for the table */
    if(!flag_sets) {
       name_o = cpl_sprintf("%s_coldpixmap.fits", recipe_name);
       assert(name_o != NULL);
    } else {
       name_o =
          cpl_sprintf("%s_coldpixmap_set%02d.fits", recipe_name,
                      which_set);
       assert(name_o != NULL);
    }
    /* Save the COLDBPM image */
    skip_if(0);
    image = cpl_image_new_from_mask(bpmcold);
    cpl_error_reset();

    if (whichext == 0) {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
	cpl_propertylist_append(mypro_bpmcold, qclist);

	skip_if(cpl_dfs_save_image(frameset, NULL, parlist, usedframes, NULL,
	                           image, CPL_BPP_IEEE_FLOAT, recipe_name,
	                           mypro_bpmcold, NULL, package, name_o));
#else
	const char * procatg_bpmcold =
	    cpl_propertylist_get_string(mypro_bpmcold, CPL_DFS_PRO_CATG);

	cpl_propertylist_append(mypro_bpmcold, qclist);

	skip_if(cpl_dfs_save_image(frameset, parlist, usedframes, image,
                             CPL_BPP_IEEE_FLOAT, recipe_name, procatg_bpmcold,
			      mypro_bpmcold, NULL, package, name_o));
#endif
    } else
	skip_if(cpl_image_save(image,
			       name_o, CPL_BPP_IEEE_FLOAT, xplist,
			       CPL_IO_EXTEND));

    /* Free */
    cpl_free(name_o);
    cpl_image_delete(image);
    image = NULL;
    name_o = NULL;

    /*****************************/
    /*  Write the DEV PIXEL MAP  */
    /*****************************/

    /* Set the file name for the table */
    if(!flag_sets) {
       name_o = cpl_sprintf("%s_devpixmap.fits", recipe_name);
       assert(name_o != NULL);
    } else {
       name_o =
          cpl_sprintf("%s_devpixmap_set%02d.fits", recipe_name,
                      which_set);
       assert(name_o != NULL);
    }
    /* Save the DEVBPM image */
    skip_if(0);
    image = cpl_image_new_from_mask(bpmdev);
    cpl_error_reset();

    if (whichext == 0) {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
	cpl_propertylist_append(mypro_bpmdev, qclist);

	skip_if(cpl_dfs_save_image(frameset, NULL,parlist, usedframes, NULL,
	                           image, CPL_BPP_IEEE_FLOAT, recipe_name,
                                   mypro_bpmdev, NULL, package, name_o));
#else
	const char * procatg_bpmdev =
	    cpl_propertylist_get_string(mypro_bpmdev, CPL_DFS_PRO_CATG);

	cpl_propertylist_append(mypro_bpmdev, qclist);

	skip_if(cpl_dfs_save_image(frameset, parlist, usedframes, image,
                             CPL_BPP_IEEE_FLOAT, recipe_name, procatg_bpmdev,
			      mypro_bpmdev, NULL, package, name_o));
#endif
    } else
	skip_if(cpl_image_save(image,
			       name_o, CPL_BPP_IEEE_FLOAT, xplist,
			       CPL_IO_EXTEND));

    /* Free */
    cpl_free(name_o);
    cpl_image_delete(image);
    image = NULL;
    name_o = NULL;

    /*******************************/
    /*  Write the SYNTHETIC        */
    /*******************************/
    if(detmon_ronbias_config.method_bitmask & PREOVERSCAN) {
	/* Set the file name for the table */
	if(!flag_sets) {
	    name_o = cpl_sprintf("%s_synthetic.fits", recipe_name);
	    assert(name_o != NULL);
	} else {
	    name_o =
                cpl_sprintf("%s_synthetic_set%02d.fits", recipe_name,
                            which_set);
	    assert(name_o != NULL);
	}

	if (whichext == 0) {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
          /* Save the SYNTHETIC image */
	    cpl_propertylist_append(mypro_synth, qclist);

	    skip_if(cpl_dfs_save_image(frameset, NULL, parlist, usedframes,
                                       NULL,synthetic, CPL_BPP_IEEE_DOUBLE,
                                       recipe_name, mypro_synth, NULL,
                                       package, name_o));
#else
          /* Save the SYNTHETIC image */
	    const char * procatg_synth =
		cpl_propertylist_get_string(mypro_synth, CPL_DFS_PRO_CATG);

	    cpl_propertylist_append(mypro_synth, qclist);

	    skip_if(cpl_dfs_save_image(frameset, parlist, usedframes, synthetic,
				       CPL_BPP_IEEE_DOUBLE, recipe_name,
				       procatg_synth,
				      mypro_synth, NULL, package, name_o));
#endif
	} else
	   skip_if(cpl_image_save(synthetic, name_o, CPL_BPP_IEEE_FLOAT,
				  xplist, CPL_IO_EXTEND));

       /* Free */
       cpl_free(name_o);
       name_o = NULL;
    }

    /*******************************/
    /*  Write the PAF file         */
    /*******************************/
    if (qclist) {
	paflist = cpl_propertylist_new();

	/* Set the file name for the PAF */
	if(detmon_ronbias_config.exts >= 0) {
	    skip_if((plist =
		     cpl_propertylist_load(cpl_frame_get_filename(ref_frame),
					   detmon_ronbias_config.exts)) == NULL);

	    if(!flag_sets) {
		name_o = cpl_sprintf("%s.paf", recipe_name);
		assert(name_o != NULL);
	    } else {
		name_o = cpl_sprintf("%s_set%02d.paf",
				     recipe_name, which_set);
		assert(name_o != NULL);
	    }
	} else {
	    skip_if((plist =
		     cpl_propertylist_load(cpl_frame_get_filename(ref_frame),
					   whichext)) == NULL);


	    if(!flag_sets) {
		name_o = cpl_sprintf("%s_ext%02d.paf",
				     recipe_name, whichext);
		assert(name_o != NULL);
	    } else {
		name_o = cpl_sprintf("%s_set%02d_ext%02d.paf",
				     recipe_name,
				     which_set, whichext);
		assert(name_o != NULL);
	    }
	}

	/* Get the keywords for the paf file */
	skip_if(cpl_propertylist_copy_property_regexp(paflist, plist,
						      pafregexp, 0));
	skip_if(cpl_propertylist_copy_property_regexp(paflist, mainplist,
						      pafregexp, 0));

	skip_if(cpl_propertylist_append(paflist, qclist));

	/* Save the PAF */
	skip_if(cpl_dfs_save_paf(pipeline_name, recipe_name, paflist, name_o));

    }

    end_skip;

    cpl_propertylist_delete(plist);
    cpl_propertylist_delete(paflist);
    cpl_propertylist_delete(mainplist);
    cpl_propertylist_delete(xplist);
    cpl_free(name_o);
    cpl_image_delete(image);

    cpl_propertylist_delete(mypro_master);
    cpl_propertylist_delete(mypro_synth);
    cpl_propertylist_delete(mypro_bpmhot);
    cpl_propertylist_delete(mypro_bpmcold);
    cpl_propertylist_delete(mypro_bpmdev);

    return cpl_error_get_code();
}

cpl_propertylist *
xsh_detmon_fill_prolist(const char * procatg,
			   const char * protype,
			   const char * protech,
			   cpl_boolean  proscience)
{
    cpl_propertylist * prolist = cpl_propertylist_new();

    cpl_propertylist_append_string(prolist, CPL_DFS_PRO_CATG,    procatg);
    cpl_propertylist_append_bool(prolist,   CPL_DFS_PRO_SCIENCE, proscience);
    if (protype)
	cpl_propertylist_append_string(prolist, CPL_DFS_PRO_TYPE,    protype);
    if (protech)
	cpl_propertylist_append_string(prolist, CPL_DFS_PRO_TECH,    protech);

    return prolist;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
int
xsh_detmon_ronbias_dfs_set_groups(cpl_frameset * set, const char *tag)
{
    cpl_frame              *cur_frame;
    const char             *cur_tag;
    int                     nframes;
    int                     i;

    /* Check entries */
    if(set == NULL)
        return -1;

    /* Initialize */
    nframes = cpl_frameset_get_size(set);

    /* Loop on frames */
    for(i = 0; i < nframes; i++) {
        cur_frame = cpl_frameset_get_frame(set, i);
        cur_tag = cpl_frame_get_tag(cur_frame);

        /* RAW frames */
        if(!strcmp(cur_tag, tag))
            cpl_frame_set_group(cur_frame, CPL_FRAME_GROUP_RAW);
        /* CALIB frames */

/*        else if (!strcmp(tag, IIINSTRUMENT_CALIB_FLAT))
            cpl_frame_set_group(cur_frame, CPL_FRAME_GROUP_CALIB) ;
*/
    }
    return 0;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_image *
xsh_detmon_build_synthetic(cpl_image * prescan, cpl_image * overscan)
{
    cpl_size                     j;

    int                     distance = detmon_ronbias_config.overscan_urx -
        detmon_ronbias_config.prescan_llx + 1;

    double                 *mean_x =
        (double *) cpl_malloc(sizeof(double) * distance);

    double                 *xvalues =
        (double *) cpl_malloc(sizeof(double) * distance);

    cpl_vector             *x = NULL;
    cpl_vector             *y = NULL;

    cpl_polynomial         *poly = NULL;
    cpl_polynomial         *poly2 = NULL;

    cpl_matrix     * samppos;
    cpl_vector     * fitresidual;

    double                  mse;
    cpl_size                     pows[2] = { 0, 0 };

    cpl_image              *synthetic = NULL;

    double                  initial = 0;

    /* Initialize */
    for(j = 0; j < distance; j++) {
        *(mean_x + j) = 0;
        *(xvalues + j) = j;
    }

    for(j = 0; j < cpl_image_get_size_x(prescan); j++) {
        *(mean_x + j) =
            cpl_image_get_mean_window(prescan, j + 1, 1, j + 1,
                                      cpl_image_get_size_y(prescan));
    }

    for(j = 0; j < cpl_image_get_size_x(overscan); j++) {
        *(mean_x + distance - cpl_image_get_size_x(overscan) + j) =
            cpl_image_get_mean_window(overscan, j + 1, 1, j + 1,
                                      cpl_image_get_size_y(overscan));
    }

    x = cpl_vector_wrap(distance, xvalues);
    y = cpl_vector_wrap(distance, mean_x);

#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
    poly = cpl_polynomial_new(1);
    samppos =
	cpl_matrix_wrap(1, cpl_vector_get_size(x), cpl_vector_get_data(x));
    fitresidual = cpl_vector_new(cpl_vector_get_size(x));

    cpl_polynomial_fit(poly, samppos, NULL, y, NULL,
		       CPL_FALSE, NULL,
		       (cpl_size*)&(detmon_ronbias_config.preoverscan_degree));

    cpl_vector_fill_polynomial_fit_residual(fitresidual, y, NULL, poly,
                                            samppos, NULL);
    cpl_matrix_unwrap(samppos);
    mse = cpl_vector_product(fitresidual, fitresidual)
         / cpl_vector_get_size(fitresidual);

    cpl_vector_delete(fitresidual);
#else
    poly =
        cpl_polynomial_fit_1d_create(x, y,
                                     detmon_ronbias_config.preoverscan_degree,
                                     &mse);
#endif

    cpl_vector_unwrap(x);
    cpl_vector_unwrap(y);

    initial = *mean_x;

    cpl_free(xvalues);
    cpl_free(mean_x);

    poly2 = cpl_polynomial_new(2);

    j = 0;
    cpl_polynomial_set_coeff(poly2, pows, cpl_polynomial_get_coeff(poly, &j));

    pows[0] = 1;
    j = 1;
    cpl_polynomial_set_coeff(poly2, pows, cpl_polynomial_get_coeff(poly, &j));

    cpl_polynomial_delete(poly);

    synthetic =
        cpl_image_new(distance, cpl_image_get_size_y(prescan),
                      CPL_TYPE_DOUBLE);

    if(cpl_image_fill_polynomial(synthetic, poly2, initial, 1, 1, 1)) {
        cpl_msg_error(cpl_func, "Error creating the synthetic frame");
        cpl_polynomial_delete(poly2);
        return NULL;
    }

    cpl_polynomial_delete(poly2);

    return synthetic;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static cpl_error_code
xsh_detmon_ronbias_dutycycl(const cpl_frameset * frameset,
                               cpl_propertylist * qclist)
{
    const cpl_frame        *first = 0;
    cpl_propertylist       *plistfirst = 0;
    double                  tfirst;
    int                     nraws;
    const cpl_frame        *last = 0;
    cpl_propertylist       *plistlast = 0;
    double                  tlast;
    double                  dutycycl;
    cpl_error_code          error;

    first = cpl_frameset_get_first_const(frameset);
    plistfirst = cpl_propertylist_load(cpl_frame_get_filename(first), 0);
    tfirst = cpl_propertylist_get_double(plistfirst, "MJD-OBS");
    nraws = cpl_frameset_get_size(frameset);
    last = cpl_frameset_get_frame_const(frameset, nraws - 1);
    plistlast = cpl_propertylist_load(cpl_frame_get_filename(last), 0);
    tlast = cpl_propertylist_get_double(plistlast, "MJD-OBS");
    dutycycl = (tlast - tfirst) / (nraws - 1);

    error = cpl_error_get_code();
    if (error != CPL_ERROR_NONE)
    {
    	goto cleanup;
    }
	cpl_propertylist_append_double(qclist,DETMON_QC_DUTYCYCL, dutycycl);
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_DUTYCYCL,
					 DETMON_QC_DUTYCYCL_C);

cleanup:

    cpl_propertylist_delete(plistfirst);
    cpl_propertylist_delete(plistlast);

    return error;
}


/*--------------------------------------------------------------------------*/

/*
 * @brief  Reduce periodic noise
 * @param  image         Input image
 * @param  freq          Readout frequency, given in Kpixel/s.
 *
 * @return 1-d image, representing the distribution of noise. NULL if failed.
 */

/*--------------------------------------------------------------------------*/


#define HORIZONTAL TRUE

cpl_table              *
xsh_detmon_pernoise_reduce(cpl_image * image)
{
    int                     nsamples, nffts;
    int                     i, j;

    int status;
    float * hanning = 0;
    float * data = 0;
    float * power = 0;
    cpl_image * power_im = 0;
    cpl_image * output = 0;
    cpl_image * pos_spec = 0;
    cpl_table * table = 0;
    cpl_image* fourier_im = 0;
    double freq;
    cpl_error_code error = CPL_ERROR_NONE;
    cpl_image * sub_image = 0;
    int nffts_old;


    if(detmon_pernoise_config.direction == HORIZONTAL) {
        error = cpl_image_flip(image, 1);
        cpl_ensure(!error, error, NULL);
    }

    nsamples = cpl_image_get_size_x(image);
    nffts = cpl_image_get_size_y(image);


    /* Rewrite the previous lines with a better style (: ...) */

    /*
     * 1. preprocessing task:
     * Estimate the background fitting the image to a
     * 2-D polynomial and substract it from the image.
     */

    error = xsh_detmon_pernoise_rm_bg(image, nsamples, nffts);
    cpl_ensure(!error, error, NULL);

    sub_image = cpl_image_extract(image, nsamples/8 + 1, nffts/8+1,
                                              nsamples*7/8, nffts*7/8);
    nffts_old = nffts;
    nsamples = cpl_image_get_size_x(sub_image);
    nffts = cpl_image_get_size_y(sub_image);

    /*
     * 2. preprocessing task:
     * Remove the effect of hot and dark pixels, replacing their values by
     * the average value of the neighbouring pixels.
     */

    /*
     * 3. preprocessing task:
     * Apply a Hanning vector
     */

    hanning = cpl_malloc(sizeof(float) * nsamples);

    for(i = 0; i < nsamples; i++) {
        *(hanning + i) = 0.5 - 0.5 * cos(2 * CPL_MATH_PI * (float) i / nsamples);
        for(j = 0; j < nffts; j++) {
            double                  value =
                cpl_image_get(sub_image, i + 1, j + 1, &status);
            error = cpl_image_set(sub_image, i + 1, j + 1, (*(hanning + i)) * value);
        }
    }

    cpl_free(hanning);
    if (error != CPL_ERROR_NONE)
    {
    	goto cleanup;
    }
    data = cpl_image_get_data_float(sub_image);

    power = (float *) cpl_calloc(sizeof(float), nsamples * nffts);


    fourier_im = cpl_image_new(nsamples,nffts,  CPL_TYPE_FLOAT_COMPLEX);
    error = cpl_fft_image(fourier_im, sub_image, CPL_FFT_FORWARD);
/*
      fourier =
	(fftw_complex *) fftw_malloc(sizeof(fftw_complex) * nsamples*nffts);

      input =
	(fftw_complex *) fftw_malloc(sizeof(fftw_complex) * nsamples*nffts);

      for(i = 0; i< nffts*nsamples; i++){
 	*((float complex *)input + i) = data[i] + I * 0.0;
      }

      p = fftw_create_plan(nsamples, FFTW_FORWARD, FFTW_ESTIMATE);

      fftw(p, nffts, input, 1, nsamples,
	 fourier, 1, nsamples);

      fftw_destroy_plan(p);
*/
    for(i = 1; i <= nffts; i++) {
        for(j = 1; j <= nsamples; j++) {
        	int rej = 0;
        	double complex cvalue = cpl_image_get_complex(fourier_im,j, i, &rej );
        	double value = cabs(cvalue);
                /*
             *(power + j + i * nsamples) = *((float complex *)fourier + j + i * nsamples) *
		conjf(*((float complex *)fourier +j + i * nsamples));
                */
        	cpl_image_set(power_im, j, i, value);
        }
        /*  Is it necessary to divide here by 2 * pi? */
    }
    cpl_image_delete(fourier_im);

/*
    fftw_free(fourier);
    fftw_free(input);
    power_im = cpl_image_wrap_float(nsamples, nffts, power);
*/
    output   = cpl_image_collapse_create(power_im, 0);
    pos_spec = cpl_image_extract(output, 1, 1, nsamples/2, 1);

    /* cpl_image_unwrap(power_im); */
    cpl_image_delete(power_im);
    cpl_free(power);

    cpl_image_delete(output);

    table = cpl_table_new(nsamples/2);
    cpl_table_new_column(table, "FREQ", CPL_TYPE_DOUBLE);
    cpl_table_new_column(table, "POW", CPL_TYPE_DOUBLE);

    freq = detmon_pernoise_config.speed*1000/nffts_old;

    for(i = 0; i < nsamples/2; i++) {
        error = cpl_table_set(table, "FREQ", i, freq/(nsamples/2)*i);
        error = cpl_table_set(table, "POW", i, cpl_image_get(pos_spec, i+1, 1, &status));
    }

    for(i= 0; i < 5; i++) {
        error = cpl_table_set(table, "POW", i, 0.0);
    }


cleanup:
    cpl_image_delete(pos_spec);

    cpl_image_delete(sub_image);
    if (error != CPL_ERROR_NONE)
    {
    	cpl_table_delete(table);
    	table = 0;
    }
    return table;
}
#undef HORIZONTAL



/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/


cpl_error_code
xsh_detmon_rm_bpixs(cpl_image ** image,
                       const double kappa, int nffts, int nsamples)
{
    int                     i, j;

    float                  *data = cpl_image_get_data_float(*image);
    int k = 0;
    for(i = 0; i < nffts; i++) {
        for(j = 0; j < nsamples; j++) {
            float                   neighbours = 0;
            int                     nneighs = 0;
            float                   average = 0;

            /*
             * Look for the way to optimize this:
             * Some of the points added to neighbours coincide
             * in one iteration and the following
             */
            if(i > 0) {
                neighbours += *(data + (i - 1) * nsamples + j);
                nneighs++;
            }
            if(i < nffts - 1) {
                neighbours += *(data + (i + 1) * nsamples + j);
                nneighs++;
            }
            if(j > 0) {
                neighbours += *(data + i * nsamples + (j - 1));
                nneighs++;
            }
            if(j < nsamples - 1) {
                neighbours += *(data + i * nsamples + (j + 1));
                nneighs++;
            }
            average = neighbours / nneighs;
            if(average > 0) {
                if(*(data + i * nsamples + j) < average * (-1 * kappa) ||
                   *(data + i * nsamples + j) > average * (kappa)) {
                    k++;
                    *(data + i * nsamples + j) = average;
                }
            }
            if(average < 0) {
                if(*(data + i * nsamples + j) > average * (-1 * kappa) ||
                   *(data + i * nsamples + j) < average * (kappa)) {
                    k++;
                    *(data + i * nsamples + j) = average;
                }
            }

        }
    }


    return cpl_error_get_code();

}

/* Start duplicated code */

#define RECT_RON_HS         4
#define RECT_RON_SAMPLES    100

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_flux_get_bias_window(const cpl_image * diff,
                            const int *zone_def,
                            int ron_hsize,
                            int ron_nsamp, double *bias, double *error)
{
    const int               hsize = ron_hsize < 0 ? RECT_RON_HS : ron_hsize;
    const int               nsamples =
        ron_nsamp < 0 ? RECT_RON_SAMPLES : ron_nsamp;
    cpl_bivector           *sample_reg;
    cpl_vector             *rms_list;
    int                     rect[4];
    int                     zone[4];
    double                 *px;
    double                 *py;
    double                 *pr;
    int                     i;

    /* Test entries */
    cpl_ensure_code(diff && bias, CPL_ERROR_NULL_INPUT);

    /* Generate nsamples window centers in the image */
    if(zone_def != NULL) {
        rect[0] = zone_def[0] + hsize + 1;      /* xmin */
        rect[1] = zone_def[1] - hsize - 1;      /* xmax */
        rect[2] = zone_def[2] + hsize + 1;      /* ymin */
        rect[3] = zone_def[3] - hsize - 1;      /* ymax */
    } else {
        rect[0] = hsize + 1;    /* xmin */
        rect[1] = cpl_image_get_size_x(diff) - hsize - 1;       /* xmax */
        rect[2] = hsize + 1;    /* ymin */
        rect[3] = cpl_image_get_size_y(diff) - hsize - 1;       /* ymax */
    }

    cpl_ensure_code(rect[0] < rect[1] && rect[2] < rect[3],
                    CPL_ERROR_ILLEGAL_INPUT);

    /* Generate n+1 regions, because the first region is always at (0,0) */
    /* and it would bias the measurement. */
    sample_reg =
        irplib_bivector_gen_rect_poisson(rect, nsamples + 1, nsamples + 1);
    cpl_ensure(sample_reg != NULL, CPL_ERROR_ILLEGAL_INPUT,
               CPL_ERROR_ILLEGAL_INPUT);

    px = cpl_bivector_get_x_data(sample_reg);
    py = cpl_bivector_get_y_data(sample_reg);

    /* Now, for each window center, extract a vignette and compute the */
    /* signal RMS in it. Store this rms into a table. */
    rms_list = cpl_vector_new(nsamples);
    cpl_ensure(rms_list != NULL, CPL_ERROR_NULL_INPUT, CPL_ERROR_NULL_INPUT);
    pr = cpl_vector_get_data(rms_list);

    for(i = 0; i < nsamples; i++) {
        zone[0] = (int) px[i + 1] - hsize;
        zone[1] = (int) px[i + 1] + hsize;
        zone[2] = (int) py[i + 1] - hsize;
        zone[3] = (int) py[i + 1] + hsize;
        pr[i] = cpl_image_get_mean_window(diff,
                                          zone[0], zone[2], zone[1], zone[3]);
    }
    cpl_bivector_delete(sample_reg);

    /* The error is the rms of the rms */
    if(error != NULL)
        *error = cpl_vector_get_stdev(rms_list);

    /* The final computed RMS is the median of all values.  */
    /* This call will modify the rms_list */
    *bias = cpl_vector_get_median(rms_list);

    cpl_vector_delete(rms_list);

    return CPL_ERROR_NONE;
}

#undef RECT_RON_HS
#undef RECT_RON_SAMPLES

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static cpl_bivector    *
irplib_bivector_gen_rect_poisson(const int *r, const int np, const int homog)
{
    double                  min_dist;
    int                     i;
    int                     gnp;
    cpl_bivector           *list;
    double                  cand_x, cand_y;
    int                     ok;
    int                     start_ndx;
    int                     xmin, xmax, ymin, ymax;

    /* Corrected Homogeneity factor */
    const int               homogc = 0 < homog && homog < np ? homog : np;
    double                 *px;
    double                 *py;

    /* error handling: test arguments are correct */
    cpl_ensure(r, CPL_ERROR_NULL_INPUT, NULL);
    cpl_ensure(np > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);

    list = cpl_bivector_new(np);
    cpl_ensure(list, CPL_ERROR_NULL_INPUT, NULL);
    px = cpl_bivector_get_x_data(list);
    py = cpl_bivector_get_y_data(list);

    xmin = r[0];
    xmax = r[1];
    ymin = r[2];
    ymax = r[3];

    min_dist =
        CPL_MATH_SQRT1_2 * ((xmax - xmin) * (ymax - ymin) / (double) (homogc + 1));
    gnp = 1;
    px[0] = 0;
    py[0] = 0;

    /* First: generate <homog> points */
    while(gnp < homogc) {
        /* Pick a random point within requested range */
        cand_x = cpl_drand() * (xmax - xmin) + xmin;
        cand_y = cpl_drand() * (ymax - ymin) + ymin;

        /* Check the candidate obeys the minimal Poisson distance */
        ok = 1;
        for(i = 0; i < gnp; i++) {
            if(pdist(cand_x, cand_y, px[i], py[i]) < min_dist) {
                /* does not check Poisson law: reject point */
                ok = 0;
                break;
            }
        }
        if(ok) {
            /* obeys Poisson law: register the point as valid */
            px[gnp] = cand_x;
            py[gnp] = cand_y;
            gnp++;
        }
    }

    /* Iterative process: */
    /* Pick points out of Poisson distance of the last <homogc-1> points. */
    start_ndx = 0;
    while(gnp < np) {
        /* Pick a random point within requested range */
        cand_x = cpl_drand() * (xmax - xmin) + xmin;
        cand_y = cpl_drand() * (ymax - ymin) + ymin;

        /* Check the candidate obeys the minimal Poisson distance */
        ok = 1;
        for(i = 0; i < homogc; i++) {
            if(pdist(cand_x,
                     cand_y,
                     px[start_ndx + i], py[start_ndx + i]) < min_dist) {
                /* does not check Poisson law: reject point */
                ok = 0;
                break;
            }
        }
        if(ok) {
            /* obeys Poisson law: register the point as valid */
            px[gnp] = cand_x;
            py[gnp] = cand_y;
            gnp++;
        }
    }

    /* Iterative process: */
    /* Pick points out of Poisson distance of the last <homogc-1> points. */
    start_ndx = 0;
    while(gnp < np) {
        /* Pick a random point within requested range */
        cand_x = cpl_drand() * (xmax - xmin) + xmin;
        cand_y = cpl_drand() * (ymax - ymin) + ymin;

        /* Check the candidate obeys the minimal Poisson distance */
        ok = 1;
        for(i = 0; i < homogc; i++) {
            if(pdist(cand_x,
                     cand_y,
                     px[start_ndx + i], py[start_ndx + i]) < min_dist) {
                /* does not check Poisson law: reject point */
                ok = 0;
                break;
            }
        }
        if(ok) {
            /* obeys Poisson law: register the point as valid */
            px[gnp] = cand_x;
            py[gnp] = cand_y;
            gnp++;
            start_ndx++;
        }
    }
    return list;
}

/* End of duplicated code */

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_detmon_pernoise(cpl_frameset      * frameset,
                       const cpl_parameterlist * parlist,
                       const char        * tag,
                       const char        * recipe_name,
                       const char        * pipeline_name,
                       const char        * procatg_tbl,
                       const char        * package,
                       int              (*compare)(const cpl_frame *,
                                                   const cpl_frame *))
{
    cpl_size                     nsets;
    cpl_size                    *selection = NULL;
    int                     i;
    cpl_error_code          error;

    if(xsh_detmon_pernoise_dfs_set_groups(frameset, tag)) {
        cpl_msg_error(cpl_func, "Cannot identify RAW and CALIB frames");
    }

    /*
     * This function reads all inputs parameters from parlist
     * and stores them in a global variable detmon_ronbias_config.
     * Similar to xsh_detmon_lg_retrieve_parlist(). See xsh_detmon.c
     */
    error = xsh_detmon_pernoise_retrieve_parlist(pipeline_name,
                                                   recipe_name, parlist);
    cpl_ensure_code(!error, error);

    /* Labelise all input frames */
    if(compare == NULL)
        nsets = 1;
    else {
        cpl_msg_info(cpl_func, "Identify the different settings");
        selection = cpl_frameset_labelise(frameset, compare, &nsets);
        if(selection == NULL)
            cpl_msg_error(cpl_func, "Cannot labelise input frames");
    }

    detmon_pernoise_config.nb_extensions = 1;
    if(detmon_pernoise_config.exts < 0) {
        const cpl_frame        *cur_frame =
            cpl_frameset_get_first_const(frameset);
        /* Get the nb of extensions */
        detmon_pernoise_config.nb_extensions =
            cpl_frame_get_nextensions(cur_frame);
    }

    /* Extract settings and reduce each of them */
    for(i = 0; i < nsets; i++)
    {
        int j;
        cpl_table ** freq_table;
        cpl_propertylist ** qclist =
            (cpl_propertylist **)
            cpl_malloc(detmon_pernoise_config.nb_extensions *
                       sizeof(cpl_propertylist *));

        cpl_imagelist ** raws = (cpl_imagelist **) cpl_malloc(detmon_pernoise_config.nb_extensions * sizeof(cpl_imagelist *));
        cpl_image ** input = (cpl_image **) cpl_malloc(detmon_pernoise_config.nb_extensions * sizeof(cpl_image *));

        /* Initialise memory for products */
        if(detmon_pernoise_config.mode == 1)
        {
            freq_table =
                (cpl_table **) cpl_malloc(detmon_pernoise_config.nb_extensions *
                                          4 * sizeof(cpl_table *));
        } else
        {
            freq_table =
                (cpl_table **) cpl_malloc(detmon_pernoise_config.nb_extensions *
                                          sizeof(cpl_table *));
        }

        if(detmon_pernoise_config.exts >= 0)
        {
            *raws =
                cpl_imagelist_load_frameset(frameset, CPL_TYPE_FLOAT, 1,
                                            detmon_pernoise_config.exts);
            *input = cpl_image_subtract_create(cpl_imagelist_get(*raws,0),
                                               cpl_imagelist_get(*raws,1));
        } else
        {
            cpl_imagelist          *raws_all_exts =
                cpl_imagelist_load_frameset(frameset, CPL_TYPE_FLOAT, 1,
                                            -1);
            for(j = 0; j < detmon_pernoise_config.nb_extensions; j++)
            {
                int nframes = cpl_frameset_get_size(frameset);
                int                     k;
                for(k = 0; k < nframes; k++)
                {
                    cpl_image              *image =
                        cpl_imagelist_unset(raws_all_exts,
                                            (detmon_pernoise_config.
                                             nb_extensions - 1 - j) * k);
                    cpl_imagelist_set(raws[j], image, k);
                }
                input[j] =
                    cpl_image_subtract_create(cpl_imagelist_get(raws[j],0),
                                              cpl_imagelist_get(raws[j],1));
            }
        }

        for(j = 0; j < detmon_pernoise_config.nb_extensions; j++) {
            cpl_msg_info(cpl_func, "Starting reduction");
            qclist[j] = cpl_propertylist_new();
            if(detmon_pernoise_config.mode == 1)
            {
                int nx = cpl_image_get_size_x(input[j]);
                int ny = cpl_image_get_size_y(input[j]);
                int k = 0;
                cpl_image* quad[4];

                quad[0] = cpl_image_extract(input[j], 1, 1, nx/2, ny/2);
                quad[1] = cpl_image_extract(input[j], 1, ny/2+1, nx/2, ny);
                quad[2] = cpl_image_extract(input[j], nx/2+1, 1, nx, ny/2);
                quad[3] = cpl_image_extract(input[j], nx/2+1, ny/2+1, nx, ny);

                for (k = 0; k < 4; k++)
                {
                	freq_table[j * 4 + k] = xsh_detmon_pernoise_reduce(quad[k]);
                }
                for(k = 0; k < 4; k++)
                {
                	error = xsh_detmon_pernoise_qc(qclist[j], freq_table[j + k], k+1);
                	if (error != CPL_ERROR_NONE)
                		break;
                }
                for (k = 0; k < 4; k++)
                {
                	cpl_image_delete(quad[k]);
                }
            } else
            {
                freq_table[j] = xsh_detmon_pernoise_reduce(input[j]);
                if(freq_table[j] != NULL)
                {
                	error = xsh_detmon_pernoise_qc(qclist[j], freq_table[j], 0);
                }
            }
            if (error != CPL_ERROR_NONE)
            {
            	break;
            }
        }
        if (error == CPL_ERROR_NONE)
        {
        	error = xsh_detmon_pernoise_save(parlist, frameset, recipe_name,
                                            pipeline_name, procatg_tbl,
                                            package, freq_table, qclist, 0,
                                            0, frameset);
        }

        for(j = 0; j < detmon_pernoise_config.nb_extensions; j++)
        {
            cpl_propertylist_delete(qclist[j]);
            cpl_imagelist_delete(raws[j]);
            cpl_image_delete(input[j]);
        }
        cpl_free(qclist);
        cpl_free(raws);
        cpl_free(input);
        if(detmon_pernoise_config.mode == 1)
        {
            for(j= 0; j < detmon_pernoise_config.nb_extensions * 4; j++) {
                cpl_table_delete(freq_table[j]);
            }
        } else {
            for(j= 0; j < detmon_pernoise_config.nb_extensions; j++) {
                cpl_table_delete(freq_table[j]);
            }
        }
        cpl_free(freq_table);
        if (error != CPL_ERROR_NONE)
        {
        	break;
        }
    }

    return cpl_error_get_code();
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
int
xsh_detmon_pernoise_dfs_set_groups(cpl_frameset * set, const char *tag)
{
    cpl_frame              *cur_frame;
    const char             *cur_tag;
    int                     nframes;
    int                     i;

    /* Check entries */
    if(set == NULL)
        return -1;

    /* Initialize */
    nframes = cpl_frameset_get_size(set);

    /* Loop on frames */
    for(i = 0; i < nframes; i++) {
        cur_frame = cpl_frameset_get_frame(set, i);
        cur_tag = cpl_frame_get_tag(cur_frame);

        /* RAW frames */
        if(!strcmp(cur_tag, tag))
            cpl_frame_set_group(cur_frame, CPL_FRAME_GROUP_RAW);
        /* CALIB frames */

/*        else if (!strcmp(tag, IIINSTRUMENT_CALIB_FLAT))
          cpl_frame_set_group(cur_frame, CPL_FRAME_GROUP_CALIB) ;
*/
    }
    return 0;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_detmon_fill_pernoise_params(cpl_parameterlist * parlist,
                                   const char *recipe_name,
                                   const char *pipeline_name,
                                   int mode,
                                   const char * direction,
                                   double speed,
                                   int llx,
                                   int lly,
                                   int urx,
                                   int ury,
                                   double kappa,
                                   int exts)
{
    xsh_detmon_fill_parlist(parlist, recipe_name, pipeline_name, 9,

                               "mode",
                               "Mode",
                               "CPL_TYPE_INT", mode,

                               "direction",
                               "Readout direction",
                               "CPL_TYPE_BOOL", direction,

                               "speed",
                               "Readout speed",
                               "CPL_TYPE_DOUBLE", speed,

                               "llx",
                               "(yet unsupported) x coordinate of the lower-left "
                               "point of the region of interest. If not modified, default value will be 1.",
                               "CPL_TYPE_INT", llx,
                               "lly",
                               "(yet unsupported) y coordinate of the lower-left "
                               "point of the region of interest. If not modified, default value will be 1.",
                               "CPL_TYPE_INT", lly,
                               "urx",
                               "(yet unsupported) x coordinate of the upper-right "
                               "point of the region of interest. If not modified, default value will be X dimension of the input image.",
                               "CPL_TYPE_INT", urx,
                               "ury",
                               "(yet unsupported) y coordinate of the upper-right "
                               "point of the region of interest. If not modified, default value will be Y dimension of the input image.",
                               "CPL_TYPE_INT", ury,

                               "kappa",
                               "Kappa used for determining threshold of bad (hot, cold) pixels",
                               "CPL_TYPE_DOUBLE", kappa,

                               "exts",
                               "Activate the multi-exts option",
                               "CPL_TYPE_INT", exts);

    return 0;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
int
xsh_detmon_fill_pernoise_params_default(cpl_parameterlist * parlist,
                                           const char *recipe_name,
                                           const char *pipeline_name)
{
    xsh_detmon_fill_pernoise_params(parlist, recipe_name, pipeline_name,
                                       1,           /* --mode      */
                                       "CPL_TRUE",  /* --direction */
                                       84.5,        /* --speed     */
                                       -1,          /* --llx       */
                                       -1,          /* --lly       */
                                       -1,          /* --urx       */
                                       -1,          /* --ury       */
                                       100,          /* --kappa     */
                                       0);          /* --exts      */

    return 0;

}


static cpl_error_code
xsh_detmon_pernoise_retrieve_parlist(const char *pipeline_name,
                                       const char *recipe_name,
                                       const cpl_parameterlist * parlist)
{
    char                   *par_name;
    cpl_parameter          *par;

    /* --mode */
    detmon_pernoise_config.mode =
        xsh_detmon_retrieve_par_int("mode", pipeline_name, recipe_name,
                                   parlist);

    /* --direction */
    par_name = cpl_sprintf("%s.%s.direction", pipeline_name, recipe_name);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    detmon_pernoise_config.direction = cpl_parameter_get_bool(par);
    cpl_free(par_name);

    /* --speed */
    par_name = cpl_sprintf("%s.%s.speed", pipeline_name, recipe_name);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    detmon_pernoise_config.speed = cpl_parameter_get_double(par);
    cpl_free(par_name);

    /* --llx */
    detmon_pernoise_config.llx =
        xsh_detmon_retrieve_par_int("llx", pipeline_name, recipe_name,
                                   parlist);

    /* --lly */
    detmon_pernoise_config.lly =
        xsh_detmon_retrieve_par_int("lly", pipeline_name, recipe_name,
                                   parlist);
    /* --urx */
    detmon_pernoise_config.urx =
        xsh_detmon_retrieve_par_int("urx", pipeline_name, recipe_name,
                                   parlist);
    /* --ury */
    detmon_pernoise_config.ury =
        xsh_detmon_retrieve_par_int("ury", pipeline_name, recipe_name,
                                   parlist);
    /* --kappa */
    detmon_pernoise_config.kappa =
        xsh_detmon_retrieve_par_double("kappa", pipeline_name, recipe_name,
                                   parlist);

    /* --exts */
    detmon_pernoise_config.exts =
        xsh_detmon_retrieve_par_int("exts", pipeline_name, recipe_name,
                                   parlist);

    if(cpl_error_get_code()) {
        cpl_msg_error(cpl_func, "Failed to retrieve the input parameters");
        cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
    }

    return cpl_error_get_code();
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static                  cpl_error_code
xsh_detmon_pernoise_save(const cpl_parameterlist * parlist,
                            cpl_frameset * frameset,
                            const char *recipe_name,
                            const char *pipeline_name,
                            const char *procatg_tbl,
                            const char *package,
                            cpl_table ** freq_table,
                            cpl_propertylist ** qclist,
                            const int flag_sets,
                            const int which_set,
                            const cpl_frameset * usedframes)
{

    cpl_frame              *ref_frame;
    cpl_propertylist       *plist;
    char                   *name_o = NULL; /* Avoid (false) uninit warning */
    int                     i, j;
    cpl_propertylist       *paflist;
    cpl_error_code          error;

#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
    cpl_propertylist * pro_tbl = cpl_propertylist_new();

    cpl_propertylist_append_string(pro_tbl,
                                   CPL_DFS_PRO_CATG, procatg_tbl);

    cpl_propertylist_append(pro_tbl, qclist[0]);

#endif
    /*******************************/
    /*  Write the FREQ TABLE  */

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

    if(detmon_pernoise_config.mode != 1) {
        /* Set the file name for the table */
        if(!flag_sets) {
            name_o = cpl_sprintf("%s_freq_table.fits", recipe_name);
            assert(name_o != NULL);
        } else {
            name_o =
                cpl_sprintf("%s_freq_table_set%02d.fits", recipe_name,
                               which_set);
            assert(name_o != NULL);
        }

#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
        /* Save the table */
        if(cpl_dfs_save_table(frameset, NULL, parlist, usedframes, NULL, freq_table[0],
                              NULL, recipe_name, pro_tbl, NULL,
                              package, name_o)) {
            cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
            cpl_free(name_o);
            cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
        }
#else
        /* Save the table */
        if(cpl_dfs_save_table(frameset, parlist, usedframes, freq_table[0],
                              NULL, recipe_name, procatg_tbl, qclist[0], NULL,
                              package, name_o)) {
            cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
            cpl_free(name_o);
            cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
        }
#endif

        if(detmon_pernoise_config.exts < 0) {

            for(i = 1; i < detmon_pernoise_config.nb_extensions; i++) {
                error =
                    cpl_table_save(freq_table[i], NULL, qclist[i], name_o,
                                   CPL_IO_EXTEND);
                cpl_ensure_code(!error, error);
            }
        }

        /* Free */
        cpl_free(name_o);

    } else {
        for (j = 1; j <= 4; j++) {
            /* Set the file name for the table */
            if(!flag_sets) {
                name_o = cpl_sprintf("%s_freq_table_quad%02d.fits",
                                        recipe_name, j);
                assert(name_o != NULL);
            } else {
                name_o =
                    cpl_sprintf("%s_freq_table_quad%02d_set%02d.fits",
                                   recipe_name, j, which_set);
                assert(name_o != NULL);
            }

#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
            /* Save the table */
            if(cpl_dfs_save_table(frameset, NULL, parlist, usedframes, NULL,
                                  freq_table[j - 1],
                                  NULL, recipe_name, pro_tbl, NULL,
                                  package, name_o)) {
                cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
                cpl_free(name_o);
                cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
            }
#else
            /* Save the table */
            if(cpl_dfs_save_table(frameset, parlist, usedframes,
                                  freq_table[j - 1],
                                  NULL, recipe_name, procatg_tbl, qclist[0], NULL,
                                  package, name_o)) {
                cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
                cpl_free(name_o);
                cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
            }
#endif

            if(detmon_pernoise_config.exts < 0) {
                for(i = 1; i < detmon_pernoise_config.nb_extensions; i++) {
                    error = cpl_table_save(freq_table[(j-1) + 4 * i],
                                           NULL, qclist[i], name_o,
                                           CPL_IO_EXTEND);
                    cpl_ensure_code(!error, error);
                }
            }

            /* Free */
            cpl_free(name_o);
        }

    }
    /*******************************/
    /*  Write the PAF file(s)      */
    /*******************************/

    /* Get FITS header from reference file */
    ref_frame = cpl_frameset_get_first(frameset);
    if((plist = cpl_propertylist_load(cpl_frame_get_filename(ref_frame),
                                      0)) == NULL) {
        cpl_msg_error(cpl_func, "getting header from reference frame");
        cpl_ensure_code(0, cpl_error_get_code());
    }

    /* Get the keywords for the paf file */
    paflist = cpl_propertylist_new();
    cpl_propertylist_copy_property_regexp(paflist, plist,
                                          "^(ARCFILE|MJD-OBS|ESO TPL ID|"
                                          "DATE-OBS|ESO DET DIT|ESO DET NDIT|"
                                          "ESO DET NCORRS|"
                                          "ESO DET MODE NAME)$", 0);

    for(i = 0; i < detmon_pernoise_config.nb_extensions; i++) {
        cpl_propertylist * c_paflist = cpl_propertylist_duplicate(paflist);
        error = cpl_propertylist_append(c_paflist, qclist[i]);
        cpl_ensure_code(!error, error);

        /* Set the file name for the bpm */
        if(detmon_pernoise_config.exts >= 0) {
            if(!flag_sets) {
                name_o = cpl_sprintf("%s.paf", recipe_name);
                assert(name_o != NULL);
            } else {
                name_o = cpl_sprintf("%s_set%02d.paf", recipe_name, which_set);
                assert(name_o != NULL);
            }
        } else {
            if(!flag_sets) {
                name_o = cpl_sprintf("%s_ext%02d.paf", recipe_name, i+1);
                assert(name_o != NULL);
            } else {
                name_o = cpl_sprintf("%s_set%02d_ext%02d.paf", recipe_name, which_set, i+1);
                assert(name_o != NULL);
            }
        }
        /* Save the PAF */
        if(cpl_dfs_save_paf(pipeline_name, recipe_name, c_paflist, name_o)) {
            cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
            cpl_free(name_o);
            cpl_propertylist_delete(paflist);
            cpl_propertylist_delete(plist);
            cpl_free(name_o);
            cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
        }
        cpl_propertylist_delete(c_paflist);
        cpl_free(name_o);
    }

    cpl_propertylist_delete(plist);
    cpl_propertylist_delete(paflist);
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
    cpl_propertylist_delete(pro_tbl);
#endif
    return cpl_error_get_code();
}

static cpl_error_code
xsh_detmon_pernoise_qc(cpl_propertylist * qclist,
                          cpl_table * table,
                          int iquad)
{
    cpl_error_code error;
    char * propname;

    double freqs[3] = {0, 0, 0};
    double pows[3] = {0, 0, 0};

/*    error = cpl_propertylist_append_bool(reflist, "POW", TRUE);
    cpl_ensure_code(!error, error);

    error = cpl_table_sort(table, reflist);
    cpl_ensure_code(!error, error);
*/

    int nrows = cpl_table_get_nrow(table);
    int i;

    double * all_freqs = cpl_table_get_data_double(table, "FREQ");
    double * all_pows  = cpl_table_get_data_double(table, "POW");

    for ( i= 1; i< nrows-1; i++){
        if (all_pows[i] > pows[0]) {
            if(all_pows[i-1] < all_pows[i] && all_pows[i] > all_pows[i+1]){
                pows[2]=pows[1];
                pows[1]=pows[0];
                pows[0]=all_pows[i];

                freqs[2]=freqs[1];
                freqs[1]=freqs[0];
                freqs[0]=all_freqs[i];
            }
        } else if (all_pows[i] > pows[1]) {
            if(all_pows[i-1] < all_pows[i] && all_pows[i] > all_pows[i+1]){
                pows[2]=pows[1];
                pows[1]=all_pows[i];

                freqs[2]=freqs[1];
                freqs[1]=all_freqs[i];
            }

        } else if(all_pows[i] > pows[2]) {
            if(all_pows[i-1] < all_pows[i] && all_pows[i] > all_pows[i+1]){
                pows[2]=all_pows[i];

                freqs[2]=all_freqs[i];
            }

        }
    }

    if (detmon_pernoise_config.mode == 1) {
        propname = cpl_sprintf("ESO QC FREQ1 %d", iquad);
        assert(propname != NULL);
    } else {
        propname = cpl_sprintf("ESO QC FREQ1");
    }

    error = cpl_propertylist_append_double(qclist, propname, freqs[0]);
    error = cpl_propertylist_set_comment(qclist,propname,DETMON_QC_FREQ_C);
    cpl_ensure_code(!error, error);

    cpl_free(propname);

    if (detmon_pernoise_config.mode == 1) {
        propname = cpl_sprintf("ESO QC FREQ2 %d", iquad);
        assert(propname != NULL);
    } else {
        propname = cpl_sprintf("ESO QC FREQ2");
    }

    error = cpl_propertylist_append_double(qclist, propname, freqs[1]);
    error = cpl_propertylist_set_comment(qclist,propname,DETMON_QC_FREQ_C);
    cpl_ensure_code(!error, error);

    cpl_free(propname);

    if (detmon_pernoise_config.mode == 1) {
        propname = cpl_sprintf("ESO QC FREQ3 %d", iquad);
        assert(propname != NULL);
    } else {
        propname = cpl_sprintf("ESO QC FREQ3");
    }

    error = cpl_propertylist_append_double(qclist, propname, freqs[2]);
    error = cpl_propertylist_set_comment(qclist,propname,DETMON_QC_FREQ_C);
    cpl_ensure_code(!error, error);

    cpl_free(propname);

    if (detmon_pernoise_config.mode == 1) {
        propname = cpl_sprintf("ESO QC POW1 %d", iquad);
        assert(propname != NULL);
    } else {
        propname = cpl_sprintf("ESO QC POW1");
    }

    error = cpl_propertylist_append_double(qclist, propname, pows[0]);
    error = cpl_propertylist_set_comment(qclist,propname,DETMON_QC_POW_C);
    cpl_ensure_code(!error, error);

    cpl_free(propname);

    if (detmon_pernoise_config.mode == 1) {
        propname = cpl_sprintf("ESO QC POW2 %d", iquad);
        assert(propname != NULL);
    } else {
        propname = cpl_sprintf("ESO QC POW2");
    }

    error = cpl_propertylist_append_double(qclist, propname, pows[1]);
    error = cpl_propertylist_set_comment(qclist,propname,DETMON_QC_POW_C);
    cpl_ensure_code(!error, error);

    cpl_free(propname);

    if (detmon_pernoise_config.mode == 1) {
        propname = cpl_sprintf("ESO QC POW3 %d", iquad);
        assert(propname != NULL);
    } else {
        propname = cpl_sprintf("ESO QC POW3");
    }

    error = cpl_propertylist_append_double(qclist, propname, pows[2]);
    error = cpl_propertylist_set_comment(qclist,propname,DETMON_QC_POW_C);
    cpl_ensure_code(!error, error);


    cpl_free(propname);

    return cpl_error_get_code();
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_detmon_pernoise_rm_bg(cpl_image * image, int nsamples, int nffts)
{
    cpl_vector             *values = cpl_vector_new(nsamples * nffts);

    int                     rejected;
    int i, j;
    cpl_vector *xy_pos = cpl_vector_new(nsamples * nffts * 2);
    double mse = 0;
    cpl_polynomial * poly_2d = 0;
    cpl_image * poly_ima = 0;
    cpl_size degree = 3;
    cpl_error_code error = CPL_ERROR_NONE;
    cpl_matrix * samppos = 0;
    cpl_vector * fitresidual = 0;

    for(i = 1; i <= nffts; i++) {
        for(j = 1; j <= nsamples; j++) {
            cpl_vector_set(xy_pos, (i - 1) * nsamples + (j - 1), j);
            cpl_vector_set(xy_pos, (i - 1) * nsamples + (j - 1) + nsamples * nffts, i);
            cpl_vector_set(values, (i - 1) * nsamples + (j - 1),
                           cpl_image_get(image, j, i, &rejected));
            error = cpl_error_get_code();
            if (error != CPL_ERROR_NONE)
            {
            	break;
            }
        }
        if (error != CPL_ERROR_NONE)
        {
        	break;
        }
    }
    if (error != CPL_ERROR_NONE)
    {
    	goto cleanup;
    }
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
    poly_2d = cpl_polynomial_new(2);
    samppos =
	cpl_matrix_wrap(2, nsamples * nffts, cpl_vector_get_data(xy_pos));
    fitresidual = cpl_vector_new(nsamples * nffts);
    cpl_polynomial_fit(poly_2d, samppos, NULL, values, NULL,
		       CPL_FALSE, NULL, &degree);

    cpl_vector_fill_polynomial_fit_residual(fitresidual, values, NULL, poly_2d,
					    samppos, NULL);
    cpl_matrix_unwrap(samppos);
    mse = cpl_vector_product(fitresidual, fitresidual)
	/ cpl_vector_get_size(fitresidual);
    cpl_vector_delete(fitresidual);

#else
    poly_2d = cpl_polynomial_fit_2d_create(xy_pos, values, 3, &mse);
#endif

    poly_ima = cpl_image_new(nsamples, nffts, CPL_TYPE_FLOAT);

    cpl_image_fill_polynomial(poly_ima, poly_2d, 1, 1, 1, 1);

    cpl_image_subtract(image, poly_ima);

cleanup:
    cpl_polynomial_delete(poly_2d);
    cpl_image_delete(poly_ima);
    cpl_vector_delete(xy_pos);
    cpl_vector_delete(values);

    return cpl_error_get_code();
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_detmon_dark(cpl_frameset      * frameset,
                   const cpl_parameterlist * parlist,
                   const char        * tag,
                   const char        * recipe_name,
                   const char        * pipeline_name,
                   const char        * procatg_master,
                   const char        * procatg_dsnu,
                   const char        * procatg_tbl,
                   const char        * package,
                   int              (*compare)(const cpl_frame *,
                                               const cpl_frame *))
{
    cpl_size                     nsets;
    cpl_size                    *selection = NULL;
    int                     i;
    cpl_error_code          error;

    if(xsh_detmon_dark_dfs_set_groups(frameset, tag)) {
        cpl_msg_error(cpl_func, "Cannot identify RAW and CALIB frames");
    }

    /*
     * This function reads all inputs parameters from parlist
     * and stores them in a global variable detmon_ronbias_config.
     * Similar to xsh_detmon_lg_retrieve_parlist(). See xsh_detmon.c
     */
    error = xsh_detmon_retrieve_dark_params(pipeline_name,
                                               recipe_name, parlist);
    cpl_ensure_code(!error, error);

    /* Labelise all input frames */
    if(compare == NULL)
        nsets = 1;
    else {
        cpl_msg_info(cpl_func, "Identify the different settings");
        selection = cpl_frameset_labelise(frameset, compare, &nsets);
        if(selection == NULL)
            cpl_msg_error(cpl_func, "Cannot labelise input frames");
    }

    detmon_dark_config.nb_extensions = 1;
    if(detmon_dark_config.exts < 0) {
        const cpl_frame        *cur_frame =
            cpl_frameset_get_first_const(frameset);
        /* Get the nb of extensions */
        detmon_dark_config.nb_extensions =
            cpl_frame_get_nextensions(cur_frame);
    }

    /* Extract settings and reduce each of them */
    for(i = 0; i < nsets; i++) {
        cpl_size                    *select_dits = NULL;
        cpl_frameset           *cur_fset =
            nsets == 1 ? cpl_frameset_duplicate(frameset) :
            cpl_frameset_extract(frameset, selection, i);

        cpl_size                     ndits = 0;
        int                     j, k;
        cpl_table ** dsnu_table = NULL;
        cpl_imagelist ** dsnu = NULL;

        cpl_propertylist ** qclist =
            (cpl_propertylist **)
            cpl_malloc(detmon_dark_config.nb_extensions *
                       sizeof(cpl_propertylist *));


        cpl_imagelist ** masters =
            (cpl_imagelist **)
            cpl_malloc(detmon_dark_config.nb_extensions *
                       sizeof(cpl_imagelist *));

        /* Initialise memory for products */
        if(detmon_dark_config.opt_nir == OPT) {
            dsnu_table =
                (cpl_table **) cpl_malloc(detmon_dark_config.nb_extensions *
                                          sizeof(cpl_table *));
            dsnu =
                (cpl_imagelist **)
                cpl_malloc(detmon_dark_config.nb_extensions *
                                          sizeof(cpl_imagelist *));
        }

        select_dits = cpl_frameset_labelise(cur_fset,
                                            xsh_detmon_compare_dits,
                                            &ndits);

        if(detmon_dark_config.exts >= 0) {
            *masters = cpl_imagelist_new();
            if(detmon_dark_config.opt_nir == OPT) {
                *dsnu = cpl_imagelist_new();
                *dsnu_table = cpl_table_new(ndits);
            }
            *qclist = cpl_propertylist_new();
            cpl_table_new_column(*dsnu_table, "DIT", CPL_TYPE_DOUBLE);
            cpl_table_new_column(*dsnu_table, "STDEV", CPL_TYPE_DOUBLE);
        } else {
            for ( j = 0; j < detmon_dark_config.nb_extensions; j ++) {
                masters[j] = cpl_imagelist_new();
                if(detmon_dark_config.opt_nir == OPT) {
                    dsnu[j] = cpl_imagelist_new();
                    dsnu_table[j] = cpl_table_new(ndits);
                }
                qclist[j] = cpl_propertylist_new();
                cpl_table_new_column(dsnu_table[j], "DIT", CPL_TYPE_DOUBLE);
                cpl_table_new_column(dsnu_table[j], "STDEV", CPL_TYPE_DOUBLE);
            }
        }

        for(j = 0; j < ndits; j++) {
            cpl_frameset  * cur_fdit = cpl_frameset_extract(cur_fset,
                                                            select_dits, j);
            cpl_imagelist ** raws =
                (cpl_imagelist **)
                cpl_malloc(detmon_dark_config.nb_extensions *
                           sizeof(cpl_imagelist *));

            if(detmon_dark_config.exts >= 0) {
                cpl_image * collapsed;
                *raws =
                    cpl_imagelist_load_frameset(cur_fdit, CPL_TYPE_FLOAT, 1,
                                                detmon_dark_config.exts);
                collapsed = cpl_imagelist_collapse_create(*raws);
                cpl_imagelist_set(*masters, collapsed, j);
                if(detmon_dark_config.opt_nir == OPT) {
                    xsh_detmon_dark_dsnu(cur_fdit, *dsnu, *dsnu_table,
                                            collapsed, j);
                }
                xsh_detmon_dark_qc(*qclist, collapsed);
            } else {
                cpl_imagelist          *raws_all_exts =
                    cpl_imagelist_load_frameset(cur_fdit, CPL_TYPE_FLOAT, 1,
                                                -1);
                for(k = 0; k < detmon_dark_config.nb_extensions; k++) {
                    int nframes = cpl_frameset_get_size(cur_fdit);
                    int h;
                    cpl_image * collapsed;
                    for(h = 0; h < nframes; h++) {
                        cpl_image              *image =
                            cpl_imagelist_unset(raws_all_exts,
                                                (detmon_dark_config.
                                                 nb_extensions - 1 - k) * h);
                        cpl_imagelist_set(raws[k], image, h);
                    }
                    collapsed = cpl_imagelist_collapse_create(raws[k]);
                    cpl_imagelist_set(masters[k],collapsed, j);
                    if(detmon_dark_config.opt_nir == OPT) {
                        xsh_detmon_dark_dsnu(cur_fdit, dsnu[k],
                                                dsnu_table[j], collapsed, j);
                    }
                    xsh_detmon_dark_qc(qclist[k], collapsed);
                }
            }

            cpl_frameset_delete(cur_fdit);
            for(k = 0; k < detmon_dark_config.nb_extensions; k++) {
                cpl_imagelist_delete(raws[k]);
            }
            cpl_free(raws);
        } /* end of loop (for) around different DIT values */

        cpl_frameset_delete(cur_fset);

        xsh_detmon_dark_save(parlist, frameset, recipe_name, pipeline_name,
                                procatg_master, procatg_tbl, procatg_dsnu,
                                package, masters, dsnu_table, dsnu, qclist,
                                0, 0, frameset);

        if(detmon_dark_config.opt_nir == OPT) {
            for(j = 0; j < detmon_dark_config.nb_extensions; j++) {
                cpl_table_delete(dsnu_table[j]);
                cpl_imagelist_delete(dsnu[j]);
            }
            cpl_free(dsnu_table);
            cpl_free(dsnu);
        }

        for(j = 0; j < detmon_dark_config.nb_extensions; j++) {
            cpl_propertylist_delete(qclist[j]);
            cpl_imagelist_delete(masters[j]);
        }
        cpl_free(qclist);
        cpl_free(masters);
        cpl_free(select_dits);

    } /* end of loop (for) around different setting */

    cpl_free(selection);

    return cpl_error_get_code();
}


/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
int
xsh_detmon_dark_dfs_set_groups(cpl_frameset * set, const char *tag)
{
    cpl_frame              *cur_frame;
    const char             *cur_tag;
    int                     nframes;
    int                     i;

    /* Check entries */
    if(set == NULL)
        return -1;

    /* Initialize */
    nframes = cpl_frameset_get_size(set);

    /* Loop on frames */
    for(i = 0; i < nframes; i++) {
        cur_frame = cpl_frameset_get_frame(set, i);
        cur_tag = cpl_frame_get_tag(cur_frame);

        /* RAW frames */
        if(!strcmp(cur_tag, tag))
            cpl_frame_set_group(cur_frame, CPL_FRAME_GROUP_RAW);
        /* CALIB frames */

/*        else if (!strcmp(tag, IIINSTRUMENT_CALIB_FLAT))
          cpl_frame_set_group(cur_frame, CPL_FRAME_GROUP_CALIB) ;
*/
    }
    return 0;
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static                  cpl_error_code
xsh_detmon_retrieve_dark_params(const char *pipeline_name,
                                      const char *recipe_name,
                                      const cpl_parameterlist * parlist)
{
    char                   *par_name;
    cpl_parameter          *par;

    /* --ron.method */
    par_name = cpl_sprintf("%s.%s.ron.method", pipeline_name, recipe_name);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    detmon_dark_config.ron_method = cpl_parameter_get_string(par);
    cpl_free(par_name);

    /* --dsnu.method */
    par_name = cpl_sprintf("%s.%s.dsnu.method", pipeline_name, recipe_name);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    detmon_dark_config.dsnu_method = cpl_parameter_get_string(par);
    cpl_free(par_name);

    /* --opt_nir */
    par_name = cpl_sprintf("%s.%s.opt_nir", pipeline_name, recipe_name);
    assert(par_name != NULL);
    par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
    detmon_dark_config.opt_nir = cpl_parameter_get_bool(par);
    cpl_free(par_name);

    /* --exts */
    detmon_dark_config.exts =
        xsh_detmon_retrieve_par_int("exts", pipeline_name, recipe_name,
                                   parlist);

    if(cpl_error_get_code()) {
        cpl_msg_error(cpl_func, "Failed to retrieve the input parameters");
        cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
    }


    return CPL_ERROR_NONE;
}


/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_detmon_fill_dark_params(cpl_parameterlist * parlist,
                               const char *recipe_name,
                               const char *pipeline_name,
                               const char * ron_method,
                               const char * dsnu_method,
                               const char * opt_nir,
                               int exts)
{
    xsh_detmon_fill_parlist(parlist, recipe_name, pipeline_name, 4,

                               "ron.method",
                               "Method used to compute RON. Currently no "
			       "change is possible, RMS computed",
                               "CPL_TYPE_STRING", ron_method,

                               "dsnu.method",
                               "Method used to compute DSNU map. Currently no "
			       "change is possible. Method used STDEV",
                               "CPL_TYPE_STRING", dsnu_method,

                               "opt_nir",
                               "Boolean, OPT (FALSE) or NIR(TRUE)",
                               "CPL_TYPE_BOOL", opt_nir,

                               "exts",
                               "Activate the multi-exts option. Default 0"
			       "(primary unit), -1 (all exts)",
                               "CPL_TYPE_INT", exts);

    return cpl_error_get_code();
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
int
xsh_detmon_fill_dark_params_default(cpl_parameterlist * parlist,
                                       const char *recipe_name,
                                       const char *pipeline_name)
{
    xsh_detmon_fill_dark_params(parlist, recipe_name, pipeline_name,
                                   "SIMPLE", /* --ron.method  */
                                   "STDEV",  /* --dsnu.method */
                                   "CPL_FALSE",  /* OPT*/
                                   0);       /* --exts        */
    return cpl_error_get_code();
}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
cpl_error_code
xsh_detmon_dark_dsnu(cpl_frameset * cur_fdit,
                        cpl_imagelist * dsnu,
                        cpl_table * dsnu_table,
                        cpl_image * collapsed,
                        int pos)
{
    cpl_frame * first = cpl_frameset_get_first(cur_fdit);
    cpl_propertylist * plist =
        cpl_propertylist_load(cpl_frame_get_filename(first), 0);
    double dit = irplib_pfits_get_exptime(plist);
    double mean = cpl_image_get_mean(collapsed);

    cpl_image * dsnu_map =
        cpl_image_subtract_scalar_create(collapsed, mean);
    double stdev;
    cpl_image_divide_scalar(dsnu_map, mean);
    stdev = cpl_image_get_stdev(dsnu_map);

    cpl_imagelist_set(dsnu, dsnu_map, pos);

    cpl_table_set(dsnu_table, "DIT", pos, dit);
    cpl_table_set(dsnu_table, "STDEV", pos, stdev);

    cpl_propertylist_delete(plist);

    return cpl_error_get_code();

}

/*---------------------------------------------------------------------------*/

/*
 * @brief  Retrieve input parameters
 * @param  pipeline_name        Input image
 * @param  recipe_name          Input image
 * @param  parlist              Shift to apply on the x-axis
 * @return CPL_ERROR_NONE on success.
 */

/*---------------------------------------------------------------------------*/
static                  cpl_error_code
xsh_detmon_dark_save(const cpl_parameterlist * parlist,
                        cpl_frameset * frameset,
                        const char *recipe_name,
                        const char *pipeline_name,
                        const char *procatg_master,
                        const char *procatg_tbl,
                        const char *procatg_dsnu,
                        const char *package,
                        cpl_imagelist ** masters,
                        cpl_table ** dsnu_table,
                        cpl_imagelist ** dsnu,
                        cpl_propertylist ** qclist,
                        const int flag_sets,
                        const int which_set,
                        const cpl_frameset * usedframes)
{

    cpl_frame              *ref_frame;
    cpl_propertylist       *plist;
    char                   *name_o = NULL; /* Avoid (false) uninit warning */
    int                     i, j;
    cpl_propertylist       *paflist;
    cpl_error_code          error;
    int nb_images;

    /***************************/
    /*  Write the MASTER FITS  */
    /***************************/

    nb_images = cpl_imagelist_get_size(masters[0]);
    cpl_ensure_code(nb_images > 0, CPL_ERROR_DATA_NOT_FOUND);


    for(i = 0; i < nb_images; i++) {
        /* Set the file name for each image */
        if(!flag_sets) {
            name_o =
                cpl_sprintf("%s_master_dit_%d.fits", recipe_name, i+1);
            assert(name_o != NULL);
        } else {
            name_o =
                cpl_sprintf("%s_master_dit_%d_set%02d.fits",
                               recipe_name, i, which_set);
            assert(name_o != NULL);
        }


        /* Save the image */
        if(detmon_dark_config.exts >= 0) {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
            cpl_propertylist * pro_master = cpl_propertylist_new();

            cpl_propertylist_append_string(pro_master,
                                           CPL_DFS_PRO_CATG, procatg_master);

            cpl_propertylist_append(pro_master, qclist[0]);

            if(cpl_dfs_save_image
               (frameset, NULL, parlist, usedframes, NULL,
                cpl_imagelist_get(*masters, i), CPL_BPP_IEEE_FLOAT,
                recipe_name, pro_master, NULL, package,
                name_o)) {
                cpl_msg_error(cpl_func, "Cannot save the product: %s",
                              name_o);
                cpl_free(name_o);
                cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);

            }

	    cpl_propertylist_delete(pro_master);
#else
            if(cpl_dfs_save_image
               (frameset, parlist, usedframes,
                cpl_imagelist_get(*masters, i), CPL_BPP_IEEE_FLOAT,
                recipe_name, procatg_master, qclist[0], NULL, package,
                name_o)) {
                cpl_msg_error(cpl_func, "Cannot save the product: %s",
                              name_o);
                cpl_free(name_o);
                cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);

            }
#endif
        } else {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
            cpl_propertylist * pro_master = cpl_propertylist_new();

            cpl_propertylist_append_string(pro_master,
                                           CPL_DFS_PRO_CATG, procatg_master);

            cpl_propertylist_append(pro_master, qclist[0]);

            if(cpl_dfs_save_image(frameset, NULL, parlist, usedframes, NULL,
	                          NULL, CPL_BPP_IEEE_FLOAT, recipe_name,
                                  pro_master, NULL,
                                  package, name_o)) {
                cpl_msg_error(cpl_func, "Cannot save the product: %s",
                              name_o);
                cpl_free(name_o);
                cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
            }

	    cpl_propertylist_delete(pro_master);
#else
            if(cpl_dfs_save_image(frameset, parlist, usedframes, NULL,
                                  CPL_BPP_IEEE_FLOAT, recipe_name,
                                  procatg_master, qclist[0], NULL,
                                  package, name_o)) {
                cpl_msg_error(cpl_func, "Cannot save the product: %s",
                              name_o);
                cpl_free(name_o);
                cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
            }
#endif
            for(j = 0; j < detmon_dark_config.nb_extensions; j++) {
                error =
                    cpl_image_save(cpl_imagelist_get(masters[j], i),
                                   name_o, CPL_BPP_IEEE_FLOAT, qclist[j],
                                   CPL_IO_EXTEND);
                cpl_ensure_code(!error, error);
            }
        }
	cpl_free(name_o);
    }

    if (detmon_dark_config.opt_nir == OPT) {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
        cpl_propertylist * pro_tbl = cpl_propertylist_new();

        cpl_propertylist_append_string(pro_tbl,
                                     CPL_DFS_PRO_CATG, procatg_tbl);

        cpl_propertylist_append(pro_tbl, qclist[0]);
#endif
        /*******************************/
        /*  Write the LINEARITY TABLE  */
        /*******************************/

        /* Set the file name for the table */
        if(!flag_sets) {
            name_o = cpl_sprintf("%s_dsnu_table.fits", recipe_name);
            assert(name_o != NULL);
        } else {
            name_o =
                cpl_sprintf("%s_dsnu_table_set%02d.fits", recipe_name,
                               which_set);
            assert(name_o != NULL);
        }
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
        /* Save the table */
        if(cpl_dfs_save_table(frameset, NULL, parlist, usedframes, NULL,
                              dsnu_table[0], NULL, recipe_name, pro_tbl, NULL,
                              package, name_o)) {
            cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
            cpl_free(name_o);
            cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
        }
#else
        /* Save the table */
        if(cpl_dfs_save_table(frameset, parlist, usedframes, dsnu_table[0],
                              NULL, recipe_name, procatg_tbl, qclist[0], NULL,
                              package, name_o)) {
            cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
            cpl_free(name_o);
            cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
        }
#endif

#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
        cpl_propertylist_delete(pro_tbl);
#endif

        if(detmon_dark_config.exts < 0) {

            for(i = 1; i < detmon_dark_config.nb_extensions; i++) {
                error =
                    cpl_table_save(dsnu_table[i], NULL, qclist[i], name_o,
                                   CPL_IO_EXTEND);
                cpl_ensure_code(!error, error);
            }
        }

        /* Free */
        cpl_free(name_o);

        /***************************/
        /*  Write the DSNU_MAP FITS  */
        /***************************/

        for(i = 0; i < nb_images; i++) {
            /* Set the file name for each image */
            if(!flag_sets) {
                name_o =
                    cpl_sprintf("%s_dsnu_map_dit_%d.fits", recipe_name, i+1);
                assert(name_o != NULL);
            } else {
                name_o =
                    cpl_sprintf("%s_dsnu_map_dit_%d_set%02d.fits",
                                   recipe_name, i, which_set);
                assert(name_o != NULL);
            }


            /* Save the image */
            if(detmon_dark_config.exts >= 0) {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
	    	cpl_propertylist * pro_dsnu = cpl_propertylist_new();

		cpl_propertylist_append_string(pro_dsnu,
	                                       CPL_DFS_PRO_CATG, procatg_dsnu);

	        cpl_propertylist_append(pro_dsnu, qclist[0]);

                if(cpl_dfs_save_image
                   (frameset, NULL, parlist, usedframes, NULL,
                    cpl_imagelist_get(*dsnu, i), CPL_BPP_IEEE_FLOAT,
                    recipe_name, pro_dsnu, NULL, package,
                    name_o)) {
                    cpl_msg_error(cpl_func, "Cannot save the product: %s",
                                  name_o);
                    cpl_free(name_o);
                    cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);

                }

	        cpl_propertylist_delete(pro_dsnu);
#else
                if(cpl_dfs_save_image
                   (frameset, parlist, usedframes,
                    cpl_imagelist_get(*dsnu, i), CPL_BPP_IEEE_FLOAT,
                    recipe_name, procatg_dsnu, qclist[0], NULL, package,
                    name_o)) {
                    cpl_msg_error(cpl_func, "Cannot save the product: %s",
                                  name_o);
                    cpl_free(name_o);
                    cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);

                }
#endif
            } else {
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
	    	cpl_propertylist * pro_dsnu = cpl_propertylist_new();

		cpl_propertylist_append_string(pro_dsnu,
	                                       CPL_DFS_PRO_CATG, procatg_dsnu);

	        cpl_propertylist_append(pro_dsnu, qclist[0]);

	        if(cpl_dfs_save_image(frameset, NULL, parlist, usedframes,
	                              NULL, NULL,
                                      CPL_BPP_IEEE_FLOAT, recipe_name,
                                      pro_dsnu, NULL,
                                      package, name_o)) {
                    cpl_msg_error(cpl_func, "Cannot save the product: %s",
                                  name_o);
                    cpl_free(name_o);
                    cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
                }

	        cpl_propertylist_delete(pro_dsnu);
#else
                if(cpl_dfs_save_image(frameset, parlist, usedframes, NULL,
                                      CPL_BPP_IEEE_FLOAT, recipe_name,
                                      procatg_dsnu, qclist[0], NULL,
                                      package, name_o)) {
                    cpl_msg_error(cpl_func, "Cannot save the product: %s",
                                  name_o);
                    cpl_free(name_o);
                    cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
                }
#endif
                for(j = 0; j < detmon_dark_config.nb_extensions; j++) {
                    error =
                        cpl_image_save(cpl_imagelist_get(dsnu[j], i),
                                       name_o, CPL_BPP_IEEE_FLOAT, qclist[j],
                                       CPL_IO_EXTEND);
                    cpl_ensure_code(!error, error);
                }
            }
	    cpl_free(name_o);
        }



    } /* End of if(OPT) */

    /*******************************/
    /*  Write the PAF file(s)      */
    /*******************************/

    /* Get FITS header from reference file */
    ref_frame = cpl_frameset_get_first(frameset);
    if((plist = cpl_propertylist_load(cpl_frame_get_filename(ref_frame),
                                      0)) == NULL) {
        cpl_msg_error(cpl_func, "getting header from reference frame");
        cpl_ensure_code(0, cpl_error_get_code());
    }

    /* Get the keywords for the paf file */
    paflist = cpl_propertylist_new();
    cpl_propertylist_copy_property_regexp(paflist, plist,
                                          "^(ARCFILE|MJD-OBS|ESO TPL ID|"
                                          "DATE-OBS|ESO DET DIT|ESO DET NDIT|"
                                          "ESO DET NCORRS|"
                                          "ESO DET MODE NAME)$", 0);

    for(i = 0; i < detmon_dark_config.nb_extensions; i++) {
        cpl_propertylist * c_paflist = cpl_propertylist_duplicate(paflist);
        error = cpl_propertylist_append(c_paflist, qclist[i]);
        cpl_ensure_code(!error, error);

        /* Set the file name for the bpm */
        if(detmon_dark_config.exts >= 0) {
            if(!flag_sets) {
                name_o = cpl_sprintf("%s.paf", recipe_name);
                assert(name_o != NULL);
            } else {
                name_o = cpl_sprintf("%s_set%02d.paf", recipe_name, which_set);
                assert(name_o != NULL);
            }
        } else {
            if(!flag_sets) {
                name_o = cpl_sprintf("%s_ext%02d.paf", recipe_name, i+1);
                assert(name_o != NULL);
            } else {
                name_o = cpl_sprintf("%s_set%02d_ext%02d.paf", recipe_name, which_set, i+1);
                assert(name_o != NULL);
            }
        }
        /* Save the PAF */
        if(cpl_dfs_save_paf(pipeline_name, recipe_name, c_paflist, name_o)) {
            cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
            cpl_free(name_o);
            cpl_propertylist_delete(paflist);
            cpl_propertylist_delete(plist);
            cpl_free(name_o);
            cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
        }
        cpl_propertylist_delete(c_paflist);
        cpl_free(name_o);
    }

    cpl_propertylist_delete(plist);
    cpl_propertylist_delete(paflist);

    return cpl_error_get_code();
}

cpl_error_code
xsh_detmon_dark_qc(cpl_propertylist * qclist,
                      cpl_image * collapsed)
{
    double mean = cpl_image_get_mean(collapsed);
    double stdev = cpl_image_get_stdev(collapsed);

    cpl_error_code error;

    error = cpl_propertylist_append_double(qclist,DETMON_QC_DARK, mean);
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_DARK,
					 DETMON_QC_DARK_C);
    cpl_ensure_code(!error, error);

    error = cpl_propertylist_append_double(qclist,DETMON_QC_DARK_STDEV, stdev);
    error = cpl_propertylist_set_comment(qclist,DETMON_QC_DARK_STDEV,
					 DETMON_QC_DARK_STDEV_C);
    cpl_ensure_code(!error, error);

    return cpl_error_get_code();
}


/*---------------------------------------------------------------------------*/
/**
  @brief    Collapse an imagelist to the stdev of each pixel position
  @param    imlist    the input images list
  @return   the average image or NULL on error case.
  @see cpl_imagelist_is_uniform()

  The returned image has to be deallocated with cpl_image_delete().

  The bad pixel maps of the images in the input list are taken into
  account, the result image pixels are flagged as rejected for those
  where there were no good pixel at the same position in the input image list.

  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL
  - CPL_ERROR_ILLEGAL_INPUT if the input image list is not valid
 */
/*---------------------------------------------------------------------------*/
cpl_image *
irplib_imagelist_collapse_stdev_create(const cpl_imagelist * imlist)
{
    cpl_image          *    mean;
    cpl_image          *    delta;
    cpl_image          *    sq_delta;
    cpl_image          *    stdev;

    int                     i;

    /* Check inputs */
    cpl_ensure(imlist != NULL, CPL_ERROR_NULL_INPUT, NULL);
    cpl_ensure(cpl_imagelist_is_uniform(imlist) == 0, CPL_ERROR_ILLEGAL_INPUT,
               NULL);

    /* Create mean image with its first iterative value = first image */
    mean = cpl_image_duplicate(cpl_imagelist_get_const(imlist, 0));
    cpl_image_fill_rejected(mean, 0.0);
    cpl_image_accept_all(mean);

    stdev = cpl_image_new(cpl_image_get_size_x(mean),
			  cpl_image_get_size_y(mean),
			  CPL_TYPE_FLOAT);

    for (i = 1; i < cpl_imagelist_get_size(imlist); i++) {
        delta = cpl_image_subtract_create(cpl_imagelist_get_const(imlist, i),
	                                  mean);
        cpl_image_fill_rejected(delta, 0.0);
        cpl_image_accept_all(delta);

	sq_delta = cpl_image_multiply_create(delta, delta);

	cpl_image_multiply_scalar(sq_delta, ((double) i / (double)(i+1)));
	cpl_image_add(stdev, sq_delta);

	cpl_image_divide_scalar(delta, i + 1);
	cpl_image_add(mean, delta);

        cpl_image_delete(delta);
        cpl_image_delete(sq_delta);
    }

    cpl_image_divide_scalar(stdev, cpl_imagelist_get_size(imlist) - 1);
    cpl_image_power(stdev, 0.5);

    cpl_image_delete(mean);

    return stdev;
}
