// Ref:
// G.P. Lepage, LQCD for Novices
#include "su3_utils.c"
#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_fit.h>

// globals
const gsl_rng_type *T;
gsl_rng *r; // random generator
su3_matrix links[4*N*N*N*N]; // link variables
su3_matrix rands[50]; // random unitary matrices

FILE *output;

// prototypes
void setup();
void cleanup();

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

  int O[4]={0,0,0,0};

  for(int n=0;n<Ntherm;n++) {
    //fprintf(output, "%f \n",plaquette(links,0,1,O));
    hb_update(links,r,rands); //thermalize
  }

  // Confinement phase should have small Polyakov VEV
  if(confined){
    while(polyakov_mean(links) > pol_thres){
      hb_update(links,r,rands); //thermalize
    }
  }

  printf("Thermalized! \n");

  for(int n=0;n<Ncf;n++){
    // Decorrelate
    for(int k=0;k<Ncor;k++){
      hb_update(links,r,rands);
    }

    if(n%5==0){printf("%d \n",n);}
    // Measurement

    //fprintf(output, "%f %f \n", wloop_mean(1,1,links),wloop_mean(1,2,links));
    //fprintf(output, "%f %f %f %f %f %f %f %f\n",twloop2_mean(1,links),twloop2_mean(2,links),twloop2_mean(3,links),twloop2_mean(4,links),twloop2_mean(5,links),twloop2_mean(6,links),twloop2_mean(7,links),twloop2_mean(8,links));
    //fprintf(output, "%f %f %f %f %f %f %f %f\n",plaq_corr(1,links),plaq_corr(2,links),plaq_corr(3,links),plaq_corr(4,links),plaq_corr(5,links),plaq_corr(6,links),plaq_corr(7,links),plaq_corr(8,links));
    //printf("%f \n",polyakov_mean(links));
    printf( "%f \n", plaq_mean_s(0,links));
    printf( "%f %f \n", wloop_mean(1,1,links),wloop_mean(1,2,links) );
  }

  printf("acc=%f\n", (double)acc/tot);

  cleanup();
  return 0;
}

void
setup (){
  acc=0;
  tot=0;
  // ready random num generator
  gsl_rng_env_setup ();
  T = gsl_rng_mt19937;
  r = gsl_rng_alloc (T);
  // fill in table of rand matrices
  for(int i=0;i<25;i++){
    rands[2*i]=su3_rand(r);
    rands[2*i+1]=su3_inv(rands[2*i]);
  }
  //init links to identities
  for(int i=0;i<4*N*N*N*N;i++){
    links[i]=su3_unit();
  }

  output = fopen("out.txt", "w+");
}

void
cleanup (){
  gsl_rng_free(r);
  fclose(output);
}


/*	$Id$ */
/*
 * Copyright (c) 2014--2016 Kristaps Dzonsons <kristaps@kcons.eu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <errno.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#ifdef __linux__
#include <bsd/stdlib.h> /* arc4random() */
#endif
#include <string.h>

#include <gtk/gtk.h>
#include <gsl/gsl_multifit.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <kplot.h>

#include "extern.h"

/*
 * For a given point "x" in the domain, fit ourselves to the polynomial
 * coefficients of degree "fitpoly + 1".
 */
static double
fitpoly(const double *fits, size_t poly, double x)
{
	double	 y, v;
	size_t	 i, j;

	for (y = 0.0, i = 0; i < poly; i++) {
		v = fits[i];
		for (j = 0; j < i; j++)
			v *= x;
		y += v;
	}
	return(y);
}

/*
 * Copy the "hotlsb" data into "warm" holding.
 * While here, set our simulation's "work" parameter (if "fitpoly" is
 * set) to contain the necessary dependent variable.
 * We're guaranteed to be the only ones in here, and the only ones with
 * a lock on the LBS data.
 */
