/*
 * Replacement.cpp
 *
 *  Created on: Jul 28, 2015
 *      Author: jjpalacios
 */


#include "./Replacement.h"

namespace clbDEB {

//=============================================================================
//
//	Abstract class Replacement
//
//=============================================================================
//=============================================================================
//		CONSTRUCTORS / INITIALIZERS
//=============================================================================
//-----  Main constructor  ----------------------------------------------------
Replacement::Replacement(ParameterDB * parameters) {
	if(parameters != NULL)
		this->setup(parameters);
}



//=============================================================================
//
//	Class ElitistReplacement
//
//=============================================================================
//=============================================================================
//		CONSTRUCTORS / INITIALIZERS
//=============================================================================
//-----  Main constructor  ----------------------------------------------------
ElitistReplacement::ElitistReplacement(ParameterDB * parameters)
: Replacement() {
	this->P_ELITE = REPLACE_ELITE;
	this->elite = -1;
	if(parameters != NULL)
		this->setup(parameters);
}

//-----  Setup method  --------------------------------------------------------
void ElitistReplacement::setup(ParameterDB * parameters) {
	// Load the parameters of your parent
	Replacement::setup(parameters);

	// Load own parameters
	this->elite = parameters->getInteger(this->P_ELITE, 0);
}


//=============================================================================
//		METHODS
//=============================================================================
//-----  apply  ---------------------------------------------------------------
void ElitistReplacement::apply(Population *oldPopulation,
		Population *newPopulation, const SharedVars *svars) const {

	/* Look for the elite worst individuals in the new population.
	 * This will save time sorting the population each time we replace an
	 * individual.
	 */
	int popSize = newPopulation->size();
	Individual *replaced;
	std::vector<int> worst;
	for(int i=0; i < this->elite; i++)
		worst.push_back(newPopulation->whoIsBest(popSize-i-1, svars));

	// Move the best individuals of the old population to the new one
	Individual *keep;
	for(int i=0; i < this->elite; i++) {
		keep = oldPopulation->getBest(i, svars);
		replaced = newPopulation->replaceIndividual(
				worst[i],	// Pick worst
				keep->clone());
		delete replaced;
	}
	delete oldPopulation;
}





//=============================================================================
//
//	Class ClearingReplacement
//
//=============================================================================
//=============================================================================
//		CONSTRUCTORS / INITIALIZERS
//=============================================================================
//-----  Main constructor  ----------------------------------------------------
ClearingReplacement::ClearingReplacement(ParameterDB * parameters)
: Replacement() {
	this->P_CAPACITY = REPLACE_NICHE_CAPACITY;
	this->P_RADIUS = REPLACE_NICHE_RADIUS;
	this->nicheCapacity = 1;
	this->nicheRadius = 0;
	if(parameters != NULL)
		this->setup(parameters);
}

//-----  Setup method  --------------------------------------------------------
void ClearingReplacement::setup(ParameterDB * parameters) {
	// Load the parameters of your parent
	Replacement::setup(parameters);

	// Load own parameters
	this->nicheCapacity = parameters->getInteger(this->P_CAPACITY, 1);
	this->nicheRadius = parameters->getDouble(this->P_RADIUS, 0);
}


//=============================================================================
//		METHODS
//=============================================================================
//-----  apply  ---------------------------------------------------------------
void ClearingReplacement::apply(Population *oldPopulation,
		Population *newPopulation, const SharedVars *svars) const {

	// Join both populations into one.
	int popSize = newPopulation->size();
	int badInd = 0;
	for(int i=0; i < newPopulation->size(); i++) {
		while(badInd < oldPopulation->size() &&
				!svars->isPenalty(oldPopulation->individual(badInd)->fitness))
			badInd++;

		if(badInd < oldPopulation->size())
			oldPopulation->replaceIndividual(badInd, newPopulation->individual(i));
		else
			oldPopulation->addIndividual(newPopulation->individual(i));
	}

	// Apply the clearing
	int nbWinners, nNiches;
	Individual *ind_i, *ind_j;
	double distance;

	nNiches = 0;
	for(int i=0; i < oldPopulation->size(); i++) {
		ind_i = oldPopulation->getBest(i, svars);
		if(!(svars->isPenalty(ind_i->fitness))) {
			nbWinners = 1;
			nNiches++;

			// Look for individuals in the same niche
			for(int j=i+1; j < oldPopulation->size(); j++) {
				ind_j = oldPopulation->getBest(j, svars);
				if(!(svars->isPenalty(ind_j->fitness))) {
					if(ind_j->getType() == INTEGER_INDIVIDUAL)
						distance = euclideanDistance(
							svars,
							dynamic_cast<IntegerIndividual *>(ind_i),
							dynamic_cast<IntegerIndividual *>(ind_j)
						);
					else if(ind_j->getType() == DOUBLE_INDIVIDUAL)
						distance = euclideanDistance(
							svars,
							dynamic_cast<DoubleIndividual *>(ind_i),
							dynamic_cast<DoubleIndividual *>(ind_j)
						);
					if(distance < this->nicheRadius) {
						if(nbWinners < this->nicheCapacity)
							nbWinners++;
						else
							oldPopulation->individual(
								oldPopulation->whoIsBest(j, svars))
								->fitness = svars->penaltyFitness();
					}
				}
			}
		}
	}

	// Remove the extra individuals
	oldPopulation->sort(svars, true);
	for(int i=0; i < popSize; i++) {
		ind_i = oldPopulation->getBest(i, svars)->clone();
		if(svars->isPenalty(ind_i->fitness))
			ind_i->evaluated = false;
		newPopulation->replaceIndividual(i, ind_i);
	}
	delete oldPopulation;
}

}

