/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004,2005 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */
/***************************************************************************
* E.S.O. - VLT project
*
*
*
* who       when      what
* --------  --------  ----------------------------------------------
* schreib  25/05/00  created
*/

/************************************************************************
*   NAME
*        sinfo_spectrum_ops.c -
*        some sinfo_vector procedures to operate on spectra
*
*   SYNOPSIS
*   #include "sinfo_spectrum_ops.h"
*
*   1) Vector * sinfo_new_vector( ulong32 n_elements )
*   2) void * sinfo_new_destroy_vector( Vector *sinfo_vector )
*   3) cpl_image * sinfo_new_vector_to_image( Vector * spectrum )
*   4) Vector * sinfo_new_image_to_vector( cpl_image * spectrum )
*   5) cpl_image * 
       sinfo_new_extract_spectrum_from_resampled_flat(cpl_image * resflat,
*                                                   float      loreject,
*                                                   float      hireject ) 
*   6) cpl_image * sinfo_new_multiply_image_with_spectrum(cpl_image * image, 
                                                          cpl_image * spectrum)
*   7) cpl_image * sinfo_new_optimal_extraction_from_cube(cpl_imagelist * cube, 
*                                            int       halfbox_x, 
*                                            int       halfbox_y,
*                                            float     fwhm_factor,
*                                            float     backvariance,
*                                            float     sky,
*                                            float     gain,
*                                            float     exptime)
*   8) Vector * sinfo_new_extract_sky_from_cube( cpl_imagelist * cube,
*                                   float     loReject,
*                                   float     hiReject,
*                                   int     * position,
*                                   int       tolerance,
*                                   int       posindicator )
*    9) Vector * sinfo_new_sum_rectangle_of_cube_spectra( cpl_imagelist * cube,
*                                     int llx,
*                                     int lly,
*                                     int urx,
*                                     int ury )
*   10) Vector * sinfo_new_sum_circle_of_cube_spectra( cpl_imagelist * cube,
*                                        int       centerx,
*                                        int       centery,
*                                        int       radius )
*   11) Vector * 
        sinfo_new_mean_rectangle_of_cube_spectra( cpl_imagelist * cube,
*                                            int llx,
*                                            int lly,
*                                            int urx,
*                                            int ury )
*   12) Vector * sinfo_new_mean_circle_of_cube_spectra( cpl_imagelist * cube,
*                                         int       centerx,
*                                         int       centery,
*                                         int       radius )
*   13) Vector * 
        sinfo_new_blackbody_spectrum(char * templateSpec, double temp )
*   14) Vector * 
        sinfo_new_median_rectangle_of_cube_spectra(cpl_imagelist * cube,
*                                               int llx,
*                                               int lly,
*                                               int urx,
*                                               int ury )
*   15) Vector * sinfo_new_median_circle_of_cube_spectra( cpl_imagelist * cube,
*                                           int       centerx,
*                                           int       centery,
*                                           int       radius )
*   16) Vector * 
        sinfo_new_cleanmean_rectangle_of_cube_spectra(cpl_imagelist * cube,
*                                                 int llx,
*                                                 int lly,
*                                                 int urx,
*                                                 int ury,
*                                                 float lo_reject,
*                                                 float hi_reject )
*   17) Vector * 
        sinfo_new_cleanmean_circle_of_cube_spectra( cpl_imagelist * cube,
*                                              int       centerx,
*                                              int       centery,
*                                              int       radius,
*                                              float     lo_reject,
*                                              float     hi_reject )
*   18) float * sinfo_new_shift_array ( float * input, 
                                        int n_elements, 
                                        float shift, 
                                        double * ker ) 
*
*   DESCRIPTION
*   1) allocates memory for a new sinfo_vector
*   2) frees memory of a sinfo_vector
*   3) converts a spectral sinfo_vector to a fits image
*      remark: sinfo_vector object spectrum is destroyed
*   4) converts a fits image to a spectral sinfo_vector
*      remark: input image is destroyed
*   5) builds one spectrum in a fits image out of a resampled
*      flatfield frame by taking a clean mean along the spatial pixels
*   6) multiplys a resampled image with a resampled spectrum
*      (calibrated halogen lamp spectrum) in the same spectral range
*      that means all image columns are multiplied with the same spectrum
*   7) does the optimal extraction of a standard star spectrum
*      according to the equation:
*       S = sum { (P^2 / V) * (I - B) / P } / sum{ P^2 / V } 
*       S: spectral flux at a particular wavelength
*       P: normalized PSF (determined by a 2D-Gaussian fit)
*       I: pixel value
*       B: background pixel value determined by the background parameter 
           of the 2D-Gaussian fit
*       V: estimated variance of a pixel: 
           V = [R^2 + D + sky + I,c/exptime]/gain
*          where R is the read noise, and D the sinfo_dark current variance.
*          backvariance is R^2 + D in counts/sec. 
           I,c is the source intensity in counts
*          Remember: sigma,e[e-] = gain[e/count] * sigma,c [counts] = 
                     sqrt(I,e) = sqrt(gain*I,c)
*          => V,c = sigma,c^2 = sigma,e^2/gain^2 
*          => sigma,c = sqrt(I,c/gain) => V,c = I,c/gain
*   8) extracts a sky spectrum from a reduced sky spider observation, that
*      means from a data cube. Therefore, the position of the sky within the
*      field of view must be first read from the fits header.  
       A pixel tolerance is subtracted.
*      The found sky spectra are averaged by rejecting the extreme 
       high and low values.
*   9) summing routine for a reduced data to get a better spectral S/N
*      only for a rectangular aperture.
*   10) summing routine for a reduced data to get a better spectral S/N
*       only for a circular aperture.
*   11) averaging routine for a reduced data to get a better spectral S/N
*       only for a rectangular aperture.
*   12) averaging routine for a reduced data to get a better spectral S/N
*       only for a circular aperture.
*   13) computes a blackbody spectral intensity distribution
*       (W/(m^2 lambda ster)) 
*   14) sinfo_median routine for a reduced data to get a better spectral S/N
*       only for a rectangular aperture.
*   15) sinfo_median routine for a reduced data to get a better spectral S/N
*       only for a circular aperture.
*   16) clean averaging routine for a reduced data to get a better spectral S/N
*       only for a rectangular aperture.
*   17) clean averaging routine for a reduced data to get a better spectral S/N
*       only for a circular aperture.
*   18) shifts an array by a sub-pixel shift value using a tanh
*       interpolation kernel
*
*   FILES
*
*   ENVIRONMENT
*
*   RETURN VALUES
*
*   CAUTIONS
*
*   EXAMPLES
*
*   SEE ALSO
*
*   BUGS
*
*------------------------------------------------------------------------
*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#define POSIX_SOURCE 1
#include "sinfo_vltPort.h"

/*
 * System Headers
 */

/*
 * Local Headers
 */
#include "sinfo_pfits.h"
#include "sinfo_spectrum_ops.h"
#include "sinfo_resampling.h"
#include "sinfo_utilities.h"
#include "sinfo_utils_wrappers.h"
/*----------------------------------------------------------------------------
 *                            Function codes
 *--------------------------------------------------------------------------*/

/**@{*/
/**
 * @defgroup sinfo_spectrum_ops Operations on spectra
 *
 * TBD
 */



/**
@brief convert an image in a table spectrum 
@name sinfo_stectrum_ima2table
@param spc       input image spectrum
@param filename  input filename
@param tbl       output table
*/

int sinfo_stectrum_ima2table(
                 const cpl_image* spc,
                 const char* filename,
                 cpl_table** tbl)
{
  const float* pidata=NULL;
  int nx=0;
  int ny=0;
  int nraw=0;
  int i=0;
  double wav=0;
 
  double step=0;
  double ws=0;
  double we=0;
  double wc=0;
  cpl_propertylist* plist=NULL;

  if(spc == NULL){
    sinfo_msg_error("Input image is null");
    return -1;
  }

  pidata = cpl_image_get_data_const(spc);
  nx=cpl_image_get_size_x(spc);
  ny=cpl_image_get_size_y(spc);

  if((nx == 0) || (ny == 0)) {
    sinfo_msg_error("Input image has improper size: nx=%d ny=%d",nx,ny);
    return -1;
  }
  if((nx > 1) && (ny > 1)) {
    sinfo_msg_error("Input image has improper size: nx=%d ny=%d",nx,ny);
    return -1;
  }


  nraw=nx*ny;
  *tbl = cpl_table_new(nraw);
  cpl_table_new_column(*tbl,"WAVE",CPL_TYPE_DOUBLE);
  cpl_table_new_column(*tbl,"INT",CPL_TYPE_DOUBLE);


  if ((cpl_error_code)((plist = cpl_propertylist_load(filename, 0)) == NULL)) {
      sinfo_msg_error( "getting header from reference frame %s",filename);
      cpl_propertylist_delete(plist) ;
      return -1 ;
  }


  if(nx>1) {
    step=sinfo_pfits_get_cdelt1(plist);
    wc=sinfo_pfits_get_crval1(plist);
  } else {

    step=sinfo_pfits_get_cdelt2(plist);
    wc=sinfo_pfits_get_crval2(plist);
  }

  ws=wc-nraw*step/2;
  we=wc+nraw*step/2;
  wav=ws;
  sinfo_msg("ws=%f we=%f step=%f",ws,we,step);
  cpl_table_set_double(*tbl,"WAVE",0,wav);
  cpl_table_set_double(*tbl,"INT",0,pidata[i]);

  for(i=1;i<nraw;i++) {
    wav+=step;
    double amp=(double)pidata[i];
    cpl_table_set_double(*tbl,"WAVE",i,wav);
    cpl_table_set_double(*tbl,"INT",i,amp);
  }
  cpl_propertylist_delete(plist);
  return 0;


}



