/*                                                                           *
 *   This file is part of the ESO X-shooter Pipeline                         *
 *   Copyright (C) 2006 European Southern Observatory                        *
 *                                                                           *
 *   This library 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA    *
 *                                                                           */
/*
 * $Author: amodigli $
 * $Date: 2013-07-12 15:10:08 $
 * $Revision: 1.34 $
 */

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

#include <math.h>
#include <xsh_utils_response.h>
#include <xsh_efficiency_response.h>
#include <xsh_dfs.h>
#include <xsh_pfits_qc.h>
#include <xsh_error.h>
/*--------------------------------------------------------------------------*/
/**
  @defgroup xsh_test_tools     
    Test some tools functions for performances check
  @ingroup unit_tests
*/
/*--------------------------------------------------------------------------*/
/**@{*/

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

static double 
xsh_resample_double(double wout,double* pw1,double* pf1,double wmin,double wmax,int size_obs)
{

  double m=0;
  double w1=0;
  double w2=0;
  double f1=0;
  double f2=0;
  double fout=0;

  int i=0;

  /* are we in case of extrapolation ? */
  if( wout < wmin ) {
  /* w < wmin */
    m=(pf1[1]-pf1[0])/(pw1[1]-pw1[0]);
    fout=m*(wout-wmin)+pf1[0];
  } else if ( wout > wmax ) {
  /* w > wmax */
    m=(pf1[size_obs-1]-pf1[size_obs-2])/(pw1[size_obs-1]-pw1[size_obs-2]);
    fout=m*(wout-wmax)+pf1[size_obs-1];

  } else {
  /* else, interpolate */
    for(i=0;i<size_obs;i++) {
      if(pw1[i]<wout) {
        w1=pw1[i]; 
        f1=pf1[i]; 
      } else {
        w2=pw1[i];
        f2=pf1[i]; 
        break;
      }
    }
    m=(f2-f1)/(w2-w1);   
    fout=m*(wout-w1)+f1;
  }
  return fout;
}

static cpl_table* 
xsh_table_resample_table(cpl_table* tinp,const char* cwinp, const char* cfinp,
		   cpl_table* tref,const char* cwref, const char* cfref)
{

  cpl_table* tres=NULL;
  double* pwinp=NULL;
  double* pwref=NULL;
  double* pfinp=NULL;
  double* pfres=NULL;
  int size_inp=0;
  int size_ref=0;
  double wmin=0;
  double wmax=0;
  int i=0;

  check(size_inp=cpl_table_get_nrow(tinp));
  check(size_ref=cpl_table_get_nrow(tref));
  //xsh_msg("cwinp=%s",cwinp);
  check(wmin=cpl_table_get_column_min(tinp,cwinp));
  check(wmax=cpl_table_get_column_max(tinp,cwinp));
  //xsh_msg("wmin=% vag wmax=%g",wmin,wmax);

  /* we first duplicate ref to resampled table to have the same wavelength 
     sampling and then we take care of computing the flux */
  tres=cpl_table_duplicate(tref);
  check(pwinp=cpl_table_get_data_double(tinp,cwinp));
  check(pwref=cpl_table_get_data_double(tref,cwref));

  check(pfinp=cpl_table_get_data_double(tinp,cfinp));
  check(pfres=cpl_table_get_data_double(tres,cfref));

  for(i=0;i<size_ref;i++) {
    pfres[i]=xsh_resample_double(pwref[i],pwinp,pfinp,wmin,wmax,size_inp);
  }

 cleanup:
  return tres;
}





static cpl_table* 
xsh_table_downsample_table(cpl_table* tinp,const char* cwinp, const char* cfinp,
		   cpl_table* tref,const char* cwref, const char* cfref)
{

  cpl_table* tres=NULL;
  double* pwinp=NULL;
  double* pwref=NULL;
  double* pwres=NULL;

  double* pfinp=NULL;
  //double* pfref=NULL;
  double* pfres=NULL;

  int size_inp=0;
  int size_ref=0;
  double wmin=0;
  double wmax=0;
  int i=0;

  check(size_inp=cpl_table_get_nrow(tinp));
  check(size_ref=cpl_table_get_nrow(tref));
  //xsh_msg("cwinp=%s",cwinp);
  check(wmin=cpl_table_get_column_min(tinp,cwinp));
  check(wmax=cpl_table_get_column_max(tinp,cwinp));
  //xsh_msg("wmin=%g wmax=%g",wmin,wmax);

  /* we create a table of size as large as the reference one */
  tres=cpl_table_new(size_ref);
  /* we create the minimum tables required */
  cpl_table_new_column(tres,cwref,CPL_TYPE_DOUBLE);
  cpl_table_new_column(tres,cfref,CPL_TYPE_DOUBLE);
  cpl_table_fill_column_window_double(tres,cwref,0,size_ref,0.);
  cpl_table_fill_column_window_double(tres,cfref,0,size_ref,0.);

  //cpl_table_dump_structure(tres,stdout);
  check(pwinp=cpl_table_get_data_double(tinp,cwinp));
  check(pwref=cpl_table_get_data_double(tref,cwref));

  check(pfinp=cpl_table_get_data_double(tinp,cfinp));
  check(pfres=cpl_table_get_data_double(tres,cfref));

  check(pwres=cpl_table_get_data_double(tres,cwref));
  check(pfres=cpl_table_get_data_double(tres,cfref));

  for(i=0;i<size_ref;i++) {
    pwres[i]=pwref[i];
    pfres[i]=xsh_resample_double(pwres[i],pwinp,pfinp,wmin,wmax,size_inp);
  }

 cleanup:
  return tres;
}

//re-sample a table to a uniform sampling step */
cpl_table*
xsh_table_resample_uniform(cpl_table* tinp,const char* cwinp, const char* cfinp,
       const double wstp)
{

  cpl_table* tres=NULL;
  double* pwinp=NULL;
  double* pwref=NULL;
  double* pfinp=NULL;
  double* pfres=NULL;
  int size_inp=0;
  double wmin=0;
  double wmax=0;
  int i=0;
  int size=0;
  check(size_inp=cpl_table_get_nrow(tinp));
  //xsh_msg("cwinp=%s",cwinp);
  check(wmin=cpl_table_get_column_min(tinp,cwinp));
  check(wmax=cpl_table_get_column_max(tinp,cwinp));

  size=(int)((wmax-wmin)/wstp+0.5);
  //xsh_msg("wmin=%15.10g wmax=%15.10g wstp=%15.10g size=%d",wmin,wmax,wstp,size);
  /* we first duplicate ref to resampled table to have the same wavelength
     sampling and then we take care of computing the flux */
  tres=cpl_table_new(size);
  cpl_table_new_column(tres,cwinp,CPL_TYPE_DOUBLE);
  cpl_table_new_column(tres,cfinp,CPL_TYPE_DOUBLE);

  cpl_table_fill_column_window_double(tres,cwinp,0,size,0.);
  cpl_table_fill_column_window_double(tres,cfinp,0,size,0.);
  check(pwref=cpl_table_get_data_double(tres,cwinp));
  for(i=0;i<size;i++) {
    pwref[i]=wmin+i*wstp;
  }
  check(pwinp=cpl_table_get_data_double(tinp,cwinp));
  check(pfinp=cpl_table_get_data_double(tinp,cfinp));
  check(pfres=cpl_table_get_data_double(tres,cfinp));

  for(i=0;i<size;i++) {
    pfres[i]=xsh_resample_double(pwref[i],pwinp,pfinp,wmin,wmax,size_inp);
  }

 cleanup:
  return tres;
}

