/*                                                                            *
 *   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: 2012-12-18 16:10:42 $
 * $Revision: 1.110 $
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/*----------------------------------------------------------------------------*/
/**
 * @defgroup xsh_opt_extract Optimal extraction handling
 * @ingroup drl_functions
 */
/*----------------------------------------------------------------------------*/
/**@{*/

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

#include <math.h>
#include <xsh_drl.h>

#include <xsh_badpixelmap.h>
#include <xsh_data_rec.h>
#include <xsh_data_pre.h>
#include <xsh_utils_wrappers.h>
#include <xsh_data_order.h>
#include <xsh_data_wavesol.h>
#include <xsh_data_spectralformat.h>
#include <xsh_data_localization.h>
#include <xsh_dfs.h>
#include <xsh_pfits.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_fit.h>
#include <xsh_model_io.h>
#include <xsh_model_kernel.h>
#include <xsh_rectify.h>
#include <cpl.h>
#include <gsl/gsl_sf_erf.h>
#include <xsh_blaze.h>
/*-----------------------------------------------------------------------------
  Functions prototypes
  -----------------------------------------------------------------------------*/
#define SLIT_USEFUL_WINDOW_FACTOR 0.80
#define FIT_FWHM_LIMIT 30
#define OPT_EXTRACT_SLIT_SIZE 80

#define FLUX_MODE 0
#define WAVEMAP_MODE 1

#define REGDEBUG_PIXELSIZE 0
#define REGDEBUG_INTEGRATE 0
/*-----------------------------------------------------------------------------
 *
  Prototype
  -----------------------------------------------------------------------------*/

static void xsh_image_gaussian_fit_y( cpl_image* img, int chunk_size,
  int deg_poly, int oversample,
  cpl_polynomial** center, cpl_polynomial** height,
  cpl_polynomial** width, cpl_polynomial** offset);

static cpl_image* xsh_image_create_gaussian_image( cpl_image *src,
  cpl_polynomial* centerp, cpl_polynomial* heightp, cpl_polynomial* widthp,
  cpl_polynomial* offsetp);

static cpl_image* xsh_image_create_model_image( cpl_image *src_img,
  double *x_data, double *y_data, double kappa, int niter, double frac_min);

static cpl_vector* xsh_vector_integrate( int biny, int absorder, int oversample,
  cpl_vector *ref_pos, double step,
  cpl_vector *ref_values,  cpl_vector *err_values, cpl_vector *qual_values,
  cpl_vector *new_pos,
  cpl_vector **spectrum_err, cpl_vector **spectrum_qual);

/*-----------------------------------------------------------------------------
 *
  Implementation
  -----------------------------------------------------------------------------*/

static void xsh_interpolate_spectrum( int biny, int oversample, 
  int absorder, double lambda_step, 
  cpl_vector *init_pos,
  cpl_vector *std_flux, cpl_vector *std_err, cpl_vector *std_qual, 
  cpl_vector *opt_flux, cpl_vector *opt_err, cpl_vector *opt_qual,
  cpl_vector **res_pos, 
  cpl_vector **res_std_flux, cpl_vector **res_std_err, cpl_vector **res_std_qual,
  cpl_vector **res_opt_flux, cpl_vector **res_opt_err, cpl_vector **res_opt_qual)
{

  int init_size;
  double lambda_min, lambda_max;
  double binlambda_min, binlambda_max;
  int mult_min, mult_max;
  int res_size, i;

  XSH_ASSURE_NOT_NULL( init_pos);
  XSH_ASSURE_NOT_NULL( std_flux);
  XSH_ASSURE_NOT_NULL( std_err);
  XSH_ASSURE_NOT_NULL( std_qual);
  XSH_ASSURE_NOT_NULL( opt_flux);
  XSH_ASSURE_NOT_NULL( opt_err);
  XSH_ASSURE_NOT_NULL( opt_qual);
  XSH_ASSURE_NOT_NULL( res_pos);
  XSH_ASSURE_NOT_NULL( res_std_flux);
  XSH_ASSURE_NOT_NULL( res_std_err);
  XSH_ASSURE_NOT_NULL( res_std_qual);
  XSH_ASSURE_NOT_NULL( res_opt_flux);
  XSH_ASSURE_NOT_NULL( res_opt_err);
  XSH_ASSURE_NOT_NULL( res_opt_qual);

  check( init_size = cpl_vector_get_size( init_pos));

  check( lambda_min = cpl_vector_get( init_pos, 0));
  check( lambda_max = cpl_vector_get( init_pos, init_size-1));

  /* round value to be multiple of lambda_step */
  mult_min = (int) ceil(lambda_min/lambda_step)+1;
  binlambda_min = mult_min*lambda_step;

  mult_max = (int) floor(lambda_max/lambda_step)-1;
  binlambda_max = mult_max*lambda_step;

  xsh_msg("lambda_min %f lambda_max %f TEST %f %f : step %f", lambda_min, lambda_max, binlambda_min, binlambda_max, lambda_step);

  res_size = mult_max-mult_min+1;
 
  check( *res_pos = cpl_vector_new( res_size));

  for( i=0; i< res_size; i++){
    check( cpl_vector_set( *res_pos, i,binlambda_min+i*lambda_step));
  }

  check( *res_std_flux = xsh_vector_integrate( 
      biny, absorder, oversample, init_pos, lambda_step,
      std_flux, std_err, std_qual, *res_pos, res_std_err, res_std_qual));
  check( *res_opt_flux = xsh_vector_integrate( 
      biny, absorder, oversample, init_pos, lambda_step,
      opt_flux, opt_err, opt_qual, *res_pos, res_opt_err, res_opt_qual));

  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_vector( res_pos);
      xsh_free_vector( res_std_flux);
      xsh_free_vector( res_std_err);
       xsh_free_vector( res_std_qual);
      xsh_free_vector( res_opt_flux);
      xsh_free_vector( res_opt_err);
      xsh_free_vector( res_opt_qual);
    }
    return;
}


cpl_image* xsh_optextract_produce_model( cpl_image* s2Dby1D_img, 
  int method, int chunk_ovsamp_size, int deg_poly, int oversample, 
  double *extract_x_data, double *extract_y_data, int abs_order,
  double kappa, int niter, double frac_min){

  cpl_polynomial *center_gauss_poly = NULL;
  cpl_polynomial *height_gauss_poly = NULL;
  cpl_polynomial *offset_gauss_poly = NULL;
  cpl_polynomial *width_gauss_poly = NULL;
  cpl_image *model_img = NULL;
  int model_x, model_y, ix, iy;
  double *model_data = NULL;

  XSH_ASSURE_NOT_NULL( s2Dby1D_img);
  XSH_ASSURE_NOT_NULL( extract_x_data);
  XSH_ASSURE_NOT_NULL( extract_y_data);

  xsh_msg_dbg_high( "Produce model for order %d", abs_order);
  
  if ( method == GAUSS_METHOD){
      check( xsh_image_gaussian_fit_y( s2Dby1D_img, chunk_ovsamp_size,
        deg_poly, oversample,
        &center_gauss_poly, &height_gauss_poly, &width_gauss_poly,
        &offset_gauss_poly));

      check( model_img = xsh_image_create_gaussian_image( s2Dby1D_img,
        center_gauss_poly,
        height_gauss_poly, width_gauss_poly, offset_gauss_poly));

#if REGDEBUG_GAUSSIAN
      if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM) {
        char poly_name[256];
        FILE* poly_file = NULL;
        int nx = cpl_image_get_size_x( model_img);
        int i;

        sprintf( poly_name, "gaussian_center_poly_%d.dat", abs_order);
        poly_file = fopen( poly_name, "w");

        for(i=0; i< nx; i++){
          fprintf( poly_file, "%d %d\n", i+1,
            (int) cpl_polynomial_eval_1d( center_gauss_poly, i, NULL)+1);
        }
        fclose( poly_file);

        sprintf( poly_name, "gaussian_sigma_poly_%d.dat", abs_order);
        poly_file = fopen( poly_name, "w");

        for(i=0; i< nx; i++){
          fprintf( poly_file, "%d %d\n", i+1,
            (int) cpl_polynomial_eval_1d( width_gauss_poly, i, NULL)+1);
        }
        fclose( poly_file);
      }
#endif
  }
    else{
      check( model_img = xsh_image_create_model_image( s2Dby1D_img,
        extract_x_data, extract_y_data, kappa, niter, frac_min));
    }

  /* force positivity */
  model_x = cpl_image_get_size_x( model_img);
  model_y = cpl_image_get_size_y( model_img);
  model_data = cpl_image_get_data_double( model_img);

  for( ix=0; ix < model_x; ix++){
    double psum =0.0;

    for( iy=0; iy < model_y; iy++){
      int ipos = iy*model_x+ix;

      if ( model_data[ipos] < 0){
        model_data[ipos] = 0;
      }
      psum += model_data[ipos];
    }
    /* normalize */
    for( iy=0; iy < model_y; iy++){
      int ipos = iy*model_x+ix;

      model_data[ipos] /= psum;
    }
  }

  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_image( &model_img);
    }
    xsh_free_polynomial( &center_gauss_poly);
    xsh_free_polynomial( &width_gauss_poly);
    xsh_free_polynomial( &height_gauss_poly);
    xsh_free_polynomial( &offset_gauss_poly);
    return model_img;
}





    /* SLIT aproximative position */
static void xsh_object_localize( cpl_frame *slitmap_frame, 
  cpl_frame *loc_frame, int oversample, int box_hsize,
  int nlambdas, int ny_extract, double *extract_x_data, 
  double *extract_y_data, int *ymin, int *ymax)
{
   int u;
  double scen=0.0;
  cpl_image *slitmap = NULL;
  float *slit_data = NULL;
  int slit_nx;
  int ny_extract_min =-1, ny_extract_max=-1, ny_extract_cen=-1;
  int mid_lambda;
  double diff_s_min = 50.0;
  xsh_localization *loc = NULL;
  int ref_ov=0;

  XSH_ASSURE_NOT_NULL( slitmap_frame);
  XSH_ASSURE_NOT_NULL( loc_frame);
  XSH_ASSURE_NOT_NULL( extract_x_data);
  XSH_ASSURE_NOT_NULL( extract_y_data);
  XSH_ASSURE_NOT_NULL( ymin);
  XSH_ASSURE_NOT_NULL( ymax);

  check( slitmap = cpl_image_load( cpl_frame_get_filename( slitmap_frame),
    CPL_TYPE_FLOAT, 0, 0));
  check( loc = xsh_localization_load( loc_frame));

  check( slit_nx = cpl_image_get_size_x( slitmap));
  check( slit_data = cpl_image_get_data_float( slitmap));

  check( scen = cpl_polynomial_eval_1d( loc->cenpoly, 0, NULL));

  mid_lambda = 0.5*nlambdas;

  for( u=1; u < ny_extract; u++){
    int x,y;
    double s;
    double diff;

    x = (int) (extract_x_data[u*nlambdas+mid_lambda]+0.5);
    y = (int) (extract_y_data[u*nlambdas+mid_lambda]+0.5);
    s = slit_data[x+y*slit_nx];

    diff = fabs(s-scen);

    if (diff < diff_s_min){
      ny_extract_cen= u;
      diff_s_min = diff;
    }
  }

  /* and the over-sample of reference pixel */
  ref_ov += floor(0.5*(oversample-1));
  
  ny_extract_min= ny_extract_cen -box_hsize-ref_ov;
  ny_extract_max= ny_extract_cen +box_hsize+ref_ov;

  if ( ny_extract_min < 0){
    ny_extract_min = 0;
  }
  if ( ny_extract_max >= ny_extract){
    ny_extract_max = ny_extract-1;
  }
  *ymin = ny_extract_min; 
  *ymax = ny_extract_max;

  cleanup:
    xsh_free_image( &slitmap);
    xsh_localization_free( &loc);
    return;
}


static int xsh_interpolate_linear(float *fluxtab, float *errtab, int *qualtab,
  int nx, int ny, float pos_x, 
  float pos_y, double *flux, double *err, int* qual, int mode){
  
  float f00=0.0, f10=0.0, f01=0.0, f11=0.0;
  float e00=0.0, e10=0.0, e01=0.0, e11=0.0;
  int intx, inty;
  float fx=0.0, fy=0.0;
  //int nbval = 0;
  int qual_comb = -1;
  double A1, A2, A3, A4;
  int ret=0;

  intx = (int) pos_x;
  inty = (int) pos_y;

  XSH_ASSURE_NOT_ILLEGAL( intx >= 0 && intx <nx);
  XSH_ASSURE_NOT_ILLEGAL( inty >= 0 && inty <ny);
  XSH_ASSURE_NOT_NULL( flux);
  XSH_ASSURE_NOT_NULL( err);
  int pix=intx+inty*nx;
  int pix_plus_1=pix+1;
  /* combine the bad pixels but flag them */
  f00 = fluxtab[pix];
  e00 = errtab[pix];
  qual_comb = qualtab[pix];

  if ( (intx +1) < nx){
    f10 = fluxtab[pix_plus_1];
    e10 = errtab[pix_plus_1];
    fx = pos_x-intx;
    qual_comb |= qualtab[pix_plus_1];
  }
  if ( (inty +1) < ny){
    f01 = fluxtab[pix+nx];
    e01 = errtab[pix+nx];
    fy = pos_y-inty;
    qual_comb |= qualtab[pix+nx];
  }
  if ( ((intx +1) < nx) && ((inty +1) < ny)){
    f11 = fluxtab[pix_plus_1+nx];
    e11 = errtab[pix_plus_1+nx];
    qual_comb |= qualtab[pix_plus_1+nx];
  }

  if  ( mode == WAVEMAP_MODE && 
      ( f00 == 0.0 || f10 == 0.0 || f01 == 0 || f11 == 0)){
    xsh_msg_dbg_medium("pixel %f, %f at zero, interpolate with "\
      "(%d,%d)%f, (%d,%d)%f (%d,%d)%f, (%d,%d)%f", pos_x, pos_y, 
      intx, inty, f00, intx+1, inty, f10, intx, inty+1, f01,
      intx+1, inty+1, f11);
    ret = 1;
  }
  A1 = (1-fx)*(1-fy);
  A2 = fx*(1-fy);
  A3 = (1-fx)*fy;
  A4 = fx*fy;

  *flux = f00*A1 + f10*A2 + f01*A3 + f11*A4;

  *err = sqrt( A1*A1*e00*e00+A2*A2*e10*e10+A3*A3*e01*e01+A4*A4*e11*e11);
  *qual = qual_comb;

  cleanup:
    return ret;
}
/*****************************************************************************/
 
