/*
 * Copyright (C) 2014 avigier
 *
 * 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 3 of the 
 * License, or (at your option) any later version.
 * 
 * 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.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 */

package optimizations;

import static org.nuiton.i18n.I18n._;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import fr.ifremer.isisfish.util.Doc;
import fr.ifremer.isisfish.simulator.Optimization;
import fr.ifremer.isisfish.simulator.OptimizationContext;
import fr.ifremer.isisfish.datastore.SimulationStorage;
import fr.ifremer.isisfish.datastore.ResultStorage;
import fr.ifremer.isisfish.entities.Population;
import fr.ifremer.isisfish.entities.PopulationGroup;
import fr.ifremer.isisfish.entities.PopulationSeasonInfo;
import fr.ifremer.isisfish.types.Month;
import java.io.File;
import java.io.FileReader;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Comparator;
import org.apache.commons.io.FileUtils;
import org.nuiton.math.matrix.MatrixFactory;
import org.nuiton.math.matrix.MatrixIterator;
import org.nuiton.math.matrix.MatrixND;
import org.nuiton.topia.TopiaContext;
import org.nuiton.util.FileUtil;
//import scripts.ResultName;
import resultinfos.MatrixLandingWeight;

/**
 * GeneticAlgorithmOptimization.java
 *
 * Created: 28 avril 2014
 *
 * @author avigier <user.name@vcs.hostName>
 * @version $Revision: 1545 $
 * Last update: $Date: 28 avril 2014 $
 * by : $Author: avigier $
 */
public class GeneticAlgorithmOptimization implements Optimization {

	/** to use log facility, just put in your code: log.error("..."); */
    private static Log log = LogFactory.getLog(GeneticAlgorithmOptimization.class);

	@Doc("Path to export the historic file. The root is the folder where the .bat is located.")
	public String param_exportPath="HistoricGA.csv";
    protected String exportHisto = "";
    @Doc("Population which parameters are calibrated")
    public Population param_Population = null;

    // ***put here the path and name of the file containing the data used to calibrate
       // your fishery ( here observed landings per season and age groups)
    @Doc(value = "file name and path of observed landings")
    public String param_nomfichier_debarquements = "Input_essai_GA/Inputlandings.csv";
    protected File debarquementsObserves;
    protected MatrixND matrixDebarquement;
    
	@Doc("GA parameter: Number of individuals in the population of solutions")
	public int param_taillePop = 10;
    
    @Doc("GA parameter: Lower values of parameters, separated with semicolons: de la forme(\"xx1;xx2;xx3\")")
    public String param_borneInf = "0;0;0";// devient un parametre du plan d analyse/// Rentrer ici les bornes inferieruers de chaque parametre.
    @Doc("GA parameter: Upper values of parameters, separated with semicolons: de la forme(\"xx1;xx2;xx3\"); Keep the order used to fill borneInf.")
    public String param_borneSup = "10;10;10";// devient un parametre du plan d analyse/// Rentrer ici les bornes superieruers de chaque parametre.
	
	@Doc("GA parameter: Crossover method, choose between \"1X\", \"2X\", \"8X\", \"UX\", \"AX\"")
	public String param_crossoverMethod= "1X";
	@Doc("GA parameter: Crossover variations method (crossover are applied at each generation according to a probability) : \"Fixe\" or \"Adaptative\" or \"Geometric\"")
	public String param_crossoverVariationsMethod= "Geometric";
    @Doc("GA parameter: Crossover threshold, initial probability of applying crossover (between 0: never and 1: always)")
    public double param_crossoverThreshold= 0.5;	

    @Doc("GA parameter: Mutation method, choose between \"Classic\", \"Normal\"")
    public String param_mutationMethod= "Classic";
	@Doc("GA parameter: Mutation variations method (mutations are applied at each generation according to a probability): \"Fixe\" or \"Adaptative\"")
	public String param_mutationVariationsMethod= "Fixe";
	@Doc("GA parameter: Mutation threshold, initial probability of applying mutation (between 0: never and 1: always)")
	public double param_mutationThreshold= 0.5;
    @Doc("GA parameter: Mutation rate, between 0 and 1")
    public double param_mutationRate= 0.8;
    
	@Doc("GA parameter: Elite number, between 1 and taillePop")
	public int param_eliteRank= 2;
	@Doc("GA parameter: Acceptation rank, between eliteRank and taillePop")
	public int param_acceptationRank= 8;

    @Doc("GA parameter: Stopping criterion, choose between \"GenerationNumber\", \"ConfidenceLevel\"")
    public String param_criterionType= "GenerationNumber";
	@Doc("GA parameter: level of confidence to reach before stopping the algorithm")
	public double param_criterionLevel= 200;

	//public long param_seed = 1;
	
