#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <math.h>
#include <float.h>
#include "per_eval.h"
#include "defs.h"






PerEval::PerEval(double *true_labels, int n)
{
	this->true_labels = true_labels;

	this->n = n;

	accuracy = -1.0;
	FMeasure = -1.0;
	precision = -1.0;
	recall = -1.0;

}


double PerEval::calcAccuracy(double *pred_labels, bool print)
{
	double right = 0.0;
	
	for( int i = 0; i < n; i++ )
		if( pred_labels[i] == true_labels[i] )
			right++;

	accuracy = right/((double)n);
	
	if( print )
		{
		printf("\n########################### RESULT ##########################\n");
		printf(" Accuracy = %f [%d/%d]\n",accuracy,(int)right,n);
		printf("#############################################################\n");
		}
	return accuracy;
}


double PerEval::calcFMeasure(double *pred_labels, bool print)
{
	return calcAll(pred_labels, FMEASURE , print);
}


double PerEval::calcPrecision(double *pred_labels, bool print)
{
	return calcAll(pred_labels, PRECISION , print);
}


double PerEval::calcRecall(double *pred_labels, bool print)
{
	return calcAll(pred_labels, RECALL , print);
}

inline int comp_lift( const void *e1,  const void *e2)
{
	register double **pa1 = (double**)e1;
	register double **pa2 = (double**)e2;
	if(*pa1[0] > *pa2[0]) return -1;
	if(*pa1[0] < *pa2[0]) return 1;
	return 0;
}


double PerEval::calcLift20P(double *prob, bool print)
{
	double **a = Malloc(double*,n);
	for( int i = 0; i < n; i++ )
		{
		a[i] = Malloc(double,2);
		a[i][1] = true_labels[i];
		a[i][0] = prob[i];
		}

	qsort(a,n,sizeof(double *),comp_lift);

	int n20p = n / 5;

	double lift20p = 0.0;
	double na = 0.0;

	for( int i = 0; i < n; i++ )
		{
		if( i < n20p && a[i][1] == 1.0 ) 
			lift20p++;
		if( a[i][1] == 1.0 ) 
			na++;
		}
	
	lift20p = lift20p / na * 5.0;

	if( print )
		{
		printf("\n########################### RESULT ##########################\n");
		printf(" Lift 20 perc = %f \n",lift20p);
		printf("#############################################################\n");
		}


	//for( int i = 0; i < n; i++ )
	//	{
	//	printf("%f -- %f\n",a[i][0],a[i][1]);
	//	free(a[i]);
	//	}

	free(a);
	return lift20p;
}


double PerEval::calcAll(double *pred_labels, int opt, bool print)
{
	//double right = 0.0;
	double tp = 0.0, tn = 0.0, fp = 0.0, fn = 0.0;
	bool no_fm = false;
	int acc = 0;
	
	for( int i = 0; i < n; i++ )
		{
		if( true_labels[i] != 1 && true_labels[i] > 0.0 )
			no_fm = true;
		if( pred_labels[i] == true_labels[i] ) acc++;
		if( pred_labels[i] == 1.0 && true_labels[i] == 1.0 )	tp++;
		else if( pred_labels[i] == 1.0 && true_labels[i] <= 0.0 )	fp++;
		else if( pred_labels[i] <= 0.0 && true_labels[i] == 1.0 )	fn++;
		else if( pred_labels[i] <= 0.0 && true_labels[i] <= 0.0 )	tn++;
		}

	accuracy = (tp+tn)/((double)n);
	precision = tp/(tp+fp);
	recall = tp/(tp+fn);
	FMeasure = 2.0 * precision * recall / ( precision + recall );

	if( print && !no_fm )
		{
		printf("\n########################### RESULT ##########################\n");
		printf(" Accuracy  = %f [%d/%d]\n",accuracy,(int)(tp+tn),n);
		printf(" Precision = %f [%d/%d]\n",precision,(int)(tp),(int)(tp+fp));
		printf(" Recall    = %f [%d/%d]\n",recall,(int)(tp),(int)(tp+fn));
		printf(" FMeasure  = %f \n",FMeasure);
		printf("#############################################################\n");
		}

	if( no_fm )
		{
		accuracy = ((double)(acc))/((double)n);
		if( print )
			{
			printf("\n########################### RESULT ##########################\n");
			printf(" Accuracy  = %f [%d/%d]\n",accuracy,(int)(acc),n);
			printf(" Precision not appliable (some labels are not -1 or +1)\n");
			printf(" Recall    not appliable (some labels are not -1 or +1)\n");
			printf(" FMeasure  not appliable (some labels are not -1 or +1)\n");
			printf("#############################################################\n");
			}
		}

	if( opt == ACCURACY ) return accuracy;
	if( opt == PRECISION ) return precision;
	if( opt == RECALL ) return recall;
	if( opt == FMEASURE ) 
		{
		if( !( FMeasure >= 0.0 || FMeasure <= 1.0 ) ) return 0.0;
		return FMeasure;
		}
	return -1.0;
}
