/*
    Copyright (C) 2012 Bartosz Broda, Jacek Bochenek, Łukasz Biłeńkij, 
    Damian Jaworski, Wojciech Mazur, Dominik Piasecki, Maciej Piasecki, 
    Radosław Ramocki
    
    This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.

    This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. 

    See the LICENSE and COPYING files for more details.
*/

#include "supermatrixfilter.h"
#include "measures.h"

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("SuperMatrix.matrices.supermatrixfilter"));

namespace Matrices
{
	CRSMatrixPtr FilterCRS(CRSMatrixPtr pMatrix, std::set<size_t> &erase_row_indices)
	{
		size_t initial_rows = pMatrix->NumberOfRows();
		size_t columns = pMatrix->NumberOfColumns();

		std::vector<CompressedVectorPtr> rows;
		for(size_t row_no = 0; row_no < initial_rows; ++row_no)
			if(erase_row_indices.find(row_no) == erase_row_indices.end())
				rows.push_back( pMatrix->GetCompressedRow(row_no) );

		CRSMatrixPtr pCRS( new CRSMatrix("matrix_crs", rows.size(), columns) );

		for(size_t row_no = 0; row_no < rows.size(); ++row_no)
			pCRS->SetCompressedRow(row_no, rows[row_no]);

		return pCRS;
	}

	CCSMatrixPtr FilterCCS(CCSMatrixPtr pMatrix, std::set<size_t> &erase_column_indices)
	{
		size_t rows = pMatrix->NumberOfRows();
		size_t initial_columns = pMatrix->NumberOfColumns();

		std::vector<CompressedVectorPtr> columns;
		for(size_t column_no = 0; column_no < initial_columns; column_no++)
			if(erase_column_indices.find(column_no) == erase_column_indices.end())
				columns.push_back( pMatrix->GetCompressedColumn(column_no) );

		CCSMatrixPtr pCCS( new CCSMatrix("matrix_ccs", rows, columns.size()) );
		for(size_t column_no = 0; column_no < columns.size(); column_no++ )
			pCCS->SetCompressedColumn( column_no, columns[column_no] );

		return pCCS;
	}

	CCSMatrixPtr CloneToCCS(CRSMatrixPtr pMatrix)
	{
		size_t rows = pMatrix->NumberOfRows();
		size_t columns = pMatrix->NumberOfColumns();
		CCSMatrixPtr pCCS( new CCSMatrix("matrix_ccs", rows, columns) );
		for(size_t row_no = 0; row_no < rows; ++row_no)
		{
			CompressedVectorPtr pRow = pMatrix->GetCompressedRow(row_no);

			STDoubleMapPtr  pVals = pRow->GetValues();
			STDoubleMapIter end = pVals->end();
			for(STDoubleMapIter column_iter = pVals->begin(); column_iter != end; ++column_iter)
				pCCS->SetCell(column_iter->second, row_no, column_iter->first);
		}

		return pCCS;
	}

	CRSMatrixPtr CloneToCRS(CCSMatrixPtr pMatrix)
	{
		size_t rows = pMatrix->NumberOfRows();
		size_t columns = pMatrix->NumberOfColumns();
		CRSMatrixPtr pCRS( new CRSMatrix("matrix_crs", rows, columns) );
		for(size_t column_no = 0; column_no < columns; ++column_no)
		{
			CompressedVectorPtr pColumn = pMatrix->GetCompressedColumn(column_no);
			STDoubleMapPtr  pVals = pColumn->GetValues();
			STDoubleMapIter end = pVals->end();
			for(STDoubleMapIter row_iter = pVals->begin(); row_iter != end; ++row_iter)
				pCRS->SetCell( row_iter->second, row_iter->first, column_no);
		}

		return pCRS;
	}

	FeatureVectorPtr FilterRowFeatures(CRSMatrixPtr pMatrix, std::set<size_t> &erased_row_indices,
										 FeatureVectorPtr old_features, bool update_features, bool update_tf)
	{
		FeatureVectorPtr new_row_features( new FeatureVector() );
		size_t new_row_no = -1;
		for(size_t row_no = 0; row_no < old_features->size(); row_no++)
		{
			if(erased_row_indices.find(row_no) != erased_row_indices.end())
				continue;
			new_row_no++;
			Feature old_feature = (*old_features)[row_no];
			if(update_features)
			{
				Matrices::CompressedVectorPtr row = pMatrix->GetCompressedRow(new_row_no);
				double entropy  = Measures::CountVectorEntropy( *row );
				size_t tf = old_feature.GetTermFrequency();
				if(update_tf)
					tf = (size_t) Measures::CountVectorSum( *row );
				Feature f(old_feature.GetLabel(), row->GetValues()->size(), tf, old_feature.GetGlobalTermFrequency(),
						  entropy);
				new_row_features->push_back(f);
			}
			else
				new_row_features->push_back(old_feature);
		}
		return new_row_features;
	}

