@Override
  public Boolean f(PopulationBasedAlgorithm a, PopulationBasedAlgorithm b) {
    Particle p1 =
        (Particle) Topologies.getBestEntity(a.getTopology(), new SocialBestFitnessComparator());
    Particle p2 =
        (Particle) Topologies.getBestEntity(b.getTopology(), new SocialBestFitnessComparator());
    Vector v1 = ((Vector) p1.getBestPosition()).subtract((Vector) p1.getCandidateSolution());
    Vector v2 = ((Vector) p2.getBestPosition()).subtract((Vector) p2.getCandidateSolution());

    return v1.dot(v2) > 0;
  }
  @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());
    }
  }
  /**
   * Returns the new position
   *
   * @param particle The particle to update
   * @return the particle's new position
   */
  @Override
  public Vector get(Particle particle) {
    particle.setPositionProvider(new LinearPositionProvider());

    Vector solution = (Vector) particle.getCandidateSolution();
    Vector pBest = (Vector) particle.getBestPosition();
    Vector nBest = (Vector) particle.getNeighbourhoodBest().getBestPosition();

    Set<Vector> solutions = new HashSet<Vector>(Arrays.asList(solution, pBest, nBest));

    if (solutions.size() == 3) {
      return applyCrossover(particle, Lists.newLinkedList(solutions), mainCrossover);
    }

    Vector prevPos = (Vector) particle.getProperties().get(EntityType.PREVIOUS_SOLUTION);
    solutions.add(prevPos);

    if (solutions.size() == 3) {
      return applyCrossover(particle, Lists.newLinkedList(solutions), mainCrossover);
    }

    if (solutions.size() == 2) {
      return applyCrossover(particle, Lists.newLinkedList(solutions), alternateCrossover);
    }

    particle.setPositionProvider(new StandardPositionProvider());
    return delegate.get(particle);
  }