/*
static cpl_error_code
xsh_spectrum_free_table(cpl_table* t)
{

  cpl_table_unwrap(t,"flux");
  cpl_table_unwrap(t,"errs");
  cpl_table_unwrap(t,"qual");
  cpl_table_unwrap(t,"logwave");

  return cpl_error_get_code();
}
*/

static cpl_table*
xsh_spectrum_to_table(xsh_spectrum* s)
{

  cpl_table* table_s=NULL;
  int size=0;
  int i=0;

  double wmin=0;
  double wstp=0;

  double* pwave=NULL;

  double* perrs=NULL;
  double* pflux=NULL;
  int* pqual=NULL;

  double* errs=NULL;
  double* flux=NULL;
  int* qual=NULL;

  size=xsh_spectrum_get_size_lambda(s);
  wmin= xsh_spectrum_get_lambda_min(s);
  wstp= xsh_spectrum_get_lambda_step(s);
  //xsh_msg("wmin=%g[nm] wstp=%g[nm]",wmin,wstp);

  //xsh_msg("Lambda size spectrum=%d",size);
  table_s=cpl_table_new(size);
  cpl_table_new_column(table_s,"wavelength",CPL_TYPE_DOUBLE);
  cpl_table_fill_column_window_double(table_s,"wavelength",0,size,0);
  pwave=cpl_table_get_data_double(table_s,"wavelength");

  cpl_table_new_column(table_s,"flux",CPL_TYPE_DOUBLE);
  cpl_table_fill_column_window_double(table_s,"flux",0,size,0);
  pflux=cpl_table_get_data_double(table_s,"flux");

  cpl_table_new_column(table_s,"errs",CPL_TYPE_DOUBLE);
  cpl_table_fill_column_window_double(table_s,"errs",0,size,0);
  perrs=cpl_table_get_data_double(table_s,"errs");

  cpl_table_new_column(table_s,"qual",CPL_TYPE_INT);
  cpl_table_fill_column_window_int(table_s,"qual",0,size,0);
  pqual=cpl_table_get_data_int(table_s,"qual");


  flux=xsh_spectrum_get_flux(s);
  errs=xsh_spectrum_get_errs(s);
  qual=xsh_spectrum_get_qual(s);

  for(i=0;i<size;i++) {
    pwave[i]=wmin+i*wstp;
    pflux[i]=flux[i];
    perrs[i]=errs[i];
    pqual[i]=qual[i];
  }

  cpl_table_duplicate_column(table_s,"logwave",table_s,"wavelength");
  cpl_table_logarithm_column(table_s,"logwave",CPL_MATH_E);

  return table_s;

}

static cpl_error_code xsh_evaluate_tell_model(cpl_table* corr,
    xsh_instrument* instrument, const int ext, double* mean, double* rms) {

  HIGH_ABS_REGION * phigh = NULL;
  int nsamples = 0;
  cpl_table* tab_tmp = NULL;
  cpl_table* tab_stack = NULL;
  char fname[256];
  phigh = xsh_fill_tell_compute_resid_regions(instrument, NULL);
  /* we remove from table points that should not be sampled */
  if (phigh != NULL) {
    int counter = 0;
    //int kh = 0;
    int nrow = 0;
    //xsh_msg("Extract from correction only sub-regions to compute residuals" );
    for (; phigh->lambda_min != 0.; phigh++) {
      //xsh_msg("Flag [%g,%g]",phigh->lambda_min,phigh->lambda_max);
      nrow = cpl_table_and_selected_double(corr, "wave", CPL_NOT_LESS_THAN,
          phigh->lambda_min);
      //xsh_msg("Select min=%d",nrow);
      nrow = cpl_table_and_selected_double(corr, "wave", CPL_LESS_THAN,
          phigh->lambda_max);
      //xsh_msg("Select max=%d",nrow);
      tab_tmp = cpl_table_extract_selected(corr);
      cpl_table_select_all(corr);
      if (counter == 0) {
        //xsh_msg("create stack table nraws=%d",cpl_table_get_nrow(tab_corr));
        tab_stack = cpl_table_duplicate(tab_tmp);
        counter++;
      } else {
        nrow = cpl_table_get_nrow(tab_stack);
        //xsh_msg("append to stack table size=%d",nrow);
        cpl_table_insert(tab_stack, tab_tmp, nrow);
      }
      xsh_free_table(&tab_tmp);
      nsamples++;
    }

  }
  sprintf(fname, "tab_corr_sampl.fits");
  //xsh_table_save(tab_stack, NULL, NULL, fname, ext);

  *mean = fabs(  ( cpl_table_get_column_mean(tab_stack, "correction") - 1. ) );
  *rms = cpl_table_get_column_stdev(tab_stack, "correction");

  //xsh_msg("mean=%g rms=%g", *mean, *rms);
  xsh_free_table(&tab_tmp);
  xsh_free_table(&tab_stack);
  return cpl_error_get_code();

}