/**
@brief allocates memory for a new sinfo_vector
@name sinfo_new_vector()
@param n_elements number of sinfo_vector elements
@return Vector 
*/



Vector * sinfo_new_vector( ulong32 n_elements )
{
    Vector * local_new_vector ;

    if ( n_elements <= 0 )
    {
        sinfo_msg_error (" wrong number of elements\n") ;
        return NullVector ;
    }
    
    /* allocate memory for a sinfo_vector with the given number of elements */
    local_new_vector = (Vector *) cpl_malloc (sizeof (Vector)) ; 
    local_new_vector -> n_elements = n_elements ;
    local_new_vector -> data = (pixelvalue *) cpl_calloc (n_elements, 
                                                  sizeof (pixelvalue)) ;

    return local_new_vector ;
}

/**
@brief frees memory of a sinfo_vector
@name sinfo_free_svector()
@param sinfo_vector to destroy
@return nothing 
*/

void sinfo_free_svector( Vector **svector )
{
    if ( *svector != NULL )   
    {
       
      if((*svector) -> data != NULL) {
    cpl_free ( (*svector) -> data ) ;
    (*svector)->data = NULL;
      }
      cpl_free ( *svector ) ;
      *svector = NULL;
    }
    return ;
}

/**
@brief frees memory of a sinfo_vector
@name sinfo_new_destroy_vector()
@param sinfo_vector to destroy
@return nothing 
*/

void sinfo_new_destroy_vector( Vector *sinfo_vector )
{
    if ( sinfo_vector == NULL )   
    {
        sinfo_msg_error(" NULL Vector given!\n") ;
        return ;
    }    
    
    cpl_free ( sinfo_vector -> data ) ;
    cpl_free ( sinfo_vector ) ;
}

/**
@brief converts a spectral sinfo_vector to a fits image
@name sinfo_new_vector_to_image()
@param spectral sinfo_vector that should be converted to a fits image
@return image with lx = 1 and ly = length of sinfo_vector
@note sinfo_vector object spectrum is destroyed
*/

