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

SVDRunner::SVDRunner()
{
	Config* cfg = Config::getInstance();
	dimensions = cfg->dimensions;
	iterations = cfg->iterations;
	svdPath = cfg->svdPath;
	workDir = cfg->workdir;
	matrixPath = cfg->matrix;
	output = cfg->output;
}

void SVDRunner::getDesiredDimensions()
{
	Config* cfg = Config::getInstance();
	int resultsDimensions=-1;
	while(resultsDimensions<dimensions)
	{
		std::stringstream command;
		command<<"tcsh -c '"<<svdPath<<" -d "<<dimensions<<" -i "<<iterations<<" -o "<<workDir<<" -r st "<<matrixPath<<"'";
		int returnValue = system(command.str().c_str());
		if(returnValue!=0)
			std::cerr<<"Return value from system: "<<returnValue<<std::endl;
		std::ifstream Sfile((workDir+"-S").c_str());
		if(!Sfile)
			throw std::runtime_error("Cannot open results file from SVD\n");
		std::string line;
		std::getline(Sfile,line);
		resultsDimensions = atoi(line.c_str());
		Sfile.close();
		if(resultsDimensions>dimensions)
			throw std::runtime_error("Unknown error - resultsDimensions>dimensions");
		if(resultsDimensions<dimensions)
			iterations+=cfg->iterations;
	}
}

Matrices::CRSMatrixPtr SVDRunner::multiplyMatrices(Matrices::CRSMatrixPtr matrix1, Matrices::CRSMatrixPtr matrix2)
{
	Matrices::CRSMatrixPtr outputMatrix(new Matrices::CRSMatrix("multiplied_matrix",matrix1->NumberOfRows(),matrix2->NumberOfColumns()));
	for(size_t row=0;row<outputMatrix->NumberOfRows();row++)
	{
		for(size_t col=0;col<outputMatrix->NumberOfColumns();col++)
		{
			double sum=0;
			for(size_t iterator=0;iterator<matrix1->NumberOfColumns();iterator++)
				sum+=matrix1->GetCell(row,iterator)*matrix2->GetCell(iterator,col);
			outputMatrix->SetCell(sum,row,col);
		}
		std::cout<<"Multiplying matrices "<<matrix1->GetName()<<" x "<<matrix2->GetName()<<"...row "<<row<<" / "<<outputMatrix->NumberOfRows()<<std::endl;
	}
	return outputMatrix;

}
Matrices::CRSMatrixPtr SVDRunner::multiplications()
{
	std::cout<<"Multiplying matrices U x S..\n";
	Matrices::CRSMatrixPtr UxS = multiplyMatrices(UCRS,SCRS);
	UCRS.reset();
	SCRS.reset();
	return UxS;
}

void SVDRunner::loadMatrices()
{
	std::string line;
	std::vector<std::string> tokens;
	std::ifstream Ufile((workDir+"-Ut").c_str());
	if(!Ufile)
		throw std::runtime_error("Cannot open results file from SVD\n");
	std::getline(Ufile,line);
	boost::split(tokens,line,boost::is_any_of(" "));
	size_t URows = atoi(tokens[1].c_str());
	size_t UCols = atoi(tokens[0].c_str());
	UCRS = Matrices::CRSMatrixPtr(new Matrices::CRSMatrix("U matrix",URows,UCols));
	for(size_t i=0;i<UCols;i++)
	{
		if(!(std::getline(Ufile,line)))
			throw std::runtime_error("Unexpected end of -Ut file\n");
		boost::split(tokens,line,boost::is_any_of(" "));
		if(tokens.size()!=URows)
			throw std::runtime_error("Invalid number of columns in -Ut file");
		for(size_t j=0;j<URows;j++)
			UCRS->SetCell(atof(tokens[j].c_str()),j,i);
	}
	Ufile.close();
	std::ifstream Sfile((workDir+"-S").c_str());
	if(!Sfile)
		throw std::runtime_error("Cannot open results file from SVD\n");

	std::getline(Sfile,line);
	int Sdimensions = atoi(line.c_str());
	SCRS = Matrices::CRSMatrixPtr(new Matrices::CRSMatrix("S matrix",Sdimensions,Sdimensions));
	for(int i=0;i<Sdimensions;i++)
	{
		if(!(std::getline(Sfile,line)))
			throw std::runtime_error("Unexpected end of -S file\n");
		SCRS->SetCell(atof(line.c_str()),i,i);
	}
	Sfile.close();

}

void SVDRunner::Start()
{
	getDesiredDimensions();
	loadMatrices();
	Matrices::CRSMatrixPtr outputMatrix = multiplications();
	Matrices::IO::WriteToSingleFile(outputMatrix,output);

}
