/*                                                                           *
 *   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-03-20 12:25:43 $
 * $Revision: 1.46 $
 * $Name: not supported by cvs2svn $
 */

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



/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_data_resid_tab Residual tab
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/

/**@{*/


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

#include <xsh_data_resid_tab.h>
#include <xsh_utils.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <cpl.h>
#include <xsh_drl.h>
#include <xsh_utils_table.h>
#include <xsh_data_wavesol.h>

/*----------------------------------------------------------------------------
                                 Function implementation
 ----------------------------------------------------------------------------*/


/**
  @brief  Create a residual tab structure.
  @param[in] size The numbers of lines in residual tab
  @param[in] lambda The wavelength of the size lines 
  @param[in] order order array
  @param[in] slit slit array
  @param[in] slit_index slit_index array (for each pinhole)
  @param[in] thpre_x THE X position
  @param[in] thpre_y THE Y position
  @param[in] corr_x corrective x factor
  @param[in] corr_y corrective y factor
  @param[in] gaussian_fit_x x position after Gaussian fit
  @param[in] gaussian_fit_y y position after Gaussian fit
  @param[in] gaussian_sigma_x x sigma after Gaussian fit
  @param[in] gaussian_sigma_y y sigma after Gaussian fit
  @param[in] wavesol wavelength solution
  @param[in] wavesol_type wavelength solution type

  @return 
    The resid_tab allocated structure
*/
/*---------------------------------------------------------------------------*/
xsh_resid_tab* 
xsh_resid_tab_create(int size, double *lambda, double *order,
                     double *slit, double* sn, int *slit_index,
                     double *thpre_x, double *thpre_y, 
                     double*corr_x, double* corr_y,
                     double *gaussian_norm, 
                     double *gaussian_fit_x, double *gaussian_fit_y,
                     double *gaussian_sigma_x, double *gaussian_sigma_y, 
                     double *gaussian_fwhm_x, double *gaussian_fwhm_y, int* flag,
                     xsh_wavesol *wavesol, int wavesol_type)
{
  enum wavesol_type type = XSH_WAVESOL_GUESS;
  xsh_resid_tab* result = NULL;
  int i;

  /* verify input */
  XSH_ASSURE_NOT_ILLEGAL( size>=0);
  XSH_ASSURE_NOT_NULL( lambda);
  XSH_ASSURE_NOT_NULL( order);
  XSH_ASSURE_NOT_NULL( slit);
  XSH_ASSURE_NOT_NULL( slit_index);
  XSH_ASSURE_NOT_NULL( thpre_x);
  XSH_ASSURE_NOT_NULL( thpre_y);

  XSH_ASSURE_NOT_NULL( gaussian_fit_x);
  XSH_ASSURE_NOT_NULL( gaussian_fit_y);
  XSH_ASSURE_NOT_NULL( gaussian_sigma_x);
  XSH_ASSURE_NOT_NULL( gaussian_sigma_y);
  XSH_ASSURE_NOT_NULL( gaussian_fwhm_x);
  XSH_ASSURE_NOT_NULL( gaussian_fwhm_y);
  XSH_ASSURE_NOT_NULL( gaussian_norm);

  XSH_CALLOC( result, xsh_resid_tab, 1);

  XSH_CALLOC( result->lambda, double, size);
  XSH_CALLOC( result->order, double, size);
  XSH_CALLOC( result->slit, double, size);
  XSH_CALLOC( result->sn, double, size);
  XSH_CALLOC( result->slit_index, int, size);
  XSH_CALLOC( result->thpre_x, double, size);
  XSH_CALLOC( result->thpre_y, double, size);
  XSH_CALLOC( result->thcor_x, double, size);
  XSH_CALLOC( result->thcor_y, double, size);
  XSH_CALLOC( result->xgauss, double, size);
  XSH_CALLOC( result->ygauss, double, size);
  XSH_CALLOC( result->sig_xgauss, double, size);
  XSH_CALLOC( result->sig_ygauss, double, size);
  XSH_CALLOC( result->norm_gauss, double, size);
  XSH_CALLOC( result->fwhm_xgauss, double, size);
  XSH_CALLOC( result->fwhm_ygauss, double, size);

  XSH_CALLOC( result->xpoly, double, size);
  XSH_CALLOC( result->ypoly, double, size);
  if(flag) {
    XSH_CALLOC( result->flag, int, size);
  }
  check (result->header = cpl_propertylist_new());

  result->size = size;
  result->solution_type = wavesol_type;

  if (wavesol != NULL) {
    check (type = xsh_wavesol_get_type( wavesol));
  }
  else{
    type = XSH_WAVESOL_UNDEFINED;
  }

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

    result->lambda[i] = lambda[i];
    result->order[i] = order[i];
    result->slit[i] = slit[i];
    if(sn != NULL) {
       result->sn[i] = sn[i];
    } else {
       result->sn[i]=0;
    }
    result->slit_index[i] = slit_index[i];
    result->thpre_x[i] = thpre_x[i];
    result->thpre_y[i] = thpre_y[i];
    result->thcor_x[i] = thpre_x[i]+corr_x[i];
    result->thcor_y[i] = thpre_y[i]+corr_y[i];
    result->xgauss[i] = gaussian_fit_x[i];
    result->ygauss[i] = gaussian_fit_y[i];
    result->sig_xgauss[i] = gaussian_sigma_x[i];
    result->sig_ygauss[i] = gaussian_sigma_y[i];
    result->fwhm_xgauss[i] = gaussian_fwhm_x[i];
    result->fwhm_ygauss[i] = gaussian_fwhm_y[i];
    result->norm_gauss[i] = gaussian_norm[i];
    if(flag) {
      result->flag[i] = flag[i];
    }
    if ( type == XSH_WAVESOL_2D){
      double polx, poly;
    
      check( polx = xsh_wavesol_eval_polx( wavesol, lambda[i], 
        order[i], slit[i]));
      check( poly = xsh_wavesol_eval_poly( wavesol, lambda[i],
        order[i], slit[i]));
      result->xpoly[i] = polx;
      result->ypoly[i] = poly;
    }
    else if ( type == XSH_WAVESOL_GUESS){
      check( result->xpoly[i] = xsh_wavesol_eval_polx( wavesol, 
        lambda[i], order[i], slit[i])+result->thcor_x[i]);
      check( result->ypoly[i] = xsh_wavesol_eval_poly( wavesol, 
        lambda[i], order[i], slit[i])+result->thcor_y[i]);
    }
    else{
      result->xpoly[i] = 0.0;
      result->ypoly[i] = 0.0;
    }

  }

