const population TwoPoint::crossover(const population & mates, const Problem & problem) const {
  // two-point crossover
  population children = mates;
  int_dist percent(0, 100);
  if (chance || percent(rg.engine) < int(100 * chance)) {
    int_dist gene_dist(0, children[0].size() - 1);
    int start = gene_dist(rg.engine);
    int length = gene_dist(rg.engine);
    for (Individual & child : children)
      std::rotate(child.begin(), child.begin() + start, child.end());
    for (int i = 0; i < length; i++)
      std::swap(children[0][i], children[1][i]);
  }
  // update fitnesses
  for (Individual & child : children) child.fitness = problem.fitness(child);
  return children;
}
const Individual Creep::mutate(const Problem & problem, const Individual & subject) const {
  // basic mutation by delta distribution and chance
  Individual mutant = subject; // non-const copy to mutate
  int_dist percent(0, 100);
  // distribution of real delta values, scaled for problem domain
  real_dist delta_dist(-problem.delta * (std::abs(problem.domain_min) +
					 std::abs(problem.domain_max)) / 2,
		       problem.delta * (std::abs(problem.domain_min) +
					std::abs(problem.domain_max)) / 2);
  for (parameter & gene : mutant)
    // short circuit for problem.chance == 1
    if (problem.chance || percent(rg.engine) < int(100 * problem.chance))
      mutant.mutate(gene, gene + delta_dist(rg.engine));
  // update fitness
  mutant.fitness = problem.fitness(mutant);
  return mutant;
}