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

wbst_optimizer::wbst_optimizer()
{
}

int wbst_optimizer::getIntAllel(Individual& i, size_t chromNum, size_t allelNum)
{
	return (dynamic_cast< std::vector< int >& >(i [chromNum]))[allelNum];
}
double wbst_optimizer::getDoubleAllel(Individual& i, size_t chromNum, size_t allelNum)
{
	return (dynamic_cast< std::vector< double >& >(i [chromNum]))[allelNum];
}
bool wbst_optimizer::getBoolAllel(Individual& i, size_t chromNum, size_t allelNum)
{
	if((dynamic_cast< std::vector< bool >& >(i [chromNum]))[allelNum]==1)
		return true;
	else
		return false;

}

int wbst_optimizer::getIntAllel(Population *pop, size_t indivNum, size_t chromNum, size_t allelNum)
{
	return (dynamic_cast< std::vector< int >& >((*pop)[indivNum][chromNum]))[allelNum];
}
double wbst_optimizer::getDoubleAllel(Population *pop, size_t indivNum, size_t chromNum, size_t allelNum)
{
	return (dynamic_cast< std::vector< double >& >((*pop)[indivNum][chromNum]))[allelNum];
}
bool wbst_optimizer::getBoolAllel(Population *pop, size_t indivNum, size_t chromNum, size_t allelNum)
{
	if((dynamic_cast< std::vector< bool >& >((*pop)[indivNum][chromNum]))[allelNum]==1)
		return true;
	else
		return false;

}
FoldingInterface* wbst_optimizer::createFoldingClass()
{
	Config* cfg = Config::getInstance();
	std::string method = cfg->foldingMethod;
	if(method.compare("n-fold")==0)
		return new NFold();
	else
		throw std::runtime_error("Invalid folding method!\n");
}
std::vector<std::string> wbst_optimizer::loadWBSTFile(std::string filename)
{

	std::ifstream file(filename.c_str() );
	if(!file)
		throw std::runtime_error( "Cannot open wbst file: " + filename);
	std::string line;
	std::getline(file, line);//first line is only a header
	WBSTFileHeader= line;

	std::vector<std::string> results;

	while(std::getline(file,line) )
	{
		boost::trim(line);
		if(line.length() == 0 )
			continue;

		results.push_back( line );
	}
	return results;
}
std::string wbst_optimizer::loadFunctions(std::string filename)
{

	Config* c = Config::getInstance();
	std::ifstream file(filename.c_str() );
	if(!file)
		throw std::runtime_error( "Cannot open functions file: " + filename);

	std::string baseFileName = c->workdir+"oldFunctions.txt";
	std::string tempFileName = c->workdir+"tempFunctions.txt";
	std::ofstream oldFile(baseFileName.c_str());

	oldFile<<" \n";
	oldFile.close();



	std::ifstream tempOldFile(baseFileName.c_str());
	std::ofstream tempNewFile;
	std::string fileLine;

	std::string line;
	std::vector<std::string> tokens;

	bool goFlag;

	while(std::getline(file,line))
	{
		boost::split(tokens,line,boost::is_any_of(";"));
		if(tokens[0].compare("syntax")==0)
		{
			tempNewFile.open(tempFileName.c_str());

			while(std::getline(tempOldFile,fileLine))
			{
				tempNewFile<<fileLine<<tokens[1]<<std::endl;
			}
			tempOldFile.close();
			tempNewFile.close();

			boost::filesystem::remove(baseFileName);
			boost::filesystem::rename(tempFileName,baseFileName);
			tempOldFile.open(baseFileName.c_str());
		}
		else if(tokens[0].compare("string")==0)
		{
			//Possible string string:
			//string;val1;val2;val3
			std::vector<std::string> stringValues;
			for(size_t i=1;i<tokens.size();i++)
				stringValues.push_back(tokens[i]);
			tempNewFile.open(tempFileName.c_str());

			while(std::getline(tempOldFile,fileLine))
			{
				for(size_t j=0;j<stringValues.size();j++)
					tempNewFile<<fileLine<<stringValues[j]<<std::endl;
			}
			tempOldFile.close();
			tempNewFile.close();

			boost::filesystem::remove(baseFileName);
			boost::filesystem::rename(tempFileName,baseFileName);
			tempOldFile.open(baseFileName.c_str());
		}
		else if(tokens[0].compare("bool")==0)
		{
			//Possible bool strings:
			//bool
			//bool;true;false
			//bool;true
			//bool;false;req;rank=12212
			//bool;true;false;req;ranking=true;someNumber=122212312;somethingElse=3123
			std::vector<std::string> boolValues;
			if(tokens.size()==1)
			{
				boolValues.push_back("true");
				boolValues.push_back("false");
			}
			else
			{
				boolValues.push_back(tokens[1]);
				if(tokens.size()>2 && tokens[2].compare("req")!=0)
					boolValues.push_back(tokens[2]);
			}

			tempNewFile.open(tempFileName.c_str());


			while(std::getline(tempOldFile,fileLine))
			{
				goFlag=true;
				for(int j=0;j<(int)tokens.size()-(2+(int)boolValues.size());j++)
				{
					if(fileLine.find(tokens[2+(int)boolValues.size()+j],0)==std::string::npos)
						goFlag=false;
				}
				if(goFlag)
					for(int j=0;j<(int)boolValues.size();j++)
						tempNewFile<<fileLine<<boolValues[j]<<std::endl;
				else
					tempNewFile<<fileLine<<std::endl;


			}
			tempOldFile.close();
			tempNewFile.close();

			boost::filesystem::remove(baseFileName);
			boost::filesystem::rename(tempFileName,baseFileName);
			tempOldFile.open(baseFileName.c_str());

		}
		else if(tokens[0].compare("int")==0)
		{
			//Possible strings:
			//int;1;1000;10
			//int;{1,5,7,12,123};ranking=false

			bool stepping=false;
			std::vector<int> intValues;

			if(tokens[1].find('{',0)==std::string::npos)
			{
				if(tokens.size()<4)
					throw std::runtime_error("Too few arguments in functions file in line: " +line);
				int startValue=atoi(tokens[1].c_str());
				int endValue=atoi(tokens[2].c_str());
				int step=atoi(tokens[3].c_str());
				stepping=true;
				for(int i=startValue;i<=endValue;i+=step)
					intValues.push_back(i);
			}
			else
			{
				std::string values=tokens[1];
				values=values.substr(1,std::string::npos);
				values=values.substr(0,values.length()-1);
				std::vector<std::string> intValuesInStrings;
				boost::split(intValuesInStrings,values,boost::is_any_of(","));
				for(int i=0;i<(int)intValuesInStrings.size();i++)
					intValues.push_back(atoi(intValuesInStrings[i].c_str()));
			}
			tempNewFile.open(tempFileName.c_str());
			int skipped;
			if(stepping)
				skipped=4;
			else
				skipped=2;

			while(std::getline(tempOldFile,fileLine))
			{
				goFlag=true;
				for(int j=0;j<(int)tokens.size()-skipped;j++)
				{
					if(fileLine.find(tokens[skipped+j],0)==std::string::npos)
						goFlag=false;
				}

				if(goFlag)
					for(int j=0;j<(int)intValues.size();j++)
					{

					tempNewFile<<fileLine<<intValues[j]<<std::endl;

				}
				else
					tempNewFile<<fileLine<<std::endl;

			}
			tempOldFile.close();
			tempNewFile.close();

			boost::filesystem::remove(baseFileName);
			boost::filesystem::rename(tempFileName,baseFileName);
			tempOldFile.open(baseFileName.c_str());

		}
		else if(tokens[0].compare("double")==0)
		{
			//Possible strings:
			//double;1;1000;100.5
			//double;{1,5,7,12,123};ranking=false

			bool stepping=false;
			std::vector<double> doubleValues;

			if(tokens[1].find('{',0)==std::string::npos)
			{
				if(tokens.size()<4)
					throw std::runtime_error("Too few arguments in functions file in line: " +line);
				double startValue=atof(tokens[1].c_str());
				double endValue=atof(tokens[2].c_str());
				double step=atof(tokens[3].c_str());
				stepping=true;
				for(double i=startValue;i<=endValue;i+=step)
					doubleValues.push_back(i);
			}
			else
			{
				std::string values=tokens[1];
				values=values.substr(1,std::string::npos);
				values=values.substr(0,values.length()-1);
				std::vector<std::string> doubleValuesInStrings;
				boost::split(doubleValuesInStrings,values,boost::is_any_of(","));
				for(int i=0;i<(int)doubleValuesInStrings.size();i++)
					doubleValues.push_back(atof(doubleValuesInStrings[i].c_str()));
			}
			tempNewFile.open(tempFileName.c_str());
			int skipped;
			if(stepping)
				skipped=4;
			else
				skipped=2;

			while(std::getline(tempOldFile,fileLine))
			{
				goFlag=true;
				for(int j=0;j<(int)tokens.size()-skipped;j++)
				{
					if(fileLine.find(tokens[skipped+j],0)==std::string::npos)
						goFlag=false;
				}

				if(goFlag)
				{
					for(int j=0;j<(int)doubleValues.size();j++)
					{

						tempNewFile<<fileLine<<doubleValues[j]<<std::endl;
					}
				}
				else
					tempNewFile<<fileLine<<std::endl;


			}
			tempOldFile.close();
			tempNewFile.close();

			boost::filesystem::remove(baseFileName);
			boost::filesystem::rename(tempFileName,baseFileName);
			tempOldFile.open(baseFileName.c_str());

		}
		else
		{
			std::cerr<<"Caught line: "<<line<<std::endl;
			throw std::runtime_error("Invalid argument in functions file");
		}

	}

	//	tempNewFile.open(tempFileName.c_str());
	//	while(std::getline(tempOldFile,fileLine))
	//	{
	//		tempNewFile<<functionName<<"("<<fileLine.substr(0,fileLine.length()-1)<<")"<<std::endl;
	//	}
	//
	//	tempOldFile.close();
	//	tempNewFile.close();
	//	boost::filesystem::remove(baseFileName);
	//	boost::filesystem::rename(tempFileName,baseFileName);

	return baseFileName;

}

