/*
 *  vbHmmTsFret.c
 *  Model-specific core functions for VB-HMM-TS.
 *
 *  Created by OKAMOTO Kenji, SAKO Yasushi and RIKEN
 *  Copyright 2011-2015
 *  Cellular Informatics Laboratory, Advance Science Institute, RIKEN, Japan.
 *  All rights reserved.
 *
 *  Ver. 1.1.0
 *  Last modified on 2015.09.17
 */

#include "vbHmmTsFret.h"
#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_sf_psi.h>
#include <string.h>
#include "rand.h"

#ifdef _OPENMP
#include "omp.h"
#endif

//// Uncomment one/both of the following defenitions to activate constraint on I or/and K.
//#define  INTENSITY_CAP
//#define  TRANSITION_RATE_CAP
#ifdef INTENSITY_CAP
#define  maxIntensityRatio  10.0
#endif
#ifdef TRANSITION_RATE_CAP
#define  minPhotonNumPerState  10.0
#endif

#define  MAX(a,b)  ((a)>(b)?(a):(b))
#define  MIN(a,b)  ((a)<(b)?(a):(b))

//static int isGlobalAnalysis = 0;

void setFunctions_tsFret(){
    commonFunctions funcs;
    funcs.newModelParameters    = newModelParameters_tsFret;
    funcs.freeModelParameters   = freeModelParameters_tsFret;
    funcs.newModelStats         = newModelStats_tsFret;
    funcs.freeModelStats        = freeModelStats_tsFret;
    funcs.initializeVbHmm       = initializeVbHmm_tsFret;
    funcs.pTilde_z1             = pTilde_z1_tsFret;
    funcs.pTilde_zn_zn1         = pTilde_zn_zn1_tsFret;
    funcs.pTilde_xn_zn          = pTilde_xn_zn_tsFret;
    funcs.calcStatsVars         = calcStatsVars_tsFret;
    funcs.maximization          = maximization_tsFret;
    funcs.varLowerBound         = varLowerBound_tsFret;
    funcs.reorderParameters     = reorderParameters_tsFret;
    funcs.outputResults         = outputResults_tsFret;
    setFunctions( funcs );
}

//void setGFunctions_tsFret(){
//    gCommonFunctions funcs;
//    funcs.newModelParameters    = newModelParameters_tsFret;
//    funcs.freeModelParameters   = freeModelParameters_tsFret;
//    funcs.newModelStats         = newModelStats_tsFret;
//    funcs.freeModelStats        = freeModelStats_tsFret;
//    funcs.newModelStatsG        = newModelStatsG_tsFret;
//    funcs.freeModelStatsG       = freeModelStatsG_tsFret;
//    funcs.initializeVbHmmG      = initializeVbHmmG_tsFret;
//    funcs.pTilde_z1             = pTilde_z1_tsFret;
//    funcs.pTilde_zn_zn1         = pTilde_zn_zn1_tsFret;
//    funcs.pTilde_xn_zn          = pTilde_xn_zn_tsFret;
//    funcs.calcStatsVarsG        = calcStatsVarsG_tsFret;
//    funcs.maximizationG         = maximizationG_tsFret;
//    funcs.varLowerBoundG        = varLowerBoundG_tsFret;
//    funcs.reorderParametersG    = reorderParametersG_tsFret;
//    funcs.outputResultsG        = outputResultsG_tsFret;
//    setGFunctions( funcs );
//    isGlobalAnalysis = 1;
//}

void outputResults_tsFret( xn, gv, iv, logFP )
xnDataSet *xn;
globalVars *gv;
indVars *iv;
FILE *logFP;
{
    outputTsFretResults( xn, gv, iv, logFP );
}

//void outputResultsG_tsFret( xns, gv, ivs, logFP )
//xnDataBundle *xns;
//globalVars *gv;
//indVarBundle *ivs;
//FILE *logFP;
//{
//    outputTsFretResultsG( xns, gv, ivs, logFP );
//}

void *newModelParameters_tsFret( xn, sNo )
xnDataSet *xn;
int sNo;
{
    int i;
    tsFretParameters *p = (void*)malloc( sizeof(tsFretParameters) );

    p->uPiArr = (double*)malloc( sNo * sizeof(double) );
    p->sumUPi = 0.0;
    // hyperparameter for p( k(i,j) ) (i != j)
    p->uKMat = (double**)malloc( sNo * sizeof(double*) );
    for( i = 0 ; i < sNo ; i++ ){
        p->uKMat[i] = (double*)malloc( sNo * sizeof(double) );
    }
    p->sumUKArr = (double*)malloc( sNo * sizeof(double) );
    // hyperparameter for p( I(k) )
    p->aIArr = (double*)malloc( sNo * sizeof(double) );
    p->bIArr = (double*)malloc( sNo * sizeof(double) );
    // hyperparameter for p( E(i) )
    p->uEArr = (double*)malloc( sNo * sizeof(double) );
    p->vEArr = (double*)malloc( sNo * sizeof(double) );

    // parameters
    p->avgPi = (double *)malloc( sNo * sizeof(double) );
    p->avgLnPi = (double *)malloc( sNo * sizeof(double) );
    p->avgI = (double *)malloc( sNo * sizeof(double) );
    p->avgLnI = (double *)malloc( sNo * sizeof(double) );
    p->avgK = (double **)malloc( sNo * sizeof(double*) );
    p->avgLnK = (double **)malloc( sNo * sizeof(double*) );
    for( i = 0 ; i < sNo ; i++ ){
        p->avgK[i] = (double *)malloc( sNo * sizeof(double) );
        p->avgLnK[i] = (double *)malloc( sNo * sizeof(double) );
    }
    p->avgLnKI = (double *)malloc( sNo * sizeof(double) );
    p->avgE = (double *)malloc( sNo * sizeof(double) );
    p->avgLnE = (double **)malloc( sNo * sizeof(double*) );
    for( i = 0 ; i < sNo ; i++ )
    {   p->avgLnE[i] = (double *)malloc( 2 * sizeof(double) );   }

    return p;
}

void freeModelParameters_tsFret( p, xn, sNo )
void **p;
xnDataSet *xn;
int sNo;
{
    tsFretParameters *gp = *p;
    int i;

    free( gp->uPiArr );
    for( i = 0 ; i < sNo ; i++ ){
        free( gp->uKMat[i] );
    }
    free( gp->uKMat );
    free( gp->sumUKArr );
    free( gp->aIArr );
    free( gp->bIArr );
    free( gp->uEArr );
    free( gp->vEArr );

    free( gp->avgPi );
    free( gp->avgLnPi );
    free( gp->avgI );
    free( gp->avgLnI );
    for( i = 0 ; i < sNo ; i++ ){
        free( gp->avgK[i] );
        free( gp->avgLnK[i] );
    }
    free( gp->avgK );
    free( gp->avgLnK );
    free( gp->avgLnKI );
    free( gp->avgE );
    for( i = 0 ; i < sNo ; i++ ){
        free( gp->avgLnE[i] );
    }
    free( gp->avgLnE );

    free( *p );
    *p = NULL;
}

void *newModelStats_tsFret( xn, gv, iv )
xnDataSet *xn;
globalVars *gv;
indVars *iv;
{
//    if( isGlobalAnalysis == 0 ){
        int sNo = gv->sNo;
        tsFretStats *s = (tsFretStats*)malloc( sizeof(tsFretStats) );

        int i;
        s->Ni = (double *)malloc( sNo * sizeof(double) );
        s->Ti = (double *)malloc( sNo * sizeof(double) );
        s->Nii = (double *)malloc( sNo * sizeof(double) );
        s->Nij = (double *)malloc( sNo * sizeof(double) );
        s->Mij = (double **)malloc( sNo * sizeof(double*) );
        for( i = 0 ; i < sNo ; i++ )
        {   s->Mij[i] = (double *)malloc( sNo * sizeof(double) );   }
        s->eps = (double *)malloc( sNo * sizeof(double) );

    return s;

//    } else {
//
//        return NULL;
//
//    }
}

void freeModelStats_tsFret( s, xn, gv, iv )
void **s;
xnDataSet *xn;
globalVars *gv;
indVars *iv;
{
//    if( isGlobalAnalysis == 0 ){
        int sNo = gv->sNo;
        tsFretStats *gs = *s;
        int i;
        free( gs->Ni );
        free( gs->Ti );
        free( gs->Nii );
        free( gs->Nij );
        for( i = 0 ; i < sNo ; i++ )
        {   free( gs->Mij[i] );   }
        free( gs->Mij );
        free( gs->eps );

        free( gs );
        *s = NULL;
//    }
}

//void *newModelStatsG_tsFret( xns, gv, ivs)
//xnDataBundle *xns;
//globalVars *gv;
//indVarBundle *ivs;
//{
//    int sNo = gv->sNo;
//    tsGlobalStats *gs = (tsGlobalStats*)malloc( sizeof(tsGlobalStats) );
//
//    return gs;
//}

//void freeModelStatsG_tsFret( gs, xns, gv, ivs )
//void **gs;
//xnDataBundle *xns;
//globalVars *gv;
//indVarBundle *ivs;
//{
//
//    free( *gs );
//    *gs = NULL;
//}

void initializeVbHmm_tsFret( xn, gv, iv )
xnDataSet *xn;
globalVars *gv;
indVars *iv;
{
    tsFretData *d = xn->data;
    int sNo = gv->sNo;
    tsFretParameters *p = gv->params;
    int i, j;

    // hyperparameter for p( pi(i) )
    p->sumUPi = 0.0;
    for( i = 0 ; i < sNo ; i++ ){
        p->uPiArr[i] = 1.0;
        p->sumUPi += p->uPiArr[i];
    }

    // hyperparameter for p( k(i,j) ) (i != j)
    for( i = 0 ; i < sNo ; i++ ){
        p->uKMat[i] = (double*)malloc( sNo * sizeof(double) );
        p->sumUKArr[i] = 0.0;
        for( j = 0 ; j < sNo ; j++ ){
            p->uKMat[i][j] = 1.0;
            if( j != i ){
                p->sumUKArr[i] += p->uKMat[i][j];
            }
        }
    }

    double meanI = (double)xn->N / d->T;

    // hyperparameter for p( I(k) )
    for( i = 0 ; i < sNo ; i++ ){
        p->aIArr[i] = 1.0;
        p->bIArr[i] = 1.0 / meanI;
    }

    // hyperparameter for p( E(i) )
    for( i = 0 ; i < sNo ; i++ ){
        p->uEArr[i] = 1.0;
        p->vEArr[i] = 1.0;
    }

    initialize_indVars_tsFret( xn, gv, iv );

    calcStatsVars_tsFret( xn, gv, iv );
    maximization_tsFret( xn, gv, iv );
}

//void initializeVbHmmG_tsFret( xns, gv, ivs )
//xnDataBundle *xns;
//globalVars *gv;
//indVarBundle *ivs;
//{
//}

void initialize_indVars_tsFret( xn, gv, iv )
xnDataSet *xn;
globalVars *gv;
indVars *iv;
{
    size_t dLen = xn->N;
    int sNo = gv->sNo;
    double **gmMat = iv->gmMat;

    int i;
    size_t n;
    double sumPar;
    for( n = 0 ; n < dLen ; n++ ){
        sumPar = 0.0;
        for( i = 0 ; i < sNo ; i++ ){
            gmMat[n][i] = enoise(1.0) + 1.0;
            sumPar += gmMat[n][i];
        }
        for( i = 0 ; i < sNo ; i++ ){
            gmMat[n][i] /= sumPar;
        }
    }
}

xnDataSet *newXnDataSet_tsFret( filename )
const char *filename;
{
    xnDataSet *xn = (xnDataSet*)malloc( sizeof(xnDataSet) );
    xn->name = (char*)malloc( strlen(filename) + 2 );
    strncpy( xn->name, filename, strlen(filename)+1 );
    xn->data = (tsFretData*)malloc( sizeof(tsFretData) );
    tsFretData *d = (tsFretData*)xn->data;
    d->T = 0.0;
    d->dt = NULL;
    d->time = NULL;
    d->ch = NULL;
    return xn;
}

void freeXnDataSet_tsFret( xn )
xnDataSet **xn;
{
    tsFretData *d = (tsFretData*)(*xn)->data;
    free( d->dt );
    free( d->time );
    free( d->ch );
    free( (*xn)->data );
    free( (*xn)->name );
    free( *xn );
    *xn = NULL;
}

double pTilde_z1_tsFret( i, params )
int i;
void *params;
{
    tsFretParameters *p = (tsFretParameters*)params;
    return exp( p->avgLnPi[i] );
}

