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

function generateDatedTipConstraints (treeNameID, parameterToConstrain, tipDateAVL, rateAVL, presetValues)
{
	DT_String = "";
	DT_String * 8192;
	

	ExecuteCommands ("treePostOrderAVL = "+treeNameID+"^0;");
	ExecuteCommands ("treePreOrderAVL = "+treeNameID+"^1;");
	nodeCount 	=  Abs(treePostOrderAVL);
	doneAssignment = {};
	
	for (nodeIndex = 0; nodeIndex < nodeCount-1; nodeIndex = nodeIndex+1)
	{
		aRate = rateAVL[nodeIndex];
		if (doneAssignment[aRate] == 0)
		{
			doneAssignment[aRate] = 1;
			DT_String * ("global "+treeNameID+"_scaler_"+aRate+" = 1.0;\n"+treeNameID+"_scaler_"+aRate+" :> 0.0;");
		}
	}

	timeStops				  		  = {};

	for (nodeIndex = 1; nodeIndex < nodeCount; nodeIndex = nodeIndex+1)
	{
		nodeInfo 	= treePostOrderAVL[nodeIndex];
		nodeNameS	= nodeInfo["Name"];
		timeStops[nodeNameS] = 1e100;
	
		if (Abs(nodeInfo["Children"]))
		{
			DT_String * (" "+treeNameID+"_"+nodeNameS+"_T = 1;global "+treeNameID+"_"+nodeNameS+"_T:>(-10000); "+treeNameID+"_"+nodeNameS+"_BL = 0.0001; "+treeNameID+"_"+nodeNameS+"_BL :> 0;\n");
			if (Abs(nodeInfo["Parent"]) == 0)
			{
				
				if (minV > 0)
				{
					minV = minV/2;
				}
				else
				{
					minV = minV*2;
				}
				DT_String * (treeNameID+"_"+nodeNameS+"_T = " + minV + ";");
				timeStops[nodeNameS] = minV;
			}
		}
		else
		{
			treePostOrderAVL[nodeIndex] = nodeInfo;
		}
	}
	
	descendantsList 		  = {};
	
	for (nodeIndex = 1; nodeIndex < nodeCount; nodeIndex = nodeIndex+1)
	{
		nodeInfo 	= treePostOrderAVL[nodeIndex];
		nodeNameS	= nodeInfo["Name"];
		nodeParent = nodeInfo["Parent"];
		if (nodeParent)
		{
			parentNodeID = nodeParent;
			nodeParent = treePostOrderAVL[nodeParent];
		}
		if (Abs(nodeParent))
		{
			treePostOrderAVL[parentNodeID] = nodeParent;
			pName    = nodeParent["Name"];
			insIndex = Abs (descendantsList[pName]);
			if (insIndex == 0)
			{
				descendantsList[pName] = {};
			}
			
			rateClass = rateAVL[nodeIndex-1];
						
			if (Abs(nodeInfo["Children"]))
			{
				DT_String * (treeNameID+"."+nodeNameS+"."+parameterToConstrain+":="+treeNameID+"_scaler_"+rateClass+"*("+
							 treeNameID+"_"+nodeNameS+"_BL);"+
							 treeNameID+"_"+nodeNameS+"_T:="+treeNameID+"_"+nodeParent["Name"]+"_T+"+treeNameID+"_"+nodeNameS+"_BL;\n");
							 
				(descendantsList[pName])[insIndex] = treeNameID+"_"+nodeNameS+"_T";
				timeStops[pName] = Min (timeStops[pName],timeStops[nodeNameS]);
			}
			else
			{
				DT_String * (treeNameID+"."+nodeNameS+"."+parameterToConstrain+":="+treeNameID+"_scaler_"+rateClass+"*("+
							 tipDateAVL[nodeNameS]+"-"+treeNameID+"_"+nodeParent["Name"]+"_T);\n");			
				(descendantsList[pName])[insIndex] = tipDateAVL[nodeNameS];
				timeStops[pName] = Min (timeStops[pName],tipDateAVL[nodeNameS]);
			}
		}
	}
	
	if (Abs (presetValues))
	{
		for (nodeIndex = 2; nodeIndex < nodeCount; nodeIndex = nodeIndex+1)
		{
			nodeInfo 	= treePreOrderAVL[nodeIndex];
			if (Abs(nodeInfo["Children"]))
			{
				nodeNameS	= nodeInfo["Name"];
				DT_String * (treeNameID + "_" + nodeNameS + "_BL = " + presetValues[nodeNameS] + ";");
			}
		}	
		nodeNameS	= (treePreOrderAVL[1])["Name"];
		DT_String * (treeNameID + "_" + nodeNameS + "_T = " + presetValues[nodeNameS] + ";");
		for (nodeIndex = 0; nodeIndex < Abs(doneAssignment); nodeIndex = nodeIndex+1)
		{
			DT_String * (treeNameID+"_scaler_"+nodeIndex+" = " + presetValues["Rate"] + ";");
		}
	}	
	else
	{
		for (nodeIndex = 1; nodeIndex < nodeCount; nodeIndex = nodeIndex+1)
		{
			nodeInfo 	= treePreOrderAVL[nodeIndex];
			if (Abs(nodeInfo["Children"]) && Abs (nodeInfo["Parent"]))
			{
				nodeNameS	= nodeInfo["Name"];
				pName		= timeStops[(treePreOrderAVL[(nodeInfo["Parent"])])["Name"]];
				rateClass = Random(0.2,0.8)*(timeStops[nodeNameS] - pName);
				DT_String * (treeNameID+"_"+nodeNameS+"_BL = " + rateClass + ";");
				timeStops[nodeNameS] = pName + rateClass;			
			}
		}	
	}

	rateClass = Rows (descendantsList); 
	for (nodeIndex = 0; nodeIndex < Columns (rateClass); nodeIndex = nodeIndex + 1)
	{
		pName 				= rateClass[nodeIndex];
		nodePT				= descendantsList[pName];
		doneAssignment		= 1e100;
		for (nodeInfo = 0; nodeInfo< Abs (nodePT); nodeInfo = nodeInfo + 1)
		{
			nodeNameS = nodePT[nodeInfo];
			if (0+nodeNameS == nodeNameS+0)
			{
				doneAssignment = Min (doneAssignment, nodeNameS);
			}
			else
			{
				break;
			}
		}
		if (nodeInfo == Abs (nodePT))
		{
			DT_String * (treeNameID+"_"+pName+"_T:<"+doneAssignment+";\n");
		}
	}
	
	DT_String * 0;
	return DT_String;
}

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

