RequireVersion ("0.99.20070118");

MESSAGE_LOGGING 		   = 0;
VERBOSITY_LEVEL 		   = -1;
TRY_NUMERIC_SEQUENCE_MATCH = 1;

_pddSNS 				   = 0;


/*------------------------------------------------------------------------------------------
	OPTIONS FOR CODON MODELS
------------------------------------------------------------------------------------------*/

if (Abs(_Genetic_Code) && dataType == 1)
{
	ChoiceList (_pddSNS,"Codon Branch Length",1,SKIP_NONE,
												"Total",      "Synonymous and non-synonymous distance",	
												"Synonymous", "Synonymous distance",	
												"Non-synonymous", "Non-synonymous distance");
	if (_pddSNS < 0)
	{
		return 0;
	}				
	
	if (_pddSNS > 0)
	{
		ExecuteAFile (HYPHY_LIB_DIRECTORY+"TemplateBatchFiles"+DIRECTORY_SEPARATOR+"dSdNTreeTools.ibf");
		if (_pddSNS == 1)
		{
			BRANCH_LENGTH_STENCIL = (ComputeScalingStencils (0))["Syn"];
		}
		else
		{
			BRANCH_LENGTH_STENCIL = (ComputeScalingStencils (0))["NonSyn"];		
		}
	}
}

/*------------------------------------------------------------------------------------------
	MPI SETUP
------------------------------------------------------------------------------------------*/

if (MPI_NODE_COUNT > 1)
{
	_pddMPI      = {MPI_NODE_COUNT-1,2}["-1"];
	_pddMPIFirst = {};
	Export (_pddModelExport, USE_LAST_MODEL);
	DATA_FILE_PRINT_FORMAT = 6;
}	
else
{
	Tree Inferred_Tree 	    = (1,2);
}




/*------------------------------------------------------------------------------------------
	IF THERE EXISTS A TREE
	SEE IF ONE NEEDS TO ESTIMATE GLOBAL
	PARAMETERS FOR THE MODEL AND FIX THEM FOR PAIRWISE ESTIMATIONS
	
------------------------------------------------------------------------------------------*/

if (Abs(DATAFILE_TREE))
{
	if (Abs(_useThisLF))
	{
		if (_reoptimizeLFValues)
		{
			Tree _fileTree = DATAFILE_TREE;
			if (_pddVF)
			{
				fprintf (stdout, "[RE-OPTIMIZING PARAMETERS]\n");
			}
			LikelihoodFunction _pdeLF = (filteredData, _fileTree);
			Optimize					(_resLF,_pdeLF);
		}
		fixGlobalParameters (_useThisLF);
	}
	else
	{
		Tree _fileTree = DATAFILE_TREE;
		if (Abs(_Genetic_Code) && dataType == 1)	
		{
			DataSetFilter copy_filteredData = CreateFilter (ds,3,"","",GeneticCodeExclusions);
		}
		else
		{
			DataSetFilter copy_filteredData = CreateFilter (ds,1);	
		}
		LikelihoodFunction lf = (copy_filteredData,_fileTree);
		GetString (_pddNodeID, lf, -1);
		
		if (Columns (_pddNodeID["Global Independent"]))
		{
			ChoiceList (_paramOption,	"Global parameters",1,SKIP_NONE,
						"Estimated",	"Estimate global model parameters using a provided tree",
						"Manual",		"Enter parameter values by hand");
					
			if (_paramOption < 0)
			{
				return 0;
			}
			
			LoadFunctionLibrary ("GrabBag.bf");
            
			if (_paramOption == 0)
			{
				if (Abs(_Genetic_Code) && dataType)
				{
					defineIfNeeded ("AC",1);
					defineIfNeeded ("AT",1);
					defineIfNeeded ("CG",1);
					defineIfNeeded ("CT",1);
					defineIfNeeded ("GT",1);
					
					_REVMX = {{*,AC*mu,mu,AT*mu}{AC*mu,*,CG*mu,CT*mu}{mu,CG*mu,*,GT*mu}{AT*mu,CT*mu,GT*mu,*}};

					if (_pddVF)
					{
						fprintf (stdout, "[OBTAINING APPROXIMATE BRANCH LENGTHS FROM A NUCLEOTIDE MODEL]\n");
					}
					DataSetFilter	 	_nucData = CreateFilter (ds,1);
					HarvestFrequencies	(_nucF,_nucData,1,1,1);
					Model _REVModel =   (_REVMX,_nucF,1);
					
					Tree 			 	_nucTree = DATAFILE_TREE;
					
					LikelihoodFunction nuc_lf = (_nucData,_nucTree);
					Optimize 		  (res,nuc_lf);
					global 			   _bsf = 0.33;
					global			   _gW	= 1;
					ReplicateConstraint ("this1.?.synRate:=_bsf*this2.?.mu__",_fileTree,_nucTree);
					ReplicateConstraint ("this1.?.nonSynRate:=_gW*this2.?.synRate",_fileTree,_fileTree);
				}
				if (_pddVF)
				{
					fprintf (stdout, "[OBTAINING GLOBAL PARAMETER ESTIMATES FROM THE SUPPLIED TREE]\n");
				}
				/*AUTO_PARALLELIZE_OPTIMIZE = 1;
				VERBOSITY_LEVEL = 1;*/
				Optimize (res,lf);
				VERBOSITY_LEVEL = -1;
				/*AUTO_PARALLELIZE_OPTIMIZE = 0;*/
				ClearConstraints (_fileTree);
				echoGlobalParameters ("lf");
				fixGlobalParameters ("lf");
			}
			else
			{
				promptForGlobalParameters ("lf");
				echoGlobalParameters 	  ("lf");
			}
		}
	}
}



