/*$Id: sinfo_new_cube_ops.c,v 1.47 2013-08-02 14:11:23 amodigli Exp $
 * 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  17/05/00  created
 */
/*
 * $Author: amodigli $
 * $Date: 2013-08-02 14:11:23 $
 * $Revision: 1.47 $
 * $Name: not supported by cvs2svn $
 */

/************************************************************************
 *   NAME
 *       sinfo_new_cube_ops.c -
 *       cube arithmetic routines
 *
 *   SYNOPSIS
 *    #include "sinfo_new_cube_ops.h"
 *
 *
 *
 *    2) cpl_imagelist *
 *       sinfo_new_cube_ops( cpl_imagelist    *    cube1,
 *                cpl_imagelist    *    cube2,
 *                int        operation)
 *
 *    3) cpl_imagelist *
 *       sinfo_new_cube_const_ops(
 *                     cpl_imagelist    * cube1,
 *                     double    constant,
 *                     int        operation)
 *
 *    4) cpl_imagelist *
 *       sinfo_new_cube_sub(
 *               cpl_imagelist    *    c1,
 *               cpl_imagelist    *    c2 )
 *
 *    5) cpl_imagelist *
 *       sinfo_new_cube_add(
 *               cpl_imagelist    *    c1,
 *               cpl_imagelist    *    c2  )
 *    6) cpl_imagelist *
 *       sinfo_new_cube_mul(
 *               cpl_imagelist    *    c1,
 *               cpl_imagelist    *    c2 )
 *
 *    7) cpl_imagelist *
 *       sinfo_new_cube_div(
 *               cpl_imagelist    *    c1,
 *               cpl_imagelist    *    c2 )
 *
 *    8) cpl_imagelist * sinfo_new_add_image_to_cube(cpl_imagelist * cu,
                                                    cpl_image * im)
 *
 *    9) cpl_imagelist * sinfo_new_sub_image_from_cube (cpl_imagelist * cu,
                                                    cpl_image * im)
 *
 *    10) cpl_imagelist * sinfo_new_mul_image_to_cube(cpl_imagelist * cu,
                                                    cpl_image * im)
 *
 *    11) cpl_imagelist * sinfo_new_div_cube_by_image(cpl_imagelist * cu,
                                                    cpl_image * im)
 *
 *    12) cpl_imagelist * sinfo_new_add_spectrum_to_cube(cpl_imagelist *cu,
                                                    Vector *spec)
 *
 *    13) cpl_imagelist * sinfo_new_sub_spectrum_from_cube(cpl_imagelist *cu,
                                                    Vector *spec)
 *
 *    14) cpl_imagelist * sinfo_new_mul_spectrum_to_cube(cpl_imagelist *cu,
                                                    Vector *spec)
 *
 *    15) cpl_imagelist * sinfo_new_div_cube_by_spectrum(cpl_imagelist *cu,
                                                    Vector *spec)
 *
 *    16) Vector * sinfo_new_clean_mean_of_spectra(cpl_imagelist * cube,
 *                                    int llx,
 *                                    int lly,
 *                                    int urx,
 *                                    int ury,
 *                                    double lo_reject,
 *                                    double hi_reject)
 *
 *    17) cpl_image * sinfo_new_median_cube(cpl_imagelist * cube)
 *
 *    18) cpl_image * sinfo_new_average_cube_to_image(cpl_imagelist * cube)
 *
 *    19) cpl_image * sinfo_new_sum_cube_to_image(cpl_imagelist * cube)
 *
 *    20) cpl_image *
         sinfo_new_average_cube_to_image_between_waves (cpl_imagelist * cube,
 *                                                   float     dispersion,
 *                                                   float     centralWave,
 *                                                   float     initialLambda,
 *                                                   float     finalLambda)
 *
 *    21) cpl_image * sinfo_new_extract_image_from_cube(cpl_imagelist * cube,
                                                    int plane_index)
 *
 *    22) Vector * sinfo_new_extract_spectrum_from_cube( cpl_imagelist * cube,
                                                    int x_pos, int y_pos )
 *    23) cpl_imagelist *
         sinfo_new_combine_jittered_cubes ( cpl_imagelist ** cubes,
 *                                         cpl_imagelist  * mergedCube,
 *                                         int        n_cubes,
 *                                         float    * cumoffsetx,
 *                                         float    * cumoffsety,
 *                                         float    * exptimes,
 *                                         char     * kernel_type )
 *    24) cpl_imagelist * sinfo_new_interpol_cube_simple( cpl_imagelist * cube,
 *                                      cpl_imagelist * badcube,
 *                                      int       maxdist )
 *
 *
 *    25) cpl_imagelist * sinfo_cube_zshift(const cpl_imagelist * cube,
 *                                          const double shift,
 *                                          double* rest)
 *
 *    26) cpl_imagelist * sinfo_cube_zshift_poly(const cpl_imagelist * cube,
 *                                               const double shift,
 *                                               const int    order)
 *
 *    27) cpl_imagelist * sinfo_cube_zshift_spline3(const cpl_imagelist * cube,
 *                                                  const double shift)
 *
 *
 *
 *
 *   DESCRIPTION
 *    2) 4 operations between 2 cubes
 *    3) 4 operations between a cube and a constant
 *    4)    subtract one cube from another
 *    5) add a cube to another
 *    6) multiply two cubes
 *    7) divide two cubes
 *    8) add an image to all planes in the cube
 *    9) subtract an image from all planes in the cube
 *    10) multiply an image to all planes in the cube
 *    11) divide all planes in the cube by an image
 *    12) adds a spectrum (in z-direction) to all data
 *                        points in a cube
 *    13) subtracts a spectrum (in z-direction) from all
 *                        data points in a cube
 *    14) multiplies a spectrum (in z-direction) to all data
 *                        points in a cube
 *    15) divides all data points of a cube by a spectrum
 *                        (in z-direction)
 *    16) averaging routine to get a better spectral S/N, sorts
 *        the values of the same z-position, cuts the lowest and
 *        highest values according to given thresholds and then
 *        takes the average within the x-y plane , cannot have
 *        a sum of low and high rejected values greater than 90%
 *        of all values
 *    17) determines the sinfo_new_median value in every pixel position
 *        by considering all pixels along the third axis.
 *        ZERO pixels in a plane are not considered. If all
 *        pixels at a position are not valid the result will
 *        be 'ZERO'.
 *    18) determines the average value in every pixel position
 *        by considering all pixels along the third axis.
 *        ZERO pixels in a plane are not considered. If all
 *        pixels at a position are not valid the result will
 *        be 'ZERO'.
 *    19) determines the sum value in every pixel position
 *        by considering all pixels along the third axis.
 *        ZERO pixels in a plane are not considered. If all
 *        pixels at a position are not valid the result will
 *        be 'ZERO'.
 *    20) determines the average value in every pixel position
 *        by considering only the pixels along the third axis
 *        which lie between the given wavelength values.
 *        These values are first recalculated to plane indices
 *        by using the given dispersion and minimum wavelength in
 *        the cube.
 *        ZERO pixels in a plane are not considered. If all
 *        pixels at a position are not valid the result will
 *        be 'ZERO'.
 *    21) returns the wanted image plane of the cube
 *    22) returns the wanted single spectrum of the cube
 *    23) merges jittered data cubes to one bigger cube
 *        by averaging the overlap regions weighted by
 *        the integration times. The x, y size of the final data
 *        cube is user given, and should be between 32 and 64
 *        pixels, while the relative pixel-offset (sub-pixel
 *        accuracy) of the single cubes with respect to the
 *        first cube in the list is read from the SEQ CUMOFFSETX,Y
 *        fits header keyword.
 *   24)  interpolates bad pixel of an object cube if a bad pixel
 *        mask cube is available by using the nearest neighbors
 *        in 3 dimensions.
 *
 *   25)  shifts an imagelist by a given amount to integer pixel accuracy
 *   26)  shifts an imagelist by a given amount to sub-pixel accuracy
 *   27)  shifts an imagelist by a given amount to sub-pixel accuracy
 *   FILES
 *
 *   ENVIRONMENT
 *
 *   RETURN VALUES
 *
 *   CAUTIONS
 *
 *   EXAMPLES
 *
 *   SEE ALSO
 *
 *   BUGS
 *
 *------------------------------------------------------------------------
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "sinfo_vltPort.h"

/*
 * System Headers
 */

#include <sys/types.h>
#include <sys/times.h>
#include <math.h>
/*
 * Local Headers
 */
#include "sinfo_dfs.h"
#include "sinfo_new_cube_ops.h"
#include "sinfo_resampling.h"
#include "sinfo_function_1d.h"
#include "sinfo_error.h"
#include "sinfo_globals.h"
#include "sinfo_utils_wrappers.h"

#include <cpl_vector.h>
/*----------------------------------------------------------------------------
 *                            Function codes
 *--------------------------------------------------------------------------*/


static int
sinfo_shift_cubes(cpl_imagelist** tmpcubes,
                  char* kernel_type,
                  const int n_cubes,
                  cpl_imagelist** cubes,
                  const int z_min,
                  const int z_max,
                  float* sub_offsetx,
                  float* sub_offsety,
                  const int mlx,
                  const int mly,
                  cpl_imagelist* mask);

static int
sinfo_build_mask_cube(const int z_min,
                      const int z_max,
                      const int olx,
                      const int oly,
                      const int n_cubes,
                      const int* llx,
                      const int* lly,
                      double    * exptimes,
                      cpl_imagelist** cubes,
                      cpl_imagelist** tmpcubes,
                      cpl_imagelist* mask);

static int
sinfo_build_mask_cube_thomas(const int z_min,
                             const int z_max,
                             const int olx,
                             const int oly,
                             const int n_cubes,
                             const int* llx,
                             const int* lly,
                             double    * exptimes,
                             cpl_imagelist** cubes,
                             cpl_imagelist** tmpcubes,
                             cpl_imagelist* mask);
static int
sinfo_compute_weight_average(const int z_min,
                             const int z_max,
                             const int ilx,
                             const int ily,
                             const int n_cubes,
                             cpl_imagelist* mergedCube,
                             cpl_imagelist* mask,
                             cpl_imagelist** tmpcubes,
                             double* exptimes,
                             int* llx,
                             int* lly);

static int
sinfo_check_input(cpl_imagelist** cubes,
                  const int n_cubes,
                  float* cumoffsetx,
                  float* cumoffsety,
                  double* exptimes);
static int
sinfo_coadd_with_ks_clip2(const int z_min,
                          const int z_max,
                          const int ilx,
                          const int ily,
                          const int n_cubes,
                          const double kappa,
                          int* llx,
                          int* lly,
                          double* exptimes,
                          cpl_imagelist* mask,
                          cpl_imagelist* mergedCube,
                          cpl_imagelist** tmpcubes);


/* temporally commented out as not yet used
static int
sinfo_ks_clip(
	      const int n_cubes,
              const int nc,
	      const int ilx,
	      const int ily,
	      const double kappa,
	      int* llx,
	      int* lly,
	      double* exptimes,
	      cpl_imagelist** tmpcubes,
              float* podata,
              float* pmdata,
	      const int x,
	      const int y,
	      const int m,
	      const int mlx,
	      const int olx
	      );


 */

/* TODO: not used */
static int
sinfo_coadd_with_ks_clip(const int z_min,
                         const int z_max,
                         const int ilx,
                         const int ily,
                         const int n_cubes,
                         const double kappa,
                         int* llx,
                         int* lly,
                         double* exptimes,
                         cpl_imagelist* mask,
                         cpl_imagelist* mergedCube,
                         cpl_imagelist** tmpcubes);


/**@{*/
/**
 * @defgroup sinfo_new_cube_ops Cube operations
 *
 * TBD
 */




/**
   @name       sinfo_new_cube_ops()
   @memo       4 operations between 2 cubes
   @param      cube1 1st cube
   @param      cube2 2nd cube
   @param      operation
   @out        result cube
   @note       possible operations are:
          #        Addition    '+'
          #        Subtraction     '-'
          #        Multiplication    '*'
          #        Division    '/'
 */
/* TODO: not used */
cpl_imagelist *
sinfo_new_cube_ops(
                cpl_imagelist    *    cube1,
                cpl_imagelist    *    cube2,
                int        operation)
{

    if (cube1==NULL || cube2==NULL)
    {
        sinfo_msg_error("null cubes");
        return NULL ;
    }

    switch(operation)
    {
    case '+':
        return sinfo_new_cube_add(cube1, cube2) ;

    case '-':
        return sinfo_new_cube_sub(cube1, cube2) ;


    case '*':
        return sinfo_new_cube_mul(cube1, cube2) ;


    case '/':
        return sinfo_new_cube_div(cube1, cube2) ;


    default:
        sinfo_msg_error("illegal requested operation: aborting cube arithmetic") ;
        return NULL ;
    }
}


/*----------------------------------------------------------------------------
   Function    :    sinfo_new_cube_const_ops()
   In         :    1 cube, 1 constant, operation to perform
   Out         :    result cube
   Job        :    4 operations between a cube and a constant
   Notice    :    possible operations are:
                  Addition    '+'
                  Subtraction     '-'
                  Multiplication    '*'
                  Division    '/'
                  Logarithm    'l'
                  Power        '^'
                  Exponentiation    'e'

 ---------------------------------------------------------------------------*/
/* TODO: not used */
cpl_imagelist *
sinfo_new_cube_const_ops(
                cpl_imagelist    *    c1,
                double        constant,
                int        operation)
{
    /*
    int ilx1=0;
    int ily1=0;
    int inp1=0;
     */
    cpl_imagelist* c2=NULL;
    //cpl_image* img1=NULL;



    if (c1 == NULL)
    {
        sinfo_msg_error("null cube") ;
        return NULL ;
    }
    //inp1=cpl_imagelist_get_size(c1);
    //img1=cpl_imagelist_get(c1,0);
    //ilx1=cpl_image_get_size_x(img1);
    //ily1=cpl_image_get_size_y(img1);





    if ((constant == 0.0) && (operation == '/'))
    {
        sinfo_msg_error("division by zero requested "
                        "in cube/constant operation") ;
        return NULL ;
    }
    /*
    if ( NULL == (c2 = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube" ) ;
        return NULL ;
    }
    */
    c2=cpl_imagelist_duplicate(c1);
    if(operation == '+') {
        cpl_imagelist_add_scalar(c2,constant);
    } else if (operation == '-') {
        cpl_imagelist_subtract_scalar(c2,constant);
    } else if (operation == '*') {
        cpl_imagelist_multiply_scalar(c2,constant);
    } else if (operation == '/') {
        cpl_imagelist_divide_scalar(c2,constant);

    } else {
        sinfo_free_imagelist(&c2);
        sinfo_msg_error("operation not supported");
        return NULL;
    }
    return c2 ;
}


/*----------------------------------------------------------------------------
 * Function    :    sinfo_new_cube_sub()
 * In         :    two cubes
 * Out         :    result cube
 * Job        :    subtract one cube from another
 *--------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_cube_sub(
                cpl_imagelist    *    c1,
                cpl_imagelist    *    c2
)
{
    cpl_imagelist   *                 c3 ;
    ulong32            i ;
    int                     np ;
    int ilx1=0;
    int ily1=0;
    int inp1=0;
    int ilx2=0;
    int ily2=0;
    int inp2=0;

    cpl_image* i_img=NULL;

    inp1=cpl_imagelist_get_size(c1);
    i_img=cpl_imagelist_get(c1,0);
    ilx1=cpl_image_get_size_x(i_img);
    ily1=cpl_image_get_size_y(i_img);


    inp2=cpl_imagelist_get_size(c2);
    i_img=cpl_imagelist_get(c2,0);
    ilx2=cpl_image_get_size_x(i_img);
    ily2=cpl_image_get_size_y(i_img);

    if ((ilx1 != ilx2) ||
                    (ily1 != ily2))
    {
        sinfo_msg_error("incompatible size: cannot subtract") ;
        return NULL ;
    }

    if ((inp2 != inp1) &&
                    (inp2 != 1))
    {
        sinfo_msg_error("cannot compute with these number of planes") ;
        return NULL ;
    }

    if ( NULL == (c3 = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube" ) ;
        return NULL ;
    }
    cpl_image* img3=NULL;
    for (np=0 ; np < inp1 ; np++)
    {
        img3=cpl_image_new(ilx1,ily1,CPL_TYPE_FLOAT);
        cpl_imagelist_set(c3,img3,np);
    }

    for (np=0 ; np < inp1 ; np++)
    {
        cpl_image* img1=cpl_imagelist_get(c1,np);
        float* p1data=cpl_image_get_data_float(img1);
        cpl_image* img2=cpl_imagelist_get(c2,np);
        float* p2data=cpl_image_get_data_float(img2);
        img3=cpl_imagelist_get(c3,np);
        float* p3data=cpl_image_get_data_float(img3);

        for (i=0 ; i< (ulong32)ilx1*ily1 ; i++)
        {
            p3data[i] = p1data[i] - p2data[i] ;
        }
    }

    return c3 ;
}


/*----------------------------------------------------------------------------
 * Function    :    sinfo_new_cube_add()
 * In         :    two cubes
 * Out         :    result cube
 * Job        :    add a cube to another
 *--------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_cube_add(
                cpl_imagelist    *    c1,
                cpl_imagelist    *    c2
)
{
    cpl_imagelist  *          c3 ;
    ulong32        i ;
    int         np ;
    int ilx1=0;
    int ily1=0;
    int inp1=0;
    int ilx2=0;
    int ily2=0;
    int inp2=0;


    cpl_image* i_img=NULL;
    cpl_image* img3=NULL;

    inp1=cpl_imagelist_get_size(c1);
    i_img=cpl_imagelist_get(c1,0);
    ilx1=cpl_image_get_size_x(i_img);
    ily1=cpl_image_get_size_y(i_img);

    inp2=cpl_imagelist_get_size(c2);
    i_img=cpl_imagelist_get(c2,0);
    ilx2=cpl_image_get_size_x(i_img);
    ily2=cpl_image_get_size_y(i_img);
    if ((ilx1 != ilx2) || (ily1 != ily2))
    {
        sinfo_msg_error("incompatible size: cannot add") ;
        return NULL ;
    }
    if ((inp2 != inp1) && (inp2 != 1))
    {
        sinfo_msg_error("cannot compute with these number of planes") ;
        return NULL ;
    }

    if (NULL == (c3 = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube") ;
        return NULL ;
    }

    for (np=0 ; np < inp1 ; np++)
    {
        img3=cpl_image_new(ilx1,ily1,CPL_TYPE_FLOAT);
        cpl_imagelist_set(c3,img3,np);
    }


    for (np=0 ; np < inp1 ; np++)
    {
        cpl_image* img1=cpl_imagelist_get(c1,np);
        float* p1data=cpl_image_get_data_float(img1);
        cpl_image* img2=cpl_imagelist_get(c2,np);
        float* p2data=cpl_image_get_data_float(img2);
        img3=cpl_imagelist_get(c3,np);
        float* p3data=cpl_image_get_data_float(img3);
        for (i=0 ; i< (ulong32)ilx1*ily1 ; i++)
        {
            p3data[i] = p1data[i] + p2data[i] ;
        }
    }

    return c3 ;
}

/*----------------------------------------------------------------------------
 * Function    :    sinfo_new_cube_mul()
 * In         :    two cubes
 * Out         :    result cube
 * Job        :    multiply 2 cubes
 *--------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_cube_mul(
                cpl_imagelist    *    c1,
                cpl_imagelist    *    c2
)
{
    cpl_imagelist             *c3 ;
    ulong32        i ;
    int                np ;
    int ilx1=0;
    int ily1=0;
    int inp1=0;
    int ilx2=0;
    int ily2=0;
    int inp2=0;


    cpl_image* i_img=NULL;

    inp1=cpl_imagelist_get_size(c1);
    i_img=cpl_imagelist_get(c1,0);
    ilx1=cpl_image_get_size_x(i_img);
    ily1=cpl_image_get_size_y(i_img);

    inp2=cpl_imagelist_get_size(c2);
    i_img=cpl_imagelist_get(c2,0);
    ilx2=cpl_image_get_size_x(i_img);
    ily2=cpl_image_get_size_y(i_img);

    if ((ilx1 != ilx2) || (ily1 != ily2))
    {
        sinfo_msg_error("incompatible size: cannot multiply") ;
        return NULL ;
    }

    if ((inp2 != inp1) && (inp2 != 1))
    {
        sinfo_msg_error("cannot compute with these number of planes") ;
        return NULL ;
    }

    if ( NULL == (c3 = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube" ) ;
        return NULL ;
    }

    cpl_image* img3=NULL;
    for (np=0 ; np < inp1 ; np++)
    {
        img3=cpl_image_new(ilx1,ily1,CPL_TYPE_FLOAT);
        cpl_imagelist_set(c3,img3,np);
    }


    for (np=0 ; np < inp1 ; np++)
    {
        cpl_image* img1=cpl_imagelist_get(c1,np);
        float* p1data=cpl_image_get_data_float(img1);
        cpl_image* img2=cpl_imagelist_get(c2,np);
        float* p2data=cpl_image_get_data_float(img2);
        img3=cpl_imagelist_get(c3,np);
        float* p3data=cpl_image_get_data_float(img3);
        for (i=0 ; i< (ulong32)ilx1*ilx2 ; i++)
        {
            p3data[i] = p1data[i] * p2data[i] ;
        }
    }

    return c3 ;
}


/*----------------------------------------------------------------------------
 * Function    :    sinfo_new_cube_div()
 * In         :    two cubes
 * Out         :    result cube
 * Job        :    divide 2 cubes
 *--------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_cube_div(
                cpl_imagelist    *    c1,
                cpl_imagelist    *    c2
)
{
    cpl_imagelist *           c3 ;
    ulong32        i ;
    int         np ;
    int ilx1=0;
    int ily1=0;
    int inp1=0;
    int ilx2=0;
    int ily2=0;
    int inp2=0;

    cpl_image* i_img=NULL;

    inp1=cpl_imagelist_get_size(c1);
    i_img=cpl_imagelist_get(c1,0);
    ilx1=cpl_image_get_size_x(i_img);
    ily1=cpl_image_get_size_y(i_img);

    inp2=cpl_imagelist_get_size(c2);
    i_img=cpl_imagelist_get(c2,0);
    ilx2=cpl_image_get_size_x(i_img);
    ily2=cpl_image_get_size_y(i_img);

    if ((ilx1 != ilx2) ||
                    (ily1 != ily2))
    {
        sinfo_msg_error("incompatible size: cannot divide") ;
        return NULL ;
    }

    if ((inp2 != inp1) && (inp2 != 1))
    {
        sinfo_msg_error("cannot compute with these number of planes") ;
        return NULL ;
    }

    if (NULL == (c3 = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate a new cube") ;
        return NULL ;
    }

    cpl_image* img3=NULL;
    for (np=0 ; np < inp1 ; np++)
    {
        img3=cpl_image_new(ilx1,ily1,CPL_TYPE_FLOAT);
        cpl_imagelist_set(c3,img3,np);
    }

    for (np=0 ; np < inp1 ; np++)
    {
        cpl_image* img1=cpl_imagelist_get(c1,np);
        float* p1data=cpl_image_get_data_float(img1);
        cpl_image* img2=cpl_imagelist_get(c2,np);
        float* p2data=cpl_image_get_data_float(img2);
        img3=cpl_imagelist_get(c3,np);
        float* p3data=cpl_image_get_data_float(img3);


        for (i=0 ; i< (ulong32) ilx1*ily1 ; i++)
        {
            if (fabs((double)p2data[i]) < 1e-10)
            {
                p3data[i] = 0.0 ;
            }
            else
            {
                p3data[i] = p1data[i] / p2data[i] ;
            }
        }
    }

    return c3 ;
}



/*---------------------------------------------------------------------------
   Function    :    sinfo_new_add_image_to_cube()
   In         :    1 allocated cube, 1 allocated image
   Out         :    result cube
   Job        :    add an image to all planes in the cube
 ---------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_add_image_to_cube(cpl_imagelist * cu, cpl_image * im)
{
    cpl_imagelist *   cube ;
    int               i ;
    int clx=0;
    int cly=0;
    int cnp=0;
    int ilx=0;
    int ily=0;


    cpl_image* i_img=NULL;

    if (cu==NULL || im==NULL)
    {
        sinfo_msg_error ("null cube or null image") ;
        return NULL ;
    }
    cnp=cpl_imagelist_get_size(cu);
    i_img=cpl_imagelist_get(cu,0);
    clx=cpl_image_get_size_x(i_img);
    cly=cpl_image_get_size_y(i_img);

    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);

    if ((clx != ilx) || (cly != ily))
    {
        sinfo_msg_error("incompatible size: cannot add image to cube") ;
        return NULL ;
    }

    cube = cpl_imagelist_duplicate (cu) ;

    for (i=0 ; i<cnp ; i++)
    {
        /* AMO
        here may be we have to use cpl_image_add_create and cpl_imagelist_set
         */
        cpl_image_add(cpl_imagelist_get(cube,i), im) ;
    }

    return cube ;
}

