//! Some iterator that produces tuples use std::iter::Cycle; use std::iter::Fuse; use std::iter::FusedIterator; use std::marker::PhantomData; use crate::size_hint; // `HomogeneousTuple` is a public facade for `TupleCollect`, allowing // tuple-related methods to be used by clients in generic contexts, while // hiding the implementation details of `TupleCollect`. // See https://github.com/rust-itertools/itertools/issues/387 /// Implemented for homogeneous tuples of size up to 12. pub trait HomogeneousTuple: TupleCollect {} impl HomogeneousTuple for T {} /// An iterator over a incomplete tuple. /// /// See [`.tuples()`](crate::Itertools::tuples) and /// [`Tuples::into_buffer()`]. #[derive(Clone, Debug)] pub struct TupleBuffer where T: HomogeneousTuple, { cur: usize, buf: T::Buffer, } impl TupleBuffer where T: HomogeneousTuple, { fn new(buf: T::Buffer) -> Self { TupleBuffer { cur: 0, buf } } } impl Iterator for TupleBuffer where T: HomogeneousTuple, { type Item = T::Item; fn next(&mut self) -> Option { let s = self.buf.as_mut(); if let Some(ref mut item) = s.get_mut(self.cur) { self.cur += 1; item.take() } else { None } } fn size_hint(&self) -> (usize, Option) { let buffer = &self.buf.as_ref()[self.cur..]; let len = if buffer.is_empty() { 0 } else { buffer .iter() .position(|x| x.is_none()) .unwrap_or_else(|| buffer.len()) }; (len, Some(len)) } } impl ExactSizeIterator for TupleBuffer where T: HomogeneousTuple {} /// An iterator that groups the items in tuples of a specific size. /// /// See [`.tuples()`](crate::Itertools::tuples) for more information. #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Tuples where I: Iterator, T: HomogeneousTuple, { iter: Fuse, buf: T::Buffer, } /// Create a new tuples iterator. pub fn tuples(iter: I) -> Tuples where I: Iterator, T: HomogeneousTuple, { Tuples { iter: iter.fuse(), buf: Default::default(), } } impl Iterator for Tuples where I: Iterator, T: HomogeneousTuple, { type Item = T; fn next(&mut self) -> Option { T::collect_from_iter(&mut self.iter, &mut self.buf) } fn size_hint(&self) -> (usize, Option) { // The number of elts we've drawn from the underlying iterator, but have // not yet produced as a tuple. let buffered = T::buffer_len(&self.buf); // To that, we must add the size estimates of the underlying iterator. let (unbuffered_lo, unbuffered_hi) = self.iter.size_hint(); // The total low estimate is the sum of the already-buffered elements, // plus the low estimate of remaining unbuffered elements, divided by // the tuple size. let total_lo = add_then_div(unbuffered_lo, buffered, T::num_items()).unwrap_or(usize::MAX); // And likewise for the total high estimate, but using the high estimate // of the remaining unbuffered elements. let total_hi = unbuffered_hi.and_then(|hi| add_then_div(hi, buffered, T::num_items())); (total_lo, total_hi) } } /// `(n + a) / d` avoiding overflow when possible, returns `None` if it overflows. fn add_then_div(n: usize, a: usize, d: usize) -> Option { debug_assert_ne!(d, 0); (n / d).checked_add(a / d)?.checked_add((n % d + a % d) / d) } impl ExactSizeIterator for Tuples where I: ExactSizeIterator, T: HomogeneousTuple, { } impl Tuples where I: Iterator, T: HomogeneousTuple, { /// Return a buffer with the produced items that was not enough to be grouped in a tuple. /// /// ``` /// use itertools::Itertools; /// /// let mut iter = (0..5).tuples(); /// assert_eq!(Some((0, 1, 2)), iter.next()); /// assert_eq!(None, iter.next()); /// itertools::assert_equal(vec![3, 4], iter.into_buffer()); /// ``` pub fn into_buffer(self) -> TupleBuffer { TupleBuffer::new(self.buf) } } /// An iterator over all contiguous windows that produces tuples of a specific size. /// /// See [`.tuple_windows()`](crate::Itertools::tuple_windows) for more /// information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] pub struct TupleWindows where I: Iterator, T: HomogeneousTuple, { iter: I, last: Option, } /// Create a new tuple windows iterator. pub fn tuple_windows(iter: I) -> TupleWindows where I: Iterator, T: HomogeneousTuple, T::Item: Clone, { TupleWindows { last: None, iter } } impl Iterator for TupleWindows where I: Iterator, T: HomogeneousTuple + Clone, T::Item: Clone, { type Item = T; fn next(&mut self) -> Option { if T::num_items() == 1 { return T::collect_from_iter_no_buf(&mut self.iter); } if let Some(new) = self.iter.next() { if let Some(ref mut last) = self.last { last.left_shift_push(new); Some(last.clone()) } else { use std::iter::once; let iter = once(new).chain(&mut self.iter); self.last = T::collect_from_iter_no_buf(iter); self.last.clone() } } else { None } } fn size_hint(&self) -> (usize, Option) { let mut sh = self.iter.size_hint(); // Adjust the size hint at the beginning // OR when `num_items == 1` (but it does not change the size hint). if self.last.is_none() { sh = size_hint::sub_scalar(sh, T::num_items() - 1); } sh } } impl ExactSizeIterator for TupleWindows where I: ExactSizeIterator, T: HomogeneousTuple + Clone, T::Item: Clone, { } impl FusedIterator for TupleWindows where I: FusedIterator, T: HomogeneousTuple + Clone, T::Item: Clone, { } /// An iterator over all windows, wrapping back to the first elements when the /// window would otherwise exceed the length of the iterator, producing tuples /// of a specific size. /// /// See [`.circular_tuple_windows()`](crate::Itertools::circular_tuple_windows) for more /// information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Debug, Clone)] pub struct CircularTupleWindows where I: Iterator + Clone, T: TupleCollect + Clone, { iter: TupleWindows, T>, len: usize, phantom_data: PhantomData, } pub fn circular_tuple_windows(iter: I) -> CircularTupleWindows where I: Iterator + Clone + ExactSizeIterator, T: TupleCollect + Clone, T::Item: Clone, { let len = iter.len(); let iter = tuple_windows(iter.cycle()); CircularTupleWindows { iter, len, phantom_data: PhantomData {}, } } impl Iterator for CircularTupleWindows where I: Iterator + Clone, T: TupleCollect + Clone, T::Item: Clone, { type Item = T; fn next(&mut self) -> Option { if self.len != 0 { self.len -= 1; self.iter.next() } else { None } } fn size_hint(&self) -> (usize, Option) { (self.len, Some(self.len)) } } impl ExactSizeIterator for CircularTupleWindows where I: Iterator + Clone, T: TupleCollect + Clone, T::Item: Clone, { } impl FusedIterator for CircularTupleWindows where I: Iterator + Clone, T: TupleCollect + Clone, T::Item: Clone, { } pub trait TupleCollect: Sized { type Item; type Buffer: Default + AsRef<[Option]> + AsMut<[Option]>; fn buffer_len(buf: &Self::Buffer) -> usize { let s = buf.as_ref(); s.iter().position(Option::is_none).unwrap_or(s.len()) } fn collect_from_iter(iter: I, buf: &mut Self::Buffer) -> Option where I: IntoIterator; fn collect_from_iter_no_buf(iter: I) -> Option where I: IntoIterator; fn num_items() -> usize; fn left_shift_push(&mut self, item: Self::Item); } macro_rules! rev_for_each_ident{ ($m:ident, ) => {}; ($m:ident, $i0:ident, $($i:ident,)*) => { rev_for_each_ident!($m, $($i,)*); $m!($i0); }; } macro_rules! impl_tuple_collect { ($dummy:ident,) => {}; // stop ($dummy:ident, $($Y:ident,)*) => ( impl_tuple_collect!($($Y,)*); impl TupleCollect for ($(ignore_ident!($Y, A),)*) { type Item = A; type Buffer = [Option; count_ident!($($Y)*) - 1]; #[allow(unused_assignments, unused_mut)] fn collect_from_iter(iter: I, buf: &mut Self::Buffer) -> Option where I: IntoIterator { let mut iter = iter.into_iter(); $( let mut $Y = None; )* loop { $( $Y = iter.next(); if $Y.is_none() { break } )* return Some(($($Y.unwrap()),*,)) } let mut i = 0; let mut s = buf.as_mut(); $( if i < s.len() { s[i] = $Y; i += 1; } )* return None; } fn collect_from_iter_no_buf(iter: I) -> Option where I: IntoIterator { let mut iter = iter.into_iter(); Some(($( { let $Y = iter.next()?; $Y }, )*)) } fn num_items() -> usize { count_ident!($($Y)*) } fn left_shift_push(&mut self, mut item: A) { use std::mem::replace; let &mut ($(ref mut $Y),*,) = self; macro_rules! replace_item{($i:ident) => { item = replace($i, item); }} rev_for_each_ident!(replace_item, $($Y,)*); drop(item); } } ) } impl_tuple_collect!(dummy, a, b, c, d, e, f, g, h, i, j, k, l,);