/* -*- linux-c -*- */
/* sigma_binsingle.c

   Copyright (C) 2002-2004 John M. Fregeau

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <getopt.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_rng.h>
#include "fewbody.h"
#include "sigma_binsingle.h"

/* print the usage */
void print_usage(FILE *stream)
{
	fprintf(stream, "USAGE:\n");
	fprintf(stream, "  sigma_binsingle [options...]\n");
	fprintf(stream, "\n");
	fprintf(stream, "OPTIONS:\n");
	fprintf(stream, "  -m --m0 <m0/MSUN>             : set mass of single star [%.6g]\n", FB_M0/FB_CONST_MSUN);
	fprintf(stream, "  -n --m10 <m10/MSUN>           : set mass of star 0 of binary [%.6g]\n", FB_M10/FB_CONST_MSUN);
	fprintf(stream, "  -o --m11 <m11/MSUN>           : set mass of star 1 of binary [%.6g]\n", FB_M11/FB_CONST_MSUN);
	fprintf(stream, "  -r --r0 <r0/RSUN>             : set radius of single star [%.6g]\n", FB_R0/FB_CONST_RSUN);
	fprintf(stream, "  -g --r10 <r10/RSUN>           : set radius of star 0 of binary [%.6g]\n", FB_R10/FB_CONST_RSUN);
	fprintf(stream, "  -i --r11 <r11/RSUN>           : set radius of star 1 of binary [%.6g]\n", FB_R11/FB_CONST_RSUN);
	fprintf(stream, "  -a --a1 <a1/AU>               : set semimajor axis of binary [%.6g]\n", FB_A1/FB_CONST_AU);
	fprintf(stream, "  -e --e1 <e1>                  : set eccentricity of binary 0 [%.6g]\n", FB_E1);
	fprintf(stream, "  -v --vinf <vinf/v_crit>       : set velocity at infinity [%.6g]\n", FB_VINF);
	fprintf(stream, "  -P --precision <dsigma/sigma> : set fractional precision on cross\n");
	fprintf(stream, "                                  section [%.6g]\n", FB_PRECISION);
	fprintf(stream, "  -t --tstop <tstop/t_dyn>      : set stopping time [%.6g]\n", FB_TSTOP);
	fprintf(stream, "  -D --dt <dt/t_dyn>            : set approximate output dt [%.6g]\n", FB_DT);
	fprintf(stream, "  -c --tcpustop <tcpustop/sec>  : set cpu stopping time [%.6g]\n", FB_TCPUSTOP);
	fprintf(stream, "  -A --absacc <absacc>          : set integrator's absolute accuracy [%.6g]\n", FB_ABSACC);
	fprintf(stream, "  -R --relacc <relacc>          : set integrator's relative accuracy [%.6g]\n", FB_RELACC);
	fprintf(stream, "  -N --ncount <ncount>          : set number of integration steps between calls\n");
	fprintf(stream, "                                  to fb_classify() [%d]\n", FB_NCOUNT);
	fprintf(stream, "  -z --tidaltol <tidaltol>      : set tidal tolerance [%.6g]\n", FB_TIDALTOL);
	fprintf(stream, "  -x --fexp <f_exp>             : set expansion factor of merger product [%.6g]\n", FB_FEXP);
	fprintf(stream, "  -k --ks                       : turn K-S regularization on or off [%d]\n", FB_KS);
	fprintf(stream, "  -s --seed                     : set random seed [%ld]\n", FB_SEED);
	fprintf(stream, "  -d --debug                    : turn on debugging\n");
	fprintf(stream, "  -V --version                  : print version info\n");
	fprintf(stream, "  -h --help                     : display this help text\n");
}

/* calculate the units used */
int calc_units(fb_obj_t *obj[2], fb_units_t *units)
{
	units->v = sqrt(FB_CONST_G*(obj[0]->m + obj[1]->m)/(obj[0]->m * obj[1]->m) * \
			(obj[1]->obj[0]->m * obj[1]->obj[1]->m / obj[1]->a));
	units->l = obj[1]->a;
	units->t = units->l / units->v;
	units->m = units->l * fb_sqr(units->v) / FB_CONST_G;
	units->E = units->m * fb_sqr(units->v);

	return(0);
}

