Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 53 additions & 51 deletions src/pad_tail.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::size_hint;
use std::iter::{Fuse, FusedIterator};

/// An iterator adaptor that pads a sequence to a minimum length by filling
Expand All @@ -11,28 +10,36 @@ use std::iter::{Fuse, FusedIterator};
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct PadUsing<I, F> {
iter: Fuse<I>,
min: usize,
pos: usize,
elements_from_next: usize,
elements_from_next_back: usize,
elements_required: usize,
filler: F,
}

impl<I, F> std::fmt::Debug for PadUsing<I, F>
where
I: std::fmt::Debug,
{
debug_fmt_fields!(PadUsing, iter, min, pos);
debug_fmt_fields!(
PadUsing,
iter,
elements_from_next,
elements_from_next_back,
elements_required
);
}

/// Create a new `PadUsing` iterator.
pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
pub fn pad_using<I, F>(iter: I, elements_required: usize, filler: F) -> PadUsing<I, F>
where
I: Iterator,
F: FnMut(usize) -> I::Item,
{
PadUsing {
iter: iter.fuse(),
min,
pos: 0,
elements_from_next: 0,
elements_from_next_back: 0,
elements_required,
filler,
}
}
Expand All @@ -46,38 +53,34 @@ where

#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
None => {
if self.pos < self.min {
let e = Some((self.filler)(self.pos));
self.pos += 1;
e
} else {
None
}
}
e => {
self.pos += 1;
e
}
let total_consumed = self.elements_from_next + self.elements_from_next_back;

if total_consumed >= self.elements_required {
self.iter.next()
} else if let Some(e) = self.iter.next() {
self.elements_from_next += 1;
Some(e)
} else {
let e = (self.filler)(self.elements_from_next);
self.elements_from_next += 1;
Some(e)
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
let tail = self.min.saturating_sub(self.pos);
size_hint::max(self.iter.size_hint(), (tail, Some(tail)))
}
let total_consumed = self.elements_from_next + self.elements_from_next_back;

if total_consumed >= self.elements_required {
return self.iter.size_hint();
}

let elements_remaining = self.elements_required - total_consumed;
let (low, high) = self.iter.size_hint();

let lower_bound = low.max(elements_remaining);
let upper_bound = high.map(|h| h.max(elements_remaining));

fn fold<B, G>(self, mut init: B, mut f: G) -> B
where
G: FnMut(B, Self::Item) -> B,
{
let mut pos = self.pos;
init = self.iter.fold(init, |acc, item| {
pos += 1;
f(acc, item)
});
(pos..self.min).map(self.filler).fold(init, f)
(lower_bound, upper_bound)
}
}

Expand All @@ -87,25 +90,24 @@ where
F: FnMut(usize) -> I::Item,
{
fn next_back(&mut self) -> Option<Self::Item> {
if self.min == 0 {
self.iter.next_back()
} else if self.iter.len() >= self.min {
self.min -= 1;
self.iter.next_back()
} else {
self.min -= 1;
Some((self.filler)(self.min))
let total_consumed = self.elements_from_next + self.elements_from_next_back;

if total_consumed >= self.elements_required {
return self.iter.next_back();
}
}

fn rfold<B, G>(self, mut init: B, mut f: G) -> B
where
G: FnMut(B, Self::Item) -> B,
{
init = (self.iter.len()..self.min)
.map(self.filler)
.rfold(init, &mut f);
self.iter.rfold(init, f)
let elements_remaining = self.elements_required - total_consumed;
self.elements_from_next_back += 1;

if self.iter.len() < elements_remaining {
Some((self.filler)(
self.elements_required - self.elements_from_next_back,
))
} else {
let item = self.iter.next_back();
debug_assert!(item.is_some()); // If this triggers, we should not have incremented elements_from_next_back, and the input iterator mistakenly reported that it would be able to produce at least elements_remaining items.
item
}
}
}

Expand Down
39 changes: 39 additions & 0 deletions tests/quick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,45 @@ quickcheck! {
}
}

quickcheck! {
fn pad_using_next_next_back(v: Vec<i8>) -> () {
let only_oks = v.into_iter().map(Ok).collect::<Vec<_>>();
for elements_required in 0..128 {
let mut oks_then_errs_vector = only_oks.clone();
while oks_then_errs_vector.len() < elements_required {
oks_then_errs_vector.push(Err(oks_then_errs_vector.len()));
}
let oks_then_errs_pad_using = only_oks.iter().copied().pad_using(elements_required, Err);
assert_eq!(
oks_then_errs_pad_using.clone().collect::<Vec<_>>(),
oks_then_errs_vector
);
// Check next, next_back (https://github.com/rust-itertools/itertools/issues/1065)
let mut iter_check = oks_then_errs_vector.into_iter();
let mut iter_real = oks_then_errs_pad_using;
let mut rng = rand::thread_rng();
loop {
let next_or_next_back = if rng.gen_bool(0.5) {
let element_next_check = iter_check.next();
let element_next_real = iter_real.next();
assert_eq!(element_next_check, element_next_real);
element_next_real
} else {
let element_next_back_check = iter_check.next_back();
let element_next_back_real = iter_real.next_back();
assert_eq!(element_next_back_check, element_next_back_real);
element_next_back_real
};
if next_or_next_back.is_none() {
assert!(iter_real.next().is_none());
assert!(iter_real.next_back().is_none());
break;
}
}
}
}
}

quickcheck! {
fn size_powerset(it: Iter<u8, Exact>) -> bool {
// Powerset cardinality gets large very quickly, limit input to keep test fast.
Expand Down
Loading