/*
 * Poblacio.java
 *
 * Created on 26 de diciembre de 2003, 21:38
 */

package ATSP;
import java.util.TreeSet;
import java.util.Iterator;
import java.util.Random;

/**
 *
 * @author  gabriel
 */
public class Poblacio {
   
    public static final int SELECCIO_ELITISM=1;
    
    private TreeSet individus;
    private int tipusMutacio=Individu.MUTACIO_CANVI2;
    private int tipusCreuament=Individu.CREUAMENT_PMX;
    private int tipusSeleccio=Poblacio.SELECCIO_ELITISM;
    private double probMutacio=0.1;
    private double probCreuament=.9;
    private double probEmigracio=0.1;
    private double probInmigracio=0.1;
    private boolean hibrid=false;
    private int generacio = 1;
    private int iTamanyIndividus;
    private String sNom;
    
    /**
     * Crea una nova població aleatòria
     */
    public Poblacio(int iTamanyPoblacio, int iTamanyIndividus, String sNom) {
        this.sNom = sNom;
        this.individus = new TreeSet();      
        TreeSet valors = new TreeSet();
        //Individu i = new Individu(iTamanyIndividus);
        while( this.individus.size()<iTamanyPoblacio )
        {
            //i = new Individu(i,1000);
            //this.individus.add(i);
            this.individus.add( new Individu(iTamanyIndividus) );
        }
        this.iTamanyIndividus = iTamanyIndividus;
    }
    
    public Individu novaGeneracio()
    {
        Random r = new Random();
        int N = this.individus.size();
        
        /* Seleccio */
        TreeSet pares = this.seleccio(this.tipusSeleccio, 0.1);
        
        /* Envelliment */
        Iterator it = this.individus.iterator();
        while ( it.hasNext() )
            ((Individu)it.next()).envelleix();
        
        /* Creuament */
        TreeSet fills = new TreeSet();
        Individu [] aInds = new Individu[pares.size()];
        pares.toArray(aInds);
        for( int pare1=0; pare1<aInds.length; pare1++ )
        {
            if ( fills.size() >= N ) break;
            for( int pare2=0; pare2<aInds.length; pare2++ )
            {
                if ( fills.size() >= N ) break;
                if( pare1==pare2 ) continue;

                Individu [] aFills;
                if( r.nextDouble()<this.probCreuament )
                {
                    aFills = Individu.creua(this.tipusCreuament, aInds[pare1],aInds[pare2]);
                }
                else
                {
                    aFills = new Individu[2];
                    aFills[0]=aInds[pare1];
                    aFills[1]=aInds[pare2];
                }
                for( int j=0; j<aFills.length; j++)
                    fills.add(aFills[j]);
            }
        }
        /* Si cal acabem d'omplir amb els individus actuals */
        it = this.individus.iterator();
        while( fills.size()<N && it.hasNext() )
            fills.add( it.next() );
        
        /* Mutacio */
        Iterator itFills = fills.iterator();
        while( itFills.hasNext() )
        {
            Individu fill = (Individu)itFills.next();
            if( r.nextDouble()<this.probMutacio)
                fill.muta(this.tipusMutacio);
        }
        this.individus = fills;
        
        /* Hibridació */
        if( this.isHibrid() )
        {
            itFills = fills.iterator();
            while( itFills.hasNext() )
            {
                Individu fill = (Individu)itFills.next();
                fill.muta(Individu.MUTACIO_BEST_NR3OPT);
            }
        }
        return (Individu)this.individus.first();
    }
    
    /**
     * Selecciona els individus de la següent generació segons el mètode
     * especificat.
     */
    TreeSet seleccio(int iTipusSeleccio, double proporcio)
    {
        switch(iTipusSeleccio)
        {
            case SELECCIO_ELITISM:
                return this.seleccioElitism(proporcio);
            default:
                throw new java.lang.IllegalArgumentException("Mètode de selecció no implementat.");
        }
    }
    
    TreeSet seleccioElitism(double proporcio)
    {
        int N = this.individus.size();
        Iterator it = this.individus.iterator();
        TreeSet nousIndividus = new TreeSet();
        while( it.hasNext() && nousIndividus.size()<N*proporcio)
        {
            nousIndividus.add(it.next());
        }
        return nousIndividus;
    }
    
    /** Recupera el valor de la propietat tipusMutacio.
     * @return Valor de la propietat tipusMutacio.
     *
     */
    public int getTipusMutacio() {
        return tipusMutacio;
    }
    
