/* $Id: sinfo_skycor.c,v 1.50 2012-05-04 08:11:35 amodigli Exp $
 *
 * This file is part of the SINFONI Pipeline
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author: amodigli $
 * $Date: 2012-05-04 08:11:35 $
 * $Revision: 1.50 $
 * $Name: not supported by cvs2svn $
 */

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

/*-----------------------------------------------------------------------------
                                Includes
 ----------------------------------------------------------------------------*/
#include <math.h>
#include <cpl.h>
#include <sinfo_cpl_size.h>

#include <irplib_utils.h>

#include <sinfo_skycor.h>
#include <sinfo_new_cube_ops.h>
#include "sinfo_pfits.h"
#include "sinfo_functions.h"

#include "sinfo_msg.h"
#include "sinfo_error.h"
#include "sinfo_globals.h"
#include "sinfo_utils_wrappers.h"
#include "sinfo_utl_cube2spectrum.h"
#include "sinfo_pro_types.h"
/*-----------------------------------------------------------------------------
                                Defines
 ----------------------------------------------------------------------------*/

#define BAND_H_WAVE_MIN 1.445 //not used
#define BAND_H_WAVE_MAX 1.820 //not used

#define BAND_K_WAVE_MIN 1.945 //not used
#define BAND_K_WAVE_MAX 2.460 //not used

#define BAND_J_WAVE_MIN 1.445 //not used
#define BAND_J_WAVE_MAX 1.82  //not used

#define SINFO_FIT_BKG_TEMP 280.
#define SKY_THRES 0.95
#define SKY_LINE_MAX_CUT 4      /* this should be 4 */
#define SKY_LINE_MIN_CUT 4      /* this should be 4 */


#define XCOR_YSHIFT_KS_CLIP 5      /* this should be 5 */


#define HPLANK 6.62606876e-34;   // J s
#define CLIGHT 2.99792458e+08;   // m / s
#define KBOLTZ 1.3806503e-23;    // J / K
#define AMOEBA_FTOL 1.e-5
#define NBOUND 14
#define NROT   25
#define N_ITER_FIT_LM 15
#define N_ITER_FIT_AMOEBA 10

double sinfo_scale_fct=1;
static cpl_vector* sa_vx=NULL;
static cpl_vector* sa_vy=NULL;

static cpl_vector* sa_ox=NULL;
static cpl_vector* sa_oy=NULL;
static cpl_vector* sa_sy=NULL;

/*-----------------------------------------------------------------------------
                            Functions prototypes
-----------------------------------------------------------------------------*/
cpl_vector* sinfo_sky_background_estimate(cpl_vector *spectrum,
                                             int msize,
                                             int fsize);


static int
sinfo_scales_obj_sky_cubes(cpl_imagelist* obj_cub,
                            cpl_imagelist* sky_cub,
                            cpl_table* bkg,
                            cpl_table* rscale,
                            cpl_imagelist** obj_cor);

static int
sinfo_sub_thr_bkg_from_obj_cube(cpl_imagelist* obj_cub,
				cpl_table* int_sky,
				cpl_imagelist** obj_cor);

static cpl_vector*
sinfo_filter_min(const cpl_vector* vi, const int size);

static cpl_vector*
sinfo_filter_max(const cpl_vector* vi, const int size);

static cpl_vector*
sinfo_filter_smo(const cpl_vector* vi, const int size);

static cpl_imagelist*
sinfo_cube_zshift_simple(cpl_imagelist* inp,
                        const float shift);

static void
sinfo_optimise_sky_sub(const double wtol,
                       const double line_hw,
                       const int method,
                       const int do_rot,
                       cpl_table* lrange,
                       cpl_table* lambda,
                       cpl_table* lr41,
                       cpl_table* lr52,
                       cpl_table* lr63,
                       cpl_table* lr74,
                       cpl_table* lr02,
                       cpl_table* lr85,
                       cpl_table* lr20,
                       cpl_table* lr31,
                       cpl_table* lr42,
                       cpl_table* lr53,
                       cpl_table* lr64,
                       cpl_table* lr75,
                       cpl_table* lr86,
                       cpl_table* lr97,
                       cpl_table* lr00,
                       cpl_table** int_obj,
                       cpl_table** int_sky,
                       cpl_table** rscale);

static void
sinfo_shift_sky(cpl_frame** sky_frm,
                cpl_table** int_sky,
                const double zshift);

static double
sinfo_xcorr(cpl_table* int_obj,
            cpl_table* int_sky,
            cpl_table* lambda,
            const double dispersion,
            const double line_hw);

static cpl_table*
sinfo_interpolate_sky(const cpl_table* inp,const cpl_table* lambdas);

static int
sinfo_check_screw_values(cpl_table** int_obj,
                         cpl_table** int_sky,
                         cpl_table* grange,
                         const double wtol);

static int
sinfo_choose_good_sky_pixels(cpl_frame* obj_frm,
                             cpl_image* r_img,
                             cpl_image* g_img,
                             const double min_frac,
                             cpl_image** mask);


static int
sinfo_sum_spectra(const cpl_frame* obj,
                  const cpl_frame* sky,
                  cpl_image* mask,
                  cpl_table* wrange,
                  const int llx,
                  const int lly,
                  const int urx,
                  const int ury,
                  cpl_table** int_obj,
                  cpl_table** int_sky);

int
sinfo_thermal_background2(cpl_table* int_sky,
                         cpl_table* lambda,
                         cpl_table* lrange,
              cpl_table** bkg);

static int
sinfo_thermal_background(cpl_table* int_sky,
                         cpl_table* lambda,
                         cpl_table* lrange,
                         const double temp,
                         const int niter,
                         const int filter_width,
                         const double wtol,
                         cpl_table** bkg,
                         int* success_fit);

static int
sinfo_object_flag_sky_pixels(cpl_frame* obj_frm,
                             cpl_table* lambda,
                             cpl_table* mrange,
                 cpl_imagelist* flag_data,
                             const double tol,
                             cpl_image** g_img,
                             cpl_image** r_img,
                             cpl_image** image);

static int
sinfo_object_flag_low_values(cpl_frame* obj_frm,
                             const double cnt,
                             const double sig,
                             cpl_imagelist** flag_data);

static int
sinfo_object_estimate_noise(cpl_frame* obj_frm, const int obj_noise_fit,
                            double* centre, double* noise);



static int
sinfo_set_ranges(cpl_frame* obj_frm,
                 cpl_frame* sky_frm,
                 cpl_parameterlist* cfg,
                 cpl_table** lambda,
                 cpl_table** lr41,
                 cpl_table** lr52,
                 cpl_table** lr63,
                 cpl_table** lr74,
                 cpl_table** lr02,
                 cpl_table** lr85,
                 cpl_table** lr20,
                 cpl_table** lr31,
                 cpl_table** lr42,
                 cpl_table** lr53,
                 cpl_table** lr64,
                 cpl_table** lr75,
                 cpl_table** lr86,
                 cpl_table** lr97,
                 cpl_table** lr00,
                 cpl_table** lrange,
                 cpl_table** grange,
                 cpl_table** lambdas,
                 cpl_table** mrange,
                 int* sky_interp_sw,
                 double* dispersion);


static cpl_table*
sinfo_table_extract_rest(cpl_table* inp,
                         cpl_table* low,
                         cpl_table* med,
                         const double wtol);

static int
sinfo_get_sub_regions(cpl_table* sky,
                      cpl_table* x1,
                      cpl_table* pos,
                      const double wtol,
                      const int npixw,
                      cpl_table** sub_regions);


static int
sinfo_get_obj_sky_wav_sub(cpl_table* obj,
                          cpl_table* sky,
                          cpl_table* wav,
                          cpl_table* sel,
                          const double wtol,
                          cpl_table** sub_obj,
                          cpl_table** sub_sky,
                          cpl_table** sub_wav);


static cpl_table*
sinfo_find_rot_waves(const double w_rot[],
                     const int npix_w,
                     const double w_step,
                     cpl_table* range);
static int
sinfo_compute_line_ratio(cpl_table* obj,
                         cpl_table* sky,
                         const double wtol,
                         const int meth,
                         const cpl_table* sel_regions,
                         cpl_table* cont_regions,
                         double* r);

static cpl_table*
sinfo_table_subtract_continuum(cpl_table* lin,cpl_table* cnt);

static double
sinfo_fit_bkg(double p[]);

static double
sinfo_fit_sky(double p[]);

static int
sinfo_get_line_ratio_amoeba(cpl_table* obj,
                            cpl_table* sky,
                            double* r);

static cpl_table*
sinfo_table_interpol(cpl_table* obj_lin,
                     cpl_table* obj_cnt,
                     cpl_table* sky_lin,
                     cpl_table* sky_cnt,
                     const double r);


static int
sinfo_get_line_ratio(cpl_table* obj_lin,
                     cpl_table* obj_cnt,
                     cpl_table* sky_lin,
                     cpl_table* sky_cnt,
                     const int method,
                     double* r);

static cpl_table*
sinfo_table_shift_simple(cpl_table* inp,
                         const char* col,
                         const double shift);
/*
static int
sinfo_table_set_column_invalid(cpl_table** int_sky,const char* col);
*/
static int
sinfo_table_set(cpl_table** out,
                const cpl_table* ref,
                const double val,
                const double tol);

static int
sinfo_table_threshold(cpl_table** t,
                      const char* column,
                      const double low_cut,
                      const double hig_cut,
                      const double low_ass,
                      const double hig_ass);




static double sinfo_fac(const double x, const double t);

static int sinfo_fitbkg(const double x[],
                        const double a[],
                        double *result);
static int sinfo_fitbkg_derivative(const double x[],
                                   const double a[],
                       double result[]);


static int
sinfo_convolve_kernel(cpl_table** t, const int rad);
int
sinfo_convolve_kernel2(cpl_table** t, const int rad);

int
sinfo_convolve_gauss(cpl_table** t, const int rad, const double fwhm);
int
sinfo_convolve_exp(cpl_table** t, const int rad, const double fwhm);

static int
sinfo_table_sky_obj_flag_nan(cpl_table** s,cpl_table** o, cpl_table** w);

static int
sinfo_table_set_nan_out_min_max(cpl_table** s,
                                const char* c,
                                const double min,
                                const double max);




static double
sinfo_gaussian_amp(double area,double sigma,double x,double x0,double off);
static double
sinfo_gaussian_area(double amp,double sigma,double x,double x0,double off);

int
sinfo_table_smooth_column(cpl_table** t, const char* c, const int r);

static int
sinfo_image_flag_nan(cpl_image** im);

static int
sinfo_table_flag_nan(cpl_table** t,const char* label);



static int
sinfo_cnt_mask_thresh_and_obj_finite(const cpl_image* mask,
                                     const double t,
                                     const cpl_image* obj);




static cpl_table*
sinfo_interpolate(const cpl_table* inp,
                  const cpl_table* lambdas,
                  const char* name,
                  const char* method);

static cpl_table*
sinfo_image2table(const cpl_image* im);

static int
sinfo_table_extract_finite(const cpl_table* in1,
                           const cpl_table* in2,
                                 cpl_table** ou1,
                                 cpl_table** ou2);

static cpl_table*
sinfo_slice_z(const cpl_imagelist* cin,const int i,const int j);



static cpl_imagelist*
sinfo_imagelist_select_range(const cpl_imagelist* inp,
                                  const cpl_table* full,
                                  const cpl_table* good,
                                  const double tol);



static cpl_table*
sinfo_table_select_range(cpl_table* inp,
                              cpl_table* ref,
                              const double tol);

static int
sinfo_table_fill_column_over_range(cpl_table** inp,
                                   const cpl_table* ref,
                                   const char* col,
                                   const double val,
                                   const double tol);




static int
sinfo_table_column_dindgen(cpl_table** t, const char* label);














/**@{*/
/*---------------------------------------------------------------------------*/
/**
 * @addtogroup sinfo_utl_skycor Functions to correct sky residuals on \
                              science cubes
 */
/*---------------------------------------------------------------------------*/

/**
@name sinfo_skycor_qc_new
@brief structure to init skycor_qc
@return pointer to structure

*/
sinfo_skycor_qc*
sinfo_skycor_qc_new(void)
 {
   sinfo_skycor_qc * sqc;
   sqc= cpl_malloc(sizeof(sinfo_skycor_qc));

   sqc->th_fit=0;

   return sqc;

}
/**
@name sinfo_skycor_qc_delete
@brief function to free a skycor_qc structure
@return void

*/
void
sinfo_skycor_qc_delete(sinfo_skycor_qc** sqc)
{
  if((*sqc) != NULL) {
    cpl_free(*sqc);
    *sqc=NULL;
  }
}



/**
  @brief    Execute the plugin instance given by the interface
  @param    config  parameter configuration
  @param    obj_frm input object frm
  @param    sky_frm input sky frm
  @param    sqc      QC parameters
  @param    obj_cor  corrected object cube
  @param    int_obj  corrected object spectrum
  @return   0 if everything is ok, -1 else
 */
/*---------------------------------------------------------------------------*/
int
sinfo_skycor(cpl_parameterlist * config,
             cpl_frame* obj_frm,
             cpl_frame* sky_frm,
             sinfo_skycor_qc* sqc,
             cpl_imagelist** obj_cor,
             cpl_table** int_obj)
{

  cpl_table* bkg=NULL;

  cpl_table* lambda=NULL;
  cpl_table* lr41=NULL;
  cpl_table* lr52=NULL;
  cpl_table* lr63=NULL;
  cpl_table* lr74=NULL;
  cpl_table* lr02=NULL;
  cpl_table* lr85=NULL;
  cpl_table* lr20=NULL;
  cpl_table* lr31=NULL;
  cpl_table* lr42=NULL;
  cpl_table* lr53=NULL;
  cpl_table* lr64=NULL;
  cpl_table* lr75=NULL;
  cpl_table* lr86=NULL;
  cpl_table* lr97=NULL;
  cpl_table* lr00=NULL;
  cpl_table* lrange=NULL;
  cpl_table* mrange=NULL;
  cpl_table* grange=NULL;
  cpl_table* lambdas=NULL;

  cpl_table* int_sky=NULL;

  cpl_image* mask=NULL;
  cpl_image* gpix=NULL;
  cpl_image* ratio=NULL;
  cpl_image* ima_sky=NULL;
  cpl_imagelist* fdata=NULL;
  cpl_table* rscale=NULL;
  cpl_parameter* p=NULL;

  int th_fit=0;
  double dispersion=0;
  double noise=0;
  //double temp=252.69284;
  double centre=0;
  int sky_interp_sw=0;
  double wshift=0;
  double pshift=0;
  int method=0;
  int do_rot=0;
  int obj_noise_fit=0;
  int niter=3;
  double min_frac=0.8;
  double line_hw=7;
  double fit_temp=280;
  int filter_width=SINFO_SKY_BKG_FILTER_WIDTH;
  int llx=0;
  int lly=0;
  int urx=64;
  int ury=64;
  cpl_imagelist* obj_cub=NULL;
  cpl_imagelist* sky_cub=NULL;
  int sub_thr_bkg=0;


  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.min_frac"));
  check_nomsg(min_frac=cpl_parameter_get_double(p));

  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.line_half_width"));
  check_nomsg(line_hw=cpl_parameter_get_double(p));



  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.sky_bkg_filter_width"));
  check_nomsg(filter_width=cpl_parameter_get_int(p));

  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.scale_method"));
  check_nomsg(method=cpl_parameter_get_int(p));



  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.rot_cor"));
  check_nomsg(do_rot=cpl_parameter_get_bool(p));


  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.sub_thr_bkg_from_obj"));
  check_nomsg(sub_thr_bkg=cpl_parameter_get_bool(p));


  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.fit_obj_noise"));
  check_nomsg(obj_noise_fit=cpl_parameter_get_bool(p));


  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.niter"));
  check_nomsg(niter=cpl_parameter_get_int(p));


  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.pshift"));
  check_nomsg(pshift=cpl_parameter_get_double(p));



  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.llx"));
  check_nomsg(llx=cpl_parameter_get_int(p));


  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.lly"));
  check_nomsg(lly=cpl_parameter_get_int(p));


  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.urx"));
  check_nomsg(urx=cpl_parameter_get_int(p));


  check_nomsg(p=cpl_parameterlist_find(config,
                "sinfoni.sinfo_utl_skycor.ury"));
  check_nomsg(ury=cpl_parameter_get_int(p));

  // set wavelength ranges
  sinfo_msg("Set wavelength ranges");
  ck0(sinfo_set_ranges(obj_frm,sky_frm,config,&lambda,
                       &lr41,&lr52,&lr63,&lr74,&lr02,&lr85,&lr20,&lr31,&lr42,
                       &lr53,&lr64,&lr75,&lr86,&lr97,&lr00,
                       &lrange,&grange,&lambdas,&mrange,&sky_interp_sw,
                       &dispersion),"Setting wavelength ranges");
  //check_nomsg(cpl_table_save(grange, NULL, NULL, "out_grange.fits",
  //CPL_IO_DEFAULT));

  /*
  sinfo_msg("lr20=%d",cpl_table_get_nrow(lr20));
  sinfo_msg("lr31=%d",cpl_table_get_nrow(lr31));
  sinfo_msg("lr42=%d",cpl_table_get_nrow(lr42));
  sinfo_msg("lr53=%d",cpl_table_get_nrow(lr53));
  sinfo_msg("lr64=%d",cpl_table_get_nrow(lr64));
  sinfo_msg("lr75=%d",cpl_table_get_nrow(lr75));
  sinfo_msg("lr86=%d",cpl_table_get_nrow(lr86));
  sinfo_msg("lr97=%d",cpl_table_get_nrow(lr97));
  sinfo_msg("lr00=%d",cpl_table_get_nrow(lr00));

  sinfo_msg("min_lrange=%f",cpl_table_get_column_min(lrange,"INDEX"));
  sinfo_msg("min_grange=%f",cpl_table_get_column_min(grange,"INDEX"));
  sinfo_msg("min_srange=%f",cpl_table_get_column_min(lambdas,"WAVE"));
  sinfo_msg("min_mrange=%f",cpl_table_get_column_min(mrange,"INDEX"));

  sinfo_msg("max_lrange=%f",cpl_table_get_column_max(lrange,"INDEX"));
  sinfo_msg("max_grange=%f",cpl_table_get_column_max(grange,"INDEX"));
  sinfo_msg("max_srange=%f",cpl_table_get_column_max(lambdas,"WAVE"));
  sinfo_msg("max_mrange=%f",cpl_table_get_column_max(mrange,"INDEX"));
  */

  sinfo_msg("Estimate noise");
  ck0(sinfo_object_estimate_noise(obj_frm,obj_noise_fit,&centre,&noise),
                                  "Estimating noise");

  sinfo_msg("Background=%f Noise=%f",centre,noise);
  sinfo_msg("Flag object low_levels");
  ck0(sinfo_object_flag_low_values(obj_frm,centre,noise,&fdata),
      "Flagging low pix");

  //cpl_imagelist_save(fdata,"out_fdata.fits",
  //                   CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);


  sinfo_msg("Flag sky pixels");
  ck0(sinfo_object_flag_sky_pixels(obj_frm,lambda,mrange,fdata,dispersion,
                                   &gpix,&ratio,&ima_sky),
                                   "Flagging sky pixels");

  //cpl_image_save(gpix,"out_gpix.fits",CPL_BPP_IEEE_FLOAT,
  //                 NULL,CPL_IO_DEFAULT);
  //cpl_image_save(ratio,"out_ratio.fits",CPL_BPP_IEEE_FLOAT,
  //                 NULL,CPL_IO_DEFAULT);
  //cpl_image_save(ima_sky,"out_ima_sky.fits",
  //               CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);




  // choose pixels which seems to be good sky pixels
  // (95% spectral pixels are flagged)
  sinfo_msg("Choose good sky (with > 95%% good spectral) pixels");
  ck0(sinfo_choose_good_sky_pixels(obj_frm,ratio,gpix,min_frac,&mask),
      "Choosing good sky pixels");

  //cpl_image_save(mask,"out_mask.fits",CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);

  // threshold ratio for fraction 'minfract' of spatial pixels to be 'sky'
  //sinfo_msg("To do: flag_threshold_sky_pixels");


  // sum spectra of flagged pixels in object and sky frames
  sinfo_msg("Sum obj and sky spectra");
  ck0(sinfo_sum_spectra(obj_frm,sky_frm,mask,lambda,llx,lly,urx,ury,int_obj,
			&int_sky),"summing obj & sky spectra");

  //check_nomsg(cpl_table_save(*int_obj, NULL, NULL, "out_int_obj.fits",
  // CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(int_sky, NULL, NULL, "out_int_sky.fits",
  //CPL_IO_DEFAULT));

   // Computes thermal background
  sinfo_msg("Computes thermal background");
  /*
  ck0(sinfo_thermal_background2(int_sky,lambda,lrange,&bkg),
     "getting termal bkg");
  */

  ck0(sinfo_thermal_background(int_sky,lambda,lrange,fit_temp,niter,
                               filter_width,dispersion,&bkg,&th_fit),
      "getting termal bkg");


  check_nomsg(cpl_table_duplicate_column(*int_obj,"INT_SKY_ORG",int_sky,"INT"));
  check_nomsg(cpl_table_duplicate_column(*int_obj,"INT_BKG_FIT",bkg,"INT2"));
  check_nomsg(cpl_table_duplicate_column(*int_obj,"INT_BKG_SMO",int_sky,
                                         "INT_BKG_SMO"));

  sqc->th_fit=th_fit;
  //check_nomsg(cpl_table_save(bkg,NULL,NULL,"out_thermal_background.fits",
  //CPL_IO_DEFAULT));


  /*
  ck0(sinfo_pro_save_tbl(bkg,set,set,"out_thermal_background.fits",
             "THERMAL_BACKGROUND",NULL,cpl_func,config),
      "Error saving %s","THERMAL_BACKGROUND");
  */

  sinfo_msg("Subtracts thermal background from integrated OH spectrum");
  //sinfo_msg("nrow=%d %d",cpl_table_get_nrow(int_sky),
  //                       cpl_table_get_nrow(bkg));
  check_nomsg(cpl_table_duplicate_column(int_sky,"BKG",bkg,"INT2"));
  check_nomsg(cpl_table_duplicate_column(int_sky,"INT0",int_sky,"INT"));
  check_nomsg(cpl_table_subtract_columns(int_sky,"INT","BKG"));



  //check_nomsg(cpl_table_duplicate_column(int_obj,"INT",
  //                                         int_obj,"INT_OBJ_COR"));

  if(sub_thr_bkg == 1) {
    check_nomsg(cpl_table_duplicate_column(*int_obj,"THR_BKG",bkg,"INT2"));
    check_nomsg(cpl_table_subtract_columns(*int_obj,"INT","THR_BKG"));
  }

  //check_nomsg(cpl_table_save(*int_obj, NULL, NULL, "out_int_obj.fits",
  //CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(int_sky, NULL, NULL, "out_int_sky.fits",
  //CPL_IO_DEFAULT));


  //check_nomsg(cpl_table_erase_column(int_sky,"BKG"));
  //check_nomsg(cpl_table_save(grange, NULL, NULL, "out_grange_6.fits",
  //CPL_IO_DEFAULT));

   //check_nomsg(cpl_table_save(int_sky, NULL, NULL, "out_int_sky_sub.fits",
   //CPL_IO_DEFAULT));



  // check for screw values at ends of spectrum
  sinfo_msg("Checks for screw values at ends of spectrum");
  sinfo_check_screw_values(int_obj,&int_sky,grange,dispersion);
  //check_nomsg(cpl_table_save(grange, NULL, NULL, "out_grange_7.fits",
  //CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(*int_obj, NULL, NULL, "out_int_obj_chk.fits",
  //CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(int_sky, NULL, NULL, "out_int_sky_chk.fits",
  //CPL_IO_DEFAULT));



  if(sky_interp_sw == 1) {
    sinfo_msg("Interpolate sky if necessary");
    sinfo_interpolate_sky(int_sky,lambdas);
  }


  sinfo_msg("Crosscorrelate obj & sky to check for lambda offset");
  //check_nomsg(cpl_table_save(*int_obj, NULL, NULL, "out_int_obj_chk.fits",
  //CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(int_sky, NULL, NULL, "out_int_sky_chk.fits",
  //CPL_IO_DEFAULT));
  check_nomsg(wshift=sinfo_xcorr(*int_obj,int_sky,lambda,dispersion,line_hw));


  //wshift=-1.7164495*dispersion;
  sinfo_msg("Dispersion=%f",dispersion);
  if(pshift == 0) {
    pshift=wshift/dispersion;
  }
  sinfo_msg("Shift sky of %f pixels toward object",pshift);

  check_nomsg(sinfo_shift_sky(&sky_frm,&int_sky,pshift));
  //check_nomsg(cpl_table_save(*int_obj, NULL, NULL, "out_pip3_int_obj.fits",
  //CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(int_sky, NULL, NULL, "out_pip3_int_sky.fits",
  //CPL_IO_DEFAULT));


  //DEBUG
  sinfo_msg("Optimise sky subtraction");
  check_nomsg(sinfo_optimise_sky_sub(dispersion,line_hw,method,do_rot,
                                     lrange,lambda,
                                     lr41,lr52,lr63,lr74,lr02,lr85,
                                     lr20,lr31,lr42,lr53,lr64,lr75,
				     lr86,lr97,lr00,int_obj,&int_sky,
				     &rscale));


  //check_nomsg(cpl_table_save(*int_obj, NULL, NULL, "out_int_obj.fits",
  //CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(int_sky, NULL, NULL, "out_int_sky.fits",
  //CPL_IO_DEFAULT));


  sinfo_msg("Apply same scaling to whole cubes");


  cknull_nomsg(obj_cub=cpl_imagelist_load(cpl_frame_get_filename(obj_frm),
                                          CPL_TYPE_DOUBLE,0));
  cknull_nomsg(sky_cub=cpl_imagelist_load(cpl_frame_get_filename(sky_frm),
                                          CPL_TYPE_DOUBLE,0));




  if(sub_thr_bkg == 1) {
    ck0_nomsg(sinfo_sub_thr_bkg_from_obj_cube(obj_cub,int_sky,obj_cor));
  } else {
    check_nomsg(*obj_cor=cpl_imagelist_duplicate(obj_cub));
  }

  ck0_nomsg(sinfo_scales_obj_sky_cubes(*obj_cor,sky_cub,bkg,rscale,obj_cor));

  check_nomsg(cpl_table_name_column(*int_obj,"INT","INT_OBJ_ORG"));
  check_nomsg(cpl_table_name_column(*int_obj,"INTC","INT_OBJ_COR"));
  check_nomsg(cpl_table_name_column(*int_obj,"SKYC","INT_SKY_COR"));


 cleanup:
  sinfo_free_table(&rscale);
  sinfo_free_imagelist(&fdata);

  sinfo_free_table(&bkg);
  sinfo_free_table(&lambda);
  sinfo_free_table(&lrange);
  sinfo_free_table(&mrange);
  sinfo_free_table(&grange);
  sinfo_free_table(&lambdas);
  sinfo_free_image(&mask);

  sinfo_free_table(&lr41);
  sinfo_free_table(&lr52);
  sinfo_free_table(&lr63);
  sinfo_free_table(&lr74);
  sinfo_free_table(&lr02);
  sinfo_free_table(&lr85);
  sinfo_free_table(&lr20);
  sinfo_free_table(&lr31);
  sinfo_free_table(&lr42);
  sinfo_free_table(&lr53);
  sinfo_free_table(&lr64);
  sinfo_free_table(&lr75);
  sinfo_free_table(&lr86);
  sinfo_free_table(&lr97);
  sinfo_free_table(&lr00);

  sinfo_free_image(&gpix);
  sinfo_free_image(&ratio);
  sinfo_free_image(&ima_sky);
  //sinfo_free_table(&int_obj);
  sinfo_free_table(&int_sky);

  sinfo_free_imagelist(&obj_cub);
  sinfo_free_imagelist(&sky_cub);

  if (cpl_error_get_code() != CPL_ERROR_NONE) {
    return -1;
  } else {
    return 0;
  }


}

/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_set_ranges
  @memo        defines several wavelength ranges
  @param obj_frm input object frame
  @param int_sky input sky spectra table
  @param obj_cor  output object cube corrected for thermal background
  @return    int 0, -1
  @doc

  Returns in case of succes -1 else.
 */
/*--------------------------------------------------------------------------*/


static int
sinfo_sub_thr_bkg_from_obj_cube(cpl_imagelist* obj_cub,
				cpl_table* int_sky,
				cpl_imagelist** obj_cor)

{
  double* pthr_bkg=NULL;
  int zsz=0;
  int k=0;
  cpl_image* imgo=NULL;

  check_nomsg(pthr_bkg=cpl_table_get_data_double(int_sky,"BKG"));
  check_nomsg(zsz=cpl_imagelist_get_size(obj_cub));
  check_nomsg(*obj_cor=cpl_imagelist_duplicate(obj_cub));

  for(k=0;k<zsz;k++) {
    check_nomsg(imgo=cpl_imagelist_get(obj_cub,k));
    check_nomsg(cpl_image_subtract_scalar(imgo,pthr_bkg[k]));
    check_nomsg(cpl_imagelist_set(*obj_cor,imgo,k));
  }

 cleanup:
  if (cpl_error_get_code() != CPL_ERROR_NONE) {
    return -1;
  } else {
    return 0;
  }

}

/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_set_ranges
  @memo        defines several wavelength ranges
  @param obj_frm input object frame
  @param sky_frm input sky frame
  @param lambda  output wavelength range
  @param lr41    output wavelength range corresponding to transition 4-1
  @param lr52    output wavelength range corresponding to transition 5-2
  @param lr63    output wavelength range corresponding to transition 6-3
  @param lr74    output wavelength range corresponding to transition 7-4
  @param lr02    output wavelength range corresponding to transition 0-2
  @param lr85    output wavelength range corresponding to transition 8-5
  @param lr20    output wavelength range corresponding to transition 2-0
  @param lr31    output wavelength range corresponding to transition 3-1
  @param lr42    output wavelength range corresponding to transition 4-2
  @param lr53    output wavelength range corresponding to transition 5-3
  @param lr64    output wavelength range corresponding to transition 6-4
  @param lr75    output wavelength range corresponding to transition 7-5
  @param lr86    output wavelength range corresponding to transition 8-6
  @param lr97    output wavelength range corresponding to transition 9-7
  @param lr00    output wavelength range corresponding to transition 0-0
  @param lrange  output wavelength range corresponding to ...
  @param grange  output wavelength range corresponding to grange
  @param lambdas output wavelength range corresponding to skyrange
  @param mrange  output wavelength range corresponding to mrange
  @return    int 0, -1
  @doc

  Returns in case of succes -1 else.
 */
/*--------------------------------------------------------------------------*/


int
sinfo_set_ranges(cpl_frame* obj_frm,
                 cpl_frame* sky_frm,
                 cpl_parameterlist* cfg,
                 cpl_table** lambda,
                 cpl_table** lr41,
                 cpl_table** lr52,
                 cpl_table** lr63,
                 cpl_table** lr74,
                 cpl_table** lr02,
                 cpl_table** lr85,
                 cpl_table** lr20,
                 cpl_table** lr31,
                 cpl_table** lr42,
                 cpl_table** lr53,
                 cpl_table** lr64,
                 cpl_table** lr75,
                 cpl_table** lr86,
                 cpl_table** lr97,
                 cpl_table** lr00,
                 cpl_table** lrange,
                 cpl_table** grange,
                 cpl_table** lambdas,
                 cpl_table** mrange,
                 int* sky_interp_sw,
                 double* dispersion)

