if (branchUpdateThreshold == 0)
{
	branchUpdateThreshold = 10;
}

crapAIC 		 = -sortedScores[0][0];

fprintf (stdout, "Baseline Model Fit\nc-AIC = ", crapAIC , "\nStarting the GA now...\n");

startTimer		 = Time (1);

if (MPI_NODE_COUNT>1)
{
	MPINodeState = {MPI_NODE_COUNT-1,3};
}

/*---------------------------------------------------------------------------*/

function addNumbers (n1,n2,digits,theBase)
{
	n3 			 = {1,digits};
	
	carryOverBit = 0;
	for (litter = digits-1; litter >=0 ; litter = litter - 1)
	{
		carryOverBit = n1[litter]+n2[litter]+carryOverBit;
		if (carryOverBit>=theBase)
		{
			n3[litter] = carryOverBit-theBase;
			carryOverBit = 1;
		}
		else
		{
			n3[litter] = carryOverBit;
			carryOverBit = 0;
		}
	}
	return n3;
}

/*---------------------------------------------------------------------------*/

function multNumbersByDig (n1,digits,theBase,mBy)
{
	_n3 			 = {1,digits+1};
	
	carryOverBit = 0;
	for (litter = digits; litter >0 ; litter = litter - 1)
	{
		carryOverBit = n1[litter-1]*mBy+carryOverBit;
		if (carryOverBit>=theBase)
		{
			_n3[litter] = carryOverBit%theBase;
			carryOverBit = carryOverBit$theBase;
		}
		else
		{
			_n3[litter] = carryOverBit;
			carryOverBit = 0;
		}
	}
	_n3[0] = carryOverBit;
	
	return _n3;
}

/*---------------------------------------------------------------------------*/

function multNumbers (n1,n2,digits,digits2,digits3,theBase)
{
	n3 			 = {1,digits2};
	
	shift = digits2-digits-1;
	
	cachedDigs = {};
	
	for (litterOut = digits3-1; litterOut >=0; litterOut = litterOut - 1)
	{
		d = n1[litterOut];
		if (d)
		{
			dv = cachedDigs[d];
			if (Abs(dv) == 0)
			{
				dv = multNumbersByDig (n2,digits,theBase,d);
				cachedDigs[d] = dv;
			}
			
			carryOverBit = 0;
			for (litter = digits; litter >=0 ; litter = litter - 1)
			{
				carryOverBit = n3[litter+shift]+dv[litter]+carryOverBit;
				if (carryOverBit>=theBase)
				{
					n3[litter+shift] = carryOverBit-theBase;
					carryOverBit = 1;
				}
				else
				{
					n3[litter+shift] = carryOverBit;
					carryOverBit = 0;
				}
			}
		}
		if (carryOverBit)
		{
			n3[shift-1] = carryOverBit;
		}
		shift = shift - 1;
	}
	return n3;
}

/*---------------------------------------------------------------------------*/
/*from base2 to base 1*/

function convertBase (vect1, base1, base2, length1)
{
	/*
	fprintf (stdout, "Enter convertBase\n");
	*/
	baseBits   = (Log(base2)/Log(base1));
	convLength = baseBits*length1;
	if (convLength $ 1 != convLength)
	{
		convLength = convLength$1+1;
	}
	
	baseBits = baseBits+1;
	if (baseBits != Log(base2)/Log(base1))
	{
		baseBits = baseBits + 1;
	}
	
	digits    = {};
	digitVect = {1,baseBits};
	oneVect   = {1,baseBits};
	oneVect	    [baseBits-1] = 1;
	
	for (k=0; k<base2; k=k+1)
	{
		digits[k] = digitVect;
		digitVect = addNumbers(digitVect,oneVect,baseBits,base1);
	}
	
	if (carryOverBit)
	{
		vi = {1,baseBits+1};
		vi[0] = carryOverBit;
		for (k=0; k<baseBits; k=k+1)
		{
			vi[k+1] = digitVect[k];
		}	
		digitVect = vi;
	}
	
	outVect     = {1, convLength};
	outVect[convLength-1] = 1;
	
	output		= {1, convLength};
	
	for (vi = length1-1; vi >=0 ; vi = vi-1)
	{
		aNumber = vect1[vi];
		if (aNumber)
		{
			aNumber = digits[aNumber];
			toAdd   = multNumbers (aNumber, outVect, convLength, convLength, baseBits, base1);
			output  = addNumbers (toAdd, output, convLength, base1);
		}
		outVect = multNumbers (digitVect, outVect, convLength, convLength, Columns(digitVect), base1);
	}
	/*
	fprintf (stdout, "Converted a vector (", length1, ") from base ", base2, " to base ", base1," output lentgh ", Columns (output), "\n");
	*/
	return output;
}