static cpl_table*
xsh_extract_ranges_to_fit(cpl_table* table_in,const char* cwav,xsh_instrument* instrument) {

  cpl_table* stack = NULL;
  HIGH_ABS_REGION * phigh = NULL;

  phigh = xsh_fill_tell_compute_resid_regions(instrument, NULL);

  cpl_table* tab_tmp = NULL;
  int nsamples = 0;
  /*
  cpl_table_dump(table_in,0,2,stdout);
  xsh_msg("wmin=%g wmax=%g",
          cpl_table_get_column_min(table_in,cwav),
         cpl_table_get_column_max(table_in,cwav));
  */
  /* we remove from table points that should not be sampled */
  if (phigh != NULL) {
    int counter = 0;
    //int kh = 0;
    int nrow = 0;
    //xsh_msg("Extract from ratio only regions to be sampled" );
    for (; phigh->lambda_min != 0.; phigh++) {
      //xsh_msg("Flag [%g,%g]",phigh->lambda_min,phigh->lambda_max);
      check(nrow = cpl_table_and_selected_double(table_in, cwav, CPL_NOT_LESS_THAN,
						 phigh->lambda_min));
      //xsh_msg("Select min=%d",nrow);
      check(nrow = cpl_table_and_selected_double(table_in,cwav, CPL_LESS_THAN,
						 phigh->lambda_max));
      //xsh_msg("Select max=%d",nrow);
      check(tab_tmp = cpl_table_extract_selected(table_in));
      cpl_table_select_all(table_in);
      if (counter == 0) {
        //xsh_msg("create stack table");
        stack = cpl_table_duplicate(tab_tmp);
        counter++;
      } else {
        nrow=cpl_table_get_nrow(stack);
        //xsh_msg("append to stack table size=%d",nrow);
        cpl_table_insert(stack, tab_tmp, nrow);
      }
      xsh_free_table(&tab_tmp);
      nsamples++;
    }
  }
  xsh_free_table(&tab_tmp);

 cleanup:
  return stack;
}
/*
static cpl_error_code
xsh_extract_points_to_fit(cpl_table* table,const char* cwav,const char* cratio, int nsamples, xsh_instrument* instrument,cpl_vector** vec_wave,cpl_vector** vec_flux)
{


  double* pvec_wave = NULL;
  double* pvec_flux = NULL;
  int kh=0;
  HIGH_ABS_REGION * phigh = NULL;
  cpl_table* tab_tmp=NULL;
  double wmin=0;
  double wmax=0;
  double wstp=1.;
  int nsel=0;
  double flux_sto=0;
  double wmin_fit_region=9999;
  double wmax_fit_region=-9999;
  int kh_min=0;
  int kh_max=0;

  wmin=cpl_table_get_column_min(table,cwav);
  wmax=cpl_table_get_column_max(table,cwav);

  phigh=xsh_fill_tell_fit_regions(instrument,NULL);
  for (kh = 0; phigh->lambda_min != 0.; phigh++) {
    if( wmin_fit_region>  phigh->lambda_min) wmin_fit_region=phigh->lambda_min;
    if( wmax_fit_region<  phigh->lambda_max) wmax_fit_region=phigh->lambda_max;
  }
  xsh_msg("wmin_fit_region=%g wmax_fit_region=%g",wmin_fit_region,wmax_fit_region);
  xsh_msg("wmin=%g wmax=%g",wmin,wmax);
  //if(wmin_fit_region<wmin) {
    nsamples+=1;
    kh_min=1;
    //}

  if(wmax_fit_region>wmax) {
    nsamples+=1;
    kh_max=nsamples-1;
  }


  *vec_wave = cpl_vector_new(nsamples);
  *vec_flux = cpl_vector_new(nsamples);
  pvec_wave = cpl_vector_get_data(*vec_wave);
  pvec_flux = cpl_vector_get_data(*vec_flux);
  //xsh_msg("sk1 nsamples=%d",nsamples);

  //if(wmin_fit_region<wmin) {
    // add one point at the start of the wave range 
    cpl_table_and_selected_double(table, cwav, CPL_LESS_THAN,wmin+wstp);
    tab_tmp = cpl_table_extract_selected(table);

    pvec_wave[0] = wmin;
    check(pvec_flux[0] = cpl_table_get_column_median(tab_tmp, cratio));
    cpl_table_select_all(table);
    xsh_free_table(&tab_tmp);
    //}

  phigh=xsh_fill_tell_fit_regions(instrument,NULL);
  if (phigh != NULL) {
    //int nrow = 0;
    //xsh_msg("Fill median ratio values" );
    for (kh = kh_min; phigh->lambda_min != 0.; phigh++) {
      //xsh_msg("Process line %d Flag [%g,%g]",kh,phigh->lambda_min,phigh->lambda_max);
      cpl_table_and_selected_double(table, cwav,
          CPL_NOT_LESS_THAN, phigh->lambda_min);
      //xsh_msg("Select min=%d",nrow);
      nsel=cpl_table_and_selected_double(table, cwav, CPL_LESS_THAN,
          phigh->lambda_max);
      //xsh_msg("Select max=%d",nrow);
      if(nsel>0) {
	tab_tmp = cpl_table_extract_selected(table);
	cpl_table_select_all(table);

	pvec_wave[kh] = 0.5 * (phigh->lambda_max + phigh->lambda_min);
	check(pvec_flux[kh] = cpl_table_get_column_median(tab_tmp, cratio));
	flux_sto=pvec_flux[kh];
	xsh_free_table(&tab_tmp);
        //xsh_msg("wave=%g",pvec_wave[kh+1]);
      } else {
	pvec_wave[kh] = 0.5 * (phigh->lambda_max + phigh->lambda_min);
	pvec_flux[kh]=flux_sto;
	//xsh_msg("wave=%g",pvec_wave[kh+1]);
      }
      kh++;
      //TO_BE_FIXED
    }
  }

  if(wmax_fit_region>wmax) {
    cpl_table_select_all(table);

    cpl_table_and_selected_double(table, cwav, CPL_NOT_LESS_THAN,wmax-wstp);
    tab_tmp = cpl_table_extract_selected(table);
 
    pvec_wave[kh_max] = wmax;
    xsh_msg("wave=%g",pvec_wave[kh+1]);
    check(pvec_flux[kh_max] = cpl_table_get_column_median(tab_tmp, cratio));
    xsh_free_table(&tab_tmp);
  }

 cleanup:

  return cpl_error_get_code();
}
*/

