/*************************************************************************/
/*                                                                  	 */
/*	Evaluation of a test on a continuous valued attribute	         	 */
/*	-----------------------------------------------------	        	 */
/*								                                    	 */
/*************************************************************************/


#include "buildex.h"


float
	*SplitGain,	
	*SplitInfo;	




/*************************************************************************/
/*								                                    	 */
/*  Continuous attributes are treated as if they have possible values	 */
/*	0 (unknown), 1 (less than cut), 2(greater than cut)	             	 */
/*  This routine finds the best cut for items Fp through Lp and sets	 */
/*  Info[], Gain[] and Bar[]					                      	 */
/*								  	                                     */
/*************************************************************************/


    EvalC45ContinuousAtt(Att, Fp, Lp)
/*  -----------------  */ 
    Attribute Att;
    ItemNo Fp, Lp; 
{ 
    ItemNo i, BestI, Xp, Tries=0;
    ItemCount Items, KnownItems, LowItems, MinSplit, CountItems();
    ClassNo c;
    float AvGain=0, Val, BestVal, BaseInfo, ThreshCost,
	ComputeGain(), TotalInfo(), Worth();
    void Swap();

    Verbosity(2) printf("\tAtt %s", AttName[Att]);
    Verbosity(3) printf("\n");

    ResetFreq(2);

    Items = CountItems(Fp, Lp);
    Xp = Fp;
    ForEach(i, Fp, Lp)
    {
	if ( CVal(Item[i],Att) == Unknown )
	{
	    Freq[ 0 ][ Class(Item[i]) ] += Weight[i];
	    Swap(Xp, i);
	    Xp++;
	}
    }

    ValFreq[0] = 0;
    ForEach(c, 0, MaxClass)
    {
	ValFreq[0] += Freq[0][c];
    }

    KnownItems = Items - ValFreq[0];
    UnknownRate[Att] = 1.0 - KnownItems / Items;

    if ( KnownItems < 2 * MINOBJS )
    {
	Verbosity(2) printf("\tinsufficient cases with known values\n");

	Gain[Att] = -Epsilon;
	Info[Att] = 0.0;
	return;
    }

    Quicksort(Xp, Lp, Att, Swap);

    ForEach(i, Xp, Lp)
    {
	Freq[ 2 ][ Class(Item[i]) ] += Weight[i];
	SplitGain[i] = -Epsilon;
	SplitInfo[i] = 0;
    }

    BaseInfo = TotalInfo(Freq[2], 0, MaxClass) / KnownItems;

    MinSplit = 0.10 * KnownItems / (MaxClass + 1);
    if ( MinSplit <= MINOBJS ) MinSplit = MINOBJS;
    else
    if ( MinSplit > 25 ) MinSplit = 25;

    LowItems = 0;
    ForEach(i, Xp, Lp - 1)
    {
	c = Class(Item[i]);
	LowItems   += Weight[i];
	Freq[1][c] += Weight[i];
	Freq[2][c] -= Weight[i];

	if ( LowItems < MinSplit ) continue;
	else
	if ( LowItems > KnownItems - MinSplit ) break;

	if ( CVal(Item[i],Att) < CVal(Item[i+1],Att) - 1E-5 )
	{
	    ValFreq[1] = LowItems;
	    ValFreq[2] = KnownItems - LowItems;
	    SplitGain[i] = ComputeGain(BaseInfo, UnknownRate[Att], 2, KnownItems);
	    SplitInfo[i] = TotalInfo(ValFreq, 0, 2) / Items;
	    AvGain += SplitGain[i];
	    Tries++;

	    Verbosity(3)
	    {	printf("\t\tCut at %.3f  (gain %.3f, val %.3f):",
	               ( CVal(Item[i],Att) + CVal(Item[i+1],Att) ) / 2,
	    	       SplitGain[i],
	    	       Worth(SplitInfo[i], SplitGain[i], Epsilon));
	    	       PrintDistribution(Att, 2, true);
	    }
	}
    }

    ThreshCost = Log(Tries) / Items;

    BestVal = 0;
    BestI   = None;
    ForEach(i, Xp, Lp - 1)
    {
	if ( (Val = SplitGain[i] - ThreshCost) > BestVal )
	{
	    BestI   = i;
	    BestVal = Val;
	}
    }

    if ( BestI == None )
    {
	Gain[Att] = -Epsilon;
	Info[Att] = 0.0;

	Verbosity(2) printf("\tno gain\n");
    }
    else
    {
	Bar[Att]  = (CVal(Item[BestI],Att) + CVal(Item[BestI+1],Att)) / 2;
	Gain[Att] = BestVal;
	Info[Att] = SplitInfo[BestI];

	Verbosity(2)
	    printf("\tcut=%.3f, inf %.3f, gain %.3f\n",
		   Bar[Att], Info[Att], Gain[Att]);
    }
} 
 






