use super::ValueGenerator;
use crate::util::splitmix64;
pub struct UniformRandom {
min: f64,
max: f64,
seed: u64,
}
impl UniformRandom {
pub fn new(min: f64, max: f64, seed: u64) -> Self {
Self { min, max, seed }
}
}
impl ValueGenerator for UniformRandom {
fn value(&self, tick: u64) -> f64 {
let hash = splitmix64(self.seed ^ tick);
let unit = (hash as f64) / (u64::MAX as f64);
self.min + unit * (self.max - self.min)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn same_seed_and_tick_returns_same_value() {
let gen = UniformRandom::new(0.0, 100.0, 42);
let v1 = gen.value(7);
let v2 = gen.value(7);
assert_eq!(v1, v2, "same seed+tick must produce identical output");
}
#[test]
fn determinism_across_instances() {
let gen_a = UniformRandom::new(0.0, 100.0, 99);
let gen_b = UniformRandom::new(0.0, 100.0, 99);
for tick in 0..100 {
assert_eq!(
gen_a.value(tick),
gen_b.value(tick),
"two generators with same seed must agree at tick {tick}"
);
}
}
#[test]
fn all_values_within_range_for_10000_ticks() {
let gen = UniformRandom::new(5.0, 10.0, 0);
for tick in 0..10_000 {
let v = gen.value(tick);
assert!(
v >= 5.0 && v <= 10.0,
"value {v} at tick {tick} is outside [5.0, 10.0]"
);
}
}
#[test]
fn different_seeds_produce_different_sequences() {
let gen_a = UniformRandom::new(0.0, 1.0, 1);
let gen_b = UniformRandom::new(0.0, 1.0, 2);
let any_differ = (0..100).any(|tick| gen_a.value(tick) != gen_b.value(tick));
assert!(
any_differ,
"different seeds must produce different sequences"
);
}
#[test]
fn different_ticks_produce_different_values() {
let gen = UniformRandom::new(0.0, 1.0, 0);
let first = gen.value(0);
let any_differ = (1..100).any(|tick| gen.value(tick) != first);
assert!(
any_differ,
"consecutive ticks must not all produce the same value"
);
}
#[test]
fn zero_range_returns_min() {
let gen = UniformRandom::new(7.5, 7.5, 0);
for tick in 0..10 {
assert_eq!(
gen.value(tick),
7.5,
"zero-width range should return min at tick {tick}"
);
}
}
}