/*------------------------------------------------------------------------------------------
	MAIN LOOP
------------------------------------------------------------------------------------------*/

if (_pddVF)
{
	fprintf (stdout, "[STARTING DISTANCE COMPUTATIONS]\n");
	_pTimer	 = Time (1);
	_pLastU	 = _pTimer;
	_pDone	 = 1;
	_pTotal  = ds.species*(ds.species-1)/2;
}


for (_pddi = 0; _pddi<ds.species-1; _pddi=_pddi+1)
{
	for (_pddj = 0; _pddj<=_pddi; _pddj = _pddj+1)
	{
		if (dataType)
		{
			DataSetFilter twoSpecFilter = CreateFilter (filteredData,3,"",speciesIndex==_pddi+1 || speciesIndex==_pddj,GeneticCodeExclusions);
            GetDataInfo (s1, twoSpecFilter, 0);
            GetDataInfo (s2, twoSpecFilter, 1);
		}
		else
		{
			DataSetFilter twoSpecFilter = CreateFilter (filteredData,1,"",speciesIndex==_pddi+1 || speciesIndex==_pddj);
            GetDataInfo (s1, twoSpecFilter, 0);
            GetDataInfo (s2, twoSpecFilter, 1);
			if (pairwiseAlign)
			{
				inStr = {{s1^{{"[\\?\\-]",""}},s2^{{"[\\?\\-]",""}}}};
			}
		}
		if (MPI_NODE_COUNT > 1)
		{
			_nexusJob      = "";
			_nexusJobShort = "";
			_nexusJob      * 256;
			_nexusJobShort * 256;
            
            if (pairwiseAlign)
            {
            _pddFilterExport = "alignOptions = " + alignOptions + ";\n" + 
                               "inStr=" + inStr + ";\n" + 
                                "AlignSequences(aligned, inStr, alignOptions);" + 
                                "aligned = aligned[0];" + 
                                "s1 = \">1\\n\" + aligned[1] + \"\\n>2\\n\" + aligned[2] + \"\\n\";" + 
                                "DataSet ds 			= ReadFromString (s1);";
                                //"DataSetFilter twoSpecFilter = CreateFilter (realigned,1);";  
            }
            else
            {
            _pddFilterExport = "aligned = aligned[0];" + 
                                "s1 = \">1\\n\"" + s1 + "\"\\n>2\\n\"" + s2 + "\"\\n\";" + 
                                "DataSet ds 			= ReadFromString (s1);";
                               // "DataSetFilter twoSpecFilter = CreateFilter (realigned,1);";  
            }
 
			_nexusJob * _pddFilterExport;
			_nexusJobShort * _pddFilterExport;
           
			
			_nexusJob      * ("\nBRANCH_LENGTH_STENCIL="+BRANCH_LENGTH_STENCIL+";\n");
			_nexusJob      * _pddModelExport;
			_nexusJob      * ("PRESERVE_SLAVE_NODE_STATE=1;TRY_NUMERIC_SEQUENCE_MATCH=1;Tree givenTree = (1,2);");
			if (dataType)
			{
				_nexusJob *      ("DataSetFilter twoSpecFilter = CreateFilter (ds,3,\"\",\"\",\""+GeneticCodeExclusions+"\");");
				_nexusJobShort * ("DataSetFilter twoSpecFilter = CreateFilter (ds,3,\"\",\"\",\""+GeneticCodeExclusions+"\");");
			}
			else
			{
				_nexusJob * "DataSetFilter twoSpecFilter = CreateFilter (ds,1);";
				_nexusJobShort * "DataSetFilter twoSpecFilter = CreateFilter (ds,1);";
			}
			_nexusJob * "LikelihoodFunction lf = (twoSpecFilter,givenTree);Optimize(res,lf);return BranchLength (givenTree,0);\n";
			_nexusJob * 0;
			_nexusJobShort * "LikelihoodFunction lf = (twoSpecFilter,givenTree);Optimize(res,lf);return BranchLength (givenTree,0);\n";
			_nexusJobShort * 0;
            
            //fprintf (stdout, _nexusJobShort, "\n");
		}
        else
        {
            if (pairwiseAlign)
            {
                AlignSequences(aligned, inStr, alignOptions);
                aligned = aligned[0];
                s1 = ">1\n" + aligned[1] + "\n>2\n" + aligned[2] + "\n";
                //fprintf (stdout, s1, "\n");
                DataSet realigned 			= ReadFromString (s1);
                DataSetFilter twoSpecFilter = CreateFilter (realigned,1);  
            }
        }
        
		_pddSendJobs (_pddi,_pddj); 
	}
}

