geop_core_topology/contains/
rng.rs1use geop_core_math::{
5 scalars::Scalar,
6 vector::{Vector2, Vector3},
7};
8
9pub struct Rng {
11 state: u64,
12}
13
14impl Rng {
15 pub fn new(seed: u64) -> Self {
16 Self {
18 state: if seed == 0 { 0x9E3779B97F4A7C15 } else { seed },
19 }
20 }
21
22 pub fn next_u64(&mut self) -> u64 {
23 let mut x = self.state;
24 x ^= x >> 12;
25 x ^= x << 25;
26 x ^= x >> 27;
27 self.state = x;
28 x.wrapping_mul(0x2545_F491_4F6C_DD1D)
29 }
30
31 pub fn next_f64(&mut self) -> f64 {
33 (self.next_u64() >> 11) as f64 * (1.0 / (1u64 << 53) as f64)
34 }
35
36 pub fn next_range(&mut self, lo: f64, hi: f64) -> f64 {
38 lo + self.next_f64() * (hi - lo)
39 }
40
41 pub fn next_direction2<S: Scalar>(&mut self) -> Vector2<S> {
43 let theta = self.next_range(0.0, std::f64::consts::TAU);
44 Vector2::from_array([S::from_f64(theta.cos()), S::from_f64(theta.sin())])
45 }
46
47 pub fn next_direction3<S: Scalar>(&mut self) -> Vector3<S> {
50 let z = self.next_range(-1.0, 1.0);
51 let theta = self.next_range(0.0, std::f64::consts::TAU);
52 let r = (1.0 - z * z).max(0.0).sqrt();
53 Vector3::from_array([
54 S::from_f64(r * theta.cos()),
55 S::from_f64(r * theta.sin()),
56 S::from_f64(z),
57 ])
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::Rng;
64
65 #[test]
66 fn same_seed_is_deterministic() {
67 let mut a = Rng::new(42);
68 let mut b = Rng::new(42);
69 for _ in 0..100 {
70 assert_eq!(a.next_u64(), b.next_u64());
71 }
72 }
73
74 #[test]
75 fn different_seeds_diverge() {
76 let mut a = Rng::new(1);
77 let mut b = Rng::new(2);
78 assert_ne!(a.next_u64(), b.next_u64());
79 }
80
81 #[test]
82 fn f64_stays_in_unit_range() {
83 let mut r = Rng::new(7);
84 for _ in 0..1000 {
85 let x = r.next_f64();
86 assert!(x >= 0.0 && x < 1.0);
87 }
88 }
89}