diff --git a/Cargo.lock b/Cargo.lock index b0d74ebe5644..086c75136f60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -423,7 +423,7 @@ dependencies = [ "bitflags 2.9.0", "cexpr", "clang-sys", - "itertools", + "itertools 0.10.999", "lazy_static", "lazycell", "proc-macro2", @@ -3393,9 +3393,16 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.5" +version = "0.10.999" +dependencies = [ + "itertools 0.14.0", +] + +[[package]] +name = "itertools" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -4176,7 +4183,7 @@ dependencies = [ "futures", "getrandom 0.2.999", "hex", - "itertools", + "itertools 0.10.999", "maybe-async", "mls-rs-codec", "mls-rs-core", @@ -4423,7 +4430,7 @@ dependencies = [ "icu_properties", "idna", "indexmap", - "itertools", + "itertools 0.14.0", "libc", "lmdb-rkv-sys", "log", @@ -5338,7 +5345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.999", "proc-macro2", "quote", "syn", @@ -6316,7 +6323,7 @@ dependencies = [ "gecko-profiler", "icu_segmenter", "indexmap", - "itertools", + "itertools 0.14.0", "itoa", "lazy_static", "log", diff --git a/Cargo.toml b/Cargo.toml index c7cad879b12d..2043c9181fb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -220,6 +220,9 @@ ron = { path = "build/rust/ron" } # Patch `strum` 0.26.* to 0.27. strum = { path = "build/rust/strum" } +# Patch `itertools` 0.10.* to 0.14. +itertools = { path = "build/rust/itertools" } + # Overrides to allow easier use of common internal crates. moz_asserts = { path = "mozglue/static/rust/moz_asserts" } diff --git a/build/rust/itertools/Cargo.toml b/build/rust/itertools/Cargo.toml new file mode 100644 index 000000000000..1265cdc0083a --- /dev/null +++ b/build/rust/itertools/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "itertools" +version = "0.10.999" +edition = "2018" +license = "MIT OR Apache-2.0" + +[lib] +path = "lib.rs" + +[dependencies.itertools] +version = "0.14.0" +default-features = false + +[features] +default = ["itertools/default"] +use_alloc = ["itertools/use_alloc"] +use_std = ["itertools/use_std"] diff --git a/build/rust/itertools/lib.rs b/build/rust/itertools/lib.rs new file mode 100644 index 000000000000..dcd8116971b0 --- /dev/null +++ b/build/rust/itertools/lib.rs @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +pub use itertools::*; diff --git a/build/workspace-hack/Cargo.toml b/build/workspace-hack/Cargo.toml index 746d9fcb6988..5696d02eb1c3 100644 --- a/build/workspace-hack/Cargo.toml +++ b/build/workspace-hack/Cargo.toml @@ -88,7 +88,7 @@ scopeguard = { version = "1", optional = true } [build-dependencies] bindgen = { version = "0.69", default-features = false, features = ["runtime"], optional = true } cc = { version = "1.0", features = ["parallel"], optional = true } -itertools = { version = "0.10", default-features = false, features = ["use_alloc"], optional = true } +itertools = { version = "0.14", default-features = false, features = ["use_alloc"], optional = true } libc = "0.2" log = { version = "0.4", features = ["std"], optional = true } memchr = { version = "2", optional = true } diff --git a/servo/components/style/Cargo.toml b/servo/components/style/Cargo.toml index c06cc4c50208..2968932f1b76 100644 --- a/servo/components/style/Cargo.toml +++ b/servo/components/style/Cargo.toml @@ -64,7 +64,7 @@ euclid = "0.22" fxhash = "0.2" icu_segmenter = { version = "1.5", default-features = false, features = ["auto", "compiled_data"] } indexmap = {version = "2", features = ["std"]} -itertools = "0.10" +itertools = "0.14" itoa = "1.0" lazy_static = "1" log = "0.4" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 54bf46974cc7..c630fc98c543 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -1210,6 +1210,24 @@ crate is broadly used throughout the ecosystem and does not contain anything suspicious. """ +[[audits.bytecode-alliance.audits.itertools]] +who = "Nick Fitzgerald " +criteria = "safe-to-deploy" +delta = "0.10.5 -> 0.12.1" +notes = """ +Minimal `unsafe` usage. Few blocks that existed looked reasonable. Does what it +says on the tin: lots of iterators. +""" + +[[audits.bytecode-alliance.audits.itertools]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.12.1 -> 0.14.0" +notes = """ +Lots of new iterators and shuffling some things around. Some new unsafe code but +it's well-documented and well-tested. Nothing suspicious. +""" + [[audits.bytecode-alliance.audits.itoa]] who = "Dan Gohman " criteria = "safe-to-deploy" diff --git a/third_party/rust/itertools/.cargo-checksum.json b/third_party/rust/itertools/.cargo-checksum.json index a4f2873c5c25..13a468128884 100644 --- a/third_party/rust/itertools/.cargo-checksum.json +++ b/third_party/rust/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":"60ec9a1801e015e49f7fbb299b608ac730d5b1a3fb298e6cf7935c0695ddf7fe","CONTRIBUTING.md":"d5787d0fd4df15481e2e09a37234ac5dec22c007c890826991f633d890efa29e","Cargo.lock":"4422e732d4ce6f650afb53a1dcffb1e53c86dd066c3dcd66bc9620acd898b99e","Cargo.toml":"77735549383196a4156a2246dd1cd6742029b223679b3004531456d33562b0ce","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","README.md":"7dba3f55ea54eff2606f8b9f005e369cf9885dd972cb481432fce00234ee7750","benches/bench1.rs":"d632c8b839d7b318d1cb7b81b9c62570c77dcdf0696b8ce3d52067c79c930f78","benches/combinations.rs":"5b3bd243336d6b6bdc111d66218f3f0a4ecdb10fb72e90db79959e3d8bb2cf6f","benches/combinations_with_replacement.rs":"11f29160652a2d90ce7ca4b1c339c4457888ab6867e2456ce1c62e3adf9be737","benches/fold_specialization.rs":"66ab13fd8576a662afb59ef72c5565f5c3d27f7f30a976450ee5a14958654fa2","benches/k_smallest.rs":"c1bb2aa597def7f7c282d1196f9d70be6d10e1acbae03279c05bc8065544bb8e","benches/powerset.rs":"dc1fd729584147e5d8e4d19c6ca6f8706087d41c3c5beb7293d9ea43b4beab14","benches/specializations.rs":"daa989877d83ccd58e8de529184d50905b3d6fa60a9eeae917f38bd570d72fa4","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":"1e0cf7409c7291a923cf87f1c6d27cc57bd9f71ca52f7dd08a429aa994daa6ce","src/adaptors/multi_product.rs":"ad501e8ae4e5089b9d2f2be1f9a4713da6a2103b14daa759e09918409f88e321","src/combinations.rs":"9c4490bc4c7488fe9a8dba656a30493d90abb33006f7abbe3e266c237ffc986b","src/combinations_with_replacement.rs":"c7d6b7a122057e9aab075b2de41440ccd2973a6985fba75c2e93af13de55ef90","src/concat_impl.rs":"d61c00d43eca1193f135e25d3b6afd840c02ef1c573c29b264c47ee3429a45e8","src/cons_tuples_impl.rs":"7f46da33168107860b2b15d05d8edfe618e41bbc66ff0e16baf82d7373c4356d","src/diff.rs":"6e5ba3705a3a076d4fc360594014c90f1dfc597e61e8a629f96efa050e2433f5","src/duplicates_impl.rs":"1be37249b4566edc8da611ed9766ec851a526e7513bd13d80fe97482dcfcf7f3","src/either_or_both.rs":"cac278666b5d3c1fd103d97d15ce4c40960ea459441aeae83c6502087fd2ad8d","src/exactly_one_err.rs":"90b6204551161d27394af72107765dbfe3b51a77f4770c2e506fa4938985a184","src/extrema_set.rs":"11de200d853941716e9aa9a9b520b006704827c3c43a0c5f067906b0941e51d1","src/flatten_ok.rs":"62c18e5221a27949a00de49414306d6dfd601515817c1c8ae6189e3275756dd3","src/format.rs":"3ae6414043e0040f7358028c560437ea49afdbb2416df138a2410169e2619589","src/free.rs":"00ec21acee2ae2b30bf99e62472f9684c0a1719fbafc8dd2e4195ea8377c5b5d","src/group_map.rs":"c9da201137c6bb479b9308bfc38398b76950e39905f4ce8bc435c5318371522c","src/groupbylazy.rs":"5862629719258703aad47977ba1060f20fff15e962e18e6142758ebf6cd4a61c","src/grouping_map.rs":"3896c46ba4bd8ea886e5344a245658235626a104758d5ccecf223544a9ba471b","src/impl_macros.rs":"97fc5f39574805e0c220aa462cf1ae7dcac5c1082d6ee5500e7d71c120db5f88","src/intersperse.rs":"55031819e985c3184275e254c9600ecbe01e9fb49f198039c5da82a87ea5b90e","src/iter_index.rs":"1b0ff8376a4ad855d44db8c662450c777db84e0f4997b53ca575c65b107bb83b","src/k_smallest.rs":"a6840980e4c1aedd6987f93c904d362aa09a101537e447121fff58bb2473638d","src/kmerge_impl.rs":"3f999d55d7def904f71f2ca88b36707461f7f23b32966e0ccaf31d808886d843","src/lazy_buffer.rs":"baa01490fceb5a8dd7bd9c2634e19ce8a785bd1df352a9dd77d1344e3e0f8892","src/lib.rs":"e7f144351cca5018dc12270b4c33f0562afddb92452acfa85153cb276aebd6e9","src/merge_join.rs":"f2f257e63c84ed772b235229cc787ebe9ae009d7c80ed2087002a6b62c5e2133","src/minmax.rs":"0ec34b172ca8efc4aacb96f3e5771bdc5e8ac882876ee0f59d698c3924717c48","src/multipeek_impl.rs":"79eef0be49ad66f15d41808e72c03976c4f7cff5838b69d17975d3ece266f3f8","src/next_array.rs":"295924058891c08f9fe6313a1d9dd0042dcf60f0a514c6a6e009a1396d804fc9","src/pad_tail.rs":"e6bb5b086478600b0dbb8726cae8364bf83ab36d989ef467e1264eea43933b50","src/peek_nth.rs":"093f1a157b1c917f041af5244a5a46311affa2922126e36dc0ee2c501c79b58c","src/peeking_take_while.rs":"075ce13475c84e2cfdfaf1a7a0d0695332102c82a61914ed4c00a7d2634b1d34","src/permutations.rs":"b316084ee14e9e138d22f177367b3bfa24cb3e5e90ab20b9b00a9a23d653496f","src/powerset.rs":"7ab24fefc914b339dd92a6c8e639d0cad34479e09293b3346078856d6bc02d34","src/process_results_impl.rs":"6b5d82447eef4e87fef7b2a8e56b906ac7b000886a468ce421252f34ec86e371","src/put_back_n_impl.rs":"5a58d7a31c03029f0726e4d42de3be869580cf76b73c6d1ef70dd40c240b03a0","src/rciter_impl.rs":"081fd206ba4a601bd65e2f3b8d7c95e3e4a3564beb7c98944bd2e7986959c230","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":"3282b177e7dece5d3fbdc9b03563f209589a399ea45e70abf23ccca3b5512ac7","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":"c2d517badf593c0ba9b70e987a6f8482ed8997547b2e88bbec70babd9b677aa2","tests/merge_join.rs":"5fb506b989f4a331d46cdec5775ea594656985134196099eaf8d3905bdddcdd5","tests/peeking_take_while.rs":"f834361c5520dda15eb9e9ebe87507c905462201412b21859d9f83dab91d0e0b","tests/quick.rs":"d463bf8712b32742c93bc2cf3993031aeaba1f5b0505ca0ecd347313c3da0582","tests/specializations.rs":"fefbdac83bb774186882e3d31be619c9ce1dbfc4191a99ed2ac90aa764737a8f","tests/test_core.rs":"9bbc6772e97a60d93d4021c184b9e4de1c0440a1c48b991a92edc189699fd20d","tests/test_std.rs":"b0e56deefe309b9c11804bd069c744b5caea7960cd4b68244531f6edf38b9741","tests/tuples.rs":"014e4da776174bfe923270e2a359cd9c95b372fce4b952b8138909d6e2c52762","tests/zip.rs":"457e34761d9bf2943a43abd9020d2b1a4492ebff9e4d94efe4c730652fbf62af"},"package":"2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"} \ No newline at end of file diff --git a/third_party/rust/itertools/CHANGELOG.md b/third_party/rust/itertools/CHANGELOG.md index d2b40b5dbd15..6b08f68309ab 100644 --- a/third_party/rust/itertools/CHANGELOG.md +++ b/third_party/rust/itertools/CHANGELOG.md @@ -1,5 +1,192 @@ # Changelog +## 0.14.0 + +### Breaking +- Increased MSRV to 1.63.0 (#960) +- Removed generic parameter from `cons_tuples` (#988) + +### Added +- Added `array_combinations` (#991) +- Added `k_smallest_relaxed` and variants (#925) +- Added `next_array` and `collect_array` (#560) +- Implemented `DoubleEndedIterator` for `FilterOk` (#948) +- Implemented `DoubleEndedIterator` for `FilterMapOk` (#950) + +### Changed +- Allow `Q: ?Sized` in `Itertools::contains` (#971) +- Improved hygiene of `chain!` (#943) +- Improved `into_group_map_by` documentation (#1000) +- Improved `tree_reduce` documentation (#955) +- Improved discoverability of `merge_join_by` (#966) +- Improved discoverability of `take_while_inclusive` (#972) +- Improved documentation of `find_or_last` and `find_or_first` (#984) +- Prevented exponentially large type sizes in `tuple_combinations` (#945) +- Added `track_caller` attr for `asser_equal` (#976) + +### Notable Internal Changes +- Fixed clippy lints (#956, #987, #1008) +- Addressed warnings within doctests (#964) +- CI: Run most tests with miri (#961) +- CI: Speed up "cargo-semver-checks" action (#938) +- Changed an instance of `default_features` in `Cargo.toml` to `default-features` (#985) + +## 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>` for `Option>` (#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,Option)` to `EitherOrBoth` (#713) +- Added conversion from `Either` to `EitherOrBoth` (#715) +- Implemented `ExactSizeIterator` for `Tuples` (#761) +- Implemented `ExactSizeIterator` for `(Circular)TupleWindows` (#752) +- Made `EitherOrBoth` a shorthand for `EitherOrBoth` (#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` (#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 +194,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/third_party/rust/itertools/CONTRIBUTING.md b/third_party/rust/itertools/CONTRIBUTING.md new file mode 100644 index 000000000000..1dbf6f59dd54 --- /dev/null +++ b/third_party/rust/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`. + +
+4 examples (click to expand) + +```rust +use std::fmt; + +/* ===== Simple derive. ===== */ +#[derive(Debug)] +struct Name1 { + iter: I, +} + +/* ===== With an unclonable field. ===== */ +struct Name2 { + iter: I, + func: F, +} + +// No `F: Debug` bound and the field `func` is ignored. +impl fmt::Debug for Name2 { + // 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 { + iter: I, + item: Option, + func: F, +} + +// Same about `F` and `func`, similar about `I` but we must add the `I::Item: Debug` bound. +impl fmt::Debug for Name3 +where + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Name3, iter, item); +} + +/* ===== With an unclonable field for which we can provide some information. ===== */ +struct Name4 { + iter: I, + func: Option, +} + +// If ignore a field is not good enough, implement Debug fully manually. +impl fmt::Debug for Name4 { + 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() + } +} +``` +
+ +### 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/third_party/rust/itertools/Cargo.lock b/third_party/rust/itertools/Cargo.lock new file mode 100644 index 000000000000..fbf369b2fab4 --- /dev/null +++ b/third_party/rust/itertools/Cargo.lock @@ -0,0 +1,740 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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 = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "bitflags", + "clap_lex", + "indexmap", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.10.5", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.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" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +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" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "permutohedron" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "rand", + "rand_core", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[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" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "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" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/third_party/rust/itertools/Cargo.toml b/third_party/rust/itertools/Cargo.toml index 40be7e48f0e5..96f9597712b7 100644 --- a/third_party/rust/itertools/Cargo.toml +++ b/third_party/rust/itertools/Cargo.toml @@ -3,71 +3,165 @@ # 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.63.0" name = "itertools" -version = "0.10.5" +version = "0.14.0" authors = ["bluss"] -exclude = ["/bors.toml"] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false 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 + +[features] +default = ["use_std"] +use_alloc = [] +use_std = [ + "use_alloc", + "either/use_std", +] [lib] +name = "itertools" +path = "src/lib.rs" test = false bench = false -[[bench]] -name = "tuple_combinations" -harness = false +[[example]] +name = "iris" +path = "examples/iris.rs" -[[bench]] +[[test]] +name = "adaptors_no_collect" +path = "tests/adaptors_no_collect.rs" + +[[test]] +name = "flatten_ok" +path = "tests/flatten_ok.rs" + +[[test]] +name = "laziness" +path = "tests/laziness.rs" + +[[test]] +name = "macros_hygiene" +path = "tests/macros_hygiene.rs" + +[[test]] +name = "merge_join" +path = "tests/merge_join.rs" + +[[test]] +name = "peeking_take_while" +path = "tests/peeking_take_while.rs" + +[[test]] +name = "quick" +path = "tests/quick.rs" + +[[test]] +name = "specializations" +path = "tests/specializations.rs" + +[[test]] +name = "test_core" +path = "tests/test_core.rs" + +[[test]] +name = "test_std" +path = "tests/test_std.rs" + +[[test]] name = "tuples" -harness = false +path = "tests/tuples.rs" -[[bench]] -name = "fold_specialization" -harness = false - -[[bench]] -name = "combinations_with_replacement" -harness = false - -[[bench]] -name = "tree_fold1" -harness = false +[[test]] +name = "zip" +path = "tests/zip.rs" [[bench]] name = "bench1" +path = "benches/bench1.rs" harness = false [[bench]] name = "combinations" +path = "benches/combinations.rs" +harness = false + +[[bench]] +name = "combinations_with_replacement" +path = "benches/combinations_with_replacement.rs" +harness = false + +[[bench]] +name = "fold_specialization" +path = "benches/fold_specialization.rs" +harness = false + +[[bench]] +name = "k_smallest" +path = "benches/k_smallest.rs" harness = false [[bench]] name = "powerset" +path = "benches/powerset.rs" harness = false + +[[bench]] +name = "specializations" +path = "benches/specializations.rs" +harness = false + +[[bench]] +name = "tree_reduce" +path = "benches/tree_reduce.rs" +harness = false + +[[bench]] +name = "tuple_combinations" +path = "benches/tuple_combinations.rs" +harness = false + +[[bench]] +name = "tuples" +path = "benches/tuples.rs" +harness = false + [dependencies.either] version = "1.0" default-features = false + [dev-dependencies.criterion] -version = "=0" +version = "0.4.0" +features = ["html_reports"] [dev-dependencies.paste] version = "1.0.0" @@ -82,7 +176,5 @@ default-features = false [dev-dependencies.rand] version = "0.7" -[features] -default = ["use_std"] -use_alloc = [] -use_std = ["use_alloc", "either/use_std"] +[profile.bench] +debug = 2 diff --git a/third_party/rust/itertools/README.md b/third_party/rust/itertools/README.md index a911127f4559..46acc3fcae09 100644 --- a/third_party/rust/itertools/README.md +++ b/third_party/rust/itertools/README.md @@ -4,14 +4,11 @@ Extra iterator adaptors, functions and macros. Please read the [API documentation here](https://docs.rs/itertools/). -[![build_status](https://github.com/rust-itertools/itertools/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-itertools/itertools/actions) -[![crates.io](https://img.shields.io/crates/v/itertools.svg)](https://crates.io/crates/itertools) - How to use with Cargo: ```toml [dependencies] -itertools = "0.10.5" +itertools = "0.14.0" ``` How to use in your crate: @@ -21,17 +18,9 @@ use itertools::Itertools; ``` ## 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``, -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/third_party/rust/itertools/benches/bench1.rs b/third_party/rust/itertools/benches/bench1.rs index 71278d17b6b1..53e77b0da46a 100644 --- a/third_party/rust/itertools/benches/bench1.rs +++ b/third_party/rust/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 @@ fn slice_iter_rev(c: &mut Criterion) { 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 zip_slices_ziptuple(c: &mut Criterion) { }); } -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 @@ fn zip_unchecked_counted_loop(c: &mut Criterion) { 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 @@ fn zipdot_i32_unchecked_counted_loop(c: &mut Criterion) { 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 @@ fn zipdot_f32_unchecked_counted_loop(c: &mut Criterion) { 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 @@ fn zip_unchecked_counted_loop3(c: &mut Criterion) { 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 @@ fn group_by_lazy_1(c: &mut Criterion) { 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_1(c: &mut Criterion) { }); } -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 @@ fn group_by_lazy_2(c: &mut Criterion) { 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 @@ fn slice_chunks(c: &mut Criterion) { 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 @@ fn chunks_lazy_1(c: &mut Criterion) { 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 @@ fn equal(c: &mut Criterion) { 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 @@ fn merge_default(c: &mut Criterion) { 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 @@ fn merge_by_cmp(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; @@ -521,9 +453,7 @@ fn merge_by_cmp(c: &mut Criterion) { 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 @@ fn merge_by_lt(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; @@ -549,9 +481,7 @@ fn merge_by_lt(c: &mut Criterion) { 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 @@ fn kmerge_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; @@ -578,9 +510,7 @@ fn kmerge_default(c: &mut Criterion) { 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 @@ fn kmerge_tenway(c: &mut Criterion) { 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 @@ fn kmerge_tenway(c: &mut Criterion) { 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 @@ fn kmerge_tenway(c: &mut Criterion) { // 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(iter: I) -> I::Item - where I: IntoIterator, - I::Item: Default + Add +where + I: IntoIterator, + I::Item: Default + Add, { iter.into_iter().fold(<_>::default(), |x, y| x + y) } @@ -629,9 +558,7 @@ fn step_vec_2(c: &mut Criterion) { 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 @@ fn step_vec_10(c: &mut Criterion) { 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 @@ fn step_range_2(c: &mut Criterion) { 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 @@ fn step_range_10(c: &mut Criterion) { 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_iterator(c: &mut Criterion) { }); } -fn cartesian_product_fold(c: &mut Criterion) { - let xs = vec![0; 16]; - - c.bench_function("cartesian product fold", move |b| { - b.iter(|| { - let mut sum = 0; - iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| { - sum += x; - sum += y; - sum += z; - }); - sum - }) - }); -} - fn multi_cartesian_product_iterator(c: &mut Criterion) { let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; @@ -713,22 +634,6 @@ fn multi_cartesian_product_iterator(c: &mut Criterion) { }); } -fn multi_cartesian_product_fold(c: &mut Criterion) { - let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; - - c.bench_function("multi cartesian product fold", move |b| { - b.iter(|| { - let mut sum = 0; - xs.iter().multi_cartesian_product().fold((), |(), x| { - sum += x[0]; - sum += x[1]; - sum += x[2]; - }); - sum - }) - }); -} - fn cartesian_product_nested_for(c: &mut Criterion) { let xs = vec![0; 16]; @@ -753,9 +658,7 @@ fn all_equal(c: &mut Criterion) { 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 @@ fn permutations_iter(c: &mut Criterion) { } 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 @@ fn permutations_slice(c: &mut Criterion) { 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 @@ criterion_group!( 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 @@ criterion_group!( 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 @@ criterion_group!( 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/third_party/rust/itertools/benches/combinations.rs b/third_party/rust/itertools/benches/combinations.rs index e7433a4cb099..42a452111ea8 100644 --- a/third_party/rust/itertools/benches/combinations.rs +++ b/third_party/rust/itertools/benches/combinations.rs @@ -111,15 +111,7 @@ fn comb_c14(c: &mut Criterion) { } 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/third_party/rust/itertools/benches/extra/mod.rs b/third_party/rust/itertools/benches/extra/mod.rs deleted file mode 100644 index 52fe5cc3fe05..000000000000 --- a/third_party/rust/itertools/benches/extra/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub use self::zipslices::ZipSlices; -mod zipslices; diff --git a/third_party/rust/itertools/benches/extra/zipslices.rs b/third_party/rust/itertools/benches/extra/zipslices.rs deleted file mode 100644 index 633be5906869..000000000000 --- a/third_party/rust/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` 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: 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 ZipSlices - 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 Iterator for ZipSlices - where T: Slice, - U: Slice -{ - type Item = (T::Item, U::Item); - - #[inline(always)] - fn next(&mut self) -> Option { - 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) { - let len = self.len - self.index; - (len, Some(len)) - } -} - -impl DoubleEndedIterator for ZipSlices - where T: Slice, - U: Slice -{ - #[inline(always)] - fn next_back(&mut self) -> Option { - 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 ExactSizeIterator for ZipSlices - where T: Slice, - U: Slice -{} - -unsafe impl Slice for ZipSlices - 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/third_party/rust/itertools/benches/fold_specialization.rs b/third_party/rust/itertools/benches/fold_specialization.rs index 5de4671e9894..b44f34721463 100644 --- a/third_party/rust/itertools/benches/fold_specialization.rs +++ b/third_party/rust/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); impl Iterator for Unspecialized -where I: Iterator +where + I: Iterator, { type Item = I::Item; @@ -25,8 +28,7 @@ mod specialization { 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 @@ mod specialization { }); } - 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/third_party/rust/itertools/benches/k_smallest.rs b/third_party/rust/itertools/benches/k_smallest.rs new file mode 100644 index 000000000000..509ed7f8abd7 --- /dev/null +++ b/third_party/rust/itertools/benches/k_smallest.rs @@ -0,0 +1,61 @@ +use criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion}; +use itertools::Itertools; +use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng}; + +fn strict(b: &mut Bencher, (k, vals): &(usize, &Vec)) { + b.iter(|| black_box(vals.iter()).k_smallest(*k)) +} + +fn relaxed(b: &mut Bencher, (k, vals): &(usize, &Vec)) { + b.iter(|| black_box(vals.iter()).k_smallest_relaxed(*k)) +} + +fn ascending(n: usize) -> Vec { + (0..n).collect() +} + +fn random(n: usize) -> Vec { + let mut vals = (0..n).collect_vec(); + vals.shuffle(&mut StdRng::seed_from_u64(42)); + vals +} + +fn descending(n: usize) -> Vec { + (0..n).rev().collect() +} + +fn k_smallest(c: &mut Criterion, order: &str, vals: fn(usize) -> Vec) { + let mut g = c.benchmark_group(format!("k-smallest/{order}")); + + for log_n in 20..23 { + let n = 1 << log_n; + + let vals = vals(n); + + for log_k in 7..10 { + let k = 1 << log_k; + + let params = format!("{log_n}/{log_k}"); + let input = (k, &vals); + g.bench_with_input(BenchmarkId::new("strict", ¶ms), &input, strict); + g.bench_with_input(BenchmarkId::new("relaxed", ¶ms), &input, relaxed); + } + } + + g.finish() +} + +fn k_smallest_asc(c: &mut Criterion) { + k_smallest(c, "asc", ascending); +} + +fn k_smallest_rand(c: &mut Criterion) { + k_smallest(c, "rand", random); +} + +fn k_smallest_desc(c: &mut Criterion) { + k_smallest(c, "desc", descending); +} + +criterion_group!(benches, k_smallest_asc, k_smallest_rand, k_smallest_desc); +criterion_main!(benches); diff --git a/third_party/rust/itertools/benches/powerset.rs b/third_party/rust/itertools/benches/powerset.rs index 074550bc4494..018333d316c1 100644 --- a/third_party/rust/itertools/benches/powerset.rs +++ b/third_party/rust/itertools/benches/powerset.rs @@ -20,17 +20,64 @@ fn powerset_n(c: &mut Criterion, n: usize) { }); } -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 @@ criterion_group!( 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/third_party/rust/itertools/benches/specializations.rs b/third_party/rust/itertools/benches/specializations.rs new file mode 100644 index 000000000000..e70323f8e6c2 --- /dev/null +++ b/third_party/rust/itertools/benches/specializations.rs @@ -0,0 +1,669 @@ +#![allow(unstable_name_collisions)] + +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::>() + })); + $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::() + } + 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 { + DoubleEndedIterator + { + 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 { + DoubleEndedIterator + { + 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/third_party/rust/itertools/benches/tree_fold1.rs b/third_party/rust/itertools/benches/tree_reduce.rs similarity index 50% rename from third_party/rust/itertools/benches/tree_fold1.rs rename to third_party/rust/itertools/benches/tree_reduce.rs index f12995db8e38..051b14883481 100644 --- a/third_party/rust/itertools/benches/tree_fold1.rs +++ b/third_party/rust/itertools/benches/tree_reduce.rs @@ -1,12 +1,15 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use itertools::{Itertools, cloned}; +#![allow(deprecated)] -trait IterEx : Iterator { +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_fold1_vec(self, mut f: F) -> Option - where F: FnMut(Self::Item, Self::Item) -> Self::Item, - Self: Sized, + fn tree_reduce_vec(self, mut f: F) -> Option + where + F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized, { let hint = self.size_hint().0; let cap = std::mem::size_of::() * 8 - hint.leading_zeros() as usize; @@ -21,24 +24,23 @@ trait IterEx : Iterator { stack.into_iter().fold1(f) } } -impl IterEx for T {} +impl 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 = (0.. $N).collect(); + let v: Vec = (0..$N).collect(); - c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " sum"), move |b| { - b.iter(|| { - cloned(&v).$FUN(|x, y| x + y) - }) - }); + 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) { @@ -46,11 +48,10 @@ macro_rules! def_benchs { 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) - }) - }); + 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) { @@ -58,13 +59,18 @@ macro_rules! def_benchs { // 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 = (0.. ($N/4)).collect(); + let v: Vec = (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)) - }) - }); + 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)) + }) + }, + ); } } @@ -74,71 +80,71 @@ macro_rules! def_benchs { $BENCH_NAME::complex_iter, $BENCH_NAME::string_format, ); - ) + }; } -def_benchs!{ +def_benchs! { 10_000, fold1, fold1_10k, } -def_benchs!{ +def_benchs! { 10_000, - tree_fold1, - tree_fold1_stack_10k, + tree_reduce, + tree_reduce_stack_10k, } -def_benchs!{ +def_benchs! { 10_000, - tree_fold1_vec, - tree_fold1_vec_10k, + tree_reduce_vec, + tree_reduce_vec_10k, } -def_benchs!{ +def_benchs! { 100, fold1, fold1_100, } -def_benchs!{ +def_benchs! { 100, - tree_fold1, - tree_fold1_stack_100, + tree_reduce, + tree_reduce_stack_100, } -def_benchs!{ +def_benchs! { 100, - tree_fold1_vec, - tree_fold1_vec_100, + tree_reduce_vec, + tree_reduce_vec_100, } -def_benchs!{ +def_benchs! { 8, fold1, fold1_08, } -def_benchs!{ +def_benchs! { 8, - tree_fold1, - tree_fold1_stack_08, + tree_reduce, + tree_reduce_stack_08, } -def_benchs!{ +def_benchs! { 8, - tree_fold1_vec, - tree_fold1_vec_08, + tree_reduce_vec, + tree_reduce_vec_08, } criterion_main!( fold1_10k, - tree_fold1_stack_10k, - tree_fold1_vec_10k, + tree_reduce_stack_10k, + tree_reduce_vec_10k, fold1_100, - tree_fold1_stack_100, - tree_fold1_vec_100, + tree_reduce_stack_100, + tree_reduce_vec_100, fold1_08, - tree_fold1_stack_08, - tree_fold1_vec_08, + tree_reduce_stack_08, + tree_reduce_vec_08, ); diff --git a/third_party/rust/itertools/benches/tuples.rs b/third_party/rust/itertools/benches/tuples.rs index ea50aaaee1ce..2eca34712ad5 100644 --- a/third_party/rust/itertools/benches/tuples.rs +++ b/third_party/rust/itertools/benches/tuples.rs @@ -33,7 +33,7 @@ fn sum_s4(s: &[u32]) -> u32 { 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 @@ macro_rules! def_benchs { $WINDOWS:ident; $FOR_CHUNKS:ident, $FOR_WINDOWS:ident - ) => ( + ) => { fn $FOR_CHUNKS(c: &mut Criterion) { - let v: Vec = (0.. $N * 1_000).collect(); + let v: Vec = (0..$N * 1_000).collect(); let mut s = 0; c.bench_function(&stringify!($FOR_CHUNKS).replace('_', " "), move |b| { b.iter(|| { @@ -90,7 +90,7 @@ macro_rules! def_benchs { } fn $TUPLES(c: &mut Criterion) { - let v: Vec = (0.. $N * 1_000).collect(); + let v: Vec = (0..$N * 1_000).collect(); let mut s = 0; c.bench_function(&stringify!($TUPLES).replace('_', " "), move |b| { b.iter(|| { @@ -103,7 +103,7 @@ macro_rules! def_benchs { } fn $CHUNKS(c: &mut Criterion) { - let v: Vec = (0.. $N * 1_000).collect(); + let v: Vec = (0..$N * 1_000).collect(); let mut s = 0; c.bench_function(&stringify!($CHUNKS).replace('_', " "), move |b| { b.iter(|| { @@ -150,10 +150,10 @@ macro_rules! def_benchs { $TUPLE_WINDOWS, $WINDOWS, ); - ) + }; } -def_benchs!{ +def_benchs! { 1; benches_1, sum_t1, @@ -166,7 +166,7 @@ def_benchs!{ for_windows_1 } -def_benchs!{ +def_benchs! { 2; benches_2, sum_t2, @@ -179,7 +179,7 @@ def_benchs!{ for_windows_2 } -def_benchs!{ +def_benchs! { 3; benches_3, sum_t3, @@ -192,7 +192,7 @@ def_benchs!{ for_windows_3 } -def_benchs!{ +def_benchs! { 4; benches_4, sum_t4, @@ -205,9 +205,4 @@ def_benchs!{ 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/third_party/rust/itertools/clippy.toml b/third_party/rust/itertools/clippy.toml deleted file mode 100644 index 0a5485386ed7..000000000000 --- a/third_party/rust/itertools/clippy.toml +++ /dev/null @@ -1 +0,0 @@ -msrv = "1.36.0" diff --git a/third_party/rust/itertools/examples/iris.rs b/third_party/rust/itertools/examples/iris.rs index 987d9e9cba49..63f9c4832604 100644 --- a/third_party/rust/itertools/examples/iris.rs +++ b/third_party/rust/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 @@ struct Iris { data: [f32; 4], } +#[allow(dead_code)] // fields are currently ignored #[derive(Clone, Debug)] enum ParseError { Numeric(ParseFloatError), @@ -26,7 +26,7 @@ enum ParseError { impl From for ParseError { fn from(err: ParseFloatError) -> Self { - ParseError::Numeric(err) + Self::Numeric(err) } } @@ -35,8 +35,11 @@ impl FromStr for Iris { type Err = ParseError; fn from_str(s: &str) -> Result { - 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 @@ impl FromStr for Iris { 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 @@ impl FromStr for Iris { 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 @@ fn main() { 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/third_party/rust/itertools/src/adaptors/coalesce.rs b/third_party/rust/itertools/src/adaptors/coalesce.rs index 3df7cc5823fb..ab1ab5255dd6 100644 --- a/third_party/rust/itertools/src/adaptors/coalesce.rs +++ b/third_party/rust/itertools/src/adaptors/coalesce.rs @@ -4,62 +4,78 @@ use std::iter::FusedIterator; use crate::size_hint; #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct CoalesceBy +pub struct CoalesceBy where I: Iterator, + C: CountItem, { iter: I, - last: Option, + /// `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>, f: F, } -impl Clone for CoalesceBy +impl Clone for CoalesceBy where - I: Iterator, + I: Clone + Iterator, + F: Clone, + C: CountItem, + C::CItem: Clone, { clone_fields!(last, iter, f); } -impl fmt::Debug for CoalesceBy +impl fmt::Debug for CoalesceBy where I: Iterator + fmt::Debug, - T: fmt::Debug, + C: CountItem, + C::CItem: fmt::Debug, { - debug_fmt_fields!(CoalesceBy, iter); + debug_fmt_fields!(CoalesceBy, iter, last); } pub trait CoalescePredicate { fn coalesce_pair(&mut self, t: T, item: Item) -> Result; } -impl Iterator for CoalesceBy +impl Iterator for CoalesceBy where I: Iterator, - F: CoalescePredicate, + F: CoalescePredicate, + C: CountItem, { - type Item = T; + type Item = C::CItem; fn next(&mut self) -> Option { + 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) { - 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 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 @@ where } } -impl, T> FusedIterator for CoalesceBy {} +impl FusedIterator for CoalesceBy +where + I: Iterator, + F: CoalescePredicate, + C: CountItem, +{ +} + +pub struct NoCount; + +pub struct WithCount; + +pub trait CountItem { + type CItem; + fn new(t: T) -> Self::CItem; +} + +impl CountItem for NoCount { + type CItem = T; + #[inline(always)] + fn new(t: T) -> T { + t + } +} + +impl CountItem 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 = CoalesceBy::Item>; +pub type Coalesce = CoalesceBy; impl CoalescePredicate for F where @@ -99,12 +150,12 @@ where } /// Create a new `Coalesce`. -pub fn coalesce(mut iter: I, f: F) -> Coalesce +pub fn coalesce(iter: I, f: F) -> Coalesce where I: Iterator, { Coalesce { - last: iter.next(), + last: None, iter, f, } @@ -113,7 +164,7 @@ where /// 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 = CoalesceBy, ::Item>; +pub type DedupBy = CoalesceBy, NoCount>; #[derive(Clone)] pub struct DedupPred2CoalescePred(DP); @@ -156,12 +207,12 @@ impl bool> DedupPredicate for F { } /// Create a new `DedupBy`. -pub fn dedup_by(mut iter: I, dedup_pred: Pred) -> DedupBy +pub fn dedup_by(iter: I, dedup_pred: Pred) -> DedupBy where I: Iterator, { DedupBy { - last: iter.next(), + last: None, iter, f: DedupPred2CoalescePred(dedup_pred), } @@ -186,7 +237,7 @@ where /// 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 = - CoalesceBy, (usize, ::Item)>; + CoalesceBy, WithCount>; #[derive(Clone, Debug)] pub struct DedupPredWithCount2CoalescePred(DP); @@ -215,12 +266,12 @@ where pub type DedupWithCount = DedupByWithCount; /// Create a new `DedupByWithCount`. -pub fn dedup_by_with_count(mut iter: I, dedup_pred: Pred) -> DedupByWithCount +pub fn dedup_by_with_count(iter: I, dedup_pred: Pred) -> DedupByWithCount where I: Iterator, { DedupByWithCount { - last: iter.next().map(|v| (1, v)), + last: None, iter, f: DedupPredWithCount2CoalescePred(dedup_pred), } diff --git a/third_party/rust/itertools/src/adaptors/map.rs b/third_party/rust/itertools/src/adaptors/map.rs index cf5e5a00d552..c78b9be69803 100644 --- a/third_party/rust/itertools/src/adaptors/map.rs +++ b/third_party/rust/itertools/src/adaptors/map.rs @@ -4,8 +4,8 @@ use std::marker::PhantomData; #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MapSpecialCase { - iter: I, - f: F, + pub(crate) iter: I, + pub(crate) f: F, } pub trait MapSpecialCaseFn { @@ -67,10 +67,6 @@ where /// See [`.map_ok()`](crate::Itertools::map_ok) for more information. pub type MapOk = MapSpecialCase>; -/// See [`MapOk`]. -#[deprecated(note = "Use MapOk instead", since = "0.10.0")] -pub type MapResults = MapOk; - impl MapSpecialCaseFn> for MapSpecialCaseFnOk where F: FnMut(T) -> U, @@ -112,9 +108,19 @@ impl, U> MapSpecialCaseFn for MapSpecialCaseFnInto { } } -#[derive(Clone, Debug)] pub struct MapSpecialCaseFnInto(PhantomData); +impl std::fmt::Debug for MapSpecialCaseFnInto { + debug_fmt_fields!(MapSpecialCaseFnInto, 0); +} + +impl Clone for MapSpecialCaseFnInto { + #[inline] + fn clone(&self) -> Self { + Self(PhantomData) + } +} + /// Create a new [`MapInto`] iterator. pub fn map_into(iter: I) -> MapInto { MapSpecialCase { diff --git a/third_party/rust/itertools/src/adaptors/mod.rs b/third_party/rust/itertools/src/adaptors/mod.rs index 1695bbd6550c..77192f26eb70 100644 --- a/third_party/rust/itertools/src/adaptors/mod.rs +++ b/third_party/rust/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 @@ use crate::size_hint; #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Interleave { - a: Fuse, - b: Fuse, - flag: bool, + i: Fuse, + j: Fuse, + 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: I, j: J) -> Interleave<::IntoIter, ::IntoIter> - where I: IntoIterator, - J: IntoIterator +/// [`IntoIterator`] enabled version of [`Itertools::interleave`](crate::Itertools::interleave). +pub fn interleave( + i: I, + j: J, +) -> Interleave<::IntoIter, ::IntoIter> +where + I: IntoIterator, + J: IntoIterator, { 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 Iterator for Interleave - where I: Iterator, - J: Iterator +where + I: Iterator, + J: Iterator, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { - 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) { - size_hint::add(self.a.size_hint(), self.b.size_hint()) + size_hint::add(self.i.size_hint(), self.j.size_hint()) + } + + fn fold(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 FusedIterator for Interleave - where I: Iterator, - J: Iterator -{} +where + I: Iterator, + J: Iterator, +{ +} /// An iterator adaptor that alternates elements from the two iterators until /// one of them runs out. @@ -88,37 +121,44 @@ impl FusedIterator for Interleave #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct InterleaveShortest - where I: Iterator, - J: Iterator +where + I: Iterator, + J: Iterator, { - 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(a: I, b: J) -> InterleaveShortest - where I: Iterator, - J: Iterator +pub fn interleave_shortest(i: I, j: J) -> InterleaveShortest +where + I: Iterator, + J: Iterator, { InterleaveShortest { - it0: a, - it1: b, - phase: false, + i, + j, + next_coming_from_j: false, } } impl Iterator for InterleaveShortest - where I: Iterator, - J: Iterator +where + I: Iterator, + J: Iterator, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { - 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 @@ impl Iterator for InterleaveShortest #[inline] fn size_hint(&self) -> (usize, Option) { 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 @@ impl Iterator for InterleaveShortest }; (lower, upper) } + + fn fold(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 FusedIterator for InterleaveShortest - where I: FusedIterator, - J: FusedIterator -{} +where + I: FusedIterator, + J: FusedIterator, +{ +} #[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 - where I: Iterator +where + I: Iterator, { top: Option, iter: I, @@ -179,7 +250,8 @@ pub struct PutBack /// Create an iterator where you can put back a single item pub fn put_back(iterable: I) -> PutBack - where I: IntoIterator +where + I: IntoIterator, { PutBack { top: None, @@ -188,7 +260,8 @@ pub fn put_back(iterable: I) -> PutBack } impl PutBack - 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 @@ impl PutBack /// Split the `PutBack` into its parts. #[inline] pub fn into_parts(self) -> (Option, 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 { + self.top.replace(x) } } impl Iterator for PutBack - where I: Iterator +where + I: Iterator, { type Item = I::Item; #[inline] @@ -252,7 +326,8 @@ impl Iterator for PutBack } fn all(&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 @@ impl Iterator for PutBack } fn fold(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 @@ impl Iterator for PutBack /// See [`.cartesian_product()`](crate::Itertools::cartesian_product) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Product - where I: Iterator +where + I: Iterator, { a: I, - a_cur: Option, + /// `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>, b: J, b_orig: J, } @@ -293,13 +373,14 @@ pub struct Product /// Create a new cartesian product iterator /// /// Iterator element type is `(I::Item, J::Item)`. -pub fn cartesian_product(mut i: I, j: J) -> Product - where I: Iterator, - J: Clone + Iterator, - I::Item: Clone +pub fn cartesian_product(i: I, j: J) -> Product +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 @@ pub fn cartesian_product(mut i: I, j: J) -> Product } impl Iterator for Product - 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 { - 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) { - 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(mut self, mut accum: Acc, mut f: G) -> Acc - where G: FnMut(Acc, Self::Item) -> Acc, + fn fold(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 Iterator for Product } impl FusedIterator for Product - 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\*. +/// Iterator element type is `X` if the return type of `F` is `Option`. /// /// See [`.batching()`](crate::Itertools::batching) for more information. #[derive(Clone)] @@ -383,7 +481,10 @@ pub struct Batching { iter: I, } -impl fmt::Debug for Batching where I: fmt::Debug { +impl fmt::Debug for Batching +where + I: fmt::Debug, +{ debug_fmt_fields!(Batching, iter); } @@ -393,8 +494,9 @@ pub fn batching(iter: I, f: F) -> Batching { } impl Iterator for Batching - where I: Iterator, - F: FnMut(&mut I) -> Option +where + I: Iterator, + F: FnMut(&mut I) -> Option, { type Item = B; #[inline] @@ -403,205 +505,6 @@ impl Iterator for Batching } } -/// 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 { - iter: Fuse, - skip: usize, -} - -/// Create a `Step` iterator. -/// -/// **Panics** if the step is 0. -#[allow(deprecated)] -pub fn step(iter: I, step: usize) -> Step - where I: Iterator -{ - assert!(step != 0); - Step { - iter: iter.fuse(), - skip: step - 1, - } -} - -#[allow(deprecated)] -impl Iterator for Step - where I: Iterator -{ - type Item = I::Item; - #[inline] - fn next(&mut self) -> Option { - let elt = self.iter.next(); - if self.skip > 0 { - self.iter.nth(self.skip - 1); - } - elt - } - - fn size_hint(&self) -> (usize, Option) { - 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 ExactSizeIterator for Step - where I: ExactSizeIterator -{} - -pub trait MergePredicate { - fn merge_pred(&mut self, a: &T, b: &T) -> bool; -} - -#[derive(Clone, Debug)] -pub struct MergeLte; - -impl MergePredicate 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 = MergeBy; - -/// 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: I, j: J) -> Merge<::IntoIter, ::IntoIter> - where I: IntoIterator, - J: IntoIterator, - 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 - where I: Iterator, - J: Iterator -{ - a: Peekable, - b: Peekable, - fused: Option, - cmp: F, -} - -impl fmt::Debug for MergeBy - where I: Iterator + fmt::Debug, J: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(MergeBy, a, b); -} - -implbool> MergePredicate for F { - fn merge_pred(&mut self, a: &T, b: &T) -> bool { - self(a, b) - } -} - -/// Create a `MergeBy` iterator. -pub fn merge_by_new(a: I, b: J, cmp: F) -> MergeBy - where I: IntoIterator, - J: IntoIterator, - F: MergePredicate, -{ - MergeBy { - a: a.into_iter().peekable(), - b: b.into_iter().peekable(), - fused: None, - cmp, - } -} - -impl Clone for MergeBy - where I: Iterator, - J: Iterator, - Peekable: Clone, - Peekable: Clone, - F: Clone -{ - clone_fields!(a, b, fused, cmp); -} - -impl Iterator for MergeBy - where I: Iterator, - J: Iterator, - F: MergePredicate -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - 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) { - // Not ExactSizeIterator because size may be larger than usize - size_hint::add(self.a.size_hint(), self.b.size_hint()) - } -} - -impl FusedIterator for MergeBy - where I: FusedIterator, - J: FusedIterator, - F: MergePredicate -{} - /// An iterator adaptor that borrows from a `Clone`-able iterator /// to only pick off elements while the predicate returns `true`. /// @@ -612,22 +515,25 @@ pub struct TakeWhileRef<'a, I: 'a, F> { f: F, } -impl<'a, I, F> fmt::Debug for TakeWhileRef<'a, I, F> - where I: Iterator + fmt::Debug, +impl fmt::Debug for TakeWhileRef<'_, I, F> +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(iter: &mut I, f: F) -> TakeWhileRef - 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 +impl Iterator for TakeWhileRef<'_, I, F> +where + I: Iterator + Clone, + F: FnMut(&I::Item) -> bool, { type Item = I::Item; @@ -667,7 +573,8 @@ pub fn while_some(iter: I) -> WhileSome { } impl Iterator for WhileSome - where I: Iterator> +where + I: Iterator>, { type Item = A; @@ -681,6 +588,22 @@ impl Iterator for WhileSome fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) } + + fn fold(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 @@ impl Iterator for WhileSome /// 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 - where I: Iterator, - T: HasCombination +where + I: Iterator, + T: HasCombination, { iter: T::Combination, _mi: PhantomData, @@ -704,9 +628,10 @@ pub trait HasCombination: Sized { /// Create a new `TupleCombinations` from a clonable iterator. pub fn tuple_combinations(iter: I) -> TupleCombinations - where I: Iterator + Clone, - I::Item: Clone, - T: HasCombination, +where + I: Iterator + Clone, + I::Item: Clone, + T: HasCombination, { TupleCombinations { iter: T::Combination::from(iter), @@ -715,20 +640,38 @@ pub fn tuple_combinations(iter: I) -> TupleCombinations } impl Iterator for TupleCombinations - where I: Iterator, - T: HasCombination, +where + I: Iterator, + T: HasCombination, { type Item = T; fn next(&mut self) -> Option { self.iter.next() } + + fn size_hint(&self) -> SizeHint { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.count() + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.fold(init, f) + } } impl FusedIterator for TupleCombinations - where I: FusedIterator, - T: HasCombination, -{} +where + I: FusedIterator, + T: HasCombination, +{ +} #[derive(Clone, Debug)] pub struct Tuple1Combination { @@ -737,7 +680,7 @@ pub struct Tuple1Combination { impl From for Tuple1Combination { fn from(iter: I) -> Self { - Tuple1Combination { iter } + Self { iter } } } @@ -747,6 +690,21 @@ impl Iterator for Tuple1Combination { fn next(&mut self) -> Option { self.iter.next().map(|x| (x,)) } + + fn size_hint(&self) -> SizeHint { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.count() + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.map(|x| (x,)).fold(init, f) + } } impl HasCombination for (I::Item,) { @@ -780,22 +738,67 @@ macro_rules! impl_tuple_combination { impl Iterator for $C where I: Iterator + Clone, - I::Item: Clone + A: Clone, { type Item = (A, $(ignore_ident!($X, A)),*); fn next(&mut self) -> Option { - 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(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + // We outline this closure to prevent it from unnecessarily + // capturing the type parameters `I`, `B`, and `F`. Not doing + // so ended up causing exponentially big types during MIR + // inlining when building itertools with optimizations enabled. + // + // This change causes a small improvement to compile times in + // release mode. + type CurrTuple
= (A, $(ignore_ident!($X, A)),*); + type PrevTuple = ($(ignore_ident!($X, A),)*); + fn map_fn(z: &A) -> impl FnMut(PrevTuple) -> CurrTuple + '_ { + move |($($X,)*)| (z.clone(), $($X),*) + } + let Self { c, item, mut iter } = self; + if let Some(z) = item.as_ref() { + init = c + .map(map_fn::(z)) + .fold(init, &mut f); + } + while let Some(z) = iter.next() { + let c: $P = iter.clone().into(); + init = c + .map(map_fn::(&z)) + .fold(init, &mut f); + } + init + } } impl HasCombination for (A, $(ignore_ident!($X, A)),*) @@ -831,6 +834,42 @@ impl_tuple_combination!(Tuple10Combination Tuple9Combination; a b c d e f g h i) impl_tuple_combination!(Tuple11Combination Tuple10Combination; a b c d e f g h i j); impl_tuple_combination!(Tuple12Combination Tuple11Combination; a b c d e f g h i j k); +// https://en.wikipedia.org/wiki/Binomial_coefficient#In_programming_languages +pub(crate) fn checked_binomial(mut n: usize, mut k: usize) -> Option { + 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 +877,7 @@ impl_tuple_combination!(Tuple12Combination Tuple11Combination; a b c d e f g h i #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct FilterOk { iter: I, - f: F + f: F, } impl fmt::Debug for FilterOk @@ -850,33 +889,26 @@ where /// Create a new `FilterOk` iterator. pub fn filter_ok(iter: I, f: F) -> FilterOk - where I: Iterator>, - F: FnMut(&T) -> bool, +where + I: Iterator>, + F: FnMut(&T) -> bool, { - FilterOk { - iter, - f, - } + FilterOk { iter, f } } impl Iterator for FilterOk - where I: Iterator>, - F: FnMut(&T) -> bool, +where + I: Iterator>, + F: FnMut(&T) -> bool, { type Item = Result; fn next(&mut self) -> Option { - 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) { @@ -884,36 +916,65 @@ impl Iterator for FilterOk } fn 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(self) -> C - where C: FromIterator + where + C: FromIterator, { 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 DoubleEndedIterator for FilterOk +where + I: DoubleEndedIterator>, + F: FnMut(&T) -> bool, +{ + fn next_back(&mut self) -> Option { + let f = &mut self.f; + self.iter.rfind(|res| match res { + Ok(t) => f(t), + _ => true, + }) + } + + fn rfold(self, init: Acc, fold_f: Fold) -> 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)) + .rfold(init, fold_f) } } impl FusedIterator for FilterOk - where I: FusedIterator>, - F: FnMut(&T) -> bool, -{} +where + I: FusedIterator>, + 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 { iter: I, - f: F + f: F, } impl fmt::Debug for FilterMapOk @@ -933,33 +994,26 @@ fn transpose_result(result: Result, E>) -> Option> /// Create a new `FilterOk` iterator. pub fn filter_map_ok(iter: I, f: F) -> FilterMapOk - where I: Iterator>, - F: FnMut(T) -> Option, +where + I: Iterator>, + F: FnMut(T) -> Option, { - FilterMapOk { - iter, - f, - } + FilterMapOk { iter, f } } impl Iterator for FilterMapOk - where I: Iterator>, - F: FnMut(T) -> Option, +where + I: Iterator>, + F: FnMut(T) -> Option, { type Item = Result; fn next(&mut self) -> Option { - 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) { @@ -967,28 +1021,56 @@ impl Iterator for FilterMapOk } fn 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(self) -> C - where C: FromIterator + where + C: FromIterator, { 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 DoubleEndedIterator for FilterMapOk +where + I: DoubleEndedIterator>, + F: FnMut(T) -> Option, +{ + fn next_back(&mut self) -> Option { + let f = &mut self.f; + self.iter.by_ref().rev().find_map(|res| match res { + Ok(t) => f(t).map(Ok), + Err(e) => Some(Err(e)), + }) + } + + fn rfold(self, init: Acc, fold_f: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter + .filter_map(|v| transpose_result(v.map(&mut f))) + .rfold(init, fold_f) } } impl FusedIterator for FilterMapOk - where I: FusedIterator>, - F: FnMut(T) -> Option, -{} +where + I: FusedIterator>, + F: FnMut(T) -> Option, +{ +} /// An iterator adapter to get the positions of each element that matches a predicate. /// @@ -996,70 +1078,90 @@ impl FusedIterator for FilterMapOk #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Positions { - iter: I, + iter: Enumerate, f: F, - count: usize, } impl fmt::Debug for Positions where I: fmt::Debug, { - debug_fmt_fields!(Positions, iter, count); + debug_fmt_fields!(Positions, iter); } /// Create a new `Positions` iterator. pub fn positions(iter: I, f: F) -> Positions - 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 Iterator for Positions - where I: Iterator, - F: FnMut(I::Item) -> bool, +where + I: Iterator, + F: FnMut(I::Item) -> bool, { type Item = usize; fn next(&mut self) -> Option { - 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; + self.iter.find_map(|(count, val)| f(val).then_some(count)) } fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) } + + fn fold(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 DoubleEndedIterator for Positions - where I: DoubleEndedIterator + ExactSizeIterator, - F: FnMut(I::Item) -> bool, +where + I: DoubleEndedIterator + ExactSizeIterator, + F: FnMut(I::Item) -> bool, { fn next_back(&mut self) -> Option { - while let Some(v) = self.iter.next_back() { - if (self.f)(v) { - return Some(self.count + self.iter.len()) + let f = &mut self.f; + self.iter + .by_ref() + .rev() + .find_map(|(count, val)| f(val).then_some(count)) + } + + fn rfold(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 FusedIterator for Positions - 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 +1210,28 @@ where } fn fold(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(self) -> C - where C: FromIterator + where + C: FromIterator, { 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 +1239,8 @@ impl ExactSizeIterator for Update where I: ExactSizeIterator, F: FnMut(&mut I::Item), -{} +{ +} impl DoubleEndedIterator for Update where @@ -1148,4 +1261,5 @@ impl FusedIterator for Update where I: FusedIterator, F: FnMut(&mut I::Item), -{} +{ +} diff --git a/third_party/rust/itertools/src/adaptors/multi_product.rs b/third_party/rust/itertools/src/adaptors/multi_product.rs index 0b384069873e..314d4a46ef92 100644 --- a/third_party/rust/itertools/src/adaptors/multi_product.rs +++ b/third_party/rust/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`. +/// An iterator element type is `Vec`. /// /// 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(Vec>) - where I: Iterator + Clone, - I::Item: Clone; +pub struct MultiProduct(State>) +where + I: Iterator + Clone, + I::Item: Clone; + +#[derive(Clone)] +/// Internals for `MultiProduct`. +struct MultiProductInner +where + I: Iterator + Clone, + I::Item: Clone, +{ + /// Holds the iterators. + iters: Vec>, + /// Not populated at the beginning then it holds the current item of each iterator. + cur: CurrentItems>, +} impl std::fmt::Debug for MultiProduct where I: Iterator + Clone + std::fmt::Debug, I::Item: Clone + std::fmt::Debug, { - debug_fmt_fields!(CoalesceBy, 0); + debug_fmt_fields!(MultiProduct, 0); +} + +impl std::fmt::Debug for MultiProductInner +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 @@ where /// /// Iterator element is of type `Vec`. pub fn multi_cartesian_product(iters: H) -> MultiProduct<::IntoIter> - where H: Iterator, - H::Item: IntoIterator, - ::IntoIter: Clone, - ::Item: Clone +where + H: Iterator, + H::Item: IntoIterator, + ::IntoIter: Clone, + ::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 - where I: Iterator + Clone, - I::Item: Clone +where + I: Iterator + Clone, + I::Item: Clone, { - cur: Option, 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 MultiProduct - 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], - 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 { - 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 MultiProductIter - 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 Iterator for MultiProduct - where I: Iterator + Clone, - I::Item: Clone +where + I: Iterator + Clone, + I::Item: Clone, { type Item = Vec; fn next(&mut self) -> Option { - 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> = 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) { - // 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 { - 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 std::iter::FusedIterator for MultiProduct +where + I: Iterator + Clone, + I::Item: Clone, +{ +} diff --git a/third_party/rust/itertools/src/combinations.rs b/third_party/rust/itertools/src/combinations.rs index 68a59c5e4d06..54a027551607 100644 --- a/third_party/rust/itertools/src/combinations.rs +++ b/third_party/rust/itertools/src/combinations.rs @@ -1,61 +1,258 @@ +use core::array; +use core::borrow::BorrowMut; use std::fmt; use std::iter::FusedIterator; use super::lazy_buffer::LazyBuffer; use alloc::vec::Vec; +use crate::adaptors::checked_binomial; + +/// Iterator for `Vec` valued combinations returned by [`.combinations()`](crate::Itertools::combinations) +pub type Combinations = CombinationsGeneric>; +/// Iterator for const generic combinations returned by [`.array_combinations()`](crate::Itertools::array_combinations) +pub type ArrayCombinations = CombinationsGeneric; + +/// Create a new `Combinations` from a clonable iterator. +pub fn combinations(iter: I, k: usize) -> Combinations +where + I::Item: Clone, +{ + Combinations::new(iter, (0..k).collect()) +} + +/// Create a new `ArrayCombinations` from a clonable iterator. +pub fn array_combinations(iter: I) -> ArrayCombinations +where + I::Item: Clone, +{ + ArrayCombinations::new(iter, array::from_fn(|i| i)) +} + /// An iterator to iterate through all the `k`-length combinations in an iterator. /// -/// See [`.combinations()`](crate::Itertools::combinations) for more information. +/// See [`.combinations()`](crate::Itertools::combinations) and [`.array_combinations()`](crate::Itertools::array_combinations) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Combinations { - indices: Vec, +pub struct CombinationsGeneric { + indices: Idx, pool: LazyBuffer, first: bool, } -impl Clone for Combinations - where I: Clone + Iterator, - I::Item: Clone, +/// A type holding indices of elements in a pool or buffer of items from an inner iterator +/// and used to pick out different combinations in a generic way. +pub trait PoolIndex: BorrowMut<[usize]> { + type Item; + + fn extract_item>(&self, pool: &LazyBuffer) -> Self::Item + where + T: Clone; + + fn len(&self) -> usize { + self.borrow().len() + } +} + +impl PoolIndex for Vec { + type Item = Vec; + + fn extract_item>(&self, pool: &LazyBuffer) -> Vec + where + T: Clone, + { + pool.get_at(self) + } +} + +impl PoolIndex for [usize; K] { + type Item = [T; K]; + + fn extract_item>(&self, pool: &LazyBuffer) -> [T; K] + where + T: Clone, + { + pool.get_array(*self) + } +} + +impl Clone for CombinationsGeneric +where + I: Iterator + Clone, + I::Item: Clone, + Idx: Clone, { clone_fields!(indices, pool, first); } -impl fmt::Debug for Combinations - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, +impl fmt::Debug for CombinationsGeneric +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug, + Idx: fmt::Debug, { debug_fmt_fields!(Combinations, indices, pool, first); } -/// Create a new `Combinations` from a clonable iterator. -pub fn combinations(iter: I, k: usize) -> Combinations - where I: Iterator -{ - let mut pool = LazyBuffer::new(iter); - pool.prefill(k); - - Combinations { - indices: (0..k).collect(), - pool, - first: true, +impl> CombinationsGeneric { + /// Constructor with arguments the inner iterator and the initial state for the indices. + fn new(iter: I, indices: Idx) -> Self { + Self { + indices, + pool: LazyBuffer::new(iter), + first: true, + } } -} -impl Combinations { /// 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 { + &self.pool + } + /// Return the length of the inner iterator and the count of remaining combinations. + 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.borrow()).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; + } + + done + } + + /// 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 { + // Borrow once instead of noise each time it's indexed + let indices = self.indices.borrow_mut(); + + if indices.is_empty() { + return true; // Done + } + // Scan from the end, looking for an index to increment + let mut i: usize = indices.len() - 1; + + // Check if we need to consume more from the iterator + if indices[i] == self.pool.len() - 1 { + self.pool.get_next(); // may change pool size + } + + while indices[i] == i + self.pool.len() - indices.len() { + if i > 0 { + i -= 1; + } else { + // Reached the last combination + return true; + } + } + + // Increment index, and reset the ones to its right + indices[i] += 1; + for j in i + 1..indices.len() { + indices[j] = 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<::Item, usize> + where + I: Iterator, + 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.indices.extract_item(&self.pool)) + } +} + +impl Iterator for CombinationsGeneric +where + I: Iterator, + I::Item: Clone, + Idx: PoolIndex, +{ + type Item = Idx::Item; + fn next(&mut self) -> Option { + let done = if self.first { + self.init() + } else { + self.increment_indices() + }; + + if done { + return None; + } + + Some(self.indices.extract_item(&self.pool)) + } + + fn nth(&mut self, n: usize) -> Option { + self.try_nth(n).ok() + } + + fn size_hint(&self) -> (usize, Option) { + let (mut low, mut upp) = self.pool.size_hint(); + low = remaining_for(low, self.first, self.indices.borrow()).unwrap_or(usize::MAX); + upp = upp.and_then(|upp| remaining_for(upp, self.first, self.indices.borrow())); + (low, upp) + } + + #[inline] + fn count(self) -> usize { + self.n_and_count().1 + } +} + +impl FusedIterator for CombinationsGeneric +where + I: Iterator, + I::Item: Clone, + Idx: PoolIndex, +{ +} + +impl Combinations { /// 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 /// of the data pool an attempt is made to prefill the pool so that it holds `k` @@ -68,7 +265,6 @@ impl Combinations { for i in 0..k { self.indices[i] = i; } - } else { for i in 0..self.indices.len() { self.indices[i] = i; @@ -79,50 +275,34 @@ impl Combinations { } } -impl Iterator for Combinations - where I: Iterator, - I::Item: Clone -{ - type Item = Vec; - fn next(&mut self) -> Option { - if self.first { - if self.k() > self.n() { - return None; - } - 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; +/// 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 { + 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 - // 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 - } + // 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. - while self.indices[i] == i + self.pool.len() - self.indices.len() { - if i > 0 { - i -= 1; - } else { - // Reached the last combination - return None; - } - } - - // 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; - } - } - - // Create result vector based on the indices - Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect()) + // Below, `n0` resembles indices[i]. + indices.iter().enumerate().try_fold(0usize, |sum, (i, n0)| { + sum.checked_add(checked_binomial(n - 1 - *n0, k - i)?) + }) } } - -impl FusedIterator for Combinations - where I: Iterator, - I::Item: Clone -{} diff --git a/third_party/rust/itertools/src/combinations_with_replacement.rs b/third_party/rust/itertools/src/combinations_with_replacement.rs index 0fec9671acb5..c17e752508f8 100644 --- a/third_party/rust/itertools/src/combinations_with_replacement.rs +++ b/third_party/rust/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 where I: Iterator, I::Item: Clone, { - indices: Vec, + indices: Box<[usize]>, pool: LazyBuffer, first: bool, } @@ -24,18 +27,7 @@ where I: Iterator + fmt::Debug, I::Item: fmt::Debug + Clone, { - debug_fmt_fields!(Combinations, indices, pool, first); -} - -impl CombinationsWithReplacement -where - I: Iterator, - I::Item: Clone, -{ - /// Map the current mask over the pool to get an output combination - fn current(&self) -> Vec { - 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 @@ where I: Iterator, I::Item: Clone, { - let indices: Vec = alloc::vec![0; k]; + let indices = alloc::vec![0; k].into_boxed_slice(); let pool: LazyBuffer = LazyBuffer::new(iter); CombinationsWithReplacement { @@ -54,51 +46,95 @@ where } } +impl CombinationsWithReplacement +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 + self.indices[increment_from..].fill(increment_value); + false + } + // Otherwise, we're done + None => true, + } + } +} + impl Iterator for CombinationsWithReplacement where I: Iterator, I::Item: Clone, { type Item = Vec; + fn next(&mut self) -> Option { - // 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 { + 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) { + 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 +142,47 @@ impl FusedIterator for CombinationsWithReplacement 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 { + // 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/third_party/rust/itertools/src/concat_impl.rs b/third_party/rust/itertools/src/concat_impl.rs index f022ec90afb2..dc80839ce797 100644 --- a/third_party/rust/itertools/src/concat_impl.rs +++ b/third_party/rust/itertools/src/concat_impl.rs @@ -1,8 +1,6 @@ -use crate::Itertools; - /// Combine all an iterator's elements into one element by using [`Extend`]. /// -/// [`IntoIterator`]-enabled version of [`Itertools::concat`]. +/// [`IntoIterator`]-enabled version of [`Itertools::concat`](crate::Itertools::concat). /// /// This combinator will extend the first item with each of the rest of the /// items of the iterator. If the iterator is empty, the default value of @@ -10,14 +8,20 @@ use crate::Itertools; /// /// ```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(iterable: I) -> I::Item - where I: IntoIterator, - I::Item: Extend<<::Item as IntoIterator>::Item> + IntoIterator + Default +where + I: IntoIterator, + I::Item: Extend<<::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() + .reduce(|mut a, b| { + a.extend(b); + a + }) + .unwrap_or_default() } diff --git a/third_party/rust/itertools/src/cons_tuples_impl.rs b/third_party/rust/itertools/src/cons_tuples_impl.rs index ae0f48f3493d..7e86260b48f5 100644 --- a/third_party/rust/itertools/src/cons_tuples_impl.rs +++ b/third_party/rust/itertools/src/cons_tuples_impl.rs @@ -1,3 +1,4 @@ +use crate::adaptors::map::{MapSpecialCase, MapSpecialCaseFn}; macro_rules! impl_cons_iter( ($_A:ident, $_B:ident, ) => (); // stop @@ -5,60 +6,34 @@ macro_rules! impl_cons_iter( ($A:ident, $($B:ident,)*) => ( impl_cons_iter!($($B,)*); #[allow(non_snake_case)] - impl Iterator for ConsTuples - where Iter: Iterator, - { - type Item = ($($B,)* X, ); - fn next(&mut self) -> Option { - self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, )) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - fn fold(self, accum: Acc, mut f: Fold) -> Acc - where Fold: FnMut(Acc, Self::Item) -> Acc, - { - self.iter.fold(accum, move |acc, (($($B,)*), x)| f(acc, ($($B,)* x, ))) + impl<$($B),*, X> MapSpecialCaseFn<(($($B,)*), X)> for ConsTuplesFn { + type Out = ($($B,)* X, ); + fn call(&mut self, (($($B,)*), X): (($($B,)*), X)) -> Self::Out { + ($($B,)* X, ) } } - - #[allow(non_snake_case)] - impl DoubleEndedIterator for ConsTuples - where Iter: DoubleEndedIterator, - { - fn next_back(&mut self) -> Option { - self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, )) - } - } - ); ); impl_cons_iter!(A, B, C, D, E, F, G, H, I, J, K, L,); +#[derive(Debug, Clone)] +pub struct ConsTuplesFn; + /// An iterator that maps an iterator of tuples like /// `((A, B), C)` to an iterator of `(A, B, C)`. /// /// Used by the `iproduct!()` macro. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[derive(Debug)] -pub struct ConsTuples - where I: Iterator, -{ - iter: I, -} - -impl Clone for ConsTuples - where I: Clone + Iterator, -{ - clone_fields!(iter); -} +pub type ConsTuples = MapSpecialCase; /// Create an iterator that maps for example iterators of /// `((A, B), C)` to `(A, B, C)`. -pub fn cons_tuples(iterable: I) -> ConsTuples - where I: IntoIterator +pub fn cons_tuples(iterable: I) -> ConsTuples +where + I: IntoIterator, { - ConsTuples { iter: iterable.into_iter() } + ConsTuples { + iter: iterable.into_iter(), + f: ConsTuplesFn, + } } diff --git a/third_party/rust/itertools/src/diff.rs b/third_party/rust/itertools/src/diff.rs index 1731f0639fce..df88d803286f 100644 --- a/third_party/rust/itertools/src/diff.rs +++ b/third_party/rust/itertools/src/diff.rs @@ -1,10 +1,12 @@ //! "Diff"ing iterators for caching elements to sequential collections without requiring the new //! elements' iterator to be `Clone`. //! -//! - [`Diff`] (produced by the [`diff_with`] function) +//! [`Diff`] (produced by the [`diff_with`] function) //! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from //! a lock-step comparison. +use std::fmt; + use crate::free::put_back; use crate::structs::PutBack; @@ -13,8 +15,9 @@ use crate::structs::PutBack; /// `Diff` represents the way in which the elements yielded by the iterator `I` differ to some /// iterator `J`. pub enum Diff - 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 @@ pub enum Diff Longer(usize, PutBack), } +impl fmt::Debug for Diff +where + I: Iterator, + J: Iterator, + PutBack: fmt::Debug, + PutBack: 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 Clone for Diff +where + I: Iterator, + J: Iterator, + PutBack: Clone, + PutBack: 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 @@ pub enum Diff /// /// 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: I, j: J, is_equal: F) - -> Option> - where I: IntoIterator, - J: IntoIterator, - F: Fn(&I::Item, &J::Item) -> bool +pub fn diff_with(i: I, j: J, mut is_equal: F) -> Option> +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 @@ pub fn diff_with(i: I, j: J, is_equal: F) 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/third_party/rust/itertools/src/duplicates_impl.rs b/third_party/rust/itertools/src/duplicates_impl.rs index 28eda44a97f8..a0db15432d85 100644 --- a/third_party/rust/itertools/src/duplicates_impl.rs +++ b/third_party/rust/itertools/src/duplicates_impl.rs @@ -2,8 +2,8 @@ use std::hash::Hash; 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 @@ mod private { impl DuplicatesBy { pub(crate) fn new(iter: I, key_method: F) -> Self { - DuplicatesBy { + Self { iter, meta: Meta { used: HashMap::new(), @@ -77,7 +77,7 @@ mod private { type Item = I::Item; fn next(&mut self) -> Option { - let DuplicatesBy { iter, meta } = self; + let Self { iter, meta } = self; iter.find_map(|v| meta.filter(v)) } @@ -109,7 +109,7 @@ mod private { F: KeyMethod, { fn next_back(&mut self) -> Option { - let DuplicatesBy { iter, meta } = self; + let Self { iter, meta } = self; iter.rev().find_map(|v| meta.filter(v)) } } @@ -122,7 +122,7 @@ mod private { } /// Apply the identity function to elements before checking them for equality. - #[derive(Debug)] + #[derive(Debug, Clone)] pub struct ById; impl KeyMethod for ById { type Container = JustValue; @@ -133,6 +133,7 @@ mod private { } /// Apply a user-supplied function to elements before checking them for equality. + #[derive(Clone)] pub struct ByFn(pub(crate) F); impl fmt::Debug for ByFn { debug_fmt_fields!(ByFn,); @@ -213,4 +214,3 @@ where { Duplicates::new(iter, private::ById) } - diff --git a/third_party/rust/itertools/src/either_or_both.rs b/third_party/rust/itertools/src/either_or_both.rs index ef3985f757a9..b7a7fc14115b 100644 --- a/third_party/rust/itertools/src/either_or_both.rs +++ b/third_party/rust/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 { +pub enum EitherOrBoth { /// Both values are present. Both(A, B), /// Only the left value of type `A` is present. @@ -14,7 +16,7 @@ pub enum EitherOrBoth { } impl EitherOrBoth { - /// 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 @@ impl EitherOrBoth { 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 { match self { Left(left) | Both(left, _) => Some(left), @@ -56,7 +51,7 @@ impl EitherOrBoth { } } - /// 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 { match self { Right(right) | Both(_, right) => Some(right), @@ -64,7 +59,65 @@ impl EitherOrBoth { } } - /// 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, Option) { + 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 { + 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 { + 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 @@ impl EitherOrBoth { } } + /// 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, + { + 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, + { + match self { + Right(b) | Both(_, b) => b, + Left(a) => a.into(), + } + } + /// Converts from `&EitherOrBoth` to `EitherOrBoth<&A, &B>`. pub fn as_ref(&self) -> EitherOrBoth<&A, &B> { match *self { @@ -90,6 +165,32 @@ impl EitherOrBoth { } } + /// Converts from `&EitherOrBoth` 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` 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` to `EitherOrBoth`. pub fn flip(self) -> EitherOrBoth { match self { @@ -200,9 +301,9 @@ impl EitherOrBoth { 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 @@ impl EitherOrBoth { 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(&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(&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 EitherOrBoth { - /// 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(self, f: F) -> T where F: FnOnce(T, T) -> T, @@ -243,12 +494,21 @@ impl EitherOrBoth { } } -impl Into>> for EitherOrBoth { - fn into(self) -> Option> { - match self { - EitherOrBoth::Left(l) => Some(Either::Left(l)), - EitherOrBoth::Right(r) => Some(Either::Right(r)), - _ => None, +impl From> for Option> { + fn from(value: EitherOrBoth) -> Self { + match value { + Left(l) => Some(Either::Left(l)), + Right(r) => Some(Either::Right(r)), + Both(..) => None, + } + } +} + +impl From> for EitherOrBoth { + fn from(either: Either) -> Self { + match either { + Either::Left(l) => Left(l), + Either::Right(l) => Right(l), } } } diff --git a/third_party/rust/itertools/src/exactly_one_err.rs b/third_party/rust/itertools/src/exactly_one_err.rs index c54ae77ca926..19b9e19189a5 100644 --- a/third_party/rust/itertools/src/exactly_one_err.rs +++ b/third_party/rust/itertools/src/exactly_one_err.rs @@ -8,7 +8,7 @@ use either::Either; 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 @@ where 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) { size_hint::add_scalar(self.inner.size_hint(), self.additional_len()) } -} + fn fold(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 ExactSizeIterator for ExactlyOneError where I: ExactSizeIterator {} -impl Display for ExactlyOneError - where I: Iterator, +impl Display for ExactlyOneError +where + I: Iterator, { fn fmt(&self, f: &mut Formatter) -> FmtResult { let additional = self.additional_len(); @@ -85,26 +96,30 @@ impl Display for ExactlyOneError } } -impl Debug for ExactlyOneError - where I: Iterator + Debug, - I::Item: Debug, +impl Debug for ExactlyOneError +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) - } - None => { - write!(f, "ExactlyOneError[RemainingIter: {:?}]", self.inner) + dbg.field("second", second); } + None => {} } + dbg.field("inner", &self.inner).finish() } } #[cfg(feature = "use_std")] -impl Error for ExactlyOneError where I: Iterator + Debug, I::Item: Debug, {} - - +impl Error for ExactlyOneError +where + I: Iterator + Debug, + I::Item: Debug, +{ +} diff --git a/third_party/rust/itertools/src/extrema_set.rs b/third_party/rust/itertools/src/extrema_set.rs index ae128364c5d1..7d353821f36b 100644 --- a/third_party/rust/itertools/src/extrema_set.rs +++ b/third_party/rust/itertools/src/extrema_set.rs @@ -1,3 +1,4 @@ +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/third_party/rust/itertools/src/flatten_ok.rs b/third_party/rust/itertools/src/flatten_ok.rs index 21ae1f722366..48f1e90a6479 100644 --- a/third_party/rust/itertools/src/flatten_ok.rs +++ b/third_party/rust/itertools/src/flatten_ok.rs @@ -72,6 +72,29 @@ where } } + fn fold(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) { let inner_hint = |inner: &Option| { inner @@ -130,6 +153,29 @@ where } } } + + fn rfold(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 Clone for FlattenOk @@ -147,13 +193,7 @@ where 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/third_party/rust/itertools/src/format.rs b/third_party/rust/itertools/src/format.rs index d87cee95035a..3abdda369c90 100644 --- a/third_party/rust/itertools/src/format.rs +++ b/third_party/rust/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 @@ use std::cell::RefCell; /// 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>, + /// `FormatWith` uses interior mutability because `Display::fmt` takes `&self`. + inner: Cell>, } /// Format all iterator elements lazily, separated by `sep`. @@ -21,38 +20,40 @@ pub struct FormatWith<'a, I, F> { /// /// 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>, + /// `Format` uses interior mutability because `Display::fmt` takes `&self`. + inner: Cell>, } pub fn new_format(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(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 +impl fmt::Display for FormatWith<'_, I, F> +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, F> fmt::Display for FormatWith<'a, I, F> } } -impl<'a, I> Format<'a, I> - where I: Iterator, +impl fmt::Debug for FormatWith<'_, I, F> +where + I: Iterator, + F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, { - fn format(&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 Format<'_, 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 @@ macro_rules! impl_format { } } -impl_format!{Display Debug - UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer} +impl_format! {Display Debug UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer} + +impl Clone for FormatWith<'_, 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 Drop for PutBackOnDrop<'_, '_, 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 Clone for Format<'_, I> +where + I: Clone, +{ + fn clone(&self) -> Self { + struct PutBackOnDrop<'r, 'a, I> { + into: &'r Format<'a, I>, + inner: Option, + } + // This ensures we preserve the state of the original `FormatWith` if `Clone` panics + impl Drop for PutBackOnDrop<'_, '_, 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/third_party/rust/itertools/src/free.rs b/third_party/rust/itertools/src/free.rs index 19e3e28694d0..4c6820543caf 100644 --- a/third_party/rust/itertools/src/free.rs +++ b/third_party/rust/itertools/src/free.rs @@ -10,30 +10,24 @@ use std::iter::{self, Zip}; type VecIntoIter = alloc::vec::IntoIter; #[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. /// @@ -42,11 +36,12 @@ pub use crate::rciter_impl::rciter; /// ``` /// use itertools::intersperse; /// -/// itertools::assert_equal(intersperse((0..3), 8), vec![0, 8, 1, 8, 2]); +/// itertools::assert_equal(intersperse(0..3, 8), vec![0, 8, 1, 8, 2]); /// ``` pub fn intersperse(iterable: I, element: I::Item) -> Intersperse - where I: IntoIterator, - ::Item: Clone +where + I: IntoIterator, + ::Item: Clone, { Itertools::intersperse(iterable.into_iter(), element) } @@ -60,12 +55,13 @@ pub fn intersperse(iterable: I, element: I::Item) -> Intersperse /// use itertools::intersperse_with; /// /// let mut i = 10; -/// itertools::assert_equal(intersperse_with((0..3), || { i -= 1; i }), vec![0, 9, 1, 8, 2]); +/// itertools::assert_equal(intersperse_with(0..3, || { i -= 1; i }), vec![0, 9, 1, 8, 2]); /// assert_eq!(i, 8); /// ``` pub fn intersperse_with(iterable: I, element: F) -> IntersperseWith - where I: IntoIterator, - F: FnMut() -> I::Item +where + I: IntoIterator, + F: FnMut() -> I::Item, { Itertools::intersperse_with(iterable.into_iter(), element) } @@ -79,10 +75,12 @@ pub fn intersperse_with(iterable: I, element: F) -> IntersperseWith(iterable: I) -> iter::Enumerate - where I: IntoIterator +where + I: IntoIterator, { iterable.into_iter().enumerate() } @@ -96,11 +94,13 @@ pub fn enumerate(iterable: I) -> iter::Enumerate /// /// for elt in rev(&[1, 2, 3]) { /// /* loop body */ +/// # let _ = elt; /// } /// ``` pub fn rev(iterable: I) -> iter::Rev - where I: IntoIterator, - I::IntoIter: DoubleEndedIterator +where + I: IntoIterator, + I::IntoIter: DoubleEndedIterator, { iterable.into_iter().rev() } @@ -108,7 +108,7 @@ pub fn rev(iterable: I) -> iter::Rev /// Converts the arguments to iterators and zips them. /// /// [`IntoIterator`] enabled version of [`Iterator::zip`]. -/// +/// /// ## Example /// /// ``` @@ -121,23 +121,26 @@ pub fn rev(iterable: I) -> iter::Rev /// } /// 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: I, j: J) -> Zip - 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 = Vec::new(); /// /// for element in chain(&[1, 2, 3], &[4]) { @@ -145,14 +148,18 @@ pub fn zip(i: I, j: J) -> Zip /// } /// assert_eq!(result, vec![1, 2, 3, 4]); /// ``` -pub fn chain(i: I, j: J) -> iter::Chain<::IntoIter, ::IntoIter> - where I: IntoIterator, - J: IntoIterator +pub fn chain( + i: I, + j: J, +) -> iter::Chain<::IntoIter, ::IntoIter> +where + I: IntoIterator, + J: IntoIterator, { 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 +168,10 @@ pub fn chain(i: I, j: J) -> iter::Chain<::IntoIter, (iterable: I) -> iter::Cloned - where I: IntoIterator, - T: Clone, +pub fn cloned<'a, I, T>(iterable: I) -> iter::Cloned +where + I: IntoIterator, + T: Clone + 'a, { iterable.into_iter().cloned() } @@ -178,8 +186,9 @@ pub fn cloned<'a, I, T: 'a>(iterable: I) -> iter::Cloned /// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.); /// ``` pub fn fold(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 +203,9 @@ pub fn fold(iterable: I, init: B, f: F) -> B /// assert!(all(&[1, 2, 3], |elt| *elt > 0)); /// ``` pub fn all(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 +220,9 @@ pub fn all(iterable: I, f: F) -> bool /// assert!(any(&[0, -1, 2], |elt| *elt > 0)); /// ``` pub fn any(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 +237,9 @@ pub fn any(iterable: I, f: F) -> bool /// assert_eq!(max(0..10), Some(9)); /// ``` pub fn max(iterable: I) -> Option - where I: IntoIterator, - I::Item: Ord +where + I: IntoIterator, + I::Item: Ord, { iterable.into_iter().max() } @@ -242,14 +254,14 @@ pub fn max(iterable: I) -> Option /// assert_eq!(min(0..10), Some(0)); /// ``` pub fn min(iterable: I) -> Option - 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 +272,9 @@ pub fn min(iterable: I) -> Option /// ``` #[cfg(feature = "use_alloc")] pub fn join(iterable: I, sep: &str) -> String - where I: IntoIterator, - I::Item: Display +where + I: IntoIterator, + I::Item: Display, { iterable.into_iter().join(sep) } @@ -278,9 +291,29 @@ pub fn join(iterable: I, sep: &str) -> String /// ``` #[cfg(feature = "use_alloc")] pub fn sorted(iterable: I) -> VecIntoIter - 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(iterable: I) -> VecIntoIter +where + I: IntoIterator, + I::Item: Ord, +{ + iterable.into_iter().sorted_unstable() +} diff --git a/third_party/rust/itertools/src/group_map.rs b/third_party/rust/itertools/src/group_map.rs index a2d0ebb2abad..3dcee83afd00 100644 --- a/third_party/rust/itertools/src/group_map.rs +++ b/third_party/rust/itertools/src/group_map.rs @@ -9,8 +9,9 @@ use std::iter::Iterator; /// See [`.into_group_map()`](crate::Itertools::into_group_map) /// for more information. pub fn into_group_map(iter: I) -> HashMap> - where I: Iterator, - K: Hash + Eq, +where + I: Iterator, + K: Hash + Eq, { let mut lookup = HashMap::new(); @@ -21,12 +22,11 @@ pub fn into_group_map(iter: I) -> HashMap> lookup } -pub fn into_group_map_by(iter: I, f: impl Fn(&V) -> K) -> HashMap> - where - I: Iterator, - K: Hash + Eq, +pub fn into_group_map_by(iter: I, mut f: F) -> HashMap> +where + I: Iterator, + 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/third_party/rust/itertools/src/groupbylazy.rs b/third_party/rust/itertools/src/groupbylazy.rs index a5a321df4535..5847c8f7d1f7 100644 --- a/third_party/rust/itertools/src/groupbylazy.rs +++ b/third_party/rust/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 { type Key; fn call_mut(&mut self, arg: A) -> Self::Key; } -impl KeyFunction for F - where F: FnMut(A) -> K +impl KeyFunction for F +where + F: FnMut(A) -> K + ?Sized, { type Key = K; #[inline] @@ -17,9 +18,8 @@ impl KeyFunction for F } } - /// `ChunkIndex` acts like the grouping key function for `IntoChunks` -#[derive(Debug)] +#[derive(Debug, Clone)] struct ChunkIndex { size: usize, index: usize, @@ -29,7 +29,7 @@ struct ChunkIndex { impl ChunkIndex { #[inline(always)] fn new(size: usize) -> Self { - ChunkIndex { + Self { size, index: 0, key: 0, @@ -50,9 +50,10 @@ impl KeyFunction for ChunkIndex { } } - +#[derive(Clone)] struct GroupInner - where I: Iterator +where + I: Iterator, { key: F, iter: I, @@ -65,19 +66,21 @@ struct GroupInner /// 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>, - /// 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 GroupInner - 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 @@ impl GroupInner */ 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 @@ impl GroupInner // `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 @@ impl GroupInner fn next_element(&mut self) -> Option { 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 { // requested a later group -- walk through the current group up to @@ -171,11 +177,13 @@ impl GroupInner 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 @@ impl GroupInner 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 GroupInner } impl GroupInner - where I: Iterator, +where + I: Iterator, { /// Called when a group is dropped fn drop_group(&mut self, client: usize) { @@ -272,10 +283,14 @@ impl GroupInner } } -/// `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 = ChunkBy; + +/// `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 @@ impl GroupInner /// 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 - where I: Iterator, +pub struct ChunkBy +where + I: Iterator, { inner: RefCell>, // the group iterator's current index. Keep this in the main value @@ -296,11 +312,12 @@ pub struct GroupBy } /// Create a new -pub fn new(iter: J, f: F) -> GroupBy - where J: IntoIterator, - F: FnMut(&J::Item) -> K, +pub fn new(iter: J, f: F) -> ChunkBy +where + J: IntoIterator, + F: FnMut(&J::Item) -> K, { - GroupBy { + ChunkBy { inner: RefCell::new(GroupInner { key: f, iter: iter.into_iter(), @@ -317,13 +334,15 @@ pub fn new(iter: J, f: F) -> GroupBy } } -impl GroupBy - where I: Iterator, +impl ChunkBy +where + I: Iterator, { /// `client`: Index of group that requests next element fn step(&self, client: usize) -> Option - 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 GroupBy } } -impl<'a, K, I, F> IntoIterator for &'a GroupBy - where I: Iterator, - I::Item: 'a, - F: FnMut(&I::Item) -> K, - K: PartialEq +impl<'a, K, I, F> IntoIterator for &'a ChunkBy +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 @@ impl<'a, K, I, F> IntoIterator for &'a GroupBy } } - /// 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, + parent: &'a ChunkBy, } 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 @@ impl<'a, K, I, F> Iterator for Groups<'a, K, I, F> 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 @@ impl<'a, K, I, F> Iterator for Groups<'a, K, I, F> /// 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, + parent: &'a ChunkBy, index: usize, first: Option, } 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> Drop for Group<'a, K, I, F> } 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 @@ impl<'a, K, I, F> Iterator for Group<'a, K, I, F> /// Create a new pub fn new_chunks(iter: J, size: usize) -> IntoChunks - where J: IntoIterator, +where + J: IntoIterator, { IntoChunks { inner: RefCell::new(GroupInner { @@ -447,10 +479,9 @@ pub fn new_chunks(iter: J, size: usize) -> IntoChunks } } - /// `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 @@ pub fn new_chunks(iter: J, size: usize) -> IntoChunks /// See [`.chunks()`](crate::Itertools::chunks) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct IntoChunks - where I: Iterator, +where + I: Iterator, { inner: RefCell>, // the chunk iterator's current index. Keep this in the main value @@ -471,9 +503,17 @@ pub struct IntoChunks index: Cell, } +impl Clone for IntoChunks +where + I: Clone + Iterator, + I::Item: Clone, +{ + clone_fields!(inner, index); +} impl IntoChunks - where I: Iterator, +where + I: Iterator, { /// `client`: Index of chunk that requests next element fn step(&self, client: usize) -> Option { @@ -487,36 +527,37 @@ impl IntoChunks } impl<'a, I> IntoIterator for &'a IntoChunks - 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, } 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 @@ impl<'a, I> Iterator for Chunks<'a, I> 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 @@ impl<'a, I> Iterator for Chunks<'a, I> /// 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, index: usize, @@ -548,8 +588,9 @@ pub struct Chunk<'a, I: 'a> } 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> Drop for Chunk<'a, I> } 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/third_party/rust/itertools/src/grouping_map.rs b/third_party/rust/itertools/src/grouping_map.rs index bb5b582c928e..86cb55dc0077 100644 --- a/third_party/rust/itertools/src/grouping_map.rs +++ b/third_party/rust/itertools/src/grouping_map.rs @@ -1,50 +1,58 @@ -#![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); +pub type MapForGrouping = MapSpecialCase>; -impl MapForGrouping { - pub(crate) fn new(iter: I, key_mapper: F) -> Self { - Self(iter, key_mapper) +#[derive(Clone)] +pub struct GroupingMapFn(F); + +impl std::fmt::Debug for GroupingMapFn { + debug_fmt_fields!(GroupingMapFn,); +} + +impl K> MapSpecialCaseFn for GroupingMapFn { + type Out = (K, V); + fn call(&mut self, v: V) -> Self::Out { + ((self.0)(&v), v) } } -impl Iterator for MapForGrouping - where I: Iterator, - K: Hash + Eq, - F: FnMut(&V) -> K, -{ - type Item = (K, V); - fn next(&mut self) -> Option { - self.0.next().map(|val| ((self.1)(&val), val)) +pub(crate) fn new_map_for_grouping K>( + iter: I, + key_mapper: F, +) -> MapForGrouping { + MapSpecialCase { + iter, + f: GroupingMapFn(key_mapper), } } /// Creates a new `GroupingMap` from `iter` pub fn new(iter: I) -> GroupingMap - where I: Iterator, - K: Hash + Eq, +where + I: Iterator, + K: Hash + Eq, { GroupingMap { iter } } /// `GroupingMapBy` is an intermediate struct for efficient group-and-fold operations. -/// +/// /// See [`GroupingMap`] for more informations. pub type GroupingMapBy = GroupingMap>; /// `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 +61,14 @@ pub struct GroupingMap { } impl GroupingMap - where I: Iterator, - K: Hash + Eq, +where + I: Iterator, + 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 +77,17 @@ impl GroupingMap /// - 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 +98,7 @@ impl GroupingMap /// 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 +106,8 @@ impl GroupingMap /// assert_eq!(lookup.len(), 3); // The final keys are only 0, 1 and 2 /// ``` pub fn aggregate(self, mut operation: FO) -> HashMap - where FO: FnMut(Option, &K, V) -> Option, + where + FO: FnMut(Option, &K, V) -> Option, { let mut destination_map = HashMap::new(); @@ -111,6 +121,50 @@ impl GroupingMap destination_map } + /// 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 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(self, mut init: FI, mut operation: FO) -> HashMap + 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. @@ -123,27 +177,25 @@ impl GroupingMap /// - 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(self, init: R, mut operation: FO) -> HashMap - where R: Clone, - FO: FnMut(R, &K, V) -> R, + pub fn fold(self, init: R, operation: FO) -> HashMap + 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 +210,24 @@ impl GroupingMap /// - 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(self, mut operation: FO) -> HashMap - where FO: FnMut(V, &K, V) -> V, + pub fn reduce(self, mut operation: FO) -> HashMap + where + FO: FnMut(V, &K, V) -> V, { self.aggregate(|acc, key, val| { Some(match acc { @@ -184,250 +237,271 @@ impl GroupingMap }) } + /// See [`.reduce()`](GroupingMap::reduce). + #[deprecated(note = "Use .reduce() instead", since = "0.13.0")] + pub fn fold_first(self, operation: FO) -> HashMap + 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::>(); - /// + /// /// assert_eq!(lookup[&0], vec![0, 3, 6].into_iter().collect::>()); /// assert_eq!(lookup[&1], vec![1, 4].into_iter().collect::>()); /// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect::>()); /// assert_eq!(lookup.len(), 3); /// ``` pub fn collect(self) -> HashMap - where C: Default + Extend, + where + C: Default + Extend, { 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 - 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(self, mut compare: F) -> HashMap - 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(self, mut f: F) -> HashMap - 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 - 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(self, mut compare: F) -> HashMap - 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(self, mut f: F) -> HashMap - 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> - 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(self, mut compare: F) -> HashMap> - where F: FnMut(&K, &V, &V) -> Ordering, + where + F: FnMut(&K, &V, &V) -> Ordering, { self.aggregate(|acc, key, val| { Some(match acc { @@ -455,81 +529,84 @@ impl GroupingMap /// 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(self, mut f: F) -> HashMap> - 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 - where V: Add + where + V: Add, { - 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 - where V: Mul, + where + V: Mul, { - self.fold_first(|acc, _, val| acc * val) + self.reduce(|acc, _, val| acc * val) } } diff --git a/third_party/rust/itertools/src/impl_macros.rs b/third_party/rust/itertools/src/impl_macros.rs index a029843b05ff..3db5ba021967 100644 --- a/third_party/rust/itertools/src/impl_macros.rs +++ b/third_party/rust/itertools/src/impl_macros.rs @@ -1,4 +1,4 @@ -//! +//! //! Implementation's internal macros macro_rules! debug_fmt_fields { @@ -27,3 +27,8 @@ macro_rules! clone_fields { 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/third_party/rust/itertools/src/intersperse.rs b/third_party/rust/itertools/src/intersperse.rs index 10a3a5389c0c..5f4f7938ad05 100644 --- a/third_party/rust/itertools/src/intersperse.rs +++ b/third_party/rust/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 { fn generate(&mut self) -> Item; @@ -26,12 +26,13 @@ pub type Intersperse = IntersperseWith(iter: I, elt: I::Item) -> Intersperse - where I: Iterator, +where + I: Iterator, { intersperse_with(iter, IntersperseElementSimple(elt)) } -implItem> IntersperseElement for F { +impl Item> IntersperseElement for F { fn generate(&mut self) -> Item { self() } @@ -48,71 +49,94 @@ implItem> IntersperseElement for F { #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] pub struct IntersperseWith - where I: Iterator, +where + I: Iterator, { element: ElemF, iter: Fuse, - peek: Option, + /// `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>, } /// Create a new `IntersperseWith` iterator pub fn intersperse_with(iter: I, elt: ElemF) -> IntersperseWith - where I: Iterator, +where + I: Iterator, { - let mut iter = iter.fuse(); IntersperseWith { - peek: iter.next(), - iter, + peek: None, + iter: iter.fuse(), element: elt, } } impl Iterator for IntersperseWith - where I: Iterator, - ElemF: IntersperseElement +where + I: Iterator, + ElemF: IntersperseElement, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { - 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) { - // 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(mut self, init: B, mut f: F) -> B where - Self: Sized, F: FnMut(B, Self::Item) -> B, + fn fold(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 FusedIterator for IntersperseWith - where I: Iterator, - ElemF: IntersperseElement -{} +where + I: Iterator, + ElemF: IntersperseElement, +{ +} diff --git a/third_party/rust/itertools/src/iter_index.rs b/third_party/rust/itertools/src/iter_index.rs new file mode 100644 index 000000000000..aadaa72a7669 --- /dev/null +++ b/third_party/rust/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 {} + impl Sealed for ops::RangeInclusive {} + impl Sealed for ops::RangeTo {} + impl Sealed for ops::RangeToInclusive {} + impl Sealed for ops::RangeFrom {} + impl Sealed for ops::RangeFull {} +} + +/// Used by [`Itertools::get`] to know which iterator +/// to turn different ranges into. +pub trait IteratorIndex: private_iter_index::Sealed +where + I: Iterator, +{ + /// The type returned for this type of index. + type Output: Iterator; + + /// 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 IteratorIndex for Range +where + I: Iterator, +{ + type Output = Skip>; + + fn index(self, iter: I) -> Self::Output { + iter.take(self.end).skip(self.start) + } +} + +impl IteratorIndex for RangeInclusive +where + I: Iterator, +{ + type Output = Take>; + + 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 IteratorIndex for RangeTo +where + I: Iterator, +{ + type Output = Take; + + fn index(self, iter: I) -> Self::Output { + iter.take(self.end) + } +} + +impl IteratorIndex for RangeToInclusive +where + I: Iterator, +{ + type Output = Take; + + fn index(self, iter: I) -> Self::Output { + assert_ne!(self.end, usize::MAX); + iter.take(self.end + 1) + } +} + +impl IteratorIndex for RangeFrom +where + I: Iterator, +{ + type Output = Skip; + + fn index(self, iter: I) -> Self::Output { + iter.skip(self.start) + } +} + +impl IteratorIndex for RangeFull +where + I: Iterator, +{ + type Output = I; + + fn index(self, iter: I) -> Self::Output { + iter + } +} + +pub fn get(iter: I, index: R) -> R::Output +where + I: IntoIterator, + R: IteratorIndex, +{ + index.index(iter.into_iter()) +} diff --git a/third_party/rust/itertools/src/k_smallest.rs b/third_party/rust/itertools/src/k_smallest.rs index acaea5941cb6..7e4ace262b72 100644 --- a/third_party/rust/itertools/src/k_smallest.rs +++ b/third_party/rust/itertools/src/k_smallest.rs @@ -1,20 +1,138 @@ -use alloc::collections::BinaryHeap; -use core::cmp::Ord; +use alloc::vec::Vec; +use core::cmp::Ordering; -pub(crate) fn k_smallest>(mut iter: I, k: usize) -> BinaryHeap { - if k == 0 { return BinaryHeap::new(); } +/// Consumes a given iterator, returning the minimum elements in **ascending** order. +pub(crate) fn k_smallest_general(iter: I, k: usize, mut comparator: F) -> Vec +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(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::>(); + 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 = 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 +} + +pub(crate) fn k_smallest_relaxed_general(iter: I, k: usize, mut comparator: F) -> Vec +where + I: Iterator, + F: FnMut(&I::Item, &I::Item) -> Ordering, +{ + if k == 0 { + iter.last(); + return Vec::new(); + } + + let mut iter = iter.fuse(); + let mut buf = iter.by_ref().take(2 * k).collect::>(); + + if buf.len() < k { + buf.sort_unstable_by(&mut comparator); + return buf; + } + + buf.select_nth_unstable_by(k - 1, &mut comparator); + buf.truncate(k); + + iter.for_each(|val| { + if comparator(&val, &buf[k - 1]) != Ordering::Less { + return; + } + + assert_ne!(buf.len(), buf.capacity()); + buf.push(val); + + if buf.len() == 2 * k { + buf.select_nth_unstable_by(k - 1, &mut comparator); + buf.truncate(k); + } + }); + + buf.sort_unstable_by(&mut comparator); + buf.truncate(k); + buf +} + +#[inline] +pub(crate) fn key_to_cmp(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/third_party/rust/itertools/src/kmerge_impl.rs b/third_party/rust/itertools/src/kmerge_impl.rs index 509d5fc6a3ec..9ea73f9e8b10 100644 --- a/third_party/rust/itertools/src/kmerge_impl.rs +++ b/third_party/rust/itertools/src/kmerge_impl.rs @@ -1,10 +1,9 @@ use crate::size_hint; -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 +14,21 @@ use std::fmt; /// `KMerge` into a min-heap. #[derive(Debug)] struct HeadTail - where I: Iterator +where + I: Iterator, { head: I::Item, tail: I, } impl HeadTail - 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> { + fn new(mut it: I) -> Option { 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 +49,17 @@ impl HeadTail } impl Clone for HeadTail - 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(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 +68,8 @@ fn heapify(data: &mut [T], mut less_than: S) /// Sift down element at `index` (`heap` is a min-heap wrt the ordering) fn sift_down(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 +80,7 @@ fn sift_down(heap: &mut [T], index: usize, mut less_than: S) 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 +118,7 @@ impl KMergePredicate for KMergeByLt { } } -implbool> KMergePredicate for F { +impl bool> KMergePredicate for F { fn kmerge_pred(&mut self, a: &T, b: &T) -> bool { self(a, b) } @@ -128,19 +127,21 @@ implbool> KMergePredicate for F { /// Create an iterator that merges elements of the contained iterators using /// the ordering function. /// -/// [`IntoIterator`] enabled version of [`Itertools::kmerge`]. +/// [`IntoIterator`] enabled version of [`Itertools::kmerge`](crate::Itertools::kmerge). /// /// ``` /// use itertools::kmerge; /// /// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) { /// /* loop body */ +/// # let _ = elt; /// } /// ``` pub fn kmerge(iterable: I) -> KMerge<::IntoIter> - where I: IntoIterator, - I::Item: IntoIterator, - <::Item as IntoIterator>::Item: PartialOrd +where + I: IntoIterator, + I::Item: IntoIterator, + <::Item as IntoIterator>::Item: PartialOrd, { kmerge_by(iterable, KMergeByLt) } @@ -152,29 +153,34 @@ pub fn kmerge(iterable: I) -> KMerge<::IntoIter> /// /// 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 - where I: Iterator, +where + I: Iterator, { heap: Vec>, less_than: F, } impl fmt::Debug for KMergeBy - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug, { debug_fmt_fields!(KMergeBy, heap); } /// Create an iterator that merges elements of the contained iterators. /// -/// [`IntoIterator`] enabled version of [`Itertools::kmerge_by`]. -pub fn kmerge_by(iterable: I, mut less_than: F) - -> KMergeBy<::IntoIter, F> - where I: IntoIterator, - I::Item: IntoIterator, - F: KMergePredicate<<::Item as IntoIterator>::Item>, +/// [`IntoIterator`] enabled version of [`Itertools::kmerge_by`](crate::Itertools::kmerge_by). +pub fn kmerge_by( + iterable: I, + mut less_than: F, +) -> KMergeBy<::IntoIter, F> +where + I: IntoIterator, + I::Item: IntoIterator, + F: KMergePredicate<<::Item as IntoIterator>::Item>, { let iter = iterable.into_iter(); let (lower, _) = iter.size_hint(); @@ -185,16 +191,18 @@ pub fn kmerge_by(iterable: I, mut less_than: F) } impl Clone for KMergeBy - where I: Iterator + Clone, - I::Item: Clone, - F: Clone, +where + I: Iterator + Clone, + I::Item: Clone, + F: Clone, { clone_fields!(heap, less_than); } impl Iterator for KMergeBy - where I: Iterator, - F: KMergePredicate +where + I: Iterator, + F: KMergePredicate, { type Item = I::Item; @@ -208,20 +216,24 @@ impl Iterator for KMergeBy 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) { - #[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()) + .reduce(size_hint::add) + .unwrap_or((0, Some(0))) } } impl FusedIterator for KMergeBy - where I: Iterator, - F: KMergePredicate -{} +where + I: Iterator, + F: KMergePredicate, +{ +} diff --git a/third_party/rust/itertools/src/lazy_buffer.rs b/third_party/rust/itertools/src/lazy_buffer.rs index ca24062aab05..fafa5f726131 100644 --- a/third_party/rust/itertools/src/lazy_buffer.rs +++ b/third_party/rust/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 { - pub it: I, - done: bool, + it: Fuse, buffer: Vec, } @@ -12,10 +14,9 @@ impl LazyBuffer where I: Iterator, { - pub fn new(it: I) -> LazyBuffer { - LazyBuffer { - it, - done: false, + pub fn new(it: I) -> Self { + Self { + it: it.fuse(), buffer: Vec::new(), } } @@ -24,36 +25,51 @@ where 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 LazyBuffer +where + I: Iterator, + I::Item: Clone, +{ + pub fn get_at(&self, indices: &[usize]) -> Vec { + indices.iter().map(|i| self.buffer[*i].clone()).collect() + } + + pub fn get_array(&self, indices: [usize; K]) -> [I::Item; K] { + indices.map(|i| self.buffer[i].clone()) + } +} + impl Index for LazyBuffer where I: Iterator, I::Item: Sized, - Vec: Index + Vec: Index, { type Output = as Index>::Output; diff --git a/third_party/rust/itertools/src/lib.rs b/third_party/rust/itertools/src/lib.rs index f91968870fae..20226d88a00a 100644 --- a/third_party/rust/itertools/src/lib.rs +++ b/third_party/rust/itertools/src/lib.rs @@ -1,6 +1,7 @@ -#![warn(missing_docs)] -#![crate_name="itertools"] +#![warn(missing_docs, clippy::default_numeric_fallback)] +#![crate_name = "itertools"] #![cfg_attr(not(feature = "use_std"), no_std)] +#![doc(test(attr(deny(warnings), allow(deprecated, unstable_name_collisions))))] //! Extra iterator adaptors, functions and macros. //! @@ -8,6 +9,7 @@ //! the [`Itertools`] trait: //! //! ``` +//! # #[allow(unused_imports)] //! use itertools::Itertools; //! ``` //! @@ -29,6 +31,7 @@ //! //! for elt in interleave(&[1, 2, 3], &[2, 3, 4]) { //! /* loop body */ +//! # let _ = elt; //! } //! ``` //! @@ -37,13 +40,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.63.0 or later. #[cfg(not(feature = "use_std"))] extern crate core as std; @@ -52,28 +58,26 @@ extern crate core as std; 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 = alloc::collections::vec_deque::IntoIter; #[cfg(feature = "use_alloc")] type VecIntoIter = alloc::vec::IntoIter; -#[cfg(feature = "use_alloc")] use std::iter::FromIterator; #[macro_use] @@ -85,72 +89,56 @@ pub use std::iter as __std_iter; /// 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; + pub use crate::combinations::{ArrayCombinations, 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 +149,26 @@ pub mod structs { /// 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 +177,28 @@ pub use crate::either_or_both::EitherOrBoth; 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")] @@ -217,6 +209,7 @@ mod merge_join; mod minmax; #[cfg(feature = "use_alloc")] mod multipeek_impl; +mod next_array; mod pad_tail; #[cfg(feature = "use_alloc")] mod peek_nth; @@ -233,12 +226,11 @@ mod rciter_impl; 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; @@ -260,6 +252,7 @@ mod ziptuple; /// // from (0, 0, 0), (0, 0, 1), .., (0, 1, 0), (0, 1, 1), .. etc until (3, 3, 3) /// for (i, j, k) in iproduct!(0..4, 0..4, 0..4) { /// // .. +/// # let _ = (i, j, k); /// } /// # } /// ``` @@ -270,13 +263,22 @@ macro_rules! iproduct { (@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::Iterator::map( + $crate::__std_iter::IntoIterator::into_iter($I), + |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,)+) ); } @@ -336,19 +338,24 @@ macro_rules! izip { // binary ($first:expr, $second:expr $(,)*) => { - $crate::izip!($first) - .zip($second) + $crate::__std_iter::Iterator::zip( + $crate::__std_iter::IntoIterator::into_iter($first), + $second, + ) }; // n-ary where n > 2 ( $first:expr $( , $rest:expr )* $(,)* ) => { - $crate::izip!($first) + { + let iter = $crate::__std_iter::IntoIterator::into_iter($first); $( - .zip($rest) + let iter = $crate::__std_iter::Iterator::zip(iter, $rest); )* - .map( + $crate::__std_iter::Iterator::map( + iter, $crate::izip!(@closure a => (a) $( , $rest )*) ) + } }; } @@ -373,16 +380,16 @@ macro_rules! izip { /// /// Invocations of `chain!` with one argument expand to [`arg.into_iter()`](IntoIterator): /// ``` -/// use std::{ops::Range, slice}; +/// use std::ops::Range; /// use itertools::chain; -/// let _: as IntoIterator>::IntoIter = chain!((2..6),); // trailing comma optional! +/// let _: as IntoIterator>::IntoIter = chain!(2..6,); // trailing comma optional! /// let _: <&[_] as IntoIterator>::IntoIter = chain!(&[2, 3, 4]); /// ``` /// /// Invocations of `chain!` with multiple arguments [`.into_iter()`](IntoIterator) each /// argument, and then [`chain`] them together: /// ``` -/// use std::{iter::*, ops::Range, slice}; +/// use std::{iter::*, slice}; /// use itertools::{assert_equal, chain}; /// /// // e.g., this: @@ -399,16 +406,16 @@ macro_rules! izip { /// ``` macro_rules! chain { () => { - core::iter::empty() + $crate::__std_iter::empty() }; ($first:expr $(, $rest:expr )* $(,)?) => { { - let iter = core::iter::IntoIterator::into_iter($first); + let iter = $crate::__std_iter::IntoIterator::into_iter($first); $( let iter = - core::iter::Iterator::chain( + $crate::__std_iter::Iterator::chain( iter, - core::iter::IntoIterator::into_iter($rest)); + $crate::__std_iter::IntoIterator::into_iter($rest)); )* iter } @@ -421,14 +428,14 @@ macro_rules! chain { /// This trait defines a number of methods. They are divided into two groups: /// /// * *Adaptors* take an iterator and parameter as input, and return -/// a new iterator value. These are listed first in the trait. An example -/// of an adaptor is [`.interleave()`](Itertools::interleave) +/// a new iterator value. These are listed first in the trait. An example +/// of an adaptor is [`.interleave()`](Itertools::interleave) /// /// * *Regular methods* are those that don't return iterators and instead -/// return a regular value of some other kind. -/// [`.next_tuple()`](Itertools::next_tuple) is an example and the first regular -/// method in the list. -pub trait Itertools : Iterator { +/// 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 { // adaptors /// Alternate elements from two iterators until both have run out. @@ -444,8 +451,9 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3, 4, 5, 6]); /// ``` fn interleave(self, other: J) -> Interleave - where J: IntoIterator, - Self: Sized + where + J: IntoIterator, + Self: Sized, { interleave(self, other) } @@ -462,8 +470,9 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3]); /// ``` fn interleave_shortest(self, other: J) -> InterleaveShortest - where J: IntoIterator, - Self: Sized + where + J: IntoIterator, + Self: Sized, { adaptors::interleave_shortest(self, other.into_iter()) } @@ -481,8 +490,9 @@ pub trait Itertools : Iterator { /// itertools::assert_equal((0..3).intersperse(8), vec![0, 8, 1, 8, 2]); /// ``` fn intersperse(self, element: Self::Item) -> Intersperse - where Self: Sized, - Self::Item: Clone + where + Self: Sized, + Self::Item: Clone, { intersperse::intersperse(self, element) } @@ -502,12 +512,63 @@ pub trait Itertools : Iterator { /// assert_eq!(i, 8); /// ``` fn intersperse_with(self, element: F) -> IntersperseWith - 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(self, index: R) -> R::Output + where + Self: Sized, + R: traits::IteratorIndex, + { + 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 +597,9 @@ pub trait Itertools : Iterator { /// ``` #[inline] fn zip_longest(self, other: J) -> ZipLongest - where J: IntoIterator, - Self: Sized + where + J: IntoIterator, + Self: Sized, { zip_longest::zip_longest(self, other.into_iter()) } @@ -549,8 +611,9 @@ pub trait Itertools : Iterator { /// lengths. #[inline] fn zip_eq(self, other: J) -> ZipEq - where J: IntoIterator, - Self: Sized + where + J: IntoIterator, + Self: Sized, { zip_eq(self, other) } @@ -579,8 +642,9 @@ pub trait Itertools : Iterator { /// ``` /// fn batching(self, f: F) -> Batching - where F: FnMut(&mut Self) -> Option, - Self: Sized + where + F: FnMut(&mut Self) -> Option, + Self: Sized, { adaptors::batching(self, f) } @@ -589,10 +653,10 @@ pub trait Itertools : Iterator { /// 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 +671,47 @@ pub trait Itertools : Iterator { /// ``` /// 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(self, key: F) -> GroupBy - where Self: Sized, - F: FnMut(&Self::Item) -> K, - K: PartialEq, + fn chunk_by(self, key: F) -> ChunkBy + 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(self, key: F) -> ChunkBy + 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 +734,8 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn chunks(self, size: usize) -> IntoChunks - where Self: Sized, + where + Self: Sized, { assert!(size != 0); groupbylazy::new_chunks(self, size) @@ -697,9 +775,10 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4)]); /// ``` fn tuple_windows(self) -> TupleWindows - where Self: Sized + Iterator, - T: traits::HomogeneousTuple, - T::Item: Clone + where + Self: Sized + Iterator, + T: traits::HomogeneousTuple, + T::Item: Clone, { tuple_impl::tuple_windows(self) } @@ -732,9 +811,10 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4), (3, 4, 1), (4, 1, 2)]); /// ``` fn circular_tuple_windows(self) -> CircularTupleWindows - where Self: Sized + Clone + Iterator + ExactSizeIterator, - T: tuple_impl::TupleCollect + Clone, - T::Item: Clone + where + Self: Sized + Clone + Iterator + ExactSizeIterator, + T: tuple_impl::TupleCollect + Clone, + T::Item: Clone, { tuple_impl::circular_tuple_windows(self) } @@ -770,8 +850,9 @@ pub trait Itertools : Iterator { /// /// See also [`Tuples::into_buffer`]. fn tuples(self) -> Tuples - where Self: Sized + Iterator, - T: traits::HomogeneousTuple + where + Self: Sized + Iterator, + T: traits::HomogeneousTuple, { tuple_impl::tuples(self) } @@ -795,36 +876,13 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn tee(self) -> (Tee, Tee) - 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 - where Self: Sized - { - adaptors::step(self, n) - } - /// Convert each item of the iterator using the [`Into`] trait. /// /// ```rust @@ -833,21 +891,13 @@ pub trait Itertools : Iterator { /// (1i32..42i32).map_into::().collect_vec(); /// ``` fn map_into(self) -> MapInto - where Self: Sized, - Self::Item: Into, + where + Self: Sized, + Self::Item: Into, { adaptors::map_into(self) } - /// See [`.map_ok()`](Itertools::map_ok). - #[deprecated(note="Use .map_ok() instead", since="0.10.0")] - fn map_results(self, f: F) -> MapOk - where Self: Iterator> + 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 +910,9 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![Ok(42), Err(false), Ok(12)]); /// ``` fn map_ok(self, f: F) -> MapOk - where Self: Iterator> + Sized, - F: FnMut(T) -> U, + where + Self: Iterator> + Sized, + F: FnMut(T) -> U, { adaptors::map_ok(self, f) } @@ -878,8 +929,9 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![Ok(22), Err(false)]); /// ``` fn filter_ok(self, f: F) -> FilterOk - where Self: Iterator> + Sized, - F: FnMut(&T) -> bool, + where + Self: Iterator> + Sized, + F: FnMut(&T) -> bool, { adaptors::filter_ok(self, f) } @@ -896,15 +948,16 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![Ok(44), Err(false)]); /// ``` fn filter_map_ok(self, f: F) -> FilterMapOk - where Self: Iterator> + Sized, - F: FnMut(T) -> Option, + where + Self: Iterator> + Sized, + F: FnMut(T) -> Option, { 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 +967,57 @@ pub trait Itertools : Iterator { /// 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, bool> = it.collect(); /// assert_eq!(output_result, Err(false)); /// ``` fn flatten_ok(self) -> FlattenOk - where Self: Iterator> + Sized, - T: IntoIterator + where + Self: Iterator> + 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; + /// + /// let first_values: Vec = vec![Ok(1), Ok(0), Ok(3)]; + /// let second_values: Vec = 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(self, processor: F) -> Result + where + Self: Iterator> + Sized, + F: FnOnce(ProcessResults) -> 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 +1027,16 @@ pub trait Itertools : Iterator { /// ``` /// 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(self, other: J) -> Merge - where Self: Sized, - Self::Item: PartialOrd, - J: IntoIterator + where + Self: Sized, + Self::Item: PartialOrd, + J: IntoIterator, { merge(self, other) } @@ -963,19 +1056,22 @@ pub trait Itertools : Iterator { /// let it = a.merge_by(b, |x, y| x.1 <= y.1); /// itertools::assert_equal(it, vec![(0, 'a'), (0, 'b'), (1, 'c'), (1, 'd')]); /// ``` - fn merge_by(self, other: J, is_first: F) -> MergeBy - where Self: Sized, - J: IntoIterator, - F: FnMut(&Self::Item, &Self::Item) -> bool + where + Self: Sized, + J: IntoIterator, + 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 +1087,50 @@ pub trait Itertools : Iterator { /// 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)] + /// // This performs a diff in the style of the Unix command comm(1), + /// // generalized to arbitrary types rather than text. + /// a.merge_join_by(b, Ord::cmp), + /// 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(self, other: J, cmp_fn: F) -> MergeJoinBy - where J: IntoIterator, - F: FnMut(&Self::Item, &J::Item) -> std::cmp::Ordering, - Self: Sized + #[doc(alias = "comm")] + fn merge_join_by(self, other: J, cmp_fn: F) -> MergeJoinBy + where + J: IntoIterator, + F: FnMut(&Self::Item, &J::Item) -> T, + Self: Sized, { merge_join_by(self, other, cmp_fn) } @@ -1018,17 +1145,18 @@ pub trait Itertools : Iterator { /// ``` /// 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<::IntoIter> - where Self: Sized, - Self::Item: IntoIterator, - ::Item: PartialOrd, + where + Self: Sized, + Self::Item: IntoIterator, + ::Item: PartialOrd, { kmerge(self) } @@ -1054,12 +1182,11 @@ pub trait Itertools : Iterator { /// assert_eq!(it.last(), Some(-7.)); /// ``` #[cfg(feature = "use_alloc")] - fn kmerge_by(self, first: F) - -> KMergeBy<::IntoIter, F> - where Self: Sized, - Self::Item: IntoIterator, - F: FnMut(&::Item, - &::Item) -> bool + fn kmerge_by(self, first: F) -> KMergeBy<::IntoIter, F> + where + Self: Sized, + Self::Item: IntoIterator, + F: FnMut(&::Item, &::Item) -> bool, { kmerge_by(self, first) } @@ -1076,10 +1203,11 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![(0, 'α'), (0, 'β'), (1, 'α'), (1, 'β')]); /// ``` fn cartesian_product(self, other: J) -> Product - 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 +1219,11 @@ pub trait Itertools : Iterator { /// the product of iterators yielding multiple types, use the /// [`iproduct`] macro instead. /// - /// /// The iterator element type is `Vec`, 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 +1238,23 @@ pub trait Itertools : Iterator { /// 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<::IntoIter> - where Self: Sized, - Self::Item: IntoIterator, - ::IntoIter: Clone, - ::Item: Clone + where + Self: Sized, + Self::Item: IntoIterator, + ::IntoIter: Clone, + ::Item: Clone, { adaptors::multi_cartesian_product(self) } @@ -1148,9 +1288,9 @@ pub trait Itertools : Iterator { /// vec![-6., 4., -1.]); /// ``` fn coalesce(self, f: F) -> Coalesce - where Self: Sized, - F: FnMut(Self::Item, Self::Item) - -> Result + where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Result, { adaptors::coalesce(self, f) } @@ -1170,8 +1310,9 @@ pub trait Itertools : Iterator { /// vec![1., 2., 3., 2.]); /// ``` fn dedup(self) -> Dedup - where Self: Sized, - Self::Item: PartialEq, + where + Self: Sized, + Self::Item: PartialEq, { adaptors::dedup(self) } @@ -1192,8 +1333,9 @@ pub trait Itertools : Iterator { /// vec![(0, 1.), (0, 2.), (0, 3.), (1, 2.)]); /// ``` fn dedup_by(self, cmp: Cmp) -> DedupBy - 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 +1402,9 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_std")] fn duplicates(self) -> Duplicates - where Self: Sized, - Self::Item: Eq + Hash + where + Self: Sized, + Self::Item: Eq + Hash, { duplicates_impl::duplicates(self) } @@ -1285,9 +1428,10 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_std")] fn duplicates_by(self, f: F) -> DuplicatesBy - 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 +1456,9 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_std")] fn unique(self) -> Unique - where Self: Sized, - Self::Item: Clone + Eq + Hash + where + Self: Sized, + Self::Item: Clone + Eq + Hash, { unique_impl::unique(self) } @@ -1338,9 +1483,10 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_std")] fn unique_by(self, f: F) -> UniqueBy - 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 +1504,9 @@ pub trait Itertools : Iterator { /// See also [`.take_while_ref()`](Itertools::take_while_ref) /// which is a similar adaptor. fn peeking_take_while(&mut self, accept: F) -> PeekingTakeWhile - 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 +1530,82 @@ pub trait Itertools : Iterator { /// /// ``` fn take_while_ref(&mut self, accept: F) -> TakeWhileRef - 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); + #[doc(alias = "take_until")] + fn take_while_inclusive(self, accept: F) -> TakeWhileInclusive + where + Self: Sized, + F: FnMut(&Self::Item) -> bool, + { + take_while_inclusive::TakeWhileInclusive::new(self, accept) + } + /// Return an iterator adaptor that filters `Option` iterator elements /// and produces `A`. Stops on the first `None` encountered. /// @@ -1404,7 +1621,8 @@ pub trait Itertools : Iterator { /// /// ``` fn while_some(self) -> WhileSome - where Self: Sized + Iterator> + where + Self: Sized + Iterator>, { adaptors::while_some(self) } @@ -1415,6 +1633,11 @@ pub trait Itertools : Iterator { /// 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,19 +1666,72 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]); /// ``` fn tuple_combinations(self) -> TupleCombinations - where Self: Sized + Clone, - Self::Item: Clone, - T: adaptors::HasCombination, + where + Self: Sized + Clone, + Self::Item: Clone, + T: adaptors::HasCombination, { adaptors::tuple_combinations(self) } + /// Return an iterator adaptor that iterates over the combinations of the + /// elements from an iterator. + /// + /// Iterator element type is [Self::Item; K]. The iterator produces a new + /// array 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; + /// + /// let mut v = Vec::new(); + /// for [a, b] in (1..5).array_combinations() { + /// v.push([a, b]); + /// } + /// assert_eq!(v, vec![[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]); + /// + /// let mut it = (1..5).array_combinations(); + /// assert_eq!(Some([1, 2, 3]), it.next()); + /// assert_eq!(Some([1, 2, 4]), it.next()); + /// assert_eq!(Some([1, 3, 4]), it.next()); + /// assert_eq!(Some([2, 3, 4]), it.next()); + /// assert_eq!(None, it.next()); + /// + /// // this requires a type hint + /// let it = (1..5).array_combinations::<3>(); + /// itertools::assert_equal(it, vec![[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]); + /// + /// // you can also specify the complete type + /// use itertools::ArrayCombinations; + /// use std::ops::Range; + /// + /// let it: ArrayCombinations, 3> = (1..5).array_combinations(); + /// itertools::assert_equal(it, vec![[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]); + /// ``` + #[cfg(feature = "use_alloc")] + fn array_combinations(self) -> ArrayCombinations + where + Self: Sized + Clone, + Self::Item: Clone, + { + combinations::array_combinations(self) + } + /// Return an iterator adaptor that iterates over the `k`-length combinations of /// the elements from an iterator. /// - /// Iterator element type is `Vec`. The iterator produces a new Vec per iteration, + /// Iterator element type is `Vec`. 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 +1757,9 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn combinations(self, k: usize) -> Combinations - where Self: Sized, - Self::Item: Clone + where + Self: Sized, + Self::Item: Clone, { combinations::combinations(self, k) } @@ -1490,7 +1767,7 @@ pub trait Itertools : Iterator { /// Return an iterator that iterates over the `k`-length combinations of /// the elements from an iterator, with replacement. /// - /// Iterator element type is `Vec`. The iterator produces a new Vec per iteration, + /// Iterator element type is `Vec`. The iterator produces a new `Vec` per iteration, /// and clones the iterator elements. /// /// ``` @@ -1519,11 +1796,14 @@ pub trait Itertools : Iterator { /// elements from an iterator. /// /// Iterator element type is `Vec` 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 +1834,9 @@ pub trait Itertools : Iterator { /// re-iterated if the permutations adaptor is completed and re-iterated. #[cfg(feature = "use_alloc")] fn permutations(self, k: usize) -> Permutations - where Self: Sized, - Self::Item: Clone + where + Self: Sized, + Self::Item: Clone, { permutations::permutations(self, k) } @@ -1590,8 +1871,9 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn powerset(self) -> Powerset - where Self: Sized, - Self::Item: Clone, + where + Self: Sized, + Self::Item: Clone, { powerset::powerset(self) } @@ -1614,33 +1896,35 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(it, vec![18, 16, 14, 12, 10, 4, 3, 2, 1, 0]); /// ``` fn pad_using(self, min: usize, f: F) -> PadUsing - 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`](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 - where Self: Sized, + where + Self: Sized, { with_position::with_position(self) } @@ -1648,7 +1932,7 @@ pub trait Itertools : Iterator { /// 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 +1943,9 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(data.iter().positions(|v| v % 2 == 1).rev(), vec![7, 6, 3, 2, 0]); /// ``` fn positions

(self, predicate: P) -> Positions - where Self: Sized, - P: FnMut(Self::Item) -> bool, + where + Self: Sized, + P: FnMut(Self::Item) -> bool, { adaptors::positions(self, predicate) } @@ -1672,17 +1957,62 @@ pub trait Itertools : Iterator { /// use itertools::Itertools; /// /// let input = vec![vec![1], vec![3, 2, 1]]; - /// let it = input.into_iter().update(|mut v| v.push(0)); + /// let it = input.into_iter().update(|v| v.push(0)); /// itertools::assert_equal(it, vec![vec![1, 0], vec![3, 2, 1, 0]]); /// ``` fn update(self, updater: F) -> Update - where Self: Sized, - F: FnMut(&mut Self::Item), + where + Self: Sized, + F: FnMut(&mut Self::Item), { adaptors::update(self, updater) } // non-adaptor methods + /// Advances the iterator and returns the next items grouped in an array of + /// a specific size. + /// + /// If there are enough elements to be grouped in an array, then the array + /// is returned inside `Some`, otherwise `None` is returned. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut iter = 1..5; + /// + /// assert_eq!(Some([1, 2]), iter.next_array()); + /// ``` + fn next_array(&mut self) -> Option<[Self::Item; N]> + where + Self: Sized, + { + next_array::next_array(self) + } + + /// Collects all items from the iterator into an array of a specific size. + /// + /// If the number of elements inside the iterator is **exactly** equal to + /// the array size, then the array is returned inside `Some`, otherwise + /// `None` is returned. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let iter = 1..3; + /// + /// if let Some([x, y]) = iter.collect_array() { + /// assert_eq!([x, y], [1, 2]) + /// } else { + /// panic!("Expected two elements") + /// } + /// ``` + fn collect_array(mut self) -> Option<[Self::Item; N]> + where + Self: Sized, + { + self.next_array().filter(|_| self.next().is_none()) + } + /// Advances the iterator and returns the next items grouped in a tuple of /// a specific size (up to 12). /// @@ -1697,8 +2027,9 @@ pub trait Itertools : Iterator { /// assert_eq!(Some((1, 2)), iter.next_tuple()); /// ``` fn next_tuple(&mut self) -> Option - where Self: Sized + Iterator, - T: traits::HomogeneousTuple + where + Self: Sized + Iterator, + T: traits::HomogeneousTuple, { T::collect_from_iter_no_buf(self) } @@ -1722,19 +2053,19 @@ pub trait Itertools : Iterator { /// } /// ``` fn collect_tuple(mut self) -> Option - where Self: Sized + Iterator, - T: traits::HomogeneousTuple + where + Self: Sized + Iterator, + 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 +2077,10 @@ pub trait Itertools : Iterator { /// assert_eq!(text.chars().find_position(|ch| ch.is_lowercase()), Some((1, 'α'))); /// ``` fn find_position

(&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. /// @@ -1766,14 +2093,31 @@ pub trait Itertools : Iterator { /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 5), Some(&4)); /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 2), Some(&3)); /// assert_eq!(std::iter::empty::().find_or_last(|&x| x > 5), None); + /// + /// // An iterator of Results can return the first Ok or the last Err: + /// let input = vec![Err(()), Ok(11), Err(()), Ok(22)]; + /// assert_eq!(input.into_iter().find_or_last(Result::is_ok), Some(Ok(11))); + /// + /// let input: Vec> = vec![Err(11), Err(22)]; + /// assert_eq!(input.into_iter().find_or_last(Result::is_ok), Some(Err(22))); + /// + /// assert_eq!(std::iter::empty::>().find_or_last(Result::is_ok), None); /// ``` fn find_or_last

(mut self, mut predicate: P) -> Option - 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. /// @@ -1786,10 +2130,20 @@ pub trait Itertools : Iterator { /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 5), Some(&1)); /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 2), Some(&3)); /// assert_eq!(std::iter::empty::().find_or_first(|&x| x > 5), None); + /// + /// // An iterator of Results can return the first Ok or the first Err: + /// let input = vec![Err(()), Ok(11), Err(()), Ok(22)]; + /// assert_eq!(input.into_iter().find_or_first(Result::is_ok), Some(Ok(11))); + /// + /// let input: Vec> = vec![Err(11), Err(22)]; + /// assert_eq!(input.into_iter().find_or_first(Result::is_ok), Some(Err(11))); + /// + /// assert_eq!(std::iter::empty::>().find_or_first(Result::is_ok), None); /// ``` fn find_or_first

(mut self, mut predicate: P) -> Option - 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 +2164,14 @@ pub trait Itertools : Iterator { /// /// #[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 @@ -1827,7 +2181,7 @@ pub trait Itertools : Iterator { where Self: Sized, Self::Item: Borrow, - Q: PartialEq, + Q: PartialEq + ?Sized, { self.any(|x| x.borrow() == query) } @@ -1849,8 +2203,9 @@ pub trait Itertools : Iterator { /// 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 +2213,38 @@ pub trait Itertools : Iterator { } } + /// 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 = None; + /// assert_eq!(data.into_iter().all_equal_value(), Err(None)); + /// ``` + #[allow(clippy::type_complexity)] + fn all_equal_value(&mut self) -> Result> + 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 +2262,9 @@ pub trait Itertools : Iterator { /// ``` #[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,20 +2273,21 @@ pub trait Itertools : Iterator { /// 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. /// /// ``` /// use itertools::Itertools; /// - /// let mut iter = "αβγ".chars().dropping(2); + /// let iter = "αβγ".chars().dropping(2); /// itertools::assert_equal(iter, "γ".chars()); /// ``` /// /// *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 +2311,8 @@ pub trait Itertools : Iterator { /// 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 +2320,6 @@ pub trait Itertools : Iterator { 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(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 +2334,10 @@ pub trait Itertools : Iterator { /// vec![1, 2, 3, 4, 5, 6]); /// ``` fn concat(self) -> Self::Item - where Self: Sized, - Self::Item: Extend<<::Item as IntoIterator>::Item> + IntoIterator + Default + where + Self: Sized, + Self::Item: + Extend<<::Item as IntoIterator>::Item> + IntoIterator + Default, { concat(self) } @@ -1980,7 +2346,8 @@ pub trait Itertools : Iterator { /// for convenience. #[cfg(feature = "use_alloc")] fn collect_vec(self) -> Vec - where Self: Sized + where + Self: Sized, { self.collect() } @@ -1996,16 +2363,18 @@ pub trait Itertools : Iterator { /// /// fn process_dir_entries(entries: &[fs::DirEntry]) { /// // ... + /// # let _ = entries; /// } /// - /// fn do_stuff() -> std::io::Result<()> { + /// fn do_stuff() -> io::Result<()> { /// let entries: Vec<_> = fs::read_dir(".")?.try_collect()?; /// process_dir_entries(&entries); /// /// Ok(()) /// } + /// + /// # let _ = do_stuff; /// ``` - #[cfg(feature = "use_alloc")] fn try_collect(self) -> Result where Self: Sized + Iterator>, @@ -2031,21 +2400,17 @@ pub trait Itertools : Iterator { /// ``` #[inline] fn set_from<'a, A: 'a, J>(&mut self, from: J) -> usize - where Self: Iterator, - J: IntoIterator + where + Self: Iterator, + J: IntoIterator, { - 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 +2422,8 @@ pub trait Itertools : Iterator { /// ``` #[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 +2457,8 @@ pub trait Itertools : Iterator { /// "1.10, 2.72, -3.00"); /// ``` fn format(self, sep: &str) -> Format - where Self: Sized, + where + Self: Sized, { format::new_format_default(self, sep) } @@ -2129,21 +2496,13 @@ pub trait Itertools : Iterator { /// /// ``` fn format_with(self, sep: &str, format: F) -> FormatWith - 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(&mut self, start: B, f: F) -> Result - where Self: Iterator>, - 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,11 +2517,14 @@ pub trait Itertools : Iterator { /// 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); /// accum = f(accum, 3); + /// # let _ = accum; /// ``` /// /// With a `start` value of 0 and an addition as folding function, @@ -2187,8 +2549,9 @@ pub trait Itertools : Iterator { /// ); /// ``` fn fold_ok(&mut self, mut start: B, mut f: F) -> Result - where Self: Iterator>, - F: FnMut(B, A) -> B + where + Self: Iterator>, + F: FnMut(B, A) -> B, { for elt in self { match elt { @@ -2219,8 +2582,9 @@ pub trait Itertools : Iterator { /// assert_eq!(more_values.next().unwrap(), Some(0)); /// ``` fn fold_options(&mut self, mut start: B, mut f: F) -> Option - where Self: Iterator>, - F: FnMut(B, A) -> B + where + Self: Iterator>, + F: FnMut(B, A) -> B, { for elt in self { match elt { @@ -2243,10 +2607,14 @@ pub trait Itertools : Iterator { /// 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(mut self, f: F) -> Option - 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 +2647,110 @@ pub trait Itertools : Iterator { /// └─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: + /// + /// For an iterator producing `n` elements, both [`Iterator::reduce`] and `tree_reduce` will + /// call `f` `n - 1` times. However, `tree_reduce` will call `f` on earlier intermediate + /// results, which is beneficial for `f` that allocate and produce longer results for longer + /// arguments. For example if `f` combines arguments using `format!`, then `tree_reduce` will + /// operate on average on shorter arguments resulting in less bytes being allocated overall. + /// + /// Moreover, the output of `tree_reduce` is preferable to that of [`Iterator::reduce`] in + /// certain cases. For example, building a binary search tree using `tree_reduce` will result in + /// a balanced tree with height `O(ln(n))`, while [`Iterator::reduce`] will output a tree with + /// height `O(n)`, essentially a linked list. + /// + /// If `f` does not benefit from such a reordering, 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. /// /// ``` /// use itertools::Itertools; /// + /// let f = |a: String, b: String| { + /// format!("f({a}, {b})") + /// }; + /// /// // 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)), - /// Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))"))); + /// assert_eq!((1..8).map(|x| x.to_string()).tree_reduce(f), + /// 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); + /// // Like reduce, an empty iterator produces None + /// assert_eq!((0..0).tree_reduce(|x, y| x * y), None); + /// + /// // tree_reduce matches reduce for associative operations... + /// assert_eq!((0..10).tree_reduce(|x, y| x + y), + /// (0..10).reduce(|x, y| x + y)); /// - /// // tree_fold1 matches fold1 for associative operations... - /// assert_eq!((0..10).tree_fold1(|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), - /// (0..10).fold1(|x, y| x - y)); + /// assert_ne!((0..10).tree_reduce(|x, y| x - y), + /// (0..10).reduce(|x, y| x - y)); + /// + /// let mut total_len_reduce = 0; + /// let reduce_res = (1..100).map(|x| x.to_string()) + /// .reduce(|a, b| { + /// let r = f(a, b); + /// total_len_reduce += r.len(); + /// r + /// }) + /// .unwrap(); + /// + /// let mut total_len_tree_reduce = 0; + /// let tree_reduce_res = (1..100).map(|x| x.to_string()) + /// .tree_reduce(|a, b| { + /// let r = f(a, b); + /// total_len_tree_reduce += r.len(); + /// r + /// }) + /// .unwrap(); + /// + /// assert_eq!(total_len_reduce, 33299); + /// assert_eq!(total_len_tree_reduce, 4228); + /// assert_eq!(reduce_res.len(), tree_reduce_res.len()); /// ``` - fn tree_fold1(mut self, mut f: F) -> Option - where F: FnMut(Self::Item, Self::Item) -> Self::Item, - Self: Sized, + fn tree_reduce(mut self, mut f: F) -> Option + where + F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized, { type State = Result>; fn inner0(it: &mut II, f: &mut FF) -> State - where - II: Iterator, - FF: FnMut(T, T) -> T + where + II: Iterator, + 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(stop: usize, it: &mut II, f: &mut FF) -> State - where - II: Iterator, - FF: FnMut(T, T) -> T + where + II: Iterator, + 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 +2765,22 @@ pub trait Itertools : Iterator { 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(self, f: F) -> Option + 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 +2821,19 @@ pub trait Itertools : Iterator { /// `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(&mut self, init: B, mut f: F) -> FoldWhile - where Self: Sized, - F: FnMut(B, Self::Item) -> FoldWhile + where + Self: Sized, + F: FnMut(B, Self::Item) -> FoldWhile, { - 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 +2864,11 @@ pub trait Itertools : Iterator { /// assert_eq!(nonempty_sum, Some(55)); /// ``` fn sum1(mut self) -> Option - where Self: Sized, - S: std::iter::Sum, + where + Self: Sized, + S: std::iter::Sum, { - 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 +2892,11 @@ pub trait Itertools : Iterator { /// assert_eq!(nonempty_product, Some(3628800)); /// ``` fn product1

(mut self) -> Option

- where Self: Sized, - P: std::iter::Product, + where + Self: Sized, + P: std::iter::Product, { - 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 +2905,8 @@ pub trait Itertools : Iterator { /// [`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 +2920,9 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn sorted_unstable(self) -> VecIntoIter - 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 +2937,8 @@ pub trait Itertools : Iterator { /// [`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 +2958,9 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn sorted_unstable_by(self, cmp: F) -> VecIntoIter - 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 +2973,8 @@ pub trait Itertools : Iterator { /// [`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 +2994,10 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn sorted_unstable_by_key(self, f: F) -> VecIntoIter - 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 +3010,8 @@ pub trait Itertools : Iterator { /// [`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 +3025,9 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn sorted(self) -> VecIntoIter - 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 +3042,8 @@ pub trait Itertools : Iterator { /// [`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 +3051,7 @@ pub trait Itertools : Iterator { /// 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 +3063,9 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn sorted_by(self, cmp: F) -> VecIntoIter - 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 +3078,8 @@ pub trait Itertools : Iterator { /// [`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 +3087,7 @@ pub trait Itertools : Iterator { /// 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 +3099,10 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn sorted_by_key(self, f: F) -> VecIntoIter - 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 +3116,8 @@ pub trait Itertools : Iterator { /// [`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 +3125,7 @@ pub trait Itertools : Iterator { /// 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 +3176,440 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_alloc")] fn k_smallest(self, k: usize) -> VecIntoIter - 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(self, k: usize, cmp: F) -> VecIntoIter + 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(self, k: usize, key: F) -> VecIntoIter + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord, + { + self.k_smallest_by(k, k_smallest::key_to_cmp(key)) + } + + /// Sort the k smallest elements into a new iterator, in ascending order, relaxing the amount of memory required. + /// + /// **Note:** This consumes the entire iterator, and returns the result + /// as a new iterator that owns its elements. If the input contains + /// less than k elements, the result is equivalent to `self.sorted()`. + /// + /// This is guaranteed to use `2 * k * sizeof(Self::Item) + O(1)` memory + /// and `O(n + k log k)` time, with `n` the number of elements in the input, + /// meaning it uses more memory than the minimum obtained by [`k_smallest`](Itertools::k_smallest) + /// but achieves linear time in the number of elements. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// **Note:** This is functionally-equivalent to `self.sorted().take(k)` + /// but much more efficient. + /// + /// ``` + /// 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_relaxed(5); + /// + /// itertools::assert_equal(five_smallest, 0..5); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_smallest_relaxed(self, k: usize) -> VecIntoIter + where + Self: Sized, + Self::Item: Ord, + { + self.k_smallest_relaxed_by(k, Ord::cmp) + } + + /// Sort the k smallest elements into a new iterator using the provided comparison, relaxing the amount of memory required. + /// + /// 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_relaxed`](Itertools::k_smallest_relaxed) corresponds to `self.sorted().take(k)`, + /// in both semantics and complexity. + /// + /// ``` + /// 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_relaxed_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_relaxed_by(self, k: usize, cmp: F) -> VecIntoIter + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + k_smallest::k_smallest_relaxed_general(self, k, cmp).into_iter() + } + + /// Return the elements producing the k smallest outputs of the provided function, relaxing the amount of memory required. + /// + /// 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_relaxed`](Itertools::k_smallest_relaxed) corresponds to `self.sorted().take(k)`, + /// in both semantics and complexity. + /// + /// ``` + /// 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_relaxed_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_relaxed_by_key(self, k: usize, key: F) -> VecIntoIter + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord, + { + self.k_smallest_relaxed_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 + 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(self, k: usize, mut cmp: F) -> VecIntoIter + 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(self, k: usize, key: F) -> VecIntoIter + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord, + { + self.k_largest_by(k, k_smallest::key_to_cmp(key)) + } + + /// Sort the k largest elements into a new iterator, in descending order, relaxing the amount of memory required. + /// + /// 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_relaxed`](Itertools::k_smallest_relaxed) + /// 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_relaxed(5); + /// + /// itertools::assert_equal(five_largest, vec![14, 13, 12, 11, 10]); + /// ``` + #[cfg(feature = "use_alloc")] + fn k_largest_relaxed(self, k: usize) -> VecIntoIter + where + Self: Sized, + Self::Item: Ord, + { + self.k_largest_relaxed_by(k, Self::Item::cmp) + } + + /// Sort the k largest elements into a new iterator using the provided comparison, relaxing the amount of memory required. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// Functionally equivalent to [`k_smallest_relaxed_by`](Itertools::k_smallest_relaxed_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_relaxed_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_relaxed_by(self, k: usize, mut cmp: F) -> VecIntoIter + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + self.k_smallest_relaxed_by(k, move |a, b| cmp(b, a)) + } + + /// Return the elements producing the k largest outputs of the provided function, relaxing the amount of memory required. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// Functionally equivalent to [`k_smallest_relaxed_by_key`](Itertools::k_smallest_relaxed_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_relaxed_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_relaxed_by_key(self, k: usize, key: F) -> VecIntoIter + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord, + { + self.k_largest_relaxed_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 + 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 +3634,11 @@ pub trait Itertools : Iterator { /// assert_eq!(failures, [false, true]); /// ``` fn partition_map(self, mut predicate: F) -> (A, B) - where Self: Sized, - F: FnMut(Self::Item) -> Either, - A: Default + Extend, - B: Default + Extend, + where + Self: Sized, + F: FnMut(Self::Item) -> Either, + A: Default + Extend, + B: Default + Extend, { let mut left = A::default(); let mut right = B::default(); @@ -2795,10 +3667,10 @@ pub trait Itertools : Iterator { /// assert_eq!(failures, [false, true]); /// ``` fn partition_result(self) -> (A, B) - where - Self: Iterator> + Sized, - A: Default + Extend, - B: Default + Extend, + where + Self: Iterator> + Sized, + A: Default + Extend, + B: Default + Extend, { self.partition_map(|r| match r { Ok(v) => Either::Left(v), @@ -2824,14 +3696,15 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_std")] fn into_group_map(self) -> HashMap> - where Self: Iterator + Sized, - K: Hash + Eq, + where + Self: Iterator + Sized, + K: Hash + Eq, { group_map::into_group_map(self) } - /// Return an `Iterator` on a `HashMap`. Keys mapped to `Vec`s of values. The key is specified - /// in the closure. + /// Return a `HashMap` of keys mapped to `Vec`s of values. The key is specified + /// in the closure. The values are taken from the input iterator. /// /// Essentially a shorthand for `.into_grouping_map_by(f).collect::>()`. /// @@ -2843,7 +3716,7 @@ pub trait Itertools : Iterator { /// let lookup: HashMap> = /// data.clone().into_iter().into_group_map_by(|a| a.0); /// - /// assert_eq!(lookup[&0], vec![(0,10),(0,20)]); + /// assert_eq!(lookup[&0], vec![(0,10), (0,20)]); /// assert_eq!(lookup.get(&1), None); /// assert_eq!(lookup[&2], vec![(2,12), (2,42)]); /// assert_eq!(lookup[&3], vec![(3,13), (3,33)]); @@ -2859,46 +3732,48 @@ pub trait Itertools : Iterator { /// ``` #[cfg(feature = "use_std")] fn into_group_map_by(self, f: F) -> HashMap> - where - Self: Iterator + Sized, - K: Hash + Eq, - F: Fn(&V) -> K, + where + Self: Iterator + 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(self) -> GroupingMap - where Self: Iterator + Sized, - K: Hash + Eq, + where + Self: Iterator + 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(self, key_mapper: F) -> GroupingMapBy - where Self: Iterator + Sized, - K: Hash + Eq, - F: FnMut(&V) -> K + where + Self: Iterator + 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 +3798,11 @@ pub trait Itertools : Iterator { /// /// 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 - 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 +3831,13 @@ pub trait Itertools : Iterator { /// /// 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(self, mut compare: F) -> Vec - 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 +3863,12 @@ pub trait Itertools : Iterator { /// /// 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(self, key: F) -> Vec - 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 +3895,11 @@ pub trait Itertools : Iterator { /// /// 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 - 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 +3928,16 @@ pub trait Itertools : Iterator { /// /// 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(self, mut compare: F) -> Vec - 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 +3960,12 @@ pub trait Itertools : Iterator { /// /// 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(self, key: F) -> Vec - 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 +4006,9 @@ pub trait Itertools : Iterator { /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. fn minmax(self) -> MinMaxResult - where Self: Sized, Self::Item: PartialOrd + where + Self: Sized, + Self::Item: PartialOrd, { minmax::minmax_impl(self, |_| (), |x, y, _, _| x < y) } @@ -3142,7 +4025,10 @@ pub trait Itertools : Iterator { /// The keys can be floats but no particular result is guaranteed /// if a key is NaN. fn minmax_by_key(self, key: F) -> MinMaxResult - 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 +4042,11 @@ pub trait Itertools : Iterator { /// the last maximal element wins. This matches the behavior of the standard /// [`Iterator::min`] and [`Iterator::max`] methods. fn minmax_by(self, mut compare: F) -> MinMaxResult - 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 +4069,9 @@ pub trait Itertools : Iterator { /// assert_eq!(a.iter().position_max(), Some(1)); /// ``` fn position_max(self) -> Option - 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 +4099,10 @@ pub trait Itertools : Iterator { /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(3)); /// ``` fn position_max_by_key(self, mut key: F) -> Option - 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 +4130,9 @@ pub trait Itertools : Iterator { /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(1)); /// ``` fn position_max_by(self, mut compare: F) -> Option - 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 +4159,9 @@ pub trait Itertools : Iterator { /// assert_eq!(a.iter().position_min(), Some(2)); /// ``` fn position_min(self) -> Option - 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 +4189,10 @@ pub trait Itertools : Iterator { /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(0)); /// ``` fn position_min_by_key(self, mut key: F) -> Option - 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 +4220,9 @@ pub trait Itertools : Iterator { /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(2)); /// ``` fn position_min_by(self, mut compare: F) -> Option - 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 +4272,11 @@ pub trait Itertools : Iterator { /// assert_eq!(a.iter().position_minmax(), MinMax(2, 1)); /// ``` fn position_minmax(self) -> MinMaxResult - 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 +4319,12 @@ pub trait Itertools : Iterator { /// /// [`position_minmax`]: Self::position_minmax fn position_minmax_by_key(self, mut key: F) -> MinMaxResult - 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 +4364,11 @@ pub trait Itertools : Iterator { /// /// [`position_minmax`]: Self::position_minmax fn position_minmax_by(self, mut compare: F) -> MinMaxResult - 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 +4398,18 @@ pub trait Itertools : Iterator { 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 +4431,13 @@ pub trait Itertools : Iterator { 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), } } @@ -3572,7 +4471,7 @@ pub trait Itertools : Iterator { /// # Examples /// ``` /// # use itertools::Itertools; - /// let counts = [1, 1, 1, 3, 3, 5].into_iter().counts(); + /// let counts = [1, 1, 1, 3, 3, 5].iter().counts(); /// assert_eq!(counts[&1], 3); /// assert_eq!(counts[&3], 2); /// assert_eq!(counts[&5], 1); @@ -3598,9 +4497,10 @@ pub trait Itertools : Iterator { /// # use itertools::Itertools; /// struct Character { /// first_name: &'static str, + /// # #[allow(dead_code)] /// last_name: &'static str, /// } - /// + /// /// let characters = /// vec![ /// Character { first_name: "Amy", last_name: "Pond" }, @@ -3611,12 +4511,12 @@ pub trait Itertools : Iterator { /// 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 +4533,11 @@ pub trait Itertools : Iterator { /// 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 +4557,33 @@ pub trait Itertools : Iterator { { 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 { + let sh = self.size_hint(); + match sh { + (lo, Some(hi)) if lo == hi => Ok(lo), + _ => Err(sh), + } + } } -impl Itertools for T where T: Iterator { } +impl 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 +4596,10 @@ impl Itertools for T where T: Iterator { } /// assert!(!itertools::equal(&[0, 0], &[0, 0, 0])); /// ``` pub fn equal(a: I, b: J) -> bool - where I: IntoIterator, - J: IntoIterator, - I::Item: PartialEq +where + I: IntoIterator, + J: IntoIterator, + I::Item: PartialEq, { a.into_iter().eq(b) } @@ -3683,31 +4608,39 @@ pub fn equal(a: I, b: J) -> bool /// 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'. /// ``` +#[track_caller] pub fn assert_equal(a: I, b: J) - where I: IntoIterator, - J: IntoIterator, - I::Item: fmt::Debug + PartialEq, - J::Item: fmt::Debug, +where + I: IntoIterator, + J: IntoIterator, + I::Item: fmt::Debug + PartialEq, + 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 +4665,18 @@ pub fn assert_equal(a: I, b: J) /// assert_eq!(split_index, 3); /// ``` pub fn partition<'a, A: 'a, I, F>(iter: I, mut pred: F) -> usize - where I: IntoIterator, - I::IntoIter: DoubleEndedIterator, - F: FnMut(&A) -> bool +where + I: IntoIterator, + 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 +4699,15 @@ impl FoldWhile { /// 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/third_party/rust/itertools/src/merge_join.rs b/third_party/rust/itertools/src/merge_join.rs index f2fbdea2c483..5f4a605fa251 100644 --- a/third_party/rust/itertools/src/merge_join.rs +++ b/third_party/rust/itertools/src/merge_join.rs @@ -1,151 +1,315 @@ 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 = MergeBy; + +/// 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 */ +/// # let _ = elt; +/// } +/// ``` +pub fn merge( + i: I, + j: J, +) -> Merge<::IntoIter, ::IntoIter> +where + I: IntoIterator, + J: IntoIterator, + 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 { + left: PutBack>, + right: PutBack>, + cmp_fn: F, +} + +/// Create a `MergeBy` iterator. +pub fn merge_by_new(a: I, b: J, cmp: F) -> MergeBy +where + I: IntoIterator, + J: IntoIterator, +{ + 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(left: I, right: J, cmp_fn: F) - -> MergeJoinBy - where I: IntoIterator, - J: IntoIterator, - F: FnMut(&I::Item, &J::Item) -> Ordering +pub fn merge_join_by( + left: I, + right: J, + cmp_fn: F, +) -> MergeJoinBy +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 { - left: PutBack>, - right: PutBack>, - cmp_fn: F +pub type MergeJoinBy = + MergeBy::Item, ::Item>>::T>>; + +#[derive(Clone, Debug)] +pub struct MergeFuncLR(F, PhantomData); + +pub trait FuncLR { + type T; } -impl Clone for MergeJoinBy - where I: Iterator, - J: Iterator, - PutBack>: Clone, - PutBack>: Clone, - F: Clone, -{ - clone_fields!(left, right, cmp_fn); +impl T> FuncLR for F { + type T = T; } -impl fmt::Debug for MergeJoinBy - 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 { + type MergeResult; + fn left(left: L) -> Self::MergeResult; + fn right(right: R) -> Self::MergeResult; + // "merge" never returns (Some(...), Some(...), ...) so Option> + // 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>, Self::MergeResult); + fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint; } -impl Iterator for MergeJoinBy - where I: Iterator, - J: Iterator, - F: FnMut(&I::Item, &J::Item) -> Ordering -{ - type Item = EitherOrBoth; - - fn next(&mut self) -> Option { - 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 Ordering> OrderingOrBool for MergeFuncLR { + type MergeResult = EitherOrBoth; + 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>, 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) { - 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 bool> OrderingOrBool for MergeFuncLR { + type MergeResult = Either; + 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>, 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 bool> OrderingOrBool 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>, 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 OrderingOrBool 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>, 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 Clone for MergeBy +where + I: Iterator, + J: Iterator, + PutBack>: Clone, + PutBack>: Clone, + F: Clone, +{ + clone_fields!(left, right, cmp_fn); +} + +impl fmt::Debug for MergeBy +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug, + J: Iterator + fmt::Debug, + J::Item: fmt::Debug, +{ + debug_fmt_fields!(MergeBy, left, right); +} + +impl Iterator for MergeBy +where + I: Iterator, + J: Iterator, + F: OrderingOrBool, +{ + type Item = F::MergeResult; + + fn next(&mut self) -> Option { + 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 { - let mut previous_element = None; + fn fold(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 { @@ -156,14 +320,29 @@ impl Iterator for MergeJoinBy 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 FusedIterator for MergeBy +where + I: Iterator, + J: Iterator, + F: OrderingOrBool, +{ +} diff --git a/third_party/rust/itertools/src/minmax.rs b/third_party/rust/itertools/src/minmax.rs index 52b2f115ddd8..5c9674e01124 100644 --- a/third_party/rust/itertools/src/minmax.rs +++ b/third_party/rust/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 { /// Empty iterator NoElements, @@ -12,7 +11,7 @@ pub enum MinMaxResult { /// More than one element in the iterator, the first element is not larger /// than the second - MinMax(T, T) + MinMax(T, T), } impl MinMaxResult { @@ -36,34 +35,36 @@ impl MinMaxResult { /// 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(mut it: I, mut key_for: F, - mut lt: L) -> MinMaxResult - where I: Iterator, - F: FnMut(&I::Item) -> K, - L: FnMut(&I::Item, &I::Item, &K, &K) -> bool, +pub fn minmax_impl(mut it: I, mut key_for: F, mut lt: L) -> MinMaxResult +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 @@ pub fn minmax_impl(mut it: I, mut key_for: F, // 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 @@ pub fn minmax_impl(mut it: I, mut key_for: F, } break; } - Some(x) => x + Some(x) => x, }; let first_key = key_for(&first); let second_key = key_for(&second); diff --git a/third_party/rust/itertools/src/multipeek_impl.rs b/third_party/rust/itertools/src/multipeek_impl.rs index 8b49c695eb0a..6f800b6fb6c9 100644 --- a/third_party/rust/itertools/src/multipeek_impl.rs +++ b/third_party/rust/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 - where I: Iterator +where + I: Iterator, { iter: Fuse, buf: VecDeque, @@ -20,7 +22,8 @@ pub struct MultiPeek /// /// [`IntoIterator`] enabled version of [`Itertools::multipeek`]. pub fn multipeek(iterable: I) -> MultiPeek - where I: IntoIterator +where + I: IntoIterator, { MultiPeek { iter: iterable.into_iter().fuse(), @@ -30,7 +33,8 @@ pub fn multipeek(iterable: I) -> MultiPeek } impl MultiPeek - where I: Iterator +where + I: Iterator, { /// Reset the peeking “cursor” pub fn reset_peek(&mut self) { @@ -62,24 +66,31 @@ impl MultiPeek { } impl PeekingNext for MultiPeek - where I: Iterator, +where + I: Iterator, { fn peeking_next(&mut self, accept: F) -> Option - 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.front() { + if !accept(r) { + return None; } - } else if let Some(r) = self.buf.get(0) { - if !accept(r) { return None } } self.next() } } impl Iterator for MultiPeek - where I: Iterator +where + I: Iterator, { type Item = I::Item; @@ -91,11 +102,15 @@ impl Iterator for MultiPeek fn size_hint(&self) -> (usize, Option) { size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) } + + fn fold(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 ExactSizeIterator for MultiPeek - where I: ExactSizeIterator -{} - - +impl ExactSizeIterator for MultiPeek where I: ExactSizeIterator {} diff --git a/third_party/rust/itertools/src/next_array.rs b/third_party/rust/itertools/src/next_array.rs new file mode 100644 index 000000000000..86480b19740f --- /dev/null +++ b/third_party/rust/itertools/src/next_array.rs @@ -0,0 +1,269 @@ +use core::mem::{self, MaybeUninit}; + +/// An array of at most `N` elements. +struct ArrayBuilder { + /// The (possibly uninitialized) elements of the `ArrayBuilder`. + /// + /// # Safety + /// + /// The elements of `arr[..len]` are valid `T`s. + arr: [MaybeUninit; N], + + /// The number of leading elements of `arr` that are valid `T`s, len <= N. + len: usize, +} + +impl ArrayBuilder { + /// Initializes a new, empty `ArrayBuilder`. + pub fn new() -> Self { + // SAFETY: The safety invariant of `arr` trivially holds for `len = 0`. + Self { + arr: [(); N].map(|_| MaybeUninit::uninit()), + len: 0, + } + } + + /// Pushes `value` onto the end of the array. + /// + /// # Panics + /// + /// This panics if `self.len >= N`. + #[inline(always)] + pub fn push(&mut self, value: T) { + // PANICS: This will panic if `self.len >= N`. + let place = &mut self.arr[self.len]; + // SAFETY: The safety invariant of `self.arr` applies to elements at + // indices `0..self.len` — not to the element at `self.len`. Writing to + // the element at index `self.len` therefore does not violate the safety + // invariant of `self.arr`. Even if this line panics, we have not + // created any intermediate invalid state. + *place = MaybeUninit::new(value); + // Lemma: `self.len < N`. By invariant, `self.len <= N`. Above, we index + // into `self.arr`, which has size `N`, at index `self.len`. If `self.len == N` + // at that point, that index would be out-of-bounds, and the index + // operation would panic. Thus, `self.len != N`, and since `self.len <= N`, + // that means that `self.len < N`. + // + // PANICS: Since `self.len < N`, and since `N <= usize::MAX`, + // `self.len + 1 <= usize::MAX`, and so `self.len += 1` will not + // overflow. Overflow is the only panic condition of `+=`. + // + // SAFETY: + // - We are required to uphold the invariant that `self.len <= N`. + // Since, by the preceding lemma, `self.len < N` at this point in the + // code, `self.len += 1` results in `self.len <= N`. + // - We are required to uphold the invariant that `self.arr[..self.len]` + // are valid instances of `T`. Since this invariant already held when + // this method was called, and since we only increment `self.len` + // by 1 here, we only need to prove that the element at + // `self.arr[self.len]` (using the value of `self.len` before incrementing) + // is valid. Above, we construct `place` to point to `self.arr[self.len]`, + // and then initialize `*place` to `MaybeUninit::new(value)`, which is + // a valid `T` by construction. + self.len += 1; + } + + /// Consumes the elements in the `ArrayBuilder` and returns them as an array + /// `[T; N]`. + /// + /// If `self.len() < N`, this returns `None`. + pub fn take(&mut self) -> Option<[T; N]> { + if self.len == N { + // SAFETY: Decreasing the value of `self.len` cannot violate the + // safety invariant on `self.arr`. + self.len = 0; + + // SAFETY: Since `self.len` is 0, `self.arr` may safely contain + // uninitialized elements. + let arr = mem::replace(&mut self.arr, [(); N].map(|_| MaybeUninit::uninit())); + + Some(arr.map(|v| { + // SAFETY: We know that all elements of `arr` are valid because + // we checked that `len == N`. + unsafe { v.assume_init() } + })) + } else { + None + } + } +} + +impl AsMut<[T]> for ArrayBuilder { + fn as_mut(&mut self) -> &mut [T] { + let valid = &mut self.arr[..self.len]; + // SAFETY: By invariant on `self.arr`, the elements of `self.arr` at + // indices `0..self.len` are in a valid state. Since `valid` references + // only these elements, the safety precondition of + // `slice_assume_init_mut` is satisfied. + unsafe { slice_assume_init_mut(valid) } + } +} + +impl Drop for ArrayBuilder { + // We provide a non-trivial `Drop` impl, because the trivial impl would be a + // no-op; `MaybeUninit` has no innate awareness of its own validity, and + // so it can only forget its contents. By leveraging the safety invariant of + // `self.arr`, we do know which elements of `self.arr` are valid, and can + // selectively run their destructors. + fn drop(&mut self) { + // SAFETY: + // - by invariant on `&mut [T]`, `self.as_mut()` is: + // - valid for reads and writes + // - properly aligned + // - non-null + // - the dropped `T` are valid for dropping; they do not have any + // additional library invariants that we've violated + // - no other pointers to `valid` exist (since we're in the context of + // `drop`) + unsafe { core::ptr::drop_in_place(self.as_mut()) } + } +} + +/// Assuming all the elements are initialized, get a mutable slice to them. +/// +/// # Safety +/// +/// The caller guarantees that the elements `T` referenced by `slice` are in a +/// valid state. +unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [T] { + // SAFETY: Casting `&mut [MaybeUninit]` to `&mut [T]` is sound, because + // `MaybeUninit` is guaranteed to have the same size, alignment and ABI + // as `T`, and because the caller has guaranteed that `slice` is in the + // valid state. + unsafe { &mut *(slice as *mut [MaybeUninit] as *mut [T]) } +} + +/// Equivalent to `it.next_array()`. +pub(crate) fn next_array(it: &mut I) -> Option<[I::Item; N]> +where + I: Iterator, +{ + let mut builder = ArrayBuilder::new(); + for _ in 0..N { + builder.push(it.next()?); + } + builder.take() +} + +#[cfg(test)] +mod test { + use super::ArrayBuilder; + + #[test] + fn zero_len_take() { + let mut builder = ArrayBuilder::<(), 0>::new(); + let taken = builder.take(); + assert_eq!(taken, Some([(); 0])); + } + + #[test] + #[should_panic] + fn zero_len_push() { + let mut builder = ArrayBuilder::<(), 0>::new(); + builder.push(()); + } + + #[test] + fn push_4() { + let mut builder = ArrayBuilder::<(), 4>::new(); + assert_eq!(builder.take(), None); + + builder.push(()); + assert_eq!(builder.take(), None); + + builder.push(()); + assert_eq!(builder.take(), None); + + builder.push(()); + assert_eq!(builder.take(), None); + + builder.push(()); + assert_eq!(builder.take(), Some([(); 4])); + } + + #[test] + fn tracked_drop() { + use std::panic::{catch_unwind, AssertUnwindSafe}; + use std::sync::atomic::{AtomicU16, Ordering}; + + static DROPPED: AtomicU16 = AtomicU16::new(0); + + #[derive(Debug, PartialEq)] + struct TrackedDrop; + + impl Drop for TrackedDrop { + fn drop(&mut self) { + DROPPED.fetch_add(1, Ordering::Relaxed); + } + } + + { + let builder = ArrayBuilder::::new(); + assert_eq!(DROPPED.load(Ordering::Relaxed), 0); + drop(builder); + assert_eq!(DROPPED.load(Ordering::Relaxed), 0); + } + + { + let mut builder = ArrayBuilder::::new(); + builder.push(TrackedDrop); + assert_eq!(builder.take(), None); + assert_eq!(DROPPED.load(Ordering::Relaxed), 0); + drop(builder); + assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 1); + } + + { + let mut builder = ArrayBuilder::::new(); + builder.push(TrackedDrop); + builder.push(TrackedDrop); + assert!(matches!(builder.take(), Some(_))); + assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 2); + drop(builder); + assert_eq!(DROPPED.load(Ordering::Relaxed), 0); + } + + { + let mut builder = ArrayBuilder::::new(); + + builder.push(TrackedDrop); + builder.push(TrackedDrop); + + assert!(catch_unwind(AssertUnwindSafe(|| { + builder.push(TrackedDrop); + })) + .is_err()); + + assert_eq!(DROPPED.load(Ordering::Relaxed), 1); + + drop(builder); + + assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 3); + } + + { + let mut builder = ArrayBuilder::::new(); + + builder.push(TrackedDrop); + builder.push(TrackedDrop); + + assert!(catch_unwind(AssertUnwindSafe(|| { + builder.push(TrackedDrop); + })) + .is_err()); + + assert_eq!(DROPPED.load(Ordering::Relaxed), 1); + + assert!(matches!(builder.take(), Some(_))); + + assert_eq!(DROPPED.load(Ordering::Relaxed), 3); + + builder.push(TrackedDrop); + builder.push(TrackedDrop); + + assert!(matches!(builder.take(), Some(_))); + + assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 5); + } + } +} diff --git a/third_party/rust/itertools/src/pad_tail.rs b/third_party/rust/itertools/src/pad_tail.rs index 248a4324368a..5595b42bacf2 100644 --- a/third_party/rust/itertools/src/pad_tail.rs +++ b/third_party/rust/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 @@ where /// Create a new `PadUsing` iterator. pub fn pad_using(iter: I, min: usize, filler: F) -> PadUsing - where I: Iterator, - F: FnMut(usize) -> I::Item +where + I: Iterator, + F: FnMut(usize) -> I::Item, { PadUsing { iter: iter.fuse(), @@ -37,8 +38,9 @@ pub fn pad_using(iter: I, min: usize, filler: F) -> PadUsing } impl Iterator for PadUsing - where I: Iterator, - F: FnMut(usize) -> I::Item +where + I: Iterator, + F: FnMut(usize) -> I::Item, { type Item = I::Item; @@ -53,7 +55,7 @@ impl Iterator for PadUsing } else { None } - }, + } e => { self.pos += 1; e @@ -65,11 +67,24 @@ impl Iterator for PadUsing let tail = self.min.saturating_sub(self.pos); size_hint::max(self.iter.size_hint(), (tail, Some(tail))) } + + fn fold(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 DoubleEndedIterator for PadUsing - where I: DoubleEndedIterator + ExactSizeIterator, - F: FnMut(usize) -> I::Item +where + I: DoubleEndedIterator + ExactSizeIterator, + F: FnMut(usize) -> I::Item, { fn next_back(&mut self) -> Option { if self.min == 0 { @@ -82,15 +97,28 @@ impl DoubleEndedIterator for PadUsing Some((self.filler)(self.min)) } } + + fn rfold(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 ExactSizeIterator for PadUsing - where I: ExactSizeIterator, - F: FnMut(usize) -> I::Item -{} - +where + I: ExactSizeIterator, + F: FnMut(usize) -> I::Item, +{ +} impl FusedIterator for PadUsing - where I: FusedIterator, - F: FnMut(usize) -> I::Item -{} +where + I: FusedIterator, + F: FnMut(usize) -> I::Item, +{ +} diff --git a/third_party/rust/itertools/src/peek_nth.rs b/third_party/rust/itertools/src/peek_nth.rs index bcca45838eb5..b03a3ef5f2a7 100644 --- a/third_party/rust/itertools/src/peek_nth.rs +++ b/third_party/rust/itertools/src/peek_nth.rs @@ -5,6 +5,7 @@ use std::iter::Fuse; /// See [`peek_nth()`] for more information. #[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct PeekNth where I: Iterator, @@ -34,30 +35,35 @@ impl PeekNth 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 @@ where 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 { + 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(&mut self, expected: &T) -> Option + where + T: ?Sized, + I::Item: PartialEq, + { + self.next_if(|next| next == expected) + } } impl Iterator for PeekNth @@ -84,6 +152,14 @@ where fn size_hint(&self) -> (usize, Option) { size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) } + + fn fold(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 ExactSizeIterator for PeekNth where I: ExactSizeIterator {} diff --git a/third_party/rust/itertools/src/peeking_take_while.rs b/third_party/rust/itertools/src/peeking_take_while.rs index b3a9c5ccb465..f3259a919013 100644 --- a/third_party/rust/itertools/src/peeking_take_while.rs +++ b/third_party/rust/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 @@ use crate::PutBackN; /// /// 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(&mut self, accept: F) -> Option - where F: FnOnce(&Self::Item) -> bool; + where + Self: Sized, + F: FnOnce(&Self::Item) -> bool; +} + +impl PeekingNext for &mut I +where + I: PeekingNext, +{ + fn peeking_next(&mut self, accept: F) -> Option + where + F: FnOnce(&Self::Item) -> bool, + { + (*self).peeking_next(accept) + } } impl PeekingNext for Peekable - where I: Iterator, +where + I: Iterator, { fn peeking_next(&mut self, accept: F) -> Option - 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 PeekingNext for Peekable } impl PeekingNext for PutBack - where I: Iterator, +where + I: Iterator, { fn peeking_next(&mut self, accept: F) -> Option - 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 @@ impl PeekingNext for PutBack #[cfg(feature = "use_alloc")] impl PeekingNext for PutBackN - where I: Iterator, +where + I: Iterator, { fn peeking_next(&mut self, accept: F) -> Option - 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 PeekingNext for PutBackN } } +impl PeekingNext for RepeatN { + fn peeking_next(&mut self, accept: F) -> Option + 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(iter: &mut I, f: F) -> PeekingTakeWhile - 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, - +impl Iterator for PeekingTakeWhile<'_, I, F> +where + I: PeekingNext, + F: FnMut(&I::Item) -> bool, { type Item = I::Item; fn next(&mut self) -> Option { @@ -115,6 +148,20 @@ impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F> } } +impl PeekingNext for PeekingTakeWhile<'_, I, F> +where + I: PeekingNext, + F: FnMut(&I::Item) -> bool, +{ + fn peeking_next(&mut self, g: G) -> Option + 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 @@ peeking_next_by_clone! { ['a, T] alloc::collections::vec_deque::Iter<'a, T> } // 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 } +::std::iter::Rev } diff --git a/third_party/rust/itertools/src/permutations.rs b/third_party/rust/itertools/src/permutations.rs index d03b852626cf..91389a73a752 100644 --- a/third_party/rust/itertools/src/permutations.rs +++ b/third_party/rust/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 @@ pub struct Permutations { } impl Clone for Permutations - 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, - cycles: Vec, - } -} - -enum CompleteStateRemaining { - Known(usize), - Overflow, + /// No permutation left to generate. + End, } impl fmt::Debug for Permutations - 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(iter: I, k: usize) -> Permutations { - 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 Iterator for Permutations where I: Iterator, - I::Item: Clone + I::Item: Clone, { type Item = Vec; fn next(&mut self) -> Option { - 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) { - 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 Permutations +impl FusedIterator for Permutations 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 }; +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 +} - // 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 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) }; - } -} - -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 remaining(&self) -> CompleteStateRemaining { - use self::CompleteStateRemaining::{Known, Overflow}; - match *self { - CompleteState::Start { n, k } => { - if n < k { - return Known(0); - } - - let count: Option = (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 - } - } - 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) + (count.unwrap_or(usize::MAX), count) } + Self::End => (0, Some(0)), } } } diff --git a/third_party/rust/itertools/src/powerset.rs b/third_party/rust/itertools/src/powerset.rs index 4d7685b12a87..734eaf6149ac 100644 --- a/third_party/rust/itertools/src/powerset.rs +++ b/third_party/rust/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 @@ use super::size_hint; #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Powerset { combs: Combinations, - // Iterator `position` (equal to count of yielded elements). - pos: usize, } impl Clone for Powerset - where I: Clone + Iterator, - I::Item: Clone, +where + I: Clone + Iterator, + I::Item: Clone, { - clone_fields!(combs, pos); + clone_fields!(combs); } impl fmt::Debug for Powerset - 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(src: I) -> Powerset - where I: Iterator, - I::Item: Clone, +where + I: Iterator, + I::Item: Clone, { Powerset { combs: combinations(src, 0), - pos: 0, + } +} + +impl Powerset { + /// 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 Iterator for Powerset - where - I: Iterator, - I::Item: Clone, +where + I: Iterator, + I::Item: Clone, { type Item = Vec; fn next(&mut self) -> Option { 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) { - // 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 { + 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(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 FusedIterator for Powerset - where - I: Iterator, - I::Item: Clone, -{} +where + I: Iterator, + I::Item: Clone, +{ +} + +fn remaining_for(n: usize, k: usize) -> Option { + (k + 1..=n).try_fold(0usize, |sum, i| sum.checked_add(checked_binomial(n, i)?)) +} diff --git a/third_party/rust/itertools/src/process_results_impl.rs b/third_party/rust/itertools/src/process_results_impl.rs index 44308f378094..31389c5fda8b 100644 --- a/third_party/rust/itertools/src/process_results_impl.rs +++ b/third_party/rust/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 @@ pub struct ProcessResults<'a, I, E: 'a> { iter: I, } -impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E> - where I: Iterator> -{ - type Item = T; - - fn next(&mut self) -> Option { - match self.iter.next() { +impl ProcessResults<'_, I, E> { + #[inline(always)] + fn next_body(&mut self, item: Option>) -> Option { + match item { Some(Ok(x)) => Some(x), Some(Err(e)) => { *self.error = Err(e); @@ -26,6 +25,18 @@ impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E> None => None, } } +} + +impl Iterator for ProcessResults<'_, I, E> +where + I: Iterator>, +{ + type Item = T; + + fn next(&mut self) -> Option { + let item = self.iter.next(); + self.next_body(item) + } fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) @@ -49,49 +60,49 @@ impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E> } } +impl DoubleEndedIterator for ProcessResults<'_, I, E> +where + I: Iterator>, + I: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + let item = self.iter.next_back(); + self.next_body(item) + } + + fn rfold(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` 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; -/// -/// let first_values: Vec = vec![Ok(1), Ok(0), Ok(3)]; -/// let second_values: Vec = 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(iterable: I, processor: F) -> Result - where I: IntoIterator>, - F: FnOnce(ProcessResults) -> R +where + I: IntoIterator>, + F: FnOnce(ProcessResults) -> 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/third_party/rust/itertools/src/put_back_n_impl.rs b/third_party/rust/itertools/src/put_back_n_impl.rs index 60ea8e64950a..a9eb4179c49a 100644 --- a/third_party/rust/itertools/src/put_back_n_impl.rs +++ b/third_party/rust/itertools/src/put_back_n_impl.rs @@ -7,6 +7,7 @@ use crate::size_hint; /// /// Iterator element type is `I::Item`. #[derive(Debug, Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct PutBackN { top: Vec, iter: I, @@ -17,7 +18,8 @@ pub struct PutBackN { /// /// Iterator element type is `I::Item`. pub fn put_back_n(iterable: I) -> PutBackN - where I: IntoIterator +where + I: IntoIterator, { PutBackN { top: Vec::new(), @@ -26,7 +28,8 @@ pub fn put_back_n(iterable: I) -> PutBackN } impl PutBackN { - /// 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 @@ impl Iterator for PutBackN { fn size_hint(&self) -> (usize, Option) { size_hint::add_scalar(self.iter.size_hint(), self.top.len()) } -} + fn fold(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/third_party/rust/itertools/src/rciter_impl.rs b/third_party/rust/itertools/src/rciter_impl.rs index 7298350a881c..96a0fd69c5bf 100644 --- a/third_party/rust/itertools/src/rciter_impl.rs +++ b/third_party/rust/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>`, that implements the `Iterator` trait. #[derive(Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct RcIter { /// The boxed iterator. pub rciter: Rc>, @@ -45,9 +45,12 @@ pub struct RcIter { /// `.next()`, i.e. if it somehow participates in an “iterator knot” /// where it is an adaptor of itself. pub fn rciter(iterable: I) -> RcIter - 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 Clone for RcIter { @@ -55,7 +58,8 @@ impl Clone for RcIter { } impl Iterator for RcIter - where I: Iterator +where + I: Iterator, { type Item = A; #[inline] @@ -73,7 +77,8 @@ impl Iterator for RcIter } impl DoubleEndedIterator for RcIter - where I: DoubleEndedIterator +where + I: DoubleEndedIterator, { #[inline] fn next_back(&mut self) -> Option { @@ -82,8 +87,9 @@ impl DoubleEndedIterator for RcIter } /// Return an iterator from `&RcIter` (by simply cloning it). -impl<'a, I> IntoIterator for &'a RcIter - where I: Iterator +impl IntoIterator for &RcIter +where + I: Iterator, { type Item = I::Item; type IntoIter = RcIter; @@ -93,7 +99,4 @@ impl<'a, I> IntoIterator for &'a RcIter } } - -impl FusedIterator for RcIter - where I: FusedIterator -{} +impl FusedIterator for RcIter where I: FusedIterator {} diff --git a/third_party/rust/itertools/src/repeatn.rs b/third_party/rust/itertools/src/repeatn.rs index e025f6f6a580..d86ad9facd33 100644 --- a/third_party/rust/itertools/src/repeatn.rs +++ b/third_party/rust/itertools/src/repeatn.rs @@ -6,23 +6,28 @@ use std::iter::FusedIterator; #[must_use = "iterators are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] pub struct RepeatN { - elt: Option, + pub(crate) elt: Option, n: usize, } /// Create an iterator that produces `n` repetitions of `element`. pub fn repeat_n(element: A, n: usize) -> RepeatN - 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 Iterator for RepeatN - where A: Clone +where + A: Clone, { type Item = A; @@ -39,21 +44,40 @@ impl Iterator for RepeatN fn size_hint(&self) -> (usize, Option) { (self.n, Some(self.n)) } + + fn fold(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 DoubleEndedIterator for RepeatN - where A: Clone +where + A: Clone, { #[inline] fn next_back(&mut self) -> Option { self.next() } + + #[inline] + fn rfold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.fold(init, f) + } } -impl ExactSizeIterator for RepeatN - where A: Clone -{} +impl ExactSizeIterator for RepeatN where A: Clone {} -impl FusedIterator for RepeatN - where A: Clone -{} +impl FusedIterator for RepeatN where A: Clone {} diff --git a/third_party/rust/itertools/src/size_hint.rs b/third_party/rust/itertools/src/size_hint.rs index 71ea1412b536..6cfead7f2b42 100644 --- a/third_party/rust/itertools/src/size_hint.rs +++ b/third_party/rust/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); @@ -31,7 +29,6 @@ pub fn add_scalar(sh: SizeHint, x: usize) -> SizeHint { /// 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 @@ pub fn sub_scalar(sh: SizeHint, x: usize) -> SizeHint { (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 @@ pub fn mul_scalar(sh: SizeHint, x: usize) -> SizeHint { (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 @@ pub fn min(a: SizeHint, b: SizeHint) -> SizeHint { }; (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/third_party/rust/itertools/src/sources.rs b/third_party/rust/itertools/src/sources.rs index 3877ce3c8b38..c405ffdc7196 100644 --- a/third_party/rust/itertools/src/sources.rs +++ b/third_party/rust/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, -} - -impl fmt::Debug for RepeatCall -{ - 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(function: F) -> RepeatCall - where F: FnMut() -> A -{ - RepeatCall { f: function } -} - -impl Iterator for RepeatCall - where F: FnMut() -> A -{ - type Item = A; - - #[inline] - fn next(&mut self) -> Option { - Some((self.f)()) - } - - fn size_hint(&self) -> (usize, Option) { - (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 @@ impl Iterator for RepeatCall /// 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(initial_state: St, f: F) -> Unfold - where F: FnMut(&mut St) -> Option +where + F: FnMut(&mut St) -> Option, { Unfold { f, @@ -107,7 +56,8 @@ pub fn unfold(initial_state: St, f: F) -> Unfold } impl fmt::Debug for Unfold - where St: fmt::Debug, +where + St: fmt::Debug, { debug_fmt_fields!(Unfold, state); } @@ -115,6 +65,10 @@ impl fmt::Debug for Unfold /// 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 { f: F, /// Internal state that will be passed to the closure on the next iteration @@ -122,7 +76,8 @@ pub struct Unfold { } impl Iterator for Unfold - where F: FnMut(&mut St) -> Option +where + F: FnMut(&mut St) -> Option, { type Item = A; @@ -144,13 +99,15 @@ pub struct Iterate { } impl fmt::Debug for Iterate - where St: fmt::Debug, +where + St: fmt::Debug, { debug_fmt_fields!(Iterate, state); } impl Iterator for Iterate - where F: FnMut(&St) -> St +where + F: FnMut(&St) -> St, { type Item = St; @@ -162,7 +119,7 @@ impl Iterator for Iterate #[inline] fn size_hint(&self) -> (usize, Option) { - (usize::max_value(), None) + (usize::MAX, None) } } @@ -171,10 +128,23 @@ impl Iterator for Iterate /// ``` /// 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(initial_value: St, f: F) -> Iterate - where F: FnMut(&St) -> St +where + F: FnMut(&St) -> St, { Iterate { state: initial_value, diff --git a/third_party/rust/itertools/src/take_while_inclusive.rs b/third_party/rust/itertools/src/take_while_inclusive.rs new file mode 100644 index 000000000000..420da9847af9 --- /dev/null +++ b/third_party/rust/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 { + iter: I, + predicate: F, + done: bool, +} + +impl TakeWhileInclusive +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 fmt::Debug for TakeWhileInclusive +where + I: Iterator + fmt::Debug, +{ + debug_fmt_fields!(TakeWhileInclusive, iter, done); +} + +impl Iterator for TakeWhileInclusive +where + I: Iterator, + F: FnMut(&I::Item) -> bool, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + if self.done { + None + } else { + self.iter.next().map(|item| { + if !(self.predicate)(&item) { + self.done = true; + } + item + }) + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.done { + (0, Some(0)) + } else { + (0, self.iter.size_hint().1) + } + } + + fn 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 FusedIterator for TakeWhileInclusive +where + I: Iterator, + F: FnMut(&I::Item) -> bool, +{ +} diff --git a/third_party/rust/itertools/src/tee.rs b/third_party/rust/itertools/src/tee.rs index ea4752906f05..0984c5de9639 100644 --- a/third_party/rust/itertools/src/tee.rs +++ b/third_party/rust/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 @@ struct TeeBuffer { #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Debug)] pub struct Tee - where I: Iterator +where + I: Iterator, { rcbuffer: Rc>>, id: bool, } pub fn new(iter: I) -> (Tee, Tee) - 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 Iterator for Tee - where I: Iterator, - I::Item: Clone +where + I: Iterator, + I::Item: Clone, { type Item = I::Item; fn next(&mut self) -> Option { @@ -73,6 +86,8 @@ impl Iterator for Tee } impl ExactSizeIterator for Tee - where I: ExactSizeIterator, - I::Item: Clone -{} +where + I: ExactSizeIterator, + I::Item: Clone, +{ +} diff --git a/third_party/rust/itertools/src/tuple_impl.rs b/third_party/rust/itertools/src/tuple_impl.rs index 06b5c13cb34f..c0d556fc95b1 100644 --- a/third_party/rust/itertools/src/tuple_impl.rs +++ b/third_party/rust/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 @@ use std::marker::PhantomData; // 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 HomogeneousTuple for T {} @@ -24,25 +22,25 @@ impl HomogeneousTuple for T {} /// [`Tuples::into_buffer()`]. #[derive(Clone, Debug)] pub struct TupleBuffer - where T: HomogeneousTuple +where + T: HomogeneousTuple, { cur: usize, buf: T::Buffer, } impl TupleBuffer - where T: HomogeneousTuple +where + T: HomogeneousTuple, { fn new(buf: T::Buffer) -> Self { - TupleBuffer { - cur: 0, - buf, - } + Self { cur: 0, buf } } } impl Iterator for TupleBuffer - where T: HomogeneousTuple +where + T: HomogeneousTuple, { type Item = T::Item; @@ -61,18 +59,16 @@ impl Iterator for TupleBuffer 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 ExactSizeIterator for TupleBuffer - where T: HomogeneousTuple -{ -} +impl ExactSizeIterator for TupleBuffer where T: HomogeneousTuple {} /// An iterator that groups the items in tuples of a specific size. /// @@ -80,8 +76,9 @@ impl ExactSizeIterator for TupleBuffer #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Tuples - where I: Iterator, - T: HomogeneousTuple +where + I: Iterator, + T: HomogeneousTuple, { iter: Fuse, buf: T::Buffer, @@ -89,8 +86,9 @@ pub struct Tuples /// Create a new tuples iterator. pub fn tuples(iter: I) -> Tuples - where I: Iterator, - T: HomogeneousTuple +where + I: Iterator, + T: HomogeneousTuple, { Tuples { iter: iter.fuse(), @@ -99,19 +97,50 @@ pub fn tuples(iter: I) -> Tuples } impl Iterator for Tuples - where I: Iterator, - T: HomogeneousTuple +where + I: Iterator, + T: HomogeneousTuple, { type Item = T; fn next(&mut self) -> Option { T::collect_from_iter(&mut self.iter, &mut self.buf) } + + fn size_hint(&self) -> (usize, Option) { + // 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 { + debug_assert_ne!(d, 0); + (n / d).checked_add(a / d)?.checked_add((n % d + a % d) / d) +} + +impl ExactSizeIterator for Tuples +where + I: ExactSizeIterator, + T: HomogeneousTuple, +{ } impl Tuples - where I: Iterator, - T: HomogeneousTuple +where + I: Iterator, + T: HomogeneousTuple, { /// Return a buffer with the produced items that was not enough to be grouped in a tuple. /// @@ -128,7 +157,6 @@ impl Tuples } } - /// 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 @@ impl Tuples #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] pub struct TupleWindows - where I: Iterator, - T: HomogeneousTuple +where + I: Iterator, + T: HomogeneousTuple, { iter: I, last: Option, } /// Create a new tuple windows iterator. -pub fn tuple_windows(mut iter: I) -> TupleWindows - where I: Iterator, - T: HomogeneousTuple, - T::Item: Clone +pub fn tuple_windows(iter: I) -> TupleWindows +where + I: Iterator, + 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 Iterator for TupleWindows - where I: Iterator, - T: HomogeneousTuple + Clone, - T::Item: Clone +where + I: Iterator, + T: HomogeneousTuple + Clone, + T::Item: Clone, { type Item = T; fn next(&mut self) -> Option { 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) { + 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 FusedIterator for TupleWindows - where I: FusedIterator, - T: HomogeneousTuple + Clone, - T::Item: Clone -{} +impl ExactSizeIterator for TupleWindows +where + I: ExactSizeIterator, + T: HomogeneousTuple + Clone, + T::Item: Clone, +{ +} -/// An iterator over all windows,wrapping back to the first elements when the +impl FusedIterator for TupleWindows +where + I: FusedIterator, + 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 - where I: Iterator + Clone, - T: TupleCollect + Clone +#[derive(Debug, Clone)] +pub struct CircularTupleWindows +where + I: Iterator + Clone, + T: TupleCollect + Clone, { - iter: Take, T>>, - phantom_data: PhantomData + iter: TupleWindows, T>, + len: usize, } pub fn circular_tuple_windows(iter: I) -> CircularTupleWindows - where I: Iterator + Clone + ExactSizeIterator, - T: TupleCollect + Clone, - T::Item: Clone +where + I: Iterator + 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 Iterator for CircularTupleWindows - where I: Iterator + Clone, - T: TupleCollect + Clone, - T::Item: Clone +where + I: Iterator + Clone, + T: TupleCollect + Clone, + T::Item: Clone, { type Item = T; fn next(&mut self) -> Option { - self.iter.next() + if self.len != 0 { + self.len -= 1; + self.iter.next() + } else { + None + } } + + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl ExactSizeIterator for CircularTupleWindows +where + I: Iterator + Clone, + T: TupleCollect + Clone, + T::Item: Clone, +{ +} + +impl FusedIterator for CircularTupleWindows +where + I: Iterator + Clone, + T: TupleCollect + Clone, + T::Item: Clone, +{ } pub trait TupleCollect: Sized { type Item; type Buffer: Default + AsRef<[Option]> + AsMut<[Option]>; + 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(iter: I, buf: &mut Self::Buffer) -> Option - where I: IntoIterator; + where + I: IntoIterator; fn collect_from_iter_no_buf(iter: I) -> Option - where I: IntoIterator; + where + I: IntoIterator; 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 @@ macro_rules! impl_tuple_collect { impl_tuple_collect!($($Y,)*); impl TupleCollect for ($(ignore_ident!($Y, A),)*) { type Item = A; - type Buffer = [Option; count_ident!($($Y,)*) - 1]; + type Buffer = [Option; count_ident!($($Y)*) - 1]; #[allow(unused_assignments, unused_mut)] fn collect_from_iter(iter: I, buf: &mut Self::Buffer) -> Option @@ -312,7 +382,7 @@ macro_rules! impl_tuple_collect { } fn num_items() -> usize { - count_ident!($($Y,)*) + count_ident!($($Y)*) } fn left_shift_push(&mut self, mut item: A) { diff --git a/third_party/rust/itertools/src/unique_impl.rs b/third_party/rust/itertools/src/unique_impl.rs index 4e81e78ec03b..0f6397e48fb9 100644 --- a/third_party/rust/itertools/src/unique_impl.rs +++ b/third_party/rust/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 @@ pub struct UniqueBy { } impl fmt::Debug for UniqueBy - 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(iter: I, f: F) -> UniqueBy - 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 @@ pub fn unique_by(iter: I, f: F) -> UniqueBy // count the number of new unique keys in iterable (`used` is the set already seen) fn count_new_keys(mut used: HashMap, iterable: I) -> usize - where I: IntoIterator, - K: Hash + Eq, +where + I: IntoIterator, + K: Hash + Eq, { let iter = iterable.into_iter(); let current_used = used.len(); @@ -50,20 +53,16 @@ fn count_new_keys(mut used: HashMap, iterable: I) -> usize } impl Iterator for UniqueBy - 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 { - 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 Iterator for UniqueBy } impl DoubleEndedIterator for UniqueBy - 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 { - 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 FusedIterator for UniqueBy - where I: FusedIterator, - V: Eq + Hash, - F: FnMut(&I::Item) -> V -{} +where + I: FusedIterator, + V: Eq + Hash, + F: FnMut(&I::Item) -> V, +{ +} impl Iterator for Unique - 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 { - 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 Iterator for Unique } impl DoubleEndedIterator for Unique - where I: DoubleEndedIterator, - I::Item: Eq + Hash + Clone +where + I: DoubleEndedIterator, + I::Item: Eq + Hash + Clone, { fn next_back(&mut self) -> Option { - 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 FusedIterator for Unique - 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 { +pub struct Unique +where + I: Iterator, + I::Item: Eq + Hash + Clone, +{ iter: UniqueBy, } impl fmt::Debug for Unique - 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(iter: I) -> Unique - 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/third_party/rust/itertools/src/unziptuple.rs b/third_party/rust/itertools/src/unziptuple.rs index 7af29ec4abd8..2c79c2d842cb 100644 --- a/third_party/rust/itertools/src/unziptuple.rs +++ b/third_party/rust/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/third_party/rust/itertools/src/with_position.rs b/third_party/rust/itertools/src/with_position.rs index 1388503d1cd8..2d56bb9b2247 100644 --- a/third_party/rust/itertools/src/with_position.rs +++ b/third_party/rust/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`. +/// 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 - where I: Iterator, +where + I: Iterator, { handled_first: bool, peekable: Peekable>, } +impl fmt::Debug for WithPosition +where + I: Iterator, + Peekable>: fmt::Debug, +{ + debug_fmt_fields!(WithPosition, handled_first, peekable); +} + impl Clone for WithPosition - 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(iter: I) -> WithPosition - where I: Iterator, +where + I: Iterator, { WithPosition { handled_first: false, @@ -30,36 +42,24 @@ pub fn with_position(iter: I) -> WithPosition } } -/// 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 { +#[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 Position { - /// 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 Iterator for WithPosition { - type Item = Position; + type Item = (Position, I::Item); fn next(&mut self) -> Option { match self.peekable.next() { @@ -70,15 +70,15 @@ impl Iterator for WithPosition { // 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 @@ impl Iterator for WithPosition { fn size_hint(&self) -> (usize, Option) { self.peekable.size_hint() } + + fn fold(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 ExactSizeIterator for WithPosition - where I: ExactSizeIterator, -{ } +impl ExactSizeIterator for WithPosition where I: ExactSizeIterator {} -impl FusedIterator for WithPosition -{} +impl FusedIterator for WithPosition {} diff --git a/third_party/rust/itertools/src/zip_eq_impl.rs b/third_party/rust/itertools/src/zip_eq_impl.rs index a079b326a4ce..3240a40eb3cd 100644 --- a/third_party/rust/itertools/src/zip_eq_impl.rs +++ b/third_party/rust/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 @@ pub struct ZipEq { 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). /// @@ -22,11 +21,13 @@ pub struct ZipEq { /// let data = [1, 2, 3, 4, 5]; /// for (a, b) in zip_eq(&data[..data.len() - 1], &data[1..]) { /// /* loop body */ +/// # let _ = (a, b); /// } /// ``` pub fn zip_eq(i: I, j: J) -> ZipEq - where I: IntoIterator, - J: IntoIterator +where + I: IntoIterator, + J: IntoIterator, { ZipEq { a: i.into_iter(), @@ -35,8 +36,9 @@ pub fn zip_eq(i: I, j: J) -> ZipEq } impl Iterator for ZipEq - where I: Iterator, - J: Iterator +where + I: Iterator, + J: Iterator, { type Item = (I::Item, J::Item); @@ -44,8 +46,9 @@ impl Iterator for ZipEq 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 +58,8 @@ impl Iterator for ZipEq } impl ExactSizeIterator for ZipEq - where I: ExactSizeIterator, - J: ExactSizeIterator -{} +where + I: ExactSizeIterator, + J: ExactSizeIterator, +{ +} diff --git a/third_party/rust/itertools/src/zip_longest.rs b/third_party/rust/itertools/src/zip_longest.rs index cb9a7bacb255..d4eb9a882e3a 100644 --- a/third_party/rust/itertools/src/zip_longest.rs +++ b/third_party/rust/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 @@ use crate::either_or_both::EitherOrBoth; // 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 @@ pub struct ZipLongest { /// Create a new `ZipLongest` iterator. pub fn zip_longest(a: T, b: U) -> ZipLongest - where T: Iterator, - U: Iterator +where + T: Iterator, + U: Iterator, { ZipLongest { a: a.fuse(), @@ -31,8 +33,9 @@ pub fn zip_longest(a: T, b: U) -> ZipLongest } impl Iterator for ZipLongest - where T: Iterator, - U: Iterator +where + T: Iterator, + U: Iterator, { type Item = EitherOrBoth; @@ -50,11 +53,29 @@ impl Iterator for ZipLongest fn size_hint(&self) -> (usize, Option) { size_hint::max(self.a.size_hint(), self.b.size_hint()) } + + #[inline] + fn fold(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 DoubleEndedIterator for ZipLongest - where T: DoubleEndedIterator + ExactSizeIterator, - U: DoubleEndedIterator + ExactSizeIterator +where + T: DoubleEndedIterator + ExactSizeIterator, + U: DoubleEndedIterator + ExactSizeIterator, { #[inline] fn next_back(&mut self) -> Option { @@ -70,14 +91,49 @@ impl DoubleEndedIterator for ZipLongest Less => self.b.next_back().map(EitherOrBoth::Right), } } + + fn rfold(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 ExactSizeIterator for ZipLongest - where T: ExactSizeIterator, - U: ExactSizeIterator -{} +where + T: ExactSizeIterator, + U: ExactSizeIterator, +{ +} impl FusedIterator for ZipLongest - where T: Iterator, - U: Iterator -{} +where + T: Iterator, + U: Iterator, +{ +} diff --git a/third_party/rust/itertools/src/ziptuple.rs b/third_party/rust/itertools/src/ziptuple.rs index 6d3a584c49c5..3ada0296caac 100644 --- a/third_party/rust/itertools/src/ziptuple.rs +++ b/third_party/rust/itertools/src/ziptuple.rs @@ -7,7 +7,7 @@ pub struct Zip { 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 @@ pub struct Zip { /// 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 @@ pub struct Zip { /// /// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]); /// ``` -/// [`izip!()`]: crate::izip pub fn multizip(t: U) -> Zip - where Zip: From, - Zip: Iterator, +where + Zip: From + Iterator, { Zip::from(t) } @@ -82,7 +81,7 @@ macro_rules! impl_zip_iter { fn size_hint(&self) -> (usize, Option) { - 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/third_party/rust/itertools/tests/adaptors_no_collect.rs b/third_party/rust/itertools/tests/adaptors_no_collect.rs index 103db23f1eb1..977224af29f7 100644 --- a/third_party/rust/itertools/tests/adaptors_no_collect.rs +++ b/third_party/rust/itertools/tests/adaptors_no_collect.rs @@ -22,9 +22,14 @@ impl Iterator for PanickingCounter { } fn no_collect_test(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 @@ fn combinations_no_collect() { #[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/third_party/rust/itertools/tests/laziness.rs b/third_party/rust/itertools/tests/laziness.rs new file mode 100644 index 000000000000..c559d33adc5d --- /dev/null +++ b/third_party/rust/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 { + panic!("iterator adaptor is not lazy") + } + + fn size_hint(&self) -> (usize, Option) { + (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::(); + } + map_ok { + let _ = Panicking.map(Ok::).map_ok(|x| x + 1); + } + filter_ok { + let _ = Panicking.map(Ok::).filter_ok(|x| x % 2 == 0); + } + filter_map_ok { + let _ = Panicking.map(Ok::).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/third_party/rust/itertools/tests/macros_hygiene.rs b/third_party/rust/itertools/tests/macros_hygiene.rs index d1111245d691..e6e89555562b 100644 --- a/third_party/rust/itertools/tests/macros_hygiene.rs +++ b/third_party/rust/itertools/tests/macros_hygiene.rs @@ -1,5 +1,11 @@ +mod alloc {} +mod core {} +mod either {} +mod std {} + #[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); @@ -11,3 +17,11 @@ fn izip_hygiene() { let _ = itertools::izip!(0..6, 0..9); let _ = itertools::izip!(0..6, 0..9, 0..12); } + +#[test] +fn chain_hygiene() { + let _: ::std::iter::Empty = itertools::chain!(); + let _ = itertools::chain!(0..6); + let _ = itertools::chain!(0..6, 0..9); + let _ = itertools::chain!(0..6, 0..9, 0..12); +} diff --git a/third_party/rust/itertools/tests/merge_join.rs b/third_party/rust/itertools/tests/merge_join.rs index 3280b7d4ecb0..776252fc58d1 100644 --- a/third_party/rust/itertools/tests/merge_join.rs +++ b/third_party/rust/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 = vec![]; let right: Vec = vec![]; - let expected_result: Vec> = vec![]; - let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) - .collect::>(); + let expected_result: Vec> = vec![]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn left_only() { - let left: Vec = vec![1,2,3]; + let left: Vec = vec![1, 2, 3]; let right: Vec = vec![]; - let expected_result: Vec> = vec![ + let expected_result: Vec> = 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::>(); + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn right_only() { let left: Vec = vec![]; - let right: Vec = vec![1,2,3]; - let expected_result: Vec> = vec![ + let right: Vec = vec![1, 2, 3]; + let expected_result: Vec> = 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::>(); + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn first_left_then_right() { - let left: Vec = vec![1,2,3]; - let right: Vec = vec![4,5,6]; - let expected_result: Vec> = vec![ + let left: Vec = vec![1, 2, 3]; + let right: Vec = vec![4, 5, 6]; + let expected_result: Vec> = 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::>(); + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn first_right_then_left() { - let left: Vec = vec![4,5,6]; - let right: Vec = vec![1,2,3]; - let expected_result: Vec> = vec![ + let left: Vec = vec![4, 5, 6]; + let right: Vec = vec![1, 2, 3]; + let expected_result: Vec> = 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::>(); + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn interspersed_left_and_right() { - let left: Vec = vec![1,3,5]; - let right: Vec = vec![2,4,6]; - let expected_result: Vec> = vec![ + let left: Vec = vec![1, 3, 5]; + let right: Vec = vec![2, 4, 6]; + let expected_result: Vec> = 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::>(); + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn overlapping_left_and_right() { - let left: Vec = vec![1,3,4,6]; - let right: Vec = vec![2,3,4,5]; - let expected_result: Vec> = vec![ + let left: Vec = vec![1, 3, 4, 6]; + let right: Vec = vec![2, 3, 4, 5]; + let expected_result: Vec> = 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::>(); + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } diff --git a/third_party/rust/itertools/tests/peeking_take_while.rs b/third_party/rust/itertools/tests/peeking_take_while.rs index a1147027e82b..5be97271dd80 100644 --- a/third_party/rust/itertools/tests/peeking_take_while.rs +++ b/third_party/rust/itertools/tests/peeking_take_while.rs @@ -48,3 +48,22 @@ fn peeking_take_while_slice_iter_rev() { 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/third_party/rust/itertools/tests/quick.rs b/third_party/rust/itertools/tests/quick.rs index 0adcf1ad72c5..672901e7c70a 100644 --- a/third_party/rust/itertools/tests/quick.rs +++ b/third_party/rust/itertools/tests/quick.rs @@ -2,35 +2,28 @@ //! and adaptors. //! //! In particular we test the tedious size_hint and exact size correctness. +//! +//! **NOTE:** Due to performance limitations, these tests are not run with miri! +//! They cannot be relied upon to discover soundness issues. +#![cfg(not(miri))] +#![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 +42,7 @@ impl HintKind for Exact { impl qc::Arbitrary for Exact { fn arbitrary(_: &mut G) -> Self { - Exact {} + Self {} } } @@ -66,8 +59,10 @@ struct Inexact { impl HintKind for Inexact { fn loosen_bounds(&self, org_hint: (usize, Option)) -> (usize, Option) { 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 +71,23 @@ impl qc::Arbitrary for Inexact { 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> { + fn shrink(&self) -> Box> { 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 +107,12 @@ struct Iter { hint_kind: SK, } -impl Iter where HK: HintKind +impl Iter +where + HK: HintKind, { fn new(it: Range, hint_kind: HK) -> Self { - Iter { + Self { iterator: it, fuse_flag: 0, hint_kind, @@ -128,64 +121,66 @@ impl Iter where HK: HintKind } impl Iterator for Iter - where Range: Iterator, - as Iterator>::Item: Default, - HK: HintKind, +where + Range: Iterator, + as Iterator>::Item: Default, + HK: HintKind, { type Item = as Iterator>::Item; - fn next(&mut self) -> Option - { + fn next(&mut self) -> Option { 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) - { + fn size_hint(&self) -> (usize, Option) { let org_hint = self.iterator.size_hint(); self.hint_kind.loosen_bounds(org_hint) } } impl DoubleEndedIterator for Iter - where Range: DoubleEndedIterator, - as Iterator>::Item: Default, - HK: HintKind +where + Range: DoubleEndedIterator, + as Iterator>::Item: Default, + HK: HintKind, { - fn next_back(&mut self) -> Option { self.iterator.next_back() } + fn next_back(&mut self) -> Option { + self.iterator.next_back() + } } -impl ExactSizeIterator for Iter where Range: ExactSizeIterator, +impl ExactSizeIterator for Iter +where + Range: ExactSizeIterator, as Iterator>::Item: Default, -{ } +{ +} impl qc::Arbitrary for Iter - where T: qc::Arbitrary, - HK: HintKind, +where + T: qc::Arbitrary, + HK: HintKind, { - fn arbitrary(g: &mut G) -> Self - { - Iter::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g)) + fn arbitrary(g: &mut G) -> Self { + Self::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g)) } - fn shrink(&self) -> Box>> - { + fn shrink(&self) -> Box> { 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 +196,10 @@ struct ShiftRange { hint_kind: HK, } -impl Iterator for ShiftRange where HK: HintKind { +impl Iterator for ShiftRange +where + HK: HintKind, +{ type Item = Iter; fn next(&mut self) -> Option { @@ -219,10 +217,11 @@ impl Iterator for ShiftRange where HK: HintKind { } } -impl ExactSizeIterator for ShiftRange { } +impl ExactSizeIterator for ShiftRange {} impl qc::Arbitrary for ShiftRange - where HK: HintKind +where + HK: HintKind, { fn arbitrary(g: &mut G) -> Self { const MAX_STARTING_RANGE_DIFF: i32 = 32; @@ -236,7 +235,7 @@ impl qc::Arbitrary for ShiftRange 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 +249,7 @@ impl qc::Arbitrary for ShiftRange fn correct_count(get_it: F) -> bool where I: Iterator, - F: Fn() -> I + F: Fn() -> I, { let mut counts = vec![get_it().count()]; @@ -258,7 +257,6 @@ where let mut it = get_it(); for _ in 0..(counts.len() - 1) { - #[allow(clippy::manual_assert)] if it.next().is_none() { panic!("Iterator shouldn't be finished, may not be deterministic"); } @@ -276,7 +274,10 @@ where 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 +300,10 @@ fn correct_size_hint(mut it: I) -> bool { // 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 +312,19 @@ fn correct_size_hint(mut it: I) -> bool { fn exact_size(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 +334,19 @@ fn exact_size(mut it: I) -> bool { fn exact_size_for_this(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 +453,10 @@ quickcheck! { assert_eq!(answer.into_iter().last(), a.multi_cartesian_product().last()); } - #[allow(deprecated)] - fn size_step(a: Iter, 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, 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, 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::>::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, s: u8) -> bool { @@ -596,6 +574,20 @@ quickcheck! { let b = &b[..len]; itertools::equal(zip_eq(a, b), zip(a, b)) } + + #[should_panic] + fn zip_eq_panics(a: Vec, b: Vec) -> 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) -> 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, b: Iter) -> bool { let filt = a.clone().dedup(); let filt2 = b.clone().dedup(); @@ -750,6 +742,56 @@ quickcheck! { } } +quickcheck! { + fn correct_peek_nth(mut a: Vec) -> () { + 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, b: Vec) -> () { + 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) -> () { + 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) -> bool { let mut b = a.clone(); @@ -811,9 +853,8 @@ quickcheck! { quickcheck! { fn size_put_back(a: Vec, x: Option) -> 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) } @@ -829,6 +870,31 @@ quickcheck! { } } +quickcheck! { + fn merge_join_by_ordering_vs_bool(a: Vec, b: Vec) -> 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, b: Vec) -> 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) -> bool { let (mut t1, mut t2) = a.iter().tee(); @@ -870,8 +936,31 @@ quickcheck! { } quickcheck! { - fn size_combinations(it: Iter) -> bool { - correct_size_hint(it.tuple_combinations::<(_, _)>()) + fn size_combinations(a: Iter) -> 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) -> 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) -> 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) -> 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::() / (1..=k).product::() } } @@ -887,7 +976,7 @@ quickcheck! { } } } - cmb.next() == None + cmb.next().is_none() } } @@ -936,63 +1025,74 @@ quickcheck! { } quickcheck! { - fn fuzz_group_by_lazy_1(it: Iter) -> bool { + fn fuzz_chunk_by_lazy_1(it: Iter) -> 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) -> 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) -> 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) -> 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) -> 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, 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, 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, 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, size: u8) -> bool { let mut size = size; @@ -1010,7 +1110,75 @@ quickcheck! { } } +// tuple iterators quickcheck! { + fn equal_circular_tuple_windows_1(a: Vec) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> bool { + exact_size(a.iter().circular_tuple_windows::<(_, _, _, _)>()) + } + fn equal_tuple_windows_1(a: Vec) -> bool { let x = a.windows(1).map(|s| (&s[0], )); let y = a.iter().tuple_windows::<(_,)>(); @@ -1035,6 +1203,14 @@ quickcheck! { itertools::equal(x, y) } + fn tuple_windows_exact_size_1(a: Vec) -> bool { + exact_size(a.iter().tuple_windows::<(_,)>()) + } + + fn tuple_windows_exact_size_4(a: Vec) -> bool { + exact_size(a.iter().tuple_windows::<(_, _, _, _)>()) + } + fn equal_tuples_1(a: Vec) -> bool { let x = a.chunks(1).map(|s| (&s[0], )); let y = a.iter().tuples::<(_,)>(); @@ -1066,6 +1242,18 @@ quickcheck! { assert_eq!(buffer.len(), a.len() % 4); exact_size(buffer) } + + fn tuples_size_hint_inexact(a: Iter) -> 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) -> bool { + exact_size(a.clone().tuples::<(_,)>()) + && exact_size(a.clone().tuples::<(_, _)>()) + && exact_size(a.tuples::<(_, _, _, _)>()) + } } // with_position @@ -1097,14 +1285,14 @@ quickcheck! { #[derive(Clone, Debug, PartialEq, Eq)] struct Val(u32, u32); -impl PartialOrd for Val { - fn partial_cmp(&self, other: &Val) -> Option { - self.0.partial_cmp(&other.0) +impl PartialOrd for Val { + fn partial_cmp(&self, other: &Self) -> Option { + 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 +1300,10 @@ impl Ord for Val { impl qc::Arbitrary for Val { fn arbitrary(g: &mut G) -> Self { let (x, y) = <(u32, u32)>::arbitrary(g); - Val(x, y) + Self(x, y) } fn shrink(&self) -> Box> { - 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 +1345,12 @@ quickcheck! { } quickcheck! { - #[allow(deprecated)] - fn tree_fold1_f64(mut a: Vec) -> TestResult { + fn tree_reduce_f64(mut a: Vec) -> TestResult { fn collapse_adjacent(x: Vec, mut f: F) -> Vec 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 +1364,7 @@ quickcheck! { 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 +1389,7 @@ quickcheck! { fn at_most_one_i32(a: Vec) -> 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 +1419,7 @@ quickcheck! { Some(acc.unwrap_or(0) + val) } }); - + let group_map_lookup = a.iter() .map(|&b| b as u64) .map(|i| (i % modulo, i)) @@ -1252,7 +1439,7 @@ quickcheck! { 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 +1454,35 @@ quickcheck! { } } + fn correct_grouping_map_by_fold_with_modulo_key(a: Vec, 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::>(); + 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::()); + } + } + fn correct_grouping_map_by_fold_modulo_key(a: Vec, 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,22 +1506,21 @@ quickcheck! { } } - fn correct_grouping_map_by_fold_first_modulo_key(a: Vec, modulo: u8) -> () { + fn correct_grouping_map_by_reduce_modulo_key(a: Vec, 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 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().fold1(|acc, val| acc + val).unwrap())) + .map(|(key, vals)| (key, vals.into_iter().reduce(|acc, val| acc + val).unwrap())) .collect::>(); assert_eq!(lookup, group_map_lookup); @@ -1372,7 +1587,7 @@ quickcheck! { 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, 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 +1638,7 @@ quickcheck! { 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, 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 +1751,7 @@ quickcheck! { .min_by(|_, _, _| Ordering::Equal); assert_eq!(lookup[&0], 0); - + let lookup = (0..=10) .into_grouping_map_by(|_| 0) .minmax_by(|_, _, _| Ordering::Equal); @@ -1594,12 +1809,10 @@ quickcheck! { } } - -fn is_fused(mut it: I) -> bool -{ +fn is_fused(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 +1853,7 @@ quickcheck! { !is_fused(a.clone().interleave_shortest(b.clone())) && is_fused(a.fuse().interleave_shortest(b.fuse())) } - + fn fused_product(a: Iter, b: Iter) -> bool { is_fused(a.fuse().cartesian_product(b.fuse())) @@ -1746,4 +1959,11 @@ quickcheck! { result_set.is_empty() } } + + fn tail(v: Vec, 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/third_party/rust/itertools/tests/specializations.rs b/third_party/rust/itertools/tests/specializations.rs index 057e11c9f67e..e6694c8e7a32 100644 --- a/third_party/rust/itertools/tests/specializations.rs +++ b/third_party/rust/itertools/tests/specializations.rs @@ -1,8 +1,20 @@ +//! Test specializations of methods with default impls match the behavior of the +//! default impls. +//! +//! **NOTE:** Due to performance limitations, these tests are not run with miri! +//! They cannot be relied upon to discover soundness issues. + +#![cfg(not(miri))] +#![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); + impl Iterator for Unspecialized where I: Iterator, @@ -15,30 +27,41 @@ where } } -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 DoubleEndedIterator for Unspecialized +where + I: DoubleEndedIterator, +{ + #[inline(always)] + fn next_back(&mut self) -> Option { + self.0.next_back() } } -fn test_specializations( - it: &Iter, -) where - IterItem: Eq + Debug + Clone, - Iter: Iterator + Clone, +fn test_specializations(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::>()); 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 +73,7 @@ fn test_specializations( 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 +95,270 @@ fn test_specializations( } } +fn test_double_ended_specializations(it: &I) +where + I::Item: Eq + Debug + Clone, + I: DoubleEndedIterator + Clone, +{ + macro_rules! check_specialized { + ($src:expr, |$it:pat| $closure:expr) => { + // Many iterators special-case the first elements, so we test specializations for iterators that have already been advanced. + let mut src = $src.clone(); + for step in 0..8 { + let $it = src.clone(); + let v1 = $closure; + let $it = Unspecialized(src.clone()); + let v2 = $closure; + assert_eq!(v1, v2); + if step % 2 == 0 { + src.next(); + } else { + src.next_back(); + } + } + } + } + check_specialized!(it, |i| { + let mut parameters_from_rfold = vec![]; + let rfold_result = i.rfold(vec![], |mut acc, v: I::Item| { + parameters_from_rfold.push((acc.clone(), v.clone())); + acc.push(v); + acc + }); + (parameters_from_rfold, rfold_result) + }); + let size = it.clone().count(); + for n in 0..size + 2 { + check_specialized!(it, |mut i| i.nth_back(n)); + } +} + quickcheck! { + fn interleave(v: Vec, w: Vec) -> () { + test_specializations(&v.iter().interleave(w.iter())); + } + + fn interleave_shortest(v: Vec, w: Vec) -> () { + test_specializations(&v.iter().interleave_shortest(w.iter())); + } + + fn batching(v: Vec) -> () { + test_specializations(&v.iter().batching(Iterator::next)); + } + + fn tuple_windows(v: Vec) -> () { + test_specializations(&v.iter().tuple_windows::<(_,)>()); + test_specializations(&v.iter().tuple_windows::<(_, _)>()); + test_specializations(&v.iter().tuple_windows::<(_, _, _)>()); + } + + fn circular_tuple_windows(v: Vec) -> () { + 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) -> () { + test_specializations(&v.iter().tuples::<(_,)>()); + test_specializations(&v.iter().tuples::<(_, _)>()); + test_specializations(&v.iter().tuples::<(_, _, _)>()); + } + + fn cartesian_product(a: Vec, b: Vec) -> 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, b: Vec, c: Vec) -> 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) -> () { + test_specializations(&v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) })) + } + + fn dedup(v: Vec) -> () { + test_specializations(&v.iter().dedup()) + } + + fn dedup_by(v: Vec) -> () { + test_specializations(&v.iter().dedup_by(PartialOrd::ge)) + } + + fn dedup_with_count(v: Vec) -> () { + test_specializations(&v.iter().dedup_with_count()) + } + + fn dedup_by_with_count(v: Vec) -> () { + test_specializations(&v.iter().dedup_by_with_count(PartialOrd::ge)) + } + + fn duplicates(v: Vec) -> () { + let it = v.iter().duplicates(); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn duplicates_by(v: Vec) -> () { + let it = v.iter().duplicates_by(|x| *x % 10); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn unique(v: Vec) -> () { + let it = v.iter().unique(); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn unique_by(v: Vec) -> () { + let it = v.iter().unique_by(|x| *x % 50); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn take_while_inclusive(v: Vec) -> () { + test_specializations(&v.iter().copied().take_while_inclusive(|&x| x < 100)); + } + + fn while_some(v: Vec) -> () { + test_specializations(&v.iter().map(|&x| if x < 100 { Some(2 * x) } else { None }).while_some()); + } + + fn pad_using(v: Vec) -> () { + 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) -> () { + test_specializations(&v.iter().with_position()); + } + + fn positions(v: Vec) -> () { + let it = v.iter().positions(|x| x % 5 == 0); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn update(v: Vec) -> () { + let it = v.iter().copied().update(|x| *x = x.wrapping_mul(7)); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn tuple_combinations(v: Vec) -> TestResult { + 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) -> () { test_specializations(&v.into_iter().intersperse(0)); } + + fn intersperse_with(v: Vec) -> () { + test_specializations(&v.into_iter().intersperse_with(|| 0)); + } + + fn array_combinations(v: Vec) -> TestResult { + if v.len() > 10 { + return TestResult::discard(); + } + test_specializations(&v.iter().array_combinations::<1>()); + test_specializations(&v.iter().array_combinations::<2>()); + test_specializations(&v.iter().array_combinations::<3>()); + TestResult::passed() + } + + fn combinations(a: Vec, 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, 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, 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) -> TestResult { + if a.len() > 6 { + return TestResult::discard(); + } + test_specializations(&a.iter().powerset()); + TestResult::passed() + } + + fn zip_longest(a: Vec, b: Vec) -> () { + let it = a.into_iter().zip_longest(b); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn zip_eq(a: Vec) -> () { + test_specializations(&a.iter().zip_eq(a.iter().rev())) + } + + fn multizip(a: Vec) -> () { + let it = itertools::multizip((a.iter(), a.iter().rev(), a.iter().take(50))); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn izip(a: Vec, b: Vec) -> () { + test_specializations(&itertools::izip!(b.iter(), a, b.iter().rev())); + } + + fn iproduct(a: Vec, b: Vec, c: Vec) -> 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) -> 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 +368,128 @@ quickcheck! { pb.put_back(1); test_specializations(&pb); } + + fn put_back_n(v: Vec, 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, 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, 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, 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, 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, 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, i2: Vec) -> () { - test_specializations(&i1.into_iter().merge_join_by(i2.into_iter(), std::cmp::Ord::cmp)); + fn merge(a: Vec, b: Vec) -> () { + test_specializations(&a.into_iter().merge(b)) + } + + fn merge_by(a: Vec, b: Vec) -> () { + test_specializations(&a.into_iter().merge_by(b, PartialOrd::ge)) + } + + fn merge_join_by_ordering(i1: Vec, i2: Vec) -> () { + test_specializations(&i1.into_iter().merge_join_by(i2, Ord::cmp)); + } + + fn merge_join_by_bool(i1: Vec, i2: Vec) -> () { + test_specializations(&i1.into_iter().merge_join_by(i2, PartialOrd::ge)); + } + + fn kmerge(a: Vec, b: Vec, c: Vec) -> () { + test_specializations(&vec![a, b, c] + .into_iter() + .map(|v| v.into_iter().sorted()) + .kmerge()); + } + + fn kmerge_by(a: Vec, b: Vec, c: Vec) -> () { + 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) -> () { - test_specializations(&v.into_iter().map_into::()); + let it = v.into_iter().map_into::(); + test_specializations(&it); + test_double_ended_specializations(&it); } -} -quickcheck! { fn map_ok(v: Vec>) -> () { - test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1))); + let it = v.into_iter().map_ok(|u| u.checked_add(1)); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn filter_ok(v: Vec>) -> () { + let it = v.into_iter().filter_ok(|&i| i < 20); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + fn filter_map_ok(v: Vec>) -> () { + let it = v.into_iter().filter_map_ok(|i| if i < 20 { Some(i * 2) } else { None }); + test_specializations(&it); + test_double_ended_specializations(&it); + } + + // `SmallIter2` because `Vec` is too slow and we get bad coverage from a singleton like Option + fn flatten_ok(v: Vec, 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>) -> () { helper(v.iter().copied()); helper(v.iter().copied().filter(Result::is_ok)); - fn helper(it: impl Iterator> + Clone) { + fn helper(it: impl DoubleEndedIterator> + Clone) { macro_rules! check_results_specialized { ($src:expr, |$it:pat| $closure:expr) => { assert_eq!( @@ -126,6 +505,7 @@ quickcheck! { check_results_specialized!(it, |i| i.count()); check_results_specialized!(it, |i| i.last()); check_results_specialized!(it, |i| i.collect::>()); + check_results_specialized!(it, |i| i.rev().collect::>()); check_results_specialized!(it, |i| { let mut parameters_from_fold = vec![]; let fold_result = i.fold(vec![], |mut acc, v| { @@ -135,6 +515,15 @@ quickcheck! { }); (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 +537,67 @@ quickcheck! { 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` with maximum 2 elements. +#[derive(Debug, Clone, Default)] +enum SmallIter2 { + #[default] + Zero, + One(T), + Two(T, T), +} + +impl Arbitrary for SmallIter2 { + fn arbitrary(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 Iterator for SmallIter2 { + type Item = T; + + fn next(&mut self) -> Option { + 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) { + let len = match self { + Self::Zero => 0, + Self::One(_) => 1, + Self::Two(_, _) => 2, + }; + (len, Some(len)) + } +} + +impl DoubleEndedIterator for SmallIter2 { + fn next_back(&mut self) -> Option { + 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/third_party/rust/itertools/tests/test_core.rs b/third_party/rust/itertools/tests/test_core.rs index df94eb665fed..4936160850d7 100644 --- a/third_party/rust/itertools/tests/test_core.rs +++ b/third_party/rust/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(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(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 @@ fn product2() { 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 @@ fn product_temporary() { 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 @@ fn izip_macro() { #[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 @@ fn chain_macro() { #[test] fn chain2() { let _ = chain!(1.., 2..); - let _ = chain!(1.., 2.., ); + let _ = chain!(1.., 2..,); } #[test] @@ -127,7 +179,7 @@ fn write_to() { #[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 @@ fn test_intersperse_with() { 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 @@ fn test_put_back() { 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 @@ fn count_clones() { use core::cell::Cell; #[derive(PartialEq, Debug)] struct Foo { - n: Cell + n: Cell, } - 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 @@ fn part() { } #[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); } @@ -315,3 +372,28 @@ fn product1() { assert_eq!(v[1..3].iter().cloned().product1::(), Some(2)); assert_eq!(v[1..5].iter().cloned().product1::(), Some(24)); } + +#[test] +fn next_array() { + let v = [1, 2, 3, 4, 5]; + let mut iter = v.iter(); + assert_eq!(iter.next_array(), Some([])); + assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([1, 2])); + assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([3, 4])); + assert_eq!(iter.next_array::<2>(), None); +} + +#[test] +fn collect_array() { + let v = [1, 2]; + let iter = v.iter().cloned(); + assert_eq!(iter.collect_array(), Some([1, 2])); + + let v = [1]; + let iter = v.iter().cloned(); + assert_eq!(iter.collect_array::<2>(), None); + + let v = [1, 2, 3]; + let iter = v.iter().cloned(); + assert_eq!(iter.collect_array::<2>(), None); +} diff --git a/third_party/rust/itertools/tests/test_std.rs b/third_party/rust/itertools/tests/test_std.rs index f590342349d1..ad391faad648 100644 --- a/third_party/rust/itertools/tests/test_std.rs +++ b/third_party/rust/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 @@ fn product3() { } } } - 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 = vec![0, 2, 4]; let v1: Vec = 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 = vec![0, 2, 4, 6, 8]; let v1: Vec = 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 @@ fn duplicates_by() { let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"]; let ys = ["aa", "bbbb", "cccc"]; it::assert_equal(ys.iter(), xs.iter().duplicates_by(|x| x[..2].to_string())); - it::assert_equal(ys.iter(), xs.iter().rev().duplicates_by(|x| x[..2].to_string()).rev()); + 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 @@ fn duplicates() { 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 @@ fn unique_by() { 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 @@ fn intersperse() { 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 @@ fn dedup() { #[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 @@ fn coalesce() { #[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 @@ fn dedup_with_count() { 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,11 +275,27 @@ fn all_equal() { 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()); @@ -240,7 +316,7 @@ fn test_put_back_n() { #[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 @@ fn tee() { 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 @@ fn test_rciter() { 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 + impl<'r, X, I> Iterator for ByRef<'r, I> + where + I: ?Sized + 'r + Iterator, { type Item = X; - fn next(&mut self) -> Option - { + fn next(&mut self) -> Option { self.0.next() } } - let mut it = Box::new(0..10) as Box>; + let mut it = Box::new(0..10) as Box>; assert_eq!(it.next(), Some(0)); { - /* make sure foreach works on non-Sized */ let jt: &mut dyn Iterator = &mut *it; assert_eq!(jt.next(), Some(1)); @@ -314,15 +387,22 @@ fn trait_pointers() { } 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 @@ fn merge_by_btree() { 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 @@ fn kmerge_empty_size_hint() { #[test] fn join() { let many = [1, 2, 3]; - let one = [1]; + let one = [1]; let none: Vec = 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,33 +484,85 @@ fn sorted_unstable_by_key() { #[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()); it::assert_equal(v, vec![4, 3, 2, 1, 0]); } +#[cfg(not(miri))] 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 = (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); + } + + fn k_smallest_relaxed_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 usize, m as u64); + + let mut v: Vec<_> = (n..n.saturating_add(m as _)).collect(); + // Generate a random permutation of n..n+m + v.shuffle(&mut thread_rng()); + + // 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_relaxed(k); + let smallest_by = v.iter().cloned().k_smallest_relaxed_by(k, Ord::cmp); + let smallest_by_key = v.iter().cloned().k_smallest_relaxed_by_key(k, |&x| x); + + let sorted_largest = sorted[sorted.len() - num_elements..].iter().rev().cloned(); + let largest = v.iter().cloned().k_largest_relaxed(k); + let largest_by = v.iter().cloned().k_largest_relaxed_by(k, Ord::cmp); + let largest_by_key = v.iter().cloned().k_largest_relaxed_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 +571,13 @@ struct RandIter + _t: PhantomData, } impl Iterator for RandIter -where Standard: Distribution { +where + Standard: Distribution, +{ type Item = T; fn next(&mut self) -> Option { if self.idx == self.len { @@ -461,11 +591,11 @@ where Standard: Distribution { impl qc::Arbitrary for RandIter { fn arbitrary(g: &mut G) -> Self { - RandIter { + Self { idx: 0, len: g.size(), rng: R::seed_from_u64(g.next_u64()), - _t : PhantomData{}, + _t: PhantomData {}, } } } @@ -478,11 +608,25 @@ where I::Item: Ord + Debug, { let j = i.clone(); + let i1 = i.clone(); + let j1 = 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)); + it::assert_equal(i1.k_smallest_relaxed(k), j1.sorted().take(k)); +} + +// Similar to `k_smallest_sort` but for our custom heap implementation. +fn k_smallest_by_sort(i: I, k: u16) +where + I: Iterator + Clone, + I::Item: Ord + Debug, +{ + let j = i.clone(); + let i1 = i.clone(); + let j1 = i.clone(); + let k = k as usize; + it::assert_equal(i.k_smallest_by(k, Ord::cmp), j.sorted().take(k)); + it::assert_equal(i1.k_smallest_relaxed_by(k, Ord::cmp), j1.sorted().take(k)); } macro_rules! generic_test { @@ -497,7 +641,10 @@ macro_rules! generic_test { }; } +#[cfg(not(miri))] generic_test!(k_smallest_sort, u8, u16, u32, u64, i8, i16, i32, i64); +#[cfg(not(miri))] +generic_test!(k_smallest_by_sort, u8, u16, u32, u64, i8, i16, i32, i64); #[test] fn sorted_by_key() { @@ -534,7 +681,7 @@ fn sorted_by_cached_key() { #[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::>()); @@ -575,7 +722,7 @@ fn test_multipeek_reset() { #[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)); @@ -598,9 +745,24 @@ fn test_multipeek_peeking_next() { assert_eq!(mp.peek(), None); } +#[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::>()); @@ -635,7 +797,7 @@ fn test_peek_nth() { #[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)); @@ -662,6 +824,35 @@ fn test_peek_nth_peeking_next() { assert_eq!(iter.peek(), None); } +#[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 +867,14 @@ fn pad_using() { } #[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 +887,24 @@ fn group_by() { // 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 +921,11 @@ fn group_by() { { 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 +933,95 @@ fn group_by() { { 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 +1042,8 @@ fn concat_empty() { #[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 +1051,20 @@ fn combinations() { 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::<(_, _)>(), >::new()); it::assert_equal((0..1).tuple_combinations::<(_, _)>(), >::new()); @@ -886,13 +1084,88 @@ fn combinations_of_too_short() { } } - #[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::() / (1..=k).product::() + } +} + +#[test] +fn combinations_range_count() { + for n in 0..=7 { + for k in 0..=7 { + 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..=7 { + 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 +1173,41 @@ fn permutations_zero() { } #[test] +fn permutations_range_count() { + for n in 0..=4 { + for k in 0..=4 { + 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] +#[cfg(not(miri))] fn combinations_with_replacement() { // Pool smaller than n it::assert_equal((0..1).combinations_with_replacement(2), vec![vec![0, 0]]); @@ -916,15 +1224,9 @@ fn combinations_with_replacement() { ], ); // 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), @@ -932,26 +1234,75 @@ fn combinations_with_replacement() { ); } +#[test] +fn combinations_with_replacement_range_count() { + for n in 0..=4 { + for k in 0..=4 { + 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..=4 { + 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 +1316,20 @@ fn diff_mismatch() { #[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![5, 6], + Some(it::Diff::Longer(_, remaining)) => remaining.collect::>() == 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 +1349,14 @@ fn extrema_set() { #[derive(Clone, Debug, PartialEq, Eq)] struct Val(u32, u32); - impl PartialOrd for Val { - fn partial_cmp(&self, other: &Val) -> Option { - self.0.partial_cmp(&other.0) + impl PartialOrd for Val { + fn partial_cmp(&self, other: &Self) -> Option { + 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 +1367,7 @@ fn extrema_set() { 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 +1390,34 @@ fn extrema_set() { #[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 for Val { - fn partial_cmp(&self, other: &Val) -> Option { - self.0.partial_cmp(&other.0) + impl PartialOrd for Val { + fn partial_cmp(&self, other: &Self) -> Option { + 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::>.iter().minmax(), MinMaxResult::NoElements); + assert_eq!( + None::>.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 +1426,11 @@ fn minmax() { 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 +1453,34 @@ fn format() { #[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 +1501,13 @@ fn tree_fold1() { "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 +1517,53 @@ fn exactly_one_question_mark_syntax_works() { exactly_one_question_mark_return().unwrap_err(); } -fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError>> { +fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError>> +{ [].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/third_party/rust/itertools/tests/zip.rs b/third_party/rust/itertools/tests/zip.rs index 75157d34f3aa..daed31e32da4 100644 --- a/third_party/rust/itertools/tests/zip.rs +++ b/third_party/rust/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] @@ -20,7 +20,7 @@ fn test_zip_longest_size_hint() { let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; let v2 = &[10, 11, 12]; - assert_eq!(c.zip_longest(v.iter()).size_hint(), (std::usize::MAX, None)); + assert_eq!(c.zip_longest(v.iter()).size_hint(), (usize::MAX, None)); assert_eq!(v.iter().zip_longest(v2.iter()).size_hint(), (10, Some(10))); } @@ -54,24 +54,3 @@ fn test_double_ended_zip() { 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(); -}