static void
snapshot(struct sim *sim, struct simwarm *warm,
	uint64_t truns, uint64_t tgens)
{
	double	 	v, chisq, x, y;
	struct kpair	kp;
	int		rc;
	size_t	 	i, j, k;

	/* Warm copy is already up to date. */
	if (warm->truns == truns) {
		g_assert(warm->tgens == tgens);
		return;
	}

	/* Copy out: we have a lock. */
	simbuf_copy_warm(sim->bufs.times);
	simbuf_copy_warm(sim->bufs.imeans);
	simbuf_copy_warm(sim->bufs.istddevs);
	simbuf_copy_warm(sim->bufs.islandmeans);
	simbuf_copy_warm(sim->bufs.islandstddevs);
	simbuf_copy_warm(sim->bufs.means);
	simbuf_copy_warm(sim->bufs.stddevs);
	simbuf_copy_warm(sim->bufs.mextinct);
	simbuf_copy_warm(sim->bufs.iextinct);
	warm->truns = truns;
	warm->tgens = tgens;

	/*
	 * If we're going to fit to a polynomial, set the dependent
	 * variable within the conditional.
	 */
	if (sim->fitpoly)
		for (i = 0; i < sim->dims; i++) {
			rc = kdata_get(sim->bufs.means->warm, i, &kp);
			g_assert(0 != rc);
			gsl_vector_set(sim->work.y, i, kp.y);
		}

	/*
	 * If we're going to run a weighted polynomial multifit, then
	 * use the variance as the weight.
	 */
	if (sim->fitpoly && sim->weighted)
		for (i = 0; i < sim->dims; i++) {
			rc = kdata_get(sim->bufs.stddevs->warm, i, &kp);
			g_assert(0 != rc);
			gsl_vector_set(sim->work.w, i, kp.y);
		}

	/*
	 * If we're not fitting to a polynomial, simply notify that
	 * we've copied out and continue on our way.
	 */
	if (0 == sim->fitpoly)
		return;

	/*
	 * If we're fitting to a polynomial, initialise our independent
	 * variables now.
	 */
	for (i = 0; i < sim->dims; i++) {
		gsl_matrix_set(sim->work.X, i, 0, 1.0);
		for (j = 0; j < sim->fitpoly; j++) {
			v = sim->xmin +
				(sim->xmax -
				 sim->xmin) *
				(i / (double)sim->dims);
			for (k = 0; k < j; k++)
				v *= v;
			gsl_matrix_set(sim->work.X, i, j + 1, v);
		}
	}

	/*
	 * Now perform the actual fitting.
	 * We either use weighted (weighted with the variance) or
	 * non-weighted fitting algorithms.
	 */
	if (sim->weighted)
		gsl_multifit_wlinear(sim->work.X, sim->work.w,
			sim->work.y, sim->work.c, sim->work.cov,
			&chisq, sim->work.work);
	else
		gsl_multifit_linear(sim->work.X, sim->work.y,
			sim->work.c, sim->work.cov, &chisq,
			sim->work.work);

	/*
	 * Now snapshot the fitted polynomial coefficients and compute
	 * the fitted approximation for all incumbents (along with the
	 * minimum value).
	 */
	for (i = 0; i < sim->fitpoly + 1; i++)
		sim->work.coeffs[i] = gsl_vector_get(sim->work.c, i);

	for (i = 0; i < sim->dims; i++) {
		x = sim->xmin + (sim->xmax - sim->xmin) *
			i / (double)sim->dims;
		y = fitpoly(sim->work.coeffs,
			sim->fitpoly + 1, x);
		kdata_array_set(sim->bufs.fitpoly, i, x, y);
	}
}

/*
 * In a given simulation, compute the next mutant/incumbent pair.
 * We make sure that incumbents are striped evenly in any given
 * simulation but that mutants are randomly selected from within the
 * strategy domain.
 */
