/*
    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_stdheaders.h"
#include "RankBuilder.h"

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("SuperMatrix.comparator.weights.RankBuilder"));

namespace smartcomparator
{
	namespace weights
	{

		RankBuilder::RankBuilder(Matrices::SuperMatrixPtr pMatrix, size_t rank_size )
			: m_stRankSize(rank_size),m_pMatrix(pMatrix)

		{
		}

		RankBuilder::~RankBuilder()
		{
			// do nothing
		}

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

		NormalRankBuilder::NormalRankBuilder(Matrices::SuperMatrixPtr pMatrix, size_t rank_size, bool uniform_rank)
			: RankBuilder(pMatrix, rank_size),
			m_bUniformRank(uniform_rank)
		{
		}

		NormalRankBuilder::~NormalRankBuilder()
		{
			// does nothing
		}

		void NormalRankBuilder::operator()(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
				std::set<size_t> &filteredIndices)
		{
			v->GetValues()->clear();
			filteredIndices.clear();

			if(m_bUniformRank)
			{
				size_t ranked = 0;
				for(std::multimap<double, size_t>::reverse_iterator i = val_to_col_no.rbegin(); 
						i != val_to_col_no.rend() && ranked < m_stRankSize; i++)
				{
					size_t index = i->second ;
					v->SetElement(index, (double) (m_stRankSize - ranked++));
					filteredIndices.insert( index );
				}
			}//if m_bUniformRank
			else// not m_bUniformRank
			{

				std::multimap<double, size_t> score_rank_sorted_and_constrained;
				size_t ranked = 0;
				for(std::multimap<double, size_t>::reverse_iterator i = val_to_col_no.rbegin(); i != val_to_col_no.rend() && ranked < m_stRankSize; i++)
				{
					size_t index = i->second ;
					double value = i->first;
					score_rank_sorted_and_constrained.insert(std::pair<double, size_t>(value,index));
				}

				ranked = 0;
				for(std::multimap<double, size_t>::reverse_iterator i = score_rank_sorted_and_constrained.rbegin(); 
						i != score_rank_sorted_and_constrained.rend() && ranked < m_stRankSize;	i++)
				{
					size_t index = i->second ;
					v->SetElement(index, (double) (score_rank_sorted_and_constrained.size() - ranked++));
					filteredIndices.insert( index );

				}
			}// !m_bUniformRank
		}

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

		void PartialOrderRankBuilder::DoRanking(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
				std::set<size_t> &filteredIndices)
		{
			if(m_bSportsRank)
				SportsRank(val_to_col_no, row_no, v, filteredIndices);
			else
				NormalRank(val_to_col_no, row_no, v, filteredIndices);
		}


		PartialOrderRankBuilder::PartialOrderRankBuilder(Matrices::SuperMatrixPtr pMatrix, size_t rank_size, bool uniform_rank, bool sports_ranking,
														 bool idf, double idf_strength)
			: RankBuilder(pMatrix, rank_size),
			m_bUniformRank(uniform_rank),
			m_bSportsRank(sports_ranking),
			m_bIdf(idf),
			m_dIdfStrength(idf_strength)
		{
		}

		PartialOrderRankBuilder::~PartialOrderRankBuilder()
		{
			// do nothing
		}

		void PartialOrderRankBuilder::operator()(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
				std::set<size_t> &filteredIndices)
		{
			v->GetValues()->clear();
			filteredIndices.clear();


			DoDiscretization(val_to_col_no, row_no, v, filteredIndices);
			if(m_bIdf)
				IdfWeight(val_to_col_no, row_no, v, filteredIndices);

			DoRanking(val_to_col_no, row_no, v, filteredIndices);
		}

		void PartialOrderRankBuilder::SportsRank(std::multimap<double,size_t> &val_to_col_no,
				size_t row_no, Matrices::CompressedVector *v,std::set<size_t> &filteredIndices)
		{
			size_t rank_no = m_stRankSize, rank_skip = 0;
			double last_val = -10000;
			for(std::multimap<double, size_t>::reverse_iterator i = val_to_col_no.rbegin(); 
					i != val_to_col_no.rend(); i++)
			{
				size_t index = i->second ;
				double value = i->first;

				if( fabs(last_val - value) > 0.00001) 
				{
					rank_no -= rank_skip;
					rank_skip = 0;
					last_val = value;
					// if we will give negative rank - stop
					if(rank_no <= 0)
					{
						rank_no += rank_skip; // for 2nd arg of ToNonUniform
						break;
					}
				}
				else
					rank_skip++;

				v->SetElement(index, (double) ( rank_no ));
				filteredIndices.insert( index );
			}


			if(!m_bUniformRank && rank_no > 0)
				ToNonUniform(v->GetValues(), rank_no - 1);
		}

		void PartialOrderRankBuilder::NormalRank(std::multimap<double,size_t> &val_to_col_no, 
				size_t row_no, Matrices::CompressedVector *v,std::set<size_t> &filteredIndices)
		{
			size_t rank_no = m_stRankSize + 1;
			double last_val = -10000;
			for(std::multimap<double, size_t>::reverse_iterator i = val_to_col_no.rbegin(); 
					i != val_to_col_no.rend() ; i++)
			{
				size_t index = i->second ;
				double value = i->first;

				if( fabs(last_val - value) > 0.00001) 
				{
					rank_no--;
					last_val = value;

					if(rank_no <= 0 ) break;
				}

				v->SetElement(index, double ( rank_no ) );
				filteredIndices.insert( index );
			}
			if(!m_bUniformRank && rank_no > 0)
				ToNonUniform(v->GetValues(), rank_no - 1);
		}

		void PartialOrderRankBuilder::ToNonUniform(std::map<size_t,double> *pVectorElements, size_t next_rank)
		{
			size_t substract = next_rank;

			for(std::map<size_t,double>::iterator iter = pVectorElements->begin(); iter != pVectorElements->end(); iter++)
				iter->second -= substract;
		}

		void PartialOrderRankBuilder::IdfWeight(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices)
		{
			double N = (double) m_pMatrix->NumberOfRows();
			std::multimap<double, size_t> new_map;
			for(std::multimap<double,size_t>::iterator iter = val_to_col_no.begin(); iter != val_to_col_no.end(); iter++)
			{
				size_t column = iter->second;
				double value = iter->first;
				double  df_t = (double) (*m_pMatrix->GetColumnFeatures())[column].GetNZ();
				double idf = 0.1;
				if (df_t > 0)
					idf = log(N / df_t);
				else
					LOG4CXX_ERROR(logger,"*** df = " << df_t << " for column: " << column);
				new_map.insert( std::make_pair(value*idf * m_dIdfStrength, column) );
			}
			val_to_col_no = new_map;
		}
		///////////////////////////////////////////////

		RoundingBasedPartialOrder::RoundingBasedPartialOrder(Matrices::SuperMatrixPtr pMatrix, size_t rank_size, bool uniform_rank,
				double divide_factor, bool sports_ranking, bool idf, double idf_strenght )
			: PartialOrderRankBuilder(pMatrix, rank_size, uniform_rank, sports_ranking, idf, idf_strenght ),
			m_dDivideFactor(divide_factor)
		{
		}

		RoundingBasedPartialOrder::~RoundingBasedPartialOrder()
		{
			// do nothing
		}

		void RoundingBasedPartialOrder::DoDiscretization(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
				std::set<size_t> &filteredIndices)
		{
			//TODO not efficient

			std::multimap<double, size_t> new_map;
			for(std::multimap<double,size_t>::iterator iter = val_to_col_no.begin(); iter != val_to_col_no.end(); iter++)
				new_map.insert( std::pair<double, size_t> (round( (iter->first)/m_dDivideFactor), iter->second ) );
			val_to_col_no = new_map;
		}

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

	}//namespace weights
}//namespace smartcomparator