cpl_image * sinfo_new_vector_to_image( Vector * spectrum )
{
    cpl_image * returnIm ;
    int i ;
   
    float* podata=NULL;


    if ( spectrum == NULL )
    {
        sinfo_msg_error(" no spectrum given!\n") ;
        return NULL ;
    }
 
    /* allocate memory */
    if ( NULL == (returnIm = cpl_image_new(1, spectrum->n_elements,
                                           CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error(" no spectrum given!\n") ;
        sinfo_new_destroy_vector(spectrum) ;
        return NULL ;
    }

    podata=cpl_image_get_data_float(returnIm);
    for ( i = 0 ; i < spectrum->n_elements ; i++ )
    {
        podata[i] = spectrum -> data[i] ;
    }

    sinfo_new_destroy_vector (spectrum) ;
    return returnIm ;
}

/**
@brief converts a fits image to a spectral sinfo_vector
@name sinfo_new_image_to_vector()
@param 1-D Fits image that should be converted to a spectral sinfo_vector
@return spectral sinfo_vector with length ly 
@note input image is destroyed
*/

Vector * sinfo_new_image_to_vector( cpl_image * spectrum )
{
    Vector * returnVector ;
    int i ;
    int ilx=0;
    int ily=0;
  
    float* pidata=NULL;

    if ( spectrum == NULL )
    {
        sinfo_msg_error(" no spectrum given!") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(spectrum);
    ily=cpl_image_get_size_y(spectrum);

    /* allocate memory */
    if ( NULL == (returnVector = sinfo_new_vector(ilx*ily)) )
    {
        sinfo_msg_error(" cannot allocate memory!") ;
        cpl_image_delete(spectrum) ;
        return NULL ;
    }

    pidata=cpl_image_get_data_float(spectrum);
    for ( i = 0 ; i < (int) ilx*ily ; i++ )
    {
        returnVector -> data[i] = pidata[i] ;
    }

    cpl_image_delete (spectrum) ;
    return returnVector ;
}

/**
@brief extracts a spectrum from an image image 
@name sinfo_new_extract_spectrum_from_resampled_flat()
@param resflat: resampled halogen lamp frame, bad pixel corrected
@param loreject, 
@param hireject: percentage of extreme low and high intensity values
                 to be rejected from averaging
@return fits image that contains the final halogen lamp spectrum
@doc builds one spectrum in a fits image out of a resampled flatfield 
     frame by taking a clean mean along the spatial pixels
*/

cpl_image * 
sinfo_new_extract_spectrum_from_resampled_flat( cpl_image * resflat,
                                             float      loreject,
                                             float      hireject ) 
{
    cpl_image * retIm ;
    int col, row ;
    float* array=NULL ;
    float cleanMean ;
    Vector * spectrum ;

    int ilx=0;
    int ily=0;
  
    float* pidata=NULL;
  
    if ( resflat == NULL )
    {
        sinfo_msg_error(" no flatfield given!") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(resflat);
    ily=cpl_image_get_size_y(resflat);

    /* allocate memory */
    if ( NullVector == (spectrum = sinfo_new_vector(ily) ) )
    {
        sinfo_msg_error(" could not allocate memory!") ;
        return NULL ;
    }

    array=cpl_calloc(ily,sizeof(float)) ;

    pidata=cpl_image_get_data_float(resflat);
    for ( row = 0 ; row < ily ; row++ )
    {
        int n = 0 ;
        for ( col = 0 ; col < ilx ; col++ )
        {
            if ( !isnan(pidata[col + row*ilx]) )
            {
                array[n] = pidata[col+row*ilx] ;
                n++ ;
            }
        }
        if ( n == 0 )
        {
            sinfo_msg_warning(" only bad pixels in row: %d!", row) ;
            cleanMean = ZERO ;
        }
        else
        {
            if ( FLT_MAX == (cleanMean = sinfo_new_clean_mean(array, n, 
                                                              loreject, 
                                                              hireject)) )
            {
                sinfo_msg_error(" could not do sinfo_clean_mean!") ;
                sinfo_new_destroy_vector(spectrum) ;
                return NULL ;
            }
        }
        spectrum->data[row] = cleanMean ; 
    }
    if ( NULL == ( retIm = sinfo_new_vector_to_image( spectrum ) ) )
    {
        sinfo_msg_error(" could not do sinfo_vectorToImage!") ;
        sinfo_new_destroy_vector(spectrum) ;
        return NULL ;
    }
    cpl_free(array) ;

    return retIm ;
}

/**
@brief multiplies a resampled image with a resampled spectrum in the 
       same spectral range
@name sinfo_new_multiply_image_with_spectrum()
@param image: resampled image
@param spectrum: resampled spectrum in image format
@return resulting image
@doc
multiplys a resampled image with a resampled spectrum (calibrated halogen lamp
spectrum) in the same spectral range that means all image columns are 
multiplied with the same spectrum
 */

cpl_image * 
sinfo_new_multiply_image_with_spectrum( cpl_image * image, 
                                        cpl_image * spectrum )
{
    int col, row ;
    cpl_image * retImage ;


    int ilx=0;
    int ily=0;
    /* int slx=0; */
    int sly=0;

    float* pidata=NULL;
    float* psdata=NULL;
    float* podata=NULL;


    if ( image == NULL )
    {
        sinfo_msg_error(" no image given!") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(image);
    ily=cpl_image_get_size_y(image);

    if ( spectrum == NULL )
    {
        sinfo_msg_error(" no spectrum image given!") ;
        return NULL ;
    }
    /* slx=cpl_image_get_size_x(spectrum); */
    sly=cpl_image_get_size_y(spectrum);

    if ( sly != ily )
    {
        sinfo_msg_error(" images are not compatible in pixel length!") ;
        return NULL ;
    }

    if ( NULL == (retImage = cpl_image_duplicate(image)) )
    {
        sinfo_msg_error(" could not copy original image!\n") ;
        return NULL ;
    }

    pidata=cpl_image_get_data_float(image);
    psdata=cpl_image_get_data_float(spectrum);
    podata=cpl_image_get_data_float(retImage);

    for ( col = 0 ; col < ilx ; col++ )
    {
        for ( row = 0 ; row < ily ; row++ )
        {
            if ( !isnan(pidata[col+row*ilx]) &&
                 !isnan(psdata[col+row*ilx]))
            {
                podata[col+row*ilx] = pidata[col+row*ilx] * psdata[row] ;
                                                   
            }
        }
    }
    return retImage ;
}

/**
@brief does the optimal extraction of a standard star spectrum
@name sinfo_new_optimal_extraction_from_cube()
@param cube: input data cube
@param llx, 
@param lly: lower left sinfo_edge points of the 2d Gaussian fitting box
@param halfbox_x, 
@param halfbox_y: half width of a box inside which a 
                  2D-Gaussian fit is carried out
@param fwhm_factor: factor applied to the found fwhms of a 2D-Gaussian
                    fit, defines the radius of the aperture inside which the 
                    spectral extraction is carried out (default: 0.6).
@param backvariance: (readnoise^2 + sinfo_dark current variance) 
                     needed to determine 
                     the noise variance of the background. Must be given
                     in counts/sec.
@param sky:         estimated sky variance in counts/sec
@param gain:        conversion factor electrons/count
@param exptime:     total exposure time
@return resulting spectrum stored in a 1D-image
@doc does the optimal extraction of a standard star spectrum according 
  to the equation:
S = sum { (P^2 / V) * (I - B) / P } / sum{ P^2 / V } 
S: spectral flux at a particular wavelength
P: normalized PSF (determined by a 2D-Gaussian fit)
I: pixel value
B: background pixel value determined by the background 
   parameter of the 2D-Gaussian fit
V: estimated variance of a pixel: V = [R^2 + D + sky + I,c/exptime]/gain
   where R is the read noise, and D the sinfo_dark current variance.
   backvariance is R^2 + D in counts/sec. 
   I,c is the source intensity in counts
@note: sigma,e[e-] = gain[e/count] * sigma,c [counts] = sqrt(I,e) = 
       sqrt(gain*I,c)
       => V,c = sigma,c^2 = sigma,e^2/gain^2 
       => sigma,c = sqrt(I,c/gain) => V,c = I,c/gain
 */

cpl_image * sinfo_new_optimal_extraction_from_cube( cpl_imagelist * cube, 
                                      int       llx,
                                      int       lly,
                                      int       halfbox_x, 
                                      int       halfbox_y,
                                      float     fwhm_factor,
                                      float     backvariance,
                                      float     sky,
                                      float     gain,
                                      float     exptime,
                                      const char* name,
                                      cpl_table** spectrum,
                      int       qc_info,
                                      int*      check2)
{
    int col, row, z ;
    cpl_image * averagedIm ; 
    cpl_image * retIm ;
    double fit_par[7] ;
    double derv_par[7] ;
    int mpar[7] ;
    double gfit_par[7] ;
    double gderv_par[7] ;
    int gmpar[7] ;
    int fitInd ;
    int i ;
    double sum ;
    double** weight=NULL ;
    double** sinfo_psf=NULL ;

    double variance ;
    double xdat[2] ;

    int first_col, last_col ;
    int first_row, last_row ;
    float norm ;
    float sum_psf=0;
    float sum_wgt=0;
    float cenpix = 0;
    float cenLambda = 0;
    float dispersion = 0;
    float lambda_start=0;

    int ilx=0;
    int ily=0;
    int inp=0;
    float* padata=NULL;
    float* podata=NULL;
    float tmp_val=0;
    cpl_propertylist* plist=NULL;


    /* TODO: the sky here is not really used. We remove compilation warning */
    sky=0;

    if ( NULL == cube )
    {
        sinfo_msg_error(" no cube given!\n") ;
        return NULL ;
    }


    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( llx < 0 || llx + 2*halfbox_x >= ilx || 
         lly < 0 || lly + 2*halfbox_y >= ily )
    {
      sinfo_msg("llx=%d, lly=%d,  llx + 2*halfbox_x=%d, "
                "lly + 2*halfbox_y=%d",
                llx,lly,llx + 2*halfbox_x,lly + 2*halfbox_y);
      sinfo_msg("tresh_min_x=%d, tresh_min_y=%d, "
                "tresh_max_x=%d, tresh_max_y=%d",0,0,ilx,ily);
        sinfo_msg_error(" lower left sinfo_edge points wrong position!") ;
        return NULL ;
    }
    if ( halfbox_x <= 0 || halfbox_y <= 0 || 
         2*halfbox_x > ilx || 2*halfbox_y > ily )
    {
        sinfo_msg_error(" wrong halfbox width given!") ;
        return NULL ;
    }
    if ( fwhm_factor <= 0. )
    {
        sinfo_msg_error(" wrong fwhm_factor given!") ;
        return NULL ;
    }
    if ( backvariance < 0. )
    {
        sinfo_msg_error(" wrong backvariance given!") ;
        return NULL ;
    }
    if ( exptime <= 0. || exptime == FLAG )
    {
        sinfo_msg_error(" wrong exposure time given!") ;
        return NULL ;
    }

    /* allocate memory for spectrum */
    if ( NULL == (retIm = cpl_image_new(1, inp,CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error(" memory allocation failed!\n") ;
        return NULL ;
    }
    /* collapse the cube to be able to compute the weights 
       for optimal extraction */
    if ( NULL == (averagedIm = sinfo_new_average_cube_to_image(cube)) )
    {
        sinfo_msg_error(" sinfo_averageCubeToImage failed!") ;
        cpl_image_delete(retIm) ;
        return NULL ;
    }

    /* call the 2D-Gaussian fit routine */
    for ( i = 0 ; i < 7 ; i++ )
    {
        mpar[i] = 1 ;
    }

    if ( -1 == (fitInd = sinfo_new_fit_2d_gaussian(averagedIm,
                                                   fit_par,
                                                   derv_par,
                                                   mpar,
                                                   llx,
                                                   lly,
                                                   halfbox_x,
                                                   halfbox_y,
                                                   check2 )) )
    {
        sinfo_msg_warning("sinfo_fit2dGaussian failed!") ;
        cpl_image_delete(retIm) ;
        cpl_image_delete(averagedIm) ;
        return NULL ;
    }

    /* determine the PSF by using the found 2D-Gaussian */
    sinfo_psf=sinfo_new_2Ddoublearray(ilx,ily) ;
    sum = 0 ;
    for ( row = 0 ; row < ily ; row++ )
    {
        for ( col = 0 ; col < ilx ; col++ )
        {
            xdat[0] = (double) col ;
            xdat[1] = (double) row ;
            sinfo_psf[col][row] = sinfo_new_gaussian_ellipse(xdat,fit_par) - 
                                  fit_par[3] ;
            sum += sinfo_psf[col][row] ;
        }
    }
    /* Scale the PSF and determine the pixel variances and the 
       normalization factor */
    norm = 0. ;
    variance = 0. ;
    sum_psf=0;

    weight=sinfo_new_2Ddoublearray(ilx,ily) ;

    padata=cpl_image_get_data_float(averagedIm);
    for ( row = 0 ; row < ily ; row++ )
    {
        for ( col = 0 ; col < ilx ; col++ )
        {
            sinfo_psf[col][row] /= sum ;
        sum_psf +=  sinfo_psf[col][row];
            if ( !isnan(padata[col+row*ilx]) )
            {
          /*
                variance = (backvariance + sky + padata[col+row*ilx] / 
                            exptime) / gain ;
          */
                variance = padata[col+row*ilx] / gain ;

            }
            else 
            {
                weight[col][row] = 0. ;
            }
            if (variance == 0.)
            {
                weight[col][row] = 0. ;
            }
            else
            {
         
                weight[col][row] = sinfo_psf[col][row]/variance ;
           
                norm += weight[col][row] * weight[col][row] * variance ;

            }
        
        }
    }

    sum_wgt=0;
    for ( row = 0 ; row < ily ; row++ )
    {
        for ( col = 0 ; col < ilx ; col++ )
        {
      weight[col][row] /= norm;
          sum_wgt += weight[col][row]*sinfo_psf[col][row];
    }
    }
    sinfo_msg_debug("sum_psf=%f sum_wgt=%f norm=%f",sum_psf,sum_wgt,norm);
    cpl_image_delete(averagedIm) ;
    if ( norm == 0. )
    {
        sinfo_msg_error(" normalization sum is zero\n") ;
        cpl_image_delete(retIm) ;
        return NULL ;
    }

    /* limit the extraction region to the Gaussian, center +- fwhmx/y * 
                 cos(theta)  */
    /*
    sinfo_msg("fit_par: %f %f %f %f %f %f %f", 
              fit_par[0],fit_par[1],fit_par[2],fit_par[3],
              fit_par[4],fit_par[5],fit_par[6]);
    sinfo_msg("fwhm_factor=%f",fwhm_factor);
    */

    if(fabs(fit_par[6]) > PI_NUMB/4) {
      fit_par[6]=0;
    }
    first_col = (int) (fit_par[0] - 
                       fwhm_factor*fit_par[4]*cos((double)fit_par[6])) ;
    first_col = (first_col > 2 ) ? first_col : 2;

    last_col =  (int) (fit_par[0] + 
                       fwhm_factor*fit_par[4]*cos((double)fit_par[6])) ;
    last_col = (last_col < 63 ) ? last_col : 63;

    first_row = (int) (fit_par[1] - 
                       fwhm_factor*fit_par[5]*cos((double)fit_par[6])) ;
    first_row = (first_row > 2 ) ? first_row : 2;
    last_row =  (int) (fit_par[1] + 
                       fwhm_factor*fit_par[5]*cos((double)fit_par[6])) ;
    last_row = (last_row < 63 ) ? last_row : 63;

    
    if(first_col > last_col) {
      tmp_val=last_col;
      last_col=first_col;
      first_col=tmp_val;
    }

    if(first_row > last_row) {
      tmp_val=last_row;
      last_col=first_row;
      first_col=tmp_val;
    }
    if(abs(first_col- last_col) < 1) {
      first_col -=1;
      last_col +=1;
    }
    if(abs(first_row- last_row) < 1) {
      first_row -=1;
      last_row +=1;
    }

    if ( first_col < 0 || first_row < 0 || last_col >= ilx || last_row >= ily )
    {
        sinfo_msg_error(" star badly centered in FOV!") ;
        cpl_image_delete(retIm) ;
        return NULL ;
    }


    cpl_table_new_column(*spectrum,"wavelength", CPL_TYPE_FLOAT);
    /* cpl_table_new_column(*spectrum,"intensity" , CPL_TYPE_FLOAT); */
    cpl_table_new_column(*spectrum,"counts_tot" , CPL_TYPE_FLOAT);
    cpl_table_new_column(*spectrum,"counts_bkg" , CPL_TYPE_FLOAT);
    cpl_table_new_column(*spectrum,"bkg_tot" , CPL_TYPE_FLOAT);

    if(qc_info==1) {
       cpl_table_new_column(*spectrum,"AMP" , CPL_TYPE_FLOAT);
       cpl_table_new_column(*spectrum,"XC" , CPL_TYPE_FLOAT);
       cpl_table_new_column(*spectrum,"YC" , CPL_TYPE_FLOAT);
       cpl_table_new_column(*spectrum,"BKG" , CPL_TYPE_FLOAT);
       cpl_table_new_column(*spectrum,"FWHMX" , CPL_TYPE_FLOAT);
       cpl_table_new_column(*spectrum,"FWHMY" , CPL_TYPE_FLOAT);
       cpl_table_new_column(*spectrum,"ANGLE" , CPL_TYPE_FLOAT);
    }
    plist=cpl_propertylist_load(name,0);
    cenpix = sinfo_pfits_get_crpix3(plist);
    cenLambda = sinfo_pfits_get_crval3(plist);
    dispersion = sinfo_pfits_get_cdelt3(plist);
    cpl_propertylist_delete(plist);
    lambda_start=cenLambda-cenpix*dispersion;

    sinfo_msg_debug("frow %d lrow %d fcol %d lcol %d",
                     first_row, last_row, first_col, last_col);
    /* go through the planes */
    podata=cpl_image_get_data_float(retIm);
    for ( z = 0 ; z < inp ; z++ )
    {
        cpl_image* i_img=cpl_imagelist_get(cube,z);
        float* pidata=cpl_image_get_data_float(i_img);
        float weighted_sum = 0. ;
        float counts_tot=0.;
        float counts_bkg=0.;

        float bkg_tot=0.;

        if(qc_info==1) {
           sinfo_new_fit_2d_gaussian(i_img,gfit_par,
                                     gderv_par,gmpar,llx,lly,
                                     halfbox_x,halfbox_y,check2);
	}

        for ( row = first_row ; row <= last_row ; row++ )
        {
            for ( col = first_col ; col < last_col ; col++ )
            {
                if ( !isnan(pidata[col+row*ilx]) )
                {
          
                    weighted_sum += weight[col][row] * (pidata[col+row*ilx] - 
                                    fit_par[3]);
            
                    counts_bkg += (pidata[col+row*ilx] - fit_par[3]);
                    counts_tot += (pidata[col+row*ilx]);
                    bkg_tot += fit_par[3];
            
                } 
            }
        }    

        if (weighted_sum == 0.)
        {
            weighted_sum = ZERO ;
            counts_tot = ZERO;
            counts_bkg = ZERO;
            bkg_tot = ZERO;

        }
        else
        {
      /*
            weighted_sum /= norm ;
      */
      
      
        }

        podata[z] = weighted_sum ;
        float lambda=lambda_start+z*dispersion;
        cpl_table_set_float(*spectrum,"wavelength" ,z,lambda);
        /* cpl_table_set_float(*spectrum,"intensity" ,z,weighted_sum); */
        cpl_table_set_float(*spectrum,"counts_tot" ,z,counts_tot);
        cpl_table_set_float(*spectrum,"counts_bkg" ,z,counts_bkg);
        cpl_table_set_float(*spectrum,"bkg_tot" ,z,bkg_tot);
        sinfo_msg_debug("w=%f I=%f b=%f a=%f",
                         lambda,counts_tot,counts_bkg,bkg_tot);
        if(qc_info==1) {
           cpl_table_set_float(*spectrum,"AMP" ,z,gfit_par[0]);
           cpl_table_set_float(*spectrum,"XC" ,z,gfit_par[1]);
           cpl_table_set_float(*spectrum,"YC" ,z,gfit_par[2]);
           cpl_table_set_float(*spectrum,"BKG" ,z,gfit_par[3]);
           cpl_table_set_float(*spectrum,"FWHMX" ,z,gfit_par[4]);
           cpl_table_set_float(*spectrum,"FWHMY" ,z,gfit_par[5]);
           cpl_table_set_float(*spectrum,"ANGLE" ,z,gfit_par[6]);
	}

    }

    sinfo_new_destroy_2Ddoublearray(&sinfo_psf,ilx) ;
    sinfo_new_destroy_2Ddoublearray(&weight,ilx) ;

    return retIm ;
}

/**
@brief extracts a sky spectrum from a data cube
@name sinfo_new_extract_sky_from_cube()
@param cube: reduced cube from sky spider observation
@param loReject, 
@param hiReject: fraction (percentage) of the extreme high and low
                 sky spectrum values that are rejected before 
                 averaging the found sky spectra.
@param position: end pixel positions of the straight line in the image
                 dividing the sky from the object pixels.
@param tolerance: pixel tolerance which are not considered and subtracted from
                  the diagonal line to be sure to get a clean sky, default: 2  
@param posindicator: indicates in which sinfo_edge of the field of view 
                     the sky is projected. output of 
                     spiffi_get_spiderposindex() in fitshead.c
@return resulting averaged sky spectrum
@doc extracts a sky spectrum from a reduced sky spider observation, that means 
     from a data cube. Therefore, the position of the sky within the field of 
     view must be first read from the fits header. A pixel tolerance is 
     subtracted. The found sky spectra are averaged by rejecting the extreme 
     high and low values.
*/

Vector * sinfo_new_extract_sky_from_cube( cpl_imagelist * cube,
                             float     loReject,
                             float     hiReject,
                             int     * position,
                             int       tolerance,
                             int       posindicator )
{
    Vector * spectrum ;
    int x, y, z ;
    int n_sky ;
    int x_low , x_high ;
    int y_low , y_high ;
    int hi_x, lo_x ;

    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;

    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( NULL == cube )
    {
        sinfo_msg_error(" no cube given!\n") ;
        return NullVector ;
    }
    if ( loReject < 0. || hiReject < 0. || loReject + hiReject >= 90. )
    {
        sinfo_msg_error("wrong or unrealistic loReject and hiReject values!") ;
        return NullVector ;
    }
    if ( position == NULL)
    {
        sinfo_msg_error(" no position array given!") ;
        return NullVector ;
    }
    if ( position[0] < 0 || position[1] < 0 || 
         position[0] > ilx  || position[1] > ily )
    {
        sinfo_msg_error(" wrong position of sky spider!") ;
        return NullVector ;
    }
    if ( tolerance < 0 || tolerance >= ilx )
    {
        sinfo_msg_error(" wrong tolerance given!") ;
        return NullVector ;
    }
    if ( posindicator == 0 )
    {
        sinfo_msg_error(" no sinfo_edge indicator given!") ;
        return NullVector ;
    }

    /* determine the edge of the image where the sky spectra are placed */
    switch(posindicator)
    {
        /* lower right sinfo_edge */
        case 1:
            x_low  = position[0] + tolerance ;
            x_high = ilx ;
            y_low  = 0 ;
            y_high = position[1] - tolerance ;
            break ;
        /* upper right sinfo_edge */
        case 2:
            x_low  = position[0] + tolerance ;
            x_high = ilx ;
            y_low  = position[1] + tolerance ;
            y_high = ily ;
            break ;
        /* upper left sinfo_edge */
        case 3:
            x_low  = 0 ;
            x_high = position[0] - tolerance ;
            y_low  = position [1] + tolerance ;
            y_high = ily ;
            break ;
        default:
            sinfo_msg_error(" wrong position indicator index!") ;
            return NullVector ;
            break ;
    }
    if ( x_low >= ilx || x_high < 1 || y_low >= ily || y_high < 1 )
    {
        sinfo_msg_error(" tolerance too high!") ;
        return NullVector ;
    }
    if ( x_high - x_low != y_high - y_low )
    {
        sinfo_msg_error(" sky sinfo_edge is not a diagonal line!\n") ;
        return NullVector ;
    }

    /* determine the number of sky pixels in one image plane, take only 
       the full sky pixels which are not cut by the diagonal line */
    n_sky = (x_high - x_low) * (x_high - x_low - 1) / 2 ;
    if ( n_sky <= 0 )
    {
        sinfo_msg_error(" no sky spectrum in found in cube!") ;
        return NullVector ;
    }
    if ( n_sky == 1 )
    {
        sinfo_msg_warning(" only one sky spectrum is taken, no averaging!") ;
    }

    /* allocate memory for the output spectrum */
    if ( NullVector == (spectrum = sinfo_new_vector(inp)) )
    {
        sinfo_msg_error(" could not allocate memory!") ;
        return NullVector ;
    }

    /* go through the image planes */
    for ( z = 0 ; z < inp ; z++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,z);
      pidata=cpl_image_get_data_float(i_img);
        /* allocate memory for the sky pixels in one image plane */
        float* to_average=NULL;
        if (NULL == (to_average = (float*) cpl_calloc(n_sky, sizeof (float))))
        {
            sinfo_msg_error(" could not allocate memory!") ;
            sinfo_new_destroy_vector(spectrum) ;
            return NullVector ;
        }
        int n = 0 ;
        switch(posindicator)
        {
            /* lower right sinfo_edge */
            case 1:
                lo_x = x_low ;
                for ( y = y_low ; y < y_high - 1 ; y++ )
                {
                    lo_x++ ;
                    for ( x = lo_x ; x < x_high ; x++ )
                    {
                        to_average[n] = pidata[x+y*ilx] ;
                        n++ ;
                    }
                }
                break ;
            /* lower left sinfo_edge */
            case 2:
                hi_x = x_high ;
                for ( y = y_low ; y < y_high - 1 ; y++ )
                {
                    hi_x-- ;
                    for ( x = x_low ; x < hi_x ; x++ )
                    {
                        to_average[n] = pidata[x+y*ilx] ;
                        n++ ;
                    }
                }
                break ;
            /* upper right sinfo_edge */
            case 3:
                lo_x = x_high ;
                for ( y = y_low+1 ; y < y_high ; y++ )
                {
                    lo_x-- ;
                    for ( x = lo_x ; x < x_high ; x++ )
                    {
                        to_average[n] = pidata[x+y*ilx] ;
                        n++ ;
                    }
                }
                break ;
            /* upper left sinfo_edge */
            case 4:
                hi_x = x_low ;
                for ( y = y_low+1 ; y < y_high ; y++ )
                {
                    hi_x++ ;
                    for ( x = x_low ; x < hi_x ; x++ )
                    {
                        to_average[n] = pidata[x+y*ilx] ;
                        n++ ;
                    }
                }
                break ;
            default:
                sinfo_msg_error(" wrong position indicator index!\n") ;
                cpl_free(to_average) ;
                return NullVector ;
                break ;
        }
        if ( n != n_sky )
        {
            sinfo_msg_warning("number of stored sky image pixels does "
                              "not equal number of computed sky pixels!") ;
        }

        /* now take a clean mean of the sky "image" */
        float cleanMean = sinfo_new_clean_mean (to_average, n, loReject, hiReject) ;
        if (cleanMean == FLT_MAX)
        {
            sinfo_msg_error(" could not take a clean mean!\n") ;
            sinfo_new_destroy_vector(spectrum) ;
            cpl_free(to_average) ;
            return NullVector ;
        }
        spectrum->data[z] = cleanMean ;
        cpl_free (to_average) ;
    }
     
    return spectrum ;
}

/**
@brief summing routine for a reduced data to get a better spectral S/N only 
       for a rectangular aperture
@name sinfo_new_sum_rectangle_of_cube_spectra()
@param cube: 1 allocated cube, 
@param llx, 
@param lly, 
@param urx, 
@param ury: lower left and upper right position of rectangle in x-y plane ,
                                            image coordinates 0...
@return result spectrum sinfo_vector
 */

Vector * sinfo_new_sum_rectangle_of_cube_spectra( cpl_imagelist * cube,
                                     int llx,
                                     int lly,
                                     int urx,
                                     int ury )
{
    Vector          * sum ;
    int             i, j, k, l ;
    int             recsize ;
    int ilx=0;
    int ily=0;
    int inp=0;




    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( cube == NULL || inp < 1 )
    {
        sinfo_msg_error (" no cube to take the mean of his spectra\n") ;
        return NullVector ;
    }

    if ((llx<0) || (llx>=ilx) ||
        (urx<0) || (urx>=ilx) ||
        (lly<0) || (lly>=ily) ||
        (ury<0) || (ury>=ily) ||
        (llx>=urx) || (lly>=ury))
    {
        sinfo_msg_error(" invalid rectangle coordinates:") ;
        sinfo_msg_error("lower left is [%d %d] upper right is [%d %d]", 
                         llx, lly, urx, ury) ;
        return NullVector ;
    }

    recsize = (urx - llx + 1) * (ury - lly + 1) ;

    /* allocate a new sinfo_vector to store the average spectral values */
    if (NULL == (sum = sinfo_new_vector (inp)) )
    {
        sinfo_msg_error (" cannot allocate a new sinfo_vector") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane rectangle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < inp ; i++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,i);
      float* pidata=cpl_image_get_data_float(i_img);
        int m = 0 ;
        pixelvalue* local_rectangle = (pixelvalue *) cpl_calloc (recsize, 
                                         sizeof (pixelvalue*));

        for ( j = lly ; j <= ury ; j++ )
        {
            for ( k = llx ; k <= urx ; k++ )
            {
                local_rectangle[m] = pidata[k + j * ilx] ;
                m ++ ;
            }
        }
        for ( l = 0 ; l < recsize ; l++ )
        {
            if ( isnan(local_rectangle[l]) )
            {
                continue ;
            }
            sum -> data[i] += local_rectangle[l] ;
        }
        cpl_free ( local_rectangle ) ;
    }
    return sum ;
}

/**
@brief summing routine for a reduced data to get a better spectral S/N 
       only for a circular aperture.
@name sinfo_new_sum_circle_of_cube_spectra()
@param cube: 1 allocated cube, 
@param centerx, 
@param centery: center pixel of circular aperture in image coordinates
@param radius: integer radius of circular aperture
@return result spectrum sinfo_vector 
*/

Vector * sinfo_new_sum_circle_of_cube_spectra( cpl_imagelist * cube,
                                  int       centerx,
                                  int       centery,
                                  int       radius )
{
    Vector          * sum ;
    int             i, j, k, l, n ;
    int             circsize ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;


    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( cube == NULL || inp < 1 )
    {
        sinfo_msg_error (" no cube to take the mean of his spectra\n") ;
        return NullVector ;
    }

    if ((centerx+radius>=ilx) ||
        (centery+radius>=ily) ||
        (centerx-radius<0) ||
        (centery-radius<0))
    {
        sinfo_msg_error(" invalid circular coordinates") ;
        return NullVector ;
    }

    n = 0 ;
    for ( j = centery - radius ; j <= centery + radius ; j++ )
    {
        for ( k = centerx - radius ; k <= centerx + radius ; k++ )
        {
            if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                           radius*radius )
            {
                n ++ ;
            }
        }
    }
    if (n == 0)
    {
        sinfo_msg_error (" no data points found!") ;
        return NullVector ;
    }
    circsize = n ;

    /* allocate a new sinfo_vector to store the average spectral values */
    if (NULL == (sum = sinfo_new_vector (inp)) )
    {
        sinfo_msg_error ("  cannot allocate a new sinfo_vector") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane circle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < inp ; i++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,i);
      pidata=cpl_image_get_data_float(i_img);
        int m = 0 ;
        pixelvalue* circle = (pixelvalue *) cpl_calloc (circsize, sizeof (pixelvalue*));

        for ( j = centery - radius ; j <= centery + radius ; j++ )
        {
            for ( k = centerx - radius ; k <= centerx + radius ; k++ )
            {
                if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                     radius*radius )
                {
                    circle[m] = pidata[k + j * ilx] ;
                    m ++ ;
                }
            }
        }

        for ( l = 0 ; l < circsize ; l++ )
        {
            if ( isnan(circle[l]) )
            {
                continue ;
            }
            sum -> data[i] += circle[l] ;
        }
        cpl_free (circle) ;
    }
    return sum ;
}

/**
@brief averaging routine for a reduced data to get a better 
       spectral S/N only for a rectangular aperture.
@name sinfo_new_mean_rectangle_of_cube_spectra()
@param cube: 1 allocated cube, 
@param llx, 
@param lly, 
@param urx, 
@param ury: lower left and upper right position of rectangle in x-y plane ,
            image coordinates 0...
@return result spectrum sinfo_vector 
*/

Vector * sinfo_new_mean_rectangle_of_cube_spectra( cpl_imagelist * cube,
                                     int llx,
                                     int lly,
                                     int urx,
                                     int ury )
{
    Vector          * mean ;
    int             i, j, k, l ;
    int             recsize ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;

    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( cube == NULL || inp < 1 )
    {
        sinfo_msg_error ("  no cube to take the mean of his spectra\n") ;
        return NullVector ;
    }

    if ((llx<0) || (llx>=ilx) ||
        (urx<0) || (urx>=ilx) ||
        (lly<0) || (lly>=ily) ||
        (ury<0) || (ury>=ily) ||
        (llx>=urx) || (lly>=ury))
    {
        sinfo_msg_error("  invalid rectangle coordinates:") ;
        sinfo_msg_error("lower left is [%d %d] upper right is [%d %d]",
                 llx, lly, urx, ury) ;
        return NullVector ;
    }

    recsize = (urx - llx + 1) * (ury - lly + 1) ;

    /* allocate a new sinfo_vector to store the average spectral values */
    if (NULL == (mean = sinfo_new_vector (inp)) )
    {
        sinfo_msg_error (" cannot allocate a new sinfo_vector") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane rectangle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < inp ; i++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,i);
      pidata=cpl_image_get_data_float(i_img);
        int m = 0 ;
        pixelvalue* local_rectangle = (pixelvalue *) cpl_calloc (recsize, 
                           sizeof (pixelvalue*));

        for ( j = lly ; j <= ury ; j++ )
        {
            for ( k = llx ; k <= urx ; k++ )
            {
                local_rectangle[m] = pidata[k + j * ilx] ;
                m ++ ;
            }
        }
        int nv = 0 ;
        for ( l = 0 ; l < recsize ; l++ )
        {
            if ( isnan(local_rectangle[l]) )
            {
                continue ;
            }
            mean -> data[i] += local_rectangle[l] ;
            nv ++;
        }
        if ( nv == 0 )
        {
            mean -> data[i] = ZERO ;
        }
        else
        {
            mean -> data[i] /= nv ;
        }
        cpl_free ( local_rectangle ) ;
    }
    return mean ;
}

/**
@brief averaging routine for a reduced data to get a better 
       spectral S/N only for a circular aperture.
@name sinfo_new_mean_circle_of_cube_spectra()
@param cube: 1 allocated cube, 
@param centerx, 
@param centery: center pixel of circular aperture in image coordinates
@param radius: integer radius of circular aperture
@return result spectrum sinfo_vector
*/

Vector * 
sinfo_new_mean_circle_of_cube_spectra( cpl_imagelist * cube,
                                  int       centerx,
                                  int       centery,
                                  int       radius )
{
    Vector          * mean ;
    int             i, j, k, l, n ;
    int             circsize ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;


    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( cube == NULL || inp < 1 )
    {
        sinfo_msg_error ("no cube to take the mean of his spectra") ;
        return NullVector ;
    }

    if ((centerx+radius>=ilx) ||
        (centery+radius>=ily) ||
        (centerx-radius<0) ||
        (centery-radius<0))
    {
        sinfo_msg_error(" invalid circular coordinates") ;
        return NullVector ;
    }

    n = 0 ;
    for ( j = centery - radius ; j <= centery + radius ; j++ )
    {
        for ( k = centerx - radius ; k <= centerx + radius ; k++ )
        {
            if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                 radius*radius )
            {
                n ++ ;
            }
        }
    }
    if (n == 0)
    {
        sinfo_msg_error (" no data points found!\n") ;
        return NullVector ;
    }
    circsize = n ;

    /* allocate a new sinfo_vector to store the average spectral values */
    if (NULL == (mean = sinfo_new_vector (inp)) )
    {
        sinfo_msg_error (" cannot allocate a new sinfo_vector \n") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane circle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < inp ; i++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,i);
      pidata=cpl_image_get_data_float(i_img);
        int m = 0 ;
        pixelvalue* circle = (pixelvalue *) cpl_calloc (circsize, sizeof (pixelvalue*));

        for ( j = centery - radius ; j <= centery + radius ; j++ )
        {
            for ( k = centerx - radius ; k <= centerx + radius ; k++ )
            {
                if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                     radius*radius )
                {
                    circle[m] = pidata[k + j * ilx] ;
                    m ++ ;
                }
            }
        }

        int nv = 0 ;
        for ( l = 0 ; l < circsize ; l++ )
        {
            if ( isnan(circle[l]) )
            {
                continue ;
            }
            mean -> data[i] += circle[l] ;
            nv ++;
        }
        if ( nv == 0 )
        {
            mean -> data[i] = ZERO ;
        }
        else
        {
            mean -> data[i] /= nv ;
        }

        cpl_free (circle) ;
    }
    return mean ;
}