static int
on_sim_next(struct sim *sim, const gsl_rng *rng,
	size_t *islandidx, double *mutantp, double *incumbentp,
	size_t *incumbentidx, double *vp, const size_t *islands,
	size_t gen)
{
	int		 dosnap, rc;
	size_t		 mutant;
	uint64_t	 truns, tgens;

	if (sim->terminate)
		return(0);

	truns = tgens = 0; /* Silence compiler. */

	/* This is set if we "own" the LSB->warm process. */
	dosnap = 0;

	g_assert(*incumbentidx < sim->dims);
	g_assert(*islandidx < sim->islands);
	g_mutex_lock(&sim->hot.mux);

	/*
	 * If we're entering this with a result value, then plug it into
	 * the index associated with the run and increment our count.
	 * This prevents us from overwriting others' results.
	 */
	if (NULL != vp) {
		rc = kdata_array_add
			(sim->bufs.times->hot, gen, 1.0);
		g_assert(0 != rc);
		rc = kdata_array_fill_ysizes
			(sim->bufs.islands, islands);
		g_assert(0 != rc);
		rc = kdata_array_set(sim->bufs.fractions,
			*incumbentidx, *incumbentp, *vp);
		g_assert(0 != rc);
		rc = kdata_array_set(sim->bufs.ifractions,
			*islandidx, *islandidx, *vp);
		g_assert(0 != rc);
		rc = kdata_array_set(sim->bufs.mutants,
			*incumbentidx, *incumbentp, 0.0 == *vp);
		g_assert(0 != rc);
		rc = kdata_array_set(sim->bufs.incumbents,
			*incumbentidx, *incumbentp, 1.0 == *vp);
		g_assert(0 != rc);
		sim->hot.tgens += gen;
		sim->hot.truns++;
	}

	/*
	 * Check if we've been requested to pause.
	 * If so, wait for a broadcast on our condition.
	 * This will unlock the mutex for others to process.
	 */
	if (1 == sim->hot.pause)
		g_cond_wait(&sim->hot.cond, &sim->hot.mux);

	/*
	 * Check if we've been instructed by the main thread of
	 * execution to snapshot hot data into warm storage.
	 * We do this in two parts: first, we register that we're in a
	 * copyout (it's now 2) and then actually do the copyout outside
	 * of the hot mutex.
	 */
	if (1 == sim->hot.copyout) {
		simbuf_copy_hotlsb(sim->bufs.times);
		simbuf_copy_hotlsb(sim->bufs.islandmeans);
		simbuf_copy_hotlsb(sim->bufs.islandstddevs);
		simbuf_copy_hotlsb(sim->bufs.imeans);
		simbuf_copy_hotlsb(sim->bufs.istddevs);
		simbuf_copy_hotlsb(sim->bufs.means);
		simbuf_copy_hotlsb(sim->bufs.stddevs);
		simbuf_copy_hotlsb(sim->bufs.mextinct);
		simbuf_copy_hotlsb(sim->bufs.iextinct);
		truns = sim->hot.truns;
		tgens = sim->hot.tgens;
		sim->hot.copyout = dosnap = 2;
	}

	/*
	 * Reassign our mutant and incumbent from the ring sized by the
	 * configured lattice dimensions.
	 * These both increment in single movements until the end of the
	 * lattice, then wrap around.
	 */
	*islandidx = sim->hot.island;
	mutant = sim->hot.mutant;
	*incumbentidx = sim->hot.incumbent;
	sim->hot.mutant++;
	if (sim->hot.mutant == sim->dims) {
		sim->hot.incumbent++;
		if (sim->hot.incumbent == sim->dims) {
			sim->hot.incumbent = 0;
			if (MAPINDEX_STRIPED == sim->mapindex)
				sim->hot.island =
					(sim->hot.island + 1) %
					sim->islands;
		}
		sim->hot.mutant = 0;
	}

	g_mutex_unlock(&sim->hot.mux);

	/*
	 * Assign our incumbent and mutant.
	 * The incumbent just gets the current lattice position,
	 * ensuring an even propogation.
	 * The mutant is either assigned the same way or from a Gaussian
	 * distribution around the current incumbent.
	 */
	*incumbentp = sim->xmin + (sim->xmax - sim->xmin) *
		(*incumbentidx / (double)sim->dims);

	if (MUTANTS_GAUSSIAN == sim->mutants) {
		do {
			*mutantp = *incumbentp +
				gsl_ran_gaussian
				(rng, sim->mutantsigma);
		} while (*mutantp < sim->ymin ||
			 *mutantp >= sim->ymax);
	} else
		*mutantp = sim->xmin +
			(sim->xmax -
			 sim->xmin) *
			(mutant / (double)sim->dims);

	/*
	 * If we were the ones to set the copyout bit, then do the
	 * copyout right now.
	 * When we're finished, lower the copyout semaphor.
	 */
	if (dosnap) {
		snapshot(sim, &sim->warm, truns, tgens);
		g_mutex_lock(&sim->hot.mux);
		g_assert(2 == sim->hot.copyout);
		sim->hot.copyout = 0;
		g_mutex_unlock(&sim->hot.mux);
	}

	return(1);
}

/*
 * For a given island (size "pop") player's strategy "x" where mutants
 * (numbering "mutants") have strategy "mutant" and incumbents
 * (numbering "incumbents") have strategy "incumbent", compute the
 * a(1 + delta(pi(x, X)) function.
 */
static double
reproduce(const struct sim *sim, double x,
	double mutant, double incumbent, size_t mutants, size_t pop)
{
	double	 v;

	if (0 == pop)
		return(0.0);

	v = hnode_exec
		((const struct hnode *const *)
		 sim->exp, x,
		 (mutants * mutant) + ((pop - mutants) * incumbent),
		 pop);
	g_assert( ! (isnan(v) || isinf(v)));
	return(sim->alpha * (1.0 + sim->delta * v));
}

static size_t
migrate(const struct sim *sim, const gsl_rng *rng, size_t cur)
{
	double	 v;
	size_t	 i;

again:
	while (0.0 == (v = gsl_rng_uniform(rng)))
		/* Loop. */ ;

	for (i = 0; i < sim->islands - 1; i++)
		if ((v -= sim->ms[cur][i]) <= 0.0)
			break;

	/*
	 * This can occur due to floating-point rounding.
	 * If it does, re-run the algorithm.
	 */
	if (i == sim->islands - 1 && i == cur) {
		g_debug("Degenerate probability: re-running");
		goto again;
	}

	g_assert(cur != i);
	return(i);
}

/*
 * Run a simulation.
 * This can be one thread of many within the same simulation.
 */