{

  cpl_propertylist* plist=NULL;
  double crval_obj=0;
  double cdelt_obj=0;
  double crpix_obj=0;
  /*
  int xsize_obj=0;
  int ysize_obj=0;
  */
  int zsize_obj=0;


  double crval_sky=0;
  double cdelt_sky=0;
  double crpix_sky=0;
  /*
  int xsize_sky=0;
  int ysize_sky=0;
  */
  int zsize_sky=0;

  int nrow=0;
  /* wavelength min-max J-H-K band */
  const double w_j_min=1.100;
  const double w_j_max=1.400;
  const double w_h_min=1.445;
  const double w_h_max=1.820;
  const double w_k_min=1.945;
  const double w_k_max=2.460;

  double ws=0;
  double we=0;
  double mean=0;

  cpl_parameter* p=NULL;

  /* wavelength boundaries between line groups corresponding
     to transitions 5-2 to 9-7 */
  double w_bound[NBOUND]={1.067,1.125,1.196,1.252,1.289,
                          1.400,1.472,1.5543,1.6356,1.7253,
                          1.840,1.9570,2.095,2.300};

  cpl_table* tmp_tbl=NULL;
  cpl_table* add1=NULL;



  /* Get Object relevant information */
  cknull_nomsg(plist=cpl_propertylist_load(cpl_frame_get_filename(obj_frm),0));
  check_nomsg(crval_obj=sinfo_pfits_get_crval3(plist));
  check_nomsg(cdelt_obj=sinfo_pfits_get_cdelt3(plist));
  check_nomsg(crpix_obj=sinfo_pfits_get_crpix3(plist));
  //check_nomsg(xsize_obj=sinfo_pfits_get_naxis1(plist));
  //check_nomsg(ysize_obj=sinfo_pfits_get_naxis2(plist));
  check_nomsg(zsize_obj=sinfo_pfits_get_naxis3(plist));

  sinfo_free_propertylist(&plist);
  *dispersion=cdelt_obj;

  /* defines object related wavelength ranges */
  check_nomsg(*lambda=cpl_table_new(zsize_obj));
  cpl_table_new_column(*lambda,"WAVE",CPL_TYPE_DOUBLE);
  cpl_table_new_column(*lambda,"INDEX",CPL_TYPE_DOUBLE);
  check_nomsg(sinfo_table_column_dindgen(lambda,"INDEX"));
  check_nomsg(sinfo_table_column_dindgen(lambda,"WAVE"));

  check_nomsg(cpl_table_add_scalar(*lambda,"WAVE",1.));
  check_nomsg(cpl_table_subtract_scalar(*lambda,"WAVE",crpix_obj));
  check_nomsg(cpl_table_multiply_scalar(*lambda,"WAVE",cdelt_obj));
  check_nomsg(cpl_table_add_scalar(*lambda,"WAVE",crval_obj));




  cknull_nomsg(*lr41=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_j_min,
                                             CPL_LESS_THAN,
                                             w_bound[0]));

  cknull_nomsg(*lr52=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[0],
                                             CPL_LESS_THAN,
                                             w_bound[1]));

  cknull_nomsg(*lr63=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[1],
                                             CPL_LESS_THAN,
                                             w_bound[2]));


  cknull_nomsg(*lr74=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[2],
                                             CPL_LESS_THAN,
                                             w_bound[3]));

 cknull_nomsg(*lr20=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[3],
                                             CPL_LESS_THAN,
                                             w_bound[4]));

 cknull_nomsg(*lr02=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[4],
                                             CPL_LESS_THAN,
                                             w_bound[5]));


 cknull_nomsg(*lr85=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[5],
                                             CPL_LESS_THAN,
                                             w_bound[6]));

  cknull_nomsg(*lr31=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[6],
                                             CPL_LESS_THAN,
                                             w_bound[7]));

  cknull_nomsg(*lr42=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[7],
                                             CPL_LESS_THAN,
                                             w_bound[8]));

  cknull_nomsg(*lr53=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[8],
                                             CPL_LESS_THAN,
                                             w_bound[9]));

  cknull_nomsg(*lr64=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[0],
                                             CPL_LESS_THAN,
                                             w_bound[10]));

  cknull_nomsg(*lr75=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[10],
                                             CPL_LESS_THAN,
                                             w_bound[11]));

  cknull_nomsg(*lr86=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[11],
                                             CPL_LESS_THAN,
                                             w_bound[12]));

  cknull_nomsg(*lr97=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                             CPL_NOT_LESS_THAN,
                                             w_bound[12],
                                             CPL_LESS_THAN,
                                             w_bound[13]));

  cknull_nomsg(*lr00=sinfo_where_tab_min_max(*lambda,
                                             "WAVE",
                                              CPL_NOT_LESS_THAN,
                                              w_bound[13],
                                              CPL_LESS_THAN,
                                              w_k_max));

  /* lrange */
  cknull_nomsg(*lrange=sinfo_where_tab_min_max(*lambda,
                                               "WAVE",
                                               CPL_NOT_LESS_THAN,
                                               w_j_min,
                                               CPL_NOT_GREATER_THAN,
                                               w_j_max));



  cknull_nomsg(add1=sinfo_where_tab_min_max(*lambda,
                                               "WAVE",
                                               CPL_NOT_LESS_THAN,
                                               w_h_min,
                                               CPL_NOT_GREATER_THAN,
                                               w_h_max));

  check_nomsg(nrow=cpl_table_get_nrow(*lrange));
  check_nomsg(cpl_table_insert(*lrange,add1,nrow));
  sinfo_free_table(&add1);

  cknull_nomsg(add1=sinfo_where_tab_min_max(*lambda,
                                               "WAVE",
                                               CPL_NOT_LESS_THAN,
                                               w_k_min,
                                               CPL_NOT_GREATER_THAN,
                                               w_k_max));


  check_nomsg(nrow=cpl_table_get_nrow(*lrange));
  check_nomsg(cpl_table_insert(*lrange,add1,nrow));
  sinfo_free_table(&add1);


  /* mrange */
  cknull_nomsg(*grange=sinfo_where_tab_min_max(*lambda,
                                               "WAVE",
                                               CPL_NOT_LESS_THAN,
                                               1.10,
                                               CPL_NOT_GREATER_THAN,
                                               1.35));




  cknull_nomsg(add1=sinfo_where_tab_min_max(*lambda,
                                               "WAVE",
                                               CPL_NOT_LESS_THAN,
                                               1.5,
                                               CPL_NOT_GREATER_THAN,
                                               1.7));

  check_nomsg(nrow=cpl_table_get_nrow(*grange));
  check_nomsg(cpl_table_insert(*grange,add1,nrow));
  sinfo_free_table(&add1);



  cknull_nomsg(add1=sinfo_where_tab_min_max(*lambda,
                                            "WAVE",
                                            CPL_NOT_LESS_THAN,
                                            2.0,
                                            CPL_NOT_GREATER_THAN,
                                            2.3));

  check_nomsg(nrow=cpl_table_get_nrow(*grange));
  check_nomsg(cpl_table_insert(*grange,add1,nrow));
  sinfo_free_table(&add1);


  /* Get Sky relevant information */
  cknull_nomsg(plist=cpl_propertylist_load(cpl_frame_get_filename(sky_frm),0));
  check_nomsg(crval_sky=sinfo_pfits_get_crval3(plist));
  check_nomsg(cdelt_sky=sinfo_pfits_get_cdelt3(plist));
  check_nomsg(crpix_sky=sinfo_pfits_get_crpix3(plist));
  //check_nomsg(xsize_sky=sinfo_pfits_get_naxis1(plist));
  //check_nomsg(ysize_sky=sinfo_pfits_get_naxis2(plist));
  check_nomsg(zsize_sky=sinfo_pfits_get_naxis3(plist));
  sinfo_free_propertylist(&plist);

  /* defines sky related wavelength ranges */
  check_nomsg(*lambdas=cpl_table_new(zsize_sky));
  cpl_table_new_column(*lambdas,"WAVE",CPL_TYPE_DOUBLE);
  check_nomsg(sinfo_table_column_dindgen(lambdas,"WAVE"));

  check_nomsg(cpl_table_add_scalar(*lambdas,"WAVE",1.));
  check_nomsg(cpl_table_subtract_scalar(*lambdas,"WAVE",crpix_sky));
  check_nomsg(cpl_table_multiply_scalar(*lambdas,"WAVE",cdelt_sky));
  check_nomsg(cpl_table_add_scalar(*lambdas,"WAVE",crval_sky));

  check_nomsg(p=cpl_parameterlist_find(cfg,"sinfoni.sinfo_utl_skycor.mask_ws"));
  check_nomsg(ws=cpl_parameter_get_double(p));
  check_nomsg(p=cpl_parameterlist_find(cfg,"sinfoni.sinfo_utl_skycor.mask_we"));
  check_nomsg(we=cpl_parameter_get_double(p));
  if((ws != SINFO_MASK_WAVE_MIN) || (we != SINFO_MASK_WAVE_MAX)) {
    cknull_nomsg(*mrange=sinfo_where_tab_min_max(*lambda,"WAVE",
                                                 CPL_NOT_LESS_THAN,ws,
                                                 CPL_NOT_GREATER_THAN,we));
   } else {
     check_nomsg(*mrange=cpl_table_duplicate(*lrange));
  }


  check_nomsg(cpl_table_duplicate_column(*lambda,"WDIFF",*lambdas,"WAVE"));
  check_nomsg(cpl_table_subtract_columns(*lambda,"WDIFF","WAVE"));
  check_nomsg(mean=cpl_table_get_column_mean(*lambda,"WDIFF"));
  check_nomsg(nrow=cpl_table_get_nrow(*lambda));
  sinfo_msg_warning("diff %f",nrow*mean);
  if((fabs(nrow*mean) > 0) || (zsize_obj != zsize_sky)) {
    sinfo_msg("We have to interpolate sky frame - this is not good");
    *sky_interp_sw=1;
  }


  return 0;

 cleanup:
  sinfo_free_table(&add1);
  sinfo_free_table(&tmp_tbl);
  sinfo_free_propertylist(&plist);
  return -1;

}

/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_table_dindgen
  @memo        Fill a table column with values from 0 to table_size-1
  @param    t     table to be filled
  @param    label name of table column
  @return    int 0, -1
  @doc

  Returns in case of succes -1 else.
 */
/*--------------------------------------------------------------------------*/

static int
sinfo_table_column_dindgen(cpl_table** t, const char* label)
{

  int sz=0;
  int i=0;

  cknull(*t,"Null input vector");
  check(sz=cpl_table_get_nrow(*t),"Getting size of a vector");
  for(i=0;i<sz;i++) {
    cpl_table_set(*t,label,i,(double)i);
  }

  return 0;
 cleanup:
  return -1;

}

/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_sum_spectra
  @memo        Find the obj and sky spectra by collapsing each plane signal
  @param    obj_frm  object frame
  @param    sky_frm  sky    frame
  @param    mask     mask ?
  @param    int_obj  obj spectrum table
  @param    int_sky  sky spectrum table
  @return    int 0 (if success), -1 (else)
 */
/*--------------------------------------------------------------------------*/