wbst_optimizer::~wbst_optimizer()
{
	for(size_t i=0;i<varBoundValues.size();i++)
		delete varBoundValues[i];
}

void wbst_optimizer::initPopulation(std::string filename)
{
	Config* c = Config::getInstance();
	std::ifstream file(filename.c_str() );
	if(!file)
		throw std::runtime_error( "Cannot open functions file: " + filename);

	std::string line;
	std::vector<std::string> tokens;
	while(std::getline(file,line))
	{
		boost::split(tokens,line,boost::is_any_of(";"));
		varTypes.push_back(tokens[0]);
		if(tokens[0].compare("syntax")==0)
		{
			std::vector<std::string>*values = new std::vector<std::string>;
			values->push_back(tokens[1]);
			varBoundValues.push_back(values);
		}
		else if(tokens[0].compare("string")==0)
		{
			std::vector<std::string>*values = new std::vector<std::string>;
			std::stringstream ss;
			ss<<tokens.size()-1;
			values->push_back(ss.str());
			for(size_t i=1;i<tokens.size();i++)
				values->push_back(tokens[i]);
			varBoundValues.push_back(values);

			ChromosomeT<int>* intChrom = new ChromosomeT<int>(1);
			parentsChromosomeVector.push_back(intChrom);

			intChrom = new ChromosomeT<int>(1);
			childrenChromosomeVector.push_back(intChrom);

		}
		else if(tokens[0].compare("bool")==0)
		{
			//Possible bool strings:
			//bool;name

			ChromosomeT<bool>* boolChrom = new ChromosomeT<bool>(1);
			boolChrom->initialize();
			parentsChromosomeVector.push_back(boolChrom);

			boolChrom = new ChromosomeT<bool>(1);
			boolChrom->initialize();
			childrenChromosomeVector.push_back(boolChrom);
			std::vector<std::string> *values = new std::vector<std::string>;
			values->push_back("true");
			values->push_back("false");
			varBoundValues.push_back(values);

		}
		else if(tokens[0].compare("int")==0)
		{
			//Possible strings:
			//int;name;1;1000


			ChromosomeT<int>* intChrom = new ChromosomeT<int>(1);
			parentsChromosomeVector.push_back(intChrom);

			intChrom = new ChromosomeT<int>(1);
			childrenChromosomeVector.push_back(intChrom);
			std::vector<std::string>*values = new std::vector<std::string>;
			values->push_back(tokens[1]);
			values->push_back(tokens[2]);
			varBoundValues.push_back(values);

		}
		else if(tokens[0].compare("double")==0)
		{
			//Possible strings:
			//double;name;1;1000


			ChromosomeT<double>* doubleChrom = new ChromosomeT<double>(1);
			parentsChromosomeVector.push_back(doubleChrom);

			doubleChrom = new ChromosomeT<double>(1);
			childrenChromosomeVector.push_back(doubleChrom);
			std::vector<std::string>*values = new std::vector<std::string>;
			values->push_back(tokens[1]);
			values->push_back(tokens[2]);
			varBoundValues.push_back(values);
		}
	}
	file.close();

	parents = new Population(c->parentsPopulationSize,parentsChromosomeVector);
	children = new Population(c->childrenPopulationSize,childrenChromosomeVector);

	parents->setMaximize();
	children->setMaximize();
	//Drugie czytanie

	for(size_t varType=0, chromosomeCounter=0;varType<varTypes.size();varType++)
		//Function's name:
	{
		if(varTypes[varType].compare("string")==0)
		{
			int numOfStrings=atoi((*varBoundValues[varType])[0].c_str());
			for(size_t i = 0; i < parents->size(); ++i) {

				dynamic_cast< ChromosomeT< int >& >
						((*parents)[ i ][ chromosomeCounter ]).initialize(1,numOfStrings);
			}
			chromosomeCounter++;
		}
		else if(varTypes[varType].compare("bool")==0)
		{
			for(size_t i = 0; i < parents->size(); ++i) {

				dynamic_cast< ChromosomeT< bool >& >
						((*parents)[ i ][ chromosomeCounter ]).initialize(false);

				dynamic_cast< ChromosomeT< bool >& >
						((*parents)[ i ][ chromosomeCounter ]).flip(0.5);

			}
			chromosomeCounter++;

		}
		else if(varTypes[varType].compare("int")==0)
		{
			int startValue=atoi((*varBoundValues[varType])[0].c_str());
			int endValue=atoi((*varBoundValues[varType])[1].c_str());
			for(size_t i = 0; i < parents->size(); ++i) {

				dynamic_cast< ChromosomeT< int >& >
						((*parents)[ i ][ chromosomeCounter ]).initialize(startValue,endValue);
			}
			chromosomeCounter++;
		}
		else if(varTypes[varType].compare("double")==0)
		{
			double startValue=atof((*varBoundValues[varType])[0].c_str());
			double endValue=atof((*varBoundValues[varType])[1].c_str());
			for(size_t i = 0; i < parents->size(); ++i) {

				dynamic_cast< ChromosomeT< double >& >
						((*parents)[ i ][ chromosomeCounter ]).initialize(startValue,endValue);
			}
			chromosomeCounter++;
		}
	}
}