	FeatureVectorPtr FilterColumnFeatures(CCSMatrixPtr pMatrix, std::set<size_t> &erased_column_indices,
										 FeatureVectorPtr old_features, bool update_features, bool update_tf)
	{
		FeatureVectorPtr new_column_features( new FeatureVector() );
		size_t new_col_no = -1;
		for(size_t column_no = 0; column_no < old_features->size(); column_no++)
		{
			if(erased_column_indices.find(column_no) != erased_column_indices.end())
				continue;
			new_col_no ++;
			Feature old_feature = (*old_features)[column_no];
			if(update_features)
			{
				Matrices::CompressedVectorPtr column = pMatrix->GetCompressedColumn(new_col_no);
				double entropy  = Measures::CountVectorEntropy( *column );
				size_t tf = old_feature.GetTermFrequency();
				if(update_tf)
					tf = (size_t) Measures::CountVectorSum( *column );
				Feature f(old_feature.GetLabel(), column->GetValues()->size(), tf,
						  old_feature.GetGlobalTermFrequency(), entropy);
				new_column_features->push_back(f);
			}
			else
				new_column_features->push_back(old_feature);
		}
		return new_column_features;
	}

	size_t NumberOfLowerThenOrEqualTo(size_t index, std::set<size_t> &indices)
	{
		size_t no = 0;
		for(std::set<size_t>::iterator iter = indices.begin(); iter != indices.end(); ++iter)
		{
			if( *iter <= index )
				no++;
			else
				break;
		}
		return no;
	}
	size_t NumberOfLowerThan(size_t index, std::set<size_t> &indices)
	{
		size_t no = 0;
		for(std::set<size_t>::iterator iter = indices.begin(); iter != indices.end(); ++iter)
		{
			if( *iter < index )
				no++;
			else
				break;
		}
		return no;
	}
	std::map<size_t, std::string> UpdateBoundaries(SuperMatrixPtr pOldMatrix, std::set<size_t> &erased_column_indices)
	{
		std::map<size_t, std::string> boundaries;

		std::map<size_t, std::string> &old_boundaries = pOldMatrix->Boundaries();

		for(std::map<size_t, std::string>::iterator iter = old_boundaries.begin(); iter != old_boundaries.end(); ++iter)
		{
			size_t to_substract = NumberOfLowerThan(iter->first, erased_column_indices);

			//Prevention from inserting empty boundaries
			std::map<size_t, std::string>::iterator finder = boundaries.find((iter->first) - to_substract);
			//Already there							Empty first boundary
			if(finder==boundaries.end() &&  (iter->first) - to_substract != 0)
				boundaries.insert( std::make_pair( (iter->first) - to_substract, iter->second)  );
		}

		return boundaries;
	}

	SuperMatrixPtr FilterSuperMatrix(SuperMatrixPtr pMatrix, std::set<size_t> &erase_row_indices,
									 std::set<size_t> &erase_column_indices, bool update_features,
									 bool output_crs, bool output_ccs, bool update_tf)
	{
		LOG4CXX_INFO(logger,"Filtering CRS");
		// create new CRS matrix
		CRSMatrixPtr pCRS = FilterCRS(pMatrix->GetCRS(), erase_row_indices);
		// create new CCS matrix
		LOG4CXX_INFO(logger,"Syncing with CCS");
		CCSMatrixPtr pCCS = CloneToCCS(pCRS);
		LOG4CXX_INFO(logger,"Filtering CCS");
		pCCS = FilterCCS(pCCS, erase_column_indices);
		LOG4CXX_INFO(logger,"Syncing with CRS");
		pCRS = CloneToCRS(pCCS);

		LOG4CXX_INFO(logger,"Creating new row features");
		FeatureVectorPtr new_row_features = FilterRowFeatures(pCRS, erase_row_indices,
															  pMatrix->GetRowFeatures(), update_features, update_tf);
		LOG4CXX_INFO(logger,"Creating new column features");
		FeatureVectorPtr new_column_features = FilterColumnFeatures(pCCS, erase_column_indices,
																	pMatrix->GetColumnFeatures(), update_features, update_tf);

		std::map<size_t, std::string> boundaries = UpdateBoundaries(pMatrix, erase_column_indices);
		return SuperMatrixPtr( new SuperMatrix(pMatrix->GetName()+"_filtered", pCCS, pCRS, new_row_features,
							new_column_features, pMatrix->GetTokenCount(), pMatrix->GetCorpusInfo(), boundaries) );
	}
}//namespace Matrices

