1use alloc::vec::Vec;
2use std::fmt;
3use std::iter::FusedIterator;
4
5use super::lazy_buffer::LazyBuffer;
6
7/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
8///
9/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
10/// for more information.
11#[derive(Clone)]
12pub struct CombinationsWithReplacement<I>
13where
14 I: Iterator,
15 I::Item: Clone,
16{
17 indices: Vec<usize>,
18 pool: LazyBuffer<I>,
19 first: bool,
20}
21
22impl<I> fmt::Debug for CombinationsWithReplacement<I>
23where
24 I: Iterator + fmt::Debug,
25 I::Item: fmt::Debug + Clone,
26{
27 debug_fmt_fields!(Combinations, indices, pool, first);
28}
29
30impl<I> CombinationsWithReplacement<I>
31where
32 I: Iterator,
33 I::Item: Clone,
34{
35 /// Map the current mask over the pool to get an output combination
36 fn current(&self) -> Vec<I::Item> {
37 self.indices.iter().map(|i| self.pool[*i].clone()).collect()
38 }
39}
40
41/// Create a new `CombinationsWithReplacement` from a clonable iterator.
42pub fn combinations_with_replacement<I>(iter: I, k: usize) -> CombinationsWithReplacement<I>
43where
44 I: Iterator,
45 I::Item: Clone,
46{
47 let indices: Vec<usize> = alloc::vec![0; k];
48 let pool: LazyBuffer<I> = LazyBuffer::new(iter);
49
50 CombinationsWithReplacement {
51 indices,
52 pool,
53 first: true,
54 }
55}
56
57impl<I> Iterator for CombinationsWithReplacement<I>
58where
59 I: Iterator,
60 I::Item: Clone,
61{
62 type Item = Vec<I::Item>;
63 fn next(&mut self) -> Option<Self::Item> {
64 // If this is the first iteration, return early
65 if self.first {
66 // In empty edge cases, stop iterating immediately
67 return if !(self.indices.is_empty() || self.pool.get_next()) {
68 None
69 // Otherwise, yield the initial state
70 } else {
71 self.first = false;
72 Some(self.current())
73 };
74 }
75
76 // Check if we need to consume more from the iterator
77 // This will run while we increment our first index digit
78 self.pool.get_next();
79
80 // Work out where we need to update our indices
81 let mut increment: Option<(usize, usize)> = None;
82 for (i, indices_int) in self.indices.iter().enumerate().rev() {
83 if *indices_int < self.pool.len()-1 {
84 increment = Some((i, indices_int + 1));
85 break;
86 }
87 }
88
89 match increment {
90 // If we can update the indices further
91 Some((increment_from, increment_value)) => {
92 // We need to update the rightmost non-max value
93 // and all those to the right
94 for indices_index in increment_from..self.indices.len() {
95 self.indices[indices_index] = increment_value;
96 }
97 Some(self.current())
98 }
99 // Otherwise, we're done
100 None => None,
101 }
102 }
103}
104
105impl<I> FusedIterator for CombinationsWithReplacement<I>
106where
107 I: Iterator,
108 I::Item: Clone,
109{}
110