/*
Gene program by Dale Swanson May 6, 2007
Simulates the effect of a gene on a simple population.  The gene causes a male that has 1 of it to produce sperms with that gene with greater than normal ammount (50% normal), any male with 2 of the genes will be sterile.  It has no effect on females, but obviously they still carry it.  There is no simulation of competition for food, or anything else, every male, who's not sterile, over the age of 1 will spawn 1 child every year, all animals die at age 5.  The males mate will be a random female, selected at random each year.
*/


#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
boost::random::mt19937 gen;

using namespace std;
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <time.h>
#include <cstring>

char basen[100] = "G"; //needed for fnamecomb
char ext[10] = ".csv"; //needed for fnamecomb
char fname[100]; //needed for fnamecomb

int x, y, z; //general varibles
int pop[9][9][9]; //index of pop, [x][y][z], x = sex (1-male, 2-female), y = number genes, z = age
int mate; //used to pick a random mate
int childgenes; //how many genes the child will have
int odds; //odds that a male with 1 gene's sperms will have the gene (note 50 would be normal)
int turns; //number of turns or years or weeks or whatever the cycle time frame is
int sex; //random sex of child
int poploop; //used to loop through the population every turn
int popsize = 1; //total size of the population
int poptot[9][9]; //total population that has every lived for each sex and for each possible ammount of genes
int popmax; //the highest population ever reached
int debug; //set to 138 to enter debug mode
int history[10000][5][5]; //to record the history of the population
int mateage; //age of the mate
int automate=0; //set to 0 to get require no inputs 
int plimit=10000; //upper limit on population size, not a hard limit, will tend to limit at 2 * poplimit



int ran(int a, int b) //returns a random number between a min, and b max
{// custom random number gen, for no reason
	/*
	time_t sseconds; //start seconds, will be used to seed the rng
    time(&sseconds); //get our seed for rng
	
	int k;
	int range;
	int c = 0; //this is your real random number that will be used to return
	range = (b - a) + 1;
	if (rand() == 138) srand(rand() + clock()); 
	// 1 in 32k odds to reseed the RNG uses both a rand number and the clock (clock isn't seconds, it's system ticks, faster than seconds) 
	if ((rand() + clock()) % 12 == 1) 
	// 1 in 5 chance of looping a wasting some random numbers
	{
		for (k = ((clock() % 4) + ((rand() + clock()) % 7) + (rand() % 18) + 9); k > 0; k--)//comes up with a number to loop from about 10 to 70 times
		{
			rand(); //wastes some random numbers
		}
	}
	c = a + int(range * rand() / (RAND_MAX + 1));
	if (c>32000 && rand() < 5)
	{
		cout<<"\nRand - "<<c<<", Timer - "<<sseconds<<", clock - "<<clock()<<", Time(0) - "<<time(0);
	}
	return c;
	*/
	boost::random::uniform_int_distribution<> dist(a, b);
	return dist(gen);
	
	
	
}


void fnamecomb() //combines basen + the time + ext into fname
{//requires the global char's basen, ext, fname
	time_t tsec; //sets time to tsec
	time(&tsec);
	int tnum;
	char sttim[100];
	tnum = int(tsec);
	
	itoa(tnum, sttim, 10); //sets the string sttime to the value of the current time
	strcpy (fname, basen); //combines the 3
	strcat (fname, sttim);
	strcat (fname, ext);
	return;
}