/*****************************************************************************/
/** 
  @brief
    Give the value of lambda and x central position for a given y
  @param wavemap_frame
    The wave map frame
  @param slitmap_frame
    The slit map frame  
  @param starty
    The first y position
  @param endy
    The last y position
  @param oversample
    The oversample factor in y
  @param order_list
    The order table list
  @param iorder
    Index of order
  @param[out] xtab
    The array of x central positions 
  @param[out] ytab
    The array of y central positions
  @param order
    The absolute order
  @param spectralformat
    The spectral format list
  @param[out] lambdatab
    The array of wavelength at central positions 
  @param[out] slitstab
    The array of slit at central positions
  @param(out] sizetab
    The array size of different tab
  @param[in] instr
    The instrument structure
*/
static void xsh_wavemap_lambda_range( cpl_frame *wavemap_frame, 
  cpl_frame *slitmap_frame,
  int starty, 
  int endy, int oversample, xsh_order_list *order_list, int iorder,double *xtab, 
  double *ytab, int order, xsh_spectralformat_list *spectralformat, 
  double *lambdastab, double *slitstab, int* sizetab, xsh_instrument *instr)
{
  cpl_image* wavemap = NULL;
  cpl_image *slitmap = NULL;
  int* qual = NULL;
  float *wavemap_data = NULL;
  float *slitmap_data = NULL;
  int  i, nx=0, ny=0, qual_val=0;
  double lambda=0.0, lambda_next=0.0, lambda_old=0, err=0;
  double lambda_sf_max=0;
  double slit= -1;
  float y_ovs=0, x=0, y=0;
  int size=0;
  int ycen=0;
  double xcen=0;
  double lambda_min=0, lambda_max=0;
  const char* wave_filename = NULL;
  const char* slitmap_name = NULL;

  XSH_ASSURE_NOT_NULL( wavemap_frame);
  XSH_ASSURE_NOT_NULL( sizetab);
  XSH_ASSURE_NOT_NULL( order_list);
  XSH_ASSURE_NOT_NULL( xtab);
  XSH_ASSURE_NOT_NULL( ytab);
  XSH_ASSURE_NOT_NULL( spectralformat);
  XSH_ASSURE_NOT_NULL( lambdastab);

  check( wave_filename = cpl_frame_get_filename(  wavemap_frame));
  check( slitmap_name = cpl_frame_get_filename(  slitmap_frame));
  xsh_msg_dbg_medium("Wave map name %s", wave_filename);
  xsh_msg_dbg_medium("Slit map name %s", slitmap_name);

  check( wavemap = cpl_image_load( wave_filename,
    CPL_TYPE_FLOAT, 0, 0)); 
  check( slitmap = cpl_image_load( slitmap_name,
    CPL_TYPE_FLOAT, 0, 0));

  check( nx = cpl_image_get_size_x( wavemap));
  check( ny = cpl_image_get_size_y( wavemap));

  XSH_CALLOC(qual, int, nx*ny);
  check( wavemap_data = cpl_image_get_data_float( wavemap));
  check( slitmap_data = cpl_image_get_data_float( slitmap));

  check( lambda_min = xsh_spectralformat_list_get_lambda_min(
    spectralformat, order));
  check( lambda_sf_max = xsh_spectralformat_list_get_lambda_max(
    spectralformat, order)); 

  xsh_msg_dbg_high( "SPECTRAL FORMAT lambda min %f max %f", lambda_min, lambda_sf_max);

  lambda_max = lambda_sf_max;


  if ( xsh_instrument_get_arm( instr) !=XSH_ARM_NIR){

    ycen = starty-1;

    xsh_msg_dbg_high("DBG ycen %d", ycen);

    while( !((lambda >= lambda_min) && (lambda_next > lambda)) ){
      double start_x_next;

      ycen++;
      check( xcen = xsh_order_list_eval( order_list, order_list->list[iorder].cenpoly,
        ycen));
      xsh_msg_dbg_high("DBG xcen %f ycen %d", xcen, ycen);
      check( xsh_interpolate_linear( wavemap_data, wavemap_data, qual,
        nx, ny, xcen-1, ycen-1, &lambda, &err, &qual_val, WAVEMAP_MODE));
      xsh_msg_dbg_high("interLambda %f", lambda);

      check( start_x_next= xsh_order_list_eval( order_list, order_list->list[iorder].cenpoly,
        ycen+1));
      check( xsh_interpolate_linear( wavemap_data, wavemap_data, qual,
        nx, ny, start_x_next-1, ycen, &lambda_next, &err, &qual_val, WAVEMAP_MODE));
    }
    y_ovs=ycen*oversample+1;
  }
  else{

    ycen = endy+1;
   
//    xsh_msg_dbg_high("DBG ycen %d", ycen);
 
    while( !((lambda >= lambda_min) && (lambda_next > lambda)) ){
      double start_x_next;

      ycen--;
      check( xcen = xsh_order_list_eval( order_list, order_list->list[iorder].cenpoly,
        ycen));
//      xsh_msg_dbg_high("DBG xcen %f ycen %d", xcen, ycen);
      check( xsh_interpolate_linear( wavemap_data, wavemap_data, qual,
        nx, ny, xcen-1, ycen-1, &lambda, &err, &qual_val, WAVEMAP_MODE));
//      xsh_msg_dbg_high("interLambda %f", lambda);

      check( start_x_next= xsh_order_list_eval( order_list, order_list->list[iorder].cenpoly,
        ycen-1));
      check( xsh_interpolate_linear( wavemap_data, wavemap_data, qual,
        nx, ny, start_x_next-1, ycen-2, &lambda_next, &err, &qual_val, WAVEMAP_MODE));
    }
    y_ovs=ycen*oversample-1;
  }

  /* initialize */
  size=0;
  x= (float) xcen;
  y =(float) ycen;
  lambda_old = -1;

  xsh_msg_dbg_high("first X,Y %f,%f lambda %f in [%f,%f]", x,y, lambda, lambda_min, lambda_max);

  if ( xsh_instrument_get_arm( instr) !=XSH_ARM_NIR){

    while( (lambda >= lambda_min) && (lambda > lambda_old) &&  
      (lambda <= lambda_max) &&
      ( y_ovs <= (endy * oversample)) ){

      xsh_msg_dbg_high("size %d lambda %f ,x %f y %f", size, lambda, x,y);
      /* take the X,Y pixel */
      lambdastab[size] = lambda;
      xtab[size] = x;
      ytab[size] = y;
      size++;
      lambda_old = lambda;
      y = (float) (y_ovs) / (float) oversample;

      check( x = xsh_order_list_eval( order_list, order_list->list[iorder].cenpoly,
        y));

      check( xsh_interpolate_linear( wavemap_data, wavemap_data, qual,
        nx, ny, x-1, y-1, &lambda, &err, &qual_val, WAVEMAP_MODE));
      y_ovs++;
    }
  }
  else{
    while( (lambda >= lambda_min) &&
      (lambda <= lambda_max) &&
      ( y_ovs >= (starty * oversample)) ){

      xsh_msg_dbg_high("size %d lambda %f ,x %f y %f", size, lambda, x,y);
      /* take the X,Y pixel */
      lambdastab[size] = lambda;
      xtab[size] = x;
      ytab[size] = y;
      size++;
      lambda_old = lambda;
      y = (float) (y_ovs) / (float) oversample;

      check( x = xsh_order_list_eval( order_list, order_list->list[iorder].cenpoly,
        y));

      check( xsh_interpolate_linear( wavemap_data, wavemap_data, qual,
        nx, ny, x-1, y-1, &lambda, &err, &qual_val, WAVEMAP_MODE));
      y_ovs--;
//      xsh_msg_dbg_high("X Y %f, %f", x, y);
//      xsh_msg_dbg_high(" END : lambda %f [%f,%f] (lambda %f > old %f) (Y %f >= %d)", lambda, lambda_min, lambda_max, lambda, lambda_old, y_ovs, starty * oversample);
    }
  }

  *sizetab = size;

  for (i=0; i< size; i++){
    x = xtab[i];
    y = ytab[i];
    check( xsh_interpolate_linear( slitmap_data, slitmap_data, qual,
        nx, ny, x-1, y-1, &slit, &err, &qual_val, WAVEMAP_MODE));
    slitstab[i] = slit;
  }

/* REGDEBUG to remove after */
/*
{
  int itest;

  for(itest=0; itest < size; itest++){
    xsh_msg("CMP order, X,Y, lambda slit : %d %f %f %f %f", order, xtab[itest], ytab[itest], lambdastab[itest], 
      slitstab[itest]);
  }
}
*/

  cleanup:
    XSH_FREE( qual);
    xsh_free_image( &wavemap); 
    return;
}
/*****************************************************************************/


/*****************************************************************************/
/** 
  @brief
    Divide vector values by a polynomial
  @param[in,out] vector
    The vector values
  @param[in] oversample
    The oversample factor
  @param[in] poly
    The polynomial function that define value to divide
  @param[in] shift
    Shift between vector and polynomial
  @param(in] instr
    Instrument structure used to define ARM
*/
/*****************************************************************************/
static void xsh_vector_divide_poly( cpl_vector *vector, double oversample,
  cpl_polynomial *poly, int shift, xsh_instrument *instr)
{
  int i, size;

  XSH_ASSURE_NOT_NULL( vector);
  XSH_ASSURE_NOT_NULL( poly);

  check( size = cpl_vector_get_size( vector));

  for( i=0; i<  size; i++){
    double flux, val;

    check( val = cpl_polynomial_eval_1d( poly,
      (double)i/oversample+shift, NULL));
    if ( xsh_instrument_get_arm( instr) == XSH_ARM_NIR){
      check( val = cpl_polynomial_eval_1d( poly,
      (double)(size-1-i)/oversample+shift, NULL));
    }
    else{
      check( val = cpl_polynomial_eval_1d( poly,
      (double)i/oversample+shift, NULL));
    }
    check( flux = cpl_vector_get( vector, i));
    check( cpl_vector_set( vector, i, flux/val));
  }

  cleanup:
    return;
}


