/*
 * Population.cpp
 *
 *  Created on: Jul 24, 2015
 *      Author: jjpalacios
 */

#include "./Population.h"

namespace clbDEB {

//=============================================================================
//		CONSTRUCTORS / INITIALIZERS
//=============================================================================
//-----  Copy constructor  ----------------------------------------------------
Population::Population(const Population &source) {
	this->sorted = source.sorted;
	this->individuals.resize(source.size());
	this->order.resize(source.size());

	// Clone all the individuals from the source population
	for(int i=0; i < source.size(); i++) {
		this->individuals.push_back(source.individual(i)->clone());
		this->order[i] = source.order[i];
	}
}


//-----  Destructor  ----------------------------------------------------------
Population::~Population() {
	for(int i=0; i < (int)this->individuals.size(); i++)
		delete this->individuals[i];
}


//-----  Clone  ---------------------------------------------------------------
Population* Population::clone() const {
	return new Population(*this);
}


//-----  Clear  ---------------------------------------------------------------
void Population::clear() {
	for(int i=0; i < (int)individuals.size(); i++)
		delete this->individuals[i];
	this->individuals.clear();
	this->order.clear();
	this->sorted = true;
}



//=============================================================================
//		GET/SET METHODS
//=============================================================================
//-----  individual  ----------------------------------------------------------
Individual * Population::individual(const int position) const {
	if(position < 0 || position >= (int)this->individuals.size()) {
		std::string errorMsg = "Attempt to access to a non-existing ";
		errorMsg += "position in the population";
		throw ClbException(errorMsg);
	}
	return this->individuals[position];
}


//-----  setFitness  ----------------------------------------------------------
void Population::setFitness(const int position, double fitness) {
	if(position < 0 || position >= (int)this->individuals.size()) {
		std::string errorMsg = "Attempt to access to a non-existing ";
		errorMsg += "position in the population";
		throw ClbException(errorMsg);
	}
	this->individuals[position]->setFitness(fitness);
	this->sorted = false;
}


//-----  getFitness  ----------------------------------------------------------
double Population::getFitness(const int position) const {
	if(position < 0 || position >= (int)this->individuals.size()) {
		std::string errorMsg = "Attempt to access to a non-existing ";
		errorMsg += "position in the population";
		throw ClbException(errorMsg);
	}
	return this->individuals[position]->fitness;
}


//-----  getBest  -------------------------------------------------------------
Individual * Population::getBest(const int k, const SharedVars *svars) {
	if(k < 0 || k >= (int)this->individuals.size()) {
		std::string errorMsg = "Attempt to access to a non-existing ";
		errorMsg += "position in the population";
		throw ClbException(errorMsg);
	}

	if(!(this->sorted)) {
		this->sort(svars);
	}

	return this->individuals[order[k]];
}


//-----  whoIsBest  -----------------------------------------------------------
int Population::whoIsBest(const int k, const SharedVars *svars) {
	if(k < 0 || k >= (int)this->individuals.size()) {
			std::string errorMsg = "Attempt to access to a non-existing ";
			errorMsg += "position in the population";
			throw ClbException(errorMsg);
		}

		if(!(this->sorted)) {
			this->sort(svars);
		}

		return this->order[k];
}


//-----  getAverage  ----------------------------------------------------------
double Population::getAverage() const {
	double sum=0.0;
	for(int i=0; i < (int)this->individuals.size(); i++) {
		if(!individuals[i]->evaluated)
			return -1.0;
		sum += individuals[i]->fitness;
	}
	return sum / (int)this->individuals.size();
}



//=============================================================================
//		METHODS
//=============================================================================
//-----  addIndividual  -------------------------------------------------------
void Population::addIndividual(Individual *individual) {
	this->individuals.push_back(individual);
	order.push_back((int)(this->individuals.size())-1);
	sorted = false;
}


//-----  replaceIndividual  ----------------------------------------------------
Individual * Population::replaceIndividual(
		const int position, Individual *individual) {
	if(position < 0 || position >= (int)this->individuals.size()) {
		std::string errorMsg = "Attempt to access to a non-existing ";
		errorMsg += "position in the population";
		throw ClbException(errorMsg);
	}

	Individual *old = this->individuals[position];
	this->individuals[position] = individual;
	this->sorted = false;
	return old;
}


//-----  sort  ----------------------------------------------------------------
void Population::sort(const SharedVars *svars, const bool force) {
	if(this->sorted && !force) return;
	this->quickSort(0, (int)this->individuals.size()-1, svars);
	this->sorted = true;
}


//-----  quickSort  -----------------------------------------------------------
void Population::quickSort(const int left, const int right,
		const SharedVars *svars) {
	int pivot, pos;

	if (left >= right)
		return;

	pivot = svars->rng->getInteger(left, right);

	std::swap(order[pivot], order[right]);
	pos = left;
	for (int i = left; i < right; i++) {
		if(svars->hasBetterFitness(individuals[order[i]]->fitness,
				individuals[order[right]]->fitness)) {
			std::swap(order[i], order[pos]);
			pos++;
		}
	}

	std::swap(order[pos], order[right]);
	this->quickSort(left, pos - 1, svars);
	this->quickSort(pos + 1, right, svars);
}

}