double pTilde_zn_zn1_tsFret( i, j, params )
int i, j;
void *params;
{
    tsFretParameters *p = (tsFretParameters*)params;
    if( i == j ){
        return exp( p->avgLnI[i] - p->avgLnKI[i] );
    } else {
        return exp( p->avgLnK[i][i] + p->avgLnK[i][j] - p->avgLnKI[i] );
    }
}

double pTilde_xn_zn_tsFret( xn, n, i, params )
xnDataSet *xn;
size_t n;
int i;
void *params;
{
    tsFretParameters *p = (tsFretParameters*)params;
    tsFretData *d = (tsFretData*)xn->data;
    return exp( p->avgLnI[i] - (p->avgI[i] * d->dt[n]) + p->avgLnE[i][d->ch[n]] );
}

void calcStatsVars_tsFret( xn, gv, iv )
xnDataSet *xn;
globalVars *gv;
indVars *iv;
{
    tsFretData *d = (tsFretData*)xn->data;
    tsFretStats *s = (tsFretStats*)iv->stats;
    size_t dLen = xn->N;
    int sNo = gv->sNo;
    double **gmMat = iv->gmMat, ***xiMat = iv->xiMat;
    double *Ni = s->Ni, *Ti = s->Ti, *eps = s->eps;
    double *Nii = s->Nii, *Nij = s->Nij, **Mij = s->Mij;
    size_t n;
    int i, j;

    for( i = 0 ; i < sNo ; i++ ){
        Ni[i] = 1e-10;
        Ti[i] = 1e-10;
        eps[i] = 1e-10;
        Nii[i] = 0.0;
        Nij[i] = 0.0;
        for( j = 0 ; j < sNo ; j++ ){
            Mij[i][j] = 1e-10;
        }
    }
    for( n = 0 ; n < dLen ; n++ ){
        for( i = 0 ; i < sNo ; i++ ){
            Ni[i]  += gmMat[n][i];
            Ti[i]  += gmMat[n][i] * d->dt[n];
            eps[i] += gmMat[n][i] * d->ch[n];
            Nii[i] += xiMat[n][i][i];
            for( j = 0 ; j < sNo ; j++ ){
                if( j != i ){
                    Mij[i][j] += xiMat[n][i][j];
                    Nij[i]    += xiMat[n][i][j];
                }
            }
        }
    }
    for( i = 0 ; i < sNo ; i++ ){
        Nii[i] = MAX( Nii[i], 1.0 );
        Nij[i] = MAX( Nij[i], 1.0 );
    }
}

//void calcStatsVarsG_tsFret( xns, gv, ivs )
//xnDataBundle *xns;
//globalVars *gv;
//indVarBundle *ivs;
//{
//}

void maximization_tsFret( xn, gv, iv )
xnDataSet *xn;
globalVars *gv;
indVars *iv;
{
    tsFretParameters *p = (tsFretParameters*)gv->params;
    tsFretStats *s = (tsFretStats*)iv->stats;
    int sNo = gv->sNo;
    double **gmMat = iv->gmMat;
    double *avgPi = p->avgPi, *avgLnPi = p->avgLnPi, **avgK = p->avgK, **avgLnK = p->avgLnK;
    double *avgLnKI = p->avgLnKI, *avgI = p->avgI, *avgLnI = p->avgLnI;
    double *avgE = p->avgE, **avgLnE = p->avgLnE;
    double *uPiArr = p->uPiArr, sumUPi = p->sumUPi, *aIArr = p->aIArr, *bIArr = p->bIArr;
    double **uKMat = p->uKMat, *sumUKArr = p->sumUKArr;
    double *uEArr = p->uEArr, *vEArr = p->vEArr;
    double *Ni = s->Ni, *Ti = s->Ti, *eps = s->eps;
    double *Nii = s->Nii, *Nij = s->Nij, **Mij = s->Mij;
    int i, j;

    for( i = 0 ; i < sNo ; i++ ){
        avgPi[i] = ( uPiArr[i] + gmMat[0][i] ) / ( sumUPi + 1.0 );
        avgLnPi[i] = gsl_sf_psi( uPiArr[i] + gmMat[0][i] ) - gsl_sf_psi( sumUPi + 1.0 );

        avgK[i][i] = (Ni[i] + aIArr[i]) * Nij[i] / (Nii[i] - 1.0) / (Ti[i] + bIArr[i]);
        avgLnK[i][i]  = gsl_sf_psi(Ni[i] + aIArr[i]) + gsl_sf_psi(Nij[i]);
        avgLnK[i][i] += -gsl_sf_psi(Nii[i]) - log(Ti[i] + bIArr[i]);

        avgI[i] = (Ni[i] + aIArr[i]) / (Ti[i] + bIArr[i]);
        avgLnI[i] = gsl_sf_psi(Ni[i] + aIArr[i]) - log(Ti[i] + bIArr[i]);

        avgLnKI[i]  = gsl_sf_psi(Ni[i] + aIArr[i]) + gsl_sf_psi(Nii[i] + Nij[i]);
        avgLnKI[i] += -gsl_sf_psi(Nii[i]) - log(Ti[i] + bIArr[i]);

#ifdef INTENSITY_CAP
        double meanI = (double)xnWv->N / xnWv->T;
        avgI[i]  = MIN( avgI[i], maxIntensityRatio * meanI );
        avgLnI[i]  = MIN( avgLnI[i], log(maxIntensityRatio * meanI) );
#endif
#ifdef TRANSITION_RATE_CAP
        avgK[i][i] = MIN( avgK[i][i], avgI[i]/minPhotonNumPerState );
        avgLnK[i][i]  = MIN( avgLnK[i][i], avgLnI[i] - log(minPhotonNumPerState) );
        avgLnKI[i]  = MIN( avgLnKI[i], avgLnI[i] + log(1.0 + 1.0/minPhotonNumPerState) );
#endif

        for( j = 0 ; j < sNo ; j++ ){
            if( i != j ){
                avgK[i][j] = ( uKMat[i][j] + Mij[i][j] ) / ( sumUKArr[i] + Nij[i] );
                avgLnK[i][j] = gsl_sf_psi( uKMat[i][j] + Mij[i][j] ) - gsl_sf_psi( sumUKArr[i] + Nij[i] );
            }
        }

        avgE[i] = ( uEArr[i] + eps[i] ) / ( uEArr[i] + vEArr[i] + Ni[i] );
        avgLnE[i][0]  = gsl_sf_psi( vEArr[i] + Ni[i] - eps[i] );    // ln(1-E) for donor
        avgLnE[i][0] -= gsl_sf_psi( uEArr[i] + vEArr[i] + Ni[i] );
        avgLnE[i][1]  = gsl_sf_psi( uEArr[i] + eps[i] );            // ln(E) for acceptor
        avgLnE[i][1] -= gsl_sf_psi( uEArr[i] + vEArr[i] + Ni[i] );
    }
}

//void maximizationG_tsFret( xns, gv, ivs )
//xnDataBundle *xns;
//globalVars *gv;
//indVarBundle *ivs;
//{
//}

double varLowerBound_tsFret( xn, gv, iv )
xnDataSet *xn;
globalVars *gv;
indVars *iv;
{
    tsFretParameters *p = (tsFretParameters*)gv->params;
    tsFretStats *s = (tsFretStats*)iv->stats;
    size_t dLen = xn->N;
    int sNo = gv->sNo;
    double **gmMat = iv->gmMat, *cn = iv->cn;
    double *avgLnPi = p->avgLnPi, **avgLnK = p->avgLnK, *avgI = p->avgI, *avgLnI = p->avgLnI;
    double *avgLnKI = p->avgLnKI, **avgLnE = p->avgLnE;
    double *uPiArr = p->uPiArr, sumUPi = p->sumUPi, *aIArr = p->aIArr, *bIArr = p->bIArr;
    double **uKMat = p->uKMat, *sumUKArr = p->sumUKArr;
    double *uEArr = p->uEArr, *vEArr = p->vEArr;
    double *Ni = s->Ni, *Ti = s->Ti, *eps = s->eps;
    double *Nii = s->Nii, *Nij = s->Nij, **Mij = s->Mij;
    size_t n;
    int i, j;

    double lnpPi = gsl_sf_lngamma(sumUPi);
    double Ck = 1.0, lnpKii = sNo * log(Ck);
    double lnpKij = 0.0;
    double lnpI = 0.0;
    double lnpE = 0.0;
    double lnqPi = gsl_sf_lngamma(sumUPi + 1);
    double lnqKiiI = 0.0;
    double lnqKij = 0.0;
    double lnqE = 0.0;
    for( i = 0 ; i < sNo ; i++ ){
        lnpPi += (uPiArr[i]-1.0) * avgLnPi[i] - gsl_sf_lngamma(uPiArr[i]);

        lnpKii -= avgLnK[i][i];

        lnpI += aIArr[i] * log(bIArr[i]) - gsl_sf_lngamma(aIArr[i]);
        lnpI += (aIArr[i]-1.0)*avgLnI[i] - bIArr[i]*avgI[i];

        lnpE += gsl_sf_lngamma(uEArr[i]+vEArr[i]) - gsl_sf_lngamma(uEArr[i]);
        lnpE += -gsl_sf_lngamma(vEArr[i]);
        lnpE += (uEArr[i]-1.0)*avgLnE[i][1] + (vEArr[i]-1.0)*avgLnE[i][0];

        lnqPi += (uPiArr[i]+gmMat[0][i]-1.0) * (gsl_sf_psi(uPiArr[i]+gmMat[0][i]) - gsl_sf_psi(sumUPi+1.0));
        lnqPi -= gsl_sf_lngamma(uPiArr[i] + gmMat[0][i]);

        lnqKiiI += gsl_sf_lngamma(Nii[i] + Nij[i]) + (Ni[i] + aIArr[i]) * log(Ti[i] + bIArr[i]);
        lnqKiiI += -gsl_sf_lngamma(Ni[i] + aIArr[i]) -gsl_sf_lngamma(Nii[i]);
        lnqKiiI += -gsl_sf_lngamma(Nij[i]) + (Nij[i]-1.0)*avgLnK[i][i];
        lnqKiiI += (Ni[i] + Nii[i] + aIArr[i] - 1.0)*avgLnI[i];
        lnqKiiI += -(Nii[i] + Nij[i])*avgLnKI[i] - (Ti[i] + bIArr[i])*avgI[i];

        lnqE += gsl_sf_lngamma(uEArr[i] + vEArr[i] + Ni[i]);
        lnqE -= gsl_sf_lngamma(uEArr[i] + eps[i]);
        lnqE -= gsl_sf_lngamma(vEArr[i] + Ni[i] - eps[i]);
        lnqE += (uEArr[i] + eps[i] - 1.0)*avgLnE[i][1];
        lnqE += (vEArr[i] + Ni[i] - eps[i] - 1.0)*avgLnE[i][0];

        lnpKij += gsl_sf_lngamma(sumUKArr[i]);
        lnqKij += gsl_sf_lngamma(sumUKArr[i] + Nij[i]);
        for( j = 0 ; j < sNo ; j++ ){
            if( j != i ){
                lnpKij += (uKMat[i][j]-1.0)*avgLnK[i][j] - gsl_sf_lngamma(uKMat[i][j]);
                lnqKij += (uKMat[i][j]+Mij[i][j]-1)*(gsl_sf_psi(uKMat[i][j]+Mij[i][j])-gsl_sf_psi(sumUKArr[i]+Nij[i]));
                lnqKij -= gsl_sf_lngamma( uKMat[i][j] + Mij[i][j] );
            }
        }
    }

    double lnpX = 0.0;
    for( n = 0 ; n < dLen ; n++ ){
        lnpX += log( cn[n] );
    }

    double val;
    val  = lnpPi + lnpKii + lnpKij + lnpI + lnpE;
    val -= lnqPi + lnqKiiI + lnqKij + lnqE;
    val += lnpX;

    return val;
}

//double varLowerBoundG_tsFret( xns, gv, ivs )
//xnDataBundle *xns;
//globalVars *gv;
//indVarBundle *ivs;
//{
//}

