criterion/stats/
rand_util.rsuse rand_core::{RngCore, SeedableRng};
use rand_os::OsRng;
use rand_xoshiro::Xoshiro256StarStar;
use std::cell::RefCell;
pub type Rng = Xoshiro256StarStar;
thread_local! {
static SEED_RAND: RefCell<OsRng> = RefCell::new(OsRng::new().unwrap());
}
pub fn new_rng() -> Rng {
SEED_RAND.with(|r| {
let mut seed = [0u8; 32];
r.borrow_mut().fill_bytes(&mut seed);
Xoshiro256StarStar::from_seed(seed)
})
}
pub struct Range {
low: usize,
range: usize,
zone: usize,
}
impl Range {
pub fn new_exclusive(low: usize, high: usize) -> Range {
Range::new_inclusive(low, high - 1)
}
pub fn new_inclusive(low: usize, high: usize) -> Range {
let max = ::std::usize::MAX;
assert!(low < high);
assert!(high < max);
let range = high.wrapping_sub(low).wrapping_add(1);
let ints_to_reject = (max - range + 1) % range;
let zone = max - ints_to_reject;
Range { low, range, zone }
}
pub fn sample<R: RngCore>(&self, rng: &mut R) -> usize {
loop {
let value: usize = rng.next_u64() as usize;
if value <= self.zone {
return (value % self.range) + self.low;
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use quickcheck::TestResult;
use std::cmp;
quickcheck! {
fn range_returns_samples_in_range(low: usize, high: usize) -> TestResult {
let (low, high) = (cmp::min(low, high), cmp::max(low, high));
if (high - low) < 2 || high == ::std::usize::MAX {
return TestResult::discard();
}
let mut rng = new_rng();
let range = Range::new_inclusive(low, high);
for _ in 0..1000 {
let value = range.sample(&mut rng);
if !(low <= value && value <= high) {
return TestResult::from_bool(false);
}
}
return TestResult::from_bool(true);
}
}
struct CountingRng {
count: u64,
}
impl ::rand_core::RngCore for CountingRng {
fn next_u32(&mut self) -> u32 {
self.next_u64() as u32
}
fn next_u64(&mut self) -> u64 {
let value = self.count;
self.count = self.count.wrapping_add(1);
value
}
fn fill_bytes(&mut self, _dest: &mut [u8]) {
unimplemented!()
}
fn try_fill_bytes(
&mut self,
_dest: &mut [u8],
) -> ::std::result::Result<(), ::rand_core::Error> {
unimplemented!()
}
}
const SIZE: usize = 17;
const ROUNDS: usize = 200;
#[test]
fn range_is_uniform() {
let mut rng = CountingRng { count: 0 };
let range = Range::new_exclusive(0, SIZE);
let mut array = [0usize; SIZE];
for _ in 0..(ROUNDS * SIZE) {
let index = range.sample(&mut rng);
array[index] += 1;
}
assert_eq!([ROUNDS; SIZE], array);
}
#[test]
fn range_is_uniform_outside_of_zone() {
let mut rng = CountingRng {
count: ::std::u64::MAX - ((SIZE * ROUNDS) / 2) as u64,
};
let range = Range::new_exclusive(0, SIZE);
let mut array = [0usize; SIZE];
for _ in 0..(ROUNDS * SIZE) {
let index = range.sample(&mut rng);
array[index] += 1;
}
assert_eq!([ROUNDS; SIZE], array);
}
}