void *
simulation(void *arg)
{
	struct simthr	  *thr = arg;
	struct sim	  *sim = thr->sim;
	double		   mutant, incumbent, v, lambda, prob;
	unsigned int	   offs;
	unsigned long	   seed;
	double		  *vp, *icache, *mcache;
	double		***icaches, ***mcaches;
	size_t		  *kids[2], *migrants[2], *imutants, *npops,
			  *ndeaths;
	size_t		   t, i, j, k, new, mutants, incumbents,
			   len1, len2, incumbentidx, islandidx,
			   ntotalpop;
	int		   mutant_old, mutant_new;
	gsl_rng		  *rng;

	rng = gsl_rng_alloc(gsl_rng_default);
	seed = arc4random();
	gsl_rng_set(rng, seed);

	g_debug("%p: Thread (simulation %p) "
		"start", g_thread_self(), sim);

	icache = mcache = NULL;
	icaches = mcaches = NULL;
	kids[0] = g_malloc0_n(sim->islands, sizeof(size_t));
	kids[1] = g_malloc0_n(sim->islands, sizeof(size_t));
	ndeaths = g_malloc0_n(sim->islands, sizeof(size_t));
	migrants[0] = g_malloc0_n(sim->islands, sizeof(size_t));
	migrants[1] = g_malloc0_n(sim->islands, sizeof(size_t));
	imutants = g_malloc0_n(sim->islands, sizeof(size_t));
	vp = NULL;
	npops = NULL;
	incumbentidx = 0;
	islandidx = MAPINDEX_FIXED == sim->mapindex ?
		sim->mapindexfix : 0;
	mutant = incumbent = 0.0;
	t = 0;

	/*
	 * Set up our mutant and incumbent payoff caches.
	 * These consist of all possible payoffs with a given number of
	 * mutants and incumbents on an island.
	 * We have two ways of doing this: with non-uniform island sizes
	 * (icaches and mcaches) and uniform sizes (icache and mcache,
	 * notice the singular).
	 * The non-uniform island size can also change, so we precompute
	 * for all possible populations as well.
	 */
	if (NULL != sim->pops) {
		g_assert(0 == sim->pop);
		npops = g_malloc0_n(sim->islands, sizeof(size_t));
		for (i = 0; i < sim->islands; i++)
			npops[i] = sim->pops[i];
		icaches = g_malloc0_n(sim->islands, sizeof(double **));
		mcaches = g_malloc0_n(sim->islands, sizeof(double **));
		for (i = 0; i < sim->islands; i++) {
			icaches[i] = g_malloc0_n
				(sim->pops[i] + 1, sizeof(double *));
			mcaches[i] = g_malloc0_n
				(sim->pops[i] + 1, sizeof(double *));
			for (j = 0; j <= sim->pops[i]; j++) {
				icaches[i][j] = g_malloc0_n(j + 1, sizeof(double));
				mcaches[i][j] = g_malloc0_n(j + 1, sizeof(double));
			}
		}
	} else {
		g_assert(sim->pop > 0);
		icache = g_malloc0_n(sim->pop + 1, sizeof(double));
		mcache = g_malloc0_n(sim->pop + 1, sizeof(double));
	}
again:
	/*
	 * Repeat til we're instructed to terminate.
	 * We also pass in our last result for processing.
	 */
	if ( ! on_sim_next(sim, rng, &islandidx, &mutant,
		&incumbent, &incumbentidx, vp, imutants, t)) {
		g_debug("%p: Thread (simulation %p) exiting",
			g_thread_self(), sim);
		/*
		 * Upon termination, free up all of the memory
		 * associated with our simulation.
		 */
		g_free(ndeaths);
		g_free(imutants);
		g_free(kids[0]);
		g_free(kids[1]);
		g_free(migrants[0]);
		g_free(migrants[1]);
		if (NULL != sim->pops)
			for (i = 0; i < sim->islands; i++) {
				for (j = 0; j <= sim->pops[i]; j++) {
					g_free(icaches[i][j]);
					g_free(mcaches[i][j]);
				}
				g_free(icaches[i]);
				g_free(mcaches[i]);
			}
		g_free(icaches);
		g_free(mcaches);
		g_free(icache);
		g_free(mcache);
		return(NULL);
	}

	/*
	 * Initialise a random island to have one mutant.
	 * The rest are all incumbents.
	 */
	memset(imutants, 0, sim->islands * sizeof(size_t));
	imutants[islandidx] = 1;
	mutants = 1;
	incumbents = sim->totalpop - mutants;
	ntotalpop = sim->totalpop;

	/*
	 * Precompute all possible payoffs.
	 * This allows us not to re-run the lambda calculation for each
	 * individual.
	 * If we have only a single island size, then avoid allocating
	 * for each island by using only the first mcaches index.
	 */
	if (NULL != sim->pops)
		for (i = 0; i < sim->islands; i++) {
			for (j = 0; j <= sim->pops[i]; j++) {
				for (k = 0; k <= j; k++) {
					mcaches[i][j][k] = reproduce
						(sim, mutant, mutant,
						 incumbent, k, j);
					icaches[i][j][k] = reproduce
						(sim, incumbent, mutant,
						 incumbent, k, j);
				}
			}
		}
	else
		for (i = 0; i <= sim->pop; i++) {
			mcache[i] = reproduce(sim, mutant,
				mutant, incumbent, i, sim->pop);
			icache[i] = reproduce(sim, incumbent,
				mutant, incumbent, i, sim->pop);
		}

	for (t = 0; t < sim->stop; t++) {
		if (NULL != sim->pops && sim->ideathmean > 0) {
			/*
			 * If we're a non-uniform population and have an
			 * island death mean, then see if we're supposed
			 * to kill off islands, then re-set the shot
			 * clock.
			 */
			for (i = 0; i < sim->islands; i++) {
				/*
				 * If the shot-clock has not been
				 * started OR the island is already
				 * dead, set it again.
				 */
				if (0 == ndeaths[i] || 0 == npops[i]) {
					ndeaths[i] = t + 1 +
						gsl_ran_poisson
						(rng, sim->ideathmean);
					continue;
				} else if (t != ndeaths[i])
					continue;

				ndeaths[i] = t + 1 +
					gsl_ran_poisson
					(rng, sim->ideathmean);

				/*
				 * What's the sum of our payoffs?
				 * Compute the probability that we're
				 * going to be killed from that and our
				 * coefficient.
				 */
				g_assert(npops[i] > 0);
				v = mcaches[i][npops[i]][imutants[i]] *
					imutants[i] +
				    icaches[i][npops[i]][imutants[i]] *
				    	(npops[i] - imutants[i]);
				prob = sim->ideathcoef * exp(-v);
				if (gsl_rng_uniform(rng) >= prob)
					continue;
				mutants -= imutants[i];
				incumbents -= (npops[i] - imutants[i]);
				ntotalpop -= npops[i];
				npops[i] = 0;
				imutants[i] = 0;
			}
		}

		/*
		 * Birth process: have each individual (first mutants,
		 * then incumbents) give birth.
		 * Use a Poisson process with the given mean in order to
		 * properly compute this.
		 * We need two separate versions for whichever type of
		 * mcache we decide to use.
		 */
		if (NULL != sim->pops)
			for (j = 0; j < sim->islands; j++) {
				if (0 == npops[j])
					continue;
				g_assert(0 == kids[0][j]);
				g_assert(0 == kids[1][j]);
				g_assert(0 == migrants[0][j]);
				g_assert(0 == migrants[1][j]);
				g_assert(imutants[j] <= npops[j]);
				lambda = mcaches[j]
					[npops[j]][imutants[j]];
				for (k = 0; k < imutants[j]; k++) {
					offs = gsl_ran_poisson
						(rng, lambda);
					kids[0][j] += offs;
				}
				lambda = icaches[j]
					[npops[j]][imutants[j]];
				for ( ; k < npops[j]; k++) {
					offs = gsl_ran_poisson
						(rng, lambda);
					kids[1][j] += offs;
				}
			}
		else
			for (j = 0; j < sim->islands; j++) {
				g_assert(0 == kids[0][j]);
				g_assert(0 == kids[1][j]);
				g_assert(0 == migrants[0][j]);
				g_assert(0 == migrants[1][j]);
				lambda = mcache[imutants[j]];
				for (k = 0; k < imutants[j]; k++) {
					offs = gsl_ran_poisson
						(rng, lambda);
					kids[0][j] += offs;
				}
				lambda = icache[imutants[j]];
				for ( ; k < sim->pop; k++) {
					offs = gsl_ran_poisson
						(rng, lambda);
					kids[1][j] += offs;
				}
			}

		/*
		 * Determine whether we're going to migrate and, if
		 * migration is stipulated, to where.
		 */
		if (NULL != sim->ms)
			for (j = 0; j < sim->islands; j++) {
				for (k = 0; k < kids[0][j]; k++) {
					new = j;
					if (gsl_rng_uniform(rng) < sim->m)
						new = migrate(sim, rng, j);
					migrants[0][new]++;
				}
				for (k = 0; k < kids[1][j]; k++) {
					new = j;
					if (gsl_rng_uniform(rng) < sim->m)
						new = migrate(sim, rng, j);
					migrants[1][new]++;
				}
				kids[0][j] = kids[1][j] = 0;
			}
		else
			for (j = 0; j < sim->islands; j++) {
				for (k = 0; k < kids[0][j]; k++) {
					new = j;
					if (gsl_rng_uniform(rng) < sim->m) do
						new = gsl_rng_uniform_int
							(rng, sim->islands);
					while (new == j);
					migrants[0][new]++;
				}
				for (k = 0; k < kids[1][j]; k++) {
					new = j;
					if (gsl_rng_uniform(rng) < sim->m) do
						new = gsl_rng_uniform_int
							(rng, sim->islands);
					while (new == j);
					migrants[1][new]++;
				}
				kids[0][j] = kids[1][j] = 0;
			}

		/*
		 * Perform the migration itself.
		 * We randomly select an individual on the destination
		 * island as well as one from the migrant queue.
		 * We then replace one with the other.
		 */
		if (NULL != sim->pops)
			for (j = 0; j < sim->islands; j++) {
				if (npops[j] < sim->pops[j]) {
					/*
					 * This is the case where a
					 * given island has had its
					 * population killed off.
					 * Try to fill it up: don't
					 * replace anybody.
					 */
					while (npops[j] < sim->pops[j]) {
						len1 = migrants[0][j] +
							migrants[1][j];
						if (0 == len1)
							break;
						len2 = gsl_rng_uniform_int
							(rng, len1);
						if (len2 < migrants[0][j]) {
							migrants[0][j]--;
							imutants[j]++;
							mutants++;
						} else {
							migrants[1][j]--;
							incumbents++;
						}
						npops[j]++;
						ntotalpop++;
					}
				} else {
					len1 = migrants[0][j] +
						migrants[1][j];
					if (0 == len1)
						continue;
					len2 = gsl_rng_uniform_int
						(rng, npops[j]);
					mutant_old = len2 < imutants[j];
					len2 = gsl_rng_uniform_int(rng, len1);
					mutant_new = len2 < migrants[0][j];
					if (mutant_old && ! mutant_new) {
						imutants[j]--;
						mutants--;
						incumbents++;
					} else if ( ! mutant_old && mutant_new) {
						imutants[j]++;
						mutants++;
						incumbents--;
					}
				}
				migrants[0][j] = migrants[1][j] = 0;
			}
		else
			for (j = 0; j < sim->islands; j++) {
				len1 = migrants[0][j] + migrants[1][j];
				if (0 == len1)
					continue;

				len2 = gsl_rng_uniform_int(rng, sim->pop);
				mutant_old = len2 < imutants[j];
				len2 = gsl_rng_uniform_int(rng, len1);
				mutant_new = len2 < migrants[0][j];

				if (mutant_old && ! mutant_new) {
					imutants[j]--;
					mutants--;
					incumbents++;
				} else if ( ! mutant_old && mutant_new) {
					imutants[j]++;
					mutants++;
					incumbents--;
				}

				migrants[0][j] = migrants[1][j] = 0;
			}

		/* Stop when a population goes extinct. */
		if (0 == mutants || 0 == incumbents)
			break;
	}

	/*
	 * Assign the result pointer to the last population fraction.
	 * This will be processed by on_sim_next().
	 */
	if (incumbents == 0) {
		g_assert(mutants == ntotalpop);
		v = 1.0;
	} else if (mutants == 0) {
		g_assert(incumbents == ntotalpop);
		v = 0.0;
	} else
		v = mutants / (double)ntotalpop;

	vp = &v;
	goto again;
}


