fprintf (stdout, "\n\nPhase 1:Nucleotide Model (",ModelTitle,") Model Fit\n\n");

global dNdS    = 1;

if (nrChoice == 0)
{
	for (fileID = 1; fileID <= fileCount; fileID = fileID + 1)
	{
		ExecuteCommands ("Tree nucTree_" + fileID + "=" + treeStrings[fileID] + ";");
	}
	ExecuteCommands (constructLF("nucLF", "nucData", "nucTree", fileCount));
	Optimize (res,nucLF);
	stashLOF = LIKELIHOOD_FUNCTION_OUTPUT ;
	LIKELIHOOD_FUNCTION_OUTPUT  = 6;
	if (Abs(NUC_FILE_PATH))
	{
		fprintf (NUC_FILE_PATH,CLEAR_FILE,nucLF);
	}
	LIKELIHOOD_FUNCTION_OUTPUT  = stashLOF;
}

fprintf (stdout, "\n",nucLF);
fprintf (stdout, "\n\nPhase 2:MG94x(",ModelTitle,") Model Fit\n\n");
				
CodonMatrix = {ModelMatrixDimension,ModelMatrixDimension};

hshift = 0;

for (h=0; h<64; h=h+1)
{
	if (_Genetic_Code[h]==10) 
	{
		hshift = hshift+1;
		continue; 
	}
	vshift = hshift;
	for (v = h+1; v<64; v=v+1)
	{
		diff = v-h;
		if (_Genetic_Code[v]==10) 
		{
			vshift = vshift+1;
			continue; 
		}
		nucPosInCodon = 2;
		if ((h$4==v$4)||((diff%4==0)&&(h$16==v$16))||(diff%16==0))
		{
			if (h$4==v$4)
			{
				transition = v%4;
				transition2= h%4;
			}
			else
			{
				if(diff%16==0)
				{
					transition = v$16;
					transition2= h$16;
					nucPosInCodon = 0;
				}
				else
				{
					transition = v%16$4;
					transition2= h%16$4;
					nucPosInCodon = 1;
				}
			}
			if (transition<transition2)
			{
				trSM = transition;
				trLG = transition2;
			}
			else
			{
				trSM = transition2;
				trLG = transition;
			}
			
			if (trSM==0)
			{
				if (trLG==1)
				{
					if (_Genetic_Code[0][h]==_Genetic_Code[0][v]) 
					{
						CodonMatrix[h-hshift][v-vshift] := AC__*synRate*positionFrequencies__[transition__][nucPosInCodon__];
						CodonMatrix[v-vshift][h-hshift] := AC__*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
					}
					else
					{
						CodonMatrix[h-hshift][v-vshift] := AC__*dNdS*synRate*positionFrequencies__[transition__][nucPosInCodon__];
						CodonMatrix[v-vshift][h-hshift] := AC__*dNdS*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
					}
				}
				else
				{
					if (trLG==2)
					{
						if (_Genetic_Code[0][h]==_Genetic_Code[0][v]) 
						{
							CodonMatrix[h-hshift][v-vshift] := synRate*positionFrequencies__[transition__][nucPosInCodon__];
							CodonMatrix[v-vshift][h-hshift] := synRate*positionFrequencies__[transition2__][nucPosInCodon__];
						}
						else
						{
							CodonMatrix[h-hshift][v-vshift] := dNdS*synRate*positionFrequencies__[transition__][nucPosInCodon__];
							CodonMatrix[v-vshift][h-hshift] := dNdS*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
						}							
					}
					else
					{
						if (_Genetic_Code[0][h]==_Genetic_Code[0][v]) 
						{
							CodonMatrix[h-hshift][v-vshift] := AT__*synRate*positionFrequencies__[transition__][nucPosInCodon__];
							CodonMatrix[v-vshift][h-hshift] := AT__*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
						}
						else
						{
							CodonMatrix[h-hshift][v-vshift] := AT__*dNdS*synRate*positionFrequencies__[transition__][nucPosInCodon__];
							CodonMatrix[v-vshift][h-hshift] := AT__*dNdS*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
						}							
					}
				}
			}
			else
			{
				if (trSM==1)
				{
					if (trLG==2)
					{
						if (_Genetic_Code[0][h]==_Genetic_Code[0][v]) 
						{
							CodonMatrix[h-hshift][v-vshift] := CG__*synRate*positionFrequencies__[transition__][nucPosInCodon__];
							CodonMatrix[v-vshift][h-hshift] := CG__*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
						}
						else
						{
							CodonMatrix[h-hshift][v-vshift] := CG__*dNdS*synRate*positionFrequencies__[transition__][nucPosInCodon__];
							CodonMatrix[v-vshift][h-hshift] := CG__*dNdS*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
						}
					}
					else
					{
						if (_Genetic_Code[0][h]==_Genetic_Code[0][v]) 
						{
							CodonMatrix[h-hshift][v-vshift] := CT__*synRate*positionFrequencies__[transition__][nucPosInCodon__];
							CodonMatrix[v-vshift][h-hshift] := CT__*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
						}
						else
						{
							CodonMatrix[h-hshift][v-vshift] := CT__*dNdS*synRate*positionFrequencies__[transition__][nucPosInCodon__];
							CodonMatrix[v-vshift][h-hshift] := CT__*dNdS*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
						}							
					}
				}
				else
				{
					if (_Genetic_Code[0][h]==_Genetic_Code[0][v]) 
					{
						CodonMatrix[h-hshift][v-vshift] := GT__*synRate*positionFrequencies__[transition__][nucPosInCodon__];
						CodonMatrix[v-vshift][h-hshift] := GT__*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
					}
					else
					{
						CodonMatrix[h-hshift][v-vshift] := GT__*dNdS*synRate*positionFrequencies__[transition__][nucPosInCodon__];
						CodonMatrix[v-vshift][h-hshift] := GT__*dNdS*synRate*positionFrequencies__[transition2__][nucPosInCodon__];
					}							
				}
			}
		}
   }
}		