void wbst_optimizer::createTest(std::vector<std::string> WBSTQuestions,std::string fileName,std::vector<int> IDVector)
{

	std::ofstream outFile(fileName.c_str());
	if(!outFile)
		throw std::runtime_error( "Cannot open fold file: " + fileName);
	outFile<<WBSTFileHeader<<std::endl;
	for(std::vector<int>::iterator iter=IDVector.begin();iter!=IDVector.end();++iter)
		outFile<<WBSTQuestions[(*iter)]<<std::endl;


	outFile.close();

}
void wbst_optimizer::generateChildren()
{
	for(size_t c=0;c<children->size();c++)
	{
		Individual& mom = parents->random();
		Individual& dad = parents->random();
		double tau = 1. / sqrt(double(varTypes.size()));

		for(size_t varType=0, chromosomeCounter=0;varType<varTypes.size();varType++)
		{
			if(varTypes[varType].compare("string")==0)
			{
				ChromosomeT<int>& objvar =
						dynamic_cast< ChromosomeT< int >& >((*children)[c][chromosomeCounter]);
				objvar  .recombineDiscrete(mom[ chromosomeCounter ], dad[ chromosomeCounter ]);
				//TODO: Mutation factor
				objvar  .mutateDiffGeom((atoi((*varBoundValues[varType])[0].c_str()))*tau/100);
				objvar.cutOff(1,atoi((*varBoundValues[varType])[0].c_str()));
				chromosomeCounter++;
			}
			else if(varTypes[varType].compare("int")==0)
			{
				ChromosomeT<int>& objvar =
						dynamic_cast< ChromosomeT< int >& >((*children)[c][chromosomeCounter]);
				objvar  .recombineDiscrete(mom[ chromosomeCounter ], dad[ chromosomeCounter ]);
				//TODO: Mutation factor
				objvar  .mutateDiffGeom((atoi((*varBoundValues[varType])[1].c_str())-atoi((*varBoundValues[varType])[0].c_str()))*tau/100);
				objvar.cutOff(atoi((*varBoundValues[varType])[0].c_str()),atoi((*varBoundValues[varType])[1].c_str()));
				chromosomeCounter++;

			}
			else if(varTypes[varType].compare("double")==0)
			{
				ChromosomeT<double>& objvar =
						dynamic_cast< ChromosomeT< double >& >((*children)[c][chromosomeCounter]);
				objvar.recombineIntermediate(mom[chromosomeCounter],dad[chromosomeCounter]);

				//TODO: Mutation factor
				objvar.mutateNormal(tau/10);

				objvar.cutOff(atof((*varBoundValues[varType])[0].c_str()),atof((*varBoundValues[varType])[1].c_str()));
				chromosomeCounter++;
			}
			else if(varTypes[varType].compare("bool")==0)
			{
				ChromosomeT<bool>& objvar =
						dynamic_cast< ChromosomeT< bool >& >((*children)[c][chromosomeCounter]);
				objvar.recombineDiscrete (mom[chromosomeCounter],dad[chromosomeCounter]);

				//TODO: Mutation factor
				objvar.flip(0.5);
				chromosomeCounter++;
			}
		}
	}
}

