mirror of
https://github.com/Magnus167/rustframe.git
synced 2025-11-20 01:56:10 +00:00
114 lines
3.1 KiB
Rust
114 lines
3.1 KiB
Rust
//! Extensions for shuffling slices with a random number generator.
|
|
//!
|
|
//! ```
|
|
//! use rustframe::random::{rng, SliceRandom};
|
|
//! let mut data = [1, 2, 3];
|
|
//! data.shuffle(&mut rng());
|
|
//! assert_eq!(data.len(), 3);
|
|
//! ```
|
|
use crate::random::Rng;
|
|
|
|
/// Trait for randomizing slices.
|
|
pub trait SliceRandom {
|
|
/// Shuffle the slice in place using the provided RNG.
|
|
fn shuffle<R: Rng>(&mut self, rng: &mut R);
|
|
}
|
|
|
|
impl<T> SliceRandom for [T] {
|
|
fn shuffle<R: Rng>(&mut self, rng: &mut R) {
|
|
for i in (1..self.len()).rev() {
|
|
let j = rng.random_range(0..(i + 1));
|
|
self.swap(i, j);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::random::{CryptoRng, Prng};
|
|
|
|
#[test]
|
|
fn test_shuffle_slice() {
|
|
let mut rng = Prng::new(3);
|
|
let mut arr = [1, 2, 3, 4, 5];
|
|
let orig = arr.clone();
|
|
arr.shuffle(&mut rng);
|
|
assert_eq!(arr.len(), orig.len());
|
|
let mut sorted = arr.to_vec();
|
|
sorted.sort();
|
|
assert_eq!(sorted, orig.to_vec());
|
|
}
|
|
|
|
#[test]
|
|
fn test_slice_shuffle_deterministic_with_prng() {
|
|
let mut rng1 = Prng::new(11);
|
|
let mut rng2 = Prng::new(11);
|
|
let mut a = [1u8, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
let mut b = a.clone();
|
|
a.shuffle(&mut rng1);
|
|
b.shuffle(&mut rng2);
|
|
assert_eq!(a, b);
|
|
}
|
|
|
|
#[test]
|
|
fn test_slice_shuffle_crypto_random_changes() {
|
|
let mut rng1 = CryptoRng::new();
|
|
let mut rng2 = CryptoRng::new();
|
|
let orig = [1u8, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
let mut a = orig.clone();
|
|
let mut b = orig.clone();
|
|
a.shuffle(&mut rng1);
|
|
b.shuffle(&mut rng2);
|
|
assert!(a != orig || b != orig, "Shuffles did not change order");
|
|
assert_ne!(a, b, "Two Crypto RNG shuffles produced same order");
|
|
}
|
|
|
|
#[test]
|
|
fn test_shuffle_single_element_no_change() {
|
|
let mut rng = Prng::new(1);
|
|
let mut arr = [42];
|
|
arr.shuffle(&mut rng);
|
|
assert_eq!(arr, [42]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_shuffles_different_results() {
|
|
let mut rng = Prng::new(5);
|
|
let mut arr1 = [1, 2, 3, 4];
|
|
let mut arr2 = [1, 2, 3, 4];
|
|
arr1.shuffle(&mut rng);
|
|
arr2.shuffle(&mut rng);
|
|
assert_ne!(arr1, arr2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_shuffle_empty_slice() {
|
|
let mut rng = Prng::new(1);
|
|
let mut arr: [i32; 0] = [];
|
|
arr.shuffle(&mut rng);
|
|
assert!(arr.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_shuffle_three_uniform() {
|
|
use std::collections::HashMap;
|
|
let mut rng = Prng::new(123);
|
|
let mut counts: HashMap<[u8; 3], usize> = HashMap::new();
|
|
for _ in 0..6000 {
|
|
let mut arr = [1u8, 2, 3];
|
|
arr.shuffle(&mut rng);
|
|
*counts.entry(arr).or_insert(0) += 1;
|
|
}
|
|
let expected = 1000.0;
|
|
let chi2: f64 = counts
|
|
.values()
|
|
.map(|&c| {
|
|
let diff = c as f64 - expected;
|
|
diff * diff / expected
|
|
})
|
|
.sum();
|
|
assert!(chi2 < 30.0, "shuffle chi-square too high: {chi2}");
|
|
}
|
|
}
|