/*
    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 "comparator_utils.h"
#include "comparator_all.h"
#include "matrices_all.h"

void zscore_weight_matrix(Matrices::SuperMatrixPtr pMatrix)
{
	boost::shared_ptr<smartcomparator::weights::WeightFeature> pw(new smartcomparator::weights::TScoreWeight(pMatrix));
	smartcomparator::weights::MatrixTransformer transformer(pw);
	transformer.Transform();
}

void lin_weight_matrix(Matrices::SuperMatrixPtr pMatrix)
{
	boost::shared_ptr<smartcomparator::weights::WeightFeature> pw(new smartcomparator::weights::MIWeight(pMatrix));
	smartcomparator::weights::MatrixTransformer transformer(pw);
	transformer.Transform();
}

void pmi_weight_matrix(Matrices::SuperMatrixPtr pMatrix)
{
	boost::shared_ptr<smartcomparator::weights::WeightFeature> pw(new smartcomparator::weights::SimpleMIWeight(pMatrix));
	smartcomparator::weights::MatrixTransformer transformer(pw);
	transformer.Transform();
}

void logent_weight_matrix(Matrices::SuperMatrixPtr pMatrix)
{
	boost::shared_ptr<smartcomparator::weights::WeightFeature> pw(new smartcomparator::weights::LogEntWeight(pMatrix));
	smartcomparator::weights::MatrixTransformer transformer(pw);
	transformer.Transform();
}

void noise_filter_matrix(Matrices::SuperMatrixPtr pMatrix, size_t mintf, double noisePrec)
{
	Matrices::FeatureVectorPtr features = pMatrix->GetColumnFeatures();
	size_t cNoise = (size_t) (pMatrix->NumberOfColumns()*noisePrec);
	smartcomparator::filters::NoiseKTFFilter filter(features, cNoise, mintf);

	for(size_t rowindex = 0; rowindex < pMatrix->NumberOfRows(); rowindex++)
	{
		Matrices::CompressedVectorPtr row = pMatrix->GetRow(rowindex);
		std::map<size_t, double> *values = row->GetValues();

		std::set<size_t> nzind;
		std::set<size_t> after_filtering;
		std::set<size_t> intersection;
		std::insert_iterator< std::set<size_t> > inserter( intersection, intersection.begin() );

		for(std::map<size_t, double>::iterator iter = values->begin(); iter != values->end(); ++iter)
			nzind.insert(iter->first);

		filter.Filter(after_filtering, &nzind);
		std::set_difference(nzind.begin(), nzind.end(), after_filtering.begin(), after_filtering.end(), inserter);
		for(std::set<size_t>::iterator iter = intersection.begin(); iter != intersection.end(); ++iter)
			row->SetElement(*iter, 0);
	}
	pMatrix->SyncCRSToCCS();
}

///////////////////////// rwf //////////////////////

void rwf_pmi_weight_matrix(Matrices::SuperMatrixPtr pMatrix, double threshold, bool discount, const std::string &args)
{
	size_t arg_c = 0;
	boost::shared_ptr<smartcomparator::weights::WeightFeature> pw(new smartcomparator::weights::RWFPMIWeight(pMatrix,
				smartcomparator::compare::FunctionFactory::CreateRankBuilder(args,pMatrix, arg_c)
				, discount, threshold ));
	smartcomparator::weights::MatrixTransformer transformer(pw);
	transformer.Transform();
}

void rwf_lin_weight_matrix(Matrices::SuperMatrixPtr pMatrix, double threshold, const std::string &args)
{
	size_t arg_c = 0;
	boost::shared_ptr<smartcomparator::weights::WeightFeature> pw(new smartcomparator::weights::RWFLinWeight(pMatrix,
				smartcomparator::compare::FunctionFactory::CreateRankBuilder(args,pMatrix, arg_c)
				, threshold ));
	smartcomparator::weights::MatrixTransformer transformer(pw);
	transformer.Transform();
}

void rwf_zscore_weight_matrix(Matrices::SuperMatrixPtr pMatrix, double min_rz, const std::string &args)
{
	size_t arg_c = 0;
	boost::shared_ptr<smartcomparator::weights::WeightFeature> pw(new smartcomparator::weights::RWFZScore(pMatrix,
				smartcomparator::compare::FunctionFactory::CreateRankBuilder(args,pMatrix, arg_c)
				, min_rz ));
	smartcomparator::weights::MatrixTransformer transformer(pw);
	transformer.Transform();
}

///////////////////////////

std::vector<std::string> get_kbest(smartcomparator::compare::CompareFunctionPtr pCmp, const std::string &word, int k )
{
	Matrices::SuperMatrixPtr pMatrix = pCmp->GetMatrix();
	size_t row = pMatrix->GetRowIndex(word);
	size_t rows = pMatrix->NumberOfRows();
	if(row > rows)
	{
		std::cerr << "Word " << word << " is not in matrix!" << std::endl;
		return std::vector<std::string>();
	}

	std::multimap<double, size_t> simmap;

	for(size_t row_no = 0; row_no < rows ; row_no ++)
	{
		if(row_no == row)
			continue;
		double sim = pCmp->Compare(row, row_no);
		simmap.insert( std::pair<double,size_t>(sim, row_no));
	}
	std::vector<std::string> kbest;
	Matrices::FeatureVectorPtr features = pMatrix->GetRowFeatures();
	for(std::multimap<double, size_t>::reverse_iterator riter = simmap.rbegin();
		riter != simmap.rend() && kbest.size() < k; riter++)
	{
		double sim = riter->first;
		if (sim < 0.000001)
			break;
		kbest.push_back( features->operator[]( riter->second ).GetLabel() );
	}
	return kbest;
}

std::vector< std::pair<std::string,double> > get_kbest_val(smartcomparator::compare::CompareFunctionPtr pCmp, const std::string &word, int k )
{
	Matrices::SuperMatrixPtr pMatrix = pCmp->GetMatrix();
	size_t row = pMatrix->GetRowIndex(word);
	size_t rows = pMatrix->NumberOfRows();
	if(row > rows)
	{
		std::cerr << "Word " << word << " is not in matrix!" << std::endl;
		return std::vector< std::pair<std::string,double> >();
	}

	std::multimap<double, size_t> simmap;

	for(size_t row_no = 0; row_no < rows ; row_no ++)
	{
		if(row_no == row)
			continue;
		double sim = pCmp->Compare(row, row_no);
		simmap.insert( std::pair<double,size_t>(sim, row_no));
	}
	std::vector<std::pair<std::string, double> > kbest;
	Matrices::FeatureVectorPtr features = pMatrix->GetRowFeatures();
	for(std::multimap<double, size_t>::reverse_iterator riter = simmap.rbegin();
		riter != simmap.rend() && kbest.size() < k; riter++)
	{
		double sim = riter->first;
		if (sim < 0.000001)
			break;
		kbest.push_back( std::pair<std::string,double>(features->operator[]( riter->second ).GetLabel(), sim) );
	}
	return kbest;
}

std::vector< std::pair<std::string, double> > generate_kbest(smartcomparator::compare::CompareFunctionPtr pCmp, const std::string &word, int k )
{
	std::vector< std::pair<std::string, double> > kbest;

	Matrices::SuperMatrixPtr pMatrix = pCmp->GetMatrix();
	size_t row = pMatrix->GetRowIndex(word);
	size_t rows = pMatrix->NumberOfRows();
	if(row > rows)
	{
		std::cerr << "Word " << word << " is not in matrix!" << std::endl;
		return kbest;
	}

	std::multimap<double, size_t> simmap;

	for(size_t row_no = 0; row_no < rows ; row_no ++)
	{
		if(row_no == row)
			continue;
		double sim = pCmp->Compare(row, row_no);
		simmap.insert( std::pair<double,size_t>(sim, row_no));
	}
	Matrices::FeatureVectorPtr features = pMatrix->GetRowFeatures();
	size_t found = 0;
	for(std::multimap<double, size_t>::reverse_iterator riter = simmap.rbegin();
		riter != simmap.rend() && found < k; riter++)
	{
		found++;
		double sim = riter->first;
		if (sim < 0.000001)
			break;
		kbest.push_back( std::make_pair( features->operator[]( riter->second ).GetLabel(), sim) );
	}
	return kbest;

}