/*---------------------------------------------------------------------------------------------------------------------------------------------
  SPAWN INITIAL POPULATION; IT IS STORED AS GENERATING MATRICES
---------------------------------------------------------------------------------------------------------------------------------------------*/

resultProcessingContext = 0;


for (individual=0; individual<(populationSize-1) && individual<Abs(predef); individual = individual + 1)
{
	aChild = Transpose(MakeStringCanonical (predef[individual],rateClassesCount));
	aaRateMultipliers = StringToMatrix(aChild);
	sortedScores[individual+1][1] = individual+1;
	currentPopulation[individual+1] = aChild;
	cString = aChild;
	RunASample (compressedString[rateClassesCount-1],individual+1);
}

for (individual=individual+1; individual<populationSize; individual=individual+1)
{
	goOn = 1;
	while (goOn)
	{
		goOn = 0;
		cString = SpawnRandomString(rateClassesCount);
		for (v=0; v<individual; v=v+1)
		{
			if (Abs(cString-currentPopulation[v])==0)
			{
				goOn = 1;
				break;
			}
		}
	}
	aaRateMultipliers = StringToMatrix(cString);
	sortedScores[individual][1] = individual;
	currentPopulation[individual] = cString;
	RunASample (compressedString[rateClassesCount-1],individual);
}

CleanUpMPI (0);

sortedScores = sortedScores%0;
sampleProbs = {populationSize,1};

doMutation = 0;

lastImprovedGeneration = 0;
lastBestAIC		       = 1e100;
lastBranchAIC		   = crapAIC;


/*---------------------------------------------------------------------------------------------------------------------------------------------
  RUNS THE CHC;
---------------------------------------------------------------------------------------------------------------------------------------------*/

