use crate::error::GaError;
use crate::operations::crossover::order::ox_build_child;
use crate::traits::LinearChromosome;
use rand::Rng;
use std::borrow::Cow;
pub fn multi_group_ox<U: LinearChromosome>(parent_1: &U, parent_2: &U) -> Result<Vec<U>, GaError> {
let groups = parent_1.group_ranges();
if groups.is_empty() {
return Err(GaError::ConfigurationError(
"MultiUniqueChromosome must have at least one group for multi-group OX crossover. \
Check that group alphabets were supplied when constructing the chromosome."
.to_string(),
));
}
let p2_groups = parent_2.group_ranges();
if groups != p2_groups {
return Err(GaError::ConfigurationError(format!(
"MultiGroupOx: parents have different group structures. \
Parent 1 groups: {:?}, Parent 2 groups: {:?}",
groups, p2_groups
)));
}
let p1_dna = parent_1.dna();
let p2_dna = parent_2.dna();
let mut child_dna_1 = p1_dna.to_vec();
let mut child_dna_2 = p2_dna.to_vec();
let mut rng = crate::rng::make_rng();
for (start, end) in &groups {
let group_len = end - start + 1;
if group_len == 1 {
continue;
}
let (p1_pos, p2_pos) = if group_len == 2 {
(0, 1)
} else {
let mut pos1 = rng.random_range(0..group_len);
let mut pos2 = rng.random_range(0..group_len);
while pos1 == pos2 {
pos2 = rng.random_range(0..group_len);
}
if pos1 > pos2 {
std::mem::swap(&mut pos1, &mut pos2);
}
(pos1, pos2)
};
let slice_1 = ox_build_child(
&p1_dna[*start..=*end],
&p2_dna[*start..=*end],
p1_pos,
p2_pos,
)?;
let slice_2 = ox_build_child(
&p2_dna[*start..=*end],
&p1_dna[*start..=*end],
p1_pos,
p2_pos,
)?;
child_dna_1[*start..=*end].clone_from_slice(&slice_1);
child_dna_2[*start..=*end].clone_from_slice(&slice_2);
}
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));
Ok(vec![child_1, child_2])
}