use core::{marker::Destruct, ops::Try};
use array_trait::length;
use crate::{Bulk, DoubleEndedBulk, util::ArrayBuffer};
#[must_use = "bulks are lazy and do nothing unless consumed"]
pub struct MapWindows<I, F, const N: usize>
where
I: Bulk,
F: for<'a> FnMut<(&'a [I::Item; N],)>
{
bulk: I,
f: F
}
impl<I: Bulk, F, U, const N: usize> MapWindows<I, F, N>
where
I: Bulk,
F: FnMut(&[I::Item; N]) -> U
{
pub(crate) const fn new(bulk: I, f: F) -> Self
{
assert!(N != 0, "array in `Bulk::map_windows` must contain more than 0 elements");
if core::mem::size_of::<I::Item>() != 0
{
assert!(N.checked_mul(2).is_some(), "array size of `Iterator::map_windows` is too large");
}
Self { bulk, f }
}
}
const impl<I: Bulk, F, U, const N: usize> IntoIterator for MapWindows<I, F, N>
where
I: Bulk + [const] IntoIterator<IntoIter: [const] Iterator>,
F: FnMut(&[I::Item; N]) -> U
{
type IntoIter = core::iter::MapWindows<I::IntoIter, F, N>;
type Item = U;
fn into_iter(self) -> Self::IntoIter
{
let Self { bulk, f } = self;
bulk.into_iter().map_windows(f)
}
}
const impl<I: Bulk, F, U, const N: usize> Bulk for MapWindows<I, F, N>
where
I: [const] Bulk<Item: [const] Destruct>,
F: [const] FnMut(&[I::Item; N]) -> U + [const] Destruct
{
type MaxLength = length::Windowed<I::MaxLength, [(); N]>;
type MinLength = length::Windowed<I::MinLength, [(); N]>;
fn len(&self) -> usize
{
let Self { bulk, f: _ } = self;
bulk.len().saturating_sub(N - 1)
}
fn is_empty(&self) -> bool
{
let Self { bulk, f: _ } = self;
bulk.len() > N - 1
}
fn for_each<FF>(self, f: FF)
where
Self: Sized,
FF: [const] FnMut(Self::Item) + [const] Destruct
{
let Self { bulk, f: map } = self;
bulk.for_each(Closure::<_, _, _, _, _, false> {
map,
f,
buffer: ArrayBuffer::new()
});
}
fn try_for_each<FF, R>(self, f: FF) -> R
where
Self: Sized,
FF: [const] FnMut(Self::Item) -> R + [const] Destruct,
R: [const] core::ops::Try<Output = ()>
{
let Self { bulk, f: map } = self;
bulk.try_for_each(TryClosure::<_, _, _, _, _, _, false> {
map,
f,
buffer: ArrayBuffer::new()
})
}
}
const impl<I: Bulk, F, U, const N: usize> DoubleEndedBulk for MapWindows<I, F, N>
where
I: [const] DoubleEndedBulk<Item: [const] Destruct>,
F: [const] FnMut(&[I::Item; N]) -> U + [const] Destruct,
Self::IntoIter: DoubleEndedIterator
{
fn rev_for_each<FF>(self, f: FF)
where
Self: Sized,
FF: [const] FnMut(Self::Item) + [const] Destruct
{
let Self { bulk, f: map } = self;
bulk.rev_for_each(Closure::<_, _, _, _, _, true> {
map,
f,
buffer: ArrayBuffer::new()
});
}
fn try_rev_for_each<FF, R>(self, f: FF) -> R
where
Self: Sized,
FF: [const] FnMut(Self::Item) -> R + [const] Destruct,
R: [const] core::ops::Try<Output = ()>
{
let Self { bulk, f: map } = self;
bulk.try_rev_for_each(TryClosure::<_, _, _, _, _, _, true> {
map,
f,
buffer: ArrayBuffer::new()
})
}
}
struct Closure<F, FF, T, U, const N: usize, const REV: bool>
where
F: FnMut(&[T; N]) -> U,
FF: FnMut(U)
{
map: F,
f: FF,
buffer: ArrayBuffer<T, N, REV>
}
const impl<F, FF, T, U, const N: usize, const REV: bool> FnOnce<(T,)> for Closure<F, FF, T, U, N, REV>
where
T: [const] Destruct,
F: [const] FnMut(&[T; N]) -> U + [const] Destruct,
FF: [const] FnMut(U) + [const] Destruct
{
type Output = ();
extern "rust-call" fn call_once(mut self, args: (T,)) -> Self::Output
{
self.call_mut(args)
}
}
const impl<F, FF, T, U, const N: usize, const REV: bool> FnMut<(T,)> for Closure<F, FF, T, U, N, REV>
where
T: [const] Destruct,
F: [const] FnMut(&[T; N]) -> U,
FF: [const] FnMut(U)
{
extern "rust-call" fn call_mut(&mut self, (x,): (T,)) -> Self::Output
{
let Self { map, f, buffer } = self;
buffer.push_out(x);
if let Some(window) = buffer.as_array()
{
f(map(window))
}
}
}
struct TryClosure<F, FF, T, U, R, const N: usize, const REV: bool>
where
F: FnMut(&[T; N]) -> U,
FF: FnMut(U) -> R
{
map: F,
f: FF,
buffer: ArrayBuffer<T, N, REV>
}
const impl<F, FF, T, U, R, const N: usize, const REV: bool> FnOnce<(T,)> for TryClosure<F, FF, T, U, R, N, REV>
where
T: [const] Destruct,
F: [const] FnMut(&[T; N]) -> U + [const] Destruct,
FF: [const] FnMut(U) -> R + [const] Destruct,
R: [const] Try<Output = ()>
{
type Output = R;
extern "rust-call" fn call_once(mut self, args: (T,)) -> Self::Output
{
self.call_mut(args)
}
}
const impl<F, FF, T, U, R, const N: usize, const REV: bool> FnMut<(T,)> for TryClosure<F, FF, T, U, R, N, REV>
where
T: [const] Destruct,
F: [const] FnMut(&[T; N]) -> U,
FF: [const] FnMut(U) -> R,
R: [const] Try<Output = ()>
{
extern "rust-call" fn call_mut(&mut self, (x,): (T,)) -> Self::Output
{
let Self { map, f, buffer } = self;
buffer.push_out(x);
if let Some(window) = buffer.as_array()
{
f(map(window))?
}
R::from_output(())
}
}
#[cfg(test)]
mod test
{
use crate::*;
#[test]
fn it_works()
{
let mut i = 0;
let a = crate::repeat_n_with(
|| {
i += 1;
i
},
[(); 20]
)
.map_windows(|&[n, m]| n + m)
.collect::<[_; _], _>();
println!("{a:?}")
}
}