itertools/adaptors/
multi_product.rs

1#![cfg(feature = "use_std")]
2
3use size_hint;
4use Itertools;
5
6#[derive(Clone)]
7/// An iterator adaptor that iterates over the cartesian product of
8/// multiple iterators of type `I`.
9///
10/// An iterator element type is `Vec<I>`.
11///
12/// See [`.multi_cartesian_product()`](../trait.Itertools.html#method.multi_cartesian_product)
13/// for more information.
14#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
15pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
16    where I: Iterator + Clone,
17          I::Item: Clone;
18
19/// Create a new cartesian product iterator over an arbitrary number
20/// of iterators of the same type.
21///
22/// Iterator element is of type `Vec<H::Item::Item>`.
23pub fn multi_cartesian_product<H>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
24    where H: Iterator,
25          H::Item: IntoIterator,
26          <H::Item as IntoIterator>::IntoIter: Clone,
27          <H::Item as IntoIterator>::Item: Clone
28{
29    MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect())
30}
31
32#[derive(Clone, Debug)]
33/// Holds the state of a single iterator within a MultiProduct.
34struct MultiProductIter<I>
35    where I: Iterator + Clone,
36          I::Item: Clone
37{
38    cur: Option<I::Item>,
39    iter: I,
40    iter_orig: I,
41}
42
43/// Holds the current state during an iteration of a MultiProduct.
44#[derive(Debug)]
45enum MultiProductIterState {
46    StartOfIter,
47    MidIter { on_first_iter: bool },
48}
49
50impl<I> MultiProduct<I>
51    where I: Iterator + Clone,
52          I::Item: Clone
53{
54    /// Iterates the rightmost iterator, then recursively iterates iterators
55    /// to the left if necessary.
56    ///
57    /// Returns true if the iteration succeeded, else false.
58    fn iterate_last(
59        multi_iters: &mut [MultiProductIter<I>],
60        mut state: MultiProductIterState
61    ) -> bool {
62        use self::MultiProductIterState::*;
63
64        if let Some((last, rest)) = multi_iters.split_last_mut() {
65            let on_first_iter = match state {
66                StartOfIter => {
67                    let on_first_iter = !last.in_progress();
68                    state = MidIter { on_first_iter: on_first_iter };
69                    on_first_iter
70                },
71                MidIter { on_first_iter } => on_first_iter
72            };
73
74            if !on_first_iter {
75                last.iterate();
76            }
77
78            if last.in_progress() {
79                true
80            } else if MultiProduct::iterate_last(rest, state) {
81                last.reset();
82                last.iterate();
83                // If iterator is None twice consecutively, then iterator is
84                // empty; whole product is empty.
85                last.in_progress()
86            } else {
87                false
88            }
89        } else {
90            // Reached end of iterator list. On initialisation, return true.
91            // At end of iteration (final iterator finishes), finish.
92            match state {
93                StartOfIter => false,
94                MidIter { on_first_iter } => on_first_iter
95            }
96        }
97    }
98
99    /// Returns the unwrapped value of the next iteration.
100    fn curr_iterator(&self) -> Vec<I::Item> {
101        self.0.iter().map(|multi_iter| {
102            multi_iter.cur.clone().unwrap()
103        }).collect()
104    }
105
106    /// Returns true if iteration has started and has not yet finished; false
107    /// otherwise.
108    fn in_progress(&self) -> bool {
109        if let Some(last) = self.0.last() {
110            last.in_progress()
111        } else {
112            false
113        }
114    }
115}
116
117impl<I> MultiProductIter<I>
118    where I: Iterator + Clone,
119          I::Item: Clone
120{
121    fn new(iter: I) -> Self {
122        MultiProductIter {
123            cur: None,
124            iter: iter.clone(),
125            iter_orig: iter
126        }
127    }
128
129    /// Iterate the managed iterator.
130    fn iterate(&mut self) {
131        self.cur = self.iter.next();
132    }
133
134    /// Reset the managed iterator.
135    fn reset(&mut self) {
136        self.iter = self.iter_orig.clone();
137    }
138
139    /// Returns true if the current iterator has been started and has not yet
140    /// finished; false otherwise.
141    fn in_progress(&self) -> bool {
142        self.cur.is_some()
143    }
144}
145
146impl<I> Iterator for MultiProduct<I>
147    where I: Iterator + Clone,
148          I::Item: Clone
149{
150    type Item = Vec<I::Item>;
151
152    fn next(&mut self) -> Option<Self::Item> {
153        if MultiProduct::iterate_last(
154            &mut self.0,
155            MultiProductIterState::StartOfIter
156        ) {
157            Some(self.curr_iterator())
158        } else {
159            None
160        }
161    }
162
163    fn count(self) -> usize {
164        if self.0.len() == 0 {
165            return 0;
166        }
167
168        if !self.in_progress() {
169            return self.0.into_iter().fold(1, |acc, multi_iter| {
170                acc * multi_iter.iter.count()
171            });
172        }
173
174        self.0.into_iter().fold(
175            0,
176            |acc, MultiProductIter { iter, iter_orig, cur: _ }| {
177                let total_count = iter_orig.count();
178                let cur_count = iter.count();
179                acc * total_count + cur_count
180            }
181        )
182    }
183
184    fn size_hint(&self) -> (usize, Option<usize>) {
185        // Not ExactSizeIterator because size may be larger than usize
186        if self.0.len() == 0 {
187            return (0, Some(0));
188        }
189
190        if !self.in_progress() {
191            return self.0.iter().fold((1, Some(1)), |acc, multi_iter| {
192                size_hint::mul(acc, multi_iter.iter.size_hint())
193            });
194        }
195
196        self.0.iter().fold(
197            (0, Some(0)),
198            |acc, &MultiProductIter { ref iter, ref iter_orig, cur: _ }| {
199                let cur_size = iter.size_hint();
200                let total_size = iter_orig.size_hint();
201                size_hint::add(size_hint::mul(acc, total_size), cur_size)
202            }
203        )
204    }
205
206    fn last(self) -> Option<Self::Item> {
207        let iter_count = self.0.len();
208
209        let lasts: Self::Item = self.0.into_iter()
210            .map(|multi_iter| multi_iter.iter.last())
211            .while_some()
212            .collect();
213
214        if lasts.len() == iter_count {
215            Some(lasts)
216        } else {
217            None
218        }
219    }
220}