package ec.gp.semantic.geometry;

import ec.EvolutionState;
import ec.app.semanticGP.func.logic.And;
import ec.app.semanticGP.func.logic.Inv;
import ec.app.semanticGP.func.numeric.*;
import ec.gp.*;
import ec.gp.semantic.ISemanticProblem;
import ec.gp.semantic.func.SimpleNodeBase;
import ec.gp.semantic.utils.InstructionJoiner;
import ec.util.Parameter;
import library.semantics.TestCase;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * Creates Double programs, which semantics are guaranteed to constitute a basis.
 *
 * @author Tomasz Pawlak
 */
public class DoubleBasisConstructor extends GPNodeBuilder {

	private final static Parameter DEFAULT_BASE = new Parameter("LC");

	private Queue<SimpleNodeBase<?>> programs = new LinkedList<SimpleNodeBase<?>>();

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

	@Override
	public GPNode newRootedTree(EvolutionState state, GPType type, int thread, GPNodeParent parent, GPFunctionSet set, int argposition, int requestedSize) {
		if (this.programs.size() > 0) {
			return this.programs.poll();
		}

		// build base here
		ISemanticProblem<Double> problem = (ISemanticProblem<Double>) state.evaluator.p_problem;
		List<TestCase<Double>> fitnessCases = problem.getFitnessCases();

		double negMult = -1.0;

		for (TestCase<Double> tc : fitnessCases) {

			Double[] arguments = tc.getArguments();
			List<SimpleNodeBase<?>> toJoin = new ArrayList<SimpleNodeBase<?>>();

			for (int i = 0; i < arguments.length; ++i) {
				Sub sub = new Sub();
				sub.children = new GPNode[]{Xn.get((byte)i), new Constant(arguments[i])};

				Mul mul = new Mul();
				mul.children = new GPNode[]{sub, sub};

				toJoin.add(mul);
			}

			SimpleNodeBase<?> sum = InstructionJoiner.join(toJoin, Sum.class, 2);

			Mul neg = new Mul();
			neg.children = new GPNode[]{new Constant(negMult), sum};
			negMult -= 1.0;

			Exp exp = new Exp();
			exp.children = new GPNode[]{neg};

			this.programs.add(exp);
		}

		return this.programs.poll();
	}

}