void setup()
{// sets up the default values, asks user if they want to set custom values
	//index of pop, [x][y][z], x = sex (1-male, 2-female), y = number genes, z = age
	pop[1][0][1] = 5000; //males with 0 genes (1 year old)
	pop[2][0][1] = 5000; //females with 0 genes
	pop[1][1][1] = 25; //males with 1 gene
	pop[2][1][1] = 0; //females with 1 gene
	pop[1][2][1] = 0; //males with 2 gene (sterile)
	pop[2][2][1] = 0; //females with 2 gene
	odds = 86; //odds as a % that a male with 1 gene will produce sperm with that gene (50% is normal)
	int plimit=10000; //upper limit on population size, not a hard limit, will tend to limit at 2 * poplimit

	cout<<"\nEnter 1 to set varibles, 0 to use built in varibles, 2 for an explanation. ";
	cin>>debug;
	
	if (debug == 1)
	{//lets the user set custom values if they want
		cout<<"\nEnter in this order, number of males with 0 gene, 1 gene, 2 gene, then females with 0 gene, 1 gene, 2 gene. ";
		cin>>pop[1][0][1];
		cin>>pop[1][1][1];
		cin>>pop[1][2][1];
		cin>>pop[2][0][1];
		cin>>pop[2][1][1];
		cin>>pop[2][2][1];
		cout<<"\nEnter Odds (as percentage). ";
		cin>>odds;
		cout<<"\nEnter population limit. ";
		cin>>plimit;
		cout<<"\nRequire input - 1 or run automatically - 0.";
		cin>>automate;
	}
	
	else if (debug == 2)
	{//describes what this program does
		cout<<"\n\nSimulates the effect of a gene on a simple population.  The gene cause a male that has 1 of it to produce sperms with that gene with greater than normal ammount (50% normal), any male with 2 of the genes will be sterile.  It has no effect on females, but obviously they still carry it.  There is no simulation of competition for food, or anything else, every male, who's not sterile, over the age of 1 will spawn 1 child every year, all animals die at age 5.  The males mate will be a random female, selected at random each year.";
		cout<<"\nThe program stores the population in an array, with 3 index's, one for sex, genes, and age.  If you see X, Y, and Z anywhere that is what they refer to.  Index of pop, [x][y][z], x = sex (1-male, 2-female), y = number genes, z = age.\n\n";
		system("PAUSE");
	}
}