/**
@brief computes a blackbody spectral intensity 
       distribution (W/(m^2 lambda ster)) 
@name sinfo_new_blackbody_spectrum()
@param templateSpec: spectrum of a standard star (1-d image with fits header)
@param temp: blackbody temperature in Kelvin (standard Star temp), 
@return resulting spectrum sinfo_vector 
*/

Vector * sinfo_new_blackbody_spectrum( char * templateSpec, double temp )
{
    Vector * retSpec ;
    int n ;
    double cenpix ;
    int npix ;
    double cenLambda ;
    double firstLambda ;
    double disp ;
    cpl_propertylist* plist=NULL;

    if ( NULL == templateSpec )
    {
        sinfo_msg_error (" now input image given!\n") ;
        return NULL ;
    }
    if ( temp < 0. )
    {
        sinfo_msg_error (" wrong temperature given!\n") ;
        return NULL ;
    }
    /* get the fits header information needed */
    if ((cpl_error_code)((plist=cpl_propertylist_load(templateSpec,0))==NULL)){
      sinfo_msg_error( "getting header from frame %s",templateSpec);
      cpl_propertylist_delete(plist) ;
      return NULL ;
    }


    cenpix = sinfo_pfits_get_crpix2(plist);
    if(cpl_error_get_code() != CPL_ERROR_NONE) {
        sinfo_msg_error (" cannot get CRPIX2\n") ;
        sinfo_free_propertylist(&plist) ;
        return NULL ;
    }

    cenLambda = sinfo_pfits_get_crval2(plist);
    if(cpl_error_get_code() != CPL_ERROR_NONE) {
        sinfo_msg_error (" cannot get CRVAL2\n") ;
        sinfo_free_propertylist(&plist) ;
        return NULL ;
    }
    disp = sinfo_pfits_get_cdelt2(plist);
    if(cpl_error_get_code() != CPL_ERROR_NONE) {
        sinfo_msg_error (" cannot get CDELT2\n") ;
        sinfo_free_propertylist(&plist) ;
        return NULL ;
    }
    npix = sinfo_pfits_get_naxis2(plist);
    if(cpl_error_get_code() != CPL_ERROR_NONE) {
        sinfo_msg_error (" cannot get NAXIS2\n") ;
        sinfo_free_propertylist(&plist) ;
        return NULL ;
    }
    sinfo_free_propertylist(&plist) ;


    if (NULL == (retSpec = sinfo_new_vector (npix)))
    {
        sinfo_msg_error (" could not allocate memory!\n") ;
        return NULL ;
    }
    
    /* shift from fits to image coordinates */
    cenpix-- ;
  
    firstLambda = cenLambda - cenpix * disp ;
    for ( n = 0 ; n < npix ; n++ )
    {
        double lambda = firstLambda + disp * (double)n ;
        /* convert from microns to m */

        lambda /= 1.0e6 ;
        /* replaced 1./(exp(PLANCK*SPEED_OF_LIGHT/(lambda*BOLTZMANN*temp)) - 1.) 
         * by 1./(expm1(PLANCK*SPEED_OF_LIGHT/(lambda*BOLTZMANN*temp))) 
         * for better accuracy if argument of exponential is small 
         */
        double denom = 1./(expm1(PLANCK*SPEED_OF_LIGHT/(lambda*BOLTZMANN*temp))) ;
        double intens = 2.*PI_NUMB*PLANCK*SPEED_OF_LIGHT*SPEED_OF_LIGHT / 
                 pow(lambda, 5) * denom ;
        retSpec->data[n] = intens ;   
    }
    double norm = retSpec->data[npix/2] ;
    for ( n = 0 ; n < npix ; n++ )
    {
        retSpec->data[n] /= norm ;
    }
    
    return retSpec ;
}


