use crate::error::GaError;
use crate::traits::{GeneT, LinearChromosome};
use rand::Rng;
use std::borrow::Cow;
use std::collections::HashSet;
pub fn order<U: LinearChromosome>(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(),
));
}
let ids_1: HashSet<i32> = parent_1.dna().iter().map(|g| g.id()).collect();
let ids_2: HashSet<i32> = parent_2.dna().iter().map(|g| g.id()).collect();
if ids_1.len() != len || ids_2.len() != len {
return Err(GaError::CrossoverError(
"Order crossover requires unique gene IDs in both parents (permutation)".to_string(),
));
}
crate::log_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 = U::new();
let mut child_2 = U::new();
child_1.set_dna(Cow::Owned(child_dna_1));
child_2.set_dna(Cow::Owned(child_dna_2));
crate::log_debug!(target="crossover_events", method="order"; "Order crossover finished with points ({}, {})", p1, p2);
Ok(vec![child_1, child_2])
}
pub(crate) fn ox_build_child<G: crate::traits::GeneT>(
donor: &[G],
filler: &[G],
p1: usize,
p2: usize,
) -> Result<Vec<G>, GaError> {
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.ok_or_else(|| {
GaError::CrossoverError(format!(
"Order crossover: child position {} was not filled \
— indicates non-unique gene IDs in the parents.",
i
))
})
})
.collect::<Result<Vec<_>, _>>()
}