268 lines
7.8 KiB
Rust
Executable File
268 lines
7.8 KiB
Rust
Executable File
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<f64>,
|
|
/// 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));
|
|
}
|
|
});
|
|
}
|
|
}
|