std::string wbst_optimizer::indiv2string(Individual& i)
{
	std::stringstream ss;
	ss<<"sim=";
	for(size_t j=0,chromosomeCounter=0;j<varTypes.size();j++)
	{
		if(varTypes[j].compare("syntax")==0)
		{
			ss<<(*varBoundValues[j])[0];
		}
		else if(varTypes[j].compare("string")==0)
		{
			ss<<(*varBoundValues[j])[getIntAllel(i,chromosomeCounter,0)];
			chromosomeCounter++;
		}
		else if(varTypes[j].compare("int")==0)
		{
			ss<<getIntAllel(i,chromosomeCounter,0);
			chromosomeCounter++;
		}
		else if(varTypes[j].compare("double")==0)
		{
			ss<<getDoubleAllel(i,chromosomeCounter,0);
			chromosomeCounter++;
		}
		else if(varTypes[j].compare("bool")==0)
		{
			if(getBoolAllel(i,chromosomeCounter,0))
				ss<<"true";
			else ss<<"false";
			chromosomeCounter++;
		}
	}
	return ss.str();
}

void wbst_optimizer::evaluatePopulation(std::string foldFileName)
{
	Config* cfg = Config::getInstance();
	//Preparing wbst cfg file
	std::string wbstLearningFileName = cfg->workdir+"wbstLearning.cfg";
	std::ofstream wbstLearningFile(wbstLearningFileName.c_str());
	if(!wbstLearningFile)
		throw std::runtime_error( "Cannot create wbst config file: "+wbstLearningFileName);
	wbstLearningFile<<"wbst="<<foldFileName<<std::endl;
	for(size_t i=0;i<children->size();i++)
	{
		wbstLearningFile<<"sim=";
		for(size_t j=0,chromosomeCounter=0;j<varTypes.size();j++)
		{
			if(varTypes[j].compare("syntax")==0)
			{
				wbstLearningFile<<(*varBoundValues[j])[0];
			}
			else if(varTypes[j].compare("string")==0)
			{
				wbstLearningFile<<(*varBoundValues[j])[getIntAllel(children,i,chromosomeCounter,0)];
				chromosomeCounter++;
			}
			else if(varTypes[j].compare("int")==0)
			{
				wbstLearningFile<<getIntAllel(children,i,chromosomeCounter,0);
				chromosomeCounter++;
			}
			else if(varTypes[j].compare("double")==0)
			{
				wbstLearningFile<<getDoubleAllel(children,i,chromosomeCounter,0);
				chromosomeCounter++;
			}
			else if(varTypes[j].compare("bool")==0)
			{
				if(getBoolAllel(children,i,chromosomeCounter,0))
					wbstLearningFile<<"true";
				else wbstLearningFile<<"false";
				chromosomeCounter++;
			}
		}
		wbstLearningFile<<std::endl;
	}
	wbstLearningFile<<"outdir="<<cfg->workdir<<"learning/"<<std::endl;

	wbstLearningFile<<"matrices="<<cfg->matrix<<std::endl;

	if(cfg->flex.compare("subst")!=0)
		wbstLearningFile<<"flex="<<cfg->flex<<std::endl;
	if(cfg->loadCCS)
		wbstLearningFile<<"load-ccs=1\n";
	wbstLearningFile.close();

	//Running app
	std::stringstream ss;
	ss<<"mpirun -n "<<cfg->numOfNodes<<" --hostfile "<<cfg->hostFile<<" "<<cfg->wbstTesterPath
			<<" --config-file "<<wbstLearningFileName<<" --no-xml";
	std::string command = ss.str();
	std::cout<<"Running: "<<command<<std::endl;
	int returnValue = system(command.c_str());
	if(returnValue!=0)
		std::cout<<"Return value from system: "<<returnValue<<std::endl;

	//Reading results
	ss.str(cfg->workdir+"learning/results.csv");
	std::string resultsFileName = ss.str();

	std::ifstream resultsFile(resultsFileName.c_str());
	if(!resultsFile)
		throw std::runtime_error("Something wrong with results file:"+resultsFileName);

	std::string line;
	//header
	std::getline(resultsFile,line);
	for(size_t i=0;i<children->size();i++)
	{
		std::getline(resultsFile,line);
		std::vector<std::string> tokens;
		boost::split(tokens,line,boost::is_any_of(";"));
		if(tokens.size()!=9)
		{
			throw std::runtime_error("Invalid number of tokens in results file!");
		}
		(*children)[i].setFitness(atof(tokens[8].c_str()));
	}
}

