Repository: rust-itertools/itertools
Pull request #796 (2023-11-06 09:41:23)

Implement a free `sorted_unstable`
Would it be possible to merge this?

Philippe-Cholet: It surely seems legitimate. But I don't know what is our policy about "free" functions yet.
@phimuemue @jswrenn 

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 2a023a1..69f653f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -71,3 +71,7 @@ harness = false
 [[bench]]
 name = "powerset"
 harness = false
+
+[[bench]]
+name = "specializations"
+harness = false
diff --git a/benches/bench1.rs b/benches/bench1.rs
index 84caf88..0946aff 100644
--- a/benches/bench1.rs
+++ b/benches/bench1.rs
@@ -1,8 +1,7 @@
 use criterion::{black_box, criterion_group, criterion_main, Criterion};
 use itertools::free::cloned;
+use itertools::iproduct;
 use itertools::Itertools;
-use itertools::Position;
-use itertools::{iproduct, EitherOrBoth};
 
 use std::cmp;
 use std::iter::repeat;
@@ -391,25 +390,6 @@ fn zip_unchecked_counted_loop3(c: &mut Criterion) {
     });
 }
 
-fn ziplongest(c: &mut Criterion) {
-    let v1 = black_box((0..768).collect_vec());
-    let v2 = black_box((0..1024).collect_vec());
-    c.bench_function("ziplongest", move |b| {
-        b.iter(|| {
-            let zip = v1.iter().zip_longest(v2.iter());
-            let sum = zip.fold(0u32, |mut acc, val| {
-                match val {
-                    EitherOrBoth::Both(x, y) => acc += x * y,
-                    EitherOrBoth::Left(x) => acc += x,
-                    EitherOrBoth::Right(y) => acc += y,
-                }
-                acc
-            });
-            sum
-        })
-    });
-}
-
 fn group_by_lazy_1(c: &mut Criterion) {
     let mut data = vec![0; 1024];
     for (index, elt) in data.iter_mut().enumerate() {
@@ -448,25 +428,6 @@ fn group_by_lazy_2(c: &mut Criterion) {
     });
 }
 
-fn while_some(c: &mut Criterion) {
-    c.bench_function("while_some", |b| {
-        b.iter(|| {
-            let data = black_box(
-                (0..)
-                    .fuse()
-                    .map(|i| std::char::from_digit(i, 16))
-                    .while_some(),
-            );
-            // let result: String = data.fold(String::new(), |acc, ch| acc + &ch.to_string());
-            let result = data.fold(String::new(), |mut acc, ch| {
-                acc.push(ch);
-                acc
-            });
-            assert_eq!(result.as_str(), "0123456789abcdef");
-        });
-    });
-}
-
 fn slice_chunks(c: &mut Criterion) {
     let data = vec![0; 1024];
 
@@ -703,22 +664,6 @@ fn cartesian_product_iterator(c: &mut Criterion) {
     });
 }
 
-fn cartesian_product_fold(c: &mut Criterion) {
-    let xs = vec![0; 16];
-
-    c.bench_function("cartesian product fold", move |b| {
-        b.iter(|| {
-            let mut sum = 0;
-            iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| {
-                sum += x;
-                sum += y;
-                sum += z;
-            });
-            sum
-        })
-    });
-}
-
 fn multi_cartesian_product_iterator(c: &mut Criterion) {
     let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
 
@@ -735,22 +680,6 @@ fn multi_cartesian_product_iterator(c: &mut Criterion) {
     });
 }
 
-fn multi_cartesian_product_fold(c: &mut Criterion) {
-    let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
-
-    c.bench_function("multi cartesian product fold", move |b| {
-        b.iter(|| {
-            let mut sum = 0;
-            xs.iter().multi_cartesian_product().fold((), |(), x| {
-                sum += x[0];
-                sum += x[1];
-                sum += x[2];
-            });
-            sum
-        })
-    });
-}
-
 fn cartesian_product_nested_for(c: &mut Criterion) {
     let xs = vec![0; 16];
 
@@ -839,33 +768,6 @@ fn permutations_slice(c: &mut Criterion) {
     });
 }
 
