1use rand_xoshiro::{rand_core::RngCore, Xoshiro256PlusPlus};
2use xxhash_rust::xxh3::xxh3_128;
3
4use crate::prng::make_prng;
5
6pub struct SubRandomnessProvider {
7 rng: Xoshiro256PlusPlus,
8}
9
10impl SubRandomnessProvider {
11 pub fn provide(&mut self) -> [u8; 32] {
12 let mut out = [0u8; 32];
13 self.rng.fill_bytes(&mut out);
14 out
15 }
16}
17
18impl Iterator for SubRandomnessProvider {
19 type Item = [u8; 32];
20
21 fn next(&mut self) -> Option<Self::Item> {
22 Some(self.provide())
23 }
24}
25
26pub fn sub_randomness_with_key(
48 mut randomness: [u8; 32],
49 key: impl AsRef<[u8]>,
50) -> Box<SubRandomnessProvider> {
51 let hashed_key = xxh3_128(key.as_ref()).to_be_bytes();
52 for (pos, byte) in hashed_key.iter().enumerate() {
53 randomness[pos] ^= byte;
54 }
55
56 let rng = make_prng(randomness);
57
58 Box::new(SubRandomnessProvider { rng })
59}
60
61pub fn sub_randomness(randomness: [u8; 32]) -> Box<SubRandomnessProvider> {
113 sub_randomness_with_key(randomness, b"_^default^_")
114}
115
116#[cfg(test)]
117mod tests {
118 use crate::{coinflip, pick, RANDOMNESS1};
119
120 use super::*;
121
122 #[test]
123 fn sub_randomness_with_key_works() {
124 let mut provider1 = sub_randomness_with_key([0xA6; 32], "A");
126 let mut provider2 = sub_randomness_with_key([0xA6; 32], "A");
127 assert_eq!(provider1.provide(), provider2.provide());
128 assert_eq!(provider1.provide(), provider2.provide());
129 assert_eq!(provider1.provide(), provider2.provide());
130
131 let mut provider1 = sub_randomness_with_key([0xA6; 32], "/my_namespace/ab");
133 let mut provider2 = sub_randomness_with_key([0xA6; 32], "/my_namespace/cd");
134 assert_ne!(provider1.provide(), provider2.provide());
135 assert_ne!(provider1.provide(), provider2.provide());
136 assert_ne!(provider1.provide(), provider2.provide());
137 }
138
139 #[test]
140 fn sub_randomness_works() {
141 let randomness: [u8; 32] = [0x77; 32];
142 let mut provider = sub_randomness(randomness);
143 let v1 = provider.provide();
144 let v2 = provider.provide();
145 let v3 = provider.provide();
146 let v4 = provider.provide();
147 println!("v1 = {v1:?}");
148 println!("v2 = {v2:?}");
149 println!("v3 = {v3:?}");
150 println!("v4 = {v4:?}");
151
152 let mut provider1 = sub_randomness([0xA6; 32]);
154 let mut provider2 = sub_randomness([0xA6; 32]);
155 assert_eq!(provider1.provide(), provider2.provide());
156 assert_eq!(provider1.provide(), provider2.provide());
157 assert_eq!(provider1.provide(), provider2.provide());
158
159 let mut provider1 = sub_randomness([0xA6; 32]);
161 let mut provider2 = sub_randomness([0xCF; 32]);
162 assert_ne!(provider1.provide(), provider2.provide());
163 assert_ne!(provider1.provide(), provider2.provide());
164 assert_ne!(provider1.provide(), provider2.provide());
165
166 let mut provider1 = sub_randomness([0xA6; 32]);
168 let mut provider2 = sub_randomness_with_key([0xA6; 32], "_^default^_");
169 assert_eq!(provider1.provide(), provider2.provide());
170 assert_eq!(provider1.provide(), provider2.provide());
171 assert_eq!(provider1.provide(), provider2.provide());
172 }
173
174 #[test]
175 fn sub_randomness_implements_iterator() {
176 let randomness: [u8; 32] = [0x77; 32];
177 let mut provider = sub_randomness(randomness);
178 let v1 = provider.next().unwrap();
179 let v2 = provider.next().unwrap();
180 let v3 = provider.next().unwrap();
181 let v4 = provider.next().unwrap();
182 println!("v1 = {v1:?}");
183 println!("v2 = {v2:?}");
184 println!("v3 = {v3:?}");
185 println!("v4 = {v4:?}");
186 }
187 #[test]
188 fn coinflip_distribution_is_uniform() {
189 use crate::sub_randomness::sub_randomness;
193 use std::collections::HashMap;
194
195 const TEST_SAMPLE_SIZE: usize = 100_000;
196 const ACCURACY: f32 = 0.01;
197
198 let mut result = vec![];
199
200 let mut provider = sub_randomness(RANDOMNESS1);
201
202 for _ in 0..TEST_SAMPLE_SIZE {
203 let flip_is_heads = coinflip(provider.next().unwrap()).is_heads();
204 println!("{}", flip_is_heads);
205 result.push(flip_is_heads);
206 }
207
208 let mut histogram = HashMap::new();
209
210 for element in result {
211 let count = histogram.entry(element).or_insert(0);
212 *count += 1;
213 }
214
215 let estimated_count_for_uniform_distribution = (TEST_SAMPLE_SIZE / 2) as f32;
216 let estimation_min: i32 =
217 (estimated_count_for_uniform_distribution * (1_f32 - ACCURACY)) as i32;
218 let estimation_max: i32 =
219 (estimated_count_for_uniform_distribution * (1_f32 + ACCURACY)) as i32;
220 println!(
221 "estimation {}, max: {}, min: {}",
222 estimated_count_for_uniform_distribution, estimation_max, estimation_min
223 );
224 for (bin, count) in histogram {
226 println!("{}: {}", bin, count);
227 assert!(count >= estimation_min && count <= estimation_max);
228 }
229 }
230
231 #[test]
232 fn pick_distribution_is_uniform() {
233 use crate::sub_randomness::sub_randomness;
238 use std::collections::HashMap;
239
240 const TEST_SAMPLE_SIZE: usize = 300_000;
241 const N_PICKED_ELEMENTS: usize = 3;
242 const ACCURACY: f32 = 0.01;
243
244 let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
245
246 let mut result = vec![vec![]];
247
248 let mut provider = sub_randomness(RANDOMNESS1);
249
250 for _ in 0..TEST_SAMPLE_SIZE - 1 {
251 let pick_result = pick(provider.next().unwrap(), N_PICKED_ELEMENTS, data.clone());
252
253 result.push(pick_result);
254 }
255
256 let mut histogram = HashMap::new();
257
258 for row in result {
259 for element in row {
260 let count = histogram.entry(element).or_insert(0);
261 *count += 1;
262 }
263 }
264 let estimated_count_for_uniform_distribution =
265 (TEST_SAMPLE_SIZE * N_PICKED_ELEMENTS / data.len()) as f32;
266 let estimation_min: i32 =
267 (estimated_count_for_uniform_distribution * (1_f32 - ACCURACY)) as i32;
268 let estimation_max: i32 =
269 (estimated_count_for_uniform_distribution * (1_f32 + ACCURACY)) as i32;
270 println!(
271 "estimation {}, max: {}, min: {}",
272 estimated_count_for_uniform_distribution, estimation_max, estimation_min
273 );
274 for (bin, count) in histogram {
276 println!("{}: {}", bin, count);
277 assert!(count >= estimation_min && count <= estimation_max);
278 }
279 }
280}