/******************************************************************************
CosmoLike Configuration Space Covariances for Projected Galaxy 2-Point Statistics
https://github.com/CosmoLike/CosmoCov
by CosmoLike developers
******************************************************************************/

#include <math.h>
#include <stdlib.h>
#if !defined(__APPLE__)
#include <malloc.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <string.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 <fftw3.h>

#include "../cosmolike_core/2dfftlog/twobessel.h"
#include "../cosmolike_core/2dfftlog/utils.h"

#include "../cosmolike_core/theory/basics.c"
#include "../cosmolike_core/theory/structs.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/cosmo2D_fourier.c"

#include "../cosmolike_core/theory/covariances_3D.c"
#include "../cosmolike_core/theory/covariances_fourier.c"
#include "../cosmolike_core/theory/covariances_real_bin_fft.c"
#include "../cosmolike_core/theory/run_covariances_real_bin_fft.c"
#include "init.c"

#include "../cosmolike_core/2dfftlog/utils_complex.h"

#include <time.h>

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

  if (argc != 3){
    fprintf(stderr, "Syntax: %s  block_number  config_file\n", argv[0]);
    exit(1);
  }

  int hit=atoi(argv[1]);
  char *inifile;
  inifile = argv[2];

  FILE *F1,*F2;
  int i,l,m,n,o,s,p,output;
  double ktmp;
  char OUTFILE[400],filename[400];

  // set this to one to output details about inputs for diagnostics
  output = 0;
  FILE *F;

  set_cosmological_parameters(inifile,1);
  set_survey_parameters(inifile,1);
  set_cov_parameters(inifile,1);

  init_source_sample(redshift.shear_REDSHIFT_FILE,tomo.shear_Nbin);
  init_lens_sample(redshift.clustering_REDSHIFT_FILE,tomo.clustering_Nbin);

  if (covparams.lin_bins){
    printf("covariances_real_flat_fft does not support linear angular binning\nEXIT\n");exit(1);
  }
  if (covparams.full_tomo){
    printf("covariances_real_flat_fft does not support full cross-zbins clustering yet\nEXIT\n");exit(1);
  }

  int NG, cNG;
  if (covparams.ng==1){
    NG = 1;
    if (covparams.cng==1){
      cNG = 1;
    }
    else{
      cNG = 0;
    }
  }
  else {
    NG = 0;
    cNG = 0;
  }

  printf("running multi_covariance_real with NG = %d, cNG = %d\n",NG, cNG);

  like.Ntheta=covparams.ntheta;
  like.vtmin = covparams.tmin;
  like.vtmax = covparams.tmax;
  int Ntheta = like.Ntheta;
  double *thetamin,*dtheta,*theta;
  thetamin=create_double_vector(0,Ntheta);
  dtheta=create_double_vector(0,Ntheta-1);
  theta=create_double_vector(0,Ntheta-1);
  set_angular_binning(thetamin,dtheta);
  for (i =0; i< Ntheta; i++){
    theta[i] = 2./3.*(pow(thetamin[i+1],3.)-pow(thetamin[i],3.))/(pow(thetamin[i+1],2.)-pow(thetamin[i],2.));
  }

  int k=1;
  if (strcmp(covparams.ss,"true")==0)
  {
    sprintf(OUTFILE,"%s_ssss_++_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,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_shear_shear_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,1,1,k);
        }
        k=k+1;
      }
    }
    sprintf(OUTFILE,"%s_ssss_--_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,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_shear_shear_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,0,0,k);
        }
        k=k+1;
      }
    }
    sprintf(OUTFILE,"%s_ssss_+-_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,tomo.shear_Nbin);
    for (l=0;l<tomo.shear_Npowerspectra; l++){
      for (m=0;m<tomo.shear_Npowerspectra; m++){
        if(k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_shear_shear_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,1,0,k);
        }
        k=k+1;
      }
    }
  }
  if (strcmp(covparams.ll,"true")==0)
  {
    sprintf(OUTFILE,"%s_llll_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,tomo.shear_Nbin);
    for (l=0;l<tomo.clustering_Npowerspectra; l++){
      for (m=l;m<tomo.clustering_Npowerspectra; m++){
        if(k==hit){
          sprintf(filename,"%s%s_%d",covparams.outdir,OUTFILE,k);
          run_cov_clustering_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,k);
        }
        k=k+1;
      }
    }
  }
  if (strcmp(covparams.ls,"true")==0)
  {
    sprintf(OUTFILE,"%s_lsls_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,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_ggl_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,k);
        }
        k=k+1;
      }
    }
  }
  if (strcmp(covparams.ls,"true")==0 && strcmp(covparams.ss,"true")==0)
  {
    sprintf(OUTFILE,"%s_lsss_+_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,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_ggl_shear_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,1,k);
        }
        k=k+1;
      }
    }
    sprintf(OUTFILE,"%s_lsss_-_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,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_ggl_shear_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,0,k);
        }
        k=k+1;
      }
    }
  }
  if (strcmp(covparams.ll,"true")==0 && strcmp(covparams.ss,"true")==0)
  {
    sprintf(OUTFILE,"%s_llss_+_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,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_clustering_shear_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,1,k);
        }
        k=k+1;
      }
    }
    sprintf(OUTFILE,"%s_llss_-_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,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_clustering_shear_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,0,k);
        }
        k=k+1;
      }
    }
  }
  if (strcmp(covparams.ll,"true")==0 && strcmp(covparams.ls,"true")==0)
  {
    sprintf(OUTFILE,"%s_llls_cov_Ntheta%d_Ntomo%d",covparams.filename,Ntheta,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_clustering_ggl_real_binned(OUTFILE,covparams.outdir,theta,thetamin,dtheta,Ntheta,l,m,k);
        }
        k=k+1;
      }
    }
  }

  if (hit==0)
  {
    sprintf(OUTFILE,"%s%s",covparams.outdir,covparams.filename);
    // write_gglensing_zbins(OUTFILE);

    sprintf(OUTFILE,"%s%s.blocks",covparams.outdir,covparams.filename);
    F1 = fopen(OUTFILE,"w");
    fprintf(F1,"%d\n",k-1);
    fclose(F1);
  }

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