/*****************************************************************************/
/** 
  @brief
    Do a gaussian fit of Y columns by chunk and fit the position by polynomials
    in X
  @param[in] img 
    The image 
  @param[in] chunk_size
    The size of chunk in pixels along X axis
  @param[in] deg_poly
    The degree of polynomial fit
  @param[in] oversample
    The oversample factor  
  @param[out] center
    The NEW ALLOCATED polynomial that describes gaussian center position along X axis
  @param[out] height
    The NEW ALLOCATED polynomial that describes gaussian height along X axis
  @param[out] width
    The NEW ALLOCATED polynomial that describes gaussian width along X axis
  @param[out] offset
    The NEW ALLOCATED polynomial that describes gaussian offset along X axis  
*/  
/*****************************************************************************/
static void xsh_image_gaussian_fit_y( cpl_image* img, int chunk_size, 
  int deg_poly, int oversample, 
  cpl_polynomial** center, cpl_polynomial** height, 
  cpl_polynomial** width, cpl_polynomial** offset)
{
  
  int nx, ny, nb_chunk, i;
  cpl_vector *center_gauss = NULL;
  cpl_vector *height_gauss = NULL;
  cpl_vector *width_gauss = NULL;
  cpl_vector *offset_gauss = NULL;
  cpl_vector *xpos_gauss = NULL;
  cpl_vector *chunk_vector = NULL;
  cpl_vector *chunk_pos_vector = NULL;
  cpl_vector *median_vect = NULL;
  double *data = NULL;
  double *center_gauss_data = NULL;
  double *height_gauss_data = NULL;
  double *width_gauss_data = NULL;
  double *offset_gauss_data = NULL;
  double *xpos_gauss_data = NULL;
  double *median_data = NULL;

  int nbfit = 0;

  XSH_ASSURE_NOT_NULL( img);
  XSH_ASSURE_NOT_ILLEGAL( chunk_size >=0);
  XSH_ASSURE_NOT_ILLEGAL( deg_poly >=0);
  XSH_ASSURE_NOT_NULL( center);
  XSH_ASSURE_NOT_NULL( height);
  XSH_ASSURE_NOT_NULL( width);
  XSH_ASSURE_NOT_NULL( offset);

  check( nx = cpl_image_get_size_x( img));
  check( ny = cpl_image_get_size_y( img));
  check( data = cpl_image_get_data_double( img));  

  if ( (chunk_size >= nx) || (chunk_size == 0)){
    chunk_size = nx;
  }

  nb_chunk = nx / chunk_size;

  XSH_MALLOC( center_gauss_data, double, nb_chunk*2);
  XSH_MALLOC( height_gauss_data, double, nb_chunk*2);
  XSH_MALLOC( width_gauss_data, double, nb_chunk*2);
  XSH_MALLOC( offset_gauss_data, double, nb_chunk*2);
  XSH_MALLOC( xpos_gauss_data, double, nb_chunk*2);
  XSH_CALLOC( median_data, double, chunk_size);

  check( chunk_vector = cpl_vector_new( ny));
  check( chunk_pos_vector = cpl_vector_new( ny));

  xsh_msg_dbg_medium( "nx %d nb_chunks %d of size %d", nx, nb_chunk, 
    chunk_size);

  for( i=0; i< nb_chunk; i++){
    double x0, sigma, area, offset_val, height_val, fwhm;
    double med;
    int j;
    int ideb, ilast;

    ideb = i*chunk_size;
    ilast = ideb+chunk_size;

    for( j=0; j< ny; j++){
      int l, val_nb=0;
        
      for( l=ideb; l< ilast; l++){
        val_nb++;
        median_data[l-ideb] = data[j*nx+l];
      }
      check( median_vect = cpl_vector_wrap( val_nb, median_data));
      check( med = cpl_vector_get_median( median_vect));
      check( cpl_vector_set( chunk_vector, j, med));
      check( cpl_vector_set( chunk_pos_vector, j, j));
    }
#if REGDEBUG_OPTEXTRACT
#if REGDEBUG_GAUSSIAN
    /* REGDEBUG */
    {
      FILE* regdebug = NULL;
      char filename[256];

      sprintf(filename, "gaussian_points_%d.dat",i);
      regdebug = fopen(filename,"w");
      for( j=0; j< ny; j++){
        double pos, val;

        pos = cpl_vector_get( chunk_pos_vector,j);
        val = cpl_vector_get( chunk_vector,j);

        fprintf(regdebug, "%f %f\n", pos, val);
      }
      fclose(regdebug);
    }
#endif
#endif
    cpl_vector_fit_gaussian( chunk_pos_vector, NULL, chunk_vector, NULL,
      CPL_FIT_ALL, &x0, &sigma, &area, &offset_val, NULL, NULL, NULL);

    if ( cpl_error_get_code() == CPL_ERROR_NONE){
      height_val = area / sqrt(2*CPL_MATH_PI*sigma*sigma);
      fwhm = CPL_MATH_FWHM_SIG*sigma;
      if ( fwhm < (oversample*FIT_FWHM_LIMIT)){
        center_gauss_data[nbfit] = x0;
        height_gauss_data[nbfit] = height_val;
        width_gauss_data[nbfit] = sigma;
        xpos_gauss_data[nbfit] = ideb;
        offset_gauss_data[nbfit] = offset_val;
        nbfit++;
        center_gauss_data[nbfit] = x0;
        height_gauss_data[nbfit] = height_val;
        width_gauss_data[nbfit] = sigma;
        xpos_gauss_data[nbfit] = ilast;
        offset_gauss_data[nbfit] = offset_val;
        nbfit++;
      }
      else{
        xsh_msg("WARNING chunk %d-%d so big FWHM %f expected < %d",
          ideb, ilast, fwhm, oversample*FIT_FWHM_LIMIT);
      }
    }
    else {
      xsh_msg("WARNING chunk %d-%d don't converge with gaussian fit",
        ideb, ilast);
      xsh_error_reset();
    }
  }
  /* fit with order 3 polynomials */

  if ( nbfit == 0){
    xsh_msg("WARNING nbfit == 0 force poly degree to 0");
    nbfit = 1;
    deg_poly = 0;
    xpos_gauss_data[0] = 0;
    center_gauss_data[0] = ny/2.0;
    height_gauss_data[0] = 1;
    width_gauss_data[0] = ny;
    offset_gauss_data[0] = 0.0;
  }
  else if ( nbfit <= deg_poly){
    xsh_msg("WARNING %d (nbfit) < %d (poly degree) force poly degree to %d",
      nbfit, deg_poly, nbfit-1);
    deg_poly = nbfit-1; 
  }

  check(xpos_gauss = cpl_vector_wrap( nbfit, xpos_gauss_data));
  check( center_gauss = cpl_vector_wrap( nbfit, center_gauss_data));
  check( height_gauss = cpl_vector_wrap( nbfit, height_gauss_data));
  check( width_gauss = cpl_vector_wrap( nbfit, width_gauss_data));
  check( offset_gauss = cpl_vector_wrap( nbfit, offset_gauss_data));

  check( *center = xsh_polynomial_fit_1d_create( xpos_gauss,
    center_gauss, deg_poly, NULL));
  check( *height = xsh_polynomial_fit_1d_create( xpos_gauss,
    height_gauss, deg_poly, NULL));
  check( *width = xsh_polynomial_fit_1d_create( xpos_gauss,
    width_gauss, deg_poly, NULL));
  check( *offset = xsh_polynomial_fit_1d_create( xpos_gauss,
    offset_gauss, deg_poly, NULL));

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_polynomial( center);
      xsh_free_polynomial( height);
      xsh_free_polynomial( width);
      xsh_free_polynomial( offset);
    }
    xsh_unwrap_vector( &center_gauss);
    xsh_unwrap_vector( &width_gauss);
    xsh_unwrap_vector( &height_gauss);
    xsh_unwrap_vector( &offset_gauss);
    xsh_unwrap_vector( &xpos_gauss);
    xsh_unwrap_vector( &median_vect);
    XSH_FREE( center_gauss_data);
    XSH_FREE( width_gauss_data);
    XSH_FREE( height_gauss_data);
    XSH_FREE( offset_gauss_data);
    XSH_FREE( xpos_gauss_data);
    XSH_FREE( median_data);
    xsh_free_vector( &chunk_vector);
    xsh_free_vector( &chunk_pos_vector);
    return;
}


static cpl_image* xsh_image_create_model_image( cpl_image *src_img,
  double *x_data, double *y_data, double kappa, int niter, double frac_min)
{
  cpl_image *result = NULL;
  int nx,ny;
  int i,j;
  double *data = NULL;
  double *src_data = NULL;
  double *pos = NULL;
  double *line = NULL;
  cpl_vector *pos_vect = NULL;
  cpl_vector *line_vect = NULL;
  int size;
  cpl_polynomial *fit = NULL;

  /* Check input parameters */
  XSH_ASSURE_NOT_NULL( src_img);
  check( nx = cpl_image_get_size_x( src_img));
  check( ny = cpl_image_get_size_y( src_img));
  check( result = cpl_image_new( nx, ny, CPL_TYPE_DOUBLE));
  check( data = cpl_image_get_data_double( result));
  check( src_data = cpl_image_get_data_double( src_img));
  XSH_CALLOC( pos, double, nx);
  XSH_CALLOC( line, double, nx);
  double before=cpl_test_get_walltime();
   xsh_msg_debug("setting timing");

  for(j=0; j<ny; j++){
    double val;
    double chisq;
    int *flags;
    int j_nx=j*nx;
    size = 0;
    for(i=0; i<nx; i++){
      double diffx, diffy;
      int pix=i+j_nx;
      diffx = x_data[pix]-(int)x_data[pix];
      if(diffx<0.2) {
         diffy = y_data[pix]-(int)y_data[pix];

         if ( diffy < 0.2){
           pos[size] = i;
           line[size] = src_data[pix];
           size++;
         }
      }
    }
    check( pos_vect = cpl_vector_wrap( size, pos));
    check( line_vect = cpl_vector_wrap( size, line));

    check( xsh_array_clip_poly1d( pos_vect, line_vect, kappa, niter, 
      frac_min, 3, &fit, &chisq, &flags));

    for(i=0; i<nx; i++){
      check( val = cpl_polynomial_eval_1d( fit, i, NULL));
      data[i+j_nx] = fabs(val);
    }
    xsh_unwrap_vector( &pos_vect);
    xsh_unwrap_vector( &line_vect);
    xsh_free_polynomial( &fit);
    XSH_FREE( flags);
  }
  double after=cpl_test_get_walltime();
     xsh_msg_debug("setting timing");
     xsh_msg("Time spent %g %g %g",before,after,after-before);

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_image( &result);
      xsh_unwrap_vector( &pos_vect);
      xsh_unwrap_vector( &line_vect);
      xsh_free_polynomial( &fit);
    }
    XSH_FREE( pos);
    XSH_FREE( line);
    return result;
}

static cpl_image* xsh_image_create_gaussian_image( cpl_image *src, 
  cpl_polynomial* centerp, cpl_polynomial* heightp, cpl_polynomial* widthp,
  cpl_polynomial* offsetp)
{
  cpl_image *result = NULL;
  int nx,ny;
  int i,j;
  double *data = NULL;

  /* Check input parameters */
  XSH_ASSURE_NOT_NULL( src);
  XSH_ASSURE_NOT_NULL( centerp);
  XSH_ASSURE_NOT_NULL( heightp);
  XSH_ASSURE_NOT_NULL( widthp);
  XSH_ASSURE_NOT_NULL( offsetp);

  check( nx = cpl_image_get_size_x( src));
  check( ny = cpl_image_get_size_y( src));
  check( result = cpl_image_new( nx, ny, CPL_TYPE_DOUBLE));
  check( data = cpl_image_get_data_double( result));

  for(i=0; i<nx; i++){
    double x0, height, width, offset, z;
    //double zlow, zup;
    /* not needed: weight; */

    check( x0 = cpl_polynomial_eval_1d( centerp, i, NULL));
    check( height = cpl_polynomial_eval_1d( heightp, i, NULL));
    check( width = cpl_polynomial_eval_1d( widthp, i, NULL));
    check( offset = cpl_polynomial_eval_1d( offsetp, i, NULL));

    double fct=width*XSH_MATH_SQRT_2;
    double inv_fct=1.0/fct;
    //double x0_plus=x0+0.5;
    //double x0_minus=x0-0.5;

    for(j=0; j<ny; j++){
      z = (j-x0)*inv_fct;
      //zlow = (j-x0_plus)*inv_fct;
      //zup = (j-x0_minus)*inv_fct;
      data[i+j*nx] = height*exp(-(z*z))+offset;
/* not needed

 weight = fct*(gsl_sf_erf(zup)-gsl_sf_erf(zlow));
 */
    }

  }


  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_image( &result);
    }
    return result;
}
/*****************************************************************************/
/** 
  @brief
    Do a standard extraction on image.
  @param[in] img
    The data image
  @param[in] err_img
    The error image
  @param[in] qual_img
    The bad pixel image
  @param[out] err_v
    The error vector associate with result
  @param[out] qual_v
    The bad pixel vector associate with result  
  @return
    NEW ALLOCATED Extracted positions vector
*/
/*****************************************************************************/

static cpl_vector* xsh_image_extract_standard( cpl_image* img, 
  cpl_image *err_img, cpl_image *qual_img, 
  cpl_vector **err_v, cpl_vector **qual_v)
{
  cpl_vector *result = NULL;
  int nx, ny, i;
  double *data = NULL;
  double *err = NULL;
  int *qual = NULL;
  double* pres=NULL;
  double* perr=NULL;
  double* pqua=NULL;


  XSH_ASSURE_NOT_NULL( img);
  XSH_ASSURE_NOT_NULL( err_img);
  XSH_ASSURE_NOT_NULL( qual_img);
  XSH_ASSURE_NOT_NULL( err_v);
  XSH_ASSURE_NOT_NULL( qual_v);

  /* get data */
  check (nx = cpl_image_get_size_x( img));
  check (ny = cpl_image_get_size_y( img));
  check( data = cpl_image_get_data_double( img));
  check( err = cpl_image_get_data_double( err_img));
  check( qual = cpl_image_get_data_int( qual_img));

  /* allocate memory */ 
  check( result = cpl_vector_new( nx));
  check( *err_v = cpl_vector_new( nx));
  check( *qual_v = cpl_vector_new( nx)); 
  pres=cpl_vector_get_data(result);
  perr=cpl_vector_get_data(*err_v);
  pqua=cpl_vector_get_data(*qual_v);
  /* standard extraction */
  for( i=0; i< nx; i++){
    int j;
    double pix_val = 0.0, err_val = 0.0;    
    int qual_val = 0;

    for( j=0; j< ny; j++){
      int pix=j*nx+i;
      pix_val += data[pix];
      err_val += err[pix]*err[pix];
      qual_val |= qual[pix];
    }

    pres[i]=pix_val;
    perr[i]=sqrt(err_val);
    pqua[i]=(double)qual_val;

  }

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_vector( &result);
      xsh_free_vector( err_v);
      xsh_free_vector( qual_v);
    }
    return result;
}
/*****************************************************************************/