/* the main attraction */
int main(int argc, char *argv[])
{
	int i, j, k, res, err, sid, bid, precisionmet=0;
	long l, nb;
	unsigned long int seed;
	double m0, m10, m11, r0, r10, r11, a1, e1, precision;
	double rtid, vinf, rperi, b, b0, bmin, bmax, db, bxlast, t, vc;
	fb_sigma_t sigmacurr[11], sigmaprev[11];
	char outcome[11][FB_MAX_STRING_LENGTH];
	/* char string1[FB_MAX_STRING_LENGTH], string2[FB_MAX_STRING_LENGTH]; */
	fb_hier_t hier;
	fb_input_t input;
	fb_ret_t retval;
	fb_units_t units;
	gsl_rng *rng;
	const gsl_rng_type *rng_type=gsl_rng_mt19937;
	const char *short_opts = "m:n:o:r:g:i:a:e:v:P:t:D:c:A:R:N:z:x:k:s:dVh";
	const struct option long_opts[] = {
		{"m0", required_argument, NULL, 'm'},
		{"m10", required_argument, NULL, 'n'},
		{"m11", required_argument, NULL, 'o'},
		{"r0", required_argument, NULL, 'r'},
		{"r10", required_argument, NULL, 'g'},
		{"r11", required_argument, NULL, 'i'},
		{"a1", required_argument, NULL, 'a'},
		{"e1", required_argument, NULL, 'e'},
		{"vinf", required_argument, NULL, 'v'},
		{"precision", required_argument, NULL, 'P'},
		{"tstop", required_argument, NULL, 't'},
		{"dt", required_argument, NULL, 'D'},
		{"tcpustop", required_argument, NULL, 'c'},
		{"absacc", required_argument, NULL, 'A'},
		{"relacc", required_argument, NULL, 'R'},
		{"ncount", required_argument, NULL, 'N'},
		{"tidaltol", required_argument, NULL, 'z'},
		{"fexp", required_argument, NULL, 'x'},
		{"ks", required_argument, NULL, 'k'},
		{"seed", required_argument, NULL, 's'},
		{"debug", no_argument, NULL, 'd'},
		{"version", no_argument, NULL, 'V'},
		{"help", no_argument, NULL, 'h'},
		{NULL, 0, NULL, 0}
	};

	/* set parameters to default values */
	m0 = FB_M0;
	m10 = FB_M10;
	m11 = FB_M11;
	r0 = FB_R0;
	r10 = FB_R10;
	r11 = FB_R11;
	a1 = FB_A1;
	e1 = FB_E1;
	vinf = FB_VINF;
	precision = 1.0e-2;
	input.ks = FB_KS;
	input.tstop = FB_TSTOP;
	input.Dflag = 0;
	input.dt = FB_DT;
	input.tcpustop = FB_TCPUSTOP;
	input.absacc = FB_ABSACC;
	input.relacc = FB_RELACC;
	input.ncount = FB_NCOUNT;
	input.tidaltol = FB_TIDALTOL;
	input.fexp = FB_FEXP;
	seed = FB_SEED;
	fb_debug = FB_DEBUG;

	while ((i = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
		switch (i) {
		case 'm':
			m0 = atof(optarg) * FB_CONST_MSUN;
			break;
		case 'n':
			m10 = atof(optarg) * FB_CONST_MSUN;
			break;
		case 'o':
			m11 = atof(optarg) * FB_CONST_MSUN;
			break;
		case 'r':
			r0 = atof(optarg) * FB_CONST_RSUN;
			break;
		case 'g':
			r10 = atof(optarg) * FB_CONST_RSUN;
			break;
		case 'i':
			r11 = atof(optarg) * FB_CONST_RSUN;
			break;
		case 'a':
			a1 = atof(optarg) * FB_CONST_AU;
			break;
		case 'e':
			e1 = atof(optarg);
			if (e1 >= 1.0) {
				fprintf(stderr, "e0 must be less than 1\n");
				return(1);
			}
			break;
		case 'v':
			vinf = atof(optarg);
			if (vinf < 0.0) {
				fprintf(stderr, "vinf must be non-negative\n");
				return(1);
			}
			break;
		case 'P':
			precision = atof(optarg);
			if (precision <= 0.0) {
				fprintf(stderr, "precision must be > 0\n");
				return(1);
			} else if (precision > 1.0) {
				fprintf(stderr, "precision must be < 1\n");
				return(1);
			}
			break;
		case 't':
			input.tstop = atof(optarg);
			break;
		case 'D':
			input.Dflag = 1;
			input.dt = atof(optarg);
			break;
		case 'c':
			input.tcpustop = atof(optarg);
			break;
		case 'A':
			input.absacc = atof(optarg);
			break;
		case 'R':
			input.relacc = atof(optarg);
			break;
		case 'N':
			input.ncount = atoi(optarg);
			break;
		case 'z':
			input.tidaltol = atof(optarg);
			break;
		case 'x':
			input.fexp = atof(optarg);
			break;
		case 'k':
			input.ks = atoi(optarg);
			break;
		case 's':
			seed = atol(optarg);
			break;
		case 'd':
			fb_debug = 1;
			break;
		case 'V':
			fb_print_version(stdout);
			return(0);
		case 'h':
			fb_print_version(stdout);
			fprintf(stdout, "\n");
			print_usage(stdout);
			return(0);
		default:
			break;
		}
	}

	/* check to make sure there was nothing crazy on the command line */
	if (optind < argc) {
		print_usage(stdout);
		return(1);
	}

	/* initialize a few things for integrator */
	hier.nstarinit = 3;
	fb_malloc_hier(&hier);

	/* initialize GSL rng */
	gsl_rng_env_setup();
	rng = gsl_rng_alloc(rng_type);
	gsl_rng_set(rng, seed);

	/* zero everything out before summing */
	for (i=0; i<11; i++) {
		sigmacurr[i].sigma_r = 0.0;
		sigmacurr[i].dsigma2_r_minus = 0.0;
		sigmacurr[i].dsigma2_r_plus = 0.0;
		sigmacurr[i].sigma_nr = 0.0;
		sigmacurr[i].dsigma2_nr_minus = 0.0;
		sigmacurr[i].dsigma2_nr_plus = 0.0;
		sigmaprev[i].sigma_r = 0.0;
		sigmaprev[i].dsigma2_r_minus = 0.0;
		sigmaprev[i].dsigma2_r_plus = 0.0;
		sigmaprev[i].sigma_nr = 0.0;
		sigmaprev[i].dsigma2_nr_minus = 0.0;
		sigmaprev[i].dsigma2_nr_plus = 0.0;
	}
	/* loop through and calcluate cross sections */
	nb = (long) (1.0/precision);
	while (!precisionmet) {
		/* select b0 that gives r_p=2a */
		vc = sqrt(FB_CONST_G * (m0+m10+m11) / (m0*(m10+m11)) * (m10*m11/a1));
		rperi = 2.0 * a1;
		/* b0 is in code units here (vinf is in units of vc) */
		b0 = sqrt(fb_sqr(rperi) + 2.0 * FB_CONST_G * (m0+m10+m11) * rperi / fb_sqr(vinf*vc)) / a1;
		db = b0/((double) nb);
		bxlast = GSL_POSINF;

		l = 0;
		bmin = ((double) l) * db;
		bmax = bmin + db;
		while (bmax <= b0 || bmax <= 2.0*bxlast) {
			bmin = ((double) l) * db;
			bmax = bmin + db;
			/* choose b uniformly in area from bmin to bmax */
			b = sqrt(gsl_rng_uniform(rng) * (fb_sqr(bmax)-fb_sqr(bmin)) + fb_sqr(bmin));

			/* do scattering experiment */
			/* initialize a few things for integrator */
			t = 0.0;
			hier.nstar = 3;
			fb_init_hier(&hier);

			/* create binary */
			hier.hier[hier.hi[2]+0].obj[0] = &(hier.hier[hier.hi[1]+1]);
			hier.hier[hier.hi[2]+0].obj[1] = &(hier.hier[hier.hi[1]+2]);
			hier.hier[hier.hi[2]+0].t = t;

			/* give the objects some properties */
			for (j=0; j<hier.nstar; j++) {
				hier.hier[hier.hi[1]+j].ncoll = 1;
				hier.hier[hier.hi[1]+j].id[0] = j;
				snprintf(hier.hier[hier.hi[1]+j].idstring, FB_MAX_STRING_LENGTH, "%d", j);
				hier.hier[hier.hi[1]+j].n = 1;
				hier.hier[hier.hi[1]+j].obj[0] = NULL;
				hier.hier[hier.hi[1]+j].obj[1] = NULL;
				hier.hier[hier.hi[1]+j].Eint = 0.0;
				hier.hier[hier.hi[1]+j].Lint[0] = 0.0;
				hier.hier[hier.hi[1]+j].Lint[1] = 0.0;
				hier.hier[hier.hi[1]+j].Lint[2] = 0.0;
			}

			hier.hier[hier.hi[1]+0].R = r0;
			hier.hier[hier.hi[1]+1].R = r10;
			hier.hier[hier.hi[1]+2].R = r11;

			hier.hier[hier.hi[1]+0].m = m0;
			hier.hier[hier.hi[1]+1].m = m10;
			hier.hier[hier.hi[1]+2].m = m11;

			hier.hier[hier.hi[2]+0].m = m10 + m11;

			hier.hier[hier.hi[2]+0].a = a1;
			hier.hier[hier.hi[2]+0].e = e1;

			hier.obj[0] = &(hier.hier[hier.hi[1]+0]);
			hier.obj[1] = &(hier.hier[hier.hi[2]+0]);
			hier.obj[2] = NULL;

			/* get the units and normalize */
			calc_units(hier.obj, &units);
			fb_normalize(&hier, units);

			/* move hierarchies analytically in from infinity along hyperbolic orbit */
			rtid = pow(2.0*(hier.obj[0]->m + hier.obj[1]->m) / (hier.obj[1]->m * input.tidaltol), 1.0/3.0) *
				hier.obj[1]->a * (1.0 + hier.obj[1]->e);
			fb_init_scattering(hier.obj[0], hier.obj[1], vinf, b, rtid);

			/* trickle down the binary properties, then back up */
			fb_randorient(&(hier.hier[hier.hi[2]+0]), rng);
			fb_downsync(&(hier.hier[hier.hi[2]+0]), t);
			/* fb_upsync(&(hier.hier[hier.hi[2]+0]), t); */

			/* call fewbody! */
			retval = fewbody(input, &hier, &t);

			k = -1;
			res = 0;
			err = 0;

			/* analyze outcome */
			if (retval.retval == 1 &&
			    FB_MIN(fabs(retval.DeltaEfrac), fabs(retval.DeltaE)) <= 1.0e-4 &&
			    FB_MIN(fabs(retval.DeltaLfrac), fabs(retval.DeltaL)) <= 1.0e-4) {
				/* check for resonance */
				if (retval.Nosc >= 1) {
					res = 1;
				} else {
					res = 0;
				}
				/* classify outcome */
				if (hier.nstar == 3) {
					if (hier.nobj == 1) {
						/* triples physically forbidden */
						err = 1;
					} else if (hier.nobj == 2) {
						if (fb_n_hier(hier.obj[0])==2) {
							bid = 0;
							sid = 1;
						} else {
							bid = 1;
							sid = 0;
						}
						if (hier.obj[sid]->id[0] == 0) {
							k = 0;
						} else if (hier.obj[sid]->id[0] == 1) {
							k = 1;
						} else {
							k = 2;
						}
					} else { /* hier.nobj == 3 */
						k = 3;
					}
                                } else if (hier.nstar == 2) {
					if (hier.nobj == 1) {
						if ((hier.obj[0]->obj[0]->ncoll==1 && hier.obj[0]->obj[0]->id[0]==0) ||
						    (hier.obj[0]->obj[1]->ncoll==1 && hier.obj[0]->obj[1]->id[0]==0)) {
							k = 4;
						} else if ((hier.obj[0]->obj[0]->ncoll==1 && hier.obj[0]->obj[0]->id[0]==1) ||
							   (hier.obj[0]->obj[1]->ncoll==1 && hier.obj[0]->obj[1]->id[0]==1)) {
							k = 5;
						} else {
							k = 6;
						}
					} else { /* hier.nobj == 2 */
						if ((hier.obj[0]->ncoll==1 && hier.obj[0]->id[0]==0) ||
						    (hier.obj[1]->ncoll==1 && hier.obj[1]->id[0]==0)) {
							k = 7;
						} else if ((hier.obj[0]->ncoll==1 && hier.obj[0]->id[0]==1) ||
							   (hier.obj[1]->ncoll==1 && hier.obj[1]->id[0]==1)) {
							k = 8;
						} else {
							k = 9;
						}
					}
				} else if (hier.nstar == 1) {
					k = 10;
				} else {
					fprintf(stderr, "ERROR: hier.nstar!=1,2,3!\n");
					exit(1);
				}
                        } else { /* bad outcome */
				err = 1;
			}

			/* tally up cross sections */
			if (!err) {
				for (i=0; i<11; i++) {
					if (i==k) {
						if (res) {
							sigmacurr[i].sigma_r += FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin));
							sigmacurr[i].dsigma2_r_minus += fb_sqr(0.1587 * FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin)));
							sigmacurr[i].dsigma2_nr_plus += fb_sqr(0.8413/21.0 * FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin)));
						} else {
							sigmacurr[i].sigma_nr += FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin));
							sigmacurr[i].dsigma2_nr_minus += fb_sqr(0.1587 * FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin)));
							sigmacurr[i].dsigma2_r_plus += fb_sqr(0.8413/21.0 * FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin)));
						}
					} else {
						sigmacurr[i].dsigma2_r_plus += fb_sqr(0.8413/21.0 * FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin)));
						sigmacurr[i].dsigma2_nr_plus += fb_sqr(0.8413/21.0 * FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin)));
					}
				}
			} else {
				for (i=0; i<11; i++) {
					sigmacurr[i].dsigma2_r_plus += fb_sqr(0.8413/22.0 * FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin)));
					sigmacurr[i].dsigma2_nr_plus += fb_sqr(0.8413/22.0 * FB_CONST_PI * (fb_sqr(bmax)-fb_sqr(bmin)));
				}
			}

			/* if the outcome was not a weak flyby, then record last impact parameter */
			if (!err && (k!=0 || (k==0 && res))) {
				bxlast = bmax;
			}

			/*
			fprintf(stderr, "outcome:  retval=%d  %s  (%s)\n", retval.retval,
				fb_sprint_hier(hier, string1), fb_sprint_hier_hr(hier, string2));
			fprintf(stderr, "  DeltaL/L0=%.6g  DeltaL=%.6g\n", retval.DeltaLfrac, retval.DeltaL);
			fprintf(stderr, "  DeltaE/E0=%.6g  DeltaE=%.6g\n", retval.DeltaEfrac, retval.DeltaE);
			fprintf(stderr, "  l=%ld b0=%g bmin=%g bmax=%g bxlast=%g k=%d res=%d err=%d\n", l, b0, bmin, bmax, bxlast, k, res, err);
			*/

			l++;
		}

		/* add current and previous sigmas, and transfer current to previous */

		/* increase resolution geometrically */
		nb *= 2;

		precisionmet = 1;
	}

	snprintf(outcome[0], FB_MAX_STRING_LENGTH, "[1 2] 0 (preservation)");
	snprintf(outcome[1], FB_MAX_STRING_LENGTH, "[0 2] 1 (exchange_1)");
	snprintf(outcome[2], FB_MAX_STRING_LENGTH, "[0 1] 2 (exchange_2)");
	snprintf(outcome[3], FB_MAX_STRING_LENGTH, "0 1 2 (ionization)");
	snprintf(outcome[4], FB_MAX_STRING_LENGTH, "[1:2 0] (merger_binary_12)");
	snprintf(outcome[5], FB_MAX_STRING_LENGTH, "[0:2 1] (merger_binary_02)");
	snprintf(outcome[6], FB_MAX_STRING_LENGTH, "[0:1 2] (merger_binary_01)");
	snprintf(outcome[7], FB_MAX_STRING_LENGTH, "1:2 0 (merger_12)");
	snprintf(outcome[8], FB_MAX_STRING_LENGTH, "0:2 1 (merger_02)");
	snprintf(outcome[9], FB_MAX_STRING_LENGTH, "0:1 2 (merger_01)");
	snprintf(outcome[10], FB_MAX_STRING_LENGTH, "0:1:2 (triple_merger)");

	/* precision has now been met, so print out results */
	fprintf(stdout, "outcome  sigma_r  dsigma_r_minus  dsigma_r_plus  sigma_nr  dsigma_nr_minus  dsigma_nr_plus\n");
	for (i=0; i<11; i++) {
		fprintf(stdout, "%27s  %6.6g  %6.6g  %6.6g  %6.6g  %6.6g  %6.6g\n", outcome[i],
			sigmacurr[i].sigma_r/FB_CONST_PI,
			sqrt(sigmacurr[i].dsigma2_r_minus)/FB_CONST_PI,
			sqrt(sigmacurr[i].dsigma2_r_plus)/FB_CONST_PI,
			sigmacurr[i].sigma_nr/FB_CONST_PI,
			sqrt(sigmacurr[i].dsigma2_nr_minus)/FB_CONST_PI,
			sqrt(sigmacurr[i].dsigma2_nr_plus)/FB_CONST_PI);
	}

	/* free GSL stuff */
	gsl_rng_free(rng);

	/* free our own stuff */
	fb_free_hier(hier);

	/* done! */
	return(0);
}


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_statistics_double.h>
#include <math.h>
#include "tz_error.h"
#include "tz_constant.h"
#include "tz_voxel_linked_list.h"
#include "tz_stack_sampling.h"
#include "tz_voxel_graphics.h"
#include "tz_neurotrace.h"
#include "tz_stack_math.h"
#include "tz_stack_utils.h"
#include "tz_objdetect.h"
#include "tz_voxeltrans.h"
#include "tz_stack_stat.h"
#include "tz_stack_draw.h"
#include "tz_stack_io.h"
#include "tz_stack_bwmorph.h"
#include "tz_stack_bwdist.h"
#include "tz_stack_threshold.h"

INIT_EXCEPTION_MAIN(e)

int main(int argc, char* argv[])
{
  char neuron_name[100];
  if (argc == 1) {
    strcpy(neuron_name, "fly_neuron");
  } else {
    strcpy(neuron_name, argv[1]);
  }

  char file_path[100];

  sprintf(file_path, "../data/%s.tif", neuron_name);
  Stack *signal = Read_Stack(file_path);
  Translate_Stack(signal, GREY, 1);
  Stack *stack = Translate_Stack(signal, COLOR, 0);

  sprintf(file_path, "../data/%s/soma.tif", neuron_name);
  Stack *label = Read_Stack(file_path);
  Stack *label2 = Copy_Stack(label);

  Stack_Threshold(label, 2);
  Stack_Binarize(label);

  Stack_Threshold(label2, 1);
  Stack_Binarize(label2);
  Stack_Xor(label, label2, label2);

  Print_Stack_Info(signal);

  Stack_Label_Color(stack, label, 0.0, 1.0, signal);
  Stack_Label_Color(stack, label2, 4.0, 1.0, signal);

  int i;
  for (i = 0; i < 3; i++) {
    Stack_Channel_Extraction(stack, i, signal);
    sprintf(file_path, "../data/%s/label_%d.tif", neuron_name, i);
    Write_Stack(file_path, signal);
  }

  Kill_Stack(signal);
  Kill_Stack(stack);
  Kill_Stack(label);
  Kill_Stack(label2);

  sprintf(file_path, "../data/%s/traced.tif", neuron_name);
  Stack *canvas = Read_Stack(file_path);
  sprintf(file_path, "../data/%s/soma.tif", neuron_name);
  Stack *soma = Read_Stack(file_path);
  Stack_Blend_Mc_L(canvas, soma, 2, 4.0);
  Stack_Blend_Mc_L(canvas, soma, 3, 1.0);
  sprintf(file_path, "../data/%s/label_color.tif", neuron_name);
  Write_Stack(file_path, canvas);

  return 0;
}