void reorderParameters_tsFret( xn, gv, iv )
xnDataSet *xn;
globalVars *gv;
indVars *iv;
{
    tsFretParameters *p = (tsFretParameters*)gv->params;
    tsFretStats *s = (tsFretStats*)iv->stats;
    size_t dLen = xn->N;
    int sNo = gv->sNo;
    double **gmMat = iv->gmMat, ***xiMat = iv->xiMat;
    double *avgPi = p->avgPi, *avgLnPi = p->avgLnPi, **avgK = p->avgK;
    double **avgLnK = p->avgLnK, *avgLnKI = p->avgLnKI;
    double *avgI = p->avgI, *avgLnI = p->avgLnI, *avgE = p->avgE, **avgLnE = p->avgLnE;
    double *Ni = s->Ni, *Ti = s->Ti, *eps = s->eps;
    size_t n;
    int i, j;

    int *index = (int*)malloc( sNo * sizeof(int) );
    double *store = (double*)malloc( sNo * sizeof(double) );
    double **s2D = (double**)malloc( sNo * sizeof(double*) );
    for( i = 0 ; i < sNo ; i++ )
    {   s2D[i] = (double*)malloc( MAX(sNo,2) * sizeof(double) );   }

    // index indicates order of avgE values (0=biggest avgE -- sNo=smallest avgE).
    for( i = 0 ; i < sNo ; i++ ){
        index[i] = sNo - 1;
        for( j = 0 ; j < sNo ; j++ ){
            if( j != i ){
                if( avgE[i] < avgE[j] ){
                    index[i]--;
                } else if( avgE[i] == avgE[j] ){
                    if( j > i )
                    {   index[i]--;   }
                }
            }
        }
    }

    for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = avgPi[i];   }
    for( i = 0 ; i < sNo ; i++ ){   avgPi[i] = store[i];   }

    for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = avgLnPi[i];   }
    for( i = 0 ; i < sNo ; i++ ){   avgLnPi[i] = store[i];   }

    for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = avgI[i];   }
    for( i = 0 ; i < sNo ; i++ ){   avgI[i] = store[i];   }

    for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = avgLnI[i];   }
    for( i = 0 ; i < sNo ; i++ ){   avgLnI[i] = store[i];   }

    for( j = 0 ; j < sNo ; j++ ){
        for( i = 0 ; i < sNo ; i++ ){   s2D[index[i]][index[j]] = avgK[i][j];   }
    }
    for( j = 0 ; j < sNo ; j++ ){
        for( i = 0 ; i < sNo ; i++ ){   avgK[i][j] = s2D[i][j];   }
    }

    for( j = 0 ; j < sNo ; j++ ){
        for( i = 0 ; i < sNo ; i++ ){   s2D[index[i]][index[j]] = avgLnK[i][j];   }
    }
    for( j = 0 ; j < sNo ; j++ ){
        for( i = 0 ; i < sNo ; i++ ){   avgLnK[i][j] = s2D[i][j];   }
    }

    for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = avgLnKI[i];   }
    for( i = 0 ; i < sNo ; i++ ){   avgLnKI[i] = store[i];   }

    for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = avgE[i];   }
    for( i = 0 ; i < sNo ; i++ ){   avgE[i] = store[i];   }

    for( i = 0 ; i < sNo ; i++ )
    {   s2D[index[i]][0] = avgLnE[i][0];
        s2D[index[i]][1] = avgLnE[i][1];   }
    for( i = 0 ; i < sNo ; i++ )
    {   avgLnE[i][0] = s2D[i][0];
        avgLnE[i][1] = s2D[i][1];   }

    for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = Ni[i];   }
    for( i = 0 ; i < sNo ; i++ ){   Ni[i] = store[i];   }

    for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = Ti[i];   }
    for( i = 0 ; i < sNo ; i++ ){   Ti[i] = store[i];   }

    for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = eps[i];   }
    for( i = 0 ; i < sNo ; i++ ){   eps[i] = store[i];   }

    for( n = 0 ; n < dLen ; n++ ){
        for( i = 0 ; i < sNo ; i++ ){   store[index[i]] = gmMat[n][i];   }
        for( i = 0 ; i < sNo ; i++ ){   gmMat[n][i] = store[i];   }
    }

    for( n = 0 ; n < dLen ; n++ ){
        for( j = 0 ; j < sNo ; j++ ){
            for( i = 0 ; i < sNo ; i++ ){   s2D[index[i]][index[j]] = xiMat[n][i][j];   }
        }
        for( j = 0 ; j < sNo ; j++ ){
            for( i = 0 ; i < sNo ; i++ ){   xiMat[n][i][j] = s2D[i][j];   }
        }
    }

    for( i = 0 ; i < sNo ; i++ ){   free( s2D[i] );   }
    free( s2D );
    free( store );
    free( index );
}

//void reorderParametersG_tsFret( xns, gv, ivs )
//xnDataBundle *xns;
//globalVars *gv;
//indVarBundle *ivs;
//{
//}

void outputTsFretResults( xn, gv, iv, logFP )
xnDataSet *xn;
globalVars *gv;
indVars *iv;
FILE *logFP;
{
    tsFretParameters *p = (tsFretParameters*)gv->params;
    int sNo = gv->sNo;
    int i, j;

    fprintf(logFP, "  results: K = %d \n", sNo);

    fprintf(logFP, "   intensities: ( %g", p->avgI[0]);
    for( i = 1 ; i < sNo ; i++ )
    {   fprintf(logFP, ", %g", p->avgI[i]);   }
    fprintf(logFP, " ) \n");

    fprintf(logFP, "   FRET efficiencies: ( %g", p->avgE[0]);
    for( i = 1 ; i < sNo ; i++ ){
        fprintf(logFP, ", %g", p->avgE[i]);
    }
    fprintf(logFP, " ) \n");

    fprintf(logFP, "   pi: ( %g", p->avgPi[0]);
    for( i = 1 ; i < sNo ; i++ ){
        fprintf(logFP, ", %g", p->avgPi[i]);
    }
    fprintf(logFP, " ) \n");

    fprintf(logFP, "   k_matrix: [");
    for( i = 0 ; i < sNo ; i++ ){
            fprintf(logFP, " ( %g", p->avgK[i][0]);
            for( j = 1 ; j < sNo ; j++ )
            {   fprintf(logFP, ", %g", p->avgK[i][j]);   }
            fprintf(logFP, ")");
    }
    fprintf(logFP, " ] \n\n");

    char fn[256];
    FILE *fp;
    size_t n;

    sprintf( fn, "%s.param%03d", xn->name, sNo );
    if( (fp = fopen( fn, "w")) != NULL ){
        fprintf(fp, "I, E, pi");
        for( i = 0 ; i < sNo ; i++ )
        {   fprintf(fp, ", K%dx", i);   }
        fprintf(fp, "\n");

        for( i = 0 ; i < sNo ; i++ ){
            fprintf(fp, "%g, %g, %g", p->avgI[i], p->avgE[i], p->avgPi[i]);
            for( j = 0 ; j < sNo ; j++ )
            {   fprintf(fp, ", %g", p->avgK[j][i]);   }
            fprintf(fp, "\n");
        }
        fclose(fp);
    }

    sprintf( fn, "%s.Lq%03d", xn->name, sNo );
    if( (fp = fopen( fn, "w")) != NULL ){
        for( n = 0 ; n < gv->iteration ; n++ ){
            fprintf( fp, "%24.20e\n", gv->LqArr[n] );
        }
        fclose(fp);
    }

    sprintf( fn, "%s.maxS%03d", xn->name, sNo );
    if( (fp = fopen( fn, "w")) != NULL ){
        for( n = 0 ; n < xn->N ; n++ ){
            fprintf( fp, "%d\n", iv->stateTraj[n] );
        }
        fclose(fp);
    }
}

//void outputTsFretResultsG( xns, gv, ivs, logFP )
//xnDataBundle *xns;
//globalVars *gv;
//indVarBundle *ivs;
//FILE *logFP;
//{
//}

//


#include <time.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <gsl/gsl_cdf.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_vector.h>
#include "../include/type.h"
#include "../include/util.h"
#include "../include/myfunc.h"
#include "../include/global.h"

double rho = 0.9;
double cutoff = 0.0001;
clock_t dur = 0;
// char *optarg;
// int optind = 0;

int main(int argc, char *argv[]) {
    char *inputf = "input.txt", *outputf = (char*)malloc(sizeof(char)*MAX_CHAR);
    char str_line[MAX_LINE], *title_element_list[100];
    int nthread = 1, batch_size = 1, opt, row_num = 0, i = 0;

    while ((opt = getopt(argc, argv, "t:bc:o:i:")) != -1) {
        switch (opt) {
        case 't':
            nthread = atoi(optarg);
            break;
        case 'b':
            batch_size = 0;
            break;
        case 'c':
            cutoff = atof(optarg);
            break;
        case 'o':
            // strcpy(outputf, optarg);
            // strcat(outputf, "/rMATS_Result_P.txt");
            outputf = optarg;
            break;
        case 'i':
            inputf = optarg;
            break;
        default: /* '?' */
            fprintf(stderr, "Usage: %s [-t nthread] [-c] cutoff [-o] output folder [-i] input file\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    printf("number of thread=%d; input file=%s; output folder=%s; cutoff=%g;\n",\
           nthread, inputf, outputf, cutoff);

    if (optind > argc) {
        fprintf(stderr, "Expected argument after options\n");
        exit(EXIT_FAILURE);
    }

    if (nthread > MAXTHREAD) {
        printf("nthread (%d) should be less than MAXTHREAD (%d)\n", nthread, MAXTHREAD);
        exit(0);
    }

    diff_list_node *list = (diff_list_node*)malloc(sizeof(diff_list_node));
    list->data = NULL, list->next = NULL, list->end = list;
    row_num = parse_file(inputf, list, title_element_list);

    clock_t start, diff;
    start = clock();

    odiff *datum[row_num];
    diff_list_node *node = list;
    double *prob[row_num];
    for (i = 0; i < row_num; ++i) {
        node = node->next;
        datum[i] = node->data;
    }
    mp_threadpool(nthread, row_num, thread_wrapper_for_LT, (void**)datum, (void**)prob);

    diff = clock() - start;
    int msec = diff * 1000 / CLOCKS_PER_SEC;
    printf("Total Wallclock time taken %d seconds %d milliseconds\n", msec/1000, msec%1000);
    printf("Wallclock time per thread taken %d seconds %d milliseconds\n", msec/1000/nthread, (msec%1000)/nthread);

    FILE *ifp = NULL, *ofp = NULL;
    if ((ifp = fopen(inputf, "r")) == NULL) {
        printf("Fail to open %s!", inputf);
        return -1;
    }
    if ((ofp = fopen(outputf, "w")) == NULL) {
        printf("Fail to open %s!", outputf);
        return -1;
    }

    // original comment: analyze the title of the inputed data file to find the
    //                   information of how much simulation are involved
    fgets(str_line, MAX_LINE, ifp);
    str_line[strlen(str_line)-1] = 0;
    fprintf(ofp, "%s\tPValue\n", str_line);
    for (i = 0; i < row_num; ++i) {
        fgets(str_line, MAX_LINE, ifp);
        str_line[strlen(str_line)-1] = 0;
        fprintf(ofp, "%s\t%.12g\n", str_line, *prob[i]);
    }
    fclose(ifp);
    fclose(ofp);

    msec = dur * 1000 / CLOCKS_PER_SEC;
    printf("Time for func(single thread): %d seconds %d milliseconds\n", msec/1000, msec%1000);

    return 0;
}


/* Copyright (c) 2014, Giuseppe Argentieri <giuseppe.argentieri@ts.infn.it>

 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
/*
 *
 *
 *       Filename:  write.c
 *
 *    Description:  Write the data into file .dat
 *
 *        Version:  1.0
 *        Created:  07/05/2014 22:46:50
 *       Revision:  none
 *        License:  BSD
 *
 *         Author:  Giuseppe Argentieri (ga), giuseppe.argentieri@ts.infn.it
 *   Organization:  Università degli Studi di Trieste
 *
 *
 */

#include "funcs.h"
#include <stdio.h>
#include <gsl/gsl_matrix.h>

/*
 *      FUNCTION
 *         Name:  save_integrals
 *  Description:
 *
 */
int save_integrals ( void* params )
{
	/* perform the integrals */
  	double integrals[12] ;
	void* p = (void*) params ;
	int status = integration ( p, integrals ) ;

	/* write them into a file */
	FILE* f_integ = fopen ( "INTEGRALS.dat", "w+" ) ;
	int i ;
	for ( i = 0 ; i < 12 ; i++ )
		fprintf( f_integ, "integrals[%d]: %.9f\n", i, integrals[i] ) ;
	fclose( f_integ ) ;

	return status;
}		/* -----  end of function save_integrals  ----- */

/*
 *      FUNCTION
 *         Name:  save_matrices
 *  Description:
 *
 */
int save_matrices ( void* params )
{
	void* p = (void *) params ;

	/* create the Redfield matrix and save it into a file */
	gsl_matrix* red_matrix = gsl_matrix_calloc ( 4, 4 ) ;
	int status1 = red_mat ( red_matrix, p ) ;
	int status2 = mat_write ( red_matrix, "REDFIELD_MATRIX" ) ;

	/* create the CP matrix and save it into a file */
	gsl_matrix* cp_matrix = gsl_matrix_calloc ( 4, 4 ) ;
	int status3 = cp_mat ( cp_matrix, p ) ;
	int status4 = mat_write ( cp_matrix, "CP_MATRIX" ) ;

	gsl_matrix_free (red_matrix) ;
	gsl_matrix_free (cp_matrix) ;

	return status1+status2+status3+status4 ;
}		/* -----  end of function save_matrices  ----- */


/*
    KGSX: Biomolecular Kino-geometric Sampling and Fitting of Experimental Data
    Yao et al, Proteins. 2012 Jan;80(1):25-43
    e-mail: latombe@cs.stanford.edu, vdbedem@slac.stanford.edu, julie.bernauer@inria.fr

        Copyright (C) 2011-2013 Stanford University

        Permission is hereby granted, free of charge, to any person obtaining a copy of
        this software and associated documentation files (the "Software"), to deal in
        the Software without restriction, including without limitation the rights to
        use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
        of the Software, and to permit persons to whom the Software is furnished to do
        so, subject to the following conditions:

        This entire text, including the above copyright notice and this permission notice
        shall be included in all copies or substantial portions of the Software.

        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
        OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
        FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
        IN THE SOFTWARE.
*/

#ifndef KGS_ATOMPAIRDISTANCEDIRECTION_H
#define KGS_ATOMPAIRDISTANCEDIRECTION_H

#include <gsl/gsl_vector.h>

#include <tuple>
#include <vector>

#include "core/graph/KinTree.h"
#include "directions/Direction.h"
#include "Selection.h"

/**
 * A direction where the gradient seeks to drive configurations toward a desired distance between a particular pair of
 * atoms. This class is conceptually similar to `RelativeMSDDirection` and to a lesser degree `LSNrelativeDirection`,
 * but only takes a single pair of atoms and desired distance.
 */
class AtomPairDistanceDirection: public Direction {
 public:
  AtomPairDistanceDirection(const std::tuple<Atom*, Atom*, double> &relativeDistances);

 protected:
  void computeGradient(Configuration* conf, Configuration* target, gsl_vector* ret);

 private:
  const std::tuple<Atom*, Atom*, double> &m_relativeDistance;
};

#endif //KGS_ATOMPAIRDISTANCEDIRECTION_H


#include <math.h>
#include <stdlib.h>
#if !defined(__APPLE__)
#include <malloc.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <string.h>
#include <stdbool.h>

#include <fftw3.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_erf.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_sf_legendre.h>
#include <gsl/gsl_sf_bessel.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_sf_expint.h>
#include <gsl/gsl_deriv.h>
#include <gsl/gsl_interp2d.h>
#include <gsl/gsl_spline2d.h>

#include "../cosmolike_core/theory/basics.c"
#include "../cosmolike_core/theory/structs.c"
#include "../cosmolike_core/theory/parameters.c"
#include "../cosmolike_core/emu17/P_cb/emu.c"
#include "../cosmolike_core/theory/recompute.c"
#include "../cosmolike_core/theory/cosmo3D.c"
#include "../cosmolike_core/theory/redshift_spline.c"
#include "../cosmolike_core/theory/halo.c"
#include "../cosmolike_core/theory/HOD.c"
#include "../cosmolike_core/theory/pt.c"
#include "../cosmolike_core/theory/cosmo2D_fourier.c"
#include "../cosmolike_core/theory/IA.c"
#include "../cosmolike_core/theory/BAO.c"
#include "../cosmolike_core/theory/external_prior.c"
#include "../cosmolike_core/theory/covariances_3D.c"
#include "../cosmolike_core/theory/covariances_fourier.c"
#include "../cosmolike_core/theory/CMBxLSS_fourier.c"
#include "../cosmolike_core/theory/covariances_CMBxLSS_fourier.c"

#include "init_LSSxCMB.c"

// Naming convention:
// l = galaxy positions ("l" as in "lens sample")
// k = kappa CMB ("k" as in "kappa")
// s = kappa from source galaxies ("s" as in "source sample")
// And alphabetical order

void run_cov_ls_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start);
void run_cov_ll_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start);
void run_cov_ll_ls(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start);
void run_cov_ll_ll(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start);
void run_cov_ls_ls(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start);
void run_cov_ss_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start);