	String [] borneInf;
	String [] borneSup;
	int tailleSol;
	int taillePop;
	int eliteRank;
	int acceptationRank;
	int compteurSimus;
	SecureRandom random;
	double obj;
	boolean bool;
	String crossoverMethod;
	String mutationMethod;
	double seuilCrossoverInit;
	double seuilMutationInit;
	//int nbGenLimite;
	double criterionLevel;
	double mutationRate;
	String crossoverVariationsMethod;
	String mutationVariationsMethod;
	double critAytug;
	File exportHistoric = new File(param_exportPath);
	
	
	
	ArrayList<Experience> historique = new ArrayList<Experience>();
    // HashMap <List<Integer>,Experience> historique = new HashMap <List<Integer>,Experience>();
    // public List newList (int a, int b){
    // List nl = new ArrayList<Integer>();
    // nl.add(a);
    // nl.add(b);
    // return nl;
    //}

	//*** write the name of the simulated matrix that contains the data corresponding
	// to your observations (here MATRIX_CATCH_WEIGHT_PER_STRATEGY_MET_PER_ZONE_POP)
	public String[] necessaryResult = {
        MatrixLandingWeight.NAME,
    };
	
    public String[] getNecessaryResult() {
        return this.necessaryResult;
    }

    /**
     * Permet d'afficher a l'utilisateur une aide sur le plan.
     * @return L'aide ou la description du plan
     */
    public String getDescription() throws Exception {
        return ("Calibration using a genetic algorithm (GA): user" +
                 "gives a file of observations (here catches)(.csv), simulated output" +
                 "will try to approach oservations by changing the values of catchability");
    }
	
    /**
     * Appele lors de l'initialisation.
     *
     * @param context
     */
    public void init(OptimizationContext context) throws java.io.IOException{
    	random = new SecureRandom();
		//random.setSeed(param_seed);

            debarquementsObserves = new File(param_nomfichier_debarquements);

        // ***Create the matrix named matrixDebarquement that will contain your observed landings
        int[] dimMatrix = {7,11};
        matrixDebarquement=MatrixFactory.getInstance().create(dimMatrix); //date est un int donnant le nombre de mois sur lequel on a simule.
        // ***Then import your file in it
        matrixDebarquement.importCSV(new FileReader(debarquementsObserves),new int []{0,0});
		matrixDebarquement= matrixDebarquement.reduce(); //Indispensable pour pouvoir travailler sur la matrice et avoir les memes dimensions que les simulations.
        //log.error("MatrixDebarquement : " + matrixDebarquement);

		borneInf=param_borneInf.split(";");
		borneSup=param_borneSup.split(";");
		tailleSol=borneInf.length;
		taillePop=param_taillePop;
		crossoverMethod=param_crossoverMethod;
		mutationMethod=param_mutationMethod;
		seuilCrossoverInit=param_crossoverThreshold;
		seuilMutationInit=param_mutationThreshold;
		eliteRank=param_eliteRank;
		acceptationRank=param_acceptationRank;
		//nbGenLimite = 2;
		criterionLevel = param_criterionLevel;
		mutationRate = param_mutationRate;
		mutationVariationsMethod = param_mutationVariationsMethod;
		crossoverVariationsMethod = param_crossoverVariationsMethod;
        if(param_criterionType == "GenerationNumber") critAytug = (Math.log(1-criterionLevel))/(taillePop*(Math.log(Math.min(Math.pow(mutationRate,tailleSol), Math.pow((1-mutationRate),tailleSol)))));
    }

    /**
     * La premiere generation doit etre construite dans cette methode
     * via des appels a context.newSimulation(...)
     *
     * @param context
     */
    public void firstSimulation(OptimizationContext context) throws Exception {
		Experience genTemp[] = new Experience [taillePop];// On cree une nouvelle generation dans la table historique, contenant autant d'Experience vides que de solutions
        compteurSimus = context.getCurrentGeneration(); //numero de la generation a venir
		for (int i = 0; i < taillePop ; i++){
			SimulationStorage nextSimulation = context.newSimulation();
			genTemp[i]=createExperience(i,0,null,null);// Il faut un compteur d'experiences pour la generation.
			genTemp[i].refused = false; 
			changeDB(genTemp[i], nextSimulation);
		} 
    }

