/*
 * 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  22/01/02  created
 */


#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include "sinfo_vltPort.h"
#include <math.h>

/* 
 * System Headers
 */
/* 
 * Local Headers
 */

#include "sinfo_function_1d.h"
#include "sinfo_recipes.h"
#include "sinfo_wavecal.h"
#include "sinfo_wave_calibration.h"
#include "sinfo_solve_poly_root.h"
#include "sinfo_utils_wrappers.h"
#include "sinfo_error.h"

#include "sinfo_svd.h"
/*
 * Private functions prototype
 */
static Bcoeffs * 
sinfo_new_b_coeffs( int n_slitlets,
                    int n_acoeffs,
                    int n_bcoeffs ) ;

static void 
sinfo_new_destroy_b_coeffs ( Bcoeffs * bco ) ;

static int   
sinfo_new_coeffs_cross_slit_fit ( int      n_columns,
                                  float ** acoefs,
                                  float ** dacoefs,
                                  Bcoeffs* bco,
                                  float    sigma_factor,
                                  float    dispersion,
                                  float    pixel_dist,
                                  float  * chisq ) ;


static int   
sinfo_new_spred_coeffs_cross_slit_fit ( int      n_columns,
                                        float ** acoefs,
                                        float ** dacoefs,
                                        Bcoeffs* bco,
                                        float    sigma_factor,
                                        float    dispersion,
                                        float    pixel_dist,
                                        float  * chisq,
                                        float ** sinfo_slit_pos) ;
/*
 * function definitions
 */
/**@{*/
/**
 * @addtogroup sinfo_rec_wavecal wavelength calibration functions
 *
 *  routines needed for wavelength calibration with smoothing only
 *     within the slitlets
 */
/**
   @brief allocates memory for a new array of Bcoeffs data structures
   @name   sinfo_new_b_coeffs()
   @param  n_slitlets: number of slitlets
   @param  n_acoeffs: number of acoeffs
   @param  n_bcoeffs: number of bcoeffs
   @return allocated array of Bcoeffs data structures


 */

static Bcoeffs * 
sinfo_new_b_coeffs( int n_slitlets,
                    int n_acoeffs,
                    int n_bcoeffs )
{
    int i, n ;
    Bcoeffs * returnbco ;

    if(NULL == (returnbco=(Bcoeffs*) cpl_calloc(n_slitlets, sizeof(Bcoeffs))) )
    {
        sinfo_msg_error ("could not allocate memory") ;
        return NULL ;
    }
    returnbco -> n_slitlets = n_slitlets ;
    returnbco -> n_acoeffs  = n_acoeffs ;
    returnbco -> n_bcoeffs  = n_bcoeffs ;
    for ( i = 0 ; i < n_slitlets ; i++ )
    {
        returnbco[i].slitlet = i ;
        if ( NULL == (returnbco[i].b = (float**)cpl_calloc(n_acoeffs, 
                        sizeof(float*)) ) )
        {
            sinfo_msg_error ("could not allocate memory") ;
            return NULL ;
        }
        for ( n = 0 ; n < n_acoeffs ; n++ )
        {
            if ( NULL == (returnbco[i].b[n] = (float*)cpl_calloc(n_bcoeffs, 
                            sizeof(float))) )
            {
                sinfo_msg_error ("could not allocate memory") ;
                return NULL ;
            }
        }  
    }
    return returnbco ;
}

/**
@brief frees memory of an array of Bcoeffs data structures
@name sinfo_new_destroy_b_coeffs()
@param bcoeffs to destroy
@return nothing
 */

static void 
sinfo_new_destroy_b_coeffs ( Bcoeffs * bco )
{
    int i, n ;

    for ( i = 0 ; i < bco->n_slitlets ; i++ )
    {
        for ( n = 0 ; n < bco->n_acoeffs ; n++ )
        {
            cpl_free (bco[i].b[n]) ;
        }
        cpl_free(bco[i].b) ;
    }

    cpl_free (bco) ;  
}

/**
@brief Fits each single polynomial coefficient acoefs resulting from 
       polyfit across the columns of each slitlet and use the result 
       of this fit to smooth the acoefs.
@name  sinfo_new_coeffs_cross_slit_fit()
@param n_columns:    number of image columns
@param acoefs:       coeffs fitted in sinfo_polyfit
@note: this is a sinfo_matrix of the polynomial
       coefficients, each index for each column:
       acoefs[index][column]
@param dacoefs:      fit errors of the corresponding acoefs
@param bco:          the fitted Bcoeffs data structure for each slitlet
@param sigma_factor: factor of sigma beyond which the
       column coefficients are discarded for the fit
@param dispersion:   estimate of the dispersion
@param pixel_dist:   estimate of minimal pixel distance in spectral 
                     direction between slitlets
@return 
           # 0 if all went o.k
           # -1 if an error occurred
           # the found fit coefficients bco data structure to calculate
           # the smoothed acoefs
           # chisq:        list of the chi squared of each fit for each slitlet

 */

static int   
sinfo_new_coeffs_cross_slit_fit ( int      n_columns,
                                  float ** acoefs,
                                  float ** dacoefs,
                                  Bcoeffs* bco,
                                  float    sigma_factor,
                                  float    dispersion,
                                  float    pixel_dist,
                                  float  * chisq )
{
    float col_index;
    float ** ucoefs, **vcoefs, **covar ;
    float * acoefsclean ;
    double sum, sumq, mean ;
    double sigma ;
    double cliphi, cliplo ;
    float offset ;
    float threshold ;
    float* sub_col_index=NULL ;
    float* sub_acoefs=NULL;
    float* sub_dacoefs=NULL ;
    float* wcoefs=NULL ;
    int* edge=NULL ;

    int ed1, ed2 ;
    int i, n, num, ndata ;
    int nc, ns ;
    int loc_index ;
    int last_i=PIXEL;

    if ( n_columns < 1 )
    {
        sinfo_msg_error("wrong number of image columns given") ;
        return -1 ;
    }
    if ( acoefs == NULL || dacoefs == NULL )
    {
        sinfo_msg_error("acoeffs or errors of coefficients are not given") ;
        return -1 ;
    }
    if ( bco == NULL )
    {
        sinfo_msg_error("bcoeffs are not allocated") ;
        return -1 ;
    }
    if ( sigma_factor <= 0. )
    {
        sinfo_msg_error("impossible sigma_factor given!") ;
        return -1 ;
    }
    if ( dispersion == 0. )
    {
        sinfo_msg_error("impossible dispersion given!") ;
        return -1 ;
    }

    /*-------------------------------------------------------------------------
     * search for the slitlet edges by comparing the a0 coefficients along 
       the columns if a bigger deviation occurrs it is assumed that there 
       is an edge.
     */

    edge=cpl_calloc(bco->n_slitlets,sizeof(int)) ;
    wcoefs=cpl_calloc(bco->n_bcoeffs,sizeof(float)) ;

    n = 0 ;
    threshold = pixel_dist * fabs(dispersion) ;
    for ( i = PIXEL ; i < n_columns - PIXEL ; )
    {
        if ( !isnan(acoefs[0][i+1]) && 
                        acoefs[0][i+1] != 0. &&
                        acoefs[0][i] != 0.  &&
                        dacoefs[0][i+1] != 0.)
        {
            if ( isnan(acoefs[0][i]) || acoefs[0][i] == 0. )
            {
                if (fabs(acoefs[0][i+1] - acoefs[0][i-1]) >= threshold )
                {
                    if( (i-last_i) < 60 && (i > 80) ) {
                        sinfo_msg_warning("skip1 i=%d diff=%d\n",i,i-last_i);
                        goto skip;
                    } else {
                        /*
                 sinfo_msg("diff1=%f i=%d threshold-%f size=%d\n", 
                               fabs(acoefs[0][i+1] - acoefs[0][i-1]),i,
                               threshold,i-last_i);
                         */
                        edge[n] = i+1 ;
                        //sinfo_msg("1found edge: %d",edge[n]);
                        n++ ;
                        last_i = i;
                        i += PIXEL ;
                    }
                }
            }
            else
            {
                if ((fabs(acoefs[0][i+1] - acoefs[0][i]) >= threshold) || 
                                (i-last_i) > 63 )
                {
                    if( (i-last_i) < 60 && ((i> 80) || (i<PIXEL+2))) {
                        sinfo_msg_warning("skip2 i=%d diff=%d\n",i,i-last_i);
                        goto skip;
                    } else {

                        /*
                      sinfo_msg_warning("diff2=%f i=%d threshold-%f size=%d",
                      fabs(acoefs[0][i+1] - acoefs[0][i]),
                      i,threshold,i-last_i);                      
                         */



                        edge[n] = i+1 ;
                        //sinfo_msg("2found edge: %d",edge[n]);
                        n++ ;
                        last_i = i;
                        i += PIXEL ;
                    }
                }
            }
            /* sometimes a slitlet may be lost due to divergences in acoeffs[0]
               we try to recover it */
            if( ( (i-last_i) > 63 ) && 
                            ( isnan(fabs(acoefs[0][i+1] - acoefs[0][i])) ||
                                            isnan(fabs(acoefs[0][i+1] - acoefs[0][i-1])) ) )
            {
                edge[n] = i+1 ;
                //sinfo_msg("3found edge: %d",edge[n]);
                n++ ;
                last_i = i;
                sinfo_msg_warning("recovered slitlet edge i=%d",i);
                i += PIXEL ;

            }
        }
        skip:
        i++ ;
    }
    /*
    printf("X min %d max %d last %d\n", PIXEL, n_columns - PIXEL, i);
    printf("n=%d check=%d\n",n,bco->n_slitlets - 1);
     */
    if ( n != bco->n_slitlets - 1 )
    {
        sinfo_msg_error("could not find the right number "
                        "of slitlets, found: %d",n+1) ;
        return -1 ;
    }

    sub_col_index=cpl_calloc(n_columns,sizeof(float)) ;
    sub_acoefs=cpl_calloc(n_columns,sizeof(float));
    sub_dacoefs=cpl_calloc(n_columns,sizeof(float)) ;

    /* go through the coefficents indices */
    for ( loc_index = 0 ; loc_index < bco->n_acoeffs ; loc_index++ )
    {
        /* go through the single slitlets */
        for ( ns = 0 ; ns < bco->n_slitlets ; ns++ )
        {
            /* determine the slitlet edges */
            if ( ns == 0 )
            {
                ed1 = 0 ;
                ed2 = edge[0] ;
            }
            else if ( ns == bco->n_slitlets - 1 )
            {
                ed1 = edge[bco->n_slitlets - 2] ;
                ed2 = n_columns ;
            }
            else
            {
                ed1 = edge[ns-1] ;
                ed2 = edge[ns] ;
            }

            nc = 0 ;
            for ( i = ed1 ; i < ed2 ; i++ )
            {
                if ( isnan(acoefs[loc_index][i]) || 
                                acoefs[loc_index][i] == 0. ||
                                dacoefs[loc_index][i] == 0. )
                {
                    continue ;
                }
                else
                {
                    nc++ ;
                }
            }
            if (NULL==(acoefsclean = (float*) cpl_calloc(nc , sizeof(float))) )
            {
                sinfo_msg_error("could not allocate memory for acoefsclean!") ;
                return -1 ;
            }
            nc = 0 ;
            for ( i = ed1 ; i < ed2 ; i++ )
            {
                if ( isnan(acoefs[loc_index][i]) || 
                                acoefs[loc_index][i] == 0. ||
                                dacoefs[loc_index][i] == 0. )
                {
                    continue ;
                }
                else
                {
                    acoefsclean[nc] = acoefs[loc_index][i] ;
                    nc++ ;
                }
            }

            /* ----------------------------------------------------------
             * determine the clean mean and sigma value of the coefficients,
             * that means reject 10 % of the extreme low and high values
             */
            sinfo_pixel_qsort(acoefsclean, nc) ;

            sum   = 0. ;
            sumq  = 0. ;
            mean  = 0. ;
            sigma = 0. ;
            n     = 0 ;
            for ( i = (int)((float)nc*LOW_REJECT) ; 
                            i < (int)((float)nc*HIGH_REJECT) ; i++ )
            {
                sum  += (double)acoefsclean[i] ;
                sumq += ((double)acoefsclean[i] * (double)acoefsclean[i]) ;
                n ++ ;
            }
            mean          = sum/(double)n ;
            sigma         = sqrt( sumq/(double)n - (mean * mean) ) ;
            cliphi        = mean + sigma * (double)sigma_factor ;
            cliplo        = mean - sigma * (double)sigma_factor ;
            /* fit only the reasonnable values */
            num = 0 ;
            col_index = 0 ;
            /*
            printf("ed1=%d ed2=%d\n",ed1,ed2);
             */
            for ( i = ed1 ; i < ed2 ; i++ )
            {
                /* take only the reasonnable coefficients */
                /*
        printf("acoeffs=%f dacoefs=%f cliphi=%f cliplo=%f\n",
                   acoefs[loc_index][i],dacoefs[loc_index][i],cliphi,cliplo);
                 */
                if ( !isnan(acoefs[loc_index][i])     && 
                                (acoefs[loc_index][i] <= cliphi) &&
                                (acoefs[loc_index][i] >= cliplo) &&
                                (dacoefs[loc_index][i] != 0. )   &&
                                (acoefs[loc_index][i] != 0.)     )
                {
                    sub_acoefs[num]    = acoefs[loc_index][i] ;
                    sub_dacoefs[num]   = dacoefs[loc_index][i] ;
                    sub_col_index[num] = col_index ;
                    num ++ ;
                }
                col_index++ ;
            }
            ndata = num ;
            offset = (float)(col_index-1) / 2. ;
            /* printf("ndata=%d bco->n_bcoeffs=%d\n",ndata,bco->n_bcoeffs); */

            if ( ndata < bco->n_bcoeffs )
            {
                sinfo_msg_error("not enough data found in slitlet %d to "
                                "determine the fit coefficients.", ns) ;
                cpl_free(acoefsclean) ;
                return -1 ;
            }

            /* allocate coefficient matrices */
            ucoefs = sinfo_matrix(1, ndata, 1, bco->n_bcoeffs) ;
            vcoefs = sinfo_matrix(1, ndata, 1, bco->n_bcoeffs) ;
            covar  = sinfo_matrix(1, bco->n_bcoeffs, 1, bco->n_bcoeffs) ;

            /* scale the x-values for the fit */
            for ( i = 0 ; i < ndata ; i++ )
            {
                sub_col_index[i] = (sub_col_index[i] - offset) / offset ;
            }

            /* finally, do the singular value decomposition fit */
            sinfo_svd_fitting ( sub_col_index-1, sub_acoefs-1, 
                                sub_dacoefs-1, ndata, bco[ns].b[loc_index]-1,
                                bco->n_bcoeffs, ucoefs, vcoefs, 
                                wcoefs-1, covar, &chisq[ns], sinfo_fpol ) ;

            /* scale the found coefficients */
            for ( i = 0 ; i < bco->n_bcoeffs ; i ++ )
            {
                bco[ns].b[loc_index][i] /= pow( offset, i ) ;
            }

            /* free memory */
            cpl_free (acoefsclean) ;
            sinfo_free_matrix( ucoefs, 1/*, ndata*/, 1/*, bco->n_bcoeffs */) ;
            sinfo_free_matrix( vcoefs, 1/*, ndata*/, 1/*, bco->n_bcoeffs */) ;
            sinfo_free_matrix( covar, 1/*, bco->n_bcoeffs*/, 
                               1/*, bco->n_bcoeffs */) ;

            /* now calculate the smoothed acoefs for each column */
            col_index = 0 ;
            for ( i = ed1 ; i < ed2  ; i++ )
            {
                acoefs[loc_index][i] = 0. ;
                for ( n = 0 ; n < bco->n_bcoeffs ; n++ )
                {
                    acoefs[loc_index][i] += bco[ns].b[loc_index][n] * 
                                    pow(col_index - offset, n) ;
                }
                col_index++ ;
            }

        }
    }

    cpl_free(sub_col_index) ;
    cpl_free(sub_acoefs) ;
    cpl_free(sub_dacoefs) ;


    cpl_free(edge) ;
    cpl_free(wcoefs) ;

    return 0 ;
}