void wbst_optimizer::startGenetic()
{
	Config* cfg = Config::getInstance();

	//Creation of tests

	if(cfg->isSeedSet)
	{
		srand(cfg->randomSeed);
		Rng::seed(cfg->randomSeed);
	}
	else
	{
		srand ( unsigned ( time (NULL) ) );
		Rng::seed(unsigned ( time (NULL) ) );
	}
	initPopulation(cfg->functionFile);

	std::string generationsFileName=cfg->workdir+"generations.txt";
	std::ofstream generations(generationsFileName.c_str());
	if(!generations)
		throw std::runtime_error("Cannot open file for generations report.\n");
	for(int i=0;i<cfg->iterations;i++)
	{

		//Generate new children
		generateChildren();

		evaluatePopulation(cfg->wbstLearningFold);

		parents->selectMuLambda(*children,5);
		std::cout<<"Iteration "<<i<<", top result: "<<children->best().fitnessValue()<<std::endl;
		generations<<"Iteration "<< i <<" top individual: "<< indiv2string(children->best())<<" with a fitness of "<<children->best().fitnessValue()<<"\n";

	}

	//Final test on testing fold
	evaluatePopulation(cfg->wbstTestingFold);
	//Parsing final results
	std::stringstream ss;
	ss.str(cfg->workdir+"learning/results.csv");
	std::string finalResultsFileName = ss.str();
	std::ifstream finalResultsFile(finalResultsFileName.c_str());
	if(!finalResultsFile)
		throw std::runtime_error("Something wrong with results file:"+finalResultsFileName);

	std::string line;
	//First line is just a header
	std::getline(finalResultsFile,line);

	int counter=0;
	double sum=0;
	double bestResult=0;
	std::string bestFunction;
	for(size_t i=0;i<children->size();i++)
	{
		std::getline(finalResultsFile,line);
		std::vector<std::string> tokens;
		boost::split(tokens,line,boost::is_any_of(";"));
		if(tokens.size()!=9)
			throw std::runtime_error("Error in results file");

		sum+= atof(tokens[8].c_str());
		if(atof(tokens[8].c_str())>bestResult)
		{
			bestResult=atof(tokens[8].c_str());
			bestFunction=tokens[2];
		}
		counter++;
	}
	//	resultsFile.close();
	double avg=sum/counter;

	std::string finalReportFileName=cfg->workdir+"finalReport.txt";
	std::ofstream finalReport(finalReportFileName.c_str());
	if(!finalReport)
		throw std::runtime_error("Cannot open file for final report.\n");
	finalReport<<"Best function: "<<bestFunction<<std::endl;
	finalReport<<"Best function's result: "<<bestResult<<std::endl;
	finalReport<<"Average result of last population: "<<avg<<std::endl;
	finalReport.close();

}
void wbst_optimizer::startBruteForce()
{
	Config* cfg = Config::getInstance();

	std::vector<std::string> WBSTQuestions = loadWBSTFile(cfg->wbstLearningFold);
	if(cfg->isSeedSet)
		srand(cfg->randomSeed);
	else
		srand ( unsigned ( time (NULL) ) );
	std::random_shuffle(WBSTQuestions.begin(),WBSTQuestions.end());

	std::string functionsFileName = loadFunctions(cfg->functionFile);
	FoldingInterface* foldingClass = createFoldingClass();
	foldingClass->init(WBSTQuestions.size(), cfg->numOfFolds);

	std::string wbstLearningFileName = cfg->workdir+"wbstLearning.cfg";
	std::ofstream wbstLearningFile(wbstLearningFileName.c_str());
	if(!wbstLearningFile)
		throw std::runtime_error( "Cannot create wbst config file: "+wbstLearningFileName);
	//Creation of tests
	std::string fileName;

	std::vector<std::vector<int> >testingVector;

	std::pair<std::vector<int>,std::vector<int> > fold;

	for(int i=0;i<cfg->numOfFolds;i++)
	{
		std::stringstream ss;
		ss<<cfg->workdir<<"fold"<<i;
		fileName=ss.str();
		fold=foldingClass->next();
		createTest(WBSTQuestions,fileName,fold.first);
		testingVector.push_back(fold.second);
		wbstLearningFile<<"wbst="<<fileName<<std::endl;
	}

	std::ifstream functionsFile(functionsFileName.c_str());
	if(!functionsFile)
		throw std::runtime_error("Cannot open file with functions!\n");
	std::string line;
	while(std::getline(functionsFile,line))
		wbstLearningFile<<"sim="<<line<<std::endl;

	functionsFile.close();

	//TODO: znalezc metode przeszukiwania stanow :D

	boost::filesystem::remove(functionsFileName);

	wbstLearningFile<<"outdir="<<cfg->workdir<<"learning/"<<std::endl;

	wbstLearningFile<<"matrices="<<cfg->matrix<<std::endl;

	if(cfg->flex.compare("subst")!=0)
		wbstLearningFile<<"flex="<<cfg->flex<<std::endl;
	if(cfg->loadCCS)
		wbstLearningFile<<"load-ccs=1\n";
	wbstLearningFile.close();
	//Running app
	std::stringstream ss;
	ss<<"mpirun -n "<<cfg->numOfNodes<<" --hostfile "<<cfg->hostFile<<" "<<cfg->wbstTesterPath
			<<" --config-file "<<wbstLearningFileName<<" --no-xml";
	std::string command = ss.str();
	std::cout<<"Running: "<<command<<std::endl;
	int returnValue = system(command.c_str());
	if(returnValue!=0)
		std::cout<<"Return value from system: "<<returnValue<<std::endl;
	
	//Reading results
	ss.str(cfg->workdir+"learning/results.csv");
	std::string resultsFileName = ss.str();

	std::ifstream resultsFile(resultsFileName.c_str());
	if(!resultsFile)
		throw std::runtime_error("Something wrong with results file:"+resultsFileName);


	std::vector<std::pair<std::string,double> > bestResults;
	for(int i=0;i<cfg->numOfFolds;i++)
		bestResults.push_back(std::pair<std::string,double>("",-1));

	//First line is just a header
	std::getline(resultsFile,line);

	std::vector<std::string> tokens;
	int foldNum;
	double results;
	std::string function;
	while(std::getline(resultsFile,line) && line.length()>0)
	{
		boost::split(tokens,line,boost::is_any_of(";"));
		if(tokens.size()!=9)
		{
			std::cerr<<"Caught line: "<<line<<std::endl;
			throw std::runtime_error("Error in results file");
		}
		foldNum = atoi(tokens[3].c_str());
		results = atof(tokens[8].c_str());
		function=tokens[2];

		if(bestResults[foldNum].second < results)
		{
			bestResults[foldNum].first = function;
			bestResults[foldNum].second = results;
		}
	}

	std::string wbstTestingFileName = cfg->workdir+"wbstTesting.cfg";
	std::ofstream wbstTestingFile(wbstTestingFileName.c_str());
	if(!wbstTestingFile)
		throw std::runtime_error( "Cannot create wbst config file: "+wbstTestingFileName);

	for(int i=0;i<cfg->numOfFolds;i++)
	{
		std::stringstream ss;
		ss<<cfg->workdir<<"testFold"<<i;
		fileName=ss.str();

		createTest(WBSTQuestions,fileName,testingVector[i]);

		wbstTestingFile<<"wbst="<<fileName<<std::endl;
		wbstTestingFile<<"sim="<<bestResults[i].first<<std::endl;
	}
	wbstTestingFile<<"matrices="<<cfg->matrix<<std::endl;
	wbstTestingFile<<"outdir="<<cfg->workdir<<"testing/"<<std::endl;


	wbstTestingFile.close();

	ss.str();
	ss<<"mpirun -n "<<cfg->numOfNodes<<" --hostfile "<<cfg->hostFile<<" "<<cfg->wbstTesterPath
			<<" --config-file "<<wbstTestingFileName<<" --no-loop"<<" --no-xml";
	returnValue = system(ss.str().c_str());
	if(returnValue!=0)
		std::cout<<"Return value from system: "<<returnValue<<std::endl;

	//Parsing final results
	ss.str(cfg->workdir+"learning/results.csv");
	std::string finalResultsFileName = ss.str();
	std::ifstream finalResultsFile(finalResultsFileName.c_str());
	if(!finalResultsFile)
		throw std::runtime_error("Something wrong with results file:"+finalResultsFileName);



	//First line is just a header
	std::getline(finalResultsFile,line);

	int counter=0;
	double sum=0;
	while(std::getline(finalResultsFile,line) && line.length()>0)
	{
		boost::split(tokens,line,boost::is_any_of(";"));
		if(tokens.size()!=9)
		{
			std::cerr<<"Caught line: "<<line<<std::endl;
			throw std::runtime_error("Error in results file");
		}

		sum+= atof(tokens[8].c_str());
		counter++;
	}
	resultsFile.close();
	double avg=sum/counter;

	std::string finalReportFileName=cfg->workdir+"finalReport.txt";
	std::ofstream finalReport(finalReportFileName.c_str());
	if(!finalReport)
		throw std::runtime_error("Cannot open file for final report.\n");

	finalReport<<"Average result: "<<avg<<std::endl;
	finalReport.close();



	delete foldingClass;
}