    /**
     * Genere une nouvelle serie de simulation suivant le context d'optimisation.
     * Pour cela vous devez appeler context.newSimulation(...) pour ajouter
     * des simulations pour la prochaine generation.
     *
     * @param context context
     */
    public void nextSimulation(OptimizationContext context) throws Exception {
		int generation = context.getCurrentGeneration();
        
       //Faut-il realiser la prochaine generation?
        boolean stop = isCritereArretAtteint(generation);
        if (!stop){
      System.out.println("enter next Simulation");
		    List<Experience> genNext = new ArrayList <Experience>();
    		List <Experience> genPrec = getGeneration(generation-1);// historique.subList(taillePop*(generation-1),taillePop*(generation-1)+(taillePop-1));
      System.out.println("created lists");    
            // copy ellites in decreasing order:
            int i = 0;// i sert de compteur pour savoir combien de crossover/2 il faut faire. Avec cette methode, la population non elite doit imperativement contenir un nombre pair de solutions
            List<Experience> genPrecOrd = genPrec;
            Collections.sort(genPrecOrd);
      System.out.println("sorted generation");
            for(Experience exp : genPrecOrd){
                if(exp.status.equals("Elite")) {
                    Experience [] parents = {exp};
    				genNext.add(createExperience(i,generation, parents,exp.parametrisation)); // la crée et la stoque dans l'historique
    				i++;
    			}
    		}
      System.out.println("added ellites to next generation");
           // Cross over
    		genNext = crossover(i, generation, genNext); // réalisé pour tous les i
            
            // retour sur les mauvaises solutions
    		i = acceptationRank;
    		genNext = remplacement(i, genNext, generation);//Remplacement des mauvaises sol par des croisements d'elites 
    
    
            // Mutations
            for(Experience exp : genNext.subList(param_eliteRank,genNext.size()-1)){// Mutations sur celles qui ne decoulent pas des elites
    			// Pour chaque parametre mutation ou pas puis modif
    			for(int k=0; k<tailleSol; k++){
    					mutateParameter(k, exp.parametrisation, generation);
    			}
    		}
    
    
    
    			for (Experience exp : genNext){
                SimulationStorage nextSimulation = context.newSimulation();
    				changeDB(exp, nextSimulation);//Voir comment faire reference a la bonne simulation et faire une sorte de bouscle changeDB puis appel a ISIS.
    			}
		}
    }

    /**
     * Cette methode est appelee apres chaque serie de simulation
     * soit apres firstSimulation et nextSimulation
     * @param context
     */
    public void endSimulation(OptimizationContext context) throws Exception {
		int generation = context.getCurrentGeneration();// Numero de la generation qui vient de se terminer
		List<SimulationStorage>  lastGeneration = context.getLastSimulations();
		List<Experience> genCurr = getGeneration(generation); 

      // compute and store objective fonctions
		for (Experience exp : genCurr){
			SimulationStorage simSto = lastGeneration.get(genCurr.indexOf(exp));
			ResultStorage result = simSto.getResultStorage();//Reference a la generation qui vient de se terminer. Voir s'il y a une appelleation pour faire reference a une simuilation de la derniere generation et boucler dessus
			exp.objective = calculFonctionObjectif(result);//Voir comment faire reference a la bonne simulation.
		    simSto.closeStorage();
		}
       // compute and store best solution of the generation
		Experience best = bestSolution(genCurr);
		for (Experience exp : genCurr){
			exp.best = best;
		}

       // attribute status to each simulation 
		attributionStatuts(genCurr);
		for (Experience exp : genCurr){
			exportHisto+=exp.toCSV();//Sauvegarde de tout ce qu'il s'est passe dans un fichier exterieur
			FileUtils.writeStringToFile(exportHistoric,exportHisto);
		}
       
    }

    /**
     * Cette methode est appelee lorsqu'il n'y a plus de simulation a faire
     * (firstSimulation ou nextSimulation n'ont pas fait appel a context.newSimulation)
     * @param context
     */
    public void finish(OptimizationContext context) throws Exception{

	}
	/////////////////////
	/////////////////////
	///		 		  ///
	///    CLASSES	  ///
	///		 		  ///
	/////////////////////
	/////////////////////
	
	//// Class Parameter 
	class Parameter{

		double value;
		double inf;
		double sup;
		
		public Parameter (double valeur, double borneinf, double bornesup){
			/*if (borneinf >= bornesup) {
				throws SimulationException(String.format("Error: inf(%s) >= sup(%s)", borneinf, bornesup));
			}*/
			value=valeur;
			inf=borneinf;
			sup=bornesup;
		}
		
		public Parameter copy() {
			return new Parameter(value, inf, sup);
		}
	}

	
	// Class Experience
	class Experience implements Comparable<Experience>{
		
		Parameter[] parametrisation;
		Experience[] parents;
		int id;
		boolean refused;
		boolean crossed;
		boolean mutated;
		int generation;		//Variables renseignees par le constructeur

		String status;
		Experience best;
		double objective;
		int rank;


		public Experience(int id, int generation, Experience[] parents, Parameter[] parametrisation){
			this.id = id;
			this.generation = generation;
			if (parents != null){
				this.parents = parents;
				this.parametrisation = parametrisation;
			}
			this.crossed=false;
			this.mutated=false;
			this.refused=false;
			
			if ((parents == null )|| (parametrisation == null)) {
				this.parametrisation = initParam();
			}
		}
		