#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_eigen.h>

#include "egsl.h"

val egsl_sub(val v1,val v2){
	return egsl_sum(v1, egsl_scale(-1.0,v2));
}

val egsl_compose_col(val v1, val v2){
	gsl_matrix *m1 = egsl_gslm(v1);
	gsl_matrix *m2 = egsl_gslm(v2);
	egsl_expect_size(v2, 0, m1->size2);
	val v3 = egsl_alloc(m1->size1+m2->size1,m1->size2);
	gsl_matrix *m3 = egsl_gslm(v3);
	size_t i,j;
	for(j=0;j<m1->size2;j++) {
		for(i=0;i<m1->size1;i++)
			gsl_matrix_set(m3, i, j, gsl_matrix_get(m1,i,j));

		for(i=0;i<m2->size1;i++)
			gsl_matrix_set(m3, m1->size1+i, j, gsl_matrix_get(m2,i,j));
	}
	return v3;
}

val egsl_compose_row(val v1, val v2){
	gsl_matrix *m1 = egsl_gslm(v1);
	gsl_matrix *m2 = egsl_gslm(v2);
	egsl_expect_size(v2, m1->size1, 0);
	val v3 = egsl_alloc(m1->size1, m1->size2 + m2->size2);
	gsl_matrix *m3 = egsl_gslm(v3);
	size_t i,j;
	for(i=0;i<m1->size1;i++) {
		for(j=0;j<m1->size2;j++)
			gsl_matrix_set(m3, i, j, gsl_matrix_get(m1,i,j));

		for(j=0;j<m2->size2;j++)
			gsl_matrix_set(m3, i, m1->size2+j, gsl_matrix_get(m2,i,j));
	}
	return v3;
}

