LoadFunctionLibrary ("CF3x4");

function BuildCodonFrequencies (obsF)
{
	if (!ModelMatrixDimension)
	{
		ModelMatrixDimension = 64;
		for (h = 0 ;h<64; h=h+1)
		{
			if (_Genetic_Code[h]==10)
			{
				ModelMatrixDimension = ModelMatrixDimension-1;
			}
		}
	}

	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 ReportSite1 (siteI, siteM)
{
	fullSites[siteI][0] = doneSites[siteM][0];
	fullSites[siteI][1] = doneSites[siteM][1];
	fullSites[siteI][2] = doneSites[siteM][2];
	fullSites[siteI][3] = doneSites[siteM][3];

	fprintf (stdout, "Site ", Format(siteI+1,5,0)," dN/dS = ", Format(doneSites[siteM][0],12,5),
					 " Full Log(L) = ", Format(doneSites[siteM][3],12,5),
					 " LRT = ",Format(doneSites[siteM][1],12,5),
					 " p-value = ",Format(doneSites[siteM][2],12,5));

	if (doneSites[siteM][2]<pValue)
	{
		if (fullSites[siteI][0]>1)
		{
			fprintf (stdout, " *P");
		}
		else
		{
			fprintf (stdout, " *N");
		}

	}
	fprintf (stdout, "\n");
	return 0;
}

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

function ReportSite2 (siteI, siteM)
{
	fullSites[siteI][0] = doneSites[siteM][0];
	fullSites[siteI][1] = doneSites[siteM][1];
	fullSites[siteI][2] = doneSites[siteM][6];
	fullSites[siteI][3] = doneSites[siteM][2];
	fullSites[siteI][4] = doneSites[siteM][3];
	fullSites[siteI][5] = doneSites[siteM][4];
	fullSites[siteI][6] = doneSites[siteM][5];
	if (Columns (fullSites) > 7)
	{
		fullSites[siteI][7] = doneSites[siteM][7];
	}


	fprintf (stdout, "| Codon: ", 		Format(siteI+1,4,0),
					 "| dN/dS: ", 		Format(fullSites[siteI][2],10,2),
					 "| dN: ", 			Format(fullSites[siteI][0],5,2),
					 "| dS: ", 			Format(fullSites[siteI][1],5,2),
					 "| dS(=dN): ",		Format(fullSites[siteI][3],5,2),
					 "| Log(L): ",		Format(fullSites[siteI][6],10,2),
					 "| LRT: ",			Format(fullSites[siteI][4],5,2),
					 "| p: ",			Format(fullSites[siteI][5],5,2));

	if (Columns (fullSites) > 7)
	{
		fprintf (stdout, "| dN_other: ", 		Format(fullSites[siteI][7],5,2));
	}

	if (fullSites[siteI][5]<pValue)
	{
		if (fullSites[siteI][0]>fullSites[siteI][1])
		{
			fprintf (stdout, " *P");
		}
		else
		{
			fprintf (stdout, " *N");
		}
	}
	fprintf (stdout, "\n");
	return 0;
}



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

function ReportSite3 (siteI, siteM)
{
	fullSites[siteI][0] = doneSites[siteM][0];
	fullSites[siteI][1] = doneSites[siteM][1];
	fullSites[siteI][2] = doneSites[siteM][2];


	fprintf (stdout, "Site ", Format(siteI+1,4,0),
					 " dS = ", Format(fullSites[siteI][0],7,4),
					 " dN = ", Format(fullSites[siteI][1],7,4),
					 " Log(L) ",Format(fullSites[siteI][2],7,4),"\n");

	return 0;
}

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

function ReceiveJobs1 (sendOrNot)
{
	MPIReceive (-1, fromNode, result_String);

	siteIndex = MPINodeState[fromNode-1][1];

	if (sendOrNot)
	{
		MPISend (fromNode,siteLikelihood);
		MPINodeState[fromNode-1][1] = siteCount;
	}
	else
	{
		MPINodeState[fromNode-1][0] = 0;
		MPINodeState[fromNode-1][1] = -1;
	}

	ExecuteCommands (result_String);

	siteMap = dupInfo[siteIndex];
	doneSites[siteMap][0] = dNdS;
	dNdS = 1;
	LFCompute (siteLikelihood,LF_START_COMPUTE);
	LFCompute (siteLikelihood,nullLF);
	LFCompute (siteLikelihood,LF_DONE_COMPUTE);


	doneSites[siteMap][1] = 2*(siteLikelihood_MLES[1][0]-nullLF);
	doneSites[siteMap][2] = (1-CChi2(doneSites[siteMap][1],1))/2;
	doneSites[siteMap][3] = siteLikelihood_MLES[1][0];

	dummy = ReportSite1 (siteIndex, siteMap);

	return fromNode-1;
}
/*------------------------------------------------------------------------*/

function ReportSiteMEME (siteI, siteM, doQ)
{
    // labels       = {{"omega1","omega2","weight1","weight2","Length_scaler","LRT","p-value","Full Log(L)"}};
	fullSites[siteI][0] = doneSites[siteM][0];
	fullSites[siteI][1] = doneSites[siteM][1];
	fullSites[siteI][2] = doneSites[siteM][2];
	fullSites[siteI][3] = 1-doneSites[siteM][2];
	fullSites[siteI][4] = doneSites[siteM][7];
	fullSites[siteI][5] = doneSites[siteM][3];
	fullSites[siteI][6] = doneSites[siteM][4];
	fullSites[siteI][8] = doneSites[siteM][5];

    if (doQ >= 0) {
       fullSites[siteI][7] = doQ;
       qStr = "| q: " + Format(fullSites[siteI][7],5,2);
    } else {
       qStr = "";
    }


	fprintf (stdout, "| Codon: ", 		Format(siteI+1,4,0),
					 "| Beta1: ", 		Format(fullSites[siteI][0],10,2),
					 "| P(Beta1): ", 	Format(fullSites[siteI][2],5,2),
					 "| Beta2: ", 		Format(fullSites[siteI][1],10,2),
					 "| P(Beta2): ",	Format(fullSites[siteI][3],5,2),
					 "| alpha: ",		Format(fullSites[siteI][4],10,2),
					 "| LRT: ",			Format(fullSites[siteI][5],6,2),
					 "| p: ",			Format(fullSites[siteI][6],5,2),
                     qStr,
					 "| Log(L): ",		Format(fullSites[siteI][8],5,2));

	if (fullSites[siteI][6]<pValue)
	{
		if (fullSites[siteI][1]>fullSites[siteI][4])
		{
			fprintf (stdout, " *P");
		}
	}
	fprintf (stdout, "\n");
	return 0;
}

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

function ReportSiteMEMEInternal (siteI, siteM, doQ)
{
    // labels       = {{"beta1","beta2","weight1","weight2","beta1[leaves]","beta2[leaves]","weight1[leaves]","weight2[leaves]","Length_scaler","LRT","p-value","q-value","Full Log(L)"}};
	//                        0       1               2        3            4        5            6                7                   8           9      10        11         12
	fullSites[siteI][0] = doneSites[siteM][0];
	fullSites[siteI][1] = doneSites[siteM][1];
	fullSites[siteI][2] = doneSites[siteM][2];
	fullSites[siteI][3] = 1-doneSites[siteM][2];

	fullSites[siteI][4] = doneSites[siteM][6];
	fullSites[siteI][5] = doneSites[siteM][7];
	fullSites[siteI][6] = doneSites[siteM][8];
	fullSites[siteI][7] = 1-doneSites[siteM][8];


	fullSites[siteI][8] = doneSites[siteM][9];
	fullSites[siteI][9] = doneSites[siteM][3];
	fullSites[siteI][10] = doneSites[siteM][4];
	fullSites[siteI][12] = doneSites[siteM][5];

    if (doQ >= 0) {
       fullSites[siteI][11] = doQ;
       qStr = "| q: " + Format(fullSites[siteI][11],5,2);
    } else {
       qStr = "";
    }


	fprintf (stdout, "| Codon: ", 		Format(siteI+1,4,0),
					 "| Internal: ", 	Format(fullSites[siteI][0],8,2), "[", Format(fullSites[siteI][2],4,2), "], ", Format(fullSites[siteI][1],8,2), "[",  Format(1-fullSites[siteI][2],4,2), "]",
					 "| Leaves: ", 	    Format(fullSites[siteI][4],8,2), "[", Format(fullSites[siteI][6],4,2), "], ", Format(fullSites[siteI][5],8,2), "[",  Format(1-fullSites[siteI][6],4,2), "]",
					 "| alpha: ",		Format(fullSites[siteI][8],8,2),
					 "| LRT: ",			Format(fullSites[siteI][9],6,2),
					 "| p: ",			Format(fullSites[siteI][10],5,2),
                     qStr,
					 "| Log(L): ",		Format(fullSites[siteI][12],5,2));

	if (fullSites[siteI][10]<pValue)
	{
		if (fullSites[siteI][1]>fullSites[siteI][8])
		{
			fprintf (stdout, " *P");
		}
	}
	fprintf (stdout, "\n");
	return 0;
}
/*------------------------------------------------------------------------*/

function ReceiveJobs4 (sendOrNot, nullAlt)
{
	MPIReceive (-1, fromNode, result_String);

	siteIndex = MPINodeState[fromNode-1][1];
	siteNA	  = MPINodeState[fromNode-1][2];

	if (sendOrNot)
	{
		MPISend (fromNode,siteLikelihood);
		MPINodeState[fromNode-1][1] = siteCount;
		MPINodeState[fromNode-1][2] = nullAlt;
	}
	else
	{
		MPINodeState[fromNode-1][0] = 0;
		MPINodeState[fromNode-1][1] = -1;
	}

	siteMap = dupInfo[siteIndex];

	ExecuteCommands (result_String);

	if (siteNA)
	{
		doneSites[siteMap][0] = dNdS;
		doneSites[siteMap][1] = doneSites[siteMap][1]+2*siteLikelihood_MLES[1][0];
		doneSites[siteMap][3] = siteLikelihood_MLES[1][0];
	}
	else
	{
		doneSites[siteMap][1] = doneSites[siteMap][1]-2*siteLikelihood_MLES[1][0];
	}

	if (doneSites[siteMap][2] == 0)
	{
		doneSites[siteMap][2] = -1;
	}
	else
	{
		if (doneSites[siteMap][2] == (-1))
		{
			doneSites[siteMap][2] = 1-CChi2(doneSites[siteMap][1],1);
			dummy = ReportSite1 (siteIndex, siteMap);
		}
	}

	return fromNode-1;
}

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

function ReceiveJobs2 (sendOrNot, nullAlt)
{
	MPIReceive (-1, fromNode, result_String);

	siteIndex = MPINodeState[fromNode-1][1];
	siteNA	  = MPINodeState[fromNode-1][2];

	if (sendOrNot)
	{
		MPISend (fromNode,siteLikelihood);
		MPINodeState[fromNode-1][1] = siteCount;
		MPINodeState[fromNode-1][2] = nullAlt;
		MPINodeState[fromNode-1][3] = MPINodeState[fromNode-1][3]+1;
	}
	else
	{
		MPINodeState[fromNode-1][0] = 0;
		MPINodeState[fromNode-1][1] = -1;
	}

	siteMap = dupInfo[siteIndex];

	ExecuteCommands (result_String);

	nFValue = siteLikelihood_MLE_VALUES ["nFactor"];
	sFValue = siteLikelihood_MLE_VALUES ["sFactor"];
	if (Columns (doneSites) > 7)
	{
		noFValue = siteLikelihood_MLE_VALUES ["nFactorOther"];
	}

	if (siteNA)
	{
		doneSites[siteMap][0] = nFValue;
		doneSites[siteMap][1] = sFValue;
		if (Columns (doneSites) > 7)
		{
			doneSites[siteMap][7] = noFValue;
		}

			/*if ((sFactor > 1e-10) || (nFactor > 1e-10))
			{
				svpc = COVARIANCE_PRECISION;
				COVARIANCE_PRECISION = -1;
				if (sFactor < 1e-10 && nFactor > 1e-10)
				{
					global inverseRatioEstimator = 0;
					sFactor := inverseRatioEstimator*nFactor;
					COVARIANCE_PARAMETER = "inverseRatioEstimator";
					CovarianceMatrix (covMx,siteLikelihood);
					sFactor = doneSites[siteMap][1];
					doneSites[siteMap][6] = 1/covMx[0][2];
				}
				else
				{
					if (sFactor > 1e-10 && nFactor < 1e-10)
					{
						global inverseRatioEstimator = 0;
						nFactor := inverseRatioEstimator*sFactor;
						COVARIANCE_PARAMETER = "inverseRatioEstimator";
						CovarianceMatrix (covMx,siteLikelihood);
						nFactor = doneSites[siteMap][0];
						doneSites[siteMap][6] = 1/covMx[0][2];
					}
					else
					{
						global inverseRatioEstimator = nFactor/sFactor;
						nFactor := inverseRatioEstimator*sFactor;
						COVARIANCE_PARAMETER = "inverseRatioEstimator";
						CovarianceMatrix (covMx,siteLikelihood);
						nFactor = doneSites[siteMap][0];
						if (nFactor>sFactor)
						{
							doneSites[siteMap][6] = covMx[0][0];
						}
						else
						{
							doneSites[siteMap][6] = 1/covMx[0][2];
						}
					}
				}
				COVARIANCE_PRECISION = svpc;
			}
			else
			{
				doneSites[siteMap][6] = 0;
			}*/

		doneSites[siteMap][6] = nFValue/sFValue;
		doneSites[siteMap][3] = doneSites[siteMap][3]+2*siteLikelihood_MLES[1][0];
		doneSites[siteMap][5] = siteLikelihood_MLES[1][0];
	}
	else
	{
		doneSites[siteMap][3] = doneSites[siteMap][3]-2*siteLikelihood_MLES[1][0];
		doneSites[siteMap][2] = sFValue;
	}

	if (doneSites[siteMap][4] == 0)
	{
		doneSites[siteMap][4] = -1;
	}
	else
	{
		if (doneSites[siteMap][4] == (-1))
		{
			doneSites[siteMap][4] = 1-CChi2(doneSites[siteMap][3],1);
			dummy = ReportSite2 (siteIndex, siteMap);
		}
	}

	return fromNode-1;
}


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

function MPISendJobMEMEInternal ()
{
    for (mpiNode = 0; mpiNode < MPI_NODE_COUNT-1; mpiNode += 1)
    {
        if (MPINodeState[mpiNode][0]==0)
        {
            break;
        }
    }

    if (mpiNode == MPI_NODE_COUNT-1)
    {
        mpiNode = ReceiveJobsMEMEInternal ();
    }


    lastKey = Abs(toDoList);

    if (lastKey > 0)
    {
        lastKey  = toDoList ["INDEXORDER"][0];
        theJob   = toDoList [lastKey];
        toDoList - lastKey;
        MPINodeState[mpiNode][1] = theJob[0];

        filterString = "" + (theJob[0]*3) + "-" + (theJob[0]*3+2);
        DataSetFilter siteFilter = CreateFilter (filteredData,3,filterString,"",GeneticCodeExclusions);

        if (theJob[1] == 2)
        {
            sFactor   = 1;
            ClearConstraints (nsFactor2);
            nsFactor2 :> 0;
            nsFactor2 = dNdS;
            OPTIMIZATION_METHOD = 0;
            MPISend (mpiNode+1, felLikelihood);

            if (debugVerboseFlag)
            {
                fprintf (stdout, "[DEBUG: Sending FEL fit of site ", theJob[0], " to node ", mpiNode+1, "]\n");
            }
        }
        else
        {
            /* doneSites[siteMap][0] = nsFactor1;
                                            doneSites[siteMap][1] = nsFactor2;
                                            doneSites[siteMap][2] = mixingP;
                                            doneSites[siteMap][6] = nsFactor1leaves;
                                            doneSites[siteMap][7] = nsFactor2leaves;
                                            doneSites[siteMap][8] = mixingPleaves;
                                            doneSites[siteMap][9] = sFactor;
                                            */

            if (theJob[1] == 1)
            {
                sFactor   = bySiteCache[theJob[0]][0];
                ClearConstraints (nsFactor2);
                ClearConstraints (nsFactor2leaves);
                nsFactor2 :> 0;
                nsFactor2 = bySiteCache[theJob[0]][1];
                if (nsFactor2 > sFactor)
                {
                    nsFactor1           =  1;
                    mixingP             = 0.25;
                    nsFactor2leaves     = 1;
                    mixingPleaves       = 1;
                }
                else
                {
                    nsFactor1    = nsFactor2/sFactor;
                    if (nsFactor2 == 0)
                    {
                        nsFactor2    = sFactor*0.5;
                        mixingP      = 0.05;
                        nsFactor2leaves    = sFactor*0.5;
                        mixingPleaves      = 0.05;

                    }
                    else
                    {
                        nsFactor2    = sFactor*1.5;
                        nsFactor2leaves    = sFactor*0.5;
                        mixingP      = 0.05;
                        mixingPleaves      = 0.75;
                    }
                }
                OPTIMIZATION_METHOD = 0;
                LF_NEXUS_EXPORT_EXTRA = "siteID = " + theJob[0] + "; jobSuffix = \".alt\";" + _memeExtra;
                MPISend (mpiNode+1, siteLikelihood);
                //fName = "/Volumes/sergei-raid/Desktop/" + theJob[0];
                // fprintf (fName, CLEAR_FILE, MPI_LAST_SENT_MSG);
                LF_NEXUS_EXPORT_EXTRA = "";
                if (debugVerboseFlag)
                {
                    fprintf (stdout, "[DEBUG: Sending MEME fit of site ", theJob[0], " to node ", mpiNode+1, "]");
                    fprintf (stdout, "\n\talpha  = ", sFactor,
                                     "\n\tbeta1  = ", nsFactor1*sFactor,
                                     "\n\tbeta2  = ", nsFactor2,
                                     "\n\tmixing = ", mixingP,
                                     "\n\tbeta1[leaves]  = ", nsFactor1leaves*sFactor,
                                     "\n\tbeta2[leaves]  = ", nsFactor2leaves,
                                     "\n\tmixing[leaves] = ", mixingPleaves,
                                     "\n");
                }
            }
            else
            {
                sFactor      = bySiteCache[theJob[0]][0];
                nsFactor1    = bySiteCache[theJob[0]][1];
                mixingP      = bySiteCache[theJob[0]][2];

                nsFactor1leaves    = bySiteCache[theJob[0]][3];
                nsFactor2leaves    = bySiteCache[theJob[0]][4];
                mixingPleaves      = bySiteCache[theJob[0]][5];

                omega2       = 1;
                nsFactor2    := omega2 * sFactor;
                if (sFactor == 0)
                {
                    sFactor = 0.001;
                }
                OPTIMIZATION_METHOD = 0;
                LF_NEXUS_EXPORT_EXTRA =  "siteID = " + theJob[0] + "; jobSuffix = \".null\";" + _memeExtraNull;
                MPISend (mpiNode+1, siteLikelihood);
                LF_NEXUS_EXPORT_EXTRA = "";
                if (debugVerboseFlag)
                {
                    fprintf (stdout, "[DEBUG: Sending MEME NULL fit of site ", theJob[0], " to node ", mpiNode+1, "]");
                    fprintf (stdout, "\n\talpha  = ", sFactor,
                                     "\n\tbeta1  = ", nsFactor1*sFactor,
                                     "\n\tbeta2  = ", nsFactor2,
                                     "\n\tmixing = ", mixingP,
                                     "\n\tbeta1[leaves]  = ", nsFactor1leaves*sFactor,
                                     "\n\tbeta2[leaves]  = ", nsFactor2leaves,
                                     "\n\tmixing[leaves] = ", mixingPleaves,
                                     "\n");
                }
            }
        }
 		MPINodeState[mpiNode][0] = 1;
		MPINodeState[mpiNode][1] = theJob[0];
		MPINodeState[mpiNode][2] = theJob[1];
        MPINodeState[mpiNode][3] += 1;

        if (debugVerboseFlag)
        {
            fileOut = "/tmp/" + theJob[0] + "." + theJob[1];
            fprintf (fileOut, CLEAR_FILE, MPI_LAST_SENT_MSG);
        }
    }
    else
    {
        if (+(MPINodeState [-1][0]))
        {
            ReceiveJobsMEMEInternal();
        }
    }

    return +(MPINodeState [-1][0]) || Abs(toDoList);
}

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

function MPISendJobMEME ()
{
    for (mpiNode = 0; mpiNode < MPI_NODE_COUNT-1; mpiNode += 1)
    {
        if (MPINodeState[mpiNode][0]==0)
        {
            break;
        }
    }

    if (mpiNode == MPI_NODE_COUNT-1)
    {
        mpiNode = ReceiveJobsMEME ();
    }


    lastKey = Abs(toDoList);

    if (lastKey > 0)
    {
        lastKey  = toDoList ["INDEXORDER"][0];
        theJob   = toDoList [lastKey];
        toDoList - lastKey;
        MPINodeState[mpiNode][1] = theJob[0];

        filterString = "" + (theJob[0]*3) + "-" + (theJob[0]*3+2);
        DataSetFilter siteFilter = CreateFilter (filteredData,3,filterString,"",GeneticCodeExclusions);

        if (theJob[1] == 2)
        {
            sFactor   = 1;
            ClearConstraints (nsFactor2);
            nsFactor2 :> 0;
            nsFactor2 = dNdS;
            OPTIMIZATION_METHOD = 0;
            MPISend (mpiNode+1, felLikelihood);

            if (debugVerboseFlag)
            {
                fprintf (stdout, "[DEBUG: Sending FEL fit of site ", theJob[0], " to node ", mpiNode+1, "]\n");
            }
        }
        else
        {
            if (theJob[1] == 1)
            {
                sFactor   = bySiteCache[theJob[0]][0];
                ClearConstraints (nsFactor2);
                nsFactor2 :> 0;
                nsFactor2 = bySiteCache[theJob[0]][1];
                if (nsFactor2 > sFactor)
                {
                    nsFactor1    = 1;
                    mixingP      = 0.25;
                }
                else
                {
                    nsFactor1    = nsFactor2/sFactor;
                    if (nsFactor2 == 0)
                    {
                        nsFactor2    = sFactor*0.5;
                        mixingP      = 0.05;
                    }
                    else
                    {
                        nsFactor2    = sFactor*1.5;
                        mixingP      = 0.75;
                    }
                }
                OPTIMIZATION_METHOD = 0;
                LF_NEXUS_EXPORT_EXTRA = "siteID = " + theJob[0] + "; jobSuffix = \".alt\";" + _memeExtra;
                MPISend (mpiNode+1, siteLikelihood);
                //fName = "/Users/sergei/Desktop/" + theJob[0];
                //fprintf (fName, CLEAR_FILE, MPI_LAST_SENT_MSG);
                LF_NEXUS_EXPORT_EXTRA = "";
                if (debugVerboseFlag)
                {
                    fprintf (stdout, "[DEBUG: Sending MEME fit of site ", theJob[0], " to node ", mpiNode+1, "]");
                    fprintf (stdout, "\n\talpha  = ", sFactor,
                                     "\n\tbeta1  = ", nsFactor1*sFactor,
                                     "\n\tbeta2  = ", nsFactor2,
                                     "\n\tmixing = ", mixingP,
                                     "\n");
                }
            }
            else
            {
                sFactor      = bySiteCache[theJob[0]][0];
                nsFactor1    = bySiteCache[theJob[0]][1];
                mixingP      = bySiteCache[theJob[0]][2];
                omega2       = 1;
                nsFactor2    := omega2 * sFactor;
                if (sFactor == 0)
                {
                    sFactor = 0.001;
                }
                OPTIMIZATION_METHOD = 0;
                LF_NEXUS_EXPORT_EXTRA =  "siteID = " + theJob[0] + "; jobSuffix = \".null\";" + _memeExtraNull;
                MPISend (mpiNode+1, siteLikelihood);
                LF_NEXUS_EXPORT_EXTRA = "";
                if (debugVerboseFlag)
                {
                    fprintf (stdout, "[DEBUG: Sending MEME NULL fit of site ", theJob[0], " to node ", mpiNode+1, "]");
                    fprintf (stdout, "\n\talpha  = ", sFactor,
                                     "\n\tbeta1  = ", nsFactor1*sFactor,
                                     "\n\tbeta2  = ", nsFactor2,
                                     "\n\tmixing = ", mixingP,
                                     "\n");
                }
            }
        }
 		MPINodeState[mpiNode][0] = 1;
		MPINodeState[mpiNode][1] = theJob[0];
		MPINodeState[mpiNode][2] = theJob[1];
        MPINodeState[mpiNode][3] += 1;

        if (debugVerboseFlag)
        {
            fileOut = "/tmp/" + theJob[0] + "." + theJob[1];
            fprintf (fileOut, CLEAR_FILE, MPI_LAST_SENT_MSG);
        }
    }
    else
    {
        if (+(MPINodeState [-1][0]))
        {
            ReceiveJobsMEME();
        }
    }

    return +(MPINodeState [-1][0]) || Abs(toDoList);
}

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

function ReceiveJobsMEME ()
{
	MPIReceive (-1, fromNode, result_String);

	//fprintf (stdout, result_String, "\n");

	siteIndex = MPINodeState[fromNode-1][1];
	siteNAF	  = MPINodeState[fromNode-1][2];

    MPINodeState[fromNode-1][0] = 0;
    MPINodeState[fromNode-1][1] = -1;

	siteMap = dupInfo[siteIndex];

	if (siteNAF == 1)
	{
	    res = Eval (result_String);
	    siteLikelihood_MLES = res["MLES"];
	    siteLikelihood_MLE_VALUES = res["VALUES"];
	    byBranchEstimates    = res["BRANCHES"];
	    bySiteBranchReports [siteMap] = byBranchEstimates;
	}
	else
	{
	    ExecuteCommands (result_String);
	}

    if (siteNAF < 2)
    {
        nsf1V   = siteLikelihood_MLE_VALUES ["nsFactor1"];
        nsf2V   = siteLikelihood_MLE_VALUES ["nsFactor2"];

        nsf1V   = siteLikelihood_MLE_VALUES ["nsFactor1"];
        nsf2V   = siteLikelihood_MLE_VALUES ["nsFactor2"];

        omega2F = siteLikelihood_MLE_VALUES ["omega2"];

        mixingF = siteLikelihood_MLE_VALUES ["mixingP"];
        sFValue = siteLikelihood_MLE_VALUES ["sFactor"];
    }
    else
    {
        nsf2V   = felLikelihood_MLE_VALUES ["nsFactor2"];
        sFValue = felLikelihood_MLE_VALUES ["sFactor"];
    }

	if (siteNAF == 1) // alternative
	{
        doneSites[siteMap][0] = nsf1V*sFValue;
        doneSites[siteMap][1] = nsf2V;
        doneSites[siteMap][2] = mixingF;
        doneSites[siteMap][6] = 1-mixingF;
        doneSites[siteMap][7] = sFValue;

		doneSites[siteMap][3] = doneSites[siteMap][3]+2*siteLikelihood_MLES[1][0];
		doneSites[siteMap][5] = siteLikelihood_MLES[1][0];

        bySiteCache[siteIndex][0] = sFValue;
        bySiteCache[siteIndex][1] = nsf1V;
        bySiteCache[siteIndex][2] = mixingF;

        if (debugVerboseFlag)
        {
            fprintf (stdout, "[DEBUG: Received MEME alternative fit of site ", siteIndex, " from node ", fromNode, "]");
            fprintf (stdout, "\n\talpha  = ", doneSites[siteMap][7],
                             "\n\tbeta1  = ", doneSites[siteMap][0],
                             "\n\tbeta2  = ", doneSites[siteMap][1],
                             "\n\tmixing = ", doneSites[siteMap][2],
                             "\n");
        }


       if (nsf2V > sFValue)
        {
            toDoList["MEME_NULL_" + siteIndex] = {{siteIndex__,0}};
            if (debugVerboseFlag)
            {
                fprintf (stdout, "[DEBUG: Added null model fit for site ", siteIndex, " to the queue]\n");
            }
        }
        else
        {
            doneSites[siteMap][3] = 0;
            doneSites[siteMap][4] = -1;
        }
	}
	else
    {
        if (siteNAF == 2) // FEL
        {
            bySiteCache[siteIndex][0] = sFValue;
            bySiteCache[siteIndex][1] = nsf2V;
            toDoList ["MEME_ALT_" + siteIndex] = {{siteIndex__,1}};
            if (debugVerboseFlag)
            {
                fprintf (stdout, "[DEBUG: Received FEL fit of site ", siteIndex, " from node ", fromNode, "]");
                fprintf (stdout, "\n\talpha  = ", sFValue,
                                 "\n\tbeta  = ", nsf2V,
                                 "\n");
            }

        }
        else // null
        {
            doneSites[siteMap][3] = doneSites[siteMap][3]-2*siteLikelihood_MLES[1][0];
            if (debugVerboseFlag)
            {
                fprintf (stdout, "[DEBUG: Received MEME NULL fit of site ", siteIndex, " from node ", fromNode, "]");
                fprintf (stdout, "\n\talpha  = ",  sFValue,
                                 "\n\tbeta1  = ",  nsf1V,
                                 "\n\tomega2  = ", omega2F,
                                 "\n\tmixing = ",  mixingF,
                                 "\n");
            }
        }
    }

    if (siteNAF < 2)
    {
        if (doneSites[siteMap][4] == 0)
        {
            doneSites[siteMap][4] = -1;
        }
        else
        {
            if (doneSites[siteMap][4] == (-1))
            {
                doneSites[siteMap][4] = 2/3-2/3*(0.45*CChi2(doneSites[siteMap][3],1)+0.55*CChi2(doneSites[siteMap][3],2));
                ReportSiteMEME (siteIndex, siteMap, -1);
            }
        }
    }

	return fromNode-1;
}

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

function ReceiveJobsMEMEInternal ()
{
	MPIReceive (-1, fromNode, result_String);


	siteIndex = MPINodeState[fromNode-1][1];
	siteNAF	  = MPINodeState[fromNode-1][2];

    MPINodeState[fromNode-1][0] = 0;
    MPINodeState[fromNode-1][1] = -1;

	siteMap = dupInfo[siteIndex];

	if (siteNAF == 1)
	{
	    res = Eval (result_String);
	    siteLikelihood_MLES = res["MLES"];
	    siteLikelihood_MLE_VALUES = res["VALUES"];
	    byBranchEstimates    = res["BRANCHES"];
	    bySiteBranchReports [siteMap] = byBranchEstimates;
	}
	else
	{
	    ExecuteCommands (result_String);
	}

    if (siteNAF < 2)
    {
        //fprintf (stdout, siteLikelihood_MLE_VALUES, "\n");

        nsf1V   = siteLikelihood_MLE_VALUES ["nsFactor1"];
        nsf2V   = siteLikelihood_MLE_VALUES ["nsFactor2"];
        nsf1Vl   = siteLikelihood_MLE_VALUES ["nsFactor1leaves"];
        nsf2Vl   = siteLikelihood_MLE_VALUES ["nsFactor2leaves"];

        omega2F = siteLikelihood_MLE_VALUES ["omega2"];

        mixingF = siteLikelihood_MLE_VALUES ["mixingP"];
        mixingFl = siteLikelihood_MLE_VALUES ["mixingPleaves"];
        sFValue = siteLikelihood_MLE_VALUES ["sFactor"];
    }
    else
    {
        nsf2V   = felLikelihood_MLE_VALUES ["nsFactor2"];
        sFValue = felLikelihood_MLE_VALUES ["sFactor"];
    }

	if (siteNAF == 1) // alternative
	{
        doneSites[siteMap][0] = nsf1V*sFValue;
        doneSites[siteMap][1] = nsf2V;
        doneSites[siteMap][2] = mixingF;
        doneSites[siteMap][6] = nsf1Vl;
        doneSites[siteMap][7] = nsf2Vl;
        doneSites[siteMap][8] = mixingFl;
        doneSites[siteMap][9] = sFValue;


		doneSites[siteMap][3] = doneSites[siteMap][3]+2*siteLikelihood_MLES[1][0];
		doneSites[siteMap][5] = siteLikelihood_MLES[1][0];

        bySiteCache[siteIndex][0] = sFValue;
        bySiteCache[siteIndex][1] = nsf1V;
        bySiteCache[siteIndex][2] = mixingF;
        bySiteCache[siteIndex][3] = nsf1Vl;
        bySiteCache[siteIndex][4] = nsf2Vl;
        bySiteCache[siteIndex][5] = mixingFl;

        if (debugVerboseFlag)
        {
            fprintf (stdout, "[DEBUG: Received MEME alternative fit of site ", siteIndex, " from node ", fromNode, "]");
            fprintf (stdout, "\n\talpha  = ", doneSites[siteMap][9],
                             "\n\tbeta1  = ", doneSites[siteMap][0],
                             "\n\tbeta2  = ", doneSites[siteMap][1],
                             "\n\tmixing = ", doneSites[siteMap][2],
                             "\n\tbeta1[leaves]  = ", doneSites[siteMap][6],
                             "\n\tbeta2[leaves]  = ", doneSites[siteMap][7],
                             "\n\tmixing[leaves] = ", doneSites[siteMap][8],
                             "\n");
        }


       if (nsf2V > sFValue)
        {
            toDoList["MEME_NULL_" + siteIndex] = {{siteIndex__,0}};
            if (debugVerboseFlag)
            {
                fprintf (stdout, "[DEBUG: Added null model fit for site ", siteIndex, " to the queue]\n");
            }
        }
        else
        {
            doneSites[siteMap][3] = 0;
            doneSites[siteMap][4] = -1;
        }
	}
	else
    {
        if (siteNAF == 2) // FEL
        {
            bySiteCache[siteIndex][0] = sFValue;
            bySiteCache[siteIndex][1] = nsf2V;
            toDoList ["MEME_ALT_" + siteIndex] = {{siteIndex__,1}};
            if (debugVerboseFlag)
            {
                fprintf (stdout, "[DEBUG: Received FEL fit of site ", siteIndex, " from node ", fromNode, "]");
                fprintf (stdout, "\n\talpha  = ", sFValue,
                                 "\n\tbeta  = ", nsf2V,
                                 "\n");
            }

        }
        else // null
        {
            doneSites[siteMap][3] = doneSites[siteMap][3]-2*siteLikelihood_MLES[1][0];
            if (debugVerboseFlag)
            {
                fprintf (stdout, "[DEBUG: Received MEME NULL fit of site ", siteIndex, " from node ", fromNode, "]");
                fprintf (stdout, "\n\talpha  = ",  sFValue,
                                 "\n\tbeta1  = ",  nsf1V,
                                 "\n\tomega2  = ", omega2F,
                                 "\n\tmixing = ",  mixingF,
                                 "\n\tbeta1[leaves]  = ", nsf1Vl,
                                 "\n\tbeta2[leaves]  = ", nsf2Vl,
                                 "\n\tmixing[leaves] = ", mixingFl,
                                 "\n");
            }
        }
    }

    if (siteNAF < 2)
    {
        if (doneSites[siteMap][4] == 0)
        {
            doneSites[siteMap][4] = -1;
        }
        else
        {
            if (doneSites[siteMap][4] == (-1))
            {
                doneSites[siteMap][4] = 2/3-2/3*(0.45*CChi2(doneSites[siteMap][3],1)+0.55*CChi2(doneSites[siteMap][3],2));
                ReportSiteMEMEInternal (siteIndex, siteMap, -1);
            }
        }
    }

	return fromNode-1;
}

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

function ReceiveJobs3 (sendOrNot)
{
	MPIReceive (-1, fromNode, result_String);

	siteIndex = MPINodeState[fromNode-1][1];

	if (sendOrNot)
	{
		MPISend (fromNode,siteLikelihood);
		MPINodeState[fromNode-1][1] = siteCount;
	}
	else
	{
		MPINodeState[fromNode-1][0] = 0;
		MPINodeState[fromNode-1][1] = -1;
	}

	siteMap = dupInfo[siteIndex];

	ExecuteCommands (result_String);

	doneSites[siteMap][0] = sFactor;
	doneSites[siteMap][1] = nFactor;
	doneSites[siteMap][2] = siteLikelihood_MLES[1][0];

	ReportSite3 (siteIndex, siteMap);

	return fromNode-1;
}

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

if (_DO_TREE_REBALANCE_ == None) {
    _DO_TREE_REBALANCE_ = 1;
}

if (pipeThroughFlag == 0)
{
	#include   "TemplateModels/chooseGeneticCode.def";
	ChoiceList (nrChoice, "New/Restore",1,SKIP_NONE,
				"New Analysis","Perform a new analysis.",
				"Restore", "Restore an earlier nucleotide fit");

	if (nrChoice < 0)
	{
		return;
	}
}

global AC 	= 1;
global AT 	= 1;
global CG 	= 1;
global CT 	= 1;
global GT 	= 1;
global dNdS = 1;

NucleotideMatrix	 = {{*,AC*t,t,AT*t}{AC*t,*,CG*t,CT*t}{t,CG*t,*,GT*t}{AT*t,CT*t,GT*t,*}};

if (nrChoice == 0)
{
	if (pipeThroughFlag == 0)
	{
		SetDialogPrompt ("Please specify a codon data file:");

		DataSet 	  ds 		= ReadDataFile (PROMPT_FOR_FILE);


		ChoiceList (modelChoice, "Model Options",1,SKIP_NONE,
					"Default","Use HKY85 and MG94xHKY85.",
					"Custom", "Use any reversible nucleotide model crossed with MG94.");

		if (modelChoice < 0)
		{
			return;
		}

		modelDesc = "";

		if (modelChoice)
		{
			done = 0;
			while (!done)
			{
				fprintf (stdout,"\nPlease enter a 6 character model designation (e.g:010010 defines HKY85):");
				fscanf  (stdin,"String", modelDesc);
				if (Abs(modelDesc)==6)
				{
					done = 1;
				}
			}
		}
		else
		{
			modelDesc = "010010";
		}
	}

	DataSetFilter filteredData = CreateFilter (ds,3,"","",GeneticCodeExclusions);
	DataSetFilter nucData 	= CreateFilter (filteredData,1);

	ModelTitle = ""+modelDesc[0];

	rateBiasTerms = {{"AC","1","AT","CG","CT","GT"}};
	paramCount	  = 0;

	modelConstraintString = "";

	for (customLoopCounter2=1; customLoopCounter2<6; customLoopCounter2=customLoopCounter2+1)
	{
		for (customLoopCounter=0; customLoopCounter<customLoopCounter2; customLoopCounter=customLoopCounter+1)
		{
			if (modelDesc[customLoopCounter2]==modelDesc[customLoopCounter])
			{
				ModelTitle  = ModelTitle+modelDesc[customLoopCounter2];
				if (rateBiasTerms[customLoopCounter2] == "1")
				{
					modelConstraintString = modelConstraintString + rateBiasTerms[customLoopCounter]+":="+rateBiasTerms[customLoopCounter2]+";";
				}
				else
				{
					modelConstraintString = modelConstraintString + rateBiasTerms[customLoopCounter2]+":="+rateBiasTerms[customLoopCounter]+";";
				}
				break;
			}
		}
		if (customLoopCounter==customLoopCounter2)
		{
			ModelTitle = ModelTitle+modelDesc[customLoopCounter2];
		}
	}

	if (Abs(modelConstraintString))
	{
		ExecuteCommands (modelConstraintString);
	}


	HarvestFrequencies (overallFrequencies,  nucData,1,1,0);
	HarvestFrequencies (rawPositionFrequencies, nucData,3,1,1);

    positionFrequencies = CF3x4 (rawPositionFrequencies, GeneticCodeExclusions);
	codonFrequencies    = BuildCodonFrequencies(positionFrequencies);

	Model NucModel = (NucleotideMatrix, overallFrequencies, 1);


	if (pipeThroughFlag == 0)
	{
		#include	 "queryTree.bf";
		SetDialogPrompt ("Save nucleotide model fit to:");
		fprintf (PROMPT_FOR_FILE, CLEAR_FILE);
		NUC_FILE_PATH = LAST_FILE_PATH;
	}
	else
	{
		Tree givenTree = treeString;
	}
}
else
{
	if (pipeThroughFlag == 0)
	{
		SetDialogPrompt ("Restore nucleotide fit from:");
		fscanf (PROMPT_FOR_FILE, "Raw", a);
		ExecuteCommands (a);
		DataSetFilter filteredData = CreateFilter (ds,3,"","",GeneticCodeExclusions);
		HarvestFrequencies (positionFrequencies, nucData,3,1,1);
		codonFrequencies = BuildCodonFrequencies(positionFrequencies);

		treeString = ""+givenTree;

		pMatch = {6,1};
		pCheck = {{AC__,AT__,CG__,CT__,GT__}};
		if (pCheck[0] == 1)
		{
			ModelTitle = "00";
			pCount = 1;
			pMatch[0] = 1;
		}
		else
		{
			pCount = 2;
			ModelTitle = "01";
			pMatch[0] = pCheck[0];
			pMatch[1] = 1;
		}

		for (l=1; l<5; l=l+1)
		{
			for (k=0; k<pCount; k=k+1)
			{
				if (pCheck[l] == pMatch[k])
				{
					break;
				}
			}

			if (k<pCount)
			{
				ModelTitle = ModelTitle + k;
			}
			else
			{
				pMatch[pCount] = pCheck[l];
				ModelTitle = ModelTitle + pCount;
				pCount = pCount+1;
			}
		}
	}
}