/* gcPresAbs.c - Calculate probability of expression in an mrna
   sample given a cel file of results. Use empiracal rank within
   a gc population to estimate probability and fisher's method
   of combining probabilities to calculate probe set probability.
*/
#include "common.h"
#include "hash.h"
#include "linefile.h"
#include "obscure.h"
#include "options.h"
#include "dystring.h"
#include <gsl/gsl_math.h>
#include <gsl/gsl_cdf.h>

struct row1lq
/* A single row from a affymetrix 1lq file. Also used for
   storing values and probability of expression. */
{
    struct row1lq *next;    /* Next in list. */
    char *psName;           /* Name of probe set.*/
    char *seq;              /* Sequence of probe, in weird affy format. */
    int x;                  /* X coordinate from affy cel file. */
    int y;                  /* Y coordinate from affy cel file. */
    int gcCount;            /* Number of g's and c's in sequence. */
    int repCount;           /* Number of experiments we're looking at. */
    double *intensity;      /* Intensities for each experiment. */
    double *pVal;           /* Pvals for each experiment. */
};

struct psProb
/* Probability that a probe set is expressed. */
{
    struct psProb *next; /* Next in list. */
    char psName[128];    /* Name of probe set. */
    double pVal;         /* P-Value of expression. */
};

struct replicateMap
/* Map the replicates to eachother. */
{
    struct replicateMap *next;  /* Next in list. */
    char *rootName;             /* Root name of replicates. */
    int repCount;               /* Number of replicates. */
    int *repIndexes;            /* Indexes into matrix columns. */
};

struct row1lq **gcBins = NULL;     /* Bins of lists of row1lqs for different amounts of GCs. */
struct row1lq *badBin = NULL;
struct hash *probeSetHash = NULL;  /* Hash of slRefs for each probe set where slRef->val is a row1lq. */
struct hash *killHash = NULL;      /* Hash of probes to be ignored. */
struct hash *outputValsHash= NULL; /* Hash of probe sets to output values for. */
FILE *outputPsSetSeqs = NULL;      /* Output probe sets and sequences used here. */
FILE *outputIntenVals = NULL;       /* File to output intensity for individual values to. */
FILE *outputPvals = NULL;          /* File to output pvals for individual to. */
int minGcBin = 0;                  /* Minimum GC bin. */
int maxGcBin = 0;                  /* Maximum GC bin. */
struct psProb *probePVals = NULL;  /* List of psProbe structs with pVals filled in. */
int minProbeNum = 3;               /* Ignore probe sets that have less than this many probes. */
char *placeHolder = "dummy";       /* Hold a place in a hash. */
boolean combineCols = FALSE;       /* Combine columns that are the same samples? */
int repToSort = 0;                 /* What replicate to sort the gc bins by. */
int maxReplicates = 0;             /* What are the maximum number of replicates. */
int sampleCount = 0;               /* Number of samples. */
int numBadProbes = 0;              /* Number of bad probes counted. */
FILE *gCountsFile = NULL;              /* File to output 'G' counts per probe to. */
FILE *gcCountFile = NULL;             /* File to output 'G' + 'C' counts per probe to. */
FILE *cCountsFile = NULL;              /* File to output 'C' counts per probe. */
boolean doMedianComb = FALSE;          /* Do a median rather than Fisher's combination. */
boolean doCombo = FALSE; /* Do a combination of median and fisher. */

static struct optionSpec optionSpecs[] =
/* Our acceptable options to be called with. */
{
    {"help", OPTION_BOOLEAN},
    {"1lqFile", OPTION_STRING},
    {"outFile", OPTION_STRING},
    {"badProbes", OPTION_STRING},
    {"combineReplicates", OPTION_BOOLEAN},
    {"outputDuplicates", OPTION_BOOLEAN},
    {"testVals", OPTION_BOOLEAN},
    {"countPrefix", OPTION_STRING},
    {"doMedianComb", OPTION_BOOLEAN},
    {"testMedian", OPTION_BOOLEAN},
    {"doCombo", OPTION_BOOLEAN},
    {"outputVals", OPTION_STRING},
    {"outputValsList", OPTION_STRING},
    {"outputPsSetSeqs", OPTION_STRING},
    {NULL,0}
};

static char *optionDescripts[] =
/* Description of our options for usage summary. */
{
    "Display this message.",
    "1lq file from affymetrix specifying probe layout on chip",
    "File to output matrix of probabilities to.",
    "File with sequence of probes that are known to be bad and should be ignored.",
    "Combine experiments with same name prefix.",
    "When used with combineReplicates, outputs a column for each tissue that has same prefix.",
    "Skip everything else and just run inverse chisq on each value degreeFreedom pair on command line.",
    "Output G/C probe countent counts to prefixCounts.tab",
    "Use median rather than Fisher's combination",
    "Test the median code.",
    "Do a combination of medians and fischer's combination.",
    "Output the intensity and probability to file with this prefix.",
    "Only output probe sets in this list.",
    "Output probe sets and sequences in 5'->3' orientation"
};

void usage()
/** Print usage and quit. */
{
int i=0;
warn("gcPresAbs - Calculate probability of expression in an mrna\n"
     "sample given a cel file of results. Use empiracal rank within\n"
     "a gc population to estimate probability and fisher's method\n"
     "of combining probabilities to calculate probe set probability.");
for(i=0; i<ArraySize(optionSpecs) -1; i++)
    fprintf(stderr, "  -%s -- %s\n", optionSpecs[i].name, optionDescripts[i]);
errAbort("\nusage:\n   "
	 "gcPresAbs -1lqFile=chipmousea.1lq -outFile=probeSet.matrix.probs file1.cel file2.cel ... fileN.cel");
}

int psCmp(const void *va, const void *vb)
/* Compare based on probeSet name. */
{
const struct psProb *a = *((struct psProb **)va);
const struct psProb *b = *((struct psProb **)vb);
return strcmp(a->psName, b->psName);
}

int row1lqCmp(const void *va, const void *vb)
/* Compare to sort based on intensity for the
   given repliate. */
{
const struct row1lq *a = *((struct row1lq **)va);
const struct row1lq *b = *((struct row1lq **)vb);
double bI = b->intensity[repToSort];
double aI = a->intensity[repToSort];
if(aI < bI)
    return 1;
if(aI > bI)
    return -1;
return 0;
}

int countGc(char *sequence)
/* Count the numbers of G's+C's in the sequence. */
{
int gCount = 0, cCount =0;
tolowers(sequence);
gCount = countChars(sequence, 'g');
if(gCountsFile)
    fprintf(gCountsFile, "%d\n", gCount);
cCount = countChars(sequence, 'c');
if(cCountsFile)
    fprintf(cCountsFile, "%d\n", cCount);
if(gcCountFile)
    fprintf(gcCountFile, "%d\n", cCount + gCount);
return cCount + gCount;
}

void initGcBins(int minGc, int maxGc)
/* Intialize the gcBins structure for maxGc-minGc bins. */
{
int numBins = maxGc - minGc + 1;
minGcBin = minGc;
maxGcBin = maxGc;
AllocArray(gcBins, numBins);
}

int binForGc(int gcCount)
/* Return the correct bin for this gc count. */
{
int count = 0;
if(gcCount < minGcBin)
    gcCount = minGcBin;
if(gcCount > maxGcBin)
    gcCount = maxGcBin;
return gcCount - minGcBin;
}

void readBadProbes(char *fileName)
/* Read in a list that contains bad probes sequence should be in first row. */
{
struct lineFile *lf = NULL;
char *line = NULL;
killHash = newHash(15);
lf = lineFileOpen(fileName, TRUE);
while(lineFileNextReal(lf, &line))
    {
    tolowers(line);
    hashAdd(killHash, line, placeHolder);
    }
lineFileClose(&lf);
}

void read1lqFile(char *name, struct row1lq ***pRowArray, int *pRowCount)
/* Read in the 1lq popluating the gcBins and hasing by probe set name.
   Need to index rows by a few different ways:
   1) Index in file, for filling in intensity vals from cel files.
   2) Gc Bin for calculating the rank within a set of probes that have same gc content.
   3) Probe set for calculating the probability that the probe set as a whole is expressed.
*/
{
struct lineFile *lf = lineFileOpen(name, TRUE);
struct row1lq **array1lq = NULL;
struct row1lq *rowList = NULL, *row=NULL, *rowNext = NULL;
int i;
char *words[50];
char *line = NULL;
int count =0;
boolean found = FALSE;
struct slRef *refList = NULL, *ref = NULL;
while(lineFileNextReal(lf, &line))
    {
    if(stringIn("X\tY\t", line))
	{
	found = TRUE;
	break;
	}
    }
if(!found)
    errAbort("Couldn't find \"X\tY\t\" in 1lq file: %s", name);

while(lineFileChopNextTab(lf, words, sizeof(words)))
    {
    AllocVar(row);
    row->psName = cloneString(words[5]);
    row->seq = cloneString(words[2]);
    row->x = atoi(words[0]);
    row->y = atoi(words[1]);
    slAddHead(&rowList, row);
    (*pRowCount)++;
    }
slReverse(&rowList);
AllocArray(array1lq, (*pRowCount));

probeSetHash = newHash(15);

for(row = rowList; row != NULL; row = rowNext)
    {
    int bin = -1;
    struct slRef *ref = NULL, *reflist = NULL;

    rowNext = row->next;

    /* Keep track of the row by its index in the file. */
    array1lq[count++] = row;

    /* Skip things that are on the kill list. */
    tolowers(row->seq);
    if(sameString(row->seq, "!") || (killHash != NULL && hashFindVal(killHash, row->seq) != NULL))
	{
	slAddHead(&badBin, row);
	numBadProbes++;
	continue;
	}

    /* Add it to the correct gc bin. */
    row->gcCount = countGc(row->seq);
    bin = binForGc(row->gcCount);
    slAddHead(&gcBins[bin], row);

    /* Create a list of references indexed by the probe sets. */
    AllocVar(ref);
    ref->val = row;
    refList = hashFindVal(probeSetHash, row->psName);
    if(refList == NULL)
	hashAddUnique(probeSetHash, row->psName, ref);
    else
	slAddTail(&refList, ref);
    }

for(i = minGcBin; i < maxGcBin; i++)
    slReverse(&gcBins[i-minGcBin]);

//lineFileClose(&lf);
*pRowArray = array1lq;
}

void fillInMatrix(double **matrix, int celIx, int rowCount, char *celFile)
/* Read in the values of the cel file into matrix column celIx. */
{
struct lineFile *lf = lineFileOpen(celFile, TRUE);
char *words[50];
char *line = NULL;
boolean found = FALSE;
int count =0;
/* Read out the header. */
while(lineFileNextReal(lf, &line))
    {
    if(stringIn("[INTENSITY]", line))
	{
	found = TRUE;
	break;
	}
    }
if(!found)
    errAbort("Couldn't find \"[INTENSITY]\" in cel file: %s", celFile);
count =0;
lineFileNextReal(lf, &line); /* NumberCells... */
lineFileNextReal(lf, &line); /* CellHeader...*/
/* Read in the data. */
while(lineFileChopNext(lf, words, sizeof(words)))
    {
    if(*(words[0]) == '[')
	break;
    assert(count+1 <= rowCount);
    matrix[count][celIx] = atof(words[2]);
    count++;
    if(lf->bytesInBuf == 0)
	warn("Something weird is happening...");
    }
//warn("Closing line file.");
lineFileClose(&lf);
}

int xy2i(int x, int y)
/* Take in the x,y coordinates on the array and
   return the index into the array. */
{
return (712 * y) + x;
}

int doubleComp(const void *a, const void *b)
/* Return difference to sort on doubles. */
{
double x = (*(double *)a);
double y = (*(double *)b);
if(x > y)
    return 1;
else if(y > x)
    return -1;
else
    return 0;
}