/**
@brief sinfo_median routine for a reduced data to get a better 
       spectral S/N only for a rectangular aperture.
@name sinfo_new_median_rectangle_of_cube_spectra()
@param cube: 1 allocated cube, 
@param llx, 
@param lly, 
@param urx, 
@param ury: lower left and upper right position of rectangle in x-y plane ,
            image coordinates 0...
@return result spectrum sinfo_vector
*/

Vector * sinfo_new_median_rectangle_of_cube_spectra( cpl_imagelist * cube,
                                             int llx,
                                             int lly,
                                             int urx,
                                             int ury )
{
    Vector          * med ;
    int             i, j, k ;
    int             recsize ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;

    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( cube == NULL || inp < 1 )
    {
        sinfo_msg_error (" no cube to take the mean of his spectra\n") ;
        return NullVector ;
    }

    if ((llx<0) || (llx>=ilx) ||
        (urx<0) || (urx>=ilx) ||
        (lly<0) || (lly>=ily) ||
        (ury<0) || (ury>=ily) ||
        (llx>=urx) || (lly>=ury))
    {
        sinfo_msg_error(" invalid rectangle coordinates:") ;
        sinfo_msg_error("lower left is [%d %d] upper right is [%d %d]", 
                        llx, lly, urx, ury) ;
        return NullVector ;
    }

    recsize = (urx - llx + 1) * (ury - lly + 1) ;

    /* allocate a new sinfo_vector to store the average spectral values */
    if (NULL == (med = sinfo_new_vector (inp)) )
    {
        sinfo_msg_error (" cannot allocate a new sinfo_vector \n") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane rectangle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < inp ; i++ )
    {

      cpl_image* i_img=cpl_imagelist_get(cube,i);
      pidata=cpl_image_get_data_float(i_img);
      int m = 0 ;
      pixelvalue* local_rectangle=(pixelvalue *)cpl_calloc(recsize, sizeof (pixelvalue*));

        for ( j = lly ; j <= ury ; j++ )
        {
            for ( k = llx ; k <= urx ; k++ )
            {
                if ( isnan(pidata[k+j*ilx]) )
                {
                    continue ;
                }
                else
                {
                    local_rectangle[m] = pidata[k + j * ilx] ;
                    m ++ ;
                }
            }
        }
        if ( m == 0 )
        {
            med->data[i] = 0. ;
        }
        else
        {
            med->data[i] = sinfo_new_median(local_rectangle, m) ;
        }
        cpl_free ( local_rectangle ) ;
    }
    return med ;
}

