pub trait WindowedExt<'a, T: 'a> {
type IterBy: Iterator<Item = &'a [T]> + DoubleEndedIterator + ExactSizeIterator;
fn sliding_windows_by(&'a self, size: usize, step: usize) -> Self::IterBy;
}
impl<'a, T: 'a> WindowedExt<'a, T> for [T] {
type IterBy = SlidingBy<'a, T>;
#[inline]
fn sliding_windows_by(&'a self, size: usize, step: usize) -> Self::IterBy {
SlidingBy::new(self, size, step)
}
}
pub struct SlidingBy<'a, T> {
slice: &'a [T],
size: usize,
step: usize,
k_front: usize,
k_back: usize,
count: usize,
}
impl<'a, T> SlidingBy<'a, T> {
pub fn new(slice: &'a [T], size: usize, step: usize) -> Self {
let len = slice.len();
let step = step.max(1);
let count = if size == 0 || len < size {
0
} else {
1 + (len - size) / step
};
Self {
slice,
size,
step,
k_front: 0,
k_back: count,
count,
}
}
#[inline]
fn start_of(&self, k: usize) -> usize {
k * self.step
}
}
impl<'a, T> Iterator for SlidingBy<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<Self::Item> {
if self.k_front >= self.k_back {
return None;
}
let start = self.start_of(self.k_front);
let end = start + self.size;
self.k_front += 1;
self.slice.get(start..end)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let rem = self.k_back.saturating_sub(self.k_front);
(rem, Some(rem))
}
}
impl<'a, T> DoubleEndedIterator for SlidingBy<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.k_front >= self.k_back {
return None;
}
self.k_back -= 1;
let start = self.start_of(self.k_back);
let end = start + self.size;
self.slice.get(start..end)
}
}
impl<'a, T> ExactSizeIterator for SlidingBy<'a, T> {
#[inline]
fn len(&self) -> usize {
self.k_back - self.k_front
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sliding_by_step_2() {
let v = vec![10, 20, 30, 40, 50, 60, 70];
let mut windows = v.sliding_windows_by(3, 2);
assert_eq!(windows.next(), Some([10, 20, 30].as_slice()));
assert_eq!(windows.next(), Some([30, 40, 50].as_slice()));
assert_eq!(windows.next(), Some([50, 60, 70].as_slice()));
assert_eq!(windows.next(), None);
}
}