AdventOfCode/Rust/lib/iter_ext.rs
2024-12-03 10:27:57 +01:00

232 lines
5.4 KiB
Rust

use std::{hash::Hash, iter::Iterator};
use rustc_hash::FxHashMap;
pub trait IterExt: Iterator {
fn take_while_inclusive<P>(self, predicate: P) -> TakeWhileInclusive<Self, P>
where
Self: Sized,
P: FnMut(&Self::Item) -> bool;
fn chain_if_empty<U>(self, other: U) -> ChainIfEmpty<Self, U::IntoIter>
where
Self: Sized,
U: IntoIterator<Item = Self::Item>;
fn transpose(self) -> Transpose<<Self::Item as IntoIterator>::IntoIter>
where
Self: Sized,
Self::Item: IntoIterator;
fn all_consume<P>(self, predicate: P) -> bool
where
Self: Sized,
P: FnMut(Self::Item) -> bool;
fn any_consume<P>(self, predicate: P) -> bool
where
Self: Sized,
P: FnMut(Self::Item) -> bool;
fn index(mut self, elem: impl PartialEq<Self::Item>) -> Option<usize>
where
Self: Sized,
{
self.position(|x| elem == x)
}
fn counts_fx(self) -> FxHashMap<Self::Item, usize>
where
Self: Sized,
Self::Item: Hash + Eq,
{
self.fold(FxHashMap::default(), |mut counts, x| {
counts.entry(x).and_modify(|x| *x += 1).or_insert(1);
counts
})
}
}
impl<I> IterExt for I
where
I: Iterator,
{
fn take_while_inclusive<P>(self, predicate: P) -> TakeWhileInclusive<I, P>
where
P: FnMut(&I::Item) -> bool,
{
TakeWhileInclusive::new(self, predicate)
}
fn chain_if_empty<U>(self, other: U) -> ChainIfEmpty<I, U::IntoIter>
where
U: IntoIterator<Item = Self::Item>,
{
ChainIfEmpty {
iter: self,
other: other.into_iter(),
state: State::Unknown,
}
}
fn transpose(self) -> Transpose<<Self::Item as std::iter::IntoIterator>::IntoIter>
where
Self::Item: IntoIterator,
{
Transpose {
iterators: self.map(|it| it.into_iter()).collect(),
}
}
fn all_consume<P>(self, mut predicate: P) -> bool
where
Self: Sized,
P: FnMut(Self::Item) -> bool,
{
#[allow(clippy::unnecessary_fold)]
self.fold(true, |acc, x| acc && predicate(x))
}
fn any_consume<P>(self, mut predicate: P) -> bool
where
Self: Sized,
P: FnMut(Self::Item) -> bool,
{
#[allow(clippy::unnecessary_fold)]
self.fold(false, |acc, x| acc || predicate(x))
}
}
pub struct TakeWhileInclusive<I, P> {
iter: I,
predicate: P,
done: bool,
}
impl<I, P> TakeWhileInclusive<I, P> {
fn new(iter: I, predicate: P) -> Self {
TakeWhileInclusive {
iter,
predicate,
done: false,
}
}
}
impl<I, P> Iterator for TakeWhileInclusive<I, P>
where
I: Iterator,
P: FnMut(&I::Item) -> bool,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
self.iter.next().inspect(|item| {
if !(self.predicate)(item) {
self.done = true;
}
})
}
}
enum State {
Empty,
NotEmpty,
Unknown,
}
pub struct ChainIfEmpty<I, U> {
iter: I,
other: U,
state: State,
}
impl<I, U> Iterator for ChainIfEmpty<I, U>
where
I: Iterator,
U: Iterator<Item = I::Item>,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
match self.state {
State::Empty => self.other.next(),
State::NotEmpty => self.iter.next(),
State::Unknown => match self.iter.next() {
Some(x) => {
self.state = State::NotEmpty;
Some(x)
}
None => {
self.state = State::Empty;
self.other.next()
}
},
}
}
}
pub struct Transpose<I: Iterator> {
iterators: Vec<I>,
}
impl<I: Iterator> Iterator for Transpose<I> {
type Item = Vec<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
self.iterators
.iter_mut()
.map(|it| it.next())
.collect::<Option<Vec<_>>>()
}
}
#[cfg(test)]
mod tests_take_while_inclusive {
use super::*;
macro_rules! test {
($name:ident, $inp:expr, $pred:expr, $exp: expr) => {
#[test]
fn $name() {
let pred = $pred;
assert_eq!(
$inp.iter()
.take_while_inclusive(|&&x| (pred)(x))
.copied()
.collect::<Vec<_>>(),
$exp
)
}
};
}
test!(split, [1, 2, 3, 4], |x| x < 3, vec![1, 2, 3]);
test!(always_true, [1, 2, 3, 4], |_| true, vec![1, 2, 3, 4]);
test!(always_false, [1, 2, 3, 4], |_| false, vec![1]);
}
#[cfg(test)]
mod tests_chain_if_empty {
use super::*;
macro_rules! test {
($name:ident, $inp1:expr, $inp2:expr, $exp: expr) => {
#[test]
fn $name() {
assert_eq!(
$inp1.into_iter().chain_if_empty($inp2).collect::<Vec<_>>(),
$exp
);
}
};
}
test!(both_empty, Vec::<i32>::new(), [], vec![]);
test!(first_empty, [], [4, 5, 6], vec![4, 5, 6]);
test!(second_empty, [1, 2, 3], [], vec![1, 2, 3]);
test!(both_nonempty, [1, 2, 3], [4, 5, 6], vec![1, 2, 3]);
}