use core::iter::FusedIterator;
pub trait IterCartesianProduct: Iterator {
fn cartesian_product<J>(self, other: J) -> CartesianProduct<Self, J::IntoIter>
where
Self: Sized,
Self::Item: Clone,
J: IntoIterator,
J::IntoIter: Clone,
{
CartesianProduct::new(self, other.into_iter())
}
}
impl<I: ?Sized> IterCartesianProduct for I where I: Iterator {}
#[derive(Debug, Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct CartesianProduct<I, J>
where
I: Iterator,
{
a: I,
b: J,
a_item: Option<I::Item>,
b_curr: J,
}
impl<I, J> CartesianProduct<I, J>
where
I: Iterator,
J: Iterator + Clone,
{
fn new(mut a: I, b: J) -> Self {
CartesianProduct {
a_item: a.next(),
a,
b_curr: b.clone(),
b,
}
}
}
impl<I, J> Iterator for CartesianProduct<I, J>
where
I: Iterator,
J: Iterator + Clone,
I::Item: Clone,
{
type Item = (I::Item, J::Item);
fn next(&mut self) -> Option<Self::Item> {
let b_item = match self.b_curr.next() {
Some(b_item) => b_item,
None => {
self.b_curr = self.b.clone();
let b_item = self.b_curr.next()?;
self.a_item = self.a.next();
b_item
}
};
self.a_item.as_ref().map(|a| (a.clone(), b_item))
}
}
impl<I, J> FusedIterator for CartesianProduct<I, J>
where
I: FusedIterator,
J: FusedIterator + Clone,
I::Item: Clone,
{
}
#[macro_export]
macro_rules! cartesian_product {
($I:expr $(,)?) => {
$crate::core::iter::IntoIterator::into_iter($I)
};
($I:expr, $J:expr $(,)?) => {
$crate::IterCartesianProduct::cartesian_product(
$crate::cartesian_product!($I),
$crate::cartesian_product!($J),
)
};
($I:expr, $J:expr, $($K:expr),+ $(,)?) => {{
$crate::cartesian_product!($crate::cartesian_product!($I, $J), $($K),+)
.map($crate::flatten_tuple)
}};
}