/*******************************************************************************
 * JMMC project ( http://www.jmmc.fr ) - Copyright (C) CNRS.
 ******************************************************************************/
/**
 * @file
 * Functions to compute the instantaneous correlated fluxes, pistons, 
 * photometries and fringe quality estimator, and to derive bin-averaged
 * values of normalised visibilities, debiassed squared visibilities, 
 * triple products. Additionally contains routines to estimate noise-averaged
 * values of these multidimensional values.
 */

#define _POSIX_SOURCE 1

/*
 * System Headers
 */
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "fitsio.h"

/*
 * Local Headers
 */
#include "amdlib.h"
#include "amdlibProtected.h"

/** the DATE part  of an ISO 8601 Date+Time format*/
#define amdlibDATE_LENGTH 10 
/** The limit for the chi square rejection */
#define amdlibFILTER_CHISQUARE_LIMIT 0
/** List of instrument configuration keywords which influence the P2VM */
static char *amdlibP2vmInsCfgKeywList[] = {
    "HIERARCH ESO INS OPTI2 NAME", 
    "HIERARCH ESO INS GRIS1 NAME",
    "HIERARCH ESO INS GRIS2 NAME", 
    "HIERARCH ESO INS GRAT1 NAME",
    "HIERARCH ESO INS GRAT1 ORDER", 
    "HIERARCH ESO INS GRAT1 WLEN",
    NULL};

/* Local functions */
amdlibCOMPL_STAT amdlibComputeUVCoords(amdlibSCIENCE_DATA *data,
                                       int nbBases,
                                       amdlibDOUBLE **uCoord,
                                       amdlibDOUBLE **vCoord);

amdlibCOMPL_STAT amdlibTagPiston(amdlibPISTON   *instantOpd,
                                 double          threshold,
                                 double          errThreshold,
                                 amdlibBAND      band);
amdlibCOMPL_STAT amdlibClosePiston(amdlibPISTON *instantOpd,
                                   double        threshold,
                                   amdlibBAND    band);

double amdlibAbacusErrPhi(double x);

void amdlibFilterByChiSquare(amdlibVIS *instantCorrFlux, double **chisq, double range);

static amdlibCOMPL_STAT amdlibComputeChiSquare(/* Input */
                                       amdlibVIS       *instantCorrFlux,
                                       double          *p2v,
                                       unsigned char   *badp2v,
                                       double          *mk,
                                       double          *sigma2_mk,
                                       int             iFrame,
                                       int             nbLVis,
                                       int             nbPix,
                                       int             nbBases,
                                       /* Output */
                                       double          **chisq,
                                       amdlibERROR_MSG errMsg);

static amdlibCOMPL_STAT amdlibCrossP2vmAndData(/* Input */
                                               amdlibP2VM_MATRIX  *p2vm,
                                               amdlibSCIENCE_DATA *data,
                                               amdlibWAVEDATA     *waveData,
                                               double             *fudge,
                                               int                iFrame,
                                               int                *channelNo,
                                               int                nbChannels,
                                               /* Output */
                                               amdlibWAVELENGTH   *wave,
                                               double             *p2vCkDk,
                                               unsigned char     *badPixelFlags,
                                               double             *mk,
                                               double             *sigma2_mk,
                                               double             *cova_mk,
                                               double             *ikMean,
                                               double             *sigma2_ikMean,
                                               amdlibERROR_MSG    errMsg);

static amdlibCOMPL_STAT amdlibGetInstantPhotometry(/* Input */
                                               amdlibP2VM_MATRIX  *p2vm,
                                               amdlibSCIENCE_DATA *data,
                                               double             *fudge,
                                               int                *channelNo,
                                               int                nbChannels,
                                               /* Output */
                                               amdlibPHOTOMETRY   *photometry,
                                               amdlibERROR_MSG    errMsg);

static amdlibCOMPL_STAT amdlibCrossGeneralizedP2vmAndData(/* Input */
                                               amdlibP2VM_MATRIX  *p2vm,
                                               amdlibSCIENCE_DATA *data,
                                               int                *channelNo,
                                               int                nbChannels,
                                               /* Output */
                                               double             *gp2vCkDk,
                                               unsigned char     *badPixelFlags,
                                               double             *ikMean,
                                               double             *sigma2_ikMean,
                                               amdlibERROR_MSG    errMsg);

static amdlibCOMPL_STAT amdlibInvertGeneralizedP2v(/* Input */
                                        double          *gp2v,
                                        unsigned char   *badp2v,
                                        double          *sigma2_ikMean,
                                        int             nbLVis,
                                        int             nbPix,
                                        int             nbBases,
                                        /* Output */
                                        double          *gv2p,
                                        amdlibERROR_MSG errMsg);
static amdlibCOMPL_STAT amdlibInvertP2v(/* Input */
                                        double          *p2v,
                                        unsigned char   *badp2v,
                                        double          *cova_mk,
                                        int             nbLVis,
                                        int             nbPix,
                                        int             nbBases,
                                        /* Output */
                                        double          *v2p,
                                        amdlibERROR_MSG errMsg);

static amdlibCOMPL_STAT amdlibComputeCorrFlux(/* Input */
                                              double                *v2p,
                                              double                *mk,
                                              double                *sigma2_mk,
                                              int                   nbFrames,
                                              int                   nbLVis,
                                              int                   nbPix,
                                              int                   nbBases,
                                              /* Output */
                                              amdlibVIS             *instantCorrFlux,
                                              amdlibERROR_MSG       errMsg);
static amdlibCOMPL_STAT amdlibComputeFudgeFactor(/* Input */
                                              double                *gv2p,
                                              double                *ikMean,
                                              int                   nbLVis,
                                              int                   nbPix,
                                              int                   nbBases,
                                              /* Output */
                                              double                *fudge,
                                              amdlibERROR_MSG       errMsg);
static amdlibCOMPL_STAT amdlibMeanCpxVis(
                                         amdlibVIS         *instantCorrFlux,
                                         amdlibPHOTOMETRY  *photometry,
                                         int               firstFrame,
                                         int               nbFrames,
                                         int               iBin,
                                         amdlibVIS         *meanVis);

static amdlibCOMPL_STAT amdlibSumPhotometry(
                                            amdlibPHOTOMETRY *instantPhotometry,
                                            int              firstFrame,
                                            int              nbFrames,
                                            int               iBin,
                                            amdlibPHOTOMETRY  *sumPhotometry);

static void amdlibComputeFringeCriterion(/* Input */
                                         amdlibVIS       *instantCorrFlux,
                                         int             iBin,
                                         amdlibBAND      band,
                                         amdlibSELECTION *selection,
                                         /* Output */
                                         amdlibVIS       *vis);
static void amdlibBinFringeCriterion(/* Input */
                                     amdlibVIS       *instantCorrFlux,
                                     int             firstFrame,
                                     int             nbFrames,
                                     int             iBin,
                                     amdlibBAND      band,
                                     /* Output */
                                     amdlibVIS       *vis);

static void amdlibAverageVis2(amdlibVIS2 *vis2);

static amdlibCOMPL_STAT amdlibComputeSqVis(/* Input */
                                           amdlibVIS          *instantCorrFlux,
                                           amdlibPHOTOMETRY   *photometry,
                                           int                iBin,
                                           amdlibBAND         band,
                                           amdlibSELECTION    *selection,
                                           amdlibERROR_TYPE   errorType,
                                           /* Output */
                                           amdlibVIS2         *vis2,
                                           amdlibERROR_MSG    errMsg);

static amdlibCOMPL_STAT amdlibBinSqVis(/* Input */
                                       amdlibVIS          *instantCorrFlux,
                                       amdlibPHOTOMETRY   *photometry,
                                       int                firstFrame,
                                       int                nbFrames,
                                       int                iBin,
                                       amdlibBAND         band,
                                       amdlibERROR_TYPE   errorType,
                                       /* Output */
                                       amdlibVIS2         *vis2,
                                       amdlibERROR_MSG    errMsg);

static amdlibCOMPL_STAT amdlibComputeDiffVis(/* Input */
                                       amdlibVIS              *instantCorrFlux,
                                       amdlibWAVELENGTH       *wave,
                                       amdlibPISTON           *instantOpd,
                                       int                    iBin,
                                       amdlibBAND             band,
                                       amdlibSELECTION        *selection,
                                       /* Output */
                                       amdlibVIS              *vis,
                                       amdlibERROR_MSG        errMsg);
static amdlibCOMPL_STAT amdlibBinDiffVis(/* Input */
                                       amdlibVIS              *instantCorrFlux,
                                       amdlibWAVELENGTH       *wave,
                                       amdlibPISTON           *instantOpd,
                                       int                    firstFrame,
                                       int                    nbFrames,
                                       int                    iBin,
                                       amdlibBAND             band,
                                       /* Output */
                                       amdlibVIS              *vis,
                                       amdlibERROR_MSG        errMsg);

static amdlibCOMPL_STAT amdlibAverageVis(/* Input */
                                    amdlibVIS              *instantCorrFlux,
                                    int                    iBin,
                                    amdlibBAND             band,
                                    amdlibSELECTION        *selection,
                                    /* Output */
                                    amdlibVIS              *vis,
                                    amdlibERROR_MSG        errMsg);

static amdlibCOMPL_STAT amdlibGetThreshold(double          *array,
                                           int             arraySize,
                                           double          percentage,
                                           double          *threshold,
                                           amdlibERROR_MSG errMsg);

static amdlibCOMPL_STAT 
amdlibCorrectFromPistonExtenuation(amdlibVIS        *instantCorrFlux,
                                   amdlibPISTON     *instantOpd,
                                   amdlibWAVELENGTH *wave,
                                   amdlibBAND        band,
                                   double            R,
                                   amdlibERROR_MSG   errMsg);
/*
 * Public functions
 */
/**
 * @warning obsolete method. Please use amdlibComputeVisibilities.
 * 
 * @param data structure containing the science observation (where the fringes 
 * are)
 * @param p2vm structure where P2VM is stored
 * @param waveData the wavelength definition structure
 * @param band the spectral band to select in the observations 
 * @param binning the number of consecutives frames in the science observation
 * that will be used to measure a single point of visibilties in output.
 * the total time span of the frames in a bin should be of the order of
 * the coherence time of the atmosphere, unless the observation has 
 * benefitted of a fringe tracker device. At Paranal coherence time are 
 * rarely more than a few Millisecond. 
 * @param errorType indicates wether the noise figures are estimated 
 * statistically from the sequence of photometries during the bin 
 * (amdlibSTATISTICAL_ERROR) or theoretically by the poisson error on N photons
 * (amdlibTHEORETICAL_ERROR). The latter is forced obviously when the binsize
 * is 1.
 * @param pistonType indicates whether the piston is to be measured by fitting
 * a slope in the phasors amdlibITERATIVE_PHASOR or in the differential phases
 * after dewrapping (amdlibUNWRAPPED_PHASE). The latter is deprecated and
 * is not maintained anymore.
 * @param noCheckP2vmId forces amdlib to use without question the passed p2vm,
 * even if its magic number is not OK. Can happen if the P2VM has been 
 * observed AFTER the science observations. 
 * @param photometry output structure containing the (binned) photometry 
 * results.
 * @param vis structure containing the (binned) raw visibilities.
 * @param vis2 structure containing the (binned) squared visibilities.
 * @param vis3 structure containing the (binned) triple products.
 * @param wave structure containing the wavelengths corresponding to the other
 * output structures. 
 * @param opd structure containing the (binned) piston measurements.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * the number of valid wavelengths for input band on successful completion.
 * Otherwise -1 is returned.
 */
int amdlibExtractVisibilities(/* Input */
                              amdlibSCIENCE_DATA     *data,
                              amdlibP2VM_MATRIX      *p2vm,
                              amdlibWAVEDATA         *waveData,
                              amdlibBAND             band,
                              int                    binning,
                              amdlibERROR_TYPE       errorType,
                              amdlibPISTON_ALGORITHM pistonType,
                              amdlibBOOLEAN         noCheckP2vmId,
                              /* Output */
                              amdlibPHOTOMETRY       *photometry,
                              amdlibVIS              *vis,
                              amdlibVIS2             *vis2,
                              amdlibVIS3             *vis3,
                              amdlibWAVELENGTH       *wave,
                              amdlibPISTON           *opd,
                              amdlibERROR_MSG        errMsg)
{
    amdlibCPT_VIS_OPTIONS visOpt = {binning, errorType, pistonType, 
        noCheckP2vmId, amdlibNO_FRAME_SEL, 1};

    amdlibLogTrace("amdlibExtractVisibilities()");

    amdlibLogWarning("amdlibExtractVisibilities is obsolete. Please use "
                     "amdlibComputeVisibilities");
    
    return (amdlibComputeVisibilities(data, p2vm, waveData, band, &visOpt, 
                                      photometry, vis, vis2, vis3, wave,
                                      opd, errMsg));
}


/**
 * main entry point, gathers all strutures in input and output,
 * merely calls amdlibComputeVisibilitiesFor()
 *
 * @param data structure containing the science observation (where the fringes 
 * are)
 * @param p2vm structure where P2VM is stored
 * @param waveData the wavelength definition structure
 * @param band the spectral band to select in the observations 
 * @param visOptions information necessary for visibilities computation
 * (binning, noise and piston computation, requirement about p2vm use and frame
 * selection). 
 * @param photometry output structure containing the (binned) photometry 
 * results.
 * @param vis structure containing the (binned) raw visibilities.
 * @param vis2 structure containing the (binned) squared visibilities.
 * @param vis3 structure containing the (binned) triple products.
 * @param wave structure containing the wavelengths corresponding to the other
 * output structures. 
 * @param opd structure containing the (binned) piston measurements.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * the number of valid wavelengths for input band on successful completion.
 * Otherwise -1 is returned.
 */
int amdlibComputeVisibilities(/* Input */
                              amdlibSCIENCE_DATA     *data,
                              amdlibP2VM_MATRIX      *p2vm,
                              amdlibWAVEDATA         *waveData,
                              amdlibBAND             band,
                              amdlibCPT_VIS_OPTIONS  *visOptions,
                              /* Output */
                              amdlibPHOTOMETRY       *photometry,
                              amdlibVIS              *vis,
                              amdlibVIS2             *vis2,
                              amdlibVIS3             *vis3,
                              amdlibWAVELENGTH       *wave,
                              amdlibPISTON           *opd,
                              amdlibERROR_MSG        errMsg)
{
    int nbChannelsInBand = 0;
    int channelNoForBand[amdlibNB_SPECTRAL_CHANNELS];
    amdlibSELECTION selectedFrames;
    int i, base;
    amdlibBOOLEAN noVis3 = amdlibFALSE;

    amdlibLogTrace("amdlibComputeVisibilities()");

    if ((visOptions->binSize != 1) && 
        (visOptions->frameSelectionType != amdlibNO_FRAME_SEL))
    {
        amdlibLogError("Binning AND frame selection are not compatible");
        return 0;
    }
    
    /* Search for the number of valid (considering p2vm flags) spectral channels
     * for specified band */
    if (amdlibGetChannelsInBand(p2vm, data, waveData, band, 
                                &nbChannelsInBand, 
                                channelNoForBand) != amdlibSUCCESS)
    {
        return -1;
    }

    if (nbChannelsInBand == 0)
    {
        return 0;
    }

    /* Compute visibilities for these valid spectral channels. */
    if (amdlibComputeVisibilitiesFor(data, p2vm, waveData, 
                                     channelNoForBand, nbChannelsInBand, 
                                     visOptions->binSize, 
                                     visOptions->errorType, 
                                     visOptions->pistonType, 
                                     visOptions->noCheckP2vmId, 
                                     photometry, vis, vis2, vis3, 
                                     wave, opd, errMsg) != amdlibSUCCESS)
    {
        return -1;
    }

    /* If required, perform frame selection */
    if (visOptions->frameSelectionType != amdlibNO_FRAME_SEL)
    {
        amdlibLogInfo("Performing Selection...");
        if (amdlibAllocateSelection(&selectedFrames, vis->nbFrames,
                                    vis->nbBases, errMsg) != amdlibSUCCESS)
        {
            return -1;
        }

        /* Select 'good' frames depending on selection criterion */
        if (amdlibSelectFrames(vis, photometry, opd,
                               visOptions->frameSelectionType, 
                               visOptions->frameSelectionRatio, 
                               &selectedFrames, 
                               band, errMsg) != amdlibSUCCESS)
        {
            return -1;
        }

        /* Average visibilities, photometries and pistons on good frames */
        if (amdlibAverageVisibilities(photometry,
                                      vis, vis2, vis3, 
                                      opd, band, wave,
                                      &selectedFrames, errMsg) != amdlibSUCCESS)
        {
            return -1;
        }

        for (base = 0; base < vis->nbBases; base++)
        {
            if (selectedFrames.band[band].nbSelectedFrames[base] == 0)
            {
                /* No frames are selected for that base, so flag them */
                for (i=0; i < vis->nbFrames; i++)
                {
                    memset(vis->table[i*vis->nbBases+base].flag,
                           amdlibTRUE, 
                           vis->nbWlen * sizeof(amdlibBOOLEAN));
                    memset(vis2->table[i*vis2->nbBases+base].flag,
                           amdlibTRUE, 
                           vis2->nbWlen * sizeof(amdlibBOOLEAN));
                }
                noVis3 = amdlibTRUE;
            }
        }
        if ((vis3 != NULL) && (noVis3 == amdlibTRUE))
        {
            for (i=0; i < vis3->nbClosures * vis3->nbFrames; i++)
            {
                memset(vis3->table[i].flag, amdlibTRUE, 
                       vis3->nbWlen * sizeof(amdlibBOOLEAN));
            }
        }

        amdlibReleaseSelection(&selectedFrames);
    }

    return nbChannelsInBand;    
}

/**
 * Select frames according to frame selection criterion chosen.
 *
 * @param vis input visibility structure.
 * @param phot input photometry structure.
 * @param opd input piston structure.
 * @param frameSelectionType frame criterion to apply.
 * @param frameSelectionRatio percentage of good frames to keep or, directly,
 * threshold corresponding to the chosen criterion.
 * @param selectedFrames output structure containing a description of selected
 * frames.
 * @param band the current spectral band treated.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibSelectFrames(amdlibVIS             *vis,
                                    amdlibPHOTOMETRY      *phot,
                                    amdlibPISTON          *opd,
                                    amdlibFRAME_SELECTION frameSelectionType,
                                    double                frameSelectionRatio,
                                    amdlibSELECTION       *selectedFrames,
                                    amdlibBAND            band,
                                    amdlibERROR_MSG       errMsg)
{
    int iFrame, iBase, iWlen, index, index2;
    amdlibVIS_TABLE_ENTRY **visTablePtr = NULL;
    amdlibPHOTOMETRY_TABLE_ENTRY **photTablePtr = NULL;
    amdlibDOUBLE                        **opdTablePtr = NULL;
    
    double *tmpArray[amdlibNBASELINE];
    double threshold[amdlibNBASELINE];
    amdlibBOOLEAN select[amdlibNBASELINE] = {amdlibTRUE, amdlibTRUE, amdlibTRUE};

    amdlibLogTrace("amdlibSelectFrames()");

    if ((frameSelectionType == amdlibFRG_CONTRAST_SEL_PCG) ||
        (frameSelectionType == amdlibFRG_CONTRAST_SEL_THR))
    {
        /* Wrap vis table */
        visTablePtr = 
            (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(vis->table,
                                            vis->nbBases, vis->nbFrames,
                                            sizeof(amdlibVIS_TABLE_ENTRY),
                                            errMsg);
        if (visTablePtr == NULL)
        {
            return amdlibFAILURE;
        }
        /* Allocate memory for temporary arrays */
        for (iBase = 0; iBase < vis->nbBases; iBase++)
        {
            tmpArray[iBase] = calloc(selectedFrames->band[band].nbSelectedFrames[iBase], 
                                    sizeof(double));
            if (tmpArray[iBase] == NULL)
            {
                amdlibSetErrMsg("Could not allocate memory for temporary buffer");
                return amdlibFAILURE;
            }
        }
        
        /* Loop on frames, copy fringe contrast snr in a temporary array for
         * each baseline ... */
        for (iBase = 0; iBase < vis->nbBases; iBase++)
        {
            index = 0;
            for (iFrame = 0; iFrame < vis->nbFrames; iFrame++)
            {
                if (selectedFrames->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE)
                {
                    tmpArray[iBase][index] = 
                        visTablePtr[iFrame][iBase].frgContrastSnrArray[band];
                    index++;
                }
            }
        }
        
	/* Set threshold */
        if (frameSelectionType == amdlibFRG_CONTRAST_SEL_THR)
        {
            for (iBase = 0; iBase < vis->nbBases; iBase++)
            {
                threshold[iBase] = frameSelectionRatio;
            }
        }
        else
        {
            /* Get threshold associated to the frameSelectionRatio 
             * by sorting the temporary array */        
            for (iBase = 0; iBase < vis->nbBases; iBase++)
            {
                if (amdlibGetThreshold(tmpArray[iBase], 
                                       selectedFrames->band[band].nbSelectedFrames[iBase], 
                                       frameSelectionRatio, 
                                       &threshold[iBase], 
                                       errMsg) != amdlibSUCCESS)
                {
                    amdlibLogWarning("Could not get threshold associated to "
                                     "the frameSelectionRatio");
                    amdlibLogWarningDetail(errMsg);
                    amdlibLogWarning("Could not perform frame selection on "
                                     "baseline %d in band %c",
                                     iBase+1, amdlibBandNumToStr(band));
                    select[iBase] = amdlibFALSE; 
                }
            }
        }
        /* Loop on frames and stamp the bad ones (criterion lower than 
         * threshold) */
        for (iBase = 0; iBase < vis->nbBases; iBase++)
        {
            if (select[iBase] == amdlibTRUE)
            {
                index = 0;
                for (iFrame = 0; iFrame < vis->nbFrames; iFrame++)
                {
                    if (selectedFrames->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE)
                    {
                        if (tmpArray[iBase][index] < threshold[iBase])
                        {
                            selectedFrames->band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE;
                            selectedFrames->band[band].nbSelectedFrames[iBase]--;
                        }
                        index++;
                    }
                }
            }
	    /* In the case no selection was possible, set all frames to false */
	    else
	    {
                for (iFrame = 0; iFrame < vis->nbFrames; iFrame++)
                {
		    selectedFrames->band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE;
		}
		selectedFrames->band[band].nbSelectedFrames[iBase] = 0;
	    }
        }
        for (iBase = 0; iBase < vis->nbBases; iBase++)
        {
            free(tmpArray[iBase]);
        }
        amdlibFree2DArrayWrapping((void **)visTablePtr);
    }
    else if ((frameSelectionType == amdlibFLUX_SEL_PCG) ||
             (frameSelectionType == amdlibFLUX_SEL_THR))
    {
        /* Wrap phot table */
        photTablePtr = 
            (amdlibPHOTOMETRY_TABLE_ENTRY **)amdlibWrap2DArray(phot->table,
                                        phot->nbBases, phot->nbFrames,
                                        sizeof(amdlibPHOTOMETRY_TABLE_ENTRY),
                                        errMsg);
        if (photTablePtr == NULL)
        {
            return amdlibFAILURE;
        }
        /* Allocate memory for temporary arrays */
        for (iBase = 0; iBase < phot->nbBases; iBase++)
        {
            tmpArray[iBase] = calloc(selectedFrames->band[band].nbSelectedFrames[iBase], 
                                    sizeof(double));
            if (tmpArray[iBase] == NULL)
            {
                amdlibSetErrMsg("Could not allocate memory for temporary buffer");
                return amdlibFAILURE;
            }
        }

        /* Loop on frames, insert flux criterion in the temporary array 
         * for each baseline. Flux criterion is here 
         * sqrt(Pi*Pj)/signedsqrt(fluxPi+fluxPj+npix*RON^2)
         * (summed over spectrum = all lambdas) 
         */

        for (iBase = 0; iBase < phot->nbBases; iBase++)
        {
            index = 0;
            for (iFrame = 0; iFrame < phot->nbFrames; iFrame++)
            {
                if (selectedFrames->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE)
                {
		    index2=0;
                    for (iWlen = 0; iWlen < phot->nbWlen; iWlen++)
		      {
			if (!amdlibCompareDouble(
			    photTablePtr[iFrame][iBase].sigma2FluxSumPiPj[iWlen], 
			    amdlibBLANKING_VALUE))
                        {
			    tmpArray[iBase][index] += 
                            amdlibSignedSqrt(photTablePtr[iFrame][iBase].PiMultPj[iWlen])
                            / amdlibSignedSqrt(photTablePtr[iFrame][iBase].sigma2FluxSumPiPj[iWlen]);
			    index2++;
			}
		    }
		    if (index2>0) 
		      {
			tmpArray[iBase][index] /= sqrt(index2);
		      }
		    else
		      {
			tmpArray[iBase][index] = amdlibBLANKING_VALUE; /*negative: dont count*/
		      }
		    index++;
                }
            }
        }

        
        if (frameSelectionType == amdlibFLUX_SEL_THR)
        {
            for (iBase = 0; iBase < phot->nbBases; iBase++)
            {
                threshold[iBase] = frameSelectionRatio;
            }
        }
        else
        {
            /* Sort these temporary arrays and get associated thresholds */
            for (iBase = 0; iBase < phot->nbBases; iBase++)
            {
                if (amdlibGetThreshold(tmpArray[iBase], 
                                       selectedFrames->band[band].nbSelectedFrames[iBase], 
                                       frameSelectionRatio, 
                                       &threshold[iBase], 
                                       errMsg) != amdlibSUCCESS)
                {
                    amdlibLogWarning("Could not get threshold associated to "
                                     "the frameSelectionRatio");
                    amdlibLogWarningDetail(errMsg);
                    amdlibLogWarning("Could not perform frame selection on "
                                     "baseline %d in band %c",
                                     iBase+1, amdlibBandNumToStr(band));
                    select[iBase] = amdlibFALSE; 
                }
            }
        }
        /* Loop on frames and stamp the bad ones ( criterion lower than 
         * threshold) */
        for (iBase = 0; iBase < phot->nbBases; iBase++)
        {
            if (select[iBase] == amdlibTRUE)
            {
                index = 0;
                for (iFrame = 0; iFrame < phot->nbFrames; iFrame++)
                {
                    if (selectedFrames->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE)
                    {
                        if (tmpArray[iBase][index] < threshold[iBase])
                        {
                            selectedFrames->band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE;
                            selectedFrames->band[band].nbSelectedFrames[iBase]--;
                        }
                        index++;
                    }
                }
            }
	    /* In the case no selection was possible, set all frames to false */
	    else
	    {
                for (iFrame = 0; iFrame < phot->nbFrames; iFrame++)
                {
		    selectedFrames->band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE;
		}
		selectedFrames->band[band].nbSelectedFrames[iBase] = 0;
	    }
        }
        for (iBase = 0; iBase < phot->nbBases; iBase++)
        {
            free(tmpArray[iBase]);
        }
        amdlibFree2DArrayWrapping((void **)photTablePtr);
    }
    /* PISTONS: HERE WE SELECT PISTONS SMALLER THAN THR, NOT GREATER! 
    ** Actually the selection on pistons should not be done this way. 
    ** However, to keep on the same algorithm, we make the largest pistons 
    ** the smallest "criterium value" using -1*fabs(piston) 
    ** and we select the same as for the other criteria, i.e., greater than the criterium. 
    ** It is thus necessary to apply -1*fabs(piston) to the absolute threshold also.*/
    else if ((frameSelectionType == amdlibOPD_SEL_PCG) ||
             (frameSelectionType == amdlibOPD_SEL_THR))
    {
        /* Wrap opd table */
        opdTablePtr = amdlibWrap2DArrayDouble(opd->pistonOPDArray[band],
                                             opd->nbBases,
                                             opd->nbFrames, errMsg);
        if (opdTablePtr == NULL)
        {
            return amdlibFAILURE;
        }
        /* Filter Blanks eventually */

        for (iBase = 0; iBase < opd->nbBases; iBase++)
        {
            for (iFrame = 0; iFrame < opd->nbFrames; iFrame++)
            {
                if (amdlibCompareDouble(opdTablePtr[iFrame][iBase],amdlibBLANKING_VALUE))
		{
		    selectedFrames->band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE;
		    selectedFrames->band[band].nbSelectedFrames[iBase]--;
		}
            }
        }

        /* Allocate memory for temporary arrays */
        for (iBase = 0; iBase < opd->nbBases; iBase++)
        {
            tmpArray[iBase] = 
                calloc(selectedFrames->band[band].nbSelectedFrames[iBase], 
                       sizeof(double));
            if (tmpArray[iBase] == NULL)
            {
                amdlibSetErrMsg("Could not allocate memory for temporary buffer");
                return amdlibFAILURE;
            }
        }

        /* Loop on frames, copy ABSOLUTE VALUE of piston in a temporary array for each baseline */
        for (iBase = 0; iBase < opd->nbBases; iBase++)
        {
            index = 0;
            for (iFrame = 0; iFrame < opd->nbFrames; iFrame++)
            {
                if (selectedFrames->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE )
                {
                    tmpArray[iBase][index] = -1*fabs(opdTablePtr[iFrame][iBase]);
                    index++;
                }
            }
        }

        /* Threshold case */
        if (frameSelectionType == amdlibOPD_SEL_THR)
        {
            for (iBase = 0; iBase < opd->nbBases; iBase++)
            {
                threshold[iBase] = -1*fabs(frameSelectionRatio);
            }
        }
        else
        {
            /* Sort these temporary arrays and get associated thresholds */
            for (iBase = 0; iBase < opd->nbBases; iBase++)
            {
                if (amdlibGetThreshold(tmpArray[iBase], 
                                       selectedFrames->band[band].nbSelectedFrames[iBase], 
                                       frameSelectionRatio, 
                                       &threshold[iBase], 
                                       errMsg) != amdlibSUCCESS)
                {
                    amdlibLogWarning("Could not get threshold associated to "
                                     "the frameSelectionRatio");
                    amdlibLogWarningDetail(errMsg);
                    amdlibLogWarning("Could not perform frame selection "
                                     "on baseline %d in band %c",
                                     iBase+1, amdlibBandNumToStr(band));
                    select[iBase] = amdlibFALSE; 
                }
            }
        }
        
        /* Loop on frames and stamp the bad ones (criterion lower than 
         * threshold) */
        for (iBase = 0; iBase < opd->nbBases; iBase++)
        {
            if (select[iBase] == amdlibTRUE)
            {
                index = 0;
                for (iFrame = 0; iFrame < opd->nbFrames; iFrame++)
                {
                    if (selectedFrames->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE)
                    {
                        if (tmpArray[iBase][index] < threshold[iBase])
                        {
                            selectedFrames->band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE;
                            selectedFrames->band[band].nbSelectedFrames[iBase]--;
                        }
                        index++;
                    }
                }
            }
	    /* In the case no selection was possible, set all frames to false */
	    else
	    {
                for (iFrame = 0; iFrame < opd->nbFrames; iFrame++)
                {
		    selectedFrames->band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE;
		}
		selectedFrames->band[band].nbSelectedFrames[iBase] = 0;
	    }
        }
        for (iBase = 0; iBase < opd->nbBases; iBase++)
        {
            free(tmpArray[iBase]);
        }
        amdlibFree2DArrayWrapping((void **)photTablePtr);
    }
    else if (frameSelectionType == amdlibNO_FRAME_SEL)
    {
        return amdlibSUCCESS;
    }
    else 
    {
        amdlibSetErrMsg("Invalid frame selection type '%d'",
                        frameSelectionType);
        return amdlibFAILURE;
    }

    /* Update frame selection */
    amdlibUpdateSelection(selectedFrames);

    return amdlibSUCCESS;
}