void run_cov_ls_kk(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int start);
void run_cov_ls_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int zs2, int start);
void run_cov_lk_lk(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int zl2, int start);
void run_cov_lk_ls(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int n2, int start);
void run_cov_lk_kk(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int start);
void run_cov_lk_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int zs2, int start);
void run_cov_lk_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int n2,int start);
void run_cov_kk_kk(char *OUTFILE, char *PATH, double *ell, double *dell,int start);
void run_cov_kk_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int zs2, int start);
void run_cov_kk_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int n2, int start);
void run_cov_ks_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int zs1, int zs2,int start);
void run_cov_ks_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int zs1, int n2, int start);
void run_cov_ll_kk(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int start);
void run_cov_ll_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int zs2, int start);
void run_cov_ll_lk(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int zl2,int start);

void run_cov_ls_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start)
{
  int zl,zs,z3,z4,nl1,nl2,weight,i,j;
  double c_ng, c_g;
  FILE *F1;
  char filename[300];
  sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
  F1 =fopen(filename,"w");
  zl = ZL(n1); zs = ZS(n1);
  printf("\nN_ggl = %d (%d, %d)\n", n1,zl,zs);
  z3 = Z1(n2); z4 = Z2(n2);
  printf("N_shear = %d (%d, %d)\n", n2,z3,z4);
  for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
    for (nl2 = 0; nl2 <like.Ncl; nl2 ++){
      c_ng = 0.; c_g = 0.;
      weight = test_kmax(ell[nl1],zl);
      if (weight && ell[nl2] < like.lmax_shear){
        if (test_zoverlap(zl,z3)*test_zoverlap(zl,z4)){
          c_ng = cov_NG_gl_shear_tomo(ell[nl1],ell[nl2],zl,zs,z3,z4);
        }
        if (nl1 == nl2){
          c_g =  cov_G_gl_shear_tomo(ell[nl1],dell[nl1],zl,zs,z3,z4);
        }
      }
      i=like.Ncl*(tomo.shear_Npowerspectra+n1)+nl1;
      j=like.Ncl*n2+nl2;
      fprintf(F1, "%d %d %e %e %d %d %d %d  %e %e\n",i,j,ell[nl1],ell[nl2],zl,zs,z3,z4,c_g,c_ng);
    }
  }
  fclose(F1);
}

void run_cov_ll_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start)
{
  int z1,z2,z3,z4,nl1,nl2,i,j,weight;
  double c_ng, c_g;
  FILE *F1;
  char filename[300];
  sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
  F1 =fopen(filename,"w");
  z1 = n1; z2 = n1;
  printf("\nN_cl = %d \n", n1);
  z3 = Z1(n2); z4 = Z2(n2);
  printf("N_shear = %d (%d, %d)\n", n2,z3,z4);
  for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
    for (nl2 = 0; nl2 <like.Ncl; nl2 ++){
      c_ng = 0.; c_g = 0.;
      weight = test_kmax(ell[nl1],z1);
      if (weight && ell[nl2] < like.lmax_shear){
        if (test_zoverlap(z1,z3)*test_zoverlap(z1,z4)){
          c_ng = cov_NG_cl_shear_tomo(ell[nl1],ell[nl2],z1,z2,z3,z4);
        }
        if (nl1 == nl2){
          c_g =  cov_G_cl_shear_tomo(ell[nl1],dell[nl1],z1,z2,z3,z4);
        }
      }
      i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+n1)+nl1;
      j=like.Ncl*n2+nl2;
      fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],z1,z2,z3,z4,c_g,c_ng);
    }
  }
  fclose(F1);
}

void run_cov_ll_ls(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start)
{
  int z1,z2,zl,zs,nl1,nl2,i,j,weight;
  double c_ng, c_g;
  FILE *F1;
  char filename[300];
  sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
  F1 =fopen(filename,"w");
  z1 = n1; z2 = n1;
  printf("\nN_cl_1 = %d \n", n1);
  zl = ZL(n2); zs = ZS(n2);
  printf("N_tomo_2 = %d (%d, %d)\n", n2,zl,zs);
  for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
    for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
      c_ng = 0.; c_g = 0.;
      weight = test_kmax(ell[nl1],z1)*test_kmax(ell[nl2],zl);
      if (weight){
        if((z1 == zl)*test_zoverlap(z1,zs)){c_ng = cov_NG_cl_gl_tomo(ell[nl1],ell[nl2],z1,z2,zl,zs);}
        if (nl1 == nl2){
          c_g =  cov_G_cl_gl_tomo(ell[nl1],dell[nl1],z1,z2,zl,zs);
        }
      }
      i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+n1)+nl1;
      j=like.Ncl*(tomo.shear_Npowerspectra+n2)+nl2;
      fprintf(F1, "%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],z1,z2,zl,zs,c_g,c_ng);
    }
  }
  fclose(F1);
}

void run_cov_ll_ll(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start)
{
  int z1,z2,z3,z4,nl1,nl2,i,j,weight;
  double c_ng, c_g;
  FILE *F1;
  char filename[300];
  sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
  F1 =fopen(filename,"w");
  z1 = n1; z2 = n1;
  printf("\nN_cl_1 = %d \n", n1);
  z3 = n2; z4 = n2;
  printf("N_cl_2 = %d\n", n2);
  for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
    for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
      c_ng = 0.; c_g = 0.;
      weight = test_kmax(ell[nl1],z1)*test_kmax(ell[nl2],z3);
      if ((z1 == z3) && weight) {
        c_ng = cov_NG_cl_cl_tomo(ell[nl1],ell[nl2],z1,z2,z3,z4);
      }
      if (nl1 == nl2){
        c_g =  cov_G_cl_cl_tomo(ell[nl1],dell[nl1],z1,z2,z3,z4);
      }
      i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+n1)+nl1;
      j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+n2)+nl2;
      fprintf(F1, "%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],z1,z2,z3,z4,c_g,c_ng);
    }
  }
  fclose(F1);
}

void run_cov_ls_ls(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int n2,int start)
{
  int zl1,zl2,zs1,zs2,nl1,nl2,i,j,weight;
  double c_ng, c_g;
  double fsky = survey.area/41253.0;
  FILE *F1;
  char filename[300];
  sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
  F1 =fopen(filename,"w");

  zl1 = ZL(n1); zs1 = ZS(n1);
  printf("\nN_tomo_1 = %d (%d, %d)\n", n1,zl1,zs1);
  zl2 = ZL(n2); zs2 = ZS(n2);
  printf("N_tomo_2 = %d (%d, %d)\n", n2,zl2,zs2);
  for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
    for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
      c_ng = 0.; c_g = 0.;
      weight = test_kmax(ell[nl1],zl1)*test_kmax(ell[nl2],zl2);
      if (weight && zl1 == zl2) {
        c_ng = cov_NG_gl_gl_tomo(ell[nl1],ell[nl2],zl1,zs1,zl2,zs2);
      }
      if (nl1 == nl2){
        c_g =  cov_G_gl_gl_tomo(ell[nl1],dell[nl1],zl1,zs1,zl2,zs2);
      }
      if (weight ==0 && n2 != n1){
        c_g = 0;
      }
      i=like.Ncl*(tomo.shear_Npowerspectra+n1)+nl1;
      j=like.Ncl*(tomo.shear_Npowerspectra+n2)+nl2;
      fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],zl1,zs1,zl2,zs2,c_g,c_ng);
    }
  }
  fclose(F1);
}

void run_cov_ss_ss(char *OUTFILE, char *PATH, double *ell, double *dell,int n1, int n2,int start)
{
  int z1,z2,z3,z4,nl1,nl2,i,j,weight;
  double c_ng, c_g;
  FILE *F1;
  char filename[300];
  z1 = Z1(n1); z2 = Z2(n1);
  printf("N_shear = %d\n", n1);
  z3 = Z1(n2); z4 = Z2(n2);
  printf("N_shear = %d (%d, %d)\n",n2,z3,z4);
  sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
  F1 =fopen(filename,"w");
  for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
    for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
      c_ng = 0.; c_g = 0.;
      if (ell[nl1] < like.lmax_shear && ell[nl2] < like.lmax_shear){
        c_ng = cov_NG_shear_shear_tomo(ell[nl1],ell[nl2],z1,z2,z3,z4);
      }
      if (nl1 == nl2){
        c_g =  cov_G_shear_shear_tomo(ell[nl1],dell[nl1],z1,z2,z3,z4);
        if (ell[nl1] > like.lmax_shear && n1!=n2){c_g = 0.;}
      }
      i=like.Ncl*n1+nl1;
      j=like.Ncl*n2+nl2;
      fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],z1,z2,z3,z4,c_g,c_ng);
      //printf("%d %d %e %e %d %d %d %d %e %e\n", like.Ncl*n1+nl1,like.Ncl*(n2)+nl2, ell[nl1],ell[nl2],z1,z2,z3,z4,c_g,c_ng);
    }
  }
  fclose(F1);
}