/*  check (xsh_tools_get_statistics( result->diff_poly_fit_x, size, 
    &result->median_diff_poly_fit_x, &result->mean_diff_poly_fit_x, 
    &result->stdev_diff_poly_fit_x));
  check (xsh_tools_get_statistics( result->diff_poly_fit_y, size, 
    &result->median_diff_poly_fit_y, &result->mean_diff_poly_fit_y, 
    &result->stdev_diff_poly_fit_y));
*/
  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_resid_tab_free( &result);
    }
    return result;  
}



xsh_resid_tab*
xsh_resid_tab_create_not_flagged(int size, double *lambda, double *order,
    double *slit, double* sn, int *slit_index, double *thpre_x, double *thpre_y,
    double*corr_x, double* corr_y, double *gaussian_norm,
    double *gaussian_fit_x, double *gaussian_fit_y, double *gaussian_sigma_x,
    double *gaussian_sigma_y, double *gaussian_fwhm_x, double *gaussian_fwhm_y,
    int* flag, xsh_wavesol *wavesol, int wavesol_type) {
  enum wavesol_type type = XSH_WAVESOL_GUESS;
  xsh_resid_tab* result = NULL;
  int i;
  int ngood = 0;


  /* As several lines has been flagged we need to count the good pixels */
  for (i = 0; i < size; i++) {

    if (flag != NULL && flag[i] == 0) {
      ngood++;
    }

  }

  /* verify input */
  XSH_ASSURE_NOT_ILLEGAL( size>=0);
  XSH_ASSURE_NOT_NULL( lambda);
  XSH_ASSURE_NOT_NULL( order);
  XSH_ASSURE_NOT_NULL( slit);
  XSH_ASSURE_NOT_NULL( slit_index);
  XSH_ASSURE_NOT_NULL( thpre_x);
  XSH_ASSURE_NOT_NULL( thpre_y);

  XSH_ASSURE_NOT_NULL( gaussian_fit_x);
  XSH_ASSURE_NOT_NULL( gaussian_fit_y);
  XSH_ASSURE_NOT_NULL( gaussian_sigma_x);
  XSH_ASSURE_NOT_NULL( gaussian_sigma_y);
  XSH_ASSURE_NOT_NULL( gaussian_fwhm_x);
  XSH_ASSURE_NOT_NULL( gaussian_fwhm_y);
  XSH_ASSURE_NOT_NULL( gaussian_norm);

  XSH_CALLOC( result, xsh_resid_tab, 1);

  /* the result table contains only values corresponding to good pixels */
  XSH_CALLOC  ( result->lambda, double, ngood);
  XSH_CALLOC( result->order, double, ngood);
  XSH_CALLOC  ( result->slit, double, ngood);
  XSH_CALLOC( result->sn, double, ngood);
  XSH_CALLOC  ( result->slit_index, int, ngood);
  XSH_CALLOC( result->thpre_x, double,ngood);
  XSH_CALLOC  ( result->thpre_y, double, ngood);
  XSH_CALLOC( result->thcor_x, double,ngood);
  XSH_CALLOC  ( result->thcor_y, double, ngood);
  XSH_CALLOC( result->xgauss, double,  ngood);
  XSH_CALLOC  ( result->ygauss, double, ngood);
  XSH_CALLOC( result->sig_xgauss, double, ngood);
  XSH_CALLOC  ( result->sig_ygauss, double, ngood);
  XSH_CALLOC( result->norm_gauss,double, ngood);
  XSH_CALLOC  ( result->fwhm_xgauss, double, ngood);
  XSH_CALLOC( result->fwhm_ygauss,double, ngood);
  XSH_CALLOC  ( result->xpoly, double, ngood);
  XSH_CALLOC( result->ypoly, double, ngood);

  if(  flag) {
    XSH_CALLOC( result->flag, int, ngood);
  }
  check(result->header = cpl_propertylist_new());

  result->size = ngood;
  result->solution_type = wavesol_type;

  if (wavesol != NULL) {
    check(type = xsh_wavesol_get_type( wavesol));
  } else {
    type = XSH_WAVESOL_UNDEFINED;
  }

  /* we need an additional variable because result has a different size than
     input arrays */

  int k = 0;
  for (i = 0; i < size; i++) {
    if (flag != NULL && flag[i] == 0) {
      result->lambda[k] = lambda[i];
      result->order[k] = order[i];
      result->slit[k] = slit[i];
      if (sn != NULL) {
        result->sn[k] = sn[i];
      } else {
        result->sn[k] = 0;
      }
      result->slit_index[k] = slit_index[i];
      result->thpre_x[k] = thpre_x[i];
      result->thpre_y[k] = thpre_y[i];
      result->thcor_x[k] = thpre_x[i] + corr_x[i];
      result->thcor_y[k] = thpre_y[i] + corr_y[i];
      result->xgauss[k] = gaussian_fit_x[i];
      result->ygauss[k] = gaussian_fit_y[i];
      result->sig_xgauss[k] = gaussian_sigma_x[i];
      result->sig_ygauss[k] = gaussian_sigma_y[i];
      result->fwhm_xgauss[k] = gaussian_fwhm_x[i];
      result->fwhm_ygauss[k] = gaussian_fwhm_y[i];
      result->norm_gauss[k] = gaussian_norm[i];
      if (flag) {
        result->flag[k] = flag[i];
      }
      if (type == XSH_WAVESOL_2D) {
        double polx, poly;

        check(
            polx = xsh_wavesol_eval_polx( wavesol, lambda[i], order[i], slit[i]));
        check(
            poly = xsh_wavesol_eval_poly( wavesol, lambda[i], order[i], slit[i]));
        result->xpoly[k] = polx;
        result->ypoly[k] = poly;
      } else if (type == XSH_WAVESOL_GUESS) {
        check(
            result->xpoly[k] = xsh_wavesol_eval_polx( wavesol, lambda[i], order[i], slit[i])+result->thcor_x[k]);
        check(
            result->ypoly[k] = xsh_wavesol_eval_poly( wavesol, lambda[i], order[i], slit[i])+result->thcor_y[k]);
      } else {
        result->xpoly[k] = 0.0;
        result->ypoly[k] = 0.0;
      }
      k++;
    }
  }

  cleanup: if (cpl_error_get_code() != CPL_ERROR_NONE) {
    xsh_resid_tab_free(&result);
  }
  return result;
}