/*------------------------------------------------------------------------------------------
	MPI CLEANUP
------------------------------------------------------------------------------------------*/

if (MPI_NODE_COUNT > 1)
{
	_jr = 0;
	for (_pddNodeID = 0; _pddNodeID < MPI_NODE_COUNT-1; _pddNodeID = _pddNodeID + 1)
	{
		if (_pddMPI[_pddNodeID][0] > 0)
		{
			_jr = _jr + 1;
		}
	}
	while (_jr)
	{
		_pddReceiveJobs (0);
		_jr = _jr - 1;
	}
}

/*------------------------------------------------------------------------------------------
	 CLEANUP
------------------------------------------------------------------------------------------*/

BRANCH_LENGTH_STENCIL = 0;

/*------------------------------------------------------------------------------------------
	RECEIVE FINISHED JOBS
------------------------------------------------------------------------------------------*/

function _pddReceiveJobs (dummy)
{
	if (MPI_NODE_COUNT > 1)
	{
		MPIReceive (-1,_pddFromNode,_pddRes);
		_di = _pddMPI[_pddFromNode-1][0];
		_dj = _pddMPI[_pddFromNode-1][1];
		ExecuteCommands ("_dk="+_pddRes);
		_pddMPI[_pddFromNode-1][0] = -1;
		_pddMPI[_pddFromNode-1][1] = -1;
	}
	else
	{
		_di 	= _pddi;
		_dj		= _pddj;
		_dk 	= BranchLength 		  (Inferred_Tree,0);
        
        //fprintf (stdout, _dk, "\n");
	}
	
	distanceMatrix[_dj][_di+1]  = _dk;
	distanceMatrix[_di+1][_dj]  = _dk;
	if (_pddVF)
	{
		_pDone	 = _pDone + 1;
		_pTimer2 = Time(1);
		GetString (_n1, filteredData,_dj);
		GetString (_n2, filteredData,_di+1);
		if (_pTimer2 - _pLastU > 1)
		{
			_pLastU	 = _pTimer2;
			fprintf (stdout,"DONE WITH ",_pDone,"/", _pTotal, " EVALUATIONS. ETA: ",(_pTotal-_pDone)/_pDone*(_pTimer2-_pTimer)," seconds \n");
		}
	}	
	return _pddFromNode;
}

/*------------------------------------------------------------------------------------------
	SEND A JOB
------------------------------------------------------------------------------------------*/

function _pddSendJobs (_di2,_dj2)
{
	if (MPI_NODE_COUNT > 1)
	{
		for (_pddNodeID = 0; _pddNodeID < MPI_NODE_COUNT-1; _pddNodeID = _pddNodeID + 1)
		{
			if (_pddMPI[_pddNodeID][0] < 0)
			{
				break;
			}
		}
		if (_pddNodeID == MPI_NODE_COUNT-1)
		{
			_pddNodeID = _pddReceiveJobs (0) - 1;
		}
		
		_pddMPI [_pddNodeID][0] = _di2;
		_pddMPI [_pddNodeID][1] = _dj2;
		if (_pddMPIFirst[_pddNodeID] == 0)
		{	
			_pddMPIFirst[_pddNodeID] = 1;
			MPISend (_pddNodeID+1,_nexusJob);
		}
		else
		{
			MPISend (_pddNodeID+1,_nexusJobShort);		
		}
	}
	else
	{
		LikelihoodFunction lf   =  (twoSpecFilter,Inferred_Tree);
		Optimize 				  (res,lf);
		_pddReceiveJobs			  (0);
	}
	return 0;
}

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