/*
    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 "RWFLinWeight.h"

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

namespace smartcomparator
{
	namespace weights
	{
		RWFLinWeight::RWFLinWeight(Matrices::SuperMatrixPtr pMatrix, RankBuilderPtr pRankbuilder, double treshold)
			: RWFBase(pMatrix, pRankbuilder ),
			m_dMiFilter(treshold),
			m_bInitalized(false),
			m_pWordForRelationWithAnyWord(NULL),
			m_pAnyWordForRelationWithWord(NULL),
			m_pAnyWordForRelationsWithAnyWord(NULL)
		{
			Initalize();
		}

		RWFLinWeight::~RWFLinWeight()
		{
			FreeMem();
		}

		void RWFLinWeight::FreeMem()
		{
			if(m_pAnyWordForRelationWithWord)
			{
				delete m_pAnyWordForRelationWithWord;
				m_pAnyWordForRelationWithWord = NULL;
			}

			if(m_pWordForRelationWithAnyWord)
			{
				for(size_t i = 0; i < m_vOperatorIndices.size(); i++)
				{
					if(m_pWordForRelationWithAnyWord[i] != NULL)
						delete m_pWordForRelationWithAnyWord[i];
				}
				m_pWordForRelationWithAnyWord = NULL;
			}

			if(m_pAnyWordForRelationsWithAnyWord)
			{
				delete m_pAnyWordForRelationsWithAnyWord;
				m_pAnyWordForRelationsWithAnyWord = NULL;
			}

			m_bInitalized = false;
		}//FreeMem

		void RWFLinWeight::Initalize()
		{
			std::map<size_t, std::string> boundaries = m_pMatrix->Boundaries();
			for(std::map<size_t, std::string>::iterator iter = boundaries.begin(); iter != boundaries.end(); ++iter)
				m_vOperatorIndices.push_back( iter->first );
			LOG4CXX_INFO(logger, "Initalizing weight function based on Lin's measure...");
			FreeMem();
			InitalizeAnyWordForRelationWithWordAndRelations();
			InitalizeWordForRelationWithAnyWord();

			LOG4CXX_INFO(logger,  "Initalizing weight function based on Lin's measure... Done!");

			m_bInitalized = true;

		}//Initalize

		void RWFLinWeight::InitalizeWordForRelationWithAnyWord()
		{
			Matrices::CRSMatrixPtr crs = m_pMatrix->GetCRS();

			if( crs == NULL )
				throw std::string("No CRS matrix in SuperMatrix");

			LOG4CXX_INFO(logger, "Counting || w,r,* || ");

			m_pWordForRelationWithAnyWord = new double*[m_vOperatorIndices.size()];

			for( size_t relation = 0; relation < m_vOperatorIndices.size() ; relation++ )
			{
				size_t lastIndex = (m_vOperatorIndices)[relation];

				m_pWordForRelationWithAnyWord[relation] = new double[crs->NumberOfRows()];

				for( size_t rowNo = 0; rowNo < crs->NumberOfRows() ; rowNo++ )
				{
					Matrices::CompressedVectorPtr row = crs->GetCompressedRow(rowNo);
					std::map<size_t, double> *vals = row->GetValues();
					double sum = 0;

					for( std::map<size_t, double>::iterator i = vals->begin();
						i != vals->end() && i->first < lastIndex ; i++)
						sum += i->second;


					m_pWordForRelationWithAnyWord[relation][rowNo] = sum;
				}

			}

			LOG4CXX_INFO(logger, "Counting || w,r,* || ");
		}//InitalizeWordForRelationWithAnyWord

		void RWFLinWeight::InitalizeAnyWordForRelationWithWordAndRelations()
		{
			Matrices::FeatureVectorPtr column_features = m_pMatrix->GetColumnFeatures();
			Matrices::FeatureVectorPtr row_features = m_pMatrix->GetRowFeatures();

			LOG4CXX_INFO(logger, "Counting || *, r, w' || and || *, r, * ||");

			size_t colSize = m_pMatrix->NumberOfColumns();

			m_pAnyWordForRelationWithWord = new double[colSize];

			size_t sum = 0;
			size_t currRelation = 0;

			std::copy(m_vOperatorIndices.begin(), m_vOperatorIndices.end(), std::ostream_iterator<size_t>(std::cout, ", ") );
			std::cout << std::endl;
			m_pAnyWordForRelationsWithAnyWord = new double[m_vOperatorIndices.size()];

			for( size_t w_prime = 0; w_prime < colSize; w_prime++)
			{
				m_pAnyWordForRelationWithWord[w_prime] = (double)(*column_features)[w_prime].GetTermFrequency();

				sum += (*column_features)[w_prime].GetTermFrequency();

				if( w_prime == (size_t) (m_vOperatorIndices)[currRelation] )
				{
					m_pAnyWordForRelationsWithAnyWord[currRelation++] = (double) sum;
					sum = 0;
				}
			}

			if(sum != 0)
				m_pAnyWordForRelationsWithAnyWord[currRelation] = (double) sum;

			LOG4CXX_INFO(logger,"Counting || *, r, w' || and || *, r, * || ... Done");
		}//InitalizeAnyWordForRelationWithWordAndRelations

		void RWFLinWeight::DoWeight(size_t rowindex, Matrices::CompressedVector *v, std::set<size_t> &filteredIndices,
				std::multimap<double, size_t> & val_to_col_no)
		{
			if(!m_bInitalized)
				Initalize();

			std::map<size_t, double>::iterator i = v->GetValues()->begin();

			for( size_t relation = 0; relation < m_vOperatorIndices.size() ; relation++ )
			{
				size_t lastIndex = (m_vOperatorIndices)[relation];

				double st_r_st = m_pAnyWordForRelationsWithAnyWord[relation];
				double w_r_st = m_pWordForRelationWithAnyWord[relation][rowindex];

				// could (should?) iterate over filteredIndices, and do removing of I<=0 after the loop...
				// but weighting is usualy done at the beginning of the loop
				while( i != v->GetValues()->end() && i->first < lastIndex)
				{
					if(filteredIndices.find(i->first) == filteredIndices.end() )
					{
						i++;
						continue;
					}

					double w_r_w = i->second;

					double st_r_w = m_pAnyWordForRelationWithWord[i->first];

					double a = w_r_w * st_r_st;
					double b = w_r_st * st_r_w;

					double div = a/b;

					if(div <= 0 )
					{
						i++;
						continue;
					}

					double I = log(div);

					if( I < m_dMiFilter)
					{
						i++;
						continue;
					}

					val_to_col_no.insert( std::pair<double, size_t> (I, i->first) );
					i++;
				}
			}
		}//Weight
	}//namespace weights

}//namespace smartcomparator