    /** Posa el valor de la propietat tipusMutacio.
     * @param tipusMutacio nou valor de tipusMutacio.
     *
     */
    public void setTipusMutacio(int tipusMutacio) {
        this.tipusMutacio = tipusMutacio;
    }
    
    /** Recupera el valor de la propietat tipusCreuament.
     * @return Valor de la propietat tipusCreuament.
     *
     */
    public int getTipusCreuament() {
        return tipusCreuament;
    }
    
    /** Posa el valor de la propietat tipusCreuament.
     * @param tipusCreuament nou valor de tipusCreuament.
     *
     */
    public void setTipusCreuament(int tipusCreuament) {
        this.tipusCreuament = tipusCreuament;
    }
    
    /** Recupera el valor de la propietat probMutacio.
     * @return Valor de la propietat probMutacio.
     *
     */
    public double getProbMutacio() {
        return probMutacio;
    }
    
    /** Posa el valor de la propietat probMutacio.
     * @param probMutacio nou valor de probMutacio.
     *
     */
    public void setProbMutacio(double probMutacio) {
        this.probMutacio = probMutacio;
    }
    
    /** Recupera el valor de la propietat probCreuament.
     * @return Valor de la propietat probCreuament.
     *
     */
    public double getProbCreuament() {
        return probCreuament;
    }
    
    /** Posa el valor de la propietat probCreuament.
     * @param probCreuament nou valor de probCreuament.
     *
     */
    public void setProbCreuament(double probCreuament) {
        this.probCreuament = probCreuament;
    }
    
    /** Recupera el valor de la propietat tipusSeleccio.
     * @return Valor de la propietat tipusSeleccio.
     *
     */
    public int getTipusSeleccio() {
        return tipusSeleccio;
    }
    
    /** Posa el valor de la propietat tipusSeleccio.
     * @param tipusSeleccio nou valor de tipusSeleccio.
     *
     */
    public void setTipusSeleccio(int tipusSeleccio) {
        this.tipusSeleccio = tipusSeleccio;
    }
    
    public void desastre()
    {
        int iTamanyPoblacio = this.individus.size();
        Individu millor = (Individu)this.individus.first();
        this.individus.clear();
        this.individus.add(millor);
        //Individu actual = new Individu(millor, 1);
        while( this.individus.size()<iTamanyPoblacio )
        {
            //this.individus.add(actual);
            //actual = new Individu(actual, 1000);
            this.individus.add( new Individu(this.iTamanyIndividus) );
        }
    }
    
    /** Retorna una copia dels N millors individus
     * @return Els N millors individus copiats
     *
     */
    public java.util.TreeSet getMillorsIndividus(int N) {
        java.util.TreeSet retorn = new java.util.TreeSet();
        Iterator it = this.individus.iterator();
        while (retorn.size()<N)
            retorn.add(((Individu)it.next()).clone());
        return retorn;
    }
    
    /** Afegeix els individus passats a la poblacio
     * @param individus Els nous individus de la poblacio.
     *
     */
    public void afegeixIndividus(java.util.TreeSet individus) {
        this.individus.addAll(individus);
    }
    
    /** Recupera el valor de la propietat probEmigracio.
     * @return Valor de la propietat probEmigracio.
     *
     */
    public double getProbEmigracio() {
        return probEmigracio;
    }
    
    /** Posa el valor de la propietat probEmigracio.
     * @param probEmigracio Nou valor de la propietat probEmigracio.
     *
     */
    public void setProbEmigracio(double probEmigracio) {
        this.probEmigracio = probEmigracio;
    }
    
    /** Recupera el valor de la propietat probInmigracio.
     * @return Valor de la propietat probInmigracio.
     *
     */
    public double getProbInmigracio() {
        return probInmigracio;
    }
    
    /** Posa el valor de la propietat probInmigracio.
     * @param probInmigracio Nou valor de la propietat probInmigracio.
     *
     */
    public void setProbInmigracio(double probInmigracio) {
        this.probInmigracio = probInmigracio;
    }
    
    /** Retorna el valor de la propietat sNom.
     * @return Valor de la propietat sNom.
     *
     */
    public java.lang.String getSNom() {
        return sNom;
    }

    /** Retorna el valor de la propietat hibrid.
     * @return Valor de la propietat hibrid.
     *
     */
    public boolean isHibrid() {
        return hibrid;
    }    
    
    /** Posa el valor de la propietat hibrid.
     * @param hibrid Nou valor de la propietat hibrid.
     *
     */
    public void setHibrid(boolean hibrid) {
        this.hibrid = hibrid;
    }
    
}