/* double quantileArray(double *array, int count, double quantile) */
/* /\* give the quantile at a given position of the array. *\/ */
/* { */
/* double *results = NULL; */
/* boolean isEven = FALSE; */
/* int i = 0; */
/* double quant = 0; */

/* assert(count); */
/* assert(quantile >=0 && quantile <=1); */
/* AllocArray(results, count); */
/* if(floor(count * quantile) == ceil(count *quantile)) */
/*     isEven = TRUE; */

/* if(isEven && (count % 2 != 0)) */
/*     errAbort("Wrong even for quantile %.2f with count %d", quantile, count); */
/* if(!isEven && (count % 2 == 0)) */
/*     errAbort("Wrong odd for quantile %.2f with count %d", quantile, count); */

/* for(i = 0; i < count; i++) */
/*     results[i] = array[i]; */
/* qsort(results, count, sizeof(results[0]), doubleComp); */
/* if(isEven) */
/*     quant = (results[(int)floor(count * quantile)-1] + results[(int)floor(count*quantile)])/2; */
/* else */
/*     quant = results[(int)floor(count * quantile)]; */
/* freez(&results); */
/* return quant; */
/* } */

/* double medianArray(double *array, int count)  */
/* /\* Give the median, or a quantile of an array. *\/ */
/* { */
/* return quantileArray(array, count, quantile); */
/* } */

double medianArray(double *array, int count)
/* give the median of the array. */
{
double *results = NULL;
boolean isEven = FALSE;
int i = 0;
double median = 0;

assert(count);
AllocArray(results, count);
if(count % 2 == 0)
    isEven = TRUE;

for(i = 0; i < count; i++)
    results[i] = array[i];
qsort(results, count, sizeof(results[0]), doubleComp);
if(isEven)
    median = (results[count/2-1] + results[count/2])/2;
else
    median = results[count/2];
freez(&results);
return median;
}

void printDoubleArray(double *array, int count)
/* Print the elements of a double array. */
{
int i = 0;
for(i = 0; i<count; i++)
    printf("%.3f, ", array[i]);
printf("\n");
}

double medianRefList(struct slRef *refList)
/** Return the median value of the row list. */
{
struct slRef *ref = NULL;
struct row1lq *row = NULL;
int count = 0, currentIx = 0;
boolean isEven = FALSE;
double median = 0;
double *results = NULL;
int repIx = 0;

/* How much memory do we need? */
for(ref = refList; ref != NULL; ref = ref->next)
    {
    row = ref->val;
    count += row->repCount;
    }

assert(count > 0);
AllocArray(results, count);
/* Fill in the values. */
for(ref = refList; ref != NULL; ref = ref->next)
    {
    row = ref->val;
    for(repIx = 0; repIx < row->repCount; repIx++)
	{
	results[currentIx++] = row->pVal[repIx];
	}
    }

/* Get the median. */
median = medianArray(results, count);

freez(&results);
return 1 - median;
}

void probeSetCalcPval(void *val)
/* Loop through the slRef list calculating pVal based
   on row1lq->pval using fishers method for combining probabilities.
   Create a psProbe val and add it to the probePVals list. */
{
struct slRef *ref = NULL, *refList = NULL;
struct psProb *ps = NULL;
struct row1lq *row = NULL;
boolean debug = FALSE;
double probProduct = 0;
int probeCount = 0;
int probRepCount = 0;
int i = 0, j = 0;
refList = val;
probeCount = slCount(refList);
if(outputPsSetSeqs != NULL)
    {
    assert(refList);
    row = refList->val;
    fprintf(outputPsSetSeqs, "%s\t", row->psName);
    for(ref = refList; ref != NULL; ref = ref->next)
	{
	row = ref->val;
	reverseBytes(row->seq, strlen(row->seq));
	if(ref->next != NULL)
	    fprintf(outputPsSetSeqs, "%s,", row->seq);
	else
	    fprintf(outputPsSetSeqs, "%s", row->seq);

	reverseBytes(row->seq, strlen(row->seq));
	}
    fprintf(outputPsSetSeqs, "\n");
    }
/* Allocate some memory. */
AllocVar(ps);
row = refList->val;
ps->pVal = 0;
safef(ps->psName, sizeof(ps->psName), "%s", row->psName);

if(probeCount < minProbeNum)
    {
    ps->pVal=0;
    }
else if(doMedianComb)
    {
    ps->pVal = medianRefList(refList);
    }
else
    {
    for(ref = refList; ref != NULL; ref = ref->next)
	{
	row = ref->val;
	if(doCombo)
	    {
	    double med = medianArray(row->pVal, row->repCount);
	    if(med == 0)
		med = .00000000001;
	    probRepCount++;
	    probProduct += log(med);
	    }
	else
	    {
	    for(i = 0; i < row->repCount; i++)
		{
		probRepCount++;
		if(row->pVal[i] != 0)
		    probProduct = probProduct + log(row->pVal[i]);
		else
		    probProduct = probProduct + log(.000000001);
		}
	    }
	}
/* Fisher's method for combining probabilities. */
    ps->pVal = gsl_cdf_chisq_P(-2*probProduct,2*probRepCount);
    }
if(ps->pVal > 1 || ps->pVal < 0)
    warn("%s pVal is %.10f, wrong!");
slAddHead(&probePVals, ps);
}

doInvChiSq(int argc, char *argv[])
/* Test routine to make sure that gsl_cdf_chisq_P() is working correctly. */
{
int i = 0;
double result = 0;
for(i = 1; i < argc; i+=2)
    {
    result = gsl_cdf_chisq_P(atof(argv[i]), atoi(argv[i+1]));
    printf("%s\t%s\t%.2f\n", argv[i], argv[i+1], result);
    }
}

void printIntensities(struct row1lq *rowList, int limit)
/* Loop through and print out the row intensity for a list. */
{
struct row1lq *row = NULL;
int count = 0;
if(limit == -1)
    limit = slCount(rowList);

for(row = rowList; count < limit && row != NULL; row = row->next)
    {
    fprintf(stdout, "%.1f\n", row->intensity[repToSort]);
    count++;
    }
}

void fillInProbeSetPvals(double **matrix, int rowCount, int colCount, int repCount, int *repIndexes,
			 int sampleIx, struct row1lq **row1lqArray, double ***probeSetPValsP,
			 char ***probeSetNamesP, int *psCountP, double **probMatrix)
/* Calculate and fill in the probe set probabilities of expression given
   the data in the matrix. */
{
int i = 0, j = 0;
int rank = 0;
int binCount = 0;
struct psProb *probe = NULL;
struct row1lq *row = NULL;
int psCount = 0;

/* Fill in the values for the rows. */
for(i = 0; i < rowCount; i++)
    {
    row1lqArray[i]->repCount = repCount;
    for(j = 0; j < repCount; j++)
	{
	row1lqArray[i]->intensity[j] = matrix[i][repIndexes[j]];
	}
    }
/* Sort the gc bins by their rank and fill
   in the pVals as an empiracal rank. */
for(i = minGcBin; i <= maxGcBin; i++)
    {
    /* Sort for each replicate. */
    for(j = 0; j < repCount; j++)
	{
	rank = 1;
	repToSort = j;
	slSort(&gcBins[i-minGcBin], row1lqCmp);
	binCount = slCount(gcBins[i-minGcBin]);
	for(row = gcBins[i-minGcBin]; row != NULL; row = row->next)
	    {
	    row->pVal[j] = ((double)rank)/binCount;
	    probMatrix[xy2i(row->x, row->y)][repIndexes[j]] = row->pVal[j];
	    rank++;
	    }
	}
    }
/* Do the bad bin. */
for(row = badBin; row != NULL; row = row->next)
    {
    for(j = 0; j < repCount; j++)
	{
	row->pVal[j] = 0;
	}
    }
hashTraverseVals(probeSetHash, probeSetCalcPval);
if(outputPsSetSeqs == NULL)
    carefulClose(&outputPsSetSeqs);
slSort(&probePVals, psCmp);
psCount = slCount(probePVals);
/* If this is the first time through Allocate some memory. */
if(*probeSetPValsP == NULL)
    {
    (*psCountP) = psCount;
    AllocArray((*probeSetNamesP), psCount);
    AllocArray((*probeSetPValsP), psCount);
    for(i = 0; i < psCount; i++)
	{
	AllocArray((*probeSetPValsP)[i], sampleCount);
	}
    i = 0;
    for(probe = probePVals; probe != NULL; probe = probe->next)
	{
	(*probeSetNamesP)[i++] = cloneString(probe->psName);
	}
    }

i = 0;
for(probe = probePVals; probe != NULL; probe = probe->next)
    {
    (*probeSetPValsP)[i++][sampleIx] = probe->pVal;
    }
slFreeList(&probePVals);
}

struct replicateMap *createReplicateMap(char **expNames, int expCount)
/* Group replicates together if required or
   just make a replicate for each one if no replicates
   are required. */
{
struct replicateMap *rMapList = NULL, *rMap = NULL;
int i = 0, j = 0;
boolean combineReplicates = optionExists("combineReplicates");
boolean *used = NULL;
char expBuff[256], nameBuff[256];
char *mark = NULL;

/* If we're combining results look for
   expNames that have the same root (string before
   first ".") */
if(combineReplicates)
    {
    AllocArray(used, expCount);
    for(i = 0; i < expCount; i++)
	{
	/* If we haven't seen this experiment before
	   start a new replicateMap. */
	if(!used[i])
	    {
	    safef(nameBuff, sizeof(nameBuff), "%s", expNames[i]);
	    mark = strchr(nameBuff, '.');
	    if(mark != NULL)
		*mark = '\0';
	    AllocVar(rMap);
	    used[i] = TRUE;
	    rMap->rootName = cloneString(nameBuff);
	    rMap->repCount = 1;
	    AllocArray(rMap->repIndexes, expCount);
	    rMap->repIndexes[0] = i;

	    /* Loop through the experiments finding the ones
	       that are replicates. */
	    for(j = i+1; j < expCount; j++)
		{
		safef(nameBuff, sizeof(nameBuff), "%s", expNames[j]);
		mark = strchr(nameBuff, '.');
		if(mark != NULL)
		    *mark = '\0';
		if(sameString(nameBuff, rMap->rootName))
		    {
		    used[j] = TRUE;
		    rMap->repIndexes[rMap->repCount++] = j;
		    }
		}
	    maxReplicates = max(maxReplicates, rMap->repCount);
	    slAddHead(&rMapList, rMap);
	    }
	}
    }
else /* Make each expName a new replicate. */
    {
    for(i = 0; i < expCount; i++)
	{
	AllocVar(rMap);
	rMap->rootName = cloneString(expNames[i]);
	rMap->repCount = 1;
	AllocArray(rMap->repIndexes, rMap->repCount);
	rMap->repIndexes[0] = i;
	maxReplicates = max(maxReplicates, rMap->repCount);
	slAddHead(&rMapList, rMap);
	}
    }

slReverse(&rMapList);
/* Do a summmary of the replicates... */
/* for(rMap = rMapList; rMap != NULL; rMap = rMap->next) */
/*     { */
/*     printf("%s\t", rMap->rootName); */
/*     for(i = 0; i < rMap->repCount; i++) */
/* 	{ */
/* 	printf("%s,", expNames[rMap->repIndexes[i]]); */
/* 	} */
/*     printf("\n"); */
/*     } */
return rMapList;
}

boolean reportThisPSet(char *name)
/* Return TRUE if thes pset should be outputed for intensity and pvals. */
{
if(outputValsHash == NULL)
    return TRUE;
return hashLookup(outputValsHash, name) != NULL;
}

void outputPvalValues(struct row1lq **row1lqArray, double **probMatrix,
		      char **expNames, int rowCount, int colCount)
