/*
 * 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 java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
import java.util.TreeSet;
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.MatrixCatchPerStrategyMetPerZonePop;
import org.apache.commons.lang3.StringUtils;

/**
 * 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 DichotomyMultiSpecies implements Optimization {

	/** to use log facility, just put in your code: log.error("..."); */
    private static Log log = LogFactory.getLog(DichotomyMultiSpecies.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 = "population;generation;id;status;objective;parents;best;simNum\n";
    
    @Doc("Populations which parameters are calibrated separated by ;")
    //public Population param_Population = null;
	public String param_populations = "Germon Atlantique;pop_BSS_Manche;pop_BSS_Gascogne";
	protected List<Population> pops;
	
	
    // ***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 = "pop names ; file name and path of observed landings, seperated by \"|\"")
    public String param_nomfichier_debarquements = "InputGdGCeres/Obs_landings_all_strat_month.csv";
    protected File debarquementsObserves;

   
	
	@Doc("GA parameter: Number of individuals in the population of solutions")
	public int param_taillePop = 11;
    
	@Doc("GA parameter: Elite number, between 1 and taillePop")
	public int param_eliteRank= 2;

    @Doc("GA parameter: Stopping criterion, choose between \"GenerationNumber\", \"ConfidenceLevel\"")
    public String param_criterionType= "GenerationNumber";
    @Doc("GA parameter: number to reach before stopping the algorithm")
    public double param_criterionLevel= 2;
    
    @Doc("GA parameter: Lower values of parameters, separated with : de la forme(\"pop1;xx:pop2;yy\")")
    public String param_borneInf = "Germon Atlantique;0.0:pop_BSS_Manche;0.0:pop_BSS_Gascogne;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 : de la forme(\"pop1;xx:pop2;yy\")")
    public String param_borneSup = "Germon Atlantique;1.0:pop_BSS_Manche;1.0:pop_BSS_Gascogne;1.0";// devient un parametre du plan d analyse/// Rentrer ici les bornes superieruers de chaque parametre.
	
    	
	protected Map<String, Population> mapPops ;
    protected MatrixND MatrixDebarquementAllPop;
	protected Map<Population,String> mapPopBoundsInf;
	protected Map<Population,String> mapPopBoundsSup;

	double obj;
	boolean bool;
	
	File exportHistoric;
	protected Historique historique;
	protected Ellites ellites; 

	//*** 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 = {
        MatrixCatchPerStrategyMetPerZonePop.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 of one or several populations by dichotomy: user" +
                 "gives min and max bounds and files of observations (here catches)(.csv), simulated output" +
                 "will try to approach oservations by changing the values of catchability." + 
				 "The script proeeds by intervals of best values, starting with 10 values between min and max, then refining interval at each generation");
    }
	
    /**
     * Appele lors de l'initialisation.
     *
     * @param context
     */
    public void init(OptimizationContext context) throws java.io.IOException {
		
        exportHistoric = new File(param_exportPath);
		historique = new Historique();
		ellites = new Ellites(param_eliteRank);
		pops = new ArrayList<Population>();
        mapPops = new HashMap<String, Population>();
     //System.out.println("pops size:"+pops.size());
		String[] popnames = param_populations.split(";");
		for(String popname : popnames){
			for(Population pop: context.getParam().getPopulations()){
				if(pop.getName().equals(popname)){
					pops.add(pop);
                  //System.out.println(popname + pop.getName());
					mapPops.put(popname,pop);
				}
			}
		}
		
		// matrix of catch
		String Nomfichier = param_nomfichier_debarquements;

		/*debarquementsObserves = new File(Nomfichier);
		MatrixDebarquementAllPop = MatrixFactory.getInstance().create(debarquementsObserves);
		MatrixDebarquementAllPop = MatrixDebarquementAllPop.reduce(); //Indispensable pour pouvoir travailler sur la matrice et avoir les memes dimensions que les simulations.
*/
		
		// parameter bounds
		mapPopBoundsInf = new HashMap<Population, String>();	
		mapPopBoundsSup = new HashMap<Population, String>();	
		String[] listBorneInf=param_borneInf.split(":");
		String[] listBorneSup=param_borneSup.split(":");
		for(String bi : listBorneInf){
			List<String> popBorneinf = Arrays.asList(bi.split(";"));
            int pbis = popBorneinf.size() ;
			mapPopBoundsInf.put(mapPops.get(popBorneinf.get(0)),popBorneinf.get(1));
		}
		for(String bs : listBorneSup){
			List<String> popBornesup = Arrays.asList(bs.split(";"));
            int pbss = popBornesup.size();
			mapPopBoundsSup.put(mapPops.get(popBornesup.get(0)),popBornesup.get(1));
		}
		
	
        int tailleSol = 1;
        
    }

    /**
     * La premiere generation doit etre construite dans cette methode
     * via des appels a context.newSimulation(...)
     *
     * @param context
     */
    public void firstSimulation(OptimizationContext context) throws Exception {
		for (int i = 0; i < param_taillePop ; i++){
			SimulationStorage nextSimulation = context.newSimulation();
			for (Population pop : pops) {
				Experience exp = createExperience(pop, i, 0, null, null);// Il faut un compteur d'experiences pour la generation.
				exp.simulationNumber = nextSimulation.getParameter().getSimulationPlanNumber();
				changeDB(exp, 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 {
        log.info("enter next simu");
            int generation = context.getCurrentGeneration();        
       //Faut-il realiser la prochaine generation?
        boolean stop = isCritereArretAtteint(generation);
        if (!stop){
         
			for (Population pop: pops) {
		
				List <Experience> genPrec = historique.getGeneration(generation-1).get(pop);// historique.subList(taillePop*(generation-1),taillePop*(generation-1)+(taillePop-1));
		 
				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
				
				for(Experience exp : genPrec){
                    if (exp == null || exp.status == null) {
                        throw new RuntimeException("!!!! ca marche pô: " + exp + "\n" + genPrec);
                    }
					if(exp.status.equals("Elite")) {
						Experience [] parents = {exp, exp};
						createExperience(pop, i, generation, parents, exp.parametrisation); // la crée et la stoque dans l'historique
						i++;
					}
				}

				remplacement(i, generation, pop);//Remplacement des mauvaises sol par des croisements d'elites 

				ArrayList<Experience> genNext = historique.getGeneration(generation).get(pop);

 
			}    

            log.info("entre creation des nelles simu");
			Map<Population, ArrayList<Experience>> mapPopExp = historique.getGeneration(generation);
			for (int i = 0; i < param_taillePop; i++) {
				SimulationStorage nextSimulation = context.newSimulation();
				for (ArrayList<Experience> exps : mapPopExp.values()) {
					Experience exp = exps.get(i);
					exp.simulationNumber = nextSimulation.getParameter().getSimulationPlanNumber();
					changeDB(exp, nextSimulation);//Voir comment faire reference a la bonne simulation et faire une sorte de boucle changeDB puis appel a ISIS.
				}
    		}
		}
       log.info("exit next simu");
    }

    /**
     * Cette methode est appelee apres chaque serie de simulation
     * soit apres firstSimulation et nextSimulation
     * @param context
     */
    public void endSimulation(OptimizationContext context) throws Exception {
		log.info("enter end simu");
		int generation = context.getCurrentGeneration();// Numero de la generation qui vient de se terminer
		List<SimulationStorage>  lastGeneration = context.getLastSimulations();
		Map<Population, ArrayList<Experience>> mapPopExp = historique.getGeneration(generation);
		
		for (Population pop: mapPopExp.keySet()){
        log.info("boucle pop"+pop.getName());
			List<Experience> genCurr = mapPopExp.get(pop); 

		  // compute and store objective fonctions
			for (int i = 0, maxi = genCurr.size(); i<maxi; i++){ 
				Experience exp = genCurr.get(i);
				SimulationStorage simSto = lastGeneration.get(i);
				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(pop, result);//Voir comment faire reference a la bonne simulation.
				log.info("Objective computed: " + exp);
				simSto.closeStorage();
			}
		}
		 log.info("Elites");
		ellites.put(historique.getGeneration(generation));
 log.info("export histo");
		for (List<Experience> genCurr: mapPopExp.values()){
			for (Experience exp : genCurr){
				exportHisto+=exp.toCSV();//Sauvegarde de tout ce qu'il s'est passe dans un fichier exterieur
				FileUtils.writeStringToFile(exportHistoric,exportHisto);
			}
		}
       log.info("exit end simu");
				
   }

    /**
     * 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>{
		
		Population pop;
		Parameter[] parametrisation;
		Experience[] parents;
		int id;
		boolean crossed;
		boolean mutated;
		int generation;		//Variables renseignees par le constructeur
		int simulationNumber;

		String status;
		int best = -1;
		double objective;
		int rank;


		public Experience(Population pop, int id, int generation, Experience[] parents, Parameter[] parametrisation){
			this.pop = pop;
			this.id = id;
			this.generation = generation;
			if (parents != null){
				this.parents = parents;
				this.parametrisation = parametrisation;
                if (parents.length != 2 || parents[0] == null || parents[1] == null) {
                    throw new RuntimeException("C pas possible, il ne doit pas y avoir de parent null :D");
                }
			}
			this.crossed=false;
			this.mutated=false;
			
			if ((parents == null )|| (parametrisation == null)) {
				this.parametrisation = initParam(id,pop);
			}
		}
		
		public String toCSV() {
			String sep = ";";
			String result = "";
			String saut = "\n";
			result += pop.getName() + sep;
			result += generation + sep;
			result += id + sep;
			result += status + sep;
			//result += crossed +sep;
			//result += mutated +sep;
			result += objective + sep;
            if (parents != null){
                for(int i=0; i<parents.length; i++){
                    result += parents[i].id + sep;
                }
            }else result +=";;";
            log.info("ds toCSV "+ pop.getName() + " best id" + best);
            result += best + sep;   
            result += simulationNumber + sep;
			for (int i=0; i<pop.sizePopulationGroup(); i++){
				result += parametrisation[i].value + sep;
			}
            result += 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) {
            int result = this.getFO().compareTo(exp.getFO());
            if (result == 0) {
                result = exp.simulationNumber - this.simulationNumber;
            }
            return result;
        }
        
        @Override
        public String toString() {
            return "Exp n°" + id + 
                " pop:" + pop +
                " parent:" + parents +
                " crossed:" + crossed +
                " mutated:" + mutated +
                " generation:" + generation +
                " simulationNumber" + simulationNumber +
                " status:" + status +
                " best:" + best +
                " objective:" + objective +
                " rank:" + rank;
        }
	}
	
	
	
	class Historique {
		protected Map <Integer, Map<Population, ArrayList<Experience>>> historique = new HashMap<Integer,Map<Population, ArrayList<Experience>>>();
		public void add(Experience exp) {
		
			Map<Population, ArrayList<Experience>> mapPopExp = historique.get(exp.generation);
			if(mapPopExp == null){
				mapPopExp = new HashMap<Population, ArrayList<Experience>>();
				historique.put(exp.generation, mapPopExp);
			}
			ArrayList <Experience> listExp = mapPopExp.get(exp.pop);
			if(listExp == null){
				listExp = new ArrayList<Experience>();
				mapPopExp.put(exp.pop,listExp);
			}
			listExp.add(exp);
		}
		
		public Map<Population, ArrayList<Experience>> getGeneration(int n){
			Map<Population, ArrayList<Experience>> result = historique.get(n);
			/*if(mapPopExp == null){
				mapPopExp = new HashMap<Population, ArrayList<Experience>>();
				historique.put(exp.generation, mapPopExp);
			}*/
			return result;
		}
	}
	
	
	class Ellites {
		protected int max;
		protected Map <Population, TreeSet<Experience>> ellites = new HashMap<Population, TreeSet<Experience>>();
		public Ellites(int max) {
			this.max = max; 
		}
		public Map <Population, TreeSet<Experience>> get() {
			return ellites;
		}
		
		public void put(Map<Population, ArrayList<Experience>> mapPopExp) {
		// comparer avec les vielles et remplacer la moins bonne si nouvelle meilleure 
			for (Population pop : mapPopExp.keySet()){
				TreeSet<Experience> ellitePop = ellites.get(pop);
				if(ellitePop == null || true){
                    // on force la recreation des elites car actuellement on resimule
                    // a chaque generation les elites. Il ne faut pas se retrouver 
                    // plusieurs fois la meme elite. Lorsqu'on ne simulera plus
                    // les elites a chaque generation, il ne faudra pas recréer
                    // ellitePop, mais l'utiliser pour afficher la génération courante.
					ellitePop = new TreeSet<Experience>();
					ellites.put(pop, ellitePop);
				}
				
				TreeSet<Experience> exps = new TreeSet<Experience>(mapPopExp.get(pop));
				Experience thebest = exps.first();
				log.info(pop.getName() +" best:"+thebest.id + "; "+thebest.simulationNumber);
				int pos = 0;
				for (Experience e : exps) {
                log.info("boucle status exp is :"+e.id);
					e.best = thebest.id;
                    if (ellitePop.size() < max) {// ca marche car exps est trié
                        e.status = "Elite";
                        ellitePop.add(e);
                   
                    }else if (e.compareTo(ellitePop.last()) < 0) {
						e.status = "Elite";
                        ellitePop.pollLast();
						ellitePop.add(e);
					} else {
						if ( pos   < param_taillePop){
							e.status = "Non elite";
						} else{
							e.status = "Refused";
						}
					}
					pos++;
				}				
			}
		}
	}
	
	/////////////////////
	/////////////////////
	///		 		  ///
	///    METHODES	  ///
	///		 		  ///
	/////////////////////
	/////////////////////
	
	/**
	*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(Population pop, 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(pop,
                MatrixCatchPerStrategyMetPerZonePop.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(2);
        L= L.sumOverDim(3);
		L= L.sumOverDim(4);
		L= L.reduce(); //Indispensable pour pouvoir travailler sur la matrice et avoir les memes dimensions que les observations.
        
        ///////////////////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)
        //log.info("simulated landings :"+pop.getName()+L.getDim(0)+""+L.getDim(1));
        for (MatrixIterator g = L.iterator(); g.hasNext();) {
            g.next();
            Object[] dim = g.getSemanticsCoordinates();
            /*MatrixND matrixDebarquement = MatrixDebarquementAllPop.getSubMatrix(3,pop.getName()).reduce();
        log.info("obs landings :"+pop.getName()+matrixDebarquement.getDim(0)+""+matrixDebarquement.getDim(1));
            double obs = matrixDebarquement.getValue(dim)*1000;
            if(obs == 0){
                obs =1;
            }*/
            double simules = g.getValue();
            //obj += Math.pow((obs - simules), 2);// On prend le millieme au carre pour eviter les valeurs trop grandes
            obj += simules;
		}
		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
		Population pop = exp.pop;
        TopiaContext db = nextSimulation.getStorage().beginTransaction();//ouvrir un context pour modifier les donnees
        pop = (Population) db.findByTopiaId(pop.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];

            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(Population pop, int id, int generation, Experience [] parents, Parameter [] parametrisation){
		Experience result = new Experience (pop, 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 generation
    * 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 void croiser (Population pop, int i, int generation, Experience parent1, Experience parent2){
//log.info("entre croiser");
		List<Experience> genPrec = historique.getGeneration(generation-1).get(pop); //subList(param_taillePop*(generation-1),param_taillePop*(generation-1)+(param_taillePop-1));
        //log.info("size genPrec = "+genPrec.size());
        /*Experience [] genPrec = new Experience[param_taillePop];
		for(int j=0; j<param_taillePop; j++){
			genPrec[j]=historique.get(param_taillePop*(generation-1)+j);
		}*/
		int localisation;
		Parameter[] nouvelleParametrisation1 = new Parameter[pop.sizePopulationGroup()]; 
		

          
			double newInf = parent1.parametrisation[0].value;
           	double newSup = parent2.parametrisation[0].value;
			System.out.println("NEWINF:"+newInf+" NEWSUP:"+newSup);
			double taillePop = param_taillePop - 1.0;
			double mult = i - param_eliteRank - generation * param_taillePop + 1;
			System.out.println("mult:"+ mult + " i:"+i+" gerneration: +"+generation);
			nouvelleParametrisation1[0] = parent1.parametrisation[0].copy();
			nouvelleParametrisation1[0].value = newInf + mult*(newSup-newInf)/taillePop;
			
			Experience [] parents = {parent1,parent2};
			if(i-1 < param_taillePop) {
				createExperience(pop, i-1, generation, parents, nouvelleParametrisation1);
			}
	}
   
	
   
	/**
	* 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(int id, Population pop){
		Parameter M1[] = new Parameter[1];
			//for (int i=0; i<pop.sizePopulationGroup(); i++){
                //System.out.println("map size: "+mapPopBoundsInf.get(pop).size());
                //System.out.println("map val i: "+mapPopBoundsInf.get(pop).get(i));
				double inf = Double.parseDouble(mapPopBoundsInf.get(pop));
				double sup = Double.parseDouble(mapPopBoundsSup.get(pop));
				double val = 0;
				double uni = 0;
				double taillePop = param_taillePop -1.0;
				val= inf+id*(sup-inf)/taillePop;	
				Parameter param = new Parameter(val, inf, sup);
				M1[0]=param;
			
		
		return M1;
	}
   
	/** Stopping criteria
    * 
    */
	public boolean isCritereArretAtteint(int generation){
		boolean bool = false;
		if(param_criterionType.equals("GenerationNumber") && generation >= (int)param_criterionLevel){
			bool = true;
		}
		//log.info("bool = " + bool);
		return bool;
	}
	

    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 void remplacement(int i, int generation, Population pop){
		List<Experience> genPrecInit = historique.getGeneration(generation -1).get(pop);//historique.subList(taillePop*(generation-1),taillePop*(generation-1)+(taillePop-1));
		while ((i < param_taillePop)){
            List<Experience> genPrec = new ArrayList<Experience>(genPrecInit);
            i += 1;
			List<Experience> ell = new ArrayList<Experience>();
			for(Experience exp : genPrec){
                if (exp == null || exp.status == null) {
                        throw new RuntimeException("!!!! ca marche pô: " + exp + "\n" + genPrec);
                    }
				if(exp.status.equals("Elite")) {
					ell.add(exp);
				}
			}
            Experience p1 = ell.get(0);
            Experience p2 = ell.get(1);
		    croiser(pop, i, generation, p1, p2);
		}
	}
	   
}