Model MGModel   = (CodonMatrix,codonFrequencies,0);
for (fileID = 1; fileID <= fileCount; fileID = fileID + 1)
{
	ExecuteCommands ("Tree codonTree_" + fileID + "=" + treeStrings[fileID] + ";");
}

/* compute the branch conversion factor */

synRate = 1;
blCodon = 0;

for (h=0; h<ModelMatrixDimension; h=h+1)
{
	blCodon = blCodon - CodonMatrix[h][h]*codonFrequencies[h];
}

blNuc = 0;

t = 1;

for  (h=0; h<4; h=h+1)
{
	blCodon2 = 0;
	for (v=0; v<4; v=v+1)
	{
		if (h==v)
		{
			continue;
		}
		blCodon2 = blCodon2 + NucleotideMatrix[h][v]*overallFrequencies[v];
	}
	blNuc = blNuc + overallFrequencies[h]*blCodon2;
}

ExecuteCommands (constructLF("lf", "filteredData", "codonTree", fileCount));

if (rOptions>=2)
{
	global rConstr = 1;
	blCodon2 	   = 0;
	dNdS 		   = 2;

	for (h=0; h<ModelMatrixDimension; h=h+1)
	{
		blCodon2 = blCodon2 - CodonMatrix[h][h]*codonFrequencies[h];
	}
	
	blCodon2 = blCodon2-blCodon;
	blCodon  = blCodon-blCodon2;
	
	dNdS = 0.5;
	
	fprintf (stdout, "\n\nPhase 3:Estimating dN/dS\n\n");
	if (rOptions == 4)
	{
		global 	   rConstr := 3*blNuc__/(blCodon__+dNdS*blCodon2__);
		for (fileID = 1; fileID <= fileCount; fileID = fileID + 1)
		{
			ExecuteCommands ("ReplicateConstraint(\"this1.?.synRate:=rConstr*this2.?.t__\",codonTree_"+fileID+",nucTree_"+fileID+")");
		}
	}
	else
	{
		global 		   rConstr = 1; /*3*blNuc/(blCodon+dNdS*blCodon2)*/;
		rConstr :> 0;
		for (fileID = 1; fileID <= fileCount; fileID = fileID + 1)
		{
			ExecuteCommands ("ReplicateConstraint(\"this1.?.synRate:=this2.?.t__*rConstr\",codonTree_"+fileID+",nucTree_"+fileID+")");
		}
	}
	
	saveOM = SKIP_CONJUGATE_GRADIENT;
	SKIP_CONJUGATE_GRADIENT = 1;
	
	Optimize (resC,lf);
	SKIP_CONJUGATE_GRADIENT = saveOM;

	fprintf (stdout, "\nNuc->codon scaling factor:", (3*blNuc/(blCodon+dNdS*blCodon2)),"\nRaw scaling factor:", rConstr, "\nTree scaling factor(S): ", rConstr/(3*blNuc/(blCodon+dNdS*blCodon2)));

	if (rOptions == 3 || rOptions == 5)
	{
		COVARIANCE_PRECISION = 0.95;
		ClearConstraints(codonTree);
		for (fileID = 1; fileID <= fileCount; fileID = fileID + 1)
		{
			ExecuteCommands("ReplicateConstraint(\"this1.?.synRate:=rConstr__*this2.?.t__\",codonTree_"+fileID+",nucTree_"+fileID+")");
		}
		savedNdS = dNdS;
		COVARIANCE_PARAMETER = "dNdS";
		CovarianceMatrix (covMx, lf);	
		fprintf (stdout,"\n\nUsing dN/dS=", dNdS, "(Estimated 95% CI = [", covMx[0][0], "," , covMx[0][2], "])\nCodon model:", lf);
		dNdS = savedNdS;
		COVARIANCE_PARAMETER = 0;
	}
	else
	{
		fprintf (stdout,"\n\nUsing dN/dS=", dNdS, "\nCodon model:", lf);	
	}
}
else
{
	blNuc = 3*blNuc/blCodon;
	
	if (pipeThroughFlag == 0)
	{
		global 		   rConstr = 1;
		rConstr :> 0;
		fprintf (stdout, "Branch Corrections Factor (<0 to estimate):");
		fscanf  (stdin,  "Number", rConstr);
	}
	
	if (rConstr<=0.0)
	{
		rConstr = blNuc;
		for (fileID = 1; fileID <= fileCount; fileID = fileID + 1)
		{
			ExecuteCommands("ReplicateConstraint(\"this1.?.synRate:=this2.?.t__*rConstr\",codonTree_"+fileID+",nucTree_"+fileID+")");
		}
		dNdS := dNdS__;
		saveOM = SKIP_CONJUGATE_GRADIENT;
		SKIP_CONJUGATE_GRADIENT = 1;
		Optimize (resC,lf);
		SKIP_CONJUGATE_GRADIENT = saveOM;
		dNdS = dNdS;
	}
	else
	{
		for (fileID = 1; fileID <= fileCount; fileID = fileID + 1)
		{
			ExecuteCommands("ReplicateConstraint(\"this1.?.synRate:=this2.?.t__*rConstr__\",codonTree_"+fileID+",nucTree_"+fileID+")");
		}
	}

	fprintf (stdout, "\nNuc->Codon branch correction factor: ", blNuc);
	fprintf (stdout, "\nRaw scaling factor: ", rConstr);
	fprintf (stdout, "\nTree scaling factor(S): ", rConstr/blNuc);
	fprintf (stdout,"\n\nUsing dN/dS=", dNdS, "\nCodon model:", lf);
}