		public String toCSV() {
			String sep = ";";
			String result = "";
			String saut = "\n";
			result += generation + sep;
			result += id + sep;
			result += status + sep;
			result += refused + sep;
			result += crossed +sep;
			result += mutated +sep;
			result += objective + sep;
			for (int i=0; i<tailleSol; i++){
				result += parametrisation[i].value + sep;
			}
			if (parents != null){
				for(int i=0; i<parents.length; i++){
					result += parents[i].id + sep;
				}
			}
			result += best.id + saut;				//Il y aura des nullPointerException...	
			return result;
		}

        public Double getFO()  {
           return this.objective;
        }

        public Integer getGeneration()  {
            return this.generation;
        }
        
        @Override
        public int compareTo(Experience exp) {
            return this.getFO().compareTo(exp.getFO());
        }
       
	}
	
	/////////////////////
	/////////////////////
	///		 		  ///
	///    METHODES	  ///
	///		 		  ///
	/////////////////////
	/////////////////////
	
	/**Gives elite, non elite or refused status to each Experience of a generation following its ranking
	* @param genCurr the current generation
	*/
	public void attributionStatuts(List<Experience> genCurr){// classement est le classement par ordre d'identifiant, z.B. [5, 0, 2, 6, [...]] ou chaque int = 1 numero d'identifiant, la position dans la liste est le rang-1.
		List<Experience> genCurrOrd = genCurr;
        Collections.sort(genCurrOrd);
		for(Experience exp : genCurrOrd){
			if (genCurrOrd.indexOf(exp) < eliteRank){
				exp.status = "Elite";
			} else if (genCurrOrd.indexOf(exp) < acceptationRank){
				exp.status = "Non elite";
			} else{
				exp.status = "Refused";
			}
		}
	}
   
	/**Gives best experience of the generation
    * @param genCurr the current generation
    */
	public Experience bestSolution(List<Experience> genCurr){ // Renvoie la meilleure elite de la generation, qui est la meuilleure solution jamais parcourue.
		List<Experience> genCurrOrd = genCurr;
        Collections.sort(genCurrOrd);
		Experience best = genCurrOrd.get(0);
		return best;
	}
	
	/**
	*Calcule la valeur de la fonction d'objectif de la parametrisation utilisee pour la simulation qui vient de s'achever.
	*Pour la methode de calcul, voir ce qui a le mieux marche pour le recuit.
	*@param result le numero de la simulation venant de s'achever
	*@return la valeur de fonction d'objectif
	*/
	public double calculFonctionObjectif(ResultStorage result){// A adapter a l'aspect muklti-solutions d'une generation
        /////***import the matrix of simulated data (here landings) from the simulation result
		MatrixND L = result.getMatrix(param_Population,
                MatrixLandingWeight.NAME);
        /////*** extract, sum, etc to obtain the same format/data as your observation matrix
        // useful methods to work on matrix : sumOverDim(), getSubMatrix(), reduce()
		//Somme sur les mois, strategies, metiers et zones
        L= L.sumOverDim(0,12);
		L= L.sumOverDim(1);
		L= L.sumOverDim(2);
		L= L.sumOverDim(4);
		L= L.reduce(); //Indispensable pour pouvoir travailler sur la matrice et avoir les memes dimensions que les observations.
        for(int k = 0; k<L.getDim().length;k++){
        System.out.println("Dim de L:"+L.getDim(k));    
        }
        
        ///////////////////Calcul du critere//////////////////
        double obj = 0;
        // *** using the matrixIterator assumes that the observation and simulated
        // matrix are organised in the exact same way ( columns and rows corresponding in each of them)
        for (MatrixIterator g = L.iterator(); g.hasNext();) {
            g.next();
            int[] dim = g.getCoordinates();
            double obs = matrixDebarquement.getValue(dim);
            double simules = g.getValue();
            obj += Math.pow(0.001*(obs - simules), 2);// On prend le millieme au carre pour eviter les valeurs trop grandes
		}
		return obj;
	}
	
	/**
     * Modify nextSimulation database with parameters in Experience exp.
     * @param exp the Experience in process
     * @param nextSimulation storage for the next simulation
     * @throws Exception
     */	
	protected void changeDB(Experience exp, SimulationStorage nextSimulation)
            throws Exception {
        // methode appelee dans before simualtion
        TopiaContext db = nextSimulation.getStorage().beginTransaction();//ouvrir un context pour modifier les donnees
        Population pop = (Population) db.findByTopiaId(param_Population.getTopiaId()); //reccupere la pop ciblee
        MatrixND c = pop.getCapturability(); // recupere la matrice de capturabilite
        // *** that is where you explain how to fill the catchability matrix with q1 and q2 
        for (MatrixIterator i = c.iterator(); i.hasNext();) {
            i.next();
            Object[] sem = i.getSemanticsCoordinates();
            PopulationGroup group = (PopulationGroup) sem[0];

         
            /*// *** On travaille avec des ca&pturabilites par saison. Le plus coherent serait de le faire avec les groupes seulement par la suite (10 parametres a la fois), si le recuit le permet.
            PopulationSeasonInfo season = (PopulationSeasonInfo) sem[1];
            if (season.getFirstMonth().after(Month.JULY)){ 
				log.error("setValue : " + exp.parametrisation[2].value);
                i.setValue(exp.parametrisation[2].value); 
            } else if (season.getFirstMonth().after(Month.MARCH)) {
				log.error("setValue : " + exp.parametrisation[1].value);                
				i.setValue(exp.parametrisation[1].value); 
            } else {
				log.error("setValue : " + exp.parametrisation[0].value);
                i.setValue(exp.parametrisation[0].value); 
            } */

            i.setValue(exp.parametrisation[group.getId()].value);
        
        }//fin du for

        db.commitTransaction(); // effectue la modification
        db.closeContext(); // ferme le context
    }
	
