/**
   * 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
  }
  @Override
  public void performIteration(PSO algorithm) {
    delegate.performIteration(algorithm);
    Topology<Particle> topology = algorithm.getTopology();

    // calculate vAvg
    Vector avgV =
        Vectors.mean(
            Lists.transform(
                topology,
                new Function<Particle, Vector>() {
                  @Override
                  public Vector apply(Particle f) {
                    return (Vector) f.getVelocity();
                  }
                }));

    Vector.Builder builder = Vector.newBuilder();
    for (Numeric n : avgV) {
      if (Math.abs(n.doubleValue()) > vMax.getParameter()) {
        builder.add(vMax.getParameter());
      } else {
        builder.add(n);
      }
    }

    avgV = builder.build();

    // mutation
    Particle gBest = Topologies.getBestEntity(topology, new SocialBestFitnessComparator());
    Particle mutated = gBest.getClone();
    Vector pos = (Vector) gBest.getBestPosition();
    final Bounds bounds = pos.boundsOf(0);

    pos =
        pos.plus(
            avgV.multiply(
                new Supplier<Number>() {
                  @Override
                  public Number get() {
                    return distribution.getRandomNumber() * bounds.getRange()
                        + bounds.getLowerBound();
                  }
                }));

    mutated.setCandidateSolution(pos);
    mutated.calculateFitness();

    if (gBest.getBestFitness().compareTo(mutated.getFitness()) < 0) {
      gBest.getProperties().put(EntityType.Particle.BEST_FITNESS, mutated.getBestFitness());
      gBest.getProperties().put(EntityType.Particle.BEST_POSITION, mutated.getBestPosition());
    }
  }
  /** {@inheritDoc} */
  @Override
  public Vector get(Particle particle) {
    double averageParticleVelocity = 0.0;

    Vector averageVelocity = null; // velocity.getClone();
    //        averageVelocity.reset();
    PSO pso = (PSO) AbstractAlgorithm.get();
    for (Particle p : pso.getTopology()) {
      if (averageVelocity == null) {
        averageVelocity = (Vector) p.getVelocity();
        continue;
      }
      Vector particleVelocity = (Vector) p.getVelocity();
      averageVelocity = averageVelocity.plus(particleVelocity);
      averageParticleVelocity += particleVelocity.norm();
    }
    averageVelocity = averageVelocity.divide(particle.getDimension());
    averageParticleVelocity /= particle.getDimension();

    double swarmCenterVelocity = averageVelocity.norm();
    double swarmCoherence = calculateSwarmCoherence(swarmCenterVelocity, averageParticleVelocity);

    double sigmoidValue = this.sigmoid.apply(swarmCoherence);

    Vector standardVelocity = this.delegate.get(particle);

    Vector.Builder builder = Vector.newBuilder();
    for (int i = 0; i < particle.getDimension(); ++i) {
      double coherenceVelocity =
          this.scalingFactor.getParameter()
              * sigmoidValue
              * averageVelocity.doubleValueOf(i)
              * this.randomNumber.getRandomNumber();
      builder.add(coherenceVelocity);
    }
    Vector coherence = builder.build();

    return Vectors.sumOf(standardVelocity, coherence);

    //        float social = socialRandomGenerator.nextFloat();
    //        float cognitive = cognitiveRandomGenerator.nextFloat();
    //
    //        //DistanceMeasure adm = new AbsoluteDistanceMeasure();
    //        //DistanceMeasure dm = new MetricDistanceMeasure();
    //
    //        double avgv = 0.0;
    //        double swv = 0.0;
    //        Topology<Particle> topology = ((PSO)Algorithm.get()).getTopology();
    //          Iterator<? extends Particle> it = topology.neighbourhood(null);
    //          double[] al = new double[particle.getDimension()];
    //           while (it.hasNext()) {
    //               Particle pl = it.next();
    //               double tmpv = 0.0;
    //               //double tmpsv = 0.0;
    //               for(int dim = 0; dim < particle.getDimension(); dim++) {
    //                al[dim] = al[dim]+((Vector)pl.getVelocity()).getReal(dim);
    //                   tmpv += Math.pow(((Vector)pl.getVelocity()).getReal(dim), 2);
    //               }
    //               tmpv = Math.sqrt(tmpv);
    //               avgv += tmpv;
    //           }
    //           for(int i = 0; i < particle.getDimension(); i++) {
    //            //al.set(i, ;
    //            swv += (al[i]/topology.size()) * (al[i]/topology.size());
    //        }
    //        swv = Math.sqrt(swv);
    //
    //        for (int i = 0; i < particle.getDimension(); ++i) {
    //            double tmp = 0.0;
    //            tmp = inertiaWeight.getParameter()*velocity.getReal(i)
    //                + cognitive * (bestPosition.getReal(i) - position.getReal(i)) *
    // cognitiveAcceleration.getParameter()
    //                + social * (nBestPosition.getReal(i) - position.getReal(i)) *
    // socialAcceleration.getParameter();
    //
    //            double avgdim = 0.0;
    //              it = topology.neighbourhood(null);
    //               while (it.hasNext()) {
    //                   avgdim += ((Vector)(it.next().getVelocity())).getReal(i);
    //               }
    //            avgdim /= particle.getDimension();
    //
    //            double cvelocity = MathUtil.sigmoid(swv/avgv)*avgdim*randomNumber.getCauchy();
    //
    //            System.out.println(cvelocity);
    //            tmp += cvelocity;
    //
    //            velocity.setReal(i, tmp);
    //
    //            clamp(velocity, i);
    //        }
  }
  /** {@inheritDoc} */
  @Override
  public <E extends Entity> List<E> crossover(List<E> parentCollection) {
    checkState(
        parentCollection.size() >= 3,
        "There must be a minimum of three parents to perform UNDX crossover.");
    checkState(
        numberOfOffspring > 0,
        "At least one offspring must be generated. Check 'numberOfOffspring'.");

    List<Vector> solutions = Entities.<Vector>getCandidateSolutions(parentCollection);
    List<E> offspring = Lists.newArrayListWithCapacity(numberOfOffspring);
    UniformDistribution randomParent = new UniformDistribution();
    final int k = solutions.size();
    final int n = solutions.get(0).size();

    for (int os = 0; os < numberOfOffspring; os++) {
      // get index of main parent and put its solution at the end of the list
      int parent = (int) randomParent.getRandomNumber(0.0, k);
      Collections.swap(solutions, parent, k - 1);

      List<Vector> e_zeta = new ArrayList<Vector>();

      // calculate mean of parents except main parent
      Vector g = Vectors.mean(solutions.subList(0, k - 1));

      // basis vectors defined by parents
      for (int i = 0; i < k - 1; i++) {
        Vector d = solutions.get(i).subtract(g);

        if (!d.isZero()) {
          double dbar = d.length();
          Vector e = d.orthogonalize(e_zeta);

          if (!e.isZero()) {
            e_zeta.add(e.normalize().multiply(dbar));
          }
        }
      }

      final double D = solutions.get(k - 1).subtract(g).length();

      // create the remaining basis vectors
      List<Vector> e_eta = Lists.newArrayList();
      e_eta.add(solutions.get(k - 1).subtract(g));

      for (int i = 0; i < n - e_zeta.size() - 1; i++) {
        Vector d = Vector.newBuilder().copyOf(g).buildRandom();
        e_eta.add(d);
      }

      e_eta = Vectors.orthonormalize(e_eta);

      // construct the offspring
      Vector variables = Vector.copyOf(g);

      if (!useIndividualProviders) {
        for (int i = 0; i < e_zeta.size(); i++) {
          variables =
              variables.plus(
                  e_zeta.get(i).multiply(random.getRandomNumber(0.0, sigma1.getParameter())));
        }

        for (int i = 0; i < e_eta.size(); i++) {
          variables =
              variables.plus(
                  e_eta
                      .get(i)
                      .multiply(
                          D * random.getRandomNumber(0.0, sigma2.getParameter() / Math.sqrt(n))));
        }
      } else {
        for (int i = 0; i < e_zeta.size(); i++) {
          variables =
              variables.plus(
                  e_zeta
                      .get(i)
                      .multiply(
                          new Supplier<Number>() {

                            @Override
                            public Number get() {
                              return random.getRandomNumber(0.0, sigma1.getParameter());
                            }
                          }));
        }

        for (int i = 0; i < e_eta.size(); i++) {
          variables =
              variables.plus(
                  e_eta
                      .get(i)
                      .multiply(
                          new Supplier<Number>() {

                            @Override
                            public Number get() {
                              return D
                                  * random.getRandomNumber(
                                      0.0, sigma2.getParameter() / Math.sqrt(n));
                            }
                          }));
        }
      }

      E child = (E) parentCollection.get(parent).getClone();
      child.setCandidateSolution(variables);

      offspring.add(child);
    }

    return offspring;
  }