use crate::math::{Fix128, Vec3Fix};
#[derive(Clone, Debug)]
pub struct DeterministicRng {
state: u64,
inc: u64,
}
impl DeterministicRng {
const MULTIPLIER: u64 = 6364136223846793005;
#[must_use]
pub fn new(seed: u64) -> Self {
let mut rng = Self {
state: 0,
inc: (seed << 1) | 1, };
rng.next_u32();
rng.state = rng.state.wrapping_add(seed);
rng.next_u32();
rng
}
#[must_use]
pub fn new_with_stream(seed: u64, stream: u64) -> Self {
let mut rng = Self {
state: 0,
inc: (stream << 1) | 1,
};
rng.next_u32();
rng.state = rng.state.wrapping_add(seed);
rng.next_u32();
rng
}
#[inline]
pub fn next_u32(&mut self) -> u32 {
let old_state = self.state;
self.state = old_state
.wrapping_mul(Self::MULTIPLIER)
.wrapping_add(self.inc);
let xorshifted = (((old_state >> 18) ^ old_state) >> 27) as u32;
let rot = (old_state >> 59) as u32;
xorshifted.rotate_right(rot)
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
let hi = self.next_u32() as u64;
let lo = self.next_u32() as u64;
(hi << 32) | lo
}
#[inline]
pub fn next_fix128(&mut self) -> Fix128 {
Fix128 {
hi: 0,
lo: self.next_u64(),
}
}
pub fn next_fix128_range(&mut self, lo: Fix128, hi: Fix128) -> Fix128 {
let t = self.next_fix128(); let range = hi - lo;
lo + range * t
}
pub fn next_direction(&mut self) -> Vec3Fix {
for _ in 0..64 {
let u = self.next_fix128_range(Fix128::NEG_ONE, Fix128::ONE);
let v = self.next_fix128_range(Fix128::NEG_ONE, Fix128::ONE);
let s = u * u + v * v;
if s >= Fix128::ONE || s.is_zero() {
continue;
}
let factor = (Fix128::ONE - s).sqrt();
let two = Fix128::from_int(2);
return Vec3Fix::new(two * u * factor, two * v * factor, Fix128::ONE - two * s);
}
Vec3Fix::UNIT_Y
}
#[inline]
pub fn next_bounded(&mut self, max: u32) -> u32 {
if max == 0 {
return 0;
}
let threshold = max.wrapping_neg() % max;
loop {
let r = self.next_u32();
if r >= threshold {
return r % max;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_determinism() {
let mut rng1 = DeterministicRng::new(12345);
let mut rng2 = DeterministicRng::new(12345);
for _ in 0..100 {
assert_eq!(rng1.next_u32(), rng2.next_u32());
}
}
#[test]
fn test_different_seeds() {
let mut rng1 = DeterministicRng::new(1);
let mut rng2 = DeterministicRng::new(2);
let mut same_count = 0;
for _ in 0..100 {
if rng1.next_u32() == rng2.next_u32() {
same_count += 1;
}
}
assert!(
same_count < 5,
"Different seeds should produce different sequences"
);
}
#[test]
fn test_fix128_range() {
let mut rng = DeterministicRng::new(42);
for _ in 0..100 {
let val = rng.next_fix128();
assert!(val >= Fix128::ZERO);
assert!(val < Fix128::ONE);
}
}
#[test]
fn test_bounded() {
let mut rng = DeterministicRng::new(99);
for _ in 0..100 {
let val = rng.next_bounded(10);
assert!(val < 10);
}
}
#[test]
fn test_streams() {
let mut rng1 = DeterministicRng::new_with_stream(42, 1);
let mut rng2 = DeterministicRng::new_with_stream(42, 2);
let a = rng1.next_u32();
let b = rng2.next_u32();
assert_ne!(a, b, "Different streams should produce different values");
}
#[test]
fn test_direction_vector() {
let mut rng = DeterministicRng::new(777);
for _ in 0..10 {
let dir = rng.next_direction();
let len_sq = dir.length_squared();
let diff = if len_sq > Fix128::ONE {
len_sq - Fix128::ONE
} else {
Fix128::ONE - len_sq
};
assert!(
diff < Fix128::from_ratio(1, 10),
"Direction should be roughly unit length"
);
}
}
}