Snap for 8564071 from 9809c901b8737f6d3e2a91fadf77fdf445620553 to mainline-art-release

Change-Id: Iebec161800eefc941f5f6dcaf3d0c17062ffab81
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index e778cc0..5b491ea 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "4dac4c718895d8c0347d90be70f478673dd28193"
+    "sha1": "3003c2a968f144cbccb63c768d2fec2e83a69ca4"
   }
 }
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index efc779f..143ac2b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,14 +38,3 @@
     steps:
       - name: Mark the job as successful
         run: exit 0
-
-  end-failure:
-    name: bors build finished
-    if: "!success()"
-    runs-on: ubuntu-latest
-    needs: [msrv,stable]
-
-    steps:
-      - name: Mark the job as a failure
-        run: exit 1
-
diff --git a/Android.bp b/Android.bp
index 963b929..09dfee1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,4 +1,4 @@
-// This file is generated by cargo2android.py --run --device --dependencies.
+// This file is generated by cargo2android.py --config cargo2android.json.
 // Do not modify this file as changes will be overridden on upgrade.
 
 package {
@@ -39,8 +39,11 @@
 
 rust_library {
     name: "libitertools",
+    // has rustc warnings
     host_supported: true,
     crate_name: "itertools",
+    cargo_env_compat: true,
+    cargo_pkg_version: "0.10.3",
     srcs: ["src/lib.rs"],
     edition: "2018",
     features: [
@@ -52,6 +55,3 @@
         "libeither",
     ],
 }
-
-// dependent_library ["feature_list"]
-//   either-1.6.1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67633ab..5e2032c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,59 @@
 # Changelog
 
+## 0.10.2
+  - Add `Itertools::multiunzip` (#362, #565)
+  - Add `intersperse` and `intersperse_with` free functions (#555)
+  - Add `Itertools::sorted_by_cached_key` (#424, #575)
+  - Specialize `ProcessResults::fold` (#563)
+  - Fix subtraction overflow in `DuplicatesBy::size_hint` (#552)
+  - Fix specialization tests (#574)
+  - More `Debug` impls (#573)
+  - Deprecate `fold1` (use `reduce` instead) (#580)
+  - Documentation fixes (`HomogenousTuple`, `into_group_map`, `into_group_map_by`, `MultiPeek::peek`) (#543 et al.)
+
+## 0.10.1
+  - Add `Itertools::contains` (#514)
+  - Add `Itertools::counts_by` (#515)
+  - Add `Itertools::partition_result` (#511)
+  - Add `Itertools::all_unique` (#241)
+  - Add `Itertools::duplicates` and `Itertools::duplicates_by` (#502)
+  - Add `chain!` (#525)
+  - Add `Itertools::at_most_one` (#523)
+  - Add `Itertools::flatten_ok` (#527)
+  - Add `EitherOrBoth::or_default` (#583)
+  - Add `Itertools::find_or_last` and `Itertools::find_or_first` (#535)
+  - Implement `FusedIterator` for `FilterOk`, `FilterMapOk`, `InterleaveShortest`, `KMergeBy`, `MergeBy`, `PadUsing`, `Positions`, `Product` , `RcIter`, `TupleWindows`, `Unique`, `UniqueBy`,  `Update`, `WhileSome`, `Combinations`, `CombinationsWithReplacement`, `Powerset`, `RepeatN`, and `WithPosition` (#550)
+  - Implement `FusedIterator` for `Interleave`, `IntersperseWith`, and `ZipLongest` (#548)
+
+## 0.10.0
+  - **Increase minimum supported Rust version to 1.32.0**
+  - Improve macro hygiene (#507)
+  - Add `Itertools::powerset` (#335)
+  - Add `Itertools::sorted_unstable`, `Itertools::sorted_unstable_by`, and `Itertools::sorted_unstable_by_key` (#494)
+  - Implement `Error` for `ExactlyOneError` (#484)
+  - Undeprecate `Itertools::fold_while` (#476)
+  - Tuple-related adapters work for tuples of arity up to 12 (#475)
+  - `use_alloc` feature for users who have `alloc`, but not `std` (#474)
+  - Add `Itertools::k_smallest` (#473)
+  - Add `Itertools::into_grouping_map` and `GroupingMap` (#465)
+  - Add `Itertools::into_grouping_map_by` and `GroupingMapBy` (#465)
+  - Add `Itertools::counts` (#468)
+  - Add implementation of `DoubleEndedIterator` for `Unique` (#442)
+  - Add implementation of `DoubleEndedIterator` for `UniqueBy` (#442)
+  - Add implementation of `DoubleEndedIterator` for `Zip` (#346)
+  - Add `Itertools::multipeek` (#435)
+  - Add `Itertools::dedup_with_count` and `DedupWithCount` (#423)
+  - Add `Itertools::dedup_by_with_count` and `DedupByWithCount` (#423)
+  - Add `Itertools::intersperse_with` and `IntersperseWith` (#381)
+  - Add `Itertools::filter_ok` and `FilterOk` (#377)
+  - Add `Itertools::filter_map_ok` and `FilterMapOk` (#377)
+  - Deprecate `Itertools::fold_results`, use `Itertools::fold_ok` instead (#377)
+  - Deprecate `Itertools::map_results`, use `Itertools::map_ok` instead (#377)
+  - Deprecate `FoldResults`, use `FoldOk` instead (#377)
+  - Deprecate `MapResults`, use `MapOk` instead (#377)
+  - Add `Itertools::circular_tuple_windows` and `CircularTupleWindows` (#350)
+  - Add `peek_nth` and `PeekNth` (#303)
+
 ## 0.9.0
   - Fix potential overflow in `MergeJoinBy::size_hint` (#385)
   - Add `derive(Clone)` where possible (#382)
@@ -163,7 +217,7 @@
 ## 0.5.1
   - Workaround module/function name clash that made racer crash on completing itertools. Only internal changes needed.
 ## 0.5.0
-  - [Release announcement](http://bluss.github.io/rust/2016/09/26/itertools-0.5.0/)
+  - [Release announcement](https://bluss.github.io/rust/2016/09/26/itertools-0.5.0/)
   - Renamed:
     - `combinations` is now `tuple_combinations`
     - `combinations_n` to `combinations`
@@ -217,7 +271,7 @@
 ## 0.4.15
   - Fixup on top of the workaround in 0.4.14. A function in `itertools::free` was removed by mistake and now it is added back again.
 ## 0.4.14
-  - Workaround an upstream regression in a rust nightly build that broke compilation of of `itertools::free::{interleave, merge}`
+  - Workaround an upstream regression in a Rust nightly build that broke compilation of of `itertools::free::{interleave, merge}`
 ## 0.4.13
   - Add `.minmax()` and `.minmax_by_key()`, iterator methods for finding both minimum and maximum in one scan.
   - Add `.format_default()`, a simpler version of `.format()` (lazy formatting for iterators).
@@ -283,9 +337,9 @@
 ## 0.3.19
   - Added `.group_by_lazy()`, a possibly nonallocating group by
   - Added `.format()`, a nonallocating formatting helper for iterators
-  - Remove uses of `RandomAccessIterator` since it has been deprecated in rust.
+  - Remove uses of `RandomAccessIterator` since it has been deprecated in Rust.
 ## 0.3.17
-  - Added (adopted) `Unfold` from rust
+  - Added (adopted) `Unfold` from Rust
 ## 0.3.16
   - Added adaptors `.unique()`, `.unique_by()`
 ## 0.3.15
diff --git a/Cargo.toml b/Cargo.toml
index ffe2f37..525cae5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,15 +13,16 @@
 [package]
 edition = "2018"
 name = "itertools"
-version = "0.10.0"
+version = "0.10.3"
 authors = ["bluss"]
 exclude = ["/bors.toml"]
 description = "Extra iterator adaptors, iterator methods, free functions, and macros."
 documentation = "https://docs.rs/itertools/"
+readme = "README.md"
 keywords = ["iterator", "data-structure", "zip", "product", "group-by"]
 categories = ["algorithms", "rust-patterns"]
 license = "MIT/Apache-2.0"
-repository = "https://github.com/bluss/rust-itertools"
+repository = "https://github.com/rust-itertools/itertools"
 [package.metadata.release]
 no-dev-version = true
 [profile.bench]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 46b2094..22a08a8 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,11 +1,12 @@
 [package]
 name = "itertools"
-version = "0.10.0"
+version = "0.10.3"
 
 license = "MIT/Apache-2.0"
-repository = "https://github.com/bluss/rust-itertools"
+repository = "https://github.com/rust-itertools/itertools"
 documentation = "https://docs.rs/itertools/"
 authors = ["bluss"]
+readme = "README.md"
 
 description = "Extra iterator adaptors, iterator methods, free functions, and macros."
 
@@ -27,8 +28,8 @@
 
 [dev-dependencies]
 rand = "0.7"
-criterion = "=0" # TODO how could this work with our minimum supported rust version?
-paste = "1.0.0" # Used in test_std to instanciate generic tests
+criterion = "=0" # TODO how could this work with our minimum supported Rust version?
+paste = "1.0.0" # Used in test_std to instantiate generic tests
 
 [dev-dependencies.quickcheck]
 version = "0.9"
diff --git a/METADATA b/METADATA
index b617903..ca38654 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/itertools/itertools-0.10.0.crate"
+    value: "https://static.crates.io/crates/itertools/itertools-0.10.3.crate"
   }
-  version: "0.10.0"
+  version: "0.10.3"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2021
-    month: 4
+    year: 2022
+    month: 3
     day: 1
   }
 }
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4cc3f8f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,44 @@
+# Itertools
+
+Extra iterator adaptors, functions and macros.
+
+Please read the [API documentation here](https://docs.rs/itertools/).
+
+[![build_status](https://github.com/rust-itertools/itertools/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-itertools/itertools/actions)
+[![crates.io](https://img.shields.io/crates/v/itertools.svg)](https://crates.io/crates/itertools)
+
+How to use with Cargo:
+
+```toml
+[dependencies]
+itertools = "0.10.2"
+```
+
+How to use in your crate:
+
+```rust
+use itertools::Itertools;
+```
+
+## How to contribute
+
+- Fix a bug or implement a new thing
+- Include tests for your new feature, preferably a QuickCheck test
+- Make a Pull Request
+
+For new features, please first consider filing a PR to [rust-lang/rust](https://github.com/rust-lang/rust),
+adding your new feature to the `Iterator` trait of the standard library, if you believe it is reasonable.
+If it isn't accepted there, proposing it for inclusion in ``itertools`` is a good idea.
+The reason for doing is this is so that we avoid future breakage as with ``.flatten()``.
+However, if your feature involves heap allocation, such as storing elements in a ``Vec<T>``,
+then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead.
+
+## License
+
+Dual-licensed to be compatible with the Rust project.
+
+Licensed under the Apache License, Version 2.0
+https://www.apache.org/licenses/LICENSE-2.0 or the MIT license
+https://opensource.org/licenses/MIT, at your
+option. This file may not be copied, modified, or distributed
+except according to those terms.
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 4e37007..0000000
--- a/README.rst
+++ /dev/null
@@ -1,55 +0,0 @@
-
-Itertools
-=========
-
-Extra iterator adaptors, functions and macros.
-
-Please read the `API documentation here`__
-
-__ https://docs.rs/itertools/
-
-|build_status|_ |crates|_
-
-.. |build_status| image:: https://travis-ci.org/rust-itertools/itertools.svg?branch=master
-.. _build_status: https://travis-ci.org/rust-itertools/itertools
-
-.. |crates| image:: http://meritbadge.herokuapp.com/itertools
-.. _crates: https://crates.io/crates/itertools
-
-How to use with cargo:
-
-.. code:: toml
-
-    [dependencies]
-    itertools = "0.9"
-
-How to use in your crate:
-
-.. code:: rust
-
-    use itertools::Itertools;
-
-How to contribute
------------------
-
-- Fix a bug or implement a new thing
-- Include tests for your new feature, preferably a quickcheck test
-- Make a Pull Request
-
-For new features, please first consider filing a PR to `rust-lang/rust <https://github.com/rust-lang/rust/>`_,
-adding your new feature to the `Iterator` trait of the standard library, if you believe it is reasonable.
-If it isn't accepted there, proposing it for inclusion in ``itertools`` is a good idea.
-The reason for doing is this is so that we avoid future breakage as with ``.flatten()``.
-However, if your feature involves heap allocation, such as storing elements in a ``Vec<T>``,
-then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead.
-
-License
--------
-
-Dual-licensed to be compatible with the Rust project.
-
-Licensed under the Apache License, Version 2.0
-http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
-http://opensource.org/licenses/MIT, at your
-option. This file may not be copied, modified, or distributed
-except according to those terms.
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..86d1024
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,33 @@
+// Generated by update_crate_tests.py for tests that depend on this crate.
+{
+  "imports": [
+    {
+      "path": "external/rust/crates/base64"
+    },
+    {
+      "path": "external/rust/crates/tinytemplate"
+    },
+    {
+      "path": "external/rust/crates/tinyvec"
+    },
+    {
+      "path": "external/rust/crates/unicode-xid"
+    }
+  ],
+  "presubmit": [
+    {
+      "name": "apkdmverity.test"
+    },
+    {
+      "name": "microdroid_manager_test"
+    }
+  ],
+  "presubmit-rust": [
+    {
+      "name": "apkdmverity.test"
+    },
+    {
+      "name": "microdroid_manager_test"
+    }
+  ]
+}
diff --git a/cargo2android.json b/cargo2android.json
new file mode 100644
index 0000000..bf78496
--- /dev/null
+++ b/cargo2android.json
@@ -0,0 +1,4 @@
+{
+  "device": true,
+  "run": true
+}
\ No newline at end of file
diff --git a/src/adaptors/coalesce.rs b/src/adaptors/coalesce.rs
index 116f688..b1aff6e 100644
--- a/src/adaptors/coalesce.rs
+++ b/src/adaptors/coalesce.rs
@@ -40,20 +40,21 @@
 
     fn next(&mut self) -> Option<Self::Item> {
         // this fuses the iterator
-        let mut last = match self.last.take() {
-            None => return None,
-            Some(x) => x,
-        };
-        for next in &mut self.iter {
-            match self.f.coalesce_pair(last, next) {
-                Ok(joined) => last = joined,
-                Err((last_, next_)) => {
-                    self.last = Some(next_);
-                    return Some(last_);
-                }
-            }
-        }
-        Some(last)
+        let last = self.last.take()?;
+
+        let self_last = &mut self.last;
+        let self_f = &mut self.f;
+        Some(
+            self.iter
+                .try_fold(last, |last, next| match self_f.coalesce_pair(last, next) {
+                    Ok(joined) => Ok(joined),
+                    Err((last_, next_)) => {
+                        *self_last = Some(next_);
+                        Err(last_)
+                    }
+                })
+                .unwrap_or_else(|x| x),
+        )
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
@@ -84,7 +85,7 @@
 
 /// An iterator adaptor that may join together adjacent elements.
 ///
-/// See [`.coalesce()`](../trait.Itertools.html#method.coalesce) for more information.
+/// See [`.coalesce()`](crate::Itertools::coalesce) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub type Coalesce<I, F> = CoalesceBy<I, F, <I as Iterator>::Item>;
 
@@ -111,13 +112,17 @@
 
 /// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function.
 ///
-/// See [`.dedup_by()`](../trait.Itertools.html#method.dedup_by) or [`.dedup()`](../trait.Itertools.html#method.dedup) for more information.
+/// See [`.dedup_by()`](crate::Itertools::dedup_by) or [`.dedup()`](crate::Itertools::dedup) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as Iterator>::Item>;
 
 #[derive(Clone)]
 pub struct DedupPred2CoalescePred<DP>(DP);
 
+impl<DP> fmt::Debug for DedupPred2CoalescePred<DP> {
+    debug_fmt_fields!(DedupPred2CoalescePred,);
+}
+
 pub trait DedupPredicate<T> {
     // TODO replace by Fn(&T, &T)->bool once Rust supports it
     fn dedup_pair(&mut self, a: &T, b: &T) -> bool;
@@ -136,7 +141,7 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct DedupEq;
 
 impl<T: PartialEq> DedupPredicate<T> for DedupEq {
@@ -165,7 +170,7 @@
 
 /// An iterator adaptor that removes repeated duplicates.
 ///
-/// See [`.dedup()`](../trait.Itertools.html#method.dedup) for more information.
+/// See [`.dedup()`](crate::Itertools::dedup) for more information.
 pub type Dedup<I> = DedupBy<I, DedupEq>;
 
 /// Create a new `Dedup`.
@@ -179,13 +184,13 @@
 /// An iterator adaptor that removes repeated duplicates, while keeping a count of how many
 /// repeated elements were present. This will determine equality using a comparison function.
 ///
-/// See [`.dedup_by_with_count()`](../trait.Itertools.html#method.dedup_by_with_count) or
-/// [`.dedup_with_count()`](../trait.Itertools.html#method.dedup_with_count) for more information.
+/// See [`.dedup_by_with_count()`](crate::Itertools::dedup_by_with_count) or
+/// [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub type DedupByWithCount<I, Pred> =
     CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>;
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct DedupPredWithCount2CoalescePred<DP>(DP);
 
 impl<DP, T> CoalescePredicate<T, (usize, T)> for DedupPredWithCount2CoalescePred<DP>
@@ -208,7 +213,7 @@
 /// An iterator adaptor that removes repeated duplicates, while keeping a count of how many
 /// repeated elements were present.
 ///
-/// See [`.dedup_with_count()`](../trait.Itertools.html#method.dedup_with_count) for more information.
+/// See [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information.
 pub type DedupWithCount<I> = DedupByWithCount<I, DedupEq>;
 
 /// Create a new `DedupByWithCount`.
diff --git a/src/adaptors/map.rs b/src/adaptors/map.rs
index ff377f7..cf5e5a0 100644
--- a/src/adaptors/map.rs
+++ b/src/adaptors/map.rs
@@ -1,7 +1,7 @@
 use std::iter::FromIterator;
 use std::marker::PhantomData;
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct MapSpecialCase<I, F> {
     iter: I,
@@ -64,10 +64,10 @@
 
 /// An iterator adapter to apply a transformation within a nested `Result::Ok`.
 ///
-/// See [`.map_ok()`](../trait.Itertools.html#method.map_ok) for more information.
+/// See [`.map_ok()`](crate::Itertools::map_ok) for more information.
 pub type MapOk<I, F> = MapSpecialCase<I, MapSpecialCaseFnOk<F>>;
 
-/// See [`MapOk`](struct.MapOk.html).
+/// See [`MapOk`].
 #[deprecated(note = "Use MapOk instead", since = "0.10.0")]
 pub type MapResults<I, F> = MapOk<I, F>;
 
@@ -84,6 +84,10 @@
 #[derive(Clone)]
 pub struct MapSpecialCaseFnOk<F>(F);
 
+impl<F> std::fmt::Debug for MapSpecialCaseFnOk<F> {
+    debug_fmt_fields!(MapSpecialCaseFnOk,);
+}
+
 /// Create a new `MapOk` iterator.
 pub fn map_ok<I, F, T, U, E>(iter: I, f: F) -> MapOk<I, F>
 where
@@ -98,7 +102,7 @@
 
 /// An iterator adapter to apply `Into` conversion to each element.
 ///
-/// See [`.map_into()`](../trait.Itertools.html#method.map_into) for more information.
+/// See [`.map_into()`](crate::Itertools::map_into) for more information.
 pub type MapInto<I, R> = MapSpecialCase<I, MapSpecialCaseFnInto<R>>;
 
 impl<T: Into<U>, U> MapSpecialCaseFn<T> for MapSpecialCaseFnInto<U> {
@@ -108,10 +112,10 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct MapSpecialCaseFnInto<U>(PhantomData<U>);
 
-/// Create a new [`MapInto`](struct.MapInto.html) iterator.
+/// Create a new [`MapInto`] iterator.
 pub fn map_into<I, R>(iter: I) -> MapInto<I, R> {
     MapSpecialCase {
         iter,
diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs
index 8a8697b..2010f53 100644
--- a/src/adaptors/mod.rs
+++ b/src/adaptors/mod.rs
@@ -1,6 +1,6 @@
 //! Licensed under the Apache License, Version 2.0
-//! http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
-//! http://opensource.org/licenses/MIT, at your
+//! <https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+//! <https://opensource.org/licenses/MIT>, at your
 //! option. This file may not be copied, modified, or distributed
 //! except according to those terms.
 
@@ -15,7 +15,7 @@
 pub use self::multi_product::*;
 
 use std::fmt;
-use std::iter::{Fuse, Peekable, FromIterator};
+use std::iter::{Fuse, Peekable, FromIterator, FusedIterator};
 use std::marker::PhantomData;
 use crate::size_hint;
 
@@ -24,7 +24,7 @@
 ///
 /// This iterator is *fused*.
 ///
-/// See [`.interleave()`](../trait.Itertools.html#method.interleave) for more information.
+/// See [`.interleave()`](crate::Itertools::interleave) for more information.
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Interleave<I, J> {
@@ -35,9 +35,9 @@
 
 /// Create an iterator that interleaves elements in `i` and `j`.
 ///
-/// `IntoIterator` enabled version of `i.interleave(j)`.
+/// [`IntoIterator`] enabled version of `i.interleave(j)`.
 ///
-/// See [`.interleave()`](trait.Itertools.html#method.interleave) for more information.
+/// See [`.interleave()`](crate::Itertools::interleave) for more information.
 pub fn interleave<I, J>(i: I, j: J) -> Interleave<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
     where I: IntoIterator,
           J: IntoIterator<Item = I::Item>
@@ -75,12 +75,17 @@
     }
 }
 
+impl<I, J> FusedIterator for Interleave<I, J>
+    where I: Iterator,
+          J: Iterator<Item = I::Item>
+{}
+
 /// An iterator adaptor that alternates elements from the two iterators until
 /// one of them runs out.
 ///
 /// This iterator is *fused*.
 ///
-/// See [`.interleave_shortest()`](../trait.Itertools.html#method.interleave_shortest)
+/// See [`.interleave_shortest()`](crate::Itertools::interleave_shortest)
 /// for more information.
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
@@ -157,6 +162,11 @@
     }
 }
 
+impl<I, J> FusedIterator for InterleaveShortest<I, J>
+    where I: FusedIterator,
+          J: FusedIterator<Item = I::Item>
+{}
+
 #[derive(Clone, Debug)]
 /// An iterator adaptor that allows putting back a single
 /// item to the front of the iterator.
@@ -271,7 +281,7 @@
 ///
 /// Iterator element type is `(I::Item, J::Item)`.
 ///
-/// See [`.cartesian_product()`](../trait.Itertools.html#method.cartesian_product) for more information.
+/// See [`.cartesian_product()`](crate::Itertools::cartesian_product) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Product<I, J>
     where I: Iterator
@@ -361,12 +371,18 @@
     }
 }
 
+impl<I, J> FusedIterator for Product<I, J>
+    where I: FusedIterator,
+          J: Clone + FusedIterator,
+          I::Item: Clone
+{}
+
 /// A “meta iterator adaptor”. Its closure receives a reference to the iterator
 /// and may pick off as many elements as it likes, to produce the next iterator element.
 ///
 /// Iterator element type is *X*, if the return type of `F` is *Option\<X\>*.
 ///
-/// See [`.batching()`](../trait.Itertools.html#method.batching) for more information.
+/// See [`.batching()`](crate::Itertools::batching) for more information.
 #[derive(Clone)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Batching<I, F> {
@@ -400,7 +416,7 @@
 /// The iterator steps by yielding the next element from the base iterator,
 /// then skipping forward *n-1* elements.
 ///
-/// See [`.step()`](../trait.Itertools.html#method.step) for more information.
+/// See [`.step()`](crate::Itertools::step) for more information.
 #[deprecated(note="Use std .step_by() instead", since="0.8.0")]
 #[allow(deprecated)]
 #[derive(Clone, Debug)]
@@ -461,7 +477,7 @@
     fn merge_pred(&mut self, a: &T, b: &T) -> bool;
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct MergeLte;
 
 impl<T: PartialOrd> MergePredicate<T> for MergeLte {
@@ -475,13 +491,13 @@
 ///
 /// Iterator element type is `I::Item`.
 ///
-/// See [`.merge()`](../trait.Itertools.html#method.merge_by) for more information.
+/// See [`.merge()`](crate::Itertools::merge_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub type Merge<I, J> = MergeBy<I, J, MergeLte>;
 
 /// Create an iterator that merges elements in `i` and `j`.
 ///
-/// `IntoIterator` enabled version of `i.merge(j)`.
+/// [`IntoIterator`] enabled version of [`Itertools::merge`](crate::Itertools::merge).
 ///
 /// ```
 /// use itertools::merge;
@@ -503,7 +519,7 @@
 ///
 /// Iterator element type is `I::Item`.
 ///
-/// See [`.merge_by()`](../trait.Itertools.html#method.merge_by) for more information.
+/// See [`.merge_by()`](crate::Itertools::merge_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct MergeBy<I, J, F>
     where I: Iterator,
@@ -588,10 +604,16 @@
     }
 }
 
+impl<I, J, F> FusedIterator for MergeBy<I, J, F>
+    where I: FusedIterator,
+          J: FusedIterator<Item = I::Item>,
+          F: MergePredicate<I::Item>
+{}
+
 /// An iterator adaptor that borrows from a `Clone`-able iterator
 /// to only pick off elements while the predicate returns `true`.
 ///
-/// See [`.take_while_ref()`](../trait.Itertools.html#method.take_while_ref) for more information.
+/// See [`.take_while_ref()`](crate::Itertools::take_while_ref) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct TakeWhileRef<'a, I: 'a, F> {
     iter: &'a mut I,
@@ -640,7 +662,7 @@
 /// An iterator adaptor that filters `Option<A>` iterator elements
 /// and produces `A`. Stops on the first `None` encountered.
 ///
-/// See [`.while_some()`](../trait.Itertools.html#method.while_some) for more information.
+/// See [`.while_some()`](crate::Itertools::while_some) for more information.
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct WhileSome<I> {
@@ -672,7 +694,7 @@
 /// An iterator to iterate through all combinations in a `Clone`-able iterator that produces tuples
 /// of a specific size.
 ///
-/// See [`.tuple_combinations()`](../trait.Itertools.html#method.tuple_combinations) for more
+/// See [`.tuple_combinations()`](crate::Itertools::tuple_combinations) for more
 /// information.
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
@@ -682,7 +704,6 @@
 {
     iter: T::Combination,
     _mi: PhantomData<I>,
-    _mt: PhantomData<T>
 }
 
 pub trait HasCombination<I>: Sized {
@@ -698,7 +719,6 @@
     TupleCombinations {
         iter: T::Combination::from(iter),
         _mi: PhantomData,
-        _mt: PhantomData,
     }
 }
 
@@ -713,6 +733,11 @@
     }
 }
 
+impl<I, T> FusedIterator for TupleCombinations<I, T>
+    where I: FusedIterator,
+          T: HasCombination<I>,
+{}
+
 #[derive(Clone, Debug)]
 pub struct Tuple1Combination<I> {
     iter: I,
@@ -737,7 +762,7 @@
 }
 
 macro_rules! impl_tuple_combination {
-    ($C:ident $P:ident ; $A:ident, $($I:ident),* ; $($X:ident)*) => (
+    ($C:ident $P:ident ; $($X:ident)*) => (
         #[derive(Clone, Debug)]
         pub struct $C<I: Iterator> {
             item: Option<I::Item>,
@@ -747,30 +772,25 @@
 
         impl<I: Iterator + Clone> From<I> for $C<I> {
             fn from(mut iter: I) -> Self {
-                $C {
+                Self {
                     item: iter.next(),
                     iter: iter.clone(),
-                    c: $P::from(iter),
+                    c: iter.into(),
                 }
             }
         }
 
         impl<I: Iterator + Clone> From<I> for $C<Fuse<I>> {
             fn from(iter: I) -> Self {
-                let mut iter = iter.fuse();
-                $C {
-                    item: iter.next(),
-                    iter: iter.clone(),
-                    c: $P::from(iter),
-                }
+                Self::from(iter.fuse())
             }
         }
 
-        impl<I, $A> Iterator for $C<I>
-            where I: Iterator<Item = $A> + Clone,
+        impl<I, A> Iterator for $C<I>
+            where I: Iterator<Item = A> + Clone,
                   I::Item: Clone
         {
-            type Item = ($($I),*);
+            type Item = (A, $(ignore_ident!($X, A)),*);
 
             fn next(&mut self) -> Option<Self::Item> {
                 if let Some(($($X),*,)) = self.c.next() {
@@ -779,15 +799,15 @@
                 } else {
                     self.item = self.iter.next();
                     self.item.clone().and_then(|z| {
-                        self.c = $P::from(self.iter.clone());
+                        self.c = self.iter.clone().into();
                         self.c.next().map(|($($X),*,)| (z, $($X),*))
                     })
                 }
             }
         }
 
-        impl<I, $A> HasCombination<I> for ($($I),*)
-            where I: Iterator<Item = $A> + Clone,
+        impl<I, A> HasCombination<I> for (A, $(ignore_ident!($X, A)),*)
+            where I: Iterator<Item = A> + Clone,
                   I::Item: Clone
         {
             type Combination = $C<Fuse<I>>;
@@ -800,29 +820,28 @@
 //    use itertools::Itertools;
 //
 //    for i in 2..=12 {
-//        println!("impl_tuple_combination!(Tuple{arity}Combination Tuple{prev}Combination; {tys}; {idents});",
+//        println!("impl_tuple_combination!(Tuple{arity}Combination Tuple{prev}Combination; {idents});",
 //            arity = i,
 //            prev = i - 1,
-//            tys = iter::repeat("A").take(i + 1).join(", "),
 //            idents = ('a'..'z').take(i - 1).join(" "),
 //        );
 //    }
 // It could probably be replaced by a bit more macro cleverness.
-impl_tuple_combination!(Tuple2Combination Tuple1Combination; A, A, A; a);
-impl_tuple_combination!(Tuple3Combination Tuple2Combination; A, A, A, A; a b);
-impl_tuple_combination!(Tuple4Combination Tuple3Combination; A, A, A, A, A; a b c);
-impl_tuple_combination!(Tuple5Combination Tuple4Combination; A, A, A, A, A, A; a b c d);
-impl_tuple_combination!(Tuple6Combination Tuple5Combination; A, A, A, A, A, A, A; a b c d e);
-impl_tuple_combination!(Tuple7Combination Tuple6Combination; A, A, A, A, A, A, A, A; a b c d e f);
-impl_tuple_combination!(Tuple8Combination Tuple7Combination; A, A, A, A, A, A, A, A, A; a b c d e f g);
-impl_tuple_combination!(Tuple9Combination Tuple8Combination; A, A, A, A, A, A, A, A, A, A; a b c d e f g h);
-impl_tuple_combination!(Tuple10Combination Tuple9Combination; A, A, A, A, A, A, A, A, A, A, A; a b c d e f g h i);
-impl_tuple_combination!(Tuple11Combination Tuple10Combination; A, A, A, A, A, A, A, A, A, A, A, A; a b c d e f g h i j);
-impl_tuple_combination!(Tuple12Combination Tuple11Combination; A, A, A, A, A, A, A, A, A, A, A, A, A; a b c d e f g h i j k);
+impl_tuple_combination!(Tuple2Combination Tuple1Combination; a);
+impl_tuple_combination!(Tuple3Combination Tuple2Combination; a b);
+impl_tuple_combination!(Tuple4Combination Tuple3Combination; a b c);
+impl_tuple_combination!(Tuple5Combination Tuple4Combination; a b c d);
+impl_tuple_combination!(Tuple6Combination Tuple5Combination; a b c d e);
+impl_tuple_combination!(Tuple7Combination Tuple6Combination; a b c d e f);
+impl_tuple_combination!(Tuple8Combination Tuple7Combination; a b c d e f g);
+impl_tuple_combination!(Tuple9Combination Tuple8Combination; a b c d e f g h);
+impl_tuple_combination!(Tuple10Combination Tuple9Combination; a b c d e f g h i);
+impl_tuple_combination!(Tuple11Combination Tuple10Combination; a b c d e f g h i j);
+impl_tuple_combination!(Tuple12Combination Tuple11Combination; a b c d e f g h i j k);
 
 /// An iterator adapter to filter values within a nested `Result::Ok`.
 ///
-/// See [`.filter_ok()`](../trait.Itertools.html#method.filter_ok) for more information.
+/// See [`.filter_ok()`](crate::Itertools::filter_ok) for more information.
 #[derive(Clone)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct FilterOk<I, F> {
@@ -830,6 +849,13 @@
     f: F
 }
 
+impl<I, F> fmt::Debug for FilterOk<I, F>
+where
+    I: fmt::Debug,
+{
+    debug_fmt_fields!(FilterOk, iter);
+}
+
 /// Create a new `FilterOk` iterator.
 pub fn filter_ok<I, F, T, E>(iter: I, f: F) -> FilterOk<I, F>
     where I: Iterator<Item = Result<T, E>>,
@@ -884,15 +910,27 @@
     }
 }
 
+impl<I, F, T, E> FusedIterator for FilterOk<I, F>
+    where I: FusedIterator<Item = Result<T, E>>,
+          F: FnMut(&T) -> bool,
+{}
+
 /// An iterator adapter to filter and apply a transformation on values within a nested `Result::Ok`.
 ///
-/// See [`.filter_map_ok()`](../trait.Itertools.html#method.filter_map_ok) for more information.
+/// See [`.filter_map_ok()`](crate::Itertools::filter_map_ok) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct FilterMapOk<I, F> {
     iter: I,
     f: F
 }
 
+impl<I, F> fmt::Debug for FilterMapOk<I, F>
+where
+    I: fmt::Debug,
+{
+    debug_fmt_fields!(FilterMapOk, iter);
+}
+
 fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>> {
     match result {
         Ok(Some(v)) => Some(Ok(v)),
@@ -955,9 +993,14 @@
     }
 }
 
+impl<I, F, T, U, E> FusedIterator for FilterMapOk<I, F>
+    where I: FusedIterator<Item = Result<T, E>>,
+          F: FnMut(T) -> Option<U>,
+{}
+
 /// An iterator adapter to get the positions of each element that matches a predicate.
 ///
-/// See [`.positions()`](../trait.Itertools.html#method.positions) for more information.
+/// See [`.positions()`](crate::Itertools::positions) for more information.
 #[derive(Clone)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Positions<I, F> {
@@ -966,6 +1009,13 @@
     count: usize,
 }
 
+impl<I, F> fmt::Debug for Positions<I, F>
+where
+    I: fmt::Debug,
+{
+    debug_fmt_fields!(Positions, iter, count);
+}
+
 /// Create a new `Positions` iterator.
 pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F>
     where I: Iterator,
@@ -1014,9 +1064,14 @@
     }
 }
 
+impl<I, F> FusedIterator for Positions<I, F>
+    where I: FusedIterator,
+          F: FnMut(I::Item) -> bool,
+{}
+
 /// An iterator adapter to apply a mutating function to each element before yielding it.
 ///
-/// See [`.update()`](../trait.Itertools.html#method.update) for more information.
+/// See [`.update()`](crate::Itertools::update) for more information.
 #[derive(Clone)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Update<I, F> {
@@ -1024,6 +1079,13 @@
     f: F,
 }
 
+impl<I, F> fmt::Debug for Update<I, F>
+where
+    I: fmt::Debug,
+{
+    debug_fmt_fields!(Update, iter);
+}
+
 /// Create a new `Update` iterator.
 pub fn update<I, F>(iter: I, f: F) -> Update<I, F>
 where
@@ -1089,3 +1151,9 @@
         }
     }
 }
+
+impl<I, F> FusedIterator for Update<I, F>
+where
+    I: FusedIterator,
+    F: FnMut(&mut I::Item),
+{}
diff --git a/src/adaptors/multi_product.rs b/src/adaptors/multi_product.rs
index 6938014..30650ed 100644
--- a/src/adaptors/multi_product.rs
+++ b/src/adaptors/multi_product.rs
@@ -11,13 +11,21 @@
 ///
 /// An iterator element type is `Vec<I>`.
 ///
-/// See [`.multi_cartesian_product()`](../trait.Itertools.html#method.multi_cartesian_product)
+/// See [`.multi_cartesian_product()`](crate::Itertools::multi_cartesian_product)
 /// for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
     where I: Iterator + Clone,
           I::Item: Clone;
 
+impl<I> std::fmt::Debug for MultiProduct<I>
+where
+    I: Iterator + Clone + std::fmt::Debug,
+    I::Item: Clone + std::fmt::Debug,
+{
+    debug_fmt_fields!(CoalesceBy, 0);
+}
+
 /// Create a new cartesian product iterator over an arbitrary number
 /// of iterators of the same type.
 ///
diff --git a/src/combinations.rs b/src/combinations.rs
index e6ba4ac..68a59c5 100644
--- a/src/combinations.rs
+++ b/src/combinations.rs
@@ -1,11 +1,12 @@
 use std::fmt;
+use std::iter::FusedIterator;
 
 use super::lazy_buffer::LazyBuffer;
 use alloc::vec::Vec;
 
 /// An iterator to iterate through all the `k`-length combinations in an iterator.
 ///
-/// See [`.combinations()`](../trait.Itertools.html#method.combinations) for more information.
+/// See [`.combinations()`](crate::Itertools::combinations) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Combinations<I: Iterator> {
     indices: Vec<usize>,
@@ -47,9 +48,7 @@
     pub fn k(&self) -> usize { self.indices.len() }
 
     /// Returns the (current) length of the pool from which combination elements are
-    /// selected. This value can change between invocations of [`next`].
-    ///
-    /// [`next`]: #method.next
+    /// selected. This value can change between invocations of [`next`](Combinations::next).
     #[inline]
     pub fn n(&self) -> usize { self.pool.len() }
 
@@ -122,3 +121,8 @@
         Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect())
     }
 }
+
+impl<I> FusedIterator for Combinations<I>
+    where I: Iterator,
+          I::Item: Clone
+{}
diff --git a/src/combinations_with_replacement.rs b/src/combinations_with_replacement.rs
index 7e7a802..81b13f1 100644
--- a/src/combinations_with_replacement.rs
+++ b/src/combinations_with_replacement.rs
@@ -1,11 +1,13 @@
 use alloc::vec::Vec;
 use std::fmt;
+use std::iter::FusedIterator;
 
 use super::lazy_buffer::LazyBuffer;
 
 /// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
 ///
-/// See [`.combinations_with_replacement()`](../trait.Itertools.html#method.combinations_with_replacement) for more information.
+/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
+/// for more information.
 #[derive(Clone)]
 pub struct CombinationsWithReplacement<I>
 where
@@ -99,3 +101,9 @@
         }
     }
 }
+
+impl<I> FusedIterator for CombinationsWithReplacement<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{}
diff --git a/src/concat_impl.rs b/src/concat_impl.rs
index 0233d01..450f7fc 100644
--- a/src/concat_impl.rs
+++ b/src/concat_impl.rs
@@ -1,8 +1,8 @@
 use crate::Itertools;
 
-/// Combine all an iterator's elements into one element by using `Extend`.
+/// Combine all an iterator's elements into one element by using [`Extend`].
 ///
-/// `IntoIterator`-enabled version of `.concat()`
+/// [`IntoIterator`]-enabled version of [`Itertools::concat`].
 ///
 /// This combinator will extend the first item with each of the rest of the
 /// items of the iterator. If the iterator is empty, the default value of
diff --git a/src/diff.rs b/src/diff.rs
index c196d8d..1731f06 100644
--- a/src/diff.rs
+++ b/src/diff.rs
@@ -1,14 +1,14 @@
 //! "Diff"ing iterators for caching elements to sequential collections without requiring the new
 //! elements' iterator to be `Clone`.
 //!
-//! - [**Diff**](./enum.Diff.html) (produced by the [**diff_with**](./fn.diff_with.html) function)
+//! - [`Diff`] (produced by the [`diff_with`] function)
 //! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from
 //! a lock-step comparison.
 
 use crate::free::put_back;
 use crate::structs::PutBack;
 
-/// A type returned by the [`diff_with`](./fn.diff_with.html) function.
+/// A type returned by the [`diff_with`] function.
 ///
 /// `Diff` represents the way in which the elements yielded by the iterator `I` differ to some
 /// iterator `J`.
@@ -26,7 +26,7 @@
 }
 
 /// Compares every element yielded by both `i` and `j` with the given function in lock-step and
-/// returns a `Diff` which describes how `j` differs from `i`.
+/// returns a [`Diff`] which describes how `j` differs from `i`.
 ///
 /// If the number of elements yielded by `j` is less than the number of elements yielded by `i`,
 /// the number of `j` elements yielded will be returned along with `i`'s remaining elements as
diff --git a/src/duplicates_impl.rs b/src/duplicates_impl.rs
new file mode 100644
index 0000000..640d481
--- /dev/null
+++ b/src/duplicates_impl.rs
@@ -0,0 +1,217 @@
+use std::hash::Hash;
+
+mod private {
+    use std::collections::HashMap;
+    use std::hash::Hash;
+    use std::fmt;
+
+    #[derive(Clone)]
+    #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+    pub struct DuplicatesBy<I: Iterator, Key, F> {
+        pub(crate) iter: I,
+        pub(crate) meta: Meta<Key, F>,
+    }
+
+    impl<I, V, F> fmt::Debug for DuplicatesBy<I, V, F>
+    where
+        I: Iterator + fmt::Debug,
+        V: fmt::Debug + Hash + Eq,
+    {
+        debug_fmt_fields!(DuplicatesBy, iter, meta.used);
+    }
+
+    impl<I: Iterator, Key: Eq + Hash, F> DuplicatesBy<I, Key, F> {
+        pub(crate) fn new(iter: I, key_method: F) -> Self {
+            DuplicatesBy {
+                iter,
+                meta: Meta {
+                    used: HashMap::new(),
+                    pending: 0,
+                    key_method,
+                },
+            }
+        }
+    }
+
+    #[derive(Clone)]
+    pub struct Meta<Key, F> {
+        used: HashMap<Key, bool>,
+        pending: usize,
+        key_method: F,
+    }
+
+    impl<Key, F> Meta<Key, F>
+    where
+        Key: Eq + Hash,
+    {
+        /// Takes an item and returns it back to the caller if it's the second time we see it.
+        /// Otherwise the item is consumed and None is returned
+        #[inline(always)]
+        fn filter<I>(&mut self, item: I) -> Option<I>
+        where
+            F: KeyMethod<Key, I>,
+        {
+            let kv = self.key_method.make(item);
+            match self.used.get_mut(kv.key_ref()) {
+                None => {
+                    self.used.insert(kv.key(), false);
+                    self.pending += 1;
+                    None
+                }
+                Some(true) => None,
+                Some(produced) => {
+                    *produced = true;
+                    self.pending -= 1;
+                    Some(kv.value())
+                }
+            }
+        }
+    }
+
+    impl<I, Key, F> Iterator for DuplicatesBy<I, Key, F>
+    where
+        I: Iterator,
+        Key: Eq + Hash,
+        F: KeyMethod<Key, I::Item>,
+    {
+        type Item = I::Item;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            let DuplicatesBy { iter, meta } = self;
+            iter.find_map(|v| meta.filter(v))
+        }
+
+        #[inline]
+        fn size_hint(&self) -> (usize, Option<usize>) {
+            let (_, hi) = self.iter.size_hint();
+            let hi = hi.map(|hi| {
+                if hi <= self.meta.pending {
+                    // fewer or equally many iter-remaining elements than pending elements
+                    // => at most, each iter-remaining element is matched
+                    hi
+                } else {
+                    // fewer pending elements than iter-remaining elements
+                    // => at most:
+                    //    * each pending element is matched
+                    //    * the other iter-remaining elements come in pairs
+                    self.meta.pending + (hi - self.meta.pending) / 2
+                }
+            });
+            // The lower bound is always 0 since we might only get unique items from now on
+            (0, hi)
+        }
+    }
+
+    impl<I, Key, F> DoubleEndedIterator for DuplicatesBy<I, Key, F>
+    where
+        I: DoubleEndedIterator,
+        Key: Eq + Hash,
+        F: KeyMethod<Key, I::Item>,
+    {
+        fn next_back(&mut self) -> Option<Self::Item> {
+            let DuplicatesBy { iter, meta } = self;
+            iter.rev().find_map(|v| meta.filter(v))
+        }
+    }
+
+    /// A keying method for use with `DuplicatesBy`
+    pub trait KeyMethod<K, V> {
+        type Container: KeyXorValue<K, V>;
+
+        fn make(&mut self, value: V) -> Self::Container;
+    }
+
+    /// Apply the identity function to elements before checking them for equality.
+    #[derive(Debug)]
+    pub struct ById;
+    impl<V> KeyMethod<V, V> for ById {
+        type Container = JustValue<V>;
+
+        fn make(&mut self, v: V) -> Self::Container {
+            JustValue(v)
+        }
+    }
+
+    /// Apply a user-supplied function to elements before checking them for equality.
+    pub struct ByFn<F>(pub(crate) F);
+    impl<F> fmt::Debug for ByFn<F> {
+        debug_fmt_fields!(ByFn,);
+    }
+    impl<K, V, F> KeyMethod<K, V> for ByFn<F>
+    where
+        F: FnMut(&V) -> K,
+    {
+        type Container = KeyValue<K, V>;
+
+        fn make(&mut self, v: V) -> Self::Container {
+            KeyValue((self.0)(&v), v)
+        }
+    }
+
+    // Implementors of this trait can hold onto a key and a value but only give access to one of them
+    // at a time. This allows the key and the value to be the same value internally
+    pub trait KeyXorValue<K, V> {
+        fn key_ref(&self) -> &K;
+        fn key(self) -> K;
+        fn value(self) -> V;
+    }
+
+    #[derive(Debug)]
+    pub struct KeyValue<K, V>(K, V);
+    impl<K, V> KeyXorValue<K, V> for KeyValue<K, V> {
+        fn key_ref(&self) -> &K {
+            &self.0
+        }
+        fn key(self) -> K {
+            self.0
+        }
+        fn value(self) -> V {
+            self.1
+        }
+    }
+
+    #[derive(Debug)]
+    pub struct JustValue<V>(V);
+    impl<V> KeyXorValue<V, V> for JustValue<V> {
+        fn key_ref(&self) -> &V {
+            &self.0
+        }
+        fn key(self) -> V {
+            self.0
+        }
+        fn value(self) -> V {
+            self.0
+        }
+    }
+}
+
+/// An iterator adapter to filter for duplicate elements.
+///
+/// See [`.duplicates_by()`](crate::Itertools::duplicates_by) for more information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub type DuplicatesBy<I, V, F> = private::DuplicatesBy<I, V, private::ByFn<F>>;
+
+/// Create a new `DuplicatesBy` iterator.
+pub fn duplicates_by<I, Key, F>(iter: I, f: F) -> DuplicatesBy<I, Key, F>
+where
+    Key: Eq + Hash,
+    F: FnMut(&I::Item) -> Key,
+    I: Iterator,
+{
+    DuplicatesBy::new(iter, private::ByFn(f))
+}
+
+/// An iterator adapter to filter out duplicate elements.
+///
+/// See [`.duplicates()`](crate::Itertools::duplicates) for more information.
+pub type Duplicates<I> = private::DuplicatesBy<I, <I as Iterator>::Item, private::ById>;
+
+/// Create a new `Duplicates` iterator.
+pub fn duplicates<I>(iter: I) -> Duplicates<I>
+where
+    I: Iterator,
+    I::Item: Eq + Hash,
+{
+    Duplicates::new(iter, private::ById)
+}
+
diff --git a/src/either_or_both.rs b/src/either_or_both.rs
index a03a4f1..28d1df7 100644
--- a/src/either_or_both.rs
+++ b/src/either_or_both.rs
@@ -25,7 +25,7 @@
     }
 
     /// If Left, return true otherwise, return false.
-    /// Exclusive version of [`has_left`].
+    /// Exclusive version of [`has_left`](EitherOrBoth::has_left).
     pub fn is_left(&self) -> bool {
         match *self {
             Left(_) => true,
@@ -34,7 +34,7 @@
     }
 
     /// If Right, return true otherwise, return false.
-    /// Exclusive version of [`has_right`].
+    /// Exclusive version of [`has_right`](EitherOrBoth::has_right).
     pub fn is_right(&self) -> bool {
         match *self {
             Right(_) => true,
@@ -140,7 +140,7 @@
         }
     }
 
-    /// Apply the function `f` on the value `b` in `Right(b)` or `Both(a, _)` variants if it is
+    /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, _)` variants if it is
     /// present.
     pub fn left_and_then<F, L>(self, f: F) -> EitherOrBoth<L, B>
     where
@@ -152,8 +152,8 @@
         }
     }
 
-    /// Apply the function `f` on the value `a`
-    /// in `Left(a)` or `Both(a, _)` variants if it is present.
+    /// Apply the function `f` on the value `b`
+    /// in `Right(b)` or `Both(_, b)` variants if it is present.
     pub fn right_and_then<F, R>(self, f: F) -> EitherOrBoth<A, R>
     where
         F: FnOnce(B) -> EitherOrBoth<A, R>,
@@ -163,6 +163,21 @@
             Right(b) | Both(_, b) => f(b),
         }
     }
+
+    /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
+    /// Otherwise, returns the wrapped value for the present element, and the [`default`](Default::default)
+    /// for the other.
+    pub fn or_default(self) -> (A, B)
+    where
+        A: Default,
+        B: Default,
+    {
+        match self {
+            EitherOrBoth::Left(l) => (l, B::default()),
+            EitherOrBoth::Right(r) => (A::default(), r),
+            EitherOrBoth::Both(l, r) => (l, r),
+        }
+    }
 }
 
 impl<T> EitherOrBoth<T, T> {
diff --git a/src/flatten_ok.rs b/src/flatten_ok.rs
new file mode 100644
index 0000000..d46bbde
--- /dev/null
+++ b/src/flatten_ok.rs
@@ -0,0 +1,166 @@
+use crate::size_hint;
+use std::{
+    fmt,
+    iter::{DoubleEndedIterator, FusedIterator},
+};
+
+pub fn flatten_ok<I, T, E>(iter: I) -> FlattenOk<I, T, E>
+where
+    I: Iterator<Item = Result<T, E>>,
+    T: IntoIterator,
+{
+    FlattenOk {
+        iter,
+        inner_front: None,
+        inner_back: None,
+    }
+}
+
+/// An iterator adaptor that flattens `Result::Ok` values and
+/// allows `Result::Err` values through unchanged.
+///
+/// See [`.flatten_ok()`](crate::Itertools::flatten_ok) for more information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct FlattenOk<I, T, E>
+where
+    I: Iterator<Item = Result<T, E>>,
+    T: IntoIterator,
+{
+    iter: I,
+    inner_front: Option<T::IntoIter>,
+    inner_back: Option<T::IntoIter>,
+}
+
+impl<I, T, E> Iterator for FlattenOk<I, T, E>
+where
+    I: Iterator<Item = Result<T, E>>,
+    T: IntoIterator,
+{
+    type Item = Result<T::Item, E>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        loop {
+            // Handle the front inner iterator.
+            if let Some(inner) = &mut self.inner_front {
+                if let Some(item) = inner.next() {
+                    return Some(Ok(item));
+                } else {
+                    // This is necessary for the iterator to implement `FusedIterator`
+                    // with only the orginal iterator being fused.
+                    self.inner_front = None;
+                }
+            }
+
+            match self.iter.next() {
+                Some(Ok(ok)) => self.inner_front = Some(ok.into_iter()),
+                Some(Err(e)) => return Some(Err(e)),
+                None => {
+                    // Handle the back inner iterator.
+                    if let Some(inner) = &mut self.inner_back {
+                        if let Some(item) = inner.next() {
+                            return Some(Ok(item));
+                        } else {
+                            // This is necessary for the iterator to implement `FusedIterator`
+                            // with only the orginal iterator being fused.
+                            self.inner_back = None;
+                        }
+                    } else {
+                        return None;
+                    }
+                }
+            }
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let inner_hint = |inner: &Option<T::IntoIter>| {
+            inner
+                .as_ref()
+                .map(Iterator::size_hint)
+                .unwrap_or((0, Some(0)))
+        };
+        let inner_front = inner_hint(&self.inner_front);
+        let inner_back = inner_hint(&self.inner_back);
+        // The outer iterator `Ok` case could be (0, None) as we don't know its size_hint yet.
+        let outer = match self.iter.size_hint() {
+            (0, Some(0)) => (0, Some(0)),
+            _ => (0, None),
+        };
+
+        size_hint::add(size_hint::add(inner_front, inner_back), outer)
+    }
+}
+
+impl<I, T, E> DoubleEndedIterator for FlattenOk<I, T, E>
+where
+    I: DoubleEndedIterator<Item = Result<T, E>>,
+    T: IntoIterator,
+    T::IntoIter: DoubleEndedIterator,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        loop {
+            // Handle the back inner iterator.
+            if let Some(inner) = &mut self.inner_back {
+                if let Some(item) = inner.next_back() {
+                    return Some(Ok(item));
+                } else {
+                    // This is necessary for the iterator to implement `FusedIterator`
+                    // with only the orginal iterator being fused.
+                    self.inner_back = None;
+                }
+            }
+
+            match self.iter.next_back() {
+                Some(Ok(ok)) => self.inner_back = Some(ok.into_iter()),
+                Some(Err(e)) => return Some(Err(e)),
+                None => {
+                    // Handle the front inner iterator.
+                    if let Some(inner) = &mut self.inner_front {
+                        if let Some(item) = inner.next_back() {
+                            return Some(Ok(item));
+                        } else {
+                            // This is necessary for the iterator to implement `FusedIterator`
+                            // with only the orginal iterator being fused.
+                            self.inner_front = None;
+                        }
+                    } else {
+                        return None;
+                    }
+                }
+            }
+        }
+    }
+}
+
+impl<I, T, E> Clone for FlattenOk<I, T, E>
+where
+    I: Iterator<Item = Result<T, E>> + Clone,
+    T: IntoIterator,
+    T::IntoIter: Clone,
+{
+    #[inline]
+    clone_fields!(iter, inner_front, inner_back);
+}
+
+impl<I, T, E> fmt::Debug for FlattenOk<I, T, E>
+where
+    I: Iterator<Item = Result<T, E>> + fmt::Debug,
+    T: IntoIterator,
+    T::IntoIter: fmt::Debug,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("FlattenOk")
+            .field("iter", &self.iter)
+            .field("inner_front", &self.inner_front)
+            .field("inner_back", &self.inner_back)
+            .finish()
+    }
+}
+
+/// Only the iterator being flattened needs to implement [`FusedIterator`].
+impl<I, T, E> FusedIterator for FlattenOk<I, T, E>
+where
+    I: FusedIterator<Item = Result<T, E>>,
+    T: IntoIterator,
+{
+}
diff --git a/src/format.rs b/src/format.rs
index ec2dc7a..d87cee9 100644
--- a/src/format.rs
+++ b/src/format.rs
@@ -6,7 +6,7 @@
 /// The format value can only be formatted once, after that the iterator is
 /// exhausted.
 ///
-/// See [`.format_with()`](../trait.Itertools.html#method.format_with) for more information.
+/// See [`.format_with()`](crate::Itertools::format_with) for more information.
 #[derive(Clone)]
 pub struct FormatWith<'a, I, F> {
     sep: &'a str,
@@ -19,7 +19,7 @@
 /// The format value can only be formatted once, after that the iterator is
 /// exhausted.
 ///
-/// See [`.format()`](../trait.Itertools.html#method.format)
+/// See [`.format()`](crate::Itertools::format)
 /// for more information.
 #[derive(Clone)]
 pub struct Format<'a, I> {
diff --git a/src/free.rs b/src/free.rs
index bfc5ab4..6674030 100644
--- a/src/free.rs
+++ b/src/free.rs
@@ -1,6 +1,6 @@
 //! Free functions that create iterator adaptors or call iterator methods.
 //!
-//! The benefit of free functions is that they accept any `IntoIterator` as
+//! The benefit of free functions is that they accept any [`IntoIterator`] as
 //! argument, so the resulting code may be easier to read.
 
 #[cfg(feature = "use_alloc")]
@@ -14,8 +14,8 @@
     string::String,
 };
 
-#[cfg(feature = "use_alloc")]
 use crate::Itertools;
+use crate::intersperse::{Intersperse, IntersperseWith};
 
 pub use crate::adaptors::{
     interleave,
@@ -35,9 +35,44 @@
 #[cfg(feature = "use_alloc")]
 pub use crate::rciter_impl::rciter;
 
+/// Iterate `iterable` with a particular value inserted between each element.
+///
+/// [`IntoIterator`] enabled version of [`Iterator::intersperse`].
+///
+/// ```
+/// use itertools::intersperse;
+///
+/// itertools::assert_equal(intersperse((0..3), 8), vec![0, 8, 1, 8, 2]);
+/// ```
+pub fn intersperse<I>(iterable: I, element: I::Item) -> Intersperse<I::IntoIter>
+    where I: IntoIterator,
+          <I as IntoIterator>::Item: Clone
+{
+    Itertools::intersperse(iterable.into_iter(), element)
+}
+
+/// Iterate `iterable` with a particular value created by a function inserted
+/// between each element.
+///
+/// [`IntoIterator`] enabled version of [`Iterator::intersperse_with`].
+///
+/// ```
+/// use itertools::intersperse_with;
+///
+/// let mut i = 10;
+/// itertools::assert_equal(intersperse_with((0..3), || { i -= 1; i }), vec![0, 9, 1, 8, 2]);
+/// assert_eq!(i, 8);
+/// ```
+pub fn intersperse_with<I, F>(iterable: I, element: F) -> IntersperseWith<I::IntoIter, F>
+    where I: IntoIterator,
+          F: FnMut() -> I::Item
+{
+    Itertools::intersperse_with(iterable.into_iter(), element)
+}
+
 /// Iterate `iterable` with a running index.
 ///
-/// `IntoIterator` enabled version of `.enumerate()`.
+/// [`IntoIterator`] enabled version of [`Iterator::enumerate`].
 ///
 /// ```
 /// use itertools::enumerate;
@@ -54,7 +89,7 @@
 
 /// Iterate `iterable` in reverse.
 ///
-/// `IntoIterator` enabled version of `.rev()`.
+/// [`IntoIterator`] enabled version of [`Iterator::rev`].
 ///
 /// ```
 /// use itertools::rev;
@@ -72,7 +107,7 @@
 
 /// Iterate `i` and `j` in lock step.
 ///
-/// `IntoIterator` enabled version of `i.zip(j)`.
+/// [`IntoIterator`] enabled version of [`Iterator::zip`].
 ///
 /// ```
 /// use itertools::zip;
@@ -91,7 +126,7 @@
 
 /// Create an iterator that first iterates `i` and then `j`.
 ///
-/// `IntoIterator` enabled version of `i.chain(j)`.
+/// [`IntoIterator`] enabled version of [`Iterator::chain`].
 ///
 /// ```
 /// use itertools::chain;
@@ -109,7 +144,7 @@
 
 /// Create an iterator that clones each element from &T to T
 ///
-/// `IntoIterator` enabled version of `i.cloned()`.
+/// [`IntoIterator`] enabled version of [`Iterator::cloned`].
 ///
 /// ```
 /// use itertools::cloned;
@@ -125,7 +160,7 @@
 
 /// Perform a fold operation over the iterable.
 ///
-/// `IntoIterator` enabled version of `i.fold(init, f)`
+/// [`IntoIterator`] enabled version of [`Iterator::fold`].
 ///
 /// ```
 /// use itertools::fold;
@@ -141,7 +176,7 @@
 
 /// Test whether the predicate holds for all elements in the iterable.
 ///
-/// `IntoIterator` enabled version of `i.all(f)`
+/// [`IntoIterator`] enabled version of [`Iterator::all`].
 ///
 /// ```
 /// use itertools::all;
@@ -157,7 +192,7 @@
 
 /// Test whether the predicate holds for any elements in the iterable.
 ///
-/// `IntoIterator` enabled version of `i.any(f)`
+/// [`IntoIterator`] enabled version of [`Iterator::any`].
 ///
 /// ```
 /// use itertools::any;
@@ -173,7 +208,7 @@
 
 /// Return the maximum value of the iterable.
 ///
-/// `IntoIterator` enabled version of `i.max()`.
+/// [`IntoIterator`] enabled version of [`Iterator::max`].
 ///
 /// ```
 /// use itertools::max;
@@ -189,7 +224,7 @@
 
 /// Return the minimum value of the iterable.
 ///
-/// `IntoIterator` enabled version of `i.min()`.
+/// [`IntoIterator`] enabled version of [`Iterator::min`].
 ///
 /// ```
 /// use itertools::min;
@@ -206,7 +241,7 @@
 
 /// Combine all iterator elements into one String, seperated by `sep`.
 ///
-/// `IntoIterator` enabled version of `iterable.join(sep)`.
+/// [`IntoIterator`] enabled version of [`Itertools::join`].
 ///
 /// ```
 /// use itertools::join;
@@ -223,9 +258,7 @@
 
 /// Sort all iterator elements into a new iterator in ascending order.
 ///
-/// `IntoIterator` enabled version of [`iterable.sorted()`][1].
-///
-/// [1]: trait.Itertools.html#method.sorted
+/// [`IntoIterator`] enabled version of [`Itertools::sorted`].
 ///
 /// ```
 /// use itertools::sorted;
diff --git a/src/group_map.rs b/src/group_map.rs
index 4231de0..a2d0ebb 100644
--- a/src/group_map.rs
+++ b/src/group_map.rs
@@ -6,7 +6,7 @@
 
 /// Return a `HashMap` of keys mapped to a list of their corresponding values.
 ///
-/// See [`.into_group_map()`](../trait.Itertools.html#method.into_group_map)
+/// See [`.into_group_map()`](crate::Itertools::into_group_map)
 /// for more information.
 pub fn into_group_map<I, K, V>(iter: I) -> HashMap<K, Vec<V>>
     where I: Iterator<Item=(K, V)>,
diff --git a/src/groupbylazy.rs b/src/groupbylazy.rs
index 5d4a0c9..91c52ea 100644
--- a/src/groupbylazy.rs
+++ b/src/groupbylazy.rs
@@ -279,12 +279,12 @@
 /// no allocations. It needs allocations only if several group iterators
 /// are alive at the same time.
 ///
-/// This type implements `IntoIterator` (it is **not** an iterator
+/// This type implements [`IntoIterator`] (it is **not** an iterator
 /// itself), because the group iterators need to borrow from this
 /// value. It should be stored in a local variable or temporary and
 /// iterated.
 ///
-/// See [`.group_by()`](../trait.Itertools.html#method.group_by) for more information.
+/// See [`.group_by()`](crate::Itertools::group_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct GroupBy<K, I, F>
     where I: Iterator,
@@ -354,7 +354,7 @@
 /// Iterator element type is `(K, Group)`:
 /// the group's key `K` and the group's iterator.
 ///
-/// See [`.group_by()`](../trait.Itertools.html#method.group_by) for more information.
+/// See [`.group_by()`](crate::Itertools::group_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Groups<'a, K: 'a, I: 'a, F: 'a>
     where I: Iterator,
@@ -453,14 +453,14 @@
 /// `IntoChunks` behaves just like `GroupBy`: it is iterable, and
 /// it only buffers if several chunk iterators are alive at the same time.
 ///
-/// This type implements `IntoIterator` (it is **not** an iterator
+/// This type implements [`IntoIterator`] (it is **not** an iterator
 /// itself), because the chunk iterators need to borrow from this
 /// value. It should be stored in a local variable or temporary and
 /// iterated.
 ///
 /// Iterator element type is `Chunk`, each chunk's iterator.
 ///
-/// See [`.chunks()`](../trait.Itertools.html#method.chunks) for more information.
+/// See [`.chunks()`](crate::Itertools::chunks) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct IntoChunks<I>
     where I: Iterator,
@@ -505,7 +505,7 @@
 ///
 /// Iterator element type is `Chunk`.
 ///
-/// See [`.chunks()`](../trait.Itertools.html#method.chunks) for more information.
+/// See [`.chunks()`](crate::Itertools::chunks) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Chunks<'a, I: 'a>
     where I: Iterator,
diff --git a/src/grouping_map.rs b/src/grouping_map.rs
index 5232f5d..be22ec8 100644
--- a/src/grouping_map.rs
+++ b/src/grouping_map.rs
@@ -7,7 +7,7 @@
 use std::iter::Iterator;
 use std::ops::{Add, Mul};
 
-/// A wrapper to allow for an easy [`into_grouping_map_by`](../trait.Itertools.html#method.into_grouping_map_by)
+/// A wrapper to allow for an easy [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by)
 #[derive(Clone, Debug)]
 pub struct MapForGrouping<I, F>(I, F);
 
@@ -38,7 +38,7 @@
 
 /// `GroupingMapBy` is an intermediate struct for efficient group-and-fold operations.
 /// 
-/// See [`GroupingMap`](./struct.GroupingMap.html) for more informations.
+/// See [`GroupingMap`] for more informations.
 #[must_use = "GroupingMapBy is lazy and do nothing unless consumed"]
 pub type GroupingMapBy<I, F> = GroupingMap<MapForGrouping<I, F>>;
 
@@ -102,12 +102,12 @@
     {
         let mut destination_map = HashMap::new();
 
-        for (key, val) in self.iter {
+        self.iter.for_each(|(key, val)| {
             let acc = destination_map.remove(&key);
             if let Some(op_res) = operation(acc, &key, val) {
                 destination_map.insert(key, op_res);
             }
-        }
+        });
 
         destination_map
     }
@@ -160,7 +160,7 @@
     ///
     /// Return a `HashMap` associating the key of each group with the result of folding that group's elements.
     /// 
-    /// [`fold`]: #tymethod.fold
+    /// [`fold`]: GroupingMap::fold
     /// 
     /// ```
     /// use itertools::Itertools;
@@ -208,9 +208,9 @@
     {
         let mut destination_map = HashMap::new();
 
-        for (key, val) in self.iter {
+        self.iter.for_each(|(key, val)| {
             destination_map.entry(key).or_insert_with(C::default).extend(Some(val));
-        }
+        });
 
         destination_map
     }
@@ -377,7 +377,7 @@
     /// If several elements are equally maximum, the last element is picked.
     /// If several elements are equally minimum, the first element is picked.
     /// 
-    /// See [.minmax()](../trait.Itertools.html#method.minmax) for the non-grouping version.
+    /// See [.minmax()](crate::Itertools::minmax) for the non-grouping version.
     /// 
     /// Differences from the non grouping version:
     /// - It never produces a `MinMaxResult::NoElements`
diff --git a/src/impl_macros.rs b/src/impl_macros.rs
index 04ab8e1..5772bae 100644
--- a/src/impl_macros.rs
+++ b/src/impl_macros.rs
@@ -2,7 +2,7 @@
 //! Implementation's internal macros
 
 macro_rules! debug_fmt_fields {
-    ($tyname:ident, $($($field:ident).+),*) => {
+    ($tyname:ident, $($($field:tt/*TODO ideally we would accept ident or tuple element here*/).+),*) => {
         fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
             f.debug_struct(stringify!($tyname))
                 $(
@@ -22,3 +22,7 @@
         }
     }
 }
+
+macro_rules! ignore_ident{
+    ($id:ident, $($t:tt)*) => {$($t)*};
+}
diff --git a/src/intersperse.rs b/src/intersperse.rs
index a0d79b0..2c660d4 100644
--- a/src/intersperse.rs
+++ b/src/intersperse.rs
@@ -1,4 +1,4 @@
-use std::iter::Fuse;
+use std::iter::{Fuse, FusedIterator};
 use super::size_hint;
 
 pub trait IntersperseElement<Item> {
@@ -21,7 +21,7 @@
 ///
 /// This iterator is *fused*.
 ///
-/// See [`.intersperse()`](../trait.Itertools.html#method.intersperse) for more information.
+/// See [`.intersperse()`](crate::Itertools::intersperse) for more information.
 pub type Intersperse<I> = IntersperseWith<I, IntersperseElementSimple<<I as Iterator>::Item>>;
 
 /// Create a new Intersperse iterator
@@ -44,7 +44,7 @@
 ///
 /// This iterator is *fused*.
 ///
-/// See [`.intersperse_with()`](../trait.Itertools.html#method.intersperse_with) for more information.
+/// See [`.intersperse_with()`](crate::Itertools::intersperse_with) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Clone, Debug)]
 pub struct IntersperseWith<I, ElemF>
@@ -112,3 +112,8 @@
         })
     }
 }
+
+impl<I, ElemF> FusedIterator for IntersperseWith<I, ElemF>
+    where I: Iterator,
+          ElemF: IntersperseElement<I::Item>
+{}
diff --git a/src/k_smallest.rs b/src/k_smallest.rs
index d58ec70..acaea59 100644
--- a/src/k_smallest.rs
+++ b/src/k_smallest.rs
@@ -6,7 +6,7 @@
 
     let mut heap = iter.by_ref().take(k).collect::<BinaryHeap<_>>();
 
-    for i in iter {
+    iter.for_each(|i| {
         debug_assert_eq!(heap.len(), k);
         // Equivalent to heap.push(min(i, heap.pop())) but more efficient.
         // This should be done with a single `.peek_mut().unwrap()` but
@@ -14,7 +14,7 @@
         if *heap.peek().unwrap() > i {
             *heap.peek_mut().unwrap() = i;
         }
-    }
+    });
 
     heap
 }
diff --git a/src/kmerge_impl.rs b/src/kmerge_impl.rs
index a1b3d8e..bd56b03 100644
--- a/src/kmerge_impl.rs
+++ b/src/kmerge_impl.rs
@@ -2,6 +2,7 @@
 use crate::Itertools;
 
 use alloc::vec::Vec;
+use std::iter::FusedIterator;
 use std::mem::replace;
 use std::fmt;
 
@@ -74,14 +75,13 @@
     debug_assert!(index <= heap.len());
     let mut pos = index;
     let mut child = 2 * pos + 1;
-    // the `pos` conditional is to avoid a bounds check
-    while pos < heap.len() && child < heap.len() {
-        let right = child + 1;
-
+    // Require the right child to be present
+    // This allows to find the index of the smallest child without a branch
+    // that wouldn't be predicted if present
+    while child + 1 < heap.len() {
         // pick the smaller of the two children
-        if right < heap.len() && less_than(&heap[right], &heap[child]) {
-            child = right;
-        }
+        // use aritmethic to avoid an unpredictable branch
+        child += less_than(&heap[child+1], &heap[child]) as usize;
 
         // sift down is done if we are already in order
         if !less_than(&heap[child], &heap[pos]) {
@@ -91,6 +91,11 @@
         pos = child;
         child = 2 * pos + 1;
     }
+    // Check if the last (left) child was an only child
+    // if it is then it has to be compared with the parent
+    if child + 1 == heap.len() && less_than(&heap[child], &heap[pos]) {
+        heap.swap(pos, child);
+    }
 }
 
 /// An iterator adaptor that merges an abitrary number of base iterators in ascending order.
@@ -98,7 +103,7 @@
 ///
 /// Iterator element type is `I::Item`.
 ///
-/// See [`.kmerge()`](../trait.Itertools.html#method.kmerge) for more information.
+/// See [`.kmerge()`](crate::Itertools::kmerge) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub type KMerge<I> = KMergeBy<I, KMergeByLt>;
 
@@ -106,7 +111,7 @@
     fn kmerge_pred(&mut self, a: &T, b: &T) -> bool;
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct KMergeByLt;
 
 impl<T: PartialOrd> KMergePredicate<T> for KMergeByLt {
@@ -146,7 +151,7 @@
 ///
 /// Iterator element type is `I::Item`.
 ///
-/// See [`.kmerge_by()`](../trait.Itertools.html#method.kmerge_by) for more
+/// See [`.kmerge_by()`](crate::Itertools::kmerge_by) for more
 /// information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct KMergeBy<I, F>
@@ -215,3 +220,8 @@
                  .unwrap_or((0, Some(0)))
     }
 }
+
+impl<I, F> FusedIterator for KMergeBy<I, F>
+    where I: Iterator,
+          F: KMergePredicate<I::Item>
+{}
diff --git a/src/lib.rs b/src/lib.rs
index 2ef7bd9..df95e19 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,13 +5,13 @@
 //! Extra iterator adaptors, functions and macros.
 //!
 //! To extend [`Iterator`] with methods in this crate, import
-//! the [`Itertools` trait](./trait.Itertools.html):
+//! the [`Itertools`] trait:
 //!
 //! ```
 //! use itertools::Itertools;
 //! ```
 //!
-//! Now, new methods like [`interleave`](./trait.Itertools.html#method.interleave)
+//! Now, new methods like [`interleave`](Itertools::interleave)
 //! are available on all iterators:
 //!
 //! ```
@@ -43,8 +43,6 @@
 //! ## Rust Version
 //!
 //! This version of itertools requires Rust 1.32 or later.
-//!
-//! [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
 #![doc(html_root_url="https://docs.rs/itertools/0.8/")]
 
 #[cfg(not(feature = "use_std"))]
@@ -61,12 +59,15 @@
 
 pub use either::Either;
 
+use core::borrow::Borrow;
 #[cfg(feature = "use_std")]
 use std::collections::HashMap;
 use std::iter::{IntoIterator, once};
 use std::cmp::Ordering;
 use std::fmt;
 #[cfg(feature = "use_std")]
+use std::collections::HashSet;
+#[cfg(feature = "use_std")]
 use std::hash::Hash;
 #[cfg(feature = "use_alloc")]
 use std::fmt::Write;
@@ -118,6 +119,7 @@
     pub use crate::cons_tuples_impl::ConsTuples;
     pub use crate::exactly_one_err::ExactlyOneError;
     pub use crate::format::{Format, FormatWith};
+    pub use crate::flatten_ok::FlattenOk;
     #[cfg(feature = "use_std")]
     pub use crate::grouping_map::{GroupingMap, GroupingMapBy};
     #[cfg(feature = "use_alloc")]
@@ -148,6 +150,8 @@
     pub use crate::tee::Tee;
     pub use crate::tuple_impl::{TupleBuffer, TupleWindows, CircularTupleWindows, Tuples};
     #[cfg(feature = "use_std")]
+    pub use crate::duplicates_impl::{Duplicates, DuplicatesBy};
+    #[cfg(feature = "use_std")]
     pub use crate::unique_impl::{Unique, UniqueBy};
     pub use crate::with_position::WithPosition;
     pub use crate::zip_eq_impl::ZipEq;
@@ -175,6 +179,7 @@
 #[allow(deprecated)]
 pub use crate::sources::{repeat_call, unfold, iterate};
 pub use crate::with_position::Position;
+pub use crate::unziptuple::{multiunzip, MultiUnzip};
 pub use crate::ziptuple::multizip;
 mod adaptors;
 mod either_or_both;
@@ -191,6 +196,7 @@
 mod combinations_with_replacement;
 mod exactly_one_err;
 mod diff;
+mod flatten_ok;
 mod format;
 #[cfg(feature = "use_std")]
 mod grouping_map;
@@ -229,7 +235,10 @@
 mod tee;
 mod tuple_impl;
 #[cfg(feature = "use_std")]
+mod duplicates_impl;
+#[cfg(feature = "use_std")]
 mod unique_impl;
+mod unziptuple;
 mod with_position;
 mod zip_eq_impl;
 mod zip_longest;
@@ -289,8 +298,6 @@
 /// Prefer this macro `izip!()` over [`multizip`] for the performance benefits
 /// of using the standard library `.zip()`.
 ///
-/// [`multizip`]: fn.multizip.html
-///
 /// ```
 /// # use itertools::izip;
 /// #
@@ -343,6 +350,69 @@
     };
 }
 
+#[macro_export]
+/// [Chain][`chain`] zero or more iterators together into one sequence.
+///
+/// The comma-separated arguments must implement [`IntoIterator`].
+/// The final argument may be followed by a trailing comma.
+///
+/// [`chain`]: Iterator::chain
+///
+/// # Examples
+///
+/// Empty invocations of `chain!` expand to an invocation of [`std::iter::empty`]:
+/// ```
+/// use std::iter;
+/// use itertools::chain;
+///
+/// let _: iter::Empty<()> = chain!();
+/// let _: iter::Empty<i8> = chain!();
+/// ```
+///
+/// Invocations of `chain!` with one argument expand to [`arg.into_iter()`](IntoIterator):
+/// ```
+/// use std::{ops::Range, slice};
+/// use itertools::chain;
+/// let _: <Range<_> as IntoIterator>::IntoIter = chain!((2..6),); // trailing comma optional!
+/// let _:     <&[_] as IntoIterator>::IntoIter = chain!(&[2, 3, 4]);
+/// ```
+///
+/// Invocations of `chain!` with multiple arguments [`.into_iter()`](IntoIterator) each
+/// argument, and then [`chain`] them together:
+/// ```
+/// use std::{iter::*, ops::Range, slice};
+/// use itertools::{assert_equal, chain};
+///
+/// // e.g., this:
+/// let with_macro:  Chain<Chain<Once<_>, Take<Repeat<_>>>, slice::Iter<_>> =
+///     chain![once(&0), repeat(&1).take(2), &[2, 3, 5],];
+///
+/// // ...is equivalant to this:
+/// let with_method: Chain<Chain<Once<_>, Take<Repeat<_>>>, slice::Iter<_>> =
+///     once(&0)
+///         .chain(repeat(&1).take(2))
+///         .chain(&[2, 3, 5]);
+///
+/// assert_equal(with_macro, with_method);
+/// ```
+macro_rules! chain {
+    () => {
+        core::iter::empty()
+    };
+    ($first:expr $(, $rest:expr )* $(,)?) => {
+        {
+            let iter = core::iter::IntoIterator::into_iter($first);
+            $(
+                let iter =
+                    core::iter::Iterator::chain(
+                        iter,
+                        core::iter::IntoIterator::into_iter($rest));
+            )*
+            iter
+        }
+    };
+}
+
 /// An [`Iterator`] blanket implementation that provides extra adaptors and
 /// methods.
 ///
@@ -350,14 +420,12 @@
 ///
 /// * *Adaptors* take an iterator and parameter as input, and return
 /// a new iterator value. These are listed first in the trait. An example
-/// of an adaptor is [`.interleave()`](#method.interleave)
+/// of an adaptor is [`.interleave()`](Itertools::interleave)
 ///
 /// * *Regular methods* are those that don't return iterators and instead
 /// return a regular value of some other kind.
-/// [`.next_tuple()`](#method.next_tuple) is an example and the first regular
+/// [`.next_tuple()`](Itertools::next_tuple) is an example and the first regular
 /// method in the list.
-///
-/// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
 pub trait Itertools : Iterator {
     // adaptors
 
@@ -456,7 +524,7 @@
     /// will return `None`.
     ///
     /// Iterator element type is
-    /// [`EitherOrBoth<Self::Item, J::Item>`](enum.EitherOrBoth.html).
+    /// [`EitherOrBoth<Self::Item, J::Item>`](EitherOrBoth).
     ///
     /// ```rust
     /// use itertools::EitherOrBoth::{Both, Right};
@@ -526,7 +594,7 @@
     /// allocations.  It needs allocations only if several group iterators
     /// are alive at the same time.
     ///
-    /// This type implements `IntoIterator` (it is **not** an iterator
+    /// This type implements [`IntoIterator`] (it is **not** an iterator
     /// itself), because the group iterators need to borrow from this
     /// value. It should be stored in a local variable or temporary and
     /// iterated.
@@ -594,7 +662,7 @@
     }
 
     /// Return an iterator over all contiguous windows producing tuples of
-    /// a specific size (up to 4).
+    /// a specific size (up to 12).
     ///
     /// `tuple_windows` clones the iterator elements so that they can be
     /// part of successive windows, this makes it most suited for iterators
@@ -636,7 +704,7 @@
 
     /// Return 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 (up to 4).
+    /// iterator, producing tuples of a specific size (up to 12).
     ///
     /// `circular_tuple_windows` clones the iterator elements so that they can be
     /// part of successive windows, this makes it most suited for iterators
@@ -669,9 +737,9 @@
         tuple_impl::circular_tuple_windows(self)
     }
     /// Return an iterator that groups the items in tuples of a specific size
-    /// (up to 4).
+    /// (up to 12).
     ///
-    /// See also the method [`.next_tuple()`](#method.next_tuple).
+    /// See also the method [`.next_tuple()`](Itertools::next_tuple).
     ///
     /// ```
     /// use itertools::Itertools;
@@ -698,7 +766,7 @@
     /// itertools::assert_equal(it, vec![(1, 2, 3), (4, 5, 6)]);
     /// ```
     ///
-    /// See also [`Tuples::into_buffer`](structs/struct.Tuples.html#method.into_buffer).
+    /// See also [`Tuples::into_buffer`].
     fn tuples<T>(self) -> Tuples<Self, T>
         where Self: Sized + Iterator<Item = T::Item>,
               T: traits::HomogeneousTuple
@@ -755,7 +823,7 @@
         adaptors::step(self, n)
     }
 
-    /// Convert each item of the iterator using the `Into` trait.
+    /// Convert each item of the iterator using the [`Into`] trait.
     ///
     /// ```rust
     /// use itertools::Itertools;
@@ -769,7 +837,7 @@
         adaptors::map_into(self)
     }
 
-    /// See [`.map_ok()`](#method.map_ok).
+    /// See [`.map_ok()`](Itertools::map_ok).
     #[deprecated(note="Use .map_ok() instead", since="0.10.0")]
     fn map_results<F, T, U, E>(self, f: F) -> MapOk<Self, F>
         where Self: Iterator<Item = Result<T, E>> + Sized,
@@ -832,6 +900,30 @@
         adaptors::filter_map_ok(self, f)
     }
 
+    /// Return an iterator adaptor that flattens every `Result::Ok` value into
+    /// a series of `Result::Ok` values. `Result::Err` values are unchanged.
+    /// 
+    /// This is useful when you have some common error type for your crate and
+    /// need to propogate it upwards, but the `Result::Ok` case needs to be flattened.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let input = vec![Ok(0..2), Err(false), Ok(2..4)];
+    /// let it = input.iter().cloned().flatten_ok();
+    /// itertools::assert_equal(it.clone(), vec![Ok(0), Ok(1), Err(false), Ok(2), Ok(3)]);
+    /// 
+    /// // This can also be used to propogate errors when collecting.
+    /// let output_result: Result<Vec<i32>, bool> = it.collect();
+    /// assert_eq!(output_result, Err(false));
+    /// ```
+    fn flatten_ok<T, E>(self) -> FlattenOk<Self, T, E>
+        where Self: Iterator<Item = Result<T, E>> + Sized,
+              T: IntoIterator
+    {
+        flatten_ok::flatten_ok(self)
+    }
+
     /// Return an iterator adaptor that merges the two base iterators in
     /// ascending order.  If both base iterators are sorted (ascending), the
     /// result is sorted.
@@ -855,7 +947,7 @@
     }
 
     /// Return an iterator adaptor that merges the two base iterators in order.
-    /// This is much like `.merge()` but allows for a custom ordering.
+    /// This is much like [`.merge()`](Itertools::merge) but allows for a custom ordering.
     ///
     /// This can be especially useful for sequences of tuples.
     ///
@@ -995,7 +1087,7 @@
     ///
     /// All provided iterators must yield the same `Item` type. To generate
     /// the product of iterators yielding multiple types, use the
-    /// [`iproduct`](macro.iproduct.html) macro instead.
+    /// [`iproduct`] macro instead.
     ///
     ///
     /// The iterator element type is `Vec<T>`, where `T` is the iterator element
@@ -1115,12 +1207,13 @@
     /// ```
     /// use itertools::Itertools;
     ///
-    /// let data = vec![1., 1., 2., 3., 3., 2., 2.];
+    /// let data = vec!['a', 'a', 'b', 'c', 'c', 'b', 'b'];
     /// itertools::assert_equal(data.into_iter().dedup_with_count(),
-    ///                         vec![(2, 1.), (1, 2.), (2, 3.), (2, 2.)]);
+    ///                         vec![(2, 'a'), (1, 'b'), (2, 'c'), (2, 'b')]);
     /// ```
     fn dedup_with_count(self) -> DedupWithCount<Self>
-        where Self: Sized,
+    where
+        Self: Sized,
     {
         adaptors::dedup_with_count(self)
     }
@@ -1137,17 +1230,66 @@
     /// ```
     /// use itertools::Itertools;
     ///
-    /// let data = vec![(0, 1.), (1, 1.), (0, 2.), (0, 3.), (1, 3.), (1, 2.), (2, 2.)];
+    /// let data = vec![(0, 'a'), (1, 'a'), (0, 'b'), (0, 'c'), (1, 'c'), (1, 'b'), (2, 'b')];
     /// itertools::assert_equal(data.into_iter().dedup_by_with_count(|x, y| x.1 == y.1),
-    ///                         vec![(2, (0, 1.)), (1, (0, 2.)), (2, (0, 3.)), (2, (1, 2.))]);
+    ///                         vec![(2, (0, 'a')), (1, (0, 'b')), (2, (0, 'c')), (2, (1, 'b'))]);
     /// ```
     fn dedup_by_with_count<Cmp>(self, cmp: Cmp) -> DedupByWithCount<Self, Cmp>
-        where Self: Sized,
-              Cmp: FnMut(&Self::Item, &Self::Item) -> bool,
+    where
+        Self: Sized,
+        Cmp: FnMut(&Self::Item, &Self::Item) -> bool,
     {
         adaptors::dedup_by_with_count(self, cmp)
     }
 
+    /// Return an iterator adaptor that produces elements that appear more than once during the
+    /// iteration. Duplicates are detected using hash and equality.
+    ///
+    /// The iterator is stable, returning the duplicate items in the order in which they occur in
+    /// the adapted iterator. Each duplicate item is returned exactly once. If an item appears more
+    /// than twice, the second item is the item retained and the rest are discarded.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let data = vec![10, 20, 30, 20, 40, 10, 50];
+    /// itertools::assert_equal(data.into_iter().duplicates(),
+    ///                         vec![20, 10]);
+    /// ```
+    #[cfg(feature = "use_std")]
+    fn duplicates(self) -> Duplicates<Self>
+        where Self: Sized,
+              Self::Item: Eq + Hash
+    {
+        duplicates_impl::duplicates(self)
+    }
+
+    /// Return an iterator adaptor that produces elements that appear more than once during the
+    /// iteration. Duplicates are detected using hash and equality.
+    ///
+    /// Duplicates are detected by comparing the key they map to with the keying function `f` by
+    /// hash and equality. The keys are stored in a hash map in the iterator.
+    ///
+    /// The iterator is stable, returning the duplicate items in the order in which they occur in
+    /// the adapted iterator. Each duplicate item is returned exactly once. If an item appears more
+    /// than twice, the second item is the item retained and the rest are discarded.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let data = vec!["a", "bb", "aa", "c", "ccc"];
+    /// itertools::assert_equal(data.into_iter().duplicates_by(|s| s.len()),
+    ///                         vec!["aa", "c"]);
+    /// ```
+    #[cfg(feature = "use_std")]
+    fn duplicates_by<V, F>(self, f: F) -> DuplicatesBy<Self, V, F>
+        where Self: Sized,
+              V: Eq + Hash,
+              F: FnMut(&Self::Item) -> V
+    {
+        duplicates_impl::duplicates_by(self, f)
+    }
+
     /// Return an iterator adaptor that filters out elements that have
     /// already been produced once during the iteration. Duplicates
     /// are detected using hash and equality.
@@ -1211,7 +1353,7 @@
     /// `peeking_take_while` is done.
     ///
     ///
-    /// See also [`.take_while_ref()`](#method.take_while_ref)
+    /// See also [`.take_while_ref()`](Itertools::take_while_ref)
     /// which is a similar adaptor.
     fn peeking_take_while<F>(&mut self, accept: F) -> PeekingTakeWhile<Self, F>
         where Self: Sized + PeekingNext,
@@ -1480,7 +1622,7 @@
     /// ease special-case handling of the first or last elements.
     ///
     /// Iterator element type is
-    /// [`Position<Self::Item>`](enum.Position.html)
+    /// [`Position<Self::Item>`](Position)
     ///
     /// ```
     /// use itertools::{Itertools, Position};
@@ -1613,6 +1755,82 @@
         }
         None
     }
+    /// Find the value of the first element satisfying a predicate or return the last element, if any.
+    ///
+    /// The iterator is not advanced past the first element found.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let numbers = [1, 2, 3, 4];
+    /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 5), Some(&4));
+    /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 2), Some(&3));
+    /// assert_eq!(std::iter::empty::<i32>().find_or_last(|&x| x > 5), None);
+    /// ```
+    fn find_or_last<P>(mut self, mut predicate: P) -> Option<Self::Item>
+        where Self: Sized,
+              P: FnMut(&Self::Item) -> bool,
+    {
+        let mut prev = None;
+        self.find_map(|x| if predicate(&x) { Some(x) } else { prev = Some(x); None })
+            .or(prev)
+    }
+    /// Find the value of the first element satisfying a predicate or return the first element, if any.
+    ///
+    /// The iterator is not advanced past the first element found.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let numbers = [1, 2, 3, 4];
+    /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 5), Some(&1));
+    /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 2), Some(&3));
+    /// assert_eq!(std::iter::empty::<i32>().find_or_first(|&x| x > 5), None);
+    /// ```
+    fn find_or_first<P>(mut self, mut predicate: P) -> Option<Self::Item>
+        where Self: Sized,
+              P: FnMut(&Self::Item) -> bool,
+    {
+        let first = self.next()?;
+        Some(if predicate(&first) {
+            first
+        } else {
+            self.find(|x| predicate(&x)).unwrap_or(first)
+        })
+    }
+    /// Returns `true` if the given item is present in this iterator.
+    ///
+    /// This method is short-circuiting. If the given item is present in this
+    /// iterator, this method will consume the iterator up-to-and-including
+    /// the item. If the given item is not present in this iterator, the
+    /// iterator will be exhausted.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// #[derive(PartialEq, Debug)]
+    /// enum Enum { A, B, C, D, E, }
+    /// 
+    /// let mut iter = vec![Enum::A, Enum::B, Enum::C, Enum::D].into_iter();
+    /// 
+    /// // search `iter` for `B`
+    /// assert_eq!(iter.contains(&Enum::B), true);
+    /// // `B` was found, so the iterator now rests at the item after `B` (i.e, `C`).
+    /// assert_eq!(iter.next(), Some(Enum::C));
+    /// 
+    /// // search `iter` for `E`
+    /// assert_eq!(iter.contains(&Enum::E), false);
+    /// // `E` wasn't found, so `iter` is now exhausted
+    /// assert_eq!(iter.next(), None);
+    /// ```
+    fn contains<Q>(&mut self, query: &Q) -> bool
+    where
+        Self: Sized,
+        Self::Item: Borrow<Q>,
+        Q: PartialEq,
+    {
+        self.any(|x| x.borrow() == query)
+    }
 
     /// Check whether all elements compare equal.
     ///
@@ -1640,6 +1858,30 @@
         }
     }
 
+    /// Check whether all elements are unique (non equal).
+    ///
+    /// Empty iterators are considered to have unique elements:
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let data = vec![1, 2, 3, 4, 1, 5];
+    /// assert!(!data.iter().all_unique());
+    /// assert!(data[0..4].iter().all_unique());
+    /// assert!(data[1..6].iter().all_unique());
+    ///
+    /// let data : Option<usize> = None;
+    /// assert!(data.into_iter().all_unique());
+    /// ```
+    #[cfg(feature = "use_std")]
+    fn all_unique(&mut self) -> bool
+        where Self: Sized,
+              Self::Item: Eq + Hash
+    {
+        let mut used = HashSet::new();
+        self.all(move |elt| used.insert(elt))
+    }
+
     /// Consume the first `n` elements from the iterator eagerly,
     /// and return the same iterator again.
     ///
@@ -1714,7 +1956,7 @@
         self.for_each(f)
     }
 
-    /// Combine all an iterator's elements into one element by using `Extend`.
+    /// Combine all an iterator's elements into one element by using [`Extend`].
     ///
     /// This combinator will extend the first item with each of the rest of the
     /// items of the iterator. If the iterator is empty, the default value of
@@ -1734,7 +1976,7 @@
         concat(self)
     }
 
-    /// `.collect_vec()` is simply a type specialization of `.collect()`,
+    /// `.collect_vec()` is simply a type specialization of [`Iterator::collect`],
     /// for convenience.
     #[cfg(feature = "use_alloc")]
     fn collect_vec(self) -> Vec<Self::Item>
@@ -1856,7 +2098,7 @@
 
     /// Format all iterator elements, separated by `sep`.
     ///
-    /// This is a customizable version of `.format()`.
+    /// This is a customizable version of [`.format()`](Itertools::format).
     ///
     /// The supplied closure `format` is called once per iterator element,
     /// with two arguments: the element and a callback that takes a
@@ -1893,7 +2135,7 @@
         format::new_format(self, sep, format)
     }
 
-    /// See [`.fold_ok()`](#method.fold_ok).
+    /// See [`.fold_ok()`](Itertools::fold_ok).
     #[deprecated(note="Use .fold_ok() instead", since="0.10.0")]
     fn fold_results<A, E, B, F>(&mut self, start: B, f: F) -> Result<B, E>
         where Self: Iterator<Item = Result<A, E>>,
@@ -1963,7 +2205,7 @@
     /// value is returned inside `Some`. Otherwise, the operation terminates
     /// and returns `None`. No iterator elements are consumed after the `None`.
     ///
-    /// This is the `Option` equivalent to `fold_ok`.
+    /// This is the `Option` equivalent to [`fold_ok`](Itertools::fold_ok).
     ///
     /// ```
     /// use std::ops::Add;
@@ -2001,6 +2243,7 @@
     /// assert_eq!((0..10).fold1(|x, y| x + y).unwrap_or(0), 45);
     /// assert_eq!((0..0).fold1(|x, y| x * y), None);
     /// ```
+    #[deprecated(since = "0.10.2", note = "Use `Iterator::reduce` instead")]
     fn fold1<F>(mut self, f: F) -> Option<Self::Item>
         where F: FnMut(Self::Item, Self::Item) -> Self::Item,
               Self: Sized,
@@ -2117,7 +2360,7 @@
 
     /// An iterator method that applies a function, producing a single, final value.
     ///
-    /// `fold_while()` is basically equivalent to `fold()` but with additional support for
+    /// `fold_while()` is basically equivalent to [`Iterator::fold`] but with additional support for
     /// early exit via short-circuiting.
     ///
     /// ```
@@ -2236,7 +2479,7 @@
     /// Sort all iterator elements into a new iterator in ascending order.
     ///
     /// **Note:** This consumes the entire iterator, uses the
-    /// `slice::sort_unstable()` method and returns the result as a new
+    /// [`slice::sort_unstable`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2265,7 +2508,7 @@
     /// Sort all iterator elements into a new iterator in ascending order.
     ///
     /// **Note:** This consumes the entire iterator, uses the
-    /// `slice::sort_unstable_by()` method and returns the result as a new
+    /// [`slice::sort_unstable_by`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2298,7 +2541,7 @@
     /// Sort all iterator elements into a new iterator in ascending order.
     ///
     /// **Note:** This consumes the entire iterator, uses the
-    /// `slice::sort_unstable_by_key()` method and returns the result as a new
+    /// [`slice::sort_unstable_by_key`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2332,7 +2575,7 @@
     /// Sort all iterator elements into a new iterator in ascending order.
     ///
     /// **Note:** This consumes the entire iterator, uses the
-    /// `slice::sort()` method and returns the result as a new
+    /// [`slice::sort`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2361,7 +2604,7 @@
     /// Sort all iterator elements into a new iterator in ascending order.
     ///
     /// **Note:** This consumes the entire iterator, uses the
-    /// `slice::sort_by()` method and returns the result as a new
+    /// [`slice::sort_by`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2394,7 +2637,7 @@
     /// Sort all iterator elements into a new iterator in ascending order.
     ///
     /// **Note:** This consumes the entire iterator, uses the
-    /// `slice::sort_by_key()` method and returns the result as a new
+    /// [`slice::sort_by_key`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2425,6 +2668,43 @@
         v.into_iter()
     }
 
+    /// Sort all iterator elements into a new iterator in ascending order. The key function is
+    /// called exactly once per key.
+    ///
+    /// **Note:** This consumes the entire iterator, uses the
+    /// [`slice::sort_by_cached_key`] method and returns the result as a new
+    /// iterator that owns its elements.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // sort people in descending order by age
+    /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)];
+    ///
+    /// let oldest_people_first = people
+    ///     .into_iter()
+    ///     .sorted_by_cached_key(|x| -x.1)
+    ///     .map(|(person, _age)| person);
+    ///
+    /// itertools::assert_equal(oldest_people_first,
+    ///                         vec!["Jill", "Jack", "Jane", "John"]);
+    /// ```
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn sorted_by_cached_key<K, F>(self, f: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
+    {
+        let mut v = Vec::from_iter(self);
+        v.sort_by_cached_key(f);
+        v.into_iter()
+    }
+
     /// Sort the k smallest elements into a new iterator, in ascending order.
     ///
     /// **Note:** This consumes the entire iterator, and returns the result
@@ -2463,7 +2743,7 @@
     }
 
     /// Collect all iterator elements into one of two
-    /// partitions. Unlike `Iterator::partition`, each partition may
+    /// partitions. Unlike [`Iterator::partition`], each partition may
     /// have a distinct type.
     ///
     /// ```
@@ -2500,9 +2780,38 @@
         (left, right)
     }
 
+    /// Partition a sequence of `Result`s into one list of all the `Ok` elements
+    /// and another list of all the `Err` elements.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let successes_and_failures = vec![Ok(1), Err(false), Err(true), Ok(2)];
+    ///
+    /// let (successes, failures): (Vec<_>, Vec<_>) = successes_and_failures
+    ///     .into_iter()
+    ///     .partition_result();
+    ///
+    /// assert_eq!(successes, [1, 2]);
+    /// assert_eq!(failures, [false, true]);
+    /// ```
+    fn partition_result<A, B, T, E>(self) -> (A, B)
+        where
+            Self: Iterator<Item = Result<T, E>> + Sized,
+            A: Default + Extend<T>,
+            B: Default + Extend<E>,
+    {
+        self.partition_map(|r| match r {
+            Ok(v) => Either::Left(v),
+            Err(v) => Either::Right(v),
+        })
+    }
+
     /// Return a `HashMap` of keys mapped to `Vec`s of values. Keys and values
     /// are taken from `(Key, Value)` tuple pairs yielded by the input iterator.
     ///
+    /// Essentially a shorthand for `.into_grouping_map().collect::<Vec<_>>()`.
+    ///
     /// ```
     /// use itertools::Itertools;
     ///
@@ -2522,18 +2831,18 @@
         group_map::into_group_map(self)
     }
 
-    /// Return an `Iterator` on a HahMap. Keys mapped to `Vec`s of values. The key is specified in
+    /// Return an `Iterator` on a `HashMap`. Keys mapped to `Vec`s of values. The key is specified
     /// in the closure.
-    /// Different of into_group_map_by because the key is still present. It is also more general.
-    /// you can also fold the group_map.
+    ///
+    /// Essentially a shorthand for `.into_grouping_map_by(f).collect::<Vec<_>>()`.
     ///
     /// ```
     /// use itertools::Itertools;
     /// use std::collections::HashMap;
     ///
     /// let data = vec![(0, 10), (2, 12), (3, 13), (0, 20), (3, 33), (2, 42)];
-    /// let lookup: HashMap<u32,Vec<(u32, u32)>> = data.clone().into_iter().into_group_map_by(|a|
-    /// a.0);
+    /// let lookup: HashMap<u32,Vec<(u32, u32)>> =
+    ///     data.clone().into_iter().into_group_map_by(|a| a.0);
     ///
     /// assert_eq!(lookup[&0], vec![(0,10),(0,20)]);
     /// assert_eq!(lookup.get(&1), None);
@@ -2542,10 +2851,12 @@
     ///
     /// assert_eq!(
     ///     data.into_iter()
-    ///     .into_group_map_by(|x| x.0)
-    ///     .into_iter()
-    ///     .map(|(key, values)| (key, values.into_iter().fold(0,|acc, (_,v)| acc + v )))
-    ///     .collect::<HashMap<u32,u32>>()[&0], 30)
+    ///         .into_group_map_by(|x| x.0)
+    ///         .into_iter()
+    ///         .map(|(key, values)| (key, values.into_iter().fold(0,|acc, (_,v)| acc + v )))
+    ///         .collect::<HashMap<u32,u32>>()[&0],
+    ///     30,
+    /// );
     /// ```
     #[cfg(feature = "use_std")]
     fn into_group_map_by<K, V, F>(self, f: F) -> HashMap<K, Vec<V>>
@@ -2564,7 +2875,7 @@
     /// value of type `K` will be used as key to identify the groups and the
     /// value of type `V` as value for the folding operation.
     /// 
-    /// See [`GroupingMap`](./structs/struct.GroupingMap.html) for more informations
+    /// See [`GroupingMap`] for more informations
     /// on what operations are available.
     #[cfg(feature = "use_std")]
     fn into_grouping_map<K, V>(self) -> GroupingMap<Self>
@@ -2580,7 +2891,7 @@
     /// The values from this iterator will be used as values for the folding operation
     /// while the keys will be obtained from the values by calling `key_mapper`.
     /// 
-    /// See [`GroupingMap`](./structs/struct.GroupingMap.html) for more informations
+    /// See [`GroupingMap`] for more informations
     /// on what operations are available.
     #[cfg(feature = "use_std")]
     fn into_grouping_map_by<K, V, F>(self, key_mapper: F) -> GroupingMapBy<Self, F>
@@ -2635,11 +2946,11 @@
     /// Return the minimum and maximum element of an iterator, as determined by
     /// the specified function.
     ///
-    /// The return value is a variant of `MinMaxResult` like for `minmax()`.
+    /// The return value is a variant of [`MinMaxResult`] like for [`.minmax()`](Itertools::minmax).
     ///
     /// For the minimum, the first minimal element is returned.  For the maximum,
     /// the last maximal element wins.  This matches the behavior of the standard
-    /// `Iterator::min()` and `Iterator::max()` methods.
+    /// [`Iterator::min`] and [`Iterator::max`] methods.
     ///
     /// The keys can be floats but no particular result is guaranteed
     /// if a key is NaN.
@@ -2652,11 +2963,11 @@
     /// Return the minimum and maximum element of an iterator, as determined by
     /// the specified comparison function.
     ///
-    /// The return value is a variant of `MinMaxResult` like for `minmax()`.
+    /// The return value is a variant of [`MinMaxResult`] like for [`.minmax()`](Itertools::minmax).
     ///
     /// For the minimum, the first minimal element is returned.  For the maximum,
     /// the last maximal element wins.  This matches the behavior of the standard
-    /// `Iterator::min()` and `Iterator::max()` methods.
+    /// [`Iterator::min`] and [`Iterator::max`] methods.
     fn minmax_by<F>(self, mut compare: F) -> MinMaxResult<Self::Item>
         where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
     {
@@ -2875,8 +3186,6 @@
     /// let a = [1, 1, -1, -1];
     /// assert_eq!(a.iter().position_minmax(), MinMax(2, 1));
     /// ```
-    ///
-    /// [`MinMaxResult`]: enum.MinMaxResult.html
     fn position_minmax(self) -> MinMaxResult<usize>
         where Self: Sized, Self::Item: PartialOrd
     {
@@ -2921,8 +3230,7 @@
     /// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), MinMax(0, 3));
     /// ```
     ///
-    /// [`MinMaxResult`]: enum.MinMaxResult.html
-    /// [`position_minmax`]: #method.position_minmax
+    /// [`position_minmax`]: Self::position_minmax
     fn position_minmax_by_key<K, F>(self, mut key: F) -> MinMaxResult<usize>
         where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K
     {
@@ -2964,8 +3272,7 @@
     /// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), MinMax(2, 1));
     /// ```
     ///
-    /// [`MinMaxResult`]: enum.MinMaxResult.html
-    /// [`position_minmax`]: #method.position_minmax
+    /// [`position_minmax`]: Self::position_minmax
     fn position_minmax_by<F>(self, mut compare: F) -> MinMaxResult<usize>
         where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
     {
@@ -3013,6 +3320,42 @@
         }
     }
 
+    /// If the iterator yields no elements, Ok(None) will be returned. If the iterator yields
+    /// exactly one element, that element will be returned, otherwise an error will be returned
+    /// containing an iterator that has the same output as the input iterator.
+    ///
+    /// This provides an additional layer of validation over just calling `Iterator::next()`.
+    /// If your assumption that there should be at most one element yielded is false this provides
+    /// the opportunity to detect and handle that, preventing errors at a distance.
+    ///
+    /// # Examples
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// assert_eq!((0..10).filter(|&x| x == 2).at_most_one().unwrap(), Some(2));
+    /// assert!((0..10).filter(|&x| x > 1 && x < 4).at_most_one().unwrap_err().eq(2..4));
+    /// assert!((0..10).filter(|&x| x > 1 && x < 5).at_most_one().unwrap_err().eq(2..5));
+    /// assert_eq!((0..10).filter(|&_| false).at_most_one().unwrap(), None);
+    /// ```
+    fn at_most_one(mut self) -> Result<Option<Self::Item>, ExactlyOneError<Self>>
+    where
+        Self: Sized,
+    {
+        match self.next() {
+            Some(first) => {
+                match self.next() {
+                    Some(second) => {
+                        Err(ExactlyOneError::new(Some(Either::Left([first, second])), self))
+                    }
+                    None => {
+                        Ok(Some(first))
+                    }
+                }
+            }
+            None => Ok(None),
+        }
+    }
+
     /// An iterator adaptor that allows the user to peek at multiple `.next()`
     /// values without advancing the base iterator.
     ///
@@ -3058,6 +3401,75 @@
         self.for_each(|item| *counts.entry(item).or_default() += 1);
         counts
     }
+
+    /// Collect the items in this iterator and return a `HashMap` which
+    /// contains each item that appears in the iterator and the number
+    /// of times it appears,
+    /// determining identity using a keying function.
+    ///
+    /// ```
+    /// # use itertools::Itertools;
+    /// struct Character {
+    ///   first_name: &'static str,
+    ///   last_name:  &'static str,
+    /// }
+    /// 
+    /// let characters =
+    ///     vec![
+    ///         Character { first_name: "Amy",   last_name: "Pond"      },
+    ///         Character { first_name: "Amy",   last_name: "Wong"      },
+    ///         Character { first_name: "Amy",   last_name: "Santiago"  },
+    ///         Character { first_name: "James", last_name: "Bond"      },
+    ///         Character { first_name: "James", last_name: "Sullivan"  },
+    ///         Character { first_name: "James", last_name: "Norington" },
+    ///         Character { first_name: "James", last_name: "Kirk"      },
+    ///     ];
+    /// 
+    /// let first_name_frequency = 
+    ///     characters
+    ///         .into_iter()
+    ///         .counts_by(|c| c.first_name);
+    ///     
+    /// assert_eq!(first_name_frequency["Amy"], 3);
+    /// assert_eq!(first_name_frequency["James"], 4);
+    /// assert_eq!(first_name_frequency.contains_key("Asha"), false);
+    /// ```
+    #[cfg(feature = "use_std")]
+    fn counts_by<K, F>(self, f: F) -> HashMap<K, usize>
+    where
+        Self: Sized,
+        K: Eq + Hash,
+        F: FnMut(Self::Item) -> K,
+    {
+        self.map(f).counts()
+    }
+
+    /// Converts an iterator of tuples into a tuple of containers.
+    ///
+    /// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
+    /// column.
+    ///
+    /// This function is, in some sense, the opposite of [`multizip`].
+    /// 
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)];
+    ///
+    /// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = inputs
+    ///     .into_iter()
+    ///     .multiunzip();
+    ///
+    /// assert_eq!(a, vec![1, 4, 7]);
+    /// assert_eq!(b, vec![2, 5, 8]);
+    /// assert_eq!(c, vec![3, 6, 9]);
+    /// ```
+    fn multiunzip<FromI>(self) -> FromI
+    where
+        Self: Sized + MultiUnzip<FromI>,
+    {
+        MultiUnzip::multiunzip(self)
+    }
 }
 
 impl<T: ?Sized> Itertools for T where T: Iterator { }
@@ -3066,8 +3478,8 @@
 /// (elements pairwise equal and sequences of the same length),
 /// `false` otherwise.
 ///
-/// This is an `IntoIterator` enabled function that is similar to the standard
-/// library method `Iterator::eq`.
+/// This is an [`IntoIterator`] enabled function that is similar to the standard
+/// library method [`Iterator::eq`].
 ///
 /// ```
 /// assert!(itertools::equal(vec![1, 2, 3], 1..4));
@@ -3092,7 +3504,7 @@
 }
 
 /// Assert that two iterables produce equal sequences, with the same
-/// semantics as *equal(a, b)*.
+/// semantics as [`equal(a, b)`](equal).
 ///
 /// **Panics** on assertion failure with a message that shows the
 /// two iteration elements.
@@ -3167,9 +3579,9 @@
     split_index
 }
 
-/// An enum used for controlling the execution of `.fold_while()`.
+/// An enum used for controlling the execution of `fold_while`.
 ///
-/// See [`.fold_while()`](trait.Itertools.html#method.fold_while) for more information.
+/// See [`.fold_while()`](Itertools::fold_while) for more information.
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum FoldWhile<T> {
     /// Continue folding with this value
diff --git a/src/merge_join.rs b/src/merge_join.rs
index 0f87ae4..4c0048f 100644
--- a/src/merge_join.rs
+++ b/src/merge_join.rs
@@ -7,7 +7,7 @@
 
 /// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order.
 ///
-/// See [`.merge_join_by()`](trait.Itertools.html#method.merge_join_by) for more information.
+/// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more information.
 pub fn merge_join_by<I, J, F>(left: I, right: J, cmp_fn: F)
     -> MergeJoinBy<I::IntoIter, J::IntoIter, F>
     where I: IntoIterator,
@@ -23,7 +23,7 @@
 
 /// An iterator adaptor that merge-joins items from the two base iterators in ascending order.
 ///
-/// See [`.merge_join_by()`](../trait.Itertools.html#method.merge_join_by) for more information.
+/// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct MergeJoinBy<I: Iterator, J: Iterator, F> {
     left: PutBack<Fuse<I>>,
diff --git a/src/minmax.rs b/src/minmax.rs
index 38180ef..52b2f11 100644
--- a/src/minmax.rs
+++ b/src/minmax.rs
@@ -1,6 +1,7 @@
 
-/// `MinMaxResult` is an enum returned by `minmax`. See `Itertools::minmax()` for
-/// more detail.
+/// `MinMaxResult` is an enum returned by `minmax`.
+///
+/// See [`.minmax()`](crate::Itertools::minmax) for more detail.
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum MinMaxResult<T> {
     /// Empty iterator
diff --git a/src/multipeek_impl.rs b/src/multipeek_impl.rs
index 986e5b4..5917681 100644
--- a/src/multipeek_impl.rs
+++ b/src/multipeek_impl.rs
@@ -3,7 +3,7 @@
 use crate::size_hint;
 use crate::PeekingNext;
 
-/// See [`multipeek()`](../fn.multipeek.html) for more information.
+/// See [`multipeek()`] for more information.
 #[derive(Clone, Debug)]
 pub struct MultiPeek<I>
     where I: Iterator
@@ -38,6 +38,7 @@
     /// Works exactly like `.next()` with the only difference that it doesn't
     /// advance itself. `.peek()` can be called multiple times, to peek
     /// further ahead.
+    /// When `.next()` is called, reset the peeking “cursor”.
     pub fn peek(&mut self) -> Option<&I::Item> {
         let ret = if self.index < self.buf.len() {
             Some(&self.buf[self.index])
diff --git a/src/pad_tail.rs b/src/pad_tail.rs
index 4ed83c3..de57ee4 100644
--- a/src/pad_tail.rs
+++ b/src/pad_tail.rs
@@ -1,4 +1,4 @@
-use std::iter::Fuse;
+use std::iter::{Fuse, FusedIterator};
 use crate::size_hint;
 
 /// An iterator adaptor that pads a sequence to a minimum length by filling
@@ -6,7 +6,7 @@
 ///
 /// Iterator element type is `I::Item`.
 ///
-/// See [`.pad_using()`](../trait.Itertools.html#method.pad_using) for more information.
+/// See [`.pad_using()`](crate::Itertools::pad_using) for more information.
 #[derive(Clone)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct PadUsing<I, F> {
@@ -16,6 +16,13 @@
     filler: F,
 }
 
+impl<I, F> std::fmt::Debug for PadUsing<I, F>
+where
+    I: std::fmt::Debug,
+{
+    debug_fmt_fields!(PadUsing, iter, min, pos);
+}
+
 /// Create a new **PadUsing** iterator.
 pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
     where I: Iterator,
@@ -81,3 +88,9 @@
     where I: ExactSizeIterator,
           F: FnMut(usize) -> I::Item
 {}
+
+
+impl<I, F> FusedIterator for PadUsing<I, F>
+    where I: FusedIterator,
+          F: FnMut(usize) -> I::Item
+{}
diff --git a/src/peek_nth.rs b/src/peek_nth.rs
index d22258b..bcca458 100644
--- a/src/peek_nth.rs
+++ b/src/peek_nth.rs
@@ -3,7 +3,7 @@
 use alloc::collections::VecDeque;
 use std::iter::Fuse;
 
-/// See [`peek_nth()`](../fn.peek_nth.html) for more information.
+/// See [`peek_nth()`] for more information.
 #[derive(Clone, Debug)]
 pub struct PeekNth<I>
 where
@@ -13,7 +13,7 @@
     buf: VecDeque<I::Item>,
 }
 
-/// A drop-in replacement for `std::iter::Peekable` which adds a `peek_nth`
+/// A drop-in replacement for [`std::iter::Peekable`] which adds a `peek_nth`
 /// method allowing the user to `peek` at a value several iterations forward
 /// without advancing the base iterator.
 ///
diff --git a/src/peeking_take_while.rs b/src/peeking_take_while.rs
index 70ef988..cd0945a 100644
--- a/src/peeking_take_while.rs
+++ b/src/peeking_take_while.rs
@@ -5,7 +5,7 @@
 
 /// An iterator that allows peeking at an element before deciding to accept it.
 ///
-/// See [`.peeking_take_while()`](trait.Itertools.html#method.peeking_take_while)
+/// See [`.peeking_take_while()`](crate::Itertools::peeking_take_while)
 /// for more information.
 ///
 /// This is implemented by peeking adaptors like peekable and put back,
@@ -73,7 +73,7 @@
 
 /// An iterator adaptor that takes items while a closure returns `true`.
 ///
-/// See [`.peeking_take_while()`](../trait.Itertools.html#method.peeking_take_while)
+/// See [`.peeking_take_while()`](crate::Itertools::peeking_take_while)
 /// for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct PeekingTakeWhile<'a, I: 'a, F>
@@ -83,6 +83,13 @@
     f: F,
 }
 
+impl<'a, I: 'a, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F>
+where
+    I: Iterator + std::fmt::Debug,
+{
+    debug_fmt_fields!(PeekingTakeWhile, iter);
+}
+
 /// Create a PeekingTakeWhile
 pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F>
     where I: Iterator,
diff --git a/src/permutations.rs b/src/permutations.rs
index d96bbac..3080f9d 100644
--- a/src/permutations.rs
+++ b/src/permutations.rs
@@ -7,7 +7,7 @@
 /// An iterator adaptor that iterates through all the `k`-permutations of the
 /// elements from an iterator.
 ///
-/// See [`.permutations()`](../trait.Itertools.html#method.permutations) for
+/// See [`.permutations()`](crate::Itertools::permutations) for
 /// more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Permutations<I: Iterator> {
diff --git a/src/powerset.rs b/src/powerset.rs
index ef17752..4d7685b 100644
--- a/src/powerset.rs
+++ b/src/powerset.rs
@@ -1,4 +1,5 @@
 use std::fmt;
+use std::iter::FusedIterator;
 use std::usize;
 use alloc::vec::Vec;
 
@@ -7,7 +8,7 @@
 
 /// An iterator to iterate through the powerset of the elements from an iterator.
 ///
-/// See [`.powerset()`](../trait.Itertools.html#method.powerset) for more
+/// See [`.powerset()`](crate::Itertools::powerset) for more
 /// information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Powerset<I: Iterator> {
@@ -81,3 +82,9 @@
         }
     }
 }
+
+impl<I> FusedIterator for Powerset<I>
+    where
+        I: Iterator,
+        I::Item: Clone,
+{}
diff --git a/src/process_results_impl.rs b/src/process_results_impl.rs
index d74925a..44308f3 100644
--- a/src/process_results_impl.rs
+++ b/src/process_results_impl.rs
@@ -2,7 +2,7 @@
 /// An iterator that produces only the `T` values as long as the
 /// inner iterator produces `Ok(T)`.
 ///
-/// Used by [`process_results`](../fn.process_results.html), see its docs
+/// Used by [`process_results`](crate::process_results), see its docs
 /// for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Debug)]
@@ -30,6 +30,23 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         (0, self.iter.size_hint().1)
     }
+
+    fn fold<B, F>(mut self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let error = self.error;
+        self.iter
+            .try_fold(init, |acc, opt| match opt {
+                Ok(x) => Ok(f(acc, x)),
+                Err(e) => {
+                    *error = Err(e);
+                    Err(acc)
+                }
+            })
+            .unwrap_or_else(|e| e)
+    }
 }
 
 /// “Lift” a function of the values of an iterator so that it can process
diff --git a/src/rciter_impl.rs b/src/rciter_impl.rs
index 9122dad..782908e 100644
--- a/src/rciter_impl.rs
+++ b/src/rciter_impl.rs
@@ -1,5 +1,5 @@
 
-use std::iter::IntoIterator;
+use std::iter::{FusedIterator, IntoIterator};
 use alloc::rc::Rc;
 use std::cell::RefCell;
 
@@ -93,3 +93,8 @@
         self.clone()
     }
 }
