/*********************************************************************
 * 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.12 $
 *********************************************************************/

#ifndef _CONFUSIONMATRIX_H
#define _CONFUSIONMATRIX_H

#include "Sample.h"
#include <List.h>
#include <Matrix.h>
#include <Exception.h>
#include <iostream>
#include <stdio.h>
#include <string>

namespace prapi
{
	/**
	 * ConfusionMatrix is a class for representing classification
	 * results. It provides methods for calculating error rates for
	 * different classes and for the whole matrix. A ConfusionMatrix can
	 * be printed in a human-readable format.
	 **/
	class ConfusionMatrix : public util::Matrix<int>
	{
	public:
		/**
		 * Create an initially empty size-by-size confusion matrix.
		 **/
		ConfusionMatrix(int size=1) : util::Matrix<int>(size) {}
		/**
		 * Create a confusion matrix out of a list of classified samples.
		 * @param lst a list of classified samples
		 * @param classCount the number of classes (-1 to determine automatically)
		 **/
		template <class T, class I, class C> ConfusionMatrix(const util::List<Sample<T,I,C> >& lst, int classCount = -1)
			throw (util::InvalidArgumentException&);
		//ConfusionMatrix(int rows, int columns) : Matrix<int>(rows,columns) {}
		//ConfusionMatrix(Matrix& mat) : Matrix(mat) {}

		/**
		 * Print the confusion matrix to the given output stream. The
		 * class names will be taken from <i>lst</i>, and indicated amount
		 * of space is reserved for each matrix column.
		 *
		 * @param out the output stream
		 * @param lst list of classes
		 * @param space column width-1 (maximum length of a matrix entry represented as a decimal number)
		 **/
		void print(std::ostream& out, const util::List<std::string>& lst, int space=4) const;
		/**
		 * Get the error percentage for the whole confusion matrix.
		 **/
		double getError() const;
		/**
		 * Get the error percentage for a row in the matrix.
		 **/
		double getError(int row) const;
		/**
		 * Get the mixup percentage for a column. The mixup percentage
		 * tells the fraction of correctly classified samples among all
		 * samples classified to a certain class.
		 **/
		double getMixup(int column) const;
	};

	template <class T, class I, class C> ConfusionMatrix::ConfusionMatrix(const util::List<Sample<T,I,C> >& lst, int classCount)
		throw (InvalidArgumentException&)
		: util::Matrix<int>(classCount)
	{
		if (classCount = -1)
			{
				for (int i=lst.getLength();i--;)
					{
						if ((int)lst[i].getTrueClass() >= classCount)
							classCount = (int)lst[i].getTrueClass() + 1;
						if ((int)lst[i].getClassification() >= classCount)
							classCount = (int)lst[i].getClassification() + 1;
					}
				if (classCount <= 0)
					throw InvalidArgumentException("ConfusionMatrix::ConfusionMatrix(): There must be at least one class. Found none.");
			}
		for (int i=0;i<lst.getLength();i++)
			{
				int classIndex = (int)lst[i].getTrueClass(), classification = (int)lst[i].getClassification();
				if (classIndex < 0 || classIndex >= classCount || classification < 0 || classification >= classCount)
					{
						//cerr << "Class: " << classIndex << ", Classification: " << classification << endl;
						throw InvalidArgumentException("ConfusionMatrix::ConfusionMatrix(): "
																					 "invalid class (" + util::String::toString((int)classIndex) + ") "
																					 "or classification index (" + util::String::toString((int)classification) + ").");
					}
				(*this)(classIndex,classification)++;
			}
	}
}

#endif