/*****************************************************************************/
/**
  @brief
    Divide an image by a 1D spectrum (same scale)
  @param image
    The data image to divide
  @param err_img
    The error image associate with data
  @param s1D
    The 1d spectrum
  @param err_s1D
    The 1d error spectrum
  @param[out] err_2dby1D_img
    The pointer on NEW ALLOCATED error associate with result
  @return
    The NEW ALLOCATED divided image
*/
/*****************************************************************************/
static cpl_image* xsh_image_divide_1D( cpl_image *image, cpl_image *err_img, 
  cpl_vector *s1D, cpl_vector *err_s1D, cpl_image **err_2dby1D_img)
{
  cpl_image *result = NULL;
  int i,j, nx, ny, size;
  double *data = NULL;
  double *err = NULL;
  double *data_res= NULL;
  double *err_res = NULL;

  /* Check parameters */
  XSH_ASSURE_NOT_NULL( image);
  XSH_ASSURE_NOT_NULL( err_img);
  XSH_ASSURE_NOT_NULL( s1D);
  XSH_ASSURE_NOT_NULL( err_s1D);
  XSH_ASSURE_NOT_NULL( err_2dby1D_img);
  
  check( size = cpl_vector_get_size( s1D));
  check( nx = cpl_image_get_size_x( image));
  check( ny = cpl_image_get_size_y( image));
  XSH_ASSURE_NOT_ILLEGAL( size == nx);

  check( data = cpl_image_get_data_double( image));
  check( err = cpl_image_get_data_double( err_img));
  check( result = cpl_image_new( nx, ny, CPL_TYPE_DOUBLE));
  check( *err_2dby1D_img = cpl_image_new( nx, ny, CPL_TYPE_DOUBLE));
  check( data_res = cpl_image_get_data_double( result));
  check( err_res = cpl_image_get_data_double( *err_2dby1D_img));

  for(i=0; i< nx; i++){
    double d, d2, e2, d1, e1;

    check( d2 = cpl_vector_get( s1D, i));
    check( e2 = cpl_vector_get( err_s1D, i));

    if ( d2 == 0.0){
      if (i > 0 && i < (nx-1)){
        d2 = 0.5*(cpl_vector_get( s1D, i-1)+cpl_vector_get( s1D, i+1));
        e2 = 0.5*sqrt(cpl_vector_get( err_s1D, i-1)+cpl_vector_get( err_s1D, i+1));
      }
    }
    for( j=0; j <ny; j++){
      int pix=i+j*nx;
      d1 = data[pix];
      e1 = err[pix];
      d = d1/d2;

      data_res[pix] = d;
      err_res[pix] = d * sqrt( (e1*e1)/(d1*d1) + (e2*e2)/(d2*d2) );
    }
  }
 
  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_image( &result);
      xsh_free_image( err_2dby1D_img);
    }
    return result;
}
/*****************************************************************************/


/*****************************************************************************/
/** 
  @brief
    Do an optimal extraction on image
  @param img
    The image
  @param errs_img
    The variance image
  @param qual_img
    The bad pixels image
  @param opt_par
    The optimal extraction parameters
  @param model_img
    The reference model image
    @param decode_bp
    bad pixel code
  @param[out] corr_img 
    The corrected image
  @param[out] err_v 
    The error vector associate with result
  @param[out] qual_v 
    The bad pixel vector associate with result
  @return
    NEW ALLOCATED Extracted positions vector
*/
/*****************************************************************************/

static cpl_vector* xsh_image_extract_optimal( cpl_image* img, 
  cpl_image *errs_img, cpl_image *qual_img, xsh_opt_extract_param* opt_par,
  cpl_image *model_img,const int decode_bp,
  cpl_image** corr_img, cpl_vector **err_v, cpl_vector **qual_v)
{
  int nb_rejected =-1, iter=1, clip_niter = 1, nbtotal_rejected=0;
  double clip_kappa = 3, clip_frac=0, clip_frac_rej =0.4;
  int *rejected = NULL;
  double *ab = NULL;
  cpl_vector *median_vect = NULL;
 
  cpl_vector *result = NULL;
  int nx, ny, i;
  double *data = NULL;
  double *errs = NULL;
  int *qual_data = NULL;
  cpl_image *corr_image = NULL;
  double *corr_data = NULL;
  double *model_data = NULL;
  

  XSH_ASSURE_NOT_NULL( img);
  XSH_ASSURE_NOT_NULL( errs_img);
  XSH_ASSURE_NOT_NULL( qual_img);
  XSH_ASSURE_NOT_NULL( model_img);
  XSH_ASSURE_NOT_NULL( corr_img);  
  XSH_ASSURE_NOT_NULL( err_v);
  XSH_ASSURE_NOT_NULL( qual_v);
  XSH_ASSURE_NOT_NULL( opt_par);

  /* get parameters */
  clip_kappa = opt_par->clip_kappa;
  clip_niter = opt_par->clip_niter;
  clip_frac = opt_par->clip_frac;

  /* get data */
  check (nx = cpl_image_get_size_x( img));
  check (ny = cpl_image_get_size_y( img));
  check( data = cpl_image_get_data_double( img));
  check( errs = cpl_image_get_data_double( errs_img));
  check( qual_data = cpl_image_get_data_int( qual_img));

  check( corr_image = cpl_image_new( nx, ny, CPL_TYPE_DOUBLE));
  check( corr_data = cpl_image_get_data_double( corr_image));
  check( model_data = cpl_image_get_data_double( model_img));

  /* allocate memory */ 
  check( result = cpl_vector_new( nx));
  check( *err_v = cpl_vector_new( nx)); 
  check( *qual_v = cpl_vector_new( nx));

  XSH_CALLOC( rejected, int, ny);
  XSH_CALLOC( ab, double, nx);

  /* optimal extraction */
  for( i=0; i< nx; i++){
    int j;
    double pix_val = 0.0;
    double f_err = 0.0;
    double f_num = 0.0;
    double f_denom = 0.0;
    double sum_MP = 0.0;
    //double f_denom_mod = 0.0;
    double err_val=0.0;
    int qual_val = 0;
    //double s2dby1d_variance, crh_threshold;
    int count = 0;
    double med_flux;
    double sum, sum_err;
    int sum_qual = 0;

    for( j=0; j< ny; j++){
      rejected[j] = 1;
    }

    /* First detect bad points */

    count = 0;
    nbtotal_rejected=0;
    sum = 0;
    sum_err = 0;

    for( j=0; j< ny; j++){
      int qual;
      double flux = 0.0, err_val = 0.0;
      double model_flux = 0.0;     
 
      flux = data[i+j*nx];
      err_val = errs[i+j*nx];
      qual = qual_data[i+j*nx];
      model_flux = model_data[i+j*nx];
  
      if ( ((qual & decode_bp ) == 0) && model_flux != 0){
        /* good pix */
        ab[count] = flux/model_flux;
        count++;
        qual_val |= qual;
      }
      else{
        rejected[j] =0;
        nbtotal_rejected++;
        sum += flux;
        sum_err += err_val*err_val;
        sum_qual |= qual;
      }
    }
    clip_frac = nbtotal_rejected/(double)ny;
    xsh_msg_dbg_medium("Bad pixel fraction %f for %d", clip_frac, i);
 
    if (clip_frac < 1){
      check( median_vect = cpl_vector_wrap( count, ab)); 
      check( med_flux = cpl_vector_get_median( median_vect));
      xsh_unwrap_vector( &median_vect);
    }
    else{
      med_flux = sum;
      sum_err = sqrt( sum_err);
    }

    iter=1;
    nb_rejected =-1;

    /* sigma clipping */
    while( iter <= clip_niter && nb_rejected != 0 && count != 0 
      && clip_frac <= clip_frac_rej){
      nb_rejected = 0;
      int nbdiff =0;
      double sigma;

      xsh_msg_dbg_medium("Sigma clipping : Iteration %d/%d", iter, clip_niter);

      /* compute sigma */
      for( j=0; j< ny; j++){
        double flux = 0.0;
        double model_flux = 0.0;
        double diff = 0.0;      

        model_flux = model_data[i+j*nx];
        flux = data[i+j*nx];

        /* Good pixel only */
        if ( rejected[j] == 1){
          diff = flux-med_flux*model_flux;
          ab[nbdiff] =  diff;
          nbdiff++;
        }
      }
      check( median_vect = cpl_vector_wrap( nbdiff, ab));
      check( sigma = cpl_vector_get_stdev( median_vect));
      xsh_unwrap_vector( &median_vect);
      xsh_msg_dbg_medium( "Sigma %f", sigma);

      /* rejected point */
      for( j=0; j< ny; j++){
        double flux = 0.0;
        double model_flux = 0.0;
        double diff = 0.0;

        model_flux = model_data[i+j*nx];
        flux = data[i+j*nx];
        diff = flux-med_flux*model_flux;

        if ( rejected[j] == 1 && fabs( diff) > clip_kappa*sigma){
          nb_rejected++;
          rejected[j] = 0;
          qual_val = QFLAG_COSMIC_RAY_REMOVED;
        }
      }
      nbtotal_rejected += nb_rejected;
      /* recompute median */
      count = 0;

      for( j=0; j< ny; j++){
        double flux = 0.0;
        double model_flux = 0.0;

        flux = data[i+j*nx];
        model_flux = model_data[i+j*nx];

        if ( rejected[j] == 1 && model_flux != 0){
          ab[count] = flux/model_flux;
          count++;
        }
      }
      if ( count != 0){
        check( median_vect = cpl_vector_wrap( count, ab));
        check( med_flux = cpl_vector_get_median( median_vect));
        xsh_unwrap_vector( &median_vect);
      }
      clip_frac = nbtotal_rejected/(double)ny;
      iter++;
    }
    xsh_msg_dbg_medium("Fraction rejected %f", clip_frac);

    /* correct bad points */
    for( j=0; j< ny; j++){
      int qual;
      //double flux = 0.0;
      qual = qual_data[i+j*nx];
      if ( ( (qual & decode_bp) == 0 ) && rejected[j] == 1 ){
        corr_data[i+j*nx] = data[i+j*nx];
      }
      else{
        corr_data[i+j*nx] = 0;
      }
    } 

    /* optimal extraction */
    if ( clip_frac < 1){
      for( j=0; j< ny; j++){
        double p, p2;
        double variance;

        p = model_data[i+j*nx]; 
        p2 = p*p;
        variance = errs[i+j*nx]*errs[i+j*nx];
        //      xsh_msg(" Rejected %d %f %f %f\n", rejected[j], f_num, f_denom,corr_data[i+j*nx]);
        sum_MP += rejected[j]*p;
        //f_err += rejected[j]*p2;
        f_err += rejected[j]*p/variance;
        //xsh_msg("f_err %f", f_err);
        f_num +=  (double)rejected[j]*p*corr_data[i+j*nx]/variance;
        f_denom += (double)rejected[j]*p2/variance;
      }
      pix_val = f_num / f_denom;
      err_val = sqrt( sum_MP / f_denom);
    }
    else{
      pix_val = sum;
      err_val = sum_err;
    }  
    check( cpl_vector_set( result, i, pix_val));
    check( cpl_vector_set( *err_v, i, err_val));
    check( cpl_vector_set( *qual_v, i, (double)qual_val));
  }


#if REGDEBUG_N
    if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM) {
      char std_spectrum_name[256];
      FILE* std_spectrum_file = NULL;

      sprintf( std_spectrum_name, "n.dat");
      std_spectrum_file = fopen( std_spectrum_name, "w");

      fprintf( std_spectrum_file, "#index n\n");

      for(i=0; i< nx; i++){
        fprintf(std_spectrum_file, "%d %f\n", i, n[i]);
      }
      fclose( std_spectrum_file);

      sprintf( std_spectrum_name, "rejfrac.dat");
      std_spectrum_file = fopen( std_spectrum_name, "w");

      fprintf( std_spectrum_file, "#index rejfrac\n");

      for(i=0; i< nx; i++){
        fprintf(std_spectrum_file, "%d %f\n", i, rej[i]);
      }
      fclose( std_spectrum_file);
    }
#endif
  *corr_img = corr_image;

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_vector( &result);
      xsh_free_vector( err_v);
      xsh_free_vector( qual_v);
    }
    XSH_FREE( rejected);
    XSH_FREE( ab);
    return result;
}

/*****************************************************************************/
/** 
  @brief
    Interpolate values following given positions
  @param oversample
    Oversample factor
  @param abs_order
    Absolute order
  @param ref_pos
    The reference position orderby increasing values
  @param step
    The step in wavelength
  @param[in] ref_values
    The reference values
  @param[in] err_values
    The error values
  @param[in] qual_values
    The bad pixel values  
  @param[in] new_pos
    The search positions orderby increasing values
  @param[out] spectrum_err
    The error valuea associate with result
  @param[in] spectrum_qual
    The bad pixel values associate with result
  @return
    NEW ALLOCATED vector containing values for new positions
*/
/*****************************************************************************/