/**
 * Computes the selection threshold, ie the minimal possible value for frame 
 * selection criterion so as to a frame should be selected. 
 *
 * @param array input data 
 * @param arraySize size of array.
 * @param frameSelectionRatio percentage of good frames to keep.
 * @param threshold output threshold.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibGetThreshold(double          *array,
                                    int             arraySize,
                                    double          frameSelectionRatio,
                                    double          *threshold,
                                    amdlibERROR_MSG errMsg)
{
    int    i, j;
    double val;
    int    index;
    amdlibBOOLEAN isSorted;
    double * phantomArray;

    amdlibLogTrace("amdlibGetThreshold()");
    /*here we are in PCG, so frameSelectionRatio must be >0.0 and <1.0*/
    frameSelectionRatio=amdlibMin(frameSelectionRatio,1.0);
    frameSelectionRatio=amdlibMax(frameSelectionRatio,0.0);

    phantomArray = calloc(arraySize, sizeof(double));
    memcpy(phantomArray, array, arraySize*sizeof(double));
    /* Protected Bubble sort, leaves array invariant! */
    isSorted = amdlibFALSE;
    i = 0; 
    while ((i < arraySize) && (isSorted == amdlibFALSE))
    {
        /* Assume array is already sorted */
        isSorted = amdlibTRUE;
        for (j = 1; j < arraySize - i; j++)
        {
            if (phantomArray[j] < phantomArray[j-1])
            {
                /* Invert both values */
                val = phantomArray[j-1];
                phantomArray[j-1] = phantomArray[j];
                phantomArray[j] = val;

                /* phantomArray is not sorted yet */
                isSorted = amdlibFALSE;
            }
        }
        i++;
    }

    /* Find threshold */
    index = floor((1.0-frameSelectionRatio)*arraySize);
    *threshold = phantomArray[index];
    free(phantomArray);


    if (isnan(*threshold))
    {
        amdlibSetErrMsg("Impossible to determine threshold; probably "
                        "due to poor data quality");
        return amdlibFAILURE;
    }

    return amdlibSUCCESS;
}


/** Useful macro to free amdlibAverageVisibilities structures */ 
#define amdlibAverageVisibilities_FREEALL()                   \
    amdlibFree2DArrayWrapping((void **)photTabPtr);   \
    amdlibFree2DArrayWrapping((void **)visTabPtr);    \
    amdlibFree2DArrayWrapping((void **)vis2TabPtr);   \
    amdlibFree2DArrayWrapping((void **)vis3TabPtr); \
    amdlibFree3DArrayDouble(tmpphot);                 \
    amdlibFree3DArrayDouble(s2phot); 
/**
 * Average data for selected frames in each output data structure for
 * visibilities computation. 
 *
 * @param phot input/output photometry structure. 
 * @param vis input/output OI_VIS structure.
 * @param vis2 input/output OI_VIS2 structure.
 * @param vis3 input/output OI_VIS3 structure.
 * @param opd input/output piston structure.
 * @param band the current spectral band treated.
 * @param wave structure containing the wavelengths. 
 * @param selectedFrames structure containing information about selected frames.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibAverageVisibilities(amdlibPHOTOMETRY *phot,
                                           amdlibVIS        *vis, 
                                           amdlibVIS2       *vis2, 
                                           amdlibVIS3       *vis3,
                                           amdlibPISTON     *opd,
                                           amdlibBAND       band,
                                           amdlibWAVELENGTH *wave,
                                           amdlibSELECTION  *selectedFrames,
                                           amdlibERROR_MSG  errMsg)
{
    amdlibPHOTOMETRY_TABLE_ENTRY **photTabPtr = NULL;
    amdlibVIS_TABLE_ENTRY **visTabPtr = NULL;
    amdlibVIS2_TABLE_ENTRY **vis2TabPtr = NULL;
    amdlibVIS3_TABLE_ENTRY **vis3TabPtr = NULL;
    int nbFrames = vis->nbFrames;
    int nbBases = vis->nbBases;
    int nbWlen = vis->nbWlen;
    int i, j, k;
    int totalNbSel;

    /* Intermediate structures */
    amdlibPHOTOMETRY imdPhot = {NULL};
    amdlibVIS imdVis = {NULL};
    amdlibVIS2 imdVis2 = {NULL};
    amdlibVIS3 imdVis3 = {NULL};
    amdlibPISTON imdOpd = {NULL};

    double ***tmpphot = NULL, ***s2phot = NULL;
    double P1P2, P1MoreP2, P1DiviP2, P1, P2, sigma2P1MoreP2, sigma2P1DiviP2, 
           sigma2_P1, sigma2_P2;
    
    amdlibLogTrace("amdlibAverageVisibilities()");

    /* Allocate intermediate structures  which will contain mean values*/
    if (amdlibAllocatePhotometry(&imdPhot, 1, phot->nbBases, 
                                 phot->nbWlen) != amdlibSUCCESS)
    {
        amdlibAverageVisibilities_FREEALL();
        amdlibSetErrMsg("Could not allocate memory for intermediate structures");
        return amdlibFAILURE;
    }
    if (amdlibAllocateVis(&imdVis, 1, vis->nbBases, 
                          vis->nbWlen) != amdlibSUCCESS)
    {
        amdlibAverageVisibilities_FREEALL();
        amdlibReleasePhotometry(&imdPhot);
        amdlibSetErrMsg("Could not allocate memory for intermediate structures");
        return amdlibFAILURE;
    }
    if (amdlibAllocateVis2(&imdVis2, 1, vis2->nbBases, 
                           vis2->nbWlen) != amdlibSUCCESS)
    {
        amdlibAverageVisibilities_FREEALL();
        amdlibReleasePhotometry(&imdPhot);
        amdlibReleaseVis(&imdVis);
        amdlibSetErrMsg("Could not allocate memory for intermediate structures");
        return amdlibFAILURE;
    }

    if (nbBases > 1)
    {
        if (amdlibAllocateVis3(&imdVis3, 1, vis3->nbClosures, 
                               vis3->nbWlen) != amdlibSUCCESS)
        {
            amdlibAverageVisibilities_FREEALL();
            amdlibReleasePhotometry(&imdPhot);
            amdlibReleaseVis(&imdVis);
            amdlibReleaseVis2(&imdVis2);
            amdlibSetErrMsg("Could not allocate memory for intermediate structures");
            return amdlibFAILURE;
        }
    }
    if (amdlibAllocatePiston(&imdOpd, 1, opd->nbBases) != amdlibSUCCESS)
    {
        amdlibAverageVisibilities_FREEALL();
        amdlibReleasePhotometry(&imdPhot);
        amdlibReleaseVis(&imdVis);
        amdlibReleaseVis2(&imdVis2);
        amdlibReleaseVis3(&imdVis3);
        amdlibSetErrMsg("Could not allocate memory for intermediate structures");
        return amdlibFAILURE;
    }
    
    /* Allocate memory */
    tmpphot = amdlibAlloc3DArrayDouble(2, nbWlen, nbBases, errMsg);
    if (tmpphot == NULL)
    {
        amdlibAverageVisibilities_FREEALL();
        return amdlibFAILURE;
    }
    s2phot = amdlibAlloc3DArrayDouble(2, nbWlen, nbBases, errMsg);
    if (s2phot == NULL)
    {
        amdlibAverageVisibilities_FREEALL();
        return amdlibFAILURE;
    }

    /* Wrap on structures to average */
    photTabPtr = (amdlibPHOTOMETRY_TABLE_ENTRY **)
        amdlibWrap2DArray(phot->table,  nbBases, nbFrames, 
                          sizeof(*(phot->table)), errMsg);
    if (photTabPtr == NULL)
    {
        amdlibAverageVisibilities_FREEALL();
        return amdlibFAILURE;
    }
    visTabPtr = (amdlibVIS_TABLE_ENTRY **)
        amdlibWrap2DArray(vis->table, nbBases, nbFrames,
                          sizeof(*(vis->table)), errMsg);
    if (visTabPtr == NULL)
    {
        amdlibAverageVisibilities_FREEALL();
        return amdlibFAILURE;
    }
    vis2TabPtr = (amdlibVIS2_TABLE_ENTRY **)
        amdlibWrap2DArray(vis2->table, nbBases, nbFrames,
                          sizeof(*(vis2->table)), errMsg);
    if (vis2TabPtr == NULL)
    {
        amdlibAverageVisibilities_FREEALL();
        return amdlibFAILURE;
    }
    if (nbBases > 1)
    {
        vis3TabPtr = (amdlibVIS3_TABLE_ENTRY **)
            amdlibWrap2DArray(vis3->table, vis3->nbClosures, nbFrames,
                              sizeof(*(vis3->table)), errMsg);
        if (vis3TabPtr == NULL)
        {
            amdlibAverageVisibilities_FREEALL();
            return amdlibFAILURE;
        }
    }

    /* Check there is at least one frame selected */
    totalNbSel = 0;
    for (j=0; j < nbBases; j++)
    {
        totalNbSel += selectedFrames->band[band].nbSelectedFrames[j];
    }
    
    if (totalNbSel != 0)
    {
        /* Sum all values that have to be simply averaged */
        for (j = 0; j < nbBases; j++)
        {
            selectedFrames->band[band].firstSelectedFrame[j] = -1;
        }

        for (j = 0; j < nbBases; j++)
        {
            if (selectedFrames->band[band].nbSelectedFrames[j] == 0)
            {
                continue;
            }
            for (i = 0; i < nbFrames; i++)
            {
                if (selectedFrames->band[band].isSelectedPt[j][i] == amdlibTRUE)
                {
                    imdVis.table[j].expTime += visTabPtr[i][j].expTime;
                    imdVis2.table[j].expTime += vis2TabPtr[i][j].expTime;
		    /*FIXME: if all phots are BLANK , unpredictable result!*/
                    for (k = 0; k < nbWlen; k++)
                    {
                        if (!amdlibCompareDouble(
					photTabPtr[i][j].sigma2FluxSumPiPj[k], 
                                        amdlibBLANKING_VALUE))
                        {
			  P1MoreP2 = photTabPtr[i][j].fluxSumPiPj[k];
                          
			  sigma2P1MoreP2 = photTabPtr[i][j].sigma2FluxSumPiPj[k];
			  
			  P1DiviP2 = photTabPtr[i][j].fluxRatPiPj[k];
			  
			  sigma2P1DiviP2 = photTabPtr[i][j].sigma2FluxRatPiPj[k];
			  
			  P2 = P1MoreP2 / (1.0 + P1DiviP2);
			  
			  sigma2_P2 = sigma2P1MoreP2 / (1.0 + sigma2P1DiviP2);
			  
			  P1 = P1MoreP2*P1DiviP2/(1.0+P1DiviP2);
			  
			  sigma2_P1 = sigma2P1MoreP2 * sigma2P1DiviP2 / 
                            (1.0 + sigma2P1DiviP2);
			  
			  tmpphot[j][k][0] += P1;
			  s2phot[j][k][0] += sigma2_P1;
			  tmpphot[j][k][1] += P2;
			  s2phot[j][k][1] += sigma2_P2;
			  
			  imdPhot.table[j].PiMultPj[k] +=
                            photTabPtr[i][j].PiMultPj[k];
                        }
                    }
		    
                    if (selectedFrames->band[band].firstSelectedFrame[j] == -1)
		      {
                        selectedFrames->band[band].firstSelectedFrame[j] = i;
		      }
                }
            }
        }
        
        for (j = 0; j < nbBases; j++)
        {
            if (selectedFrames->band[band].nbSelectedFrames[j] == 0)
            {
                for (k = 0; k < nbWlen; k++)
                {
                    imdPhot.table[j].fluxSumPiPj[k] = amdlibBLANKING_VALUE;
                    
                    imdPhot.table[j].sigma2FluxSumPiPj[k] = amdlibBLANKING_VALUE;
                    
                    imdPhot.table[j].fluxRatPiPj[k] = amdlibBLANKING_VALUE;
                    
                    imdPhot.table[j].sigma2FluxRatPiPj[k] = amdlibBLANKING_VALUE;
                }
            }
            else
            {
                for (k = 0; k < nbWlen; k++)
                {
                    imdPhot.table[j].fluxSumPiPj[k] = tmpphot[j][k][0] + 
                    tmpphot[j][k][1];
                    
                    imdPhot.table[j].sigma2FluxSumPiPj[k] = s2phot[j][k][0] + 
                    s2phot[j][k][1];
                    
                    imdPhot.table[j].fluxRatPiPj[k] = tmpphot[j][k][0] / 
                    tmpphot[j][k][1];
                    
                    imdPhot.table[j].sigma2FluxRatPiPj[k] = s2phot[j][k][0] / 
                    s2phot[j][k][1];
                }
            }
        }

        /* Update complex vis (VISDATA) section accordingly. */
        amdlibAverageVis(vis, 0, band, selectedFrames, &imdVis, errMsg);

        /* In order to recompute vis2, reconvert visdata (normalized) in 
         * coherent fluxes :*/
        for (i = 0; i < nbFrames; i++)
        {
            for (j = 0; j < nbBases; j++)
            {
                for (k = 0; k < nbWlen; k++)
                {
                    if (visTabPtr[i][j].flag[k]!=amdlibTRUE)
                    {
                        P1P2 = photTabPtr[i][j].PiMultPj[k];
                        if (!amdlibCompareDouble(P1P2, amdlibBLANKING_VALUE))
                        {
                            visTabPtr[i][j].vis[k].re *= (2*amdlibSignedSqrt(P1P2));
                            visTabPtr[i][j].vis[k].im *= (2*amdlibSignedSqrt(P1P2));
                            visTabPtr[i][j].sigma2Vis[k].re *=  (4 * P1P2);
                            visTabPtr[i][j].sigma2Vis[k].im *=  (4 * P1P2);
                        }
                        else
                        { /* Should keep vis BLANK, but vis is already tagged */
			    amdlibLogWarning("Wrongly Unflagged Vis -- please report!");
                            visTabPtr[i][j].flag[k]=amdlibTRUE;
                        }
                    }
                }
            }
        }

        /* Compute vis (diffvis) on selected frames exclusively. */
        amdlibComputeDiffVis(/* INPUT */
			     vis, wave, opd, 0, band,
			     selectedFrames,
			     /* OUTPUT */
			     &imdVis, errMsg);


        /* Compute fringe criteria on selected frames exclusively. */
        amdlibComputeFringeCriterion(vis, 0, band, selectedFrames, &imdVis); /*protected against flagged V */

        /* Re-Compute vis2 on selected frames exclusively */
        amdlibComputeSqVis(vis, phot, 0, band, selectedFrames, 
                           (amdlibERROR_TYPE)amdlibSTATISTICAL_ERROR,
                           &imdVis2, errMsg); /*protected against flagged V and blanked phots.*/
        /* update average values in header */
        amdlibAverageVis2(&imdVis2);

        /* Compute piston on selected frames exclusively */
        if (amdlibMeanPiston(opd, band, 0, selectedFrames, 
                             &imdOpd) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Could not mean piston");        
            return amdlibFAILURE;
        }

        /* Build vis3 data structure */
        if (nbBases > 1)
        {
            if (selectedFrames->band[band].nbFramesOkForClosure != 0)
            {
                /* Build vis3 values again */
                if (amdlibComputeClosurePhases(vis, 0, band, selectedFrames,
					       (amdlibERROR_TYPE)amdlibSTATISTICAL_ERROR,
					       &imdVis3,
                                               errMsg) != amdlibSUCCESS)
                {
                    return amdlibFAILURE;
                }
                amdlibAverageClosurePhases(&imdVis3);

                /* Fill VIS3 data structure */
                i = selectedFrames->band[band].frameOkForClosure[0];
                for (i = 0; i < selectedFrames->band[band].nbFramesOkForClosure; i++)
                {
                    j = selectedFrames->band[band].frameOkForClosure[i];
                    imdVis3.table[0].expTime += vis3TabPtr[j][0].expTime;
                }
            }
        }

    } 
    else
    {
        amdlibLogWarning("No frames selected for band %c", 
                         amdlibBandNumToStr(band));
    }

    /* Compute UV coords */
    for (j=0; j < nbBases; j++)
    {
        /* UV coordinates treatment */
        imdVis.table[j].uCoord = (visTabPtr[0][j].uCoord + 
                                  visTabPtr[nbFrames-1][j].uCoord) / 2;
        imdVis.table[j].vCoord = (visTabPtr[0][j].vCoord + 
                                  visTabPtr[nbFrames-1][j].vCoord) / 2;
        imdVis2.table[j].uCoord = (vis2TabPtr[0][j].uCoord + 
                                   vis2TabPtr[nbFrames-1][j].uCoord) / 2;
        imdVis2.table[j].vCoord = (vis2TabPtr[0][j].vCoord + 
                                   vis2TabPtr[nbFrames-1][j].vCoord) / 2;

        /* Time / date treatment */
        imdVis.table[j].time = (visTabPtr[0][j].time + 
                                visTabPtr[nbFrames-1][j].time) / 2;
        imdVis2.table[j].time = (vis2TabPtr[0][j].time + 
                                 vis2TabPtr[nbFrames-1][j].time) / 2;
        imdVis.table[j].dateObsMJD = (visTabPtr[0][j].dateObsMJD + 
                                      visTabPtr[nbFrames-1][j].dateObsMJD) / 2;
        imdVis2.table[j].dateObsMJD = (vis2TabPtr[0][j].dateObsMJD + 
                                       vis2TabPtr[nbFrames-1][j].dateObsMJD)/2;

        /* target identifier and station indexes treatment */
        imdVis.table[j].targetId = visTabPtr[0][j].targetId;
        imdVis.table[j].stationIndex[0] = visTabPtr[0][j].stationIndex[0];
        imdVis.table[j].stationIndex[1] = visTabPtr[0][j].stationIndex[1];

        imdVis2.table[j].targetId = vis2TabPtr[0][j].targetId;
        imdVis2.table[j].stationIndex[0] = vis2TabPtr[0][j].stationIndex[0];
        imdVis2.table[j].stationIndex[1] = vis2TabPtr[0][j].stationIndex[1];
    }
    if (nbBases > 1)
    {
        imdVis3.table[0].targetId = vis3TabPtr[0][0].targetId;
        imdVis3.table[0].time = (vis3TabPtr[0][0].time + 
                                 vis3TabPtr[nbFrames-1][0].time) / 2;
        imdVis3.table[0].dateObsMJD = (vis3TabPtr[0][0].dateObsMJD +
                                       vis3TabPtr[nbFrames-1][0].dateObsMJD)/2;
        imdVis3.table[0].stationIndex[0] = vis3TabPtr[0][0].stationIndex[0];
        imdVis3.table[0].stationIndex[1] = vis3TabPtr[0][0].stationIndex[1];
        imdVis3.table[0].stationIndex[2] = vis3TabPtr[0][0].stationIndex[2];

        imdVis3.table[0].u1Coord = (vis3TabPtr[0][0].u1Coord + 
                                    vis3TabPtr[nbFrames-1][0].u1Coord) / 2;
        imdVis3.table[0].v1Coord = (vis3TabPtr[0][0].v1Coord + 
                                    vis3TabPtr[nbFrames-1][0].v1Coord) / 2;
        imdVis3.table[0].u2Coord = (vis3TabPtr[0][0].u2Coord + 
                                    vis3TabPtr[nbFrames-1][0].u2Coord) / 2;
        imdVis3.table[0].v2Coord = (vis3TabPtr[0][0].v2Coord + 
                                    vis3TabPtr[nbFrames-1][0].v2Coord) / 2;
    }
    
    /* Free all wrappings */
    amdlibAverageVisibilities_FREEALL();

    /* Update all data structures: set computed mean values */
    free(phot->table[0].fluxSumPiPj);
    free(phot->table[0].sigma2FluxSumPiPj);
    free(phot->table[0].fluxRatPiPj);
    free(phot->table[0].sigma2FluxRatPiPj);
    free(phot->table[0].PiMultPj);
    free(phot->table);
    phot->nbFrames = 1;
    phot->table = imdPhot.table;

    free(vis->table[0].vis);
    free(vis->table[0].sigma2Vis);
    free(vis->table[0].visCovRI);
    free(vis->table[0].diffVisAmp);
    free(vis->table[0].diffVisAmpErr);
    free(vis->table[0].diffVisPhi);
    free(vis->table[0].diffVisPhiErr);
    free(vis->table[0].flag);
    free(vis->table);
    vis->nbFrames = 1;
    vis->table = imdVis.table;

    free(vis2->table[0].vis2);
    free(vis2->table[0].vis2Error);
    free(vis2->table[0].flag);
    free(vis2->table);
    vis2->nbFrames = 1;
    vis2->table = imdVis2.table;
    vis2->vis12 = imdVis2.vis12;
    vis2->vis23 = imdVis2.vis23;
    vis2->vis31 = imdVis2.vis31;
    vis2->sigmaVis12 = imdVis2.sigmaVis12;
    vis2->sigmaVis23 = imdVis2.sigmaVis23;
    vis2->sigmaVis31 = imdVis2.sigmaVis31;

    if (nbBases > 1)
    {
        free(vis3->table[0].vis3Amplitude);
        free(vis3->table[0].vis3AmplitudeError);
        free(vis3->table[0].vis3Phi);
        free(vis3->table[0].vis3PhiError);
        free(vis3->table[0].flag);
        free(vis3->table);
        vis3->nbFrames = 1;
        vis3->averageClosure = imdVis3.averageClosure;
        vis3->averageClosureError = imdVis3.averageClosureError;
        vis3->table = imdVis3.table;
    }

    opd->nbFrames = 1;
    for (i = amdlibJ_BAND; i <= amdlibK_BAND; i++)
    {
        if (opd->pistonOPDArray[i] != NULL)
        {
            free(opd->pistonOPDArray[i]);
        }
        if (opd->sigmaPistonArray[i] != NULL)
        {
            free(opd->sigmaPistonArray[i]);
        }
        opd->pistonOPDArray[i] = imdOpd.pistonOPDArray[i];
        opd->sigmaPistonArray[i] = imdOpd.sigmaPistonArray[i];
    }
    free(opd->pistonOPD);
    free(opd->sigmaPiston);
    opd->pistonOPD = imdOpd.pistonOPD;
    opd->sigmaPiston = imdOpd.sigmaPiston;

    return amdlibSUCCESS;
}
#undef amdlibAverageVisibilities_FREEALL

/** Useful macro to free amdlibComputeVisibilitiesFor structures */ 
#define amdlibComputeVisibilitiesFor_FREEALL()  \
    free(p2v); free(mk); free(sigma2_mk); free(cova_mk);  \
    free(v2p); free(fudge); \
    amdlibFree2DArrayDouble(chisq);  \
    free(gp2v); free(ikMean); free(sigma2_ikMean);       \
    free(gv2p); free(badp2v); \
    amdlibReleasePhotometry(&instantPhotometry); \
    amdlibReleaseVis(&instantCorrFlux); \
    amdlibReleasePiston(&instantOpd); 
/**
 * sub-entry point, gathers all strutures in input and output,
 * calls the other functions in sequence
 *
 * @param data structure containing the science observation (where the fringes   * are)
 * @param p2vm structure where P2VM is stored
 * @param waveData the wavelength definition structure
 * @param channelNo first channel included in computation
 * @param nbChannels number of channels total used in computation
 * @param binning the number of consecutives frames in the science observation
 * that will be used to measure a single point of visibilties in output.
 * the total time span of the frames in a bin should be of the order of
 * the coherence time of the atmosphere, unless the observation has 
 * benefitted of a fringe tracker device. At Paranal coherence time are 
 * rarely more than a few Millisecond. 
 * @param errorType indicates wether the noise figures are estimated 
 * statistically from the sequence of photometries during the bin 
 * (amdlibSTATISTICAL_ERROR) or theoretically by the poisson error on N photons
 * (amdlibTHEORETICAL_ERROR). The latter is forced obviously when the binsize
 * is 1.
 * @param pistonType indicates wether the piston is to be measured by fitting
 * a slope in the phasors amdlibITERATIVE_PHASOR or in the differential phases
 * after dewrapping (amdlibUNWRAPPED_PHASE). The latter is deprecated and
 * is not mainained anymore.
 * @param noCheckP2vmId forces amdlib to use without question the passed p2vm,
 * even if its magic number is not OK. Can happen if the P2VM has been 
 * observed AFTER the science observations. 
 * @param photometry output structure containing the (binned) photometry 
 * results.
 * @param vis structure containing the (binned) raw visibilities.
 * @param vis2 structure containing the (binned) squared visibilities.
 * @param vis3 structure containing the (binned) triple products.
 * @param wave structure containing the wavelengths corresponding to the other
 * output structures. 
 * @param opd structure containing the (binned) piston measurements.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.  */