/*** CMBkappa part ****/
// ls_kk
void run_cov_ls_kk(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int start)
{
   int zl1, zs1,i,j, weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   zl1 = ZL(n1); zs1 = ZS(n1);
   printf("Bin for ls: %d (%d, %d)\n", n1, zl1, zs1);
   for (int nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (int nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         weight = test_kmax(ell[nl1], zl1);
         if (weight && ell[nl2]<like.lmax_kappacmb) {
            c_ng = cov_NG_gs_kk(ell[nl1],ell[nl2], zl1, zs1);
            if (nl1==nl2){
               c_g = cov_G_gs_kk(ell[nl1], dell[nl1], zl1, zs1);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+n1)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+tomo.shear_Nbin)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],zl1,zs1,0,0,c_g,c_ng);
      }
   }
   fclose(F1);
}

// ls_ks
void run_cov_ls_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int zs2, int start)
{
   int zl1, zs1,i,j,weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   zl1 = ZL(n1); zs1 = ZS(n1);
   printf("Bin for ls: %d (%d, %d)\n", n1, zl1, zs1);
   printf("Bin for ks: %d\n", zs2);
   for (int nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (int nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         weight = test_kmax(ell[nl1], zl1);
         if (weight && ell[nl2]<like.lmax_kappacmb) {
            if (test_zoverlap(zl1, zs2)) {
               c_ng = cov_NG_gs_ks(ell[nl1],ell[nl2], zl1, zs1, zs2);
            }
            if (nl1==nl2){
               c_g = cov_G_gs_ks(ell[nl1], dell[nl1], zl1, zs1, zs2);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+n1)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+zs2)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],zl1,zs1,0,zs2,c_g,c_ng);
      }
   }
   fclose(F1);
}
// lk_lk
void run_cov_lk_lk(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int zl2, int start)
{
   int weight,i,j;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   printf("Lens bins: %d, %d\n", zl1, zl2);

   for (int nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (int nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         weight = test_kmax(ell[nl1],zl1)*test_kmax(ell[nl2],zl2);
         bool compute = weight * (ell[nl1]<like.lmax_kappacmb) * (ell[nl2]<like.lmax_kappacmb);
         if (compute && (zl1==zl2)) {
            c_ng = cov_NG_gk_gk(ell[nl1],ell[nl2], zl1, zl2);
         }
         if (nl1==nl2){
            c_g = cov_G_gk_gk(ell[nl1], dell[nl1], zl1, zl2);
         }
         if (!compute && (zl2!=zl1)) {
            c_g = 0;
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+zl1)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+zl2)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],zl1,0,zl2,0,c_g,c_ng);
      }
   }
   fclose(F1);
}

// lk_ls
void run_cov_lk_ls(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int n2, int start)
{
   int zl2,zs2,nl1,nl2,i,j, weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   // sprintf(filename,"%scov/%s_%s_cov_gkgs_Nell%d_Ns%d_Ng%d_%d", PATH, survey.name, cmb.name, like.Ncl, tomo.shear_Nbin, tomo.clustering_Nbin, start);
   // printf("Saving to: %s\n",filename);
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   printf("Lens bin for lk: %d\n", zl1);
   zl2 = ZL(n2); zs2 = ZS(n2);
   printf("N_tomo for ls = %d (%d, %d)\n", n2,zl2,zs2);

   for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         weight = test_kmax(ell[nl1],zl1)*test_kmax(ell[nl2],zl2);
         if (weight && ell[nl1]<like.lmax_kappacmb && ell[nl2]<like.lmax_kappacmb) {
            if (zl1==zl2) {
               c_ng = cov_NG_gk_gs(ell[nl1],ell[nl2],zl1,zl2,zs2);
            }
            if (nl1==nl2){
               c_g = cov_G_gk_gs(ell[nl1], dell[nl1], zl1,zl2,zs2);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+zl1)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+n2)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],zl1,0,zl2,zs2,c_g,c_ng);
      }
   }
   fclose(F1);
}

// lk_kk
void run_cov_lk_kk(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int start)
{
   int weight,i,j;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   // sprintf(filename,"%scov/%s_%s_cov_gkkk_Nell%d_Ns%d_Ng%d_%d", PATH, survey.name, cmb.name, like.Ncl, tomo.shear_Nbin, tomo.clustering_Nbin, start);
   // printf("Saving to: %s\n",filename);
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   printf("Bin for lk: %d\n", zl1);

   for (int nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (int nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         weight = test_kmax(ell[nl1],zl1);
         if (weight && ell[nl1]<like.lmax_kappacmb && ell[nl2]<like.lmax_kappacmb) {
            c_ng = cov_NG_gk_kk(ell[nl1],ell[nl2],zl1);
            if (nl1==nl2){
               c_g = cov_G_gk_kk(ell[nl1],dell[nl1],zl1);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+zl1)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+tomo.shear_Nbin)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],zl1,0,0,0,c_g,c_ng);
      }
   }
   fclose(F1);
}

// lk_ks
void run_cov_lk_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int zs2, int start)
{
   int weight,i,j;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   // sprintf(filename,"%scov/%s_%s_cov_gkks_Nell%d_Ns%d_Ng%d_%d", PATH, survey.name, cmb.name, like.Ncl, tomo.shear_Nbin, tomo.clustering_Nbin, start);
   // printf("Saving to: %s\n",filename);
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   printf("Bin for lk: %d\n", zl1);
   printf("Bin for ks: %d\n", zs2);

   for (int nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (int nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         weight = test_kmax(ell[nl1], zl1);
         if (weight && ell[nl1]<like.lmax_kappacmb && ell[nl2]<like.lmax_kappacmb) {
            if (test_zoverlap(zl1, zs2)) {
               c_ng = cov_NG_gk_ks(ell[nl1],ell[nl2], zl1, zs2);
            }
            if (nl1==nl2){
               c_g =  cov_G_gk_ks(ell[nl1], dell[nl1], zl1, zs2);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+zl1)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+zs2)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],zl1,0,0,zs2,c_g,c_ng);
      }
   }
   fclose(F1);
}

// lk_ss
void run_cov_lk_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int zl1, int n2,int start)
{
   int zs2,zs3,nl1,nl2,weight,i,j;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   // sprintf(filename,"%scov/%s_%s_cov_gkss_Nell%d_Ns%d_Ng%d_%d",PATH,survey.name, cmb.name, like.Ncl, tomo.shear_Nbin, tomo.clustering_Nbin, start);
   // printf("Saving to: %s\n",filename);
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   printf("Bin for lk: %d\n", zl1);
   zs2 = Z1(n2); zs3 = Z2(n2);
   printf("Bin for ss: %d (%d, %d)\n", n2, zs2, zs3);
   for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (nl2 = 0; nl2 <like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         weight = test_kmax(ell[nl1],zl1);
         if (weight && ell[nl1]<like.lmax_kappacmb && ell[nl2]<like.lmax_shear){
            if (test_zoverlap(zl1, zs2)*test_zoverlap(zl1, zs3)) {
               c_ng = cov_NG_gk_ss(ell[nl1],ell[nl2],zl1,zs2,zs3);
            }
            if (nl1==nl2){
               c_g = cov_G_gk_ss(ell[nl1],dell[nl1],zl1,zs2, zs3);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+zl1)+nl1;
         j=like.Ncl*n2+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],zl1,0,zs2,zs3,c_g,c_ng);
      }
   }
   fclose(F1);
}

// kk_kk
void run_cov_kk_kk(char *OUTFILE, char *PATH, double *ell, double *dell,int start)
{
   int nl1,nl2,i,j,weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   // sprintf(filename,"%scov/%s_%s_cov_kkkk_Nell%d_Ns%d_Ng%d_%d",PATH,survey.name, cmb.name,like.Ncl, tomo.shear_Nbin, tomo.clustering_Nbin, start);
   // printf("Saving to: %s\n",filename);
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         if (ell[nl1]<like.lmax_kappacmb && ell[nl2]<like.lmax_kappacmb){
            c_ng = cov_NG_kk_kk(ell[nl1],ell[nl2]);
         }
         if (nl1==nl2){
            c_g = cov_G_kk_kk(ell[nl1], dell[nl1]);
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+tomo.shear_Nbin)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+tomo.shear_Nbin)+nl2;
         fprintf(F1, "%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],0,0,0,0,c_g,c_ng);
//         printf("l1, l2, Cg, Cng = %le, %le, %le, %le\n", ell[nl1], ell[nl2], c_g, c_ng);
      }
   }
   fclose(F1);
}

// kk_ks
void run_cov_kk_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int zs2, int start)
{
   int nl1,nl2,i,j, weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   // sprintf(filename,"%scov/%s_%s_cov_kkks_Nell%d_Ns%d_Ng%d_%d", PATH, survey.name, cmb.name, like.Ncl, tomo.shear_Nbin, tomo.clustering_Nbin, start);
   // printf("Saving to: %s\n",filename);
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   printf("Source bin for ks: %d\n", zs2);
   for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         if (ell[nl1]<like.lmax_kappacmb && ell[nl2]<like.lmax_kappacmb) {
            c_ng = cov_NG_kk_ks(ell[nl1],ell[nl2],zs2);
            if (nl1==nl2){
               c_g = cov_G_kk_ks(ell[nl1],dell[nl1], zs2);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+tomo.shear_Nbin)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+zs2)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],0,0,0,zs2,c_g,c_ng);
      }
   }
   fclose(F1);
}

// kk_ss
void run_cov_kk_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int n2, int start)
{
   int z2,z3,nl1,nl2,i,j,weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   z2 = Z1(n2); z3 = Z2(n2);
   printf("Bin for ss: %d (%d, %d)\n", n2, z2, z3);
   // sprintf(filename,"%scov/%s_%s_cov_kkss_Nell%d_Ns%d_Ng%d_%d",PATH,survey.name, cmb.name,like.Ncl, tomo.shear_Nbin, tomo.clustering_Nbin, start);
   // printf("Saving to: %s\n",filename);
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         if (ell[nl1]<like.lmax_kappacmb && ell[nl2]<like.lmax_shear){
            c_ng = cov_NG_kk_ss(ell[nl1],ell[nl2],z2,z3);
            if (nl1==nl2){
               c_g = cov_G_kk_ss(ell[nl1],dell[nl1],z2,z3);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+tomo.shear_Nbin)+nl1;
         j=like.Ncl*n2+nl2;
         fprintf(F1, "%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],0,0,z2,z3,c_g,c_ng);
      }
   }
   fclose(F1);
}

// ks_ks
void run_cov_ks_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int zs1, int zs2,int start)
{
   int nl1,nl2,i,j, weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   // sprintf(filename,"%scov/%s_%s_cov_ksks_Nell%d_Ns%d_Ng%d_%d", PATH, survey.name, cmb.name, like.Ncl, tomo.shear_Nbin, tomo.clustering_Nbin, start);
   // printf("Saving to: %s\n",filename);
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   printf("Source bins for ks: %d, %d\n", zs1, zs2);
   for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         if (ell[nl1]<like.lmax_kappacmb && ell[nl2]<like.lmax_kappacmb) {
            c_ng = cov_NG_ks_ks(ell[nl1],ell[nl2],zs1,zs2);
         }
         if (nl1==nl2){
            c_g = cov_G_ks_ks(ell[nl1],dell[nl1], zs1, zs2);
         }
         if ((ell[nl1]>like.lmax_kappacmb || ell[nl2]>like.lmax_kappacmb) && zs2!=zs1){
            c_g = 0;
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+zs1)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+zs2)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],0,zs1,0,zs2,c_g,c_ng);
      }
   }
   fclose(F1);
}

