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