#ifndef _score_c_
#define _score_c_
#include <math.h>
#include <stdlib.h>

/* Lift Globals */
extern float BaseLift;
extern int BestClassCount;
extern float BestSupport;
extern float ScoreChange;

/* a^2/(a+b) Globals */
extern int best, rest;
extern int **aCounts, **bCounts;

/* EffortPDPF Globals */
extern int LOC, *LOCs;
extern float Alpha, Beta, Gamma;

int getPairCount( PickObject *po, int item )
{
  int pairCount = 0, att, attVal;
  for ( att = 0; att < po->mSize; att++ )
    for( attVal = 0; attVal < po->mPicked[att].mSize; attVal++ )
      if ( po->mPicked[att].mAttrVal[attVal] == Item[item][po->mPicked[att].mAttr]._discr_val )
      {
	pairCount++;
	attVal = po->mPicked[att].mSize;
      }

  return pairCount;
}

float scoreInfoGain( void *i )
{
  PickObject *po = (PickObject*)i;
  float toRet = 0;
  int freq = 0, *counts, item, pair = 0;
  int j, isGoodLine = 1;

  counts = (int*)malloc( (MaxClass+1) * sizeof( int ) );
  for ( j = 0; j < MaxClass+1; j++ ) counts[j] = 0;
  
  // Get the frequency of the PickObject with each class value
  // it appears with.
  for ( item = 0; item < MaxItem + 1; item++ )
  {
    if ( getPairCount( po, item ) >= po->mSize )
    {
      counts[Class(Item[item])]++;
      freq++;
    }
  }

  if ( MaxItem * BestSupport > freq ) return 0;

  // Get the entropy of this PickObject.
  for ( j = 0; j < MaxClass+1; j++ )
  {
    if ( counts[j] != 0 )
      toRet = -(counts[j]/(float)freq) * log2f(counts[j]/(float)freq) + toRet;
  }

  free( counts );
  return ( -1 * toRet );
}

float scoreInfoGainDiscr( void *i )
{
  PickObject *po = (PickObject*)i;
  float toRet = 0;
  int freq = 0, *counts, item, pair = 0;
  int j, isGoodLine = 1;

  counts = (int*)malloc( (MaxClass+1) * sizeof( int ) );
  for ( j = 0; j < MaxClass+1; j++ ) counts[j] = 0;
  
  // Get the frequency of the PickObject with each class value
  // it appears with.
  for ( item = 0; item < MaxItem + 1; item++ )
  {
    if ( getPairCount( po, item ) >= po->mSize )
    {
      counts[Class(Item[item])]++;
      freq++;
    }
  }

  // Get the entropy of this PickObject.
  for ( j = 0; j < MaxClass+1; j++ )
  {
    if ( counts[j] != 0 )
      toRet = -(counts[j]/(float)freq) * log2f(counts[j]/(float)freq) + toRet;
  }

  // Add in how many items this rule actually hits.
  //toRet = sqrt( toRet * toRet + (freq/(float)MaxItem)*(freq/(float)MaxItem))/sqrt(2);
  toRet += freq/(float)MaxItem;

  free( counts );
  return ( 1 - toRet );
}


/**
 * This method will calculate the frequency counts of each attribute range to the best/rest classes.
 * It will also calculate the number of best and rest class instances in the data set.
 **/