/*---------------------------------------------------------------------------
   Function    :    sinfo_new_sub_image_from_cube()
   In         :    1 allocated cube, 1 allocated image
   Out         :       result cube
   Job        :    subtract an image from all planes in the cube
 ---------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_sub_image_from_cube (cpl_imagelist * cu, cpl_image * im)
{
    cpl_imagelist   * cube ;
    int               i ;
    int clx=0;
    int cly=0;
    int cnp=0;
    int ilx=0;
    int ily=0;


    cpl_image* i_img=NULL;

    if (cu==NULL || im==NULL)
    {
        sinfo_msg_error ("null cube or null image") ;
        return NULL ;
    }
    cnp=cpl_imagelist_get_size(cu);
    i_img=cpl_imagelist_get(cu,0);
    clx=cpl_image_get_size_x(i_img);
    cly=cpl_image_get_size_y(i_img);

    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);

    if ((clx != ilx) || (cly != ily))
    {

        sinfo_msg_error("incompatible size: cannot subtract image from cube") ;
        return NULL ;
    }

    cube = cpl_imagelist_duplicate (cu) ;

    for (i=0 ; i<cnp ; i++)
    {
        /* AMO
        here may be we have to use cpl_image_add_create and cpl_imagelist_set
         */
        cpl_image_subtract(cpl_imagelist_get(cube,i), im) ;
    }
    return cube ;
}

/*---------------------------------------------------------------------------
   Function    :    sinfo_new_mul_image_to_cube()
   In         :    1 allocated cube, 1 allocated image
   Out         :    result cube
   Job        :    multiply an image to all planes in the cube
 ---------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_mul_image_to_cube(cpl_imagelist * cu, cpl_image * im)
{
    cpl_imagelist   * cube ;
    int               i ;
    int clx=0;
    int cly=0;
    int cnp=0;
    int ilx=0;
    int ily=0;


    cpl_image* i_img=NULL;

    if (cu==NULL || im==NULL)
    {
        sinfo_msg_error("null cube or null image") ;
        return NULL ;
    }
    cnp=cpl_imagelist_get_size(cu);
    i_img=cpl_imagelist_get(cu,0);
    clx=cpl_image_get_size_x(i_img);
    cly=cpl_image_get_size_y(i_img);

    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);

    if ((clx != ilx) || (cly != ily))
    {
        sinfo_msg_error("incompatible size: cannot multiply image by cube") ;
        return NULL ;
    }

    cube = cpl_imagelist_duplicate (cu) ;

    for (i=0 ; i<cnp ; i++)
    {
        /* AMO
        here may be we have to use cpl_image_add_create and cpl_imagelist_set
         */
        cpl_image_multiply(cpl_imagelist_get(cube,i), im) ;
    }

    return cube ;
}

/*---------------------------------------------------------------------------
   Function    :    sinfo_new_div_cube_by_image()
   In         :    1 allocated cube, 1 allocated image
   Out         :    result cube
   Job        :    divide all planes in the cube by an image
 ---------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_div_cube_by_image(cpl_imagelist * cu, cpl_image * im)
{
    cpl_imagelist   * cube ;
    int               i ;
    int clx=0;
    int cly=0;
    int cnp=0;
    int ilx=0;
    int ily=0;


    cpl_image* i_img=NULL;

    if (cu==NULL || im==NULL)
    {
        sinfo_msg_error ("null cube or null image") ;
        return NULL ;
    }
    cnp=cpl_imagelist_get_size(cu);
    i_img=cpl_imagelist_get(cu,0);
    clx=cpl_image_get_size_x(i_img);
    cly=cpl_image_get_size_y(i_img);

    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);

    if ((clx != ilx) || (cly != ily))
    {
        sinfo_msg_error("incompatible size: cannot divide cube by image") ;
        return NULL ;
    }

    cube = cpl_imagelist_duplicate (cu) ;

    for (i=0 ; i<cnp ; i++)
    {
        /* AMO
        here may be we have to use cpl_image_add_create and cpl_imagelist_set
         */
        cpl_image_divide(cpl_imagelist_get(cube,i), im) ;
    }

    return cube ;
}


/*---------------------------------------------------------------------------
   Function    :    sinfo_new_add_spectrum_to_cube()
   In         :    1 allocated cube, 1 allocated spectrum sinfo_vector
   Out         :    result cube
   Job        :    adds a spectrum (in z-direction) to all data
                        points in a cube
 ---------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_add_spectrum_to_cube(cpl_imagelist *cu, Vector *spec)
{
    cpl_imagelist *   cube ;
    int         i ,j ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;

    cpl_image* i_img=NULL;
    cpl_image* o_img=NULL;

    if (cu == NULL || spec == NULL)
    {
        sinfo_msg_error ("null cube or null spectrum") ;
        return NULL ;
    }
    inp=cpl_imagelist_get_size(cu);
    i_img=cpl_imagelist_get(cu,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    if ( inp != spec -> n_elements )
    {
        sinfo_msg_error("cube length and spectrum length are not compatible") ;
        return NULL ;
    }

    if ( NULL == (cube = cpl_imagelist_new ()) )
    {
        sinfo_msg_error ("cannot allocate new cube" ) ;
        return NULL ;
    }
    for ( i = 0; i < inp; i++)
    {
        o_img=cpl_image_new(ilx,ily,CPL_TYPE_FLOAT);
        cpl_imagelist_set(cube,o_img,i);
    }


    for ( i = 0; i < inp; i++)
    {
        i_img=cpl_imagelist_get(cu,i);
        pidata=cpl_image_get_data_float(i_img);
        o_img=cpl_imagelist_get(cube,i);
        float* podata=cpl_image_get_data_float(o_img);
        for ( j = 0; j < (int) ilx*ily; j++)
        {
            podata[j] = pidata[j] + spec -> data[i] ;
        }
    }

    return cube ;
}

/*---------------------------------------------------------------------------
   Function    :    sinfo_new_sub_spectrum_from_cube()
   In         :    1 allocated cube, 1 allocated spectrum sinfo_vector
   Out         :    result cube
   Job        :    subtracts a spectrum (in z-direction) from all
                        data points in a cube
 ---------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_sub_spectrum_from_cube(cpl_imagelist *cu, Vector *spec)
{
    cpl_imagelist *   cube ;
    int         i ,j ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;

    cpl_image* i_img=NULL;
    cpl_image* o_img=NULL;

    if (cu == NULL || spec == NULL)
    {
        sinfo_msg_error ("null cube or null spectrum") ;
        return NULL ;
    }
    inp=cpl_imagelist_get_size(cu);
    i_img=cpl_imagelist_get(cu,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    if ( inp != spec -> n_elements )
    {
        sinfo_msg_error("cube length and spectrum length are not compatible") ;
        return NULL ;
    }

    if ( NULL == (cube = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube" ) ;
        return NULL ;
    }
    for ( i = 0; i < inp; i++)
    {
        o_img=cpl_image_new(ilx,ily,CPL_TYPE_FLOAT);
        cpl_imagelist_set(cube,o_img,i);
    }


    for ( i = 0; i < inp; i++)
    {
        i_img=cpl_imagelist_get(cu,i);
        pidata=cpl_image_get_data_float(i_img);
        o_img=cpl_imagelist_get(cube,i);
        float* podata=cpl_image_get_data_float(o_img);
        for ( j = 0; j < (int) ilx*ily; j++)
        {
            if ( isnan(pidata[j]) || isnan(spec -> data[i]) )
            {
                podata[j] = ZERO ;
            }
            else
            {
                podata[j] = pidata[j] - spec -> data[i] ;
            }
        }
    }

    return cube ;
}


/*---------------------------------------------------------------------------
   Function    :    sinfo_new_mul_spectrum_to_cube()
   In         :    1 allocated cube, 1 allocated spectrum sinfo_vector
   Out         :    result cube
   Job        :    multiplies a spectrum (in z-direction) to all data
                        points in a cube
 ---------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_mul_spectrum_to_cube(cpl_imagelist *cu, Vector *spec)
{
    cpl_imagelist *   cube ;
    int         i ,j ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;

    cpl_image* i_img=NULL;
    cpl_image* o_img=NULL;

    if (cu == NULL || spec == NULL)
    {
        sinfo_msg_error ("null cube or null spectrum") ;
        return NULL ;
    }
    inp=cpl_imagelist_get_size(cu);
    i_img=cpl_imagelist_get(cu,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    if ( inp != spec -> n_elements )
    {
        sinfo_msg_error("cube length and spectrum length are not compatible") ;
        return NULL ;
    }

    if ( NULL == (cube = cpl_imagelist_new ()) )
    {
        sinfo_msg_error ("cannot allocate new cube" ) ;
        return NULL ;
    }

    for ( i = 0; i < inp; i++)
    {
        o_img=cpl_image_new(ilx,ily,CPL_TYPE_FLOAT);
        cpl_imagelist_set(cube,o_img,i);
    }


    for ( i = 0; i < inp; i++)
    {
        i_img=cpl_imagelist_get(cu,i);
        pidata=cpl_image_get_data_float(i_img);
        o_img=cpl_imagelist_get(cube,i);
        float* podata=cpl_image_get_data_float(o_img);
        for ( j = 0; j < (int) ilx*ily; j++)
        {
            if ( isnan(pidata[j]) || isnan(spec->data[i]) )
            {
                podata[j] = ZERO ;
            }
            else
            {
                podata[j] = pidata[j] * spec -> data[i] ;
            }
        }
    }

    return cube ;
}


/*---------------------------------------------------------------------------
   Function    :    sinfo_new_div_cube_by_spectrum()
   In         :    1 allocated cube, 1 allocated spectrum sinfo_vector
   Out         :    result cube
   Job        :    divides all data points of a cube by a spectrum
                        (in z-direction)
 ---------------------------------------------------------------------------*/

cpl_imagelist *
sinfo_new_div_cube_by_spectrum(cpl_imagelist *cu, Vector *spec)
{
    cpl_imagelist *   cube ;
    float       help ;
    int         i ,j ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;

    cpl_image* i_img=NULL;
    cpl_image* o_img=NULL;

    if (cu == NULL || spec == NULL)
    {
        sinfo_msg_error ("null cube or null spectrum") ;
        return NULL ;
    }
    inp=cpl_imagelist_get_size(cu);
    i_img=cpl_imagelist_get(cu,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    if ( inp != spec -> n_elements )
    {
        sinfo_msg_error("cube length and spectrum length are not compatible") ;
        return NULL ;
    }

    if (NULL == (cube = cpl_imagelist_new ()) )
    {
        sinfo_msg_error ("cannot allocate new cube") ;
        return NULL ;
    }

    for ( i = 0; i < inp; i++)
    {
        o_img=cpl_image_new(ilx,ily,CPL_TYPE_FLOAT);
        cpl_imagelist_set(cube,o_img,i);
    }


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

        i_img=cpl_imagelist_get(cu,i);
        pidata=cpl_image_get_data_float(i_img);
        o_img=cpl_imagelist_get(cube,i);
        float* podata=cpl_image_get_data_float(o_img);
        for ( j = 0; j < (int) ilx*ily; j++)
        {
            if (!isnan(spec->data[i]) && spec->data[i] != 0.)
            {
                help = 1/spec->data[i] ;
                if ( help > THRESH )
                {
                    help = 1. ;
                }
            }
            else
            {
                help = ZERO ;
            }

            if ( isnan(help) || isnan(pidata[j]) )
            {
                podata[j] = ZERO ;
            }
            else
            {
                podata[j] = pidata[j] * help ;
            }
        }
    }
    return cube ;
}


/*---------------------------------------------------------------------------
   Function    :    sinfo_new_clean_mean_of_spectra()
   In         :    1 allocated cube, position of rectangle in x-y plane ,
                        low and high cut threshold
   Out         :    result spectrum sinfo_vector
   Job        :    averaging routine to get a better spectral S/N, sorts
                        the values of the same z-position, cuts the lowest and
                        highest values according to given thresholds and then
                        takes the average within the x-y plane , cannot have
                        a sum of low and high rejected values greater than 90%
                        of all values
 ---------------------------------------------------------------------------*/
