package ac.essex.gp.genetics;

import ac.essex.gp.individuals.Individual;
import ac.essex.gp.params.GPParams;
import ac.essex.gp.util.DeepCopy;
import ac.essex.gp.util.DataStack;
import ac.essex.gp.problems.Problem;

import java.util.Vector;

/**
 * <p/>
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version,
 * provided that any use properly credits the author.
 * 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
 * GNU General Public License for more details at http://www.gnu.org
 * </p>
 *
 * @author Olly Oechsle, University of Essex, Date: 15-Jan-2007
 * @version 1.0
 */
public class Breeder {

    public static Vector<Individual> createNewGeneration(Vector<Individual> parents, Vector<Individual> nextGeneration, GPParams params, Problem p) {

        DeepCopy copier = new DeepCopy();

        while (nextGeneration.size() < params.getPopulationSize()) {

            // choose two parents at random
            Individual male = getRandomIndividual(parents, null);
            Individual female = getRandomIndividual(parents, male);

            // copy them so it doesn't affect the pool
            Individual child1 = (Individual) copier.copy(male);
            Individual child2 = (Individual) copier.copy(female);

            if (Math.random() <= params.getCrossoverProbability()) {
                Crossover.produceOffspring(child1, child2, params, p);                             
            }

            Mutation.mutateERCs(child1, params);
            Mutation.mutateERCs(child2, params);

            if (Math.random() <= params.getPointMutationProbability()) {
                Mutation.mutateTree(child1, params);
            }

            if (Math.random() <= params.getPointMutationProbability()) {
                Mutation.mutateTree(child2, params);
            }

            nextGeneration.add(child1);
            nextGeneration.add(child2);

        }

        return nextGeneration;

    }

    /**
     * Returns an individual from a set of individuals. Guarantees that is wont be the same
     * as the second argument.
     */
    public static Individual getRandomIndividual(Vector<Individual> parents, Individual notThisOne) {

        int index = (int) (Math.random() * parents.size());
        Individual ind = parents.elementAt(index);
        if (ind == notThisOne) {
            index--;
            if (index < 0) index = parents.size() - 1;
            return parents.elementAt(index);
        }
        return ind;

    }

}
