use crate::Itertools; use std::iter::Fuse; /// An iterator over all contiguous windows of the input iterator, /// producing arrays of a specific size. /// /// See [`.array_windows()`](crate::Itertools::array_windows) for more /// information. #[derive(Debug, Clone)] pub struct ArrayWindows where I: Iterator + Sized, I::Item: Clone, { iter: Fuse, inner: Option>, } #[derive(Debug, Clone)] struct ArrayWindowsInner { // `window` stores the `N` items delivered in the most // recent output window. It is stored in the form of a ring // buffer, with `window_start` identifying the element // that logically comes first. window: [T; N], window_start: usize, } impl ArrayWindowsInner { /// Replace the least recent item in `window` with a new /// item. fn add_to_buffer(&mut self, item: T) { if N > 0 { self.window[self.window_start] = item; self.window_start = (self.window_start + 1) % N; } } /// Construct an array window to return. fn make_window(&self) -> [T; N] { std::array::from_fn(|i| self.window[(i + self.window_start) % N].clone()) } } impl Iterator for ArrayWindows where I: Iterator + Sized, I::Item: Clone, { type Item = [I::Item; N]; fn next(&mut self) -> Option<[I::Item; N]> { match &mut self.inner { // Initialisation code, when next() is called for the first time None => match self.iter.next_array() { None => { // The input iterator was completely empty None } Some(buf) => { let inner = ArrayWindowsInner { window: buf, window_start: 0, }; let window = inner.make_window(); self.inner = Some(inner); Some(window) } }, Some(inner) => match self.iter.next() { Some(item) => { inner.add_to_buffer(item); Some(inner.make_window()) } None => None, }, } } } pub fn array_windows(iter: I) -> ArrayWindows where I: Iterator + Sized, I::Item: Clone, { ArrayWindows { iter: iter.fuse(), inner: None, } } /// An iterator over all windows, wrapping back to the first elements when the /// window would otherwise exceed the length of the iterator, producing arrays /// of a specific size. /// /// See [`.circular_array_windows()`](crate::Itertools::circular_array_windows) /// for more information. #[derive(Debug, Clone)] pub struct CircularArrayWindows where I: Iterator + Sized, I::Item: Clone, { iter: Fuse, inner: Option>, } #[derive(Debug, Clone)] struct CircularArrayWindowsInner { // `prefix` stores the first `N` items output from this iterator. // If the input contained fewer than `N` items, then it is filled // with clones of the previous items in a cycle. // // `prefix_pos` tracks the number of items that have been _used_ // from `prefix`. It begins counting up from 0 once the input runs // out. (So in the case where the input iterator is shorter than // `N`, it will begin counting up before `prefix` has even been // populated during setup.) prefix: [T; N], prefix_pos: usize, // For delivering the output arrays, we reuse `ArrayWindowsInner` // unchanged. arraywin: ArrayWindowsInner, } impl Iterator for CircularArrayWindows where I: Iterator + Sized, I::Item: Clone, { type Item = [I::Item; N]; fn next(&mut self) -> Option<[I::Item; N]> { match &mut self.inner { // Initialisation code, when next() is called for the first time None => match self.iter.next() { None => { // The input iterator was completely empty None } Some(first) => { // We have at least one item, so we can definitely // populate `prefix` (even if we have to make N // copies of this element). // Construct [Option; N] and convert to [T; N] // once it's full. TODO: can this be improved? let mut items = std::array::from_fn(|_| None); let mut prefix_pos = 0; if N > 0 { // The first item stored is the one passed to // us from our caller. items[0] = Some(first); } for i in 1..N { // Populate the remaining slots in `items` // from the input iterator. items[i] = self.iter.next(); if items[i].is_none() { // If the input iterator runs out early, // populate the rest of `items` by // recycling from the beginning, and set // `prefix_pos` to indicate that we have // already consumed those items. for j in i..N { items[j] = items[j - i].clone(); } prefix_pos = N - i; break; } } let items = items.map(Option::unwrap); let inner = CircularArrayWindowsInner { prefix: items.clone(), prefix_pos, arraywin: ArrayWindowsInner { window: items, window_start: 0, }, }; let window = inner.arraywin.make_window(); self.inner = Some(inner); Some(window) } }, Some(inner) => { // Normal case. Read the next item in the logical // input sequence (consisting of the contents of the // input iterator followed by N-1 items recycling from // the beginning), and add it to the ring buffer. let item = if let Some(item) = self.iter.next() { // Read from the input iterator. item } else { assert!(N == 0 || inner.prefix_pos < N); if N == 0 || inner.prefix_pos + 1 == N { // The input iterator has run out, and we've // emitted as many windows as we read items, // so we've finished. return None; } let item = inner.prefix[inner.prefix_pos].clone(); inner.prefix_pos += 1; item }; inner.arraywin.add_to_buffer(item); Some(inner.arraywin.make_window()) } } } } pub fn circular_array_windows(iter: I) -> CircularArrayWindows where I: Iterator + Sized, I::Item: Clone, { CircularArrayWindows { iter: iter.fuse(), inner: None, } }