	/**
	* Creates a new Experience using the appropriate constructor
	* @param parametrisation the parametrisation of the Experience to create
	* @param id the id number of the Experience to create
	* @param generation the generation number of the Experience to create
	* @param parents the parent(s) Experience(s) of the Experience to create
	* @return the created Experience
	*/
	public Experience createExperience(int id, int generation, Experience [] parents, Parameter [] parametrisation){
		Experience result = new Experience (id,generation,parents, parametrisation);
		historique.add(result);
		return result;
	}


    /**
    * Implements the crossover according to chosen method, and creates 2 new individuals that are addd to the next genaration
    * Called by crossover each time two new parents have to be crossed
    * @param id, max(id of the individuals to create) + 1
    * @param generation the generation number of the Experiences to create
    * @param parents the parent(s) Experience(s) of the Experience to create
    * @return the next generation with the two new individuals created
    */
    
	public List<Experience> croiser (int i, int generation, List<Experience> genNext, Experience parent1, Experience parent2){
		List<Experience> genPrec = historique.subList(taillePop*(generation-1),taillePop*(generation-1)+(taillePop-1));
        /*Experience [] genPrec = new Experience[taillePop];
		for(int j=0; j<taillePop; j++){
			genPrec[j]=historique.get(taillePop*(generation-1)+j);
		}*/
		int localisation;
		Parameter[] nouvelleParametrisation1 = new Parameter[tailleSol]; 
		Parameter[] nouvelleParametrisation2 = new Parameter[tailleSol];

        // Methodes ou on coupe 1,2, 8 fois
		if (crossoverMethod.equals("1X")||crossoverMethod.equals("2X")||crossoverMethod.equals("8X")){//Avec des points de croisement (1X,2X,8X)
			int aPlacer = 0;
			if (crossoverMethod.equals("1X")){
				aPlacer=1;
			}
			if (crossoverMethod.equals("2X")){
				aPlacer=2;
			}
			if (crossoverMethod.equals("8X")){
				aPlacer=8;
			}
			ArrayList <Integer> crossLocations = new ArrayList<Integer>();
			while (aPlacer>0){
				localisation=hasard1(tailleSol);
				while (crossLocations.contains(localisation)){
					localisation=hasard1(tailleSol);
				}
				crossLocations.add(localisation);
				aPlacer--;
			}
            //----------------------------------------------
			Collections.sort(crossLocations);
			Experience parentA = parent1;
			Experience parentB = parent2;
			Experience parentTemp;
          
			for (int k=0; k<tailleSol; k++){// On recopie l'intégralité des parents.....
				nouvelleParametrisation1[k] = parentA.parametrisation[k].copy();
				nouvelleParametrisation2[k] = parentB.parametrisation[k].copy();
			}

           //----------------------------------------------

            for(int cl : crossLocations){ // for each cross locations, the entire part of the chromosome between the cross loc and the end is exchanged
			    for (int j=cl ; j<tailleSol ; j++){
				//if (crossLocations.contains(genPrec.get(j).id)){ ???
						nouvelleParametrisation1[j] = parentB.parametrisation[j].copy();
						nouvelleParametrisation2[j] = parentA.parametrisation[j].copy();
				}
                   // parents are then inverted, to alternate origine of the parts of chromosome that are exchanged
					parentTemp=parentA;
					parentA=parentB;
					parentB=parentTemp;
			}

/*NON
 *  for (listIterator cl = crosLocations.listIterator(), cl.next()){
 *      for(int k = cl; k<cl.nextIndex(); k++ ){
 *             nouvelleParametrisation1[k] = parentB.parametrisation[k].copy();
               nouvelleParametrisation2[k] = parentA.parametrisation[k].copy();
 *      }
 *  }
 */
           
			
	    }

/*       // Methode : TODO
		if (crossoverMethod.equals("UX")){//UX REPRENDRE LA MANIERE DE COPIER DE 1X
			int crossNumbers;
			for (int j=0; j<taillePop; j++){
				crossNumbers=hasard2();
				if (crossNumbers==0){
					nouvelleParametrisation1[j] = parent1.parametrisation[j].copy();
					nouvelleParametrisation2[j] = parent2.parametrisation[j].copy();
				}else{
					nouvelleParametrisation1[j] = parent2.parametrisation[j].copy();
					nouvelleParametrisation2[j] = parent1.parametrisation[j].copy();				
				}
			}
		}

       // Methode : TODO
		if (crossoverMethod.equals("AX")){//AX REPRENDRE LA MANIERE DE COPIER DE 1X
			int crossNumbers;
			double valeur1;
			double valeur2;
			double rand;
			nouvelleParametrisation1 = parent1.parametrisation;
			nouvelleParametrisation2 = parent2.parametrisation;// Question de facilite. Les valeurs vont etre changees juste paers.
			for (int j=0; j<taillePop; j++){
				rand=hasardUniforme();
				nouvelleParametrisation1[j].value = rand*parent1.parametrisation[j].value+(1-rand)*parent2.parametrisation[j].value;
				nouvelleParametrisation2[j].value = rand*parent2.parametrisation[j].value+(1-rand)*parent1.parametrisation[j].value;
			}
		}
*/
       
		Experience [] parents = {parent1,parent2};
		genNext.set(i-2,createExperience(i-2, generation, parents, nouvelleParametrisation1));
		genNext.set(i-1,createExperience(i-1, generation, parents, nouvelleParametrisation2));
		return genNext;
	}
   
	
	/**
	* Does the crossovers on the current generation non elites parametrisations, following the method the user indicated
	* @param i the number of children solutions to generate by crossover
	* @param generation the current generation number
	* @param genNext a temporary generation used to stock the children parametrisations and Experience
	* @return the temporary generation with the children parametrisations generated by crossover
	*/
	public List<Experience> crossover(int i, int generation, List<Experience> genNext){
        boolean doCrossover = isCrossover(generation);
        if(doCrossover){     
            Experience parent1;     
            Experience parent2;
    		List<Experience> genPrec = getGeneration(generation-1);
    		int id1, id2;
    		while (i < acceptationRank){
    			id1=hasard1(taillePop); //Tirer 2 numeros d'id au hasard(id1 et id2)
    			id2=hasard1(taillePop);
    			while (!(genPrec.get(id1).status.equals("Non elite"))||genPrec.get(id1).crossed==true){
    				id1=hasard1(taillePop);
    			}
    			while (!(genPrec.get(id2).status.equals("Non elite"))||(genPrec.get(id2).crossed==true)||(id1==id2)){
    				id2=hasard1(taillePop);
    			}
    			genPrec.get(id1).crossed = true;//Marquer les parents comme ayant ete croises
    			genPrec.get(id2).crossed = true;
    			i += 2;
             // TO DO changer croiser pour renvoyer uniquement les 2 experiences?
    			genNext = croiser(i, generation, genNext, genPrec.get(id1), genPrec.get(id2));
    		}
        }
    		return genNext;
	}