int
sinfo_sum_spectra(const cpl_frame* obj_frm,
                  const cpl_frame* sky_frm,
                  cpl_image* mask,
                  cpl_table* wrange,
                  const int llx,
                  const int lly,
                  const int urx,
                  const int ury,
                  cpl_table** int_obj,
                  cpl_table** int_sky)
{



  cpl_image* obj_slice=NULL;
  cpl_image* sky_slice=NULL;
  cpl_image* gslice=NULL;
  cpl_image* pos_tmp=NULL;
  cpl_image* msk_tmp=NULL;
  cpl_imagelist* obj=NULL;
  cpl_imagelist* sky=NULL;


  cpl_table* loop=NULL;
  cpl_table* opos_tbl=NULL;
  cpl_table* spos_tbl=NULL;
  cpl_table* tmp_tbl=NULL;
  cpl_table* loop_tbl=NULL;

  double med=0;
  double sdv=0;
  double avg=0;

  int zsize=0;
  int i=0;
  int pos_i=0;

  // sum spectra of flagged spaxels

  cknull_nomsg(obj=cpl_imagelist_load(cpl_frame_get_filename(obj_frm),
                                      CPL_TYPE_DOUBLE,0));
  cknull_nomsg(sky=cpl_imagelist_load(cpl_frame_get_filename(sky_frm),
                                      CPL_TYPE_DOUBLE,0));

  check_nomsg(zsize=cpl_imagelist_get_size(obj));
  check_nomsg(*int_obj = cpl_table_new(zsize));
  check_nomsg(*int_sky = cpl_table_new(zsize));
  check_nomsg(cpl_table_duplicate_column(*int_obj,"WAVE",wrange,"WAVE"));
  check_nomsg(cpl_table_duplicate_column(*int_sky,"WAVE",wrange,"WAVE"));
  check_nomsg(cpl_table_new_column(*int_obj,"INT",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(*int_sky,"INT",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_fill_column_window_double(*int_obj,"INT",0,zsize,0));
  check_nomsg(cpl_table_fill_column_window_double(*int_sky,"INT",0,zsize,0));

  //loop = where(mask > 0.5);
  //TO BE REMOVED: loop_tbl is not used
  cknull_nomsg(loop_tbl=sinfo_image2table(mask));
  check_nomsg(cpl_table_and_selected_double(loop_tbl,"VALUE",
                                            CPL_GREATER_THAN,0.5));
  check_nomsg(loop=cpl_table_extract_selected(loop_tbl));
  sinfo_free_table(&loop_tbl);
  sinfo_free_table(&loop);

  //Determines object spectrum
  for (i=0;i<zsize;i++) {
    check_nomsg(obj_slice = cpl_imagelist_get(obj,i));

    //pos = where(mask > 0.5 && finite(obj_slice),pos_i);
    pos_i=sinfo_cnt_mask_thresh_and_obj_finite(mask,0.5,obj_slice);
    if (pos_i >= 1) {
      if ((pos_i) < 3 ) {
    //int_obj[i] = mean(obj_slice[pos]);
        //TODO here obj_slice should be considered only on pos:
        //     one should do a selection/thresholding
        check_nomsg(cpl_table_set_double(*int_obj,"INT",i,
					 cpl_image_get_mean_window(obj_slice,
								   llx,lly,
								   urx,ury)));
      } else {
        // select only poisitions where mask>0.5 and obj is finite
    // gslice = obj_slice[pos];
        //sinfo_msg("obj pos_i=%d",pos_i);

        check_nomsg(gslice = cpl_image_duplicate(obj_slice));
        check_nomsg(sinfo_image_flag_nan(&gslice));
    /*
        sinfo_msg("obj: min=%f max=%f",
                  cpl_image_get_min(obj_slice),
          cpl_image_get_max(obj_slice));
    */
        //check_nomsg(cpl_image_threshold(gslice,SINFO_DBL_MIN,3.0e6,1,0));
        //check_nomsg(cpl_image_multiply(gslice,mask));
        if(cpl_image_count_rejected(gslice) < 2048) { //2048=64*64/2

	  check_nomsg(med = cpl_image_get_median_window(gslice,llx,lly,urx,ury));
	  check_nomsg(sdv = cpl_image_get_stdev_window(gslice,llx,lly,urx,ury));
	  //sinfo_msg("med=%f sdv=%f",med,sdv);
	  //avg = mean(gslice[where(gslice < med+3*sdv && gslice > med-3*sdv)]);
	  check_nomsg(cpl_image_threshold(gslice,med-3*sdv,med+3*sdv,0,0));
	  check_nomsg(avg= cpl_image_get_mean_window(gslice,llx,lly,urx,ury));
	  check_nomsg(cpl_table_set_double(*int_obj,"INT",i,avg));
	} else {
	  check_nomsg(cpl_table_set_invalid(*int_obj,"INT",i));
	}

        sinfo_free_image(&gslice);
        //sinfo_msg("sky int=%f",avg);
      }
    }

    //Determines sky spectrum
    check_nomsg(sky_slice = cpl_imagelist_get(sky,i));
    //pos = where(mask > 0.5 and finite(sky_slice),pos_i);
    pos_i=sinfo_cnt_mask_thresh_and_obj_finite(mask,0.5,sky_slice);
    if (pos_i >= 1) {
      if ((pos_i) < 3) {
    //int_obj[i] = mean(obj_slice[pos]);
        //TODO here obj_slice should be considered only on pos:
        //     one should do a selection/thresholding
        check_nomsg(cpl_table_set_double(*int_sky,"INT",i,
					 cpl_image_get_mean_window(sky_slice,
								   llx,lly,
								   urx,ury)));
      } else {
        //sinfo_msg("pos_i=%d",pos_i);
        // select only poisitions where mask>0.5 and obj is finite
    // gslice = obj_slice[pos];
        check_nomsg(gslice = cpl_image_duplicate(sky_slice));
        check_nomsg(sinfo_image_flag_nan(&gslice));
        //check_nomsg(cpl_image_threshold(gslice,SINFO_DBL_MIN,3.0e6,1,0));
        //check_nomsg(cpl_image_multiply(gslice,mask));
        if(cpl_image_count_rejected(gslice) < 2048) { //2048=64*64/2

	check_nomsg(med = cpl_image_get_median_window(gslice,llx,lly,urx,ury));
	check_nomsg(sdv = cpl_image_get_stdev_window(gslice,llx,lly,urx,ury));
        //avg = mean(gslice[where(gslice < med+3*sdv && gslice > med-3*sdv)]);
        check_nomsg(cpl_image_threshold(gslice,med-3*sdv,med+3*sdv,0,0));
	check_nomsg(avg= cpl_image_get_mean_window(gslice,llx,lly,urx,ury));
        check_nomsg(cpl_table_set_double(*int_sky,"INT",i,avg));
	} else {
	  check_nomsg(cpl_table_set_invalid(*int_sky,"INT",i));
	}
        sinfo_free_image(&gslice);
    /*
        if(i<100) {
           sinfo_msg("sky: wave=%f int=%f",
                      cpl_table_get_double(*int_sky,"WAVE",i,&status),avg);

    }
    */
      }
    }
  }

  sinfo_free_imagelist(&obj);
  sinfo_free_imagelist(&sky);


  return 0;

 cleanup:
  sinfo_free_image(&gslice);
  sinfo_free_image(&pos_tmp);
  sinfo_free_image(&msk_tmp);
  sinfo_free_table(&tmp_tbl);
  sinfo_free_table(&opos_tbl);
  sinfo_free_table(&spos_tbl);
  sinfo_free_table(&loop_tbl);
  sinfo_free_table(&loop);
  sinfo_free_imagelist(&obj);
  sinfo_free_imagelist(&sky);

  return -1;
}





/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_cnt_mask_thresh_and_obj_finite
  @memo        count pixel values satisfying conditions
  @param    mask input mask frame
  @param    t    input mask min threshold
  @param    obj  input mask frame
  @return    No of points satisfying condition or -1
  @doc

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


static int
sinfo_cnt_mask_thresh_and_obj_finite(const cpl_image* mask,
                                     const double t,
                                     const cpl_image* obj)
{

  int cnt=0;
  int sxm=0;
  int sym=0;
  int sxo=0;
  int syo=0;
  int i=0;
  const double* pm=NULL;
  const double* po=NULL;

  check_nomsg(sxm=cpl_image_get_size_x(mask));
  check_nomsg(sym=cpl_image_get_size_y(mask));
  check_nomsg(sxo=cpl_image_get_size_x(obj));
  check_nomsg(syo=cpl_image_get_size_y(obj));
  if( sxm != sxo || sym != syo) {
    goto cleanup;
  }
  check_nomsg(pm=cpl_image_get_data_double_const(mask));
  check_nomsg(po=cpl_image_get_data_double_const(obj));

  for(i=0;i<sxm*sym;i++) {

    if( (pm[i] > t) && (!irplib_isnan(po[i]))) { cnt++; }

  }

  return cnt;
 cleanup:
  return -1;

}





/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_flag_nan
  @memo        reject as bad pix image NANs
  @param    im input image frame
  @return    if success
                   The number of bad bixels
                else
                   -1

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


static int
sinfo_image_flag_nan(cpl_image** im)
{

  int cnt=0;
  int sx=0;
  int sy=0;
  int i=0;
  int j=0;

  double* pi=NULL;

  check_nomsg(sx=cpl_image_get_size_x(*im));
  check_nomsg(sy=cpl_image_get_size_y(*im));
  check_nomsg(pi=cpl_image_get_data_double(*im));

  for(j=0;j<sy;j++) {
    for(i=0;i<sx;i++) {
     if(irplib_isnan(pi[j*sx+i])) {
    check_nomsg(cpl_image_reject(*im,i+1,j+1));
    cnt++;
      }
    }
  }
  //sinfo_msg("No bad pixels: %d",cnt);
  return cnt;
 cleanup:
  return -1;

}



/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_object_estimate_noise
  @memo        Estimate object noise
  @param    obj_frm input object frame
  @param    centre  output centre of object's intensity histogram
  @param    noise   output noise
  @return    int
                    if success: 0
                    else -1

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

int
sinfo_object_estimate_noise(cpl_frame* obj_frm,
                            const int obj_noise_fit,
                            double* centre,
                            double* noise)
{

  const int nbins=HISTO_NBINS;

  int xsz=0;
  int ysz=0;
  int zsz=0;
  int n=0;
  int i=0;
  int k=0;
  int r=0;

  //int max_h=0;
  int min_x=0;
  int max_x=0;
  int status=0;
  int max_pos=0;
  int min_pos=0;
  int min_xi_sz=0;
  int ndist=0;

  double avg_d=0;
  double std_d=0;
  double hmin=0;
  double hmax=0;
  double kappa=3;

  double* pdata=NULL;
  double* disth=NULL;
  double* distx=NULL;

  double peak=0;
  double tempc=0;
  //double value=0;
  double thres=0;
  double val=0;
  double x0=0;
  double sigma=0;
  double area=0;
  double offset=0;
  //double mse=0;
  //double chired=0;

  cpl_propertylist* plist=NULL;
  cpl_imagelist* obj_cub=NULL;
  cpl_table* data_tbl=NULL;
  cpl_table* histo=NULL;
  cpl_image* img=NULL;
  cpl_table* dist=NULL;
  cpl_table* min_xi=NULL;
  cpl_table* tmp_tbl1=NULL;
  cpl_table* tmp_tbl2=NULL;
  cpl_vector* vx=NULL;
  cpl_vector* vy=NULL;
  cpl_vector* sx=NULL;
  cpl_vector* sy=NULL;
  int counter=0;

  // Get Object relevant information
  cknull_nomsg(plist=cpl_propertylist_load(cpl_frame_get_filename(obj_frm),0));
  check_nomsg(xsz=sinfo_pfits_get_naxis1(plist));
  check_nomsg(ysz=sinfo_pfits_get_naxis2(plist));
  check_nomsg(zsz=sinfo_pfits_get_naxis3(plist));
  sinfo_free_propertylist(&plist);

  cknull_nomsg(obj_cub=cpl_imagelist_load(cpl_frame_get_filename(obj_frm),
                                          CPL_TYPE_DOUBLE,0));

  n=xsz*ysz*zsz;
  check_nomsg(data_tbl=cpl_table_new(n));
  check_nomsg(cpl_table_new_column(data_tbl,"DATA",CPL_TYPE_DOUBLE));


  for(k=0;k<zsz;k++) {
    check_nomsg(img=cpl_imagelist_get(obj_cub,k));
    check_nomsg(pdata=cpl_image_get_data(img));
    for(i=0;i<xsz*ysz;i++) {
      if(!irplib_isnan(pdata[i])) {
    cpl_table_set_double(data_tbl,"DATA",r,pdata[i]);
        r++;
      }
    }
  }
  sinfo_free_imagelist(&obj_cub);

  check_nomsg(cpl_table_erase_invalid(data_tbl));
  check_nomsg(avg_d=cpl_table_get_column_mean(data_tbl,"DATA"));
  check_nomsg(std_d=cpl_table_get_column_stdev(data_tbl,"DATA"));

  //cpl_table_save(data_tbl, NULL, NULL, "out_data.fits",CPL_IO_DEFAULT);
  hmin=avg_d-kappa*std_d;
  hmax=avg_d+kappa*std_d;
  //sinfo_msg("mean=%f stdv=%f",avg_d,std_d);
  //sinfo_msg("hmin=%f hmax=%f",hmin,hmax);
  sinfo_msg("Computes histogram");
  ck0(sinfo_histogram(data_tbl,nbins,hmin,hmax,&histo),"building histogram");

  //value=(double)(hmax-hmin)/nbins/2.;
  //sinfo_msg("value=%10.8f",value);


  while(min_xi_sz < HISTO_MIN_SIZE && counter < 10) {
    counter++;
    //check_nomsg(max_h=cpl_table_get_column_max(histo,"HY"));
    //cpl_table_save(histo, NULL, NULL, "out_pippo.fits", CPL_IO_DEFAULT);
    check_nomsg(max_pos=sinfo_table_get_index_of_max(histo,"HY",CPL_TYPE_INT));
    //sinfo_msg("max_pos=%d",max_pos);

    /*
    check_nomsg(max_pos=sinfo_extract_table_rows(histo,"HY",
                                                 CPL_EQUAL_TO,max_h));
    sinfo_msg("size max_pos %d",cpl_table_get_nrow(max_pos));
    sinfo_msg("value max_pos %d",cpl_table_get_int(max_pos,"HY",0,&status));
    */
    min_x=max_pos-1;
    max_x=max_pos+2;
    //sinfo_msg("min_x=%d max_x=%d",min_x,max_x);

    sinfo_free_table(&tmp_tbl1);
    //sinfo_msg("x selection threshold: %f %d",
    //          cpl_table_get(histo,"HL",max_pos,&status),max_pos);
    check_nomsg(tmp_tbl1=sinfo_extract_table_rows(histo,"HL",
                                                  CPL_LESS_THAN,
                                 cpl_table_get(histo,"HL",max_pos,&status)));
    thres=cpl_table_get_column_max(tmp_tbl1,"HY")/HISTO_Y_CUT;
    //sinfo_msg("threshold=%f",thres);


    sinfo_free_table(&min_xi);
    check_nomsg(min_xi=sinfo_extract_table_rows(tmp_tbl1,"HY",
                                                CPL_GREATER_THAN,thres));

    //cpl_table_save(min_xi, NULL, NULL, "out_min_xi.fits", CPL_IO_DEFAULT);



    min_xi_sz=cpl_table_get_nrow(min_xi);
    val=cpl_table_get(min_xi,"HL",0,&status);

    check_nomsg(min_pos=sinfo_table_get_index_of_val(histo,"HL",val,
                                                     CPL_TYPE_DOUBLE));
    //sinfo_msg("min_pos=%d max_pos=%d max(h)=%d min_xi_sz=%d x[maxpos[0]]=%f",
    //           min_pos,   max_pos,   max_h,    min_xi_sz, val);



    if (min_xi_sz > 0) {
      min_x = min_pos-HISTO_X_LEFT_CUT*(max_pos-min_pos);
      max_x = max_pos+HISTO_X_RIGHT_CUT*(max_pos-min_pos);
    }

    //sinfo_msg("min_x=%d max_x=%d",min_x,max_x);
    check_nomsg(hmin=sinfo_table_column_interpolate(histo,"HL",min_x));
    check_nomsg(hmax=sinfo_table_column_interpolate(histo,"HL",max_x));
    //sinfo_msg("hmin=%f hmax=%f min_xi_sz=%d",hmin,hmax,min_xi_sz);
    //cpl_table_save(histo, NULL, NULL, "out_histo.fits", CPL_IO_DEFAULT);
    sinfo_free_table(&histo);
    ck0(sinfo_histogram(data_tbl,nbins,hmin,hmax,&histo),"building histogram");
    //cpl_table_save(histo, NULL, NULL, "out_histo1.fits", CPL_IO_DEFAULT);
    check_nomsg(cpl_table_add_scalar(histo,"HL",(hmax-hmin)/nbins/2));
    //cpl_table_save(histo, NULL, NULL, "out_histo2.fits", CPL_IO_DEFAULT);



  }
  sinfo_free_table(&data_tbl);
  sinfo_free_table(&min_xi);

  //cpl_table_save(histo, NULL, NULL, "out_histo.fits", CPL_IO_DEFAULT);

  check_nomsg(peak=cpl_table_get_column_max(histo,"HY"));
  //sinfo_msg("peak=%f",peak);
  sinfo_free_table(&tmp_tbl1);

  check_nomsg(tmp_tbl1=sinfo_extract_table_rows(histo,"HY",CPL_EQUAL_TO,peak));

  //cpl_table_save(tmp_tbl1, NULL, NULL, "out_tmp_tbl1.fits", CPL_IO_DEFAULT);


  check_nomsg(*centre=cpl_table_get_column_mean(tmp_tbl1,"HL"));
  //sinfo_msg("Background level=%f",*centre);

  sinfo_free_table(&tmp_tbl1);
  check_nomsg(tmp_tbl1=sinfo_extract_table_rows(histo,"HY",
                                                CPL_GREATER_THAN,
                                                peak/HISTO_Y_CUT));
  sinfo_free_table(&tmp_tbl2);
  check_nomsg(tmp_tbl2=sinfo_extract_table_rows(tmp_tbl1,"HY",
                                                CPL_LESS_THAN,peak));
  sinfo_free_table(&tmp_tbl1);

  check_nomsg(tempc=*centre-cpl_table_get_column_min(tmp_tbl2,"HL"));
  //sinfo_msg("min HX %f",cpl_table_get_column_min(tmp_tbl2,"HL"));
  sinfo_free_table(&tmp_tbl2);
  //sinfo_msg("Tempc=%f",tempc);
  check_nomsg(dist=sinfo_where_tab_min_max(histo,"HL",
                 CPL_GREATER_THAN,*centre-HISTO_DIST_TEMPC_MIN_FCT*tempc,
                 CPL_NOT_GREATER_THAN,*centre+HISTO_DIST_TEMPC_MAX_FCT*tempc));

  offset=cpl_table_get_column_min(histo,"HY");
  sinfo_free_table(&histo);


  check_nomsg(ndist=cpl_table_get_nrow(dist));
  check_nomsg(cpl_table_cast_column(dist,"HY","HYdouble",CPL_TYPE_DOUBLE));
  check_nomsg(disth=cpl_table_get_data_double(dist,"HYdouble"));
  check_nomsg(distx=cpl_table_get_data_double(dist,"HL"));
  //cpl_table_save(dist, NULL, NULL, "out_dist.fits", CPL_IO_DEFAULT);

  //TODO
  //gaussfit(distx,disty,dista,nterms=3);
  //*noise=dista[2];
  *noise=tempc/2;
  /* THIS DOES NOT WORK */
  //sinfo_msg("FWHM/2=%f",*noise);

  if(obj_noise_fit == 1) {
    check_nomsg(vy=cpl_vector_wrap(ndist,disth));
    check_nomsg(vx=cpl_vector_wrap(ndist,distx));
    check_nomsg(sx=cpl_vector_new(ndist));
    check_nomsg(cpl_vector_fill(sx,1.));
    check_nomsg(sy=cpl_vector_duplicate(sx));
    x0=*centre;
    sigma=tempc/2;

    check_nomsg(cpl_vector_fit_gaussian(vx,NULL,
                                        vy,NULL,
                                        CPL_FIT_ALL,
                                        &x0,&sigma,&area,&offset,
                                        NULL,NULL,NULL));
    //sinfo_msg("Gauss fit parameters:"
    //          "x0=%f sigma=%f area=%f offset=%f mse=%f chired=%f",
    //           x0,sigma,area,offset,mse,chired);
    //sinfo_msg("Background level=%f",*centre);
    //sinfo_msg("Noise=%f",sigma);
    *noise=sigma;
    sinfo_unwrap_vector(&vx);
    sinfo_unwrap_vector(&vy);
    sinfo_free_my_vector(&sx);
    sinfo_free_my_vector(&sy);
  }
  sinfo_free_table(&dist);
  //*noise=18.7448;
  //*noise=20.585946;
  return 0;

 cleanup:
  sinfo_free_imagelist(&obj_cub);
  sinfo_free_propertylist(&plist);
  sinfo_free_table(&min_xi);
  sinfo_free_table(&tmp_tbl1);
  sinfo_free_table(&tmp_tbl2);
  sinfo_free_table(&histo);
  sinfo_free_table(&dist);
  sinfo_free_table(&data_tbl);
  sinfo_free_my_vector(&sx);
  sinfo_free_my_vector(&sy);
  sinfo_unwrap_vector(&vx);
  sinfo_unwrap_vector(&vy);

  return -1;

}


/**
@name sinfo_where_tab_min_max
@brief finds table rows comprised between a min and a max
@param t   input table
@param col column involved in selection
@param op1 first operation of selection
@param v1  first threshold value
@param op2 second operation of selection
@param v2  second threshold value
@return new allocated sub table satisfying conditions
*/

cpl_table*
sinfo_where_tab_min_max(cpl_table* t,
                        const char* col,
                        cpl_table_select_operator op1,
                        const double v1,
                        cpl_table_select_operator op2,
                        const double v2)
{

  cpl_table* res=NULL;
  cpl_table* tmp=NULL;

  check_nomsg(cpl_table_and_selected_double(t,col,op1,v1));
  check_nomsg(tmp=cpl_table_extract_selected(t));
  check_nomsg(cpl_table_and_selected_double(tmp,col,op2,v2));
  check_nomsg(res=cpl_table_extract_selected(tmp));
  check_nomsg(cpl_table_select_all(t));
  sinfo_free_table(&tmp);

  return res;

 cleanup:
  sinfo_free_table(&tmp);
  sinfo_free_table(&res);

  return NULL;

}
/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_histogram
  @memo        computes histogram
  @param data  input data table
  @param nbins input size of histogram bin
  @param min   input min data to be considered in histogram
  @param max   input max data to be considered in histogram
  @param hist  output histogram: table with results:
       # H: histogram
       # X: array of data values corresponding to the center of each bin
       # Xmean: 1D array corresponding to the mean of the data values entering
                each histogram bin
  Returns in case of succes -1 else.
  @doc

       Compute the histogram with the IDL intrinsic function HISTOGRAM, using
       the input options specified by the parameters Dmin, Dmax, Bin. All
       the computations are performed in floating-point arithmetics.
       Then compute arrays of values corresponding to each histogram bin,
       useful for plots, fitting, etc.


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

int
sinfo_histogram(const cpl_table* data,
                const int nbins,
                const double min,
                const double max,
                cpl_table** histo)
{
  cpl_table* tmp_tbl1=NULL;
  cpl_table* tmp_tbl2=NULL;
  cpl_table* dat=NULL;
  int ntot=0;
  int i=0;
  int* phy=NULL;
  double* pdt=NULL;
  /* double* phx=NULL; */

  double vtmp=0;
  double vstp=0;
  double vmax=0;
  double vmin=0;

  int h=0;
  check_nomsg(dat=cpl_table_duplicate(data));
  check_nomsg(cpl_table_cast_column(dat,"DATA","DDATA",CPL_TYPE_DOUBLE));
  /*
  sinfo_msg("min=%f max=%f",
            cpl_table_get_column_min(dat,"DDATA"),
            cpl_table_get_column_max(dat,"DDATA"));
  */
  check_nomsg(cpl_table_and_selected_double(dat,"DDATA",
                                            CPL_NOT_GREATER_THAN,max));
  /*
  check_nomsg(cpl_table_and_selected_double(dat,"DDATA",CPL_LESS_THAN,max));
  */
  check_nomsg(tmp_tbl1=cpl_table_extract_selected(dat));
  /*
  sinfo_msg("min=%f max=%f",
             cpl_table_get_column_min(tmp_tbl1,"DDATA"),
             cpl_table_get_column_max(tmp_tbl1,"DDATA"));
  */
  /*
  check_nomsg(cpl_table_and_selected_double(tmp_tbl1,"DDATA",
                                            CPL_NOT_LESS_THAN,min));
  */
  check_nomsg(cpl_table_and_selected_double(tmp_tbl1,"DDATA",
                                            CPL_GREATER_THAN,min));
  check_nomsg(tmp_tbl2=cpl_table_extract_selected(tmp_tbl1));
  /*
  sinfo_msg("min=%f max=%f",
             cpl_table_get_column_min(tmp_tbl2,"DDATA"),
             cpl_table_get_column_max(tmp_tbl2,"DDATA"));
  */
  sinfo_free_table(&tmp_tbl1);
  /*
  check_nomsg(tmp_tbl1=sinfo_extract_table_rows(dat,"DDATA",
                                                CPL_NOT_GREATER_THAN,max));
  check_nomsg(tmp_tbl2=sinfo_extract_table_rows(tmp_tbl1,"DDATA",
                                                CPL_NOT_LESS_THAN,min));
  */

  check_nomsg(ntot=cpl_table_get_nrow(tmp_tbl2));
  /* not necessry to sort:
    check_nomsg(sinfo_sort_table_1(tmp_tbl2,"DDATA",FALSE));*/
  check_nomsg(vmin=cpl_table_get_column_min(tmp_tbl2,"DDATA"));
  check_nomsg(vmax=cpl_table_get_column_max(tmp_tbl2,"DDATA"));
  vstp=(vmax-vmin)/(nbins-1);
  /* sinfo_msg("vmin=%f vmax=%f step=%f",vmin,vmax,vstp); */
  check_nomsg(*histo=cpl_table_new(nbins));
  check_nomsg(cpl_table_new_column(*histo,"HX",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(*histo,"HL",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(*histo,"HY",CPL_TYPE_INT));

  /*check_nomsg(cpl_table_fill_column_window(*histo,"HX",0,nbins,0)); */
  check_nomsg(cpl_table_fill_column_window(*histo,"HL",0,nbins,0));
  check_nomsg(cpl_table_fill_column_window(*histo,"HY",0,nbins,0));

  check_nomsg(phy=cpl_table_get_data_int(*histo,"HY"));
  /*check_nomsg(phx=cpl_table_get_data_double(*histo,"HX")); */
  check_nomsg(pdt=cpl_table_get_data_double(dat,"DATA"));

  for(i=0;i<nbins;i++) {
    cpl_table_set_double(*histo,"HX",i,(double)i);
    vtmp=vmin+i*vstp;
    cpl_table_set_double(*histo,"HL",i,vtmp);
  }
  h=0;

  for(i=0;i<ntot;i++) {
    h=floor((pdt[i]-vmin)/vstp);
    if((h<nbins) && (h>-1)) {
      phy[h]++;
    }
  }
  //cpl_table_save(*histo, NULL, NULL, "out_histo_p5.fits", CPL_IO_DEFAULT);

  sinfo_free_table(&tmp_tbl2);
  sinfo_free_table(&dat);


  return 0;
 cleanup:
  sinfo_free_table(&tmp_tbl1);
  sinfo_free_table(&tmp_tbl2);
  sinfo_free_table(&dat);

  return -1;

}


/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_object_flag_low_values
  @memo        flags low values in object frame: val>=cnt+2*sig
  @param obj_frm  input object frame
  @param cnt      input value involved in thresold
  @param sig      input value involved in threshold
  @param flag_data output imagelist
  @return 0 in case of succes -1 else.
 */
/*--------------------------------------------------------------------------*/

int
sinfo_object_flag_low_values(cpl_frame* obj_frm,
                             const double cnt,
                             const double sig,
                             cpl_imagelist** flag_data)
{

  int xsz=0;
  int ysz=0;
  int zsz=0;
  int n=0;
  int i=0;
  int k=0;
  int r=0;

  cpl_propertylist* plist=NULL;
  cpl_table* data_tbl=NULL;
  cpl_table* flag_tbl=NULL;
  cpl_image* img=NULL;
  cpl_imagelist* obj_cub=NULL;

  double* pdata=NULL;
  double* pflag=NULL;

 /* Get Object relevant information */
  cknull_nomsg(plist=cpl_propertylist_load(cpl_frame_get_filename(obj_frm),0));
  check_nomsg(xsz=sinfo_pfits_get_naxis1(plist));
  check_nomsg(ysz=sinfo_pfits_get_naxis2(plist));
  check_nomsg(zsz=sinfo_pfits_get_naxis3(plist));
  sinfo_free_propertylist(&plist);

  cknull_nomsg(obj_cub=cpl_imagelist_load(cpl_frame_get_filename(obj_frm),
                                          CPL_TYPE_DOUBLE,0));

  n=xsz*ysz*zsz;
  check_nomsg(data_tbl=cpl_table_new(n));
  check_nomsg(cpl_table_new_column(data_tbl,"DATA",CPL_TYPE_DOUBLE));

  for(k=0;k<zsz;k++) {
    check_nomsg(img=cpl_imagelist_get(obj_cub,k));
    check_nomsg(pdata=cpl_image_get_data_double(img));
    for(i=0;i<xsz*ysz;i++) {
      if(!irplib_isnan(pdata[i])) {
    check_nomsg(cpl_table_set_double(data_tbl,"DATA",r,pdata[i]));
        r++;
      }
    }
  }

  check_nomsg(cpl_table_erase_invalid(data_tbl));
  //sinfo_msg("Background level: %f Noise: %f",cnt,sig);
  check_nomsg(cpl_table_and_selected_double(data_tbl,"DATA",
                                           CPL_LESS_THAN,cnt+2*sig));
  check_nomsg(flag_tbl=cpl_table_extract_selected(data_tbl));
  sinfo_free_table(&data_tbl);
  //check_nomsg(cpl_table_save(flag_tbl,NULL,NULL,
  //                             "out_flag.fits",CPL_IO_DEFAULT));
  sinfo_free_table(&flag_tbl);

  check_nomsg(*flag_data=cpl_imagelist_new());
  for(i=0;i<zsz;i++) {
    check_nomsg(img=cpl_image_new(xsz,ysz,CPL_TYPE_DOUBLE));
    check_nomsg(cpl_image_add_scalar(img,0));
    check_nomsg(cpl_imagelist_set(*flag_data,cpl_image_duplicate(img),i));
    sinfo_free_image(&img);
  }
  for(k=0;k<zsz;k++) {
    check_nomsg(img=cpl_imagelist_get(*flag_data,k));
    pflag=cpl_image_get_data_double(cpl_imagelist_get(*flag_data,k));
    pdata=cpl_image_get_data_double(cpl_imagelist_get(obj_cub,k));
    for(i=0;i<xsz*ysz;i++) {
      if((!irplib_isnan(pdata[i])) && pdata[i] < (cnt+2*sig)) {
        pflag[i]=1;
      }
    }
  }

  sinfo_free_imagelist(&obj_cub);




  return 0;

 cleanup:
  sinfo_free_propertylist(&plist);
  sinfo_free_imagelist(&obj_cub);
  sinfo_free_table(&data_tbl);
  sinfo_free_table(&flag_tbl);

  return -1;
}

/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_object_flag_sky_pixels
  @memo        flags sky pixels in cube object
  @param obj_frm  input object frame
  @param lambda   input wavelength range table
  @param mrange   input wavelength range table
  @param flag_data input imagelist with flagged data
  @param tol      input tolerance
  @param g_img    output image with good pixels
  @param r_img    output image with ratio good/all pixels
  @param image    output sky image
  @return 0 in case of succes, -1 else.
 */
/*--------------------------------------------------------------------------*/


int
sinfo_object_flag_sky_pixels(cpl_frame* obj_frm,
                             cpl_table* lambda,
                             cpl_table* mrange,
                 cpl_imagelist* flag_data,
                             const double tol,
                             cpl_image** g_img,
                             cpl_image** r_img,
                             cpl_image** image)
{

  int xsz=0;
  int ysz=0;
  //int zsz=0;
  int i=0;
  int j=0;
  int gpix_i=0;
  double tot=0;
  double all_pix=0;
  double flag_pix=0;
  double ratio=0;

  double* pr_img=NULL;
  double* pg_img=NULL;
  double* pimage=NULL;
  cpl_propertylist* plist=NULL;
  cpl_imagelist* osel=NULL;
  cpl_imagelist* fsel=NULL;
  cpl_table* gpix=NULL;
  cpl_table* gspec=NULL;
  cpl_table* fspec=NULL;
  cpl_table* ospec=NULL;

  cpl_imagelist* obj_cub=NULL;

  /* Get Object relevant information */
  cknull_nomsg(plist=cpl_propertylist_load(cpl_frame_get_filename(obj_frm),0));

  check_nomsg(xsz=sinfo_pfits_get_naxis1(plist));
  check_nomsg(ysz=sinfo_pfits_get_naxis2(plist));
  //check_nomsg(zsz=sinfo_pfits_get_naxis3(plist));
  sinfo_free_propertylist(&plist);
  cknull_nomsg(obj_cub=cpl_imagelist_load(cpl_frame_get_filename(obj_frm),
                                                      CPL_TYPE_DOUBLE,0));

  /* Flag sky pixels in data cube */
  /* create images */
  check_nomsg(*r_img=cpl_image_new(xsz,ysz,CPL_TYPE_DOUBLE));
  check_nomsg(*g_img=cpl_image_new(xsz,ysz,CPL_TYPE_DOUBLE));
  check_nomsg(*image=cpl_image_new(xsz,ysz,CPL_TYPE_DOUBLE));

  cknull_nomsg(pr_img=cpl_image_get_data_double(*r_img));
  cknull_nomsg(pg_img=cpl_image_get_data_double(*g_img));
  cknull_nomsg(pimage=cpl_image_get_data_double(*image));

  /* TODO */
  // fill image points:
  // g_img: mask with at least half good pixels along spectral range
  // r_img: mask with ratio of good pixels along spectral range
  // image: image with mean of spectrum over good pixels

  //check_nomsg(cpl_table_save(lambda, NULL, NULL,
  //                             "out_lambda.fits", CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(mrange, NULL, NULL,
  //                             "out_mrange.fits", CPL_IO_DEFAULT));

  cknull_nomsg(osel=sinfo_imagelist_select_range(obj_cub,lambda,
                                                      mrange,tol));

  sinfo_free_imagelist(&obj_cub);

  cknull_nomsg(fsel=sinfo_imagelist_select_range(flag_data,lambda,
                                                      mrange,tol));

  //check_nomsg(cpl_imagelist_save(osel,"out_osel.fits",
  //                               CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT));
  //check_nomsg(cpl_imagelist_save(fsel,"out_fsel.fits",
  //                               CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT));

  for(j=0;j<ysz;j++) {
    for(i=0;i<xsz;i++) {
      // consider only planes in the proper wavelegth ranges
      cknull_nomsg(ospec=sinfo_slice_z(osel,i,j));
      cknull_nomsg(fspec=sinfo_slice_z(fsel,i,j));
      // consider only finite pixels
      check_nomsg(gpix_i=sinfo_table_extract_finite(ospec,fspec,&gpix,&gspec));
      //sinfo_msg("gpix_i=%d",gpix_i);
      if(gpix_i > 0) {
        // build two arrays of proper size
        all_pix=(double)gpix_i;
    /*
        sinfo_msg("flagspec: min=%f max=%f",
                  cpl_table_get_column_min(fspec,"VALUE"),
                  cpl_table_get_column_max(fspec,"VALUE"));
        sinfo_msg("good flagspec: min=%f max=%f",
                  cpl_table_get_column_min(gspec,"VALUE"),
                  cpl_table_get_column_max(gspec,"VALUE"));
        sinfo_msg("nfspec=%d",cpl_table_get_nrow(fspec));
        check_nomsg(cpl_table_save(fspec, NULL, NULL,
                    "out_fspec.fits",CPL_IO_DEFAULT));
        check_nomsg(cpl_table_save(gspec, NULL, NULL,
                    "out_gspec.fits", CPL_IO_DEFAULT));
    */
        //check_nomsg(flag_pix=cpl_table_and_selected_double(fspec,"VALUE",
        //                                              CPL_GREATER_THAN,0.5));
        //sinfo_msg("all_pix=%f flag_pix=%f",all_pix,flag_pix);

        check_nomsg(flag_pix=cpl_table_and_selected_double(gspec,"VALUE",
                                                        CPL_GREATER_THAN,0.5));
        //sinfo_msg("all_pix=%f flag_pix=%f",all_pix,flag_pix);
        // flag_pix = float(n_elements(where(fspec[gpix] > 0.5)));
        // compute the ratio between the two arrays
        ratio=flag_pix/all_pix;
        // considers only pixels with have at least half good pixels
        if(all_pix > cpl_table_get_nrow(mrange)/2) {
          pg_img[i+j*xsz]=1;
          pr_img[i+j*xsz]=ratio;
        }
        //mean(ospec(gpix))
        check_nomsg(pimage[i+j*xsz]=cpl_table_get_column_mean(gpix,"VALUE"));
        //sinfo_msg("ix=%d iy=%d r=%f",i,j,ratio);
      }
      sinfo_free_table(&ospec);
      sinfo_free_table(&fspec);
      sinfo_free_table(&gpix);
      sinfo_free_table(&gspec);

    } /* end for over i */
  } /* end for over j */
  sinfo_free_imagelist(&osel);
  sinfo_free_imagelist(&fsel);

  /*
  cpl_image_save(*r_img,"out_r_img.fits",CPL_BPP_IEEE_FLOAT,
                 NULL,CPL_IO_DEFAULT);
  cpl_image_save(*g_img,"out_g_img.fits",CPL_BPP_IEEE_FLOAT,
                 NULL,CPL_IO_DEFAULT);
  cpl_image_save(*image,"out_image.fits",CPL_BPP_IEEE_FLOAT,
                 NULL,CPL_IO_DEFAULT);
  */
  // get total(g_arr)
  check_nomsg(tot=cpl_image_get_flux(*g_img));
  if(tot < 1) {
    sinfo_msg_error("no good spaxel");
    goto cleanup;
  }

  return 0;


 cleanup:
  sinfo_free_propertylist(&plist);
  sinfo_free_imagelist(&obj_cub);
  sinfo_free_imagelist(&osel);
  sinfo_free_imagelist(&fsel);
   sinfo_free_table(&ospec);
  sinfo_free_table(&fspec);
  sinfo_free_table(&gpix);
  sinfo_free_table(&gspec);

  return -1;


}

/**
@name sinfo_choose_good_sky_pixels
@param obj_frm  input object frame
@param r_img    input image ratio good/all pixels
@param g_img    input image with good pixels
@param min_frac input threshold setting the minimum fraction of good pixels
@param mask     output mask indicating pixels which satisfy the condition
*/
int
sinfo_choose_good_sky_pixels(cpl_frame* obj_frm,
                             cpl_image* r_img,
                             cpl_image* g_img,
                             const double min_frac,
                             cpl_image** mask)
{

  int xsz=0;
  int ysz=0;
  //int zsz=0;
  int r2i=0;
  int status=0;
  double tot=0;
  double thres=SKY_THRES;
  double cum_x_max=0;

  cpl_image* r2img=NULL;
  cpl_propertylist* plist=NULL;
  cpl_table* cum=NULL;
  cpl_table* hcum=NULL;
  cpl_table* thres_tbl=NULL;

  cknull_nomsg(plist=cpl_propertylist_load(cpl_frame_get_filename(obj_frm),0));
  check_nomsg(xsz=sinfo_pfits_get_naxis1(plist));
  check_nomsg(ysz=sinfo_pfits_get_naxis2(plist));
  //check_nomsg(zsz=sinfo_pfits_get_naxis3(plist));
  sinfo_free_propertylist(&plist);

  // choose pixels which seem to be sky (ie 95% of spectral pixels are flagged)
  check_nomsg(*mask=cpl_image_new(xsz,ysz,CPL_TYPE_DOUBLE));
  //r2 = where(r_img >= thres,r2i);
  // count good pixels: set to 0 what < thres and to 1 what > thres
  check_nomsg(r2img=cpl_image_duplicate(r_img));
  check_nomsg(cpl_image_threshold(r2img,thres,thres,0,1));
  check_nomsg(r2i=cpl_image_get_flux(r2img));

  if(r2i>0) {
    sinfo_free_image(&(*mask));
    check_nomsg(*mask=cpl_image_duplicate(r2img));
  }
  sinfo_free_image(&r2img);
  check_nomsg(r2img=cpl_image_duplicate(r_img));
  check_nomsg(cpl_image_threshold(r2img,thres,SINFO_DBL_MAX,0,SINFO_DBL_MAX));
  sinfo_free_image(&r2img);

  check_nomsg(tot=cpl_image_get_flux(g_img));


   sinfo_msg("%2.2d spaxels (%4.1f %% of good pixels) are designated as sky",
             r2i,100.*r2i/tot);

   //threshold ratio for fraction 'minfrac' of spatial pixels to be 'sky'
   if (1.*r2i/tot < min_frac) {
     sinfo_msg("this is too small - will increase it to %4.1f %%",
           100.*min_frac);
     check_nomsg(cum=cpl_table_new(xsz*ysz));
     check_nomsg(cpl_table_new_column(cum,"X",CPL_TYPE_DOUBLE));
     sinfo_table_column_dindgen(&cum,"X");
     check_nomsg(cpl_table_add_scalar(cum,"X",1.));

     //hcum = r_img(sort(r_img));
     hcum = sinfo_image2table(r_img);
     check_nomsg(sinfo_sort_table_1(hcum,"VALUE",FALSE));

     //thresh = hcum[where(xcum/max(xcum) >= 1.-min_frac)];
     check_nomsg(cpl_table_duplicate_column(cum,"H",hcum,"VALUE"));
     check_nomsg(cum_x_max=cpl_table_get_column_max(cum,"X"));
     check_nomsg(cpl_table_duplicate_column(cum,"R",cum,"X"));
     check_nomsg(cpl_table_divide_scalar(cum,"R",cum_x_max));
     check_nomsg(cpl_table_and_selected_double(cum,"R",
                                              CPL_NOT_LESS_THAN,
                                              (1.-min_frac)));
     check_nomsg(thres_tbl=cpl_table_extract_selected(cum));

     check_nomsg(thres = cpl_table_get(thres_tbl,"R",0,&status));
     //*mask[where(r_img >= thresh)] = 1;
     sinfo_free_image(&(*mask));


     check_nomsg(*mask=cpl_image_duplicate(r_img));
     check_nomsg(cpl_image_threshold(*mask,thres,thres,0,1));
  }
  sinfo_free_table(&cum);
  sinfo_free_table(&hcum);
  sinfo_free_table(&thres_tbl);

  return 0;
 cleanup:

  sinfo_free_propertylist(&plist);
  sinfo_free_image(&r2img);
  sinfo_free_table(&cum);
  sinfo_free_table(&hcum);
  sinfo_free_table(&thres_tbl);

  return -1;

}

/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_fit_bkg
  @memo        Computes chi2 of difference INT(sky)-thermal_background

  @param sa      pointer to sinfo_amoeba structure
  @param p           input fit parameters
  Returns chi2= sum_i[int(sky)_i-therma_i]
 */
/*--------------------------------------------------------------------------*/

static double
sinfo_fit_bkg(double p[])

{
 double* px=NULL;
  double* py=NULL;
  double* pv=NULL;
  cpl_vector* vtmp=NULL;
  double max=0;
  int i=0;
  int np=0;

  double chi2=0;

  check_nomsg(px= cpl_vector_get_data(sa_vx));
  check_nomsg(py= cpl_vector_get_data(sa_vy));
  check_nomsg(np= cpl_vector_get_size(sa_vx));
  check_nomsg(vtmp=cpl_vector_duplicate(sa_vy));
  check_nomsg(pv=cpl_vector_get_data(vtmp));

  for(i=0;i<np;i++) {
    pv[i]=sinfo_fac(px[i],p[2]);
    //sinfo_msg("x=%g p=%g",px[i],pv[i]);
  }
  check_nomsg(max=cpl_vector_get_max(vtmp));
  if(max> 0) {
    check_nomsg(cpl_vector_divide_scalar(vtmp,max));
    check_nomsg(cpl_vector_multiply_scalar(vtmp,p[1]));
    check_nomsg(cpl_vector_add_scalar(vtmp,p[0]));
  }


  for(i=0;i<np;i++) {
    chi2+=(py[i]-pv[i])*(py[i]-pv[i]);
  }
  sinfo_free_my_vector(&vtmp);
  return chi2;
 cleanup:
  sinfo_free_my_vector(&vtmp);
  return -1;

}


/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_termal_background2
  @memo        computes thermal emission background from the sky at a i
                given temperature
  @param int_sky      input sky spectrum table
  @param lambda       input wavelength table
  @param lrange       input wavelength table
  @param temp         input temperature in Kelvin
  @param bkg          output background
  Returns in case of succes -1 else.
 */
/*--------------------------------------------------------------------------*/

int
sinfo_thermal_background2(cpl_table* int_sky,
                         cpl_table* lambda,
                         cpl_table* lrange,
                         cpl_table** bkg)
{

  int n2=0;
  int i=0;
  int j=0;
  int nrow=0;

  cpl_table* tmp1=NULL;
  cpl_table* tmp2=NULL;

  double max=0;
  double wmin=0;
  double wmax=0;
  double p0[3];
  const int MP=4;
  const int NP=3;
  double y[MP];
  double** ap=NULL;
  int nfunc=0;
  int status=0;
  int row=0;
  double bkg_min=0;
  double bkg_max=0;
  double p0_min=0;
  double p0_max=0;
  double p1_min=0;
  double p1_max=0;
  double p2_min=0;
  double p2_max=0;
  double* pw=NULL;
  double* pf=NULL;

  ap=(double**) cpl_calloc(MP,sizeof(double*));

  for(i=0;i<MP;i++) {
    ap[i]=cpl_calloc(NP,sizeof(double));
  }

  cknull(int_sky,"Null input table sky");
  cknull(lambda,"Null input table lambda");
  cknull(lrange,"Null input table lrange");


  //TO BE FIXED: Why lrange to gat wave min and max: int_sky is sufficient
  check_nomsg(wmin=cpl_table_get_column_min(lrange,"WAVE"));
  check_nomsg(wmax=cpl_table_get_column_max(lrange,"WAVE"));
  check_nomsg(cpl_table_and_selected_double(int_sky,"WAVE",
              CPL_NOT_LESS_THAN,wmin));
  check_nomsg(cpl_table_and_selected_double(int_sky,"WAVE",
              CPL_NOT_GREATER_THAN,wmax));
  check_nomsg(tmp1=cpl_table_extract_selected(int_sky));

  check_nomsg(row=sinfo_table_get_index_of_val(tmp1,"WAVE",
                                               wmax,CPL_TYPE_DOUBLE));
  check_nomsg(max=cpl_table_get_double(tmp1,"INT",row,&status));
  check_nomsg(sinfo_table_flag_nan(&tmp1,"INT"));
  check_nomsg(cpl_table_erase_invalid(tmp1));
  check_nomsg(cpl_table_and_selected_double(tmp1,"INT",CPL_NOT_EQUAL_TO,0));
  check_nomsg(tmp2=cpl_table_extract_selected(tmp1));

  sinfo_free_table(&tmp1);
  check_nomsg(n2=cpl_table_get_nrow(tmp2));
  check_nomsg(sa_vx=cpl_vector_wrap(n2,
              cpl_table_get_data_double(tmp2,"WAVE")));
  check_nomsg(sa_vy=cpl_vector_wrap(n2,
              cpl_table_get_data_double(tmp2,"INT")));


  for(i=0;i<MP;i++) {
    for(j=0;j<NP;j++) {
      ap[i][j]=0;
    }
  }

  check_nomsg(bkg_min=cpl_table_get_column_min(tmp2,"INT"));
  check_nomsg(bkg_max=cpl_table_get_double(tmp2,"INT",row,&status));


  //Init amoeba fit parameters
  p0_min=bkg_min*0.9;
  p0_max=bkg_min*1.1;
  p1_min=bkg_max*0.9;
  p1_max=bkg_max*1.1;
  p1_min=200;
  p2_max=300;

  ap[0][0]=p0_min; ap[0][1]=p1_min; ap[0][2]=p2_min;
  ap[1][0]=p0_max; ap[1][1]=p1_min; ap[1][2]=p2_min;
  ap[2][0]=p0_min; ap[2][1]=p1_max; ap[2][2]=p2_min;
  ap[3][0]=p0_min; ap[3][1]=p1_min; ap[3][2]=p2_max;

  sinfo_msg("Before amoeba fit");
  for(i=0;i<MP;i++) {
    for(j=0;j<NP;j++) {
      sinfo_msg("ap[%d][%d]=%g",i,j,ap[i][j]);
    }
  }




  for(i=0;i<MP;i++) {
    p0[0]=ap[i][0];
    p0[1]=ap[i][1];
    p0[2]=ap[i][2];
    y[i]=sinfo_fit_bkg(p0);
  }

  sinfo_msg("p0=%g %g %g",p0[0],p0[1],p0[2]);
  for(i=0;i<N_ITER_FIT_AMOEBA;i++) {
    check_nomsg(sinfo_fit_amoeba(ap,y,NP,AMOEBA_FTOL,sinfo_fit_bkg,&nfunc));
    sinfo_msg("After amoeba fit");
    sinfo_msg("iter=%d ap=%g %g %g",i,ap[0][0],ap[0][1],ap[0][2]);
  }
  sinfo_unwrap_vector(&sa_vx);
  sinfo_unwrap_vector(&sa_vy);
  sinfo_free_table(&tmp2);


  sinfo_msg("After amoeba fit");
  for(i=0;i<MP;i++) {
    for(j=0;j<NP;j++) {
      sinfo_msg("ap[%d][%d]=%g",i,j,ap[i][j]);
    }
    sinfo_msg("y[%d]=%g",i,y[i]);
  }



  check_nomsg(nrow=cpl_table_get_nrow(lambda));
  check_nomsg(*bkg=cpl_table_new(nrow));
  check_nomsg(cpl_table_duplicate_column(*bkg,"WAVE",lambda,"WAVE"));
  check_nomsg(cpl_table_new_column(*bkg,"INT2",CPL_TYPE_DOUBLE));
  cpl_table_fill_column_window(*bkg,"INT2",0,nrow,0.);
  check_nomsg(pw=cpl_table_get_data_double(*bkg,"WAVE"));
  check_nomsg(pf=cpl_table_get_data_double(*bkg,"INT2"));

  for(i=0;i<nrow;i++) {
    pf[i]=sinfo_fac(pw[i],ap[0][2]);
  }
  check_nomsg(max=cpl_table_get_column_max(*bkg,"INT2"));

  if(max != 0) {
     check_nomsg(cpl_table_divide_scalar(*bkg,"INT2",max));
  }
  check_nomsg(cpl_table_multiply_scalar(*bkg,"INT2",ap[0][1]));
  check_nomsg(cpl_table_add_scalar(*bkg,"INT2",ap[0][0]));
  //check_nomsg(cpl_table_save(*bkg,NULL,NULL,
  //"out_amoeba5.fits",CPL_IO_DEFAULT ));
  sinfo_new_destroy_2Ddoublearray(&ap,MP);


  return 0;

 cleanup:
  sinfo_new_destroy_2Ddoublearray(&ap,MP);
  sinfo_free_table(&tmp1);
  sinfo_free_table(&tmp2);
  sinfo_unwrap_vector(&sa_vx);
  sinfo_unwrap_vector(&sa_vy);
  return -1;

}



/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_termal_background
  @memo        computes thermal emission background from the sky at a i
                given temperature
  @param int_sky      input sky spectrum table
  @param lambda       input wavelength table
  @param lrange       input wavelength table
  @param temp         input temperature in Kelvin
  @param bkg          output background
  Returns in case of succes -1 else.
 */
/*--------------------------------------------------------------------------*/

int
sinfo_thermal_background(cpl_table* int_sky,
                         cpl_table* lambda,
                         cpl_table* lrange,
                         const double temp,
                         const int niter,
                         const int filter_width,
                         const double tol,
                         cpl_table** bkg,
                         int* success_fit)
{

  int npix=0;
  int i=0;
  int row=0;
  const int ndim=3;/* There are 3 parameters */
    int ia[ndim];

  int NPOINTS=0;


  double temp1=0;
  double temp2=0;

  //double r0=80.306773;
  //double r1=450.50027;
  //double r2=252.17949;
  double max_tmp2=0;
  double* ptmp1=NULL;
  double thermal=0;
  double* pw=NULL;
  double p0[3];
  double wmin=0;
  double wmax=0;
  double ga0=0;
  double ga1=0;
  //double ga1=0;
  double ga2=0;
  double mse=0;
  double chired=0;


  cpl_vector *a = cpl_vector_new(ndim);
  cpl_table* xlr=NULL;
  cpl_table* ylr=NULL;
  cpl_table* wlr=NULL;
  cpl_table* tmp=NULL;
  cpl_table* temp2_tbl=NULL;

  cpl_vector* y=NULL;
  cpl_vector* fy=NULL;

  cpl_vector* sy=NULL;

  cpl_matrix* x_matrix=NULL;
  //double bkg_min=0;
  double bkg_max=0;
  int status=0;
  double avg=0;
  double sdv=0;
  double med=0;
  double* pif=NULL;
  double* pwf=NULL;
  double* pws=NULL;
  int k=0;
  int nrow=0;

  //check_nomsg(cpl_table_save(int_sky,NULL,NULL,
  //"out_pippo.fits", CPL_IO_DEFAULT));
  check_nomsg(wmin=cpl_table_get_column_min(lrange,"WAVE"));
  check_nomsg(wmax=cpl_table_get_column_max(lrange,"WAVE"));

  //bkg_min=sinfo_fac(wmin,temp);
  bkg_max=sinfo_fac(wmax,temp);
  //sinfo_msg("bkg: min=%g max=%g",bkg_min,bkg_max);
  //sinfo_scale_fct=sinfo_scale_fct*bkg_max;
  //sinfo_scale_fct=sinfo_scale_fct;

  check_nomsg(cpl_table_and_selected_double(lambda,"WAVE",
                                            CPL_NOT_LESS_THAN,wmin));
  check_nomsg(tmp=cpl_table_extract_selected(lambda));

  check_nomsg(cpl_table_and_selected_double(tmp,"WAVE",
                                            CPL_NOT_GREATER_THAN,wmax));
  check_nomsg(xlr=cpl_table_extract_selected(tmp));
  sinfo_free_table(&tmp);


  check_nomsg(cpl_table_and_selected_double(int_sky,"WAVE",
                                            CPL_NOT_LESS_THAN,wmin));
  check_nomsg(tmp=cpl_table_extract_selected(int_sky));
  check_nomsg(cpl_table_and_selected_double(tmp,"WAVE",
                                            CPL_NOT_GREATER_THAN,wmax));


  //To be sure one has not strange cases
  check_nomsg(cpl_table_and_selected_double(tmp,"INT",CPL_GREATER_THAN,-2));
  check_nomsg(ylr=cpl_table_extract_selected(tmp));
  //check_nomsg(cpl_table_save(ylr,NULL,NULL,"out_ylr_0.fits",CPL_IO_DEFAULT));
  sinfo_free_table(&tmp);
  check_nomsg(tmp=cpl_table_duplicate(ylr));
  sinfo_free_table(&ylr);

  check_nomsg(avg=cpl_table_get_column_mean(tmp,"INT"));
  check_nomsg(sdv=cpl_table_get_column_stdev(tmp,"INT"));
  check_nomsg(cpl_table_and_selected_double(tmp,"INT",
                        CPL_LESS_THAN,avg+10*sdv));

  check_nomsg(ylr=cpl_table_extract_selected(tmp));
  sinfo_free_table(&tmp);


  /*
  check_nomsg(xlr=sinfo_table_select_range(lambda,lrange,0.003));
  check_nomsg(ylr=sinfo_table_select_range(int_sky,lrange,0.003));
  */
  check_nomsg(cpl_table_and_selected_double(ylr,"INT",CPL_NOT_EQUAL_TO,0));

  check_nomsg(wlr=cpl_table_extract_selected(ylr));


  check_nomsg(p0[0]=cpl_table_get_column_min(wlr,"INT"));
  check_nomsg(row=sinfo_table_get_index_of_val(ylr,"WAVE",
                                               wmax,CPL_TYPE_DOUBLE));
  check_nomsg(p0[1]=cpl_table_get_double(ylr,"INT",row,&status));
  p0[2]=temp;


  ga0=p0[0];
  ga1=p0[1]/bkg_max;
  //ga1=p0[1];
  ga2=p0[2];

  //sinfo_msg("p= %g %g %g",p0[0],p0[1],p0[2]);
  check_nomsg(sinfo_table_flag_nan(&wlr,"INT"));
  check_nomsg(cpl_table_erase_invalid(wlr));
  //check_nomsg(cpl_table_save(xlr,NULL,NULL,"out_xlr.fits",CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(ylr,NULL,NULL,"out_ylr.fits",CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(wlr,NULL,NULL,"out_wlr.fits",CPL_IO_DEFAULT));


  check_nomsg(NPOINTS=cpl_table_get_nrow(ylr));

  check_nomsg(x_matrix = cpl_matrix_wrap(NPOINTS,1,
                                       cpl_table_get_data_double(ylr,"WAVE")));
  check_nomsg(y=cpl_vector_wrap(NPOINTS,cpl_table_get_data_double(ylr,"INT")));
  //check_nomsg(fy=cpl_vector_filter_median_create(y,1));
  //check_nomsg(fy=cpl_vector_filter_lowpass_create(y,CPL_LOWPASS_LINEAR,3));
  //check_nomsg(cpl_table_save(ylr,NULL,NULL,"out_ylr1.fits",CPL_IO_DEFAULT));
  check_nomsg(fy=sinfo_sky_background_estimate(y,filter_width,filter_width));
  //check_nomsg(cpl_table_save(ylr,NULL,NULL,"out_ylr2.fits",CPL_IO_DEFAULT));
  pif=cpl_vector_get_data(fy);
  pwf=cpl_table_get_data_double(ylr,"WAVE");


  check_nomsg(cpl_table_new_column(int_sky,"INT_BKG_SMO",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(int_sky,"WAVE_SMO",CPL_TYPE_DOUBLE));
  pws=cpl_table_get_data_double(int_sky,"WAVE");

  k=0;
  i=0;
  check_nomsg(nrow=cpl_table_get_nrow(int_sky));
  if((pws[0]-pwf[0])>0) {
    for(i=0;i<NPOINTS;i++) {
      if(fabs(pws[k]-pwf[i]) < tol) {
    check_nomsg(cpl_table_set_double(int_sky,"INT_BKG_SMO",k,pif[i]));
    check_nomsg(cpl_table_set_double(int_sky,"WAVE_SMO",k,pws[i]));
    k++;
      }
    }
  } else {
    for(k=0;k<nrow;k++) {
      if((i<NPOINTS) && (fabs(pws[k]-pwf[i]) < tol)) {
    check_nomsg(cpl_table_set_double(int_sky,"INT_BKG_SMO",k,pif[i]));
    check_nomsg(cpl_table_set_double(int_sky,"WAVE_SMO",k,pws[i]));
    i++;
      }
    }

  }

  //check_nomsg(cpl_table_save(ylr,NULL,NULL,"out_ylr3.fits",CPL_IO_DEFAULT));


  check_nomsg(cpl_vector_set(a, 0, ga0));
  check_nomsg(cpl_vector_set(a, 1, ga1));
  check_nomsg(cpl_vector_set(a, 2, ga2));

  check_nomsg(sy=cpl_vector_duplicate(y));
  check_nomsg(cpl_vector_power(sy,2));
  check_nomsg(cpl_vector_power(sy,0.5));
  //check_nomsg(cpl_vector_fill(sy,0.001));

  ia[0] = 1;
  ia[1] = 1;
  ia[2] = 1;


  for(i=0;i<niter;i++) {

    /*
    sinfo_msg("before fit: a=%g %g %g",
              cpl_vector_get(a,0),
              cpl_vector_get(a,1),
              cpl_vector_get(a,2));
    */
    if(CPL_ERROR_NONE != sinfo_fit_lm(x_matrix,NULL,fy,sy,a,ia,sinfo_fitbkg,
                      sinfo_fitbkg_derivative,
                      &mse,&chired,NULL)) {
      sinfo_msg_warning("Thermal background fit failed");
      cpl_error_reset();
      *success_fit=1;

      goto recover;
    }

    //bkg_max=sinfo_fac(wmax,cpl_vector_get(a,2));
    //sinfo_scale_fct=sinfo_scale_fct*bkg_max;
    /*
    sinfo_msg("after fit: a=%g %g %g chired=%g",
              cpl_vector_get(a,0),
          cpl_vector_get(a,1),
              cpl_vector_get(a,2),
              chired);

    */

  }

    sinfo_msg("Last fit: a=%g %g %g chired=%g",
              cpl_vector_get(a,0),
          cpl_vector_get(a,1),
              cpl_vector_get(a,2),
              chired);

  sinfo_free_my_vector(&fy);
  sinfo_unwrap_vector(&y);
  sinfo_free_my_vector(&sy);
  sinfo_unwrap_matrix(&x_matrix);
  sinfo_free_table(&xlr);
  sinfo_free_table(&ylr);
  sinfo_free_table(&wlr);

  ga0=cpl_vector_get(a,0);
  ga1=cpl_vector_get(a,1);
  ga2=cpl_vector_get(a,2);
  //ga2=252.69284;
  check_nomsg(npix=cpl_table_get_nrow(lrange));
  check_nomsg(pw=cpl_table_get_data_double(lrange,"WAVE"));
  check_nomsg(temp2_tbl=cpl_table_new(npix));
  check_nomsg(cpl_table_new_column(temp2_tbl,"TEMP2",CPL_TYPE_DOUBLE));

  for(i=0;i<npix;i++) {
    temp2=sinfo_fac(pw[i],ga2);
    check_nomsg(cpl_table_set_double(temp2_tbl,"TEMP2",i,temp2));
  }
  check_nomsg(max_tmp2=cpl_table_get_column_max(temp2_tbl,"TEMP2"));
  sinfo_free_table(&temp2_tbl);



  check_nomsg(npix=cpl_table_get_nrow(lambda));
  check_nomsg(pw=cpl_table_get_data_double(lambda,"WAVE"));
  check_nomsg(*bkg=cpl_table_new(npix));
  check_nomsg(cpl_table_new_column(*bkg,"WAVE",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(*bkg,"TEMP1",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(*bkg,"INT",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(*bkg,"INT2",CPL_TYPE_DOUBLE));

  for(i=0;i<npix;i++) {
    check_nomsg(cpl_table_set_double(*bkg,"WAVE",i,pw[i]));
    temp1=sinfo_fac(pw[i],ga2);
    check_nomsg(cpl_table_set_double(*bkg,"TEMP1",i,temp1));
  }

  check_nomsg(ptmp1=cpl_table_get_data_double(*bkg,"TEMP1"));
  //bkg_max=sinfo_fac(wmax,ga2);

  for(i=0;i<npix;i++) {
    thermal=ga0+ptmp1[i]/max_tmp2*ga1;
    check_nomsg(cpl_table_set_double(*bkg,"INT",i,thermal));
    thermal=ga0+ga1*sinfo_fac(pw[i],ga2);
    check_nomsg(cpl_table_set_double(*bkg,"INT2",i,thermal));
  }
  sinfo_free_my_vector(&a);

  return 0;

 recover:
  sinfo_msg_warning("Recover fit of thermal background");
  check_nomsg(npix=cpl_table_get_nrow(lambda));
  check_nomsg(*bkg=cpl_table_new(npix));
  check_nomsg(cpl_table_new_column(*bkg,"TEMP1",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(*bkg,"INT",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(*bkg,"INT2",CPL_TYPE_DOUBLE));

  med=cpl_table_get_column_median(ylr,"INT");
  for(i=0;i<npix;i++) {
    check_nomsg(cpl_table_set_double(*bkg,"INT",i,med));
    check_nomsg(cpl_table_set_double(*bkg,"INT2",i,med));
  }

  sinfo_free_my_vector(&a);
  sinfo_unwrap_vector(&y);
  sinfo_free_my_vector(&sy);
  sinfo_unwrap_matrix(&x_matrix);
  sinfo_free_table(&xlr);
  sinfo_free_table(&ylr);
  sinfo_free_table(&wlr);
  sinfo_free_table(&tmp);

  return 0;


 cleanup:
  sinfo_free_my_vector(&a);
  sinfo_unwrap_vector(&y);
  sinfo_free_my_vector(&sy);
  sinfo_unwrap_matrix(&x_matrix);

  sinfo_free_table(&xlr);
  sinfo_free_table(&ylr);
  sinfo_free_table(&wlr);
  sinfo_free_table(&tmp);
  sinfo_free_table(&temp2_tbl);

  return -1;

}

/**
@name sinfo_filter_min
@memo apply a filter min of a given size to a vector
@param vi input vector
@param size size of filter
@return a new allocated vector resulting from the filter proces
@doc
The following static function passes a min filter of given box
size on the data buffer. The box size must be a positive odd integer.

*/
static cpl_vector*
sinfo_filter_min(const cpl_vector* vi, const int size)
{

  cpl_vector* vo=NULL;
  double min=0;
  int start=size/2;
  int end=0;
  int length=0;
  int i=0;
  int j=0;
  const double* pi=NULL;
  double* po=NULL;
  cknull(vi,"null input vector");
  pi=cpl_vector_get_data_const(vi);
  length=cpl_vector_get_size(vi);
  end=length-size/2;
  vo=cpl_vector_new(length);
  po=cpl_vector_get_data(vo);

  for(i=start; i < end; i++) {
    min=pi[i-start];
    for(j=i-start+1;j<i+start+1;j++) {
      if(min> pi[j]) {
    min=pi[j];
      }
    }
    po[i]=min;

  }

  // To prevent border effects:
  for (i = 0; i < start; i++) {
    po[i] = po[start];
  }

  for (i = end; i < length; i++) {
    po[i] = po[end-1];
  }
  return vo;

 cleanup:
  return NULL;


}


/**
@name sinfo_filter_max
@memo apply a filter max of a given size to a vector
@param vi input vector
@param size size of filter
@return a new allocated vector resulting from the filter proces
@doc
The following static function passes a max filter of given box
size on the data buffer. The box size must be a positive odd integer.

*/
static cpl_vector*
sinfo_filter_max(const cpl_vector* vi, const int size)
{

  cpl_vector* vo=NULL;
  double max=0;
  int start=size/2;
  int end=0;
  int length=0;
  int i=0;
  int j=0;
  const double* pi=NULL;
  double* po=NULL;

  cknull(vi,"null input vector");
  pi=cpl_vector_get_data_const(vi);
  length=cpl_vector_get_size(vi);
  end=length-size/2;
  vo=cpl_vector_new(length);
  po=cpl_vector_get_data(vo);

  for(i=start; i < end; i++) {
    max=pi[i-start];
    for(j=i-start+1;j<i+start+1;j++) {
      if(max< pi[j]) {
    max=pi[j];
      }
    }
    po[i]=max;

  }

  // To prevent border effects:
  for (i = 0; i < start; i++) {
    po[i] = po[start];
  }

  for (i = end; i < length; i++) {
    po[i] = po[end-1];
  }
  return vo;

 cleanup:
  return NULL;


}



/**
@name sinfo_filter_smo
@memo passes a running mean of a given size to a vector
@param vi input vector
@param size size of filter
@return a new allocated vector resulting from the filter proces
@doc
The following static function passes a running mean of given box
size on the data buffer. The box size must be a positive odd integer.

*/
static cpl_vector*
sinfo_filter_smo(const cpl_vector* vi, const int size)
{


  double sum=0;
  int start=size/2;
  int end=0;
  int length=0;
  int i=0;
  int j=0;
  const double* pi=NULL;
  double* po=NULL;
  cpl_vector* vo=NULL;

  cknull(vi,"null input vector");
  length=cpl_vector_get_size(vi);
  end=length-size/2;
  vo=cpl_vector_new(length);
  pi=cpl_vector_get_data_const(vi);
  po=cpl_vector_get_data(vo);

  for(i=start; i < end; i++) {
    sum=0;
    for(j=i - start;j<i+start+1;j++) {
      sum += pi[j];
    }
    po[i]=sum/size;

  }

  // To prevent border effects:
  for (i = 0; i < start; i++) {
    po[i] = po[start];
  }

  for (i = end; i < length; i++) {
    po[i] = po[end-1];
  }
  return vo;

 cleanup:
  return NULL;

}

/**
 * @brief
 *   Background determination on 1D emission line spectrum (arc)
 *
 * @param spectrum A 1D emission line spectrum
 * @param msize    Size of min-filter
 * @param fsize    Size of running-average-filter
 *
 * @return CPL_ERROR_NONE in case of success
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The input spectrum is a <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_ILLEGAL_INPUT</td>
 *       <td class="ecr">
 *         Either <i>msize</i> is less than 3, or <i>fsize</i> is less
 *         than <i>msize</i>, or <i>fsize</i> is greater than <i>length/2</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * This function fills the array @em back with the estimated values
 * of the background along the input spectrum. The algorithm is
 * based on the assumption that there is at least one background
 * value at any position of the min-filter box running along the
 * spectrum. A min-filter is passed on the spectrum, and the result
 * is smoothed by averaging on a running box of size @em fsize.
 * The min-filter is run between the positions @em msize / 2
 * and @em length - @em msize / 2, and the min value found at
 * such positions is then repeated up to the spectrum ends. Similarly,
 * the running average is limited to the interval from @em fsize / 2
 * and @em length - @em fsize / 2, leaving the most external values
 * untouched. After this, a max filter and a smoothing using boxes
 * with double the specified sizes are run, as a way to eliminate
 * the contamination from occasional cold pixels. Finally, the
 * min filter and the smoothing are applied again to obviate the
 * slight background over-estimation introduced by the max filter.
 *
 * It is required that the @em back array is at least long as the
 * array @em spectrum.  Moreover @em msize must be greater than 1,
 * and @em fsize greater than, or equal to, @em msize. Likewise,
 * @em length must be greater than twice @em fsize. If such conditions
 * are not met, or if the input arrays are @c NULL pointers, this
 * function will set an error code, and leave the @em back array
 * untouched. If either @em msize or @em fsize are even numbers,
 * they are made odd by adding 1. Suggested values for @em msize
 * and @em fsize are 15 pixels for typical arc lamp spectra.
 */

cpl_vector* sinfo_sky_background_estimate(cpl_vector *spectrum,
                                             int msize,
                                             int fsize)
{

    cpl_vector  * minf=NULL;
    cpl_vector  * maxf=NULL;
    cpl_vector  * smof=NULL;
    cpl_vector  * back=NULL;
    double* pb=NULL;
    double* ps=NULL;

    int     i=0;
    int length=0;


    cknull(spectrum,"null input data");

    if (msize % 2 == 0)
        msize++;

    if (fsize % 2 == 0)
        fsize++;
    check_nomsg(length=cpl_vector_get_size(spectrum));

    if (msize < 3 || fsize < msize || length < 2*fsize)
        return NULL;


    cknull_nomsg(minf = sinfo_filter_min(spectrum, msize));
    cknull_nomsg(smof = sinfo_filter_smo(minf, fsize));
    cpl_vector_delete(minf);
    cknull_nomsg(maxf = sinfo_filter_max(smof,2*msize+1));
    cpl_vector_delete(smof);
    cknull_nomsg(smof = sinfo_filter_smo(maxf, 2*fsize+1));
    cpl_vector_delete(maxf);
    cknull_nomsg(minf = sinfo_filter_min(smof, 2*msize+1));
    cpl_vector_delete(smof);
    cknull_nomsg(smof = sinfo_filter_smo(minf, 2*fsize+1));
    cpl_vector_delete(minf);
    cknull_nomsg(back=cpl_vector_new(length));
    cknull_nomsg(pb=cpl_vector_get_data(back));
    cknull_nomsg(ps=cpl_vector_get_data(smof));

    for (i = 0; i < length; i++) {
        pb[i] = ps[i];
     }
    cpl_vector_delete(smof);

    return back;
 cleanup:

    return NULL;

}



/**
@name sinfo_slice_z
@memo extract a cube slice (spectrum) along z at i,j
@param cin input imagelist
@param i   inpout position (x)
@param j   inpout position (y)
@return if success a table containg the spectrum along z at i,j
        else NULL
*/
static cpl_table*
sinfo_slice_z(const cpl_imagelist* cin,const int i,const int j)
{
  int sx=0;
  //int sy=0;
  int sz=0;
  int k=0;
  cpl_table* tout=NULL;
  const cpl_image* img=NULL;
  const double* pim=NULL;

  cknull(cin,"null input imagelist");
  check_nomsg(sz=cpl_imagelist_get_size(cin));
  check_nomsg(img=cpl_imagelist_get_const(cin,0));
  check_nomsg(sx=cpl_image_get_size_x(img));
  //check_nomsg(sy=cpl_image_get_size_y(img));
  check_nomsg(tout=cpl_table_new(sz));
  check_nomsg(cpl_table_new_column(tout,"VALUE",CPL_TYPE_DOUBLE));
  for(k=0;k<sz;k++) {
    check_nomsg(img=cpl_imagelist_get_const(cin,k));
    check_nomsg(pim=cpl_image_get_data_double_const(img));
    check_nomsg(cpl_table_set(tout,"VALUE",k,pim[j*sx+i]));
  }

  return tout;
 cleanup:
  sinfo_free_table(&tout);

  return NULL;

}

/**
@name sinfo_xcorr
@memo cross correlate object and sky spectra considering lines of given \
      half width
@param int_obj  input obj spectrum
@param int_sky  input sky spectrum
@param lambda   input wavelength range
@param dispersion input dispersion value
@param line_hw    input line half width
*/
double
sinfo_xcorr(cpl_table* int_obj,
            cpl_table* int_sky,
            cpl_table* lambda,
            const double dispersion,
            const double line_hw)
{

  cpl_table* z=NULL;
  cpl_table* tmp_sky=NULL;

  cpl_table* z_diff=NULL;
  cpl_table* z_pos=NULL;

  int z_ext=0;
  double z_mean=0;
  double z_sdv=0;
  int nrow=0;
  int i=0;
  /*
  double g_lam=0;
  double g_err=0;
  */
  double sky_max=0;

  double* pint=NULL;
  int* ppos=NULL;
  int status=0;
  int zsize=0;
  int iq=0;

  //double g_diff=0;
  int jz=0;
  int z1=0;
  int nfit=0;
  int npos=0;
  cpl_table* z_good=NULL;
  cpl_table* w_tbl=NULL;
  cpl_table* o_tbl=NULL;
  cpl_table* s_tbl=NULL;
  cpl_vector* vw=NULL;
  cpl_vector* vs=NULL;
  cpl_vector* vo=NULL;
  cpl_vector* sx=NULL;
  cpl_vector* sy=NULL;


  double o1=0;
  double o2=0;
  double oc=0;
  double om=0;



  double zfit=0;


  double mse=0;

  double ws=0;
  double wc_s=0;
  double sig_s=0;
  double bkg_s=0;
  double amp_s=0;
  double area_s=0;

  double wo=0;
  double wc_o=0;
  double sig_o=0;
  double bkg_o=0;
  double amp_o=0;
  double area_o=0;

  cpl_polynomial* cfit=NULL;
  cpl_size pows[2];
  cpl_vector* vx=NULL;
  cpl_vector* vy=NULL;


  cpl_error_code error_code=CPL_ERROR_NONE;

  // crosscorrelate obj & sky to check for lambda offset

  //if (mean(z[where(finite(z))]) < 0) z = z * (-1);
  //if sky mean is < 0 flip sky intensity
  zsize=cpl_table_get_nrow(int_obj);
  check_nomsg(z = cpl_table_duplicate(int_sky));
  ck0_nomsg(sinfo_table_flag_nan(&z,"INT"));
  //check_nomsg(cpl_table_save(z,NULL,NULL,"out_z1.fits",CPL_IO_DEFAULT));
  check_nomsg(z_mean=cpl_table_get_column_mean(z,"INT"));
  if(z_mean < 0) {
    check_nomsg(cpl_table_multiply_scalar(z,"INT",-1));
  }
  //check_nomsg(cpl_table_save(z,NULL,NULL,"out_z2.fits",CPL_IO_DEFAULT));

  //z[where(int_sky < max(int_sky[where(finite(int_sky))])/4)] = 0;
  // take in consideration only strong sky lines (set else to 0)
  check_nomsg(tmp_sky=cpl_table_duplicate(int_sky));
  ck0_nomsg(sinfo_table_flag_nan(&tmp_sky,"INT"));
  check_nomsg(sky_max=cpl_table_get_column_max(tmp_sky,"INT"));
  sinfo_free_table(&tmp_sky);

  //flag too low values
  check_nomsg(nrow=cpl_table_get_nrow(z));
  check_nomsg(pint=cpl_table_get_data_double(z,"INT"));
  check_nomsg(sky_max=cpl_table_get_column_max(z,"INT"));
  for(i=0;i<nrow;i++) {
    if(pint[i]<sky_max/SKY_LINE_MIN_CUT) {
      pint[i]=0;
    }
  }


  //check_nomsg(cpl_table_save(z,NULL,NULL,"out_z4.fits",CPL_IO_DEFAULT));
  //computes gradient
  //z_diff = z[0:n_elements(z)-2] - z[1:n_elements(z)-1];
  check_nomsg(z_diff=cpl_table_duplicate(z));
  check_nomsg(cpl_table_duplicate_column(z_diff,"INT1",z,"INT"));
  check_nomsg(cpl_table_duplicate_column(z_diff,"INT2",z,"INT"));
  check_nomsg(cpl_table_shift_column(z_diff,"INT1",-1));
  check_nomsg(cpl_table_duplicate_column(z_diff,"DIFF",z_diff,"INT2"));
  check_nomsg(cpl_table_subtract_columns(z_diff,"DIFF","INT1"));

  check_nomsg(cpl_table_erase_window(z_diff,nrow-2,2));
  //check_nomsg(cpl_table_save(z_diff,NULL,NULL,
  //                             "out_z_diff.fits",CPL_IO_DEFAULT));

  //identify points positions at which there is a line pick
  check_nomsg(cpl_table_new_column(z_diff,"POS",CPL_TYPE_INT));
  check_nomsg(cpl_table_fill_column_window_int(z_diff,"POS",0,nrow,0));

  check_nomsg(pint=cpl_table_get_data_double(z_diff,"DIFF"));
  check_nomsg(ppos=cpl_table_get_data_int(z_diff,"POS"));
  check_nomsg(nrow=cpl_table_get_nrow(z_diff));
  for(i=1;i<nrow;i++) {
    if(!irplib_isnan(pint[i]) && (pint[i]>0 && pint[i-1]<0)) {
      ppos[i]=i;
    }
  }

  //check_nomsg(cpl_table_save(z_diff,NULL,NULL,"out_z_diff.fits",
  //                             CPL_IO_DEFAULT));
  check_nomsg(cpl_table_select_all(z_diff));
  check_nomsg(cpl_table_and_selected_int(z_diff,"POS",CPL_GREATER_THAN,0));
  check_nomsg(z_pos=cpl_table_extract_selected(z_diff));
  sinfo_free_table(&z_diff);
  //check_nomsg(cpl_table_save(z_pos,NULL,NULL,
  //                             "out_z_pos.fits",CPL_IO_DEFAULT));
  //Do a gaussian fit in a range of size 2*zext centered at
  //each line maximum position (fit the line) to get in corresponding arrays:
  // 1) line lambda position of object and sky
  // 2) line object -sky intensity
  // 3) line object-sky intensity error

  /*
  g_lam = 0.;
  g_diff = 0.;
  g_err = 0.;
  */
  check_nomsg(npos=cpl_table_get_nrow(z_pos));
  z_ext = line_hw ;
  check_nomsg(cpl_table_new_column(z_pos,"STATUS_S",CPL_TYPE_INT));
  check_nomsg(cpl_table_new_column(z_pos,"STATUS_O",CPL_TYPE_INT));
  check_nomsg(cpl_table_fill_column_window_int(z_pos,"STATUS_S",0,npos,0));
  check_nomsg(cpl_table_fill_column_window_int(z_pos,"STATUS_O",0,npos,0));
  check_nomsg(cpl_table_new_column(z_pos,"SIGS",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"WAVES",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"BKGS",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"AREAS",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"AMPS",CPL_TYPE_DOUBLE));


  check_nomsg(cpl_table_new_column(z_pos,"SIGO",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"WAVEO",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"BKGO",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"AREAO",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"AMPO",CPL_TYPE_DOUBLE));

  check_nomsg(cpl_table_new_column(z_pos,"WAVEC",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"WDIF",CPL_TYPE_DOUBLE));
  check_nomsg(cpl_table_new_column(z_pos,"ERR",CPL_TYPE_DOUBLE));

  nfit=2*z_ext+1;
  //sinfo_msg("npos=%d z_ext=%d",npos,z_ext);
  //sinfo_table_column_dump(z_pos,"POS",CPL_TYPE_INT);
  //check_nomsg(cpl_table_save(z_pos,NULL,NULL,
  //                             "out_z_pos_0.fits",CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(int_obj,NULL,NULL,
  //                             "out_int_obj_0.fits",CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(int_sky,NULL,NULL,
  //                             "out_int_sky_0.fits",CPL_IO_DEFAULT));

  for (jz=0;jz<npos;jz++) {
    check_nomsg(z1 = cpl_table_get_int(z_pos,"POS",jz,&status));
    //sinfo_msg("z1=%d",z1);
    // AMO added if check to prevent array explosion
    if((z1-z_ext) > 0 && (z1+z_ext) < zsize) {
      check_nomsg(cpl_table_select_all(int_sky));
      check_nomsg(cpl_table_select_all(int_obj));
      check_nomsg(cpl_table_select_all(lambda));
      check_nomsg(cpl_table_and_selected_window(int_sky,z1-z_ext,nfit));
      check_nomsg(s_tbl=cpl_table_extract_selected(int_sky));
      check_nomsg(cpl_table_and_selected_window(lambda,z1-z_ext,nfit));
      check_nomsg(w_tbl=cpl_table_extract_selected(lambda));
      check_nomsg(cpl_table_and_selected_window(int_obj,z1-z_ext,nfit));
      check_nomsg(o_tbl=cpl_table_extract_selected(int_obj));


      check_nomsg(vw=cpl_vector_wrap(nfit,
                     cpl_table_get_data_double(w_tbl,"WAVE")));
      check_nomsg(vs=cpl_vector_wrap(nfit,
                     cpl_table_get_data_double(s_tbl,"INT")));
      check_nomsg(vo=cpl_vector_wrap(nfit,
                     cpl_table_get_data_double(o_tbl,"INT")));


      check_nomsg(sx=cpl_vector_new(nfit));
      check_nomsg(cpl_vector_fill(sx,10.));
      check_nomsg(sy=cpl_vector_duplicate(sx));


      // Check if the object line is in emission or absorbtion
      o1=cpl_vector_get(vo,0);
      o2=cpl_vector_get(vo,nfit-1);
      oc=(o1+o2)*0.5;
      om=cpl_vector_get_median_const(vo);
      if(om<oc) {
    cpl_vector_multiply_scalar(vo,-1.);
      }
      check_nomsg(ws=cpl_table_get_double(lambda,"WAVE",z1,&status));
      check_nomsg(amp_s=cpl_table_get_double(z_pos,"INT",jz,&status));
      wc_s=ws;
      sig_s=z_ext*dispersion;
      bkg_s=0;
      area_s=sinfo_gaussian_area(amp_s,sig_s,ws,wc_s,bkg_s);
      if(wc_s < 2.35) {
        //sinfo_msg("wc_s=%f",wc_s);
        //cpl_vector_dump(vw,stdout);
        //cpl_vector_dump(vs,stdout);

        error_code=cpl_vector_fit_gaussian(vw,NULL,
                        vs,NULL,
                       CPL_FIT_ALL,
                       &wc_s,&sig_s,
                       &area_s,&bkg_s,
                       NULL,NULL,NULL);
        if(error_code == CPL_ERROR_NONE) {
      amp_s=sinfo_gaussian_amp(area_s,sig_s,ws,wc_s,bkg_s);
          check_nomsg(cpl_table_set_double(z_pos,"WAVES",jz,wc_s));
          check_nomsg(cpl_table_set_double(z_pos,"SIGS",jz,sig_s));
          check_nomsg(cpl_table_set_double(z_pos,"AREAS",jz,area_s));
          check_nomsg(cpl_table_set_double(z_pos,"BKGS",jz,bkg_s));
          check_nomsg(cpl_table_set_double(z_pos,"AMPS",jz,amp_s));
      /*
          sinfo_msg("Gauss fit parameters:");
          sinfo_msg("wc_s=%f sig_s=%f area_s=%f bkg_s=%f",
                     wc_s,sig_s,area_s,bkg_s);
          sinfo_msg("mse=%f chired=%f amp_s=%f",
                     mse,chired,amp_s);
      */

    } else if (error_code == CPL_ERROR_CONTINUE) {
      cpl_error_reset();
      amp_s=sinfo_gaussian_amp(area_s,sig_s,ws,wc_s,bkg_s);
          check_nomsg(cpl_table_set_double(z_pos,"WAVES",jz,wc_s));
          check_nomsg(cpl_table_set_double(z_pos,"SIGS",jz,sig_s));
          check_nomsg(cpl_table_set_double(z_pos,"AREAS",jz,area_s));
          check_nomsg(cpl_table_set_double(z_pos,"BKGS",jz,bkg_s));
          check_nomsg(cpl_table_set_double(z_pos,"AMPS",jz,amp_s));
          check_nomsg(cpl_table_set_int(z_pos,"STATUS_S",jz,-1));
    } else {
      cpl_error_reset();
          check_nomsg(cpl_table_set_double(z_pos,"WAVES",jz,wc_s));
          check_nomsg(cpl_table_set_double(z_pos,"SIGS",jz,sig_s));
          check_nomsg(cpl_table_set_double(z_pos,"AREAS",jz,area_s));
          check_nomsg(cpl_table_set_double(z_pos,"BKGS",jz,bkg_s));
          check_nomsg(cpl_table_set_double(z_pos,"AMPS",jz,amp_s));
          check_nomsg(cpl_table_set_int(z_pos,"STATUS_S",jz,-2));
    }
        check_nomsg(wo=cpl_table_get_double(lambda,"WAVE",z1,&status));
        check_nomsg(amp_o=cpl_table_get_double(z_pos,"INT",jz,&status));
        wc_o=wo;
        sig_o=z_ext*dispersion;
        bkg_o=0;
        area_o=sinfo_gaussian_area(amp_o,sig_o,wo,wc_o,bkg_o);
        error_code = cpl_vector_fit_gaussian(vw,NULL,
                      vo,sy,
                     CPL_FIT_ALL,
                     &wc_o,&sig_o,
                     &area_o,&bkg_o,
                     NULL,NULL,NULL);

        if(error_code == CPL_ERROR_NONE) {

          amp_o=sinfo_gaussian_amp(area_o,sig_o,wo,wc_o,bkg_o);
          check_nomsg(cpl_table_set_double(z_pos,"WAVEO",jz,wc_o));
          check_nomsg(cpl_table_set_double(z_pos,"SIGO",jz,sig_o));
          check_nomsg(cpl_table_set_double(z_pos,"AREAO",jz,area_o));
          check_nomsg(cpl_table_set_double(z_pos,"BKGO",jz,bkg_o));
          check_nomsg(cpl_table_set_double(z_pos,"AMPO",jz,amp_o));
      /*
          sinfo_msg("Gauss fit parameters:");
          sinfo_msg("wc_o=%f sig_o=%f area_o=%f bkg_o=%f",
                     wc_o,sig_o,area_o,bkg_o);
          sinfo_msg("mse=%f chired=%f amp_o=%f",
                     mse,chired,amp_o);
      */
    } else if (error_code == CPL_ERROR_CONTINUE) {

      cpl_error_reset();
          amp_o=sinfo_gaussian_amp(area_o,sig_o,wo,wc_o,bkg_o);
          check_nomsg(cpl_table_set_double(z_pos,"WAVEO",jz,wc_o));
          check_nomsg(cpl_table_set_double(z_pos,"SIGO",jz,sig_o));
          check_nomsg(cpl_table_set_double(z_pos,"AREAO",jz,area_o));
          check_nomsg(cpl_table_set_double(z_pos,"BKGO",jz,bkg_o));
          check_nomsg(cpl_table_set_double(z_pos,"AMPO",jz,amp_o));
          check_nomsg(cpl_table_set_int(z_pos,"STATUS_O",jz,-1));

    } else {
      cpl_error_reset();
          check_nomsg(cpl_table_set_double(z_pos,"WAVEO",jz,wc_o));
          check_nomsg(cpl_table_set_double(z_pos,"SIGO",jz,sig_o));
          check_nomsg(cpl_table_set_double(z_pos,"AREAO",jz,area_o));
          check_nomsg(cpl_table_set_double(z_pos,"BKGO",jz,bkg_o));
          check_nomsg(cpl_table_set_double(z_pos,"AMPO",jz,amp_o));
          check_nomsg(cpl_table_set_int(z_pos,"STATUS_O",jz,-2));
          /*
          if (lambda[z1] < 2.35 &&
             total(finite([l1,s1,o1])) == n_elements([l1,s1,o1])) {
            gs1 = float(gaussfit(l1,s1,as1,nterms=3));
            go1 = float(gaussfit(l1,o1,ao1,nterms=3));
            g_lam = [g_lam,(as1[1]+ao1[1])/2.];
            g_diff = [g_diff,as1[1]-ao1[1]];
            g_err = [g_err,sqrt(as1[2]^2+ao1[2]^2)];
          }
          */
        }
        check_nomsg(cpl_table_set_double(z_pos,"ERR",
                                       jz,sqrt(sig_s*sig_s+sig_o*sig_o)));
        check_nomsg(cpl_table_set_double(z_pos,"WDIF",jz,wc_s-wc_o));
        check_nomsg(cpl_table_set_double(z_pos,"WAVEC",jz,(wc_o+wc_s)/2));

      } else {
          check_nomsg(cpl_table_set_int(z_pos,"STATUS_S",jz,-3));
          check_nomsg(cpl_table_set_int(z_pos,"STATUS_O",jz,-3));
      }
      sinfo_unwrap_vector(&vw);
      sinfo_unwrap_vector(&vs);
      sinfo_unwrap_vector(&vo);
      sinfo_free_my_vector(&sx);
      sinfo_free_my_vector(&sy);
      sinfo_free_table(&w_tbl);
      sinfo_free_table(&s_tbl);
      sinfo_free_table(&o_tbl);
    }
  }


  check_nomsg(cpl_table_duplicate_column(z_pos,"YDIF",z_pos,"WDIF"));
  check_nomsg(cpl_table_divide_scalar(z_pos,"YDIF",dispersion));
  //sinfo_table_column_dump(z_pos,"WAVEC",CPL_TYPE_DOUBLE);
  //sinfo_table_column_dump(z_pos,"STATUS",CPL_TYPE_INT);
  //sinfo_table_column_dump(z_pos,"WDIF",CPL_TYPE_DOUBLE);

  check_nomsg(cpl_table_and_selected_int(z_pos,"STATUS_S",CPL_GREATER_THAN,-2));
  check_nomsg(cpl_table_and_selected_int(z_pos,"STATUS_O",CPL_GREATER_THAN,-2));

  //check_nomsg(cpl_table_save(z_pos,NULL,NULL,
  //                             "out_z_pos.fits",CPL_IO_DEFAULT));

  //goto cleanup;

  check_nomsg(z_good=cpl_table_extract_selected(z_pos));
  check_nomsg(npos=cpl_table_get_nrow(z_good));
  sinfo_free_table(&z_pos);
  if(npos == 0) {
    return 0;
  }
  check_nomsg(z_pos=cpl_table_duplicate(z_good));
  check_nomsg(z_mean = cpl_table_get_column_median(z_pos,"WDIF"));
  check_nomsg(z_sdv  = cpl_table_get_column_stdev(z_pos,"WDIF"));

  //check_nomsg(cpl_table_save(z_pos,NULL,NULL,
  //                             "out_z_pos.fits",CPL_IO_DEFAULT));

  check_nomsg(cpl_table_duplicate_column(z_pos,"CHECK",
                                         z_pos,"WDIF"));


  cpl_table_erase_column(z_pos,"AMPO");
  cpl_table_erase_column(z_pos,"SIGO");
  cpl_table_erase_column(z_pos,"AREAO");
  cpl_table_erase_column(z_pos,"BKGO");
  cpl_table_erase_column(z_pos,"WAVEO");
  cpl_table_erase_column(z_pos,"AMPS");
  cpl_table_erase_column(z_pos,"SIGS");
  cpl_table_erase_column(z_pos,"AREAS");
  cpl_table_erase_column(z_pos,"BKGS");
  cpl_table_erase_column(z_pos,"WAVES");
  cpl_table_erase_column(z_pos,"STATUS_S");
  cpl_table_erase_column(z_pos,"STATUS_O");

  cpl_table_erase_column(z_pos,"INT");
  cpl_table_erase_column(z_pos,"INT1");
  cpl_table_erase_column(z_pos,"INT2");
  cpl_table_erase_column(z_pos,"ERR");
  cpl_table_erase_column(z_pos,"POS");
  cpl_table_erase_column(z_pos,"DIFF");

  //check_nomsg(cpl_table_save(z_good,NULL,NULL,
  //                             "out_z_good.fits",CPL_IO_DEFAULT));
  //Do a kappa-sigma clip of the differences of line positions
  //as determined in the object and in the sky spectrum

  sinfo_msg("ks-clip1");
  sinfo_msg("iter mean (um) sdv (um) mean (pix) sdv (pix)");
  //sinfo_table_column_dump(z_pos,"WAVEC",CPL_TYPE_DOUBLE);
  //sinfo_table_column_dump(z_pos,"WDIF",CPL_TYPE_DOUBLE);

  for (iq = 0;iq<XCOR_YSHIFT_KS_CLIP;iq++) {
    //sinfo_msg("nval=%d",cpl_table_get_nrow(z_pos));
    sinfo_msg("  %d  %3.2g   %3.2g  %5.4g     %5.4g",
               iq,z_mean,z_sdv,z_mean/dispersion,z_sdv/dispersion);
    //z_good = where(abs(g_diff-z_mean) <= 2*z_sdv);

    check_nomsg(cpl_table_subtract_scalar(z_pos,"CHECK",z_mean));
    check_nomsg(cpl_table_duplicate_column(z_pos,"CHECKW",z_pos,"CHECK"));
    check_nomsg(cpl_table_multiply_columns(z_pos,"CHECKW","CHECK"));
    check_nomsg(cpl_table_power_column(z_pos,"CHECKW",0.5));
    check_nomsg(cpl_table_add_scalar(z_pos,"CHECK",z_mean));
    check_nomsg(cpl_table_and_selected_double(z_pos,"CHECKW",
                                              CPL_NOT_GREATER_THAN,2*z_sdv));
    sinfo_free_table(&z_good);
    check_nomsg(z_good=cpl_table_extract_selected(z_pos));
    //sinfo_msg("ngood=%d",cpl_table_get_nrow(z_good));
    check_nomsg(cpl_table_select_all(z_pos));
    //z_mean = median(g_diff[z_good]);
    //z_sdv = stddev(g_diff[z_good]);
    check_nomsg(z_mean = cpl_table_get_column_median(z_good,"WDIF"));
    if(nfit>1) {
    check_nomsg(z_sdv  = cpl_table_get_column_stdev(z_good,"WDIF"));
    } else {
      z_sdv=0;
    }
    sinfo_free_table(&z_good);
    check_nomsg(cpl_table_erase_column(z_pos,"CHECKW"));

  }
  /* do a poly fit of wdif versus wave*/
  /*
  for (iq = 0; iq<3; iq++) {
    // sinfo_msg("%d %f %f",iq,mean(zfit),zsdv);
    par1 = poly_fit(g_lam[z_good],g_diff[z_good],poly_n);
    z_fit = g_diff*0.;
    for (ii=0;ii<poly_n) z_fit = z_fit + par1[ii]*g_lam^ii;
    z_res = g_diff-z_fit;
    z_sdv = stddev(z_res[zgood]);
    z_good = where(abs(z_res) le 3*z_sdv);
  }
  */
  cpl_table_select_all(z_pos);
  check_nomsg(cpl_table_new_column(z_pos,"ZFIT",CPL_TYPE_DOUBLE));
  check_nomsg(nfit=cpl_table_get_nrow(z_pos));
  check_nomsg(cpl_table_fill_column_window(z_pos,"ZFIT",0,nfit,0));
  //check_nomsg(cpl_table_save(z_pos,NULL,NULL,
  //                             "out_z_pos2.fits",CPL_IO_DEFAULT));
  check_nomsg(z_good=cpl_table_duplicate(z_pos));

  //Do a fit of a uniform function to the residuals line position differences
  sinfo_msg("ks-clip2");
  sinfo_msg("iter mean (um) sdv (um) mean (pix) sdv (pix)");
  check_nomsg(cpl_table_select_all(z_good));
  //sinfo_table_column_dump(z_pos,"WDIF",CPL_TYPE_DOUBLE);
  for(iq=0;iq<XCOR_YSHIFT_KS_CLIP;iq++) {
    //cpl_table_dump(z_pos,0,cpl_table_get_nrow(z_pos),stdout);
    check_nomsg(nfit=cpl_table_get_nrow(z_good));
    //sinfo_msg("nfit=%d",nfit);
    if(nfit>0) {
    check_nomsg(vx=cpl_vector_wrap(nfit,
                   cpl_table_get_data_double(z_good,"WAVE")));
    check_nomsg(vy=cpl_vector_wrap(nfit,
                   cpl_table_get_data_double(z_good,"WDIF")));
    check_nomsg(cfit=sinfo_polynomial_fit_1d_create(vx,vy,0,&mse));
    pows[0]=0;
    pows[1]=0;
    check_nomsg(zfit=cpl_polynomial_get_coeff(cfit,pows));
    sinfo_free_polynomial(&cfit);
    //sinfo_msg("coeff 0=%g um %g pix",zfit,zfit/dispersion);

    //computes residuals=difference-fit and their standard deviation
    //and then do a kappa-sigma clip of outliers (out of 3 sigma)
    check_nomsg(cpl_table_fill_column_window(z_good,"ZFIT",0,nfit,zfit));
    check_nomsg(cpl_table_duplicate_column(z_good,"WRES",z_good,"WDIF"));
    check_nomsg(cpl_table_subtract_columns(z_good,"WRES","ZFIT"));
    if(nfit>1) {
      //sinfo_msg("nfit=%d",nfit);
      //cpl_table_dump(z_good,0,nfit,stdout);
      check_nomsg(z_sdv=cpl_table_get_column_stdev(z_good,"WRES"));
      //sinfo_msg("z_sdv=%f",z_sdv);
    } else {
      z_sdv=0;
    }
    check_nomsg(z_mean=cpl_table_get_column_mean(z_good,"WDIF"));

    sinfo_msg("  %d  %3.2g   %3.2g  %5.4g     %5.4g",
              iq,z_mean,z_sdv,z_mean/dispersion,z_sdv/dispersion);

    check_nomsg(nfit=cpl_table_get_nrow(z_pos));
    check_nomsg(cpl_table_fill_column_window(z_pos,"ZFIT",0,nfit,zfit));
    check_nomsg(cpl_table_duplicate_column(z_pos,"WRES",z_pos,"WDIF"));
    check_nomsg(cpl_table_subtract_columns(z_pos,"WRES","ZFIT"));

    check_nomsg(cpl_table_multiply_columns(z_pos,"WRES","WRES"));
    check_nomsg(cpl_table_power_column(z_pos,"WRES",0.5));
    //cpl_table_dump(z_pos,0,cpl_table_get_nrow(z_pos),stdout);
    /*
    sinfo_msg("min=%g max=%g ndat=%d",
              cpl_table_get_column_min(z_pos,"WRES"),
              cpl_table_get_column_max(z_pos,"WRES"),
              cpl_table_get_nrow(z_pos));
    */
    check_nomsg(cpl_table_and_selected_double(z_pos,"WRES",
                                              CPL_NOT_GREATER_THAN,3*z_sdv));

    check_nomsg(sinfo_free_table(&z_good));
    check_nomsg(z_good=cpl_table_extract_selected(z_pos));


    check_nomsg(cpl_table_select_all(z_pos));
    check_nomsg(cpl_table_select_all(z_good));
    check_nomsg(cpl_table_erase_column(z_good,"WRES"));
    check_nomsg(cpl_table_erase_column(z_pos,"WRES"));

    sinfo_unwrap_vector(&vx);
    sinfo_unwrap_vector(&vy);

    }

  }
  //sinfo_msg(">>mean=%g",cpl_table_get_column_mean(z_good,"WDIF"));

  //check_nomsg(cpl_table_save(z_good,NULL,NULL,
  //                             "out_z_pos3.fits",CPL_IO_DEFAULT));
  sinfo_unwrap_vector(&vx);
  sinfo_unwrap_vector(&vy);
  sinfo_free_polynomial(&cfit);
  sinfo_free_table(&z);
  sinfo_free_table(&z_pos);
  sinfo_free_table(&z_good);


  return zfit;
 cleanup:

  sinfo_free_table(&z_good);
  sinfo_free_table(&z);
  sinfo_free_table(&z_diff);
  sinfo_free_table(&tmp_sky);
  sinfo_free_table(&z_pos);
  sinfo_unwrap_vector(&vw);
  sinfo_unwrap_vector(&vs);
  sinfo_unwrap_vector(&vo);
  sinfo_free_my_vector(&sx);
  sinfo_free_my_vector(&sy);
  sinfo_unwrap_vector(&vx);
  sinfo_unwrap_vector(&vy);
  sinfo_free_table(&w_tbl);
  sinfo_free_table(&s_tbl);
  sinfo_free_table(&o_tbl);
  sinfo_free_polynomial(&cfit);

  return 0;


}




/**
@name sinfo_table_flag_nan
@memo flag NAN values in a table column
@param t input table
@param label column name
@param min minimum value
@param min maximum value

@return 0 if success, -1 else

*/

static int
sinfo_table_set_nan_out_min_max(cpl_table** t,
                                const char* c,
                                const double min,
                                const double max)

{

  int sz=0;
  int i=0;
  double* pt=NULL;

  check_nomsg(sz=cpl_table_get_nrow(*t));
  check_nomsg(pt=cpl_table_get_data_double(*t,c));
  for(i=0;i<sz;i++) {
    if(pt[i] < min || pt[i] > max) {
      check_nomsg(cpl_table_set_invalid(*t ,c,i));
    }
  }

  return 0;

 cleanup:

  return -1;


}

/**
@name sinfo_table_flag_nan
@memo flag NAN values in a table column
@param t input table
@param label column name
@return 0 if success, -1 else

*/

static int
sinfo_table_flag_nan(cpl_table** t,const char* label)
{

  int sz=0;
  int i=0;
  double* pt=NULL;

  check_nomsg(sz=cpl_table_get_nrow(*t));
  check_nomsg(pt=cpl_table_get_data_double(*t,label));
  for(i=0;i<sz;i++) {
    if(irplib_isnan(pt[i])) {
      check_nomsg(cpl_table_set_invalid(*t ,label,i));
    }
  }

  return 0;

 cleanup:

  return -1;
}

/**
@name sinfo_table_sky_obj_flag_nan
@memo flag raws for which both object and sky table intensity is NAN
@param s sky table
@param o obj table
@param w wavelength table
@return 0 if success, -1 else
*/

static int
sinfo_table_sky_obj_flag_nan(cpl_table** s,cpl_table** o, cpl_table** w)
{

  int no=0;
  int ns=0;
  int nw=0;
  int ni=0;

  int i=0;
  double* po=NULL;
  double* ps=NULL;
  double* pw=NULL;

  check_nomsg(no=cpl_table_get_nrow(*o));
  check_nomsg(ns=cpl_table_get_nrow(*s));
  check_nomsg(nw=cpl_table_get_nrow(*w));
  if(no != ns || ns != nw || no != nw) {
    sinfo_msg_error("different input tables sizes");
    goto cleanup;
  }
  check_nomsg(po=cpl_table_get_data_double(*o,"INT"));
  check_nomsg(ps=cpl_table_get_data_double(*s,"INT"));
  check_nomsg(pw=cpl_table_get_data_double(*w,"WAVE"));

  for(i=0;i<no;i++) {
    if( (0==cpl_table_is_valid(*o,"INT",i)) ||
        irplib_isnan(po[i]) || irplib_isnan(ps[i]) || irplib_isnan(pw[i]) ) {
      check_nomsg(cpl_table_set_invalid(*o ,"INT",i));
      check_nomsg(cpl_table_set_invalid(*s ,"INT",i));
      check_nomsg(cpl_table_set_invalid(*w ,"WAVE",i));
      //sinfo_msg_debug("Flagged raw %d",i);
      ni++;
    }
  }

  return no-ni;

 cleanup:

  return -1;
}

/*
static void
sinfo_shift_sky(const int x,const int y)
{

  //To remove compilation warnings
  ck0_nomsg(x);
  ck0_nomsg(y);

  // shift sky spectrum of a given amount
  if (max(abs(z_fit))/cdelts < 0.01) {
    sinfo_msg("shift <0.01 pixels will not be applied");
  } else {
    sinfo_msg("shifting sky cube by mean of %f pix wrt object",
             cpl_table_column_mean(z_fit,"VALUE")/cdelto);
    sinfo_msg("this will take a couple of minutes...");
    z_good = where(finite(int_sky));
    new_sky = spline(lambda[z_good]-z_mean,int_sky[z_good],lambda);
    int_sky = new_sky;
    sky_out = dblarr(xsize,ysize,zsize) + !values.f_nan;
    for (ix=0; ix<xsize;ix++) {
      for (iy=0;iy<ysize;iy++) {
    old_sky = reform(sky[ix,iy,*]);
    z_good = where(finite(old_sky),z_good_i);
    if (z_good_i > 0) {
          new_sky= spline(lambda[z_good]-z_fit[z_good],old_sky[z_good],lambda);
          new_fin= where(finite(new_sky,/infinity) ||
                          finite(old_sky,/nan),newfin_i);
      if (new_fin_i > 0) new_sky[new_fin] = !values.f_nan;
      sky_out[ix,iy,*] = new_sky;
    }
      }
    }
    sky = sky_out;
  }
 cleanup:
  return;

}
  */
/**
@name  sinfo_optimise_sky_sub
@memo  optimse sky subtraction
@param wtol    tolerance on wavelength for selection
@param line_hw line half width
@param method  optimization method to determine ratio line(obj)/line(sky)
@param lambda  wavelength range
@param lr41    4-1 transition wavelength range table
@param lr52    5-2 transition wavelength range table
@param lr63    6-3 transition wavelength range table
@param lr74    7-4 transition wavelength range table
@param lr02    0-2 transition wavelength range table
@param lr85    8-5 transition wavelength range table
@param lr20    2-0 transition wavelength range table
@param lr31    3-1 transition wavelength range table
@param lr42    4-2 transition wavelength range table
@param lr53    5-3 transition wavelength range table
@param lr64    6-4 transition wavelength range table
@param lr75    7-5 transition wavelength range table
@param lr86    8-6 transition wavelength range table
@param lr97    9-7 transition wavelength range table
@param lr00    0-0 transition wavelength range table
@param int_obj input/output(corrected) object spectrum
@param int_sky input/output(corrected) sky spectrum
@param rscale  output computed ratio table
*/
void
sinfo_optimise_sky_sub(const double wtol,
                       const double line_hw,
                       const int method,
                       const int do_rot,
                       cpl_table* lrange,
                       cpl_table* lambda,
                       cpl_table* lr41,
                       cpl_table* lr52,
                       cpl_table* lr63,
                       cpl_table* lr74,
                       cpl_table* lr02,
                       cpl_table* lr85,
                       cpl_table* lr20,
                       cpl_table* lr31,
                       cpl_table* lr42,
                       cpl_table* lr53,
                       cpl_table* lr64,
                       cpl_table* lr75,
                       cpl_table* lr86,
                       cpl_table* lr97,
                       cpl_table* lr00,
                       cpl_table** int_obj,
                       cpl_table** int_sky,
                       cpl_table** rscale)

{

  int npixw=2*line_hw; //full width in pixels of unresolved emission line
  cpl_array* do_hk=NULL;
  cpl_array* rfit=NULL;
  int i=0;
  cpl_table* sky_lr=NULL;
  cpl_table* obj_lr=NULL;
  cpl_table* wav_lr=NULL;
  double sky_med=0;
  double sky_sdv=0;
  int lr41_i=0;
  int lr52_i=0;
  int lr63_i=0;
  int lr74_i=0;
  int lr02_i=0;
  int lr85_i=0;
  int lr20_i=0;
  int lr31_i=0;
  int lr42_i=0;
  int lr53_i=0;
  int lr64_i=0;
  int lr75_i=0;
  int lr86_i=0;
  int lr97_i=0;
  int lr00_i=0;

  int xxx1_i=0;
  int status=0;
  int finite_pix_i=0;
  double sky_thresh=0.;

  cpl_table* rat_sky=NULL;

  cpl_table* xxx1=NULL;
  cpl_table* xxx2=NULL;
  cpl_table* xxx1_sub=NULL;
  cpl_table* line_regions=NULL;
  cpl_table* cont_regions=NULL;
  int line_i=0;
  int cont_i=0;
  double fmed=0;
  double fsdv=0;
  cpl_table* fline_res=NULL;
  int fclip_i=0;
  int fline_i=0;
  cpl_table* rscale0=NULL;
  double r=0;
  cpl_table* obj_cont=NULL;
  cpl_table* sky_cont=NULL;
  cpl_table* obj_line=NULL;
  cpl_table* sky_line=NULL;


  //Rotational parameters
  int low_pos_i=0;
  int med_pos_i=0;
  int hi_pos_i=0;

  cpl_table* finite_pix=NULL;
  cpl_table* tmp_tbl=NULL;

  cpl_table* low_scale=NULL;
  cpl_table* med_scale=NULL;
  cpl_table* hi_regions=NULL;

  cpl_table* low_regions=NULL;
  cpl_table* med_regions=NULL;


  cpl_table* low_pos=NULL;
  cpl_table* med_pos=NULL;
  cpl_table* hi_pos=NULL;
  cpl_table* llr_xxx1=NULL;

  double rhi=0;
  double rmed=0;
  double rlow=0;

  double min_lrange=0;
  double max_lrange=0;

  int nrow=0;


  double w_rot_low[NROT]={1.00852,1.03757,1.09264,1.15388,1.22293,
                          1.30216,1.45190,1.52410,1.60308,1.69037,
                          1.78803,2.02758,2.18023,1.02895,1.08343,
                          1.14399,1.21226,1.29057,1.43444,1.50555,
                          1.58333,1.66924,1.76532,2.00082,2.15073};


  double w_rot_med[NROT]={1.00282,1.02139,1.04212,1.07539,1.09753,
                          1.13542,1.15917,1.20309,1.22870,1.28070,
                          1.30853,1.41861,1.46048,1.48877,1.53324,
                          1.56550,1.61286,1.65024,1.70088,1.74500,
                          1.79940,1.97719,2.04127,2.12496,2.19956};



  check_nomsg(do_hk = cpl_array_new(NBOUND+1,CPL_TYPE_INT));
  check_nomsg(rfit  = cpl_array_new(NBOUND+1,CPL_TYPE_DOUBLE));

  lr41_i=cpl_table_get_nrow(lr41);
  lr52_i=cpl_table_get_nrow(lr52);
  lr63_i=cpl_table_get_nrow(lr63);
  lr74_i=cpl_table_get_nrow(lr74);
  lr02_i=cpl_table_get_nrow(lr02);
  lr85_i=cpl_table_get_nrow(lr85);
  lr20_i=cpl_table_get_nrow(lr20);
  lr31_i=cpl_table_get_nrow(lr31);
  lr42_i=cpl_table_get_nrow(lr42);
  lr53_i=cpl_table_get_nrow(lr53);
  lr64_i=cpl_table_get_nrow(lr64);
  lr75_i=cpl_table_get_nrow(lr75);
  lr86_i=cpl_table_get_nrow(lr86);
  lr97_i=cpl_table_get_nrow(lr97);
  check_nomsg(lr00_i=cpl_table_get_nrow(lr00));

  cpl_array_set_int(do_hk,0,lr41_i);
  cpl_array_set_int(do_hk,1,lr52_i);
  cpl_array_set_int(do_hk,2,lr63_i);
  cpl_array_set_int(do_hk,3,lr74_i);
  cpl_array_set_int(do_hk,4,lr02_i);
  cpl_array_set_int(do_hk,5,lr85_i);
  cpl_array_set_int(do_hk,6,lr20_i);
  cpl_array_set_int(do_hk,7,lr31_i);
  cpl_array_set_int(do_hk,8,lr42_i);
  cpl_array_set_int(do_hk,9,lr53_i);
  cpl_array_set_int(do_hk,10,lr64_i);
  cpl_array_set_int(do_hk,11,lr75_i);
  cpl_array_set_int(do_hk,12,lr86_i);
  cpl_array_set_int(do_hk,13,lr97_i);
  check_nomsg(cpl_array_set_int(do_hk,14,lr00_i));

  check_nomsg(rscale0=cpl_table_duplicate(*int_sky));
  check_nomsg(cpl_table_new_column(rscale0,"RATIO",CPL_TYPE_DOUBLE));
  check_nomsg(nrow=cpl_table_get_nrow(rscale0));
  check_nomsg(cpl_table_fill_column_window(rscale0,"RATIO",0,nrow,0));

  // For each range extract proper: obj, sky, wave spectra
  for (i=0;i<NBOUND+1;i++) {
    if (cpl_array_get_int(do_hk,i,&status) > 0) {


      switch(i) {


      case 0:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr41,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;

      case 1:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr52,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;

      case 2:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr63,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;

      case 3:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr74,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;

      case 4:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr02,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;

      case 5:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr85,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;
      case 6:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr20,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;
      case 7:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr31,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;
      case 8:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr42,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;
      case 9:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr53,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;
      case 10:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr64,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;
      case 11:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr75,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;
      case 12:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr86,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;
      case 13:
        ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr97,wtol,
                        &obj_lr,&sky_lr,&wav_lr));
	break;
      case 14:
         ck0_nomsg(sinfo_get_obj_sky_wav_sub(*int_obj,*int_sky,lambda,lr00,
                         wtol,&obj_lr,&sky_lr,&wav_lr));
	 break;
      default:
        sinfo_msg_error("case not supported");
	goto cleanup;
      }
      if(sky_lr == NULL || obj_lr == NULL || wav_lr == NULL) {
	finite_pix_i=0;
        sinfo_msg("no good pix left");
      } else {
	//AMO: the following 2 seems to be critical for robustness
	//check_nomsg(cpl_table_save(sky_lr,NULL,NULL,"out_skylr0.fits",
	//			   CPL_IO_DEFAULT));
	//check_nomsg(cpl_table_save(obj_lr,NULL,NULL,"out_objlr0.fits",
	//			   CPL_IO_DEFAULT));
	//check_nomsg(cpl_table_save(wav_lr,NULL,NULL,"out_wavlr0.fits",
	//			   CPL_IO_DEFAULT));



	check_nomsg(finite_pix_i=sinfo_table_sky_obj_flag_nan(&sky_lr,
                                                              &obj_lr,
                                                              &wav_lr));


      }


      if (finite_pix_i > npixw) {
        // identify sky lines
        //sinfo_msg("finite_pix_i=%d",finite_pix_i);
        check_nomsg(cpl_table_erase_invalid(obj_lr));
	check_nomsg(cpl_table_erase_invalid(sky_lr));
	check_nomsg(cpl_table_erase_invalid(wav_lr));


	check_nomsg(sky_med=cpl_table_get_column_median(sky_lr,"INT"));
	check_nomsg(sky_sdv=cpl_table_get_column_stdev(sky_lr,"INT"));
        check_nomsg(cpl_table_select_all(sky_lr));
        sky_thresh=sky_med+sky_sdv;
        //sinfo_msg("sky_thresh=%f",sky_thresh);
        check_nomsg(xxx1_i=cpl_table_and_selected_double(sky_lr,"INT",
                           CPL_GREATER_THAN,sky_thresh));
	check_nomsg(xxx1 = cpl_table_extract_selected(sky_lr));
        check_nomsg(cpl_table_select_all(sky_lr));

	if (xxx1_i > 0) {
	  //separate line and continuum regions
          //by convolving with a hat region of large as a line
          //sinfo_msg("xxx1_i=%d",xxx1_i);
	  check_nomsg(xxx2 = cpl_table_duplicate(sky_lr));
          //check_nomsg(cpl_table_save(sky_lr,NULL,NULL,
          //                           "out_skylr.fits",CPL_IO_DEFAULT));
          //check_nomsg(cpl_table_save(obj_lr,NULL,NULL,
          //                             "out_objlr.fits",CPL_IO_DEFAULT));
          //check_nomsg(cpl_table_save(xxx2,NULL,NULL,
          //                             "out_xxx2_0.fits",CPL_IO_DEFAULT));
          ck0_nomsg(sinfo_table_threshold(&xxx2,"INT",sky_thresh,
                                          sky_thresh,0.,10.));
          //check_nomsg(cpl_table_save(xxx2,NULL,NULL,
          //                             "out_xxx2_1.fits",CPL_IO_DEFAULT));


          /* TODO
      xxx2[xxx1] = 10.;
      */
          //sinfo_msg("npixw/2=%d",npixw);
	  check_nomsg(sinfo_convolve_kernel(&xxx2,npixw/2));
          //check_nomsg(cpl_table_save(xxx2,NULL,NULL,
          //                             "out_xxx2_2.fits",CPL_IO_DEFAULT));

          // get line_regions
          check_nomsg(line_i=cpl_table_and_selected_double(xxx2,"CNV",
                                                         CPL_GREATER_THAN,0));

          check_nomsg(line_regions=cpl_table_extract_selected(xxx2));

          //check_nomsg(cpl_table_save(line_regions,NULL,NULL,
          //"out_line_regions.fits",CPL_IO_DEFAULT));

	  check_nomsg(cpl_table_erase_column(line_regions,"INT"));
	  check_nomsg(cpl_table_erase_column(line_regions,"CNV"));

          check_nomsg(cpl_table_select_all(xxx2));

          // get cont_regions
          check_nomsg(cont_i=cpl_table_and_selected_double(xxx2,"CNV",
                                                           CPL_EQUAL_TO,0));
          check_nomsg(cont_regions=cpl_table_extract_selected(xxx2));

          //check_nomsg(cpl_table_save(line_regions,NULL,NULL,
          //"out_cont_regions.fits",CPL_IO_DEFAULT));

	  check_nomsg(cpl_table_erase_column(cont_regions,"INT"));
	  check_nomsg(cpl_table_erase_column(cont_regions,"CNV"));
          check_nomsg(cpl_table_select_all(xxx2));
	  sinfo_free_table(&xxx2);


	  if (line_i >= 3 && cont_i >= 3) {
            //If we have enough line points and continuum points
             //sinfo_msg("line_i=%d cont_i=%d",line_i,cont_i);
	    if (i == 0) sinfo_msg("optimising 4-1 transitions");
            if (i == 1) sinfo_msg("optimising 5-2 transitions");
            if (i == 2) sinfo_msg("optimising 6-3 transitions");
            if (i == 3) sinfo_msg("optimising 7-4 transitions");
            if (i == 4) sinfo_msg("optimising 0-2 transitions");
            if (i == 5) sinfo_msg("optimising 8-5 transitions");
            if (i == 6) sinfo_msg("optimising 2-0 transitions");
            if (i == 7) sinfo_msg("optimising 3-1 transitions");
            if (i == 8) sinfo_msg("optimising 4-2 transitions");
            if (i == 9) sinfo_msg("optimising 5-3 transitions");
            if (i == 10) sinfo_msg("optimising 6-4 transitions");
            if (i == 11) sinfo_msg("optimising 7-5 transitions");
            if (i == 12) sinfo_msg("optimising 8-6 transitions");
            if (i == 13) sinfo_msg("optimising 9-7 transitions");
            if (i == 14) sinfo_msg("optimising final bit");
	    // Fit the object profile='fline_res' of the sky line residuals
            // left after proper scaled sky spectrum lines (and continua)
            // subtraction. Then determines median and stdev to flag outliers

	    //Free memory for each loop
            sinfo_free_table(&obj_cont);
            sinfo_free_table(&sky_cont);
            sinfo_free_table(&sky_line);
            sinfo_free_table(&obj_line);
	    //Identify obj lines and continuum, same for sky
            cknull_nomsg(obj_line=sinfo_table_select_range(obj_lr,line_regions,
                                  wtol));


            cknull_nomsg(sky_line=sinfo_table_select_range(sky_lr,line_regions,
                                  wtol));
            cknull_nomsg(obj_cont=sinfo_table_select_range(obj_lr,cont_regions,
                                  wtol));
             cknull_nomsg(sky_cont=sinfo_table_select_range(sky_lr,cont_regions,
                                  wtol));

	    //Here was commented
            //check_nomsg(cpl_table_save(line_regions,NULL,NULL,
            //            "out_line.fits",CPL_IO_DEFAULT));
            //check_nomsg(cpl_table_save(cont_regions,NULL,NULL,
            //            "out_cont.fits",CPL_IO_DEFAULT));
            //check_nomsg(cpl_table_save(obj_cont,NULL,NULL,
            //            "out_obj_cont.fits",CPL_IO_DEFAULT));
            //check_nomsg(cpl_table_save(obj_line,NULL,NULL,
            //            "out_obj_line.fits",CPL_IO_DEFAULT));
            //check_nomsg(cpl_table_save(sky_line,NULL,NULL,
            //            "out_sky_line.fits",CPL_IO_DEFAULT));
            //check_nomsg(cpl_table_save(sky_cont,NULL,NULL,
            //            "out_sky_cont.fits",CPL_IO_DEFAULT));


            sinfo_free_table(&fline_res);
            //FIXME: in some cases obj_cont is empty
            //sinfo_msg("first line ratio determination");
            ck0_nomsg(sinfo_get_line_ratio(obj_line,obj_cont,
					   sky_line,sky_cont,method,&r));
	    sinfo_msg("1st Line ratio %g",r);


            if(cpl_table_get_nrow(obj_cont) > 0) {
               check_nomsg(fline_res=sinfo_table_interpol(obj_line,obj_cont,
                                                          sky_line,sky_cont,
                                                          r));
	    } else {
              check_nomsg(fline_res=cpl_table_duplicate(obj_line));
	    }

            // check if there are outliers
            cpl_table_select_all(fline_res);
            check_nomsg(fmed = cpl_table_get_column_median(fline_res,"INT"));
            check_nomsg(fsdv = cpl_table_get_column_stdev(fline_res,"INT"));

            check_nomsg(cpl_table_duplicate_column(fline_res,"AINT",
                                                   fline_res,"INT"));
            check_nomsg(cpl_table_multiply_columns(fline_res,"AINT","INT"));
            check_nomsg(cpl_table_power_column(fline_res,"AINT",0.5));
            check_nomsg(fclip_i=cpl_table_and_selected_double(fline_res,"AINT",
                                                  CPL_GREATER_THAN,
                                                  fmed+3*fsdv));

            check_nomsg(cpl_table_select_all(fline_res));


	    if (fclip_i > 0) {
              // do a k-sigma clip to select a better line region
              //sinfo_msg("fclip_i=%d",fclip_i);
              //Find again line_regions
              check_nomsg(line_i=cpl_table_and_selected_double(fline_res,
							       "AINT",
							       CPL_NOT_GREATER_THAN,
							       fmed+3*fsdv));
	      // get new (better) line_regions
	      sinfo_free_table(&line_regions);
              //sinfo_msg("line_i=%d",line_i);
              check_nomsg(line_regions=cpl_table_extract_selected(fline_res));
              check_nomsg(cpl_table_erase_column(line_regions,"INT"));
              check_nomsg(cpl_table_erase_column(line_regions,"AINT"));

	      sinfo_free_table(&obj_line);
	      sinfo_free_table(&sky_line);

	      //check_nomsg(cpl_table_save(obj_lr,NULL,NULL,
              //                           "out_obj_lr.fits",CPL_IO_DEFAULT));
	      //check_nomsg(cpl_table_save(line_regions,NULL,NULL,
              //                           "out_line_regions.fits",
              //                           CPL_IO_DEFAULT));




	      // The following 2 may return an error so we do not check and
	      // later we reset the error
              obj_line=sinfo_table_select_range(obj_lr,line_regions,wtol);
              sky_line=sinfo_table_select_range(sky_lr,line_regions,wtol);
              fline_i=cpl_table_get_nrow(line_regions);

              //sinfo_msg("fline_i=%d",fline_i);
              if(fline_i>=3) {
                // repeat the determination of the line ratio
                //sinfo_msg("second line ratio determination");
                ck0_nomsg(sinfo_get_line_ratio(obj_line,obj_cont,
                                                sky_line,sky_cont,method,&r));

		sinfo_msg("2nd Line ratio %g",r);

	      } else {
                cpl_error_reset();
	      }

	      sinfo_free_table(&sky_line);
	      sinfo_free_table(&obj_line);
	    }

            cpl_msg_info(cpl_func,"use %" CPL_SIZE_FORMAT 
                         " pixels for line and %" CPL_SIZE_FORMAT 
                         " for continuum estimation",
            cpl_table_get_nrow(line_regions),cpl_table_get_nrow(cont_regions));

	    sinfo_msg("OH spectrum scaling = %f ",r);
            check_nomsg(cpl_array_set_double(rfit,i,r));
            ck0_nomsg(sinfo_table_set(&rscale0,wav_lr,r,wtol));

	  } /* end if line_i */
        } /* end if xxx1_i */
     } /* end finite_pix_i */

    }

    sinfo_free_table(&xxx1);
    sinfo_free_table(&xxx2);
    sinfo_free_table(&sky_lr);
    sinfo_free_table(&obj_lr);
    sinfo_free_table(&wav_lr);

    sinfo_free_table(&line_regions);
    sinfo_free_table(&cont_regions);

  } /* end for loop on i */

  sinfo_free_array(&do_hk);
  sinfo_free_array(&rfit);

  //sinfo_msg("n scale=%d",cpl_table_get_nrow(rscale0));
  //check_nomsg(cpl_table_save(rscale0,NULL,NULL,
  //                                   "out_rscale0.fits",CPL_IO_DEFAULT));

  check_nomsg(cpl_table_select_all(rscale0));
 /* TODO: here one has to implementa an interpol function
  check_nomsg(range0_i=cpl_table_and_selected_double(rscale0,"RATIO",
                                                     CPL_NOT_EQUAL_TO,0));
 */
  check_nomsg(*rscale = cpl_table_extract_selected(rscale0));
  sinfo_free_table(&rscale0);


  check_nomsg(rat_sky=cpl_table_duplicate(*int_sky));
  check_nomsg(cpl_table_duplicate_column(rat_sky,"RATIO",*rscale,"RATIO"));
  check_nomsg(cpl_table_duplicate_column(*int_obj,"COR_FCT_VIB",
					 *rscale,"RATIO"));
  //check_nomsg(cpl_table_save(rat_sky,NULL,NULL,
  //                           "rat_sky0.fits",CPL_IO_DEFAULT));
  check_nomsg(cpl_table_multiply_columns(rat_sky,"INT","RATIO"));


  //check_nomsg(cpl_table_save(*int_obj,NULL,NULL,
  //                             "out_obj0.fits",CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(*int_sky,NULL,NULL,
  //                             "out_sky0.fits",CPL_IO_DEFAULT));

  /*
  check_nomsg(cpl_table_duplicate_column(*int_obj,"INTC",*int_obj,"INT"));
  check_nomsg(cpl_table_duplicate_column(*int_obj,"SKYC",*int_sky,"INTC"));
  check_nomsg(cpl_table_subtract_columns(*int_obj,"INTC","SKYC"));
  */

  // do simple rotational correction
  if (do_rot == 1) {

    //finite_pix = where(finite(int_sky) && finite(int_obj),finite_pix_i);
    check_nomsg(min_lrange=cpl_table_get_column_min(lrange,"WAVE"));
    check_nomsg(max_lrange=cpl_table_get_column_max(lrange,"WAVE"));
    //sinfo_msg("min_lrange=%g max_lrange=%g",min_lrange,max_lrange);
    //check_nomsg(finite_pix_i=sinfo_table_sky_obj_flag_nan(&rat_sky,
    //                                                      int_obj,
    //                                                      &lambda));
    check_nomsg(finite_pix_i=sinfo_table_sky_obj_flag_nan(int_sky,
                                                          int_obj,
                                                          &lambda));


    check_nomsg(finite_pix=cpl_table_duplicate(lambda));
    //TODO: lambda invalid values need to be reset to valid (?)

    check_nomsg(cpl_table_erase_invalid(finite_pix));


    if (finite_pix_i > npixw) {

      //finite_pix = finite_pix[where(finite_pix > min(lrange) &&
      //                              finite_pix < max(lrange))];

      check_nomsg(cpl_table_and_selected_double(finite_pix,"WAVE",
                                                CPL_GREATER_THAN,
                                                min_lrange));

      check_nomsg(cpl_table_and_selected_double(finite_pix,"WAVE",
                                                CPL_LESS_THAN,
                                                max_lrange));



      check_nomsg(tmp_tbl=cpl_table_extract_selected(finite_pix));
      sinfo_free_table(&finite_pix);
      check_nomsg(finite_pix=cpl_table_duplicate(tmp_tbl));
      sinfo_free_table(&tmp_tbl);
      sinfo_free_table(&sky_lr);
      sinfo_free_table(&obj_lr);
      sinfo_free_table(&wav_lr);


      cknull(sky_lr=sinfo_table_select_range(rat_sky,finite_pix,wtol),
         "extracting sky sub range");
      cknull(obj_lr=sinfo_table_select_range(*int_obj,finite_pix,wtol),
         "extracting obj sub range");
      cknull(wav_lr=sinfo_table_select_range(lambda,finite_pix,wtol),
         "extracting sky sub range");


      //check_nomsg(cpl_table_save(rat_sky,NULL,NULL,
      //                             "out_rat_sky.fits",CPL_IO_DEFAULT));
      //check_nomsg(cpl_table_save(finite_pix,NULL,NULL,
      //                             "out_finite_pix.fits",CPL_IO_DEFAULT));
      //check_nomsg(cpl_table_save(sky_lr,NULL,NULL,
      //                             "out_sky_lr.fits",CPL_IO_DEFAULT));
      //check_nomsg(cpl_table_save(obj_lr,NULL,NULL,
      //                             "out_obj_lr.fits",CPL_IO_DEFAULT));
      //check_nomsg(cpl_table_save(wav_lr,NULL,NULL,
      //                             "out_wav_lr.fits",CPL_IO_DEFAULT));

      //The following may fail (sky_lr may be empty) so we do not check
      if(1 == cpl_table_has_valid(sky_lr,"INT")) {
	check_nomsg(sky_med=cpl_table_get_column_median(sky_lr,"INT"));
	check_nomsg(sky_sdv=cpl_table_get_column_stdev(sky_lr,"INT"));
	sky_thresh=sky_med+sky_sdv;
        //xxx1 = where(sky_lr > median(sky_lr)+stddev(sky_lr),xxx1_i);
        check_nomsg(xxx1_i=cpl_table_and_selected_double(sky_lr,"INT",
                           CPL_GREATER_THAN,sky_thresh));
        check_nomsg(xxx1=cpl_table_extract_selected(sky_lr));
        check_nomsg(cpl_table_select_all(sky_lr));
      } else {
        xxx1_i=0;
      }
      if (xxx1_i > 0) {
        sinfo_msg("xxx1_i=%d",xxx1_i);

        sinfo_msg("wav_lr wmin=%g wmax=%g",
          cpl_table_get_column_min(wav_lr,"WAVE"),
          cpl_table_get_column_max(wav_lr,"WAVE"));

        cknull_nomsg(llr_xxx1=sinfo_table_select_range(wav_lr,xxx1,wtol));
        //check_nomsg(cpl_table_save(wav_lr,NULL,NULL,
        //                             "out_llr_xxx1.fits",CPL_IO_DEFAULT));

        cknull(low_pos=sinfo_find_rot_waves(w_rot_low,npixw,wtol,llr_xxx1),
	       "Determining low positions");


        check_nomsg(low_pos_i=cpl_table_get_nrow(low_pos));
        //check_nomsg(cpl_table_dump(low_pos,0,low_pos_i,stdout));
        cknull(med_pos=sinfo_find_rot_waves(w_rot_med,npixw,wtol,llr_xxx1),
               "Determining med positions");
        check_nomsg(med_pos_i=cpl_table_get_nrow(med_pos));


        //check_nomsg(cpl_table_dump(med_pos,0,med_pos_i,stdout));

        //TODO:
        //hipos = [0]
        //for i=0,n_elements(xxx1)-1 do begin
        //    x1 = where(lowpos eq i,x1_i)
        //    x2 = where(medpos eq i,x2_i)
        //    if (x1_i eq 0 and x2_i eq 0) then hipos = [hipos,i]
        //endfor
        //hipos = hipos[1:n_elements(hipos)-1]
        //TODO: hi_pos=sinfo_find_rot_waves(w_rot_hi,npixw,wtol,wav_lr);


        cknull(hi_pos=sinfo_table_extract_rest(xxx1,low_pos,med_pos,wtol),
           "determining hi position");
        check_nomsg(hi_pos_i=cpl_table_get_nrow(hi_pos));
        //check_nomsg(cpl_table_dump(hi_pos,0,hi_pos_i,stdout));


        //xxx2[xxx1] = 10.;
        check_nomsg(xxx2 = cpl_table_duplicate(sky_lr));
        check_nomsg(nrow=cpl_table_get_nrow(sky_lr));
        //check_nomsg(cpl_table_save(xxx2,NULL,NULL,
        //                             "out_xxx1.fits",CPL_IO_DEFAULT));
        //check_nomsg(cpl_table_save(xxx2,NULL,NULL,
        //                             "out_xxx2_0.fits",CPL_IO_DEFAULT));

        // AMO: Why the following?
        //check_nomsg(cpl_table_fill_column_window(xxx2,"INT",0,nrow,0));

        //xxx2 = convol(xxx2,replicate(1,npixw),/edge_truncate,/center);
        //cont_regions = where(xxx2 == 0,cont_i);
        ck0_nomsg(sinfo_table_threshold(&xxx2,"INT",sky_thresh,
                    sky_thresh,0.,10.));
        sinfo_msg("sky_thresh=%g %g %f",sky_thresh,sky_med,sky_sdv);
        //check_nomsg(cpl_table_save(xxx2,NULL,NULL,
        //                             "out_xxx2_1.fits",CPL_IO_DEFAULT));
        check_nomsg(sinfo_convolve_kernel(&xxx2,npixw/2));

        //check_nomsg(cpl_table_save(xxx2,NULL,NULL,
        //                             "out_xxx2_2.fits",CPL_IO_DEFAULT));
        check_nomsg(cont_i=cpl_table_and_selected_double(xxx2,"CNV",
                             CPL_EQUAL_TO,0));
        check_nomsg(cont_regions=cpl_table_extract_selected(xxx2));

        sinfo_free_table(&xxx2);
        check_nomsg(cpl_table_erase_column(cont_regions,"INT"));
        check_nomsg(cpl_table_erase_column(cont_regions,"CNV"));

        check(low_pos_i=sinfo_get_sub_regions(sky_lr,xxx1,low_pos,wtol,
                         npixw,&low_regions),"failed determining low regions");

        check(med_pos_i=sinfo_get_sub_regions(sky_lr,xxx1,med_pos,wtol,
                         npixw,&med_regions),"failed determining med regions");


        check(hi_pos_i=sinfo_get_sub_regions(sky_lr,xxx1,hi_pos,wtol,
                        npixw,&hi_regions),"failed determining hi regions");
    /*
        sinfo_msg("xxx1: wmin=%g wmax=%g",
                   cpl_table_get_column_min(xxx1,"WAVE"),
                   cpl_table_get_column_max(xxx1,"WAVE"));

        sinfo_msg("low_pos: wmin=%g wmax=%g",
                   cpl_table_get_column_min(low_pos,"WAVE"),
                   cpl_table_get_column_max(low_pos,"WAVE"));
    */
        sinfo_msg("hi_pos_i : %d med_pos_i : %d low_pos_i : %d cont_i: %d",
                   hi_pos_i,     med_pos_i,     low_pos_i,     cont_i);


        if (hi_pos_i >= 3 && med_pos_i >= 3 && low_pos_i >= 3 && cont_i >= 3) {

          //compute line ratio for hi_regions
          ck0_nomsg(sinfo_compute_line_ratio(obj_lr, sky_lr,wtol, method,
                                             hi_regions,cont_regions,&rhi));
          sinfo_msg("high rotational OH scaling %g",rhi);

          //compute line ratio for med_regions
          ck0_nomsg(sinfo_compute_line_ratio(obj_lr, sky_lr,wtol, method,
                                             med_regions,cont_regions,&rmed));

          sinfo_msg("P1(3.5) & R1(1.5) rotational OH scaling %g ",rmed);

          //compute line ratio for med_regions
          ck0_nomsg(sinfo_compute_line_ratio(obj_lr, sky_lr,wtol, method,
                                             low_regions,cont_regions,&rlow));
          sinfo_msg("P1(2.5) & Q1(1.5) rotational OH scaling %g",rlow);

          cknull(low_scale=sinfo_find_rot_waves(w_rot_low,npixw,wtol,lambda),
		 "Determining low scale");



          cknull(med_scale=sinfo_find_rot_waves(w_rot_low,npixw,wtol,lambda),
		 "Determining low scale");
          check_nomsg(cpl_table_multiply_scalar(*rscale,"RATIO",rhi));
          ck0_nomsg(sinfo_table_fill_column_over_range(rscale,med_scale,
                                                  "RATIO",rmed/rhi,wtol));
          ck0_nomsg(sinfo_table_fill_column_over_range(rscale,low_scale,
                                 "RATIO",rlow/rhi,wtol));

	}
      } //xxx1_i > 0
    }//finitepix > npixw
  }//do_rot==1
  //end of new rotational bit
  //check_nomsg(cpl_table_save(*int_obj,NULL,NULL,
  //                             "out_obj.fits",CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(*int_sky,NULL,NULL,
  //                             "out_sky.fits",CPL_IO_DEFAULT));


  check_nomsg(cpl_table_duplicate_column(*int_sky,"INTC",*int_sky,"INT"));
  //sinfo_msg("n sky=%d",cpl_table_get_nrow(*int_sky));
  //sinfo_msg("n scale=%d",cpl_table_get_nrow(*rscale));

  check_nomsg(cpl_table_duplicate_column(*int_obj,"COR_FCT_ALL",
                                         *rscale,"RATIO"));
  check_nomsg(cpl_table_duplicate_column(*int_sky,"RATIO",*rscale,"RATIO"));
  check_nomsg(cpl_table_multiply_columns(*int_sky,"INTC","RATIO"));

  check_nomsg(cpl_table_duplicate_column(*int_obj,"INTC",*int_obj,"INT"));
  //check_nomsg(cpl_table_save(*int_obj,NULL,NULL,
  //                             "out_obj1.fits",CPL_IO_DEFAULT));
  //check_nomsg(cpl_table_save(*int_sky,NULL,NULL,
  //                             "out_sky1.fits",CPL_IO_DEFAULT));

  check_nomsg(cpl_table_duplicate_column(*int_obj,"SKYC",*int_sky,"INTC"));
  check_nomsg(cpl_table_subtract_columns(*int_obj,"INTC","SKYC"));


  check_nomsg(cpl_table_erase_column(*int_sky,"INT"));
  check_nomsg(cpl_table_name_column(*int_sky,"INTC","INT"));



 cleanup:
  sinfo_free_table(&llr_xxx1);
  sinfo_free_table(&hi_pos);
  sinfo_free_table(&low_pos);
  sinfo_free_table(&med_pos);
  sinfo_free_table(&low_regions);
  sinfo_free_table(&med_regions);
  sinfo_free_table(&hi_regions);
  sinfo_free_table(&low_scale);
  sinfo_free_table(&med_scale);


  sinfo_free_table(&finite_pix);
  sinfo_free_table(&xxx1_sub);
  sinfo_free_table(&tmp_tbl);
  sinfo_free_table(&rat_sky);
  sinfo_free_table(&fline_res);
  sinfo_free_table(&sky_cont);
  sinfo_free_table(&obj_cont);
  sinfo_free_table(&obj_line);
  sinfo_free_table(&sky_line);
  sinfo_free_table(&rscale0);
  sinfo_free_table(&xxx1);
  sinfo_free_table(&xxx2);
  sinfo_free_table(&line_regions);
  sinfo_free_table(&cont_regions);
  sinfo_free_table(&sky_lr);
  sinfo_free_table(&obj_lr);
  sinfo_free_table(&wav_lr);
  sinfo_free_array(&rfit);
  sinfo_free_array(&do_hk);
  return;

}
/**
@name sinfo_table_get_index_of_max
@memo find raw index of table column's max
@param t input table
@param name input table column
@param type CPL_TYPE of input table column
@return table raw correspoinding to table max
*/
int
sinfo_table_get_index_of_max(cpl_table* t,const char* name,cpl_type type)
{

  int i=0;
  int result=0;
  int nrow=0;
  int* pi=NULL;
  float* pf=NULL;
  double* pd=NULL;
  double max=0;


  if(t == NULL) {
   cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
   return result;
  }
  max=cpl_table_get_column_max(t,name);
  nrow=cpl_table_get_nrow(t);
  switch(type) {

  case CPL_TYPE_INT:
    pi=cpl_table_get_data_int(t,name);
    for(i=0;i<nrow;i++) {
      if(pi[i]==(int)max) result=i;
    }
    break;
  case CPL_TYPE_FLOAT:
    pf=cpl_table_get_data_float(t,name);
    for(i=0;i<nrow;i++) {
      if(pf[i]==(float)max) result=i;
    }
    break;
  case CPL_TYPE_DOUBLE:
    pd=cpl_table_get_data_double(t,name);
    for(i=0;i<nrow;i++) {
      if(pd[i]==max) result=i;
    }
    break;
  default:
    sinfo_msg_error("Wrong column type");
   cpl_error_set(cpl_func, CPL_ERROR_TYPE_MISMATCH);
   return result;

  }
  return result;
}



/**
@name sinfo_table_get_index_of_val
@memo get raw index corresponding to input table column's value
@param t input table
@param name input table column name
@param val table column value
@param type CPL_TYPE of table column
*/

int
sinfo_table_get_index_of_val(cpl_table* t,
                             const char* name,
                             double val,
                             cpl_type type)
{

  int i=0;
  int result=0;
  int nrow=0;
  int* pi=NULL;
  float* pf=NULL;
  double* pd=NULL;

  if(t == NULL) {
   cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
   return result;
  }

  nrow=cpl_table_get_nrow(t);
  switch(type) {

  case CPL_TYPE_INT:
    pi=cpl_table_get_data_int(t,name);
    for(i=0;i<nrow;i++) {
      if(pi[i]==(int)val) result=i;
    }
    break;
  case CPL_TYPE_FLOAT:
    pf=cpl_table_get_data_float(t,name);
    for(i=0;i<nrow;i++) {
      if(pf[i]==(float)val) result=i;
    }
    break;
  case CPL_TYPE_DOUBLE:
    pd=cpl_table_get_data_double(t,name);
    for(i=0;i<nrow;i++) {
      if(pd[i]==val) result=i;
    }
    break;
  default:
    sinfo_msg_error("Wrong column type");
   cpl_error_set(cpl_func, CPL_ERROR_TYPE_MISMATCH);
   return result;

  }
  return result;
}

/**
@name sinfo_table_column_interpolate
@memo interpolate table column
@param t     input table
@param name  input table column name
@param x     (double) raw value at which the column value need to be estimate
return if success
          table column value at x
       else
          -1
*/

double
sinfo_table_column_interpolate(const cpl_table* t,
                               const char* name,
                               const double x)
{

  double val1=0;
  double val2=0;
  int x1=0;
  int x2=0;
  double m=0;
  double y=0;
  int status=0;
  int nrow=0;
  nrow=cpl_table_get_nrow(t);
  if ((1<x) && (x<nrow-1)) {
    x1=x-1;
    x2=x+1;
  } else if (x<2) {
    x1=0;
    x2=1;
  } else {
    x1=nrow-2;
    x2=nrow-1;
  }
  check_nomsg(val1=cpl_table_get(t,name,x1,&status));
  check_nomsg(val2=cpl_table_get(t,name,x2,&status));

  m=(val2-val1)/(x2-x1);
  y=val1+m*(x-x1);

  return y;

 cleanup:

  return -1;


}

/**
@name sinfo_imagelist_select_range
@memo select imagelist planes corresponding to a given range
@param inp  input im agelist
@param full table specifying full wavelength range
@param good table specifying good wavelength range
@param tol wavelength tolerance
*/
static cpl_imagelist*
sinfo_imagelist_select_range(const cpl_imagelist* inp,
                                  const cpl_table* full,
                                  const cpl_table* good,
                                  const double tol)
{
  cpl_imagelist* out=NULL;
  //int osz=0;
  int isz=0;
  int ksz=0;
  int k=0;
  int i=0;
  int status=0;

  double wave_chk=0;
  double wave_sel=0;

  const cpl_image* img=NULL;


  /* Get Object relevant information */
  /* here one should scan the inp image constructing a wave range from it
     and not from another table */
  //check_nomsg(osz=cpl_table_get_nrow(good));
  check_nomsg(ksz=cpl_imagelist_get_size(inp));
  check_nomsg(isz=cpl_table_get_nrow(good));
  check_nomsg(out=cpl_imagelist_new());


  for(k=0;k<ksz;k++) {
    check_nomsg(img=cpl_imagelist_get_const(inp,k));
    check_nomsg(wave_chk=cpl_table_get(full,"WAVE",k,&status));
    if(i<isz) {
      check_nomsg(wave_sel=cpl_table_get(good,"WAVE",i,&status));
    }
    // insert cubes with wavelengths with appropriate values only
    if(fabs(wave_chk - wave_sel) < tol) {
      check_nomsg(cpl_imagelist_set(out,cpl_image_duplicate(img),i));
      i++;
    }
  }
  if(i==0) {
    sinfo_msg_error("No lines selected");
    goto cleanup;
  }
  return out;

 cleanup:

  return NULL;

}

/**
@name sinfo_table_extract_finite
@memo extract finite values from two tables
@param in1 1st input table
@param in2 2nd input table
@param ou1 1st output table
@param ou2 2nd output table
@return if success 0, else -1
*/
static int
sinfo_table_extract_finite(const cpl_table* in1,
                           const cpl_table* in2,
                                 cpl_table** ou1,
                                 cpl_table** ou2)
{

  int size1=0;
  int size2=0;
  int i=0;
  int ninv1=0;
  int ninv2=0;
  double* pout1=NULL;
  //double* pout2=NULL;

  cknull(in1,"null input image");
  cknull(in2,"null input image");
  cknull_nomsg(*ou1=cpl_table_duplicate(in1));
  cknull_nomsg(*ou2=cpl_table_duplicate(in2));

  check_nomsg(size1=cpl_table_get_nrow(*ou1));
  check_nomsg(size2=cpl_table_get_nrow(*ou2));

  check_nomsg(pout1=cpl_table_get_data_double(*ou1,"VALUE"));
  //check_nomsg(pout2=cpl_table_get_data_double(*ou2,"VALUE"));
  for(i=0;i<size1;i++) {
    if (irplib_isnan(pout1[i])) {
      check_nomsg(cpl_table_set_invalid(*ou1,"VALUE",i));
      check_nomsg(cpl_table_set_invalid(*ou2,"VALUE",i));
    }
  }
  ninv1=cpl_table_count_invalid(*ou1,"VALUE");
  ninv2=cpl_table_count_invalid(*ou2,"VALUE");
  if(ninv1==size1) {
    goto cleanup;
  }
  if(ninv2==size2) {
    goto cleanup;
  }
  check_nomsg(cpl_table_erase_invalid(*ou1));
  check_nomsg(cpl_table_erase_invalid(*ou2));
  return (size1-ninv1);

 cleanup:
  return 0;

}

/**
@name sinfo_image2table
@memo convert an image in a table
@param im input image
@return output table
*/
static cpl_table*
sinfo_image2table(const cpl_image* im)
{
  cpl_table* out=NULL;
  int sx=0;
  int sy=0;
  const double* pim=NULL;
  //double* pval=NULL;
  int i=0;
  int j=0;
  int k=0;

  cknull(im,"input image is NULL");

  check_nomsg(sx=cpl_image_get_size_x(im));
  check_nomsg(sy=cpl_image_get_size_y(im));
  check_nomsg(pim=cpl_image_get_data_double_const(im));
  check_nomsg(out=cpl_table_new(sx*sy));
  check_nomsg(cpl_table_new_column(out,"VALUE",CPL_TYPE_DOUBLE));
  //check_nomsg(pval=cpl_table_get_data_double(out,"VALUE"));

  for(j=0;j<sy;j++) {
    for(i=0;i<sx;i++) {
      /*
      pval[k++]=pim[j*sx+i];
      sinfo_msg("set tab %f",pim[j*sx+i]);
      */
      cpl_table_set_double(out,"VALUE",k++,pim[j*sx+i]);
    }
  }

  return out;
 cleanup:
  sinfo_free_table(&out);
  return NULL;

}
/**
@name sinfo_check_screw_values
@memo check for screw values at sky spectrum edges
@param int_obj  input object spectrum
@param int_sky  input sky object spectrum
@param grange   good wavelength range
@param wtol     wavelength tolerance
*/
int
sinfo_check_screw_values(cpl_table** int_obj,
                         cpl_table** int_sky,
                         cpl_table* grange,
                         const double wtol)
{
  // check for screwy values at ends of spectrum
  cpl_table* xsky=NULL;
  cpl_table* xobj=NULL;

  double sky_min=0;
  double sky_max=0;
  double gsky_min=0;
  double gsky_max=0;
  double obj_min=0;
  double obj_max=0;
  double gobj_min=0;
  double gobj_max=0;

  cknull(*int_sky,"Null input sky spectrum");
  cknull(*int_obj,"Null input obj spectrum");
  cknull(grange,"Null input wavelength range");
  //check_nomsg(cpl_table_save(*int_sky,NULL,NULL,
  //                             "out_grange0.fits",CPL_IO_DEFAULT));
  cknull_nomsg(xsky=sinfo_table_select_range(*int_sky,grange,wtol));
  //check_nomsg(cpl_table_save(xsky,NULL,NULL,
  //                             "out_grange1.fits",CPL_IO_DEFAULT));
  check_nomsg(sky_min=cpl_table_get_column_min(xsky,"INT"));
  check_nomsg(sky_max=cpl_table_get_column_max(xsky,"INT"));
  //sinfo_msg("gskymax=%f gskymin=%f",sky_max,sky_min);

  gsky_max = (sky_max>0) ? sky_max : 0;
  gsky_min = (sky_min<0) ? sky_min : 0;
  //gsky_pos = where(int_sky > 1.*gsky_max || int_sky < 1.*gsky_min,gskypos_i);
  check_nomsg(cpl_table_select_all(*int_sky));
  ck0_nomsg(sinfo_table_set_nan_out_min_max(int_sky,"INT",gsky_min,gsky_max));
  //check_nomsg(cpl_table_save(*int_sky,NULL,NULL,
  //                             "out_gsky_pos.fits",CPL_IO_DEFAULT));

  sinfo_free_table(&xsky);
  //sinfo_msg("gsky_min=%f gsky_max=%f",gsky_min,gsky_max);

  cknull_nomsg(xobj=sinfo_table_select_range(*int_obj,grange,wtol));
  check_nomsg(obj_min=cpl_table_get_column_min(xobj,"INT"));
  check_nomsg(obj_max=cpl_table_get_column_max(xobj,"INT"));
  //check_nomsg(cpl_table_save(xobj,NULL,NULL,"out_xobj.fits",CPL_IO_DEFAULT));
  gobj_max = (obj_max>0) ? obj_max : 0;
  gobj_min = (obj_min<0) ? obj_min : 0;
  //gobj_pos=where(int_obj > 1.*gobj_max || int_obj < 1.*gobj_min,gobj_pos_i);
  check_nomsg(cpl_table_select_all(*int_obj));
  ck0_nomsg(sinfo_table_set_nan_out_min_max(int_obj,"INT",gobj_min,gobj_max));
  //check_nomsg(cpl_table_save(*int_obj,NULL,NULL,
  //              "out_gobj_pos.fits",CPL_IO_DEFAULT));
  //sinfo_msg("gobj_min=%f gobj_max=%f",gobj_min,gobj_max);
  sinfo_free_table(&xobj);

  return 0;
 cleanup:
  sinfo_free_table(&xsky);
  sinfo_free_table(&xobj);

  return -1;


}



/**
@name sinfo_table_fill_column_over_range
@param inp input table
@param ref input table specifying the wavelength range
@param col input table column
@param tol input tolerance
@return pointer to a new allocated table corresponding to the selection
*/

static int
sinfo_table_fill_column_over_range(cpl_table** inp,
                                   const cpl_table* ref,
                                   const char* col,
                                   const double val,
                                   const double tol)
{

  int i=0;
  int k=0;
  int nref=0;
  int ninp=0;

  double* pwin=NULL;
  double* pcin=NULL;
  const double* pwrf=NULL;

  cknull(inp,"null input table");
  cknull(ref,"null reference table");

  check_nomsg(ninp=cpl_table_get_nrow(*inp));
  check_nomsg(nref=cpl_table_get_nrow(ref));
  check_nomsg(pwin=cpl_table_get_data_double(*inp,"WAVE"));
  check_nomsg(pcin=cpl_table_get_data_double(*inp,col));
  check_nomsg(pwrf=cpl_table_get_data_double_const(ref,"WAVE"));

  k=0;
  i=0;
  /*
  sinfo_msg("ninp=%d nref=%d",ninp,nref);
  sinfo_msg("winp(0)=%f wref(0)=%f tol=%f diff=%f",
            pwin[0],pwrf[0],tol,fabs(pwin[0]-pwrf[0]));
  */
  if(pwin[0]<=pwrf[0]) {
    //sinfo_msg("case 1");
    for(i=0;i<ninp;i++) {
      if(k<nref) {
    /*
        sinfo_msg("case1: %f %f %f %f %d %d",
                  fabs(pwin[i] - pwrf[k]),tol,pwin[i],pwrf[k],i,k);
    */
    if(fabs(pwin[i] - pwrf[k])< tol) {
      pcin[i]=val;
      k++;
    }
      }
    }
  } else {

    //pwin[0]>pwrf[0]
    //sinfo_msg("case 2");
    for(k=0;k<nref;k++) {
      if(i<ninp) {
    /*
        sinfo_msg("case2: %f %f %f %f %d %d",
                  fabs(pwin[i] - pwrf[k]),tol,pwin[i],pwrf[k],i,k);
    */
    if(fabs(pwin[i] - pwrf[k])< tol) {
      pcin[i]=val;
      i++;
    }
      }
    }
  }

  return 0;

 cleanup:
  return -1;

}


/**
@name sinfo_table_select_range
@param inp input table
@param ref input table specifying the wavelength range
@param tol input tolerance
@return pointer to a new allocated table corresponding to the selection
*/

static cpl_table*
sinfo_table_select_range(cpl_table* inp, cpl_table* ref,const double tol)
{

  cpl_table* out=NULL;
  int ninp=0;
  int nref=0;
  int nout=0;

  int i=0;
  int k=0;
  double* pou;
  double* prf;
  double wmin=0;
  double wmax=0;
  cpl_table* tmp=NULL;
  cknull(inp,"null input table");
  cknull(ref,"null reference table");

  check_nomsg(ninp=cpl_table_get_nrow(inp));
  check_nomsg(nref=cpl_table_get_nrow(ref));
  if(ninp != nref) {
    //sinfo_msg_warning("ninp != nref");
    check_nomsg(wmin=cpl_table_get_column_min(ref,"WAVE"));
    check_nomsg(wmax=cpl_table_get_column_max(ref,"WAVE"));
    //sinfo_msg_debug("wmin=%f wmax=%f",wmin,wmax);
    cpl_table_select_all(inp);
    check_nomsg(ninp=cpl_table_and_selected_double(inp,"WAVE",
                                                   CPL_NOT_LESS_THAN,wmin));
    check_nomsg(tmp=cpl_table_extract_selected(inp));
    check_nomsg(ninp=cpl_table_and_selected_double(tmp,"WAVE",
                                                   CPL_NOT_GREATER_THAN,wmax));
    check_nomsg(out=cpl_table_extract_selected(tmp));
    sinfo_free_table(&tmp);
  } else {
    check_nomsg(out=cpl_table_duplicate(inp));
  }

  check_nomsg(nout=cpl_table_get_nrow(out));
  if(nout == 0) {
    //sinfo_msg("nout=%d",nout);
    goto cleanup;
  }
  tmp=cpl_table_duplicate(out);
  sinfo_free_table(&out);
  check_nomsg(pou=cpl_table_get_data_double(tmp,"WAVE"));
  check_nomsg(prf=cpl_table_get_data_double(ref,"WAVE"));

  check_nomsg(cpl_table_new_column(tmp,"FLAG",CPL_TYPE_INT));
  check_nomsg(cpl_table_fill_column_window(tmp,"FLAG",0,nout,-1));

  k=0;
  i=0;

  //sinfo_msg_debug("nout=%d nref=%d",nout,nref);
  //sinfo_msg_debug("wout(0)=%f wref(0)=%f tol=%f diff=%f",
  //          pou[0],prf[0],tol,fabs(pou[0]-prf[0]));

  if(pou[0]<=prf[0]) {
    //sinfo_msg_debug("case 1");
    for(i=0;i<nout;i++) {
      if(k<nref) {
	if(fabs(pou[i] - prf[k])< tol) {
	  check_nomsg(cpl_table_set_int(tmp,"FLAG",i,1));
	  k++;
	}
      }
    }
  } else {

    //pou[0]>prf[0]
    //sinfo_msg_debug("case 2");
    for(k=0;k<nref;k++) {
      if(i<nout) {
	/*
        sinfo_msg("check: %f %f %f %f",
                  fabs(pou[i] - prf[k]),tol,pou[i],prf[k]);
	*/
	if(fabs(pou[i] - prf[k])< tol) {
	  check_nomsg(cpl_table_set_int(tmp,"FLAG",i,1));
	  i++;
	}
      }
    }
  }
  //check_nomsg(cpl_table_save(tmp,NULL,NULL,"out_pre0.fits",CPL_IO_DEFAULT));
  check_nomsg(nout=cpl_table_and_selected_int(tmp,"FLAG",CPL_GREATER_THAN,0));
  check_nomsg(out=cpl_table_extract_selected(tmp));
  sinfo_free_table(&tmp);
  check_nomsg(cpl_table_erase_column(out,"FLAG"));
  if(nout==0) {
    goto cleanup;
  }
  //check_nomsg(cpl_table_save(out,NULL,NULL,"out_post0.fits",CPL_IO_DEFAULT));
  /* sinfo_msg("nout=%d",nout); */
  return out;

 cleanup:
  sinfo_free_table(&tmp);
  sinfo_free_table(&out);
  return NULL;

}

/**
@name sinfo_interpolate_sky
@memo interpolate sky spectrum in case of wavelength shift obj-sky frame
@param inp      input table
@param lambdas  input table
@note TO BE IMPLEMENTED
*/


cpl_table*
sinfo_interpolate_sky(const cpl_table* inp,const cpl_table* lambdas)
{
  //interpolate sky if necessary
  cpl_table* new=NULL;
  new = sinfo_interpolate(inp,lambdas,"WAVE","lsquadratic");;

  return new;
}


/**
@name sinfo_interpolate
@memo interpolate spectrum in case of wavelength shift obj-sky frame
@param inp      input table
@param lambdas  input table
@param name     column name
@param method   interpolationg method
@note TO BE IMPLEMENTED
*/
static cpl_table*
sinfo_interpolate(const cpl_table* inp,
                  const cpl_table* lambdas,
                  const char* name,
                  const char* method)
{
  //TODO
  cpl_table* out=NULL;

  //To remove compilation warnings
  cknull_nomsg(lambdas);
  cknull_nomsg(name);
  cknull_nomsg(method);

  out=cpl_table_duplicate(inp);
  return out;

 cleanup:

  return NULL;

}



/**
@name sinfo_gaussian_amp
@memo computes Gaussian amplitude
@param area  Gaussian area
@param sigma Gaussian sigma
@param x     Gaussian x position
@param x0    Gaussian centre
@param off   Gaussian background
@return Gaussian value
*/


static double
sinfo_gaussian_amp(double area,double sigma,double x,double x0,double off)
{
  return area/sqrt(2*PI_NUMB*sigma*sigma)*
         exp(-(x-x0)*(x-x0)/(2*sigma*sigma))+off;
}


/**
@name sinfo_gaussian_area
@memo computes Gaussian area
@param area  Gaussian area
@param sigma Gaussian sigma
@param x     Gaussian x position
@param x0    Gaussian centre
@param off   Gaussian background
@return Gaussian value
*/


static double
sinfo_gaussian_area(double amp,double sigma,double x,double x0,double off)
{
  return sqrt(2*PI_NUMB*sigma*sigma)*exp((x-x0)*(x-x0)/(2*sigma*sigma))*
         (amp-off);
}


/**
@name sinfo_table_smooth_column
@param t input/output(smoothed) table
@param c table column
@param r smoothing radii
*/
int
sinfo_table_smooth_column(cpl_table** t, const char* c, const int r)
{
  int nrow=0;
  int i=0;
  int j=0;
  double* pval=NULL;
  double sum;
  check_nomsg(nrow=cpl_table_get_nrow(*t));
  check_nomsg(pval=cpl_table_get_data_double(*t,c));
  for(i=r;i<nrow;i++) {
    sum=0;
    for(j=-r;j<=r;j++) {
      sum+=pval[i+j];
    }
    pval[i]=sum/(2*r+1);
  }
  return 0;
 cleanup:
  return -1;
}

/**
@name sinfo_shift_sky
@memo shift a frame and sky spectrum in wavelength
@param sky_frm input/output(shifted) sky frame
@param int_sky input/output(shifted) sky table
@param zshift shift in pixels
@return void
*/
void
sinfo_shift_sky(cpl_frame** sky_frm,
                cpl_table** int_sky,
                const double zshift)
{
  /*
  int xsz=0;
  int ysz=0;
  int zsz=0;
  */
  cpl_propertylist* plist=NULL;
  cpl_imagelist* sky_cub=NULL;
  cpl_imagelist* sky_shift=NULL;
  cpl_table* int_sky_dup=NULL;
  /*
  double min=0;
  double max=0;
  */
 /* Get Object relevant information */
  cknull_nomsg(plist=cpl_propertylist_load(
                     cpl_frame_get_filename(*sky_frm),0));
  /*
  check_nomsg(xsz=sinfo_pfits_get_naxis1(plist));
  check_nomsg(ysz=sinfo_pfits_get_naxis2(plist));
  check_nomsg(zsz=sinfo_pfits_get_naxis3(plist));
  */
  sinfo_free_propertylist(&plist);

  cknull_nomsg(sky_cub=cpl_imagelist_load(cpl_frame_get_filename(*sky_frm),
                                            CPL_TYPE_FLOAT,0));
  /*
  check_nomsg(min=cpl_table_get_column_min(*int_sky,"INT"));
  check_nomsg(max=cpl_table_get_column_max(*int_sky,"INT"));
  */
  int_sky_dup=cpl_table_duplicate(*int_sky);
  sinfo_free_table(&(*int_sky));
  /*
  cknull_nomsg(*int_sky=sinfo_table_shift_column_int(int_sky_dup,
                                                     "INT", zshift,&zrest));
  check_nomsg(cpl_table_save(*int_sky, NULL, NULL,
                             "out_sky_shift1.fits", CPL_IO_DEFAULT));
  sinfo_free_table(&(*int_sky));

  sinfo_msg("min=%f max=%f",min,max);
  check_nomsg(cpl_table_save(int_sky_dup, NULL, NULL,
                             "out_sky_pre2.fits", CPL_IO_DEFAULT));
  cknull_nomsg(*int_sky=sinfo_table_shift_column_poly(int_sky_dup,
                                                      "INT", zshift,2));
  check_nomsg(cpl_table_save(*int_sky, NULL, NULL,
                             "out_sky_shift2.fits", CPL_IO_DEFAULT));
  */
  //check_nomsg(cpl_table_save(int_sky_dup, NULL, NULL,
  //                             "out_sky_pre2.fits", CPL_IO_DEFAULT));
  check_nomsg(*int_sky=sinfo_table_shift_simple(int_sky_dup,"INT",zshift));
  /*
  sinfo_free_table(&(*int_sky));
  cknull_nomsg(*int_sky=sinfo_table_shift_column_spline3(int_sky_dup,
                                                         "INT", zshift));
  check_nomsg(cpl_table_save(*int_sky, NULL, NULL,
                             "out_sky_shift3.fits", CPL_IO_DEFAULT));
  */
  sinfo_free_table(&int_sky_dup);
  /*
  check_nomsg(cpl_table_select_all(*int_sky));
  check_nomsg(n=cpl_table_and_selected_double(*int_sky,"INT",
                                              CPL_LESS_THAN,min));
  ck0_nomsg(sinfo_table_set_column_invalid(int_sky,"INT"));
  sinfo_msg("n=%d",n);
  check_nomsg(cpl_table_select_all(*int_sky));
  check_nomsg(n=cpl_table_and_selected_double(*int_sky,"INT",
                CPL_GREATER_THAN,max));
  sinfo_msg("n=%d",n);
  ck0_nomsg(sinfo_table_set_column_invalid(int_sky,"INT"));
  check_nomsg(cpl_table_select_all(*int_sky));
  */
  //check_nomsg(cpl_table_save(*int_sky, NULL, NULL,
  //                           "out_sky_shift3.fits", CPL_IO_DEFAULT));



  check_nomsg(sky_shift=sinfo_cube_zshift_simple(sky_cub,(float)zshift));

  //check_nomsg(sky_shift=sinfo_cube_zshift(sky_cub,zshift,&zrest));
  //check_nomsg(cpl_imagelist_save(sky_shift,"out_sky1.fits",
  //                 CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT));

  sinfo_free_imagelist(&sky_shift);
  //sinfo_free_imagelist(&sky_shift);
  //sinfo_msg("zrest=%f",zrest);

  //check_nomsg(sky_shift=sinfo_cube_zshift_poly(sky_cub,zshift,2));
  //check_nomsg(cpl_imagelist_save(sky_shift,"out_sky2.fits",
  //                               CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT));
  //sinfo_free_imagelist(&sky_shift);

  //check_nomsg(sky_shift=sinfo_cube_zshift_spline3(sky_cub,zshift));
  //check_nomsg(cpl_imagelist_save(sky_shift,"out_sky3.fits",
  //                               CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT));
  //sinfo_free_imagelist(&sky_shift);
  sinfo_free_imagelist(&sky_cub);

  return;

 cleanup:
  sinfo_free_table(&int_sky_dup);
  sinfo_free_propertylist(&plist);
  sinfo_free_imagelist(&sky_shift);
  sinfo_free_imagelist(&sky_cub);
  return;
}


/**
@name sinfo_convolve_kernel
@memo convolve a table with a box of a given radii
@param t input table
@param rad kernel's radii
*/
static int
sinfo_convolve_kernel(cpl_table** t, const int rad)
{
  int i=0;
  int j=0;
  int np=0;
  double val=0;
  double* pidata=NULL;
  double* pcdata=NULL;
  /*
  double dw=0;
  double dr=0;
  double ws=0;
  double we=0;
  */
  cknull(*t,"null input table");
  check_nomsg(cpl_table_new_column(*t,"CNV",CPL_TYPE_DOUBLE));
  check_nomsg(pidata=cpl_table_get_data_double(*t,"INT"));
  check_nomsg(pcdata=cpl_table_get_data_double(*t,"CNV"));
  /*
  check_nomsg(ws=cpl_table_get_column_min(*t,"WAVE"));
  check_nomsg(we=cpl_table_get_column_max(*t,"WAVE"));
  */
  check_nomsg(np=cpl_table_get_nrow(*t));
  //dw=(we-ws)/(np-1);
  //dr=(we-ws)/(rad-1);
  /* set to 0 edges */
  for(i=0;i<rad;i++) {
    pcdata[i]=0.;
  }
  for(i=np-rad;i<np;i++) {
    pcdata[i]=0.;
  }
  for(i=rad;i<np-rad;i++) {
    val=0;
    for(j=-rad;j<rad;j++) {
      val+=pidata[i+j];
    }
    /*val*=dw; */
    check_nomsg(cpl_table_set_double(*t,"CNV",i,val));
  }
  return 0;

 cleanup:
  return -1;

}



/**
@name sinfo_convolve_kernel
@memo convolve a table with a box of a given radii
@param t input table
@param rad kernel's radii
*/

int
sinfo_convolve_kernel2(cpl_table** t, const int rad)
{
  int i=0;
  int j=0;
  int np=0;
  double val=0;
  double* pidata=NULL;
  double* pcdata=NULL;
  /*
  double dw=0;
  double dr=0;
  double ws=0;
  double we=0;
  */
  cknull(*t,"null input table");
  check_nomsg(cpl_table_new_column(*t,"CNV",CPL_TYPE_DOUBLE));
  check_nomsg(pidata=cpl_table_get_data_double(*t,"INT"));
  check_nomsg(pcdata=cpl_table_get_data_double(*t,"CNV"));
  /*
  check_nomsg(ws=cpl_table_get_column_min(*t,"WAVE"));
  check_nomsg(we=cpl_table_get_column_max(*t,"WAVE"));
  */
  check_nomsg(np=cpl_table_get_nrow(*t));
  /*
  dw=(we-ws)/(np-1);
  dr=(we-ws)/(rad-1);
  */
  /* set to 0 edges */
  for(i=0;i<rad;i++) {
    pcdata[i]=0.;
  }
  for(i=np-rad;i<np;i++) {
    pcdata[i]=0.;
  }
  for(i=0;i<np-rad;i++) {
    val=0;
    for(j=0;j<rad;j++) {
      val+=pidata[i+j];
    }
    /*val*=dw; */
    check_nomsg(cpl_table_set_double(*t,"CNV",i,val));
  }
  return 0;

 cleanup:
  return -1;

}



/**
@name sinfo_convolve_exp
@memo convolve a table with an exponential of a given radii and fwhm
@param t input table
@param rad Gaussian radii
@param fwhm Gaussian radii
*/
int
sinfo_convolve_exp(cpl_table** t, const int rad, const double fwhm)
{
  int i=0;
  int j=0;
  int np=0;
  double ln2=0.693147180560;
  double k=ln2/fwhm;
  double val=0;
  double* pidata=NULL;
  double* pcdata=NULL;
  /*
  double dw=0;
  double dr=0;
  double ws=0;
  double we=0;
  */
  cknull(*t,"null input table");
  check_nomsg(cpl_table_new_column(*t,"CNV",CPL_TYPE_DOUBLE));
  check_nomsg(pidata=cpl_table_get_data_double(*t,"INT"));
  check_nomsg(pcdata=cpl_table_get_data_double(*t,"CNV"));
  /*
  check_nomsg(ws=cpl_table_get_column_min(*t,"WAVE"));
  check_nomsg(we=cpl_table_get_column_max(*t,"WAVE"));
  */
  check_nomsg(np=cpl_table_get_nrow(*t));
  //dw=(we-ws)/(np-1);
  //dr=(we-ws)/(rad-1);
  /* set to 0 edges */
  for(i=0;i<rad;i++) {
    pcdata[i]=0.;
  }
  for(i=np-rad;i<np;i++) {
    pcdata[i]=0.;
  }
  for(i=rad;i<np-rad;i++) {
    val=0;
    for(j=-rad;j<rad;j++) {
      val+=pidata[i+j]*k*pow(2.0,-2.0*fabs(i-rad)/fwhm);
    }
    /*val*=dw; */
    check_nomsg(cpl_table_set_double(*t,"CNV",i,val));
  }
  return 0;

 cleanup:
  return -1;

}


/**
@name sinfo_convolve_gauss
@memo convolve a table with an Gaussian of a given radii and fwhm
@param t input table
@param rad Gaussian radii
@param fwhm Gaussian radii
*/

int
sinfo_convolve_gauss(cpl_table** t, const int rad, const double fwhm)
{
  int i=0;
  int j=0;
  int np=0;
  double val=0;
  double sigma=fwhm/2.3548;
  double sigma2=sigma*sigma;
  double* pidata=NULL;
  double* pcdata=NULL;
  /*
  double dw=0;
  double dr=0;
  double ws=0;
  double we=0;
  */
  double tx=0;

  cknull(*t,"null input table");
  check_nomsg(cpl_table_new_column(*t,"CNV",CPL_TYPE_DOUBLE));
  check_nomsg(pidata=cpl_table_get_data_double(*t,"INT"));
  check_nomsg(pcdata=cpl_table_get_data_double(*t,"CNV"));
  /*
  check_nomsg(ws=cpl_table_get_column_min(*t,"WAVE"));
  check_nomsg(we=cpl_table_get_column_max(*t,"WAVE"));
  */
  check_nomsg(np=cpl_table_get_nrow(*t));
  /*
  dw=(we-ws)/(np-1);
  dr=(we-ws)/(rad-1);
  */
  /* set to 0 edges */
  for(i=0;i<rad;i++) {
    pcdata[i]=0.;
  }
  for(i=np-rad;i<np;i++) {
    pcdata[i]=0.;
  }
  for(i=rad;i<np-rad;i++) {
    val=0;
    for(j=-rad;j<rad;j++) {
      tx=i-rad;
      val+=pidata[i+j]*exp(-tx*tx/2.0/sigma2)/(sigma*sqrt(2.0*PI_NUMB));
    }
    /*val*=dw; */
    check_nomsg(cpl_table_set_double(*t,"CNV",i,val));
  }
  return 0;

 cleanup:
  return -1;

}



/**
@name sinfo_scales_obj_sky_cubes
@param obj_cub input object cube
@param sky_cub input sky cube
@param bkg     input background
@param rscale  scale ratio to be applied to sky frame before subtraction \
               from object
@param obj_cor corrected object
@return if success 0, else -1
*/

static int
sinfo_scales_obj_sky_cubes(cpl_imagelist* obj_cub,
                            cpl_imagelist* sky_cub,
                            cpl_table* bkg,
                            cpl_table* rscale,
                            cpl_imagelist** obj_cor)
{


  int i=0;
  int j=0;
  int k=0;
  int xsz=0;
  int ysz=0;
  int zsz=0;

  double* podata=NULL;
  double* psdata=NULL;
  double* pbdata=NULL;
  double* pcdata=NULL;
  double* pscale=NULL;


  cpl_image* imgo=NULL;
  cpl_image* imgs=NULL;
  cpl_image* imgc=NULL;


  check_nomsg(imgo=cpl_imagelist_get(obj_cub,0));
  check_nomsg(xsz=cpl_image_get_size_x(imgo));
  check_nomsg(ysz=cpl_image_get_size_y(imgo));
  check_nomsg(zsz=cpl_imagelist_get_size(obj_cub));

  check_nomsg(*obj_cor=cpl_imagelist_duplicate(obj_cub));

  for(k=0;k<zsz;k++) {
    check_nomsg(imgo=cpl_imagelist_get(obj_cub,k));
    check_nomsg(imgc=cpl_imagelist_get(*obj_cor,k));
    check_nomsg(imgs=cpl_imagelist_get(sky_cub,k));

    check_nomsg(podata=cpl_image_get_data_double(imgo));
    check_nomsg(pcdata=cpl_image_get_data_double(imgc));
    check_nomsg(psdata=cpl_image_get_data_double(imgs));
    check_nomsg(pbdata=cpl_table_get_data_double(bkg,"INT2"));
    check_nomsg(pscale=cpl_table_get_data_double(rscale,"RATIO"));

    for (j=0;j<ysz; j++) {
      for (i=0;i<xsz; i++) {
        if(!irplib_isnan(podata[i+j*xsz]) &&
           !irplib_isnan(psdata[i+j*xsz]) &&
           !irplib_isnan(pbdata[k]) &&
           !irplib_isnan(pscale[k])) {
    pcdata[i+j*xsz] = podata[i+j*xsz]-
                          (psdata[i+j*xsz]-pbdata[k])*pscale[k];
    }
      }
    }
  }


  return 0;
 cleanup:

  return -1;
}


/**
@name sinfo_fitbkg
@memo Black Body Thermal emission
@param x evaluation points
@param a function parameters
@param result function value

@doc This function is a Black Body profile
   c=14387.7
   y(x,a)=a[0]+a[1]*x^(-5)/(exp{c/(x*a[2])}-1)=a[0]+a[1]*fac(x,a)

   where
   fac(x,a)=x^(-5)/(exp{c/(x*a[2])}-1)

  */

static int
sinfo_fitbkg(const double x[],
             const double a[],
             double *result)
{


  double fac  = sinfo_fac(x[0],a[2]);
  /*
  int n=sizeof(x)/sizeof(double);
  double fmin = sinfo_fac(x[0],a[2]);
  double fmax = sinfo_fac(x[n-1],a[2]);
  sinfo_msg("n=%d",n);
  if(fmax < 0) sinfo_msg("fmax=%f",fmax);
  */
  //*result = a[0]+a[1]*fac/sinfo_scale_fct;
  *result = a[0]+a[1]*fac;

  return 0;
}

/**
@name sinfo_fitbkg_derivative
@memo Black Body Thermal emission derivative
@param x evaluation points
@param a function parameters
@param result derivative value

@doc This function is a Black Body profile

   c=14387.7
   y(x,a)=a[0]+a[1]*x^(-5)/(exp{c/(x*a[2])}-1)/fct=a[0]+a[1]*fac(x,a)/fct

   where
   fac(x,a)=x^(-5)/(exp{c/(x*a[2])}-1)

   dyda[0]=1
   dyda[1]=x^(-5)/(exp{c/(x*a[2])}-1)=fac/fct
   dyda[2]=a[1]*x^(-5)*(exp{c/(x*a[2])}-1)^(-2)*c/(x*a[2]^2)
          =a[1]*fac^(2)*x^(5)*c/(x*a[2])^2
          =a[1]*fac^(2)*x^4*c/a[2]^2

  */

static int
sinfo_fitbkg_derivative(const double x[],
                        const double a[],
                  double d[])
{
  double c=14387.7;
  /*
  int n=sizeof(x)/sizeof(double);
  double fmin = sinfo_fac(x[0],a[2]);
  double fmax = sinfo_fac(x[n],a[2]);
  */
  double fac  = sinfo_fac(x[0],a[2]);
  /*
  //double f2=0;
  //double f1=0;
  //double da=0.001;
  //f1=a[0]+a[1]*fac;
  //f2=f1+da*a[0];
  //f2=a[0]+(a[1]+da*a[1])*fac;
  //f2=a[0]+a[1]*sinfo_fac(x[0],a[2]+da*a[2]);
   */
  d[0]=1.;
  d[1]=fac;
  d[2]=a[1]*fac*fac*x[0]*x[0]*x[0]*x[0]*c/(a[2]*a[2])*exp(c/(x[0]*a[2]));
  //sinfo_msg("d0=%g d1=%g d2=%g",d[0]*a[0]/f,d[1]*a[1]/f,d[2]*a[2]/f);
  //sinfo_msg("comp d1=%g",d[2]);
  //sinfo_msg("real d1=%g",(f2-f1)/(da*a[2]));

  return 0;
}



/**
@name sinfo_fac
@memo Black Body Thermal emission
@param x evaluation points
@param a function parameters
@return result function value

@doc This function is a Black Body profile
   c=14387.7
   fac(x,a)=x^(-5)/(exp{c/(x*a[2])}-1)

  */

static double
sinfo_fac(const double x, const double t)
{

  double c=14387.7;

  //return pow(x,-5.)/(exp(c/(x*fabs(t)))-1.)/sinfo_scale_fct;
  /* replace pow(x,-5.)/(exp(c/(x*fabs(t)))-1.); by
   *         pow(x,-5.)/(expm1(c/(x*fabs(t))));
   * to get better accuracy
   */

  return pow(x,-5.)/(expm1(c/(x*fabs(t))));
}

/**
@name sinfo_table_threshold
@param t input/output(thresholded) table
@param column input table's column
@param low_cut low cut threshold
@param hig_cut hight cut threshold
@param low_ass value to assign to raws < low cut threshold
@param hig_ass value to assign to raws > low cut threshold
*/
static int
sinfo_table_threshold(cpl_table** t,
                      const char* column,
                      const double low_cut,
                      const double hig_cut,
                      const double low_ass,
                      const double hig_ass)
{

  int nrow=0;
  int i=0;
  double* pdata=NULL;
  cknull(*t,"null input table!");

  check_nomsg(nrow=cpl_table_get_nrow(*t));
  check_nomsg(pdata=cpl_table_get_data_double(*t,column));

  for(i=0;i<nrow;i++) {

    if(pdata[i]<low_cut) {
      pdata[i]=low_ass;
    }
    if (pdata[i] >= hig_cut) {
      pdata[i]=hig_ass;
    }

  }

  return 0;

 cleanup:

  return -1;
}

/**
@name sinfo_table_set_column_invalid
@param t   input/output table
@param col column to check


static int
sinfo_table_set_column_invalid(cpl_table** t,const char* col)
{
  int nrow=0;
  int i=0;
  cknull(*t,"input table is NULL");
  check_nomsg(nrow=cpl_table_get_nrow(*t));
  for(i=0;i<nrow;i++) {
    if( cpl_table_is_selected(*t,i) ) {
      cpl_table_set_invalid(*t,col,i);
    }
  }
  return 0;
 cleanup:
  return -1;

}

*/



static int
sinfo_table_set(cpl_table** inp,
                const cpl_table* ref,
                const double val,
                const double tol)
{

  int ninp=0;
  //int nref=0;
  double* piw=NULL;
  const double* prw=NULL;
  //double* pir=NULL;
  int i=0;
  int k=0;
  cknull(*inp,"NULL input table");
  cknull(ref,"NULL reference table");

  check_nomsg(ninp=cpl_table_get_nrow(*inp));
  //check_nomsg(nref=cpl_table_get_nrow(ref));

  check_nomsg(prw=cpl_table_get_data_double_const(ref,"WAVE"));
  check_nomsg(piw=cpl_table_get_data_double(*inp,"WAVE"));
  //check_nomsg(pir=cpl_table_get_data_double(*inp,"RATIO"));


  for(i=0;i<ninp;i++) {
    /*sinfo_msg("check =%g thresh=%g",fabs(piw[i]-prw[k]),tol); */
    if(fabs(piw[i]-prw[k]) < tol) {
      check_nomsg(cpl_table_set_double(*inp,"RATIO",i,val));
      k++;
    }
  }
  return 0;

 cleanup:

  return -1;

}



static cpl_table*
sinfo_table_shift_simple(cpl_table* inp,
                         const char* col,
                         const double shift)
{

  int nrow=0;
  cpl_table* out=NULL;
  int is=(int)shift;
  double ds=shift-is;
  double* pi=NULL;
  double* po=NULL;
  double m=0;
  int i=0;
  cknull(inp,"null input table");

  check_nomsg(nrow=cpl_table_get_nrow(inp));
  check_nomsg(out=cpl_table_duplicate(inp));
  check_nomsg(cpl_table_fill_column_window(out,col,0,nrow,0));
  check_nomsg(pi=cpl_table_get_data_double(inp,col));
  check_nomsg(po=cpl_table_get_data_double(out,col));


  for(i=0;i<nrow;i++) {
    if((i+is)>0 && (i+is+1) < nrow) {
      m=pi[i+is+1]-pi[i+is];
      po[i]=pi[i+is]+m*ds;
    }
  }
  return out;
  cleanup:
  sinfo_free_table(&out);
  return NULL;

}




static cpl_imagelist*
sinfo_cube_zshift_simple(cpl_imagelist* inp,
                        const float shift)
{

  int nx=0;
  int ny=0;
  int nz=0;

  int i=0;
  int j=0;
  int k=0;
  int ks=(int)shift;

  float ds=shift-ks;
  float* pu=NULL;
  float* pl=NULL;
  float* po=NULL;

  float  int2=0;
  float  int1=0;
  float m=0;

  cpl_imagelist* out=NULL;
  cpl_image* imgu=NULL;
  cpl_image* imgl=NULL;
  cpl_image* imgo=NULL;


  cknull(inp,"null input cube");

  check_nomsg(nz=cpl_imagelist_get_size(inp));
  check_nomsg(out=cpl_imagelist_duplicate(inp));
  check_nomsg(imgo=cpl_imagelist_get(out,0));
  check_nomsg(nx=cpl_image_get_size_x(imgo));
  check_nomsg(ny=cpl_image_get_size_y(imgo));

  for(k=0;k<nz;k++) {
    if((k+ks)>0 && (k+ks+1) < nz) {

      check_nomsg(imgu=cpl_imagelist_get(inp,k+ks+1));
      check_nomsg(imgl=cpl_imagelist_get(inp,k+ks));
      check_nomsg(imgo=cpl_imagelist_get(out,k));

      check_nomsg(pu=cpl_image_get_data_float(imgu));
      check_nomsg(pl=cpl_image_get_data_float(imgl));
      check_nomsg(po=cpl_image_get_data_float(imgo));


      for(j=0;j<ny;j++) {
    for(i=0;i<nx;i++) {
          int2=pu[nx*j+i];
          int1=pl[nx*j+i];
      m=int2-int1;
      po[nx*j+i]=int1+m*ds;
    }
      }
    }


  }
  return out;
  cleanup:
  sinfo_free_imagelist(&out);
  return NULL;

}


/**
 @name sinfo_get_line_ratio
 @brief compute line ratio obj_residual/sky

 @param obj input object table
 @param sky input sky table
 @param method input method
 @param r output ratio
 @return if success 0, else -1
*/
static int
sinfo_get_line_ratio(cpl_table* obj_lin,
                      cpl_table* obj_cnt,
                      cpl_table* sky_lin,
                      cpl_table* sky_cnt,
                      const int method,
                      double* r)
{

  int nobj;
  int nsky;
  int i=0;

  cpl_table* obj_dif=NULL;
  cpl_table* sky_dif=NULL;

  double* poi=NULL;
  double* psi=NULL;
  double* pvd=NULL;
  double* pvn=NULL;
  double* pvr=NULL;

  cpl_vector* num=NULL;
  cpl_vector* den=NULL;
  cpl_vector* rat=NULL;
  cpl_vector* wav=NULL;
  double mnum=0;
  double mden=0;
  double tnum=0;
  double tden=0;
  cpl_size pows[2];
  cpl_polynomial* cfit=NULL;
  double mse=0;

  cknull(obj_lin,"null obj line table");
  cknull(sky_lin,"null sky line table");

  cknull(obj_cnt,"null obj cont table");
  cknull(sky_cnt,"null sky cont table");


  cknull_nomsg(obj_dif=sinfo_table_subtract_continuum(obj_lin,obj_cnt));
  cknull_nomsg(sky_dif=sinfo_table_subtract_continuum(sky_lin,sky_cnt));

  check_nomsg(nobj=cpl_table_get_nrow(obj_dif));
  check_nomsg(nsky=cpl_table_get_nrow(sky_dif));



  if(nobj != nsky) {
    sinfo_msg_error("obj and sky table must have the same no of rows!");
    sinfo_msg_error("nobj=%d nsky=%d",nobj,nsky);
    goto cleanup;
  }
  //sinfo_msg("Object sky residuals/Sky lines ratio determination method=%d",
  //          method);
  if(method == 0) {
    ck0_nomsg(sinfo_get_line_ratio_amoeba(obj_dif,sky_dif,r));
    sinfo_free_table(&obj_dif);
    sinfo_free_table(&sky_dif);
   return 0;
  }


  check_nomsg(poi=cpl_table_get_data_double(obj_dif,"INT"));
  check_nomsg(psi=cpl_table_get_data_double(sky_dif,"INT"));

  check_nomsg(num=cpl_vector_new(nobj));
  check_nomsg(den=cpl_vector_new(nobj));
  check_nomsg(rat=cpl_vector_new(nobj));
  check_nomsg(cpl_vector_fill(num,0));
  check_nomsg(cpl_vector_fill(den,0));
  check_nomsg(cpl_vector_fill(rat,0));
  check_nomsg(pvd=cpl_vector_get_data(den));
  check_nomsg(pvn=cpl_vector_get_data(num));
  check_nomsg(pvr=cpl_vector_get_data(rat));

  for(i=0;i<nobj;i++) {
    if(!irplib_isnan(psi[i]) &&
       !irplib_isnan(poi[i]) &&
       !irplib_isinf(psi[i]) &&
       !irplib_isinf(poi[i]) ) {
      pvn[i]=psi[i]*poi[i];
      pvd[i]=psi[i]*psi[i];
      if(psi[i] != 0) {
         pvr[i]=poi[i]/psi[i];
      }
    }
  }
  sinfo_free_table(&sky_dif);

  check_nomsg(mnum=cpl_vector_get_median_const(num));
  check_nomsg(mden=cpl_vector_get_median_const(den));
  check_nomsg(tnum=cpl_vector_get_mean(num)*nobj);
  check_nomsg(tden=cpl_vector_get_mean(den)*nobj);

  //sinfo_msg("mden=%g tden=%g",mden,tden);
  //sinfo_msg("mnum=%g tnum=%g",mnum,tnum);
  sinfo_free_my_vector(&num);
  sinfo_free_my_vector(&den);
  if(method == 1) {
    *r=tnum/tden;
  } else if (method == 2) {
    *r=mnum/mden;
  } else if (method == 3) {
    *r=cpl_vector_get_median_const(rat);
  } else if (method == 4) {
    *r=cpl_vector_get_mean(rat);
  } else if (method == 5) {

    check_nomsg(wav=cpl_vector_wrap(nobj,
                    cpl_table_get_data_double(obj_dif,"WAVE")));
    check_nomsg(cfit=sinfo_polynomial_fit_1d_create(wav,rat,0,&mse));
    sinfo_unwrap_vector(&wav);
    pows[0]=0;
    pows[1]=0;
    check_nomsg(*r=cpl_polynomial_get_coeff(cfit,pows));
    sinfo_free_polynomial(&cfit);

  }

  sinfo_free_table(&obj_dif);
  sinfo_free_my_vector(&rat);
  return 0;
 cleanup:
  sinfo_free_table(&obj_dif);
  sinfo_free_table(&sky_dif);
  sinfo_free_my_vector(&num);
  sinfo_free_my_vector(&den);
  sinfo_free_my_vector(&rat);
  sinfo_unwrap_vector(&wav);

  return -1;
}




/**
 @name sinfo_get_line_ratio_amoeba
 @brief compute line ratio obj_residual/sky

 @param obj input object table
 @param sky input sky table
 @param r output ratio
 @return if success 0, else -1
*/
static int
sinfo_get_line_ratio_amoeba(cpl_table* obj,
                            cpl_table* sky,
                            double* r)
{


  int i=0;
  const int MP=2;
  const int NP=1;
  double y[MP];
  double p0[NP];
  double** ap=NULL;
  int nfunc=0;
  int np=0;
  check_nomsg(np=cpl_table_get_nrow(obj));
  check_nomsg(sa_ox=cpl_vector_wrap(np,cpl_table_get_data_double(obj,"WAVE")));
  check_nomsg(sa_oy=cpl_vector_wrap(np,cpl_table_get_data_double(obj,"INT")));
  check_nomsg(sa_sy=cpl_vector_wrap(np,cpl_table_get_data_double(sky,"INT")));
  // Amoeba part


  ap=(double**) cpl_calloc(MP,sizeof(double*));
  for(i=0;i<MP;i++) {
    ap[i]=cpl_calloc(NP,sizeof(double));
  }

  ap[0][0]=-1.;
  ap[1][0]=+1.;

  //sinfo_msg("Before amoeba fit");
  //sinfo_msg("ap[0][0]=%g ap[0][1]=%g",ap[0][0],ap[1][0]);
  for(i=0;i<MP;i++) {
    p0[0]=ap[i][0];
    y[i]=sinfo_fit_sky(p0);
  }


  check_nomsg(sinfo_fit_amoeba(ap,y,NP,AMOEBA_FTOL,sinfo_fit_sky,&nfunc));

  sinfo_msg("After amoeba fit");
  sinfo_msg("ap[0][0]=%g ap[0][1]=%g",ap[0][0],ap[1][0]);

  *r=ap[0][0];

  sinfo_unwrap_vector(&sa_ox);
  sinfo_unwrap_vector(&sa_oy);
  sinfo_unwrap_vector(&sa_sy);
  sinfo_new_destroy_2Ddoublearray(&ap,MP);


  return 0;

 cleanup:
  sinfo_unwrap_vector(&sa_ox);
  sinfo_unwrap_vector(&sa_oy);
  sinfo_unwrap_vector(&sa_sy);
  sinfo_new_destroy_2Ddoublearray(&ap,MP);

  return -1;

}


/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_fit_sky
  @memo        Computes rms of difference INT(sky)-thermal_background

  @param sa      pointer to sinfo_amoeba structure
  @param p           input fit parameters
  Returns rms= stdev[obj_line-obj_cont-sky*p]
 */
/*--------------------------------------------------------------------------*/

static double
sinfo_fit_sky(double p[])

{
  double* ps=NULL;
  double* po=NULL;
  double* pv=NULL;
  cpl_vector* vtmp=NULL;
  int i=0;
  int np=0;
  cpl_size pows[2];
  double mse=0;
  double cont=0;
  cpl_polynomial* pfit=NULL;

  double rms=0;


  //fit residual obj continuum and subtract it
  check_nomsg(pfit=sinfo_polynomial_fit_1d_create(sa_ox,sa_oy,0,&mse));
  pows[0]=0;
  pows[1]=0;
  check_nomsg(cont=cpl_polynomial_get_coeff(pfit,pows));
  check_nomsg(sinfo_free_polynomial(&pfit));
  check_nomsg(cpl_vector_subtract_scalar(sa_oy,cont));


  //fit residual sky continuum and subtract it
  check_nomsg(pfit=sinfo_polynomial_fit_1d_create(sa_ox,sa_sy,0,&mse));
  pows[0]=0;
  pows[1]=0;
  check_nomsg(cont=cpl_polynomial_get_coeff(pfit,pows));
  check_nomsg(sinfo_free_polynomial(&pfit));
  check_nomsg(cpl_vector_subtract_scalar(sa_sy,cont));

  //computes diff=(obj-conto)-(sky-contsky)*p[0]
  check_nomsg(po= cpl_vector_get_data(sa_oy));
  check_nomsg(ps= cpl_vector_get_data(sa_sy));

  check_nomsg(np=cpl_vector_get_size(sa_oy));
  check_nomsg(vtmp=cpl_vector_new(np));
  check_nomsg(pv= cpl_vector_get_data(vtmp));


  for(i=0;i<np;i++) {
    pv[i]=po[i]-ps[i]*p[0];
  }
  //computes rms diff
  check_nomsg(rms=cpl_vector_get_stdev(vtmp));
  sinfo_free_my_vector(&vtmp);
  return rms;
 cleanup:
  sinfo_free_my_vector(&vtmp);
  return -1;

}



/**
 @name sinfo_table_interpol
 @brief interpolate value in table cnt with a uniform fit and subtract that
        value from column INT of table lin
 @param obj_lin object line table
 @param obj_cnt object cont table
 @param sky_lin sky    line table
 @param sky_cnt sky    cont table
 @param r   ratio which minimise diff=(obj_lin-obj_cnt) -r*(sky_lin-sky_cnt)
 @return new table with corrected intensity.
*/
static cpl_table*
sinfo_table_interpol(cpl_table* obj_lin,
                     cpl_table* obj_cnt,
                     cpl_table* sky_lin,
                     cpl_table* sky_cnt,
                     const double r)
{

  cpl_table* out=NULL;
  cpl_table* obj_dif=NULL;
  cpl_table* sky_dif=NULL;
  cknull(obj_lin,"null line table");
  cknull(obj_cnt,"null cont table");

  cknull_nomsg(obj_dif=sinfo_table_subtract_continuum(obj_lin,obj_cnt));
  cknull_nomsg(sky_dif=sinfo_table_subtract_continuum(sky_lin,sky_cnt));

  check_nomsg(out=cpl_table_duplicate(obj_dif));
  check_nomsg(cpl_table_duplicate_column(out,"CSKY",sky_dif,"INT"));
  check_nomsg(cpl_table_multiply_scalar(out,"CSKY",r));
  check_nomsg(cpl_table_subtract_columns(out,"INT","CSKY"));

  sinfo_free_table(&obj_dif);
  sinfo_free_table(&sky_dif);

  return out;

 cleanup:
  sinfo_free_table(&obj_dif);
  sinfo_free_table(&sky_dif);
  sinfo_free_table(&out);
  return NULL;

}






/**
 @name sinfo_table_subtract_column
 @brief interpolate value in table cnt with a uniform fit and subtract that
        value from column INT of table lin
 @param lin input table
 @param cnt reference table
 @return new table with corrected intensity.
*/
static cpl_table*
sinfo_table_subtract_continuum(cpl_table* lin,
                               cpl_table* cnt)

{

  cpl_table* out=NULL;
  int nlin=0;
  int ncnt=0;
  int i=0;
  double* poi=NULL;
  cpl_vector* vx=NULL;
  cpl_vector* vy=NULL;
  cpl_polynomial* cfit=NULL;
  cpl_size pows[2];
  double mse=0;
  double yfit=0;

  cknull(lin,"null line table");
  cknull(cnt,"null cont table");
  check_nomsg(out=cpl_table_duplicate(lin));
  check_nomsg(cpl_table_new_column(out,"CONT",CPL_TYPE_DOUBLE));
  check_nomsg(nlin=cpl_table_get_nrow(lin));
  check_nomsg(ncnt=cpl_table_get_nrow(cnt));
  //sinfo_msg("nlin=%d",nlin);
  check_nomsg(cpl_table_fill_column_window(out,"CONT",0,nlin,0));

  //do a uniform fit
  check_nomsg(vx=cpl_vector_wrap(ncnt,cpl_table_get_data_double(cnt,"WAVE")));
  check_nomsg(vy=cpl_vector_wrap(ncnt,cpl_table_get_data_double(cnt,"INT")));
  check_nomsg(cfit=sinfo_polynomial_fit_1d_create(vx,vy,0,&mse));
  sinfo_unwrap_vector(&vx);
  sinfo_unwrap_vector(&vy);

  pows[0]=0;
  pows[1]=0;
  check_nomsg(yfit=cpl_polynomial_get_coeff(cfit,pows));
  sinfo_free_polynomial(&cfit);
  //sinfo_msg("coeff 0=%g",yfit);

  check_nomsg(poi=cpl_table_get_data_double(out,"CONT"));
  for(i=0;i<nlin;i++) {
    poi[i]=yfit;
  }

  check_nomsg(cpl_table_subtract_columns(out,"INT","CONT"));
  check_nomsg(cpl_table_erase_column(out,"CONT"));



  return out;

 cleanup:
  sinfo_unwrap_vector(&vx);
  sinfo_unwrap_vector(&vy);
  sinfo_free_polynomial(&cfit);
  sinfo_free_table(&out);
  return NULL;

}


static int
sinfo_compute_line_ratio(cpl_table* obj,
                         cpl_table* sky,
                         const double wtol,
                         const int meth,
                         const cpl_table* sel_regions,
                         cpl_table* cont_regions,
                         double* r)
{
  cpl_table* line_regions=NULL;
  cpl_table* obj_cnt=NULL;
  cpl_table* sky_cnt=NULL;
  cpl_table* obj_lin=NULL;
  cpl_table* sky_lin=NULL;
  cpl_table* lres=NULL;
  double fmed=0;
  double fsdv=0;
  double fthresh=0;
  int fclip_i=0;
  int line_i=0;


  //line_regions = med_regions;
  check_nomsg(line_regions = cpl_table_duplicate(sel_regions));
  //r = amoeba(1.e-5,function_name='fitsky',p0=[1.],scale=[0.1]);
  //Identify obj lines and continuum, same for sky
  check_nomsg(obj_lin=sinfo_table_select_range(obj,line_regions,wtol));
  check_nomsg(sky_lin=sinfo_table_select_range(sky,line_regions,wtol));
  check_nomsg(obj_cnt=sinfo_table_select_range(obj,cont_regions,wtol));
  check_nomsg(sky_cnt=sinfo_table_select_range(sky,cont_regions,wtol));

  ck0_nomsg(sinfo_get_line_ratio(obj_lin,obj_cnt,sky_lin,sky_cnt,meth,r));


  //fline_res = (obj_lr[line_regions]-
  //             interpol(obj_lr[cont_regions],llr[cont_regions],
  //             llr[line_regions])) -
  //            (sky_lr[line_regions] -
  //             interpol(sky_lr[cont_regions],llr[cont_regions],
  //
  //            llr[line_regions]))*r[0];
  check_nomsg(lres=sinfo_table_interpol(obj_lin,obj_cnt,sky_lin,sky_cnt,*r));

  check_nomsg(fmed = cpl_table_get_column_median(lres,"INT"));
  check_nomsg(fsdv = cpl_table_get_column_stdev(lres,"INT"));
  fthresh=fmed+3*fsdv;
  //fclip = where(abs(fline_res) > fmed+3*fsdv,fclip_i);
  check_nomsg(cpl_table_duplicate_column(lres,"AINT",lres,"INT"));
  check_nomsg(cpl_table_multiply_columns(lres,"AINT","INT"));
  check_nomsg(cpl_table_power_column(lres,"AINT",0.5));
  check_nomsg(fclip_i=cpl_table_and_selected_double(lres,"AINT",
                                                    CPL_GREATER_THAN,
                                                    fthresh));
  check_nomsg(cpl_table_select_all(lres));


  if (fclip_i > 0) {
    //line_regions = line_regions[where(abs(fline_res) < fmed+3*fsdv)];
    check_nomsg(line_i=cpl_table_and_selected_double(lres,"AINT",
                             CPL_LESS_THAN,
                             fthresh));
    sinfo_free_table(&line_regions);
    check_nomsg(line_regions=cpl_table_extract_selected(lres));
    sinfo_free_table(&lres);

    check_nomsg(cpl_table_erase_column(line_regions,"INT"));
    check_nomsg(cpl_table_erase_column(line_regions,"AINT"));


    if (line_i >= 3) {

    sinfo_free_table(&obj_lin);
    sinfo_free_table(&sky_lin);
    check_nomsg(obj_lin=sinfo_table_select_range(obj,line_regions,wtol));
    check_nomsg(sky_lin=sinfo_table_select_range(sky,line_regions,wtol));

    sinfo_free_table(&line_regions);


     //r = amoeba(1.e-5,function_name='fitsky',p0=[1.],scale=[0.1]);
      ck0_nomsg(sinfo_get_line_ratio(obj_lin,obj_cnt,sky_lin,sky_cnt,meth,r));
    }
  }
  *r=fabs(*r);
  //Free memory
  sinfo_free_table(&obj_cnt);
  sinfo_free_table(&sky_cnt);
  sinfo_free_table(&sky_lin);
  sinfo_free_table(&obj_lin);
  sinfo_free_table(&lres);
  sinfo_free_table(&line_regions);


  return 0;


 cleanup:


  sinfo_free_table(&obj_cnt);
  sinfo_free_table(&sky_cnt);
  sinfo_free_table(&sky_lin);
  sinfo_free_table(&obj_lin);

  sinfo_free_table(&lres);
  sinfo_free_table(&line_regions);

  return -1;

}
/**

@name   sinfo_find_rot_waves
@memo   find wavelength range corresponding to a given rotational level
@param  w_rot array with wavelengths corresponding to rotational levels
@param  npix_w number of pixels corresponding to line width
@param  w_step wavelength sampling step
@param  range full wavelength range
@return wavelength range corresponding to a given rotational level
*/

static cpl_table*
sinfo_find_rot_waves(
             const double  w_rot[],
                     const int npix_w,
                     const double w_step,
                     cpl_table* range
             )
{
  int i=0;
  int x_i=0;
  int r_start=0;
  double w_min=0;
  double w_max=0;

  cpl_table* w_sel=NULL;
  cpl_table* res=NULL;

  check_nomsg(res = cpl_table_new(0));

  check_nomsg(cpl_table_copy_structure(res,range));

  for (i=0; i< NROT; i++) {

    //x = where(lambda > l_rot_low[i]-npixw*cdelto &&
    //          lambda < l_rot_low[i]+npixw*cdelto,x_i);

    w_min=w_rot[i]-npix_w*w_step;
    w_max=w_rot[i]+npix_w*w_step;

    check_nomsg(cpl_table_and_selected_double(range,"WAVE",
                                              CPL_GREATER_THAN,w_min));
    check_nomsg(cpl_table_and_selected_double(range,"WAVE",
                                              CPL_LESS_THAN,w_max));
    sinfo_free_table(&w_sel);
    check_nomsg(w_sel=cpl_table_extract_selected(range));
    check_nomsg(x_i=cpl_table_get_nrow(w_sel));

    if (x_i > 0) {
      check_nomsg(r_start=cpl_table_get_nrow(res));
      //sinfo_msg("i=%d x_i=%d w_min=%g w_max=%g",i,x_i,w_min,w_max);
      check_nomsg(cpl_table_insert(res,w_sel,r_start));
    }
    check_nomsg(cpl_table_select_all(range));
  }

  //res = range[1:cpl_table_get_nrow(res)-1];
  sinfo_free_table(&w_sel);


  return res;

 cleanup:
  sinfo_free_table(&w_sel);
  sinfo_free_table(&res);
  return NULL;

}

/**
@name sinfo_get_obj_sky_wav_sub
@param obj input object spectrum
@param sky input sky spectrum
@param wav input full wavelength range
@param sel input selection wavelength range
@param wtol intup wavelength tolerance
@param sub_obj output sub set of object spectrum
@param sub_sky output sub set of sky spectrum
@param sub_wav output sub set of wavelength range
*/

static int
sinfo_get_obj_sky_wav_sub(cpl_table* obj,
                          cpl_table* sky,
                          cpl_table* wav,
                          cpl_table* sel,
                          const double wtol,
                          cpl_table** sub_obj,
                          cpl_table** sub_sky,
                          cpl_table** sub_wav)

{
  cknull_nomsg(*sub_obj = sinfo_table_select_range(obj,sel,wtol));
  cknull_nomsg(*sub_sky = sinfo_table_select_range(sky,sel,wtol));
  cknull_nomsg(*sub_wav = sinfo_table_select_range(wav,sel,wtol));
  return 0;

 cleanup:
  sinfo_free_table(&(*sub_obj));
  sinfo_free_table(&(*sub_sky));
  sinfo_free_table(&(*sub_wav));

  return -1;

}

static int
sinfo_get_sub_regions(cpl_table* sky,
                      cpl_table* x1,
                      cpl_table* pos,
                      const double wtol,
                      const int npixw,
                      cpl_table** res)
{

  cpl_table* x1_sub=NULL;
  cpl_table* x2=NULL;

  int nrow=0;
  int np=0;

  cknull(sky,"Null input sky table");
  cknull(x1 ,"Null input x1 table");
  cknull(pos,"Null input pos table");

  check_nomsg(x2=cpl_table_duplicate(sky));
  check_nomsg(nrow=cpl_table_get_nrow(sky));
  check_nomsg(cpl_table_fill_column_window(x2,"INT",0,nrow,0));

  //x2[x1[pos]] = 10.;
  //x2 = convol(x2,replicate(1,npixw),/edge_truncate,/center);
  //res = where(x2 > 0,hi_i);
  //cpl_table_save(x1, NULL, NULL, "out_x1.fits", CPL_IO_DEFAULT);

  x1_sub=sinfo_table_select_range(x1,pos,wtol);

  if(x1_sub != NULL) {
    ck0_nomsg(sinfo_table_fill_column_over_range(&x2,x1_sub,"INT",10.,wtol));
    sinfo_free_table(&x1_sub);
    check_nomsg(sinfo_convolve_kernel(&x2,npixw/2));
    check_nomsg(np=cpl_table_and_selected_double(x2,"CNV",CPL_GREATER_THAN,0));
    check_nomsg(*res=cpl_table_extract_selected(x2));
    sinfo_free_table(&x2);
    check_nomsg(cpl_table_erase_column(*res,"INT"));
    check_nomsg(cpl_table_erase_column(*res,"CNV"));

  } else {
    cpl_error_reset();
    sinfo_free_table(&x1_sub);
    sinfo_free_table(&x2);

    return np;
  }

  return np;
 cleanup:

  sinfo_free_table(&x1_sub);
  sinfo_free_table(&x2);
  return -1;

}

static cpl_table*
sinfo_table_extract_rest(cpl_table* inp,
                         cpl_table* low,
                         cpl_table* med,
                         const double wtol)
{

  cpl_table* out=NULL;
  double* pinp=NULL;
  double* plow=NULL;
  double* pmed=NULL;
  int nlow=0;
  int nmed=0;

  int nrow=0;
  int i=0;
  int k=0;
  cpl_table* tmp=NULL;

  cknull(inp,"null input table");


  check_nomsg(tmp=cpl_table_duplicate(inp));
  check_nomsg(nrow=cpl_table_get_nrow(tmp));
  check_nomsg(cpl_table_new_column(tmp,"SEL",CPL_TYPE_INT));
  check_nomsg(cpl_table_fill_column_window_int(tmp,"SEL",0,nrow,0));

  check_nomsg(pinp=cpl_table_get_data_double(inp,"WAVE"));
  check_nomsg(plow=cpl_table_get_data_double(low,"WAVE"));
  check_nomsg(pmed=cpl_table_get_data_double(med,"WAVE"));
  nlow=cpl_table_get_nrow(low);


  //check_nomsg(cpl_table_save(low,NULL,NULL,"out_low.fits",CPL_IO_DEFAULT));
  if(nlow > 0) {
    k=0;
    for(i=0;i<nrow;i++) {
      if(fabs(pinp[i]-plow[k]) < wtol) {
    cpl_table_set_int(tmp,"SEL",k,-1);
    k++;
      }
    }
  }
  nmed=cpl_table_get_nrow(med);

  k=0;
  if(nmed > 0) {
    for(i=0;i<nrow;i++) {
      if(fabs(pinp[i]-pmed[k]) < wtol) {
    cpl_table_set_int(tmp,"SEL",k,-1);
    k++;
      }
    }
  }

  check_nomsg(cpl_table_and_selected_int(tmp,"SEL",CPL_GREATER_THAN,-1));
  check_nomsg(out=cpl_table_extract_selected(tmp));
  sinfo_free_table(&tmp);
  check_nomsg(cpl_table_erase_column(out,"SEL"));

  return out;

 cleanup:
  sinfo_free_table(&tmp);
  return NULL;

}

/**@}*/
