package multinomad.tools.convex;

import java.util.ArrayList;

import multinomad.config.Configuration;
import multinomad.individuals.DoubleIndividual;

public class ConvexFunction {

	/**
	 * Let X be a convex set in a real vector space and let f: X -> R be a function f.
	 * f is called convex if:
	 * For every x1 and x2 in X, and every t in [0,1]:
	 * f(tx1+(1-t)x2) <= tf(x1) + (1-t)f(x2)
	 * 
	 * We make t=0.5. We can explore a set of points between x1 and x2 by recursively halving the interval. 
	 * 
	 * @param i1 first individual containing the vector x1
	 * @param i2 second individual containing the vector x2
	 * @param list Must be initialized. It keeps a list with all the individuals explored
	 * @param currentDepth initially 0
	 * @param maxDepth The depth of the tree to explore. 1 means we just test a single intermediate point; 
	 * a depth of 2 means 1 + 2 intermediate points to explore (i.e. 3). A depth of 3 would explore a max of 1 + 2 + 4 intermediate points (i.e. 7)   
	 * @return true if in maxDepth we did not detect any violation of convexity.
	 */
	public static boolean convexFunction(DoubleIndividual i1, DoubleIndividual i2, ArrayList<DoubleIndividual> list, int currentDepth, int maxDepth) {
		if (currentDepth >= maxDepth)
			return true;
		else {
			DoubleIndividual imean = averageIndividual(i1, i2);
			list.add(imean);
			if(isConvex(i1, i2, imean)) {
				boolean first = convexFunction(i1, imean, list, currentDepth+1, maxDepth);
				boolean second = convexFunction(imean, i2, list, currentDepth+1, maxDepth);
				return first && second;
			}else
				return false;
		}
	}
	
	private static DoubleIndividual averageIndividual(DoubleIndividual i1, DoubleIndividual i2) {
		double[] xx = new double[i1.getChr().getLength()];
		double[] x1 = i1.getChr().asdouble();
		double[] x2 = i2.getChr().asdouble();
		for(int i=0; i< xx.length;i++) {
			xx[i] = (x1[i]+x2[i])/2.0;
		}
		DoubleIndividual imean = new DoubleIndividual(xx);
		imean.setFitness(Configuration.f.evaluate(xx));
		return imean;
	}
	
	/**
	 * Let X be a convex set in a real vector space and let f: X -> R be a function f.
	 * f is called convex if:
	 * For every x1 and x2 in X, and every t in [0,1]:
	 * f(tx1+(1-t)x2) <= tf(x1) + (1-t)f(x2)
	 * 
	 * We assume that (tx1+(1-t)x2) is imean (i.e. the average between i1 and i2), therefore t=0.5
	 * @return true if imean doesn't violate the inequality above.
	 */
	private static boolean isConvex (DoubleIndividual i1, DoubleIndividual i2, DoubleIndividual imean) {
		double firstterm = imean.getFitness();
		double tfx1 = 0.5*i1.getFitness();
		double tfx2 = 0.5*i2.getFitness();
		
		if (firstterm >= (tfx1 + tfx2)) // Without lost of generality we talk about a maximization problem 
			return true;
		else
			return false;
		
	}
	
}