/**
@brief sinfo_median routine for a reduced data to get a better 
       spectral S/N only for a circular aperture.
@name sinfo_new_median_circle_of_cube_spectra()
@param cube: 1 allocated cube, 
@param centerx, 
@param centery: center pixel of circular aperture in image coordinates
@param radius: integer radius of circular aperture
@return result spectrum sinfo_vector 
*/

Vector * sinfo_new_median_circle_of_cube_spectra( cpl_imagelist * cube,
                                  int       centerx,
                                  int       centery,
                                  int       radius )
{
    Vector          * med ;
    int             i, j, k, l, n ;
    int             circsize ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;

    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( cube == NULL || inp < 1 )
    {
        sinfo_msg_error (" no cube to take the mean of his spectra\n") ;
        return NullVector ;
    }

    if ((centerx+radius>=ilx) ||
        (centery+radius>=ily) ||
        (centerx-radius<0) ||
        (centery-radius<0))
    {
        sinfo_msg_error(" invalid circular coordinates") ;
        return NullVector ;
    }

    n = 0 ;
    for ( j = centery - radius ; j <= centery + radius ; j++ )
    {
        for ( k = centerx - radius ; k <= centerx + radius ; k++ )
        {
            if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                  radius*radius )
            {
                n ++ ;
            }
        }
    }
    if (n == 0)
    {
        sinfo_msg_error (" no data points found!") ;
        return NullVector ;
    }
    circsize = n ;

    /* allocate a new sinfo_vector to store the average spectral values */
    if (NULL == (med = sinfo_new_vector (inp)) )
    {
        sinfo_msg_error (" cannot allocate a new sinfo_vector") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane circle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < inp ; i++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,i);
      pidata=cpl_image_get_data_float(i_img);
        int m = 0 ;
        pixelvalue* circle = (pixelvalue *) cpl_calloc (circsize, sizeof (pixelvalue*));

        for ( j = centery - radius ; j <= centery + radius ; j++ )
        {
            for ( k = centerx - radius ; k <= centerx + radius ; k++ )
            {
                if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                      radius*radius )
                {
                    circle[m] = pidata[k + j * ilx] ;
                    m ++ ;
                }
            }
        }

        int nv = 0 ;
        for ( l = 0 ; l < circsize ; l++ )
        {
            if ( isnan(circle[l]) )
            {
                continue ;
            }
            med -> data[i] += circle[l] ;
            nv ++;
        }
        if ( nv == 0 )
        {
            med->data[i] = 0. ;
        }
        else
        {
            med->data[i] = sinfo_new_median(circle, nv) ; 
        }
        cpl_free (circle) ;
    }
    return med ;
}

