blob: 81b13f13099f5adbe7ea573830fc106de62288a6 [file] [log] [blame]
Joel Galenson6f798712021-04-01 17:03:06 -07001use alloc::vec::Vec;
Jakub Kotura425e552020-12-21 17:28:15 +01002use std::fmt;
Joel Galensonb593e252021-06-21 13:15:57 -07003use std::iter::FusedIterator;
Jakub Kotura425e552020-12-21 17:28:15 +01004
5use super::lazy_buffer::LazyBuffer;
6
7/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
8///
Joel Galensonb593e252021-06-21 13:15:57 -07009/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
10/// for more information.
Jakub Kotura425e552020-12-21 17:28:15 +010011#[derive(Clone)]
12pub struct CombinationsWithReplacement<I>
13where
14 I: Iterator,
15 I::Item: Clone,
16{
Jakub Kotura425e552020-12-21 17:28:15 +010017 indices: Vec<usize>,
Jakub Kotura425e552020-12-21 17:28:15 +010018 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{
Joel Galenson6f798712021-04-01 17:03:06 -070027 debug_fmt_fields!(Combinations, indices, pool, first);
Jakub Kotura425e552020-12-21 17:28:15 +010028}
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{
Joel Galenson6f798712021-04-01 17:03:06 -070047 let indices: Vec<usize> = alloc::vec![0; k];
Jakub Kotura425e552020-12-21 17:28:15 +010048 let pool: LazyBuffer<I> = LazyBuffer::new(iter);
49
50 CombinationsWithReplacement {
Jakub Kotura425e552020-12-21 17:28:15 +010051 indices,
Jakub Kotura425e552020-12-21 17:28:15 +010052 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
Joel Galenson6f798712021-04-01 17:03:06 -070067 return if self.indices.len() != 0 && !self.pool.get_next() {
Jakub Kotura425e552020-12-21 17:28:15 +010068 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
Joel Galenson6f798712021-04-01 17:03:06 -070078 self.pool.get_next();
Jakub Kotura425e552020-12-21 17:28:15 +010079
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() {
Joel Galenson6f798712021-04-01 17:03:06 -070083 if *indices_int < self.pool.len()-1 {
Jakub Kotura425e552020-12-21 17:28:15 +010084 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}
Joel Galensonb593e252021-06-21 13:15:57 -0700104
105impl<I> FusedIterator for CombinationsWithReplacement<I>
106where
107 I: Iterator,
108 I::Item: Clone,
109{}