Upgrade itertools to the latest version.

Bug: 339424309
Test: treehugger
Change-Id: I5e7a547b54a18140a1ec89163d00d0ddb0e6b5da
diff --git a/crates/itertools/.cargo-checksum.json b/crates/itertools/.cargo-checksum.json
index a4f2873..38bbe13 100644
--- a/crates/itertools/.cargo-checksum.json
+++ b/crates/itertools/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"CHANGELOG.md":"ed6c781d541c40d4a19eaecd794cadebb94b3f4d51e32367803542c88f0457ee","Cargo.toml":"6dcbab25126c0cdf64f5089156de0d4346914c6d47c557d370b8e20e039ca7d3","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","README.md":"3acfeb07424200ae70bf571ef63a96bae954c298bddf447c9bea0ea9394825cc","benches/bench1.rs":"bb06f39db0544b1380cd4929139ccf521a9eecab7ca3f910b9499f965ec0a047","benches/combinations.rs":"51523ee1ca438a56f14711f0b04ee943895062d35859fbe23a2714d2fca3289d","benches/combinations_with_replacement.rs":"11f29160652a2d90ce7ca4b1c339c4457888ab6867e2456ce1c62e3adf9be737","benches/extra/mod.rs":"6ca290d72302a1945078621610b5788060b0de29639decebbdc557a80044aa97","benches/extra/zipslices.rs":"40e9f68a7c00f8429193fca463caef18851fa49b33355cc136bad3ccc840d655","benches/fold_specialization.rs":"5a517bbe29d366a15f6f751660e17ab1aa3e7b21552a1983048c662e34f0d69e","benches/powerset.rs":"6fd9d69a3483b37dc2411f99fb4efa6131577696f2dbdc8d1de9e4d7642fe3a3","benches/tree_fold1.rs":"539232e74f9aaea295a42069ac5af707811e90dc1c71c6e0a9064ffc731999de","benches/tuple_combinations.rs":"16366158743307a0289fc1df423a3cec45009807d410a9fe9922d5b6f8b7d002","benches/tuples.rs":"5a620783ae203e9ff9623d10d2c7fe9911d8b6c811cbad7613afa30e390c759d","clippy.toml":"33ffb83bbddb772575b3aa565b7136a8158ee386c216ffc2588fed9e83fa3826","examples/iris.data":"596ffd580471ca4d4880f8e439c7281f3b50d8249a5960353cb200b1490f63a0","examples/iris.rs":"1b465ed6a417180913104bc95a545fd9d1a3d67d121871ab737ad87e31b8be37","src/adaptors/coalesce.rs":"a0073325d40f297d29101538d18a267aef81889a999338dc09cb43a31cb4ec8b","src/adaptors/map.rs":"241971e856e468d71323071fb4a09867fbcedb83877320be132dc03516fe60e8","src/adaptors/mod.rs":"7f3bd7d011a348ce5e4bea486ef2e6346b64c7fe27540334d56d3f147f981d59","src/adaptors/multi_product.rs":"bb43e6dce68c815c21006d5b01c56e038d54b0c3bb8ee6bb8a4de11e2952c7ad","src/combinations.rs":"fb25babb459389093f886721016c72bf9f00e51d02735f638d871bb3a447ffd0","src/combinations_with_replacement.rs":"463011a574facbdd84278386b533a90e4dd517f0417e05adb82d182049db1f50","src/concat_impl.rs":"03b1ed61cbed242c286c3c4c5c848dbd57e02ab83fcef264f3a592b58107f324","src/cons_tuples_impl.rs":"c253d03b861831c01d62cacc57b49715ee62f6171e69f6886bb5a6ca0863bc3a","src/diff.rs":"a7800e9ce7a87b53ebe2338481335751fb43d44fa6a1ca719aceaaab40e5c8fe","src/duplicates_impl.rs":"f62fe4b642f501f785721ce5a505cf622a771e457210726dd0fb8b30be7ebbbc","src/either_or_both.rs":"76b13fbfac6bc959b4c1d8b7c99ce51726e95f994ca5429477e523a3d3950e4a","src/exactly_one_err.rs":"aa50081f6a31b5109b30e3ed305e3ec2413c6908dedc8990ec5378a99cee2b39","src/extrema_set.rs":"2a25b0b86eed2fd5d05622d591a3085cab823973d450816c2c3b8cb76e9c187e","src/flatten_ok.rs":"fe209fd886ecd9cb98d99625aa0c7274af7e644eff4a10de15b4dec8bbbc934a","src/format.rs":"a8192d85c0f9de8e633c202456e3cde0f3bc50f19b6bd8a4b2cfa3ef5123de1a","src/free.rs":"dfc57b7f56a08d4986a96b679018b41346576a7a34b668e008cc01109e728750","src/group_map.rs":"f7b02c964f63505d3e36280cfdc1755e05287714201efe983dacf702eee61434","src/groupbylazy.rs":"4f2181c022a45ff8444597708861fc6863eceb6f7555ea81cf3eeba19b492971","src/grouping_map.rs":"cbc45ac563345c96f3ac50c78f73c83d870523436a7ab88c1c9a685d204461d3","src/impl_macros.rs":"4f829b458873bed556f1aff2ae4e88dbd576766e2b5bcc07ff3ac8756758e6f4","src/intersperse.rs":"b9717242495846a4a979c95d93d5681caccb7c07a0d889eab763ad3d49a46125","src/k_smallest.rs":"603eb34314c01769ff7f6def2a24cf7a7b38507e6f3658b7aafc23a3b2e9b322","src/kmerge_impl.rs":"a347b0f6fa7715afd8a54d85ce139ed5b14c9e58a16c2b3648f5b288fdb5375f","src/lazy_buffer.rs":"834f6ef7fdf9f00c8a6329beb38eaefb706847ceeec309c221dce705c2c1e05b","src/lib.rs":"fadb0045279aafe8e8cccb45fadc383c7b358197b83c9c38fba87ada4cb2f84a","src/merge_join.rs":"1016113f6c983a9498bae5dc0570190437e1357b3333f6e19ea95c88599a1225","src/minmax.rs":"96d3897c28c8c63284d4729becc9ada6855e0953cac6e1bd35cf6f38c50b0ec0","src/multipeek_impl.rs":"35162bca4456bfa20a08e8d40e4d1cc6783dc662778789fdcded60371e975122","src/pad_tail.rs":"04be2ca73abb85815b06b5524c99d6feb2919180c486a4646f9cc6c87462f67b","src/peek_nth.rs":"6a0a51f2f373ce14d3d58595c46464878a14976bf00841a7396c03f9f9ab07ac","src/peeking_take_while.rs":"2b1b77c8882be32cfd76e973d303aa62f73370efd470c60764add0cdcca524d5","src/permutations.rs":"97831e7e26904c3cae68c97e74f7c6981ceb2fb2f2217282a0e5e54083a565fc","src/powerset.rs":"e0ee6b1316b4dd314c1e81502b90ae8113e1cda12168322520c5a65410e584b2","src/process_results_impl.rs":"9ed7fa46c8316238272ef47577387a386c1a109b50377dd3caf4291b6587cb73","src/put_back_n_impl.rs":"821e047fecd6ca0036290029f4febe7638a3abf1faa05e1e747a3bf9d80ff464","src/rciter_impl.rs":"5b156082ef2d25a94a4ad01d94cba2813c4b3e72e212515a8ad0fc8588f8045d","src/repeatn.rs":"bfc8f9145c9d8a3ea651f012b7d5a8d2fbbcbefdee76eafd098d02e7c54cda90","src/size_hint.rs":"021e57aad7df8f1e70ef588e9e9b8a1695aab183b1098f1848561f96c5dc9bcb","src/sources.rs":"61637f32c2cea2290ecfc1980c0b2d0f68463839ac09bd81006f8258ab8ecaae","src/tee.rs":"665832aa547389a420c3441470ff2494249f0ed2841be0c6a578367fe9dbd381","src/tuple_impl.rs":"00a9b61942425fb477b9691c3348646c0f9f534ff94f6321027f38c61ce2478c","src/unique_impl.rs":"3b89cdd668b74cc0a0eabb1522489e2305a0d2d8da25d6a1884e8626bbdb5959","src/unziptuple.rs":"84b50e5d29b9ddbf21a46a1cc2fd7877729c7f7da9bdc8ae1966dbaf2d2f6f60","src/with_position.rs":"c8a9b3476b3b90986b004a8877c19ff54b4c6800c5ac7ca1458d914036dacfe9","src/zip_eq_impl.rs":"4a41dc6dfe99359585d50ce648bdc85f15276c602048872b1d152e90841d8cad","src/zip_longest.rs":"f7cf5fffc3ca053ee80b410a05b27de1a475021f6de3181aea981010d7e8453f","src/ziptuple.rs":"7f9df12bf6556f382bbd4ad8cf17eb8b60c1c47fadbce016141133ba0f3384a1","tests/adaptors_no_collect.rs":"f459f36d54f5d475b2b2e83f5a1c98109c15062756ae822fa379486f3eeed666","tests/flatten_ok.rs":"b7894874132918b8229c7150b2637511d8e3e14197d8eeb9382d46b2a514efa2","tests/macros_hygiene.rs":"522afa0106e3f11a5149e9218f89c2329e405546d2ef0ea756d6a27e8a0e9ca3","tests/merge_join.rs":"b08c4ee6529d234c68d411a413b8781455d18a1eab17872d1828bb75a4fcf79b","tests/peeking_take_while.rs":"4b1c394e44a9ef9bc0de707ae080b45803db722f79834c20f15b826d7c3f1f2e","tests/quick.rs":"6fcc0649b9270f024b169b1f499dd4cc7fecb0c9aec0dfc155b264916cc626e7","tests/specializations.rs":"fdd16dc663330033fedcc478609b393d4aa369dc07dc8cda31a75219fb793087","tests/test_core.rs":"32576ba90aa8e5db985b6e6ffe30e3046bc6a11d392db8f6b4bdd2ba48d9b24d","tests/test_std.rs":"f28a78a1912c950e7c37be1e82867e70dc585d60afecdebc7a97965194eee8e6","tests/tuples.rs":"014e4da776174bfe923270e2a359cd9c95b372fce4b952b8138909d6e2c52762","tests/zip.rs":"99af365fe6054ef1c6089d3e604e34da8fea66e55861ae4be9e7336ec8de4b56"},"package":"b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"}
\ No newline at end of file
+{"files":{"CHANGELOG.md":"ceee4376468a3f7647f3bf4649e195a86873dd3091f23e3f992d248bd143fba2","CONTRIBUTING.md":"d5787d0fd4df15481e2e09a37234ac5dec22c007c890826991f633d890efa29e","Cargo.lock":"fd2c9ca8e299f51d7ed2a0f3760c393f03c544c817743ab7341c1f22b8c1d869","Cargo.toml":"49abb2101a0dd9cb137df206454b6620d04929a4975921fab6682ba834435620","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","README.md":"fc812ab0d5756b62c2ae34f38365899204b53332d5e6a87a695b0fe15a466957","benches/bench1.rs":"d632c8b839d7b318d1cb7b81b9c62570c77dcdf0696b8ce3d52067c79c930f78","benches/combinations.rs":"5b3bd243336d6b6bdc111d66218f3f0a4ecdb10fb72e90db79959e3d8bb2cf6f","benches/combinations_with_replacement.rs":"11f29160652a2d90ce7ca4b1c339c4457888ab6867e2456ce1c62e3adf9be737","benches/fold_specialization.rs":"66ab13fd8576a662afb59ef72c5565f5c3d27f7f30a976450ee5a14958654fa2","benches/powerset.rs":"dc1fd729584147e5d8e4d19c6ca6f8706087d41c3c5beb7293d9ea43b4beab14","benches/specializations.rs":"d8320071a692147c1239881725079003be2f924f6124c3aa3bdf6a4596d66a66","benches/tree_reduce.rs":"fa4f22f042b76df89094ddf6e925ba42c4c3992f8195e719ed035f2e7cfa05bd","benches/tuple_combinations.rs":"16366158743307a0289fc1df423a3cec45009807d410a9fe9922d5b6f8b7d002","benches/tuples.rs":"5ab542aca40df4390de0ebf3819665df402d924a7dd6f4280e6ffc942bbd25c4","examples/iris.data":"596ffd580471ca4d4880f8e439c7281f3b50d8249a5960353cb200b1490f63a0","examples/iris.rs":"42c1b2fc148df52a050b013a57b577ad19911f1fe85b9525863df501979b5cd1","src/adaptors/coalesce.rs":"b57157c205ae077dd398740b61c7f49023aa80868abd8a071a6fe89ae6ecc9ad","src/adaptors/map.rs":"4952ee770cb54e98b2f649efd9c98f18951689358eb9b6bee10f139d056353ae","src/adaptors/mod.rs":"7064a1043baec815c02803d5043bd950e6a515f3a0247e44028ee080004dc225","src/adaptors/multi_product.rs":"ad501e8ae4e5089b9d2f2be1f9a4713da6a2103b14daa759e09918409f88e321","src/combinations.rs":"6c1cd55051eb59c595780b055ccabb07db72add134120dd8b2f5aa60c0f5fa6e","src/combinations_with_replacement.rs":"cad1885ca51e52a1dc324a0b06bd0d1d911f1dd58cf5d76bd9a9c78a09853b86","src/concat_impl.rs":"6094463eb57f77e115f6a3fe7f469992eef81c0c4caa9585b99a426d87f794fb","src/cons_tuples_impl.rs":"3ceee1ff0dbd4c3b43195a490b8f38b05de3a46e0fb691ba11fbbe1e7e3ad746","src/diff.rs":"046b3ac4a22036b9ec8741aba4e8f6729ae44bf14346b61c23192b88d9fc7c88","src/duplicates_impl.rs":"1be37249b4566edc8da611ed9766ec851a526e7513bd13d80fe97482dcfcf7f3","src/either_or_both.rs":"cac278666b5d3c1fd103d97d15ce4c40960ea459441aeae83c6502087fd2ad8d","src/exactly_one_err.rs":"90b6204551161d27394af72107765dbfe3b51a77f4770c2e506fa4938985a184","src/extrema_set.rs":"7e0d92ca1aafc1221e08d0297087b35373463d03228a0e65628cfd1734273e90","src/flatten_ok.rs":"62c18e5221a27949a00de49414306d6dfd601515817c1c8ae6189e3275756dd3","src/format.rs":"94675a6ac4500ec52bbf8463b2241b870fea8b5dd6b113accb8a00b2c1174871","src/free.rs":"6f3597a5ccf8a9b0606da7df6803f7368152ebcf7b7bcfd31b17fcff3a286139","src/group_map.rs":"c9da201137c6bb479b9308bfc38398b76950e39905f4ce8bc435c5318371522c","src/groupbylazy.rs":"5862629719258703aad47977ba1060f20fff15e962e18e6142758ebf6cd4a61c","src/grouping_map.rs":"8dac807a6cbf1893fdc147b4160000c452bfb5e533e1c774ed6bd3af91cf46da","src/impl_macros.rs":"97fc5f39574805e0c220aa462cf1ae7dcac5c1082d6ee5500e7d71c120db5f88","src/intersperse.rs":"55031819e985c3184275e254c9600ecbe01e9fb49f198039c5da82a87ea5b90e","src/iter_index.rs":"1b0ff8376a4ad855d44db8c662450c777db84e0f4997b53ca575c65b107bb83b","src/k_smallest.rs":"6a665742f6665e350a54ae3ff821252e7c599b57aee3239a03fa56a9d1930467","src/kmerge_impl.rs":"2e425d4189898566c5146e8f5bd258045c246f6babbe3ac5fef10ca08ae2efd2","src/lazy_buffer.rs":"a065f73c228f156bdf901824977ea9375f912823af4f9b05378e3f633d3b20e4","src/lib.rs":"75903dcd21573a8a77a205cfb8d335c60c2939771481c6431c29a0918d8dbfb0","src/merge_join.rs":"bb1fccddcc647fe21da1895a8808c06596d49900f5cf60a69a9c9141fc12af11","src/minmax.rs":"0ec34b172ca8efc4aacb96f3e5771bdc5e8ac882876ee0f59d698c3924717c48","src/multipeek_impl.rs":"79eef0be49ad66f15d41808e72c03976c4f7cff5838b69d17975d3ece266f3f8","src/pad_tail.rs":"e6bb5b086478600b0dbb8726cae8364bf83ab36d989ef467e1264eea43933b50","src/peek_nth.rs":"093f1a157b1c917f041af5244a5a46311affa2922126e36dc0ee2c501c79b58c","src/peeking_take_while.rs":"6967ba212f045145da7683a192471b2dcfcedf90d23922d70a5b7e2a1b36622e","src/permutations.rs":"b316084ee14e9e138d22f177367b3bfa24cb3e5e90ab20b9b00a9a23d653496f","src/powerset.rs":"7ab24fefc914b339dd92a6c8e639d0cad34479e09293b3346078856d6bc02d34","src/process_results_impl.rs":"a6f91aec53c56b042e15ecb8f8ca489c81e3ee92347dc9fa8352a5baac44a247","src/put_back_n_impl.rs":"5a58d7a31c03029f0726e4d42de3be869580cf76b73c6d1ef70dd40c240b03a0","src/rciter_impl.rs":"9a50cdc0106587be8ee49c2af5fcf84436b74d353c2846b401eb638c23b4733c","src/repeatn.rs":"dd9a5bf5a63ef9cc6ec5c8a6137c7ffba80f13568b6d001e189daaa29ffbaf39","src/size_hint.rs":"6022c2327ddc6df7e7b939eb60a93ee66ea9aa4d3aab49b9952e663ff4bff10b","src/sources.rs":"ef942af209ca1effcd28a95abedad8c45b659ae2a15b66c2158cb604f6e325f8","src/take_while_inclusive.rs":"1973a9f5322b3dae3b5ccded5912a08a8e2e975b9a5eac666192b118b230d305","src/tee.rs":"dad50ca162627cf0a67786f0993ef27d06cdefc14d412463e58c07824ef409d8","src/tuple_impl.rs":"0213261109e7c65746ccc22425d19141907bf7ea1e3dd4c40e9f278e6148e272","src/unique_impl.rs":"1efc280226f13ddd7dd5f7eedeec0093b704596652c942f3a0b2f8c90fa2e2f7","src/unziptuple.rs":"f3f6a2ee2658fa07db7592f2c344c2e3b1263a21fc75e1325f2be32c9dc1e750","src/with_position.rs":"9ca1eb195d04690b0c3a62a6c0eea349b8042e11c4ca4b80744f54103e1c7355","src/zip_eq_impl.rs":"4e0d38266c26982ea8b8d055994cb1298e93b7749caadbd7f25d2b6e0c8ce0d7","src/zip_longest.rs":"5572699564dd5717cc074b7733333ed238c2e9f3e6819d45e33e3a2dbda74478","src/ziptuple.rs":"d3a12221d39c8a5514574adb3ad2ccd1803d514b1cb09fbcd9253e3ddd628310","tests/adaptors_no_collect.rs":"7e6240878b1fc13b6384fdde0317d5d7ccca3e417b10a201ba61eb5255400fda","tests/flatten_ok.rs":"b7894874132918b8229c7150b2637511d8e3e14197d8eeb9382d46b2a514efa2","tests/laziness.rs":"89e6caec10da3d7aeadf9e30d5caf03cda36d07cee8415ff134b5b8e2a2cf144","tests/macros_hygiene.rs":"c9e9f0546a8c12ea52311c0eadd77d75c782d4e10ae9e74d410ea2861d526c66","tests/merge_join.rs":"5fb506b989f4a331d46cdec5775ea594656985134196099eaf8d3905bdddcdd5","tests/peeking_take_while.rs":"f834361c5520dda15eb9e9ebe87507c905462201412b21859d9f83dab91d0e0b","tests/quick.rs":"60b1ca6d820aa505545f20d6082fd08c1e0470b5326b711567ec1c93d07f9ced","tests/specializations.rs":"7c6a461850a2b4f783801ef23b2303ad985c58f2295c569001369b3c9d4c6e33","tests/test_core.rs":"482e077e0c5fe78ba0a8a126d8c0821162d820a21936855fadede713b1d4e70a","tests/test_std.rs":"f788573adc9ae19eb4bd2886c3967b273dd881982af407f6f5b6276434df0f00","tests/tuples.rs":"014e4da776174bfe923270e2a359cd9c95b372fce4b952b8138909d6e2c52762","tests/zip.rs":"2f68d531170fa2f106efafaf38ae854281d93305bf1b2b8d4bea833072518ecd"},"package":"413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"}
\ No newline at end of file
diff --git a/crates/itertools/Android.bp b/crates/itertools/Android.bp
index bf20315..18776c3 100644
--- a/crates/itertools/Android.bp
+++ b/crates/itertools/Android.bp
@@ -18,7 +18,7 @@
     host_supported: true,
     crate_name: "itertools",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.10.5",
+    cargo_pkg_version: "0.13.0",
     crate_root: "src/lib.rs",
     edition: "2018",
     features: [
diff --git a/crates/itertools/CHANGELOG.md b/crates/itertools/CHANGELOG.md
index d2b40b5..de9564c 100644
--- a/crates/itertools/CHANGELOG.md
+++ b/crates/itertools/CHANGELOG.md
@@ -1,5 +1,161 @@
 # Changelog
 
+## 0.13.0
+
+### Breaking
+- Removed implementation of `DoubleEndedIterator` for `ConsTuples` (#853)
+- Made `MultiProduct` fused and fixed on an empty iterator (#835, #834)
+- Changed `iproduct!` to return tuples for maxi one iterator too (#870)
+- Changed `PutBack::put_back` to return the old value (#880)
+- Removed deprecated `repeat_call, Itertools::{foreach, step, map_results, fold_results}` (#878)
+- Removed `TakeWhileInclusive::new` (#912)
+
+### Added
+- Added `Itertools::{smallest_by, smallest_by_key, largest, largest_by, largest_by_key}` (#654, #885)
+- Added `Itertools::tail` (#899)
+- Implemented `DoubleEndedIterator` for `ProcessResults` (#910)
+- Implemented `Debug` for `FormatWith` (#931)
+- Added `Itertools::get` (#891)
+
+### Changed
+- Deprecated `Itertools::group_by` (renamed `chunk_by`) (#866, #879)
+- Deprecated `unfold` (use `std::iter::from_fn` instead) (#871)
+- Optimized `GroupingMapBy` (#873, #876)
+- Relaxed `Fn` bounds to `FnMut` in `diff_with, Itertools::into_group_map_by` (#886)
+- Relaxed `Debug/Clone` bounds for `MapInto` (#889)
+- Documented the `use_alloc` feature (#887)
+- Optimized `Itertools::set_from` (#888)
+- Removed badges in `README.md` (#890)
+- Added "no-std" categories in `Cargo.toml` (#894)
+- Fixed `Itertools::k_smallest` on short unfused iterators (#900)
+- Deprecated `Itertools::tree_fold1` (renamed `tree_reduce`) (#895)
+- Deprecated `GroupingMap::fold_first` (renamed `reduce`) (#902)
+- Fixed `Itertools::k_smallest(0)` to consume the iterator, optimized `Itertools::k_smallest(1)` (#909)
+- Specialized `Combinations::nth` (#914)
+- Specialized `MergeBy::fold` (#920)
+- Specialized `CombinationsWithReplacement::nth` (#923)
+- Specialized `FlattenOk::{fold, rfold}` (#927)
+- Specialized `Powerset::nth` (#924)
+- Documentation fixes (#882, #936)
+- Fixed `assert_equal` for iterators longer than `i32::MAX` (#932)
+- Updated the `must_use` message of non-lazy `KMergeBy` and `TupleCombinations` (#939)
+
+### Notable Internal Changes
+- Tested iterator laziness (#792)
+- Created `CONTRIBUTING.md` (#767)
+
+## 0.12.1
+
+### Added
+- Documented iteration order guarantee for `Itertools::[tuple_]combinations` (#822)
+- Documented possible panic in `iterate` (#842)
+- Implemented `Clone` and `Debug` for `Diff` (#845)
+- Implemented `Debug` for `WithPosition` (#859)
+- Implemented `Eq` for `MinMaxResult` (#838)
+- Implemented `From<EitherOrBoth<A, B>>` for `Option<Either<A, B>>` (#843)
+- Implemented `PeekingNext` for `RepeatN` (#855)
+
+### Changed
+- Made `CoalesceBy` lazy (#801)
+- Optimized `Filter[Map]Ok::next`, `Itertools::partition`, `Unique[By]::next[_back]` (#818)
+- Optimized `Itertools::find_position` (#837)
+- Optimized `Positions::next[_back]` (#816)
+- Optimized `ZipLongest::fold` (#854)
+- Relaxed `Debug` bounds for `GroupingMapBy` (#860)
+- Specialized `ExactlyOneError::fold` (#826)
+- Specialized `Interleave[Shortest]::fold` (#849)
+- Specialized `MultiPeek::fold` (#820)
+- Specialized `PadUsing::[r]fold` (#825)
+- Specialized `PeekNth::fold` (#824)
+- Specialized `Positions::[r]fold` (#813)
+- Specialized `PutBackN::fold` (#823)
+- Specialized `RepeatN::[r]fold` (#821)
+- Specialized `TakeWhileInclusive::fold` (#851)
+- Specialized `ZipLongest::rfold` (#848)
+
+### Notable Internal Changes
+- Added test coverage in CI (#847, #856)
+- Added semver check in CI (#784)
+- Enforced `clippy` in CI (#740)
+- Enforced `rustdoc` in CI (#840)
+- Improved specialization tests (#807)
+- More specialization benchmarks (#806)
+
+## 0.12.0
+
+### Breaking
+- Made `take_while_inclusive` consume iterator by value (#709)
+- Added `Clone` bound to `Unique` (#777)
+
+### Added
+- Added `Itertools::try_len` (#723)
+- Added free function `sort_unstable` (#796)
+- Added `GroupMap::fold_with` (#778, #785)
+- Added `PeekNth::{peek_mut, peek_nth_mut}` (#716)
+- Added `PeekNth::{next_if, next_if_eq}` (#734)
+- Added conversion into `(Option<A>,Option<B>)` to `EitherOrBoth` (#713)
+- Added conversion from `Either<A, B>` to `EitherOrBoth<A, B>` (#715)
+- Implemented `ExactSizeIterator` for `Tuples` (#761)
+- Implemented `ExactSizeIterator` for `(Circular)TupleWindows` (#752)
+- Made `EitherOrBoth<T>` a shorthand for `EitherOrBoth<T, T>` (#719)
+
+### Changed
+- Added missing `#[must_use]` annotations on iterator adaptors (#794)
+- Made `Combinations` lazy (#795)
+- Made `Intersperse(With)` lazy (#797)
+- Made `Permutations` lazy (#793)
+- Made `Product` lazy (#800)
+- Made `TupleWindows` lazy (#602)
+- Specialized `Combinations::{count, size_hint}` (#729)
+- Specialized `CombinationsWithReplacement::{count, size_hint}` (#737)
+- Specialized `Powerset::fold` (#765)
+- Specialized `Powerset::count` (#735)
+- Specialized `TupleCombinations::{count, size_hint}` (#763)
+- Specialized `TupleCombinations::fold` (#775)
+- Specialized `WhileSome::fold` (#780)
+- Specialized `WithPosition::fold` (#772)
+- Specialized `ZipLongest::fold` (#774)
+- Changed `{min, max}_set*` operations require `alloc` feature, instead of `std` (#760)
+- Improved documentation of `tree_fold1` (#787)
+- Improved documentation of `permutations` (#724)
+- Fixed typo in documentation of `multiunzip` (#770)
+
+### Notable Internal Changes
+- Improved specialization tests (#799, #786, #782)
+- Simplified implementation of `Permutations` (#739, #748, #790)
+- Combined `Merge`/`MergeBy`/`MergeJoinBy` implementations (#736)
+- Simplified `Permutations::size_hint` (#739)
+- Fix wrapping arithmetic in benchmarks (#770)
+- Enforced `rustfmt` in CI (#751)
+- Disallowed compile warnings in CI (#720)
+- Used `cargo hack` to check MSRV (#754)
+
+## 0.11.0
+
+### Breaking
+- Make `Itertools::merge_join_by` also accept functions returning bool (#704)
+- Implement `PeekingNext` transitively over mutable references (#643)
+- Change `with_position` to yield `(Position, Item)` instead of `Position<Item>` (#699)
+
+### Added
+- Add `Itertools::take_while_inclusive` (#616)
+- Implement `PeekingNext` for `PeekingTakeWhile` (#644)
+- Add `EitherOrBoth::{just_left, just_right, into_left, into_right, as_deref, as_deref_mut, left_or_insert, right_or_insert, left_or_insert_with, right_or_insert_with, insert_left, insert_right, insert_both}` (#629)
+- Implement `Clone` for `CircularTupleWindows` (#686)
+- Implement `Clone` for `Chunks` (#683)
+- Add `Itertools::process_results` (#680)
+
+### Changed
+- Use `Cell` instead of `RefCell` in `Format` and `FormatWith` (#608)
+- CI tweaks (#674, #675)
+- Document and test the difference between stable and unstable sorts (#653)
+- Fix documentation error on `Itertools::max_set_by_key` (#692)
+- Move MSRV metadata to `Cargo.toml` (#672)
+- Implement `equal` with `Iterator::eq` (#591)
+
+## 0.10.5
+  - Maintenance
+
 ## 0.10.4
   - Add `EitherOrBoth::or` and `EitherOrBoth::or_else` (#593)
   - Add `min_set`, `max_set` et al. (#613, #323)
@@ -7,6 +163,9 @@
   - Documentation fixes (#612, #625, #632, #633, #634, #638)
   - Code maintenance (#623, #624, #627, #630)
 
+## 0.10.3
+  - Maintenance
+
 ## 0.10.2
   - Add `Itertools::multiunzip` (#362, #565)
   - Add `intersperse` and `intersperse_with` free functions (#555)
diff --git a/crates/itertools/CONTRIBUTING.md b/crates/itertools/CONTRIBUTING.md
new file mode 100644
index 0000000..1dbf6f5
--- /dev/null
+++ b/crates/itertools/CONTRIBUTING.md
@@ -0,0 +1,189 @@
+# Contributing to itertools
+
+We use stable Rust only.
+Please check the minimum version of Rust we use in `Cargo.toml`.
+
+_If you are proposing a major change to CI or a new iterator adaptor for this crate,
+then **please first file an issue** describing your proposal._
+[Usual concerns about new methods](https://github.com/rust-itertools/itertools/issues/413#issuecomment-657670781).
+
+To pass CI tests successfully, your code must be free of "compiler warnings" and "clippy warnings" and be "rustfmt" formatted.
+
+Note that small PRs are easier to review and therefore are more easily merged.
+
+## Write a new method/adaptor for `Itertools` trait
+In general, the code logic should be tested with [quickcheck](https://crates.io/crates/quickcheck) tests in `tests/quick.rs`
+which allow us to test properties about the code with randomly generated inputs.
+
+### Behind `use_std`/`use_alloc` feature?
+If it needs the "std" (such as using hashes) then it should be behind the `use_std` feature,
+or if it requires heap allocation (such as using vectors) then it should be behind the `use_alloc` feature.
+Otherwise it should be able to run in `no_std` context.
+
+This mostly applies to your new module, each import from it, and to your new `Itertools` method.
+
+### Pick the right receiver
+`self`, `&mut self` or `&self`? From [#710](https://github.com/rust-itertools/itertools/pull/710):
+
+- Take by value when:
+    - It transfers ownership to another iterator type, such as `filter`, `map`...
+    - It consumes the iterator completely, such as `count`, `last`, `max`...
+- Mutably borrow when it consumes only part of the iterator, such as `find`, `all`, `try_collect`...
+- Immutably borrow when there is no change, such as `size_hint`.
+
+### Laziness
+Iterators are [lazy](https://doc.rust-lang.org/std/iter/index.html#laziness):
+
+- structs of iterator adaptors should have `#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]` ;
+- structs of iterators should have `#[must_use = "iterators are lazy and do nothing unless consumed"]`.
+
+Those behaviors are **tested** in `tests/laziness.rs`.
+
+## Specialize `Iterator` methods
+It might be more performant to specialize some methods.
+However, each specialization should be thoroughly tested.
+
+Correctly specializing methods can be difficult, and _we do not require that you do it on your initial PR_.
+
+Most of the time, we want specializations of:
+
+- [`size_hint`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.size_hint):
+  It mostly allows allocation optimizations.
+  When always exact, it also enables to implement `ExactSizeIterator`.
+  See our private module `src/size_hint.rs` for helpers.
+- [`fold`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold)
+  might make iteration faster than calling `next` repeatedly.
+- [`count`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.count),
+  [`last`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.last),
+  [`nth`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.nth)
+  as we might be able to avoid iterating on every item with `next`.
+
+Additionally,
+
+- `for_each`, `reduce`, `max/min[_by[_key]]` and `partition` all rely on `fold` so you should specialize it instead.
+- `all`, `any`, `find`, `find_map`, `cmp`, `partial_cmp`, `eq`, `ne`, `lt`, `le`, `gt`, `ge` and `position` all rely (by default) on `try_fold`
+  which we can not specialize on stable rust, so you might want to wait it stabilizes
+  or specialize each of them.
+- `DoubleEndedIterator::{nth_back, rfold, rfind}`: similar reasoning.
+
+An adaptor might use the inner iterator specializations for its own specializations.
+
+They are **tested** in `tests/specializations.rs` and **benchmarked** in `benches/specializations.rs`
+(build those benchmarks is slow so you might want to temporarily remove the ones you do not want to measure).
+
+## Additional implementations
+### The [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) implementation
+All our iterators should implement `Debug`.
+
+When one of the field is not debuggable (such as _functions_), you must not derive `Debug`.
+Instead, manually implement it and _ignore this field_ in our helper macro `debug_fmt_fields`.
+
+<details>
+<summary>4 examples (click to expand)</summary>
+
+```rust
+use std::fmt;
+
+/* ===== Simple derive. ===== */
+#[derive(Debug)]
+struct Name1<I> {
+    iter: I,
+}
+
+/* ===== With an unclonable field. ===== */
+struct Name2<I, F> {
+    iter: I,
+    func: F,
+}
+
+// No `F: Debug` bound and the field `func` is ignored.
+impl<I: fmt::Debug, F> fmt::Debug for Name2<I, F> {
+    // it defines the `fmt` function from a struct name and the fields you want to debug.
+    debug_fmt_fields!(Name2, iter);
+}
+
+/* ===== With an unclonable field, but another bound to add. ===== */
+struct Name3<I: Iterator, F> {
+    iter: I,
+    item: Option<I::Item>,
+    func: F,
+}
+
+// Same about `F` and `func`, similar about `I` but we must add the `I::Item: Debug` bound.
+impl<I: Iterator + fmt::Debug, F> fmt::Debug for Name3<I, F>
+where
+    I::Item: fmt::Debug,
+{
+    debug_fmt_fields!(Name3, iter, item);
+}
+
+/* ===== With an unclonable field for which we can provide some information. ===== */
+struct Name4<I, F> {
+    iter: I,
+    func: Option<F>,
+}
+
+// If ignore a field is not good enough, implement Debug fully manually.
+impl<I: fmt::Debug, F> fmt::Debug for Name4<I, F> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let func = if self.func.is_some() { "Some(_)" } else { "None" };
+        f.debug_struct("Name4")
+            .field("iter", &self.iter)
+            .field("func", &func)
+            .finish()
+    }
+}
+```
+</details>
+
+### When/How to implement [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html)
+All our iterators should implement `Clone` when possible.
+
+Note that a mutable reference is never clonable so `struct Name<'a, I: 'a> { iter: &'a mut I }` can not implement `Clone`.
+
+Derive `Clone` on a generic struct adds the bound `Clone` on each generic parameter.
+It might be an issue in which case you should manually implement it with our helper macro `clone_fields` (it defines the `clone` function calling `clone` on each field) and be careful about the bounds.
+
+### When to implement [`std::iter::FusedIterator`](https://doc.rust-lang.org/std/iter/trait.FusedIterator.html)
+This trait should be implemented _by all iterators that always return `None` after returning `None` once_, because it allows to optimize `Iterator::fuse()`.
+
+The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, eventually refined to ensure it behaves in a fused way.
+
+### When to implement [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html)
+_When we are always able to return an exact non-overflowing length._
+
+Therefore, we do not implement it on adaptors that makes the iterator longer as the resulting length could overflow.
+
+One should not override `ExactSizeIterator::len` method but rely on an exact `Iterator::size_hint` implementation, meaning it returns `(length, Some(length))` (unless you could make `len` more performant than the default).
+
+The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure the size hint is exact.
+
+### When to implement [`DoubleEndedIterator`](https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html)
+When the iterator structure allows to handle _iterating on both fronts simultaneously_.
+The iteration might stop in the middle when both fronts meet.
+
+The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure we can iterate on both fronts simultaneously.
+
+### When to implement [`itertools::PeekingNext`](https://docs.rs/itertools/latest/itertools/trait.PeekingNext.html)
+TODO
+
+This is currently **tested** in `tests/test_std.rs`.
+
+## About lending iterators
+TODO
+
+
+## Other notes
+No guideline about using `#[inline]` yet.
+
+### `.fold` / `.for_each` / `.try_fold` / `.try_for_each`
+In the Rust standard library, it's quite common for `fold` to be implemented in terms of `try_fold`. But it's not something we do yet because we can not specialize `try_fold` methods yet (it uses the unstable `Try`).
+
+From [#781](https://github.com/rust-itertools/itertools/pull/781), the general rule to follow is something like this:
+
+- If you need to completely consume an iterator:
+  - Use `fold` if you need an _owned_ access to an accumulator.
+  - Use `for_each` otherwise.
+- If you need to partly consume an iterator, the same applies with `try_` versions:
+  - Use `try_fold` if you need an _owned_ access to an accumulator.
+  - Use `try_for_each` otherwise.
diff --git a/crates/itertools/Cargo.lock b/crates/itertools/Cargo.lock
index b44809e..d2183c2 100644
--- a/crates/itertools/Cargo.lock
+++ b/crates/itertools/Cargo.lock
@@ -18,10 +18,15 @@
 checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
 
 [[package]]
-name = "anstyle"
-version = "1.0.8"
+name = "atty"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
 
 [[package]]
 name = "autocfg"
@@ -30,18 +35,18 @@
 checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
 
 [[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
 name = "bumpalo"
 version = "3.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
-[[package]]
 name = "cast"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -82,44 +87,40 @@
 
 [[package]]
 name = "clap"
-version = "4.5.16"
+version = "3.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
+checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
 dependencies = [
- "clap_builder",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.5.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
-dependencies = [
- "anstyle",
+ "bitflags",
  "clap_lex",
+ "indexmap",
+ "textwrap",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.7.2"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
+]
 
 [[package]]
 name = "criterion"
-version = "0.5.1"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
+checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
 dependencies = [
  "anes",
+ "atty",
  "cast",
  "ciborium",
  "clap",
  "criterion-plot",
- "is-terminal",
- "itertools 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.10.5",
+ "lazy_static",
  "num-traits",
- "once_cell",
  "oorandom",
  "plotters",
  "rayon",
@@ -138,7 +139,7 @@
 checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
 dependencies = [
  "cast",
- "itertools 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.10.5",
 ]
 
 [[package]]
@@ -162,9 +163,9 @@
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.20"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
 
 [[package]]
 name = "crunchy"
@@ -174,9 +175,9 @@
 
 [[package]]
 name = "either"
-version = "1.13.0"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
 
 [[package]]
 name = "getrandom"
@@ -200,32 +201,28 @@
 ]
 
 [[package]]
-name = "hermit-abi"
-version = "0.4.0"
+name = "hashbrown"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
 [[package]]
-name = "is-terminal"
-version = "0.4.13"
+name = "hermit-abi"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
 dependencies = [
- "hermit-abi",
  "libc",
- "windows-sys 0.52.0",
 ]
 
 [[package]]
-name = "itertools"
-version = "0.10.5"
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
- "criterion",
- "either",
- "paste",
- "permutohedron",
- "quickcheck",
- "rand",
+ "autocfg",
+ "hashbrown",
 ]
 
 [[package]]
@@ -238,6 +235,18 @@
 ]
 
 [[package]]
+name = "itertools"
+version = "0.13.0"
+dependencies = [
+ "criterion",
+ "either",
+ "paste",
+ "permutohedron",
+ "quickcheck",
+ "rand",
+]
+
+[[package]]
 name = "itoa"
 version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -245,30 +254,36 @@
 
 [[package]]
 name = "js-sys"
-version = "0.3.70"
+version = "0.3.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
 dependencies = [
  "wasm-bindgen",
 ]
 
 [[package]]
-name = "libc"
-version = "0.2.158"
+name = "lazy_static"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.154"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
 
 [[package]]
 name = "log"
-version = "0.4.22"
+version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
 
 [[package]]
 name = "memchr"
-version = "2.7.4"
+version = "2.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
 
 [[package]]
 name = "num-traits"
@@ -287,9 +302,15 @@
 
 [[package]]
 name = "oorandom"
-version = "11.1.4"
+version = "11.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
+checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
 
 [[package]]
 name = "paste"
@@ -305,9 +326,9 @@
 
 [[package]]
 name = "plotters"
-version = "0.3.6"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3"
+checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
 dependencies = [
  "num-traits",
  "plotters-backend",
@@ -318,33 +339,30 @@
 
 [[package]]
 name = "plotters-backend"
-version = "0.3.6"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7"
+checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
 
 [[package]]
 name = "plotters-svg"
-version = "0.3.6"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705"
+checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
 dependencies = [
  "plotters-backend",
 ]
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.20"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
-dependencies = [
- "zerocopy",
-]
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.86"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
 dependencies = [
  "unicode-ident",
 ]
@@ -361,9 +379,9 @@
 
 [[package]]
 name = "quote"
-version = "1.0.37"
+version = "1.0.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
 dependencies = [
  "proc-macro2",
 ]
@@ -431,9 +449,9 @@
 
 [[package]]
 name = "regex"
-version = "1.10.6"
+version = "1.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -443,9 +461,9 @@
 
 [[package]]
 name = "regex-automata"
-version = "0.4.7"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -454,9 +472,9 @@
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.4"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
 
 [[package]]
 name = "ryu"
@@ -475,18 +493,18 @@
 
 [[package]]
 name = "serde"
-version = "1.0.209"
+version = "1.0.202"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
+checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.209"
+version = "1.0.202"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
+checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -495,21 +513,20 @@
 
 [[package]]
 name = "serde_json"
-version = "1.0.127"
+version = "1.0.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
+checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
 dependencies = [
  "itoa",
- "memchr",
  "ryu",
  "serde",
 ]
 
 [[package]]
 name = "syn"
-version = "2.0.76"
+version = "2.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
+checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -517,6 +534,12 @@
 ]
 
 [[package]]
+name = "textwrap"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
+
+[[package]]
 name = "tinytemplate"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -550,20 +573,19 @@
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.93"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
 dependencies = [
  "cfg-if",
- "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.93"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
 dependencies = [
  "bumpalo",
  "log",
@@ -576,9 +598,9 @@
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.93"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -586,9 +608,9 @@
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.93"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -599,30 +621,52 @@
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.93"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
 
 [[package]]
 name = "web-sys"
-version = "0.3.70"
+version = "0.3.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
 ]
 
 [[package]]
-name = "winapi-util"
-version = "0.1.9"
+name = "winapi"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 dependencies = [
- "windows-sys 0.59.0",
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
 ]
 
 [[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
 name = "windows-sys"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -632,19 +676,10 @@
 ]
 
 [[package]]
-name = "windows-sys"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
-dependencies = [
- "windows-targets",
-]
-
-[[package]]
 name = "windows-targets"
-version = "0.52.6"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
 dependencies = [
  "windows_aarch64_gnullvm",
  "windows_aarch64_msvc",
@@ -658,69 +693,48 @@
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.52.6"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.52.6"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.52.6"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
 
 [[package]]
 name = "windows_i686_gnullvm"
-version = "0.52.6"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.52.6"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.52.6"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.52.6"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.52.6"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-
-[[package]]
-name = "zerocopy"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
-dependencies = [
- "byteorder",
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
diff --git a/crates/itertools/Cargo.toml b/crates/itertools/Cargo.toml
index 40be7e4..21896fe 100644
--- a/crates/itertools/Cargo.toml
+++ b/crates/itertools/Cargo.toml
@@ -3,30 +3,38 @@
 # When uploading crates to the registry Cargo will automatically
 # "normalize" Cargo.toml files for maximal compatibility
 # with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
 #
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
 
 [package]
 edition = "2018"
+rust-version = "1.43.1"
 name = "itertools"
-version = "0.10.5"
+version = "0.13.0"
 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"
+keywords = [
+    "iterator",
+    "data-structure",
+    "zip",
+    "product",
+]
+categories = [
+    "algorithms",
+    "rust-patterns",
+    "no-std",
+    "no-std::no-alloc",
+]
+license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-itertools/itertools"
-[package.metadata.release]
-no-dev-version = true
+
 [profile.bench]
-debug = true
+debug = 2
 
 [lib]
 test = false
@@ -49,7 +57,7 @@
 harness = false
 
 [[bench]]
-name = "tree_fold1"
+name = "tree_reduce"
 harness = false
 
 [[bench]]
@@ -63,11 +71,17 @@
 [[bench]]
 name = "powerset"
 harness = false
+
+[[bench]]
+name = "specializations"
+harness = false
+
 [dependencies.either]
 version = "1.0"
 default-features = false
+
 [dev-dependencies.criterion]
-version = "=0"
+version = "0.4.0"
 
 [dev-dependencies.paste]
 version = "1.0.0"
@@ -77,7 +91,7 @@
 
 [dev-dependencies.quickcheck]
 version = "0.9"
-default-features = false
+default_features = false
 
 [dev-dependencies.rand]
 version = "0.7"
@@ -85,4 +99,7 @@
 [features]
 default = ["use_std"]
 use_alloc = []
-use_std = ["use_alloc", "either/use_std"]
+use_std = [
+    "use_alloc",
+    "either/use_std",
+]
diff --git a/crates/itertools/METADATA b/crates/itertools/METADATA
index bac5c1b..074c4e6 100644
--- a/crates/itertools/METADATA
+++ b/crates/itertools/METADATA
@@ -1,23 +1,16 @@
-# This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update rust/crates/itertools
-# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
-
 name: "itertools"
 description: "Extra iterator adaptors, iterator methods, free functions, and macros."
 third_party {
-  url {
-    type: HOMEPAGE
-    value: "https://crates.io/crates/itertools"
-  }
-  url {
-    type: ARCHIVE
-    value: "https://static.crates.io/crates/itertools/itertools-0.10.5.crate"
-  }
-  version: "0.10.5"
+  version: "0.13.0"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 12
-    day: 12
+    year: 2024
+    month: 9
+    day: 4
+  }
+  identifier {
+    type: "Archive"
+    value: "https://static.crates.io/crates/itertools/itertools-0.13.0.crate"
+    version: "0.13.0"
   }
 }
diff --git a/crates/itertools/README.md b/crates/itertools/README.md
index a911127..982ef5d 100644
--- a/crates/itertools/README.md
+++ b/crates/itertools/README.md
@@ -4,14 +4,11 @@
 
 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.5"
+itertools = "0.13.0"
 ```
 
 How to use in your crate:
@@ -21,17 +18,9 @@
 ```
 
 ## How to contribute
+If you're not sure what to work on, try checking the [help wanted](https://github.com/rust-itertools/itertools/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label.
 
-- 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.
+See our [CONTRIBUTING.md](https://github.com/rust-itertools/itertools/blob/master/CONTRIBUTING.md) for a detailed guide.
 
 ## License
 
diff --git a/crates/itertools/benches/bench1.rs b/crates/itertools/benches/bench1.rs
index 71278d1..53e77b0 100644
--- a/crates/itertools/benches/bench1.rs
+++ b/crates/itertools/benches/bench1.rs
@@ -1,22 +1,20 @@
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-use itertools::Itertools;
+use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
 use itertools::free::cloned;
 use itertools::iproduct;
+use itertools::Itertools;
 
-use std::iter::repeat;
 use std::cmp;
+use std::iter::repeat;
 use std::ops::{Add, Range};
 
-mod extra;
-
-use crate::extra::ZipSlices;
-
 fn slice_iter(c: &mut Criterion) {
     let xs: Vec<_> = repeat(1i32).take(20).collect();
 
     c.bench_function("slice iter", move |b| {
-        b.iter(|| for elt in xs.iter() {
-            black_box(elt);
+        b.iter(|| {
+            for elt in xs.iter() {
+                black_box(elt);
+            }
         })
     });
 }
@@ -25,8 +23,10 @@
     let xs: Vec<_> = repeat(1i32).take(20).collect();
 
     c.bench_function("slice iter rev", move |b| {
-        b.iter(|| for elt in xs.iter().rev() {
-            black_box(elt);
+        b.iter(|| {
+            for elt in xs.iter().rev() {
+                black_box(elt);
+            }
         })
     });
 }
@@ -116,72 +116,6 @@
     });
 }
 
-fn zipslices(c: &mut Criterion) {
-    let xs = vec![0; 1024];
-    let ys = vec![0; 768];
-    let xs = black_box(xs);
-    let ys = black_box(ys);
-
-    c.bench_function("zipslices", move |b| {
-        b.iter(|| {
-            for (&x, &y) in ZipSlices::new(&xs, &ys) {
-                black_box(x);
-                black_box(y);
-            }
-        })
-    });
-}
-
-fn zipslices_mut(c: &mut Criterion) {
-    let xs = vec![0; 1024];
-    let ys = vec![0; 768];
-    let xs = black_box(xs);
-    let mut ys = black_box(ys);
-
-    c.bench_function("zipslices mut", move |b| {
-        b.iter(|| {
-            for (&x, &mut y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) {
-                black_box(x);
-                black_box(y);
-            }
-        })
-    });
-}
-
-fn zipdot_i32_zipslices(c: &mut Criterion) {
-    let xs = vec![2; 1024];
-    let ys = vec![2; 768];
-    let xs = black_box(xs);
-    let ys = black_box(ys);
-
-    c.bench_function("zipdot i32 zipslices", move |b| {
-        b.iter(|| {
-            let mut s = 0i32;
-            for (&x, &y) in ZipSlices::new(&xs, &ys) {
-                s += x * y;
-            }
-            s
-        })
-    });
-}
-
-fn zipdot_f32_zipslices(c: &mut Criterion) {
-    let xs = vec![2f32; 1024];
-    let ys = vec![2f32; 768];
-    let xs = black_box(xs);
-    let ys = black_box(ys);
-
-    c.bench_function("zipdot f32 zipslices", move |b| {
-        b.iter(|| {
-            let mut s = 0.;
-            for (&x, &y) in ZipSlices::new(&xs, &ys) {
-                s += x * y;
-            }
-            s
-        })
-    });
-}
-
 fn zip_checked_counted_loop(c: &mut Criterion) {
     let xs = vec![0; 1024];
     let ys = vec![0; 768];
@@ -307,10 +241,10 @@
             let len = cmp::min(xs.len(), ys.len());
             for i in 0..len {
                 unsafe {
-                let x = *xs.get_unchecked(i);
-                let y = *ys.get_unchecked(i);
-                black_box(x);
-                black_box(y);
+                    let x = *xs.get_unchecked(i);
+                    let y = *ys.get_unchecked(i);
+                    black_box(x);
+                    black_box(y);
                 }
             }
         })
@@ -329,9 +263,9 @@
             let mut s = 0i32;
             for i in 0..len {
                 unsafe {
-                let x = *xs.get_unchecked(i);
-                let y = *ys.get_unchecked(i);
-                s += x * y;
+                    let x = *xs.get_unchecked(i);
+                    let y = *ys.get_unchecked(i);
+                    s += x * y;
                 }
             }
             s
@@ -351,9 +285,9 @@
             let mut s = 0f32;
             for i in 0..len {
                 unsafe {
-                let x = *xs.get_unchecked(i);
-                let y = *ys.get_unchecked(i);
-                s += x * y;
+                    let x = *xs.get_unchecked(i);
+                    let y = *ys.get_unchecked(i);
+                    s += x * y;
                 }
             }
             s
@@ -374,19 +308,19 @@
             let len = cmp::min(xs.len(), cmp::min(ys.len(), zs.len()));
             for i in 0..len {
                 unsafe {
-                let x = *xs.get_unchecked(i);
-                let y = *ys.get_unchecked(i);
-                let z = *zs.get_unchecked(i);
-                black_box(x);
-                black_box(y);
-                black_box(z);
+                    let x = *xs.get_unchecked(i);
+                    let y = *ys.get_unchecked(i);
+                    let z = *zs.get_unchecked(i);
+                    black_box(x);
+                    black_box(y);
+                    black_box(z);
                 }
             }
         })
     });
 }
 
-fn group_by_lazy_1(c: &mut Criterion) {
+fn chunk_by_lazy_1(c: &mut Criterion) {
     let mut data = vec![0; 1024];
     for (index, elt) in data.iter_mut().enumerate() {
         *elt = index / 10;
@@ -394,10 +328,10 @@
 
     let data = black_box(data);
 
-    c.bench_function("group by lazy 1", move |b| {
+    c.bench_function("chunk by lazy 1", move |b| {
         b.iter(|| {
-            for (_key, group) in &data.iter().group_by(|elt| **elt) {
-                for elt in group {
+            for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -405,7 +339,7 @@
     });
 }
 
-fn group_by_lazy_2(c: &mut Criterion) {
+fn chunk_by_lazy_2(c: &mut Criterion) {
     let mut data = vec![0; 1024];
     for (index, elt) in data.iter_mut().enumerate() {
         *elt = index / 2;
@@ -413,10 +347,10 @@
 
     let data = black_box(data);
 
-    c.bench_function("group by lazy 2", move |b| {
+    c.bench_function("chunk by lazy 2", move |b| {
         b.iter(|| {
-            for (_key, group) in &data.iter().group_by(|elt| **elt) {
-                for elt in group {
+            for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -432,8 +366,8 @@
 
     c.bench_function("slice chunks", move |b| {
         b.iter(|| {
-            for group in data.chunks(sz) {
-                for elt in group {
+            for chunk in data.chunks(sz) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -449,8 +383,8 @@
 
     c.bench_function("chunks lazy 1", move |b| {
         b.iter(|| {
-            for group in &data.iter().chunks(sz) {
-                for elt in group {
+            for chunk in &data.iter().chunks(sz) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -464,17 +398,15 @@
     let alpha = black_box(&data[1..]);
     let beta = black_box(&data[..l - 1]);
 
-    c.bench_function("equal", move |b| {
-        b.iter(|| {
-            itertools::equal(alpha, beta)
-        })
-    });
+    c.bench_function("equal", move |b| b.iter(|| itertools::equal(alpha, beta)));
 }
 
 fn merge_default(c: &mut Criterion) {
     let mut data1 = vec![0; 1024];
     let mut data2 = vec![0; 800];
     let mut x = 0;
+
+    #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
     for (_, elt) in data1.iter_mut().enumerate() {
         *elt = x;
         x += 1;
@@ -493,9 +425,7 @@
     let data2 = black_box(data2);
 
     c.bench_function("merge default", move |b| {
-        b.iter(|| {
-            data1.iter().merge(&data2).count()
-        })
+        b.iter(|| data1.iter().merge(&data2).count())
     });
 }
 
@@ -503,6 +433,8 @@
     let mut data1 = vec![0; 1024];
     let mut data2 = vec![0; 800];
     let mut x = 0;
+
+    #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
     for (_, elt) in data1.iter_mut().enumerate() {
         *elt = x;
         x += 1;
@@ -521,9 +453,7 @@
     let data2 = black_box(data2);
 
     c.bench_function("merge by cmp", move |b| {
-        b.iter(|| {
-            data1.iter().merge_by(&data2, PartialOrd::le).count()
-        })
+        b.iter(|| data1.iter().merge_by(&data2, PartialOrd::le).count())
     });
 }
 
@@ -531,6 +461,8 @@
     let mut data1 = vec![0; 1024];
     let mut data2 = vec![0; 800];
     let mut x = 0;
+
+    #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
     for (_, elt) in data1.iter_mut().enumerate() {
         *elt = x;
         x += 1;
@@ -549,9 +481,7 @@
     let data2 = black_box(data2);
 
     c.bench_function("merge by lt", move |b| {
-        b.iter(|| {
-            data1.iter().merge_by(&data2, |a, b| a <= b).count()
-        })
+        b.iter(|| data1.iter().merge_by(&data2, |a, b| a <= b).count())
     });
 }
 
@@ -559,6 +489,8 @@
     let mut data1 = vec![0; 1024];
     let mut data2 = vec![0; 800];
     let mut x = 0;
+
+    #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
     for (_, elt) in data1.iter_mut().enumerate() {
         *elt = x;
         x += 1;
@@ -578,9 +510,7 @@
     let its = &[data1.iter(), data2.iter()];
 
     c.bench_function("kmerge default", move |b| {
-        b.iter(|| {
-            its.iter().cloned().kmerge().count()
-        })
+        b.iter(|| its.iter().cloned().kmerge().count())
     });
 }
 
@@ -589,7 +519,7 @@
 
     let mut state = 1729u16;
     fn rng(state: &mut u16) -> u16 {
-        let new = state.wrapping_mul(31421) + 6927;
+        let new = state.wrapping_mul(31421).wrapping_add(6927);
         *state = new;
         new
     }
@@ -600,10 +530,10 @@
 
     let mut chunks = Vec::new();
     let mut rest = &mut data[..];
-    while rest.len() > 0 {
+    while !rest.is_empty() {
         let chunk_len = 1 + rng(&mut state) % 512;
         let chunk_len = cmp::min(rest.len(), chunk_len as usize);
-        let (fst, tail) = {rest}.split_at_mut(chunk_len);
+        let (fst, tail) = { rest }.split_at_mut(chunk_len);
         fst.sort();
         chunks.push(fst.iter().cloned());
         rest = tail;
@@ -612,15 +542,14 @@
     // println!("Chunk lengths: {}", chunks.iter().format_with(", ", |elt, f| f(&elt.len())));
 
     c.bench_function("kmerge tenway", move |b| {
-        b.iter(|| {
-            chunks.iter().cloned().kmerge().count()
-        })
+        b.iter(|| chunks.iter().cloned().kmerge().count())
     });
 }
 
 fn fast_integer_sum<I>(iter: I) -> I::Item
-    where I: IntoIterator,
-          I::Item: Default + Add<Output=I::Item>
+where
+    I: IntoIterator,
+    I::Item: Default + Add<Output = I::Item>,
 {
     iter.into_iter().fold(<_>::default(), |x, y| x + y)
 }
@@ -629,9 +558,7 @@
     let v = vec![0; 1024];
 
     c.bench_function("step vec 2", move |b| {
-        b.iter(|| {
-            fast_integer_sum(cloned(v.iter().step_by(2)))
-        })
+        b.iter(|| fast_integer_sum(cloned(v.iter().step_by(2))))
     });
 }
 
@@ -639,9 +566,7 @@
     let v = vec![0; 1024];
 
     c.bench_function("step vec 10", move |b| {
-        b.iter(|| {
-            fast_integer_sum(cloned(v.iter().step_by(10)))
-        })
+        b.iter(|| fast_integer_sum(cloned(v.iter().step_by(10))))
     });
 }
 
@@ -649,9 +574,7 @@
     let v = black_box(0..1024);
 
     c.bench_function("step range 2", move |b| {
-        b.iter(|| {
-            fast_integer_sum(v.clone().step_by(2))
-        })
+        b.iter(|| fast_integer_sum(v.clone().step_by(2)))
     });
 }
 
@@ -659,9 +582,23 @@
     let v = black_box(0..1024);
 
     c.bench_function("step range 10", move |b| {
-        b.iter(|| {
-            fast_integer_sum(v.clone().step_by(10))
-        })
+        b.iter(|| fast_integer_sum(v.clone().step_by(10)))
+    });
+}
+
+fn vec_iter_mut_partition(c: &mut Criterion) {
+    let data = std::iter::repeat(-1024i32..1024)
+        .take(256)
+        .flatten()
+        .collect_vec();
+    c.bench_function("vec iter mut partition", move |b| {
+        b.iter_batched(
+            || data.clone(),
+            |mut data| {
+                black_box(itertools::partition(black_box(&mut data), |n| *n >= 0));
+            },
+            BatchSize::LargeInput,
+        )
     });
 }
 
@@ -681,22 +618,6 @@
     });
 }
 
-fn cartesian_product_fold(c: &mut Criterion) {
-    let xs = vec![0; 16];
-
-    c.bench_function("cartesian product fold", move |b| {
-        b.iter(|| {
-            let mut sum = 0;
-            iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| {
-                sum += x;
-                sum += y;
-                sum += z;
-            });
-            sum
-        })
-    });
-}
-
 fn multi_cartesian_product_iterator(c: &mut Criterion) {
     let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
 
@@ -713,22 +634,6 @@
     });
 }
 
-fn multi_cartesian_product_fold(c: &mut Criterion) {
-    let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
-
-    c.bench_function("multi cartesian product fold", move |b| {
-        b.iter(|| {
-            let mut sum = 0;
-            xs.iter().multi_cartesian_product().fold((), |(), x| {
-                sum += x[0];
-                sum += x[1];
-                sum += x[2];
-            });
-            sum
-        })
-    });
-}
-
 fn cartesian_product_nested_for(c: &mut Criterion) {
     let xs = vec![0; 16];
 
@@ -753,9 +658,7 @@
     let mut xs = vec![0; 5_000_000];
     xs.extend(vec![1; 5_000_000]);
 
-    c.bench_function("all equal", move |b| {
-        b.iter(|| xs.iter().all_equal())
-    });
+    c.bench_function("all equal", move |b| b.iter(|| xs.iter().all_equal()));
 }
 
 fn all_equal_for(c: &mut Criterion) {
@@ -797,21 +700,17 @@
     }
 
     c.bench_function("permutations iter", move |b| {
-        b.iter(|| {
-            for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) {
-
-            }
-        })
+        b.iter(
+            || {
+                for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) {}
+            },
+        )
     });
 }
 
 fn permutations_range(c: &mut Criterion) {
     c.bench_function("permutations range", move |b| {
-        b.iter(|| {
-            for _ in (0..PERM_COUNT).permutations(PERM_COUNT) {
-
-            }
-        })
+        b.iter(|| for _ in (0..PERM_COUNT).permutations(PERM_COUNT) {})
     });
 }
 
@@ -819,11 +718,7 @@
     let v = (0..PERM_COUNT).collect_vec();
 
     c.bench_function("permutations slice", move |b| {
-        b.iter(|| {
-            for _ in v.as_slice().iter().permutations(PERM_COUNT) {
-
-            }
-        })
+        b.iter(|| for _ in v.as_slice().iter().permutations(PERM_COUNT) {})
     });
 }
 
@@ -836,10 +731,6 @@
     zipdot_f32_default_zip,
     zip_default_zip3,
     zip_slices_ziptuple,
-    zipslices,
-    zipslices_mut,
-    zipdot_i32_zipslices,
-    zipdot_f32_zipslices,
     zip_checked_counted_loop,
     zipdot_i32_checked_counted_loop,
     zipdot_f32_checked_counted_loop,
@@ -848,8 +739,8 @@
     zipdot_i32_unchecked_counted_loop,
     zipdot_f32_unchecked_counted_loop,
     zip_unchecked_counted_loop3,
-    group_by_lazy_1,
-    group_by_lazy_2,
+    chunk_by_lazy_1,
+    chunk_by_lazy_2,
     slice_chunks,
     chunks_lazy_1,
     equal,
@@ -862,10 +753,9 @@
     step_vec_10,
     step_range_2,
     step_range_10,
+    vec_iter_mut_partition,
     cartesian_product_iterator,
-    cartesian_product_fold,
     multi_cartesian_product_iterator,
-    multi_cartesian_product_fold,
     cartesian_product_nested_for,
     all_equal,
     all_equal_for,
diff --git a/crates/itertools/benches/combinations.rs b/crates/itertools/benches/combinations.rs
index e7433a4..42a4521 100644
--- a/crates/itertools/benches/combinations.rs
+++ b/crates/itertools/benches/combinations.rs
@@ -111,15 +111,7 @@
 }
 
 criterion_group!(
-    benches,
-    comb_for1,
-    comb_for2,
-    comb_for3,
-    comb_for4,
-    comb_c1,
-    comb_c2,
-    comb_c3,
-    comb_c4,
+    benches, comb_for1, comb_for2, comb_for3, comb_for4, comb_c1, comb_c2, comb_c3, comb_c4,
     comb_c14,
 );
 criterion_main!(benches);
diff --git a/crates/itertools/benches/extra/mod.rs b/crates/itertools/benches/extra/mod.rs
deleted file mode 100644
index 52fe5cc..0000000
--- a/crates/itertools/benches/extra/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub use self::zipslices::ZipSlices;
-mod zipslices;
diff --git a/crates/itertools/benches/extra/zipslices.rs b/crates/itertools/benches/extra/zipslices.rs
deleted file mode 100644
index 633be59..0000000
--- a/crates/itertools/benches/extra/zipslices.rs
+++ /dev/null
@@ -1,188 +0,0 @@
-use std::cmp;
-
-// Note: There are different ways to implement ZipSlices.
-// This version performed the best in benchmarks.
-//
-// I also implemented a version with three pointers (tptr, tend, uptr),
-// that mimiced slice::Iter and only checked bounds by using tptr == tend,
-// but that was inferior to this solution.
-
-/// An iterator which iterates two slices simultaneously.
-///
-/// `ZipSlices` acts like a double-ended `.zip()` iterator.
-///
-/// It was intended to be more efficient than `.zip()`, and it was, then
-/// rustc changed how it optimizes so it can not promise improved performance
-/// at this time.
-///
-/// Note that elements past the end of the shortest of the two slices are ignored.
-///
-/// Iterator element type for `ZipSlices<T, U>` is `(T::Item, U::Item)`. For example,
-/// for a `ZipSlices<&'a [A], &'b mut [B]>`, the element type is `(&'a A, &'b mut B)`.
-#[derive(Clone)]
-pub struct ZipSlices<T, U> {
-    t: T,
-    u: U,
-    len: usize,
-    index: usize,
-}
-
-impl<'a, 'b, A, B> ZipSlices<&'a [A], &'b [B]> {
-    /// Create a new `ZipSlices` from slices `a` and `b`.
-    ///
-    /// Act like a double-ended `.zip()` iterator, but more efficiently.
-    ///
-    /// Note that elements past the end of the shortest of the two slices are ignored.
-    #[inline(always)]
-    pub fn new(a: &'a [A], b: &'b [B]) -> Self {
-        let minl = cmp::min(a.len(), b.len());
-        ZipSlices {
-            t: a,
-            u: b,
-            len: minl,
-            index: 0,
-        }
-    }
-}
-
-impl<T, U> ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{
-    /// Create a new `ZipSlices` from slices `a` and `b`.
-    ///
-    /// Act like a double-ended `.zip()` iterator, but more efficiently.
-    ///
-    /// Note that elements past the end of the shortest of the two slices are ignored.
-    #[inline(always)]
-    pub fn from_slices(a: T, b: U) -> Self {
-        let minl = cmp::min(a.len(), b.len());
-        ZipSlices {
-            t: a,
-            u: b,
-            len: minl,
-            index: 0,
-        }
-    }
-}
-
-impl<T, U> Iterator for ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{
-    type Item = (T::Item, U::Item);
-
-    #[inline(always)]
-    fn next(&mut self) -> Option<Self::Item> {
-        unsafe {
-            if self.index >= self.len {
-                None
-            } else {
-                let i = self.index;
-                self.index += 1;
-                Some((
-                    self.t.get_unchecked(i),
-                    self.u.get_unchecked(i)))
-            }
-        }
-    }
-
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let len = self.len - self.index;
-        (len, Some(len))
-    }
-}
-
-impl<T, U> DoubleEndedIterator for ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{
-    #[inline(always)]
-    fn next_back(&mut self) -> Option<Self::Item> {
-        unsafe {
-            if self.index >= self.len {
-                None
-            } else {
-                self.len -= 1;
-                let i = self.len;
-                Some((
-                    self.t.get_unchecked(i),
-                    self.u.get_unchecked(i)))
-            }
-        }
-    }
-}
-
-impl<T, U> ExactSizeIterator for ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{}
-
-unsafe impl<T, U> Slice for ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{
-    type Item = (T::Item, U::Item);
-
-    fn len(&self) -> usize {
-        self.len - self.index
-    }
-
-    unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item {
-        (self.t.get_unchecked(i),
-         self.u.get_unchecked(i))
-    }
-}
-
-/// A helper trait to let `ZipSlices` accept both `&[T]` and `&mut [T]`.
-///
-/// Unsafe trait because:
-///
-/// - Implementors must guarantee that `get_unchecked` is valid for all indices `0..len()`.
-pub unsafe trait Slice {
-    /// The type of a reference to the slice's elements
-    type Item;
-    #[doc(hidden)]
-    fn len(&self) -> usize;
-    #[doc(hidden)]
-    unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item;
-}
-
-unsafe impl<'a, T> Slice for &'a [T] {
-    type Item = &'a T;
-    #[inline(always)]
-    fn len(&self) -> usize { (**self).len() }
-    #[inline(always)]
-    unsafe fn get_unchecked(&mut self, i: usize) -> &'a T {
-        debug_assert!(i < self.len());
-        (**self).get_unchecked(i)
-    }
-}
-
-unsafe impl<'a, T> Slice for &'a mut [T] {
-    type Item = &'a mut T;
-    #[inline(always)]
-    fn len(&self) -> usize { (**self).len() }
-    #[inline(always)]
-    unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T {
-        debug_assert!(i < self.len());
-        // override the lifetime constraints of &mut &'a mut [T]
-        (*(*self as *mut [T])).get_unchecked_mut(i)
-    }
-}
-
-#[test]
-fn zipslices() {
-
-    let xs = [1, 2, 3, 4, 5, 6];
-    let ys = [1, 2, 3, 7];
-    ::itertools::assert_equal(ZipSlices::new(&xs, &ys), xs.iter().zip(&ys));
-
-    let xs = [1, 2, 3, 4, 5, 6];
-    let mut ys = [0; 6];
-    for (x, y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) {
-        *y = *x;
-    }
-    ::itertools::assert_equal(&xs, &ys);
-}
diff --git a/crates/itertools/benches/fold_specialization.rs b/crates/itertools/benches/fold_specialization.rs
index 5de4671..b44f347 100644
--- a/crates/itertools/benches/fold_specialization.rs
+++ b/crates/itertools/benches/fold_specialization.rs
@@ -1,10 +1,13 @@
+#![allow(unstable_name_collisions)]
+
 use criterion::{criterion_group, criterion_main, Criterion};
 use itertools::Itertools;
 
 struct Unspecialized<I>(I);
 
 impl<I> Iterator for Unspecialized<I>
-where I: Iterator
+where
+    I: Iterator,
 {
     type Item = I::Item;
 
@@ -25,8 +28,7 @@
     pub mod intersperse {
         use super::*;
 
-        pub fn external(c: &mut Criterion)
-        {
+        pub fn external(c: &mut Criterion) {
             let arr = [1; 1024];
 
             c.bench_function("external", move |b| {
@@ -40,23 +42,23 @@
             });
         }
 
-        pub fn internal_specialized(c: &mut Criterion)
-        {
+        pub fn internal_specialized(c: &mut Criterion) {
             let arr = [1; 1024];
 
             c.bench_function("internal specialized", move |b| {
                 b.iter(|| {
+                    #[allow(clippy::unnecessary_fold)]
                     arr.iter().intersperse(&0).fold(0, |acc, x| acc + x)
                 })
             });
         }
 
-        pub fn internal_unspecialized(c: &mut Criterion)
-        {
+        pub fn internal_unspecialized(c: &mut Criterion) {
             let arr = [1; 1024];
 
             c.bench_function("internal unspecialized", move |b| {
                 b.iter(|| {
+                    #[allow(clippy::unnecessary_fold)]
                     Unspecialized(arr.iter().intersperse(&0)).fold(0, |acc, x| acc + x)
                 })
             });
diff --git a/crates/itertools/benches/powerset.rs b/crates/itertools/benches/powerset.rs
index 074550b..018333d 100644
--- a/crates/itertools/benches/powerset.rs
+++ b/crates/itertools/benches/powerset.rs
@@ -20,17 +20,64 @@
     });
 }
 
-fn powerset_0(c: &mut Criterion) { powerset_n(c, 0); }
+fn powerset_n_fold(c: &mut Criterion, n: usize) {
+    let id = format!("powerset {} fold", n);
+    c.bench_function(id.as_str(), move |b| {
+        b.iter(|| {
+            for _ in 0..calc_iters(n) {
+                (0..n).powerset().fold(0, |s, elt| s + black_box(elt).len());
+            }
+        })
+    });
+}
 
-fn powerset_1(c: &mut Criterion) { powerset_n(c, 1); }
+fn powerset_0(c: &mut Criterion) {
+    powerset_n(c, 0);
+}
 
-fn powerset_2(c: &mut Criterion) { powerset_n(c, 2); }
+fn powerset_1(c: &mut Criterion) {
+    powerset_n(c, 1);
+}
 
-fn powerset_4(c: &mut Criterion) { powerset_n(c, 4); }
+fn powerset_2(c: &mut Criterion) {
+    powerset_n(c, 2);
+}
 
-fn powerset_8(c: &mut Criterion) { powerset_n(c, 8); }
+fn powerset_4(c: &mut Criterion) {
+    powerset_n(c, 4);
+}
 
-fn powerset_12(c: &mut Criterion) { powerset_n(c, 12); }
+fn powerset_8(c: &mut Criterion) {
+    powerset_n(c, 8);
+}
+
+fn powerset_12(c: &mut Criterion) {
+    powerset_n(c, 12);
+}
+
+fn powerset_0_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 0);
+}
+
+fn powerset_1_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 1);
+}
+
+fn powerset_2_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 2);
+}
+
+fn powerset_4_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 4);
+}
+
+fn powerset_8_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 8);
+}
+
+fn powerset_12_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 12);
+}
 
 criterion_group!(
     benches,
@@ -40,5 +87,11 @@
     powerset_4,
     powerset_8,
     powerset_12,
+    powerset_0_fold,
+    powerset_1_fold,
+    powerset_2_fold,
+    powerset_4_fold,
+    powerset_8_fold,
+    powerset_12_fold,
 );
-criterion_main!(benches);
\ No newline at end of file
+criterion_main!(benches);
diff --git a/crates/itertools/benches/specializations.rs b/crates/itertools/benches/specializations.rs
new file mode 100644
index 0000000..18039fc
--- /dev/null
+++ b/crates/itertools/benches/specializations.rs
@@ -0,0 +1,667 @@
+#![allow(unstable_name_collisions, clippy::incompatible_msrv)]
+
+use criterion::black_box;
+use criterion::BenchmarkId;
+use itertools::Itertools;
+
+const NTH_INPUTS: &[usize] = &[0, 1, 2, 4, 8];
+
+/// Create multiple functions each defining a benchmark group about iterator methods.
+///
+/// Each created group has functions with the following ids:
+///
+/// - `next`, `size_hint`, `count`, `last`, `nth`, `collect`, `fold`
+/// - and when marked as `DoubleEndedIterator`: `next_back`, `nth_back`, `rfold`
+/// - and when marked as `ExactSizeIterator`: `len`
+///
+/// Note that this macro can be called only once.
+macro_rules! bench_specializations {
+    (
+        $(
+            $name:ident {
+                $($extra:ident)*
+                {$(
+                    $init:stmt;
+                )*}
+                $iterator:expr
+            }
+        )*
+    ) => {
+        $(
+            #[allow(unused_must_use)]
+            fn $name(c: &mut ::criterion::Criterion) {
+                let mut bench_group = c.benchmark_group(stringify!($name));
+                $(
+                    $init
+                )*
+                let bench_first_its = {
+                    let mut bench_idx = 0;
+                    [0; 1000].map(|_| {
+                        let mut it = $iterator;
+                        if bench_idx != 0 {
+                            it.nth(bench_idx - 1);
+                        }
+                        bench_idx += 1;
+                        it
+                    })
+                };
+                bench_specializations!(@Iterator bench_group bench_first_its: $iterator);
+                $(
+                    bench_specializations!(@$extra bench_group bench_first_its: $iterator);
+                )*
+                bench_group.finish();
+            }
+        )*
+
+        ::criterion::criterion_group!(benches, $($name, )*);
+        ::criterion::criterion_main!(benches);
+    };
+
+    (@Iterator $group:ident $first_its:ident: $iterator:expr) => {
+        $group.bench_function("next", |bencher| bencher.iter(|| {
+            let mut it = $iterator;
+            while let Some(x) = it.next() {
+                black_box(x);
+            }
+        }));
+        $group.bench_function("size_hint", |bencher| bencher.iter(|| {
+            $first_its.iter().for_each(|it| {
+                black_box(it.size_hint());
+            })
+        }));
+        $group.bench_function("count", |bencher| bencher.iter(|| {
+            $iterator.count()
+        }));
+        $group.bench_function("last", |bencher| bencher.iter(|| {
+            $iterator.last()
+        }));
+        for n in NTH_INPUTS {
+            $group.bench_with_input(BenchmarkId::new("nth", n), n, |bencher, n| bencher.iter(|| {
+                for start in 0_usize..10 {
+                    let mut it = $iterator;
+                    if let Some(s) = start.checked_sub(1) {
+                        black_box(it.nth(s));
+                    }
+                    while let Some(x) = it.nth(*n) {
+                        black_box(x);
+                    }
+                }
+            }));
+        }
+        $group.bench_function("collect", |bencher| bencher.iter(|| {
+            $iterator.collect::<Vec<_>>()
+        }));
+        $group.bench_function("fold", |bencher| bencher.iter(|| {
+            $iterator.fold((), |(), x| {
+                black_box(x);
+            })
+        }));
+    };
+
+    (@DoubleEndedIterator $group:ident $_first_its:ident: $iterator:expr) => {
+        $group.bench_function("next_back", |bencher| bencher.iter(|| {
+            let mut it = $iterator;
+            while let Some(x) = it.next_back() {
+                black_box(x);
+            }
+        }));
+        for n in NTH_INPUTS {
+            $group.bench_with_input(BenchmarkId::new("nth_back", n), n, |bencher, n| bencher.iter(|| {
+                for start in 0_usize..10 {
+                    let mut it = $iterator;
+                    if let Some(s) = start.checked_sub(1) {
+                        black_box(it.nth_back(s));
+                    }
+                    while let Some(x) = it.nth_back(*n) {
+                        black_box(x);
+                    }
+                }
+            }));
+        }
+        $group.bench_function("rfold", |bencher| bencher.iter(|| {
+            $iterator.rfold((), |(), x| {
+                black_box(x);
+            })
+        }));
+    };
+
+    (@ExactSizeIterator $group:ident $first_its:ident: $_iterator:expr) => {
+        $group.bench_function("len", |bencher| bencher.iter(|| {
+            $first_its.iter().for_each(|it| {
+                black_box(it.len());
+            })
+        }));
+    };
+}
+
+// Usage examples:
+// - For `ZipLongest::fold` only:
+//     cargo bench --bench specializations zip_longest/fold
+// - For `.combinations(k).nth(8)`:
+//     cargo bench --bench specializations combinations./nth/8
+bench_specializations! {
+    interleave {
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 768]);
+        }
+        v1.iter().interleave(&v2)
+    }
+    interleave_shortest {
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 768]);
+        }
+        v1.iter().interleave_shortest(&v2)
+    }
+    batching {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().batching(Iterator::next)
+    }
+    tuple_windows1 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_windows::<(_,)>()
+    }
+    tuple_windows2 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_windows::<(_, _)>()
+    }
+    tuple_windows3 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_windows::<(_, _, _)>()
+    }
+    tuple_windows4 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_windows::<(_, _, _, _)>()
+    }
+    circular_tuple_windows1 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().circular_tuple_windows::<(_,)>()
+    }
+    circular_tuple_windows2 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().circular_tuple_windows::<(_, _)>()
+    }
+    circular_tuple_windows3 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().circular_tuple_windows::<(_, _, _)>()
+    }
+    circular_tuple_windows4 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().circular_tuple_windows::<(_, _, _, _)>()
+    }
+    tuples1 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuples::<(_,)>()
+    }
+    tuples2 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuples::<(_, _)>()
+    }
+    tuples3 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuples::<(_, _, _)>()
+    }
+    tuples4 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuples::<(_, _, _, _)>()
+    }
+    tuple_buffer {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 11]);
+            // Short but the buffer can't have 12 or more elements.
+        }
+        {
+            let mut it = v.iter().tuples::<(_, _, _, _, _, _, _, _, _, _, _, _)>();
+            it.next(); // No element but it fills the buffer.
+            it.into_buffer()
+        }
+    }
+    cartesian_product {
+        {
+            let v = black_box(vec![0; 16]);
+        }
+        itertools::iproduct!(&v, &v, &v)
+    }
+    multi_cartesian_product {
+        {
+            let vs = black_box([0; 3].map(|_| vec![0; 16]));
+        }
+        vs.iter().multi_cartesian_product()
+    }
+    coalesce {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) })
+    }
+    dedup {
+        {
+            let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
+        }
+        v.iter().dedup()
+    }
+    dedup_by {
+        {
+            let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
+        }
+        v.iter().dedup_by(PartialOrd::ge)
+    }
+    dedup_with_count {
+        {
+            let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
+        }
+        v.iter().dedup_with_count()
+    }
+    dedup_by_with_count {
+        {
+            let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
+        }
+        v.iter().dedup_by_with_count(PartialOrd::ge)
+    }
+    duplicates {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..32).cycle().take(1024).collect_vec());
+        }
+        v.iter().duplicates()
+    }
+    duplicates_by {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().duplicates_by(|x| *x % 10)
+    }
+    unique {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..32).cycle().take(1024).collect_vec());
+        }
+        v.iter().unique()
+    }
+    unique_by {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().unique_by(|x| *x % 50)
+    }
+    take_while_inclusive {
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().take_while_inclusive(|x| **x < 1000)
+    }
+    pad_using {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().copied().pad_using(2048, |i| 5 * i)
+    }
+    positions {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().positions(|x| x % 5 == 0)
+    }
+    update {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v = black_box((0_i32..1024).collect_vec());
+        }
+        v.iter().copied().update(|x| *x *= 7)
+    }
+    tuple_combinations1 {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_combinations::<(_,)>()
+    }
+    tuple_combinations2 {
+        {
+            let v = black_box(vec![0; 64]);
+        }
+        v.iter().tuple_combinations::<(_, _)>()
+    }
+    tuple_combinations3 {
+        {
+            let v = black_box(vec![0; 64]);
+        }
+        v.iter().tuple_combinations::<(_, _, _)>()
+    }
+    tuple_combinations4 {
+        {
+            let v = black_box(vec![0; 64]);
+        }
+        v.iter().tuple_combinations::<(_, _, _, _)>()
+    }
+    intersperse {
+        {
+            let v = black_box(vec![0; 1024]);
+            let n = black_box(0);
+        }
+        v.iter().intersperse(&n)
+    }
+    intersperse_with {
+        {
+            let v = black_box(vec![0; 1024]);
+            let n = black_box(0);
+        }
+        v.iter().intersperse_with(|| &n)
+    }
+    combinations1 {
+        {
+            let v = black_box(vec![0; 1792]);
+        }
+        v.iter().combinations(1)
+    }
+    combinations2 {
+        {
+            let v = black_box(vec![0; 60]);
+        }
+        v.iter().combinations(2)
+    }
+    combinations3 {
+        {
+            let v = black_box(vec![0; 23]);
+        }
+        v.iter().combinations(3)
+    }
+    combinations4 {
+        {
+            let v = black_box(vec![0; 16]);
+        }
+        v.iter().combinations(4)
+    }
+    combinations_with_replacement1 {
+        {
+            let v = black_box(vec![0; 4096]);
+        }
+        v.iter().combinations_with_replacement(1)
+    }
+    combinations_with_replacement2 {
+        {
+            let v = black_box(vec![0; 90]);
+        }
+        v.iter().combinations_with_replacement(2)
+    }
+    combinations_with_replacement3 {
+        {
+            let v = black_box(vec![0; 28]);
+        }
+        v.iter().combinations_with_replacement(3)
+    }
+    combinations_with_replacement4 {
+        {
+            let v = black_box(vec![0; 16]);
+        }
+        v.iter().combinations_with_replacement(4)
+    }
+    permutations1 {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().permutations(1)
+    }
+    permutations2 {
+        {
+            let v = black_box(vec![0; 36]);
+        }
+        v.iter().permutations(2)
+    }
+    permutations3 {
+        {
+            let v = black_box(vec![0; 12]);
+        }
+        v.iter().permutations(3)
+    }
+    permutations4 {
+        {
+            let v = black_box(vec![0; 8]);
+        }
+        v.iter().permutations(4)
+    }
+    powerset {
+        {
+            let v = black_box(vec![0; 10]);
+        }
+        v.iter().powerset()
+    }
+    while_some {
+        {}
+        (0..)
+            .map(black_box)
+            .map(|i| char::from_digit(i, 16))
+            .while_some()
+    }
+    with_position {
+        ExactSizeIterator
+        {
+            let v = black_box((0..10240).collect_vec());
+        }
+        v.iter().with_position()
+    }
+    zip_longest {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let xs = black_box(vec![0; 1024]);
+            let ys = black_box(vec![0; 768]);
+        }
+        xs.iter().zip_longest(ys.iter())
+    }
+    zip_eq {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().zip_eq(v.iter().rev())
+    }
+    multizip {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 768]);
+            let v3 = black_box(vec![0; 2048]);
+        }
+        itertools::multizip((&v1, &v2, &v3))
+    }
+    izip {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 768]);
+            let v3 = black_box(vec![0; 2048]);
+        }
+        itertools::izip!(&v1, &v2, &v3)
+    }
+    put_back {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        itertools::put_back(&v).with_value(black_box(&0))
+    }
+    put_back_n {
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 16]);
+        }
+        {
+            let mut it = itertools::put_back_n(&v1);
+            for n in &v2 {
+                it.put_back(n);
+            }
+            it
+        }
+    }
+    exactly_one_error {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        // Use `at_most_one` would be similar.
+        v.iter().exactly_one().unwrap_err()
+    }
+    multipeek {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+            let n = black_box(16);
+        }
+        {
+            let mut it = v.iter().multipeek();
+            for _ in 0..n {
+                it.peek();
+            }
+            it
+        }
+    }
+    peek_nth {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+            let n = black_box(16);
+        }
+        {
+            let mut it = itertools::peek_nth(&v);
+            it.peek_nth(n);
+            it
+        }
+    }
+    repeat_n {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {}
+        itertools::repeat_n(black_box(0), black_box(1024))
+    }
+    merge {
+        {
+            let v1 = black_box((0..1024).collect_vec());
+            let v2 = black_box((0..768).collect_vec());
+        }
+        v1.iter().merge(&v2)
+    }
+    merge_by {
+        {
+            let v1 = black_box((0..1024).collect_vec());
+            let v2 = black_box((0..768).collect_vec());
+        }
+        v1.iter().merge_by(&v2, PartialOrd::ge)
+    }
+    merge_join_by_ordering {
+        {
+            let v1 = black_box((0..1024).collect_vec());
+            let v2 = black_box((0..768).collect_vec());
+        }
+        v1.iter().merge_join_by(&v2, Ord::cmp)
+    }
+    merge_join_by_bool {
+        {
+            let v1 = black_box((0..1024).collect_vec());
+            let v2 = black_box((0..768).collect_vec());
+        }
+        v1.iter().merge_join_by(&v2, PartialOrd::ge)
+    }
+    kmerge {
+        {
+            let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]);
+        }
+        vs.iter().kmerge()
+    }
+    kmerge_by {
+        {
+            let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]);
+        }
+        vs.iter().kmerge_by(PartialOrd::ge)
+    }
+    map_into {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0_u8; 1024]);
+        }
+        v.iter().copied().map_into::<u32>()
+    }
+    map_ok {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v = black_box((0_u32..1024)
+                .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) })
+                .collect_vec());
+        }
+        v.iter().copied().map_ok(|x| x + 1)
+    }
+    filter_ok {
+        {
+            let v = black_box((0_u32..1024)
+                .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) })
+                .collect_vec());
+        }
+        v.iter().copied().filter_ok(|x| x % 3 == 0)
+    }
+    filter_map_ok {
+        {
+            let v = black_box((0_u32..1024)
+                .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) })
+                .collect_vec());
+        }
+        v.iter().copied().filter_map_ok(|x| if x % 3 == 0 { Some(x + 1) } else { None })
+    }
+    flatten_ok {
+        DoubleEndedIterator
+        {
+            let d = black_box(vec![0; 8]);
+            let v = black_box((0..512)
+                .map(|x| if x % 2 == 0 { Ok(&d) } else { Err(x) })
+                .collect_vec());
+        }
+        v.iter().copied().flatten_ok()
+    }
+}
diff --git a/crates/itertools/benches/tree_fold1.rs b/crates/itertools/benches/tree_fold1.rs
deleted file mode 100644
index f12995d..0000000
--- a/crates/itertools/benches/tree_fold1.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use criterion::{criterion_group, criterion_main, Criterion};
-use itertools::{Itertools, cloned};
-
-trait IterEx : Iterator {
-    // Another efficient implementation against which to compare,
-    // but needs `std` so is less desirable.
-    fn tree_fold1_vec<F>(self, mut f: F) -> Option<Self::Item>
-        where F: FnMut(Self::Item, Self::Item) -> Self::Item,
-              Self: Sized,
-    {
-        let hint = self.size_hint().0;
-        let cap = std::mem::size_of::<usize>() * 8 - hint.leading_zeros() as usize;
-        let mut stack = Vec::with_capacity(cap);
-        self.enumerate().for_each(|(mut i, mut x)| {
-            while (i & 1) != 0 {
-                x = f(stack.pop().unwrap(), x);
-                i >>= 1;
-            }
-            stack.push(x);
-        });
-        stack.into_iter().fold1(f)
-    }
-}
-impl<T:Iterator> IterEx for T {}
-
-macro_rules! def_benchs {
-    ($N:expr,
-     $FUN:ident,
-     $BENCH_NAME:ident,
-     ) => (
-        mod $BENCH_NAME {
-            use super::*;
-
-            pub fn sum(c: &mut Criterion) {
-                let v: Vec<u32> = (0.. $N).collect();
-
-                c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " sum"), move |b| {
-                    b.iter(|| {
-                        cloned(&v).$FUN(|x, y| x + y)
-                    })
-                });
-            }
-
-            pub fn complex_iter(c: &mut Criterion) {
-                let u = (3..).take($N / 2);
-                let v = (5..).take($N / 2);
-                let it = u.chain(v);
-
-                c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"), move |b| {
-                    b.iter(|| {
-                        it.clone().map(|x| x as f32).$FUN(f32::atan2)
-                    })
-                });
-            }
-
-            pub fn string_format(c: &mut Criterion) {
-                // This goes quadratic with linear `fold1`, so use a smaller
-                // size to not waste too much time in travis.  The allocations
-                // in here are so expensive anyway that it'll still take
-                // way longer per iteration than the other two benchmarks.
-                let v: Vec<u32> = (0.. ($N/4)).collect();
-
-                c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " string format"), move |b| {
-                    b.iter(|| {
-                        cloned(&v).map(|x| x.to_string()).$FUN(|x, y| format!("{} + {}", x, y))
-                    })
-                });
-            }
-        }
-
-        criterion_group!(
-            $BENCH_NAME,
-            $BENCH_NAME::sum,
-            $BENCH_NAME::complex_iter,
-            $BENCH_NAME::string_format,
-        );
-    )
-}
-
-def_benchs!{
-    10_000,
-    fold1,
-    fold1_10k,
-}
-
-def_benchs!{
-    10_000,
-    tree_fold1,
-    tree_fold1_stack_10k,
-}
-
-def_benchs!{
-    10_000,
-    tree_fold1_vec,
-    tree_fold1_vec_10k,
-}
-
-def_benchs!{
-    100,
-    fold1,
-    fold1_100,
-}
-
-def_benchs!{
-    100,
-    tree_fold1,
-    tree_fold1_stack_100,
-}
-
-def_benchs!{
-    100,
-    tree_fold1_vec,
-    tree_fold1_vec_100,
-}
-
-def_benchs!{
-    8,
-    fold1,
-    fold1_08,
-}
-
-def_benchs!{
-    8,
-    tree_fold1,
-    tree_fold1_stack_08,
-}
-
-def_benchs!{
-    8,
-    tree_fold1_vec,
-    tree_fold1_vec_08,
-}
-
-criterion_main!(
-    fold1_10k,
-    tree_fold1_stack_10k,
-    tree_fold1_vec_10k,
-    fold1_100,
-    tree_fold1_stack_100,
-    tree_fold1_vec_100,
-    fold1_08,
-    tree_fold1_stack_08,
-    tree_fold1_vec_08,
-);
diff --git a/crates/itertools/benches/tree_reduce.rs b/crates/itertools/benches/tree_reduce.rs
new file mode 100644
index 0000000..051b148
--- /dev/null
+++ b/crates/itertools/benches/tree_reduce.rs
@@ -0,0 +1,150 @@
+#![allow(deprecated)]
+
+use criterion::{criterion_group, criterion_main, Criterion};
+use itertools::{cloned, Itertools};
+
+trait IterEx: Iterator {
+    // Another efficient implementation against which to compare,
+    // but needs `std` so is less desirable.
+    fn tree_reduce_vec<F>(self, mut f: F) -> Option<Self::Item>
+    where
+        F: FnMut(Self::Item, Self::Item) -> Self::Item,
+        Self: Sized,
+    {
+        let hint = self.size_hint().0;
+        let cap = std::mem::size_of::<usize>() * 8 - hint.leading_zeros() as usize;
+        let mut stack = Vec::with_capacity(cap);
+        self.enumerate().for_each(|(mut i, mut x)| {
+            while (i & 1) != 0 {
+                x = f(stack.pop().unwrap(), x);
+                i >>= 1;
+            }
+            stack.push(x);
+        });
+        stack.into_iter().fold1(f)
+    }
+}
+impl<T: Iterator> IterEx for T {}
+
+macro_rules! def_benchs {
+    ($N:expr,
+     $FUN:ident,
+     $BENCH_NAME:ident,
+     ) => {
+        mod $BENCH_NAME {
+            use super::*;
+
+            pub fn sum(c: &mut Criterion) {
+                let v: Vec<u32> = (0..$N).collect();
+
+                c.bench_function(
+                    &(stringify!($BENCH_NAME).replace('_', " ") + " sum"),
+                    move |b| b.iter(|| cloned(&v).$FUN(|x, y| x + y)),
+                );
+            }
+
+            pub fn complex_iter(c: &mut Criterion) {
+                let u = (3..).take($N / 2);
+                let v = (5..).take($N / 2);
+                let it = u.chain(v);
+
+                c.bench_function(
+                    &(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"),
+                    move |b| b.iter(|| it.clone().map(|x| x as f32).$FUN(f32::atan2)),
+                );
+            }
+
+            pub fn string_format(c: &mut Criterion) {
+                // This goes quadratic with linear `fold1`, so use a smaller
+                // size to not waste too much time in travis.  The allocations
+                // in here are so expensive anyway that it'll still take
+                // way longer per iteration than the other two benchmarks.
+                let v: Vec<u32> = (0..($N / 4)).collect();
+
+                c.bench_function(
+                    &(stringify!($BENCH_NAME).replace('_', " ") + " string format"),
+                    move |b| {
+                        b.iter(|| {
+                            cloned(&v)
+                                .map(|x| x.to_string())
+                                .$FUN(|x, y| format!("{} + {}", x, y))
+                        })
+                    },
+                );
+            }
+        }
+
+        criterion_group!(
+            $BENCH_NAME,
+            $BENCH_NAME::sum,
+            $BENCH_NAME::complex_iter,
+            $BENCH_NAME::string_format,
+        );
+    };
+}
+
+def_benchs! {
+    10_000,
+    fold1,
+    fold1_10k,
+}
+
+def_benchs! {
+    10_000,
+    tree_reduce,
+    tree_reduce_stack_10k,
+}
+
+def_benchs! {
+    10_000,
+    tree_reduce_vec,
+    tree_reduce_vec_10k,
+}
+
+def_benchs! {
+    100,
+    fold1,
+    fold1_100,
+}
+
+def_benchs! {
+    100,
+    tree_reduce,
+    tree_reduce_stack_100,
+}
+
+def_benchs! {
+    100,
+    tree_reduce_vec,
+    tree_reduce_vec_100,
+}
+
+def_benchs! {
+    8,
+    fold1,
+    fold1_08,
+}
+
+def_benchs! {
+    8,
+    tree_reduce,
+    tree_reduce_stack_08,
+}
+
+def_benchs! {
+    8,
+    tree_reduce_vec,
+    tree_reduce_vec_08,
+}
+
+criterion_main!(
+    fold1_10k,
+    tree_reduce_stack_10k,
+    tree_reduce_vec_10k,
+    fold1_100,
+    tree_reduce_stack_100,
+    tree_reduce_vec_100,
+    fold1_08,
+    tree_reduce_stack_08,
+    tree_reduce_vec_08,
+);
diff --git a/crates/itertools/benches/tuples.rs b/crates/itertools/benches/tuples.rs
index ea50aaa..2eca347 100644
--- a/crates/itertools/benches/tuples.rs
+++ b/crates/itertools/benches/tuples.rs
@@ -33,7 +33,7 @@
     s4(s[0], s[1], s[2], s[3])
 }
 
-fn sum_t1(s: &(&u32, )) -> u32 {
+fn sum_t1(s: &(&u32,)) -> u32 {
     s1(*s.0)
 }
 
@@ -60,9 +60,9 @@
      $WINDOWS:ident;
      $FOR_CHUNKS:ident,
      $FOR_WINDOWS:ident
-     ) => (
+     ) => {
         fn $FOR_CHUNKS(c: &mut Criterion) {
-            let v: Vec<u32> = (0.. $N * 1_000).collect();
+            let v: Vec<u32> = (0..$N * 1_000).collect();
             let mut s = 0;
             c.bench_function(&stringify!($FOR_CHUNKS).replace('_', " "), move |b| {
                 b.iter(|| {
@@ -90,7 +90,7 @@
         }
 
         fn $TUPLES(c: &mut Criterion) {
-            let v: Vec<u32> = (0.. $N * 1_000).collect();
+            let v: Vec<u32> = (0..$N * 1_000).collect();
             let mut s = 0;
             c.bench_function(&stringify!($TUPLES).replace('_', " "), move |b| {
                 b.iter(|| {
@@ -103,7 +103,7 @@
         }
 
         fn $CHUNKS(c: &mut Criterion) {
-            let v: Vec<u32> = (0.. $N * 1_000).collect();
+            let v: Vec<u32> = (0..$N * 1_000).collect();
             let mut s = 0;
             c.bench_function(&stringify!($CHUNKS).replace('_', " "), move |b| {
                 b.iter(|| {
@@ -150,10 +150,10 @@
             $TUPLE_WINDOWS,
             $WINDOWS,
         );
-    )
+    };
 }
 
-def_benchs!{
+def_benchs! {
     1;
     benches_1,
     sum_t1,
@@ -166,7 +166,7 @@
     for_windows_1
 }
 
-def_benchs!{
+def_benchs! {
     2;
     benches_2,
     sum_t2,
@@ -179,7 +179,7 @@
     for_windows_2
 }
 
-def_benchs!{
+def_benchs! {
     3;
     benches_3,
     sum_t3,
@@ -192,7 +192,7 @@
     for_windows_3
 }
 
-def_benchs!{
+def_benchs! {
     4;
     benches_4,
     sum_t4,
@@ -205,9 +205,4 @@
     for_windows_4
 }
 
-criterion_main!(
-    benches_1,
-    benches_2,
-    benches_3,
-    benches_4,
-);
+criterion_main!(benches_1, benches_2, benches_3, benches_4,);
diff --git a/crates/itertools/clippy.toml b/crates/itertools/clippy.toml
deleted file mode 100644
index 0a54853..0000000
--- a/crates/itertools/clippy.toml
+++ /dev/null
@@ -1 +0,0 @@
-msrv = "1.36.0"
diff --git a/crates/itertools/examples/iris.rs b/crates/itertools/examples/iris.rs
index 987d9e9..63f9c48 100644
--- a/crates/itertools/examples/iris.rs
+++ b/crates/itertools/examples/iris.rs
@@ -3,14 +3,13 @@
 /// and does some simple manipulations.
 ///
 /// Iterators and itertools functionality are used throughout.
-
 use itertools::Itertools;
 use std::collections::HashMap;
 use std::iter::repeat;
 use std::num::ParseFloatError;
 use std::str::FromStr;
 
-static DATA: &'static str = include_str!("iris.data");
+static DATA: &str = include_str!("iris.data");
 
 #[derive(Clone, Debug)]
 struct Iris {
@@ -18,6 +17,7 @@
     data: [f32; 4],
 }
 
+#[allow(dead_code)] // fields are currently ignored
 #[derive(Clone, Debug)]
 enum ParseError {
     Numeric(ParseFloatError),
@@ -26,7 +26,7 @@
 
 impl From<ParseFloatError> for ParseError {
     fn from(err: ParseFloatError) -> Self {
-        ParseError::Numeric(err)
+        Self::Numeric(err)
     }
 }
 
@@ -35,8 +35,11 @@
     type Err = ParseError;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let mut iris = Iris { name: "".into(), data: [0.; 4] };
-        let mut parts = s.split(",").map(str::trim);
+        let mut iris = Self {
+            name: "".into(),
+            data: [0.; 4],
+        };
+        let mut parts = s.split(',').map(str::trim);
 
         // using Iterator::by_ref()
         for (index, part) in parts.by_ref().take(4).enumerate() {
@@ -45,7 +48,7 @@
         if let Some(name) = parts.next() {
             iris.name = name.into();
         } else {
-            return Err(ParseError::Other("Missing name"))
+            return Err(ParseError::Other("Missing name"));
         }
         Ok(iris)
     }
@@ -53,12 +56,13 @@
 
 fn main() {
     // using Itertools::fold_results to create the result of parsing
-    let irises = DATA.lines()
-                     .map(str::parse)
-                     .fold_ok(Vec::new(), |mut v, iris: Iris| {
-                         v.push(iris);
-                         v
-                     });
+    let irises = DATA
+        .lines()
+        .map(str::parse)
+        .fold_ok(Vec::new(), |mut v, iris: Iris| {
+            v.push(iris);
+            v
+        });
     let mut irises = match irises {
         Err(e) => {
             println!("Error parsing: {:?}", e);
@@ -74,19 +78,18 @@
     let mut plot_symbols = "+ox".chars().cycle();
     let mut symbolmap = HashMap::new();
 
-    // using Itertools::group_by
-    for (species, species_group) in &irises.iter().group_by(|iris| &iris.name) {
+    // using Itertools::chunk_by
+    for (species, species_chunk) in &irises.iter().chunk_by(|iris| &iris.name) {
         // assign a plot symbol
-        symbolmap.entry(species).or_insert_with(|| {
-            plot_symbols.next().unwrap()
-        });
+        symbolmap
+            .entry(species)
+            .or_insert_with(|| plot_symbols.next().unwrap());
         println!("{} (symbol={})", species, symbolmap[species]);
 
-        for iris in species_group {
+        for iris in species_chunk {
             // using Itertools::format for lazy formatting
             println!("{:>3.1}", iris.data.iter().format(", "));
         }
-
     }
 
     // Look at all combinations of the four columns
diff --git a/crates/itertools/src/adaptors/coalesce.rs b/crates/itertools/src/adaptors/coalesce.rs
index 3df7cc5..ab1ab52 100644
--- a/crates/itertools/src/adaptors/coalesce.rs
+++ b/crates/itertools/src/adaptors/coalesce.rs
@@ -4,62 +4,78 @@
 use crate::size_hint;
 
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct CoalesceBy<I, F, T>
+pub struct CoalesceBy<I, F, C>
 where
     I: Iterator,
+    C: CountItem<I::Item>,
 {
     iter: I,
-    last: Option<T>,
+    /// `last` is `None` while no item have been taken out of `iter` (at definition).
+    /// Then `last` will be `Some(Some(item))` until `iter` is exhausted,
+    /// in which case `last` will be `Some(None)`.
+    last: Option<Option<C::CItem>>,
     f: F,
 }
 
-impl<I: Clone, F: Clone, T: Clone> Clone for CoalesceBy<I, F, T>
+impl<I, F, C> Clone for CoalesceBy<I, F, C>
 where
-    I: Iterator,
+    I: Clone + Iterator,
+    F: Clone,
+    C: CountItem<I::Item>,
+    C::CItem: Clone,
 {
     clone_fields!(last, iter, f);
 }
 
-impl<I, F, T> fmt::Debug for CoalesceBy<I, F, T>
+impl<I, F, C> fmt::Debug for CoalesceBy<I, F, C>
 where
     I: Iterator + fmt::Debug,
-    T: fmt::Debug,
+    C: CountItem<I::Item>,
+    C::CItem: fmt::Debug,
 {
-    debug_fmt_fields!(CoalesceBy, iter);
+    debug_fmt_fields!(CoalesceBy, iter, last);
 }
 
 pub trait CoalescePredicate<Item, T> {
     fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)>;
 }
 
-impl<I, F, T> Iterator for CoalesceBy<I, F, T>
+impl<I, F, C> Iterator for CoalesceBy<I, F, C>
 where
     I: Iterator,
-    F: CoalescePredicate<I::Item, T>,
+    F: CoalescePredicate<I::Item, C::CItem>,
+    C: CountItem<I::Item>,
 {
-    type Item = T;
+    type Item = C::CItem;
 
     fn next(&mut self) -> Option<Self::Item> {
+        let Self { iter, last, f } = self;
         // this fuses the iterator
-        let last = self.last.take()?;
+        let init = match last {
+            Some(elt) => elt.take(),
+            None => {
+                *last = Some(None);
+                iter.next().map(C::new)
+            }
+        }?;
 
-        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),
+            iter.try_fold(init, |accum, next| match f.coalesce_pair(accum, next) {
+                Ok(joined) => Ok(joined),
+                Err((last_, next_)) => {
+                    *last = Some(Some(next_));
+                    Err(last_)
+                }
+            })
+            .unwrap_or_else(|x| x),
         )
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), self.last.is_some() as usize);
+        let (low, hi) = size_hint::add_scalar(
+            self.iter.size_hint(),
+            matches!(self.last, Some(Some(_))) as usize,
+        );
         ((low > 0) as usize, hi)
     }
 
@@ -67,9 +83,13 @@
     where
         FnAcc: FnMut(Acc, Self::Item) -> Acc,
     {
-        if let Some(last) = self.last {
-            let mut f = self.f;
-            let (last, acc) = self.iter.fold((last, acc), |(last, acc), elt| {
+        let Self {
+            mut iter,
+            last,
+            mut f,
+        } = self;
+        if let Some(last) = last.unwrap_or_else(|| iter.next().map(C::new)) {
+            let (last, acc) = iter.fold((last, acc), |(last, acc), elt| {
                 match f.coalesce_pair(last, elt) {
                     Ok(joined) => (joined, acc),
                     Err((last_, next_)) => (next_, fn_acc(acc, last_)),
@@ -82,12 +102,43 @@
     }
 }
 
-impl<I: Iterator, F: CoalescePredicate<I::Item, T>, T> FusedIterator for CoalesceBy<I, F, T> {}
+impl<I, F, C> FusedIterator for CoalesceBy<I, F, C>
+where
+    I: Iterator,
+    F: CoalescePredicate<I::Item, C::CItem>,
+    C: CountItem<I::Item>,
+{
+}
+
+pub struct NoCount;
+
+pub struct WithCount;
+
+pub trait CountItem<T> {
+    type CItem;
+    fn new(t: T) -> Self::CItem;
+}
+
+impl<T> CountItem<T> for NoCount {
+    type CItem = T;
+    #[inline(always)]
+    fn new(t: T) -> T {
+        t
+    }
+}
+
+impl<T> CountItem<T> for WithCount {
+    type CItem = (usize, T);
+    #[inline(always)]
+    fn new(t: T) -> (usize, T) {
+        (1, t)
+    }
+}
 
 /// An iterator adaptor that may join together adjacent elements.
 ///
 /// See [`.coalesce()`](crate::Itertools::coalesce) for more information.
-pub type Coalesce<I, F> = CoalesceBy<I, F, <I as Iterator>::Item>;
+pub type Coalesce<I, F> = CoalesceBy<I, F, NoCount>;
 
 impl<F, Item, T> CoalescePredicate<Item, T> for F
 where
@@ -99,12 +150,12 @@
 }
 
 /// Create a new `Coalesce`.
-pub fn coalesce<I, F>(mut iter: I, f: F) -> Coalesce<I, F>
+pub fn coalesce<I, F>(iter: I, f: F) -> Coalesce<I, F>
 where
     I: Iterator,
 {
     Coalesce {
-        last: iter.next(),
+        last: None,
         iter,
         f,
     }
@@ -113,7 +164,7 @@
 /// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function.
 ///
 /// See [`.dedup_by()`](crate::Itertools::dedup_by) or [`.dedup()`](crate::Itertools::dedup) for more information.
-pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as Iterator>::Item>;
+pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, NoCount>;
 
 #[derive(Clone)]
 pub struct DedupPred2CoalescePred<DP>(DP);
@@ -156,12 +207,12 @@
 }
 
 /// Create a new `DedupBy`.
-pub fn dedup_by<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupBy<I, Pred>
+pub fn dedup_by<I, Pred>(iter: I, dedup_pred: Pred) -> DedupBy<I, Pred>
 where
     I: Iterator,
 {
     DedupBy {
-        last: iter.next(),
+        last: None,
         iter,
         f: DedupPred2CoalescePred(dedup_pred),
     }
@@ -186,7 +237,7 @@
 /// See [`.dedup_by_with_count()`](crate::Itertools::dedup_by_with_count) or
 /// [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information.
 pub type DedupByWithCount<I, Pred> =
-    CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>;
+    CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, WithCount>;
 
 #[derive(Clone, Debug)]
 pub struct DedupPredWithCount2CoalescePred<DP>(DP);
@@ -215,12 +266,12 @@
 pub type DedupWithCount<I> = DedupByWithCount<I, DedupEq>;
 
 /// Create a new `DedupByWithCount`.
-pub fn dedup_by_with_count<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred>
+pub fn dedup_by_with_count<I, Pred>(iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred>
 where
     I: Iterator,
 {
     DedupByWithCount {
-        last: iter.next().map(|v| (1, v)),
+        last: None,
         iter,
         f: DedupPredWithCount2CoalescePred(dedup_pred),
     }
diff --git a/crates/itertools/src/adaptors/map.rs b/crates/itertools/src/adaptors/map.rs
index cf5e5a0..c78b9be 100644
--- a/crates/itertools/src/adaptors/map.rs
+++ b/crates/itertools/src/adaptors/map.rs
@@ -4,8 +4,8 @@
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct MapSpecialCase<I, F> {
-    iter: I,
-    f: F,
+    pub(crate) iter: I,
+    pub(crate) f: F,
 }
 
 pub trait MapSpecialCaseFn<T> {
@@ -67,10 +67,6 @@
 /// See [`.map_ok()`](crate::Itertools::map_ok) for more information.
 pub type MapOk<I, F> = MapSpecialCase<I, MapSpecialCaseFnOk<F>>;
 
-/// See [`MapOk`].
-#[deprecated(note = "Use MapOk instead", since = "0.10.0")]
-pub type MapResults<I, F> = MapOk<I, F>;
-
 impl<F, T, U, E> MapSpecialCaseFn<Result<T, E>> for MapSpecialCaseFnOk<F>
 where
     F: FnMut(T) -> U,
@@ -112,9 +108,19 @@
     }
 }
 
-#[derive(Clone, Debug)]
 pub struct MapSpecialCaseFnInto<U>(PhantomData<U>);
 
+impl<U> std::fmt::Debug for MapSpecialCaseFnInto<U> {
+    debug_fmt_fields!(MapSpecialCaseFnInto, 0);
+}
+
+impl<U> Clone for MapSpecialCaseFnInto<U> {
+    #[inline]
+    fn clone(&self) -> Self {
+        Self(PhantomData)
+    }
+}
+
 /// Create a new [`MapInto`] iterator.
 pub fn map_into<I, R>(iter: I) -> MapInto<I, R> {
     MapSpecialCase {
diff --git a/crates/itertools/src/adaptors/mod.rs b/crates/itertools/src/adaptors/mod.rs
index 1695bbd..52e36c4 100644
--- a/crates/itertools/src/adaptors/mod.rs
+++ b/crates/itertools/src/adaptors/mod.rs
@@ -5,19 +5,17 @@
 //! except according to those terms.
 
 mod coalesce;
-mod map;
+pub(crate) mod map;
 mod multi_product;
 pub use self::coalesce::*;
 pub use self::map::{map_into, map_ok, MapInto, MapOk};
-#[allow(deprecated)]
-pub use self::map::MapResults;
 #[cfg(feature = "use_alloc")]
 pub use self::multi_product::*;
 
+use crate::size_hint::{self, SizeHint};
 use std::fmt;
-use std::iter::{Fuse, Peekable, FromIterator, FusedIterator};
+use std::iter::{Enumerate, FromIterator, Fuse, FusedIterator};
 use std::marker::PhantomData;
-use crate::size_hint;
 
 /// An iterator adaptor that alternates elements from two iterators until both
 /// run out.
@@ -28,55 +26,90 @@
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Interleave<I, J> {
-    a: Fuse<I>,
-    b: Fuse<J>,
-    flag: bool,
+    i: Fuse<I>,
+    j: Fuse<J>,
+    next_coming_from_j: bool,
 }
 
 /// Create an iterator that interleaves elements in `i` and `j`.
 ///
-/// [`IntoIterator`] enabled version of `[Itertools::interleave]`.
-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>
+/// [`IntoIterator`] enabled version of [`Itertools::interleave`](crate::Itertools::interleave).
+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>,
 {
     Interleave {
-        a: i.into_iter().fuse(),
-        b: j.into_iter().fuse(),
-        flag: false,
+        i: i.into_iter().fuse(),
+        j: j.into_iter().fuse(),
+        next_coming_from_j: false,
     }
 }
 
 impl<I, J> Iterator for Interleave<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
 {
     type Item = I::Item;
     #[inline]
     fn next(&mut self) -> Option<Self::Item> {
-        self.flag = !self.flag;
-        if self.flag {
-            match self.a.next() {
-                None => self.b.next(),
+        self.next_coming_from_j = !self.next_coming_from_j;
+        if self.next_coming_from_j {
+            match self.i.next() {
+                None => self.j.next(),
                 r => r,
             }
         } else {
-            match self.b.next() {
-                None => self.a.next(),
+            match self.j.next() {
+                None => self.i.next(),
                 r => r,
             }
         }
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
-        size_hint::add(self.a.size_hint(), self.b.size_hint())
+        size_hint::add(self.i.size_hint(), self.j.size_hint())
+    }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let Self {
+            mut i,
+            mut j,
+            next_coming_from_j,
+        } = self;
+        if next_coming_from_j {
+            match j.next() {
+                Some(y) => init = f(init, y),
+                None => return i.fold(init, f),
+            }
+        }
+        let res = i.try_fold(init, |mut acc, x| {
+            acc = f(acc, x);
+            match j.next() {
+                Some(y) => Ok(f(acc, y)),
+                None => Err(acc),
+            }
+        });
+        match res {
+            Ok(acc) => j.fold(acc, f),
+            Err(acc) => i.fold(acc, f),
+        }
     }
 }
 
 impl<I, J> FusedIterator for Interleave<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
-{}
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
+{
+}
 
 /// An iterator adaptor that alternates elements from the two iterators until
 /// one of them runs out.
@@ -88,37 +121,44 @@
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct InterleaveShortest<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
 {
-    it0: I,
-    it1: J,
-    phase: bool, // false ==> it0, true ==> it1
+    i: I,
+    j: J,
+    next_coming_from_j: bool,
 }
 
 /// Create a new `InterleaveShortest` iterator.
-pub fn interleave_shortest<I, J>(a: I, b: J) -> InterleaveShortest<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
+pub fn interleave_shortest<I, J>(i: I, j: J) -> InterleaveShortest<I, J>
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
 {
     InterleaveShortest {
-        it0: a,
-        it1: b,
-        phase: false,
+        i,
+        j,
+        next_coming_from_j: false,
     }
 }
 
 impl<I, J> Iterator for InterleaveShortest<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
 {
     type Item = I::Item;
 
     #[inline]
     fn next(&mut self) -> Option<Self::Item> {
-        let e = if self.phase { self.it1.next() } else { self.it0.next() };
+        let e = if self.next_coming_from_j {
+            self.j.next()
+        } else {
+            self.i.next()
+        };
         if e.is_some() {
-            self.phase = !self.phase;
+            self.next_coming_from_j = !self.next_coming_from_j;
         }
         e
     }
@@ -126,24 +166,23 @@
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
         let (curr_hint, next_hint) = {
-            let it0_hint = self.it0.size_hint();
-            let it1_hint = self.it1.size_hint();
-            if self.phase {
-                (it1_hint, it0_hint)
+            let i_hint = self.i.size_hint();
+            let j_hint = self.j.size_hint();
+            if self.next_coming_from_j {
+                (j_hint, i_hint)
             } else {
-                (it0_hint, it1_hint)
+                (i_hint, j_hint)
             }
         };
         let (curr_lower, curr_upper) = curr_hint;
         let (next_lower, next_upper) = next_hint;
         let (combined_lower, combined_upper) =
             size_hint::mul_scalar(size_hint::min(curr_hint, next_hint), 2);
-        let lower =
-            if curr_lower > next_lower {
-                combined_lower + 1
-            } else {
-                combined_lower
-            };
+        let lower = if curr_lower > next_lower {
+            combined_lower + 1
+        } else {
+            combined_lower
+        };
         let upper = {
             let extra_elem = match (curr_upper, next_upper) {
                 (_, None) => false,
@@ -158,20 +197,52 @@
         };
         (lower, upper)
     }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let Self {
+            mut i,
+            mut j,
+            next_coming_from_j,
+        } = self;
+        if next_coming_from_j {
+            match j.next() {
+                Some(y) => init = f(init, y),
+                None => return init,
+            }
+        }
+        let res = i.try_fold(init, |mut acc, x| {
+            acc = f(acc, x);
+            match j.next() {
+                Some(y) => Ok(f(acc, y)),
+                None => Err(acc),
+            }
+        });
+        match res {
+            Ok(val) => val,
+            Err(val) => val,
+        }
+    }
 }
 
 impl<I, J> FusedIterator for InterleaveShortest<I, J>
-    where I: FusedIterator,
-          J: FusedIterator<Item = I::Item>
-{}
+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.
 ///
 /// Iterator element type is `I::Item`.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct PutBack<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     top: Option<I::Item>,
     iter: I,
@@ -179,7 +250,8 @@
 
 /// Create an iterator where you can put back a single item
 pub fn put_back<I>(iterable: I) -> PutBack<I::IntoIter>
-    where I: IntoIterator
+where
+    I: IntoIterator,
 {
     PutBack {
         top: None,
@@ -188,7 +260,8 @@
 }
 
 impl<I> PutBack<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     /// put back value `value` (builder method)
     pub fn with_value(mut self, value: I::Item) -> Self {
@@ -199,21 +272,22 @@
     /// Split the `PutBack` into its parts.
     #[inline]
     pub fn into_parts(self) -> (Option<I::Item>, I) {
-        let PutBack{top, iter} = self;
+        let Self { top, iter } = self;
         (top, iter)
     }
 
     /// Put back a single value to the front of the iterator.
     ///
-    /// If a value is already in the put back slot, it is overwritten.
+    /// If a value is already in the put back slot, it is returned.
     #[inline]
-    pub fn put_back(&mut self, x: I::Item) {
-        self.top = Some(x);
+    pub fn put_back(&mut self, x: I::Item) -> Option<I::Item> {
+        self.top.replace(x)
     }
 }
 
 impl<I> Iterator for PutBack<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     type Item = I::Item;
     #[inline]
@@ -252,7 +326,8 @@
     }
 
     fn all<G>(&mut self, mut f: G) -> bool
-        where G: FnMut(Self::Item) -> bool
+    where
+        G: FnMut(Self::Item) -> bool,
     {
         if let Some(elt) = self.top.take() {
             if !f(elt) {
@@ -263,7 +338,8 @@
     }
 
     fn fold<Acc, G>(mut self, init: Acc, mut f: G) -> Acc
-        where G: FnMut(Acc, Self::Item) -> Acc,
+    where
+        G: FnMut(Acc, Self::Item) -> Acc,
     {
         let mut accum = init;
         if let Some(elt) = self.top.take() {
@@ -282,10 +358,14 @@
 /// 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
+where
+    I: Iterator,
 {
     a: I,
-    a_cur: Option<I::Item>,
+    /// `a_cur` is `None` while no item have been taken out of `a` (at definition).
+    /// Then `a_cur` will be `Some(Some(item))` until `a` is exhausted,
+    /// in which case `a_cur` will be `Some(None)`.
+    a_cur: Option<Option<I::Item>>,
     b: J,
     b_orig: J,
 }
@@ -293,13 +373,14 @@
 /// Create a new cartesian product iterator
 ///
 /// Iterator element type is `(I::Item, J::Item)`.
-pub fn cartesian_product<I, J>(mut i: I, j: J) -> Product<I, J>
-    where I: Iterator,
-          J: Clone + Iterator,
-          I::Item: Clone
+pub fn cartesian_product<I, J>(i: I, j: J) -> Product<I, J>
+where
+    I: Iterator,
+    J: Clone + Iterator,
+    I::Item: Clone,
 {
     Product {
-        a_cur: i.next(),
+        a_cur: None,
         a: i,
         b: j.clone(),
         b_orig: j,
@@ -307,54 +388,69 @@
 }
 
 impl<I, J> Iterator for Product<I, J>
-    where I: Iterator,
-          J: Clone + Iterator,
-          I::Item: Clone
+where
+    I: Iterator,
+    J: Clone + Iterator,
+    I::Item: Clone,
 {
     type Item = (I::Item, J::Item);
 
     fn next(&mut self) -> Option<Self::Item> {
-        let elt_b = match self.b.next() {
+        let Self {
+            a,
+            a_cur,
+            b,
+            b_orig,
+        } = self;
+        let elt_b = match b.next() {
             None => {
-                self.b = self.b_orig.clone();
-                match self.b.next() {
+                *b = b_orig.clone();
+                match b.next() {
                     None => return None,
                     Some(x) => {
-                        self.a_cur = self.a.next();
+                        *a_cur = Some(a.next());
                         x
                     }
                 }
             }
-            Some(x) => x
+            Some(x) => x,
         };
-        self.a_cur.as_ref().map(|a| (a.clone(), elt_b))
+        a_cur
+            .get_or_insert_with(|| a.next())
+            .as_ref()
+            .map(|a| (a.clone(), elt_b))
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let has_cur = self.a_cur.is_some() as usize;
         // Not ExactSizeIterator because size may be larger than usize
-        let (b_min, b_max) = self.b.size_hint();
-
         // Compute a * b_orig + b for both lower and upper bound
-        size_hint::add(
-            size_hint::mul(self.a.size_hint(), self.b_orig.size_hint()),
-            (b_min * has_cur, b_max.map(move |x| x * has_cur)))
+        let mut sh = size_hint::mul(self.a.size_hint(), self.b_orig.size_hint());
+        if matches!(self.a_cur, Some(Some(_))) {
+            sh = size_hint::add(sh, self.b.size_hint());
+        }
+        sh
     }
 
-    fn fold<Acc, G>(mut self, mut accum: Acc, mut f: G) -> Acc
-        where G: FnMut(Acc, Self::Item) -> Acc,
+    fn fold<Acc, G>(self, mut accum: Acc, mut f: G) -> Acc
+    where
+        G: FnMut(Acc, Self::Item) -> Acc,
     {
         // use a split loop to handle the loose a_cur as well as avoiding to
         // clone b_orig at the end.
-        if let Some(mut a) = self.a_cur.take() {
-            let mut b = self.b;
+        let Self {
+            mut a,
+            a_cur,
+            mut b,
+            b_orig,
+        } = self;
+        if let Some(mut elt_a) = a_cur.unwrap_or_else(|| a.next()) {
             loop {
-                accum = b.fold(accum, |acc, elt| f(acc, (a.clone(), elt)));
+                accum = b.fold(accum, |acc, elt| f(acc, (elt_a.clone(), elt)));
 
                 // we can only continue iterating a if we had a first element;
-                if let Some(next_a) = self.a.next() {
-                    b = self.b_orig.clone();
-                    a = next_a;
+                if let Some(next_elt_a) = a.next() {
+                    b = b_orig.clone();
+                    elt_a = next_elt_a;
                 } else {
                     break;
                 }
@@ -365,15 +461,17 @@
 }
 
 impl<I, J> FusedIterator for Product<I, J>
-    where I: FusedIterator,
-          J: Clone + FusedIterator,
-          I::Item: Clone
-{}
+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\>*.
+/// Iterator element type is `X` if the return type of `F` is `Option<X>`.
 ///
 /// See [`.batching()`](crate::Itertools::batching) for more information.
 #[derive(Clone)]
@@ -383,7 +481,10 @@
     iter: I,
 }
 
-impl<I, F> fmt::Debug for Batching<I, F> where I: fmt::Debug {
+impl<I, F> fmt::Debug for Batching<I, F>
+where
+    I: fmt::Debug,
+{
     debug_fmt_fields!(Batching, iter);
 }
 
@@ -393,8 +494,9 @@
 }
 
 impl<B, F, I> Iterator for Batching<I, F>
-    where I: Iterator,
-          F: FnMut(&mut I) -> Option<B>
+where
+    I: Iterator,
+    F: FnMut(&mut I) -> Option<B>,
 {
     type Item = B;
     #[inline]
@@ -403,205 +505,6 @@
     }
 }
 
-/// An iterator adaptor that steps a number elements in the base iterator
-/// for each iteration.
-///
-/// The iterator steps by yielding the next element from the base iterator,
-/// then skipping forward *n-1* elements.
-///
-/// See [`.step()`](crate::Itertools::step) for more information.
-#[deprecated(note="Use std .step_by() instead", since="0.8.0")]
-#[allow(deprecated)]
-#[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Step<I> {
-    iter: Fuse<I>,
-    skip: usize,
-}
-
-/// Create a `Step` iterator.
-///
-/// **Panics** if the step is 0.
-#[allow(deprecated)]
-pub fn step<I>(iter: I, step: usize) -> Step<I>
-    where I: Iterator
-{
-    assert!(step != 0);
-    Step {
-        iter: iter.fuse(),
-        skip: step - 1,
-    }
-}
-
-#[allow(deprecated)]
-impl<I> Iterator for Step<I>
-    where I: Iterator
-{
-    type Item = I::Item;
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        let elt = self.iter.next();
-        if self.skip > 0 {
-            self.iter.nth(self.skip - 1);
-        }
-        elt
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let (low, high) = self.iter.size_hint();
-        let div = |x: usize| {
-            if x == 0 {
-                0
-            } else {
-                1 + (x - 1) / (self.skip + 1)
-            }
-        };
-        (div(low), high.map(div))
-    }
-}
-
-// known size
-#[allow(deprecated)]
-impl<I> ExactSizeIterator for Step<I>
-    where I: ExactSizeIterator
-{}
-
-pub trait MergePredicate<T> {
-    fn merge_pred(&mut self, a: &T, b: &T) -> bool;
-}
-
-#[derive(Clone, Debug)]
-pub struct MergeLte;
-
-impl<T: PartialOrd> MergePredicate<T> for MergeLte {
-    fn merge_pred(&mut self, a: &T, b: &T) -> bool {
-        a <= b
-    }
-}
-
-/// An iterator adaptor that merges the two base iterators in ascending order.
-/// If both base iterators are sorted (ascending), the result is sorted.
-///
-/// Iterator element type is `I::Item`.
-///
-/// See [`.merge()`](crate::Itertools::merge_by) for more information.
-pub type Merge<I, J> = MergeBy<I, J, MergeLte>;
-
-/// Create an iterator that merges elements in `i` and `j`.
-///
-/// [`IntoIterator`] enabled version of [`Itertools::merge`](crate::Itertools::merge).
-///
-/// ```
-/// use itertools::merge;
-///
-/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) {
-///     /* loop body */
-/// }
-/// ```
-pub fn merge<I, J>(i: I, j: J) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
-    where I: IntoIterator,
-          J: IntoIterator<Item = I::Item>,
-          I::Item: PartialOrd
-{
-    merge_by_new(i, j, MergeLte)
-}
-
-/// An iterator adaptor that merges the two base iterators in ascending order.
-/// If both base iterators are sorted (ascending), the result is sorted.
-///
-/// Iterator element type is `I::Item`.
-///
-/// 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,
-          J: Iterator<Item = I::Item>
-{
-    a: Peekable<I>,
-    b: Peekable<J>,
-    fused: Option<bool>,
-    cmp: F,
-}
-
-impl<I, J, F> fmt::Debug for MergeBy<I, J, F>
-    where I: Iterator + fmt::Debug, J: Iterator<Item = I::Item> + fmt::Debug,
-          I::Item: fmt::Debug,
-{
-    debug_fmt_fields!(MergeBy, a, b);
-}
-
-impl<T, F: FnMut(&T, &T)->bool> MergePredicate<T> for F {
-    fn merge_pred(&mut self, a: &T, b: &T) -> bool {
-        self(a, b)
-    }
-}
-
-/// Create a `MergeBy` iterator.
-pub fn merge_by_new<I, J, F>(a: I, b: J, cmp: F) -> MergeBy<I::IntoIter, J::IntoIter, F>
-    where I: IntoIterator,
-          J: IntoIterator<Item = I::Item>,
-          F: MergePredicate<I::Item>,
-{
-    MergeBy {
-        a: a.into_iter().peekable(),
-        b: b.into_iter().peekable(),
-        fused: None,
-        cmp,
-    }
-}
-
-impl<I, J, F> Clone for MergeBy<I, J, F>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>,
-          Peekable<I>: Clone,
-          Peekable<J>: Clone,
-          F: Clone
-{
-    clone_fields!(a, b, fused, cmp);
-}
-
-impl<I, J, F> Iterator for MergeBy<I, J, F>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>,
-          F: MergePredicate<I::Item>
-{
-    type Item = I::Item;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let less_than = match self.fused {
-            Some(lt) => lt,
-            None => match (self.a.peek(), self.b.peek()) {
-                (Some(a), Some(b)) => self.cmp.merge_pred(a, b),
-                (Some(_), None) => {
-                    self.fused = Some(true);
-                    true
-                }
-                (None, Some(_)) => {
-                    self.fused = Some(false);
-                    false
-                }
-                (None, None) => return None,
-            }
-        };
-        if less_than {
-            self.a.next()
-        } else {
-            self.b.next()
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        // Not ExactSizeIterator because size may be larger than usize
-        size_hint::add(self.a.size_hint(), self.b.size_hint())
-    }
-}
-
-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`.
 ///
@@ -613,21 +516,24 @@
 }
 
 impl<'a, I, F> fmt::Debug for TakeWhileRef<'a, I, F>
-    where I: Iterator + fmt::Debug,
+where
+    I: Iterator + fmt::Debug,
 {
     debug_fmt_fields!(TakeWhileRef, iter);
 }
 
 /// Create a new `TakeWhileRef` from a reference to clonable iterator.
 pub fn take_while_ref<I, F>(iter: &mut I, f: F) -> TakeWhileRef<I, F>
-    where I: Iterator + Clone
+where
+    I: Iterator + Clone,
 {
     TakeWhileRef { iter, f }
 }
 
 impl<'a, I, F> Iterator for TakeWhileRef<'a, I, F>
-    where I: Iterator + Clone,
-          F: FnMut(&I::Item) -> bool
+where
+    I: Iterator + Clone,
+    F: FnMut(&I::Item) -> bool,
 {
     type Item = I::Item;
 
@@ -667,7 +573,8 @@
 }
 
 impl<I, A> Iterator for WhileSome<I>
-    where I: Iterator<Item = Option<A>>
+where
+    I: Iterator<Item = Option<A>>,
 {
     type Item = A;
 
@@ -681,6 +588,22 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         (0, self.iter.size_hint().1)
     }
+
+    fn fold<B, F>(mut self, acc: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let res = self.iter.try_fold(acc, |acc, item| match item {
+            Some(item) => Ok(f(acc, item)),
+            None => Err(acc),
+        });
+
+        match res {
+            Ok(val) => val,
+            Err(val) => val,
+        }
+    }
 }
 
 /// An iterator to iterate through all combinations in a `Clone`-able iterator that produces tuples
@@ -689,10 +612,11 @@
 /// See [`.tuple_combinations()`](crate::Itertools::tuple_combinations) for more
 /// information.
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "this iterator adaptor is not lazy but does nearly nothing unless consumed"]
 pub struct TupleCombinations<I, T>
-    where I: Iterator,
-          T: HasCombination<I>
+where
+    I: Iterator,
+    T: HasCombination<I>,
 {
     iter: T::Combination,
     _mi: PhantomData<I>,
@@ -704,9 +628,10 @@
 
 /// Create a new `TupleCombinations` from a clonable iterator.
 pub fn tuple_combinations<T, I>(iter: I) -> TupleCombinations<I, T>
-    where I: Iterator + Clone,
-          I::Item: Clone,
-          T: HasCombination<I>,
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+    T: HasCombination<I>,
 {
     TupleCombinations {
         iter: T::Combination::from(iter),
@@ -715,20 +640,38 @@
 }
 
 impl<I, T> Iterator for TupleCombinations<I, T>
-    where I: Iterator,
-          T: HasCombination<I>,
+where
+    I: Iterator,
+    T: HasCombination<I>,
 {
     type Item = T;
 
     fn next(&mut self) -> Option<Self::Item> {
         self.iter.next()
     }
+
+    fn size_hint(&self) -> SizeHint {
+        self.iter.size_hint()
+    }
+
+    fn count(self) -> usize {
+        self.iter.count()
+    }
+
+    fn fold<B, F>(self, init: B, f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        self.iter.fold(init, f)
+    }
 }
 
 impl<I, T> FusedIterator for TupleCombinations<I, T>
-    where I: FusedIterator,
-          T: HasCombination<I>,
-{}
+where
+    I: FusedIterator,
+    T: HasCombination<I>,
+{
+}
 
 #[derive(Clone, Debug)]
 pub struct Tuple1Combination<I> {
@@ -737,7 +680,7 @@
 
 impl<I> From<I> for Tuple1Combination<I> {
     fn from(iter: I) -> Self {
-        Tuple1Combination { iter }
+        Self { iter }
     }
 }
 
@@ -747,6 +690,21 @@
     fn next(&mut self) -> Option<Self::Item> {
         self.iter.next().map(|x| (x,))
     }
+
+    fn size_hint(&self) -> SizeHint {
+        self.iter.size_hint()
+    }
+
+    fn count(self) -> usize {
+        self.iter.count()
+    }
+
+    fn fold<B, F>(self, init: B, f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        self.iter.map(|x| (x,)).fold(init, f)
+    }
 }
 
 impl<I: Iterator> HasCombination<I> for (I::Item,) {
@@ -780,22 +738,55 @@
 
         impl<I, A> Iterator for $C<I>
             where I: Iterator<Item = A> + Clone,
-                  I::Item: Clone
+                  A: Clone,
         {
             type Item = (A, $(ignore_ident!($X, A)),*);
 
             fn next(&mut self) -> Option<Self::Item> {
-                if let Some(($($X),*,)) = self.c.next() {
+                if let Some(($($X,)*)) = self.c.next() {
                     let z = self.item.clone().unwrap();
                     Some((z, $($X),*))
                 } else {
                     self.item = self.iter.next();
                     self.item.clone().and_then(|z| {
                         self.c = self.iter.clone().into();
-                        self.c.next().map(|($($X),*,)| (z, $($X),*))
+                        self.c.next().map(|($($X,)*)| (z, $($X),*))
                     })
                 }
             }
+
+            fn size_hint(&self) -> SizeHint {
+                const K: usize = 1 + count_ident!($($X)*);
+                let (mut n_min, mut n_max) = self.iter.size_hint();
+                n_min = checked_binomial(n_min, K).unwrap_or(usize::MAX);
+                n_max = n_max.and_then(|n| checked_binomial(n, K));
+                size_hint::add(self.c.size_hint(), (n_min, n_max))
+            }
+
+            fn count(self) -> usize {
+                const K: usize = 1 + count_ident!($($X)*);
+                let n = self.iter.count();
+                checked_binomial(n, K).unwrap() + self.c.count()
+            }
+
+            fn fold<B, F>(self, mut init: B, mut f: F) -> B
+            where
+                F: FnMut(B, Self::Item) -> B,
+            {
+                let Self { c, item, mut iter } = self;
+                if let Some(z) = item.as_ref() {
+                    init = c
+                        .map(|($($X,)*)| (z.clone(), $($X),*))
+                        .fold(init, &mut f);
+                }
+                while let Some(z) = iter.next() {
+                    let c: $P<I> = iter.clone().into();
+                    init = c
+                        .map(|($($X,)*)| (z.clone(), $($X),*))
+                        .fold(init, &mut f);
+                }
+                init
+            }
         }
 
         impl<I, A> HasCombination<I> for (A, $(ignore_ident!($X, A)),*)
@@ -831,6 +822,42 @@
 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);
 
+// https://en.wikipedia.org/wiki/Binomial_coefficient#In_programming_languages
+pub(crate) fn checked_binomial(mut n: usize, mut k: usize) -> Option<usize> {
+    if n < k {
+        return Some(0);
+    }
+    // `factorial(n) / factorial(n - k) / factorial(k)` but trying to avoid it overflows:
+    k = (n - k).min(k); // symmetry
+    let mut c = 1;
+    for i in 1..=k {
+        c = (c / i)
+            .checked_mul(n)?
+            .checked_add((c % i).checked_mul(n)? / i)?;
+        n -= 1;
+    }
+    Some(c)
+}
+
+#[test]
+fn test_checked_binomial() {
+    // With the first row: [1, 0, 0, ...] and the first column full of 1s, we check
+    // row by row the recurrence relation of binomials (which is an equivalent definition).
+    // For n >= 1 and k >= 1 we have:
+    //   binomial(n, k) == binomial(n - 1, k - 1) + binomial(n - 1, k)
+    const LIMIT: usize = 500;
+    let mut row = vec![Some(0); LIMIT + 1];
+    row[0] = Some(1);
+    for n in 0..=LIMIT {
+        for k in 0..=LIMIT {
+            assert_eq!(row[k], checked_binomial(n, k));
+        }
+        row = std::iter::once(Some(1))
+            .chain((1..=LIMIT).map(|k| row[k - 1]?.checked_add(row[k]?)))
+            .collect();
+    }
+}
+
 /// An iterator adapter to filter values within a nested `Result::Ok`.
 ///
 /// See [`.filter_ok()`](crate::Itertools::filter_ok) for more information.
@@ -838,7 +865,7 @@
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct FilterOk<I, F> {
     iter: I,
-    f: F
+    f: F,
 }
 
 impl<I, F> fmt::Debug for FilterOk<I, F>
@@ -850,33 +877,26 @@
 
 /// 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>>,
-          F: FnMut(&T) -> bool,
+where
+    I: Iterator<Item = Result<T, E>>,
+    F: FnMut(&T) -> bool,
 {
-    FilterOk {
-        iter,
-        f,
-    }
+    FilterOk { iter, f }
 }
 
 impl<I, F, T, E> Iterator for FilterOk<I, F>
-    where I: Iterator<Item = Result<T, E>>,
-          F: FnMut(&T) -> bool,
+where
+    I: Iterator<Item = Result<T, E>>,
+    F: FnMut(&T) -> bool,
 {
     type Item = Result<T, E>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            match self.iter.next() {
-                Some(Ok(v)) => {
-                    if (self.f)(&v) {
-                        return Some(Ok(v));
-                    }
-                },
-                Some(Err(e)) => return Some(Err(e)),
-                None => return None,
-            }
-        }
+        let f = &mut self.f;
+        self.iter.find(|res| match res {
+            Ok(t) => f(t),
+            _ => true,
+        })
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
@@ -884,36 +904,41 @@
     }
 
     fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
-        where Fold: FnMut(Acc, Self::Item) -> Acc,
+    where
+        Fold: FnMut(Acc, Self::Item) -> Acc,
     {
         let mut f = self.f;
-        self.iter.filter(|v| {
-            v.as_ref().map(&mut f).unwrap_or(true)
-        }).fold(init, fold_f)
+        self.iter
+            .filter(|v| v.as_ref().map(&mut f).unwrap_or(true))
+            .fold(init, fold_f)
     }
 
     fn collect<C>(self) -> C
-        where C: FromIterator<Self::Item>
+    where
+        C: FromIterator<Self::Item>,
     {
         let mut f = self.f;
-        self.iter.filter(|v| {
-            v.as_ref().map(&mut f).unwrap_or(true)
-        }).collect()
+        self.iter
+            .filter(|v| v.as_ref().map(&mut f).unwrap_or(true))
+            .collect()
     }
 }
 
 impl<I, F, T, E> FusedIterator for FilterOk<I, F>
-    where I: FusedIterator<Item = Result<T, E>>,
-          F: FnMut(&T) -> bool,
-{}
+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()`](crate::Itertools::filter_map_ok) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Clone)]
 pub struct FilterMapOk<I, F> {
     iter: I,
-    f: F
+    f: F,
 }
 
 impl<I, F> fmt::Debug for FilterMapOk<I, F>
@@ -933,33 +958,26 @@
 
 /// Create a new `FilterOk` iterator.
 pub fn filter_map_ok<I, F, T, U, E>(iter: I, f: F) -> FilterMapOk<I, F>
-    where I: Iterator<Item = Result<T, E>>,
-          F: FnMut(T) -> Option<U>,
+where
+    I: Iterator<Item = Result<T, E>>,
+    F: FnMut(T) -> Option<U>,
 {
-    FilterMapOk {
-        iter,
-        f,
-    }
+    FilterMapOk { iter, f }
 }
 
 impl<I, F, T, U, E> Iterator for FilterMapOk<I, F>
-    where I: Iterator<Item = Result<T, E>>,
-          F: FnMut(T) -> Option<U>,
+where
+    I: Iterator<Item = Result<T, E>>,
+    F: FnMut(T) -> Option<U>,
 {
     type Item = Result<U, E>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            match self.iter.next() {
-                Some(Ok(v)) => {
-                    if let Some(v) = (self.f)(v) {
-                        return Some(Ok(v));
-                    }
-                },
-                Some(Err(e)) => return Some(Err(e)),
-                None => return None,
-            }
-        }
+        let f = &mut self.f;
+        self.iter.find_map(|res| match res {
+            Ok(t) => f(t).map(Ok),
+            Err(e) => Some(Err(e)),
+        })
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
@@ -967,28 +985,32 @@
     }
 
     fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
-        where Fold: FnMut(Acc, Self::Item) -> Acc,
+    where
+        Fold: FnMut(Acc, Self::Item) -> Acc,
     {
         let mut f = self.f;
-        self.iter.filter_map(|v| {
-            transpose_result(v.map(&mut f))
-        }).fold(init, fold_f)
+        self.iter
+            .filter_map(|v| transpose_result(v.map(&mut f)))
+            .fold(init, fold_f)
     }
 
     fn collect<C>(self) -> C
-        where C: FromIterator<Self::Item>
+    where
+        C: FromIterator<Self::Item>,
     {
         let mut f = self.f;
-        self.iter.filter_map(|v| {
-            transpose_result(v.map(&mut f))
-        }).collect()
+        self.iter
+            .filter_map(|v| transpose_result(v.map(&mut f)))
+            .collect()
     }
 }
 
 impl<I, F, T, U, E> FusedIterator for FilterMapOk<I, F>
-    where I: FusedIterator<Item = Result<T, E>>,
-          F: FnMut(T) -> Option<U>,
-{}
+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.
 ///
@@ -996,70 +1018,93 @@
 #[derive(Clone)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Positions<I, F> {
-    iter: I,
+    iter: Enumerate<I>,
     f: F,
-    count: usize,
 }
 
 impl<I, F> fmt::Debug for Positions<I, F>
 where
     I: fmt::Debug,
 {
-    debug_fmt_fields!(Positions, iter, count);
+    debug_fmt_fields!(Positions, iter);
 }
 
 /// Create a new `Positions` iterator.
 pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F>
-    where I: Iterator,
-          F: FnMut(I::Item) -> bool,
+where
+    I: Iterator,
+    F: FnMut(I::Item) -> bool,
 {
-    Positions {
-        iter,
-        f,
-        count: 0
-    }
+    let iter = iter.enumerate();
+    Positions { iter, f }
 }
 
 impl<I, F> Iterator for Positions<I, F>
-    where I: Iterator,
-          F: FnMut(I::Item) -> bool,
+where
+    I: Iterator,
+    F: FnMut(I::Item) -> bool,
 {
     type Item = usize;
 
     fn next(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.next() {
-            let i = self.count;
-            self.count = i + 1;
-            if (self.f)(v) {
-                return Some(i);
-            }
-        }
-        None
+        let f = &mut self.f;
+        // TODO: once MSRV >= 1.62, use `then_some`.
+        self.iter
+            .find_map(|(count, val)| if f(val) { Some(count) } else { None })
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
         (0, self.iter.size_hint().1)
     }
+
+    fn fold<B, G>(self, init: B, mut func: G) -> B
+    where
+        G: FnMut(B, Self::Item) -> B,
+    {
+        let mut f = self.f;
+        self.iter.fold(init, |mut acc, (count, val)| {
+            if f(val) {
+                acc = func(acc, count);
+            }
+            acc
+        })
+    }
 }
 
 impl<I, F> DoubleEndedIterator for Positions<I, F>
-    where I: DoubleEndedIterator + ExactSizeIterator,
-          F: FnMut(I::Item) -> bool,
+where
+    I: DoubleEndedIterator + ExactSizeIterator,
+    F: FnMut(I::Item) -> bool,
 {
     fn next_back(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.next_back() {
-            if (self.f)(v) {
-                return Some(self.count + self.iter.len())
+        let f = &mut self.f;
+        // TODO: once MSRV >= 1.62, use `then_some`.
+        self.iter
+            .by_ref()
+            .rev()
+            .find_map(|(count, val)| if f(val) { Some(count) } else { None })
+    }
+
+    fn rfold<B, G>(self, init: B, mut func: G) -> B
+    where
+        G: FnMut(B, Self::Item) -> B,
+    {
+        let mut f = self.f;
+        self.iter.rfold(init, |mut acc, (count, val)| {
+            if f(val) {
+                acc = func(acc, count);
             }
-        }
-        None
+            acc
+        })
     }
 }
 
 impl<I, F> FusedIterator for Positions<I, F>
-    where I: FusedIterator,
-          F: FnMut(I::Item) -> bool,
-{}
+where
+    I: FusedIterator,
+    F: FnMut(I::Item) -> bool,
+{
+}
 
 /// An iterator adapter to apply a mutating function to each element before yielding it.
 ///
@@ -1108,18 +1153,28 @@
     }
 
     fn fold<Acc, G>(self, init: Acc, mut g: G) -> Acc
-        where G: FnMut(Acc, Self::Item) -> Acc,
+    where
+        G: FnMut(Acc, Self::Item) -> Acc,
     {
         let mut f = self.f;
-        self.iter.fold(init, move |acc, mut v| { f(&mut v); g(acc, v) })
+        self.iter.fold(init, move |acc, mut v| {
+            f(&mut v);
+            g(acc, v)
+        })
     }
 
     // if possible, re-use inner iterator specializations in collect
     fn collect<C>(self) -> C
-        where C: FromIterator<Self::Item>
+    where
+        C: FromIterator<Self::Item>,
     {
         let mut f = self.f;
-        self.iter.map(move |mut v| { f(&mut v); v }).collect()
+        self.iter
+            .map(move |mut v| {
+                f(&mut v);
+                v
+            })
+            .collect()
     }
 }
 
@@ -1127,7 +1182,8 @@
 where
     I: ExactSizeIterator,
     F: FnMut(&mut I::Item),
-{}
+{
+}
 
 impl<I, F> DoubleEndedIterator for Update<I, F>
 where
@@ -1148,4 +1204,5 @@
 where
     I: FusedIterator,
     F: FnMut(&mut I::Item),
-{}
+{
+}
diff --git a/crates/itertools/src/adaptors/multi_product.rs b/crates/itertools/src/adaptors/multi_product.rs
index 0b38406..314d4a4 100644
--- a/crates/itertools/src/adaptors/multi_product.rs
+++ b/crates/itertools/src/adaptors/multi_product.rs
@@ -1,29 +1,52 @@
 #![cfg(feature = "use_alloc")]
-
-use crate::size_hint;
-use crate::Itertools;
+use Option::{self as State, None as ProductEnded, Some as ProductInProgress};
+use Option::{self as CurrentItems, None as NotYetPopulated, Some as Populated};
 
 use alloc::vec::Vec;
 
+use crate::size_hint;
+
 #[derive(Clone)]
 /// An iterator adaptor that iterates over the cartesian product of
 /// multiple iterators of type `I`.
 ///
-/// An iterator element type is `Vec<I>`.
+/// An iterator element type is `Vec<I::Item>`.
 ///
 /// 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;
+pub struct MultiProduct<I>(State<MultiProductInner<I>>)
+where
+    I: Iterator + Clone,
+    I::Item: Clone;
+
+#[derive(Clone)]
+/// Internals for `MultiProduct`.
+struct MultiProductInner<I>
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+{
+    /// Holds the iterators.
+    iters: Vec<MultiProductIter<I>>,
+    /// Not populated at the beginning then it holds the current item of each iterator.
+    cur: CurrentItems<Vec<I::Item>>,
+}
 
 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);
+    debug_fmt_fields!(MultiProduct, 0);
+}
+
+impl<I> std::fmt::Debug for MultiProductInner<I>
+where
+    I: Iterator + Clone + std::fmt::Debug,
+    I::Item: Clone + std::fmt::Debug,
+{
+    debug_fmt_fields!(MultiProductInner, iters, cur);
 }
 
 /// Create a new cartesian product iterator over an arbitrary number
@@ -31,200 +54,178 @@
 ///
 /// Iterator element is of type `Vec<H::Item::Item>`.
 pub fn multi_cartesian_product<H>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
-    where H: Iterator,
-          H::Item: IntoIterator,
-          <H::Item as IntoIterator>::IntoIter: Clone,
-          <H::Item as IntoIterator>::Item: Clone
+where
+    H: Iterator,
+    H::Item: IntoIterator,
+    <H::Item as IntoIterator>::IntoIter: Clone,
+    <H::Item as IntoIterator>::Item: Clone,
 {
-    MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect())
+    let inner = MultiProductInner {
+        iters: iters
+            .map(|i| MultiProductIter::new(i.into_iter()))
+            .collect(),
+        cur: NotYetPopulated,
+    };
+    MultiProduct(ProductInProgress(inner))
 }
 
 #[derive(Clone, Debug)]
 /// Holds the state of a single iterator within a `MultiProduct`.
 struct MultiProductIter<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
 {
-    cur: Option<I::Item>,
     iter: I,
     iter_orig: I,
 }
 
-/// Holds the current state during an iteration of a `MultiProduct`.
-#[derive(Debug)]
-enum MultiProductIterState {
-    StartOfIter,
-    MidIter { on_first_iter: bool },
-}
-
-impl<I> MultiProduct<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
-{
-    /// Iterates the rightmost iterator, then recursively iterates iterators
-    /// to the left if necessary.
-    ///
-    /// Returns true if the iteration succeeded, else false.
-    fn iterate_last(
-        multi_iters: &mut [MultiProductIter<I>],
-        mut state: MultiProductIterState
-    ) -> bool {
-        use self::MultiProductIterState::*;
-
-        if let Some((last, rest)) = multi_iters.split_last_mut() {
-            let on_first_iter = match state {
-                StartOfIter => {
-                    let on_first_iter = !last.in_progress();
-                    state = MidIter { on_first_iter };
-                    on_first_iter
-                },
-                MidIter { on_first_iter } => on_first_iter
-            };
-
-            if !on_first_iter {
-                last.iterate();
-            }
-
-            if last.in_progress() {
-                true
-            } else if MultiProduct::iterate_last(rest, state) {
-                last.reset();
-                last.iterate();
-                // If iterator is None twice consecutively, then iterator is
-                // empty; whole product is empty.
-                last.in_progress()
-            } else {
-                false
-            }
-        } else {
-            // Reached end of iterator list. On initialisation, return true.
-            // At end of iteration (final iterator finishes), finish.
-            match state {
-                StartOfIter => false,
-                MidIter { on_first_iter } => on_first_iter
-            }
-        }
-    }
-
-    /// Returns the unwrapped value of the next iteration.
-    fn curr_iterator(&self) -> Vec<I::Item> {
-        self.0.iter().map(|multi_iter| {
-            multi_iter.cur.clone().unwrap()
-        }).collect()
-    }
-
-    /// Returns true if iteration has started and has not yet finished; false
-    /// otherwise.
-    fn in_progress(&self) -> bool {
-        if let Some(last) = self.0.last() {
-            last.in_progress()
-        } else {
-            false
-        }
-    }
-}
-
 impl<I> MultiProductIter<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
 {
     fn new(iter: I) -> Self {
-        MultiProductIter {
-            cur: None,
+        Self {
             iter: iter.clone(),
-            iter_orig: iter
+            iter_orig: iter,
         }
     }
-
-    /// Iterate the managed iterator.
-    fn iterate(&mut self) {
-        self.cur = self.iter.next();
-    }
-
-    /// Reset the managed iterator.
-    fn reset(&mut self) {
-        self.iter = self.iter_orig.clone();
-    }
-
-    /// Returns true if the current iterator has been started and has not yet
-    /// finished; false otherwise.
-    fn in_progress(&self) -> bool {
-        self.cur.is_some()
-    }
 }
 
 impl<I> Iterator for MultiProduct<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
 {
     type Item = Vec<I::Item>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        if MultiProduct::iterate_last(
-            &mut self.0,
-            MultiProductIterState::StartOfIter
-        ) {
-            Some(self.curr_iterator())
-        } else {
-            None
+        // This fuses the iterator.
+        let inner = self.0.as_mut()?;
+        match &mut inner.cur {
+            Populated(values) => {
+                debug_assert!(!inner.iters.is_empty());
+                // Find (from the right) a non-finished iterator and
+                // reset the finished ones encountered.
+                for (iter, item) in inner.iters.iter_mut().zip(values.iter_mut()).rev() {
+                    if let Some(new) = iter.iter.next() {
+                        *item = new;
+                        return Some(values.clone());
+                    } else {
+                        iter.iter = iter.iter_orig.clone();
+                        // `cur` is populated so the untouched `iter_orig` can not be empty.
+                        *item = iter.iter.next().unwrap();
+                    }
+                }
+                self.0 = ProductEnded;
+                None
+            }
+            // Only the first time.
+            NotYetPopulated => {
+                let next: Option<Vec<_>> = inner.iters.iter_mut().map(|i| i.iter.next()).collect();
+                if next.is_none() || inner.iters.is_empty() {
+                    // This cartesian product had at most one item to generate and now ends.
+                    self.0 = ProductEnded;
+                } else {
+                    inner.cur.clone_from(&next);
+                }
+                next
+            }
         }
     }
 
     fn count(self) -> usize {
-        if self.0.is_empty() {
-            return 0;
+        match self.0 {
+            ProductEnded => 0,
+            // The iterator is fresh so the count is the product of the length of each iterator:
+            // - If one of them is empty, stop counting.
+            // - Less `count()` calls than the general case.
+            ProductInProgress(MultiProductInner {
+                iters,
+                cur: NotYetPopulated,
+            }) => iters
+                .into_iter()
+                .map(|iter| iter.iter_orig.count())
+                .try_fold(1, |product, count| {
+                    if count == 0 {
+                        None
+                    } else {
+                        Some(product * count)
+                    }
+                })
+                .unwrap_or_default(),
+            // The general case.
+            ProductInProgress(MultiProductInner {
+                iters,
+                cur: Populated(_),
+            }) => iters.into_iter().fold(0, |mut acc, iter| {
+                if acc != 0 {
+                    acc *= iter.iter_orig.count();
+                }
+                acc + iter.iter.count()
+            }),
         }
-
-        if !self.in_progress() {
-            return self.0.into_iter().fold(1, |acc, multi_iter| {
-                acc * multi_iter.iter.count()
-            });
-        }
-
-        self.0.into_iter().fold(
-            0,
-            |acc, MultiProductIter { iter, iter_orig, cur: _ }| {
-                let total_count = iter_orig.count();
-                let cur_count = iter.count();
-                acc * total_count + cur_count
-            }
-        )
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
-        // Not ExactSizeIterator because size may be larger than usize
-        if self.0.is_empty() {
-            return (0, Some(0));
-        }
-
-        if !self.in_progress() {
-            return self.0.iter().fold((1, Some(1)), |acc, multi_iter| {
-                size_hint::mul(acc, multi_iter.iter.size_hint())
-            });
-        }
-
-        self.0.iter().fold(
-            (0, Some(0)),
-            |acc, &MultiProductIter { ref iter, ref iter_orig, cur: _ }| {
-                let cur_size = iter.size_hint();
-                let total_size = iter_orig.size_hint();
-                size_hint::add(size_hint::mul(acc, total_size), cur_size)
+        match &self.0 {
+            ProductEnded => (0, Some(0)),
+            ProductInProgress(MultiProductInner {
+                iters,
+                cur: NotYetPopulated,
+            }) => iters
+                .iter()
+                .map(|iter| iter.iter_orig.size_hint())
+                .fold((1, Some(1)), size_hint::mul),
+            ProductInProgress(MultiProductInner {
+                iters,
+                cur: Populated(_),
+            }) => {
+                if let [first, tail @ ..] = &iters[..] {
+                    tail.iter().fold(first.iter.size_hint(), |mut sh, iter| {
+                        sh = size_hint::mul(sh, iter.iter_orig.size_hint());
+                        size_hint::add(sh, iter.iter.size_hint())
+                    })
+                } else {
+                    // Since it is populated, this cartesian product has started so `iters` is not empty.
+                    unreachable!()
+                }
             }
-        )
+        }
     }
 
     fn last(self) -> Option<Self::Item> {
-        let iter_count = self.0.len();
-
-        let lasts: Self::Item = self.0.into_iter()
-            .map(|multi_iter| multi_iter.iter.last())
-            .while_some()
-            .collect();
-
-        if lasts.len() == iter_count {
-            Some(lasts)
+        let MultiProductInner { iters, cur } = self.0?;
+        // Collect the last item of each iterator of the product.
+        if let Populated(values) = cur {
+            let mut count = iters.len();
+            let last = iters
+                .into_iter()
+                .zip(values)
+                .map(|(i, value)| {
+                    i.iter.last().unwrap_or_else(|| {
+                        // The iterator is empty, use its current `value`.
+                        count -= 1;
+                        value
+                    })
+                })
+                .collect();
+            if count == 0 {
+                // `values` was the last item.
+                None
+            } else {
+                Some(last)
+            }
         } else {
-            None
+            iters.into_iter().map(|i| i.iter.last()).collect()
         }
     }
 }
+
+impl<I> std::iter::FusedIterator for MultiProduct<I>
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+{
+}
diff --git a/crates/itertools/src/combinations.rs b/crates/itertools/src/combinations.rs
index 68a59c5..6bb2f3e 100644
--- a/crates/itertools/src/combinations.rs
+++ b/crates/itertools/src/combinations.rs
@@ -4,6 +4,8 @@
 use super::lazy_buffer::LazyBuffer;
 use alloc::vec::Vec;
 
+use crate::adaptors::checked_binomial;
+
 /// An iterator to iterate through all the `k`-length combinations in an iterator.
 ///
 /// See [`.combinations()`](crate::Itertools::combinations) for more information.
@@ -15,29 +17,29 @@
 }
 
 impl<I> Clone for Combinations<I>
-    where I: Clone + Iterator,
-          I::Item: Clone,
+where
+    I: Clone + Iterator,
+    I::Item: Clone,
 {
     clone_fields!(indices, pool, first);
 }
 
 impl<I> fmt::Debug for Combinations<I>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
 {
     debug_fmt_fields!(Combinations, indices, pool, first);
 }
 
 /// Create a new `Combinations` from a clonable iterator.
 pub fn combinations<I>(iter: I, k: usize) -> Combinations<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
-    let mut pool = LazyBuffer::new(iter);
-    pool.prefill(k);
-
     Combinations {
         indices: (0..k).collect(),
-        pool,
+        pool: LazyBuffer::new(iter),
         first: true,
     }
 }
@@ -45,16 +47,22 @@
 impl<I: Iterator> Combinations<I> {
     /// Returns the length of a combination produced by this iterator.
     #[inline]
-    pub fn k(&self) -> usize { self.indices.len() }
+    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`](Combinations::next).
     #[inline]
-    pub fn n(&self) -> usize { self.pool.len() }
+    pub fn n(&self) -> usize {
+        self.pool.len()
+    }
 
-    /// Returns a reference to the source iterator.
+    /// Returns a reference to the source pool.
     #[inline]
-    pub(crate) fn src(&self) -> &I { &self.pool.it }
+    pub(crate) fn src(&self) -> &LazyBuffer<I> {
+        &self.pool
+    }
 
     /// Resets this `Combinations` back to an initial state for combinations of length
     /// `k` over the same pool data source. If `k` is larger than the current length
@@ -68,7 +76,6 @@
             for i in 0..k {
                 self.indices[i] = i;
             }
-
         } else {
             for i in 0..self.indices.len() {
                 self.indices[i] = i;
@@ -77,52 +84,160 @@
             self.pool.prefill(k);
         }
     }
-}
 
-impl<I> Iterator for Combinations<I>
-    where I: Iterator,
-          I::Item: Clone
-{
-    type Item = Vec<I::Item>;
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.first {
-            if self.k() > self.n() {
-                return None;
-            }
+    pub(crate) fn n_and_count(self) -> (usize, usize) {
+        let Self {
+            indices,
+            pool,
+            first,
+        } = self;
+        let n = pool.count();
+        (n, remaining_for(n, first, &indices).unwrap())
+    }
+
+    /// Initialises the iterator by filling a buffer with elements from the
+    /// iterator. Returns true if there are no combinations, false otherwise.
+    fn init(&mut self) -> bool {
+        self.pool.prefill(self.k());
+        let done = self.k() > self.n();
+        if !done {
             self.first = false;
-        } else if self.indices.is_empty() {
-            return None;
-        } else {
-            // Scan from the end, looking for an index to increment
-            let mut i: usize = self.indices.len() - 1;
+        }
 
-            // Check if we need to consume more from the iterator
-            if self.indices[i] == self.pool.len() - 1 {
-                self.pool.get_next(); // may change pool size
-            }
+        done
+    }
 
-            while self.indices[i] == i + self.pool.len() - self.indices.len() {
-                if i > 0 {
-                    i -= 1;
-                } else {
-                    // Reached the last combination
-                    return None;
-                }
-            }
+    /// Increments indices representing the combination to advance to the next
+    /// (in lexicographic order by increasing sequence) combination. For example
+    /// if we have n=4 & k=2 then `[0, 1] -> [0, 2] -> [0, 3] -> [1, 2] -> ...`
+    ///
+    /// Returns true if we've run out of combinations, false otherwise.
+    fn increment_indices(&mut self) -> bool {
+        if self.indices.is_empty() {
+            return true; // Done
+        }
 
-            // Increment index, and reset the ones to its right
-            self.indices[i] += 1;
-            for j in i+1..self.indices.len() {
-                self.indices[j] = self.indices[j - 1] + 1;
+        // Scan from the end, looking for an index to increment
+        let mut i: usize = self.indices.len() - 1;
+
+        // Check if we need to consume more from the iterator
+        if self.indices[i] == self.pool.len() - 1 {
+            self.pool.get_next(); // may change pool size
+        }
+
+        while self.indices[i] == i + self.pool.len() - self.indices.len() {
+            if i > 0 {
+                i -= 1;
+            } else {
+                // Reached the last combination
+                return true;
             }
         }
 
-        // Create result vector based on the indices
-        Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect())
+        // Increment index, and reset the ones to its right
+        self.indices[i] += 1;
+        for j in i + 1..self.indices.len() {
+            self.indices[j] = self.indices[j - 1] + 1;
+        }
+
+        // If we've made it this far, we haven't run out of combos
+        false
+    }
+
+    /// Returns the n-th item or the number of successful steps.
+    pub(crate) fn try_nth(&mut self, n: usize) -> Result<<Self as Iterator>::Item, usize>
+    where
+        I::Item: Clone,
+    {
+        let done = if self.first {
+            self.init()
+        } else {
+            self.increment_indices()
+        };
+        if done {
+            return Err(0);
+        }
+        for i in 0..n {
+            if self.increment_indices() {
+                return Err(i + 1);
+            }
+        }
+        Ok(self.pool.get_at(&self.indices))
+    }
+}
+
+impl<I> Iterator for Combinations<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    type Item = Vec<I::Item>;
+    fn next(&mut self) -> Option<Self::Item> {
+        let done = if self.first {
+            self.init()
+        } else {
+            self.increment_indices()
+        };
+
+        if done {
+            return None;
+        }
+
+        Some(self.pool.get_at(&self.indices))
+    }
+
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        self.try_nth(n).ok()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (mut low, mut upp) = self.pool.size_hint();
+        low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX);
+        upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices));
+        (low, upp)
+    }
+
+    #[inline]
+    fn count(self) -> usize {
+        self.n_and_count().1
     }
 }
 
 impl<I> FusedIterator for Combinations<I>
-    where I: Iterator,
-          I::Item: Clone
-{}
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+}
+
+/// For a given size `n`, return the count of remaining combinations or None if it would overflow.
+fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
+    let k = indices.len();
+    if n < k {
+        Some(0)
+    } else if first {
+        checked_binomial(n, k)
+    } else {
+        // https://en.wikipedia.org/wiki/Combinatorial_number_system
+        // http://www.site.uottawa.ca/~lucia/courses/5165-09/GenCombObj.pdf
+
+        // The combinations generated after the current one can be counted by counting as follows:
+        // - The subsequent combinations that differ in indices[0]:
+        //   If subsequent combinations differ in indices[0], then their value for indices[0]
+        //   must be at least 1 greater than the current indices[0].
+        //   As indices is strictly monotonically sorted, this means we can effectively choose k values
+        //   from (n - 1 - indices[0]), leading to binomial(n - 1 - indices[0], k) possibilities.
+        // - The subsequent combinations with same indices[0], but differing indices[1]:
+        //   Here we can choose k - 1 values from (n - 1 - indices[1]) values,
+        //   leading to binomial(n - 1 - indices[1], k - 1) possibilities.
+        // - (...)
+        // - The subsequent combinations with same indices[0..=i], but differing indices[i]:
+        //   Here we can choose k - i values from (n - 1 - indices[i]) values: binomial(n - 1 - indices[i], k - i).
+        //   Since subsequent combinations can in any index, we must sum up the aforementioned binomial coefficients.
+
+        // Below, `n0` resembles indices[i].
+        indices.iter().enumerate().try_fold(0usize, |sum, (i, n0)| {
+            sum.checked_add(checked_binomial(n - 1 - *n0, k - i)?)
+        })
+    }
+}
diff --git a/crates/itertools/src/combinations_with_replacement.rs b/crates/itertools/src/combinations_with_replacement.rs
index 0fec967..f363f9b 100644
--- a/crates/itertools/src/combinations_with_replacement.rs
+++ b/crates/itertools/src/combinations_with_replacement.rs
@@ -1,20 +1,23 @@
+use alloc::boxed::Box;
 use alloc::vec::Vec;
 use std::fmt;
 use std::iter::FusedIterator;
 
 use super::lazy_buffer::LazyBuffer;
+use crate::adaptors::checked_binomial;
 
 /// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
 ///
 /// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
 /// for more information.
 #[derive(Clone)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct CombinationsWithReplacement<I>
 where
     I: Iterator,
     I::Item: Clone,
 {
-    indices: Vec<usize>,
+    indices: Box<[usize]>,
     pool: LazyBuffer<I>,
     first: bool,
 }
@@ -24,18 +27,7 @@
     I: Iterator + fmt::Debug,
     I::Item: fmt::Debug + Clone,
 {
-    debug_fmt_fields!(Combinations, indices, pool, first);
-}
-
-impl<I> CombinationsWithReplacement<I>
-where
-    I: Iterator,
-    I::Item: Clone,
-{
-    /// Map the current mask over the pool to get an output combination
-    fn current(&self) -> Vec<I::Item> {
-        self.indices.iter().map(|i| self.pool[*i].clone()).collect()
-    }
+    debug_fmt_fields!(CombinationsWithReplacement, indices, pool, first);
 }
 
 /// Create a new `CombinationsWithReplacement` from a clonable iterator.
@@ -44,7 +36,7 @@
     I: Iterator,
     I::Item: Clone,
 {
-    let indices: Vec<usize> = alloc::vec![0; k];
+    let indices = alloc::vec![0; k].into_boxed_slice();
     let pool: LazyBuffer<I> = LazyBuffer::new(iter);
 
     CombinationsWithReplacement {
@@ -54,51 +46,99 @@
     }
 }
 
+impl<I> CombinationsWithReplacement<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    /// Increments indices representing the combination to advance to the next
+    /// (in lexicographic order by increasing sequence) combination.
+    ///
+    /// Returns true if we've run out of combinations, false otherwise.
+    fn increment_indices(&mut self) -> bool {
+        // Check if we need to consume more from the iterator
+        // This will run while we increment our first index digit
+        self.pool.get_next();
+
+        // Work out where we need to update our indices
+        let mut increment = None;
+        for (i, indices_int) in self.indices.iter().enumerate().rev() {
+            if *indices_int < self.pool.len() - 1 {
+                increment = Some((i, indices_int + 1));
+                break;
+            }
+        }
+        match increment {
+            // If we can update the indices further
+            Some((increment_from, increment_value)) => {
+                // We need to update the rightmost non-max value
+                // and all those to the right
+                for i in &mut self.indices[increment_from..] {
+                    *i = increment_value;
+                }
+                // TODO: once MSRV >= 1.50, use `fill` instead:
+                // self.indices[increment_from..].fill(increment_value);
+                false
+            }
+            // Otherwise, we're done
+            None => true,
+        }
+    }
+}
+
 impl<I> Iterator for CombinationsWithReplacement<I>
 where
     I: Iterator,
     I::Item: Clone,
 {
     type Item = Vec<I::Item>;
+
     fn next(&mut self) -> Option<Self::Item> {
-        // If this is the first iteration, return early
         if self.first {
             // In empty edge cases, stop iterating immediately
-            return if !(self.indices.is_empty() || self.pool.get_next()) {
-                None
-            // Otherwise, yield the initial state
-            } else {
-                self.first = false;
-                Some(self.current())
-            };
+            if !(self.indices.is_empty() || self.pool.get_next()) {
+                return None;
+            }
+            self.first = false;
+        } else if self.increment_indices() {
+            return None;
         }
+        Some(self.pool.get_at(&self.indices))
+    }
 
-        // Check if we need to consume more from the iterator
-        // This will run while we increment our first index digit
-        self.pool.get_next();
-
-        // Work out where we need to update our indices
-        let mut increment: Option<(usize, usize)> = None;
-        for (i, indices_int) in self.indices.iter().enumerate().rev() {
-            if *indices_int < self.pool.len()-1 {
-                increment = Some((i, indices_int + 1));
-                break;
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        if self.first {
+            // In empty edge cases, stop iterating immediately
+            if !(self.indices.is_empty() || self.pool.get_next()) {
+                return None;
+            }
+            self.first = false;
+        } else if self.increment_indices() {
+            return None;
+        }
+        for _ in 0..n {
+            if self.increment_indices() {
+                return None;
             }
         }
+        Some(self.pool.get_at(&self.indices))
+    }
 
-        match increment {
-            // If we can update the indices further
-            Some((increment_from, increment_value)) => {
-                // We need to update the rightmost non-max value
-                // and all those to the right
-                for indices_index in increment_from..self.indices.len() {
-                    self.indices[indices_index] = increment_value;
-                }
-                Some(self.current())
-            }
-            // Otherwise, we're done
-            None => None,
-        }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (mut low, mut upp) = self.pool.size_hint();
+        low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX);
+        upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices));
+        (low, upp)
+    }
+
+    fn count(self) -> usize {
+        let Self {
+            indices,
+            pool,
+            first,
+        } = self;
+        let n = pool.count();
+        remaining_for(n, first, &indices).unwrap()
     }
 }
 
@@ -106,4 +146,47 @@
 where
     I: Iterator,
     I::Item: Clone,
-{}
+{
+}
+
+/// For a given size `n`, return the count of remaining combinations with replacement or None if it would overflow.
+fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
+    // With a "stars and bars" representation, choose k values with replacement from n values is
+    // like choosing k out of k + n − 1 positions (hence binomial(k + n - 1, k) possibilities)
+    // to place k stars and therefore n - 1 bars.
+    // Example (n=4, k=6): ***|*||** represents [0,0,0,1,3,3].
+    let count = |n: usize, k: usize| {
+        let positions = if n == 0 {
+            k.saturating_sub(1)
+        } else {
+            (n - 1).checked_add(k)?
+        };
+        checked_binomial(positions, k)
+    };
+    let k = indices.len();
+    if first {
+        count(n, k)
+    } else {
+        // The algorithm is similar to the one for combinations *without replacement*,
+        // except we choose values *with replacement* and indices are *non-strictly* monotonically sorted.
+
+        // The combinations generated after the current one can be counted by counting as follows:
+        // - The subsequent combinations that differ in indices[0]:
+        //   If subsequent combinations differ in indices[0], then their value for indices[0]
+        //   must be at least 1 greater than the current indices[0].
+        //   As indices is monotonically sorted, this means we can effectively choose k values with
+        //   replacement from (n - 1 - indices[0]), leading to count(n - 1 - indices[0], k) possibilities.
+        // - The subsequent combinations with same indices[0], but differing indices[1]:
+        //   Here we can choose k - 1 values with replacement from (n - 1 - indices[1]) values,
+        //   leading to count(n - 1 - indices[1], k - 1) possibilities.
+        // - (...)
+        // - The subsequent combinations with same indices[0..=i], but differing indices[i]:
+        //   Here we can choose k - i values with replacement from (n - 1 - indices[i]) values: count(n - 1 - indices[i], k - i).
+        //   Since subsequent combinations can in any index, we must sum up the aforementioned binomial coefficients.
+
+        // Below, `n0` resembles indices[i].
+        indices.iter().enumerate().try_fold(0usize, |sum, (i, n0)| {
+            sum.checked_add(count(n - 1 - *n0, k - i)?)
+        })
+    }
+}
diff --git a/crates/itertools/src/concat_impl.rs b/crates/itertools/src/concat_impl.rs
index f022ec9..ec7b91c 100644
--- a/crates/itertools/src/concat_impl.rs
+++ b/crates/itertools/src/concat_impl.rs
@@ -10,14 +10,21 @@
 ///
 /// ```rust
 /// use itertools::concat;
-/// 
+///
 /// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]];
 /// assert_eq!(concat(input), vec![1, 2, 3, 4, 5, 6]);
 /// ```
 pub fn concat<I>(iterable: I) -> I::Item
-    where I: IntoIterator,
-          I::Item: Extend<<<I as IntoIterator>::Item as IntoIterator>::Item> + IntoIterator + Default
+where
+    I: IntoIterator,
+    I::Item: Extend<<<I as IntoIterator>::Item as IntoIterator>::Item> + IntoIterator + Default,
 {
     #[allow(deprecated)] //TODO: once msrv hits 1.51. replace `fold1` with `reduce`
-    iterable.into_iter().fold1(|mut a, b| { a.extend(b); a }).unwrap_or_default()
+    iterable
+        .into_iter()
+        .fold1(|mut a, b| {
+            a.extend(b);
+            a
+        })
+        .unwrap_or_default()
 }
diff --git a/crates/itertools/src/cons_tuples_impl.rs b/crates/itertools/src/cons_tuples_impl.rs
index ae0f48f..9ab3094 100644
--- a/crates/itertools/src/cons_tuples_impl.rs
+++ b/crates/itertools/src/cons_tuples_impl.rs
@@ -1,4 +1,3 @@
-
 macro_rules! impl_cons_iter(
     ($_A:ident, $_B:ident, ) => (); // stop
 
@@ -22,16 +21,6 @@
                 self.iter.fold(accum, move |acc, (($($B,)*), x)| f(acc, ($($B,)* x, )))
             }
         }
-
-        #[allow(non_snake_case)]
-        impl<X, Iter, $($B),*> DoubleEndedIterator for ConsTuples<Iter, (($($B,)*), X)>
-            where Iter: DoubleEndedIterator<Item = (($($B,)*), X)>,
-        {
-            fn next_back(&mut self) -> Option<Self::Item> {
-                self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
-            }
-        }
-
     );
 );
 
@@ -44,13 +33,15 @@
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Debug)]
 pub struct ConsTuples<I, J>
-    where I: Iterator<Item=J>,
+where
+    I: Iterator<Item = J>,
 {
     iter: I,
 }
 
 impl<I, J> Clone for ConsTuples<I, J>
-    where I: Clone + Iterator<Item=J>,
+where
+    I: Clone + Iterator<Item = J>,
 {
     clone_fields!(iter);
 }
@@ -58,7 +49,10 @@
 /// Create an iterator that maps for example iterators of
 /// `((A, B), C)` to `(A, B, C)`.
 pub fn cons_tuples<I, J>(iterable: I) -> ConsTuples<I::IntoIter, J>
-    where I: IntoIterator<Item=J>
+where
+    I: IntoIterator<Item = J>,
 {
-    ConsTuples { iter: iterable.into_iter() }
+    ConsTuples {
+        iter: iterable.into_iter(),
+    }
 }
diff --git a/crates/itertools/src/diff.rs b/crates/itertools/src/diff.rs
index 1731f06..c6d9965 100644
--- a/crates/itertools/src/diff.rs
+++ b/crates/itertools/src/diff.rs
@@ -5,6 +5,8 @@
 //! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from
 //! a lock-step comparison.
 
+use std::fmt;
+
 use crate::free::put_back;
 use crate::structs::PutBack;
 
@@ -13,8 +15,9 @@
 /// `Diff` represents the way in which the elements yielded by the iterator `I` differ to some
 /// iterator `J`.
 pub enum Diff<I, J>
-    where I: Iterator,
-          J: Iterator
+where
+    I: Iterator,
+    J: Iterator,
 {
     /// The index of the first non-matching element along with both iterator's remaining elements
     /// starting with the first mis-match.
@@ -25,6 +28,43 @@
     Longer(usize, PutBack<J>),
 }
 
+impl<I, J> fmt::Debug for Diff<I, J>
+where
+    I: Iterator,
+    J: Iterator,
+    PutBack<I>: fmt::Debug,
+    PutBack<J>: fmt::Debug,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::FirstMismatch(idx, i, j) => f
+                .debug_tuple("FirstMismatch")
+                .field(idx)
+                .field(i)
+                .field(j)
+                .finish(),
+            Self::Shorter(idx, i) => f.debug_tuple("Shorter").field(idx).field(i).finish(),
+            Self::Longer(idx, j) => f.debug_tuple("Longer").field(idx).field(j).finish(),
+        }
+    }
+}
+
+impl<I, J> Clone for Diff<I, J>
+where
+    I: Iterator,
+    J: Iterator,
+    PutBack<I>: Clone,
+    PutBack<J>: Clone,
+{
+    fn clone(&self) -> Self {
+        match self {
+            Self::FirstMismatch(idx, i, j) => Self::FirstMismatch(*idx, i.clone(), j.clone()),
+            Self::Shorter(idx, i) => Self::Shorter(*idx, i.clone()),
+            Self::Longer(idx, j) => Self::Longer(*idx, j.clone()),
+        }
+    }
+}
+
 /// 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`.
 ///
@@ -37,11 +77,11 @@
 ///
 /// If `i` becomes exhausted before `j` becomes exhausted, the number of elements in `i` along with
 /// the remaining `j` elements will be returned as `Diff::Longer`.
-pub fn diff_with<I, J, F>(i: I, j: J, is_equal: F)
-    -> Option<Diff<I::IntoIter, J::IntoIter>>
-    where I: IntoIterator,
-          J: IntoIterator,
-          F: Fn(&I::Item, &J::Item) -> bool
+pub fn diff_with<I, J, F>(i: I, j: J, mut is_equal: F) -> Option<Diff<I::IntoIter, J::IntoIter>>
+where
+    I: IntoIterator,
+    J: IntoIterator,
+    F: FnMut(&I::Item, &J::Item) -> bool,
 {
     let mut i = i.into_iter();
     let mut j = j.into_iter();
@@ -49,13 +89,16 @@
     while let Some(i_elem) = i.next() {
         match j.next() {
             None => return Some(Diff::Shorter(idx, put_back(i).with_value(i_elem))),
-            Some(j_elem) => if !is_equal(&i_elem, &j_elem) {
-                let remaining_i = put_back(i).with_value(i_elem);
-                let remaining_j = put_back(j).with_value(j_elem);
-                return Some(Diff::FirstMismatch(idx, remaining_i, remaining_j));
-            },
+            Some(j_elem) => {
+                if !is_equal(&i_elem, &j_elem) {
+                    let remaining_i = put_back(i).with_value(i_elem);
+                    let remaining_j = put_back(j).with_value(j_elem);
+                    return Some(Diff::FirstMismatch(idx, remaining_i, remaining_j));
+                }
+            }
         }
         idx += 1;
     }
-    j.next().map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem)))
+    j.next()
+        .map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem)))
 }
diff --git a/crates/itertools/src/duplicates_impl.rs b/crates/itertools/src/duplicates_impl.rs
index 28eda44..a0db154 100644
--- a/crates/itertools/src/duplicates_impl.rs
+++ b/crates/itertools/src/duplicates_impl.rs
@@ -2,8 +2,8 @@
 
 mod private {
     use std::collections::HashMap;
-    use std::hash::Hash;
     use std::fmt;
+    use std::hash::Hash;
 
     #[derive(Clone)]
     #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
@@ -22,7 +22,7 @@
 
     impl<I: Iterator, Key: Eq + Hash, F> DuplicatesBy<I, Key, F> {
         pub(crate) fn new(iter: I, key_method: F) -> Self {
-            DuplicatesBy {
+            Self {
                 iter,
                 meta: Meta {
                     used: HashMap::new(),
@@ -77,7 +77,7 @@
         type Item = I::Item;
 
         fn next(&mut self) -> Option<Self::Item> {
-            let DuplicatesBy { iter, meta } = self;
+            let Self { iter, meta } = self;
             iter.find_map(|v| meta.filter(v))
         }
 
@@ -109,7 +109,7 @@
         F: KeyMethod<Key, I::Item>,
     {
         fn next_back(&mut self) -> Option<Self::Item> {
-            let DuplicatesBy { iter, meta } = self;
+            let Self { iter, meta } = self;
             iter.rev().find_map(|v| meta.filter(v))
         }
     }
@@ -122,7 +122,7 @@
     }
 
     /// Apply the identity function to elements before checking them for equality.
-    #[derive(Debug)]
+    #[derive(Debug, Clone)]
     pub struct ById;
     impl<V> KeyMethod<V, V> for ById {
         type Container = JustValue<V>;
@@ -133,6 +133,7 @@
     }
 
     /// Apply a user-supplied function to elements before checking them for equality.
+    #[derive(Clone)]
     pub struct ByFn<F>(pub(crate) F);
     impl<F> fmt::Debug for ByFn<F> {
         debug_fmt_fields!(ByFn,);
@@ -213,4 +214,3 @@
 {
     Duplicates::new(iter, private::ById)
 }
-
diff --git a/crates/itertools/src/either_or_both.rs b/crates/itertools/src/either_or_both.rs
index ef3985f..b7a7fc1 100644
--- a/crates/itertools/src/either_or_both.rs
+++ b/crates/itertools/src/either_or_both.rs
@@ -1,10 +1,12 @@
+use core::ops::{Deref, DerefMut};
+
 use crate::EitherOrBoth::*;
 
 use either::Either;
 
 /// Value that either holds a single A or B, or both.
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
-pub enum EitherOrBoth<A, B> {
+pub enum EitherOrBoth<A, B = A> {
     /// Both values are present.
     Both(A, B),
     /// Only the left value of type `A` is present.
@@ -14,7 +16,7 @@
 }
 
 impl<A, B> EitherOrBoth<A, B> {
-    /// If `Left`, or `Both`, return true, otherwise, return false.
+    /// If `Left`, or `Both`, return true. Otherwise, return false.
     pub fn has_left(&self) -> bool {
         self.as_ref().left().is_some()
     }
@@ -24,31 +26,24 @@
         self.as_ref().right().is_some()
     }
 
-    /// If Left, return true otherwise, return false.
+    /// If `Left`, return true. Otherwise, return false.
     /// Exclusive version of [`has_left`](EitherOrBoth::has_left).
     pub fn is_left(&self) -> bool {
-        match *self {
-            Left(_) => true,
-            _ => false,
-        }
+        matches!(self, Left(_))
     }
 
-    /// If Right, return true otherwise, return false.
+    /// If `Right`, return true. Otherwise, return false.
     /// Exclusive version of [`has_right`](EitherOrBoth::has_right).
     pub fn is_right(&self) -> bool {
-        match *self {
-            Right(_) => true,
-            _ => false,
-        }
+        matches!(self, Right(_))
     }
 
-    /// If Right, return true otherwise, return false.
-    /// Equivalent to `self.as_ref().both().is_some()`.
+    /// If `Both`, return true. Otherwise, return false.
     pub fn is_both(&self) -> bool {
         self.as_ref().both().is_some()
     }
 
-    /// If `Left`, or `Both`, return `Some` with the left value, otherwise, return `None`.
+    /// If `Left`, or `Both`, return `Some` with the left value. Otherwise, return `None`.
     pub fn left(self) -> Option<A> {
         match self {
             Left(left) | Both(left, _) => Some(left),
@@ -56,7 +51,7 @@
         }
     }
 
-    /// If `Right`, or `Both`, return `Some` with the right value, otherwise, return `None`.
+    /// If `Right`, or `Both`, return `Some` with the right value. Otherwise, return `None`.
     pub fn right(self) -> Option<B> {
         match self {
             Right(right) | Both(_, right) => Some(right),
@@ -64,7 +59,65 @@
         }
     }
 
-    /// If Both, return `Some` tuple containing left and right.
+    /// Return tuple of options corresponding to the left and right value respectively
+    ///
+    /// If `Left` return `(Some(..), None)`, if `Right` return `(None,Some(..))`, else return
+    /// `(Some(..),Some(..))`
+    pub fn left_and_right(self) -> (Option<A>, Option<B>) {
+        self.map_any(Some, Some).or_default()
+    }
+
+    /// If `Left`, return `Some` with the left value. If `Right` or `Both`, return `None`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// // On the `Left` variant.
+    /// # use itertools::{EitherOrBoth, EitherOrBoth::{Left, Right, Both}};
+    /// let x: EitherOrBoth<_, ()> = Left("bonjour");
+    /// assert_eq!(x.just_left(), Some("bonjour"));
+    ///
+    /// // On the `Right` variant.
+    /// let x: EitherOrBoth<(), _> = Right("hola");
+    /// assert_eq!(x.just_left(), None);
+    ///
+    /// // On the `Both` variant.
+    /// let x = Both("bonjour", "hola");
+    /// assert_eq!(x.just_left(), None);
+    /// ```
+    pub fn just_left(self) -> Option<A> {
+        match self {
+            Left(left) => Some(left),
+            _ => None,
+        }
+    }
+
+    /// If `Right`, return `Some` with the right value. If `Left` or `Both`, return `None`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// // On the `Left` variant.
+    /// # use itertools::{EitherOrBoth::{Left, Right, Both}, EitherOrBoth};
+    /// let x: EitherOrBoth<_, ()> = Left("auf wiedersehen");
+    /// assert_eq!(x.just_left(), Some("auf wiedersehen"));
+    ///
+    /// // On the `Right` variant.
+    /// let x: EitherOrBoth<(), _> = Right("adios");
+    /// assert_eq!(x.just_left(), None);
+    ///
+    /// // On the `Both` variant.
+    /// let x = Both("auf wiedersehen", "adios");
+    /// assert_eq!(x.just_left(), None);
+    /// ```
+    pub fn just_right(self) -> Option<B> {
+        match self {
+            Right(right) => Some(right),
+            _ => None,
+        }
+    }
+
+    /// If `Both`, return `Some` containing the left and right values. Otherwise, return `None`.
     pub fn both(self) -> Option<(A, B)> {
         match self {
             Both(a, b) => Some((a, b)),
@@ -72,6 +125,28 @@
         }
     }
 
+    /// If `Left` or `Both`, return the left value. Otherwise, convert the right value and return it.
+    pub fn into_left(self) -> A
+    where
+        B: Into<A>,
+    {
+        match self {
+            Left(a) | Both(a, _) => a,
+            Right(b) => b.into(),
+        }
+    }
+
+    /// If `Right` or `Both`, return the right value. Otherwise, convert the left value and return it.
+    pub fn into_right(self) -> B
+    where
+        A: Into<B>,
+    {
+        match self {
+            Right(b) | Both(_, b) => b,
+            Left(a) => a.into(),
+        }
+    }
+
     /// Converts from `&EitherOrBoth<A, B>` to `EitherOrBoth<&A, &B>`.
     pub fn as_ref(&self) -> EitherOrBoth<&A, &B> {
         match *self {
@@ -90,6 +165,32 @@
         }
     }
 
+    /// Converts from `&EitherOrBoth<A, B>` to `EitherOrBoth<&_, &_>` using the [`Deref`] trait.
+    pub fn as_deref(&self) -> EitherOrBoth<&A::Target, &B::Target>
+    where
+        A: Deref,
+        B: Deref,
+    {
+        match *self {
+            Left(ref left) => Left(left),
+            Right(ref right) => Right(right),
+            Both(ref left, ref right) => Both(left, right),
+        }
+    }
+
+    /// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut _, &mut _>` using the [`DerefMut`] trait.
+    pub fn as_deref_mut(&mut self) -> EitherOrBoth<&mut A::Target, &mut B::Target>
+    where
+        A: DerefMut,
+        B: DerefMut,
+    {
+        match *self {
+            Left(ref mut left) => Left(left),
+            Right(ref mut right) => Right(right),
+            Both(ref mut left, ref mut right) => Both(left, right),
+        }
+    }
+
     /// Convert `EitherOrBoth<A, B>` to `EitherOrBoth<B, A>`.
     pub fn flip(self) -> EitherOrBoth<B, A> {
         match self {
@@ -200,9 +301,9 @@
         B: Default,
     {
         match self {
-            EitherOrBoth::Left(l) => (l, B::default()),
-            EitherOrBoth::Right(r) => (A::default(), r),
-            EitherOrBoth::Both(l, r) => (l, r),
+            Left(l) => (l, B::default()),
+            Right(r) => (A::default(), r),
+            Both(l, r) => (l, r),
         }
     }
 
@@ -227,10 +328,160 @@
             Both(inner_l, inner_r) => (inner_l, inner_r),
         }
     }
+
+    /// Returns a mutable reference to the left value. If the left value is not present,
+    /// it is replaced with `val`.
+    pub fn left_or_insert(&mut self, val: A) -> &mut A {
+        self.left_or_insert_with(|| val)
+    }
+
+    /// Returns a mutable reference to the right value. If the right value is not present,
+    /// it is replaced with `val`.
+    pub fn right_or_insert(&mut self, val: B) -> &mut B {
+        self.right_or_insert_with(|| val)
+    }
+
+    /// If the left value is not present, replace it the value computed by the closure `f`.
+    /// Returns a mutable reference to the now-present left value.
+    pub fn left_or_insert_with<F>(&mut self, f: F) -> &mut A
+    where
+        F: FnOnce() -> A,
+    {
+        match self {
+            Left(left) | Both(left, _) => left,
+            Right(_) => self.insert_left(f()),
+        }
+    }
+
+    /// If the right value is not present, replace it the value computed by the closure `f`.
+    /// Returns a mutable reference to the now-present right value.
+    pub fn right_or_insert_with<F>(&mut self, f: F) -> &mut B
+    where
+        F: FnOnce() -> B,
+    {
+        match self {
+            Right(right) | Both(_, right) => right,
+            Left(_) => self.insert_right(f()),
+        }
+    }
+
+    /// Sets the `left` value of this instance, and returns a mutable reference to it.
+    /// Does not affect the `right` value.
+    ///
+    /// # Examples
+    /// ```
+    /// # use itertools::{EitherOrBoth, EitherOrBoth::{Left, Right, Both}};
+    ///
+    /// // Overwriting a pre-existing value.
+    /// let mut either: EitherOrBoth<_, ()> = Left(0_u32);
+    /// assert_eq!(*either.insert_left(69), 69);
+    ///
+    /// // Inserting a second value.
+    /// let mut either = Right("no");
+    /// assert_eq!(*either.insert_left("yes"), "yes");
+    /// assert_eq!(either, Both("yes", "no"));
+    /// ```
+    pub fn insert_left(&mut self, val: A) -> &mut A {
+        match self {
+            Left(left) | Both(left, _) => {
+                *left = val;
+                left
+            }
+            Right(right) => {
+                // This is like a map in place operation. We move out of the reference,
+                // change the value, and then move back into the reference.
+                unsafe {
+                    // SAFETY: We know this pointer is valid for reading since we got it from a reference.
+                    let right = std::ptr::read(right as *mut _);
+                    // SAFETY: Again, we know the pointer is valid since we got it from a reference.
+                    std::ptr::write(self as *mut _, Both(val, right));
+                }
+
+                if let Both(left, _) = self {
+                    left
+                } else {
+                    // SAFETY: The above pattern will always match, since we just
+                    // set `self` equal to `Both`.
+                    unsafe { std::hint::unreachable_unchecked() }
+                }
+            }
+        }
+    }
+
+    /// Sets the `right` value of this instance, and returns a mutable reference to it.
+    /// Does not affect the `left` value.
+    ///
+    /// # Examples
+    /// ```
+    /// # use itertools::{EitherOrBoth, EitherOrBoth::{Left, Both}};
+    /// // Overwriting a pre-existing value.
+    /// let mut either: EitherOrBoth<_, ()> = Left(0_u32);
+    /// assert_eq!(*either.insert_left(69), 69);
+    ///
+    /// // Inserting a second value.
+    /// let mut either = Left("what's");
+    /// assert_eq!(*either.insert_right(9 + 10), 21 - 2);
+    /// assert_eq!(either, Both("what's", 9+10));
+    /// ```
+    pub fn insert_right(&mut self, val: B) -> &mut B {
+        match self {
+            Right(right) | Both(_, right) => {
+                *right = val;
+                right
+            }
+            Left(left) => {
+                // This is like a map in place operation. We move out of the reference,
+                // change the value, and then move back into the reference.
+                unsafe {
+                    // SAFETY: We know this pointer is valid for reading since we got it from a reference.
+                    let left = std::ptr::read(left as *mut _);
+                    // SAFETY: Again, we know the pointer is valid since we got it from a reference.
+                    std::ptr::write(self as *mut _, Both(left, val));
+                }
+                if let Both(_, right) = self {
+                    right
+                } else {
+                    // SAFETY: The above pattern will always match, since we just
+                    // set `self` equal to `Both`.
+                    unsafe { std::hint::unreachable_unchecked() }
+                }
+            }
+        }
+    }
+
+    /// Set `self` to `Both(..)`, containing the specified left and right values,
+    /// and returns a mutable reference to those values.
+    pub fn insert_both(&mut self, left: A, right: B) -> (&mut A, &mut B) {
+        *self = Both(left, right);
+        if let Both(left, right) = self {
+            (left, right)
+        } else {
+            // SAFETY: The above pattern will always match, since we just
+            // set `self` equal to `Both`.
+            unsafe { std::hint::unreachable_unchecked() }
+        }
+    }
 }
 
 impl<T> EitherOrBoth<T, T> {
-    /// Return either value of left, right, or the product of `f` applied where `Both` are present.
+    /// Return either value of left, right, or apply a function `f` to both values if both are present.
+    /// The input function has to return the same type as both Right and Left carry.
+    ///
+    /// This function can be used to preferrably extract the left resp. right value,
+    /// but fall back to the other (i.e. right resp. left) if the preferred one is not present.
+    ///
+    /// # Examples
+    /// ```
+    /// # use itertools::EitherOrBoth;
+    /// assert_eq!(EitherOrBoth::Both(3, 7).reduce(u32::max), 7);
+    /// assert_eq!(EitherOrBoth::Left(3).reduce(u32::max), 3);
+    /// assert_eq!(EitherOrBoth::Right(7).reduce(u32::max), 7);
+    ///
+    /// // Extract the left value if present, fall back to the right otherwise.
+    /// assert_eq!(EitherOrBoth::Left("left").reduce(|l, _r| l), "left");
+    /// assert_eq!(EitherOrBoth::Right("right").reduce(|l, _r| l), "right");
+    /// assert_eq!(EitherOrBoth::Both("left", "right").reduce(|l, _r| l), "left");
+    /// ```
     pub fn reduce<F>(self, f: F) -> T
     where
         F: FnOnce(T, T) -> T,
@@ -243,12 +494,21 @@
     }
 }
 
-impl<A, B> Into<Option<Either<A, B>>> for EitherOrBoth<A, B> {
-    fn into(self) -> Option<Either<A, B>> {
-        match self {
-            EitherOrBoth::Left(l) => Some(Either::Left(l)),
-            EitherOrBoth::Right(r) => Some(Either::Right(r)),
-            _ => None,
+impl<A, B> From<EitherOrBoth<A, B>> for Option<Either<A, B>> {
+    fn from(value: EitherOrBoth<A, B>) -> Self {
+        match value {
+            Left(l) => Some(Either::Left(l)),
+            Right(r) => Some(Either::Right(r)),
+            Both(..) => None,
+        }
+    }
+}
+
+impl<A, B> From<Either<A, B>> for EitherOrBoth<A, B> {
+    fn from(either: Either<A, B>) -> Self {
+        match either {
+            Either::Left(l) => Left(l),
+            Either::Right(l) => Right(l),
         }
     }
 }
diff --git a/crates/itertools/src/exactly_one_err.rs b/crates/itertools/src/exactly_one_err.rs
index c54ae77..19b9e19 100644
--- a/crates/itertools/src/exactly_one_err.rs
+++ b/crates/itertools/src/exactly_one_err.rs
@@ -8,7 +8,7 @@
 
 use crate::size_hint;
 
-/// Iterator returned for the error case of `IterTools::exactly_one()`
+/// Iterator returned for the error case of `Itertools::exactly_one()`
 /// This iterator yields exactly the same elements as the input iterator.
 ///
 /// During the execution of `exactly_one` the iterator must be mutated.  This wrapper
@@ -54,26 +54,37 @@
             Some(Either::Left([first, second])) => {
                 self.first_two = Some(Either::Right(second));
                 Some(first)
-            },
-            Some(Either::Right(second)) => {
-                Some(second)
             }
-            None => {
-                self.inner.next()
-            }
+            Some(Either::Right(second)) => Some(second),
+            None => self.inner.next(),
         }
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
         size_hint::add_scalar(self.inner.size_hint(), self.additional_len())
     }
-}
 
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        match self.first_two {
+            Some(Either::Left([first, second])) => {
+                init = f(init, first);
+                init = f(init, second);
+            }
+            Some(Either::Right(second)) => init = f(init, second),
+            None => {}
+        }
+        self.inner.fold(init, f)
+    }
+}
 
 impl<I> ExactSizeIterator for ExactlyOneError<I> where I: ExactSizeIterator {}
 
-impl<I> Display for ExactlyOneError<I> 
-    where I: Iterator,
+impl<I> Display for ExactlyOneError<I>
+where
+    I: Iterator,
 {
     fn fmt(&self, f: &mut Formatter) -> FmtResult {
         let additional = self.additional_len();
@@ -85,26 +96,30 @@
     }
 }
 
-impl<I> Debug for ExactlyOneError<I> 
-    where I: Iterator + Debug,
-          I::Item: Debug,
+impl<I> Debug for ExactlyOneError<I>
+where
+    I: Iterator + Debug,
+    I::Item: Debug,
 {
     fn fmt(&self, f: &mut Formatter) -> FmtResult {
+        let mut dbg = f.debug_struct("ExactlyOneError");
         match &self.first_two {
             Some(Either::Left([first, second])) => {
-                write!(f, "ExactlyOneError[First: {:?}, Second: {:?}, RemainingIter: {:?}]", first, second, self.inner)
-            },
+                dbg.field("first", first).field("second", second);
+            }
             Some(Either::Right(second)) => {
-                write!(f, "ExactlyOneError[Second: {:?}, RemainingIter: {:?}]", second, self.inner)
+                dbg.field("second", second);
             }
-            None => {
-                write!(f, "ExactlyOneError[RemainingIter: {:?}]", self.inner)
-            }
+            None => {}
         }
+        dbg.field("inner", &self.inner).finish()
     }
 }
 
 #[cfg(feature = "use_std")]
-impl<I> Error for ExactlyOneError<I>  where I: Iterator + Debug, I::Item: Debug, {}
-
-
+impl<I> Error for ExactlyOneError<I>
+where
+    I: Iterator + Debug,
+    I::Item: Debug,
+{
+}
diff --git a/crates/itertools/src/extrema_set.rs b/crates/itertools/src/extrema_set.rs
index ae12836..d24114c 100644
--- a/crates/itertools/src/extrema_set.rs
+++ b/crates/itertools/src/extrema_set.rs
@@ -1,3 +1,5 @@
+#![cfg(feature = "use_alloc")]
+use alloc::{vec, vec::Vec};
 use std::cmp::Ordering;
 
 /// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`.
diff --git a/crates/itertools/src/flatten_ok.rs b/crates/itertools/src/flatten_ok.rs
index 21ae1f7..48f1e90 100644
--- a/crates/itertools/src/flatten_ok.rs
+++ b/crates/itertools/src/flatten_ok.rs
@@ -72,6 +72,29 @@
         }
     }
 
+    fn fold<B, F>(self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        // Front
+        let mut acc = match self.inner_front {
+            Some(x) => x.fold(init, |a, o| f(a, Ok(o))),
+            None => init,
+        };
+
+        acc = self.iter.fold(acc, |acc, x| match x {
+            Ok(it) => it.into_iter().fold(acc, |a, o| f(a, Ok(o))),
+            Err(e) => f(acc, Err(e)),
+        });
+
+        // Back
+        match self.inner_back {
+            Some(x) => x.fold(acc, |a, o| f(a, Ok(o))),
+            None => acc,
+        }
+    }
+
     fn size_hint(&self) -> (usize, Option<usize>) {
         let inner_hint = |inner: &Option<T::IntoIter>| {
             inner
@@ -130,6 +153,29 @@
             }
         }
     }
+
+    fn rfold<B, F>(self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        // Back
+        let mut acc = match self.inner_back {
+            Some(x) => x.rfold(init, |a, o| f(a, Ok(o))),
+            None => init,
+        };
+
+        acc = self.iter.rfold(acc, |acc, x| match x {
+            Ok(it) => it.into_iter().rfold(acc, |a, o| f(a, Ok(o))),
+            Err(e) => f(acc, Err(e)),
+        });
+
+        // Front
+        match self.inner_front {
+            Some(x) => x.rfold(acc, |a, o| f(a, Ok(o))),
+            None => acc,
+        }
+    }
 }
 
 impl<I, T, E> Clone for FlattenOk<I, T, E>
@@ -147,13 +193,7 @@
     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()
-    }
+    debug_fmt_fields!(FlattenOk, iter, inner_front, inner_back);
 }
 
 /// Only the iterator being flattened needs to implement [`FusedIterator`].
diff --git a/crates/itertools/src/format.rs b/crates/itertools/src/format.rs
index d87cee9..15cee34 100644
--- a/crates/itertools/src/format.rs
+++ b/crates/itertools/src/format.rs
@@ -1,5 +1,5 @@
+use std::cell::Cell;
 use std::fmt;
-use std::cell::RefCell;
 
 /// Format all iterator elements lazily, separated by `sep`.
 ///
@@ -7,11 +7,10 @@
 /// exhausted.
 ///
 /// See [`.format_with()`](crate::Itertools::format_with) for more information.
-#[derive(Clone)]
 pub struct FormatWith<'a, I, F> {
     sep: &'a str,
-    /// FormatWith uses interior mutability because Display::fmt takes &self.
-    inner: RefCell<Option<(I, F)>>,
+    /// `FormatWith` uses interior mutability because `Display::fmt` takes `&self`.
+    inner: Cell<Option<(I, F)>>,
 }
 
 /// Format all iterator elements lazily, separated by `sep`.
@@ -21,38 +20,40 @@
 ///
 /// See [`.format()`](crate::Itertools::format)
 /// for more information.
-#[derive(Clone)]
 pub struct Format<'a, I> {
     sep: &'a str,
-    /// Format uses interior mutability because Display::fmt takes &self.
-    inner: RefCell<Option<I>>,
+    /// `Format` uses interior mutability because `Display::fmt` takes `&self`.
+    inner: Cell<Option<I>>,
 }
 
 pub fn new_format<I, F>(iter: I, separator: &str, f: F) -> FormatWith<'_, I, F>
-    where I: Iterator,
-          F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result
+where
+    I: Iterator,
+    F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
 {
     FormatWith {
         sep: separator,
-        inner: RefCell::new(Some((iter, f))),
+        inner: Cell::new(Some((iter, f))),
     }
 }
 
 pub fn new_format_default<I>(iter: I, separator: &str) -> Format<'_, I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     Format {
         sep: separator,
-        inner: RefCell::new(Some(iter)),
+        inner: Cell::new(Some(iter)),
     }
 }
 
 impl<'a, I, F> fmt::Display for FormatWith<'a, I, F>
-    where I: Iterator,
-          F: FnMut(I::Item, &mut dyn  FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result
+where
+    I: Iterator,
+    F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let (mut iter, mut format) = match self.inner.borrow_mut().take() {
+        let (mut iter, mut format) = match self.inner.take() {
             Some(t) => t,
             None => panic!("FormatWith: was already formatted once"),
         };
@@ -70,13 +71,26 @@
     }
 }
 
-impl<'a, I> Format<'a, I>
-    where I: Iterator,
+impl<'a, I, F> fmt::Debug for FormatWith<'a, I, F>
+where
+    I: Iterator,
+    F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
 {
-    fn format<F>(&self, f: &mut fmt::Formatter, mut cb: F) -> fmt::Result
-        where F: FnMut(&I::Item, &mut fmt::Formatter) -> fmt::Result,
-    {
-        let mut iter = match self.inner.borrow_mut().take() {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(self, f)
+    }
+}
+
+impl<'a, I> Format<'a, I>
+where
+    I: Iterator,
+{
+    fn format(
+        &self,
+        f: &mut fmt::Formatter,
+        cb: fn(&I::Item, &mut fmt::Formatter) -> fmt::Result,
+    ) -> fmt::Result {
+        let mut iter = match self.inner.take() {
             Some(t) => t,
             None => panic!("Format: was already formatted once"),
         };
@@ -109,5 +123,56 @@
     }
 }
 
-impl_format!{Display Debug
-             UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer}
+impl_format! {Display Debug UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer}
+
+impl<'a, I, F> Clone for FormatWith<'a, I, F>
+where
+    (I, F): Clone,
+{
+    fn clone(&self) -> Self {
+        struct PutBackOnDrop<'r, 'a, I, F> {
+            into: &'r FormatWith<'a, I, F>,
+            inner: Option<(I, F)>,
+        }
+        // This ensures we preserve the state of the original `FormatWith` if `Clone` panics
+        impl<'r, 'a, I, F> Drop for PutBackOnDrop<'r, 'a, I, F> {
+            fn drop(&mut self) {
+                self.into.inner.set(self.inner.take())
+            }
+        }
+        let pbod = PutBackOnDrop {
+            inner: self.inner.take(),
+            into: self,
+        };
+        Self {
+            inner: Cell::new(pbod.inner.clone()),
+            sep: self.sep,
+        }
+    }
+}
+
+impl<'a, I> Clone for Format<'a, I>
+where
+    I: Clone,
+{
+    fn clone(&self) -> Self {
+        struct PutBackOnDrop<'r, 'a, I> {
+            into: &'r Format<'a, I>,
+            inner: Option<I>,
+        }
+        // This ensures we preserve the state of the original `FormatWith` if `Clone` panics
+        impl<'r, 'a, I> Drop for PutBackOnDrop<'r, 'a, I> {
+            fn drop(&mut self) {
+                self.into.inner.set(self.inner.take())
+            }
+        }
+        let pbod = PutBackOnDrop {
+            inner: self.inner.take(),
+            into: self,
+        };
+        Self {
+            inner: Cell::new(pbod.inner.clone()),
+            sep: self.sep,
+        }
+    }
+}
diff --git a/crates/itertools/src/free.rs b/crates/itertools/src/free.rs
index 19e3e28..8d0bcf3 100644
--- a/crates/itertools/src/free.rs
+++ b/crates/itertools/src/free.rs
@@ -10,30 +10,24 @@
 type VecIntoIter<T> = alloc::vec::IntoIter<T>;
 
 #[cfg(feature = "use_alloc")]
-use alloc::{
-    string::String,
-};
+use alloc::string::String;
 
-use crate::Itertools;
 use crate::intersperse::{Intersperse, IntersperseWith};
+use crate::Itertools;
 
-pub use crate::adaptors::{
-    interleave,
-    merge,
-    put_back,
-};
+pub use crate::adaptors::{interleave, put_back};
 #[cfg(feature = "use_alloc")]
-pub use crate::put_back_n_impl::put_back_n;
+pub use crate::kmerge_impl::kmerge;
+pub use crate::merge_join::{merge, merge_join_by};
 #[cfg(feature = "use_alloc")]
 pub use crate::multipeek_impl::multipeek;
 #[cfg(feature = "use_alloc")]
 pub use crate::peek_nth::peek_nth;
 #[cfg(feature = "use_alloc")]
-pub use crate::kmerge_impl::kmerge;
-pub use crate::zip_eq_impl::zip_eq;
-pub use crate::merge_join::merge_join_by;
+pub use crate::put_back_n_impl::put_back_n;
 #[cfg(feature = "use_alloc")]
 pub use crate::rciter_impl::rciter;
+pub use crate::zip_eq_impl::zip_eq;
 
 /// Iterate `iterable` with a particular value inserted between each element.
 ///
@@ -45,8 +39,9 @@
 /// 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
+where
+    I: IntoIterator,
+    <I as IntoIterator>::Item: Clone,
 {
     Itertools::intersperse(iterable.into_iter(), element)
 }
@@ -64,8 +59,9 @@
 /// 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
+where
+    I: IntoIterator,
+    F: FnMut() -> I::Item,
 {
     Itertools::intersperse_with(iterable.into_iter(), element)
 }
@@ -82,7 +78,8 @@
 /// }
 /// ```
 pub fn enumerate<I>(iterable: I) -> iter::Enumerate<I::IntoIter>
-    where I: IntoIterator
+where
+    I: IntoIterator,
 {
     iterable.into_iter().enumerate()
 }
@@ -99,8 +96,9 @@
 /// }
 /// ```
 pub fn rev<I>(iterable: I) -> iter::Rev<I::IntoIter>
-    where I: IntoIterator,
-          I::IntoIter: DoubleEndedIterator
+where
+    I: IntoIterator,
+    I::IntoIter: DoubleEndedIterator,
 {
     iterable.into_iter().rev()
 }
@@ -108,7 +106,7 @@
 /// Converts the arguments to iterators and zips them.
 ///
 /// [`IntoIterator`] enabled version of [`Iterator::zip`].
-/// 
+///
 /// ## Example
 ///
 /// ```
@@ -121,23 +119,26 @@
 /// }
 /// assert_eq!(result, vec![(1, 'a'),(2, 'b'),(3, 'c')]);
 /// ```
-#[deprecated(note="Use [std::iter::zip](https://doc.rust-lang.org/std/iter/fn.zip.html) instead", since="0.10.4")]
+#[deprecated(
+    note = "Use [std::iter::zip](https://doc.rust-lang.org/std/iter/fn.zip.html) instead",
+    since = "0.10.4"
+)]
 pub fn zip<I, J>(i: I, j: J) -> Zip<I::IntoIter, J::IntoIter>
-    where I: IntoIterator,
-          J: IntoIterator
+where
+    I: IntoIterator,
+    J: IntoIterator,
 {
     i.into_iter().zip(j)
 }
 
-
-/// Takes two iterables and creates a new iterator over both in sequence. 
+/// Takes two iterables and creates a new iterator over both in sequence.
 ///
 /// [`IntoIterator`] enabled version of [`Iterator::chain`].
 ///
 /// ## Example
 /// ```
 /// use itertools::chain;
-/// 
+///
 /// let mut result:Vec<i32> = Vec::new();
 ///
 /// for element in chain(&[1, 2, 3], &[4]) {
@@ -145,14 +146,18 @@
 /// }
 /// assert_eq!(result, vec![1, 2, 3, 4]);
 /// ```
-pub fn chain<I, J>(i: I, j: J) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
-    where I: IntoIterator,
-          J: IntoIterator<Item = I::Item>
+pub fn chain<I, J>(
+    i: I,
+    j: J,
+) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
+where
+    I: IntoIterator,
+    J: IntoIterator<Item = I::Item>,
 {
     i.into_iter().chain(j)
 }
 
-/// Create an iterator that clones each element from &T to T
+/// Create an iterator that clones each element from `&T` to `T`.
 ///
 /// [`IntoIterator`] enabled version of [`Iterator::cloned`].
 ///
@@ -161,9 +166,10 @@
 ///
 /// assert_eq!(cloned(b"abc").next(), Some(b'a'));
 /// ```
-pub fn cloned<'a, I, T: 'a>(iterable: I) -> iter::Cloned<I::IntoIter>
-    where I: IntoIterator<Item=&'a T>,
-          T: Clone,
+pub fn cloned<'a, I, T>(iterable: I) -> iter::Cloned<I::IntoIter>
+where
+    I: IntoIterator<Item = &'a T>,
+    T: Clone + 'a,
 {
     iterable.into_iter().cloned()
 }
@@ -178,8 +184,9 @@
 /// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.);
 /// ```
 pub fn fold<I, B, F>(iterable: I, init: B, f: F) -> B
-    where I: IntoIterator,
-          F: FnMut(B, I::Item) -> B
+where
+    I: IntoIterator,
+    F: FnMut(B, I::Item) -> B,
 {
     iterable.into_iter().fold(init, f)
 }
@@ -194,8 +201,9 @@
 /// assert!(all(&[1, 2, 3], |elt| *elt > 0));
 /// ```
 pub fn all<I, F>(iterable: I, f: F) -> bool
-    where I: IntoIterator,
-          F: FnMut(I::Item) -> bool
+where
+    I: IntoIterator,
+    F: FnMut(I::Item) -> bool,
 {
     iterable.into_iter().all(f)
 }
@@ -210,8 +218,9 @@
 /// assert!(any(&[0, -1, 2], |elt| *elt > 0));
 /// ```
 pub fn any<I, F>(iterable: I, f: F) -> bool
-    where I: IntoIterator,
-          F: FnMut(I::Item) -> bool
+where
+    I: IntoIterator,
+    F: FnMut(I::Item) -> bool,
 {
     iterable.into_iter().any(f)
 }
@@ -226,8 +235,9 @@
 /// assert_eq!(max(0..10), Some(9));
 /// ```
 pub fn max<I>(iterable: I) -> Option<I::Item>
-    where I: IntoIterator,
-          I::Item: Ord
+where
+    I: IntoIterator,
+    I::Item: Ord,
 {
     iterable.into_iter().max()
 }
@@ -242,14 +252,14 @@
 /// assert_eq!(min(0..10), Some(0));
 /// ```
 pub fn min<I>(iterable: I) -> Option<I::Item>
-    where I: IntoIterator,
-          I::Item: Ord
+where
+    I: IntoIterator,
+    I::Item: Ord,
 {
     iterable.into_iter().min()
 }
 
-
-/// Combine all iterator elements into one String, separated by `sep`.
+/// Combine all iterator elements into one `String`, separated by `sep`.
 ///
 /// [`IntoIterator`] enabled version of [`Itertools::join`].
 ///
@@ -260,8 +270,9 @@
 /// ```
 #[cfg(feature = "use_alloc")]
 pub fn join<I>(iterable: I, sep: &str) -> String
-    where I: IntoIterator,
-          I::Item: Display
+where
+    I: IntoIterator,
+    I::Item: Display,
 {
     iterable.into_iter().join(sep)
 }
@@ -278,9 +289,29 @@
 /// ```
 #[cfg(feature = "use_alloc")]
 pub fn sorted<I>(iterable: I) -> VecIntoIter<I::Item>
-    where I: IntoIterator,
-          I::Item: Ord
+where
+    I: IntoIterator,
+    I::Item: Ord,
 {
     iterable.into_iter().sorted()
 }
 
+/// Sort all iterator elements into a new iterator in ascending order.
+/// This sort is unstable (i.e., may reorder equal elements).
+///
+/// [`IntoIterator`] enabled version of [`Itertools::sorted_unstable`].
+///
+/// ```
+/// use itertools::sorted_unstable;
+/// use itertools::assert_equal;
+///
+/// assert_equal(sorted_unstable("rust".chars()), "rstu".chars());
+/// ```
+#[cfg(feature = "use_alloc")]
+pub fn sorted_unstable<I>(iterable: I) -> VecIntoIter<I::Item>
+where
+    I: IntoIterator,
+    I::Item: Ord,
+{
+    iterable.into_iter().sorted_unstable()
+}
diff --git a/crates/itertools/src/group_map.rs b/crates/itertools/src/group_map.rs
index a2d0ebb..3dcee83 100644
--- a/crates/itertools/src/group_map.rs
+++ b/crates/itertools/src/group_map.rs
@@ -9,8 +9,9 @@
 /// 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)>,
-          K: Hash + Eq,
+where
+    I: Iterator<Item = (K, V)>,
+    K: Hash + Eq,
 {
     let mut lookup = HashMap::new();
 
@@ -21,12 +22,11 @@
     lookup
 }
 
-pub fn into_group_map_by<I, K, V>(iter: I, f: impl Fn(&V) -> K) -> HashMap<K, Vec<V>>
-    where
-        I: Iterator<Item=V>,
-        K: Hash + Eq,
+pub fn into_group_map_by<I, K, V, F>(iter: I, mut f: F) -> HashMap<K, Vec<V>>
+where
+    I: Iterator<Item = V>,
+    K: Hash + Eq,
+    F: FnMut(&V) -> K,
 {
-    into_group_map(
-        iter.map(|v| (f(&v), v))
-    )
+    into_group_map(iter.map(|v| (f(&v), v)))
 }
diff --git a/crates/itertools/src/groupbylazy.rs b/crates/itertools/src/groupbylazy.rs
index a5a321d..5847c8f 100644
--- a/crates/itertools/src/groupbylazy.rs
+++ b/crates/itertools/src/groupbylazy.rs
@@ -1,14 +1,15 @@
-use std::cell::{Cell, RefCell};
 use alloc::vec::{self, Vec};
+use std::cell::{Cell, RefCell};
 
-/// A trait to unify `FnMut` for `GroupBy` with the chunk key in `IntoChunks`
+/// A trait to unify `FnMut` for `ChunkBy` with the chunk key in `IntoChunks`
 trait KeyFunction<A> {
     type Key;
     fn call_mut(&mut self, arg: A) -> Self::Key;
 }
 
-impl<A, K, F: ?Sized> KeyFunction<A> for F
-    where F: FnMut(A) -> K
+impl<A, K, F> KeyFunction<A> for F
+where
+    F: FnMut(A) -> K + ?Sized,
 {
     type Key = K;
     #[inline]
@@ -17,9 +18,8 @@
     }
 }
 
-
 /// `ChunkIndex` acts like the grouping key function for `IntoChunks`
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 struct ChunkIndex {
     size: usize,
     index: usize,
@@ -29,7 +29,7 @@
 impl ChunkIndex {
     #[inline(always)]
     fn new(size: usize) -> Self {
-        ChunkIndex {
+        Self {
             size,
             index: 0,
             key: 0,
@@ -50,9 +50,10 @@
     }
 }
 
-
+#[derive(Clone)]
 struct GroupInner<K, I, F>
-    where I: Iterator
+where
+    I: Iterator,
 {
     key: F,
     iter: I,
@@ -65,19 +66,21 @@
     /// Least index for which we still have elements buffered
     oldest_buffered_group: usize,
     /// Group index for `buffer[0]` -- the slots
-    /// bottom_group..oldest_buffered_group are unused and will be erased when
+    /// `bottom_group..oldest_buffered_group` are unused and will be erased when
     /// that range is large enough.
     bottom_group: usize,
     /// Buffered groups, from `bottom_group` (index 0) to `top_group`.
     buffer: Vec<vec::IntoIter<I::Item>>,
-    /// index of last group iter that was dropped, usize::MAX == none
+    /// index of last group iter that was dropped,
+    /// `usize::MAX` initially when no group was dropped
     dropped_group: usize,
 }
 
 impl<K, I, F> GroupInner<K, I, F>
-    where I: Iterator,
-          F: for<'a> KeyFunction<&'a I::Item, Key=K>,
-          K: PartialEq,
+where
+    I: Iterator,
+    F: for<'a> KeyFunction<&'a I::Item, Key = K>,
+    K: PartialEq,
 {
     /// `client`: Index of group that requests next element
     #[inline(always)]
@@ -90,9 +93,8 @@
         */
         if client < self.oldest_buffered_group {
             None
-        } else if client < self.top_group ||
-            (client == self.top_group &&
-             self.buffer.len() > self.top_group - self.bottom_group)
+        } else if client < self.top_group
+            || (client == self.top_group && self.buffer.len() > self.top_group - self.bottom_group)
         {
             self.lookup_buffer(client)
         } else if self.done {
@@ -118,8 +120,10 @@
             // `bottom_group..oldest_buffered_group` is unused, and if it's large enough, erase it.
             self.oldest_buffered_group += 1;
             // skip forward further empty queues too
-            while self.buffer.get(self.oldest_buffered_group - self.bottom_group)
-                             .map_or(false, |buf| buf.len() == 0)
+            while self
+                .buffer
+                .get(self.oldest_buffered_group - self.bottom_group)
+                .map_or(false, |buf| buf.len() == 0)
             {
                 self.oldest_buffered_group += 1;
             }
@@ -144,12 +148,14 @@
     fn next_element(&mut self) -> Option<I::Item> {
         debug_assert!(!self.done);
         match self.iter.next() {
-            None => { self.done = true; None }
+            None => {
+                self.done = true;
+                None
+            }
             otherwise => otherwise,
         }
     }
 
-
     #[inline(never)]
     fn step_buffering(&mut self, client: usize) -> Option<I::Item> {
         // requested a later group -- walk through the current group up to
@@ -171,11 +177,13 @@
             let key = self.key.call_mut(&elt);
             match self.current_key.take() {
                 None => {}
-                Some(old_key) => if old_key != key {
-                    self.current_key = Some(key);
-                    first_elt = Some(elt);
-                    break;
-                },
+                Some(old_key) => {
+                    if old_key != key {
+                        self.current_key = Some(key);
+                        first_elt = Some(elt);
+                        break;
+                    }
+                }
             }
             self.current_key = Some(key);
             if self.top_group != self.dropped_group {
@@ -220,12 +228,14 @@
                 let key = self.key.call_mut(&elt);
                 match self.current_key.take() {
                     None => {}
-                    Some(old_key) => if old_key != key {
-                        self.current_key = Some(key);
-                        self.current_elt = Some(elt);
-                        self.top_group += 1;
-                        return None;
-                    },
+                    Some(old_key) => {
+                        if old_key != key {
+                            self.current_key = Some(key);
+                            self.current_elt = Some(elt);
+                            self.top_group += 1;
+                            return None;
+                        }
+                    }
                 }
                 self.current_key = Some(key);
                 Some(elt)
@@ -261,7 +271,8 @@
 }
 
 impl<K, I, F> GroupInner<K, I, F>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     /// Called when a group is dropped
     fn drop_group(&mut self, client: usize) {
@@ -272,10 +283,14 @@
     }
 }
 
-/// `GroupBy` is the storage for the lazy grouping operation.
+#[deprecated(note = "Use `ChunkBy` instead", since = "0.13.0")]
+/// See [`ChunkBy`](crate::structs::ChunkBy).
+pub type GroupBy<K, I, F> = ChunkBy<K, I, F>;
+
+/// `ChunkBy` is the storage for the lazy grouping operation.
 ///
 /// If the groups are consumed in their original order, or if each
-/// group is dropped without keeping it around, then `GroupBy` uses
+/// group is dropped without keeping it around, then `ChunkBy` uses
 /// no allocations. It needs allocations only if several group iterators
 /// are alive at the same time.
 ///
@@ -284,10 +299,11 @@
 /// value. It should be stored in a local variable or temporary and
 /// iterated.
 ///
-/// See [`.group_by()`](crate::Itertools::group_by) for more information.
+/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct GroupBy<K, I, F>
-    where I: Iterator,
+pub struct ChunkBy<K, I, F>
+where
+    I: Iterator,
 {
     inner: RefCell<GroupInner<K, I, F>>,
     // the group iterator's current index. Keep this in the main value
@@ -296,11 +312,12 @@
 }
 
 /// Create a new
-pub fn new<K, J, F>(iter: J, f: F) -> GroupBy<K, J::IntoIter, F>
-    where J: IntoIterator,
-          F: FnMut(&J::Item) -> K,
+pub fn new<K, J, F>(iter: J, f: F) -> ChunkBy<K, J::IntoIter, F>
+where
+    J: IntoIterator,
+    F: FnMut(&J::Item) -> K,
 {
-    GroupBy {
+    ChunkBy {
         inner: RefCell::new(GroupInner {
             key: f,
             iter: iter.into_iter(),
@@ -317,13 +334,15 @@
     }
 }
 
-impl<K, I, F> GroupBy<K, I, F>
-    where I: Iterator,
+impl<K, I, F> ChunkBy<K, I, F>
+where
+    I: Iterator,
 {
     /// `client`: Index of group that requests next element
     fn step(&self, client: usize) -> Option<I::Item>
-        where F: FnMut(&I::Item) -> K,
-              K: PartialEq,
+    where
+        F: FnMut(&I::Item) -> K,
+        K: PartialEq,
     {
         self.inner.borrow_mut().step(client)
     }
@@ -334,11 +353,12 @@
     }
 }
 
-impl<'a, K, I, F> IntoIterator for &'a GroupBy<K, I, F>
-    where I: Iterator,
-          I::Item: 'a,
-          F: FnMut(&I::Item) -> K,
-          K: PartialEq
+impl<'a, K, I, F> IntoIterator for &'a ChunkBy<K, I, F>
+where
+    I: Iterator,
+    I::Item: 'a,
+    F: FnMut(&I::Item) -> K,
+    K: PartialEq,
 {
     type Item = (K, Group<'a, K, I, F>);
     type IntoIter = Groups<'a, K, I, F>;
@@ -348,26 +368,29 @@
     }
 }
 
-
 /// An iterator that yields the Group iterators.
 ///
 /// Iterator element type is `(K, Group)`:
 /// the group's key `K` and the group's iterator.
 ///
-/// See [`.group_by()`](crate::Itertools::group_by) for more information.
+/// See [`.chunk_by()`](crate::Itertools::chunk_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,
-          I::Item: 'a
+pub struct Groups<'a, K, I, F>
+where
+    I: Iterator + 'a,
+    I::Item: 'a,
+    K: 'a,
+    F: 'a,
 {
-    parent: &'a GroupBy<K, I, F>,
+    parent: &'a ChunkBy<K, I, F>,
 }
 
 impl<'a, K, I, F> Iterator for Groups<'a, K, I, F>
-    where I: Iterator,
-          I::Item: 'a,
-          F: FnMut(&I::Item) -> K,
-          K: PartialEq
+where
+    I: Iterator,
+    I::Item: 'a,
+    F: FnMut(&I::Item) -> K,
+    K: PartialEq,
 {
     type Item = (K, Group<'a, K, I, F>);
 
@@ -378,11 +401,14 @@
         let inner = &mut *self.parent.inner.borrow_mut();
         inner.step(index).map(|elt| {
             let key = inner.group_key(index);
-            (key, Group {
-                parent: self.parent,
-                index,
-                first: Some(elt),
-            })
+            (
+                key,
+                Group {
+                    parent: self.parent,
+                    index,
+                    first: Some(elt),
+                },
+            )
         })
     }
 }
@@ -390,18 +416,22 @@
 /// An iterator for the elements in a single group.
 ///
 /// Iterator element type is `I::Item`.
-pub struct Group<'a, K: 'a, I: 'a, F: 'a>
-    where I: Iterator,
-          I::Item: 'a,
+pub struct Group<'a, K, I, F>
+where
+    I: Iterator + 'a,
+    I::Item: 'a,
+    K: 'a,
+    F: 'a,
 {
-    parent: &'a GroupBy<K, I, F>,
+    parent: &'a ChunkBy<K, I, F>,
     index: usize,
     first: Option<I::Item>,
 }
 
 impl<'a, K, I, F> Drop for Group<'a, K, I, F>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     fn drop(&mut self) {
         self.parent.drop_group(self.index);
@@ -409,10 +439,11 @@
 }
 
 impl<'a, K, I, F> Iterator for Group<'a, K, I, F>
-    where I: Iterator,
-          I::Item: 'a,
-          F: FnMut(&I::Item) -> K,
-          K: PartialEq,
+where
+    I: Iterator,
+    I::Item: 'a,
+    F: FnMut(&I::Item) -> K,
+    K: PartialEq,
 {
     type Item = I::Item;
     #[inline]
@@ -428,7 +459,8 @@
 
 /// Create a new
 pub fn new_chunks<J>(iter: J, size: usize) -> IntoChunks<J::IntoIter>
-    where J: IntoIterator,
+where
+    J: IntoIterator,
 {
     IntoChunks {
         inner: RefCell::new(GroupInner {
@@ -447,10 +479,9 @@
     }
 }
 
-
 /// `ChunkLazy` is the storage for a lazy chunking operation.
 ///
-/// `IntoChunks` behaves just like `GroupBy`: it is iterable, and
+/// `IntoChunks` behaves just like `ChunkBy`: 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
@@ -463,7 +494,8 @@
 /// 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,
+where
+    I: Iterator,
 {
     inner: RefCell<GroupInner<usize, I, ChunkIndex>>,
     // the chunk iterator's current index. Keep this in the main value
@@ -471,9 +503,17 @@
     index: Cell<usize>,
 }
 
+impl<I> Clone for IntoChunks<I>
+where
+    I: Clone + Iterator,
+    I::Item: Clone,
+{
+    clone_fields!(inner, index);
+}
 
 impl<I> IntoChunks<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     /// `client`: Index of chunk that requests next element
     fn step(&self, client: usize) -> Option<I::Item> {
@@ -487,36 +527,37 @@
 }
 
 impl<'a, I> IntoIterator for &'a IntoChunks<I>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     type Item = Chunk<'a, I>;
     type IntoIter = Chunks<'a, I>;
 
     fn into_iter(self) -> Self::IntoIter {
-        Chunks {
-            parent: self,
-        }
+        Chunks { parent: self }
     }
 }
 
-
 /// An iterator that yields the Chunk iterators.
 ///
 /// Iterator element type is `Chunk`.
 ///
 /// 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,
-          I::Item: 'a,
+#[derive(Clone)]
+pub struct Chunks<'a, I>
+where
+    I: Iterator + 'a,
+    I::Item: 'a,
 {
     parent: &'a IntoChunks<I>,
 }
 
 impl<'a, I> Iterator for Chunks<'a, I>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     type Item = Chunk<'a, I>;
 
@@ -525,12 +566,10 @@
         let index = self.parent.index.get();
         self.parent.index.set(index + 1);
         let inner = &mut *self.parent.inner.borrow_mut();
-        inner.step(index).map(|elt| {
-            Chunk {
-                parent: self.parent,
-                index,
-                first: Some(elt),
-            }
+        inner.step(index).map(|elt| Chunk {
+            parent: self.parent,
+            index,
+            first: Some(elt),
         })
     }
 }
@@ -538,9 +577,10 @@
 /// An iterator for the elements in a single chunk.
 ///
 /// Iterator element type is `I::Item`.
-pub struct Chunk<'a, I: 'a>
-    where I: Iterator,
-          I::Item: 'a,
+pub struct Chunk<'a, I>
+where
+    I: Iterator + 'a,
+    I::Item: 'a,
 {
     parent: &'a IntoChunks<I>,
     index: usize,
@@ -548,8 +588,9 @@
 }
 
 impl<'a, I> Drop for Chunk<'a, I>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     fn drop(&mut self) {
         self.parent.drop_group(self.index);
@@ -557,8 +598,9 @@
 }
 
 impl<'a, I> Iterator for Chunk<'a, I>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     type Item = I::Item;
     #[inline]
diff --git a/crates/itertools/src/grouping_map.rs b/crates/itertools/src/grouping_map.rs
index bb5b582..b4aae9e 100644
--- a/crates/itertools/src/grouping_map.rs
+++ b/crates/itertools/src/grouping_map.rs
@@ -1,50 +1,60 @@
 #![cfg(feature = "use_std")]
 
-use crate::MinMaxResult;
-use std::collections::HashMap;
+use crate::{
+    adaptors::map::{MapSpecialCase, MapSpecialCaseFn},
+    MinMaxResult,
+};
 use std::cmp::Ordering;
+use std::collections::HashMap;
 use std::hash::Hash;
 use std::iter::Iterator;
 use std::ops::{Add, Mul};
 
 /// 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);
+pub type MapForGrouping<I, F> = MapSpecialCase<I, GroupingMapFn<F>>;
 
-impl<I, F> MapForGrouping<I, F> {
-    pub(crate) fn new(iter: I, key_mapper: F) -> Self {
-        Self(iter, key_mapper)
+#[derive(Clone)]
+pub struct GroupingMapFn<F>(F);
+
+impl<F> std::fmt::Debug for GroupingMapFn<F> {
+    debug_fmt_fields!(GroupingMapFn,);
+}
+
+impl<V, K, F: FnMut(&V) -> K> MapSpecialCaseFn<V> for GroupingMapFn<F> {
+    type Out = (K, V);
+    fn call(&mut self, v: V) -> Self::Out {
+        ((self.0)(&v), v)
     }
 }
 
-impl<K, V, I, F> Iterator for MapForGrouping<I, F>
-    where I: Iterator<Item = V>,
-          K: Hash + Eq,
-          F: FnMut(&V) -> K,
-{
-    type Item = (K, V);
-    fn next(&mut self) -> Option<Self::Item> {
-        self.0.next().map(|val| ((self.1)(&val), val))
+pub(crate) fn new_map_for_grouping<K, I: Iterator, F: FnMut(&I::Item) -> K>(
+    iter: I,
+    key_mapper: F,
+) -> MapForGrouping<I, F> {
+    MapSpecialCase {
+        iter,
+        f: GroupingMapFn(key_mapper),
     }
 }
 
 /// Creates a new `GroupingMap` from `iter`
 pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
-    where I: Iterator<Item = (K, V)>,
-          K: Hash + Eq,
+where
+    I: Iterator<Item = (K, V)>,
+    K: Hash + Eq,
 {
     GroupingMap { iter }
 }
 
 /// `GroupingMapBy` is an intermediate struct for efficient group-and-fold operations.
-/// 
+///
 /// See [`GroupingMap`] for more informations.
 pub type GroupingMapBy<I, F> = GroupingMap<MapForGrouping<I, F>>;
 
 /// `GroupingMap` is an intermediate struct for efficient group-and-fold operations.
 /// It groups elements by their key and at the same time fold each group
 /// using some aggregating operation.
-/// 
+///
 /// No method on this struct performs temporary allocations.
 #[derive(Clone, Debug)]
 #[must_use = "GroupingMap is lazy and do nothing unless consumed"]
@@ -53,13 +63,14 @@
 }
 
 impl<I, K, V> GroupingMap<I>
-    where I: Iterator<Item = (K, V)>,
-          K: Hash + Eq,
+where
+    I: Iterator<Item = (K, V)>,
+    K: Hash + Eq,
 {
     /// This is the generic way to perform any operation on a `GroupingMap`.
     /// It's suggested to use this method only to implement custom operations
     /// when the already provided ones are not enough.
-    /// 
+    ///
     /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
     /// of each group sequentially, passing the previously accumulated value, a reference to the key
     /// and the current element as arguments, and stores the results in an `HashMap`.
@@ -68,17 +79,17 @@
     ///  - the current value of the accumulator of the group if there is currently one;
     ///  - a reference to the key of the group this element belongs to;
     ///  - the element from the source being aggregated;
-    /// 
+    ///
     /// If `operation` returns `Some(element)` then the accumulator is updated with `element`,
     /// otherwise the previous accumulation is discarded.
     ///
     /// Return a `HashMap` associating the key of each group with the result of aggregation of
     /// that group's elements. If the aggregation of the last element of a group discards the
     /// accumulator then there won't be an entry associated to that group's key.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let data = vec![2, 8, 5, 7, 9, 0, 4, 10];
     /// let lookup = data.into_iter()
     ///     .into_grouping_map_by(|&n| n % 4)
@@ -89,7 +100,7 @@
     ///             Some(acc.unwrap_or(0) + val)
     ///         }
     ///     });
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 4);        // 0 resets the accumulator so only 4 is summed
     /// assert_eq!(lookup[&1], 5 + 9);
     /// assert_eq!(lookup.get(&2), None); // 10 resets the accumulator and nothing is summed afterward
@@ -97,7 +108,8 @@
     /// assert_eq!(lookup.len(), 3);      // The final keys are only 0, 1 and 2
     /// ```
     pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
-        where FO: FnMut(Option<R>, &K, V) -> Option<R>,
+    where
+        FO: FnMut(Option<R>, &K, V) -> Option<R>,
     {
         let mut destination_map = HashMap::new();
 
@@ -115,6 +127,50 @@
     /// of each group sequentially, passing the previously accumulated value, a reference to the key
     /// and the current element as arguments, and stores the results in a new map.
     ///
+    /// `init` is called to obtain the initial value of each accumulator.
+    ///
+    /// `operation` is a function that is invoked on each element with the following parameters:
+    ///  - the current value of the accumulator of the group;
+    ///  - a reference to the key of the group this element belongs to;
+    ///  - the element from the source being accumulated.
+    ///
+    /// Return a `HashMap` associating the key of each group with the result of folding that group's elements.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// #[derive(Debug, Default)]
+    /// struct Accumulator {
+    ///   acc: usize,
+    /// }
+    ///
+    /// let lookup = (1..=7)
+    ///     .into_grouping_map_by(|&n| n % 3)
+    ///     .fold_with(|_key, _val| Default::default(), |Accumulator { acc }, _key, val| {
+    ///         let acc = acc + val;
+    ///         Accumulator { acc }
+    ///      });
+    ///
+    /// assert_eq!(lookup[&0].acc, 3 + 6);
+    /// assert_eq!(lookup[&1].acc, 1 + 4 + 7);
+    /// assert_eq!(lookup[&2].acc, 2 + 5);
+    /// assert_eq!(lookup.len(), 3);
+    /// ```
+    pub fn fold_with<FI, FO, R>(self, mut init: FI, mut operation: FO) -> HashMap<K, R>
+    where
+        FI: FnMut(&K, &V) -> R,
+        FO: FnMut(R, &K, V) -> R,
+    {
+        self.aggregate(|acc, key, val| {
+            let acc = acc.unwrap_or_else(|| init(key, &val));
+            Some(operation(acc, key, val))
+        })
+    }
+
+    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
+    /// of each group sequentially, passing the previously accumulated value, a reference to the key
+    /// and the current element as arguments, and stores the results in a new map.
+    ///
     /// `init` is the value from which will be cloned the initial value of each accumulator.
     ///
     /// `operation` is a function that is invoked on each element with the following parameters:
@@ -123,27 +179,25 @@
     ///  - the element from the source being accumulated.
     ///
     /// Return a `HashMap` associating the key of each group with the result of folding that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = (1..=7)
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .fold(0, |acc, _key, val| acc + val);
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3 + 6);
     /// assert_eq!(lookup[&1], 1 + 4 + 7);
     /// assert_eq!(lookup[&2], 2 + 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
-    pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
-        where R: Clone,
-              FO: FnMut(R, &K, V) -> R,
+    pub fn fold<FO, R>(self, init: R, operation: FO) -> HashMap<K, R>
+    where
+        R: Clone,
+        FO: FnMut(R, &K, V) -> R,
     {
-        self.aggregate(|acc, key, val| {
-            let acc = acc.unwrap_or_else(|| init.clone());
-            Some(operation(acc, key, val))
-        })
+        self.fold_with(|_, _| init.clone(), operation)
     }
 
     /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
@@ -158,23 +212,24 @@
     ///  - the element from the source being accumulated.
     ///
     /// Return a `HashMap` associating the key of each group with the result of folding that group's elements.
-    /// 
+    ///
     /// [`fold`]: GroupingMap::fold
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = (1..=7)
     ///     .into_grouping_map_by(|&n| n % 3)
-    ///     .fold_first(|acc, _key, val| acc + val);
-    /// 
+    ///     .reduce(|acc, _key, val| acc + val);
+    ///
     /// assert_eq!(lookup[&0], 3 + 6);
     /// assert_eq!(lookup[&1], 1 + 4 + 7);
     /// assert_eq!(lookup[&2], 2 + 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
-    pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
-        where FO: FnMut(V, &K, V) -> V,
+    pub fn reduce<FO>(self, mut operation: FO) -> HashMap<K, V>
+    where
+        FO: FnMut(V, &K, V) -> V,
     {
         self.aggregate(|acc, key, val| {
             Some(match acc {
@@ -184,250 +239,271 @@
         })
     }
 
+    /// See [`.reduce()`](GroupingMap::reduce).
+    #[deprecated(note = "Use .reduce() instead", since = "0.13.0")]
+    pub fn fold_first<FO>(self, operation: FO) -> HashMap<K, V>
+    where
+        FO: FnMut(V, &K, V) -> V,
+    {
+        self.reduce(operation)
+    }
+
     /// Groups elements from the `GroupingMap` source by key and collects the elements of each group in
-    /// an instance of `C`. The iteration order is preserved when inserting elements. 
-    /// 
+    /// an instance of `C`. The iteration order is preserved when inserting elements.
+    ///
     /// Return a `HashMap` associating the key of each group with the collection containing that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
     /// use std::collections::HashSet;
-    /// 
+    ///
     /// let lookup = vec![0, 1, 2, 3, 4, 5, 6, 2, 3, 6].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .collect::<HashSet<_>>();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], vec![0, 3, 6].into_iter().collect::<HashSet<_>>());
     /// assert_eq!(lookup[&1], vec![1, 4].into_iter().collect::<HashSet<_>>());
     /// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect::<HashSet<_>>());
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn collect<C>(self) -> HashMap<K, C>
-        where C: Default + Extend<V>,
+    where
+        C: Default + Extend<V>,
     {
         let mut destination_map = HashMap::new();
 
         self.iter.for_each(|(key, val)| {
-            destination_map.entry(key).or_insert_with(C::default).extend(Some(val));
+            destination_map
+                .entry(key)
+                .or_insert_with(C::default)
+                .extend(Some(val));
         });
 
         destination_map
     }
 
     /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group.
-    /// 
+    ///
     /// If several elements are equally maximum, the last element is picked.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .max();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 12);
     /// assert_eq!(lookup[&1], 7);
     /// assert_eq!(lookup[&2], 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn max(self) -> HashMap<K, V>
-        where V: Ord,
+    where
+        V: Ord,
     {
         self.max_by(|_, v1, v2| V::cmp(v1, v2))
     }
 
     /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group
     /// with respect to the specified comparison function.
-    /// 
+    ///
     /// If several elements are equally maximum, the last element is picked.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .max_by(|_key, x, y| y.cmp(x));
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3);
     /// assert_eq!(lookup[&1], 1);
     /// assert_eq!(lookup[&2], 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V>
-        where F: FnMut(&K, &V, &V) -> Ordering,
+    where
+        F: FnMut(&K, &V, &V) -> Ordering,
     {
-        self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
+        self.reduce(|acc, key, val| match compare(key, &acc, &val) {
             Ordering::Less | Ordering::Equal => val,
-            Ordering::Greater => acc
+            Ordering::Greater => acc,
         })
     }
 
     /// Groups elements from the `GroupingMap` source by key and finds the element of each group
     /// that gives the maximum from the specified function.
-    /// 
+    ///
     /// If several elements are equally maximum, the last element is picked.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .max_by_key(|_key, &val| val % 4);
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3);
     /// assert_eq!(lookup[&1], 7);
     /// assert_eq!(lookup[&2], 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
-        where F: FnMut(&K, &V) -> CK,
-              CK: Ord,
+    where
+        F: FnMut(&K, &V) -> CK,
+        CK: Ord,
     {
         self.max_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2)))
     }
 
     /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group.
-    /// 
+    ///
     /// If several elements are equally minimum, the first element is picked.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .min();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3);
     /// assert_eq!(lookup[&1], 1);
     /// assert_eq!(lookup[&2], 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn min(self) -> HashMap<K, V>
-        where V: Ord,
+    where
+        V: Ord,
     {
         self.min_by(|_, v1, v2| V::cmp(v1, v2))
     }
 
     /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group
     /// with respect to the specified comparison function.
-    /// 
+    ///
     /// If several elements are equally minimum, the first element is picked.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .min_by(|_key, x, y| y.cmp(x));
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 12);
     /// assert_eq!(lookup[&1], 7);
     /// assert_eq!(lookup[&2], 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V>
-        where F: FnMut(&K, &V, &V) -> Ordering,
+    where
+        F: FnMut(&K, &V, &V) -> Ordering,
     {
-        self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
+        self.reduce(|acc, key, val| match compare(key, &acc, &val) {
             Ordering::Less | Ordering::Equal => acc,
-            Ordering::Greater => val
+            Ordering::Greater => val,
         })
     }
 
     /// Groups elements from the `GroupingMap` source by key and finds the element of each group
     /// that gives the minimum from the specified function.
-    /// 
+    ///
     /// If several elements are equally minimum, the first element is picked.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .min_by_key(|_key, &val| val % 4);
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 12);
     /// assert_eq!(lookup[&1], 4);
     /// assert_eq!(lookup[&2], 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
-        where F: FnMut(&K, &V) -> CK,
-              CK: Ord,
+    where
+        F: FnMut(&K, &V) -> CK,
+        CK: Ord,
     {
         self.min_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2)))
     }
 
     /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of
     /// each group.
-    /// 
+    ///
     /// If several elements are equally maximum, the last element is picked.
     /// If several elements are equally minimum, the first element is picked.
-    /// 
-    /// See [.minmax()](crate::Itertools::minmax) for the non-grouping version.
-    /// 
+    ///
+    /// See [`Itertools::minmax`](crate::Itertools::minmax) for the non-grouping version.
+    ///
     /// Differences from the non grouping version:
     /// - It never produces a `MinMaxResult::NoElements`
     /// - It doesn't have any speedup
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
     /// use itertools::MinMaxResult::{OneElement, MinMax};
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .minmax();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], MinMax(3, 12));
     /// assert_eq!(lookup[&1], MinMax(1, 7));
     /// assert_eq!(lookup[&2], OneElement(5));
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn minmax(self) -> HashMap<K, MinMaxResult<V>>
-        where V: Ord,
+    where
+        V: Ord,
     {
         self.minmax_by(|_, v1, v2| V::cmp(v1, v2))
     }
 
     /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of
     /// each group with respect to the specified comparison function.
-    /// 
+    ///
     /// If several elements are equally maximum, the last element is picked.
     /// If several elements are equally minimum, the first element is picked.
-    /// 
+    ///
     /// It has the same differences from the non-grouping version as `minmax`.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
     /// use itertools::MinMaxResult::{OneElement, MinMax};
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .minmax_by(|_key, x, y| y.cmp(x));
-    /// 
+    ///
     /// assert_eq!(lookup[&0], MinMax(12, 3));
     /// assert_eq!(lookup[&1], MinMax(7, 1));
     /// assert_eq!(lookup[&2], OneElement(5));
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>>
-        where F: FnMut(&K, &V, &V) -> Ordering,
+    where
+        F: FnMut(&K, &V, &V) -> Ordering,
     {
         self.aggregate(|acc, key, val| {
             Some(match acc {
@@ -455,81 +531,84 @@
 
     /// Groups elements from the `GroupingMap` source by key and find the elements of each group
     /// that gives the minimum and maximum from the specified function.
-    /// 
+    ///
     /// If several elements are equally maximum, the last element is picked.
     /// If several elements are equally minimum, the first element is picked.
-    /// 
+    ///
     /// It has the same differences from the non-grouping version as `minmax`.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
     /// use itertools::MinMaxResult::{OneElement, MinMax};
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .minmax_by_key(|_key, &val| val % 4);
-    /// 
+    ///
     /// assert_eq!(lookup[&0], MinMax(12, 3));
     /// assert_eq!(lookup[&1], MinMax(4, 7));
     /// assert_eq!(lookup[&2], OneElement(5));
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>>
-        where F: FnMut(&K, &V) -> CK,
-              CK: Ord,
+    where
+        F: FnMut(&K, &V) -> CK,
+        CK: Ord,
     {
         self.minmax_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2)))
     }
-    
+
     /// Groups elements from the `GroupingMap` source by key and sums them.
-    /// 
-    /// This is just a shorthand for `self.fold_first(|acc, _, val| acc + val)`.
+    ///
+    /// This is just a shorthand for `self.reduce(|acc, _, val| acc + val)`.
     /// It is more limited than `Iterator::sum` since it doesn't use the `Sum` trait.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the sum of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .sum();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3 + 9 + 12);
     /// assert_eq!(lookup[&1], 1 + 4 + 7);
     /// assert_eq!(lookup[&2], 5 + 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn sum(self) -> HashMap<K, V>
-        where V: Add<V, Output = V>
+    where
+        V: Add<V, Output = V>,
     {
-        self.fold_first(|acc, _, val| acc + val)
+        self.reduce(|acc, _, val| acc + val)
     }
 
     /// Groups elements from the `GroupingMap` source by key and multiply them.
-    /// 
-    /// This is just a shorthand for `self.fold_first(|acc, _, val| acc * val)`.
+    ///
+    /// This is just a shorthand for `self.reduce(|acc, _, val| acc * val)`.
     /// It is more limited than `Iterator::product` since it doesn't use the `Product` trait.
-    /// 
+    ///
     /// Returns a `HashMap` associating the key of each group with the product of that group's elements.
-    /// 
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .product();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3 * 9 * 12);
     /// assert_eq!(lookup[&1], 1 * 4 * 7);
     /// assert_eq!(lookup[&2], 5 * 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn product(self) -> HashMap<K, V>
-        where V: Mul<V, Output = V>,
+    where
+        V: Mul<V, Output = V>,
     {
-        self.fold_first(|acc, _, val| acc * val)
+        self.reduce(|acc, _, val| acc * val)
     }
 }
diff --git a/crates/itertools/src/impl_macros.rs b/crates/itertools/src/impl_macros.rs
index a029843..3db5ba0 100644
--- a/crates/itertools/src/impl_macros.rs
+++ b/crates/itertools/src/impl_macros.rs
@@ -1,4 +1,4 @@
-//! 
+//!
 //! Implementation's internal macros
 
 macro_rules! debug_fmt_fields {
@@ -27,3 +27,8 @@
 macro_rules! ignore_ident{
     ($id:ident, $($t:tt)*) => {$($t)*};
 }
+
+macro_rules! count_ident {
+    () => {0};
+    ($i0:ident $($i:ident)*) => {1 + count_ident!($($i)*)};
+}
diff --git a/crates/itertools/src/intersperse.rs b/crates/itertools/src/intersperse.rs
index 10a3a53..5f4f793 100644
--- a/crates/itertools/src/intersperse.rs
+++ b/crates/itertools/src/intersperse.rs
@@ -1,5 +1,5 @@
-use std::iter::{Fuse, FusedIterator};
 use super::size_hint;
+use std::iter::{Fuse, FusedIterator};
 
 pub trait IntersperseElement<Item> {
     fn generate(&mut self) -> Item;
@@ -26,12 +26,13 @@
 
 /// Create a new Intersperse iterator
 pub fn intersperse<I>(iter: I, elt: I::Item) -> Intersperse<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     intersperse_with(iter, IntersperseElementSimple(elt))
 }
 
-impl<Item, F: FnMut()->Item> IntersperseElement<Item> for F {
+impl<Item, F: FnMut() -> Item> IntersperseElement<Item> for F {
     fn generate(&mut self) -> Item {
         self()
     }
@@ -48,71 +49,94 @@
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Clone, Debug)]
 pub struct IntersperseWith<I, ElemF>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     element: ElemF,
     iter: Fuse<I>,
-    peek: Option<I::Item>,
+    /// `peek` is None while no item have been taken out of `iter` (at definition).
+    /// Then `peek` will alternatively be `Some(None)` and `Some(Some(item))`,
+    /// where `None` indicates it's time to generate from `element` (unless `iter` is empty).
+    peek: Option<Option<I::Item>>,
 }
 
 /// Create a new `IntersperseWith` iterator
 pub fn intersperse_with<I, ElemF>(iter: I, elt: ElemF) -> IntersperseWith<I, ElemF>
-    where I: Iterator,
+where
+    I: Iterator,
 {
-    let mut iter = iter.fuse();
     IntersperseWith {
-        peek: iter.next(),
-        iter,
+        peek: None,
+        iter: iter.fuse(),
         element: elt,
     }
 }
 
 impl<I, ElemF> Iterator for IntersperseWith<I, ElemF>
-    where I: Iterator,
-          ElemF: IntersperseElement<I::Item>
+where
+    I: Iterator,
+    ElemF: IntersperseElement<I::Item>,
 {
     type Item = I::Item;
     #[inline]
     fn next(&mut self) -> Option<Self::Item> {
-        if self.peek.is_some() {
-            self.peek.take()
-        } else {
-            self.peek = self.iter.next();
-            if self.peek.is_some() {
-                Some(self.element.generate())
-            } else {
-                None
+        let Self {
+            element,
+            iter,
+            peek,
+        } = self;
+        match peek {
+            Some(item @ Some(_)) => item.take(),
+            Some(None) => match iter.next() {
+                new @ Some(_) => {
+                    *peek = Some(new);
+                    Some(element.generate())
+                }
+                None => None,
+            },
+            None => {
+                *peek = Some(None);
+                iter.next()
             }
         }
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
-        // 2 * SH + { 1 or 0 }
-        let has_peek = self.peek.is_some() as usize;
-        let sh = self.iter.size_hint();
-        size_hint::add_scalar(size_hint::add(sh, sh), has_peek)
+        let mut sh = self.iter.size_hint();
+        sh = size_hint::add(sh, sh);
+        match self.peek {
+            Some(Some(_)) => size_hint::add_scalar(sh, 1),
+            Some(None) => sh,
+            None => size_hint::sub_scalar(sh, 1),
+        }
     }
 
-    fn fold<B, F>(mut self, init: B, mut f: F) -> B where
-        Self: Sized, F: FnMut(B, Self::Item) -> B,
+    fn fold<B, F>(self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
     {
+        let Self {
+            mut element,
+            mut iter,
+            peek,
+        } = self;
         let mut accum = init;
 
-        if let Some(x) = self.peek.take() {
+        if let Some(x) = peek.unwrap_or_else(|| iter.next()) {
             accum = f(accum, x);
         }
 
-        let element = &mut self.element;
-
-        self.iter.fold(accum,
-            |accum, x| {
-                let accum = f(accum, element.generate());
-                f(accum, x)
+        iter.fold(accum, |accum, x| {
+            let accum = f(accum, element.generate());
+            f(accum, x)
         })
     }
 }
 
 impl<I, ElemF> FusedIterator for IntersperseWith<I, ElemF>
-    where I: Iterator,
-          ElemF: IntersperseElement<I::Item>
-{}
+where
+    I: Iterator,
+    ElemF: IntersperseElement<I::Item>,
+{
+}
diff --git a/crates/itertools/src/iter_index.rs b/crates/itertools/src/iter_index.rs
new file mode 100644
index 0000000..aadaa72
--- /dev/null
+++ b/crates/itertools/src/iter_index.rs
@@ -0,0 +1,116 @@
+use core::iter::{Skip, Take};
+use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
+
+#[cfg(doc)]
+use crate::Itertools;
+
+mod private_iter_index {
+    use core::ops;
+
+    pub trait Sealed {}
+
+    impl Sealed for ops::Range<usize> {}
+    impl Sealed for ops::RangeInclusive<usize> {}
+    impl Sealed for ops::RangeTo<usize> {}
+    impl Sealed for ops::RangeToInclusive<usize> {}
+    impl Sealed for ops::RangeFrom<usize> {}
+    impl Sealed for ops::RangeFull {}
+}
+
+/// Used by [`Itertools::get`] to know which iterator
+/// to turn different ranges into.
+pub trait IteratorIndex<I>: private_iter_index::Sealed
+where
+    I: Iterator,
+{
+    /// The type returned for this type of index.
+    type Output: Iterator<Item = I::Item>;
+
+    /// Returns an adapted iterator for the current index.
+    ///
+    /// Prefer calling [`Itertools::get`] instead
+    /// of calling this directly.
+    fn index(self, from: I) -> Self::Output;
+}
+
+impl<I> IteratorIndex<I> for Range<usize>
+where
+    I: Iterator,
+{
+    type Output = Skip<Take<I>>;
+
+    fn index(self, iter: I) -> Self::Output {
+        iter.take(self.end).skip(self.start)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeInclusive<usize>
+where
+    I: Iterator,
+{
+    type Output = Take<Skip<I>>;
+
+    fn index(self, iter: I) -> Self::Output {
+        // end - start + 1 without overflowing if possible
+        let length = if *self.end() == usize::MAX {
+            assert_ne!(*self.start(), 0);
+            self.end() - self.start() + 1
+        } else {
+            (self.end() + 1).saturating_sub(*self.start())
+        };
+        iter.skip(*self.start()).take(length)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeTo<usize>
+where
+    I: Iterator,
+{
+    type Output = Take<I>;
+
+    fn index(self, iter: I) -> Self::Output {
+        iter.take(self.end)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeToInclusive<usize>
+where
+    I: Iterator,
+{
+    type Output = Take<I>;
+
+    fn index(self, iter: I) -> Self::Output {
+        assert_ne!(self.end, usize::MAX);
+        iter.take(self.end + 1)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeFrom<usize>
+where
+    I: Iterator,
+{
+    type Output = Skip<I>;
+
+    fn index(self, iter: I) -> Self::Output {
+        iter.skip(self.start)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeFull
+where
+    I: Iterator,
+{
+    type Output = I;
+
+    fn index(self, iter: I) -> Self::Output {
+        iter
+    }
+}
+
+pub fn get<I, R>(iter: I, index: R) -> R::Output
+where
+    I: IntoIterator,
+    R: IteratorIndex<I::IntoIter>,
+{
+    index.index(iter.into_iter())
+}
diff --git a/crates/itertools/src/k_smallest.rs b/crates/itertools/src/k_smallest.rs
index acaea59..7b2f62e 100644
--- a/crates/itertools/src/k_smallest.rs
+++ b/crates/itertools/src/k_smallest.rs
@@ -1,20 +1,98 @@
-use alloc::collections::BinaryHeap;
-use core::cmp::Ord;
+use alloc::vec::Vec;
+use core::cmp::Ordering;
 
-pub(crate) fn k_smallest<T: Ord, I: Iterator<Item = T>>(mut iter: I, k: usize) -> BinaryHeap<T> {
-    if k == 0 { return BinaryHeap::new(); }
+/// Consumes a given iterator, returning the minimum elements in **ascending** order.
+pub(crate) fn k_smallest_general<I, F>(iter: I, k: usize, mut comparator: F) -> Vec<I::Item>
+where
+    I: Iterator,
+    F: FnMut(&I::Item, &I::Item) -> Ordering,
+{
+    /// Sift the element currently at `origin` away from the root until it is properly ordered.
+    ///
+    /// This will leave **larger** elements closer to the root of the heap.
+    fn sift_down<T, F>(heap: &mut [T], is_less_than: &mut F, mut origin: usize)
+    where
+        F: FnMut(&T, &T) -> bool,
+    {
+        #[inline]
+        fn children_of(n: usize) -> (usize, usize) {
+            (2 * n + 1, 2 * n + 2)
+        }
 
-    let mut heap = iter.by_ref().take(k).collect::<BinaryHeap<_>>();
+        while origin < heap.len() {
+            let (left_idx, right_idx) = children_of(origin);
+            if left_idx >= heap.len() {
+                return;
+            }
 
-    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
-        //  `PeekMut` sifts-down unconditionally on Rust 1.46.0 and prior.
-        if *heap.peek().unwrap() > i {
-            *heap.peek_mut().unwrap() = i;
+            let replacement_idx =
+                if right_idx < heap.len() && is_less_than(&heap[left_idx], &heap[right_idx]) {
+                    right_idx
+                } else {
+                    left_idx
+                };
+
+            if is_less_than(&heap[origin], &heap[replacement_idx]) {
+                heap.swap(origin, replacement_idx);
+                origin = replacement_idx;
+            } else {
+                return;
+            }
+        }
+    }
+
+    if k == 0 {
+        iter.last();
+        return Vec::new();
+    }
+    if k == 1 {
+        return iter.min_by(comparator).into_iter().collect();
+    }
+    let mut iter = iter.fuse();
+    let mut storage: Vec<I::Item> = iter.by_ref().take(k).collect();
+
+    let mut is_less_than = move |a: &_, b: &_| comparator(a, b) == Ordering::Less;
+
+    // Rearrange the storage into a valid heap by reordering from the second-bottom-most layer up to the root.
+    // Slightly faster than ordering on each insert, but only by a factor of lg(k).
+    // The resulting heap has the **largest** item on top.
+    for i in (0..=(storage.len() / 2)).rev() {
+        sift_down(&mut storage, &mut is_less_than, i);
+    }
+
+    iter.for_each(|val| {
+        debug_assert_eq!(storage.len(), k);
+        if is_less_than(&val, &storage[0]) {
+            // Treating this as an push-and-pop saves having to write a sift-up implementation.
+            // https://en.wikipedia.org/wiki/Binary_heap#Insert_then_extract
+            storage[0] = val;
+            // We retain the smallest items we've seen so far, but ordered largest first so we can drop the largest efficiently.
+            sift_down(&mut storage, &mut is_less_than, 0);
         }
     });
 
-    heap
+    // Ultimately the items need to be in least-first, strict order, but the heap is currently largest-first.
+    // To achieve this, repeatedly,
+    // 1) "pop" the largest item off the heap into the tail slot of the underlying storage,
+    // 2) shrink the logical size of the heap by 1,
+    // 3) restore the heap property over the remaining items.
+    let mut heap = &mut storage[..];
+    while heap.len() > 1 {
+        let last_idx = heap.len() - 1;
+        heap.swap(0, last_idx);
+        // Sifting over a truncated slice means that the sifting will not disturb already popped elements.
+        heap = &mut heap[..last_idx];
+        sift_down(heap, &mut is_less_than, 0);
+    }
+
+    storage
+}
+
+#[inline]
+pub(crate) fn key_to_cmp<T, K, F>(mut key: F) -> impl FnMut(&T, &T) -> Ordering
+where
+    F: FnMut(&T) -> K,
+    K: Ord,
+{
+    move |a, b| key(a).cmp(&key(b))
 }
diff --git a/crates/itertools/src/kmerge_impl.rs b/crates/itertools/src/kmerge_impl.rs
index 509d5fc..0be3840 100644
--- a/crates/itertools/src/kmerge_impl.rs
+++ b/crates/itertools/src/kmerge_impl.rs
@@ -2,9 +2,9 @@
 use crate::Itertools;
 
 use alloc::vec::Vec;
+use std::fmt;
 use std::iter::FusedIterator;
 use std::mem::replace;
-use std::fmt;
 
 /// Head element and Tail iterator pair
 ///
@@ -15,24 +15,21 @@
 /// `KMerge` into a min-heap.
 #[derive(Debug)]
 struct HeadTail<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     head: I::Item,
     tail: I,
 }
 
 impl<I> HeadTail<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     /// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the `Iterator` is empty.
-    fn new(mut it: I) -> Option<HeadTail<I>> {
+    fn new(mut it: I) -> Option<Self> {
         let head = it.next();
-        head.map(|h| {
-            HeadTail {
-                head: h,
-                tail: it,
-            }
-        })
+        head.map(|h| Self { head: h, tail: it })
     }
 
     /// Get the next element and update `head`, returning the old head in `Some`.
@@ -53,15 +50,17 @@
 }
 
 impl<I> Clone for HeadTail<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
 {
     clone_fields!(head, tail);
 }
 
 /// Make `data` a heap (min-heap w.r.t the sorting).
 fn heapify<T, S>(data: &mut [T], mut less_than: S)
-    where S: FnMut(&T, &T) -> bool
+where
+    S: FnMut(&T, &T) -> bool,
 {
     for i in (0..data.len() / 2).rev() {
         sift_down(data, i, &mut less_than);
@@ -70,7 +69,8 @@
 
 /// Sift down element at `index` (`heap` is a min-heap wrt the ordering)
 fn sift_down<T, S>(heap: &mut [T], index: usize, mut less_than: S)
-    where S: FnMut(&T, &T) -> bool
+where
+    S: FnMut(&T, &T) -> bool,
 {
     debug_assert!(index <= heap.len());
     let mut pos = index;
@@ -81,7 +81,7 @@
     while child + 1 < heap.len() {
         // pick the smaller of the two children
         // use arithmetic to avoid an unpredictable branch
-        child += less_than(&heap[child+1], &heap[child]) as usize;
+        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]) {
@@ -119,7 +119,7 @@
     }
 }
 
-impl<T, F: FnMut(&T, &T)->bool> KMergePredicate<T> for F {
+impl<T, F: FnMut(&T, &T) -> bool> KMergePredicate<T> for F {
     fn kmerge_pred(&mut self, a: &T, b: &T) -> bool {
         self(a, b)
     }
@@ -138,9 +138,10 @@
 /// }
 /// ```
 pub fn kmerge<I>(iterable: I) -> KMerge<<I::Item as IntoIterator>::IntoIter>
-    where I: IntoIterator,
-          I::Item: IntoIterator,
-          <<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd
+where
+    I: IntoIterator,
+    I::Item: IntoIterator,
+    <<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd,
 {
     kmerge_by(iterable, KMergeByLt)
 }
@@ -152,17 +153,19 @@
 ///
 /// See [`.kmerge_by()`](crate::Itertools::kmerge_by) for more
 /// information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "this iterator adaptor is not lazy but does nearly nothing unless consumed"]
 pub struct KMergeBy<I, F>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     heap: Vec<HeadTail<I>>,
     less_than: F,
 }
 
 impl<I, F> fmt::Debug for KMergeBy<I, F>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
 {
     debug_fmt_fields!(KMergeBy, heap);
 }
@@ -170,11 +173,14 @@
 /// Create an iterator that merges elements of the contained iterators.
 ///
 /// [`IntoIterator`] enabled version of [`Itertools::kmerge_by`].
-pub fn kmerge_by<I, F>(iterable: I, mut less_than: F)
-    -> KMergeBy<<I::Item as IntoIterator>::IntoIter, F>
-    where I: IntoIterator,
-          I::Item: IntoIterator,
-          F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>,
+pub fn kmerge_by<I, F>(
+    iterable: I,
+    mut less_than: F,
+) -> KMergeBy<<I::Item as IntoIterator>::IntoIter, F>
+where
+    I: IntoIterator,
+    I::Item: IntoIterator,
+    F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>,
 {
     let iter = iterable.into_iter();
     let (lower, _) = iter.size_hint();
@@ -185,16 +191,18 @@
 }
 
 impl<I, F> Clone for KMergeBy<I, F>
-    where I: Iterator + Clone,
-          I::Item: Clone,
-          F: Clone,
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+    F: Clone,
 {
     clone_fields!(heap, less_than);
 }
 
 impl<I, F> Iterator for KMergeBy<I, F>
-    where I: Iterator,
-          F: KMergePredicate<I::Item>
+where
+    I: Iterator,
+    F: KMergePredicate<I::Item>,
 {
     type Item = I::Item;
 
@@ -208,20 +216,25 @@
             self.heap.swap_remove(0).head
         };
         let less_than = &mut self.less_than;
-        sift_down(&mut self.heap, 0, |a, b| less_than.kmerge_pred(&a.head, &b.head));
+        sift_down(&mut self.heap, 0, |a, b| {
+            less_than.kmerge_pred(&a.head, &b.head)
+        });
         Some(result)
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
         #[allow(deprecated)] //TODO: once msrv hits 1.51. replace `fold1` with `reduce`
-        self.heap.iter()
-                 .map(|i| i.size_hint())
-                 .fold1(size_hint::add)
-                 .unwrap_or((0, Some(0)))
+        self.heap
+            .iter()
+            .map(|i| i.size_hint())
+            .fold1(size_hint::add)
+            .unwrap_or((0, Some(0)))
     }
 }
 
 impl<I, F> FusedIterator for KMergeBy<I, F>
-    where I: Iterator,
-          F: KMergePredicate<I::Item>
-{}
+where
+    I: Iterator,
+    F: KMergePredicate<I::Item>,
+{
+}
diff --git a/crates/itertools/src/lazy_buffer.rs b/crates/itertools/src/lazy_buffer.rs
index ca24062..fefcff8 100644
--- a/crates/itertools/src/lazy_buffer.rs
+++ b/crates/itertools/src/lazy_buffer.rs
@@ -1,10 +1,12 @@
-use std::ops::Index;
 use alloc::vec::Vec;
+use std::iter::Fuse;
+use std::ops::Index;
+
+use crate::size_hint::{self, SizeHint};
 
 #[derive(Debug, Clone)]
 pub struct LazyBuffer<I: Iterator> {
-    pub it: I,
-    done: bool,
+    it: Fuse<I>,
     buffer: Vec<I::Item>,
 }
 
@@ -12,10 +14,9 @@
 where
     I: Iterator,
 {
-    pub fn new(it: I) -> LazyBuffer<I> {
-        LazyBuffer {
-            it,
-            done: false,
+    pub fn new(it: I) -> Self {
+        Self {
+            it: it.fuse(),
             buffer: Vec::new(),
         }
     }
@@ -24,36 +25,47 @@
         self.buffer.len()
     }
 
+    pub fn size_hint(&self) -> SizeHint {
+        size_hint::add_scalar(self.it.size_hint(), self.len())
+    }
+
+    pub fn count(self) -> usize {
+        self.len() + self.it.count()
+    }
+
     pub fn get_next(&mut self) -> bool {
-        if self.done {
-            return false;
-        }
         if let Some(x) = self.it.next() {
             self.buffer.push(x);
             true
         } else {
-            self.done = true;
             false
         }
     }
 
     pub fn prefill(&mut self, len: usize) {
         let buffer_len = self.buffer.len();
-
-        if !self.done && len > buffer_len {
+        if len > buffer_len {
             let delta = len - buffer_len;
-
             self.buffer.extend(self.it.by_ref().take(delta));
-            self.done = self.buffer.len() < len;
         }
     }
 }
 
+impl<I> LazyBuffer<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    pub fn get_at(&self, indices: &[usize]) -> Vec<I::Item> {
+        indices.iter().map(|i| self.buffer[*i].clone()).collect()
+    }
+}
+
 impl<I, J> Index<J> for LazyBuffer<I>
 where
     I: Iterator,
     I::Item: Sized,
-    Vec<I::Item>: Index<J>
+    Vec<I::Item>: Index<J>,
 {
     type Output = <Vec<I::Item> as Index<J>>::Output;
 
diff --git a/crates/itertools/src/lib.rs b/crates/itertools/src/lib.rs
index f919688..f4de79c 100644
--- a/crates/itertools/src/lib.rs
+++ b/crates/itertools/src/lib.rs
@@ -1,5 +1,5 @@
-#![warn(missing_docs)]
-#![crate_name="itertools"]
+#![warn(missing_docs, clippy::default_numeric_fallback)]
+#![crate_name = "itertools"]
 #![cfg_attr(not(feature = "use_std"), no_std)]
 
 //! Extra iterator adaptors, functions and macros.
@@ -37,13 +37,16 @@
 //! - `use_std`
 //!   - Enabled by default.
 //!   - Disable to compile itertools using `#![no_std]`. This disables
-//!     any items that depend on collections (like `group_by`, `unique`,
+//!     any item that depend on allocations (see the `use_alloc` feature)
+//!     and hash maps (like `unique`, `counts`, `into_grouping_map` and more).
+//! - `use_alloc`
+//!   - Enabled by default.
+//!   - Enables any item that depend on allocations (like `chunk_by`,
 //!     `kmerge`, `join` and many more).
 //!
 //! ## Rust Version
 //!
-//! This version of itertools requires Rust 1.32 or later.
-#![doc(html_root_url="https://docs.rs/itertools/0.8/")]
+//! This version of itertools requires Rust 1.43.1 or later.
 
 #[cfg(not(feature = "use_std"))]
 extern crate core as std;
@@ -52,28 +55,26 @@
 extern crate alloc;
 
 #[cfg(feature = "use_alloc")]
-use alloc::{
-    string::String,
-    vec::Vec,
-};
+use alloc::{collections::VecDeque, string::String, vec::Vec};
 
 pub use either::Either;
 
 use core::borrow::Borrow;
+use std::cmp::Ordering;
 #[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;
+use std::fmt;
 #[cfg(feature = "use_alloc")]
 use std::fmt::Write;
+#[cfg(feature = "use_std")]
+use std::hash::Hash;
+use std::iter::{once, IntoIterator};
+#[cfg(feature = "use_alloc")]
+type VecDequeIntoIter<T> = alloc::collections::vec_deque::IntoIter<T>;
 #[cfg(feature = "use_alloc")]
 type VecIntoIter<T> = alloc::vec::IntoIter<T>;
-#[cfg(feature = "use_alloc")]
 use std::iter::FromIterator;
 
 #[macro_use]
@@ -85,72 +86,56 @@
 
 /// The concrete iterator types.
 pub mod structs {
-    pub use crate::adaptors::{
-        Dedup,
-        DedupBy,
-        DedupWithCount,
-        DedupByWithCount,
-        Interleave,
-        InterleaveShortest,
-        FilterMapOk,
-        FilterOk,
-        Product,
-        PutBack,
-        Batching,
-        MapInto,
-        MapOk,
-        Merge,
-        MergeBy,
-        TakeWhileRef,
-        WhileSome,
-        Coalesce,
-        TupleCombinations,
-        Positions,
-        Update,
-    };
-    #[allow(deprecated)]
-    pub use crate::adaptors::{MapResults, Step};
     #[cfg(feature = "use_alloc")]
     pub use crate::adaptors::MultiProduct;
+    pub use crate::adaptors::{
+        Batching, Coalesce, Dedup, DedupBy, DedupByWithCount, DedupWithCount, FilterMapOk,
+        FilterOk, Interleave, InterleaveShortest, MapInto, MapOk, Positions, Product, PutBack,
+        TakeWhileRef, TupleCombinations, Update, WhileSome,
+    };
     #[cfg(feature = "use_alloc")]
     pub use crate::combinations::Combinations;
     #[cfg(feature = "use_alloc")]
     pub use crate::combinations_with_replacement::CombinationsWithReplacement;
     pub use crate::cons_tuples_impl::ConsTuples;
+    #[cfg(feature = "use_std")]
+    pub use crate::duplicates_impl::{Duplicates, DuplicatesBy};
     pub use crate::exactly_one_err::ExactlyOneError;
-    pub use crate::format::{Format, FormatWith};
     pub use crate::flatten_ok::FlattenOk;
+    pub use crate::format::{Format, FormatWith};
+    #[allow(deprecated)]
+    #[cfg(feature = "use_alloc")]
+    pub use crate::groupbylazy::GroupBy;
+    #[cfg(feature = "use_alloc")]
+    pub use crate::groupbylazy::{Chunk, ChunkBy, Chunks, Group, Groups, IntoChunks};
     #[cfg(feature = "use_std")]
     pub use crate::grouping_map::{GroupingMap, GroupingMapBy};
-    #[cfg(feature = "use_alloc")]
-    pub use crate::groupbylazy::{IntoChunks, Chunk, Chunks, GroupBy, Group, Groups};
     pub use crate::intersperse::{Intersperse, IntersperseWith};
     #[cfg(feature = "use_alloc")]
     pub use crate::kmerge_impl::{KMerge, KMergeBy};
-    pub use crate::merge_join::MergeJoinBy;
+    pub use crate::merge_join::{Merge, MergeBy, MergeJoinBy};
     #[cfg(feature = "use_alloc")]
     pub use crate::multipeek_impl::MultiPeek;
+    pub use crate::pad_tail::PadUsing;
     #[cfg(feature = "use_alloc")]
     pub use crate::peek_nth::PeekNth;
-    pub use crate::pad_tail::PadUsing;
     pub use crate::peeking_take_while::PeekingTakeWhile;
     #[cfg(feature = "use_alloc")]
     pub use crate::permutations::Permutations;
-    pub use crate::process_results_impl::ProcessResults;
     #[cfg(feature = "use_alloc")]
     pub use crate::powerset::Powerset;
+    pub use crate::process_results_impl::ProcessResults;
     #[cfg(feature = "use_alloc")]
     pub use crate::put_back_n_impl::PutBackN;
     #[cfg(feature = "use_alloc")]
     pub use crate::rciter_impl::RcIter;
     pub use crate::repeatn::RepeatN;
     #[allow(deprecated)]
-    pub use crate::sources::{RepeatCall, Unfold, Iterate};
+    pub use crate::sources::{Iterate, Unfold};
+    pub use crate::take_while_inclusive::TakeWhileInclusive;
     #[cfg(feature = "use_alloc")]
     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};
+    pub use crate::tuple_impl::{CircularTupleWindows, TupleBuffer, TupleWindows, Tuples};
     #[cfg(feature = "use_std")]
     pub use crate::unique_impl::{Unique, UniqueBy};
     pub use crate::with_position::WithPosition;
@@ -161,25 +146,26 @@
 
 /// Traits helpful for using certain `Itertools` methods in generic contexts.
 pub mod traits {
+    pub use crate::iter_index::IteratorIndex;
     pub use crate::tuple_impl::HomogeneousTuple;
 }
 
-#[allow(deprecated)]
-pub use crate::structs::*;
 pub use crate::concat_impl::concat;
 pub use crate::cons_tuples_impl::cons_tuples;
 pub use crate::diff::diff_with;
 pub use crate::diff::Diff;
 #[cfg(feature = "use_alloc")]
-pub use crate::kmerge_impl::{kmerge_by};
+pub use crate::kmerge_impl::kmerge_by;
 pub use crate::minmax::MinMaxResult;
 pub use crate::peeking_take_while::PeekingNext;
 pub use crate::process_results_impl::process_results;
 pub use crate::repeatn::repeat_n;
 #[allow(deprecated)]
-pub use crate::sources::{repeat_call, unfold, iterate};
-pub use crate::with_position::Position;
+pub use crate::sources::{iterate, unfold};
+#[allow(deprecated)]
+pub use crate::structs::*;
 pub use crate::unziptuple::{multiunzip, MultiUnzip};
+pub use crate::with_position::Position;
 pub use crate::ziptuple::multizip;
 mod adaptors;
 mod either_or_both;
@@ -188,25 +174,28 @@
 pub mod free;
 #[doc(inline)]
 pub use crate::free::*;
-mod concat_impl;
-mod cons_tuples_impl;
 #[cfg(feature = "use_alloc")]
 mod combinations;
 #[cfg(feature = "use_alloc")]
 mod combinations_with_replacement;
-mod exactly_one_err;
+mod concat_impl;
+mod cons_tuples_impl;
 mod diff;
-mod flatten_ok;
 #[cfg(feature = "use_std")]
+mod duplicates_impl;
+mod exactly_one_err;
+#[cfg(feature = "use_alloc")]
 mod extrema_set;
+mod flatten_ok;
 mod format;
-#[cfg(feature = "use_std")]
-mod grouping_map;
 #[cfg(feature = "use_alloc")]
 mod group_map;
 #[cfg(feature = "use_alloc")]
 mod groupbylazy;
+#[cfg(feature = "use_std")]
+mod grouping_map;
 mod intersperse;
+mod iter_index;
 #[cfg(feature = "use_alloc")]
 mod k_smallest;
 #[cfg(feature = "use_alloc")]
@@ -233,12 +222,11 @@
 mod repeatn;
 mod size_hint;
 mod sources;
+mod take_while_inclusive;
 #[cfg(feature = "use_alloc")]
 mod tee;
 mod tuple_impl;
 #[cfg(feature = "use_std")]
-mod duplicates_impl;
-#[cfg(feature = "use_std")]
 mod unique_impl;
 mod unziptuple;
 mod with_position;
@@ -270,13 +258,19 @@
     (@flatten $I:expr, $J:expr, $($K:expr,)*) => (
         $crate::iproduct!(@flatten $crate::cons_tuples($crate::iproduct!($I, $J)), $($K,)*)
     );
-    ($I:expr) => (
-        $crate::__std_iter::IntoIterator::into_iter($I)
+    () => (
+        $crate::__std_iter::once(())
     );
-    ($I:expr, $J:expr) => (
-        $crate::Itertools::cartesian_product($crate::iproduct!($I), $crate::iproduct!($J))
+    ($I:expr $(,)?) => (
+        $crate::__std_iter::IntoIterator::into_iter($I).map(|elt| (elt,))
     );
-    ($I:expr, $J:expr, $($K:expr),+) => (
+    ($I:expr, $J:expr $(,)?) => (
+        $crate::Itertools::cartesian_product(
+            $crate::__std_iter::IntoIterator::into_iter($I),
+            $crate::__std_iter::IntoIterator::into_iter($J),
+        )
+    );
+    ($I:expr, $J:expr, $($K:expr),+ $(,)?) => (
         $crate::iproduct!(@flatten $crate::iproduct!($I, $J), $($K,)+)
     );
 }
@@ -428,7 +422,7 @@
 /// return a regular value of some other kind.
 /// [`.next_tuple()`](Itertools::next_tuple) is an example and the first regular
 /// method in the list.
-pub trait Itertools : Iterator {
+pub trait Itertools: Iterator {
     // adaptors
 
     /// Alternate elements from two iterators until both have run out.
@@ -444,8 +438,9 @@
     /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3, 4, 5, 6]);
     /// ```
     fn interleave<J>(self, other: J) -> Interleave<Self, J::IntoIter>
-        where J: IntoIterator<Item = Self::Item>,
-              Self: Sized
+    where
+        J: IntoIterator<Item = Self::Item>,
+        Self: Sized,
     {
         interleave(self, other)
     }
@@ -462,8 +457,9 @@
     /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3]);
     /// ```
     fn interleave_shortest<J>(self, other: J) -> InterleaveShortest<Self, J::IntoIter>
-        where J: IntoIterator<Item = Self::Item>,
-              Self: Sized
+    where
+        J: IntoIterator<Item = Self::Item>,
+        Self: Sized,
     {
         adaptors::interleave_shortest(self, other.into_iter())
     }
@@ -481,8 +477,9 @@
     /// itertools::assert_equal((0..3).intersperse(8), vec![0, 8, 1, 8, 2]);
     /// ```
     fn intersperse(self, element: Self::Item) -> Intersperse<Self>
-        where Self: Sized,
-              Self::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         intersperse::intersperse(self, element)
     }
@@ -502,12 +499,63 @@
     /// assert_eq!(i, 8);
     /// ```
     fn intersperse_with<F>(self, element: F) -> IntersperseWith<Self, F>
-        where Self: Sized,
-        F: FnMut() -> Self::Item
+    where
+        Self: Sized,
+        F: FnMut() -> Self::Item,
     {
         intersperse::intersperse_with(self, element)
     }
 
+    /// Returns an iterator over a subsection of the iterator.
+    ///
+    /// Works similarly to [`slice::get`](https://doc.rust-lang.org/std/primitive.slice.html#method.get).
+    ///
+    /// **Panics** for ranges `..=usize::MAX` and `0..=usize::MAX`.
+    ///
+    /// It's a generalisation of [`Iterator::take`] and [`Iterator::skip`],
+    /// and uses these under the hood.
+    /// Therefore, the resulting iterator is:
+    /// - [`ExactSizeIterator`] if the adapted iterator is [`ExactSizeIterator`].
+    /// - [`DoubleEndedIterator`] if the adapted iterator is [`DoubleEndedIterator`] and [`ExactSizeIterator`].
+    ///
+    /// # Unspecified Behavior
+    /// The result of indexing with an exhausted [`core::ops::RangeInclusive`] is unspecified.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let vec = vec![3, 1, 4, 1, 5];
+    ///
+    /// let mut range: Vec<_> =
+    ///         vec.iter().get(1..=3).copied().collect();
+    /// assert_eq!(&range, &[1, 4, 1]);
+    ///
+    /// // It works with other types of ranges, too
+    /// range = vec.iter().get(..2).copied().collect();
+    /// assert_eq!(&range, &[3, 1]);
+    ///
+    /// range = vec.iter().get(0..1).copied().collect();
+    /// assert_eq!(&range, &[3]);
+    ///
+    /// range = vec.iter().get(2..).copied().collect();
+    /// assert_eq!(&range, &[4, 1, 5]);
+    ///
+    /// range = vec.iter().get(..=2).copied().collect();
+    /// assert_eq!(&range, &[3, 1, 4]);
+    ///
+    /// range = vec.iter().get(..).copied().collect();
+    /// assert_eq!(range, vec);
+    /// ```
+    fn get<R>(self, index: R) -> R::Output
+    where
+        Self: Sized,
+        R: traits::IteratorIndex<Self>,
+    {
+        iter_index::get(self, index)
+    }
+
     /// Create an iterator which iterates over both this and the specified
     /// iterator simultaneously, yielding pairs of two optional elements.
     ///
@@ -536,8 +584,9 @@
     /// ```
     #[inline]
     fn zip_longest<J>(self, other: J) -> ZipLongest<Self, J::IntoIter>
-        where J: IntoIterator,
-              Self: Sized
+    where
+        J: IntoIterator,
+        Self: Sized,
     {
         zip_longest::zip_longest(self, other.into_iter())
     }
@@ -549,8 +598,9 @@
     /// lengths.
     #[inline]
     fn zip_eq<J>(self, other: J) -> ZipEq<Self, J::IntoIter>
-        where J: IntoIterator,
-              Self: Sized
+    where
+        J: IntoIterator,
+        Self: Sized,
     {
         zip_eq(self, other)
     }
@@ -579,8 +629,9 @@
     /// ```
     ///
     fn batching<B, F>(self, f: F) -> Batching<Self, F>
-        where F: FnMut(&mut Self) -> Option<B>,
-              Self: Sized
+    where
+        F: FnMut(&mut Self) -> Option<B>,
+        Self: Sized,
     {
         adaptors::batching(self, f)
     }
@@ -589,10 +640,10 @@
     /// Consecutive elements that map to the same key (“runs”), are assigned
     /// to the same group.
     ///
-    /// `GroupBy` is the storage for the lazy grouping operation.
+    /// `ChunkBy` is the storage for the lazy grouping operation.
     ///
     /// If the groups are consumed in order, or if each group's iterator is
-    /// dropped without keeping it around, then `GroupBy` uses no
+    /// dropped without keeping it around, then `ChunkBy` uses no
     /// allocations.  It needs allocations only if several group iterators
     /// are alive at the same time.
     ///
@@ -607,34 +658,47 @@
     /// ```
     /// use itertools::Itertools;
     ///
-    /// // group data into runs of larger than zero or not.
+    /// // chunk data into runs of larger than zero or not.
     /// let data = vec![1, 3, -2, -2, 1, 0, 1, 2];
-    /// // groups:     |---->|------>|--------->|
+    /// // chunks:     |---->|------>|--------->|
     ///
-    /// // Note: The `&` is significant here, `GroupBy` is iterable
+    /// // Note: The `&` is significant here, `ChunkBy` is iterable
     /// // only by reference. You can also call `.into_iter()` explicitly.
     /// let mut data_grouped = Vec::new();
-    /// for (key, group) in &data.into_iter().group_by(|elt| *elt >= 0) {
-    ///     data_grouped.push((key, group.collect()));
+    /// for (key, chunk) in &data.into_iter().chunk_by(|elt| *elt >= 0) {
+    ///     data_grouped.push((key, chunk.collect()));
     /// }
     /// assert_eq!(data_grouped, vec![(true, vec![1, 3]), (false, vec![-2, -2]), (true, vec![1, 0, 1, 2])]);
     /// ```
     #[cfg(feature = "use_alloc")]
-    fn group_by<K, F>(self, key: F) -> GroupBy<K, Self, F>
-        where Self: Sized,
-              F: FnMut(&Self::Item) -> K,
-              K: PartialEq,
+    fn chunk_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: PartialEq,
     {
         groupbylazy::new(self, key)
     }
 
+    /// See [`.chunk_by()`](Itertools::chunk_by).
+    #[deprecated(note = "Use .chunk_by() instead", since = "0.13.0")]
+    #[cfg(feature = "use_alloc")]
+    fn group_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: PartialEq,
+    {
+        self.chunk_by(key)
+    }
+
     /// Return an *iterable* that can chunk the iterator.
     ///
     /// Yield subiterators (chunks) that each yield a fixed number elements,
     /// determined by `size`. The last chunk will be shorter if there aren't
     /// enough elements.
     ///
-    /// `IntoChunks` is based on `GroupBy`: it is iterable (implements
+    /// `IntoChunks` is based on `ChunkBy`: it is iterable (implements
     /// `IntoIterator`, **not** `Iterator`), and it only buffers if several
     /// chunk iterators are alive at the same time.
     ///
@@ -657,7 +721,8 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn chunks(self, size: usize) -> IntoChunks<Self>
-        where Self: Sized,
+    where
+        Self: Sized,
     {
         assert!(size != 0);
         groupbylazy::new_chunks(self, size)
@@ -697,9 +762,10 @@
     /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4)]);
     /// ```
     fn tuple_windows<T>(self) -> TupleWindows<Self, T>
-        where Self: Sized + Iterator<Item = T::Item>,
-              T: traits::HomogeneousTuple,
-              T::Item: Clone
+    where
+        Self: Sized + Iterator<Item = T::Item>,
+        T: traits::HomogeneousTuple,
+        T::Item: Clone,
     {
         tuple_impl::tuple_windows(self)
     }
@@ -732,9 +798,10 @@
     /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4), (3, 4, 1), (4, 1, 2)]);
     /// ```
     fn circular_tuple_windows<T>(self) -> CircularTupleWindows<Self, T>
-        where Self: Sized + Clone + Iterator<Item = T::Item> + ExactSizeIterator,
-              T: tuple_impl::TupleCollect + Clone,
-              T::Item: Clone
+    where
+        Self: Sized + Clone + Iterator<Item = T::Item> + ExactSizeIterator,
+        T: tuple_impl::TupleCollect + Clone,
+        T::Item: Clone,
     {
         tuple_impl::circular_tuple_windows(self)
     }
@@ -770,8 +837,9 @@
     ///
     /// See also [`Tuples::into_buffer`].
     fn tuples<T>(self) -> Tuples<Self, T>
-        where Self: Sized + Iterator<Item = T::Item>,
-              T: traits::HomogeneousTuple
+    where
+        Self: Sized + Iterator<Item = T::Item>,
+        T: traits::HomogeneousTuple,
     {
         tuple_impl::tuples(self)
     }
@@ -795,36 +863,13 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn tee(self) -> (Tee<Self>, Tee<Self>)
-        where Self: Sized,
-              Self::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         tee::new(self)
     }
 
-    /// Return an iterator adaptor that steps `n` elements in the base iterator
-    /// for each iteration.
-    ///
-    /// The iterator steps by yielding the next element from the base iterator,
-    /// then skipping forward `n - 1` elements.
-    ///
-    /// Iterator element type is `Self::Item`.
-    ///
-    /// **Panics** if the step is 0.
-    ///
-    /// ```
-    /// use itertools::Itertools;
-    ///
-    /// let it = (0..8).step(3);
-    /// itertools::assert_equal(it, vec![0, 3, 6]);
-    /// ```
-    #[deprecated(note="Use std .step_by() instead", since="0.8.0")]
-    #[allow(deprecated)]
-    fn step(self, n: usize) -> Step<Self>
-        where Self: Sized
-    {
-        adaptors::step(self, n)
-    }
-
     /// Convert each item of the iterator using the [`Into`] trait.
     ///
     /// ```rust
@@ -833,21 +878,13 @@
     /// (1i32..42i32).map_into::<f64>().collect_vec();
     /// ```
     fn map_into<R>(self) -> MapInto<Self, R>
-        where Self: Sized,
-              Self::Item: Into<R>,
+    where
+        Self: Sized,
+        Self::Item: Into<R>,
     {
         adaptors::map_into(self)
     }
 
-    /// 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,
-              F: FnMut(T) -> U,
-    {
-        self.map_ok(f)
-    }
-
     /// Return an iterator adaptor that applies the provided closure
     /// to every `Result::Ok` value. `Result::Err` values are
     /// unchanged.
@@ -860,8 +897,9 @@
     /// itertools::assert_equal(it, vec![Ok(42), Err(false), Ok(12)]);
     /// ```
     fn map_ok<F, T, U, E>(self, f: F) -> MapOk<Self, F>
-        where Self: Iterator<Item = Result<T, E>> + Sized,
-              F: FnMut(T) -> U,
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        F: FnMut(T) -> U,
     {
         adaptors::map_ok(self, f)
     }
@@ -878,8 +916,9 @@
     /// itertools::assert_equal(it, vec![Ok(22), Err(false)]);
     /// ```
     fn filter_ok<F, T, E>(self, f: F) -> FilterOk<Self, F>
-        where Self: Iterator<Item = Result<T, E>> + Sized,
-              F: FnMut(&T) -> bool,
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        F: FnMut(&T) -> bool,
     {
         adaptors::filter_ok(self, f)
     }
@@ -896,15 +935,16 @@
     /// itertools::assert_equal(it, vec![Ok(44), Err(false)]);
     /// ```
     fn filter_map_ok<F, T, U, E>(self, f: F) -> FilterMapOk<Self, F>
-        where Self: Iterator<Item = Result<T, E>> + Sized,
-              F: FnMut(T) -> Option<U>,
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        F: FnMut(T) -> Option<U>,
     {
         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 propagate it upwards, but the `Result::Ok` case needs to be flattened.
     ///
@@ -914,18 +954,57 @@
     /// 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 propagate 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
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        T: IntoIterator,
     {
         flatten_ok::flatten_ok(self)
     }
 
+    /// “Lift” a function of the values of the current iterator so as to process
+    /// an iterator of `Result` values instead.
+    ///
+    /// `processor` is a closure that receives an adapted version of the iterator
+    /// as the only argument — the adapted iterator produces elements of type `T`,
+    /// as long as the original iterator produces `Ok` values.
+    ///
+    /// If the original iterable produces an error at any point, the adapted
+    /// iterator ends and it will return the error iself.
+    ///
+    /// Otherwise, the return value from the closure is returned wrapped
+    /// inside `Ok`.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// type Item = Result<i32, &'static str>;
+    ///
+    /// let first_values: Vec<Item> = vec![Ok(1), Ok(0), Ok(3)];
+    /// let second_values: Vec<Item> = vec![Ok(2), Ok(1), Err("overflow")];
+    ///
+    /// // “Lift” the iterator .max() method to work on the Ok-values.
+    /// let first_max = first_values.into_iter().process_results(|iter| iter.max().unwrap_or(0));
+    /// let second_max = second_values.into_iter().process_results(|iter| iter.max().unwrap_or(0));
+    ///
+    /// assert_eq!(first_max, Ok(3));
+    /// assert!(second_max.is_err());
+    /// ```
+    fn process_results<F, T, E, R>(self, processor: F) -> Result<R, E>
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        F: FnOnce(ProcessResults<Self, E>) -> R,
+    {
+        process_results(self, processor)
+    }
+
     /// Return an iterator adaptor that merges the two base iterators in
     /// ascending order.  If both base iterators are sorted (ascending), the
     /// result is sorted.
@@ -935,15 +1014,16 @@
     /// ```
     /// use itertools::Itertools;
     ///
-    /// let a = (0..11).step(3);
-    /// let b = (0..11).step(5);
+    /// let a = (0..11).step_by(3);
+    /// let b = (0..11).step_by(5);
     /// let it = a.merge(b);
     /// itertools::assert_equal(it, vec![0, 0, 3, 5, 6, 9, 10]);
     /// ```
     fn merge<J>(self, other: J) -> Merge<Self, J::IntoIter>
-        where Self: Sized,
-              Self::Item: PartialOrd,
-              J: IntoIterator<Item = Self::Item>
+    where
+        Self: Sized,
+        Self::Item: PartialOrd,
+        J: IntoIterator<Item = Self::Item>,
     {
         merge(self, other)
     }
@@ -965,17 +1045,21 @@
     /// ```
 
     fn merge_by<J, F>(self, other: J, is_first: F) -> MergeBy<Self, J::IntoIter, F>
-        where Self: Sized,
-              J: IntoIterator<Item = Self::Item>,
-              F: FnMut(&Self::Item, &Self::Item) -> bool
+    where
+        Self: Sized,
+        J: IntoIterator<Item = Self::Item>,
+        F: FnMut(&Self::Item, &Self::Item) -> bool,
     {
-        adaptors::merge_by_new(self, other.into_iter(), is_first)
+        merge_join::merge_by_new(self, other, is_first)
     }
 
     /// Create an iterator that merges items from both this and the specified
     /// iterator in ascending order.
     ///
-    /// It chooses whether to pair elements based on the `Ordering` returned by the
+    /// The function can either return an `Ordering` variant or a boolean.
+    ///
+    /// If `cmp_fn` returns `Ordering`,
+    /// it chooses whether to pair elements based on the `Ordering` returned by the
     /// specified compare function. At any point, inspecting the tip of the
     /// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type
     /// `J::Item` respectively, the resulting iterator will:
@@ -991,19 +1075,47 @@
     /// use itertools::Itertools;
     /// use itertools::EitherOrBoth::{Left, Right, Both};
     ///
-    /// let multiples_of_2 = (0..10).step(2);
-    /// let multiples_of_3 = (0..10).step(3);
+    /// let a = vec![0, 2, 4, 6, 1].into_iter();
+    /// let b = (0..10).step_by(3);
     ///
     /// itertools::assert_equal(
-    ///     multiples_of_2.merge_join_by(multiples_of_3, |i, j| i.cmp(j)),
-    ///     vec![Both(0, 0), Left(2), Right(3), Left(4), Both(6, 6), Left(8), Right(9)]
+    ///     a.merge_join_by(b, |i, j| i.cmp(j)),
+    ///     vec![Both(0, 0), Left(2), Right(3), Left(4), Both(6, 6), Left(1), Right(9)]
+    /// );
+    /// ```
+    ///
+    /// If `cmp_fn` returns `bool`,
+    /// it chooses whether to pair elements based on the boolean returned by the
+    /// specified function. At any point, inspecting the tip of the
+    /// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type
+    /// `J::Item` respectively, the resulting iterator will:
+    ///
+    /// - Emit `Either::Left(i)` when `true`,
+    ///   and remove `i` from its source iterator
+    /// - Emit `Either::Right(j)` when `false`,
+    ///   and remove `j` from its source iterator
+    ///
+    /// It is similar to the `Ordering` case if the first argument is considered
+    /// "less" than the second argument.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    /// use itertools::Either::{Left, Right};
+    ///
+    /// let a = vec![0, 2, 4, 6, 1].into_iter();
+    /// let b = (0..10).step_by(3);
+    ///
+    /// itertools::assert_equal(
+    ///     a.merge_join_by(b, |i, j| i <= j),
+    ///     vec![Left(0), Right(0), Left(2), Right(3), Left(4), Left(6), Left(1), Right(6), Right(9)]
     /// );
     /// ```
     #[inline]
-    fn merge_join_by<J, F>(self, other: J, cmp_fn: F) -> MergeJoinBy<Self, J::IntoIter, F>
-        where J: IntoIterator,
-              F: FnMut(&Self::Item, &J::Item) -> std::cmp::Ordering,
-              Self: Sized
+    fn merge_join_by<J, F, T>(self, other: J, cmp_fn: F) -> MergeJoinBy<Self, J::IntoIter, F>
+    where
+        J: IntoIterator,
+        F: FnMut(&Self::Item, &J::Item) -> T,
+        Self: Sized,
     {
         merge_join_by(self, other, cmp_fn)
     }
@@ -1018,17 +1130,18 @@
     /// ```
     /// use itertools::Itertools;
     ///
-    /// let a = (0..6).step(3);
-    /// let b = (1..6).step(3);
-    /// let c = (2..6).step(3);
+    /// let a = (0..6).step_by(3);
+    /// let b = (1..6).step_by(3);
+    /// let c = (2..6).step_by(3);
     /// let it = vec![a, b, c].into_iter().kmerge();
     /// itertools::assert_equal(it, vec![0, 1, 2, 3, 4, 5]);
     /// ```
     #[cfg(feature = "use_alloc")]
     fn kmerge(self) -> KMerge<<Self::Item as IntoIterator>::IntoIter>
-        where Self: Sized,
-              Self::Item: IntoIterator,
-              <Self::Item as IntoIterator>::Item: PartialOrd,
+    where
+        Self: Sized,
+        Self::Item: IntoIterator,
+        <Self::Item as IntoIterator>::Item: PartialOrd,
     {
         kmerge(self)
     }
@@ -1054,12 +1167,11 @@
     /// assert_eq!(it.last(), Some(-7.));
     /// ```
     #[cfg(feature = "use_alloc")]
-    fn kmerge_by<F>(self, first: F)
-        -> KMergeBy<<Self::Item as IntoIterator>::IntoIter, F>
-        where Self: Sized,
-              Self::Item: IntoIterator,
-              F: FnMut(&<Self::Item as IntoIterator>::Item,
-                       &<Self::Item as IntoIterator>::Item) -> bool
+    fn kmerge_by<F>(self, first: F) -> KMergeBy<<Self::Item as IntoIterator>::IntoIter, F>
+    where
+        Self: Sized,
+        Self::Item: IntoIterator,
+        F: FnMut(&<Self::Item as IntoIterator>::Item, &<Self::Item as IntoIterator>::Item) -> bool,
     {
         kmerge_by(self, first)
     }
@@ -1076,10 +1188,11 @@
     /// itertools::assert_equal(it, vec![(0, 'α'), (0, 'β'), (1, 'α'), (1, 'β')]);
     /// ```
     fn cartesian_product<J>(self, other: J) -> Product<Self, J::IntoIter>
-        where Self: Sized,
-              Self::Item: Clone,
-              J: IntoIterator,
-              J::IntoIter: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
+        J: IntoIterator,
+        J::IntoIter: Clone,
     {
         adaptors::cartesian_product(self, other.into_iter())
     }
@@ -1091,10 +1204,11 @@
     /// the product of iterators yielding multiple types, use the
     /// [`iproduct`] macro instead.
     ///
-    ///
     /// The iterator element type is `Vec<T>`, where `T` is the iterator element
     /// of the subiterators.
     ///
+    /// Note that the iterator is fused.
+    ///
     /// ```
     /// use itertools::Itertools;
     /// let mut multi_prod = (0..3).map(|i| (i * 2)..(i * 2 + 2))
@@ -1109,12 +1223,23 @@
     /// assert_eq!(multi_prod.next(), Some(vec![1, 3, 5]));
     /// assert_eq!(multi_prod.next(), None);
     /// ```
+    ///
+    /// If the adapted iterator is empty, the result is an iterator yielding a single empty vector.
+    /// This is known as the [nullary cartesian product](https://en.wikipedia.org/wiki/Empty_product#Nullary_Cartesian_product).
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    /// let mut nullary_cartesian_product = (0..0).map(|i| (i * 2)..(i * 2 + 2)).multi_cartesian_product();
+    /// assert_eq!(nullary_cartesian_product.next(), Some(vec![]));
+    /// assert_eq!(nullary_cartesian_product.next(), None);
+    /// ```
     #[cfg(feature = "use_alloc")]
     fn multi_cartesian_product(self) -> MultiProduct<<Self::Item as IntoIterator>::IntoIter>
-        where Self: Sized,
-              Self::Item: IntoIterator,
-              <Self::Item as IntoIterator>::IntoIter: Clone,
-              <Self::Item as IntoIterator>::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: IntoIterator,
+        <Self::Item as IntoIterator>::IntoIter: Clone,
+        <Self::Item as IntoIterator>::Item: Clone,
     {
         adaptors::multi_cartesian_product(self)
     }
@@ -1148,9 +1273,9 @@
     ///         vec![-6., 4., -1.]);
     /// ```
     fn coalesce<F>(self, f: F) -> Coalesce<Self, F>
-        where Self: Sized,
-              F: FnMut(Self::Item, Self::Item)
-                       -> Result<Self::Item, (Self::Item, Self::Item)>
+    where
+        Self: Sized,
+        F: FnMut(Self::Item, Self::Item) -> Result<Self::Item, (Self::Item, Self::Item)>,
     {
         adaptors::coalesce(self, f)
     }
@@ -1170,8 +1295,9 @@
     ///                         vec![1., 2., 3., 2.]);
     /// ```
     fn dedup(self) -> Dedup<Self>
-        where Self: Sized,
-              Self::Item: PartialEq,
+    where
+        Self: Sized,
+        Self::Item: PartialEq,
     {
         adaptors::dedup(self)
     }
@@ -1192,8 +1318,9 @@
     ///                         vec![(0, 1.), (0, 2.), (0, 3.), (1, 2.)]);
     /// ```
     fn dedup_by<Cmp>(self, cmp: Cmp) -> DedupBy<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(self, cmp)
     }
@@ -1260,8 +1387,9 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn duplicates(self) -> Duplicates<Self>
-        where Self: Sized,
-              Self::Item: Eq + Hash
+    where
+        Self: Sized,
+        Self::Item: Eq + Hash,
     {
         duplicates_impl::duplicates(self)
     }
@@ -1285,9 +1413,10 @@
     /// ```
     #[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
+    where
+        Self: Sized,
+        V: Eq + Hash,
+        F: FnMut(&Self::Item) -> V,
     {
         duplicates_impl::duplicates_by(self, f)
     }
@@ -1312,8 +1441,9 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn unique(self) -> Unique<Self>
-        where Self: Sized,
-              Self::Item: Clone + Eq + Hash
+    where
+        Self: Sized,
+        Self::Item: Clone + Eq + Hash,
     {
         unique_impl::unique(self)
     }
@@ -1338,9 +1468,10 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn unique_by<V, F>(self, f: F) -> UniqueBy<Self, V, F>
-        where Self: Sized,
-              V: Eq + Hash,
-              F: FnMut(&Self::Item) -> V
+    where
+        Self: Sized,
+        V: Eq + Hash,
+        F: FnMut(&Self::Item) -> V,
     {
         unique_impl::unique_by(self, f)
     }
@@ -1358,8 +1489,9 @@
     /// 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,
-              F: FnMut(&Self::Item) -> bool,
+    where
+        Self: Sized + PeekingNext,
+        F: FnMut(&Self::Item) -> bool,
     {
         peeking_take_while::peeking_take_while(self, accept)
     }
@@ -1383,12 +1515,81 @@
     ///
     /// ```
     fn take_while_ref<F>(&mut self, accept: F) -> TakeWhileRef<Self, F>
-        where Self: Clone,
-              F: FnMut(&Self::Item) -> bool
+    where
+        Self: Clone,
+        F: FnMut(&Self::Item) -> bool,
     {
         adaptors::take_while_ref(self, accept)
     }
 
+    /// Returns an iterator adaptor that consumes elements while the given
+    /// predicate is `true`, *including* the element for which the predicate
+    /// first returned `false`.
+    ///
+    /// The [`.take_while()`][std::iter::Iterator::take_while] adaptor is useful
+    /// when you want items satisfying a predicate, but to know when to stop
+    /// taking elements, we have to consume that first element that doesn't
+    /// satisfy the predicate. This adaptor includes that element where
+    /// [`.take_while()`][std::iter::Iterator::take_while] would drop it.
+    ///
+    /// The [`.take_while_ref()`][crate::Itertools::take_while_ref] adaptor
+    /// serves a similar purpose, but this adaptor doesn't require [`Clone`]ing
+    /// the underlying elements.
+    ///
+    /// ```rust
+    /// # use itertools::Itertools;
+    /// let items = vec![1, 2, 3, 4, 5];
+    /// let filtered: Vec<_> = items
+    ///     .into_iter()
+    ///     .take_while_inclusive(|&n| n % 3 != 0)
+    ///     .collect();
+    ///
+    /// assert_eq!(filtered, vec![1, 2, 3]);
+    /// ```
+    ///
+    /// ```rust
+    /// # use itertools::Itertools;
+    /// let items = vec![1, 2, 3, 4, 5];
+    ///
+    /// let take_while_inclusive_result: Vec<_> = items
+    ///     .iter()
+    ///     .copied()
+    ///     .take_while_inclusive(|&n| n % 3 != 0)
+    ///     .collect();
+    /// let take_while_result: Vec<_> = items
+    ///     .into_iter()
+    ///     .take_while(|&n| n % 3 != 0)
+    ///     .collect();
+    ///
+    /// assert_eq!(take_while_inclusive_result, vec![1, 2, 3]);
+    /// assert_eq!(take_while_result, vec![1, 2]);
+    /// // both iterators have the same items remaining at this point---the 3
+    /// // is lost from the `take_while` vec
+    /// ```
+    ///
+    /// ```rust
+    /// # use itertools::Itertools;
+    /// #[derive(Debug, PartialEq)]
+    /// struct NoCloneImpl(i32);
+    ///
+    /// let non_clonable_items: Vec<_> = vec![1, 2, 3, 4, 5]
+    ///     .into_iter()
+    ///     .map(NoCloneImpl)
+    ///     .collect();
+    /// let filtered: Vec<_> = non_clonable_items
+    ///     .into_iter()
+    ///     .take_while_inclusive(|n| n.0 % 3 != 0)
+    ///     .collect();
+    /// let expected: Vec<_> = vec![1, 2, 3].into_iter().map(NoCloneImpl).collect();
+    /// assert_eq!(filtered, expected);
+    fn take_while_inclusive<F>(self, accept: F) -> TakeWhileInclusive<Self, F>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> bool,
+    {
+        take_while_inclusive::TakeWhileInclusive::new(self, accept)
+    }
+
     /// Return an iterator adaptor that filters `Option<A>` iterator elements
     /// and produces `A`. Stops on the first `None` encountered.
     ///
@@ -1404,7 +1605,8 @@
     ///
     /// ```
     fn while_some<A>(self) -> WhileSome<Self>
-        where Self: Sized + Iterator<Item = Option<A>>
+    where
+        Self: Sized + Iterator<Item = Option<A>>,
     {
         adaptors::while_some(self)
     }
@@ -1415,6 +1617,11 @@
     /// Iterator element can be any homogeneous tuple of type `Self::Item` with
     /// size up to 12.
     ///
+    /// # Guarantees
+    ///
+    /// If the adapted iterator is deterministic,
+    /// this iterator adapter yields items in a reliable order.
+    ///
     /// ```
     /// use itertools::Itertools;
     ///
@@ -1443,9 +1650,10 @@
     /// itertools::assert_equal(it, vec![(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]);
     /// ```
     fn tuple_combinations<T>(self) -> TupleCombinations<Self, T>
-        where Self: Sized + Clone,
-              Self::Item: Clone,
-              T: adaptors::HasCombination<Self>,
+    where
+        Self: Sized + Clone,
+        Self::Item: Clone,
+        T: adaptors::HasCombination<Self>,
     {
         adaptors::tuple_combinations(self)
     }
@@ -1453,9 +1661,14 @@
     /// Return an iterator adaptor that iterates over the `k`-length combinations of
     /// the elements from an iterator.
     ///
-    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new Vec per iteration,
+    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new `Vec` per iteration,
     /// and clones the iterator elements.
     ///
+    /// # Guarantees
+    ///
+    /// If the adapted iterator is deterministic,
+    /// this iterator adapter yields items in a reliable order.
+    ///
     /// ```
     /// use itertools::Itertools;
     ///
@@ -1481,8 +1694,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn combinations(self, k: usize) -> Combinations<Self>
-        where Self: Sized,
-              Self::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         combinations::combinations(self, k)
     }
@@ -1490,7 +1704,7 @@
     /// Return an iterator that iterates over the `k`-length combinations of
     /// the elements from an iterator, with replacement.
     ///
-    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new Vec per iteration,
+    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new `Vec` per iteration,
     /// and clones the iterator elements.
     ///
     /// ```
@@ -1519,11 +1733,14 @@
     /// elements from an iterator.
     ///
     /// Iterator element type is `Vec<Self::Item>` with length `k`. The iterator
-    /// produces a new Vec per iteration, and clones the iterator elements.
+    /// produces a new `Vec` per iteration, and clones the iterator elements.
     ///
     /// If `k` is greater than the length of the input iterator, the resultant
     /// iterator adaptor will be empty.
     ///
+    /// If you are looking for permutations with replacements,
+    /// use `repeat_n(iter, k).multi_cartesian_product()` instead.
+    ///
     /// ```
     /// use itertools::Itertools;
     ///
@@ -1554,8 +1771,9 @@
     /// re-iterated if the permutations adaptor is completed and re-iterated.
     #[cfg(feature = "use_alloc")]
     fn permutations(self, k: usize) -> Permutations<Self>
-        where Self: Sized,
-              Self::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         permutations::permutations(self, k)
     }
@@ -1590,8 +1808,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn powerset(self) -> Powerset<Self>
-        where Self: Sized,
-              Self::Item: Clone,
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         powerset::powerset(self)
     }
@@ -1614,33 +1833,35 @@
     /// itertools::assert_equal(it, vec![18, 16, 14, 12, 10, 4, 3, 2, 1, 0]);
     /// ```
     fn pad_using<F>(self, min: usize, f: F) -> PadUsing<Self, F>
-        where Self: Sized,
-              F: FnMut(usize) -> Self::Item
+    where
+        Self: Sized,
+        F: FnMut(usize) -> Self::Item,
     {
         pad_tail::pad_using(self, min, f)
     }
 
-    /// Return an iterator adaptor that wraps each element in a `Position` to
+    /// Return an iterator adaptor that combines each element with a `Position` to
     /// ease special-case handling of the first or last elements.
     ///
     /// Iterator element type is
-    /// [`Position<Self::Item>`](Position)
+    /// [`(Position, Self::Item)`](Position)
     ///
     /// ```
     /// use itertools::{Itertools, Position};
     ///
     /// let it = (0..4).with_position();
     /// itertools::assert_equal(it,
-    ///                         vec![Position::First(0),
-    ///                              Position::Middle(1),
-    ///                              Position::Middle(2),
-    ///                              Position::Last(3)]);
+    ///                         vec![(Position::First, 0),
+    ///                              (Position::Middle, 1),
+    ///                              (Position::Middle, 2),
+    ///                              (Position::Last, 3)]);
     ///
     /// let it = (0..1).with_position();
-    /// itertools::assert_equal(it, vec![Position::Only(0)]);
+    /// itertools::assert_equal(it, vec![(Position::Only, 0)]);
     /// ```
     fn with_position(self) -> WithPosition<Self>
-        where Self: Sized,
+    where
+        Self: Sized,
     {
         with_position::with_position(self)
     }
@@ -1648,7 +1869,7 @@
     /// Return an iterator adaptor that yields the indices of all elements
     /// satisfying a predicate, counted from the start of the iterator.
     ///
-    /// Equivalent to `iter.enumerate().filter(|(_, v)| predicate(v)).map(|(i, _)| i)`.
+    /// Equivalent to `iter.enumerate().filter(|(_, v)| predicate(*v)).map(|(i, _)| i)`.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -1659,8 +1880,9 @@
     /// itertools::assert_equal(data.iter().positions(|v| v % 2 == 1).rev(), vec![7, 6, 3, 2, 0]);
     /// ```
     fn positions<P>(self, predicate: P) -> Positions<Self, P>
-        where Self: Sized,
-              P: FnMut(Self::Item) -> bool,
+    where
+        Self: Sized,
+        P: FnMut(Self::Item) -> bool,
     {
         adaptors::positions(self, predicate)
     }
@@ -1676,8 +1898,9 @@
     /// itertools::assert_equal(it, vec![vec![1, 0], vec![3, 2, 1, 0]]);
     /// ```
     fn update<F>(self, updater: F) -> Update<Self, F>
-        where Self: Sized,
-              F: FnMut(&mut Self::Item),
+    where
+        Self: Sized,
+        F: FnMut(&mut Self::Item),
     {
         adaptors::update(self, updater)
     }
@@ -1697,8 +1920,9 @@
     /// assert_eq!(Some((1, 2)), iter.next_tuple());
     /// ```
     fn next_tuple<T>(&mut self) -> Option<T>
-        where Self: Sized + Iterator<Item = T::Item>,
-              T: traits::HomogeneousTuple
+    where
+        Self: Sized + Iterator<Item = T::Item>,
+        T: traits::HomogeneousTuple,
     {
         T::collect_from_iter_no_buf(self)
     }
@@ -1722,19 +1946,19 @@
     /// }
     /// ```
     fn collect_tuple<T>(mut self) -> Option<T>
-        where Self: Sized + Iterator<Item = T::Item>,
-              T: traits::HomogeneousTuple
+    where
+        Self: Sized + Iterator<Item = T::Item>,
+        T: traits::HomogeneousTuple,
     {
         match self.next_tuple() {
             elt @ Some(_) => match self.next() {
                 Some(_) => None,
                 None => elt,
             },
-            _ => None
+            _ => None,
         }
     }
 
-
     /// Find the position and value of the first element satisfying a predicate.
     ///
     /// The iterator is not advanced past the first element found.
@@ -1746,14 +1970,10 @@
     /// assert_eq!(text.chars().find_position(|ch| ch.is_lowercase()), Some((1, 'α')));
     /// ```
     fn find_position<P>(&mut self, mut pred: P) -> Option<(usize, Self::Item)>
-        where P: FnMut(&Self::Item) -> bool
+    where
+        P: FnMut(&Self::Item) -> bool,
     {
-        for (index, elt) in self.enumerate() {
-            if pred(&elt) {
-                return Some((index, elt));
-            }
-        }
-        None
+        self.enumerate().find(|(_, elt)| pred(elt))
     }
     /// Find the value of the first element satisfying a predicate or return the last element, if any.
     ///
@@ -1768,12 +1988,20 @@
     /// 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,
+    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)
+        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.
     ///
@@ -1788,8 +2016,9 @@
     /// 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,
+    where
+        Self: Sized,
+        P: FnMut(&Self::Item) -> bool,
     {
         let first = self.next()?;
         Some(if predicate(&first) {
@@ -1810,14 +2039,14 @@
     ///
     /// #[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
@@ -1849,8 +2078,9 @@
     /// assert!(data.into_iter().all_equal());
     /// ```
     fn all_equal(&mut self) -> bool
-        where Self: Sized,
-              Self::Item: PartialEq,
+    where
+        Self: Sized,
+        Self::Item: PartialEq,
     {
         match self.next() {
             None => true,
@@ -1858,6 +2088,38 @@
         }
     }
 
+    /// If there are elements and they are all equal, return a single copy of that element.
+    /// If there are no elements, return an Error containing None.
+    /// If there are elements and they are not all equal, return a tuple containing the first
+    /// two non-equal elements found.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 4, 5, 5];
+    /// assert_eq!(data.iter().all_equal_value(), Err(Some((&1, &2))));
+    /// assert_eq!(data[0..3].iter().all_equal_value(), Ok(&1));
+    /// assert_eq!(data[3..5].iter().all_equal_value(), Ok(&2));
+    /// assert_eq!(data[5..8].iter().all_equal_value(), Ok(&3));
+    ///
+    /// let data : Option<usize> = None;
+    /// assert_eq!(data.into_iter().all_equal_value(), Err(None));
+    /// ```
+    #[allow(clippy::type_complexity)]
+    fn all_equal_value(&mut self) -> Result<Self::Item, Option<(Self::Item, Self::Item)>>
+    where
+        Self: Sized,
+        Self::Item: PartialEq,
+    {
+        let first = self.next().ok_or(None)?;
+        let other = self.find(|x| x != &first);
+        if let Some(other) = other {
+            Err(Some((first, other)))
+        } else {
+            Ok(first)
+        }
+    }
+
     /// Check whether all elements are unique (non equal).
     ///
     /// Empty iterators are considered to have unique elements:
@@ -1875,8 +2137,9 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn all_unique(&mut self) -> bool
-        where Self: Sized,
-              Self::Item: Eq + Hash
+    where
+        Self: Sized,
+        Self::Item: Eq + Hash,
     {
         let mut used = HashSet::new();
         self.all(move |elt| used.insert(elt))
@@ -1885,7 +2148,7 @@
     /// Consume the first `n` elements from the iterator eagerly,
     /// and return the same iterator again.
     ///
-    /// It works similarly to *.skip(* `n` *)* except it is eager and
+    /// It works similarly to `.skip(n)` except it is eager and
     /// preserves the iterator type.
     ///
     /// ```
@@ -1898,7 +2161,8 @@
     /// *Fusing notes: if the iterator is exhausted by dropping,
     /// the result of calling `.next()` again depends on the iterator implementation.*
     fn dropping(mut self, n: usize) -> Self
-        where Self: Sized
+    where
+        Self: Sized,
     {
         if n > 0 {
             self.nth(n - 1);
@@ -1922,8 +2186,8 @@
     /// itertools::assert_equal(init, vec![0, 3, 6]);
     /// ```
     fn dropping_back(mut self, n: usize) -> Self
-        where Self: Sized,
-              Self: DoubleEndedIterator
+    where
+        Self: Sized + DoubleEndedIterator,
     {
         if n > 0 {
             (&mut self).rev().nth(n - 1);
@@ -1931,31 +2195,6 @@
         self
     }
 
-    /// Run the closure `f` eagerly on each element of the iterator.
-    ///
-    /// Consumes the iterator until its end.
-    ///
-    /// ```
-    /// use std::sync::mpsc::channel;
-    /// use itertools::Itertools;
-    ///
-    /// let (tx, rx) = channel();
-    ///
-    /// // use .foreach() to apply a function to each value -- sending it
-    /// (0..5).map(|x| x * 2 + 1).foreach(|x| { tx.send(x).unwrap(); } );
-    ///
-    /// drop(tx);
-    ///
-    /// itertools::assert_equal(rx.iter(), vec![1, 3, 5, 7, 9]);
-    /// ```
-    #[deprecated(note="Use .for_each() instead", since="0.8.0")]
-    fn foreach<F>(self, f: F)
-        where F: FnMut(Self::Item),
-              Self: Sized,
-    {
-        self.for_each(f);
-    }
-
     /// 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
@@ -1970,8 +2209,10 @@
     ///            vec![1, 2, 3, 4, 5, 6]);
     /// ```
     fn concat(self) -> Self::Item
-        where Self: Sized,
-              Self::Item: Extend<<<Self as Iterator>::Item as IntoIterator>::Item> + IntoIterator + Default
+    where
+        Self: Sized,
+        Self::Item:
+            Extend<<<Self as Iterator>::Item as IntoIterator>::Item> + IntoIterator + Default,
     {
         concat(self)
     }
@@ -1980,7 +2221,8 @@
     /// for convenience.
     #[cfg(feature = "use_alloc")]
     fn collect_vec(self) -> Vec<Self::Item>
-        where Self: Sized
+    where
+        Self: Sized,
     {
         self.collect()
     }
@@ -2005,7 +2247,6 @@
     ///     Ok(())
     /// }
     /// ```
-    #[cfg(feature = "use_alloc")]
     fn try_collect<T, U, E>(self) -> Result<U, E>
     where
         Self: Sized + Iterator<Item = Result<T, E>>,
@@ -2031,21 +2272,17 @@
     /// ```
     #[inline]
     fn set_from<'a, A: 'a, J>(&mut self, from: J) -> usize
-        where Self: Iterator<Item = &'a mut A>,
-              J: IntoIterator<Item = A>
+    where
+        Self: Iterator<Item = &'a mut A>,
+        J: IntoIterator<Item = A>,
     {
-        let mut count = 0;
-        for elt in from {
-            match self.next() {
-                None => break,
-                Some(ptr) => *ptr = elt,
-            }
-            count += 1;
-        }
-        count
+        from.into_iter()
+            .zip(self)
+            .map(|(new, old)| *old = new)
+            .count()
     }
 
-    /// Combine all iterator elements into one String, separated by `sep`.
+    /// Combine all iterator elements into one `String`, separated by `sep`.
     ///
     /// Use the `Display` implementation of each element.
     ///
@@ -2057,7 +2294,8 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn join(&mut self, sep: &str) -> String
-        where Self::Item: std::fmt::Display
+    where
+        Self::Item: std::fmt::Display,
     {
         match self.next() {
             None => String::new(),
@@ -2091,7 +2329,8 @@
     ///            "1.10, 2.72, -3.00");
     /// ```
     fn format(self, sep: &str) -> Format<Self>
-        where Self: Sized,
+    where
+        Self: Sized,
     {
         format::new_format_default(self, sep)
     }
@@ -2129,21 +2368,13 @@
     ///
     /// ```
     fn format_with<F>(self, sep: &str, format: F) -> FormatWith<Self, F>
-        where Self: Sized,
-              F: FnMut(Self::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
+    where
+        Self: Sized,
+        F: FnMut(Self::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
     {
         format::new_format(self, sep, format)
     }
 
-    /// 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>>,
-              F: FnMut(B, A) -> B
-    {
-        self.fold_ok(start, f)
-    }
-
     /// Fold `Result` values from an iterator.
     ///
     /// Only `Ok` values are folded. If no error is encountered, the folded
@@ -2158,7 +2389,9 @@
     /// For example the sequence *Ok(1), Ok(2), Ok(3)* will result in a
     /// computation like this:
     ///
-    /// ```ignore
+    /// ```no_run
+    /// # let start = 0;
+    /// # let f = |x, y| x + y;
     /// let mut accum = start;
     /// accum = f(accum, 1);
     /// accum = f(accum, 2);
@@ -2187,8 +2420,9 @@
     /// );
     /// ```
     fn fold_ok<A, E, B, F>(&mut self, mut start: B, mut f: F) -> Result<B, E>
-        where Self: Iterator<Item = Result<A, E>>,
-              F: FnMut(B, A) -> B
+    where
+        Self: Iterator<Item = Result<A, E>>,
+        F: FnMut(B, A) -> B,
     {
         for elt in self {
             match elt {
@@ -2219,8 +2453,9 @@
     /// assert_eq!(more_values.next().unwrap(), Some(0));
     /// ```
     fn fold_options<A, B, F>(&mut self, mut start: B, mut f: F) -> Option<B>
-        where Self: Iterator<Item = Option<A>>,
-              F: FnMut(B, A) -> B
+    where
+        Self: Iterator<Item = Option<A>>,
+        F: FnMut(B, A) -> B,
     {
         for elt in self {
             match elt {
@@ -2243,10 +2478,14 @@
     /// 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")]
+    #[deprecated(
+        note = "Use [`Iterator::reduce`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.reduce) instead",
+        since = "0.10.2"
+    )]
     fn fold1<F>(mut self, f: F) -> Option<Self::Item>
-        where F: FnMut(Self::Item, Self::Item) -> Self::Item,
-              Self: Sized,
+    where
+        F: FnMut(Self::Item, Self::Item) -> Self::Item,
+        Self: Sized,
     {
         self.next().map(move |x| self.fold(x, f))
     }
@@ -2279,65 +2518,80 @@
     /// └─f─f─f─f─f─f
     /// ```
     ///
-    /// If `f` is associative, prefer the normal [`Iterator::reduce`] instead.
+    /// If `f` is associative you should also decide carefully:
+    ///
+    /// - if `f` is a trivial operation like `u32::wrapping_add`, prefer the normal
+    /// [`Iterator::reduce`] instead since it will most likely result in the generation of simpler
+    /// code because the compiler is able to optimize it
+    /// - otherwise if `f` is non-trivial like `format!`, you should use `tree_reduce` since it
+    /// reduces the number of operations from `O(n)` to `O(ln(n))`
+    ///
+    /// Here "non-trivial" means:
+    ///
+    /// - any allocating operation
+    /// - any function that is a composition of many operations
     ///
     /// ```
     /// use itertools::Itertools;
     ///
     /// // The same tree as above
     /// let num_strings = (1..8).map(|x| x.to_string());
-    /// assert_eq!(num_strings.tree_fold1(|x, y| format!("f({}, {})", x, y)),
+    /// assert_eq!(num_strings.tree_reduce(|x, y| format!("f({}, {})", x, y)),
     ///     Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))")));
     ///
     /// // Like fold1, an empty iterator produces None
-    /// assert_eq!((0..0).tree_fold1(|x, y| x * y), None);
+    /// assert_eq!((0..0).tree_reduce(|x, y| x * y), None);
     ///
-    /// // tree_fold1 matches fold1 for associative operations...
-    /// assert_eq!((0..10).tree_fold1(|x, y| x + y),
+    /// // tree_reduce matches fold1 for associative operations...
+    /// assert_eq!((0..10).tree_reduce(|x, y| x + y),
     ///     (0..10).fold1(|x, y| x + y));
     /// // ...but not for non-associative ones
-    /// assert_ne!((0..10).tree_fold1(|x, y| x - y),
+    /// assert_ne!((0..10).tree_reduce(|x, y| x - y),
     ///     (0..10).fold1(|x, y| x - y));
     /// ```
-    fn tree_fold1<F>(mut self, mut f: F) -> Option<Self::Item>
-        where F: FnMut(Self::Item, Self::Item) -> Self::Item,
-              Self: Sized,
+    fn tree_reduce<F>(mut self, mut f: F) -> Option<Self::Item>
+    where
+        F: FnMut(Self::Item, Self::Item) -> Self::Item,
+        Self: Sized,
     {
         type State<T> = Result<T, Option<T>>;
 
         fn inner0<T, II, FF>(it: &mut II, f: &mut FF) -> State<T>
-            where
-                II: Iterator<Item = T>,
-                FF: FnMut(T, T) -> T
+        where
+            II: Iterator<Item = T>,
+            FF: FnMut(T, T) -> T,
         {
             // This function could be replaced with `it.next().ok_or(None)`,
-            // but half the useful tree_fold1 work is combining adjacent items,
+            // but half the useful tree_reduce work is combining adjacent items,
             // so put that in a form that LLVM is more likely to optimize well.
 
-            let a =
-                if let Some(v) = it.next() { v }
-                else { return Err(None) };
-            let b =
-                if let Some(v) = it.next() { v }
-                else { return Err(Some(a)) };
+            let a = if let Some(v) = it.next() {
+                v
+            } else {
+                return Err(None);
+            };
+            let b = if let Some(v) = it.next() {
+                v
+            } else {
+                return Err(Some(a));
+            };
             Ok(f(a, b))
         }
 
         fn inner<T, II, FF>(stop: usize, it: &mut II, f: &mut FF) -> State<T>
-            where
-                II: Iterator<Item = T>,
-                FF: FnMut(T, T) -> T
+        where
+            II: Iterator<Item = T>,
+            FF: FnMut(T, T) -> T,
         {
             let mut x = inner0(it, f)?;
             for height in 0..stop {
                 // Try to get another tree the same size with which to combine it,
                 // creating a new tree that's twice as big for next time around.
-                let next =
-                    if height == 0 {
-                        inner0(it, f)
-                    } else {
-                        inner(height, it, f)
-                    };
+                let next = if height == 0 {
+                    inner0(it, f)
+                } else {
+                    inner(height, it, f)
+                };
                 match next {
                     Ok(y) => x = f(x, y),
 
@@ -2352,12 +2606,22 @@
             Ok(x)
         }
 
-        match inner(usize::max_value(), &mut self, &mut f) {
+        match inner(usize::MAX, &mut self, &mut f) {
             Err(x) => x,
             _ => unreachable!(),
         }
     }
 
+    /// See [`.tree_reduce()`](Itertools::tree_reduce).
+    #[deprecated(note = "Use .tree_reduce() instead", since = "0.13.0")]
+    fn tree_fold1<F>(self, f: F) -> Option<Self::Item>
+    where
+        F: FnMut(Self::Item, Self::Item) -> Self::Item,
+        Self: Sized,
+    {
+        self.tree_reduce(f)
+    }
+
     /// An iterator method that applies a function, producing a single, final value.
     ///
     /// `fold_while()` is basically equivalent to [`Iterator::fold`] but with additional support for
@@ -2398,19 +2662,19 @@
     /// `fold()` called the provided closure for every item of the callee iterator,
     /// `fold_while()` actually stopped iterating as soon as it encountered `Fold::Done(_)`.
     fn fold_while<B, F>(&mut self, init: B, mut f: F) -> FoldWhile<B>
-        where Self: Sized,
-              F: FnMut(B, Self::Item) -> FoldWhile<B>
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> FoldWhile<B>,
     {
-        use Result::{
-            Ok as Continue,
-            Err as Break,
-        };
+        use Result::{Err as Break, Ok as Continue};
 
-        let result = self.try_fold(init, #[inline(always)] |acc, v|
-            match f(acc, v) {
-              FoldWhile::Continue(acc) => Continue(acc),
-              FoldWhile::Done(acc) => Break(acc),
-            }
+        let result = self.try_fold(
+            init,
+            #[inline(always)]
+            |acc, v| match f(acc, v) {
+                FoldWhile::Continue(acc) => Continue(acc),
+                FoldWhile::Done(acc) => Break(acc),
+            },
         );
 
         match result {
@@ -2441,11 +2705,11 @@
     /// assert_eq!(nonempty_sum, Some(55));
     /// ```
     fn sum1<S>(mut self) -> Option<S>
-        where Self: Sized,
-              S: std::iter::Sum<Self::Item>,
+    where
+        Self: Sized,
+        S: std::iter::Sum<Self::Item>,
     {
-        self.next()
-            .map(|first| once(first).chain(self).sum())
+        self.next().map(|first| once(first).chain(self).sum())
     }
 
     /// Iterate over the entire iterator and multiply all the elements.
@@ -2469,11 +2733,11 @@
     /// assert_eq!(nonempty_product, Some(3628800));
     /// ```
     fn product1<P>(mut self) -> Option<P>
-        where Self: Sized,
-              P: std::iter::Product<Self::Item>,
+    where
+        Self: Sized,
+        P: std::iter::Product<Self::Item>,
     {
-        self.next()
-            .map(|first| once(first).chain(self).product())
+        self.next().map(|first| once(first).chain(self).product())
     }
 
     /// Sort all iterator elements into a new iterator in ascending order.
@@ -2482,6 +2746,8 @@
     /// [`slice::sort_unstable`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
+    /// This sort is unstable (i.e., may reorder equal elements).
+    ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
     /// without any extra copying or allocation cost.
     ///
@@ -2495,8 +2761,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_unstable(self) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         // Use .sort_unstable() directly since it is not quite identical with
         // .sort_by(Ord::cmp)
@@ -2511,6 +2778,8 @@
     /// [`slice::sort_unstable_by`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
+    /// This sort is unstable (i.e., may reorder equal elements).
+    ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
     /// without any extra copying or allocation cost.
     ///
@@ -2530,8 +2799,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_unstable_by<F>(self, cmp: F) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
         let mut v = Vec::from_iter(self);
         v.sort_unstable_by(cmp);
@@ -2544,6 +2814,8 @@
     /// [`slice::sort_unstable_by_key`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
+    /// This sort is unstable (i.e., may reorder equal elements).
+    ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
     /// without any extra copying or allocation cost.
     ///
@@ -2563,9 +2835,10 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_unstable_by_key<K, F>(self, f: F) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              K: Ord,
-              F: FnMut(&Self::Item) -> K,
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         let mut v = Vec::from_iter(self);
         v.sort_unstable_by_key(f);
@@ -2578,6 +2851,8 @@
     /// [`slice::sort`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
+    /// This sort is stable (i.e., does not reorder equal elements).
+    ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
     /// without any extra copying or allocation cost.
     ///
@@ -2591,8 +2866,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted(self) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         // Use .sort() directly since it is not quite identical with
         // .sort_by(Ord::cmp)
@@ -2607,6 +2883,8 @@
     /// [`slice::sort_by`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
+    /// This sort is stable (i.e., does not reorder equal elements).
+    ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
     /// without any extra copying or allocation cost.
     ///
@@ -2614,7 +2892,7 @@
     /// use itertools::Itertools;
     ///
     /// // sort people in descending order by age
-    /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)];
+    /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 30)];
     ///
     /// let oldest_people_first = people
     ///     .into_iter()
@@ -2626,8 +2904,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_by<F>(self, cmp: F) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
         let mut v = Vec::from_iter(self);
         v.sort_by(cmp);
@@ -2640,6 +2919,8 @@
     /// [`slice::sort_by_key`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
+    /// This sort is stable (i.e., does not reorder equal elements).
+    ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
     /// without any extra copying or allocation cost.
     ///
@@ -2647,7 +2928,7 @@
     /// use itertools::Itertools;
     ///
     /// // sort people in descending order by age
-    /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)];
+    /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 30)];
     ///
     /// let oldest_people_first = people
     ///     .into_iter()
@@ -2659,9 +2940,10 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_by_key<K, F>(self, f: F) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              K: Ord,
-              F: FnMut(&Self::Item) -> K,
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         let mut v = Vec::from_iter(self);
         v.sort_by_key(f);
@@ -2675,6 +2957,8 @@
     /// [`slice::sort_by_cached_key`] method and returns the result as a new
     /// iterator that owns its elements.
     ///
+    /// This sort is stable (i.e., does not reorder equal elements).
+    ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
     /// without any extra copying or allocation cost.
     ///
@@ -2682,7 +2966,7 @@
     /// use itertools::Itertools;
     ///
     /// // sort people in descending order by age
-    /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)];
+    /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 30)];
     ///
     /// let oldest_people_first = people
     ///     .into_iter()
@@ -2733,12 +3017,253 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn k_smallest(self, k: usize) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
-        crate::k_smallest::k_smallest(self, k)
-            .into_sorted_vec()
-            .into_iter()
+        // The stdlib heap has optimised handling of "holes", which is not included in our heap implementation in k_smallest_general.
+        // While the difference is unlikely to have practical impact unless `Self::Item` is very large, this method uses the stdlib structure
+        // to maintain performance compared to previous versions of the crate.
+        use alloc::collections::BinaryHeap;
+
+        if k == 0 {
+            self.last();
+            return Vec::new().into_iter();
+        }
+        if k == 1 {
+            return self.min().into_iter().collect_vec().into_iter();
+        }
+
+        let mut iter = self.fuse();
+        let mut heap: BinaryHeap<_> = iter.by_ref().take(k).collect();
+
+        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
+            //  `PeekMut` sifts-down unconditionally on Rust 1.46.0 and prior.
+            if *heap.peek().unwrap() > i {
+                *heap.peek_mut().unwrap() = i;
+            }
+        });
+
+        heap.into_sorted_vec().into_iter()
+    }
+
+    /// Sort the k smallest elements into a new iterator using the provided comparison.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// This corresponds to `self.sorted_by(cmp).take(k)` in the same way that
+    /// [`k_smallest`](Itertools::k_smallest) corresponds to `self.sorted().take(k)`,
+    /// in both semantics and complexity.
+    ///
+    /// Particularly, a custom heap implementation ensures the comparison is not cloned.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_smallest = numbers
+    ///     .into_iter()
+    ///     .k_smallest_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b)));
+    ///
+    /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_smallest_by<F>(self, k: usize, cmp: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    {
+        k_smallest::k_smallest_general(self, k, cmp).into_iter()
+    }
+
+    /// Return the elements producing the k smallest outputs of the provided function.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// This corresponds to `self.sorted_by_key(key).take(k)` in the same way that
+    /// [`k_smallest`](Itertools::k_smallest) corresponds to `self.sorted().take(k)`,
+    /// in both semantics and complexity.
+    ///
+    /// Particularly, a custom heap implementation ensures the comparison is not cloned.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_smallest = numbers
+    ///     .into_iter()
+    ///     .k_smallest_by_key(5, |n| (n % 7, *n));
+    ///
+    /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_smallest_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: Ord,
+    {
+        self.k_smallest_by(k, k_smallest::key_to_cmp(key))
+    }
+
+    /// Sort the k largest elements into a new iterator, in descending order.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// It is semantically equivalent to [`k_smallest`](Itertools::k_smallest)
+    /// with a reversed `Ord`.
+    /// However, this is implemented with a custom binary heap which does not
+    /// have the same performance characteristics for very large `Self::Item`.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_largest = numbers
+    ///     .into_iter()
+    ///     .k_largest(5);
+    ///
+    /// itertools::assert_equal(five_largest, vec![14, 13, 12, 11, 10]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_largest(self, k: usize) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        Self::Item: Ord,
+    {
+        self.k_largest_by(k, Self::Item::cmp)
+    }
+
+    /// Sort the k largest elements into a new iterator using the provided comparison.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// Functionally equivalent to [`k_smallest_by`](Itertools::k_smallest_by)
+    /// with a reversed `Ord`.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_largest = numbers
+    ///     .into_iter()
+    ///     .k_largest_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b)));
+    ///
+    /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_largest_by<F>(self, k: usize, mut cmp: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    {
+        self.k_smallest_by(k, move |a, b| cmp(b, a))
+    }
+
+    /// Return the elements producing the k largest outputs of the provided function.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// Functionally equivalent to [`k_smallest_by_key`](Itertools::k_smallest_by_key)
+    /// with a reversed `Ord`.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_largest = numbers
+    ///     .into_iter()
+    ///     .k_largest_by_key(5, |n| (n % 7, *n));
+    ///
+    /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_largest_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: Ord,
+    {
+        self.k_largest_by(k, k_smallest::key_to_cmp(key))
+    }
+
+    /// Consumes the iterator and return an iterator of the last `n` elements.
+    ///
+    /// The iterator, if directly collected to a `VecDeque`, is converted
+    /// without any extra copying or allocation cost.
+    /// If directly collected to a `Vec`, it may need some data movement
+    /// but no re-allocation.
+    ///
+    /// ```
+    /// use itertools::{assert_equal, Itertools};
+    ///
+    /// let v = vec![5, 9, 8, 4, 2, 12, 0];
+    /// assert_equal(v.iter().tail(3), &[2, 12, 0]);
+    /// assert_equal(v.iter().tail(10), &v);
+    ///
+    /// assert_equal(v.iter().tail(1), v.iter().last());
+    ///
+    /// assert_equal((0..100).tail(10), 90..100);
+    ///
+    /// assert_equal((0..100).filter(|x| x % 3 == 0).tail(10), (72..100).step_by(3));
+    /// ```
+    ///
+    /// For double ended iterators without side-effects, you might prefer
+    /// `.rev().take(n).rev()` to have a similar result (lazy and non-allocating)
+    /// without consuming the entire iterator.
+    #[cfg(feature = "use_alloc")]
+    fn tail(self, n: usize) -> VecDequeIntoIter<Self::Item>
+    where
+        Self: Sized,
+    {
+        match n {
+            0 => {
+                self.last();
+                VecDeque::new()
+            }
+            1 => self.last().into_iter().collect(),
+            _ => {
+                // Skip the starting part of the iterator if possible.
+                let (low, _) = self.size_hint();
+                let mut iter = self.fuse().skip(low.saturating_sub(n));
+                // TODO: If VecDeque has a more efficient method than
+                // `.pop_front();.push_back(val)` in the future then maybe revisit this.
+                let mut data: Vec<_> = iter.by_ref().take(n).collect();
+                // Update `data` cyclically.
+                let idx = iter.fold(0, |i, val| {
+                    debug_assert_eq!(data.len(), n);
+                    data[i] = val;
+                    if i + 1 == n {
+                        0
+                    } else {
+                        i + 1
+                    }
+                });
+                // Respect the insertion order, efficiently.
+                let mut data = VecDeque::from(data);
+                data.rotate_left(idx);
+                data
+            }
+        }
+        .into_iter()
     }
 
     /// Collect all iterator elements into one of two
@@ -2763,10 +3288,11 @@
     /// assert_eq!(failures, [false, true]);
     /// ```
     fn partition_map<A, B, F, L, R>(self, mut predicate: F) -> (A, B)
-        where Self: Sized,
-              F: FnMut(Self::Item) -> Either<L, R>,
-              A: Default + Extend<L>,
-              B: Default + Extend<R>,
+    where
+        Self: Sized,
+        F: FnMut(Self::Item) -> Either<L, R>,
+        A: Default + Extend<L>,
+        B: Default + Extend<R>,
     {
         let mut left = A::default();
         let mut right = B::default();
@@ -2795,10 +3321,10 @@
     /// 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>,
+    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),
@@ -2824,8 +3350,9 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn into_group_map<K, V>(self) -> HashMap<K, Vec<V>>
-        where Self: Iterator<Item=(K, V)> + Sized,
-              K: Hash + Eq,
+    where
+        Self: Iterator<Item = (K, V)> + Sized,
+        K: Hash + Eq,
     {
         group_map::into_group_map(self)
     }
@@ -2859,46 +3386,48 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn into_group_map_by<K, V, F>(self, f: F) -> HashMap<K, Vec<V>>
-        where
-            Self: Iterator<Item=V> + Sized,
-            K: Hash + Eq,
-            F: Fn(&V) -> K,
+    where
+        Self: Iterator<Item = V> + Sized,
+        K: Hash + Eq,
+        F: FnMut(&V) -> K,
     {
         group_map::into_group_map_by(self, f)
     }
 
-    /// Constructs a `GroupingMap` to be used later with one of the efficient 
+    /// Constructs a `GroupingMap` to be used later with one of the efficient
     /// group-and-fold operations it allows to perform.
-    /// 
+    ///
     /// The input iterator must yield item in the form of `(K, V)` where the
     /// 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`] for more informations
     /// on what operations are available.
     #[cfg(feature = "use_std")]
     fn into_grouping_map<K, V>(self) -> GroupingMap<Self>
-        where Self: Iterator<Item=(K, V)> + Sized,
-              K: Hash + Eq,
+    where
+        Self: Iterator<Item = (K, V)> + Sized,
+        K: Hash + Eq,
     {
         grouping_map::new(self)
     }
 
-    /// Constructs a `GroupingMap` to be used later with one of the efficient 
+    /// Constructs a `GroupingMap` to be used later with one of the efficient
     /// group-and-fold operations it allows to perform.
-    /// 
+    ///
     /// 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`] 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>
-        where Self: Iterator<Item=V> + Sized,
-              K: Hash + Eq,
-              F: FnMut(&V) -> K
+    where
+        Self: Iterator<Item = V> + Sized,
+        K: Hash + Eq,
+        F: FnMut(&V) -> K,
     {
-        grouping_map::new(grouping_map::MapForGrouping::new(self, key_mapper))
+        grouping_map::new(grouping_map::new_map_for_grouping(self, key_mapper))
     }
 
     /// Return all minimum elements of an iterator.
@@ -2923,9 +3452,11 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn min_set(self) -> Vec<Self::Item>
-        where Self: Sized, Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x.cmp(y))
     }
@@ -2954,15 +3485,13 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn min_set_by<F>(self, mut compare: F) -> Vec<Self::Item>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        extrema_set::min_set_impl(
-            self,
-            |_| (),
-            |x, y, _, _| compare(x, y)
-        )
+        extrema_set::min_set_impl(self, |_| (), |x, y, _, _| compare(x, y))
     }
 
     /// Return all minimum elements of an iterator, as determined by
@@ -2988,9 +3517,12 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn min_set_by_key<K, F>(self, key: F) -> Vec<Self::Item>
-        where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky))
     }
@@ -3017,9 +3549,11 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn max_set(self) -> Vec<Self::Item>
-        where Self: Sized, Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x.cmp(y))
     }
@@ -3048,18 +3582,16 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn max_set_by<F>(self, mut compare: F) -> Vec<Self::Item>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        extrema_set::max_set_impl(
-            self,
-            |_| (),
-            |x, y, _, _| compare(x, y)
-        )
+        extrema_set::max_set_impl(self, |_| (), |x, y, _, _| compare(x, y))
     }
 
-    /// Return all minimum elements of an iterator, as determined by
+    /// Return all maximum elements of an iterator, as determined by
     /// the specified function.
     ///
     /// # Examples
@@ -3082,9 +3614,12 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn max_set_by_key<K, F>(self, key: F) -> Vec<Self::Item>
-        where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky))
     }
@@ -3125,7 +3660,9 @@
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
     fn minmax(self) -> MinMaxResult<Self::Item>
-        where Self: Sized, Self::Item: PartialOrd
+    where
+        Self: Sized,
+        Self::Item: PartialOrd,
     {
         minmax::minmax_impl(self, |_| (), |x, y, _, _| x < y)
     }
@@ -3142,7 +3679,10 @@
     /// The keys can be floats but no particular result is guaranteed
     /// if a key is NaN.
     fn minmax_by_key<K, F>(self, key: F) -> MinMaxResult<Self::Item>
-        where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: PartialOrd,
+        F: FnMut(&Self::Item) -> K,
     {
         minmax::minmax_impl(self, key, |_, _, xk, yk| xk < yk)
     }
@@ -3156,13 +3696,11 @@
     /// the last maximal element wins.  This matches the behavior of the standard
     /// [`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
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        minmax::minmax_impl(
-            self,
-            |_| (),
-            |x, y, _, _| Ordering::Less == compare(x, y)
-        )
+        minmax::minmax_impl(self, |_| (), |x, y, _, _| Ordering::Less == compare(x, y))
     }
 
     /// Return the position of the maximum element in the iterator.
@@ -3185,7 +3723,9 @@
     /// assert_eq!(a.iter().position_max(), Some(1));
     /// ```
     fn position_max(self) -> Option<usize>
-        where Self: Sized, Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         self.enumerate()
             .max_by(|x, y| Ord::cmp(&x.1, &y.1))
@@ -3213,7 +3753,10 @@
     /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(3));
     /// ```
     fn position_max_by_key<K, F>(self, mut key: F) -> Option<usize>
-        where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         self.enumerate()
             .max_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1)))
@@ -3241,7 +3784,9 @@
     /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(1));
     /// ```
     fn position_max_by<F>(self, mut compare: F) -> Option<usize>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
         self.enumerate()
             .max_by(|x, y| compare(&x.1, &y.1))
@@ -3268,7 +3813,9 @@
     /// assert_eq!(a.iter().position_min(), Some(2));
     /// ```
     fn position_min(self) -> Option<usize>
-        where Self: Sized, Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         self.enumerate()
             .min_by(|x, y| Ord::cmp(&x.1, &y.1))
@@ -3296,7 +3843,10 @@
     /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(0));
     /// ```
     fn position_min_by_key<K, F>(self, mut key: F) -> Option<usize>
-        where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         self.enumerate()
             .min_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1)))
@@ -3324,7 +3874,9 @@
     /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(2));
     /// ```
     fn position_min_by<F>(self, mut compare: F) -> Option<usize>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
         self.enumerate()
             .min_by(|x, y| compare(&x.1, &y.1))
@@ -3374,9 +3926,11 @@
     /// assert_eq!(a.iter().position_minmax(), MinMax(2, 1));
     /// ```
     fn position_minmax(self) -> MinMaxResult<usize>
-        where Self: Sized, Self::Item: PartialOrd
+    where
+        Self: Sized,
+        Self::Item: PartialOrd,
     {
-        use crate::MinMaxResult::{NoElements, OneElement, MinMax};
+        use crate::MinMaxResult::{MinMax, NoElements, OneElement};
         match minmax::minmax_impl(self.enumerate(), |_| (), |x, y, _, _| x.1 < y.1) {
             NoElements => NoElements,
             OneElement(x) => OneElement(x.0),
@@ -3419,9 +3973,12 @@
     ///
     /// [`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
+    where
+        Self: Sized,
+        K: PartialOrd,
+        F: FnMut(&Self::Item) -> K,
     {
-        use crate::MinMaxResult::{NoElements, OneElement, MinMax};
+        use crate::MinMaxResult::{MinMax, NoElements, OneElement};
         match self.enumerate().minmax_by_key(|e| key(&e.1)) {
             NoElements => NoElements,
             OneElement(x) => OneElement(x.0),
@@ -3461,9 +4018,11 @@
     ///
     /// [`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
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        use crate::MinMaxResult::{NoElements, OneElement, MinMax};
+        use crate::MinMaxResult::{MinMax, NoElements, OneElement};
         match self.enumerate().minmax_by(|x, y| compare(&x.1, &y.1)) {
             NoElements => NoElements,
             OneElement(x) => OneElement(x.0),
@@ -3493,21 +4052,18 @@
         Self: Sized,
     {
         match self.next() {
-            Some(first) => {
-                match self.next() {
-                    Some(second) => {
-                        Err(ExactlyOneError::new(Some(Either::Left([first, second])), self))
-                    }
-                    None => {
-                        Ok(first)
-                    }
-                }
-            }
+            Some(first) => match self.next() {
+                Some(second) => Err(ExactlyOneError::new(
+                    Some(Either::Left([first, second])),
+                    self,
+                )),
+                None => Ok(first),
+            },
             None => Err(ExactlyOneError::new(None, self)),
         }
     }
 
-    /// If the iterator yields no elements, Ok(None) will be returned. If the iterator yields
+    /// 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.
     ///
@@ -3529,16 +4085,13 @@
         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))
-                    }
-                }
-            }
+            Some(first) => match self.next() {
+                Some(second) => Err(ExactlyOneError::new(
+                    Some(Either::Left([first, second])),
+                    self,
+                )),
+                None => Ok(Some(first)),
+            },
             None => Ok(None),
         }
     }
@@ -3600,7 +4153,7 @@
     ///   first_name: &'static str,
     ///   last_name:  &'static str,
     /// }
-    /// 
+    ///
     /// let characters =
     ///     vec![
     ///         Character { first_name: "Amy",   last_name: "Pond"      },
@@ -3611,12 +4164,12 @@
     ///         Character { first_name: "James", last_name: "Norington" },
     ///         Character { first_name: "James", last_name: "Kirk"      },
     ///     ];
-    /// 
-    /// let first_name_frequency = 
+    ///
+    /// 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);
@@ -3633,11 +4186,11 @@
 
     /// 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
+    /// It 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;
     ///
@@ -3657,9 +4210,33 @@
     {
         MultiUnzip::multiunzip(self)
     }
+
+    /// Returns the length of the iterator if one exists.
+    /// Otherwise return `self.size_hint()`.
+    ///
+    /// Fallible [`ExactSizeIterator::len`].
+    ///
+    /// Inherits guarantees and restrictions from [`Iterator::size_hint`].
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// assert_eq!([0; 10].iter().try_len(), Ok(10));
+    /// assert_eq!((10..15).try_len(), Ok(5));
+    /// assert_eq!((15..10).try_len(), Ok(0));
+    /// assert_eq!((10..).try_len(), Err((usize::MAX, None)));
+    /// assert_eq!((10..15).filter(|x| x % 2 == 0).try_len(), Err((0, Some(5))));
+    /// ```
+    fn try_len(&self) -> Result<usize, size_hint::SizeHint> {
+        let sh = self.size_hint();
+        match sh {
+            (lo, Some(hi)) if lo == hi => Ok(lo),
+            _ => Err(sh),
+        }
+    }
 }
 
-impl<T: ?Sized> Itertools for T where T: Iterator { }
+impl<T> Itertools for T where T: Iterator + ?Sized {}
 
 /// Return `true` if both iterables produce equal sequences
 /// (elements pairwise equal and sequences of the same length),
@@ -3672,9 +4249,10 @@
 /// assert!(!itertools::equal(&[0, 0], &[0, 0, 0]));
 /// ```
 pub fn equal<I, J>(a: I, b: J) -> bool
-    where I: IntoIterator,
-          J: IntoIterator,
-          I::Item: PartialEq<J::Item>
+where
+    I: IntoIterator,
+    J: IntoIterator,
+    I::Item: PartialEq<J::Item>,
 {
     a.into_iter().eq(b)
 }
@@ -3683,31 +4261,38 @@
 /// semantics as [`equal(a, b)`](equal).
 ///
 /// **Panics** on assertion failure with a message that shows the
-/// two iteration elements.
+/// two different elements and the iteration index.
 ///
-/// ```ignore
+/// ```should_panic
+/// # use itertools::assert_equal;
 /// assert_equal("exceed".split('c'), "excess".split('c'));
-/// // ^PANIC: panicked at 'Failed assertion Some("eed") == Some("ess") for iteration 1',
+/// // ^PANIC: panicked at 'Failed assertion Some("eed") == Some("ess") for iteration 1'.
 /// ```
 pub fn assert_equal<I, J>(a: I, b: J)
-    where I: IntoIterator,
-          J: IntoIterator,
-          I::Item: fmt::Debug + PartialEq<J::Item>,
-          J::Item: fmt::Debug,
+where
+    I: IntoIterator,
+    J: IntoIterator,
+    I::Item: fmt::Debug + PartialEq<J::Item>,
+    J::Item: fmt::Debug,
 {
     let mut ia = a.into_iter();
     let mut ib = b.into_iter();
-    let mut i = 0;
+    let mut i: usize = 0;
     loop {
         match (ia.next(), ib.next()) {
             (None, None) => return,
             (a, b) => {
                 let equal = match (&a, &b) {
-                    (&Some(ref a), &Some(ref b)) => a == b,
+                    (Some(a), Some(b)) => a == b,
                     _ => false,
                 };
-                assert!(equal, "Failed assertion {a:?} == {b:?} for iteration {i}",
-                        i=i, a=a, b=b);
+                assert!(
+                    equal,
+                    "Failed assertion {a:?} == {b:?} for iteration {i}",
+                    i = i,
+                    a = a,
+                    b = b
+                );
                 i += 1;
             }
         }
@@ -3732,22 +4317,18 @@
 /// assert_eq!(split_index, 3);
 /// ```
 pub fn partition<'a, A: 'a, I, F>(iter: I, mut pred: F) -> usize
-    where I: IntoIterator<Item = &'a mut A>,
-          I::IntoIter: DoubleEndedIterator,
-          F: FnMut(&A) -> bool
+where
+    I: IntoIterator<Item = &'a mut A>,
+    I::IntoIter: DoubleEndedIterator,
+    F: FnMut(&A) -> bool,
 {
     let mut split_index = 0;
     let mut iter = iter.into_iter();
-    'main: while let Some(front) = iter.next() {
+    while let Some(front) = iter.next() {
         if !pred(front) {
-            loop {
-                match iter.next_back() {
-                    Some(back) => if pred(back) {
-                        std::mem::swap(front, back);
-                        break;
-                    },
-                    None => break 'main,
-                }
+            match iter.rfind(|back| pred(back)) {
+                Some(back) => std::mem::swap(front, back),
+                None => break,
             }
         }
         split_index += 1;
@@ -3770,15 +4351,15 @@
     /// Return the value in the continue or done.
     pub fn into_inner(self) -> T {
         match self {
-            FoldWhile::Continue(x) | FoldWhile::Done(x) => x,
+            Self::Continue(x) | Self::Done(x) => x,
         }
     }
 
     /// Return true if `self` is `Done`, false if it is `Continue`.
     pub fn is_done(&self) -> bool {
         match *self {
-            FoldWhile::Continue(_) => false,
-            FoldWhile::Done(_) => true,
+            Self::Continue(_) => false,
+            Self::Done(_) => true,
         }
     }
 }
diff --git a/crates/itertools/src/merge_join.rs b/crates/itertools/src/merge_join.rs
index f2fbdea..c0de35f 100644
--- a/crates/itertools/src/merge_join.rs
+++ b/crates/itertools/src/merge_join.rs
@@ -1,151 +1,314 @@
 use std::cmp::Ordering;
-use std::iter::Fuse;
 use std::fmt;
+use std::iter::{Fuse, FusedIterator};
+use std::marker::PhantomData;
 
-use super::adaptors::{PutBack, put_back};
+use either::Either;
+
+use super::adaptors::{put_back, PutBack};
 use crate::either_or_both::EitherOrBoth;
+use crate::size_hint::{self, SizeHint};
 #[cfg(doc)]
 use crate::Itertools;
 
+#[derive(Clone, Debug)]
+pub struct MergeLte;
+
+/// An iterator adaptor that merges the two base iterators in ascending order.
+/// If both base iterators are sorted (ascending), the result is sorted.
+///
+/// Iterator element type is `I::Item`.
+///
+/// See [`.merge()`](crate::Itertools::merge_by) for more information.
+pub type Merge<I, J> = MergeBy<I, J, MergeLte>;
+
+/// Create an iterator that merges elements in `i` and `j`.
+///
+/// [`IntoIterator`] enabled version of [`Itertools::merge`](crate::Itertools::merge).
+///
+/// ```
+/// use itertools::merge;
+///
+/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) {
+///     /* loop body */
+/// }
+/// ```
+pub fn merge<I, J>(
+    i: I,
+    j: J,
+) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
+where
+    I: IntoIterator,
+    J: IntoIterator<Item = I::Item>,
+    I::Item: PartialOrd,
+{
+    merge_by_new(i, j, MergeLte)
+}
+
+/// An iterator adaptor that merges the two base iterators in ascending order.
+/// If both base iterators are sorted (ascending), the result is sorted.
+///
+/// Iterator element type is `I::Item`.
+///
+/// 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: Iterator, J: Iterator, F> {
+    left: PutBack<Fuse<I>>,
+    right: PutBack<Fuse<J>>,
+    cmp_fn: F,
+}
+
+/// Create a `MergeBy` iterator.
+pub fn merge_by_new<I, J, F>(a: I, b: J, cmp: F) -> MergeBy<I::IntoIter, J::IntoIter, F>
+where
+    I: IntoIterator,
+    J: IntoIterator<Item = I::Item>,
+{
+    MergeBy {
+        left: put_back(a.into_iter().fuse()),
+        right: put_back(b.into_iter().fuse()),
+        cmp_fn: cmp,
+    }
+}
+
 /// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order.
 ///
 /// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`].
-pub fn merge_join_by<I, J, F>(left: I, right: J, cmp_fn: F)
-    -> MergeJoinBy<I::IntoIter, J::IntoIter, F>
-    where I: IntoIterator,
-          J: IntoIterator,
-          F: FnMut(&I::Item, &J::Item) -> Ordering
+pub fn merge_join_by<I, J, F, T>(
+    left: I,
+    right: J,
+    cmp_fn: F,
+) -> MergeJoinBy<I::IntoIter, J::IntoIter, F>
+where
+    I: IntoIterator,
+    J: IntoIterator,
+    F: FnMut(&I::Item, &J::Item) -> T,
 {
-    MergeJoinBy {
+    MergeBy {
         left: put_back(left.into_iter().fuse()),
         right: put_back(right.into_iter().fuse()),
-        cmp_fn,
+        cmp_fn: MergeFuncLR(cmp_fn, PhantomData),
     }
 }
 
 /// An iterator adaptor that merge-joins items from the two base iterators in ascending order.
 ///
 /// 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>>,
-    right: PutBack<Fuse<J>>,
-    cmp_fn: F
+pub type MergeJoinBy<I, J, F> =
+    MergeBy<I, J, MergeFuncLR<F, <F as FuncLR<<I as Iterator>::Item, <J as Iterator>::Item>>::T>>;
+
+#[derive(Clone, Debug)]
+pub struct MergeFuncLR<F, T>(F, PhantomData<T>);
+
+pub trait FuncLR<L, R> {
+    type T;
 }
 
-impl<I, J, F> Clone for MergeJoinBy<I, J, F>
-    where I: Iterator,
-          J: Iterator,
-          PutBack<Fuse<I>>: Clone,
-          PutBack<Fuse<J>>: Clone,
-          F: Clone,
-{
-    clone_fields!(left, right, cmp_fn);
+impl<L, R, T, F: FnMut(&L, &R) -> T> FuncLR<L, R> for F {
+    type T = T;
 }
 
-impl<I, J, F> fmt::Debug for MergeJoinBy<I, J, F>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
-          J: Iterator + fmt::Debug,
-          J::Item: fmt::Debug,
-{
-    debug_fmt_fields!(MergeJoinBy, left, right);
+pub trait OrderingOrBool<L, R> {
+    type MergeResult;
+    fn left(left: L) -> Self::MergeResult;
+    fn right(right: R) -> Self::MergeResult;
+    // "merge" never returns (Some(...), Some(...), ...) so Option<Either<I::Item, J::Item>>
+    // is appealing but it is always followed by two put_backs, so we think the compiler is
+    // smart enough to optimize it. Or we could move put_backs into "merge".
+    fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult);
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint;
 }
 
-impl<I, J, F> Iterator for MergeJoinBy<I, J, F>
-    where I: Iterator,
-          J: Iterator,
-          F: FnMut(&I::Item, &J::Item) -> Ordering
-{
-    type Item = EitherOrBoth<I::Item, J::Item>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match (self.left.next(), self.right.next()) {
-            (None, None) => None,
-            (Some(left), None) =>
-                Some(EitherOrBoth::Left(left)),
-            (None, Some(right)) =>
-                Some(EitherOrBoth::Right(right)),
-            (Some(left), Some(right)) => {
-                match (self.cmp_fn)(&left, &right) {
-                    Ordering::Equal =>
-                        Some(EitherOrBoth::Both(left, right)),
-                    Ordering::Less => {
-                        self.right.put_back(right);
-                        Some(EitherOrBoth::Left(left))
-                    },
-                    Ordering::Greater => {
-                        self.left.put_back(left);
-                        Some(EitherOrBoth::Right(right))
-                    }
-                }
-            }
+impl<L, R, F: FnMut(&L, &R) -> Ordering> OrderingOrBool<L, R> for MergeFuncLR<F, Ordering> {
+    type MergeResult = EitherOrBoth<L, R>;
+    fn left(left: L) -> Self::MergeResult {
+        EitherOrBoth::Left(left)
+    }
+    fn right(right: R) -> Self::MergeResult {
+        EitherOrBoth::Right(right)
+    }
+    fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult) {
+        match self.0(&left, &right) {
+            Ordering::Equal => (None, EitherOrBoth::Both(left, right)),
+            Ordering::Less => (Some(Either::Right(right)), EitherOrBoth::Left(left)),
+            Ordering::Greater => (Some(Either::Left(left)), EitherOrBoth::Right(right)),
         }
     }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let (a_lower, a_upper) = self.left.size_hint();
-        let (b_lower, b_upper) = self.right.size_hint();
-
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
+        let (a_lower, a_upper) = left;
+        let (b_lower, b_upper) = right;
         let lower = ::std::cmp::max(a_lower, b_lower);
-
         let upper = match (a_upper, b_upper) {
             (Some(x), Some(y)) => x.checked_add(y),
             _ => None,
         };
-
         (lower, upper)
     }
+}
 
-    fn count(mut self) -> usize {
-        let mut count = 0;
-        loop {
-            match (self.left.next(), self.right.next()) {
-                (None, None) => break count,
-                (Some(_left), None) => break count + 1 + self.left.into_parts().1.count(),
-                (None, Some(_right)) => break count + 1 + self.right.into_parts().1.count(),
-                (Some(left), Some(right)) => {
-                    count += 1;
-                    match (self.cmp_fn)(&left, &right) {
-                        Ordering::Equal => {}
-                        Ordering::Less => self.right.put_back(right),
-                        Ordering::Greater => self.left.put_back(left),
+impl<L, R, F: FnMut(&L, &R) -> bool> OrderingOrBool<L, R> for MergeFuncLR<F, bool> {
+    type MergeResult = Either<L, R>;
+    fn left(left: L) -> Self::MergeResult {
+        Either::Left(left)
+    }
+    fn right(right: R) -> Self::MergeResult {
+        Either::Right(right)
+    }
+    fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult) {
+        if self.0(&left, &right) {
+            (Some(Either::Right(right)), Either::Left(left))
+        } else {
+            (Some(Either::Left(left)), Either::Right(right))
+        }
+    }
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
+        // Not ExactSizeIterator because size may be larger than usize
+        size_hint::add(left, right)
+    }
+}
+
+impl<T, F: FnMut(&T, &T) -> bool> OrderingOrBool<T, T> for F {
+    type MergeResult = T;
+    fn left(left: T) -> Self::MergeResult {
+        left
+    }
+    fn right(right: T) -> Self::MergeResult {
+        right
+    }
+    fn merge(&mut self, left: T, right: T) -> (Option<Either<T, T>>, Self::MergeResult) {
+        if self(&left, &right) {
+            (Some(Either::Right(right)), left)
+        } else {
+            (Some(Either::Left(left)), right)
+        }
+    }
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
+        // Not ExactSizeIterator because size may be larger than usize
+        size_hint::add(left, right)
+    }
+}
+
+impl<T: PartialOrd> OrderingOrBool<T, T> for MergeLte {
+    type MergeResult = T;
+    fn left(left: T) -> Self::MergeResult {
+        left
+    }
+    fn right(right: T) -> Self::MergeResult {
+        right
+    }
+    fn merge(&mut self, left: T, right: T) -> (Option<Either<T, T>>, Self::MergeResult) {
+        if left <= right {
+            (Some(Either::Right(right)), left)
+        } else {
+            (Some(Either::Left(left)), right)
+        }
+    }
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
+        // Not ExactSizeIterator because size may be larger than usize
+        size_hint::add(left, right)
+    }
+}
+
+impl<I, J, F> Clone for MergeBy<I, J, F>
+where
+    I: Iterator,
+    J: Iterator,
+    PutBack<Fuse<I>>: Clone,
+    PutBack<Fuse<J>>: Clone,
+    F: Clone,
+{
+    clone_fields!(left, right, cmp_fn);
+}
+
+impl<I, J, F> fmt::Debug for MergeBy<I, J, F>
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
+    J: Iterator + fmt::Debug,
+    J::Item: fmt::Debug,
+{
+    debug_fmt_fields!(MergeBy, left, right);
+}
+
+impl<I, J, F> Iterator for MergeBy<I, J, F>
+where
+    I: Iterator,
+    J: Iterator,
+    F: OrderingOrBool<I::Item, J::Item>,
+{
+    type Item = F::MergeResult;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match (self.left.next(), self.right.next()) {
+            (None, None) => None,
+            (Some(left), None) => Some(F::left(left)),
+            (None, Some(right)) => Some(F::right(right)),
+            (Some(left), Some(right)) => {
+                let (not_next, next) = self.cmp_fn.merge(left, right);
+                match not_next {
+                    Some(Either::Left(l)) => {
+                        self.left.put_back(l);
                     }
+                    Some(Either::Right(r)) => {
+                        self.right.put_back(r);
+                    }
+                    None => (),
                 }
+
+                Some(next)
             }
         }
     }
 
-    fn last(mut self) -> Option<Self::Item> {
-        let mut previous_element = None;
+    fn fold<B, G>(mut self, init: B, mut f: G) -> B
+    where
+        Self: Sized,
+        G: FnMut(B, Self::Item) -> B,
+    {
+        let mut acc = init;
+        let mut left = self.left.next();
+        let mut right = self.right.next();
+
         loop {
-            match (self.left.next(), self.right.next()) {
-                (None, None) => break previous_element,
-                (Some(left), None) => {
-                    break Some(EitherOrBoth::Left(
-                        self.left.into_parts().1.last().unwrap_or(left),
-                    ))
-                }
-                (None, Some(right)) => {
-                    break Some(EitherOrBoth::Right(
-                        self.right.into_parts().1.last().unwrap_or(right),
-                    ))
-                }
-                (Some(left), Some(right)) => {
-                    previous_element = match (self.cmp_fn)(&left, &right) {
-                        Ordering::Equal => Some(EitherOrBoth::Both(left, right)),
-                        Ordering::Less => {
-                            self.right.put_back(right);
-                            Some(EitherOrBoth::Left(left))
-                        }
-                        Ordering::Greater => {
-                            self.left.put_back(left);
-                            Some(EitherOrBoth::Right(right))
-                        }
+            match (left, right) {
+                (Some(l), Some(r)) => match self.cmp_fn.merge(l, r) {
+                    (Some(Either::Right(r)), x) => {
+                        acc = f(acc, x);
+                        left = self.left.next();
+                        right = Some(r);
                     }
+                    (Some(Either::Left(l)), x) => {
+                        acc = f(acc, x);
+                        left = Some(l);
+                        right = self.right.next();
+                    }
+                    (None, x) => {
+                        acc = f(acc, x);
+                        left = self.left.next();
+                        right = self.right.next();
+                    }
+                },
+                (Some(l), None) => {
+                    self.left.put_back(l);
+                    acc = self.left.fold(acc, |acc, x| f(acc, F::left(x)));
+                    break;
+                }
+                (None, Some(r)) => {
+                    self.right.put_back(r);
+                    acc = self.right.fold(acc, |acc, x| f(acc, F::right(x)));
+                    break;
+                }
+                (None, None) => {
+                    break;
                 }
             }
         }
+
+        acc
+    }
+
+    fn size_hint(&self) -> SizeHint {
+        F::size_hint(self.left.size_hint(), self.right.size_hint())
     }
 
     fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
@@ -156,14 +319,29 @@
             n -= 1;
             match (self.left.next(), self.right.next()) {
                 (None, None) => break None,
-                (Some(_left), None) => break self.left.nth(n).map(EitherOrBoth::Left),
-                (None, Some(_right)) => break self.right.nth(n).map(EitherOrBoth::Right),
-                (Some(left), Some(right)) => match (self.cmp_fn)(&left, &right) {
-                    Ordering::Equal => {}
-                    Ordering::Less => self.right.put_back(right),
-                    Ordering::Greater => self.left.put_back(left),
-                },
+                (Some(_left), None) => break self.left.nth(n).map(F::left),
+                (None, Some(_right)) => break self.right.nth(n).map(F::right),
+                (Some(left), Some(right)) => {
+                    let (not_next, _) = self.cmp_fn.merge(left, right);
+                    match not_next {
+                        Some(Either::Left(l)) => {
+                            self.left.put_back(l);
+                        }
+                        Some(Either::Right(r)) => {
+                            self.right.put_back(r);
+                        }
+                        None => (),
+                    }
+                }
             }
         }
     }
 }
+
+impl<I, J, F> FusedIterator for MergeBy<I, J, F>
+where
+    I: Iterator,
+    J: Iterator,
+    F: OrderingOrBool<I::Item, J::Item>,
+{
+}
diff --git a/crates/itertools/src/minmax.rs b/crates/itertools/src/minmax.rs
index 52b2f11..5c9674e 100644
--- a/crates/itertools/src/minmax.rs
+++ b/crates/itertools/src/minmax.rs
@@ -1,8 +1,7 @@
-
 /// `MinMaxResult` is an enum returned by `minmax`.
 ///
 /// See [`.minmax()`](crate::Itertools::minmax) for more detail.
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum MinMaxResult<T> {
     /// Empty iterator
     NoElements,
@@ -12,7 +11,7 @@
 
     /// More than one element in the iterator, the first element is not larger
     /// than the second
-    MinMax(T, T)
+    MinMax(T, T),
 }
 
 impl<T: Clone> MinMaxResult<T> {
@@ -36,34 +35,36 @@
     /// let r = MinMax(1, 2);
     /// assert_eq!(r.into_option(), Some((1, 2)));
     /// ```
-    pub fn into_option(self) -> Option<(T,T)> {
+    pub fn into_option(self) -> Option<(T, T)> {
         match self {
-            MinMaxResult::NoElements => None,
-            MinMaxResult::OneElement(x) => Some((x.clone(), x)),
-            MinMaxResult::MinMax(x, y) => Some((x, y))
+            Self::NoElements => None,
+            Self::OneElement(x) => Some((x.clone(), x)),
+            Self::MinMax(x, y) => Some((x, y)),
         }
     }
 }
 
 /// Implementation guts for `minmax` and `minmax_by_key`.
-pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F,
-                               mut lt: L) -> MinMaxResult<I::Item>
-    where I: Iterator,
-          F: FnMut(&I::Item) -> K,
-          L: FnMut(&I::Item, &I::Item, &K, &K) -> bool,
+pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F, mut lt: L) -> MinMaxResult<I::Item>
+where
+    I: Iterator,
+    F: FnMut(&I::Item) -> K,
+    L: FnMut(&I::Item, &I::Item, &K, &K) -> bool,
 {
     let (mut min, mut max, mut min_key, mut max_key) = match it.next() {
         None => return MinMaxResult::NoElements,
-        Some(x) => {
-            match it.next() {
-                None => return MinMaxResult::OneElement(x),
-                Some(y) => {
-                    let xk = key_for(&x);
-                    let yk = key_for(&y);
-                    if !lt(&y, &x, &yk, &xk) {(x, y, xk, yk)} else {(y, x, yk, xk)}
+        Some(x) => match it.next() {
+            None => return MinMaxResult::OneElement(x),
+            Some(y) => {
+                let xk = key_for(&x);
+                let yk = key_for(&y);
+                if !lt(&y, &x, &yk, &xk) {
+                    (x, y, xk, yk)
+                } else {
+                    (y, x, yk, xk)
                 }
             }
-        }
+        },
     };
 
     loop {
@@ -74,7 +75,7 @@
         // for 2 elements.
         let first = match it.next() {
             None => break,
-            Some(x) => x
+            Some(x) => x,
         };
         let second = match it.next() {
             None => {
@@ -86,7 +87,7 @@
                 }
                 break;
             }
-            Some(x) => x
+            Some(x) => x,
         };
         let first_key = key_for(&first);
         let second_key = key_for(&second);
diff --git a/crates/itertools/src/multipeek_impl.rs b/crates/itertools/src/multipeek_impl.rs
index 8b49c69..6f800b6 100644
--- a/crates/itertools/src/multipeek_impl.rs
+++ b/crates/itertools/src/multipeek_impl.rs
@@ -1,14 +1,16 @@
-use std::iter::Fuse;
-use alloc::collections::VecDeque;
 use crate::size_hint;
-use crate::PeekingNext;
 #[cfg(doc)]
 use crate::Itertools;
+use crate::PeekingNext;
+use alloc::collections::VecDeque;
+use std::iter::Fuse;
 
 /// See [`multipeek()`] for more information.
 #[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct MultiPeek<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     iter: Fuse<I>,
     buf: VecDeque<I::Item>,
@@ -20,7 +22,8 @@
 ///
 /// [`IntoIterator`] enabled version of [`Itertools::multipeek`].
 pub fn multipeek<I>(iterable: I) -> MultiPeek<I::IntoIter>
-    where I: IntoIterator
+where
+    I: IntoIterator,
 {
     MultiPeek {
         iter: iterable.into_iter().fuse(),
@@ -30,7 +33,8 @@
 }
 
 impl<I> MultiPeek<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     /// Reset the peeking “cursor”
     pub fn reset_peek(&mut self) {
@@ -62,24 +66,31 @@
 }
 
 impl<I> PeekingNext for MultiPeek<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool
+    where
+        F: FnOnce(&Self::Item) -> bool,
     {
         if self.buf.is_empty() {
             if let Some(r) = self.peek() {
-                if !accept(r) { return None }
+                if !accept(r) {
+                    return None;
+                }
             }
-        } else if let Some(r) = self.buf.get(0) {
-            if !accept(r) { return None }
+        } else if let Some(r) = self.buf.front() {
+            if !accept(r) {
+                return None;
+            }
         }
         self.next()
     }
 }
 
 impl<I> Iterator for MultiPeek<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     type Item = I::Item;
 
@@ -91,11 +102,15 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         size_hint::add_scalar(self.iter.size_hint(), self.buf.len())
     }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        init = self.buf.into_iter().fold(init, &mut f);
+        self.iter.fold(init, f)
+    }
 }
 
 // Same size
-impl<I> ExactSizeIterator for MultiPeek<I>
-    where I: ExactSizeIterator
-{}
-
-
+impl<I> ExactSizeIterator for MultiPeek<I> where I: ExactSizeIterator {}
diff --git a/crates/itertools/src/pad_tail.rs b/crates/itertools/src/pad_tail.rs
index 248a432..5595b42 100644
--- a/crates/itertools/src/pad_tail.rs
+++ b/crates/itertools/src/pad_tail.rs
@@ -1,5 +1,5 @@
-use std::iter::{Fuse, FusedIterator};
 use crate::size_hint;
+use std::iter::{Fuse, FusedIterator};
 
 /// An iterator adaptor that pads a sequence to a minimum length by filling
 /// missing elements using a closure.
@@ -25,8 +25,9 @@
 
 /// Create a new `PadUsing` iterator.
 pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
-    where I: Iterator,
-          F: FnMut(usize) -> I::Item
+where
+    I: Iterator,
+    F: FnMut(usize) -> I::Item,
 {
     PadUsing {
         iter: iter.fuse(),
@@ -37,8 +38,9 @@
 }
 
 impl<I, F> Iterator for PadUsing<I, F>
-    where I: Iterator,
-          F: FnMut(usize) -> I::Item
+where
+    I: Iterator,
+    F: FnMut(usize) -> I::Item,
 {
     type Item = I::Item;
 
@@ -53,7 +55,7 @@
                 } else {
                     None
                 }
-            },
+            }
             e => {
                 self.pos += 1;
                 e
@@ -65,11 +67,24 @@
         let tail = self.min.saturating_sub(self.pos);
         size_hint::max(self.iter.size_hint(), (tail, Some(tail)))
     }
+
+    fn fold<B, G>(self, mut init: B, mut f: G) -> B
+    where
+        G: FnMut(B, Self::Item) -> B,
+    {
+        let mut pos = self.pos;
+        init = self.iter.fold(init, |acc, item| {
+            pos += 1;
+            f(acc, item)
+        });
+        (pos..self.min).map(self.filler).fold(init, f)
+    }
 }
 
 impl<I, F> DoubleEndedIterator for PadUsing<I, F>
-    where I: DoubleEndedIterator + ExactSizeIterator,
-          F: FnMut(usize) -> I::Item
+where
+    I: DoubleEndedIterator + ExactSizeIterator,
+    F: FnMut(usize) -> I::Item,
 {
     fn next_back(&mut self) -> Option<Self::Item> {
         if self.min == 0 {
@@ -82,15 +97,28 @@
             Some((self.filler)(self.min))
         }
     }
+
+    fn rfold<B, G>(self, mut init: B, mut f: G) -> B
+    where
+        G: FnMut(B, Self::Item) -> B,
+    {
+        init = (self.iter.len()..self.min)
+            .map(self.filler)
+            .rfold(init, &mut f);
+        self.iter.rfold(init, f)
+    }
 }
 
 impl<I, F> ExactSizeIterator for PadUsing<I, F>
-    where I: ExactSizeIterator,
-          F: FnMut(usize) -> I::Item
-{}
-
+where
+    I: ExactSizeIterator,
+    F: FnMut(usize) -> I::Item,
+{
+}
 
 impl<I, F> FusedIterator for PadUsing<I, F>
-    where I: FusedIterator,
-          F: FnMut(usize) -> I::Item
-{}
+where
+    I: FusedIterator,
+    F: FnMut(usize) -> I::Item,
+{
+}
diff --git a/crates/itertools/src/peek_nth.rs b/crates/itertools/src/peek_nth.rs
index bcca458..b03a3ef 100644
--- a/crates/itertools/src/peek_nth.rs
+++ b/crates/itertools/src/peek_nth.rs
@@ -5,6 +5,7 @@
 
 /// See [`peek_nth()`] for more information.
 #[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct PeekNth<I>
 where
     I: Iterator,
@@ -34,30 +35,35 @@
 where
     I: Iterator,
 {
-    /// Works exactly like the `peek` method in `std::iter::Peekable`
+    /// Works exactly like the `peek` method in [`std::iter::Peekable`].
     pub fn peek(&mut self) -> Option<&I::Item> {
         self.peek_nth(0)
     }
 
+    /// Works exactly like the `peek_mut` method in [`std::iter::Peekable`].
+    pub fn peek_mut(&mut self) -> Option<&mut I::Item> {
+        self.peek_nth_mut(0)
+    }
+
     /// Returns a reference to the `nth` value without advancing the iterator.
     ///
     /// # Examples
     ///
     /// Basic usage:
     ///
-    /// ```rust
+    /// ```
     /// use itertools::peek_nth;
     ///
-    /// let xs = vec![1,2,3];
-    /// let mut iter = peek_nth(xs.iter());
+    /// let xs = vec![1, 2, 3];
+    /// let mut iter = peek_nth(xs.into_iter());
     ///
-    /// assert_eq!(iter.peek_nth(0), Some(&&1));
-    /// assert_eq!(iter.next(), Some(&1));
+    /// assert_eq!(iter.peek_nth(0), Some(&1));
+    /// assert_eq!(iter.next(), Some(1));
     ///
     /// // The iterator does not advance even if we call `peek_nth` multiple times
-    /// assert_eq!(iter.peek_nth(0), Some(&&2));
-    /// assert_eq!(iter.peek_nth(1), Some(&&3));
-    /// assert_eq!(iter.next(), Some(&2));
+    /// assert_eq!(iter.peek_nth(0), Some(&2));
+    /// assert_eq!(iter.peek_nth(1), Some(&3));
+    /// assert_eq!(iter.next(), Some(2));
     ///
     /// // Calling `peek_nth` past the end of the iterator will return `None`
     /// assert_eq!(iter.peek_nth(1), None);
@@ -69,6 +75,68 @@
 
         self.buf.get(n)
     }
+
+    /// Returns a mutable reference to the `nth` value without advancing the iterator.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// use itertools::peek_nth;
+    ///
+    /// let xs = vec![1, 2, 3, 4, 5];
+    /// let mut iter = peek_nth(xs.into_iter());
+    ///
+    /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 1));
+    /// assert_eq!(iter.next(), Some(1));
+    ///
+    /// // The iterator does not advance even if we call `peek_nth_mut` multiple times
+    /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 2));
+    /// assert_eq!(iter.peek_nth_mut(1), Some(&mut 3));
+    /// assert_eq!(iter.next(), Some(2));
+    ///
+    /// // Peek into the iterator and set the value behind the mutable reference.
+    /// if let Some(p) = iter.peek_nth_mut(1) {
+    ///     assert_eq!(*p, 4);
+    ///     *p = 9;
+    /// }
+    ///
+    /// // The value we put in reappears as the iterator continues.
+    /// assert_eq!(iter.next(), Some(3));
+    /// assert_eq!(iter.next(), Some(9));
+    ///
+    /// // Calling `peek_nth_mut` past the end of the iterator will return `None`
+    /// assert_eq!(iter.peek_nth_mut(1), None);
+    /// ```
+    pub fn peek_nth_mut(&mut self, n: usize) -> Option<&mut I::Item> {
+        let unbuffered_items = (n + 1).saturating_sub(self.buf.len());
+
+        self.buf.extend(self.iter.by_ref().take(unbuffered_items));
+
+        self.buf.get_mut(n)
+    }
+
+    /// Works exactly like the `next_if` method in [`std::iter::Peekable`].
+    pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
+        match self.next() {
+            Some(item) if func(&item) => Some(item),
+            Some(item) => {
+                self.buf.push_front(item);
+                None
+            }
+            _ => None,
+        }
+    }
+
+    /// Works exactly like the `next_if_eq` method in [`std::iter::Peekable`].
+    pub fn next_if_eq<T>(&mut self, expected: &T) -> Option<I::Item>
+    where
+        T: ?Sized,
+        I::Item: PartialEq<T>,
+    {
+        self.next_if(|next| next == expected)
+    }
 }
 
 impl<I> Iterator for PeekNth<I>
@@ -84,6 +152,14 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         size_hint::add_scalar(self.iter.size_hint(), self.buf.len())
     }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        init = self.buf.into_iter().fold(init, &mut f);
+        self.iter.fold(init, f)
+    }
 }
 
 impl<I> ExactSizeIterator for PeekNth<I> where I: ExactSizeIterator {}
diff --git a/crates/itertools/src/peeking_take_while.rs b/crates/itertools/src/peeking_take_while.rs
index b3a9c5c..19872a9 100644
--- a/crates/itertools/src/peeking_take_while.rs
+++ b/crates/itertools/src/peeking_take_while.rs
@@ -1,7 +1,8 @@
-use std::iter::Peekable;
 use crate::PutBack;
 #[cfg(feature = "use_alloc")]
 use crate::PutBackN;
+use crate::RepeatN;
+use std::iter::Peekable;
 
 /// An iterator that allows peeking at an element before deciding to accept it.
 ///
@@ -10,20 +11,36 @@
 ///
 /// This is implemented by peeking adaptors like peekable and put back,
 /// but also by a few iterators that can be peeked natively, like the slice’s
-/// by reference iterator (`std::slice::Iter`).
-pub trait PeekingNext : Iterator {
+/// by reference iterator ([`std::slice::Iter`]).
+pub trait PeekingNext: Iterator {
     /// Pass a reference to the next iterator element to the closure `accept`;
-    /// if `accept` returns true, return it as the next element,
-    /// else None.
+    /// if `accept` returns `true`, return it as the next element,
+    /// else `None`.
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool;
+    where
+        Self: Sized,
+        F: FnOnce(&Self::Item) -> bool;
+}
+
+impl<'a, I> PeekingNext for &'a mut I
+where
+    I: PeekingNext,
+{
+    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
+    where
+        F: FnOnce(&Self::Item) -> bool,
+    {
+        (*self).peeking_next(accept)
+    }
 }
 
 impl<I> PeekingNext for Peekable<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool
+    where
+        F: FnOnce(&Self::Item) -> bool,
     {
         if let Some(r) = self.peek() {
             if !accept(r) {
@@ -35,10 +52,12 @@
 }
 
 impl<I> PeekingNext for PutBack<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool
+    where
+        F: FnOnce(&Self::Item) -> bool,
     {
         if let Some(r) = self.next() {
             if !accept(&r) {
@@ -54,10 +73,12 @@
 
 #[cfg(feature = "use_alloc")]
 impl<I> PeekingNext for PutBackN<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool
+    where
+        F: FnOnce(&Self::Item) -> bool,
     {
         if let Some(r) = self.next() {
             if !accept(&r) {
@@ -71,39 +92,51 @@
     }
 }
 
+impl<T: Clone> PeekingNext for RepeatN<T> {
+    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
+    where
+        F: FnOnce(&Self::Item) -> bool,
+    {
+        let r = self.elt.as_ref()?;
+        if !accept(r) {
+            return None;
+        }
+        self.next()
+    }
+}
+
 /// An iterator adaptor that takes items while a closure returns `true`.
 ///
 /// 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>
-    where I: Iterator,
+pub struct PeekingTakeWhile<'a, I, F>
+where
+    I: Iterator + 'a,
 {
     iter: &'a mut I,
     f: F,
 }
 
-impl<'a, I: 'a, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F>
+impl<'a, I, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F>
 where
-    I: Iterator + std::fmt::Debug,
+    I: Iterator + std::fmt::Debug + 'a,
 {
     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,
+where
+    I: Iterator,
 {
-    PeekingTakeWhile {
-        iter,
-        f,
-    }
+    PeekingTakeWhile { iter, f }
 }
 
 impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F>
-    where I: PeekingNext,
-          F: FnMut(&I::Item) -> bool,
-
+where
+    I: PeekingNext,
+    F: FnMut(&I::Item) -> bool,
 {
     type Item = I::Item;
     fn next(&mut self) -> Option<Self::Item> {
@@ -115,6 +148,20 @@
     }
 }
 
+impl<'a, I, F> PeekingNext for PeekingTakeWhile<'a, I, F>
+where
+    I: PeekingNext,
+    F: FnMut(&I::Item) -> bool,
+{
+    fn peeking_next<G>(&mut self, g: G) -> Option<Self::Item>
+    where
+        G: FnOnce(&Self::Item) -> bool,
+    {
+        let f = &mut self.f;
+        self.iter.peeking_next(|r| f(r) && g(r))
+    }
+}
+
 // Some iterators are so lightweight we can simply clone them to save their
 // state and use that for peeking.
 macro_rules! peeking_next_by_clone {
@@ -151,4 +198,4 @@
 
 // cloning a Rev has no extra overhead; peekable and put backs are never DEI.
 peeking_next_by_clone! { [I: Clone + PeekingNext + DoubleEndedIterator]
-                         ::std::iter::Rev<I> }
+::std::iter::Rev<I> }
diff --git a/crates/itertools/src/permutations.rs b/crates/itertools/src/permutations.rs
index d03b852..91389a7 100644
--- a/crates/itertools/src/permutations.rs
+++ b/crates/itertools/src/permutations.rs
@@ -1,8 +1,11 @@
+use alloc::boxed::Box;
 use alloc::vec::Vec;
 use std::fmt;
 use std::iter::once;
+use std::iter::FusedIterator;
 
 use super::lazy_buffer::LazyBuffer;
+use crate::size_hint::{self, SizeHint};
 
 /// An iterator adaptor that iterates through all the `k`-permutations of the
 /// elements from an iterator.
@@ -16,262 +19,168 @@
 }
 
 impl<I> Clone for Permutations<I>
-    where I: Clone + Iterator,
-          I::Item: Clone,
+where
+    I: Clone + Iterator,
+    I::Item: Clone,
 {
     clone_fields!(vals, state);
 }
 
 #[derive(Clone, Debug)]
 enum PermutationState {
-    StartUnknownLen {
-        k: usize,
+    /// No permutation generated yet.
+    Start { k: usize },
+    /// Values from the iterator are not fully loaded yet so `n` is still unknown.
+    Buffered { k: usize, min_n: usize },
+    /// All values from the iterator are known so `n` is known.
+    Loaded {
+        indices: Box<[usize]>,
+        cycles: Box<[usize]>,
     },
-    OngoingUnknownLen {
-        k: usize,
-        min_n: usize,
-    },
-    Complete(CompleteState),
-    Empty,
-}
-
-#[derive(Clone, Debug)]
-enum CompleteState {
-    Start {
-        n: usize,
-        k: usize,
-    },
-    Ongoing {
-        indices: Vec<usize>,
-        cycles: Vec<usize>,
-    }
-}
-
-enum CompleteStateRemaining {
-    Known(usize),
-    Overflow,
+    /// No permutation left to generate.
+    End,
 }
 
 impl<I> fmt::Debug for Permutations<I>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
 {
     debug_fmt_fields!(Permutations, vals, state);
 }
 
 pub fn permutations<I: Iterator>(iter: I, k: usize) -> Permutations<I> {
-    let mut vals = LazyBuffer::new(iter);
-
-    if k == 0 {
-        // Special case, yields single empty vec; `n` is irrelevant
-        let state = PermutationState::Complete(CompleteState::Start { n: 0, k: 0 });
-
-        return Permutations {
-            vals,
-            state
-        };
-    }
-
-    let mut enough_vals = true;
-
-    while vals.len() < k {
-        if !vals.get_next() {
-            enough_vals = false;
-            break;
-        }
-    }
-
-    let state = if enough_vals {
-        PermutationState::StartUnknownLen { k }
-    } else {
-        PermutationState::Empty
-    };
-
     Permutations {
-        vals,
-        state
+        vals: LazyBuffer::new(iter),
+        state: PermutationState::Start { k },
     }
 }
 
 impl<I> Iterator for Permutations<I>
 where
     I: Iterator,
-    I::Item: Clone
+    I::Item: Clone,
 {
     type Item = Vec<I::Item>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        self.advance();
-
-        let &mut Permutations { ref vals, ref state } = self;
-
-        match *state {
-            PermutationState::StartUnknownLen { .. } => panic!("unexpected iterator state"),
-            PermutationState::OngoingUnknownLen { k, min_n } => {
-                let latest_idx = min_n - 1;
-                let indices = (0..(k - 1)).chain(once(latest_idx));
-
-                Some(indices.map(|i| vals[i].clone()).collect())
+        let Self { vals, state } = self;
+        match state {
+            PermutationState::Start { k: 0 } => {
+                *state = PermutationState::End;
+                Some(Vec::new())
             }
-            PermutationState::Complete(CompleteState::Ongoing { ref indices, ref cycles }) => {
+            &mut PermutationState::Start { k } => {
+                vals.prefill(k);
+                if vals.len() != k {
+                    *state = PermutationState::End;
+                    return None;
+                }
+                *state = PermutationState::Buffered { k, min_n: k };
+                Some(vals[0..k].to_vec())
+            }
+            PermutationState::Buffered { ref k, min_n } => {
+                if vals.get_next() {
+                    let item = (0..*k - 1)
+                        .chain(once(*min_n))
+                        .map(|i| vals[i].clone())
+                        .collect();
+                    *min_n += 1;
+                    Some(item)
+                } else {
+                    let n = *min_n;
+                    let prev_iteration_count = n - *k + 1;
+                    let mut indices: Box<[_]> = (0..n).collect();
+                    let mut cycles: Box<[_]> = (n - k..n).rev().collect();
+                    // Advance the state to the correct point.
+                    for _ in 0..prev_iteration_count {
+                        if advance(&mut indices, &mut cycles) {
+                            *state = PermutationState::End;
+                            return None;
+                        }
+                    }
+                    let item = vals.get_at(&indices[0..*k]);
+                    *state = PermutationState::Loaded { indices, cycles };
+                    Some(item)
+                }
+            }
+            PermutationState::Loaded { indices, cycles } => {
+                if advance(indices, cycles) {
+                    *state = PermutationState::End;
+                    return None;
+                }
                 let k = cycles.len();
-                Some(indices[0..k].iter().map(|&i| vals[i].clone()).collect())
-            },
-            PermutationState::Complete(CompleteState::Start { .. }) | PermutationState::Empty => None
+                Some(vals.get_at(&indices[0..k]))
+            }
+            PermutationState::End => None,
         }
     }
 
     fn count(self) -> usize {
-        fn from_complete(complete_state: CompleteState) -> usize {
-            match complete_state.remaining() {
-                CompleteStateRemaining::Known(count) => count,
-                CompleteStateRemaining::Overflow => {
-                    panic!("Iterator count greater than usize::MAX");
-                }
-            }
-        }
-
-        let Permutations { vals, state } = self;
-        match state {
-            PermutationState::StartUnknownLen { k } => {
-                let n = vals.len() + vals.it.count();
-                let complete_state = CompleteState::Start { n, k };
-
-                from_complete(complete_state)
-            }
-            PermutationState::OngoingUnknownLen { k, min_n } => {
-                let prev_iteration_count = min_n - k + 1;
-                let n = vals.len() + vals.it.count();
-                let complete_state = CompleteState::Start { n, k };
-
-                from_complete(complete_state) - prev_iteration_count
-            },
-            PermutationState::Complete(state) => from_complete(state),
-            PermutationState::Empty => 0
-        }
+        let Self { vals, state } = self;
+        let n = vals.count();
+        state.size_hint_for(n).1.unwrap()
     }
 
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        match self.state {
-            PermutationState::StartUnknownLen { .. } |
-            PermutationState::OngoingUnknownLen { .. } => (0, None), // TODO can we improve this lower bound?
-            PermutationState::Complete(ref state) => match state.remaining() {
-                CompleteStateRemaining::Known(count) => (count, Some(count)),
-                CompleteStateRemaining::Overflow => (::std::usize::MAX, None)
-            }
-            PermutationState::Empty => (0, Some(0))
-        }
+    fn size_hint(&self) -> SizeHint {
+        let (mut low, mut upp) = self.vals.size_hint();
+        low = self.state.size_hint_for(low).0;
+        upp = upp.and_then(|n| self.state.size_hint_for(n).1);
+        (low, upp)
     }
 }
 
-impl<I> Permutations<I>
+impl<I> FusedIterator for Permutations<I>
 where
     I: Iterator,
-    I::Item: Clone
+    I::Item: Clone,
 {
-    fn advance(&mut self) {
-        let &mut Permutations { ref mut vals, ref mut state } = self;
-
-        *state = match *state {
-            PermutationState::StartUnknownLen { k } => {
-                PermutationState::OngoingUnknownLen { k, min_n: k }
-            }
-            PermutationState::OngoingUnknownLen { k, min_n } => {
-                if vals.get_next() {
-                    PermutationState::OngoingUnknownLen { k, min_n: min_n + 1 }
-                } else {
-                    let n = min_n;
-                    let prev_iteration_count = n - k + 1;
-                    let mut complete_state = CompleteState::Start { n, k };
-
-                    // Advance the complete-state iterator to the correct point
-                    for _ in 0..(prev_iteration_count + 1) {
-                        complete_state.advance();
-                    }
-
-                    PermutationState::Complete(complete_state)
-                }
-            }
-            PermutationState::Complete(ref mut state) => {
-                state.advance();
-
-                return;
-            }
-            PermutationState::Empty => { return; }
-        };
-    }
 }
 
-impl CompleteState {
-    fn advance(&mut self) {
-        *self = match *self {
-            CompleteState::Start { n, k } => {
-                let indices = (0..n).collect();
-                let cycles = ((n - k)..n).rev().collect();
-
-                CompleteState::Ongoing {
-                    cycles,
-                    indices
-                }
-            },
-            CompleteState::Ongoing { ref mut indices, ref mut cycles } => {
-                let n = indices.len();
-                let k = cycles.len();
-
-                for i in (0..k).rev() {
-                    if cycles[i] == 0 {
-                        cycles[i] = n - i - 1;
-
-                        let to_push = indices.remove(i);
-                        indices.push(to_push);
-                    } else {
-                        let swap_index = n - cycles[i];
-                        indices.swap(i, swap_index);
-
-                        cycles[i] -= 1;
-                        return;
-                    }
-                }
-
-                CompleteState::Start { n, k }
-            }
+fn advance(indices: &mut [usize], cycles: &mut [usize]) -> bool {
+    let n = indices.len();
+    let k = cycles.len();
+    // NOTE: if `cycles` are only zeros, then we reached the last permutation.
+    for i in (0..k).rev() {
+        if cycles[i] == 0 {
+            cycles[i] = n - i - 1;
+            indices[i..].rotate_left(1);
+        } else {
+            let swap_index = n - cycles[i];
+            indices.swap(i, swap_index);
+            cycles[i] -= 1;
+            return false;
         }
     }
+    true
+}
 
-    fn remaining(&self) -> CompleteStateRemaining {
-        use self::CompleteStateRemaining::{Known, Overflow};
-
+impl PermutationState {
+    fn size_hint_for(&self, n: usize) -> SizeHint {
+        // At the beginning, there are `n!/(n-k)!` items to come.
+        let at_start = |n, k| {
+            debug_assert!(n >= k);
+            let total = (n - k + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i));
+            (total.unwrap_or(usize::MAX), total)
+        };
         match *self {
-            CompleteState::Start { n, k } => {
-                if n < k {
-                    return Known(0);
-                }
-
-                let count: Option<usize> = (n - k + 1..n + 1).fold(Some(1), |acc, i| {
-                    acc.and_then(|acc| acc.checked_mul(i))
+            Self::Start { k } if n < k => (0, Some(0)),
+            Self::Start { k } => at_start(n, k),
+            Self::Buffered { k, min_n } => {
+                // Same as `Start` minus the previously generated items.
+                size_hint::sub_scalar(at_start(n, k), min_n - k + 1)
+            }
+            Self::Loaded {
+                ref indices,
+                ref cycles,
+            } => {
+                let count = cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| {
+                    acc.checked_mul(indices.len() - i)
+                        .and_then(|count| count.checked_add(c))
                 });
-
-                match count {
-                    Some(count) => Known(count),
-                    None => Overflow
-                }
+                (count.unwrap_or(usize::MAX), count)
             }
-            CompleteState::Ongoing { ref indices, ref cycles } => {
-                let mut count: usize = 0;
-
-                for (i, &c) in cycles.iter().enumerate() {
-                    let radix = indices.len() - i;
-                    let next_count = count.checked_mul(radix)
-                        .and_then(|count| count.checked_add(c));
-
-                    count = match next_count {
-                        Some(count) => count,
-                        None => { return Overflow; }
-                    };
-                }
-
-                Known(count)
-            }
+            Self::End => (0, Some(0)),
         }
     }
 }
diff --git a/crates/itertools/src/powerset.rs b/crates/itertools/src/powerset.rs
index 4d7685b..734eaf6 100644
--- a/crates/itertools/src/powerset.rs
+++ b/crates/itertools/src/powerset.rs
@@ -1,10 +1,10 @@
+use alloc::vec::Vec;
 use std::fmt;
 use std::iter::FusedIterator;
-use std::usize;
-use alloc::vec::Vec;
 
-use super::combinations::{Combinations, combinations};
-use super::size_hint;
+use super::combinations::{combinations, Combinations};
+use crate::adaptors::checked_binomial;
+use crate::size_hint::{self, SizeHint};
 
 /// An iterator to iterate through the powerset of the elements from an iterator.
 ///
@@ -13,78 +13,119 @@
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Powerset<I: Iterator> {
     combs: Combinations<I>,
-    // Iterator `position` (equal to count of yielded elements).
-    pos: usize,
 }
 
 impl<I> Clone for Powerset<I>
-    where I: Clone + Iterator,
-          I::Item: Clone,
+where
+    I: Clone + Iterator,
+    I::Item: Clone,
 {
-    clone_fields!(combs, pos);
+    clone_fields!(combs);
 }
 
 impl<I> fmt::Debug for Powerset<I>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
 {
-    debug_fmt_fields!(Powerset, combs, pos);
+    debug_fmt_fields!(Powerset, combs);
 }
 
 /// Create a new `Powerset` from a clonable iterator.
 pub fn powerset<I>(src: I) -> Powerset<I>
-    where I: Iterator,
-          I::Item: Clone,
+where
+    I: Iterator,
+    I::Item: Clone,
 {
     Powerset {
         combs: combinations(src, 0),
-        pos: 0,
+    }
+}
+
+impl<I: Iterator> Powerset<I> {
+    /// Returns true if `k` has been incremented, false otherwise.
+    fn increment_k(&mut self) -> bool {
+        if self.combs.k() < self.combs.n() || self.combs.k() == 0 {
+            self.combs.reset(self.combs.k() + 1);
+            true
+        } else {
+            false
+        }
     }
 }
 
 impl<I> Iterator for Powerset<I>
-    where
-        I: Iterator,
-        I::Item: Clone,
+where
+    I: Iterator,
+    I::Item: Clone,
 {
     type Item = Vec<I::Item>;
 
     fn next(&mut self) -> Option<Self::Item> {
         if let Some(elt) = self.combs.next() {
-            self.pos = self.pos.saturating_add(1);
             Some(elt)
-        } else if self.combs.k() < self.combs.n()
-            || self.combs.k() == 0
-        {
-            self.combs.reset(self.combs.k() + 1);
-            self.combs.next().map(|elt| {
-                self.pos = self.pos.saturating_add(1);
-                elt
-            })
+        } else if self.increment_k() {
+            self.combs.next()
         } else {
             None
         }
     }
 
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        // Total bounds for source iterator.
-        let src_total = size_hint::add_scalar(self.combs.src().size_hint(), self.combs.n());
-
-        // Total bounds for self ( length(powerset(set) == 2 ^ length(set) )
-        let self_total = size_hint::pow_scalar_base(2, src_total);
-
-        if self.pos < usize::MAX {
-            // Subtract count of elements already yielded from total.
-            size_hint::sub_scalar(self_total, self.pos)
-        } else {
-            // Fallback: self.pos is saturated and no longer reliable.
-            (0, self_total.1)
+    fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
+        loop {
+            match self.combs.try_nth(n) {
+                Ok(item) => return Some(item),
+                Err(steps) => {
+                    if !self.increment_k() {
+                        return None;
+                    }
+                    n -= steps;
+                }
+            }
         }
     }
+
+    fn size_hint(&self) -> SizeHint {
+        let k = self.combs.k();
+        // Total bounds for source iterator.
+        let (n_min, n_max) = self.combs.src().size_hint();
+        let low = remaining_for(n_min, k).unwrap_or(usize::MAX);
+        let upp = n_max.and_then(|n| remaining_for(n, k));
+        size_hint::add(self.combs.size_hint(), (low, upp))
+    }
+
+    fn count(self) -> usize {
+        let k = self.combs.k();
+        let (n, combs_count) = self.combs.n_and_count();
+        combs_count + remaining_for(n, k).unwrap()
+    }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let mut it = self.combs;
+        if it.k() == 0 {
+            init = it.by_ref().fold(init, &mut f);
+            it.reset(1);
+        }
+        init = it.by_ref().fold(init, &mut f);
+        // n is now known for sure because k >= 1 and all k-combinations have been generated.
+        for k in it.k() + 1..=it.n() {
+            it.reset(k);
+            init = it.by_ref().fold(init, &mut f);
+        }
+        init
+    }
 }
 
 impl<I> FusedIterator for Powerset<I>
-    where
-        I: Iterator,
-        I::Item: Clone,
-{}
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+}
+
+fn remaining_for(n: usize, k: usize) -> Option<usize> {
+    (k + 1..=n).try_fold(0usize, |sum, i| sum.checked_add(checked_binomial(n, i)?))
+}
diff --git a/crates/itertools/src/process_results_impl.rs b/crates/itertools/src/process_results_impl.rs
index 44308f3..ad6c60d 100644
--- a/crates/itertools/src/process_results_impl.rs
+++ b/crates/itertools/src/process_results_impl.rs
@@ -1,3 +1,5 @@
+#[cfg(doc)]
+use crate::Itertools;
 
 /// An iterator that produces only the `T` values as long as the
 /// inner iterator produces `Ok(T)`.
@@ -11,13 +13,10 @@
     iter: I,
 }
 
-impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
-    where I: Iterator<Item = Result<T, E>>
-{
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match self.iter.next() {
+impl<'a, I, E> ProcessResults<'a, I, E> {
+    #[inline(always)]
+    fn next_body<T>(&mut self, item: Option<Result<T, E>>) -> Option<T> {
+        match item {
             Some(Ok(x)) => Some(x),
             Some(Err(e)) => {
                 *self.error = Err(e);
@@ -26,6 +25,18 @@
             None => None,
         }
     }
+}
+
+impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
+where
+    I: Iterator<Item = Result<T, E>>,
+{
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let item = self.iter.next();
+        self.next_body(item)
+    }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
         (0, self.iter.size_hint().1)
@@ -49,49 +60,49 @@
     }
 }
 
+impl<'a, I, T, E> DoubleEndedIterator for ProcessResults<'a, I, E>
+where
+    I: Iterator<Item = Result<T, E>>,
+    I: DoubleEndedIterator,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        let item = self.iter.next_back();
+        self.next_body(item)
+    }
+
+    fn rfold<B, F>(mut self, init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let error = self.error;
+        self.iter
+            .try_rfold(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
 /// an iterator of `Result` values instead.
 ///
-/// `iterable` is an iterator or iterable with `Result<T, E>` elements, where
-/// `T` is the value type and `E` the error type.
-///
-/// `processor` is a closure that receives an adapted version of the iterable
-/// as the only argument — the adapted iterator produces elements of type `T`,
-/// as long as the original iterator produces `Ok` values.
-///
-/// If the original iterable produces an error at any point, the adapted
-/// iterator ends and the `process_results` function will return the
-/// error iself.
-///
-/// Otherwise, the return value from the closure is returned wrapped
-/// inside `Ok`.
-///
-/// # Example
-///
-/// ```
-/// use itertools::process_results;
-///
-/// type R = Result<i32, &'static str>;
-///
-/// let first_values: Vec<R> = vec![Ok(1), Ok(0), Ok(3)];
-/// let second_values: Vec<R> = vec![Ok(2), Ok(1), Err("overflow")];
-///
-/// // “Lift” the iterator .max() method to work on the values in Results using process_results
-///
-/// let first_max = process_results(first_values, |iter| iter.max().unwrap_or(0));
-/// let second_max = process_results(second_values, |iter| iter.max().unwrap_or(0));
-///
-/// assert_eq!(first_max, Ok(3));
-/// assert!(second_max.is_err());
-/// ```
+/// [`IntoIterator`] enabled version of [`Itertools::process_results`].
 pub fn process_results<I, F, T, E, R>(iterable: I, processor: F) -> Result<R, E>
-    where I: IntoIterator<Item = Result<T, E>>,
-          F: FnOnce(ProcessResults<I::IntoIter, E>) -> R
+where
+    I: IntoIterator<Item = Result<T, E>>,
+    F: FnOnce(ProcessResults<I::IntoIter, E>) -> R,
 {
     let iter = iterable.into_iter();
     let mut error = Ok(());
 
-    let result = processor(ProcessResults { error: &mut error, iter });
+    let result = processor(ProcessResults {
+        error: &mut error,
+        iter,
+    });
 
     error.map(|_| result)
 }
diff --git a/crates/itertools/src/put_back_n_impl.rs b/crates/itertools/src/put_back_n_impl.rs
index 60ea8e6..a9eb417 100644
--- a/crates/itertools/src/put_back_n_impl.rs
+++ b/crates/itertools/src/put_back_n_impl.rs
@@ -7,6 +7,7 @@
 ///
 /// Iterator element type is `I::Item`.
 #[derive(Debug, Clone)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct PutBackN<I: Iterator> {
     top: Vec<I::Item>,
     iter: I,
@@ -17,7 +18,8 @@
 ///
 /// Iterator element type is `I::Item`.
 pub fn put_back_n<I>(iterable: I) -> PutBackN<I::IntoIter>
-    where I: IntoIterator
+where
+    I: IntoIterator,
 {
     PutBackN {
         top: Vec::new(),
@@ -26,7 +28,8 @@
 }
 
 impl<I: Iterator> PutBackN<I> {
-    /// Puts x in front of the iterator.
+    /// Puts `x` in front of the iterator.
+    ///
     /// The values are yielded in order of the most recently put back
     /// values first.
     ///
@@ -57,5 +60,12 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         size_hint::add_scalar(self.iter.size_hint(), self.top.len())
     }
-}
 
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        init = self.top.into_iter().rfold(init, &mut f);
+        self.iter.fold(init, f)
+    }
+}
diff --git a/crates/itertools/src/rciter_impl.rs b/crates/itertools/src/rciter_impl.rs
index 7298350..e3b7532 100644
--- a/crates/itertools/src/rciter_impl.rs
+++ b/crates/itertools/src/rciter_impl.rs
@@ -1,10 +1,10 @@
-
-use std::iter::{FusedIterator, IntoIterator};
 use alloc::rc::Rc;
 use std::cell::RefCell;
+use std::iter::{FusedIterator, IntoIterator};
 
 /// A wrapper for `Rc<RefCell<I>>`, that implements the `Iterator` trait.
 #[derive(Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct RcIter<I> {
     /// The boxed iterator.
     pub rciter: Rc<RefCell<I>>,
@@ -45,9 +45,12 @@
 /// `.next()`, i.e. if it somehow participates in an “iterator knot”
 /// where it is an adaptor of itself.
 pub fn rciter<I>(iterable: I) -> RcIter<I::IntoIter>
-    where I: IntoIterator
+where
+    I: IntoIterator,
 {
-    RcIter { rciter: Rc::new(RefCell::new(iterable.into_iter())) }
+    RcIter {
+        rciter: Rc::new(RefCell::new(iterable.into_iter())),
+    }
 }
 
 impl<I> Clone for RcIter<I> {
@@ -55,7 +58,8 @@
 }
 
 impl<A, I> Iterator for RcIter<I>
-    where I: Iterator<Item = A>
+where
+    I: Iterator<Item = A>,
 {
     type Item = A;
     #[inline]
@@ -73,7 +77,8 @@
 }
 
 impl<I> DoubleEndedIterator for RcIter<I>
-    where I: DoubleEndedIterator
+where
+    I: DoubleEndedIterator,
 {
     #[inline]
     fn next_back(&mut self) -> Option<Self::Item> {
@@ -83,7 +88,8 @@
 
 /// Return an iterator from `&RcIter<I>` (by simply cloning it).
 impl<'a, I> IntoIterator for &'a RcIter<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     type Item = I::Item;
     type IntoIter = RcIter<I>;
@@ -93,7 +99,4 @@
     }
 }
 
-
-impl<A, I> FusedIterator for RcIter<I>
-    where I: FusedIterator<Item = A>
-{}
+impl<A, I> FusedIterator for RcIter<I> where I: FusedIterator<Item = A> {}
diff --git a/crates/itertools/src/repeatn.rs b/crates/itertools/src/repeatn.rs
index e025f6f..d86ad9f 100644
--- a/crates/itertools/src/repeatn.rs
+++ b/crates/itertools/src/repeatn.rs
@@ -6,23 +6,28 @@
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 #[derive(Clone, Debug)]
 pub struct RepeatN<A> {
-    elt: Option<A>,
+    pub(crate) elt: Option<A>,
     n: usize,
 }
 
 /// Create an iterator that produces `n` repetitions of `element`.
 pub fn repeat_n<A>(element: A, n: usize) -> RepeatN<A>
-    where A: Clone,
+where
+    A: Clone,
 {
     if n == 0 {
-        RepeatN { elt: None, n, }
+        RepeatN { elt: None, n }
     } else {
-        RepeatN { elt: Some(element), n, }
+        RepeatN {
+            elt: Some(element),
+            n,
+        }
     }
 }
 
 impl<A> Iterator for RepeatN<A>
-    where A: Clone
+where
+    A: Clone,
 {
     type Item = A;
 
@@ -39,21 +44,40 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         (self.n, Some(self.n))
     }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        match self {
+            Self { elt: Some(elt), n } => {
+                debug_assert!(n > 0);
+                init = (1..n).map(|_| elt.clone()).fold(init, &mut f);
+                f(init, elt)
+            }
+            _ => init,
+        }
+    }
 }
 
 impl<A> DoubleEndedIterator for RepeatN<A>
-    where A: Clone
+where
+    A: Clone,
 {
     #[inline]
     fn next_back(&mut self) -> Option<Self::Item> {
         self.next()
     }
+
+    #[inline]
+    fn rfold<B, F>(self, init: B, f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        self.fold(init, f)
+    }
 }
 
-impl<A> ExactSizeIterator for RepeatN<A>
-    where A: Clone
-{}
+impl<A> ExactSizeIterator for RepeatN<A> where A: Clone {}
 
-impl<A> FusedIterator for RepeatN<A>
-    where A: Clone
-{}
+impl<A> FusedIterator for RepeatN<A> where A: Clone {}
diff --git a/crates/itertools/src/size_hint.rs b/crates/itertools/src/size_hint.rs
index 71ea141..6cfead7 100644
--- a/crates/itertools/src/size_hint.rs
+++ b/crates/itertools/src/size_hint.rs
@@ -1,9 +1,7 @@
 //! Arithmetic on `Iterator.size_hint()` values.
 //!
 
-use std::usize;
 use std::cmp;
-use std::u32;
 
 /// `SizeHint` is the return type of `Iterator::size_hint()`.
 pub type SizeHint = (usize, Option<usize>);
@@ -31,7 +29,6 @@
 
 /// Subtract `x` correctly from a `SizeHint`.
 #[inline]
-#[allow(dead_code)]
 pub fn sub_scalar(sh: SizeHint, x: usize) -> SizeHint {
     let (mut low, mut hi) = sh;
     low = low.saturating_sub(x);
@@ -39,22 +36,7 @@
     (low, hi)
 }
 
-
 /// Multiply `SizeHint` correctly
-///
-/// ```ignore
-/// use std::usize;
-/// use itertools::size_hint;
-///
-/// assert_eq!(size_hint::mul((3, Some(4)), (3, Some(4))),
-///            (9, Some(16)));
-///
-/// assert_eq!(size_hint::mul((3, Some(4)), (usize::MAX, None)),
-///            (usize::MAX, None));
-///
-/// assert_eq!(size_hint::mul((3, None), (0, Some(0))),
-///            (0, Some(0)));
-/// ```
 #[inline]
 pub fn mul(a: SizeHint, b: SizeHint) -> SizeHint {
     let low = a.0.saturating_mul(b.0);
@@ -75,20 +57,6 @@
     (low, hi)
 }
 
-/// Raise `base` correctly by a `SizeHint` exponent.
-#[inline]
-pub fn pow_scalar_base(base: usize, exp: SizeHint) -> SizeHint {
-    let exp_low = cmp::min(exp.0, u32::MAX as usize) as u32;
-    let low = base.saturating_pow(exp_low);
-
-    let hi = exp.1.and_then(|exp| {
-        let exp_hi = cmp::min(exp, u32::MAX as usize) as u32;
-        base.checked_pow(exp_hi)
-    });
-
-    (low, hi)
-}
-
 /// Return the maximum
 #[inline]
 pub fn max(a: SizeHint, b: SizeHint) -> SizeHint {
@@ -117,3 +85,10 @@
     };
     (lower, upper)
 }
+
+#[test]
+fn mul_size_hints() {
+    assert_eq!(mul((3, Some(4)), (3, Some(4))), (9, Some(16)));
+    assert_eq!(mul((3, Some(4)), (usize::MAX, None)), (usize::MAX, None));
+    assert_eq!(mul((3, None), (0, Some(0))), (0, Some(0)));
+}
diff --git a/crates/itertools/src/sources.rs b/crates/itertools/src/sources.rs
index 3877ce3..c405ffd 100644
--- a/crates/itertools/src/sources.rs
+++ b/crates/itertools/src/sources.rs
@@ -5,62 +5,6 @@
 use std::fmt;
 use std::mem;
 
-/// 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> {
-    f: F,
-}
-
-impl<F> fmt::Debug for RepeatCall<F>
-{
-    debug_fmt_fields!(RepeatCall, );
-}
-
-/// An iterator source that produces elements indefinitely by calling
-/// a given closure.
-///
-/// Iterator element type is the return type of the closure.
-///
-/// ```
-/// use itertools::repeat_call;
-/// use itertools::Itertools;
-/// use std::collections::BinaryHeap;
-///
-/// let mut heap = BinaryHeap::from(vec![2, 5, 3, 7, 8]);
-///
-/// // extract each element in sorted order
-/// for element in repeat_call(|| heap.pop()).while_some() {
-///     print!("{}", element);
-/// }
-///
-/// itertools::assert_equal(
-///     repeat_call(|| 1).take(5),
-///     vec![1, 1, 1, 1, 1]
-/// );
-/// ```
-#[deprecated(note="Use std repeat_with() instead", since="0.8.0")]
-pub fn repeat_call<F, A>(function: F) -> RepeatCall<F>
-    where F: FnMut() -> A
-{
-    RepeatCall { f: function }
-}
-
-impl<A, F> Iterator for RepeatCall<F>
-    where F: FnMut() -> A
-{
-    type Item = A;
-
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        Some((self.f)())
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (usize::max_value(), None)
-    }
-}
-
 /// Creates a new unfold source with the specified closure as the "iterator
 /// function" and an initial state to eventually pass to the closure
 ///
@@ -97,8 +41,13 @@
 ///                         vec![1, 1, 2, 3, 5, 8, 13, 21]);
 /// assert_eq!(fibonacci.last(), Some(2_971_215_073))
 /// ```
+#[deprecated(
+    note = "Use [std::iter::from_fn](https://doc.rust-lang.org/std/iter/fn.from_fn.html) instead",
+    since = "0.13.0"
+)]
 pub fn unfold<A, St, F>(initial_state: St, f: F) -> Unfold<St, F>
-    where F: FnMut(&mut St) -> Option<A>
+where
+    F: FnMut(&mut St) -> Option<A>,
 {
     Unfold {
         f,
@@ -107,7 +56,8 @@
 }
 
 impl<St, F> fmt::Debug for Unfold<St, F>
-    where St: fmt::Debug,
+where
+    St: fmt::Debug,
 {
     debug_fmt_fields!(Unfold, state);
 }
@@ -115,6 +65,10 @@
 /// See [`unfold`](crate::unfold) for more information.
 #[derive(Clone)]
 #[must_use = "iterators are lazy and do nothing unless consumed"]
+#[deprecated(
+    note = "Use [std::iter::FromFn](https://doc.rust-lang.org/std/iter/struct.FromFn.html) instead",
+    since = "0.13.0"
+)]
 pub struct Unfold<St, F> {
     f: F,
     /// Internal state that will be passed to the closure on the next iteration
@@ -122,7 +76,8 @@
 }
 
 impl<A, St, F> Iterator for Unfold<St, F>
-    where F: FnMut(&mut St) -> Option<A>
+where
+    F: FnMut(&mut St) -> Option<A>,
 {
     type Item = A;
 
@@ -144,13 +99,15 @@
 }
 
 impl<St, F> fmt::Debug for Iterate<St, F>
-    where St: fmt::Debug,
+where
+    St: fmt::Debug,
 {
     debug_fmt_fields!(Iterate, state);
 }
 
 impl<St, F> Iterator for Iterate<St, F>
-    where F: FnMut(&St) -> St
+where
+    F: FnMut(&St) -> St,
 {
     type Item = St;
 
@@ -162,7 +119,7 @@
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        (usize::max_value(), None)
+        (usize::MAX, None)
     }
 }
 
@@ -171,10 +128,23 @@
 /// ```
 /// use itertools::iterate;
 ///
-/// itertools::assert_equal(iterate(1, |&i| i * 3).take(5), vec![1, 3, 9, 27, 81]);
+/// itertools::assert_equal(iterate(1, |i| i % 3 + 1).take(5), vec![1, 2, 3, 1, 2]);
 /// ```
+///
+/// **Panics** if compute the next value does.
+///
+/// ```should_panic
+/// # use itertools::iterate;
+/// let mut it = iterate(25u32, |x| x - 10).take_while(|&x| x > 10);
+/// assert_eq!(it.next(), Some(25)); // `Iterate` holds 15.
+/// assert_eq!(it.next(), Some(15)); // `Iterate` holds 5.
+/// it.next(); // `5 - 10` overflows.
+/// ```
+///
+/// You can alternatively use [`core::iter::successors`] as it better describes a finite iterator.
 pub fn iterate<St, F>(initial_value: St, f: F) -> Iterate<St, F>
-    where F: FnMut(&St) -> St
+where
+    F: FnMut(&St) -> St,
 {
     Iterate {
         state: initial_value,
diff --git a/crates/itertools/src/take_while_inclusive.rs b/crates/itertools/src/take_while_inclusive.rs
new file mode 100644
index 0000000..420da98
--- /dev/null
+++ b/crates/itertools/src/take_while_inclusive.rs
@@ -0,0 +1,96 @@
+use core::iter::FusedIterator;
+use std::fmt;
+
+/// An iterator adaptor that consumes elements while the given predicate is
+/// `true`, including the element for which the predicate first returned
+/// `false`.
+///
+/// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive)
+/// for more information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Clone)]
+pub struct TakeWhileInclusive<I, F> {
+    iter: I,
+    predicate: F,
+    done: bool,
+}
+
+impl<I, F> TakeWhileInclusive<I, F>
+where
+    I: Iterator,
+    F: FnMut(&I::Item) -> bool,
+{
+    /// Create a new [`TakeWhileInclusive`] from an iterator and a predicate.
+    pub(crate) fn new(iter: I, predicate: F) -> Self {
+        Self {
+            iter,
+            predicate,
+            done: false,
+        }
+    }
+}
+
+impl<I, F> fmt::Debug for TakeWhileInclusive<I, F>
+where
+    I: Iterator + fmt::Debug,
+{
+    debug_fmt_fields!(TakeWhileInclusive, iter, done);
+}
+
+impl<I, F> Iterator for TakeWhileInclusive<I, F>
+where
+    I: Iterator,
+    F: FnMut(&I::Item) -> bool,
+{
+    type Item = I::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.done {
+            None
+        } else {
+            self.iter.next().map(|item| {
+                if !(self.predicate)(&item) {
+                    self.done = true;
+                }
+                item
+            })
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        if self.done {
+            (0, Some(0))
+        } else {
+            (0, self.iter.size_hint().1)
+        }
+    }
+
+    fn fold<B, Fold>(mut self, init: B, mut f: Fold) -> B
+    where
+        Fold: FnMut(B, Self::Item) -> B,
+    {
+        if self.done {
+            init
+        } else {
+            let predicate = &mut self.predicate;
+            self.iter
+                .try_fold(init, |mut acc, item| {
+                    let is_ok = predicate(&item);
+                    acc = f(acc, item);
+                    if is_ok {
+                        Ok(acc)
+                    } else {
+                        Err(acc)
+                    }
+                })
+                .unwrap_or_else(|err| err)
+        }
+    }
+}
+
+impl<I, F> FusedIterator for TakeWhileInclusive<I, F>
+where
+    I: Iterator,
+    F: FnMut(&I::Item) -> bool,
+{
+}
diff --git a/crates/itertools/src/tee.rs b/crates/itertools/src/tee.rs
index ea47529..0984c5d 100644
--- a/crates/itertools/src/tee.rs
+++ b/crates/itertools/src/tee.rs
@@ -1,8 +1,8 @@
 use super::size_hint;
 
-use std::cell::RefCell;
 use alloc::collections::VecDeque;
 use alloc::rc::Rc;
+use std::cell::RefCell;
 
 /// Common buffer object for the two tee halves
 #[derive(Debug)]
@@ -19,24 +19,37 @@
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Debug)]
 pub struct Tee<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     rcbuffer: Rc<RefCell<TeeBuffer<I::Item, I>>>,
     id: bool,
 }
 
 pub fn new<I>(iter: I) -> (Tee<I>, Tee<I>)
-    where I: Iterator
+where
+    I: Iterator,
 {
-    let buffer = TeeBuffer{backlog: VecDeque::new(), iter, owner: false};
-    let t1 = Tee{rcbuffer: Rc::new(RefCell::new(buffer)), id: true};
-    let t2 = Tee{rcbuffer: t1.rcbuffer.clone(), id: false};
+    let buffer = TeeBuffer {
+        backlog: VecDeque::new(),
+        iter,
+        owner: false,
+    };
+    let t1 = Tee {
+        rcbuffer: Rc::new(RefCell::new(buffer)),
+        id: true,
+    };
+    let t2 = Tee {
+        rcbuffer: t1.rcbuffer.clone(),
+        id: false,
+    };
     (t1, t2)
 }
 
 impl<I> Iterator for Tee<I>
-    where I: Iterator,
-          I::Item: Clone
+where
+    I: Iterator,
+    I::Item: Clone,
 {
     type Item = I::Item;
     fn next(&mut self) -> Option<Self::Item> {
@@ -73,6 +86,8 @@
 }
 
 impl<I> ExactSizeIterator for Tee<I>
-    where I: ExactSizeIterator,
-          I::Item: Clone
-{}
+where
+    I: ExactSizeIterator,
+    I::Item: Clone,
+{
+}
diff --git a/crates/itertools/src/tuple_impl.rs b/crates/itertools/src/tuple_impl.rs
index 06b5c13..c0d556f 100644
--- a/crates/itertools/src/tuple_impl.rs
+++ b/crates/itertools/src/tuple_impl.rs
@@ -1,10 +1,10 @@
 //! Some iterator that produces tuples
 
+use std::iter::Cycle;
 use std::iter::Fuse;
 use std::iter::FusedIterator;
-use std::iter::Take;
-use std::iter::Cycle;
-use std::marker::PhantomData;
+
+use crate::size_hint;
 
 // `HomogeneousTuple` is a public facade for `TupleCollect`, allowing
 // tuple-related methods to be used by clients in generic contexts, while
@@ -12,9 +12,7 @@
 // See https://github.com/rust-itertools/itertools/issues/387
 
 /// Implemented for homogeneous tuples of size up to 12.
-pub trait HomogeneousTuple
-    : TupleCollect
-{}
+pub trait HomogeneousTuple: TupleCollect {}
 
 impl<T: TupleCollect> HomogeneousTuple for T {}
 
@@ -24,25 +22,25 @@
 /// [`Tuples::into_buffer()`].
 #[derive(Clone, Debug)]
 pub struct TupleBuffer<T>
-    where T: HomogeneousTuple
+where
+    T: HomogeneousTuple,
 {
     cur: usize,
     buf: T::Buffer,
 }
 
 impl<T> TupleBuffer<T>
-    where T: HomogeneousTuple
+where
+    T: HomogeneousTuple,
 {
     fn new(buf: T::Buffer) -> Self {
-        TupleBuffer {
-            cur: 0,
-            buf,
-        }
+        Self { cur: 0, buf }
     }
 }
 
 impl<T> Iterator for TupleBuffer<T>
-    where T: HomogeneousTuple
+where
+    T: HomogeneousTuple,
 {
     type Item = T::Item;
 
@@ -61,18 +59,16 @@
         let len = if buffer.is_empty() {
             0
         } else {
-            buffer.iter()
-                  .position(|x| x.is_none())
-                  .unwrap_or_else(|| buffer.len())
+            buffer
+                .iter()
+                .position(|x| x.is_none())
+                .unwrap_or(buffer.len())
         };
         (len, Some(len))
     }
 }
 
-impl<T> ExactSizeIterator for TupleBuffer<T>
-    where T: HomogeneousTuple
-{
-}
+impl<T> ExactSizeIterator for TupleBuffer<T> where T: HomogeneousTuple {}
 
 /// An iterator that groups the items in tuples of a specific size.
 ///
@@ -80,8 +76,9 @@
 #[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>,
-          T: HomogeneousTuple
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
 {
     iter: Fuse<I>,
     buf: T::Buffer,
@@ -89,8 +86,9 @@
 
 /// Create a new tuples iterator.
 pub fn tuples<I, T>(iter: I) -> Tuples<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
 {
     Tuples {
         iter: iter.fuse(),
@@ -99,19 +97,50 @@
 }
 
 impl<I, T> Iterator for Tuples<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
 {
     type Item = T;
 
     fn next(&mut self) -> Option<Self::Item> {
         T::collect_from_iter(&mut self.iter, &mut self.buf)
     }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        // The number of elts we've drawn from the underlying iterator, but have
+        // not yet produced as a tuple.
+        let buffered = T::buffer_len(&self.buf);
+        // To that, we must add the size estimates of the underlying iterator.
+        let (unbuffered_lo, unbuffered_hi) = self.iter.size_hint();
+        // The total low estimate is the sum of the already-buffered elements,
+        // plus the low estimate of remaining unbuffered elements, divided by
+        // the tuple size.
+        let total_lo = add_then_div(unbuffered_lo, buffered, T::num_items()).unwrap_or(usize::MAX);
+        // And likewise for the total high estimate, but using the high estimate
+        // of the remaining unbuffered elements.
+        let total_hi = unbuffered_hi.and_then(|hi| add_then_div(hi, buffered, T::num_items()));
+        (total_lo, total_hi)
+    }
+}
+
+/// `(n + a) / d` avoiding overflow when possible, returns `None` if it overflows.
+fn add_then_div(n: usize, a: usize, d: usize) -> Option<usize> {
+    debug_assert_ne!(d, 0);
+    (n / d).checked_add(a / d)?.checked_add((n % d + a % d) / d)
+}
+
+impl<I, T> ExactSizeIterator for Tuples<I, T>
+where
+    I: ExactSizeIterator<Item = T::Item>,
+    T: HomogeneousTuple,
+{
 }
 
 impl<I, T> Tuples<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
 {
     /// Return a buffer with the produced items that was not enough to be grouped in a tuple.
     ///
@@ -128,7 +157,6 @@
     }
 }
 
-
 /// An iterator over all contiguous windows that produces tuples of a specific size.
 ///
 /// See [`.tuple_windows()`](crate::Itertools::tuple_windows) for more
@@ -136,125 +164,167 @@
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Clone, Debug)]
 pub struct TupleWindows<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
 {
     iter: I,
     last: Option<T>,
 }
 
 /// Create a new tuple windows iterator.
-pub fn tuple_windows<I, T>(mut iter: I) -> TupleWindows<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple,
-          T::Item: Clone
+pub fn tuple_windows<I, T>(iter: I) -> TupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
+    T::Item: Clone,
 {
-    use std::iter::once;
-
-    let mut last = None;
-    if T::num_items() != 1 {
-        // put in a duplicate item in front of the tuple; this simplifies
-        // .next() function.
-        if let Some(item) = iter.next() {
-            let iter = once(item.clone()).chain(once(item)).chain(&mut iter);
-            last = T::collect_from_iter_no_buf(iter);
-        }
-    }
-
-    TupleWindows {
-        iter,
-        last,
-    }
+    TupleWindows { last: None, iter }
 }
 
 impl<I, T> Iterator for TupleWindows<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple + Clone,
-          T::Item: Clone
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple + Clone,
+    T::Item: Clone,
 {
     type Item = T;
 
     fn next(&mut self) -> Option<Self::Item> {
         if T::num_items() == 1 {
-            return T::collect_from_iter_no_buf(&mut self.iter)
+            return T::collect_from_iter_no_buf(&mut self.iter);
         }
-        if let Some(ref mut last) = self.last {
-            if let Some(new) = self.iter.next() {
+        if let Some(new) = self.iter.next() {
+            if let Some(ref mut last) = self.last {
                 last.left_shift_push(new);
-                return Some(last.clone());
+                Some(last.clone())
+            } else {
+                use std::iter::once;
+                let iter = once(new).chain(&mut self.iter);
+                self.last = T::collect_from_iter_no_buf(iter);
+                self.last.clone()
             }
+        } else {
+            None
         }
-        None
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let mut sh = self.iter.size_hint();
+        // Adjust the size hint at the beginning
+        // OR when `num_items == 1` (but it does not change the size hint).
+        if self.last.is_none() {
+            sh = size_hint::sub_scalar(sh, T::num_items() - 1);
+        }
+        sh
     }
 }
 
-impl<I, T> FusedIterator for TupleWindows<I, T>
-    where I: FusedIterator<Item = T::Item>,
-          T: HomogeneousTuple + Clone,
-          T::Item: Clone
-{}
+impl<I, T> ExactSizeIterator for TupleWindows<I, T>
+where
+    I: ExactSizeIterator<Item = T::Item>,
+    T: HomogeneousTuple + Clone,
+    T::Item: Clone,
+{
+}
 
-/// An iterator over all windows,wrapping back to the first elements when the
+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()`](crate::Itertools::circular_tuple_windows) for more
 /// information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-#[derive(Debug)]
-pub struct CircularTupleWindows<I, T: Clone>
-    where I: Iterator<Item = T::Item> + Clone,
-          T: TupleCollect + Clone
+#[derive(Debug, Clone)]
+pub struct CircularTupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item> + Clone,
+    T: TupleCollect + Clone,
 {
-    iter: Take<TupleWindows<Cycle<I>, T>>,
-    phantom_data: PhantomData<T>
+    iter: TupleWindows<Cycle<I>, T>,
+    len: usize,
 }
 
 pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T>
-    where I: Iterator<Item = T::Item> + Clone + ExactSizeIterator,
-          T: TupleCollect + Clone,
-          T::Item: Clone
+where
+    I: Iterator<Item = T::Item> + Clone + ExactSizeIterator,
+    T: TupleCollect + Clone,
+    T::Item: Clone,
 {
     let len = iter.len();
-    let iter = tuple_windows(iter.cycle()).take(len);
+    let iter = tuple_windows(iter.cycle());
 
-    CircularTupleWindows {
-        iter,
-        phantom_data: PhantomData{}
-    }
+    CircularTupleWindows { iter, len }
 }
 
 impl<I, T> Iterator for CircularTupleWindows<I, T>
-    where I: Iterator<Item = T::Item> + Clone,
-          T: TupleCollect + Clone,
-          T::Item: Clone
+where
+    I: Iterator<Item = T::Item> + Clone,
+    T: TupleCollect + Clone,
+    T::Item: Clone,
 {
     type Item = T;
 
     fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
+        if self.len != 0 {
+            self.len -= 1;
+            self.iter.next()
+        } else {
+            None
+        }
     }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.len, Some(self.len))
+    }
+}
+
+impl<I, T> ExactSizeIterator for CircularTupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item> + Clone,
+    T: TupleCollect + Clone,
+    T::Item: Clone,
+{
+}
+
+impl<I, T> FusedIterator for CircularTupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item> + Clone,
+    T: TupleCollect + Clone,
+    T::Item: Clone,
+{
 }
 
 pub trait TupleCollect: Sized {
     type Item;
     type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>;
 
+    fn buffer_len(buf: &Self::Buffer) -> usize {
+        let s = buf.as_ref();
+        s.iter().position(Option::is_none).unwrap_or(s.len())
+    }
+
     fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
-        where I: IntoIterator<Item = Self::Item>;
+    where
+        I: IntoIterator<Item = Self::Item>;
 
     fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
-        where I: IntoIterator<Item = Self::Item>;
+    where
+        I: IntoIterator<Item = Self::Item>;
 
     fn num_items() -> usize;
 
     fn left_shift_push(&mut self, item: Self::Item);
 }
 
-macro_rules! count_ident{
-    () => {0};
-    ($i0:ident, $($i:ident,)*) => {1 + count_ident!($($i,)*)};
-}
 macro_rules! rev_for_each_ident{
     ($m:ident, ) => {};
     ($m:ident, $i0:ident, $($i:ident,)*) => {
@@ -269,7 +339,7 @@
         impl_tuple_collect!($($Y,)*);
         impl<A> TupleCollect for ($(ignore_ident!($Y, A),)*) {
             type Item = A;
-            type Buffer = [Option<A>; count_ident!($($Y,)*) - 1];
+            type Buffer = [Option<A>; count_ident!($($Y)*) - 1];
 
             #[allow(unused_assignments, unused_mut)]
             fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
@@ -312,7 +382,7 @@
             }
 
             fn num_items() -> usize {
-                count_ident!($($Y,)*)
+                count_ident!($($Y)*)
             }
 
             fn left_shift_push(&mut self, mut item: A) {
diff --git a/crates/itertools/src/unique_impl.rs b/crates/itertools/src/unique_impl.rs
index 4e81e78..0f6397e 100644
--- a/crates/itertools/src/unique_impl.rs
+++ b/crates/itertools/src/unique_impl.rs
@@ -1,7 +1,7 @@
-use std::collections::HashMap;
 use std::collections::hash_map::Entry;
-use std::hash::Hash;
+use std::collections::HashMap;
 use std::fmt;
+use std::hash::Hash;
 use std::iter::FusedIterator;
 
 /// An iterator adapter to filter out duplicate elements.
@@ -19,17 +19,19 @@
 }
 
 impl<I, V, F> fmt::Debug for UniqueBy<I, V, F>
-    where I: Iterator + fmt::Debug,
-          V: fmt::Debug + Hash + Eq,
+where
+    I: Iterator + fmt::Debug,
+    V: fmt::Debug + Hash + Eq,
 {
     debug_fmt_fields!(UniqueBy, iter, used);
 }
 
 /// Create a new `UniqueBy` iterator.
 pub fn unique_by<I, V, F>(iter: I, f: F) -> UniqueBy<I, V, F>
-    where V: Eq + Hash,
-          F: FnMut(&I::Item) -> V,
-          I: Iterator,
+where
+    V: Eq + Hash,
+    F: FnMut(&I::Item) -> V,
+    I: Iterator,
 {
     UniqueBy {
         iter,
@@ -40,8 +42,9 @@
 
 // count the number of new unique keys in iterable (`used` is the set already seen)
 fn count_new_keys<I, K>(mut used: HashMap<K, ()>, iterable: I) -> usize
-    where I: IntoIterator<Item=K>,
-          K: Hash + Eq,
+where
+    I: IntoIterator<Item = K>,
+    K: Hash + Eq,
 {
     let iter = iterable.into_iter();
     let current_used = used.len();
@@ -50,20 +53,16 @@
 }
 
 impl<I, V, F> Iterator for UniqueBy<I, V, F>
-    where I: Iterator,
-          V: Eq + Hash,
-          F: FnMut(&I::Item) -> V
+where
+    I: Iterator,
+    V: Eq + Hash,
+    F: FnMut(&I::Item) -> V,
 {
     type Item = I::Item;
 
     fn next(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.next() {
-            let key = (self.f)(&v);
-            if self.used.insert(key, ()).is_none() {
-                return Some(v);
-            }
-        }
-        None
+        let Self { iter, used, f } = self;
+        iter.find(|v| used.insert(f(v), ()).is_none())
     }
 
     #[inline]
@@ -79,42 +78,42 @@
 }
 
 impl<I, V, F> DoubleEndedIterator for UniqueBy<I, V, F>
-    where I: DoubleEndedIterator,
-          V: Eq + Hash,
-          F: FnMut(&I::Item) -> V
+where
+    I: DoubleEndedIterator,
+    V: Eq + Hash,
+    F: FnMut(&I::Item) -> V,
 {
     fn next_back(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.next_back() {
-            let key = (self.f)(&v);
-            if self.used.insert(key, ()).is_none() {
-                return Some(v);
-            }
-        }
-        None
+        let Self { iter, used, f } = self;
+        iter.rfind(|v| used.insert(f(v), ()).is_none())
     }
 }
 
 impl<I, V, F> FusedIterator for UniqueBy<I, V, F>
-    where I: FusedIterator,
-          V: Eq + Hash,
-          F: FnMut(&I::Item) -> V
-{}
+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
+where
+    I: Iterator,
+    I::Item: Eq + Hash + Clone,
 {
     type Item = I::Item;
 
     fn next(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.iter.next() {
-            if let Entry::Vacant(entry) = self.iter.used.entry(v) {
+        let UniqueBy { iter, used, .. } = &mut self.iter;
+        iter.find_map(|v| {
+            if let Entry::Vacant(entry) = used.entry(v) {
                 let elt = entry.key().clone();
                 entry.insert(());
                 return Some(elt);
             }
-        }
-        None
+            None
+        })
     }
 
     #[inline]
@@ -129,51 +128,61 @@
 }
 
 impl<I> DoubleEndedIterator for Unique<I>
-    where I: DoubleEndedIterator,
-          I::Item: Eq + Hash + Clone
+where
+    I: DoubleEndedIterator,
+    I::Item: Eq + Hash + Clone,
 {
     fn next_back(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.iter.next_back() {
-            if let Entry::Vacant(entry) = self.iter.used.entry(v) {
+        let UniqueBy { iter, used, .. } = &mut self.iter;
+        iter.rev().find_map(|v| {
+            if let Entry::Vacant(entry) = used.entry(v) {
                 let elt = entry.key().clone();
                 entry.insert(());
                 return Some(elt);
             }
-        }
-        None
+            None
+        })
     }
 }
 
 impl<I> FusedIterator for Unique<I>
-    where I: FusedIterator,
-          I::Item: Eq + Hash + Clone
-{}
+where
+    I: FusedIterator,
+    I::Item: Eq + Hash + Clone,
+{
+}
 
 /// An iterator adapter to filter out duplicate elements.
 ///
 /// 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> {
+pub struct Unique<I>
+where
+    I: Iterator,
+    I::Item: Eq + Hash + Clone,
+{
     iter: UniqueBy<I, I::Item, ()>,
 }
 
 impl<I> fmt::Debug for Unique<I>
-    where I: Iterator + fmt::Debug,
-          I::Item: Hash + Eq + fmt::Debug,
+where
+    I: Iterator + fmt::Debug,
+    I::Item: Hash + Eq + fmt::Debug + Clone,
 {
     debug_fmt_fields!(Unique, iter);
 }
 
 pub fn unique<I>(iter: I) -> Unique<I>
-    where I: Iterator,
-          I::Item: Eq + Hash,
+where
+    I: Iterator,
+    I::Item: Eq + Hash + Clone,
 {
     Unique {
         iter: UniqueBy {
             iter,
             used: HashMap::new(),
             f: (),
-        }
+        },
     }
 }
diff --git a/crates/itertools/src/unziptuple.rs b/crates/itertools/src/unziptuple.rs
index 7af29ec..2c79c2d 100644
--- a/crates/itertools/src/unziptuple.rs
+++ b/crates/itertools/src/unziptuple.rs
@@ -1,6 +1,6 @@
 /// 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
+/// `multiunzip()` 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`].
diff --git a/crates/itertools/src/with_position.rs b/crates/itertools/src/with_position.rs
index 1388503..2d56bb9 100644
--- a/crates/itertools/src/with_position.rs
+++ b/crates/itertools/src/with_position.rs
@@ -1,28 +1,40 @@
-use std::iter::{Fuse,Peekable, FusedIterator};
+use std::fmt;
+use std::iter::{Fuse, FusedIterator, Peekable};
 
 /// An iterator adaptor that wraps each element in an [`Position`].
 ///
-/// Iterator element type is `Position<I::Item>`.
+/// Iterator element type is `(Position, I::Item)`.
 ///
 /// 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,
+where
+    I: Iterator,
 {
     handled_first: bool,
     peekable: Peekable<Fuse<I>>,
 }
 
+impl<I> fmt::Debug for WithPosition<I>
+where
+    I: Iterator,
+    Peekable<Fuse<I>>: fmt::Debug,
+{
+    debug_fmt_fields!(WithPosition, handled_first, peekable);
+}
+
 impl<I> Clone for WithPosition<I>
-    where I: Clone + Iterator,
-          I::Item: Clone,
+where
+    I: Clone + Iterator,
+    I::Item: Clone,
 {
     clone_fields!(handled_first, peekable);
 }
 
 /// Create a new `WithPosition` iterator.
 pub fn with_position<I>(iter: I) -> WithPosition<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     WithPosition {
         handled_first: false,
@@ -30,36 +42,24 @@
     }
 }
 
-/// A value yielded by `WithPosition`.
+/// The first component of the value yielded by `WithPosition`.
 /// Indicates the position of this element in the iterator results.
 ///
 /// See [`.with_position()`](crate::Itertools::with_position) for more information.
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Position<T> {
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Position {
     /// This is the first element.
-    First(T),
+    First,
     /// This is neither the first nor the last element.
-    Middle(T),
+    Middle,
     /// This is the last element.
-    Last(T),
+    Last,
     /// This is the only element.
-    Only(T),
-}
-
-impl<T> Position<T> {
-    /// Return the inner value.
-    pub fn into_inner(self) -> T {
-        match self {
-            Position::First(x) |
-            Position::Middle(x) |
-            Position::Last(x) |
-            Position::Only(x) => x,
-        }
-    }
+    Only,
 }
 
 impl<I: Iterator> Iterator for WithPosition<I> {
-    type Item = Position<I::Item>;
+    type Item = (Position, I::Item);
 
     fn next(&mut self) -> Option<Self::Item> {
         match self.peekable.next() {
@@ -70,15 +70,15 @@
                     // Peek to see if this is also the last item,
                     // in which case tag it as `Only`.
                     match self.peekable.peek() {
-                        Some(_) => Some(Position::First(item)),
-                        None => Some(Position::Only(item)),
+                        Some(_) => Some((Position::First, item)),
+                        None => Some((Position::Only, item)),
                     }
                 } else {
                     // Have seen the first item, and there's something left.
                     // Peek to see if this is the last item.
                     match self.peekable.peek() {
-                        Some(_) => Some(Position::Middle(item)),
-                        None => Some(Position::Last(item)),
+                        Some(_) => Some((Position::Middle, item)),
+                        None => Some((Position::Last, item)),
                     }
                 }
             }
@@ -90,11 +90,35 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         self.peekable.size_hint()
     }
+
+    fn fold<B, F>(mut self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        if let Some(mut head) = self.peekable.next() {
+            if !self.handled_first {
+                // The current head is `First` or `Only`,
+                // it depends if there is another item or not.
+                match self.peekable.next() {
+                    Some(second) => {
+                        let first = std::mem::replace(&mut head, second);
+                        init = f(init, (Position::First, first));
+                    }
+                    None => return f(init, (Position::Only, head)),
+                }
+            }
+            // Have seen the first item, and there's something left.
+            init = self.peekable.fold(init, |acc, mut item| {
+                std::mem::swap(&mut head, &mut item);
+                f(acc, (Position::Middle, item))
+            });
+            // The "head" is now the last item.
+            init = f(init, (Position::Last, head));
+        }
+        init
+    }
 }
 
-impl<I> ExactSizeIterator for WithPosition<I>
-    where I: ExactSizeIterator,
-{ }
+impl<I> ExactSizeIterator for WithPosition<I> where I: ExactSizeIterator {}
 
-impl<I: Iterator> FusedIterator for WithPosition<I> 
-{}
+impl<I: Iterator> FusedIterator for WithPosition<I> {}
diff --git a/crates/itertools/src/zip_eq_impl.rs b/crates/itertools/src/zip_eq_impl.rs
index a079b32..6d3b682 100644
--- a/crates/itertools/src/zip_eq_impl.rs
+++ b/crates/itertools/src/zip_eq_impl.rs
@@ -1,6 +1,7 @@
 use super::size_hint;
 
 /// An iterator which iterates two other iterators simultaneously
+/// and panic if they have different lengths.
 ///
 /// See [`.zip_eq()`](crate::Itertools::zip_eq) for more information.
 #[derive(Clone, Debug)]
@@ -10,9 +11,7 @@
     b: J,
 }
 
-/// Iterate `i` and `j` in lock step.
-///
-/// **Panics** if the iterators are not of the same length.
+/// Zips two iterators but **panics** if they are not of the same length.
 ///
 /// [`IntoIterator`] enabled version of [`Itertools::zip_eq`](crate::Itertools::zip_eq).
 ///
@@ -25,8 +24,9 @@
 /// }
 /// ```
 pub fn zip_eq<I, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter>
-    where I: IntoIterator,
-          J: IntoIterator
+where
+    I: IntoIterator,
+    J: IntoIterator,
 {
     ZipEq {
         a: i.into_iter(),
@@ -35,8 +35,9 @@
 }
 
 impl<I, J> Iterator for ZipEq<I, J>
-    where I: Iterator,
-          J: Iterator
+where
+    I: Iterator,
+    J: Iterator,
 {
     type Item = (I::Item, J::Item);
 
@@ -44,8 +45,9 @@
         match (self.a.next(), self.b.next()) {
             (None, None) => None,
             (Some(a), Some(b)) => Some((a, b)),
-            (None, Some(_)) | (Some(_), None) =>
-            panic!("itertools: .zip_eq() reached end of one iterator before the other")
+            (None, Some(_)) | (Some(_), None) => {
+                panic!("itertools: .zip_eq() reached end of one iterator before the other")
+            }
         }
     }
 
@@ -55,6 +57,8 @@
 }
 
 impl<I, J> ExactSizeIterator for ZipEq<I, J>
-    where I: ExactSizeIterator,
-          J: ExactSizeIterator
-{}
+where
+    I: ExactSizeIterator,
+    J: ExactSizeIterator,
+{
+}
diff --git a/crates/itertools/src/zip_longest.rs b/crates/itertools/src/zip_longest.rs
index cb9a7ba..d4eb9a8 100644
--- a/crates/itertools/src/zip_longest.rs
+++ b/crates/itertools/src/zip_longest.rs
@@ -1,5 +1,5 @@
-use std::cmp::Ordering::{Equal, Greater, Less};
 use super::size_hint;
+use std::cmp::Ordering::{Equal, Greater, Less};
 use std::iter::{Fuse, FusedIterator};
 
 use crate::either_or_both::EitherOrBoth;
@@ -8,6 +8,7 @@
 // and dedicated to itertools https://github.com/rust-lang/rust/pull/19283
 
 /// An iterator which iterates two other iterators simultaneously
+/// and wraps the elements in [`EitherOrBoth`].
 ///
 /// This iterator is *fused*.
 ///
@@ -21,8 +22,9 @@
 
 /// Create a new `ZipLongest` iterator.
 pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U>
-    where T: Iterator,
-          U: Iterator
+where
+    T: Iterator,
+    U: Iterator,
 {
     ZipLongest {
         a: a.fuse(),
@@ -31,8 +33,9 @@
 }
 
 impl<T, U> Iterator for ZipLongest<T, U>
-    where T: Iterator,
-          U: Iterator
+where
+    T: Iterator,
+    U: Iterator,
 {
     type Item = EitherOrBoth<T::Item, U::Item>;
 
@@ -50,11 +53,29 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         size_hint::max(self.a.size_hint(), self.b.size_hint())
     }
+
+    #[inline]
+    fn fold<B, F>(self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let Self { mut a, mut b } = self;
+        let res = a.try_fold(init, |init, a| match b.next() {
+            Some(b) => Ok(f(init, EitherOrBoth::Both(a, b))),
+            None => Err(f(init, EitherOrBoth::Left(a))),
+        });
+        match res {
+            Ok(acc) => b.map(EitherOrBoth::Right).fold(acc, f),
+            Err(acc) => a.map(EitherOrBoth::Left).fold(acc, f),
+        }
+    }
 }
 
 impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
-    where T: DoubleEndedIterator + ExactSizeIterator,
-          U: DoubleEndedIterator + ExactSizeIterator
+where
+    T: DoubleEndedIterator + ExactSizeIterator,
+    U: DoubleEndedIterator + ExactSizeIterator,
 {
     #[inline]
     fn next_back(&mut self) -> Option<Self::Item> {
@@ -70,14 +91,49 @@
             Less => self.b.next_back().map(EitherOrBoth::Right),
         }
     }
+
+    fn rfold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let Self { mut a, mut b } = self;
+        let a_len = a.len();
+        let b_len = b.len();
+        match a_len.cmp(&b_len) {
+            Equal => {}
+            Greater => {
+                init = a
+                    .by_ref()
+                    .rev()
+                    .take(a_len - b_len)
+                    .map(EitherOrBoth::Left)
+                    .fold(init, &mut f)
+            }
+            Less => {
+                init = b
+                    .by_ref()
+                    .rev()
+                    .take(b_len - a_len)
+                    .map(EitherOrBoth::Right)
+                    .fold(init, &mut f)
+            }
+        }
+        a.rfold(init, |acc, item_a| {
+            f(acc, EitherOrBoth::Both(item_a, b.next_back().unwrap()))
+        })
+    }
 }
 
 impl<T, U> ExactSizeIterator for ZipLongest<T, U>
-    where T: ExactSizeIterator,
-          U: ExactSizeIterator
-{}
+where
+    T: ExactSizeIterator,
+    U: ExactSizeIterator,
+{
+}
 
 impl<T, U> FusedIterator for ZipLongest<T, U>
-    where T: Iterator,
-          U: Iterator
-{}
+where
+    T: Iterator,
+    U: Iterator,
+{
+}
diff --git a/crates/itertools/src/ziptuple.rs b/crates/itertools/src/ziptuple.rs
index 6d3a584..3ada029 100644
--- a/crates/itertools/src/ziptuple.rs
+++ b/crates/itertools/src/ziptuple.rs
@@ -7,7 +7,7 @@
     t: T,
 }
 
-/// An iterator that generalizes *.zip()* and allows running multiple iterators in lockstep.
+/// 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
@@ -16,11 +16,11 @@
 /// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to `E` are the
 /// element types of the subiterator.
 ///
-/// **Note:** The result of this macro is a value of a named type (`Zip<(I, J,
+/// **Note:** The result of this function is a value of a named type (`Zip<(I, J,
 /// ..)>` of each component iterator `I, J, ...`) if each component iterator is
 /// nameable.
 ///
-/// Prefer [`izip!()`] over `multizip` for the performance benefits of using the
+/// Prefer [`izip!()`](crate::izip) over `multizip` for the performance benefits of using the
 /// standard library `.zip()`. Prefer `multizip` if a nameable type is needed.
 ///
 /// ```
@@ -36,10 +36,9 @@
 ///
 /// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]);
 /// ```
-/// [`izip!()`]: crate::izip
 pub fn multizip<T, U>(t: U) -> Zip<T>
-    where Zip<T>: From<U>,
-          Zip<T>: Iterator,
+where
+    Zip<T>: From<U> + Iterator,
 {
     Zip::from(t)
 }
@@ -82,7 +81,7 @@
 
             fn size_hint(&self) -> (usize, Option<usize>)
             {
-                let sh = (::std::usize::MAX, None);
+                let sh = (usize::MAX, None);
                 let ($(ref $B,)*) = self.t;
                 $(
                     let sh = size_hint::min($B.size_hint(), sh);
diff --git a/crates/itertools/tests/adaptors_no_collect.rs b/crates/itertools/tests/adaptors_no_collect.rs
index 103db23..977224a 100644
--- a/crates/itertools/tests/adaptors_no_collect.rs
+++ b/crates/itertools/tests/adaptors_no_collect.rs
@@ -22,9 +22,14 @@
 }
 
 fn no_collect_test<A, T>(to_adaptor: T)
-    where A: Iterator, T: Fn(PanickingCounter) -> A
+where
+    A: Iterator,
+    T: Fn(PanickingCounter) -> A,
 {
-    let counter = PanickingCounter { curr: 0, max: 10_000 };
+    let counter = PanickingCounter {
+        curr: 0,
+        max: 10_000,
+    };
     let adaptor = to_adaptor(counter);
 
     for _ in adaptor.take(5) {}
@@ -43,4 +48,4 @@
 #[test]
 fn combinations_with_replacement_no_collect() {
     no_collect_test(|iter| iter.combinations_with_replacement(5))
-}
\ No newline at end of file
+}
diff --git a/crates/itertools/tests/laziness.rs b/crates/itertools/tests/laziness.rs
new file mode 100644
index 0000000..c559d33
--- /dev/null
+++ b/crates/itertools/tests/laziness.rs
@@ -0,0 +1,283 @@
+#![allow(unstable_name_collisions)]
+
+use itertools::Itertools;
+
+#[derive(Debug, Clone)]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+struct Panicking;
+
+impl Iterator for Panicking {
+    type Item = u8;
+
+    fn next(&mut self) -> Option<u8> {
+        panic!("iterator adaptor is not lazy")
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, Some(0))
+    }
+}
+
+impl ExactSizeIterator for Panicking {}
+
+/// ## Usage example
+/// ```compile_fail
+/// must_use_tests! {
+///     name {
+///         Panicking.name(); // Add `let _ =` only if required (encountered error).
+///     }
+///     // ...
+/// }
+/// ```
+///
+/// **TODO:** test missing `must_use` attributes better, maybe with a new lint.
+macro_rules! must_use_tests {
+    ($($(#[$attr:meta])* $name:ident $body:block)*) => {
+        $(
+            /// `#[deny(unused_must_use)]` should force us to ignore the resulting iterators
+            /// by adding `let _ = ...;` on every iterator.
+            /// If it does not, then a `must_use` attribute is missing on the associated struct.
+            ///
+            /// However, it's only helpful if we don't add `let _ =` before seeing if there is an error or not.
+            /// And it does not protect us against removed `must_use` attributes.
+            /// There is no simple way to test this yet.
+            #[deny(unused_must_use)]
+            #[test]
+            $(#[$attr])*
+            fn $name() $body
+        )*
+    };
+}
+
+must_use_tests! {
+    // Itertools trait:
+    interleave {
+        let _ = Panicking.interleave(Panicking);
+    }
+    interleave_shortest {
+        let _ = Panicking.interleave_shortest(Panicking);
+    }
+    intersperse {
+        let _ = Panicking.intersperse(0);
+    }
+    intersperse_with {
+        let _ = Panicking.intersperse_with(|| 0);
+    }
+    get {
+        let _ = Panicking.get(1..4);
+        let _ = Panicking.get(1..=4);
+        let _ = Panicking.get(1..);
+        let _ = Panicking.get(..4);
+        let _ = Panicking.get(..=4);
+        let _ = Panicking.get(..);
+    }
+    zip_longest {
+        let _ = Panicking.zip_longest(Panicking);
+    }
+    zip_eq {
+        let _ = Panicking.zip_eq(Panicking);
+    }
+    batching {
+        let _ = Panicking.batching(Iterator::next);
+    }
+    chunk_by {
+        // ChunkBy
+        let _ = Panicking.chunk_by(|x| *x);
+        // Groups
+        let _ = Panicking.chunk_by(|x| *x).into_iter();
+    }
+    chunks {
+        // IntoChunks
+        let _ = Panicking.chunks(1);
+        let _ = Panicking.chunks(2);
+        // Chunks
+        let _ = Panicking.chunks(1).into_iter();
+        let _ = Panicking.chunks(2).into_iter();
+    }
+    tuple_windows {
+        let _ = Panicking.tuple_windows::<(_,)>();
+        let _ = Panicking.tuple_windows::<(_, _)>();
+        let _ = Panicking.tuple_windows::<(_, _, _)>();
+    }
+    circular_tuple_windows {
+        let _ = Panicking.circular_tuple_windows::<(_,)>();
+        let _ = Panicking.circular_tuple_windows::<(_, _)>();
+        let _ = Panicking.circular_tuple_windows::<(_, _, _)>();
+    }
+    tuples {
+        let _ = Panicking.tuples::<(_,)>();
+        let _ = Panicking.tuples::<(_, _)>();
+        let _ = Panicking.tuples::<(_, _, _)>();
+    }
+    tee {
+        let _ = Panicking.tee();
+    }
+    map_into {
+        let _ = Panicking.map_into::<u16>();
+    }
+    map_ok {
+        let _ = Panicking.map(Ok::<u8, ()>).map_ok(|x| x + 1);
+    }
+    filter_ok {
+        let _ = Panicking.map(Ok::<u8, ()>).filter_ok(|x| x % 2 == 0);
+    }
+    filter_map_ok {
+        let _ = Panicking.map(Ok::<u8, ()>).filter_map_ok(|x| {
+            if x % 2 == 0 {
+                Some(x + 1)
+            } else {
+                None
+            }
+        });
+    }
+    flatten_ok {
+        let _ = Panicking.map(|x| Ok::<_, ()>([x])).flatten_ok();
+    }
+    merge {
+        let _ = Panicking.merge(Panicking);
+    }
+    merge_by {
+        let _ = Panicking.merge_by(Panicking, |_, _| true);
+    }
+    merge_join_by {
+        let _ = Panicking.merge_join_by(Panicking, |_, _| true);
+        let _ = Panicking.merge_join_by(Panicking, Ord::cmp);
+    }
+    #[should_panic]
+    kmerge {
+        let _ = Panicking.map(|_| Panicking).kmerge();
+    }
+    #[should_panic]
+    kmerge_by {
+        let _ = Panicking.map(|_| Panicking).kmerge_by(|_, _| true);
+    }
+    cartesian_product {
+        let _ = Panicking.cartesian_product(Panicking);
+    }
+    multi_cartesian_product {
+        let _ = vec![Panicking, Panicking, Panicking].into_iter().multi_cartesian_product();
+    }
+    coalesce {
+        let _ = Panicking.coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) });
+    }
+    dedup {
+        let _ = Panicking.dedup();
+    }
+    dedup_by {
+        let _ = Panicking.dedup_by(|_, _| true);
+    }
+    dedup_with_count {
+        let _ = Panicking.dedup_with_count();
+    }
+    dedup_by_with_count {
+        let _ = Panicking.dedup_by_with_count(|_, _| true);
+    }
+    duplicates {
+        let _ = Panicking.duplicates();
+    }
+    duplicates_by {
+        let _ = Panicking.duplicates_by(|x| *x);
+    }
+    unique {
+        let _ = Panicking.unique();
+    }
+    unique_by {
+        let _ = Panicking.unique_by(|x| *x);
+    }
+    peeking_take_while {
+        let _ = Panicking.peekable().peeking_take_while(|x| x % 2 == 0);
+    }
+    take_while_ref {
+        let _ = Panicking.take_while_ref(|x| x % 2 == 0);
+    }
+    take_while_inclusive {
+        let _ = Panicking.take_while_inclusive(|x| x % 2 == 0);
+    }
+    while_some {
+        let _ = Panicking.map(Some).while_some();
+    }
+    tuple_combinations1 {
+        let _ = Panicking.tuple_combinations::<(_,)>();
+    }
+    #[should_panic]
+    tuple_combinations2 {
+        let _ = Panicking.tuple_combinations::<(_, _)>();
+    }
+    #[should_panic]
+    tuple_combinations3 {
+        let _ = Panicking.tuple_combinations::<(_, _, _)>();
+    }
+    combinations {
+        let _ = Panicking.combinations(0);
+        let _ = Panicking.combinations(1);
+        let _ = Panicking.combinations(2);
+    }
+    combinations_with_replacement {
+        let _ = Panicking.combinations_with_replacement(0);
+        let _ = Panicking.combinations_with_replacement(1);
+        let _ = Panicking.combinations_with_replacement(2);
+    }
+    permutations {
+        let _ = Panicking.permutations(0);
+        let _ = Panicking.permutations(1);
+        let _ = Panicking.permutations(2);
+    }
+    powerset {
+        let _ = Panicking.powerset();
+    }
+    pad_using {
+        let _ = Panicking.pad_using(25, |_| 10);
+    }
+    with_position {
+        let _ = Panicking.with_position();
+    }
+    positions {
+        let _ = Panicking.positions(|v| v % 2 == 0);
+    }
+    update {
+        let _ = Panicking.update(|n| *n += 1);
+    }
+    multipeek {
+        let _ = Panicking.multipeek();
+    }
+    // Not iterator themselves but still lazy.
+    into_grouping_map {
+        let _ = Panicking.map(|x| (x, x + 1)).into_grouping_map();
+    }
+    into_grouping_map_by {
+        let _ = Panicking.into_grouping_map_by(|x| *x);
+    }
+    // Macros:
+    iproduct {
+        let _ = itertools::iproduct!(Panicking);
+        let _ = itertools::iproduct!(Panicking, Panicking);
+        let _ = itertools::iproduct!(Panicking, Panicking, Panicking);
+    }
+    izip {
+        let _ = itertools::izip!(Panicking);
+        let _ = itertools::izip!(Panicking, Panicking);
+        let _ = itertools::izip!(Panicking, Panicking, Panicking);
+    }
+    chain {
+        let _ = itertools::chain!(Panicking);
+        let _ = itertools::chain!(Panicking, Panicking);
+        let _ = itertools::chain!(Panicking, Panicking, Panicking);
+    }
+    // Free functions:
+    multizip {
+        let _ = itertools::multizip((Panicking, Panicking));
+    }
+    put_back {
+        let _ = itertools::put_back(Panicking);
+        let _ = itertools::put_back(Panicking).with_value(15);
+    }
+    peek_nth {
+        let _ = itertools::peek_nth(Panicking);
+    }
+    put_back_n {
+        let _ = itertools::put_back_n(Panicking);
+    }
+    rciter {
+        let _ = itertools::rciter(Panicking);
+    }
+}
diff --git a/crates/itertools/tests/macros_hygiene.rs b/crates/itertools/tests/macros_hygiene.rs
index d111124..20b59fb 100644
--- a/crates/itertools/tests/macros_hygiene.rs
+++ b/crates/itertools/tests/macros_hygiene.rs
@@ -1,5 +1,6 @@
 #[test]
 fn iproduct_hygiene() {
+    let _ = itertools::iproduct!();
     let _ = itertools::iproduct!(0..6);
     let _ = itertools::iproduct!(0..6, 0..9);
     let _ = itertools::iproduct!(0..6, 0..9, 0..12);
diff --git a/crates/itertools/tests/merge_join.rs b/crates/itertools/tests/merge_join.rs
index 3280b7d..776252f 100644
--- a/crates/itertools/tests/merge_join.rs
+++ b/crates/itertools/tests/merge_join.rs
@@ -1,108 +1,101 @@
-use itertools::EitherOrBoth;
 use itertools::free::merge_join_by;
+use itertools::EitherOrBoth;
 
 #[test]
 fn empty() {
     let left: Vec<u32> = vec![];
     let right: Vec<u32> = vec![];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![];
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
     assert_eq!(expected_result, actual_result);
 }
 
 #[test]
 fn left_only() {
-    let left: Vec<u32> = vec![1,2,3];
+    let left: Vec<u32> = vec![1, 2, 3];
     let right: Vec<u32> = vec![];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
         EitherOrBoth::Left(1),
         EitherOrBoth::Left(2),
-        EitherOrBoth::Left(3)
+        EitherOrBoth::Left(3),
     ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
     assert_eq!(expected_result, actual_result);
 }
 
 #[test]
 fn right_only() {
     let left: Vec<u32> = vec![];
-    let right: Vec<u32> = vec![1,2,3];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
+    let right: Vec<u32> = vec![1, 2, 3];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
         EitherOrBoth::Right(1),
         EitherOrBoth::Right(2),
-        EitherOrBoth::Right(3)
+        EitherOrBoth::Right(3),
     ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
     assert_eq!(expected_result, actual_result);
 }
 
 #[test]
 fn first_left_then_right() {
-    let left: Vec<u32> = vec![1,2,3];
-    let right: Vec<u32> = vec![4,5,6];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
+    let left: Vec<u32> = vec![1, 2, 3];
+    let right: Vec<u32> = vec![4, 5, 6];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
         EitherOrBoth::Left(1),
         EitherOrBoth::Left(2),
         EitherOrBoth::Left(3),
         EitherOrBoth::Right(4),
         EitherOrBoth::Right(5),
-        EitherOrBoth::Right(6)
+        EitherOrBoth::Right(6),
     ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
     assert_eq!(expected_result, actual_result);
 }
 
 #[test]
 fn first_right_then_left() {
-    let left: Vec<u32> = vec![4,5,6];
-    let right: Vec<u32> = vec![1,2,3];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
+    let left: Vec<u32> = vec![4, 5, 6];
+    let right: Vec<u32> = vec![1, 2, 3];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
         EitherOrBoth::Right(1),
         EitherOrBoth::Right(2),
         EitherOrBoth::Right(3),
         EitherOrBoth::Left(4),
         EitherOrBoth::Left(5),
-        EitherOrBoth::Left(6)
+        EitherOrBoth::Left(6),
     ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
     assert_eq!(expected_result, actual_result);
 }
 
 #[test]
 fn interspersed_left_and_right() {
-    let left: Vec<u32> = vec![1,3,5];
-    let right: Vec<u32> = vec![2,4,6];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
+    let left: Vec<u32> = vec![1, 3, 5];
+    let right: Vec<u32> = vec![2, 4, 6];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
         EitherOrBoth::Left(1),
         EitherOrBoth::Right(2),
         EitherOrBoth::Left(3),
         EitherOrBoth::Right(4),
         EitherOrBoth::Left(5),
-        EitherOrBoth::Right(6)
+        EitherOrBoth::Right(6),
     ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
     assert_eq!(expected_result, actual_result);
 }
 
 #[test]
 fn overlapping_left_and_right() {
-    let left: Vec<u32> = vec![1,3,4,6];
-    let right: Vec<u32> = vec![2,3,4,5];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
+    let left: Vec<u32> = vec![1, 3, 4, 6];
+    let right: Vec<u32> = vec![2, 3, 4, 5];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
         EitherOrBoth::Left(1),
         EitherOrBoth::Right(2),
         EitherOrBoth::Both(3, 3),
         EitherOrBoth::Both(4, 4),
         EitherOrBoth::Right(5),
-        EitherOrBoth::Left(6)
+        EitherOrBoth::Left(6),
     ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
     assert_eq!(expected_result, actual_result);
 }
diff --git a/crates/itertools/tests/peeking_take_while.rs b/crates/itertools/tests/peeking_take_while.rs
index a114702..5be9727 100644
--- a/crates/itertools/tests/peeking_take_while.rs
+++ b/crates/itertools/tests/peeking_take_while.rs
@@ -48,3 +48,22 @@
     r.peeking_take_while(|_| true).count();
     assert_eq!(r.next(), None);
 }
+
+#[test]
+fn peeking_take_while_nested() {
+    let mut xs = (0..10).peekable();
+    let ys: Vec<_> = xs
+        .peeking_take_while(|x| *x < 6)
+        .peeking_take_while(|x| *x != 3)
+        .collect();
+    assert_eq!(ys, vec![0, 1, 2]);
+    assert_eq!(xs.next(), Some(3));
+
+    let mut xs = (4..10).peekable();
+    let ys: Vec<_> = xs
+        .peeking_take_while(|x| *x != 3)
+        .peeking_take_while(|x| *x < 6)
+        .collect();
+    assert_eq!(ys, vec![4, 5]);
+    assert_eq!(xs.next(), Some(6));
+}
diff --git a/crates/itertools/tests/quick.rs b/crates/itertools/tests/quick.rs
index 0adcf1a..5b8fd6a 100644
--- a/crates/itertools/tests/quick.rs
+++ b/crates/itertools/tests/quick.rs
@@ -3,34 +3,23 @@
 //!
 //! In particular we test the tedious size_hint and exact size correctness.
 
+#![allow(deprecated, unstable_name_collisions)]
+
+use itertools::free::{
+    cloned, enumerate, multipeek, peek_nth, put_back, put_back_n, rciter, zip, zip_eq,
+};
+use itertools::Itertools;
+use itertools::{iproduct, izip, multizip, EitherOrBoth};
 use quickcheck as qc;
+use std::cmp::{max, min, Ordering};
+use std::collections::{HashMap, HashSet};
 use std::default::Default;
 use std::num::Wrapping;
 use std::ops::Range;
-use std::cmp::{max, min, Ordering};
-use std::collections::{HashMap, HashSet};
-use itertools::Itertools;
-use itertools::{
-    multizip,
-    EitherOrBoth,
-    iproduct,
-    izip,
-};
-use itertools::free::{
-    cloned,
-    enumerate,
-    multipeek,
-    peek_nth,
-    put_back,
-    put_back_n,
-    rciter,
-    zip,
-    zip_eq,
-};
 
-use rand::Rng;
-use rand::seq::SliceRandom;
 use quickcheck::TestResult;
+use rand::seq::SliceRandom;
+use rand::Rng;
 
 /// Trait for size hint modifier types
 trait HintKind: Copy + Send + qc::Arbitrary {
@@ -49,7 +38,7 @@
 
 impl qc::Arbitrary for Exact {
     fn arbitrary<G: qc::Gen>(_: &mut G) -> Self {
-        Exact {}
+        Self {}
     }
 }
 
@@ -66,8 +55,10 @@
 impl HintKind for Inexact {
     fn loosen_bounds(&self, org_hint: (usize, Option<usize>)) -> (usize, Option<usize>) {
         let (org_lower, org_upper) = org_hint;
-        (org_lower.saturating_sub(self.underestimate),
-         org_upper.and_then(move |x| x.checked_add(self.overestimate)))
+        (
+            org_lower.saturating_sub(self.underestimate),
+            org_upper.and_then(move |x| x.checked_add(self.overestimate)),
+        )
     }
 }
 
@@ -76,27 +67,23 @@
         let ue_value = usize::arbitrary(g);
         let oe_value = usize::arbitrary(g);
         // Compensate for quickcheck using extreme values too rarely
-        let ue_choices = &[0, ue_value, usize::max_value()];
-        let oe_choices = &[0, oe_value, usize::max_value()];
-        Inexact {
+        let ue_choices = &[0, ue_value, usize::MAX];
+        let oe_choices = &[0, oe_value, usize::MAX];
+        Self {
             underestimate: *ue_choices.choose(g).unwrap(),
             overestimate: *oe_choices.choose(g).unwrap(),
         }
     }
 
-    fn shrink(&self) -> Box<dyn Iterator<Item=Self>> {
+    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
         let underestimate_value = self.underestimate;
         let overestimate_value = self.overestimate;
-        Box::new(
-            underestimate_value.shrink().flat_map(move |ue_value|
-                overestimate_value.shrink().map(move |oe_value|
-                    Inexact {
-                        underestimate: ue_value,
-                        overestimate: oe_value,
-                    }
-                )
-            )
-        )
+        Box::new(underestimate_value.shrink().flat_map(move |ue_value| {
+            overestimate_value.shrink().map(move |oe_value| Self {
+                underestimate: ue_value,
+                overestimate: oe_value,
+            })
+        }))
     }
 }
 
@@ -116,10 +103,12 @@
     hint_kind: SK,
 }
 
-impl<T, HK> Iter<T, HK> where HK: HintKind
+impl<T, HK> Iter<T, HK>
+where
+    HK: HintKind,
 {
     fn new(it: Range<T>, hint_kind: HK) -> Self {
-        Iter {
+        Self {
             iterator: it,
             fuse_flag: 0,
             hint_kind,
@@ -128,64 +117,66 @@
 }
 
 impl<T, HK> Iterator for Iter<T, HK>
-    where Range<T>: Iterator,
-          <Range<T> as Iterator>::Item: Default,
-          HK: HintKind,
+where
+    Range<T>: Iterator,
+    <Range<T> as Iterator>::Item: Default,
+    HK: HintKind,
 {
     type Item = <Range<T> as Iterator>::Item;
 
-    fn next(&mut self) -> Option<Self::Item>
-    {
+    fn next(&mut self) -> Option<Self::Item> {
         let elt = self.iterator.next();
         if elt.is_none() {
             self.fuse_flag += 1;
             // check fuse flag
             if self.fuse_flag == 2 {
-                return Some(Default::default())
+                return Some(Default::default());
             }
         }
         elt
     }
 
-    fn size_hint(&self) -> (usize, Option<usize>)
-    {
+    fn size_hint(&self) -> (usize, Option<usize>) {
         let org_hint = self.iterator.size_hint();
         self.hint_kind.loosen_bounds(org_hint)
     }
 }
 
 impl<T, HK> DoubleEndedIterator for Iter<T, HK>
-    where Range<T>: DoubleEndedIterator,
-          <Range<T> as Iterator>::Item: Default,
-          HK: HintKind
+where
+    Range<T>: DoubleEndedIterator,
+    <Range<T> as Iterator>::Item: Default,
+    HK: HintKind,
 {
-    fn next_back(&mut self) -> Option<Self::Item> { self.iterator.next_back() }
+    fn next_back(&mut self) -> Option<Self::Item> {
+        self.iterator.next_back()
+    }
 }
 
-impl<T> ExactSizeIterator for Iter<T, Exact> where Range<T>: ExactSizeIterator,
+impl<T> ExactSizeIterator for Iter<T, Exact>
+where
+    Range<T>: ExactSizeIterator,
     <Range<T> as Iterator>::Item: Default,
-{ }
+{
+}
 
 impl<T, HK> qc::Arbitrary for Iter<T, HK>
-    where T: qc::Arbitrary,
-          HK: HintKind,
+where
+    T: qc::Arbitrary,
+    HK: HintKind,
 {
-    fn arbitrary<G: qc::Gen>(g: &mut G) -> Self
-    {
-        Iter::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g))
+    fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
+        Self::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g))
     }
 
-    fn shrink(&self) -> Box<dyn Iterator<Item=Iter<T, HK>>>
-    {
+    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
         let r = self.iterator.clone();
         let hint_kind = self.hint_kind;
-        Box::new(
-            r.start.shrink().flat_map(move |a|
-                r.end.shrink().map(move |b|
-                    Iter::new(a.clone()..b, hint_kind)
-                )
-            )
-        )
+        Box::new(r.start.shrink().flat_map(move |a| {
+            r.end
+                .shrink()
+                .map(move |b| Self::new(a.clone()..b, hint_kind))
+        }))
     }
 }
 
@@ -201,7 +192,10 @@
     hint_kind: HK,
 }
 
-impl<HK> Iterator for ShiftRange<HK> where HK: HintKind {
+impl<HK> Iterator for ShiftRange<HK>
+where
+    HK: HintKind,
+{
     type Item = Iter<i32, HK>;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -219,10 +213,11 @@
     }
 }
 
-impl ExactSizeIterator for ShiftRange<Exact> { }
+impl ExactSizeIterator for ShiftRange<Exact> {}
 
 impl<HK> qc::Arbitrary for ShiftRange<HK>
-    where HK: HintKind
+where
+    HK: HintKind,
 {
     fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
         const MAX_STARTING_RANGE_DIFF: i32 = 32;
@@ -236,7 +231,7 @@
         let iter_count = g.gen_range(0, MAX_ITER_COUNT + 1);
         let hint_kind = qc::Arbitrary::arbitrary(g);
 
-        ShiftRange {
+        Self {
             range_start,
             range_end,
             start_step,
@@ -250,7 +245,7 @@
 fn correct_count<I, F>(get_it: F) -> bool
 where
     I: Iterator,
-    F: Fn() -> I
+    F: Fn() -> I,
 {
     let mut counts = vec![get_it().count()];
 
@@ -276,7 +271,10 @@
     for (i, returned_count) in counts.into_iter().enumerate() {
         let actual_count = total_actual_count - i;
         if actual_count != returned_count {
-            println!("Total iterations: {} True count: {} returned count: {}", i, actual_count, returned_count);
+            println!(
+                "Total iterations: {} True count: {} returned count: {}",
+                i, actual_count, returned_count
+            );
 
             return false;
         }
@@ -299,12 +297,10 @@
     // check all the size hints
     for &(low, hi) in &hints {
         true_count -= 1;
-        if low > true_count ||
-            (hi.is_some() && hi.unwrap() < true_count)
-        {
+        if low > true_count || (hi.is_some() && hi.unwrap() < true_count) {
             println!("True size: {:?}, size hint: {:?}", true_count, (low, hi));
             //println!("All hints: {:?}", hints);
-            return false
+            return false;
         }
     }
     true
@@ -313,13 +309,19 @@
 fn exact_size<I: ExactSizeIterator>(mut it: I) -> bool {
     // check every iteration
     let (mut low, mut hi) = it.size_hint();
-    if Some(low) != hi { return false; }
+    if Some(low) != hi {
+        return false;
+    }
     while let Some(_) = it.next() {
         let (xlow, xhi) = it.size_hint();
-        if low != xlow + 1 { return false; }
+        if low != xlow + 1 {
+            return false;
+        }
         low = xlow;
         hi = xhi;
-        if Some(low) != hi { return false; }
+        if Some(low) != hi {
+            return false;
+        }
     }
     let (low, hi) = it.size_hint();
     low == 0 && hi == Some(0)
@@ -329,13 +331,19 @@
 fn exact_size_for_this<I: Iterator>(mut it: I) -> bool {
     // check every iteration
     let (mut low, mut hi) = it.size_hint();
-    if Some(low) != hi { return false; }
+    if Some(low) != hi {
+        return false;
+    }
     while let Some(_) = it.next() {
         let (xlow, xhi) = it.size_hint();
-        if low != xlow + 1 { return false; }
+        if low != xlow + 1 {
+            return false;
+        }
         low = xlow;
         hi = xhi;
-        if Some(low) != hi { return false; }
+        if Some(low) != hi {
+            return false;
+        }
     }
     let (low, hi) = it.size_hint();
     low == 0 && hi == Some(0)
@@ -442,43 +450,10 @@
         assert_eq!(answer.into_iter().last(), a.multi_cartesian_product().last());
     }
 
-    #[allow(deprecated)]
-    fn size_step(a: Iter<i16, Exact>, s: usize) -> bool {
-        let mut s = s;
-        if s == 0 {
-            s += 1; // never zero
-        }
-        let filt = a.clone().dedup();
-        correct_size_hint(filt.step(s)) &&
-            exact_size(a.step(s))
-    }
-
-    #[allow(deprecated)]
-    fn equal_step(a: Iter<i16>, s: usize) -> bool {
-        let mut s = s;
-        if s == 0 {
-            s += 1; // never zero
-        }
-        let mut i = 0;
-        itertools::equal(a.clone().step(s), a.filter(|_| {
-            let keep = i % s == 0;
-            i += 1;
-            keep
-        }))
-    }
-
-    #[allow(deprecated)]
-    fn equal_step_vec(a: Vec<i16>, s: usize) -> bool {
-        let mut s = s;
-        if s == 0 {
-            s += 1; // never zero
-        }
-        let mut i = 0;
-        itertools::equal(a.iter().step(s), a.iter().filter(|_| {
-            let keep = i % s == 0;
-            i += 1;
-            keep
-        }))
+    fn correct_empty_multi_product() -> () {
+        let empty = Vec::<std::vec::IntoIter<i32>>::new().into_iter().multi_cartesian_product();
+        assert!(correct_size_hint(empty.clone()));
+        itertools::assert_equal(empty, std::iter::once(Vec::new()))
     }
 
     fn size_multipeek(a: Iter<u16, Exact>, s: u8) -> bool {
@@ -596,6 +571,20 @@
         let b = &b[..len];
         itertools::equal(zip_eq(a, b), zip(a, b))
     }
+
+    #[should_panic]
+    fn zip_eq_panics(a: Vec<u8>, b: Vec<u8>) -> TestResult {
+        if a.len() == b.len() { return TestResult::discard(); }
+        zip_eq(a.iter(), b.iter()).for_each(|_| {});
+        TestResult::passed() // won't come here
+    }
+
+    fn equal_positions(a: Vec<i32>) -> bool {
+        let with_pos = a.iter().positions(|v| v % 2 == 0);
+        let without = a.iter().enumerate().filter(|(_, v)| *v % 2 == 0).map(|(i, _)| i);
+        itertools::equal(with_pos.clone(), without.clone())
+            && itertools::equal(with_pos.rev(), without.rev())
+    }
     fn size_zip_longest(a: Iter<i16, Exact>, b: Iter<i16, Exact>) -> bool {
         let filt = a.clone().dedup();
         let filt2 = b.clone().dedup();
@@ -751,6 +740,56 @@
 }
 
 quickcheck! {
+    fn correct_peek_nth(mut a: Vec<u16>) -> () {
+        let mut it = peek_nth(a.clone());
+        for start_pos in 0..a.len() + 2 {
+            for real_idx in start_pos..a.len() + 2 {
+                let peek_idx = real_idx - start_pos;
+                assert_eq!(it.peek_nth(peek_idx), a.get(real_idx));
+                assert_eq!(it.peek_nth_mut(peek_idx), a.get_mut(real_idx));
+            }
+            assert_eq!(it.next(), a.get(start_pos).copied());
+        }
+    }
+
+    fn peek_nth_mut_replace(a: Vec<u16>, b: Vec<u16>) -> () {
+        let mut it = peek_nth(a.iter());
+        for (i, m) in b.iter().enumerate().take(a.len().min(b.len())) {
+            *it.peek_nth_mut(i).unwrap() = m;
+        }
+        for (i, m) in a.iter().enumerate() {
+             assert_eq!(it.next().unwrap(), b.get(i).unwrap_or(m));
+        }
+        assert_eq!(it.next(), None);
+        assert_eq!(it.next(), None);
+    }
+
+    fn peek_nth_next_if(a: Vec<u8>) -> () {
+        let mut it = peek_nth(a.clone());
+        for (idx, mut value) in a.iter().copied().enumerate() {
+            let should_be_none = it.next_if(|x| x != &value);
+            assert_eq!(should_be_none, None);
+            if value % 5 == 0 {
+                // Sometimes, peek up to 3 further.
+                let n = value as usize % 3;
+                let nth = it.peek_nth(n);
+                assert_eq!(nth, a.get(idx + n));
+            } else if value % 5 == 1 {
+                // Sometimes, peek next element mutably.
+                if let Some(v) = it.peek_mut() {
+                    *v = v.wrapping_sub(1);
+                    let should_be_none = it.next_if_eq(&value);
+                    assert_eq!(should_be_none, None);
+                    value = value.wrapping_sub(1);
+                }
+            }
+            let eq = it.next_if_eq(&value);
+            assert_eq!(eq, Some(value));
+        }
+    }
+}
+
+quickcheck! {
     fn dedup_via_coalesce(a: Vec<i32>) -> bool {
         let mut b = a.clone();
         b.dedup();
@@ -811,9 +850,8 @@
 quickcheck! {
     fn size_put_back(a: Vec<u8>, x: Option<u8>) -> bool {
         let mut it = put_back(a.into_iter());
-        match x {
-            Some(t) => it.put_back(t),
-            None => {}
+        if let Some(t) = x {
+            it.put_back(t);
         }
         correct_size_hint(it)
     }
@@ -830,6 +868,31 @@
 }
 
 quickcheck! {
+    fn merge_join_by_ordering_vs_bool(a: Vec<u8>, b: Vec<u8>) -> bool {
+        use either::Either;
+        use itertools::free::merge_join_by;
+        let mut has_equal = false;
+        let it_ord = merge_join_by(a.clone(), b.clone(), Ord::cmp).flat_map(|v| match v {
+            EitherOrBoth::Both(l, r) => {
+                has_equal = true;
+                vec![Either::Left(l), Either::Right(r)]
+            }
+            EitherOrBoth::Left(l) => vec![Either::Left(l)],
+            EitherOrBoth::Right(r) => vec![Either::Right(r)],
+        });
+        let it_bool = merge_join_by(a, b, PartialOrd::le);
+        itertools::equal(it_ord, it_bool) || has_equal
+    }
+    fn merge_join_by_bool_unwrapped_is_merge_by(a: Vec<u8>, b: Vec<u8>) -> bool {
+        use either::Either;
+        use itertools::free::merge_join_by;
+        let it = a.clone().into_iter().merge_by(b.clone(), PartialOrd::ge);
+        let it_join = merge_join_by(a, b, PartialOrd::ge).map(Either::into_inner);
+        itertools::equal(it, it_join)
+    }
+}
+
+quickcheck! {
     fn size_tee(a: Vec<u8>) -> bool {
         let (mut t1, mut t2) = a.iter().tee();
         t1.next();
@@ -870,8 +933,31 @@
 }
 
 quickcheck! {
-    fn size_combinations(it: Iter<i16>) -> bool {
-        correct_size_hint(it.tuple_combinations::<(_, _)>())
+    fn size_combinations(a: Iter<i16>) -> bool {
+        let it = a.clone().tuple_combinations::<(_, _)>();
+        correct_size_hint(it.clone()) && it.count() == binomial(a.count(), 2)
+    }
+
+    fn exact_size_combinations_1(a: Vec<u8>) -> bool {
+        let it = a.iter().tuple_combinations::<(_,)>();
+        exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 1)
+    }
+    fn exact_size_combinations_2(a: Vec<u8>) -> bool {
+        let it = a.iter().tuple_combinations::<(_, _)>();
+        exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 2)
+    }
+    fn exact_size_combinations_3(mut a: Vec<u8>) -> bool {
+        a.truncate(15);
+        let it = a.iter().tuple_combinations::<(_, _, _)>();
+        exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 3)
+    }
+}
+
+fn binomial(n: usize, k: usize) -> usize {
+    if k > n {
+        0
+    } else {
+        (n - k + 1..=n).product::<usize>() / (1..=k).product::<usize>()
     }
 }
 
@@ -887,7 +973,7 @@
                 }
             }
         }
-        cmb.next() == None
+        cmb.next().is_none()
     }
 }
 
@@ -936,64 +1022,75 @@
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_1(it: Iter<u8>) -> bool {
+    fn fuzz_chunk_by_lazy_1(it: Iter<u8>) -> bool {
         let jt = it.clone();
-        let groups = it.group_by(|k| *k);
-        itertools::equal(jt, groups.into_iter().flat_map(|(_, x)| x))
+        let chunks = it.chunk_by(|k| *k);
+        itertools::equal(jt, chunks.into_iter().flat_map(|(_, x)| x))
     }
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_2(data: Vec<u8>) -> bool {
-        let groups = data.iter().group_by(|k| *k / 10);
-        let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
+    fn fuzz_chunk_by_lazy_2(data: Vec<u8>) -> bool {
+        let chunks = data.iter().chunk_by(|k| *k / 10);
+        let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
         res
     }
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_3(data: Vec<u8>) -> bool {
-        let grouper = data.iter().group_by(|k| *k / 10);
-        let groups = grouper.into_iter().collect_vec();
-        let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
+    fn fuzz_chunk_by_lazy_3(data: Vec<u8>) -> bool {
+        let grouper = data.iter().chunk_by(|k| *k / 10);
+        let chunks = grouper.into_iter().collect_vec();
+        let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
         res
     }
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
-        let grouper = data.iter().group_by(|k| *k / 3);
-        let mut groups1 = grouper.into_iter();
-        let mut groups2 = grouper.into_iter();
+    fn fuzz_chunk_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
+        let grouper = data.iter().chunk_by(|k| *k / 3);
+        let mut chunks1 = grouper.into_iter();
+        let mut chunks2 = grouper.into_iter();
         let mut elts = Vec::<&u8>::new();
-        let mut old_groups = Vec::new();
+        let mut old_chunks = Vec::new();
 
         let tup1 = |(_, b)| b;
         for &(ord, consume_now) in &order {
-            let iter = &mut [&mut groups1, &mut groups2][ord as usize];
+            let iter = &mut [&mut chunks1, &mut chunks2][ord as usize];
             match iter.next() {
                 Some((_, gr)) => if consume_now {
-                    for og in old_groups.drain(..) {
+                    for og in old_chunks.drain(..) {
                         elts.extend(og);
                     }
                     elts.extend(gr);
                 } else {
-                    old_groups.push(gr);
+                    old_chunks.push(gr);
                 },
                 None => break,
             }
         }
-        for og in old_groups.drain(..) {
+        for og in old_chunks.drain(..) {
             elts.extend(og);
         }
-        for gr in groups1.map(&tup1) { elts.extend(gr); }
-        for gr in groups2.map(&tup1) { elts.extend(gr); }
+        for gr in chunks1.map(&tup1) { elts.extend(gr); }
+        for gr in chunks2.map(&tup1) { elts.extend(gr); }
         itertools::assert_equal(&data, elts);
         true
     }
 }
 
 quickcheck! {
+    fn chunk_clone_equal(a: Vec<u8>, size: u8) -> () {
+        let mut size = size;
+        if size == 0 {
+            size += 1;
+        }
+        let it = a.chunks(size as usize);
+        itertools::assert_equal(it.clone(), it);
+    }
+}
+
+quickcheck! {
     fn equal_chunks_lazy(a: Vec<u8>, size: u8) -> bool {
         let mut size = size;
         if size == 0 {
@@ -1010,7 +1107,75 @@
     }
 }
 
+// tuple iterators
 quickcheck! {
+    fn equal_circular_tuple_windows_1(a: Vec<u8>) -> bool {
+        let x = a.iter().map(|e| (e,) );
+        let y = a.iter().circular_tuple_windows::<(_,)>();
+        itertools::assert_equal(x,y);
+        true
+    }
+
+    fn equal_circular_tuple_windows_2(a: Vec<u8>) -> bool {
+        let x = (0..a.len()).map(|start_idx| (
+            &a[start_idx],
+            &a[(start_idx + 1) % a.len()],
+        ));
+        let y = a.iter().circular_tuple_windows::<(_, _)>();
+        itertools::assert_equal(x,y);
+        true
+    }
+
+    fn equal_circular_tuple_windows_3(a: Vec<u8>) -> bool {
+        let x = (0..a.len()).map(|start_idx| (
+            &a[start_idx],
+            &a[(start_idx + 1) % a.len()],
+            &a[(start_idx + 2) % a.len()],
+        ));
+        let y = a.iter().circular_tuple_windows::<(_, _, _)>();
+        itertools::assert_equal(x,y);
+        true
+    }
+
+    fn equal_circular_tuple_windows_4(a: Vec<u8>) -> bool {
+        let x = (0..a.len()).map(|start_idx| (
+            &a[start_idx],
+            &a[(start_idx + 1) % a.len()],
+            &a[(start_idx + 2) % a.len()],
+            &a[(start_idx + 3) % a.len()],
+        ));
+        let y = a.iter().circular_tuple_windows::<(_, _, _, _)>();
+        itertools::assert_equal(x,y);
+        true
+    }
+
+    fn equal_cloned_circular_tuple_windows(a: Vec<u8>) -> bool {
+        let x = a.iter().circular_tuple_windows::<(_, _, _, _)>();
+        let y = x.clone();
+        itertools::assert_equal(x,y);
+        true
+    }
+
+    fn equal_cloned_circular_tuple_windows_noninitial(a: Vec<u8>) -> bool {
+        let mut x = a.iter().circular_tuple_windows::<(_, _, _, _)>();
+        let _ = x.next();
+        let y = x.clone();
+        itertools::assert_equal(x,y);
+        true
+    }
+
+    fn equal_cloned_circular_tuple_windows_complete(a: Vec<u8>) -> bool {
+        let mut x = a.iter().circular_tuple_windows::<(_, _, _, _)>();
+        for _ in x.by_ref() {}
+        let y = x.clone();
+        itertools::assert_equal(x,y);
+        true
+    }
+
+    fn circular_tuple_windows_exact_size(a: Vec<u8>) -> bool {
+        exact_size(a.iter().circular_tuple_windows::<(_, _, _, _)>())
+    }
+
     fn equal_tuple_windows_1(a: Vec<u8>) -> bool {
         let x = a.windows(1).map(|s| (&s[0], ));
         let y = a.iter().tuple_windows::<(_,)>();
@@ -1035,6 +1200,14 @@
         itertools::equal(x, y)
     }
 
+    fn tuple_windows_exact_size_1(a: Vec<u8>) -> bool {
+        exact_size(a.iter().tuple_windows::<(_,)>())
+    }
+
+    fn tuple_windows_exact_size_4(a: Vec<u8>) -> bool {
+        exact_size(a.iter().tuple_windows::<(_, _, _, _)>())
+    }
+
     fn equal_tuples_1(a: Vec<u8>) -> bool {
         let x = a.chunks(1).map(|s| (&s[0], ));
         let y = a.iter().tuples::<(_,)>();
@@ -1066,6 +1239,18 @@
         assert_eq!(buffer.len(), a.len() % 4);
         exact_size(buffer)
     }
+
+    fn tuples_size_hint_inexact(a: Iter<u8>) -> bool {
+        correct_size_hint(a.clone().tuples::<(_,)>())
+            && correct_size_hint(a.clone().tuples::<(_, _)>())
+            && correct_size_hint(a.tuples::<(_, _, _, _)>())
+    }
+
+    fn tuples_size_hint_exact(a: Iter<u8, Exact>) -> bool {
+        exact_size(a.clone().tuples::<(_,)>())
+            && exact_size(a.clone().tuples::<(_, _)>())
+            && exact_size(a.tuples::<(_, _, _, _)>())
+    }
 }
 
 // with_position
@@ -1097,14 +1282,14 @@
 #[derive(Clone, Debug, PartialEq, Eq)]
 struct Val(u32, u32);
 
-impl PartialOrd<Val> for Val {
-    fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
-        self.0.partial_cmp(&other.0)
+impl PartialOrd<Self> for Val {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
     }
 }
 
 impl Ord for Val {
-    fn cmp(&self, other: &Val) -> Ordering {
+    fn cmp(&self, other: &Self) -> Ordering {
         self.0.cmp(&other.0)
     }
 }
@@ -1112,10 +1297,10 @@
 impl qc::Arbitrary for Val {
     fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
         let (x, y) = <(u32, u32)>::arbitrary(g);
-        Val(x, y)
+        Self(x, y)
     }
     fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
-        Box::new((self.0, self.1).shrink().map(|(x, y)| Val(x, y)))
+        Box::new((self.0, self.1).shrink().map(|(x, y)| Self(x, y)))
     }
 }
 
@@ -1157,13 +1342,12 @@
 }
 
 quickcheck! {
-    #[allow(deprecated)]
-    fn tree_fold1_f64(mut a: Vec<f64>) -> TestResult {
+    fn tree_reduce_f64(mut a: Vec<f64>) -> TestResult {
         fn collapse_adjacent<F>(x: Vec<f64>, mut f: F) -> Vec<f64>
             where F: FnMut(f64, f64) -> f64
         {
             let mut out = Vec::new();
-            for i in (0..x.len()).step(2) {
+            for i in (0..x.len()).step_by(2) {
                 if i == x.len()-1 {
                     out.push(x[i])
                 } else {
@@ -1177,7 +1361,7 @@
             return TestResult::discard();
         }
 
-        let actual = a.iter().cloned().tree_fold1(f64::atan2);
+        let actual = a.iter().cloned().tree_reduce(f64::atan2);
 
         while a.len() > 1 {
             a = collapse_adjacent(a, f64::atan2);
@@ -1202,7 +1386,7 @@
     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),
+            0 => TestResult::from_bool(ret.unwrap().is_none()),
             1 => TestResult::from_bool(ret.unwrap() == Some(a[0])),
             _ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())),
         }
@@ -1232,7 +1416,7 @@
                     Some(acc.unwrap_or(0) + val)
                 }
             });
-        
+
         let group_map_lookup = a.iter()
             .map(|&b| b as u64)
             .map(|i| (i % modulo, i))
@@ -1252,7 +1436,7 @@
 
         for m in 0..modulo {
             assert_eq!(
-                lookup.get(&m).copied(), 
+                lookup.get(&m).copied(),
                 a.iter()
                     .map(|&b| b as u64)
                     .filter(|&val| val % modulo == m)
@@ -1267,6 +1451,35 @@
         }
     }
 
+    fn correct_grouping_map_by_fold_with_modulo_key(a: Vec<u8>, modulo: u8) -> () {
+        #[derive(Debug, Default, PartialEq)]
+        struct Accumulator {
+            acc: u64,
+        }
+
+        let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
+        let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
+            .into_grouping_map_by(|i| i % modulo)
+            .fold_with(|_key, _val| Default::default(), |Accumulator { acc }, &key, val| {
+                assert!(val % modulo == key);
+                let acc = acc + val;
+                Accumulator { acc }
+            });
+
+        let group_map_lookup = a.iter()
+            .map(|&b| b as u64)
+            .map(|i| (i % modulo, i))
+            .into_group_map()
+            .into_iter()
+            .map(|(key, vals)| (key, vals.into_iter().sum())).map(|(key, acc)| (key,Accumulator { acc }))
+            .collect::<HashMap<_,_>>();
+        assert_eq!(lookup, group_map_lookup);
+
+        for (&key, &Accumulator { acc: sum }) in lookup.iter() {
+            assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::<u64>());
+        }
+    }
+
     fn correct_grouping_map_by_fold_modulo_key(a: Vec<u8>, modulo: u8) -> () {
         let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
         let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
@@ -1290,16 +1503,16 @@
         }
     }
 
-    fn correct_grouping_map_by_fold_first_modulo_key(a: Vec<u8>, modulo: u8) -> () {
+    fn correct_grouping_map_by_reduce_modulo_key(a: Vec<u8>, modulo: u8) -> () {
         let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
         let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
             .into_grouping_map_by(|i| i % modulo)
-            .fold_first(|acc, &key, val| {
+            .reduce(|acc, &key, val| {
                 assert!(val % modulo == key);
                 acc + val
             });
 
-        // TODO: Swap `fold1` with stdlib's `fold_first` when it's stabilized
+        // TODO: Swap `fold1` with stdlib's `reduce` when it's stabilized
         let group_map_lookup = a.iter()
             .map(|&b| b as u64)
             .map(|i| (i % modulo, i))
@@ -1372,7 +1585,7 @@
             assert_eq!(Some(max), a.iter().copied().filter(|&val| val % modulo == key).max_by_key(|&val| val));
         }
     }
-    
+
     fn correct_grouping_map_by_min_modulo_key(a: Vec<u8>, modulo: u8) -> () {
         let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0`
         let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).min();
@@ -1423,7 +1636,7 @@
             assert_eq!(Some(min), a.iter().copied().filter(|&val| val % modulo == key).min_by_key(|&val| val));
         }
     }
-    
+
     fn correct_grouping_map_by_minmax_modulo_key(a: Vec<u8>, modulo: u8) -> () {
         let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0`
         let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).minmax();
@@ -1536,7 +1749,7 @@
             .min_by(|_, _, _| Ordering::Equal);
 
         assert_eq!(lookup[&0], 0);
-        
+
         let lookup = (0..=10)
             .into_grouping_map_by(|_| 0)
             .minmax_by(|_, _, _| Ordering::Equal);
@@ -1594,12 +1807,10 @@
     }
 }
 
-
-fn is_fused<I: Iterator>(mut it: I) -> bool
-{
+fn is_fused<I: Iterator>(mut it: I) -> bool {
     for _ in it.by_ref() {}
-    for _ in 0..10{
-        if it.next().is_some(){
+    for _ in 0..10 {
+        if it.next().is_some() {
             return false;
         }
     }
@@ -1640,7 +1851,7 @@
         !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()))
@@ -1746,4 +1957,11 @@
             result_set.is_empty()
         }
     }
+
+    fn tail(v: Vec<i32>, n: u8) -> bool {
+        let n = n as usize;
+        let result = &v[v.len().saturating_sub(n)..];
+        itertools::equal(v.iter().tail(n), result)
+            && itertools::equal(v.iter().filter(|_| true).tail(n), result)
+    }
 }
diff --git a/crates/itertools/tests/specializations.rs b/crates/itertools/tests/specializations.rs
index 057e11c..7123114 100644
--- a/crates/itertools/tests/specializations.rs
+++ b/crates/itertools/tests/specializations.rs
@@ -1,8 +1,13 @@
+#![allow(unstable_name_collisions)]
+
 use itertools::Itertools;
+use quickcheck::Arbitrary;
+use quickcheck::{quickcheck, TestResult};
+use rand::Rng;
 use std::fmt::Debug;
-use quickcheck::quickcheck;
 
 struct Unspecialized<I>(I);
+
 impl<I> Iterator for Unspecialized<I>
 where
     I: Iterator,
@@ -15,30 +20,41 @@
     }
 }
 
-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);
+impl<I> DoubleEndedIterator for Unspecialized<I>
+where
+    I: DoubleEndedIterator,
+{
+    #[inline(always)]
+    fn next_back(&mut self) -> Option<Self::Item> {
+        self.0.next_back()
     }
 }
 
-fn test_specializations<IterItem, Iter>(
-    it: &Iter,
-) where
-    IterItem: Eq + Debug + Clone,
-    Iter: Iterator<Item = IterItem> + Clone,
+fn test_specializations<I>(it: &I)
+where
+    I::Item: Eq + Debug + Clone,
+    I: Iterator + Clone,
 {
+    macro_rules! check_specialized {
+        ($src:expr, |$it:pat| $closure:expr) => {
+            // Many iterators special-case the first elements, so we test specializations for iterators that have already been advanced.
+            let mut src = $src.clone();
+            for _ in 0..5 {
+                let $it = src.clone();
+                let v1 = $closure;
+                let $it = Unspecialized(src.clone());
+                let v2 = $closure;
+                assert_eq!(v1, v2);
+                src.next();
+            }
+        }
+    }
     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| {
+        let fold_result = i.fold(vec![], |mut acc, v: I::Item| {
             parameters_from_fold.push((acc.clone(), v.clone()));
             acc.push(v);
             acc
@@ -50,7 +66,7 @@
         let first = i.next();
         let all_result = i.all(|x| {
             parameters_from_all.push(x.clone());
-            Some(x)==first
+            Some(x) == first
         });
         (parameters_from_all, all_result)
     });
@@ -72,10 +88,260 @@
     }
 }
 
+fn test_double_ended_specializations<I>(it: &I)
+where
+    I::Item: Eq + Debug + Clone,
+    I: DoubleEndedIterator + Clone,
+{
+    macro_rules! check_specialized {
+        ($src:expr, |$it:pat| $closure:expr) => {
+            // Many iterators special-case the first elements, so we test specializations for iterators that have already been advanced.
+            let mut src = $src.clone();
+            for step in 0..8 {
+                let $it = src.clone();
+                let v1 = $closure;
+                let $it = Unspecialized(src.clone());
+                let v2 = $closure;
+                assert_eq!(v1, v2);
+                if step % 2 == 0 {
+                    src.next();
+                } else {
+                    src.next_back();
+                }
+            }
+        }
+    }
+    check_specialized!(it, |i| {
+        let mut parameters_from_rfold = vec![];
+        let rfold_result = i.rfold(vec![], |mut acc, v: I::Item| {
+            parameters_from_rfold.push((acc.clone(), v.clone()));
+            acc.push(v);
+            acc
+        });
+        (parameters_from_rfold, rfold_result)
+    });
+    let size = it.clone().count();
+    for n in 0..size + 2 {
+        check_specialized!(it, |mut i| i.nth_back(n));
+    }
+}
+
 quickcheck! {
+    fn interleave(v: Vec<u8>, w: Vec<u8>) -> () {
+        test_specializations(&v.iter().interleave(w.iter()));
+    }
+
+    fn interleave_shortest(v: Vec<u8>, w: Vec<u8>) -> () {
+        test_specializations(&v.iter().interleave_shortest(w.iter()));
+    }
+
+    fn batching(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().batching(Iterator::next));
+    }
+
+    fn tuple_windows(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().tuple_windows::<(_,)>());
+        test_specializations(&v.iter().tuple_windows::<(_, _)>());
+        test_specializations(&v.iter().tuple_windows::<(_, _, _)>());
+    }
+
+    fn circular_tuple_windows(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().circular_tuple_windows::<(_,)>());
+        test_specializations(&v.iter().circular_tuple_windows::<(_, _)>());
+        test_specializations(&v.iter().circular_tuple_windows::<(_, _, _)>());
+    }
+
+    fn tuples(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().tuples::<(_,)>());
+        test_specializations(&v.iter().tuples::<(_, _)>());
+        test_specializations(&v.iter().tuples::<(_, _, _)>());
+    }
+
+    fn cartesian_product(a: Vec<u8>, b: Vec<u8>) -> TestResult {
+        if a.len() * b.len() > 100 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().cartesian_product(&b));
+        TestResult::passed()
+    }
+
+    fn multi_cartesian_product(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult {
+        if a.len() * b.len() * c.len() > 100 {
+            return TestResult::discard();
+        }
+        test_specializations(&vec![a, b, c].into_iter().multi_cartesian_product());
+        TestResult::passed()
+    }
+
+    fn coalesce(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) }))
+    }
+
+    fn dedup(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup())
+    }
+
+    fn dedup_by(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup_by(PartialOrd::ge))
+    }
+
+    fn dedup_with_count(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup_with_count())
+    }
+
+    fn dedup_by_with_count(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup_by_with_count(PartialOrd::ge))
+    }
+
+    fn duplicates(v: Vec<u8>) -> () {
+        let it = v.iter().duplicates();
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn duplicates_by(v: Vec<u8>) -> () {
+        let it = v.iter().duplicates_by(|x| *x % 10);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn unique(v: Vec<u8>) -> () {
+        let it = v.iter().unique();
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn unique_by(v: Vec<u8>) -> () {
+        let it = v.iter().unique_by(|x| *x % 50);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn take_while_inclusive(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().copied().take_while_inclusive(|&x| x < 100));
+    }
+
+    fn while_some(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().map(|&x| if x < 100 { Some(2 * x) } else { None }).while_some());
+    }
+
+    fn pad_using(v: Vec<u8>) -> () {
+        use std::convert::TryFrom;
+        let it = v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX));
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn with_position(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().with_position());
+    }
+
+    fn positions(v: Vec<u8>) -> () {
+        let it = v.iter().positions(|x| x % 5 == 0);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn update(v: Vec<u8>) -> () {
+        let it = v.iter().copied().update(|x| *x = x.wrapping_mul(7));
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn tuple_combinations(v: Vec<u8>) -> TestResult {
+        if v.len() > 10 {
+            return TestResult::discard();
+        }
+        test_specializations(&v.iter().tuple_combinations::<(_,)>());
+        test_specializations(&v.iter().tuple_combinations::<(_, _)>());
+        test_specializations(&v.iter().tuple_combinations::<(_, _, _)>());
+        TestResult::passed()
+    }
+
     fn intersperse(v: Vec<u8>) -> () {
         test_specializations(&v.into_iter().intersperse(0));
     }
+
+    fn intersperse_with(v: Vec<u8>) -> () {
+        test_specializations(&v.into_iter().intersperse_with(|| 0));
+    }
+
+    fn combinations(a: Vec<u8>, n: u8) -> TestResult {
+        if n > 3 || a.len() > 8 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().combinations(n as usize));
+        TestResult::passed()
+    }
+
+    fn combinations_with_replacement(a: Vec<u8>, n: u8) -> TestResult {
+        if n > 3 || a.len() > 7 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().combinations_with_replacement(n as usize));
+        TestResult::passed()
+    }
+
+    fn permutations(a: Vec<u8>, n: u8) -> TestResult {
+        if n > 3 || a.len() > 8 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().permutations(n as usize));
+        TestResult::passed()
+    }
+
+    fn powerset(a: Vec<u8>) -> TestResult {
+        if a.len() > 6 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().powerset());
+        TestResult::passed()
+    }
+
+    fn zip_longest(a: Vec<u8>, b: Vec<u8>) -> () {
+        let it = a.into_iter().zip_longest(b);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn zip_eq(a: Vec<u8>) -> () {
+        test_specializations(&a.iter().zip_eq(a.iter().rev()))
+    }
+
+    fn multizip(a: Vec<u8>) -> () {
+        let it = itertools::multizip((a.iter(), a.iter().rev(), a.iter().take(50)));
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn izip(a: Vec<u8>, b: Vec<u8>) -> () {
+        test_specializations(&itertools::izip!(b.iter(), a, b.iter().rev()));
+    }
+
+    fn iproduct(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult {
+        if a.len() * b.len() * c.len() > 200 {
+            return TestResult::discard();
+        }
+        test_specializations(&itertools::iproduct!(a, b.iter(), c));
+        TestResult::passed()
+    }
+
+    fn repeat_n(element: i8, n: u8) -> () {
+        let it = itertools::repeat_n(element, n as usize);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn exactly_one_error(v: Vec<u8>) -> TestResult {
+        // Use `at_most_one` would be similar.
+        match v.iter().exactly_one() {
+            Ok(_) => TestResult::discard(),
+            Err(it) => {
+                test_specializations(&it);
+                TestResult::passed()
+            }
+        }
+    }
 }
 
 quickcheck! {
@@ -85,32 +351,124 @@
         pb.put_back(1);
         test_specializations(&pb);
     }
+
+    fn put_back_n(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::put_back_n(v);
+        for k in 0..n {
+            it.put_back(k);
+        }
+        test_specializations(&it);
+    }
+
+    fn multipeek(v: Vec<u8>, n: u8) -> () {
+        let mut it = v.into_iter().multipeek();
+        for _ in 0..n {
+            it.peek();
+        }
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        for _ in 0..n {
+            it.peek();
+        }
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek_nth(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        it.peek_nth(n as usize);
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek_mut(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        for _ in 0..n {
+            if let Some(x) = it.peek_mut() {
+                *x = x.wrapping_add(50);
+            }
+        }
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek_nth_mut(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        if let Some(x) = it.peek_nth_mut(n as usize) {
+            *x = x.wrapping_add(50);
+        }
+        test_specializations(&it);
+    }
 }
 
 quickcheck! {
-    fn merge_join_by_qc(i1: Vec<usize>, i2: Vec<usize>) -> () {
-        test_specializations(&i1.into_iter().merge_join_by(i2.into_iter(), std::cmp::Ord::cmp));
+    fn merge(a: Vec<u8>, b: Vec<u8>) -> () {
+        test_specializations(&a.into_iter().merge(b))
+    }
+
+    fn merge_by(a: Vec<u8>, b: Vec<u8>) -> () {
+        test_specializations(&a.into_iter().merge_by(b, PartialOrd::ge))
+    }
+
+    fn merge_join_by_ordering(i1: Vec<u8>, i2: Vec<u8>) -> () {
+        test_specializations(&i1.into_iter().merge_join_by(i2, Ord::cmp));
+    }
+
+    fn merge_join_by_bool(i1: Vec<u8>, i2: Vec<u8>) -> () {
+        test_specializations(&i1.into_iter().merge_join_by(i2, PartialOrd::ge));
+    }
+
+    fn kmerge(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () {
+        test_specializations(&vec![a, b, c]
+            .into_iter()
+            .map(|v| v.into_iter().sorted())
+            .kmerge());
+    }
+
+    fn kmerge_by(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () {
+        test_specializations(&vec![a, b, c]
+            .into_iter()
+            .map(|v| v.into_iter().sorted_by_key(|a| a.abs()))
+            .kmerge_by(|a, b| a.abs() < b.abs()));
     }
 }
 
 quickcheck! {
     fn map_into(v: Vec<u8>) -> () {
-        test_specializations(&v.into_iter().map_into::<u32>());
+        let it = v.into_iter().map_into::<u32>();
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
     }
-}
 
-quickcheck! {
     fn map_ok(v: Vec<Result<u8, char>>) -> () {
-        test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1)));
+        let it = v.into_iter().map_ok(|u| u.checked_add(1));
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn filter_ok(v: Vec<Result<u8, char>>) -> () {
+        test_specializations(&v.into_iter().filter_ok(|&i| i < 20));
+    }
+
+    fn filter_map_ok(v: Vec<Result<u8, char>>) -> () {
+        test_specializations(&v.into_iter().filter_map_ok(|i| if i < 20 { Some(i * 2) } else { None }));
+    }
+
+    // `SmallIter2<u8>` because `Vec<u8>` is too slow and we get bad coverage from a singleton like Option<u8>
+    fn flatten_ok(v: Vec<Result<SmallIter2<u8>, char>>) -> () {
+        let it = v.into_iter().flatten_ok();
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
     }
 }
 
 quickcheck! {
+    // TODO Replace this function by a normal call to test_specializations
     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) {
+        fn helper(it: impl DoubleEndedIterator<Item = Result<u8, u8>> + Clone) {
             macro_rules! check_results_specialized {
                 ($src:expr, |$it:pat| $closure:expr) => {
                     assert_eq!(
@@ -126,6 +484,7 @@
             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| i.rev().collect::<Vec<_>>());
             check_results_specialized!(it, |i| {
                 let mut parameters_from_fold = vec![];
                 let fold_result = i.fold(vec![], |mut acc, v| {
@@ -135,6 +494,15 @@
                 });
                 (parameters_from_fold, fold_result)
             });
+            check_results_specialized!(it, |i| {
+                let mut parameters_from_rfold = vec![];
+                let rfold_result = i.rfold(vec![], |mut acc, v| {
+                    parameters_from_rfold.push((acc.clone(), v));
+                    acc.push(v);
+                    acc
+                });
+                (parameters_from_rfold, rfold_result)
+            });
             check_results_specialized!(it, |mut i| {
                 let mut parameters_from_all = vec![];
                 let first = i.next();
@@ -148,6 +516,67 @@
             for n in 0..size + 2 {
                 check_results_specialized!(it, |mut i| i.nth(n));
             }
+            for n in 0..size + 2 {
+                check_results_specialized!(it, |mut i| i.nth_back(n));
+            }
+        }
+    }
+}
+
+/// Like `VecIntoIter<T>` with maximum 2 elements.
+#[derive(Debug, Clone, Default)]
+enum SmallIter2<T> {
+    #[default]
+    Zero,
+    One(T),
+    Two(T, T),
+}
+
+impl<T: Arbitrary> Arbitrary for SmallIter2<T> {
+    fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self {
+        match g.gen_range(0u8, 3) {
+            0 => Self::Zero,
+            1 => Self::One(T::arbitrary(g)),
+            2 => Self::Two(T::arbitrary(g), T::arbitrary(g)),
+            _ => unreachable!(),
+        }
+    }
+    // maybe implement shrink too, maybe not
+}
+
+impl<T> Iterator for SmallIter2<T> {
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match std::mem::take(self) {
+            Self::Zero => None,
+            Self::One(val) => Some(val),
+            Self::Two(val, second) => {
+                *self = Self::One(second);
+                Some(val)
+            }
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let len = match self {
+            Self::Zero => 0,
+            Self::One(_) => 1,
+            Self::Two(_, _) => 2,
+        };
+        (len, Some(len))
+    }
+}
+
+impl<T> DoubleEndedIterator for SmallIter2<T> {
+    fn next_back(&mut self) -> Option<Self::Item> {
+        match std::mem::take(self) {
+            Self::Zero => None,
+            Self::One(val) => Some(val),
+            Self::Two(first, val) => {
+                *self = Self::One(first);
+                Some(val)
+            }
         }
     }
 }
diff --git a/crates/itertools/tests/test_core.rs b/crates/itertools/tests/test_core.rs
index df94eb6..32af246 100644
--- a/crates/itertools/tests/test_core.rs
+++ b/crates/itertools/tests/test_core.rs
@@ -4,18 +4,71 @@
 //! option. This file may not be copied, modified, or distributed
 //! except according to those terms.
 #![no_std]
+#![allow(deprecated)]
 
-use core::iter;
-use itertools as it;
-use crate::it::Itertools;
+use crate::it::chain;
+use crate::it::free::put_back;
 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;
+use crate::it::multizip;
+use crate::it::Itertools;
+use core::iter;
+use itertools as it;
+
+#[allow(dead_code)]
+fn get_esi_then_esi<I: ExactSizeIterator + Clone>(it: I) {
+    fn is_esi(_: impl ExactSizeIterator) {}
+    is_esi(it.clone().get(1..4));
+    is_esi(it.clone().get(1..=4));
+    is_esi(it.clone().get(1..));
+    is_esi(it.clone().get(..4));
+    is_esi(it.clone().get(..=4));
+    is_esi(it.get(..));
+}
+
+#[allow(dead_code)]
+fn get_dei_esi_then_dei_esi<I: DoubleEndedIterator + ExactSizeIterator + Clone>(it: I) {
+    fn is_dei_esi(_: impl DoubleEndedIterator + ExactSizeIterator) {}
+    is_dei_esi(it.clone().get(1..4));
+    is_dei_esi(it.clone().get(1..=4));
+    is_dei_esi(it.clone().get(1..));
+    is_dei_esi(it.clone().get(..4));
+    is_dei_esi(it.clone().get(..=4));
+    is_dei_esi(it.get(..));
+}
+
+#[test]
+fn get_1_max() {
+    let mut it = (0..5).get(1..=usize::MAX);
+    assert_eq!(it.next(), Some(1));
+    assert_eq!(it.next_back(), Some(4));
+}
+
+#[test]
+#[should_panic]
+fn get_full_range_inclusive() {
+    let _it = (0..5).get(0..=usize::MAX);
+}
+
+#[test]
+fn product0() {
+    let mut prod = iproduct!();
+    assert_eq!(prod.next(), Some(()));
+    assert!(prod.next().is_none());
+}
+
+#[test]
+fn iproduct1() {
+    let s = "αβ";
+
+    let mut prod = iproduct!(s.chars());
+    assert_eq!(prod.next(), Some(('α',)));
+    assert_eq!(prod.next(), Some(('β',)));
+    assert!(prod.next().is_none());
+}
 
 #[test]
 fn product2() {
@@ -26,7 +79,7 @@
     assert!(prod.next() == Some(('α', 1)));
     assert!(prod.next() == Some(('β', 0)));
     assert!(prod.next() == Some(('β', 1)));
-    assert!(prod.next() == None);
+    assert!(prod.next().is_none());
 }
 
 #[test]
@@ -34,13 +87,12 @@
     for (_x, _y, _z) in iproduct!(
         [0, 1, 2].iter().cloned(),
         [0, 1, 2].iter().cloned(),
-        [0, 1, 2].iter().cloned())
-    {
+        [0, 1, 2].iter().cloned()
+    ) {
         // ok
     }
 }
 
-
 #[test]
 fn izip_macro() {
     let mut zip = izip!(2..3);
@@ -61,7 +113,7 @@
 #[test]
 fn izip2() {
     let _zip1: iter::Zip<_, _> = izip!(1.., 2..);
-    let _zip2: iter::Zip<_, _> = izip!(1.., 2.., );
+    let _zip2: iter::Zip<_, _> = izip!(1.., 2..,);
 }
 
 #[test]
@@ -109,7 +161,7 @@
 #[test]
 fn chain2() {
     let _ = chain!(1.., 2..);
-    let _ = chain!(1.., 2.., );
+    let _ = chain!(1.., 2..,);
 }
 
 #[test]
@@ -127,7 +179,7 @@
 
 #[test]
 fn test_interleave() {
-    let xs: [u8; 0]  = [];
+    let xs: [u8; 0] = [];
     let ys = [7u8, 9, 8, 10];
     let zs = [2u8, 77];
     let it = interleave(xs.iter(), ys.iter());
@@ -155,15 +207,6 @@
     it::assert_equal(it, ys.iter());
 }
 
-#[allow(deprecated)]
-#[test]
-fn foreach() {
-    let xs = [1i32, 2, 3];
-    let mut sum = 0;
-    xs.iter().foreach(|elt| sum += *elt);
-    assert!(sum == 6);
-}
-
 #[test]
 fn dropping() {
     let xs = [1, 2, 3];
@@ -197,21 +240,11 @@
     it::assert_equal(pb, xs.iter().cloned());
 }
 
-#[allow(deprecated)]
-#[test]
-fn step() {
-    it::assert_equal((0..10).step(1), 0..10);
-    it::assert_equal((0..10).step(2), (0..10).filter(|x: &i32| *x % 2 == 0));
-    it::assert_equal((0..10).step(10), 0..1);
-}
-
-#[allow(deprecated)]
 #[test]
 fn merge() {
-    it::assert_equal((0..10).step(2).merge((1..10).step(2)), 0..10);
+    it::assert_equal((0..10).step_by(2).merge((1..10).step_by(2)), 0..10);
 }
 
-
 #[test]
 fn repeatn() {
     let s = "α";
@@ -231,29 +264,33 @@
     use core::cell::Cell;
     #[derive(PartialEq, Debug)]
     struct Foo {
-        n: Cell<usize>
+        n: Cell<usize>,
     }
 
-    impl Clone for Foo
-    {
-        fn clone(&self) -> Self
-        {
+    impl Clone for Foo {
+        fn clone(&self) -> Self {
             let n = self.n.get();
             self.n.set(n + 1);
-            Foo { n: Cell::new(n + 1) }
+            Self {
+                n: Cell::new(n + 1),
+            }
         }
     }
 
-
     for n in 0..10 {
-        let f = Foo{n: Cell::new(0)};
+        let f = Foo { n: Cell::new(0) };
         let it = it::repeat_n(f, n);
         // drain it
         let last = it.last();
         if n == 0 {
             assert_eq!(last, None);
         } else {
-            assert_eq!(last, Some(Foo{n: Cell::new(n - 1)}));
+            assert_eq!(
+                last,
+                Some(Foo {
+                    n: Cell::new(n - 1)
+                })
+            );
         }
     }
 }
@@ -276,25 +313,45 @@
 }
 
 #[test]
-fn tree_fold1() {
+fn tree_reduce() {
     for i in 0..100 {
-        assert_eq!((0..i).tree_fold1(|x, y| x + y), (0..i).fold1(|x, y| x + y));
+        assert_eq!((0..i).tree_reduce(|x, y| x + y), (0..i).fold1(|x, y| x + y));
     }
 }
 
 #[test]
 fn exactly_one() {
     assert_eq!((0..10).filter(|&x| x == 2).exactly_one().unwrap(), 2);
-    assert!((0..10).filter(|&x| x > 1 && x < 4).exactly_one().unwrap_err().eq(2..4));
-    assert!((0..10).filter(|&x| x > 1 && x < 5).exactly_one().unwrap_err().eq(2..5));
-    assert!((0..10).filter(|&_| false).exactly_one().unwrap_err().eq(0..0));
+    assert!((0..10)
+        .filter(|&x| x > 1 && x < 4)
+        .exactly_one()
+        .unwrap_err()
+        .eq(2..4));
+    assert!((0..10)
+        .filter(|&x| x > 1 && x < 5)
+        .exactly_one()
+        .unwrap_err()
+        .eq(2..5));
+    assert!((0..10)
+        .filter(|&_| false)
+        .exactly_one()
+        .unwrap_err()
+        .eq(0..0));
 }
 
 #[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!((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);
 }
 
diff --git a/crates/itertools/tests/test_std.rs b/crates/itertools/tests/test_std.rs
index f590342..00246d5 100644
--- a/crates/itertools/tests/test_std.rs
+++ b/crates/itertools/tests/test_std.rs
@@ -1,19 +1,26 @@
-use quickcheck as qc;
-use rand::{distributions::{Distribution, Standard}, Rng, SeedableRng, rngs::StdRng};
-use rand::{seq::SliceRandom, thread_rng};
-use std::{cmp::min, fmt::Debug, marker::PhantomData};
-use itertools as it;
-use crate::it::Itertools;
-use crate::it::ExactlyOneError;
-use crate::it::multizip;
-use crate::it::multipeek;
-use crate::it::peek_nth;
-use crate::it::free::rciter;
-use crate::it::free::put_back_n;
-use crate::it::FoldWhile;
+#![allow(unstable_name_collisions)]
+
 use crate::it::cloned;
+use crate::it::free::put_back_n;
+use crate::it::free::rciter;
 use crate::it::iproduct;
 use crate::it::izip;
+use crate::it::multipeek;
+use crate::it::multizip;
+use crate::it::peek_nth;
+use crate::it::repeat_n;
+use crate::it::ExactlyOneError;
+use crate::it::FoldWhile;
+use crate::it::Itertools;
+use itertools as it;
+use quickcheck as qc;
+use rand::{
+    distributions::{Distribution, Standard},
+    rngs::StdRng,
+    Rng, SeedableRng,
+};
+use rand::{seq::SliceRandom, thread_rng};
+use std::{cmp::min, fmt::Debug, marker::PhantomData};
 
 #[test]
 fn product3() {
@@ -27,28 +34,26 @@
             }
         }
     }
-    for (_, _, _, _) in iproduct!(0..3, 0..2, 0..2, 0..3) {
-        /* test compiles */
-    }
+    for (_, _, _, _) in iproduct!(0..3, 0..2, 0..2, 0..3) { /* test compiles */ }
 }
 
 #[test]
 fn interleave_shortest() {
     let v0: Vec<i32> = vec![0, 2, 4];
     let v1: Vec<i32> = vec![1, 3, 5, 7];
-    let it = v0.into_iter().interleave_shortest(v1.into_iter());
+    let it = v0.into_iter().interleave_shortest(v1);
     assert_eq!(it.size_hint(), (6, Some(6)));
     assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5]);
 
     let v0: Vec<i32> = vec![0, 2, 4, 6, 8];
     let v1: Vec<i32> = vec![1, 3, 5];
-    let it = v0.into_iter().interleave_shortest(v1.into_iter());
+    let it = v0.into_iter().interleave_shortest(v1);
     assert_eq!(it.size_hint(), (7, Some(7)));
     assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5, 6]);
 
     let i0 = ::std::iter::repeat(0);
     let v1: Vec<_> = vec![1, 3, 5];
-    let it = i0.interleave_shortest(v1.into_iter());
+    let it = i0.interleave_shortest(v1);
     assert_eq!(it.size_hint(), (7, Some(7)));
 
     let v0: Vec<_> = vec![0, 2, 4];
@@ -62,9 +67,15 @@
     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());
+    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());
+    it::assert_equal(
+        ys_rev.iter(),
+        xs.iter().duplicates_by(|x| x[..2].to_string()).rev(),
+    );
 }
 
 #[test]
@@ -83,10 +94,13 @@
     let ys_rev = [1, 0];
     it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev());
 
-    let xs = vec![0, 1, 2, 1, 2];
+    let xs = [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());
+    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());
 }
@@ -96,9 +110,15 @@
     let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"];
     let ys = ["aaa", "bbbbb", "ccc"];
     it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string()));
-    it::assert_equal(ys.iter(), xs.iter().rev().unique_by(|x| x[..2].to_string()).rev());
+    it::assert_equal(
+        ys.iter(),
+        xs.iter().rev().unique_by(|x| x[..2].to_string()).rev(),
+    );
     let ys_rev = ["cccc", "aaaaa", "bbbb"];
-    it::assert_equal(ys_rev.iter(), xs.iter().unique_by(|x| x[..2].to_string()).rev());
+    it::assert_equal(
+        ys_rev.iter(),
+        xs.iter().unique_by(|x| x[..2].to_string()).rev(),
+    );
 }
 
 #[test]
@@ -127,7 +147,7 @@
 
     let ys = [0, 1, 2, 3];
     let mut it = ys[..0].iter().copied().intersperse(1);
-    assert!(it.next() == None);
+    assert!(it.next().is_none());
 }
 
 #[test]
@@ -148,14 +168,14 @@
 
 #[test]
 fn coalesce() {
-    let data = vec![-1., -2., -3., 3., 1., 0., -1.];
-    let it = data.iter().cloned().coalesce(|x, y|
+    let data = [-1., -2., -3., 3., 1., 0., -1.];
+    let it = data.iter().cloned().coalesce(|x, y| {
         if (x >= 0.) == (y >= 0.) {
             Ok(x + y)
         } else {
             Err((x, y))
         }
-    );
+    });
     itertools::assert_equal(it.clone(), vec![-6., 4., -1.]);
     assert_eq!(
         it.fold(vec![], |mut v, n| {
@@ -168,17 +188,37 @@
 
 #[test]
 fn dedup_by() {
-    let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)];
+    let xs = [
+        (0, 0),
+        (0, 1),
+        (1, 1),
+        (2, 1),
+        (0, 2),
+        (3, 1),
+        (0, 3),
+        (1, 3),
+    ];
     let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)];
-    it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.1==y.1));
+    it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.1 == y.1));
     let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)];
     let ys = [(0, 1)];
-    it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.0==y.0));
+    it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.0 == y.0));
 
-    let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)];
+    let xs = [
+        (0, 0),
+        (0, 1),
+        (1, 1),
+        (2, 1),
+        (0, 2),
+        (3, 1),
+        (0, 3),
+        (1, 3),
+    ];
     let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)];
     let mut xs_d = Vec::new();
-    xs.iter().dedup_by(|x, y| x.1==y.1).fold((), |(), &elt| xs_d.push(elt));
+    xs.iter()
+        .dedup_by(|x, y| x.1 == y.1)
+        .fold((), |(), &elt| xs_d.push(elt));
     assert_eq!(&xs_d, &ys);
 }
 
@@ -195,18 +235,38 @@
     it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count());
 }
 
-
 #[test]
 fn dedup_by_with_count() {
-    let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)];
-    let ys = [(1, &(0, 0)), (3, &(0, 1)), (1, &(0, 2)), (1, &(3, 1)), (2, &(0, 3))];
+    let xs = [
+        (0, 0),
+        (0, 1),
+        (1, 1),
+        (2, 1),
+        (0, 2),
+        (3, 1),
+        (0, 3),
+        (1, 3),
+    ];
+    let ys = [
+        (1, &(0, 0)),
+        (3, &(0, 1)),
+        (1, &(0, 2)),
+        (1, &(3, 1)),
+        (2, &(0, 3)),
+    ];
 
-    it::assert_equal(ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.1==y.1));
+    it::assert_equal(
+        ys.iter().cloned(),
+        xs.iter().dedup_by_with_count(|x, y| x.1 == y.1),
+    );
 
     let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)];
-    let ys = [( 5, &(0, 1))];
+    let ys = [(5, &(0, 1))];
 
-    it::assert_equal(ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.0==y.0));
+    it::assert_equal(
+        ys.iter().cloned(),
+        xs.iter().dedup_by_with_count(|x, y| x.0 == y.0),
+    );
 }
 
 #[test]
@@ -215,12 +275,28 @@
     assert!("A".chars().all_equal());
     assert!(!"AABBCCC".chars().all_equal());
     assert!("AAAAAAA".chars().all_equal());
-    for (_key, mut sub) in &"AABBCCC".chars().group_by(|&x| x) {
+    for (_key, mut sub) in &"AABBCCC".chars().chunk_by(|&x| x) {
         assert!(sub.all_equal());
     }
 }
 
 #[test]
+fn all_equal_value() {
+    assert_eq!("".chars().all_equal_value(), Err(None));
+    assert_eq!("A".chars().all_equal_value(), Ok('A'));
+    assert_eq!("AABBCCC".chars().all_equal_value(), Err(Some(('A', 'B'))));
+    assert_eq!("AAAAAAA".chars().all_equal_value(), Ok('A'));
+    {
+        let mut it = [1, 2, 3].iter().copied();
+        let result = it.all_equal_value();
+        assert_eq!(result, Err(Some((1, 2))));
+        let remaining = it.next();
+        assert_eq!(remaining, Some(3));
+        assert!(it.next().is_none());
+    }
+}
+
+#[test]
 fn all_unique() {
     assert!("ABCDEFGH".chars().all_unique());
     assert!(!"ABCDEFGA".chars().all_unique());
@@ -240,7 +316,7 @@
 
 #[test]
 fn tee() {
-    let xs  = [0, 1, 2, 3];
+    let xs = [0, 1, 2, 3];
     let (mut t1, mut t2) = xs.iter().cloned().tee();
     assert_eq!(t1.next(), Some(0));
     assert_eq!(t2.next(), Some(0));
@@ -264,7 +340,6 @@
     it::assert_equal(t1.zip(t2), xs.iter().cloned().zip(xs.iter().cloned()));
 }
 
-
 #[test]
 fn test_rciter() {
     let xs = [0, 1, 1, 1, 2, 1, 3, 5, 6];
@@ -285,26 +360,24 @@
     assert_eq!(z.next(), Some((0, 1)));
 }
 
-#[allow(deprecated)]
 #[test]
 fn trait_pointers() {
-    struct ByRef<'r, I: ?Sized>(&'r mut I) ;
+    struct ByRef<'r, I: ?Sized>(&'r mut I);
 
-    impl<'r, X, I: ?Sized> Iterator for ByRef<'r, I> where
-        I: 'r + Iterator<Item=X>
+    impl<'r, X, I> Iterator for ByRef<'r, I>
+    where
+        I: ?Sized + 'r + Iterator<Item = X>,
     {
         type Item = X;
-        fn next(&mut self) -> Option<Self::Item>
-        {
+        fn next(&mut self) -> Option<Self::Item> {
             self.0.next()
         }
     }
 
-    let mut it = Box::new(0..10) as Box<dyn Iterator<Item=i32>>;
+    let mut it = Box::new(0..10) as Box<dyn Iterator<Item = i32>>;
     assert_eq!(it.next(), Some(0));
 
     {
-        /* make sure foreach works on non-Sized */
         let jt: &mut dyn Iterator<Item = i32> = &mut *it;
         assert_eq!(jt.next(), Some(1));
 
@@ -314,15 +387,22 @@
         }
 
         assert_eq!(jt.find_position(|x| *x == 4), Some((1, 4)));
-        jt.foreach(|_| ());
+        jt.for_each(|_| ());
     }
 }
 
 #[test]
 fn merge_by() {
-    let odd : Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")];
-    let even = vec![(2, "foo"), (4, "bar"), (6, "baz")];
-    let expected = vec![(1, "hello"), (2, "foo"), (3, "world"), (4, "bar"), (5, "!"), (6, "baz")];
+    let odd: Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")];
+    let even = [(2, "foo"), (4, "bar"), (6, "baz")];
+    let expected = [
+        (1, "hello"),
+        (2, "foo"),
+        (3, "world"),
+        (4, "bar"),
+        (5, "!"),
+        (6, "baz"),
+    ];
     let results = odd.iter().merge_by(even.iter(), |a, b| a.0 <= b.0);
     it::assert_equal(results, expected.iter());
 }
@@ -336,23 +416,21 @@
     let mut bt2 = BTreeMap::new();
     bt2.insert("foo", 2);
     bt2.insert("bar", 4);
-    let results = bt1.into_iter().merge_by(bt2.into_iter(), |a, b| a.0 <= b.0 );
+    let results = bt1.into_iter().merge_by(bt2, |a, b| a.0 <= b.0);
     let expected = vec![("bar", 4), ("foo", 2), ("hello", 1), ("world", 3)];
-    it::assert_equal(results, expected.into_iter());
+    it::assert_equal(results, expected);
 }
 
-#[allow(deprecated)]
 #[test]
 fn kmerge() {
-    let its = (0..4).map(|s| (s..10).step(4));
+    let its = (0..4).map(|s| (s..10).step_by(4));
 
     it::assert_equal(its.kmerge(), 0..10);
 }
 
-#[allow(deprecated)]
 #[test]
 fn kmerge_2() {
-    let its = vec![3, 2, 1, 0].into_iter().map(|s| (s..10).step(4));
+    let its = vec![3, 2, 1, 0].into_iter().map(|s| (s..10).step_by(4));
 
     it::assert_equal(its.kmerge(), 0..10);
 }
@@ -378,19 +456,17 @@
 #[test]
 fn join() {
     let many = [1, 2, 3];
-    let one  = [1];
+    let one = [1];
     let none: Vec<i32> = vec![];
 
     assert_eq!(many.iter().join(", "), "1, 2, 3");
-    assert_eq!( one.iter().join(", "), "1");
+    assert_eq!(one.iter().join(", "), "1");
     assert_eq!(none.iter().join(", "), "");
 }
 
 #[test]
 fn sorted_unstable_by() {
-    let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| {
-        a.cmp(&b)
-    });
+    let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| a.cmp(&b));
     it::assert_equal(sc, vec![1, 2, 3, 4]);
 
     let v = (0..5).sorted_unstable_by(|&a, &b| a.cmp(&b).reverse());
@@ -408,9 +484,7 @@
 
 #[test]
 fn sorted_by() {
-    let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| {
-        a.cmp(&b)
-    });
+    let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| a.cmp(&b));
     it::assert_equal(sc, vec![1, 2, 3, 4]);
 
     let v = (0..5).sorted_by(|&a, &b| a.cmp(&b).reverse());
@@ -418,23 +492,40 @@
 }
 
 qc::quickcheck! {
-    fn k_smallest_range(n: u64, m: u16, k: u16) -> () {
+    fn k_smallest_range(n: i64, m: u16, k: u16) -> () {
         // u16 is used to constrain k and m to 0..2¹⁶,
         //  otherwise the test could use too much memory.
-        let (k, m) = (k as u64, m as u64);
+        let (k, m) = (k as usize, m as u64);
 
+        let mut v: Vec<_> = (n..n.saturating_add(m as _)).collect();
         // Generate a random permutation of n..n+m
-        let i = {
-            let mut v: Vec<u64> = (n..n.saturating_add(m)).collect();
-            v.shuffle(&mut thread_rng());
-            v.into_iter()
-        };
+        v.shuffle(&mut thread_rng());
 
-        // Check that taking the k smallest elements yields n..n+min(k, m)
-        it::assert_equal(
-            i.k_smallest(k as usize),
-            n..n.saturating_add(min(k, m))
-        );
+        // Construct the right answers for the top and bottom elements
+        let mut sorted = v.clone();
+        sorted.sort();
+        // how many elements are we checking
+        let num_elements = min(k, m as _);
+
+        // Compute the top and bottom k in various combinations
+        let sorted_smallest = sorted[..num_elements].iter().cloned();
+        let smallest = v.iter().cloned().k_smallest(k);
+        let smallest_by = v.iter().cloned().k_smallest_by(k, Ord::cmp);
+        let smallest_by_key = v.iter().cloned().k_smallest_by_key(k, |&x| x);
+
+        let sorted_largest = sorted[sorted.len() - num_elements..].iter().rev().cloned();
+        let largest = v.iter().cloned().k_largest(k);
+        let largest_by = v.iter().cloned().k_largest_by(k, Ord::cmp);
+        let largest_by_key = v.iter().cloned().k_largest_by_key(k, |&x| x);
+
+        // Check the variations produce the same answers and that they're right
+        it::assert_equal(smallest, sorted_smallest.clone());
+        it::assert_equal(smallest_by, sorted_smallest.clone());
+        it::assert_equal(smallest_by_key, sorted_smallest);
+
+        it::assert_equal(largest, sorted_largest.clone());
+        it::assert_equal(largest_by, sorted_largest.clone());
+        it::assert_equal(largest_by_key, sorted_largest);
     }
 }
 
@@ -443,11 +534,13 @@
     idx: usize,
     len: usize,
     rng: R,
-    _t: PhantomData<T>
+    _t: PhantomData<T>,
 }
 
 impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> Iterator for RandIter<T, R>
-where Standard: Distribution<T> {
+where
+    Standard: Distribution<T>,
+{
     type Item = T;
     fn next(&mut self) -> Option<T> {
         if self.idx == self.len {
@@ -461,11 +554,11 @@
 
 impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> qc::Arbitrary for RandIter<T, R> {
     fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
-        RandIter {
+        Self {
             idx: 0,
             len: g.size(),
             rng: R::seed_from_u64(g.next_u64()),
-            _t : PhantomData{},
+            _t: PhantomData {},
         }
     }
 }
@@ -479,10 +572,18 @@
 {
     let j = i.clone();
     let k = k as usize;
-    it::assert_equal(
-        i.k_smallest(k),
-        j.sorted().take(k)
-    )
+    it::assert_equal(i.k_smallest(k), j.sorted().take(k))
+}
+
+// Similar to `k_smallest_sort` but for our custom heap implementation.
+fn k_smallest_by_sort<I>(i: I, k: u16)
+where
+    I: Iterator + Clone,
+    I::Item: Ord + Debug,
+{
+    let j = i.clone();
+    let k = k as usize;
+    it::assert_equal(i.k_smallest_by(k, Ord::cmp), j.sorted().take(k))
 }
 
 macro_rules! generic_test {
@@ -498,6 +599,7 @@
 }
 
 generic_test!(k_smallest_sort, u8, u16, u32, u64, i8, i16, i32, i64);
+generic_test!(k_smallest_by_sort, u8, u16, u32, u64, i8, i16, i32, i64);
 
 #[test]
 fn sorted_by_key() {
@@ -534,7 +636,7 @@
 
 #[test]
 fn test_multipeek() {
-    let nums = vec![1u8,2,3,4,5];
+    let nums = vec![1u8, 2, 3, 4, 5];
 
     let mp = multipeek(nums.iter().copied());
     assert_eq!(nums, mp.collect::<Vec<_>>());
@@ -575,7 +677,7 @@
 #[test]
 fn test_multipeek_peeking_next() {
     use crate::it::PeekingNext;
-    let nums = vec![1u8,2,3,4,5,6,7];
+    let nums = [1u8, 2, 3, 4, 5, 6, 7];
 
     let mut mp = multipeek(nums.iter().copied());
     assert_eq!(mp.peeking_next(|&x| x != 0), Some(1));
@@ -599,8 +701,23 @@
 }
 
 #[test]
+fn test_repeat_n_peeking_next() {
+    use crate::it::PeekingNext;
+    let mut rn = repeat_n(0, 5);
+    assert_eq!(rn.peeking_next(|&x| x != 0), None);
+    assert_eq!(rn.peeking_next(|&x| x <= 0), Some(0));
+    assert_eq!(rn.next(), Some(0));
+    assert_eq!(rn.peeking_next(|&x| x <= 0), Some(0));
+    assert_eq!(rn.peeking_next(|&x| x != 0), None);
+    assert_eq!(rn.peeking_next(|&x| x >= 0), Some(0));
+    assert_eq!(rn.next(), Some(0));
+    assert_eq!(rn.peeking_next(|&x| x <= 0), None);
+    assert_eq!(rn.next(), None);
+}
+
+#[test]
 fn test_peek_nth() {
-    let nums = vec![1u8,2,3,4,5];
+    let nums = vec![1u8, 2, 3, 4, 5];
 
     let iter = peek_nth(nums.iter().copied());
     assert_eq!(nums, iter.collect::<Vec<_>>());
@@ -635,7 +752,7 @@
 #[test]
 fn test_peek_nth_peeking_next() {
     use it::PeekingNext;
-    let nums = vec![1u8,2,3,4,5,6,7];
+    let nums = [1u8, 2, 3, 4, 5, 6, 7];
     let mut iter = peek_nth(nums.iter().copied());
 
     assert_eq!(iter.peeking_next(|&x| x != 0), Some(1));
@@ -663,6 +780,35 @@
 }
 
 #[test]
+fn test_peek_nth_next_if() {
+    let nums = [1u8, 2, 3, 4, 5, 6, 7];
+    let mut iter = peek_nth(nums.iter().copied());
+
+    assert_eq!(iter.next_if(|&x| x != 0), Some(1));
+    assert_eq!(iter.next(), Some(2));
+
+    assert_eq!(iter.peek_nth(0), Some(&3));
+    assert_eq!(iter.peek_nth(1), Some(&4));
+    assert_eq!(iter.next_if_eq(&3), Some(3));
+    assert_eq!(iter.peek(), Some(&4));
+
+    assert_eq!(iter.next_if(|&x| x != 4), None);
+    assert_eq!(iter.next_if_eq(&4), Some(4));
+    assert_eq!(iter.peek_nth(0), Some(&5));
+    assert_eq!(iter.peek_nth(1), Some(&6));
+
+    assert_eq!(iter.next_if(|&x| x != 5), None);
+    assert_eq!(iter.peek(), Some(&5));
+
+    assert_eq!(iter.next_if(|&x| x % 2 == 1), Some(5));
+    assert_eq!(iter.next_if_eq(&6), Some(6));
+    assert_eq!(iter.peek_nth(0), Some(&7));
+    assert_eq!(iter.peek_nth(1), None);
+    assert_eq!(iter.next(), Some(7));
+    assert_eq!(iter.peek(), None);
+}
+
+#[test]
 fn pad_using() {
     it::assert_equal((0..0).pad_using(1, |_| 1), 1..2);
 
@@ -676,14 +822,14 @@
 }
 
 #[test]
-fn group_by() {
-    for (ch1, sub) in &"AABBCCC".chars().group_by(|&x| x) {
+fn chunk_by() {
+    for (ch1, sub) in &"AABBCCC".chars().chunk_by(|&x| x) {
         for ch2 in sub {
             assert_eq!(ch1, ch2);
         }
     }
 
-    for (ch1, sub) in &"AAABBBCCCCDDDD".chars().group_by(|&x| x) {
+    for (ch1, sub) in &"AAABBBCCCCDDDD".chars().chunk_by(|&x| x) {
         for ch2 in sub {
             assert_eq!(ch1, ch2);
             if ch1 == 'C' {
@@ -696,24 +842,24 @@
 
     // try all possible orderings
     for indices in permutohedron::Heap::new(&mut [0, 1, 2, 3]) {
-        let groups = "AaaBbbccCcDDDD".chars().group_by(&toupper);
-        let mut subs = groups.into_iter().collect_vec();
+        let chunks = "AaaBbbccCcDDDD".chars().chunk_by(&toupper);
+        let mut subs = chunks.into_iter().collect_vec();
 
         for &idx in &indices[..] {
             let (key, text) = match idx {
-                 0 => ('A', "Aaa".chars()),
-                 1 => ('B', "Bbb".chars()),
-                 2 => ('C', "ccCc".chars()),
-                 3 => ('D', "DDDD".chars()),
-                 _ => unreachable!(),
+                0 => ('A', "Aaa".chars()),
+                1 => ('B', "Bbb".chars()),
+                2 => ('C', "ccCc".chars()),
+                3 => ('D', "DDDD".chars()),
+                _ => unreachable!(),
             };
             assert_eq!(key, subs[idx].0);
             it::assert_equal(&mut subs[idx].1, text);
         }
     }
 
-    let groups = "AAABBBCCCCDDDD".chars().group_by(|&x| x);
-    let mut subs = groups.into_iter().map(|(_, g)| g).collect_vec();
+    let chunks = "AAABBBCCCCDDDD".chars().chunk_by(|&x| x);
+    let mut subs = chunks.into_iter().map(|(_, g)| g).collect_vec();
 
     let sd = subs.pop().unwrap();
     let sc = subs.pop().unwrap();
@@ -730,9 +876,11 @@
     {
         let mut ntimes = 0;
         let text = "AABCCC";
-        for (_, sub) in &text.chars().group_by(|&x| { ntimes += 1; x}) {
-            for _ in sub {
-            }
+        for (_, sub) in &text.chars().chunk_by(|&x| {
+            ntimes += 1;
+            x
+        }) {
+            for _ in sub {}
         }
         assert_eq!(ntimes, text.len());
     }
@@ -740,91 +888,95 @@
     {
         let mut ntimes = 0;
         let text = "AABCCC";
-        for _ in &text.chars().group_by(|&x| { ntimes += 1; x}) {
-        }
+        for _ in &text.chars().chunk_by(|&x| {
+            ntimes += 1;
+            x
+        }) {}
         assert_eq!(ntimes, text.len());
     }
 
     {
         let text = "ABCCCDEEFGHIJJKK";
-        let gr = text.chars().group_by(|&x| x);
+        let gr = text.chars().chunk_by(|&x| x);
         it::assert_equal(gr.into_iter().flat_map(|(_, sub)| sub), text.chars());
     }
 }
 
 #[test]
-fn group_by_lazy_2() {
-    let data = vec![0, 1];
-    let groups = data.iter().group_by(|k| *k);
-    let gs = groups.into_iter().collect_vec();
+fn chunk_by_lazy_2() {
+    let data = [0, 1];
+    let chunks = data.iter().chunk_by(|k| *k);
+    let gs = chunks.into_iter().collect_vec();
     it::assert_equal(data.iter(), gs.into_iter().flat_map(|(_k, g)| g));
 
-    let data = vec![0, 1, 1, 0, 0];
-    let groups = data.iter().group_by(|k| *k);
-    let mut gs = groups.into_iter().collect_vec();
+    let data = [0, 1, 1, 0, 0];
+    let chunks = data.iter().chunk_by(|k| *k);
+    let mut gs = chunks.into_iter().collect_vec();
     gs[1..].reverse();
     it::assert_equal(&[0, 0, 0, 1, 1], gs.into_iter().flat_map(|(_, g)| g));
 
-    let grouper = data.iter().group_by(|k| *k);
-    let mut groups = Vec::new();
-    for (k, group) in &grouper {
+    let grouper = data.iter().chunk_by(|k| *k);
+    let mut chunks = Vec::new();
+    for (k, chunk) in &grouper {
         if *k == 1 {
-            groups.push(group);
+            chunks.push(chunk);
         }
     }
-    it::assert_equal(&mut groups[0], &[1, 1]);
+    it::assert_equal(&mut chunks[0], &[1, 1]);
 
-    let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
-    let grouper = data.iter().group_by(|k| *k);
-    let mut groups = Vec::new();
-    for (i, (_, group)) in grouper.into_iter().enumerate() {
+    let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
+    let grouper = data.iter().chunk_by(|k| *k);
+    let mut chunks = Vec::new();
+    for (i, (_, chunk)) in grouper.into_iter().enumerate() {
         if i < 2 {
-            groups.push(group);
+            chunks.push(chunk);
         } else if i < 4 {
-            for _ in group {
-            }
+            for _ in chunk {}
         } else {
-            groups.push(group);
+            chunks.push(chunk);
         }
     }
-    it::assert_equal(&mut groups[0], &[0, 0, 0]);
-    it::assert_equal(&mut groups[1], &[1, 1]);
-    it::assert_equal(&mut groups[2], &[3, 3]);
+    it::assert_equal(&mut chunks[0], &[0, 0, 0]);
+    it::assert_equal(&mut chunks[1], &[1, 1]);
+    it::assert_equal(&mut chunks[2], &[3, 3]);
 
-    // use groups as chunks
-    let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
+    let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
     let mut i = 0;
-    let grouper = data.iter().group_by(move |_| { let k = i / 3; i += 1; k });
-    for (i, group) in &grouper {
+    let grouper = data.iter().chunk_by(move |_| {
+        let k = i / 3;
+        i += 1;
+        k
+    });
+    for (i, chunk) in &grouper {
         match i {
-            0 => it::assert_equal(group, &[0, 0, 0]),
-            1 => it::assert_equal(group, &[1, 1, 0]),
-            2 => it::assert_equal(group, &[0, 2, 2]),
-            3 => it::assert_equal(group, &[3, 3]),
+            0 => it::assert_equal(chunk, &[0, 0, 0]),
+            1 => it::assert_equal(chunk, &[1, 1, 0]),
+            2 => it::assert_equal(chunk, &[0, 2, 2]),
+            3 => it::assert_equal(chunk, &[3, 3]),
             _ => unreachable!(),
         }
     }
 }
 
 #[test]
-fn group_by_lazy_3() {
-    // test consuming each group on the lap after it was produced
-    let data = vec![0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2];
-    let grouper = data.iter().group_by(|elt| *elt);
+fn chunk_by_lazy_3() {
+    // test consuming each chunk on the lap after it was produced
+    let data = [0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2];
+    let grouper = data.iter().chunk_by(|elt| *elt);
     let mut last = None;
-    for (key, group) in &grouper {
+    for (key, chunk) in &grouper {
         if let Some(gr) = last.take() {
             for elt in gr {
                 assert!(elt != key && i32::abs(elt - key) == 1);
             }
         }
-        last = Some(group);
+        last = Some(chunk);
     }
 }
 
 #[test]
 fn chunks() {
-    let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
+    let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
     let grouper = data.iter().chunks(3);
     for (i, chunk) in grouper.into_iter().enumerate() {
         match i {
@@ -845,8 +997,8 @@
 
 #[test]
 fn concat_non_empty() {
-    let data = vec![vec![1,2,3], vec![4,5,6], vec![7,8,9]];
-    assert_eq!(data.into_iter().concat(), vec![1,2,3,4,5,6,7,8,9])
+    let data = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
+    assert_eq!(data.into_iter().concat(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9])
 }
 
 #[test]
@@ -854,19 +1006,20 @@
     assert!((1..3).combinations(5).next().is_none());
 
     let it = (1..3).combinations(2);
-    it::assert_equal(it, vec![
-        vec![1, 2],
-        ]);
+    it::assert_equal(it, vec![vec![1, 2]]);
 
     let it = (1..5).combinations(2);
-    it::assert_equal(it, vec![
-        vec![1, 2],
-        vec![1, 3],
-        vec![1, 4],
-        vec![2, 3],
-        vec![2, 4],
-        vec![3, 4],
-        ]);
+    it::assert_equal(
+        it,
+        vec![
+            vec![1, 2],
+            vec![1, 3],
+            vec![1, 4],
+            vec![2, 3],
+            vec![2, 4],
+            vec![3, 4],
+        ],
+    );
 
     it::assert_equal((0..0).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
     it::assert_equal((0..1).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
@@ -886,13 +1039,88 @@
     }
 }
 
-
 #[test]
 fn combinations_zero() {
     it::assert_equal((1..3).combinations(0), vec![vec![]]);
     it::assert_equal((0..0).combinations(0), vec![vec![]]);
 }
 
+fn binomial(n: usize, k: usize) -> usize {
+    if k > n {
+        0
+    } else {
+        (n - k + 1..=n).product::<usize>() / (1..=k).product::<usize>()
+    }
+}
+
+#[test]
+fn combinations_range_count() {
+    for n in 0..=10 {
+        for k in 0..=10 {
+            let len = binomial(n, k);
+            let mut it = (0..n).combinations(k);
+            assert_eq!(len, it.clone().count());
+            assert_eq!(len, it.size_hint().0);
+            assert_eq!(Some(len), it.size_hint().1);
+            for count in (0..len).rev() {
+                let elem = it.next();
+                assert!(elem.is_some());
+                assert_eq!(count, it.clone().count());
+                assert_eq!(count, it.size_hint().0);
+                assert_eq!(Some(count), it.size_hint().1);
+            }
+            let should_be_none = it.next();
+            assert!(should_be_none.is_none());
+        }
+    }
+}
+
+#[test]
+fn combinations_inexact_size_hints() {
+    for k in 0..=10 {
+        let mut numbers = (0..18).filter(|i| i % 2 == 0); // 9 elements
+        let mut it = numbers.clone().combinations(k);
+        let real_n = numbers.clone().count();
+        let len = binomial(real_n, k);
+        assert_eq!(len, it.clone().count());
+
+        let mut nb_loaded = 0;
+        let sh = numbers.size_hint();
+        assert_eq!(binomial(sh.0 + nb_loaded, k), it.size_hint().0);
+        assert_eq!(sh.1.map(|n| binomial(n + nb_loaded, k)), it.size_hint().1);
+
+        for next_count in 1..=len {
+            let elem = it.next();
+            assert!(elem.is_some());
+            assert_eq!(len - next_count, it.clone().count());
+            if next_count == 1 {
+                // The very first time, the lazy buffer is prefilled.
+                nb_loaded = numbers.by_ref().take(k).count();
+            } else {
+                // Then it loads one item each time until exhausted.
+                let nb = numbers.next();
+                if nb.is_some() {
+                    nb_loaded += 1;
+                }
+            }
+            let sh = numbers.size_hint();
+            if next_count > real_n - k + 1 {
+                assert_eq!(0, sh.0);
+                assert_eq!(Some(0), sh.1);
+                assert_eq!(real_n, nb_loaded);
+                // Once it's fully loaded, size hints of `it` are exacts.
+            }
+            assert_eq!(binomial(sh.0 + nb_loaded, k) - next_count, it.size_hint().0);
+            assert_eq!(
+                sh.1.map(|n| binomial(n + nb_loaded, k) - next_count),
+                it.size_hint().1
+            );
+        }
+        let should_be_none = it.next();
+        assert!(should_be_none.is_none());
+    }
+}
+
 #[test]
 fn permutations_zero() {
     it::assert_equal((1..3).permutations(0), vec![vec![]]);
@@ -900,6 +1128,40 @@
 }
 
 #[test]
+fn permutations_range_count() {
+    for n in 0..=7 {
+        for k in 0..=7 {
+            let len = if k <= n { (n - k + 1..=n).product() } else { 0 };
+            let mut it = (0..n).permutations(k);
+            assert_eq!(len, it.clone().count());
+            assert_eq!(len, it.size_hint().0);
+            assert_eq!(Some(len), it.size_hint().1);
+            for count in (0..len).rev() {
+                let elem = it.next();
+                assert!(elem.is_some());
+                assert_eq!(count, it.clone().count());
+                assert_eq!(count, it.size_hint().0);
+                assert_eq!(Some(count), it.size_hint().1);
+            }
+            let should_be_none = it.next();
+            assert!(should_be_none.is_none());
+        }
+    }
+}
+
+#[test]
+fn permutations_overflowed_size_hints() {
+    let mut it = std::iter::repeat(()).permutations(2);
+    assert_eq!(it.size_hint().0, usize::MAX);
+    assert_eq!(it.size_hint().1, None);
+    for nb_generated in 1..=1000 {
+        it.next();
+        assert!(it.size_hint().0 >= usize::MAX - nb_generated);
+        assert_eq!(it.size_hint().1, None);
+    }
+}
+
+#[test]
 fn combinations_with_replacement() {
     // Pool smaller than n
     it::assert_equal((0..1).combinations_with_replacement(2), vec![vec![0, 0]]);
@@ -916,15 +1178,9 @@
         ],
     );
     // Zero size
-    it::assert_equal(
-        (0..3).combinations_with_replacement(0),
-        vec![vec![]],
-    );
+    it::assert_equal((0..3).combinations_with_replacement(0), vec![vec![]]);
     // Zero size on empty pool
-    it::assert_equal(
-        (0..0).combinations_with_replacement(0),
-        vec![vec![]],
-    );
+    it::assert_equal((0..0).combinations_with_replacement(0), vec![vec![]]);
     // Empty pool
     it::assert_equal(
         (0..0).combinations_with_replacement(2),
@@ -933,25 +1189,74 @@
 }
 
 #[test]
+fn combinations_with_replacement_range_count() {
+    for n in 0..=7 {
+        for k in 0..=7 {
+            let len = binomial(usize::saturating_sub(n + k, 1), k);
+            let mut it = (0..n).combinations_with_replacement(k);
+            assert_eq!(len, it.clone().count());
+            assert_eq!(len, it.size_hint().0);
+            assert_eq!(Some(len), it.size_hint().1);
+            for count in (0..len).rev() {
+                let elem = it.next();
+                assert!(elem.is_some());
+                assert_eq!(count, it.clone().count());
+                assert_eq!(count, it.size_hint().0);
+                assert_eq!(Some(count), it.size_hint().1);
+            }
+            let should_be_none = it.next();
+            assert!(should_be_none.is_none());
+        }
+    }
+}
+
+#[test]
 fn powerset() {
     it::assert_equal((0..0).powerset(), vec![vec![]]);
     it::assert_equal((0..1).powerset(), vec![vec![], vec![0]]);
-    it::assert_equal((0..2).powerset(), vec![vec![], vec![0], vec![1], vec![0, 1]]);
-    it::assert_equal((0..3).powerset(), vec![
-        vec![],
-        vec![0], vec![1], vec![2],
-        vec![0, 1], vec![0, 2], vec![1, 2],
-        vec![0, 1, 2]
-    ]);
+    it::assert_equal(
+        (0..2).powerset(),
+        vec![vec![], vec![0], vec![1], vec![0, 1]],
+    );
+    it::assert_equal(
+        (0..3).powerset(),
+        vec![
+            vec![],
+            vec![0],
+            vec![1],
+            vec![2],
+            vec![0, 1],
+            vec![0, 2],
+            vec![1, 2],
+            vec![0, 1, 2],
+        ],
+    );
 
     assert_eq!((0..4).powerset().count(), 1 << 4);
     assert_eq!((0..8).powerset().count(), 1 << 8);
     assert_eq!((0..16).powerset().count(), 1 << 16);
+
+    for n in 0..=10 {
+        let mut it = (0..n).powerset();
+        let len = 2_usize.pow(n);
+        assert_eq!(len, it.clone().count());
+        assert_eq!(len, it.size_hint().0);
+        assert_eq!(Some(len), it.size_hint().1);
+        for count in (0..len).rev() {
+            let elem = it.next();
+            assert!(elem.is_some());
+            assert_eq!(count, it.clone().count());
+            assert_eq!(count, it.size_hint().0);
+            assert_eq!(Some(count), it.size_hint().1);
+        }
+        let should_be_none = it.next();
+        assert!(should_be_none.is_none());
+    }
 }
 
 #[test]
 fn diff_mismatch() {
-    let a = vec![1, 2, 3, 4];
+    let a = [1, 2, 3, 4];
     let b = vec![1.0, 5.0, 3.0, 4.0];
     let b_map = b.into_iter().map(|f| f as i32);
     let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
@@ -965,21 +1270,20 @@
 
 #[test]
 fn diff_longer() {
-    let a = vec![1, 2, 3, 4];
+    let a = [1, 2, 3, 4];
     let b = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
     let b_map = b.into_iter().map(|f| f as i32);
     let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
 
     assert!(match diff {
-        Some(it::Diff::Longer(_, remaining)) =>
-            remaining.collect::<Vec<_>>() == vec![5, 6],
+        Some(it::Diff::Longer(_, remaining)) => remaining.collect::<Vec<_>>() == vec![5, 6],
         _ => false,
     });
 }
 
 #[test]
 fn diff_shorter() {
-    let a = vec![1, 2, 3, 4];
+    let a = [1, 2, 3, 4];
     let b = vec![1.0, 2.0];
     let b_map = b.into_iter().map(|f| f as i32);
     let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
@@ -999,14 +1303,14 @@
     #[derive(Clone, Debug, PartialEq, Eq)]
     struct Val(u32, u32);
 
-    impl PartialOrd<Val> for Val {
-        fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
-            self.0.partial_cmp(&other.0)
+    impl PartialOrd<Self> for Val {
+        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+            Some(self.cmp(other))
         }
     }
 
     impl Ord for Val {
-        fn cmp(&self, other: &Val) -> Ordering {
+        fn cmp(&self, other: &Self) -> Ordering {
             self.0.cmp(&other.0)
         }
     }
@@ -1017,7 +1321,7 @@
     assert_eq!(Some(1u32).iter().min_set(), vec![&1]);
     assert_eq!(Some(1u32).iter().max_set(), vec![&1]);
 
-    let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
+    let data = [Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
 
     let min_set = data.iter().min_set();
     assert_eq!(min_set, vec![&Val(0, 1), &Val(0, 2)]);
@@ -1040,31 +1344,34 @@
 
 #[test]
 fn minmax() {
-    use std::cmp::Ordering;
     use crate::it::MinMaxResult;
+    use std::cmp::Ordering;
 
     // A peculiar type: Equality compares both tuple items, but ordering only the
     // first item.  This is so we can check the stability property easily.
     #[derive(Clone, Debug, PartialEq, Eq)]
     struct Val(u32, u32);
 
-    impl PartialOrd<Val> for Val {
-        fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
-            self.0.partial_cmp(&other.0)
+    impl PartialOrd<Self> for Val {
+        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+            Some(self.cmp(other))
         }
     }
 
     impl Ord for Val {
-        fn cmp(&self, other: &Val) -> Ordering {
+        fn cmp(&self, other: &Self) -> Ordering {
             self.0.cmp(&other.0)
         }
     }
 
-    assert_eq!(None::<Option<u32>>.iter().minmax(), MinMaxResult::NoElements);
+    assert_eq!(
+        None::<Option<u32>>.iter().minmax(),
+        MinMaxResult::NoElements
+    );
 
     assert_eq!(Some(1u32).iter().minmax(), MinMaxResult::OneElement(&1));
 
-    let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
+    let data = [Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
 
     let minmax = data.iter().minmax();
     assert_eq!(minmax, MinMaxResult::MinMax(&Val(0, 1), &Val(2, 1)));
@@ -1073,7 +1380,11 @@
     assert_eq!(min, &Val(2, 0));
     assert_eq!(max, &Val(0, 2));
 
-    let (min, max) = data.iter().minmax_by(|x, y| x.1.cmp(&y.1)).into_option().unwrap();
+    let (min, max) = data
+        .iter()
+        .minmax_by(|x, y| x.1.cmp(&y.1))
+        .into_option()
+        .unwrap();
     assert_eq!(min, &Val(2, 0));
     assert_eq!(max, &Val(0, 2));
 }
@@ -1096,31 +1407,34 @@
 
 #[test]
 fn while_some() {
-    let ns = (1..10).map(|x| if x % 5 != 0 { Some(x) } else { None })
-                    .while_some();
+    let ns = (1..10)
+        .map(|x| if x % 5 != 0 { Some(x) } else { None })
+        .while_some();
     it::assert_equal(ns, vec![1, 2, 3, 4]);
 }
 
-#[allow(deprecated)]
 #[test]
 fn fold_while() {
     let mut iterations = 0;
     let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
-    let sum = vec.into_iter().fold_while(0, |acc, item| {
-        iterations += 1;
-        let new_sum = acc + item;
-        if new_sum <= 20 {
-            FoldWhile::Continue(new_sum)
-        } else {
-            FoldWhile::Done(acc)
-        }
-    }).into_inner();
+    let sum = vec
+        .into_iter()
+        .fold_while(0, |acc, item| {
+            iterations += 1;
+            let new_sum = acc + item;
+            if new_sum <= 20 {
+                FoldWhile::Continue(new_sum)
+            } else {
+                FoldWhile::Done(acc)
+            }
+        })
+        .into_inner();
     assert_eq!(iterations, 6);
     assert_eq!(sum, 15);
 }
 
 #[test]
-fn tree_fold1() {
+fn tree_reduce() {
     let x = [
         "",
         "0",
@@ -1141,9 +1455,13 @@
         "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 15 x x x x",
     ];
     for (i, &s) in x.iter().enumerate() {
-        let expected = if s.is_empty() { None } else { Some(s.to_string()) };
+        let expected = if s.is_empty() {
+            None
+        } else {
+            Some(s.to_string())
+        };
         let num_strings = (0..i).map(|x| x.to_string());
-        let actual = num_strings.tree_fold1(|a, b| format!("{} {} x", a, b));
+        let actual = num_strings.tree_reduce(|a, b| format!("{} {} x", a, b));
         assert_eq!(actual, expected);
     }
 }
@@ -1153,16 +1471,53 @@
     exactly_one_question_mark_return().unwrap_err();
 }
 
-fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError<std::slice::Iter<'static, ()>>> {
+fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError<std::slice::Iter<'static, ()>>>
+{
     [].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();    
+    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]));
+    #[allow(clippy::type_complexity)]
+    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]
+        )
+    );
 }
diff --git a/crates/itertools/tests/zip.rs b/crates/itertools/tests/zip.rs
index 75157d3..716ac20 100644
--- a/crates/itertools/tests/zip.rs
+++ b/crates/itertools/tests/zip.rs
@@ -1,17 +1,17 @@
-use itertools::Itertools;
-use itertools::EitherOrBoth::{Both, Left, Right};
-use itertools::free::zip_eq;
 use itertools::multizip;
+use itertools::EitherOrBoth::{Both, Left, Right};
+use itertools::Itertools;
 
 #[test]
 fn zip_longest_fused() {
     let a = [Some(1), None, Some(3), Some(4)];
     let b = [1, 2, 3];
 
-    let unfused = a.iter().batching(|it| *it.next().unwrap())
+    let unfused = a
+        .iter()
+        .batching(|it| *it.next().unwrap())
         .zip_longest(b.iter().cloned());
-    itertools::assert_equal(unfused,
-                       vec![Both(1, 1), Right(2), Right(3)]);
+    itertools::assert_equal(unfused, vec![Both(1, 1), Right(2), Right(3)]);
 }
 
 #[test]
@@ -54,24 +54,3 @@
     assert_eq!(it.next_back(), Some((1, 1)));
     assert_eq!(it.next_back(), None);
 }
-
-
-#[should_panic]
-#[test]
-fn zip_eq_panic1()
-{
-    let a = [1, 2];
-    let b = [1, 2, 3];
-
-    zip_eq(&a, &b).count();
-}
-
-#[should_panic]
-#[test]
-fn zip_eq_panic2()
-{
-    let a: [i32; 0] = [];
-    let b = [1, 2, 3];
-
-    zip_eq(&a, &b).count();
-}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index 74a059f..c91ebac 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -149,7 +149,7 @@
  "idna",
  "instant",
  "intrusive-collections",
- "itertools",
+ "itertools 0.13.0",
  "jni-sys",
  "lazy_static",
  "libc",
@@ -790,7 +790,7 @@
  "ciborium",
  "clap 3.2.25",
  "criterion-plot",
- "itertools",
+ "itertools 0.10.5",
  "lazy_static",
  "num-traits",
  "oorandom",
@@ -811,7 +811,7 @@
 checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
 dependencies = [
  "cast",
- "itertools",
+ "itertools 0.10.5",
 ]
 
 [[package]]
@@ -1629,6 +1629,15 @@
 ]
 
 [[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
 name = "itoa"
 version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index 4192296..264b9ec 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -101,7 +101,7 @@
 idna = "=0.5.0"
 instant = "=0.1.12"
 intrusive-collections = "=0.9.6"
-itertools = "=0.10.5"
+itertools = "=0.13.0"
 jni-sys = "=0.3.0"
 lazy_static = "=1.4.0"
 libc = "=0.2.153"