use rand::{self, Rng, SeedableRng, XorShiftRng};
use std::fmt::{self, Debug, Display, Formatter};
use std::sync::Mutex;
use std::thread;
lazy_static! {
static ref SEED: Mutex<Option<[u32; 4]>> = Mutex::new(None);
}
pub struct SeededRng {
seed: [u32; 4],
inner: XorShiftRng,
}
impl SeededRng {
pub fn new() -> Self {
let mut optional_seed = &mut *unwrap!(SEED.lock());
let seed = if let Some(current_seed) = *optional_seed {
current_seed
} else {
let new_seed = [rand::random(), rand::random(), rand::random(), rand::random()];
*optional_seed = Some(new_seed);
new_seed
};
SeededRng {
seed: seed,
inner: XorShiftRng::from_seed(seed),
}
}
pub fn from_seed(seed: [u32; 4]) -> Self {
let mut optional_seed = &mut *unwrap!(SEED.lock());
if let Some(current_seed) = *optional_seed {
if current_seed != seed {
panic!("\nThe static seed has already been initialised to a different value via \
a call to `SeededRng::new()`\nor `SeededRng::from_seed(...)`. This \
could be due to setting a hard-coded value for the seed in a\nsingle \
test case, but running the whole test suite. If so, try running just \
the single test case.\n");
}
} else {
*optional_seed = Some(seed);
}
SeededRng {
seed: seed,
inner: XorShiftRng::from_seed(seed),
}
}
pub fn new_rng(&mut self) -> XorShiftRng {
XorShiftRng::from_seed([self.inner.gen(),
self.inner.gen(),
self.inner.gen(),
self.inner.gen()])
}
}
impl Default for SeededRng {
fn default() -> Self {
SeededRng::new()
}
}
impl Display for SeededRng {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "RNG seed: {:?}", self.seed)
}
}
impl Debug for SeededRng {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
<Self as Display>::fmt(self, formatter)
}
}
impl Drop for SeededRng {
fn drop(&mut self) {
if thread::panicking() {
let msg = format!("{}", self);
let border = (0..msg.len()).map(|_| "=").collect::<String>();
println!("\n{}\n{}\n{}\n", border, msg, border);
}
}
}
impl Rng for SeededRng {
fn next_u32(&mut self) -> u32 {
self.inner.next_u32()
}
}
#[cfg(test)]
mod tests {
use rand::Rng;
use super::*;
#[test]
#[should_panic(expected = "\nThe static seed has already been initialised to a different value \
via a call to `SeededRng::new()`\nor `SeededRng::from_seed(...)`. \
This could be due to setting a hard-coded value for the seed in \
a\nsingle test case, but running the whole test suite. If so, try \
running just the single test case.\n")]
fn seeded_rng() {
{
let seed = [0, 1, 2, 3];
let mut seeded_rng1 = SeededRng::from_seed(seed);
let mut seeded_rng2 = SeededRng::new();
let expected = 12884903946;
assert_eq!(seeded_rng1.next_u64(), expected);
assert_eq!(seeded_rng2.next_u64(), expected);
let mut rng1_from_seeded_rng1 = seeded_rng1.new_rng();
let mut rng2_from_seeded_rng1 = seeded_rng1.new_rng();
let expected1 = 36055743652167817;
let expected2 = 19781043125127688;
assert_eq!(rng1_from_seeded_rng1.next_u64(), expected1);
assert_eq!(rng2_from_seeded_rng1.next_u64(), expected2);
let mut rng1_from_seeded_rng2 = seeded_rng2.new_rng();
let mut rng2_from_seeded_rng2 = seeded_rng2.new_rng();
assert_eq!(rng1_from_seeded_rng2.next_u64(), expected1);
assert_eq!(rng2_from_seeded_rng2.next_u64(), expected2);
}
let _ = SeededRng::from_seed([3, 2, 1, 0]);
}
}