for (generation=0; generation<generationCount; generation=generation+1)
{	
	timeSoFar = Time(1)-startTimer;
	fprintf (stdout, "Generation ", generation+2, 
						   "\nBest c-AIC found far is ", -sortedScores[populationSize-1][0], 
						   "\nThis is a ", crapAIC+sortedScores[populationSize-1][0], " improvement over the single-rate model",
						   "\nGA has considered ", Abs (MasterList), " unique models so far",
						   "\nTotal run time so far is ", timeSoFar$3600, " hrs ", timeSoFar%3600$60, " mins ", timeSoFar%60, " seconds",
						   "\nAverage cluster time per generation is ", Format(timeSoFar/(generation+1),5, 2), " seconds",
						   "\nAverage CPU time per model is ", Format (timeSoFar/Abs(MasterList), 5, 2), " seconds ",
						   "\nConvergence criterion: ", generation-lastImprovedGeneration,"/", stoppingCriterion, "\n"); 

	if ((generation+1) % annealingPhase == 0)
	{
		mutationProb = Max(0.01,mutationProb*mutationProbDecrease);
	}
	doRepop = 1;
	doLocalMutation = generation-lastImprovedGeneration;
	doLocalMutation = doLocalMutation*((doLocalMutation % localMutationInterval) == 0);
	if (doMutation || doLocalMutation)
	{
		resultProcessingContext = 0;
		if (doLocalMutation)
		{
			mutProb = localMutationRate;
		}
		else
		{
			mutProb = mutationProb;
		}
		for (individual=populationSize-2; individual>=0; individual=individual-1)
		{
			goOn = 1;
			while (goOn)
			{
				mpos = 0;
				goOn 	= 0;
				if (doLocalMutation)
				{
					cString = currentPopulation[individual];
				}
				else
				{
					cString = currentPopulation[populationSize-1];
				}
				for (h=0; h<stateVectorDimension; h=h+1)
				{
					if (Random(0,1)<mutProb)
					{
						 newVal = cString[h];
						 while (newVal == cString[h])
						 {
						 	newVal = Random (0,rateClassesCount)$1;
						 }
						 cString[h] = newVal;
						 mpos = mpos + 1;
					}
				}
				for (v=0; v<populationSize; v=v+1)
				{
					if (Abs(cString-currentPopulation[v])==0)
					{
						goOn = 1;
						break;
					}
				}
			}
			if (verboseFlag)
			{
				fprintf (stdout, "Mutated ", Format (mpos, 4, 0), " position in individual ", individual+1, "\n");
			}
			cString = MakeStringCanonical (cString,rateClassesCount);
			aaRateMultipliers = StringToMatrix(cString);
			sortedScores[individual][1] = individual;
			currentPopulation[individual] = cString;
			RunASample (compressedString[rateClassesCount-1],individual);
		}		
		CleanUpMPI (0);
		doMutation = 0;
		sortedScores = sortedScores%0;
	}
	/* compute selection probabilities */
	ExportAMatrix (modelFile,StringToMatrix(currentPopulation[populationSize-1]),1);
	resultProcessingContext = 1;
	fitnessSum = 0;
	intermediateProbs = {populationSize+produceOffspring,2};
        
    if (matingChoice == 3)
    {
		lmatingChoice = Random(0,2.9999999999999)$1;
	}
	else
	{
		lmatingChoice = matingChoice;
	}
	
	for (individual=0; individual<populationSize; individual=individual+1)
	{
		if (lmatingChoice==0)
		{
			sampleProbs[individual] = Exp((sortedScores[individual][0]-sortedScores[populationSize-1][0])/2);
		}
		else
		{
			if (lmatingChoice == 1)
			{
				sampleProbs[individual] = 1;		
			}
			else
			{
				sampleProbs[individual] = 2*(individual+1);					
			}
		}
		intermediateProbs[individual][0] = sortedScores[individual][0];
		intermediateProbs[individual][1] = sortedScores[individual][1];
		fitnessSum = fitnessSum + sampleProbs[individual];
	}
	
	genBestAIC = -sortedScores[populationSize-1][0];
	
        fprintf (stdout, "\n", lastBranchAIC, "\t", genBestAIC, "\n");

	if (genBestAIC + 0.001 < lastBestAIC)
	{
		if (lastBranchAIC-genBestAIC > branchUpdateThreshold)
		{
			UpdateBL (0);
			lastBranchAIC = -sortedScores[populationSize-1][0];
			intermediateProbs[populationSize-1][0] = -lastBranchAIC;
			genBestAIC = lastBestAIC;
		}
		lastBestAIC = -sortedScores[populationSize-1][0];
		lastImprovedGeneration = generation;
	} 
	else
	{
		if (generation-lastImprovedGeneration>=stoppingCriterion)
		{
			break;
		}
	}
	
	sampleProbs = sampleProbs*(1/fitnessSum);
	
	if (verboseFlag)
	{
		fprintf (stdout, "\nMating probabilities\n");
		for (individual=0; individual<populationSize; individual=individual+1)
		{
			fprintf (stdout, "Individual ", Format(individual,3,0), ", sorted ", Format(sortedScores[individual][1],3,0), ". c-AIC: ", 
							  Format (sortedScores[individual][0], 8, 3), ", mating prob ", Format (sampleProbs[individual],6,4), ".\n");
		}
	}
	
	children 	   = {};
	pairwiseCounts = {populationSize,populationSize};
	convertedVectors = {};

	for (individual=0; individual<produceOffspring; individual=individual+1)
	{
		/* pick two individuals to produce offspring */
		/* pick 1st parent */
		
		h 			       = Random (0,1);
		fitnessSum		   = sampleProbs[0];
		v = 0;
		while (fitnessSum < h)
		{
			v = v+1;
			fitnessSum = fitnessSum + sampleProbs[v];
		}

		p1 			   = sortedScores[v][1];
		ps1			   = convertedVectors[p1];
		if (Abs(ps1) == 0)
		{
			ps1 = convertBase(currentPopulation[p1],2,rateClassesCount,stateVectorDimension);
			convertedVectors[p1] = ps1;
		}
		goOn 		   = 1;
		
		disallowedProbs 	= sampleProbs[v];
		
		for (sampleTry = 0; (sampleTry < maxSampleTries) && goOn; sampleTry = sampleTry + 1)
		{
			goOn 		   = 0;
			if (disallowedProbs<1)
			{
				h 			   = Random (0,1-disallowedProbs);
				if (v == 0)
				{
					fitnessSum	   = 0;			
				}
				else
				{
					fitnessSum	   = sampleProbs[0];
				}
				
				v2 = 0;
				while (fitnessSum < h)
				{
					v2 = v2 + 1;
					if (v2 == populationSize)
					{
						while (v2==v)
						{
							v2 = Min(Random (0,populationSize)$1,populationSize-1);
						}
						break;
					}
					if (v2!=v)
					{
						fitnessSum = fitnessSum + sampleProbs[v2];
					}
				}	
			}
			else
			{
				v2 = v;
				while (v2==v)
				{
					v2 = Min(Random (0,populationSize)$1,populationSize-1);
				}
			}
			
			if (v2 == populationSize)
			{
				goOn = 1;
				continue;
			}
			
			while (pairwiseCounts[v][v2] > familyControlSize)
			{
				v2 =  Min(Random (0,populationSize)$1,populationSize-1);
			}
			
			p2			   = sortedScores[v2][1];
			ps2			   = convertedVectors[p2];
			if (Abs(ps2) == 0)
			{
				ps2 = convertBase(currentPopulation[p2],2,rateClassesCount,stateVectorDimension);
				convertedVectors[p2] = ps2;
			}
			fitnessSum 	   = 0;
			
			matingDimension = Min(Columns(ps1),Columns(ps2));
			
			for (h=matingDimension-1; h>=0; h=h-1)
			{
				if (ps1[h] != ps2[h])
				{
					fitnessSum = fitnessSum+1;
					/*if (incestDistance<fitnessSum)
					{
						break;
					}*/
				}
			}
			if (incestDistance>=fitnessSum)
			{
				goOn = 1;
			}
			else
			{
				pairwiseCounts[v][v2] = pairwiseCounts[v][v2] + 1;
				pairwiseCounts[v2][v] = pairwiseCounts[v2][v] + 1;
			}
		}
		
		if (sampleTry == maxSampleTries)
		{
			doMutation = 1;
			generation = generation - 1;
			CleanUpMPI (0);
			doRepop = 0;
			break;
		}
		else
		{
			if (verboseFlag)
			{
				fprintf (stdout, "Mating ", p1, " and ", p2, ". Difference of ", fitnessSum, " positions\n");
			}
			aChild = {matingDimension,1};
			for (h=matingDimension-1; h>=0; h=h-1)
			{
				if (ps1[h] == ps2[h])
				{
					aChild[h] = ps1[h];
				}
				else
				{
					if (Random(0,1)<0.5)
					{
						aChild[h] = ps1[h];
					}
					else
					{
						aChild[h] = ps2[h];					
					}
				}
			}
			aChild = convertBase(aChild,rateClassesCount,2,matingDimension);
			if (Columns(aChild)>stateVectorDimension)
			{
				aC2 = {stateVectorDimension,1};
				for (h=0; h<stateVectorDimension;h=h+1)
				{
					aC2[h] = aChild[h+1];
				}
				aChild = aC2;
				aC2 = 0;
			}
			else
			{
				aChild = Transpose (aChild);
			}
			aChild = MakeStringCanonical (aChild,rateClassesCount);
			aaRateMultipliers = StringToMatrix(aChild);
			intermediateProbs[populationSize+individual][1] = populationSize+individual;
			children  [individual] = aChild;
			cString = aChild;
			RunASample (compressedString[rateClassesCount-1],populationSize+individual);
		}
	}
	CleanUpMPI (0);
	if (doRepop)
	{
		newPop = {};
		intermediateProbs = intermediateProbs%0;
		for (h=produceOffspring; h<populationSize+produceOffspring; h=h+1)
		{
			v = intermediateProbs[h][1];
			sortedScores[h-produceOffspring][1] = h-produceOffspring;
			sortedScores[h-produceOffspring][0] = intermediateProbs[h][0];		
			if (v<populationSize)
			{
				newPop[h-produceOffspring] = currentPopulation[v];
			}
			else
			{
				newPop[h-produceOffspring] = children[v-populationSize];
			}
		}
		for (h=0; h<populationSize; h=h+1)
		{
			currentPopulation[h] = newPop[h];
		}
		
		h = Abs((sortedScores[populationSize-1][0]-sortedScores[0][0])/sortedScores[0][0]);
		if (verboseFlag)
		{
			fprintf (stdout, "Diversity level: ", h, " (hypermutation threshold: ", mutationThreshhold, ")\n");	
		}
		if (h<mutationThreshhold)
		{
			if (verboseFlag)
			{
				fprintf (stdout, "\nTriggered hypermutation...\n");
			}
			doMutation = 1;
			generation = generation - 1;
		}
	}
}
