#include "heuristicMethodNPBootstrap.bf";
#include "branchSwappingFunctions.bf";
				
/*---------------------------------------------------------------*/

function ReceiveJobs (sendOrNot, nni_flagrj)
{
	if (MPI_NODE_COUNT>1)
	{
		MPIReceive (-1, fromNode, result_String);
		
		_thisTree	 = 	MPITreeStrings[fromNode-1];

		_tempStorage = {2*taxonCounter-1,2};
		_tempStorage2 = {taxonCounter-2,2};
		_cindex		 = 2*(fromNode-1);
		
		for (_rc = 0; _rc < 2*taxonCounter-1; _rc = _rc + 1)
		{
			_tempStorage[_rc][0] = MPINodeTree[_rc][_cindex];
			_tempStorage[_rc][1] = MPINodeTree[_rc][_cindex+1];
		}

		for (_rc = 0; _rc < taxonCounter-2; _rc = _rc + 1)
		{
			_tempStorage2[_rc][0] = MPINodeClades[_rc][_cindex];
			_tempStorage2[_rc][1] = MPINodeClades[_rc][_cindex+1];
		}
		
		if (sendOrNot)
		{
			MPITreeStrings[fromNode-1] = thisTree;
			MPISend (fromNode,lf);
			for (_rc = 0; _rc < 2*taxonCounter-1; _rc = _rc + 1)
			{
				MPINodeTree[_rc][_cindex] 	= treeNodes[_rc][2];
			    MPINodeTree[_rc][_cindex+1] = treeNodes[_rc][3];
			}

			for (_rc = 0; _rc < taxonCounter-2; _rc = _rc + 1)
			{
				MPINodeClades[_rc][_cindex] 	= cladesInfo[_rc][2];
			    MPINodeClades[_rc][_cindex+1]   = cladesInfo[_rc][3];
			}
		}
		else
		{
			MPINodeState[fromNode-1]    = 0;
		}
		
		for (_rc = 0; _rc < 2*taxonCounter-1; _rc = _rc + 1)
		{
			treeNodes[_rc][2] = _tempStorage[_rc][0];
			treeNodes[_rc][3] = _tempStorage[_rc][1];
		}
		
		for (_rc = 0; _rc < taxonCounter-2; _rc = _rc + 1)
		{
			cladesInfo[_rc][2] = _tempStorage2[_rc][0];
			cladesInfo[_rc][3] = _tempStorage2[_rc][1];
		}

		/*thisTree = TreeMatrix2TreeString (2,0);*/
		thisTree = _thisTree;
		ExecuteCommands (result_String);
		res = lf_MLES;
		fprintf (stdout, "\nReceived a tree from node: ", fromNode-1,"  =>   ", res[1][0]);
	}
	
	dummy = CheckForImprovement(nni_flagrj);

	return fromNode-1;
}

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

function CheckForImprovement (nni_flagcfi)
{
	if (res[1][0]>bestValue)
	{
		bestValue = res[1][0];
		bestTree = thisTree;
		if (nni_flagcfi)
		{
			nniDidBetter = 1;
			if (verbFlag)
			{
				OpenWindow (TREEWINDOW, {{"Inferred_Tree"}});
				fprintf (stdout, "\n\tRestarting NNI on the better tree.");
			}
			for (l4=2*taxonCounter-2;l4>=0;l4=l4-1)
			{
				bestTreeNodes[l4][0]= treeNodes[l4][2];
				bestTreeNodes[l4][1]= treeNodes[l4][3];
				treeNodes[l4][0]    = treeNodes[l4][2];
				treeNodes[l4][1]    = treeNodes[l4][3];
			}
			for (l4=0; l4<taxonCounter-2; l4=l4+1)
			{
				bestCladesInfo[l4][0]= cladesInfo[l4][2];
				bestCladesInfo[l4][1]= cladesInfo[l4][3];
				cladesInfo[l4][0]    = cladesInfo[l4][2];
				cladesInfo[l4][1]    = cladesInfo[l4][3];
			}
			l = 0;
			while (cladesInfo[l][1])
			{
				l = l+1;
			}				
			l2 = -1;		
		}
		else
		{
			for (l=2*taxonCounter-2;l>=0;l=l-1)
			{
				bestTreeNodes[l][0]=treeNodes[l][2];
				bestTreeNodes[l][1]=treeNodes[l][3];
			}
			for (l=0; l<taxonCounter-2; l=l+1)
			{
				bestCladesInfo[l][0]=cladesInfo[l][2];
				bestCladesInfo[l][1]=cladesInfo[l][3];
			}
			if (verbFlag)
			{
				GetString (nodeName, filteredData, taxonIndex);
				mxTreeSpec = {5,1};
				mxTreeSpec [0] = "Inferred_Tree";
				mxTreeSpec [3] = "";
				mxTreeSpec [4] = "Inferred_Tree."+nodeName;
				if (twHasBeenOpened)
				{
					mxTreeSpec [1] = "";
					mxTreeSpec [2] = "";
				}
				else
				{
					mxTreeSpec [1] = "8211";
					mxTreeSpec [2] = "10,40,-10,-175,1";
					twHasBeenOpened = 1;
				}
				OpenWindow (TREEWINDOW, mxTreeSpec );
			}
		}
	}
	return 0;
}

