/*********************************************************************
 * This file is part of the PRAPI library.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.7 $
 *********************************************************************/

#include "ConfusionMatrix.h"

using namespace std;
using namespace util;

namespace prapi
{
	double ConfusionMatrix::getMixup(int column) const
	{
		int wrong = 0, correct = 0;
		int* ptr = _pData + column;
		
		for (int i=0;i<_iRows;i++,ptr+=_iColumns)
			{
				if (i != column)
					wrong += *ptr;
				else
					correct = *ptr;
			}

		return double(wrong)/double(wrong+correct);
	}

	double ConfusionMatrix::getError(int row) const
	{
		int wrong = 0, correct = 0;
		int* ptr = _pData + row*_iColumns;
		
		for (int i=0;i<_iColumns;i++,ptr++)
			{
				if (i != row)
					wrong += *ptr;
				else
					correct = *ptr;
			}

		return double(wrong)/double(wrong+correct);
	}

	double ConfusionMatrix::getError() const
	{
		int wrong = 0, correct = 0;
		for (int i=0,index=0;i<_iRows;i++)
			{
				int w = 0, c = 0;
				for (int j=0;j<_iColumns;j++,index++)
					{
						if (j!=i)
							w += _pData[index];
						else
							c = _pData[index];
					}
				wrong += w;
				correct += c;
			}
		return double(wrong)/double(wrong+correct);
	}

	void ConfusionMatrix::print(ostream& out, const List<string>& lst, int space) const
	{
		char bfr[32];
		int max=0;
		if (space > 20)
			space = 20;
		else if (space < 4)
			space = 4;

		//Find the maximum length for a class name
		for (int i=0;i<lst.getLength();i++)
			{
				int len = lst[i].size();
				if (len > max)
					max = len;
			}
		int hMax = max;
		if (hMax < 5) hMax = 5;

		//Print names vertically
		for (int i=0;i<max;i++)
			{
				for (int j=0;j<hMax;j++)
					out << ' ';
				out << " |";
				for (int j=0;j<lst.getLength();j++)
					{
						int len = lst[j].size();
						for (int k=0;k<space;k++)
							out << ' ';
						if (len >= max-i)
							out << lst[j][len-max+i];
						else
							out << ' ';
					}
				out << " |";
				if (i==max-1)
					out << " Error";
				out << endl;
			}
		//Print line
		for (int i=0;i<hMax+_iColumns*(space+1)+11;i++)
			{
				if (i!=hMax+1 && i!=hMax+_iColumns*(space+1)+3)
					out << '-';
				else
					out << "+";
			}
		out << endl;

		int correct = 0, wrong = 0;
		//Print rows
		for (int i=0,*ptr=_pData;i<_iRows;i++)
			{
				//Class names first
				int remainder = hMax;
				if (lst.getLength() > i)
					remainder = hMax-lst[i].size();
				for (int j=0;j<remainder;j++)
					out << ' ';
				if (lst.getLength() > i)
					out << lst[i];
				out << " |";
				//Then data
				for (int j=0;j<_iColumns;j++,ptr++)
					{
						if (i!=j)
							wrong += *ptr;
						else
							correct += *ptr;
						
						if (!*ptr)
							strcpy(bfr,".");
						else
							sprintf(bfr,"%d",*ptr);
						for (int k=0;k<space-(int)strlen(bfr)+1;k++)
							out << ' ';
						out << bfr;
					}
				//Finally, error percentage
				sprintf(bfr,"%5.1f",getError(i)*100);
				out << " | " << bfr << endl;
			}
		//Print line
		for (int i=0;i<hMax+_iColumns*(space+1)+11;i++)
			{
				if (i!=hMax+1 && i!=hMax+_iColumns*(space+1)+3)
					out << '-';
				else
					out << "+";
			}
		out << endl;
		//Mixup percentages
		for (int i=0;i<hMax-5;i++)
			out << ' ';
		out << "Mixup |";
		for (int i=0;i<_iColumns;i++)
			{
				double mixup = getMixup(i);
				if (mixup != 1.0)
					sprintf(bfr,"%.1f",getMixup(i)*100);
				else
					strcpy(bfr,"100");
				for (int k=0;k<space-(int)strlen(bfr)+1;k++)
					out << ' ';
				out << bfr;
			}
		out << " |  %" << endl;

		out << endl
				<< "Number of classes       : " << _iColumns << endl
				<< "Total number of entries : " << wrong+correct << endl
				<< "Correctly classified    : " << correct << endl
				<< "Incorrectly classified  : " << wrong << endl
				<< "Total error             : " << (double(wrong*100)/double(wrong+correct)) << " %" << endl;
	}	
}
