/*
    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 "matrices_headers.h"
#include "CRSMatrix.h"
#include "CCSMatrix.h"

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("SuperMatrix.matrices.CRSMatrix"));

namespace Matrices
{
	CRSMatrix::CRSMatrix(const std::string &name, size_t rows, size_t columns)
		: Matrix(name, rows, columns),
		m_vRows(NULL)
	{
		AllocateMemory();
	}
	CRSMatrix::CRSMatrix(const std::string &name, std::vector<CompressedVectorPtr> *rows, size_t columns)
		: Matrix(name, rows->size(), columns), m_vRows(rows)
	{

	}

	CRSMatrix::~CRSMatrix(void)
	{
		ClearMemory();
	}

	void CRSMatrix::AllocateMemory()
	{
		if( m_vRows != NULL)
			ClearMemory();

		m_vRows = new std::vector<CompressedVectorPtr>();
		for(size_t i = 0; i < m_stRows; i++)
			m_vRows->push_back(CompressedVectorPtr( new CompressedVector((size_t) m_stColumns, NULL) ));
	}

	void CRSMatrix::ClearMemory()
	{
		if(m_vRows)
		{
			delete m_vRows;
			m_vRows = NULL;
		}
	}

	double* CRSMatrix::GetColumn(size_t colNo)
	{
		size_t size = NumberOfRows();
		double *column = new double[size];

		for(size_t i = 0; i < size; i++)
			column[i] = GetCell(i, colNo );

		return column;
	}

	double* CRSMatrix::GetRow(size_t rowNo)
	{
		size_t size = NumberOfColumns();
		double *row = new double[size];
		double * data = (*m_vRows)[rowNo]->DecompressData();
		memcpy( row, data, sizeof(double) * size );
		(*m_vRows)[rowNo]->FreeBuffer();
		return row;
	}

	double* CRSMatrix::operator[](size_t rowNo)
	{
		return GetRow(rowNo);
	}

	void CRSMatrix::SetRow(std::vector<double> &row, size_t rowIndex)
	{
		for(size_t c = 0; c < m_stColumns ; c++)
			SetCell(row[c],rowIndex,c);
	}

	void CRSMatrix::SetRow(double* row, size_t rowIndex)
	{
		CompressedVectorPtr _row = (*m_vRows)[rowIndex];
		(*m_vRows)[rowIndex] = CompressedVectorPtr(new CompressedVector(NumberOfColumns(), row) );
	}

	void CRSMatrix::SetColumn(double* col, size_t colIndex)
	{
		for(size_t c = 0; c < m_stRows ; c++)
			SetCell(col[c],c,colIndex);
	}

	void CRSMatrix::SumRows(double *row, size_t rowIndex)
	{
		double *rowToSum = GetRow(rowIndex);
		size_t columns = m_stColumns;
		for(size_t c = 0; c < columns ; c++)
			rowToSum[c] += row[c];
		SetRow(rowToSum,rowIndex);
	}

	Matrix*& CRSMatrix::Transpose(Matrix*&  returnMatrix)
	{
		if(returnMatrix != NULL)
			throw std::runtime_error("CRSMatrix::Transpose : returnMatrix != NULL");

		CRSMatrix* mat = new CRSMatrix(m_sName+"_T",m_stColumns,m_stRows);
		returnMatrix = (Matrix*) mat;

		size_t columns = m_stColumns;
		for(size_t oldMCol = 0; oldMCol < columns ; oldMCol++)//dla kazdej kolumny w starej macierzy
		{
			double* col = GetColumn(oldMCol);
			mat->SetRow(col, oldMCol);
			delete[] col;
			if(oldMCol%20 == 0)
				LOG4CXX_INFO(logger, "Transposing matrix... " << oldMCol<< " / " << columns);
		}

		return returnMatrix;
	}


	void CRSMatrix::SetCell(double val, size_t rowInd, size_t colInd)
	{
		CompressedVectorPtr row = (*m_vRows)[rowInd];
		if(row == NULL)
		{
			std::stringstream err;
			err << "No row in CRSMatrix::SetCell (index outside of matrix). Row id:" << rowInd << " col id: " << colInd <<
					" val: "<< val << std::endl;
			throw std::runtime_error(err.str());
		}
		row->SetElement(colInd, val);
	}

	double CRSMatrix::GetCell(size_t rowInd, size_t colInd)
	{
		CompressedVectorPtr row = (*m_vRows)[rowInd];
		return (*row)[colInd];
	}

	void CRSMatrix::Clear()
	{
		ClearMemory();
		AllocateMemory();
	}

	void CRSMatrix::AddToCell(double value, size_t rowIndex, size_t columnIndex)
	{
		SetCell(GetCell(rowIndex,columnIndex)+value, rowIndex, columnIndex);
	}

	void CRSMatrix::Clone(Matrix* matrix)
	{
		using namespace boost::posix_time;
		if (matrix == NULL)
			throw std::runtime_error( std::string("CRSMatrix::Clone: input matrix cannot be null") );

		matrix->Clear();
		matrix->SetDescriptor(m_sName, NumberOfRows(),NumberOfColumns());

		ptime start = microsec_clock::local_time();
		time_duration total_time;
		for(size_t rowIndex = 0; rowIndex < NumberOfRows(); rowIndex++)
		{
			double * row = GetRow(rowIndex);
			matrix->SetRow(row, rowIndex);
			FreeMem(row);

			ptime end = microsec_clock::local_time();
			time_duration len = end-start;
			if(len.total_milliseconds() > 5000)
			{
				total_time += len;
				long total_ms = total_time.total_milliseconds();
				long eta_ms =  (total_ms*(NumberOfRows()-rowIndex-1)) / (rowIndex+1);
				LOG4CXX_INFO(logger, "Cloning row " << rowIndex << " / " << NumberOfRows()
							 << " ETA: " << to_simple_string( milliseconds(eta_ms) ));
				start = microsec_clock::local_time();
			}
		}

	}

	void CRSMatrix::Clone(CRSMatrix* matrix)
	{
		using namespace boost::posix_time;
		matrix->Clear();
		matrix->SetDescriptor(m_sName, NumberOfRows(),NumberOfColumns());

		ptime start = microsec_clock::local_time();
		time_duration total_time;
		for(size_t rowIndex = 0; rowIndex < NumberOfRows(); rowIndex++)
		{
			CompressedVectorPtr  row = GetCompressedRow(rowIndex);
			matrix->SetCompressedRow(rowIndex, *row);
			ptime end = microsec_clock::local_time();
			time_duration len = end-start;
			if(len.total_milliseconds() > 5000)
			{
				total_time += len;
				long total_ms = total_time.total_milliseconds();
				long eta_ms =  (total_ms*(NumberOfRows()-rowIndex-1)) / (rowIndex+1);
				LOG4CXX_INFO(logger, "Cloning row " << rowIndex << " / " << NumberOfRows()
							 << " ETA: " << to_simple_string( milliseconds(eta_ms) ) );
				start = microsec_clock::local_time();
			}
		}

	}
        void CRSMatrix::Clone(CCSMatrix* matrix)
	{
		using namespace boost::posix_time;
		matrix->Clear();
		matrix->SetDescriptor(m_sName, NumberOfRows(),NumberOfColumns());

		ptime start = microsec_clock::local_time();
		time_duration total_time;
		for(size_t rowIndex = 0; rowIndex < NumberOfRows(); rowIndex++)
		{
			CompressedVectorPtr  row = GetCompressedRow(rowIndex);
			matrix->SetCompressedRow(rowIndex, *row);
			ptime end = microsec_clock::local_time();
			time_duration len = end-start;
			if(len.total_milliseconds() > 5000)
			{
				total_time += len;
				long total_ms = total_time.total_milliseconds();
				long eta_ms =  (total_ms*(NumberOfRows()-rowIndex-1)) / (rowIndex+1);
				LOG4CXX_INFO(logger, "Cloning row " << rowIndex << " / " << NumberOfRows()
							 << " ETA: " << to_simple_string( milliseconds(eta_ms) ) );
				start = microsec_clock::local_time();
			}
		}

	}

	CompressedVectorPtr CRSMatrix::GetCompressedRow(size_t rowInd)
	{
		return (*m_vRows)[rowInd];
	}

	void CRSMatrix::SetCompressedRow(size_t rowNo, const CompressedVectorPtr pV)
	{
		(*m_vRows)[rowNo] = pV;
	}
	void CRSMatrix::SetCompressedColumn(size_t colNo, const CompressedVectorPtr pV)
	{
		STDoubleMapPtr values = pV->GetValues();
		for(STDoubleMapIter iter=values->begin();iter!=values->end();++iter)
		{
			SetCell(iter->second,iter->first,colNo);
		}
	}

	void CRSMatrix::SetCompressedColumn(size_t colNo, CompressedVector pV)
	{
		STDoubleMapPtr values = pV.GetValues();
		for(STDoubleMapIter iter=values->begin();iter!=values->end();++iter)
		{
			SetCell(iter->second,iter->first,colNo);
		}
	}

	void CRSMatrix::SetCompressedRow(size_t rowNo, const CompressedVector & v)
	{
		(*m_vRows)[rowNo] = CompressedVectorPtr(new CompressedVector(v));
	}

	void CRSMatrix::SetDescriptor(const std::string &name, size_t rows, size_t columns)
	{
		ClearMemory();
		Matrix::SetDescriptor(name, rows, columns);
		AllocateMemory();
	}
	CRSMatrixPtr GetNewCRSMatrix(const std::string &name, size_t rows, size_t columns)
	{
		return CRSMatrixPtr( new CRSMatrix(name,rows,columns) );
	}
}