// ------ MEME helper function --------

function obtainBranchWiseEBEstimates (_sFactor,_nsFactor1,_nsFactor2, _mixingP,filterString) {

        ClearConstraints    (perBranchTree);
        ReplicateConstraint ("this1.?.alpha:=_sFactor*this2.?.synRate__",perBranchTree,codonTree);
        ReplicateConstraint ("this1.?.beta1:=_nsFactor1*sFactor*this2.?.synRate__",perBranchTree,codonTree);
        ReplicateConstraint ("this1.?.beta2:=_nsFactor2*this2.?.synRate__",perBranchTree,codonTree);
        ReplicateConstraint ("this1.?.lmp:=_mixingP", perBranchTree);
        
        
        LoadFunctionLibrary ("AncestralMapper");
        ancID = _buildAncestralCache ("siteLikelihood",0);
        subMap = _tabulateSubstitutionsAtSiteByBranch (ancID,0);
        _destroyAncestralCache (ancID);

       _bn = BranchName (perBranchTree, -1);
        
        DataSetFilter locSiteFilter = CreateFilter (ds,3,filterString,"",GeneticCodeExclusions);
        
		LikelihoodFunction siteLikelihoodLoc = (locSiteFilter, perBranchTree);
        LFCompute (siteLikelihoodLoc,LF_START_COMPUTE);
        LFCompute (siteLikelihoodLoc,baseline);
        
        _totalBranchCount  = Columns (_bn) - 1;
        posteriorEstimates = {};
        
        if (_mixingP != 1 && _mixingP != 0) {
            _priorOdds = (1-_mixingP)/_mixingP;
        } else {
            _priorOdds = 0;
        }
        
        for (k = 0; k < _totalBranchCount; k+=1)
        {
             _pname = "perBranchTree." + _bn[k] + ".lmp";
            ExecuteCommands ("`_pname`=1");
            LFCompute (siteLikelihoodLoc,LOGL0);
            
            MaxL     = -Max (LOGL0,baseline);
            
            baseline += MaxL;
            LOGL0 = Exp(MaxL+LOGL0);
            LOGL1 = (Exp(baseline) - _mixingP * LOGL0) / (1-_mixingP);
            
            ExecuteCommands ("`_pname`=_mixingP");
            _posteriorProb = {{LOGL0 * _mixingP, LOGL1 * (1-_mixingP)}};
            _posteriorProb = _posteriorProb * (1/(+_posteriorProb));
            if ( _priorOdds != 0) {
                eBF = _posteriorProb[1] / (1 - _posteriorProb[1]) / _priorOdds;
            } else {
                eBF = 1;
            }
            posteriorEstimates [_bn[k]] = {1,4};
            (posteriorEstimates [_bn[k]])[0] = _posteriorProb[1];
            (posteriorEstimates [_bn[k]])[1] = eBF;
            (posteriorEstimates [_bn[k]])[2] = (subMap[_bn[k]])[0];
            (posteriorEstimates [_bn[k]])[3] = (subMap[_bn[k]])[1];
            baseline += -MaxL;
        } 
        LFCompute (siteLikelihoodLoc,LF_DONE_COMPUTE);
        
        return posteriorEstimates;
}