// ks_ss
void run_cov_ks_ss(char *OUTFILE, char *PATH, double *ell, double *dell, int zs1, int n2, int start)
{
   int z2,z3,nl1,nl2,i,j,weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   z2 = Z1(n2); z3 = Z2(n2);
   printf("Source bin for ks: %d\n", zs1);
   printf("Bin for ss: %d (%d, %d)\n", n2, z2, z3);

   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         if (ell[nl1]<like.lmax_kappacmb && ell[nl2]<like.lmax_shear){
            c_ng = cov_NG_ks_ss(ell[nl1],ell[nl2],zs1,z2,z3);
            if (nl1==nl2){
               c_g = cov_G_ks_ss(ell[nl1],dell[nl1],zs1,z2,z3);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+zs1)+nl1;
         j=like.Ncl*n2+nl2;
         fprintf(F1, "%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],0,zs1,z2,z3,c_g,c_ng);
      }
   }
   fclose(F1);
}

// ll_kk
void run_cov_ll_kk(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int start)
{
   int i,j, weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   // zl1 = ZL(n1); zs1 = ZS(n1);
   printf("Bin for ll: %d \n", n1);
   for (int nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (int nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         weight = test_kmax(ell[nl1], n1);
         if (weight && ell[nl2]<like.lmax_kappacmb) {
            c_ng = cov_NG_gg_kk(ell[nl1],ell[nl2], n1, n1);
            if (nl1==nl2){
               c_g = cov_G_gg_kk(ell[nl1], dell[nl1], n1, n1);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+n1)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+tomo.shear_Nbin)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],n1,n1,0,0,c_g,c_ng);
      }
   }
   fclose(F1);
}

// ll_ks
void run_cov_ll_ks(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int zs2, int start)
{
   int i,j,weight;
   double c_ng, c_g;
   FILE *F1;
   char filename[300];
   sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
   F1 =fopen(filename,"w");
   // zl1 = ZL(n1); zs1 = ZS(n1);
   printf("Bin for ll: %d\n", n1);
   printf("Bin for ks: %d\n", zs2);
   for (int nl1 = 0; nl1 < like.Ncl; nl1 ++){
      for (int nl2 = 0; nl2 < like.Ncl; nl2 ++){
         c_ng = 0.; c_g = 0.;
         weight = test_kmax(ell[nl1], n1);
         if (weight && ell[nl2]<like.lmax_kappacmb) {
            if (test_zoverlap(n1, zs2)) {
               c_ng = cov_NG_gg_ks(ell[nl1],ell[nl2], n1, n1, zs2);
            }
            if (nl1==nl2){
               c_g = cov_G_gg_ks(ell[nl1], dell[nl1], n1, n1, zs2);
            }
         }
         i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+n1)+nl1;
         j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+tomo.clustering_Nbin+zs2)+nl2;
         fprintf(F1,"%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],n1,n1,0,zs2,c_g,c_ng);
      }
   }
   fclose(F1);
}

// ll_lk
void run_cov_ll_lk(char *OUTFILE, char *PATH, double *ell, double *dell, int n1, int zl2,int start)
{
  int z1,z2,zl,zs,nl1,nl2,i,j,weight;
  double c_ng, c_g;
  FILE *F1;
  char filename[300];
  sprintf(filename,"%s%s_%d",PATH,OUTFILE,start);
  F1 =fopen(filename,"w");
  z1 = n1; z2 = n1;
  printf("\nN_cl_1 = %d \n", n1);
  // zl = ZL(n2); zs = ZS(n2);
  zl = zl2;
  printf("N_tomo_2 = %d\n", zl2);
  for (nl1 = 0; nl1 < like.Ncl; nl1 ++){
    for (nl2 = 0; nl2 < like.Ncl; nl2 ++){
      c_ng = 0.; c_g = 0.;
      weight = test_kmax(ell[nl1],z1)*test_kmax(ell[nl2],zl);
      if (weight){
        if((z1 == zl)) {c_ng = cov_NG_gg_gk(ell[nl1],ell[nl2],z1,z2,zl);}
        if (nl1 == nl2){
          c_g =  cov_G_gg_gk(ell[nl1],dell[nl1],z1,z2,zl);
        }
      }
      i=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+n1)+nl1;
      j=like.Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra+tomo.clustering_Nbin+zl)+nl2;
      fprintf(F1, "%d %d %e %e %d %d %d %d %e %e\n",i,j,ell[nl1],ell[nl2],z1,z2,zl,0,c_g,c_ng);
    }
  }
  fclose(F1);
}

int main(int argc, char** argv)
{

  int i,l,m,n,o,s,p,nl1,t,k;
  char OUTFILE[400],filename[400],arg1[400],arg2[400];

  int N_scenarios=2;
  double area_table[2]={18000.0,2000.0};
  double nsource_table[2]={43.0,51.0};
  double nlens_table[2]={50.0,66.0};

  char survey_designation[2][200]={"WFIRSTwidexSO5","WFIRSTstdxSO5"};

  char source_zfile[2][400]={"zdistri_WFIRST_LSST_lensing_fine_bin","zdistri_WFIRST_LSST_lensing_fine_bin"};

#ifdef ONESAMPLE
  char lens_zfile[2][400]={"zdistri_WFIRST_LSST_lensing_fine_bin","zdistri_WFIRST_LSST_lensing_fine_bin"};
  nlens_table[0] = nsource_table[0];
  nlens_table[1] = nsource_table[1];
#else
  char lens_zfile[2][400]={"zdistri_WFIRST_LSST_clustering_fine_bin","zdistri_WFIRST_LSST_clustering_fine_bin"};
#endif

  int hit=atoi(argv[1]);
  Ntable.N_a=100;
  k=1;

  t = atoi(argv[2]);

  //RUN MODE setup
  init_cosmo_runmode("emu");
  // init_binning_fourier(20,30.0,3000.0,3000.0,21.0,10,10);
  init_binning_fourier(15,20.0,3000.0,3000.0,21.0,10,10);
  init_survey(survey_designation[t],nsource_table[t],nlens_table[t],area_table[t]);
  sprintf(arg1,"zdistris/%s",source_zfile[t]);
  sprintf(arg2,"zdistris/%s",lens_zfile[t]);
  init_galaxies(arg1,arg2,"none","none","source_std","WF_SN10");
  init_IA("none","GAMA");
  init_probes("6x2pt");

  init_cmb("so_Y5");

  //set l-bins for shear, ggl, clustering, clusterWL
  double logdl=(log(like.lmax)-log(like.lmin))/like.Ncl;
  double *ell, *dell, *ell_Cluster, *dell_Cluster;
  ell=create_double_vector(0,like.Ncl-1);
  dell=create_double_vector(0,like.Ncl-1);
  ell_Cluster=create_double_vector(0,Cluster.lbin-1);
  dell_Cluster=create_double_vector(0,Cluster.lbin-1);
  int j=0;
  for(i=0;i<like.Ncl;i++){
    ell[i]=exp(log(like.lmin)+(i+0.5)*logdl);
    dell[i]=exp(log(like.lmin)+(i+1)*logdl)-exp(log(like.lmin)+(i*logdl));
    if(ell[i]<like.lmax_shear) printf("%le\n",ell[i]);
  }

  printf("----------------------------------\n");
  sprintf(survey.name,"%s_area%le_ng%le_nl%le",survey_designation[t],survey.area,survey.n_gal,survey.n_lens);
  printf("area: %le n_source: %le n_lens: %le\n",survey.area,survey.n_gal,survey.n_lens);

  // sprintf(covparams.outdir,"/home/u17/timeifler/covparallel/");
#ifdef ONESAMPLE
  sprintf(covparams.outdir,"out_cov_wfirstxso_1sample/");
#else
  sprintf(covparams.outdir,"out_cov_wfirstxso/");
  //sprintf(covparams.outdir,"/halo_nobackup/cosmos/teifler/covparallel/");
#endif

  printf("----------------------------------\n");
  sprintf(OUTFILE,"%s_ssss_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.shear_Npowerspectra; l++){
    for (m=l;m<tomo.shear_Npowerspectra; m++){
      if(k==hit){
        sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
        run_cov_ss_ss(OUTFILE,covparams.outdir,ell,dell,l,m,k);
      }
      k=k+1;
      //printf("%d\n",k);
    }
  }

  sprintf(OUTFILE,"%s_lsls_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.ggl_Npowerspectra; l++){
    for (m=l;m<tomo.ggl_Npowerspectra; m++){
      if(k==hit){
        sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
        run_cov_ls_ls(OUTFILE,covparams.outdir,ell,dell,l,m,k);
      }
     //printf("%d\n",k);
      k=k+1;
    }
  }
  sprintf(OUTFILE,"%s_llll_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Npowerspectra; l++){ //auto bins only for now!
    for (m=l;m<tomo.clustering_Npowerspectra; m++){
      if(k==hit){
        sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
        run_cov_ll_ll(OUTFILE,covparams.outdir,ell,dell,l,m,k);
      }
      k=k+1;
      //printf("%d %d %d\n",l,m,k);
    }
  }
  sprintf(OUTFILE,"%s_llss_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Npowerspectra; l++){
    for (m=0;m<tomo.shear_Npowerspectra; m++){
      if(k==hit){
        sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
        run_cov_ll_ss(OUTFILE,covparams.outdir,ell,dell,l,m,k);
      }
      k=k+1;
      //printf("%d\n",k);
    }
  }
  sprintf(OUTFILE,"%s_llls_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Npowerspectra; l++){
    for (m=0;m<tomo.ggl_Npowerspectra; m++){
      if(k==hit){
        sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
        run_cov_ll_ls(OUTFILE,covparams.outdir,ell,dell,l,m,k);
      }
      k=k+1;
      //printf("%d\n",k);
    }
  }
  sprintf(OUTFILE,"%s_lsss_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.ggl_Npowerspectra; l++){
    for (m=0;m<tomo.shear_Npowerspectra; m++){
      if(k==hit){
        sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
        run_cov_ls_ss(OUTFILE,covparams.outdir,ell,dell,l,m,k);
      }
      k=k+1;
      //printf("%d\n",k);
    }
  }

 // lk_lk
  sprintf(OUTFILE,"%s_lklk_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Nbin; l++){
     for (m=l;m<tomo.clustering_Nbin; m++){
        if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_lk_lk(OUTFILE,covparams.outdir,ell,dell,l,m,k);
        }
        k=k+1;
     }
  }

  printf("lklk %d\n",k);

  // lk_ls
  sprintf(OUTFILE,"%s_lkls_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Nbin; l++){
     for (m=0;m<tomo.ggl_Npowerspectra; m++){
        if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_lk_ls(OUTFILE,covparams.outdir,ell,dell,l,m,k);
        }
        k=k+1;
     }
  }
  printf("lkls %d\n",k);

  // lk_kk
  sprintf(OUTFILE,"%s_lkkk_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Nbin; l++){
     if (k==hit){
        sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
        run_cov_lk_kk(OUTFILE,covparams.outdir,ell,dell,l,k);
     }
     // printf("%d\n",k);
     k=k+1;
  }
  printf("lkkk %d\n",k);

  // lk_ks
  sprintf(OUTFILE,"%s_lkks_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Nbin; l++){
     for (m=0;m<tomo.shear_Nbin;m++) {
        if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_lk_ks(OUTFILE,covparams.outdir,ell,dell,l,m,k);
        }
        // printf("%d\n",k);
        k=k+1;
     }
  }
  printf("lkks %d\n",k);

  // lk_ss
  sprintf(OUTFILE,"%s_lkss_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Nbin; l++){
     for (m=0;m<tomo.shear_Npowerspectra; m++){
        if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_lk_ss(OUTFILE,covparams.outdir,ell,dell,l,m,k);
        }
        k=k+1;
        // printf("%d\n",k);
     }
  }
  printf("lkss %d\n",k);

  // ls_kk
  sprintf(OUTFILE,"%s_lskk_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.ggl_Npowerspectra; l++){
     if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_ls_kk(OUTFILE,covparams.outdir,ell,dell,l,k);
        }
    // printf("%d\n",k);
     k=k+1;
  }
  printf("lskk%d\n",k);

  // ls_ks
  sprintf(OUTFILE,"%s_lsks_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.ggl_Npowerspectra; l++){
     for (m=0;m<tomo.shear_Nbin;m++) {
        if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_ls_ks(OUTFILE,covparams.outdir,ell,dell,l,m,k);
        }
        // printf("%d\n",k);
        k=k+1;
     }
  }
  printf("lsks %d\n",k);

  // kk_kk
  sprintf(OUTFILE,"%s_kkkk_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  if (k==hit){
    sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
    run_cov_kk_kk(OUTFILE,covparams.outdir,ell,dell,k);
  }
  // printf("%d\n",k);
  k=k+1;
  printf("kkkk %d\n",k);

  // kk_ks
  sprintf(OUTFILE,"%s_kkks_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.shear_Nbin; l++){
     if (k==hit){
        sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
        run_cov_kk_ks(OUTFILE,covparams.outdir,ell,dell,l,k);
     }
     // printf("%d\n",k);
     k=k+1;
  }
  printf("kkks %d\n",k);

  // kk_ss
  sprintf(OUTFILE,"%s_kkss_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.shear_Npowerspectra; l++){
     if (k==hit){
      sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
      run_cov_kk_ss(OUTFILE,covparams.outdir,ell,dell,l,k);
     }
     k=k+1;
     // printf("%d\n",k);
  }
  printf("kkss %d\n",k);

  // ks_ks
  sprintf(OUTFILE,"%s_ksks_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.shear_Nbin; l++){
     for (m=l;m<tomo.shear_Nbin; m++){
        if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_ks_ks(OUTFILE,covparams.outdir,ell,dell,l,m,k);
        }
        // printf("%d\n",k);
        k=k+1;
     }
  }
  printf("ksks %d\n",k);

 // ks_ss
 sprintf(OUTFILE,"%s_ksss_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
 for (l=0;l<tomo.shear_Nbin; l++){
   for (m=0;m<tomo.shear_Npowerspectra; m++){
     if (k==hit){
        sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
        run_cov_ks_ss(OUTFILE,covparams.outdir,ell,dell,l,m,k);
     }
     k=k+1;
     // printf("%d\n",k);
    }
  }
  printf("ksss %d\n",k);

  // ll_kk
  sprintf(OUTFILE,"%s_llkk_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Npowerspectra; l++){
     if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_ll_kk(OUTFILE,covparams.outdir,ell,dell,l,k);
        }
    // printf("%d\n",k);
     k=k+1;
  }
  printf("llkk %d\n",k);

  // ll_ks
  sprintf(OUTFILE,"%s_llks_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Npowerspectra; l++){
     for (m=0;m<tomo.shear_Nbin;m++) {
        if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_ll_ks(OUTFILE,covparams.outdir,ell,dell,l,m,k);
        }
        // printf("%d\n",k);
        k=k+1;
     }
  }
  printf("llks %d\n",k);

  // ll_lk
  sprintf(OUTFILE,"%s_lllk_cov_Ncl%d_Ntomo%d",survey.name,like.Ncl,tomo.shear_Nbin);
  for (l=0;l<tomo.clustering_Npowerspectra; l++){
     for (m=0;m<tomo.clustering_Nbin;m++) {
        if (k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_ll_lk(OUTFILE,covparams.outdir,ell,dell,l,m,k);
        }
        // printf("%d\n",k);
        k=k+1;
     }
  }
  printf("lllk %d\n",k);

  printf("number of cov blocks for parallelization: %d\n",k-1);
  printf("-----------------\n");
  printf("PROGRAM EXECUTED\n");
  printf("-----------------\n");
  return 0;
}


