/rust/registry/src/index.crates.io-1949cf8c6b5b557f/itertools-0.12.1/src/combinations.rs
Line | Count | Source |
1 | | use std::fmt; |
2 | | use std::iter::FusedIterator; |
3 | | |
4 | | use super::lazy_buffer::LazyBuffer; |
5 | | use alloc::vec::Vec; |
6 | | |
7 | | use crate::adaptors::checked_binomial; |
8 | | |
9 | | /// An iterator to iterate through all the `k`-length combinations in an iterator. |
10 | | /// |
11 | | /// See [`.combinations()`](crate::Itertools::combinations) for more information. |
12 | | #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] |
13 | | pub struct Combinations<I: Iterator> { |
14 | | indices: Vec<usize>, |
15 | | pool: LazyBuffer<I>, |
16 | | first: bool, |
17 | | } |
18 | | |
19 | | impl<I> Clone for Combinations<I> |
20 | | where |
21 | | I: Clone + Iterator, |
22 | | I::Item: Clone, |
23 | | { |
24 | | clone_fields!(indices, pool, first); |
25 | | } |
26 | | |
27 | | impl<I> fmt::Debug for Combinations<I> |
28 | | where |
29 | | I: Iterator + fmt::Debug, |
30 | | I::Item: fmt::Debug, |
31 | | { |
32 | | debug_fmt_fields!(Combinations, indices, pool, first); |
33 | | } |
34 | | |
35 | | /// Create a new `Combinations` from a clonable iterator. |
36 | 0 | pub fn combinations<I>(iter: I, k: usize) -> Combinations<I> |
37 | 0 | where |
38 | 0 | I: Iterator, |
39 | | { |
40 | 0 | Combinations { |
41 | 0 | indices: (0..k).collect(), |
42 | 0 | pool: LazyBuffer::new(iter), |
43 | 0 | first: true, |
44 | 0 | } |
45 | 0 | } |
46 | | |
47 | | impl<I: Iterator> Combinations<I> { |
48 | | /// Returns the length of a combination produced by this iterator. |
49 | | #[inline] |
50 | 0 | pub fn k(&self) -> usize { |
51 | 0 | self.indices.len() |
52 | 0 | } |
53 | | |
54 | | /// Returns the (current) length of the pool from which combination elements are |
55 | | /// selected. This value can change between invocations of [`next`](Combinations::next). |
56 | | #[inline] |
57 | 0 | pub fn n(&self) -> usize { |
58 | 0 | self.pool.len() |
59 | 0 | } |
60 | | |
61 | | /// Returns a reference to the source pool. |
62 | | #[inline] |
63 | 0 | pub(crate) fn src(&self) -> &LazyBuffer<I> { |
64 | 0 | &self.pool |
65 | 0 | } |
66 | | |
67 | | /// Resets this `Combinations` back to an initial state for combinations of length |
68 | | /// `k` over the same pool data source. If `k` is larger than the current length |
69 | | /// of the data pool an attempt is made to prefill the pool so that it holds `k` |
70 | | /// elements. |
71 | 0 | pub(crate) fn reset(&mut self, k: usize) { |
72 | 0 | self.first = true; |
73 | | |
74 | 0 | if k < self.indices.len() { |
75 | 0 | self.indices.truncate(k); |
76 | 0 | for i in 0..k { |
77 | 0 | self.indices[i] = i; |
78 | 0 | } |
79 | | } else { |
80 | 0 | for i in 0..self.indices.len() { |
81 | 0 | self.indices[i] = i; |
82 | 0 | } |
83 | 0 | self.indices.extend(self.indices.len()..k); |
84 | 0 | self.pool.prefill(k); |
85 | | } |
86 | 0 | } |
87 | | |
88 | 0 | pub(crate) fn n_and_count(self) -> (usize, usize) { |
89 | | let Self { |
90 | 0 | indices, |
91 | 0 | pool, |
92 | 0 | first, |
93 | 0 | } = self; |
94 | 0 | let n = pool.count(); |
95 | 0 | (n, remaining_for(n, first, &indices).unwrap()) |
96 | 0 | } |
97 | | } |
98 | | |
99 | | impl<I> Iterator for Combinations<I> |
100 | | where |
101 | | I: Iterator, |
102 | | I::Item: Clone, |
103 | | { |
104 | | type Item = Vec<I::Item>; |
105 | 0 | fn next(&mut self) -> Option<Self::Item> { |
106 | 0 | if self.first { |
107 | 0 | self.pool.prefill(self.k()); |
108 | 0 | if self.k() > self.n() { |
109 | 0 | return None; |
110 | 0 | } |
111 | 0 | self.first = false; |
112 | 0 | } else if self.indices.is_empty() { |
113 | 0 | return None; |
114 | | } else { |
115 | | // Scan from the end, looking for an index to increment |
116 | 0 | let mut i: usize = self.indices.len() - 1; |
117 | | |
118 | | // Check if we need to consume more from the iterator |
119 | 0 | if self.indices[i] == self.pool.len() - 1 { |
120 | 0 | self.pool.get_next(); // may change pool size |
121 | 0 | } |
122 | | |
123 | 0 | while self.indices[i] == i + self.pool.len() - self.indices.len() { |
124 | 0 | if i > 0 { |
125 | 0 | i -= 1; |
126 | 0 | } else { |
127 | | // Reached the last combination |
128 | 0 | return None; |
129 | | } |
130 | | } |
131 | | |
132 | | // Increment index, and reset the ones to its right |
133 | 0 | self.indices[i] += 1; |
134 | 0 | for j in i + 1..self.indices.len() { |
135 | 0 | self.indices[j] = self.indices[j - 1] + 1; |
136 | 0 | } |
137 | | } |
138 | | |
139 | | // Create result vector based on the indices |
140 | 0 | Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect()) |
141 | 0 | } |
142 | | |
143 | 0 | fn size_hint(&self) -> (usize, Option<usize>) { |
144 | 0 | let (mut low, mut upp) = self.pool.size_hint(); |
145 | 0 | low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX); |
146 | 0 | upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices)); |
147 | 0 | (low, upp) |
148 | 0 | } |
149 | | |
150 | | #[inline] |
151 | 0 | fn count(self) -> usize { |
152 | 0 | self.n_and_count().1 |
153 | 0 | } |
154 | | } |
155 | | |
156 | | impl<I> FusedIterator for Combinations<I> |
157 | | where |
158 | | I: Iterator, |
159 | | I::Item: Clone, |
160 | | { |
161 | | } |
162 | | |
163 | | /// For a given size `n`, return the count of remaining combinations or None if it would overflow. |
164 | 0 | fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> { |
165 | 0 | let k = indices.len(); |
166 | 0 | if n < k { |
167 | 0 | Some(0) |
168 | 0 | } else if first { |
169 | 0 | checked_binomial(n, k) |
170 | | } else { |
171 | | // https://en.wikipedia.org/wiki/Combinatorial_number_system |
172 | | // http://www.site.uottawa.ca/~lucia/courses/5165-09/GenCombObj.pdf |
173 | | |
174 | | // The combinations generated after the current one can be counted by counting as follows: |
175 | | // - The subsequent combinations that differ in indices[0]: |
176 | | // If subsequent combinations differ in indices[0], then their value for indices[0] |
177 | | // must be at least 1 greater than the current indices[0]. |
178 | | // As indices is strictly monotonically sorted, this means we can effectively choose k values |
179 | | // from (n - 1 - indices[0]), leading to binomial(n - 1 - indices[0], k) possibilities. |
180 | | // - The subsequent combinations with same indices[0], but differing indices[1]: |
181 | | // Here we can choose k - 1 values from (n - 1 - indices[1]) values, |
182 | | // leading to binomial(n - 1 - indices[1], k - 1) possibilities. |
183 | | // - (...) |
184 | | // - The subsequent combinations with same indices[0..=i], but differing indices[i]: |
185 | | // Here we can choose k - i values from (n - 1 - indices[i]) values: binomial(n - 1 - indices[i], k - i). |
186 | | // Since subsequent combinations can in any index, we must sum up the aforementioned binomial coefficients. |
187 | | |
188 | | // Below, `n0` resembles indices[i]. |
189 | 0 | indices.iter().enumerate().try_fold(0usize, |sum, (i, n0)| { |
190 | 0 | sum.checked_add(checked_binomial(n - 1 - *n0, k - i)?) |
191 | 0 | }) |
192 | | } |
193 | 0 | } |