// ------ MEME helper function --------

function obtainBranchWiseEBEstimatesMPI (_sFactor,_nsFactor1,_nsFactor2,_mixingP) {
        if (_nsFactor2 <= _sFactor || _mixingP == 1 || _mixingP == 0)
        {
            return {};
        }
        
        sFactor   = _sFactor;
        nsFactor1 = _nsFactor1;
        nsFactor2 = _nsFactor2;
        mixingP   = _mixingP;
        
        treeString = Format (siteTree,1,1);
        
        LoadFunctionLibrary ("AncestralMapper");
        ancID = _buildAncestralCache ("siteLikelihood",0);
        subMap = _tabulateSubstitutionsAtSiteByBranch (ancID,0);
        _destroyAncestralCache (ancID);

        Model 		MGLocalMix		=		  ("Exp(MGMatrix1)*lmp+Exp(MGMatrix2)*(1-lmp)",codonFrequencies,EXPLICIT_FORM_MATRIX_EXPONENTIAL);
        Tree        perBranchTree 			= treeString;
        ClearConstraints    (perBranchTree);
        ReplicateConstraint ("this1.?.alpha:=this2.?.alpha__",perBranchTree,siteTree);
        ReplicateConstraint ("this1.?.beta1:=this2.?.beta1__",perBranchTree,siteTree);
        ReplicateConstraint ("this1.?.beta2:=this2.?.beta2__",perBranchTree,siteTree);
        ReplicateConstraint ("this1.?.lmp:=_mixingP", perBranchTree);
        
        _bn = BranchName (perBranchTree, -1);
        
		LikelihoodFunction siteLikelihoodLoc = (siteFilter, perBranchTree);
        LFCompute (siteLikelihoodLoc,LF_START_COMPUTE);
        LFCompute (siteLikelihoodLoc,baseline);
        
        _totalBranchCount  = Columns (_bn) - 1;
        posteriorEstimates = {};
        
        _priorOdds = (1-_mixingP)/_mixingP;
        
        for (k = 0; k < _totalBranchCount; k+=1)
        {
             _pname = "perBranchTree." + _bn[k] + ".lmp";
            ExecuteCommands ("`_pname`=1");
            LFCompute (siteLikelihoodLoc,LOGL0);
            
            MaxL     = -Max (LOGL0,baseline);
            
            baseline += MaxL;
            LOGL0 = Exp(MaxL+LOGL0);
            LOGL1 = (Exp(baseline) - _mixingP * LOGL0) / (1-_mixingP);
            
            ExecuteCommands ("`_pname`=_mixingP");
            _posteriorProb = {{LOGL0 * _mixingP, LOGL1 * (1-_mixingP)}};
            _posteriorProb = _posteriorProb * (1/(+_posteriorProb));
            if ( _priorOdds != 0) {
                eBF = _posteriorProb[1] / (1 - _posteriorProb[1]) / _priorOdds;
            } else {
                eBF = 1;
            }
            posteriorEstimates [_bn[k]] = {1,4};
            (posteriorEstimates [_bn[k]])[0] = _posteriorProb[1];
            (posteriorEstimates [_bn[k]])[1] = eBF;
            (posteriorEstimates [_bn[k]])[2] = (subMap[_bn[k]])[0];
            (posteriorEstimates [_bn[k]])[3] = (subMap[_bn[k]])[1];
            baseline += -MaxL;
        } 
        
        LFCompute (siteLikelihoodLoc,LF_DONE_COMPUTE);
        
         
        return posteriorEstimates;
}