-fn with_position_fold(c: &mut Criterion) {
-    let v = black_box((0..10240).collect_vec());
-    c.bench_function("with_position fold", move |b| {
-        b.iter(|| {
-            v.iter()
-                .with_position()
-                .fold(0, |acc, (pos, &item)| match pos {
-                    Position::Middle => acc + item,
-                    Position::First => acc - 2 * item,
-                    Position::Last => acc + 2 * item,
-                    Position::Only => acc + 5 * item,
-                })
-        })
-    });
-}
-
-fn tuple_combinations_fold(c: &mut Criterion) {
-    let v = black_box((0..64).collect_vec());
-    c.bench_function("tuple_combinations fold", move |b| {
-        b.iter(|| {
-            v.iter()
-                .tuple_combinations()
-                .fold(0, |acc, (a, b, c, d)| acc + *a * *c - *b * *d)
-        })
-    });
-}
-
 criterion_group!(
     benches,
     slice_iter,
@@ -887,7 +789,6 @@ criterion_group!(
     zipdot_i32_unchecked_counted_loop,
     zipdot_f32_unchecked_counted_loop,
     zip_unchecked_counted_loop3,
-    ziplongest,
     group_by_lazy_1,
     group_by_lazy_2,
     slice_chunks,
@@ -903,9 +804,7 @@ criterion_group!(
     step_range_2,
     step_range_10,
     cartesian_product_iterator,
-    cartesian_product_fold,
     multi_cartesian_product_iterator,
-    multi_cartesian_product_fold,
     cartesian_product_nested_for,
     all_equal,
     all_equal_for,
@@ -913,8 +812,5 @@ criterion_group!(
     permutations_iter,
     permutations_range,
     permutations_slice,
-    with_position_fold,
-    tuple_combinations_fold,
-    while_some,
 );
 criterion_main!(benches);
diff --git a/benches/specializations.rs b/benches/specializations.rs
new file mode 100644
index 0000000..1392708
--- /dev/null
+++ b/benches/specializations.rs
@@ -0,0 +1,176 @@
+use criterion::black_box;
+use itertools::iproduct;
+use itertools::Itertools;
+
+/// Create multiple functions each defining a benchmark group about iterator methods.
+///
+/// Each created group has functions with the following ids:
+///
+/// - `next`, `size_hint`, `count`, `last`, `nth`, `collect`, `fold`
+/// - and when marked as `DoubleEndedIterator`: `next_back`, `rfold`
+/// - and when marked as `ExactSizeIterator`: `len`
+///
+/// Note that this macro can be called only once.
+macro_rules! bench_specializations {
+    (
+        $(
+            $name:ident {
+                $($extra:ident)*
+                {$(
+                    $init:stmt;
+                )*}
+                $iterator:expr
+            }
+        )*
+    ) => {
+        $(
+            fn $name(c: &mut ::criterion::Criterion) {
+                let mut bench_group = c.benchmark_group(stringify!($name));
+                $(
+                    $init
+                )*
+                let bench_first_its = {
+                    let mut bench_idx = 0;
+                    [0; 1000].map(|_| {
+                        let mut it = $iterator;
+                        if bench_idx != 0 {
+                            it.nth(bench_idx - 1);
+                        }
+                        bench_idx += 1;
+                        it
+                    })
+                };
+                bench_specializations!(@Iterator bench_group bench_first_its: $iterator);
+                $(
+                    bench_specializations!(@$extra bench_group bench_first_its: $iterator);
+                )*
+                bench_group.finish();
+            }
+        )*
+
+        ::criterion::criterion_group!(benches, $($name, )*);
+        ::criterion::criterion_main!(benches);
+    };
+
+    (@Iterator $group:ident $first_its:ident: $iterator:expr) => {
+        $group.bench_function("next", |bencher| bencher.iter(|| {
+            let mut it = $iterator;
+            while let Some(x) = it.next() {
+                black_box(x);
+            }
+        }));
+        $group.bench_function("size_hint", |bencher| bencher.iter(|| {
+            $first_its.iter().for_each(|it| {
+                black_box(it.size_hint());
+            })
+        }));
+        $group.bench_function("count", |bencher| bencher.iter(|| {
+            $iterator.count()
+        }));
+        $group.bench_function("last", |bencher| bencher.iter(|| {
+            $iterator.last()
+        }));
+        $group.bench_function("nth", |bencher| bencher.iter(|| {
+            for start in 0_usize..10 {
+                for n in 0..10 {
+                    let mut it = $iterator;
+                    if let Some(s) = start.checked_sub(1) {
+                        black_box(it.nth(s));
+                    }
+                    while let Some(x) = it.nth(n) {
+                        black_box(x);
+                    }
+                }
+            }
+        }));
+        $group.bench_function("collect", |bencher| bencher.iter(|| {
+            $iterator.collect::<Vec<_>>()
+        }));
+        $group.bench_function("fold", |bencher| bencher.iter(|| {
+            $iterator.fold((), |(), x| {
+                black_box(x);
+            })
+        }));
+    };
+
+    (@DoubleEndedIterator $group:ident $_first_its:ident: $iterator:expr) => {
+        $group.bench_function("next_back", |bencher| bencher.iter(|| {
+            let mut it = $iterator;
+            while let Some(x) = it.next_back() {
+                black_box(x);
+            }
+        }));
+        $group.bench_function("nth_back", |bencher| bencher.iter(|| {
+            for start in 0_usize..10 {
+                for n in 0..10 {
+                    let mut it = $iterator;
+                    if let Some(s) = start.checked_sub(1) {
+                        black_box(it.nth_back(s));
+                    }
+                    while let Some(x) = it.nth_back(n) {
+                        black_box(x);
+                    }
+                }
+            }
+        }));
+        $group.bench_function("rfold", |bencher| bencher.iter(|| {
+            $iterator.rfold((), |(), x| {
+                black_box(x);
+            })
+        }));
+    };
+
+    (@ExactSizeIterator $group:ident $first_its:ident: $_iterator:expr) => {
+        $group.bench_function("len", |bencher| bencher.iter(|| {
+            $first_its.iter().for_each(|it| {
+                black_box(it.len());
+            })
+        }));
+    };
+}
+
+// Example: To bench only `ZipLongest::fold`, you can do
+//     cargo bench --bench specializations zip_longest/fold
+bench_specializations! {
+    cartesian_product {
+        {
+            let v = black_box(vec![0; 16]);
+        }
+        iproduct!(&v, &v, &v)
+    }
+    multi_cartesian_product {
+        {
+            let vs = black_box([0; 3].map(|_| vec![0; 16]));
+        }
+        vs.iter().multi_cartesian_product()
+    }
+    tuple_combinations {
+        {
+            let v = black_box((0..64).collect_vec());
+        }
+        v.iter().tuple_combinations::<(_, _, _, _)>()
+    }
+    while_some {
+        {}
+        (0..)
+            .map(black_box)
+            .map(|i| char::from_digit(i, 16))
+            .while_some()
+    }
+    with_position {
+        ExactSizeIterator
+        {
+            let v = black_box((0..10240).collect_vec());
+        }
+        v.iter().with_position()
+    }
+    zip_longest {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let xs = black_box(vec![0; 1024]);
+            let ys = black_box(vec![0; 768]);
+        }
+        xs.iter().zip_longest(ys.iter())
+    }
+}
diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs
index 7d05a70..4355937 100644
--- a/src/adaptors/mod.rs
+++ b/src/adaptors/mod.rs
@@ -935,6 +935,7 @@ where
 ///
 /// See [`.filter_map_ok()`](crate::Itertools::filter_map_ok) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Clone)]
 pub struct FilterMapOk<I, F> {
     iter: I,
     f: F,
diff --git a/src/combinations.rs b/src/combinations.rs
index 1c834a7..d8b5351 100644
--- a/src/combinations.rs
+++ b/src/combinations.rs
@@ -37,12 +37,9 @@ pub fn combinations<I>(iter: I, k: usize) -> Combinations<I>
 where
     I: Iterator,
 {
-    let mut pool = LazyBuffer::new(iter);
-    pool.prefill(k);
-
     Combinations {
         indices: (0..k).collect(),
-        pool,
+        pool: LazyBuffer::new(iter),
         first: true,
     }
 }
@@ -107,6 +104,7 @@ where
     type Item = Vec<I::Item>;
     fn next(&mut self) -> Option<Self::Item> {
         if self.first {
+            self.pool.prefill(self.k());
             if self.k() > self.n() {
                 return None;
             }
diff --git a/src/duplicates_impl.rs b/src/duplicates_impl.rs
index a80316a..71ed218 100644
--- a/src/duplicates_impl.rs
+++ b/src/duplicates_impl.rs
@@ -122,7 +122,7 @@ mod private {
     }
 
     /// Apply the identity function to elements before checking them for equality.
-    #[derive(Debug)]
+    #[derive(Debug, Clone)]
     pub struct ById;
     impl<V> KeyMethod<V, V> for ById {
         type Container = JustValue<V>;
@@ -133,6 +133,7 @@ mod private {
     }
 
     /// Apply a user-supplied function to elements before checking them for equality.
+    #[derive(Clone)]
     pub struct ByFn<F>(pub(crate) F);
     impl<F> fmt::Debug for ByFn<F> {
         debug_fmt_fields!(ByFn,);
diff --git a/src/free.rs b/src/free.rs
index 6de0a0d..5ce1b49 100644
--- a/src/free.rs
+++ b/src/free.rs
@@ -295,3 +295,22 @@ where
 {
     iterable.into_iter().sorted()
 }
+
+/// Sort all iterator elements into a new iterator in ascending order.
+/// This sort is unstable (i.e., may reorder equal elements).
+/// [`IntoIterator`] enabled version of [`Itertools::sorted_unstable`].
+///
+/// ```
+/// use itertools::sorted_unstable;
+/// use itertools::assert_equal;
+///
+/// assert_equal(sorted_unstable("rust".chars()), "rstu".chars());
+/// ```
+#[cfg(feature = "use_alloc")]
+pub fn sorted_unstable<I>(iterable: I) -> VecIntoIter<I::Item>
+where
+    I: IntoIterator,
+    I::Item: Ord,
+{
+    iterable.into_iter().sorted_unstable()
+}
diff --git a/src/take_while_inclusive.rs b/src/take_while_inclusive.rs
index 46c7a9b..5207d8a 100644
--- a/src/take_while_inclusive.rs
+++ b/src/take_while_inclusive.rs
@@ -8,6 +8,7 @@ use std::fmt;
 /// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive)
 /// for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Clone)]
 pub struct TakeWhileInclusive<I, F> {
     iter: I,
     predicate: F,


Test patch:
diff --git a/tests/specializations.rs b/tests/specializations.rs
index 3f5e106..fe14234 100644
--- a/tests/specializations.rs
+++ b/tests/specializations.rs
@@ -1,7 +1,7 @@
 #![allow(unstable_name_collisions)]
 
 use itertools::Itertools;
-use quickcheck::quickcheck;
+use quickcheck::{quickcheck, TestResult};
 use std::fmt::Debug;
 
 struct Unspecialized<I>(I);
@@ -76,29 +76,188 @@ where
 }
 
 quickcheck! {
+    fn interleave(v: Vec<u8>, w: Vec<u8>) -> () {
+        test_specializations(&v.iter().interleave(w.iter()));
+    }
+
+    fn interleave_shortest(v: Vec<u8>, w: Vec<u8>) -> () {
+        test_specializations(&v.iter().interleave_shortest(w.iter()));
+    }
+
+    fn batching(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().batching(Iterator::next));
+    }
+
+    fn tuple_windows(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().tuple_windows::<(_,)>());
+        test_specializations(&v.iter().tuple_windows::<(_, _)>());
+        test_specializations(&v.iter().tuple_windows::<(_, _, _)>());
+    }
+
+    fn circular_tuple_windows(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().circular_tuple_windows::<(_,)>());
+        test_specializations(&v.iter().circular_tuple_windows::<(_, _)>());
+        test_specializations(&v.iter().circular_tuple_windows::<(_, _, _)>());
+    }
+
+    fn tuples(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().tuples::<(_,)>());
+        test_specializations(&v.iter().tuples::<(_, _)>());
+        test_specializations(&v.iter().tuples::<(_, _, _)>());
+    }
+
+    fn cartesian_product(a: Vec<u8>, b: Vec<u8>) -> TestResult {
+        if a.len() * b.len() > 100 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().cartesian_product(&b));
+        TestResult::passed()
+    }
+
+    #[ignore] // It currently fails because `MultiProduct` is not fused.
+    fn multi_cartesian_product(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult {
+        if a.len() * b.len() * c.len() > 100 {
+            return TestResult::discard();
+        }
+        test_specializations(&vec![a, b, c].into_iter().multi_cartesian_product());
+        TestResult::passed()
+    }
+
+    fn coalesce(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) }))
+    }
+
+    fn dedup(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup())
+    }
+
+    fn dedup_by(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup_by(PartialOrd::ge))
+    }
+
+    fn dedup_with_count(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup_with_count())
+    }
+
+    fn dedup_by_with_count(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup_by_with_count(PartialOrd::ge))
+    }
+
+    fn duplicates(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().duplicates());
+    }
+
+    fn duplicates_by(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().duplicates_by(|x| *x % 10));
+    }
+
+    fn unique(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().unique());
+    }
+
+    fn unique_by(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().unique_by(|x| *x % 50));
+    }
+
+    fn take_while_inclusive(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().copied().take_while_inclusive(|&x| x < 100));
+    }
+
+    fn while_some(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().map(|&x| if x < 100 { Some(2 * x) } else { None }).while_some());
+    }
+
+    fn pad_using(v: Vec<u8>) -> () {
+        use std::convert::TryFrom;
+        test_specializations(&v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX)));
+    }
+
     fn with_position(v: Vec<u8>) -> () {
         test_specializations(&v.iter().with_position());
     }
 
