package ec.gp.semantic.select;

import ec.*;
import ec.gp.*;
import ec.gp.semantic.*;
import ec.gp.semantic.func.*;
import ec.select.*;
import ec.util.*;

/**
 * Geometric Semantic Tournament randomly picks n individuals from population, then selects the best of them, next
 * calculates the optimal opposite semantics (in the opposite side of the target), finally it chooses the most similar
 * individual from the previously picked set of individuals. It prohibits the chosen parents to be semantically equal.
 * 
 * @author Tomasz Pawlak
 */
public class GST extends TournamentSelection {

	private static final Parameter DEFAULT_BASE = new Parameter("GST");
	private static final String SENSITIVITY = "sensitivity";

	protected EvolutionState state;
	protected double sensitivity = 1E-6;
	protected ISemantics target;
	private int nextChosenInd = -1;

	@Override
	public Parameter defaultBase() {
		return DEFAULT_BASE;
	}

	@Override
	public void setup(EvolutionState state, Parameter base) {
		super.setup(state, base);

		this.state = state;
		this.target = ((ISemanticProblem) state.evaluator.p_problem).getTargetSemantics();
		this.sensitivity = state.parameters.getDouble(base.push(SENSITIVITY), defaultBase().push(SENSITIVITY));
	}

	@Override
	public int typicalIndsProduced() {
		return 2; // this method is designed to return two individuals
	}

	@Override
	public int produce(int subpopulation, EvolutionState state, int thread) {
		if (nextChosenInd >= 0) {
			int id = nextChosenInd;
			nextChosenInd = -1;
			return id;
		}

		// pick size random individuals, then pick the best.
		Individual[] oldinds = state.population.subpops[subpopulation].individuals;

		int s = getTournamentSizeToUse(state.random[thread]);
		double bestDistance = pickWorst ? 0 : Double.MAX_VALUE;
		int firstParent = super.produce(subpopulation, state, thread);
		ISemantics firstParentSem = ((SimpleNodeBase<?>) ((GPIndividual) oldinds[firstParent]).trees[0].child).getSemantics();
		int best = -1;
		int candidate;
		double distance;
		ISemantics desired2ndParentSemantics;

		desired2ndParentSemantics = target.counterpointTo(firstParentSem);

		for (int i = 0; i < s; ++i) {
			candidate = getRandomIndividual(i, subpopulation, state, thread);

			ISemantics semI = ((SimpleNodeBase<?>) ((GPIndividual) oldinds[candidate]).trees[0].child).getSemantics();
			distance = semI.fastDistanceTo(desired2ndParentSemantics);

			if (!(semI.distanceTo(firstParentSem) >= this.sensitivity)) {
				continue;
			}

			if (Double.isInfinite(distance) || Double.isNaN(distance)) {
				// it is slightly less than maximum value Double.MAX_VALUE; we must use this number due to comparison few lines below
				distance = 1E308;
			}

			if (pickWorst) {
				if (distance > bestDistance) {
					bestDistance = distance;
					best = candidate;
				}
			} else {
				if (distance < bestDistance) {
					bestDistance = distance;
					best = candidate;
				}
			}
		}

		if (best >= 0) {
			this.nextChosenInd = best;
		}
		return firstParent;
	}

}