/*************************************************************************/
/*								                                    	 */
/*  The importance for Continuous attributes are calculated              */
/*	according to chisquare statistic method         	             	 */
/*								  	                                     */
/*************************************************************************/

double	ContinuousAttChiValue(Att, Fp, Lp)
		Attribute Att;
		ItemNo Fp, Lp; 
{
	
	ItemNo i, BestI, Xp;
	DiscrValue v;
    ItemCount Items, KnownItems, LowItems, MinSplit, CountItems();
	ItemCount **TrueFreq,*TrueValFreq;
    ClassNo c;

	float ChiQuare=0,*PvalueSplitGain;
	long N;
	float BestVal, PvalueBaseInfo,ComputeGain(), TotalInfo();
    void Swap(); 

	ItemNo *ClassFrequcy;
	float **T;

	ClassFrequcy = (ItemNo *) calloc(MaxClass+1, sizeof(ItemNo));
    
	T  = (float **) calloc(3, sizeof(float *));
    ForEach(v, 0, 2)
    {
		T[v]  = (float *) calloc(MaxClass+1, sizeof(float));
    }


	TrueFreq  = (ItemCount **) calloc(3, sizeof(ItemCount *));
	ForEach(v, 0, 2)
    {
	TrueFreq[v]  = (ItemCount *) calloc(MaxClass+1, sizeof(ItemCount));
    }

    TrueValFreq = (ItemCount *) calloc(3, sizeof(ItemCount));

	PvalueSplitGain = (float *) calloc(MaxItem+1, sizeof(float));

	//Initialize
	ResetFreq(2);

	ForEach(c, 0, MaxClass)
	{
	    ClassFrequcy[c] = 0;
	}

	ForEach(v, 0, 2) 
    { 
	ForEach(c, 0, MaxClass)
	{
	    T[v][c] = 0.00;
	}
    }

    Items = CountItems(Fp, Lp);
    Xp = Fp;
    ForEach(i, Fp, Lp)
    {
	if ( CVal(Item[i],Att) == Unknown )
	{
	    Freq[ 0 ][ Class(Item[i]) ] += Weight[i];
	    Swap(Xp, i);
	    Xp++;
	}
    }

    ValFreq[0] = 0;
    ForEach(c, 0, MaxClass)
    {
	ValFreq[0] += Freq[0][c];
    }

    KnownItems = Items - ValFreq[0];


    if ( KnownItems < 2 * MINOBJS )
    {
		return 0.000000;
    }

	Quicksort(Xp, Lp, Att, Swap);

    ForEach(i, Xp, Lp)
    {
		Freq[ 2 ][ Class(Item[i]) ] += Weight[i];
		PvalueSplitGain[i] = 0.000;
    }

    PvalueBaseInfo = TotalInfo(Freq[2], 0, MaxClass) / KnownItems;


	BestVal = 0;
    BestI   = None;

    MinSplit = 0.10 * KnownItems / (MaxClass + 1);
    if ( MinSplit <= MINOBJS ) MinSplit = MINOBJS;
    else
    if ( MinSplit > 25 ) MinSplit = 25;

    LowItems = 0;


	ForEach(i, Xp, Lp)
	{
		c = Class(Item[i]);
		ClassFrequcy[ c ]++;
	}


    ForEach(i, Xp, Lp - 1)
    {
		c = Class(Item[i]);
		LowItems   += Weight[i];
		Freq[1][c] += Weight[i];
		Freq[2][c] -= Weight[i];

		if ( LowItems < MinSplit ) continue;
		else
		if ( LowItems > KnownItems - MinSplit ) break;

		if ( CVal(Item[i],Att) < CVal(Item[i+1],Att) - 1E-5 )
		{
			ValFreq[1] = LowItems;
			ValFreq[2] = KnownItems - LowItems;
			PvalueSplitGain[i] = ComputeGain(PvalueBaseInfo, UnknownRate[Att], 2, KnownItems);
			
			if ( PvalueSplitGain[i] > BestVal )
			{
				BestI   = i;
				BestVal = PvalueSplitGain[i];

				ForEach(v, 1, 2) 
				{ 
				ForEach(c, 0, MaxClass)
				{
					TrueFreq[v][c] = Freq[v][c];
				}
				}

				TrueValFreq[1] = ValFreq[1];
				TrueValFreq[2] = ValFreq[2];

			}

		}

    }


	ForEach(v, 1, 2) 
    { 
		ForEach(c, 0, MaxClass)
		{
			T[v][c] = TrueValFreq[v] * ClassFrequcy[c]/KnownItems;//knownItemsδ֪
		}
    }

	ForEach(v, 1, 2) 
    { 
		ForEach(c, 0, MaxClass)
		{
			if(T[v][c]==0)
			{
				continue;
			}

			ChiQuare += (TrueFreq[v][c]-T[v][c])*(TrueFreq[v][c]-T[v][c])/T[v][c];
		}
    }


	//Free Memory	
	free(ClassFrequcy);
	ClassFrequcy = NULL;

	ForEach(v, 0, 2)
	{
	  free(T[v]);
	  T[v] = NULL;
	}	
	free(T);
	T = NULL;

	ForEach(v, 0, 2)
    {
	  free(TrueFreq[v]);
	  TrueFreq[v] = NULL;
    }
	free(TrueFreq);
	TrueFreq = NULL;

   	free(TrueValFreq);
	TrueValFreq = NULL;


	free(PvalueSplitGain);
	PvalueSplitGain = NULL;



	if(ChiQuare == 0)
	{
		return 0.00000;
	}

	return (double)(ChiQuare);
}





