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

#ifndef COMPARATOR_RANK_BUILDER
#define COMPARATOR_RANK_BUILDER

namespace smartcomparator
{
	namespace weights
	{
		/**
		 * Function object for RWFBase class. It is used for creating of different ranks.
		 */
		class SMART_COMPARATOR_API RankBuilder
		{
			public:
				/**
				 * \param rank_size Size of ranking.
				 */ 
				RankBuilder(Matrices::SuperMatrixPtr pMatrix, size_t rank_size = 350);
				virtual ~RankBuilder();

				/**
				 * \param val_to_col_no weighted value -> no_of_column --- used in creation of rank
				 * \param v vector to insert ranks (first, it should be cleared and then filled)
				 * \param filteredIndices indices of non-zero in v (first, it should be cleared and then filled)
				 */
				virtual void operator()(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices) = 0;

			protected:
				size_t m_stRankSize;
				Matrices::SuperMatrixPtr m_pMatrix;
		};

		typedef boost::shared_ptr<RankBuilder> RankBuilderPtr;

		/**
		 *
		 */ 
		class NormalRankBuilder : public RankBuilder
		{
			public:
				/**
				 * \param uniform_rank if true then rank starts at rank_size and goes down (even if no. of features is less then rank_size)
				 * If false rank starts from number of cells above z-score threshold, 
				 * e.g. if only 50 cells are above z-score threshold (or only 50 cells are non-zero) 
				 * and rank_size = 100, then best cell will have value of 100 for uniform_rank = true and 50 for uniform_rank = false
				 */
				NormalRankBuilder(Matrices::SuperMatrixPtr pMatrix, size_t rank_size = 350, bool uniform_rank = false);
				virtual ~NormalRankBuilder();
				virtual void operator()(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices);

			protected:
				bool m_bUniformRank;

		};
		
		/**
		 *
		 */ 
		class PartialOrderRankBuilder : public RankBuilder
		{
			public:
				PartialOrderRankBuilder(Matrices::SuperMatrixPtr pMatrix, size_t rank_size = 350, bool uniform_rank = false,
										bool sports_ranking = false, bool idf = false, double idf_strength = 1.0);
				virtual ~PartialOrderRankBuilder();
				/**
				 * Calls sequentially DoDiscretization and DoRanking
				 */ 
				virtual void operator()(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices);
			protected:
				/**
				 * Performs discretization of features. Simple scheme will be based on i.e. rounding, more advanced schemes can use
				 * f.e. avarage-link approach. 
				 * It updates values in val_to_col_no
				 */ 
				virtual void DoDiscretization(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices) = 0;
				/**
				 * Multiply each cell by "inversed document frequency", i.e., log (N / column_non_zeros),
				 * where N is number of rows and column_non_zeros is number of non zero elements in given column
				 * (similar to document frequency).
				 */
				void IdfWeight(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices);
				/**
				 * Creates ranks out of discretized features.
				 */ 
				virtual void DoRanking(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices);

				virtual void SportsRank(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices);
				virtual void NormalRank(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices);

				/**
				 * \param next_rank Next rank value (when assigned in descending order). In other words, lowest rank value - 1
				 */ 
				void ToNonUniform(std::map<size_t,double> *pVectorElements, size_t next_rank);

				/// see NormalRankBuilder
				bool m_bUniformRank;
				bool m_bSportsRank;
				bool m_bIdf;
				bool m_dIdfStrength;

		};

		class RoundingBasedPartialOrder : public PartialOrderRankBuilder
		{
			public:
				RoundingBasedPartialOrder(Matrices::SuperMatrixPtr pMatrix, size_t rank_size = 350, bool uniform_rank = false,
						double divide_factor = 10.0, bool sports_ranking = false, bool idf = false, double idf_strength = 1.0);
				~RoundingBasedPartialOrder();
			protected:
				virtual void DoDiscretization(std::multimap<double,size_t> &val_to_col_no, size_t row_no, Matrices::CompressedVector *v,
						std::set<size_t> &filteredIndices);

				double m_dDivideFactor;
		};

	}//namespace weights
	
}//namespace smartcomparator

#endif //COMPARATOR_RANK_BUILDER