void egsl_add_to(val v1, val v2) {
	gsl_matrix * m1 = egsl_gslm(v1);
	gsl_matrix * m2 = egsl_gslm(v2);
	gsl_matrix_add(m1,m2);
}

void egsl_add_to_col(val v1, size_t j, val v2) {
/*	egsl_print("m1",v1);
	egsl_print("m2",v2); */
	gsl_matrix * m1 = egsl_gslm(v1);
	gsl_matrix * m2 = egsl_gslm(v2);

/*	printf("m1 size = %d,%d j = %d\n",m1->size1,m1->size2,j); */
	egsl_expect_size(v2, m1->size1, 1);
	size_t i;
	for(i=0;i<m1->size1;i++) {
		*gsl_matrix_ptr(m1, i, j) += gsl_matrix_get(m2,i,0);
	}
}

val egsl_copy_val(val v1) {
	gsl_matrix * m1 = egsl_gslm(v1);
	val v2 = egsl_alloc(m1->size1,m1->size2);
	gsl_matrix * m2 = egsl_gslm(v2);
	gsl_matrix_memcpy(m2,m1);
	return v2;
}

val egsl_scale(double s, val v1){
	val v2 = egsl_copy_val(v1);
	gsl_matrix * m2 = egsl_gslm(v2);
	gsl_matrix_scale(m2, s);
	return v2;
}