static cpl_error_code
xsh_extract_points_to_fit(cpl_table* table,const char* cwav,const char* cratio, const int nsamples, xsh_instrument* instrument,cpl_vector** vec_wave,cpl_vector** vec_flux)
{


  double* pvec_wave = NULL;
  double* pvec_flux = NULL;
  int kh=0;
  HIGH_ABS_REGION * phigh = NULL;
  cpl_table* tab_tmp=NULL;
  double wmin=0;
  double wmax=0;
  double wstp=1.;
  /*
  double wmin_fit_region=9999;
  double wmax_fit_region=-9999;
  */

  wmin=cpl_table_get_column_min(table,cwav);
  wmax=cpl_table_get_column_max(table,cwav);

  *vec_wave = cpl_vector_new(nsamples+2);
  *vec_flux = cpl_vector_new(nsamples+2);

  /*
  phigh=xsh_fill_tell_fit_regions(instrument,NULL);
  for (kh = 0; phigh->lambda_min != 0.; phigh++) {
    if( wmin_fit_region>  phigh->lambda_min) wmin_fit_region=phigh->lambda_min;
    if( wmax_fit_region<  phigh->lambda_max) wmax_fit_region=phigh->lambda_max;
  }

  if(wmax_fit_region>wmax) {
  *vec_wave = cpl_vector_new(nsamples+2);
  *vec_flux = cpl_vector_new(nsamples+2);
  } else {
  *vec_wave = cpl_vector_new(nsamples+1);
  *vec_flux = cpl_vector_new(nsamples+1);
  }
  */
  pvec_wave = cpl_vector_get_data(*vec_wave);
  pvec_flux = cpl_vector_get_data(*vec_flux);
  //xsh_msg("sk1 nsamples=%d",nsamples);

  // add one point at the start of the wave range 
  cpl_table_and_selected_double(table, cwav, CPL_LESS_THAN,wmin+wstp);
  tab_tmp = cpl_table_extract_selected(table);
  pvec_wave[0] = wmin;
  pvec_flux[0] = cpl_table_get_column_median(tab_tmp, cratio);
  cpl_table_select_all(table);
  xsh_free_table(&tab_tmp);

  phigh=xsh_fill_tell_fit_regions(instrument,NULL);
  if (phigh != NULL) {
    //int nrow = 0;
    //xsh_msg("Fill median ratio values" );
    for (kh = 0; phigh->lambda_min != 0.; phigh++) {
      //xsh_msg("Process line %d Flag [%g,%g]",kh,phigh->lambda_min,phigh->lambda_max);
      cpl_table_and_selected_double(table, cwav,
          CPL_NOT_LESS_THAN, phigh->lambda_min);
      //xsh_msg("Select min=%d",nrow);
      cpl_table_and_selected_double(table, cwav, CPL_LESS_THAN,
          phigh->lambda_max);
      //xsh_msg("Select max=%d",nrow);
      tab_tmp = cpl_table_extract_selected(table);
      cpl_table_select_all(table);

      pvec_wave[kh+1] = 0.5 * (phigh->lambda_max + phigh->lambda_min);
      pvec_flux[kh+1] = cpl_table_get_column_median(tab_tmp, cratio);
      kh++;
      xsh_free_table(&tab_tmp);
    }
  }

  /*
  if(wmax_fit_region>wmax) {
    cpl_table_select_all(table);

    cpl_table_and_selected_double(table, cwav, CPL_NOT_LESS_THAN,wmax-wstp);
    tab_tmp = cpl_table_extract_selected(table);
 
    pvec_wave[kh+1] = wmax;
    xsh_msg("wave=%g",pvec_wave[kh+1]);
    check(pvec_flux[kh+1] = cpl_table_get_column_median(tab_tmp, cratio));
    xsh_free_table(&tab_tmp);
  }
  */
  
  
  cpl_table_select_all(table);

  cpl_table_and_selected_double(table, cwav, CPL_NOT_LESS_THAN,wmax-wstp);
  tab_tmp = cpl_table_extract_selected(table);
 
  pvec_wave[kh+1] = wmax;
  pvec_flux[kh+1] = cpl_table_get_column_median(tab_tmp, cratio);
  xsh_free_table(&tab_tmp);
  

 //cleanup:

  return cpl_error_get_code();
}

static cpl_error_code
xsh_get_xcorrel_peak(cpl_vector* wcorr, cpl_vector* fcorr,XSH_GAUSSIAN_FIT* gfit,const double range,const int ext)
{
  int size_x;
  cpl_table* tab = NULL;
  cpl_table* ext_tab;
  char fname[256];
  cpl_vector* wave=NULL;
  cpl_vector* flux=NULL;

  size_x=cpl_vector_get_size(wcorr);
  tab = cpl_table_new(size_x);
  cpl_table_wrap_double(tab, cpl_vector_get_data(wcorr), "logwave");
  cpl_table_wrap_double(tab, cpl_vector_get_data(fcorr), "flux");

  sprintf(fname, "fcorr_org.fits");
  //xsh_table_save(tab,NULL,NULL,fname,ext);
  cpl_table_and_selected_double(tab, "logwave", CPL_GREATER_THAN,
      (*gfit).peakpos - range);
  cpl_table_and_selected_double(tab, "logwave", CPL_LESS_THAN,
      (*gfit).peakpos + range);

  ext_tab = cpl_table_extract_selected(tab);
  cpl_table_unwrap(tab,"logwave");
  cpl_table_unwrap(tab,"flux");
  xsh_free_table(&tab);
  sprintf(fname, "fcorr_ext.fits");
  //xsh_table_save(ext_tab,NULL,NULL,fname,ext);
  int next = 0;
  next = cpl_table_get_nrow(ext_tab);
  sprintf(fname, "fcorr_tab.fits");
  //xsh_table_save(ext_tab,NULL,NULL,fname,ext);
  double peakpos=0;
  double sigma=0;
      double area=0;
      double offset=0;
      double mse=0;

  wave = cpl_vector_wrap(next, cpl_table_get_data_double(ext_tab, "logwave"));
  flux = cpl_vector_wrap(next, cpl_table_get_data_double(ext_tab, "flux"));
  //xsh_msg("gauss before fit: peak=%g sigma=%g",(*gfit).peakpos,(*gfit).sigma);

  cpl_vector_fit_gaussian( wave, NULL, flux,NULL, CPL_FIT_ALL,
       &peakpos, &sigma,&area, &offset, &mse,NULL,NULL);
  //xsh_msg("gauss check fit: peak=%g sigma=%g",peakpos,sigma);
  cpl_vector_fit_gaussian( wave, NULL, flux,NULL, CPL_FIT_ALL,
      &gfit->peakpos, &gfit->sigma,&gfit->area, &gfit->offset, &gfit->mse,NULL,NULL);
  //double fwhm = CPL_MATH_FWHM_SIG * gfit.sigma;
  //xsh_msg("gauss after fit: peak=%g sigma=%g",(*gfit).peakpos,(*gfit).sigma);

  //cleanup:
  cpl_vector_unwrap(wave);
  cpl_vector_unwrap(flux);
  xsh_free_table(&ext_tab);
  return cpl_error_get_code();
}

static cpl_table*
xsh_table_select_range(cpl_table* table_in,const char* col,const double wmin, const double wmax)
{
  cpl_table* table_ext=NULL;

  cpl_table_and_selected_double(table_in, col, CPL_NOT_LESS_THAN, wmin);
  cpl_table_and_selected_double(table_in, col, CPL_NOT_GREATER_THAN, wmax);
  table_ext=cpl_table_extract_selected(table_in);

  return table_ext;
}

/* perform correlation of spectrum and model over a given range. Correlation peak determination is refined by Gauss fit
   * result is the shift to be applied and the FWHM of the correlation function (useful to make 2nd iteration of correlation
   * near peak pos)
   */
