package ac.essex.gp.problems.examples.maths;

import ac.essex.gp.params.GPParams;
import ac.essex.gp.params.NodeConstraints;
import ac.essex.gp.individuals.Individual;
import ac.essex.gp.problems.DataStack;
import ac.essex.gp.nodes.*;
import ac.essex.gp.Evolve;
import ac.essex.gp.interfaces.console.ConsoleListener;
import ac.essex.gp.problems.Problem;
import ac.essex.gp.nodes.X;
import ac.essex.gp.nodes.ercs.CustomRangeIntegerERC;
import ac.essex.gp.nodes.looping.List;
import ac.essex.gp.nodes.looping.Repeat;
import ac.essex.gp.nodes.registers.Get;
import ac.essex.gp.nodes.registers.Set;

/**
 * The Simplest Example GP Problem
 *
 * Attempts to find a GP program which can find the function f(x) which relates
 * one number to the other.
 *
 * @author Olly Oechsle, University of Essex, Date: 15-Jan-2007
 * @version 1.0
 */
public class Fibonnacci extends Problem {

    public static void main(String[] args) {
        Fibonnacci p = new Fibonnacci();
        Evolve e = new Evolve(p, new ConsoleListener(ConsoleListener.HIGH_VERBOSITY));
        e.run();
    }

    public String getName() {
        return "Fibonnacci Problem";
    }

    public void initialise(Evolve e, GPParams params) {

        // for memory.
        for (int i = 0; i <= 2; i++) {
            params.registerNode(new Set(i));
            params.registerNode(new Get(i));
        }

        //params.registerNode(new List(2));
        //params.registerNode(new List(3));
        params.registerNode(new List(2));
        params.registerNode(new Repeat());
        params.registerNode(new X());

        // arithmetic
        params.registerNode(new Add());
        //params.registerNode(new Mul());
        //params.registerNode(new Sub());
        //params.registerNode(new Div());

        // numbers
        params.registerNode(new CustomRangeIntegerERC(0,2));

        // set up additional parameters
        params.setReturnType(NodeConstraints.NUMBER);

    }

    public void customiseParameters(GPParams params) {
        params.setPopulationSize(500);
        params.setGenerations(1000);
        //params.setDynamicSizeLimiting(true);
        params.setTreeBuilder(GPParams.RAMPED_HALF_AND_HALF);
    }

    public void evaluate(Individual ind, DataStack data, Evolve e) {

        double fitness = 0;

        int hits = 0;
        int mistakes = 0;

        // try a few different values of X
        for (int x = 2; x < 20; x++) {

            // maths problem - the function we expect
            double expected = fibonnacci(x);

            // put this into the data stack
            data.setX(x);

            // see what we get back
            double received = ind.execute(data);

            // count the number of hits
            if (received == expected) hits++;
            else mistakes++;

            // regression - find the difference between expected and received
            fitness += (Math.abs(expected - received) / expected);

        }

        //fitness = mistakes;

        ind.setKozaFitness(fitness);
        ind.setHits(hits);

    }

    public static int fibonnacci(int iterations) {
        int r0 = 0;
        int r1 = 1;
        int r2 = r1;
        for(int i = 0; i < iterations; i++) {
            r2 = r0 + r1;
            r0 = r1;
            r1 = r2;
        }
        return r2;
    }

}