val egsl_sum(val v1, val v2){
	gsl_matrix * m1 = egsl_gslm(v1);
	gsl_matrix * m2 = egsl_gslm(v2);
	val v3 = egsl_alloc(m1->size1,m1->size2);
	gsl_matrix * m3 = egsl_gslm(v3);
	gsl_matrix_memcpy(m3,m1);
	gsl_matrix_add(m3,m2);
	return v3;
}

val egsl_sum3(val v1, val v2, val v3){
	return egsl_sum(v1, egsl_sum(v2,v3));
}

val egsl_mult(val v1, val v2){
	gsl_matrix * a = egsl_gslm(v1);
	gsl_matrix * b = egsl_gslm(v2);
	val v = egsl_alloc(a->size1,b->size2);
	gsl_matrix * ab = egsl_gslm(v);
	gsl_blas_dgemm(CblasNoTrans,CblasNoTrans,1.0,a,b,0.0,ab);
	return v;
}

val egsl_transpose(val v1){
	gsl_matrix * m1 = egsl_gslm(v1);
	val v2 = egsl_alloc(m1->size2,m1->size1);
	gsl_matrix * m2 = egsl_gslm(v2);
	gsl_matrix_transpose_memcpy(m2,m1);
	return v2;
}

val egsl_inverse(val v1){
	gsl_matrix*A = egsl_gslm(v1);
	val v2 = egsl_alloc(A->size1,A->size1);
	gsl_matrix*invA = egsl_gslm(v2);
	size_t n = A->size1;
	gsl_matrix * m = gsl_matrix_alloc(n,n);
	gsl_matrix_memcpy(m,A);
	gsl_permutation * perm = gsl_permutation_alloc (n);
	/* Make LU decomposition of matrix m */
	int s;
	gsl_linalg_LU_decomp (m, perm, &s);
	/* Invert the matrix m */
	gsl_linalg_LU_invert (m, perm, invA);
	gsl_permutation_free(perm);
	gsl_matrix_free(m);
	return v2;
}

void egsl_symm_eig(val v, double* eigenvalues, val* eigenvectors) {
	gsl_matrix *m = egsl_gslm(v);
	size_t N = m->size1;
	/* Check for v to be square */

	gsl_matrix *A = gsl_matrix_alloc(N,N);
	gsl_matrix_memcpy(A, m);

	gsl_vector *eval = gsl_vector_alloc(N);
	gsl_matrix *evec = gsl_matrix_alloc(N,N);

	gsl_eigen_symmv_workspace * ws = gsl_eigen_symmv_alloc(N);
	gsl_eigen_symmv(A, eval, evec, ws);
	gsl_eigen_symmv_free(ws);

	gsl_eigen_symmv_sort(eval, evec, GSL_EIGEN_SORT_VAL_DESC);

	size_t j;
	for(j=0;j<N;j++) {
		eigenvalues[j] = gsl_vector_get(eval, j);
		eigenvectors[j] = egsl_alloc(N,1);
		size_t i;
		for(i=0;i<N;i++)
			*egsl_atmp(eigenvectors[j],i,0) = gsl_matrix_get(evec,i,j);
	}

	gsl_vector_free(eval);
	gsl_matrix_free(evec);
	gsl_matrix_free(A);
}