/* Write out the pvals of the probe sets over all the tissues. */
{
int rowIx = 0, colIx = 0;
struct row1lq *row = NULL;
assert(outputPvals);
/* Print column headers. */
fprintf(outputPvals, "%s\t", "probeSetName");
for(colIx = 0; colIx < colCount-1; colIx++)
    fprintf(outputPvals, "%s\t", expNames[colIx]);
fprintf(outputPvals
, "%s\n", expNames[colIx]);

/* Print all the data. */
for(rowIx = 0; rowIx < rowCount; rowIx++)
    {
    row = row1lqArray[rowIx];
    if(reportThisPSet(row->psName))
	{
	fprintf(outputPvals,"%d\t%s",rowIx, row->psName);
	for(colIx = 0; colIx < colCount; colIx++)
	    {
	    fprintf(outputPvals, "\t%.5f", probMatrix[rowIx][colIx]);
	    }
	fputc('\n', outputPvals);
	}
    }
}

void outputValues(double **matrix, struct row1lq **row1lqArray, char **expNames, int rowCount, int colCount)
/* Write out the intensity values for each probe. */
{
int rowIx = 0, colIx = 0;
struct row1lq *row = NULL;
assert(outputIntenVals);
fprintf(outputIntenVals, "%s\t", "probeSetName");
for(colIx = 0; colIx < colCount-1; colIx++)
    fprintf(outputIntenVals, "%s\t", expNames[colIx]);
fprintf(outputIntenVals, "%s\n", expNames[colIx]);

for(rowIx = 0; rowIx < rowCount; rowIx++)
    {
    row = row1lqArray[rowIx];
    if(reportThisPSet(row->psName))
	{
	assert(rowIx == xy2i(row->x, row->y));
 	fprintf(outputIntenVals, "%d\t%s", rowIx, row->psName);
/* 	fprintf(outputIntenVals, "%s\t%s\t%d\t%d\t%d\t%d", row->psName, row->seq, row->x, row->y, rowIx, xy2i(row->x, row->y)); */
	for(colIx = 0; colIx < colCount; colIx++)
	    fprintf(outputIntenVals, "\t%.1f", matrix[rowIx][colIx]);
	fputc('\n', outputIntenVals);
	}
    }
}

void gcPresAbs(char *outFile, char *file1lq, int celCount, char *celFiles[])
/* Output all of the probe sets and their pvals for expression as
   calculated from the intensities in the cel files. */
{
struct row1lq **rowArray = NULL, *row=NULL;
char **expNames = NULL, **expOutNames = NULL;
double **matrix = NULL, **probMatrix = NULL;
double **probeSetPVals = NULL; /* Matrix of pVals of expression with probe sets ordered altphabetically. */
char **probeSetNames = NULL;   /* List of probe set names indexing the probeSetPVals matrix. */
char *badProbeName = NULL;     /* Name of file with bad probes. */
int psCount = 0;
int i=0, j=0, k=0;
int rowCount=0;
FILE *out = NULL;
struct replicateMap *rMapList = NULL, *rMap = NULL;
boolean outputDuplicates = optionExists("outputDuplicates");
dotForUserInit(1);
initGcBins(8,17); /* Determined by where most of the data lives...
		     On the altmousea chip the data drops of significantly
		     before 8 (15,732 3% <= 8) and after 18 (9636 2% >=18) */

/* See if we have a kill list. */
badProbeName = optionVal("badProbes", NULL);
if(badProbeName != NULL)
    {
    warn("Reading the kill list");
    readBadProbes(badProbeName);
    }

/* Read in a 1lq file and put it in an array, lists and hash
   all at the same time. */
warn("Reading 1lq file: %s", file1lq);
read1lqFile(file1lq, &rowArray, &rowCount);

/* Lets initialize some memory. */
AllocArray(expNames, celCount);
AllocArray(expOutNames, celCount);
AllocArray(matrix, rowCount);
AllocArray(probMatrix, rowCount);
for(i=0; i<rowCount; i++)
    {
    AllocArray(matrix[i], celCount);
    AllocArray(probMatrix[i], celCount);
    }

/* Read in the cel files. */
for(i=0; i<celCount; i++)
    {
    char *lastSlash = NULL;
    dotForUser();
    expNames[i] = cloneString(celFiles[i]);
    lastSlash = strrchr(expNames[i], '/');
    if(lastSlash != NULL)
	expNames[i] = lastSlash+1;
    fillInMatrix(matrix, i, rowCount, celFiles[i]);
    }

if(optionExists("outputVals"))
    outputValues(matrix, rowArray, expNames, rowCount, celCount);
rMapList = createReplicateMap(expNames, celCount);
sampleCount = slCount(rMapList);
/* Allocate enough memory for the maximum number of replicates. */
for(i = 0; i < rowCount; i++)
    {
    AllocArray(rowArray[i]->intensity, maxReplicates);
    AllocArray(rowArray[i]->pVal, maxReplicates);
    }

warn("Calculating pVals");
//for(i=0; i<celCount; i++)
i = 0;
for(rMap = rMapList; rMap != NULL; rMap = rMap->next)
    {
    dotForUser();
    for(j = 0; j < rMap->repCount; j++)
	expOutNames[i+j] = cloneString(expNames[rMap->repIndexes[j]]);
    fillInProbeSetPvals(matrix, rowCount, celCount, rMap->repCount, rMap->repIndexes, i++, rowArray,
			&probeSetPVals, &probeSetNames, &psCount, probMatrix);
    }
if(optionExists("outputVals"))
    outputPvalValues(rowArray, probMatrix, expOutNames, rowCount, celCount);

warn("Done.");
warn("Found %d bad probes.", numBadProbes);
warn("Writing out the results.");
out = mustOpen(outFile, "w");

if(outputDuplicates)
    {
    /* Output each name, this only works if duplicates are
       next to each other. */
    for(rMap = rMapList; rMap->next != NULL; rMap = rMap->next)
	{
	for(i = 0; i < rMap->repCount; i++)
	    fprintf(out, "%s\t", expNames[rMap->repIndexes[i]]);
	}
    for(i = 0; i < rMap->repCount - 1; i++)
	fprintf(out, "%s\t", expNames[rMap->repIndexes[i]]);
    fprintf(out, "%s\n", expNames[rMap->repIndexes[i]]);

    for(i = 0; i < psCount; i++)
	{
	fprintf(out, "%s\t", probeSetNames[i]);
	for(j = 0; j < sampleCount; j++)
	    {
	    /* Output a column for each repetition. */
	    rMap = slElementFromIx(rMapList, j);
	    for(k = 0; k < rMap->repCount -1; k++)
		fprintf(out, "%.5g\t", probeSetPVals[i][j]);

	    /* If this is the last repetion then print the newline
	       otherwise continue with tabs. */
	    if(j == sampleCount -1)
		fprintf(out, "%.5g\n", probeSetPVals[i][j]);
	    else
		fprintf(out, "%.5g\t", probeSetPVals[i][j]);
	    }
	}
    }
else /* Only print one column per rMap. */
    {
    for(rMap = rMapList; rMap->next != NULL; rMap = rMap->next)
	fprintf(out, "%s\t", rMap->rootName);
    fprintf(out, "%s\n", rMap->rootName);

    for(i = 0; i < psCount; i++)
	{
	fprintf(out, "%s\t", probeSetNames[i]);
	for(j = 0; j < sampleCount -1; j++)
	    fprintf(out, "%.5g\t", probeSetPVals[i][j]);
	fprintf(out, "%.5g\n", probeSetPVals[i][j]);
	}
    }
carefulClose(&out);
warn("Done");
}

void initCountFiles(char *prefix)
/* open files for probe counts. */
{
struct dyString *dy = newDyString(1024);
dyStringClear(dy);
dyStringPrintf(dy, "%s.gcCounts.tab", prefix);
gcCountFile = mustOpen(dy->string, "w");

dyStringClear(dy);
dyStringPrintf(dy, "%s.gCounts.tab", prefix);
gCountsFile = mustOpen(dy->string, "w");

dyStringClear(dy);
dyStringPrintf(dy, "%s.cCounts.tab", prefix);
cCountsFile = mustOpen(dy->string, "w");
dyStringFree(&dy);
}

void closeCountFiles()
/* Close the counting file handles. */
{
carefulClose(&gcCountFile);
carefulClose(&gCountsFile);
carefulClose(&cCountsFile);
}

void setupTestRow(struct slRef **refList, double *array, int count)
/* Create a test row and add it to the list. */
{
int i = 0;
struct row1lq *row = NULL;
struct slRef *ref = NULL;
AllocVar(row);
row->repCount = count;
AllocArray(row->pVal, row->repCount);
for(i = 0; i < count; i++)
    row->pVal[i] = array[i];

AllocVar(ref);
ref->val = row;
slAddHead(refList, ref);
}

boolean sameDoubles(double a, double b)
/* return TRUE if two doubles are the same to 6 significant
   digits. */
{
char buffA[256];
char buffB[256];
safef(buffA, sizeof(buffA), "%.6f", a);
safef(buffB, sizeof(buffB), "%.6f", b);
return sameString(buffA, buffB);
}

void testMedian()
/* Quick testing of the median function. */
{
struct slRef *refList = NULL;
int count = 0, i = 0;
double first[] = {.7, .8, .9, .6, .5};
double second[] = {.8, .7, .6};
double third[] = {.6, .5};
double median = 0;
/* set up the first guy. */
setupTestRow(&refList, first, ArraySize(first));

median = medianRefList(refList);
if(!sameDoubles(median, .3))
    errAbort("Median should be .3, but it isn't.");
setupTestRow(&refList, second, ArraySize(second));

median = medianRefList(refList);
if(!sameDoubles(median, .3))
    errAbort("Median should still be .3, but it isn't.");
setupTestRow(&refList, third, ArraySize(third));
median = medianRefList(refList);
if(!sameDoubles(median, .35))
    errAbort("Median should be .7, but it isn't.");
warn("Passed all median tests.");
exit(0);
}

void initOutputValsList()
/* Read in the output vals list from the file and inititalize
   the outputValsHash. */
{
struct lineFile *lf = NULL;
char *s = NULL;
char *fileName = optionVal("outputValsList", NULL);
assert(fileName);
lf = lineFileOpen(fileName, TRUE);
outputValsHash = newHash(10);
while(lineFileNextReal(lf, &s))
    hashAdd(outputValsHash, s, NULL);
lineFileClose(&lf);
}

void initOutputVals()
/* Open two files for writing the probe level intensities
   and probabilities to. */
{
struct dyString *s = newDyString(512);
char *filePrefix = optionVal("outputVals", NULL);
assert(filePrefix);
dyStringPrintf(s, "%s.inten", filePrefix);
outputIntenVals = mustOpen(s->string, "w");
dyStringClear(s);
dyStringPrintf(s, "%s.pvals", filePrefix);
outputPvals = mustOpen(s->string, "w");
dyStringFree(&s);
}

int main(int argc, char *argv[])
/* Everybodys favorite function. */
{
char *affy1lqName = NULL;
char *outFileName = NULL;
int origCount = argc;
optionInit(&argc, argv, optionSpecs);
if(optionExists("testMedian"))
    testMedian();
if(optionExists("help") || origCount < 3)
    usage();
if(optionExists("countPrefix"))
    initCountFiles(optionVal("countPrefix", NULL));
if(optionExists("doMedianComb"))
    doMedianComb = TRUE;
if(optionExists("doCombo"))
    doCombo = TRUE;
if(optionExists("outputValsList"))
    initOutputValsList();
if(optionExists("outputVals"))
    initOutputVals();
if(optionExists("outputPsSetSeqs"))
    outputPsSetSeqs = mustOpen(optionVal("outputPsSetSeqs", NULL), "w");
affy1lqName = optionVal("1lqFile", NULL);
outFileName = optionVal("outFile", NULL);
if(optionExists("testVals"))
    doInvChiSq(argc, argv);
else if(affy1lqName == NULL || outFileName == NULL)
    errAbort("Need to specify 1lqFile and outFile, use -help for usage.");
else
    gcPresAbs(outFileName, affy1lqName, argc-1, argv+1);

if(optionExists("countPrefix"))
    closeCountFiles();
return 0;
}


