/**
   * An evaluator that performs coevolutionary evaluation. Like SimpleEvaluator, it applies
   * evolution pipelines, one per thread, to various subchunks of a new population.
   */
  public void evaluatePopulation(final EvolutionState state) {
    int numinds[] = new int[state.evalthreads];
    int from[] = new int[state.evalthreads];
    boolean[] assessFitness = new boolean[state.population.subpops.length];
    for (int i = 0; i < assessFitness.length; i++)
      assessFitness[i] = true; // update everyone's fitness in preprocess and postprocess

    for (int y = 0; y < state.evalthreads; y++) {
      // figure numinds
      if (y < state.evalthreads - 1) // not last one
      numinds[y] = state.population.subpops[0].individuals.length / state.evalthreads;
      else
        numinds[y] =
            state.population.subpops[0].individuals.length / state.evalthreads
                + (state.population.subpops[0].individuals.length
                    - (state.population.subpops[0].individuals.length / state.evalthreads)
                        * state.evalthreads);
      // figure from
      from[y] = (state.population.subpops[0].individuals.length / state.evalthreads) * y;
    }

    randomizeOrder(state, state.population.subpops[0].individuals);

    GroupedProblemForm prob = (GroupedProblemForm) (p_problem.clone());

    prob.preprocessPopulation(
        state, state.population, assessFitness, style == STYLE_SINGLE_ELIMINATION);

    switch (style) {
      case STYLE_SINGLE_ELIMINATION:
        evalSingleElimination(state, state.population.subpops[0].individuals, 0, prob);
        break;
      case STYLE_ROUND_ROBIN:
        evalRoundRobin(state, from, numinds, state.population.subpops[0].individuals, 0, prob);
        break;
      case STYLE_N_RANDOM_COMPETITORS_ONEWAY:
        evalNRandomOneWay(state, from, numinds, state.population.subpops[0].individuals, 0, prob);
        break;
      case STYLE_N_RANDOM_COMPETITORS_TWOWAY:
        evalNRandomTwoWay(state, from, numinds, state.population.subpops[0].individuals, 0, prob);
        break;
      default:
        state.output.fatal(
            "Invalid competition style in CompetitiveEvaluator.evaluatePopulation()");
    }

    prob.postprocessPopulation(
        state, state.population, assessFitness, style == STYLE_SINGLE_ELIMINATION);
  }
  /**
   * A private helper function for evalutatePopulation which evaluates a chunk of individuals in a
   * subpopulation for a given thread.
   *
   * <p>Although this method is declared public (for the benefit of a private helper class in this
   * file), you should not call it.
   *
   * @param state
   * @param numinds
   * @param from
   * @param threadnum
   * @param prob
   */
  public void evalRoundRobinPopChunk(
      final EvolutionState state,
      int from,
      int numinds,
      int threadnum,
      final Individual[] individuals,
      int subpop,
      final GroupedProblemForm prob) {
    Individual[] competition = new Individual[2];
    int[] subpops = new int[] {subpop, subpop};
    boolean[] updates = new boolean[2];
    updates[0] = updates[1] = true;
    int upperBound = from + numinds;

    // evaluate chunk of population against entire population
    // since an individual x will be evaluated against all
    // other individuals <x in other threads, only evaluate it against
    // individuals >x in this thread.
    for (int x = from; x < upperBound; x++)
      for (int y = x + 1; y < individuals.length; y++) {
        competition[0] = individuals[x];
        competition[1] = individuals[y];
        prob.evaluate(state, competition, updates, false, subpops, 0);
      }
  }
  public void evalSingleElimination(
      final EvolutionState state,
      final Individual[] individuals,
      final int subpop,
      final GroupedProblemForm prob) {
    // for a single-elimination tournament, the subpop[0] size must be 2^n for
    // some value n.  We don't check that here!  Check it in setup.

    // create the tournament array
    Individual[] tourn = new Individual[individuals.length];
    System.arraycopy(individuals, 0, tourn, 0, individuals.length);
    int len = tourn.length;
    Individual[] competition = new Individual[2];
    int[] subpops = new int[] {subpop, subpop};
    boolean[] updates = new boolean[2];
    updates[0] = updates[1] = true;

    // the "top half" of our array will be losers.
    // the bottom half will be winners.  Then we cut our array in half and repeat.
    while (len > 1) {
      for (int x = 0; x < len / 2; x++) {
        competition[0] = tourn[x];
        competition[1] = tourn[len - x - 1];

        prob.evaluate(state, competition, updates, true, subpops, 0);
      }

      for (int x = 0; x < len / 2; x++) {
        // if the second individual is better, or coin flip if equal, than we switch them around
        if (tourn[len - x - 1].fitness.betterThan(tourn[x].fitness)
            || (tourn[len - x - 1].fitness.equivalentTo(tourn[x].fitness)
                && state.random[0].nextBoolean())) {
          Individual temp = tourn[x];
          tourn[x] = tourn[len - x - 1];
          tourn[len - x - 1] = temp;
        }
      }

      // last part of the tournament: deal with odd values of len!
      if (len % 2 != 0) len = 1 + len / 2;
      else len /= 2;
    }
  }
  public void evalNRandomOneWayPopChunk(
      final EvolutionState state,
      int from,
      int numinds,
      int threadnum,
      final Individual[] individuals,
      final int subpop,
      final GroupedProblemForm prob) {
    Individual[] queue = new Individual[individuals.length];
    int len = queue.length;
    System.arraycopy(individuals, 0, queue, 0, len);

    Individual[] competition = new Individual[2];
    int subpops[] = new int[] {subpop, subpop};
    boolean[] updates = new boolean[2];
    updates[0] = true;
    updates[1] = false;
    int upperBound = from + numinds;

    for (int x = from; x < upperBound; x++) {
      competition[0] = individuals[x];
      // fill up our tournament
      for (int y = 0; y < groupSize; ) {
        // swap to end and remove
        int index = state.random[0].nextInt(len - y);
        competition[1] = queue[index];
        queue[index] = queue[len - y - 1];
        queue[len - y - 1] = competition[1];
        // if the opponent is not the actual individual, we can
        // have a competition
        if (competition[1] != individuals[x]) {
          prob.evaluate(state, competition, updates, false, subpops, 0);
          y++;
        }
      }
    }
  }
  public void evalNRandomTwoWayPopChunk(
      final EvolutionState state,
      int from,
      int numinds,
      int threadnum,
      final Individual[] individuals,
      final int subpop,
      final GroupedProblemForm prob) {

    // the number of games played for each player
    EncapsulatedIndividual[] individualsOrdered = new EncapsulatedIndividual[individuals.length];
    EncapsulatedIndividual[] queue = new EncapsulatedIndividual[individuals.length];
    for (int i = 0; i < individuals.length; i++)
      individualsOrdered[i] = new EncapsulatedIndividual(individuals[i], 0);

    Individual[] competition = new Individual[2];
    int[] subpops = new int[] {subpop, subpop};
    boolean[] updates = new boolean[2];
    updates[0] = true;
    int upperBound = from + numinds;

    for (int x = from; x < upperBound; x++) {
      System.arraycopy(individualsOrdered, 0, queue, 0, queue.length);
      competition[0] = queue[x].ind;

      // if the rest of individuals is not enough to fill
      // all games remaining for the current individual
      // (meaning that the current individual has left a
      // lot of games to play versus players with index
      // greater than his own), then it should play with
      // all. In the end, we should check that he finished
      // all the games he needs. If he did, everything is
      // ok, otherwise he should play with some other players
      // with index smaller than his own, but all these games
      // will count only for his fitness evaluation, and
      // not for the opponents' (unless allowOverEvaluations is set to true)

      // if true, it means that he has to play against all opponents with greater index
      if (individuals.length - x - 1 <= groupSize - queue[x].nOpponentsMet) {
        for (int y = x + 1; y < queue.length; y++) {
          competition[1] = queue[y].ind;
          updates[1] = (queue[y].nOpponentsMet < groupSize) || allowOverEvaluation;
          prob.evaluate(state, competition, updates, false, subpops, 0);
          queue[x].nOpponentsMet++;
          if (updates[1]) queue[y].nOpponentsMet++;
        }
      } else // here he has to play against a selection of the opponents with greater index
      {
        // we can use the queue structure because we'll just rearrange the indexes
        // but we should make sure we also rearrange the other vectors referring to the individuals

        for (int y = 0; groupSize > queue[x].nOpponentsMet; y++) {
          // swap to the end and remove from list
          int index = state.random[0].nextInt(queue.length - x - 1 - y) + x + 1;
          competition[1] = queue[index].ind;

          updates[1] = (queue[index].nOpponentsMet < groupSize) || allowOverEvaluation;
          prob.evaluate(state, competition, updates, false, subpops, 0);
          queue[x].nOpponentsMet++;
          if (updates[1]) queue[index].nOpponentsMet++;

          // swap the players (such that a player will not be considered twice)
          EncapsulatedIndividual temp = queue[index];
          queue[index] = queue[queue.length - y - 1];
          queue[queue.length - y - 1] = temp;
        }
      }

      // if true, it means that the current player needs to play some games with other players with
      // lower indexes.
      // this is an unfortunate situation, since all those players have already had their groupSize
      // games for the evaluation
      if (queue[x].nOpponentsMet < groupSize) {
        for (int y = queue[x].nOpponentsMet; y < groupSize; y++) {
          // select a random opponent with smaller index (don't even care for duplicates)
          int index;
          if (x > 0) // if x is 0, then there are no players with smaller index, therefore pick a
            // random one
            index = state.random[0].nextInt(x);
          else index = state.random[0].nextInt(queue.length - 1) + 1;
          // use the opponent for the evaluation
          competition[1] = queue[index].ind;
          updates[1] = (queue[index].nOpponentsMet < groupSize) || allowOverEvaluation;
          prob.evaluate(state, competition, updates, false, subpops, 0);
          queue[x].nOpponentsMet++;
          if (updates[1]) queue[index].nOpponentsMet++;
        }
      }
    }
  }