static cpl_vector* xsh_vector_integrate( int biny, 
  int oversample, int absorder,
  cpl_vector *ref_pos, double step,
  cpl_vector *ref_values,  cpl_vector *err_values, cpl_vector *qual_values,
  cpl_vector *new_pos,
  cpl_vector **spectrum_err, cpl_vector **spectrum_qual)
{

  int new_pos_size, ref_pos_size, pixel_pos_size;
  int i, j;
  cpl_vector *result=NULL;
  cpl_vector *pixel_pos_vect = NULL, *pixel_size_vect = NULL;
  cpl_polynomial *pixel_size_poly = NULL;
  double hstep;
  double lambda_bin_factor =0.0;

  XSH_ASSURE_NOT_NULL( ref_pos);
  XSH_ASSURE_NOT_NULL( ref_values);
  XSH_ASSURE_NOT_NULL( err_values);
  XSH_ASSURE_NOT_NULL( qual_values);
  XSH_ASSURE_NOT_NULL( new_pos);
  XSH_ASSURE_NOT_NULL( spectrum_err);
  XSH_ASSURE_NOT_NULL( spectrum_qual);

  check( new_pos_size = cpl_vector_get_size( new_pos));
  check( ref_pos_size = cpl_vector_get_size( ref_pos));
  check( result = cpl_vector_new( new_pos_size));
  
  check( *spectrum_err  = cpl_vector_new( new_pos_size));
  check( *spectrum_qual  = cpl_vector_new( new_pos_size));

  /* normalize by pixels size */
  pixel_pos_size = ref_pos_size-2*oversample;
  check( pixel_pos_vect = cpl_vector_new( pixel_pos_size));
  check( pixel_size_vect = cpl_vector_new( pixel_pos_size));

  for( j=oversample; j< ref_pos_size-oversample; j++){
    double xA, xB, xC;
    double sizeAB, sizeBC, size;

    check( xA = cpl_vector_get( ref_pos, j-oversample));
    check( xB = cpl_vector_get( ref_pos, j));
    check( xC = cpl_vector_get( ref_pos, j+oversample));
    sizeAB = xB-xA;
    sizeBC = xC-xB;
    size = (sizeAB+sizeBC)/2.0;
    check( cpl_vector_set( pixel_pos_vect, j-oversample, xB));
    check( cpl_vector_set( pixel_size_vect, j-oversample, size));
  }

  check( pixel_size_poly = xsh_polynomial_fit_1d_create( pixel_pos_vect,
    pixel_size_vect, 1, NULL));

  j =1;
  hstep = step/2.0;

  for( i=0; i< new_pos_size; i++){
    double xA=0, xB=0, xC=0;
    double deltaA1=0, deltaA2=0;

    //double y_min, y_max;
    double size_min=0, pos_min=0, frac_min=0, sigy_min=0, flux_min=0;
    double size_max=0, pos_max=0, frac_max=0, sigy_max=0, flux_max=0;

    double sig_min=0, sig_max=0;
    int qual_min=0, qual_max=0, qual=0;
    //double yA, yB, sig_yA, sig_yB;

    double y=0, ymin=0, ymax=0;
    //double border_xmin, border_xmax, delta_xmin, delta_xmax;
    double xmin=0, xmax=0;
    /* TODO: why sig_y has been set to 26? */
    double x=0, sig_y = 26;
    int j_start=0, j_end=0, k=0;
    //double A, B, min_cont, max_cont;

    double flux=0, err=0, pixel_size_comp=0;
    double nb_x=0;

    check( x= cpl_vector_get( new_pos, i));
    xmin = x-hstep;
    xmax = x+hstep;
    check( xA = cpl_vector_get( ref_pos, j-1));
    check( xB = cpl_vector_get( ref_pos, j));
    deltaA2 = (xB-xA)/2.0;

    if ( j>1){
      check( xC = cpl_vector_get( ref_pos, j-2));
      deltaA1 = (xA-xC)/2.0;
    }
    else{
      deltaA1 = deltaA2;
    }

    while( !(xmin >= (xA-deltaA1) && xmin <= (xA+deltaA2)) ){
      j++;
      check( xA = cpl_vector_get( ref_pos, j-1));
      check( xB = cpl_vector_get( ref_pos, j));
      check( xC = cpl_vector_get( ref_pos, j-2));
      deltaA1 = (xA-xC)/2.0;
      deltaA2 = (xB-xA)/2.0;
    }

    j_start = j-1;
    size_min = deltaA1+deltaA2;
    pos_min = xA+deltaA2;
    frac_min = (pos_min-xmin)/size_min;
    check( ymin = cpl_vector_get( ref_values, j_start));
    check( sigy_min = cpl_vector_get( err_values, j_start));
    check( qual_min = cpl_vector_get( qual_values, j_start));

    flux_min = frac_min*ymin;
    sig_min = frac_min*frac_min*sigy_min*sigy_min;

    while( !(xmax >= (xA-deltaA1) && xmax <= (xA+deltaA2)) ){
      j++;
      check( xA = cpl_vector_get( ref_pos, j-1));
      check( xC = cpl_vector_get( ref_pos, j-2));

      deltaA1 = (xA-xC)/2.0;

      if ( j < ref_pos_size){
        check( xB = cpl_vector_get( ref_pos, j));
        deltaA2 = (xB-xA)/2.0;
      }
      else{
        deltaA2 = deltaA1;
      }        
    }

    j_end = j-1;
    j = j_end;

    if ( j_start == j_end){
      flux_min =0;
      frac_min = 0;
      frac_max = (xmax-xmin)/size_min;
    }
    else{
      size_max = deltaA1+deltaA2;
      pos_max = xA-deltaA1;
      frac_max = (xmax-pos_max)/size_max;
    }
    check( ymax = cpl_vector_get( ref_values, j_end));
    check( sigy_max = cpl_vector_get( err_values, j_end));
    check( qual_max = cpl_vector_get( qual_values, j_end));

    flux_max = frac_max*ymax;
    sig_max = frac_max*frac_max*sigy_max*sigy_max;

    xsh_msg_dbg_high( "xmin %f pos_min %f diff %f size %f frac*size %f", xmin , pos_min, pos_min-xmin, size_min, frac_min*size_min);
    xsh_msg_dbg_high( "xmax %f pos_max %f diff %f size %f frac*size %f", xmax , pos_max, xmax-pos_max, size_max, frac_max*size_max);

    nb_x = j_end-j_start-1;
    if ( nb_x > 0){
      nb_x += frac_min+frac_max;
    }
    else{
      nb_x = frac_min+frac_max;;
    }
   
    qual = qual_min;
    qual |= qual_max;
    flux = flux_min+flux_max;
    err = sig_min+sig_max;
 
    xsh_msg_dbg_high("nb_x %f size %f %f cont_min %f max_cont %f", nb_x, size_min, size_max, frac_min, frac_max);

    for( k=j_start+1; k <= j_end-1; k++){
      check( y = cpl_vector_get( ref_values, k));
      check( sig_y = cpl_vector_get( err_values, k));
      flux +=y;
      err += sig_y*sig_y;
      qual |= (int)xsh_round_double(cpl_vector_get( qual_values, k));
    }
    err = sqrt( err);

    /* correct pixel size conversion */ 
    check( pixel_size_comp = cpl_polynomial_eval_1d( pixel_size_poly, x, NULL));

    lambda_bin_factor = 1.0/(step*biny);

    flux *= pixel_size_comp*lambda_bin_factor;
    err *= pixel_size_comp*lambda_bin_factor;

    check( cpl_vector_set( result, i, flux));
    check( cpl_vector_set( *spectrum_err, i, err));
    check( cpl_vector_set( *spectrum_qual, i, qual));
  }

#if REGDEBUG_PIXELSIZE
  if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM) {
    FILE* debug_file = NULL;
    char debug_name[256];

    sprintf( debug_name, "pixel_size_%d.dat", absorder);
    xsh_msg("Create %s", debug_name);

    debug_file = fopen( debug_name, "w");

    fprintf( debug_file,"# wavelength size fit\n");

    for( j=0; j< pixel_pos_size; j++){
      double pos, size, pol;

      check( pos = cpl_vector_get( pixel_pos_vect, j));
      check( size = cpl_vector_get( pixel_size_vect, j));
      check( pol = cpl_polynomial_eval_1d( pixel_size_poly, pos, NULL)); 
      fprintf( debug_file ,"%f %f %f\n", pos, size, pol);
    }
    fclose( debug_file);
  }
#endif

#if REGDEBUG_INTEGRATE
  if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM) {
    FILE* debug_file = NULL;
    char debug_name[256];

    sprintf( debug_name, "integrate_flux_%d.dat", absorder);
    xsh_msg("Create %s", debug_name);

    debug_file = fopen( debug_name, "w");

    fprintf( debug_file,"# wavelength flux err qual\n");

    for( j=0; j< new_pos_size; j++){
      double xA, yA, sig_yA, qual_yA;

      check( xA = cpl_vector_get( new_pos, j));
      check( yA = cpl_vector_get( result, j));
      check( sig_yA = cpl_vector_get( *spectrum_err, j));
      check( qual_yA = cpl_vector_get( *spectrum_qual, j));
      fprintf( debug_file ,"%f %f %f %f\n", xA, yA, sig_yA, qual_yA);
    }

    fclose( debug_file);
  }
#endif
/* ENDREGDEBUG */

  cleanup:
    xsh_free_vector( &pixel_pos_vect);
    xsh_free_vector( &pixel_size_vect);
    xsh_free_polynomial( &pixel_size_poly);
    if( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_vector( &result);
      xsh_free_vector( spectrum_err);
      xsh_free_vector( spectrum_qual);
    }
    return result;
}
/*****************************************************************************/


/*****************************************************************************/
/** 
  @brief
    Create a SPECTRUM 1D from a Science frame
  @param sci_frame 
    The science frame
  @param orderlist_frame
    The order table Frame
  @param wavesol_frame
    The wave solution frame
  @param model_frame
    The model configuration frame
  @param wavemap_frame
    The wave map frame
  @param slitmap_frame
    The slit map frame
  @param loc_frame
    The localization frame
  @param spectralformat_frame
    The spectral format frame
  @param masterflat_frame
    The master flat frame
  @param instrument
    The instrument structure
  @param opt_extract_par
    Parameters for optimal extraction
  @param rec_prefix
    The recipe PREFIX to build output files name or NULL
  @param[out] orderext1d_frame
    The orderext1d result
  @param[out] orderoxt1d_frame
    The orderoxt1d result
  @param[out] qc_subextract_frame
    The QC subextract image
  @param[out] qc_s2ddiv1d_frame
    The QC S2ddiv1d image
  @param[out] qc_model_frame
    The QC model image
  @param[out] qc_weight_frame
    The QC weight image
*/
/*****************************************************************************/
void xsh_opt_extract(cpl_frame *sci_frame, cpl_frame *orderlist_frame,
    cpl_frame *wavesol_frame, cpl_frame *model_frame, cpl_frame *wavemap_frame,
    cpl_frame *slitmap_frame, cpl_frame *loc_frame,
    cpl_frame *spectralformat_frame, cpl_frame *masterflat_frame,
    xsh_instrument *instrument, xsh_opt_extract_param* opt_extract_par,
    const char* rec_prefix, cpl_frame** orderext1d_frame,
    cpl_frame** orderoxt1d_frame, cpl_frame** orderoxt1d_eso_frame,
    cpl_frame** qc_subextract_frame, cpl_frame** qc_s2ddiv1d_frame,
    cpl_frame** qc_model_frame, cpl_frame** qc_weight_frame)

{

  check(
      xsh_opt_extract_orders( sci_frame, orderlist_frame, wavesol_frame,
                             model_frame, wavemap_frame, slitmap_frame, loc_frame,spectralformat_frame,
                             masterflat_frame, instrument, opt_extract_par, 0, 100, rec_prefix,
                             orderext1d_frame, orderoxt1d_frame, orderoxt1d_eso_frame, qc_subextract_frame,
                             qc_s2ddiv1d_frame, qc_model_frame, qc_weight_frame));

  cleanup: return;
}