/**
@brief clean averaging routine for a reduced data to get a 
       better spectral S/N only for a rectangular aperture.
@name sinfo_new_cleanmean_rectangle_of_cube_spectra()
@param cube: 1 allocated cube, 
@param llx, 
@param lly, 
@param urx, 
@param ury: lower left and upper right position of rectangle in x-y plane ,
            image coordinates 0...
@return result spectrum sinfo_vector 
*/

Vector * 
sinfo_new_cleanmean_rectangle_of_cube_spectra( cpl_imagelist * cube,
                                          int llx,
                                          int lly,
                                          int urx,
                                          int ury,
                                          float lo_reject,
                                          float hi_reject )
{
    Vector          * clean ;
    int             i, j, k ;
    int             recsize ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;


    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( cube == NULL || inp < 1 )
    {
        sinfo_msg_error (" no cube to take the mean of his spectra\n") ;
        return NullVector ;
    }

    if ((llx<0) || (llx>=ilx) ||
        (urx<0) || (urx>=ilx) ||
        (lly<0) || (lly>=ily) ||
        (ury<0) || (ury>=ily) ||
        (llx>=urx) || (lly>=ury))
    {
        sinfo_msg_error(" invalid rectangle coordinates:") ;
        sinfo_msg_error("lower left is [%d %d] upper right is [%d %d]",
                 llx, lly, urx, ury) ;
        return NullVector ;
    }

    recsize = (urx - llx + 1) * (ury - lly + 1) ;

    /* allocate a new sinfo_vector to store the average spectral values */
    if (NULL == (clean = sinfo_new_vector (inp)) )
    {
        sinfo_msg_error (" cannot allocate a new sinfo_vector") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane rectangle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < inp ; i++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,i);
      pidata=cpl_image_get_data_float(i_img);
      int m = 0 ;
      pixelvalue* local_rectangle=(pixelvalue *) cpl_calloc(recsize, sizeof (pixelvalue*));

        for ( j = lly ; j <= ury ; j++ )
        {
            for ( k = llx ; k <= urx ; k++ )
            {
                if ( isnan(pidata[k+j*ilx]) )
                {
                    continue ;
                }
                else
                {
                    local_rectangle[m] = pidata[k + j * ilx] ;
                    m ++ ;
                }
            }
        }
        if ( m == 0 )
        {
            clean->data[i] = 0. ;
        }
        else
        {
            clean->data[i] = sinfo_new_clean_mean(local_rectangle, m, 
                                                  lo_reject, hi_reject) ;
        }
        cpl_free ( local_rectangle ) ;
    }
    return clean ;
}

/**
@brief clean averaging routine for a reduced data to get a better 
       spectral S/N only for a circular aperture.
@name sinfo_new_cleanmean_circle_of_cube_spectra()
@param cube: 1 allocated cube, 
@param centerx, 
@param centery: center pixel of circular aperture in image coordinates
@param radius: integer radius of circular aperture
@return result spectrum sinfo_vector 
*/

Vector * 
sinfo_new_cleanmean_circle_of_cube_spectra( cpl_imagelist * cube,
                                  int       centerx,
                                  int       centery,
                                  int       radius,
                                  float     lo_reject,
                                  float     hi_reject )
{
    Vector          * clean ;
    int             i, j, k, l, n ;
    int             circsize ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;

    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( cube == NULL || inp < 1 )
    {
        sinfo_msg_error (" no cube to take the mean of his spectra\n") ;
        return NullVector ;
    }

    if ((centerx+radius>=ilx) ||
        (centery+radius>=ily) ||
        (centerx-radius<0) ||
        (centery-radius<0))
    {
        sinfo_msg_error(" invalid circular coordinates") ;
        return NullVector ;
    }

    n = 0 ;
    for ( j = centery - radius ; j <= centery + radius ; j++ )
    {
        for ( k = centerx - radius ; k <= centerx + radius ; k++ )
        {
            if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                  radius*radius )
            {
                n ++ ;
            }
        }
    }
    if (n == 0)
    {
        sinfo_msg_error (" no data points found!\n") ;
        return NullVector ;
    }
    circsize = n ;

    /* allocate a new sinfo_vector to store the average spectral values */
    if (NULL == (clean = sinfo_new_vector (inp)) )
    {
        sinfo_msg_error (" cannot allocate a new sinfo_vector \n") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane circle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < inp ; i++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,i);
      pidata=cpl_image_get_data_float(i_img);
        int m = 0 ;
        pixelvalue* circle = (pixelvalue *) cpl_calloc (circsize, sizeof (pixelvalue*));

        for ( j = centery - radius ; j <= centery + radius ; j++ )
        {
            for ( k = centerx - radius ; k <= centerx + radius ; k++ )
            {
                if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                     radius*radius )
                {
                    circle[m] = pidata[k + j * ilx] ;
                    m ++ ;
                }
            }
        }

        int nv = 0 ;
        for ( l = 0 ; l < circsize ; l++ )
        {
            if ( isnan(circle[l]) )
            {
                continue ;
            }
            clean -> data[i] += circle[l] ;
            nv ++;
        }
        if ( nv == 0 )
        {
            clean->data[i] = 0. ;
        }
        else
        {
            clean->data[i] = sinfo_new_clean_mean(circle, nv, 
                                                  lo_reject, hi_reject) ; 
        }
        cpl_free (circle) ;
    }
    return clean ;
}

/**
@brief shifts an array by a sub-pixel shift value using a tanh 
       interpolation kernel
@name sinfo_new_shift_array()
@param input: input array, 
@param n_elements: number of elements in input array
@param shift: sub-pixel shift value (must be < 1.)
@param ker: interpolation kernel
@return resulting float array  
*/

float * 
sinfo_new_shift_array ( float * input, int n_elements, 
                        float shift, double * ker ) 
{
    float  *         shifted ;
    int              samples = KERNEL_SAMPLES ;
    int           i ;
    int              tabx ;
    float            value ;
    /*size_t           pos ;*/
    register float * pix ;

    /* error handling: test entries */
    if (input==NULL) 
    {
        sinfo_msg_error(" no input array given!\n") ;
        return NULL ;
    }
    if (n_elements<=0) 
    {
        sinfo_msg_error(" wrong number of elements in input array given!\n") ;
        return NULL ;
    }

    shifted    = (float*) cpl_calloc(n_elements, sizeof(float)) ;

    /* Shifting by a zero offset returns a copy of the input image */
    if ((fabs(shift)<1e-2))
    {
        for (i = 0 ; i <  n_elements ; i++ )
        {
            shifted[i] = input[i] ;
        }
        return shifted ;
    }
    
    int mid = (int)samples/(int)2 ;

    for (i=1 ; i<  n_elements-2 ; i++) 
    {
        float fx = (float)i+shift ;
        int px = sinfo_new_nint(fx) ;
        float rx = fx - (float)px ;
        pix = input ;

        if ((px>=1) && (px<(n_elements-2))) 
        {
            tabx = (int)(fabs((float)mid * rx)) ;
            /* exclude blank (ZERO) pixels from interpolation */
            if (isnan(pix[i]))
            {
                value = ZERO ;
            }
            else
            {
                if (isnan(pix[i-1]))
                {
                    pix[i-1] = 0. ;
                }
                if (isnan(pix[i+1]))
                {
                    pix[i+1] = 0. ;
                }
                if (isnan(pix[i+2]))
                {
                    pix[i+2] = 0. ;
                }

                /*
                 * Sum up over 4 closest pixel values,
                 * weighted by interpolation kernel values
                 */
                value =     pix[i-1] * ker[mid+tabx] +
                            pix[i] *   ker[tabx] +
                            pix[i+1] * ker[mid-tabx] +
                            pix[i+2] * ker[samples-tabx-1] ;
                /*
                 * Also sum up interpolation kernel coefficients
                 * for further normalization
                 */
                float norm =      ker[mid+tabx] +
                            ker[tabx] +
                            ker[mid-tabx] +
                            ker[samples-tabx-1] ;
                if (fabs(norm) > 1e-4) 
                {
                    value /= norm ;
                }
            }
        }   
        else 
        {
            value = 0.0 ;
        }
        if ( isnan(value) )
        {
            shifted[i] = ZERO ;
        }
        else
        {
            shifted[i] = value ;
        }
    }  
    return shifted ;
}


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

