use std::{collections::HashSet, mem};
use ndarray::prelude::*;
use rand::seq::IteratorRandom;
use rand::Rng;
use crate::repro_rng::thread_rng;
pub fn swap_one<T, D>(array1: &mut Array<T, D>, array2: &mut Array<T, D>)
where
D: Dimension,
{
debug_assert_eq!(array1.len(), array2.len());
let target = thread_rng().gen_range(0..array1.len());
let mut i: usize = 0;
azip!((a in array1, b in array2) {
if i == target {
mem::swap(a, b);
}
i += 1;
});
}
pub fn swap_n<T, D>(n_swaps: usize, array1: &mut Array<T, D>, array2: &mut Array<T, D>)
where
D: Dimension,
{
debug_assert_eq!(array1.len(), array2.len());
assert!(
n_swaps <= array1.len(),
"n_swaps must be less than or equal to the array size"
);
let targets: HashSet<usize> = {
let mut hs = HashSet::with_capacity(n_swaps);
for target in (0..array1.len()).choose_multiple(&mut thread_rng(), n_swaps) {
debug_assert!(hs.insert(target));
}
hs
};
let mut i: usize = 0;
azip!((a in array1, b in array2) {
if targets.contains(&i) {
mem::swap(a, b);
}
i += 1;
});
}
pub fn swap_each_random<T, D>(indpb: f64, array1: &mut Array<T, D>, array2: &mut Array<T, D>)
where
D: Dimension,
{
let mut rng = thread_rng();
azip!((a in array1, b in array2) {
if rng.gen_bool(indpb) {
mem::swap(a, b);
}
})
}
pub fn uniform<T, D>(array1: &mut Array<T, D>, array2: &mut Array<T, D>)
where
T: Clone,
D: Dimension,
{
uniform_with_ratio(0.5, array1, array2);
}
pub fn uniform_with_ratio<T, D>(
mixing_ratio: f64,
array1: &mut Array<T, D>,
array2: &mut Array<T, D>,
) where
T: Clone,
D: Dimension,
{
let mut rng = thread_rng();
azip!((a in array1, b in array2) {
let a_choice = rng.gen_bool(mixing_ratio);
let b_choice = rng.gen_bool(mixing_ratio);
if a_choice && !b_choice {
mem::swap(a, b);
} else if !a_choice && !b_choice {
b.clone_from(&a);
} else if a_choice && b_choice {
a.clone_from(&b);
} });
}
pub fn one_point<T, D>(array1: &mut Array<T, D>, array2: &mut Array<T, D>)
where
D: Dimension,
{
n_point(1, array1, array2);
}
pub fn two_point<T, D>(array1: &mut Array<T, D>, array2: &mut Array<T, D>)
where
D: Dimension,
{
n_point(2, array1, array2);
}
pub fn n_point<T, D>(n_pivots: usize, array1: &mut Array<T, D>, array2: &mut Array<T, D>)
where
D: Dimension,
{
debug_assert_eq!(array1.len(), array2.len());
assert!(
n_pivots < array1.len(),
"n_pivots must be less than the array size"
);
let pivots: HashSet<usize> = {
let mut hs = HashSet::with_capacity(n_pivots);
for pivot in (0..array1.len() - 1).choose_multiple(&mut thread_rng(), n_pivots) {
hs.insert(pivot);
}
hs
};
let mut i: usize = 0;
let mut swap = false;
azip!((a in array1, b in array2) {
if swap {
mem::swap(a, b);
}
if pivots.contains(&i) {
swap = !swap;
}
i += 1;
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_swap_one() {
let mut a =
Array::from_shape_vec((3, 3), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
.unwrap();
let mut b = -a.clone();
swap_one(&mut a, &mut b);
assert_eq!(count_neg(&a), 1);
assert_eq!(count_neg(&b), 8);
}
#[test]
fn test_swap_n() {
let mut a =
Array::from_shape_vec((3, 3), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
.unwrap();
let mut b = -a.clone();
let n = thread_rng().gen_range(0..=9);
swap_n(n, &mut a, &mut b);
assert_eq!(count_neg(&a), n);
assert_eq!(count_neg(&b), 9 - n);
}
fn count_neg<D: Dimension>(arr: &Array<f64, D>) -> usize {
arr.mapv(|x| if x < 0.0 { 1 } else { 0 }).sum()
}
#[test]
fn test_n_point() {
let mut a =
Array::from_shape_vec((3, 3), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
.unwrap();
let mut b = -a.clone();
n_point(3, &mut a, &mut b);
}
}