package ac.essex.gp.genetics;

import ac.essex.gp.individuals.Individual;
import ac.essex.gp.tree.Node;
import ac.essex.gp.params.GPParams;
import ac.essex.gp.params.RangeTypes;
import ac.essex.gp.nodes.ADFNode;
import ac.essex.gp.nodes.ercs.BasicERC;

/**
 * <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: 16-Jan-2007
 * @version 1.0
 */
public class Mutation {

    public static void mutateERCs(Individual ind, GPParams params) {
        mutateERCs(ind.getTree(), params);
    }

    private static void mutateERCs(Node parent, GPParams params)  {

        if (parent instanceof BasicERC) {
            if (Math.random() <= params.getERCmutateProbability()) {
                ((BasicERC) parent).mutate();
            } else {
                if (Math.random() <= params.getERCjitterProbability()) {
                    ((BasicERC) parent).jitter();
                }
            }
        }

        if (parent instanceof ADFNode) {
            if (Math.random() < params.getADFSwapProbability()) {
                // ADF nodes are classified as terminals as they have no children
                // (although this isn't quite true - they do have a subtree, but it is not considered in this context)
                parent.replaceMyselfWith(params.getTerminalNodeByType(parent.getReturnType(), RangeTypes.DONT_CARE).getInstance());
            }
        }

        for (int i = 0; i < parent.countChildren(); i++) {
            mutateERCs(parent.child[i], params);
        }

    }

    public static void mutateTree(Individual ind, GPParams params) {

        //int oldSize = ind.getTreeSize();

        // pick a random subtree
        Node randomSubTree = ind.getRandomSubtree(GPParams.ANY_RETURN_TYPE, 1.0);

        // small individuals may not have subtrees, behave gracefuly
        if (randomSubTree == null) return;

        // create an equivalent mutated tree using whatever tree builder Params tells us to use
        Node newTree = params.getTreeBuilder().produceMutatedTree(ind, randomSubTree, params);

        // replace sub tree
        randomSubTree.getParent().replaceChild(randomSubTree, newTree);

        //int newSize = ind.getTreeSize();

        //if (newSize > params.getMaxTreeSize()) {
            //System.out.println("MUTATION: Individual is now: " + newSize + "(was " + oldSize + ")");
        //}

    }
    
}
