1use glam::Vec3;
14
15#[derive(Clone, Debug)]
17pub struct Rng {
18 state: u64,
19}
20
21impl Default for Rng {
22 fn default() -> Self {
23 Self::new(0x9E37_79B9_7F4A_7C15)
24 }
25}
26
27impl Rng {
28 pub fn new(seed: u64) -> Self {
30 let mut z = seed.wrapping_add(0x9E37_79B9_7F4A_7C15);
32 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
33 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
34 Self { state: (z ^ (z >> 31)) | 1 }
35 }
36
37 pub fn next_u64(&mut self) -> u64 {
39 let mut x = self.state;
40 x ^= x >> 12;
41 x ^= x << 25;
42 x ^= x >> 27;
43 self.state = x;
44 x.wrapping_mul(0x2545_F491_4F6C_DD1D)
45 }
46
47 pub fn f32(&mut self) -> f32 {
49 (self.next_u64() >> 40) as f32 / (1u64 << 24) as f32
50 }
51
52 pub fn range(&mut self, min: f32, max: f32) -> f32 {
54 min + (max - min) * self.f32()
55 }
56
57 pub fn range_i32(&mut self, min: i32, max: i32) -> i32 {
59 if max <= min {
60 return min;
61 }
62 min + (self.next_u64() % (max as i64 - min as i64) as u64) as i32
63 }
64
65 pub fn chance(&mut self, p: f32) -> bool {
67 self.f32() < p
68 }
69
70 pub fn pick<'a, T>(&mut self, items: &'a [T]) -> Option<&'a T> {
72 if items.is_empty() {
73 None
74 } else {
75 items.get(self.range_i32(0, items.len() as i32) as usize)
76 }
77 }
78
79 pub fn unit_vec3(&mut self) -> Vec3 {
81 let z = self.range(-1.0, 1.0);
82 let a = self.range(0.0, std::f32::consts::TAU);
83 let r = (1.0 - z * z).sqrt();
84 Vec3::new(r * a.cos(), z, r * a.sin())
85 }
86
87 pub fn in_sphere(&mut self, radius: f32) -> Vec3 {
89 self.unit_vec3() * radius * self.f32().cbrt()
90 }
91
92 pub fn on_circle(&mut self, radius: f32) -> Vec3 {
94 let a = self.range(0.0, std::f32::consts::TAU);
95 Vec3::new(a.cos(), 0.0, a.sin()) * radius
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn streams_are_distinct_and_in_range() {
105 let mut a = Rng::new(1);
106 let mut b = Rng::new(2);
107 assert_ne!(a.next_u64(), b.next_u64());
108 for _ in 0..1000 {
109 assert!((0.0..1.0).contains(&a.f32()));
110 assert!((-3..3).contains(&a.range_i32(-3, 3)));
111 assert!(a.in_sphere(2.0).length() <= 2.0001);
112 }
113 assert_eq!(a.range_i32(4, 4), 4);
114 assert!(a.pick::<i32>(&[]).is_none());
115 }
116}