/* TODO: not used */
Vector *
sinfo_new_clean_mean_of_spectra(cpl_imagelist * cube,
                                int llx,
                                int lly,
                                int urx,
                                int ury,
                                double lo_reject,
                                double hi_reject)
{
    Vector                           * mean ;

    int                    i, j, k, l;
    int             recsize, lo_n, hi_n;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;
    cpl_image* i_img=NULL;

    if ( cube == NULL || cpl_imagelist_get_size(cube) < 1 )
    {
        sinfo_msg_error ("no cube to take the mean of his spectra") ;
        return NullVector ;
    }
    inp=cpl_imagelist_get_size(cube);
    i_img=cpl_imagelist_get(cube,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    if ((llx<1) || (llx>ilx) ||
                    (urx<1) || (urx>ilx) ||
                    (lly<1) || (lly>ily) ||
                    (ury<1) || (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 ;
    }

    if ((lo_reject + hi_reject) > 0.9)
    {
        sinfo_msg_error("illegal rejection thresholds: [%f] and [%f]",
                        lo_reject, hi_reject) ;
        sinfo_msg_error("threshold sum should not be over 0.9"
                        " aborting average") ;
        return NullVector ;
    }

    /* shift from FITS coordinates to C coordinates */
    llx -- ;
    lly -- ;
    urx -- ;
    ury -- ;

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

    lo_n = (int) (recsize * lo_reject + 0.5) ;
    hi_n = (int) (recsize * hi_reject + 0.5) ;

    if (lo_n + hi_n >= recsize)
    {
        sinfo_msg_error ("everything would be rejected") ;
        return NullVector;
    }

    /* 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++ )
    {
        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 ++ ;
            }
        }
        /*sorts the pixelvalues in the buffer*/
        sinfo_pixel_qsort (local_rectangle, recsize) ;

        int nv = 0 ;
        for ( l = lo_n ; l < (recsize - hi_n) ; l++ )
        {
            mean -> data[i] += local_rectangle[l] ;
            nv ++;
        }
        mean -> data[i] /= nv ;

        cpl_free ( local_rectangle ) ;
    }
    return mean ;
}


/*---------------------------------------------------------------------------
   Function    :sinfo_new_median_cube()
   In         :1 allocated cube
   Out         :result image
   Job        :determines the sinfo_new_median value in every pixel position
                 by considering all pixels along the third axis.
                 ZERO pixels in a plane are not considered. If all
                 pixels at a position are not valid the result will
                 be 'ZERO'.
 ---------------------------------------------------------------------------*/
cpl_image *
sinfo_new_median_cube(cpl_imagelist * cube)
{
    cpl_image  *         im ;
    pixelvalue *    buffer ;
    int        i, j ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;
    float* podata=NULL;
    cpl_image* i_img=NULL;

    if ( cube == NULL )
    {
        sinfo_msg_error ("null cube") ;
        return NULL ;
    }
    inp=cpl_imagelist_get_size(cube);
    i_img=cpl_imagelist_get(cube,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    /* allocate memory */
    if (NULL == (im = cpl_image_new (ilx, ily, CPL_TYPE_FLOAT )) )
    {
        sinfo_msg_error ("cannot allocate new image") ;
        return NULL ;
    }

    /*------------------------------------------------------------------------
     * transfer each sinfo_vector in z direction in a buffer and collect
       only non-blank data.
     */

    podata=cpl_image_get_data_float(im);
    for ( i = 0 ; i < (int) ilx*ily ; i++ )
    {
        buffer = (pixelvalue *) cpl_calloc (inp, sizeof (pixelvalue *));
        int k = 0 ;
        for ( j = 0 ; j < inp ; j ++ )
        {
            i_img=cpl_imagelist_get(cube,j);
            pidata=cpl_image_get_data_float(i_img);
            if ( !isnan(pidata[i]) )
            {
                buffer[k] = pidata[i] ;
                k ++ ;
            }
        }
        int nz = k ;

        /* proceed depending on the number of valid pixels */
        if ( nz > 2 )
        {
            podata[i] = sinfo_new_median ( buffer, nz ) ;
        }
        else if (nz == 2)
        {
            podata[i] = (buffer[0] + buffer[1]) / 2. ;
        }
        else if (nz == 1)
        {
            podata[i] = buffer[0] ;
        }
        else if (nz == 0)
        {
            podata[i] = ZERO ;
        }

        cpl_free ( buffer ) ;
    }

    return im ;
}


/*---------------------------------------------------------------------------
   Function    :    sinfo_new_average_cube_to_image()
   In         :    1 allocated cube
   Out         :    result image
   Job        :    determines the average value in every pixel position
                        by considering all pixels along the third axis.
                        ZERO pixels in a plane are not considered. If all
                        pixels at a position are not valid the result will
                        be 'ZERO'.
 ---------------------------------------------------------------------------*/
cpl_image *
sinfo_new_average_cube_to_image(cpl_imagelist * cube)
{
    cpl_image  *      im ;
    int        i, j;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;
    float* podata=NULL;
    cpl_image* i_img=NULL;

    if ( cube == NULL )
    {
        sinfo_msg_error ("null cube") ;
        return NULL ;
    }
    inp=cpl_imagelist_get_size(cube);
    i_img=cpl_imagelist_get(cube,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    /* allocate memory */
    if (NULL == (im = cpl_image_new (ilx, ily,CPL_TYPE_FLOAT )) )
    {
        sinfo_msg_error ("cannot allocate new image") ;
        return NULL ;
    }

    /*------------------------------------------------------------------------
     * transfer each vector in z direction in a buffer and collect
       only non-blank data.
     */

    podata=cpl_image_get_data_float(im);
    for ( i = 0 ; i < (int) ilx*ily ; i++ )
    {
        int nz = 0 ;
        for ( j = 0 ; j < inp ; j ++ )
        {
            i_img=cpl_imagelist_get(cube,j);
            pidata=cpl_image_get_data_float(i_img);
            if ( !isnan(pidata[i]) )
            {
                nz ++ ;
                podata[i] += pidata[i] ;
            }
        }

        /* proceed depending on the number of valid pixels */
        if ( nz >= 1 )
        {
            podata[i] /= nz ;
        }
        else if (nz == 0)
        {
            podata[i] = ZERO ;
        }
    }

    return im ;
}

/*---------------------------------------------------------------------------
   Function     :       sinfo_new_sum_cube_to_image()
   In           :       1 allocated cube
   Out          :       result image
   Job          :       determines the sum value in every pixel position
                        by considering all pixels along the third axis.
                        ZERO pixels in a plane are not considered. If all
                        pixels at a position are not valid the result will
                        be 'ZERO'.
 ---------------------------------------------------------------------------*/
cpl_image *
sinfo_new_sum_cube_to_image(cpl_imagelist * cube)
{
    cpl_image  *      im ;
    int        i, j;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;
    float* podata=NULL;
    cpl_image* i_img=NULL;

    if ( cube == NULL )
    {
        sinfo_msg_error ("null cube") ;
        return NULL ;
    }
    inp=cpl_imagelist_get_size(cube);
    i_img=cpl_imagelist_get(cube,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    /* allocate memory */
    if (NULL == (im = cpl_image_new (ilx, ily, CPL_TYPE_FLOAT )) )
    {
        sinfo_msg_error ("cannot allocate new image") ;
        return NULL ;
    }

    /*-------------------------------------------------------------------------
     * transfer each vector in z direction in a buffer and collect only
       non-blank data.
     */

    podata=cpl_image_get_data_float(im);
    for ( i = 0 ; i < (int) ilx*ily ; i++ )
    {
        int nz = 0 ;
        for ( j = 0 ; j < inp ; j ++ )
        {
            i_img=cpl_imagelist_get(cube,j);
            pidata=cpl_image_get_data_float(i_img);
            if ( !isnan(pidata[i]) )
            {
                nz++ ;
                podata[i] += pidata[i] ;
            }
        }

        /* proceed depending on the number of valid pixels */
        if (nz == 0)
        {
            podata[i] = ZERO ;
        }
    }

    return im ;
}

/*---------------------------------------------------------------------------
   Function    sinfo_new_average_cube_to_image_between_waves()
   In         cube: data cube to collapse
                dispersion: dispersion per pixel in microns/pixel
                (derived from fits header information)
                centralWave: central wavelength in the cube in microns
                                       (derived from fits header information)
                initialLambda, finalLambda: wavelength values in microns
                                            within which the cube is averaged
   Out         :resulting averaged image
   Job        :determines the average value in every pixel position
                 by considering only the pixels along the third axis
                 which lie between the given wavelength values.
                 These values are first recalculated to plane indices
                 by using the given dispersion and minimum wavelength in
                 the cube.
                 ZERO pixels in a plane are not considered. If all
                 pixels at a position are not valid the result will
                 be 'ZERO'.
 ---------------------------------------------------------------------------*/
cpl_image *
sinfo_new_average_cube_to_image_between_waves (cpl_imagelist * cube,
                                               float     dispersion,
                                               float     centralWave,
                                               float     initialLambda,
                                               float     finalLambda)
{
    cpl_image  *      im ;
    int        firstPlane ;
    int        lastPlane ;
    int        i, j;
    float      minWave ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;
    float* podata=NULL;
    cpl_image* i_img=NULL;

    if ( cube == NULL )
    {
        sinfo_msg_error ("null cube") ;
        return NULL ;
    }
    i_img=cpl_imagelist_get(cube,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    inp=cpl_imagelist_get_size(cube);

    minWave = centralWave - (float) (inp / 2)*dispersion ;

    if ( dispersion <= 0. || minWave <= 0. )
    {
        sinfo_msg_error ("wrong dispersion or minimum wavelength given") ;
        return NULL ;
    }

    if ( initialLambda < minWave ||
                    (initialLambda >= minWave + dispersion * inp) )
    {
        sinfo_msg_error ("wrong initial wavelength given") ;
        return NULL ;
    }

    if ( finalLambda <= minWave ||
                    (finalLambda > minWave + dispersion * inp) )
    {
        sinfo_msg_error ("wrong final wavelength given") ;
        return NULL ;
    }

    /* allocate memory */
    if (NULL == (im = cpl_image_new (ilx, ily, CPL_TYPE_FLOAT )) )
    {
        sinfo_msg_error ("cannot allocate new image") ;
        return NULL ;
    }

    /* transfer the wavelength range to image plane indices */
    firstPlane = sinfo_new_nint ((double) ((initialLambda - minWave) /
                    dispersion)) ;
    lastPlane  = sinfo_new_nint ((double) ((finalLambda - minWave) /
                    dispersion)) ;

    if ( firstPlane < 0 || firstPlane >= inp ||
                    lastPlane  < 0 || lastPlane  >  inp )
    {
        sinfo_msg_error ("wrong values given!") ;
        cpl_image_delete(im);
        return NULL ;
    }

    /*------------------------------------------------------------------------
     * transfer each vector in z direction in a buffer and collect only
       non-blank data.
     */



    podata=cpl_image_get_data_float(im);
    for ( i = 0 ; i < (int) ilx*ily ; i++ )
    {
        int nz = 0 ;

        for ( j = firstPlane ; j <= lastPlane ; j ++ )
        {
            i_img=cpl_imagelist_get(cube,j);
            pidata=cpl_image_get_data_float(i_img);
            if ( !isnan(pidata[i]) )
            {
                nz ++ ;
                podata[i] += pidata[i] ;
            }
        }

        /* proceed depending on the number of valid pixels */
        if ( nz >= 1 )
        {
            podata[i] /= nz ;
        }
        else if (nz == 0)
        {
            podata[i] = ZERO ;
        }
    }

    return im ;
}

/*---------------------------------------------------------------------------
   Function    :    sinfo_new_extract_image_from_cube()
   In         :    1 allocated cube
                        index of cube plane
   Out         :    extracted image
   Job        :    returns the wanted image plane of the cube
 ---------------------------------------------------------------------------*/
/* TODO: not used */

cpl_image *
sinfo_new_extract_image_from_cube(cpl_imagelist * cube, int plane_index)
{
    if ( cube == NULL )
    {
        sinfo_msg_error ("null cube") ;
        return NULL ;
    }

    if ( plane_index < 0 || plane_index >= cpl_imagelist_get_size(cube) )
    {
        sinfo_msg_error ("wrong plane index for image to be extracted") ;
        return NULL ;
    }

    return cpl_imagelist_get(cube,plane_index) ;
}

/*---------------------------------------------------------------------------
   Function    :sinfo_new_extract_spectrum_from_cube()
   In         :cube: 1 allocated cube
                 x_pos, y_pos: x, y pixel position of the
                               spectrum counted from 0
   Out         :extracted spectral sinfo_vector object
   Job        :returns the wanted single spectrum of the cube
 ---------------------------------------------------------------------------*/
/* TODO: not used */

Vector *
sinfo_new_extract_spectrum_from_cube(cpl_imagelist * cube,
                                     int x_pos, int y_pos)
{
    Vector * returnedSpectrum ;
    int i ;
    int ilx=0;
    int ily=0;
    int inp=0;
    float* pidata=NULL;
    cpl_image* i_img=NULL;

    if ( cube == NULL )
    {
        sinfo_msg_error ("no cube given!") ;
        return NullVector ;
    }
    i_img=cpl_imagelist_get(cube,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);
    inp=cpl_imagelist_get_size(cube);

    if ( x_pos < 0 || x_pos >= ilx )
    {
        sinfo_msg_error ("wrong x-positon of spectrum given!") ;
        return NullVector ;
    }

    if ( y_pos < 0 || y_pos >= ily )
    {
        sinfo_msg_error ("wrong y-positon of spectrum given!") ;
        return NullVector ;
    }

    /* allocate memory */
    if ( NULL == (returnedSpectrum = sinfo_new_vector ( inp )) )
    {
        sinfo_msg_error ("cannot allocate new spectrum!") ;
        return NullVector ;
    }

    for ( i = 0 ; i < inp ; i++ )
    {
        i_img=cpl_imagelist_get(cube,i);
        pidata=cpl_image_get_data_float(i_img);
        returnedSpectrum -> data[i] = pidata[x_pos + ilx*y_pos] ;
    }

    return returnedSpectrum ;
}

/*---------------------------------------------------------------------------
   Function     :       sinfo_new_combine_jittered_cubes()
   In           :       cubes: list of jittered cubes to mosaic
                        mergedCube: resulting merged cube containing the
                                      jittered cubes
                        n_cubes: number of cubes in the list to merge
                        cumoffsetx,y: array of relative x, y pixel offsets
                                      with respect to the first frame in the
                                      same sequence as the cube list.
                        exptimes: exposure times array giving the time
                                  in the same sequence as the cube list
                        kernel_type: the name of the interpolation kernel
                                     that you want to generate using the
                                     eclipse routine
                                     sinfo_generate_interpolation_kernel()
                                     Supported kernels are:
                                     NULL:      default kernel, currently tanh
                                     "default": dito
                                     "tanh":    Hyperbolic tangent
                                     "sinc2":   Square sinc
                                     "lanczos": Lanczos2 kernel
                                     "hamming": Hamming kernel
                                     "hann":    Hann kernel
   Out          :       mask: cube of the same size as combinedCube
                              containing 0 for blank (ZERO pixels) and
                              the summed integration times for
                              overlapping regions
                        mergedCube: final data cube containing the
                                    jittered cubes
   Job          :       merges jittered data cubes to one bigger cube
                        by averaging the overlap regions weighted by
                        the integration times. The x, y size of the final data
                        cube is user given, and should be between 32 and 64
                        pixels, while the relative pixel-offset (sub-pixel
                        accuracy) of the single cubes with respect to the
                        first cube in the list is read from the
                        SEQ CUMOFFSETX,Y
                        fits header keyword.
 ---------------------------------------------------------------------------*/
/* TODO: not used */

cpl_imagelist *
sinfo_new_combine_jittered_cubes ( cpl_imagelist ** cubes,
                                   cpl_imagelist  * mergedCube,
                                   int        n_cubes,
                                   float    * cumoffsetx,
                                   float    * cumoffsety,
                                   float    * exptimes,
                                   char     * kernel_type )
{

    int i=0 ;
    int x=0;
    int y=0;
    int z=0;
    int llx0=0;
    int lly0=0;
    int posx=0;
    int posy=0;
    float weight=0;
    cpl_imagelist * mask=NULL;
    double * kernel=NULL;
    /*cpl_image * shiftedImage ;*/

    int* llx=NULL ;
    int* lly=NULL ;

    float* sub_offsetx=NULL ;
    float* sub_offsety=NULL ;

    cpl_imagelist ** tmpcubes=NULL ;



    int ilx=0;
    int ily=0;
    int olx=0;
    int oly=0;
    int mlx=0;
    int onp=0;
    int inp=0;

    float* podata=NULL;
    float* pmdata=NULL;
    float* ptdata=NULL;

    cpl_image* i_img=NULL;
    cpl_image* o_img=NULL;
    cpl_image* m_img=NULL;
    cpl_image* t_img=NULL;


    if ( cubes == NULL )
    {
        sinfo_msg_error ("no cube list given!") ;
        return NULL ;
    }
    if ( n_cubes <= 0 )
    {
        sinfo_msg_error ("wrong number of data cubes in list!") ;
        return NULL ;
    }
    if ( cumoffsetx == NULL || cumoffsety == NULL )
    {
        sinfo_msg_error ("no cumoffsetx/y given!") ;
        return NULL ;
    }
    if ( exptimes == NULL )
    {
        sinfo_msg_error ("no exposure time array given!") ;
        return NULL ;
    }

    o_img=cpl_imagelist_get(mergedCube,0);
    olx=cpl_image_get_size_x(o_img);
    oly=cpl_image_get_size_y(o_img);
    onp=cpl_imagelist_get_size(mergedCube);
    if ( NULL == (mask = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("could not allocate cube!") ;
        return NULL ;
    }
    for(i=0;i<onp;i++){
        o_img=cpl_image_new(olx,oly,CPL_TYPE_FLOAT);
        cpl_imagelist_set(mergedCube,o_img,i);
    }

    i_img=cpl_imagelist_get(cubes[0],0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);

    inp=cpl_imagelist_get_size(cubes[0]);

    /*--------------------------------------------------------------------
     * center the cubes within the allocated big cube
     * that means define the (0,0) positions of the cubes in the image planes
     * to sub-pixel accuracy by using cumoffsetx,y and the reference cube
     */
    /* position of first reference frame, centered in big cube */
    llx0 = olx/2 - ilx/2 ;
    lly0 = oly/2 - ily/2 ;

    /*--------------------------------------------------------------------
     * go through the frame list and determine the lower left edge position
     * of the shifted cubes. Additionnally, the sub-pixel offsets are
     * determined.
     */

    llx=cpl_calloc(n_cubes,sizeof(int)); ;
    lly=cpl_calloc(n_cubes,sizeof(int)) ;

    sub_offsetx=cpl_calloc(n_cubes,sizeof(float)) ;
    sub_offsety=cpl_calloc(n_cubes,sizeof(float)) ;

    for ( i = 0 ; i < n_cubes ; i++ )
    {
        llx[i] = llx0 - sinfo_new_nint(cumoffsetx[i]) ;
        sub_offsetx[i] = (float)sinfo_new_nint(cumoffsetx[i]) - cumoffsetx[i] ;
        lly[i] = lly0 - sinfo_new_nint(cumoffsety[i]) ;
        sub_offsety[i] = (float)sinfo_new_nint(cumoffsety[i]) - cumoffsety[i] ;
    }


    /* -------------------------------------------------------------
     * shift the cubes according to the computed sub-pixel offsets
     * that means shift the single image planes of each cube
     * first determine an interpolation kernel
     */
    if ( NULL == (kernel = sinfo_generate_interpolation_kernel(kernel_type)))
    {
        sinfo_msg_warning ("could not generate desired interpolation kernel"
                        " or no kernel_typ was given, the default kernel"
                        " is used now!") ;
    }
    /* go through the frame list */


    tmpcubes=(cpl_imagelist**)cpl_calloc(n_cubes,sizeof(cpl_imagelist*)) ;

    for ( i = 0 ; i < n_cubes ; i++ )
    {
        pixelvalue* tmpspace = cpl_calloc(ilx, ily*sizeof(pixelvalue)) ;
        tmpcubes[i] = cpl_imagelist_new();

        for ( z = 0 ; z < inp ; z++ )
        {


            t_img=sinfo_new_shift_image(cpl_imagelist_get(cubes[i],z),
                            sub_offsetx[i], sub_offsety[i], kernel);

            if (t_img==NULL)
            {
                sinfo_msg_error ("could not shift image plane no %d"
                                " in cube no %d!", z, i) ;
                cpl_imagelist_delete(mergedCube) ;
                cpl_imagelist_delete(mask) ;
                cpl_free(kernel) ;
                return NULL ;
            }
            cpl_imagelist_set(tmpcubes[i],t_img,z);
        }
        cpl_free(tmpspace);
    }

    /*-------------------------------------------------------------------------
     * Build the mask data cube.
     * The mask is 0 where no data is available, otherwise the integration
       time of one frame, respectively the summed integration
     * times in the overlapping regions are inserted
     */
    /* go through the frame list */
    for ( i = 0 ; i < n_cubes ; i++ )
    {

        /* go through the first image plane of the big data cube */
        for ( y = 0 ; y < oly ; y++ )
        {
            for ( x = 0 ; x < olx ; x++ )
            {
                /* find the position of the present cube and
                   go through the single spectra */
                if ( y >= lly[i] && y < lly[i]+ily &&
                                x >= llx[i] && x < llx[i]+ilx )
                {
                    posx = x - llx[i] ;
                    posy = y - lly[i] ;
                    for ( z = 0 ; z < onp ; z++ )
                    {
                        t_img=cpl_imagelist_get(tmpcubes[i],z);
                        ptdata=cpl_image_get_data_float(t_img);
                        m_img=cpl_imagelist_get(mask,z);
                        pmdata=cpl_image_get_data_float(m_img);
                        if (!isnan(ptdata[posx+posy*ilx]) &&
                                        ptdata[posx+posy*ilx] != 0.)
                        {
                            pmdata[x+y*mlx] += exptimes[i] ;
                        }
                    }
                }
            }
        }
    }






    /* calculate a weighted average using the
       exposure time of the single frames
       of the overlapping regions of the cubes */
    for ( i = 0 ; i < n_cubes ; i++ )
    {

        /* go through the first image plane of the big data cube */
        for ( y = 0 ; y < oly ; y++ )
        {

            for ( x = 0 ; x < olx ; x++ )
            {

                /* find the position of the present cube
                   and go through the single spectra */
                if ( y >= lly[i] && y < lly[i]+ily &&
                                x >= llx[i] && x < llx[i]+ilx )
                {

                    posx = x - llx[i] ;
                    posy = y - lly[i] ;
                    for ( z = 0 ; z < onp ; z++ )
                    {

                        t_img=cpl_imagelist_get(tmpcubes[i],z);
                        ptdata=cpl_image_get_data_float(t_img);
                        m_img=cpl_imagelist_get(mask,z);
                        pmdata=cpl_image_get_data_float(m_img);
                        mlx=cpl_image_get_size_x(m_img);

                        o_img=cpl_imagelist_get(mergedCube,z);
                        podata=cpl_image_get_data_float(o_img);
                        podata[x+y*olx]=0;
                        if (!isnan(ptdata[posx+posy*ilx]))
                        {
                            if (pmdata[x+y*mlx] != 0.)
                            {
                                /* adjust the intensities to
                                   the first reference cube */
                                weight = exptimes[0] / pmdata[x+y*mlx] ;
                            }
                            else
                            {
                                weight = 0. ;
                            }
                            podata[x+y*olx] +=
                                            weight*ptdata[posx+posy*ilx] ;
                        }
                    }
                }
            }
        }
    }




    /* convert the "free space" in the cube to blank pixels */
    /* convert_0_to_ZERO_for_cubes(mergedCube) ; */
    cpl_free(kernel) ; /* originated by eclise-malloc */
    for( i = 0 ; i < n_cubes ; i++ )
    {
        cpl_imagelist_delete (tmpcubes[i]) ;
    }

    cpl_free(tmpcubes); ;
    cpl_free(llx); ;
    cpl_free(lly) ;

    cpl_free(sub_offsetx) ;
    cpl_free(sub_offsety) ;

    return mask ;
}








/**

   @name sinfo_build_mask_cube
   @brief Build the mask data cube.
   @param z_min  minimum cube's plane processed
   @param z_max  maximum cube's plane processed
   @param olx    output cube x size
   @param oly    output cube y size
   @param n_cubes: number of cubes in the list to merge
   @param llx    lower left edge x position of the shifted cubes.
   @param lly    lower left edge y position of the shifted cubes.
   @param exptimes: exposure times array giving the time
                    in the same sequence as the cube list

   @param  cubes: list of jittered cubes to mosaic
   @param  tmpcubes: list of shifted jittered cubes to mosaic
   @param  mask: cube of the same size as combinedCube
                 containing 0 for blank (ZERO pixels) and
                 the summed integration times for
                 overlapping regions


   @doc Build the mask data cube.
        The mask is 0 where no data is available, otherwise the
        integration time of one frame, respectively the summed integration
        times in the overlapping regions are inserted

 */

static int
sinfo_build_mask_cube(const int z_min,
                      const int z_max,
                      const int olx,
                      const int oly,
                      const int n_cubes,
                      const int* llx,
                      const int* lly,
                      double    * exptimes,
                      cpl_imagelist** cubes,
                      cpl_imagelist** tmpcubes,
                      cpl_imagelist* mask)
{

    int i=0;
    int y=0;
    int z=0;
    int ilx=0;
    int ily=0;
    cpl_image* i_img=NULL;
    cpl_image* t_img=NULL;
    int posx=0;
    int posy=0;
    float* ptdata=NULL;
    float* pmdata=NULL;
    int m=0;
    int x=0;
    int mlx=0;
    cpl_image* m_img=NULL;


    for ( z = z_min, m=0 ; z < z_max ; z++, m++ ) {

        // go through the first image plane of the big data cube
        for ( y = 0 ; y < oly ; y++ ) {
            for ( x = 0 ; x < olx ; x++ ) {
                for ( i = 0 ; i < n_cubes ; i++ ) {

                    i_img=cpl_imagelist_get(cubes[i],0);
                    ilx=cpl_image_get_size_x(i_img);
                    ily=cpl_image_get_size_y(i_img);


                    // find the position of the present cube and go
                    // through the single spectra */
                    if ( y >= lly[i] && y < lly[i]+ily &&
                                    x >= llx[i] && x < llx[i]+ilx )
                    {
                        posx = x - llx[i] ;
                        posy = y - lly[i] ;


                        t_img=cpl_imagelist_get(tmpcubes[i],m);
                        ptdata=cpl_image_get_data_float(t_img);
                        m_img=cpl_imagelist_get(mask,z);
                        pmdata=cpl_image_get_data_float(m_img);
                        mlx=cpl_image_get_size_x(m_img);

                        if (!isnan(ptdata[posx+posy*ilx]) &&
                                        ptdata[posx+posy*ilx] != 0.)
                        {
                            pmdata[x+y*mlx] += (float)exptimes[i] ;
                        } else if (isnan(ptdata[posx+posy*ilx])) {
                            sinfo_msg_debug("ptdata %d, %d, %d is NAN\t",x,y,z);
                        } else if (ptdata[posx+posy*ilx] == 0.) {
                            sinfo_msg_debug("ptdata %d, %d, %d is 0\t",x,y,z);
                        }

                    } else {
                        sinfo_msg_debug("point %d, %d, %d outside range\n",x,y,z);
                    }
                }
            }
        }
    }
    return 0;

}




static int
sinfo_build_mask_cube_thomas(const int z_min,
                             const int z_max,
                             const int olx,
                             const int oly,
                             const int n_cubes,
                             const int* llx,
                             const int* lly,
                             double    * exptimes,
                             cpl_imagelist** cubes,
                             cpl_imagelist** tmpcubes,
                             cpl_imagelist* mask)
{

    int i=0;
    int y=0;
    int z=0;
    //int inp=0;

    cpl_image* t_img=NULL;
    int posx=0;
    int posy=0;
    float* ptdata=NULL;
    float* pmdata=NULL;
    int m=0;
    int x=0;
    int mlx=0;


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

        cpl_image* i_img=cpl_imagelist_get(cubes[i],0);
        int ilx=cpl_image_get_size_x(i_img);
        int ily=cpl_image_get_size_y(i_img);
        //inp=cpl_imagelist_get_size(cubes[i]);

        //go through the first image plane of the big data cube
        for ( y = 0 ; y < oly ; y++ ){
            for ( x = 0 ; x < olx ; x++ ){
                // find the position of the present cube and go
                // through the single spectra
                if ( y >= lly[i] && y < lly[i]+ily &&
                                x >= llx[i] && x < llx[i]+ilx ) {
                    posx = x - llx[i] ;
                    posy = y - lly[i] ;
                    for ( z = z_min,m=0 ; z < z_max ; z++,m++ ) {
                        t_img=cpl_imagelist_get(tmpcubes[i],m);
                        ptdata=cpl_image_get_data_float(t_img);
                        cpl_image* m_img=cpl_imagelist_get(mask,z);
                        pmdata=cpl_image_get_data_float(m_img);
                        mlx=cpl_image_get_size_x(m_img);

                        if (!isnan(ptdata[posx+posy*ilx]) &&
                                        ptdata[posx+posy*ilx] != 0.) {
                            pmdata[x+y*mlx] += (float)exptimes[i]  ;
                        }
                    }
                }
            }
        }
    }
    return 0;
}





/**

   @name   sinfo_new_combine_jittered_cubes()
   @param  cubes: list of jittered cubes to mosaic
   @param  mergedCube: resulting merged cube containing the
                                      jittered cubes
   @param  mask: cube of the same size as combinedCube
                 containing 0 for blank (ZERO pixels) and
                 the summed integration times for
                 overlapping regions

   @param n_cubes: number of cubes in the list to merge
   @param cumoffsetx: array of relative x pixel offsets
                      with respect to the first frame in the
                      same sequence as the cube list.

   @param cumoffsety: array of relative y pixel offsets
                      with respect to the first frame in the
                      same sequence as the cube list.

   @param exptimes: exposure times array giving the time
                    in the same sequence as the cube list
   @param kernel_type: the name of the interpolation kernel
                       that you want to generate using the
                       eclipse routine
                       sinfo_generate_interpolation_kernel()
                       Supported kernels are:
                       NULL:      default kernel, currently tanh
                                     "default": dito
                                     "tanh":    Hyperbolic tangent
                                     "sinc2":   Square sinc
                                     "lanczos": Lanczos2 kernel
                                     "hamming": Hamming kernel
                                     "hann":    Hann kernel
   @param z_min  minimum cube's plane processed
   @param z_max  maximum cube's plane processed


   @doc merges jittered data cubes to one bigger cubem_img
        by averaging the overlap regions weighted by
        the integration times. The x, y size of the final data
        cube is user given, and should be between 32 and 64
        pixels, while the relative pixel-offset (sub-pixel
        accuracy) of the single cubes with respect to the
        first cube in the list is read from the
        SEQ CUMOFFSETX,Y fits header keyword.
 */



int
sinfo_new_combine_jittered_cubes_range ( cpl_imagelist ** cubes,
                                         cpl_imagelist  * mergedCube,
                                         cpl_imagelist  * mask,
                                         int        n_cubes,
                                         float    * cumoffsetx,
                                         float    * cumoffsety,
                                         double    * exptimes,
                                         char     * kernel_type,
                                         const int z_min, const int z_max )
{

    int i;
    int llx0, lly0 ;
    cpl_imagelist ** tmpcubes=NULL ;
    int* llx=NULL ;
    int* lly=NULL ;
    float* sub_offsetx=NULL ;
    float* sub_offsety=NULL ;

    int ilx=0;
    int ily=0;
    int olx=0;
    int oly=0;
    int mlx=0;
    int mly=0;

    cpl_image* i_img=NULL;
    cpl_image* o_img=NULL;


    if(sinfo_check_input(cubes,n_cubes,cumoffsetx,cumoffsety,exptimes) == -1) {
        return -1;
    }

    o_img=cpl_imagelist_get(mergedCube,z_min);
    olx=cpl_image_get_size_x(o_img);
    oly=cpl_image_get_size_y(o_img);
    i_img=cpl_imagelist_get(cubes[0],0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);
    mlx=olx;
    mly=oly;


    /*--------------------------------------------------------------------
     * center the cubes within the allocated big cube
     * that means define the (0,0) positions of the cubes in the image planes
     * to sub-pixel accuracy by using cumoffsetx,y and the reference cube
     */
    /* position of first reference frame, centered in big cube */
    llx0 = olx/2 - ilx/2 ;
    lly0 = oly/2 - ily/2 ;

    /*--------------------------------------------------------------------
     * go through the frame list and determine the lower left edge position
     * of the shifted cubes. Additionnally, the sub-pixel offsets are
     * determined.
     */


    llx=cpl_calloc(n_cubes,sizeof(int)) ;
    lly=cpl_calloc(n_cubes,sizeof(int)) ;
    sub_offsetx=cpl_calloc(n_cubes,sizeof(float)) ;
    sub_offsety=cpl_calloc(n_cubes,sizeof(float)) ;

    for ( i = 0 ; i < n_cubes ; i++ )
    {
        llx[i] = llx0 - sinfo_new_nint(cumoffsetx[i]) ;
        sub_offsetx[i] = (float)sinfo_new_nint(cumoffsetx[i]) - cumoffsetx[i] ;
        lly[i] = lly0 - sinfo_new_nint(cumoffsety[i]) ;
        sub_offsety[i] = (float)sinfo_new_nint(cumoffsety[i]) - cumoffsety[i] ;
    }

    tmpcubes=(cpl_imagelist**)cpl_calloc(n_cubes,sizeof(cpl_imagelist*)) ;
    /* -------------------------------------------------------------
     * shift the cubes according to the computed sub-pixel offsets
     * that means shift the single image planes of each cube
     * first determine an interpolation kernel
     */
    if(sinfo_shift_cubes(tmpcubes,kernel_type,n_cubes,cubes,z_min, z_max,
                    sub_offsetx,sub_offsety,mlx,mly,mask) == -1) {
    	cpl_free(llx) ;
    	cpl_free(lly) ;
        return -1;
    }


    /*-----------------------------------------------------------------------
     * Build the mask data cube.
     * The mask is 0 where no data is available, otherwise the
       integration time of
     * one frame, respectively the summed integration
     * times in the overlapping regions are inserted
     */
    /* go through the frame list */
    sinfo_build_mask_cube(z_min,z_max,olx,oly,n_cubes,llx,lly,exptimes,
                          cubes,tmpcubes,mask);


    /* calculate a weighted average using the exposure time of the
       single frames of the overlapping regions of the cubes */

    sinfo_compute_weight_average(z_min,z_max,ilx,ily,n_cubes,mergedCube,mask,
                                 tmpcubes,exptimes,llx,lly);

    /* convert the "free space" in the cube to blank pixels */
    /* convert_0_to_ZERO_for_cubes(mergedCube) ; */

    for( i = 0 ; i < n_cubes ; i++ )
    {
        cpl_imagelist_delete (tmpcubes[i]) ;
    }


    cpl_free(tmpcubes) ;
    cpl_free(llx) ;
    cpl_free(lly) ;
    cpl_free(sub_offsetx) ;
    cpl_free(sub_offsety) ;

    return 0 ;
}

/**
   @name   sinfo_new_combine_jittered_cubes()
   @short check input of cube coaddition functions
   @param  cubes: list of jittered cubes to mosaic
   @param n_cubes: number of cubes in the list to merge
   @param cumoffsetx: array of relative x pixel offsets
                      with respect to the first frame in the
                      same sequence as the cube list.

   @param cumoffsety: array of relative y pixel offsets
                      with respect to the first frame in the
                      same sequence as the cube list.

   @param exptimes: exposure times array giving the time
                    in the same sequence as the cube list

 */
static int
sinfo_check_input(cpl_imagelist** cubes,
                  const int n_cubes,
                  float* cumoffsetx,
                  float* cumoffsety,
                  double* exptimes)
{
    if ( cubes == NULL )
    {
        sinfo_msg_error ("no cube list given!") ;
        return -1 ;
    }
    if ( n_cubes <= 0 )
    {
        sinfo_msg_error ("wrong number of data cubes in list!") ;
        return -1 ;
    }
    if ( cumoffsetx == NULL || cumoffsety == NULL )
    {
        sinfo_msg_error ("no cumoffsetx/y given!") ;
        return -1;
    }
    if ( exptimes == NULL )
    {
        sinfo_msg_error ("no exposure time array given!") ;
        return -1 ;
    }

    return 0;
}

/**
   @name   sinfo_compute_weight_average
   @short compute weighted mean of shifted cubes
   @param z_min  minimum cube's plane processed
   @param z_max  maximum cube's plane processed
   @param ilx    input cube component x size
   @param ily    input cube component y size
   @param n_cubes: number of cubes in the list to merge
   @param  cubes: list of jittered cubes to mosaic
   @param  mergedCube: resulting merged cube containing the
                                      jittered cubes
   @param  mask: cube of the same size as combinedCube
                 containing 0 for blank (ZERO pixels) and
                 the summed integration times for
                 overlapping regions

   @param  tmpcubes: shifted list of jittered cubes to mosaic

   @param exptimes: exposure times array giving the time
                    in the same sequence as the cube list

   @doc calculate a weighted average using the exposure time of the
        single frames of the overlapping regions of the cubes

 */
static int
sinfo_compute_weight_average(const int z_min,
                             const int z_max,
                             const int ilx,
                             const int ily,
                             const int n_cubes,
                             cpl_imagelist* mergedCube,
                             cpl_imagelist* mask,
                             cpl_imagelist** tmpcubes,
                             double* exptimes,
                             int* llx,
                             int* lly)
{

    int m=0;
    int x=0;
    int y=0;
    int z=0;
    int i=0;

    int mlx=0;
    //int mly=0;
    int olx=0;
    int oly=0;

    cpl_image* o_img=NULL;

    cpl_image* t_img=NULL;


    double weight=0;

    int posx=0;
    int posy=0;


    o_img=cpl_imagelist_get(mergedCube,z_min);
    olx=cpl_image_get_size_x(o_img);
    oly=cpl_image_get_size_y(o_img);
    mlx=olx;
    //mly=oly;

    /* calculate a weighted average using the exposure time of the
     single frames of the overlapping regions of the cubes */

    float* ptdata=NULL;
    for ( z = z_min, m = 0 ; z < z_max ; z++, m++ ) {
        o_img=cpl_imagelist_get(mergedCube,z);
        float* podata=cpl_image_get_data_float(o_img);
        cpl_image* m_img=cpl_imagelist_get(mask,z);
        float* pmdata=cpl_image_get_data_float(m_img);
        mlx=cpl_image_get_size_x(m_img);

        /* go through the first image plane of the big data cube */
        for ( y = 0 ; y < oly ; y++ ) {
            for ( x = 0 ; x < olx ; x++ ) {

                /* find the position of the present cube and
          go through the single spectra */

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

                    if ( y >= lly[i] && y < lly[i]+ily &&
                                    x >= llx[i] && x < llx[i]+ilx ) {
                        posx = x - llx[i] ;
                        posy = y - lly[i] ;

                        t_img=cpl_imagelist_get(tmpcubes[i],m);
                        ptdata=cpl_image_get_data_float(t_img);
                        /* To prevent black regions in peculiar batterfly cases
              podata[x+y*olx]=0;
                         */
                        if (!isnan(ptdata[posx+posy*ilx])) {
                            if (pmdata[x+y*mlx] != 0.) {
                                /* adjust the intensities to the
                   first reference cube */
                                weight = exptimes[0] / pmdata[x+y*mlx] ;
                            } else {
                                weight = 0. ;
                            }
                            podata[x+y*olx] += weight*ptdata[posx+posy*ilx] ;

                        }
                    }
                }
            }
        }
    }
    return 0;
}


/**

   @name sinfo_shift_cubes
   @brief shift the cubes according to the computed sub-pixel offsets
   @param tmpcubes the shifted cubes list
   @param kernel_type: the name of the interpolation kernel
                       that you want to generate using the
                       eclipse routine
                       sinfo_generate_interpolation_kernel()
                       Supported kernels are:
                       NULL:      default kernel, currently tanh
                                     "default": dito
                                     "tanh":    Hyperbolic tangent
                                     "sinc2":   Square sinc
                                     "lanczos": Lanczos2 kernel
                                     "hamming": Hamming kernel
                                     "hann":    Hann kernel

   @param n_cubes: number of cubes in the list to merge
   @param  cubes: list of jittered cubes to mosaic
   @param z_min  minimum cube's plane processed
   @param z_max  maximum cube's plane processed
   @param sub_offsetx lower left edge x sub pixel position of the
                 shifted cubes.
   @param sub_offsety lower left edge y sub pixel position of the
                 shifted cubes.

   @param mlx    output mask cube x size
   @param mly    output mask cube y size
   @param  mask: cube of the same size as combinedCube
                 containing 0 for blank (ZERO pixels) and
                 the summed integration times for
                 overlapping regions

    @doc shift the cubes according to the computed sub-pixel offsets
         that means shift the single image planes of each cube
         first determine an interpolation kernel
 */

static int
sinfo_shift_cubes(cpl_imagelist** tmpcubes,
                  char* kernel_type,
                  const int n_cubes,
                  cpl_imagelist** cubes,
                  const int z_min,
                  const int z_max,
                  float* sub_offsetx,
                  float* sub_offsety,
                  const int mlx,
                  const int mly,
                  cpl_imagelist* mask)
{

    double * kernel ;
    int i=0;

    //int inp=0;

    int z=0;
    cpl_image* t_img=NULL;
    cpl_image* m_img=NULL;
    int m=0;

    /* -------------------------------------------------------------
     * shift the cubes according to the computed sub-pixel offsets
     * that means shift the single image planes of each cube
     * first determine an interpolation kernel
     */

    if ( NULL == (kernel = sinfo_generate_interpolation_kernel(kernel_type)) )
    {
        sinfo_msg_warning ("could not generate desired interpolation kernel"
                        "or no kernel_typ was given, the default kernel"
                        "is used now!") ;
    }
    /* go through the frame list */
    for ( i = 0 ; i < n_cubes ; i++ )
    {

        cpl_image* i_img=cpl_imagelist_get(cubes[i],0);
        int ilx=cpl_image_get_size_x(i_img);
        int ily=cpl_image_get_size_y(i_img);
        //inp=cpl_imagelist_get_size(cubes[i]);
        pixelvalue *tmpspace = cpl_calloc(ilx, ily*sizeof(pixelvalue)) ;
        tmpcubes[i]=cpl_imagelist_new();

        for ( z = z_min, m=0 ; z < z_max ; z++, m++ )
        {
            t_img=sinfo_new_shift_image(cpl_imagelist_get(cubes[i],z),
                            sub_offsetx[i],
                            sub_offsety[i],
                            kernel);

            if (t_img==NULL)
            {
                sinfo_msg_error("could not shift image plane no %d "
                                "in cube no %d!", z, i) ;
                cpl_free(kernel) ;
                return -1 ;
            }

            cpl_imagelist_set(tmpcubes[i],t_img,m);
            m_img=cpl_image_new(mlx,mly,CPL_TYPE_FLOAT);
            cpl_imagelist_set(mask,m_img,z);
        }

        cpl_free(tmpspace);

    }
    if(kernel != NULL) cpl_free(kernel) ;

    return 0;

}


/* Temporally commented out as not yet used
static int
sinfo_ks_clip(
	      const int n_cubes,
              const int nc,
	      const int ilx,
	      const int ily,
	      const double kappa,
	      int* llx,
	      int* lly,
	      double* exptimes,
	      cpl_imagelist** tmpcubes,
              float* podata,
              float* pmdata,
	      const int x,
	      const int y,
	      const int m,
	      const int mlx,
	      const int olx
  )
{


  int posx=0;
  int posy=0;
  int i=0;
  int nclip=0;
  int ks=0;

  float sig=0;
  float med=0;
  float ovr=0;
  float avg=0;

  float* ptdata=NULL;
  float* pvdata=NULL;

  cpl_image* t_img=NULL;
  float  msk_sum=0;
  float  val_msk_sum=0;
  cpl_image* v_img=NULL;

  cpl_vector* val=NULL;
  cpl_vector* msk=NULL;

  msk=cpl_vector_new(n_cubes);
  for (i=0;i<n_cubes;i++) {
    cpl_vector_set(msk,i,1);
  }

  // k-s clipping
  nclip=0;

  for (ks=0;ks<nc;ks++) {

    sig=0;
    med=0;
    ovr=0;
    if(nc-nclip >0) {
      val=cpl_vector_new(nc-nclip);
    }

    // fill val
    for ( i = 0 ; i < n_cubes ; i++ ) {
      t_img=cpl_imagelist_get(tmpcubes[i],m);
      ptdata=cpl_image_get_data_float(t_img);
      if ( y >= lly[i] && y < lly[i]+ily &&
	   x >= llx[i] && x < llx[i]+ilx ) {
	posx = x - llx[i] ;
	posy = y - lly[i] ;
	if (!isnan(ptdata[posx+posy*ilx]) &&
	    ptdata[posx+posy*ilx] != 0. &&
	    (cpl_vector_get(msk,i) != 0)) {
	  cpl_vector_set(val,ovr,(double)ptdata[posx+posy*ilx]);
	  ovr++;
	}
      }
    }

    // get avg, med, sig
    if(ovr>0) {
      avg=cpl_vector_get_mean(val);
      med=cpl_vector_get_median_const(val);
      if(ovr>1) {
	sig=cpl_vector_get_stdev(val);
      } else {
	sig=0;
      }
      cpl_vector_delete(val);
    }

    for ( i = 0 ; i < n_cubes ; i++ ) {
      t_img=cpl_imagelist_get(tmpcubes[i],m);
      ptdata=cpl_image_get_data_float(t_img);
      // Do k-s clipping at each pixel
      if ( y >= lly[i] && y < lly[i]+ily &&
	   x >= llx[i] && x < llx[i]+ilx ) {
	posx = x - llx[i] ;
	posy = y - lly[i] ;
	//sinfo_msg_warning("llx[%d]=%d lly[%d],=%d",i,llx[i],i,lly[i]);
	//sinfo_msg_warning("posx=%d posy=%d",posx,posy);
	if (!isnan(ptdata[posx+posy*ilx]) &&
	    ptdata[posx+posy*ilx] != 0. &&
	    (cpl_vector_get(msk,i) != 0)) {
	  if(abs((ptdata[posx+posy*ilx]-med))> kappa*sig) {
	    ptdata[posx+posy*ilx]=0;

	    pmdata[x+y*mlx] -= exptimes[i]  ;

	    cpl_vector_set(msk,i,0);
	    nclip++;
	  }
	}
      }

    }
  }

  msk_sum=0;
  val_msk_sum=0;
  for ( i = 0 ; i < n_cubes ; i++ ) {
    v_img=cpl_imagelist_get(tmpcubes[i],m);
    pvdata=cpl_image_get_data_float(v_img);
    // computes sky at each point
    if ( y >= lly[i] && y < lly[i]+ily &&
	 x >= llx[i] && x < llx[i]+ilx ) {
      posx = x - llx[i] ;
      posy = y - lly[i] ;
      if (!isnan(pvdata[posx+posy*ilx]) &&
	  pvdata[posx+posy*ilx] != 0. &&
	  (cpl_vector_get(msk,i) != 0)) {

	msk_sum+= pmdata[x+y*mlx];

	val_msk_sum+=pvdata[posx+posy*ilx]*
	  pmdata[x+y*mlx];

      }
    }
  }

  podata[x+y*olx]=val_msk_sum/msk_sum;
  cpl_vector_delete(msk);

  return 0;

}

 */

/**

   @name   sinfo_new_combine_jittered_cubes_thomas_range()
   @param  cubes: list of jittered cubes to mosaic (over a given range
                  with kappa-sigma clipping of outliers)
   @param  mergedCube: resulting merged cube containing the
                                      jittered cubes
   @param  mask: cube of the same size as combinedCube
                 containing 0 for blank (ZERO pixels) and
                 the summed integration times for
                 overlapping regions

   @param n_cubes: number of cubes in the list to merge
   @param cumoffsetx: array of relative x pixel offsets
                      with respect to the first frame in the
                      same sequence as the cube list.

   @param cumoffsety: array of relative y pixel offsets
                      with respect to the first frame in the
                      same sequence as the cube list.

   @param exptimes: exposure times array giving the time
                    in the same sequence as the cube list
   @param kernel_type: the name of the interpolation kernel
                       that you want to generate using the
                       eclipse routine
                       sinfo_generate_interpolation_kernel()
                       Supported kernels are:
                       NULL:      default kernel, currently tanh
                                     "default": dito
                                     "tanh":    Hyperbolic tangent
                                     "sinc2":   Square sinc
                                     "lanczos": Lanczos2 kernel
                                     "hamming": Hamming kernel
                                     "hann":    Hann kernel
   @param z_min  minimum cube's plane processed
   @param z_max  maximum cube's plane processed
   @param kappa  value for kappa-sigma clipping

   @doc merges jittered data cubes to one bigger cube
        by averaging the overlap regions weighted by
        the integration times. The x, y size of the final data
        cube is user given, and should be between 32 and 64
        pixels, while the relative pixel-offset (sub-pixel
        accuracy) of the single cubes with respect to the
        first cube in the list is read from the
        SEQ CUMOFFSETX,Y fits header keyword.
 */

int
sinfo_new_combine_jittered_cubes_thomas_range(cpl_imagelist ** cubes,
                                              cpl_imagelist  * mergedCube,
                                              cpl_imagelist  * mask,
                                              int        n_cubes,
                                              float    * cumoffsetx,
                                              float    * cumoffsety,
                                              double    * exptimes,
                                              char     * kernel_type,
                                              const int z_min,
                                              const int z_max,
                                              const double kappa )
{
    const int VERY_BIG_INT = 268431360;
    int i ;
    int llx0, lly0 ;
    int* llx=NULL;
    int* lly=NULL ;
    float* sub_offsetx=NULL ;
    float* sub_offsety=NULL ;
    cpl_imagelist ** tmpcubes=NULL ;
    const int z_siz=z_max-z_min;
    int ilx=0;
    int ily=0;
    int olx=0;
    int oly=0;
    int mlx=0;
    int mly=0;
    //int onp=0;
    cpl_image* i_img=NULL;
    cpl_image* o_img=NULL;
    int min_lx = VERY_BIG_INT;
    int min_ly = VERY_BIG_INT;


    if(sinfo_check_input(cubes,n_cubes,cumoffsetx,cumoffsety,exptimes) == -1) {
        return -1;
    }

    if (z_siz <= 0 ){
        sinfo_msg_error ("z_max <= z_min given!") ;
        return -1 ;
    }

    i_img=cpl_imagelist_get(cubes[0],0);
    o_img=cpl_imagelist_get(mergedCube,0);
    ilx=cpl_image_get_size_x(i_img);
    ily=cpl_image_get_size_y(i_img);
    olx=cpl_image_get_size_x(o_img);
    oly=cpl_image_get_size_y(o_img);
    mlx=olx;
    mly=oly;
    //  sinfo_msg_warning(" cube size [%d:%d] merged cube size[%d:%d]" , ilx, ily, olx, oly);
    /*--------------------------------------------------------------------
     * center the cubes within the allocated big cube
     * that means define the (0,0) positions of the cubes in the image planes
     * to sub-pixel accuracy by using cumoffsetx,y and the reference cube
     */
    /* position of first reference frame, centered in big cube */
    llx0 = (1.0 * olx- 1.0 * ilx)/2.0 ;
    lly0 = (1.0 * oly - 1.0 * ily)/2.0 ;
    //  sinfo_msg_warning(" zero point [%d:%d]" , llx0, lly0);
    /*--------------------------------------------------------------------
     * go through the frame list and determine the lower left edge position
     * of the shifted cubes. Additionnally, the sub-pixel offsets are
     * determined.
     */

    llx=cpl_calloc(n_cubes,sizeof(int));
    lly=cpl_calloc(n_cubes,sizeof(int)) ;
    sub_offsetx=cpl_calloc(n_cubes,sizeof(float)) ;
    sub_offsety=cpl_calloc(n_cubes,sizeof(float)) ;

    for ( i = 0 ; i < n_cubes ; i++ ) {
        llx[i] = llx0 - sinfo_new_nint(cumoffsetx[i]) ;

        sub_offsetx[i] = (float)sinfo_new_nint(cumoffsetx[i]) - cumoffsetx[i] ;
        lly[i] = lly0 - sinfo_new_nint(cumoffsety[i]) ;
        sub_offsety[i] = (float)sinfo_new_nint(cumoffsety[i]) - cumoffsety[i] ;
        /*    sinfo_msg_warning("suboff[%d]= %f %f  ll[%d:%d] cumoffset[%f:%f]" ,
    		i,sub_offsetx[i],sub_offsety[i], llx[i], lly[i],
    		cumoffsetx[i], cumoffsety[i]);*/
        if (llx[i] < min_lx)
        {
            min_lx = llx[i];
        }
        if (lly[i] < min_ly)
        {
            min_ly = lly[i];
        }
    }
    /***********---------
     * "normalize" the shift - minimum should be 0
     **********************************************/
    if (min_lx != 0)
    {
        for (i = 0 ; i < n_cubes ; i++ )
        {
            llx[i] = llx[i] - min_lx;
        }
    }
    if (min_ly != 0)
    {
        for (i = 0 ; i < n_cubes ; i++ )
        {
            lly[i] = lly[i] - min_ly;
        }
    }

    /* -------------------------------------------------------------
     * shift the cubes according to the computed sub-pixel offsets
     * that means shift the single image planes of each cube
     * first determine an interpolation kernel
     */

    tmpcubes=(cpl_imagelist**)cpl_calloc(n_cubes,sizeof(cpl_imagelist*)) ;

    if(sinfo_shift_cubes(tmpcubes,kernel_type,n_cubes,cubes,z_min, z_max,
                    sub_offsetx,sub_offsety,mlx,mly,mask) == -1) {
    	cpl_free(llx);
    	cpl_free(lly) ;
        return -1;

    }


    /*-------------------------------------------------------------------------
     * Build the mask data cube.
     * The mask is 0 where no data is available, otherwise the integration
     * time of one frame, respectively the summed integration
     * times in the overlapping regions are inserted
     */
    /* go through the frame list */


    o_img=cpl_imagelist_get(mergedCube,0);
    olx=cpl_image_get_size_x(o_img);
    oly=cpl_image_get_size_y(o_img);
    //onp=cpl_imagelist_get_size(mergedCube);

    if(-1 == sinfo_build_mask_cube_thomas(z_min,z_max,olx,oly,n_cubes,llx,lly,
                    exptimes,cubes,tmpcubes,mask) ) {
        return -1;
    }
    /////////////////////////////////// use an optimized version ///////////////
    /*
  check_nomsg(sinfo_coadd_with_ks_clip_optimized(z_min,z_max,n_cubes,
                                           kappa,llx,lly,
                                           exptimes,mask,mergedCube,tmpcubes));
     */
    ////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////// not_optimized version ///////////////
    check_nomsg(sinfo_coadd_with_ks_clip2(z_min,z_max,ilx,ily,n_cubes,kappa,llx,lly,
                    exptimes,mask,mergedCube,tmpcubes));

    ////////////////////////////////////////////////////////////////////////////
    /* convert the "free space" in the cube to blank pixels */
    /* convert_0_to_ZERO_for_cubes(mergedCube) ; */
    /* convert_0_to_ZERO_for_cubes(mergedSky) ; */
    //cpl_free(kernel) ; /* originated by eclise-malloc */

    cleanup:

    for( i = 0 ; i < n_cubes ; i++ ) {
        cpl_imagelist_delete (tmpcubes[i]) ;
    }

    cpl_free(tmpcubes);
    cpl_free(llx);
    cpl_free(lly) ;
    cpl_free(sub_offsetx) ;
    cpl_free(sub_offsety) ;
    sinfo_print_rec_status(0);

    return 0 ;
}

/**
   @name sinfo_new_interpol_cube_simple()
   @param cube: 1 allocated cube
   @param badcube: bad pixel mask cube (0: bad, 1: good pixel)
   @param maxdist: maximal pixel distance from bad pixel
                   to search for good pixels, don't make this
                   value too big!
   @return interpolated cube, and corrected bad pixel mask cube
   @doc interpolates bad pixel of an object cube if a bad pixel
        mask cube is available by using the nearest neighbors
        in 3 dimensions.
 */
 /* TODO: not used */
cpl_imagelist *
sinfo_new_interpol_cube_simple( cpl_imagelist * cube,
                                cpl_imagelist * badcube,
                                int       maxdist )
{
    cpl_imagelist * intercube ;
    float*     goodNeighbors=NULL ;
    int z, row, col ;
    int nx, ny, nz ;
    int llx, lly, llz ;
    int zi, coli, rowi ;
    int n ;

    //int bly=0;

    int cnp=0;







    cpl_image* bzi_img=NULL;
    cpl_image* czi_img=NULL;



    if ( cube == NULL || badcube == NULL )
    {
        sinfo_msg_error("no cube given!") ;
        return NULL ;
    }
    if ( maxdist < 1 )
    {
        sinfo_msg_error("wrong maxrad given!") ;
        return NULL ;
    }
    intercube = cpl_imagelist_duplicate(cube) ;

    goodNeighbors=cpl_calloc((2*maxdist+1)*(2*maxdist+1)*(2*maxdist+1) -1,
                    sizeof(float)) ;

    cnp=cpl_imagelist_get_size(cube);
    float* pczidata=NULL;
    float* pbzidata=NULL;
    for ( z = 0 ; z < cnp ; z++ )
    {
        cpl_image* b_img=cpl_imagelist_get(badcube,z);
        cpl_image* i_img=cpl_imagelist_get(intercube,z);
        float* pbdata=cpl_image_get_data_float(b_img);
        float* pidata=cpl_image_get_data_float(i_img);
        int blx=cpl_image_get_size_x(b_img);
        //bly=cpl_image_get_size_y(b_img);

        cpl_image* c_img=cpl_imagelist_get(cube,z);
        int clx=cpl_image_get_size_x(c_img);
        int cly=cpl_image_get_size_y(c_img);

        for ( row = 0 ; row < cly ; row++ )
        {
            for ( col = 0 ; col < clx ; col++ )
            {
                if ( pbdata[col+row*clx] == 0 )
                {
                    /* determine the lower left sinfo_edge of the cube */
                    llx = col - maxdist ;
                    nx = 2*maxdist +1 ;
                    if (llx < 0)
                    {
                        nx += llx ;
                        llx = 0 ;
                    }
                    if ( llx + nx > clx )
                    {
                        nx -= (llx + nx - clx) ;
                    }

                    lly = row - maxdist ;
                    ny = 2*maxdist +1 ;
                    if (lly < 0)
                    {
                        ny += lly ;
                        lly = 0 ;
                    }
                    if ( lly + ny > cly )
                    {
                        ny -= (lly + ny - cly) ;
                    }

                    llz = z - maxdist ;
                    nz = 2*maxdist +1 ;
                    if (llz < 0)
                    {
                        nz += llz ;
                        llz = 0 ;
                    }
                    if ( llz + nz > cnp )
                    {
                        nz -= (llz + nz - cnp) ;
                    }
                    n = 0 ;
                    for ( zi = llz ; zi < llz+nz ; zi++ )
                    {
                        bzi_img=cpl_imagelist_get(badcube,zi);
                        czi_img=cpl_imagelist_get(cube,zi);
                        pbzidata=cpl_image_get_data_float(bzi_img);
                        pczidata=cpl_image_get_data_float(czi_img);

                        for ( rowi = lly ; rowi < lly+ny ; rowi++ )
                        {
                            for ( coli = llx ; coli < llx+nx ; coli++ )
                            {
                                if ( pbzidata[coli+rowi*blx] == 1 )
                                {
                                    goodNeighbors[n] = pczidata[coli+rowi*clx] ;
                                    n++ ;
                                }
                            }
                        }
                    }
                    if ( n > 0 )
                    {
                        pidata[col+row*clx]=sinfo_new_median(goodNeighbors,n);
                        pbdata[col+row*clx]=1 ;
                    }
                    else
                    {
                        continue ;
                    }
                }
            }
        }
    }
    cpl_free(goodNeighbors) ;
    return intercube ;
}





/**

   @name   sinfo_new_combine_cubes()
   @param  cubes: list of jittered cubes to mosaic
   @param  mergedCube: resulting merged cube containing the
                                      jittered cubes
   @param  mask: cube of the same size as combinedCube
                 containing 0 for blank (ZERO pixels) and
                 the summed integration times for
                 overlapping regions

   @param n_cubes: number of cubes in the list to merge
   @param cumoffsetx: array of relative x pixel offsets
                      with respect to the first frame in the
                      same sequence as the cube list.

   @param cumoffsety: array of relative y pixel offsets
                      with respect to the first frame in the
                      same sequence as the cube list.


   @factor: sigma factor beyond which pixels are thrown away.
   @param kernel_type: the name of the interpolation kernel
                       that you want to generate using the
                       eclipse routine
                       sinfo_generate_interpolation_kernel()
                       Supported kernels are:
                       NULL:      default kernel, currently tanh
                                     "default": dito
                                     "tanh":    Hyperbolic tangent
                                     "sinc2":   Square sinc
                                     "lanczos": Lanczos2 kernel
                                     "hamming": Hamming kernel
                                     "hann":    Hann kernel

   @return mask: cube of the same size as combinedCube
                containing 0 for blank (ZERO pixels) and
                n used pixels for overlapping regions
          mergedCube: final data cube containing the jittered cubes


   @doc          merges jittered data cubes to one bigger cube
                 by taking the sinfo_new_median in each pixel and throw
                 away the high deviation pixels (bigger than factor * sigma)
                 The x, y size of the final data
                 cube is user given, and should be between 32 and 64
                 pixels, while the relative pixel-offset (sub-pixel
                 accuracy) of the single cubes with respect to the
                 first cube in the list is read from the SEQ CUMOFFSETX,Y
                 fits header keyword.
 */
/* TODO: not used */
cpl_imagelist *
sinfo_new_combine_cubes ( cpl_imagelist ** cubes,
                          cpl_imagelist  * mergedCube,
                          int        n_cubes,
                          float    * cumoffsetx,
                          float    * cumoffsety,
                          float      factor,
                          char     * kernel_type )
{
    int i=0 ;
    int x=0;
    int y=0;
    int z=0;
    int llx0=0;
    int lly0=0;
    int posx=0;
    int posy=0;
    cpl_imagelist * mask=NULL ;
    double * kernel=NULL ;
    cpl_image * shiftedImage=NULL ;
    int n=0;
    int ns=0;
    double sum=0;
    double sum2=0;
    double mean=0;
    double sigma=0;

    cpl_imagelist ** tmpcubes=NULL ;

    int* llx=NULL ;
    int* lly=NULL ;

    float* sub_offsetx=NULL ;
    float* sub_offsety=NULL ;
    float* cubedata=NULL ;

    int mlx=0;
    int mly=0;
    int clx=0;
    int cly=0;
    int mnp=0;
    int cnp=0;


    float* ptdata=NULL;
    float* podata=NULL;
    float* pmdata=NULL;

    cpl_image* tmp_img=NULL;
    cpl_image* o_img=NULL;
    cpl_image* m_img=NULL;
    cpl_image* c_img=NULL;
    cpl_image* t_img=NULL;




    if ( cubes == NULL )
    {
        sinfo_msg_error ("no cube list given!") ;
        return NULL ;
    }


    if ( mergedCube == NULL )
    {
        sinfo_msg_error ("no out cube  given!") ;
        return NULL ;
    }


    if ( n_cubes <= 0 )
    {
        sinfo_msg_error ("wrong number of data cubes in list!") ;
        return NULL ;
    }
    if ( cumoffsetx == NULL || cumoffsety == NULL )
    {
        sinfo_msg_error ("no cumoffsetx/y given!") ;
        return NULL;
    }

    if ( factor <= 0. )
    {
        sinfo_msg_error ("wrong factor given!") ;
        return NULL ;
    }

    m_img=cpl_imagelist_get(mergedCube,0);
    mlx=cpl_image_get_size_x(m_img);
    mly=cpl_image_get_size_y(m_img);
    cnp=cpl_imagelist_get_size(cubes[0]);
    c_img=cpl_imagelist_get(cubes[0],0);
    clx=cpl_image_get_size_x(c_img);
    cly=cpl_image_get_size_y(c_img);


    tmpcubes=(cpl_imagelist**)cpl_calloc(n_cubes,sizeof(cpl_imagelist*)) ;

    /* allocation for a cube structure without the image planes  */
    /*
   for ( i = 0 ; i < n_cubes ; i++ )
    {
         tmpcubes[i] = (cpl_imagelist*)cpl_malloc(sizeof(cpl_imagelist)) ;
        tmpcubes[i]->plane = (cpl_image**)cpl_calloc(cubes[0]->np ,
                              sizeof(cpl_image*)) ;

        tmpcubes[i]->lx = cubes[0]->lx ;
        tmpcubes[i]->ly = cubes[0]->ly ;
        tmpcubes[i]->np = cubes[0]->np ;
        tmpcubes[i]->nbpix = (ulong32)cubes[0]->lx *
                             (ulong32)cubes[0]->ly *
                             (ulong32)cubes[0]->np ;
        tmpcubes[i]->history = (char*)NULL ;
        tmpcubes[i]->n_comments = 0 ;
        tmpcubes[i]->orig_ptype = BPP_DEFAULT ;
        tmpcubes[i]->filename = NULL ;
    }
     */
    tmpcubes[0]=cpl_imagelist_duplicate(cubes[0]);

    /*--------------------------------------------------------------------
     * center the cubes within the allocated big cube
     * that means define the (0,0) positions of the cubes in the image planes
     * to sub-pixel accuracy by using cumoffsetx,y and the reference cube
     */
    /* position of first reference frame, centered in big cube */
    llx0 = mlx/2 - clx/2 ;
    lly0 = mly/2 - cly/2 ;

    /*--------------------------------------------------------------------
     * go through the frame list and determine the lower left edge position
     * of the shifted cubes. Additionnally, the sub-pixel offsets are
     * determined.
     */


    llx=cpl_calloc(n_cubes,sizeof(int)) ;
    lly=cpl_calloc(n_cubes,sizeof(int)) ;
    sub_offsetx=cpl_calloc(n_cubes,sizeof(float)) ;
    sub_offsety=cpl_calloc(n_cubes,sizeof(float)) ;

    for ( i = 0 ; i < n_cubes ; i++ )
    {
        llx[i] = llx0 - sinfo_new_nint(cumoffsetx[i]) ;
        sub_offsetx[i] = (float)sinfo_new_nint(cumoffsetx[i]) - cumoffsetx[i] ;
        lly[i] = lly0 - sinfo_new_nint(cumoffsety[i]) ;
        sub_offsety[i] = (float)sinfo_new_nint(cumoffsety[i]) - cumoffsety[i] ;
    }

    /* -------------------------------------------------------------
     * shift the cubes according to the computed sub-pixel offsets
     * that means shift the single image planes of each cube
     * first determine an interpolation kernel
     */
    if ( NULL == (kernel = sinfo_generate_interpolation_kernel(kernel_type)) )
    {
        sinfo_msg_warning ("could not generate desired interpolation kernel"
                        " or no kernel_typ was given, the default kernel"
                        " is used now!") ;
    }
    /* go through the frame list */
    for ( i = 0 ; i < n_cubes ; i++ )
    {
        /* go through the image planes and shift each plane by a
         sub-pixel value */
        for ( z = 0 ; z < cnp ; z++ )
        {
            tmp_img=cpl_imagelist_get(cubes[i],z);
            if ( NULL == (shiftedImage = sinfo_new_shift_image(tmp_img,
                            sub_offsetx[i],
                            sub_offsety[i],
                            kernel ) ) )
            {
                sinfo_msg_error ("could not shift image plane no %d "
                                "in cube no %d!", z, i) ;
                cpl_imagelist_delete(mergedCube) ;
                cpl_imagelist_delete(mask) ;
                cpl_free(kernel) ;
                for( i = 0 ; i < n_cubes ; i++ ) {
                      cpl_imagelist_delete (tmpcubes[i]) ;
                }
                cpl_free(tmpcubes);
                return NULL ;
            }
            cpl_imagelist_set(tmpcubes[i],shiftedImage,z);
        }
    }

    cubedata=cpl_calloc(n_cubes,sizeof(float)) ;

    for ( y = 0 ; y < mly ; y++ )
    {
        for ( x = 0 ; x < mlx ; x++ )
        {
            for ( z = 0 ; z < mnp ; z++ )
            {
                sum = 0. ;
                sum2 = 0. ;
                n = 0 ;
                for ( i = 0 ; i < n_cubes ; i++ )
                {
                    c_img=cpl_imagelist_get(cubes[i],z);

                    clx=cpl_image_get_size_x(c_img);
                    cly=cpl_image_get_size_y(c_img);

                    t_img=cpl_imagelist_get(tmpcubes[i],z);
                    ptdata=cpl_image_get_data_float(t_img);

                    m_img=cpl_imagelist_get(mergedCube,z);
                    pmdata=cpl_image_get_data_float(m_img);
                    o_img=cpl_imagelist_get(mask,z);
                    podata=cpl_image_get_data_float(o_img);
                    /*
                    find the position of the present cube and go
                    through the single spectra
                     */
                    if ( y >= lly[i] && y < lly[i]+cly &&
                                    x >= llx[i] && x < llx[i]+clx )
                    {
                        posx = x - llx[i] ;
                        posy = y - lly[i] ;
                        if (!isnan(ptdata[posx+posy*clx]))
                        {
                            sum += ptdata[posx+posy*clx] ;
                            sum2 += (ptdata[posx+posy*clx] *
                                            ptdata[posx+posy*clx]) ;
                            cubedata[n] = ptdata[posx+posy*clx] ;
                            n++ ;
                        }
                    }
                }

                if ( n == 0 )
                {
                    mean = 0. ;
                    sigma = 0. ;
                    pmdata[x+y*mlx] = 0. ;
                    podata[x+y*mlx] = 0 ;
                }
                else if ( n == 1 )
                {
                    mean = sum ;
                    sigma = 0. ;
                    pmdata[x+y*mlx] = mean ;
                    podata[x+y*mlx] = 1 ;
                }
                else
                {
                    mean = sum/(double)n ;
                    sigma = sqrt( (sum2 - sum*mean) / (double)(n - 1) ) ;
                    ns = 0 ;
                    for ( i = 0 ; i < n ; i++ )
                    {
                        if ( cubedata[i] > mean+factor*sigma ||
                                        cubedata[i] < mean-factor*sigma )
                        {
                            continue ;
                        }
                        else
                        {
                            pmdata[x+y*mlx] += cubedata[i] ;
                            ns++ ;
                        }
                    }
                    if ( ns == 0 )
                    {
                        pmdata[x+y*mlx] = 0. ;
                    }
                    else
                    {
                        pmdata[x+y*mlx] /= (float)ns ;
                    }
                    podata[x+y*mlx] = (float)ns ;
                }
            }
        }
    }

    for( i = 0 ; i < n_cubes ; i++ )
    {
        cpl_imagelist_delete (tmpcubes[i]) ;
    }
    cpl_free(tmpcubes);
    cpl_free(llx);
    cpl_free(lly);
    cpl_free(sub_offsetx);
    cpl_free(sub_offsety);
    cpl_free(cubedata);

    /* convert the "free space" in the cube to blank pixels */
    sinfo_new_convert_0_to_ZERO_for_cubes(mergedCube) ;
    cpl_free(kernel) ;
    return mask ;
}

cpl_imagelist *
sinfo_new_bin_cube(cpl_imagelist *cu,
                   int xscale,
                   int yscale,
                   int xmin,
                   int xmax,
                   int ymin,
                   int ymax)
{
    int i,j,k;
    cpl_imagelist * cube;
    int ilx=0;
    //int ily=0;
    int olx=0;
    int oly=0;
    int inp=0;



    cpl_image* i_img=NULL;



    /* old code
  if (NULL == (cube = sinfo_newCube (xmax-xmin+1,ymax-ymin+1, cu->np)) )
  {
      sinfo_msg_error ("cannot allocate new cube") ;
      return NULL ;
  }
     */
    inp=cpl_imagelist_get_size(cu);
    i_img=cpl_imagelist_get(cu,0);
    ilx=cpl_image_get_size_x(i_img);
    //ily=cpl_image_get_size_y(i_img);
    olx=xmax-xmin+1;
    oly=ymax-ymin+1;


    cube=cpl_imagelist_new();
    cpl_image* o_img=NULL;
    for ( i = 0 ; i < inp ; i++ ) {
        o_img = cpl_image_new(olx,oly,CPL_TYPE_FLOAT);
        cpl_imagelist_set(cube,o_img,i);
    }


    for (i=0;i<inp;i++){
        cpl_image* i_img=cpl_imagelist_get(cu,i);
        float* pidata=cpl_image_get_data_float(i_img);
        o_img=cpl_imagelist_get(cube,i);
        float* podata=cpl_image_get_data_float(o_img);
        for (j=0 ; j < olx ; j++) {
            for (k=0 ; k< oly ; k++) {
                podata[j+k*olx]=pidata[((int) (j+xmin)/xscale)+
                                       ((int) (k+ymin)/yscale)*ilx]/
                                       (xscale*yscale);
            }
        }
    }

    return cube;
}

/* TODO: not used */
cpl_imagelist *
sinfo_new_scale_cube(cpl_imagelist *cu,
                     float xscale,
                     float yscale,
                     char * kernel_type)
{
    cpl_imagelist    *    cube ;
    int             i=0, j=0, k=0, l=0 ;
    int             lx_out, ly_out ;
    double           cur ;
    double      *    invert_transform ;
    double           neighbors[16] ;
    double           rsc[8],
    sumrs ;
    double        param[6];
    double           x, y ;
    int             px, py ;
    int             pos ;
    int             tabx, taby ;
    double      *    kernel ;
    int                  leaps[16] ;
    int                 ilx=0;
    int                 ily=0;

    int                 inp;
    cpl_image*          in_img=NULL;


    if (cu == NULL)
    {
        sinfo_msg_error ("null cube") ;
        return NULL ;
    }

    param[0]=xscale;
    param[1]=0;
    param[2]=0;
    param[3]=0;
    param[4]=yscale;
    param[5]=0;


    invert_transform = sinfo_invert_linear_transform(param) ;
    if (invert_transform == NULL) {
        sinfo_msg_error("cannot compute sinfo_invert transform: "
                        "aborting warping") ;
        return NULL ;
    }

    /* Generate default interpolation kernel */
    kernel = sinfo_generate_interpolation_kernel(kernel_type) ;
    if (kernel == NULL) {
        sinfo_msg_error("cannot generate kernel: aborting resampling") ;
        return NULL ;
    }

    /* Compute new image size   */
    /* Compute new image size   */
    ilx=cpl_image_get_size_x(cpl_imagelist_get(cu,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cu,0));
    inp=cpl_imagelist_get_size(cu);

    lx_out = (int) ilx*xscale ;
    ly_out = (int) ily*yscale ;

    cube=cpl_imagelist_new();
    for ( l = 0 ; l < inp ; l++ ) {
        in_img = cpl_image_new(ilx,ily,CPL_TYPE_FLOAT);
        cpl_imagelist_set(cube,in_img,l);
    }

    /* old code
    if (NULL == (cube = sinfo_newCube (lx_out, ly_out, cu->np)) )
    {
        sinfo_msg_error (" cannot allocate new cube") ;
        return NULL ;
    }
     */

    for (l=0;l<inp;l++){
        in_img=cpl_imagelist_get(cu,l);
        cpl_image* ou_img=cpl_imagelist_get(cube,l);
        int tlx=cpl_image_get_size_x(in_img);
        int tly=cpl_image_get_size_y(in_img);
        float* podata=cpl_image_get_data_float(ou_img);
        /* Pre compute leaps for 16 closest neighbors positions */
        leaps[0] = -1 - tlx ;
        leaps[1] =    - tlx ;
        leaps[2] =  1 - tlx ;
        leaps[3] =  2 - tlx ;

        leaps[4] = -1 ;
        leaps[5] =  0 ;
        leaps[6] =  1 ;
        leaps[7] =  2 ;

        leaps[8] = -1 + tlx ;
        leaps[9] =      tlx ;
        leaps[10]=  1 + tlx ;
        leaps[11]=  2 + tlx ;

        leaps[12]= -1 + 2*tlx ;
        leaps[13]=      2*tlx ;
        leaps[14]=  1 + 2*tlx ;
        leaps[15]=  2 + 2*tlx ;

        /* Double loop on the output image  */
        for (j=0 ; j < ly_out ; j++) {
            for (i=0 ; i< lx_out ; i++) {
                /* Compute the original source for this pixel   */

                x = invert_transform[0] * (double)i +
                                invert_transform[1] * (double)j +
                                invert_transform[2] ;

                y = invert_transform[3] * (double)i +
                                invert_transform[4] * (double)j +
                                invert_transform[5] ;

                /* Which is the closest integer positioned neighbor?    */
                px = (int)x ;
                py = (int)y ;

                if ((px < 1) ||
                                (px > (tlx-2)) ||
                                (py < 1) ||
                                (py > (tly-2)))
                    podata[i+j*lx_out] = (pixelvalue)0.0 ;
                else {
                    /* Now feed the positions for the closest 16 neighbors  */
                    pos = px + py * tlx ;
                    for (k=0 ; k<16 ; k++){
                        if(!isnan(podata[(int)(pos+leaps[k])])) neighbors[k] =
                                        (double)(podata[(int)(pos+leaps[k])]) ;
                        else neighbors[k]=0;
                    }

                    /* Which tabulated value index shall we use?    */
                    tabx = (x - (double)px) * (double)(TABSPERPIX) ;
                    taby = (y - (double)py) * (double)(TABSPERPIX) ;

                    /* Compute resampling coefficients  */
                    /* rsc[0..3] in x, rsc[4..7] in y   */

                    rsc[0] = kernel[TABSPERPIX + tabx] ;
                    rsc[1] = kernel[tabx] ;
                    rsc[2] = kernel[TABSPERPIX - tabx] ;
                    rsc[3] = kernel[2 * TABSPERPIX - tabx] ;
                    rsc[4] = kernel[TABSPERPIX + taby] ;
                    rsc[5] = kernel[taby] ;
                    rsc[6] = kernel[TABSPERPIX - taby] ;
                    rsc[7] = kernel[2 * TABSPERPIX - taby] ;

                    sumrs = (rsc[0]+rsc[1]+rsc[2]+rsc[3]) *
                                    (rsc[4]+rsc[5]+rsc[6]+rsc[7]) ;

                    /* Compute interpolated pixel now   */
                    cur =   rsc[4] * (  rsc[0]*neighbors[0] +
                                    rsc[1]*neighbors[1] +
                                    rsc[2]*neighbors[2] +
                                    rsc[3]*neighbors[3] ) +
                                    rsc[5] * (  rsc[0]*neighbors[4] +
                                                    rsc[1]*neighbors[5] +
                                                    rsc[2]*neighbors[6] +
                                                    rsc[3]*neighbors[7] ) +
                                                    rsc[6] * (  rsc[0]*neighbors[8] +
                                                                    rsc[1]*neighbors[9] +
                                                                    rsc[2]*neighbors[10] +
                                                                    rsc[3]*neighbors[11] ) +
                                                                    rsc[7] * (  rsc[0]*neighbors[12] +
                                                                                    rsc[1]*neighbors[13] +
                                                                                    rsc[2]*neighbors[14] +
                                                                                    rsc[3]*neighbors[15] ) ;

                    /* Affect the value to the output image */
                    podata[i+j*lx_out] = (pixelvalue)(cur/sumrs) ;
                    /* done ! */
                }
            }
        }
    }
    cpl_free(kernel) ;
    cpl_free(invert_transform) ;
    return cube ;
}


/**
 @short shifts an imagelist by a given amount to integer pixel accuracy
 @name sinfo_cube_zshift
 @param cube  input cube to be shifted
 @param shift amount of z shift to be applied
 @param rest  amout of fractional shift remaining (<1)

 */
/* TODO: not used */
cpl_imagelist *
sinfo_cube_zshift(const cpl_imagelist * cube_inp,
                  const double shift,
                  double* sub_shift)
{

    cpl_imagelist * cube_out=NULL ;
    const cpl_image* img_inp=NULL;
    cpl_image* img_out=NULL;
    int        col, row,z ;
    int        int_shift ;
    int ilx=0;
    int ily=0;
    int ilz=0;

    int olx=0;
    int oly=0;
    int olz=0;
    int i=0;
    const float* pidata=NULL;
    float* podata=NULL;

    cknull(cube_inp,"no input cube given!") ;
    check_nomsg(img_inp=cpl_imagelist_get_const(cube_inp,0));
    check_nomsg(ilx=cpl_image_get_size_x(img_inp));
    check_nomsg(ily=cpl_image_get_size_y(img_inp));
    check_nomsg(ilz=cpl_imagelist_get_size(cube_inp));

    olx=ilx;
    oly=ily;
    olz=ilz;

    int_shift = sinfo_new_nint(shift) ;
    *sub_shift = shift - (double) int_shift ;
    if ( int_shift == 0 )
    {
        cube_out =cpl_imagelist_duplicate(cube_inp) ;
        return cube_out ;
    }
    else
    {
        /* allocate memory */
        cknull(cube_out = cpl_imagelist_new(),"could not allocate memory!") ;
        for ( i = 0 ; i < olz ; i++ ) {
            check_nomsg(img_out=cpl_image_new(olx,oly,CPL_TYPE_FLOAT));
            check_nomsg(cpl_imagelist_set(cube_out,img_out,i));
        }
    }

    for(z=0; z< ilz; z++) {
        if ( (z-int_shift >= 0 ) && (z - int_shift < olz) ) {
            check_nomsg(img_inp=cpl_imagelist_get_const(cube_inp,z));
            check_nomsg(img_out=cpl_imagelist_get(cube_out,z-int_shift));
            check_nomsg(pidata=cpl_image_get_data_float_const(img_inp));
            check_nomsg(podata=cpl_image_get_data_float(img_out));
            for ( col = 0 ; col < ilx ; col++ ) {
                for ( row = 0 ; row < ily ; row++ ) {
                    podata[col+row*olx] = pidata[col+row*olx] ;
                }
            }
        }
    }
    return cube_out ;

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

/**
 @short shifts an imagelist by a given amount to sub-pixel accuracy
 @name sinfo_cube_zshift
 @param cube  input cube to be shifted
 @param shift amount of z shift to be applied
 @param order polynomial order

 */
/* TODO: not used */
cpl_imagelist *
sinfo_cube_zshift_poly(const cpl_imagelist * cube_inp,
                       const double sub_shift,
                       const int    order)
{
    cpl_imagelist * cube_out ;

    float* spec=NULL ;
    float* corrected_spec=NULL ;
    float* xnum=NULL ;

    float sum=0;
    float new_sum=0 ;
    float eval=0 ;
    float * imageptr=NULL ;
    int row=0;
    int col=0 ;
    int firstpos=0 ;
    int n_points=0 ;
    int i=0 ;
    int flag=0;
    int ilx=0;
    int ily=0;
    int ilz=0;

    int olx=0;
    int oly=0;
    //int olz=0;
    int z=0;

    const float* pidata=NULL;
    float* podata=NULL;
    const cpl_image* img_inp=NULL;
    cpl_image* img_out=NULL;

    if ( cube_inp == NULL ) {
        sinfo_msg_error("no imagelist given!") ;
        return NULL ;
    }

    img_inp=cpl_imagelist_get_const(cube_inp,0);

    ilx=cpl_image_get_size_x(img_inp);
    ily=cpl_image_get_size_y(img_inp);
    ilz=cpl_imagelist_get_size(cube_inp);

    if ( order <= 0 ) {
        sinfo_msg_error("wrong order of interpolation polynom given!") ;
        return NULL ;
    }


    olx=ilx;
    oly=ily;
    //olz=ilz;
    /* allocate memory */

    if ( NULL == (cube_out = cpl_imagelist_new()) ) {
        sinfo_msg_error ("could not allocate memory!") ;
        return NULL ;
    } else {
        for ( i = 0 ; i < ilz ; i++ ) {
            img_out=cpl_image_new(olx,oly,CPL_TYPE_FLOAT);
            cpl_imagelist_set(cube_out,img_out,i);
        }
    }


    n_points = order + 1 ;
    if ( n_points % 2 == 0 ) {
        firstpos = (int)(n_points/2) - 1 ;
    } else {
        firstpos = (int)(n_points/2) ;
    }

    spec=cpl_calloc(ilz,sizeof(float)) ;
    corrected_spec=cpl_calloc(ilz,sizeof(float)) ;
    xnum=cpl_calloc(order+1,sizeof(float)) ;


    /* fill the xa[] array for the polint function */
    for ( i = 0 ; i < n_points ; i++ ) {
        xnum[i] = i ;
    }

    for ( col = 0 ; col < ilx ; col++ ) {
        for ( row = 0 ; row < ily ; row++ ) {
            for( z=0; z< ilz; z++) {
                corrected_spec[z] = 0. ;
            }
            sum = 0. ;
            for ( z = 0 ; z < ilz ; z++ ) {
                img_inp=cpl_imagelist_get_const(cube_inp,z);
                pidata=cpl_image_get_data_float_const(img_inp);
                spec[z] = pidata[col + row*ilx] ;
                if (isnan(spec[z]) ) {
                    spec[z] = 0. ;

                    for ( i = z - firstpos ; i < z-firstpos+n_points ; i++ ) {
                        if ( i < 0 ) continue ;
                        if ( i >= ilz) continue  ;
                        corrected_spec[i] = ZERO ;
                    }
                }
                if ( z != 0 && z != ilz - 1 ) {
                    sum += spec[z] ;
                }

            }

            new_sum = 0. ;
            for ( z = 0 ; z < ilz ; z++ ) {

                /* ---------------------------------------------------------------
                 * now determine the arrays of size n_points with which the
                 * polynom is determined and determine the position eval
                 * where the polynom is evaluated in polynomial interpolation.
                 * Take care of the points near the row edges!
                 */
                if (isnan(corrected_spec[z])) continue ;
                if ( z - firstpos < 0 ) {
                    imageptr = &spec[0] ;
                    eval     = sub_shift + z ;
                } else if ( z - firstpos + n_points >= ilz ) {
                    imageptr = &spec[ilz - n_points] ;
                    eval     = sub_shift + z + n_points - ilz ;
                } else {
                    imageptr = &spec[z-firstpos] ;
                    eval     = sub_shift + firstpos ;
                }

                flag=0;
                corrected_spec[z]=sinfo_new_nev_ille(xnum,imageptr,order,eval,&flag);
                if ( z != 0 && z != ilz - 1 ) {
                    new_sum += corrected_spec[z] ;
                }
            }

            /* fill the output spectrum */
            for (z = 0 ; z < ilz ; z++ )
            {
                img_out=cpl_imagelist_get(cube_out,z);
                podata=cpl_image_get_data_float(img_out);
                if ( new_sum == 0. ) {
                    new_sum = 1. ;
                }
                if ( z == 0 ) {
                    podata[col+row*olx] = ZERO ;
                } else if ( z == ilz - 1 ) {
                    podata[col+row*olx] = ZERO ;
                } else if ( isnan(corrected_spec[z]) ) {
                    podata[col+row*olx] = ZERO ;
                } else {
                    corrected_spec[z] *= sum / new_sum ;
                    podata[col+row*olx] = corrected_spec[z] ;
                }
            }

        }
    }

    cpl_free(spec) ;
    cpl_free(corrected_spec) ;
    cpl_free(xnum) ;
    return cube_out ;


}

/**
 @short shifts an imagelist by a given amount to sub-pixel accuracy
 @name sinfo_cube_zshift
 @param cube  input cube to be shifted
 @param shift amount of z shift to be applied

 */
/* TODO: not used */
cpl_imagelist *
sinfo_cube_zshift_spline3(const cpl_imagelist * cube_inp,
                          const double sub_shift)
{

    cpl_imagelist * cube_out=NULL ;
    float* spec=NULL ;
    float* corrected_spec=NULL ;
    float* xnum=NULL ;
    float* eval=NULL ;
    float sum=0;
    float new_sum=0 ;
    int row=0;
    int col=0;
    int i=0;
    int z=0;

    int ilx=0;
    int ily=0;
    int ilz=0;
    int olx=0;
    int oly=0;
    //int olz=0;

    const float* pidata=NULL;
    float* podata=NULL;
    const cpl_image* img_inp=NULL;
    cpl_image* img_out=NULL;

    if ( cube_inp == NULL ) {
        sinfo_msg_error("no imagelist given!") ;
        return NULL ;
    }

    img_inp=cpl_imagelist_get_const(cube_inp,0);
    ilx=cpl_image_get_size_x(img_inp);
    ily=cpl_image_get_size_y(img_inp);
    ilz=cpl_imagelist_get_size(cube_inp);


    olx=ilx;
    oly=ily;
    //olz=ilz;
    /* allocate memory */
    if ( NULL == (cube_out = cpl_imagelist_new()) ) {
        sinfo_msg_error ("could not allocate memory!") ;
        return NULL ;
    } else {
        for ( i = 0 ; i < ilz ; i++ ) {
            img_out=cpl_image_new(olx,oly,CPL_TYPE_FLOAT);
            cpl_imagelist_set(cube_out,img_out,i);
        }
    }

    xnum=cpl_calloc(ilz,sizeof(float)) ;
    /* fill the xa[] array for the spline function */
    for ( i = 0 ; i < ilz ; i++ ) {
        xnum[i] = i ;
    }

    spec=cpl_calloc(ilz,sizeof(float)) ;
    corrected_spec=cpl_calloc(ilz,sizeof(float)) ;
    eval=cpl_calloc(ilz,sizeof(float)) ;

    for ( col = 0 ; col < ilx ; col++ ) {
        for ( row = 0 ; row < ily ; row++ ) {
            sum = 0. ;
            for ( z = 0 ; z < ilz ; z++ ) {
                img_inp=cpl_imagelist_get_const(cube_inp,z);
                pidata=cpl_image_get_data_float_const(img_inp);
                spec[z] = pidata[col + row*ilx] ;
                if (isnan(spec[z]) ) {
                    for ( i = z-1 ; i <= z+1 ; i++ ) {
                        if ( i < 0 ) continue ;
                        if ( i >= ilz) continue ;
                        corrected_spec[i] = ZERO ;
                    }
                    spec[z] = 0. ;
                }
                sum += spec[z] ;
                eval[z] = (float)sub_shift+(float)z ;
            }
            /* now we do the spline interpolation*/
            if ( -1 == sinfo_function1d_natural_spline( xnum, spec, ilz, eval,
                            corrected_spec, ilz ) )
            {
                sinfo_msg_error("error in spline interpolation!") ;
                return NULL ;
            }

            new_sum = 0. ;
            for ( z = 0 ; z < ilz ; z++ ) {
                if ( isnan(corrected_spec[z]) ) {
                    continue ;
                }
                new_sum += corrected_spec[z] ;
            }
            /* fill output imagelist */
            for ( z = 0 ; z < ilz ; z++ ) {
                img_out=cpl_imagelist_get(cube_out,z);
                podata=cpl_image_get_data_float(img_out);
                if ( new_sum == 0. ) new_sum =1. ;
                {
                    if ( isnan(corrected_spec[z]) ) {
                        podata[col+row*olx] = ZERO ;
                    } else {
                        corrected_spec[z] *= sum / new_sum ;
                        podata[col+row*olx] = corrected_spec[z] ;
                    }
                }
            }
        }
    }
    cpl_free(xnum);
    cpl_free(spec) ;
    cpl_free(corrected_spec) ;
    cpl_free(eval) ;

    return cube_out ;
}

/*  kappa-sigma optimized version */
/* The structure for string index data for kappa-sigma
 *
 * */
struct _CubeData
{
    int iCubeNumber;
    int iLocalX;
    int iLocalY;
};
typedef struct _CubeData CubeData;

struct _CubeDataVector
{
    int size;
    CubeData** pdata;
};
typedef struct _CubeDataVector CubeDataVector;
/**
 @short implamentation of kappa-sigma clipping
 @name sinfo_kappa_sigma_offset_with_mask
 @param globalSizeX size of the result image by X axis
 @param globalSizeY size of the result image by Y axis
 @param z_min  plane number to start coaadition
 @param z_max  the last plane number to be used for coaddition
 @param nCubes number of cubes in the inputCubes array
 @param inputCubes array of the input cubes, each cube is an cpl_imagelist
 @param exptimes exposure times array, one element for each cube in inputCubes
 @param imResult result cube
 @param offsetX each element represents the offset of the corresponding cube by X axis
 @param offsetY each element represents the offset of the corresponding cube by Y axis
 @param skyMask output parameter shows "bad pixels", pixels which were not taken to calculation due kappa-sigma
 @param kappa parameter for kappa-sigma algorithm

 */
static int sinfo_kappa_sigma_offset_with_mask(
                int z_min,
                int z_max,
                int nCubes,
                cpl_imagelist** inputCubes,
                const double* exptimes,
                cpl_imagelist* imResult,
                int* offsetX,
                int* offsetY,
                cpl_imagelist* sky_mask,
                const double kappa
);
static void
sinfo_kappa_sigma_CubeDataVector(
                int globalX,
                int globalY,
                CubeDataVector* pCubeDataVector,
                cpl_imagelist* imlistResult,
                cpl_imagelist** input_cubes,
                cpl_imagelist* sky_mask,
                int iPlanesNumber,
                int z_min,
                const double kappa,
                const double* exptimes
);

double
sinfo_kappa_sigma_array_with_mask(cpl_array* parray, int szArray,
                                  const double kappa,cpl_image* imMask,
                                  const double* exptimes, int x, int y,
                                  double mask_delta)
{
    double result = 0;
    int nInvalidPoints = 0;
    const double EPS = 1E-10;
    //sinfo_msg("sinfo_kappa_sigma_array_with_mask, x[%d] y[%d]"
    double mask_adjustment = 0;
    mask_adjustment = mask_delta;
    do
    {
        double median = 0;
        double sig = 0;
        int z = 0;
        nInvalidPoints = 0;

        check_nomsg(median = cpl_array_get_median(parray));
        check_nomsg(sig = cpl_array_get_stdev(parray));
        for (z = 0; z < szArray; z++)
        {
            int isnull = 0;
            double value = 0;
            check_nomsg(value = cpl_array_get(parray, z, &isnull));
            if(!isnull)
            {
                if (fabs(value - median) > (kappa * sig))
                {

                    //                       sinfo_msg("entered");
                    //		  sinfo_msg("val=%g check=%g",
                    //			    fabs(value - median),(kappa * sig));
                    //			  sinfo_msg("kappa=%f sig=%g median=%g value=%g",
                    //				    kappa,sig,median,value);

                    //double msk_new_value = 0;
                    cpl_array_fill_window_invalid(parray, z, 1);
                    mask_adjustment += exptimes[z];
                    ++nInvalidPoints;
                }
            }
        }
        /*if (nInvalidPoints)
		{
			sinfo_msg("nInvalidPoints %d[%d][%d] median[%f] sig[%f]", nInvalidPoints,x,y, median, sig );
		}*/

    }
    while (nInvalidPoints);
    if(imMask && fabs(mask_adjustment) > EPS)
    {
        // adjust mask image
        int px_rejected = 0;
        double msk_value = 0;
        check_nomsg(msk_value = cpl_image_get(imMask, x, y, &px_rejected));
        check_nomsg(cpl_image_set(imMask, x,y, msk_value - mask_adjustment));
    }
    // get a result value for the point
    check_nomsg(result = cpl_array_get_mean(parray));
    return result;
    cleanup:
    sinfo_msg("Error in sinfo_kappa_sigma_array_with_mask");
    return 0;
}


int sinfo_coadd_with_ks_clip_optimized(
                const int z_min,
                const int z_max,
                const int n_cubes,
                const double kappa,
                int* llx,
                int* lly,
                double* exptimes,
                cpl_imagelist* sky_mask,
                cpl_imagelist* mergedCube,
                cpl_imagelist** tmpcubes
)
{
    /*
	sinfo_msg("sinfo_coadd_with_ks_clip_optimized() z_min[%d] z_max[%d] n_cubes[%d] kappa[%f] llx[%d] lly[%d] exptimes[%d] sky_mask[%d]",
			z_min, z_max, ilx, ily, n_cubes, kappa,llx, lly,exptimes,sky_mask);
     */
    int result=0;
    check_nomsg(result=sinfo_kappa_sigma_offset_with_mask(z_min, z_max, n_cubes, tmpcubes, exptimes, mergedCube, llx, lly, sky_mask, kappa));

    cleanup:

    return result;

}

static int sinfo_kappa_sigma_offset_with_mask(
                int z_min,
                int z_max,
                int nCubes,
                cpl_imagelist** inputCubes,
                const double* exptimes,
                cpl_imagelist* imResult,
                int* global_offsetX,
                int* global_offsetY,
                cpl_imagelist* sky_mask,
                const double kappa
)
{
    const int BIG_ENOUGH_INT = 65535;
    CubeDataVector*** indexX = 0;
    int x = 0;
    int y = 0;
    int z = 0;
    int iPlanesNumber = z_max - z_min;
    int nIndexXbytes = 0;
    int globalSizeX = 0 ;
    int globalSizeY = 0;

    int xmax = -BIG_ENOUGH_INT;
    int ymax = -BIG_ENOUGH_INT;
    int xmin = BIG_ENOUGH_INT;
    int ymin = BIG_ENOUGH_INT;
    int* offsetX = 0; // local offset of the cubes, normalized
    int* offsetY = 0;
    //sinfo_msg(" starting kappa-sigma clipping for cubes[%d] planes[%d]", nCubes, z_max - z_min );
    // determine size of the coadded cube
    sinfo_check_rec_status(0);
    for (z = 0; z < nCubes; z++)
    {

        cpl_imagelist* pCube = inputCubes[z];
        cpl_image* pImage = 0;
        int localMaxX = 0;
        int localMaxY = 0;
        int localMinX = 0;
        int localMinY = 0;

        pImage = cpl_imagelist_get(pCube, 0);

        localMaxX = cpl_image_get_size_x(pImage) + global_offsetX[z];
        localMaxY = cpl_image_get_size_y(pImage) + global_offsetY[z];
        localMinX = global_offsetX[z];
        localMinY = global_offsetY[z];

        if(localMaxX > xmax) xmax = localMaxX;
        if(localMaxY > ymax) ymax = localMaxY;

        if(localMinX < xmin) xmin = localMinX;
        if(localMinY < ymin) ymin = localMinY;
    }
    sinfo_check_rec_status(1);

    // DFS09121 xmax and ymax could be more then output cube - check and adjust
    {
        int msize_x = 0;
        int msize_y = 0;
        //sinfo_msg("DFS09121 before:  xmax=%d ymax=%d", xmax, ymax);
        cpl_image * pmaskimage = cpl_imagelist_get(sky_mask, 0);
        msize_x = cpl_image_get_size_x(pmaskimage);
        msize_y = cpl_image_get_size_y(pmaskimage);
        xmax = msize_x < xmax ? msize_x : xmax;
        ymax = msize_y < ymax ? msize_y : ymax;
        //sinfo_msg("DFS09121 after:  xmax=%d ymax=%d", xmax, ymax);
    }
    // rely on the data received outside
    globalSizeX = xmax;// - xmin;
    globalSizeY = ymax;// - ymin;
    // calculate local offset
    check_nomsg(offsetX = cpl_malloc(sizeof(offsetX[0]) * nCubes));
    check_nomsg(offsetY = cpl_malloc(sizeof(offsetY[0]) * nCubes));
    sinfo_check_rec_status(2);
    for (z = 0; z < nCubes; z++) // use the offset from the caller
    {
        offsetX[z] = global_offsetX[z];// - xmin;
        offsetY[z] = global_offsetY[z];// - ymin;
        //		sinfo_msg("for cube [%d] offset X[%d : %d] Y[%d : %d]", z, offsetX[z], global_offsetX[z], offsetY[z], global_offsetY[z]);
    }
    sinfo_check_rec_status(3);
    // Because of DFS09121, the allocated size is taken +1
    nIndexXbytes = sizeof(CubeDataVector**) * (globalSizeX+1 );
    //	sinfo_msg("   kappa_sigma_offset, globalSizeX[%d] globalSizeY[%d] nIndexXbytes[%d]", globalSizeX, globalSizeY, nIndexXbytes);
    indexX = cpl_malloc(nIndexXbytes);
    memset(&indexX[0], 0, (globalSizeX+1 )* sizeof(indexX[0]));
    // prepare result planes and mask

    // 1. Fill indexes - do it only for a 0 plane in the cube
    for (z = 0; z < nCubes; z++)
    {
        int iCubeSizeX = 0;
        int iCubeSizeY = 0;
        int iOffsetX = 0;
        int iOffsetY = 0;

        cpl_imagelist* pCube = inputCubes[z];
        cpl_image* pImage = 0;
        pImage = cpl_imagelist_get(pCube, 0);

        iCubeSizeX = cpl_image_get_size_x(pImage);
        iCubeSizeY = cpl_image_get_size_y(pImage);
        iOffsetX = offsetX[z];
        iOffsetY = offsetY[z];
        //		sinfo_msg("   processing cube [%d] offsetX[%d] offsetY[%d] iCubeSizeX[%d] iCubeSizeY[%d]", z, iOffsetX, iOffsetY, iCubeSizeX, iCubeSizeY);
        for (x = 1; x <= iCubeSizeX; x++)
        {
            int iGlobalX = x + iOffsetX;

            CubeDataVector** ppVector = 0;
            if (indexX[iGlobalX - 1] == 0)
            {
                // Because of DFS09121, the allocated size is taken +1
                int nBytes = sizeof(CubeDataVector*) * (globalSizeY+1 );
                ppVector= cpl_malloc(nBytes);
                memset(&ppVector[0],0,(globalSizeY+1) * sizeof(ppVector[0]));
                indexX[iGlobalX - 1] = ppVector;
            }
            else
            {
                ppVector = indexX[iGlobalX - 1];
            }
            for (y = 1; y <=iCubeSizeY; y++)
            {
                CubeData* pCubeData = 0;
                int iGlobalY = y + iOffsetY;
                CubeDataVector* pVector = ppVector[iGlobalY - 1];
                if(pVector == 0)
                {
                    int nbytes = sizeof(CubeDataVector);
                    check_nomsg(pVector = cpl_malloc(nbytes));
                    ppVector[iGlobalY - 1] = pVector;
                    pVector->size = 0;
                    nbytes = sizeof(CubeData*) * nCubes;
                    pVector->pdata = cpl_malloc(nbytes);
                    //					memset(&pVector->pdata[0], 0, nCubes * sizeof(pVector->pdata[0]));
                }
                pCubeData = cpl_malloc(sizeof(CubeData));
                pVector->pdata[(pVector->size)++] = pCubeData;
                pCubeData->iCubeNumber = z;
                pCubeData->iLocalX = x;
                pCubeData->iLocalY = y;
            }
        }
    }
    sinfo_check_rec_status(4);

    // 2. for each index value in global coordinates (x,y) call kappa-sigma
    for (x = 1; x <= globalSizeX; x++)
    {
        CubeDataVector** pDataX = indexX[x - 1];
        if (pDataX)
        {
            for (y = 1; y <= globalSizeY; y++)
            {
                CubeDataVector* pDataY = pDataX[y - 1];
                if (pDataY && pDataY->size)
                {
                    sinfo_kappa_sigma_CubeDataVector(x, y, pDataY, imResult,
                                    inputCubes, sky_mask, iPlanesNumber, z_min,
                                    kappa, exptimes);
                }
                if (pDataY)
                {
                    check_nomsg(cpl_free(pDataY->pdata));
                    check_nomsg(cpl_free(pDataY));
                }
            }
            check_nomsg(cpl_free(pDataX));
        }
    }
    sinfo_check_rec_status(5);
    cleanup:
    cpl_free(indexX);
    cpl_free(offsetX);
    cpl_free(offsetY);
    return 0;

}

static void
sinfo_kappa_sigma_CubeDataVector(
                int globalX,
                int globalY,
                CubeDataVector* pCubeDataVector,
                cpl_imagelist* imlistResult,
                cpl_imagelist** input_cubes,
                cpl_imagelist* sky_mask,
                int iPlanesNumber,
                int z_min,
                const double kappa,
                const double* exptimes
)
{
    int plane = 0;
    int z = 0;

    // iterate through all planes
    cpl_array* pArray = 0;
    check_nomsg(pArray = cpl_array_new(pCubeDataVector->size, CPL_TYPE_DOUBLE));


    for (plane = z_min; plane < z_min + iPlanesNumber; plane++)
    {
        //double val_msk = 0; // value of the mask in the point
        //int px = 0;
        cpl_image* imResult = NULL;
        cpl_image* imMask = NULL;
        double mask_adjustment = 0;
        int nValidPoints = 0;
        cpl_array_fill_window_invalid(pArray, 0, pCubeDataVector->size);
        check_nomsg(imMask = cpl_imagelist_get(sky_mask, plane - z_min));
        //check_nomsg(val_msk = cpl_image_get(imMask, globalX, globalY, &px));
        for (z = 0; z < pCubeDataVector->size; z++) // through all cubes for that point - prepare the array
        {

            cpl_imagelist* pCube = 0;
            CubeData* pCubeData = pCubeDataVector->pdata[z];
            pCube = input_cubes[pCubeData->iCubeNumber];
            if (pCube)
            {
                cpl_image* pImage = cpl_imagelist_get(pCube, plane - z_min);

                if (pImage)
                {
                    int is_rejected = 0;
                    double value = 0;
                    check_nomsg(value = cpl_image_get(pImage, pCubeData->iLocalX,
                                    pCubeData->iLocalY, &is_rejected));
                    if (!isnan(value))
                    {
                        check_nomsg(cpl_array_set(pArray, z, value));
                        ++nValidPoints;
                    }
                    else
                    {
                        mask_adjustment += exptimes[z];
                    }
                }
                else
                {
                    sinfo_msg("pImage is null");
                }
            }
        }
        if(nValidPoints)
        {
            sinfo_kappa_sigma_array_with_mask(pArray, pCubeDataVector->size,
                            kappa, imMask, exptimes, globalX, globalY,
                            mask_adjustment);
            check_nomsg(imResult = cpl_imagelist_get(imlistResult, plane));
            if (imResult)
            {
                check_nomsg(cpl_image_set(imResult, globalX, globalY,
                                cpl_array_get_mean(pArray)));
            }
            else
            {
                sinfo_msg("imResult is null");
            }
        } else
        {
            // adjust the mask
            check_nomsg(cpl_image_set(imMask, globalX,globalY, 0));
        }
    }
    for (z = 0; z < pCubeDataVector->size; z++) // through all cubes  - delete the data
    {
        CubeData* pCubeData = pCubeDataVector->pdata[z];
        cpl_free(pCubeData);
    }
    cpl_array_delete(pArray);
    return;
    cleanup:
    //	sinfo_msg("   -----cleanup");
    return;
}

/* TODO: not used */
static int
sinfo_coadd_with_ks_clip(const int z_min,
                         const int z_max,
                         const int ilx,
                         const int ily,
                         const int n_cubes,
                         const double kappa,
                         int* llx,
                         int* lly,
                         double* exptimes,
                         cpl_imagelist* mask,
                         cpl_imagelist* mergedCube,
                         cpl_imagelist** tmpcubes)

{

    int m=0;
    int x=0;
    int y=0;
    int z=0;

    //int mly=0;
    int nc=0;
    int olx=0;
    int oly=0;
    int posx=0;
    int posy=0;
    int i=0;
    int nclip=0;
    int ks=0;

    float sig=0;
    float med=0;
    float ovr=0;
    float  msk_sum=0;
    float  val_msk_sum=0;
    //float avg=0;

    float* pmdata=NULL;
    float* ptdata=NULL;
    float* pvdata=NULL;
    cpl_image* t_img=NULL;
    cpl_image* v_img=NULL;


    cpl_vector* val=NULL;
    cpl_vector* msk=NULL;


    cpl_image* o_img=cpl_imagelist_get(mergedCube,0);
    olx=cpl_image_get_size_x(o_img);
    oly=cpl_image_get_size_y(o_img);

    m=0;
    for ( z = z_min; z < z_max ; z++ ) {
        cpl_image* m_img=cpl_imagelist_get(mask,z);
        pmdata=cpl_image_get_data_float(m_img);
        o_img=cpl_imagelist_get(mergedCube,z);
        float* podata=cpl_image_get_data_float(o_img);
        int mlx=cpl_image_get_size_x(m_img);
        //mly=cpl_image_get_size_y(m_img);
        // go through the first image plane of the big data cube
        for ( y = 0 ; y < oly ; y++ ) {
            for ( x = 0 ; x < olx ; x++ ) {
                //avg=0;
                nc=0;
                // computes nc
                for ( i = 0 ; i < n_cubes ; i++ ) {
                    t_img=cpl_imagelist_get(tmpcubes[i],m);
                    ptdata=cpl_image_get_data_float(t_img);
                    if ( y >= lly[i] && y < lly[i]+ily &&
                                    x >= llx[i] && x < llx[i]+ilx ) {
                        posx = x - llx[i] ;
                        posy = y - lly[i] ;
                        if (!isnan(ptdata[posx+posy*ilx]) &&
                                        ptdata[posx+posy*ilx] != 0.) {
                            nc++;
                        }
                    }
                }
                if( nc > 0 ) {


                    msk=cpl_vector_new(n_cubes);
                    for (i=0;i<n_cubes;i++) {
                        cpl_vector_set(msk,i,1);
                    }

                    // k-s clipping
                    nclip=0;


                    for (ks=0;ks<nc;ks++) {
                        sig=0;
                        med=0;
                        ovr=0;
                        if(nc-nclip >0) {
                            val=cpl_vector_new(nc-nclip);
                        }
                        // fill val
                        for ( i = 0 ; i < n_cubes ; i++ ) {
                            t_img=cpl_imagelist_get(tmpcubes[i],m);
                            ptdata=cpl_image_get_data_float(t_img);
                            if ( y >= lly[i] && y < lly[i]+ily &&
                                            x >= llx[i] && x < llx[i]+ilx ) {
                                posx = x - llx[i] ;
                                posy = y - lly[i] ;
                                if (!isnan(ptdata[posx+posy*ilx]) &&
                                                ptdata[posx+posy*ilx] != 0. &&
                                                (cpl_vector_get(msk,i) != 0)) {
                                    cpl_vector_set(val,ovr,(double)ptdata[posx+posy*ilx]);
                                    ovr++;
                                }
                            }
                        }

                        // get avg, med, sig
                        if(ovr>0) {
                            //avg=cpl_vector_get_mean(val);
                            med=cpl_vector_get_median_const(val);
                            if(ovr>1) {
                                sig=cpl_vector_get_stdev(val);
                            } else {
                                sig=0;
                            }
                            cpl_vector_delete(val);
                        }

                        for ( i = 0 ; i < n_cubes ; i++ ) {
                            t_img=cpl_imagelist_get(tmpcubes[i],m);
                            ptdata=cpl_image_get_data_float(t_img);
                            // Do k-s clipping at each pixel
                            if ( y >= lly[i] && y < lly[i]+ily &&
                                            x >= llx[i] && x < llx[i]+ilx ) {
                                posx = x - llx[i] ;
                                posy = y - lly[i] ;
                                if (!isnan(ptdata[posx+posy*ilx]) &&
                                                ptdata[posx+posy*ilx] != 0. &&
                                                (cpl_vector_get(msk,i) != 0)) {
                                    if(abs((ptdata[posx+posy*ilx]-med))> kappa*sig) {
                                        ptdata[posx+posy*ilx]=0;
                                        pmdata[x+y*mlx] -= exptimes[i]  ;
                                        cpl_vector_set(msk,i,0);
                                        nclip++;
                                    }
                                }
                            }
                        }
                    } // end of k-s clipping

                    msk_sum=0;
                    val_msk_sum=0;
                    for ( i = 0 ; i < n_cubes ; i++ ) {
                        v_img=cpl_imagelist_get(tmpcubes[i],m);
                        pvdata=cpl_image_get_data_float(v_img);
                        // computes sky at each point
                        if ( y >= lly[i] && y < lly[i]+ily &&
                                        x >= llx[i] && x < llx[i]+ilx ) {
                            posx = x - llx[i] ;
                            posy = y - lly[i] ;
                            //sinfo_msg_warning("llx[%d]=%d lly[%d],=%d",i,llx[i],i,lly[i]);
                            //sinfo_msg_warning("posx=%d posy=%d",posx,posy);
                            if (!isnan(pvdata[posx+posy*ilx]) &&
                                            pvdata[posx+posy*ilx] != 0. &&
                                            (cpl_vector_get(msk,i) != 0)) {
                                msk_sum+=pmdata[x+y*mlx];
                                val_msk_sum+=pvdata[posx+posy*ilx]*
                                                pmdata[x+y*mlx];
                            }
                        }
                    }
                    podata[x+y*olx]=val_msk_sum/msk_sum;
                    cpl_vector_delete(msk);
                    /*
 	  sinfo_ks_clip(n_cubes,nc,ilx,ily,kappa,llx,lly,exptimes,
			tmpcubes,podata,pmdata,x,y,m,mlx,oly);

                     */

                } // end check if overlap nc >0
            } // end loop over x
        } // end loop over y
        m++;
    } // end loop over z

    return 0;


}




static int
sinfo_compute_contributes_at_pos(cpl_imagelist** tmpcubes, 
                                 int* llx, int* lly, 
                                 const int x, const int y,
                                 const int ilx, const int ily, 
                                 const int m,const int n_cubes)
{

    int result=0;
    int i=0;
    int post=0;
    int posx=0;
    int posy=0;

    /* computes nc the number of intensity contributes from
      each overlapping cube point intensity at x,y
     */
    for ( i = 0 ; i < n_cubes ; i++ ) {
        cpl_image* t_img=cpl_imagelist_get(tmpcubes[i],m);
        float* ptdata=cpl_image_get_data_float(t_img);
        int lox=llx[i];
        int loy=lly[i];
        int upx=llx[i]+ilx;
        int upy=lly[i]+ily;

        if ( y >= loy && y < upy && x >= lox && x < upx ) {
            posx = x - lox;
            posy = y - loy;
            post = posx+posy*ilx;

            if (!isnan(ptdata[post]) && ptdata[post] != 0.) {
                result++;
            }
        }
    }


    return result;

}




static int
sinfo_cubes_coadd_with_ks_clip(cpl_imagelist** tmpcubes, 
                               const int n_cubes,const int nc,
                               const int x, const int y, const int m,
                               int* llx, int* lly, 
                               const int ilx, const int ily,
                               const double kappa, 
                               double* exptimes, float** pmdata, 
                               cpl_vector** msk, const int mlx)


{


    cpl_vector* val=NULL;
    cpl_image* t_img=NULL;

    int i=0;
    int nclip=0;
    int ks=0;

    int lox=0;
    int loy=0;
    int upx=0;
    int upy=0;

    int posx=0;
    int posy=0;
    int post=0;

    int ovr=0;

    float sig=0;
    //float avg=0;
    float med=0;

    float* ptdata=NULL;


    // k-s clipping
    nclip=0;


    for (ks=0;ks<nc;ks++) {
        sig=0;
        med=0;
        ovr=0;
        if(nc-nclip >0) {
            check_nomsg(val=cpl_vector_new(nc-nclip));
        }
        // fill val
        for ( i = 0 ; i < n_cubes ; i++ ) {
            check_nomsg(t_img=cpl_imagelist_get(tmpcubes[i],m));
            check_nomsg(ptdata=cpl_image_get_data_float(t_img));

            lox=llx[i];
            loy=lly[i];
            upx=llx[i]+ilx;
            upy=lly[i]+ily;

            if ( y >= loy && y < upy && x >= lox && x < upx ) {
                posx = x - lox ;
                posy = y - loy ;
                post=posx+posy*ilx;

                if (!isnan(ptdata[post]) && ptdata[post] != 0. &&
                                (cpl_vector_get(*msk,i) != 0)) {
                    cpl_vector_set(val,ovr,(double)ptdata[post]);
                    ovr++;
                }
            }
        }

        // get avg, med, sig
        if(ovr>0) {
            //check_nomsg(avg=cpl_vector_get_mean(val));
            med=cpl_vector_get_median_const(val);
            if(ovr>1) {
                sig=cpl_vector_get_stdev(val);
            } else {
                sig=0;
            }
            cpl_vector_delete(val);
        }

        for ( i = 0 ; i < n_cubes ; i++ ) {
            t_img=cpl_imagelist_get(tmpcubes[i],m);
            ptdata=cpl_image_get_data_float(t_img);

            lox=llx[i];
            loy=lly[i];
            upx=llx[i]+ilx;
            upy=lly[i]+ily;

            // Do k-s clipping at each pixel
            if ( y >= loy && y < upy && x >= lox && x < upx ) {
                posx = x - lox ;
                posy = y - loy ;
                post = posx+posy*ilx;
                if (!isnan(ptdata[post]) && ptdata[post] != 0. &&
                                (cpl_vector_get(*msk,i) != 0)) {
                    if( abs( (ptdata[post]-med) ) > kappa*sig ) {
                        ptdata[post]=0;
                        (*pmdata)[x+y*mlx] -= exptimes[i]  ;
                        check_nomsg(cpl_vector_set(*msk,i,0));
                        nclip++;
                    }
                }
            }
        }
    } // end of k-s clipping

    cleanup:
    return 0;
}

/**
   @name sinfo_coadd_with_ks_clip coadd cubes with kappa-sigma clip of
         overlapping pixels intensity outliers
   @param z_min: min cube's plane to be processed
   @param z_max: max cube's plane to be processed
   @param ilx: input image X size
   @param ily: input image Y size
   @param n_cubes: number of cubes to be coadded
   @param kappa value for kappa-sigma clip rejection
   @param llx: array holding lower left X for each input cube
   @param lly: array holding lower left Y for each input cube
   @param exptimes: array holding exposure times for each input cube
   @param mask: output cube mask   
   @param mergedCube: output coadded cube 
   @param tmccubes: input cubes to be coadded

   @return interpolated cube, and corrected bad pixel mask cube
   @doc interpolates bad pixel of an object cube if a bad pixel
        mask cube is available by using the nearest neighbors
        in 3 dimensions.
 */


static int
sinfo_coadd_with_ks_clip2(const int z_min,
                          const int z_max,
                          const int ilx,
                          const int ily,
                          const int n_cubes,
                          const double kappa,
                          int* llx,
                          int* lly,
                          double* exptimes,
                          cpl_imagelist* mask,
                          cpl_imagelist* mergedCube,
                          cpl_imagelist** tmpcubes)

{

    int m=0;
    int x=0;
    int y=0;
    int z=0;

    //int mly=0;
    int nc=0;
    int olx=0;
    int oly=0;
    int posx=0;
    int posy=0;
    int i=0;

    float  msk_sum=0;
    float  val_msk_sum=0;
    //float avg=0;

    float* pmdata=NULL;
    float* pvdata=NULL;

    cpl_image* o_img=NULL;
    cpl_image* v_img=NULL;


    cpl_vector* msk=NULL;


    o_img=cpl_imagelist_get(mergedCube,0);
    olx=cpl_image_get_size_x(o_img);
    oly=cpl_image_get_size_y(o_img);

    m=0;
    for ( z = z_min; z < z_max ; z++ ) {
        cpl_image* m_img=cpl_imagelist_get(mask,z);
        pmdata=cpl_image_get_data_float(m_img);
        o_img=cpl_imagelist_get(mergedCube,z);
        float* podata=cpl_image_get_data_float(o_img);
        int mlx=cpl_image_get_size_x(m_img);
        //mly=cpl_image_get_size_y(m_img);
        // go through the first image plane of the big data cube
        for ( y = 0 ; y < oly ; y++ ) {
            for ( x = 0 ; x < olx ; x++ ) {
                //avg=0;
                //nc=0;
                // computes nc

                nc=sinfo_compute_contributes_at_pos(tmpcubes,llx,lly,x,y,
                                ilx,ily,m,n_cubes);

                if( nc > 0 ) {


                    msk=cpl_vector_new(n_cubes);
                    for (i=0;i<n_cubes;i++) {
                        cpl_vector_set(msk,i,1);
                    }


                    sinfo_cubes_coadd_with_ks_clip(tmpcubes, n_cubes,nc,x,y,m,
                                                   llx,lly,ilx,ily,kappa,
                                                   exptimes,&pmdata, &msk,mlx);

                    msk_sum=0;
                    val_msk_sum=0;
                    for ( i = 0 ; i < n_cubes ; i++ ) {
                        v_img=cpl_imagelist_get(tmpcubes[i],m);
                        pvdata=cpl_image_get_data_float(v_img);
                        // computes sky at each point
                        if ( y >= lly[i] && y < lly[i]+ily &&
                                        x >= llx[i] && x < llx[i]+ilx ) {
                            posx = x - llx[i] ;
                            posy = y - lly[i] ;
                            //sinfo_msg_warning("llx[%d]=%d lly[%d],=%d",i,llx[i],i,lly[i]);
                            //sinfo_msg_warning("posx=%d posy=%d",posx,posy);
                            if (!isnan(pvdata[posx+posy*ilx]) &&
                                            pvdata[posx+posy*ilx] != 0. &&
                                            (cpl_vector_get(msk,i) != 0)) {
                                msk_sum+=pmdata[x+y*mlx];
                                val_msk_sum+=pvdata[posx+posy*ilx]*
                                                pmdata[x+y*mlx];
                            }
                        }
                    }
                    podata[x+y*olx]=val_msk_sum/msk_sum;
                    cpl_vector_delete(msk);
                    /*
 	                sinfo_ks_clip(n_cubes,nc,ilx,ily,kappa,llx,lly,exptimes,
			                      tmpcubes,podata,pmdata,x,y,m,mlx,oly);

                     */

                } // end check if overlap nc >0
            } // end loop over x
        } // end loop over y
        m++;
    } // end loop over z

    return 0;


}

cpl_error_code
sinfo_imagelist_reject_value(cpl_imagelist* iml,cpl_value value)
{
    int sz=cpl_imagelist_get_size(iml);
    int i;
    for(i=0;i<sz;i++) {
      cpl_image* ima=cpl_imagelist_get(iml,i);
      cpl_image_reject_value(ima, CPL_VALUE_NAN);
    }
    return cpl_error_get_code();
}


/**@}*/