/*****************************************************************************/
/** 
  @brief
    Create a SPECTRUM 1D from a Science frame
  @param sci_frame 
    The science frame
  @param orderlist_frame
    The order table Frame
  @param wavesol_frame
    The wave solution frame
  @param model_frame
    The model configuration frame
  @param[in] wavemap_frame
    The wavemap frame
  @param slitmap_frame
    The slit map frame
  @param[in] spectralformat_frame
    The spectral format frame
  @param[in] masterflat_frame
    The master flat frame
  @param[in] instrument
    The instrument structure
  @param opt_extract_par
    Parameters for optimal extraction
  @param min_index
    The index of first order to extract
  @param max_index
    The index of last order to extract  
  @param rec_prefix
    The recipe PREFIX to build output files name or NULL
  @param[out] orderext1d_frame
    The orderext1d result
  @param[out] orderoxt1d_frame
    The orderoxt1d result
  @param[out] qc_subextract_frame
    The QC subextract image
  @param[out] qc_s2ddiv1d_frame
    The QC S2ddiv1d image
  @param[out] qc_model_frame
    The QC model image
  @param[out] qc_weight_frame
    The QC weight image
*/
/*****************************************************************************/
void 
xsh_opt_extract_orders( cpl_frame *sci_frame,
			cpl_frame *orderlist_frame,
			cpl_frame *wavesol_frame,
			cpl_frame *model_frame,
			cpl_frame *wavemap_frame,
			cpl_frame *slitmap_frame,
			cpl_frame *loc_frame,
			cpl_frame *spectralformat_frame,
			cpl_frame *masterflat_frame, 
			xsh_instrument *instrument,
			xsh_opt_extract_param* opt_extract_par,
			int min_index, int max_index,
			const char* rec_prefix,
			cpl_frame** orderext1d_frame,
			cpl_frame** orderoxt1d_frame,
                        cpl_frame** res_frame_ext,
			cpl_frame** qc_subextract_frame,
			cpl_frame** qc_s2ddiv1d_frame,
			cpl_frame** qc_model_frame,
			cpl_frame** qc_weight_frame)
{
  xsh_pre *sci_pre = NULL;
  xsh_order_list *order_list = NULL;
  cpl_image *blaze_img = NULL;
  float *sci_pre_data = NULL;
  float *sci_pre_errs = NULL;
  int *sci_pre_qual = NULL;
  xsh_wavesol *wavesol = NULL;
  xsh_spectralformat_list *spectralformat_list= NULL;

  int iorder, i;
  double *slits_cen = NULL;
  double *lambdas = NULL;
  double *pos_cen_x = NULL;
  double *pos_cen_y = NULL;
  int nlambdas = 0;
  cpl_vector *extract_lambda_vect = NULL;
  cpl_vector *result_lambda_vect = NULL;
  int result_lambda_vect_size;

  cpl_vector *div_flux_vect = NULL;
  cpl_vector *div_err_vect = NULL;

  cpl_vector *std_flux_vect = NULL;
  cpl_vector *std_err_vect = NULL;
  cpl_vector *std_qual_vect = NULL;

  cpl_vector *opt_flux_vect = NULL;
  cpl_vector *opt_err_vect = NULL;
  cpl_vector *opt_qual_vect = NULL;

  cpl_image *extract_img = NULL;
  cpl_image *extract_errs_img = NULL;
  cpl_image *extract_qual_img = NULL;

  cpl_image *x_img = NULL;
  cpl_image *y_img = NULL;

  cpl_image *sub_extract_img = NULL;
  cpl_image *sub_extract_errs_img = NULL;
  cpl_image *sub_extract_qual_img = NULL;

  cpl_image *sub_x_img = NULL;
  cpl_image *sub_y_img = NULL;

  cpl_image *s2Dby1D_img = NULL;
  cpl_image *s2Dby1D_err_img = NULL;
  cpl_image *weight_img = NULL;
  double *extract_data = NULL;
  double *extract_errs = NULL;
  int *extract_qual = NULL;
  double *extract_x_data = NULL;
  double *extract_y_data = NULL;

  int nx_extract, ny_extract;
  int box_hsize;
  int niter=1, iiter;
  int chunk_ovsamp_size;
  int extract_slit_hsize=0;
  int deg_poly = 3;
  cpl_vector* std_extract_vect = NULL;
  cpl_vector* std_err_extract_vect = NULL;
  cpl_vector* std_qual_extract_vect = NULL;

  cpl_vector *opt_extract_vect = NULL;
  cpl_vector *opt_err_extract_vect = NULL;
  cpl_vector *opt_qual_extract_vect = NULL;

  double lambda_step=0.0;
  xsh_rec_list *orderext1d = NULL;
  xsh_rec_list *orderoxt1d = NULL;
  char tag_drl[256];
  char tag[256];

  char filename[256];
  char filename_drl[256];

  cpl_image *model_img = NULL;
  int rec_min_index, rec_max_index;
  double slit_down, slit_up;
  double lambda_min, lambda_max;
  int blaze_shift;
  xsh_xs_3 config_model;
  double conad = 0.0;
  double square_oversample;
  //int binx;
  int biny;

  /* qc products */
  char qc_extname[256];
  cpl_propertylist *qc_header = NULL;
  char qc_subextract_name[256];
  char qc_s2ddiv1d_name[256];
  char qc_model_name[256];
  char qc_weight_name[256];  
  int mode = CPL_IO_DEFAULT;
  

  int decode_bp=instrument->decode_bp;
  /* Checking input parameters */
  XSH_ASSURE_NOT_NULL( sci_frame);
  XSH_ASSURE_NOT_NULL( orderlist_frame);
  XSH_ASSURE_NOT_NULL( wavemap_frame);
  XSH_ASSURE_NOT_NULL( slitmap_frame);
  XSH_ASSURE_NOT_NULL( loc_frame);
  XSH_ASSURE_NOT_NULL( spectralformat_frame);
  XSH_ASSURE_NOT_NULL( masterflat_frame);
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( opt_extract_par);
  XSH_ASSURE_NOT_NULL( orderext1d_frame);
  XSH_ASSURE_NOT_NULL( orderoxt1d_frame);

  /* Loading data and initialize binning*/
  check( sci_pre = xsh_pre_load( sci_frame, instrument));

  /* Square oversample */
  square_oversample = opt_extract_par->oversample*opt_extract_par->oversample;
  /* ADU to e- */
  conad = sci_pre->conad;

  //binx = instrument->binx;
  biny = instrument->biny;
  if (model_frame == NULL){
    XSH_ASSURE_NOT_NULL( wavesol_frame);
    check( wavesol = xsh_wavesol_load( wavesol_frame, instrument));
  }
  else{
    check( xsh_model_config_load_best( model_frame, &config_model));
    check( xsh_model_binxy( &config_model, instrument->binx, instrument->biny));
  }

  lambda_step = opt_extract_par->lambda_step;
  niter = opt_extract_par->niter;
  /* Loading data */
  check( sci_pre_data = cpl_image_get_data_float( sci_pre->data));
  check( sci_pre_errs = cpl_image_get_data_float( sci_pre->errs));
  check( sci_pre_qual = cpl_image_get_data_int( sci_pre->qual));

  check( order_list = xsh_order_list_load( orderlist_frame, instrument));

  check( spectralformat_list = xsh_spectralformat_list_load(
    spectralformat_frame, instrument));

  nx_extract = sci_pre->ny*opt_extract_par->oversample;
  ny_extract = (OPT_EXTRACT_SLIT_SIZE * opt_extract_par->oversample)+1;

  XSH_MALLOC( lambdas, double, nx_extract);
  XSH_MALLOC( slits_cen, double, nx_extract);
  XSH_MALLOC( pos_cen_x, double, nx_extract);
  XSH_MALLOC( pos_cen_y, double, nx_extract);
  XSH_MALLOC( extract_data, double, nx_extract*ny_extract);
  XSH_MALLOC( extract_errs, double, nx_extract*ny_extract);
  XSH_MALLOC( extract_qual, int, nx_extract*ny_extract);

  XSH_MALLOC( extract_x_data, double, nx_extract*ny_extract); 
  XSH_MALLOC( extract_y_data, double, nx_extract*ny_extract);

  chunk_ovsamp_size = opt_extract_par->chunk_size*opt_extract_par->oversample;
  box_hsize = opt_extract_par->box_hsize*opt_extract_par->oversample;

  xsh_msg( "Create and multiply by blaze function");

  xsh_msg_dbg_low("---opt_extract : create blaze");
  check( blaze_img = xsh_create_blaze( masterflat_frame, 
    order_list, instrument));

  xsh_msg_dbg_low("---opt_extract : multiply by blaze");
  check( xsh_pre_multiply_image( sci_pre, blaze_img));

#if REGDEBUG_BLAZE
  if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM) {
    cpl_frame *norm_frame = NULL;
    
    check( norm_frame = xsh_pre_save( sci_pre,"OPTEXT_MULT_BLAZE.fits",
                                      "OPTEXT_MULT_BLAZE",1));
    xsh_free_frame( &norm_frame);
  }