cpl_frame* 
xsh_resid_tab_erase_flagged( cpl_frame* resid,const char* name)
{

  cpl_frame* result = NULL;
  cpl_table* tab = NULL;
  cpl_table* ext = NULL;
  const char* fname = NULL;
  
  cpl_propertylist* header=NULL;
  //int bad=0;
 
  result=cpl_frame_duplicate(resid);
  check( fname = cpl_frame_get_filename( resid));
  tab=cpl_table_load(fname,1,0);
  header=cpl_propertylist_load(fname,0);
  cpl_table_and_selected_int(tab,XSH_RESID_TAB_TABLE_COLNAME_FLAG,
				 CPL_GREATER_THAN,0);
  cpl_table_erase_selected(tab);
  cpl_table_save(tab,header,NULL,name,CPL_IO_DEFAULT);
  cpl_frame_set_filename(result,name);

 cleanup:
  xsh_free_table(&ext);
  xsh_free_table(&tab);
  xsh_free_propertylist(&header);

  return result;  
}
/*---------------------------------------------------------------------------*/
/**
 *  @brief 
 *    Load a residual tab from a frame
 *
 *  @param[in] resid_tab_frame
 *    The residual tab to load
 *  @return
 *    a new allocated resid_tab structure
 */
/*---------------------------------------------------------------------------*/