    /** Methods to draw random number
     * 
     */
	public int hasard1(int i){
		int result = random.nextInt(i);
		return result;
	}

	public int hasard2(){
		int result = random.nextInt(2);
		return result;
	}
	
	public double hasard4(){
		double result = 1 + random.nextDouble()*0.1-0.05;
		return result;
	}	
	
	public double hasardNormal(){
		double result = random.nextGaussian();
		return result;
	}
		
	public int hasardPoisson(int lambda){ // See http://stackoverflow.com/questions/1241555/algorithm-to-generate-poisson-and-binomial-random-numbers (17th March 2014)
	  double L = Math.exp(-lambda);
	  double p = 1.0;
	  int k = 0;

	  do {
		k++;
		p *= Math.random();
	  } while (p > L);

	  return k - 1;
	}

	public double hasardUniforme(){
		double result = random.nextDouble();
		return result;
	}


    
	/**
	* Called by Experience constructor if the Experience to create has no parameters values. Creates a random parametrisation, using an uniform law and the parameters bounds.
	* @return the parametrisation
	*/
	public Parameter [] initParam(){
		Parameter M1[] = new Parameter[tailleSol];
		for (int i=0; i<tailleSol; i++){
			double uni=hasardUniforme();
			double inf = Double.parseDouble(borneInf[i]);
			double sup = Double.parseDouble(borneSup[i]);
			double val= inf+uni*(sup-inf);
			Parameter param = new Parameter(val, inf, sup);
			M1[i]=param;
		}
		return M1;
	}
   
	/** Stopping criteria
    * 
    */
	public boolean isCritereArretAtteint(int generation){
		boolean bool = false;
		if(param_criterionType == "GenerationNumber" && generation >= (int)criterionLevel){
			bool = true;
		}else if(param_criterionType == "ConfidenceLevel" && generation < critAytug){
		    // Ce que dit Aytug ATTENTION, une fois le probleme du critere resolu, il faudra bien re-indiquer bool=true
		    log.error("Generation " + generation + " et critere d'Aytug " + critAytug);
		}
		log.error("bool = " + bool);
		return bool;
	}
	