/**
@brief divides a resampled image with a resampled spectrum 
       in the same spectral range
@name sinfo_new_div_image_by_spectrum()
@param image: resampled image
@param spectrum: resampled spectrum in image format
@return resulting image
@doc 
divides a resampled image with a resampled spectrum in the same spectral range
that means all image columns are multiplied with the same spectrum
*/
cpl_image * 
sinfo_new_div_image_by_spectrum( cpl_image * image, cpl_image * spectrum )
{
    int col, row ;
    cpl_image * retImage ;
    int ilx=0;
    int ily=0;

    /* int slx=0; */
    int sly=0;

    float* pidata=NULL;
    float* psdata=NULL;
    float* podata=NULL;

    if ( image == NULL )
    {
        sinfo_msg_error("no image given!") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(image);
    ily=cpl_image_get_size_y(image);


    if ( spectrum == NULL )
    {
        sinfo_msg_error("no spectrum image given!") ;
        return NULL ;
    }
    /* slx=cpl_image_get_size_x(spectrum); */
    sly=cpl_image_get_size_y(spectrum);

    if ( sly != ily )
    {
        sinfo_msg_error("images are not compatible in pixel length!") ;
        return NULL ;
    }
    if ( NULL == (retImage = cpl_image_duplicate(image)) )
    {
        sinfo_msg_error("could not copy original image!") ;
        return NULL ;
    }
    pidata=cpl_image_get_data_float(image);
    psdata=cpl_image_get_data_float(spectrum);
    podata=cpl_image_get_data_float(retImage);

    for ( col = 0 ; col < ilx ; col++ )
    {
        for ( row = 0 ; row < ily ; row++ )
        {
            if ( !isnan(pidata[col+row*ilx]) &&
                 !isnan(psdata[col+row*ilx]))
            {
                podata[col+row*ilx] = pidata[col+row*ilx] / psdata[row] ;
            }
         }
    }
    return retImage ;
}

/*---------------------------------------------------------------------------
   Function     :       sinfo_new_clean_mean_circle_of_cube_spectra()
   In           :       cube: 1 allocated cube, 
                        centerx, centery: center pixel of circular aperture 
                                          in image coordinates
                        radius: integer radius of circular aperture
   Out          :       result spectrum vector
   Job          :       clean averaging routine for a reduced data to get 
                        a better spectral S/N only for a circular aperture.
 ---------------------------------------------------------------------------*/

Vector * sinfo_new_clean_mean_circle_of_cube_spectra(cpl_imagelist * cube,
                                  int       centerx,
                                  int       centery,
                                  int       radius,
                                  float     lo_reject,
                                  float     hi_reject )
{
    Vector          * clean ;
    int             i, j, k, l, n ;
    int             circsize ;
    int lx=0;
    int ly=0;
    int lz=0;
    float* pidata=NULL;
    cpl_image* img=NULL;

    lz=cpl_imagelist_get_size(cube);

    if ( cube == NULL || lz < 1 )
    {
        sinfo_msg_error (" no cube to take the mean of his spectra") ;
        return NullVector ;
    }
    img=cpl_imagelist_get(cube,0);
    lx=cpl_image_get_size_x(img);
    ly=cpl_image_get_size_y(img);

    if ((centerx+radius>=lx) ||
        (centery+radius>=ly) ||
        (centerx-radius<0) ||
        (centery-radius<0))
    {
        sinfo_msg_error(" invalid circular coordinates") ;
        return NullVector ;
    }

    n = 0 ;
    for ( j = centery - radius ; j <= centery + radius ; j++ )
    {
        for ( k = centerx - radius ; k <= centerx + radius ; k++ )
        {
            if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                 radius*radius )
            {
                n ++ ;
            }
        }
    }
    if (n == 0)
    {
        sinfo_msg_error (" no data points found!") ;
        return NullVector ;
    }
    circsize = n ;

    /* allocate a new vector to store the average spectral values */
    if (NULL == (clean = sinfo_new_vector (lz)) )
    {
        sinfo_msg_error (" cannot allocate a new vector") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane circle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < lz ; i++ )
    {
      img=cpl_imagelist_get(cube,i);
      pidata=cpl_image_get_data(img);
      int m = 0 ;
      pixelvalue* circle = (pixelvalue *) cpl_calloc (circsize, sizeof (pixelvalue*));

      for ( j = centery - radius ; j <= centery + radius ; j++ )
        {
            for ( k = centerx - radius ; k <= centerx + radius ; k++ )
            {
                if ( (k-centerx)*(k-centerx)+(j-centery)*(j-centery) <= 
                     radius*radius )
                {
                     circle[m] = pidata[k + j * lx] ;
                    m ++ ;
                }
            }
        }

        int nv = 0 ;
        for ( l = 0 ; l < circsize ; l++ )
        {
            if ( isnan(circle[l]) )
            {
                continue ;
            }
            clean -> data[i] += circle[l] ;
            nv ++;
        }
        if ( nv == 0 )
        {
            clean->data[i] = 0. ;
        }
        else
        {
            clean->data[i] = sinfo_new_clean_mean(circle, nv, 
                                                  lo_reject, hi_reject) ; 
        }
        cpl_free (circle) ;
    }
    return clean ;
}



/*---------------------------------------------------------------------------
   Function     :       sinfo_new_clean_mean_rectangle_of_cube_spectra()
   In           :       cube: 1 allocated cube, 
                        llx, lly, urx, ury: lower left and upper right
                                            position of rectangle in x-y plane ,
                                            image coordinates 0...
   Out          :       result spectrum vector
   Job          :       clean averaging routine for a reduced data to get a
                        better spectral S/N only for a rectangular aperture.
 ---------------------------------------------------------------------------*/

Vector * sinfo_new_clean_mean_rectangle_of_cube_spectra( cpl_imagelist * cube,
                                          int llx,
                                          int lly,
                                          int urx,
                                          int ury,
                                          float lo_reject,
                                          float hi_reject )
{
    Vector          * clean ;
    int             i, j, k;
    int             recsize ;
    int lx=0;
    int ly=0;
    int lz=0;
    float* pidata=0;
    cpl_image* img=NULL;

    lz=cpl_imagelist_get_size(cube);

    if ( cube == NULL || lz < 1 )
    {
        sinfo_msg_error (" no cube to take the mean of his spectra") ;
        return NullVector ;
    }
    img=cpl_imagelist_get(cube,0);
    lx=cpl_image_get_size_x(img);
    ly=cpl_image_get_size_y(img);

    if ((llx<0) || (llx>=lx) ||
        (urx<0) || (urx>=lx) ||
        (lly<0) || (lly>=ly) ||
        (ury<0) || (ury>=ly) ||
        (llx>=urx) || (lly>=ury))
    {
        sinfo_msg_error(" invalid rectangle coordinates:") ;
        sinfo_msg_error("lower left is [%d %d] upper right is [%d %d]",
                 llx, lly, urx, ury) ;
        return NullVector ;
    }

    recsize = (urx - llx + 1) * (ury - lly + 1) ;

    /* allocate a new vector to store the average spectral values */
    if (NULL == (clean = sinfo_new_vector (lz)) )
    {
        sinfo_msg_error (" cannot allocate a new vector") ;
        return NullVector ;
    }

    /*------------------------------------------------------------------------
     *  loop through the cube planes, through the x axis and the y-axis of the
     *  plane rectangle and store pixel values in a buffer.
     */
    for ( i = 0 ; i < lz ; i++ )
    {
        int m = 0 ;
        pixelvalue* rectangle = (pixelvalue *) cpl_calloc (recsize, sizeof (pixelvalue*));
        img=cpl_imagelist_get(cube,i);
        pidata=cpl_image_get_data(img);
        for ( j = lly ; j <= ury ; j++ )
        {
            for ( k = llx ; k <= urx ; k++ )
            {
                if ( isnan(pidata[k+j*lx]) )
                {
                    continue ;
                }
                else
                {
                    rectangle[m] = pidata[k + j * lx] ;
                    m ++ ;
                }
            }
        }
        if ( m == 0 )
        {
            clean->data[i] = 0. ;
        }
        else
        {
            clean->data[i] = sinfo_new_clean_mean(rectangle, m, 
                                                  lo_reject, hi_reject) ;
        }
        cpl_free ( rectangle ) ;
    }
    return clean ;
}

/*--------------------------------------------------------------------------*/
/**@}*/