+
+
+impl<A, I> FusedIterator for RcIter<I>
+    where I: FusedIterator<Item = A>
+{}
diff --git a/src/repeatn.rs b/src/repeatn.rs
index 8bc4850..e025f6f 100644
--- a/src/repeatn.rs
+++ b/src/repeatn.rs
@@ -1,7 +1,8 @@
+use std::iter::FusedIterator;
 
 /// An iterator that produces *n* repetitions of an element.
 ///
-/// See [`repeat_n()`](../fn.repeat_n.html) for more information.
+/// See [`repeat_n()`](crate::repeat_n) for more information.
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 #[derive(Clone, Debug)]
 pub struct RepeatN<A> {
@@ -52,3 +53,7 @@
 impl<A> ExactSizeIterator for RepeatN<A>
     where A: Clone
 {}
+
+impl<A> FusedIterator for RepeatN<A>
+    where A: Clone
+{}
diff --git a/src/sources.rs b/src/sources.rs
index 5bb6afe..3877ce3 100644
--- a/src/sources.rs
+++ b/src/sources.rs
@@ -5,7 +5,7 @@
 use std::fmt;
 use std::mem;
 
-/// See [`repeat_call`](../fn.repeat_call.html) for more information.
+/// See [`repeat_call`](crate::repeat_call) for more information.
 #[derive(Clone)]
 #[deprecated(note="Use std repeat_with() instead", since="0.8.0")]
 pub struct RepeatCall<F> {
@@ -67,7 +67,7 @@
 /// `unfold` is a general iterator builder: it has a mutable state value,
 /// and a closure with access to the state that produces the next value.
 ///
-/// This more or less equivalent to a regular struct with an `Iterator`
+/// This more or less equivalent to a regular struct with an [`Iterator`]
 /// implementation, and is useful for one-off iterators.
 ///
 /// ```
@@ -112,7 +112,7 @@
     debug_fmt_fields!(Unfold, state);
 }
 
-/// See [`unfold`](../fn.unfold.html) for more information.
+/// See [`unfold`](crate::unfold) for more information.
 #[derive(Clone)]
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 pub struct Unfold<St, F> {
@@ -134,9 +134,8 @@
 
 /// An iterator that infinitely applies function to value and yields results.
 ///
-/// This `struct` is created by the [`iterate()`] function. See its documentation for more.
-///
-/// [`iterate()`]: ../fn.iterate.html
+/// This `struct` is created by the [`iterate()`](crate::iterate) function.
+/// See its documentation for more.
 #[derive(Clone)]
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 pub struct Iterate<St, F> {
diff --git a/src/tee.rs b/src/tee.rs
index 0b00302..ea47529 100644
--- a/src/tee.rs
+++ b/src/tee.rs
@@ -15,7 +15,7 @@
 
 /// One half of an iterator pair where both return the same elements.
 ///
-/// See [`.tee()`](../trait.Itertools.html#method.tee) for more information.
+/// See [`.tee()`](crate::Itertools::tee) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Debug)]
 pub struct Tee<I>
diff --git a/src/tuple_impl.rs b/src/tuple_impl.rs
index 1d24b0b..d914e03 100644
--- a/src/tuple_impl.rs
+++ b/src/tuple_impl.rs
@@ -1,6 +1,7 @@
 //! Some iterator that produces tuples
 
 use std::iter::Fuse;
+use std::iter::FusedIterator;
 use std::iter::Take;
 use std::iter::Cycle;
 use std::marker::PhantomData;
@@ -10,7 +11,7 @@
 // hiding the implementation details of `TupleCollect`.
 // See https://github.com/rust-itertools/itertools/issues/387
 
-/// Implemented for homogeneous tuples of size up to 4.
+/// Implemented for homogeneous tuples of size up to 12.
 pub trait HomogeneousTuple
     : TupleCollect
 {}
@@ -19,8 +20,8 @@
 
 /// An iterator over a incomplete tuple.
 ///
-/// See [`.tuples()`](../trait.Itertools.html#method.tuples) and
-/// [`Tuples::into_buffer()`](struct.Tuples.html#method.into_buffer).
+/// See [`.tuples()`](crate::Itertools::tuples) and
+/// [`Tuples::into_buffer()`].
 #[derive(Clone, Debug)]
 pub struct TupleBuffer<T>
     where T: HomogeneousTuple
@@ -75,8 +76,8 @@
 
 /// An iterator that groups the items in tuples of a specific size.
 ///
-/// See [`.tuples()`](../trait.Itertools.html#method.tuples) for more information.
-#[derive(Clone)]
+/// 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<I, T>
     where I: Iterator<Item = T::Item>,
@@ -130,7 +131,7 @@
 
 /// An iterator over all contiguous windows that produces tuples of a specific size.
 ///
-/// See [`.tuple_windows()`](../trait.Itertools.html#method.tuple_windows) for more
+/// See [`.tuple_windows()`](crate::Itertools::tuple_windows) for more
 /// information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Clone, Debug)]
@@ -187,11 +188,17 @@
     }
 }
 
+impl<I, T> FusedIterator for TupleWindows<I, T>
+    where I: FusedIterator<Item = T::Item>,
+          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()`](../trait.Itertools.html#method.circular_tuple_windows) for more
+/// 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)]
@@ -248,9 +255,6 @@
     () => {0};
     ($i0:ident, $($i:ident,)*) => {1 + count_ident!($($i,)*)};
 }
-macro_rules! ignore_ident{
-    ($id:ident, $($t:tt)*) => {$($t)*};
-}
 macro_rules! rev_for_each_ident{
     ($m:ident, ) => {};
     ($m:ident, $i0:ident, $($i:ident,)*) => {
@@ -317,7 +321,7 @@
                 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);
             }
diff --git a/src/unique_impl.rs b/src/unique_impl.rs
index 14c14fc..2240f36 100644
--- a/src/unique_impl.rs
+++ b/src/unique_impl.rs
@@ -3,10 +3,11 @@
 use std::collections::hash_map::{Entry};
 use std::hash::Hash;
 use std::fmt;
+use std::iter::FusedIterator;
 
 /// An iterator adapter to filter out duplicate elements.
 ///
-/// See [`.unique_by()`](../trait.Itertools.html#method.unique) for more information.
+/// See [`.unique_by()`](crate::Itertools::unique) for more information.
 #[derive(Clone)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct UniqueBy<I: Iterator, V, F> {
@@ -92,6 +93,12 @@
     }
 }
 
+impl<I, V, F> FusedIterator for UniqueBy<I, V, F>
+    where I: FusedIterator,
+          V: Eq + Hash,
+          F: FnMut(&I::Item) -> V
+{}
+
 impl<I> Iterator for Unique<I>
     where I: Iterator,
           I::Item: Eq + Hash + Clone
@@ -136,9 +143,14 @@
     }
 }
 
+impl<I> FusedIterator for Unique<I>
+    where I: FusedIterator,
+          I::Item: Eq + Hash + Clone
+{}
+
 /// An iterator adapter to filter out duplicate elements.
 ///
-/// See [`.unique()`](../trait.Itertools.html#method.unique) for more information.
+/// See [`.unique()`](crate::Itertools::unique) for more information.
 #[derive(Clone)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Unique<I: Iterator> {
diff --git a/src/unziptuple.rs b/src/unziptuple.rs
new file mode 100644
index 0000000..f468f05
--- /dev/null
+++ b/src/unziptuple.rs
@@ -0,0 +1,80 @@
+/// Converts an iterator of tuples into a tuple of containers.
+///
+/// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
+/// column.
+///
+/// This function is, in some sense, the opposite of [`multizip`].
+///
+/// ```
+/// use itertools::multiunzip;
+///
+/// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)];
+///
+/// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(inputs);
+///
+/// assert_eq!(a, vec![1, 4, 7]);
+/// assert_eq!(b, vec![2, 5, 8]);
+/// assert_eq!(c, vec![3, 6, 9]);
+/// ```
+///
+/// [`multizip`]: crate::multizip
+pub fn multiunzip<FromI, I>(i: I) -> FromI
+where
+    I: IntoIterator,
+    I::IntoIter: MultiUnzip<FromI>,
+{
+    i.into_iter().multiunzip()
+}
+
+/// An iterator that can be unzipped into multiple collections.
+///
+/// See [`.multiunzip()`](crate::Itertools::multiunzip) for more information.
+pub trait MultiUnzip<FromI>: Iterator {
+    /// Unzip this iterator into multiple collections.
+    fn multiunzip(self) -> FromI;
+}
+
+macro_rules! impl_unzip_iter {
+    ($($T:ident => $FromT:ident),*) => (
+        #[allow(non_snake_case)]
+        impl<IT: Iterator<Item = ($($T,)*)>, $($T, $FromT: Default + Extend<$T>),* > MultiUnzip<($($FromT,)*)> for IT {
+            fn multiunzip(self) -> ($($FromT,)*) {
+                // This implementation mirrors the logic of Iterator::unzip as close as possible.
+                // Unfortunately a lot of the used api there is still unstable represented by
+                // the commented out parts that follow.
+                //
+                // https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#2816-2844
+
+                let mut res = ($($FromT::default(),)*);
+                let ($($FromT,)*) = &mut res;
+
+                // Still unstable #72631
+                // let (lower_bound, _) = self.size_hint();
+                // if lower_bound > 0 {
+                //     $($FromT.extend_reserve(lower_bound);)*
+                // }
+
+                self.fold((), |(), ($($T,)*)| {
+                    // Still unstable #72631
+                    // $( $FromT.extend_one($T); )*
+                    $( $FromT.extend(std::iter::once($T)); )*
+                });
+                res
+            }
+        }
+    );
+}
+
+impl_unzip_iter!();
+impl_unzip_iter!(A => FromA);
+impl_unzip_iter!(A => FromA, B => FromB);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK, L => FromL);
diff --git a/src/with_position.rs b/src/with_position.rs
index 1440fb6..1388503 100644
--- a/src/with_position.rs
+++ b/src/with_position.rs
@@ -1,10 +1,10 @@
-use std::iter::{Fuse,Peekable};
+use std::iter::{Fuse,Peekable, FusedIterator};
 