void age()
{//this is our main loop, it will cycle through each possible value for x, y, and z, then for the males who are able to breed it will give them 1 child
	if (popsize > popmax) popmax = popsize; //if the current population size is higher than the record, stored in popmax, than the current becomes the new record.
	int temppop = popsize; //temp to store pop total since we are setting it to 0
	popsize = 0; //reset popsize since we will be adding it up in this loop
	for (x = 1; x <= 2; x++)
	{//x is sex, cycle through 1 for males first, then 2 for females
		for (y = 0; y <=2; y++)
		{//y is gene count, cycle through each possible amount of genes, from 0-2
			for (z = 5; z>=0; z--)
			{//z is age, cycle through each age, start at 5 and working down to 0, 6 is dead, and 0 is just born
				if (x == 1 && y < 2 && z > 1 && ran(0, temppop) < plimit)
				{//if our animal is male, dosen't have 2 genes, and is over the age of 1 then he is able to mate, so this loop will do it
					for (poploop = 1; poploop <= pop[x][y][z]; poploop++)
					{//poploop will loop through each animal of the current x,y,z stats
						childgenes = 0; //reset child genes, since we will be using it
						mateage = ran(2, 5); //picks the age of our mate at random from 2-5
						if (pop[2][0][mateage] + pop[2][1][mateage] + pop[2][2][mateage] < 1)
						{//if the population of the age we picked for a mate is 0 then we will look for a different age to mate with
							for (mateage = 2; (pop[2][0][mateage] + pop[2][1][mateage] + pop[2][2][mateage] < 1) && (mateage < 6); mateage++) 
							{//loops through all the ages that we can mate with (2-5) starting at 2, stops when we find an age with any animals in it, or when the age is 6 (dead)
								if (debug == 138) cout<<"\nmateage - "<<mateage; //outputs debug info
							}
						}
						if (pop[2][0][mateage] + pop[2][1][mateage] + pop[2][2][mateage] > 1)
						{//if there are animals at the age we have picked for mateage then we will mate, this prevents mating with non existant mates
							mate = ran(1, pop[2][0][mateage] + pop[2][1][mateage] + pop[2][2][mateage]); //mate will be a random number from 1 to the total size of the total female population (of the same age)
							if (mate > pop[2][0][mateage])
							{// tests to see if mate is greater than the size of the female population with 0 genes (if it is then we have picked to mate with a female with either 1 or 2 genes)
								if (mate > pop[2][1][mateage] + pop[2][0][mateage])
								{//if mate is greater than the size of the female pop of 0 genes and 1 genes than we will mate with a female with 2 genes
									childgenes++; //since the female has 2 genes the child must get a gene from her
								}
								
								else
								{//other wise we are still mating with a female with 1 or 2, and since it wasn't 2 it's 1
									if (ran(1, 100) <= 50) childgenes++; //since she has 1 of the gene she has a 50% shot of passing it on
								}
							}
							//Now we've added 1 to childgenes if the mother will be passing the gene to it, now it's time to see if the father will pass it
							if (y == 1)
							{//this will add the gene if the male has 1 copy of it (if he has 2 he can't mate), with odds of whatever our odds were
								if (ran(1, 100) <= odds) childgenes++;
							}
							
							//birth
							sex = ran(1, 2); //pick the sex at random, 1 male, 2 female
							pop[sex][childgenes][0]++; //adds 1 to the population with the sex, and whatever ammount of genes were decided on, 0 years old
							if (debug == 138) cout<<"\nBirth sex - "<<sex<<", childgenes - "<<childgenes<<", mateage - "<<mateage<<", X - "<<x<<", Y - "<<y<<", Z - "<<z; //outputs debug info
						}
					}
				}
				
				//ages the population
				pop[x][y][z + 1] =  pop[x][y][z]; //the current aged population will get moved to the next highest slot in the array, note that if we are on 5 then they just get moved to 6, and forgotten about (die)
				popsize += pop[x][y][z]; //totals the current population, of any stats
				
			}
			pop[x][y][0] = 0; //since we never loop through z = -1 we have to set the population of age 0 to 0 manually
			poptot[x][y] += pop[x][y][6]; //the population at age 6 is the population that just died, we add them to the totals, note that the next group to die will over write, not add to the current group at age 6
		}
	}
}

void results()
{//outputs the history results to screen
	cout<<"\nHistory:\n";
	cout<<"\nYear, Male 0 genes, Male 1 gene, Male 2 genes, Female 0 genes, Female 1 gene, Female 2 genes, Total Male, Total Female, Total 0 genes, Total 1 gene, Total 2 genes, Total";
	cout<<"\nYear\tM 0\tM 1\tM 2\tF 0\tF 1\tF 2\tT M\tT F\tT 0\tT 1\tT 2\tTotal";
	for (x = 0; x <= turns; x++)
	{//loop through each year of the history to diplay the results
		cout<<"\nYear "<<x<<"\t"<<history[x][1][0]<<"\t"<<history[x][1][1]<<"\t"<<history[x][1][2]<<"\t"<<history[x][2][0]<<"\t"<<history[x][2][1]<<"\t"<<history[x][2][2]<<"\t"<<history[x][1][3]<<"\t"<<history[x][2][3]<<"\t"<<history[x][0][0]<<"\t"<<history[x][0][1]<<"\t"<<history[x][0][2]<<"\t"<<history[x][0][3];
	}
}

void recordresults()
{//records the results to a text file
	ofstream textfile (fname); //open the text file, file name was created with fnamecomb
	if (textfile.is_open())
	{//if the file is open then write the results
		textfile<<"Year, M 0, M 1, M 2, F 0, F 1, F 2, T M, T F, T 0, T 1, T 2, T, Odds - "<<odds;
		for (x = 0; x <= turns; x++)
		{//go through each year and output that line to the text file
			textfile<<"\n"<<x<<", "<<history[x][1][0]<<", "<<history[x][1][1]<<", "<<history[x][1][2]<<", "<<history[x][2][0]<<", "<<history[x][2][1]<<", "<<history[x][2][2]<<", "<<history[x][1][3]<<", "<<history[x][2][3]<<", "<<history[x][0][0]<<", "<<history[x][0][1]<<", "<<history[x][0][2]<<", "<<history[x][0][3];
		}
	}
	textfile.close();
}