xsh_resid_tab* xsh_resid_tab_load( cpl_frame* resid_tab_frame)
{
  xsh_resid_tab* result = NULL;
  cpl_table* table = NULL;
  const char* tablename = NULL;
  int i = 0;
  int size = 0;
  double *lambda = NULL;
  double *order = NULL;
  double *slit = NULL;
  int *slit_index = NULL;
  double *thx = NULL;
  double *thy = NULL;
  double *thcorx = NULL;
  double *thcory = NULL;
  double *xgauss = NULL;
  double *ygauss = NULL;
  double *sig_xgauss = NULL;
  double *sig_ygauss = NULL;
  double *fwhm_xgauss = NULL;
  double *fwhm_ygauss = NULL;
  double *norm_gauss = NULL;
  double *xpoly = NULL;
  double *ypoly = NULL;
  double *thanneal_x = NULL;
  double *thanneal_y = NULL;
  int* flag=NULL;
  int solution_type = XSH_DETECT_ARCLINES_TYPE_POLY;
  const char* wavesol_type = NULL;
  /* verify input */
  XSH_ASSURE_NOT_NULL( resid_tab_frame);

  /* get table */
  check( tablename = cpl_frame_get_filename( resid_tab_frame));

  XSH_TABLE_LOAD( table, tablename);

  check( size = cpl_table_get_nrow(table));

  XSH_CALLOC( result, xsh_resid_tab, 1);
  check (result->header = cpl_propertylist_load( tablename, 0));
  wavesol_type = xsh_pfits_get_wavesoltype( result->header);
  if (cpl_error_get_code() == CPL_ERROR_NONE){
    if ( strcmp(wavesol_type, XSH_WAVESOLTYPE_MODEL)==0){
      solution_type = XSH_DETECT_ARCLINES_TYPE_MODEL;
    } 
  }
  else{
    xsh_msg("Can't read keyword %s", XSH_WAVESOLTYPE); 
    cpl_error_reset();
  }
  result->size = size;
  result->solution_type = solution_type;

  XSH_CALLOC( lambda, double, size);
  XSH_CALLOC( order, double, size);
  XSH_CALLOC( slit, double, size);
  XSH_CALLOC( slit_index, int, size);
  XSH_CALLOC( thx, double, size);
  XSH_CALLOC( thy, double, size);
  XSH_CALLOC( thcorx, double, size);
  XSH_CALLOC( thcory, double, size);
  XSH_CALLOC( xgauss, double, size);
  XSH_CALLOC( ygauss, double, size);
  XSH_CALLOC( sig_xgauss, double, size);
  XSH_CALLOC( sig_ygauss, double, size);
  XSH_CALLOC( fwhm_xgauss, double, size);
  XSH_CALLOC( fwhm_ygauss, double, size);
  XSH_CALLOC( norm_gauss, double, size);
  if (solution_type == XSH_DETECT_ARCLINES_TYPE_MODEL){
    XSH_CALLOC( thanneal_x, double, size);
    XSH_CALLOC( thanneal_y, double, size);
  }
  else{
    XSH_CALLOC( xpoly, double, size);
    XSH_CALLOC( ypoly, double, size);
  }
  XSH_CALLOC( flag, int, size);

  for(i=0; i<size;i++){
    int orderv, slit_indexv;
    double lambdav, slitv, thxv, thyv;
    double thcorxv=0, thcoryv=0;
    double xgaussv, ygaussv, xpolyv, ypolyv;
    double sig_xgaussv, sig_ygaussv;
    double fwhm_xgaussv, fwhm_ygaussv;
    double norm_gaussv;
    int flagv=0;

    check( xsh_get_table_value( table, 
      XSH_RESID_TAB_TABLE_COLNAME_WAVELENGTH, 
      CPL_TYPE_DOUBLE, i, &lambdav));

    check( xsh_get_table_value( table, 
      XSH_RESID_TAB_TABLE_COLNAME_ORDER, 
      CPL_TYPE_INT, i, &orderv));

    check( xsh_get_table_value( table, 
      XSH_RESID_TAB_TABLE_COLNAME_SLITPOSITION,
      CPL_TYPE_DOUBLE, i, &slitv));

    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_SLITINDEX,
      CPL_TYPE_INT, i, &slit_indexv));

    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_XTHPRE,
      CPL_TYPE_DOUBLE, i, &thxv));

    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_YTHPRE,
      CPL_TYPE_DOUBLE, i, &thyv));

    /* AMO: Decided with reflex review to remove those columns
    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_XTHCOR,
      CPL_TYPE_DOUBLE, i, &thcorxv));
    
    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_YTHCOR,
      CPL_TYPE_DOUBLE, i, &thcoryv));
    */
    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_XGAUSS,
      CPL_TYPE_DOUBLE, i, &xgaussv));

    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_YGAUSS,
      CPL_TYPE_DOUBLE, i, &ygaussv));

    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_SIGMAXGAUSS,
      CPL_TYPE_DOUBLE, i, &sig_xgaussv));

    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_SIGMAYGAUSS,
      CPL_TYPE_DOUBLE, i, &sig_ygaussv));


    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_FWHMXGAUSS,
      CPL_TYPE_DOUBLE, i, &fwhm_xgaussv));

    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_FWHMYGAUSS,
      CPL_TYPE_DOUBLE, i, &fwhm_ygaussv));

    check( xsh_get_table_value( table,
      XSH_RESID_TAB_TABLE_COLNAME_NORMGAUSS,
      CPL_TYPE_DOUBLE, i, &norm_gaussv));

    if (solution_type == XSH_DETECT_ARCLINES_TYPE_MODEL){
      check( xsh_get_table_value( table,
        XSH_RESID_TAB_TABLE_COLNAME_XTHANNEAL,
        CPL_TYPE_DOUBLE, i, &xpolyv));
      thanneal_x[i] = xpolyv;
      check( xsh_get_table_value( table,
        XSH_RESID_TAB_TABLE_COLNAME_YTHANNEAL,
        CPL_TYPE_DOUBLE, i, &ypolyv));
      thanneal_y[i] = ypolyv;
    }
    else{
      check( xsh_get_table_value( table,
        XSH_RESID_TAB_TABLE_COLNAME_XPOLY,
        CPL_TYPE_DOUBLE, i, &xpolyv));
      xpoly[i] = xpolyv;
      check( xsh_get_table_value( table,
        XSH_RESID_TAB_TABLE_COLNAME_YPOLY,
        CPL_TYPE_DOUBLE, i, &ypolyv));
      ypoly[i] = ypolyv;
    }
    if (cpl_table_has_column(table,XSH_RESID_TAB_TABLE_COLNAME_FLAG)) {
      check( xsh_get_table_value( table,
            XSH_RESID_TAB_TABLE_COLNAME_SLITINDEX,
            CPL_TYPE_INT, i, &flagv));
    }
    lambda[i] = lambdav;
    order[i] = (double) orderv;
    slit[i] = slitv;
    slit_index[i] = slit_indexv;
    thx[i] = thxv;
    thy[i] = thyv;
    thcorx[i] =thcorxv;
    thcory[i] = thcoryv;
    xgauss[i] = xgaussv;
    ygauss[i] = ygaussv;
    sig_xgauss[i] = sig_xgaussv;
    sig_ygauss[i] = sig_ygaussv;
    fwhm_xgauss[i] = fwhm_xgaussv;
    fwhm_ygauss[i] = fwhm_ygaussv;
    norm_gauss[i] = norm_gaussv;
    flag[i]=flagv;
  }

  result->lambda = lambda;
  result->order = order;
  result->slit = slit;
  result->slit_index = slit_index;
  result->thpre_x = thx;
  result->thpre_y = thy;
  result->thcor_x = thcorx;
  result->thcor_y = thcory;
  result->xgauss = xgauss;
  result->ygauss = ygauss;
  result->sig_xgauss = sig_xgauss;
  result->sig_ygauss = sig_ygauss;
  result->fwhm_xgauss = fwhm_xgauss;
  result->fwhm_ygauss = fwhm_ygauss;
  result->norm_gauss = norm_gauss;
  if ( solution_type == XSH_DETECT_ARCLINES_TYPE_MODEL){
    result->thanneal_x = thanneal_x;
    result->thanneal_y = thanneal_y;
  }
  else{
    result->xpoly = xpoly;
    result->ypoly = ypoly;
  }
  result->flag = flag;

  cleanup:
    if (cpl_error_get_code () != CPL_ERROR_NONE) {
      xsh_error_msg("can't load frame %s",
      cpl_frame_get_filename(resid_tab_frame));
      xsh_resid_tab_free(&result);
      return NULL;
    } else {
      XSH_TABLE_FREE( table);
      return result;
    }
}

/*---------------------------------------------------------------------------*/
/**
  @brief 
    Free memory associated to a resid_tab

  @param[in,out] resid 
    The resid_tab to free
*/
/*---------------------------------------------------------------------------*/
void xsh_resid_tab_free( xsh_resid_tab** resid) {
  if ( resid && *resid) {
    XSH_FREE(  (*resid)->lambda);
    XSH_FREE(  (*resid)->order);
    XSH_FREE(  (*resid)->slit);
    XSH_FREE(  (*resid)->slit_index);
    XSH_FREE(  (*resid)->thpre_x);
    XSH_FREE(  (*resid)->thpre_y);
    XSH_FREE(  (*resid)->thcor_x);
    XSH_FREE(  (*resid)->thcor_y);
    XSH_FREE(  (*resid)->xgauss);
    XSH_FREE(  (*resid)->ygauss);
    XSH_FREE(  (*resid)->sig_xgauss);
    XSH_FREE(  (*resid)->sig_ygauss);
    XSH_FREE(  (*resid)->fwhm_xgauss);
    XSH_FREE(  (*resid)->fwhm_ygauss);
    XSH_FREE(  (*resid)->norm_gauss);
    XSH_FREE(  (*resid)->xpoly);
    XSH_FREE(  (*resid)->ypoly);
    XSH_FREE(  (*resid)->thanneal_x);
    XSH_FREE(  (*resid)->thanneal_y);
    XSH_FREE(  (*resid)->flag);
    if( (*resid)->sn) XSH_FREE(  (*resid)->sn) ;
    xsh_free_propertylist( &(*resid)->header);
    cpl_free(*resid);
    *resid = NULL;
  }

}


