/** {@inheritDoc} */
  @Override
  public void absorb(Niche algorithm) {
    for (PopulationBasedAlgorithm pba : algorithm.getPopulations()) {
      RadiusVisitor radiusVisitor = new RadiusVisitor();
      pba.accept(radiusVisitor);

      double radius = radiusVisitor.getResult().doubleValue();

      Topology<? extends Entity> mainSwarmTopology = algorithm.getMainSwarm().getTopology();
      for (int i = 0; i < mainSwarmTopology.size(); i++) {
        Entity entity = mainSwarmTopology.get(i);
        double distance =
            distanceMeasure.distance(
                entity.getCandidateSolution(),
                Topologies.getBestEntity(pba.getTopology()).getCandidateSolution());
        if (distance <= radius) {
          Particle p = (Particle) entity;
          p.setVelocityProvider(new GCVelocityProvider());
          p.setNeighbourhoodBest((Particle) Topologies.getBestEntity(pba.getTopology()));
          Topology<Particle> topology = (Topology<Particle>) pba.getTopology();
          topology.add(p);
          algorithm.getMainSwarm().getTopology().remove(entity);
        }
      }
    }
  }
  /**
   * Just reevaluate the {@link Entity entities} inside the topology. An entity's implementation
   * should handle updating anything else that is necessary, e.g. a {@link StandardParticle}'s
   * <code>personal best position</code> in the case of {@link PSO}.
   *
   * <p>{@inheritDoc}
   */
  @Override
  public void performReaction(E algorithm) {
    Topology<? extends Entity> entities = algorithm.getTopology();
    int reevaluateCount = (int) Math.floor(reevaluationRatio * entities.size());

    reevaluate(entities, reevaluateCount);
  }
  @Override
  public Topology<Particle> f(PSO pso) {
    Topology<Particle> newTopology = pso.getTopology().getClone();
    newTopology.clear();

    for (Particle p : pso.getTopology()) {
      newTopology.add(function.f(p));
    }

    return newTopology;
  }
  /**
   * Respond to environment change by re-evaluating each particle's position, personal best and
   * neighbourhood best, and reinitializing the positions of a specified percentage of particles.
   *
   * @param algorithm PSO algorithm that has to respond to environment change
   */
  @Override
  public void respond(E algorithm) {

    Topology<? extends Entity> topology = algorithm.getTopology();

    // Reevaluate current position. Update personal best (done by reevaluate()).
    Iterator<? extends Entity> iterator = topology.iterator();
    int reinitCounter = 0;
    int keepCounter = 0;
    int populationSize = algorithm.getTopology().size();
    while (iterator.hasNext()) {
      DynamicParticle current = (DynamicParticle) iterator.next();
      ZeroTransformation zt = new ZeroTransformation();

      // makes sure the charged particles are randomly positionned accross the topology
      if (reinitCounter < Math.floor(populationSize * reinitialisationRatio)
          && randomiser.nextDouble() < reinitialisationRatio
          && current != (algorithm).getTopology().getBestEntity()) {
        current.getPosition().randomize(this.randomiser);
        current
            .getProperties()
            .put(EntityType.Particle.VELOCITY, Vectors.transform(current.getVelocity(), zt));
        current
            .getProperties()
            .put(EntityType.Particle.BEST_POSITION, current.getPosition().getClone());
        ++reinitCounter;
      } // if
      else if (keepCounter > Math.floor(populationSize * (1.0 - reinitialisationRatio))
          && current != (algorithm).getTopology().getBestEntity()) {
        current.getPosition().randomize(this.randomiser);
        current
            .getProperties()
            .put(EntityType.Particle.VELOCITY, Vectors.transform(current.getVelocity(), zt));
        current
            .getProperties()
            .put(EntityType.Particle.BEST_POSITION, current.getPosition().getClone());
        ++reinitCounter;
      } // else if
      else {
        ++keepCounter;
      } // else
    }

    // Re-evaluate:
    reevaluateParticles(algorithm); // super class method
  }
  /**
   * Get a list of individuals that are suitable to be used within the recombination arithmetic
   * operator.
   *
   * @param topology The {@see net.sourceforge.cilib.entity.Topology Topology} containing the
   *     entites.
   * @return A list of unique entities.
   */
  public static List<Entity> getRandomParentEntities(Topology<? extends Entity> topology) {
    List<Entity> parents = new ArrayList<Entity>(3);

    ProbabilityDistributionFuction randomNumber = new UniformDistribution();

    int count = 0;

    while (count < 3) {
      int random = randomNumber.getRandomProvider().nextInt(topology.size());
      Entity parent = topology.get(random);
      if (!parents.contains(parent)) {
        parents.add(parent);
        count++;
      }
    }

    return parents;
  }
  /**
   * This is an Synchronous strategy:
   *
   * <ol>
   *   <li>For all particles:
   *       <ol>
   *         <li>Update the particle velocity
   *         <li>Update the particle position
   *       </ol>
   *   <li>For all particles:
   *       <ol>
   *         <li>Calculate the particle fitness
   *         <li>For all particles in the current particle's neighbourhood:
   *             <ol>
   *               <li>Update the neighbourhood best
   *             </ol>
   *       </ol>
   * </ol>
   *
   * @see
   *     net.sourceforge.cilib.PSO.IterationStrategy#performIteration(net.sourceforge.cilib.PSO.PSO)
   * @param pso The {@link PSO} to have an iteration applied.
   */
  @Override
  public void performIteration(PSO pso) {
    Topology<Particle> topology = pso.getTopology();

    for (Particle current : topology) {
      current.updateVelocity();
      current.updatePosition(); // TODO: replace with visitor (will simplify particle interface)

      boundaryConstraint.enforce(current);
    }

    Problem problem = AbstractAlgorithm.getAlgorithmList().get(0).getOptimisationProblem();

    for (Particle current : topology) {
      current.calculateFitness();
      for (Particle other : topology.neighbourhood(current)) {
        Particle p1 = current.getNeighbourhoodBest().getClone();
        Particle p2 = other.getNeighbourhoodBest().getClone();
        OptimisationSolution s1 =
            new OptimisationSolution(
                p1.getCandidateSolution().getClone(),
                problem.getFitness(p1.getCandidateSolution().getClone()));
        OptimisationSolution s2 =
            new OptimisationSolution(
                p2.getCandidateSolution().getClone(),
                problem.getFitness(p2.getCandidateSolution().getClone()));
        MOFitness fitness1 = (MOFitness) s1.getFitness();
        MOFitness fitness2 = (MOFitness) s2.getFitness();
        //                System.out.println("fitness1 = ");
        //                for (int i=0; i < fitness1.getDimension(); i++)
        //                    System.out.println(fitness1.getFitness(i).getValue());
        //
        //                System.out.println("fitness2 = ");
        //                for (int i=0; i < fitness2.getDimension(); i++)
        //                    System.out.println(fitness2.getFitness(i).getValue());
        if (fitness1.compareTo(fitness2) > 0) {
          other.setNeighbourhoodBest(current);
        }
      }
    }
  }
  /**
   * Perform an iteration of the DE algorithm defined as the DE/x/y/z implementation but including a
   * parameter adaptation step.
   *
   * @param ec The {@linkplain EC} on which to perform this iteration.
   */
  @Override
  public void performIteration(EC ec) {
    Topology<SaDEIndividual> topology = (Topology<SaDEIndividual>) ec.getTopology();

    for (int i = 0; i < topology.size(); i++) {
      SaDEIndividual current = topology.get(i);
      current.calculateFitness();

      // Create the trial vector by applying mutation
      SaDEIndividual targetEntity =
          (SaDEIndividual) targetVectorSelectionStrategy.on(topology).exclude(current).select();

      // Create the trial vector / entity
      SaDEIndividual trialEntity =
          (SaDEIndividual)
              current.getTrialVectorCreationStrategy().create(targetEntity, current, topology);

      // Create the offspring by applying cross-over
      List<SaDEIndividual> offspring =
          (List<SaDEIndividual>)
              current
                  .getCrossoverStrategy()
                  .crossover(Arrays.asList(current, trialEntity)); // Order is VERY important here!!

      // Replace the parent (current) if the offspring is better
      SaDEIndividual offspringEntity = offspring.get(0);
      boundaryConstraint.enforce(offspringEntity);
      offspringEntity.calculateFitness();

      if (offspringEntity.getFitness().compareTo(current.getFitness())
          > 0) { // the trial vector is better than the parent
        topology.set(i, offspringEntity); // Replace the parent with the offspring individual
      }

      topology.get(i).updateParameters();
    }
  }
  @Override
  public void visit(Topology<? extends Entity> topology) {
    double maxDistance = 0.0;

    Iterator<? extends Entity> k1 = topology.iterator();
    while (k1.hasNext()) {
      Entity p1 = (Entity) k1.next();
      if (p1 instanceof ChargedParticle && ((ChargedParticle) p1).getCharge() != 0) continue;
      Vector position1 = (Vector) p1.getCandidateSolution();

      Iterator<? extends Entity> k2 = topology.iterator();
      while (k2.hasNext()) {
        Entity p2 = (Entity) k2.next();
        if (p2 instanceof ChargedParticle && ((ChargedParticle) p2).getCharge() != 0) continue;
        Vector position2 = (Vector) p2.getCandidateSolution();

        double actualDistance = distanceMeasure.distance(position1, position2);
        if (actualDistance > maxDistance) maxDistance = actualDistance;
      }
    }

    // result = maxDistance;
    distance = maxDistance;
  }
  /*
   * Performs an iteration of the standard co-operative algorithm.
   * It holds a context particle, adapts the swarms to hold the context particle
   * with the appropriate dimension difference,updates the personal and global
   * bests and then updates the particles.
   */
  @Override
  public void performIteration(CooperativePSO algorithm) {
    int populationIndex = 0;
    table = new StandardDataTable();
    DataClusteringPSO pso;
    Topology newTopology;
    ClusterParticle particleWithContext;

    for (PopulationBasedAlgorithm currentAlgorithm : algorithm.getPopulations()) {

      table =
          ((SinglePopulationDataClusteringIterationStrategy)
                  ((DataClusteringPSO) currentAlgorithm).getIterationStrategy())
              .getDataset();

      if (!contextinitialised) {
        initialiseContextParticle(algorithm);
      }

      pso = ((DataClusteringPSO) currentAlgorithm);
      newTopology = ((DataClusteringPSO) currentAlgorithm).getTopology().getClone();
      newTopology.clear();

      for (ClusterParticle particle : ((DataClusteringPSO) currentAlgorithm).getTopology()) {
        clearDataPatterns(contextParticle);
        assignDataPatternsToParticle(
            (CentroidHolder) contextParticle.getCandidateSolution(), table);
        contextParticle.calculateFitness();

        particleWithContext = new ClusterParticle();
        particleWithContext.setCandidateSolution(contextParticle.getCandidateSolution().getClone());
        particleWithContext
            .getProperties()
            .put(EntityType.Particle.BEST_POSITION, particle.getBestPosition().getClone());
        particleWithContext
            .getProperties()
            .put(EntityType.Particle.BEST_FITNESS, particle.getBestFitness().getClone());
        particleWithContext
            .getProperties()
            .put(EntityType.Particle.VELOCITY, particle.getVelocity().getClone());
        particleWithContext.setNeighbourhoodBest(particle.getNeighbourhoodBest());
        ((CentroidHolder) particleWithContext.getCandidateSolution())
            .set(
                populationIndex,
                ((CentroidHolder) particle.getCandidateSolution()).get(populationIndex));
        particleWithContext
            .getProperties()
            .put(
                EntityType.Particle.Count.PBEST_STAGNATION_COUNTER,
                particle
                    .getProperties()
                    .get(EntityType.Particle.Count.PBEST_STAGNATION_COUNTER)
                    .getClone());
        particleWithContext.setCentroidInitialisationStrategy(
            particle.getCentroidInitialisationStrategyCandidate().getClone());

        clearDataPatterns(particleWithContext);
        assignDataPatternsToParticle(
            (CentroidHolder) particleWithContext.getCandidateSolution(), table);
        particleWithContext.calculateFitness();

        if (particleWithContext.getFitness().compareTo(particleWithContext.getBestFitness()) > 0) {
          particle.getProperties().put(EntityType.Particle.BEST_POSITION, particle.getPosition());
          particle.getProperties().put(EntityType.Particle.BEST_FITNESS, particle.getFitness());

          particleWithContext
              .getProperties()
              .put(EntityType.Particle.BEST_POSITION, particle.getPosition());
          particleWithContext
              .getProperties()
              .put(EntityType.Particle.BEST_FITNESS, particle.getFitness());
        }

        if (particleWithContext.getBestFitness().compareTo(contextParticle.getFitness()) > 0) {
          ((CentroidHolder) contextParticle.getCandidateSolution())
              .set(
                  populationIndex,
                  ((CentroidHolder) particle.getCandidateSolution()).get(populationIndex));
        }

        if (contextParticle.getFitness().compareTo(contextParticle.getBestFitness()) > 0) {
          contextParticle
              .getProperties()
              .put(EntityType.Particle.BEST_POSITION, contextParticle.getPosition())
              .getClone();
          contextParticle
              .getProperties()
              .put(EntityType.Particle.BEST_FITNESS, contextParticle.getFitness())
              .getClone();
        }

        newTopology.add(particleWithContext);
      }

      if (elitist) {
        contextParticle
            .getProperties()
            .put(EntityType.CANDIDATE_SOLUTION, contextParticle.getBestPosition().getClone());
        contextParticle
            .getProperties()
            .put(EntityType.FITNESS, contextParticle.getBestFitness().getClone());
      }

      pso.setTopology(newTopology);
      pso.performIteration();

      populationIndex++;
    }
  }