//! A tiny deterministic PRNG (splitmix64) so synthetic data generation needs no
//! external crate. wasm has no clock, so callers seed from a process-global
//! counter (see `external::next_seed`) to vary output between runs.
pub struct Rng(u64);
impl Rng {
pub fn new(seed: u64) -> Self {
Rng(seed ^ 0x9E37_79B9_7F4A_7C15)
}
pub fn next_u64(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
/// Uniform integer in `[0, n)`. Returns 0 when `n == 0`.
pub fn below(&mut self, n: u64) -> u64 {
if n == 0 {
0
} else {
self.next_u64() % n
}
}
pub fn bool(&mut self) -> bool {
self.next_u64() & 1 == 1
}
/// Uniform integer in the inclusive range `[min, max]`.
pub fn range_i64(&mut self, min: i64, max: i64) -> i64 {
if max <= min {
min
} else {
let span = (max - min) as u64 + 1;
min + self.below(span) as i64
}
}
/// Uniform float in `[min, max]`.
pub fn range_f64(&mut self, min: f64, max: f64) -> f64 {
if max <= min {
min
} else {
let unit = self.next_u64() as f64 / u64::MAX as f64;
min + unit * (max - min)
}
}
/// Pick a reference from a non-empty slice; `None` if empty.
pub fn pick<'a, T>(&mut self, items: &'a [T]) -> Option<&'a T> {
if items.is_empty() {
None
} else {
Some(&items[self.below(items.len() as u64) as usize])
}
}
}