/*************************************************************************/
/*								                                    	 */
/*  The importance for Continuous attributes are calculated              */
/*	according to information gain ratio method         	             	 */
/*								  	                                     */
/*************************************************************************/

double	  ContinuousAttGRValue(Att, Fp, Lp)
/*  -----------------  */ 
    Attribute Att;
    ItemNo Fp, Lp; 
{ 
    ItemNo i, BestI, Xp, Tries=0;
    ItemCount Items, KnownItems, LowItems, MinSplit, CountItems();
    ClassNo c;
	float GRGain,GRInfo,GRValue;
    float AvGain=0, Val, BestVal, BaseInfo, ThreshCost,
	ComputeGain(), TotalInfo(), Worth();
    void Swap();


    ResetFreq(2);

    Items = CountItems(Fp, Lp);
    Xp = Fp;
    ForEach(i, Fp, Lp)
    {
	if ( CVal(Item[i],Att) == Unknown )
	{
	    Freq[ 0 ][ Class(Item[i]) ] += Weight[i];
	    Swap(Xp, i);
	    Xp++;
	}
    }

    ValFreq[0] = 0;
    ForEach(c, 0, MaxClass)
    {
	ValFreq[0] += Freq[0][c];
    }

    KnownItems = Items - ValFreq[0];
    UnknownRate[Att] = 1.0 - KnownItems / Items;

    if ( KnownItems < 2 * MINOBJS )
    {
	return 0.00000;
    }

    Quicksort(Xp, Lp, Att, Swap);

    ForEach(i, Xp, Lp)
    {
	Freq[ 2 ][ Class(Item[i]) ] += Weight[i];
	SplitGain[i] = -Epsilon;
	SplitInfo[i] = 0;
    }

    BaseInfo = TotalInfo(Freq[2], 0, MaxClass) / KnownItems;

    MinSplit = 0.10 * KnownItems / (MaxClass + 1);
    if ( MinSplit <= MINOBJS ) MinSplit = MINOBJS;
    else
    if ( MinSplit > 25 ) MinSplit = 25;

    LowItems = 0;
    ForEach(i, Xp, Lp - 1)
    {
		c = Class(Item[i]);
		LowItems   += Weight[i];
		Freq[1][c] += Weight[i];
		Freq[2][c] -= Weight[i];

		if ( LowItems < MinSplit ) continue;
		else
		if ( LowItems > KnownItems - MinSplit ) break;

		if ( CVal(Item[i],Att) < CVal(Item[i+1],Att) - 1E-5 )
		{
			ValFreq[1] = LowItems;
			ValFreq[2] = KnownItems - LowItems;
			SplitGain[i] = ComputeGain(BaseInfo, UnknownRate[Att], 2, KnownItems);
			SplitInfo[i] = TotalInfo(ValFreq, 0, 2) / Items;
			AvGain += SplitGain[i];
			Tries++;

		}
    }

    ThreshCost = Log(Tries) / Items;

    BestVal = 0;
    BestI   = None;
    ForEach(i, Xp, Lp - 1)
    {
	if ( (Val = SplitGain[i] - ThreshCost) > BestVal )
	{
	    BestI   = i;
	    BestVal = Val;
	}
    }

    if ( BestI == None )
    {
		GRGain = -Epsilon;
		GRInfo = 0.0;

		return 0.00000;
    }
    else
    {

		GRGain = BestVal;
		GRInfo = SplitInfo[BestI];

		if ( GRGain >= Epsilon && GRInfo > Epsilon )
		{
			GRValue = GRGain / GRInfo;
		}
		else
		{
			GRValue = -Epsilon;
		}

    }

	if(GRValue < Epsilon)
	{
		return 0.000000;
	}
	return (double)(GRValue);

} 




/*************************************************************************/
/*                                                                   	 */
/*  Change a leaf into a test on a continuous attribute             	 */
/*                                                                  	 */
/*************************************************************************/


    ContinTest(Node, Att)
/*  ----------  */
    Tree Node;
    Attribute Att;
{
    float Thresh, GreatestValueBelow();
    ItemCount CountItems();

    Sprout(Node, 2);

    Thresh = GreatestValueBelow(Att, Bar[Att]);

    Node->NodeType	= ThreshContin;
    Node->Tested	= Att;
    Node->Cut		=
    Node->Lower		=
    Node->Upper		= Thresh;
    Node->Errors        = 0;
}



/*************************************************************************/
/*                                                                  	 */
/*  Return the greatest value of attribute Att below threshold t  	     */
/*                                                                	     */
/*************************************************************************/


float GreatestValueBelow(Att, t)
/*    ------------------  */
    Attribute Att;
    float t;
{
    ItemNo i;
    float v, Best;
    Boolean NotYet=true;

    ForEach(i, 0, MaxItem)
    {
	v = CVal(Item[i], Att);
	if ( v != Unknown && v <= t && ( NotYet || v > Best ) )
	{
	    Best = v;
	    NotYet = false;
	}
    }

    return Best;
}


