Bug 2060165 - chore(rust): audit-shim-upgrade itertools 0.14.0 → 0.15.0 r=glandium,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D315997
This commit is contained in:
Erich Gubler
2026-08-12 09:48:33 +00:00
committed by egubler@mozilla.com
parent f6a45b81e4
commit 6c3cf2c215
52 changed files with 2717 additions and 599 deletions
Generated
+18 -11
View File
@@ -584,7 +584,7 @@ dependencies = [
"bitflags 2.10.0",
"cexpr",
"clang-sys",
"itertools 0.14.0",
"itertools 0.14.999",
"proc-macro2",
"quote",
"regex",
@@ -3976,14 +3976,21 @@ dependencies = [
name = "itertools"
version = "0.13.0"
dependencies = [
"itertools 0.14.0",
"itertools 0.14.999",
]
[[package]]
name = "itertools"
version = "0.14.0"
version = "0.14.999"
dependencies = [
"itertools 0.15.0",
]
[[package]]
name = "itertools"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc"
dependencies = [
"either",
]
@@ -4928,7 +4935,7 @@ dependencies = [
"futures",
"getrandom 0.2.999",
"hex",
"itertools 0.14.0",
"itertools 0.14.999",
"maybe-async",
"mls-rs-codec",
"mls-rs-core",
@@ -4950,7 +4957,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45bd834f164dc06c1fed805540ae307a460b7ed7c2769a35a376f1de577a0dc1"
dependencies = [
"itertools 0.14.0",
"itertools 0.14.999",
"mls-rs-codec-derive",
"thiserror 2.0.12",
"wasm-bindgen",
@@ -5228,7 +5235,7 @@ dependencies = [
"icu_properties",
"idna",
"indexmap",
"itertools 0.14.0",
"itertools 0.15.0",
"libc",
"log",
"memchr",
@@ -5590,7 +5597,7 @@ dependencies = [
"glob",
"heck",
"icu_segmenter",
"itertools 0.14.0",
"itertools 0.14.999",
"lazy_static",
"mozilla-central-workspace-hack",
"regex",
@@ -6548,7 +6555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
dependencies = [
"anyhow",
"itertools 0.14.0",
"itertools 0.14.999",
"proc-macro2",
"quote",
"syn",
@@ -7727,7 +7734,7 @@ dependencies = [
"gecko-profiler",
"icu_segmenter",
"indexmap",
"itertools 0.14.0",
"itertools 0.14.999",
"itoa",
"log",
"malloc_size_of",
@@ -7823,7 +7830,7 @@ dependencies = [
"icu_normalizer",
"icu_properties",
"interrupt-support",
"itertools 0.14.0",
"itertools 0.14.999",
"once_cell",
"parking_lot",
"rc_crypto",
+5 -2
View File
@@ -266,8 +266,11 @@ base64_21 = { package = "base64", path = "build/rust/base64" }
# Patch wasi 0.11 to 0.14
wasi_0_11 = { package = "wasi", path = "build/rust/wasi" }
# Patch itertools 0.13 to 0.14
itertools = { path = "build/rust/itertools" }
# Patch itertools 0.13 to 0.15
itertools_0_13 = { package = "itertools", path = "build/rust/itertools-0.13" }
# Patch itertools 0.14 to 0.15
itertools_0_14 = { package = "itertools", path = "build/rust/itertools" }
# tokio 0.29.0 includes an experimental "tracing" feature which requires
# backtrace ^0.3.58 and the `tokio_unstable` flag. We don't use it, and nothing
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "itertools"
version = "0.13.0"
edition = "2018"
license = "MPL2.0"
[lib]
path = "lib.rs"
[dependencies.itertools]
version = "0.14"
+5
View File
@@ -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::*;
+8 -2
View File
@@ -1,11 +1,17 @@
[package]
name = "itertools"
version = "0.13.0"
version = "0.14.999"
edition = "2018"
license = "MPL2.0"
[lib]
path = "lib.rs"
[features]
default = ["itertools/default"]
use_alloc = ["itertools/use_alloc"]
use_std = ["itertools/use_std"]
[dependencies.itertools]
version = "0.14"
version = "0.15"
default-features = false
+1 -1
View File
@@ -87,7 +87,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.14", default-features = false, features = ["use_alloc"], optional = true }
itertools = { version = "0.15", default-features = false, features = ["use_alloc"], optional = true }
libc = "0.2"
log = { version = "0.4", features = ["std"], optional = true }
memchr = { version = "2", optional = true }
+5
View File
@@ -4105,6 +4105,11 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "0.10.3 -> 0.10.5"
[[audits.itertools]]
who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy"
delta = "0.14.0 -> 0.15.0"
[[audits.itoa]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"git": {
"sha1": "a015a6831525ee1637df747d3f530a627d9741bf"
"sha1": "37bd72aa6d58e594711d127b52418ca5e58b6091"
},
"path_in_vcs": ""
}
+26
View File
@@ -1,5 +1,31 @@
# Changelog
## 0.15.0
### Breaking
- Restructure `Position` as struct instead of enum (#1042, #1043)
- Canonicalize `all_equal_value`'s error type (#1032)
### Added
- Add `*_with_hasher` adaptors (#1007)
- Add strip_prefix and strip_prefix_by methods (#1104)
### Changed
- Remove `Clone` bounds from `tuple_combinations` and `array_combinations`(#1011)
- `must_use` for `collect_vec` (#1009)
- Make `izip!` temporary friendly (#1021)
- Add `array_combinations_with_replacement` (#1033)
- Implement `Debug` for remaining public types (#1038)
- Specialize `ExactlyOneError::count` (#1046)
- Implement `PeekingNext` for more types, in particular `vec::IntoIter` (#1059, #1073)
- Fix `PadUsing::next_back` (#1082)
- Introduce `[circular_]array_windows`, deprecate `tuple_windows` (#1086)
- Deprecate `tuple_combinations` (replaced by `array_combinations`) (#1085)
### Notable Internal Changes
- Make `into_group_map` code more idiomatic (#1027)
- Fix clippy lints (#1017, #1029, #1076, #1099)
## 0.14.0
### Breaking
+169 -162
View File
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
@@ -30,9 +30,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.3.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bitflags"
@@ -42,9 +42,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.16.0"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cast"
@@ -54,9 +54,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.0"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "ciborium"
@@ -144,9 +144,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
@@ -163,21 +163,45 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.2"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "either"
version = "1.11.0"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "getrandom"
@@ -192,12 +216,13 @@ dependencies = [
[[package]]
name = "half"
version = "2.4.1"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
dependencies = [
"cfg-if",
"crunchy",
"zerocopy",
]
[[package]]
@@ -236,7 +261,7 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.14.0"
version = "0.15.0"
dependencies = [
"criterion",
"either",
@@ -248,42 +273,38 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.11"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.69"
version = "0.3.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
dependencies = [
"cfg-if",
"futures-util",
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.154"
version = "0.2.186"
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"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "memchr"
version = "2.7.2"
version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
[[package]]
name = "num-traits"
@@ -296,15 +317,15 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.19.0"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "oorandom"
version = "11.1.3"
version = "11.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "os_str_bytes"
@@ -325,10 +346,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c"
[[package]]
name = "plotters"
version = "0.3.5"
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "plotters"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
dependencies = [
"num-traits",
"plotters-backend",
@@ -339,30 +366,33 @@ dependencies = [
[[package]]
name = "plotters-backend"
version = "0.3.5"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
[[package]]
name = "plotters-svg"
version = "0.3.5"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
dependencies = [
"plotters-backend",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.82"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
@@ -379,9 +409,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.36"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
@@ -429,9 +459,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.10.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
dependencies = [
"either",
"rayon-core",
@@ -439,9 +469,9 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.12.1"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
@@ -449,9 +479,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.4"
version = "1.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
dependencies = [
"aho-corasick",
"memchr",
@@ -461,9 +491,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.6"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
@@ -472,15 +502,15 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.3"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "ryu"
version = "1.0.18"
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "same-file"
@@ -493,18 +523,28 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.202"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.202"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -513,20 +553,28 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.117"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"ryu",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "syn"
version = "2.0.63"
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -535,9 +583,9 @@ dependencies = [
[[package]]
name = "textwrap"
version = "0.16.1"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
[[package]]
name = "tinytemplate"
@@ -551,9 +599,9 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.12"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "walkdir"
@@ -573,34 +621,22 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
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",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -608,28 +644,31 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
dependencies = [
"unicode-ident",
]
[[package]]
name = "web-sys"
version = "0.3.69"
version = "0.3.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -653,9 +692,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.8"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys",
]
@@ -666,75 +705,43 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.52.0"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-targets",
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
name = "zerocopy"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f"
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",
"zerocopy-derive",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
name = "zerocopy-derive"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
name = "zmij"
version = "1.0.21"
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"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+5 -1
View File
@@ -13,7 +13,7 @@
edition = "2018"
rust-version = "1.63.0"
name = "itertools"
version = "0.14.0"
version = "0.15.0"
authors = ["bluss"]
build = false
autolib = false
@@ -61,6 +61,10 @@ path = "examples/iris.rs"
name = "adaptors_no_collect"
path = "tests/adaptors_no_collect.rs"
[[test]]
name = "arrays"
path = "tests/arrays.rs"
[[test]]
name = "flatten_ok"
path = "tests/flatten_ok.rs"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "itertools"
version = "0.14.0"
version = "0.15.0"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-itertools/itertools"
+1 -1
View File
@@ -8,7 +8,7 @@ How to use with Cargo:
```toml
[dependencies]
itertools = "0.14.0"
itertools = "0.15.0"
```
How to use in your crate:
@@ -30,11 +30,42 @@ fn comb_replacement_n10_k10(c: &mut Criterion) {
})
});
}
fn array_comb_replacement_n10_k5(c: &mut Criterion) {
c.bench_function("array comb replacement n10k5", move |b| {
b.iter(|| {
for i in (0..10).array_combinations_with_replacement::<5>() {
black_box(i);
}
})
});
}
fn array_comb_replacement_n5_k10(c: &mut Criterion) {
c.bench_function("array comb replacement n5 k10", move |b| {
b.iter(|| {
for i in (0..5).array_combinations_with_replacement::<10>() {
black_box(i);
}
})
});
}
fn array_comb_replacement_n10_k10(c: &mut Criterion) {
c.bench_function("array comb replacement n10 k10", move |b| {
b.iter(|| {
for i in (0..10).array_combinations_with_replacement::<10>() {
black_box(i);
}
})
});
}
criterion_group!(
benches,
comb_replacement_n10_k5,
comb_replacement_n5_k10,
comb_replacement_n10_k10,
array_comb_replacement_n10_k5,
array_comb_replacement_n5_k10,
array_comb_replacement_n10_k10,
);
criterion_main!(benches);
+2 -2
View File
@@ -8,7 +8,7 @@ const fn calc_iters(n: usize) -> usize {
}
fn powerset_n(c: &mut Criterion, n: usize) {
let id = format!("powerset {}", n);
let id = format!("powerset {n}");
c.bench_function(id.as_str(), move |b| {
b.iter(|| {
for _ in 0..calc_iters(n) {
@@ -21,7 +21,7 @@ fn powerset_n(c: &mut Criterion, n: usize) {
}
fn powerset_n_fold(c: &mut Criterion, n: usize) {
let id = format!("powerset {} fold", n);
let id = format!("powerset {n} fold");
c.bench_function(id.as_str(), move |b| {
b.iter(|| {
for _ in 0..calc_iters(n) {
+32 -8
View File
@@ -355,29 +355,29 @@ bench_specializations! {
}
v.iter().copied().update(|x| *x *= 7)
}
tuple_combinations1 {
array_combinations1 {
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuple_combinations::<(_,)>()
v.iter().array_combinations::<1>()
}
tuple_combinations2 {
array_combinations2 {
{
let v = black_box(vec![0; 64]);
}
v.iter().tuple_combinations::<(_, _)>()
v.iter().array_combinations::<2>()
}
tuple_combinations3 {
array_combinations3 {
{
let v = black_box(vec![0; 64]);
}
v.iter().tuple_combinations::<(_, _, _)>()
v.iter().array_combinations::<3>()
}
tuple_combinations4 {
array_combinations4 {
{
let v = black_box(vec![0; 64]);
}
v.iter().tuple_combinations::<(_, _, _, _)>()
v.iter().array_combinations::<4>()
}
intersperse {
{
@@ -441,6 +441,30 @@ bench_specializations! {
}
v.iter().combinations_with_replacement(4)
}
array_combinations_with_replacement1 {
{
let v = black_box(vec![0; 4096]);
}
v.iter().array_combinations_with_replacement::<1>()
}
array_combinations_with_replacement2 {
{
let v = black_box(vec![0; 90]);
}
v.iter().array_combinations_with_replacement::<2>()
}
array_combinations_with_replacement3 {
{
let v = black_box(vec![0; 28]);
}
v.iter().array_combinations_with_replacement::<3>()
}
array_combinations_with_replacement4 {
{
let v = black_box(vec![0; 16]);
}
v.iter().array_combinations_with_replacement::<4>()
}
permutations1 {
{
let v = black_box(vec![0; 1024]);
+2 -2
View File
@@ -12,8 +12,8 @@ trait IterEx: Iterator {
Self: Sized,
{
let hint = self.size_hint().0;
let cap = std::mem::size_of::<usize>() * 8 - hint.leading_zeros() as usize;
let mut stack = Vec::with_capacity(cap);
let capacity = usize::BITS - hint.leading_zeros();
let mut stack = Vec::with_capacity(capacity as usize);
self.enumerate().for_each(|(mut i, mut x)| {
while (i & 1) != 0 {
x = f(stack.pop().unwrap(), x);
+28 -28
View File
@@ -7,8 +7,8 @@ const N2: usize = 448;
const N3: usize = 86;
const N4: usize = 41;
fn tuple_comb_for1(c: &mut Criterion) {
c.bench_function("tuple comb for1", move |b| {
fn array_comb_for1(c: &mut Criterion) {
c.bench_function("array comb for1", move |b| {
b.iter(|| {
for i in 0..N1 {
black_box(i);
@@ -17,8 +17,8 @@ fn tuple_comb_for1(c: &mut Criterion) {
});
}
fn tuple_comb_for2(c: &mut Criterion) {
c.bench_function("tuple comb for2", move |b| {
fn array_comb_for2(c: &mut Criterion) {
c.bench_function("array comb for2", move |b| {
b.iter(|| {
for i in 0..N2 {
for j in (i + 1)..N2 {
@@ -29,8 +29,8 @@ fn tuple_comb_for2(c: &mut Criterion) {
});
}
fn tuple_comb_for3(c: &mut Criterion) {
c.bench_function("tuple comb for3", move |b| {
fn array_comb_for3(c: &mut Criterion) {
c.bench_function("array comb for3", move |b| {
b.iter(|| {
for i in 0..N3 {
for j in (i + 1)..N3 {
@@ -43,8 +43,8 @@ fn tuple_comb_for3(c: &mut Criterion) {
});
}
fn tuple_comb_for4(c: &mut Criterion) {
c.bench_function("tuple comb for4", move |b| {
fn array_comb_for4(c: &mut Criterion) {
c.bench_function("array comb for4", move |b| {
b.iter(|| {
for i in 0..N4 {
for j in (i + 1)..N4 {
@@ -59,40 +59,40 @@ fn tuple_comb_for4(c: &mut Criterion) {
});
}
fn tuple_comb_c1(c: &mut Criterion) {
c.bench_function("tuple comb c1", move |b| {
fn array_comb_c1(c: &mut Criterion) {
c.bench_function("array comb c1", move |b| {
b.iter(|| {
for (i,) in (0..N1).tuple_combinations() {
for [i] in (0..N1).array_combinations() {
black_box(i);
}
})
});
}
fn tuple_comb_c2(c: &mut Criterion) {
c.bench_function("tuple comb c2", move |b| {
fn array_comb_c2(c: &mut Criterion) {
c.bench_function("array comb c2", move |b| {
b.iter(|| {
for (i, j) in (0..N2).tuple_combinations() {
for [i, j] in (0..N2).array_combinations() {
black_box(i + j);
}
})
});
}
fn tuple_comb_c3(c: &mut Criterion) {
c.bench_function("tuple comb c3", move |b| {
fn array_comb_c3(c: &mut Criterion) {
c.bench_function("array comb c3", move |b| {
b.iter(|| {
for (i, j, k) in (0..N3).tuple_combinations() {
for [i, j, k] in (0..N3).array_combinations() {
black_box(i + j + k);
}
})
});
}
fn tuple_comb_c4(c: &mut Criterion) {
c.bench_function("tuple comb c4", move |b| {
fn array_comb_c4(c: &mut Criterion) {
c.bench_function("array comb c4", move |b| {
b.iter(|| {
for (i, j, k, l) in (0..N4).tuple_combinations() {
for [i, j, k, l] in (0..N4).array_combinations() {
black_box(i + j + k + l);
}
})
@@ -101,13 +101,13 @@ fn tuple_comb_c4(c: &mut Criterion) {
criterion_group!(
benches,
tuple_comb_for1,
tuple_comb_for2,
tuple_comb_for3,
tuple_comb_for4,
tuple_comb_c1,
tuple_comb_c2,
tuple_comb_c3,
tuple_comb_c4,
array_comb_for1,
array_comb_for2,
array_comb_for3,
array_comb_for4,
array_comb_c1,
array_comb_c2,
array_comb_c3,
array_comb_c4,
);
criterion_main!(benches);
+4 -4
View File
@@ -65,7 +65,7 @@ fn main() {
});
let mut irises = match irises {
Err(e) => {
println!("Error parsing: {:?}", e);
println!("Error parsing: {e:?}");
std::process::exit(1);
}
Ok(data) => data,
@@ -99,9 +99,9 @@ fn main() {
let n = 30; // plot size
let mut plot = vec![' '; n * n];
// using Itertools::tuple_combinations
for (a, b) in (0..4).tuple_combinations() {
println!("Column {} vs {}:", a, b);
// using Itertools::array_combinations
for [a, b] in (0..4).array_combinations() {
println!("Column {a} vs {b}:");
// Clear plot
//
+4 -2
View File
@@ -74,9 +74,9 @@ where
fn size_hint(&self) -> (usize, Option<usize>) {
let (low, hi) = size_hint::add_scalar(
self.iter.size_hint(),
matches!(self.last, Some(Some(_))) as usize,
usize::from(matches!(self.last, Some(Some(_)))),
);
((low > 0) as usize, hi)
(usize::from(low > 0), hi)
}
fn fold<Acc, FnAcc>(self, acc: Acc, mut fn_acc: FnAcc) -> Acc
@@ -110,8 +110,10 @@ where
{
}
#[derive(Debug)]
pub struct NoCount;
#[derive(Debug)]
pub struct WithCount;
pub trait CountItem<T> {
+7 -7
View File
@@ -300,11 +300,11 @@ where
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
// Not ExactSizeIterator because size may be larger than usize
size_hint::add_scalar(self.iter.size_hint(), self.top.is_some() as usize)
size_hint::add_scalar(self.iter.size_hint(), usize::from(self.top.is_some()))
}
fn count(self) -> usize {
self.iter.count() + (self.top.is_some() as usize)
self.iter.count() + usize::from(self.top.is_some())
}
fn last(self) -> Option<Self::Item> {
@@ -522,8 +522,8 @@ where
debug_fmt_fields!(TakeWhileRef, iter);
}
/// Create a new `TakeWhileRef` from a reference to clonable iterator.
pub fn take_while_ref<I, F>(iter: &mut I, f: F) -> TakeWhileRef<I, F>
/// Create a new `TakeWhileRef` from a reference to cloneable iterator.
pub fn take_while_ref<I, F>(iter: &mut I, f: F) -> TakeWhileRef<'_, I, F>
where
I: Iterator + Clone,
{
@@ -626,10 +626,10 @@ pub trait HasCombination<I>: Sized {
type Combination: From<I> + Iterator<Item = Self>;
}
/// Create a new `TupleCombinations` from a clonable iterator.
/// Create a new `TupleCombinations` from a cloneable iterator.
pub fn tuple_combinations<T, I>(iter: I) -> TupleCombinations<I, T>
where
I: Iterator + Clone,
I: Iterator,
I::Item: Clone,
T: HasCombination<I>,
{
@@ -992,7 +992,7 @@ fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>>
}
}
/// Create a new `FilterOk` iterator.
/// Create a new `FilterMapOk` iterator.
pub fn filter_map_ok<I, F, T, U, E>(iter: I, f: F) -> FilterMapOk<I, F>
where
I: Iterator<Item = Result<T, E>>,
+31
View File
@@ -0,0 +1,31 @@
#[cfg(doc)]
use crate::Itertools;
#[cfg(feature = "use_std")]
use std::error::Error;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
/// Value returned for the error case of [`Itertools::all_equal_value`].
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct AllEqualValueError<Item>(pub Option<[Item; 2]>);
impl<Item> Display for AllEqualValueError<Item> {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match self.0 {
None => {
write!(
f,
"got zero elements when all elements were expected to be equal"
)
}
Some([_, _]) => {
write!(
f,
"got different elements when all elements were expected to be equal"
)
}
}
}
}
#[cfg(feature = "use_std")]
impl<Item> Error for AllEqualValueError<Item> where Item: Debug {}
+226
View File
@@ -0,0 +1,226 @@
use crate::Itertools;
use std::iter::Fuse;
/// An iterator over all contiguous windows of the input iterator,
/// producing arrays of a specific size.
///
/// See [`.array_windows()`](crate::Itertools::array_windows) for more
/// information.
#[derive(Debug, Clone)]
pub struct ArrayWindows<I, const N: usize>
where
I: Iterator + Sized,
I::Item: Clone,
{
iter: Fuse<I>,
inner: Option<ArrayWindowsInner<I::Item, N>>,
}
#[derive(Debug, Clone)]
struct ArrayWindowsInner<T: Clone, const N: usize> {
// `window` stores the `N` items delivered in the most
// recent output window. It is stored in the form of a ring
// buffer, with `window_start` identifying the element
// that logically comes first.
window: [T; N],
window_start: usize,
}
impl<T: Clone, const N: usize> ArrayWindowsInner<T, N> {
/// Replace the least recent item in `window` with a new
/// item.
fn add_to_buffer(&mut self, item: T) {
if N > 0 {
self.window[self.window_start] = item;
self.window_start = (self.window_start + 1) % N;
}
}
/// Construct an array window to return.
fn make_window(&self) -> [T; N] {
std::array::from_fn(|i| self.window[(i + self.window_start) % N].clone())
}
}
impl<I, const N: usize> Iterator for ArrayWindows<I, N>
where
I: Iterator + Sized,
I::Item: Clone,
{
type Item = [I::Item; N];
fn next(&mut self) -> Option<[I::Item; N]> {
match &mut self.inner {
// Initialisation code, when next() is called for the first time
None => match self.iter.next_array() {
None => {
// The input iterator was completely empty
None
}
Some(buf) => {
let inner = ArrayWindowsInner {
window: buf,
window_start: 0,
};
let window = inner.make_window();
self.inner = Some(inner);
Some(window)
}
},
Some(inner) => match self.iter.next() {
Some(item) => {
inner.add_to_buffer(item);
Some(inner.make_window())
}
None => None,
},
}
}
}
pub fn array_windows<I, const N: usize>(iter: I) -> ArrayWindows<I, N>
where
I: Iterator + Sized,
I::Item: Clone,
{
ArrayWindows {
iter: iter.fuse(),
inner: None,
}
}
/// An iterator over all windows, wrapping back to the first elements when the
/// window would otherwise exceed the length of the iterator, producing arrays
/// of a specific size.
///
/// See [`.circular_array_windows()`](crate::Itertools::circular_array_windows)
/// for more information.
#[derive(Debug, Clone)]
pub struct CircularArrayWindows<I, const N: usize>
where
I: Iterator + Sized,
I::Item: Clone,
{
iter: Fuse<I>,
inner: Option<CircularArrayWindowsInner<I::Item, N>>,
}
#[derive(Debug, Clone)]
struct CircularArrayWindowsInner<T: Clone, const N: usize> {
// `prefix` stores the first `N` items output from this iterator.
// If the input contained fewer than `N` items, then it is filled
// with clones of the previous items in a cycle.
//
// `prefix_pos` tracks the number of items that have been _used_
// from `prefix`. It begins counting up from 0 once the input runs
// out. (So in the case where the input iterator is shorter than
// `N`, it will begin counting up before `prefix` has even been
// populated during setup.)
prefix: [T; N],
prefix_pos: usize,
// For delivering the output arrays, we reuse `ArrayWindowsInner`
// unchanged.
arraywin: ArrayWindowsInner<T, N>,
}
impl<I, const N: usize> Iterator for CircularArrayWindows<I, N>
where
I: Iterator + Sized,
I::Item: Clone,
{
type Item = [I::Item; N];
fn next(&mut self) -> Option<[I::Item; N]> {
match &mut self.inner {
// Initialisation code, when next() is called for the first time
None => match self.iter.next() {
None => {
// The input iterator was completely empty
None
}
Some(first) => {
// We have at least one item, so we can definitely
// populate `prefix` (even if we have to make N
// copies of this element).
// Construct [Option<T>; N] and convert to [T; N]
// once it's full. TODO: can this be improved?
let mut items = std::array::from_fn(|_| None);
let mut prefix_pos = 0;
if N > 0 {
// The first item stored is the one passed to
// us from our caller.
items[0] = Some(first);
}
for i in 1..N {
// Populate the remaining slots in `items`
// from the input iterator.
items[i] = self.iter.next();
if items[i].is_none() {
// If the input iterator runs out early,
// populate the rest of `items` by
// recycling from the beginning, and set
// `prefix_pos` to indicate that we have
// already consumed those items.
for j in i..N {
items[j] = items[j - i].clone();
}
prefix_pos = N - i;
break;
}
}
let items = items.map(Option::unwrap);
let inner = CircularArrayWindowsInner {
prefix: items.clone(),
prefix_pos,
arraywin: ArrayWindowsInner {
window: items,
window_start: 0,
},
};
let window = inner.arraywin.make_window();
self.inner = Some(inner);
Some(window)
}
},
Some(inner) => {
// Normal case. Read the next item in the logical
// input sequence (consisting of the contents of the
// input iterator followed by N-1 items recycling from
// the beginning), and add it to the ring buffer.
let item = if let Some(item) = self.iter.next() {
// Read from the input iterator.
item
} else {
assert!(N == 0 || inner.prefix_pos < N);
if N == 0 || inner.prefix_pos + 1 == N {
// The input iterator has run out, and we've
// emitted as many windows as we read items,
// so we've finished.
return None;
}
let item = inner.prefix[inner.prefix_pos].clone();
inner.prefix_pos += 1;
item
};
inner.arraywin.add_to_buffer(item);
Some(inner.arraywin.make_window())
}
}
}
}
pub fn circular_array_windows<I, const N: usize>(iter: I) -> CircularArrayWindows<I, N>
where
I: Iterator + Sized,
I::Item: Clone,
{
CircularArrayWindows {
iter: iter.fuse(),
inner: None,
}
}
+12 -2
View File
@@ -1,3 +1,4 @@
use alloc::boxed::Box;
use core::array;
use core::borrow::BorrowMut;
use std::fmt;
@@ -13,7 +14,7 @@ pub type Combinations<I> = CombinationsGeneric<I, Vec<usize>>;
/// Iterator for const generic combinations returned by [`.array_combinations()`](crate::Itertools::array_combinations)
pub type ArrayCombinations<I, const K: usize> = CombinationsGeneric<I, [usize; K]>;
/// Create a new `Combinations` from a clonable iterator.
/// Create a new `Combinations` from a cloneable iterator.
pub fn combinations<I: Iterator>(iter: I, k: usize) -> Combinations<I>
where
I::Item: Clone,
@@ -21,7 +22,7 @@ where
Combinations::new(iter, (0..k).collect())
}
/// Create a new `ArrayCombinations` from a clonable iterator.
/// Create a new `ArrayCombinations` from a cloneable iterator.
pub fn array_combinations<I: Iterator, const K: usize>(iter: I) -> ArrayCombinations<I, K>
where
I::Item: Clone,
@@ -52,7 +53,16 @@ pub trait PoolIndex<T>: BorrowMut<[usize]> {
self.borrow().len()
}
}
impl<T> PoolIndex<T> for Box<[usize]> {
type Item = Vec<T>;
fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> Vec<T>
where
T: Clone,
{
pool.get_at(self)
}
}
impl<T> PoolIndex<T> for Vec<usize> {
type Item = Vec<T>;
@@ -1,52 +1,62 @@
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;
use crate::combinations::PoolIndex;
/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
///
/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
/// for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct CombinationsWithReplacement<I>
pub struct CombinationsWithReplacementGeneric<I, Idx>
where
I: Iterator,
I::Item: Clone,
{
indices: Box<[usize]>,
indices: Idx,
pool: LazyBuffer<I>,
first: bool,
}
impl<I> fmt::Debug for CombinationsWithReplacement<I>
/// Iterator for `Box<[I]>` valued combinations_with_replacement returned by [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
pub type CombinationsWithReplacement<I> = CombinationsWithReplacementGeneric<I, Box<[usize]>>;
/// Iterator for const generic combinations_with_replacement returned by [`.array_combinations_with_replacement()`](crate::Itertools::array_combinations_with_replacement)
pub type ArrayCombinationsWithReplacement<I, const K: usize> =
CombinationsWithReplacementGeneric<I, [usize; K]>;
impl<I, Idx> fmt::Debug for CombinationsWithReplacementGeneric<I, Idx>
where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug + Clone,
Idx: fmt::Debug,
{
debug_fmt_fields!(CombinationsWithReplacement, indices, pool, first);
debug_fmt_fields!(CombinationsWithReplacementGeneric, indices, pool, first);
}
/// Create a new `CombinationsWithReplacement` from a clonable iterator.
/// Create a new `ArrayCombinationsWithReplacement`` from a cloneable iterator.
pub fn array_combinations_with_replacement<I: Iterator, const K: usize>(
iter: I,
) -> ArrayCombinationsWithReplacement<I, K>
where
I::Item: Clone,
{
ArrayCombinationsWithReplacement::new(iter, [0; K])
}
/// Create a new `CombinationsWithReplacement` from a cloneable iterator.
pub fn combinations_with_replacement<I>(iter: I, k: usize) -> CombinationsWithReplacement<I>
where
I: Iterator,
I::Item: Clone,
{
let indices = alloc::vec![0; k].into_boxed_slice();
let pool: LazyBuffer<I> = LazyBuffer::new(iter);
CombinationsWithReplacement {
indices,
pool,
first: true,
}
CombinationsWithReplacementGeneric::new(iter, indices)
}
impl<I> CombinationsWithReplacement<I>
impl<I: Iterator, Idx: PoolIndex<I::Item>> CombinationsWithReplacementGeneric<I, Idx>
where
I: Iterator,
I::Item: Clone,
@@ -62,7 +72,8 @@ where
// Work out where we need to update our indices
let mut increment = None;
for (i, indices_int) in self.indices.iter().enumerate().rev() {
let indices: &mut [usize] = self.indices.borrow_mut();
for (i, indices_int) in indices.iter().enumerate().rev() {
if *indices_int < self.pool.len() - 1 {
increment = Some((i, indices_int + 1));
break;
@@ -73,39 +84,48 @@ where
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);
indices[increment_from..].fill(increment_value);
false
}
// Otherwise, we're done
None => true,
}
}
/// 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<I> Iterator for CombinationsWithReplacement<I>
impl<I, Idx> Iterator for CombinationsWithReplacementGeneric<I, Idx>
where
I: Iterator,
I::Item: Clone,
Idx: PoolIndex<I::Item>,
{
type Item = Vec<I::Item>;
type Item = Idx::Item;
fn next(&mut self) -> Option<Self::Item> {
if self.first {
// In empty edge cases, stop iterating immediately
if !(self.indices.is_empty() || self.pool.get_next()) {
if !(self.indices.borrow().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))
Some(self.indices.extract_item(&self.pool))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
if self.first {
// In empty edge cases, stop iterating immediately
if !(self.indices.is_empty() || self.pool.get_next()) {
if !(self.indices.borrow().is_empty() || self.pool.get_next()) {
return None;
}
self.first = false;
@@ -117,13 +137,13 @@ where
return None;
}
}
Some(self.pool.get_at(&self.indices))
Some(self.indices.extract_item(&self.pool))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (mut low, mut upp) = self.pool.size_hint();
low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX);
upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices));
low = 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)
}
@@ -134,14 +154,15 @@ where
first,
} = self;
let n = pool.count();
remaining_for(n, first, &indices).unwrap()
remaining_for(n, first, indices.borrow()).unwrap()
}
}
impl<I> FusedIterator for CombinationsWithReplacement<I>
impl<I, Idx> FusedIterator for CombinationsWithReplacementGeneric<I, Idx>
where
I: Iterator,
I::Item: Clone,
Idx: PoolIndex<I::Item>,
{
}
+37 -19
View File
@@ -1,31 +1,39 @@
use core::hash::BuildHasher;
use std::collections::hash_map::RandomState;
use std::hash::Hash;
mod private {
use core::hash::BuildHasher;
use std::collections::hash_map::RandomState;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct DuplicatesBy<I: Iterator, Key, F> {
pub struct DuplicatesBy<I: Iterator, Key, F, S = RandomState>
where
S: BuildHasher,
{
pub(crate) iter: I,
pub(crate) meta: Meta<Key, F>,
pub(crate) meta: Meta<Key, F, S>,
}
impl<I, V, F> fmt::Debug for DuplicatesBy<I, V, F>
impl<I, V, F, S> fmt::Debug for DuplicatesBy<I, V, F, S>
where
I: Iterator + fmt::Debug,
V: fmt::Debug + Hash + Eq,
S: BuildHasher,
{
debug_fmt_fields!(DuplicatesBy, iter, meta.used);
}
impl<I: Iterator, Key: Eq + Hash, F> DuplicatesBy<I, Key, F> {
pub(crate) fn new(iter: I, key_method: F) -> Self {
impl<I: Iterator, Key: Eq + Hash, F, S: BuildHasher> DuplicatesBy<I, Key, F, S> {
pub(crate) fn new(iter: I, key_method: F, hash_builder: S) -> Self {
Self {
iter,
meta: Meta {
used: HashMap::new(),
used: HashMap::with_hasher(hash_builder),
pending: 0,
key_method,
},
@@ -34,15 +42,16 @@ mod private {
}
#[derive(Clone)]
pub struct Meta<Key, F> {
used: HashMap<Key, bool>,
pub struct Meta<Key, F, S> {
used: HashMap<Key, bool, S>,
pending: usize,
key_method: F,
}
impl<Key, F> Meta<Key, F>
impl<Key, F, S> Meta<Key, F, S>
where
Key: Eq + Hash,
S: BuildHasher,
{
/// Takes an item and returns it back to the caller if it's the second time we see it.
/// Otherwise the item is consumed and None is returned
@@ -68,11 +77,12 @@ mod private {
}
}
impl<I, Key, F> Iterator for DuplicatesBy<I, Key, F>
impl<I, Key, F, S> Iterator for DuplicatesBy<I, Key, F, S>
where
I: Iterator,
Key: Eq + Hash,
F: KeyMethod<Key, I::Item>,
S: BuildHasher,
{
type Item = I::Item;
@@ -102,11 +112,12 @@ mod private {
}
}
impl<I, Key, F> DoubleEndedIterator for DuplicatesBy<I, Key, F>
impl<I, Key, F, S> DoubleEndedIterator for DuplicatesBy<I, Key, F, S>
where
I: DoubleEndedIterator,
Key: Eq + Hash,
F: KeyMethod<Key, I::Item>,
S: BuildHasher,
{
fn next_back(&mut self) -> Option<Self::Item> {
let Self { iter, meta } = self;
@@ -189,28 +200,35 @@ mod private {
/// An iterator adapter to filter for duplicate elements.
///
/// See [`.duplicates_by()`](crate::Itertools::duplicates_by) for more information.
pub type DuplicatesBy<I, V, F> = private::DuplicatesBy<I, V, private::ByFn<F>>;
pub type DuplicatesBy<I, V, F, S = RandomState> = private::DuplicatesBy<I, V, private::ByFn<F>, S>;
/// Create a new `DuplicatesBy` iterator.
pub fn duplicates_by<I, Key, F>(iter: I, f: F) -> DuplicatesBy<I, Key, F>
/// Create a new `DuplicatesBy` iterator with a specified hash builder.
pub fn duplicates_by_with_hasher<I, Key, F, S>(
iter: I,
f: F,
hash_builder: S,
) -> DuplicatesBy<I, Key, F, S>
where
Key: Eq + Hash,
F: FnMut(&I::Item) -> Key,
I: Iterator,
S: BuildHasher,
{
DuplicatesBy::new(iter, private::ByFn(f))
DuplicatesBy::new(iter, private::ByFn(f), hash_builder)
}
/// An iterator adapter to filter out duplicate elements.
///
/// See [`.duplicates()`](crate::Itertools::duplicates) for more information.
pub type Duplicates<I> = private::DuplicatesBy<I, <I as Iterator>::Item, private::ById>;
pub type Duplicates<I, S = RandomState> =
private::DuplicatesBy<I, <I as Iterator>::Item, private::ById, S>;
/// Create a new `Duplicates` iterator.
pub fn duplicates<I>(iter: I) -> Duplicates<I>
/// Create a new `Duplicates` iterator with a specified hash builder.
pub fn duplicates_with_hasher<I, S>(iter: I, hash_builder: S) -> Duplicates<I, S>
where
I: Iterator,
I::Item: Eq + Hash,
S: BuildHasher,
{
Duplicates::new(iter, private::ById)
Duplicates::new(iter, private::ById, hash_builder)
}
+2 -2
View File
@@ -421,7 +421,7 @@ impl<A, B> EitherOrBoth<A, B> {
/// // 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));
/// assert_eq!(either, Both("what's", 9 + 10));
/// ```
pub fn insert_right(&mut self, val: B) -> &mut B {
match self {
@@ -467,7 +467,7 @@ impl<T> EitherOrBoth<T, T> {
/// 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,
/// This function can be used to preferably 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
+10 -1
View File
@@ -8,7 +8,9 @@ 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()`](crate::Itertools::exactly_one) and
/// [`at_most_one()`](crate::Itertools::at_most_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
@@ -64,6 +66,13 @@ where
size_hint::add_scalar(self.inner.size_hint(), self.additional_len())
}
fn count(self) -> usize
where
Self: Sized,
{
self.additional_len() + self.inner.count()
}
fn fold<B, F>(self, mut init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
+4 -4
View File
@@ -119,7 +119,7 @@ where
/// for (a, b) in zip(&[1, 2, 3, 4, 5], &['a', 'b', 'c']) {
/// result.push((*a, *b));
/// }
/// assert_eq!(result, vec![(1, 'a'),(2, 'b'),(3, 'c')]);
/// 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",
@@ -141,7 +141,7 @@ where
/// ```
/// use itertools::chain;
///
/// let mut result:Vec<i32> = Vec::new();
/// let mut result: Vec<i32> = Vec::new();
///
/// for element in chain(&[1, 2, 3], &[4]) {
/// result.push(*element);
@@ -284,8 +284,8 @@ where
/// [`IntoIterator`] enabled version of [`Itertools::sorted`].
///
/// ```
/// use itertools::sorted;
/// use itertools::assert_equal;
/// use itertools::sorted;
///
/// assert_equal(sorted("rust".chars()), "rstu".chars());
/// ```
@@ -304,8 +304,8 @@ where
/// [`IntoIterator`] enabled version of [`Itertools::sorted_unstable`].
///
/// ```
/// use itertools::sorted_unstable;
/// use itertools::assert_equal;
/// use itertools::sorted_unstable;
///
/// assert_equal(sorted_unstable("rust".chars()), "rstu".chars());
/// ```
+12 -5
View File
@@ -1,5 +1,6 @@
#![cfg(feature = "use_std")]
use core::hash::BuildHasher;
use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;
@@ -8,25 +9,31 @@ use std::iter::Iterator;
///
/// See [`.into_group_map()`](crate::Itertools::into_group_map)
/// for more information.
pub fn into_group_map<I, K, V>(iter: I) -> HashMap<K, Vec<V>>
pub fn into_group_map_with_hasher<I, K, V, S>(iter: I, hash_builder: S) -> HashMap<K, Vec<V>, S>
where
I: Iterator<Item = (K, V)>,
K: Hash + Eq,
S: BuildHasher,
{
let mut lookup = HashMap::new();
let mut lookup = HashMap::<K, Vec<V>, S>::with_hasher(hash_builder);
iter.for_each(|(key, val)| {
lookup.entry(key).or_insert_with(Vec::new).push(val);
lookup.entry(key).or_default().push(val);
});
lookup
}
pub fn into_group_map_by<I, K, V, F>(iter: I, mut f: F) -> HashMap<K, Vec<V>>
pub fn into_group_map_by_with_hasher<I, K, V, F, S>(
iter: I,
mut f: F,
hash_builder: S,
) -> HashMap<K, Vec<V>, S>
where
I: Iterator<Item = V>,
K: Hash + Eq,
F: FnMut(&V) -> K,
S: BuildHasher,
{
into_group_map(iter.map(|v| (f(&v), v)))
into_group_map_with_hasher(iter.map(|v| (f(&v), v)), hash_builder)
}
+78 -18
View File
@@ -1,5 +1,6 @@
use alloc::vec::{self, Vec};
use std::cell::{Cell, RefCell};
use std::fmt::Debug;
/// A trait to unify `FnMut` for `ChunkBy` with the chunk key in `IntoChunks`
trait KeyFunction<A> {
@@ -76,6 +77,27 @@ where
dropped_group: usize,
}
impl<K, I, F> Debug for GroupInner<K, I, F>
where
K: Debug,
I: Iterator + Debug,
I::Item: Debug,
{
debug_fmt_fields!(
GroupInner,
// key, omitted because functions are almost never Debug
iter,
current_key,
current_elt,
done,
top_group,
oldest_buffered_group,
bottom_group,
buffer,
dropped_group
);
}
impl<K, I, F> GroupInner<K, I, F>
where
I: Iterator,
@@ -175,14 +197,11 @@ where
while let Some(elt) = self.next_element() {
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;
}
if let Some(old_key) = self.current_key.take() {
if old_key != key {
self.current_key = Some(key);
first_elt = Some(elt);
break;
}
}
self.current_key = Some(key);
@@ -226,15 +245,12 @@ where
None => None,
Some(elt) => {
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;
}
if let Some(old_key) = self.current_key.take() {
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);
@@ -311,6 +327,15 @@ where
index: Cell<usize>,
}
impl<K, I, F> Debug for ChunkBy<K, I, F>
where
K: Debug,
I: Iterator + Debug,
I::Item: Debug,
{
debug_fmt_fields!(ChunkBy, inner, index);
}
/// Create a new
pub fn new<K, J, F>(iter: J, f: F) -> ChunkBy<K, J::IntoIter, F>
where
@@ -385,6 +410,15 @@ where
parent: &'a ChunkBy<K, I, F>,
}
impl<'a, K, I, F> Debug for Groups<'a, K, I, F>
where
K: Debug,
I: Iterator + Debug,
I::Item: Debug,
{
debug_fmt_fields!(Groups, parent);
}
impl<'a, K, I, F> Iterator for Groups<'a, K, I, F>
where
I: Iterator,
@@ -438,6 +472,15 @@ where
}
}
impl<'a, K, I, F> Debug for Group<'a, K, I, F>
where
K: Debug,
I: Iterator + Debug,
I::Item: Debug,
{
debug_fmt_fields!(Group, parent, index, first);
}
impl<'a, K, I, F> Iterator for Group<'a, K, I, F>
where
I: Iterator,
@@ -479,7 +522,7 @@ where
}
}
/// `ChunkLazy` is the storage for a lazy chunking operation.
/// `IntoChunks` is the storage for a lazy chunking operation.
///
/// `IntoChunks` behaves just like `ChunkBy`: it is iterable, and
/// it only buffers if several chunk iterators are alive at the same time.
@@ -503,6 +546,14 @@ where
index: Cell<usize>,
}
impl<I> Debug for IntoChunks<I>
where
I: Iterator + Debug,
I::Item: Debug,
{
debug_fmt_fields!(IntoChunks, inner, index);
}
impl<I> Clone for IntoChunks<I>
where
I: Clone + Iterator,
@@ -554,6 +605,14 @@ where
parent: &'a IntoChunks<I>,
}
impl<'a, I> Debug for Chunks<'a, I>
where
I: Iterator + Debug,
I::Item: Debug,
{
debug_fmt_fields!(Chunks, parent);
}
impl<'a, I> Iterator for Chunks<'a, I>
where
I: Iterator,
@@ -577,6 +636,7 @@ where
/// An iterator for the elements in a single chunk.
///
/// Iterator element type is `I::Item`.
#[derive(Debug)]
pub struct Chunk<'a, I>
where
I: Iterator + 'a,
+37 -30
View File
@@ -2,11 +2,12 @@ use crate::{
adaptors::map::{MapSpecialCase, MapSpecialCaseFn},
MinMaxResult,
};
use std::cmp::Ordering;
use core::hash::BuildHasher;
use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;
use std::ops::{Add, Mul};
use std::{cmp::Ordering, collections::hash_map::RandomState};
/// A wrapper to allow for an easy [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by)
pub type MapForGrouping<I, F> = MapSpecialCase<I, GroupingMapFn<F>>;
@@ -36,18 +37,19 @@ pub(crate) fn new_map_for_grouping<K, I: Iterator, F: FnMut(&I::Item) -> K>(
}
/// Creates a new `GroupingMap` from `iter`
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
pub fn new<I, K, V, S>(iter: I, hash_builder: S) -> GroupingMap<I, S>
where
I: Iterator<Item = (K, V)>,
K: Hash + Eq,
S: BuildHasher,
{
GroupingMap { iter }
GroupingMap { iter, hash_builder }
}
/// `GroupingMapBy` is an intermediate struct for efficient group-and-fold operations.
///
/// See [`GroupingMap`] for more informations.
pub type GroupingMapBy<I, F> = GroupingMap<MapForGrouping<I, F>>;
/// See [`GroupingMap`] for more information.
pub type GroupingMapBy<I, F, S = RandomState> = GroupingMap<MapForGrouping<I, F>, S>;
/// `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
@@ -56,14 +58,19 @@ pub type GroupingMapBy<I, F> = GroupingMap<MapForGrouping<I, F>>;
/// No method on this struct performs temporary allocations.
#[derive(Clone, Debug)]
#[must_use = "GroupingMap is lazy and do nothing unless consumed"]
pub struct GroupingMap<I> {
pub struct GroupingMap<I, S = RandomState>
where
S: BuildHasher,
{
iter: I,
hash_builder: S,
}
impl<I, K, V> GroupingMap<I>
impl<I, K, V, S> GroupingMap<I, S>
where
I: Iterator<Item = (K, V)>,
K: Hash + Eq,
S: BuildHasher,
{
/// This is the generic way to perform any operation on a `GroupingMap`.
/// It's suggested to use this method only to implement custom operations
@@ -105,11 +112,11 @@ where
/// assert_eq!(lookup[&3], 7);
/// assert_eq!(lookup.len(), 3); // The final keys are only 0, 1 and 2
/// ```
pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R, S>
where
FO: FnMut(Option<R>, &K, V) -> Option<R>,
{
let mut destination_map = HashMap::new();
let mut destination_map = HashMap::with_hasher(self.hash_builder);
self.iter.for_each(|(key, val)| {
let acc = destination_map.remove(&key);
@@ -139,7 +146,7 @@ where
///
/// #[derive(Debug, Default)]
/// struct Accumulator {
/// acc: usize,
/// acc: usize,
/// }
///
/// let lookup = (1..=7)
@@ -154,7 +161,7 @@ where
/// assert_eq!(lookup[&2].acc, 2 + 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn fold_with<FI, FO, R>(self, mut init: FI, mut operation: FO) -> HashMap<K, R>
pub fn fold_with<FI, FO, R>(self, mut init: FI, mut operation: FO) -> HashMap<K, R, S>
where
FI: FnMut(&K, &V) -> R,
FO: FnMut(R, &K, V) -> R,
@@ -190,7 +197,7 @@ where
/// assert_eq!(lookup[&2], 2 + 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn fold<FO, R>(self, init: R, operation: FO) -> HashMap<K, R>
pub fn fold<FO, R>(self, init: R, operation: FO) -> HashMap<K, R, S>
where
R: Clone,
FO: FnMut(R, &K, V) -> R,
@@ -225,7 +232,7 @@ where
/// assert_eq!(lookup[&2], 2 + 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn reduce<FO>(self, mut operation: FO) -> HashMap<K, V>
pub fn reduce<FO>(self, mut operation: FO) -> HashMap<K, V, S>
where
FO: FnMut(V, &K, V) -> V,
{
@@ -239,7 +246,7 @@ where
/// See [`.reduce()`](GroupingMap::reduce).
#[deprecated(note = "Use .reduce() instead", since = "0.13.0")]
pub fn fold_first<FO>(self, operation: FO) -> HashMap<K, V>
pub fn fold_first<FO>(self, operation: FO) -> HashMap<K, V, S>
where
FO: FnMut(V, &K, V) -> V,
{
@@ -264,11 +271,11 @@ where
/// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect::<HashSet<_>>());
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn collect<C>(self) -> HashMap<K, C>
pub fn collect<C>(self) -> HashMap<K, C, S>
where
C: Default + Extend<V>,
{
let mut destination_map = HashMap::new();
let mut destination_map = HashMap::with_hasher(self.hash_builder);
self.iter.for_each(|(key, val)| {
destination_map
@@ -298,7 +305,7 @@ where
/// assert_eq!(lookup[&2], 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn max(self) -> HashMap<K, V>
pub fn max(self) -> HashMap<K, V, S>
where
V: Ord,
{
@@ -324,7 +331,7 @@ where
/// assert_eq!(lookup[&2], 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V>
pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V, S>
where
F: FnMut(&K, &V, &V) -> Ordering,
{
@@ -353,7 +360,7 @@ where
/// assert_eq!(lookup[&2], 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V, S>
where
F: FnMut(&K, &V) -> CK,
CK: Ord,
@@ -379,7 +386,7 @@ where
/// assert_eq!(lookup[&2], 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn min(self) -> HashMap<K, V>
pub fn min(self) -> HashMap<K, V, S>
where
V: Ord,
{
@@ -405,7 +412,7 @@ where
/// assert_eq!(lookup[&2], 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V>
pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V, S>
where
F: FnMut(&K, &V, &V) -> Ordering,
{
@@ -434,7 +441,7 @@ where
/// assert_eq!(lookup[&2], 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V, S>
where
F: FnMut(&K, &V) -> CK,
CK: Ord,
@@ -458,7 +465,7 @@ where
///
/// ```
/// use itertools::Itertools;
/// use itertools::MinMaxResult::{OneElement, MinMax};
/// use itertools::MinMaxResult::{MinMax, OneElement};
///
/// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
/// .into_grouping_map_by(|&n| n % 3)
@@ -469,7 +476,7 @@ where
/// assert_eq!(lookup[&2], OneElement(5));
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn minmax(self) -> HashMap<K, MinMaxResult<V>>
pub fn minmax(self) -> HashMap<K, MinMaxResult<V>, S>
where
V: Ord,
{
@@ -488,7 +495,7 @@ where
///
/// ```
/// use itertools::Itertools;
/// use itertools::MinMaxResult::{OneElement, MinMax};
/// use itertools::MinMaxResult::{MinMax, OneElement};
///
/// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
/// .into_grouping_map_by(|&n| n % 3)
@@ -499,7 +506,7 @@ where
/// assert_eq!(lookup[&2], OneElement(5));
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>>
pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>, S>
where
F: FnMut(&K, &V, &V) -> Ordering,
{
@@ -539,7 +546,7 @@ where
///
/// ```
/// use itertools::Itertools;
/// use itertools::MinMaxResult::{OneElement, MinMax};
/// use itertools::MinMaxResult::{MinMax, OneElement};
///
/// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
/// .into_grouping_map_by(|&n| n % 3)
@@ -550,7 +557,7 @@ where
/// assert_eq!(lookup[&2], OneElement(5));
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>>
pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>, S>
where
F: FnMut(&K, &V) -> CK,
CK: Ord,
@@ -577,7 +584,7 @@ where
/// assert_eq!(lookup[&2], 5 + 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn sum(self) -> HashMap<K, V>
pub fn sum(self) -> HashMap<K, V, S>
where
V: Add<V, Output = V>,
{
@@ -603,7 +610,7 @@ where
/// assert_eq!(lookup[&2], 5 * 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn product(self) -> HashMap<K, V>
pub fn product(self) -> HashMap<K, V, S>
where
V: Mul<V, Output = V>,
{
+3 -3
View File
@@ -80,7 +80,7 @@ where
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 += usize::from(less_than(&heap[child + 1], &heap[child]));
// sift down is done if we are already in order
if !less_than(&heap[child], &heap[pos]) {
@@ -97,7 +97,7 @@ where
}
}
/// An iterator adaptor that merges an abitrary number of base iterators in ascending order.
/// An iterator adaptor that merges an arbitrary number of base iterators in ascending order.
/// If all base iterators are sorted (ascending), the result is sorted.
///
/// Iterator element type is `I::Item`.
@@ -146,7 +146,7 @@ where
kmerge_by(iterable, KMergeByLt)
}
/// An iterator adaptor that merges an abitrary number of base iterators
/// An iterator adaptor that merges an arbitrary number of base iterators
/// according to an ordering function.
///
/// Iterator element type is `I::Item`.
+730 -100
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -24,7 +24,7 @@ impl<T: Clone> MinMaxResult<T> {
/// # Examples
///
/// ```
/// use itertools::MinMaxResult::{self, NoElements, OneElement, MinMax};
/// use itertools::MinMaxResult::{self, MinMax, NoElements, OneElement};
///
/// let r: MinMaxResult<i32> = NoElements;
/// assert_eq!(r.into_option(), None);
+54 -52
View File
@@ -1,4 +1,3 @@
use crate::size_hint;
use std::iter::{Fuse, FusedIterator};
/// An iterator adaptor that pads a sequence to a minimum length by filling
@@ -11,8 +10,9 @@ use std::iter::{Fuse, FusedIterator};
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct PadUsing<I, F> {
iter: Fuse<I>,
min: usize,
pos: usize,
elements_from_next: usize,
elements_from_next_back: usize,
elements_required: usize,
filler: F,
}
@@ -20,19 +20,26 @@ impl<I, F> std::fmt::Debug for PadUsing<I, F>
where
I: std::fmt::Debug,
{
debug_fmt_fields!(PadUsing, iter, min, pos);
debug_fmt_fields!(
PadUsing,
iter,
elements_from_next,
elements_from_next_back,
elements_required
);
}
/// Create a new `PadUsing` iterator.
pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
pub fn pad_using<I, F>(iter: I, elements_required: usize, filler: F) -> PadUsing<I, F>
where
I: Iterator,
F: FnMut(usize) -> I::Item,
{
PadUsing {
iter: iter.fuse(),
min,
pos: 0,
elements_from_next: 0,
elements_from_next_back: 0,
elements_required,
filler,
}
}
@@ -46,38 +53,34 @@ where
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
None => {
if self.pos < self.min {
let e = Some((self.filler)(self.pos));
self.pos += 1;
e
} else {
None
}
}
e => {
self.pos += 1;
e
}
let total_consumed = self.elements_from_next + self.elements_from_next_back;
if total_consumed >= self.elements_required {
self.iter.next()
} else if let Some(e) = self.iter.next() {
self.elements_from_next += 1;
Some(e)
} else {
let e = (self.filler)(self.elements_from_next);
self.elements_from_next += 1;
Some(e)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let tail = self.min.saturating_sub(self.pos);
size_hint::max(self.iter.size_hint(), (tail, Some(tail)))
}
let total_consumed = self.elements_from_next + self.elements_from_next_back;
fn fold<B, G>(self, mut init: B, mut f: G) -> B
where
G: FnMut(B, Self::Item) -> B,
{
let mut pos = self.pos;
init = self.iter.fold(init, |acc, item| {
pos += 1;
f(acc, item)
});
(pos..self.min).map(self.filler).fold(init, f)
if total_consumed >= self.elements_required {
return self.iter.size_hint();
}
let elements_remaining = self.elements_required - total_consumed;
let (low, high) = self.iter.size_hint();
let lower_bound = low.max(elements_remaining);
let upper_bound = high.map(|h| h.max(elements_remaining));
(lower_bound, upper_bound)
}
}
@@ -87,25 +90,24 @@ where
F: FnMut(usize) -> I::Item,
{
fn next_back(&mut self) -> Option<Self::Item> {
if self.min == 0 {
self.iter.next_back()
} else if self.iter.len() >= self.min {
self.min -= 1;
self.iter.next_back()
} else {
self.min -= 1;
Some((self.filler)(self.min))
}
}
let total_consumed = self.elements_from_next + self.elements_from_next_back;
fn rfold<B, G>(self, mut init: B, mut f: G) -> B
where
G: FnMut(B, Self::Item) -> B,
{
init = (self.iter.len()..self.min)
.map(self.filler)
.rfold(init, &mut f);
self.iter.rfold(init, f)
if total_consumed >= self.elements_required {
return self.iter.next_back();
}
let elements_remaining = self.elements_required - total_consumed;
self.elements_from_next_back += 1;
if self.iter.len() < elements_remaining {
Some((self.filler)(
self.elements_required - self.elements_from_next_back,
))
} else {
let item = self.iter.next_back();
debug_assert!(item.is_some()); // If this triggers, we should not have incremented elements_from_next_back, and the input iterator mistakenly reported that it would be able to produce at least elements_remaining items.
item
}
}
}
+74 -1
View File
@@ -92,6 +92,57 @@ where
}
}
#[cfg(feature = "use_alloc")]
impl<T> PeekingNext for ::alloc::vec::IntoIter<T> {
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where
F: FnOnce(&Self::Item) -> bool,
{
match accept(self.as_slice().first()?) {
true => self.next(),
false => None,
}
}
}
#[cfg(feature = "use_alloc")]
impl<'a, T> PeekingNext for ::alloc::vec::Drain<'a, T> {
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where
F: FnOnce(&Self::Item) -> bool,
{
match accept(self.as_slice().first()?) {
true => self.next(),
false => None,
}
}
}
#[cfg(feature = "use_alloc")]
impl<'a> PeekingNext for ::alloc::string::Drain<'a> {
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where
F: FnOnce(&Self::Item) -> bool,
{
match accept(&self.as_str().chars().next()?) {
true => self.next(),
false => None,
}
}
}
impl<T, const N: usize> PeekingNext for ::core::array::IntoIter<T, N> {
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where
F: FnOnce(&Self::Item) -> bool,
{
match accept(self.as_slice().first()?) {
true => self.next(),
false => None,
}
}
}
impl<T: Clone> PeekingNext for RepeatN<T> {
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where
@@ -126,7 +177,7 @@ where
}
/// Create a `PeekingTakeWhile`
pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F>
pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<'_, I, F>
where
I: Iterator,
{
@@ -191,11 +242,33 @@ peeking_next_by_clone! { ['a] ::std::str::Bytes<'a> }
peeking_next_by_clone! { ['a, T] ::std::option::Iter<'a, T> }
peeking_next_by_clone! { ['a, T] ::std::result::Iter<'a, T> }
peeking_next_by_clone! { [T] ::std::iter::Empty<T> }
#[cfg(feature = "use_alloc")]
peeking_next_by_clone! { ['a, T] alloc::collections::linked_list::Iter<'a, T> }
#[cfg(feature = "use_alloc")]
peeking_next_by_clone! { ['a, T] alloc::collections::vec_deque::Iter<'a, T> }
#[cfg(feature = "use_alloc")]
peeking_next_by_clone! { ['a, K, V] alloc::collections::btree_map::Iter<'a, K, V> }
#[cfg(feature = "use_alloc")]
peeking_next_by_clone! { ['a, K, V] alloc::collections::btree_map::Keys<'a, K, V> }
#[cfg(feature = "use_alloc")]
peeking_next_by_clone! { ['a, K, V] alloc::collections::btree_map::Values<'a, K, V> }
#[cfg(feature = "use_alloc")]
peeking_next_by_clone! { ['a, T] alloc::collections::btree_set::Iter<'a, T> }
#[cfg(feature = "use_alloc")]
peeking_next_by_clone! { ['a, T] alloc::collections::binary_heap::Iter<'a, T> }
#[cfg(feature = "use_std")]
peeking_next_by_clone! { ['a, K, V] std::collections::hash_map::Iter<'a, K, V> }
#[cfg(feature = "use_std")]
peeking_next_by_clone! { ['a, K, V] std::collections::hash_map::Keys<'a, K, V> }
#[cfg(feature = "use_std")]
peeking_next_by_clone! { ['a, K, V] std::collections::hash_map::Values<'a, K, V> }
#[cfg(feature = "use_std")]
peeking_next_by_clone! { ['a, T] std::collections::hash_set::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<I> }
+1 -1
View File
@@ -31,7 +31,7 @@ where
debug_fmt_fields!(Powerset, combs);
}
/// Create a new `Powerset` from a clonable iterator.
/// Create a new `Powerset` from a cloneable iterator.
pub fn powerset<I>(src: I) -> Powerset<I>
where
I: Iterator,
+1 -1
View File
@@ -1,5 +1,4 @@
//! Arithmetic on `Iterator.size_hint()` values.
//!
use std::cmp;
@@ -86,6 +85,7 @@ pub fn min(a: SizeHint, b: SizeHint) -> SizeHint {
(lower, upper)
}
/// Test multiplication of size hints.
#[test]
fn mul_size_hints() {
assert_eq!(mul((3, Some(4)), (3, Some(4))), (9, Some(16)));
+1
View File
@@ -385,6 +385,7 @@ macro_rules! impl_tuple_collect {
count_ident!($($Y)*)
}
/// Shift all elements one position to the left and push the new item to the end.
fn left_shift_push(&mut self, mut item: A) {
use std::mem::replace;
+36 -19
View File
@@ -1,4 +1,6 @@
use core::hash::BuildHasher;
use std::collections::hash_map::Entry;
use std::collections::hash_map::RandomState;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
@@ -9,42 +11,48 @@ use std::iter::FusedIterator;
/// See [`.unique_by()`](crate::Itertools::unique) for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct UniqueBy<I: Iterator, V, F> {
pub struct UniqueBy<I: Iterator, V, F, S = RandomState>
where
S: BuildHasher,
{
iter: I,
// Use a Hashmap for the Entry API in order to prevent hashing twice.
// This can maybe be replaced with a HashSet once `get_or_insert_with`
// or a proper Entry API for Hashset is stable and meets this msrv
used: HashMap<V, ()>,
used: HashMap<V, (), S>,
f: F,
}
impl<I, V, F> fmt::Debug for UniqueBy<I, V, F>
impl<I, V, F, S> fmt::Debug for UniqueBy<I, V, F, S>
where
I: Iterator + fmt::Debug,
V: fmt::Debug + Hash + Eq,
S: BuildHasher,
{
debug_fmt_fields!(UniqueBy, iter, used);
}
/// Create a new `UniqueBy` iterator.
pub fn unique_by<I, V, F>(iter: I, f: F) -> UniqueBy<I, V, F>
pub fn unique_by_with_hasher<I, V, F, S>(iter: I, f: F, hash_builder: S) -> UniqueBy<I, V, F, S>
where
V: Eq + Hash,
F: FnMut(&I::Item) -> V,
I: Iterator,
S: BuildHasher,
{
UniqueBy {
iter,
used: HashMap::new(),
used: HashMap::with_hasher(hash_builder),
f,
}
}
// count the number of new unique keys in iterable (`used` is the set already seen)
fn count_new_keys<I, K>(mut used: HashMap<K, ()>, iterable: I) -> usize
fn count_new_keys<I, K, S>(mut used: HashMap<K, (), S>, iterable: I) -> usize
where
I: IntoIterator<Item = K>,
K: Hash + Eq,
S: BuildHasher,
{
let iter = iterable.into_iter();
let current_used = used.len();
@@ -52,11 +60,12 @@ where
used.len() - current_used
}
impl<I, V, F> Iterator for UniqueBy<I, V, F>
impl<I, V, F, S> Iterator for UniqueBy<I, V, F, S>
where
I: Iterator,
V: Eq + Hash,
F: FnMut(&I::Item) -> V,
S: BuildHasher,
{
type Item = I::Item;
@@ -68,7 +77,7 @@ where
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (low, hi) = self.iter.size_hint();
((low > 0 && self.used.is_empty()) as usize, hi)
(usize::from(low > 0 && self.used.is_empty()), hi)
}
fn count(self) -> usize {
@@ -77,11 +86,12 @@ where
}
}
impl<I, V, F> DoubleEndedIterator for UniqueBy<I, V, F>
impl<I, V, F, S> DoubleEndedIterator for UniqueBy<I, V, F, S>
where
I: DoubleEndedIterator,
V: Eq + Hash,
F: FnMut(&I::Item) -> V,
S: BuildHasher,
{
fn next_back(&mut self) -> Option<Self::Item> {
let Self { iter, used, f } = self;
@@ -89,18 +99,20 @@ where
}
}
impl<I, V, F> FusedIterator for UniqueBy<I, V, F>
impl<I, V, F, S> FusedIterator for UniqueBy<I, V, F, S>
where
I: FusedIterator,
V: Eq + Hash,
F: FnMut(&I::Item) -> V,
S: BuildHasher,
{
}
impl<I> Iterator for Unique<I>
impl<I, S> Iterator for Unique<I, S>
where
I: Iterator,
I::Item: Eq + Hash + Clone,
S: BuildHasher,
{
type Item = I::Item;
@@ -119,7 +131,7 @@ where
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (low, hi) = self.iter.iter.size_hint();
((low > 0 && self.iter.used.is_empty()) as usize, hi)
(usize::from(low > 0 && self.iter.used.is_empty()), hi)
}
fn count(self) -> usize {
@@ -127,10 +139,11 @@ where
}
}
impl<I> DoubleEndedIterator for Unique<I>
impl<I, S> DoubleEndedIterator for Unique<I, S>
where
I: DoubleEndedIterator,
I::Item: Eq + Hash + Clone,
S: BuildHasher,
{
fn next_back(&mut self) -> Option<Self::Item> {
let UniqueBy { iter, used, .. } = &mut self.iter;
@@ -145,10 +158,11 @@ where
}
}
impl<I> FusedIterator for Unique<I>
impl<I, S> FusedIterator for Unique<I, S>
where
I: FusedIterator,
I::Item: Eq + Hash + Clone,
S: BuildHasher,
{
}
@@ -157,31 +171,34 @@ where
/// See [`.unique()`](crate::Itertools::unique) for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Unique<I>
pub struct Unique<I, S = RandomState>
where
I: Iterator,
I::Item: Eq + Hash + Clone,
S: BuildHasher,
{
iter: UniqueBy<I, I::Item, ()>,
iter: UniqueBy<I, I::Item, (), S>,
}
impl<I> fmt::Debug for Unique<I>
impl<I, S> fmt::Debug for Unique<I, S>
where
I: Iterator + fmt::Debug,
I::Item: Hash + Eq + fmt::Debug + Clone,
S: BuildHasher,
{
debug_fmt_fields!(Unique, iter);
}
pub fn unique<I>(iter: I) -> Unique<I>
pub fn unique_with_hasher<I, S>(iter: I, hash_builder: S) -> Unique<I, S>
where
I: Iterator,
I::Item: Eq + Hash + Clone,
S: BuildHasher,
{
Unique {
iter: UniqueBy {
iter,
used: HashMap::new(),
used: HashMap::with_hasher(hash_builder),
f: (),
},
}
+56 -36
View File
@@ -47,44 +47,46 @@ where
///
/// See [`.with_position()`](crate::Itertools::with_position) for more information.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Position {
/// This is the first element.
First,
/// This is neither the first nor the last element.
Middle,
/// This is the last element.
Last,
/// This is the only element.
Only,
pub struct Position {
/// This is the initial element (also true if there's exactly one element)
pub is_first: bool,
/// This is the final element (also true if there's exactly one element)
pub is_last: bool,
}
impl Position {
/// This is the first and the last element at the same time, and there are no more elements
pub fn is_exactly_one(self) -> bool {
self.is_first && self.is_last
}
/// This is neither first nor last element, and there will be more elements
pub fn is_middle(self) -> bool {
!self.is_first && !self.is_last
}
/// This is the initial element (also true if there's exactly one element)
pub fn is_first(self) -> bool {
self.is_first
}
/// This is the final element (also true if there's exactly one element)
pub fn is_last(self) -> bool {
self.is_last
}
}
impl<I: Iterator> Iterator for WithPosition<I> {
type Item = (Position, I::Item);
fn next(&mut self) -> Option<Self::Item> {
match self.peekable.next() {
Some(item) => {
if !self.handled_first {
// Haven't seen the first item yet, and there is one to give.
self.handled_first = true;
// 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)),
}
} 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)),
}
}
}
// Iterator is finished.
None => None,
}
let item = self.peekable.next()?;
let is_last = self.peekable.peek().is_none();
let is_first = !self.handled_first;
self.handled_first = true;
Some((Position { is_first, is_last }, item))
}
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -102,18 +104,36 @@ impl<I: Iterator> Iterator for WithPosition<I> {
match self.peekable.next() {
Some(second) => {
let first = std::mem::replace(&mut head, second);
init = f(init, (Position::First, first));
let position = Position {
is_first: true,
is_last: false,
};
init = f(init, (position, first));
}
None => {
let position = Position {
is_first: true,
is_last: true,
};
return f(init, (position, head));
}
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))
let position = Position {
is_first: false,
is_last: false,
};
f(acc, (position, item))
});
let position = Position {
is_first: false,
is_last: true,
};
// The "head" is now the last item.
init = f(init, (Position::Last, head));
init = f(init, (position, head));
}
init
}
+1 -1
View File
@@ -13,7 +13,7 @@ pub struct Zip<T> {
/// implement [`IntoIterator`]) and yields elements
/// until any of the subiterators yields `None`.
///
/// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to `E` are the
/// The iterator element type is a tuple like `(A, B, ..., E)` where `A` to `E` are the
/// element types of the subiterator.
///
/// **Note:** The result of this function is a value of a named type (`Zip<(I, J,
@@ -49,3 +49,7 @@ fn combinations_no_collect() {
fn combinations_with_replacement_no_collect() {
no_collect_test(|iter| iter.combinations_with_replacement(5))
}
#[test]
fn array_combinations_with_replacement_no_collect() {
no_collect_test(|iter| iter.array_combinations_with_replacement::<5>())
}
+483
View File
@@ -0,0 +1,483 @@
use itertools::Itertools;
#[test]
fn array_windows() {
let [vec0, vec1, vec2, vec4, vec10] = [
vec![],
vec![1],
vec![1, 2],
vec![1, 2, 3, 4],
vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
];
assert_eq!(
vec10
.iter()
.copied()
.array_windows::<2>()
.collect::<Vec<_>>(),
vec![
[1, 2],
[2, 3],
[3, 4],
[4, 5],
[5, 6],
[6, 7],
[7, 8],
[8, 9],
[9, 10],
]
);
assert_eq!(
vec4.iter()
.copied()
.array_windows::<2>()
.collect::<Vec<_>>(),
vec![[1, 2], [2, 3], [3, 4]]
);
assert_eq!(
vec2.iter()
.copied()
.array_windows::<2>()
.collect::<Vec<_>>(),
vec![[1, 2]]
);
assert_eq!(
vec1.iter()
.copied()
.array_windows::<2>()
.collect::<Vec<_>>(),
Vec::<[i32; 2]>::new()
);
assert_eq!(
vec0.iter()
.copied()
.array_windows::<2>()
.collect::<Vec<_>>(),
Vec::<[i32; 2]>::new()
);
assert_eq!(
vec10
.iter()
.copied()
.array_windows::<4>()
.collect::<Vec<_>>(),
vec![
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6],
[4, 5, 6, 7],
[5, 6, 7, 8],
[6, 7, 8, 9],
[7, 8, 9, 10],
]
);
// For zero-length output windows, the equation
//
// output length = input length + N - 1
//
// implies that we return one _more_ zero-length window than there
// are input items, as if we were returning a zero-length window
// for each position between elements of the input list, including
// the positions at the start and end.
assert_eq!(
vec0.iter()
.copied()
.array_windows::<0>()
.collect::<Vec<_>>(),
vec![[]]
);
assert_eq!(
vec1.iter()
.copied()
.array_windows::<0>()
.collect::<Vec<_>>(),
vec![[], []]
);
assert_eq!(
vec2.iter()
.copied()
.array_windows::<0>()
.collect::<Vec<_>>(),
vec![[], [], []]
);
assert_eq!(
vec0.iter()
.copied()
.array_windows::<1>()
.collect::<Vec<_>>(),
Vec::<[i32; 1]>::new()
);
assert_eq!(
vec1.iter()
.copied()
.array_windows::<1>()
.collect::<Vec<_>>(),
vec![[1]]
);
assert_eq!(
vec2.iter()
.copied()
.array_windows::<1>()
.collect::<Vec<_>>(),
vec![[1], [2]]
);
assert_eq!(
vec1.iter()
.copied()
.array_windows::<7>()
.collect::<Vec<_>>(),
Vec::<[i32; 7]>::new()
);
assert_eq!(
vec2.iter()
.copied()
.array_windows::<7>()
.collect::<Vec<_>>(),
Vec::<[i32; 7]>::new()
);
assert_eq!(
vec4.iter()
.copied()
.array_windows::<7>()
.collect::<Vec<_>>(),
Vec::<[i32; 7]>::new()
);
}
#[test]
fn array_windows_equal_tuple_windows() {
fn internal(slice: &[usize]) {
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_,)>()
.map(Into::<[_; 1]>::into),
slice.iter().array_windows::<1>(),
);
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_, _)>()
.map(Into::<[_; 2]>::into),
slice.iter().array_windows::<2>(),
);
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_, _, _)>()
.map(Into::<[_; 3]>::into),
slice.iter().array_windows::<3>(),
);
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_, _, _, _)>()
.map(Into::<[_; 4]>::into),
slice.iter().array_windows::<4>(),
);
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_, _, _, _, _)>()
.map(Into::<[_; 5]>::into),
slice.iter().array_windows::<5>(),
);
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_, _, _, _, _, _)>()
.map(Into::<[_; 6]>::into),
slice.iter().array_windows::<6>(),
);
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_, _, _, _, _, _, _)>()
.map(Into::<[_; 7]>::into),
slice.iter().array_windows::<7>(),
);
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_, _, _, _, _, _, _, _)>()
.map(Into::<[_; 8]>::into),
slice.iter().array_windows::<8>(),
);
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_, _, _, _, _, _, _, _, _)>()
.map(Into::<[_; 9]>::into),
slice.iter().array_windows::<9>(),
);
itertools::assert_equal(
slice
.iter()
.tuple_windows::<(_, _, _, _, _, _, _, _, _, _)>()
.map(Into::<[_; 10]>::into),
slice.iter().array_windows::<10>(),
);
}
for i in 0..100 {
internal(&(0..i).collect::<Vec<_>>());
}
}
#[test]
fn circular_array_windows() {
let [vec0, vec1, vec2, vec4, vec10] = [
vec![],
vec![1],
vec![1, 2],
vec![1, 2, 3, 4],
vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
];
assert_eq!(
vec10
.iter()
.copied()
.circular_array_windows::<2>()
.collect::<Vec<_>>(),
vec![
[1, 2],
[2, 3],
[3, 4],
[4, 5],
[5, 6],
[6, 7],
[7, 8],
[8, 9],
[9, 10],
[10, 1]
]
);
assert_eq!(
vec4.iter()
.copied()
.circular_array_windows::<2>()
.collect::<Vec<_>>(),
vec![[1, 2], [2, 3], [3, 4], [4, 1]]
);
assert_eq!(
vec2.iter()
.copied()
.circular_array_windows::<2>()
.collect::<Vec<_>>(),
vec![[1, 2], [2, 1]]
);
assert_eq!(
vec1.iter()
.copied()
.circular_array_windows::<2>()
.collect::<Vec<_>>(),
vec![[1, 1]]
);
assert_eq!(
vec0.iter()
.copied()
.circular_array_windows::<2>()
.collect::<Vec<_>>(),
Vec::<[i32; 2]>::new()
);
assert_eq!(
vec10
.iter()
.copied()
.circular_array_windows::<4>()
.collect::<Vec<_>>(),
vec![
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6],
[4, 5, 6, 7],
[5, 6, 7, 8],
[6, 7, 8, 9],
[7, 8, 9, 10],
[8, 9, 10, 1],
[9, 10, 1, 2],
[10, 1, 2, 3],
]
);
// For zero-length output windows, we preserve the invariant that
// output length = input length, and return the same number of
// zero-length windows as there were input items, as if we were
// returning a zero-length window for every possible starting
// position in the cyclic input list.
assert_eq!(
vec0.iter()
.copied()
.circular_array_windows::<0>()
.collect::<Vec<_>>(),
Vec::<[i32; 0]>::new()
);
assert_eq!(
vec1.iter()
.copied()
.circular_array_windows::<0>()
.collect::<Vec<_>>(),
vec![[]]
);
assert_eq!(
vec2.iter()
.copied()
.circular_array_windows::<0>()
.collect::<Vec<_>>(),
vec![[], []]
);
assert_eq!(
vec0.iter()
.copied()
.circular_array_windows::<1>()
.collect::<Vec<_>>(),
Vec::<[i32; 1]>::new()
);
assert_eq!(
vec1.iter()
.copied()
.circular_array_windows::<1>()
.collect::<Vec<_>>(),
vec![[1]]
);
assert_eq!(
vec2.iter()
.copied()
.circular_array_windows::<1>()
.collect::<Vec<_>>(),
vec![[1], [2]]
);
assert_eq!(
vec1.iter()
.copied()
.circular_array_windows::<7>()
.collect::<Vec<_>>(),
vec![[1, 1, 1, 1, 1, 1, 1]]
);
assert_eq!(
vec2.iter()
.copied()
.circular_array_windows::<7>()
.collect::<Vec<_>>(),
vec![[1, 2, 1, 2, 1, 2, 1], [2, 1, 2, 1, 2, 1, 2]]
);
assert_eq!(
vec4.iter()
.copied()
.circular_array_windows::<7>()
.collect::<Vec<_>>(),
vec![
[1, 2, 3, 4, 1, 2, 3],
[2, 3, 4, 1, 2, 3, 4],
[3, 4, 1, 2, 3, 4, 1],
[4, 1, 2, 3, 4, 1, 2],
]
);
}
#[test]
fn circular_array_windows_equal_circular_tuple_windows() {
fn internal(slice: &[usize]) {
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_,)>()
.map(Into::<[_; 1]>::into),
slice.iter().circular_array_windows::<1>(),
);
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_, _)>()
.map(Into::<[_; 2]>::into),
slice.iter().circular_array_windows::<2>(),
);
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_, _, _)>()
.map(Into::<[_; 3]>::into),
slice.iter().circular_array_windows::<3>(),
);
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_, _, _, _)>()
.map(Into::<[_; 4]>::into),
slice.iter().circular_array_windows::<4>(),
);
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_, _, _, _, _)>()
.map(Into::<[_; 5]>::into),
slice.iter().circular_array_windows::<5>(),
);
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_, _, _, _, _, _)>()
.map(Into::<[_; 6]>::into),
slice.iter().circular_array_windows::<6>(),
);
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_, _, _, _, _, _, _)>()
.map(Into::<[_; 7]>::into),
slice.iter().circular_array_windows::<7>(),
);
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_, _, _, _, _, _, _, _)>()
.map(Into::<[_; 8]>::into),
slice.iter().circular_array_windows::<8>(),
);
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_, _, _, _, _, _, _, _, _)>()
.map(Into::<[_; 9]>::into),
slice.iter().circular_array_windows::<9>(),
);
itertools::assert_equal(
slice
.iter()
.circular_tuple_windows::<(_, _, _, _, _, _, _, _, _, _)>()
.map(Into::<[_; 10]>::into),
slice.iter().circular_array_windows::<10>(),
);
}
for i in 0..100 {
internal(&(0..i).collect::<Vec<_>>());
}
}
+29
View File
@@ -104,6 +104,18 @@ must_use_tests! {
let _ = Panicking.circular_tuple_windows::<(_, _)>();
let _ = Panicking.circular_tuple_windows::<(_, _, _)>();
}
array_windows {
let _ = Panicking.array_windows::<0>();
let _ = Panicking.array_windows::<1>();
let _ = Panicking.array_windows::<2>();
let _ = Panicking.array_windows::<3>();
}
circular_array_windows {
let _ = Panicking.circular_array_windows::<0>();
let _ = Panicking.circular_array_windows::<1>();
let _ = Panicking.circular_array_windows::<2>();
let _ = Panicking.circular_array_windows::<3>();
}
tuples {
let _ = Panicking.tuples::<(_,)>();
let _ = Panicking.tuples::<(_, _)>();
@@ -196,17 +208,29 @@ must_use_tests! {
while_some {
let _ = Panicking.map(Some).while_some();
}
#[allow(deprecated)]
tuple_combinations1 {
let _ = Panicking.tuple_combinations::<(_,)>();
}
#[allow(deprecated)]
#[should_panic]
tuple_combinations2 {
let _ = Panicking.tuple_combinations::<(_, _)>();
}
#[allow(deprecated)]
#[should_panic]
tuple_combinations3 {
let _ = Panicking.tuple_combinations::<(_, _, _)>();
}
array_combinations1 {
let _ = Panicking.array_combinations::<1>();
}
array_combinations2 {
let _ = Panicking.array_combinations::<2>();
}
array_combinations3 {
let _ = Panicking.array_combinations::<3>();
}
combinations {
let _ = Panicking.combinations(0);
let _ = Panicking.combinations(1);
@@ -217,6 +241,11 @@ must_use_tests! {
let _ = Panicking.combinations_with_replacement(1);
let _ = Panicking.combinations_with_replacement(2);
}
array_combinations_with_replacement {
let _ = Panicking.array_combinations_with_replacement::<0>();
let _ = Panicking.array_combinations_with_replacement::<1>();
let _ = Panicking.array_combinations_with_replacement::<2>();
}
permutations {
let _ = Panicking.permutations(0);
let _ = Panicking.permutations(1);
+198 -28
View File
@@ -275,8 +275,7 @@ where
let actual_count = total_actual_count - i;
if actual_count != returned_count {
println!(
"Total iterations: {} True count: {} returned count: {}",
i, actual_count, returned_count
"Total iterations: {i} True count: {actual_count} returned count: {returned_count}"
);
return false;
@@ -450,7 +449,13 @@ quickcheck! {
}
assert_eq!(answer, actual);
assert_eq!(answer.into_iter().last(), a.multi_cartesian_product().last());
assert_eq!(
{
#[allow(clippy::double_ended_iterator_last)]
answer.into_iter().last()
},
a.multi_cartesian_product().last()
);
}
fn correct_empty_multi_product() -> () {
@@ -993,6 +998,45 @@ quickcheck! {
}
}
quickcheck! {
fn pad_using_next_next_back(v: Vec<i8>) -> () {
let only_oks = v.into_iter().map(Ok).collect::<Vec<_>>();
for elements_required in 0..128 {
let mut oks_then_errs_vector = only_oks.clone();
while oks_then_errs_vector.len() < elements_required {
oks_then_errs_vector.push(Err(oks_then_errs_vector.len()));
}
let oks_then_errs_pad_using = only_oks.iter().copied().pad_using(elements_required, Err);
assert_eq!(
oks_then_errs_pad_using.clone().collect::<Vec<_>>(),
oks_then_errs_vector
);
// Check next, next_back (https://github.com/rust-itertools/itertools/issues/1065)
let mut iter_check = oks_then_errs_vector.into_iter();
let mut iter_real = oks_then_errs_pad_using;
let mut rng = rand::thread_rng();
loop {
let next_or_next_back = if rng.gen_bool(0.5) {
let element_next_check = iter_check.next();
let element_next_real = iter_real.next();
assert_eq!(element_next_check, element_next_real);
element_next_real
} else {
let element_next_back_check = iter_check.next_back();
let element_next_back_real = iter_real.next_back();
assert_eq!(element_next_back_check, element_next_back_real);
element_next_back_real
};
if next_or_next_back.is_none() {
assert!(iter_real.next().is_none());
assert!(iter_real.next_back().is_none());
break;
}
}
}
}
}
quickcheck! {
fn size_powerset(it: Iter<u8, Exact>) -> bool {
// Powerset cardinality gets large very quickly, limit input to keep test fast.
@@ -1059,7 +1103,7 @@ quickcheck! {
let tup1 = |(_, b)| b;
for &(ord, consume_now) in &order {
let iter = &mut [&mut chunks1, &mut chunks2][ord as usize];
let iter = &mut [&mut chunks1, &mut chunks2][usize::from(ord)];
match iter.next() {
Some((_, gr)) => if consume_now {
for og in old_chunks.drain(..) {
@@ -1256,6 +1300,79 @@ quickcheck! {
}
}
// array iterators
quickcheck! {
fn equal_array_windows_0(a: Vec<u8>) -> bool {
let x = (0..=a.len()).map(|_| [&0u8; 0] );
let y = a.iter().array_windows::<0>();
itertools::assert_equal(x,y);
true
}
fn equal_array_windows_1(a: Vec<u8>) -> bool {
let x = a.iter().map(|e| [e] );
let y = a.iter().array_windows::<1>();
itertools::assert_equal(x,y);
true
}
fn equal_array_windows_2(a: Vec<u8>) -> bool {
let x = (0..a.len().saturating_sub(1)).map(|start_idx| [
&a[start_idx],
&a[start_idx + 1],
]);
let y = a.iter().array_windows::<2>();
itertools::assert_equal(x,y);
true
}
fn equal_array_windows_3(a: Vec<u8>) -> bool {
let x = (0..a.len().saturating_sub(2)).map(|start_idx| [
&a[start_idx],
&a[start_idx + 1],
&a[start_idx + 2],
]);
let y = a.iter().array_windows::<3>();
itertools::assert_equal(x,y);
true
}
fn equal_circular_array_windows_0(a: Vec<u8>) -> bool {
let x = a.iter().map(|_| [&0u8; 0] );
let y = a.iter().circular_array_windows::<0>();
itertools::assert_equal(x,y);
true
}
fn equal_circular_array_windows_1(a: Vec<u8>) -> bool {
let x = a.iter().map(|e| [e] );
let y = a.iter().circular_array_windows::<1>();
itertools::assert_equal(x,y);
true
}
fn equal_circular_array_windows_2(a: Vec<u8>) -> bool {
let x = (0..a.len()).map(|start_idx| [
&a[start_idx],
&a[(start_idx + 1) % a.len()],
]);
let y = a.iter().circular_array_windows::<2>();
itertools::assert_equal(x,y);
true
}
fn equal_circular_array_windows_3(a: Vec<u8>) -> bool {
let x = (0..a.len()).map(|start_idx| [
&a[start_idx],
&a[(start_idx + 1) % a.len()],
&a[(start_idx + 2) % a.len()],
]);
let y = a.iter().circular_array_windows::<3>();
itertools::assert_equal(x,y);
true
}
}
// with_position
quickcheck! {
fn with_position_exact_size_1(a: Vec<u8>) -> bool {
@@ -1385,6 +1502,16 @@ quickcheck! {
}
}
quickcheck! {
fn exactly_one_count(a: Vec<i32>) -> TestResult {
let ret = a.iter().cloned().exactly_one();
match a.len() {
1 => TestResult::passed(),
len => TestResult::from_bool(len == ret.unwrap_err().count()),
}
}
}
quickcheck! {
fn at_most_one_i32(a: Vec<i32>) -> TestResult {
let ret = a.iter().cloned().at_most_one();
@@ -1407,9 +1534,9 @@ quickcheck! {
}
fn correct_grouping_map_by_aggregate_modulo_key(a: Vec<u8>, modulo: u8) -> () {
let modulo = if modulo < 2 { 2 } else { modulo } as u64; // Avoid `% 0`
let modulo = u64::from(if modulo < 2 { 2 } else { modulo }); // Avoid `% 0`
let lookup = a.iter()
.map(|&b| b as u64) // Avoid overflows
.map(|&b| u64::from(b)) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.aggregate(|acc, &key, val| {
assert!(val % modulo == key);
@@ -1421,7 +1548,7 @@ quickcheck! {
});
let group_map_lookup = a.iter()
.map(|&b| b as u64)
.map(|&b| u64::from(b))
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
@@ -1441,7 +1568,7 @@ quickcheck! {
assert_eq!(
lookup.get(&m).copied(),
a.iter()
.map(|&b| b as u64)
.map(|&b| u64::from(b))
.filter(|&val| val % modulo == m)
.fold(None, |acc, val| {
if val % (modulo - 1) == 0 {
@@ -1460,8 +1587,8 @@ quickcheck! {
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
let modulo = u64::from(if modulo == 0 { 1 } else { modulo }); // Avoid `% 0`
let lookup = a.iter().map(|&b| u64::from(b)) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.fold_with(|_key, _val| Default::default(), |Accumulator { acc }, &key, val| {
assert!(val % modulo == key);
@@ -1470,7 +1597,7 @@ quickcheck! {
});
let group_map_lookup = a.iter()
.map(|&b| b as u64)
.map(|&b| u64::from(b))
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
@@ -1479,13 +1606,13 @@ quickcheck! {
assert_eq!(lookup, group_map_lookup);
for (&key, &Accumulator { acc: sum }) in lookup.iter() {
assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::<u64>());
assert_eq!(sum, a.iter().map(|&b| u64::from(b)).filter(|&val| val % modulo == key).sum::<u64>());
}
}
fn correct_grouping_map_by_fold_modulo_key(a: Vec<u8>, modulo: u8) -> () {
let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
let modulo = u64::from(if modulo == 0 { 1 } else { modulo }); // Avoid `% 0`
let lookup = a.iter().map(|&b| u64::from(b)) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.fold(0u64, |acc, &key, val| {
assert!(val % modulo == key);
@@ -1493,7 +1620,7 @@ quickcheck! {
});
let group_map_lookup = a.iter()
.map(|&b| b as u64)
.map(|&b| u64::from(b))
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
@@ -1502,13 +1629,13 @@ quickcheck! {
assert_eq!(lookup, group_map_lookup);
for (&key, &sum) in lookup.iter() {
assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::<u64>());
assert_eq!(sum, a.iter().map(|&b| u64::from(b)).filter(|&val| val % modulo == key).sum::<u64>());
}
}
fn correct_grouping_map_by_reduce_modulo_key(a: Vec<u8>, modulo: u8) -> () {
let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
let modulo = u64::from(if modulo == 0 { 1 } else { modulo }); // Avoid `% 0`
let lookup = a.iter().map(|&b| u64::from(b)) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.reduce(|acc, &key, val| {
assert!(val % modulo == key);
@@ -1516,7 +1643,7 @@ quickcheck! {
});
let group_map_lookup = a.iter()
.map(|&b| b as u64)
.map(|&b| u64::from(b))
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
@@ -1525,7 +1652,7 @@ quickcheck! {
assert_eq!(lookup, group_map_lookup);
for (&key, &sum) in lookup.iter() {
assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::<u64>());
assert_eq!(sum, a.iter().map(|&b| u64::from(b)).filter(|&val| val % modulo == key).sum::<u64>());
}
}
@@ -1691,12 +1818,12 @@ quickcheck! {
}
fn correct_grouping_map_by_sum_modulo_key(a: Vec<u8>, modulo: u8) -> () {
let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
let modulo = u64::from(if modulo == 0 { 1 } else { modulo }); // Avoid `% 0`
let lookup = a.iter().map(|&b| u64::from(b)) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.sum();
let group_map_lookup = a.iter().map(|&b| b as u64)
let group_map_lookup = a.iter().map(|&b| u64::from(b))
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
@@ -1705,17 +1832,17 @@ quickcheck! {
assert_eq!(lookup, group_map_lookup);
for (&key, &sum) in lookup.iter() {
assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::<u64>());
assert_eq!(sum, a.iter().map(|&b| u64::from(b)).filter(|&val| val % modulo == key).sum::<u64>());
}
}
fn correct_grouping_map_by_product_modulo_key(a: Vec<u8>, modulo: u8) -> () {
let modulo = Wrapping(if modulo == 0 { 1 } else { modulo } as u64); // Avoid `% 0`
let lookup = a.iter().map(|&b| Wrapping(b as u64)) // Avoid overflows
let modulo = Wrapping(u64::from(if modulo == 0 { 1 } else { modulo })); // Avoid `% 0`
let lookup = a.iter().map(|&b| Wrapping(u64::from(b))) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.product();
let group_map_lookup = a.iter().map(|&b| Wrapping(b as u64))
let group_map_lookup = a.iter().map(|&b| Wrapping(u64::from(b)))
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
@@ -1727,7 +1854,7 @@ quickcheck! {
assert_eq!(
prod,
a.iter()
.map(|&b| Wrapping(b as u64))
.map(|&b| Wrapping(u64::from(b)))
.filter(|&val| val % modulo == key)
.product::<Wrapping<u64>>()
);
@@ -1832,6 +1959,11 @@ quickcheck! {
is_fused(a.combinations_with_replacement(3))
}
fn fused_array_combination_with_replacement(a: Iter<i16>) -> bool
{
is_fused(a.clone().array_combinations_with_replacement::<1>()) &&
is_fused(a.array_combinations_with_replacement::<3>())
}
fn fused_tuple_combination(a: Iter<i16>) -> bool
{
is_fused(a.clone().fuse().tuple_combinations::<(_,)>()) &&
@@ -1966,4 +2098,42 @@ quickcheck! {
itertools::equal(v.iter().tail(n), result)
&& itertools::equal(v.iter().filter(|_| true).tail(n), result)
}
fn strip_prefix_matches_str(haystack: String, needle: String) -> bool {
let expected = haystack.strip_prefix(&needle);
let got: Option<String> = haystack
.chars()
.strip_prefix(needle.chars())
.ok()
.map(Iterator::collect);
got.as_deref() == expected
}
fn strip_prefix_by_matches_strip_prefix(v: Vec<i32>, n: u8) -> bool {
let prefix: Vec<i32> = v.iter().take(n as usize).copied().collect();
let by_eq = v.iter().strip_prefix_by(&prefix, |a, b| **a == **b).ok();
let plain = v.iter().strip_prefix(prefix.iter()).ok();
match (by_eq, plain) {
(Some(a), Some(b)) => itertools::equal(a, b),
(None, None) => true,
_ => false,
}
}
}
#[test]
fn strip_prefix_error_exposes_mismatch_and_remainders() {
let err = (1..6)
.strip_prefix([1, 2, 9, 4])
.expect_err("third item mismatches");
assert_eq!(err.mismatch, (Some(3), 9));
assert_eq!(err.prefix.collect_vec(), vec![4]);
assert_eq!(err.iterator.collect_vec(), vec![4, 5]);
}
#[test]
fn strip_prefix_error_signals_self_exhausted() {
let err = (1..3).strip_prefix([1, 2, 3]).expect_err("self exhausted");
assert_eq!(err.mismatch, (None, 3));
assert!(err.iterator.collect_vec().is_empty());
}
+15 -1
View File
@@ -255,6 +255,7 @@ quickcheck! {
test_double_ended_specializations(&it);
}
#[allow(deprecated)]
fn tuple_combinations(v: Vec<u8>) -> TestResult {
if v.len() > 10 {
return TestResult::discard();
@@ -299,6 +300,16 @@ quickcheck! {
TestResult::passed()
}
fn array_combinations_with_replacement(a: Vec<u8>) -> TestResult {
if a.len() > 10 {
return TestResult::discard();
}
test_specializations(&a.iter().array_combinations_with_replacement::<1>());
test_specializations(&a.iter().array_combinations_with_replacement::<2>());
test_specializations(&a.iter().array_combinations_with_replacement::<3>());
TestResult::passed()
}
fn permutations(a: Vec<u8>, n: u8) -> TestResult {
if n > 3 || a.len() > 8 {
return TestResult::discard();
@@ -503,7 +514,10 @@ quickcheck! {
}
check_results_specialized!(it, |i| i.count());
check_results_specialized!(it, |i| i.last());
check_results_specialized!(it, |i| {
#[allow(clippy::double_ended_iterator_last)]
i.last()
});
check_results_specialized!(it, |i| i.collect::<Vec<_>>());
check_results_specialized!(it, |i| i.rev().collect::<Vec<_>>());
check_results_specialized!(it, |i| {
+2 -1
View File
@@ -226,7 +226,7 @@ fn batching() {
let pit = xs
.iter()
.cloned()
.batching(|it| it.next().and_then(|x| it.next().map(|y| (x, y))));
.batching(|it| it.next_array::<2>().map(|[a, b]| (a, b)));
it::assert_equal(pit, ys.iter().cloned());
}
@@ -281,6 +281,7 @@ fn count_clones() {
let f = Foo { n: Cell::new(0) };
let it = it::repeat_n(f, n);
// drain it
#[allow(clippy::double_ended_iterator_last)]
let last = it.last();
if n == 0 {
assert_eq!(last, None);
+119 -11
View File
@@ -9,6 +9,7 @@ use crate::it::multipeek;
use crate::it::multizip;
use crate::it::peek_nth;
use crate::it::repeat_n;
use crate::it::AllEqualValueError;
use crate::it::ExactlyOneError;
use crate::it::FoldWhile;
use crate::it::Itertools;
@@ -20,8 +21,31 @@ use rand::{
Rng, SeedableRng,
};
use rand::{seq::SliceRandom, thread_rng};
use std::collections::HashMap;
use std::hash::BuildHasher;
use std::hash::RandomState;
use std::iter::empty;
use std::{cmp::min, fmt::Debug, marker::PhantomData};
// A Hasher which forwards it's calls to RandomState to make sure different hashers
// are accepted in the various *_with_hasher methods.
#[derive(Default)]
struct TestHasher(RandomState);
impl TestHasher {
fn new() -> Self {
TestHasher(RandomState::new())
}
}
impl BuildHasher for TestHasher {
type Hasher = <RandomState as BuildHasher>::Hasher;
fn build_hasher(&self) -> Self::Hasher {
self.0.build_hasher()
}
}
#[test]
fn product3() {
let prod = iproduct!(0..3, 0..2, 0..2);
@@ -76,6 +100,10 @@ fn duplicates_by() {
ys_rev.iter(),
xs.iter().duplicates_by(|x| x[..2].to_string()).rev(),
);
let _ = empty::<u8>()
.duplicates_by_with_hasher(|x| *x, TestHasher::new())
.next();
}
#[test]
@@ -103,6 +131,10 @@ fn duplicates() {
);
let ys_rev = vec![2, 1];
assert_eq!(ys_rev, xs.iter().duplicates().rev().cloned().collect_vec());
let _ = empty::<u8>()
.duplicates_with_hasher(TestHasher::new())
.next();
}
#[test]
@@ -119,6 +151,8 @@ fn unique_by() {
ys_rev.iter(),
xs.iter().unique_by(|x| x[..2].to_string()).rev(),
);
let _ = empty::<u8>().unique_by_with_hasher(|x| *x, TestHasher::new());
}
#[test]
@@ -136,6 +170,8 @@ fn unique() {
it::assert_equal(ys.iter(), xs.iter().rev().unique().rev());
let ys_rev = [1, 0];
it::assert_equal(ys_rev.iter(), xs.iter().unique().rev());
let _ = empty::<u8>().unique_with_hasher(TestHasher::new());
}
#[test]
@@ -282,14 +318,17 @@ fn all_equal() {
#[test]
fn all_equal_value() {
assert_eq!("".chars().all_equal_value(), Err(None));
assert_eq!("".chars().all_equal_value(), Err(AllEqualValueError(None)));
assert_eq!("A".chars().all_equal_value(), Ok('A'));
assert_eq!("AABBCCC".chars().all_equal_value(), Err(Some(('A', 'B'))));
assert_eq!(
"AABBCCC".chars().all_equal_value(),
Err(AllEqualValueError(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))));
assert_eq!(result, Err(AllEqualValueError(Some([1, 2]))));
let remaining = it.next();
assert_eq!(remaining, Some(3));
assert!(it.next().is_none());
@@ -301,6 +340,8 @@ fn all_unique() {
assert!("ABCDEFGH".chars().all_unique());
assert!(!"ABCDEFGA".chars().all_unique());
assert!(::std::iter::empty::<usize>().all_unique());
let _ = empty::<u8>().all_unique_with_hasher(TestHasher::new());
}
#[test]
@@ -443,13 +484,13 @@ fn kmerge_empty() {
#[test]
fn kmerge_size_hint() {
let its = (0..5).map(|_| (0..10));
let its = (0..5).map(|_| 0..10);
assert_eq!(its.kmerge().size_hint(), (50, Some(50)));
}
#[test]
fn kmerge_empty_size_hint() {
let its = (0..5).map(|_| (0..0));
let its = (0..5).map(|_| 0..0);
assert_eq!(its.kmerge().size_hint(), (0, Some(0)));
}
@@ -496,7 +537,7 @@ qc::quickcheck! {
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 usize, m as u64);
let (k, m) = (k as usize, u64::from(m));
let mut v: Vec<_> = (n..n.saturating_add(m as _)).collect();
// Generate a random permutation of n..n+m
@@ -532,7 +573,7 @@ qc::quickcheck! {
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 (k, m) = (k as usize, u64::from(m));
let mut v: Vec<_> = (n..n.saturating_add(m as _)).collect();
// Generate a random permutation of n..n+m
@@ -1066,9 +1107,15 @@ fn combinations() {
],
);
it::assert_equal((0..0).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
it::assert_equal((0..1).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
it::assert_equal((0..2).tuple_combinations::<(_, _)>(), vec![(0, 1)]);
#[allow(deprecated)]
{
it::assert_equal((0..0).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
it::assert_equal((0..1).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
it::assert_equal((0..2).tuple_combinations::<(_, _)>(), vec![(0, 1)]);
}
it::assert_equal((0..0).array_combinations::<2>(), <Vec<[isize; 2]>>::new());
it::assert_equal((0..1).array_combinations::<2>(), <Vec<[isize; 2]>>::new());
it::assert_equal((0..2).array_combinations::<2>(), vec![[0, 1]]);
it::assert_equal((0..0).combinations(2), <Vec<Vec<_>>>::new());
it::assert_equal((0..1).combinations(1), vec![vec![0]]);
@@ -1256,6 +1303,30 @@ fn combinations_with_replacement_range_count() {
}
}
#[test]
#[cfg(not(miri))]
fn array_combinations_with_replacement() {
// Pool smaller than n
it::assert_equal(
(0..1).array_combinations_with_replacement::<2>(),
vec![[0, 0]],
);
// Pool larger than n
it::assert_equal(
(0..3).array_combinations_with_replacement::<2>(),
vec![[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]],
);
// Zero size
it::assert_equal((0..3).array_combinations_with_replacement::<0>(), vec![[]]);
// Zero size on empty pool
it::assert_equal((0..0).array_combinations_with_replacement::<0>(), vec![[]]);
// Empty pool
it::assert_equal(
(0..0).array_combinations_with_replacement::<2>(),
vec![] as Vec<[_; 2]>,
);
}
#[test]
fn powerset() {
it::assert_equal((0..0).powerset(), vec![vec![]]);
@@ -1507,7 +1578,7 @@ fn tree_reduce() {
Some(s.to_string())
};
let num_strings = (0..i).map(|x| x.to_string());
let actual = num_strings.tree_reduce(|a, b| format!("{} {} x", a, b));
let actual = num_strings.tree_reduce(|a, b| format!("{a} {b} x"));
assert_eq!(actual, expected);
}
}
@@ -1567,3 +1638,40 @@ fn multiunzip() {
)
);
}
#[test]
fn into_group_map_with_hasher() {
let _: HashMap<_, _, TestHasher> =
empty::<(u8, u8)>().into_group_map_with_hasher(TestHasher::new());
}
#[test]
fn into_group_map_by_with_hasher() {
let _: HashMap<_, _, TestHasher> =
empty::<(u8, u8)>().into_group_map_by_with_hasher(|x| *x, TestHasher::new());
}
#[test]
fn into_grouping_map_with_hasher() {
let _: HashMap<_, Vec<_>, TestHasher> = empty::<(u8, u8)>()
.into_grouping_map_with_hasher(TestHasher::new())
.collect();
}
#[test]
fn into_grouping_map_by_with_hasher() {
let _: HashMap<_, Vec<_>, TestHasher> = empty::<(u8, u8)>()
.into_grouping_map_by_with_hasher(|x| *x, TestHasher::new())
.collect();
}
#[test]
fn counts_with_hasher() {
let _: HashMap<_, _, TestHasher> = empty::<u8>().counts_with_hasher(TestHasher::new());
}
#[test]
fn counts_by_with_hasher() {
let _: HashMap<_, _, TestHasher> =
empty::<u8>().counts_by_with_hasher(|x| x, TestHasher::new());
}
+46
View File
@@ -54,3 +54,49 @@ fn test_double_ended_zip() {
assert_eq!(it.next_back(), Some((1, 1)));
assert_eq!(it.next_back(), None);
}
#[test]
/// The purpose of this test is not really to test the iterator mechanics itself, rather that it
/// compiles and accepts temporary values as inputs, as those would be valid when not used with the
/// izip! macro and zipped manually via .zip calls
fn test_izip_with_temporaries() {
struct Owned {
data: Vec<i32>,
}
impl Owned {
fn new(val: i32) -> Self {
Self {
data: vec![val; 10],
}
}
fn as_view(&self) -> View<'_> {
View {
data: self.data.as_slice(),
}
}
}
struct View<'a> {
data: &'a [i32],
}
impl View<'_> {
fn iter(&self) -> impl Iterator<Item = &i32> {
self.data.iter()
}
}
let a = Owned::new(0);
let b = Owned::new(1);
let c = Owned::new(2);
let mut sum = 0;
for (x, y, z) in itertools::izip!(a.as_view().iter(), b.as_view().iter(), c.as_view().iter()) {
sum += x + y + z;
}
assert_eq!(sum, 30);
}