pub(crate) trait RevisitableGroupByForIterator {
type Item;
type Iter: Iterator<Item = Self::Item> + Clone;
fn revisitable_group_by<K, F>(
self,
get_key: F,
) -> RevisitableGroupBy<Self::Item, K, Self::Iter, F>
where
K: PartialEq + Copy,
F: FnMut(&Self::Item) -> K;
}
impl<I: Iterator + Clone> RevisitableGroupByForIterator for I {
type Item = <I as Iterator>::Item;
type Iter = I;
fn revisitable_group_by<K, F>(
self,
get_key: F,
) -> RevisitableGroupBy<Self::Item, K, Self::Iter, F>
where
K: PartialEq + Copy,
F: FnMut(&Self::Item) -> K,
{
RevisitableGroupBy {
iter: self,
get_key,
next_group_initial: None,
}
}
}
pub(crate) struct RevisitableGroupBy<T, K, I, F>
where
K: PartialEq + Copy,
I: Iterator<Item = T> + Clone,
F: FnMut(&T) -> K,
{
iter: I,
get_key: F,
next_group_initial: Option<(T, K)>,
}
impl<T, K, I, F> Iterator for RevisitableGroupBy<T, K, I, F>
where
K: PartialEq + Copy,
I: Iterator<Item = T> + Clone,
F: FnMut(&T) -> K,
{
type Item = RevisitableGroup<T, K, I>;
fn next(&mut self) -> Option<Self::Item> {
let (group_head, group_key) = if let Some((head, key)) = self.next_group_initial.take() {
(head, key)
} else {
if let Some(item) = self.iter.next() {
let key = (self.get_key)(&item);
(item, key)
} else {
return None;
}
};
let mut group_size = 1;
let saved_iter = self.iter.clone();
loop {
if let Some(item) = self.iter.next() {
let key = (self.get_key)(&item);
if key == group_key {
group_size += 1;
} else {
self.next_group_initial = Some((item, key));
break;
}
} else {
debug_assert!(self.next_group_initial.is_none());
break;
}
}
Some(RevisitableGroup {
key: group_key,
len: group_size,
head: Some(group_head),
iter: saved_iter,
remaining: group_size,
})
}
}
pub(crate) struct RevisitableGroup<T, K, I>
where
K: PartialEq + Copy,
I: Iterator<Item = T>,
{
pub key: K,
pub len: usize,
head: Option<T>,
iter: I,
remaining: usize,
}
impl<T, K, I> Iterator for RevisitableGroup<T, K, I>
where
K: PartialEq + Copy,
I: Iterator<Item = T>,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
None
} else {
self.remaining -= 1;
if let Some(item) = self.head.take() {
Some(item)
} else {
let result = self.iter.next();
debug_assert!(result.is_some());
result
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_group_by() {
let nums = [1, 3, 5, 2, 4, 6, 7, 9];
let grouped = nums
.iter()
.revisitable_group_by(|x| *x % 2)
.map(|group| (group.key, group.len, group.copied().collect::<Vec<_>>()))
.collect::<Vec<_>>();
assert_eq!(
grouped,
vec![
(1, 3, vec![1, 3, 5]),
(0, 3, vec![2, 4, 6]),
(1, 2, vec![7, 9]),
]
);
}
#[test]
#[allow(clippy::never_loop)] fn test_empty_outer_slice() {
let slice_of_slices: &[&[i32]] = &[];
for _group in slice_of_slices
.iter()
.copied()
.flatten()
.copied()
.revisitable_group_by(|_| 42)
{
panic!("There is no item!");
}
}
#[test]
#[allow(clippy::never_loop)] fn test_empty_inner_slice() {
let slice_of_slices: &[&[i32]] = &[&[], &[], &[]];
for _group in slice_of_slices
.iter()
.copied()
.flatten()
.copied()
.revisitable_group_by(|_| 42)
{
panic!("There is no item!");
}
}
#[test]
fn test_single_item() {
let slice_of_slices: &[&[i32]] = &[&[1]];
for group in slice_of_slices
.iter()
.copied()
.flatten()
.copied()
.revisitable_group_by(|_| 42)
{
assert_eq!(group.key, 42);
}
}
#[test]
fn test_single_slice_multi_item() {
let slice_of_slices: &[&[i32]] = &[&[1, 3, 5, 2, 4, 6, 7]];
let result = slice_of_slices
.iter()
.copied()
.flatten()
.copied()
.revisitable_group_by(|x| x % 2)
.map(|group| (group.key, group.len, group.collect::<Vec<_>>()))
.collect::<Vec<_>>();
assert_eq!(
result,
vec![
(1, 3, vec![1, 3, 5]),
(0, 3, vec![2, 4, 6]),
(1, 1, vec![7])
]
);
}
#[test]
fn test_multi_slice_multi_item() {
let slice_of_slices: &[&[i32]] = &[&[10, 20], &[11, 21, 31], &[12, 22, 32, 42]];
let result = slice_of_slices
.iter()
.copied()
.flatten()
.copied()
.revisitable_group_by(|x| x % 10)
.map(|group| (group.key, group.len, group.collect::<Vec<_>>()))
.collect::<Vec<_>>();
assert_eq!(
result,
vec![
(0, 2, vec![10, 20]),
(1, 3, vec![11, 21, 31]),
(2, 4, vec![12, 22, 32, 42])
]
);
}
#[test]
fn test_cross_slice_groups() {
let slice_of_slices: &[&[i32]] = &[&[10, 20], &[30, 40, 11, 21], &[31, 12, 22]];
let result = slice_of_slices
.iter()
.copied()
.flatten()
.copied()
.revisitable_group_by(|x| x % 10)
.map(|group| (group.key, group.len, group.collect::<Vec<_>>()))
.collect::<Vec<_>>();
assert_eq!(
result,
vec![
(0, 4, vec![10, 20, 30, 40]),
(1, 3, vec![11, 21, 31]),
(2, 2, vec![12, 22])
]
);
}
#[test]
fn test_cross_slice_groups2() {
let slice_of_slices: &[&[i32]] = &[&[10, 20, 11], &[21, 31, 41], &[51, 61], &[71, 12, 22]];
let result = slice_of_slices
.iter()
.cloned()
.flatten()
.copied()
.revisitable_group_by(|x| x % 10)
.map(|group| (group.key, group.len, group.collect::<Vec<_>>()))
.collect::<Vec<_>>();
assert_eq!(
result,
vec![
(0, 2, vec![10, 20]),
(1, 7, vec![11, 21, 31, 41, 51, 61, 71]),
(2, 2, vec![12, 22])
]
);
}
#[test]
fn test_internal_mutability() {
use std::sync::atomic::{AtomicUsize, Ordering};
let slab0 = vec![
AtomicUsize::new(1),
AtomicUsize::new(3),
AtomicUsize::new(2),
];
let slab1 = vec![
AtomicUsize::new(4),
AtomicUsize::new(6),
AtomicUsize::new(5),
];
let slab2 = vec![
AtomicUsize::new(7),
AtomicUsize::new(9),
AtomicUsize::new(10),
];
let slices: Vec<&[AtomicUsize]> = vec![&slab0[0..3], &slab1[0..3], &slab2[0..2]];
let mut collected = vec![];
for group in slices
.iter()
.copied()
.flatten()
.revisitable_group_by(|x| x.load(Ordering::SeqCst) % 2)
{
let mut group_collected = vec![];
let key = group.key;
for elem in group {
let value = elem.load(Ordering::SeqCst);
group_collected.push(value);
let new_value = value * 100 + key;
elem.store(new_value, Ordering::SeqCst);
}
collected.push(group_collected);
}
assert_eq!(collected, vec![vec![1, 3], vec![2, 4, 6], vec![5, 7, 9]]);
let load_all = |slab: Vec<AtomicUsize>| {
slab.iter()
.map(|x| x.load(Ordering::SeqCst))
.collect::<Vec<_>>()
};
assert_eq!(load_all(slab0), vec![101, 301, 200]);
assert_eq!(load_all(slab1), vec![400, 600, 501]);
assert_eq!(load_all(slab2), vec![701, 901, 10]); }
}