/*************************************************************************/
/*									                                     */
/*	Routines to manage tree growth, and evaluation		                 */
/*	------------------------------------------------------	        	 */
/*									                                     */
/*************************************************************************/


#include "defns.h"
#include "types.h"
#include "extern.h"


Tree		*Raw;


/*************************************************************************/
/*									                                     */
/*	Grow a single tree from training data subset                         */
/*									                                     */
/*************************************************************************/


    OneTree()
/*  ---------  */
{
	Tree FormC45Tree(),FormCARTTree();
    Boolean Prune();

    InitialiseTreeData();
    InitialiseWeights();

    Raw = (Tree *) calloc(1, sizeof(Tree));

    AllKnown = true;

	Raw[0] = FormC45Tree(0, MaxItem);

	freeTreeData();

}




/*************************************************************************/
/*								                                      	 */
/*	Evaluate OOB Data for a tree, category it class label.     			 */
/*	Two-dimensional array of OOBTable[treeNo][UnSampleMaxItem]           */
/*				when sampling NO.give value "null";					     */
/*				when OOB data,category result give className value       */
/*************************************************************************/

EvaluateMatrix(treeNo)
long treeNo;
{

	ClassNo Category();

	long i,j;

	long classNoIndex,length;

	long result=0;//0:in-of-bag; 1:out-of-bag;

	for(i=0;i<=UnSampleMaxItem;++i)
	{
		for(j=0;j<=OOBDataLength;++j)
		{
			if(OOBData[j]==i)
			{
				result=1;
				break;
			}
		}

		if(result==1)
		{
			classNoIndex = Category(Item[i],Raw[0]);
			length=strlen(ClassName[classNoIndex])+1;
            OOBTable[treeNo][i] = classNoIndex;
			result=0;
		}
		else
		{
            OOBTable[treeNo][i] = 123456789;
		}

	}

	if(!UNSEENS)
	{
		ReleaseTree(Raw[0]);
	}

}





/*************************************************************************/
/*								                                      	 */
/*	Evaluate testing data for a tree, category it class label.         	 */
/*	Two-dimensional array of                                             */
/*			TDResult[treeNo][EvaluateMatrixTestDataNumber]  		     */
/*				                                                         */
/*************************************************************************/

EvaluateTestDataMatrix(treeNo)
	long treeNo;
{

	ClassNo Category();

	long i,j;

	long classNoIndex,length;

	int result=0;

	for(i=0; i<=TDMaxItem;	++i)
	{
		classNoIndex = Category(TDItem[i],Raw[0]);
		TDResult[treeNo][i] = classNoIndex;
	}

	ReleaseTree(Raw[0]);

}



/*************************************************************************/
/*								                                      	 */
/*	Compute Strength,Correlation,errorbound of random forest           	 */
/*				                                                         */
/*************************************************************************/

