ModelMatrixDimension = 0;

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

function BuildCodonFrequencies (obsF)
{
	PIStop = 1.0;
	result = {ModelMatrixDimension,1};
	hshift = 0;

	for (h=0; h<64; h=h+1)
	{
		first = h$16;
		second = h%16$4;
		third = h%4;
		if (_Genetic_Code[h]==10) 
		{
			hshift = hshift+1;
			PIStop = PIStop-obsF[first][0]*obsF[second][1]*obsF[third][2];
			continue; 
		}
		result[h-hshift][0]=obsF[first][0]*obsF[second][1]*obsF[third][2];
	}
	return result*(1.0/PIStop);
}

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

function BuildParametricCodonFrequencies (obsF, freqMx&)
{
	result = {ModelMatrixDimension,1};
	hshift = 0;
	normString = "1";

	for (h=0; h<64; h=h+1)
	{
		first = h$16;
		second = h%16$4;
		third = h%4;
		if (_Genetic_Code[h]==10) 
		{
			hshift = hshift+1;
			normString = normString + "-" + obsF[first][0] + "*" + obsF[second][1]+ "*" +obsF[third][2];
			continue; 
		}
		result[h-hshift]=obsF[first][0] + "*" + obsF[second][1]+ "*" +obsF[third][2];
	}
	
	freqMx = {ModelMatrixDimension,1};
	
	hshift = 0;
	for (h=0; h<64; h=h+1)
	{
		first = h$16;
		second = h%16$4;
		third = h%4;
		if (_Genetic_Code[h]==10) 
		{
			hshift = hshift+1;
		}
		else
		{
			ExecuteCommands ("freqMx[h-hshift]:="+result[h-hshift]+"/("+normString+")");
		}
	}
	return 0;
}

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

function ConstructParametricFrequencies (observedFreq)
{
	global A1:<1;
	global A2:<1;
	global A3:<1;
	global C1:<1;
	global C2:<1;
	global C3:<1;
	global G1:<1;
	global G2:<1;
	global G3:<1;
	A1 = observedFreq[0][0];
	A2 = observedFreq[0][1];
	A3 = observedFreq[0][2];
	C1 = observedFreq[1][0]/(1-A1);
	C2 = observedFreq[1][1]/(1-A2);
	C3 = observedFreq[1][2]/(1-A3);
	G1 = observedFreq[2][0]/(1-A1)/(1-C1);
	G2 = observedFreq[2][1]/(1-A2)/(1-C2);
	G3 = observedFreq[2][2]/(1-A3)/(1-C3);
	
	paramFreqs = {{"A1","A2","A3"}
				 {"(1-A1)*C1","(1-A2)*C2","(1-A3)*C3"}
				 {"(1-A1)*(1-C1)*G1","(1-A2)*(1-C2)*G2","(1-A3)*(1-C3)*G3"}
				 {"(1-A1)*(1-C1)*(1-G1)","(1-A2)*(1-C2)*(1-G2)","(1-A3)*(1-C3)*(1-G3)"}};
				 
	return 0;
}


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

function PopulateModelMatrix (ModelMatrixName&, EFV, pf)
{
	if (!ModelMatrixDimension)
	{
		ModelMatrixDimension = 64;
		for (h = 0 ;h<64; h=h+1)
		{
			if (_Genetic_Code[h]==10)
			{
				ModelMatrixDimension = ModelMatrixDimension-1;
			}
		}
	}
	
	ModelMatrixName = {ModelMatrixDimension,ModelMatrixDimension}; 

	hshift = 0;
	
	modelDefString = "";
	modelDefString*16384;
	
	catCounterAL = {};
	
	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;
					}
				}
				hs = Format(h-hshift,0,0);
				vs = Format(v-vshift,0,0);
				ts = Format(transition,0,0);
				ts2= Format(transition2,0,0);
				ps = Format(nucPosInCodon,0,0);
				aa1 = _Genetic_Code[0][h];
				aa2 = _Genetic_Code[0][v];
				if (pf == 0)
				{
					if (aa1==aa2) 
					{
						modelDefString*("ModelMatrixName["+hs+"]["+vs+"] := "+_nucBiasTerms[transition][transition2]+"synRate*EFV__["+ts+"]["+ps+"];\n"+
														 "ModelMatrixName["+vs+"]["+hs+"] := "+_nucBiasTerms[transition][transition2]+"synRate*EFV__["+ts2+"]["+ps+"];\n");
					}
					else
					{
						bt = aaRateMultipliers[aa1][aa2];
						if (catCounterAL[bt] == 0)
						{
							catCounterAL[bt] = 1;
							modelDefString*("\nglobal NSR"+bt+"=1;\n");
						}
						bt = "NSR"+bt;
						modelDefString*("ModelMatrixName["+hs+"]["+vs+"] := "+_nucBiasTerms[transition][transition2]+bt+"*synRate*EFV__["+ts+"]["+ps+"];\n"+
														 "ModelMatrixName["+vs+"]["+hs+"] := "+_nucBiasTerms[transition][transition2]+bt+"*synRate*EFV__["+ts2+"]["+ps+"];\n");	
					}
				}
				else
				{
					if (aa1==aa2) 
					{
						modelDefString*("ModelMatrixName["+hs+"]["+vs+"] := "+_nucBiasTerms[transition][transition2]+"synRate*"+EFV[transition][nucPosInCodon]+";\n"+
														 "ModelMatrixName["+vs+"]["+hs+"] := "+_nucBiasTerms[transition][transition2]+"synRate*"+EFV[transition2][nucPosInCodon]+";\n");
					}
					else
					{
						bt = aaRateMultipliers[aa1][aa2];
						if (catCounterAL[bt] == 0)
						{
							catCounterAL[bt] = 1;
							modelDefString*("\nglobal NSR"+bt+"=1;\n");
						}
						bt = "NSR"+bt;
						modelDefString*("ModelMatrixName["+hs+"]["+vs+"] := "+_nucBiasTerms[transition][transition2]+bt+"*synRate*"+EFV[transition][nucPosInCodon]+";\n"+
														 "ModelMatrixName["+vs+"]["+hs+"] := "+_nucBiasTerms[transition][transition2]+bt+"*synRate*"+EFV[transition2][nucPosInCodon]+";\n");	
					}				
				}
			}
	    }
    }		
	modelDefString*0;
	ExecuteCommands (modelDefString);
	return 0;
}

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