/*---------------------------------------------------------------------------*/
/**
  @brief 
    Log the residual tab in a ASCII file                                                                                                                                                              
  @param[in] resid
    The resid_tab to log in
  @param[in] filename
    The name of the log file
*/
/*---------------------------------------------------------------------------*/
void xsh_resid_tab_log( xsh_resid_tab* resid, const char* filename)
{

  FILE* logfile = NULL;
  int i = 0;

  XSH_ASSURE_NOT_NULL( resid);
  XSH_ASSURE_NOT_NULL( filename);

  logfile = fopen(filename,"w");

  if ( resid->solution_type == XSH_DETECT_ARCLINES_TYPE_POLY) {
    fprintf( logfile, 
      "# lambda order slit thx, thy, gaussx, gaussy, xpoly, ypoly");
    for ( i=0; i< resid->size; i++){
      double lambda, order, slit;

      lambda = resid->lambda[i];
      order = resid->order[i];
      slit = resid->slit[i];
      fprintf( logfile,
        "%.8lg %.8lg %.8lg %.8lg %.8lg %.8lg %.8lg %.8lg %.8lg\n",
        lambda, order, slit, resid->thpre_x[i], resid->thpre_y[i], 
        resid->xgauss[i], resid->ygauss[i], 
        resid->xpoly[i], resid->ypoly[i]
      );
    }
  }
  else{
    fprintf( logfile,
      "# lambda order slit thx, thy, gaussx, gaussy, thanneal_x, thanneal_y");
    for ( i=0; i< resid->size; i++){
      double lambda, order, slit;

      lambda = resid->lambda[i];
      order = resid->order[i];
      slit = resid->slit[i];
      fprintf( logfile,
        "%.8lg %.8lg %.8lg %.8lg %.8lg %.8lg %.8lg %.8lg %.8lg\n",
        lambda, order, slit, resid->thpre_x[i], resid->thpre_y[i],
        resid->xgauss[i], resid->ygauss[i],
        resid->thanneal_x[i], resid->thanneal_y[i]
      );
    }
  }
  fclose( logfile);
  cleanup:
    return;
}

