/*
 * Crossover.cpp
 *
 *  Created on: Jul 27, 2015
 *      Author: jjpalacios
 */

#include "./Crossover.h"

namespace clbDEB {

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

//-----  Setup method  --------------------------------------------------------
void Crossover::setup(ParameterDB *parameters) {
	this->probability = parameters->getDouble(this->P_PROB);
	if(this->probability < 0 || this->probability > 1) {
		std::string err = "Invalid crossover probability.";
		err += " Incorrect value or missing parameter.";
		throw ClbException(err);
	}
}



//=============================================================================
//		METHODS
//=============================================================================
//-----  apply (Population)  --------------------------------------------------
void Crossover::apply(Population *population, const SharedVars *svars) const {
	for(int i=0; i < population->size(); i+=2) {
		// If there are two parents, generate two offspring
		if((i+1) < population->size()) {
			// Cross the individuals to generate two offspring
			this->apply(population->individual(i),
					population->individual(i+1), svars);

		}

		// If there is only one parent, keep it
	}
}





//=============================================================================
//
//	Class BlxCrossover
//
//=============================================================================
//=============================================================================
//		CONSTRUCTORS / INITIALIZERS
//=============================================================================
//-----  Main constructor  ----------------------------------------------------
BlxCrossover::BlxCrossover(ParameterDB *parameters) : Crossover() {
	this->P_ALPHA = CROSSOVER_ALPHA;
	this->alpha = -1.0;
	if(parameters != NULL)
		this->setup(parameters);
}

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

	// Load own parameters
	this->alpha = parameters->getDouble(this->P_ALPHA, -1.0);
	if(this->alpha < 0 || this->alpha > 1) {
		std::string err = "Invalid BLX-alpha value. ";
		err += "Incorrect value or missing parameter.";
		throw ClbException(err);
	}
}



//=============================================================================
//		METHODS
//=============================================================================
//-----  Apply (Individuals)  -------------------------------------------------
void BlxCrossover::apply(Individual *ind1,
			Individual *ind2, const SharedVars *svars) const {

	// Check if the individual is an array of integer values
	if(ind1->getType() == INTEGER_INDIVIDUAL) {
		IntegerIndividual *intInd1 = dynamic_cast<IntegerIndividual *>(ind1);
		IntegerIndividual *intInd2 = dynamic_cast<IntegerIndividual *>(ind2);
		return applyInteger(intInd1, intInd2, svars);
	}

	// If not, it must be made of double values
	DoubleIndividual *doubleInd1 = dynamic_cast<DoubleIndividual *>(ind1);
	DoubleIndividual *doubleInd2 = dynamic_cast<DoubleIndividual *>(ind2);
	return applyDouble(doubleInd1, doubleInd2, svars);
}



//-----  ApplyDouble  --------------------------------------------------------
void BlxCrossover::applyDouble(DoubleIndividual *ind1, DoubleIndividual *ind2,
		const SharedVars *svars) const {

	for(int i=0; i < ind1->length(); i++) {
		double min = std::min(ind1->gene(i), ind2->gene(i));
		double max = std::max(ind1->gene(i), ind2->gene(i));

		// Compute the bounds of the interval
		double minInterval = min-alpha*(max-min);
		double maxInterval = max+alpha*(max-min);

		// Generate random values in the interval
		ind1->setGene(i, svars->rng->getDouble(minInterval, maxInterval));
		ind2->setGene(i, svars->rng->getDouble(minInterval, maxInterval));

		// Check that the constraints hold
		if(ind1->gene(i) < svars->minGene[i])
			ind1->setGene(i, svars->minGene[i]);
		if(ind1->gene(i) > svars->maxGene[i])
			ind1->setGene(i, svars->maxGene[i]);
		if(ind2->gene(i) < svars->minGene[i])
			ind2->setGene(i, svars->minGene[i]);
		if(ind2->gene(i) > svars->maxGene[i])
			ind2->setGene(i, svars->maxGene[i]);
	}
}


//-----  ApplyInteger  --------------------------------------------------------
void BlxCrossover::applyInteger(IntegerIndividual *ind1,
		IntegerIndividual *ind2, const SharedVars *svars) const {

	for(int i=0; i < ind1->length(); i++) {
		double min = std::min(ind1->gene(i), ind2->gene(i));
		double max = std::max(ind1->gene(i), ind2->gene(i));

		// Compute the bounds of the interval
		int intMin = std::ceil(min-alpha*(max-min));
		int intMax = std::floor(max+alpha*(max-min));

		// Generate random values in the interval
		ind1->setGene(i, svars->rng->getInteger(intMin, intMax));
		ind2->setGene(i, svars->rng->getInteger(intMin, intMax));

		// Check that the constraints hold
		if(ind1->gene(i) < (int)svars->minGene[i])
			ind1->setGene(i, (int)svars->minGene[i]);
		if(ind1->gene(i) > (int)svars->maxGene[i])
			ind1->setGene(i, (int)svars->maxGene[i]);
		if(ind2->gene(i) < (int)svars->minGene[i])
			ind2->setGene(i, (int)svars->minGene[i]);
		if(ind2->gene(i) > (int)svars->maxGene[i])
			ind2->setGene(i, (int)svars->maxGene[i]);
	}
}


}