function generateLFSnapshot (treeNameID, rateAVL)
{
	DT_String = "";
	DT_String * 8192;
	

	ExecuteCommands ("treePreOrderAVL = "+treeNameID+"^1;");
	nodeCount 	=  Abs(treePreOrderAVL);
	doneAssignment = {};
	
	for (nodeIndex = 0; nodeIndex < nodeCount-1; nodeIndex = nodeIndex+1)
	{
		aRate = rateAVL[nodeIndex];
		if (doneAssignment[aRate] == 0)
		{
			doneAssignment[aRate] = 1;
			ExecuteCommands ("myV = "+treeNameID+"_scaler_"+aRate+";");			
			DT_String * (treeNameID+"_scaler_"+aRate+"="+myV+";");
		}
	}
	for (nodeIndex = 2; nodeIndex < nodeCount; nodeIndex = nodeIndex+1)
	{
		nodeInfo 	= treePreOrderAVL[nodeIndex];
		if (Abs(nodeInfo["Children"]))
		{
			nodeNameS	= nodeInfo["Name"];
			ExecuteCommands ("myV = "+treeNameID + "_" + nodeNameS + "_BL;");			
			DT_String * (treeNameID + "_" + nodeNameS + "_BL = " + myV+ ";");
		}
	}	
	nodeNameS	= (treePreOrderAVL[1])["Name"];
	ExecuteCommands ("myV = "+treeNameID + "_" + nodeNameS + "_T;");			
	DT_String * (treeNameID + "_" + nodeNameS + "_T = " + myV + ";");
	DT_String * 0;
	return DT_String;
}

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

function generateTimeStampVector (treeNameID)
{
	ExecuteCommands ("iNodeCount = BranchCount("+treeNameID+");");
	exportParameterList = {iNodeCount+1,1};
	_cc					= 0;
	for (nodeIndex = 1; nodeIndex < nodeCount; nodeIndex = nodeIndex+1)
	{
		nodeInfo 	= treePostOrderAVL[nodeIndex];
		nodeNameS	= nodeInfo["Name"];
	
		if (Abs(nodeInfo["Children"]))
		{
			ExecuteCommands ("myvalue = " +treeNameID+"_"+nodeNameS+"_T");
			exportParameterList[_cc] = myvalue;
			_cc = _cc+1;
		}
	}
		
	return exportParameterList*(1/maxV);
}

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

function generateRatesVector (treeNameID, rateCount)
{
	rateCount			= rateCount+1;
	exportParameterList = {rateCount,1};
	_cc					= 0;
	for (nodeIndex = 0; nodeIndex < rateCount; nodeIndex = nodeIndex+1)
	{
		ExecuteCommands 		  ("myvalue = maxV*" +treeNameID + "_scaler_" + nodeIndex);
		exportParameterList[nodeIndex] = myvalue;
	}
	return exportParameterList;
}

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

function makeReturnAVL (dummy)
{
	retValue = {};
	retValue ["LogL"]  = res[1][0];
	retValue ["DF"]    = res[1][1];
	retValue ["rsv"]   = generateRatesVector("clockTree",Max(cString,0));
	retValue ["tsv"]   = generateTimeStampVector("clockTree");
	retValue ["tree"]  = Format(clockTree,1,1);
	
	return "_hyphyAssociativeArray="+retValue;
}
