/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package auiproject.alg;

import auiproject.AbstractOptAlgInstance;
import auiproject.DoubleVectorIndividual;
import auiproject.util.RandomNumber;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author tomasherman
 */
public class Hermato4Optimalizer extends AbstractOptAlgInstance{
    private int populationSize = 30;
    private int dimension;
    private int bestOf = 3;
    DoubleVectorIndividual cand;
    List<DoubleVectorIndividual> population;
    List<DoubleVectorIndividual> newPopulation;
    DoubleVectorIndividual child1 = new DoubleVectorIndividual();
    DoubleVectorIndividual child2 = new DoubleVectorIndividual();
    DoubleVectorIndividual randomParrentInstance = new DoubleVectorIndividual();
    VectorComparator cmp = new VectorComparator();
    Random r = new Random();
    double[] sigma;
    double mutationRangePercent = 0.1;
    int mutationLikelyhood = 50;
    double interval = 0;
    List<Double> map = new ArrayList<Double>();
    public void finish() {}

    public void initialize() {
        bsf = new DoubleVectorIndividual();
        population = new ArrayList<DoubleVectorIndividual>();
        newPopulation = new ArrayList<DoubleVectorIndividual>();
        for (int i= 0; i < populationSize; i++) {
            DoubleVectorIndividual vec = new DoubleVectorIndividual();
            vec.setGenome(RandomNumber.doubleInRange(prob.getIniRange()));
            prob.evaluateIndividual(vec);
            population.add(vec);
            DoubleVectorIndividual nvec = new DoubleVectorIndividual();
            nvec.setGenome(new double[prob.getDim()]);
            newPopulation.add(nvec);
            map.add(0.0);
        }
        sigma = new double[prob.getDim()];
        for (int i = 0; i < prob.getDim(); i++) {
            sigma[i] = (-prob.getSearchRange()[0][i]+prob.getSearchRange()[1][i])*(mutationRangePercent/100);
        }
        randomParrentInstance.setGenome(new double[prob.getDim()]);
        dimension = prob.getDim();
        interval = createFitnessMap(map, population);
        Collections.sort(population, cmp);
    }

    

    public void step() {
        generateNewPopulation(population, newPopulation);
        cpyPopulation(population, newPopulation);
        Collections.sort(population, cmp);
        interval = createFitnessMap(map, population);
        bsf = population.get(0);
    }

    void generateChil(DoubleVectorIndividual parrentA,DoubleVectorIndividual parrentB, DoubleVectorIndividual output){
        for (int i = 0; i < output.getGenome().length; i++) {
            double min,max=0;
            min = parrentA.getGenome()[i]-1*(parrentB.getGenome()[i]-parrentA.getGenome()[i]);
            max = parrentB.getGenome()[i]+1*(parrentB.getGenome()[i]-parrentA.getGenome()[i]);
            output.getGenome()[i] = RandomNumber.doubleInIterval(min, max);
        }
    }

    void generateNewPopulation(List<DoubleVectorIndividual> pop,List<DoubleVectorIndividual> newPopulation){
        for (int i = 1; i < pop.size(); i++) {
            generateBestOfNChil(bestOf, getRandomParrent(interval), newPopulation.get(i));
            mutateIndividual(mutationLikelyhood, newPopulation.get(i));
        }
    }

    void generateBestOfNChil(int n,DoubleVectorIndividual parent, DoubleVectorIndividual output){
        DoubleVectorIndividual ind = new DoubleVectorIndividual();
        DoubleVectorIndividual ind2 = new DoubleVectorIndividual();
        ind.setGenome(new double[dimension]);
        ind2.setGenome(new double[dimension]);
        generateChil(parent, getRandomParrent(interval), ind);
        prob.evaluateIndividual(output);
        for (int i = 0; i < n-1; i++) {
            generateChil(parent, getRandomParrent(interval), ind2);
            prob.evaluateIndividual(ind2);
            if(ind.getFitness() > ind2.getFitness()){
                ind.setGenome(ind2.getGenome());
                ind.setFitness(ind2.getFitness());
            }
        }
        output.setFitness(ind.getFitness());
        output.setGenome(ind.getGenome());
    }

    public DoubleVectorIndividual getRandomParrent(double interval){
        double d = r.nextDouble()*interval;
        for (int i = 0; i < map.size(); i++) {
            if (d <= map.get(i)) {
                return population.get(i);
            }

        }
        return population.get(r.nextInt(populationSize));
    }

    public DoubleVectorIndividual mutateIndividual(int likelyhood, DoubleVectorIndividual individual){
        if(r.nextInt(100) < likelyhood){
            int i = r.nextInt(individual.getGenome().length);
            int signum = 0;
            if(r.nextBoolean()){
                signum = -1;
            }
            double mutationCoef = signum*r.nextDouble()*sigma[i];
            individual.getGenome()[i] = individual.getGenome()[i]+mutationCoef;
        }
        return individual;
    }


    public double createFitnessMap(List<Double> map,List<DoubleVectorIndividual> pop){
        double out = 0;
        map.clear();
        for (int i = 0; i < pop.size(); i++) {
            out = out + pop.get(populationSize-i-1).getFitness();
            map.add(out);
        }
        return out;
    }

    public void cpyPopulation(List<DoubleVectorIndividual> pop,List<DoubleVectorIndividual> newPop ){
        for (int i = 0; i < newPop.size(); i++) {
            pop.get(i).setGenome(newPop.get(i).getGenome());
            pop.get(i).setFitness(newPop.get(i).getFitness());
            prob.evaluateIndividual(pop.get(i));
        }
    }

    private double cuntAvarageFitness(List<DoubleVectorIndividual> population){
        double sum=0;
        for (DoubleVectorIndividual doubleVectorIndividual : population) {
            sum += doubleVectorIndividual.getFitness();
        }
        return sum/population.size();
    }


}
class VectorComparator implements Comparator<DoubleVectorIndividual>{
   public int compare(DoubleVectorIndividual t, DoubleVectorIndividual t1) {
        if(t.getFitness() <= t1.getFitness()){
            return -1;
        }
        return 1;
    }
}