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

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("SuperMatrix.comparator.simfunctions.MCFSim"));

namespace smartcomparator
{
	namespace simfunctions
	{
		MCFSim::MCFSim(Matrices::SuperMatrixPtr m, SimilarityFunctionPtr pSimInner,
					   weights::WeightFeaturePtr pWeight, double threshold)
				: SimilarityFunction(m),
				m_dThreshold(threshold),
				m_pSimilarity(pSimInner),
				m_pWeight(pWeight)
		{
			CountSums();
		}

		MCFSim::~MCFSim()
		{
		}

		double MCFSim::MinValue() const
		{
			return -1;
		}

		bool MCFSim::IsSymmetrical() const
		{
			return false;
		}

		double MCFSim::CountSimilarity(Matrices::CompressedVector& v1, Matrices::CompressedVector& v2,
				size_t v1ind, size_t v2ind,	const std::set<size_t> &v1Ind, const std::set<size_t> &v2Ind)
		{
			// get "smaller", in terms of NZ
			Matrices::CompressedVector *smaller, *larger;
			const std::set<size_t> *smaller_ind;

			if(v1Ind.size() > v2Ind.size())
			{
				smaller_ind = &v2Ind;
				smaller = &v2;
				larger = &v1;
			}
			else
			{
				smaller_ind = &v1Ind;
				smaller = &v1;
				larger = &v2;
			}

			// count sums
			double sum_smaller = CountSum(*smaller_ind); // to jest zle
			std::set<size_t> ind_intersec;
			std::set_intersection( v1Ind.begin(), v1Ind.end(), v2Ind.begin(), v2Ind.end(), std::inserter(ind_intersec,
																										 ind_intersec.begin()));
			double sum_intersec = CountSum(ind_intersec);

			// check threshold

			if( sum_intersec / sum_smaller > m_dThreshold)
				return m_pSimilarity->CountSimilarity(v1, v2, v1ind, v2ind, v1Ind, v2Ind);

			return -1;
		}

		double MCFSim::CountSum(const std::set<size_t> &ind)
		{
				double sum = 0.0;
				for(std::set<size_t>::iterator iter = ind.begin(); iter != ind.end(); ++iter)
					sum += m_vSums[*iter];
				return sum;
		}

		void MCFSim::CountSums()
		{
			//FIXME filtering and selection
			LOG4CXX_WARN(logger, "Warning: MCFSim is not using filtering!");

			size_t rows =  m_pMatrix->NumberOfRows();
			size_t cols =  m_pMatrix->NumberOfColumns();

			for(size_t col = 0; col < cols; col++)
				m_vSums.push_back(0);

			for(size_t row_no = 0; row_no < rows; row_no++)
			{
				Matrices::CompressedVectorPtr row = m_pMatrix->GetCRS()->GetCompressedRow(row_no);
				Matrices::CompressedVector rowCopy(*row);
				std::set<size_t> indices;
				for(std::map<size_t, double>::const_iterator iter = rowCopy.GetValues()->begin();
					iter != rowCopy.GetValues()->end(); iter++)
					indices.insert(iter->first);

				m_pWeight->Weight(row_no, &rowCopy, indices);

				for(std::map<size_t, double>::const_iterator iter = rowCopy.GetValues()->begin();
					iter != rowCopy.GetValues()->end(); iter++)
				{
					size_t i = iter->first;
					double val = iter->second;
					m_vSums[i] += val;
				}
			}
		}
	}//namespace simfunctions



}//namespace smartcomparator