-    fn tuple_combinations(v: Vec<u8>) -> () {
-        let mut v = v;
-        v.truncate(10);
+    fn positions(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().positions(|x| x % 5 == 0));
+    }
+
+    fn update(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().copied().update(|x| *x = x.wrapping_mul(7)));
+    }
+
+    fn tuple_combinations(v: Vec<u8>) -> TestResult {
+        if v.len() > 10 {
+            return TestResult::discard();
+        }
+        test_specializations(&v.iter().tuple_combinations::<(_,)>());
+        test_specializations(&v.iter().tuple_combinations::<(_, _)>());
         test_specializations(&v.iter().tuple_combinations::<(_, _, _)>());
+        TestResult::passed()
     }
 
     fn intersperse(v: Vec<u8>) -> () {
         test_specializations(&v.into_iter().intersperse(0));
     }
 
-    fn powerset(a: Vec<u8>) -> () {
-        let mut a = a;
-        a.truncate(6);
-        test_specializations(&a.iter().powerset())
+    fn intersperse_with(v: Vec<u8>) -> () {
+        test_specializations(&v.into_iter().intersperse_with(|| 0));
+    }
+
+    fn combinations(a: Vec<u8>, n: u8) -> TestResult {
+        if n > 3 || a.len() > 8 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().combinations(n as usize));
+        TestResult::passed()
+    }
+
+    fn combinations_with_replacement(a: Vec<u8>, n: u8) -> TestResult {
+        if n > 3 || a.len() > 7 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().combinations_with_replacement(n as usize));
+        TestResult::passed()
+    }
+
+    fn permutations(a: Vec<u8>, n: u8) -> TestResult {
+        if n > 3 || a.len() > 8 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().permutations(n as usize));
+        TestResult::passed()
+    }
+
+    fn powerset(a: Vec<u8>) -> TestResult {
+        if a.len() > 6 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().powerset());
+        TestResult::passed()
     }
 
     fn zip_longest(a: Vec<u8>, b: Vec<u8>) -> () {
         test_specializations(&a.into_iter().zip_longest(b))
     }
