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

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

namespace smartcomparator
{
	namespace weights
	{
		MIWeight::MIWeight(Matrices::SuperMatrixPtr m, double miFilter)
			: WeightFeature(m),
			m_dMiFilter(miFilter),
			m_bInitalized(false),
			m_pWordForRelationWithAnyWord(NULL),
			m_pAnyWordForRelationWithWord(NULL),
			m_pAnyWordForRelationsWithAnyWord(NULL)
		{
			Initalize();
		}

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

		void MIWeight::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 MIWeight::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 MIWeight::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()];

				LOG4CXX_INFO(logger, "Counting || w,r,* || for operator " << (relation+1) << " / " << m_vOperatorIndices.size());


				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,* || Done");
		}//InitalizeWordForRelationWithAnyWord

		void MIWeight::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 MIWeight::Weight(size_t rowindex, Matrices::CompressedVector *v, std::set<size_t> &filteredIndices)
		{
			if(!m_bInitalized)
				Initalize();

			std::vector<size_t> zeroedToRemove; // indices of columns to remove from v (zeroed by weighting, below mi threshold

			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 )
					{
						zeroedToRemove.push_back(i->first);
						//i->second = 0;
						filteredIndices.erase(i->first);
						i++;
						continue;
					}

					double I = log(div);

					if( I < m_dMiFilter)
					{
						//i->second = 0;
						zeroedToRemove.push_back(i->first);
						filteredIndices.erase(i->first);
						i++;
						continue;
					}

					i->second = I;

					i++;
				}
			}

			for(std::vector<size_t>::iterator removeIter = zeroedToRemove.begin(); removeIter != zeroedToRemove.end(); ++ removeIter)
				v->SetElement(*removeIter, 0); // will remove element from matrix

			/*std::multimap<double, size_t> best;
			for(std::set<size_t>::iterator iter = filteredIndices.begin(); iter != filteredIndices.end(); ++iter)
				best.insert( std::make_pair((*v)[*iter], *iter) );

			Matrices::FeatureVectorPtr row_features = m_pMatrix->GetRowFeatures();
			Matrices::FeatureVectorPtr col_features = m_pMatrix->GetColumnFeatures();
			std::cout << "Best features for " << (*row_features)[rowindex].GetLabel() << ": ";
			int z = 0;
			for(std::multimap<double,size_t>::reverse_iterator iter = best.rbegin(); iter != best.rend();++iter)
			{
				std::cout << (*col_features)[iter->second].GetLabel() << "(" << iter->first << "), ";
				if(z++ > 100)
					break;
			}
			std::cout << std::endl << std::endl;//*/
		}//Weight
	}//namespace weights

}//namespace smartcomparator

