ferray_random/bitgen/
pcg64.rs1#![allow(clippy::unreadable_literal)]
10
11use super::BitGenerator;
12
13pub struct Pcg64 {
27 state: u128,
28 inc: u128,
29}
30
31const PCG_DEFAULT_MULTIPLIER: u128 = 0x2360_ED05_1FC6_5DA4_4385_DF64_9FCC_F645;
33
34impl Pcg64 {
35 #[inline]
37 const fn step(&mut self) {
38 self.state = self
39 .state
40 .wrapping_mul(PCG_DEFAULT_MULTIPLIER)
41 .wrapping_add(self.inc);
42 }
43
44 #[inline]
46 const fn output(state: u128) -> u64 {
47 let xsl = ((state >> 64) ^ state) as u64;
48 let rot = (state >> 122) as u32;
49 xsl.rotate_right(rot)
50 }
51}
52
53impl BitGenerator for Pcg64 {
54 fn state_bytes(&self) -> Result<Vec<u8>, ferray_core::FerrayError> {
55 let mut out = Vec::with_capacity(32);
56 out.extend_from_slice(&self.state.to_le_bytes());
57 out.extend_from_slice(&self.inc.to_le_bytes());
58 Ok(out)
59 }
60
61 fn set_state_bytes(&mut self, bytes: &[u8]) -> Result<(), ferray_core::FerrayError> {
62 if bytes.len() != 32 {
63 return Err(ferray_core::FerrayError::invalid_value(format!(
64 "Pcg64 state must be 32 bytes, got {}",
65 bytes.len()
66 )));
67 }
68 self.state = u128::from_le_bytes(bytes[0..16].try_into().unwrap());
69 let inc = u128::from_le_bytes(bytes[16..32].try_into().unwrap());
70 if inc & 1 == 0 {
71 return Err(ferray_core::FerrayError::invalid_value(
72 "Pcg64 inc must be odd",
73 ));
74 }
75 self.inc = inc;
76 Ok(())
77 }
78
79 fn next_u64(&mut self) -> u64 {
80 let old_state = self.state;
81 self.step();
82 Self::output(old_state)
83 }
84
85 fn seed_from_u64(seed: u64) -> Self {
86 let seed128 = {
88 let mut s = seed;
89 let a = super::splitmix64(&mut s);
90 let b = super::splitmix64(&mut s);
91 ((a as u128) << 64) | (b as u128)
92 };
93 let inc = {
95 let mut s = seed.wrapping_add(0xda3e39cb94b95bdb);
96 let a = super::splitmix64(&mut s);
97 let b = super::splitmix64(&mut s);
98 (((a as u128) << 64) | (b as u128)) | 1
99 };
100
101 let mut rng = Self { state: 0, inc };
102 rng.step();
103 rng.state = rng.state.wrapping_add(seed128);
104 rng.step();
105 rng
106 }
107
108 fn jump(&mut self) -> Option<()> {
109 None
111 }
112
113 fn stream(_seed: u64, _stream_id: u64) -> Option<Self> {
114 None
116 }
117}
118
119impl Clone for Pcg64 {
120 fn clone(&self) -> Self {
121 Self {
122 state: self.state,
123 inc: self.inc,
124 }
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn deterministic_output() {
134 let mut rng1 = Pcg64::seed_from_u64(42);
135 let mut rng2 = Pcg64::seed_from_u64(42);
136 for _ in 0..1000 {
137 assert_eq!(rng1.next_u64(), rng2.next_u64());
138 }
139 }
140
141 #[test]
142 fn different_seeds_differ() {
143 let mut rng1 = Pcg64::seed_from_u64(42);
144 let mut rng2 = Pcg64::seed_from_u64(43);
145 let mut same = true;
146 for _ in 0..100 {
147 if rng1.next_u64() != rng2.next_u64() {
148 same = false;
149 break;
150 }
151 }
152 assert!(!same);
153 }
154
155 #[test]
156 fn jump_not_supported() {
157 let mut rng = Pcg64::seed_from_u64(42);
158 assert!(rng.jump().is_none());
159 }
160
161 #[test]
162 fn stream_not_supported() {
163 assert!(Pcg64::stream(42, 0).is_none());
164 }
165
166 #[test]
167 fn output_covers_full_range() {
168 let mut rng = Pcg64::seed_from_u64(12345);
169 let mut seen_high = false;
170 let mut seen_low = false;
171 for _ in 0..10_000 {
172 let v = rng.next_u64();
173 if v > (u64::MAX / 2) {
174 seen_high = true;
175 } else {
176 seen_low = true;
177 }
178 if seen_high && seen_low {
179 break;
180 }
181 }
182 assert!(seen_high && seen_low);
183 }
184}