itermore/adaptors/cartesian_product.rs
1use core::iter::FusedIterator;
2
3/// An extension trait that provides the [`cartesian_product`] method for
4/// iterators.
5///
6/// [`cartesian_product`]: IterCartesianProduct::cartesian_product
7pub trait IterCartesianProduct: Iterator {
8 /// Returns an iterator adaptor that iterates over the cartesian product of
9 /// the element sets of two iterators `self` and `other.into_iter()`.
10 ///
11 /// # Examples
12 ///
13 /// ```
14 /// use itermore::IterCartesianProduct;
15 ///
16 /// let v = Vec::from_iter((0..3).cartesian_product("αβ".chars()));
17 /// assert_eq!(v, [(0, 'α'), (0, 'β'), (1, 'α'), (1, 'β'), (2, 'α'), (2, 'β')]);
18 /// ```
19 fn cartesian_product<J>(self, other: J) -> CartesianProduct<Self, J::IntoIter>
20 where
21 Self: Sized,
22 Self::Item: Clone,
23 J: IntoIterator,
24 J::IntoIter: Clone,
25 {
26 CartesianProduct::new(self, other.into_iter())
27 }
28}
29
30impl<I: ?Sized> IterCartesianProduct for I where I: Iterator {}
31
32/// An iterator over the cartesian product of the element sets of two iterators
33/// `I` and `J`.
34///
35/// This struct is created by the [`cartesian_product`] method on iterators. See
36/// its documentation for more.
37///
38/// [`cartesian_product`]: IterCartesianProduct::cartesian_product
39#[derive(Debug, Clone)]
40#[must_use = "iterators are lazy and do nothing unless consumed"]
41pub struct CartesianProduct<I, J>
42where
43 I: Iterator,
44{
45 a: I,
46 b: J,
47 a_item: Option<I::Item>,
48 b_curr: J,
49}
50
51impl<I, J> CartesianProduct<I, J>
52where
53 I: Iterator,
54 J: Iterator + Clone,
55{
56 fn new(mut a: I, b: J) -> Self {
57 CartesianProduct {
58 a_item: a.next(),
59 a,
60 b_curr: b.clone(),
61 b,
62 }
63 }
64}
65
66impl<I, J> Iterator for CartesianProduct<I, J>
67where
68 I: Iterator,
69 J: Iterator + Clone,
70 I::Item: Clone,
71{
72 type Item = (I::Item, J::Item);
73
74 fn next(&mut self) -> Option<Self::Item> {
75 let b_item = match self.b_curr.next() {
76 Some(b_item) => b_item,
77 None => {
78 self.b_curr = self.b.clone();
79 let b_item = self.b_curr.next()?;
80 self.a_item = self.a.next();
81 b_item
82 }
83 };
84 self.a_item.as_ref().map(|a| (a.clone(), b_item))
85 }
86}
87
88impl<I, J> FusedIterator for CartesianProduct<I, J>
89where
90 I: FusedIterator,
91 J: FusedIterator + Clone,
92 I::Item: Clone,
93{
94}
95
96////////////////////////////////////////////////////////////////////////////////
97// Macro
98////////////////////////////////////////////////////////////////////////////////
99
100/// Returns an iterator over the cartesian product of the element sets of
101/// multiple iterators (up to 12).
102///
103/// This is essentially the equivalent of calling [`cartesian_product`] multiple
104/// times and "flattening" each item e.g. `((A, B), C)` to `(A, B, C)`.
105///
106/// # Examples
107///
108/// ```
109/// use itermore::{cartesian_product, IterCartesianProduct};
110///
111/// // With macro
112/// let i = cartesian_product!(0..3, "αβ".chars(), [-1, 0, 1]);
113///
114/// // Without macro
115/// let j = (0..3)
116/// .cartesian_product("αβ".chars())
117/// .cartesian_product([-1, 0, 1])
118/// .map(|((a, b), c)| (a, b, c));
119///
120/// assert_eq!(Vec::from_iter(i), Vec::from_iter(j));
121/// ```
122///
123/// Iterate over the 3D coordinates of a 10 x 10 x 10 cube.
124///
125/// ```
126/// use itermore::cartesian_product;
127///
128/// // With macro
129/// for (i, j, k) in cartesian_product!(0..10, 0..10, 0..10) {
130/// // ...
131/// }
132///
133/// // Without macro
134/// for i in 0..10 {
135/// for j in 0..10 {
136/// for k in 0..10 {
137/// // ...
138/// }
139/// }
140/// }
141/// ```
142///
143/// [`cartesian_product`]: IterCartesianProduct::cartesian_product
144#[macro_export]
145macro_rules! cartesian_product {
146 ($I:expr $(,)?) => {
147 $crate::core::iter::IntoIterator::into_iter($I)
148 };
149
150 ($I:expr, $J:expr $(,)?) => {
151 $crate::IterCartesianProduct::cartesian_product(
152 $crate::cartesian_product!($I),
153 $crate::cartesian_product!($J),
154 )
155 };
156
157 ($I:expr, $J:expr, $($K:expr),+ $(,)?) => {{
158 $crate::cartesian_product!($crate::cartesian_product!($I, $J), $($K),+)
159 .map($crate::flatten_tuple)
160 }};
161}