GetStrength()
{

	long i,j,k;
	long *leiNum;
	long sumLeiNum;
	long nullvalue;

	float *QSingle=NULL;
	float QSingleTotal=0.0000;
	float maxQ = 0.0000;

	float QDoubleTotal=0.0000;
    float varmr= 0.0000;
	float standardH = 0.0000;
	float *PTrueOOB=NULL;
	float *PFalseOOB=NULL;
	float *PPunion=NULL;

	long *maxQYFalseValue;
	long *maxQYTrueValue;

	float maxQvalue=0.0000;
	long *QIndexArray=NULL;

	long PUnNullValue = 0;
	long HXEqualYValue = 0;
	long HXUnEqualYMaxValue = 0;



	RFstrength = 0.0000;
    Correlation = 0.0000;
	ErrorBound = 0.0000;


   	Q = (float **) calloc( UnSampleMaxItem+1, sizeof(float *));


    ForEach(i, 0, UnSampleMaxItem)
    {
		Q[i] = (float *) calloc(MaxClass+1, sizeof(float));
    }

	QSingle = (float *) calloc( UnSampleMaxItem+1, sizeof(float));

	maxQYFalseValue = (long *) calloc( UnSampleMaxItem+1, sizeof(long));
	maxQYTrueValue = (long *) calloc( UnSampleMaxItem+1, sizeof(long));
	QIndexArray = (long *) calloc( UnSampleMaxItem+1, sizeof(long));


	PTrueOOB = (float *) calloc( TreeNum, sizeof(float));

	PFalseOOB = (float *) calloc( TreeNum, sizeof(float));

    PPunion = (float *) calloc( TreeNum, sizeof(float));

    leiNum = (long *) calloc( MaxClass+1, sizeof(long));

	for(i=0;i<= UnSampleMaxItem;++i)
	{

		for(k=0;k<= MaxClass;++k)
		{
			leiNum[k]=0;
		}

		sumLeiNum =0;
		nullvalue =0;


		for(k=0;k<= MaxClass;++k)
		{

			for(j=0;j<TreeNum;++j)
			{

				  if ( OOBTable[j][i]==123456789 )
				 {
					 nullvalue++;
				 }
				 else
				 {
					 if(k == OOBTable[j][i])
					 {
						 leiNum[k]++;
					 }
				 }
			}

		}



		for(k=0;k<= MaxClass;++k)
		{
			sumLeiNum = sumLeiNum + leiNum[k];

		}

		for(k=0;k<= MaxClass;++k)
		{
			if(sumLeiNum == 0)
			{
				Q[i][k] =0.0000;
			}
			else
			{
				Q[i][k]=(float)(leiNum[k])/sumLeiNum;
			}

		}

        QIndexArray[i]=0;

		maxQvalue = Q[i][0];

       	for(k=1;k<= MaxClass;++k)
		{
			if( maxQvalue < Q[i][k] )
			{
				QIndexArray[i] = k;
                maxQvalue = Q[i][k];
			}
		}

	}



	for(i=0;i<= UnSampleMaxItem;++i)
	{
		QSingle[i] = 0.0000;
		maxQ = 0.0000;

		for(k=0;k<= MaxClass;++k)
		{
		   if( k == Class(Item[i]))
		   {
			   QSingle[i] = QSingle[i] + Q[i][k];
               maxQYTrueValue[i] = k;
		   }
		   else
		   {
			   if( maxQ <= Q[i][k] )
			   {
				   maxQ = Q[i][k];
				   maxQYFalseValue[i] = k;
			   }
		   }
		}

		QSingle[i] = QSingle[i] - maxQ;
		QSingleTotal = QSingleTotal + QSingle[i];
		QDoubleTotal = QDoubleTotal + QSingle[i]*QSingle[i];
	}

	//Strength
	RFstrength = QSingleTotal/UnSampleMaxItem;


	//var(mr)
	varmr = QDoubleTotal/UnSampleMaxItem - RFstrength*RFstrength;

	//sd(h(*))
	for( i=0;i<TreeNum;++i)
	{

		PUnNullValue = UnSampleMaxItem;

		HXEqualYValue = 0;
        HXUnEqualYMaxValue = 0;

		for(j=0;j<= UnSampleMaxItem;++j)
		{
			 if (OOBTable[i][j]==123456789)
			 {
				 PUnNullValue--;
			 }
			 else
			 {
				 if( OOBTable[i][j] == Class(Item[j]) )
				 {
					 HXEqualYValue++;
				 }
				 else if(OOBTable[i][j] == maxQYFalseValue[j])
				 {
					 HXUnEqualYMaxValue++;
				 }
			 }
		}

		PTrueOOB[i] = (float)HXEqualYValue/PUnNullValue;
        PFalseOOB[i]= (float)HXUnEqualYMaxValue/PUnNullValue;

		PPunion[i] = (PTrueOOB[i]-PFalseOOB[i])*(PTrueOOB[i]-PFalseOOB[i]) + PTrueOOB[i] + PFalseOOB[i];

		standardH = standardH + sqrt(PPunion[i])/TreeNum;

	}

    //Correlation
    Correlation = varmr/(standardH*standardH);

	//ErrorBound
	ErrorBound = Correlation/( RFstrength * RFstrength );


    //free memory
	free(leiNum);
	leiNum=NULL;

	free(QSingle);
	QSingle =NULL;

	free(PTrueOOB);
    PTrueOOB=NULL;

	free(PFalseOOB);
	PFalseOOB = NULL;

	free(PPunion);
	PPunion =NULL;

	free(maxQYFalseValue);
	maxQYFalseValue=NULL;

	free(maxQYTrueValue);
	maxQYTrueValue = NULL;

	free(QIndexArray);
	QIndexArray=NULL;

}