#ifndef __INC_EMULATORSTRUCT__
#define __INC_EMULATORSTRUCT__

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>

/**
 * \@file emulator_struct.h
 * \brief defines the emulator_struct which contains the details of a working emulator at runtime
 */

struct modelstruct;

/*********************************************************************
This data structure holds pointers to the information used to execute
the emulator at a new point.  This is especially helpful since I want
to stream in queries and get results streamed back out.
*********************************************************************/
typedef struct emulator_struct {
	int nparams;
	int nmodel_points;
	int nregression_fns;
	int nthetas;
	struct modelstruct * model;
	gsl_matrix * cinverse;
	gsl_vector * beta_vector;
	gsl_matrix * h_matrix;
} emulator_struct;

#include "modelstruct.h"

emulator_struct * alloc_emulator_struct(modelstruct * model);
void free_emulator_struct(emulator_struct * e);

void makeHMatrix_es(gsl_matrix *h_matrix, emulator_struct *e);
void makeCovMatrix_es(gsl_matrix *cov_matrix, emulator_struct *e);
void makeKVector_es(gsl_vector *kvector, gsl_vector *point, emulator_struct *e);
void estimateBeta_es(gsl_vector *beta_vector, emulator_struct *e);
void emulate_point(emulator_struct* e, gsl_vector * point,  double * mean, double * variance);
#endif


/* blas/gsl_blas.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*
 * Author:  G. Jungman
 */
#ifndef __GSL_BLAS_H__
#define __GSL_BLAS_H__

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>

#include <gsl/gsl_blas_types.h>
#include <gsl/gsl_types.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

/* ========================================================================
 * Level 1
 * ========================================================================
 */

GSL_EXPORT int gsl_blas_sdsdot (float alpha,
                                const gsl_vector_float * X,
                                const gsl_vector_float * Y,
                                float * result
                                );

GSL_EXPORT int gsl_blas_dsdot (const gsl_vector_float * X,
                               const gsl_vector_float * Y,
                               double * result
                               );

GSL_EXPORT int gsl_blas_sdot (const gsl_vector_float * X,
                              const gsl_vector_float * Y,
                              float * result
                              );

GSL_EXPORT int gsl_blas_ddot (const gsl_vector * X,
                              const gsl_vector * Y,
                              double * result
                              );

GSL_EXPORT int  gsl_blas_cdotu (const gsl_vector_complex_float * X,
                                const gsl_vector_complex_float * Y,
                                gsl_complex_float * dotu);

GSL_EXPORT int  gsl_blas_cdotc (const gsl_vector_complex_float * X,
                                const gsl_vector_complex_float * Y,
                                gsl_complex_float * dotc);

GSL_EXPORT int  gsl_blas_zdotu (const gsl_vector_complex * X,
                                const gsl_vector_complex * Y,
                                gsl_complex * dotu);

GSL_EXPORT int  gsl_blas_zdotc (const gsl_vector_complex * X,
                                const gsl_vector_complex * Y,
                                gsl_complex * dotc);

GSL_EXPORT float  gsl_blas_snrm2  (const gsl_vector_float * X);
GSL_EXPORT float  gsl_blas_sasum  (const gsl_vector_float * X);
GSL_EXPORT double gsl_blas_dnrm2  (const gsl_vector * X);
GSL_EXPORT double gsl_blas_dasum  (const gsl_vector * X);
GSL_EXPORT float  gsl_blas_scnrm2 (const gsl_vector_complex_float * X);
GSL_EXPORT float  gsl_blas_scasum (const gsl_vector_complex_float * X);
GSL_EXPORT double gsl_blas_dznrm2 (const gsl_vector_complex * X);
GSL_EXPORT double gsl_blas_dzasum (const gsl_vector_complex * X);

GSL_EXPORT CBLAS_INDEX_t gsl_blas_isamax (const gsl_vector_float * X);
GSL_EXPORT CBLAS_INDEX_t gsl_blas_idamax (const gsl_vector * X);
GSL_EXPORT CBLAS_INDEX_t gsl_blas_icamax (const gsl_vector_complex_float * X);
GSL_EXPORT CBLAS_INDEX_t gsl_blas_izamax (const gsl_vector_complex * X);

GSL_EXPORT int  gsl_blas_sswap (gsl_vector_float * X,
                                gsl_vector_float * Y);

GSL_EXPORT int  gsl_blas_scopy (const gsl_vector_float * X,
                                gsl_vector_float * Y);

GSL_EXPORT int  gsl_blas_saxpy (float alpha,
                                const gsl_vector_float * X,
                                gsl_vector_float * Y);

GSL_EXPORT int  gsl_blas_dswap (gsl_vector * X,
                                gsl_vector * Y);

GSL_EXPORT int  gsl_blas_dcopy (const gsl_vector * X,
                                gsl_vector * Y);

GSL_EXPORT int  gsl_blas_daxpy (double alpha,
                                const gsl_vector * X,
                                gsl_vector * Y);

GSL_EXPORT int  gsl_blas_cswap (gsl_vector_complex_float * X,
                                gsl_vector_complex_float * Y);

GSL_EXPORT int  gsl_blas_ccopy (const gsl_vector_complex_float * X,
                                gsl_vector_complex_float * Y);

GSL_EXPORT int  gsl_blas_caxpy (const gsl_complex_float alpha,
                                const gsl_vector_complex_float * X,
                                gsl_vector_complex_float * Y);

GSL_EXPORT int  gsl_blas_zswap (gsl_vector_complex * X,
                                gsl_vector_complex * Y);

GSL_EXPORT int  gsl_blas_zcopy (const gsl_vector_complex * X,
                                gsl_vector_complex * Y);

GSL_EXPORT int  gsl_blas_zaxpy (const gsl_complex alpha,
                                const gsl_vector_complex * X,
                                gsl_vector_complex * Y);

GSL_EXPORT int  gsl_blas_srotg (float a[], float b[], float c[], float s[]);

GSL_EXPORT int  gsl_blas_srotmg (float d1[], float d2[], float b1[], float b2, float P[]);

GSL_EXPORT int  gsl_blas_srot (gsl_vector_float * X,
                               gsl_vector_float * Y,
                               float c, float s);

GSL_EXPORT int  gsl_blas_srotm (gsl_vector_float * X,
                                gsl_vector_float * Y,
                                const float P[]);

GSL_EXPORT int  gsl_blas_drotg (double a[], double b[], double c[], double s[]);

GSL_EXPORT int  gsl_blas_drotmg (double d1[], double d2[], double b1[],
                                 double b2, double P[]);

GSL_EXPORT int  gsl_blas_drot (gsl_vector * X,
                               gsl_vector * Y,
                               const double c, const double s);

GSL_EXPORT int  gsl_blas_drotm (gsl_vector * X,
                                gsl_vector * Y,
                                const double P[]);

GSL_EXPORT void gsl_blas_sscal  (float  alpha, gsl_vector_float * X);
GSL_EXPORT void gsl_blas_dscal  (double alpha, gsl_vector * X);
GSL_EXPORT void gsl_blas_cscal  (const gsl_complex_float alpha, gsl_vector_complex_float * X);
GSL_EXPORT void gsl_blas_zscal  (const gsl_complex alpha, gsl_vector_complex * X);
GSL_EXPORT void gsl_blas_csscal (float  alpha, gsl_vector_complex_float * X);
GSL_EXPORT void gsl_blas_zdscal (double alpha, gsl_vector_complex * X);

/* ===========================================================================
 * Level 2
 * ===========================================================================
 */

/*
 * Routines with standard 4 prefixes (S, D, C, Z)
 */
GSL_EXPORT int  gsl_blas_sgemv (CBLAS_TRANSPOSE_t TransA,
                                float alpha,
                                const gsl_matrix_float * A,
                                const gsl_vector_float * X,
                                float beta,
                                gsl_vector_float * Y);

GSL_EXPORT int  gsl_blas_strmv (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                                const gsl_matrix_float * A,
                                gsl_vector_float * X);

GSL_EXPORT int  gsl_blas_strsv (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                                const gsl_matrix_float * A,
                                gsl_vector_float * X);

GSL_EXPORT int  gsl_blas_dgemv (CBLAS_TRANSPOSE_t TransA,
                                double alpha,
                                const gsl_matrix * A,
                                const gsl_vector * X,
                                double beta,
                                gsl_vector * Y);

GSL_EXPORT int  gsl_blas_dtrmv (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                                const gsl_matrix * A,
                                gsl_vector * X);

GSL_EXPORT int  gsl_blas_dtrsv (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                                const gsl_matrix * A,
                                gsl_vector * X);

GSL_EXPORT int  gsl_blas_cgemv (CBLAS_TRANSPOSE_t TransA,
                                const gsl_complex_float alpha,
                                const gsl_matrix_complex_float * A,
                                const gsl_vector_complex_float * X,
                                const gsl_complex_float beta,
                                gsl_vector_complex_float * Y);

GSL_EXPORT int  gsl_blas_ctrmv (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                                const gsl_matrix_complex_float * A,
                                gsl_vector_complex_float * X);

GSL_EXPORT int  gsl_blas_ctrsv (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                                const gsl_matrix_complex_float * A,
                                gsl_vector_complex_float * X);

GSL_EXPORT int  gsl_blas_zgemv (CBLAS_TRANSPOSE_t TransA,
                                const gsl_complex alpha,
                                const gsl_matrix_complex * A,
                                const gsl_vector_complex * X,
                                const gsl_complex beta,
                                gsl_vector_complex * Y);

GSL_EXPORT int  gsl_blas_ztrmv (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                                const gsl_matrix_complex * A,
                                gsl_vector_complex * X);

GSL_EXPORT int  gsl_blas_ztrsv (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t TransA, CBLAS_DIAG_t Diag,
                                const gsl_matrix_complex * A,
                                gsl_vector_complex *X);