+
+    fn zip_eq(a: Vec<u8>) -> () {
+        test_specializations(&a.iter().zip_eq(a.iter().rev()))
+    }
+
+    fn multizip(a: Vec<u8>) -> () {
+        let its = (a.iter(), a.iter().rev(), a.iter().take(50));
+        test_specializations(&itertools::multizip(its));
+    }
+
+    fn izip(a: Vec<u8>, b: Vec<u8>) -> () {
+        test_specializations(&itertools::izip!(b.iter(), a, b.iter().rev()));
+    }
+
+    fn iproduct(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult {
+        if a.len() * b.len() * c.len() > 200 {
+            return TestResult::discard();
+        }
+        test_specializations(&itertools::iproduct!(a, b.iter(), c));
+        TestResult::passed()
+    }
 }
 
 quickcheck! {
@@ -108,11 +267,85 @@ quickcheck! {
         pb.put_back(1);
         test_specializations(&pb);
     }
+
+    fn put_back_n(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::put_back_n(v);
+        for k in 0..n {
+            it.put_back(k);
+        }
+        test_specializations(&it);
+    }
+
+    fn multipeek(v: Vec<u8>, n: u8) -> () {
+        let mut it = v.into_iter().multipeek();
+        for _ in 0..n {
+            it.peek();
+        }
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        for _ in 0..n {
+            it.peek();
+        }
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek_nth(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        it.peek_nth(n as usize);
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek_mut(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        for _ in 0..n {
+            if let Some(x) = it.peek_mut() {
+                *x = x.wrapping_add(50);
+            }
+        }
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek_nth_mut(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        if let Some(x) = it.peek_nth_mut(n as usize) {
+            *x = x.wrapping_add(50);
+        }
+        test_specializations(&it);
+    }
 }
 
 quickcheck! {
-    fn merge_join_by_qc(i1: Vec<usize>, i2: Vec<usize>) -> () {
-        test_specializations(&i1.into_iter().merge_join_by(i2.into_iter(), std::cmp::Ord::cmp));
+    fn merge(a: Vec<u8>, b: Vec<u8>) -> () {
+        test_specializations(&a.into_iter().merge(b))
+    }
+
+    fn merge_by(a: Vec<u8>, b: Vec<u8>) -> () {
+        test_specializations(&a.into_iter().merge_by(b, PartialOrd::ge))
+    }
+
+    fn merge_join_by_ordering(i1: Vec<u8>, i2: Vec<u8>) -> () {
+        test_specializations(&i1.into_iter().merge_join_by(i2, Ord::cmp));
+    }
+
+    fn merge_join_by_bool(i1: Vec<u8>, i2: Vec<u8>) -> () {
+        test_specializations(&i1.into_iter().merge_join_by(i2, PartialOrd::ge));
+    }
+
+    fn kmerge(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () {
+        test_specializations(&vec![a, b, c]
+            .into_iter()
+            .map(|v| v.into_iter().sorted())
+            .kmerge());
+    }
+
+    fn kmerge_by(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () {
+        test_specializations(&vec![a, b, c]
+            .into_iter()
+            .map(|v| v.into_iter().sorted_by_key(|a| a.abs()))
+            .kmerge_by(|a, b| a.abs() < b.abs()));
     }
 }
 
@@ -120,12 +353,23 @@ quickcheck! {
     fn map_into(v: Vec<u8>) -> () {
         test_specializations(&v.into_iter().map_into::<u32>());
     }
-}
 
-quickcheck! {
     fn map_ok(v: Vec<Result<u8, char>>) -> () {
         test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1)));
     }
+
+    fn filter_ok(v: Vec<Result<u8, char>>) -> () {
+        test_specializations(&v.into_iter().filter_ok(|&i| i < 20));
+    }
+
+    fn filter_map_ok(v: Vec<Result<u8, char>>) -> () {
+        test_specializations(&v.into_iter().filter_map_ok(|i| if i < 20 { Some(i * 2) } else { None }));
+    }
+
+    // `Option<u8>` because `Vec<u8>` would be very slow!! And we can't give `[u8; 3]`.
+    fn flatten_ok(v: Vec<Result<Option<u8>, char>>) -> () {
+        test_specializations(&v.into_iter().flatten_ok());
+    }
 }
 
 quickcheck! {
diff --git a/tests/test_std.rs b/tests/test_std.rs
index 732790e..732be7b 100644
--- a/tests/test_std.rs
+++ b/tests/test_std.rs
@@ -1044,7 +1044,7 @@ fn combinations_inexact_size_hints() {
         let len = binomial(real_n, k);
         assert_eq!(len, it.clone().count());
 
-        let mut nb_loaded = numbers.by_ref().take(k).count(); // because of `LazyBuffer::prefill(k)`
+        let mut nb_loaded = 0;
         let sh = numbers.size_hint();
         assert_eq!(binomial(sh.0 + nb_loaded, k), it.size_hint().0);
         assert_eq!(sh.1.map(|n| binomial(n + nb_loaded, k)), it.size_hint().1);
@@ -1053,8 +1053,10 @@ fn combinations_inexact_size_hints() {
             let elem = it.next();
             assert!(elem.is_some());
             assert_eq!(len - next_count, it.clone().count());
-            // It does not load anything more the very first time (it's prefilled).
-            if next_count > 1 {
+            if next_count == 1 {
+                // The very first time, the lazy buffer is prefilled.
+                nb_loaded = numbers.by_ref().take(k).count();
+            } else {
                 // Then it loads one item each time until exhausted.
                 let nb = numbers.next();
                 if nb.is_some() {