/*
 * Copyright (c) 2018-2021 Aleksas Mazeliauskas, Stefan Floerchinger,
 *                    Eduardo Grossi, and Derek Teaney
 * All rights reserved.
 *
 * FastReso is distributed under MIT license;
 * see the LICENSE file that should be present in the root
 * of the source distribution, or alternately available at:
 * https://github.com/amazeliauskas/FastReso/
 */
#ifndef FASTRESO_TFastReso_AZYHYDRO_h
#define FASTRESO_TFastReso_AZYHYDRO_h

#include "TFastReso.h"
#include <fstream>
#include <memory>
#include <map>
#include <vector>
#include <string>
#include <gsl/gsl_integration.h>

// The class storing the properties of particles and their irreducible distrubtion
// function components f_i.  Printing out routines are also defined there.
#include "TParticle_AZYHYDRO.h"

// List of parameters for GSL integration procedure.
#include "qag_params.h"

// List of parameters controlling the momentum discretization of irreducible components
// See also TParticle_AZYHYDRO class definition
#include "grid_params.h"

//! TFastReso_AZYHYDRO class is the top wrapper class of the fast freeze-out implementation.
class TFastReso_AZYHYDRO: public TFastReso {
  private:
    //! Data vector of TParticle_AZYHYDRO class of all read-in particles
    //! See TParticle_AZYHYDRO class definition.
    std::vector <std::unique_ptr<TParticle_AZYHYDRO>> fParticleData;

    //! Map between the particle name and its position in fParticleData vector;
    std::map <int, int> fIndexTable;

  public:
    //! reads the inputfile of resonances and creates TParticle_AZYHYDRO entry in fParticleData vector.
    //! This routine should be edited by the user to match their input file format.

    void read_particles_data(std::string inputname, std::string comps="Feq Fshear Fbulk Ftemp Fvel", bool verbose=false) override;

    //! The function call to initialized the particle distribution functions in fParticleData. Input parameters
    //! are the freeze-out temperature, chemical potential and (optionally), speed of sound (only needed for bulk perturbations).
    void do_thermal(double Tfo, double MuB=0, double MuI3=0, double MuS=0, double MuC=0, double Cs2=0.14) override;
    void do_thermal(int pi, double Tfo, double QMu=0, double Cs2 = 0.14 );

    //! Read in the input file of resonance decays and perfom the decays on the fly. It is important that decays
    //! are mass ordered to included all feeddown contributions to lower mass resonances.
    //! The table format is the same as decays.data in THERMINATOR 2 	[arXiv:1102.0273]
    //! Father Child1 Child2 (optional Child3) BranchingRatio ClebschGordanCoeff
    //! Note that ClebschGordanCoeff term is 0 or 1 identifying if the branching ration needs
    //! to be reweighted by actual Clebsch Gordon coefficients.
    void do_decays(std::string inputname, bool verbose=false) override;
  public:
    //! routine to get the pointer to TParticle_AZYHYDRO class for a particle particle name"
    TParticle_AZYHYDRO * getParticleByPDG(int pdgid){return fParticleData[fIndexTable[pdgid]].get();};
    TParticle_AZYHYDRO * getParticle(int i){return (i<0) ? fParticleData[fParticleData.size()+i].get()  : fParticleData[i].get();};
    int getParticleIndexByPDG(int pdgid){return fIndexTable[pdgid];};

    //! The printout of the calculated components is done by printing procedures in TParticle_AZYHYDRO class
};

#endif


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_integration.h>

#include "core_allvars_grid.h"
#include "core_proto_grid.h"

