/*
 * This file is part of JGAP.
 *
 * JGAP offers a dual license model containing the LGPL as well as the MPL.
 *
 * For licensing information please see the file license.txt included with JGAP
 * or have a look at the top of class org.jgap.Chromosome which representatively
 * includes the JGAP license policy applicable for any file delivered with JGAP.
 */
package org.jgap.gp.impl;

import java.io.*;
import java.util.*;

import org.apache.log4j.*;
import org.jgap.*;
import org.jgap.gp.*;
import org.jgap.util.*;

/**
 * Population for GP programs.
 *
 * @author Klaus Meffert
 * @since 3.0
 */
public class GPPopulation
    implements Serializable, Comparable {
  /** String containing the CVS revision. Read out via reflection!*/
  private final static String CVS_REVISION = "$Revision: 1.41 $";

  final static String GPPROGRAM_DELIMITER_HEADING = "<";

  final static String GPPROGRAM_DELIMITER_CLOSING = ">";

  final static String GPPROGRAM_DELIMITER = "#";

  public final static double DELTA = 0.0000001d;

  private transient Logger LOGGER = Logger.getLogger(GPPopulation.class);

  /**
   * The array of GPProgram's that make-up the Genotype's population.
   */
  private IGPProgram[] m_programs;

  private
  /*transient*/ float[] m_fitnessRank;

  private int m_popSize;

  private transient IGPProgram m_fittestProgram;

  private GPConfiguration m_config;

  /**
   * Indicates whether at least one of the programs has been changed
   * (deleted, added, modified).
   */
  private boolean m_changed;

  /**
   * Indicates that the list of GPPrograms has been sorted.
   */
  private boolean m_sorted;

  private IGPProgram m_fittestToAdd;

  /**
   * Default constructor, only for dynamic instantiation.
   *
   * @throws Exception
   *
   * @author Klaus Meffert
   * @since 3.3.4
   */
  public GPPopulation()
      throws Exception {
  }

  /*
   * @param a_config the configuration to use.
   * @param a_size the maximum size of the population in GPProgram unit
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public GPPopulation(GPConfiguration a_config, int a_size)
      throws InvalidConfigurationException {
    if (a_config == null) {
      throw new InvalidConfigurationException("Configuration must not be null!");
    }
    if (a_size < 1) {
      throw new InvalidConfigurationException(
          "Population size must be greater zero!");
    }
    m_config = a_config;
    m_programs = new GPProgram[a_size];
    m_popSize = a_size;
    m_fitnessRank = new float[a_size];
    for (int i = 0; i < a_size; i++) {
      m_fitnessRank[i] = 0.5f;
    }
  }

  /*
   * Instantiate new population with parameters from other population.
   * Don't copy the GPProgram instances from the given population!
   *
   * @param a_pop the population to retrieve the parameters from
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public GPPopulation(GPPopulation a_pop)
      throws InvalidConfigurationException {
    this(a_pop, false);
  }

  /**
   *
   * @param a_pop the population to retrieve the parameters from
   * @param a_keepPrograms true copy programs of given population to this one
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   */
  public GPPopulation(GPPopulation a_pop, boolean a_keepPrograms)
      throws InvalidConfigurationException {
    m_config = a_pop.getGPConfiguration();
    m_popSize = a_pop.getPopSize();
    m_programs = new GPProgram[m_popSize];
    m_fitnessRank = new float[m_popSize];
    if (a_keepPrograms) {
      synchronized (m_programs) {
        for (int i = 0; i < m_popSize; i++) {
          m_programs[i] = a_pop.getGPProgram(i);
          m_fitnessRank[i] = a_pop.getFitnessRank(i);
        }
      }
      m_fittestProgram = a_pop.determineFittestProgramComputed();
      if (m_fittestProgram != null) {
        m_fittestProgram = (IGPProgram) ((ICloneable)m_fittestProgram).clone();
      }
      setChanged(a_pop.isChanged());
      if (!m_changed) {
        m_sorted = true;
      }
    }
    else {
      for (int i = 0; i < m_popSize; i++) {
        m_fitnessRank[i] = 0.5f;
      }
    }
  }

  /**
   * Sorts the population into "ascending" order using some criterion for
   * "ascending". A Comparator is given which will compare two individuals,
   * and if one individual compares lower than another individual, the first
   * individual will appear in the population before the second individual.
   *
   * @param c the Comparator to use
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public void sort(Comparator c) {
    Arrays.sort(m_programs, c);
    float f = 0;
    for (int i = 0; i < m_programs.length; i++) {
      if (m_fitnessRank.length > i) {
        m_fitnessRank[i] = f;
      }
      if (m_programs[i] != null) {
        f += m_programs[i].getFitnessValue();
      }
    }
  }

  /**
   * Creates a population.
   *
   * @param a_types the type for each chromosome, the length of the array
   * represents the number of chromosomes
   * @param a_argTypes the types of the arguments to each chromosome, must be an
   * array of arrays, the first dimension of which is the number of chromosomes
   * and the second dimension of which is the number of arguments to the
   * chromosome
   * @param a_nodeSets the nodes which are allowed to be used by each chromosome,
   * must be an array of arrays, the first dimension of which is the number of
   * chromosomes and the second dimension of which is the number of nodes
   * @param a_minDepths contains the minimum depth allowed for each chromosome
   * @param a_maxDepths contains the maximum depth allowed for each chromosome
   * @param a_maxNodes reserve space for a_maxNodes number of nodes
   * @param a_fullModeAllowed array of boolean values. For each chromosome there
   * is one value indicating whether the full mode for creating chromosome
   * generations during evolution is allowed (true) or not (false)
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public void create(Class[] a_types, Class[][] a_argTypes,
                     CommandGene[][] a_nodeSets, int[] a_minDepths,
                     int[] a_maxDepths, int a_maxNodes,
                     boolean[] a_fullModeAllowed)
      throws InvalidConfigurationException {
    create(a_types, a_argTypes, a_nodeSets, a_minDepths, a_maxDepths,
           a_maxNodes, a_fullModeAllowed, new DefaultProgramCreator());
  }

  /**
   * Creates a population.
   *
   * @param a_types the type for each chromosome, the length of the array
   * represents the number of chromosomes
   * @param a_argTypes the types of the arguments to each chromosome, must be an
   * array of arrays, the first dimension of which is the number of chromosomes
   * and the second dimension of which is the number of arguments to the
   * chromosome
   * @param a_nodeSets the nodes which are allowed to be used by each chromosome,
   * must be an array of arrays, the first dimension of which is the number of
   * chromosomes and the second dimension of which is the number of nodes
   * @param a_minDepths contains the minimum depth allowed for each chromosome
   * @param a_maxDepths contains the maximum depth allowed for each chromosome
   * @param a_maxNodes reserve space for a_maxNodes number of nodes
   * @param a_fullModeAllowed array of boolean values. For each chromosome there
   * is one value indicating whether the full mode for creating chromosome
   * generations during evolution is allowed (true) or not (false)
   * @param a_programCreator service to create new programs with
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.2.2
   */
  public void create(Class[] a_types, Class[][] a_argTypes,
                     CommandGene[][] a_nodeSets, int[] a_minDepths,
                     int[] a_maxDepths, int a_maxNodes,
                     boolean[] a_fullModeAllowed,
                     IProgramCreator a_programCreator)
      throws InvalidConfigurationException {
    create(a_types, a_argTypes, a_nodeSets, a_minDepths, a_maxDepths,
           a_maxNodes, a_fullModeAllowed, a_programCreator, 0);
  }

  /**
   * Creates a population.
   *
   * @param a_types the type for each chromosome, the length of the array
   * represents the number of chromosomes
   * @param a_argTypes the types of the arguments to each chromosome, must be an
   * array of arrays, the first dimension of which is the number of chromosomes
   * and the second dimension of which is the number of arguments to the
   * chromosome
   * @param a_nodeSets the nodes which are allowed to be used by each chromosome,
   * must be an array of arrays, the first dimension of which is the number of
   * chromosomes and the second dimension of which is the number of nodes
   * @param a_minDepths contains the minimum depth allowed for each chromosome
   * @param a_maxDepths contains the maximum depth allowed for each chromosome
   * @param a_maxNodes reserve space for a_maxNodes number of nodes
   * @param a_fullModeAllowed array of boolean values. For each chromosome there
   * is one value indicating whether the full mode for creating chromosome
   * generations during evolution is allowed (true) or not (false)
   * @param a_programCreator service to create new programs with
   * @param a_offset start index for new programs to put into the configuration
   *
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.3.3
   */
  public void create(Class[] a_types, Class[][] a_argTypes,
                     CommandGene[][] a_nodeSets, int[] a_minDepths,
                     int[] a_maxDepths, int a_maxNodes,
                     boolean[] a_fullModeAllowed,
                     IProgramCreator a_programCreator,
                     int a_offset)
      throws InvalidConfigurationException {
    int divisor;
    if (m_popSize < 2) {
      divisor = 1;
    }
    else {
      divisor = m_popSize - 1;
    }
    int genNr = getGPConfiguration().getGenerationNr();
    int genI = new Random().nextInt(m_popSize);
    RandomGenerator generator = getGPConfiguration().getRandomGenerator();
    int minDepth = getGPConfiguration().getMinInitDepth();
    int maxDepth = getGPConfiguration().getMaxInitDepth();
    for (int i = a_offset; i < m_popSize; i++) {
      IGPProgram program = null;
      // Vary depth dependent on run index.
      // ----------------------------------
      /**@todo add element of randomness*/
      int depth = minDepth
          + (maxDepth - minDepth) * i
          / divisor;

      // Create new GP program.
      // ----------------------
      int tries = 0;
      int maxTries = getGPConfiguration().getProgramCreationMaxtries();
      do {
        try {
          // Randomize grow option as growing produces a valid program
          // more likely than the full mode.
          // ---------------------------------------------------------
          boolean grow;
          if (i % 2 == 0 || generator.nextInt(8) > 6) {
            grow = true;
          }
          else {
            grow = false;
          }
          program = create(i, a_types, a_argTypes, a_nodeSets,
                           a_minDepths, a_maxDepths, depth, grow,
                           a_maxNodes, a_fullModeAllowed, tries,
                           a_programCreator);
          if (program != null && getGPConfiguration().getPrototypeProgram() == null) {
            // Remember a prototype of a valid program in case generation
            // cannot find a valid program within some few tries
            // --> then clone the prototype.
            // Necessary if the maxNodes parameter is chosen small or a
            // validator is used which is quite restrictive.
            // ----------------------------------------------------------
            getGPConfiguration().setPrototypeProgram(program);
            LOGGER.info("Prototype program set");
          }
          else if (genNr % 5 == 0 && genNr > 0 && i == genI) {
              /**@todo 5: make configurable*/
            // Set prototype to new value after every some evolutions.
            // -------------------------------------------------------
            double protoFitness = getGPConfiguration().getPrototypeProgram().
                getFitnessValue();
            if (getGPConfiguration().getGPFitnessEvaluator().isFitter(program.
                getFitnessValue(), protoFitness)) {
              getGPConfiguration().setPrototypeProgram(program);
            }
          }
          break;
        } catch (IllegalStateException iex) {
          if (depth < maxDepth) {
            depth = depth + generator.nextInt(2);
          }
          else {
            depth = depth - generator.nextInt(4);
            if (depth < minDepth) {
                depth = minDepth;
            }
          }
          tries++;
          if (maxTries >=0 && tries > maxTries || (i > a_offset && tries > 40)) {
            IGPProgram prototype = getGPConfiguration().getPrototypeProgram();
            if (prototype != null) {
              ICloneHandler cloner = getGPConfiguration().getJGAPFactory().
                  getCloneHandlerFor(prototype, null);
              if (cloner != null) {
                try {
                  program = (IGPProgram) cloner.perform(prototype, null, null);
                  /**@todo only output once*/
                  LOGGER.warn("Prototype program reused because random"
                              + " program did not satisfy constraints");
                  break;
                } catch (Exception ex) {
                  // Rethrow original error.
                  // -----------------------
                  throw iex;
                }
              }
              else {
                LOGGER.warn("Warning: no clone handler found for"
                            + " prototype program type "
                            + prototype);
              }
            }
            // Rethrow original error.
            // -----------------------
            throw iex;
          }
        }
      } while (true);
      setGPProgram(i, program);
    }
    setChanged(true);
  }

  /**
   * Creates a population.
   *
   * @param a_types Class[]
   * @param a_argTypes Class[][]
   * @param a_nodeSets CommandGene[][]
   * @param a_minDepths int[]
   * @param a_maxDepths int[]
   * @param a_depth int
   * @param a_grow boolean
   * @param a_maxNodes int
   * @param a_fullModeAllowed boolean[]
   * @param a_tries int
   * @return IGPProgram
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.2.2
   */
  public IGPProgram create(Class[] a_types, Class[][] a_argTypes,
                           CommandGene[][] a_nodeSets, int[] a_minDepths,
                           int[] a_maxDepths, int a_depth, boolean a_grow,
                           int a_maxNodes, boolean[] a_fullModeAllowed,
                           int a_tries)
      throws InvalidConfigurationException {
    return create(0, a_types, a_argTypes, a_nodeSets, a_minDepths, a_maxDepths,
                  a_depth, a_grow, a_maxNodes, a_fullModeAllowed, a_tries);
  }

  /**
   *
   * @param a_programIndex int
   * @param a_types Class[]
   * @param a_argTypes Class[][]
   * @param a_nodeSets CommandGene[][]
   * @param a_minDepths int[]
   * @param a_maxDepths int[]
   * @param a_depth int
   * @param a_grow boolean
   * @param a_maxNodes int
   * @param a_fullModeAllowed boolean[]
   * @param a_tries int
   * @return IGPProgram
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.3
   */
  public IGPProgram create(int a_programIndex, Class[] a_types,
                           Class[][] a_argTypes,
                           CommandGene[][] a_nodeSets, int[] a_minDepths,
                           int[] a_maxDepths, int a_depth, boolean a_grow,
                           int a_maxNodes, boolean[] a_fullModeAllowed,
                           int a_tries)
      throws InvalidConfigurationException {
    return create(a_programIndex, a_types, a_argTypes, a_nodeSets, a_minDepths,
                  a_maxDepths, a_depth, a_grow, a_maxNodes, a_fullModeAllowed,
                  a_tries, new DefaultProgramCreator());
  }

  /**
   * Creates a valid IGPProgram. No fitness computation is initiated here!
   *
   * @param a_programIndex index of the program in the population
   * @param a_types the type of each chromosome, the length is the number of
   * chromosomes
   * @param a_argTypes the types of the arguments to each chromosome, must be an
   * array of arrays, the first dimension of which is the number of chromosomes
   * and the second dimension of which is the number of arguments to the
   * chromosome
   * @param a_nodeSets the nodes which are allowed to be used by each chromosome,
   * must be an array of arrays, the first dimension of which is the number of
   * chromosomes and the second dimension of which is the number of nodes
   * @param a_minDepths contains the minimum depth allowed for each chromosome
   * @param a_maxDepths contains the maximum depth allowed for each chromosome
   * @param a_depth the maximum depth of the program to create
   * @param a_grow true: grow mode, false: full mode
   * @param a_maxNodes reserve space for a_maxNodes number of nodes
   * @param a_fullModeAllowed array of boolean values. For each chromosome there
   * is one value indicating whether the full mode for creating chromosomes
   * during evolution is allowed (true) or not (false)
   * @param a_tries maximum number of tries to get a valid program
   * @param a_programCreator strategy class to create programs for the
   * population
   *
   * @return valid program
   *
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public IGPProgram create(int a_programIndex, Class[] a_types,
                           Class[][] a_argTypes,
                           CommandGene[][] a_nodeSets, int[] a_minDepths,
                           int[] a_maxDepths, int a_depth, boolean a_grow,
                           int a_maxNodes, boolean[] a_fullModeAllowed,
                           int a_tries, IProgramCreator a_programCreator)
      throws InvalidConfigurationException {
    // Is there a fittest program to be injected?
    // ------------------------------------------
    if (m_fittestToAdd != null) {
      IGPProgram program;
      ICloneHandler cloner = getGPConfiguration().getJGAPFactory().
          getCloneHandlerFor(m_fittestToAdd, null);
      if (cloner == null) {
        program = (IGPProgram) m_fittestToAdd;
      }
      else {
        try {
          program = (IGPProgram) cloner.perform(m_fittestToAdd, null, null);
        } catch (Exception ex) {
          ex.printStackTrace();
          program = (IGPProgram) m_fittestToAdd;
        }
      }
      // Clear out the fittest program to add as it just has been added.
      // ---------------------------------------------------------------
      m_fittestToAdd = null;
      return program;
    }
    else {
      // Create new GP program.
      // ----------------------
      IGPProgram program;
      program = a_programCreator.create(getGPConfiguration(), a_programIndex,
                                        a_types, a_argTypes, a_nodeSets,
                                        a_minDepths, a_maxDepths, a_maxNodes,
                                        a_depth, a_grow, a_tries,
                                        a_fullModeAllowed);
      return program;
    }
  }

  /**
   * @return fixed size of the population
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public int getPopSize() {
    return m_popSize;
  }

  /**
   * @return the GPConfiguration set
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public GPConfiguration getGPConfiguration() {
    return m_config;
  }

  /**
   * Sets the given GPProgram at the given index in the list of GPProgram's.
   * If the given index is exceeding the list by one, the chromosome is
   * appended.
   *
   * @param a_index the index to set the GPProgram in
   * @param a_program the GPProgram to be set
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public void setGPProgram(final int a_index, final IGPProgram a_program) {
    synchronized (m_programs) {
      m_programs[a_index] = a_program;
    }
    setChanged(true);
  }

  public IGPProgram getGPProgram(int a_index) {
    return m_programs[a_index];
  }

  /**
   * Sets the GPPrograms of the given population to this population.
   *
   * @param a_pop the population to use as template
   *
   * @author Klaus Meffert
   * @since 3.3.3
   */
  public void setGPPrograms(final GPPopulation a_pop) {
    synchronized (m_programs) {
      m_programs = a_pop.m_programs;
      m_popSize = m_programs.length;
    }
    setChanged(true);
  }

  /**
   * Sets the GPPrograms of the given array to this population.
   *
   * @param a_progs the programs to set
   *
   * @author Klaus Meffert
   * @since 3.3.3
   */
  public void setGPPrograms(final IGPProgram[] a_progs) {
    synchronized (m_programs) {
      m_programs = a_progs;
      m_popSize = m_programs.length;
    }
    setChanged(true);
  }

  /**
   * Sets the GPPrograms of the given population to this population.
   *
   * @param a_pop the population to use as template
   *
   * @author Klaus Meffert
   * @since 3.3.3
   */
  public void copyGPPrograms(final GPPopulation a_pop) {
    int size = a_pop.size();
    synchronized (m_programs) {
      for (int i = 0; i < size; i++) {
        m_programs[i] = a_pop.getGPProgram(i);
      }
    }
    setChanged(true);
  }

  /**
   * Sets the GPPrograms of the given population to this population.
   *
   * @param a_pop the population to use as template
   *
   * @author Klaus Meffert
   * @since 3.3.3
   */
  public void setGPPrograms(final List a_pop) {
    synchronized (m_programs) {
      int size = a_pop.size();
      m_programs = new GPProgram[size];
      for (int i = 0; i < size; i++) {
        m_programs[i] = (IGPProgram) a_pop.get(i);
      }
    }
    setChanged(true);
  }

  public IGPProgram[] getGPPrograms() {
    return m_programs;
  }

  public int size() {
    return m_programs.length;
  }

  /**
   * Determines the fittest GPProgram in the population (the one with the
   * highest fitness value) and memorizes it. This is an optimized version
   * compared to calling determineFittesPrograms(1).
   *
   * @return the fittest GPProgram of the population
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public IGPProgram determineFittestProgram() {
    if (!m_changed && m_fittestProgram != null) {
      return m_fittestProgram;
    }
    double bestFitness = -1.0d;
    IGPFitnessEvaluator evaluator = getGPConfiguration().getGPFitnessEvaluator();
    double fitness;
    for (int i = 0; i < m_programs.length && m_programs[i] != null; i++) {
      IGPProgram program = m_programs[i];
      try {
        fitness = program.getFitnessValue();
      } catch (IllegalStateException iex) {
        fitness = Double.NaN;
      }
      if (!Double.isNaN(fitness) &&
          Math.abs(GPFitnessFunction.NO_FITNESS_VALUE - fitness) > DELTA) {
        if (m_fittestProgram == null || evaluator.isFitter(fitness, bestFitness)) {
          bestFitness = fitness;
          m_fittestProgram = program;
        }
      }
    }
    setChanged(false);
    if (m_fittestProgram != null) {
      IJGAPFactory factory = getGPConfiguration().getJGAPFactory();
      if (factory == null) {
        throw new IllegalStateException("JGAPFactory must not be null!");
      }
      ICloneHandler cloner = factory.getCloneHandlerFor(m_fittestProgram, null);
      if (cloner != null) {
        try {
          m_fittestProgram = (IGPProgram) cloner.perform(m_fittestProgram, null, null);
        } catch (Exception ex) {
          ; // ignore
        }
      }
    }
    return m_fittestProgram;
  }

  /**
   * Determines the fittest GPProgram in the population, but only considers
   * programs with already computed fitness value.
   *
   * @return the fittest GPProgram of the population
   *
   * @author Klaus Meffert
   * @since 3.2
   */
  public IGPProgram determineFittestProgramComputed() {
    double bestFitness = -1.0d;
    IGPFitnessEvaluator evaluator = getGPConfiguration().getGPFitnessEvaluator();
    double fitness;
    IGPProgram fittest = null;
    for (int i = 0; i < m_programs.length && m_programs[i] != null; i++) {
      IGPProgram program = m_programs[i];
      if (program instanceof GPProgramBase) {
        GPProgramBase program1 = (GPProgramBase) program;
        fitness = program1.getFitnessValueDirectly();
      }
      else {
        fitness = program.getFitnessValue();
      }
      if (Math.abs(fitness - FitnessFunction.NO_FITNESS_VALUE) >
          FitnessFunction.DELTA) {
        if (fittest == null || evaluator.isFitter(fitness, bestFitness)) {
          fittest = program;
          bestFitness = fitness;
        }
      }
    }
    return fittest;
  }

  /**
   * Sorts the GPPrograms list and returns the fittest n GPPrograms in
   * the population.
   *
   * @param a_numberOfPrograms number of top performer GPPrograms to be
   * returned
   * @return list of the fittest n GPPrograms of the population, or the fittest
   * x GPPrograms with x = number of GPPrograms in case n > x.
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public List determineFittestChromosomes(final int a_numberOfPrograms) {
    int numberOfChromosomes = Math.min(a_numberOfPrograms, m_programs.length);
    if (numberOfChromosomes <= 0) {
      return null;
    }
    if (!m_changed && m_sorted) {
      return Arrays.asList(m_programs).subList(0, numberOfChromosomes);
    }
    // Sort the list of chromosomes using the fitness comparator
    sortByFitness();
    // Return the top n chromosomes
    return Arrays.asList(m_programs).subList(0, numberOfChromosomes);
  }

  /**
   * Sorts the programs within the population according to their fitness
   * value using GPProgramFitnessComparator.
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public void sortByFitness() {
    // The following construction could be cached but wrt that the
    // evaluator registered with the configuration could change
    // --> Don't cache it!
    sort(new GPProgramFitnessComparator(getGPConfiguration().
                                        getGPFitnessEvaluator()));
    setChanged(false);
    setSorted(true);
    m_fittestProgram = m_programs[0];
  }

  public float[] getFitnessRanks() {
    return m_fitnessRank;
  }

  public float getFitnessRank(int a_index) {
    return m_fitnessRank[a_index];
  }

  /**
   * Mark that for the population the fittest program may have changed.
   *
   * @param a_changed true: population's fittest program may have changed,
   * false: fittest program evaluated earlier is still valid
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  protected void setChanged(final boolean a_changed) {
    m_changed = a_changed;
    setSorted(false);
  }

  /**
   * @return true: population's programs (maybe) were changed,
   * false: not changed for sure
   *
   * @since 3.0
   */
  public boolean isChanged() {
    return m_changed;
  }

  /**
   * Mark the population as sorted.
   * @param a_sorted true: mark population as sorted
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  protected void setSorted(final boolean a_sorted) {
    m_sorted = a_sorted;
  }

  /**
   * This method is not producing symmetric results as -1 is more often returned
   * than 1 (see description of return value).
   *
   * @param a_pop the other population to compare
   * @return 1: a_pop is null or having fewer programs or equal number
   * of programs but at least one not contained. 0: both populations
   * containing exactly the same programs. -1: this population contains fewer
   * programs than a_pop
   *
   * @author Klaus Meffert
   * @since 2.6
   */
  public int compareTo(Object a_pop) {
    GPPopulation other = (GPPopulation) a_pop;
    if (a_pop == null) {
      return 1;
    }
    int size1 = size();
    int size2 = other.size();
    if (size1 != size2) {
      if (size1 < size2) {
        return -1;
      }
      else {
        return 1;
      }
    }
    IGPProgram[] progs2 = other.getGPPrograms();
    for (int i = 0; i < size1; i++) {
      if (!containedInArray(progs2, m_programs[i])) {
        return 1;
      }
    }
    return 0;
  }

  /**
   * Checks if a program is contained within an array of programs. Assumes that
   * in the array no element will follow after the first null element.
   *
   * @param a_progs the array to search thru
   * @param a_prog the program to find
   * @return true: program found in array via equals-method
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  protected boolean containedInArray(IGPProgram[] a_progs, IGPProgram a_prog) {
    for (int i = 0; i < a_progs.length; i++) {
      if (a_progs[i] == null) {
        return false;
      }
      if (a_progs[i].equals(a_prog)) {
        return true;
      }
    }
    return false;
  }

  /**
   * The equals-method.
   *
   * @param a_pop the population instance to compare with
   * @return true: given object equal to comparing one
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public boolean equals(Object a_pop) {
    try {
      return compareTo(a_pop) == 0;
    } catch (ClassCastException e) {
      // If the other object isn't an Population instance
      // then we're not equal.
      // ------------------------------------------------
      return false;
    }
  }

  /**
   * Adds a GP program to this Population. Does nothing when given null.
   * The injection is actually executed in method create(..)
   *
   * @param a_toAdd the program to add
   *
   * @author Klaus Meffert
   * @since 3.2
   */
  public void addFittestProgram(final IGPProgram a_toAdd) {
    if (a_toAdd != null) {
      m_fittestToAdd = a_toAdd;
    }
  }

  /**
   * Clears the list of programs. Normally, this should not be necessary.
   * But especially in distributed computing, a fresh population has to be
   * provided sometimes.
   *
   * @author Klaus Meffert
   * @since 3.2
   */
  public void clear() {
    for (int i = 0; i < m_programs.length; i++) {
      m_programs[i] = null;
    }
    m_changed = true;
    m_sorted = true;
    m_fittestProgram = null;
  }

  public boolean isFirstEmpty() {
    if (size() < 1) {
      return true;
    }
    if (m_programs[0] == null) {
      return true;
    }
    return false;
  }

  /**
   * @return the persistent representation of the population, including all
   * GP programs
   *
   * @author Klaus Meffert
   * @since 3.2.3
   */
  public String getPersistentRepresentation() {
    StringBuffer b = new StringBuffer();
    for (IGPProgram program : m_programs) {
      b.append(GPPROGRAM_DELIMITER_HEADING);
      b.append(encode(
          program.getClass().getName() +
          GPPROGRAM_DELIMITER +
          program.getPersistentRepresentation()));
      b.append(GPPROGRAM_DELIMITER_CLOSING);
    }
    return b.toString();
  }

  protected String encode(String a_string) {
    return StringKit.encode(a_string);
  }

  protected String decode(String a_string) {
    return StringKit.decode(a_string);
  }
}

 

Ads help maintain this website.