use rand::distributions::{Distribution, Uniform}; use rand::{Rng, SeedableRng}; use rand_pcg::Pcg32; /// A random number generator. #[derive(Clone)] pub struct Random { /// The seed of the random number generator. seed: u128, /// A float distribution. distribution: Uniform, /// The underlying RNG. PCG for fast, hard to predict random number generation. random_gen: Pcg32, } impl Default for Random { /// The default for the RNG uses the nanoseconds since epoch as seed. fn default() -> Self { let seed = chrono::Utc::now().timestamp_nanos() as u128; Random::new(seed) } } impl Random { /// Creates a new RNG with a specific seed. pub fn new(seed: u128) -> Self { Random { seed, distribution: Uniform::from(0.0..1.0), random_gen: Pcg32::from_seed(seed.to_be_bytes()), } } /// The seed used for the RNG. pub fn get_seed(&self) -> u128 { self.seed } /// Get a random 32 bit integer between minimal and maximal 32 bit integer pub fn get(&mut self) -> i32 { self.random_gen.gen() } /// Get a random 32 bit signed integer between 0 and max. If max equals 0, always returns 0. pub fn get_max(&mut self, max: i32) -> i32 { if max <= 0 { return 0; } Uniform::from(0..max).sample(&mut self.random_gen) } /// Get a random 32 bit signed integer between min and max. If max is equal or less than min, /// always returns min. pub fn get_between(&mut self, min: i32, max: i32) -> i32 { if max <= min { return min; } Uniform::from(min..max).sample(&mut self.random_gen) } /// Get a random 32 bit integer unsigned between 0 and maximal 32 bit unsigned int. pub fn get_unsigned(&mut self) -> u32 { self.random_gen.gen() } /// Get a random 32 bit signed integer between 0 and max. If max equals 0, always returns 0. pub fn get_max_unsigned(&mut self, max: u32) -> u32 { if max == 0 { return 0; } Uniform::from(0..max).sample(&mut self.random_gen) } /// Get a random 32 bit unsigned integer between min and max. If max is equal or less than min, /// always returns min. pub fn get_between_unsigned(&mut self, min: u32, max: u32) -> u32 { if max <= min { return min; } Uniform::from(min..max).sample(&mut self.random_gen) } /// Gets a random 32 bit float between 0.0 and 1.0 pub fn get_float(&mut self) -> f32 { self.get_double() as f32 } /// Gets a random 64 bit float between 0.0 and 1.0 pub fn get_double(&mut self) -> f64 { self.distribution.sample(&mut self.random_gen) } } #[cfg(test)] mod tests { use std::hint::black_box; use test::Bencher; use crate::utils::random::Random; extern crate test; #[test] #[cfg_attr(miri, ignore)] fn create_random() { let _default = Random::default(); let empty = Random::new(100); assert_eq!(empty.get_seed(), 100); } #[test] fn get_random_with_seed() { let mut v = Random::new(10); assert_eq!(v.get(), 1755576946); assert_eq!(v.get(), 1254514019); assert_eq!(v.get(), 1735834837); assert_eq!(v.get(), 51079449); assert_eq!(v.get(), 506997516); assert_eq!(v.get(), -173527621); assert_eq!(v.get(), 683138464); assert_eq!(v.get(), 580236580); } #[test] fn get_random_unsigned_with_seed() { let mut v = Random::new(10); assert_eq!(v.get_unsigned(), 1755576946); assert_eq!(v.get_unsigned(), 1254514019); assert_eq!(v.get_unsigned(), 1735834837); assert_eq!(v.get_unsigned(), 51079449); assert_eq!(v.get_unsigned(), 506997516); assert_eq!(v.get_unsigned(), 4121439675); assert_eq!(v.get_unsigned(), 683138464); assert_eq!(v.get_unsigned(), 580236580); } #[test] fn get_random_with_limit_with_seed() { let mut v = Random::new(10); assert_eq!(v.get_max(10), 4); assert_eq!(v.get_max(10), 2); assert_eq!(v.get_max(10), 4); assert_eq!(v.get_max(10), 0); assert_eq!(v.get_max(10), 1); assert_eq!(v.get_max(10), 9); assert_eq!(v.get_max(10), 1); assert_eq!(v.get_max(10), 1); assert_eq!(v.get_max(2), 0); assert_eq!(v.get_max(2), 1); assert_eq!(v.get_max(2), 1); assert_eq!(v.get_max(2), 1); assert_eq!(v.get_max(2), 0); assert_eq!(v.get_max(2), 1); assert_eq!(v.get_max(2), 0); assert_eq!(v.get_max(2), 0); assert_eq!(v.get_max(2), 1); } #[test] fn get_random_unsigned_with_limit_with_seed() { let mut v = Random::new(10); assert_eq!(v.get_max_unsigned(4121439675), 1684647164); } #[test] fn get_random_with_limit_with_range() { let mut v = Random::new(10); assert_eq!(v.get_between(10, 30), 18); assert_eq!(v.get_between(10, 30), 15); assert_eq!(v.get_between(10, 30), 18); assert_eq!(v.get_between(10, 30), 10); assert_eq!(v.get_between(10, 30), 12); assert_eq!(v.get_between(10, 30), 29); assert_eq!(v.get_between(10, 30), 13); assert_eq!(v.get_between(10, 30), 12); } #[test] fn get_random_unsigned_with_limit_with_range() { let mut v = Random::new(10); assert_eq!(v.get_between_unsigned(4121439675, 4121439678), 4121439676); } #[test] fn get_random_float() { let mut v = Random::new(10); assert_eq!(v.get_float(), 0.2920893); } #[test] fn get_random_double() { let mut v = Random::new(10); assert_eq!(v.get_double(), 0.2920893066117427); } #[test] fn get_random_clone_then_continue() { let mut v = Random::new(10); assert_eq!(v.get_unsigned(), 1755576946); let mut clone = v.clone(); assert_eq!(clone.get_unsigned(), 1254514019); assert_eq!(v.get_unsigned(), 1254514019); } #[test] #[cfg_attr(miri, ignore)] fn check_random_distribution() { let mut v = Random::new(10); const AMOUNT: usize = 100_000; let mut arr: [i32; AMOUNT] = [0; AMOUNT]; for i in 0..AMOUNT { arr[i] = v.get_between(0, 2); } let mut num_zeros = 0; let mut num_ones = 0; for v in arr.iter() { if *v == 0 { num_zeros += 1; } else if *v == 1 { num_ones += 1; } } let div = num_zeros as f32 / num_ones as f32; assert_approx_eq::assert_approx_eq!(div, 1.0, 0.01); } #[test] #[cfg_attr(miri, ignore)] fn check_random_distribution_0_to_3() { let mut v = Random::new(10); const AMOUNT: usize = 100_000; let mut arr: [i32; AMOUNT] = [0; AMOUNT]; for i in 0..AMOUNT { arr[i] = v.get_between(0, 3); } let mut num_zeros = 0; let mut num_ones = 0; let mut num_twos = 0; for v in arr.iter() { if *v == 0 { num_zeros += 1; } else if *v == 1 { num_ones += 1; } else if *v == 2 { num_twos += 1; } } assert_approx_eq::assert_approx_eq!(num_zeros as f32 / num_ones as f32, 1.0, 0.01); assert_approx_eq::assert_approx_eq!(num_zeros as f32 / num_twos as f32, 1.0, 0.01); assert_approx_eq::assert_approx_eq!(num_ones as f32 / num_twos as f32, 1.0, 0.01); } #[bench] #[cfg_attr(miri, ignore)] fn bench_1000_random_ints_between(b: &mut Bencher) { b.iter(|| { let mut random = Random::new(10); for _ in 0..1000 { black_box(random.get_between(0, 100)); } }); } }