int32_t init(void)
{
  int32_t i, status;

  sum_photons = 0.0;

  random_generator = gsl_rng_alloc(gsl_rng_ranlxd1);
  gsl_rng_set(random_generator, 42);	 // start-up seed

  set_units(); // Change units to code units where necessary.
  srand((unsigned) time(NULL));

  read_snap_list(); // Read the snapshots required.

  for(i = 0; i < Snaplistlen; i++)
  {
    ZZ[i] = 1 / AA[i] - 1; // Redshift array.
    Age[i] = time_to_present(ZZ[i]); // Age array.
  }

  if (fescPrescription == 0)
  {
    printf("\n\nUsing a constant escape fraction of %.4f\n", fesc);
  }
  else if (fescPrescription == 1)
  {
    fprintf(stderr, "\n\nDeprecated! Use a new one!\n");
    return EXIT_FAILURE;
  }
  else if (fescPrescription == 2)
  {
    printf("\n\nUsing an fesc prescription that scales with halo mass.\n\n");
    determine_fesc_constants();
  }
  else if (fescPrescription == 3)
  {
    printf("\n\nUsing an fesc prescription that scales with the fraction of ejected mass in the galaxy.\n");
    printf("\nThis takes the form A*fej + B with A = %.4e and B = %.4e\n", alpha, beta);
  }
  else if (fescPrescription == 4)
  {
    printf("\n\nUsing an fesc prescription that depends upon quasar activity.\n");
    printf("\nFor a galaxy that had a quasar event within %.2f dynamical times go the escape fraction will be %.2f.  Otherwise it will have a constant value of %.2f\n", N_dyntime, quasar_boosted, quasar_baseline);
  }
  else if (fescPrescription == 5)
  {
    printf("\n\nUsing Anne's functional form for an escape fraction that decreases for increasing halo mass.\n");
    XASSERT(fesc_low > fesc_high, "Input file contain fesc_low = %.2f and fesc_high = %.2f. For this prescription (fescPrescription == 5), we require fesc_low > fesc_high\n", fesc_low, fesc_high);
  }
  else if (fescPrescription == 6)
  {
    printf("\n\nUsing Anne's functional form for an escape fraction that increases for increasing halo mass.\n");
    XASSERT(fesc_low < fesc_high, "Input file contain fesc_low = %.2f and fesc_high = %.2f. For this prescription (fescPrescription == 6), we require fesc_low < fesc_high\n", fesc_low, fesc_high);
  }
  else if (fescPrescription == 7)
  {
    printf("\n\nUsing an fesc prescription that scales as a power law with the fraction of ejected gas.\n");
    determine_fesc_constants();
  }
  else
  {
    printf("\n\nOnly escape fraction prescriptions 0 to 6 (exlucding 1) are permitted.\n");
    return EXIT_FAILURE;
  }

  Grid = malloc(sizeof(struct GRID_STRUCT));
  if (Grid == NULL)
  {
    fprintf(stderr, "Could not allocate memory for the high level grid structure\n");
    return EXIT_FAILURE;
  }

  status = init_grid(Grid);
  if (status == EXIT_FAILURE)
  {
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;

  QuasarEventsAbovePartCut = 0;
  QuasarEventsBelowPartCut = 0;

}

int32_t init_grid(struct GRID_STRUCT *grid)
{

#define ALLOCATE_GRID_MEMORY(name, length) \
{                                          \
  name = calloc(length, sizeof(*(name)));  \
  if (name == NULL)                        \
  {                                        \
    fprintf(stderr, "Out of memory allocating %ld bytes, could not allocate"#name".\n", sizeof(*(name)* length)); \
    return EXIT_FAILURE;                   \
  }                                        \
}

  int32_t i;
  uint64_t cell_idx;

  if (grid == NULL)
  {
    fprintf(stderr, "init_grid was called with a GRID_STRUCT pointer that has not been initialized\n");
    return EXIT_FAILURE;
  }

  grid->GridSize = GridSize;
  grid->NumCellsTotal = CUBE(GridSize);

  grid->NumGrids = NGrid;

  grid->GridProperties = malloc(sizeof(struct GRID_PROPERTIES_STRUCT) * grid->NumGrids);
  if (grid->GridProperties == NULL)
  {
    fprintf(stderr, "Could not allocate memory for the grid properties struct\n");
    return EXIT_FAILURE;
  }

  for (i = 0; i < grid->NumGrids; ++i)
  {
#ifdef MPI
    printf("Allocating grid %d on Task %d.\n", i, ThisTask);
#endif

    /* For now the self-consistent model only using the number of ionizing photons. */
    /* Also use the number of galaxies for sanity check. */

    ALLOCATE_GRID_MEMORY(grid->GridProperties[i].Nion_HI, grid->NumCellsTotal);
    ALLOCATE_GRID_MEMORY(grid->GridProperties[i].GalCount, grid->NumCellsTotal);

    if (self_consistent == 0)
    {
      ALLOCATE_GRID_MEMORY(grid->GridProperties[i].SFR, grid->NumCellsTotal);
      ALLOCATE_GRID_MEMORY(grid->GridProperties[i].StellarMass, grid->NumCellsTotal);
      ALLOCATE_GRID_MEMORY(grid->GridProperties[i].Nion_HeI, grid->NumCellsTotal);
      ALLOCATE_GRID_MEMORY(grid->GridProperties[i].Nion_HeII, grid->NumCellsTotal);

    }

    // All the memory has been allocated for the inner grid.
    // Now let's initialize the values.

    for (cell_idx = 0; cell_idx < Grid->NumCellsTotal; ++cell_idx)
    {
      grid->GridProperties[i].Nion_HI[cell_idx] = 0.0;
      grid->GridProperties[i].GalCount[cell_idx] = 0;

      if (self_consistent == 0)
      {
        grid->GridProperties[i].SFR[cell_idx] = 0.0;
        grid->GridProperties[i].StellarMass[cell_idx] = 0.0;
        grid->GridProperties[i].Nion_HeI[cell_idx] = 0.0;
        grid->GridProperties[i].Nion_HeII[cell_idx] = 0.0;

      }
    }

  }

  printf("All grids have been initialized\n");
  return EXIT_SUCCESS;
}

int32_t free_grid(void)
{

  int32_t i;

  for (i = 0; i < Grid->NumGrids; ++i)
  {
    free(Grid->GridProperties[i].Nion_HI);
    free(Grid->GridProperties[i].GalCount);

    if (self_consistent == 0)
    {
      free(Grid->GridProperties[i].Nion_HeII);
      free(Grid->GridProperties[i].Nion_HeI);
      free(Grid->GridProperties[i].StellarMass);
      free(Grid->GridProperties[i].SFR);
    }
  }

  free(Grid->GridProperties);
  free(Grid);

  return EXIT_SUCCESS;

}

void set_units(void)
{

  UnitTime_in_s = UnitLength_in_cm / UnitVelocity_in_cm_per_s;
  UnitTime_in_Megayears = UnitTime_in_s / SEC_PER_MEGAYEAR;
  G = GRAVITY / pow(UnitLength_in_cm, 3) * UnitMass_in_g * pow(UnitTime_in_s, 2);
  UnitDensity_in_cgs = UnitMass_in_g / pow(UnitLength_in_cm, 3);
  UnitPressure_in_cgs = UnitMass_in_g / UnitLength_in_cm / pow(UnitTime_in_s, 2);
  UnitCoolingRate_in_cgs = UnitPressure_in_cgs / UnitTime_in_s;
  UnitEnergy_in_cgs = UnitMass_in_g * pow(UnitLength_in_cm, 2) / pow(UnitTime_in_s, 2);

  // convert some physical input parameters to internal units
  Hubble = HUBBLE * UnitTime_in_s;

  // compute a few quantitites
  RhoCrit = 3 * Hubble * Hubble / (8 * M_PI * G);

}

void read_snap_list(void)
{
  FILE *fd;
  char fname[MAXLEN];

  snprintf(fname, MAXLEN, "%s", FileWithSnapList);

  if(!(fd = fopen(fname, "r")))
  {
    printf("can't read output list in file '%s'\n", fname);
    ABORT(0);
  }

  Snaplistlen = 0;
  do
  {
    if(fscanf(fd, " %lg ", &AA[Snaplistlen]) == 1)
      Snaplistlen++;
    else
      break;
  }
  while(Snaplistlen < MAXSNAPS);

  fclose(fd);

#ifdef MPI
  if(ThisTask == 0)
#endif
  printf("found %d defined times in snaplist\n", Snaplistlen);
}

double time_to_present(double z)
{
#define WORKSIZE 1000
  gsl_function F;
  gsl_integration_workspace *workspace;
  double time, result, abserr;

  workspace = gsl_integration_workspace_alloc(WORKSIZE);
  F.function = &integrand_time_to_present;

  gsl_integration_qag(&F, 1.0 / (z + 1), 1.0, 1.0 / Hubble,
    1.0e-8, WORKSIZE, GSL_INTEG_GAUSS21, workspace, &result, &abserr);

  time = 1 / Hubble * result;

  gsl_integration_workspace_free(workspace);

  // return time to present as a function of redshift
  return time;
}

double integrand_time_to_present(double a, void *param)
{
  return 1 / sqrt(Omega / a + (1 - Omega - OmegaLambda) + OmegaLambda * a * a);
}

void determine_fesc_constants(void)
{

  double A, B, log_A;

  log_A = (log10(fesc_high) - (log10(fesc_low)*log10(MH_high)/log10(MH_low))) * pow(1 - (log10(MH_high) / log10(MH_low)), -1);
  B = (log10(fesc_low) - log_A) / log10(MH_low);
  A = pow(10, log_A);

  alpha = A;
  beta = B;

  printf("Fixing the points (%.4e, %.2f) and (%.4e, %.2f)\n", MH_low, fesc_low, MH_high, fesc_high);
  printf("This gives a power law with constants A = %.4e, B = %.4e\n", alpha, beta);

}
/*
void estimate_grid_memory(void)
{

  if (Verbose == 1)
  {
    printf("The size of the Grid Struct (that contains all the pointers) is %lu bytes\nEach grid cell has a struct, in addition to data that the pointers point to.\n", sizeof(struct GRID));
    printf("The total memory taken up by the Grid will be the sizeof a grid cell (%lu bytes) times the number of grid cells (%d) times the number of output snapshots (%d) times 2 because we need memory for both the pointers for the grid and the actual numbers themselves.\n", sizeof(struct GRID), CUBE(GridSize), NGrid);
  }

  printf("Approximate total memory for the grid ===== %lu mb\n", sizeof(struct GRID)*CUBE(GridSize)*NGrid*2/1024/1024);

}

void estimate_gal_memory(int NtotGals)
{
  if (Verbose == 1)
  {
    printf("The size of the Galaxy struct is %lu bytes and the size of the GalaxyGrid struct (that contains all the pointers for the SnapShot history) is %lu\n", sizeof(struct GALAXY_INPUT), sizeof(struct GALAXY_GRID));
    printf("The total memory taken up by this file of galaxies will be the sizeof the galaxy input (%lu bytes) times the number of galaxies (%d) plus the sizeof the galaxygrid (%lu bytes) times the number of snapshots in the simulation (%d) times the number of galaxies (%d)\n", sizeof(struct GALAXY_INPUT), NtotGals, sizeof(struct GALAXY_GRID), LastSnapShotNr, NtotGals);
  }

  printf("Approximate memory occupied for this file of galaxies ===== %lu mb\n", (sizeof(struct GALAXY_INPUT)*NtotGals + sizeof(struct GALAXY_GRID)*NtotGals*LastSnapShotNr)/1024/1024);

}
*/


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

int
main (void)
{
  int i, j;
  gsl_matrix * m = gsl_matrix_alloc (10, 3);

  for (i = 0; i < 10; i++)
    for (j = 0; j < 3; j++)
      gsl_matrix_set (m, i, j, 0.23 + 100*i + j);

  for (i = 0; i < 100; i++)  /* OUT OF RANGE ERROR */
    for (j = 0; j < 3; j++)
      printf ("m(%d,%d) = %g\n", i, j,
              gsl_matrix_get (m, i, j));

  gsl_matrix_free (m);

  return 0;
}


/**
 *
 * @file pcunmqr_blgtrd.c
 *
 *  PLASMA auxiliary routines
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Azzam Haidar
 * @date 2011-05-15
 * @generated c Tue Jan  7 11:45:13 2014
 *
 **/
#include <lapacke.h>
#include <cblas.h>
#include "common.h"
#undef REAL
#define COMPLEX

#define E(_m, _n) (E   + (_m) + LDE * (_n) )
#define V(_m)     (V   + (_m))
#define T(_m)     (T   + (_m))
#define TAU(_m)   (TAU + (_m))
/***************************************************************************
 *  Parallel apply Q2 from bulgechasing matrices - static scheduling
 *  Lower case is treated
 **/
    /*
     * side == PlasmaLeft:
     *     meaning apply E = Q*E = (q_1*q_2*.....*q_n) * E ==> so
     *     traverse Vs in reverse order (forward) from q_n to q_1 Also
     *     E is splitten by block of col over cores because each apply
     *     consist in a block of row (horizontal block)
     */
    /*
     * side == PlasmaRight:
     *     meaning apply E = E*Q = E * (q_1*q_2*.....*q_n) ==> so
     *     traverse Vs in normal order (forward) from q_1 to q_n Also
     *     E is splitten by block of row over core because each apply
     *     consist in a block of col (vertical block)
     */
/***************************************************************************/
void plasma_pcunmqr_blgtrd(plasma_context_t *plasma)
{
    int my_core_id = PLASMA_RANK;
    int cores_num  = plasma->world_size;

    /*===========================*/
    int N, NB, NE, LDE, Vblksiz, WANTZ;
    PLASMA_enum side;
    PLASMA_enum trans;
    PLASMA_Complex32_t *V;
    PLASMA_Complex32_t *T;
    PLASMA_Complex32_t *TAU;
    PLASMA_Complex32_t *E;
    PLASMA_sequence *sequence;
    PLASMA_request *request;

    /*===========================
     *   local variables
     *===========================*/
    PLASMA_Complex32_t *WORK;
    int LDT, LDV;
    int Vm, Vn, mt, nt;
    int myrow, mycol, blkj, blki;
    int firstrow, nbcolinvolvd;
    int blkid, vpos, taupos, tpos;
    int chunkid, nbchunk, colpercore, corest, corelen, len, col;
    int coreid, allcoresnb, maxrequiredcores;
    int lchunkid, rchunkid, halfchunk, nbportion, sw;
    int LWORK;
    int standalonework = 0 ;
    int versionL, versionR;

    plasma_unpack_args_14(side, trans, N, NB, NE, Vblksiz, WANTZ, V, T, TAU, E, LDE, sequence, request);
    if (sequence->status != PLASMA_SUCCESS)
        return;

    /* Quick return */
    if ( N == 0 ) {
        return;
    }
    if ( NB == 0 ) {
        return;
    }
    if ( NE == 0 ) {
        return;
    }
    /* ==========================================
     * some infor for developer
     * Initialisation and checking nb of cores
     * ==========================================*/
    /* we have to 2 algo for left (113 114) and 2 algo for right (91 92)
     * which correspond to versionL versionR.
     * They ae very similar (detail explained in tech report and matlab code)
     * however version 114 and 92 improve locality.
     * while version 113 is used in case WNATZ=1 (construct Q2) which allow
     * the construction to be done in an optimized way taking into
     * consideration that the matrix is Identity so making less flops.
     *
     */
    versionL = 113;
    versionR = 92;
    LDT      = Vblksiz;
    LDV      = NB+Vblksiz-1;
    /* use colpercore =  N/cores_num; :if i want to split E into
     * cores_num chunk so I will have large chunk for each core.
     * However I prefer to split E into chunk of small size where
     * I guarantee that blas3 occur and the size of chunk remain into
     * cache, I think it is better. than I will have different chunk of
     * small size per core and i will do each chunk till the end and
     * then move to the second one for data locality
     *
     * version v1: for each chunck it apply all the V's then move to
     *                    the other chunck. the locality here inside each
     *                    chunck meaning that thread "t" apply V_k then move
     *                    to V_k+1 which overlap with V_k meaning that the
     *                    E_k+1 overlap with E_k. so here is the
     *                    locality however thread t had to read V_k+1 and
     *                    T_k+1 at each apply. note that all thread if they
     *                    run at same speed they might reading the same V_k
     *                    and T_k at the same time.
     *
     * version v2: for each V_k and T_k thread t apply those V_k and
     *                    T_k to E_k for all its chunck, then move to V_k+1
     *                    T_k+1 and apply them to E_k+1 on all the chunck,
     *                    and so on. the difference is that, thread keep V_k
     *                    and T_K while move over the E_k.
     *                    both version look like similar in perf.
     *
     *  THIS IS v1 CODE
     * */

    if(WANTZ==1)
        colpercore =  plasma_ceildiv(NE,2*cores_num);
    else
        colpercore =  plasma_ceildiv(NE,cores_num);

    if(colpercore>1000)
        colpercore =  plasma_ceildiv(colpercore,10);
    else if(colpercore>800)
        colpercore =  plasma_ceildiv(colpercore,8);
    else if(colpercore>600)
        colpercore =  plasma_ceildiv(colpercore,6);
    else if(colpercore>400)
        colpercore =  plasma_ceildiv(colpercore,4);
    else if(colpercore>200)
        colpercore =  plasma_ceildiv(colpercore,2);
    if(colpercore>200)
        colpercore=120;
    if(colpercore<30)
        colpercore=32;
    /*colpercore = N make the code sequential running on thread=0;*/
    nbchunk          =  plasma_ceildiv(NE, colpercore);
    allcoresnb       = cores_num;
    maxrequiredcores = nbchunk;
    if( maxrequiredcores < 1 )
        maxrequiredcores = 1;
    if(allcoresnb > maxrequiredcores)
        allcoresnb = maxrequiredcores;

#if defined (ENABLE_DEBUG)
    if(my_core_id==0)
        printf("  APPLY Q_v1   parallel with threads %d   nbchunk %d  colpercore %d  N %d   NB %d   Vblksiz %d SIDE %c TRANS %c versionL %d versionR %d WANTZ %d \n",
               cores_num,nbchunk,colpercore,N,NB,Vblksiz,lapack_const(side),lapack_const(trans),versionL,versionR,WANTZ);
#endif
    /* =========================================
     * case NB = 1  special case.
     * just make the element real for complex
     * =========================================
     */
    if ( NB == 1 ) {
        /* NOTE in CASE USED STANDALONE FUNCTION
         * In COMPLEX matrix Z need to be scaled by the TAU
         * generated during the make off-diagonal elements real
         */
        /*
         * In case where the first stage has been done we are sure
         * that all element of E are real so no need to go through
         * NB=1.  However in case where this function need to be used
         * for only band matrices meaning only stage2 has been called
         * then it require to make all off-diagonal elements real and
         * so remove the return from here and from the bulgechasing
         * function
         */
        if(WANTZ==1){
            PLASMA_Complex32_t zone = 1.0;
            memset(E, 0, LDE*N*sizeof(PLASMA_Complex32_t));
            for(sw=0; sw<NE; sw++)
                E[sw+sw*LDE] = zone;
        }
        if(standalonework==0){
            return;
        }
        else{
#ifdef COMPLEX
            for (chunkid = 0; chunkid<nbchunk; chunkid++) {
                coreid  = chunkid%allcoresnb;
                corest  = chunkid*colpercore;
                corelen = min(colpercore, (NE-(chunkid*colpercore)));
                if( my_core_id==coreid ){
                    if( side==PlasmaLeft ){
                        for (mycol =1; mycol<NE; mycol++){
                            cblas_cscal(corelen, TAU(mycol), E(mycol, corest), LDE);
                        }
                    }else{
                        PLASMA_Complex32_t ztmp;
                        /*Upper case need to be checked*/
                        for (mycol = corest; mycol<corest+corelen; mycol++){
                            ztmp = conjf(*TAU(mycol));
                            cblas_cscal(N, &ztmp, E(0,mycol), 1);
                        }
                    }
                }
            }
#endif
            return;
        }
    }
    /* =========================================
     *       case NB >1  main code
     * =========================================
     */
    LWORK = 2*colpercore*max(Vblksiz,64);
    WORK  = (PLASMA_Complex32_t*) plasma_private_alloc(plasma, LWORK, PlasmaComplexFloat);
    /* WANTZ = 1 meaning E is IDENTITY so form Q using optimized update.
     *         So we use the reverse order from small q to large one,
     *         so from q_n to q_1 so Left update to Identity.
     *         Use version 113 because in 114 we need to update the whole matrix and not in icreasing order.
     * WANTZ = 2 meaning E is a full matrix and need to be updated from Left or Right so use normal update
     * */
    if ( WANTZ==1 ) {
        versionL = 113;
        halfchunk = plasma_ceildiv(nbchunk,2);
        for (lchunkid = 0; lchunkid<halfchunk; lchunkid++) {
            rchunkid  = (nbchunk-1) - lchunkid;
            nbportion = lchunkid == rchunkid ? 1 : 2;
            /* sw is the switch between left and right side chunk
             * it is used to have same load distribution of work */

            for (sw = 0; sw<nbportion; sw++) {
                chunkid = sw == 0 ? lchunkid : rchunkid;
                coreid  = lchunkid%allcoresnb;
                corest  = chunkid*colpercore;
                corelen = min(colpercore, (NE-(chunkid*colpercore)));
                if( my_core_id == coreid ) {
                    /*
                     * Version 113:
                     * loop over the block_col (nt) and for each find the
                     * number of tiles (mt) in this block_col. then loop over mt, find
                     * the size of the V's(Vm,Vn) and apply it to the corresponding
                     * portion of E.
                     */
                    nt  = plasma_ceildiv((N-1),Vblksiz);
                    for (blkj=nt-1; blkj>=0; blkj--) {
                        /* the index of the first row on the top of block (blkj) */
                        firstrow = blkj * Vblksiz + 1;
                        /*find the number of tile for this block */
                        if( blkj == nt-1 )
                            mt = plasma_ceildiv( N -  firstrow,    NB);
                        else
                            mt = plasma_ceildiv( N - (firstrow+1), NB);
                        /*loop over the tiles find the size of the Vs and apply it */
                        for (blki=mt; blki>0; blki--) {
                            /*calculate the size of each losange of Vs= (Vm,Vn)*/
                            myrow     = firstrow + (mt-blki)*NB;
                            mycol     = blkj*Vblksiz;
                            Vm = min( NB+Vblksiz-1, N-myrow);
                            if( ( blkj == nt-1 ) && ( blki == mt ) ){
                                Vn = min (Vblksiz, Vm);
                            } else {
                                Vn = min (Vblksiz, Vm-1);
                            }
                            /*calculate the pointer to the Vs and the Ts.
                             * Note that Vs and Ts have special storage done
                             * by the bulgechasing function*/
                            findVTpos(N,NB,Vblksiz,mycol,myrow, &vpos, &taupos, &tpos, &blkid);
                            if((Vm>0)&&(Vn>0)){
                                col = max(mycol,corest);
                                len = corelen - (col-corest);
                                if(side==PlasmaLeft){
                                    if( len > 0 )
                                        CORE_clarfb_gemm(
                                            side, trans,
                                            PlasmaForward, PlasmaColumnwise,
                                            Vm, len, Vn, V(vpos), LDV, T(tpos), LDT, E(myrow,col), LDE,  WORK, len);
                                }
                                else{
                                    if( len > 0 )
                                        CORE_clarfb_gemm(
                                            side, trans,
                                            PlasmaForward, PlasmaColumnwise,
                                            len, Vm, Vn, V(vpos), LDV, T(tpos), LDT, E(col, myrow), LDE,  WORK, len);
                                }
                            }
                        }
                    }
                } /* END my_core_id=coreid */
            } /* END of sw  */
        } /* END loop over the chunk */
    } /* END if WANTZ=1 */
    else{
        /*
         * WANTZ != 1
         */
        for (chunkid = 0; chunkid<nbchunk; chunkid++) {
            coreid  = chunkid%allcoresnb;
            corest  = chunkid*colpercore;
            corelen = min(colpercore, (NE-(chunkid*colpercore)));
            if(corelen < 0)
                corelen = 0;
            if( my_core_id == coreid ) {
                /*
                 * PlasmaLeft
                 */
                //if( side == PlasmaLeft ) {
                if( versionL == 113 ) {
                    /*
                     * Version 113:
                     * loop over the block_col (nt) and for each find the
                     * number of tiles (mt) in this block_col. then loop over mt, find
                     * the size of the V's(Vm,Vn) and apply it to the corresponding
                     * portion of E.
                     */
                    if( versionL == 113 ) {
                        nt  = plasma_ceildiv((N-1),Vblksiz);
                        for (blkj=nt-1; blkj>=0; blkj--) {
                            /* the index of the first row on the top of block (blkj) */
                            firstrow = blkj * Vblksiz + 1;
                            /*find the number of tile for this block */
                            if( blkj == nt-1 )
                                mt = plasma_ceildiv( N -  firstrow,    NB);
                            else
                                mt = plasma_ceildiv( N - (firstrow+1), NB);
                            /*loop over the tiles find the size of the Vs and apply it */
                            for (blki=mt; blki>0; blki--) {
                                /*calculate the size of each losange of Vs= (Vm,Vn)*/
                                myrow     = firstrow + (mt-blki)*NB;
                                mycol     = blkj*Vblksiz;
                                Vm = min( NB+Vblksiz-1, N-myrow);
                                if( ( blkj == nt-1 ) && ( blki == mt ) ){
                                    Vn = min (Vblksiz, Vm);
                                } else {
                                    Vn = min (Vblksiz, Vm-1);
                                }
                                /*calculate the pointer to the Vs and the Ts.
                                 * Note that Vs and Ts have special storage done
                                 * by the bulgechasing function*/
                                findVTpos(N,NB,Vblksiz,mycol,myrow, &vpos, &taupos, &tpos, &blkid);
                                if((Vm>0)&&(Vn>0)){
                                    if( side == PlasmaLeft ){
                                        CORE_clarfb_gemm(
                                            PlasmaLeft, trans,
                                            PlasmaForward, PlasmaColumnwise,
                                            Vm, corelen, Vn, V(vpos), LDV, T(tpos), LDT, E(myrow,corest), LDE,  WORK, corelen);

                                    }else{
                                        CORE_clarfb_gemm(
                                            PlasmaRight, trans,
                                            PlasmaForward, PlasmaColumnwise,
                                            corelen, Vm, Vn, V(vpos), LDV, T(tpos), LDT, E(corest, myrow), LDE,  WORK, corelen);
                                    }
                                }
                            }
                        }
                    }
                    /*
                     * Version 114:
                     * loop over the block_row (mt) and for each find diagonally the
                     * number of tiles (nt) in this block_row. then loop over nt, find
                     * the size of the V's(Vm,Vn) and apply it to the corresponding
                     * portion of E.
                     */
                    else {
                        mt    = plasma_ceildiv((N-1),NB);
                        for (blki = mt; blki>0; blki--) {
                            /* nbcolinvolvd = number of column corresponding to this block_row (blki) */
                            nbcolinvolvd = min(N-1, blki*NB);
                            /*find the number of tile for this block (diagonal row of tiles) */
                            nt = plasma_ceildiv(nbcolinvolvd,Vblksiz);
                            /*loop over the tiles find the size of the Vs and apply it */
                            for (blkj = nt-1; blkj>=0; blkj--) {
                                /* the index of the first row of the first col meaning
                                 * the block on the top left (blki) */
                                firstrow = (mt-blki)*NB+1;
                                /*calculate the size of each losange of Vs= (Vm,Vn)*/
                                myrow    = firstrow + blkj*Vblksiz;
                                mycol    = blkj*Vblksiz;
                                Vm = min( NB+Vblksiz-1, N-myrow);
                                if( ( blkj == nt-1 ) && ( blki == mt ) ){
                                    Vn = min (Vblksiz, Vm);
                                }else{
                                    Vn = min (Vblksiz, Vm-1);
                                }
                                /*calculate the pointer to the Vs and the Ts.
                                 * Note that Vs and Ts have special storage done
                                 * by the bulgechasing function*/
                                findVTpos(N,NB,Vblksiz,mycol,myrow, &vpos, &taupos, &tpos, &blkid);
                                if((Vm>0)&&(Vn>0)) {
                                    CORE_clarfb_gemm(
                                        PlasmaLeft, trans,
                                        PlasmaForward, PlasmaColumnwise,
                                        Vm, corelen, Vn, V(vpos), LDV, T(tpos), LDT, E(myrow,corest), LDE,  WORK, corelen);
                                }
                            }
                        }
                    }
                }
                /*
                 * PlasmaRight
                 */
                else {
                    /*
                     * Version 91:
                     */
                    if( versionR == 91 ) {
                        nt  = plasma_ceildiv((N-1),Vblksiz);
                        for (blkj=0; blkj<nt; blkj++) {
                            /* the index of the first myrow on the top of block (blkj) */
                            firstrow = blkj * Vblksiz + 1;
                            /*find the number of tile for this block */
                            if( blkj == nt-1 )
                                mt = plasma_ceildiv( N -  firstrow,    NB);
                            else
                                mt = plasma_ceildiv( N - (firstrow+1), NB);
                            /*loop over the tiles find the size of the Vs and apply it */
                            for (blki=1; blki<=mt; blki++) {
                                /*calculate the size of each losange of Vs= (Vm,Vn)*/
                                myrow  = firstrow + (mt-blki)*NB;
                                Vm = min( NB+Vblksiz-1, N-myrow);
                                if( ( blkj == nt-1 ) && ( blki == mt ) )
                                {
                                    Vn = min (Vblksiz, Vm);
                                }else{
                                    Vn = min (Vblksiz, Vm-1);
                                }
                                /*calculate the pointer to the Vs and the Ts.
                                 * Note that Vs and Ts have special storage done
                                 * by the bulgechasing function*/
                                mycol     = blkj*Vblksiz;
                                findVTpos(N,NB,Vblksiz,mycol,myrow, &vpos, &taupos, &tpos, &blkid);
                                if((Vm>0)&&(Vn>0)){
                                    CORE_clarfb_gemm(
                                        PlasmaRight,
                                        trans,
                                        PlasmaForward,
                                        PlasmaColumnwise,
                                        corelen, Vm, Vn, V(vpos), LDV, T(tpos), LDT, E(corest,myrow), LDE,  WORK, corelen);
                                }
                            }
                        }
                    }
                    /*trans
                     * Version 92:
                     */
                    else {
                        mt    = plasma_ceildiv((N-1),NB);
                        for (blki = 1; blki<=mt; blki++) {
                            /* nbcolinvolvd = number of column corresponding to this block_row (blki) */
                            nbcolinvolvd = min(N-1, blki*NB);
                            /*find the number of tile for this block (diagonal row of tiles) */
                            nt = plasma_ceildiv(nbcolinvolvd,Vblksiz);
                            /*loop over the tiles find the size of the Vs and apply it */
                            for (blkj = 0; blkj<nt; blkj++) {
                                /* the index of the first row of the first col meaning
                                 * the block on the top left (blki) */
                                firstrow = (mt-blki)*NB+1;
                                /*calculate the size of each losange of Vs= (Vm,Vn)*/
                                myrow    = firstrow + blkj*Vblksiz;
                                mycol    = blkj*Vblksiz;
                                Vm = min( NB+Vblksiz-1, N-myrow);
                                if( ( blkj == nt-1 ) && ( blki == mt ) ){
                                    Vn = min (Vblksiz, Vm);
                                }else{
                                    Vn = min (Vblksiz, Vm-1);
                                }
                                /*calculate the pointer to the Vs and the Ts.
                                 * Note that Vs and Ts have special storage done
                                 * by the bulgechasing function*/
                                findVTpos(N,NB,Vblksiz,mycol,myrow, &vpos, &taupos, &tpos, &blkid);
                                if((Vm>0)&&(Vn>0)) {
                                    CORE_clarfb_gemm(
                                        PlasmaRight, trans,
                                        PlasmaForward, PlasmaColumnwise,
                                        corelen, Vm, Vn, V(vpos), LDV, T(tpos), LDT, E(corest,myrow), LDE,  WORK, corelen);
                                }
                            }
                        }
                    }
                }
            } /* END my_core_id=coreid */
        } /* END loop over the chunk */
    } /* END ELSE of WANTZ == 1 */
    plasma_private_free(plasma, WORK);
}
#undef E
#undef V
#undef T
#undef TAU
/***************************************************************************/
#undef COMPLEX