/*---------------------------------------------------------------*/
function SendOffTreeJob (nni_flag)
{
	LikelihoodFunction lf = (smallerFilter, Inferred_Tree);
					
	if (MPI_NODE_COUNT>1)
	{
		for (mpiNode = 0; mpiNode < MPI_NODE_COUNT-1; mpiNode = mpiNode+1)
		{
			if (MPINodeState[mpiNode]==0)
			{
				break;	
			}
		}
		
		if (mpiNode==MPI_NODE_COUNT-1)
		/* all nodes busy */
		{
			mpiNode = ReceiveJobs (1, nni_flag);
		}
		else
		{
			MPISend (mpiNode+1,lf);
			MPINodeState[mpiNode] = 1;
			MPITreeStrings[mpiNode] = thisTree;
			_cindex		 = 2*mpiNode;
			
			for (_rc = 0; _rc < 2*taxonCounter-1; _rc = _rc + 1)
			{
				MPINodeTree[_rc][_cindex] 	= treeNodes[_rc][2];
			    MPINodeTree[_rc][_cindex+1] = treeNodes[_rc][3];
			}
			for (_rc = 0; _rc < taxonCounter-2; _rc = _rc + 1)
			{
				MPINodeClades[_rc][_cindex] 	= cladesInfo[_rc][2];
			    MPINodeClades[_rc][_cindex+1]   = cladesInfo[_rc][3];
			}
		}
	}
	else
	{
		Optimize (res, lf);
		mpiNode = CheckForImprovement (nni_flag);
		if (verbFlag)
		{
			fprintf (stdout, " => ", res[1][0]);
		}
	}
	
	return 0;
}

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