cpl_error_code
xsh_correl_spectra(double* flux_s, double* flux_m, const int size,
                   const int hsearch, const double wlogstp,const int norm_xcorr,
		   const double range, const int ext,XSH_GAUSSIAN_FIT* gfit)
{
  double* xcorr = NULL;
  double* pwcorr = NULL;
  double corr = 0;
  double delta=0;
  char fname[256];
  int i=0;
  double size_corr = 2 * hsearch + 1;
  cpl_vector* f1=NULL;
  cpl_vector* f2=NULL;
  cpl_vector* correl=NULL;
  double shift=0;
  cpl_vector* fcorr = NULL;
  cpl_vector* wcorr = NULL;

  check(xcorr = xsh_function1d_xcorrelate(flux_s, size, flux_m, size, hsearch,
					  norm_xcorr,&corr, &delta));


  check(f1=cpl_vector_wrap(size, flux_s));
  f2=cpl_vector_wrap(size, flux_m);
  //cpl_vector_save(f1,"f1.fits",CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);
  //cpl_vector_save(f2,"f2.fits",CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);
  //xsh_msg("size=%d",size);
  correl=cpl_vector_new(size_corr);
  check(shift=cpl_vector_correlate(correl,f1,f2));
  cpl_vector_unwrap(f1);
  cpl_vector_unwrap(f2);
  //cpl_vector_save(correl,"correl.fits",CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);
  xsh_msg("shift=%g",shift);
  //xsh_msg("correlation value=%g measured shift[sample units]=%g measured shift[lognm]=%g", corr, delta,delta*);
  //xsh_msg("hsearch=%d delta=%g sampling step=%g",hsearch,delta,wlogstp);

  gfit->peakpos = (hsearch + delta)*wlogstp;
  gfit->sigma = wlogstp*10;
  gfit->area = 1.;

  xsh_msg("gauss guess: peak: %12.8g sigma %g", gfit->peakpos, gfit->sigma);

  check(fcorr = cpl_vector_wrap(size_corr, xcorr));
  /* AMO changed: to remove a leak (next we override values and
   * later we do cpl_vector_unwrap
   */
  pwcorr= (double*) cpl_calloc(size_corr, sizeof(double));
  for (i = 0; i < size_corr; i++) {
    pwcorr[i] = i*wlogstp;
  }
  check(wcorr = cpl_vector_wrap(size_corr, pwcorr));
  sprintf(fname,"fcorr.fits");
  //xsh_vector_save(fcorr,NULL,fname,ext);
  sprintf(fname,"wcorr.fits");
  //check(xsh_vector_save(wcorr,NULL,fname,ext));

  /* 4) fit Gaussian to +/-0.001 in log_lam_nm around cross correlation peak */
  check(xsh_get_xcorrel_peak(wcorr,fcorr,gfit,range,ext));

  xsh_msg("gauss fit: peak[lognm]: %12.8g sigma[lognm] %g peak[sampl_units]: %12.8g sigma[sampl_units] %g",
      gfit->peakpos, gfit->sigma,gfit->peakpos/wlogstp, gfit->sigma/wlogstp);


  cleanup:
  cpl_vector_unwrap(fcorr);
  cpl_vector_unwrap(wcorr);
  xsh_free_vector(&correl);
  cpl_free(xcorr);
  cpl_free(pwcorr);
  return cpl_error_get_code();

}
static cpl_error_code
xsh_align_model_to_spectrum(cpl_table* table_me,cpl_table* table_se,const double wlogstp,const int norm_xcorr,const int ext,cpl_table** table_mm)
{
  cpl_table* table_rm = NULL;
  cpl_table* table_rs = NULL;
  char fname[256];
  int i=0;
  int size_mm=cpl_table_get_nrow(*table_mm);
  //cpl_lowpass filter_type = CPL_LOWPASS_LINEAR;
  cpl_vector* vfluxm = NULL;
  cpl_vector* vlowpass_fluxm = NULL;

  /* TODO: this re-sampling was decided not to use */
  //check(cpl_table_dump_structure(table_me,stdout));
  //xsh_msg("logspt=%g",wlogstp);
  check(table_rm = xsh_table_resample_uniform(table_me, "logwave", "flux", wlogstp));
  //xsh_msg("size of spectrum tab=%d",(int)cpl_table_get_nrow(table_rm));
  sprintf(fname, "model_selected_wrange_log_resampled_uniform.fits");
  //check(xsh_table_save(table_rm,NULL,NULL,fname,ext));


  check(table_rs = xsh_table_downsample_table(table_se, "logwave", "flux", table_rm,
					      "logwave", "flux"));

  sprintf(fname, "spectrum_resampled_used_in_correlation.fits");
  //xsh_table_save(table_rs,NULL,NULL,fname,ext);

  /* perform correlation of spectrum and model over a given range. Correlation peak determination is refined by Gauss fit
   * result is the shift to be applied and the FWHM of the correlation function (useful to make 2nd iteration of correlation
   * near peak pos)
   */

  //double corr = 0;
  double* flux_s = NULL;
  double* flux_m = NULL;
  int size_s = 0;
  int hsearch = 200;
  /*
  int size_m = 0;

  double delta = 0;
  double size_x = 2 * hsearch + 1;
  double* xcorr = NULL;
  double* pwcorr = NULL;
  int size=0;
  */
  XSH_GAUSSIAN_FIT gfit;

  double wrange=0.0005;
  //size=cpl_table_get_nrow(table_rs);

  flux_s = cpl_table_get_data_double(table_rs, "flux");
  flux_m = cpl_table_get_data_double(table_rm, "flux");
  size_s = cpl_table_get_nrow(table_rs);
  //size_m = cpl_table_get_nrow(table_me);

  xsh_msg("hsearch=%d",hsearch);
  check(xsh_correl_spectra(flux_s,flux_m,size_s,hsearch,wlogstp,norm_xcorr,wrange,ext,&gfit));
  //xsh_msg("sigma=%g",gfit.sigma);
  xsh_msg("computed shift[lognm]=%g",(gfit.peakpos-hsearch*wlogstp));
  hsearch=(int)3.*CPL_MATH_FWHM_SIG * gfit.sigma/wlogstp;
  xsh_msg("hsearch=%d",hsearch);
  check(xsh_correl_spectra(flux_s,flux_m,size_s,hsearch,wlogstp,norm_xcorr,wrange,ext,&gfit));

  /* 5: adjust model wavelength scale:
   :log_lam_nm_0 = :log_lam_nm+<position>
   */
  cpl_table_add_scalar(*table_mm, "logwave", (gfit.peakpos-hsearch*wlogstp));
  xsh_msg("computed shift[lognm]=%g",(gfit.peakpos-hsearch*wlogstp));
  //xsh_table_save(*table_mm,NULL,NULL,"model_shift.fits",ext);


  /*
   6) create Gaussian with measured FWHM (corrected by FWHM of model spectrum,
   but that effect is small since the model spectra have a resolution of 60000,
   so the FWHM of the cross-correlation peak is dominated by the observation)

   5) convolve model with Gaussain in log_lam_nm space (X-shooter has contant lambda/dlambda)

   We apply this operations to the model sub-range corresponding to the spectrum
   */

  /* NB:
   * the only eventually allowed filer in cpl_vector_filter_lowpass_create is CPL_LOWPASS_LINEAR
   * as CPL_LOWPASS_GAUSSIAN uses a fixed FWHM. Checking result we later preferred to use
   * cpl_wlcalib_xc_convolve_create_kernel that is deprecated but gives better results.
   * There the FWHM parameter is actually the sigma: FWHM/CPL_MATH_FWHM_SIG
   */

  double* pvlowpass_fluxm = NULL;
  double* pflux_c = NULL;
  double fwhm = CPL_MATH_FWHM_SIG * gfit.sigma;
  double fwhm_nm = pow(CPL_MATH_E, fwhm);
  cpl_vector      *   conv_kernel =NULL;
  //cpl_vector * spec_clean=NULL;
  //int fwhm_pix=(int)(fwhm_nm+0.50000);
  int fwhm_pix = (int) (fwhm / wlogstp + 0.5);

  cpl_table_new_column(*table_mm, "flux_c", CPL_TYPE_DOUBLE);
  cpl_table_fill_column_window_double(*table_mm, "flux_c", 0, size_mm, 0);
  xsh_msg("fwhm_nm=%12.10g fwhm_pix=%d",fwhm_nm,fwhm_pix);

  vfluxm = cpl_vector_wrap(size_mm,cpl_table_get_data_double(*table_mm, "flux"));

  /* we use CPL deprecated function but it works properly */
  conv_kernel = cpl_wlcalib_xc_convolve_create_kernel(fwhm_pix/CPL_MATH_FWHM_SIG,fwhm_pix/CPL_MATH_FWHM_SIG);
  vlowpass_fluxm=cpl_vector_duplicate(vfluxm);
  cpl_wlcalib_xc_convolve(vlowpass_fluxm, conv_kernel);

  //vlowpass_fluxm = cpl_vector_filter_lowpass_create(vfluxm, filter_type,fwhm_pix);

  pvlowpass_fluxm = cpl_vector_get_data(vlowpass_fluxm);
  cpl_vector_unwrap(vfluxm);

  pflux_c = cpl_table_get_data_double(*table_mm, "flux_c");
  for (i = 0; i < size_mm; i++) {
    pflux_c[i] = pvlowpass_fluxm[i];
  }
  sprintf(fname, "model_convolved.fits");
  //xsh_table_save(*table_mm,NULL,NULL,fname,ext);

  cleanup:

  xsh_free_vector(&conv_kernel);
  xsh_free_vector(&vlowpass_fluxm);
  xsh_free_table(&table_rs);
  xsh_free_table(&table_rm);

  return cpl_error_get_code();

}

