1use rand::Rng;
4
5pub struct WeightedReservoirSampler<T> {
8 selected: Option<T>,
9 total_weight: u64,
10}
11
12impl<T> WeightedReservoirSampler<T> {
13 #[inline]
15 pub fn new() -> Self {
16 Self {
17 selected: None,
18 total_weight: 0,
19 }
20 }
21
22 pub fn sample<R: Rng + ?Sized>(&mut self, weight: u64, item: T, rng: &mut R) {
26 if self.try_select(weight, rng) {
27 self.selected = Some(item);
28 }
29 }
30
31 #[inline]
33 pub fn selected(&self) -> Option<&T> {
34 self.selected.as_ref()
35 }
36
37 #[inline]
39 pub fn into_selected(self) -> Option<T> {
40 self.selected
41 }
42
43 #[inline]
45 pub fn is_empty(&self) -> bool {
46 self.total_weight == 0
47 }
48
49 #[inline]
51 pub fn total_weight(&self) -> u64 {
52 self.total_weight
53 }
54
55 fn try_select<R: Rng + ?Sized>(&mut self, weight: u64, rng: &mut R) -> bool {
59 if weight == 0 {
61 return false;
62 }
63
64 self.total_weight += weight;
65
66 if self.total_weight == weight {
68 return true;
69 }
70
71 let draw = rng.random_range(1..=self.total_weight);
73 if draw <= weight {
74 return true;
75 }
76
77 false
78 }
79}
80
81impl<T> Default for WeightedReservoirSampler<T> {
82 fn default() -> Self {
83 Self::new()
84 }
85}