#endif

  /* Create result */
  check( orderext1d = xsh_rec_list_create( instrument));
  check( orderoxt1d = xsh_rec_list_create( instrument));

  if (min_index >= 0){
    rec_min_index = min_index;
  }
  else{
    rec_min_index = 0;
  }
  if ( max_index < orderext1d->size){
    rec_max_index = max_index;
  }
  else{
    rec_max_index = orderext1d->size-1;
  }

  check( xsh_get_slit_edges( slitmap_frame, &slit_down, &slit_up,
    NULL, NULL, instrument));


  XSH_NEW_PROPERTYLIST( qc_header);
  /* Loop on order */
  for( iorder= rec_min_index; iorder <= rec_max_index; iorder++) {
    int abs_order,start_y, end_y;
    double cos_tilt=0, sin_tilt=0;
    int ny_extract_min=-1, ny_extract_max = -1;
    

    
    abs_order = order_list->list[iorder].absorder;
    check( start_y = xsh_order_list_get_starty( order_list, iorder));
    check( end_y = xsh_order_list_get_endy( order_list, iorder));
    xsh_msg("Extracting order %d", abs_order);
    xsh_msg_dbg_low("---opt_extract : order %d [%d --> %d]", abs_order, start_y, end_y);

    check( xsh_wavemap_lambda_range( wavemap_frame, slitmap_frame, start_y, end_y,
      opt_extract_par->oversample, order_list, iorder,
      pos_cen_x, pos_cen_y, abs_order, spectralformat_list, lambdas, slits_cen, 
      &nlambdas, instrument));

#if REGDEBUG_OPTEXTRACT
/* REGDEBUG */
{
  FILE *test_file;
  char test_name[256];
  float x, y, lambda, slit;
  double x_slit_min=0, y_slit_min=0;
  double x_slit_max=0, y_slit_max=0;
  double x_slit_cen=0, y_slit_cen=0; 
  double diffx, diffy;

  if (model_frame == NULL){
    sprintf( test_name, "poly%dx%d_lambda_range_%d.dat", binx, biny, abs_order);
  }
  else{
    sprintf( test_name, "model%dx%d_lambda_range_%d.dat", binx, biny, abs_order);
  }
  xsh_msg("Write %s", test_name);
  test_file = fopen( test_name, "w");
  fprintf( test_file, "# x y lambda slit slit_min slit_max x_slit_min y_slit_min x_slit_cen y_slit_cen x_slit_max y_slit_max diffx diffy\n"); 

  for(i=0; i< nlambdas; i++){
    x = pos_cen_x[i];
    y = pos_cen_y[i];
    lambda = lambdas[i];
    slit = slits_cen[i];

    if (model_frame == NULL){
      check( x_slit_min = xsh_wavesol_eval_polx( wavesol, lambda,(double) abs_order,
        slit_down));
      check( x_slit_max = xsh_wavesol_eval_polx( wavesol, lambda, (double)abs_order,
        slit_up));
      check( y_slit_min = xsh_wavesol_eval_poly( wavesol, lambda, (double)abs_order,
        slit_down));
      check( y_slit_max = xsh_wavesol_eval_poly( wavesol, lambda, (double)abs_order,
        slit_up));
      check( x_slit_cen = xsh_wavesol_eval_polx( wavesol, lambda,(double) abs_order,
        slit));
      check( y_slit_cen = xsh_wavesol_eval_poly( wavesol, lambda, (double)abs_order,
        slit));
    }
    else{
      check( xsh_model_get_xy( &config_model, instrument, lambda, (double) abs_order,
        slit_down, &x_slit_min, &y_slit_min));
      check( xsh_model_get_xy( &config_model, instrument, lambda, (double) abs_order,
        slit_up, &x_slit_max, &y_slit_max));
      check( xsh_model_get_xy( &config_model, instrument, lambda, (double) abs_order,
            slit, &x_slit_cen, &y_slit_cen));
    }
    diffx = x-x_slit_cen;
    diffy = y-y_slit_cen;

    fprintf( test_file, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f\n", x, y, lambda, slit, slit_down, slit_up,
      x_slit_min, y_slit_min, x_slit_cen, y_slit_cen, x_slit_max, y_slit_max, diffx, diffy);
  }
  fclose( test_file);
}
#endif
/* END OF REGDEBUG */


    xsh_msg_dbg_low("---opt_extract : (%f --> %f) in %d", lambdas[0], lambdas[nlambdas-1], 
      nlambdas);

    xsh_msg_dbg_low("---opt_extract : s (%f --> %f)", slit_down,  slit_up);
 
    for(i=0; i< nlambdas; i++){
      float x, y, lambda, slit;
      double x_slit_min=0, y_slit_min=0;
      double x_slit_max=0, y_slit_max=0;
      float x_min, x_max;
      float y_min, y_max;
      int l;

      x = pos_cen_x[i];
      y = pos_cen_y[i];
      lambda = lambdas[i];
      slit = slits_cen[i];
      xsh_msg_dbg_high(" x y lambda slit => %f %f %f %f", x, y, lambda, slit);

      if ( ( i%opt_extract_par->oversample) == 0){
        xsh_msg_dbg_high("evaluate tilt lambda order %f %f", lambda, (double) abs_order);
        /* compute the tilt */
        if (model_frame == NULL){
          check( x_slit_min = xsh_wavesol_eval_polx( wavesol, lambda,(double) abs_order,
            slit_down));
          check( x_slit_max = xsh_wavesol_eval_polx( wavesol, lambda, (double)abs_order,
            slit_up));
          check( y_slit_min = xsh_wavesol_eval_poly( wavesol, lambda, (double)abs_order,
            slit_down));
          check( y_slit_max = xsh_wavesol_eval_poly( wavesol, lambda, (double)abs_order,
            slit_up));
        }
        else{
          /* evaluate at slit_max slit_min */
          check( xsh_model_get_xy( &config_model, instrument, lambda, (double) abs_order, 
            slit_down, &x_slit_min, &y_slit_min));
          check( xsh_model_get_xy( &config_model, instrument, lambda, (double) abs_order, 
            slit_up, &x_slit_max, &y_slit_max));
        }
        if ( x_slit_min < x_slit_max){
          x_min = x_slit_min;
          x_max = x_slit_max;
        }
        else{
          x_min = x_slit_max;
          x_max = x_slit_min;
        }
        if ( y_slit_min < y_slit_max){
          y_min = y_slit_min;
          y_max = y_slit_max;
        }
        else{
          y_min = y_slit_max;
          y_max = y_slit_min;
        }
        xsh_msg_dbg_high("min(%f,%f) max(%f,%f)", x_min, y_min, x_max, y_max);
        /* Dont check solution in Y */
        if ( x_min >= 1 && x_max <= sci_pre->nx && (x_min <= x) && 
            ( x <= x_max) &&
             y_min >= 1 && y_max <= sci_pre->ny){
          double tilt_x, tilt_y;
          double hypotenuse_tilt;

          if ((y_min >= y) || (y >= y_max)){
            xsh_msg_warning("y out of bounds : %f [%f,%f]", y, y_min,y_max);
          }
          tilt_x = x_slit_max-x_slit_min;
          tilt_y = y_slit_max-y_slit_min;
          xsh_msg_dbg_high("tilt_x %f tilt_y %f", tilt_x, tilt_y);
 
          if ( extract_slit_hsize <= 0){
            extract_slit_hsize = (int)(fabs(tilt_x)/2.0*SLIT_USEFUL_WINDOW_FACTOR)+1;
            ny_extract = extract_slit_hsize*opt_extract_par->oversample*2+1;
            xsh_msg_dbg_high("SLIT hsize %d global %d", extract_slit_hsize,ny_extract);
          }
          hypotenuse_tilt = sqrt( tilt_y*tilt_y+tilt_x*tilt_x);
          cos_tilt = tilt_x/hypotenuse_tilt;
          sin_tilt = tilt_y/hypotenuse_tilt;
        }
      }

      for (l=0; l< ny_extract; l++){
        float xs, fy, fx;
        double flux=-1, err=-1;
        int qual_val=0;

        xs = (float)l/(float)opt_extract_par->oversample-
          extract_slit_hsize;

        /* FITS to C convention */
        fy = xs*sin_tilt+y-1;
        
        if ( (fy >= 0) && (fy < sci_pre->ny) ){
          int pixel=i+l*nlambdas;
          /* FITS to C convention */
          fx = xs*cos_tilt+x-1;

          check(xsh_interpolate_linear( sci_pre_data, sci_pre_errs, sci_pre_qual, sci_pre->nx, 
            sci_pre->ny, fx, fy, &flux, &err, &qual_val, FLUX_MODE));

          extract_x_data[pixel] = fx;
          extract_y_data[pixel] = fy;

          /* Flux in e- and divided by the factor square oversample */
          flux *=  conad/square_oversample;
          extract_data[pixel] = flux;
          extract_errs[pixel] = err * conad /
            opt_extract_par->oversample;
          extract_qual[pixel] = qual_val;
        }
        else{
          break;
        }
      }
    }/* end extraction */

    /* SLIT approximate position */
    
    check( xsh_object_localize( slitmap_frame, loc_frame, opt_extract_par->oversample,
      box_hsize, nlambdas, 
      ny_extract, extract_x_data,
      extract_y_data, &ny_extract_min, &ny_extract_max));
    
    check( extract_img = cpl_image_wrap_double( nlambdas, 
      ny_extract, extract_data));
    check( extract_errs_img = cpl_image_wrap_double( nlambdas,
      ny_extract, extract_errs));
    check( extract_qual_img = cpl_image_wrap_int( nlambdas,
      ny_extract, extract_qual));

#if REGDEBUG_EXTRACT
    { char name[256];
    sprintf( name, "extract_%d.fits", abs_order);
    cpl_image_save( extract_img, name, CPL_BPP_IEEE_DOUBLE, 
      NULL,CPL_IO_DEFAULT);
    xsh_msg("EXTRACT save %s",name);
    sprintf( name, "extract_errs_%d.fits", abs_order);
    cpl_image_save( extract_errs_img, name, CPL_BPP_IEEE_DOUBLE, 
      NULL,CPL_IO_DEFAULT);
    sprintf( name, "extract_qual_%d.fits", abs_order);
    cpl_image_save( extract_qual_img, name, XSH_PRE_QUAL_BPP,
      NULL,CPL_IO_DEFAULT);
    }
#endif


    check( x_img = cpl_image_wrap_double( nlambdas,
      ny_extract, extract_x_data));
    check( y_img = cpl_image_wrap_double( nlambdas,
      ny_extract, extract_y_data));

#if REGDEBUG_EXTRACT_XY
    { char name[256];

      sprintf( name, "extract_x_%d.fits", abs_order);
      xsh_msg("EXTRACT_XY save %s",name);
      cpl_image_save( x_img, name, CPL_BPP_IEEE_DOUBLE, NULL,CPL_IO_DEFAULT);
      sprintf( name, "extract_y_%d.fits", abs_order);
      xsh_msg("EXTRACT_XY save %s",name);
      cpl_image_save( y_img, name, CPL_BPP_IEEE_DOUBLE, NULL,CPL_IO_DEFAULT);
    }
#endif


    check( sub_extract_img = cpl_image_extract( extract_img, 1,
      ny_extract_min+1, nlambdas, ny_extract_max+1));
    check( sub_extract_errs_img = cpl_image_extract( extract_errs_img, 1,
      ny_extract_min+1, nlambdas, ny_extract_max+1));
    check( sub_extract_qual_img = cpl_image_extract( extract_qual_img, 1,
      ny_extract_min+1, nlambdas, ny_extract_max+1));

    
    /* Write QC SUB EXTRACT PROD */
    sprintf( qc_subextract_name, "sub_extract.fits");
      
    sprintf( qc_extname, "ORD%d_FLUX", abs_order);
    check( xsh_pfits_set_extname ( qc_header, qc_extname));
    cpl_image_save( sub_extract_img, qc_subextract_name, CPL_BPP_IEEE_DOUBLE, 
      qc_header,mode);
	
    sprintf( qc_extname, "ORD%d_ERRS", abs_order);
    check( xsh_pfits_set_extname ( qc_header, qc_extname));
    cpl_image_save( sub_extract_errs_img, qc_subextract_name, CPL_BPP_IEEE_DOUBLE, 
      qc_header, CPL_IO_EXTEND);
      
    sprintf( qc_extname, "ORD%d_QUAL", abs_order);
    check( xsh_pfits_set_extname ( qc_header, qc_extname));
    cpl_image_save( sub_extract_qual_img, qc_subextract_name, XSH_PRE_QUAL_BPP,
      qc_header, CPL_IO_EXTEND);



    check( sub_x_img = cpl_image_extract( x_img, 1,
      ny_extract_min+1, nlambdas, ny_extract_max+1));
    check( sub_y_img = cpl_image_extract( y_img, 1,
      ny_extract_min+1, nlambdas, ny_extract_max+1));


#if REGDEBUG_SUBEXTRACT_XY
    { char name[256];

      sprintf( name, "sub_x_%d.fits", abs_order);
      xsh_msg("EXTRACT_XY save %s",name);
      cpl_image_save( sub_x_img, name, CPL_BPP_IEEE_DOUBLE, NULL,CPL_IO_DEFAULT);
      sprintf( name, "sub_y_%d.fits", abs_order);
      xsh_msg("EXTRACT_XY save %s",name);
      cpl_image_save( sub_y_img, name, CPL_BPP_IEEE_DOUBLE, NULL,CPL_IO_DEFAULT);
    }
#endif

    check( extract_lambda_vect = cpl_vector_wrap( nlambdas, lambdas));

    /* do standard extraction */
    check( std_extract_vect = xsh_image_extract_standard( sub_extract_img,
      sub_extract_errs_img, sub_extract_qual_img, &std_err_extract_vect,
      &std_qual_extract_vect));

#if REGDEBUG_EXTRACT_STD
    {
      char std_spectrum_name[256];
      FILE* std_spectrum_file = NULL;
      int nx = cpl_vector_get_size( std_extract_vect);
      
      sprintf( std_spectrum_name, "extract_std_%d.dat", abs_order);
      std_spectrum_file = fopen( std_spectrum_name, "w");
 
      fprintf(std_spectrum_file, "#index lambda flux err qual\n");

      for(i=0; i< nx; i++){
        double lambdav;
        double fluxv;
        double lambda_err;
        double qual_v;

        lambdav = cpl_vector_get( extract_lambda_vect, i);
        fluxv = cpl_vector_get( std_extract_vect, i);
        lambda_err = cpl_vector_get( std_err_extract_vect, i);
        qual_v = cpl_vector_get( std_qual_extract_vect, i);
        fprintf(std_spectrum_file, "%d %f %f %f %f\n", i, lambdav, fluxv, 
          lambda_err, qual_v);
      }
      fclose( std_spectrum_file);
    }
#endif


    div_flux_vect = std_extract_vect;
    div_err_vect = std_err_extract_vect;

    for( iiter=1; iiter <= niter; iiter++){
      double kappa = 3.0, frac_min=0.7;
      int niter = 2;

      xsh_free_image( &s2Dby1D_img);
      xsh_free_image( &s2Dby1D_err_img);
      xsh_free_image( &model_img);
      xsh_free_image( &weight_img);

      xsh_msg_dbg_low("---opt_extract : Do optimal extraction %d / %d", iiter, niter);
      /* Create 2D/1D image */
      check( s2Dby1D_img = xsh_image_divide_1D( sub_extract_img,
        sub_extract_errs_img, div_flux_vect, div_err_vect, 
        &s2Dby1D_err_img));
	
      sprintf( qc_s2ddiv1d_name, "s2Dby1D.fits");
      sprintf( qc_extname, "ORD%d_FLUX", abs_order);
      check( xsh_pfits_set_extname ( qc_header, qc_extname));
      cpl_image_save( s2Dby1D_img, qc_s2ddiv1d_name, CPL_BPP_IEEE_DOUBLE, qc_header,
        mode);
      sprintf( qc_extname, "ORD%d_ERRS", abs_order);
      cpl_image_save( s2Dby1D_err_img, qc_s2ddiv1d_name, CPL_BPP_IEEE_DOUBLE, qc_header,
        CPL_IO_EXTEND);

      check( model_img = xsh_optextract_produce_model( s2Dby1D_img, 
        opt_extract_par->method, chunk_ovsamp_size, deg_poly, 
        opt_extract_par->oversample, extract_x_data, extract_y_data, 
        abs_order, kappa, niter, frac_min));

      sprintf( qc_model_name, "model.fits");
      sprintf( qc_extname, "ORD%d_FLUX", abs_order);
      check( xsh_pfits_set_extname ( qc_header, qc_extname));
      cpl_image_save( model_img, qc_model_name, CPL_BPP_IEEE_DOUBLE, qc_header, mode);
      
      /* optimal extraction */
      xsh_free_vector( &opt_extract_vect);
      xsh_free_vector( &opt_err_extract_vect);
      xsh_free_vector( &opt_qual_extract_vect);
      check( opt_extract_vect = xsh_image_extract_optimal( sub_extract_img, 
        sub_extract_errs_img, sub_extract_qual_img, opt_extract_par, 
        model_img, decode_bp,&weight_img, &opt_err_extract_vect, &opt_qual_extract_vect));
      div_flux_vect = opt_extract_vect;
      div_err_vect = opt_err_extract_vect;
    }

    sprintf( qc_weight_name, "weight.fits");
    sprintf( qc_extname, "ORD%d_FLUX", abs_order);
    check( xsh_pfits_set_extname ( qc_header, qc_extname));
    cpl_image_save( weight_img, qc_weight_name, CPL_BPP_IEEE_DOUBLE, qc_header,
      mode);

    /* correct blaze */
    if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
      blaze_shift = (int)pos_cen_y[0];
    }
    else{
      int nx = cpl_vector_get_size( std_extract_vect);

      blaze_shift = (int)pos_cen_y[nx-1];
    }

    check( xsh_vector_divide_poly( std_extract_vect,
      opt_extract_par->oversample,
      order_list->list[iorder].blazepoly, blaze_shift, instrument));

     check( xsh_vector_divide_poly( std_err_extract_vect,
       opt_extract_par->oversample,
       order_list->list[iorder].blazepoly, blaze_shift, instrument));

#if REGDEBUG_BLAZE
    if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM) {
      char std_spectrum_name[256];
      FILE* std_spectrum_file = NULL;
      int nx = cpl_vector_get_size( std_extract_vect);

      sprintf( std_spectrum_name, "spectrum_divbyblaze_std_%d.dat", abs_order);
      std_spectrum_file = fopen( std_spectrum_name, "w");

      fprintf( std_spectrum_file, "#index lambda flux err qual\n");
      for(i=0; i< nx; i++){
        double lambdav;
        double fluxv, flux_err, qual_v;

        lambdav = cpl_vector_get( extract_lambda_vect, i);
        fluxv = cpl_vector_get( std_extract_vect, i);
        flux_err = cpl_vector_get( std_err_extract_vect, i);
        qual_v = cpl_vector_get( std_qual_extract_vect, i);
        fprintf(std_spectrum_file, "%d %f %f %f %f\n", i, lambdav, fluxv,
          flux_err, qual_v);
      }
      fclose( std_spectrum_file);
    }
