use core::convert::Infallible;
use rand::{
Rng,
RngExt,
SeedableRng,
TryCryptoRng,
TryRng,
distr::{Distribution, StandardUniform},
};
use rand_xorshift::XorShiftRng;
pub trait Uniform: Sized {
fn rand<R: RngExt + ?Sized>(rng: &mut R) -> Self;
}
impl<T> Uniform for T
where
StandardUniform: Distribution<T>,
{
#[inline]
fn rand<R: RngExt + ?Sized>(rng: &mut R) -> Self {
rng.random()
}
}
pub trait UniformExt: Uniform {
fn rand_option<R: Rng + ?Sized>(rng: &mut R) -> Option<Self> {
if rng.random() { Some(Self::rand(rng)) } else { None }
}
}
impl<T: Uniform> UniformExt for T {}
pub struct TestRng {
seed: u64,
rng: XorShiftRng,
calls: usize,
}
impl Default for TestRng {
fn default() -> Self {
let seed: u64 = rand::random();
Self::fixed(seed)
}
}
impl TestRng {
pub fn fixed(seed: u64) -> Self {
println!("\nInitializing 'TestRng' with seed '{seed}'\n");
Self::from_seed(seed)
}
pub fn from_seed(seed: u64) -> Self {
Self { seed, rng: XorShiftRng::seed_from_u64(seed), calls: 0 }
}
pub fn next_string(&mut self, max_bytes: u32, is_fixed_size: bool) -> String {
fn adjust_unsafe_char(ch: char) -> char {
let code = ch as u32;
if code < 9
|| code == 11
|| code == 12
|| (14..=31).contains(&code)
|| code == 127
|| (0x202a..=0x202e).contains(&code)
|| (0x2066..=0x2069).contains(&code)
{
'0'
} else {
ch
}
}
fn adjust_backslash_and_doublequote(ch: char) -> char {
if ch == '\\' || ch == '\"' { '0' } else { ch }
}
let range = match is_fixed_size {
true => 0..max_bytes,
false => 0..self.random_range(0..max_bytes),
};
range.map(|_| self.random::<char>()).map(adjust_unsafe_char).map(adjust_backslash_and_doublequote).collect()
}
}
impl TryRng for TestRng {
type Error = Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
self.calls += 1;
Ok(self.rng.next_u32())
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
self.calls += 1;
Ok(self.rng.next_u64())
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
self.calls += 1;
self.rng.fill_bytes(dest);
Ok(())
}
}
impl TryCryptoRng for TestRng {}
impl Drop for TestRng {
fn drop(&mut self) {
println!("Called TestRng with seed {} {} times", self.seed, self.calls);
}
}
pub fn gen_range_inclusive_legacy(low: usize, high: usize, rng: &mut impl Rng) -> usize {
debug_assert!(low <= high);
let range = high.wrapping_sub(low).wrapping_add(1);
if range == 0 {
return rng.random::<u64>() as usize;
}
let zone = (range << range.leading_zeros()).wrapping_sub(1);
loop {
let v = rng.next_u64() as usize;
let wide = (v as u128) * (range as u128);
let hi = (wide >> 64) as usize;
let lo = wide as usize;
if lo <= zone {
return low.wrapping_add(hi);
}
}
}
pub fn choose_weighted_legacy<'a, T, R: Rng>(slice: &'a [T], weight_fn: impl Fn(&T) -> u16, rng: &mut R) -> &'a T {
let mut iter = slice.iter();
let first = iter.next().unwrap();
let mut total: u16 = weight_fn(first);
let mut cumulative: Vec<u16> = Vec::with_capacity(slice.len() - 1);
for item in iter {
cumulative.push(total);
total += weight_fn(item);
}
assert!(total > 0);
let range = total as u32;
let ints_to_reject = (u32::MAX - range + 1) % range;
let zone = u32::MAX - ints_to_reject;
let chosen: u16 = loop {
let v = rng.next_u32();
let wide = (v as u64) * (range as u64);
let hi = (wide >> 32) as u32;
let lo = wide as u32;
if lo <= zone {
break hi as u16;
}
};
let idx = cumulative.partition_point(|w| *w <= chosen);
&slice[idx]
}