ferray_random/bitgen/
philox.rs1use super::BitGenerator;
11
12pub struct Philox {
27 counter: [u32; 4],
29 key: [u32; 2],
31 buffer: [u32; 4],
33 buf_idx: usize,
35}
36
37const PHILOX_M0: u32 = 0xD251_1F53;
39const PHILOX_M1: u32 = 0xCD9E_8D57;
40const PHILOX_W0: u32 = 0x9E37_79B9;
41const PHILOX_W1: u32 = 0xBB67_AE85;
42
43#[inline]
49#[allow(clippy::trivially_copy_pass_by_ref)]
50const fn philox_round(ctr: &mut [u32; 4], key: &[u32; 2]) {
51 let lo0 = ctr[0] as u64 * PHILOX_M0 as u64;
52 let lo1 = ctr[2] as u64 * PHILOX_M1 as u64;
53 let hi0 = (lo0 >> 32) as u32;
54 let lo0 = lo0 as u32;
55 let hi1 = (lo1 >> 32) as u32;
56 let lo1 = lo1 as u32;
57 let new0 = hi1 ^ ctr[1] ^ key[0];
58 let new1 = lo1;
59 let new2 = hi0 ^ ctr[3] ^ key[1];
60 let new3 = lo0;
61 ctr[0] = new0;
62 ctr[1] = new1;
63 ctr[2] = new2;
64 ctr[3] = new3;
65}
66
67#[inline]
69const fn philox_bump_key(key: &mut [u32; 2]) {
70 key[0] = key[0].wrapping_add(PHILOX_W0);
71 key[1] = key[1].wrapping_add(PHILOX_W1);
72}
73
74const fn philox4x32_10(counter: [u32; 4], key: [u32; 2]) -> [u32; 4] {
76 let mut ctr = counter;
77 let mut k = key;
78 philox_round(&mut ctr, &k);
80 philox_bump_key(&mut k);
81 philox_round(&mut ctr, &k);
82 philox_bump_key(&mut k);
83 philox_round(&mut ctr, &k);
84 philox_bump_key(&mut k);
85 philox_round(&mut ctr, &k);
86 philox_bump_key(&mut k);
87 philox_round(&mut ctr, &k);
88 philox_bump_key(&mut k);
89 philox_round(&mut ctr, &k);
90 philox_bump_key(&mut k);
91 philox_round(&mut ctr, &k);
92 philox_bump_key(&mut k);
93 philox_round(&mut ctr, &k);
94 philox_bump_key(&mut k);
95 philox_round(&mut ctr, &k);
96 philox_bump_key(&mut k);
97 philox_round(&mut ctr, &k);
98 ctr
99}
100
101impl Philox {
102 fn increment_counter(&mut self) {
104 for word in &mut self.counter {
105 *word = word.wrapping_add(1);
106 if *word != 0 {
107 return;
108 }
109 }
110 }
111
112 fn generate_block(&mut self) {
114 self.buffer = philox4x32_10(self.counter, self.key);
115 self.increment_counter();
116 self.buf_idx = 0;
117 }
118
119 fn new_with_key(key: [u32; 2], counter: [u32; 4]) -> Self {
121 let mut rng = Self {
122 counter,
123 key,
124 buffer: [0; 4],
125 buf_idx: 4, };
127 rng.generate_block();
128 rng
129 }
130}
131
132impl BitGenerator for Philox {
133 fn next_u64(&mut self) -> u64 {
134 if self.buf_idx >= 4 {
135 self.generate_block();
136 }
137 let lo = self.buffer[self.buf_idx] as u64;
138 self.buf_idx += 1;
139 if self.buf_idx >= 4 {
140 self.generate_block();
141 }
142 let hi = self.buffer[self.buf_idx] as u64;
143 self.buf_idx += 1;
144 lo | (hi << 32)
145 }
146
147 fn seed_from_u64(seed: u64) -> Self {
148 let key = [seed as u32, (seed >> 32) as u32];
149 Self::new_with_key(key, [0; 4])
150 }
151
152 fn jump(&mut self) -> Option<()> {
153 self.counter[2] = self.counter[2].wrapping_add(1);
156 if self.counter[2] == 0 {
157 self.counter[3] = self.counter[3].wrapping_add(1);
158 }
159 self.buf_idx = 4; Some(())
161 }
162
163 fn stream(seed: u64, stream_id: u64) -> Option<Self> {
164 let key = [seed as u32, (seed >> 32) as u32];
166 let counter = [0u32, 0u32, stream_id as u32, (stream_id >> 32) as u32];
167 Some(Self::new_with_key(key, counter))
168 }
169}
170
171impl Clone for Philox {
172 fn clone(&self) -> Self {
173 Self {
174 counter: self.counter,
175 key: self.key,
176 buffer: self.buffer,
177 buf_idx: self.buf_idx,
178 }
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn deterministic_output() {
188 let mut rng1 = Philox::seed_from_u64(42);
189 let mut rng2 = Philox::seed_from_u64(42);
190 for _ in 0..1000 {
191 assert_eq!(rng1.next_u64(), rng2.next_u64());
192 }
193 }
194
195 #[test]
196 fn different_seeds_differ() {
197 let mut rng1 = Philox::seed_from_u64(42);
198 let mut rng2 = Philox::seed_from_u64(43);
199 let mut same = true;
200 for _ in 0..100 {
201 if rng1.next_u64() != rng2.next_u64() {
202 same = false;
203 break;
204 }
205 }
206 assert!(!same);
207 }
208
209 #[test]
210 fn stream_support() {
211 let rng0 = Philox::stream(42, 0);
212 let rng1 = Philox::stream(42, 1);
213 assert!(rng0.is_some());
214 assert!(rng1.is_some());
215
216 let mut rng0 = rng0.unwrap();
217 let mut rng1 = rng1.unwrap();
218 let v0 = rng0.next_u64();
220 let v1 = rng1.next_u64();
221 assert_ne!(v0, v1);
222 }
223
224 #[test]
225 fn jump_support() {
226 let mut rng = Philox::seed_from_u64(42);
227 assert!(rng.jump().is_some());
228 }
229
230 #[test]
231 fn stream_deterministic() {
232 let mut rng1 = Philox::stream(42, 7).unwrap();
233 let mut rng2 = Philox::stream(42, 7).unwrap();
234 for _ in 0..1000 {
235 assert_eq!(rng1.next_u64(), rng2.next_u64());
236 }
237 }
238}