function InferTreeTopology (verbFlag)
{
	if (filteredData.species <= 3)
	{
		fprintf (stdout, "Input file contained 3 or fewer sequences - not much inference to be done!\n");
		return 0;
	}
	if (randomOption==1)
	/* randomize sequence order */
	{
		treeNodes  = {filteredData.species,1};
		cladesInfo = {filteredData.species,1};
		
		for (currentTaxon=0; currentTaxon < filteredData.species; currentTaxon = currentTaxon+1)
		{
			cladesInfo [currentTaxon] = currentTaxon;	
		}
		
		currentTaxon = filteredData.species-1;
		
		while (currentTaxon>=0)
		{
			i = Random (0,currentTaxon+1) $ 1;
			shift = 0;
			for (s = 0; s<filteredData.species; s=s+1)
			{
				if (cladesInfo[s]>=0)
				{
					shift = shift+1;
				}
				if (i == shift - 1)
				{
					treeNodes[filteredData.species-1-currentTaxon] = cladesInfo[s];
					cladesInfo [s] = -1;
					break;
				}
			}
			currentTaxon = currentTaxon - 1;
		}
		
		/*reshuffledOrder = "";
		for (currentTaxon = 0; currentTaxon < filteredData.species-1; currentTaxon = currentTaxon+1)
		{
			reshuffledOrder = reshuffledOrder + treeNodes[currentTaxon] + ",";
		}
		reshuffledOrder = reshuffledOrder + treeNodes[currentTaxon];*/
		
		if (verbFlag)
		{
			fprintf (stdout,"\n\t Sequence order randomized as follows:\n");
			for (currentTaxon = 0; currentTaxon < filteredData.species; currentTaxon = currentTaxon+1)
			{
				GetString (nodeName, filteredData, treeNodes[currentTaxon]);
				fprintf (stdout,nodeName,"\n");
			}
			fprintf (stdout,"\n");
		}
		
				
		additionOrder = treeNodes;
		if (methodIndex == 0)
		{
			first3Taxa[0] = additionOrder[0];
			first3Taxa[1] = additionOrder[1];
			first3Taxa[2] = additionOrder[2];
		}
	}
	else
	{
		additionOrder = {filteredData.species,1};
		for (currentTaxon = 0; currentTaxon < filteredData.species; currentTaxon = currentTaxon+1)
		{
			additionOrder [currentTaxon] = currentTaxon;
		}
	}

	treeNodes 		= {2*(filteredData.species+1),4};
	bestTreeNodes 	= {2*(filteredData.species+1),2};

	
	cladesInfo 	    = {filteredData.species,4};
	bestCladesInfo  = {filteredData.species,2};

	if (MPI_NODE_COUNT>1)
	{
		MPINodeState 	= {MPI_NODE_COUNT-1,1};
		MPINodeTree  	= {2*(filteredData.species+1),2*(MPI_NODE_COUNT-1)};
		MPINodeClades   = {filteredData.species,2*(MPI_NODE_COUNT-1)};
		MPITreeStrings	= {MPI_NODE_COUNT-1,1};
		MPITreeStrings[0] = "";
	}

	currentTaxon = 0;

	treeNodes[0][0]=0;
	treeNodes[0][1]=1;
	treeNodes[1][0]=1;
	treeNodes[1][1]=1;
	treeNodes[2][0]=2;
	treeNodes[2][1]=1;
	treeNodes[3][0]=filteredData.species;
	treeNodes[3][1]=0;
	cladesInfo[0][0]=0;
	cladesInfo[0][1]=4;

	bestTree="";
	bestValue=-1e20;



	svl = VERBOSITY_LEVEL;
	VERBOSITY_LEVEL = -1;

	if (verbFlag)
	{
		fprintf (stdout, "**** Running sequential addition on ", LAST_FILE_PATH, " ****");
		if (haveTreeConstraint)
		{
			fprintf (stdout, "\n****** Using the constraint specified by ******\n",_topologyPatternString ,"\n\n");
		}
	}
	if (methodIndex == 2)
	{
		if (verbFlag)
		{
			fprintf (stdout, "\nSTEP 0). Searching for the best 3 taxa tree.");
			fprintf (stdout, "\n\t", Format (filteredData.species*(filteredData.species-1)*(filteredData.species-2)/6,0,0)," trees will be examined.\n");
		}
		for (i=0; i<filteredData.species-2; i=i+1)
		{
			for (s=i+1;s<filteredData.species-1;s=s+1)
			{
				for (shift=s+1;shift<filteredData.species; shift=shift+1)
				{
					treeNodes[0][0]=i;
					treeNodes[0][1]=1;
					treeNodes[1][0]=s;
					treeNodes[1][1]=1;
					treeNodes[2][0]=shift;
					treeNodes[2][1]=1;		
					thisTree = TreeMatrix2TreeString (0,0);
					Tree    Inferred_Tree = thisTree;
					if (dataType)
					{
						DataSetFilter smallFilter = CreateFilter (filteredData,3,"",(speciesIndex==i)||(speciesIndex==s)||(speciesIndex==shift),GeneticCodeExclusions);
					}
					else
					{
						DataSetFilter smallFilter = CreateFilter (filteredData,1,"",(speciesIndex==i)||(speciesIndex==s)||(speciesIndex==shift));
					}
					LikelihoodFunction lf = (smallFilter, Inferred_Tree);
					Optimize (res,lf);
					if (res[1][0]>bestValue)
					{
						first3Taxa[0]=i;
						first3Taxa[1]=s;
						first3Taxa[2]=shift;
						
						bestValue = res[1][0];
						bestTree = thisTree;
					}
				}
			}
		}

		if (verbFlag)
		{
			fprintf (stdout,"\n\t Best 3 taxa tree is ", bestTree, " with log likelihood of ", bestValue);
		}
	}
	else
	{
		if (methodIndex == 3)
		{
			first3Taxa[0] = Random (0,filteredData.species) $ 1;
			first3Taxa[1] = first3Taxa[0];
			while (first3Taxa[1] == first3Taxa[0])
			{
				first3Taxa[1] = Random (0,filteredData.species) $ 1;
			}
			first3Taxa[2] = first3Taxa[0];
			while ((first3Taxa[2] == first3Taxa[0])||(first3Taxa[2] == first3Taxa[1]))
			{
				first3Taxa[2] = Random (0,filteredData.species) $ 1;
			}
			if (verbFlag)
			{
				fprintf (stdout,"\n\t Random 3 taxa tree selected: (");
				for (currentTaxon = 0; currentTaxon < 3; currentTaxon = currentTaxon+1)
				{
					GetString (nodeName, filteredData, first3Taxa[currentTaxon]);
					fprintf (stdout,nodeName);
					if (currentTaxon<2)
					{
						fprintf (stdout, ",");
					}
				}
				fprintf (stdout,")\n");
			}
		}
	}

	treeNodes[0][0]=first3Taxa[0];
	treeNodes[1][0]=first3Taxa[1];
	treeNodes[2][0]=first3Taxa[2];

	currentTaxon = 0;
	taxonCounter = 3;
	
	filterSeqString = "";
	
	filterSeqString = filterSeqString + first3Taxa[0];
	filterSeqString = filterSeqString + "," + first3Taxa[1];
	filterSeqString = filterSeqString + "," + first3Taxa[2];

	while (currentTaxon<filteredData.species)
	{
		taxonIndex = additionOrder [currentTaxon];
		/* decide which taxon will be added next */
		if ((taxonIndex!=first3Taxa[0])&&(taxonIndex!=first3Taxa[1])&&(taxonIndex!=first3Taxa[2]))
		{
			GetString (nodeName, filteredData, taxonIndex);
			
			if (verbFlag)
			{
				fprintf (stdout, "\n\n\nSTEP ",Format(taxonCounter-2,0,0),"/",Format(filteredData.species-3,0,0),"). Adding taxon:", nodeName);
			}
			/* attempt to insert this taxon into current tree */
			/* produce a new data set filter */
			filterSeqString = filterSeqString + "," + taxonIndex;
			if (dataType)
			{
				DataSetFilter smallerFilter = CreateFilter (filteredData,3,"",filterSeqString,GeneticCodeExclusions);
			}
			else
			{
				DataSetFilter smallerFilter = CreateFilter (filteredData,1,"",filterSeqString);
			}
			bestValue=-1e20;
			treesConsidered = 0;
			for (t_count=0; t_count<2*taxonCounter-3; t_count=t_count+1)
			/* loop over all branches */
			{	
				i = 0;
				shift = 0;
				while (treeNodes[i][1])
				{
					if (i==t_count)
					{
						currentLevel = taxonCounter-3;
						shift = 2;
						if (treeNodes[i][0]<filteredData.species)
						/* simple branch */
						{
							treeNodes[i][2]=treeNodes[i][0];
							treeNodes[i][3]=treeNodes[i][1]+1;
							treeNodes[i+1][2]=taxonIndex;
							treeNodes[i+1][3]=treeNodes[i][1]+1;
							treeNodes[i+2][2]= taxonCounter+ filteredData.species-3;
							treeNodes[i+2][3]=treeNodes[i][1];
							cladesInfo[currentLevel][2] = i;
							cladesInfo[currentLevel][3] = 3;					
						}
						else
						{
							/* update node depths for the entire clade now*/
							l = treeNodes[i][0]-filteredData.species;
							s = cladesInfo[l][0];
							/*fprintf (stdout,"\n",i," ",cladesInfo[l][1],treeNodes, cladesInfo,"\n");*/
							for (p=s+cladesInfo[l][1]-1; p>=s; p=p-1)
							{
								treeNodes[i][2]=treeNodes[i][0];
								treeNodes[i][3]=treeNodes[i][1]+1;						
								i=i-1;
							}
							i=i+cladesInfo[l][1];
							/* new clade record */
							cladesInfo[currentLevel][2] = cladesInfo[l][0];
							cladesInfo[currentLevel][3] = cladesInfo[l][1]+2;
							/* now we need to insert two more nodes */
							treeNodes[i+1][2]=taxonIndex;
							treeNodes[i+1][3]=treeNodes[i][1]+1;
							treeNodes[i+2][2]=currentLevel+filteredData.species;
							treeNodes[i+2][3]=treeNodes[i][1];
						}
						for (p=0; p<currentLevel; p=p+1)
						{
							if (cladesInfo[p][0]>i)
							{
								cladesInfo[p][2] = cladesInfo[p][0]+2;
							}
							else
							{
								cladesInfo[p][2] = cladesInfo[p][0];
							}
							
							if ((cladesInfo[p][0]<=i)&&((cladesInfo[p][0]+cladesInfo[p][1])>i+1))
							{
								cladesInfo[p][3] = cladesInfo[p][1]+2;
							}
							else
							{
								cladesInfo[p][3] = cladesInfo[p][1];
							}
						}				
					}
					else
					{
						treeNodes[i+shift][2]=treeNodes[i][0];
						treeNodes[i+shift][3]=treeNodes[i][1];
					}
					i = i+1;
				}
				
				treeNodes[i+2][2]=treeNodes[i][0];
				treeNodes[i+2][3]=treeNodes[i][1];
				
				thisTree = TreeMatrix2TreeString (2,0);
				
				if ((verbFlag)&&(MPI_NODE_COUNT<2))
				{
					fprintf (stdout, "\n\tTree ",thisTree);
				}
				
				Tree    Inferred_Tree = thisTree;
				
				
				if (haveTreeConstraint)
				{
					if ((Inferred_Tree<=_topologyPattern) == 0)
					{
						if (verbFlag)
						{
							fprintf (stdout, " => Rejected by the topology constraint.");
						}
						continue;
					}
					else
					{
						if (verbFlag)
						{
							fprintf (stdout, " => Accepted by the topology constraint.");
						}
					}
				}
				
				dummy = SendOffTreeJob (0);
				
				treesConsidered = treesConsidered + 1;

			}
			
			if (MPI_NODE_COUNT>1)
			{
				while (1)
				{
					for (nodeCounter = 0; nodeCounter < MPI_NODE_COUNT-1; nodeCounter = nodeCounter+1)
					{
						if (MPINodeState[nodeCounter]==1)
						{
							fromNode = ReceiveJobs (0,0);
							break;	
						}
					}
					if (nodeCounter == MPI_NODE_COUNT-1)
					{
						break;
					}
				}	
			}	
	
			if (treesConsidered == 0)
			{
				fprintf (stdout, "\n***** NO TREES MATCHED THE TREE CONSTRAINT. PLEASE CHECK THAT SEQUENCE NAMES MATCH *****\n");
				VERBOSITY_LEVEL = svl;
				return 0;
			}
			for (l=2*taxonCounter-2;l>=0;l=l-1)
			{
				treeNodes[l][0]=bestTreeNodes[l][0];
				treeNodes[l][1]=bestTreeNodes[l][1];
			}
			for (l=0; l<taxonCounter-2; l=l+1)
			{
				cladesInfo[l][0]=bestCladesInfo[l][0];
				cladesInfo[l][1]=bestCladesInfo[l][1];
			}

			if (verbFlag)
			{
				fprintf (stdout,"\n\n\t Best ", Format(taxonCounter+1,0,0)," taxa tree is ", bestTree, " with log likelihood of ", bestValue);
			}
			if (((doNNIOption<=2)||((doNNIOption>2)&&(taxonCounter==filteredData.species-1)))&&(taxonCounter>3)&&(doNNIOption)||(doNNIOption==5))
			{
				starDecomposition = 0;
				if ((doNNIOption==1)||(doNNIOption==3)||((doNNIOption==5)&&(taxonCounter<filteredData.species-1)))
				/* NNI */
				{
					if (((doNNIOption==1)&&((taxonCounter-2)%nniPeriod > 0)&&(taxonCounter<filteredData.species-1)) == 0)
					{
						#include "doNNISwap.bf";
					}
				}
				else
				/* SPR */
				{
					#include "doSPRSwap.bf";
				}
			}
			taxonCounter = taxonCounter+1;
		}
		currentTaxon = currentTaxon+1;
	}
	VERBOSITY_LEVEL = svl;
	return 1.0;
}