/*---------------------------------------------------------------------------*/
/**
  @brief Save a residual tab to a frame

  @param resid
    The resid_tab structure to save
  @param[in] filename
    The name of the save file on disk
  @return 
    A newly allocated residual tab frame

*/
/*---------------------------------------------------------------------------*/
cpl_frame* xsh_resid_tab_save( xsh_resid_tab* resid, const char* filename,
			       xsh_instrument* instr,const char* tag)
{
  cpl_frame *result = NULL ;
  cpl_table *table = NULL;
  cpl_propertylist *header = NULL;
  int i = 0;

  double residx_min=0;
  double residx_max=0;
  double residx_rms=0;

  double residy_min=0;
  double residy_max=0;
  double residy_rms=0;

  XSH_ASSURE_NOT_NULL( resid);
  XSH_ASSURE_NOT_NULL( filename);
  XSH_ASSURE_NOT_NULL( instr);

  /* create a table */
  check( table = cpl_table_new( XSH_RESID_TAB_TABLE_NB_COL));
  header = resid->header;
  /* copy some QC parameters */
  if (resid->solution_type ==  XSH_DETECT_ARCLINES_TYPE_POLY){
    check( xsh_pfits_set_qc( header, &(resid->mean_diff_poly_fit_x), 
			     XSH_QC_FMTCHK_POLY_DIFFXAVG, instr));
    check( xsh_pfits_set_qc( header, &(resid->median_diff_poly_fit_x), 
			     XSH_QC_FMTCHK_POLY_DIFFXMED, instr));
    check( xsh_pfits_set_qc( header, &(resid->stdev_diff_poly_fit_x), 
			     XSH_QC_FMTCHK_POLY_DIFFXSTD, instr));
    check( xsh_pfits_set_qc( header, &(resid->mean_diff_poly_fit_y), 
			     XSH_QC_FMTCHK_POLY_DIFFYAVG, instr));
    check( xsh_pfits_set_qc( header, &(resid->median_diff_poly_fit_y), 
			     XSH_QC_FMTCHK_POLY_DIFFYMED, instr));
    check( xsh_pfits_set_qc( header, &(resid->stdev_diff_poly_fit_y), 
			     XSH_QC_FMTCHK_POLY_DIFFYSTD, instr));  
  }

  /* create columns */ 
  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_WAVELENGTH,
    XSH_RESID_TAB_TABLE_UNIT_WAVELENGTH, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_ORDER,
    XSH_RESID_TAB_TABLE_UNIT_ORDER, CPL_TYPE_INT);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_SLITPOSITION,
    XSH_RESID_TAB_TABLE_UNIT_SLITPOSITION, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_SLITINDEX,
    XSH_RESID_TAB_TABLE_UNIT_SLITINDEX, CPL_TYPE_INT);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_XTHPRE,
    XSH_RESID_TAB_TABLE_UNIT_XTHPRE, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_YTHPRE,
    XSH_RESID_TAB_TABLE_UNIT_YTHPRE, CPL_TYPE_DOUBLE);

  /* AMO: decided with Reflex review to remove those columns
  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_XTHCOR,
    XSH_RESID_TAB_TABLE_UNIT_XTHCOR, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_YTHCOR,
    XSH_RESID_TAB_TABLE_UNIT_YTHCOR, CPL_TYPE_DOUBLE);
  */

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_XGAUSS,
    XSH_RESID_TAB_TABLE_UNIT_XGAUSS, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_YGAUSS,
    XSH_RESID_TAB_TABLE_UNIT_YGAUSS, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_SIGMAXGAUSS,
    XSH_RESID_TAB_TABLE_UNIT_SIGMAXGAUSS, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_SIGMAYGAUSS,
    XSH_RESID_TAB_TABLE_UNIT_SIGMAYGAUSS, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_FWHMXGAUSS,
    XSH_RESID_TAB_TABLE_UNIT_SIGMAXGAUSS, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_FWHMYGAUSS,
    XSH_RESID_TAB_TABLE_UNIT_SIGMAYGAUSS, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_NORMGAUSS,
    XSH_RESID_TAB_TABLE_UNIT_NORMGAUSS, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_SN,
      XSH_RESID_TAB_TABLE_UNIT_SN, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_XPOLY,
    XSH_RESID_TAB_TABLE_UNIT_XPOLY, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_YPOLY,
    XSH_RESID_TAB_TABLE_UNIT_YPOLY, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_XTHANNEAL,
    XSH_RESID_TAB_TABLE_UNIT_XTHANNEAL, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_YTHANNEAL,
    XSH_RESID_TAB_TABLE_UNIT_YTHANNEAL, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_RESIDXPOLY,
    XSH_RESID_TAB_TABLE_UNIT_RESIDXPOLY, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_RESIDYPOLY,
    XSH_RESID_TAB_TABLE_UNIT_RESIDYPOLY, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_RESIDXMODEL,
    XSH_RESID_TAB_TABLE_UNIT_RESIDXMODEL, CPL_TYPE_DOUBLE);

  XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_RESIDYMODEL,
    XSH_RESID_TAB_TABLE_UNIT_RESIDYMODEL, CPL_TYPE_DOUBLE);

   if(resid->flag) {

     XSH_TABLE_NEW_COL(table, XSH_RESID_TAB_TABLE_COLNAME_FLAG,
        XSH_RESID_TAB_TABLE_UNIT_FLAG, CPL_TYPE_INT);

   }
  check(cpl_table_set_size( table, resid->size));

  /* insert data */
  for (i=0; i<resid->size; i++) {
    double lambda, order, slit,sn;
    int slit_index;
    double thpre_x, thpre_y;
    //double thcor_x, thcor_y;
    double xgauss, ygauss;
    double xpoly, ypoly;
    double sig_xgauss, sig_ygauss;
    double fwhm_xgauss, fwhm_ygauss;
    double norm_gauss;
    int flag=0;
    lambda = resid->lambda[i];
    order = resid->order[i];
    slit = resid->slit[i];
    sn = resid->sn[i];

    slit_index = resid->slit_index[i];
    thpre_x = resid->thpre_x[i];
    thpre_y = resid->thpre_y[i];
    //thcor_x = resid->thcor_x[i];
    //thcor_y = resid->thcor_y[i];
    xgauss = resid->xgauss[i];
    ygauss = resid->ygauss[i];
    sig_xgauss = resid->sig_xgauss[i];
    sig_ygauss = resid->sig_ygauss[i];    
    fwhm_xgauss = resid->fwhm_xgauss[i];
    fwhm_ygauss = resid->fwhm_ygauss[i];    
    norm_gauss = resid->norm_gauss[i];    
    xpoly = resid->xpoly[i];
    ypoly = resid->ypoly[i];
    if(resid->flag) {
      flag = resid->flag[i];
    }
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_WAVELENGTH,
      i, lambda));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_SN,
      i, sn));

    check(cpl_table_set(table,XSH_RESID_TAB_TABLE_COLNAME_ORDER,
      i, order));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_SLITPOSITION,
      i, slit));
    check(cpl_table_set_int(table,XSH_RESID_TAB_TABLE_COLNAME_SLITINDEX,
      i, slit_index));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_XTHPRE,
      i, thpre_x));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_YTHPRE,
      i, thpre_y));

    /* AMO: Decided with Reflex review to remove those columns
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_XTHCOR,
      i, thcor_x));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_YTHCOR,
      i, thcor_y));
    */

    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_XGAUSS,
      i, xgauss));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_YGAUSS,
      i, ygauss));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_SIGMAXGAUSS,
      i, sig_xgauss));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_SIGMAYGAUSS,
      i, sig_ygauss));

    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_FWHMXGAUSS,
      i, fwhm_xgauss));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_FWHMYGAUSS,
      i, fwhm_ygauss));
    check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_NORMGAUSS,
      i, norm_gauss));

    if (resid->solution_type == XSH_DETECT_ARCLINES_TYPE_POLY){
      check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_XPOLY,
        i, xpoly));
      check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_YPOLY,
        i, ypoly));
      check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_RESIDXPOLY,
        i, xpoly-xgauss));
      check(cpl_table_set_double(table,XSH_RESID_TAB_TABLE_COLNAME_RESIDYPOLY,
        i, ypoly-ygauss));
    }
   if(resid->flag) {

      check(cpl_table_set_int(table,XSH_RESID_TAB_TABLE_COLNAME_FLAG,
              i, flag));
   }
  } /* end loop over table size */

  /* create fits file */
  if (resid->solution_type ==  XSH_DETECT_ARCLINES_TYPE_POLY){
    check( xsh_pfits_set_wavesoltype( header, XSH_WAVESOLTYPE_POLYNOMIAL));

    check(residx_min=cpl_table_get_column_min(table,XSH_RESID_TAB_TABLE_COLNAME_RESIDXPOLY));
    check(residx_max=cpl_table_get_column_max(table,XSH_RESID_TAB_TABLE_COLNAME_RESIDXPOLY));
    check(residx_rms=cpl_table_get_column_stdev(table,XSH_RESID_TAB_TABLE_COLNAME_RESIDXPOLY));

    check(residy_min=cpl_table_get_column_min(table,XSH_RESID_TAB_TABLE_COLNAME_RESIDYPOLY));
    check(residy_max=cpl_table_get_column_max(table,XSH_RESID_TAB_TABLE_COLNAME_RESIDYPOLY));
    check(residy_rms=cpl_table_get_column_stdev(table,XSH_RESID_TAB_TABLE_COLNAME_RESIDYPOLY));

    cpl_propertylist_append_double(header,"ESO QC POLY RESX_MIN",residx_min);
    cpl_propertylist_append_double(header,"ESO QC POLY RESX_MAX",residx_max);
    cpl_propertylist_append_double(header,"ESO QC POLY RESX_RMS",residx_rms);


    cpl_propertylist_append_double(header,"ESO QC POLY RESY_MIN",residy_min);
    cpl_propertylist_append_double(header,"ESO QC POLY RESY_MAX",residy_max);
    cpl_propertylist_append_double(header,"ESO QC POLY RESY_RMS",residy_rms);

  }
  else{
    int size;

    size = cpl_propertylist_get_size( header);
    XSH_REGDEBUG("write MODEL in header size %d", size);
    check( xsh_pfits_set_wavesoltype( header, XSH_WAVESOLTYPE_MODEL));
    size = cpl_propertylist_get_size( header);
    XSH_REGDEBUG("write MODEL in header new size %d", size);

    /* QC results are added later from model_kernel 
       function xsh_model_pipe_anneal*/

  }

  /* added for QC request */
  int nfound4=0;
  int nfound4clean=0;
  nfound4=cpl_table_and_selected_int(table,XSH_RESID_TAB_TABLE_COLNAME_SLITINDEX,
                  CPL_EQUAL_TO,4);
  check( xsh_pfits_set_qc_nlinefound_fib4( header,nfound4));
  if(resid->flag) {
      nfound4clean=cpl_table_and_selected_int(table,XSH_RESID_TAB_TABLE_COLNAME_FLAG,
                      CPL_EQUAL_TO,0);
      check( xsh_pfits_set_qc_nlinefound_clean_fib4( header,nfound4clean));
  }
  cpl_table_select_all(table);
  XSH_REGDEBUG("save filename %s", filename);
  check( xsh_pfits_set_pcatg(header, tag));
  check( cpl_table_save(table, header,NULL,filename, CPL_IO_DEFAULT));

  /* Create the frame */
  check(result=xsh_frame_product(filename,
				 tag,
				 CPL_FRAME_TYPE_TABLE,
				 CPL_FRAME_GROUP_PRODUCT,
				 CPL_FRAME_LEVEL_TEMPORARY));


  //check (xsh_add_temporary_file( filename));

  cleanup:
    
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_frame(&result);
    }
    XSH_TABLE_FREE( table);
    return result ;
}