#endif

    check( xsh_vector_divide_poly( opt_extract_vect, 
      opt_extract_par->oversample,
      order_list->list[iorder].blazepoly, blaze_shift, instrument));

    check( xsh_vector_divide_poly( opt_err_extract_vect,
      opt_extract_par->oversample,
      order_list->list[iorder].blazepoly, blaze_shift, instrument));

    /* convert in lambda spaces */
    check( xsh_interpolate_spectrum( biny, abs_order, opt_extract_par->oversample,
      opt_extract_par->lambda_step, 
      extract_lambda_vect, 
      std_extract_vect, std_err_extract_vect, std_qual_extract_vect,
      opt_extract_vect, opt_err_extract_vect, opt_qual_extract_vect,
      &result_lambda_vect,
      &std_flux_vect, &std_err_vect, &std_qual_vect,
      &opt_flux_vect, &opt_err_vect, &opt_qual_vect));

    result_lambda_vect_size = cpl_vector_get_size( result_lambda_vect);

    /* REGDEBUG */
    if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM ) {
      char std_spectrum_name[256];
      FILE* std_spectrum_file = NULL;
    
      sprintf( std_spectrum_name, "spectrum_std_%d.dat", abs_order);
      std_spectrum_file = fopen( std_spectrum_name, "w");

      fprintf(std_spectrum_file, "#lambda flux err qual sn %d\n",
        opt_extract_par->oversample);
      for(i=0; i< result_lambda_vect_size; i++){
        double lambdav, flux, err;
        double qual;

        lambdav = cpl_vector_get( result_lambda_vect, i);
        flux = cpl_vector_get( std_flux_vect, i);
        err = cpl_vector_get( std_err_vect, i);
        qual = cpl_vector_get( std_qual_vect, i);
        fprintf(std_spectrum_file, "%f %f %f %f %f\n", lambdav, flux / conad, 
          err / conad, qual, flux/err);
      }
      fclose( std_spectrum_file);

      sprintf( std_spectrum_name, "spectrum_opt_%d.dat", abs_order);
      std_spectrum_file = fopen( std_spectrum_name, "w");

      fprintf(std_spectrum_file, "#lambda flux err qual sn %d\n",
        opt_extract_par->oversample);
      for(i=0; i< result_lambda_vect_size; i++){
        double lambdav, flux, err;
        double qual;  

        lambdav = cpl_vector_get( result_lambda_vect, i);
        flux = cpl_vector_get( opt_flux_vect, i);
        err = cpl_vector_get( opt_err_vect, i);
        qual = cpl_vector_get( opt_qual_vect, i);  

        fprintf(std_spectrum_file, "%f %f %f %f %f\n", lambdav, flux / conad, 
          err / conad, qual, flux/err);
      }
      fclose( std_spectrum_file);
    }
    /* save extracted order in rectify list */
    xsh_rec_list_set_data_size( orderext1d, iorder, abs_order,
      result_lambda_vect_size, 1);
    xsh_rec_list_set_data_size( orderoxt1d, iorder, abs_order,
      result_lambda_vect_size, 1);
    for(i=0; i< result_lambda_vect_size; i++){
      double lambdav, extflux,oxtflux;
      double exterr, oxterr;
      int extqual, oxtqual;
 
      lambdav = cpl_vector_get( result_lambda_vect, i);
      extflux = cpl_vector_get( std_flux_vect, i);
      oxtflux = cpl_vector_get( opt_flux_vect, i);

      exterr = cpl_vector_get( std_err_vect, i);
      oxterr = cpl_vector_get( opt_err_vect, i);

      extqual = (int) cpl_vector_get( std_qual_vect, i);
      oxtqual = (int) cpl_vector_get( opt_qual_vect, i);

      orderext1d->list[iorder].lambda[i] = lambdav;
      orderoxt1d->list[iorder].lambda[i] = lambdav;
      
      /* e- to ADU */
      orderext1d->list[iorder].data1[i] = extflux / conad;
      orderoxt1d->list[iorder].data1[i] = oxtflux / conad;

      orderext1d->list[iorder].errs1[i] = exterr / conad;
      orderoxt1d->list[iorder].errs1[i] = oxterr / conad;

      orderext1d->list[iorder].qual1[i] =  extqual;
      orderoxt1d->list[iorder].qual1[i] =  oxtqual;
      mode = CPL_IO_EXTEND;

    }
    /* free memory */
    xsh_free_image( &s2Dby1D_img);
    xsh_free_image( &s2Dby1D_err_img);
    xsh_free_image( &model_img);
    xsh_free_image( &weight_img);
    xsh_unwrap_image( &extract_img);
    xsh_unwrap_image( &extract_errs_img);
    xsh_unwrap_image( &extract_qual_img);
    xsh_unwrap_image( &x_img);
    xsh_unwrap_image( &y_img);
    xsh_free_image( &sub_extract_img);
    xsh_free_image( &sub_extract_errs_img);
    xsh_free_image( &sub_extract_qual_img);
    xsh_free_image( &sub_x_img);
    xsh_free_image( &sub_y_img);

    xsh_free_vector( &std_extract_vect);
    xsh_free_vector( &std_err_extract_vect);
    xsh_free_vector( &std_qual_extract_vect);

    xsh_free_vector( &opt_extract_vect);
    xsh_free_vector( &opt_err_extract_vect);
    xsh_free_vector( &opt_qual_extract_vect);

    xsh_unwrap_vector( &extract_lambda_vect);
    xsh_free_vector( &result_lambda_vect);
    xsh_free_vector( &std_flux_vect);
    xsh_free_vector( &std_err_vect);
    xsh_free_vector( &std_qual_vect);
    xsh_free_vector( &opt_flux_vect);
    xsh_free_vector( &opt_err_vect);
    xsh_free_vector( &opt_qual_vect);
  }

  /* Save orderext1d rectify list */
  /* Set header from science header */
  check( cpl_propertylist_append( orderext1d->header, sci_pre->data_header));
  /* Add useful keywords */
  check( xsh_pfits_set_rectify_bin_lambda( orderext1d->header, lambda_step));
  check( xsh_pfits_set_rectify_bin_space( orderext1d->header, 0.0));

  check( lambda_min =  xsh_rec_list_get_lambda_min( orderext1d));
  check( xsh_pfits_set_rectify_lambda_min( orderext1d->header, lambda_min));
  check( lambda_max =  xsh_rec_list_get_lambda_max( orderext1d));
  check( xsh_pfits_set_rectify_lambda_max( orderext1d->header, lambda_max));
  sprintf(tag,"%s_%s",rec_prefix,XSH_GET_TAG_FROM_ARM(XSH_ORDER_EXT1D,instrument));

  check( xsh_pfits_set_pcatg( orderext1d->header, tag));
  check( xsh_pfits_set_rectify_space_min( orderext1d->header, 0.0)) ;
  check( xsh_pfits_set_rectify_space_max( orderext1d->header, 0.0));
  sprintf(filename,"%s.fits",tag);
  check( *orderext1d_frame = xsh_rec_list_save( orderext1d,
						filename, tag, CPL_TRUE));
 

  /* Save orderoxt1d rectify list */
  /* Set header from science header */
  check( cpl_propertylist_append( orderoxt1d->header, sci_pre->data_header));
  /* Add useful keywords */
  check( xsh_pfits_set_rectify_bin_lambda( orderoxt1d->header, lambda_step));
  check( xsh_pfits_set_rectify_bin_space( orderoxt1d->header, 0.0));
  check( lambda_min =  xsh_rec_list_get_lambda_min( orderoxt1d));
  check( xsh_pfits_set_rectify_lambda_min(orderoxt1d->header, lambda_min));
  check( lambda_max =  xsh_rec_list_get_lambda_max( orderoxt1d));
  check( xsh_pfits_set_rectify_lambda_max( orderoxt1d->header, lambda_max));
  sprintf(tag,"%s_%s",rec_prefix,XSH_GET_TAG_FROM_ARM(XSH_ORDER_OXT1D,instrument));
  sprintf(tag_drl,"%s_DRL",tag);

  check( xsh_pfits_set_pcatg( orderoxt1d->header, tag_drl));
  check( xsh_pfits_set_rectify_space_min( orderoxt1d->header, 0.0)) ;
  check( xsh_pfits_set_rectify_space_max( orderoxt1d->header, 0.0));
  sprintf(filename,"%s.fits",tag);
  sprintf(filename_drl,"DRL_%s.fits",filename);
  xsh_add_temporary_file(filename);
  xsh_add_temporary_file(filename_drl);

  check( *orderoxt1d_frame = xsh_rec_list_save( orderoxt1d, filename_drl, 
                                                tag_drl,CPL_TRUE));

  check( *res_frame_ext = xsh_rec_list_save2( orderoxt1d,filename,tag));

  /* qc frames */
  XSH_ASSURE_NOT_NULL( qc_subextract_frame);
  XSH_ASSURE_NOT_NULL( qc_s2ddiv1d_frame);
  XSH_ASSURE_NOT_NULL( qc_model_frame);
  XSH_ASSURE_NOT_NULL( qc_weight_frame);
  
  *qc_subextract_frame = cpl_frame_new();
  
  sprintf(tag,"%s_OXT_SUBEXTRACT",rec_prefix);
  check ((cpl_frame_set_filename ( *qc_subextract_frame, qc_subextract_name),
          cpl_frame_set_tag( *qc_subextract_frame, tag),
          cpl_frame_set_type ( *qc_subextract_frame, CPL_FRAME_TYPE_IMAGE) )) ;

  *qc_s2ddiv1d_frame = cpl_frame_new();
  sprintf(tag,"%s_OXT_S2DDIV1D",rec_prefix);
  check ((cpl_frame_set_filename ( *qc_s2ddiv1d_frame, qc_s2ddiv1d_name),
          cpl_frame_set_tag( *qc_s2ddiv1d_frame, tag),
          cpl_frame_set_type ( *qc_s2ddiv1d_frame, CPL_FRAME_TYPE_IMAGE) )) ;

  *qc_model_frame = cpl_frame_new();
  sprintf(tag,"%s_OXT_MODEL",rec_prefix);
  check ((cpl_frame_set_filename ( *qc_model_frame, qc_model_name),
          cpl_frame_set_tag( *qc_model_frame, tag),
          cpl_frame_set_type ( *qc_model_frame, CPL_FRAME_TYPE_IMAGE) ));
	  
  *qc_weight_frame = cpl_frame_new();
  sprintf(tag,"%s_OXT_WEIGHT",rec_prefix);
  check ((cpl_frame_set_filename ( *qc_weight_frame, qc_weight_name),
          cpl_frame_set_tag( *qc_weight_frame, tag),
          cpl_frame_set_type ( *qc_weight_frame, CPL_FRAME_TYPE_IMAGE) ));
  
  check( xsh_add_temporary_file( qc_subextract_name));
  check( xsh_add_temporary_file( qc_s2ddiv1d_name));
  check( xsh_add_temporary_file( qc_model_name));
  check( xsh_add_temporary_file( qc_weight_name));
  
  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_unwrap_image( &sub_extract_img);
      xsh_unwrap_image( &sub_extract_errs_img);
      xsh_unwrap_image( &sub_extract_qual_img);

      xsh_free_vector( &std_extract_vect);
      xsh_free_vector( &std_err_extract_vect);
      xsh_free_vector( &std_qual_extract_vect);

      xsh_free_vector( &opt_extract_vect);
      xsh_free_vector( &opt_err_extract_vect);
      xsh_free_vector( &opt_qual_extract_vect);

      xsh_unwrap_vector( &extract_lambda_vect);

      xsh_free_vector( &result_lambda_vect);
      xsh_free_vector( &std_flux_vect);
      xsh_free_vector( &std_err_vect);
      xsh_free_vector( &std_qual_vect);
      xsh_free_vector( &opt_flux_vect);
      xsh_free_vector( &opt_err_vect);
      xsh_free_vector( &opt_qual_vect);
    }
    xsh_pre_free( &sci_pre);
    xsh_order_list_free( &order_list);
    xsh_spectralformat_list_free( &spectralformat_list);
    xsh_wavesol_free( &wavesol);
    XSH_FREE( lambdas);
    XSH_FREE( pos_cen_x);
    XSH_FREE( pos_cen_y);
    XSH_FREE( extract_data);
    XSH_FREE( extract_x_data);
    XSH_FREE( extract_y_data);
    XSH_FREE( extract_errs);
    XSH_FREE( extract_qual);
    xsh_free_image( &blaze_img);
    xsh_rec_list_free( &orderext1d);
    xsh_rec_list_free( &orderoxt1d);
    xsh_free_propertylist( &qc_header);
    return;   
}
/*----------------------------------------------------------------------------*/
/* END xsh_opt_extract */
/*----------------------------------------------------------------------------*/

/**@}*/