void display()
{// displays info about the current population
	cout<<"\nTurn - "<<turns<<", Current Population - "<<popsize<<", Max Population - "<<popmax; //turns is years or whatever timeframe you want 
	cout<<"\nX - sex 1 - male, 2 - female, Y - # of genes, Z - age";
	for (x = 1; x <= 2; x++)
	{//x is sex, cycle through 1 for males first, then 2 for females
		for (y = 0; y <=2; y++)
		{//y is gene count, cycle through each possible amount of genes, from 0-2
			for (z = 5; z>=0; z--)
			{//z is age, cycle through each age, start at 5 and working down to 0, 6 is dead, and 0 is just born
				cout<<"\nX - "<<x<<", Y - "<<y<<", Z - "<<z<<", pop - "<<pop[x][y][z]<<", total - "<<poptot[x][y];
				//X, Y, and Z are sex, genes, and age, pop is the current population with those stats, and poptot is the total ammount of population that has ever lived with those stats
			}
		}
	}
	results(); //displays history results on screen
	recordresults(); //writes the results to file, in here so that partial results get recorded everytime we display them
	cout<<"\n";
}

void record()
{//tally up the history populations
	for (x = 1; x <= 2; x++)
	{//x is sex, cycle through 1 for males first, then 2 for females
		for (y = 0; y <=2; y++)
		{//y is gene count, cycle through each possible amount of genes, from 0-2
			for (z = 5; z>=0; z--)
			{//z is age, cycle through each age, start at 5 and working down to 0, 6 is dead, and 0 is just born
				history[turns][x][y] += pop[x][y][z]; //will store x = sex, y = genes, turns is year
				history[turns][0][y] += pop[x][y][z]; //0 will store both sexes
				history[turns][x][3] += pop[x][y][z]; //3 will store all 3 possible gene combos (0, 1, 2)
				history[turns][0][3] += pop[x][y][z]; //0 3 will store the total population for this year
			}
		}
	}
}

int main()
{
    time_t sseconds; //start seconds, will be used to seed the rng
	time_t cseconds; //current seconds, will be used with start seconds to give time elapsed, or whenever current time is used
	time_t lseconds; //last seconds, will be used with current seconds to get time since the last display
    time(&sseconds); //get our seed for rng
    srand((unsigned int) sseconds); //seed rng
	
	
	
	setup(); //setup the varibles
	time(&lseconds); //sets the last seconds so that the results aren't displayed right away (since current seconds will > 0 + 30)
	fnamecomb(); //makes our filename, we have it here so that it only is made once, but we can reopen that file many times and overwrite it
	gen.seed(static_cast<unsigned int>(std::time(0)));
	
	while (popsize > 0)
	{//run the program until the popsize is 0
		time(&cseconds); //current seconds so that we can see how long it's been since we displayed results
		if (turns % 100 == 9 || debug == 138 || cseconds > lseconds + 10)
		{//if you start with a huge pop, or give a low odds the program may run forever, this ensures that you get some feedback, every 100 turns, or if the time since last display is > 10, or every turn in debug mode
			display(); //displays/records results
			if (automate) {system("PAUSE");} //waits for user interaction
			time(&lseconds); //since we are displaying results reset the time to now
		}
		record(); //to record the history of this year
		age(); //age is the main loop that does all the stuff
		turns++; //turns keeps track of how many turns/years have passed
	}
	
	display(); //if the loop is done then the population has died out, time to display the results
	
	cout<<"\nDone...\n";
    system("PAUSE");
    return EXIT_SUCCESS;
}
