/*
 * Population.java
 *
 * Created on April 20, 2004, 4:41 PM
 */

package spamfilter.genetic;

import java.util.Vector;
import java.text.NumberFormat;

import spamfilter.Main;
import spamfilter.email.*;

/**
 *
 * @author  AaronStJ
 */
public class Population {
    private Corpus spamCorpus;
    private Corpus nonSpamCorpus;
    private int size;
    private Vector possibleSolutions;    
    
    private PossibleSolution bestSoFar = null;
    
        
    public Population() {
        possibleSolutions = new Vector();
    }
        
    public Population(Corpus s, Corpus ns, int sz) {
        spamCorpus = s;
        nonSpamCorpus = ns;
        size = sz;
        
        possibleSolutions = new Vector();
        
        for(int i = 0; i < size; i++) {
            PossibleSolution ps = new PossibleSolution(spamCorpus, nonSpamCorpus);
            if(bestSoFar == null || ps.getFitness() > bestSoFar.getFitness())
                bestSoFar = (PossibleSolution)ps.clone();
            add(new PossibleSolution(spamCorpus, nonSpamCorpus));
        }
    }
    
    public void selectNewBreedingPopulation(int sz)
    {
        Vector breedingPop = new Vector();
        double maxFitness = getMaximumFitness();
        for(int i = 0; i < sz; i++) {
            double rand = Math.random() * maxFitness;
            boolean done = false;
            int indexPS = 0;
            double fitness = 0;
            while(!done) {
                fitness += getPossibleSolutionAtIndex(indexPS).getFitness();
                if(fitness > rand) {
                    breedingPop.add(possibleSolutions.get(indexPS));
                    done = true;
                }
                indexPS++;
            }
        }
        
        possibleSolutions = breedingPop;
    }
    
    public void addNewRandomSolutions(int n)
    {
        for(int i = 1; i < n; i++) {
            add(new PossibleSolution(spamCorpus, nonSpamCorpus));
        }
    }
    
    public void breedToCreateWorkingSolution(int n) {
        Vector workingPop = new Vector();
        
        //repopulate the population by breeding random members of the breeding population.            
        for(int i = 0; i < n - 1; i++) {                   
            int parent1 = (int)Math.floor(Math.random() * possibleSolutions.size());
            int parent2 = (int)Math.floor(Math.random() * possibleSolutions.size());

            Main.verbose(2,"Breeding " + possibleSolutions.get(parent1) +
                  " with " + possibleSolutions.get(parent2) + "...");

            PossibleSolution child = new PossibleSolution(
                (PossibleSolution)possibleSolutions.get(parent1),
                (PossibleSolution)possibleSolutions.get(parent2));                                        

            workingPop.add(child);

            if(child.getFitness() > bestSoFar.getFitness())
                bestSoFar = (PossibleSolution)child.clone();

            Main.verboseln(2," yiedling " + child);
        }
        
        possibleSolutions = workingPop;
    }
    
    public PossibleSolution getBestSolutionSoFar() {
        return bestSoFar;
    }
    
    public void add(PossibleSolution ps){
        possibleSolutions.add(ps);
    }
    
    public double getMaximumFitness() {
       double m = 0;
       for(int index = 0; index < possibleSolutions.size(); index++)
           m += ((PossibleSolution)possibleSolutions.elementAt(index)).getFitness();
        
       return m;
    }
    
    public PossibleSolution getPossibleSolutionAtIndex(int i) {
        return (PossibleSolution)possibleSolutions.elementAt(i);
    }
    
    public void clear() {
        possibleSolutions.clear();
    }
    
    public void mutate(double chance) {
       for(int index = 0 ; index < possibleSolutions.size(); index++)
           if(Math.random() < chance)
                ((PossibleSolution)possibleSolutions.get(index)).mutate();                 
            
    }
    
    public String toString() {
        StringBuffer s = new StringBuffer();
        s.append("Possible Solution\tFitness\tNormalized Fitness\n");
        
        double fitnesses[] = new double[possibleSolutions.size()];
        double totalFitness = 0;
        
        for(int index = 0; index < possibleSolutions.size(); index++) {            
            fitnesses[index] = ((PossibleSolution)possibleSolutions.elementAt(index)).getFitness();
            totalFitness += fitnesses[index];
        }
        
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(3);
        
        
        for(int index = 0; index < possibleSolutions.size(); index++) {            
            s.append(((PossibleSolution)possibleSolutions.elementAt(index)).toString());
            
            s.append("\t");
            s.append(nf.format(fitnesses[index]));
            
            s.append("\t");
            s.append(nf.format(fitnesses[index] / totalFitness));
            
            s.append("\n");
        }
        
        return s.toString();            
    }
    
}