/*************************************************************************/
/*								                                      	 */
/*	Get the results of ensemble vote for Out-of-bag data             	 */
/*				                                                         */
/*************************************************************************/
GetOOBVoteResult()
{
	long i,j,k;

	long *voteNum;

    long classNoIndex,length;
	long nullvalue;

	long whichnum,maxK;

	k =0;

	voteNum = (long *) calloc( MaxClass+1, sizeof(long));
	OOBDataVoteResult =(long *) calloc(UnSampleMaxItem+1, sizeof(long));


    for(i=0;i<UnSampleMaxItem+1;++i)
    {

		maxK = 0;
		nullvalue =0;

		for(j=1;j<=MaxClass;++j)
		{
			if( strlen(ClassName[j-1]) > strlen(ClassName[j]) )
			{
				length=strlen(ClassName[j-1])+1;
			}
			else
			{
				length=strlen(ClassName[j])+1;
			}

		}


	    for(j=0;j<=MaxClass;++j)
		{
			voteNum[j]=0;
		}

		for(j=0;j<TreeNum;++j)
		{
			 if (OOBTable[j][i]==123456789)
			 {
				 nullvalue++;
			 }
			 else
			 {
				 voteNum[OOBTable[j][i]]++;

			 }

		}

		if( nullvalue == TreeNum )
		{
			OOBDataVoteResult[i] = 123456789;
		}
		else
		{
			for(k=1;k<=MaxClass;k++)
			{
				if( voteNum[maxK] < voteNum[k] )
				{
					maxK = k;
				}
			}

			OOBDataVoteResult[i] = maxK;

		}

	}

	free(voteNum);
	voteNum = NULL;

}



/*************************************************************************/
/*								                                      	 */
/*	Get the results of ensemble vote for testing data               	 */
/*				                                                         */
/*************************************************************************/
GetVoteResult()
{
	long i,j,k;

	long *voteNum;

    long classNoIndex,length;

	long maxK;

	k =0;

	voteNum = (long *) calloc( MaxClass+1, sizeof(long));
	TestDataVoteResult = (long *) calloc(TDMaxItem+1, sizeof(long));

    for(i=0;i<TDMaxItem+1;++i)
    {

		maxK = 0;

		for(j=1;j<=MaxClass;++j)
		{
			if( strlen(ClassName[j-1]) > strlen(ClassName[j]) )
			{
				length=strlen(ClassName[j-1])+1;
			}
			else
			{
				length=strlen(ClassName[j])+1;
			}

		}


		for(j=0;j<=MaxClass;++j)
		{
			voteNum[j]=0;
		}


		for(j=0;j<TreeNum;++j)
		{
			voteNum[TDResult[j][i]]++;
		}

		for(k=1;k<=MaxClass;k++)
		{
			if( voteNum[maxK] < voteNum[k] )
			{
				maxK = k;
			}
		}

		TestDataVoteResult[i] = maxK;

	}

	free(voteNum);
	voteNum = NULL;

}


/*************************************************************************/
/*								                                      	 */
/*	Get the OOB accuracy of out-of-bag data                         	 */
/*				                                                         */
/*************************************************************************/

EvaluateOOBRFResult()
{

	ClassNo RealClass;
	ClassNo PredictClass;
    long t;
    ItemNo i, RFErrors;
	long fenmu;


	RFErrors = 0;
	TestError=0.0000;

    fenmu = UnSampleMaxItem;

	ForEach(i, 0, UnSampleMaxItem)
	{
	    RealClass = Class(Item[i]);

		if(OOBDataVoteResult[i] == 123456789)
	    {
			fenmu--;
			continue;
	    }

		PredictClass =  OOBDataVoteResult[i];

		if ( PredictClass != RealClass )
			RFErrors++;

	}

	OOBError = (float)RFErrors / (fenmu+1.0);

}


/*************************************************************************/
/*								                                      	 */
/*	Get the test accuracy of testing data                              	 */
/*				                                                         */
/*************************************************************************/
EvaluateRFResult()
{

	ClassNo RealClass;
	ClassNo PredictClass;
    long t;

    ItemNo i, RFErrors;


	TestError=0.0000;


	RFErrors = 0;

	ForEach(i, 0, TDMaxItem)
	{
	    RealClass = Class(TDItem[i]);

		PredictClass = TestDataVoteResult[i];

	    if ( PredictClass != RealClass )
			RFErrors++;

	}

	TestError = (float)RFErrors / (TDMaxItem+1.0);

}