void calcBaseProbSupt()
{
  int attr, attrVal, item;

  // Create two jagged arrays, one for both best and rest counts.
  aCounts = (int**)malloc( sizeof( int* ) * (MaxAtt+2) );
  bCounts = (int**)malloc( sizeof( int* ) * (MaxAtt+2) );
  for ( attr = 0; attr < MaxAtt+1; attr++ )
    {
      aCounts[attr] = (int*)malloc( sizeof( int ) * (MaxAttVal[attr]+2) );
      bCounts[attr] = (int*)malloc( sizeof( int ) * (MaxAttVal[attr]+2) );
      for ( attrVal = 0; attrVal < MaxAttVal[attr]; attrVal++ )
	aCounts[attr][attrVal] = bCounts[attr][attrVal] = 0;
    }

  for ( attrVal = 0; attrVal < MaxAttVal[attr]; attrVal++ )
    aCounts[attr][attrVal] = bCounts[attr][attrVal] = 0;

  // Get the number of best classes and rest classes.
  // Also get the number of times each attribute appears in the data set and with what class
  // it appears with.
  best = rest = 0;
  for ( item  = 0; item < MaxItem+1; item++ )
    {
      if ( Class( Item[item] ) == 1 ) best++;
      else rest++;

      for ( attr = 0; attr < MaxAtt+1; attr++ )
	if ( Class(Item[item]) == 1 )
	  aCounts[attr][Item[item][attr]._discr_val]++;
	else
	  bCounts[attr][Item[item][attr]._discr_val]++;
    }

  /*
  for ( attr = 0; attr < MaxAtt+1; attr++ )
    for ( attrVal = 1; attrVal < MaxAttVal[attr]+1; attrVal++ )
      printf( "%s = %s in Best.\t%d/%d\t%2.3f\n%s = %s in Rest.\t%d/%d\t%2.3f\n",
      AttName[attr], AttValName[attr][attrVal], aCounts[attr][attrVal], best, aCounts[attr][attrVal]/(float)(MaxItem),
      AttName[attr], AttValName[attr][attrVal], bCounts[attr][attrVal], rest, bCounts[attr][attrVal]/(float)(MaxItem) );  */
}

/**
 * This method will calculate the base lift of the natural data set by using the natural class distribution.
 * The base lift gotten this is then the denomenator in the Lift-Scoring function to determine how much
 * better the distribution of the rule-restricted data set is than the natural data set.
 **/
void calcBaseLift()
{
  BaseLift = 0;
  BestClassCount = 0;
  int *freq = (int*)malloc( ( MaxClass + 1 ) * sizeof( int ) ), i, item;
  for ( i = 0; i< MaxClass+1; i++ ) freq[i] = 0;
  for ( item = 0; item < MaxItem+1; item++ )
    freq[Class(Item[item])]++;

  printf( "%-30s%20s%20s\n", "Name", "Count", "Score" );
  printf( "%-30s%20s%20s\n", "----", "-----", "-----" );
  for ( i = 0; i < MaxClass+1; i++ )
  {
    printf( "%-30s%20d%20f\n", ClassName[i], freq[i], pow(ScoreChange, i+1) );
    BaseLift += ( freq[i] * pow( ScoreChange, i +  1 ) );
  }

  BaseLift /= (MaxItem+1);
  BestClassCount = freq[MaxClass];
  printf( "Number of Instances = %d\n", MaxItem + 1 );
  printf( "Best Class Count = %d\n", BestClassCount );
  printf( "Base Lift        = %f\n\n", BaseLift );
  free( freq );
}

/**
 * This method will score via lift a rule.  It does so by using the base lift calculated by calcBaseLift.
 * It will essentially calculate the base lift of a data set that is rule-restricted and divide that by
 * the original lift.  Numbers greater than 1 are good whereas numbers 1 and below are suboptimal.
 * @param i The rule you wish to score.
 * @return The score of that rule.
 **/
float scoreLift( void *i )
{
  PickObject *po = (PickObject*)i;
  float toRet = 0;
  int *freq = (int*)malloc( ( MaxClass + 1 ) * sizeof( int ) ), c, item, inst = 0;
  for ( c = 0; c < MaxClass+1; c++ ) freq[c] = 0;

  for ( item = 0; item < MaxItem+1; item++ )
  {
    if ( getPairCount( po, item ) >= po->mSize )
    {
      inst++;
      freq[Class(Item[item])]++;
    }
  }
  
  for ( c = 0; c < MaxClass+1; c++ )
  {
    toRet += ( ( freq[c]/(float)inst ) * pow( ScoreChange, c + 1 ) );
  }

  if ( inst == 0 ) return 0;
  if ( BestClassCount * BestSupport > freq[MaxClass] ) return 0;

  free( freq );
  toRet /= (MaxItem+1);
  toRet /= BaseLift;
  if ( toRet < 1 ) return 0;
  return toRet;
}

