From 33e91ccd70011951bea31eda1e462c98e2f30b44 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Sat, 25 Nov 2023 09:22:59 +0100 Subject: [PATCH 1/4] `Unspecialized`: implement `DoubleEndedIterator` --- tests/specializations.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/specializations.rs b/tests/specializations.rs index fe14234d6..24ae03abd 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -5,6 +5,7 @@ use quickcheck::{quickcheck, TestResult}; use std::fmt::Debug; struct Unspecialized(I); + impl Iterator for Unspecialized where I: Iterator, @@ -17,6 +18,16 @@ where } } +impl DoubleEndedIterator for Unspecialized +where + I: DoubleEndedIterator, +{ + #[inline(always)] + fn next_back(&mut self) -> Option { + self.0.next_back() + } +} + fn test_specializations(it: &Iter) where IterItem: Eq + Debug + Clone, From 5d3d3ff35f2f3d6c2602d571b88a4eed3e87b137 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Sat, 25 Nov 2023 09:30:48 +0100 Subject: [PATCH 2/4] Simplify `test_specializations` --- tests/specializations.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/specializations.rs b/tests/specializations.rs index 24ae03abd..d9069d8e2 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -28,10 +28,10 @@ where } } -fn test_specializations(it: &Iter) +fn test_specializations(it: &I) where - IterItem: Eq + Debug + Clone, - Iter: Iterator + Clone, + I::Item: Eq + Debug + Clone, + I: Iterator + Clone, { macro_rules! check_specialized { ($src:expr, |$it:pat| $closure:expr) => { @@ -52,7 +52,7 @@ where check_specialized!(it, |i| i.collect::>()); check_specialized!(it, |i| { let mut parameters_from_fold = vec![]; - let fold_result = i.fold(vec![], |mut acc, v: IterItem| { + let fold_result = i.fold(vec![], |mut acc, v: I::Item| { parameters_from_fold.push((acc.clone(), v.clone())); acc.push(v); acc From b10c51ea0d2624245d089a4482f4550a97d08bc0 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Sat, 25 Nov 2023 09:42:10 +0100 Subject: [PATCH 3/4] Test `DoubleEndedIterator` specializations Similar to `test_specializations` but for `DoubleEndedIterator::{rfold,nth_back}`. The other difference is that in the macro, I advance the iterator alternatively from both fronts 8 times instead of one front 5 times. Note that we don't have any `rfold/nth_back` specialization yet but it will come as I intend to do `rfold` specializations alongside `fold` ones. --- tests/specializations.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/specializations.rs b/tests/specializations.rs index d9069d8e2..b42827d88 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -86,6 +86,44 @@ where } } +fn test_double_ended_specializations(it: &I) +where + I::Item: Eq + Debug + Clone, + I: DoubleEndedIterator + Clone, +{ + macro_rules! check_specialized { + ($src:expr, |$it:pat| $closure:expr) => { + // Many iterators special-case the first elements, so we test specializations for iterators that have already been advanced. + let mut src = $src.clone(); + for step in 0..8 { + let $it = src.clone(); + let v1 = $closure; + let $it = Unspecialized(src.clone()); + let v2 = $closure; + assert_eq!(v1, v2); + if step % 2 == 0 { + src.next(); + } else { + src.next_back(); + } + } + } + } + check_specialized!(it, |i| { + let mut parameters_from_rfold = vec![]; + let rfold_result = i.rfold(vec![], |mut acc, v: I::Item| { + parameters_from_rfold.push((acc.clone(), v.clone())); + acc.push(v); + acc + }); + (parameters_from_rfold, rfold_result) + }); + let size = it.clone().count(); + for n in 0..size + 2 { + check_specialized!(it, |mut i| i.nth_back(n)); + } +} + quickcheck! { fn interleave(v: Vec, w: Vec) -> () { test_specializations(&v.iter().interleave(w.iter())); From 790bf2529c1080267808c1128295fc1ecdc3144b Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Sat, 25 Nov 2023 10:20:50 +0100 Subject: [PATCH 4/4] Use `test_double_ended_specializations` I previously forgot `repeat_n`. --- tests/specializations.rs | 55 ++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/tests/specializations.rs b/tests/specializations.rs index b42827d88..abe41fe92 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -193,19 +193,27 @@ quickcheck! { } fn duplicates(v: Vec) -> () { - test_specializations(&v.iter().duplicates()); + let it = v.iter().duplicates(); + test_specializations(&it); + test_double_ended_specializations(&it); } fn duplicates_by(v: Vec) -> () { - test_specializations(&v.iter().duplicates_by(|x| *x % 10)); + let it = v.iter().duplicates_by(|x| *x % 10); + test_specializations(&it); + test_double_ended_specializations(&it); } fn unique(v: Vec) -> () { - test_specializations(&v.iter().unique()); + let it = v.iter().unique(); + test_specializations(&it); + test_double_ended_specializations(&it); } fn unique_by(v: Vec) -> () { - test_specializations(&v.iter().unique_by(|x| *x % 50)); + let it = v.iter().unique_by(|x| *x % 50); + test_specializations(&it); + test_double_ended_specializations(&it); } fn take_while_inclusive(v: Vec) -> () { @@ -218,7 +226,9 @@ quickcheck! { fn pad_using(v: Vec) -> () { use std::convert::TryFrom; - test_specializations(&v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX))); + let it = v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX)); + test_specializations(&it); + test_double_ended_specializations(&it); } fn with_position(v: Vec) -> () { @@ -226,11 +236,15 @@ quickcheck! { } fn positions(v: Vec) -> () { - test_specializations(&v.iter().positions(|x| x % 5 == 0)); + let it = v.iter().positions(|x| x % 5 == 0); + test_specializations(&it); + test_double_ended_specializations(&it); } fn update(v: Vec) -> () { - test_specializations(&v.iter().copied().update(|x| *x = x.wrapping_mul(7))); + let it = v.iter().copied().update(|x| *x = x.wrapping_mul(7)); + test_specializations(&it); + test_double_ended_specializations(&it); } fn tuple_combinations(v: Vec) -> TestResult { @@ -284,7 +298,9 @@ quickcheck! { } fn zip_longest(a: Vec, b: Vec) -> () { - test_specializations(&a.into_iter().zip_longest(b)) + let it = a.into_iter().zip_longest(b); + test_specializations(&it); + test_double_ended_specializations(&it); } fn zip_eq(a: Vec) -> () { @@ -292,8 +308,9 @@ quickcheck! { } fn multizip(a: Vec) -> () { - let its = (a.iter(), a.iter().rev(), a.iter().take(50)); - test_specializations(&itertools::multizip(its)); + let it = itertools::multizip((a.iter(), a.iter().rev(), a.iter().take(50))); + test_specializations(&it); + test_double_ended_specializations(&it); } fn izip(a: Vec, b: Vec) -> () { @@ -307,6 +324,12 @@ quickcheck! { test_specializations(&itertools::iproduct!(a, b.iter(), c)); TestResult::passed() } + + fn repeat_n(element: i8, n: u8) -> () { + let it = itertools::repeat_n(element, n as usize); + test_specializations(&it); + test_double_ended_specializations(&it); + } } quickcheck! { @@ -400,11 +423,15 @@ quickcheck! { quickcheck! { fn map_into(v: Vec) -> () { - test_specializations(&v.into_iter().map_into::()); + let it = v.into_iter().map_into::(); + test_specializations(&it); + test_double_ended_specializations(&it); } fn map_ok(v: Vec>) -> () { - test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1))); + let it = v.into_iter().map_ok(|u| u.checked_add(1)); + test_specializations(&it); + test_double_ended_specializations(&it); } fn filter_ok(v: Vec>) -> () { @@ -417,7 +444,9 @@ quickcheck! { // `Option` because `Vec` would be very slow!! And we can't give `[u8; 3]`. fn flatten_ok(v: Vec, char>>) -> () { - test_specializations(&v.into_iter().flatten_ok()); + let it = v.into_iter().flatten_ok(); + test_specializations(&it); + test_double_ended_specializations(&it); } }