package multinomad.tools;

import java.util.ArrayList;

import multinomad.individuals.DoubleIndividual;
import multinomad.individuals.Population;

public class LoisFunctionBench extends Function {
	

	

	double[] lowerbounds = {0.1, 0.1, 0.1, 20, -90, -80, -90, -90, -90, -90, 1,  -30, 1,  -30};
	//double[] upperbounds = {2,  2,  30,  150, -2,  30,  -2,  -2,  -2,  -2,  30, -1,  30, -1};
	double[] upperbounds = {30,  30,  30,  150, -2,  30,  -2,  -2,  -2,  -2,  30, -1,  30, -1};
	double[] vecV = {-100,-90,-80,-70,-60,-50,-40,-30,-20,-10,0,10,20,30,40,50};
	// Simple benchmark parameters
	double gCa=0.4; 	double  gK=0.9;		double gL=0.24;
	double ECa=126.5; 	double EK=-86;		double EL=-49;
	double V12x1=-4.3;	double V12x2=-13.01;double V12x3=-51.7; double V12x4=-82.2;
	double kx1=22.8; 	double kx2=-29.9; 	double kx3=10.8;	double kx4=-3.3;
	
	public LoisFunctionBench() {
		super();
		// Parameters for bistable benchmark
		// ----Comment next lines if you want to execute simple benchmark. 
		gCa=7.9; 
		gK=18.8; 
		gL=2.24;
		ECa=98.9; 
		EK=-27.9; 
		EL=-80;
		V12x1=-7.4;  
		V12x2=-2;  
		V12x3=-9.1; 
		V12x4=-22.2;
		kx1=14.7; 
		kx2=-28.7; 
		kx3=12.2; 
		kx4=-13.72;
		// -----Comment until here

		dimension=14;
		bounds = new ArrayList<ClosedInterval.Double >();
		noOptima = 50;
		for(int i=0;i<dimension;i++) {
			ClosedInterval.Double newBound = new ClosedInterval.Double(lowerbounds[i],upperbounds[i]);
			bounds.add(newBound);
		}
	}
	
	
	private double xinf(double v, double v12,double k) {
		return  1.0 / (1 + Math.exp((v12-v) / k));
	}
	
	private double iinf(double vh) {
		double y = gCa*xinf(vh,V12x1,kx1)*xinf(vh,V12x2,kx2)*(vh-ECa)+gK*xinf(vh,V12x3,kx3)*xinf(vh,V12x4,kx4)*(vh-EK)+gL*(vh-EL);
		return y;
	}

	@Override
	// Fonction coût pour un modèle W
	public double evaluate(double[] x) {
		nEvals++;
		double e = 0;

		for (int i=0;i<vecV.length;i++) {
//			            (Iinf(vecV(i))-(p(1)*xinf(vecV(i),p(7),p(11))*xinf(vecV(i),p(8),p(12))*(vecV(i)-p(4))+p(2)*xinf(vecV(i),p(9),p(13))*xinf(vecV(i),p(10),p(14))*(vecV(i)-p(5))+p(3)*(vecV(i)-p(6))))
			double aux = iinf(vecV[i])-(x[0]*xinf(vecV[i],x[6],x[10])*xinf(vecV[i],x[7],x[11])*(vecV[i]-x[3])+x[1]*xinf(vecV[i],x[8],x[12])*xinf(vecV[i],x[9] ,x[13])*(vecV[i]-x[4])+x[2]*(vecV[i]-x[5]));
			e+= Math.pow(aux, 2);
		}
		double y = (e*1.0)/vecV.length;		//Transforming the minimization into a maximization problem by multiplying by -1
		return -y;
	}

	@Override
	public double[] evaluateAPopulation(Population pop) {
		//Fitness value to calculate
		double [] fitnesses = new double[pop.size()];
				
		int i = 0;
		for(DoubleIndividual ind: pop) {
			fitnesses[i] = evaluate(ind.getChr().asdouble());
			ind.setFitness(fitnesses[i]);
			i++;
		}
				
		return fitnesses;
	}

	@Override
	public Seeds how_many_goptima(Population p, double accuracy) {
		// TODO Auto-generated method stub
		return null;
	}
	
	public static void main(String[] args) {
		//Validation of the function
		//This solution x
		double[] x = {16.68256811268952, 14.089014627077335, 0.12622918799450683, 92.60586021393392, -14.82307432382028, -25.60295758548328, -41.84220898239812, -67.4409541805123, -46.81036907691764, -78.80611379533808, 9.311838620876443, -5.109349442063738, 4.407582653501139, -7.917468098569785};
		//Should return f(x) = 749.36534 according to Lois implementation in scilab
		
		LoisFunctionBench test = new LoisFunctionBench();
		System.out.println("Test: value should be f(x)=749.36534 and it is: "+test.evaluate(x));
		
	}

}