/**
 * This method will score a rule by calculating the probabilty the best class will
 * appear in the rule-restricted data set as well as how large, essentially, the rule-
 * restricted data set is.  This boils down to probability*support.
 * @param i The rule to score.
 * @return The score of the rule.
 **/
float scoreProbSupt( void *i )
{
  PickObject *po = (PickObject*)i;
  float toRet = 1;
  int att, attVal, attSum = 0;;

  for ( att = 0; att < po->mSize; att++ )
    {
      attSum = 0;
      for ( attVal = 0; attVal < po->mPicked[att].mSize; attVal++ )
      {
	attSum += aCounts[po->mPicked[att].mAttr][po->mPicked[att].mAttrVal[attVal]];
      }
      toRet *= ( attSum/(float)best );
    }

  return (toRet*toRet)/((best/(float)MaxItem) + (rest/(float)MaxItem) );
}

void calcEffEst( int locAtt )
{
  int item;
  LOCs = (int*)malloc( sizeof( int ) * (MaxItem+2) );
  LOC = 0;
  BestClassCount = 0;

  for ( item = 0; item < MaxItem+1; item++ )
  {
    LOC += Item[item][locAtt]._cont_val;
    LOCs[item] = Item[item][locAtt]._cont_val;
    if ( Class( Item[item] ) == MaxClass ) BestClassCount++;
  }

  printf( "Total number of Lines of Code: %d\n", LOC );
  printf( "The ratio of best class to all is %f\n", BestClassCount/(float)(MaxItem+1) );
}

float calcDist( float x, float y, float z, float w )
{
  return sqrt( Alpha*x*x+Beta*y*y+Gamma*z*z ) / sqrt( Alpha + Beta + Gamma );
}

float scoreEffEst( void *i )
{
  PickObject *po = (PickObject*)i;
  float pd, pf, eff = 0;
  int item, freqB = 0, freqO = 0;

  po->mEffort = 0;
  po->mPD = 0;
  po->mPF = 0;


  for ( item = 0; item < MaxItem+1; item++ )
  {
    if ( getPairCount( po, item ) >= po->mSize )
    {
      eff += LOCs[item];
      if ( Class(Item[item]) == MaxClass ) freqB++;
      else freqO++;
    }
  }

  if ( LOC > 0 ) po->mEffort /= (float)LOC;
  else po->mEffort = 0;
  if ( BestClassCount > 0 ) po->mPD = freqB / (float)BestClassCount;
  else po->mPD = 0;
  if ( freqB + freqO > 0 ) po->mPF = freqO / (float)( freqB + freqO );
  else po->mPF = 0;

  po->mSupport = ( freqB + freqO )/(float)( MaxItem );

  //if ( po->mSupport < BestSupport ) return 0;
  if ( freqB + freqO == 0 || po->mPD < BestSupport ) return 0;
  if ( po->mEffort == 0 && po->mPD == 0 && po->mPF == 0 ) return 0;
  return calcDist( po->mPD, 1-po->mPF, 1-po->mEffort, po->mSupport );
}

void scoreTmp( void *d )
{
  PickObject *po = (PickObject*)d;
  int item, att, disj, pairCnt, totLines = 0;
  int *freq = (int*)malloc( sizeof(int) * (MaxClass+1) ), c;
  for ( c = 0; c < MaxClass+1; c++ ) freq[c] = 0;
  for ( item = 0; item < MaxItem+1; item++ )
  {
      pairCnt = 0;
      for ( att = 0; att < po->mSize; att++ )
	for ( disj = 0; disj < po->mPicked[att].mSize; disj++ )
	  if ( po->mPicked[att].mAttrVal[disj] == Item[item][po->mPicked[att].mAttr]._discr_val )
	  {
	    pairCnt++;
	    disj = po->mPicked[att].mSize;
	  }
      if ( pairCnt >= po->mSize )
      {
	  totLines++;
	  freq[Class(Item[item])]++;
      }
  }

  for( c = 0; c < MaxClass+1; c++ )
    printf( "%s\t\t%d/%d\n", ClassName[c], freq[c], totLines );
  printf( "Percent of best class represented: %f\n", freq[MaxClass]/(float)BestClassCount * 100 );
  
  free( freq );
}

#endif
