/*
* -----------------------------------------------------------------
*  In Situ Adaptive Tabulation Library --- isat_lib.c
*  Version: 1.6180
*  Date: Nov 15, 2010
* -----------------------------------------------------------------
*  Programmer: Americo Barbosa da Cunha Junior
*              americo.cunhajr@gmail.com
* -----------------------------------------------------------------
*  Copyright (c) 2010 by Americo Barbosa da Cunha Junior
*
*  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 3 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.
*
*  A copy of the GNU General Public License is available in
*  LICENSE.txt or http://www.gnu.org/licenses/.
* -----------------------------------------------------------------
*  This is the implementation file of a library
*  to work with ISAT algorithm.
* -----------------------------------------------------------------
*/

#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>

#include "../include/thrm_lib.h"
#include "../include/ell_lib.h"
#include "../include/ode_lib.h"
#include "../include/isat_lib.h"

/*
*------------------------------------------------------------
*   isat_alloc
*
*   This function alocates memory for a struct isat.
*
*   Output:
*   isat - pointer to a struct isat
*
*   last update: Jul 13, 2010
*------------------------------------------------------------
*/

isat_wrk *isat_alloc()
{
    /* memory allocation for isat_wrk */
    isat_wrk *isat = NULL;
    isat = (isat_wrk *) malloc(sizeof(isat_wrk));
    if ( isat == NULL )
        return NULL;

    /* setting isat_wrk elements equal NULL and 0.0 */
    isat->root     = NULL;
    isat->lf       = 0;
    isat->nd       = 0;
    isat->add      = 0;
    isat->grw      = 0;
    isat->rtv      = 0;
    isat->dev      = 0;
    isat->hgt      = 0;
    isat->max_lf   = 0;
    isat->time_add = 0.0;
    isat->time_grw = 0.0;
    isat->time_rtv = 0.0;
    isat->time_dev = 0.0;

    return isat;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   isat_free
*
*   This function frees the memory used by a struct isat.
*
*   Input:
*   isat - pointer to a struct isat
*
*   last update: May 10, 2009
*------------------------------------------------------------
*/

void isat_free(void **isat_bl)
{
    isat_wrk *isat = NULL;

    /* checking if isat_bl is NULL */
    if ( *isat_bl == NULL )
        return;

    isat = (isat_wrk *) (*isat_bl);

    /* releasing allocated memory by isat_wrk elements */
    if( isat->root != NULL )
    {
        bst_node_free((void **)&(isat->root));
        isat->root = NULL;
    }

    /* releasing allocated memory by isat_wrk */
    free(*isat_bl);
    *isat_bl = NULL;

    return;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   isat_set
*
*   This function initiates ISAT binary search tree.
*
*   Input:
*   isat - pointer to a struct isat
*
*   last update: May 13, 2009
*------------------------------------------------------------
*/

int isat_set(isat_wrk *isat)
{
    /* checking if isat is NULL */
    if ( isat == NULL )
        return GSL_EINVAL;

    /* memory allocation for binary search tree root */
    if ( isat->root == NULL )
    {
        isat->root = bst_node_alloc();
        if ( isat->root == NULL )
        {
            free(isat);
            isat = NULL;
            return GSL_EINVAL;
        }
    }

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   isat_statistics
*
*   This function prints on the screen informations
*   about the isat workspace.
*
*   Input:
*   isat - pointer to a struct isat
*
*   last update: Jul 13, 2010
*------------------------------------------------------------
*/

void isat_statistics(isat_wrk *isat)
{
    double time_add;
    double time_grw;
    double time_rtv;
    double time_dev;

    time_add = (double) (isat->time_add / CLOCKS_PER_SEC) / isat->add;
    time_grw = (double) (isat->time_grw / CLOCKS_PER_SEC) / isat->grw;
    time_rtv = (double) (isat->time_rtv / CLOCKS_PER_SEC) / isat->rtv;
    time_dev = (double) (isat->time_dev / CLOCKS_PER_SEC) / isat->dev;

    printf("\n ISAT statistics:");
    printf("\n # of adds         = %d",   isat->add);
    printf("\n # of grows        = %d",   isat->grw);
    printf("\n # of retrieves    = %d",   isat->rtv);
    printf("\n # of dir. eval.   = %d",   isat->dev);
    printf("\n # of leaves       = %d",   isat->lf);
    printf("\n # of nodes        = %d",   isat->nd);
    printf("\n tree height       = %d\n", isat->hgt);

    printf("\n average values for CPU time (s):");
    printf("\n add: %+.6e",time_add);
    printf("\n grw: %+.6e",time_grw);
    printf("\n rtv: %+.6e",time_rtv);
    printf("\n dev: %+.6e",time_dev);

    return;
}
/*------------------------------------------------------------*/

/*
* -----------------------------------------------------------------
*   isat_input
*
*   This function receives ISAT parameters.
*
*   Input:
*   max_lf - maximum value of tree leaves
*   etol   - ISAT error tolerance
*   n0     - factor to multiply by unit roundoff
*
*   Output:
*   success or error
*
*   last update: Feb 25, 2010
* -----------------------------------------------------------------
*/

int isat_input(unsigned int *max_lf,double *etol,double *n0)
{
    printf("\n Input ISAT parameters:\n");

    printf("\n maximum of tree leaves:");
    scanf("%d", max_lf);
    printf("\n %d\n", *max_lf);
    if( *max_lf <= 0 )
	GSL_ERROR(" max_lf must be a positive integer (max_lf > 0)",GSL_EINVAL);

    printf("\n ISAT error tolerance:");
    scanf("%lf", etol);
    printf("\n %+.1e\n", *etol);
    if( *etol < 0.0 )
	GSL_ERROR(" etol must be grather than zero (etol > 0.0)",GSL_EINVAL);

    printf("\n Unit roundoff multiple:");
    scanf("%lf", n0);
    printf("\n %+.1e\n", *n0);
    if( *n0 < 0.0 )
	GSL_ERROR(" n0 must be grather than zero (n0 > 0.0)",GSL_EINVAL);

    return GSL_SUCCESS;
}
/*----------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   isat_eoa_matrix
*
*   This function computes the EOA matrix in Cholesky form.
*
*   Input:
*   A    - gradient matrix
*   n0   - factor to multiply by unit roundoff
*   etol - error tolerance
*
*   Output:
*   L    - EOA Cholesky matrix
*
*   last update: Feb 19, 2010
*------------------------------------------------------------
*/

void isat_eoa_mtrx(gsl_matrix *A,double etol,double n0,
						gsl_matrix *L)
{
    unsigned int i;
    double  eps_max   = 0.5;
    double  eps_min   = etol/(n0*DBL_EPSILON);
    gsl_matrix *Aetol = NULL;
    gsl_matrix *V     = NULL;
    gsl_vector *sig   = NULL;

    /* memory allocation */
    Aetol = gsl_matrix_calloc(A->size2,A->size2);
    V     = gsl_matrix_calloc(A->size2,A->size2);
    sig   = gsl_vector_calloc(A->size2);

    /* Aetol := A */
    gsl_matrix_memcpy(Aetol,A);

    /* Aetol := (1/etol).A */
    gsl_matrix_scale (Aetol,1.0/etol);

    /* Aetol = U*sig*V^T */
    ell_psd2eig(Aetol,V,sig);

    /* eliminating small and large singular values */
    for ( i = 0; i < sig->size; i++ )
        sig->data[i] = GSL_MIN( GSL_MAX(sig->data[i],eps_max), eps_min);

    /* V*sig^2*V^T = L*L^T */
    ell_eig2chol(V,sig,L);

    /* releasing allocated memory */
    gsl_vector_free(sig);
    gsl_matrix_free(V);
    gsl_matrix_free(Aetol);
    sig   = NULL;
    V     = NULL;
    Aetol = NULL;

    return;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   isat_lerror
*
*   This function computes the local error defined as
*
*   eps = 2-norm(Rl(phi)- R(phi)) where
*
*   Rl(phi) = R(phi0) + A*(phi-phi0).
*
*   Input:
*   Rphi  - reaction mapping of phi
*   Rphi0 - reaction mapping of phi0
*   A     - mapping gradient matrix
*   phi   - query composition
*   phi0  - initial composition
*
*   Output:
*   eps   - local error
*
*   last update: Feb 19, 2010
*------------------------------------------------------------
*/

double isat_lerror(gsl_vector *Rphi,gsl_vector *Rphi0,
		gsl_matrix *A,gsl_vector *phi,gsl_vector *phi0)
{
    double eps;
    gsl_vector *Rlphi = NULL;

    /* memory allocation for Rlphi */
    Rlphi = gsl_vector_calloc(phi->size);

    /* Rlphi := R(phi0) + A*(phi-phi0) */
    linear_approx(phi,phi0,Rphi0,A,Rlphi);

    /* Rlphi := Rl(phi) - R(phi) */
    gsl_vector_sub(Rlphi,Rphi);

    /* eps := 2-norm(Rl(phi) - R(phi)) */
    eps = gsl_blas_dnrm2(Rlphi);

    /* releasing allocated memory */
    gsl_vector_free(Rlphi);
    Rlphi = NULL;

    return eps;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   isat4
*
*   This function executes the 4-th version of the
*   in situ adaptive tabulation algorithm.
*
*
*   Input:
*   isat      - isat workspace
*   thrm_data - thermochemistry workspace
*   etol      - error tolerance
*   n0        - factor to multiply by unit roundoff
*   t0        - initial time
*   delta_t   - time step
*   phi       - query composition
*   A         - mapping gradient matrix
*   L         - EOA Cholesky matrix
*
*   Output:
*   Rphi      - reaction mapping
*   success or error
*
*   last update: Nov 15, 2010
*------------------------------------------------------------
*/

int isat4(isat_wrk *isat,void *thrm_data,void *cvode_mem,
	    double etol,double n0,double t0,double delta_t,
    gsl_vector *phi,gsl_matrix *A,gsl_matrix *L,gsl_vector *Rphi)
{
    clock_t cpu_start = clock();

    /* ISAT first step */
    if( isat->lf == 0 )
    {
        int flag;
        bst_leaf *first_leaf = NULL;

	/* memory allocation for first_leaf */
        first_leaf = bst_leaf_alloc();
	if ( first_leaf == NULL )
                return GSL_ENOMEM;

        /* performing direct integration */
        flag = odesolver_reinit(thrm_eqs,thrm_data,t0,
                                        ATOL,RTOL,phi,cvode_mem);
        if ( flag != GSL_SUCCESS )
            return flag;

        flag = odesolver(cvode_mem,delta_t,Rphi);
        if ( flag != GSL_SUCCESS )
            return flag;

        /* computing the mapping gradient matrix */
        flag = gradient(thrm_eqs,thrm_data,cvode_mem,t0,delta_t,
                                                ATOL,RTOL,phi,Rphi,A);
        if ( flag != GSL_SUCCESS )
            return flag;

        /* computing EOA Cholesky matrix */
        isat_eoa_mtrx(A,etol,n0,L);

        /* setting first leaf elements */
        bst_leaf_set(phi,Rphi,A,L,first_leaf);
        isat->root->r_leaf = first_leaf;

        /* updating leaves and height counters */
        isat->lf++;
        isat->hgt = bst_height(isat->root);

        return GSL_SUCCESS;
    }

    /* ISAT second and following steps */
    int side, flag;
    double dot         = 0.0;
    bst_leaf *end_leaf = NULL;
    bst_node *end_node = NULL;

    /* searching for the near composition in binary search tree */
    if( isat->lf > 1 )
	side = bst_search(isat->root,phi,&end_node,&end_leaf);
    else
    {
        end_node = isat->root;
        end_leaf = isat->root->r_leaf;
        side     = RIGHT;
    }

    /* checking if the near composition is inside EOA */
    dot = ell_pt_in(phi,end_leaf->phi,end_leaf->L);
    if( gsl_fcmp(dot,1.0,ATOL) < 1 )
    {
        /* computing linear approximation */
        linear_approx(phi,end_leaf->phi,end_leaf->Rphi,end_leaf->A,Rphi);

        /* updating counters */
        isat->rtv++;
	isat->time_rtv += clock() - cpu_start;

        return GSL_SUCCESS;
    }
    else
    {
        double lerror = 0.0;

        /* performing direct integration */
        flag = odesolver_reinit(thrm_eqs,thrm_data,t0,
                                        ATOL,RTOL,phi,cvode_mem);
        if ( flag != GSL_SUCCESS )
            return flag;

        flag = odesolver(cvode_mem,delta_t,Rphi);
        if ( flag != GSL_SUCCESS )
            return flag;

        /* computing ISAT local error */
        lerror = isat_lerror(Rphi,end_leaf->Rphi,
                            end_leaf->A,phi,end_leaf->phi);

        /* checking if lerror is greater than etol */
        if( gsl_fcmp(lerror,etol,ATOL) < 1 )
        {
            /* growing the EOA */
            ell_pt_modify(phi,end_leaf->phi,end_leaf->L);

            /* updating counters */
            isat->grw++;
	    isat->time_grw += clock() - cpu_start;

            return GSL_SUCCESS;
        }
        else
        {
	    /* checking if the maximum # of leaves was excced */
	    if( isat->lf > isat->max_lf )
	    {
	        /*  updating counter */
	        isat->dev++;
		isat->time_dev +=  clock() - cpu_start;

		return GSL_SUCCESS;
	    }

            bst_leaf *new_leaf = NULL;

	    /* memory allocation */
            new_leaf = bst_leaf_alloc();
            if ( new_leaf == NULL )
                return GSL_ENOMEM;

            /* computing the mapping gradient matrix */
            flag = gradient(thrm_eqs,thrm_data,cvode_mem,t0,delta_t,
                                                ATOL,RTOL,phi,Rphi,A);
            if ( flag != GSL_SUCCESS )
		return flag;

            /* computing EOA Cholesky matrix */
            isat_eoa_mtrx(A,etol,n0,L);

            /* setting new_leaf elements */
            bst_leaf_set(phi,Rphi,A,L,new_leaf);

            /* cheking if binary search tree has more than one leaf */
            if( isat->lf > 1 )
            {
                bst_node *new_node = NULL;

		/* memory allocation for new node */
                new_node = bst_node_alloc();
                if ( new_node == NULL )
                    return GSL_ENOMEM;

		/* setting the leaves of the new node */
                bst_node_set(end_leaf,new_leaf,new_node);

		/* adding the new node to the tree */
                bst_node_add(side,end_node,new_node);
            }
	    else
                bst_node_set(end_leaf,new_leaf,end_node);

            /* updating counters */
            isat->add++;
            isat->lf++;
            isat->nd++;
            isat->hgt = bst_height(isat->root);
	    isat->time_add += clock() - cpu_start;

            return GSL_SUCCESS;
        }
    }
}
/*------------------------------------------------------------*/


#include "jfftw_complex_Plan.h"
#include <fftw.h>

/*
 * Class:     jfftw_complex_Plan
 * Method:    createPlan
 * Signature: (III)V
 */
JNIEXPORT void JNICALL Java_jfftw_complex_Plan_createPlan( JNIEnv *env, jobject obj, jint n, jint dir, jint flags )
{
	jclass clazz;
	jfieldID id;
	jbyteArray arr;
	unsigned char* carr;

	if( sizeof( jdouble ) != sizeof( fftw_real ) )
	{
		(*env)->ThrowNew( env, (*env)->FindClass( env, "java/lang/RuntimeException" ), "jdouble and fftw_real are incompatible" );
		return;
	}

	clazz = (*env)->GetObjectClass( env, obj );
	id    = (*env)->GetFieldID( env, clazz, "plan", "[B" );
	arr   = (*env)->NewByteArray( env, sizeof( fftw_plan ) );
	carr  = (*env)->GetByteArrayElements( env, arr, 0 );

	(*env)->MonitorEnter( env, (*env)->FindClass( env, "jfftw/Plan" ) );

	*(fftw_plan*)carr = fftw_create_plan( n, dir, flags );

	(*env)->MonitorExit( env, (*env)->FindClass( env, "jfftw/Plan" ) );

	(*env)->ReleaseByteArrayElements( env, arr, carr, 0 );
	(*env)->SetObjectField( env, obj, id, arr );
}
/*
 * Class:     jfftw_complex_Plan
 * Method:    createPlanSpecific
 * Signature: (III[DI[DI)V
 */
JNIEXPORT void JNICALL Java_jfftw_complex_Plan_createPlanSpecific( JNIEnv *env, jobject obj, jint n, jint dir, jint flags, jdoubleArray in, jint idist, jdoubleArray out, jint odist )
{
	jclass clazz;
	jfieldID id;
	jbyteArray arr;
	unsigned char* carr;
	double *cin, *cout;

	if( sizeof( jdouble ) != sizeof( fftw_real ) )
	{
		(*env)->ThrowNew( env, (*env)->FindClass( env, "java/lang/RuntimeException" ), "jdouble and fftw_real are incompatible" );
		return;
	}

	clazz = (*env)->GetObjectClass( env, obj );
	id    = (*env)->GetFieldID( env, clazz, "plan", "[B" );
	arr   = (*env)->NewByteArray( env, sizeof( fftw_plan ) );
	carr  = (*env)->GetByteArrayElements( env, arr, 0 );
	cin   = (*env)->GetDoubleArrayElements( env, in, 0 );
	cout  = (*env)->GetDoubleArrayElements( env, out, 0 );

	(*env)->MonitorEnter( env, (*env)->FindClass( env, "jfftw/Plan" ) );

	*(fftw_plan*)carr = fftw_create_plan_specific( n, dir, flags, (fftw_complex*)cin, idist, (fftw_complex*)cout, odist );

	(*env)->MonitorExit( env, (*env)->FindClass( env, "jfftw/Plan" ) );

	(*env)->ReleaseDoubleArrayElements( env, in, cin, 0 );
	(*env)->ReleaseDoubleArrayElements( env, out, cout, 0 );
	(*env)->ReleaseByteArrayElements( env, arr, carr, 0 );
	(*env)->SetObjectField( env, obj, id, arr );
}
/*
 * Class:     jfftw_complex_Plan
 * Method:    destroyPlan
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_jfftw_complex_Plan_destroyPlan( JNIEnv* env, jobject obj )
{
	jclass clazz = (*env)->GetObjectClass( env, obj );
	jfieldID id = (*env)->GetFieldID( env, clazz, "plan", "[B" );
	jbyteArray arr = (jbyteArray)(*env)->GetObjectField( env, obj, id );
	unsigned char* carr = (*env)->GetByteArrayElements( env, arr, 0 );

	fftw_destroy_plan( *(fftw_plan*)carr );

	(*env)->ReleaseByteArrayElements( env, arr, carr, 0 );
	(*env)->SetObjectField( env, obj, id, NULL );
}
/*
 * Class:     jfftw_complex_Plan
 * Method:    transform
 * Signature: ([D)[D
 */
JNIEXPORT jdoubleArray JNICALL Java_jfftw_complex_Plan_transform___3D( JNIEnv* env, jobject obj, jdoubleArray in )
{
	jdouble *cin, *cout;
	jdoubleArray out;
	int i;

	jclass clazz = (*env)->GetObjectClass( env, obj );
	jfieldID id = (*env)->GetFieldID( env, clazz, "plan", "[B" );
	jbyteArray arr = (jbyteArray)(*env)->GetObjectField( env, obj, id );
	unsigned char* carr = (*env)->GetByteArrayElements( env, arr, 0 );
	fftw_plan plan = *(fftw_plan*)carr;
	if( plan->n * 2 != (*env)->GetArrayLength( env, in ) )
	{
		(*env)->ThrowNew( env, (*env)->FindClass( env, "java/lang/IndexOutOfBoundsException" ), "the Plan was created for a different length" );
		(*env)->ReleaseByteArrayElements( env, arr, carr, 0 );
		return NULL;
	}

	cin = (*env)->GetDoubleArrayElements( env, in, 0 );

	if( ! plan->flags & FFTW_THREADSAFE )
	{
		// synchronization
		(*env)->MonitorEnter( env, obj );
	}

	if( plan->flags & FFTW_IN_PLACE )
	{
		out = in;

		fftw_one( plan, (fftw_complex*)cin, NULL );
	}
	else
	{
		out = (*env)->NewDoubleArray( env, plan->n * 2 );
		cout = (*env)->GetDoubleArrayElements( env, out, 0 );

		fftw_one( plan, (fftw_complex*)cin, (fftw_complex*)cout );

		(*env)->ReleaseDoubleArrayElements( env, out, cout, 0 );
	}

	if( ! plan->flags & FFTW_THREADSAFE )
	{
		// synchronization
		(*env)->MonitorEnter( env, obj );
	}

	(*env)->ReleaseByteArrayElements( env, arr, carr, 0 );
	(*env)->ReleaseDoubleArrayElements( env, in, cin, 0 );
	return out;
}
/*
 * Class:     jfftw_complex_Plan
 * Method:    transform
 * Signature: (I[DII[DII)V
 */
JNIEXPORT void JNICALL Java_jfftw_complex_Plan_transform__I_3DII_3DII( JNIEnv *env, jobject obj, jint howmany, jdoubleArray in, jint istride, jint idist, jdoubleArray out, jint ostride, jint odist )
{
	jdouble *cin, *cout;
	int i;

	jclass clazz = (*env)->GetObjectClass( env, obj );
	jfieldID id = (*env)->GetFieldID( env, clazz, "plan", "[B" );
	jbyteArray arr = (jbyteArray)(*env)->GetObjectField( env, obj, id );
	unsigned char* carr = (*env)->GetByteArrayElements( env, arr, 0 );
	fftw_plan plan = *(fftw_plan*)carr;
	if( (howmany - 1) * idist * 2 + plan->n * istride * 2 != (*env)->GetArrayLength( env, in ) )
	{
		(*env)->ThrowNew( env, (*env)->FindClass( env, "java/lang/IndexOutOfBoundsException" ), "the Plan was created for a different length (in)" );
		(*env)->ReleaseByteArrayElements( env, arr, carr, 0 );
		return;
	}
	if( (howmany - 1) * odist * 2 + plan->n * ostride * 2 != (*env)->GetArrayLength( env, out ) )
	{
		(*env)->ThrowNew( env, (*env)->FindClass( env, "java/lang/IndexOutOfBoundsException" ), "the Plan was created for a different length (out)" );
		(*env)->ReleaseByteArrayElements( env, arr, carr, 0 );
		return;
	}

	cin = (*env)->GetDoubleArrayElements( env, in, 0 );
	cout = (*env)->GetDoubleArrayElements( env, out, 0 );

	if( ! plan->flags & FFTW_THREADSAFE )
	{
		// synchronization
		(*env)->MonitorEnter( env, obj );
	}
	fftw( plan, howmany, (fftw_complex*)cin, istride, idist, (fftw_complex*)cout, ostride, odist );
	if( ! plan->flags & FFTW_THREADSAFE )
	{
		// synchronization
		(*env)->MonitorExit( env, obj );
	}

	(*env)->ReleaseByteArrayElements( env, arr, carr, 0 );
	(*env)->ReleaseDoubleArrayElements( env, in, cin, 0 );
	(*env)->ReleaseDoubleArrayElements( env, out, cout, 0 );
}


#define ORIGIN_DATE       "2012.01.05"
#define ORIGIN_VERSION    "2.1"

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
#include <assert.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include "common.h"
#include "ivector.h"
#include "species.h"
#include "specieslist.h"
#include "graph.h"
#include "utils.h"

#define MODEL_BDM_NEUTRAL      0
#define MODEL_BDM_SELECTION    1

// Parameters of the simulations.
typedef struct
{
    int m;             // Type of model.
    int communities;   // Number of communities.
    int j_per_c;       // Number of individuals per community.
    int k_gen;         // Number of generations (in thousands).
    int init_species;  // Initial number of species.
    double mu;         // Mutation rate.
    double omega;      // Weight of the links between communities.
    double s;          // Selection coefficient.
    double r;          // Radius (for random geometric graphs).
    double w;          // Width (for rectangle random geometric graphs).
    char *ofilename;   // Name of the output files.
    char *shape;       // Shape of the metacommunity.
}
Params;

// Prototype for the function used by the threads
void *sim(void *parameters);
// The function used to setup the cumulative jagged array from the graph.
double **setup_cumulative_list(const graph *g, double omega);

/////////////////////////////////////////////////////////////
// Main                                                    //
/////////////////////////////////////////////////////////////
int main(int argc, const char *argv[])
{
    // Set default values;
    Params p;
    p.m = MODEL_BDM_SELECTION;
    p.communities = 10;
    p.j_per_c = 10000;
    p.init_species = 20;
    p.k_gen = 100;
    p.mu = 1e-4;
    p.omega = 5e-4;
    p.s = 0.15;
    p.r = 0.25;
    p.w = 0.25;
    p.ofilename = (char*)malloc(50);
    p.shape = (char*)malloc(20);

    // Number of simulations;
    int n_threads = 1;

    // Options;
    if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-')
    {
        // --help
        if (argv[1][2] == 'h')
        {
            printf("Usage: ./origin [options]\n");
            printf("Example: ./origin -x=8 -shape=circle -model=1 -s=0.10\n");
            printf("General options:\n");
            printf("  --help          How you got here...\n");
            printf("  --version       Display version.\n");
            printf("  --ref           Display reference.\n");
            printf("Simulation parameters:\n");
            printf("  -x\n");
            printf("    description:  Number of simulations to run. Each simulation\n");
            printf("                  will use a distinct POSIX thread. \n");
            printf("    values:       Any unsigned integer.\n");
            printf("    default:      1\n");
            printf("  -model\n");
            printf("    description:  Set the model used.\n");
            printf("    values:       0, 1.\n");
            printf("    details:      0 = Neutral BDM speciation.\n");
            printf("                  1 = BDM speciation with selection.\n");
            printf("    default:      1\n");
            printf("  -shape\n");
            printf("    description:  Set the shape of the metacommunity.\n");
            printf("    values:       circle, complete, random, rectangle, star.\n");
            printf("    default:      random\n");
            printf("  -r\n");
            printf("    description:  Threshold radius for random geometric\n");
            printf("                  graphs.\n");
            printf("    values:       Any double greater than 0.\n");
            printf("    default:      0.25\n");
            printf("  -w\n");
            printf("    description:  The width of a rectangle random geometric\n");
            printf("                  graph.\n");
            printf("    values:       0 < w < 1\n");
            printf("    default:      0.25\n");
            printf("  -g\n");
            printf("    description:  Number of generations in thousands.\n");
            printf("    values:       Any positive integer.\n");
            printf("    default:      100 (i.e.: 100 000 generations)\n");
            printf("  -c\n");
            printf("    description:  Number of local communities.\n");
            printf("    values:       Any integer greater than 0.\n");
            printf("    default:      10\n");
            printf("  -jpc\n");
            printf("    description:  Number of individuals per communities.\n");
            printf("    values:       Any integer greater than 0.\n");
            printf("    default:      10000\n");
            printf("  -sp\n");
            printf("    description:  Initial number of species. Must be a factor\n");
            printf("                  of the number of individuals/community.\n");
            printf("    values:       Any positive integer greater than 0.\n");
            printf("    default:      20\n");
            printf("  -s\n");
            printf("    description:  Set the selection coefficient (model 1 only).\n");
            printf("    values:       Any double in the (-1.0, 1.0) interval.\n");
            printf("    default:      0.15\n");
            printf("  -mu\n");
            printf("    description:  Set the mutation rate.\n");
            printf("    values:       Any positive double.\n");
            printf("    default:      1e-4\n");
            printf("  -omega\n");
            printf("    description:  The weight of the proper edges.\n");
            printf("    values:       Any nonnegative double.\n");
            printf("    default:      5e-4\n");
            printf("  -o\n");
            printf("    description:  Name of the output files.\n");
            printf("    values:       Any string.\n");
            return EXIT_SUCCESS;
        }
        else if (argv[1][2] == 'v') // --version
        {
            printf("origin v.%s (%s)\n", ORIGIN_VERSION, ORIGIN_DATE);
            printf("Copyright (c) 2010-2011 Philippe Desjardins-Proulx <philippe.d.proulx@gmail.com>\n");
            printf("GPLv2 license  (see LICENSE)\n");
            return EXIT_SUCCESS;
        }
        else if (argv[1][2] == 'r') // --ref
        {
            printf("P. Desjardins-Proul and D. Gravel. How likely is speciation in\n  neutral ecology? The American Naturalist 179(1).\n");
            return EXIT_SUCCESS;
        }
    } // end '--' options

    // Read options
    read_opt_i("g", argv, argc, &p.k_gen);
    read_opt_i("jpc", argv, argc, &p.j_per_c);
    read_opt_i("model", argv, argc, &p.m);
    read_opt_i("c", argv, argc, &p.communities);
    read_opt_i("sp", argv, argc, &p.init_species);
    read_opt_i("x", argv, argc, &n_threads);
    read_opt_d("mu", argv, argc, &p.mu);
    read_opt_d("omega", argv, argc, &p.omega);
    read_opt_d("r", argv, argc, &p.r);
    read_opt_d("w", argv, argc, &p.w);
    read_opt_d("s", argv, argc, &p.s);
    if (read_opt_s("o", argv, argc, p.ofilename) == false)
    {
        sprintf(p.ofilename, "");
    }
    if (read_opt_s("shape", argv, argc, p.shape) == false)
    {
        sprintf(p.shape, "random");
    }

    printf("<?xml version=\"1.0\"?>\n");
    printf("<origin_ssne>\n");
    printf("  <model>");
    if (p.m == MODEL_BDM_NEUTRAL)
    {
        printf("Neutral BDM speciation</model>\n");
    }
    else if (p.m == MODEL_BDM_SELECTION)
    {
        printf("BDM speciation with selection</model>\n");
    }
    printf("  <n>%d</n>\n", n_threads);
    printf("  <shape_metacom>%s</shape_metacom>\n", p.shape);
    printf("  <metacom_size>%d</metacom_size>\n", p.j_per_c * p.communities);
    printf("  <k_gen>%d</k_gen>\n", p.k_gen);
    printf("  <num_comm>%d</num_comm>\n", p.communities);
    printf("  <individuals_per_comm>%d</individuals_per_comm>\n", p.j_per_c);
    printf("  <initial_num_species>%d</initial_num_species>\n", p.init_species);
    printf("  <mutation_rate>%.2e</mutation_rate>\n", p.mu);
    printf("  <omega>%.2e</omega>\n", p.omega);
    if (p.m == MODEL_BDM_SELECTION)
    {
        printf("  <selection>%.2e</selection>\n", p.s);
    }
    if (p.shape[0] == 'r')
    {
        printf("  <radius>%.4f</radius>\n", p.r);
    }
    if (p.shape[0] == 'r' && p.shape[1] == 'e')
    {
        printf("  <width>%.4f</width>\n", p.w);
    }
    printf("  <filename>%s</filename>\n", p.ofilename);

    // The threads:
    pthread_t threads[n_threads];

    const time_t start = time(NULL);

    // Create the threads:
    for (int i = 0; i < n_threads; ++i)
    {
        pthread_create(&threads[i], NULL, sim, (void*)&p);
    }

    // Wait for the threads to end:
    for (int i = 0; i < n_threads; ++i)
    {
        pthread_join(threads[i], NULL);
    }

    const time_t end_t = time(NULL);
    printf("  <seconds>%lu</seconds>\n", (unsigned long)(end_t - start));
    printf("  <time>%s</time>\n", sec_to_string(end_t - start));
    printf("</origin_ssne>\n");
    return EXIT_SUCCESS; // yeppie !
}

///////////////////////////////////////////
// The simulation                        //
///////////////////////////////////////////
void *sim(void *parameters)
{
    const Params P = *((Params*)parameters);

    char *shape = P.shape;
    const int k_gen = P.k_gen;
    const int communities = P.communities;
    const int j_per_c = P.j_per_c;
    const int init_species = P.init_species;
    const int init_pop_size = j_per_c / init_species;
    const double omega = P.omega;
    const double mu = P.mu;
    const double s = P.s;
    const double radius = P.r;
    const double width = P.w;

    // GSL's Taus generator:
    gsl_rng *rng = gsl_rng_alloc(gsl_rng_taus2);
    // Initialize the GSL generator with /dev/urandom:
    const unsigned int seed = devurandom_get_uint();
    gsl_rng_set(rng, seed); // Seed with time
    printf("  <seed>%u</seed>\n", seed);
    // Used to name the output file:
    char *buffer = (char*)malloc(100);
    // Nme of the file:
    sprintf(buffer, "%s%u.xml", P.ofilename, seed);
    // Open the output file:
    FILE *restrict out = fopen(buffer, "w");
    // Store the total num. of species/1000 generations:
    int *restrict total_species = (int*)malloc(k_gen * sizeof(int));
    // Number of speciation events/1000 generations:
    int *restrict speciation_events = (int*)malloc(k_gen * sizeof(int));
    // Number of extinctions/1000 generations:
    int *restrict extinction_events = (int*)malloc(k_gen * sizeof(int));
    // Number of speciation events/vertex:
    int *restrict speciation_per_c = (int*)malloc(communities * sizeof(int));
    // Number of local extinction events/vertex:
    int *restrict extinction_per_c = (int*)malloc(communities * sizeof(int));
    // Store the lifespan of the extinct species:
    ivector lifespan;
    ivector_init0(&lifespan);
    // Store the population size at speciation event:
    ivector pop_size;
    ivector_init0(&pop_size);
    // (x, y) coordinates for the spatial graph:
    double *restrict x = (double*)malloc(communities * sizeof(double));
    double *restrict y = (double*)malloc(communities * sizeof(double));
    for (int i = 0; i < communities; ++i)
    {
        speciation_per_c[i] = 0;
        extinction_per_c[i] = 0;
    }
    // Initialize an empty list of species:
    species_list *restrict list = species_list_init();
    // Initialize the metacommunity and fill them with the initial species evenly:
    for (int i = 0; i < init_species; ++i)
    {
        // Intialize the species and add it to the list:
        species_list_add(list, species_init(communities, 0, 3));
    }
    // To iterate the list;
    slnode *it = list->head;
    // Fill the communities:
    while (it != NULL)
    {
        for (int i = 0; i < communities; ++i)
        {
            it->sp->n[i] = init_pop_size;
            it->sp->genotypes[0][i] = init_pop_size;
        }
        it = it->next;
    }

    // To iterate the list;
    const int remainder = j_per_c - (init_species * init_pop_size);
    for (int i = 0; i < communities; ++i)
    {
        it = list->head;
        for (int j = 0; j < remainder; ++j, it = it->next)
        {
            ++(it->sp->n[i]);
            ++(it->sp->genotypes[0][i]);
        }
    }

    // Test (will be removed for v2.0):
    int sum = 0;
    it = list->head;
    while (it != NULL)
    {
        sum += species_total(it->sp);
        it = it->next;
    }
    assert(sum == j_per_c * communities);

    // Create the metacommunity;
    graph g;
    switch(shape[0])
    {
    case 's':
        shape = "star";
        graph_get_star(&g, communities);
        break;
    case 'c':
        if (shape[1] == 'o')
        {
            shape = "complete";
            graph_get_complete(&g, communities);
            break;
        }
        else
        {
            shape = "circle";
            graph_get_circle(&g, communities);
            break;
        }
    case 'r':
        if (shape[1] == 'e')
        {
            shape = "rectangle";
            graph_get_rec_crgg(&g, communities, width, radius, x, y, rng);
            break;
        }
        else
        {
            shape = "random";
            graph_get_crgg(&g, communities, radius, x, y, rng);
            break;
        }
    default:
        shape = "random";
        graph_get_crgg(&g, communities, radius, x, y, rng);
    }
    // Setup the cumulative jagged array for migration:
    double **cumul = setup_cumulative_list(&g, omega);

    fprintf(out, "<?xml version=\"1.0\"?>\n");
    fprintf(out, "<simulation>\n");
    fprintf(out, "  <model>");
    if (P.m == MODEL_BDM_NEUTRAL)
    {
        fprintf(out, "Neutral BDM speciation</model>\n");
    }
    else if (P.m == MODEL_BDM_SELECTION)
    {
        fprintf(out, "BDM speciation with selection</model>\n");
    }
    fprintf(out, "  <seed>%u</seed>\n", seed);
    fprintf(out, "  <shape_metacom>%s</shape_metacom>\n", shape);
    fprintf(out, "  <metacom_size>%d</metacom_size>\n", j_per_c * communities);
    fprintf(out, "  <k_gen>%d</k_gen>\n", k_gen);
    fprintf(out, "  <num_comm>%d</num_comm>\n", communities);
    fprintf(out, "  <individuals_per_comm>%d</individuals_per_comm>\n", j_per_c);
    fprintf(out, "  <initial_num_species>%d</initial_num_species>\n", init_species);
    fprintf(out, "  <mutation_rate>%.2e</mutation_rate>\n", mu);
    fprintf(out, "  <omega>%.2e</omega>\n", omega);
    if (P.m == MODEL_BDM_SELECTION)
    {
        fprintf(out, "  <selection>%.2e</selection>\n", s);
    }
    if (shape[0] == 'r')
    {
        fprintf(out, "  <radius>%.4f</radius>\n", radius);
    }
    if (shape[0] == 'r' && shape[1] == 'e')
    {
        fprintf(out, "  <width>%.4f</width>\n", width);
    }
    // To select the species and genotypes to pick and replace:
    slnode *s0 = list->head; // species0
    slnode *s1 = list->head; // species1
    int g0 = 0;
    int g1 = 0;
    int v1 = 0; // Vertex of the individual 1

    /////////////////////////////////////////////
    // Groups of 1 000 generations             //
    /////////////////////////////////////////////
    for (int k = 0; k < k_gen; ++k)
    {
        extinction_events[k] = 0;
        speciation_events[k] = 0;

        /////////////////////////////////////////////
        // 1 000 generations                       //
        /////////////////////////////////////////////
        for (int gen = 0; gen < 1000; ++gen)
        {
            const int current_date = (k * 1000) + gen;
            /////////////////////////////////////////////
            // A single generation                     //
            /////////////////////////////////////////////
            for (int t = 0; t < j_per_c; ++t)
            {
                /////////////////////////////////////////////
                // A single time step (for each community) //
                /////////////////////////////////////////////
                for (int c = 0; c < communities; ++c)
                {
                    // Select the species and genotype of the individual to be replaced
                    int position = (int)(gsl_rng_uniform(rng) * j_per_c);
                    s0 = list->head;
                    int index = s0->sp->n[c];
                    while (index <= position)
                    {
                        s0 = s0->next;
                        index += s0->sp->n[c];
                    }
                    position = (int)(gsl_rng_uniform(rng) * s0->sp->n[c]);
                    if (position < s0->sp->genotypes[0][c])
                    {
                        g0 = 0;
                    }
                    else if (position < (s0->sp->genotypes[0][c] + s0->sp->genotypes[1][c]))
                    {
                        g0 = 1;
                    }
                    else
                    {
                        g0 = 2;
                    }
                    // Choose the vertex for the individual
                    const double r_v1 = gsl_rng_uniform(rng);
                    v1 = 0;
                    while (r_v1 > cumul[c][v1])
                    {
                        ++v1;
                    }
                    v1 = g.adj_list[c][v1];
                    // species of the new individual
                    position = (int)(gsl_rng_uniform(rng) * j_per_c);
                    s1 = list->head;
                    index = s1->sp->n[v1];
                    while (index <= position)
                    {
                        s1 = s1->next;
                        index += s1->sp->n[v1];
                    }
                    if (v1 == c) // local remplacement
                    {
                        const double r = gsl_rng_uniform(rng);
                        const int aa = s1->sp->genotypes[0][v1];
                        const int Ab = s1->sp->genotypes[1][v1];
                        const int AB = s1->sp->genotypes[2][v1];

                        // The total fitness of the population 'W':
                        const double w = aa + Ab * (1.0 + s) + AB * (1.0 + s) * (1.0 + s);

                        if (r < aa / w)
                        {
                            g1 = gsl_rng_uniform(rng) < mu ? 1 : 0;
                        }
                        else
                        {
                            if (AB == 0 || r < (aa + Ab * (1.0 + s)) / w)
                            {
                                g1 = gsl_rng_uniform(rng) < mu ? 2 : 1;
                            }
                            else
                            {
                                g1 = 2;
                            }
                        }
                    }
                    else
                    { // Migration event
                        g1 = 0;
                    }
                    // Apply the changes
                    s0->sp->n[c]--;
                    s0->sp->genotypes[g0][c]--;
                    s1->sp->n[c]++;
                    s1->sp->genotypes[g1][c]++;

                    ////////////////////////////////////////////
                    // Check for local extinction             //
                    ////////////////////////////////////////////
                    if (s0->sp->n[c] == 0)
                    {
                        extinction_per_c[c]++;
                    }
                    ////////////////////////////////////////////
                    // Check for speciation                   //
                    ////////////////////////////////////////////
                    else if (s0->sp->genotypes[2][c] > 0 && s0->sp->genotypes[0][c] == 0 && s0->sp->genotypes[1][c] == 0)
                    {
                        species_list_add(list, species_init(communities, current_date, 3)); // Add the new species

                        const int pop = s0->sp->n[c];
                        list->tail->sp->n[c] = pop;
                        list->tail->sp->genotypes[0][c] = pop;
                        s0->sp->n[c] = 0;
                        s0->sp->genotypes[2][c] = 0;

                        // To keep info on patterns of speciation...
                        ivector_add(&pop_size, pop);
                        ++speciation_events[k];
                        ++speciation_per_c[c];
                    }

                } // End 'c'

            } // End 't'

            // Remove extinct species from the list and store the number of extinctions.
            extinction_events[k] += species_list_rmv_extinct2(list, &lifespan, current_date);

        } // End 'g'

        total_species[k] = list->size;

    } // End 'k'

    //////////////////////////////////////////////////
    // PRINT THE FINAL RESULTS                      //
    //////////////////////////////////////////////////
    fprintf(out, "  <global>\n");
    fprintf(out, "    <proper_edges>%d</proper_edges>\n", graph_edges(&g));
    fprintf(out, "    <links_per_c>%.4f</links_per_c>\n", (double)graph_edges(&g) / communities);

    fprintf(out, "    <avr_lifespan>%.4f</avr_lifespan>\n", imean(lifespan.array, lifespan.size));
    fprintf(out, "    <median_lifespan>%.4f</median_lifespan>\n", imedian(lifespan.array, lifespan.size));

    fprintf(out, "    <avr_pop_size_speciation>%.4f</avr_pop_size_speciation>\n", imean(pop_size.array, pop_size.size));
    fprintf(out, "    <median_pop_size_speciation>%.4f</median_pop_size_speciation>\n", imedian(pop_size.array, pop_size.size));

    fprintf(out, "    <speciation_per_k_gen>");
    int i = 0;
    for (; i < k_gen - 1; ++i)
    {
        fprintf(out, "%d ", speciation_events[i]);
    }
    fprintf(out, "%d</speciation_per_k_gen>\n", speciation_events[i]);

    fprintf(out, "    <extinctions_per_k_gen>");
    for (i = 0; i < k_gen - 1; ++i)
    {
        fprintf(out, "%d ", extinction_events[i]);
    }
    fprintf(out, "%d</extinctions_per_k_gen>\n", extinction_events[i]);

    fprintf(out, "    <extant_species_per_k_gen>");
    for (i = 0; i < k_gen - 1; ++i)
    {
        fprintf(out, "%d ", total_species[i]);
    }
    fprintf(out, "%d</extant_species_per_k_gen>\n", total_species[i]);

    // Print global distribution
    fprintf(out, "    <species_distribution>");
    ivector species_distribution;
    ivector_init1(&species_distribution, 128);
    it = list->head;
    while (it != NULL)
    {
        ivector_add(&species_distribution, species_total(it->sp));
        it = it->next;
    }
    ivector_sort_asc(&species_distribution);
    ivector_print(&species_distribution, out);
    fprintf(out, "</species_distribution>\n");

    double *octaves;
    int oct_num = biodiversity_octaves(species_distribution.array, species_distribution.size, &octaves);
    fprintf(out, "    <octaves>");
    for (i = 0; i < oct_num; ++i)
    {
        fprintf(out, "%.2f ", octaves[i]);
    }
    fprintf(out, "%.2f</octaves>\n", octaves[i]);
    fprintf(out, "  </global>\n");

    // Print info on all vertices
    double *restrict ric_per_c = (double*)malloc(communities * sizeof(double));
    for (int c = 0; c < communities; ++c)
    {
        fprintf(out, "  <vertex>\n");
        fprintf(out, "    <id>%d</id>\n", c);
        if (shape[0] == 'r')
        {
            fprintf(out, "    <xcoor>%.4f</xcoor>\n", x[c]);
            fprintf(out, "    <ycoor>%.4f</ycoor>\n", y[c]);
        }
        fprintf(out, "    <degree>%d</degree>\n", g.num_e[c] + 1);
        fprintf(out, "    <speciation_events>%d</speciation_events>\n", speciation_per_c[c]);
        fprintf(out, "    <extinction_events>%d</extinction_events>\n", extinction_per_c[c]);

        int vertex_richess = 0;
        ivector_rmvall(&species_distribution);
        it = list->head;
        while (it != NULL)
        {
            ivector_add(&species_distribution, it->sp->n[c]);
            it = it->next;
        }
        // Sort the species distribution and remove the 0s
        ivector_sort_asc(&species_distribution);
        ivector_trim_small(&species_distribution, 1);

        ric_per_c[c] = (double)species_distribution.size;
        fprintf(out, "    <species_richess>%d</species_richess>\n", species_distribution.size);
        fprintf(out, "    <species_distribution>");
        ivector_print(&species_distribution, out);
        fprintf(out, "</species_distribution>\n");

        // Print octaves
        free(octaves);
        oct_num = biodiversity_octaves(species_distribution.array, species_distribution.size, &octaves);
        fprintf(out, "    <octaves>");
        for (i = 0; i < oct_num - 1; ++i)
        {
            fprintf(out, "%.2f ", octaves[i]);
        }
        fprintf(out, "%.2f</octaves>\n", octaves[i]);
        fprintf(out, "  </vertex>\n");
    }

    fprintf(out, "</simulation>\n");

    // GraphML output:
    sprintf(buffer, "%s%u.graphml", P.ofilename, seed);
    FILE *outgml = fopen(buffer, "w");
    graph_graphml(&g, outgml, seed);

    // Print to SVG files.
    sprintf(buffer, "%s%u.svg", P.ofilename, seed);
    FILE *outsvg = fopen(buffer, "w");
    graph_svg(&g, x, y, 400.0, 20.0, outsvg);

    sprintf(buffer, "%s%u-speciation.svg", P.ofilename, seed);
    FILE *outsvgspe = fopen(buffer, "w");
    double *spe_per_c = (double*)malloc(communities * sizeof(double));
    for (int c = 0; c < communities; ++c)
    {
        spe_per_c[c] = (double)speciation_per_c[c];
    }
    scale_0_1(spe_per_c, communities);
    graph_svg_abun(&g, x, y, 400.0, 20.0, spe_per_c, 2, outsvgspe);

    sprintf(buffer, "%s%u-richness.svg", P.ofilename, seed);
    FILE *outsvgric = fopen(buffer, "w");
    scale_0_1(ric_per_c, communities);
    graph_svg_abun(&g, x, y, 400.0, 20.0, ric_per_c, 1, outsvgric);

    //////////////////////////////////////////////////
    // EPILOGUE...                                  //
    //////////////////////////////////////////////////
    // Close files;
    fclose(out);
    fclose(outgml);
    fclose(outsvg);
    fclose(outsvgspe);
    fclose(outsvgric);
    // Free arrays;
    free(x);
    free(y);
    free(ric_per_c);
    free(spe_per_c);
    free(buffer);
    free(total_species);
    free(octaves);
    free(speciation_per_c);
    free(extinction_per_c);
    free(speciation_events);
    free(extinction_events);
    // Free structs;
    species_list_free(list);
    ivector_free(&species_distribution);
    ivector_free(&lifespan);
    ivector_free(&pop_size);
    graph_free(&g);
    gsl_rng_free(rng);

    return NULL;
}

double **setup_cumulative_list(const graph *g, double omega)
{
    const int num_v = g->num_v;
    double **cumul = (double**)malloc(num_v * sizeof(double*));
    for (int i = 0; i < num_v; ++i)
    {
        cumul[i] = (double*)malloc(g->num_e[i] * sizeof(double));
    }
    for (int i = 0; i < num_v; ++i)
    {
        for (int j = 0; j < g->num_e[i]; ++j)
        {
            cumul[i][j] = g->w_list[i][j];
        }
    }
    for (int i = 0; i < num_v; ++i)
    {
        for (int j = 0; j < g->num_e[i]; ++j)
        {
            if (i != g->adj_list[i][j])
            {
                cumul[i][j] = omega;
            }
            else
            {
                cumul[i][j] = 1.0;
            }
        }
    }
    for (int i = 0; i < num_v; ++i)
    {
        double sum = 0.0;
        const int num_e = g->num_e[i];
        for (int j = 0; j < num_e; ++j)
        {
            sum += cumul[i][j];
        }
        for (int j = 0; j < num_e; ++j)
        {
            cumul[i][j] /= sum;
        }
    }
    for (int i = 0; i < num_v; ++i)
    {
        const int num_e = g->num_e[i];
        for (int j = 1; j < num_e - 1; ++j)
        {
            cumul[i][j] += cumul[i][j - 1];
        }
        cumul[i][num_e - 1] = 1.0;
    }
    /*
    graph_print(g, stdout);
    for (int i = 0; i < num_v; ++i)
    {
        const int num_e = g->num_e[i];
        for (int j = 0; j < num_e; ++j)
        {
            printf("%.4f   ", cumul[i][j]);
        }
        printf("\n");
    }
    */
    return cumul;
}


//! @file
//! Various functions used during experiences (dgemmsy)
//!
//! @author
//!    Copyright (C) 2009-2011 BigDFT group
//!    This file is distributed under the terms of the
//!    GNU General Public License, see ~/COPYING file
//!    or http://www.gnu.org/copyleft/gpl.txt .
//!    For the list of contributors, see ~/AUTHORS
//! Eric Bainville, Mar 2010

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include <unistd.h>
#include <xmmintrin.h>

// Set to 1 to use BLAS axpy call to accumulate partial results.
#ifndef USE_AXPY
#define USE_AXPY 1
/*#define ACML_BLAS 1*/
#endif

#if USE_AXPY
#if defined(MKL_BLAS) || defined(WIN32)
#include <mkl_cblas.h>
#else
#if defined(ACML_BLAS)
#include <acml.h>
#define cblas_daxpy daxpy
#else
#include <cblas.h>
#endif
#endif
#endif

#include "utils.h"

// System-specific functions

#include <unistd.h>
void * allocPages(size_t sz)
{
  size_t page_size = sysconf(_SC_PAGESIZE);
  size_t extra = sz % page_size;
  if (extra != 0) sz = (sz - extra) + page_size; // round to next page
  void * address = 0;
  int status = posix_memalign(&address,page_size,sz);
  if (status != 0) return 0; // Failed
  return address;
}
void freePages(void * address)
{
  free(address);
}
double * allocDoubles(int n)
{
  void * address = 0;
  int status = posix_memalign(&address,16,n*sizeof(double));
  if (status != 0) return 0; // Failed
  return address;
}
float * allocFloats(int n)
{
  void * address = 0;
  int status = posix_memalign(&address,16,n*sizeof(float));
  if (status != 0) return 0; // Failed
  return address;
}
void freeDoubles(double * x)
{
  free(x);
}
void freeFloats(float * x)
{
  free(x);
}

// Reference implementations

void gemm_block_2x2_ref(const double * a,const double * b,long long int n,double * y,long long int ldy)
{
  int k;
  for (k=0;k<n;k+=2)
  {
    y[0] += a[2*k] * b[4*k];
    y[0] += a[2*k+1] * b[4*k+1];
    y[1] += a[2*k] * b[4*k+2];
    y[1] += a[2*k+1] * b[4*k+3];
    y[ldy] += a[2*k+2] * b[4*k];
    y[ldy] += a[2*k+3] * b[4*k+1];
    y[ldy+1] += a[2*k+2] * b[4*k+2];
    y[ldy+1] += a[2*k+3] * b[4*k+3];
  }
}

void gemm_block_2x4_ref(const double * a,const double * b,long long int n,double * y,long long int ldy)
{
  int k;
  double * work;
  for (k=0;k<n;k+=2)
  {
    work = y;
    work[0] += a[2*k]   * b[4*k];
    work[0] += a[2*k+1] * b[4*k+1];
    work += ldy;
    work[0] += a[2*k]   * b[4*k+2];
    work[0] += a[2*k+1] * b[4*k+3];
    work += ldy;
    work[0] += a[2*k]   * b[4*k+4];
    work[0] += a[2*k+1] * b[4*k+5];
    work += ldy;
    work[0] += a[2*k]   * b[4*k+6];
    work[0] += a[2*k+1] * b[4*k+7];

    work = y + 1;
    work[0] += a[2*k+2] * b[4*k];
    work[0] += a[2*k+3] * b[4*k+1];
    work += ldy;
    work[0] += a[2*k+2] * b[4*k+2];
    work[0] += a[2*k+3] * b[4*k+3];
    work += ldy;
    work[0] += a[2*k+2] * b[4*k+4];
    work[0] += a[2*k+3] * b[4*k+5];
    work += ldy;
    work[0] += a[2*k+2] * b[4*k+6];
    work[0] += a[2*k+3] * b[4*k+7];
  }
}

int dgemmsy_base(dgemmsyBaseArgs * args)
{
  int status = 0; // return value
  int row,col;
  size_t slice_size,ywork_size;
  int p2; // P rounded to next multiple of 4
  double *a_slice,*b_slice,*y_work;
  ComputeData cdata;
  TransposeData tdata;
  int slice_n,n,p;
  const double *a,*b;
  double *y;
  int lda,ldb,ldy;
  int transa,transb;
  BlockPattern_2x4_Proc pattern;
  void * pattern_arg;
  double alpha;

  if (args == 0) return -2;

  pattern = args->params.pattern;
  pattern_arg = args->params.pattern_arg;
  slice_n = args->params.slice_n;

  transa = args->transa;
  transb = args->transb;
  n = args->n;
  p = args->p;
  a = args->a;
  lda = args->lda;
  b = args->b;
  ldb = args->ldb;
  y = args->y;
  ldy = args->ldy;
  alpha = args->alpha;

  // Check dimensions
  if (slice_n < 8) return -1;
  if ( n <= 0 || p <= 0 ) return -1;
  p2 = (p+3) & 0x7FFFFFFC;

  // printf("N=%d P=%d LDA=%d LDB=%d LDY=%d  P2=%d\n",n,p,lda,ldb,ldy,p2);

  // Check other arguments
  if (a == 0 || b == 0 || y == 0 || pattern == 0) return -5;

  // Allocate memory
  slice_size = slice_n * p2 * sizeof(double); // Slice size in bytes
  ywork_size = p2 * p2 * sizeof(double); // Result size in bytes
  a_slice = (double *)allocPages(slice_size);
  b_slice = (double *)allocPages(slice_size);
  y_work = (double *)allocPages(ywork_size);
  if (a_slice == 0 || b_slice == 0 || y_work == 0) { status = -3; goto END; }
  memset(y_work,0,ywork_size);

  // Loop on all slices and accumulate products
  initComputeData(&cdata,p2,slice_n,a_slice,b_slice,y_work);
  for (row=0;row<n;row+=slice_n)
  {
    int s = slice_n;
    if (row+s > n) s = n-row; // Limit size of last slice if needed

    // 2-pack A slice
    if (transa) tpack_2(s,p,a+lda*row,lda, slice_n,p2,a_slice);
    else npack_2(s,p,a+row,lda, slice_n,p2,a_slice);

    // 4-pack B slice
    if (transb) tpack_4(s,p,b+ldb*row,ldb, slice_n,p2,b_slice);
    else npack_4(s,p,b+row,ldb, slice_n,p2,b_slice);

    pattern(pattern_arg,p2,Compute_visitor,&cdata);
  }
  cleanupComputeData(&cdata);

  // Complete result by symmetry
  initTransposeData(&tdata,p2,y_work);
  pattern(pattern_arg,p2,Transpose_visitor,&tdata);
  cleanupTransposeData(&tdata);

  // Combine and store (untransposed) result. If we are multithreading,
  // we must protect the update with a mutex, since all threads
  // will update the same Y.
  if (args->yMutex != 0)
    {
      int locked = pthread_mutex_lock(args->yMutex);
      if (locked != 0) status = -4;
    }
  for (col=0;col<p;col++)
    {
      double * yy = y+ldy*col;
      double * yy_work = y_work+p2*col;
#if USE_AXPY
      cblas_daxpy(p,alpha,yy_work,1,yy,1);
#else
      if (alpha == 1)
	{
	  for (row=0;row<p;row++) yy[row] += yy_work[row];
	}
      else
	{
	  for (row=0;row<p;row++) yy[row] += alpha * yy_work[row];
	}
#endif
    }
  if (args->yMutex != 0)
    {
      int unlocked = pthread_mutex_unlock(args->yMutex);
      if (unlocked != 0) status = -4;
    }

END:
  // Cleanup
  freePages(y_work);
  freePages(a_slice);
  freePages(b_slice);
  return status;
}

void npack_2(int n,int p,const double * a,int lda,int s,int q,double * slice)
{
  int row,col,icol;
  double * x = 0;
  int oddn = 0;
  if (n&1) { n--; oddn=1; }
  for (col=0;col<q;col++)
    {
      icol = col&1;
      x = slice + s*(col-icol) + (icol<<1);
      row = 0;
      if (col < p)
	{
	  for ( ;row<n;row+=2)
	    {
	      x[2*row] = a[row];
	      x[2*row+1] = a[row+1];
	    }
	  if (oddn) { x[2*row] = a[row]; x[2*row+1] = 0; row+=2; }
	}
      for ( ;row<s;row+=2)
	{
	  x[2*row] = 0;
	  x[2*row+1] = 0;
	}
      a += lda;
    }
}

void npack_4(int n,int p,const double * a,int lda,int s,int q,double * slice)
{
  int row,col,icol;
  double * x = 0;
  int oddn = 0;
  if (n&1) { n--; oddn=1; }
  for (col=0;col<q;col++)
    {
      icol = col&3;
      x = slice + s*(col-icol) + (icol<<1);
      row = 0;
      if (col < p)
	{
	  for ( ;row<n;row+=2)
	    {
	      x[4*row] = a[row];
	      x[4*row+1] = a[row+1];
	    }
	  if (oddn) { x[4*row] = a[row]; x[4*row+1] = 0; row+=2; }
	}
      for ( ;row<s;row+=2)
	{
	  x[4*row] = 0;
	  x[4*row+1] = 0;
	}
      a += lda;
    }
}

void tpack_2(int n,int p,const double * a,int lda,int s,int q,double * slice)
{
  int row,col,icol;
  double * x = 0;
  const double * aa;
  int da = lda<<1;
  int oddn = 0;
  if (n&1) { n--; oddn=1; }
  for (col=0;col<q;col++)
    {
      icol = col&1;
      x = slice + s*(col-icol) + (icol<<1);
      row = 0;
      if (col < p)
	{
	  aa = a;
	  for ( ;row<n;row+=2)
	    {
	      x[2*row] = aa[0];
	      x[2*row+1] = aa[lda];
	      aa += da;
	    }
	  if (oddn) { x[2*row] = aa[0]; x[2*row+1] = 0; row += 2; }
	}
      for ( ;row<s;row+=2)
	{
	  x[2*row] = 0;
	  x[2*row+1] = 0;
	}
      a++;
    }
}

void tpack_4(int n,int p,const double * a,int lda,int s,int q,double * slice)
{
  int row,col,icol;
  double * x = 0;
  const double * aa;
  int da = lda<<1;
  int oddn = 0;
  if (n&1) { n--; oddn=1; }
  for (col=0;col<q;col++)
    {
      icol = col&3;
      x = slice + s*(col-icol) + (icol<<1);
      row = 0;
      if (col < p)
	{
	  aa = a;
	  for ( ;row<n;row+=2)
	    {
	      x[4*row] = aa[0];
	      x[4*row+1] = aa[lda];
	      aa += da;
	    }
	  if (oddn) { x[4*row] = aa[0]; x[4*row+1] = 0; row += 2; }
	}
      for ( ;row<s;row+=2)
	{
	  x[4*row] = 0;
	  x[4*row+1] = 0;
	}
      a++;
    }
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// 			     //////////////////////////////////////////////////////////
//  			     //							     //
// 			     //   	        hybridMANTIS v1.0		     //
// 			     //                fastDETECT2 - C code                  //
//			     //		   (optical photons transport)		     //
//  			     //							     //
//                           //               used for Load Balancing                //
//			     //							     //
//			     //////////////////////////////////////////////////////////
//
//
//
//
// ****Disclaimer****
//  This software and documentation (the "Software") were developed at the Food and Drug Administration (FDA) by employees of the Federal Government in
//  the course of their official duties. Pursuant to Title 17, Section 105 of the United States Code, this work is not subject to copyright protection
//  and is in the public domain. Permission is hereby granted, free of charge, to any person obtaining a copy of the Software, to deal in the Software
//  without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, or sell copies of the
//  Software or derivatives, and to permit persons to whom the Software is furnished to do so. FDA assumes no responsibility whatsoever for use by other
//  parties of the Software, its source code, documentation or compiled executables, and makes no guarantees, expressed or implied, about its quality,
//  reliability, or any other characteristic. Further, use of this code in no way implies endorsement by the FDA or confers any advantage in regulatory
//  decisions. Although this software can be redistributed and/or modified freely, we ask that any derivative works bear some notice that they are
//  derived from it, and any modified versions bear some notice that they have been modified.
//
//	Detailed comments are available in "hybridMANTIS_cuda_ver1_0.cu" and "hybridMANTIS_c_ver1_0.c" files.
//
//	Associated publication: Sharma Diksha, Badal Andreu and Badano Aldo, "hybridMANTIS: a CPU-GPU Monte Carlo method for modeling indirect x-ray detectors with
//				columnar scintillators". Physics in Medicine and Biology, 57(8), pp. 2357–2372 (2012)
//
//
//	File:   	hybridMANTIS_c_ver1_0_LB.c
//	Author: 	Diksha Sharma (US Food and Drug Administration)
//	Email: 		diksha.sharma@fda.hhs.gov
//	Last updated:  	Apr 13, 2012
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////
//
//      Header libraries
//
/////////////////////////////////////////

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

/////////////////////////////////////////
//
//      Global variables
//
/////////////////////////////////////////

#define max_photon_per_EDE 900000	// maximum number of optical photons that can be generated per energy deposition event (EDE)

#ifndef USING_CUDA
	#define mybufsizeT 2304000	// CPU buffer size: # of events sent to the CPU
#endif

/////////////////////////////////////////
//
//      Include kernel program
//
/////////////////////////////////////////
#include "kernel_cuda_c_ver1_0_LB.cu"

////////////////////////////////////////////////////////////////////////////
//				MAIN PROGRAM			          //
////////////////////////////////////////////////////////////////////////////

#ifndef USING_CUDA

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// cpuoptlb():   Performs optical transport for finding optimal load using load balancing in the CPU
//	  	 Input arguments: cptime, cpubufsize
//
//		 cptime: 	time taken by GPU to call this routine
//		 cpubufsize:  	CPU buffer size defined by user in PENELOPE tally code. Only to be used for load balancing. This is different from 'mybufsize'.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	void cpuoptlb_(double *cptime, int *cpubufsize)
	{

		float dcos[3]={0}; 		// directional cosines
		float normal[3]={0}; 		// normal to surface in case of TIR
		float pos[3] = {0}; 		// new position
		float old_pos[3] = {0};  	// source coordinates

		// command line arguments
		float xdetector, ydetector, radius, height, n_C, n_IC, top_absfrac, bulk_abscoeff, beta, d_min, lbound_x, lbound_y, ubound_x, ubound_y, d_max, yield, sensorRefl;
		int pixelsize, num_primary, min_optphotons, max_optphotons, num_bins;

		int nbytes = (*cpubufsize)*sizeof(struct start_info);
		struct start_info *structa;
		structa = (struct start_info*) malloc(nbytes);
		if( structa == NULL )
			printf("\n Struct start_info array CANNOT BE ALLOCATED - %d !!", (*cpubufsize));

		// get cpu time
		clock_t start, end;
		double num_sec;

		// get current time stamp to initialize seed input for RNG
		time_t seconds;
		seconds = time (NULL);
		struct timeval tv;
		gettimeofday(&tv,NULL);

		float rr=0.0f, theta=0.0f;
		float r=0.0f;		// random number
		float norm=0.0f;
		int jj=0;

		// initialize random number generator (RANECU)
		int seed_input = 271828182 ; // ranecu seed input
		int seed[2];

		// gsl variables
		const gsl_rng_type * Tgsl;
		gsl_rng * rgsl;
		double mu_gsl;
		int my_index=0;
		int result_algo = 0;
		unsigned long long int *num_rebound;

		// output image variables
		int xdim = 0;
		int ydim = 0;
		int indexi=0, indexj=0;

	      	// create a generator chosen by the environment variable GSL_RNG_TYPE
	       	gsl_rng_env_setup();
	       	Tgsl = gsl_rng_default;
	       	rgsl = gsl_rng_alloc (Tgsl);

 		// copy to local variables from PENELOPE buffers
		xdetector = inputargs_.detx;		// x dimension of detector (in um). x in (0,xdetector)
		ydetector = inputargs_.dety;		// y dimension of detector (in um). y in (0,ydetector)
		height = inputargs_.detheight;		// height of column and thickness of detector (in um). z in range (-H/2, H/2)
		radius = inputargs_.detradius;		// radius of column (in um).
		n_C = inputargs_.detnC;			// refractive index of columns
		n_IC = inputargs_.detnIC;		// refractive index of intercolumnar material
		top_absfrac = inputargs_.dettop;	// column's top surface absorption fraction (0.0, 0.5, 0.98)
		bulk_abscoeff = inputargs_.detbulk;	// column's bulk absorption coefficient (in um^-1) (0.001, 0.1 cm^-1)
		beta = inputargs_.detbeta;		// roughness coefficient of column walls
		d_min = inputargs_.detdmin;		// minimum distance a photon can travel when transmitted from a column
		d_max = inputargs_.detdmax;
		lbound_x = inputargs_.detlboundx;	// x lower bound of region of interest of output image (in um)
		lbound_y = inputargs_.detlboundy;	// y lower bound (in um)
		ubound_x = inputargs_.detuboundx;	// x upper bound (in um)
		ubound_y = inputargs_.detuboundy;	// y upper bound (in um)
		yield = inputargs_.detyield;		// yield (/eV)
		pixelsize = inputargs_.detpixel;	// 1 pixel = pixelsize microns (in um)
		sensorRefl = inputargs_.detsensorRefl;	// Non-Ideal sensor reflectivity (%)
		num_primary = inputargs_.mynumhist;	// total number of primaries to be simulated
		min_optphotons = inputargs_.minphotons;	// minimum number of optical photons detected to be included in PHS
		max_optphotons = inputargs_.maxphotons;	// maximum number of optical photons detected to be included in PHS
		num_bins = inputargs_.mynumbins;	// number of bins for genrating PHS

		// dimensions of PRF image
		xdim = ceil((ubound_x - lbound_x)/pixelsize);
		ydim = ceil((ubound_y - lbound_y)/pixelsize);
		unsigned long long int myimage[xdim][ydim];

		// memory for storing histogram of # photons detected/primary
		int *h_num_detected_prim = 0;
		h_num_detected_prim = (int*)malloc(sizeof(int)*num_primary);

		for(indexj=0; indexj < num_primary; indexj++)
		  h_num_detected_prim[indexj] = 0;

		// start the clock
		start = clock();

	for(my_index = 0; my_index < (*cpubufsize); my_index++)		// iterate over x-rays
	{

		// reset the global counters
		num_generatedT = 0;
		num_detectT=0;
		num_abs_topT=0;
		num_abs_bulkT=0;
		num_lostT=0;
		num_outofcolT=0;
		num_theta1T=0;
		photon_distanceT=0.0f;

		// copying fortran buffer into *structa

		// units in the penelope output file are in cm. Convert to microns.
		structa[my_index].str_x = optical_.xbufopt[my_index] * 10000.0f;	// x-coordinate of interaction event.
		structa[my_index].str_y = optical_.ybufopt[my_index] * 10000.0f;	// y-coordinate
		structa[my_index].str_z = optical_.zbufopt[my_index] * 10000.0f;	// z-coordinate
		structa[my_index].str_E = optical_.debufopt[my_index];			// energy deposited
		structa[my_index].str_histnum = optical_.nbufopt[my_index];		// x-ray history number

		// sample # optical photons based on light yield and energy deposited for this interaction event (using Poisson distribution)
		mu_gsl = (double)structa[my_index].str_E * yield;
		structa[my_index].str_N = gsl_ran_poisson(rgsl,mu_gsl);

		if(structa[my_index].str_N > max_photon_per_EDE)
		{
			printf("\n\n CPU str_n exceeds max photons. program is exiting - %d !! \n\n",structa[my_index].str_N);
			exit(0);
		}

		num_rebound = (unsigned long long int*) malloc(structa[my_index].str_N*sizeof(unsigned long long int));
		if(num_rebound == NULL)
			printf("\n Error allocating num_rebound memory !\n");

		// initialize the RANECU generator in a position far away from the previous history:
		seed_input = (int)(seconds/3600+tv.tv_usec);			// seed input=seconds passed since 1970+current time in micro secs
		init_PRNG(my_index, 50000, seed_input, seed);      		// intialize RNG

		for(jj=0; jj<structa[my_index].str_N; jj++)
			num_rebound[jj] = 0;

		// reset the vectors
		dcos[0]=0.0f; dcos[1]=0.0f; dcos[2]=0.0f;
		normal[0]=0.0f; normal[1]=0.0f; normal[2]=0.0f;

		// set starting location of photon
		pos[0] = structa[my_index].str_x; pos[1] = structa[my_index].str_y; pos[2] = structa[my_index].str_z;
		old_pos[0] = structa[my_index].str_x; old_pos[1] = structa[my_index].str_y; old_pos[2] = structa[my_index].str_z;

		// initializing the direction cosines for the first particle in each core
		r = (ranecu(seed) * 2.0f) - 1.0f; // random number between (-1,1)

		while(fabs(r) <= 0.01f)
		 {
		   	r = (ranecu(seed) * 2.0f) - 1.0f;
		 }

		dcos[2] = r;		// random number between (-1,1)
		rr = sqrt(1.0f-r*r);
		theta=ranecu(seed)*twopipen;
		dcos[0]=rr*cos(theta);
		dcos[1]=rr*sin(theta);

		norm = sqrt(dcos[0]*dcos[0] + dcos[1]*dcos[1] + dcos[2]*dcos[2]);

		if ((norm < (1.0f - epsilon)) || (norm > (1.0f + epsilon)))	// normalize
		 {
			dcos[0] = dcos[0]/norm;
			dcos[1] = dcos[1]/norm;
			dcos[2] = dcos[2]/norm;
		 }

		local_counterT=0;
		while(local_counterT < structa[my_index].str_N)
		 {

			absorbedT = 0;
			detectT = 0;
			bulk_absT = 0;

			// set starting location of photon
			pos[0] = structa[my_index].str_x; pos[1] = structa[my_index].str_y; pos[2] = structa[my_index].str_z;
			old_pos[0] = structa[my_index].str_x; old_pos[1] = structa[my_index].str_y; old_pos[2] = structa[my_index].str_z;
			num_generatedT++;
			result_algo = 0;

			while(result_algo == 0)
			 {
			  	result_algo = algoT(normal, old_pos, pos, dcos, num_rebound, seed, structa[my_index], &myimage[0][0], xdetector, ydetector, radius, height, n_C, n_IC, top_absfrac, bulk_abscoeff, beta, d_min, pixelsize, lbound_x, lbound_y, ubound_x, ubound_y, sensorRefl, d_max, ydim, h_num_detected_prim);
			 }

		 }

		// release resources
		free(num_rebound);

	}	// my_index loop ends

	// end the clock
	end = clock();

	num_sec = (double)((end - start)/CLOCKS_PER_SEC);
        *cptime = num_sec;

	// release resources
	free(structa);
	free(h_num_detected_prim);

		return;
	}	// C main() ends

#endif


# ifndef LANGIL_H
# define LANGIL_H

///////////////////////////////////////////////////////
//////   GILLESPIE CLASS
//////
//////						R. Perez-Carrasco
//////                               Created: 16 Nov '13
///////////////////////////////////////////////////////
/* Class for simulating master equation through langil algorythm. It defines three classes:
		- langil: In charge of the integration
		- species: contains a name a number and methods to change number
		- reaction: contains a name, a stoiciometry associated and a pointer to a propensity computing function
*/

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <string>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <vector>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <iomanip>
//#include <omp.h>

#include "species.h"
#include "cell_cycle.h"

#define VERBOSE 1
#define NON_VERBOSE 0

#define TIMESTEPMULT 3
#define TIMESTEP 0
#define ALL 1
#define NOWRITE 2
#define LASTPOINT 4

#define GILLESPIE 0
#define MACROSCOPIC 2
#define CLEEULER 1
#define CLEMILSTEIN 3

#define ADIABATIC true
#define NONADIABATIC false

#define GEOMETRIC true
#define NONGEOMETRIC false

#define DETERMINISTIC_TIME true
#define STOCHASTIC_TIME false

#define EXPONENTIAL_REACTION 0
#define GEOMETRIC_REACTION 1
#define DETERMINISTIC_REACTION 2
#define ADIABATIC_REACTION 3

using namespace std;

struct StructDivisionState{
	double time;
	double species0before;
	double species1before;
	double species0after;
	double species1after;
};

class reaction; // defined after langil
class species; // defined after reaction
class expressionzone; // defined in a separate file

class langil{

	friend class expressionzone;

//	friend class reaction; // reaction have access to the state of the system

	public:

		langil(string outputfilename, double vol=100, int seed=-1);
		void SetState(string aname, float anumber); //Set Initial conditions
		double GetState(string a); // get the state of the selected species
		void SetTime(double time); //Set current time
		void AddSpecies(string aname, float anum); // Add a new molecular species
		void AddSpeciesTimeTracking(string aname, float anum, double lifetime); //  Add a molecular species with a timer
		void AddCellCycleSpecies(); // add a cell cycle to the system
		void addCellPhase(double duration, int type_phase); // add a cell phase to the cell cycle
		void PrintSummarySpecies();
		string StateString();
		void Add_Reaction(string name,vector<int> stoich,double (*prop_f)(v_species&),int typeofrec = EXPONENTIAL_REACTION); // Add a reaction to the system overloaded to create directly
		void UpdateTime(); // Update times for the deterministic events
		void SetRunType(int rt,double dt=0);
		double GillespieStep(double timelimit = -1); // Advance one step in the Gillespie algorithm. Returns time of the step. Timelimit does not react if the reaction would take more than timelimit
		double LangevinEulerStep(double timelimit = -1); // Make an Euler-Maruyama integration step
		double LangevinMilsteinStep(double timelimit = -1); // Make an SDE integration step with the Milstein algorithm (not tested)
		double MacroStep(double timlimit = -1); // Make an Euler (deterministic) step
		double MacroAdiabaticStep(double dtt); //  Make an Euler step if the Adiabaitc species
		double Run(double time, bool verbose=true); // React until reach time time
		double RunTimes(int N,double time, bool verbose=true); // Run Run() N times
		double RunTransition(bool (*prop_f)(v_species&), bool (*prop_g)(v_species&), int loops, double maxT); // Run transition between two points, if loops=1 it also records the transition trajectory
		void WriteState(); // Write state in file
		void WriteTempState(); // Write state in file
		void WritePrevrecState(); // Write previous recorded state in file
		void SetWriteState(int flag, double tl=0);// Switch betweem different states:
		double (langil::*MakeStep)(double); // pointer to the actual integration chosen
			// TIMESTEP: write the state every tl time lapse
			// ALL: write after each reaction takes place
			// NOWRITE: don't write anything in output file
		double SetLangevinTimeStep(double dx); // set dt to a characteristic time that will increase with the size of the system
		void Add_TimeAction(double (*actionfunc)(double));
		void RunTimeAction();
		void setPhaseDuration(int phase, double duration, int type_phase);
		void Set_AdiabaticReaction(string aname);
		void Set_GeometricReaction(string aname);
		void Set_Boundary_Behaviour(void (*b_b)(v_species&, v_species&)); // Boundary behaviour
		void Set_StoreDivisionTimes();// activate the storage of division times in DivisionTimes vector
		void (*Boundary_Behaviour)(v_species&, v_species&); // boundary behaviour function
		vector<StructDivisionState> Get_HistoryDivision();
		void Reset_HistoryDivision();
		void SetDivisionHistory(int b,int a);

	protected:

		v_species x; //value of each species. shared_ptr is used since the vector can contain species and also childrens of that class (i.e. different classes)
		vector<reaction> r;//vector with all the reactions
 		double time; // current time of simulation
		gsl_rng * rng; // allocator for the rng generator
		vector<double> rnd; // rnd numbers
		vector<double> xtemp; // temporal vector for integrations
		v_species xtemp0,xtemp1; // temporal vector for integrations
		double pro,pro0; // auxiliar variables for the program
		int sto;  // auxiliar value for stoichiometry
		double totalprop,cumprop, detprop; // sum of the propensities
		double nexttau; // time of the next step
		vector<reaction>::iterator nextreaction; // reaction selected to react
		vector<int>* nextstoichiometry;
		double Omega; // volume of the system to change between concentrations and absolute numbers
		double signal; // variable that can modulate an external signal its time course
						// may be changed through time actions with Add_TimeAction
		int celldivided; // flag to track possible celldivision
		int geostoichiometry; // dummy variable to set random geometric stoichiometries

		bool record; // Set record of the trajectory
		ofstream trajfile; // File for output traj
		string outputfilename; // File for output name
		int fwrite; // flag for write state when integrating
		double timelapse; // dt for recording if TIMESTEP
		double nextrectime; // t for recording if TIMESTEP
		double totaltime; // total integration time for the trajectory
		double dt; // timestep used in integrations for macroscopic and langevin (if negative, it is used by Langevin as a prediction for dx)

		int cellcyclespeciesidx;  // index of the species vector that tracks cell cycle phases
 		cell_cycle cell; // cell_cycle object to manage cell cycle events

 		bool fStoreDivisionTimes; // flag to store Division Times
 		vector<StructDivisionState> DivisionHistoryVector; // Division history vector. Each component is a vector
 		StructDivisionState divisionstate;
		vector<double(*)(double)> actionfuncvec; // vector to time action functions

};

////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class reaction{//Class containing the info of each reaction. Each reaction added
// through the method Add_Reaction(reaction) will be stored as an element in a vector
// by langil class. The pointer x, points out to the state of the langil system and
// is managed automatically by langil when a reaction is added to a langil instance
// It has also the property "adiabatic" meaning that it is a fast variable and can always be simulated
// from its deterministic behaviour

	friend class langil;

	public:

	reaction(string aname,vector<int> asto,double (*aprop_f)(v_species&)=NULL,int reacttype= EXPONENTIAL_REACTION,
		    vector<reaction>* x0=NULL){
			name=aname;
			stoichiometry=asto;
			prop_f=aprop_f;
			reactiontype=reacttype;
		}
		double GetPropensity(){
			return prop_f(*x);
		}
		double GetPropensity(v_species& x0){
			return prop_f(x0);
		}
		string GetName(){
			return name;
		}
		void SetPropensity(double (*p_f)(v_species&)){
			prop_f=p_f;
		}
		void SetState(v_species& x0){
			x=&x0;
		}
		vector<int>* GetStoichiometry(){
			return &stoichiometry;
		}
		bool IsAdiabatic(){
			return (reactiontype == ADIABATIC_REACTION);
		}
		bool IsNotAdiabatic(){
			return (reactiontype != ADIABATIC_REACTION);
		}
		bool IsGeometric(){
			return (reactiontype == GEOMETRIC_REACTION);
		}
		bool IsNotGeometric(){
			return (reactiontype != GEOMETRIC_REACTION);
		}
		bool IsNotDetermTime(){
			return (reactiontype != (DETERMINISTIC_REACTION));
		}
		bool IsDetermTime(){
			return (reactiontype == (DETERMINISTIC_REACTION));
		}
		void SetAdiabatic(){
			reactiontype = ADIABATIC_REACTION;
		}
		void SetGeometric(){
			reactiontype = GEOMETRIC_REACTION;
		}
		void SetDeterministicTime(){
			reactiontype = DETERMINISTIC_REACTION;
		}

	protected:

		string name; // name of the reactio
		vector<int> stoichiometry; // stoichiometry
		double (*prop_f)(v_species&); // propensity function
		v_species* x;  //value of each species. shared_ptr is used since the vector can contain species and also childrens of that class (i.e. different classes)
		int reactiontype; // can be any of the macros EXPONENTIAL_REACTION, DETERMINISITIC_REACTION, etc.

 // of the system

};

#endif


/*
 * Copyright 2012-2016 M. Andersen and L. Vandenberghe.
 * Copyright 2010-2011 L. Vandenberghe.
 * Copyright 2004-2009 J. Dahl and L. Vandenberghe.
 *
 * This file is part of CVXOPT.
 *
 * CVXOPT 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 3 of the License, or
 * (at your option) any later version.
 *
 * CVXOPT 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, see <http://www.gnu.org/licenses/>.
 */

#include "cvxopt.h"

#include <complex.h>

#include "misc.h"

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

#include <time.h>

PyDoc_STRVAR(gsl__doc__,"Random Module.");

static unsigned long seed = 0;
static const gsl_rng_type *rng_type;
static gsl_rng *rng;

static char doc_getseed[] =
  "Returns the seed value for the random number generator.\n\n"
  "getseed()";

static PyObject * getseed(PyObject *self)
{
  return Py_BuildValue("l",seed);
}

static char doc_setseed[] =
  "Sets the seed value for the random number generator.\n\n"
  "setseed(value = 0)\n\n"
  "ARGUMENTS\n"
  "value     integer seed. If the value is 0, then the system clock\n"
  "          measured in seconds is used instead";

static PyObject * setseed(PyObject *self, PyObject *args)
{
  unsigned long seed_ = 0;
  time_t seconds;

  if (!PyArg_ParseTuple(args, "|l", &seed_))
    return NULL;

  if (!seed_) {
    time(&seconds);
    seed = (unsigned long)seconds;
  }
  else seed = seed_;

  return Py_BuildValue("");
}

static char doc_normal[] =
  "Randomly generates a matrix with normally distributed entries.\n\n"
  "normal(nrows, ncols=1, mean=0, std=1)\n\n"
  "PURPOSE\n"
  "Returns a matrix with typecode 'd' and size nrows by ncols, with\n"
  "its entries randomly generated from a normal distribution with mean\n"
  "m and standard deviation std.\n\n"
  "ARGUMENTS\n"
  "nrows     number of rows\n\n"
  "ncols     number of columns\n\n"
  "mean      approximate mean of the distribution\n\n"
  "std       standard deviation of the distribution";
static PyObject *
normal(PyObject *self, PyObject *args, PyObject *kwrds)
{
  matrix *obj;
  int i, nrows, ncols = 1;
  double m = 0, s = 1;
  char *kwlist[] = {"nrows", "ncols", "mean", "std",  NULL};

  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "i|idd", kwlist,
	  &nrows, &ncols, &m, &s)) return NULL;

  if (s < 0.0) PY_ERR(PyExc_ValueError, "std must be non-negative");

  if ((nrows<0) || (ncols<0)) {
    PyErr_SetString(PyExc_TypeError, "dimensions must be non-negative");
    return NULL;
  }

  if (!(obj = Matrix_New(nrows, ncols, DOUBLE)))
    return PyErr_NoMemory();

  gsl_rng_env_setup();
  rng_type = gsl_rng_default;
  rng = gsl_rng_alloc (rng_type);
  gsl_rng_set(rng, seed);

  for (i = 0; i < nrows*ncols; i++)
    MAT_BUFD(obj)[i] = gsl_ran_gaussian (rng, s) + m;

  seed = gsl_rng_get (rng);
  gsl_rng_free(rng);

  return (PyObject *)obj;
}

static char doc_uniform[] =
  "Randomly generates a matrix with uniformly distributed entries.\n\n"
  "uniform(nrows, ncols=1, a=0, b=1)\n\n"
  "PURPOSE\n"
  "Returns a matrix with typecode 'd' and size nrows by ncols, with\n"
  "its entries randomly generated from a uniform distribution on the\n"
  "interval (a,b).\n\n"
  "ARGUMENTS\n"
  "nrows     number of rows\n\n"
  "ncols     number of columns\n\n"
  "a         lower bound\n\n"
  "b         upper bound";

static PyObject *
uniform(PyObject *self, PyObject *args, PyObject *kwrds)
{
  matrix *obj;
  int i, nrows, ncols = 1;
  double a = 0, b = 1;

  char *kwlist[] = {"nrows", "ncols", "a", "b", NULL};

  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "i|idd", kwlist,
	  &nrows, &ncols, &a, &b)) return NULL;

  if (a>b) PY_ERR(PyExc_ValueError, "a must be less than b");

  if ((nrows<0) || (ncols<0))
    PY_ERR_TYPE("dimensions must be non-negative");

  if (!(obj = (matrix *)Matrix_New(nrows, ncols, DOUBLE)))
    return PyErr_NoMemory();

  gsl_rng_env_setup();
  rng_type = gsl_rng_default;
  rng = gsl_rng_alloc (rng_type);
  gsl_rng_set(rng, seed);

  for (i= 0; i < nrows*ncols; i++)
    MAT_BUFD(obj)[i] = gsl_ran_flat (rng, a, b);

  seed = gsl_rng_get (rng);
  gsl_rng_free(rng);

  return (PyObject *)obj;
}

static PyMethodDef gsl_functions[] = {
{"getseed", (PyCFunction)getseed, METH_VARARGS|METH_KEYWORDS, doc_getseed},
{"setseed", (PyCFunction)setseed, METH_VARARGS|METH_KEYWORDS, doc_setseed},
{"normal", (PyCFunction)normal, METH_VARARGS|METH_KEYWORDS, doc_normal},
{"uniform", (PyCFunction)uniform, METH_VARARGS|METH_KEYWORDS, doc_uniform},
{NULL}  /* Sentinel */
};

#if PY_MAJOR_VERSION >= 3

static PyModuleDef gsl_module = {
    PyModuleDef_HEAD_INIT,
    "gsl",
    gsl__doc__,
    -1,
    gsl_functions,
    NULL, NULL, NULL, NULL
};

PyMODINIT_FUNC PyInit_gsl(void)
{
  PyObject *m;
  if (!(m = PyModule_Create(&gsl_module))) return NULL;
  if (import_cvxopt() < 0) return NULL;
  return m;
}

#else

PyMODINIT_FUNC initgsl(void)
{
  PyObject *m;
  m = Py_InitModule3("cvxopt.gsl", gsl_functions, gsl__doc__);
  if (import_cvxopt() < 0) return;
}

#endif