use crate::error::GaError;
use crate::traits::ChromosomeT;
use log::debug;
use rand::Rng;
use std::borrow::Cow;
use std::collections::HashSet;
pub fn order<U: ChromosomeT>(parent_1: &U, parent_2: &U) -> Result<Vec<U>, GaError> {
let len = parent_1.dna().len();
if len != parent_2.dna().len() {
return Err(GaError::CrossoverError(format!(
"Parents must have the same DNA length. Parent 1: {}, Parent 2: {}",
len,
parent_2.dna().len()
)));
}
if len < 3 {
return Err(GaError::CrossoverError(
"DNA length must be at least 3 for order crossover".to_string(),
));
}
debug!(target="crossover_events", method="order"; "Starting order crossover (OX)");
let mut rng = crate::rng::make_rng();
let mut p1 = rng.random_range(0..len);
let mut p2 = rng.random_range(0..len);
while p1 == p2 {
p2 = rng.random_range(0..len);
}
if p1 > p2 {
std::mem::swap(&mut p1, &mut p2);
}
let child_dna_1 = ox_build_child(parent_1.dna(), parent_2.dna(), p1, p2);
let child_dna_2 = ox_build_child(parent_2.dna(), parent_1.dna(), p1, p2);
let mut child_1 = parent_1.clone();
let mut child_2 = parent_2.clone();
child_1.set_dna(Cow::Owned(child_dna_1));
child_2.set_dna(Cow::Owned(child_dna_2));
debug!(target="crossover_events", method="order"; "Order crossover finished with points ({}, {})", p1, p2);
Ok(vec![child_1, child_2])
}
fn ox_build_child<G: crate::traits::GeneT>(
donor: &[G],
filler: &[G],
p1: usize,
p2: usize,
) -> Vec<G> {
let len = donor.len();
let mut child: Vec<Option<G>> = vec![None; len];
for i in p1..=p2 {
child[i] = Some(donor[i].clone());
}
let segment_ids: HashSet<i32> = (p1..=p2).map(|i| donor[i].id()).collect();
let mut filler_genes: Vec<G> = Vec::new();
let mut pos = (p2 + 1) % len;
for _ in 0..len {
if !segment_ids.contains(&filler[pos].id()) {
filler_genes.push(filler[pos].clone());
}
pos = (pos + 1) % len;
}
let mut filler_iter = filler_genes.into_iter();
let mut child_pos = (p2 + 1) % len;
for _ in 0..len {
if child[child_pos].is_none() {
if let Some(gene) = filler_iter.next() {
child[child_pos] = Some(gene);
}
}
child_pos = (child_pos + 1) % len;
}
child
.into_iter()
.enumerate()
.map(|(i, g)| {
g.unwrap_or_else(|| {
panic!(
"Order crossover: child position {} was not filled. \
This indicates non-unique gene IDs in the parents.",
i
)
})
})
.collect()
}