amdlibCOMPL_STAT 
amdlibComputeVisibilitiesFor(/* Input */
                             amdlibSCIENCE_DATA     *data,
                             amdlibP2VM_MATRIX      *p2vm,
                             amdlibWAVEDATA         *waveData,
                             int                    *channelNo,
                             int                    nbChannels,
                             int                    binning,
                             amdlibERROR_TYPE       errorType,
                             amdlibPISTON_ALGORITHM pistonType,
                             amdlibBOOLEAN          noCheckP2vmId,
                             /* Output */
                             amdlibPHOTOMETRY       *photometry,
                             amdlibVIS              *vis,
                             amdlibVIS2             *vis2,
                             amdlibVIS3             *vis3,
                             amdlibWAVELENGTH       *wave,
                             amdlibPISTON           *opd,
                             amdlibERROR_MSG        errMsg)
{

    int nbPix = data->col[amdlibINTERF_CHANNEL].nbPixels;
    int nbTel = data->nbCols - 1;  /* Number of telescopes used in data*/
    int iFrame;
    int nbFrames = data->nbFrames;
    int nbBases = nbTel * (nbTel - 1) / 2; /* Number of baselines in data*/
    int nbClos = nbTel * (nbTel - 1) * (nbTel - 2) / 6; /* Number of closures */
    int iBin; /* Index of the current binning */
    int firstFrame; /* Number of visibilities stored in OI_VIS */
    int nbBins; /* Number of binnings */
    double *p2v; /* local copy of (eventual 2T subset of) current p2vm */
    double *gp2v; /* local copy of (eventual 2T subset of) current p2vm */
    unsigned char *badp2v; /* local copy of current p2vm Bad Pixels */
    double *gv2p; /* the direct pixel to visibility matrix */
    double *v2p; /* the direct pixel to visibility matrix */
    double *mk;
    double *sigma2_mk,*cova_mk;
    double *ikMean;
    double *sigma2_ikMean;
    double *fudge;
    double **chisq;
    static amdlibVIS instantCorrFlux;
    static amdlibPHOTOMETRY instantPhotometry;
    static amdlibPISTON instantOpd;
    amdlibBAND band;
    double chi2Limit;
    double spectralResolution=1E12;
    double pistonErrThreshold=amdlibBLANKING_VALUE;
    double pistonThreshold=amdlibBLANKING_VALUE;

    amdlibLogTrace("amdlibComputeVisibilitiesFor()");

    if(binning > nbFrames)
    {
        binning = nbFrames;
        nbBins = 1;
    }
    else
    {
        nbBins = data->nbFrames / binning;
    }

    if (binning == 1)
    {
        errorType = amdlibTHEORETICAL_ERROR; 
    }
#if amdlibDEBUG
    amdlibLogTrace("amdlibComputeVisibilitiesFor() - first data : ");
    amdlibLogTrace("nbPix = %d", nbPix);
    amdlibLogTrace("nbTel = %d", nbTel);
    amdlibLogTrace("nbFrames = %d", nbFrames);
    amdlibLogTrace("nbBases = %d", nbBases);
    amdlibLogTrace("nbClos = %d", nbClos);
    amdlibLogTrace("nbBins = %d", nbBins);
    amdlibLogTrace("nbChannels = %d", nbChannels);
    amdlibLogTrace("details: ");
    int i;
    for (i = 0; i < nbChannels; i++)
    {
        amdlibLogTrace("channelNo[%d] = %d, wlen = %f", i, channelNo[i], 
                       waveData->wlen[channelNo[i]]);
    }
#endif

    if(amdlibCheckInsConfig(data, p2vm, noCheckP2vmId, 
                            errMsg) == amdlibFAILURE)
    {
        return amdlibFAILURE;
    }

    /* Allocate all the structures used by amdlibComputeVisibilitiesFor */
    /* Allocate Wavelength Structure */
    if (amdlibAllocateWavelength(wave, nbChannels, errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }
    /* Allocate memory for photometry */
    if (amdlibAllocatePhotometry(photometry, nbBins, nbBases, 
                                 nbChannels) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for PHOTOMETRY");
        return amdlibFAILURE;
    }
    /* Allocate memory for vis */
    if (amdlibAllocateVis(vis, nbBins, nbBases, nbChannels) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for VIS");
        return amdlibFAILURE;
    }
    /* Allocate memory vis2  */
    if (amdlibAllocateVis2(vis2, nbBins, nbBases, nbChannels) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for VIS2");
        return amdlibFAILURE;
    }
    if (nbTel == 3)
    {
        /* Allocate VIS3 structure */
        if (amdlibAllocateVis3(vis3, nbBins, nbClos, 
                               nbChannels) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Could not allocate memory for VIS3");
            return amdlibFAILURE;
        }
    }
    /* Allocate Piston Structure */
    if (amdlibAllocatePiston(opd, nbBins, nbBases) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for Piston structure");
        return amdlibFAILURE;
    }

    /* Allocate p2v matrix and associated matrixes */
    /* local copy of (eventual 2T subset of) current
     *  p2vm dim(nbPix,2*nbBases)
     */
    p2v       = calloc(2*nbBases*nbPix*nbChannels, sizeof(double));
    gp2v      = calloc((2*nbBases+1)*nbPix*nbChannels, sizeof(double)); 
    /* local copy of (eventual 2T subset of)
     * current p2vm Bad Pixels 
     */
    badp2v    = calloc(nbChannels*nbPix, sizeof(char)); 
    mk        = calloc(nbChannels*nbPix, sizeof(double));
    sigma2_mk = calloc(nbChannels*nbPix, sizeof(double));
    cova_mk   = calloc(nbChannels*nbPix*nbPix, sizeof(double));
    ikMean    = calloc(nbChannels*nbPix, sizeof(double)); 
    sigma2_ikMean = calloc(nbChannels*nbPix, sizeof(double));
    /* the direct visibility to pixel matrix */
    v2p       = calloc(nbPix*2*nbBases*nbChannels, sizeof(double));
    gv2p      = calloc(nbPix*(2*nbBases+1)*nbChannels, sizeof(double));
    /* The fudge factor vector */
    fudge     = calloc(nbChannels, sizeof(double));
    /* The chi-square vector */
    chisq = amdlibAlloc2DArrayDouble(nbChannels, nbFrames, errMsg);
    if (chisq == NULL)
    {
        amdlibComputeVisibilitiesFor_FREEALL();
        return amdlibFAILURE;
    }

    /* Allocate memory for the instantaneous photometry
     * used for all the computations */
    if (amdlibAllocatePhotometry(&instantPhotometry,
                                 nbFrames, nbBases, nbChannels) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for instantPhotometry");
        amdlibComputeVisibilitiesFor_FREEALL();
        return amdlibFAILURE;
    }

    /* Allocate memory for the instantaneous visibility
     * used for all the computations */
    if (amdlibAllocateVis(&instantCorrFlux,
                          nbFrames, nbBases, nbChannels) !=
        amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for instantCorrFlux");
        amdlibComputeVisibilitiesFor_FREEALL();
        return amdlibFAILURE;
    }
    /* Allocate memory for the instantaneous piston time series*/
    if (amdlibAllocatePiston(&instantOpd, nbFrames, nbBases) !=
        amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for instantOpd");
        amdlibComputeVisibilitiesFor_FREEALL();
        return amdlibFAILURE;
    }

    /* Linearize problem by retrieving the correct fudge factor
     * of the vks based on the whole measurement i.e.,
     * the long integration limit. */
    if (amdlibCrossGeneralizedP2vmAndData(/* Input */
                                          p2vm, data, channelNo, nbChannels,
                                          /* Output */
                                          gp2v, badp2v,
                                          ikMean, sigma2_ikMean,
                                          errMsg)!= amdlibSUCCESS)
    {
        amdlibComputeVisibilitiesFor_FREEALL();
        return amdlibFAILURE;
    }

    if (amdlibInvertGeneralizedP2v(/* Input */
                                   gp2v, badp2v, sigma2_ikMean, 
                                   nbChannels, nbPix, nbBases,
                                   /* Output */
                                   gv2p, errMsg)!= amdlibSUCCESS)
    {
        amdlibComputeVisibilitiesFor_FREEALL();
        return amdlibFAILURE;
    }

    if (amdlibComputeFudgeFactor(/* Input */
                                 gv2p, ikMean,
                                 nbChannels, nbPix, nbBases,
                                 /* Output */
                                 fudge,
                                 errMsg)!= amdlibSUCCESS)
    {
        amdlibComputeVisibilitiesFor_FREEALL();
        return amdlibFAILURE;
    }

    /* Retrieve All Photometries in one shot */
    if (amdlibGetInstantPhotometry(/* Input */
                                   p2vm, data, fudge, 
                                   channelNo, nbChannels,
                                   /* Output */
                                   &instantPhotometry,
                                   errMsg) != amdlibSUCCESS)
    {
        amdlibComputeVisibilitiesFor_FREEALL();
        return amdlibFAILURE;
    }
    amdlibLogInfoDetail("Computing Raw Correlated Fluxes...");
   /*Compute raw correlated fluxes for each frame. Binning will occur
    * afterwards. */
    for(iFrame=0; iFrame < nbFrames; iFrame++)
    {
        /* Get the right data, using the P2VM and data rows informations */
        if (amdlibCrossP2vmAndData(/* Input */
                                   p2vm, data, waveData, fudge, 
                                   iFrame, channelNo, nbChannels,
                                   /* Output */
                                   wave,
                                   p2v, badp2v,
                                   mk, sigma2_mk, cova_mk,
                                   ikMean, sigma2_ikMean,
                                   errMsg) != amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }
            
        /* Invert the P2VM, using a generalized inverse */
        if (amdlibInvertP2v(/* Input */
                            p2v, badp2v, cova_mk, 
                            nbChannels, nbPix, nbBases,
                            /* Output */
                            v2p, errMsg) != amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }
        
        /* Compute correlated flux, used after to compute visibilities */
        if (amdlibComputeCorrFlux(/* Input */
                                  v2p, mk, sigma2_mk,
                                  iFrame, nbChannels, nbPix, nbBases,
                                  /* Output */
                                  &instantCorrFlux, errMsg) != amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }

        if (amdlibComputeChiSquare(/* Input */
                                  &instantCorrFlux, p2v, badp2v, 
                                  mk, sigma2_mk,
                                  iFrame, nbChannels, nbPix, nbBases,
                                  /* Output */
                                  chisq,
                                  errMsg) != amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }
    }
    /* Achromatic piston computation for all frames. benefits even from 'bad'
     * correlated fluxes */
    band = amdlibGetBand(wave->wlen[0]);

    amdlibLogInfoDetail("Computing Pistons...");
    if (amdlibComputePiston(&instantCorrFlux, band, wave, 
                            &instantOpd, errMsg) != amdlibSUCCESS)
    {
        amdlibComputeVisibilitiesFor_FREEALL();
        return amdlibFAILURE;
    }
    /* Chi-Square filtering of very strange visibilities - 
     * 0 means 3 sigma around mean, 
     * -1 means none
     * a positive value is a limit */
    chi2Limit=amdlibFILTER_CHISQUARE_LIMIT;
    if (amdlibGetUserPref(amdlibCHISQUARE_LIMIT).set==amdlibTRUE)
        chi2Limit=amdlibGetUserPref(amdlibCHISQUARE_LIMIT).value;
    amdlibFilterByChiSquare(&instantCorrFlux,chisq,chi2Limit);

    /* update thresholds for piston */
    if (amdlibGetUserPref(amdlibMAX_PISTON_ERROR).set==amdlibTRUE)
        pistonErrThreshold=amdlibGetUserPref(amdlibMAX_PISTON_ERROR).value;
    if (amdlibGetUserPref(amdlibMAX_PISTON_EXCURSION).set==amdlibTRUE)
        pistonThreshold=amdlibGetUserPref(amdlibMAX_PISTON_EXCURSION).value;

    /* Improve Piston using piston closure (if requested) */
    if (amdlibGetUserPref(amdlibBEAUTIFY_PISTON).set==amdlibTRUE)
    {
        amdlibClosePiston(&instantOpd,pistonErrThreshold,band);
    }

    /* Tag bad pistons eventually */
    if (!amdlibCompareDouble(pistonErrThreshold,amdlibBLANKING_VALUE) ||
        !amdlibCompareDouble(pistonThreshold,amdlibBLANKING_VALUE)) amdlibTagPiston(&instantOpd,pistonThreshold,pistonErrThreshold,band); 

    /* Correct from piston extenuation if requested */
    if (amdlibGetUserPref(amdlibCORRECT_OPD0).set==amdlibTRUE)
    {
        spectralResolution=amdlibGetUserPref(amdlibCORRECT_OPD0).value;
        if (amdlibCorrectFromPistonExtenuation(&instantCorrFlux, &instantOpd, wave, band, spectralResolution, errMsg)!= amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }
    }

    if (binning>1) amdlibLogInfoDetail("Binning...");
    /* Binning */
    for(iBin=0; iBin < nbBins; iBin++)
    {
        /* The first frame to read in the data */
        firstFrame = iBin * binning;
        /* amdlibLogTrace("binning = %d", binning); */

        if (amdlibBinPiston(/* Input */
                             &instantOpd, band, firstFrame, binning, 
                             iBin, 
                             /* Output */
                             opd) != amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }


        /* Fringe criterion (SNR) computation */
        amdlibBinFringeCriterion(/* Input */
                                 &instantCorrFlux, firstFrame, binning,
                                 iBin, band,
                                 /* Output */
                                 vis);

        /* complex visibilities average */
        if (amdlibMeanCpxVis(/* Input */
                             &instantCorrFlux, 
                             &instantPhotometry,
                             firstFrame, binning,
                             iBin,
                             /* Output */
                             vis) != amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }

        /* squared visibilities estimation */
        if (amdlibBinSqVis(/* Input */
                               &instantCorrFlux, &instantPhotometry,
                               firstFrame, binning,
                               iBin, band, errorType,
                               /* Output */
                               vis2, errMsg) != amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }

        /* flux computation */
        if (amdlibSumPhotometry(/* Input */
				&instantPhotometry,
                                firstFrame, binning,
                                iBin,
				/* Output */
                                photometry) != amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }


        /* Compute Closure phases. */
        if (nbTel == 3)
        {
            if (amdlibBinClosurePhases(/* Input */
					   &instantCorrFlux, firstFrame,
                                           binning, iBin, band, 
                                           errorType,
					   /* Output */
					   vis3, errMsg) != amdlibSUCCESS)
            {
                amdlibComputeVisibilitiesFor_FREEALL();
                return amdlibFAILURE;
            }
        } /*End if (nbTel == 3) */

        /* Compute diffvis */
        if (amdlibBinDiffVis(/* Input */
                             &instantCorrFlux, wave, &instantOpd, 
                             firstFrame, binning, iBin, band,
                             /* Output */
                             vis, errMsg)!= amdlibSUCCESS)
        {
            amdlibComputeVisibilitiesFor_FREEALL();
            return amdlibFAILURE;
        }
    }

    /* Finalize table headers */
    amdlibFillInVisTableHeader(data, vis, errMsg);
    amdlibFillInVis2TableHeader(data, vis2, errMsg);
    if (nbTel == 3) 
    {
        amdlibFillInVis3TableHeader(data, vis3, errMsg);
    }

    amdlibReleasePhotometry(&instantPhotometry);
    amdlibReleaseVis(&instantCorrFlux);
    amdlibReleasePiston(&instantOpd);
    /* Compute global averaged values of squared visibilities and CPs */
    amdlibAverageVis2(vis2);
    amdlibAverageClosurePhases(vis3);

    amdlibComputeVisibilitiesFor_FREEALL();

    return amdlibSUCCESS;
}
#undef amdlibComputeVisibilitiesFor_FREEALL

/*
 * Local functions definitions
 */
/**
 * Check instrument configuration and coherence of the files used in the data
 * reduction.
 *
 * @param data structure containing the science observation (where the fringes 
 * are)
 * @param p2vm structure where P2VM is stored
 * @param noCheckP2vmId forces amdlib to use without question the passed p2vm,
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.  */
amdlibCOMPL_STAT amdlibCheckInsConfig(/* Input */
                                      amdlibSCIENCE_DATA     *data,
                                      amdlibP2VM_MATRIX      *p2vm,
                                      amdlibBOOLEAN          noCheckP2vmId,
                                      /* Output */
                                      amdlibERROR_MSG        errMsg)
{

    int nbTelInp2v = amdlibNB_TEL; 
    /* Number of Telescopes Used in the p2vm calibration file*/
    int nbTel = data->nbCols - 1; 
    /* Number of Telescopes Used in the Science data*/

    amdlibLogTrace("amdlibCheckInsConfig()");

    if (noCheckP2vmId == amdlibFALSE)
    {
        /* Check data reference to P2VM id */ 
        if (data->p2vmId == -1)
        {
            amdlibSetErrMsg("No P2VM available for this data");
            return amdlibFAILURE;
        }
        /* Check P2VM Ids */
        if ((p2vm->id != 0) && (data->p2vmId != 0) && 
            (p2vm->id != data->p2vmId))
        {
            amdlibSetErrMsg("PV2M id '%d' referenced in data does not match "
                            "with PV2M id '%d'", data->p2vmId, p2vm->id);
            return amdlibFAILURE;
        }
    }

    /* If P2VM id was not written in header */
    if ((p2vm->id == 0) || (data->p2vmId == 0))
    {
        /* Check instrument configuration */
        int keywIdx = 0;
        int dataKeywIdx;
        int p2vmKeywIdx;
        while (amdlibP2vmInsCfgKeywList[keywIdx] != NULL)
        {
            /* Found keyword in data instrument cfg */
            dataKeywIdx = 0;
            while ((strlen(data->insCfg.keywords[dataKeywIdx].name) != 0) &&
                   (strncmp(data->insCfg.keywords[dataKeywIdx].name, 
                            amdlibP2vmInsCfgKeywList[keywIdx],
                            strlen(amdlibP2vmInsCfgKeywList[keywIdx])) != 0))
            {
                dataKeywIdx++;
            }

            /* Found keyword in P2VM instrument cfg */
            p2vmKeywIdx = 0;
            while ((strlen(p2vm->insCfg.keywords[p2vmKeywIdx].name) != 0) &&
                   (strncmp(p2vm->insCfg.keywords[p2vmKeywIdx].name, 
                            amdlibP2vmInsCfgKeywList[keywIdx],
                            strlen(amdlibP2vmInsCfgKeywList[keywIdx])) != 0))
            {
                p2vmKeywIdx++;
            }


            /* Check keyword values (if found) */
            if ((strlen(data->insCfg.keywords[dataKeywIdx].name) != 0) &&
                (strlen(p2vm->insCfg.keywords[p2vmKeywIdx].name) != 0))
            {
                if (strcmp(data->insCfg.keywords[dataKeywIdx].value,
                           p2vm->insCfg.keywords[p2vmKeywIdx].value) != 0)
                {
                    amdlibStripBlanks(data->insCfg.keywords[dataKeywIdx].value);
                    amdlibStripBlanks(p2vm->insCfg.keywords[p2vmKeywIdx].value);

                    amdlibSetErrMsg("Value of keyword'%s' differs for P2VM "
                                    "'%s' and data '%s'",
                                    amdlibP2vmInsCfgKeywList[keywIdx],
                                    p2vm->insCfg.keywords[p2vmKeywIdx].value,
                                    data->insCfg.keywords[dataKeywIdx].value);
                    return amdlibFAILURE;
                }
            }

            /* Check next keyword */
            keywIdx++;
        }
    }

    /* Init the number of telescopes and baselines according to P2VM type */
    if (p2vm->type == amdlibP2VM_2T)
    {
        nbTelInp2v =  2;
    }
    else if (p2vm->type == amdlibP2VM_3T)
    {
        nbTelInp2v =  3;
    }
    else
    {
        amdlibSetErrMsg("Invalid P2VM type %d (see amdlibP2VM_TYPE)",
                        (int)p2vm->type);
        return amdlibFAILURE;
    }
    /* Should Check the number of telescopes and baselines according to
     * what the Science data claims
     */
    nbTel = data->nbCols - 1;
    if (nbTel > nbTelInp2v)
    {
        amdlibSetErrMsg("P2VM is %dT and Data %dT, aborting.",
                        nbTelInp2v, nbTel);
        return amdlibFAILURE;
    }
    /*
     * Make a few checks on the adequation between p2vm and science data
     */
    if (p2vm->nx != data->col[amdlibINTERF_CHANNEL].nbPixels)
    {
        amdlibSetErrMsg("Incompatible P2VM and data interf channel width "
                        "(%d vs. %d)", p2vm->nx,
                        data->col[amdlibINTERF_CHANNEL].nbPixels);
        return amdlibFAILURE;
    }

    if (data->nbChannels <= 0)
    {
        amdlibSetErrMsg("Invalid nbWlen (%d) information in data.",
                        data->nbChannels);
        return amdlibFAILURE;
    }
    /* I do not understand the following test. Corrected to accept (at least) 
     * simulated data from amdsim whose 1st valid spectral channel is 0
     * . GD */
    if(data->channelNo[0] < 0)
    {
        amdlibSetErrMsg("Invalid startPixel (%d) information in data.", 
                        data->channelNo[0]);
        return amdlibFAILURE;
    }
    return amdlibSUCCESS;

}

/**
 * Useful macro to free memory allocted in amdlibCrossGeneralizedP2vmAndData function
 */
#define amdlibCrossGeneralizedP2vmAndData_FREEALL()                                \
    amdlibFree2DArrayDouble(vk);                                        \
    amdlibFree3DArrayDoubleWrapping(gp2vCkDkPtr);                        \
    amdlibFree2DArrayDoubleWrapping(ikMeanPtr);                             \
    amdlibFree2DArrayDoubleWrapping(sigma2_ikMeanPtr);                       \
    amdlibFree2DArrayUnsignedCharWrapping(badPixelFlagsPtr);
/**
 * Extracts in the (large) input structures the relevant subset of values
 * needed,  computes the ikMean (long exposure equivalent) and photometries.
 *
 * @param p2vm pointer to P2VM.
 * @param data pointer to science data.
 * @param waveData the wavelength definition structure
 * @param firstFrame first frame in science data to handle.
 * @param nbFrames number of frames in science data to handle.
 * @param channelNo indexes of valid spectral channels to treat.
 * @param nbChannels number of valid spectral channels to treat.
 * @param wave structure containing the wavelengths corresponding to the other
 * @param photometry output structure containing the (binned) photometry 
 * results.
 * @param gp2vCkDk ck and dk and 'a' values of generalised P2VM.
 * @param badPixelFlags pointer to the bad pixel map
 * @param ikMean the mean value of the successive interferograms
 * @param sigma2_ikMean the associated variances of the ikMeans
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.  */
amdlibCOMPL_STAT amdlibCrossGeneralizedP2vmAndData(/* Input */
                                        amdlibP2VM_MATRIX  *p2vm,
                                        amdlibSCIENCE_DATA *data,
                                        int                *channelNo,
                                        int                nbChannels,
                                        /* Output */
                                        double             *gp2vCkDk,
                                        unsigned char      *badPixelFlags,
                                        double             *ikMean,
                                        double             *sigma2_ikMean,
                                        amdlibERROR_MSG    errMsg)
{

    /* loop indexes and associated limits */
    /* Index on pixels */
    int iPix;
    int nbPix = data->col[amdlibINTERF_CHANNEL].nbPixels;

    /* Index on telescopse */
    int iTel;
    int nbTel = data->nbCols - 1;

    /* Index on baselines */
    int iBase;
    int nbBases = nbTel * (nbTel - 1) / 2;

    /* Various Indexes on frames */
    int iFrame;
    int allFrames=data->nbFrames;

    /* Index on spectral channel in P2VM */
    int iP2vmChannel;

    /* Index on spectral channel in scienData */
    int iDataChannel;

    /* Index on spectral channel */
    int channel;

    /* Array to store Vk */
    double **vk = NULL;
    double ***gp2vCkDkPtr = NULL;
    double **ikMeanPtr = NULL;
    double **sigma2_ikMeanPtr = NULL;
    unsigned char **badPixelFlagsPtr = NULL;

    amdlibLogTrace("amdlibCrossGeneralizedP2vmAndData()"); 

    /* Allocate memory */
    vk = amdlibAlloc2DArrayDouble(nbPix, nbTel, errMsg);
    if (vk == NULL)
    {
        amdlibCrossGeneralizedP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
 
    /* And wrap already allocated array */
    gp2vCkDkPtr = amdlibWrap3DArrayDouble(gp2vCkDk, 2 * nbBases + 1, nbPix,
                                         nbChannels, errMsg);
    if (gp2vCkDkPtr == NULL)
    {
        amdlibCrossGeneralizedP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    ikMeanPtr = amdlibWrap2DArrayDouble(ikMean, nbPix, nbChannels, errMsg);
    if (ikMeanPtr == NULL)
    {
        amdlibCrossGeneralizedP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_ikMeanPtr = amdlibWrap2DArrayDouble(sigma2_ikMean, nbPix, nbChannels,
                                          errMsg);
    if (sigma2_ikMeanPtr == NULL)
    {
        amdlibCrossGeneralizedP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    badPixelFlagsPtr = amdlibWrap2DArrayUnsignedChar(badPixelFlags, nbPix, 
                                                     nbChannels, errMsg);
    if (badPixelFlagsPtr == NULL)
    {
        amdlibCrossGeneralizedP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }

    for (channel = 0; channel < nbChannels; channel++)
    {
        /* Check this channel is valid in P2VM, and get entry in P2VM
         * corresponding to channel */
        if (amdlibIsValidChannel(p2vm, channelNo[channel],
                                 &iP2vmChannel) == amdlibFALSE)
        {
            amdlibSetErrMsg("Channel '%d' is not in P2VM or it is not valid",
                            channelNo[channel]);
            return amdlibFAILURE;
        }

        /* Check this channel belongs to science data */
        iDataChannel = 0;
        while ((data->channelNo[iDataChannel] != channelNo[channel]) &&
               (iDataChannel < data->nbChannels))
        {
            iDataChannel++;
        }
        if (iDataChannel == data->nbChannels)
        {
            amdlibSetErrMsg("Channel '%d' does not belong to science data", 
                            channelNo[channel]);
            return amdlibFAILURE;
        }

#if amdlibDEBUG
        amdlibLogTrace("channelNo=%d, iDataChannel=%d, iP2vmChannel=%d, "
                       "wlen=%f",
                       channelNo[channel], iDataChannel, iP2vmChannel, 
               p2vm->wlen[iP2vmChannel]);
#endif

        /* first pass: retrieve initial vks */
        for (iTel = 0; iTel < nbTel; iTel++)
        {
            for (iPix = 0; iPix < nbPix; iPix++)
            {
                vk[iTel][iPix] = p2vm->vkPt[iTel][iP2vmChannel][iPix];
            }
        }

        /* Fill the generalised p2vm:
         * Extract cks, dks for all pixel of spectral channel from P2VM and
         * store into gp2v */
        for (iBase = 0; iBase < (2 * nbBases); iBase++)
        {
            for (iPix = 0; iPix < nbPix; iPix++)
            {
                gp2vCkDkPtr[channel][iPix][iBase] = 
                    p2vm->matrixPt[iP2vmChannel][iPix][iBase];
            }
        }
        /* compute p1*v1k+p2*v2k+p3*v3k, where Pi(lambda)
         * is averaged over ALL frames, and store in
         * the generalized p2vm */
        for (iTel = 0; iTel < nbTel; iTel++) 
        {
            double averagePhot=0.0;
            for (iFrame = 0; iFrame < allFrames; iFrame++)
            {
                amdlibDOUBLE *photData[amdlibNB_TEL];
                photData[0] = data->frame[iFrame].photo1;
                photData[1] = data->frame[iFrame].photo2;
                photData[2] = data->frame[iFrame].photo3;
                averagePhot+=photData[iTel][iDataChannel];
            }
            averagePhot /= allFrames;

            /* add corresponding vk*Pi in the last column in gp2vm */ 
            for (iPix = 0; iPix < nbPix; iPix++)
            { 
                gp2vCkDkPtr[channel][iPix][2*nbBases] += 
                fabs(averagePhot) * vk[iTel][iPix];
            }
        }
#if amdlibDEBUG
        for (iBase = 0; iBase < 2*nbBases+1; iBase++)
        {
            for (iPix = 0; iPix < nbPix; iPix++)
            {
                amdlibLogTrace("gp2vCkDkPtr[%d][%d][%d] = %f", channel, iPix, 
                               iBase, gp2vCkDkPtr[channel][iPix][iBase]); 
            }
        }
#endif
        /* Get bad pixels from Science */
        for (iPix = 0; iPix < nbPix; iPix++)
        {
            badPixelFlagsPtr[channel][iPix] =
                data->badPixelsPt[channel][iPix];
        }
        /* Add bad pixels from P2vm */
        for (iPix = 0; iPix < nbPix; iPix++)
        {
            if (p2vm->badPixelsPt[iP2vmChannel][iPix] == (unsigned char) 0)
            {
               badPixelFlagsPtr[channel][iPix] = (unsigned char) 0 ;
            }
        }

        /* Calculation of the averaged interferogram.*/
        for (iFrame = 0; iFrame < allFrames; iFrame++)
        {
            amdlibDOUBLE **intfPtr = NULL;
            amdlibDOUBLE **sigma2IntfPtr  = NULL;

            intfPtr = 
                amdlibWrap2DArrayDouble(data->frame[iFrame].intf,
                                       nbPix, data->nbChannels, errMsg);
            if (intfPtr == NULL)
            {
                amdlibCrossGeneralizedP2vmAndData_FREEALL();
                return amdlibFAILURE;
            }

            sigma2IntfPtr = 
                amdlibWrap2DArrayDouble(data->frame[iFrame].sigma2Intf,
                                       nbPix, data->nbChannels, errMsg);
            if (sigma2IntfPtr == NULL)
            {
                amdlibFree2DArrayDoubleWrapping(intfPtr);
                amdlibCrossGeneralizedP2vmAndData_FREEALL();
                return amdlibFAILURE;
            }

            /* Wrap interferometric channels */

            for (iPix = 0; iPix < nbPix; iPix++)
            {
                /* Sum time sequence of ik into ikMean */
                ikMeanPtr[channel][iPix] += 
                fabs(intfPtr[iDataChannel][iPix]);

                /* Sum time sequence of sigma2_ik-ik (aka sigma2_det) into sigma2_ikMean */
                sigma2_ikMeanPtr[channel][iPix] += 
                fabs(sigma2IntfPtr[iDataChannel][iPix]-intfPtr[iDataChannel][iPix]);
            }
            amdlibFree2DArrayDoubleWrapping(intfPtr);
            amdlibFree2DArrayDoubleWrapping(sigma2IntfPtr);
        }
        for (iPix = 0; iPix < nbPix; iPix++)
        {
            /*Compute average ik value */
            ikMeanPtr[channel][iPix] /= allFrames;
            /*Compute average sigma2_det*/
            sigma2_ikMeanPtr[channel][iPix] /= allFrames;
            /*Sigma2 of average_X is 1/n times the variance of X: X+ron^2 */
            sigma2_ikMeanPtr[channel][iPix]=
            (ikMeanPtr[channel][iPix]+sigma2_ikMeanPtr[channel][iPix])/allFrames;
        }
    }

    amdlibCrossGeneralizedP2vmAndData_FREEALL();

    return amdlibSUCCESS;
}
#undef amdlibCrossGeneralizedP2vmAndData_FREEALL

/**
 * Useful macro to free memory allocted in amdlibCrossP2vmAndData function
 */
#define amdlibCrossP2vmAndData_FREEALL()                                \
    amdlibFree2DArrayDouble(vk);                                        \
    amdlibFree2DArrayDouble(phot);                                      \
    amdlibFree2DArrayDouble(sigma2Phot);                                \
    amdlibFree3DArrayDoubleWrapping(p2vCkDkPtr);                        \
    amdlibFree2DArrayDoubleWrapping(mkPtr);                             \
    amdlibFree2DArrayDoubleWrapping(sigma2_mkPtr);                       \
    amdlibFree3DArrayDoubleWrapping(cova_mkPtr);                       \
    amdlibFree2DArrayDoubleWrapping(ikMeanPtr);                             \
    amdlibFree2DArrayDoubleWrapping(sigma2_ikMeanPtr);                       \
    amdlibFree2DArrayDoubleWrapping(intfPtr); \
    amdlibFree2DArrayDoubleWrapping(sigma2IntfPtr); \
    amdlibFree2DArrayDouble(sumVk);                                     \
    amdlibFree2DArrayUnsignedCharWrapping(badPixelFlagsPtr);            
/**
 * Extracts in the (large) input structures the relevant subset of values
 * needed, computes the mk and photometries.
 *
 * @param p2vm pointer to P2VM.
 * @param data pointer to science data.
 * @param waveData the wavelength definition structure
 * @param firstFrame first frame in science data to handle.
 * @param nbFrames number of frames in science data to handle.
 * @param channelNo indexes of valid spectral channels to treat.
 * @param nbChannels number of valid spectral channels to treat.
 * @param wave structure containing the wavelengths corresponding to the other
 * @param photometry output structure containing the (binned) photometry 
 * results.
 * @param p2vCkDk ck and dk values of P2VM.
 * @param badPixelFlags pointer to the bad pixel map
 * @param mk the continumm-corrected values of the interferograms
 * @param sigma2_mk the associated variances of the mks
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.  */
amdlibCOMPL_STAT amdlibCrossP2vmAndData(/* Input */
                                        amdlibP2VM_MATRIX  *p2vm,
                                        amdlibSCIENCE_DATA *data,
                                        amdlibWAVEDATA     *waveData,
                                        double             *fudge,
                                        int                iFrame,
                                        int                *channelNo,
                                        int                nbChannels,
                                        /* Output */
                                        amdlibWAVELENGTH   *wave,
                                        double             *p2vCkDk,
                                        unsigned char      *badPixelFlags,
                                        double             *mk,
                                        double             *sigma2_mk,
                                        double             *cova_mk,
                                        double             *ikMean,
                                        double             *sigma2_ikMean,
                                        amdlibERROR_MSG    errMsg)
{

    /* loop indexes and associated limits */
    /* Index on pixels */
    int iPix, jPix;
    int nbPix = data->col[amdlibINTERF_CHANNEL].nbPixels;

    /* Index on telescope */
    int iTel;
    int nbTel = data->nbCols - 1;

    /* Index on baselines */
    int iBase;
    int nbBases = nbTel * (nbTel - 1) / 2;

    /* Index on spectral channel in P2VM */
    int iP2vmChannel;

    /* Index on spectral channel in scienData */
    int iDataChannel;

    /* Index on spectral channel */
    int channel;

    /* Array to store Vk and sumVk */
    double **vk = NULL;
    double **sumVk = NULL;
    double **phot = NULL;
    double **sigma2Phot = NULL;
    double ***p2vCkDkPtr = NULL;
    double **mkPtr = NULL;
    double **sigma2_mkPtr = NULL;
    double ***cova_mkPtr = NULL;
    double **ikMeanPtr = NULL;
    double **sigma2_ikMeanPtr = NULL;
    double fluxi, fluxt;
    double sigma2Det;
    unsigned char **badPixelFlagsPtr = NULL;

    amdlibDOUBLE *photData[amdlibNB_TEL];
    amdlibDOUBLE *sigma2PhotData[amdlibNB_TEL];
    amdlibDOUBLE **intfPtr = NULL;
    amdlibDOUBLE **sigma2IntfPtr = NULL;

    amdlibLogTrace("amdlibCrossP2vmAndData()"); 

    /* Allocate memory */
    vk = amdlibAlloc2DArrayDouble(nbPix, nbTel, errMsg);
    if (vk == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    sumVk = amdlibAlloc2DArrayDouble(nbBases, nbChannels, errMsg);
    if (sumVk == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
 
    phot = amdlibAlloc2DArrayDouble(nbTel, nbChannels, errMsg);
    if (phot == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    sigma2Phot = amdlibAlloc2DArrayDouble(nbTel, nbChannels, errMsg);
    if (sigma2Phot == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }

    /* And wrap already allocated array */
    p2vCkDkPtr = amdlibWrap3DArrayDouble(p2vCkDk, 2 * nbBases, nbPix,
                                         nbChannels, errMsg);
    if (p2vCkDkPtr == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    mkPtr = amdlibWrap2DArrayDouble(mk, nbPix, nbChannels, errMsg);
    if (mkPtr == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_mkPtr = amdlibWrap2DArrayDouble(sigma2_mk, nbPix, nbChannels,
                                          errMsg);
    if (sigma2_mkPtr == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    cova_mkPtr = amdlibWrap3DArrayDouble(cova_mk, nbPix, nbPix, nbChannels,
                                          errMsg);
    if (cova_mkPtr == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    ikMeanPtr = amdlibWrap2DArrayDouble(ikMean, nbPix, nbChannels, errMsg);
    if (ikMeanPtr == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_ikMeanPtr = amdlibWrap2DArrayDouble(sigma2_ikMean, nbPix, nbChannels,
                                          errMsg);
    if (sigma2_ikMeanPtr == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    badPixelFlagsPtr = amdlibWrap2DArrayUnsignedChar(badPixelFlags, nbPix, 
                                                     nbChannels, errMsg);
    if (badPixelFlagsPtr == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }

    /* Wrap interferometric channels */
    intfPtr = 
    amdlibWrap2DArrayDouble(data->frame[iFrame].intf,
                            nbPix, data->nbChannels, errMsg);
    if (intfPtr == NULL)
    {
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
    sigma2IntfPtr = 
    amdlibWrap2DArrayDouble(data->frame[iFrame].sigma2Intf,
                            nbPix, data->nbChannels, errMsg);
    if (sigma2IntfPtr == NULL)
    {
        amdlibFree2DArrayDoubleWrapping(intfPtr);
        amdlibCrossP2vmAndData_FREEALL();
        return amdlibFAILURE;
    }
            
    /* For each channel */
    for (channel = 0; channel < nbChannels; channel++)
    {
        /* Check this channel is valid in P2VM, and get entry in P2VM
         * corresponding to channel */
        if (amdlibIsValidChannel(p2vm, channelNo[channel],
                                 &iP2vmChannel) == amdlibFALSE)
        {
            amdlibSetErrMsg("Channel '%d' is not in P2VM or it is not valid",
                            channelNo[channel]);
            return amdlibFAILURE;
        }

        /* Check this channel belongs to science data */
        iDataChannel = 0;
        while ((data->channelNo[iDataChannel] != channelNo[channel]) &&
               (iDataChannel < data->nbChannels))
        {
            iDataChannel++;
        }
        if (iDataChannel == data->nbChannels)
        {
            amdlibSetErrMsg("Channel '%d' does not belong to science data", 
                            channelNo[channel]);
            return amdlibFAILURE;
        }

#if amdlibDEBUG
        amdlibLogTrace("channelNo=%d, iDataChannel=%d, iP2vmChannel=%d, "
                       "wlen=%f",
                       channelNo[channel], iDataChannel, iP2vmChannel, 
               p2vm->wlen[iP2vmChannel]);
#endif

        /* Extract cks, dks for all pixel of spectral channel from P2VM and
         * store into p2v */
        for (iBase = 0; iBase < (2 * nbBases); iBase++)
        {
            for (iPix = 0; iPix < nbPix; iPix++)
            {
                p2vCkDkPtr[channel][iPix][iBase] = 
                    p2vm->matrixPt[iP2vmChannel][iPix][iBase];
            }
        }
#if amdlibDEBUG
        for (iBase = 0; iBase < (2 * nbBases); iBase++)
        {
            for (iPix = 0; iPix < nbPix; iPix++)
            {
                amdlibLogTrace("p2vCkDkPtr[%d][%d][%d] = %f", channel, iPix, 
                               iBase, p2vCkDkPtr[channel][iPix][iBase]); 
            }
        }
#endif
        /* Get bad pixels from Science */
        for (iPix = 0; iPix < nbPix; iPix++)
        {
            badPixelFlagsPtr[channel][iPix] =
                data->badPixelsPt[channel][iPix];
        }
        /* Add bad pixels from P2vm */
        for (iPix = 0; iPix < nbPix; iPix++)
        {
            if (p2vm->badPixelsPt[iP2vmChannel][iPix] == (unsigned char) 0)
            {
                badPixelFlagsPtr[channel][iPix] = (unsigned char) 0 ;
            }
        }

        /* Then extract Vk */
        for (iTel = 0; iTel < nbTel; iTel++)
        {
            for (iPix = 0; iPix < nbPix; iPix++)
            { /* begin loop for iPix */
                vk[iTel][iPix] = p2vm->vkPt[iTel][iP2vmChannel][iPix]*
                                 fudge[channel];
            }
        }

        /* Then extract sumVk */
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            sumVk[channel][iBase] = p2vm->sumVkPt[iBase][iP2vmChannel]*
            fudge[channel]*fudge[channel];
        }

        /* Extract time sequence of photometry and associated noise, and
         * normalize them */
        photData[0] = data->frame[iFrame].photo1;
        photData[1] = data->frame[iFrame].photo2;
        photData[2] = data->frame[iFrame].photo3;
        sigma2PhotData[0] = data->frame[iFrame].sigma2Photo1;
        sigma2PhotData[1] = data->frame[iFrame].sigma2Photo2;
        sigma2PhotData[2] = data->frame[iFrame].sigma2Photo3;
        /* extract Phot and associated sigma2Phot */
        for (iTel = 0; iTel < nbTel; iTel++) 
        {
            phot[channel][iTel] = 
            fabs(photData[iTel][iDataChannel]) ;
            sigma2Phot[channel][iTel] =
            sigma2PhotData[iTel][iDataChannel];
        }

        /* Calculation of the continuum corrected interferogram.
         * See eq 24 */
        fluxi=0.0;
        fluxt=0.0;
        for (iPix = 0; iPix < nbPix; iPix++)
        {
            /* Extract time sequence of mk = interf - sum_tel(vk*Phot) 
             * and associated flux of (sum of ik) */
            mkPtr[channel][iPix] = fabs(intfPtr[iDataChannel][iPix]);
            fluxi += mkPtr[channel][iPix];
            fluxt += fabs(ikMeanPtr[channel][iPix]);
            for (iTel = 0; iTel < nbTel; iTel++) 
            {
                mkPtr[channel][iPix] -= 
                phot[channel][iTel] * vk[iTel][iPix];
            }
        }
        /* Now we compute sigma2_mk not as the 
         * (badly measured) instantaneous 
         * pixel value, but evaluated by ratio between the flux
         * contained in the interferogram and the 
         * shape of the long-exposure equivalent.
         * We need to retrieve the detector noise also */

        for (iPix = 0; iPix < nbPix; iPix++)
        {
            sigma2Det=sigma2IntfPtr[iDataChannel][iPix]-intfPtr[iDataChannel][iPix];
            sigma2_mkPtr[channel][iPix] =
            fabs(ikMeanPtr[channel][iPix]*fluxi/fluxt)+sigma2Det;

            for (iTel = 0; iTel < nbTel; iTel++)
            { /* add percentage of noise due to photometry correction */
                sigma2_mkPtr[channel][iPix] +=
                /* sigma2Phot[channel][iTel] * */ /*note: using this overcorrects the V2. ?? */
                phot[channel][iTel] *
                amdlibPow2(vk[iTel][iPix]);
            }
        }

        for (iPix = 0; iPix < nbPix; iPix++)
        {
            for (jPix = 0; jPix <= iPix; jPix++)
            {
                if (iPix==jPix)
                {
                    sigma2Det=sigma2IntfPtr[iDataChannel][iPix]-intfPtr[iDataChannel][iPix];
                    cova_mkPtr[channel][iPix][iPix] = 
                    fabs(ikMeanPtr[channel][iPix]*fluxi/fluxt)+sigma2Det;

                    for (iTel = 0; iTel < nbTel; iTel++) 
                    { /* add percentage of noise due to photometry correction */
                        cova_mkPtr[channel][iPix][iPix] +=
                        /* sigma2Phot[channel][iTel] * */
                        phot[channel][iTel] *
                        vk[iTel][iPix]*
                        vk[iTel][iPix];
                    } 
                }
                else
                {
                    cova_mkPtr[channel][iPix][jPix] = 0;
                    for (iTel = 0; iTel < nbTel; iTel++) 
                    {
                        cova_mkPtr[channel][iPix][jPix] +=
                        /* sigma2Phot[channel][iTel] * */
                        phot[channel][iTel] *
                        vk[iTel][iPix] *
                        vk[iTel][jPix];
                    }
                }
                cova_mkPtr[channel][jPix][iPix] =
                cova_mkPtr[channel][iPix][jPix];
            }
        }
        
        /* Store corresponding wavelength in amdlibWAVELENGTH structure*/
        wave->bandwidth[channel] = waveData->bandwidth[channelNo[channel]];
        wave->wlen[channel]      = p2vm->wlen[iP2vmChannel];
    }
    /* Set number of channels */
    wave->nbWlen = nbChannels;

    amdlibCrossP2vmAndData_FREEALL();

    return amdlibSUCCESS;
}
#undef amdlibCrossP2vmAndData_FREEALL


/**
 * Useful macro to free memory allocated in amdlibGetInstantPhotometry function
 */
#define amdlibGetInstantPhotometry_FREEALL()                                \
    amdlibFree2DArrayDouble(vk);                                        \
    amdlibFree3DArrayDouble(phot);                                      \
    amdlibFree3DArrayDouble(sigma2Phot);                                \
    amdlibFree2DArrayDouble(sumVk);                                     \
    amdlibFree2DArrayWrapping((void **)photTablePtr); 
/**
 * @param p2vm pointer to P2VM.
 * @param data pointer to science data.
 * @param firstFrame first frame in science data to handle.
 * @param nbFrames number of frames in science data to handle.
 * @param channelNo indexes of valid spectral channels to treat.
 * @param nbChannels number of valid spectral channels to treat.
 * @param photometry output structure containing the (binned) photometry 
 * results.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.  */
amdlibCOMPL_STAT amdlibGetInstantPhotometry(/* Input */
                                        amdlibP2VM_MATRIX  *p2vm,
                                        amdlibSCIENCE_DATA *data,
                                        double             *fudge,
                                        int                *channelNo,
                                        int                nbChannels,
                                        /* Output */
                                        amdlibPHOTOMETRY   *photometry,
                                        amdlibERROR_MSG    errMsg)
{

    /* loop indexes and associated limits */
    /* Index on pixels */
    int iPix;
    int nbPix = data->col[amdlibINTERF_CHANNEL].nbPixels;

    /* Index on telescope */
    int iTel;
    int nbTel = data->nbCols - 1;

    /* Index on baselines */
    int iBase;
    int nbBases = nbTel * (nbTel - 1) / 2;

    /* Index on frames */
    int nbFrames=data->nbFrames;
    int iFrame;

    /* Index on spectral channel in P2VM */
    int iP2vmChannel;

    /* Index on spectral channel in scienData */
    int iDataChannel;

    /* Index on spectral channel */
    int channel;

    double **vk = NULL;
    double v1,v2;
    double **sumVk = NULL;
    double ***phot = NULL;
    double ***sigma2Phot = NULL;
    int tel1, tel2;
    double P1P2, P1, P2, sigma2P1, sigma2P2;
    double minimumFlux=0.0;
    amdlibPHOTOMETRY_TABLE_ENTRY **photTablePtr = NULL;


    amdlibLogTrace("amdlibGetInstantPhotometry()"); 

    if (amdlibGetUserPref(amdlibMIN_PHOTOMETRY).set==amdlibTRUE)
        minimumFlux=amdlibGetUserPref(amdlibMIN_PHOTOMETRY).value;

    /* Allocate memory */
    vk = amdlibAlloc2DArrayDouble(nbPix, nbTel, errMsg);
    if (vk == NULL)
    {
        amdlibGetInstantPhotometry_FREEALL();
        return amdlibFAILURE;
    }
    sumVk = amdlibAlloc2DArrayDouble(nbBases, nbChannels, errMsg);
    if (sumVk == NULL)
    {
        amdlibGetInstantPhotometry_FREEALL();
        return amdlibFAILURE;
    }
 
    phot = amdlibAlloc3DArrayDouble(nbTel, nbChannels, nbFrames, errMsg);
    if (phot == NULL)
    {
        amdlibGetInstantPhotometry_FREEALL();
        return amdlibFAILURE;
    }
    sigma2Phot = amdlibAlloc3DArrayDouble(nbTel, nbChannels, nbFrames, errMsg);
    if (sigma2Phot == NULL)
    {
        amdlibGetInstantPhotometry_FREEALL();
        return amdlibFAILURE;
    }

    /* For each channel */
    photTablePtr = (amdlibPHOTOMETRY_TABLE_ENTRY **)
        amdlibWrap2DArray(photometry->table,  nbBases, nbFrames, 
                          sizeof(*(photometry->table)), errMsg);

    for (channel = 0; channel < nbChannels; channel++)
    {
        /* Check this channel is valid in P2VM, and get entry in P2VM
         * corresponding to channel */
        if (amdlibIsValidChannel(p2vm, channelNo[channel],
                                 &iP2vmChannel) == amdlibFALSE)
        {
            amdlibSetErrMsg("Channel '%d' is not in P2VM or it is not valid",
                            channelNo[channel]);
            return amdlibFAILURE;
        }

        /* Check this channel belongs to science data */
        iDataChannel = 0;
        while ((data->channelNo[iDataChannel] != channelNo[channel]) &&
               (iDataChannel < data->nbChannels))
        {
            iDataChannel++;
        }
        if (iDataChannel == data->nbChannels)
        {
            amdlibSetErrMsg("Channel '%d' does not belong to science data", 
                            channelNo[channel]);
            return amdlibFAILURE;
        }

        /* get Vks */
        for (iTel = 0; iTel < nbTel; iTel++)
        {
            for (iPix = 0; iPix < nbPix; iPix++)
            { /* begin loop for iPix */
                vk[iTel][iPix] = p2vm->vkPt[iTel][iP2vmChannel][iPix]*
                                 fudge[channel];
            }
        }

        /* then sumVk */
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            sumVk[channel][iBase] = p2vm->sumVkPt[iBase][iP2vmChannel]*
            fudge[channel]*fudge[channel];
        }

        /* Extract time sequence of photometry and associated noise, and
         * normalize them */
        for (iFrame = 0; iFrame < nbFrames; iFrame++)
        {
            amdlibDOUBLE *photData[amdlibNB_TEL];
            amdlibDOUBLE *sigma2PhotData[amdlibNB_TEL];
            photData[0] = data->frame[iFrame].photo1;
            photData[1] = data->frame[iFrame].photo2;
            photData[2] = data->frame[iFrame].photo3;
            sigma2PhotData[0] = data->frame[iFrame].sigma2Photo1;
            sigma2PhotData[1] = data->frame[iFrame].sigma2Photo2;
            sigma2PhotData[2] = data->frame[iFrame].sigma2Photo3;
            /* extract time sequence of Phot and associated sigma2Phot */
            for (iTel = 0; iTel < nbTel; iTel++) 
            {
                phot[iFrame][channel][iTel] = 
                photData[iTel][iDataChannel] ;
                sigma2Phot[iFrame][channel][iTel] =
                sigma2PhotData[iTel][iDataChannel];
            }
        }

        for (iFrame = 0; iFrame < nbFrames; iFrame++)
        {
            for (iBase = 0; iBase < nbBases; iBase++)
            { 
                /* Set used telescope according to the baseline */
                if (iBase == amdlibBASE12)
                {
                    tel1 = amdlibTEL1;
                    tel2 = amdlibTEL2;
                }
                else if (iBase == amdlibBASE23)
                {
                    tel1 = amdlibTEL2;
                    tel2 = amdlibTEL3;
                }
                else
                {
                    tel1 = amdlibTEL1;
                    tel2 = amdlibTEL3;
                }
                /* P1P2 is directly estimated, giving PiMultPj. 
                 * if P1 or P2 is negative, tag everything unavailable 
		 * using Blanking Value: photometries having no 
		 * associated flags */
                P1 = phot[iFrame][channel][tel1] ;
                P2 = phot[iFrame][channel][tel2] ;
                if (P1<minimumFlux||P2<minimumFlux)
                {
                    if (P1<0.0) amdlibLogTrace("Too Low photometry frame %d channel %d tel %d:%lf (%lf)",iFrame+1,channel+1,tel1+1,P1,sigma2Phot[iFrame][channel][tel1]);
                    photTablePtr[iFrame][iBase].PiMultPj[channel] = amdlibBLANKING_VALUE;
                    photTablePtr[iFrame][iBase].fluxSumPiPj[channel] = amdlibBLANKING_VALUE;
                    photTablePtr[iFrame][iBase].sigma2FluxSumPiPj[channel] = amdlibBLANKING_VALUE;
                    photTablePtr[iFrame][iBase].fluxRatPiPj[channel] = amdlibBLANKING_VALUE;
                    photTablePtr[iFrame][iBase].sigma2FluxRatPiPj[channel] = amdlibBLANKING_VALUE;
                }
                else
                {
                    P1P2 = P1*P2;
                    photTablePtr[iFrame][iBase].PiMultPj[channel] = 
                    P1P2 * sumVk[channel][iBase];
                    /* Get integrated photometry and associated noise for each 
                     * beam . Involves a summation on interferometry pixels.*/
		    v1=0;
		    v2=0;
                    for (iPix = 0; iPix < nbPix; iPix++)
                    { 
		      v1 += vk[tel1][iPix];
		      v2 += vk[tel2][iPix];
                    }
		    P1 = phot[iFrame][channel][tel1] * v1;
                    sigma2P1 = sigma2Phot[iFrame][channel][tel1] * v1 * v1;

		    P2 = phot[iFrame][channel][tel2] * v2;
		    sigma2P2 = sigma2Phot[iFrame][channel][tel2] * v2 * v2;

                    /* Get integrated photometry and associated noise for both.
                     * Since we want to be able afterwards to retrieve the 
                     * individual photometries and errors, the errors of ratio
                     * is just ratio of errors, (its just a coding)*/

                    photTablePtr[iFrame][iBase].fluxSumPiPj[channel] = P1 + P2;
                    
                    photTablePtr[iFrame][iBase].sigma2FluxSumPiPj[channel] =
                    sigma2P1 + sigma2P2;          
                    
                    photTablePtr[iFrame][iBase].fluxRatPiPj[channel] = 
                    P1/P2;
                    
                    photTablePtr[iFrame][iBase].sigma2FluxRatPiPj[channel] =
                    sigma2P1/sigma2P2;
                }
            }
        }
    }
    amdlibGetInstantPhotometry_FREEALL();

    return amdlibSUCCESS;
}
#undef amdlibGetInstantPhotometry_FREEALL

/** Useful macro to free amdlibInvertGeneralizedP2v() structures */ 
#define amdlibInvertGeneralizedP2v_FREEALL()                     \
    free(mat3); free(mat2); free(mat1); free(tgp2v);   \
    amdlibFree2DArrayDouble(invcovmat);               \
    amdlibFree2DArrayUnsignedCharWrapping(badp2vPtr); \
    amdlibFree3DArrayDoubleWrapping(gp2vPtr);          \
    amdlibFree2DArrayDoubleWrapping(sigma2_ikMeanPtr);    \
    amdlibFree3DArrayDoubleWrapping(gv2pPtr);
/**
 * InvertGeneralized p2vm. 
 * 
 * @param p2v p2vm to invert.
 * @param badp2v part of bad pixels map related to the p2vm.
 * @param sigma2_ikMean the variance of the long exposure interferogram 
 * (needed to estimate the covariance matrix).
 * @param binning number of frames in the current binning.
 * @param nbChannels number of valid spectral channels treated.
 * @param nbPix number of pixels (width of p2vm).
 * @param nbBases number of baselines.
 * @param gv2p output p2vm computed: invert of p2v.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibInvertGeneralizedP2v(/* Input */
                                 double          *gp2v,
                                 unsigned char   *badp2v,
                                 double          *sigma2_ikMean,
                                 int             nbChannels,
                                 int             nbPix,
                                 int             nbBases,
                                 /* Output */
                                 double          *gv2p,
                                 amdlibERROR_MSG errMsg)
{
    int iPix; /*usually loop on nbPix*/
    int lVis;

    double *tgp2v = NULL; /* gp2v's transposed matrix */
    double *mat1 = NULL;
    double *mat2 = NULL;
    double *mat3 = NULL;
    double **invcovmat = NULL;

    double ***gp2vPtr = NULL;       /* Wrap on input gp2v */
    double **sigma2_ikMeanPtr = NULL; /* Wrap on input sigma2_ikMean */
    double ***gv2pPtr = NULL;       /* Wrap on output gv2p */
    unsigned char **badp2vPtr = NULL;


    amdlibLogTrace("amdlibInvertGeneralizedP2v()");

    tgp2v = calloc(nbPix*(2*nbBases+1), sizeof(double));
    if (tgp2v == NULL)
    {
        amdlibSetErrMsg("Allocation of tgp2v failed");
        amdlibInvertGeneralizedP2v_FREEALL();
        return amdlibFAILURE;
    }
    mat1 = calloc((2*nbBases+1)*nbPix, sizeof(double));
    if (mat1 == NULL)
    {
        amdlibSetErrMsg("Allocation of mat1 failed");
        amdlibInvertGeneralizedP2v_FREEALL();
        return amdlibFAILURE;
    }
    mat2 = calloc((2*nbBases+1)*(2*nbBases+1), sizeof(double));
    if (mat2 == NULL)
    {
        amdlibSetErrMsg("Allocation of mat2 failed");
        amdlibInvertGeneralizedP2v_FREEALL();
        return amdlibFAILURE;
    }
    mat3 = calloc(nbPix*(2*nbBases+1), sizeof(double));
    if (mat3 == NULL)
    {
        amdlibSetErrMsg("Allocation of mat3 failed");
        amdlibInvertGeneralizedP2v_FREEALL();
        return amdlibFAILURE;
    }
    invcovmat = amdlibAlloc2DArrayDouble(nbPix, nbPix, errMsg);
    if (invcovmat == NULL)
    {
        amdlibInvertGeneralizedP2v_FREEALL();
        return amdlibFAILURE;
    }

    gp2vPtr = amdlibWrap3DArrayDouble(gp2v, nbPix, (2*nbBases+1), nbChannels, errMsg);
    if (gp2vPtr == NULL)
    {
        amdlibInvertGeneralizedP2v_FREEALL();
        return amdlibFAILURE;
    }

    sigma2_ikMeanPtr = amdlibWrap2DArrayDouble(sigma2_ikMean, nbPix, nbChannels, errMsg);
    if (sigma2_ikMeanPtr == NULL)
    {
        amdlibInvertGeneralizedP2v_FREEALL();
        return amdlibFAILURE;
    }

    gv2pPtr = amdlibWrap3DArrayDouble(gv2p, nbPix, (2*nbBases+1), nbChannels, errMsg);
    if (gv2pPtr == NULL)
    {
        amdlibInvertGeneralizedP2v_FREEALL();
        return amdlibFAILURE;
    }

    badp2vPtr = amdlibWrap2DArrayUnsignedChar(badp2v, nbPix, nbChannels, errMsg);
    if (badp2vPtr == NULL)
    {
        amdlibInvertGeneralizedP2v_FREEALL();
        return amdlibFAILURE;
    }


    for (lVis=0; lVis < nbChannels; lVis++)
    {
        memset(invcovmat[0], '\0', nbPix*nbPix*sizeof(double));
        /*see amdlibInvertP2v for details */
        for (iPix = 0; iPix < nbPix; iPix++)
        {
            if (badp2vPtr[lVis][iPix] == amdlibGOOD_PIXEL_FLAG)
            {
                invcovmat[iPix][iPix] = 1./sigma2_ikMeanPtr[lVis][iPix];
            }
            else
            {
                invcovmat[iPix][iPix] = 0.0;
            }
        }
        if (amdlibProductMatrix(invcovmat[0], gp2vPtr[lVis][0], mat1, nbPix, 
                                nbPix, (2*nbBases+1)) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Product matrix error.");
            amdlibInvertGeneralizedP2v_FREEALL();
            return amdlibFAILURE;
        }

        amdlibTransposeMatrix(gp2vPtr[lVis][0], tgp2v, nbPix, (2*nbBases+1));

        if (amdlibProductMatrix(tgp2v, mat1, mat2, (2*nbBases+1), nbPix, 
                                (2*nbBases+1)) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Product matrix error.");
            amdlibInvertGeneralizedP2v_FREEALL();
            return amdlibFAILURE;
        }

        amdlibInvertMatrix(mat2, (2*nbBases+1));

        if (amdlibProductMatrix(mat2, tgp2v, mat3, (2*nbBases+1), (2*nbBases+1), 
                                nbPix)!= amdlibSUCCESS)
        {
            amdlibSetErrMsg("Product matrix error.");
            amdlibInvertGeneralizedP2v_FREEALL();
            return amdlibFAILURE;
        }
        if (amdlibProductMatrix(mat3, invcovmat[0], gv2pPtr[lVis][0], (2*nbBases+1),
                                nbPix, nbPix) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Product matrix error.");
            amdlibInvertGeneralizedP2v_FREEALL();
            return amdlibFAILURE;
        }

    }
    amdlibInvertGeneralizedP2v_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibInvertGeneralizedP2v_FREEALL

/** Useful macro to free amdlibInvertP2v() structures */ 
#define amdlibInvertP2v_FREEALL()                     \
    free(mat3); free(mat2); free(mat1); free(tp2v);   \
    amdlibFree2DArrayDouble(covmat);               \
    amdlibFree2DArrayUnsignedCharWrapping(badp2vPtr); \
    amdlibFree3DArrayDoubleWrapping(p2vPtr);          \
    amdlibFree3DArrayDoubleWrapping(cova_mkPtr);    \
    amdlibFree3DArrayDoubleWrapping(v2pPtr);
/**
 * Invert p2vm, using theoretical variance of cova_mk , frame by frame
 * 
 * @param p2v p2vm to invert.
 * @param badp2v part of bad pixels map related to the p2vm.
 * @param cova_mk the covariance of the continuum-corrected interferograms 
 * @param binning number of frames in the current binning.
 * @param nbChannels number of valid spectral channels treated.
 * @param nbPix number of pixels (width of p2vm).
 * @param nbBases number of baselines.
 * @param v2p output p2vm computed: invert of p2v.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibInvertP2v(/* Input */
                                 double          *p2v,
                                 unsigned char   *badp2v,
                                 double          *cova_mk,
                                 int             nbChannels,
                                 int             nbPix,
                                 int             nbBases,
                                 /* Output */
                                 double          *v2p,
                                 amdlibERROR_MSG errMsg)
{
    int iPix,jPix; /*usually loop on nbPix*/
    int lVis;

    double *tp2v = NULL; /* p2v's transposed matrix */
    double *mat1 = NULL;
    double *mat2 = NULL;
    double *mat3 = NULL;
    double **covmat = NULL;
    double **invcovmat = NULL;

    double ***p2vPtr = NULL;       /* Wrap on input p2v */
    double ***cova_mkPtr = NULL; /* Wrap on input cova_mk */
    double ***v2pPtr = NULL;       /* Wrap on output v2p */
    unsigned char **badp2vPtr = NULL;


    amdlibLogTrace("amdlibInvertP2v()");

    tp2v = calloc(2*nbPix*nbBases, sizeof(double));
    if (tp2v == NULL)
    {
        amdlibSetErrMsg("Allocation of tp2v failed");
        amdlibInvertP2v_FREEALL();
        return amdlibFAILURE;
    }
    mat1 = calloc(2*nbBases*nbPix, sizeof(double));
    if (mat1 == NULL)
    {
        amdlibSetErrMsg("Allocation of mat1 failed");
        amdlibInvertP2v_FREEALL();
        return amdlibFAILURE;
    }
    mat2 = calloc(2*nbBases*2*nbBases, sizeof(double));
    if (mat2 == NULL)
    {
        amdlibSetErrMsg("Allocation of mat2 failed");
        amdlibInvertP2v_FREEALL();
        return amdlibFAILURE;
    }
    mat3 = calloc(2*nbPix*nbBases, sizeof(double));
    if (mat3 == NULL)
    {
        amdlibSetErrMsg("Allocation of mat3 failed");
        amdlibInvertP2v_FREEALL();
        return amdlibFAILURE;
    }
    covmat = amdlibAlloc2DArrayDouble(nbPix, nbPix, errMsg);
    if (covmat == NULL)
    {
        amdlibInvertP2v_FREEALL();
        return amdlibFAILURE;
    }
    /*invcovmat is just an alias for covmat after inversion for readibilty w.
    * the other invertPxx() programs */
    invcovmat=covmat;
    
    p2vPtr = amdlibWrap3DArrayDouble(p2v, nbPix, 2*nbBases, nbChannels, errMsg);
    if (p2vPtr == NULL)
    {
        amdlibInvertP2v_FREEALL();
        return amdlibFAILURE;
    }
    cova_mkPtr = amdlibWrap3DArrayDouble(cova_mk, nbPix, nbPix, nbChannels,
                                         errMsg);
    if (cova_mkPtr == NULL)
    {
        amdlibInvertP2v_FREEALL();
        return amdlibFAILURE;
    }
    v2pPtr = amdlibWrap3DArrayDouble(v2p, nbPix, 2*nbBases, nbChannels,
                                     errMsg);
    if (v2pPtr == NULL)
    {
        amdlibInvertP2v_FREEALL();
        return amdlibFAILURE;
    }
    badp2vPtr = amdlibWrap2DArrayUnsignedChar(badp2v, nbPix, nbChannels, errMsg);
    if (badp2vPtr == NULL)
    {
        amdlibInvertP2v_FREEALL();
        return amdlibFAILURE;
    }

    for (lVis=0; lVis < nbChannels; lVis++)
    {
        memset(covmat[0], '\0', nbPix*nbPix*sizeof(double));
        /* Define covariance Matrix */            
        for (iPix = 0; iPix < nbPix; iPix++)
        {
            for (jPix = 0; jPix <= iPix; jPix++)
            {
                if (badp2vPtr[lVis][iPix] == amdlibGOOD_PIXEL_FLAG)
                {
                    covmat[iPix][jPix] = cova_mkPtr[lVis][iPix][jPix];
                }
                else
                {
                    if (iPix==jPix)
                    {
                        covmat[iPix][jPix] = 1.0;
                    }
                    else
                    {
                        covmat[iPix][jPix] = 0.0;
                    }
                }
                covmat[jPix][iPix] = covmat[iPix][jPix];
            }
        }
        /* compute incovmat */
        amdlibInvertMatrix(covmat[0], nbPix);
        /* set Bad Pix influence to zero */
        for (iPix = 0; iPix < nbPix; iPix++)
        {
            if (badp2vPtr[lVis][iPix] != amdlibGOOD_PIXEL_FLAG)
            {
                for (jPix = 0; jPix < nbPix; jPix++)
                {
                    invcovmat[jPix][iPix] = 0.0;
                    invcovmat[iPix][jPix] = 0.0;
                }
            }
        }


        /* computing the v2p (inverse of p2v) of this lambda,
         * following notation of report amb-igr-xx:
         * compute mat1 = cov^-1 x p2v
         * compute mat2 = tp2v x cov^-1 x p2v
         * then mat2 = (mat2)^-1
         * then mat3 = tp2v cov^-1
         * the v2p being
         * (tp2v x cov^-1 x p2v)^-1 x tp2v x cov^-1
         * is also mat2 x mat3
         */
        if (amdlibProductMatrix(invcovmat[0], p2vPtr[lVis][0], mat1, nbPix, 
                                nbPix, 2*nbBases) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Product matrix error.");
            amdlibInvertP2v_FREEALL();
            return amdlibFAILURE;
        }
        
        amdlibTransposeMatrix(p2vPtr[lVis][0], tp2v, nbPix, 2*nbBases);
        
        if (amdlibProductMatrix(tp2v, mat1, mat2, 2*nbBases, nbPix, 
                                2*nbBases) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Product matrix error.");
            amdlibInvertP2v_FREEALL();
            return amdlibFAILURE;
        }
        
        amdlibInvertMatrix(mat2, 2*nbBases);
        
        if (amdlibProductMatrix(mat2, tp2v, mat3, 2*nbBases, 2*nbBases, 
                                nbPix)!= amdlibSUCCESS)
        {
            amdlibSetErrMsg("Product matrix error.");
            amdlibInvertP2v_FREEALL();
            return amdlibFAILURE;
        }
        if (amdlibProductMatrix(mat3, invcovmat[0], v2pPtr[lVis][0], 2*nbBases,
                                nbPix, nbPix) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Product matrix error.");
            amdlibInvertP2v_FREEALL();
            return amdlibFAILURE;
        }
    }
    amdlibInvertP2v_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibInvertP2v_FREEALL


/** Useful macro to free amdlibComputeFudgeFactor() structures */ 
#define amdlibComputeFudgeFactor_FREEALL()  \
    amdlibFree2DArrayDoubleWrapping(ikMeanPtr);         \
    amdlibFree3DArrayDoubleWrapping(gv2pPtr);       
/**
 * Compute fudge factor vector to apply on photometries. 
 * 
 * @param gv2p input p2vm used.
 * @param ikMean long-exposure interferogram.
 * @param nbLVis number of spectral channels treated.
 * @param nbPix number of pixels (width of p2vm).
 * @param nbBases number of baselines.
 * @param fudge factor vector
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibComputeFudgeFactor(/* Input */
                                       double           *gv2p,
                                       double           *ikMean,
                                       int              nbLVis,
                                       int              nbPix,
                                       int              nbBases,
                                       /* Output */
                                       double           *fudge,
                                       amdlibERROR_MSG  errMsg)
{

    /* loop indexes and associated limits */
    int iPix; /*usually loop on nbPix*/
    int lVis = 0;

    double meanFudge=0.0;
    double **ikMeanPtr = NULL; /* Wrap on input ikMean */
    double ***gv2pPtr = NULL;       /* Wrap on input gv2p */

    amdlibLogTrace("amdlibComputeFudgeFactor()");

    ikMeanPtr = amdlibWrap2DArrayDouble(ikMean, nbPix, nbLVis, errMsg);
    if (ikMeanPtr == NULL)
    {
        amdlibComputeFudgeFactor_FREEALL();
        return amdlibFAILURE;
    }
    gv2pPtr = amdlibWrap3DArrayDouble(gv2p, nbPix, (2*nbBases+1), nbLVis, errMsg);
    if (gv2pPtr == NULL)
    {
        amdlibComputeFudgeFactor_FREEALL();
        return amdlibFAILURE;
    }
    if (amdlibGetUserPref(amdlibNO_FUDGE).set==amdlibTRUE)
    {
        for (lVis = 0; lVis < nbLVis; lVis++)
        {
            fudge[lVis]=1.0;
        }
    }
    else
    {
        for (lVis = 0; lVis < nbLVis; lVis++)
        {
            fudge[lVis] = 0.0;
            /* The matrix-vector product */
            for (iPix = 0; iPix < nbPix; iPix++)
            {
                fudge[lVis] += gv2pPtr[lVis][2*nbBases][iPix] * 
                ikMeanPtr[lVis][iPix];
            }
            meanFudge+=fudge[lVis];
        }
        meanFudge/=nbLVis;
        if (meanFudge==meanFudge) /*nan protect*/
        {
            amdlibLogInfoDetail("Average Linearization Factor: %f",meanFudge);
        }
        else
        {
            amdlibLogInfoDetail("Average Linearization Factor: %f",meanFudge);
            amdlibLogInfoDetail("Not-A-Number trapped, Data May be Invalid");
            amdlibLogInfoDetail("Usually Means a Bad BadPixelMap,");
            amdlibLogInfoDetail("or a Wrong Dispersion Law (LR mode).");
            amdlibLogInfoDetail("Program will be slow. use with Caution.");
            /* try to avoid nan fudges anyway */
            for (lVis = 0; lVis < nbLVis; lVis++)
            {
                if(!(fudge[lVis]==fudge[lVis]))
                {
                    fudge[lVis] = 1.0; /* */
                }
            }
        }
    }
    amdlibComputeFudgeFactor_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibComputeFudgeFactor_FREEALL
    
void amdlibFilterByChiSquare(amdlibVIS *instantCorrFlux,double **chisq, double range)
{
    double mean,rms,median;
    int nbFrames=instantCorrFlux->nbFrames;
    int nbBases=instantCorrFlux->nbBases;
    int nbLVis=instantCorrFlux->nbWlen;
    int iFrame, iBase, lVis, nbRejected=0;
    
    mean=amdlibAvgValues(nbFrames*nbLVis, chisq[0]);
    rms=amdlibRmsValues(nbFrames*nbLVis, chisq[0]);
    median=amdlibQuickSelectDble(chisq[0],nbFrames*nbLVis); 
    amdlibLogInfoDetail("Correlated Flux fit statistics: mean = %lf, rms = %lf, median = %lf",mean,rms,median);
    if (range==0) range=median+3*rms;
    if (range>0)
    {
        for (iFrame=0; iFrame < nbFrames; iFrame++)
        {
            for (lVis=0; lVis < nbLVis; lVis++)
            {
                /* reject frames with worse chi2 than range */
                if (chisq[iFrame][lVis]>range)
                {
                    nbRejected++;
                    for (iBase=0; iBase < nbBases; iBase++)
                    {
                        instantCorrFlux->table[iFrame*nbBases+iBase].flag[lVis]=amdlibTRUE;
                    }
                }
            }
        }
        if (nbRejected>0)
        {
            amdlibLogInfoDetail("(Rejecting %f %% data with fringe fit reduced Chi Square > %lf)",100.0*(float)nbRejected/(float)(nbLVis*nbFrames),range);
        }
    }
}

amdlibCOMPL_STAT amdlibCorrectFromPistonExtenuation(amdlibVIS        *instantCorrFlux,
                                                    amdlibPISTON     *instantOpd,
                                                    amdlibWAVELENGTH *wave,
                                                    amdlibBAND        band,
                                                    double            R,
                                                    amdlibERROR_MSG   errMsg)
{
    amdlibLogWarning("Correction for piston extenuation not yet implemented.",R);
    return amdlibSUCCESS;
}

#if 0
/** Useful macro to free all dynamically allocated structures */ 
#define amdlibCorrectFromPistonExtenuation_FREEALL()                       \
    amdlibFree2DArrayDoubleWrapping(instantOpdPistonPtr); \
    amdlibFree2DArrayWrapping((void **)instantCorrFluxTablePt);

amdlibCOMPL_STAT amdlibCorrectFromPistonExtenuation(amdlibVIS        *instantCorrFlux,
                                                    amdlibPISTON     *instantOpd,
                                                    amdlibWAVELENGTH *wave,
                                                    amdlibBAND        band,
                                                    double            R,
                                                    amdlibERROR_MSG   errMsg)
{
    int nbFrames=instantCorrFlux->nbFrames;
    int nbBases=instantCorrFlux->nbBases;
    int nbLVis=instantCorrFlux->nbWlen;
    int iFrame, iBase, lVis;
    double extenuation;
    amdlibDOUBLE        **instantOpdPistonPtr = NULL;
    amdlibVIS_TABLE_ENTRY **instantCorrFluxTablePt = NULL;

    amdlibLogTrace("amdlibCorrectFromPistonExtenuation()");

    if (instantOpd->bandFlag[band] == amdlibFALSE)
    {
        amdlibLogError("Piston for band '%d' not ever computed", band);
        return amdlibFAILURE;
    }

    amdlibLogInfoDetail("Correction for piston extenuation (R=%f)",R);
    instantOpdPistonPtr = 
    amdlibWrap2DArrayDouble(instantOpd->pistonOPDArray[band],
                            instantOpd->nbBases,
                            instantOpd->nbFrames, errMsg);
    if (instantOpdPistonPtr == NULL)
    {
        amdlibLogError("amdlibWrap2DArrayDouble() failed !");
        amdlibLogErrorDetail(errMsg);
        amdlibCorrectFromPistonExtenuation_FREEALL();
        return amdlibFAILURE;
    }
    instantCorrFluxTablePt = 
    (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                                nbBases, nbFrames,
                                                sizeof(amdlibVIS_TABLE_ENTRY),
                                                errMsg);
    if (instantCorrFluxTablePt == NULL)
    {
        amdlibCorrectFromPistonExtenuation_FREEALL();
        return amdlibFAILURE;
    }
    
    for (iFrame=0; iFrame < nbFrames; iFrame++)
      {
          for (iBase=0; iBase < nbBases; iBase++)
          {
              if(!amdlibCompareDouble(instantOpdPistonPtr[iFrame][iBase],amdlibBLANKING_VALUE))
              {
                  for (lVis=0; lVis < nbLVis; lVis++)
                  {
                      if(instantCorrFluxTablePt[iFrame][iBase].flag[lVis]==amdlibFALSE)
                      {
                          extenuation=(M_PI*instantOpdPistonPtr[iFrame][iBase]/
                                       (R*wave->wlen[lVis]));
                          extenuation=sin(extenuation)/extenuation;
                          instantCorrFluxTablePt[iFrame][iBase].vis[lVis].re /= extenuation;
                          instantCorrFluxTablePt[iFrame][iBase].vis[lVis].im /= extenuation; 
                          instantCorrFluxTablePt[iFrame][iBase].sigma2Vis[lVis].re /= extenuation;
                          instantCorrFluxTablePt[iFrame][iBase].sigma2Vis[lVis].im /= extenuation;
                          instantCorrFluxTablePt[iFrame][iBase].visCovRI[lVis] /= extenuation;
                          fprintf(stderr,"%lf %lf %lf\n",instantOpdPistonPtr[iFrame][iBase],extenuation,sqrt(amdlibPow2(instantCorrFluxTablePt[iFrame][iBase].vis[lVis].re)+amdlibPow2(instantCorrFluxTablePt[iFrame][iBase].vis[lVis].im)));
                      }
                  }
          }
          }
      }
    return amdlibSUCCESS;
}
#undef amdlibCorrectFromPistonExtenuation_FREEALL
#endif

#define amdlibComputeChiSquare_FREEALL()                     \
    amdlibFree2DArrayDoubleWrapping(mkPtr);         \
    amdlibFree2DArrayDoubleWrapping(sigma2_mkPtr);         \
    amdlibFree2DArrayUnsignedCharWrapping(badp2vPtr); \
    amdlibFree3DArrayDoubleWrapping(p2vPtr);          \
    amdlibFree2DArrayWrapping((void **)instantCorrFluxTablePt); \
    free(tp2v); free(model); free(mk_model);  \
    free(mat1); free(mat2);  

amdlibCOMPL_STAT amdlibComputeChiSquare(/* Input */
                                        amdlibVIS       *instantCorrFlux,
                                        double          *p2v,
                                        unsigned char   *badp2v,
                                        double          *mk,
					double          *sigma2_mk,
                                        int             iFrame,
                                        int             nbLVis,
                                        int             nbPix,
                                        int             nbBases,
                                        /* Output */
                                        double          **chisq,
                                        amdlibERROR_MSG errMsg)
{
    int iPix,jPix; /*usually loop on nbPix*/
    int lVis;
    int iBase;
    
    double *model=NULL;
    double *mk_model=NULL;
    double *tp2v = NULL;
    double *mat1, *mat2;

    double ***p2vPtr = NULL;   
    double **mkPtr = NULL; 
    double **sigma2_mkPtr = NULL; 
    amdlibVIS_TABLE_ENTRY **instantCorrFluxTablePt = NULL; /* Wrap on output instantCorrFlux->table */
    unsigned char **badp2vPtr = NULL;

    amdlibLogTrace("amdlibComputeChiSquare()");
    model= calloc(2*nbBases, sizeof(double));
    mk_model= calloc(nbPix, sizeof(double));
    tp2v=calloc(nbPix*2*nbBases, sizeof(double));
    mat1=calloc(nbPix, sizeof(double));
    mat2=calloc(nbPix, sizeof(double));
    mkPtr = amdlibWrap2DArrayDouble(mk, nbPix, nbLVis, errMsg);
    if (mkPtr == NULL)
    {
        amdlibComputeChiSquare_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_mkPtr = amdlibWrap2DArrayDouble(sigma2_mk, nbPix, nbLVis, errMsg);
    if (sigma2_mkPtr == NULL)
    {
        amdlibComputeChiSquare_FREEALL();
        return amdlibFAILURE;
    }
    p2vPtr = amdlibWrap3DArrayDouble(p2v, nbPix, 2*nbBases, nbLVis, errMsg);
    if (p2vPtr == NULL)
    {
        amdlibComputeChiSquare_FREEALL();
        return amdlibFAILURE;
    }
    badp2vPtr = amdlibWrap2DArrayUnsignedChar(badp2v, nbPix, nbLVis, errMsg);
    if (badp2vPtr == NULL)
    {
        amdlibComputeChiSquare_FREEALL();
        return amdlibFAILURE;
    }

    instantCorrFluxTablePt = 
    (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                                nbBases, instantCorrFlux->nbFrames,
                                                sizeof(amdlibVIS_TABLE_ENTRY),
                                                errMsg);
    if (instantCorrFluxTablePt == NULL)
    {
        amdlibComputeChiSquare_FREEALL();
        return amdlibFAILURE;
    }
    for (lVis=0; lVis < nbLVis; lVis++)
    {
        /*transpose p2v*/
        amdlibTransposeMatrix(p2vPtr[lVis][0], tp2v,  nbPix, 2*nbBases);
        /* create model vector */
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            model[iBase]=        instantCorrFluxTablePt[iFrame][iBase].vis[lVis].re;
            model[iBase+nbBases]=instantCorrFluxTablePt[iFrame][iBase].vis[lVis].im;
        }
        /* convert to model mks */
        if (amdlibProductMatrix( model, tp2v, mk_model, 1, 2*nbBases, nbPix) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Product matrix error.");
            amdlibComputeChiSquare_FREEALL();
            return amdlibFAILURE;
        }
	chisq[iFrame][lVis]=0.0;
	jPix = 0;
        /* compute Sum[(model-measure)^2/sigma2] */
        for (iPix = 0; iPix < nbPix; iPix++)
        {
	  if (badp2vPtr[lVis][iPix] == amdlibGOOD_PIXEL_FLAG)
	    {
	      mk_model[iPix]-=mkPtr[lVis][iPix];
	      chisq[iFrame][lVis] += amdlibPow2(mk_model[iPix])/sigma2_mkPtr[lVis][iPix];
	      jPix++;
	    }
        }
	chisq[iFrame][lVis] /= jPix-(2*nbBases)-1;
    }
    
    amdlibComputeChiSquare_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibComputeChiSquare_FREEALL

/** Useful macro to free amdlibComputeCorrFlux() structures */ 
#define amdlibComputeCorrFlux_FREEALL()             \
    amdlibFree2DArrayDoubleWrapping(mkPtr);         \
    amdlibFree2DArrayDoubleWrapping(sigma2_mkPtr);  \
    amdlibFree3DArrayDoubleWrapping(v2pPtr);        \
    amdlibFree2DArrayWrapping((void **)instantCorrFluxTablePt);
/**
 * Compute correlated flux. 
 * 
 * @param v2p input p2vm used.
 * @param mk  continumm-corrected interferograms.
 * @param sigma2_mk the associated variances.
 * @param binning number of frames in the current binning.
 * @param nbLVis number of spectral channels treated.
 * @param nbPix number of pixels (width of p2vm).
 * @param nbBases number of baselines.
 * @param vis (instant vis) structure were results are stored.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibComputeCorrFlux(/* Input */
                                       double                 *v2p,
                                       double                 *mk,
                                       double                 *sigma2_mk,
                                       int                    iFrame,
                                       int                    nbLVis,
                                       int                    nbPix,
                                       int                    nbBases,
                                       /* Output */
                                       amdlibVIS              *instantCorrFlux,
                                       amdlibERROR_MSG        errMsg)
{

    /* loop indexes and associated limits */
    int iPix; /*usually loop on nbPix*/
    int iBase; /*usually loop on nbBases*/
    int lVis = 0;

    amdlibCOMPLEX CorrFlux;
    amdlibCOMPLEX sigma2_CorrFlux;

    double        cov_RI;

    double **mkPtr = NULL; /* Wrap on input mk */
    double **sigma2_mkPtr = NULL; /* Wrap on input sigma2_mk */
    double ***v2pPtr = NULL;       /* Wrap on input v2p */
    amdlibVIS_TABLE_ENTRY **instantCorrFluxTablePt = NULL; /* Wrap on output instantCorrFlux->table */

    amdlibLogTrace("amdlibComputeCorrFlux()");

    mkPtr = amdlibWrap2DArrayDouble(mk, nbPix, nbLVis, errMsg);
    if (mkPtr == NULL)
    {
        amdlibComputeCorrFlux_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_mkPtr = amdlibWrap2DArrayDouble(sigma2_mk, nbPix, nbLVis, 
                                           errMsg);
    if (sigma2_mkPtr == NULL)
    {
        amdlibComputeCorrFlux_FREEALL();
        return amdlibFAILURE;
    }
    v2pPtr = amdlibWrap3DArrayDouble(v2p, nbPix, 2*nbBases, nbLVis, errMsg);
    if (v2pPtr == NULL)
    {
        amdlibComputeCorrFlux_FREEALL();
        return amdlibFAILURE;
    }
    instantCorrFluxTablePt = 
    (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                                nbBases, instantCorrFlux->nbFrames,
                                                sizeof(amdlibVIS_TABLE_ENTRY),
                                                errMsg);
    if (instantCorrFluxTablePt == NULL)
    {
        amdlibComputeCorrFlux_FREEALL();
        return amdlibFAILURE;
    }
    
    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        { /* begin loop for iBase */
            
            /* Initialization of the useful data */
            CorrFlux.re = 0.0;
            CorrFlux.im = 0.0;
            sigma2_CorrFlux.re = 0.0;
            sigma2_CorrFlux.im = 0.0;
            cov_RI = 0.0;
            
            /* The matrix-vector product */
            for (iPix = 0; iPix < nbPix; iPix++)
            {
                /* calculation of CorrFlux (eq 33) and
                 * save those CorrFlux into table 'instantCorrFlux'
                 */
                CorrFlux.re += v2pPtr[lVis][iBase][iPix] * 
                mkPtr[lVis][iPix];
                
                CorrFlux.im += v2pPtr[lVis][iBase+nbBases][iPix] * 
                mkPtr[lVis][iPix];
                
                /* calculation of sigma^2 of CorrFlux
                 * and the COV(R,I) see eqs 44, 45 and 46
                 * used in bias see formula AMB-IGR-019 number 36
                 */
                
                /*eq 44*/
                sigma2_CorrFlux.re += v2pPtr[lVis][iBase][iPix] * 
                v2pPtr[lVis][iBase][iPix] * 
                sigma2_mkPtr[lVis][iPix];
                
                /*eq 45*/
                sigma2_CorrFlux.im += v2pPtr[lVis][iBase+nbBases][iPix] *
                v2pPtr[lVis][iBase+nbBases][iPix] * 
                sigma2_mkPtr[lVis][iPix];
                
                cov_RI += v2pPtr[lVis][iBase][iPix] * 
                v2pPtr[lVis][iBase+nbBases][iPix] *
                sigma2_mkPtr[lVis][iPix];
                
            }
            
            /* Store For time being Raw Correlated Flux in table Vis */
            
            instantCorrFluxTablePt[iFrame][iBase].vis[lVis].re = CorrFlux.re;
            
            instantCorrFluxTablePt[iFrame][iBase].vis[lVis].im = CorrFlux.im;
            
            instantCorrFluxTablePt[iFrame][iBase].sigma2Vis[lVis].re = sigma2_CorrFlux.re;
            
            instantCorrFluxTablePt[iFrame][iBase].sigma2Vis[lVis].im = sigma2_CorrFlux.im;
            
            instantCorrFluxTablePt[iFrame][iBase].visCovRI[lVis] = cov_RI;
        }
    }
    amdlibComputeCorrFlux_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibComputeCorrFlux_FREEALL

/** Useful macro to free amdlibSumPhotometry() structures */ 
#define amdlibSumPhotometry_FREEALL()                        \
    amdlibFree2DArrayWrapping((void **)instantPhotTablePtr); \
    amdlibFree2DArrayWrapping((void **)summedPhotTablePtr); \
    amdlibFree3DArrayDouble(tmpphot);                 \
    amdlibFree3DArrayDouble(s2phot);                  
/**
 * Sum photometry. 
 * 
 * @param instantPhot photometry to sum.
 * @param iBin index of current binning.
 * @param summedPhot photometry structure were results are stored.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibSumPhotometry(amdlibPHOTOMETRY *instantPhot,
                                     int              firstFrame,
                                     int              nbFrames,
                                     int              iBin,
                                     amdlibPHOTOMETRY *summedPhot)
{
    int iFrame, runningFrame, nbGoodFrames; /* usually loop on number of frames */
    int iBase; /*usually loop on nbBases*/
    int nbBases = instantPhot->nbBases;
    int lPhot = 0;
    int nbLPhot = instantPhot->nbWlen;

    double ***tmpphot=NULL,***s2phot=NULL;
    double P1MoreP2, P1DiviP2, P1, P2,sigma2P1MoreP2,sigma2P1DiviP2,  sigma2_P1, sigma2_P2;

    amdlibPHOTOMETRY_TABLE_ENTRY **instantPhotTablePtr = NULL;
    amdlibPHOTOMETRY_TABLE_ENTRY **summedPhotTablePtr = NULL;
    static amdlibERROR_MSG errMsg;

    instantPhotTablePtr = 
        (amdlibPHOTOMETRY_TABLE_ENTRY **)amdlibWrap2DArray(instantPhot->table,
                                        nbBases, instantPhot->nbFrames,
                                        sizeof(amdlibPHOTOMETRY_TABLE_ENTRY),
                                        errMsg);
    if (instantPhotTablePtr == NULL)
    {
        amdlibSumPhotometry_FREEALL();
        return amdlibFAILURE;
    }
    summedPhotTablePtr = 
        (amdlibPHOTOMETRY_TABLE_ENTRY **)amdlibWrap2DArray(summedPhot->table,
                                        nbBases, summedPhot->nbFrames,
                                        sizeof(amdlibPHOTOMETRY_TABLE_ENTRY),
                                        errMsg);
    if (summedPhotTablePtr == NULL)
    {
        amdlibSumPhotometry_FREEALL();
        return amdlibFAILURE;
    }

    /* Allocate memory for retrieval of individual (per-beam) photometries
     * prior to summation of per-beam photometries and errors before
     * reconversion to Sum and Ratio of Fluxes and Errors*/
    tmpphot = amdlibAlloc3DArrayDouble(2, nbLPhot, nbBases, errMsg);
    s2phot = amdlibAlloc3DArrayDouble(2, nbLPhot, nbBases, errMsg);

    amdlibLogTrace("amdlibSumPhotometry()");
    
    for (lPhot = 0; lPhot < nbLPhot; lPhot++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
	    summedPhotTablePtr[iBin][iBase].PiMultPj[lPhot] = 0.0;
	    nbGoodFrames=0;
            for (iFrame = 0, runningFrame=firstFrame; iFrame < nbFrames; 
                 iFrame++, runningFrame++)
            {
                sigma2P1MoreP2 = 
                    instantPhotTablePtr[runningFrame][iBase].sigma2FluxSumPiPj[lPhot];
                if (!amdlibCompareDouble(sigma2P1MoreP2,amdlibBLANKING_VALUE))
                {
                    P1MoreP2 = 
                    instantPhotTablePtr[runningFrame][iBase].fluxSumPiPj[lPhot];
                    P1DiviP2 = 
                    instantPhotTablePtr[runningFrame][iBase].fluxRatPiPj[lPhot];
                    sigma2P1DiviP2 = 
                    instantPhotTablePtr[runningFrame][iBase].sigma2FluxRatPiPj[lPhot];
                
                    P2 = P1MoreP2 / (1.0 + P1DiviP2) ;
                    sigma2_P2 = sigma2P1MoreP2 / (1.0 + sigma2P1DiviP2) ;
                    
                    P1 = P1MoreP2*P1DiviP2/(1.0+P1DiviP2);
                    sigma2_P1 = sigma2P1MoreP2*sigma2P1DiviP2/(1.0+sigma2P1DiviP2);
                    
                    tmpphot[iBase][lPhot][0] += P1;
                    tmpphot[iBase][lPhot][1] += P2;
                    
                    s2phot[iBase][lPhot][0] += sigma2_P1;
                    s2phot[iBase][lPhot][1] += sigma2_P2;
                    
                    summedPhotTablePtr[iBin][iBase].PiMultPj[lPhot] +=
                    instantPhotTablePtr[runningFrame][iBase].PiMultPj[lPhot];
		    nbGoodFrames++;
                }
            }
	    if (nbGoodFrames==0)
	      {
		summedPhotTablePtr[iBin][iBase].fluxSumPiPj[lPhot] = amdlibBLANKING_VALUE;
		summedPhotTablePtr[iBin][iBase].sigma2FluxSumPiPj[lPhot] = amdlibBLANKING_VALUE;
		summedPhotTablePtr[iBin][iBase].fluxRatPiPj[lPhot] = amdlibBLANKING_VALUE;
		summedPhotTablePtr[iBin][iBase].sigma2FluxRatPiPj[lPhot] = amdlibBLANKING_VALUE;
		summedPhotTablePtr[iBin][iBase].PiMultPj[lPhot] = amdlibBLANKING_VALUE;
	      }
	    else
	      {
		summedPhotTablePtr[iBin][iBase].fluxSumPiPj[lPhot]=
		  tmpphot[iBase][lPhot][0]+tmpphot[iBase][lPhot][1];
		summedPhotTablePtr[iBin][iBase].sigma2FluxSumPiPj[lPhot]=
		  s2phot[iBase][lPhot][0]+s2phot[iBase][lPhot][1];
		summedPhotTablePtr[iBin][iBase].fluxRatPiPj[lPhot]=
		  tmpphot[iBase][lPhot][0]/tmpphot[iBase][lPhot][1];
		summedPhotTablePtr[iBin][iBase].sigma2FluxRatPiPj[lPhot]=
		  s2phot[iBase][lPhot][0]/s2phot[iBase][lPhot][1];
	      }
        }
    }
    
    amdlibSumPhotometry_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibSumPhotometry_FREEALL

/** Useful macro to free amdlibMeanCpxVis() structures */ 
#define amdlibMeanCpxVis_FREEALL()                         \
    free(sigma2_normedVis_im); free(sigma2_normedVis_re);  \
    free(normedVis_im); free(normedVis_re);                \
    amdlibFree2DArrayWrapping((void **)corrFluxTablePtr);  \
    amdlibFree2DArrayWrapping((void **)photTablePtr);      \
    amdlibFree2DArrayWrapping((void **)normedVisTablePtr);
/**
 * Compute averaged, normalized visibilites from a sequence (bin) of
 * instanteous correlated fluxes. 
 * 
 * @param instantCorrFlux the bin of  instanteous correlated fluxes.
 * @param photometry the associated bin of instantenous fluxes.
 * @param iBin index of the results to be written in the output (binned) 
 * normedVis structure.
 * @param normedVis structure were results are stored.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibMeanCpxVis(amdlibVIS         *instantCorrFlux,
                                  amdlibPHOTOMETRY  *photometry,
                                  int               firstFrame,
                                  int               nbFrames,
                                  int               iBin,
                                  amdlibVIS         *normedVis)
{
    int iFrame, runningFrame, nbGoodFrames; /* usually loop on number of frames */
    int iBase; /*usually loop on nbBases*/
    int nbBases = instantCorrFlux->nbBases;

    int lVis = 0;
    int nbLVis = instantCorrFlux->nbWlen;

    amdlibCOMPLEX CorrFlux, sigma2_CorrFlux;

    double P1P2;
    double *normedVis_re = NULL;
    double *normedVis_im = NULL;
    double *sigma2_normedVis_re = NULL;
    double *sigma2_normedVis_im = NULL;

    amdlibVIS_TABLE_ENTRY        **corrFluxTablePtr = NULL;
    amdlibPHOTOMETRY_TABLE_ENTRY **photTablePtr = NULL;
    amdlibVIS_TABLE_ENTRY        **normedVisTablePtr = NULL;
    static amdlibERROR_MSG errMsg;


    amdlibLogTrace("amdlibMeanCpxVis()");

    /* Allocate memory for intermediate structures */
    normedVis_re = calloc(nbFrames,sizeof(double));
    if (normedVis_re == NULL)
    {
        amdlibMeanCpxVis_FREEALL();
        return amdlibFAILURE;
    }
    normedVis_im = calloc(nbFrames,sizeof(double));
    if (normedVis_im == NULL)
    {
        amdlibMeanCpxVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_normedVis_re = calloc(nbFrames,sizeof(double));
    if (sigma2_normedVis_re == NULL)
    {
        amdlibMeanCpxVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_normedVis_im = calloc(nbFrames,sizeof(double));
    if (sigma2_normedVis_im == NULL)
    {
        amdlibMeanCpxVis_FREEALL();
        return amdlibFAILURE;
    }

    /* And wrap structures specified as parametres */
    corrFluxTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                                    instantCorrFlux->nbBases, 
                                                    instantCorrFlux->nbFrames,
                                                    sizeof(amdlibVIS_TABLE_ENTRY),
                                                    errMsg);
    if (corrFluxTablePtr == NULL)
    {
        amdlibMeanCpxVis_FREEALL();
        return amdlibFAILURE;
    }
    photTablePtr = 
        (amdlibPHOTOMETRY_TABLE_ENTRY **)amdlibWrap2DArray(photometry->table,
                                        nbBases, photometry->nbFrames,
                                        sizeof(amdlibPHOTOMETRY_TABLE_ENTRY),
                                        errMsg);
    if (photTablePtr == NULL)
    {
        amdlibMeanCpxVis_FREEALL();
        return amdlibFAILURE;
    }
    normedVisTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(normedVis->table,
                                                nbBases, normedVis->nbFrames,
                                                sizeof(amdlibVIS_TABLE_ENTRY),
                                                errMsg);
    if (normedVisTablePtr == NULL)
    {
        amdlibMeanCpxVis_FREEALL();
        return amdlibFAILURE;
    }

    /* Compute averaged, normalized visibilites */
    for (lVis = 0; lVis < nbLVis; lVis++)
    { 
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            nbGoodFrames =0;
            for (iFrame = 0, runningFrame=firstFrame; iFrame < nbFrames; 
                 iFrame++, runningFrame++)
            {
                P1P2 = photTablePtr[runningFrame][iBase].PiMultPj[lVis];
                if ((!amdlibCompareDouble(P1P2, amdlibBLANKING_VALUE)) && 
                    (corrFluxTablePtr[runningFrame][iBase].flag[lVis] ==
                     amdlibFALSE))
                {
                    CorrFlux.re = 
                        corrFluxTablePtr[runningFrame][iBase].vis[lVis].re;
                    CorrFlux.im = 
                        corrFluxTablePtr[runningFrame][iBase].vis[lVis].im;
                    
                    sigma2_CorrFlux.re = 
                    corrFluxTablePtr[runningFrame][iBase].sigma2Vis[lVis].re;
                    sigma2_CorrFlux.im =
                    corrFluxTablePtr[runningFrame][iBase].sigma2Vis[lVis].im;
                    
                    normedVis_re[nbGoodFrames] = CorrFlux.re / 
                    (2 * amdlibSignedSqrt(P1P2));
                    normedVis_im[nbGoodFrames] = CorrFlux.im / 
                    (2 * amdlibSignedSqrt(P1P2));
                    sigma2_normedVis_re[nbGoodFrames] = sigma2_CorrFlux.re / 
                        (4 * P1P2);
                    sigma2_normedVis_im[nbGoodFrames] = sigma2_CorrFlux.im / 
                        (4 * P1P2);
                    nbGoodFrames++;
                }
            }
            if (nbGoodFrames > 0)
            {
                /* Store results */
                normedVisTablePtr[iBin][iBase].flag[lVis]=amdlibFALSE;
                normedVisTablePtr[iBin][iBase].vis[lVis].re =
                amdlibAvgValues(nbGoodFrames, normedVis_re);
                
                normedVisTablePtr[iBin][iBase].vis[lVis].im =
                amdlibAvgValues(nbGoodFrames, normedVis_im); 
                
                normedVisTablePtr[iBin][iBase].sigma2Vis[lVis].re =
                amdlibAvgValues(nbGoodFrames, sigma2_normedVis_re); 
                
                normedVisTablePtr[iBin][iBase].sigma2Vis[lVis].im =
                amdlibAvgValues(nbGoodFrames, sigma2_normedVis_im);
                normedVisTablePtr[iBin][iBase].flag[lVis]=amdlibFALSE;
            }
            else
            {
                normedVisTablePtr[iBin][iBase].vis[lVis].re=amdlibBLANKING_VALUE;
                normedVisTablePtr[iBin][iBase].vis[lVis].im=amdlibBLANKING_VALUE;
                normedVisTablePtr[iBin][iBase].sigma2Vis[lVis].re =amdlibBLANKING_VALUE;
                normedVisTablePtr[iBin][iBase].sigma2Vis[lVis].im =amdlibBLANKING_VALUE;
                normedVisTablePtr[iBin][iBase].flag[lVis]=amdlibTRUE;
            }
            
        }
    }

    amdlibMeanCpxVis_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibMeanCpxVis_FREEALL

/** Useful macro to free amdlibBinSqVis() structures */ 
#define amdlibBinSqVis_FREEALL() free(I); free(R); free(sigma2_R); \
    free(sigma2_I); free(P1); free(P2); free(sigma2_P1); \
    free(sigma2_P2); free(cov_RI); free(instantP1P2); free(unbiased_CV);  \
    amdlibFree2DArrayWrapping((void **)corrFluxTablePtr);                \
    amdlibFree2DArrayWrapping((void **)photTablePtr);                    \
    amdlibFree2DArrayWrapping((void **)vis2TablePtr);

/**
 * Compute averaged, normalized, debiassed, squared visibilities from a 
 * sequence (bin) of instanteous correlated fluxes. 
 * 
 * @param instantCorrFlux the bin of  instanteous correlated fluxes.
 * @param photometry the associated bin of instantenous fluxes.
 * @param iBin index of the results to be written in the output 
 * (binned) vis2 structure.
 * @param band the current spectral band treated.
 * @param selection structure containing all data relative to frame selection.
 * @param errorType indicates wether the noise figures are estimated 
 * statistically from the sequence of photometries during the bin 
 * (amdlibSTATISTICAL_ERROR) or theoretically by the poisson error on N photons
 * (amdlibTHEORETICAL_ERROR). The latter is forced obviously when the binsize
 * is 1.
 * @param vis2 structure were results are stored.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibBinSqVis(/* Input */
                                    amdlibVIS         *instantCorrFlux,
                                    amdlibPHOTOMETRY  *photometry,
                                    int               firstFrame,
                                    int               nbFrames,
                                    int               iBin,
                                    amdlibBAND        band,
                                    amdlibERROR_TYPE  errorType,
                                    /* Output */
                                    amdlibVIS2        *vis2,
                                    amdlibERROR_MSG   errMsg)
{

    int iFrame, runningFrame, nbGoodFrames; /* usually loops on number of frames */
    int iBase; /*usually loop on nbBases*/
    int nbBases = instantCorrFlux->nbBases;
    int lVis = 0;
    int nbLVis = instantCorrFlux->nbWlen;

    double P1MoreP2, P1DiviP2, sigma2P1MoreP2, sigma2P1DiviP2;
    double biased_CV;
    double bias;

    double *R = NULL;
    double *I = NULL;
    double *sigma2_R = NULL;
    double *sigma2_I = NULL;
    double *P1 = NULL;
    double *P2 = NULL;
    double *sigma2_P1 = NULL;
    double *sigma2_P2 = NULL;
    double *cov_RI = NULL;
    double *instantP1P2 = NULL;
    double *unbiased_CV = NULL;
    double averageP1P2;
    double sigma2_P1P2;
    double average_CV;
    double sigma2_CV;
    double averageV2;
    double sigma2_V2;
    double sigma_V2;

    amdlibVIS_TABLE_ENTRY **corrFluxTablePtr = NULL;
    amdlibPHOTOMETRY_TABLE_ENTRY **photTablePtr = NULL;
    amdlibVIS2_TABLE_ENTRY **vis2TablePtr = NULL;

    amdlibLogTrace("amdlibBinSqVis()");

    /* Allocate memory for intermediate structures, of maximum size in frames */
    R = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (R == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    I = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (I == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_R = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (sigma2_R == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_I = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (sigma2_I == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }

    P1 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (P1 == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    P2 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (P2 == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_P1 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (sigma2_P1 == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_P2 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (sigma2_P2 == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }

    cov_RI = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (cov_RI == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    instantP1P2 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (instantP1P2 == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    unbiased_CV = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (unbiased_CV == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
 
    /* Wrap structures given as parametres */
    corrFluxTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                            nbBases, instantCorrFlux->nbFrames,
                                            sizeof(amdlibVIS_TABLE_ENTRY),
                                            errMsg);
    if (corrFluxTablePtr == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    photTablePtr = 
        (amdlibPHOTOMETRY_TABLE_ENTRY **)amdlibWrap2DArray(photometry->table,
                                nbBases, photometry->nbFrames,
                                sizeof(amdlibPHOTOMETRY_TABLE_ENTRY),
                                errMsg);
    if (photTablePtr == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }
    vis2TablePtr = 
        (amdlibVIS2_TABLE_ENTRY **)amdlibWrap2DArray(vis2->table,
                                                nbBases, vis2->nbFrames,
                                                sizeof(amdlibVIS2_TABLE_ENTRY),
                                                errMsg);
    if (vis2TablePtr == NULL)
    {
        amdlibBinSqVis_FREEALL();
        return amdlibFAILURE;
    }

    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            averageP1P2 = 0.0;          
            average_CV = 0.0;
	    nbGoodFrames=0;
            for (iFrame = 0, runningFrame=firstFrame; iFrame < nbFrames; 
                 iFrame++, runningFrame++)
            {
                if (!amdlibCompareDouble(
                            photTablePtr[runningFrame][iBase].PiMultPj[lVis], 
                            amdlibBLANKING_VALUE) &&
                    (corrFluxTablePtr[runningFrame][iBase].flag[lVis] == 
                        amdlibFALSE))
		  {
		    
		    P1MoreP2 = 
		      photTablePtr[runningFrame][iBase].fluxSumPiPj[lVis];
		    
		    sigma2P1MoreP2 = 
		      photTablePtr[runningFrame][iBase].sigma2FluxSumPiPj[lVis];
		    
		    P1DiviP2 = 
		      photTablePtr[runningFrame][iBase].fluxRatPiPj[lVis];
		    sigma2P1DiviP2 = 
		      photTablePtr[runningFrame][iBase].sigma2FluxRatPiPj[lVis];
		    
		    P2[nbGoodFrames]  = P1MoreP2 / (1.0 + P1DiviP2);
		    
		    sigma2_P2[nbGoodFrames]  = sigma2P1MoreP2 / (1.0 + sigma2P1DiviP2);
		    
		    P1[nbGoodFrames]  = P1MoreP2*P1DiviP2/(1.0+P1DiviP2);
		    
		    sigma2_P1[nbGoodFrames] = sigma2P1MoreP2 * sigma2P1DiviP2 / 
		      (1.0 + sigma2P1DiviP2);
		    
		    R[nbGoodFrames] =
                    corrFluxTablePtr[runningFrame][iBase].vis[lVis].re;
                    
                    I[nbGoodFrames] =
                    corrFluxTablePtr[runningFrame][iBase].vis[lVis].im;
                    
                    sigma2_R[nbGoodFrames] = 
                    corrFluxTablePtr[runningFrame][iBase].sigma2Vis[lVis].re;
                    
                    sigma2_I[nbGoodFrames] = 
                    corrFluxTablePtr[runningFrame][iBase].sigma2Vis[lVis].im;
                
                    cov_RI[nbGoodFrames] = 
                    corrFluxTablePtr[runningFrame][iBase].visCovRI[lVis];
                    
                    /* computation of the unbiased, weighted squared
                     * visibilities
                     * Clm^2 unbiased_CV: records C^2(iFrame) for each frame for
                     * this wavelength we need this expression to calculate
                     * sigma_V2
                     */
                    biased_CV = amdlibPow2(R[nbGoodFrames]) + 
                        amdlibPow2(I[nbGoodFrames]);
                    
                    /* bias: see formula AMB-IGR-019 number 36 */
                    bias = sigma2_R[nbGoodFrames] + 
                    sigma2_I[nbGoodFrames];
                    
                    /*
                     * <R^2 + I^2 - bias>_fram =
                     * <R^2>_fram + <I^2>_fram - <bias>_fram averaged =
                     * <C^2 - bias>
                     */
                    unbiased_CV[nbGoodFrames] = biased_CV - bias;
                
                    /* computation of instantP1P2 for every frame/baseline for 
                     * this wavelength. We need this expression to calculate 
                     * sigma_V2 below.  Remember also averageP1P2, the average 
                     * photometry as defined below */
                    
                    instantP1P2[nbGoodFrames] =
                    photTablePtr[runningFrame][iBase].PiMultPj[lVis];
                    
                    averageP1P2 += instantP1P2[nbGoodFrames];
                    average_CV += unbiased_CV[nbGoodFrames];
                    nbGoodFrames++;
                }
            }
            if (nbGoodFrames>0)
            {
                /* compute average of P1P2 & CV */
                averageP1P2 /= nbGoodFrames;
                average_CV /= nbGoodFrames;
                
                /* computation of the average of V2 for this wavelength */
                averageV2 = average_CV / (4 * averageP1P2);
                
                /* For statistical errors we need to have only the selected values 
                 * as contiguous tables */
                if (errorType == amdlibSTATISTICAL_ERROR && nbGoodFrames>1 )
                {
                    /* estimate sigma2_V2 as in Bram's formula 40. Since size 
                     * of arrays change with the number of frames selected per 
                     * baseline, use amdlibComputeCov instead of 
                     * amdlibComputeMatrixCov */ 
                    sigma2_CV = amdlibComputeCov(unbiased_CV, 
                                                 unbiased_CV, 
                                                 nbGoodFrames);
                    
                    sigma2_P1P2 = amdlibComputeCov(instantP1P2, 
                                                   instantP1P2, 
                                                   nbGoodFrames);  
                    
                }
                else /* THEORETICAL ERROR */
                {
                    /* We have to recompute averageP1P2 also. reason: 
                     * averageP1P2 above is an average of 
                     * P1*P2*sumvk. But sigma2_P1P2 retrieved above from P1+P2
                     * and P1/P2 does not know about sumvk. P1 is in fact 
                     * P1*sumvk1 and P2 is P2*sumvk2. So we recompute 
                     * averageP1P2 as the average of P1*P2, that goes with
                     * the theoretical sigma2_P1P2. */
                    sigma2_CV = 0.0;
                    averageP1P2 = 0.0;
                    sigma2_P1P2 = 0.0;
                    for (iFrame = 0; iFrame < nbGoodFrames; iFrame++)
                    { /* begin loop for iFrame, selection has been done */
                        sigma2_CV += 
                        4 * ((amdlibPow2(R[iFrame])*
                              sigma2_R[iFrame]) +
                             (amdlibPow2(I[iFrame])*
                              sigma2_I[iFrame]) +
                             2 * R[iFrame] * I[iFrame] * 
                             cov_RI[iFrame]);

			averageP1P2 += P1[iFrame]*P2[iFrame];
			sigma2_P1P2 += amdlibPow2(P2[iFrame]) * sigma2_P1[iFrame] + 
			  amdlibPow2(P1[iFrame]) * sigma2_P2[iFrame];   
                    }
		    sigma2_P1P2 /= nbGoodFrames;
		    averageP1P2 /= nbGoodFrames;
                    sigma2_CV /= nbGoodFrames;
		}

                /* whatever the errorType, computation of sigma_V2 writes the same */
                sigma2_V2 = amdlibPow2(averageV2) / nbGoodFrames * 
                ((sigma2_CV / amdlibPow2(average_CV)) +
                 (sigma2_P1P2 / amdlibPow2(averageP1P2)));
                
                if(sigma2_V2 >= 0)
                {
		  sigma_V2 = sqrt(sigma2_V2);
		  vis2TablePtr[iBin][iBase].vis2[lVis] = averageV2;
		  vis2TablePtr[iBin][iBase].vis2Error[lVis] = sigma_V2;
		  vis2TablePtr[iBin][iBase].flag[lVis]=amdlibFALSE;
                }
                else
		{
		  vis2TablePtr[iBin][iBase].vis2[lVis] = amdlibBLANKING_VALUE;
		  vis2TablePtr[iBin][iBase].vis2Error[lVis] = amdlibBLANKING_VALUE;
		  vis2TablePtr[iBin][iBase].flag[lVis]=amdlibTRUE;
		  amdlibLogTrace("Flagging bad Vis2Data baseline %d frame %d channel %d",iBase+1,iBin+1,lVis+1);
		}
            
            }
            else
            {
	      vis2TablePtr[iBin][iBase].vis2[lVis] = amdlibBLANKING_VALUE;
	      vis2TablePtr[iBin][iBase].vis2Error[lVis] = amdlibBLANKING_VALUE;
	      vis2TablePtr[iBin][iBase].flag[lVis]=amdlibTRUE;
	      amdlibLogTrace("No Data for Vis2Data baseline %d frame %d channel %d",iBase+1,iBin+1,lVis+1);
            }
        }
    }

    amdlibBinSqVis_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibBinSqVis_FREEALL

/** Useful macro to free amdlibComputeSqVis() structures */ 
#define amdlibComputeSqVis_FREEALL() free(I); free(R); free(sigma2_R); \
    free(sigma2_I); free(P1); free(P2); free(sigma2_P1); \
    free(sigma2_P2); free(cov_RI); free(instantP1P2); free(unbiased_CV);  \
    amdlibFree2DArrayWrapping((void **)corrFluxTablePtr);                \
    amdlibFree2DArrayWrapping((void **)photTablePtr);                    \
    amdlibFree2DArrayWrapping((void **)vis2TablePtr);

/**
 * Compute averaged, normalized, debiassed, squared visibilities from a 
 * sequence (bin) of instanteous correlated fluxes. 
 * 
 * @param instantCorrFlux the bin of  instanteous correlated fluxes.
 * @param photometry the associated bin of instantenous fluxes.
 * @param iBin index of the results to be written in the output 
 * (binned) vis2 structure.
 * @param band the current spectral band treated.
 * @param selection structure containing all data relative to frame selection.
 * @param errorType indicates wether the noise figures are estimated 
 * statistically from the sequence of photometries during the bin 
 * (amdlibSTATISTICAL_ERROR) or theoretically by the poisson error on N photons
 * (amdlibTHEORETICAL_ERROR). The latter is forced obviously when the binsize
 * is 1.
 * @param vis2 structure were results are stored.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */

amdlibCOMPL_STAT amdlibComputeSqVis(/* Input */
                                    amdlibVIS         *instantCorrFlux,
                                    amdlibPHOTOMETRY  *photometry,
                                    int               iBin,
                                    amdlibBAND        band,
                                    amdlibSELECTION   *selection,
                                    amdlibERROR_TYPE  errorType,
                                    /* Output */
                                    amdlibVIS2        *vis2,
                                    amdlibERROR_MSG   errMsg)
{

    int iFrame, nbGoodFrames; /* usually loops on number of frames */
    int nbFrames = instantCorrFlux->nbFrames;
    int iBase; /*usually loop on nbBases*/
    int nbBases = instantCorrFlux->nbBases;
    int lVis = 0;
    int nbLVis = instantCorrFlux->nbWlen;

    double P1MoreP2, P1DiviP2, sigma2P1MoreP2, sigma2P1DiviP2;
    double biased_CV;
    double bias;

    double *R = NULL;
    double *I = NULL;
    double *sigma2_R = NULL;
    double *sigma2_I = NULL;
    double *P1 = NULL;
    double *P2 = NULL;
    double *sigma2_P1 = NULL;
    double *sigma2_P2 = NULL;
    double *cov_RI = NULL;
    double *instantP1P2 = NULL;
    double *unbiased_CV = NULL;
    double averageP1P2;
    double sigma2_P1P2;
    double average_CV;
    double sigma2_CV;
    double averageV2;
    double sigma2_V2;
    double sigma_V2;

    amdlibVIS_TABLE_ENTRY **corrFluxTablePtr = NULL;
    amdlibPHOTOMETRY_TABLE_ENTRY **photTablePtr = NULL;
    amdlibVIS2_TABLE_ENTRY **vis2TablePtr = NULL;

    amdlibLogTrace("amdlibComputeSqVis()");

    /* Allocate memory for intermediate structures, of maximum size in frames */
    R = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (R == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    I = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (I == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_R = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (sigma2_R == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_I = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (sigma2_I == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }

    P1 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (P1 == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    P2 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (P2 == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_P1 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (sigma2_P1 == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    sigma2_P2 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (sigma2_P2 == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }

    cov_RI = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (cov_RI == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    instantP1P2 = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (instantP1P2 == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    unbiased_CV = calloc(nbFrames, sizeof(amdlibDOUBLE));
    if (unbiased_CV == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
 
    /* Wrap structures given as parametres */
    corrFluxTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                            nbBases, instantCorrFlux->nbFrames,
                                            sizeof(amdlibVIS_TABLE_ENTRY),
                                            errMsg);
    if (corrFluxTablePtr == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    photTablePtr = 
        (amdlibPHOTOMETRY_TABLE_ENTRY **)amdlibWrap2DArray(photometry->table,
                                nbBases, photometry->nbFrames,
                                sizeof(amdlibPHOTOMETRY_TABLE_ENTRY),
                                errMsg);
    if (photTablePtr == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }
    vis2TablePtr = 
        (amdlibVIS2_TABLE_ENTRY **)amdlibWrap2DArray(vis2->table,
                                                nbBases, vis2->nbFrames,
                                                sizeof(amdlibVIS2_TABLE_ENTRY),
                                                errMsg);
    if (vis2TablePtr == NULL)
    {
        amdlibComputeSqVis_FREEALL();
        return amdlibFAILURE;
    }

    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            averageP1P2 = 0.0;          
            average_CV = 0.0;
	    nbGoodFrames=0;
            for (iFrame = 0; iFrame < nbFrames; iFrame++)
            {
                if ((selection->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE) &&
                    !amdlibCompareDouble(
		             photTablePtr[iFrame][iBase].PiMultPj[lVis],
		             amdlibBLANKING_VALUE) && 
                    (corrFluxTablePtr[iFrame][iBase].flag[lVis]==amdlibFALSE))
                {
		    P1MoreP2 = 
		      photTablePtr[iFrame][iBase].fluxSumPiPj[lVis];

		    sigma2P1MoreP2 = 
		      photTablePtr[iFrame][iBase].sigma2FluxSumPiPj[lVis];

		    P1DiviP2 = 
		      photTablePtr[iFrame][iBase].fluxRatPiPj[lVis];
		    sigma2P1DiviP2 = 
		      photTablePtr[iFrame][iBase].sigma2FluxRatPiPj[lVis];

		    P2[nbGoodFrames]  = P1MoreP2 / (1.0 + P1DiviP2);
		    
		    sigma2_P2[nbGoodFrames]  = sigma2P1MoreP2 / (1.0 + sigma2P1DiviP2);
		    
		    P1[nbGoodFrames]  = P1MoreP2*P1DiviP2/(1.0+P1DiviP2);
		    
		    sigma2_P1[nbGoodFrames] = sigma2P1MoreP2 * sigma2P1DiviP2 / 
		      (1.0 + sigma2P1DiviP2);

                    R[nbGoodFrames] =
                    corrFluxTablePtr[iFrame][iBase].vis[lVis].re;
                    
                    I[nbGoodFrames] =
                    corrFluxTablePtr[iFrame][iBase].vis[lVis].im;
                    
                    sigma2_R[nbGoodFrames] = 
                    corrFluxTablePtr[iFrame][iBase].sigma2Vis[lVis].re;
                    
                    sigma2_I[nbGoodFrames] = 
                    corrFluxTablePtr[iFrame][iBase].sigma2Vis[lVis].im;
                
                    cov_RI[nbGoodFrames] = 
                    corrFluxTablePtr[iFrame][iBase].visCovRI[lVis];
                    
                    /* computation of the unbiased, weighted squared
                     * visibilities
                     * Clm^2 unbiased_CV: records C^2(iFrame) for each frame for
                     * this wavelength we need this expression to calculate
                     * sigma_V2
                     */
                    biased_CV = amdlibPow2(R[nbGoodFrames]) + 
                        amdlibPow2(I[nbGoodFrames]);
                    
                    /* bias: see formula AMB-IGR-019 number 36 */
                    bias = sigma2_R[nbGoodFrames] + 
                    sigma2_I[nbGoodFrames];
                    
                    /*
                     * <R^2 + I^2 - bias>_fram =
                     * <R^2>_fram + <I^2>_fram - <bias>_fram averaged =
                     * <C^2 - bias>
                     */
                    unbiased_CV[nbGoodFrames] = biased_CV - bias;
                
                    /* computation of instantP1P2 for every frame/baseline for 
                     * this wavelength. We need this expression to calculate 
                     * sigma_V2 below.  Remember also averageP1P2, the average 
                     * photometry as defined below */
                    
                    instantP1P2[nbGoodFrames] =
                    photTablePtr[iFrame][iBase].PiMultPj[lVis];
                    
                    averageP1P2 += instantP1P2[nbGoodFrames];
                    average_CV += unbiased_CV[nbGoodFrames];
                    nbGoodFrames++;
                }
            }
            if (nbGoodFrames>0)
            {
                /* compute average of P1P2 & CV */
                averageP1P2 /= nbGoodFrames;
                average_CV /= nbGoodFrames;
                
                /* computation of the average of V2 for this wavelength */
                averageV2 = average_CV / (4 * averageP1P2);
                
                /* For statistical errors we need to have only the selected values 
                 * as contiguous tables */
                if (errorType == amdlibSTATISTICAL_ERROR && nbGoodFrames>1 )
                {
                    /* estimate sigma2_V2 as in Bram's formula 40. Since size 
                     * of arrays change with the number of frames selected per 
                     * baseline, use amdlibComputeCov instead of 
                     * amdlibComputeMatrixCov */ 
                    sigma2_CV = amdlibComputeCov(unbiased_CV, 
                                                 unbiased_CV, 
                                                 nbGoodFrames);
                    
                    sigma2_P1P2 = amdlibComputeCov(instantP1P2, 
                                                   instantP1P2, 
                                                   nbGoodFrames);  
                    
                }
                else /* THEORETICAL ERROR */
                {
                    /* We have to recompute averageP1P2 also. reason: 
                     * averageP1P2 above is an average of 
                     * P1*P2*sumvk. But sigma2_P1P2 retrieved above from P1+P2
                     * and P1/P2 does not know about sumvk. P1 is in fact 
                     * P1*sumvk1 and P2 is P2*sumvk2. So we recompute 
                     * averageP1P2 as the average of P1*P2, that goes with
                     * the theoretical sigma2_P1P2. */
                    sigma2_CV = 0.0;
                    averageP1P2 = 0.0;
                    sigma2_P1P2 = 0.0;
                    for (iFrame = 0; iFrame < nbGoodFrames; iFrame++)
                    { /* begin loop for iFrame, selection has been done */
                        sigma2_CV += 
                        4 * ((amdlibPow2(R[iFrame])*
                              sigma2_R[iFrame]) +
                             (amdlibPow2(I[iFrame])*
                              sigma2_I[iFrame]) +
                             2 * R[iFrame] * I[iFrame] * 
                             cov_RI[iFrame]);

			averageP1P2 += P1[iFrame]*P2[iFrame];
			sigma2_P1P2 += amdlibPow2(P2[iFrame]) * sigma2_P1[iFrame] + 
			  amdlibPow2(P1[iFrame]) * sigma2_P2[iFrame];   
                    }
		    sigma2_P1P2 /= nbGoodFrames;
		    averageP1P2 /= nbGoodFrames;
                    sigma2_CV /= nbGoodFrames;
		}

                /* whatever the errorType, computation of sigma_V2 writes the same */
                sigma2_V2 = amdlibPow2(averageV2) / nbGoodFrames * 
                ((sigma2_CV / amdlibPow2(average_CV)) +
                 (sigma2_P1P2 / amdlibPow2(averageP1P2)));
                
                if(sigma2_V2 >= 0)
                {
		  sigma_V2 = sqrt(sigma2_V2);
		  vis2TablePtr[iBin][iBase].vis2[lVis] = averageV2;
		  vis2TablePtr[iBin][iBase].vis2Error[lVis] = sigma_V2;
		  vis2TablePtr[iBin][iBase].flag[lVis]=amdlibFALSE;
                }
                else
		{
		  vis2TablePtr[iBin][iBase].vis2[lVis] = amdlibBLANKING_VALUE;
		  vis2TablePtr[iBin][iBase].vis2Error[lVis] = amdlibBLANKING_VALUE;
		  vis2TablePtr[iBin][iBase].flag[lVis]=amdlibTRUE;
		  amdlibLogTrace("Flagging bad Vis2Data baseline %d frame %d channel %d",iBase+1,iBin+1,lVis+1);
		}
            
            }
            else
            {
	      vis2TablePtr[iBin][iBase].vis2[lVis] = amdlibBLANKING_VALUE;
	      vis2TablePtr[iBin][iBase].vis2Error[lVis] = amdlibBLANKING_VALUE;
	      vis2TablePtr[iBin][iBase].flag[lVis]=amdlibTRUE;
	      amdlibLogTrace("No Data for Vis2Data baseline %d frame %d channel %d",iBase+1,iBin+1,lVis+1);
            }
        }
    }

    amdlibComputeSqVis_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibComputeSqVis_FREEALL

/** Useful macro to free amdlibAverageVis() structures */ 
#define amdlibAverageVisData_FREEALL()                         \
    amdlibFree2DArrayWrapping((void **)cpxVisTablePtr);        \
    amdlibFree2DArrayWrapping((void **)visTablePtr);   

/**
 * Update complex pseudo-visibilities in output table by averaging values and 
 * computing rms
 *
 * @param instantCorrFlux the bin of  instanteous correlated fluxes.
 * @param band the current band
 * @param iBin index of the results to be written in the output 
 * (binned) vis structure.
 * @param selection the selection structure giving list of selected frames.
 * @param vis structure were results are stored.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibAverageVis(amdlibVIS              *instantCorrFlux,
                                  int                    iBin,
                                  amdlibBAND             band,
                                  amdlibSELECTION        *selection,
                                  /* Output */
                                  amdlibVIS              *vis,
                                  amdlibERROR_MSG        errMsg)
{

    int iFrame; /* usually loop on number of frames */
    int nbFrames = instantCorrFlux->nbFrames;
    int iBase; /*usually loop on nbBases*/
    int nbBases = instantCorrFlux->nbBases;
    int lVis = 0;
    int nbLVis = instantCorrFlux->nbWlen;
    int nbGoodFrames=0;

    amdlibVIS_TABLE_ENTRY **cpxVisTablePtr = NULL; 
    amdlibVIS_TABLE_ENTRY **visTablePtr = NULL; 
    amdlibCOMPLEX rmsVis, cpxVis;

    
    amdlibLogTrace("amdlibAverageVis()");

    /* Wrap structures given as parametres */
    cpxVisTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                                nbBases, nbFrames,
                                                sizeof(amdlibVIS_TABLE_ENTRY),
                                                errMsg);
    if (cpxVisTablePtr == NULL)
    {
        amdlibAverageVisData_FREEALL();
        return amdlibFAILURE;
    }

    visTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(vis->table,
                                                nbBases, vis->nbFrames,
                                                sizeof(amdlibVIS_TABLE_ENTRY),
                                                errMsg);
    if (visTablePtr == NULL)
    {
        amdlibAverageVisData_FREEALL();
        return amdlibFAILURE;
    }
    /* Finally, update complex pseudo-visibilities in output table by averaging
     * values and computing rms. Reuse cpxVis and rmsVis for intermediate 
     * storage */
    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            if (selection->band[band].nbSelectedFrames[iBase] != 0)
            {
                cpxVis.re = 0;
                cpxVis.im = 0;
                for (iFrame = 0, nbGoodFrames = 0; iFrame < nbFrames; iFrame++)
                {
                    if ((selection->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE)&&(cpxVisTablePtr[iFrame][iBase].flag[lVis] == amdlibFALSE))
                    {
                        cpxVis.re += cpxVisTablePtr[iFrame][iBase].vis[lVis].re;
                        cpxVis.im += cpxVisTablePtr[iFrame][iBase].vis[lVis].im;
                        nbGoodFrames++;
                    }
                }
                cpxVis.re /= nbGoodFrames;
                cpxVis.im /= nbGoodFrames;
                visTablePtr[iBin][iBase].vis[lVis].re=cpxVis.re;
                visTablePtr[iBin][iBase].vis[lVis].im=cpxVis.im;
                rmsVis.re = 0;
                rmsVis.im = 0;
                for (iFrame = 0, nbGoodFrames = 0; iFrame < nbFrames; iFrame++)
                {
                    if ((selection->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE)&&(cpxVisTablePtr[iFrame][iBase].flag[lVis] == amdlibFALSE))
                    {
                        rmsVis.re += amdlibPow2(
                            cpxVisTablePtr[iFrame][iBase].vis[lVis].re - 
                            cpxVis.re);
                        rmsVis.im += amdlibPow2(
                            cpxVisTablePtr[iFrame][iBase].vis[lVis].im - 
                            cpxVis.im);
                        nbGoodFrames++;
                    }
                }
                rmsVis.re /= nbGoodFrames;
                rmsVis.im /= nbGoodFrames;
                visTablePtr[iBin][iBase].sigma2Vis[lVis].re=rmsVis.re;
                visTablePtr[iBin][iBase].sigma2Vis[lVis].im=rmsVis.im;
            }
        }
    }
    amdlibAverageVisData_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibAverageVisData_FREEALL


/** Useful macro to free amdlibComputeDiffVis() structures */ 

#define amdlibComputeDiffVis_FREEALL()                         \
    free(cpxVisVectR); free(cpxVisVectI);                      \
    free(snrVect); free(phiVect); free(wlen);                  \
    amdlibFree2DArrayWrapping((void **)cpxVisTablePtr);        \
    amdlibFree2DArrayWrapping((void **)visTablePtr);           \
    amdlibFree2DArrayDoubleWrapping(instantOpdPistonPtr);       \
    amdlibFree3DArrayComplex(cNopTable);                       \
    amdlibFree3DArrayComplex(w1);                              \
    amdlibFree3DArrayComplex(sigma2_w1);                       \
    amdlibFree3DArrayComplex(cpxVisTable);                     \
    amdlibFree3DArrayComplex(sigma2_cpxVisTable);              \
    amdlibFree3DArrayComplex(cRefTable);                       \
    amdlibFree2DArrayDouble(opd);                              \
    amdlibFree3DArrayComplex(sigma2_cRefTable);        


/**
 * Compute differential visibility, in the way described by F. Millour et al's 
 * ("DIFFERENTIAL INTERFEROMETRY WITH THE AMBER/VLTI INSTRUMENT: DESCRIPTION, 
 * PERFORMANCES AND ILLUSTRATION", EAS Publications Series, Vol. ?, 2005)
 *
 * DiffVis are to be written in the final OI_FITS VISAMP and
 * VISPHI OI_VIS columns.
 *
 * @param instantCorrFlux the bin of  instanteous correlated fluxes.
 * @param wave the table of wavelengths.
 * @param instantOpd the table of opds
 * @param band the current band
 * @param iBin index of the results to be written in the output 
 * (binned) vis structure.
 * @param selection the selection structure giving list of selected frames.
 * @param vis structure were results are stored.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibComputeDiffVis(/* Input */
                                      amdlibVIS              *instantCorrFlux,
                                      amdlibWAVELENGTH       *wave,
                                      amdlibPISTON           *instantOpd,
                                      int                    iBin,
                                      amdlibBAND             band,
                                      amdlibSELECTION        *selection,
                                      /* Output */
                                      amdlibVIS              *vis,
                                      amdlibERROR_MSG        errMsg)
{

    int iFrame; /* usually loop on number of frames */
    int nbFrames = instantCorrFlux->nbFrames;
    int iBase; /*usually loop on nbBases*/
    int nbBases = instantCorrFlux->nbBases;
    int lVis = 0;
    int nbLVis = instantCorrFlux->nbWlen;
    int nbGoodFrames=0, nbGoodVis=0;

    amdlibVIS_TABLE_ENTRY **cpxVisTablePtr = NULL; 
    amdlibVIS_TABLE_ENTRY **visTablePtr = NULL; 
    amdlibDOUBLE **instantOpdPistonPtr = NULL;
    /* We try to follow the paper's notations */
    amdlibCOMPLEX *** cNopTable = NULL; 
    amdlibCOMPLEX *** w1 = NULL; 
    amdlibCOMPLEX *** sigma2_w1 = NULL; 
    amdlibCOMPLEX *** cRefTable = NULL;
    amdlibCOMPLEX *** sigma2_cRefTable = NULL;
    amdlibCOMPLEX *** cpxVisTable = NULL;
    amdlibCOMPLEX *** sigma2_cpxVisTable = NULL;
    amdlibDOUBLE  *wlen= NULL; 
    amdlibDOUBLE  **opd= NULL;
    double *cpxVisVectR= NULL;
    double *cpxVisVectI= NULL;
    double *snrVect = NULL;
    double *phiVect = NULL;


    amdlibCOMPLEX phasor, cpxVis, sigma2_cpxVis, w1Avg = {0.0,0.0};
    double x;

    amdlibLogTrace("amdlibComputeDiffVis()");

    /* Allocate memory for intermediate structures, of maximum size in frames */
    cNopTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (cNopTable == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cNopTable failed");
        return amdlibFAILURE;
    }
    w1 = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (w1 == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of w1 failed");
        return amdlibFAILURE;
    }
    sigma2_w1 = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (sigma2_w1 == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of w1 failed");
        return amdlibFAILURE;
    }
    cpxVisTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (cpxVisTable == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cpxVisTable failed");
        return amdlibFAILURE;
    }
    sigma2_cpxVisTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (sigma2_cpxVisTable == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cpxVisTableErr failed");
        return amdlibFAILURE;
    }
    cRefTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (cRefTable == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cRefTable failed");
        return amdlibFAILURE;
    }
    sigma2_cRefTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (sigma2_cRefTable == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cRefTableErr failed");
        return amdlibFAILURE;
    }
    opd = amdlibAlloc2DArrayDouble(nbBases, nbFrames, errMsg);
    if (opd == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of opd failed");
        return amdlibFAILURE;
    }
     wlen = calloc(nbLVis,sizeof(*wlen));
    if (wlen == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of wlen failed");
        return amdlibFAILURE;
    }

    cpxVisVectR = calloc(nbFrames,sizeof(*cpxVisVectR));
    if (cpxVisVectR == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cpxVisVectR failed");
        return amdlibFAILURE;
    }
    cpxVisVectI = calloc(nbFrames,sizeof(*cpxVisVectI));
    if (cpxVisVectI == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cpxVisVectI failed");
        return amdlibFAILURE;
    }
    snrVect = calloc(nbFrames,sizeof(*snrVect));
    if ( snrVect== NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of snrVect failed");
        return amdlibFAILURE;
    }
    phiVect = calloc(nbFrames,sizeof(*phiVect));
    if ( phiVect== NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of phiVect failed");
        return amdlibFAILURE;
    }
    /* Wrap structures given as parametres */
    cpxVisTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                            nbBases, instantCorrFlux->nbFrames,
                                            sizeof(amdlibVIS_TABLE_ENTRY),
                                            errMsg);
    if (cpxVisTablePtr == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        return amdlibFAILURE;
    }

    instantOpdPistonPtr = 
        amdlibWrap2DArrayDouble(instantOpd->pistonOPDArray[band],
                               instantOpd->nbBases,
                               instantOpd->nbFrames, errMsg);
    if (instantOpdPistonPtr == NULL)
    {
        amdlibSetErrMsg("Could not wrap 2D amdlibDOUBLE array");
        amdlibComputeDiffVis_FREEALL();
        return amdlibFAILURE;
    }

    visTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(vis->table,
                                                nbBases, vis->nbFrames,
                                                sizeof(amdlibVIS_TABLE_ENTRY),
                                                errMsg);
    if (visTablePtr == NULL)
    {
        amdlibComputeDiffVis_FREEALL();
        return amdlibFAILURE;
    }

     /* Initialize opd: local copy where all bad pistons are Blank:
     * if opd = BLANK, cNopTable[iFrame][iBase] will be BLANK, and the avg
     * and rms done at the end will avoid these values */
    for (iFrame = 0; iFrame < nbFrames; iFrame++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            if( selection->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE )
            {
                /*May contain blanks for other reasons...*/
                opd[iFrame][iBase] = instantOpdPistonPtr[iFrame][iBase];
            }
            else
            {
                opd[iFrame][iBase] = amdlibBLANKING_VALUE;
            }
        }
    }

   /* Immediately copy input cpxVis to 3D structure, flagging as BLANK when Flag is present */

    for (iFrame = 0; iFrame < nbFrames; iFrame++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            for (lVis = 0; lVis < nbLVis; lVis++)
            {
                if (instantCorrFlux->table[iFrame*nbBases+iBase].flag[lVis]== amdlibFALSE)
                {
                    cpxVisTable[iFrame][iBase][lVis].re = 
                    cpxVisTablePtr[iFrame][iBase].vis[lVis].re;
                    cpxVisTable[iFrame][iBase][lVis].im = 
                    cpxVisTablePtr[iFrame][iBase].vis[lVis].im;
                    
                    sigma2_cpxVisTable[iFrame][iBase][lVis].re = 
                    cpxVisTablePtr[iFrame][iBase].sigma2Vis[lVis].re;
                    sigma2_cpxVisTable[iFrame][iBase][lVis].im = 
                    cpxVisTablePtr[iFrame][iBase].sigma2Vis[lVis].im;
                }
                else
                {
                    cpxVisTable[iFrame][iBase][lVis].re = amdlibBLANKING_VALUE;
                    cpxVisTable[iFrame][iBase][lVis].im = amdlibBLANKING_VALUE;
                    sigma2_cpxVisTable[iFrame][iBase][lVis].re = amdlibBLANKING_VALUE;
                    sigma2_cpxVisTable[iFrame][iBase][lVis].im = amdlibBLANKING_VALUE;
                }
            }
        }
    }
    /* Initialize wlen */
    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        wlen[lVis] = wave->wlen[lVis];
    }

    /* Correct from Piston, 1st time */
    /* First, correct coherent flux from achromatic piston phase (eq 2.2)*/
    if (amdlibCorrect3DVisTableFromAchromaticPiston(cpxVisTable,
						    cNopTable,
						    nbFrames,
						    nbBases,
						    nbLVis,
						    wlen,
						    opd,
						    errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }
    
    /* Production of the reference channel: mean of all R and I parts
     * of all channels except the one considered ( eq 2.3) */
    for (iFrame = 0; iFrame < nbFrames; iFrame++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            cpxVis.re = 0.0;
            cpxVis.im = 0.0;
            sigma2_cpxVis.re = 0.0;
            sigma2_cpxVis.im = 0.0;
            /* sum all R and I */
            for (nbGoodVis= 0, lVis = 0; lVis < nbLVis; lVis++)
            {
                if (!amdlibCompareDouble(cpxVisTable[iFrame][iBase][lVis].re,amdlibBLANKING_VALUE))
                {
                    cpxVis.re += cNopTable[iFrame][iBase][lVis].re;
                    cpxVis.im += cNopTable[iFrame][iBase][lVis].im;
                    sigma2_cpxVis.re += sigma2_cpxVisTable[iFrame][iBase][lVis].re;
                    sigma2_cpxVis.im += sigma2_cpxVisTable[iFrame][iBase][lVis].im;
                    nbGoodVis++;
                }
           }
            /* then construct Cref by substracting current R and I 
             * at that Wlen and make the arithmetic mean */
            for (lVis = 0; lVis < nbLVis; lVis++)
            {
                cRefTable[iFrame][iBase][lVis].re = 
                    (cpxVis.re - cNopTable[iFrame][iBase][lVis].re) /
                    (nbGoodVis - 1);
                cRefTable[iFrame][iBase][lVis].im =
                    (cpxVis.im - cNopTable[iFrame][iBase][lVis].im) /
                    (nbGoodVis - 1);
		
                sigma2_cRefTable[iFrame][iBase][lVis].re = 
                    (sigma2_cpxVis.re -
		     sigma2_cpxVisTable[iFrame][iBase][lVis].re) /
                    (nbGoodVis - 1);
                sigma2_cRefTable[iFrame][iBase][lVis].im =
                    (sigma2_cpxVis.im -
		     sigma2_cpxVisTable[iFrame][iBase][lVis].im) /
                    (nbGoodVis - 1);
            }
        }
    }

    /* Now the interspectrum is cNopTable*~cRefTable. Store in w1. */
    for (iBase = 0; iBase < nbBases; iBase++)
    {
        for (iFrame = 0; iFrame < nbFrames; iFrame++)
        {
            for (lVis = 0; lVis < nbLVis; lVis++)
            {
                cpxVis.re = cNopTable[iFrame][iBase][lVis].re;
                cpxVis.im = cNopTable[iFrame][iBase][lVis].im;

                phasor.re = cRefTable[iFrame][iBase][lVis].re;
                phasor.im = -1*cRefTable[iFrame][iBase][lVis].im;

                amdlibCpxMul(cpxVis,phasor,w1[iFrame][iBase][lVis]);
		
		/* Please have a look to the F. Millour thesis
		   (http://tel.archives-ouvertes.fr/tel-00134268),
		   pp.91-892 (eq. 4.55 to 4.58) */
		sigma2_w1[iFrame][iBase][lVis].re =
		    sigma2_cpxVisTable[iFrame][iBase][lVis].re *
		    amdlibPow2(cRefTable[iFrame][iBase][lVis].re) +
		    sigma2_cRefTable[iFrame][iBase][lVis].re *
		    amdlibPow2(cpxVisTable[iFrame][iBase][lVis].re) +
		    sigma2_cpxVisTable[iFrame][iBase][lVis].im *
		    amdlibPow2(cRefTable[iFrame][iBase][lVis].im) +
		    sigma2_cRefTable[iFrame][iBase][lVis].im *
		    amdlibPow2(cpxVisTable[iFrame][iBase][lVis].im);
		
		sigma2_w1[iFrame][iBase][lVis].im =
		    sigma2_cpxVisTable[iFrame][iBase][lVis].im *
		    amdlibPow2(cRefTable[iFrame][iBase][lVis].re) +
		    sigma2_cRefTable[iFrame][iBase][lVis].im *
		    amdlibPow2(cpxVisTable[iFrame][iBase][lVis].re) +
		    sigma2_cpxVisTable[iFrame][iBase][lVis].re *
		    amdlibPow2(cRefTable[iFrame][iBase][lVis].im) +
		    sigma2_cRefTable[iFrame][iBase][lVis].re *
		    amdlibPow2(cpxVisTable[iFrame][iBase][lVis].im);
            }
        }
    }

    /* Compute mean VisPhi as average of selected frames. If snr is BLANK
     * (negative), averages take into account blanked values */
    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            /* The SNR vector */
            for (iFrame = 0 , nbGoodFrames =0; iFrame < nbFrames; iFrame++)
            {
                if ((selection->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE) &&  
                    !amdlibCompareDouble(opd[iFrame][iBase],amdlibBLANKING_VALUE) && 
                    !amdlibCompareDouble(cpxVisTable[iFrame][iBase][lVis].re,amdlibBLANKING_VALUE))
                {
                    snrVect[iFrame] = 1.0;
                    nbGoodFrames ++;
                }
                else
                {
                    snrVect[iFrame] = -1.0;
                }
            }
            /* The W1 vector */
            for (iFrame = 0 ; iFrame < nbFrames; iFrame++)
            {
                cpxVisVectR[iFrame] = w1[iFrame][iBase][lVis].re;
                cpxVisVectI[iFrame] = w1[iFrame][iBase][lVis].im;
            }
            /* The Phase Herself */
            /* average re and im */
            w1Avg.re = amdlibAvgTable(nbFrames, cpxVisVectR, snrVect);
            w1Avg.im = amdlibAvgTable(nbFrames, cpxVisVectI, snrVect);
            /* store */
            visTablePtr[iBin][iBase].diffVisPhi[lVis] =
            (double)(nbGoodVis-1)/(double)nbGoodVis*atan2(w1Avg.im,w1Avg.re);

            /* WE USE THE STATISTICAL ERROR FOR BINNING */
            w1Avg.im=-1*w1Avg.im;
            for (iFrame = 0 ; iFrame < nbFrames; iFrame++)
            {
                /* add w1*conj(w1Avg) to vector*/
                amdlibCpxMul(w1[iFrame][iBase][lVis],w1Avg,cpxVis);
                /* use phiVect to store phases */
                phiVect[iFrame] = atan2(cpxVis.im,cpxVis.re);
            }
            x = amdlibRmsTable(nbFrames, phiVect, snrVect);
            /* Err on Phi must be corrected with an abacus*/
            visTablePtr[iBin][iBase].diffVisPhiErr[lVis] =
            amdlibAbacusErrPhi(x/sqrt(nbGoodFrames));
        }
    }

    /* Now for the differential vis, we use cNopTable/cRefTable. Store in w1. 
    * note this is not exactly as complicated as in the computeDiff yorick 
    * implementation by Millour */
    for (iBase = 0; iBase < nbBases; iBase++)
    {
        for (iFrame = 0; iFrame < nbFrames; iFrame++)
        {
            for (lVis = 0; lVis < nbLVis; lVis++)
            {
                cpxVis.re = cNopTable[iFrame][iBase][lVis].re;
                cpxVis.im = cNopTable[iFrame][iBase][lVis].im;
                phasor.re = cRefTable[iFrame][iBase][lVis].re;
                phasor.im = cRefTable[iFrame][iBase][lVis].im;
                amdlibCpxDiv(cpxVis,phasor,w1[iFrame][iBase][lVis]);
            }
        }
    }
     /* Compute mean VisAmp as average of selected frames. */
    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            /* The SNR vector */
            for (iFrame = 0, nbGoodFrames = 0 ; iFrame < nbFrames; iFrame++)
            {
                if((selection->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE) &&  
                   !amdlibCompareDouble(opd[iFrame][iBase],amdlibBLANKING_VALUE) && 
                   !amdlibCompareDouble(cpxVisTable[iFrame][iBase][lVis].re,amdlibBLANKING_VALUE))
                    {
                        snrVect[iFrame] = 1.0;
                        nbGoodFrames++;
                    }
                   else
                   {
                       snrVect[iFrame] = -1.0;
                   }
            }
            /* The W1 vector */
            for (iFrame = 0 ; iFrame < nbFrames; iFrame++)
            {
                /* see eq 2.8 */
                x = visTablePtr[iBin][iBase].diffVisPhi[lVis];
                phasor.re = cos(x);
                phasor.im = -sin(x);
                cpxVis.re = w1[iFrame][iBase][lVis].re;
                cpxVis.im = w1[iFrame][iBase][lVis].im;
                cpxVisVectR[iFrame] =
                phasor.re * cpxVis.re - phasor.im * cpxVis.im;
                cpxVisVectI[iFrame] =
                phasor.re * cpxVis.im + phasor.im * cpxVis.re;
            }
            visTablePtr[iBin][iBase].diffVisAmp[lVis] =
            amdlibAvgTable(nbFrames, cpxVisVectR, snrVect);
            visTablePtr[iBin][iBase].diffVisAmpErr[lVis] =
            amdlibRmsTable(nbFrames, cpxVisVectR, snrVect) / sqrt(nbGoodFrames);
        }
    }
    amdlibComputeDiffVis_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibComputeDiffVis_FREEALL

/** Useful macro to free amdlibBinDiffVis() structures */ 

#define amdlibBinDiffVis_FREEALL()                             \
    free(cpxVisVectR); free(cpxVisVectI);                      \
    free(snrVect); free(phiVect); free(wlen);                  \
    amdlibFree2DArrayWrapping((void **)cpxVisTablePtr);        \
    amdlibFree2DArrayWrapping((void **)visTablePtr);           \
    amdlibFree2DArrayDoubleWrapping(instantOpdPistonPtr);      \
    amdlibFree3DArrayComplex(cNopTable);                       \
    amdlibFree3DArrayComplex(w1);                              \
    amdlibFree3DArrayComplex(sigma2_w1);                       \
    amdlibFree3DArrayComplex(cpxVisTable);                     \
    amdlibFree3DArrayComplex(sigma2_cpxVisTable);              \
    amdlibFree3DArrayComplex(cRefTable);                       \
    amdlibFree2DArrayDouble(opd);                              \
    amdlibFree3DArrayComplex(sigma2_cRefTable);              


/**
 * Compute differential visibility, in the way described by F. Millour et al's 
 * ("DIFFERENTIAL INTERFEROMETRY WITH THE AMBER/VLTI INSTRUMENT: DESCRIPTION, 
 * PERFORMANCES AND ILLUSTRATION", EAS Publications Series, Vol. ?, 2005)
 *
 * DiffVis are to be written in the final OI_FITS VISAMP and
 * VISPHI OI_VIS columns.
 *
 * @param instantCorrFlux the bin of  instanteous correlated fluxes.
 * @param wave the table of wavelengths.
 * @param instantOpd the table of opds
 * @param band the current band
 * @param iBin index of the results to be written in the output 
 * (binned) vis structure.
 * @param selection the selection structure giving list of selected frames.
 * @param vis structure were results are stored.
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibBinDiffVis(/* Input */
                                      amdlibVIS              *instantCorrFlux,
                                      amdlibWAVELENGTH       *wave,
                                      amdlibPISTON           *instantOpd,
                                      int                    firstFrame,
                                      int                    nbFrames,
                                      int                    iBin,
                                      amdlibBAND             band,
                                      /* Output */
                                      amdlibVIS              *vis,
                                      amdlibERROR_MSG        errMsg)
{

    int iFrame, runningFrame; /* usually loop on number of frames */
    int iBase; /*usually loop on nbBases*/
    int nbBases = instantCorrFlux->nbBases;
    int lVis = 0;
    int nbLVis = instantCorrFlux->nbWlen;
    int nbGoodFrames=0, nbGoodVis=0;

    amdlibVIS_TABLE_ENTRY **cpxVisTablePtr = NULL; 
    amdlibVIS_TABLE_ENTRY **visTablePtr = NULL; 
    amdlibDOUBLE **instantOpdPistonPtr = NULL;
    /* We try to follow the paper's notations */
    amdlibCOMPLEX *** cNopTable = NULL; 
    amdlibCOMPLEX *** w1 = NULL; 
    amdlibCOMPLEX *** sigma2_w1 = NULL; 
    amdlibCOMPLEX *** cRefTable = NULL;
    amdlibCOMPLEX *** sigma2_cRefTable = NULL;
    amdlibCOMPLEX *** cpxVisTable = NULL;
    amdlibCOMPLEX *** sigma2_cpxVisTable = NULL;
    amdlibDOUBLE  *wlen= NULL; 
    amdlibDOUBLE  **opd= NULL;
    double *cpxVisVectR= NULL;
    double *cpxVisVectI= NULL;
    double *snrVect = NULL;
    double *phiVect = NULL;


    amdlibCOMPLEX phasor, cpxVis, sigma2_cpxVis, w1Avg = {0.0,0.0};
    double x;

    amdlibLogTrace("amdlibBinDiffVis()");

    /* Allocate memory for intermediate structures, of maximum size in frames */
    cNopTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (cNopTable == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cNopTable failed");
        return amdlibFAILURE;
    }
    w1 = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (w1 == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of w1 failed");
        return amdlibFAILURE;
    }
    sigma2_w1 = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (sigma2_w1 == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of w1 failed");
        return amdlibFAILURE;
    }
    cpxVisTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (cpxVisTable == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cpxVisTable failed");
        return amdlibFAILURE;
    }
    sigma2_cpxVisTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (sigma2_cpxVisTable == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cpxVisTableErr failed");
        return amdlibFAILURE;
    }
    cRefTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (cRefTable == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cRefTable failed");
        return amdlibFAILURE;
    }
    sigma2_cRefTable = amdlibAlloc3DArrayComplex(nbLVis, nbBases, nbFrames, errMsg);
    if (sigma2_cRefTable == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cRefTableErr failed");
        return amdlibFAILURE;
    }
    opd = amdlibAlloc2DArrayDouble(nbBases, nbFrames, errMsg);
    if (opd == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of opd failed");
        return amdlibFAILURE;
    }
    wlen = calloc(nbLVis,sizeof(*wlen));
    if (wlen == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of wlen failed");
        return amdlibFAILURE;
    }

    cpxVisVectR = calloc(nbFrames,sizeof(*cpxVisVectR));
    if (cpxVisVectR == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cpxVisVectR failed");
        return amdlibFAILURE;
    }
    cpxVisVectI = calloc(nbFrames,sizeof(*cpxVisVectI));
    if (cpxVisVectI == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of cpxVisVectI failed");
        return amdlibFAILURE;
    }
    snrVect = calloc(nbFrames,sizeof(*snrVect));
    if ( snrVect== NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of snrVect failed");
        return amdlibFAILURE;
    }
    phiVect = calloc(nbFrames,sizeof(*phiVect));
    if ( phiVect== NULL)
    {
        amdlibBinDiffVis_FREEALL();
        amdlibSetErrMsg("Allocation of phiVect failed");
        return amdlibFAILURE;
    }
    /* Wrap structures given as parametres */
    cpxVisTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                            nbBases, instantCorrFlux->nbFrames,
                                            sizeof(amdlibVIS_TABLE_ENTRY),
                                            errMsg);
    if (cpxVisTablePtr == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        return amdlibFAILURE;
    }

    instantOpdPistonPtr = 
        amdlibWrap2DArrayDouble(instantOpd->pistonOPDArray[band],
                               instantOpd->nbBases,
                               instantOpd->nbFrames, errMsg);
    if (instantOpdPistonPtr == NULL)
    {
        amdlibSetErrMsg("Could not wrap 2D amdlibDOUBLE array");
        amdlibBinDiffVis_FREEALL();
        return amdlibFAILURE;
    }

    visTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(vis->table,
                                                nbBases, vis->nbFrames,
                                                sizeof(amdlibVIS_TABLE_ENTRY),
                                                errMsg);
    if (visTablePtr == NULL)
    {
        amdlibBinDiffVis_FREEALL();
        return amdlibFAILURE;
    }
    
    /* Initialize opd: local copy where all bad pistons are Blank:
     * if opd = BLANK, cNopTable[iFrame][iBase] will be BLANK, and the avg
     * and rms done at the end will avoid these values */
    for (iFrame = 0, runningFrame=firstFrame; iFrame < nbFrames; 
         iFrame++, runningFrame++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
	    opd[iFrame][iBase] = instantOpdPistonPtr[runningFrame][iBase];
        }
    }

    /* Immediately copy input cpxVis to 3D structure, flagging as BLANK when Flag is present */

    for (iFrame = 0, runningFrame=firstFrame; iFrame < nbFrames; 
         iFrame++, runningFrame++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            for (lVis = 0; lVis < nbLVis; lVis++)
            {
                if (instantCorrFlux->table[runningFrame*nbBases+iBase].flag[lVis]== amdlibFALSE)
                {
                    cpxVisTable[iFrame][iBase][lVis].re = 
                    cpxVisTablePtr[runningFrame][iBase].vis[lVis].re;
                    cpxVisTable[iFrame][iBase][lVis].im = 
                    cpxVisTablePtr[runningFrame][iBase].vis[lVis].im;
                    
                    sigma2_cpxVisTable[iFrame][iBase][lVis].re = 
                    cpxVisTablePtr[runningFrame][iBase].sigma2Vis[lVis].re;
                    sigma2_cpxVisTable[iFrame][iBase][lVis].im = 
                    cpxVisTablePtr[runningFrame][iBase].sigma2Vis[lVis].im;
                }
                else
                {
                    cpxVisTable[iFrame][iBase][lVis].re = amdlibBLANKING_VALUE;
                    cpxVisTable[iFrame][iBase][lVis].im = amdlibBLANKING_VALUE;
                    sigma2_cpxVisTable[iFrame][iBase][lVis].re = amdlibBLANKING_VALUE;
                    sigma2_cpxVisTable[iFrame][iBase][lVis].im = amdlibBLANKING_VALUE;
                }
            }
        }
    }
    /* Initialize wlen */
    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        wlen[lVis] = wave->wlen[lVis];
    }

    /* Correct from Piston, 1st time */
    /* First, correct coherent flux from achromatic piston phase (eq 2.2)*/
    if (amdlibCorrect3DVisTableFromAchromaticPiston(cpxVisTable,
						    cNopTable,
						    nbFrames,
						    nbBases,
						    nbLVis,
						    wlen,
						    opd,
						    errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }
    
    /* Production of the reference channel: mean of all R and I parts
     * of all channels except the one considered ( eq 2.3) */
    for (iFrame = 0; iFrame < nbFrames; iFrame++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
                cpxVis.re = 0.0;
                cpxVis.im = 0.0;
                sigma2_cpxVis.re = 0.0;
                sigma2_cpxVis.im = 0.0;
                /* sum all R and I */
                for (nbGoodVis= 0, lVis = 0; lVis < nbLVis; lVis++)
                {
                    if (!amdlibCompareDouble(cpxVisTable[iFrame][iBase][lVis].re,amdlibBLANKING_VALUE))
                    {
                        cpxVis.re += cNopTable[iFrame][iBase][lVis].re;
                        cpxVis.im += cNopTable[iFrame][iBase][lVis].im;
                        sigma2_cpxVis.re += sigma2_cpxVisTable[iFrame][iBase][lVis].re;
                        sigma2_cpxVis.im += sigma2_cpxVisTable[iFrame][iBase][lVis].im;
                        nbGoodVis++;
                    }
                }
                /* then construct Cref by substracting current R and I 
                 * at that Wlen and make the arithmetic mean */
                for (lVis = 0; lVis < nbLVis; lVis++)
                {
                    cRefTable[iFrame][iBase][lVis].re = 
                    (cpxVis.re - cNopTable[iFrame][iBase][lVis].re) /
                    (nbGoodVis - 1);
                    cRefTable[iFrame][iBase][lVis].im =
                    (cpxVis.im - cNopTable[iFrame][iBase][lVis].im) /
                    (nbGoodVis - 1);
		
                    sigma2_cRefTable[iFrame][iBase][lVis].re = 
                    (sigma2_cpxVis.re -
		     sigma2_cpxVisTable[iFrame][iBase][lVis].re) /
                    (nbGoodVis - 1);
                    sigma2_cRefTable[iFrame][iBase][lVis].im =
                    (sigma2_cpxVis.im -
		     sigma2_cpxVisTable[iFrame][iBase][lVis].im) /
                    (nbGoodVis - 1);
                }
        }
    }

    /* Now the interspectrum is cNopTable*~cRefTable. Store in w1. */
    for (iBase = 0; iBase < nbBases; iBase++)
    {
        for (iFrame = 0; iFrame < nbFrames; iFrame++)
        {
            for (lVis = 0; lVis < nbLVis; lVis++)
            {
                cpxVis.re = cNopTable[iFrame][iBase][lVis].re;
                cpxVis.im = cNopTable[iFrame][iBase][lVis].im;
                
                phasor.re = cRefTable[iFrame][iBase][lVis].re;
                phasor.im = -1*cRefTable[iFrame][iBase][lVis].im;
                
                amdlibCpxMul(cpxVis,phasor,w1[iFrame][iBase][lVis]);
		
                /* Please have a look to the F. Millour thesis
                   (http://tel.archives-ouvertes.fr/tel-00134268),
                   pp.91-892 (eq. 4.55 to 4.58) */
                sigma2_w1[iFrame][iBase][lVis].re =
                sigma2_cpxVisTable[iFrame][iBase][lVis].re *
                amdlibPow2(cRefTable[iFrame][iBase][lVis].re) +
                sigma2_cRefTable[iFrame][iBase][lVis].re *
                amdlibPow2(cpxVisTable[iFrame][iBase][lVis].re) +
                sigma2_cpxVisTable[iFrame][iBase][lVis].im *
                amdlibPow2(cRefTable[iFrame][iBase][lVis].im) +
		    sigma2_cRefTable[iFrame][iBase][lVis].im *
                amdlibPow2(cpxVisTable[iFrame][iBase][lVis].im);
		
                sigma2_w1[iFrame][iBase][lVis].im =
                sigma2_cpxVisTable[iFrame][iBase][lVis].im *
                amdlibPow2(cRefTable[iFrame][iBase][lVis].re) +
                sigma2_cRefTable[iFrame][iBase][lVis].im *
                amdlibPow2(cpxVisTable[iFrame][iBase][lVis].re) +
		    sigma2_cpxVisTable[iFrame][iBase][lVis].re *
                amdlibPow2(cRefTable[iFrame][iBase][lVis].im) +
                sigma2_cRefTable[iFrame][iBase][lVis].re *
                amdlibPow2(cpxVisTable[iFrame][iBase][lVis].im);
            }
        }
    }

    /* Compute mean VisPhi as average of frames. If snr is BLANK
     * (negative), averages take into account blanked values */
    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            /* The SNR vector */
            for (iFrame = 0 , nbGoodFrames =0; iFrame < nbFrames; iFrame++)
            {
                if ( !amdlibCompareDouble(opd[iFrame][iBase],amdlibBLANKING_VALUE) && 
                     !amdlibCompareDouble(cpxVisTable[iFrame][iBase][lVis].re,amdlibBLANKING_VALUE) )
                {
                    snrVect[iFrame] = 1.0;
                    nbGoodFrames ++;
                }
                else
                {
                    snrVect[iFrame] = -1.0;
                }
            }
            /* The W1 vector */
            for (iFrame = 0 ; iFrame < nbFrames; iFrame++)
            {
                cpxVisVectR[iFrame] = w1[iFrame][iBase][lVis].re;
                cpxVisVectI[iFrame] = w1[iFrame][iBase][lVis].im;
            }
            /* The Phase Herself */
            /* average re and im */
            w1Avg.re = amdlibAvgTable(nbFrames, cpxVisVectR, snrVect);
            w1Avg.im = amdlibAvgTable(nbFrames, cpxVisVectI, snrVect);
            /* store */
            visTablePtr[iBin][iBase].diffVisPhi[lVis] =
            (double)(nbGoodVis-1)/(double)nbGoodVis*atan2(w1Avg.im,w1Avg.re);
            
            /* WE USE THE STATISTICAL ERROR FOR BINNING */
            w1Avg.im=-1*w1Avg.im;
            for (iFrame = 0 ; iFrame < nbFrames; iFrame++)
            {
                /* add w1*conj(w1Avg) to vector*/
                amdlibCpxMul(w1[iFrame][iBase][lVis],w1Avg,cpxVis);
                /* use phiVect to store phases */
                phiVect[iFrame] = atan2(cpxVis.im,cpxVis.re);
            }
            x = amdlibRmsTable(nbFrames, phiVect, snrVect);
            /* Err on Phi must be corrected with an abacus*/
            visTablePtr[iBin][iBase].diffVisPhiErr[lVis] =
            amdlibAbacusErrPhi(x/sqrt(nbGoodFrames));
        }
    }
    
    /* Now for the differential vis, we use cNopTable/cRefTable. Store in w1. 
    * note this is not exactly as complicated as in the computeDiff yorick 
    * implementation by Millour */
    for (iBase = 0; iBase < nbBases; iBase++)
    {
        for (iFrame = 0; iFrame < nbFrames; iFrame++)
        {
            for (lVis = 0; lVis < nbLVis; lVis++)
            {
                cpxVis.re = cNopTable[iFrame][iBase][lVis].re;
                cpxVis.im = cNopTable[iFrame][iBase][lVis].im;
                phasor.re = cRefTable[iFrame][iBase][lVis].re;
                phasor.im = cRefTable[iFrame][iBase][lVis].im;
                amdlibCpxDiv(cpxVis,phasor,w1[iFrame][iBase][lVis]);
            }
        }
    }
     /* Compute mean VisAmp as average of selected frames. */
    for (lVis = 0; lVis < nbLVis; lVis++)
    {
        for (iBase = 0; iBase < nbBases; iBase++)
        {
            /* The SNR vector */
            for (iFrame = 0, nbGoodFrames = 0 ; iFrame < nbFrames; iFrame++)
            {
                if( !amdlibCompareDouble(opd[iFrame][iBase],amdlibBLANKING_VALUE) && 
                    !amdlibCompareDouble(cpxVisTable[iFrame][iBase][lVis].re,amdlibBLANKING_VALUE))
                {
                    snrVect[iFrame] = 1.0;
                    nbGoodFrames++;
                }
                else
                {
                    snrVect[iFrame] = -1.0;
                }
            }
            /* The W1 vector */
            for (iFrame = 0 ; iFrame < nbFrames; iFrame++)
            {
                /* see eq 2.8 */
                x = visTablePtr[iBin][iBase].diffVisPhi[lVis];
                phasor.re = cos(x);
                phasor.im = -sin(x);
                cpxVis.re = w1[iFrame][iBase][lVis].re;
                cpxVis.im = w1[iFrame][iBase][lVis].im;
                cpxVisVectR[iFrame] =
                phasor.re * cpxVis.re - phasor.im * cpxVis.im;
                cpxVisVectI[iFrame] =
                phasor.re * cpxVis.im + phasor.im * cpxVis.re;
            }
            visTablePtr[iBin][iBase].diffVisAmp[lVis] =
            amdlibAvgTable(nbFrames, cpxVisVectR, snrVect);
            visTablePtr[iBin][iBase].diffVisAmpErr[lVis] =
            amdlibRmsTable(nbFrames, cpxVisVectR, snrVect) / sqrt(nbGoodFrames);
        }
    }

    amdlibBinDiffVis_FREEALL();
    return amdlibSUCCESS;
}
#undef amdlibBinDiffVis_FREEALL


/** Useful macro to free all dynamically allocated structures */ 
#define amdlibComputeFringeCriterion_FREEALL()            \
    amdlibFree2DArrayWrapping((void **)corrFluxTablePtr); \
    amdlibFree2DArrayWrapping((void **)visTablePtr);
/**
 * Compute fringe criterion. 
 * 
 * @param instantCorrFlux the bin of  instanteous correlated fluxes.
 * @param iBin index of the results to be written in the output (binned) vis 
 * structure.
 * @param band current band treated.
 * @param selection structure containing information relative to frame
 * selection. By default, all frames are selected.
 * @param vis structure were results are stored.
 */
void amdlibComputeFringeCriterion(/* Input */
                                  amdlibVIS       *instantCorrFlux,
                                  int             iBin,
                                  amdlibBAND      band,
                                  amdlibSELECTION *selection,
                                  /* Output */
                                  amdlibVIS       *vis)
{
    /* loop indexes and associated limits */
    int iFrame; /* usually loop on number of frames */
    int nbFrames = instantCorrFlux->nbFrames;

    int iBase; /*usually loop on nbBases*/
    int nbBases = instantCorrFlux->nbBases; 
    /* Number of Baselines in Science Data*/
    int lVis = 0;
    int nbLVis = instantCorrFlux->nbWlen;
    int nbValid;

    /* Input values of the coherent flux */
    amdlibCOMPLEX CorrFlux;
    amdlibCOMPLEX sigma2_CorrFlux;

    /* Used for the fringes detection criterion */
    double num_SNR_CorrFlux_re;
    double num_SNR_CorrFlux_im;
    double denom_SNR_CorrFlux_re;
    double denom_SNR_CorrFlux_im;
    double frgContrastSNR;
    double sqFrgContrastSNR;

    amdlibVIS_TABLE_ENTRY **corrFluxTablePtr = NULL; 
    amdlibVIS_TABLE_ENTRY **visTablePtr = NULL; 
    static amdlibERROR_MSG errMsg;

    /* Computation of the fringe Criterion */
    /* This is useful for the fringe Criterion :
     * initialisation of the photometry and the sigma2 */

    amdlibLogTrace("amdlibComputeFringeCriterion()");

    /* Wrap structures given as parametres */
    visTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(vis->table,
                                            nbBases, vis->nbFrames,
                                            sizeof(amdlibVIS_TABLE_ENTRY),
                                            errMsg);
    if (visTablePtr == NULL)
    {
        amdlibComputeFringeCriterion_FREEALL();
    }
    corrFluxTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                            nbBases, nbFrames,
                                            sizeof(amdlibVIS_TABLE_ENTRY),
                                            errMsg);
    if (corrFluxTablePtr == NULL)
    {
        amdlibComputeFringeCriterion_FREEALL();
    }

    for (iBase = 0; iBase < nbBases; iBase++)
    {
        if (selection->band[band].nbSelectedFrames[iBase] == 0)
        {
            continue;
        }
        num_SNR_CorrFlux_re   = 0.0;
        num_SNR_CorrFlux_im   = 0.0;
        denom_SNR_CorrFlux_re = 0.0;
        denom_SNR_CorrFlux_im = 0.0;
        nbValid=0;

        for (iFrame = 0; iFrame < nbFrames; iFrame++)
        {
            if (selection->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE)
            {
                for (lVis = 0; lVis < nbLVis; lVis++)
                {
                    if (corrFluxTablePtr[iFrame][iBase].flag[lVis]==amdlibFALSE)
                    {
                        nbValid++;
                        /* Get useful information for computing */
                        sigma2_CorrFlux.re = 
                        corrFluxTablePtr[iFrame][iBase].sigma2Vis[lVis].re;
                        sigma2_CorrFlux.im = 
                        corrFluxTablePtr[iFrame][iBase].sigma2Vis[lVis].im;
                        CorrFlux.re = corrFluxTablePtr[iFrame][iBase].vis[lVis].re;
                        CorrFlux.im = corrFluxTablePtr[iFrame][iBase].vis[lVis].im;
                        
                        /* summed over wavelength */
                        num_SNR_CorrFlux_re += 
                        ((amdlibPow2(CorrFlux.re) / sigma2_CorrFlux.re) - 1) / 
                        sigma2_CorrFlux.re;
                        
                        num_SNR_CorrFlux_im +=
                        ((amdlibPow2(CorrFlux.im) / sigma2_CorrFlux.im) - 1) /
                        sigma2_CorrFlux.im;
                        
                        denom_SNR_CorrFlux_re += 1 / sigma2_CorrFlux.re;
                        
                        denom_SNR_CorrFlux_im += 1 / sigma2_CorrFlux.im;
                    }
                }
            }
        }
        if (nbValid!=0)
        {
            /* Compute fringe contrast SNR */
            sqFrgContrastSNR = num_SNR_CorrFlux_re / denom_SNR_CorrFlux_re +
            num_SNR_CorrFlux_im / denom_SNR_CorrFlux_im;
            frgContrastSNR = amdlibSignedSqrt(sqFrgContrastSNR);
            
            /* Store fringe contrast SNR */
            visTablePtr[iBin][iBase].bandFlag[band] = amdlibTRUE;
            visTablePtr[iBin][iBase].frgContrastSnrArray[band] = frgContrastSNR;
            visTablePtr[iBin][iBase].frgContrastSnr = frgContrastSNR;
        }
        else
        {
            visTablePtr[iBin][iBase].bandFlag[band] = amdlibFALSE;
            visTablePtr[iBin][iBase].frgContrastSnrArray[band] = amdlibBLANKING_VALUE;
            visTablePtr[iBin][iBase].frgContrastSnr = amdlibBLANKING_VALUE;
        }
    }
    amdlibComputeFringeCriterion_FREEALL();
}
#undef amdlibComputeFringeCriterion_FREEALL


/** Useful macro to free all dynamically allocated structures */ 
#define amdlibBinFringeCriterion_FREEALL()            \
    amdlibFree2DArrayWrapping((void **)corrFluxTablePtr); \
    amdlibFree2DArrayWrapping((void **)visTablePtr);
void amdlibBinFringeCriterion(/* Input */
                                  amdlibVIS       *instantCorrFlux,
                                  int             firstFrame,
                                  int             nbFrames,
                                  int             iBin,
                                  amdlibBAND      band,
                                  /* Output */
                                  amdlibVIS       *vis)
{
    /* loop indexes and associated limits */
    int iFrame, runningFrame; /* usually loop on number of frames */

    int iBase; /*usually loop on nbBases*/
    int nbBases = instantCorrFlux->nbBases; 
    /* Number of Baselines in Science Data*/
    int lVis = 0;
    int nbLVis = instantCorrFlux->nbWlen;
    int nbValid;
    
    /* Input values of the coherent flux */
    amdlibCOMPLEX CorrFlux;
    amdlibCOMPLEX sigma2_CorrFlux;

    /* Used for the fringes detection criterion */
    double num_SNR_CorrFlux_re;
    double num_SNR_CorrFlux_im;
    double denom_SNR_CorrFlux_re;
    double denom_SNR_CorrFlux_im;
    double frgContrastSNR;
    double sqFrgContrastSNR;

    amdlibVIS_TABLE_ENTRY **corrFluxTablePtr = NULL; 
    amdlibVIS_TABLE_ENTRY **visTablePtr = NULL; 
    static amdlibERROR_MSG errMsg;

    /* Computation of the fringe Criterion */
    /* This is useful for the fringe Criterion :
     * initialisation of the photometry and the sigma2 */

    amdlibLogTrace("amdlibBinFringeCriterion()");

    /* Wrap structures given as parametres */
    visTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(vis->table,
                                            vis->nbBases, vis->nbFrames,
                                            sizeof(amdlibVIS_TABLE_ENTRY),
                                            errMsg);
    if (visTablePtr == NULL)
    {
        amdlibBinFringeCriterion_FREEALL();
    }
    corrFluxTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(instantCorrFlux->table,
                                                    instantCorrFlux->nbBases,
                                                    instantCorrFlux->nbFrames,
                                                    sizeof(amdlibVIS_TABLE_ENTRY),
                                                    errMsg);
    if (corrFluxTablePtr == NULL)
    {
        amdlibBinFringeCriterion_FREEALL();
    }

    for (iBase = 0; iBase < nbBases; iBase++)
    {
        num_SNR_CorrFlux_re   = 0.0;
        num_SNR_CorrFlux_im   = 0.0;
        denom_SNR_CorrFlux_re = 0.0;
        denom_SNR_CorrFlux_im = 0.0;
        nbValid=0;
        for (iFrame = 0, runningFrame=firstFrame; iFrame < nbFrames; 
             iFrame++, runningFrame++)
        {
            for (lVis = 0; lVis < nbLVis; lVis++)
            {
                if(corrFluxTablePtr[runningFrame][iBase].flag[lVis]==amdlibFALSE)
                {
                    nbValid++;
                    /* Get useful information for computing */
                    sigma2_CorrFlux.re = 
                    corrFluxTablePtr[runningFrame][iBase].sigma2Vis[lVis].re;
                    sigma2_CorrFlux.im = 
                    corrFluxTablePtr[runningFrame][iBase].sigma2Vis[lVis].im;
                    CorrFlux.re = corrFluxTablePtr[runningFrame][iBase].vis[lVis].re;
                    CorrFlux.im = corrFluxTablePtr[runningFrame][iBase].vis[lVis].im;
                    
                    /* summed over wavelength */
                    num_SNR_CorrFlux_re += 
                    ((amdlibPow2(CorrFlux.re) / sigma2_CorrFlux.re) - 1) / 
                    sigma2_CorrFlux.re;
                    
                    num_SNR_CorrFlux_im +=
                    ((amdlibPow2(CorrFlux.im) / sigma2_CorrFlux.im) - 1) /
                    sigma2_CorrFlux.im;
                    
                    denom_SNR_CorrFlux_re += 1 / sigma2_CorrFlux.re;
                    
                    denom_SNR_CorrFlux_im += 1 / sigma2_CorrFlux.im;
                }
            }
        }
        if (nbValid!=0)
        {
            /* Compute fringe contrast SNR */
            sqFrgContrastSNR = num_SNR_CorrFlux_re / denom_SNR_CorrFlux_re +
            num_SNR_CorrFlux_im / denom_SNR_CorrFlux_im;
            frgContrastSNR = amdlibSignedSqrt(sqFrgContrastSNR);
            
            /* Store fringe contrast SNR */
            visTablePtr[iBin][iBase].bandFlag[band] = amdlibTRUE;
            visTablePtr[iBin][iBase].frgContrastSnrArray[band] = frgContrastSNR;
            visTablePtr[iBin][iBase].frgContrastSnr = frgContrastSNR;
        }
        else
        {
            visTablePtr[iBin][iBase].bandFlag[band] = amdlibFALSE;
            visTablePtr[iBin][iBase].frgContrastSnrArray[band] = amdlibBLANKING_VALUE;
            visTablePtr[iBin][iBase].frgContrastSnr = amdlibBLANKING_VALUE;
        }
    }
    
    amdlibBinFringeCriterion_FREEALL();
}
#undef amdlibBinFringeCriterion_FREEALL

/**
 * Compute global averaged values of squared visibilities (used as quality 
 * control).
 * 
 * @param vis2 structure used in input and also were results are stored.
 */
void amdlibAverageVis2(amdlibVIS2 *vis2)
{
    double w, w1, w2, w3, ww, ww1, ww2, ww3;
    double vis12 = 0.;
    double vis23 = 0.;
    double vis31 = 0.;
    double sigmaVis12 = 0.;
    double sigmaVis23 = 0.;
    double sigmaVis31 = 0.;
    int lVis, nbWlen = vis2->nbWlen;
    int iBin, nbBins = vis2->nbFrames;
    int nbTel = 2;
    int nbBases = vis2->nbBases;
    amdlibVIS2_TABLE_ENTRY **vis2TablePtr = NULL;
    static amdlibERROR_MSG errMsg;

    amdlibLogTrace("amdlibAverageVis2()");

    /* Wrap input parametre */
    vis2TablePtr = 
        (amdlibVIS2_TABLE_ENTRY **)amdlibWrap2DArray(vis2->table,
                                                nbBases, nbBins,
                                                sizeof(amdlibVIS2_TABLE_ENTRY),
                                                errMsg);
    if (vis2TablePtr == NULL)
    {
        amdlibFree2DArrayWrapping((void **)vis2TablePtr);
    }

    /* Initialize variables */
    if(nbBases == 3)
    {
        nbTel = 3;
    }
    vis2->vis12 = 0;
    vis2->vis23 = 0;
    vis2->vis31 = 0;
    vis2->sigmaVis12 = 0;
    vis2->sigmaVis23 = 0;
    vis2->sigmaVis31 = 0;
    ww1 = 0.;
    ww2 = 0.;
    ww3 = 0.;

    if(nbBases == 3)    
    {
        amdlibLogTrace(" Wlen   VIS2 12 (  err   ) VIS2 23 (  err   ) "
                       "VIS2 31 (  err   )"); 
    }
    else
    {
        amdlibLogTrace(" Wlen      VIS2 (  err   )");
    }

    /* Compute mean visibility per spectral channel */
    for (lVis = 0; lVis < nbWlen; lVis++)
    {
        vis12 = 0.;
        vis23 = 0.;
        vis31 = 0.;
        sigmaVis12 = 0.;
        sigmaVis23 = 0.;
        sigmaVis31 = 0.;
        w1 = 0.;
        w2 = 0.;
        w3 = 0.;

        for (iBin = 0; iBin < nbBins; iBin++)
        {
            w = 1.0 / amdlibPow2(vis2TablePtr[iBin][0].vis2Error[lVis]);
            w1 += w;  
            sigmaVis12 += vis2TablePtr[iBin][0].vis2Error[lVis] * w;
            vis12 += vis2TablePtr[iBin][0].vis2[lVis] * w; 
            if (nbTel == 3)
            {   
                w= 1.0 / amdlibPow2(vis2TablePtr[iBin][1].vis2Error[lVis]);
                w2 += w;
                sigmaVis23 += vis2TablePtr[iBin][1].vis2Error[lVis] * w;
                vis23 += vis2TablePtr[iBin][1].vis2[lVis] * w; 
                w = 1.0 / amdlibPow2(vis2TablePtr[iBin][2].vis2Error[lVis]);
                w3 += w;
                sigmaVis31 += vis2TablePtr[iBin][2].vis2Error[lVis] * w;
                vis31 += vis2TablePtr[iBin][2].vis2[lVis] * w; 
            }
        }
        vis12 /= w1;
        sigmaVis12 /= w1;
        if (nbBases == 3)
        {
            vis23 /= w2;
            sigmaVis23 /= w2;
            vis31 /= w3;
            sigmaVis31 /= w3;
        } 
        else  
        {
            vis23 = 0.;
            vis31 = 0.;
            sigmaVis23 = 0.;
            sigmaVis31 = 0.;
        } 
        ww = 1.0 / amdlibPow2(sigmaVis12);
        ww1 += ww;
        vis2->sigmaVis12 += sigmaVis12 * ww;
        vis2->vis12 += vis12 * ww;

        amdlibLogTrace("%d ", lVis);
        amdlibLogTrace("%8.3f(%8.03g)", vis12, sigmaVis12);

        if (nbBases == 3)
        {
            ww = 1.0 / amdlibPow2(sigmaVis23);
            ww2 += ww;
            vis2->sigmaVis23 += sigmaVis23 * ww;
            vis2->vis23 += vis23 * ww;

            ww = 1.0 / amdlibPow2(sigmaVis31);
            ww3 += ww;
            vis2->sigmaVis31 += sigmaVis31 * ww;
            vis2->vis31 += vis31 * ww;

            amdlibLogTrace("%8.3f(%8.03g)", vis23, sigmaVis23);
            amdlibLogTrace("%8.3f(%8.03g)", vis31, sigmaVis31);
        }
        else 
        {
            amdlibLogTrace("");
        }
    }

    amdlibLogTrace("");
    vis2->vis12 /= ww1;
    vis2->sigmaVis12 /= ww1;
    if (nbBases == 3)
    {
        vis2->vis23 /= ww2;
        vis2->vis31 /= ww3;
        vis2->sigmaVis23 /= ww2;
        vis2->sigmaVis31 /= ww3;
    }

    amdlibFree2DArrayWrapping((void **)vis2TablePtr);
}


/**
 * Fills the target structure from the values in the header of the raw data. 
 * 
 * @param data pointer to raw data.
 * @param target pointer to target structure
 * 
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.  */
amdlibCOMPL_STAT amdlibGetOiTargetFromRawData(amdlibRAW_DATA  *rawData,
                                              amdlibOI_TARGET *target)
{

    int i;
    int cIndex = 0;
    char targVal[amdlibKEYW_VAL_LEN];

    amdlibLogTrace("amdlibFillOiTargetTableEntry()");

    target->element[cIndex].targetId = 1;

    /* Retrieve from Configuration Keyword values used for various headers */
    for (i=0; i < rawData->insCfg.nbKeywords; i++)
    {
        if (strstr(rawData->insCfg.keywords[i].name, "ESO OBS TARG NAME") != NULL)
        {
            strncpy(targVal, rawData->insCfg.keywords[i].value, 
                    amdlibKEYW_VAL_LEN);
            amdlibStripQuotes(targVal);
            strncpy(target->element[cIndex].targetName, targVal, 16);
        }
        /*Use strncmp since RA or DEC are not unique in the header*/
        if (strncmp(rawData->insCfg.keywords[i].name, "RA      ", 8) == 0)
        {
            sscanf(rawData->insCfg.keywords[i].value, "%lf", 
                   &target->element[cIndex].raEp0);
        }
        if (strncmp(rawData->insCfg.keywords[i].name, "DEC     ", 8) == 0)
        {
            sscanf(rawData->insCfg.keywords[i].value, "%lf", 
                   &target->element[cIndex].decEp0);
        }
        if (strncmp(rawData->insCfg.keywords[i].name, "EQUINOX ", 8) == 0)
        {
            sscanf(rawData->insCfg.keywords[i].value, "%lf", 
                   &target->element[cIndex].equinox);
        }
        /* the following values are fixed until ESO's VLTICS writes a good 
         * OI_TARGET by itself!*/
        strncpy(target->element[cIndex].velTyp, "UNKNOWN",  8 );
        strncpy(target->element[cIndex].velDef, "OPTICAL",  8 );
        strncpy(target->element[cIndex].specTyp, "UNKNOWN",  16 );
    }
    return amdlibSUCCESS;
}


/**
 * completes the description of the vis structure
 * 
 * @param data pointer to science data.
 * @param vis the output vis structure.
 * @param errMsg error description message returned if function fails.
 * 
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.  */
amdlibCOMPL_STAT amdlibFillInVisTableHeader(/* Input */
                                            amdlibSCIENCE_DATA     *data,
                                            /* Output */
                                            amdlibVIS              *vis,
                                            amdlibERROR_MSG        errMsg)
{

    int nbTel = data->nbCols-1; 
    /*Number of Telescopes Used in the Science data*/

    int iBase; /* loop on nbBases */
    int nbBases = nbTel * (nbTel - 1) / 2; 
    /* Number of Baselines in Science Data*/

    /* The index of the binned visibility */ 
    int iBin;

    /* The number of visibilities stored in output table (binned) */
    int nbBins = vis->nbFrames;

    /* The nuber of frames of (unbinned) input table in 1 bin */
    int nbFramesInOneBin = data->nbFrames / nbBins;
    int iFrame;
    double time, expTime, dateObsMJD, mjdObsAtStart;

    double mjdObs = 0;
    double utcTime = 0;
    int i;
    amdlibVIS_TABLE_ENTRY **visTablePtr = NULL;

    double **uCoord, **vCoord;
    double uCoordMean[amdlibNBASELINE], vCoordMean[amdlibNBASELINE];

    amdlibLogTrace("amdlibFillInVisTableHeader()");

    /* Wrap output parametre */
    visTablePtr = 
        (amdlibVIS_TABLE_ENTRY **)amdlibWrap2DArray(vis->table,
                                            nbBases, nbBins,
                                            sizeof(amdlibVIS_TABLE_ENTRY),
                                            errMsg);
    if (visTablePtr == NULL)
    {
        amdlibFree2DArrayWrapping((void **)visTablePtr);
        return amdlibFAILURE;
    }

    /* Retrieve from Configuration Keyword values used for various headers */
    for (i = 0; i < data->insCfg.nbKeywords; i++)
    {
        if (strstr(data->insCfg.keywords[i].name, "MJD-OBS") != NULL)
        {
            sscanf(data->insCfg.keywords[i].value, "%lf", &mjdObs);
        }
        if (strncmp(data->insCfg.keywords[i].name, "UTC     ",8) != 0)
        {
            sscanf(data->insCfg.keywords[i].value, "%lf", &utcTime);
        }
    }

    uCoord=amdlibAlloc2DArrayDouble(nbBases, data->nbFrames, errMsg);
    vCoord=amdlibAlloc2DArrayDouble(nbBases, data->nbFrames, errMsg);
    amdlibComputeUVCoords(data,nbBases,uCoord,vCoord);
 
    if (mjdObs == 0)
    {
        amdlibLogWarning("No MJD-OBS value present in file !");
    }

    /* Define date-obs as the daystart, i.e., 00:00 the same day */
    mjdObsAtStart = (int)mjdObs;
    strncpy(vis->dateObs, amdlibMJD2ISODate(mjdObsAtStart), amdlibDATE_LENGTH);
    /* Then, define time as "the mean UTC time of the measurement in seconds 
     * since 0h on DATE-OBS.", and dateObsMJD as "the mean UTC time of the 
     * measurement expressed as a modified Julian Day.". The latter is just 
     * the Time Tag, the former is the differnce in seconds between the time 
     * tag and mjdObsAtStart. Of course since there is a binning process we 
     * must average these values over the bin.
     */

    expTime = data->expTime * nbFramesInOneBin; 

    /* Set various Vis Table Entry values */
    for (iBin = 0; iBin < nbBins; iBin++)
    { 
        /*compute averaged times */
        time = 0;
        for (iFrame = iBin * nbFramesInOneBin; 
             iFrame < (iBin + 1) * nbFramesInOneBin; 
             iFrame++)
        {
            time += data->timeTag[iFrame];
        }
        dateObsMJD = time / nbFramesInOneBin;

        time = (dateObsMJD - mjdObsAtStart) * 86400.0; /*in seconds...*/

        for (iBase = 0; iBase < nbBases; iBase++)
        {
            uCoordMean[iBase]=0;
            vCoordMean[iBase]=0;
            /*compute averaged coords */
            for (iFrame = iBin * nbFramesInOneBin; 
                 iFrame < (iBin + 1) * nbFramesInOneBin; 
                 iFrame++)
            {
                uCoordMean[iBase] += uCoord[iFrame][iBase];
                vCoordMean[iBase] += vCoord[iFrame][iBase];
            }
            uCoordMean[iBase] /= nbFramesInOneBin;
            vCoordMean[iBase] /= nbFramesInOneBin;
        }

        for (iBase = 0; iBase < nbBases; iBase++)
        { 
            visTablePtr[iBin][iBase].targetId = 1; 
            /*index to OI_TARGET table*/
            /* Resulting exposure time per Bin, in second */ 
            visTablePtr[iBin][iBase].expTime = expTime;
            visTablePtr[iBin][iBase].time = time;
            visTablePtr[iBin][iBase].dateObsMJD = dateObsMJD;
            /* U,V info */
            visTablePtr[iBin][iBase].uCoord = uCoordMean[iBase];
            visTablePtr[iBin][iBase].vCoord = vCoordMean[iBase];
            visTablePtr[iBin][0].stationIndex[0] = 
            data->issInfo.stationIndex[0];
            visTablePtr[iBin][0].stationIndex[1] = 
            data->issInfo.stationIndex[1];
            if (nbTel == 3)
            {
                visTablePtr[iBin][1].stationIndex[0] = 
                data->issInfo.stationIndex[1];
                visTablePtr[iBin][1].stationIndex[1] = 
                data->issInfo.stationIndex[2];
                visTablePtr[iBin][2].stationIndex[0] = 
                data->issInfo.stationIndex[0];
                visTablePtr[iBin][2].stationIndex[1] = 
                data->issInfo.stationIndex[2];
            }
        }
    }

    amdlibFree2DArrayWrapping((void **)visTablePtr);
    amdlibFree2DArrayDouble(uCoord);
    amdlibFree2DArrayDouble(vCoord);
    return amdlibSUCCESS;
}

/**
 * completes the description of the vis2 structure
 * 
 * @param data pointer to science data.
 * @param vis2 the output vis2 structure.
 * @param errMsg error description message returned if function fails.
 * 
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.  */
amdlibCOMPL_STAT amdlibFillInVis2TableHeader(/* Input */
                                             amdlibSCIENCE_DATA *data,
                                             /* Output */
                                             amdlibVIS2         *vis2,
                                             amdlibERROR_MSG    errMsg)
{

    int nbTel = data->nbCols - 1; 
    /*Number of Telescopes Used in the Science data*/

    int iBase; /* loop on nbBases */
    int nbBases = nbTel * (nbTel - 1) / 2; 
    /* Number of Baselines in Science Data*/

    /* The index of the binned visibility */ 
    int iBin;

    /* The number of visibilities stored in output table (binned) */
    int nbBins = vis2->nbFrames;

    /* The nuber of frames of (unbinned) input table in 1 bin */
    int nbFramesInOneBin = data->nbFrames / nbBins;
    int iFrame;
    double time, expTime, dateObsMJD, mjdObsAtStart;

    double mjdObs = 0;
    double utcTime = 0;
    int i;
    amdlibVIS2_TABLE_ENTRY **vis2TablePtr = NULL;

    double **uCoord, **vCoord;
    double uCoordMean[amdlibNBASELINE], vCoordMean[amdlibNBASELINE];

    amdlibLogTrace("amdlibFillInVis2TableHeader()");

    /* Wrap output parametre */
    vis2TablePtr = 
        (amdlibVIS2_TABLE_ENTRY **)amdlibWrap2DArray(vis2->table,
                                                nbBases, nbBins,
                                                sizeof(amdlibVIS2_TABLE_ENTRY),
                                                errMsg);
    if (vis2TablePtr == NULL)
    {
        amdlibFree2DArrayWrapping((void **)vis2TablePtr);
        return amdlibFAILURE;
    }

    /* Retrieve from Configuration Keyword values used for various headers */
    for (i = 0; i < data->insCfg.nbKeywords; i++)
    {
        if (strstr(data->insCfg.keywords[i].name, "MJD-OBS") != NULL)
        {
            sscanf(data->insCfg.keywords[i].value, "%lf", &mjdObs);
        }
        if (strncmp(data->insCfg.keywords[i].name, "UTC     ",8) != 0)
        {
            sscanf(data->insCfg.keywords[i].value, "%lf", &utcTime);
        }
    }

    uCoord=amdlibAlloc2DArrayDouble(nbBases, data->nbFrames, errMsg);
    vCoord=amdlibAlloc2DArrayDouble(nbBases, data->nbFrames, errMsg);
    amdlibComputeUVCoords(data,nbBases,uCoord,vCoord);
 
    if (mjdObs == 0)
    {
        amdlibLogWarning("No MJD-OBS value present in file !");
    }

    /* Define date-obs as the daystart, i.e., 00:00 the same day */
    mjdObsAtStart = (int)mjdObs;
    strncpy(vis2->dateObs, amdlibMJD2ISODate(mjdObsAtStart), amdlibDATE_LENGTH);
    /* Then, define time as "the mean UTC time of the measurement in seconds 
     * since 0h on DATE-OBS.", and dateObsMJD as "the mean UTC time of the 
     * measurement expressed as a modified Julian Day.". The latter is just 
     * the Time Tag, the former is the differnce in seconds between the time 
     * tag and mjdObsAtStart. Of course since there is a binning process we 
     * must average these values over the bin.
     */

    expTime = data->expTime * nbFramesInOneBin; 

    /* Set various Vis2 Table Entry values */
    for (iBin = 0; iBin < nbBins; iBin++)
    { 
        /*compute averaged times */
        time = 0;
        for (iFrame = iBin * nbFramesInOneBin; 
             iFrame < (iBin + 1) * nbFramesInOneBin; 
             iFrame++)
        {
            time += data->timeTag[iFrame];
        }
        dateObsMJD = time / nbFramesInOneBin;

        time = (dateObsMJD - mjdObsAtStart) * 86400.0; /*in seconds...*/

        for (iBase = 0; iBase < nbBases; iBase++)
        {
            uCoordMean[iBase]=0;
            vCoordMean[iBase]=0;
            /*compute averaged coords */
            for (iFrame = iBin * nbFramesInOneBin; 
                 iFrame < (iBin + 1) * nbFramesInOneBin; 
                 iFrame++)
            {
                uCoordMean[iBase] += uCoord[iFrame][iBase];
                vCoordMean[iBase] += vCoord[iFrame][iBase];
            }
            uCoordMean[iBase] /= nbFramesInOneBin;
            vCoordMean[iBase] /= nbFramesInOneBin;
        }

        for (iBase = 0; iBase < nbBases; iBase++)
        { 
            vis2TablePtr[iBin][iBase].targetId = 1; 
            /*index to OI_TARGET table*/
            /* Resulting exposure time per Bin, in second */ 
            vis2TablePtr[iBin][iBase].expTime = expTime;
            vis2TablePtr[iBin][iBase].time = time;
            vis2TablePtr[iBin][iBase].dateObsMJD = dateObsMJD;
            /* U,V info */
            vis2TablePtr[iBin][iBase].uCoord = uCoordMean[iBase];
            vis2TablePtr[iBin][iBase].vCoord = vCoordMean[iBase];
            vis2TablePtr[iBin][0].stationIndex[0] = 
            data->issInfo.stationIndex[0];
            vis2TablePtr[iBin][0].stationIndex[1] = 
            data->issInfo.stationIndex[1];
            if (nbTel == 3)
            {
                vis2TablePtr[iBin][1].stationIndex[0] = 
                data->issInfo.stationIndex[1];
                vis2TablePtr[iBin][1].stationIndex[1] = 
                data->issInfo.stationIndex[2];
                vis2TablePtr[iBin][2].stationIndex[0] = 
                data->issInfo.stationIndex[0];
                vis2TablePtr[iBin][2].stationIndex[1] = 
                data->issInfo.stationIndex[2];
            }
        }
    }

    amdlibFree2DArrayWrapping((void **)vis2TablePtr);
    amdlibFree2DArrayDouble(uCoord);
    amdlibFree2DArrayDouble(vCoord);
    return amdlibSUCCESS;
}


/**
 * completes the description of the vis3 structure
 * 
 * @param data pointer to science data.
 * @param vis3 the output vis3 tructure
 * @param errMsg error description message returned if function fails.
 * 
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.  */
amdlibCOMPL_STAT amdlibFillInVis3TableHeader(/* Input */
                                             amdlibSCIENCE_DATA *data,
                                             /* Output */
                                             amdlibVIS3         *vis3,
                                             amdlibERROR_MSG    errMsg)
{

    int nbTel = data->nbCols - 1; 
    /*Number of Telescopes Used in the Science data*/

    int iClos;
    const int nbClos = 1; /* Number of Closures FIXED */

    /* The index of the binned visibility */ 
    int iBin;

    /* The number of visibilities stored in output table (binned) */
    int nbBins = vis3->nbFrames;

    /* The nuber of frames of (unbinned) input table in 1 bin */
    int nbFramesInOneBin = data->nbFrames / nbBins;
    int iFrame;
    double time, expTime, dateObsMJD, mjdObsAtStart;

    double mjdObs = 0;
    double utcTime = 0;
    int i;
    /* give the impression we may have more than 3 baselines ...
     * without really doing the job! */
    const int tel1 = 0, tel2 = 1, tel3 = 2, nbBases = 3;
    amdlibVIS3_TABLE_ENTRY **vis3TablePtr = NULL;

    double **uCoord, **vCoord;
    double uCoordMean[amdlibNBASELINE], vCoordMean[amdlibNBASELINE];

    amdlibLogTrace("amdlibFillInVis3TableHeader()");

    /*Return if no use. Shall we confess we have been invoked in vain?*/
    if (nbTel < 3)
    {
        return amdlibSUCCESS;
    }

    /* Wrap output parametre */
    vis3TablePtr = 
        (amdlibVIS3_TABLE_ENTRY **)amdlibWrap2DArray(vis3->table,
                                                nbClos, nbBins,
                                                sizeof(amdlibVIS3_TABLE_ENTRY),
                                                errMsg);
    if (vis3TablePtr == NULL)
    {
        amdlibFree2DArrayWrapping((void **)vis3TablePtr);
        return amdlibFAILURE;
    }

    /* Retrieve from Configuration Keyword values used for various headers */
    for (i = 0; i < data->insCfg.nbKeywords; i++)
    {
        if (strstr(data->insCfg.keywords[i].name, "MJD-OBS") != NULL)
        {
            sscanf(data->insCfg.keywords[i].value, "%lf", &mjdObs);
        }
        if (strncmp(data->insCfg.keywords[i].name, "UTC     ",8) != 0)
        {
            sscanf(data->insCfg.keywords[i].value, "%lf", &utcTime);
        }
    }

    uCoord=amdlibAlloc2DArrayDouble(nbBases, data->nbFrames, errMsg);
    vCoord=amdlibAlloc2DArrayDouble(nbBases, data->nbFrames, errMsg);
    amdlibComputeUVCoords(data,nbBases,uCoord,vCoord);
 
    if (mjdObs == 0)
    {
        amdlibLogWarning("No MJD-OBS value present in file !");
    }

    /* Define date-obs as the daystart, i.e., 00:00 the same day */
    mjdObsAtStart = (int)mjdObs;
    strncpy (vis3->dateObs, amdlibMJD2ISODate(mjdObsAtStart),amdlibDATE_LENGTH);
    /* Then, define time as "the mean UTC time of the measurement in seconds
     * since 0h on DATE-OBS.", and dateObsMJD as "the mean UTC time of the
     * measurement expressed as a modified Julian Day.". The latter is just the
     * Time Tag, the former is the differnce in seconds between the time tag and
     * mjdObsAtStart. Of course since there is a binning process we must average
     * these values over the bin.
     */

    expTime = data->expTime * nbFramesInOneBin; 

    /* Set various Vis3 Table Entry values */
    for (iBin = 0; iBin < nbBins; iBin++)
    { 
        /*compute averaged times */
        time = 0;
        for (iFrame = iBin * nbFramesInOneBin; 
             iFrame < (iBin + 1) * nbFramesInOneBin; 
             iFrame++)
        {
            time += data->timeTag[iFrame];
        }
        dateObsMJD = time / nbFramesInOneBin;

        time = (dateObsMJD - mjdObsAtStart) * 86400.0; /*in seconds...*/

        uCoordMean[0]=0;
        vCoordMean[0]=0;
        uCoordMean[1]=0;
        vCoordMean[1]=0;
        /*compute averaged coords */
        for (iFrame = iBin * nbFramesInOneBin; 
             iFrame < (iBin + 1) * nbFramesInOneBin; 
             iFrame++)
        {
            uCoordMean[0] += uCoord[iFrame][0];
            vCoordMean[0] += vCoord[iFrame][0];
            uCoordMean[1] += uCoord[iFrame][1];
            vCoordMean[1] += vCoord[iFrame][1];
        }
        uCoordMean[0] /= nbFramesInOneBin;
        vCoordMean[0] /= nbFramesInOneBin;
        uCoordMean[1] /= nbFramesInOneBin;
        vCoordMean[1] /= nbFramesInOneBin;

        for (iClos = 0; iClos < nbClos; iClos++)
        {
            vis3TablePtr[iBin][iClos].targetId = 1; /*index to OI_TARGET table*/
            vis3TablePtr[iBin][iClos].expTime = expTime;
            vis3TablePtr[iBin][iClos].time = time;
            vis3TablePtr[iBin][iClos].dateObsMJD = dateObsMJD;
            vis3TablePtr[iBin][iClos].u1Coord = uCoordMean[0];
            vis3TablePtr[iBin][iClos].v1Coord = vCoordMean[0];
            vis3TablePtr[iBin][iClos].u2Coord = uCoordMean[1];
            vis3TablePtr[iBin][iClos].v2Coord = vCoordMean[1];
            /*this code could possibly be extended to more telescopes!*/
            vis3TablePtr[iBin][iClos].stationIndex[0] = 
                data->issInfo.stationIndex[tel1];
            vis3TablePtr[iBin][iClos].stationIndex[1] = 
                data->issInfo.stationIndex[tel2];
            vis3TablePtr[iBin][iClos].stationIndex[2] = 
                data->issInfo.stationIndex[tel3];
        }
    }

    amdlibFree2DArrayWrapping((void **)vis3TablePtr);
    amdlibFree2DArrayDouble(uCoord);
    amdlibFree2DArrayDouble(vCoord);
    return amdlibSUCCESS;
}