-/// An iterator adaptor that wraps each element in an [`Position`](../enum.Position.html).
+/// An iterator adaptor that wraps each element in an [`Position`].
 ///
 /// Iterator element type is `Position<I::Item>`.
 ///
-/// See [`.with_position()`](../trait.Itertools.html#method.with_position) for more information.
+/// See [`.with_position()`](crate::Itertools::with_position) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct WithPosition<I>
     where I: Iterator,
@@ -33,7 +33,7 @@
 /// A value yielded by `WithPosition`.
 /// Indicates the position of this element in the iterator results.
 ///
-/// See [`.with_position()`](trait.Itertools.html#method.with_position) for more information.
+/// See [`.with_position()`](crate::Itertools::with_position) for more information.
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum Position<T> {
     /// This is the first element.
@@ -95,3 +95,6 @@
 impl<I> ExactSizeIterator for WithPosition<I>
     where I: ExactSizeIterator,
 { }
+
+impl<I: Iterator> FusedIterator for WithPosition<I> 
+{}
diff --git a/src/zip_eq_impl.rs b/src/zip_eq_impl.rs
index 857465d..a079b32 100644
--- a/src/zip_eq_impl.rs
+++ b/src/zip_eq_impl.rs
@@ -2,7 +2,7 @@
 
 /// An iterator which iterates two other iterators simultaneously
 ///
-/// See [`.zip_eq()`](../trait.Itertools.html#method.zip_eq) for more information.
+/// See [`.zip_eq()`](crate::Itertools::zip_eq) for more information.
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct ZipEq<I, J> {
@@ -14,7 +14,7 @@
 ///
 /// **Panics** if the iterators are not of the same length.
 ///
-/// `IntoIterator` enabled version of `i.zip_eq(j)`.
+/// [`IntoIterator`] enabled version of [`Itertools::zip_eq`](crate::Itertools::zip_eq).
 ///
 /// ```
 /// use itertools::zip_eq;
diff --git a/src/zip_longest.rs b/src/zip_longest.rs
index 1395c84..cb9a7ba 100644
--- a/src/zip_longest.rs
+++ b/src/zip_longest.rs
@@ -1,6 +1,6 @@
 use std::cmp::Ordering::{Equal, Greater, Less};
 use super::size_hint;
-use std::iter::Fuse;
+use std::iter::{Fuse, FusedIterator};
 
 use crate::either_or_both::EitherOrBoth;
 
@@ -11,7 +11,7 @@
 ///
 /// This iterator is *fused*.
 ///
-/// See [`.zip_longest()`](../trait.Itertools.html#method.zip_longest) for more information.
+/// See [`.zip_longest()`](crate::Itertools::zip_longest) for more information.
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct ZipLongest<T, U> {
@@ -20,7 +20,7 @@
 }
 
 /// Create a new `ZipLongest` iterator.
-pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U> 
+pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U>
     where T: Iterator,
           U: Iterator
 {
@@ -76,3 +76,8 @@
     where T: ExactSizeIterator,
           U: ExactSizeIterator
 {}
+
+impl<T, U> FusedIterator for ZipLongest<T, U>
+    where T: Iterator,
+          U: Iterator
+{}
diff --git a/src/ziptuple.rs b/src/ziptuple.rs
index 8f40193..b7902ae 100644
--- a/src/ziptuple.rs
+++ b/src/ziptuple.rs
@@ -1,6 +1,6 @@
 use super::size_hint;
 
-/// See [`multizip`](../fn.multizip.html) for more information.
+/// See [`multizip`] for more information.
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Zip<T> {
@@ -10,7 +10,7 @@
 /// An iterator that generalizes *.zip()* and allows running multiple iterators in lockstep.
 ///
 /// The iterator `Zip<(I, J, ..., M)>` is formed from a tuple of iterators (or values that
-/// implement `IntoIterator`) and yields elements
+/// implement [`IntoIterator`]) and yields elements
 /// until any of the subiterators yields `None`.
 ///
 /// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to `E` are the
@@ -23,8 +23,6 @@
 /// Prefer [`izip!()`] over `multizip` for the performance benefits of using the
 /// standard library `.zip()`. Prefer `multizip` if a nameable type is needed.
 ///
-/// [`izip!()`]: macro.izip.html
-///
 /// ```
 /// use itertools::multizip;
 ///
diff --git a/tests/flatten_ok.rs b/tests/flatten_ok.rs
new file mode 100644
index 0000000..bf835b5
--- /dev/null
+++ b/tests/flatten_ok.rs
@@ -0,0 +1,76 @@
+use itertools::{assert_equal, Itertools};
+use std::{ops::Range, vec::IntoIter};
+
+fn mix_data() -> IntoIter<Result<Range<i32>, bool>> {
+    vec![Ok(0..2), Err(false), Ok(2..4), Err(true), Ok(4..6)].into_iter()
+}
+
+fn ok_data() -> IntoIter<Result<Range<i32>, bool>> {
+    vec![Ok(0..2), Ok(2..4), Ok(4..6)].into_iter()
+}
+
+#[test]
+fn flatten_ok_mixed_expected_forward() {
+    assert_equal(
+        mix_data().flatten_ok(),
+        vec![
+            Ok(0),
+            Ok(1),
+            Err(false),
+            Ok(2),
+            Ok(3),
+            Err(true),
+            Ok(4),
+            Ok(5),
+        ],
+    );
+}
+
+#[test]
+fn flatten_ok_mixed_expected_reverse() {
+    assert_equal(
+        mix_data().flatten_ok().rev(),
+        vec![
+            Ok(5),
+            Ok(4),
+            Err(true),
+            Ok(3),
+            Ok(2),
+            Err(false),
+            Ok(1),
+            Ok(0),
+        ],
+    );
+}
+
+#[test]
+fn flatten_ok_collect_mixed_forward() {
+    assert_eq!(
+        mix_data().flatten_ok().collect::<Result<Vec<_>, _>>(),
+        Err(false)
+    );
+}
+
+#[test]
+fn flatten_ok_collect_mixed_reverse() {
+    assert_eq!(
+        mix_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(),
+        Err(true)
+    );
+}
+
+#[test]
+fn flatten_ok_collect_ok_forward() {
+    assert_eq!(
+        ok_data().flatten_ok().collect::<Result<Vec<_>, _>>(),
+        Ok((0..6).collect())
+    );
+}
+
+#[test]
+fn flatten_ok_collect_ok_reverse() {
+    assert_eq!(
+        ok_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(),
+        Ok((0..6).rev().collect())
+    );
+}
diff --git a/tests/fold_specialization.rs b/tests/fold_specialization.rs
deleted file mode 100644
index a984b40..0000000
--- a/tests/fold_specialization.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use itertools::Itertools;
-
-#[test]
-fn specialization_intersperse() {
-    let mut iter = (1..2).intersperse(0);
-    iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
-
-    let mut iter = (1..3).intersperse(0);
-    iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
-
-    let mut iter = (1..4).intersperse(0);
-    iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
-}
diff --git a/tests/quick.rs b/tests/quick.rs
index e5bee17..7e222a6 100644
--- a/tests/quick.rs
+++ b/tests/quick.rs
@@ -916,6 +916,12 @@
 }
 
 quickcheck! {
+    fn size_duplicates(it: Iter<i8>) -> bool {
+        correct_size_hint(it.duplicates())
+    }
+}
+
+quickcheck! {
     fn size_unique(it: Iter<i8>) -> bool {
         correct_size_hint(it.unique())
     }
@@ -1198,6 +1204,17 @@
 }
 
 quickcheck! {
+    fn at_most_one_i32(a: Vec<i32>) -> TestResult {
+        let ret = a.iter().cloned().at_most_one();
+        match a.len() {
+            0 => TestResult::from_bool(ret.unwrap() == None),
+            1 => TestResult::from_bool(ret.unwrap() == Some(a[0])),
+            _ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())),
+        }
+    }
+}
+
+quickcheck! {
     fn consistent_grouping_map_with_by(a: Vec<u8>, modulo: u8) -> () {
         let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0`
 
@@ -1581,3 +1598,98 @@
         TestResult::from_bool(itertools::equal(x, y))
     }
 }
+
+
+fn is_fused<I: Iterator>(mut it: I) -> bool
+{
+    while let Some(_) = it.next() {}
+    for _ in 0..10{
+        if it.next().is_some(){
+            return false;
+        }
+    }
+    true
+}
+
+quickcheck! {
+    fn fused_combination(a: Iter<i16>) -> bool
+    {
+        is_fused(a.clone().combinations(1)) &&
+        is_fused(a.combinations(3))
+    }
+
+    fn fused_combination_with_replacement(a: Iter<i16>) -> bool
+    {
+        is_fused(a.clone().combinations_with_replacement(1)) &&
+        is_fused(a.combinations_with_replacement(3))
+    }
+
+    fn fused_tuple_combination(a: Iter<i16>) -> bool
+    {
+        is_fused(a.clone().fuse().tuple_combinations::<(_,)>()) &&
+        is_fused(a.fuse().tuple_combinations::<(_,_,_)>())
+    }
+
+    fn fused_unique(a: Iter<i16>) -> bool
+    {
+        is_fused(a.fuse().unique())
+    }
+
+    fn fused_unique_by(a: Iter<i16>) -> bool
+    {
+        is_fused(a.fuse().unique_by(|x| x % 100))
+    }
+
+    fn fused_interleave_shortest(a: Iter<i16>, b: Iter<i16>) -> bool
+    {
+        !is_fused(a.clone().interleave_shortest(b.clone())) &&
+        is_fused(a.fuse().interleave_shortest(b.fuse()))
+    }
+    
+    fn fused_product(a: Iter<i16>, b: Iter<i16>) -> bool
+    {
+        is_fused(a.fuse().cartesian_product(b.fuse()))
+    }
+
+    fn fused_merge(a: Iter<i16>, b: Iter<i16>) -> bool
+    {
+        is_fused(a.fuse().merge(b.fuse()))
+    }
+
+    fn fused_filter_ok(a: Iter<i16>) -> bool
+    {
+        is_fused(a.map(|x| if x % 2 == 0 {Ok(x)} else {Err(x)} )
+                 .filter_ok(|x| x % 3 == 0)
+                 .fuse())
+    }
+
+    fn fused_filter_map_ok(a: Iter<i16>) -> bool
+    {
+        is_fused(a.map(|x| if x % 2 == 0 {Ok(x)} else {Err(x)} )
+                 .filter_map_ok(|x| if x % 3 == 0 {Some(x / 3)} else {None})
+                 .fuse())
+    }
+
+    fn fused_positions(a: Iter<i16>) -> bool
+    {
+        !is_fused(a.clone().positions(|x|x%2==0)) &&
+        is_fused(a.fuse().positions(|x|x%2==0))
+    }
+
+    fn fused_update(a: Iter<i16>) -> bool
+    {
+        !is_fused(a.clone().update(|x|*x+=1)) &&
+        is_fused(a.fuse().update(|x|*x+=1))
+    }
+
+    fn fused_tuple_windows(a: Iter<i16>) -> bool
+    {
+        is_fused(a.fuse().tuple_windows::<(_,_)>())
+    }
+
+    fn fused_pad_using(a: Iter<i16>) -> bool
+    {
+        is_fused(a.fuse().pad_using(100,|_|0))
+    }
+}
+
diff --git a/tests/specializations.rs b/tests/specializations.rs
index bc337c2..199cf56 100644
--- a/tests/specializations.rs
+++ b/tests/specializations.rs
@@ -15,16 +15,16 @@
     }
 }
 
-fn check_specialized<'a, V, IterItem, Iter, F>(iterator: &Iter, mapper: F)
-where
-    V: Eq + Debug,
-    Iter: Iterator<Item = IterItem> + Clone + 'a,
-    F: Fn(Box<dyn Iterator<Item = IterItem> + 'a>) -> V,
-{
-    assert_eq!(
-        mapper(Box::new(Unspecialized(iterator.clone()))),
-        mapper(Box::new(iterator.clone()))
-    )
+macro_rules! check_specialized {
+    ($src:expr, |$it:pat| $closure:expr) => {
+        let $it = $src.clone();
+        let v1 = $closure;
+
+        let $it = Unspecialized($src.clone());
+        let v2 = $closure;
+
+        assert_eq!(v1, v2);
+    }
 }
 
 fn test_specializations<IterItem, Iter>(
@@ -33,10 +33,10 @@
     IterItem: Eq + Debug + Clone,
     Iter: Iterator<Item = IterItem> + Clone,
 {
-    check_specialized(it, |i| i.count());
-    check_specialized(it, |i| i.last());
-    check_specialized(it, |i| i.collect::<Vec<_>>());
-    check_specialized(it, |i| {
+    check_specialized!(it, |i| i.count());
+    check_specialized!(it, |i| i.last());
+    check_specialized!(it, |i| i.collect::<Vec<_>>());
+    check_specialized!(it, |i| {
         let mut parameters_from_fold = vec![];
         let fold_result = i.fold(vec![], |mut acc, v: IterItem| {
             parameters_from_fold.push((acc.clone(), v.clone()));
@@ -45,7 +45,7 @@
         });
         (parameters_from_fold, fold_result)
     });
-    check_specialized(it, |mut i| {
+    check_specialized!(it, |mut i| {
         let mut parameters_from_all = vec![];
         let first = i.next();
         let all_result = i.all(|x| {
@@ -56,7 +56,7 @@
     });
     let size = it.clone().count();
     for n in 0..size + 2 {
-        check_specialized(it, |mut i| i.nth(n));
+        check_specialized!(it, |mut i| i.nth(n));
     }
     // size_hint is a bit harder to check
     let mut it_sh = it.clone();
@@ -73,6 +73,12 @@
 }
 
 quickcheck! {
+    fn intersperse(v: Vec<u8>) -> () {
+        test_specializations(&v.into_iter().intersperse(0));
+    }
+}
+
+quickcheck! {
     fn put_back_qc(test_vec: Vec<i32>) -> () {
         test_specializations(&itertools::put_back(test_vec.iter()));
         let mut pb = itertools::put_back(test_vec.into_iter());
@@ -98,3 +104,50 @@
         test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1)));
     }
 }
+
+quickcheck! {
+    fn process_results(v: Vec<Result<u8, u8>>) -> () {
+        helper(v.iter().copied());
+        helper(v.iter().copied().filter(Result::is_ok));
+
+        fn helper(it: impl Iterator<Item = Result<u8, u8>> + Clone) {
+            macro_rules! check_results_specialized {
+                ($src:expr, |$it:pat| $closure:expr) => {
+                    assert_eq!(
+                        itertools::process_results($src.clone(), |$it| $closure),
+                        itertools::process_results($src.clone(), |i| {
+                            let $it = Unspecialized(i);
+                            $closure
+                        }),
+                    )
+                }
+            }
+
+            check_results_specialized!(it, |i| i.count());
+            check_results_specialized!(it, |i| i.last());
+            check_results_specialized!(it, |i| i.collect::<Vec<_>>());
+            check_results_specialized!(it, |i| {
+                let mut parameters_from_fold = vec![];
+                let fold_result = i.fold(vec![], |mut acc, v| {
+                    parameters_from_fold.push((acc.clone(), v.clone()));
+                    acc.push(v);
+                    acc
+                });
+                (parameters_from_fold, fold_result)
+            });
+            check_results_specialized!(it, |mut i| {
+                let mut parameters_from_all = vec![];
+                let first = i.next();
+                let all_result = i.all(|x| {
+                    parameters_from_all.push(x.clone());
+                    Some(x)==first
+                });
+                (parameters_from_all, all_result)
+            });
+            let size = it.clone().count();
+            for n in 0..size + 2 {
+                check_results_specialized!(it, |mut i| i.nth(n));
+            }
+        }
+    }
+}
diff --git a/tests/test_core.rs b/tests/test_core.rs
index 5861653..a7b7449 100644
--- a/tests/test_core.rs
+++ b/tests/test_core.rs
@@ -1,6 +1,6 @@
 //! Licensed under the Apache License, Version 2.0
-//! http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
-//! http://opensource.org/licenses/MIT, at your
+//! https://www.apache.org/licenses/LICENSE-2.0 or the MIT license
+//! https://opensource.org/licenses/MIT, at your
 //! option. This file may not be copied, modified, or distributed
 //! except according to those terms.
 #![no_std]
@@ -9,10 +9,13 @@
 use itertools as it;
 use crate::it::Itertools;
 use crate::it::interleave;
+use crate::it::intersperse;
+use crate::it::intersperse_with;
 use crate::it::multizip;
 use crate::it::free::put_back;
 use crate::it::iproduct;
 use crate::it::izip;
+use crate::it::chain;
 
 #[test]
 fn product2() {
@@ -88,6 +91,28 @@
 }
 
 #[test]
+fn chain_macro() {
+    let mut chain = chain!(2..3);
+    assert!(chain.next() == Some(2));
+    assert!(chain.next().is_none());
+
+    let mut chain = chain!(0..2, 2..3, 3..5i8);
+    for i in 0..5i8 {
+        assert_eq!(Some(i), chain.next());
+    }
+    assert!(chain.next().is_none());
+
+    let mut chain = chain!();
+    assert_eq!(chain.next(), Option::<()>::None);
+}
+
+#[test]
+fn chain2() {
+    let _ = chain!(1.., 2..);
+    let _ = chain!(1.., 2.., );
+}
+
+#[test]
 fn write_to() {
     let xs = [7, 9, 8];
     let mut ys = [0; 5];
@@ -113,6 +138,23 @@
     it::assert_equal(it, rs.iter());
 }
 
+#[test]
+fn test_intersperse() {
+    let xs = [1u8, 2, 3];
+    let ys = [1u8, 0, 2, 0, 3];
+    let it = intersperse(&xs, &0);
+    it::assert_equal(it, ys.iter());
+}
+
+#[test]
+fn test_intersperse_with() {
+    let xs = [1u8, 2, 3];
+    let ys = [1u8, 10, 2, 10, 3];
+    let i = 10;
+    let it = intersperse_with(&xs, || &i);
+    it::assert_equal(it, ys.iter());
+}
+
 #[allow(deprecated)]
 #[test]
 fn foreach() {
@@ -254,6 +296,14 @@
 }
 
 #[test]
+fn at_most_one() {
+    assert_eq!((0..10).filter(|&x| x == 2).at_most_one().unwrap(), Some(2));
+    assert!((0..10).filter(|&x| x > 1 && x < 4).at_most_one().unwrap_err().eq(2..4));
+    assert!((0..10).filter(|&x| x > 1 && x < 5).at_most_one().unwrap_err().eq(2..5));
+    assert_eq!((0..10).filter(|&_| false).at_most_one().unwrap(), None);
+}
+
+#[test]
 fn sum1() {
     let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
     assert_eq!(v[..0].iter().cloned().sum1::<i32>(), None);
diff --git a/tests/test_std.rs b/tests/test_std.rs
index d1ff815..2049d15 100644
--- a/tests/test_std.rs
+++ b/tests/test_std.rs
@@ -59,6 +59,39 @@
     assert_eq!(it.size_hint(), (6, Some(6)));
 }
 
+#[test]
+fn duplicates_by() {
+    let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"];
+    let ys = ["aa", "bbbb", "cccc"];
+    it::assert_equal(ys.iter(), xs.iter().duplicates_by(|x| x[..2].to_string()));
+    it::assert_equal(ys.iter(), xs.iter().rev().duplicates_by(|x| x[..2].to_string()).rev());
+    let ys_rev = ["ccc", "aa", "bbbbb"];
+    it::assert_equal(ys_rev.iter(), xs.iter().duplicates_by(|x| x[..2].to_string()).rev());
+}
+
+#[test]
+fn duplicates() {
+    let xs = [0, 1, 2, 3, 2, 1, 3];
+    let ys = [2, 1, 3];
+    it::assert_equal(ys.iter(), xs.iter().duplicates());
+    it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev());
+    let ys_rev = [3, 2, 1];
+    it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev());
+
+    let xs = [0, 1, 0, 1];
+    let ys = [0, 1];
+    it::assert_equal(ys.iter(), xs.iter().duplicates());
+    it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev());
+    let ys_rev = [1, 0];
+    it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev());
+
+    let xs = vec![0, 1, 2, 1, 2];
+    let ys = vec![1, 2];
+    assert_eq!(ys, xs.iter().duplicates().cloned().collect_vec());
+    assert_eq!(ys, xs.iter().rev().duplicates().rev().cloned().collect_vec());
+    let ys_rev = vec![2, 1];
+    assert_eq!(ys_rev, xs.iter().duplicates().rev().cloned().collect_vec());
+}
 
 #[test]
 fn unique_by() {
@@ -190,6 +223,13 @@
 }
 
 #[test]
+fn all_unique() {
+    assert!("ABCDEFGH".chars().all_unique());
+    assert!(!"ABCDEFGA".chars().all_unique());
+    assert!(::std::iter::empty::<usize>().all_unique());
+}
+
+#[test]
 fn test_put_back_n() {
     let xs = [0, 1, 1, 1, 2, 1, 3, 3];
     let mut pb = put_back_n(xs.iter().cloned());
@@ -471,6 +511,30 @@
 }
 
 #[test]
+fn sorted_by_cached_key() {
+    // Track calls to key function
+    let mut ncalls = 0;
+
+    let sorted = [3, 4, 1, 2].iter().cloned().sorted_by_cached_key(|&x| {
+        ncalls += 1;
+        x.to_string()
+    });
+    it::assert_equal(sorted, vec![1, 2, 3, 4]);
+    // Check key function called once per element
+    assert_eq!(ncalls, 4);
+
+    let mut ncalls = 0;
+
+    let sorted = (0..5).sorted_by_cached_key(|&x| {
+        ncalls += 1;
+        -x
+    });
+    it::assert_equal(sorted, vec![4, 3, 2, 1, 0]);
+    // Check key function called once per element
+    assert_eq!(ncalls, 5);
+}
+
+#[test]
 fn test_multipeek() {
     let nums = vec![1u8,2,3,4,5];
 
@@ -1047,3 +1111,12 @@
     [].iter().exactly_one()?;
     Ok(())
 }
+
+#[test]
+fn multiunzip() {
+    let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2), (3, 4, 5), (6, 7, 8)].iter().cloned().multiunzip();    
+    assert_eq!((a, b, c), (vec![0, 3, 6], vec![1, 4, 7], vec![2, 5, 8]));
+    let (): () = [(), (), ()].iter().cloned().multiunzip();
+    let t: (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)].iter().cloned().multiunzip();    
+    assert_eq!(t, (vec![0], vec![1], vec![2], vec![3], vec![4], vec![5], vec![6], vec![7], vec![8], vec![9], vec![10], vec![11]));
+}
\ No newline at end of file