use rand::Rng;
use crate::gene::Gene;
use super::Recombination;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(clippy::exhaustive_structs)]
pub struct SinglePoint;
impl<GENE> Recombination<Vec<GENE>> for SinglePoint
where
GENE: Gene + Clone,
{
const PARENTS: usize = 2;
const OFFSPRING: usize = 2;
#[allow(clippy::indexing_slicing)]
fn crossover<R>(
&self,
rng: &mut R,
parents: [Vec<GENE>; <Self as Recombination<Vec<GENE>>>::PARENTS],
) -> [Vec<GENE>; <Self as Recombination<Vec<GENE>>>::OFFSPRING]
where
R: Rng,
{
let [parent_one, parent_two] = parents;
assert!(!parent_one.is_empty(), "parent 1 has too few genes to cross-over");
assert!(!parent_two.is_empty(), "parent 2 has too few genes to cross-over");
let shortest_parent = parent_one.len().min(parent_two.len());
if shortest_parent == 1 {
let [mut offspring1, mut offspring2] = [parent_one, parent_two];
core::mem::swap(&mut offspring1[0], &mut offspring2[0]);
return [offspring1, offspring2];
}
let cutting_point_index = rng.gen_range(1..shortest_parent);
let mut offspring1 = Vec::with_capacity(parent_two.len());
let mut offspring2 = Vec::with_capacity(parent_one.len());
let parent1_left = &parent_one[..cutting_point_index];
let parent1_right = &parent_one[cutting_point_index..];
let parent2_left = &parent_two[..cutting_point_index];
let parent2_right = &parent_two[cutting_point_index..];
offspring1.extend(parent1_left.to_owned());
offspring1.extend(parent2_right.to_owned());
offspring2.extend(parent2_left.to_owned());
offspring2.extend(parent1_right.to_owned());
[offspring1, offspring2]
}
}
impl<GENE, const GENES: usize> Recombination<[GENE; GENES]> for SinglePoint
where
GENE: Gene + Clone,
{
const PARENTS: usize = 2;
const OFFSPRING: usize = 2;
fn crossover<R>(
&self,
rng: &mut R,
parents: [[GENE; GENES]; <Self as Recombination<[GENE; GENES]>>::PARENTS],
) -> [[GENE; GENES]; <Self as Recombination<[GENE; GENES]>>::OFFSPRING]
where
R: Rng,
{
let [parent_one, parent_two] = parents;
assert!(parent_one.len() >= 2, "parent 1 has too few genes to cross-over");
assert!(parent_two.len() >= 2, "parent 2 has too few genes to cross-over");
let cutting_point_index = rng.gen_range(1..GENES);
let mut offspring1 = parent_one.clone();
let mut offspring2 = parent_two.clone();
let parent1_right = &parent_one[cutting_point_index..];
let parent2_right = &parent_two[cutting_point_index..];
offspring1[cutting_point_index..].clone_from_slice(parent2_right);
offspring2[cutting_point_index..].clone_from_slice(parent1_right);
[offspring1, offspring2]
}
}
impl Recombination<String> for SinglePoint {
const PARENTS: usize = 2;
const OFFSPRING: usize = 2;
fn crossover<R>(
&self,
rng: &mut R,
parents: [String; <Self as Recombination<String>>::PARENTS],
) -> [String; <Self as Recombination<String>>::OFFSPRING]
where
R: Rng,
{
let [parent_one, parent_two] = parents;
let parent1_chars = parent_one.chars().collect::<Vec<_>>();
let parent2_chars = parent_two.chars().collect();
let offspring = self.crossover(rng, [parent1_chars, parent2_chars]);
offspring.map(|vec| vec.into_iter().collect::<String>())
}
}
#[cfg(test)]
mod tests {
use rand::rngs::SmallRng;
use rand::SeedableRng;
use super::Recombination;
use super::SinglePoint;
#[test]
fn vector_crossover() {
let mut rng = SmallRng::seed_from_u64(3);
let crossover = SinglePoint;
let parent1 = vec![1, 2, 3, 4];
let parent2 = vec![5, 6, 7, 8, 9, 10];
let offspring = crossover.crossover(&mut rng, [parent1, parent2]);
assert_eq!(offspring, [vec![1, 2, 7, 8, 9, 10], vec![5, 6, 3, 4]]);
}
#[test]
#[should_panic(expected = "parent 2 has too few genes to cross-over")]
fn vector_crossover_empty() {
let mut rng = SmallRng::seed_from_u64(0);
let crossover = SinglePoint;
let parent1 = (1..=10).collect();
let parent2 = vec![];
let _offspring = crossover.crossover(&mut rng, [parent1, parent2]);
}
#[test]
fn vector_crossover_same() {
let mut rng = SmallRng::seed_from_u64(0);
let crossover = SinglePoint;
let parent1 = vec![1, 2, 3];
let parent2 = vec![1, 2, 3];
let offspring = crossover.crossover(&mut rng, [parent1, parent2]);
assert_eq!(offspring[0], offspring[1]);
}
#[test]
fn vector_crossover_simple() {
let mut rng = SmallRng::seed_from_u64(0);
let crossover = SinglePoint;
let parent1 = vec![1, 2];
let parent2 = vec![3, 4];
let offspring = crossover.crossover(&mut rng, [parent1, parent2]);
assert_eq!(offspring, [vec![1, 4], vec![3, 2]]);
}
#[test]
fn array_crossover_same() {
let mut rng = SmallRng::seed_from_u64(0);
let crossover = SinglePoint;
let parent1 = [1, 2, 3];
let parent2 = [1, 2, 3];
let offspring = crossover.crossover(&mut rng, [parent1, parent2]);
assert_eq!(offspring[0], offspring[1]);
}
#[test]
fn array_crossover_simple() {
let mut rng = SmallRng::seed_from_u64(0);
let crossover = SinglePoint;
let parent1 = [1, 2];
let parent2 = [3, 4];
let offspring = crossover.crossover(&mut rng, [parent1, parent2]);
assert_eq!(offspring, [[1, 4], [3, 2]]);
}
}