	/**
	* Checks if the algorithm must do crossovers or not for the current generation.
	* @param generation the current generation (iteration)
	* @return true if crossovers will be done, false otherwise 
	*/
	public boolean isCrossover(int generation){
		boolean bool = true;
		double seuilCrossover = 0;
	 /*	if (crossoverVariationsMethod.equals("Adaptative")&&(isFrozen(generation))){
			int genMemeFO = sameValue(generation);
			seuilCrossover = seuilCrossoverInit*((genMemeFO-nbGenLimite)/nbGenLimite)*(alpha-seuilCrossoverInit);//Formule de seuilCrossover adaptatif, alpha a determiner a l'avance (utilisatuer), nbGenLimite a fixer par l'utilisateur
		}else if(crossoverVariationsMethod.equals("Adaptative")&&(!isFrozen(generation))){
			seuilCrossover = seuilCrossoverInit;}*/
		if(crossoverVariationsMethod.equals("Geometric")){
		//Methode 1 : on fait des crossovers ou non avec un seuil
			seuilCrossover = Math.pow(0.9975,generation)*seuilCrossoverInit;//Cas de diminution geometrique du seuil de refus (voir les autres methodes aussi)
		}else if (crossoverVariationsMethod.equals("Fixe")){
		//Methode 2 : fixe
			seuilCrossover = seuilCrossoverInit;
		}	//Autres methodes
		
		double rand=hasardUniforme();
		if (rand > seuilCrossover){
			bool = false;
		}
		return bool;
	}
	/*
	/**
	* Checks if the algorithm is "frozen", i.e. is exploring solutions with the same best objective function values for a given number of iterations
	* @param generation the current generation (iteration) number
	* @return true if the algorithm is frozen, false otherwise
	* @see sameValue
	*
	public boolean isFrozen(int generation){
		boolean bool=false;
		int memeFO = sameValue(generation);//nbGenLimite aÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Â ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬ÃƒÂ¢Ã¢â‚¬Å¾Ã‚Â¢ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â¢ÃƒÆ’Ã†â€™Ãƒâ€šÃ‚Â¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€¦Ã‚Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â¬ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Â¦ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â¡ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’Ã†â€™Ãƒâ€šÃ‚Â¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡Ãƒâ€šÃ‚Â¬ÃƒÆ’Ã¢â‚¬Â¦Ãƒâ€šÃ‚Â¡ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€¦Ã‚Â¡ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â  fixer par l'utilisateur
		if (memeFO > nbGenLimite){// Si ca fait longtemps que la valeur de FO est la meme (fait-on une variante avec "la meilleure solution est la meme depuis longtemps"?)
			bool= true;
		}
		return bool;
	}
	*/


	/**
	*Modifie un parametre selon une certaine methode, c est l operateur de mutation
	*@param parametrisation la parametrisation contenant l'Experience sur laquelle on travaille
	*@param num la position dans la parametrisation du parametre a modifier
	*/
	public void mutateParameter(int num, Parameter[] parametrisation, int generation){

        // Should mutation be applied ?
        double seuilMutation = 0;
        //Meme principe qu'isCrossover
        /*if ((mutationVariationsMethod.equals("Adaptative"))&&(isFrozen(generation))){
            int genMemeFO = sameValue(generation);
            seuilMutation = seuilMutationInit*((genMemeFO-nbGenLimite)/nbGenLimite)*(beta-seuilMutationInit);//Formule de seuilMutation adaptatif, beta a determiner a l'avance (utilisatuer), nbGenLimite a fixer par l'utilisateur
        }else if ((mutationVariationsMethod.equals("Adaptative"))&&(!isFrozen(generation))){
            seuilMutation = seuilMutationInit;}*/
        if(crossoverVariationsMethod.equals("Geometric")){
        //Methode 1 : on fait des crossovers ou non avec un seuil
            seuilMutation = Math.pow(1.002506,generation)*seuilMutationInit;//Cas de diminution geometrique (voir les autres methodes aussi)
        }else if(mutationVariationsMethod.equals("Fixe")){
        //Methode 1 : fixe
            seuilMutation = seuilMutationInit;
        }//Autres methodes
        //Cas ou : s'il n'y a pas eu crossover, alors isMutation renvoie true
        //Autres

        // aléa de mutation
        double rand = hasardUniforme();
        boolean bool = (rand < seuilMutation);

        // If yes, Apply mutation
		if(bool){
    		double amplitude = 1;
    		//Methode 1
    		if (mutationMethod.equals("Classic")){
    			amplitude = hasard4();
    		}
    		else if (mutationMethod.equals("Normal")){
    			amplitude = hasardNormal() + 1;
    		}
    		double valeur=parametrisation[num].value;
    		parametrisation[num].value=amplitude*valeur;
    		int MAX = 10000;
    		while (MAX > 0 && (parametrisation[num].value<parametrisation[num].inf || parametrisation[num].value>parametrisation[num].sup)){	//Tant qu'on est en dehors du domaine de definiton du parametre, on modifie la valeur
    			amplitude = hasard4();
    			parametrisation[num].value=amplitude*valeur;
    			MAX--;
    		}
    		/*
    		if (MAX <= 0) {
    			throws SimulationException("Can't find new value");
    		}*/
    		//Methode 2
    		//etc. L'idee est de faire varier la maniere de faire varier delta en  fcontion de l'avancement de l'algorithme.
		}
	}
	
