private Gene mutateNode(NEATNodeGene mutatee) {
    double perturbRandVal = perturbRand.nextDouble();
    double mutateBias = perturbRand.nextDouble();
    NEATNodeGene mutated = mutatee;
    double newSF = mutatee.sigmoidFactor();
    double newBias = mutatee.bias();

    if (perturbRandVal < this.pPerturb) {
      newSF = mutatee.sigmoidFactor() + MathUtils.nextClampedDouble(-perturb, perturb);
      mutated =
          new NEATNodeGene(
              mutated.getInnovationNumber(),
              mutated.id(),
              newSF,
              mutated.getType(),
              mutated.bias());
    }

    if (mutateBias < this.pMutateBias) {
      newBias += MathUtils.nextClampedDouble(-biasPerturb, biasPerturb);
      mutated =
          new NEATNodeGene(
              mutated.getInnovationNumber(),
              mutated.id(),
              mutated.sigmoidFactor(),
              mutated.getType(),
              newBias);
    }

    return (mutated);
  }
  private Gene mutateLink(NEATLinkGene mutatee) {
    double perturbRandVal = perturbRand.nextDouble();
    double disableRandVal = disableRand.nextDouble();
    double newWeight;
    NEATLinkGene mutated = mutatee;

    if (perturbRandVal < this.pPerturb) {
      if (this.pWeightReplaced > perturbRand.nextDouble()) {
        newWeight = MathUtils.nextPlusMinusOne();
      } else {
        newWeight = mutatee.getWeight() + MathUtils.nextClampedDouble(-perturb, perturb);
      }
      //			newWeight = mutatee.getWeight() + MathUtils.nextClampedDouble(-PERTURB, PERTURB);
      mutated =
          new NEATLinkGene(
              mutatee.getInnovationNumber(),
              mutatee.isEnabled(),
              mutatee.getFromId(),
              mutatee.getToId(),
              newWeight);
    }

    if (disableRandVal < this.pToggle) {
      if (this.featureSelection) {
        mutated.setEnabled(!mutated.isEnabled());
      }
    }

    return (mutated);
  }
  private Gene mutateFeature(NEATFeatureGene mutatee) {
    double perturbRandVal = perturbRand.nextDouble();
    Gene mutated = mutatee;
    if (perturbRandVal < this.pPerturb) {
      mutated =
          new NEATFeatureGene(
              mutatee.getInnovationNumber(),
              mutatee.geneAsNumber().doubleValue()
                  + MathUtils.nextClampedDouble(-perturb, perturb));
    }

    return (mutated);
  }
  private void mutateAddLink(Chromosome mutatee) {
    double linkRandVal = linkRand.nextDouble();
    NEATNodeGene from;
    NEATNodeGene to;
    int rIdx;
    int i = 0;
    ArrayList links;
    ArrayList nodes;
    Gene[] genes = new Gene[mutatee.size() + 1];
    System.arraycopy(mutatee.genes(), 0, genes, 0, mutatee.genes().length);
    Gene newLink = null;

    if (linkRandVal < this.pAddLink) {
      nodes = this.candidateNodes(mutatee.genes());
      links = this.candidateLinks(mutatee.genes(), false);
      // find a new available link
      while (newLink == null && i < MAX_LINK_ATTEMPTS) {
        rIdx = linkRand.nextInt(nodes.size());
        from = ((NEATNodeGene) nodes.get(rIdx));
        rIdx = linkRand.nextInt(nodes.size());
        to = ((NEATNodeGene) nodes.get(rIdx));
        // TODO Remove
        if (from.getInnovationNumber() == 2 && to.getInnovationNumber() == 5) {
          System.out.println("a");
        }
        if (!this.linkIllegal(from, to, links)) {
          // set it to a random value
          newLink = InnovationDatabase.database().submitLinkInnovation(from.id(), to.id());
          ((NEATLinkGene) newLink).setWeight(MathUtils.nextPlusMinusOne());
          // add link between 2 unconnected nodes
          genes[genes.length - 1] = newLink;
          mutatee.updateChromosome(genes);
        }
        i++;
      }
    }
  }