/*
 * Routines with S and D prefixes only
 */
GSL_EXPORT int  gsl_blas_ssymv (CBLAS_UPLO_t Uplo,
                                float alpha,
                                const gsl_matrix_float * A,
                                const gsl_vector_float * X,
                                float beta,
                                gsl_vector_float * Y);

GSL_EXPORT int  gsl_blas_sger (float alpha,
                               const gsl_vector_float * X,
                               const gsl_vector_float * Y,
                               gsl_matrix_float * A);

GSL_EXPORT int  gsl_blas_ssyr (CBLAS_UPLO_t Uplo,
                               float alpha,
                               const gsl_vector_float * X,
                               gsl_matrix_float * A);

GSL_EXPORT int  gsl_blas_ssyr2 (CBLAS_UPLO_t Uplo,
                                float alpha,
                                const gsl_vector_float * X,
                                const gsl_vector_float * Y,
                                gsl_matrix_float * A);

GSL_EXPORT int  gsl_blas_dsymv (CBLAS_UPLO_t Uplo,
                                double alpha,
                                const gsl_matrix * A,
                                const gsl_vector * X,
                                double beta,
                                gsl_vector * Y);

GSL_EXPORT int  gsl_blas_dger (double alpha,
                               const gsl_vector * X,
                               const gsl_vector * Y,
                               gsl_matrix * A);

GSL_EXPORT int  gsl_blas_dsyr (CBLAS_UPLO_t Uplo,
                               double alpha,
                               const gsl_vector * X,
                               gsl_matrix * A);

GSL_EXPORT int  gsl_blas_dsyr2 (CBLAS_UPLO_t Uplo,
                                double alpha,
                                const gsl_vector * X,
                                const gsl_vector * Y,
                                gsl_matrix * A);

/*
 * Routines with C and Z prefixes only
 */

GSL_EXPORT int  gsl_blas_chemv (CBLAS_UPLO_t Uplo,
                                const gsl_complex_float alpha,
                                const gsl_matrix_complex_float * A,
                                const gsl_vector_complex_float * X,
                                const gsl_complex_float beta,
                                gsl_vector_complex_float * Y);

GSL_EXPORT int  gsl_blas_cgeru (const gsl_complex_float alpha,
                                const gsl_vector_complex_float * X,
                                const gsl_vector_complex_float * Y,
                                gsl_matrix_complex_float * A);

GSL_EXPORT int  gsl_blas_cgerc (const gsl_complex_float alpha,
                                const gsl_vector_complex_float * X,
                                const gsl_vector_complex_float * Y,
                                gsl_matrix_complex_float * A);

GSL_EXPORT int  gsl_blas_cher (CBLAS_UPLO_t Uplo,
                              float alpha,
                              const gsl_vector_complex_float * X,
                              gsl_matrix_complex_float * A);

GSL_EXPORT int  gsl_blas_cher2 (CBLAS_UPLO_t Uplo,
                                const gsl_complex_float alpha,
                                const gsl_vector_complex_float * X,
                                const gsl_vector_complex_float * Y,
                                gsl_matrix_complex_float * A);

GSL_EXPORT int  gsl_blas_zhemv (CBLAS_UPLO_t Uplo,
                                const gsl_complex alpha,
                                const gsl_matrix_complex * A,
                                const gsl_vector_complex * X,
                                const gsl_complex beta,
                                gsl_vector_complex * Y);

GSL_EXPORT int  gsl_blas_zgeru (const gsl_complex alpha,
                                const gsl_vector_complex * X,
                                const gsl_vector_complex * Y,
                                gsl_matrix_complex * A);

GSL_EXPORT int  gsl_blas_zgerc (const gsl_complex alpha,
                                const gsl_vector_complex * X,
                                const gsl_vector_complex * Y,
                                gsl_matrix_complex * A);

GSL_EXPORT int  gsl_blas_zher (CBLAS_UPLO_t Uplo,
                              double alpha,
                              const gsl_vector_complex * X,
                              gsl_matrix_complex * A);

GSL_EXPORT int  gsl_blas_zher2 (CBLAS_UPLO_t Uplo,
                                const gsl_complex alpha,
                                const gsl_vector_complex * X,
                                const gsl_vector_complex * Y,
                                gsl_matrix_complex * A);

/*
 * ===========================================================================
 * Prototypes for level 3 BLAS
 * ===========================================================================
 */

/*
 * Routines with standard 4 prefixes (S, D, C, Z)
 */
GSL_EXPORT int  gsl_blas_sgemm (CBLAS_TRANSPOSE_t TransA,
                                CBLAS_TRANSPOSE_t TransB,
                                float alpha,
                                const gsl_matrix_float * A,
                                const gsl_matrix_float * B,
                                float beta,
                                gsl_matrix_float * C);

GSL_EXPORT int  gsl_blas_ssymm (CBLAS_SIDE_t Side, CBLAS_UPLO_t Uplo,
                                float alpha,
                                const gsl_matrix_float * A,
                                const gsl_matrix_float * B,
                                float beta,
                                gsl_matrix_float * C);

GSL_EXPORT int  gsl_blas_ssyrk (CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t Trans,
                                float alpha,
                                const gsl_matrix_float * A,
                                float beta,
                                gsl_matrix_float * C);

GSL_EXPORT int  gsl_blas_ssyr2k (CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t Trans,
                                 float alpha,
                                 const gsl_matrix_float * A,
                                 const gsl_matrix_float * B,
                                 float beta,
                                 gsl_matrix_float * C);

GSL_EXPORT int  gsl_blas_strmm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                                CBLAS_DIAG_t Diag,
                                float alpha,
                                const gsl_matrix_float * A,
                                gsl_matrix_float * B);

GSL_EXPORT int  gsl_blas_strsm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                                CBLAS_DIAG_t Diag,
                                float alpha,
                                const gsl_matrix_float * A,
                                gsl_matrix_float * B);

GSL_EXPORT int  gsl_blas_dgemm (CBLAS_TRANSPOSE_t TransA,
                                CBLAS_TRANSPOSE_t TransB,
                                double alpha,
                                const gsl_matrix * A,
                                const gsl_matrix * B,
                                double beta,
                                gsl_matrix * C);

GSL_EXPORT int  gsl_blas_dsymm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo,
                                double alpha,
                                const gsl_matrix * A,
                                const gsl_matrix * B,
                                double beta,
                                gsl_matrix * C);

GSL_EXPORT int  gsl_blas_dsyrk (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t Trans,
                                double alpha,
                                const gsl_matrix * A,
                                double beta,
                                gsl_matrix * C);

GSL_EXPORT int  gsl_blas_dsyr2k (CBLAS_UPLO_t Uplo,
                                 CBLAS_TRANSPOSE_t Trans,
                                 double alpha,
                                 const  gsl_matrix * A,
                                 const  gsl_matrix * B,
                                 double beta,
                                 gsl_matrix * C);

GSL_EXPORT int  gsl_blas_dtrmm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                                CBLAS_DIAG_t Diag,
                                double alpha,
                                const gsl_matrix * A,
                                gsl_matrix * B);

GSL_EXPORT int  gsl_blas_dtrsm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                                CBLAS_DIAG_t Diag,
                                double alpha,
                                const gsl_matrix * A,
                                gsl_matrix * B);

GSL_EXPORT int  gsl_blas_cgemm (CBLAS_TRANSPOSE_t TransA,
                                CBLAS_TRANSPOSE_t TransB,
                                const gsl_complex_float alpha,
                                const gsl_matrix_complex_float * A,
                                const gsl_matrix_complex_float * B,
                                const gsl_complex_float beta,
                                gsl_matrix_complex_float * C);

GSL_EXPORT int  gsl_blas_csymm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo,
                                const gsl_complex_float alpha,
                                const gsl_matrix_complex_float * A,
                                const gsl_matrix_complex_float * B,
                                const gsl_complex_float beta,
                                gsl_matrix_complex_float * C);

GSL_EXPORT int  gsl_blas_csyrk (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t Trans,
                                const gsl_complex_float alpha,
                                const gsl_matrix_complex_float * A,
                                const gsl_complex_float beta,
                                gsl_matrix_complex_float * C);

GSL_EXPORT int  gsl_blas_csyr2k (CBLAS_UPLO_t Uplo,
                                 CBLAS_TRANSPOSE_t Trans,
                                 const gsl_complex_float alpha,
                                 const gsl_matrix_complex_float * A,
                                 const gsl_matrix_complex_float * B,
                                 const gsl_complex_float beta,
                                 gsl_matrix_complex_float * C);

GSL_EXPORT int  gsl_blas_ctrmm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                                CBLAS_DIAG_t Diag,
                                const gsl_complex_float alpha,
                                const gsl_matrix_complex_float * A,
                                gsl_matrix_complex_float * B);

GSL_EXPORT int  gsl_blas_ctrsm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                                CBLAS_DIAG_t Diag,
                                const gsl_complex_float alpha,
                                const gsl_matrix_complex_float * A,
                                gsl_matrix_complex_float * B);

GSL_EXPORT int  gsl_blas_zgemm (CBLAS_TRANSPOSE_t TransA,
                                CBLAS_TRANSPOSE_t TransB,
                                const gsl_complex alpha,
                                const gsl_matrix_complex * A,
                                const gsl_matrix_complex * B,
                                const gsl_complex beta,
                                gsl_matrix_complex * C);

GSL_EXPORT int  gsl_blas_zsymm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo,
                                const gsl_complex alpha,
                                const gsl_matrix_complex * A,
                                const gsl_matrix_complex * B,
                                const gsl_complex beta,
                                gsl_matrix_complex * C);

GSL_EXPORT int  gsl_blas_zsyrk (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t Trans,
                                const gsl_complex alpha,
                                const gsl_matrix_complex * A,
                                const gsl_complex beta,
                                gsl_matrix_complex * C);

GSL_EXPORT int  gsl_blas_zsyr2k (CBLAS_UPLO_t Uplo,
                                 CBLAS_TRANSPOSE_t Trans,
                                 const gsl_complex alpha,
                                 const gsl_matrix_complex * A,
                                 const gsl_matrix_complex * B,
                                 const gsl_complex beta,
                                 gsl_matrix_complex *C);

GSL_EXPORT int  gsl_blas_ztrmm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                                CBLAS_DIAG_t Diag,
                                const gsl_complex alpha,
                                const gsl_matrix_complex * A,
                                gsl_matrix_complex * B);

GSL_EXPORT int  gsl_blas_ztrsm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo, CBLAS_TRANSPOSE_t TransA,
                                CBLAS_DIAG_t Diag,
                                const gsl_complex alpha,
                                const gsl_matrix_complex * A,
                                gsl_matrix_complex * B);

/*
 * Routines with prefixes C and Z only
 */
GSL_EXPORT int  gsl_blas_chemm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo,
                                const gsl_complex_float alpha,
                                const gsl_matrix_complex_float * A,
                                const gsl_matrix_complex_float * B,
                                const gsl_complex_float beta,
                                gsl_matrix_complex_float * C);

GSL_EXPORT int  gsl_blas_cherk (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t Trans,
                                float alpha,
                                const gsl_matrix_complex_float * A,
                                float beta,
                                gsl_matrix_complex_float * C);

GSL_EXPORT int  gsl_blas_cher2k (CBLAS_UPLO_t Uplo,
                                 CBLAS_TRANSPOSE_t Trans,
                                 const gsl_complex_float alpha,
                                 const gsl_matrix_complex_float * A,
                                 const gsl_matrix_complex_float * B,
                                 float beta,
                                 gsl_matrix_complex_float * C);

GSL_EXPORT int  gsl_blas_zhemm (CBLAS_SIDE_t Side,
                                CBLAS_UPLO_t Uplo,
                                const gsl_complex alpha,
                                const gsl_matrix_complex * A,
                                const gsl_matrix_complex * B,
                                const gsl_complex beta,
                                gsl_matrix_complex * C);

GSL_EXPORT int  gsl_blas_zherk (CBLAS_UPLO_t Uplo,
                                CBLAS_TRANSPOSE_t Trans,
                                double alpha,
                                const gsl_matrix_complex * A,
                                double beta,
                                gsl_matrix_complex * C);

GSL_EXPORT int  gsl_blas_zher2k (CBLAS_UPLO_t Uplo,
                                 CBLAS_TRANSPOSE_t Trans,
                                 const gsl_complex alpha,
                                 const gsl_matrix_complex * A,
                                 const gsl_matrix_complex * B,
                                 double beta,
                                 gsl_matrix_complex * C);

__END_DECLS

#endif /* __GSL_BLAS_H__ */