cpl_table* 
xsh_telluric_model_eval(cpl_frame* frame_m,xsh_spectrum* s,xsh_instrument* instrument,cpl_size* model_idx)
{

  cpl_table* tab_res=NULL;
  cpl_table* table_s=NULL;
  cpl_table* table_m=NULL;
  cpl_table* table_se=NULL;
  cpl_table* table_mm=NULL;
  cpl_table* table_rm = NULL;
  cpl_table* table_rs_div_mc=NULL;
  cpl_table* table_me=NULL;
  cpl_table* table_rs=NULL;
  const char* name_model=NULL;

  double wmin=0;
  double wmax=0;
  //int i=0;
  double lmin=7.0; //1096.633 nm
  double lmax=7.1; //1211.967 nm
  double wlogstp = 10.e-6;

  double um2nm=1000.; // 1nm

  int ext=0;
  int next=0;
  double mean=0;
  double rms=0;
  //int kh=0;
  int nsamples=0;
  char fname[256];
  cpl_table* tab_corr = NULL;
  cpl_table* tab_stack = NULL;
  //int size_mm=0;
  cpl_table* ext_tab = NULL;
  double* pmean=NULL;
  double* prms=NULL;
  int* pext=NULL;
  int norm_xcorr=0;
  HIGH_ABS_REGION * phigh = NULL;
  cpl_propertylist* qc_head=NULL;

  if(xsh_instrument_get_arm(instrument) == XSH_ARM_VIS) {
    lmin=6.828; //(923.342 nm)
    lmax=6.894; //(986.339 nm)
    //lmax=6.888; //(986.339 nm)
    wlogstp = 7.826e-6; //(1 nm)
    norm_xcorr=1;
  }

  wmin= xsh_spectrum_get_lambda_min(s);
  wmax= xsh_spectrum_get_lambda_max(s);
  //xsh_msg("wmin=%g wmax=%g",wmin,wmax);
  //wstp= xsh_spectrum_get_lambda_step(s);
  check(table_s=xsh_spectrum_to_table(s));
  sprintf(fname,"spectrum_obs.fits");
  //check(xsh_table_save(table_s,NULL,NULL,fname,0));
  check(phigh=xsh_fill_tell_fit_regions(instrument,NULL));

  for (; phigh->lambda_min != 0.; phigh++) {
    nsamples++;
  }

  check(name_model=cpl_frame_get_filename(frame_m));
  //cpl_frame_set_type(frame_m,CPL_FRAME_TYPE_TABLE);
  check(next=cpl_frame_get_nextensions(frame_m));
  check(tab_res=cpl_table_new(next));
  cpl_table_new_column(tab_res,"id",CPL_TYPE_INT);
  cpl_table_new_column(tab_res,"mean",CPL_TYPE_DOUBLE);
  cpl_table_new_column(tab_res,"rms",CPL_TYPE_DOUBLE);

  cpl_table_fill_column_window_int(tab_res, "id", 0, next, 0);
  cpl_table_fill_column_window_double(tab_res, "mean", 0, next, 0);
  cpl_table_fill_column_window_double(tab_res, "rms", 0, next, 0);
  pext=cpl_table_get_data_int(tab_res,"id");
  pmean=cpl_table_get_data_double(tab_res,"mean");
  prms=cpl_table_get_data_double(tab_res,"rms");
  check(next=cpl_table_get_nrow(tab_res));

  //xsh_msg("name_model %s",name_model);
  for (ext = 0; ext < next; ext++) {

    /* load input model spectrum from appropriate extension */
    check(table_m = cpl_table_load(name_model, ext+1, 1));
    sprintf(fname,"model_arm.fits");
    //check(xsh_table_save(table_m,NULL,NULL,fname,ext));
    /* the model wavelength are in um units, we need to convert to nm */
    cpl_table_multiply_scalar(table_m, "lam", um2nm);
    cpl_table_duplicate_column(table_m, "logwave", table_m, "lam");
    cpl_table_logarithm_column(table_m, "logwave", CPL_MATH_E);
    //check(xsh_table_save(table_m,NULL,NULL,fname,ext));
    /* we restrict model to range covered by observation */
    table_mm=xsh_table_select_range(table_m,"lam",wmin, wmax);
    xsh_free_table(&table_m);
    //size_mm = cpl_table_get_nrow(table_mm);
    sprintf(fname,"model_range_arm.fits");
    //check(xsh_table_save(table_mm,NULL,NULL,fname,ext));

    /* Step 2: extract the proper ranges for cross correlation */
    /* Apply this to the model */
    table_me=xsh_table_select_range(table_mm,"logwave",lmin, lmax);


    /* make a test to verify that exponential of log comes back the same
     as original wave column value 
     check(cpl_table_duplicate_column(table_me,"wav",table_me,"logwave"));
     check(cpl_table_exponential_column(table_me,"wav",CPL_MATH_E));
     */
    sprintf(fname,"model_selected_wrange_log.fits");
    //check(xsh_table_save(table_me,NULL,NULL,fname,ext));

    /* Apply this to the spectrum */
    table_se=xsh_table_select_range(table_s,"logwave",lmin, lmax);
    cpl_table_duplicate_column(table_se, "wav", table_se, "logwave");
    cpl_table_exponential_column(table_se, "wav", CPL_MATH_E);
    sprintf(fname,"spectrum_selected_wrange_log.fits");
    //check(xsh_table_save(table_se,NULL,NULL,fname,ext));

    /* Step 3: now we need to do the cross-correlation:
     Before doing this we need to be sure that each spectrum is sampled 
     to the same point. We use the model as reference and re-sample (linearly)
     the observed spectrum so that we can then compute the cross-correlation

     Step 4: fit Gaussian to +/-0.001 in log_lam_nm around cross correlation peak

     Step 5: adjust model wavelength scale:
     :log_lam_nm_0 = :log_lam_nm+<position>


      6) create Gaussian with measured FWHM (corrected by FWHM of model spectrum,
      but that effect is small since the model spectra have a resolution of 60000,
      so the FWHM of the cross-correlation peak is dominated by the observation)

      5) convolve model with Gaussain in log_lam_nm space (X-shooter has contant lambda/dlambda)

      We apply this operations to the model sub-range corresponding to the spectrum
      */
    //xsh_msg("size of model tab=%d",(int)cpl_table_get_nrow(table_me));
    //xsh_msg("size of spectrum tab=%d",(int)cpl_table_get_nrow(table_se));
    //check(xsh_table_save(table_me,NULL,NULL,"table_me.fits",ext));
    //check(xsh_table_save(table_se,NULL,NULL,"table_se.fits",ext));
    check(xsh_align_model_to_spectrum(table_me,table_se,wlogstp,norm_xcorr,ext,&table_mm));
    xsh_free_table(&table_me);
    xsh_free_table(&table_se);

    //check(xsh_table_save(table_mm,NULL,NULL,"model_shift.fits",ext));

    /*
     6) convert convolved and sifted model to lam_nm space
     */
    check(cpl_table_duplicate_column(table_mm,"wave",table_mm,"logwave"));
    check(cpl_table_exponential_column(table_mm,"wave",CPL_MATH_E));
    sprintf(fname,"model_aligned_to_obs.fits");

    //check(xsh_table_save(table_mm,NULL,NULL,fname,ext));

    /*
     7) divide observed spectrum by convolved and shifted model
     Here we get a telluric-corrected observed spectrum
     */
    xsh_free_table(&table_rs);
   
    xsh_free_table(&table_rm);
    /* In order to divide spectrum by model we need to put them on same sampling steps:
     * If we re-sample the model to the coarser spectrum step we introduce artifacts in the re-sampled model spectrum
     * Better is to up-sample the observed spectrum to the model. This simply artificially refine the observed spectrum
     */

    /* NOTE THAT WE NEED table_rpfitm at the very end of this function when we downsample table_rs */
    check(table_rm = xsh_table_resample_table(table_mm,"wave","flux",table_s,"wavelength","flux"));
    sprintf(fname,"table_rm.fits");
    //check(xsh_table_save(table_rm,NULL,NULL,fname,ext));


    check(
        table_rs=xsh_table_resample_table(table_s,"wavelength","flux",table_mm,"wave","flux"));

    //PIPPO
    sprintf(fname,"spectrum_resampled_to_model.fits");
    //check(xsh_table_save(table_rs,NULL,NULL,fname,ext));

    cpl_table_duplicate_column(table_rs, "flux_model_convolved", table_mm, "flux_c");
    cpl_table_duplicate_column(table_rs, "ratio", table_rs, "flux");
    xsh_free_table(&table_mm);
    check(cpl_table_divide_columns(table_rs,"ratio","flux_model_convolved"));
    /* NB: the ratio spectrum corresponding to the model that minimizes
           the resisiduals is actually the final product we need to use for
           the final response computation */
     
    sprintf(fname,"spectrum_observed_divided_by_model_convolved.fits");
    check(xsh_table_save(table_rs,NULL,NULL,fname,ext));
    table_rs_div_mc=cpl_table_duplicate(table_rs);
     /*
     8) use predefined continuum points (avoiding regions of strong
     telluric absorption as well as known stellar lines) and fit spline
     without smoothing

     (points.list, email from Sabine).
     Take around each of them +/- 1nm. 
     take the median in the range [-1nm+wc,wc+1nm]
     and then fit a spline (order 3: cubic)
     No smoothing
     This gives the continuum fit
     */

    check(tab_stack=xsh_extract_ranges_to_fit(table_rs,"wave",instrument));
    sprintf(fname,"tab_stack.fits");

    //check(xsh_table_save(tab_stack,NULL,NULL,fname,ext));

    /* here we make the spline fit of median flux computed in each range
     * (to rule out that oscillations in each range creates problems)
     * */

    cpl_vector* vec_wave = NULL;
    cpl_vector* vec_flux = NULL;
    //double* pwav=NULL;
    
    //check(xsh_table_save(table_rs,NULL,NULL,"pippo.fits",ext));
    check(xsh_extract_points_to_fit(table_rs,"wave","ratio",nsamples,instrument,&vec_wave,&vec_flux));
   
    //pwav = cpl_table_get_data_double(table_rm, "wave");
    double* sfit=NULL;
    double* pfit=NULL;
    //double* pwave=NULL;
    //cpl_vector_dump(vec_wave,stdout);
   //cpl_vector_dump(vec_flux,stdout);

   /*

    double* pflux=NULL;

    sprintf(fname,"ratio_sel.fits");
    check(xsh_table_save(tab_stack,NULL,NULL,fname,ext));
    */
    int nsel = 0;

    check(nsel=cpl_table_get_nrow(tab_stack));
    cpl_table_new_column(tab_stack, "fit", CPL_TYPE_DOUBLE);
    cpl_table_fill_column_window_double(tab_stack, "fit", 0, nsel, 0);
    pfit = cpl_table_get_data_double(tab_stack, "fit");
    //pwave = cpl_table_get_data_double(tab_stack, "wave");
  
    /*


     pwav = cpl_table_get_data_double(tab_stack, "wave");
     pflux = cpl_table_get_data_double(tab_stack, "ratio");
     vflux=cpl_vector_wrap(size_mm,pflux);
     vflux_med=cpl_vector_filter_median_create(vflux,15);
     xsh_msg("size_mm=%d",size_mm);
     sprintf(fname,"flux_med.fits");
     cpl_vector_save(vflux_med,fname,CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);
     mflux=cpl_vector_get_data(vflux_med);

    */

    /* here we perform the actual fit: we pass now the median points via:
     * vec_wave,pvec_flux,nsamples params
     *
     * To fit each point in each fit window region we would instead pass
     * pwav,pflux,nsel params
     *
     */

    double* pvec_wave=NULL;
    double* pvec_flux=NULL;
    pvec_wave=cpl_vector_get_data(vec_wave);
    pvec_flux=cpl_vector_get_data(vec_flux);
    //cpl_vector_save(vec_wave,"papero.fits",CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);
    //xsh_msg("Max wave=%g",cpl_vector_get_max(vec_wave));
   
    //sfit = xsh_bspline_fit_data2(pvec_wave, pvec_flux, nsamples, phigh,instrument, 1);
    

    check(sfit = xsh_bspline_interpolate_data_at_pos(pvec_wave, pvec_flux, nsamples+2,
						     pvec_wave,nsamples+2));
  

    //xsh_msg("Max wave=%g",cpl_vector_get_max(vec_wave));

    //xsh_free_vector(&vflux_med);
    //cpl_vector_unwrap(vflux);
    int i=0;
    /* this instruction is to fill the fit column in table tab_stack with result of the fit
     for(i=0;i<nsel;i++) {
     pfit[i]=sfit[i];
     xsh_msg("sfit=%g",sfit[i]);
     }


    sprintf(fname,"ratio_fit.fits");
    check(xsh_table_save(tab_stack,NULL,NULL,fname,ext));
     */
   xsh_free_table(&tab_stack);

    cpl_table* tab_med = cpl_table_new(nsamples+2);

    check(cpl_table_wrap_double(tab_med,pvec_wave,"wave"));
    check(cpl_table_wrap_double(tab_med,pvec_flux,"ratio"));
    check(cpl_table_wrap_double(tab_med,sfit,"fit"));
    sprintf(fname,"ratio_med.fits");
    //check(xsh_table_save(tab_med,NULL,NULL,fname,ext));
    //xsh_msg("Max wave=%g",cpl_vector_get_max(vec_wave));
    /*
    check(
        tab_corr=xsh_table_downsample_table(tab_med,"wave","fit",table_rs,"wave","fit"));
    */
    /*
    int nrow=cpl_table_get_nrow(tab_corr);
    */
    double* wave=NULL;
  
    tab_corr=cpl_table_duplicate(table_rs);
    int nrow=cpl_table_get_nrow(tab_corr);
    cpl_table_new_column(tab_corr,"fit",CPL_TYPE_DOUBLE);
    cpl_table_fill_column_window_double(tab_corr, "fit", 0, nrow, 0);

    check(pfit=cpl_table_get_data_double(tab_corr,"fit"));
    check(wave=cpl_table_get_data_double(tab_corr,"wave"));
    //xsh_msg("Max wave=%g",cpl_vector_get_max(vec_wave));
    cpl_free(sfit);
    check(sfit = xsh_bspline_interpolate_data_at_pos(pvec_wave, pvec_flux, 
						     nsamples+2,wave,nrow));
    for (i = 0; i < nrow; i ++) {
      pfit[i]=sfit[i];
    }
    //check(cpl_table_wrap_double(tab_corr,sfit,"fit"));
 
    //check(xsh_table_save(tab_corr,NULL,NULL,"ratio_fit_continuum.fits",ext));
  
   //check(xsh_table_save(table_rs,NULL,NULL,"table_rs_check.fits",ext));

    /*
     9) divide telluric corrected spectrum by continuum fit
     */
    check(cpl_table_duplicate_column(tab_corr, "correction", table_rs, "ratio"));
    check(cpl_table_divide_columns(tab_corr, "correction", "fit"));
    //sprintf(fname,"spectrum_obs_corrected.fits");


    cpl_table_unwrap(tab_med,"wave");
    cpl_table_unwrap(tab_med,"ratio");
    cpl_table_unwrap(tab_med,"fit");
    xsh_free_table(&tab_med);
    xsh_free_table(&table_rs);
    xsh_free_vector(&vec_wave);
    xsh_free_vector(&vec_flux);

    cpl_free(sfit);
  

    /*
     10) find  a way to identify the best correction from the residuals
     (I tried some statistical tests, but am not really convinced)
     */
    check(xsh_evaluate_tell_model(tab_corr,instrument,ext,&mean,&rms));
    qc_head=cpl_propertylist_new();
    cpl_propertylist_append_double(qc_head,XSH_QC_TELLCORR_RATAVG,mean);
    cpl_propertylist_append_double(qc_head,XSH_QC_TELLCORR_RATRMS,rms);
    //cpl_propertylist_dump(qc_head,stdout);
    //check(xsh_table_save(tab_corr,NULL,qc_head,fname,ext));
    xsh_msg("ext=%d mean=%g rms=%g", ext,mean, rms);
    xsh_free_propertylist(&qc_head);
    xsh_free_table(&ext_tab);
    xsh_free_table(&tab_corr);
    pmean[ext]=mean;
    prms[ext]=rms;
    pext[ext]=ext;
    xsh_free_table(&table_rs_div_mc);
  }
  double model_mean=0;
  double model_rms=0;
  int status=0;
  check(model_mean=cpl_table_get_column_min(tab_res,"mean"));
  //check(xsh_table_save(tab_res,NULL,NULL,"tab_res.fits",0));
  //xsh_msg("optimal model mean %g",model_mean);
  cpl_table_get_column_minpos(tab_res,"mean",model_idx);
  check(model_rms=cpl_table_get_double(tab_res,"rms",*model_idx,&status));
  xsh_msg("optimal model: mean=%g rms=%g",model_mean,model_rms);
  xsh_msg("optimal model id %d mean %g rms %g",
	  (int)(*model_idx),model_mean,model_rms);

  /* load finally optimal solution */
  //xsh_msg("fname=%s",fname);
  sprintf(fname,"spectrum_observed_divided_by_model_convolved.fits");
  xsh_add_temporary_file(fname);
  ext=(int)(*model_idx);
  xsh_msg("fname=%s ext=%d",fname,ext);
  check(table_rs=cpl_table_load(fname,ext+1,0));
  //xsh_msg("table_rs nrow=%d",(int)cpl_table_get_nrow(table_rs));
  qc_head=cpl_propertylist_new();
  cpl_propertylist_append_double(qc_head,XSH_QC_TELLCORR_RATAVG,mean);
  cpl_propertylist_append_double(qc_head,XSH_QC_TELLCORR_RATRMS,rms);
  cpl_propertylist_append_int(qc_head,XSH_QC_TELLCORR_OPTEXTID,ext+1);
  //cpl_propertylist_dump(qc_head,stdout);
  //sprintf(fname,"model_optimal.fits");
  check(xsh_table_save(table_rs,NULL,qc_head,fname,0));
  //cpl_table_dump(table_rs,1,2,stdout);

  //xsh_msg("table_rm nrow=%d",(int)cpl_table_get_nrow(table_rm));
  //cpl_table_dump(table_rm,1,2,stdout);
  //xsh_msg("ok1");

  //check(xsh_table_save(table_rs,NULL,NULL,"check.fits",0));
  check(tab_corr=xsh_table_downsample_table(table_rs,"wave","ratio",table_rm,"wavelength","ratio"));
  //xsh_msg("tab_cor nrow=%d",(int)cpl_table_get_nrow(tab_corr));
  //cpl_table_dump(tab_corr,1,2,stdout);
  //check(xsh_table_save(tab_corr,NULL,NULL,"pippo.fits",0));

 cleanup:
  xsh_free_propertylist(&qc_head);
  xsh_free_table(&table_rs);
  xsh_free_table(&tab_res);
  xsh_free_table(&table_s);
  xsh_free_table(&table_rm);

  return tab_corr;

}


/**@}*/