/**
@brief builds a new wavelength calibration map as fits image by using 
       the fit coeficients
@name sinfo_new_wave_map_slit()
@param acoefs: fit coefficient sinfo_matrix: output of 
               sinfo_coeffsCrossSlitFit()
@param n_acoefs: number of acoefs, polynomial order + 1
@param n_rows: number of final image rows
@param n_columns: number of final image columns
@return wavemap calibration map image

 */

cpl_image * sinfo_new_wave_map_slit ( float ** acoefs,
                                      int      n_acoefs,
                                      int      n_rows,
                                      int      n_columns )
{
    cpl_image * newIm=NULL ;
    float lambda=0 ;
    float offset=0 ;
    int col=0;
    int row=0 ;
    int i=0 ;
    float row_index=0 ;
    float* podata=NULL;
    if ( NULL == acoefs )
    {
        sinfo_msg_error (" no coefficient sinfo_matrix given!") ;
        return NULL ;
    }

    /* allocate new image */
    if ( NULL == (newIm = cpl_image_new(n_columns , n_rows,CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error ("could not allocate new image!") ;
        return NULL ;
    }
    podata=cpl_image_get_data_float(newIm);

    /* make the parabola symmetric to the image */
    offset = (float)(n_rows - 1) / 2. ; 

    /* go through the rows */
    for ( col = 0 ; col < n_columns ; col++ )
    {
        /* go through the columns */
        for ( row = 0 ; row < n_rows ; row++ )
        {
            lambda = 0. ;
            row_index = (float)row - offset ;
            for ( i = 0 ; i < n_acoefs ; i++ )
            {
                lambda += acoefs[i][col] * pow(row_index, i) ;
            }
            podata[col+row*n_columns] = lambda ;
        }
    }
    return newIm ;
}


/**
@brief takes an image from a calibration emission lamp and delivers the 
       smoothed fit coefficients of a polynomial fit along the columns of the 
       line positions as output.
@name  sinfo_new_wave_cal()
@param image:        merged image from a calibration emission lamp,
@param par:          fit parameters data structure storage
@param abuf:         buffer array for fit coefficients abuf[index][column]
@param row_clean: resulting list of the row indices but without the lines 
                     that are too close to 
                     each other for the fit output of sinfo_findLines()
@param wavelength_clean: corrected wavelength list corresponding to the 
                         row_clean array
                         output of sinfo_findLines()
@param n_found_lines: output of sinfo_findLines(): total number of found 
                      emission lines
@param dispersion:   dispersion of spectrum: micron per pixel
@param halfWidth:    half width of the box where the line must sit
@param minAmplitude: minimum amplitude of the Gaussian to do the fit
@param max_residual: maximum residual value, beyond that value
                     the polynomial lambda-position fit is rejected.
@param fwhm:         first guess for the full width of half maximum
                     of the sinfo_gaussian line fit
@param n_a_fitcoefs: number of fit coefficients for the single
                     column fits: lambda-position
@param n_b_fitcoefs: number of fit coefficients for the fits of
                     the single a coefficients across the columns
@param sigmaFactor:  factor of the standard deviation of the determined
                     polynomial coefficients of the columns beyond
                     which these coefficients are not used to carry out
                     the polynomial fit across the columns.
@param pixel_dist:   estimate of minimal pixel distance in spectral 
                     direction between slitlets
@param pixel_tolerance: maximum tolerated difference between estimated 
                        and fitted line positions.
@return # wavelength map image
        # abuf: array of smoothed coefficients of the polynomial fit 
          along the columns:
                              abuf[loc_index][column].
        # par: array of the resulting FitParams data structure
@doc this routine takes an image from a calibration emission lamp and 
     delivers the smoothed fit coefficients of a polynomial fit along the 
     columns of the line positions as output. This routine expects Nyquist 
     sampled spectra (either an interleaved image or an image 
     convolved with an appropriate function in spectral direction)




 */
cpl_image * sinfo_new_wave_cal( cpl_image   * image,
                                FitParams ** par ,
                                float     ** abuf,
                                int          n_slitlets,
                                int       ** row_clean,
                                float     ** wavelength_clean,
                                int        * n_found_lines,
                                float        dispersion,
                                int          halfWidth,
                                float        minAmplitude,
                                float        max_residual,
                                float        fwhm,
                                int          n_a_fitcoefs,
                                int          n_b_fitcoefs,
                                float        sigmaFactor,
                                float        pixel_dist,
                                float        pixel_tolerance )

{
    int          i=0, j=0;
    /* int k=0 ; */
    int          n_fit=0 ;
    int          n_reject=0 ;
    float     *  acoefs=NULL ;
    float     *  dacoefs=NULL ;
    float     ** dabuf=NULL ;
    float        chisq_poly=0 ;
    float     *  chisq_cross=NULL ;
    int          zeroind=0 ;
    int          crossInd=0 ;
    Bcoeffs   *  bco=NULL ;
    cpl_image  *  wavemap=NULL ;
    int ilx=0;
    int ily=0;
    /* float* pidata=NULL; */


    if (  NULL == image )
    {
        sinfo_msg_error("no image given") ;
        return NULL ;
    }
    check_nomsg(ilx=cpl_image_get_size_x(image));
    check_nomsg(ily=cpl_image_get_size_y(image));
    /* check_nomsg(pidata=cpl_image_get_data_float(image)); */

    if ( par == NULL )
    {
        sinfo_msg_error("no fit parameter data structure given") ;
        return NULL ;
    }
    if ( abuf == NULL )
    {
        sinfo_msg_error("no buffer for fit coefficients given") ;
        return NULL ;
    }
    if ( n_slitlets <= 0 )
    {
        sinfo_msg_error("impossible number of slitlets given") ;
        return NULL ;
    }
    if ( row_clean == NULL )
    {
        sinfo_msg_error("no row_clean array given") ;
        return NULL ;
    }
    if ( wavelength_clean == NULL )
    {
        sinfo_msg_error("no wavelength_clean array given") ;
        return NULL ;
    }

    if ( dispersion == 0. )
    {
        sinfo_msg_error("impossible dispersion given") ;
        return NULL ;
    }

    if ( halfWidth <= 0 || halfWidth > ily/2 )
    {
        sinfo_msg_error("impossible half width of the fitting box given") ;
        return NULL ;
    }
    if ( minAmplitude < 1. )
    {
        sinfo_msg_error("impossible minimal amplitude") ;
        return NULL ;
    }

    if ( max_residual <= 0. || max_residual > 1. )
    {
        sinfo_msg_error("impossible max_residual given") ;
        return NULL ;
    }
    if ( fwhm <= 0. || fwhm > 10. )
    {
        sinfo_msg_error("impossible fwhm given") ;
        return NULL ;
    }

    if ( n_a_fitcoefs <= 0 || n_a_fitcoefs > 9 )
    {
        sinfo_msg_error("unrealistic n_a_fitcoefs given") ;
        return NULL ;
    }

    if ( n_b_fitcoefs <= 0 || n_b_fitcoefs > 9 )
    {
        sinfo_msg_error("unrealistic n_b_fitcoefs given") ;
        return NULL ;
    }
    if ( sigmaFactor <= 0. )
    {
        sinfo_msg_error("impossible sigmaFactor given") ;
        return NULL ;
    }

    /* initialize the variables */
    n_reject = 0 ;
    n_fit = 0 ;


    /* fit each found line by using a Gaussian function and determine 
       the exact position */
    if ( 0 > (n_fit = sinfo_new_fit_lines( image , par, fwhm, n_found_lines, 
                    row_clean, wavelength_clean,
                    halfWidth, minAmplitude )) )
    {
        sinfo_msg_error("cannot fit the lines, error code of "
                        "sinfo_fitLines: %d", n_fit) ;
        return NULL ;
    }

    /* first check for faked lines like bad pixels */
    if ( -1 == sinfo_new_check_for_fake_lines (par, dispersion, 
                    wavelength_clean,
                    row_clean, n_found_lines,
                    ilx, pixel_tolerance) )
    {
        sinfo_msg_error("cannot fit the lines, error code of "
                        "sinfo_fitLines: %d", n_fit) ;
        return NULL ;
    }

    /* allocate memory */
    if (NULL == (acoefs = (float*) cpl_calloc(n_a_fitcoefs, sizeof(float))) ||
                    NULL == (dacoefs = (float*) cpl_calloc(n_a_fitcoefs, sizeof(float))) ||
                    NULL == (dabuf = (float**) cpl_calloc(n_a_fitcoefs, sizeof(float*))) ||
                    NULL == (chisq_cross = (float*) cpl_calloc(n_slitlets, sizeof(float))))
    {
        sinfo_msg_error("cannot allocate memory\n") ;
        return NULL ;
    }
    for ( i = 0 ; i < n_a_fitcoefs ; i++ )
    {
        if (  NULL == (dabuf[i] = (float*) cpl_calloc(ilx, sizeof(float))) )
        {
            sinfo_msg_error("cannot allocate memory") ;
            sinfo_free_float (&acoefs ) ;
            sinfo_free_float ( &dacoefs ) ;
            sinfo_free_float ( &chisq_cross ) ;
            sinfo_free_float_array(&dabuf,n_a_fitcoefs) ;
            return NULL ;
        }
    }


    /* fit wavelengths to the corresponding found positions for each column */
    /* k = 0 ; */
    for ( i = 0 ; i < ilx ; i++ )
    {
        zeroind = 0 ;
        if ( FLT_MAX == (chisq_poly = sinfo_new_polyfit( par, i, 
                        n_found_lines[i],
                        ily, dispersion,
                        max_residual, acoefs,
                        dacoefs, &n_reject,
                        n_a_fitcoefs)) )
        {
            /*
            sinfo_msg_warning ("error in sinfo_polyfit in column: %d\n", i) ;
             */
            for ( j = 0 ; j < n_a_fitcoefs ; j++ )
            {
                acoefs[j] = ZERO ;
                dacoefs[j] = ZERO ;
            }
        }

        for ( j = 0 ; j < n_a_fitcoefs ; j++ )
        {

            if ( acoefs[0] <= 0. || acoefs[1] ==0. ||
                            dacoefs[j] == 0. || isnan(acoefs[j]) )
            {
                zeroind = 1 ;
            }
        }
        for ( j = 0 ; j < n_a_fitcoefs ; j++ )
        {
            if ( zeroind == 0 )
            {
                abuf[j][i]  = acoefs[j] ;
                dabuf[j][i] = dacoefs[j] ;
            }
            else
            {
                abuf[j][i]  = ZERO ;
                dabuf[j][i] = ZERO ;
            }
        }
    }

    /* allocate memory for the fitting coefficients */
    if ( NULL == ( bco = sinfo_new_b_coeffs( n_slitlets, 
                    n_a_fitcoefs, n_b_fitcoefs)) )
    {
        sinfo_msg_error ("cannot allocate memory for the bcoeffs") ;
        sinfo_free_float_array(&dabuf,n_a_fitcoefs) ;
        sinfo_free_float (&acoefs ) ;
        sinfo_free_float (&dacoefs ) ;
        sinfo_free_float (&chisq_cross ) ;
        return NULL ;
    }

    /* fit each acoefs across the slitlets to smooth the result */
    if ( -1 == ( crossInd = sinfo_new_coeffs_cross_slit_fit( ilx, 
                    abuf,
                    dabuf,
                    bco,
                    sigmaFactor,
                    dispersion,
                    pixel_dist,
                    chisq_cross )) )
    {
        sinfo_msg_error ("cannot carry out the fitting of "
                        "coefficients across the columns") ;
        sinfo_free_float_array(&dabuf,n_a_fitcoefs) ;
        sinfo_free_float (&acoefs ) ;
        sinfo_free_float (&dacoefs ) ;
        sinfo_free_float (&chisq_cross ) ;
        return NULL ;
    }


    if ( NULL == (wavemap = sinfo_new_wave_map_slit (abuf, n_a_fitcoefs, 
                    ily, ilx)) )
    {
        sinfo_msg_error ("cannot carry out wavemap creation") ;
        sinfo_free_float_array(&dabuf,n_a_fitcoefs) ;
        sinfo_free_float (&acoefs ) ;
        sinfo_free_float (&dacoefs ) ;
        sinfo_free_float (&chisq_cross ) ;
        sinfo_new_destroy_b_coeffs(bco) ;
        return NULL ;
    }

    /* free all allocated memory */
    sinfo_free_float_array(&dabuf,n_a_fitcoefs) ;
    sinfo_free_float (&acoefs ) ;
    sinfo_free_float (&dacoefs ) ;
    sinfo_free_float (&chisq_cross ) ;
    sinfo_new_destroy_b_coeffs(bco) ;

    return wavemap ;
    cleanup:
    sinfo_free_float (&acoefs ) ;
    sinfo_free_float ( &dacoefs ) ;
    sinfo_free_float ( &chisq_cross ) ;
    sinfo_free_float_array(&dabuf,n_a_fitcoefs) ;
    sinfo_new_destroy_b_coeffs(bco) ;
    return NULL;
}



/**
  @brief  searches for successfully fitted fake lines like bad pixels 
          by comparing the found line positons with estimated templatate
          positions.
   @name  sinfo_new_check_for_fake_lines()
   @param par: array of the fit parameter data structure FitParams
   @param dispersion: estimated dispersion
   @param wavelength_clean: corrected wavelength list
   @param row_clean: corrected row list corresponding to the wavelength list
   @param n_found_lines: array of numbers of found lines of each column
   @param n_columns: total number of image columns
   @param pixel_tolerance: maximum tolerated difference between estimated 
                           and fitted line positions.
   @return # par: corrected FitParams
           # 0 in case of success
           # -1 in case of error
@doc this routine searches for successfully fitted fake lines like bad 
     pixels by comparing the found line positions with estimated template
     positions. This routine should be inserted in the wavelength calibration 
     routine just after the sinfo_fitLines() routine.



 */

int sinfo_new_check_for_fake_lines ( FitParams ** par,
                                     float        dispersion,
                                     float     ** wavelength_clean,
                                     int       ** row_clean,
                                     int        * n_found_lines,
                                     int          n_columns,
                                     float        pixel_tolerance )
{
    int i,  k ;
    int col ;
    int found ;
    float row ;
    float * beginWave ;
    float firstWave ;

    if ( par == NULL )
    {
        sinfo_msg_error("no fit parameter data structure given") ;
        return -1 ;
    }
    if ( dispersion == 0. )
    {
        sinfo_msg_error("dispersion zero given!") ;
        return -1 ;
    }
    if ( wavelength_clean == NULL )
    {
        sinfo_msg_error("no wavelength array given!") ;
        return -1 ;
    }
    if ( row_clean == NULL )
    {
        sinfo_msg_error("no row array given!") ;
        return -1 ;
    }
    if ( n_found_lines == NULL )
    {
        sinfo_msg_error("no number of lines given!") ;
        return -1 ;
    }
    if ( n_columns < 200 )
    {
        sinfo_msg_error("wrong number of columns given!") ;
        return -1 ;
    }

    /* first determine the estimated beginning wavelength of the first row */
    for ( col = 0 ; col < n_columns ; col++ )
    {
        if ( n_found_lines[col] == 0 )
        {
            continue ;
        } 
        if ( NULL == (beginWave = (float*) cpl_calloc( n_found_lines[col], 
                        sizeof(float) ) ) )
        {
            sinfo_msg_error("could not allocate memory!") ;
            return -1 ;
        }
        for ( k = 0 ; k < n_found_lines[col] ; k++ )
        {
            beginWave[k] = wavelength_clean[col][k] -
                            (float)row_clean[col][k] * dispersion ;
        }
        /* determine the clean mean of the estimated
           beginning wavelengths of one column */
        if ( FLT_MAX == (firstWave = sinfo_new_clean_mean (beginWave,
                        n_found_lines[col],
                        10., 10.) ) )
        {
            sinfo_msg_error("clean mean did not work!") ;
            return -1 ;
        }

        cpl_free (beginWave) ;
        /* go through the lines in that column and select the 
           correct FitParam structure */
        for ( k = 0 ; k < n_found_lines[col] ; k++ ) 
        {    
            /* compute the estimated line position */
            row = ( wavelength_clean[col][k] - firstWave ) / dispersion ;

            /* go through all fit parameters and find the corresponding one */
            found = -1 ;
            for ( i = 0 ; i < (par[0] -> n_params) ; i ++ )
            {    
                /* find the given column and go through the
                   lines in that column */
                if ( (par[i] -> column == col) && (par[i] -> line == k) &&
                                (par[i] -> wavelength == wavelength_clean[col][k]) )
                {
                    found = i ;
                    break ;
                }
            }
            if ( found != -1 )
            {
                /* set fit params to zero where the fitted row
                   position and the estimated 
            row positions are outside the tolerance */
                if ( fabs(row - par[found]->fit_par[2]) > pixel_tolerance ) 
                {
                    sinfo_msg_warning("found bad line in col: "
                                    "%d line: %d in row: %f difference: %f",
                                    col, k, par[found]->fit_par[2],
                                    row - par[found]->fit_par[2])  ;
                    par[found]->fit_par[2] = 0. ;
                }
            }
            else
            {
                sinfo_msg_warning("fit parameter of col %d and line "
                                "no %d not found!\n", col, k ) ;
            }
        }
    }

    return 0 ;
}


/**
@brief This routine cross-correlates a shifted emission line frames and 
       determines the shift to the old one which is given by its polynomial 
       coefficients.
@name  sinfo_new_create_shifted_slit_wavemap2()
@param lineIm:       new shifted emission line frame
@param coeffs:       calculated polynomial coefficients,
                     output of sinfo_coeffsCrossSlitFit()
@param n_fitcoeffs:  number of polynomial coefficients, order + 1
@param wavelength:   wavelength list from line list
@param intensity:    corresponding line intensity from line list
@param n_lines:      number of lines in the list
@param magFactor:    magnification factor for help arrays
@param dispersion:   estimate of the dispersion
@param pixel_dist:   estimate of minimal pixel distance in spectral direction
                     between slitlets
@return wavelength map
@doc This routine cross-correlates a shifted emission line frames and 
     determines the shift to the old one which is given by its polynomial 
     coefficients. Then the a0 coefficients is recalculated and afterwards 
     a new wavelength calibration map 
     is generated using the already calculated smoothed polynomial coefficients.
 */

cpl_image * sinfo_new_create_shifted_slit_wavemap2 ( cpl_image * lineIm,
                                                     float    ** coeffs,
                                                     int      n_fitcoeffs,
                                                     float  * wavelength,
                                                     float  * intensity,
                                                     int      n_lines,
                                                     int      magFactor,
                                                     float    dispersion,
                                                     float    pixel_dist )
{
    cpl_image * wavemap ;
    double * result ;
    float * filter_spec ;
    float centreval ;
    float centrepix ;
    float cenpos, cenpix ;
    float pixvalue ;
    float wavelag ;
    /*float maxres ;*/
    float angst ;
    /*float temp ;*/

    int        numpar, its ;
    int        * mpar ;
    float      tol, lab ;
    float      * xdat, * wdat ;
    Vector     * peak;
    int   iters, xdim, ndat ;
    int   row , col ;
    int   i, j, k/*, l, m*/, n ;
    int   sign, found, line, width ;
    int var, maxlag, cmin, cmax ;
    float offset2 ;
    float threshold ;
    int ed1, ed2 ;

    int edge[N_SLITLETS] ;
    float par[MAXPAR] ;
    float derv_par[MAXPAR] ;

    float* emline=NULL ;
    float* spec=NULL ;
    float* wave=NULL ;
    float* a0=NULL ;
    float* a0_clean=NULL ;


    float* sub_col_index=NULL ;
    float* sub_acoefs=NULL;
    float* sub_dacoefs=NULL ;
    double* z=NULL ;
    double* a=NULL ;

    float** bcoef=NULL ;
    float* wcoefs=NULL ;


    int ns;
    float * acoefsclean ;
    float col_index;

    float ** ucoefs, **vcoefs, **covar ;
    double sum, sumq, mean ;
    double sigma ;
    double cliphi, cliplo ;
    float chisq ;
    int num, ndata ;
    gsl_poly_complex_workspace * w ;
    double xcorr_max ;
    int delta ;
    int ilx=0;
    int ily=0;
    int olx=0;
    int oly=0;
    float* pidata=NULL;
    float* podata=NULL;


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

    ilx=cpl_image_get_size_x(lineIm);
    ily=cpl_image_get_size_y(lineIm);
    pidata=cpl_image_get_data_float(lineIm);

    if ( coeffs == NULL )
    {
        sinfo_msg_error (" no coefficient sinfo_matrix given!\n") ;
        return NULL ;
    }
    if ( n_fitcoeffs < 2 )
    {
        sinfo_msg_error (" wrong number of polynomial coefficients given!\n") ;
        return NULL ;
    }
    if ( wavelength == NULL || intensity == NULL )
    {
        sinfo_msg_error (" no input image given!\n") ;
        return NULL ;
    }
    if ( n_lines < 1 || magFactor < 1 )
    {
        sinfo_msg_error (" no input image given!\n") ;
        return NULL ;
    }
    var    = (magFactor - 1)*(magFactor - 1) ;
    /* find out if Angstroem or microns are used */
    if ( wavelength[0] > 10000. )
    {
        /* Angstroem */
        angst = 10000. ;
    }
    else if ( wavelength[0] > 1000. && wavelength[0] < 10000. )
    {
        /* nanometers */
        angst = 1000. ;
    }
    else
    {
        /* microns */
        angst = 1. ;
    }

    bcoef=sinfo_new_2Dfloatarray(N_SLITLETS,n_fitcoeffs) ;
    wcoefs=cpl_calloc(n_fitcoeffs,sizeof(float)) ;

    emline=cpl_calloc(ily,sizeof(float)) ;
    spec=cpl_calloc(ily,sizeof(float)) ;
    wave=cpl_calloc(n_lines,sizeof(float)) ;
    a0=cpl_calloc(ilx,sizeof(float)) ;
    a0_clean=cpl_calloc(ilx,sizeof(float)) ;



    sub_col_index=cpl_calloc(ilx,sizeof(float)) ;
    sub_acoefs=cpl_calloc(ilx,sizeof(float));
    sub_dacoefs=cpl_calloc(ilx,sizeof(float)) ;

    a=cpl_calloc(n_fitcoeffs,sizeof(double)) ;
    z=cpl_calloc(2*(n_fitcoeffs - 1),sizeof(double)) ;

    /* find the slitlet edges from the a0 coefficient */
    n = 0 ;
    threshold = pixel_dist * fabs(dispersion) ;
    for ( i = PIXEL ; i < ilx - PIXEL ; )
    {
        if (fabs(coeffs[0][i+1] - coeffs[0][i]) >= threshold )
        {
            edge[n] = i+1 ;
            n++ ;
            i += PIXEL ;
        }
        i++ ;
    }


    /* allocate memory */
    if ( NULL == (wavemap = cpl_image_new ( ilx, ily, CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error (" could not allocate memory!\n") ;
        cpl_free(emline) ;
        cpl_free(spec) ;
        cpl_free(a) ;
        cpl_free(z) ;
        cpl_free(sub_acoefs);
        cpl_free(sub_dacoefs) ;
        cpl_free(a0_clean) ;
        cpl_free(sub_col_index) ;
        cpl_free(wave) ;
        cpl_free(a0) ;
        return NULL ;
    }
    olx=cpl_image_get_size_x(wavemap);
    oly=cpl_image_get_size_y(wavemap);
    podata=cpl_image_get_data_float(wavemap);

    /* first store each spectrum in a buffer */
    for ( col = 0 ; col < ilx ; col++ )
    {
        /* initialize the emline array for each column */
        for ( i = 0 ; i < ily ; i++ )
        {
            emline[i] = 0. ;
        }
        /* determine the coefficients by using the given bcoefs */
        for ( i = 0 ; i < n_fitcoeffs ; i++ )
        {
            /* initialize coefficients and solution */
            if (i < n_fitcoeffs-1)
            {
                z[2*i] = 0. ;
                z[2*i+1] = 0. ;
            }
            a[i] = coeffs[i][col] ;
        }

        float a_initial = coeffs[0][col] ;
        /* go through the lines and generate an artificial spectrum */
        for ( line = 0 ; line < n_lines ; line++ )
        {
            /* go from Angstroem to micron */
            wave[line] = wavelength[line]/angst ;

            /* ------------------------------------------------------------
             * solve the polynomial for the exact offset of the line that means
             * find the root of the polynomial of order n_fitcoefs - 1
             */
            a[0] = a_initial - wave[line] ;

            if (NULL==(w=sinfo_gsl_poly_complex_workspace_alloc(n_fitcoeffs))) 
            {
                sinfo_msg_error(" could not allocate complex workspace!") ;
                cpl_image_delete(wavemap) ;
                return NULL ;
            }
            if (-1 == sinfo_gsl_poly_complex_solve(a, n_fitcoeffs, w, z)) 
            {
                sinfo_msg_error(" sinfo_gsl_poly_complex_solve did not work!") ;
                cpl_image_delete(wavemap) ;
                return NULL ;
            }
            sinfo_gsl_poly_complex_workspace_free(w) ;           

            j = 0 ;
            found = -1 ;
            for ( i = 0 ; i < n_fitcoeffs - 1 ; i++ )
            {
                /* test for appropriate solution */
                if( (z[2*i] > (-1.)*(float) ily/2. &&
                                z[2*i] < (float)ily/2.) && z[2*i+1] == 0. )
                {
                    found = 2*i ;
                    j ++ ;
                }
                else
                {
                    continue ;
                }
            }
            if ( j == 0 )
            {
                sinfo_msg_warning(" no offset solution found for "
                                "line %d in column %d\n", line, col) ;
                continue ;
            }
            else if ( j == 1 )
            {
                cenpos = z[found] + (float) ily/2. ;
            }
            else
            {
                sinfo_msg_warning(" two or more offset solutions found "
                                "for line %d in column %d", line, col) ;
                continue ;
            }

            /*----------------------------------------------------------------
             * magnify image by the given factor add an additional offset
             */
            cenpix = cenpos ;

            /* determine max and min pixel limits over which line should 
               be convolved */
            cmin = (sinfo_new_nint(cenpix) - (var-1)) > 0 ? 
                            sinfo_new_nint(cenpix) - (var-1) : 0 ;
            cmax = (sinfo_new_nint(cenpix) + (var-1)) < ily ?
                            sinfo_new_nint(cenpix) + (var-1)  : ily ;

            /* convolve neon lines with Gaussian function */
            for ( j = cmin ; j < cmax ; j++ )
            {
                emline[j] += intensity[line] * 
                                exp((double)(-0.5*(j-cenpix)*(j-cenpix))/(double)var) ;
            }
        }

        /*--------------------------------------------------------------------
         * for each column, map the image data points onto an magFactor times 
           bigger element grid for FFT  in the cross sinfo_correlation, first 
           initialize the two helping arrays for each new column.
         */
        for ( k = 0 ; k < ily ; k++ )
        {
            spec[k] = 0. ;
        }

        /* now take the image data points of the column and put them into 
           the spec array */
        for ( row = 0 ; row < ily ; row++ ) /* go through the column */
        {
            /* set bad pixels or negative values to zero */
            if (!isnan(pidata[col + row*ilx]) &&
                            (pidata[col + row*ilx] > 0.))
            {
                spec[row] = pidata[col + row*ilx] ;
            }
            else
            {
                spec[row] = 0. ;
            }
        }
        /* convolve the spectrum by Gaussian */
        filter_spec = sinfo_function1d_filter_lowpass(spec,ily, 
                        LOW_PASS_GAUSSIAN,
                        magFactor) ;

        /* now call the cross sinfo_correlation routine */
        result = sinfo_new_xcorrel( filter_spec, ily, emline, ily,
                        ily/2, &delta, &maxlag, &xcorr_max) ;

        if ( xcorr_max <= 0. )
        {
            sinfo_msg_warning("no positive cross sinfo_correlation "
                            "sum , col %d set to ZERO \n", col) ;
            for ( row = 0 ; row < ily ; row++ )
            {
                podata[col + row*ilx] = ZERO ;
            }
            sinfo_function1d_del(filter_spec) ;
            cpl_free(result) ;
            continue ;
        }

        /* in this section, we fit the sinfo_correlation function with a gauss,
          and find its peak, thus getting subpixel-accuracy */

        i = maxlag; j = i+1;
        while (result[j] < result[i])
        {
            i++; j++;
        }
        i = maxlag; k = i-1;
        while (result[k] < result[i])
        {
            i--; k--;
        }
        width = j-k+1;
        /* allocate memory for the spectral sinfo_vector */
        if ( NULL == (peak = sinfo_new_vector (width)) )
        {
            sinfo_msg_error (" cannot allocate new Vector \n") ;
            sinfo_function1d_del(filter_spec) ;
            cpl_free(result) ;
            return NULL ;
        }


        /* allocate memory */
        xdat = (float *) cpl_calloc( peak -> n_elements, sizeof (float) ) ;
        wdat = (float *) cpl_calloc( peak -> n_elements, sizeof (float) ) ;
        mpar = (int *)   cpl_calloc( MAXPAR, sizeof (int) ) ;

        /* determine the values of the spectral sinfo_vector given as input */
        /* go through the chosen column */

        for ( i = 0 ; i < width ; i++ )
        {
            peak -> data[i] = result[k+i]/xcorr_max * 100. ;
            xdat[i] = i;
            wdat[i] = 1.0;
        }

        /* set initial values for the fitting routine */
        xdim     = XDIM;
        ndat     = peak -> n_elements ;
        numpar   = MAXPAR ;
        tol      = TOL ;
        lab      = LAB ;
        its      = ITS ;
        par[1] = width/2.0 ;
        par[2] = (float) (maxlag - k) ;
        par[3] = (peak -> data[0] + peak -> data[peak->n_elements - 1]) / 2.0 ;
        par[0]  = result[maxlag]/xcorr_max * 100. - (par[3]) ;

        for ( i = 0 ; i < MAXPAR ; i++ )
        {
            derv_par[i] = 0.0 ;
            mpar[i] = 1 ;
        }

        /* finally, do the least square fit using a sinfo_gaussian */
        if ( 0 > ( iters = sinfo_new_lsqfit_c( xdat, &xdim, peak -> data, 
                        wdat, &ndat, par,
                        derv_par, mpar,
                        &numpar, &tol, &its, &lab )) )
        {
            sinfo_msg_warning (" sinfo_new_lsqfit_c: least squares fit "
                            "failed in col: %d, error no.: %d", col, iters);
            sinfo_new_destroy_vector ( peak ) ;
            cpl_free ( xdat ) ;
            cpl_free ( wdat ) ;
            cpl_free ( mpar ) ;
            sinfo_function1d_del(filter_spec) ;
            cpl_free(result) ;
            continue ;
        }

        sinfo_new_destroy_vector ( peak ) ;
        cpl_free (xdat) ;
        cpl_free (wdat) ;
        cpl_free (mpar) ;
        sinfo_function1d_del(filter_spec) ;
        cpl_free(result) ;

        wavelag =((float)ily/2 - (float)k - par[2]) ;

        if ( fabs(wavelag) > (float)ily/20. )
        {
            sinfo_msg_warning("wavelag very big , col %d set to ZERO ", col) ;
            for ( row = 0 ; row < ily ; row++ )
            {
                podata[col + row*ilx] = ZERO ;
            }
            continue ;
        }

        /*--------------------------------------------------------------------
         * determine new zero order coefficient centreval, of which the 
         * formula is determined by setting equal a polynomial shifted by 
         * wavelag with the same higher order coefficients and set the new 
         * zero order coefficient to get both sides of the equation 
         * approximately equal.
         */
        centreval = a_initial ;
        for ( i = 1 ; i < n_fitcoeffs ; i++ )
        {
            if ( i%2 == 0 )
            {
                sign = -1 ;
            }
            else
            {
                sign = 1 ;
            }
            centreval += (float)sign * coeffs[i][col]*pow(wavelag, i) ;
        }
        a0[col] = centreval ;
    }

    /* go through the single slitlets */
    for ( ns = 0 ; ns < N_SLITLETS ; ns++ )
    {
        /* determine the slitlet edges */
        if ( ns == 0 )
        {
            ed1 = 0 ;
            ed2 = edge[0] ;
        }
        else if ( ns == N_SLITLETS - 1 )
        {
            ed1 = edge[N_SLITLETS - 2] ;
            ed2 = ilx ;
        }
        else
        {
            ed1 = edge[ns-1] ;
            ed2 = edge[ns] ;
        }

        int nc = 0 ;
        for ( i = ed1 ; i < ed2 ; i++ )
        {
            if ( isnan(a0[i]) || a0[i] == 0. )
            {
                continue ;
            }
            else
            {
                nc++ ;
            }
        }
        if ( NULL == (acoefsclean = (float*) cpl_calloc(nc , sizeof(float))) )
        {
            sinfo_msg_error("could not allocate memory for acoefsclean!\n") ;
            return NULL ;
        }
        nc = 0 ;
        for ( i = ed1 ; i < ed2 ; i++ )
        {
            if ( isnan(a0[i]) || a0[i] == 0. )
            {
                continue ;
            }
            else
            {
                acoefsclean[nc] = a0[i] ;
                nc++ ;
            }
        }

        /* ----------------------------------------------------------
         * determine the clean mean and sigma value of the coefficients,
         * that means reject 10 % of the extreme low and high values
         */
        sinfo_pixel_qsort(acoefsclean, nc) ;
        sum   = 0. ;
        sumq  = 0. ;
        mean  = 0. ;
        sigma = 0. ;
        n     = 0 ;
        for ( i = (int)((float)nc*LOW_REJECT) ; 
                        i < (int)((float)nc*HIGH_REJECT) ; i++ )
        {
            sum  += (double)acoefsclean[i] ;
            sumq += ((double)acoefsclean[i] * (double)acoefsclean[i]) ;
            n ++ ;
        }
        mean          = sum/(double)n ;
        sigma         = sqrt( sumq/(double)n - (mean * mean) ) ;
        cliphi        = mean + sigma * (double)3. ;
        cliplo        = mean - sigma * (double)3. ;
        /* fit only the reasonnable values */
        num = 0 ;
        col_index = 0 ;
        for ( i = ed1 ; i < ed2 ; i++ )
        {
            /* take only the reasonnable coefficients */
            if ( !isnan(a0[i]) && (a0[i] <= cliphi) && (a0[i] >= cliplo) &&
                            (a0[i] != 0.) )
            {
                sub_acoefs[num]    = a0[i] ;
                sub_dacoefs[num]   = 0.0000005 ;
                sub_col_index[num] = col_index ;
                num ++ ;
            }
            col_index++ ;
        }
        ndata = num ;
        offset2 = (float)(col_index-1) / 2. ;

        if ( ndata < n_fitcoeffs )
        {
            sinfo_msg_error(" not enough data found in slitlet %d\
                    to determine the fit coefficients.\n", ns) ;
            cpl_free(acoefsclean) ;
            return NULL ;
        }

        /* allocate coefficient matrices, see numerical recipe function 
           sinfo_matrix */
        ucoefs = sinfo_matrix(1, ndata, 1, n_fitcoeffs) ;
        vcoefs = sinfo_matrix(1, ndata, 1, n_fitcoeffs) ;
        covar  = sinfo_matrix(1, n_fitcoeffs, 1, n_fitcoeffs) ;

        /* scale the x-values for the fit */
        for ( i = 0 ; i < ndata ; i++ )
        {
            sub_col_index[i] = (sub_col_index[i] - offset2) / offset2 ;
        }

        /* finally, do the singular value decomposition fit */
        sinfo_svd_fitting ( sub_col_index-1, sub_acoefs-1, 
                            sub_dacoefs-1, ndata, bcoef[ns]-1,
                            n_fitcoeffs, ucoefs, vcoefs, wcoefs-1, 
                            covar, &chisq, sinfo_fpol ) ;

        /* scale the found coefficients */
        for ( i = 0 ; i < n_fitcoeffs ; i ++ )
        {
            bcoef[ns][i] /= pow( offset2, i ) ;
        }

        /* free memory */
        cpl_free (acoefsclean) ;
        sinfo_free_matrix( ucoefs, 1/*, ndata*/, 1/*, n_fitcoeffs */) ;
        sinfo_free_matrix( vcoefs, 1/*, ndata*/, 1/*, n_fitcoeffs */) ;
        sinfo_free_matrix( covar, 1/*, n_fitcoeffs*/, 1/*, n_fitcoeffs*/) ;

        /* now calculate the smoothed acoefs for each column */
        col_index = 0 ;
        for ( i = ed1 ; i < ed2 ; i++ )
        {
            a0_clean[i] = 0. ;
            for ( n = 0 ; n < n_fitcoeffs ; n++ )
            {
                a0_clean[i] += bcoef[ns][n] *
                                pow((float)col_index - offset2, n) ;
            }
            col_index++ ;
        }

    }

    for ( col = 0 ; col < ilx ; col++ )
    {
        /* prepare to write out wavelength as pixel values */
        for ( row = 0 ; row < oly ; row++ )
        {
            centrepix = (float)row - ((float)oly - 1.)/2. ;
            pixvalue = 0. ;
            for ( i = 1 ; i < n_fitcoeffs ; i++ )
            {
                pixvalue += coeffs[i][col]*pow(centrepix, i) ;
            }
            podata[col+row*olx] = a0_clean[col] + pixvalue ;
        }
    }


    cpl_free(emline) ;
    cpl_free(spec) ;
    cpl_free(wave) ;
    cpl_free(a0) ;
    cpl_free(a0_clean) ;


    cpl_free(sub_col_index) ;
    cpl_free(sub_acoefs);
    cpl_free(sub_dacoefs) ;

    cpl_free(a) ;
    cpl_free(z) ;

    sinfo_new_destroy_2Dfloatarray(&bcoef,n_fitcoeffs) ;
    cpl_free(wcoefs) ;

    return wavemap ;
}


/**
@brief his routine determines the clean averaged error (shift) of the 
       brightest lines in the emission line image with respect to the 
       expected positions from the wavelength calibration.
@name       sinfo_new_check_line_positions()
@param lineIm:       new shifted emission line frame
@param coeffs:       calculated polynomial coefficients, output of 
                     sinfo_coeffsCrossSlitFit()
@param n_fitcoeffs:  number of polynomial coefficients, order + 1
@param   guess_disp1  guess dispersion (to have positioning error in pix)
@param par:          fit parameters
@return clean averaged position error (shift) of the brightest lines in the 
        emission line image with respect to the expected positions from the 
        wavelength calibration (error in the wavelength regime (microns)).
@doc This routine determines the clean averaged error (shift) of the brightest 
     lines in the emission line image with respect to the expected positions 
     from the wavelength calibration. 
     The error is given in the wavelength regime (microns).
     It should give the user an impression of the quality of the wavelength 
     calibration.  
 */

float sinfo_new_check_line_positions ( cpl_image     * lineIm,
                                       float       ** coeffs,
                                       int            n_fitcoeffs,
                                       float            gdisp1,
                                       FitParams   ** par )
{
    float wave_shift=0 ;
    float amp[100] ;
    float sort_amp[100] ;
    float offset=0 ;

    float position=0;
    float lambda=0;
    float wave=0 ;
    int i=0;
    int j=0;
    int k=0;

    int m=0;

    int col=0;
    int firstj=0;
    float* shift_col=NULL ;
    int* foundit=NULL ;
    int n_lines=0;
    int lin, found=0 ;
    int lx=0;
    int ly=0;
    /* float* pdata=NULL; */

    if ( lineIm == NULL )
    {
        sinfo_msg_error (" no input image given!\n") ;
        return FLAG ;
    }
    lx=cpl_image_get_size_x(lineIm);
    ly=cpl_image_get_size_y(lineIm);
    /* pdata=cpl_image_get_data_float(lineIm); */

    if ( coeffs == NULL )
    {
        sinfo_msg_error (" no coefficient sinfo_matrix given!\n") ;
        return FLAG ;
    }
    if ( par == NULL )
    {
        sinfo_msg_error (" no fit parameters given!\n") ;
        return FLAG ;
    }
    if ( n_fitcoeffs < 2 )
    {
        sinfo_msg_error (" wrong number of polynomial coefficients given!\n") ;
        return FLAG ;
    }

    offset = (float) (ly -1.) / 2. ;
    n_lines = par[0]->n_params/lx ;

    shift_col=cpl_calloc(lx,sizeof(float)) ;
    foundit=cpl_calloc(par[0]->n_params,sizeof(int)) ;

    /*search for the brightest 5 lines in each column and compute the 
      wavelength difference*/
    for ( col = 0 ; col < lx ; col++ )
    {
        int n = 0 ;
        for ( i = 0 ; i < par[0]->n_params ; i++ )
        {
            if (par[i]->column == col && par[i]->fit_par[2] != 0. && 
                            par[i]->fit_par[1] > 1. && par[i]->fit_par[1] < 7. )
            {
                foundit[n] = i ;
                amp[n] = par[i]->fit_par[0] ;
                sort_amp[n] = amp[n] ;
                n++ ;
            }
        }
        sinfo_pixel_qsort(sort_amp, n) ;

        if ( n > 5 )
        {
            firstj = n - 5 ;
        }
        else
        {
            firstj = 0 ;
        }
        int l = 0 ;
        float shift = 0 ;
        for ( j = firstj ; j < n ; j++ )
        {
            for ( m = 0 ; m < n ; m++ )
            {
                if ( sort_amp[j] == amp[m] )
                {
                    position = par[foundit[m]]->fit_par[2] ; 
                    lambda   = par[foundit[m]]->wavelength ;
                    wave = 0 ;
                    for ( k = 0 ; k < n_fitcoeffs ; k++ ) 
                    {
                        wave += coeffs[k][col]*pow(position-offset, k) ;
                    }
                    shift += lambda - wave ;
                    l++ ;
                }
            }
        }
        if ( l == 0 ) continue ;
        shift_col[col] = shift/(float)l ; 
    }
    wave_shift = sinfo_new_clean_mean(shift_col, lx, 10., 10.) ;
    sinfo_msg("Overall positioning error: %3.2g [um] %3.2g [pix]", 
              wave_shift,wave_shift/fabs(gdisp1)) ;


    /* determine positioning error for each found line */
    for ( lin = 0 ; lin < n_lines ; lin++ )
    {
        for ( col = 0 ; col < lx ; col++ )
        {
            shift_col[col] = 0. ;
            found = -1 ;
            for ( i = 0 ; i < par[0]->n_params ; i++ )
            {
                if (par[i]->column == col && par[i]->fit_par[2] != 0. && 
                                par[i]->fit_par[1] > 1. && par[i]->fit_par[1] < 7. &&
                                par[i]->line == lin )
                {
                    found = i ;
                }
            }
            if (found == -1) break ;

            position = par[found]->fit_par[2] ; 
            lambda  = par[found]->wavelength ;
            wave = 0 ;
            for ( k = 0 ; k < n_fitcoeffs ; k++ ) 
            {
                wave += coeffs[k][col]*pow(position-offset, k) ;
            }
            shift_col[col] = lambda - wave ;
        }
        if (found != -1 )
        {
            sinfo_msg("shift: %3.2g [um] %3.2g (pix) at: %4.3f [um]",
                            sinfo_new_clean_mean(shift_col,lx, 10., 10.),
                            sinfo_new_clean_mean(shift_col,lx, 10., 10.)/fabs(gdisp1),
                            lambda) ;
        }
    }
    cpl_free(shift_col) ;
    cpl_free(foundit) ;

    return wave_shift ;
}


/**
@brief This routine determines the clean averaged error (shift) of the 
       brightest lines in the emission line image with respect to the 
       expected positions from the wavelength calibration.
@name  sinfo_new_check_correlated_line_positions()
@param lineIm:       new shifted emission line frame
@param coeffs:       calculated polynomial coefficients,
                                      output of sinfo_coeffsCrossSlitFit()
@param n_fitcoeffs:  number of polynomial coefficients, order + 1
@param wavelength:   wavelength list from line list
@param intensity:    corresponding line intensity from line list
@param n_lines:      number of lines in list
@param fwhm:         guess value for full width of half maximum of 
                     sinfo_gaussian
@param width:        half width of the box where the line must sit
@param min_amplitude: minimum line amplitude with respect to the background to 
                      do the fit
@param par:          fit parameters
@return clean averaged position error (shift) of the brightest lines in the 
        emission line image with respect to the expected positions from the 
        wavelength calibration (error in the wavelength regime (microns)).
@doc This routine determines the clean averaged error (shift) of the brightest 
     lines in the emission line image with respect to the expected positions 
     from the wavelength calibration.  The error is given in the wavelength 
     regime (microns). It should give the user an impression of the quality 
     of the wavelength calibration.  
 */

float sinfo_new_check_correlated_line_positions ( cpl_image     * lineIm,
                                                  float       ** coeffs,
                                                  int            n_fitcoeffs,
                                                  float        * wavelength,
                                                  float        * intensity,
                                                  int            n_lines,
                                                  float          fwhm,
                                                  float          width,
                                                  float          min_amplitude,
                                                  float          dispersion,
                                                  FitParams   ** par )
{
    float wave_shift=0 ;
    float offset=0;
    float shift=0;
    float position=0;
    float lambda=0;
    float wave=0;
    int i=0;
    int j=0;
    int k=0;  /*, l, m*/

    int c=0;
    int z=0;
    int col=0;/*, firstj*/
    int found=0;/*lin,*/
    int line=0;
    int result=0;
    float cenpos=0;
    float angst=0;


    int* foundit=NULL ;
    float* shift_col=NULL ;
    float* wave_cor=NULL ;
    double* a=NULL ;
    double* zroot=NULL ;

    gsl_poly_complex_workspace * w=NULL ;
    Vector * vline=NULL;
    int    * mpar=NULL;
    float  * xdat=NULL;
    float  * wdat=NULL;
    int lx=0;
    int ly=0;
    /* float* pdata=NULL; */

    if ( lineIm == NULL )
    {
        sinfo_msg_error (" no input image given!\n") ;
        return FLAG ;
    }
    lx=cpl_image_get_size_x(lineIm);
    ly=cpl_image_get_size_y(lineIm);
    /* pdata=cpl_image_get_data_float(lineIm); */


    if ( coeffs == NULL )
    {
        sinfo_msg_error (" no coefficient sinfo_matrix given!\n") ;
        return FLAG ;
    }
    if ( par == NULL )
    {
        sinfo_msg_error (" no fit parameters given!\n") ;
        return FLAG ;
    }
    if ( n_fitcoeffs < 2 )
    {
        sinfo_msg_error (" wrong number of polynomial coefficients given!\n") ;
        return FLAG ;
    }
    if ( wavelength == NULL || intensity == NULL )
    {
        sinfo_msg_error (" no line list given!\n") ;
        return FLAG ;
    }
    if ( fwhm <= 0 )
    {
        sinfo_msg_error (" wrong guess fwhm given!\n") ;
        return FLAG ;
    }
    if ( width <= 0 )
    {
        sinfo_msg_error (" wrong half width given!\n") ;
        return FLAG ;
    } 
    if ( min_amplitude <= 0 )
    {
        sinfo_msg_error (" wrong guess amplitude given!\n") ;
        return FLAG ;
    } 

    /* allocate memory for the spectral sinfo_vector */
    if ( NULL == (vline = sinfo_new_vector (2*width + 1)) )
    {
        sinfo_msg_error (" cannot allocate new Vector \n") ;
        return -14 ;
    }
    /* allocate memory */
    xdat = (float *) cpl_calloc( vline -> n_elements, sizeof (float) ) ;
    wdat = (float *) cpl_calloc( vline -> n_elements, sizeof (float) ) ;
    mpar = (int *)   cpl_calloc( MAXPAR, sizeof (int) ) ;


    foundit=cpl_calloc(par[0]->n_params,sizeof(int)) ;
    shift_col=cpl_calloc(lx,sizeof(float)) ;
    wave_cor=cpl_calloc(n_lines,sizeof(float)) ;
    a=cpl_calloc(n_fitcoeffs,sizeof(float)) ;
    zroot=cpl_calloc(2*(n_fitcoeffs - 1),sizeof(float)) ;



    /* determine the approximate line positions using the line list and the 
       coefficients */
    /* find out if Angstroem or microns are used */
    if ( wavelength[0] > 10000. )
    {
        /* Angstroem */
        angst = 10000. ;
    }
    else if ( wavelength[0] > 1000. && wavelength[0] < 10000. )
    {
        /* nanometers */
        angst = 1000. ;
    }
    else
    {
        /* microns */
        angst = 1. ;
    }
    offset = ((float) ly -1.) / 2. ;

    k = 0 ;
    for ( col = 10 ; col < 25 ; col++ )
    {
        /* determine the coefficients by using the given bcoefs */
        for ( i = 0 ; i < n_fitcoeffs ; i++ )
        {
            /* initialize coefficients and solution */
            if (i < n_fitcoeffs-1)
            {
                zroot[2*i] = 0. ;
                zroot[2*i+1] = 0. ;
            }
            a[i] = coeffs[i][col] ;
        }
        float a_initial = a[0] ;

        /* go through the lines */
        for ( line = 0 ; line < n_lines ; line++ )
        {
            /* go from Angstroem to micron */
            wave_cor[line] = wavelength[line]/angst ;
            if (line > 0 && line < n_lines-1)
            {
                if (fabs((wave_cor[line] - wave_cor[line-1]) / 
                                dispersion ) < 2*width ||
                                fabs((wave_cor[line] - wave_cor[line+1]) /
                                                dispersion ) < 2*width )
                {
                    continue ;
                }
            }

            a[0] = a_initial - wave_cor[line] ;

            if (NULL==(w=sinfo_gsl_poly_complex_workspace_alloc(n_fitcoeffs)))
            {
                sinfo_msg_error(" could not allocate complex workspace!") ;
                return FLAG ;
            }
            if (-1 == sinfo_gsl_poly_complex_solve(a, n_fitcoeffs, w, zroot))
            {
                sinfo_msg_error(" sinfo_gsl_poly_complex_solve did not work!") ;
                return FLAG ;
            }
            sinfo_gsl_poly_complex_workspace_free(w) ;

            j = 0 ;
            found = -1 ;
            for ( i = 0 ; i < n_fitcoeffs - 1 ; i++ )
            {
                /* test for appropriate solution */
                if( (zroot[2*i] > (-1.)*(float) ly/2. &&
                                zroot[2*i] < (float)ly/2.) && zroot[2*i+1] == 0. )
                {
                    found = 2*i ;
                    j ++ ;
                }
                else
                {
                    continue ;
                }
            }

            if ( j == 0 )
            {
                sinfo_msg_warning(" no offset solution found for line %d "
                                "in column %d\n", line, col) ;
                continue ;
            }
            else if ( j == 1 )
            {
                cenpos = zroot[found] + (float)ly / 2. ; ;
            }
            else
            {
                sinfo_msg_warning(" two or more offset solutions found for \
                       line %d in column %d\n", line, col) ;
                continue ;
            }

            if ( cenpos <= 0 )
            {
                continue ;
            }

            /* --------------------------------------------------------------
             * fit the single lines using sinfo_linefit and store the 
             * parameters in
             * an array of the FitParams data structure allParams[].
             */
            if ( (result = sinfo_new_line_fit ( lineIm, par[k], 
                            fwhm, line, col,
                            width, cenpos, min_amplitude, vline,
                            mpar, xdat, wdat ) ) < 0 )
            {
                sinfo_msg_debug ("sinfo_linefit failed, error no.: %d, "
                                "column: %d, row: %f, line: %d\n",
                                result, col, cenpos, line) ;
                continue ;
            }
            if ( (par[k] -> fit_par[0] <= 0.) || (par[k] -> fit_par[1] <= 0.)
                            || (par[k] -> fit_par[2] <= 0.) )
            {
                sinfo_msg_warning ("negative fit parameters in column: %d, "
                                "line: %d\n", col, line) ;
                continue ;
            }
            par[k] -> wavelength = wavelength[line] ;
            k++ ;
        }

    }

    /* free memory */
    sinfo_new_destroy_vector(vline);
    cpl_free(xdat);
    cpl_free(wdat);
    cpl_free(mpar);


    c = 0 ;
    for ( col = 10 ; col < 25 ; col++ )
    {
        int n = 0 ;
        for ( i = 0 ; i < k ; i++ )
        {
            if (par[i]->column == col && par[i]->fit_par[2] != 0. && 
                            par[i]->fit_par[1] > 1. && par[i]->fit_par[1] < 7. )
            {
                foundit[n] = i ;
                n++ ;
            }
        }
        if ( n == 0 ) continue ;

        shift = 0 ;
        z = 0 ;
        for ( j = 0 ; j < n ; j++ )
        {
            position = par[foundit[j]]->fit_par[2] ; 
            lambda   = par[foundit[j]]->wavelength ;
            line     = par[foundit[j]]->line ;
            if (line > 0 && line < n_lines-1)
            {
                if (fabs((wave_cor[line] - wave_cor[line-1]) / 
                                dispersion ) < 2*width ||
                                fabs((wave_cor[line] - wave_cor[line+1]) /
                                                dispersion ) < 2*width )
                {
                    continue ;
                }
            }
            wave = 0 ;
            for ( i = 0 ; i < n_fitcoeffs ; i++ ) 
            {
                wave += coeffs[i][col]*pow(position-offset, i) ;
            }
            shift += lambda - wave ;
            z++ ;
        }
        shift_col[c] = shift/(float)z ; 
        c++ ;
    }
    if ( c > 0 )
    {
        wave_shift = sinfo_new_clean_mean(shift_col, c, 10., 10.) ;
        sinfo_msg("overall positioning error in microns: %g", wave_shift) ;
    }

    /* determine positioning error for each found line */
    for ( line = 0 ; line < n_lines ; line++ )
    {
        if (line > 0 && line < n_lines-1)
        {
            if (fabs((wave_cor[line] - wave_cor[line-1]) / dispersion ) < 
                            2*width ||
                            fabs((wave_cor[line] - wave_cor[line+1]) / dispersion ) <
                            2*width )
            {
                continue ;
            }
        }

        c = 0 ;
        for ( col = 10 ; col < 25 ; col++ )
        {
            shift_col[c] = 0. ;
            found = -1 ;
            for ( i = 0 ; i < k ; i++ )
            {
                if (par[i]->column == col && par[i]->fit_par[2] != 0. && 
                                par[i]->fit_par[1] > 1. && par[i]->fit_par[1] < 7. &&
                                par[i]->line == line )
                {
                    found = i ;
                }
            }
            if (found == -1) break ;

            position = par[found]->fit_par[2] ; 
            lambda  = par[found]->wavelength ;
            wave = 0 ;
            for ( i = 0 ; i < n_fitcoeffs ; i++ ) 
            {
                wave += coeffs[i][col]*pow(position-offset, i) ;
            }
            shift_col[c] = lambda - wave ;
            c++ ;
        }
        if (found != -1 && c > 0 )
        {
            sinfo_msg("shift in microns: %g at wavelength: %f\n", 
                            sinfo_new_clean_mean(shift_col, c, 20., 20.), lambda) ;
        }
    }



    cpl_free(foundit) ;
    cpl_free(shift_col) ;
    cpl_free(wave_cor) ;
    cpl_free(a) ;
    cpl_free(zroot) ;

    return wave_shift ;
}



/**
@brief Fits each single polynomial coefficient acoefs resulting from polyfit 
       across the columns of each slitlet and use the result of this fit to 
       smooth the acoefs.
@name sinfo_new_coeffs_cross_slit_fit()
@param n_columns:    number of image columns
@param acoefs:       coeffs fitted in sinfo_polyfit
@note: this is a sinfo_matrix of the polynomial
       coefficients, each loc_index for each column:
       acoefs[loc_index][column]
@param dacoefs:      fit errors of the corresponding acoefs
@param bco:          the fitted Bcoeffs data structure for each slitlet
@param sigma_factor: factor of sigma beyond which the
                     column coefficients are discarded for the fit
@param dispersion:   estimate of the dispersion
@param pixel_dist:   estimate of minimal pixel distance in spectral direction 
                     between slitlets
@return 
           # 0 if all went o.k
           # -1 if an error occurred
           # the found fit coefficients bco data structure to calculate
           # the smoothed acoefs
           # chisq:        list of the chi squared of each fit for each slitlet
 */
static int
sinfo_new_spred_coeffs_cross_slit_fit ( int      n_columns,
                                        float ** acoefs,
                                        float ** dacoefs,
                                        Bcoeffs* bco,
                                        float    sigma_factor,
                                        float    dispersion,
                                        float    pixel_dist,
                                        float  * chisq,
                                        float ** sinfo_slit_pos )
{
    float col_index;


    float ** ucoefs, **vcoefs, **covar ;
    float * acoefsclean ;
    double sum, sumq, mean ;
    double sigma ;
    double cliphi, cliplo ;
    float offset ;
    float threshold ;

    int* edge=NULL ;
    float* sub_col_index=NULL ;
    float* sub_acoefs=NULL;
    float* sub_dacoefs=NULL ;
    float* wcoefs=NULL ;


    int ed1, ed2 ;
    int i, n, num, ndata ;
    int nc, ns ;
    int loc_index ;
    int sl_index;
    int last_i=PIXEL;

    if ( n_columns < 1 )
    {
        sinfo_msg_error(" wrong number of image columns given\n") ;
        return -1 ;
    }
    if ( acoefs == NULL || dacoefs == NULL )
    {
        sinfo_msg_error(" acoeffs or errors of coefficients are not given") ;
        return -1 ;
    }
    if ( bco == NULL )
    {
        sinfo_msg_error(" bcoeffs are not allocated\n") ;
        return -1 ;
    }
    if ( sigma_factor <= 0. )
    {
        sinfo_msg_error(" impossible sigma_factor given!\n") ;
        return -1 ;
    }
    if ( dispersion == 0. )
    {
        sinfo_msg_error(" impossible dispersion given!\n") ;
        return -1 ;
    }


    edge=cpl_calloc(bco->n_slitlets,sizeof(int)) ;
    sub_col_index=cpl_calloc(n_columns,sizeof(float)) ;
    sub_acoefs=cpl_calloc(n_columns,sizeof(float));
    sub_dacoefs=cpl_calloc(n_columns,sizeof(float)) ;

    wcoefs=cpl_calloc(bco->n_bcoeffs,sizeof(float)) ;

    /*------------------------------------------------------------------------
     * search for the slitlet edges by comparing the a0 coefficients along i
     * the columns
     * if a bigger deviation occurrs it is assumed that there is an edge.
     */
    n = 0 ;
    threshold = pixel_dist * fabs(dispersion) ;
    sinfo_slit_pos[0][0]=0 ;
    sl_index  = 0;
    /* it was for ( i = PIXEL ; i  < n_columns - PIXEL ; ) */
    for ( i = 0 ; i  < n_columns - PIXEL ; )
    {
        if ( !isnan(acoefs[0][i+1]) &&
                        acoefs[0][i+1] != 0. &&
                        acoefs[0][i] != 0.
                        && dacoefs[0][i+1] != 0.)
        {
            if ( isnan(acoefs[0][i]) || acoefs[0][i] == 0. )
            {
                if (fabs(acoefs[0][i+1] - acoefs[0][i-1]) >= threshold )
                {
                    /* printf("case a pos1 %d pos2 %d \n",i,i+1); */
                    edge[n] = i+1 ;
                    sinfo_slit_pos[sl_index][1] = i ;
                    sinfo_slit_pos[sl_index+1][0] = i + 1 ;
                    sl_index++;
                    n++ ;
                    last_i = i;
                    i += PIXEL ;
                }
            }
            else
            {
                if (fabs(acoefs[0][i+1] - acoefs[0][i]) >= threshold )
                {
                    /* printf("case b pos1 %d pos2 %d \n",i,i+1); */
                    edge[n] = i+1 ;
                    sinfo_slit_pos[sl_index][1] = i ;
                    sinfo_slit_pos[sl_index+1][0] = i + 1 ;
                    sl_index++;
                    n++ ;
                    last_i = i;
                    i += PIXEL ;
                }
            }


            /* sometimes a slitlet may be lost due to divergences in 
                acoeffs[0] we try to recover it */
            if( ( (i-last_i) > 63 ) &&
                            ( isnan(fabs(acoefs[0][i+1] - acoefs[0][i])) ||
                                            isnan(fabs(acoefs[0][i+1] - acoefs[0][i-1])) ) )
            {
                edge[n] = i+1 ;
                sinfo_slit_pos[sl_index][1] = i ;
                sinfo_slit_pos[sl_index+1][0] = i + 1 ;
                sl_index++;
                n++ ;

                last_i = i;
                sinfo_msg_warning("2 recovered slitlet edge i=%d",i);
                i += PIXEL ;

            }
        }
        i++ ;
    }
    sinfo_slit_pos[sl_index][1]  = 2047;
    /* printf("2 Found n slitlest: %d check %d\n", n,bco->n_slitlets - 1); */
    if ( n != bco->n_slitlets - 1 )
    {
        sinfo_msg_error("could not find the right number of "
                        "slitlets, found: %d\n",n+1) ;
        return -1 ;
    }

    /* go through the coefficents indices */
    for ( loc_index = 0 ; loc_index < bco->n_acoeffs ; loc_index++ )
    {
        /* go through the single slitlets */
        for ( ns = 0 ; ns < bco->n_slitlets ; ns++ )
        {
            /* determine the slitlet edges */
            if ( ns == 0 )
            {
                ed1 = 0 ;
                ed2 = edge[0] ;
            }
            else if ( ns == bco->n_slitlets - 1 )
            {
                ed1 = edge[bco->n_slitlets - 2] ;
                ed2 = n_columns ;
            }
            else
            {
                ed1 = edge[ns-1] ;
                ed2 = edge[ns] ;
            }

            nc = 0 ;
            for ( i = ed1 ; i < ed2 ; i++ )
            {
                if ( isnan(acoefs[loc_index][i]) ||
                                acoefs[loc_index][i] == 0. ||
                                dacoefs[loc_index][i] == 0. )
                {
                    continue ;
                }
                else
                {
                    nc++ ;
                }
            }
            if (NULL==(acoefsclean=(float*) cpl_calloc(nc , sizeof(float))) )
            {
                sinfo_msg_error("could not allocate memory for acoefsclean!");
                return -1 ;
            }
            nc = 0 ;
            for ( i = ed1 ; i  < ed2 ; i++ )
            {
                if ( isnan(acoefs[loc_index][i]) || 
                                acoefs[loc_index][i] == 0. ||
                                dacoefs[loc_index][i] == 0. )
                {
                    continue ;
                }
                else
                {
                    acoefsclean[nc] = acoefs[loc_index][i] ;
                    nc++ ;
                }
            }

            /* ----------------------------------------------------------
             * determine the clean mean and sigma value of the coefficients,
             * that means reject 10 % of the extreme low and high values
             */
            sinfo_pixel_qsort(acoefsclean, nc) ;

            sum   = 0. ;
            sumq  = 0. ;
            mean  = 0. ;
            sigma = 0. ;
            n     = 0 ;
            for ( i = (int)((float)nc*LOW_REJECT) ; 
                            i < (int)((float)nc*HIGH_REJECT) ; i++ )
            {
                sum  += (double)acoefsclean[i] ;
                sumq += ((double)acoefsclean[i] * (double)acoefsclean[i]) ;
                n ++ ;
            }
            mean          = sum/(double)n ;
            sigma         = sqrt( sumq/(double)n - (mean * mean) ) ;
            cliphi        = mean + sigma * (double)sigma_factor ;
            cliplo        = mean - sigma * (double)sigma_factor ;
            /* fit only the reasonnable values */
            num = 0 ;
            col_index = 0 ;
            for ( i = ed1 ; i < ed2 ; i++ )
            {
                /* take only the reasonnable coefficients */
                if ( !isnan(acoefs[loc_index][i]) && 
                                (acoefs[loc_index][i] <= cliphi) &&
                                (acoefs[loc_index][i] >= cliplo) &&
                                (dacoefs[loc_index][i] != 0. ) &&
                                (acoefs[loc_index][i] != 0.) )
                {
                    sub_acoefs[num]    = acoefs[loc_index][i] ;
                    sub_dacoefs[num]   = dacoefs[loc_index][i] ;
                    sub_col_index[num] = col_index ;
                    num ++ ;
                }
                col_index++ ;
            }
            ndata = num ;
            offset = (float)(col_index-1) / 2. ;

            if ( ndata < bco->n_bcoeffs )
            {
                sinfo_msg_error(" not enough data found in slitlet %da"
                                " to determine the fit coefficients.\n", ns) ;
                cpl_free(acoefsclean) ;
                return -1 ;
            }

            /* allocate coefficient matrices */
            ucoefs = sinfo_matrix(1, ndata, 1, bco->n_bcoeffs) ;
            vcoefs = sinfo_matrix(1, ndata, 1, bco->n_bcoeffs) ;
            covar  = sinfo_matrix(1, bco->n_bcoeffs, 1, bco->n_bcoeffs) ;

            /* scale the x-values for the fit */
            for ( i = 0 ; i < ndata ; i++ )
            {
                sub_col_index[i] = (sub_col_index[i] - offset) / offset ;
            }

            /* finally, do the singular value decomposition fit */
            sinfo_svd_fitting ( sub_col_index-1, sub_acoefs-1, 
                                sub_dacoefs-1, ndata, bco[ns].b[loc_index]-1,
                                bco->n_bcoeffs, ucoefs, vcoefs, wcoefs-1, 
                                covar, &chisq[ns], sinfo_fpol ) ;

            /* scale the found coefficients */
            for ( i = 0 ; i < bco->n_bcoeffs ; i ++ )
            {
                bco[ns].b[loc_index][i] /= pow( offset, i ) ;
            }

            /* free memory */
            cpl_free (acoefsclean) ;
            sinfo_free_matrix( ucoefs, 1/*, ndata*/, 1/*, bco->n_bcoeffs */) ;
            sinfo_free_matrix( vcoefs, 1/*, ndata*/, 1/*, bco->n_bcoeffs */) ;
            sinfo_free_matrix( covar, 1/*, bco->n_bcoeffs*/, 
                               1/*, bco->n_bcoeffs */) ;

            /* now calculate the smoothed acoefs for each column */
            col_index = 0 ;
            for ( i = ed1 ; i < ed2  ; i++ )
            {
                acoefs[loc_index][i] = 0. ;
                for ( n = 0 ; n < bco->n_bcoeffs ; n++ )
                {
                    acoefs[loc_index][i] += bco[ns].b[loc_index][n] * 
                                    pow(col_index - offset, n) ;
                }
                col_index++ ;
            }

        }
    }


    cpl_free(edge) ;
    cpl_free(sub_col_index) ;
    cpl_free(sub_acoefs);
    cpl_free(sub_dacoefs) ;
    cpl_free(wcoefs) ;

    return 0 ;
}


/**
@brief takes an image from a calibration emission lamp and delivers the 
       smoothed fit coefficients of a polynomial fit along the columns of 
       the line positions as output.
@name  sinfo_new_spred_wave_cal()
@param image:        merged image from a calibration emission lamp,
@param par:          fit parameters data structure storage
@param abuf:         buffer array for fit coefficients abuf[index][column]
@param row_clean:    resulting list of the row indices but without the lines 
                     that are too close to 
                     each other for the fit output of sinfo_findLines()
@param wavelength_clean: corrected wavelength list corresponding to the 
                         row_clean array
                         output of sinfo_findLines()
@param n_found_lines: output of sinfo_findLines(): total number of found 
                      emission lines
@param dispersion:   dispersion of spectrum: micron per pixel
@param halfWidth:    half width of the box where the line must sit
@param minAmplitude: minimum amplitude of the Gaussian to do the fit
@param max_residual: maximum residual value, beyond that value
                     the polynomial lambda-position fit is rejected.
@param fwhm:         first guess for the full width of half maximum
                     of the sinfo_gaussian line fit
@param n_a_fitcoefs: number of fit coefficients for the single
                     column fits: lambda-position
@param n_b_fitcoefs: number of fit coefficients for the fits of
                     the single a coefficients across the columns
@param sigmaFactor:  factor of the standard deviation of the determined
                     polynomial coefficients of the columns beyond
                     which these coefficients are not used to carry out
                     the polynomial fit across the columns.
@param pixel_dist:   estimate of minimal pixel distance in spectral direction 
                     between slitlets
@param pixel_tolerance: maximum tolerated difference between estimated and 
                        fitted line positions.
@return # wavelength map image
        # abuf: array of smoothed coefficients of the polynomial fit along 
                the columns:
                              abuf[index][column].
        # par: array of the resulting FitParams data structure
@doc this routine takes an image from a calibration emission lamp and 
     delivers the smoothed fit coefficients of a polynomial fit along the 
     columns of the line positions as output.
     This routine expects Nyquist sampled spectra (either an interleaved 
     image or an image 
     convolved with an appropriate function in spectral direction)
 */

cpl_image * sinfo_new_spred_wave_cal( cpl_image   * image,
                                      FitParams ** par ,
                                      float     ** abuf,
                                      int          n_slitlets,
                                      int       ** row_clean,
                                      float     ** wavelength_clean,
                                      int        * n_found_lines,
                                      float        dispersion,
                                      int          halfWidth,
                                      float        minAmplitude,
                                      float        max_residual,
                                      float        fwhm,
                                      int          n_a_fitcoefs,
                                      int          n_b_fitcoefs,
                                      float        sigmaFactor,
                                      float        pixel_dist,
                                      float        pixel_tolerance,
                                      float ** sinfo_slit_pos)


{
    int          i, j;
    /* int k ; */
    int          n_fit ;
    int          n_reject ;
    float     *  acoefs ;
    float     *  dacoefs ;
    float     ** dabuf ;

    float     *  chisq_cross ;

    int          crossInd ;
    Bcoeffs   *  bco ;
    cpl_image  *  wavemap ;
    int ilx=0;
    int ily=0;
    /* float* pidata=NULL; */


    if (  NULL == image )
    {
        sinfo_msg_error(" no image given\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(image);
    ily=cpl_image_get_size_y(image);
    /* pidata=cpl_image_get_data_float(image); */

    if ( par == NULL )
    {
        sinfo_msg_error(" no fit parameter data structure given\n") ;
        return NULL ;
    }
    if ( abuf == NULL )
    {
        sinfo_msg_error(" no buffer for fit coefficients given\n") ;
        return NULL ;
    }
    if ( n_slitlets <= 0 )
    {
        sinfo_msg_error(" impossible number of slitlets given\n") ;
        return NULL ;
    }
    if ( row_clean == NULL )
    {
        sinfo_msg_error(" no row_clean array given\n") ;
        return NULL ;
    }
    if ( wavelength_clean == NULL )
    {
        sinfo_msg_error(" no wavelength_clean array given\n") ;
        return NULL ;
    }

    if ( dispersion == 0. )
    {
        sinfo_msg_error(" impossible dispersion given\n") ;
        return NULL ;
    }

    if ( halfWidth <= 0 || halfWidth > ily/2 )
    {
        sinfo_msg_error(" impossible half width of the fitting box given\n") ;
        return NULL ;
    }
    if ( minAmplitude < 1. )
    {
        sinfo_msg_error(" impossible minimal amplitude\n") ;
        return NULL ;
    }

    if ( max_residual <= 0. || max_residual > 1. )
    {
        sinfo_msg_error(" impossible max_residual given\n") ;
        return NULL ;
    }
    if ( fwhm <= 0. || fwhm > 10. )
    {
        sinfo_msg_error(" impossible fwhm given\n") ;
        return NULL ;
    }

    if ( n_a_fitcoefs <= 0 || n_a_fitcoefs > 9 )
    {
        sinfo_msg_error(" unrealistic n_a_fitcoefs given\n") ;
        return NULL ;
    }

    if ( n_b_fitcoefs <= 0 || n_b_fitcoefs > 9 )
    {
        sinfo_msg_error(" unrealistic n_b_fitcoefs given\n") ;
        return NULL ;
    }
    if ( sigmaFactor <= 0. )
    {
        sinfo_msg_error(" impossible sigmaFactor given\n") ;
        return NULL ;
    }

    /* initialize the variables */
    n_reject = 0 ;
    n_fit = 0 ;

    /* fit each found line by using a sinfo_gaussian function and
        determine the exact position */
    if ( 0 > (n_fit = sinfo_new_fit_lines( image , par, fwhm, n_found_lines,
                    row_clean, wavelength_clean,
                    halfWidth, minAmplitude )) )
    {
        sinfo_msg_error(" cannot fit the lines, error code of "
                        "sinfo_fitLines: %d", n_fit) ;
        return NULL ;
    }

    /* first check for faked lines like bad pixels */
    if ( -1 == sinfo_new_check_for_fake_lines (par,
                    dispersion,
                    wavelength_clean,
                    row_clean,
                    n_found_lines,
                    ilx,
                    pixel_tolerance) )
    {
        sinfo_msg_error(" cannot fit the lines, error code of "
                        " sinfo_fitLines: %d\n", n_fit) ;
        return NULL ;
    }


    /* allocate memory */
    if (NULL==(acoefs = (float*) cpl_calloc (n_a_fitcoefs, sizeof(float))) ||
                    NULL==(dacoefs = (float*) cpl_calloc (n_a_fitcoefs, sizeof(float))) ||
                    NULL==(dabuf = (float**) cpl_calloc (n_a_fitcoefs, sizeof(float*))) ||
                    NULL==(chisq_cross = (float*) cpl_calloc(n_slitlets, sizeof(float))) )
    {
        sinfo_msg_error(" cannot allocate memory\n") ;
        return NULL ;
    }
    for ( i = 0 ; i < n_a_fitcoefs ; i++ )
    {
        if (  NULL == (dabuf[i] = (float*) cpl_calloc(ilx, sizeof(float))) )
        {
            sinfo_msg_error(" cannot allocate memory\n") ;
            cpl_free ( acoefs ) ;
            cpl_free ( dacoefs ) ;
            cpl_free ( chisq_cross ) ;
            cpl_free(dabuf) ;
            return NULL ;
        }
    }

    /* fit wavelengths to the corresponding found positions for each column */
    /* k = 0 ; */
    for ( i = 0 ; i < ilx ; i++ )
    {
        int zeroind = 0 ;
        float chisq_poly;
        if ( FLT_MAX == (chisq_poly = sinfo_new_polyfit( par, i,
                        n_found_lines[i],
                        ily, dispersion,
                        max_residual, acoefs,
                        dacoefs, &n_reject,
                        n_a_fitcoefs)) )
        {
            sinfo_msg_warning (" error in sinfo_polyfit in column: %d\n", i) ;
            for ( j = 0 ; j < n_a_fitcoefs ; j++ )
            {
                acoefs[j] = ZERO ;
                dacoefs[j] = ZERO ;
            }
        }

        for ( j = 0 ; j < n_a_fitcoefs ; j++ )
        {

            if ( acoefs[0] <= 0. || acoefs[1] ==0. ||
                            dacoefs[j] == 0. || isnan(acoefs[j]) )
            {
                zeroind = 1 ;
            }
        }
        for ( j = 0 ; j < n_a_fitcoefs ; j++ )
        {
            if ( zeroind == 0 )
            {
                abuf[j][i]  = acoefs[j] ;
                dabuf[j][i] = dacoefs[j] ;
            }
            else
            {
                abuf[j][i]  = ZERO ;
                dabuf[j][i] = ZERO ;
            }
        }
    }

    /* allocate memory for the fitting coefficients */
    if ( NULL == ( bco = sinfo_new_b_coeffs( n_slitlets,
                    n_a_fitcoefs, n_b_fitcoefs)) )
    {
        sinfo_msg_error (" cannot allocate memory for the bcoeffs\n") ;
        for ( i = 0 ; i < n_a_fitcoefs ; i++ )
        {
            cpl_free (dabuf[i]) ;
        }
        cpl_free (dabuf) ;
        cpl_free ( acoefs ) ;
        cpl_free ( dacoefs ) ;
        cpl_free ( chisq_cross ) ;
        return NULL ;
    }

    /* fit each acoefs across the slitlets to smooth the result */
    if ( -1 == ( crossInd = sinfo_new_spred_coeffs_cross_slit_fit( ilx, abuf,
                    dabuf,
                    bco, sigmaFactor,
                    dispersion,
                    pixel_dist,
                    chisq_cross,
                    sinfo_slit_pos )) )
    {
        sinfo_msg_error (" cannot carry out the fitting of "
                        "coefficients across the columns\n") ;
        for ( i = 0 ; i < n_a_fitcoefs ; i++ )
        {
            cpl_free (dabuf[i]) ;
        }

        cpl_free (dabuf) ;
        cpl_free ( acoefs ) ;
        cpl_free ( dacoefs ) ;
        sinfo_new_destroy_b_coeffs(bco) ;
        cpl_free ( chisq_cross ) ;
        return NULL ;
    }

    if ( NULL == (wavemap = sinfo_new_wave_map_slit (abuf, n_a_fitcoefs,
                    ily, ilx)))
    {
        sinfo_msg_error (" cannot carry out wavemap creation\n") ;
        for ( i = 0 ; i < n_a_fitcoefs ; i++ )
        {
            cpl_free (dabuf[i]) ;
        }

        cpl_free (dabuf) ;
        cpl_free ( acoefs ) ;
        cpl_free ( dacoefs ) ;
        sinfo_new_destroy_b_coeffs(bco) ;
        cpl_free ( chisq_cross ) ;
        return NULL ;
    }

    /* free all allocated memory */
    for ( i = 0 ; i < n_a_fitcoefs ; i++ )
    {
        cpl_free (dabuf[i]) ;
    }
    cpl_free (dabuf) ;
    cpl_free ( acoefs ) ;
    cpl_free ( dacoefs ) ;
    sinfo_new_destroy_b_coeffs(bco) ;
    cpl_free ( chisq_cross ) ;

    return wavemap ;
}

/**@}*/
/*___oOo___*/