/*---------------------------------------------------------------------------*/
/**
  @brief Get the size of the residual tab

  @param resid
    The resid_tab structure
  @return 
    The size
*/
RESID_TAB_PROPERTY_GET ( size, int, 0)

/**
  @brief Get the lambda array of the residual tab

  @param resid
    The resid_tab structure
  @return 
    Pointer to the lambda array
*/
double * xsh_resid_tab_get_lambda_data(xsh_resid_tab *resid)
{
  double * result = NULL ;

  XSH_ASSURE_NOT_NULL( resid);
  result = resid->lambda ;

  cleanup:
    return result;
}

/**
  @brief Get the Orders array of the residual tab

  @param resid
    The resid_tab structure
  @return 
    Pointer to the order aray
*/
double * xsh_resid_tab_get_order_data(xsh_resid_tab *resid)
{
  double * result = NULL ;

  XSH_ASSURE_NOT_NULL( resid);
  result = resid->order ;

  cleanup:
    return result;
}

/**
  @brief Get the slit_pos array of the residual tab

  @param resid
    The resid_tab structure
  @return 
    Pointer to the lambda aray
*/
double * xsh_resid_tab_get_slitpos_data(xsh_resid_tab *resid)
{
  double * result = NULL ;

  XSH_ASSURE_NOT_NULL( resid);
  result = resid->slit ;

  cleanup:
    return result;
}

/**
  @brief Get the slit_index array of the residual tab

  @param resid
    The resid_tab structure
  @return 
    Pointer to the slit_index array
*/
RESID_TAB_PROPERTY_GET ( slit_index, int*, NULL)

/**
  @brief Get the thpre_x array of the residual tab

  @param resid
    The resid_tab structure
  @return 
    Pointer to the lambda aray
*/
double * xsh_resid_tab_get_thpre_x_data(xsh_resid_tab *resid)
{
  double * result = NULL ;

  XSH_ASSURE_NOT_NULL( resid);
  result = resid->thpre_x ;

  cleanup:
    return result;
}

/**
  @brief Get the thpre_y array of the residual tab

  @param resid
    The resid_tab structure
  @return 
    Pointer to the thpre_y aray
*/
double * xsh_resid_tab_get_thpre_y_data(xsh_resid_tab *resid)
{
  double * result = NULL ;

  XSH_ASSURE_NOT_NULL( resid);
  result = resid->thpre_y ;

  cleanup:
    return result;
}

/**
  @brief Get the xgauss array of the residual tab

  @param resid
    The resid_tab structure
  @return 
    Pointer to the diff_x aray
*/
double * xsh_resid_tab_get_xgauss_data(xsh_resid_tab *resid)
{
  double * result = NULL ;

  XSH_ASSURE_NOT_NULL( resid);
  result = resid->xgauss;

  cleanup:
    return result;
}

/**
  @brief Get the ygauss array of the residual tab

  @param resid
    The resid_tab structure
  @return 
    Pointer to the diff_y aray
*/
double * xsh_resid_tab_get_ygauss_data(xsh_resid_tab *resid)
{
  double * result = NULL ;

  XSH_ASSURE_NOT_NULL( resid);
  result = resid->ygauss ;

  cleanup:
    return result;
}



