1use rand::Rng;
26
27pub fn f64_unit<R: Rng + ?Sized>(rng: &mut R) -> f64 {
30 const SCALE: f64 = 1.0 / ((1u64 << 53) as f64);
31 SCALE * ((rng.next_u64() >> 11) as f64)
32}
33
34fn uniform_u32_inclusive<R: Rng + ?Sized>(rng: &mut R, low: u32, high: u32) -> u32 {
39 let range = high.wrapping_sub(low).wrapping_add(1);
40 if range == 0 {
41 return rng.next_u32();
43 }
44 let zone = (range << range.leading_zeros()).wrapping_sub(1);
45 loop {
46 let product = u64::from(rng.next_u32()) * u64::from(range);
47 let (hi, lo) = ((product >> 32) as u32, product as u32);
48 if lo <= zone {
49 return low.wrapping_add(hi);
50 }
51 }
52}
53
54pub fn index<R: Rng + ?Sized>(rng: &mut R, ubound: usize) -> usize {
59 assert!(ubound > 0, "cannot draw an index from an empty range");
60 assert!(
61 ubound <= u32::MAX as usize,
62 "index sampling is limited to u32::MAX to stay independent of usize width"
63 );
64 uniform_u32_inclusive(rng, 0, ubound as u32 - 1) as usize
65}
66
67pub fn shuffle<T, R: Rng + ?Sized>(rng: &mut R, slice: &mut [T]) {
69 for i in (1..slice.len()).rev() {
70 slice.swap(i, index(rng, i + 1));
71 }
72}
73
74pub fn choose<'a, T, R: Rng + ?Sized>(rng: &mut R, slice: &'a [T]) -> Option<&'a T> {
76 if slice.is_empty() {
77 None
78 } else {
79 Some(&slice[index(rng, slice.len())])
80 }
81}
82
83pub fn sample_indices<R: Rng + ?Sized>(rng: &mut R, length: usize, amount: usize) -> Vec<usize> {
91 assert!(
92 amount <= length,
93 "cannot sample more indices than are available"
94 );
95 assert!(
96 length <= u32::MAX as usize,
97 "index sampling is limited to u32::MAX to stay independent of usize width"
98 );
99 let (amount, length) = (amount as u32, length as u32);
100
101 if amount < 163 {
105 let scale = if length < 500_000 {
106 (1.6, 10.0)
107 } else {
108 (8.0 / 45.0, 70.0 / 9.0)
109 };
110 let amount_fp = amount as f32;
111 if amount > 11 && (length as f32) < (scale.1 + scale.0 * amount_fp) * amount_fp {
112 sample_in_place(rng, length, amount)
113 } else {
114 sample_floyd(rng, length, amount)
115 }
116 } else {
117 let scale = if length < 500_000 { 270.0 } else { 330.0 / 9.0 };
118 assert!(
119 (length as f32) < scale * (amount as f32),
120 "sampling {amount} of {length} indices needs the algorithm this module does not carry"
121 );
122 sample_in_place(rng, length, amount)
123 }
124}
125
126fn sample_floyd<R: Rng + ?Sized>(rng: &mut R, length: u32, amount: u32) -> Vec<usize> {
128 let shuffle_while_inserting = amount < 50;
131
132 let mut indices: Vec<u32> = Vec::with_capacity(amount as usize);
133 for j in length - amount..length {
134 let t = uniform_u32_inclusive(rng, 0, j);
135 if shuffle_while_inserting {
136 if let Some(pos) = indices.iter().position(|&x| x == t) {
137 indices.insert(pos, j);
138 continue;
139 }
140 } else if indices.contains(&t) {
141 indices.push(j);
142 continue;
143 }
144 indices.push(t);
145 }
146 if !shuffle_while_inserting {
147 for i in (1..amount).rev() {
148 let j = uniform_u32_inclusive(rng, 0, i) as usize;
149 indices.swap(i as usize, j);
150 }
151 }
152 indices.into_iter().map(|i| i as usize).collect()
153}
154
155fn sample_in_place<R: Rng + ?Sized>(rng: &mut R, length: u32, amount: u32) -> Vec<usize> {
157 let mut indices: Vec<u32> = (0..length).collect();
158 for i in 0..amount {
159 let j = uniform_u32_inclusive(rng, i, length - 1);
160 indices.swap(i as usize, j as usize);
161 }
162 indices.truncate(amount as usize);
163 indices.into_iter().map(|i| i as usize).collect()
164}
165
166pub fn sample_weighted<R, F>(rng: &mut R, length: usize, weight: F, amount: usize) -> Vec<usize>
176where
177 R: Rng + ?Sized,
178 F: Fn(usize) -> f64,
179{
180 let amount = amount.min(length);
181 let mut heap: Vec<(f64, usize)> = Vec::with_capacity(length);
186 for i in 0..length {
187 let w = weight(i);
188 assert!(
189 w >= 0.0,
190 "weights must be non-negative and not NaN, got {w}"
191 );
192 heap.push((f64_unit(rng).powf(1.0 / w), i));
193 heap_sift_up(&mut heap, i);
194 }
195 let mut result = Vec::with_capacity(amount);
196 while result.len() < amount {
197 let last = heap.pop().expect("heap holds at least `amount` keys");
198 let largest = if heap.is_empty() {
199 last
200 } else {
201 let largest = std::mem::replace(&mut heap[0], last);
202 heap_sift_down_to_bottom(&mut heap);
203 largest
204 };
205 result.push(largest.1);
206 }
207 result
208}
209
210fn heap_sift_up(heap: &mut [(f64, usize)], mut pos: usize) -> usize {
213 let entry = heap[pos];
214 while pos > 0 {
215 let parent = (pos - 1) / 2;
216 if entry.0 <= heap[parent].0 {
217 break;
218 }
219 heap[pos] = heap[parent];
220 pos = parent;
221 }
222 heap[pos] = entry;
223 pos
224}
225
226fn heap_sift_down_to_bottom(heap: &mut [(f64, usize)]) {
229 let end = heap.len();
230 let entry = heap[0];
231 let mut pos = 0;
232 let mut child = 1;
233 while child + 1 < end {
234 if heap[child].0 <= heap[child + 1].0 {
236 child += 1;
237 }
238 heap[pos] = heap[child];
239 pos = child;
240 child = 2 * pos + 1;
241 }
242 if child + 1 == end {
243 heap[pos] = heap[child];
244 pos = child;
245 }
246 heap[pos] = entry;
247 heap_sift_up(heap, pos);
248}
249
250#[cfg(test)]
251mod tests {
252 use rand::{SeedableRng, rngs::ChaCha12Rng};
253
254 use super::*;
255
256 fn rng(seed: u8) -> ChaCha12Rng {
257 ChaCha12Rng::from_seed([seed; 32])
258 }
259
260 #[test]
264 fn matches_captured_vectors() {
265 let mut v: Vec<u32> = (0..10).collect();
266 shuffle(&mut rng(0), &mut v);
267 assert_eq!(v, [6, 8, 3, 0, 1, 9, 2, 5, 7, 4]);
268
269 let mut v: Vec<u32> = (0..40).collect();
270 shuffle(&mut rng(7), &mut v);
271 assert_eq!(v[..8], [13, 36, 32, 11, 39, 30, 6, 24]);
272
273 let items: Vec<u32> = (0..10).collect();
274 assert_eq!(choose(&mut rng(0), &items), Some(&4));
275 assert_eq!(choose(&mut rng(1), &items), Some(&8));
276
277 assert_eq!(
280 sample_indices(&mut rng(0), 10, 10),
281 [1, 8, 4, 9, 7, 6, 3, 0, 5, 2]
282 );
283 assert_eq!(
284 sample_indices(&mut rng(0), 20, 20),
285 [
286 8, 7, 3, 17, 5, 14, 11, 13, 2, 6, 0, 1, 18, 12, 4, 16, 19, 15, 10, 9
287 ]
288 );
289 assert_eq!(
290 sample_indices(&mut rng(3), 63, 63)[..8],
291 [32, 62, 58, 27, 54, 2, 56, 26]
292 );
293
294 let weights = [1u64, 5, 2, 8, 3];
295 assert_eq!(
296 sample_weighted(
297 &mut rng(0),
298 weights.len(),
299 |i| weights[i] as f64,
300 weights.len()
301 ),
302 [1, 4, 3, 0, 2]
303 );
304 assert_eq!(
305 sample_weighted(&mut rng(9), weights.len(), |i| weights[i] as f64, 2),
306 [1, 0]
307 );
308
309 let weights = [0u64, 3, 0, 0, 5, 0, 1, 0];
312 assert_eq!(
313 sample_weighted(
314 &mut rng(0),
315 weights.len(),
316 |i| weights[i] as f64,
317 weights.len()
318 ),
319 [4, 1, 6, 5, 2, 7, 0, 3]
320 );
321 assert_eq!(
322 sample_weighted(&mut rng(5), weights.len(), |i| weights[i] as f64, 6),
323 [1, 4, 6, 5, 2, 7]
324 );
325 }
326
327 #[test]
328 fn f64_unit_lies_in_the_unit_interval() {
329 let mut gen = rng(0);
330 for _ in 0..10_000 {
331 let x = f64_unit(&mut gen);
332 assert!((0.0..1.0).contains(&x), "{x} outside [0, 1)");
333 }
334 assert_eq!(
336 f64_unit(&mut rng(4)),
337 rand::RngExt::random::<f64>(&mut rng(4))
338 );
339 }
340
341 #[test]
342 fn sampling_every_index_yields_a_permutation() {
343 for len in [1usize, 2, 11, 12, 13, 49, 50, 51, 162, 163, 200] {
344 for seed in 0..4 {
345 let mut got = sample_indices(&mut rng(seed), len, len);
346 got.sort_unstable();
347 assert_eq!(got, (0..len).collect::<Vec<_>>(), "len {len} seed {seed}");
348
349 let mut got = sample_weighted(&mut rng(seed), len, |i| (i % 7 + 1) as f64, len);
350 got.sort_unstable();
351 assert_eq!(got, (0..len).collect::<Vec<_>>(), "len {len} seed {seed}");
352 }
353 }
354 }
355
356 #[test]
357 fn zero_weight_sorts_last() {
358 let weights = [0u64, 4, 4, 4];
359 let order = sample_weighted(&mut rng(2), weights.len(), |i| weights[i] as f64, 4);
360 assert_eq!(order.last(), Some(&0));
361 }
362}