use std::iter::{Product, Sum};
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! _comprehension_iter {
($r:tt; $e:expr) => { Some(std::iter::once($e)) };
($r:tt; $i:tt movein $range:expr, $($rest:tt)+ ) => {
Some($range
.into_iter()
.zip(std::iter::repeat($r))
.filter_map(move |($i, $r)| _comprehension_iter!(($i, $r); $($rest)*)).flatten())
};
($r:tt; $i:tt in $range:expr, $($rest:tt)+ ) => {
Some($range
.into_iter()
.zip(std::iter::repeat($r))
.filter_map(|($i, $r)| _comprehension_iter!(($i, $r); $($rest)*)).flatten())
};
($r:tt; $cond:expr, $($rest:tt)*) => {
if $cond {
_comprehension_iter!($r; $($rest)*)
} else {
None
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! _comprehension {
(($($vars:tt),*); ($($clauses:tt)*); $i:tt in $range:expr) => {
_comprehension!(($($vars,)* $i); ($($clauses)* ($i in $range));)
};
(($($vars:tt),*); ($($clauses:tt)*); move $i:tt in $range:expr) => {
_comprehension!(($($vars,)* $i); ($($clauses)* ($i movein $range));)
};
(($($vars:tt),*); ($($clauses:tt)*); $i:tt in $range:expr, $($rest:tt)*) => {
_comprehension!(($($vars,)* $i); ($($clauses)* ($i in $range)); $($rest)*)
};
(($($vars:tt),*); ($($clauses:tt)*); move $i:tt in $range:expr, $($rest:tt)*) => {
_comprehension!(($($vars,)* $i); ($($clauses)* ($i movein $range)); $($rest)*)
};
(($($vars:tt),*); ($($clauses:tt)*); $cond:expr) => {
_comprehension!(($($vars),*); ($($clauses)* ($cond));)
};
(($($vars:tt),*); ($($clauses:tt)*); $cond:expr, $($rest:tt)*) => {
_comprehension!(($($vars),*); ($($clauses)* ($cond)); $($rest)*)
};
(($($vars:tt),*); ($(($($clauses:tt)+))*);) => {
_comprehension_iter!((); $($($clauses)+,)* ($($vars),*)).unwrap()
};
}
#[macro_export(local_inner_macros)]
macro_rules! comprehension {
($($rest:tt)*) => {
_comprehension!((); (); $($rest)*)
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! _map {
(($($vars:tt),*); ($($clauses:tt)*); $i:tt in $range:expr, $($rest:tt)*) => {
_map!(($($vars,)* $i); ($($clauses)* ($i in $range)); $($rest)*)
};
(($($vars:tt),*); ($($clauses:tt)*); $cond:expr, $($rest:tt)*) => {
_map!(($($vars),*); ($($clauses)* ($cond)); $($rest)*)
};
(($var:tt); ($(($($clauses:tt)+))*); $e:expr) => {
_comprehension_iter!((); $($($clauses)+,)* $var).unwrap().map(|$var| $e)
};
(($($vars:tt),*); ($(($($clauses:tt)+))*); $e:expr) => {
_comprehension_iter!((); $($($clauses)+,)* ($($vars),*)).unwrap().map(|($($vars),*)| $e)
};
}
#[macro_export(local_inner_macros)]
macro_rules! map {
($e:expr; $($rest:tt)*) => {
map!($($rest)*, $e)
};
($($rest:tt)*) => {
_map!((); (); $($rest)*)
}
}
#[macro_export(local_inner_macros)]
macro_rules! vec {
($e:expr; $i:tt in $range:expr) => {
map!($e; $i in $range).collect::<Vec<_>>()
};
($e:expr; $i:tt in $range:expr, $($rest:tt)*) => {
map!($e; $i in $range, $($rest)*).collect::<Vec<_>>()
};
($($rest:tt)*) => {
std::vec!($($rest)*)
};
}
#[macro_export(local_inner_macros)]
macro_rules! sum {
($t:ty : $e:expr; $($rest:tt)*) => { sum!($t : $($rest)*, $e) };
($t:ty : $($rest:tt)*) => { map!($($rest)*).sum::<$t>() };
($e:expr; $($rest:tt)*) => { sum!($($rest)*, $e) };
($($rest:tt)*) => { $crate::sumiter(map!($($rest)*)) };
}
#[macro_export(local_inner_macros)]
macro_rules! product {
($t:ty : $e:expr; $($rest:tt)*) => { product!($t : $($rest)*, $e) };
($t:ty : $($rest:tt)*) => { map!($($rest)*).product::<$t>() };
($e:expr; $($rest:tt)*) => { product!($($rest)*, $e) };
($($rest:tt)*) => { $crate::productiter(map!($($rest)*)) };
}
#[doc(hidden)]
pub fn sumiter<T, I>(it: I) -> T
where
T: Sum<T>,
I: Iterator<Item = T>,
{
it.sum()
}
#[doc(hidden)]
pub fn productiter<T, I>(it: I) -> T
where
T: Product<T>,
I: Iterator<Item = T>,
{
it.product()
}
#[cfg(test)]
mod tests {
#[test]
fn test_comprehension() {
let mut it = comprehension!(k in 0..3);
assert_eq!(it.next(), Some(0));
assert_eq!(it.next(), Some(1));
assert_eq!(it.next(), Some(2));
assert_eq!(it.next(), None);
let mut it = comprehension!(i in 0..3, j in 0..3);
assert_eq!(it.next(), Some((0, 0)));
assert_eq!(it.next(), Some((0, 1)));
assert_eq!(it.next(), Some((0, 2)));
assert_eq!(it.next(), Some((1, 0)));
assert_eq!(it.next(), Some((1, 1)));
assert_eq!(it.next(), Some((1, 2)));
assert_eq!(it.next(), Some((2, 0)));
assert_eq!(it.next(), Some((2, 1)));
assert_eq!(it.next(), Some((2, 2)));
assert_eq!(it.next(), None);
let mut it = comprehension!(i in 0..3, i % 2 == 0, j in 0..3);
assert_eq!(it.next(), Some((0, 0)));
assert_eq!(it.next(), Some((0, 1)));
assert_eq!(it.next(), Some((0, 2)));
assert_eq!(it.next(), Some((2, 0)));
assert_eq!(it.next(), Some((2, 1)));
assert_eq!(it.next(), Some((2, 2)));
assert_eq!(it.next(), None);
let mut it = comprehension!(i in 0..3, i % 2 == 0, j in 0..3, i + j < 4);
assert_eq!(it.next(), Some((0, 0)));
assert_eq!(it.next(), Some((0, 1)));
assert_eq!(it.next(), Some((0, 2)));
assert_eq!(it.next(), Some((2, 0)));
assert_eq!(it.next(), Some((2, 1)));
assert_eq!(it.next(), None);
assert_eq!(
comprehension!(i in 0..3, j in 0..3, k in 0..2)
.map(|(i, j, k)| i + j + k)
.collect::<Vec<_>>(),
vec![0, 1, 1, 2, 2, 3, 1, 2, 2, 3, 3, 4, 2, 3, 3, 4, 4, 5]
);
}
#[test]
fn test_map() {
let mut it = map!(k in 0..3, 2 * k);
assert_eq!(it.next(), Some(0));
assert_eq!(it.next(), Some(2));
assert_eq!(it.next(), Some(4));
assert_eq!(it.next(), None);
assert_eq!(map!(i; i in 0..5).collect::<Vec<_>>(), vec![0, 1, 2, 3, 4]);
assert_eq!(map!(i; i in 0..5, i % 2 == 0).collect::<Vec<_>>(), vec![0, 2, 4]);
assert_eq!(
map!(i*2 + j; i in 0..5, j in 0..2).collect::<Vec<_>>(),
(0..10).collect::<Vec<_>>()
);
assert_eq!(
map!((i,j); i in 0..3, j in 0..3, i < j).collect::<Vec<_>>(),
vec![(0, 1), (0, 2), (1, 2)]
);
assert_eq!(
map!((i,j); i in 0..4, i % 2 == 0, j in 0..4, i < j).collect::<Vec<_>>(),
vec![(0, 1), (0, 2), (0, 3), (2, 3)]
);
}
#[test]
fn vector() {
assert_eq!(vec!(i; i in 0..5), vec![0, 1, 2, 3, 4]);
}
#[test]
fn sum() {
assert_eq!(sum!(i; i in 0..5), 10);
assert_eq!(sum!(i in 0..5, i), 10);
}
#[test]
fn product() {
assert_eq!(product!(i; i in 1..5), 24);
assert_eq!(product!(i in 1..5, i), 24);
}
#[test]
fn sumvec() {
let x = vec![1; 10];
assert_eq!(sum!(x[i]; i in 0..10), 10);
assert_eq!(sum!(i in 0..10, x[i]), 10);
}
#[test]
fn vec() {
assert_eq!(vec![i; i in 0..5], vec![0, 1, 2, 3, 4]);
}
#[test]
fn test_mut() {
let mut x = vec![0, 1, 2];
assert_eq!(
18 * 19 / 2 - 6,
sum!({x.push(42 + i + j); x.len()}; i in 0..5, j in 0..3)
);
assert_eq!(
x,
vec![0, 1, 2, 42, 43, 44, 43, 44, 45, 44, 45, 46, 45, 46, 47, 46, 47, 48]
)
}
#[test]
fn test_tuple() {
let pairs = [(1,2), (3,4), (5,6)];
let mut it = map!(format!("{}+{}", i, j); (i, j) in pairs);
assert_eq!(it.next(), Some("1+2".to_string()));
assert_eq!(it.next(), Some("3+4".to_string()));
assert_eq!(it.next(), Some("5+6".to_string()));
assert_eq!(it.next(), None);
}
#[test]
fn test_tuple2() {
let pairs = [(1,2), (3,4), (5,6)];
let mut it = map!((i, j) in pairs, format!("{}+{}", i, j));
assert_eq!(it.next(), Some("1+2".to_string()));
assert_eq!(it.next(), Some("3+4".to_string()));
assert_eq!(it.next(), Some("5+6".to_string()));
assert_eq!(it.next(), None);
}
#[test]
fn test_sum() {
let pairs = [(1,2), (3,4), (5,6)];
assert_eq!(44, sum!(i * j; (i,j) in pairs));
assert_eq!(44, sum!((i,j) in pairs, i * j));
}
}