cpl_error_code
xsh_frame_table_resid_merge(cpl_frame* self, cpl_frame* right,
			    const int solution_type)
{
  cpl_table* stab=NULL;
  cpl_table* rtab=NULL;
  cpl_propertylist* shead=NULL;
  cpl_propertylist* rhead=NULL;
  cpl_propertylist* qc_head=NULL;
  const char* sname=NULL;
  const char* rname=NULL;
  //const char* tag=NULL;
  int i=0;
  int srow=0;
  int rrow=0;
  double wtol=0.001;

  double* pWaveSelf=NULL;
  int* pFlagSelf=NULL;

  int* pSlitIndexSelf=NULL;
  int* pOrderSelf=NULL;
  double* pXThAnnealSelf=NULL;
  double* pYThAnnealSelf=NULL;
  double* pXpolySelf=NULL;
  double* pYpolySelf=NULL;

  double* pResidXpolySelf=NULL;
  double* pResidYpolySelf=NULL;
  double* pResidXmodelSelf=NULL;
  double* pResidYmodelSelf=NULL;


  double* pWaveRight=NULL;
  int* pSlitIndexRight=NULL;
  int* pOrderRight=NULL;
  double* pXThAnnealRight=NULL;
  double* pYThAnnealRight=NULL;
  double* pXpolyRight=NULL;
  double* pYpolyRight=NULL;

  double* pResidXpolyRight=NULL;
  double* pResidYpolyRight=NULL;
  double* pResidXmodelRight=NULL;
  double* pResidYmodelRight=NULL;
  double itol=0.001;
  int k=0;

  sname=cpl_frame_get_filename(self);
  rname=cpl_frame_get_filename(right);
  //tag=cpl_frame_get_filename(right);

  shead=cpl_propertylist_load(sname,0);
  rhead=cpl_propertylist_load(rname,0);
  qc_head=cpl_propertylist_load_regexp(sname,0,"^ESO QC",0);

  cpl_propertylist_append(rhead,qc_head);
  stab=cpl_table_load(sname,1,0);
  rtab=cpl_table_load(rname,1,0);
  srow=cpl_table_get_nrow(stab);
  rrow=cpl_table_get_nrow(rtab);

  pWaveRight=cpl_table_get_data_double(rtab,XSH_RESID_TAB_TABLE_COLNAME_WAVELENGTH);
  pSlitIndexRight=cpl_table_get_data_int(rtab,XSH_RESID_TAB_TABLE_COLNAME_SLITINDEX);
  pOrderRight=cpl_table_get_data_int(rtab,XSH_RESID_TAB_TABLE_COLNAME_ORDER);
  pXThAnnealRight=cpl_table_get_data_double(rtab,XSH_RESID_TAB_TABLE_COLNAME_XTHANNEAL);
  pYThAnnealRight=cpl_table_get_data_double(rtab,XSH_RESID_TAB_TABLE_COLNAME_YTHANNEAL);
 
  pXpolyRight=cpl_table_get_data_double(rtab,XSH_RESID_TAB_TABLE_COLNAME_XPOLY);
  pYpolyRight=cpl_table_get_data_double(rtab,XSH_RESID_TAB_TABLE_COLNAME_YPOLY);

  pResidXpolyRight=cpl_table_get_data_double(rtab,XSH_RESID_TAB_TABLE_COLNAME_RESIDXPOLY);
  pResidYpolyRight=cpl_table_get_data_double(rtab,XSH_RESID_TAB_TABLE_COLNAME_RESIDYPOLY);
  pResidXmodelRight=cpl_table_get_data_double(rtab,XSH_RESID_TAB_TABLE_COLNAME_RESIDXMODEL);
  pResidYmodelRight=cpl_table_get_data_double(rtab,XSH_RESID_TAB_TABLE_COLNAME_RESIDYMODEL);


  pWaveSelf=cpl_table_get_data_double(stab,XSH_RESID_TAB_TABLE_COLNAME_WAVELENGTH);
  pSlitIndexSelf=cpl_table_get_data_int(stab,XSH_RESID_TAB_TABLE_COLNAME_SLITINDEX);
  pOrderSelf=cpl_table_get_data_int(stab,XSH_RESID_TAB_TABLE_COLNAME_ORDER);
  pFlagSelf=cpl_table_get_data_int(stab,XSH_RESID_TAB_TABLE_COLNAME_FLAG);

  pXThAnnealSelf=cpl_table_get_data_double(stab,XSH_RESID_TAB_TABLE_COLNAME_XTHANNEAL);
  pYThAnnealSelf=cpl_table_get_data_double(stab,XSH_RESID_TAB_TABLE_COLNAME_YTHANNEAL);
 
  pXpolySelf=cpl_table_get_data_double(stab,XSH_RESID_TAB_TABLE_COLNAME_XPOLY);
  pYpolySelf=cpl_table_get_data_double(stab,XSH_RESID_TAB_TABLE_COLNAME_YPOLY);

  pResidXpolySelf=cpl_table_get_data_double(stab,XSH_RESID_TAB_TABLE_COLNAME_RESIDXPOLY);
  pResidYpolySelf=cpl_table_get_data_double(stab,XSH_RESID_TAB_TABLE_COLNAME_RESIDYPOLY);
  pResidXmodelSelf=cpl_table_get_data_double(stab,XSH_RESID_TAB_TABLE_COLNAME_RESIDXMODEL);
  pResidYmodelSelf=cpl_table_get_data_double(stab,XSH_RESID_TAB_TABLE_COLNAME_RESIDYMODEL);

  /* TODO: here we need to check also for matching order id */
  for(i=0;i<srow;i++) {

  for (k = 0; k < rrow; k++) {
      if ((fabs(pWaveSelf[i] - pWaveRight[k]) < wtol)
          && (fabs(pSlitIndexSelf[i] - pSlitIndexRight[k]) < itol)
          && (fabs(pOrderSelf[i] - pOrderRight[k]) < itol)
          && pFlagSelf[i] == 0) {
        if (solution_type == XSH_DETECT_ARCLINES_TYPE_MODEL) {
          pXThAnnealSelf[i] = pXThAnnealRight[k];
          pYThAnnealSelf[i] = pYThAnnealRight[k];
          pResidXmodelSelf[i] = pResidXmodelRight[k];
          pResidYmodelSelf[i] = pResidYmodelRight[k];
        } else {
          pXpolySelf[i] = pXpolyRight[k];
          pYpolySelf[i] = pYpolyRight[k];
          pResidXpolySelf[i] = pResidXpolyRight[k];
          pResidYpolySelf[i] = pResidYpolyRight[k];

        }
      }
    }
  }

  check(cpl_table_save(stab,rhead,NULL,sname,CPL_IO_DEFAULT));


 cleanup:
  xsh_free_table(&stab);
  xsh_free_table(&rtab);
  xsh_free_propertylist(&shead);
  xsh_free_propertylist(&rhead);
  xsh_free_propertylist(&qc_head);

  return cpl_error_get_code();

}
/**@}*/
