@SuppressWarnings("unchecked")
  protected KeyedChromosome<Object> performCrossover(
      KeyedChromosome<Object> parentA, KeyedChromosome<Object> parentB) {
    KeyedChromosome<Object> child = (KeyedChromosome<Object>) parentA.clone();
    Map<Object, Gene> replaced = new HashMap<Object, Gene>();
    double originalFitness = parentA.getFitness();
    Gene originalGene;
    Gene replacement;

    boolean crossedOver;
    int attempts = 0;
    for (; attempts < maxAttempts; attempts++) {
      crossedOver = false;
      replaced.clear();

      for (Object key : child.getGenes().keySet()) {
        if (coin.flip()) {
          originalGene = child.getGenes().get(key);
          replacement = parentB.getGenes().get(key).clone();

          if (!replacement.equals(originalGene)) {
            replaced.put(key, originalGene);

            child.replaceGene(key, replacement);

            crossedOver = true;
          }
        }
      }

      if (crossedOver) {
        double fitness = fitnessEvaluator.evaluate(child);

        if (fitness > originalFitness) {
          child.setFitness(fitness);

          break;
        } else {
          // revert crossover
          for (Object key : replaced.keySet()) {
            child.replaceGene(key, replaced.get(key));
          }

          // Make sure it doesn't get re-evaluated
          child.setFitness(originalFitness);
        }
      }
    }

    if (attempts >= maxAttempts) {
      log.debug(
          "Unable to find guaranteed better fitness via crossover after "
              + maxAttempts
              + " attempts.  Returning clone of first parent.");
    }

    return child;
  }
  @Override
  public List<KeyedChromosome<Object>> crossover(
      KeyedChromosome<Object> parentA, KeyedChromosome<Object> parentB) {
    List<KeyedChromosome<Object>> children = new ArrayList<KeyedChromosome<Object>>(1);

    KeyedChromosome<Object> child = performCrossover(parentA, parentB);

    // The Chromosome could be null if it's identical to one of its parents
    if (child != null) {
      children.add(child);

      if (maxGenerations > 0) {
        child.setAncestry(
            new Ancestry(
                parentA.getId(),
                parentB.getId(),
                parentA.getAncestry(),
                parentB.getAncestry(),
                maxGenerations));
      }

      parentA.increaseNumberOfChildren();
      parentB.increaseNumberOfChildren();
    }

    return children;
  }