1use std::borrow::Borrow;
5
6use crate::rand::seq::index::sample as choose_range;
7use crate::rand::Rng;
8
9#[must_use]
22pub fn sample_single_from_known_length<I, R, T>(rng: &mut R, mut iter: I) -> Option<T>
23where
24 R: Rng,
25 I: Iterator<Item = T>,
26{
27 let (length, _) = iter.size_hint();
29 if length == 0 {
30 return None;
31 }
32 let index = rng.random_range(0..length as u32) as usize;
34 iter.nth(index)
36}
37
38#[must_use]
51pub fn sample_single_l_reservoir<I, R, T>(rng: &mut R, iterable: I) -> Option<T>
52where
53 R: Rng,
54 I: IntoIterator<Item = T>,
55{
56 let mut iter = iterable.into_iter();
57 let mut weight: f64 = rng.random(); let mut log_one_minus_weight = (-weight).ln_1p();
59 let mut chosen_item: T = iter.next()?; let mut skip = (rng.random::<f64>().ln() / log_one_minus_weight).floor() as usize;
64 weight *= rng.random::<f64>();
65 log_one_minus_weight = (-weight).ln_1p();
66
67 loop {
68 match iter.nth(skip) {
69 Some(item) => {
70 chosen_item = item;
71 skip = (rng.random::<f64>().ln() / log_one_minus_weight).floor() as usize;
72 weight *= rng.random::<f64>();
73 log_one_minus_weight = (-weight).ln_1p();
74 }
75 None => return Some(chosen_item),
76 }
77 }
78}
79
80#[must_use]
88pub fn count_and_sample_single_l_reservoir<I, R, T>(rng: &mut R, iterable: I) -> (usize, Option<T>)
89where
90 R: Rng,
91 I: IntoIterator<Item = T>,
92{
93 let mut count = 0usize;
94 let mut chosen_item: Option<T> = None;
95
96 for item in iterable {
97 count += 1;
98 if rng.random_range(0..count as u64) == 0 {
99 chosen_item = Some(item);
100 }
101 }
102
103 (count, chosen_item)
104}
105
106#[must_use]
120pub fn sample_multiple_from_known_length<I, R, T>(rng: &mut R, iter: I, requested: usize) -> Vec<T>
121where
122 R: Rng,
123 I: IntoIterator<Item = T>,
124{
125 let mut iter = iter.into_iter();
126 let (length, _) = iter.size_hint();
128
129 let mut indexes = Vec::with_capacity(requested);
130 indexes.extend(choose_range(rng, length, requested));
131 indexes.sort_unstable();
132
133 let mut selected = Vec::with_capacity(requested);
134 let mut consumed: usize = 0; for idx in indexes {
140 if let Some(item) = iter.nth(idx - consumed) {
141 selected.push(item);
142 }
143 consumed = idx + 1;
144 }
145
146 selected
147}
148
149#[must_use]
162pub fn sample_multiple_l_reservoir<I, R, T>(rng: &mut R, iter: I, requested: usize) -> Vec<T>
163where
164 R: Rng,
165 I: IntoIterator<Item = T>,
166{
167 if requested == 0 {
168 return Vec::new();
169 }
170
171 let requested_recip = 1.0 / requested as f64;
172 let mut weight: f64 = rng.random(); weight = weight.powf(requested_recip);
174 let mut log_one_minus_weight = (-weight).ln_1p();
175 let mut iter = iter.into_iter();
176 let mut reservoir: Vec<T> = iter.by_ref().take(requested).collect(); if reservoir.len() < requested {
179 return reservoir;
180 }
181
182 let mut skip = (rng.random::<f64>().ln() / log_one_minus_weight).floor() as usize;
185 let uniform_random: f64 = rng.random();
186 weight *= uniform_random.powf(requested_recip);
187 log_one_minus_weight = (-weight).ln_1p();
188
189 loop {
190 match iter.nth(skip) {
191 Some(item) => {
192 let to_remove = rng.random_range(0..reservoir.len());
193 reservoir.swap_remove(to_remove);
194 reservoir.push(item);
195
196 skip = (rng.random::<f64>().ln() / log_one_minus_weight).floor() as usize;
197 let uniform_random: f64 = rng.random();
198 weight *= uniform_random.powf(requested_recip);
199 log_one_minus_weight = (-weight).ln_1p();
200 }
201 None => return reservoir,
202 }
203 }
204}
205
206#[must_use]
215pub fn sample_single_excluding<'a, R, T, E>(
216 rng: &mut R,
217 slice: &'a [T],
218 excluded: E,
219) -> Option<&'a T>
220where
221 R: Rng,
222 T: PartialEq,
223 E: Borrow<T>,
224{
225 const SMALL_SLICE: usize = 4;
226 let excluded = excluded.borrow();
227 if slice.len() < SMALL_SLICE {
228 sample_single_excluding_iteration(rng, slice, excluded)
229 } else {
230 sample_single_excluding_rejection(rng, slice, excluded)
231 }
232}
233
234#[must_use]
239pub fn sample_single_excluding_iteration<'a, R, T, E>(
240 rng: &mut R,
241 slice: &'a [T],
242 excluded: E,
243) -> Option<&'a T>
244where
245 R: Rng,
246 T: PartialEq,
247 E: Borrow<T>,
248{
249 let excluded = excluded.borrow();
250 let valid_count = slice.iter().filter(|&x| x != excluded).count();
251 if valid_count == 0 {
252 return None;
253 }
254 let k = rng.random_range(0..valid_count as u32) as usize;
255 slice.iter().filter(|&x| x != excluded).nth(k)
256}
257
258#[must_use]
266pub fn sample_single_excluding_rejection<'a, R, T, E>(
267 rng: &mut R,
268 slice: &'a [T],
269 excluded: E,
270) -> Option<&'a T>
271where
272 R: Rng,
273 T: PartialEq,
274 E: Borrow<T>,
275{
276 const MAX_REJECTIONS: usize = 16;
283 if slice.is_empty() {
284 return None;
285 }
286 let excluded = excluded.borrow();
287 let trials = MAX_REJECTIONS.min(slice.len());
288 for _ in 0..trials {
289 let i = rng.random_range(0..slice.len() as u32) as usize;
290 let candidate = &slice[i];
291 if candidate != excluded {
292 return Some(candidate);
293 }
294 }
295 sample_single_excluding_iteration(rng, slice, excluded)
296}
297
298#[must_use]
307pub fn sample_single_excluding_l_reservoir<I, R, T, E>(
308 rng: &mut R,
309 iterable: I,
310 excluded: E,
311) -> Option<T>
312where
313 R: Rng,
314 T: PartialEq,
315 I: IntoIterator<Item = T>,
316 E: Borrow<T>,
317{
318 let excluded = excluded.borrow();
319 let (_, chosen) = count_and_sample_single_l_reservoir(
320 rng,
321 iterable.into_iter().filter(|item| item != excluded),
322 );
323 chosen
324}
325
326#[cfg(test)]
327mod tests {
328 use rand::rngs::StdRng;
329 use rand::SeedableRng;
330
331 use super::*;
332 use crate::hashing::{HashSet, HashSetExt};
333
334 #[test]
335 fn test_sample_single_l_reservoir_basic() {
336 let data: Vec<u32> = (0..1000).collect();
337 let seed: u64 = 42;
338 let mut rng = StdRng::seed_from_u64(seed);
339 let sample = sample_single_l_reservoir(&mut rng, data);
340
341 assert!(sample.is_some());
343
344 let value = sample.unwrap();
346 assert!(value < 1000);
347 }
348
349 #[test]
350 fn test_sample_single_l_reservoir_empty() {
351 let data: Vec<u32> = Vec::new();
352 let mut rng = StdRng::seed_from_u64(42);
353 let sample = sample_single_l_reservoir(&mut rng, data);
354
355 assert!(sample.is_none());
357 }
358
359 #[test]
360 fn test_sample_single_l_reservoir_single_element() {
361 let data: Vec<u32> = vec![42];
362 let mut rng = StdRng::seed_from_u64(1);
363 let sample = sample_single_l_reservoir(&mut rng, data);
364
365 assert_eq!(sample, Some(42));
367 }
368
369 #[test]
370 fn test_sample_single_l_reservoir_uniformity() {
371 let population: u32 = 1000;
372 let data: Vec<u32> = (0..population).collect();
373 let num_runs = 10000;
374 let num_bins = 10;
375 let mut counts = vec![0usize; num_bins];
376
377 for run in 0..num_runs {
378 let mut rng = StdRng::seed_from_u64(42 + run as u64);
379 let sample = sample_single_l_reservoir(&mut rng, data.iter().cloned());
380
381 if let Some(value) = sample {
382 let bin = (value as usize) / (population as usize / num_bins);
383 counts[bin] += 1;
384 }
385 }
386
387 let expected = num_runs as f64 / num_bins as f64;
389
390 let chi_square: f64 = counts
392 .iter()
393 .map(|&obs| {
394 let diff = (obs as f64) - expected;
395 diff * diff / expected
396 })
397 .sum();
398
399 let critical = 27.877;
402
403 println!("χ² = {}, counts = {:?}", chi_square, counts);
404
405 assert!(
406 chi_square < critical,
407 "Single sample fails uniformity test: χ² = {}, counts = {:?}",
408 chi_square,
409 counts
410 );
411 }
412
413 #[test]
414 fn test_sample_single_l_reservoir_hashset() {
415 let mut data = HashSet::new();
416 for i in 0..100 {
417 data.insert(i);
418 }
419
420 let mut rng = StdRng::seed_from_u64(42);
421 let sample = sample_single_l_reservoir(&mut rng, &data);
422
423 assert!(sample.is_some());
424 let value = sample.unwrap();
425 assert!(data.contains(value));
426 }
427
428 #[test]
429 fn test_count_and_sample_single_l_reservoir_empty() {
430 let data: Vec<u32> = Vec::new();
431 let mut rng = StdRng::seed_from_u64(42);
432 let (count, sample) = count_and_sample_single_l_reservoir(&mut rng, data);
433 assert_eq!(count, 0);
434 assert!(sample.is_none());
435 }
436
437 #[test]
438 fn test_count_and_sample_single_l_reservoir_count_matches() {
439 let data: Vec<u32> = (0..1000).collect();
440 let mut rng = StdRng::seed_from_u64(42);
441 let (count, sample) = count_and_sample_single_l_reservoir(&mut rng, data);
442 assert_eq!(count, 1000);
443 assert!(sample.is_some());
444 }
445
446 #[test]
447 fn test_count_and_sample_single_l_reservoir_single_element() {
448 let data: Vec<u32> = vec![7];
449 let mut rng = StdRng::seed_from_u64(42);
450 let (count, sample) = count_and_sample_single_l_reservoir(&mut rng, data);
451 assert_eq!(count, 1);
452 assert_eq!(sample, Some(7));
453 }
454
455 #[test]
456 fn test_sample_multiple_l_reservoir_basic() {
457 let data: Vec<u32> = (0..1000).collect();
458 let requested = 100;
459 let seed: u64 = 42;
460 let mut rng = StdRng::seed_from_u64(seed);
461 let sample = sample_multiple_l_reservoir(&mut rng, data, requested);
462
463 assert_eq!(sample.len(), requested);
465
466 assert!(sample.iter().all(|v| *v < 1000));
468
469 let unique: HashSet<_> = sample.iter().collect();
471 assert_eq!(unique.len(), sample.len());
472 }
473
474 #[test]
475 fn test_sample_multiple_l_reservoir_empty() {
476 let data: Vec<u32> = Vec::new();
477 let mut rng = StdRng::seed_from_u64(42);
478 let sample = sample_multiple_l_reservoir(&mut rng, &data, 10);
479
480 assert_eq!(sample.len(), 0);
482 }
483
484 #[test]
485 fn test_sample_multiple_l_reservoir_zero_requested() {
486 let data: Vec<u32> = (0..100).collect();
487 let mut rng = StdRng::seed_from_u64(42);
488 let sample = sample_multiple_l_reservoir(&mut rng, &data, 0);
489
490 assert_eq!(sample.len(), 0);
492 }
493
494 #[test]
495 fn test_sample_multiple_l_reservoir_requested_exceeds_population() {
496 let data: Vec<u32> = (0..50).collect();
497 let requested = 100;
498 let mut rng = StdRng::seed_from_u64(42);
499 let sample = sample_multiple_l_reservoir(&mut rng, data, requested);
500
501 assert_eq!(sample.len(), 50);
503
504 let unique: HashSet<_> = sample.iter().collect();
506 assert_eq!(unique.len(), 50);
507
508 assert!(sample.iter().all(|v| *v < 50));
510 }
511
512 #[test]
513 fn test_sample_multiple_l_reservoir_exact_population() {
514 let data: Vec<u32> = (0..100).collect();
515 let mut rng = StdRng::seed_from_u64(42);
516 let sample = sample_multiple_l_reservoir(&mut rng, data, 100);
517
518 assert_eq!(sample.len(), 100);
520
521 let unique: HashSet<_> = sample.iter().collect();
522 assert_eq!(unique.len(), 100);
523 }
524
525 #[test]
526 fn test_sample_multiple_l_reservoir_single_element() {
527 let data: Vec<u32> = vec![42];
528 let mut rng = StdRng::seed_from_u64(1);
529 let sample = sample_multiple_l_reservoir(&mut rng, data, 1);
530
531 assert_eq!(sample.len(), 1);
532 assert_eq!(sample[0], 42);
533 }
534
535 #[test]
536 fn test_sample_multiple_l_reservoir_hashset() {
537 let mut data = HashSet::new();
538 for i in 0..100 {
539 data.insert(i);
540 }
541
542 let mut rng = StdRng::seed_from_u64(42);
543 let sample = sample_multiple_l_reservoir(&mut rng, &data, 10);
544
545 assert_eq!(sample.len(), 10);
546
547 assert!(sample.iter().all(|v| data.contains(v)));
549
550 let unique: HashSet<_> = sample.iter().collect();
552 assert_eq!(unique.len(), 10);
553 }
554
555 #[test]
556 fn test_sample_multiple_l_reservoir_small_sample() {
557 let data: Vec<u32> = (0..1000).collect();
558 let requested = 5;
559 let mut rng = StdRng::seed_from_u64(42);
560 let sample = sample_multiple_l_reservoir(&mut rng, &data, requested);
561
562 assert_eq!(sample.len(), requested);
563
564 let unique: HashSet<_> = sample.iter().collect();
566 assert_eq!(unique.len(), requested);
567 }
568
569 #[test]
570 fn test_sample_multiple_l_reservoir_large_sample() {
571 let data: Vec<u32> = (0..1000).collect();
572 let requested = 900;
573 let mut rng = StdRng::seed_from_u64(42);
574 let sample = sample_multiple_l_reservoir(&mut rng, &data, requested);
575
576 assert_eq!(sample.len(), requested);
577
578 let unique: HashSet<_> = sample.iter().collect();
580 assert_eq!(unique.len(), requested);
581 }
582
583 #[test]
590 fn test_sample_multiple_l_reservoir_uniformity() {
591 let population: u32 = 10000;
592 let data: Vec<u32> = (0..population).collect();
593 let requested = 100;
594 let num_runs = 1000;
595 let mut chi_squares = Vec::with_capacity(num_runs);
596
597 for run in 0..num_runs {
598 let mut rng = StdRng::seed_from_u64(42 + run as u64);
599 let sample = sample_multiple_l_reservoir(&mut rng, data.iter().cloned(), requested);
600
601 let mut counts = [0usize; 10];
603 for &value in &sample {
604 let bin = (value as usize) / (population as usize / 10);
605 counts[bin] += 1;
606 }
607
608 let expected = requested as f64 / 10.0; let chi_square: f64 = counts
613 .iter()
614 .map(|&obs| {
615 let diff = (obs as f64) - expected;
616 diff * diff / expected
617 })
618 .sum();
619
620 chi_squares.push(chi_square);
621 }
622
623 let quantiles = [
633 0.0, 4.16816, 5.38005, 6.39331, 7.35703, 8.34283, 9.41364, 10.6564, 12.2421, 14.6837, f64::INFINITY, ];
645
646 let num_bins = quantiles.len() - 1;
647 let mut chi_square_counts = vec![0usize; num_bins];
648
649 for &chi_sq in &chi_squares {
650 for i in 0..num_bins {
652 if chi_sq >= quantiles[i] && chi_sq < quantiles[i + 1] {
653 chi_square_counts[i] += 1;
654 break;
655 }
656 }
657 }
658
659 let expected_per_bin = num_runs as f64 / num_bins as f64;
661 let chi_square_of_chi_squares: f64 = chi_square_counts
662 .iter()
663 .map(|&obs| {
664 let diff = (obs as f64) - expected_per_bin;
665 diff * diff / expected_per_bin
666 })
667 .sum();
668
669 let critical = 27.877;
672
673 println!(
674 "χ² = {}, counts = {:?}",
675 chi_square_of_chi_squares, chi_square_counts
676 );
677
678 assert!(
679 chi_square_of_chi_squares < critical,
680 "Chi-square statistics fail to follow chi-square(9) distribution: χ² = {}, counts = {:?}",
681 chi_square_of_chi_squares,
682 chi_square_counts
683 );
684 }
685
686 #[test]
688 fn test_sample_multiple_l_reservoir_element_probability() {
689 let population: u32 = 100;
690 let data: Vec<u32> = (0..population).collect();
691 let requested = 10;
692 let num_runs = 10000;
693 let mut selection_counts = vec![0usize; population as usize];
694
695 for run in 0..num_runs {
696 let mut rng = StdRng::seed_from_u64(42 + run as u64);
697 let sample = sample_multiple_l_reservoir(&mut rng, data.iter().cloned(), requested);
698
699 for &value in &sample {
700 selection_counts[value as usize] += 1;
701 }
702 }
703
704 let expected = (num_runs * requested) as f64 / population as f64;
707
708 let chi_square: f64 = selection_counts
710 .iter()
711 .map(|&obs| {
712 let diff = (obs as f64) - expected;
713 diff * diff / expected
714 })
715 .sum();
716
717 let critical = 148.23;
721
722 assert!(
723 chi_square < critical,
724 "Element selection probabilities are not uniform: χ² = {}",
725 chi_square
726 );
727 }
728
729 #[test]
731 fn test_sample_multiple_l_reservoir_reproducibility() {
732 let data: Vec<u32> = (0..1000).collect();
733 let test_sizes = [1, 2, 5, 10, 100, 500];
734
735 for &requested in &test_sizes {
736 let seed: u64 = 12345;
737
738 let mut rng1 = StdRng::seed_from_u64(seed);
739 let sample1 = sample_multiple_l_reservoir(&mut rng1, &data, requested);
740
741 let mut rng2 = StdRng::seed_from_u64(seed);
742 let sample2 = sample_multiple_l_reservoir(&mut rng2, &data, requested);
743
744 assert_eq!(
746 sample1.len(),
747 requested,
748 "Sample size {} doesn't match requested size {}",
749 sample1.len(),
750 requested
751 );
752 assert_eq!(
753 sample2.len(),
754 requested,
755 "Sample size {} doesn't match requested size {}",
756 sample2.len(),
757 requested
758 );
759
760 assert_eq!(
762 sample1, sample2,
763 "Reproducibility failed for requested={}",
764 requested
765 );
766 }
767 }
768
769 #[test]
770 fn test_sample_single_l_reservoir_reproducibility() {
771 let data: Vec<u32> = (0..1000).collect();
772 let seed: u64 = 12345;
773
774 let mut rng1 = StdRng::seed_from_u64(seed);
775 let sample1 = sample_single_l_reservoir(&mut rng1, &data);
776
777 let mut rng2 = StdRng::seed_from_u64(seed);
778 let sample2 = sample_single_l_reservoir(&mut rng2, &data);
779
780 assert_eq!(sample1, sample2);
782 }
783
784 #[test]
785 fn sample_single_excluding_empty_slice_returns_none() {
786 let data: [u32; 0] = [];
787 let mut rng = StdRng::seed_from_u64(42);
788 assert_eq!(sample_single_excluding(&mut rng, &data, 7), None);
789 }
790
791 #[test]
792 fn sample_single_excluding_only_excluded_returns_none() {
793 let data = [9, 9, 9, 9];
794 let mut rng = StdRng::seed_from_u64(42);
795 assert_eq!(sample_single_excluding(&mut rng, &data, 9), None);
796 }
797
798 #[test]
799 fn sample_single_excluding_never_returns_excluded_small() {
800 let data: Vec<u32> = (0..10).collect();
801 let mut rng = StdRng::seed_from_u64(42);
802 for _ in 0..1000 {
803 let v = sample_single_excluding(&mut rng, &data, 3).unwrap();
804 assert_ne!(*v, 3);
805 assert!(*v < 10);
806 }
807 }
808
809 #[test]
810 fn sample_single_excluding_never_returns_excluded_large() {
811 let data: Vec<u32> = (0..1000).collect();
813 let mut rng = StdRng::seed_from_u64(42);
814 for _ in 0..1000 {
815 let v = sample_single_excluding(&mut rng, &data, 500).unwrap();
816 assert_ne!(*v, 500);
817 assert!(*v < 1000);
818 }
819 }
820
821 #[test]
822 fn sample_single_excluding_excluded_not_in_slice() {
823 let data: Vec<u32> = (0..10).collect();
824 let mut rng = StdRng::seed_from_u64(42);
825 for _ in 0..100 {
826 let v = sample_single_excluding(&mut rng, &data, 999).unwrap();
827 assert!(*v < 10);
828 }
829 }
830
831 #[test]
832 fn sample_single_excluding_falls_back_when_excluded_dominates() {
833 let mut data = vec![0u32; 1000];
837 data[42] = 1;
838 data[800] = 2;
839 let mut rng = StdRng::seed_from_u64(42);
840 let mut seen: Vec<u32> = (0..200)
841 .map(|_| *sample_single_excluding(&mut rng, &data, 0).unwrap())
842 .collect();
843 seen.sort();
844 seen.dedup();
845 assert_eq!(seen, vec![1, 2]);
846 }
847
848 #[test]
849 fn sample_single_excluding_uniformity_small() {
850 let data: Vec<u32> = (0..20).collect();
852 let excluded = 7u32;
853 let num_runs = 50_000;
854 let mut counts = [0usize; 20];
855 let mut rng = StdRng::seed_from_u64(42);
856 for _ in 0..num_runs {
857 let v = sample_single_excluding(&mut rng, &data, excluded).unwrap();
858 counts[*v as usize] += 1;
859 }
860 assert_eq!(counts[excluded as usize], 0);
861
862 let expected = num_runs as f64 / 19.0;
863 let chi_square: f64 = counts
864 .iter()
865 .enumerate()
866 .filter(|(i, _)| *i != excluded as usize)
867 .map(|(_, &obs)| {
868 let diff = obs as f64 - expected;
869 diff * diff / expected
870 })
871 .sum();
872 assert!(chi_square < 42.31, "χ² = {chi_square}");
874 }
875
876 #[test]
877 fn sample_single_excluding_uniformity_large() {
878 let data: Vec<u32> = (0..200).collect();
880 let excluded = 99u32;
881 let num_runs = 200_000;
882 let mut counts = vec![0usize; 200];
883 let mut rng = StdRng::seed_from_u64(42);
884 for _ in 0..num_runs {
885 let v = sample_single_excluding(&mut rng, &data, excluded).unwrap();
886 counts[*v as usize] += 1;
887 }
888 assert_eq!(counts[excluded as usize], 0);
889
890 let expected = num_runs as f64 / 199.0;
891 let chi_square: f64 = counts
892 .iter()
893 .enumerate()
894 .filter(|(i, _)| *i != excluded as usize)
895 .map(|(_, &obs)| {
896 let diff = obs as f64 - expected;
897 diff * diff / expected
898 })
899 .sum();
900 assert!(chi_square < 264.69, "χ² = {chi_square}");
902 }
903
904 #[test]
905 #[allow(clippy::needless_borrows_for_generic_args)]
906 fn sample_single_excluding_accepts_owned_or_borrowed_excluded() {
907 let data: Vec<u32> = (0..10).collect();
910 let mut rng_owned = StdRng::seed_from_u64(42);
911 let mut rng_ref = StdRng::seed_from_u64(42);
912 let excluded = 3u32;
913 for _ in 0..50 {
914 let owned = sample_single_excluding(&mut rng_owned, &data, excluded);
915 let borrowed = sample_single_excluding(&mut rng_ref, &data, &excluded);
916 assert_eq!(owned, borrowed);
917 }
918 }
919
920 #[test]
921 fn sample_single_excluding_reproducibility() {
922 let data: Vec<u32> = (0..1000).collect();
923 let seed = 12345u64;
924
925 let mut rng1 = StdRng::seed_from_u64(seed);
926 let mut rng2 = StdRng::seed_from_u64(seed);
927 for _ in 0..100 {
928 let a = sample_single_excluding(&mut rng1, &data, 500);
929 let b = sample_single_excluding(&mut rng2, &data, 500);
930 assert_eq!(a, b);
931 }
932 }
933
934 #[test]
935 fn sample_single_excluding_l_reservoir_empty_returns_none() {
936 let data: [u32; 0] = [];
937 let mut rng = StdRng::seed_from_u64(42);
938 assert_eq!(
939 sample_single_excluding_l_reservoir(&mut rng, data, 7u32),
940 None
941 );
942 }
943
944 #[test]
945 fn sample_single_excluding_l_reservoir_only_excluded_returns_none() {
946 let data = [9u32, 9, 9, 9];
947 let mut rng = StdRng::seed_from_u64(42);
948 assert_eq!(
949 sample_single_excluding_l_reservoir(&mut rng, data, 9u32),
950 None
951 );
952 }
953
954 #[test]
955 fn sample_single_excluding_l_reservoir_never_returns_excluded() {
956 let data: Vec<u32> = (0..50).collect();
957 let mut rng = StdRng::seed_from_u64(42);
958 for _ in 0..1000 {
959 let v =
960 sample_single_excluding_l_reservoir(&mut rng, data.iter().copied(), 17u32).unwrap();
961 assert_ne!(v, 17);
962 assert!(v < 50);
963 }
964 }
965
966 #[test]
967 fn sample_single_excluding_l_reservoir_uniformity() {
968 let excluded = 7u32;
969 let data: Vec<u32> = (0..20).collect();
970 let num_runs = 50_000;
971 let mut counts = [0usize; 20];
972 let mut rng = StdRng::seed_from_u64(42);
973 for _ in 0..num_runs {
974 let v = sample_single_excluding_l_reservoir(&mut rng, data.iter().copied(), excluded)
975 .unwrap();
976 counts[v as usize] += 1;
977 }
978 assert_eq!(counts[excluded as usize], 0);
979
980 let expected = num_runs as f64 / 19.0;
981 let chi_square: f64 = counts
982 .iter()
983 .enumerate()
984 .filter(|(i, _)| *i != excluded as usize)
985 .map(|(_, &obs)| {
986 let diff = obs as f64 - expected;
987 diff * diff / expected
988 })
989 .sum();
990 assert!(chi_square < 42.31, "χ² = {chi_square}");
992 }
993
994 #[test]
995 #[allow(clippy::needless_borrows_for_generic_args)]
996 fn sample_single_excluding_l_reservoir_accepts_owned_or_borrowed_excluded() {
997 let data: Vec<u32> = (0..10).collect();
998 let excluded = 3u32;
999 let mut rng_owned = StdRng::seed_from_u64(42);
1000 let mut rng_ref = StdRng::seed_from_u64(42);
1001 for _ in 0..50 {
1002 let owned =
1003 sample_single_excluding_l_reservoir(&mut rng_owned, data.iter().copied(), excluded);
1004 let borrowed =
1005 sample_single_excluding_l_reservoir(&mut rng_ref, data.iter().copied(), &excluded);
1006 assert_eq!(owned, borrowed);
1007 }
1008 }
1009
1010 #[test]
1011 fn sample_single_excluding_l_reservoir_reproducibility() {
1012 let data: Vec<u32> = (0..1000).collect();
1013 let seed = 12345u64;
1014 let mut rng1 = StdRng::seed_from_u64(seed);
1015 let mut rng2 = StdRng::seed_from_u64(seed);
1016 for _ in 0..100 {
1017 let a = sample_single_excluding_l_reservoir(&mut rng1, data.iter().copied(), 500u32);
1018 let b = sample_single_excluding_l_reservoir(&mut rng2, data.iter().copied(), 500u32);
1019 assert_eq!(a, b);
1020 }
1021 }
1022}