itertools/combinations_with_replacement.rs
1use alloc::boxed::Box;
2use std::fmt;
3use std::iter::FusedIterator;
4
5use super::lazy_buffer::LazyBuffer;
6use crate::adaptors::checked_binomial;
7use crate::combinations::PoolIndex;
8/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
9///
10/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
11/// for more information.
12#[derive(Clone)]
13#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
14pub struct CombinationsWithReplacementGeneric<I, Idx>
15where
16 I: Iterator,
17 I::Item: Clone,
18{
19 indices: Idx,
20 pool: LazyBuffer<I>,
21 first: bool,
22}
23
24/// Iterator for `Box<[I]>` valued combinations_with_replacement returned by [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
25pub type CombinationsWithReplacement<I> = CombinationsWithReplacementGeneric<I, Box<[usize]>>;
26/// Iterator for const generic combinations_with_replacement returned by [`.array_combinations_with_replacement()`](crate::Itertools::array_combinations_with_replacement)
27pub type ArrayCombinationsWithReplacement<I, const K: usize> =
28 CombinationsWithReplacementGeneric<I, [usize; K]>;
29
30impl<I, Idx> fmt::Debug for CombinationsWithReplacementGeneric<I, Idx>
31where
32 I: Iterator + fmt::Debug,
33 I::Item: fmt::Debug + Clone,
34 Idx: fmt::Debug,
35{
36 debug_fmt_fields!(CombinationsWithReplacementGeneric, indices, pool, first);
37}
38
39/// Create a new `ArrayCombinationsWithReplacement`` from a cloneable iterator.
40pub fn array_combinations_with_replacement<I: Iterator, const K: usize>(
41 iter: I,
42) -> ArrayCombinationsWithReplacement<I, K>
43where
44 I::Item: Clone,
45{
46 ArrayCombinationsWithReplacement::new(iter, [0; K])
47}
48/// Create a new `CombinationsWithReplacement` from a cloneable iterator.
49pub fn combinations_with_replacement<I>(iter: I, k: usize) -> CombinationsWithReplacement<I>
50where
51 I: Iterator,
52 I::Item: Clone,
53{
54 let indices = alloc::vec![0; k].into_boxed_slice();
55
56 CombinationsWithReplacementGeneric::new(iter, indices)
57}
58
59impl<I: Iterator, Idx: PoolIndex<I::Item>> CombinationsWithReplacementGeneric<I, Idx>
60where
61 I: Iterator,
62 I::Item: Clone,
63{
64 /// Increments indices representing the combination to advance to the next
65 /// (in lexicographic order by increasing sequence) combination.
66 ///
67 /// Returns true if we've run out of combinations, false otherwise.
68 fn increment_indices(&mut self) -> bool {
69 // Check if we need to consume more from the iterator
70 // This will run while we increment our first index digit
71 self.pool.get_next();
72
73 // Work out where we need to update our indices
74 let mut increment = None;
75 let indices: &mut [usize] = self.indices.borrow_mut();
76 for (i, indices_int) in indices.iter().enumerate().rev() {
77 if *indices_int < self.pool.len() - 1 {
78 increment = Some((i, indices_int + 1));
79 break;
80 }
81 }
82 match increment {
83 // If we can update the indices further
84 Some((increment_from, increment_value)) => {
85 // We need to update the rightmost non-max value
86 // and all those to the right
87 indices[increment_from..].fill(increment_value);
88 false
89 }
90 // Otherwise, we're done
91 None => true,
92 }
93 }
94 /// Constructor with arguments the inner iterator and the initial state for the indices.
95 fn new(iter: I, indices: Idx) -> Self {
96 Self {
97 indices,
98 pool: LazyBuffer::new(iter),
99 first: true,
100 }
101 }
102}
103
104impl<I, Idx> Iterator for CombinationsWithReplacementGeneric<I, Idx>
105where
106 I: Iterator,
107 I::Item: Clone,
108 Idx: PoolIndex<I::Item>,
109{
110 type Item = Idx::Item;
111
112 fn next(&mut self) -> Option<Self::Item> {
113 if self.first {
114 // In empty edge cases, stop iterating immediately
115 if !(self.indices.borrow().is_empty() || self.pool.get_next()) {
116 return None;
117 }
118 self.first = false;
119 } else if self.increment_indices() {
120 return None;
121 }
122 Some(self.indices.extract_item(&self.pool))
123 }
124
125 fn nth(&mut self, n: usize) -> Option<Self::Item> {
126 if self.first {
127 // In empty edge cases, stop iterating immediately
128 if !(self.indices.borrow().is_empty() || self.pool.get_next()) {
129 return None;
130 }
131 self.first = false;
132 } else if self.increment_indices() {
133 return None;
134 }
135 for _ in 0..n {
136 if self.increment_indices() {
137 return None;
138 }
139 }
140 Some(self.indices.extract_item(&self.pool))
141 }
142
143 fn size_hint(&self) -> (usize, Option<usize>) {
144 let (mut low, mut upp) = self.pool.size_hint();
145 low = remaining_for(low, self.first, self.indices.borrow()).unwrap_or(usize::MAX);
146 upp = upp.and_then(|upp| remaining_for(upp, self.first, self.indices.borrow()));
147 (low, upp)
148 }
149
150 fn count(self) -> usize {
151 let Self {
152 indices,
153 pool,
154 first,
155 } = self;
156 let n = pool.count();
157 remaining_for(n, first, indices.borrow()).unwrap()
158 }
159}
160
161impl<I, Idx> FusedIterator for CombinationsWithReplacementGeneric<I, Idx>
162where
163 I: Iterator,
164 I::Item: Clone,
165 Idx: PoolIndex<I::Item>,
166{
167}
168
169/// For a given size `n`, return the count of remaining combinations with replacement or None if it would overflow.
170fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
171 // With a "stars and bars" representation, choose k values with replacement from n values is
172 // like choosing k out of k + n − 1 positions (hence binomial(k + n - 1, k) possibilities)
173 // to place k stars and therefore n - 1 bars.
174 // Example (n=4, k=6): ***|*||** represents [0,0,0,1,3,3].
175 let count = |n: usize, k: usize| {
176 let positions = if n == 0 {
177 k.saturating_sub(1)
178 } else {
179 (n - 1).checked_add(k)?
180 };
181 checked_binomial(positions, k)
182 };
183 let k = indices.len();
184 if first {
185 count(n, k)
186 } else {
187 // The algorithm is similar to the one for combinations *without replacement*,
188 // except we choose values *with replacement* and indices are *non-strictly* monotonically sorted.
189
190 // The combinations generated after the current one can be counted by counting as follows:
191 // - The subsequent combinations that differ in indices[0]:
192 // If subsequent combinations differ in indices[0], then their value for indices[0]
193 // must be at least 1 greater than the current indices[0].
194 // As indices is monotonically sorted, this means we can effectively choose k values with
195 // replacement from (n - 1 - indices[0]), leading to count(n - 1 - indices[0], k) possibilities.
196 // - The subsequent combinations with same indices[0], but differing indices[1]:
197 // Here we can choose k - 1 values with replacement from (n - 1 - indices[1]) values,
198 // leading to count(n - 1 - indices[1], k - 1) possibilities.
199 // - (...)
200 // - The subsequent combinations with same indices[0..=i], but differing indices[i]:
201 // Here we can choose k - i values with replacement from (n - 1 - indices[i]) values: count(n - 1 - indices[i], k - i).
202 // Since subsequent combinations can in any index, we must sum up the aforementioned binomial coefficients.
203
204 // Below, `n0` resembles indices[i].
205 indices.iter().enumerate().try_fold(0usize, |sum, (i, n0)| {
206 sum.checked_add(count(n - 1 - *n0, k - i)?)
207 })
208 }
209}