Skip to main content

weighted_list/
weighted_list.rs

1use std::collections::{ HashSet };
2use std::fmt::{ Debug, Display };
3use std::hash::{ Hash };
4
5use bon::bon;
6use itertools::Itertools;
7use num_traits as nums;
8use rand::prelude::*;
9use rand::seq::{ SliceRandom };
10
11use crate::*;
12
13
14/// A shorthand for [`WeightedList`].
15/// 
16/// If you refer to [`WeightedList`] prolifically in your code, you may wish to use this for brevity. Otherwise, the full [`WeightedList`] is recommended for clarity.
17pub type WList<V,W> = WeightedList<V,W>;
18
19
20/// A homogeneous list of weighted items with values of type `V` and weights of numerical type `W`.
21/// 
22/// Near-identical to `Vec<T>`, but stores [`WeightedItem<V,W>`](WeightedItem) objects instead. You can think of it like a `Vec<WeightedItem<V,W>>`.
23/// 
24/// # Usage
25/// 
26/// ```
27/// # use weighted_list::*;
28/// let wl: WeightedList<String, u32> = wlist![
29///     (2, "sup".to_string()),
30///     (3, "nova".to_string()),
31///     (5, "shard".to_string()),
32/// ];
33/// 
34/// for item in &wl {
35///     println!("{item}");
36/// }
37/// 
38/// if let Some(result) = wl.select_random_value(&mut rand::rng()) {
39///     println!("{result}");
40/// }
41/// ```
42/// 
43/// # Indexing
44/// 
45/// `WeightedList` uses *weighted* indexing; this is the key difference between it and a `Vec`. It's most easily explained with an example:
46/// 
47/// ```should_panic
48/// # use weighted_list::*;
49/// let wl = wlist![(1, "qi"), (2, "sup"), (5, "shard")];
50/// 
51/// let _ = wl[0]; // => WeightedItem { weight: 1, value: "qi" }
52/// let _ = wl[1]; // => WeightedItem { weight: 2, value: "sup" }
53/// let _ = wl[2]; // => WeightedItem { weight: 2, value: "sup" }
54/// let _ = wl[3]; // => WeightedItem { weight: 5, value: "shard" }
55/// let _ = wl[4]; // => WeightedItem { weight: 5, value: "shard" }
56/// let _ = wl[5]; // => WeightedItem { weight: 5, value: "shard" }
57/// let _ = wl[6]; // => WeightedItem { weight: 5, value: "shard" }
58/// let _ = wl[7]; // => WeightedItem { weight: 5, value: "shard" }
59/// let _ = wl[8]; // => panic - out of bounds!
60/// ```
61/// 
62/// In essence, each value is "copied" a number of times equal to its weight – this is what enables the weighted randomisation. But because the values are stored in [`WeightedItem`] objects, instead of actually being duplicated, larger weight values can be used without fear of performance impacts.
63/// 
64/// # Tips
65/// 
66/// - If you only need integer weights, use unsigned types like `u32` to enforce non-negative item weights.
67///   - If you really want to, you can use `NonZero<u32>` to further ensure item weights are valid.
68/// - Most methods return `&Self` or `&mut Self`, allowing you to chain methods. Here's a contrived example:
69/// 
70/// ```
71/// # use weighted_list::*;
72/// let mut list = wlist![(2, "sup")];
73/// 
74/// list.push_value("sup")
75///     .merge_duplicates()
76///     .prune()
77///     .len();
78/// ```
79#[derive(Clone, Hash, PartialEq, Eq, Default, Debug)]
80pub struct WeightedList<V, W: Weight>
81{
82    data: Vec<WeightedItem<V,W>>
83}
84
85// == CONSTRUCTORS == //
86/// Methods for constructing a [`WeightedList`].
87impl<V, W: Weight> WeightedList<V,W>
88{
89    /// Construct an empty list.
90    pub fn new() -> Self
91    {
92        Self { data: Vec::new() }
93    }
94
95    /// Construct an empty list with the specified capacity.
96    pub fn with_capacity(capacity: usize) -> Self
97    {
98        Self { data: Vec::with_capacity(capacity) }
99    }
100
101    /// Construct a [`WeightedList`] from an iterable of `value`s, merging duplicate values into single [`WeightedItem`]s.
102    /// 
103    /// Note that this has $O(n^2)$ time complexity.
104    pub fn from_expanded<I>(values: I) -> Self
105        where
106            I: IntoIterator<Item = V>,
107            V: PartialEq,
108    {
109        let mut out = WeightedList::new();
110
111        for value in values {
112            if let Some(existing) = out.iter_mut().find(|item| item.value == value) {
113                existing.weight += W::one();
114            }
115            else {
116                out.push_value(value);
117            }
118        }
119
120        out
121    }
122}
123
124/// Construct a [`WeightedList`] from the provided `(weight, value)` pairs.
125/// 
126/// # Usage
127/// 
128/// ```
129/// # use weighted_list::*;
130/// let wl = wlist![
131///     (2, "sup"),
132///     (3, "nova"),
133///     (5, "shard"),
134/// ];
135/// 
136/// let empty: WeightedList<(), usize> = wlist![];
137/// ```
138#[macro_export]
139macro_rules! wlist {
140    () => { WeightedList::new() };
141
142    ($( ($weight:expr, $value:expr) ),* $(,)?) =>
143    {
144        WeightedList::from_iter(
145            [
146                $( ($weight, $value), )*
147            ].into_iter()
148        )
149    };
150}
151
152// == CONVERSIONS FROM == //
153impl<V, W: Weight> FromIterator<(W,V)> for WeightedList<V,W> {
154    fn from_iter<I>(pairs: I) -> Self
155        where I: IntoIterator<Item = (W,V)>
156    {
157        Self {
158            data:
159                pairs.into_iter()
160                    .map(
161                        |(weight, value)| WeightedItem::new(weight, value)
162                    )
163                    .collect::<Vec<_>>()
164        }
165    }
166}
167impl<V, W: Weight> FromIterator<WeightedItem<V,W>> for WeightedList<V,W>
168{
169    fn from_iter<I>(items: I) -> Self
170        where I: IntoIterator<Item = WeightedItem<V,W>>
171    {
172        let mut data = vec![];
173
174        for item in items {
175            data.push(item);
176        }
177
178        Self { data }
179    }
180}
181
182impl<V, W: Weight> From<Vec<(W,V)>> for WeightedList<V,W> {
183    fn from(pairs: Vec<(W,V)>) -> Self {
184        pairs.into_iter().collect()
185    }
186}
187impl<V, W: Weight> From<Vec<WeightedItem<V,W>>> for WeightedList<V,W> {
188    fn from(data: Vec<WeightedItem<V,W>>) -> Self {
189        Self { data }
190    }
191}
192
193impl<V, W: Weight, const N: usize> From<[(W,V); N]> for WeightedList<V,W> {
194    fn from(pairs: [(W,V); N]) -> Self {
195        pairs.into_iter().collect()
196    }
197}
198impl<V, W: Weight, const N: usize> From<[WeightedItem<V,W>; N]> for WeightedList<V,W> {
199    fn from(pairs: [WeightedItem<V,W>; N]) -> Self {
200        pairs.into_iter().collect()
201    }
202}
203
204// == CONVERSIONS TO == //
205impl<V, W: Weight> From<WeightedList<V,W>> for Vec<WeightedItem<V,W>> {
206    fn from(list: WeightedList<V,W>) -> Self {
207        list.data
208    }
209}
210
211impl<V, W: Weight> AsRef<Vec<WeightedItem<V,W>>> for WeightedList<V,W> {
212    fn as_ref(&self) -> &Vec<WeightedItem<V,W>> {
213        &self.data
214    }
215}
216impl<V, W: Weight> AsRef<[WeightedItem<V,W>]> for WeightedList<V,W> {
217    fn as_ref(&self) -> &[WeightedItem<V,W>] {
218        &self.data
219    }
220}
221
222impl<V, W: Weight> AsMut<Vec<WeightedItem<V,W>>> for WeightedList<V,W> {
223    fn as_mut(&mut self) -> &mut Vec<WeightedItem<V,W>> {
224        &mut self.data
225    }
226}
227impl<V, W: Weight> AsMut<[WeightedItem<V,W>]> for WeightedList<V,W> {
228    fn as_mut(&mut self) -> &mut [WeightedItem<V,W>] {
229        &mut self.data
230    }
231}
232
233impl<V, W: Weight> std::ops::Deref for WeightedList<V,W> {
234    type Target = [WeightedItem<V,W>];
235
236    fn deref(&self) -> &Self::Target {
237        self.data.deref()
238    }
239}
240impl<V, W: Weight> std::ops::DerefMut for WeightedList<V,W> {
241    fn deref_mut(&mut self) -> &mut Self::Target {
242        self.data.deref_mut()
243    }
244}
245
246// == TRAIT IMPLEMENTATIONS == //
247impl<V, W: Weight> Extend<WeightedItem<V,W>> for WeightedList<V,W>
248{
249    fn extend<T>(&mut self, iter: T)
250        where T: IntoIterator<Item = WeightedItem<V,W>>
251    {
252        for item in iter {
253            self.push_item(item);
254        }
255    }
256}
257
258impl<V, W: Weight> Display for WeightedList<V,W>
259    where
260        V: Display,
261        W: Display,
262{
263    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
264    {            
265        write!(f,
266            "WeightedList[{}]",
267            self.data.iter().map(|item| item.to_string()).join(", ")
268        )
269    }
270}
271
272// == INDEXING == //
273impl<V, W: Weight> std::ops::Index<W> for WeightedList<V,W>
274{
275    type Output = WeightedItem<V,W>;
276
277    fn index(&self, weighted_index: W) -> &Self::Output
278    {
279        let mut t = W::zero();
280
281        for item in &self.data {
282            t += item.weight;
283
284            if t > weighted_index {
285                return item;
286            }
287        };
288
289        panic!(
290            "index out of bounds: the len is {:?} but the index is {:?}",
291            self.len(), weighted_index
292        );
293    }
294}
295
296impl<V, W: Weight> std::ops::IndexMut<W> for WeightedList<V,W>
297{
298    fn index_mut(&mut self, weighted_index: W) -> &mut Self::Output
299    {
300        let idx = self._unweight_index_(weighted_index);
301        &mut self.data[idx]
302    }
303}
304
305// == ITERATION == //
306impl<V, W: Weight> IntoIterator for WeightedList<V,W>
307{
308    type Item = WeightedItem<V,W>;
309    type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
310
311    /// ```compile_fail
312    /// # use weighted_list::*;
313    /// let list = wlist![]
314    /// for _ in list {}
315    /// list;  // compile error
316    /// ```
317    fn into_iter(self) -> Self::IntoIter {
318        self.data.into_iter()
319    }
320}
321
322impl<'l, V, W: Weight> IntoIterator for &'l WeightedList<V,W>
323{
324    type Item = &'l WeightedItem<V,W>;
325    type IntoIter = std::slice::Iter<'l, WeightedItem<V,W>>;
326
327    fn into_iter(self) -> Self::IntoIter {
328        self.data.iter()
329    }
330}
331
332impl<'l, V, W: Weight> IntoIterator for &'l mut WeightedList<V,W>
333{
334    type Item = &'l mut WeightedItem<V,W>;
335    type IntoIter = std::slice::IterMut<'l, WeightedItem<V,W>>;
336
337    fn into_iter(self) -> Self::IntoIter {
338        self.data.iter_mut()
339    }
340}
341
342// == ACCESSORS == //
343/// Methods for accessing data of the list.
344impl<V, W: Weight> WeightedList<V,W>
345{
346    /// Get an iterator over the weights of each item in the list.
347    /// 
348    /// # Usage
349    /// 
350    /// ```
351    /// # use weighted_list::*;
352    /// let wl = wlist![(2, "sup"), (3, "nova")];
353    /// 
354    /// for weight in wl.weights() {
355    ///     println!("{weight}");    // => 2, 3
356    /// }
357    /// 
358    /// let weights = wl.weights().collect::<Vec<u32>>();
359    /// assert_eq!(weights, vec![2, 3]);
360    /// ```
361    pub fn weights(&self) -> impl Iterator<Item = W>
362    {
363        self.data.iter().map(|item| item.weight)
364    }
365
366    /// Get an iterator over the values of each item in the list.
367    /// 
368    /// # Usage
369    /// 
370    /// ```
371    /// # use weighted_list::*;
372    /// let wl = wlist![(2, "sup"), (3, "nova")];
373    /// 
374    /// for value in wl.values() {
375    ///     println!("{value}");    // => &"sup", &"nova"
376    /// }
377    /// 
378    /// let values = wl.values().collect::<Vec<&&str>>();
379    /// assert_eq!(values, vec![&"sup", &"nova"]);
380    /// ```
381    pub fn values(&self) -> impl Iterator<Item = &V>
382    {
383        self.data.iter().map(|item| &item.value)
384    }
385
386    /// Get a vector of references to items in the list.
387    /// 
388    /// # Usage
389    /// 
390    /// ```
391    /// # use weighted_list::*;
392    /// let wl = wlist![(2, "sup"), (3, "nova")];
393    /// let items = wl.items();
394    /// 
395    /// assert_eq!(items[0].value, "sup");
396    /// assert_eq!(items[1].value, "nova");
397    /// ```
398    /// 
399    /// # Tips
400    /// 
401    /// - This may be helpful for [non-weighted indexing](WeightedList#indexing).
402    /// - If you'd like a vector of owned [`WeightedItem`](WeightedItem)s, [`WeightedList`](WeightedList) implements `Into<Vec>`:
403    /// 
404    /// ```rust
405    /// # use weighted_list::*;
406    /// let wl = wlist![(2, "sup"), (3, "nova")];
407    /// let items = Vec::from(wl);
408    /// 
409    /// assert_eq!(items[0].value, "sup");
410    /// assert_eq!(items[1].value, "nova");
411    /// ```
412    pub fn items(&self) -> Vec<&WeightedItem<V,W>>
413    {
414        self.data.iter().collect_vec()
415    }
416
417    /// Get an iterator over (weight, value) tuples representing each item in the list.
418    /// 
419    /// # Usage
420    /// 
421    /// ```
422    /// # use weighted_list::*;
423    /// let wl = wlist![(2, "sup"), (3, "nova")];
424    /// 
425    /// for (weight, value) in wl.raw() {
426    ///     println!("({weight}, {value})");    // => (2, "sup"), (3, "nova")
427    /// }
428    /// 
429    /// let raw = wl.raw().collect::<Vec<_>>();
430    /// assert_eq!(raw, vec![(2, &"sup"), (3, &"nova")]);
431    /// ```
432    /// 
433    /// # Notes
434    /// 
435    /// This function acts essentially like an "un-constructor", giving back the tuples you usually use to construct a [`WeightedList`]:
436    /// 
437    /// ```
438    /// # use weighted_list::*;
439    /// let wl = WeightedList::from([(2, "sup"), (3, "nova")]);
440    /// let rl = wl.raw();    // => [(2, &"sup"), (3, &"nova")]
441    /// ```
442    pub fn raw(&self) -> impl Iterator<Item = (W,&V)>
443    {
444        self.data.iter().map(|item| (item.weight, &item.value))
445    }
446
447    /// Get an iterator over each value in the list, repeated a number of times equal to its weight.
448    /// 
449    /// # Usage
450    /// 
451    /// ```
452    /// # use weighted_list::*;
453    /// let wl = wlist![(2, "sup"), (3, "nova")];
454    /// 
455    /// let mut test = vec![];
456    /// for value in wl.expanded() {
457    ///     test.push(*value);
458    /// }
459    /// 
460    /// assert_eq!(test, vec!["sup", "sup", "nova", "nova", "nova"]);
461    /// ```
462    /// 
463    /// # Notes
464    /// 
465    /// - Since repeating an entity a non-integer number of times is undefined, this requires `W` to be an integer type.
466    pub fn expanded(&self) -> impl Iterator<Item = &V>
467        where W: nums::PrimInt
468    {
469        self.data
470            .iter()
471            .flat_map(|item| std::iter::repeat_n(
472                &item.value,
473                nums::cast::<W, usize>(item.weight).unwrap_or(0)
474            ))
475    }
476
477    /// Get a vector of the weights of each item in the list.
478    /// 
479    /// # Usage
480    /// 
481    /// ```
482    /// # use weighted_list::*;
483    /// let wl = wlist![(2, "sup"), (3, "nova")];
484    /// 
485    /// let weights = wl.collect_weights();
486    /// 
487    /// assert_eq!(weights[0], 2);
488    /// assert_eq!(weights[1], 3);
489    /// ```
490    pub fn collect_weights(&self) -> Vec<W>
491    {
492        self.weights().collect_vec()
493    }
494
495    /// Get a vector of the values of each item in the list.
496    /// 
497    /// # Usage
498    /// 
499    /// ```
500    /// # use weighted_list::*;
501    /// let wl = wlist![(2, "sup"), (3, "nova")];
502    /// 
503    /// let weights = wl.collect_values();
504    /// 
505    /// assert_eq!(weights[0], &"sup");
506    /// assert_eq!(weights[1], &"nova");
507    /// ```
508    pub fn collect_values(&self) -> Vec<&V>
509    {
510        self.values().collect_vec()
511    }
512}
513
514// == PROPERTIES == //
515/// Methods for computing properties of the list.
516impl<V, W: Weight> WeightedList<V,W>
517{
518    /// Sum the weights of all items in the list.
519    /// 
520    /// # Notes
521    /// - This is not the number of items in the list – use [`.total_items()`](Self::total_items) for that.
522    /// - `self.len() == 0` does not imply the list is empty – items may have zero or negative weights! To check if the list is empty, use [`.is_empty()`](Self::is_empty) instead.
523    pub fn len(&self) -> W
524    {
525        self.data.iter().map(|item| item.weight).sum()
526    }
527
528    pub fn capacity(&self) -> usize
529    {
530        self.data.capacity()
531    }
532
533    /// How many items/values are in the list?
534    /// 
535    /// Note that this is not equivalent to [`self.len()`](Self::len), which is the total weights of all items in the list.
536    /// 
537    /// # Usage
538    /// 
539    /// ```
540    /// # use weighted_list::*;
541    /// let wl = wlist![(2, "sup"), (3, "nova")];
542    /// 
543    /// assert_eq!(wl.total_items(), 2);
544    /// assert_eq!(wl.len(), 5);
545    /// ```
546    pub fn total_items(&self) -> usize
547    {
548        self.data.len()
549    }
550
551    /// Does the list contain no items?
552    /// 
553    /// # Usage
554    /// 
555    /// ```
556    /// # use weighted_list::*;
557    /// let empty: WeightedList<(), usize> = wlist![];
558    /// assert_eq!(empty.is_empty(), true);
559    /// 
560    /// assert_eq!(wlist![(0, "qi")].is_empty(), false);
561    /// ```
562    /// 
563    /// # Notes
564    /// - [`self.len()`](Self::len) returning `0` does not guarantee `self.is_empty()`, since items could have zero or negative weights. This method only returns `true` if no items are in the list at all.
565    pub fn is_empty(&self) -> bool
566    {
567        self.data.is_empty()
568    }
569
570    /// Do all items have a weight of `0`?
571    /// 
572    /// # Usage
573    /// 
574    /// ```
575    /// # use weighted_list::*;
576    /// assert_eq!(wlist![(0, "qi")].is_zero(), true);
577    /// assert_eq!(wlist![(1, "qi")].is_zero(), false);
578    /// assert_eq!(wlist![(0, "qi"), (0, "xi")].is_zero(), true);
579    /// assert_eq!(wlist![(0, "qi"), (1, "vi")].is_zero(), false);
580    /// 
581    /// let empty: WeightedList<(), usize> = wlist![];
582    /// assert_eq!(empty.is_zero(), true);
583    /// ```
584    /// 
585    /// # Notes
586    /// - Returns `true` if `.is_empty()` is `true`.
587    pub fn is_zero(&self) -> bool
588    {
589        self.is_empty()
590        || self.data.iter().all(|item| item.weight == W::zero())
591    }
592
593    /// Do any items have a negative weight?
594    /// 
595    /// # Usage
596    /// 
597    /// ```
598    /// # use weighted_list::*;
599    /// assert_eq!(wlist![(0, "qi"), ( 2, "sup")  ].has_negative_weights(), false);
600    /// assert_eq!(wlist![(0, "qi"), (-1, "aleph")].has_negative_weights(), true);
601    /// ```
602    /// 
603    /// # Notes
604    /// - Returns `false` if the list is empty.
605    /// - If you only need integer weights, you should probably use an unsigned type like `u32` to ensure weights are never negative.
606    pub fn has_negative_weights(&self) -> bool
607    {
608        self.data.iter().any(|item| item.weight < W::zero())
609    }
610}
611
612// == LIST QUERYING == //
613/// Methods specialised from `Vec<>` for querying the list.
614impl<V, W: Weight> WeightedList<V,W>
615    where
616        V: Clone
617{
618    /// Return a clone of the list with items sorted in ascending order of weights.
619    /// 
620    /// Orderings of items with equivalent weights is (currently) undefined behaviour.
621    pub fn sorted(&self) -> Self
622        where V: Eq, W: Ord
623    {
624        let mut out = self.clone();
625        out.sort();
626        out
627    }
628    
629    /// Return a clone of the list with items reversed.
630    pub fn reversed(&self) -> Self
631    {
632        let mut out = self.clone();
633        out.reverse();
634        out
635    }
636}
637
638// == LIST MUTATION == //
639/// Methods specialised from `Vec<>` for mutating the list.
640impl<V, W: Weight> WeightedList<V,W>
641{
642    pub fn reserve(&mut self, additional: usize) -> &mut Self
643    {
644        self.data.reserve(additional);
645        self
646    }
647
648    /// Append an item to the end of the list.
649    /// 
650    /// # Usage
651    /// 
652    /// ```
653    /// # use weighted_list::*;
654    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
655    /// 
656    /// assert_eq!(
657    ///     *wl.push_item(wl[0].clone()),
658    ///     wlist![(2, "sup"), (3, "nova"), (5, "shard"), (2, "sup")],
659    /// )
660    /// ```
661    pub fn push_item(&mut self, item: WeightedItem<V,W>) -> &mut Self
662    {
663        self.data.push(item);
664        self
665    }
666
667    /// Append a new item with `value` and `weight` to the end of the list.
668    /// 
669    /// # Usage
670    /// 
671    /// ```
672    /// # use weighted_list::*;
673    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
674    /// 
675    /// assert_eq!(
676    ///     *wl.push_new_item(7, "cortex"),
677    ///     wlist![(2, "sup"), (3, "nova"), (5, "shard"), (7, "cortex")],
678    /// )
679    /// ```
680    pub fn push_new_item(&mut self, weight: W, value: V) -> &mut Self
681    {
682        self.push_item(WeightedItem { weight, value })
683    }
684    
685    /// Append a new item with `value` and a weight of `1` to the end of the list.
686    /// 
687    /// # Usage
688    /// 
689    /// ```
690    /// # use weighted_list::*;
691    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
692    /// 
693    /// assert_eq!(
694    ///     *wl.push_value("elysion"),
695    ///     wlist![(2, "sup"), (3, "nova"), (5, "shard"), (1, "elysion")],
696    /// )
697    /// ```
698    pub fn push_value(&mut self, value: V) -> &mut Self
699    {
700        self.push_item(WeightedItem::unit(value))
701    }
702
703    /// Insert an item into the list at `weighted_index`. If `weighted_index >= len`, the item is appended to the end (the function does *not* panic).
704    pub fn insert_item(&mut self,
705        weighted_index: W,
706        item: WeightedItem<V,W>
707    ) -> &mut Self
708    {
709        self.data.insert(self._unweight_index_nopanic_(weighted_index), item);
710        self
711    }
712
713    /// Insert a new item with `value` and `weight` into the list at `weighted_index`. If `weighted_index >= len`, the item is appended to the end (the function does *not* panic).
714    pub fn insert_new_item(&mut self,
715        weighted_index: W,
716        (weight, value): (W, V)
717    ) -> &mut Self
718    {
719        self.insert_item(weighted_index, WeightedItem::new(weight, value))
720    }
721
722    /// Insert an item with `value` and a weight of `1` into the list at `weighted_index`. If `weighted_index >= len`, the item is appended to the end (the function does *not* panic).
723    pub fn insert_value(&mut self,
724        weighted_index: W,
725        value: V,
726    ) -> &mut Self
727    {
728        self.insert_item(weighted_index, WeightedItem::unit(value))
729    }
730
731    /// Move all items in `other` into `self`, leaving `other` empty.
732    pub fn append(&mut self, other: &mut WeightedList<V,W>) -> &mut Self
733    {
734        self.data.append(&mut other.data);
735        self
736    }
737
738    /// Reverse the order of items in the list (in-place).
739    pub fn reverse(&mut self) -> &mut Self
740    {
741        self.data.reverse();
742        self
743    }
744
745    /// Swap the items at weighted indices `left` and `right`.
746    /// 
747    /// # Panics
748    /// 
749    /// Panics if `left` or `right` are out of bounds.
750    pub fn swap(&mut self, left: W, right: W) -> &mut Self
751    {
752        let l = self._unweight_index_(left);
753        let r = self._unweight_index_(right);
754        self.data.swap(l, r);
755        self
756    }
757
758    /// Removes the last item from the list and returns it, or `None` if the list is empty.
759    pub fn pop(&mut self) -> Option<WeightedItem<V,W>>
760    {
761        self.data.pop()
762    }
763
764    pub fn pop_if(&mut self,
765        predicate: impl FnOnce(&mut WeightedItem<V,W>) -> bool
766    ) -> Option<WeightedItem<V,W>>
767    {
768        self.data.pop_if(predicate)
769    }
770
771    /// Remove the entire item at `weighted_index` and return it.
772    /// 
773    /// # Panics
774    /// 
775    /// Panics if `weighted_index` is out of bounds.
776    pub fn remove_at(&mut self, weighted_index: W) -> WeightedItem<V,W>
777    {
778        self.data.remove(self._unweight_index_(weighted_index))
779    }
780
781    /// Remove elements from the end of the list, such that [`self.len()`](Self::len) == `len`. The last element may have its weight decreased.
782    /// 
783    /// # Usage
784    /// 
785    /// ```rust
786    /// # use weighted_list::*;
787    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
788    /// 
789    /// wl.truncate(5);
790    /// assert_eq!(wl, wlist![(2, "sup"), (3, "nova")]);
791    /// 
792    /// wl.truncate(3);
793    /// assert_eq!(wl, wlist![(2, "sup"), (1, "nova")]);
794    /// 
795    /// wl.truncate(0);
796    /// assert_eq!(wl, wlist![]);
797    /// ```
798    pub fn truncate(&mut self, len: W) -> &mut Self
799        where W: Debug
800    {
801        if len == W::zero() {
802            return self.clear();
803        }
804
805        let mut t = W::zero();
806        let mut n = 0;
807        
808        for (i, each) in self.iter_mut().enumerate() {
809            t += each.weight;
810
811            if t >= len {
812                if t > len {
813                    each.weight -= t - len;
814                }
815
816                n = i + 1;
817                break;
818            }
819        }
820
821        self.data.truncate(n);
822
823        self
824    }
825
826    /// Retain only items that fulfil `predicate``.
827    pub fn retain<F>(&mut self, predicate: F) -> &mut Self
828        where F: FnMut(&WeightedItem<V,W>) -> bool
829    {
830        self.data.retain(predicate);
831        self
832    }
833
834    /// Retain only items that fulfil `predicate``, passing a mutable reference to the predicate.
835    pub fn retain_mut<F>(&mut self, predicate: F) -> &mut Self
836        where F: FnMut(&mut WeightedItem<V,W>) -> bool
837    {
838        self.data.retain_mut(predicate);
839        self
840    }
841
842    /// Clear the list, removing all items (in-place).
843    /// 
844    /// If you'd like to set the weights of all items to `0`, you can use [`.zero_all_weights()`](Self::zero_all_weights).
845    pub fn clear(&mut self) -> &mut Self
846    {
847        self.data.clear();
848        self
849    }
850}
851
852// == SPECIALISED QUERYING == //
853/// Special [`WeightedList`]-specific methods for querying the list.
854impl<V, W: Weight> WeightedList<V,W>
855{
856    /// Does any item in the list have a value equal to `value`?
857    pub fn contains_value(&self, value: &V) -> bool
858        where V: PartialEq
859    {
860        self.data.iter().any(|item| item.value == *value)
861    }
862    
863    /// Does any item in the list have a weight equal to `weight`?
864    pub fn contains_weight(&self, weight: W) -> bool
865    {
866        self.data.iter().any(|item| item.weight == weight)
867    }
868}
869
870// == SPECIALISED MUTATION == //
871/// Special [`WeightedList`]-specific methods for mutating the list.
872impl<V, W: Weight> WeightedList<V,W>
873{
874    /// Remove all items with non-positive weight.
875    pub fn prune(&mut self) -> &mut Self
876    {
877        self.data.retain(|item| item.weight > W::zero());
878        self
879    }
880
881    /// Return a clone of the list with all items having a non-positive weight removed.
882    /// 
883    /// Out-of-place version of [`.prune()`](Self::prune).
884    pub fn pruned(&self) -> Self
885        where V: Clone
886    {
887        let mut out = self.clone();
888        out.prune();
889        out
890    }
891
892    /// Find the first occurrence (from the left) of an item with `value`, and remove the entire item.
893    /// 
894    /// # Usage
895    /// 
896    /// ```
897    /// # use weighted_list::*;
898    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
899    /// 
900    /// assert_eq!(
901    ///     *wl.remove_value_first(&"nova"),
902    ///     wlist![(2, "sup"), (5, "shard")],
903    /// )
904    /// ```
905    pub fn remove_value_first(&mut self, value: &V) -> &mut Self
906        where V: PartialEq
907    {
908        self.remove_first_where(|item| item.value == *value)
909    }
910
911    /// Find the last occurrence (from the right) of an item with `value`, and remove the entire item.
912    /// 
913    /// # Usage
914    /// 
915    /// ```
916    /// # use weighted_list::*;
917    /// let mut wl = wlist![(0, "qi"), (1, "qi"), (2, "sup")];
918    /// 
919    /// assert_eq!(
920    ///     *wl.remove_value_last(&"qi"),
921    ///     wlist![(0, "qi"), (2, "sup")],
922    /// )
923    /// ```
924    pub fn remove_value_last(&mut self, value: &V) -> &mut Self
925        where V: PartialEq
926    {
927        self.remove_last_where(|item| item.value == *value)
928    }
929
930    /// Find the first occurrence (from the left) of an item that fulfils `predicate`, and remove the entire item.
931    /// 
932    /// # Usage
933    /// 
934    /// ```
935    /// # use weighted_list::*;
936    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
937    /// 
938    /// assert_eq!(
939    ///     *wl.remove_first_where(|item| item.weight > 2),
940    ///     wlist![(2, "sup"), (5, "shard")],
941    /// )
942    /// ```
943    pub fn remove_first_where<F>(&mut self, predicate: F) -> &mut Self
944        where F: FnMut(&WeightedItem<V,W>) -> bool
945    {
946        if let Some(idx) = self.iter().position(predicate) {
947            self.data.remove(idx);
948        }
949
950        self
951    }
952
953    /// Find the last occurrence (from the right) of an item that fulfils `predicate`, and remove the entire item.
954    /// 
955    /// # Usage
956    /// 
957    /// ```
958    /// # use weighted_list::*;
959    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
960    /// 
961    /// assert_eq!(
962    ///     *wl.remove_last_where(|item| item.weight > 2),
963    ///     wlist![(2, "sup"), (3, "nova")],
964    /// );
965    /// ```
966    pub fn remove_last_where<F>(&mut self, predicate: F) -> &mut Self
967        where F: FnMut(&WeightedItem<V,W>) -> bool
968    {
969        if let Some(idx) = self.iter().rposition(predicate) {
970            self.data.remove(idx);
971        }
972
973        self
974    }
975
976    /// Set the weight of all items to `0`.
977    /// 
978    /// # Usage
979    /// 
980    /// ```
981    /// # use weighted_list::*;
982    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
983    /// 
984    /// assert_eq!(
985    ///     *wl.zero_all_weights(),
986    ///     wlist![(0, "sup"), (0, "nova"), (0, "shard")],
987    /// )
988    /// ```
989    pub fn zero_all_weights(&mut self) -> &mut Self
990    {
991        for item in &mut self.data {
992            item.weight = W::zero();
993        }
994
995        self
996    }
997
998    /// Set the weight of all items to `weight`.
999    /// 
1000    /// # Usage
1001    /// 
1002    /// ```
1003    /// # use weighted_list::*;
1004    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1005    /// 
1006    /// assert_eq!(
1007    ///     *wl.set_all_weights(1),
1008    ///     wlist![(1, "sup"), (1, "nova"), (1, "shard")],
1009    /// )
1010    /// ```
1011    pub fn set_all_weights(&mut self, weight: W) -> &mut Self
1012    {
1013        for item in &mut self.data {
1014            item.weight = weight;
1015        }
1016
1017        self
1018    }
1019
1020    /// Return a clone of the list with all item weights normalised such that they sum to `1.0`.
1021    /// 
1022    /// # Usage
1023    /// 
1024    /// ```
1025    /// # use weighted_list::*;
1026    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1027    /// 
1028    /// assert_eq!(
1029    ///     wl.normalised().ok(),
1030    ///     Some(wlist![(0.2, "sup"), (0.3, "nova"), (0.5, "shard")])
1031    /// );
1032    /// ```
1033    pub fn normalised(&mut self) -> Result<WeightedList<V, f64>, &str>
1034        where V: Clone
1035    {
1036        let total;
1037
1038        if let Some(t) = nums::cast::<W, f64>(self.len()) {
1039            total = t;
1040        } else {
1041            return Err("Error casting total weights to `f64`");
1042        };
1043
1044        let items =
1045            self.data
1046                .iter()
1047                .map(|item| {
1048                    let weight = nums::cast::<W, f64>(item.weight)?;
1049                    Some(WeightedItem {
1050                        weight: weight / total,
1051                        value: item.value.clone()
1052                    })
1053                })
1054                .collect::<Option<Vec<WeightedItem<V, f64>>>>()
1055        ;
1056
1057        match items {
1058            Some(data) => Ok(WeightedList { data }),
1059            None       => Err("Error casting weights to `f64`")
1060        }
1061    }
1062}
1063
1064/// Methods for merging items into the list.
1065/// 
1066/// This involves comparing item values to check for duplicates, hence requiring `V: PartialEq`.
1067impl<V, W: Weight> WeightedList<V,W>
1068    where
1069        V: PartialEq
1070{
1071    /// Merge an item into the list. If an item with the same value already exists, add the weight of the new item to the existing item. Otherwise, append the new item to the list.
1072    /// 
1073    /// # Tips
1074    /// 
1075    /// - Use this method when you already have an existing [`WeightedItem`] instance. If you're going to construct a new [`WeightedItem`], [`.merge_new_item()`](Self::merge_new_item) is probably more convenient.
1076    /// 
1077    /// # Usage
1078    /// 
1079    /// ```
1080    /// # use weighted_list::*;
1081    /// let mut wl = wlist![(1, "sup")];
1082    /// 
1083    /// let item = WeightedItem::new(2, "sup");
1084    /// wl.merge_item(item);
1085    /// assert!(wl == wlist![(3, "sup")]);
1086    /// // "sup" merged with existing instance
1087    /// 
1088    /// let item = WeightedItem::unit("elysion");
1089    /// wl.merge_item(item);
1090    /// assert!(wl == wlist![(3, "sup"), (1, "elysion")]);
1091    /// // "elysion" appended to end
1092    /// ```
1093    pub fn merge_item(&mut self, item: WeightedItem<V,W>) -> &mut Self
1094    {
1095        if let Some(existing) = self.data.iter_mut().find(|each| each.value == item.value) {
1096            existing.weight += item.weight;
1097        }
1098        else {
1099            self.data.push(item);
1100        }
1101
1102        self
1103    }
1104
1105    /// Merge a new item with `value` and `weight` into the list.
1106    /// 
1107    /// See [`.merge_item()`](Self::merge_item) for details.
1108    pub fn merge_new_item(&mut self, weight: W, value: V) -> &mut Self
1109    {
1110        self.merge_item(WeightedItem { weight, value })
1111    }
1112
1113    /// Merge a new item with `value` and a weight of `1` into the list.
1114    /// 
1115    /// See [`.merge_item()`](Self::merge_item) for details.
1116    pub fn merge_value(&mut self, value: V) -> &mut Self
1117    {
1118        self.merge_item(WeightedItem::unit(value))
1119    }
1120
1121    /// Merge the items of `other` into `self`, leaving `other` empty.
1122    /// 
1123    /// See [`.merge_item()`](Self::merge_item) for details.
1124    pub fn merge_with(&mut self, other: WeightedList<V,W>) -> &mut Self
1125    {
1126        for item in other {
1127            self.merge_item(item);
1128        }
1129
1130        self
1131    }
1132
1133    /// Merge any items in the list with duplicate values by combining their weights with the first instance.
1134    /// 
1135    /// # Usage
1136    /// 
1137    /// ```
1138    /// # use weighted_list::*;
1139    /// let mut wl = wlist![
1140    ///     (1, "sup"),
1141    ///     (2, ""),
1142    ///     (20, "sup")
1143    /// ];
1144    /// 
1145    /// assert_eq!(
1146    ///     *wl.merge_duplicates(),
1147    ///     wlist![
1148    ///         (21, "sup"),
1149    ///         (2, ""),
1150    ///     ]
1151    /// );
1152    /// ```
1153    pub fn merge_duplicates(&mut self) -> &mut Self
1154    {
1155        let orig = std::mem::replace(self, WeightedList::new());
1156        self.merge_with(orig);
1157        self
1158    }
1159}
1160
1161/// Methods for taking items from the list.
1162/// 
1163/// This involves returning values without removing the existing one, hence requiring `V: Clone`.
1164impl<V, W: Weight> WeightedList<V,W>
1165    where
1166        V: Clone
1167{
1168    /// Decrement the weight of the item at `weighted_index` by `1`. If its weight becomes non-positive as a result, remove the entire item. Returns a clone of the item with its updated weight.
1169    /// 
1170    /// # Usage
1171    /// 
1172    /// ```
1173    /// # use weighted_list::*;
1174    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1175    /// 
1176    /// wl.take_one_at(2);
1177    /// assert_eq!( wl, wlist![(2, "sup"), (2, "nova"), (5, "shard")] );
1178    /// 
1179    /// wl.take_one_at(2);
1180    /// assert_eq!( wl, wlist![(2, "sup"), (1, "nova"), (5, "shard")] );
1181    /// 
1182    /// wl.take_one_at(2);
1183    /// assert_eq!( wl, wlist![(2, "sup"), (5, "shard")] );
1184    /// ```
1185    pub fn take_one_at(&mut self, weighted_index: W) -> WeightedItem<V,W>
1186    {
1187        self.take_by_at(W::one(), weighted_index)
1188    }
1189
1190    /// Decrement the weight of the item at `weighted_index` by `decrement`. If its weight becomes non-positive as a result, remove the entire item. Returns a clone of the item with its updated weight.
1191    /// 
1192    /// # Panics
1193    /// 
1194    /// Panics if `weighted_index` is out of bounds.
1195    /// 
1196    /// # Usage
1197    /// 
1198    /// ```
1199    /// # use weighted_list::*;
1200    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1201    /// 
1202    /// wl.take_by_at(2, 2);
1203    /// assert_eq!( wl, wlist![(2, "sup"), (1, "nova"), (5, "shard")] );
1204    /// 
1205    /// wl.take_by_at(2, 2);
1206    /// assert_eq!( wl, wlist![(2, "sup"), (5, "shard")]);
1207    /// ```
1208    pub fn take_by_at(&mut self, decrement: W, weighted_index: W) -> WeightedItem<V,W>
1209    {
1210        let idx = self._unweight_index_(weighted_index);
1211        let target = &mut self.data[idx];
1212
1213        if decrement >= target.weight {
1214            target.weight = W::zero();
1215            self.data.remove(idx)
1216        }
1217        else {
1218            target.weight -= decrement;
1219            target.clone()
1220        }
1221    }
1222
1223    /// Remove the entire item at `weighted_index`.
1224    /// 
1225    /// # Usage
1226    /// 
1227    /// ```
1228    /// # use weighted_list::*;
1229    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1230    /// 
1231    /// wl.take_entire_at(3);
1232    /// assert_eq!( wl, wlist![(2, "sup"), (5, "shard")] );
1233    /// ```
1234    pub fn take_entire_at(&mut self, weighted_index: W) -> WeightedItem<V,W>
1235    {
1236        self.remove_at(weighted_index)
1237    }
1238}
1239
1240// == RANDOMISATION == //
1241/// Methods for out-of-place random sampling from a list.
1242impl<V, W: Weight> WeightedList<V,W>
1243{
1244    fn _get_random_weighted_index_up_to_<RNG>(&self, rng: &mut RNG, upper: W) -> Option<W>
1245        where RNG: Rng + ?Sized
1246    {
1247        let len:    f64 = nums::cast::<W, f64>(upper)?;
1248        let scalar: f64 = rng.random();
1249
1250        let idx = (len * scalar).floor();
1251        let out = nums::cast::<f64, W>(idx)?;
1252
1253        Some(out)
1254    }
1255
1256    fn _get_random_weighted_index_<RNG>(&self, rng: &mut RNG) -> Option<W>
1257        where RNG: Rng + ?Sized
1258    {
1259        self._get_random_weighted_index_up_to_(rng, self.len())
1260    }
1261
1262    /// Select a random item from the list and return its value, using weighted randomisation.
1263    /// 
1264    /// # Usage
1265    /// 
1266    /// ```
1267    /// # use weighted_list::*;
1268    /// let wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1269    /// 
1270    /// wl.select_random_value(&mut rand::rng());
1271    /// // could give:
1272    /// //   - Some("sup"  ) with 20% probability
1273    /// //   - Some("nova" ) with 30% probability
1274    /// //   - Some("shard") with 50% probability
1275    /// ```
1276    pub fn select_random_value<RNG>(&self, rng: &mut RNG) -> Option<&V>
1277        where RNG: Rng + ?Sized
1278    {
1279        let out = &self.select_random_item(rng)?.value;
1280        Some(out)
1281    }
1282
1283    /// Select a random item from the list, using weighted randomisation.
1284    /// 
1285    /// # Usage
1286    /// 
1287    /// ```
1288    /// # use weighted_list::*;
1289    /// let wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1290    /// 
1291    /// wl.select_random_item(&mut rand::rng());
1292    /// // could give:
1293    /// //   - Some(WeightedItem { 2, "sup"   }) with 20% probability
1294    /// //   - Some(WeightedItem { 3, "nova"  }) with 30% probability
1295    /// //   - Some(WeightedItem { 5, "shard" }) with 50% probability
1296    /// ```
1297    pub fn select_random_item<RNG>(&self, rng: &mut RNG) -> Option<&WeightedItem<V,W>>
1298        where RNG: Rng + ?Sized
1299    {
1300        if self.data.is_empty() { return None }
1301
1302        let idx = self._get_random_weighted_index_(rng)?;
1303        let out = &self[idx];
1304
1305        Some(out)
1306    }
1307}
1308
1309/// Methods for in-place random sampling from a list, decreasing weights of items that are chosen.
1310impl<V, W: Weight> WeightedList<V,W>
1311    where
1312        V: Clone
1313{
1314    /// Select a random item from the list using weighted randomisation, and decrement its weight by `1`.
1315    /// 
1316    /// # Usage
1317    /// 
1318    /// ```
1319    /// # use weighted_list::*;
1320    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1321    /// 
1322    /// wl.take_one_random(&mut rand::rng());
1323    /// // could give:
1324    /// //   - Some(WeightedItem { 1, "sup"   })   with 20% probability
1325    /// //   - Some(WeightedItem { 2, "nova"  })  with 30% probability
1326    /// //   - Some(WeightedItem { 4, "shard" }) with 50% probability
1327    /// ```
1328    pub fn take_one_random<RNG>(&mut self, rng: &mut RNG) -> Option<WeightedItem<V,W>>
1329        where RNG: Rng + ?Sized
1330    {
1331        self.take_by_random(rng, W::one())
1332    }
1333
1334    /// Select a random item from the list using weighted randomisation, and decrement its weight by `decrement`.
1335    /// 
1336    /// # Usage
1337    /// 
1338    /// ```
1339    /// # use weighted_list::*;
1340    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1341    /// 
1342    /// wl.take_by_random(&mut rand::rng(), 2);
1343    /// // could give:
1344    /// //   - Some(WeightedItem { 0, "sup"   })   with 20% probability
1345    /// //   - Some(WeightedItem { 1, "nova"  })  with 30% probability
1346    /// //   - Some(WeightedItem { 3, "shard" }) with 50% probability
1347    /// ```
1348    pub fn take_by_random<RNG>(&mut self, rng: &mut RNG, decrement: W) -> Option<WeightedItem<V,W>>
1349        where RNG: Rng + ?Sized
1350    {
1351        if self.data.is_empty() { return None }
1352
1353        let idx = self._get_random_weighted_index_(rng)?;
1354        let out = self.take_by_at(decrement, idx);
1355
1356        Some(out)
1357    }
1358
1359    /// Select and remove a random item from the list, using weighted randomisation.
1360    /// 
1361    /// # Usage
1362    /// 
1363    /// ```
1364    /// # use weighted_list::*;
1365    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1366    /// 
1367    /// wl.take_entire_random(&mut rand::rng());
1368    /// // could give:
1369    /// //   - Some(WeightedItem { 2, "sup"   })   with 20% probability
1370    /// //   - Some(WeightedItem { 3, "nova"  })  with 30% probability
1371    /// //   - Some(WeightedItem { 5, "shard" }) with 50% probability
1372    /// 
1373    /// assert!( wl.total_items() == 2 );
1374    /// ```
1375    pub fn take_entire_random<RNG>(&mut self, rng: &mut RNG) -> Option<WeightedItem<V,W>>
1376        where RNG: Rng + ?Sized
1377    {
1378        if self.data.is_empty() { return None }
1379
1380        let idx = self._get_random_weighted_index_(rng)?;
1381        let out = self.take_entire_at(idx);
1382
1383        Some(out)
1384    }
1385}
1386
1387/// Random sampling methods which use the bon builder syntax.
1388#[bon]
1389impl<V, W: Weight> WeightedList<V,W>
1390    where
1391        V: Clone + Eq
1392{
1393    /// Select `count` values using weighted randomisation.
1394    /// 
1395    /// Call this method using `bon` builder syntax (see § Usage below).
1396    /// 
1397    /// # Options
1398    /// 
1399    /// ```text
1400    /// rng:       RNG,
1401    /// count:     usize,
1402    /// replace:   bool = true,
1403    /// decrement: W = 1,
1404    /// ```
1405    /// 
1406    /// - `count`: How many values to select.
1407    /// - `replace` (optional): If `true`, items do not have their weight decremented after selection, and infinite values can be selected. If `false`, items have their weight decremented after selection – this would mean at most [`self.len()`](Self::len) values are returned.
1408    /// - `decrement` (optional): How much to decrement weights by if `replace` is `false`.
1409    /// 
1410    /// # Usage
1411    /// 
1412    /// This method uses the bon builder syntax:
1413    /// 
1414    /// ```
1415    /// # use weighted_list::*;
1416    /// let mut pool = wlist![
1417    ///     (2, "sup".to_string()),
1418    ///     (3, "nova".to_string()),
1419    ///     (5, "shard".to_string()),
1420    /// ];
1421    /// 
1422    /// let mut rng = rand::rng();
1423    /// 
1424    /// // with replacement
1425    /// let selected =
1426    ///     pool.select_random_values()
1427    ///         .rng(&mut rng)
1428    ///         .count(3)
1429    ///         .call();
1430    /// 
1431    /// assert!(selected.len() == 3);
1432    /// 
1433    /// // without replacement
1434    /// let selected =
1435    ///     pool.select_random_values()
1436    ///         .rng(&mut rng)
1437    ///         .count(10)
1438    ///         .replace(false)
1439    ///         .decrement(2)
1440    ///         .call();
1441    /// 
1442    /// assert!(selected.len() == 6);
1443    /// ```
1444    /// 
1445    /// # Notes
1446    /// 
1447    /// - It is not guaranteed that the results will have exactly `count` values.
1448    ///   - If `count` exceeds the maximum possible number of values that can be returned, excess iterations will be skipped.
1449    ///   - If selection for an iteration fails, that value is excluded from the output list.
1450    /// - This method reserves a `Vec<>` with capacity `count` initially, so be careful of passing in extremely large `count`s.
1451    #[builder]
1452    pub fn select_random_values<RNG>(&self,
1453        rng: &mut RNG,
1454        count: usize,
1455        replace: Option<bool>,
1456        decrement: Option<W>,
1457    ) -> Vec<V>
1458        where RNG: Rng + ?Sized
1459    {
1460        let replace = replace.unwrap_or(true);
1461        let decrement = decrement.unwrap_or(W::one());
1462
1463        if replace {
1464            (0..count)
1465                .filter_map(|_| self.select_random_value(rng).cloned())
1466                .collect()
1467        }
1468        else {
1469            let mut pool = self.clone();
1470
1471            let mut out = Vec::with_capacity(
1472                if count > 16 {
1473                    count.min(self.total_items())
1474                } else {
1475                    count
1476                }
1477            );
1478
1479            for _ in 0..count {
1480                if self.is_zero() { break }
1481
1482                if let Some(item) = pool.take_by_random(rng, decrement) {
1483                    out.push(item.value);
1484                }
1485            }
1486
1487            out
1488        }
1489    }
1490
1491    /// Select `count` unique values using weighted randomisation.
1492    /// 
1493    /// Call this method using `bon` builder syntax (see [§ Usage](Self::select_random_values_unique#usage) below).
1494    /// 
1495    /// # Options
1496    /// 
1497    /// ```text
1498    /// rng:              RNG,
1499    /// count:            usize,
1500    /// merge_duplicates: bool = false,
1501    /// ```
1502    /// 
1503    /// - `count`: How many values to select.
1504    /// - `merge_duplicates`: Whether to treat duplicate values as non-unique (see [§ Notes](Self::select_random_values_unique#notes) below for details).
1505    /// 
1506    /// # Usage
1507    /// 
1508    /// This method uses the bon builder syntax:
1509    /// 
1510    /// ```
1511    /// # use weighted_list::*;
1512    /// let pool = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1513    /// 
1514    /// let selected =
1515    ///     pool.select_random_values_unique()
1516    ///         .rng(&mut rand::rng())
1517    ///         .count(3)
1518    ///         .call();
1519    /// ```
1520    /// 
1521    /// # Notes
1522    /// 
1523    /// By default, this treats each *individual item* as ‘unique’. For instance, the following lists all contain 3 unique values:
1524    /// 
1525    /// ```rust
1526    /// # use weighted_list::*;
1527    /// wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1528    /// wlist![(2, "sup"), (3, "sup"),  (5, "shard")];
1529    /// wlist![(2, "sup"), (3, "sup"),  (5, "sup")  ];
1530    /// ```
1531    /// 
1532    /// This means using `select_random_values_unique()` on them will return at most [`self.total_items()`](Self::total_items) values.
1533    /// 
1534    /// However, if `merge_duplicates` is `true`, then duplicate values (using `Eq` comparison) will be treated as non-unique. In this case, the previous lists have 3, 2, and 1 unique values, respectively.
1535    /// 
1536    /// ```
1537    /// # use weighted_list::*;
1538    /// let mut pool = wlist![
1539    ///     (2, "sup"),
1540    ///     (2, "sup"),
1541    ///     (5, "shard"),
1542    /// ];
1543    /// 
1544    /// let mut rng = rand::rng();
1545    /// 
1546    /// // non-merged
1547    /// let selected = pool
1548    ///     .select_random_values_unique()
1549    ///     .rng(&mut rng)
1550    ///     .count(3)
1551    ///     .call();
1552    /// 
1553    /// /* guaranteed to contain {"sup", "sup", "shard"} in some order */
1554    /// assert!(selected.len() == 3);
1555    /// 
1556    /// // merged
1557    /// let selected = pool
1558    ///     .select_random_values_unique()
1559    ///     .rng(&mut rng)
1560    ///     .count(3)
1561    ///     .merge_duplicates(true)
1562    ///     .call();
1563    /// 
1564    /// /* guaranteed to contain {"sup", "shard"} in some order */
1565    /// assert!(selected.len() == 2);
1566    /// ```
1567    #[builder]
1568    pub fn select_random_values_unique<RNG>(&self,
1569        rng: &mut RNG,
1570        count: usize,
1571        merge_duplicates: Option<bool>,
1572    ) -> Vec<V>
1573        where
1574            RNG: Rng + ?Sized,
1575            V: Clone + Eq,
1576    {
1577        let merge_duplicates = merge_duplicates.unwrap_or(false);
1578
1579        let mut l = self.len();
1580        let mut seen_indices = HashSet::<usize>::new();
1581
1582        let mut out = Vec::with_capacity(
1583            if count > 16 {
1584                count.min(self.total_items())
1585            } else {
1586                count
1587            }
1588        );
1589
1590        for _ in 0..count
1591        {
1592            if l == W::zero() { break }
1593
1594            let Some(weighted_index) = self._get_random_weighted_index_up_to_(rng, l) else { continue };
1595            let Some(idx) = self._unweight_index_skipping_(weighted_index, &seen_indices) else { continue };
1596            let target = &self.data[idx];
1597
1598            if !merge_duplicates {
1599                seen_indices.insert(idx);
1600                l -= target.weight;
1601            }
1602            else {
1603                /* Yeah, the repeated traversal is horrific, but I can’t see any other way... we’ve got to discount *all* duplicates of the value we pick */
1604                let (indices, weights): (Vec<usize>, Vec<W>) =
1605                    self.data.iter().enumerate()
1606                        .filter_map(
1607                            |(i, item)| (item.value == target.value).then_some((i, item.weight))
1608                        )
1609                        .unzip();
1610            
1611                seen_indices.extend(indices);
1612                l -= weights.into_iter().sum::<W>();
1613            }
1614            
1615            out.push(target.value.clone());
1616        }
1617
1618        out
1619    }
1620
1621    /// Take `count` values using weighted randomisation.
1622    #[builder]
1623    pub fn take_random_values<RNG>(&mut self,
1624        rng: &mut RNG,
1625        count: usize,
1626        take_entire: Option<bool>,
1627            decrement: Option<W>,
1628    ) -> Vec<V>
1629        where RNG: Rng + ?Sized
1630    {
1631        let take_entire = take_entire.unwrap_or(true);
1632        let decrement = decrement.unwrap_or(W::one());
1633
1634        let mut n = 0;
1635        let mut out = Vec::with_capacity(count);
1636
1637        loop
1638        {
1639            n += 1;
1640            if n > count || self.data.is_empty() { break }
1641
1642            if let Some(item) = {
1643                if take_entire { self.take_entire_random(rng) }
1644                else           { self.take_by_random(rng, decrement) }
1645            } {
1646                out.push(item.value.clone());
1647            }
1648        }
1649
1650        out
1651    }
1652
1653    /// Take `count` unique values using weighted randomisation.
1654    ///
1655    /// This method is separate to `.take_random_values` due to having more restrictive trait bounds on `V` and using a different algorithm with heavier performance.
1656    #[builder]
1657    pub fn take_random_values_unique<RNG>(&mut self,
1658        rng: &mut RNG,
1659        count: usize,
1660        decrement: Option<W>,
1661    ) -> Vec<V>
1662        where RNG: Rng + ?Sized,
1663    {
1664        let decrement = decrement.unwrap_or(W::one());
1665
1666        let mut l = self.len();
1667        let mut seen_indices = HashSet::<usize>::new();
1668
1669        let mut out = Vec::with_capacity(
1670            /* NOTE: To guard against erroneous overallocations */
1671            if count > 16 {
1672                count.min(self.total_items())
1673            } else {
1674                count
1675            }
1676        );
1677        
1678        for _ in 0..count
1679        {
1680            if l == W::zero() || self.is_zero() { break }
1681
1682            let Some(weighted_index) = self._get_random_weighted_index_up_to_(rng, l) else { continue };
1683            let Some(idx) = self._unweight_index_skipping_(weighted_index, &seen_indices) else { continue };
1684            let target = &self.data[idx];
1685
1686            /* Yeah, the repeated traversal is horrific, but I can’t see any other way... we’ve got to discount *all* duplicates of the value we pick */
1687            let (indices, weights): (Vec<usize>, Vec<W>) =
1688                self.data.iter().enumerate()
1689                    .filter_map(
1690                        |(i, item)| (item.value == target.value).then_some((i, item.weight))
1691                    )
1692                    .unzip();
1693            
1694            seen_indices.extend(indices);
1695            l -= weights.into_iter().sum::<W>();
1696
1697            /* NOTE: Upgrade to mutable reference here to avoid conflict with earlier */
1698            let target = &mut self.data[idx];
1699
1700            if decrement >= target.weight {
1701                target.weight = W::zero();
1702            } else {
1703                target.weight -= decrement;
1704            }
1705            
1706            out.push(target.value.clone());
1707        }
1708
1709        self.prune();
1710
1711        out
1712    }
1713}
1714
1715/// Methods for shuffling data.
1716impl<V, W: Weight> WeightedList<V,W>
1717    where
1718        V: Clone,
1719{
1720    /// Shuffle the order of items in the list (in-place).
1721    pub fn shuffle_items<RNG>(&mut self, rng: &mut RNG) -> &mut Self
1722        where RNG: Rng + ?Sized
1723    {
1724        self.data.shuffle(rng);
1725        self
1726    }
1727
1728    /// Return a clone with the order of items shuffled.
1729    /// 
1730    /// Out-of-place version of [`.shuffle_items()`](Self::shuffle_items).
1731    pub fn shuffled_items<RNG>(&self, rng: &mut RNG) -> Self
1732        where RNG: Rng + ?Sized
1733    {
1734        let mut out = self.clone();
1735        out.shuffle_items(rng);
1736
1737        out
1738    }
1739
1740    /// Shuffle the pairings of (weight, value) for items in the list (in-place).
1741    /// 
1742    /// Values remain in the same order, while weights are re-assigned.
1743    /// 
1744    /// # Usage
1745    /// 
1746    /// ```
1747    /// # use weighted_list::*;
1748    /// let mut wl = wlist![(2, "sup"), (3, "nova"), (5, "shard")];
1749    /// 
1750    /// wl.shuffle_weights(&mut rand::rng());
1751    /// 
1752    /// println!("{wl}");
1753    /// // could give:
1754    /// //   WeightedList[{3, sup}, {5, nova}, {2, shard}]
1755    /// 
1756    /// println!("{wl}");
1757    /// // could now give:
1758    /// //   WeightedList[{2, sup}, {5, nova}, {3, shard}]
1759    /// ```
1760    pub fn shuffle_weights<RNG>(&mut self, rng: &mut RNG) -> &mut Self
1761        where RNG: Rng + ?Sized
1762    {
1763        let mut weights: Vec<W> = self.weights().collect();
1764        weights.shuffle(rng);
1765        
1766        for item in &mut self.data {
1767            /* guaranteed to be Some */
1768            item.weight = weights.pop().unwrap();
1769        }
1770
1771        self
1772    }
1773
1774    /// Return a clone of the list with (weight, value) pairings shuffled.
1775    /// 
1776    /// Out-of-place version of [`.shuffle_weights()`](Self::shuffle_weights).
1777    pub fn shuffled_weights<RNG>(&self, rng: &mut RNG) -> Self
1778        where RNG: Rng + ?Sized
1779    {
1780        let mut out = self.clone();
1781        out.shuffle_weights(rng);
1782
1783        out
1784    }
1785}
1786
1787// == INTERNAL == //
1788impl<V, W: Weight> WeightedList<V,W>
1789{
1790    /// Convert a `weighted_index` to its unweighted equivalent in the underlying `Vec<>`. Does not panic on overflow and instead returns `Vec::len()`.
1791    fn _unweight_index_nopanic_(&self, weighted_index: W) -> usize
1792    {
1793        let mut t = W::zero();
1794        let mut i = 0;
1795
1796        for item in &self.data {
1797            t += item.weight;
1798
1799            if t > weighted_index {
1800                return i;
1801            }
1802
1803            i += 1;
1804        }
1805
1806        i
1807    }
1808
1809    /// Convert a `weighted_index` to its unweighted equivalent in the underlying `Vec<>`. Panics on overflow.
1810    fn _unweight_index_(&self, weighted_index: W) -> usize
1811    {
1812        let mut t = W::zero();
1813
1814        for (i, item) in self.data.iter().enumerate() {
1815            t += item.weight;
1816
1817            if t > weighted_index {
1818                return i;
1819            }
1820        }
1821
1822        panic!(
1823            "index out of bounds: the len is {:?} but the index is {:?}",
1824            self.len(), weighted_index
1825        );
1826    }
1827
1828    /// Variant of `._unweighted_index_()` for random selection enforcing unique outputs.
1829    fn _unweight_index_skipping_(&self,
1830        weighted_index: W,
1831        seen: &HashSet<usize>,
1832    ) -> Option<usize>
1833    {
1834        let mut t = W::zero();
1835
1836        for (i, item) in self.data.iter().enumerate()
1837        {
1838            if seen.contains(&i) { continue }
1839
1840            t += item.weight;
1841
1842            if t > weighted_index {
1843                return Some(i);
1844            }
1845        }
1846
1847        None
1848    }
1849}
1850
1851
1852#[cfg(test)] mod tests
1853{
1854    use super::*;
1855
1856    fn el() -> WeightedList<String, i32>
1857    {
1858        wlist![]
1859    }
1860
1861    fn wl() -> WeightedList<String, i32>
1862    {
1863        wlist![
1864            (2, "sup".to_string()),
1865            (3, "nova".to_string()),
1866            (5, "shard".to_string()),
1867        ]
1868    }
1869
1870    #[test] fn _unweight_index_()
1871    {
1872        let list = wl();
1873        assert_eq!( list._unweight_index_(0), 0 );
1874        assert_eq!( list._unweight_index_(1), 0 );
1875        assert_eq!( list._unweight_index_(2), 1 );
1876        assert_eq!( list._unweight_index_(3), 1 );
1877        assert_eq!( list._unweight_index_(4), 1 );
1878        assert_eq!( list._unweight_index_(5), 2 );
1879        assert_eq!( list._unweight_index_(6), 2 );
1880        assert_eq!( list._unweight_index_(7), 2 );
1881        assert_eq!( list._unweight_index_(8), 2 );
1882        assert_eq!( list._unweight_index_(9), 2 );
1883    }
1884
1885    #[test] #[should_panic] fn _unweight_index_empty_()
1886    {
1887        el()._unweight_index_(0);
1888    }
1889
1890    #[test] #[should_panic] fn _unweight_index_out_of_bounds_()
1891    {
1892        wl()._unweight_index_(10);
1893    }
1894
1895    #[test] fn _unweight_index_nopanic_()
1896    {
1897        let list = wl();
1898        assert_eq!( list._unweight_index_nopanic_(10), 3 );
1899        assert_eq!( list._unweight_index_nopanic_(11), 3 );
1900        assert_eq!( list._unweight_index_nopanic_(12), 3 );
1901    }
1902
1903    #[test] fn _unweight_index_skipping_()
1904    {
1905        let list = wl();
1906        
1907        let seen = HashSet::from([0]);
1908        assert_eq!( list._unweight_index_skipping_(0, &seen), Some(1) );
1909        assert_eq!( list._unweight_index_skipping_(1, &seen), Some(1) );
1910        assert_eq!( list._unweight_index_skipping_(2, &seen), Some(1) );
1911        assert_eq!( list._unweight_index_skipping_(3, &seen), Some(2) );
1912        assert_eq!( list._unweight_index_skipping_(4, &seen), Some(2) );
1913        assert_eq!( list._unweight_index_skipping_(5, &seen), Some(2) );
1914        assert_eq!( list._unweight_index_skipping_(6, &seen), Some(2) );
1915        assert_eq!( list._unweight_index_skipping_(7, &seen), Some(2) );
1916
1917        let seen = HashSet::from([1]);
1918        assert_eq!( list._unweight_index_skipping_(0, &seen), Some(0) );
1919        assert_eq!( list._unweight_index_skipping_(1, &seen), Some(0) );
1920        assert_eq!( list._unweight_index_skipping_(2, &seen), Some(2) );
1921        assert_eq!( list._unweight_index_skipping_(3, &seen), Some(2) );
1922        assert_eq!( list._unweight_index_skipping_(4, &seen), Some(2) );
1923        assert_eq!( list._unweight_index_skipping_(5, &seen), Some(2) );
1924        assert_eq!( list._unweight_index_skipping_(6, &seen), Some(2) );
1925    }
1926}