//! 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(&mut self, rng: &mut R); } impl SliceRandom for [T] { fn shuffle(&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}"); } }