function FindBranchLengthExpression (dummy, rateMatrix&)
{
	/* first decide how many rate classes there are */
	
	rcCount = 0;
	for (h=0; h<21; h=h+1)
	{
		for (v=h+1; v<21; v=v+1)
		{
			if (aaRateMultipliers[h][v]>rcCount)
			{
				rcCount = aaRateMultipliers[h][v];
			}
		}
	}
	
	rcCount = rcCount+1;
	synWeight = 0;
	nsWeights = {rcCount,1};
	
	if (dummy)
	{
		for (h=0; h<rcCount; h=h+1)
		{
			ExecuteCommands ("NSR"+h+"=1;");
		}
	}
	
	synRate = 1;
	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; 
			}
			if ((h$4==v$4)||((diff%4==0)&&(h$16==v$16))||(diff%16==0))
			{
				aa1 = _Genetic_Code[0][h];
				aa2 = _Genetic_Code[0][v];
				if (aa1==aa2 || dummy == 0) 
				{
					synWeight = synWeight + rateMatrix[h-hshift][v-vshift]*vectorOfFrequencies[h-hshift]+rateMatrix[v-vshift][h-hshift]*vectorOfFrequencies[v-vshift];
				}
				else
				{
					bt = aaRateMultipliers[aa1][aa2];
					nsWeights[bt] = nsWeights[bt] + rateMatrix[h-hshift][v-vshift]*vectorOfFrequencies[h-hshift]+rateMatrix[v-vshift][h-hshift]*vectorOfFrequencies[v-vshift];
				}
			}
	    }
    }	
    if (dummy)	
    {
	    resString = "";
	    resString * 1024;
	    resString * (""+synWeight);
		for (h=0; h<rcCount; h=h+1)
		{
			resString*("+NSR"+h+"*"+nsWeights[h]);
		}
    	resString * 0;
		return resString;
	}
	return ""+synWeight;
}

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

function replicateBranchLengths (treeID)
{
	mFactor = "(" + mgPlainFactor+ ")/(" + FindBranchLengthExpression(1,"MG94custom") + ")";
	bNames  = Rows (presetBranchParameters[treeID]);
	for (brCount = Columns(bNames)-1; brCount >= 0; brCount = brCount - 1)
	{
		tn = bNames[brCount];
		ExecuteCommands ("codon_tree_"+treeID+"." + tn + ".synRate:=" + mFactor + "*" + (presetBranchParameters[treeID])[tn] + ";");
	}
	return 0;
}

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

function makeReturnValue (dummy)
{
	retVal = "lf_MLES = " + lf_MLES + ";lf_MLE_VALUES={};\n";
	GetInformation (nsr_list, "^NSR");
	for (brCount = 0; brCount < Columns (nsr_list); brCount = brCount + 1)
	{
		ExecuteCommands ("ns_val = " + nsr_list[brCount] + ";");
		retVal = retVal + "lf_MLE_VALUES[\"" + nsr_list[brCount] + "\"] = " + ns_val + ";\n";
	}
	return retVal;
}

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

function constructLF (lf_name, filter_pref, tree_pref, part_count)
{
	lfDefCommand = "";
	lfDefCommand * 128;
	lfDefCommand * ("LikelihoodFunction  " + lf_name + " = (" + filter_pref + "_1," + tree_pref + "_1");
	
	for (_partID = 2; _partID <= part_count; _partID = _partID + 1)
	{
		lfDefCommand * ("," + filter_pref + "_" + _partID + "," + tree_pref + "_" +  _partID);
	}
	
	lfDefCommand * ");";
	lfDefCommand * 0;
	return lfDefCommand;
}