	/**
	* Ranks the Experience contained in a generation by objective function value
	* @param genCurr the current generation
	* @return classement a list containing the id numbers of the Experience, ordered by objective function value
	*/
/*	public double [] ranking (Experience[] genCurr){//Tri des solutions en fonction de leur valeur de fonction d'objectif, attribution d'un rang a chacune.
		//Pour la generation en cours, copie de la valeur de FO et des identifiants de toutes les solutions, tab_copie
		double tabCopie [][] = new double [taillePop][2];
		for (int j=0; j<taillePop; j++){
			tabCopie[j][0] = genCurr[j].objective;
			tabCopie[j][1] = genCurr[j].id;
		}
		double tabRank[][] = new double [taillePop][2];//creation d'un tableau qui va contenir le rang et les identifiants de chacune, tab_rank
		int rang=1;
		int min=0;
		while (rang<=taillePop){
			for (int j=0; j<taillePop; j++){
				if ((tabCopie[j][0]<tabCopie[min][0])&&(tabCopie[j][0]>=0)){
					min=j;
				}
			}//Chercher son minimum
			tabRank[rang-1][0]=min;
			tabRank[rang-1][1]=rang;//Copier son identifant dans tab_rank avec le rang rang
			tabCopie[min][0]=-1;//Les valeurs negetives ne sont pas parcourues dabs la boucle du dessus.
			rang++;
			min=0;
		}
		double [] classement = new double [taillePop];
		for (int j=0;j<taillePop;j++){
			classement[j] = tabRank[j][0];
		}
		return classement;
	}*/


    public List<Experience> rank (List<Experience> genCurr){//Tri des solutions en fonction de leur valeur de fonction d'objectif, attribution d'un rang a chacune.
       
        List <Experience> genCurrOrd = genCurr;
        Collections.reverse(genCurrOrd);
        
        return genCurrOrd;
    }
	
		/**
	* Does the crossovers on the current generation elite parametrsisations to replace refused parametrisations.
	* @param i the number of children solutions to generate by crossover
	* @param generation the current generation number
	* @param genTemp a temporary generation used to stock the children parametrisations and Experience
	* @return the temporary generation with the children parametrisations generated by crossover
	*/
	public List<Experience> remplacement(int i, List<Experience> genNext, int generation){
		int localisation;
		List<Experience> genPrec = historique.subList(taillePop*(generation-1),taillePop*(generation-1)+(taillePop-1));
        /*Experience[] genPrec = new Experience[taillePop];
		for(int j=0;j<taillePop; j++){
			genPrec[j]=historique.get(taillePop*(generation-1)+j);
		}*/
		int id1, id2, k;
		k=0;
		while ((i < taillePop)&&(k<1000)){
			id1=hasard1(taillePop); //Tirer 2 numeros d'id au hasard(id1 et id2)
			id2=hasard1(taillePop);
			while (!(genPrec.get(id1).status.equals("Elite"))){
				id1=hasard1(taillePop);
				k++;
			}
			while (!(genPrec.get(id2).status.equals("Elite"))||(id1==id2)){
				id2=hasard1(taillePop);
				k++;
			}
			genPrec.get(id1).crossed = true;//Marquer les parents comme ayant ete croises
			genPrec.get(id2).crossed = true;
			i+= 2;
			genNext = croiser (i, generation, genNext, genPrec.get(id1), genPrec.get(id2));
		}
		return genNext;
	}
	
	/**
	* Calculates for how long the best objective function value is the same, in number of generations (iterations)
	* @param generation the current generation (iteration) number
	* @return the number of generations (iterations) with the same best objective function value
	*/
	public int sameValue (int generation){
		double value = historique.get(taillePop*(generation)+1).best.objective;
		int sameValueNumber = 1; //Combien de generations, la generation en cours comprise, ont la meme valeur de meilleure FO?
		double valuePrec = historique.get(taillePop*(generation-1)+1).best.objective;
		while (valuePrec == value){
			sameValueNumber++;
			valuePrec = historique.get(taillePop*(generation-sameValueNumber-1)+1).best.objective;
		}
		return sameValueNumber;
	}


    List<Experience> getGeneration(int generation){
        List<Experience> gen = new ArrayList<Experience>();
        for(Experience e : historique){
            if(e.getGeneration().equals(generation)) gen.add(e);
        }
        return gen;
    }

   
}