Bug 1961207 - build(rust): shim-upgrade itertools 0.10.0 → 0.14.0 r=supply-chain-reviewers,glandium

Differential Revision: https://phabricator.services.mozilla.com/D245935
This commit is contained in:
Erich Gubler
2025-04-22 12:03:45 +00:00
parent e08dd3ac94
commit d58d10bee0
85 changed files with 10016 additions and 3802 deletions

21
Cargo.lock generated
View File

@@ -423,7 +423,7 @@ dependencies = [
"bitflags 2.9.0",
"cexpr",
"clang-sys",
"itertools",
"itertools 0.10.999",
"lazy_static",
"lazycell",
"proc-macro2",
@@ -3393,9 +3393,16 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.10.5"
version = "0.10.999"
dependencies = [
"itertools 0.14.0",
]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
@@ -4176,7 +4183,7 @@ dependencies = [
"futures",
"getrandom 0.2.999",
"hex",
"itertools",
"itertools 0.10.999",
"maybe-async",
"mls-rs-codec",
"mls-rs-core",
@@ -4423,7 +4430,7 @@ dependencies = [
"icu_properties",
"idna",
"indexmap",
"itertools",
"itertools 0.14.0",
"libc",
"lmdb-rkv-sys",
"log",
@@ -5338,7 +5345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
dependencies = [
"anyhow",
"itertools",
"itertools 0.10.999",
"proc-macro2",
"quote",
"syn",
@@ -6316,7 +6323,7 @@ dependencies = [
"gecko-profiler",
"icu_segmenter",
"indexmap",
"itertools",
"itertools 0.14.0",
"itoa",
"lazy_static",
"log",

View File

@@ -220,6 +220,9 @@ ron = { path = "build/rust/ron" }
# Patch `strum` 0.26.* to 0.27.
strum = { path = "build/rust/strum" }
# Patch `itertools` 0.10.* to 0.14.
itertools = { path = "build/rust/itertools" }
# Overrides to allow easier use of common internal crates.
moz_asserts = { path = "mozglue/static/rust/moz_asserts" }

View File

@@ -0,0 +1,17 @@
[package]
name = "itertools"
version = "0.10.999"
edition = "2018"
license = "MIT OR Apache-2.0"
[lib]
path = "lib.rs"
[dependencies.itertools]
version = "0.14.0"
default-features = false
[features]
default = ["itertools/default"]
use_alloc = ["itertools/use_alloc"]
use_std = ["itertools/use_std"]

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::*;

View File

@@ -88,7 +88,7 @@ scopeguard = { version = "1", optional = true }
[build-dependencies]
bindgen = { version = "0.69", default-features = false, features = ["runtime"], optional = true }
cc = { version = "1.0", features = ["parallel"], optional = true }
itertools = { version = "0.10", default-features = false, features = ["use_alloc"], optional = true }
itertools = { version = "0.14", default-features = false, features = ["use_alloc"], optional = true }
libc = "0.2"
log = { version = "0.4", features = ["std"], optional = true }
memchr = { version = "2", optional = true }

View File

@@ -64,7 +64,7 @@ euclid = "0.22"
fxhash = "0.2"
icu_segmenter = { version = "1.5", default-features = false, features = ["auto", "compiled_data"] }
indexmap = {version = "2", features = ["std"]}
itertools = "0.10"
itertools = "0.14"
itoa = "1.0"
lazy_static = "1"
log = "0.4"

View File

@@ -1210,6 +1210,24 @@ crate is broadly used throughout the ecosystem and does not contain anything
suspicious.
"""
[[audits.bytecode-alliance.audits.itertools]]
who = "Nick Fitzgerald <fitzgen@gmail.com>"
criteria = "safe-to-deploy"
delta = "0.10.5 -> 0.12.1"
notes = """
Minimal `unsafe` usage. Few blocks that existed looked reasonable. Does what it
says on the tin: lots of iterators.
"""
[[audits.bytecode-alliance.audits.itertools]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
delta = "0.12.1 -> 0.14.0"
notes = """
Lots of new iterators and shuffling some things around. Some new unsafe code but
it's well-documented and well-tested. Nothing suspicious.
"""
[[audits.bytecode-alliance.audits.itoa]]
who = "Dan Gohman <dev@sunfishcode.online>"
criteria = "safe-to-deploy"

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,192 @@
# Changelog
## 0.14.0
### Breaking
- Increased MSRV to 1.63.0 (#960)
- Removed generic parameter from `cons_tuples` (#988)
### Added
- Added `array_combinations` (#991)
- Added `k_smallest_relaxed` and variants (#925)
- Added `next_array` and `collect_array` (#560)
- Implemented `DoubleEndedIterator` for `FilterOk` (#948)
- Implemented `DoubleEndedIterator` for `FilterMapOk` (#950)
### Changed
- Allow `Q: ?Sized` in `Itertools::contains` (#971)
- Improved hygiene of `chain!` (#943)
- Improved `into_group_map_by` documentation (#1000)
- Improved `tree_reduce` documentation (#955)
- Improved discoverability of `merge_join_by` (#966)
- Improved discoverability of `take_while_inclusive` (#972)
- Improved documentation of `find_or_last` and `find_or_first` (#984)
- Prevented exponentially large type sizes in `tuple_combinations` (#945)
- Added `track_caller` attr for `asser_equal` (#976)
### Notable Internal Changes
- Fixed clippy lints (#956, #987, #1008)
- Addressed warnings within doctests (#964)
- CI: Run most tests with miri (#961)
- CI: Speed up "cargo-semver-checks" action (#938)
- Changed an instance of `default_features` in `Cargo.toml` to `default-features` (#985)
## 0.13.0
### Breaking
- Removed implementation of `DoubleEndedIterator` for `ConsTuples` (#853)
- Made `MultiProduct` fused and fixed on an empty iterator (#835, #834)
- Changed `iproduct!` to return tuples for maxi one iterator too (#870)
- Changed `PutBack::put_back` to return the old value (#880)
- Removed deprecated `repeat_call, Itertools::{foreach, step, map_results, fold_results}` (#878)
- Removed `TakeWhileInclusive::new` (#912)
### Added
- Added `Itertools::{smallest_by, smallest_by_key, largest, largest_by, largest_by_key}` (#654, #885)
- Added `Itertools::tail` (#899)
- Implemented `DoubleEndedIterator` for `ProcessResults` (#910)
- Implemented `Debug` for `FormatWith` (#931)
- Added `Itertools::get` (#891)
### Changed
- Deprecated `Itertools::group_by` (renamed `chunk_by`) (#866, #879)
- Deprecated `unfold` (use `std::iter::from_fn` instead) (#871)
- Optimized `GroupingMapBy` (#873, #876)
- Relaxed `Fn` bounds to `FnMut` in `diff_with, Itertools::into_group_map_by` (#886)
- Relaxed `Debug/Clone` bounds for `MapInto` (#889)
- Documented the `use_alloc` feature (#887)
- Optimized `Itertools::set_from` (#888)
- Removed badges in `README.md` (#890)
- Added "no-std" categories in `Cargo.toml` (#894)
- Fixed `Itertools::k_smallest` on short unfused iterators (#900)
- Deprecated `Itertools::tree_fold1` (renamed `tree_reduce`) (#895)
- Deprecated `GroupingMap::fold_first` (renamed `reduce`) (#902)
- Fixed `Itertools::k_smallest(0)` to consume the iterator, optimized `Itertools::k_smallest(1)` (#909)
- Specialized `Combinations::nth` (#914)
- Specialized `MergeBy::fold` (#920)
- Specialized `CombinationsWithReplacement::nth` (#923)
- Specialized `FlattenOk::{fold, rfold}` (#927)
- Specialized `Powerset::nth` (#924)
- Documentation fixes (#882, #936)
- Fixed `assert_equal` for iterators longer than `i32::MAX` (#932)
- Updated the `must_use` message of non-lazy `KMergeBy` and `TupleCombinations` (#939)
### Notable Internal Changes
- Tested iterator laziness (#792)
- Created `CONTRIBUTING.md` (#767)
## 0.12.1
### Added
- Documented iteration order guarantee for `Itertools::[tuple_]combinations` (#822)
- Documented possible panic in `iterate` (#842)
- Implemented `Clone` and `Debug` for `Diff` (#845)
- Implemented `Debug` for `WithPosition` (#859)
- Implemented `Eq` for `MinMaxResult` (#838)
- Implemented `From<EitherOrBoth<A, B>>` for `Option<Either<A, B>>` (#843)
- Implemented `PeekingNext` for `RepeatN` (#855)
### Changed
- Made `CoalesceBy` lazy (#801)
- Optimized `Filter[Map]Ok::next`, `Itertools::partition`, `Unique[By]::next[_back]` (#818)
- Optimized `Itertools::find_position` (#837)
- Optimized `Positions::next[_back]` (#816)
- Optimized `ZipLongest::fold` (#854)
- Relaxed `Debug` bounds for `GroupingMapBy` (#860)
- Specialized `ExactlyOneError::fold` (#826)
- Specialized `Interleave[Shortest]::fold` (#849)
- Specialized `MultiPeek::fold` (#820)
- Specialized `PadUsing::[r]fold` (#825)
- Specialized `PeekNth::fold` (#824)
- Specialized `Positions::[r]fold` (#813)
- Specialized `PutBackN::fold` (#823)
- Specialized `RepeatN::[r]fold` (#821)
- Specialized `TakeWhileInclusive::fold` (#851)
- Specialized `ZipLongest::rfold` (#848)
### Notable Internal Changes
- Added test coverage in CI (#847, #856)
- Added semver check in CI (#784)
- Enforced `clippy` in CI (#740)
- Enforced `rustdoc` in CI (#840)
- Improved specialization tests (#807)
- More specialization benchmarks (#806)
## 0.12.0
### Breaking
- Made `take_while_inclusive` consume iterator by value (#709)
- Added `Clone` bound to `Unique` (#777)
### Added
- Added `Itertools::try_len` (#723)
- Added free function `sort_unstable` (#796)
- Added `GroupMap::fold_with` (#778, #785)
- Added `PeekNth::{peek_mut, peek_nth_mut}` (#716)
- Added `PeekNth::{next_if, next_if_eq}` (#734)
- Added conversion into `(Option<A>,Option<B>)` to `EitherOrBoth` (#713)
- Added conversion from `Either<A, B>` to `EitherOrBoth<A, B>` (#715)
- Implemented `ExactSizeIterator` for `Tuples` (#761)
- Implemented `ExactSizeIterator` for `(Circular)TupleWindows` (#752)
- Made `EitherOrBoth<T>` a shorthand for `EitherOrBoth<T, T>` (#719)
### Changed
- Added missing `#[must_use]` annotations on iterator adaptors (#794)
- Made `Combinations` lazy (#795)
- Made `Intersperse(With)` lazy (#797)
- Made `Permutations` lazy (#793)
- Made `Product` lazy (#800)
- Made `TupleWindows` lazy (#602)
- Specialized `Combinations::{count, size_hint}` (#729)
- Specialized `CombinationsWithReplacement::{count, size_hint}` (#737)
- Specialized `Powerset::fold` (#765)
- Specialized `Powerset::count` (#735)
- Specialized `TupleCombinations::{count, size_hint}` (#763)
- Specialized `TupleCombinations::fold` (#775)
- Specialized `WhileSome::fold` (#780)
- Specialized `WithPosition::fold` (#772)
- Specialized `ZipLongest::fold` (#774)
- Changed `{min, max}_set*` operations require `alloc` feature, instead of `std` (#760)
- Improved documentation of `tree_fold1` (#787)
- Improved documentation of `permutations` (#724)
- Fixed typo in documentation of `multiunzip` (#770)
### Notable Internal Changes
- Improved specialization tests (#799, #786, #782)
- Simplified implementation of `Permutations` (#739, #748, #790)
- Combined `Merge`/`MergeBy`/`MergeJoinBy` implementations (#736)
- Simplified `Permutations::size_hint` (#739)
- Fix wrapping arithmetic in benchmarks (#770)
- Enforced `rustfmt` in CI (#751)
- Disallowed compile warnings in CI (#720)
- Used `cargo hack` to check MSRV (#754)
## 0.11.0
### Breaking
- Make `Itertools::merge_join_by` also accept functions returning bool (#704)
- Implement `PeekingNext` transitively over mutable references (#643)
- Change `with_position` to yield `(Position, Item)` instead of `Position<Item>` (#699)
### Added
- Add `Itertools::take_while_inclusive` (#616)
- Implement `PeekingNext` for `PeekingTakeWhile` (#644)
- Add `EitherOrBoth::{just_left, just_right, into_left, into_right, as_deref, as_deref_mut, left_or_insert, right_or_insert, left_or_insert_with, right_or_insert_with, insert_left, insert_right, insert_both}` (#629)
- Implement `Clone` for `CircularTupleWindows` (#686)
- Implement `Clone` for `Chunks` (#683)
- Add `Itertools::process_results` (#680)
### Changed
- Use `Cell` instead of `RefCell` in `Format` and `FormatWith` (#608)
- CI tweaks (#674, #675)
- Document and test the difference between stable and unstable sorts (#653)
- Fix documentation error on `Itertools::max_set_by_key` (#692)
- Move MSRV metadata to `Cargo.toml` (#672)
- Implement `equal` with `Iterator::eq` (#591)
## 0.10.5
- Maintenance
## 0.10.4
- Add `EitherOrBoth::or` and `EitherOrBoth::or_else` (#593)
- Add `min_set`, `max_set` et al. (#613, #323)
@@ -7,6 +194,9 @@
- Documentation fixes (#612, #625, #632, #633, #634, #638)
- Code maintenance (#623, #624, #627, #630)
## 0.10.3
- Maintenance
## 0.10.2
- Add `Itertools::multiunzip` (#362, #565)
- Add `intersperse` and `intersperse_with` free functions (#555)

View File

@@ -0,0 +1,189 @@
# Contributing to itertools
We use stable Rust only.
Please check the minimum version of Rust we use in `Cargo.toml`.
_If you are proposing a major change to CI or a new iterator adaptor for this crate,
then **please first file an issue** describing your proposal._
[Usual concerns about new methods](https://github.com/rust-itertools/itertools/issues/413#issuecomment-657670781).
To pass CI tests successfully, your code must be free of "compiler warnings" and "clippy warnings" and be "rustfmt" formatted.
Note that small PRs are easier to review and therefore are more easily merged.
## Write a new method/adaptor for `Itertools` trait
In general, the code logic should be tested with [quickcheck](https://crates.io/crates/quickcheck) tests in `tests/quick.rs`
which allow us to test properties about the code with randomly generated inputs.
### Behind `use_std`/`use_alloc` feature?
If it needs the "std" (such as using hashes) then it should be behind the `use_std` feature,
or if it requires heap allocation (such as using vectors) then it should be behind the `use_alloc` feature.
Otherwise it should be able to run in `no_std` context.
This mostly applies to your new module, each import from it, and to your new `Itertools` method.
### Pick the right receiver
`self`, `&mut self` or `&self`? From [#710](https://github.com/rust-itertools/itertools/pull/710):
- Take by value when:
- It transfers ownership to another iterator type, such as `filter`, `map`...
- It consumes the iterator completely, such as `count`, `last`, `max`...
- Mutably borrow when it consumes only part of the iterator, such as `find`, `all`, `try_collect`...
- Immutably borrow when there is no change, such as `size_hint`.
### Laziness
Iterators are [lazy](https://doc.rust-lang.org/std/iter/index.html#laziness):
- structs of iterator adaptors should have `#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]` ;
- structs of iterators should have `#[must_use = "iterators are lazy and do nothing unless consumed"]`.
Those behaviors are **tested** in `tests/laziness.rs`.
## Specialize `Iterator` methods
It might be more performant to specialize some methods.
However, each specialization should be thoroughly tested.
Correctly specializing methods can be difficult, and _we do not require that you do it on your initial PR_.
Most of the time, we want specializations of:
- [`size_hint`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.size_hint):
It mostly allows allocation optimizations.
When always exact, it also enables to implement `ExactSizeIterator`.
See our private module `src/size_hint.rs` for helpers.
- [`fold`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold)
might make iteration faster than calling `next` repeatedly.
- [`count`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.count),
[`last`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.last),
[`nth`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.nth)
as we might be able to avoid iterating on every item with `next`.
Additionally,
- `for_each`, `reduce`, `max/min[_by[_key]]` and `partition` all rely on `fold` so you should specialize it instead.
- `all`, `any`, `find`, `find_map`, `cmp`, `partial_cmp`, `eq`, `ne`, `lt`, `le`, `gt`, `ge` and `position` all rely (by default) on `try_fold`
which we can not specialize on stable rust, so you might want to wait it stabilizes
or specialize each of them.
- `DoubleEndedIterator::{nth_back, rfold, rfind}`: similar reasoning.
An adaptor might use the inner iterator specializations for its own specializations.
They are **tested** in `tests/specializations.rs` and **benchmarked** in `benches/specializations.rs`
(build those benchmarks is slow so you might want to temporarily remove the ones you do not want to measure).
## Additional implementations
### The [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) implementation
All our iterators should implement `Debug`.
When one of the field is not debuggable (such as _functions_), you must not derive `Debug`.
Instead, manually implement it and _ignore this field_ in our helper macro `debug_fmt_fields`.
<details>
<summary>4 examples (click to expand)</summary>
```rust
use std::fmt;
/* ===== Simple derive. ===== */
#[derive(Debug)]
struct Name1<I> {
iter: I,
}
/* ===== With an unclonable field. ===== */
struct Name2<I, F> {
iter: I,
func: F,
}
// No `F: Debug` bound and the field `func` is ignored.
impl<I: fmt::Debug, F> fmt::Debug for Name2<I, F> {
// it defines the `fmt` function from a struct name and the fields you want to debug.
debug_fmt_fields!(Name2, iter);
}
/* ===== With an unclonable field, but another bound to add. ===== */
struct Name3<I: Iterator, F> {
iter: I,
item: Option<I::Item>,
func: F,
}
// Same about `F` and `func`, similar about `I` but we must add the `I::Item: Debug` bound.
impl<I: Iterator + fmt::Debug, F> fmt::Debug for Name3<I, F>
where
I::Item: fmt::Debug,
{
debug_fmt_fields!(Name3, iter, item);
}
/* ===== With an unclonable field for which we can provide some information. ===== */
struct Name4<I, F> {
iter: I,
func: Option<F>,
}
// If ignore a field is not good enough, implement Debug fully manually.
impl<I: fmt::Debug, F> fmt::Debug for Name4<I, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let func = if self.func.is_some() { "Some(_)" } else { "None" };
f.debug_struct("Name4")
.field("iter", &self.iter)
.field("func", &func)
.finish()
}
}
```
</details>
### When/How to implement [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html)
All our iterators should implement `Clone` when possible.
Note that a mutable reference is never clonable so `struct Name<'a, I: 'a> { iter: &'a mut I }` can not implement `Clone`.
Derive `Clone` on a generic struct adds the bound `Clone` on each generic parameter.
It might be an issue in which case you should manually implement it with our helper macro `clone_fields` (it defines the `clone` function calling `clone` on each field) and be careful about the bounds.
### When to implement [`std::iter::FusedIterator`](https://doc.rust-lang.org/std/iter/trait.FusedIterator.html)
This trait should be implemented _by all iterators that always return `None` after returning `None` once_, because it allows to optimize `Iterator::fuse()`.
The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, eventually refined to ensure it behaves in a fused way.
### When to implement [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html)
_When we are always able to return an exact non-overflowing length._
Therefore, we do not implement it on adaptors that makes the iterator longer as the resulting length could overflow.
One should not override `ExactSizeIterator::len` method but rely on an exact `Iterator::size_hint` implementation, meaning it returns `(length, Some(length))` (unless you could make `len` more performant than the default).
The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure the size hint is exact.
### When to implement [`DoubleEndedIterator`](https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html)
When the iterator structure allows to handle _iterating on both fronts simultaneously_.
The iteration might stop in the middle when both fronts meet.
The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure we can iterate on both fronts simultaneously.
### When to implement [`itertools::PeekingNext`](https://docs.rs/itertools/latest/itertools/trait.PeekingNext.html)
TODO
This is currently **tested** in `tests/test_std.rs`.
## About lending iterators
TODO
## Other notes
No guideline about using `#[inline]` yet.
### `.fold` / `.for_each` / `.try_fold` / `.try_for_each`
In the Rust standard library, it's quite common for `fold` to be implemented in terms of `try_fold`. But it's not something we do yet because we can not specialize `try_fold` methods yet (it uses the unstable `Try`).
From [#781](https://github.com/rust-itertools/itertools/pull/781), the general rule to follow is something like this:
- If you need to completely consume an iterator:
- Use `fold` if you need an _owned_ access to an accumulator.
- Use `for_each` otherwise.
- If you need to partly consume an iterator, the same applies with `try_` versions:
- Use `try_fold` if you need an _owned_ access to an accumulator.
- Use `try_for_each` otherwise.

740
third_party/rust/itertools/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,740 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
[[package]]
name = "ciborium-ll"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "3.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
dependencies = [
"bitflags",
"clap_lex",
"indexmap",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "criterion"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [
"anes",
"atty",
"cast",
"ciborium",
"clap",
"criterion-plot",
"itertools 0.10.5",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools 0.10.5",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "either"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "half"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
dependencies = [
"cfg-if",
"crunchy",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.14.0"
dependencies = [
"criterion",
"either",
"paste",
"permutohedron",
"quickcheck",
"rand",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "os_str_bytes"
version = "6.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "permutohedron"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c"
[[package]]
name = "plotters"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quickcheck"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f"
dependencies = [
"rand",
"rand_core",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "serde"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "web-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
dependencies = [
"windows-sys",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

View File

@@ -3,71 +3,165 @@
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
rust-version = "1.63.0"
name = "itertools"
version = "0.10.5"
version = "0.14.0"
authors = ["bluss"]
exclude = ["/bors.toml"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Extra iterator adaptors, iterator methods, free functions, and macros."
documentation = "https://docs.rs/itertools/"
readme = "README.md"
keywords = ["iterator", "data-structure", "zip", "product", "group-by"]
categories = ["algorithms", "rust-patterns"]
license = "MIT/Apache-2.0"
keywords = [
"iterator",
"data-structure",
"zip",
"product",
]
categories = [
"algorithms",
"rust-patterns",
"no-std",
"no-std::no-alloc",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-itertools/itertools"
[package.metadata.release]
no-dev-version = true
[profile.bench]
debug = true
[features]
default = ["use_std"]
use_alloc = []
use_std = [
"use_alloc",
"either/use_std",
]
[lib]
name = "itertools"
path = "src/lib.rs"
test = false
bench = false
[[bench]]
name = "tuple_combinations"
harness = false
[[example]]
name = "iris"
path = "examples/iris.rs"
[[bench]]
[[test]]
name = "adaptors_no_collect"
path = "tests/adaptors_no_collect.rs"
[[test]]
name = "flatten_ok"
path = "tests/flatten_ok.rs"
[[test]]
name = "laziness"
path = "tests/laziness.rs"
[[test]]
name = "macros_hygiene"
path = "tests/macros_hygiene.rs"
[[test]]
name = "merge_join"
path = "tests/merge_join.rs"
[[test]]
name = "peeking_take_while"
path = "tests/peeking_take_while.rs"
[[test]]
name = "quick"
path = "tests/quick.rs"
[[test]]
name = "specializations"
path = "tests/specializations.rs"
[[test]]
name = "test_core"
path = "tests/test_core.rs"
[[test]]
name = "test_std"
path = "tests/test_std.rs"
[[test]]
name = "tuples"
harness = false
path = "tests/tuples.rs"
[[bench]]
name = "fold_specialization"
harness = false
[[bench]]
name = "combinations_with_replacement"
harness = false
[[bench]]
name = "tree_fold1"
harness = false
[[test]]
name = "zip"
path = "tests/zip.rs"
[[bench]]
name = "bench1"
path = "benches/bench1.rs"
harness = false
[[bench]]
name = "combinations"
path = "benches/combinations.rs"
harness = false
[[bench]]
name = "combinations_with_replacement"
path = "benches/combinations_with_replacement.rs"
harness = false
[[bench]]
name = "fold_specialization"
path = "benches/fold_specialization.rs"
harness = false
[[bench]]
name = "k_smallest"
path = "benches/k_smallest.rs"
harness = false
[[bench]]
name = "powerset"
path = "benches/powerset.rs"
harness = false
[[bench]]
name = "specializations"
path = "benches/specializations.rs"
harness = false
[[bench]]
name = "tree_reduce"
path = "benches/tree_reduce.rs"
harness = false
[[bench]]
name = "tuple_combinations"
path = "benches/tuple_combinations.rs"
harness = false
[[bench]]
name = "tuples"
path = "benches/tuples.rs"
harness = false
[dependencies.either]
version = "1.0"
default-features = false
[dev-dependencies.criterion]
version = "=0"
version = "0.4.0"
features = ["html_reports"]
[dev-dependencies.paste]
version = "1.0.0"
@@ -82,7 +176,5 @@ default-features = false
[dev-dependencies.rand]
version = "0.7"
[features]
default = ["use_std"]
use_alloc = []
use_std = ["use_alloc", "either/use_std"]
[profile.bench]
debug = 2

View File

@@ -4,14 +4,11 @@ Extra iterator adaptors, functions and macros.
Please read the [API documentation here](https://docs.rs/itertools/).
[![build_status](https://github.com/rust-itertools/itertools/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-itertools/itertools/actions)
[![crates.io](https://img.shields.io/crates/v/itertools.svg)](https://crates.io/crates/itertools)
How to use with Cargo:
```toml
[dependencies]
itertools = "0.10.5"
itertools = "0.14.0"
```
How to use in your crate:
@@ -21,17 +18,9 @@ use itertools::Itertools;
```
## How to contribute
If you're not sure what to work on, try checking the [help wanted](https://github.com/rust-itertools/itertools/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label.
- Fix a bug or implement a new thing
- Include tests for your new feature, preferably a QuickCheck test
- Make a Pull Request
For new features, please first consider filing a PR to [rust-lang/rust](https://github.com/rust-lang/rust),
adding your new feature to the `Iterator` trait of the standard library, if you believe it is reasonable.
If it isn't accepted there, proposing it for inclusion in ``itertools`` is a good idea.
The reason for doing is this is so that we avoid future breakage as with ``.flatten()``.
However, if your feature involves heap allocation, such as storing elements in a ``Vec<T>``,
then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead.
See our [CONTRIBUTING.md](https://github.com/rust-itertools/itertools/blob/master/CONTRIBUTING.md) for a detailed guide.
## License

View File

@@ -1,22 +1,20 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use itertools::Itertools;
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
use itertools::free::cloned;
use itertools::iproduct;
use itertools::Itertools;
use std::iter::repeat;
use std::cmp;
use std::iter::repeat;
use std::ops::{Add, Range};
mod extra;
use crate::extra::ZipSlices;
fn slice_iter(c: &mut Criterion) {
let xs: Vec<_> = repeat(1i32).take(20).collect();
c.bench_function("slice iter", move |b| {
b.iter(|| for elt in xs.iter() {
black_box(elt);
b.iter(|| {
for elt in xs.iter() {
black_box(elt);
}
})
});
}
@@ -25,8 +23,10 @@ fn slice_iter_rev(c: &mut Criterion) {
let xs: Vec<_> = repeat(1i32).take(20).collect();
c.bench_function("slice iter rev", move |b| {
b.iter(|| for elt in xs.iter().rev() {
black_box(elt);
b.iter(|| {
for elt in xs.iter().rev() {
black_box(elt);
}
})
});
}
@@ -116,72 +116,6 @@ fn zip_slices_ziptuple(c: &mut Criterion) {
});
}
fn zipslices(c: &mut Criterion) {
let xs = vec![0; 1024];
let ys = vec![0; 768];
let xs = black_box(xs);
let ys = black_box(ys);
c.bench_function("zipslices", move |b| {
b.iter(|| {
for (&x, &y) in ZipSlices::new(&xs, &ys) {
black_box(x);
black_box(y);
}
})
});
}
fn zipslices_mut(c: &mut Criterion) {
let xs = vec![0; 1024];
let ys = vec![0; 768];
let xs = black_box(xs);
let mut ys = black_box(ys);
c.bench_function("zipslices mut", move |b| {
b.iter(|| {
for (&x, &mut y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) {
black_box(x);
black_box(y);
}
})
});
}
fn zipdot_i32_zipslices(c: &mut Criterion) {
let xs = vec![2; 1024];
let ys = vec![2; 768];
let xs = black_box(xs);
let ys = black_box(ys);
c.bench_function("zipdot i32 zipslices", move |b| {
b.iter(|| {
let mut s = 0i32;
for (&x, &y) in ZipSlices::new(&xs, &ys) {
s += x * y;
}
s
})
});
}
fn zipdot_f32_zipslices(c: &mut Criterion) {
let xs = vec![2f32; 1024];
let ys = vec![2f32; 768];
let xs = black_box(xs);
let ys = black_box(ys);
c.bench_function("zipdot f32 zipslices", move |b| {
b.iter(|| {
let mut s = 0.;
for (&x, &y) in ZipSlices::new(&xs, &ys) {
s += x * y;
}
s
})
});
}
fn zip_checked_counted_loop(c: &mut Criterion) {
let xs = vec![0; 1024];
let ys = vec![0; 768];
@@ -307,10 +241,10 @@ fn zip_unchecked_counted_loop(c: &mut Criterion) {
let len = cmp::min(xs.len(), ys.len());
for i in 0..len {
unsafe {
let x = *xs.get_unchecked(i);
let y = *ys.get_unchecked(i);
black_box(x);
black_box(y);
let x = *xs.get_unchecked(i);
let y = *ys.get_unchecked(i);
black_box(x);
black_box(y);
}
}
})
@@ -329,9 +263,9 @@ fn zipdot_i32_unchecked_counted_loop(c: &mut Criterion) {
let mut s = 0i32;
for i in 0..len {
unsafe {
let x = *xs.get_unchecked(i);
let y = *ys.get_unchecked(i);
s += x * y;
let x = *xs.get_unchecked(i);
let y = *ys.get_unchecked(i);
s += x * y;
}
}
s
@@ -351,9 +285,9 @@ fn zipdot_f32_unchecked_counted_loop(c: &mut Criterion) {
let mut s = 0f32;
for i in 0..len {
unsafe {
let x = *xs.get_unchecked(i);
let y = *ys.get_unchecked(i);
s += x * y;
let x = *xs.get_unchecked(i);
let y = *ys.get_unchecked(i);
s += x * y;
}
}
s
@@ -374,19 +308,19 @@ fn zip_unchecked_counted_loop3(c: &mut Criterion) {
let len = cmp::min(xs.len(), cmp::min(ys.len(), zs.len()));
for i in 0..len {
unsafe {
let x = *xs.get_unchecked(i);
let y = *ys.get_unchecked(i);
let z = *zs.get_unchecked(i);
black_box(x);
black_box(y);
black_box(z);
let x = *xs.get_unchecked(i);
let y = *ys.get_unchecked(i);
let z = *zs.get_unchecked(i);
black_box(x);
black_box(y);
black_box(z);
}
}
})
});
}
fn group_by_lazy_1(c: &mut Criterion) {
fn chunk_by_lazy_1(c: &mut Criterion) {
let mut data = vec![0; 1024];
for (index, elt) in data.iter_mut().enumerate() {
*elt = index / 10;
@@ -394,10 +328,10 @@ fn group_by_lazy_1(c: &mut Criterion) {
let data = black_box(data);
c.bench_function("group by lazy 1", move |b| {
c.bench_function("chunk by lazy 1", move |b| {
b.iter(|| {
for (_key, group) in &data.iter().group_by(|elt| **elt) {
for elt in group {
for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
for elt in chunk {
black_box(elt);
}
}
@@ -405,7 +339,7 @@ fn group_by_lazy_1(c: &mut Criterion) {
});
}
fn group_by_lazy_2(c: &mut Criterion) {
fn chunk_by_lazy_2(c: &mut Criterion) {
let mut data = vec![0; 1024];
for (index, elt) in data.iter_mut().enumerate() {
*elt = index / 2;
@@ -413,10 +347,10 @@ fn group_by_lazy_2(c: &mut Criterion) {
let data = black_box(data);
c.bench_function("group by lazy 2", move |b| {
c.bench_function("chunk by lazy 2", move |b| {
b.iter(|| {
for (_key, group) in &data.iter().group_by(|elt| **elt) {
for elt in group {
for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
for elt in chunk {
black_box(elt);
}
}
@@ -432,8 +366,8 @@ fn slice_chunks(c: &mut Criterion) {
c.bench_function("slice chunks", move |b| {
b.iter(|| {
for group in data.chunks(sz) {
for elt in group {
for chunk in data.chunks(sz) {
for elt in chunk {
black_box(elt);
}
}
@@ -449,8 +383,8 @@ fn chunks_lazy_1(c: &mut Criterion) {
c.bench_function("chunks lazy 1", move |b| {
b.iter(|| {
for group in &data.iter().chunks(sz) {
for elt in group {
for chunk in &data.iter().chunks(sz) {
for elt in chunk {
black_box(elt);
}
}
@@ -464,17 +398,15 @@ fn equal(c: &mut Criterion) {
let alpha = black_box(&data[1..]);
let beta = black_box(&data[..l - 1]);
c.bench_function("equal", move |b| {
b.iter(|| {
itertools::equal(alpha, beta)
})
});
c.bench_function("equal", move |b| b.iter(|| itertools::equal(alpha, beta)));
}
fn merge_default(c: &mut Criterion) {
let mut data1 = vec![0; 1024];
let mut data2 = vec![0; 800];
let mut x = 0;
#[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
for (_, elt) in data1.iter_mut().enumerate() {
*elt = x;
x += 1;
@@ -493,9 +425,7 @@ fn merge_default(c: &mut Criterion) {
let data2 = black_box(data2);
c.bench_function("merge default", move |b| {
b.iter(|| {
data1.iter().merge(&data2).count()
})
b.iter(|| data1.iter().merge(&data2).count())
});
}
@@ -503,6 +433,8 @@ fn merge_by_cmp(c: &mut Criterion) {
let mut data1 = vec![0; 1024];
let mut data2 = vec![0; 800];
let mut x = 0;
#[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
for (_, elt) in data1.iter_mut().enumerate() {
*elt = x;
x += 1;
@@ -521,9 +453,7 @@ fn merge_by_cmp(c: &mut Criterion) {
let data2 = black_box(data2);
c.bench_function("merge by cmp", move |b| {
b.iter(|| {
data1.iter().merge_by(&data2, PartialOrd::le).count()
})
b.iter(|| data1.iter().merge_by(&data2, PartialOrd::le).count())
});
}
@@ -531,6 +461,8 @@ fn merge_by_lt(c: &mut Criterion) {
let mut data1 = vec![0; 1024];
let mut data2 = vec![0; 800];
let mut x = 0;
#[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
for (_, elt) in data1.iter_mut().enumerate() {
*elt = x;
x += 1;
@@ -549,9 +481,7 @@ fn merge_by_lt(c: &mut Criterion) {
let data2 = black_box(data2);
c.bench_function("merge by lt", move |b| {
b.iter(|| {
data1.iter().merge_by(&data2, |a, b| a <= b).count()
})
b.iter(|| data1.iter().merge_by(&data2, |a, b| a <= b).count())
});
}
@@ -559,6 +489,8 @@ fn kmerge_default(c: &mut Criterion) {
let mut data1 = vec![0; 1024];
let mut data2 = vec![0; 800];
let mut x = 0;
#[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
for (_, elt) in data1.iter_mut().enumerate() {
*elt = x;
x += 1;
@@ -578,9 +510,7 @@ fn kmerge_default(c: &mut Criterion) {
let its = &[data1.iter(), data2.iter()];
c.bench_function("kmerge default", move |b| {
b.iter(|| {
its.iter().cloned().kmerge().count()
})
b.iter(|| its.iter().cloned().kmerge().count())
});
}
@@ -589,7 +519,7 @@ fn kmerge_tenway(c: &mut Criterion) {
let mut state = 1729u16;
fn rng(state: &mut u16) -> u16 {
let new = state.wrapping_mul(31421) + 6927;
let new = state.wrapping_mul(31421).wrapping_add(6927);
*state = new;
new
}
@@ -600,10 +530,10 @@ fn kmerge_tenway(c: &mut Criterion) {
let mut chunks = Vec::new();
let mut rest = &mut data[..];
while rest.len() > 0 {
while !rest.is_empty() {
let chunk_len = 1 + rng(&mut state) % 512;
let chunk_len = cmp::min(rest.len(), chunk_len as usize);
let (fst, tail) = {rest}.split_at_mut(chunk_len);
let (fst, tail) = { rest }.split_at_mut(chunk_len);
fst.sort();
chunks.push(fst.iter().cloned());
rest = tail;
@@ -612,15 +542,14 @@ fn kmerge_tenway(c: &mut Criterion) {
// println!("Chunk lengths: {}", chunks.iter().format_with(", ", |elt, f| f(&elt.len())));
c.bench_function("kmerge tenway", move |b| {
b.iter(|| {
chunks.iter().cloned().kmerge().count()
})
b.iter(|| chunks.iter().cloned().kmerge().count())
});
}
fn fast_integer_sum<I>(iter: I) -> I::Item
where I: IntoIterator,
I::Item: Default + Add<Output=I::Item>
where
I: IntoIterator,
I::Item: Default + Add<Output = I::Item>,
{
iter.into_iter().fold(<_>::default(), |x, y| x + y)
}
@@ -629,9 +558,7 @@ fn step_vec_2(c: &mut Criterion) {
let v = vec![0; 1024];
c.bench_function("step vec 2", move |b| {
b.iter(|| {
fast_integer_sum(cloned(v.iter().step_by(2)))
})
b.iter(|| fast_integer_sum(cloned(v.iter().step_by(2))))
});
}
@@ -639,9 +566,7 @@ fn step_vec_10(c: &mut Criterion) {
let v = vec![0; 1024];
c.bench_function("step vec 10", move |b| {
b.iter(|| {
fast_integer_sum(cloned(v.iter().step_by(10)))
})
b.iter(|| fast_integer_sum(cloned(v.iter().step_by(10))))
});
}
@@ -649,9 +574,7 @@ fn step_range_2(c: &mut Criterion) {
let v = black_box(0..1024);
c.bench_function("step range 2", move |b| {
b.iter(|| {
fast_integer_sum(v.clone().step_by(2))
})
b.iter(|| fast_integer_sum(v.clone().step_by(2)))
});
}
@@ -659,9 +582,23 @@ fn step_range_10(c: &mut Criterion) {
let v = black_box(0..1024);
c.bench_function("step range 10", move |b| {
b.iter(|| {
fast_integer_sum(v.clone().step_by(10))
})
b.iter(|| fast_integer_sum(v.clone().step_by(10)))
});
}
fn vec_iter_mut_partition(c: &mut Criterion) {
let data = std::iter::repeat(-1024i32..1024)
.take(256)
.flatten()
.collect_vec();
c.bench_function("vec iter mut partition", move |b| {
b.iter_batched(
|| data.clone(),
|mut data| {
black_box(itertools::partition(black_box(&mut data), |n| *n >= 0));
},
BatchSize::LargeInput,
)
});
}
@@ -681,22 +618,6 @@ fn cartesian_product_iterator(c: &mut Criterion) {
});
}
fn cartesian_product_fold(c: &mut Criterion) {
let xs = vec![0; 16];
c.bench_function("cartesian product fold", move |b| {
b.iter(|| {
let mut sum = 0;
iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| {
sum += x;
sum += y;
sum += z;
});
sum
})
});
}
fn multi_cartesian_product_iterator(c: &mut Criterion) {
let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
@@ -713,22 +634,6 @@ fn multi_cartesian_product_iterator(c: &mut Criterion) {
});
}
fn multi_cartesian_product_fold(c: &mut Criterion) {
let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
c.bench_function("multi cartesian product fold", move |b| {
b.iter(|| {
let mut sum = 0;
xs.iter().multi_cartesian_product().fold((), |(), x| {
sum += x[0];
sum += x[1];
sum += x[2];
});
sum
})
});
}
fn cartesian_product_nested_for(c: &mut Criterion) {
let xs = vec![0; 16];
@@ -753,9 +658,7 @@ fn all_equal(c: &mut Criterion) {
let mut xs = vec![0; 5_000_000];
xs.extend(vec![1; 5_000_000]);
c.bench_function("all equal", move |b| {
b.iter(|| xs.iter().all_equal())
});
c.bench_function("all equal", move |b| b.iter(|| xs.iter().all_equal()));
}
fn all_equal_for(c: &mut Criterion) {
@@ -797,21 +700,17 @@ fn permutations_iter(c: &mut Criterion) {
}
c.bench_function("permutations iter", move |b| {
b.iter(|| {
for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) {
}
})
b.iter(
|| {
for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) {}
},
)
});
}
fn permutations_range(c: &mut Criterion) {
c.bench_function("permutations range", move |b| {
b.iter(|| {
for _ in (0..PERM_COUNT).permutations(PERM_COUNT) {
}
})
b.iter(|| for _ in (0..PERM_COUNT).permutations(PERM_COUNT) {})
});
}
@@ -819,11 +718,7 @@ fn permutations_slice(c: &mut Criterion) {
let v = (0..PERM_COUNT).collect_vec();
c.bench_function("permutations slice", move |b| {
b.iter(|| {
for _ in v.as_slice().iter().permutations(PERM_COUNT) {
}
})
b.iter(|| for _ in v.as_slice().iter().permutations(PERM_COUNT) {})
});
}
@@ -836,10 +731,6 @@ criterion_group!(
zipdot_f32_default_zip,
zip_default_zip3,
zip_slices_ziptuple,
zipslices,
zipslices_mut,
zipdot_i32_zipslices,
zipdot_f32_zipslices,
zip_checked_counted_loop,
zipdot_i32_checked_counted_loop,
zipdot_f32_checked_counted_loop,
@@ -848,8 +739,8 @@ criterion_group!(
zipdot_i32_unchecked_counted_loop,
zipdot_f32_unchecked_counted_loop,
zip_unchecked_counted_loop3,
group_by_lazy_1,
group_by_lazy_2,
chunk_by_lazy_1,
chunk_by_lazy_2,
slice_chunks,
chunks_lazy_1,
equal,
@@ -862,10 +753,9 @@ criterion_group!(
step_vec_10,
step_range_2,
step_range_10,
vec_iter_mut_partition,
cartesian_product_iterator,
cartesian_product_fold,
multi_cartesian_product_iterator,
multi_cartesian_product_fold,
cartesian_product_nested_for,
all_equal,
all_equal_for,

View File

@@ -111,15 +111,7 @@ fn comb_c14(c: &mut Criterion) {
}
criterion_group!(
benches,
comb_for1,
comb_for2,
comb_for3,
comb_for4,
comb_c1,
comb_c2,
comb_c3,
comb_c4,
benches, comb_for1, comb_for2, comb_for3, comb_for4, comb_c1, comb_c2, comb_c3, comb_c4,
comb_c14,
);
criterion_main!(benches);

View File

@@ -1,2 +0,0 @@
pub use self::zipslices::ZipSlices;
mod zipslices;

View File

@@ -1,188 +0,0 @@
use std::cmp;
// Note: There are different ways to implement ZipSlices.
// This version performed the best in benchmarks.
//
// I also implemented a version with three pointers (tptr, tend, uptr),
// that mimiced slice::Iter and only checked bounds by using tptr == tend,
// but that was inferior to this solution.
/// An iterator which iterates two slices simultaneously.
///
/// `ZipSlices` acts like a double-ended `.zip()` iterator.
///
/// It was intended to be more efficient than `.zip()`, and it was, then
/// rustc changed how it optimizes so it can not promise improved performance
/// at this time.
///
/// Note that elements past the end of the shortest of the two slices are ignored.
///
/// Iterator element type for `ZipSlices<T, U>` is `(T::Item, U::Item)`. For example,
/// for a `ZipSlices<&'a [A], &'b mut [B]>`, the element type is `(&'a A, &'b mut B)`.
#[derive(Clone)]
pub struct ZipSlices<T, U> {
t: T,
u: U,
len: usize,
index: usize,
}
impl<'a, 'b, A, B> ZipSlices<&'a [A], &'b [B]> {
/// Create a new `ZipSlices` from slices `a` and `b`.
///
/// Act like a double-ended `.zip()` iterator, but more efficiently.
///
/// Note that elements past the end of the shortest of the two slices are ignored.
#[inline(always)]
pub fn new(a: &'a [A], b: &'b [B]) -> Self {
let minl = cmp::min(a.len(), b.len());
ZipSlices {
t: a,
u: b,
len: minl,
index: 0,
}
}
}
impl<T, U> ZipSlices<T, U>
where T: Slice,
U: Slice
{
/// Create a new `ZipSlices` from slices `a` and `b`.
///
/// Act like a double-ended `.zip()` iterator, but more efficiently.
///
/// Note that elements past the end of the shortest of the two slices are ignored.
#[inline(always)]
pub fn from_slices(a: T, b: U) -> Self {
let minl = cmp::min(a.len(), b.len());
ZipSlices {
t: a,
u: b,
len: minl,
index: 0,
}
}
}
impl<T, U> Iterator for ZipSlices<T, U>
where T: Slice,
U: Slice
{
type Item = (T::Item, U::Item);
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if self.index >= self.len {
None
} else {
let i = self.index;
self.index += 1;
Some((
self.t.get_unchecked(i),
self.u.get_unchecked(i)))
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len - self.index;
(len, Some(len))
}
}
impl<T, U> DoubleEndedIterator for ZipSlices<T, U>
where T: Slice,
U: Slice
{
#[inline(always)]
fn next_back(&mut self) -> Option<Self::Item> {
unsafe {
if self.index >= self.len {
None
} else {
self.len -= 1;
let i = self.len;
Some((
self.t.get_unchecked(i),
self.u.get_unchecked(i)))
}
}
}
}
impl<T, U> ExactSizeIterator for ZipSlices<T, U>
where T: Slice,
U: Slice
{}
unsafe impl<T, U> Slice for ZipSlices<T, U>
where T: Slice,
U: Slice
{
type Item = (T::Item, U::Item);
fn len(&self) -> usize {
self.len - self.index
}
unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item {
(self.t.get_unchecked(i),
self.u.get_unchecked(i))
}
}
/// A helper trait to let `ZipSlices` accept both `&[T]` and `&mut [T]`.
///
/// Unsafe trait because:
///
/// - Implementors must guarantee that `get_unchecked` is valid for all indices `0..len()`.
pub unsafe trait Slice {
/// The type of a reference to the slice's elements
type Item;
#[doc(hidden)]
fn len(&self) -> usize;
#[doc(hidden)]
unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item;
}
unsafe impl<'a, T> Slice for &'a [T] {
type Item = &'a T;
#[inline(always)]
fn len(&self) -> usize { (**self).len() }
#[inline(always)]
unsafe fn get_unchecked(&mut self, i: usize) -> &'a T {
debug_assert!(i < self.len());
(**self).get_unchecked(i)
}
}
unsafe impl<'a, T> Slice for &'a mut [T] {
type Item = &'a mut T;
#[inline(always)]
fn len(&self) -> usize { (**self).len() }
#[inline(always)]
unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T {
debug_assert!(i < self.len());
// override the lifetime constraints of &mut &'a mut [T]
(*(*self as *mut [T])).get_unchecked_mut(i)
}
}
#[test]
fn zipslices() {
let xs = [1, 2, 3, 4, 5, 6];
let ys = [1, 2, 3, 7];
::itertools::assert_equal(ZipSlices::new(&xs, &ys), xs.iter().zip(&ys));
let xs = [1, 2, 3, 4, 5, 6];
let mut ys = [0; 6];
for (x, y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) {
*y = *x;
}
::itertools::assert_equal(&xs, &ys);
}

View File

@@ -1,10 +1,13 @@
#![allow(unstable_name_collisions)]
use criterion::{criterion_group, criterion_main, Criterion};
use itertools::Itertools;
struct Unspecialized<I>(I);
impl<I> Iterator for Unspecialized<I>
where I: Iterator
where
I: Iterator,
{
type Item = I::Item;
@@ -25,8 +28,7 @@ mod specialization {
pub mod intersperse {
use super::*;
pub fn external(c: &mut Criterion)
{
pub fn external(c: &mut Criterion) {
let arr = [1; 1024];
c.bench_function("external", move |b| {
@@ -40,23 +42,23 @@ mod specialization {
});
}
pub fn internal_specialized(c: &mut Criterion)
{
pub fn internal_specialized(c: &mut Criterion) {
let arr = [1; 1024];
c.bench_function("internal specialized", move |b| {
b.iter(|| {
#[allow(clippy::unnecessary_fold)]
arr.iter().intersperse(&0).fold(0, |acc, x| acc + x)
})
});
}
pub fn internal_unspecialized(c: &mut Criterion)
{
pub fn internal_unspecialized(c: &mut Criterion) {
let arr = [1; 1024];
c.bench_function("internal unspecialized", move |b| {
b.iter(|| {
#[allow(clippy::unnecessary_fold)]
Unspecialized(arr.iter().intersperse(&0)).fold(0, |acc, x| acc + x)
})
});

View File

@@ -0,0 +1,61 @@
use criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion};
use itertools::Itertools;
use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng};
fn strict(b: &mut Bencher, (k, vals): &(usize, &Vec<usize>)) {
b.iter(|| black_box(vals.iter()).k_smallest(*k))
}
fn relaxed(b: &mut Bencher, (k, vals): &(usize, &Vec<usize>)) {
b.iter(|| black_box(vals.iter()).k_smallest_relaxed(*k))
}
fn ascending(n: usize) -> Vec<usize> {
(0..n).collect()
}
fn random(n: usize) -> Vec<usize> {
let mut vals = (0..n).collect_vec();
vals.shuffle(&mut StdRng::seed_from_u64(42));
vals
}
fn descending(n: usize) -> Vec<usize> {
(0..n).rev().collect()
}
fn k_smallest(c: &mut Criterion, order: &str, vals: fn(usize) -> Vec<usize>) {
let mut g = c.benchmark_group(format!("k-smallest/{order}"));
for log_n in 20..23 {
let n = 1 << log_n;
let vals = vals(n);
for log_k in 7..10 {
let k = 1 << log_k;
let params = format!("{log_n}/{log_k}");
let input = (k, &vals);
g.bench_with_input(BenchmarkId::new("strict", &params), &input, strict);
g.bench_with_input(BenchmarkId::new("relaxed", &params), &input, relaxed);
}
}
g.finish()
}
fn k_smallest_asc(c: &mut Criterion) {
k_smallest(c, "asc", ascending);
}
fn k_smallest_rand(c: &mut Criterion) {
k_smallest(c, "rand", random);
}
fn k_smallest_desc(c: &mut Criterion) {
k_smallest(c, "desc", descending);
}
criterion_group!(benches, k_smallest_asc, k_smallest_rand, k_smallest_desc);
criterion_main!(benches);

View File

@@ -20,17 +20,64 @@ fn powerset_n(c: &mut Criterion, n: usize) {
});
}
fn powerset_0(c: &mut Criterion) { powerset_n(c, 0); }
fn powerset_n_fold(c: &mut Criterion, n: usize) {
let id = format!("powerset {} fold", n);
c.bench_function(id.as_str(), move |b| {
b.iter(|| {
for _ in 0..calc_iters(n) {
(0..n).powerset().fold(0, |s, elt| s + black_box(elt).len());
}
})
});
}
fn powerset_1(c: &mut Criterion) { powerset_n(c, 1); }
fn powerset_0(c: &mut Criterion) {
powerset_n(c, 0);
}
fn powerset_2(c: &mut Criterion) { powerset_n(c, 2); }
fn powerset_1(c: &mut Criterion) {
powerset_n(c, 1);
}
fn powerset_4(c: &mut Criterion) { powerset_n(c, 4); }
fn powerset_2(c: &mut Criterion) {
powerset_n(c, 2);
}
fn powerset_8(c: &mut Criterion) { powerset_n(c, 8); }
fn powerset_4(c: &mut Criterion) {
powerset_n(c, 4);
}
fn powerset_12(c: &mut Criterion) { powerset_n(c, 12); }
fn powerset_8(c: &mut Criterion) {
powerset_n(c, 8);
}
fn powerset_12(c: &mut Criterion) {
powerset_n(c, 12);
}
fn powerset_0_fold(c: &mut Criterion) {
powerset_n_fold(c, 0);
}
fn powerset_1_fold(c: &mut Criterion) {
powerset_n_fold(c, 1);
}
fn powerset_2_fold(c: &mut Criterion) {
powerset_n_fold(c, 2);
}
fn powerset_4_fold(c: &mut Criterion) {
powerset_n_fold(c, 4);
}
fn powerset_8_fold(c: &mut Criterion) {
powerset_n_fold(c, 8);
}
fn powerset_12_fold(c: &mut Criterion) {
powerset_n_fold(c, 12);
}
criterion_group!(
benches,
@@ -40,5 +87,11 @@ criterion_group!(
powerset_4,
powerset_8,
powerset_12,
powerset_0_fold,
powerset_1_fold,
powerset_2_fold,
powerset_4_fold,
powerset_8_fold,
powerset_12_fold,
);
criterion_main!(benches);

View File

@@ -0,0 +1,669 @@
#![allow(unstable_name_collisions)]
use criterion::black_box;
use criterion::BenchmarkId;
use itertools::Itertools;
const NTH_INPUTS: &[usize] = &[0, 1, 2, 4, 8];
/// Create multiple functions each defining a benchmark group about iterator methods.
///
/// Each created group has functions with the following ids:
///
/// - `next`, `size_hint`, `count`, `last`, `nth`, `collect`, `fold`
/// - and when marked as `DoubleEndedIterator`: `next_back`, `nth_back`, `rfold`
/// - and when marked as `ExactSizeIterator`: `len`
///
/// Note that this macro can be called only once.
macro_rules! bench_specializations {
(
$(
$name:ident {
$($extra:ident)*
{$(
$init:stmt;
)*}
$iterator:expr
}
)*
) => {
$(
#[allow(unused_must_use)]
fn $name(c: &mut ::criterion::Criterion) {
let mut bench_group = c.benchmark_group(stringify!($name));
$(
$init
)*
let bench_first_its = {
let mut bench_idx = 0;
[0; 1000].map(|_| {
let mut it = $iterator;
if bench_idx != 0 {
it.nth(bench_idx - 1);
}
bench_idx += 1;
it
})
};
bench_specializations!(@Iterator bench_group bench_first_its: $iterator);
$(
bench_specializations!(@$extra bench_group bench_first_its: $iterator);
)*
bench_group.finish();
}
)*
::criterion::criterion_group!(benches, $($name, )*);
::criterion::criterion_main!(benches);
};
(@Iterator $group:ident $first_its:ident: $iterator:expr) => {
$group.bench_function("next", |bencher| bencher.iter(|| {
let mut it = $iterator;
while let Some(x) = it.next() {
black_box(x);
}
}));
$group.bench_function("size_hint", |bencher| bencher.iter(|| {
$first_its.iter().for_each(|it| {
black_box(it.size_hint());
})
}));
$group.bench_function("count", |bencher| bencher.iter(|| {
$iterator.count()
}));
$group.bench_function("last", |bencher| bencher.iter(|| {
$iterator.last()
}));
for n in NTH_INPUTS {
$group.bench_with_input(BenchmarkId::new("nth", n), n, |bencher, n| bencher.iter(|| {
for start in 0_usize..10 {
let mut it = $iterator;
if let Some(s) = start.checked_sub(1) {
black_box(it.nth(s));
}
while let Some(x) = it.nth(*n) {
black_box(x);
}
}
}));
}
$group.bench_function("collect", |bencher| bencher.iter(|| {
$iterator.collect::<Vec<_>>()
}));
$group.bench_function("fold", |bencher| bencher.iter(|| {
$iterator.fold((), |(), x| {
black_box(x);
})
}));
};
(@DoubleEndedIterator $group:ident $_first_its:ident: $iterator:expr) => {
$group.bench_function("next_back", |bencher| bencher.iter(|| {
let mut it = $iterator;
while let Some(x) = it.next_back() {
black_box(x);
}
}));
for n in NTH_INPUTS {
$group.bench_with_input(BenchmarkId::new("nth_back", n), n, |bencher, n| bencher.iter(|| {
for start in 0_usize..10 {
let mut it = $iterator;
if let Some(s) = start.checked_sub(1) {
black_box(it.nth_back(s));
}
while let Some(x) = it.nth_back(*n) {
black_box(x);
}
}
}));
}
$group.bench_function("rfold", |bencher| bencher.iter(|| {
$iterator.rfold((), |(), x| {
black_box(x);
})
}));
};
(@ExactSizeIterator $group:ident $first_its:ident: $_iterator:expr) => {
$group.bench_function("len", |bencher| bencher.iter(|| {
$first_its.iter().for_each(|it| {
black_box(it.len());
})
}));
};
}
// Usage examples:
// - For `ZipLongest::fold` only:
// cargo bench --bench specializations zip_longest/fold
// - For `.combinations(k).nth(8)`:
// cargo bench --bench specializations combinations./nth/8
bench_specializations! {
interleave {
{
let v1 = black_box(vec![0; 1024]);
let v2 = black_box(vec![0; 768]);
}
v1.iter().interleave(&v2)
}
interleave_shortest {
{
let v1 = black_box(vec![0; 1024]);
let v2 = black_box(vec![0; 768]);
}
v1.iter().interleave_shortest(&v2)
}
batching {
{
let v = black_box(vec![0; 1024]);
}
v.iter().batching(Iterator::next)
}
tuple_windows1 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuple_windows::<(_,)>()
}
tuple_windows2 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuple_windows::<(_, _)>()
}
tuple_windows3 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuple_windows::<(_, _, _)>()
}
tuple_windows4 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuple_windows::<(_, _, _, _)>()
}
circular_tuple_windows1 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().circular_tuple_windows::<(_,)>()
}
circular_tuple_windows2 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().circular_tuple_windows::<(_, _)>()
}
circular_tuple_windows3 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().circular_tuple_windows::<(_, _, _)>()
}
circular_tuple_windows4 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().circular_tuple_windows::<(_, _, _, _)>()
}
tuples1 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuples::<(_,)>()
}
tuples2 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuples::<(_, _)>()
}
tuples3 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuples::<(_, _, _)>()
}
tuples4 {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuples::<(_, _, _, _)>()
}
tuple_buffer {
ExactSizeIterator
{
let v = black_box(vec![0; 11]);
// Short but the buffer can't have 12 or more elements.
}
{
let mut it = v.iter().tuples::<(_, _, _, _, _, _, _, _, _, _, _, _)>();
it.next(); // No element but it fills the buffer.
it.into_buffer()
}
}
cartesian_product {
{
let v = black_box(vec![0; 16]);
}
itertools::iproduct!(&v, &v, &v)
}
multi_cartesian_product {
{
let vs = black_box([0; 3].map(|_| vec![0; 16]));
}
vs.iter().multi_cartesian_product()
}
coalesce {
{
let v = black_box(vec![0; 1024]);
}
v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) })
}
dedup {
{
let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
}
v.iter().dedup()
}
dedup_by {
{
let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
}
v.iter().dedup_by(PartialOrd::ge)
}
dedup_with_count {
{
let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
}
v.iter().dedup_with_count()
}
dedup_by_with_count {
{
let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
}
v.iter().dedup_by_with_count(PartialOrd::ge)
}
duplicates {
DoubleEndedIterator
{
let v = black_box((0..32).cycle().take(1024).collect_vec());
}
v.iter().duplicates()
}
duplicates_by {
DoubleEndedIterator
{
let v = black_box((0..1024).collect_vec());
}
v.iter().duplicates_by(|x| *x % 10)
}
unique {
DoubleEndedIterator
{
let v = black_box((0..32).cycle().take(1024).collect_vec());
}
v.iter().unique()
}
unique_by {
DoubleEndedIterator
{
let v = black_box((0..1024).collect_vec());
}
v.iter().unique_by(|x| *x % 50)
}
take_while_inclusive {
{
let v = black_box((0..1024).collect_vec());
}
v.iter().take_while_inclusive(|x| **x < 1000)
}
pad_using {
DoubleEndedIterator
ExactSizeIterator
{
let v = black_box((0..1024).collect_vec());
}
v.iter().copied().pad_using(2048, |i| 5 * i)
}
positions {
DoubleEndedIterator
{
let v = black_box((0..1024).collect_vec());
}
v.iter().positions(|x| x % 5 == 0)
}
update {
DoubleEndedIterator
ExactSizeIterator
{
let v = black_box((0_i32..1024).collect_vec());
}
v.iter().copied().update(|x| *x *= 7)
}
tuple_combinations1 {
{
let v = black_box(vec![0; 1024]);
}
v.iter().tuple_combinations::<(_,)>()
}
tuple_combinations2 {
{
let v = black_box(vec![0; 64]);
}
v.iter().tuple_combinations::<(_, _)>()
}
tuple_combinations3 {
{
let v = black_box(vec![0; 64]);
}
v.iter().tuple_combinations::<(_, _, _)>()
}
tuple_combinations4 {
{
let v = black_box(vec![0; 64]);
}
v.iter().tuple_combinations::<(_, _, _, _)>()
}
intersperse {
{
let v = black_box(vec![0; 1024]);
let n = black_box(0);
}
v.iter().intersperse(&n)
}
intersperse_with {
{
let v = black_box(vec![0; 1024]);
let n = black_box(0);
}
v.iter().intersperse_with(|| &n)
}
combinations1 {
{
let v = black_box(vec![0; 1792]);
}
v.iter().combinations(1)
}
combinations2 {
{
let v = black_box(vec![0; 60]);
}
v.iter().combinations(2)
}
combinations3 {
{
let v = black_box(vec![0; 23]);
}
v.iter().combinations(3)
}
combinations4 {
{
let v = black_box(vec![0; 16]);
}
v.iter().combinations(4)
}
combinations_with_replacement1 {
{
let v = black_box(vec![0; 4096]);
}
v.iter().combinations_with_replacement(1)
}
combinations_with_replacement2 {
{
let v = black_box(vec![0; 90]);
}
v.iter().combinations_with_replacement(2)
}
combinations_with_replacement3 {
{
let v = black_box(vec![0; 28]);
}
v.iter().combinations_with_replacement(3)
}
combinations_with_replacement4 {
{
let v = black_box(vec![0; 16]);
}
v.iter().combinations_with_replacement(4)
}
permutations1 {
{
let v = black_box(vec![0; 1024]);
}
v.iter().permutations(1)
}
permutations2 {
{
let v = black_box(vec![0; 36]);
}
v.iter().permutations(2)
}
permutations3 {
{
let v = black_box(vec![0; 12]);
}
v.iter().permutations(3)
}
permutations4 {
{
let v = black_box(vec![0; 8]);
}
v.iter().permutations(4)
}
powerset {
{
let v = black_box(vec![0; 10]);
}
v.iter().powerset()
}
while_some {
{}
(0..)
.map(black_box)
.map(|i| char::from_digit(i, 16))
.while_some()
}
with_position {
ExactSizeIterator
{
let v = black_box((0..10240).collect_vec());
}
v.iter().with_position()
}
zip_longest {
DoubleEndedIterator
ExactSizeIterator
{
let xs = black_box(vec![0; 1024]);
let ys = black_box(vec![0; 768]);
}
xs.iter().zip_longest(ys.iter())
}
zip_eq {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
v.iter().zip_eq(v.iter().rev())
}
multizip {
DoubleEndedIterator
ExactSizeIterator
{
let v1 = black_box(vec![0; 1024]);
let v2 = black_box(vec![0; 768]);
let v3 = black_box(vec![0; 2048]);
}
itertools::multizip((&v1, &v2, &v3))
}
izip {
DoubleEndedIterator
ExactSizeIterator
{
let v1 = black_box(vec![0; 1024]);
let v2 = black_box(vec![0; 768]);
let v3 = black_box(vec![0; 2048]);
}
itertools::izip!(&v1, &v2, &v3)
}
put_back {
{
let v = black_box(vec![0; 1024]);
}
itertools::put_back(&v).with_value(black_box(&0))
}
put_back_n {
{
let v1 = black_box(vec![0; 1024]);
let v2 = black_box(vec![0; 16]);
}
{
let mut it = itertools::put_back_n(&v1);
for n in &v2 {
it.put_back(n);
}
it
}
}
exactly_one_error {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
}
// Use `at_most_one` would be similar.
v.iter().exactly_one().unwrap_err()
}
multipeek {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
let n = black_box(16);
}
{
let mut it = v.iter().multipeek();
for _ in 0..n {
it.peek();
}
it
}
}
peek_nth {
ExactSizeIterator
{
let v = black_box(vec![0; 1024]);
let n = black_box(16);
}
{
let mut it = itertools::peek_nth(&v);
it.peek_nth(n);
it
}
}
repeat_n {
DoubleEndedIterator
ExactSizeIterator
{}
itertools::repeat_n(black_box(0), black_box(1024))
}
merge {
{
let v1 = black_box((0..1024).collect_vec());
let v2 = black_box((0..768).collect_vec());
}
v1.iter().merge(&v2)
}
merge_by {
{
let v1 = black_box((0..1024).collect_vec());
let v2 = black_box((0..768).collect_vec());
}
v1.iter().merge_by(&v2, PartialOrd::ge)
}
merge_join_by_ordering {
{
let v1 = black_box((0..1024).collect_vec());
let v2 = black_box((0..768).collect_vec());
}
v1.iter().merge_join_by(&v2, Ord::cmp)
}
merge_join_by_bool {
{
let v1 = black_box((0..1024).collect_vec());
let v2 = black_box((0..768).collect_vec());
}
v1.iter().merge_join_by(&v2, PartialOrd::ge)
}
kmerge {
{
let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]);
}
vs.iter().kmerge()
}
kmerge_by {
{
let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]);
}
vs.iter().kmerge_by(PartialOrd::ge)
}
map_into {
DoubleEndedIterator
ExactSizeIterator
{
let v = black_box(vec![0_u8; 1024]);
}
v.iter().copied().map_into::<u32>()
}
map_ok {
DoubleEndedIterator
ExactSizeIterator
{
let v = black_box((0_u32..1024)
.map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) })
.collect_vec());
}
v.iter().copied().map_ok(|x| x + 1)
}
filter_ok {
DoubleEndedIterator
{
let v = black_box((0_u32..1024)
.map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) })
.collect_vec());
}
v.iter().copied().filter_ok(|x| x % 3 == 0)
}
filter_map_ok {
DoubleEndedIterator
{
let v = black_box((0_u32..1024)
.map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) })
.collect_vec());
}
v.iter().copied().filter_map_ok(|x| if x % 3 == 0 { Some(x + 1) } else { None })
}
flatten_ok {
DoubleEndedIterator
{
let d = black_box(vec![0; 8]);
let v = black_box((0..512)
.map(|x| if x % 2 == 0 { Ok(&d) } else { Err(x) })
.collect_vec());
}
v.iter().copied().flatten_ok()
}
}

View File

@@ -1,12 +1,15 @@
use criterion::{criterion_group, criterion_main, Criterion};
use itertools::{Itertools, cloned};
#![allow(deprecated)]
trait IterEx : Iterator {
use criterion::{criterion_group, criterion_main, Criterion};
use itertools::{cloned, Itertools};
trait IterEx: Iterator {
// Another efficient implementation against which to compare,
// but needs `std` so is less desirable.
fn tree_fold1_vec<F>(self, mut f: F) -> Option<Self::Item>
where F: FnMut(Self::Item, Self::Item) -> Self::Item,
Self: Sized,
fn tree_reduce_vec<F>(self, mut f: F) -> Option<Self::Item>
where
F: FnMut(Self::Item, Self::Item) -> Self::Item,
Self: Sized,
{
let hint = self.size_hint().0;
let cap = std::mem::size_of::<usize>() * 8 - hint.leading_zeros() as usize;
@@ -21,24 +24,23 @@ trait IterEx : Iterator {
stack.into_iter().fold1(f)
}
}
impl<T:Iterator> IterEx for T {}
impl<T: Iterator> IterEx for T {}
macro_rules! def_benchs {
($N:expr,
$FUN:ident,
$BENCH_NAME:ident,
) => (
) => {
mod $BENCH_NAME {
use super::*;
pub fn sum(c: &mut Criterion) {
let v: Vec<u32> = (0.. $N).collect();
let v: Vec<u32> = (0..$N).collect();
c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " sum"), move |b| {
b.iter(|| {
cloned(&v).$FUN(|x, y| x + y)
})
});
c.bench_function(
&(stringify!($BENCH_NAME).replace('_', " ") + " sum"),
move |b| b.iter(|| cloned(&v).$FUN(|x, y| x + y)),
);
}
pub fn complex_iter(c: &mut Criterion) {
@@ -46,11 +48,10 @@ macro_rules! def_benchs {
let v = (5..).take($N / 2);
let it = u.chain(v);
c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"), move |b| {
b.iter(|| {
it.clone().map(|x| x as f32).$FUN(f32::atan2)
})
});
c.bench_function(
&(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"),
move |b| b.iter(|| it.clone().map(|x| x as f32).$FUN(f32::atan2)),
);
}
pub fn string_format(c: &mut Criterion) {
@@ -58,13 +59,18 @@ macro_rules! def_benchs {
// size to not waste too much time in travis. The allocations
// in here are so expensive anyway that it'll still take
// way longer per iteration than the other two benchmarks.
let v: Vec<u32> = (0.. ($N/4)).collect();
let v: Vec<u32> = (0..($N / 4)).collect();
c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " string format"), move |b| {
b.iter(|| {
cloned(&v).map(|x| x.to_string()).$FUN(|x, y| format!("{} + {}", x, y))
})
});
c.bench_function(
&(stringify!($BENCH_NAME).replace('_', " ") + " string format"),
move |b| {
b.iter(|| {
cloned(&v)
.map(|x| x.to_string())
.$FUN(|x, y| format!("{} + {}", x, y))
})
},
);
}
}
@@ -74,71 +80,71 @@ macro_rules! def_benchs {
$BENCH_NAME::complex_iter,
$BENCH_NAME::string_format,
);
)
};
}
def_benchs!{
def_benchs! {
10_000,
fold1,
fold1_10k,
}
def_benchs!{
def_benchs! {
10_000,
tree_fold1,
tree_fold1_stack_10k,
tree_reduce,
tree_reduce_stack_10k,
}
def_benchs!{
def_benchs! {
10_000,
tree_fold1_vec,
tree_fold1_vec_10k,
tree_reduce_vec,
tree_reduce_vec_10k,
}
def_benchs!{
def_benchs! {
100,
fold1,
fold1_100,
}
def_benchs!{
def_benchs! {
100,
tree_fold1,
tree_fold1_stack_100,
tree_reduce,
tree_reduce_stack_100,
}
def_benchs!{
def_benchs! {
100,
tree_fold1_vec,
tree_fold1_vec_100,
tree_reduce_vec,
tree_reduce_vec_100,
}
def_benchs!{
def_benchs! {
8,
fold1,
fold1_08,
}
def_benchs!{
def_benchs! {
8,
tree_fold1,
tree_fold1_stack_08,
tree_reduce,
tree_reduce_stack_08,
}
def_benchs!{
def_benchs! {
8,
tree_fold1_vec,
tree_fold1_vec_08,
tree_reduce_vec,
tree_reduce_vec_08,
}
criterion_main!(
fold1_10k,
tree_fold1_stack_10k,
tree_fold1_vec_10k,
tree_reduce_stack_10k,
tree_reduce_vec_10k,
fold1_100,
tree_fold1_stack_100,
tree_fold1_vec_100,
tree_reduce_stack_100,
tree_reduce_vec_100,
fold1_08,
tree_fold1_stack_08,
tree_fold1_vec_08,
tree_reduce_stack_08,
tree_reduce_vec_08,
);

View File

@@ -33,7 +33,7 @@ fn sum_s4(s: &[u32]) -> u32 {
s4(s[0], s[1], s[2], s[3])
}
fn sum_t1(s: &(&u32, )) -> u32 {
fn sum_t1(s: &(&u32,)) -> u32 {
s1(*s.0)
}
@@ -60,9 +60,9 @@ macro_rules! def_benchs {
$WINDOWS:ident;
$FOR_CHUNKS:ident,
$FOR_WINDOWS:ident
) => (
) => {
fn $FOR_CHUNKS(c: &mut Criterion) {
let v: Vec<u32> = (0.. $N * 1_000).collect();
let v: Vec<u32> = (0..$N * 1_000).collect();
let mut s = 0;
c.bench_function(&stringify!($FOR_CHUNKS).replace('_', " "), move |b| {
b.iter(|| {
@@ -90,7 +90,7 @@ macro_rules! def_benchs {
}
fn $TUPLES(c: &mut Criterion) {
let v: Vec<u32> = (0.. $N * 1_000).collect();
let v: Vec<u32> = (0..$N * 1_000).collect();
let mut s = 0;
c.bench_function(&stringify!($TUPLES).replace('_', " "), move |b| {
b.iter(|| {
@@ -103,7 +103,7 @@ macro_rules! def_benchs {
}
fn $CHUNKS(c: &mut Criterion) {
let v: Vec<u32> = (0.. $N * 1_000).collect();
let v: Vec<u32> = (0..$N * 1_000).collect();
let mut s = 0;
c.bench_function(&stringify!($CHUNKS).replace('_', " "), move |b| {
b.iter(|| {
@@ -150,10 +150,10 @@ macro_rules! def_benchs {
$TUPLE_WINDOWS,
$WINDOWS,
);
)
};
}
def_benchs!{
def_benchs! {
1;
benches_1,
sum_t1,
@@ -166,7 +166,7 @@ def_benchs!{
for_windows_1
}
def_benchs!{
def_benchs! {
2;
benches_2,
sum_t2,
@@ -179,7 +179,7 @@ def_benchs!{
for_windows_2
}
def_benchs!{
def_benchs! {
3;
benches_3,
sum_t3,
@@ -192,7 +192,7 @@ def_benchs!{
for_windows_3
}
def_benchs!{
def_benchs! {
4;
benches_4,
sum_t4,
@@ -205,9 +205,4 @@ def_benchs!{
for_windows_4
}
criterion_main!(
benches_1,
benches_2,
benches_3,
benches_4,
);
criterion_main!(benches_1, benches_2, benches_3, benches_4,);

View File

@@ -1 +0,0 @@
msrv = "1.36.0"

View File

@@ -3,14 +3,13 @@
/// and does some simple manipulations.
///
/// Iterators and itertools functionality are used throughout.
use itertools::Itertools;
use std::collections::HashMap;
use std::iter::repeat;
use std::num::ParseFloatError;
use std::str::FromStr;
static DATA: &'static str = include_str!("iris.data");
static DATA: &str = include_str!("iris.data");
#[derive(Clone, Debug)]
struct Iris {
@@ -18,6 +17,7 @@ struct Iris {
data: [f32; 4],
}
#[allow(dead_code)] // fields are currently ignored
#[derive(Clone, Debug)]
enum ParseError {
Numeric(ParseFloatError),
@@ -26,7 +26,7 @@ enum ParseError {
impl From<ParseFloatError> for ParseError {
fn from(err: ParseFloatError) -> Self {
ParseError::Numeric(err)
Self::Numeric(err)
}
}
@@ -35,8 +35,11 @@ impl FromStr for Iris {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iris = Iris { name: "".into(), data: [0.; 4] };
let mut parts = s.split(",").map(str::trim);
let mut iris = Self {
name: "".into(),
data: [0.; 4],
};
let mut parts = s.split(',').map(str::trim);
// using Iterator::by_ref()
for (index, part) in parts.by_ref().take(4).enumerate() {
@@ -45,7 +48,7 @@ impl FromStr for Iris {
if let Some(name) = parts.next() {
iris.name = name.into();
} else {
return Err(ParseError::Other("Missing name"))
return Err(ParseError::Other("Missing name"));
}
Ok(iris)
}
@@ -53,12 +56,13 @@ impl FromStr for Iris {
fn main() {
// using Itertools::fold_results to create the result of parsing
let irises = DATA.lines()
.map(str::parse)
.fold_ok(Vec::new(), |mut v, iris: Iris| {
v.push(iris);
v
});
let irises = DATA
.lines()
.map(str::parse)
.fold_ok(Vec::new(), |mut v, iris: Iris| {
v.push(iris);
v
});
let mut irises = match irises {
Err(e) => {
println!("Error parsing: {:?}", e);
@@ -74,19 +78,18 @@ fn main() {
let mut plot_symbols = "+ox".chars().cycle();
let mut symbolmap = HashMap::new();
// using Itertools::group_by
for (species, species_group) in &irises.iter().group_by(|iris| &iris.name) {
// using Itertools::chunk_by
for (species, species_chunk) in &irises.iter().chunk_by(|iris| &iris.name) {
// assign a plot symbol
symbolmap.entry(species).or_insert_with(|| {
plot_symbols.next().unwrap()
});
symbolmap
.entry(species)
.or_insert_with(|| plot_symbols.next().unwrap());
println!("{} (symbol={})", species, symbolmap[species]);
for iris in species_group {
for iris in species_chunk {
// using Itertools::format for lazy formatting
println!("{:>3.1}", iris.data.iter().format(", "));
}
}
// Look at all combinations of the four columns

View File

@@ -4,62 +4,78 @@ use std::iter::FusedIterator;
use crate::size_hint;
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct CoalesceBy<I, F, T>
pub struct CoalesceBy<I, F, C>
where
I: Iterator,
C: CountItem<I::Item>,
{
iter: I,
last: Option<T>,
/// `last` is `None` while no item have been taken out of `iter` (at definition).
/// Then `last` will be `Some(Some(item))` until `iter` is exhausted,
/// in which case `last` will be `Some(None)`.
last: Option<Option<C::CItem>>,
f: F,
}
impl<I: Clone, F: Clone, T: Clone> Clone for CoalesceBy<I, F, T>
impl<I, F, C> Clone for CoalesceBy<I, F, C>
where
I: Iterator,
I: Clone + Iterator,
F: Clone,
C: CountItem<I::Item>,
C::CItem: Clone,
{
clone_fields!(last, iter, f);
}
impl<I, F, T> fmt::Debug for CoalesceBy<I, F, T>
impl<I, F, C> fmt::Debug for CoalesceBy<I, F, C>
where
I: Iterator + fmt::Debug,
T: fmt::Debug,
C: CountItem<I::Item>,
C::CItem: fmt::Debug,
{
debug_fmt_fields!(CoalesceBy, iter);
debug_fmt_fields!(CoalesceBy, iter, last);
}
pub trait CoalescePredicate<Item, T> {
fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)>;
}
impl<I, F, T> Iterator for CoalesceBy<I, F, T>
impl<I, F, C> Iterator for CoalesceBy<I, F, C>
where
I: Iterator,
F: CoalescePredicate<I::Item, T>,
F: CoalescePredicate<I::Item, C::CItem>,
C: CountItem<I::Item>,
{
type Item = T;
type Item = C::CItem;
fn next(&mut self) -> Option<Self::Item> {
let Self { iter, last, f } = self;
// this fuses the iterator
let last = self.last.take()?;
let init = match last {
Some(elt) => elt.take(),
None => {
*last = Some(None);
iter.next().map(C::new)
}
}?;
let self_last = &mut self.last;
let self_f = &mut self.f;
Some(
self.iter
.try_fold(last, |last, next| match self_f.coalesce_pair(last, next) {
Ok(joined) => Ok(joined),
Err((last_, next_)) => {
*self_last = Some(next_);
Err(last_)
}
})
.unwrap_or_else(|x| x),
iter.try_fold(init, |accum, next| match f.coalesce_pair(accum, next) {
Ok(joined) => Ok(joined),
Err((last_, next_)) => {
*last = Some(Some(next_));
Err(last_)
}
})
.unwrap_or_else(|x| x),
)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), self.last.is_some() as usize);
let (low, hi) = size_hint::add_scalar(
self.iter.size_hint(),
matches!(self.last, Some(Some(_))) as usize,
);
((low > 0) as usize, hi)
}
@@ -67,9 +83,13 @@ where
where
FnAcc: FnMut(Acc, Self::Item) -> Acc,
{
if let Some(last) = self.last {
let mut f = self.f;
let (last, acc) = self.iter.fold((last, acc), |(last, acc), elt| {
let Self {
mut iter,
last,
mut f,
} = self;
if let Some(last) = last.unwrap_or_else(|| iter.next().map(C::new)) {
let (last, acc) = iter.fold((last, acc), |(last, acc), elt| {
match f.coalesce_pair(last, elt) {
Ok(joined) => (joined, acc),
Err((last_, next_)) => (next_, fn_acc(acc, last_)),
@@ -82,12 +102,43 @@ where
}
}
impl<I: Iterator, F: CoalescePredicate<I::Item, T>, T> FusedIterator for CoalesceBy<I, F, T> {}
impl<I, F, C> FusedIterator for CoalesceBy<I, F, C>
where
I: Iterator,
F: CoalescePredicate<I::Item, C::CItem>,
C: CountItem<I::Item>,
{
}
pub struct NoCount;
pub struct WithCount;
pub trait CountItem<T> {
type CItem;
fn new(t: T) -> Self::CItem;
}
impl<T> CountItem<T> for NoCount {
type CItem = T;
#[inline(always)]
fn new(t: T) -> T {
t
}
}
impl<T> CountItem<T> for WithCount {
type CItem = (usize, T);
#[inline(always)]
fn new(t: T) -> (usize, T) {
(1, t)
}
}
/// An iterator adaptor that may join together adjacent elements.
///
/// See [`.coalesce()`](crate::Itertools::coalesce) for more information.
pub type Coalesce<I, F> = CoalesceBy<I, F, <I as Iterator>::Item>;
pub type Coalesce<I, F> = CoalesceBy<I, F, NoCount>;
impl<F, Item, T> CoalescePredicate<Item, T> for F
where
@@ -99,12 +150,12 @@ where
}
/// Create a new `Coalesce`.
pub fn coalesce<I, F>(mut iter: I, f: F) -> Coalesce<I, F>
pub fn coalesce<I, F>(iter: I, f: F) -> Coalesce<I, F>
where
I: Iterator,
{
Coalesce {
last: iter.next(),
last: None,
iter,
f,
}
@@ -113,7 +164,7 @@ where
/// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function.
///
/// See [`.dedup_by()`](crate::Itertools::dedup_by) or [`.dedup()`](crate::Itertools::dedup) for more information.
pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as Iterator>::Item>;
pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, NoCount>;
#[derive(Clone)]
pub struct DedupPred2CoalescePred<DP>(DP);
@@ -156,12 +207,12 @@ impl<T, F: FnMut(&T, &T) -> bool> DedupPredicate<T> for F {
}
/// Create a new `DedupBy`.
pub fn dedup_by<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupBy<I, Pred>
pub fn dedup_by<I, Pred>(iter: I, dedup_pred: Pred) -> DedupBy<I, Pred>
where
I: Iterator,
{
DedupBy {
last: iter.next(),
last: None,
iter,
f: DedupPred2CoalescePred(dedup_pred),
}
@@ -186,7 +237,7 @@ where
/// See [`.dedup_by_with_count()`](crate::Itertools::dedup_by_with_count) or
/// [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information.
pub type DedupByWithCount<I, Pred> =
CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>;
CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, WithCount>;
#[derive(Clone, Debug)]
pub struct DedupPredWithCount2CoalescePred<DP>(DP);
@@ -215,12 +266,12 @@ where
pub type DedupWithCount<I> = DedupByWithCount<I, DedupEq>;
/// Create a new `DedupByWithCount`.
pub fn dedup_by_with_count<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred>
pub fn dedup_by_with_count<I, Pred>(iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred>
where
I: Iterator,
{
DedupByWithCount {
last: iter.next().map(|v| (1, v)),
last: None,
iter,
f: DedupPredWithCount2CoalescePred(dedup_pred),
}

View File

@@ -4,8 +4,8 @@ use std::marker::PhantomData;
#[derive(Clone, Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MapSpecialCase<I, F> {
iter: I,
f: F,
pub(crate) iter: I,
pub(crate) f: F,
}
pub trait MapSpecialCaseFn<T> {
@@ -67,10 +67,6 @@ where
/// See [`.map_ok()`](crate::Itertools::map_ok) for more information.
pub type MapOk<I, F> = MapSpecialCase<I, MapSpecialCaseFnOk<F>>;
/// See [`MapOk`].
#[deprecated(note = "Use MapOk instead", since = "0.10.0")]
pub type MapResults<I, F> = MapOk<I, F>;
impl<F, T, U, E> MapSpecialCaseFn<Result<T, E>> for MapSpecialCaseFnOk<F>
where
F: FnMut(T) -> U,
@@ -112,9 +108,19 @@ impl<T: Into<U>, U> MapSpecialCaseFn<T> for MapSpecialCaseFnInto<U> {
}
}
#[derive(Clone, Debug)]
pub struct MapSpecialCaseFnInto<U>(PhantomData<U>);
impl<U> std::fmt::Debug for MapSpecialCaseFnInto<U> {
debug_fmt_fields!(MapSpecialCaseFnInto, 0);
}
impl<U> Clone for MapSpecialCaseFnInto<U> {
#[inline]
fn clone(&self) -> Self {
Self(PhantomData)
}
}
/// Create a new [`MapInto`] iterator.
pub fn map_into<I, R>(iter: I) -> MapInto<I, R> {
MapSpecialCase {

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,52 @@
#![cfg(feature = "use_alloc")]
use crate::size_hint;
use crate::Itertools;
use Option::{self as State, None as ProductEnded, Some as ProductInProgress};
use Option::{self as CurrentItems, None as NotYetPopulated, Some as Populated};
use alloc::vec::Vec;
use crate::size_hint;
#[derive(Clone)]
/// An iterator adaptor that iterates over the cartesian product of
/// multiple iterators of type `I`.
///
/// An iterator element type is `Vec<I>`.
/// An iterator element type is `Vec<I::Item>`.
///
/// See [`.multi_cartesian_product()`](crate::Itertools::multi_cartesian_product)
/// for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
where I: Iterator + Clone,
I::Item: Clone;
pub struct MultiProduct<I>(State<MultiProductInner<I>>)
where
I: Iterator + Clone,
I::Item: Clone;
#[derive(Clone)]
/// Internals for `MultiProduct`.
struct MultiProductInner<I>
where
I: Iterator + Clone,
I::Item: Clone,
{
/// Holds the iterators.
iters: Vec<MultiProductIter<I>>,
/// Not populated at the beginning then it holds the current item of each iterator.
cur: CurrentItems<Vec<I::Item>>,
}
impl<I> std::fmt::Debug for MultiProduct<I>
where
I: Iterator + Clone + std::fmt::Debug,
I::Item: Clone + std::fmt::Debug,
{
debug_fmt_fields!(CoalesceBy, 0);
debug_fmt_fields!(MultiProduct, 0);
}
impl<I> std::fmt::Debug for MultiProductInner<I>
where
I: Iterator + Clone + std::fmt::Debug,
I::Item: Clone + std::fmt::Debug,
{
debug_fmt_fields!(MultiProductInner, iters, cur);
}
/// Create a new cartesian product iterator over an arbitrary number
@@ -31,200 +54,178 @@ where
///
/// Iterator element is of type `Vec<H::Item::Item>`.
pub fn multi_cartesian_product<H>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
where H: Iterator,
H::Item: IntoIterator,
<H::Item as IntoIterator>::IntoIter: Clone,
<H::Item as IntoIterator>::Item: Clone
where
H: Iterator,
H::Item: IntoIterator,
<H::Item as IntoIterator>::IntoIter: Clone,
<H::Item as IntoIterator>::Item: Clone,
{
MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect())
let inner = MultiProductInner {
iters: iters
.map(|i| MultiProductIter::new(i.into_iter()))
.collect(),
cur: NotYetPopulated,
};
MultiProduct(ProductInProgress(inner))
}
#[derive(Clone, Debug)]
/// Holds the state of a single iterator within a `MultiProduct`.
struct MultiProductIter<I>
where I: Iterator + Clone,
I::Item: Clone
where
I: Iterator + Clone,
I::Item: Clone,
{
cur: Option<I::Item>,
iter: I,
iter_orig: I,
}
/// Holds the current state during an iteration of a `MultiProduct`.
#[derive(Debug)]
enum MultiProductIterState {
StartOfIter,
MidIter { on_first_iter: bool },
}
impl<I> MultiProduct<I>
where I: Iterator + Clone,
I::Item: Clone
{
/// Iterates the rightmost iterator, then recursively iterates iterators
/// to the left if necessary.
///
/// Returns true if the iteration succeeded, else false.
fn iterate_last(
multi_iters: &mut [MultiProductIter<I>],
mut state: MultiProductIterState
) -> bool {
use self::MultiProductIterState::*;
if let Some((last, rest)) = multi_iters.split_last_mut() {
let on_first_iter = match state {
StartOfIter => {
let on_first_iter = !last.in_progress();
state = MidIter { on_first_iter };
on_first_iter
},
MidIter { on_first_iter } => on_first_iter
};
if !on_first_iter {
last.iterate();
}
if last.in_progress() {
true
} else if MultiProduct::iterate_last(rest, state) {
last.reset();
last.iterate();
// If iterator is None twice consecutively, then iterator is
// empty; whole product is empty.
last.in_progress()
} else {
false
}
} else {
// Reached end of iterator list. On initialisation, return true.
// At end of iteration (final iterator finishes), finish.
match state {
StartOfIter => false,
MidIter { on_first_iter } => on_first_iter
}
}
}
/// Returns the unwrapped value of the next iteration.
fn curr_iterator(&self) -> Vec<I::Item> {
self.0.iter().map(|multi_iter| {
multi_iter.cur.clone().unwrap()
}).collect()
}
/// Returns true if iteration has started and has not yet finished; false
/// otherwise.
fn in_progress(&self) -> bool {
if let Some(last) = self.0.last() {
last.in_progress()
} else {
false
}
}
}
impl<I> MultiProductIter<I>
where I: Iterator + Clone,
I::Item: Clone
where
I: Iterator + Clone,
I::Item: Clone,
{
fn new(iter: I) -> Self {
MultiProductIter {
cur: None,
Self {
iter: iter.clone(),
iter_orig: iter
iter_orig: iter,
}
}
/// Iterate the managed iterator.
fn iterate(&mut self) {
self.cur = self.iter.next();
}
/// Reset the managed iterator.
fn reset(&mut self) {
self.iter = self.iter_orig.clone();
}
/// Returns true if the current iterator has been started and has not yet
/// finished; false otherwise.
fn in_progress(&self) -> bool {
self.cur.is_some()
}
}
impl<I> Iterator for MultiProduct<I>
where I: Iterator + Clone,
I::Item: Clone
where
I: Iterator + Clone,
I::Item: Clone,
{
type Item = Vec<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
if MultiProduct::iterate_last(
&mut self.0,
MultiProductIterState::StartOfIter
) {
Some(self.curr_iterator())
} else {
None
// This fuses the iterator.
let inner = self.0.as_mut()?;
match &mut inner.cur {
Populated(values) => {
debug_assert!(!inner.iters.is_empty());
// Find (from the right) a non-finished iterator and
// reset the finished ones encountered.
for (iter, item) in inner.iters.iter_mut().zip(values.iter_mut()).rev() {
if let Some(new) = iter.iter.next() {
*item = new;
return Some(values.clone());
} else {
iter.iter = iter.iter_orig.clone();
// `cur` is populated so the untouched `iter_orig` can not be empty.
*item = iter.iter.next().unwrap();
}
}
self.0 = ProductEnded;
None
}
// Only the first time.
NotYetPopulated => {
let next: Option<Vec<_>> = inner.iters.iter_mut().map(|i| i.iter.next()).collect();
if next.is_none() || inner.iters.is_empty() {
// This cartesian product had at most one item to generate and now ends.
self.0 = ProductEnded;
} else {
inner.cur.clone_from(&next);
}
next
}
}
}
fn count(self) -> usize {
if self.0.is_empty() {
return 0;
match self.0 {
ProductEnded => 0,
// The iterator is fresh so the count is the product of the length of each iterator:
// - If one of them is empty, stop counting.
// - Less `count()` calls than the general case.
ProductInProgress(MultiProductInner {
iters,
cur: NotYetPopulated,
}) => iters
.into_iter()
.map(|iter| iter.iter_orig.count())
.try_fold(1, |product, count| {
if count == 0 {
None
} else {
Some(product * count)
}
})
.unwrap_or_default(),
// The general case.
ProductInProgress(MultiProductInner {
iters,
cur: Populated(_),
}) => iters.into_iter().fold(0, |mut acc, iter| {
if acc != 0 {
acc *= iter.iter_orig.count();
}
acc + iter.iter.count()
}),
}
if !self.in_progress() {
return self.0.into_iter().fold(1, |acc, multi_iter| {
acc * multi_iter.iter.count()
});
}
self.0.into_iter().fold(
0,
|acc, MultiProductIter { iter, iter_orig, cur: _ }| {
let total_count = iter_orig.count();
let cur_count = iter.count();
acc * total_count + cur_count
}
)
}
fn size_hint(&self) -> (usize, Option<usize>) {
// Not ExactSizeIterator because size may be larger than usize
if self.0.is_empty() {
return (0, Some(0));
}
if !self.in_progress() {
return self.0.iter().fold((1, Some(1)), |acc, multi_iter| {
size_hint::mul(acc, multi_iter.iter.size_hint())
});
}
self.0.iter().fold(
(0, Some(0)),
|acc, &MultiProductIter { ref iter, ref iter_orig, cur: _ }| {
let cur_size = iter.size_hint();
let total_size = iter_orig.size_hint();
size_hint::add(size_hint::mul(acc, total_size), cur_size)
match &self.0 {
ProductEnded => (0, Some(0)),
ProductInProgress(MultiProductInner {
iters,
cur: NotYetPopulated,
}) => iters
.iter()
.map(|iter| iter.iter_orig.size_hint())
.fold((1, Some(1)), size_hint::mul),
ProductInProgress(MultiProductInner {
iters,
cur: Populated(_),
}) => {
if let [first, tail @ ..] = &iters[..] {
tail.iter().fold(first.iter.size_hint(), |mut sh, iter| {
sh = size_hint::mul(sh, iter.iter_orig.size_hint());
size_hint::add(sh, iter.iter.size_hint())
})
} else {
// Since it is populated, this cartesian product has started so `iters` is not empty.
unreachable!()
}
}
)
}
}
fn last(self) -> Option<Self::Item> {
let iter_count = self.0.len();
let lasts: Self::Item = self.0.into_iter()
.map(|multi_iter| multi_iter.iter.last())
.while_some()
.collect();
if lasts.len() == iter_count {
Some(lasts)
let MultiProductInner { iters, cur } = self.0?;
// Collect the last item of each iterator of the product.
if let Populated(values) = cur {
let mut count = iters.len();
let last = iters
.into_iter()
.zip(values)
.map(|(i, value)| {
i.iter.last().unwrap_or_else(|| {
// The iterator is empty, use its current `value`.
count -= 1;
value
})
})
.collect();
if count == 0 {
// `values` was the last item.
None
} else {
Some(last)
}
} else {
None
iters.into_iter().map(|i| i.iter.last()).collect()
}
}
}
impl<I> std::iter::FusedIterator for MultiProduct<I>
where
I: Iterator + Clone,
I::Item: Clone,
{
}

View File

@@ -1,61 +1,258 @@
use core::array;
use core::borrow::BorrowMut;
use std::fmt;
use std::iter::FusedIterator;
use super::lazy_buffer::LazyBuffer;
use alloc::vec::Vec;
use crate::adaptors::checked_binomial;
/// Iterator for `Vec` valued combinations returned by [`.combinations()`](crate::Itertools::combinations)
pub type Combinations<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.
pub fn combinations<I: Iterator>(iter: I, k: usize) -> Combinations<I>
where
I::Item: Clone,
{
Combinations::new(iter, (0..k).collect())
}
/// Create a new `ArrayCombinations` from a clonable iterator.
pub fn array_combinations<I: Iterator, const K: usize>(iter: I) -> ArrayCombinations<I, K>
where
I::Item: Clone,
{
ArrayCombinations::new(iter, array::from_fn(|i| i))
}
/// An iterator to iterate through all the `k`-length combinations in an iterator.
///
/// See [`.combinations()`](crate::Itertools::combinations) for more information.
/// See [`.combinations()`](crate::Itertools::combinations) and [`.array_combinations()`](crate::Itertools::array_combinations) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Combinations<I: Iterator> {
indices: Vec<usize>,
pub struct CombinationsGeneric<I: Iterator, Idx> {
indices: Idx,
pool: LazyBuffer<I>,
first: bool,
}
impl<I> Clone for Combinations<I>
where I: Clone + Iterator,
I::Item: Clone,
/// A type holding indices of elements in a pool or buffer of items from an inner iterator
/// and used to pick out different combinations in a generic way.
pub trait PoolIndex<T>: BorrowMut<[usize]> {
type Item;
fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> Self::Item
where
T: Clone;
fn len(&self) -> usize {
self.borrow().len()
}
}
impl<T> PoolIndex<T> for Vec<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, const K: usize> PoolIndex<T> for [usize; K] {
type Item = [T; K];
fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> [T; K]
where
T: Clone,
{
pool.get_array(*self)
}
}
impl<I, Idx> Clone for CombinationsGeneric<I, Idx>
where
I: Iterator + Clone,
I::Item: Clone,
Idx: Clone,
{
clone_fields!(indices, pool, first);
}
impl<I> fmt::Debug for Combinations<I>
where I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
impl<I, Idx> fmt::Debug for CombinationsGeneric<I, Idx>
where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
Idx: fmt::Debug,
{
debug_fmt_fields!(Combinations, indices, pool, first);
}
/// Create a new `Combinations` from a clonable iterator.
pub fn combinations<I>(iter: I, k: usize) -> Combinations<I>
where I: Iterator
{
let mut pool = LazyBuffer::new(iter);
pool.prefill(k);
Combinations {
indices: (0..k).collect(),
pool,
first: true,
impl<I: Iterator, Idx: PoolIndex<I::Item>> CombinationsGeneric<I, Idx> {
/// 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> Combinations<I> {
/// Returns the length of a combination produced by this iterator.
#[inline]
pub fn k(&self) -> usize { self.indices.len() }
pub fn k(&self) -> usize {
self.indices.len()
}
/// Returns the (current) length of the pool from which combination elements are
/// selected. This value can change between invocations of [`next`](Combinations::next).
#[inline]
pub fn n(&self) -> usize { self.pool.len() }
pub fn n(&self) -> usize {
self.pool.len()
}
/// Returns a reference to the source iterator.
/// Returns a reference to the source pool.
#[inline]
pub(crate) fn src(&self) -> &I { &self.pool.it }
pub(crate) fn src(&self) -> &LazyBuffer<I> {
&self.pool
}
/// Return the length of the inner iterator and the count of remaining combinations.
pub(crate) fn n_and_count(self) -> (usize, usize) {
let Self {
indices,
pool,
first,
} = self;
let n = pool.count();
(n, remaining_for(n, first, indices.borrow()).unwrap())
}
/// Initialises the iterator by filling a buffer with elements from the
/// iterator. Returns true if there are no combinations, false otherwise.
fn init(&mut self) -> bool {
self.pool.prefill(self.k());
let done = self.k() > self.n();
if !done {
self.first = false;
}
done
}
/// Increments indices representing the combination to advance to the next
/// (in lexicographic order by increasing sequence) combination. For example
/// if we have n=4 & k=2 then `[0, 1] -> [0, 2] -> [0, 3] -> [1, 2] -> ...`
///
/// Returns true if we've run out of combinations, false otherwise.
fn increment_indices(&mut self) -> bool {
// Borrow once instead of noise each time it's indexed
let indices = self.indices.borrow_mut();
if indices.is_empty() {
return true; // Done
}
// Scan from the end, looking for an index to increment
let mut i: usize = indices.len() - 1;
// Check if we need to consume more from the iterator
if indices[i] == self.pool.len() - 1 {
self.pool.get_next(); // may change pool size
}
while indices[i] == i + self.pool.len() - indices.len() {
if i > 0 {
i -= 1;
} else {
// Reached the last combination
return true;
}
}
// Increment index, and reset the ones to its right
indices[i] += 1;
for j in i + 1..indices.len() {
indices[j] = indices[j - 1] + 1;
}
// If we've made it this far, we haven't run out of combos
false
}
/// Returns the n-th item or the number of successful steps.
pub(crate) fn try_nth(&mut self, n: usize) -> Result<<Self as Iterator>::Item, usize>
where
I: Iterator,
I::Item: Clone,
{
let done = if self.first {
self.init()
} else {
self.increment_indices()
};
if done {
return Err(0);
}
for i in 0..n {
if self.increment_indices() {
return Err(i + 1);
}
}
Ok(self.indices.extract_item(&self.pool))
}
}
impl<I, Idx> Iterator for CombinationsGeneric<I, Idx>
where
I: Iterator,
I::Item: Clone,
Idx: PoolIndex<I::Item>,
{
type Item = Idx::Item;
fn next(&mut self) -> Option<Self::Item> {
let done = if self.first {
self.init()
} else {
self.increment_indices()
};
if done {
return None;
}
Some(self.indices.extract_item(&self.pool))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.try_nth(n).ok()
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (mut low, mut upp) = self.pool.size_hint();
low = remaining_for(low, self.first, self.indices.borrow()).unwrap_or(usize::MAX);
upp = upp.and_then(|upp| remaining_for(upp, self.first, self.indices.borrow()));
(low, upp)
}
#[inline]
fn count(self) -> usize {
self.n_and_count().1
}
}
impl<I, Idx> FusedIterator for CombinationsGeneric<I, Idx>
where
I: Iterator,
I::Item: Clone,
Idx: PoolIndex<I::Item>,
{
}
impl<I: Iterator> Combinations<I> {
/// Resets this `Combinations` back to an initial state for combinations of length
/// `k` over the same pool data source. If `k` is larger than the current length
/// of the data pool an attempt is made to prefill the pool so that it holds `k`
@@ -68,7 +265,6 @@ impl<I: Iterator> Combinations<I> {
for i in 0..k {
self.indices[i] = i;
}
} else {
for i in 0..self.indices.len() {
self.indices[i] = i;
@@ -79,50 +275,34 @@ impl<I: Iterator> Combinations<I> {
}
}
impl<I> Iterator for Combinations<I>
where I: Iterator,
I::Item: Clone
{
type Item = Vec<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
if self.first {
if self.k() > self.n() {
return None;
}
self.first = false;
} else if self.indices.is_empty() {
return None;
} else {
// Scan from the end, looking for an index to increment
let mut i: usize = self.indices.len() - 1;
/// For a given size `n`, return the count of remaining combinations or None if it would overflow.
fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
let k = indices.len();
if n < k {
Some(0)
} else if first {
checked_binomial(n, k)
} else {
// https://en.wikipedia.org/wiki/Combinatorial_number_system
// http://www.site.uottawa.ca/~lucia/courses/5165-09/GenCombObj.pdf
// Check if we need to consume more from the iterator
if self.indices[i] == self.pool.len() - 1 {
self.pool.get_next(); // may change pool size
}
// The combinations generated after the current one can be counted by counting as follows:
// - The subsequent combinations that differ in indices[0]:
// If subsequent combinations differ in indices[0], then their value for indices[0]
// must be at least 1 greater than the current indices[0].
// As indices is strictly monotonically sorted, this means we can effectively choose k values
// from (n - 1 - indices[0]), leading to binomial(n - 1 - indices[0], k) possibilities.
// - The subsequent combinations with same indices[0], but differing indices[1]:
// Here we can choose k - 1 values from (n - 1 - indices[1]) values,
// leading to binomial(n - 1 - indices[1], k - 1) possibilities.
// - (...)
// - The subsequent combinations with same indices[0..=i], but differing indices[i]:
// Here we can choose k - i values from (n - 1 - indices[i]) values: binomial(n - 1 - indices[i], k - i).
// Since subsequent combinations can in any index, we must sum up the aforementioned binomial coefficients.
while self.indices[i] == i + self.pool.len() - self.indices.len() {
if i > 0 {
i -= 1;
} else {
// Reached the last combination
return None;
}
}
// Increment index, and reset the ones to its right
self.indices[i] += 1;
for j in i+1..self.indices.len() {
self.indices[j] = self.indices[j - 1] + 1;
}
}
// Create result vector based on the indices
Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect())
// Below, `n0` resembles indices[i].
indices.iter().enumerate().try_fold(0usize, |sum, (i, n0)| {
sum.checked_add(checked_binomial(n - 1 - *n0, k - i)?)
})
}
}
impl<I> FusedIterator for Combinations<I>
where I: Iterator,
I::Item: Clone
{}

View File

@@ -1,20 +1,23 @@
use alloc::boxed::Box;
use alloc::vec::Vec;
use std::fmt;
use std::iter::FusedIterator;
use super::lazy_buffer::LazyBuffer;
use crate::adaptors::checked_binomial;
/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
///
/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
/// for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct CombinationsWithReplacement<I>
where
I: Iterator,
I::Item: Clone,
{
indices: Vec<usize>,
indices: Box<[usize]>,
pool: LazyBuffer<I>,
first: bool,
}
@@ -24,18 +27,7 @@ where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug + Clone,
{
debug_fmt_fields!(Combinations, indices, pool, first);
}
impl<I> CombinationsWithReplacement<I>
where
I: Iterator,
I::Item: Clone,
{
/// Map the current mask over the pool to get an output combination
fn current(&self) -> Vec<I::Item> {
self.indices.iter().map(|i| self.pool[*i].clone()).collect()
}
debug_fmt_fields!(CombinationsWithReplacement, indices, pool, first);
}
/// Create a new `CombinationsWithReplacement` from a clonable iterator.
@@ -44,7 +36,7 @@ where
I: Iterator,
I::Item: Clone,
{
let indices: Vec<usize> = alloc::vec![0; k];
let indices = alloc::vec![0; k].into_boxed_slice();
let pool: LazyBuffer<I> = LazyBuffer::new(iter);
CombinationsWithReplacement {
@@ -54,51 +46,95 @@ where
}
}
impl<I> CombinationsWithReplacement<I>
where
I: Iterator,
I::Item: Clone,
{
/// Increments indices representing the combination to advance to the next
/// (in lexicographic order by increasing sequence) combination.
///
/// Returns true if we've run out of combinations, false otherwise.
fn increment_indices(&mut self) -> bool {
// Check if we need to consume more from the iterator
// This will run while we increment our first index digit
self.pool.get_next();
// Work out where we need to update our indices
let mut increment = None;
for (i, indices_int) in self.indices.iter().enumerate().rev() {
if *indices_int < self.pool.len() - 1 {
increment = Some((i, indices_int + 1));
break;
}
}
match increment {
// If we can update the indices further
Some((increment_from, increment_value)) => {
// We need to update the rightmost non-max value
// and all those to the right
self.indices[increment_from..].fill(increment_value);
false
}
// Otherwise, we're done
None => true,
}
}
}
impl<I> Iterator for CombinationsWithReplacement<I>
where
I: Iterator,
I::Item: Clone,
{
type Item = Vec<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
// If this is the first iteration, return early
if self.first {
// In empty edge cases, stop iterating immediately
return if !(self.indices.is_empty() || self.pool.get_next()) {
None
// Otherwise, yield the initial state
} else {
self.first = false;
Some(self.current())
};
if !(self.indices.is_empty() || self.pool.get_next()) {
return None;
}
self.first = false;
} else if self.increment_indices() {
return None;
}
Some(self.pool.get_at(&self.indices))
}
// Check if we need to consume more from the iterator
// This will run while we increment our first index digit
self.pool.get_next();
// Work out where we need to update our indices
let mut increment: Option<(usize, usize)> = None;
for (i, indices_int) in self.indices.iter().enumerate().rev() {
if *indices_int < self.pool.len()-1 {
increment = Some((i, indices_int + 1));
break;
fn nth(&mut self, n: usize) -> Option<Self::Item> {
if self.first {
// In empty edge cases, stop iterating immediately
if !(self.indices.is_empty() || self.pool.get_next()) {
return None;
}
self.first = false;
} else if self.increment_indices() {
return None;
}
for _ in 0..n {
if self.increment_indices() {
return None;
}
}
Some(self.pool.get_at(&self.indices))
}
match increment {
// If we can update the indices further
Some((increment_from, increment_value)) => {
// We need to update the rightmost non-max value
// and all those to the right
for indices_index in increment_from..self.indices.len() {
self.indices[indices_index] = increment_value;
}
Some(self.current())
}
// Otherwise, we're done
None => None,
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (mut low, mut upp) = self.pool.size_hint();
low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX);
upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices));
(low, upp)
}
fn count(self) -> usize {
let Self {
indices,
pool,
first,
} = self;
let n = pool.count();
remaining_for(n, first, &indices).unwrap()
}
}
@@ -106,4 +142,47 @@ impl<I> FusedIterator for CombinationsWithReplacement<I>
where
I: Iterator,
I::Item: Clone,
{}
{
}
/// For a given size `n`, return the count of remaining combinations with replacement or None if it would overflow.
fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
// With a "stars and bars" representation, choose k values with replacement from n values is
// like choosing k out of k + n 1 positions (hence binomial(k + n - 1, k) possibilities)
// to place k stars and therefore n - 1 bars.
// Example (n=4, k=6): ***|*||** represents [0,0,0,1,3,3].
let count = |n: usize, k: usize| {
let positions = if n == 0 {
k.saturating_sub(1)
} else {
(n - 1).checked_add(k)?
};
checked_binomial(positions, k)
};
let k = indices.len();
if first {
count(n, k)
} else {
// The algorithm is similar to the one for combinations *without replacement*,
// except we choose values *with replacement* and indices are *non-strictly* monotonically sorted.
// The combinations generated after the current one can be counted by counting as follows:
// - The subsequent combinations that differ in indices[0]:
// If subsequent combinations differ in indices[0], then their value for indices[0]
// must be at least 1 greater than the current indices[0].
// As indices is monotonically sorted, this means we can effectively choose k values with
// replacement from (n - 1 - indices[0]), leading to count(n - 1 - indices[0], k) possibilities.
// - The subsequent combinations with same indices[0], but differing indices[1]:
// Here we can choose k - 1 values with replacement from (n - 1 - indices[1]) values,
// leading to count(n - 1 - indices[1], k - 1) possibilities.
// - (...)
// - The subsequent combinations with same indices[0..=i], but differing indices[i]:
// Here we can choose k - i values with replacement from (n - 1 - indices[i]) values: count(n - 1 - indices[i], k - i).
// Since subsequent combinations can in any index, we must sum up the aforementioned binomial coefficients.
// Below, `n0` resembles indices[i].
indices.iter().enumerate().try_fold(0usize, |sum, (i, n0)| {
sum.checked_add(count(n - 1 - *n0, k - i)?)
})
}
}

View File

@@ -1,8 +1,6 @@
use crate::Itertools;
/// Combine all an iterator's elements into one element by using [`Extend`].
///
/// [`IntoIterator`]-enabled version of [`Itertools::concat`].
/// [`IntoIterator`]-enabled version of [`Itertools::concat`](crate::Itertools::concat).
///
/// This combinator will extend the first item with each of the rest of the
/// items of the iterator. If the iterator is empty, the default value of
@@ -15,9 +13,15 @@ use crate::Itertools;
/// assert_eq!(concat(input), vec![1, 2, 3, 4, 5, 6]);
/// ```
pub fn concat<I>(iterable: I) -> I::Item
where I: IntoIterator,
I::Item: Extend<<<I as IntoIterator>::Item as IntoIterator>::Item> + IntoIterator + Default
where
I: IntoIterator,
I::Item: Extend<<<I as IntoIterator>::Item as IntoIterator>::Item> + IntoIterator + Default,
{
#[allow(deprecated)] //TODO: once msrv hits 1.51. replace `fold1` with `reduce`
iterable.into_iter().fold1(|mut a, b| { a.extend(b); a }).unwrap_or_default()
iterable
.into_iter()
.reduce(|mut a, b| {
a.extend(b);
a
})
.unwrap_or_default()
}

View File

@@ -1,3 +1,4 @@
use crate::adaptors::map::{MapSpecialCase, MapSpecialCaseFn};
macro_rules! impl_cons_iter(
($_A:ident, $_B:ident, ) => (); // stop
@@ -5,60 +6,34 @@ macro_rules! impl_cons_iter(
($A:ident, $($B:ident,)*) => (
impl_cons_iter!($($B,)*);
#[allow(non_snake_case)]
impl<X, Iter, $($B),*> Iterator for ConsTuples<Iter, (($($B,)*), X)>
where Iter: Iterator<Item = (($($B,)*), X)>,
{
type Item = ($($B,)* X, );
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
fn fold<Acc, Fold>(self, accum: Acc, mut f: Fold) -> Acc
where Fold: FnMut(Acc, Self::Item) -> Acc,
{
self.iter.fold(accum, move |acc, (($($B,)*), x)| f(acc, ($($B,)* x, )))
impl<$($B),*, X> MapSpecialCaseFn<(($($B,)*), X)> for ConsTuplesFn {
type Out = ($($B,)* X, );
fn call(&mut self, (($($B,)*), X): (($($B,)*), X)) -> Self::Out {
($($B,)* X, )
}
}
#[allow(non_snake_case)]
impl<X, Iter, $($B),*> DoubleEndedIterator for ConsTuples<Iter, (($($B,)*), X)>
where Iter: DoubleEndedIterator<Item = (($($B,)*), X)>,
{
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
}
}
);
);
impl_cons_iter!(A, B, C, D, E, F, G, H, I, J, K, L,);
#[derive(Debug, Clone)]
pub struct ConsTuplesFn;
/// An iterator that maps an iterator of tuples like
/// `((A, B), C)` to an iterator of `(A, B, C)`.
///
/// Used by the `iproduct!()` macro.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug)]
pub struct ConsTuples<I, J>
where I: Iterator<Item=J>,
{
iter: I,
}
impl<I, J> Clone for ConsTuples<I, J>
where I: Clone + Iterator<Item=J>,
{
clone_fields!(iter);
}
pub type ConsTuples<I> = MapSpecialCase<I, ConsTuplesFn>;
/// Create an iterator that maps for example iterators of
/// `((A, B), C)` to `(A, B, C)`.
pub fn cons_tuples<I, J>(iterable: I) -> ConsTuples<I::IntoIter, J>
where I: IntoIterator<Item=J>
pub fn cons_tuples<I>(iterable: I) -> ConsTuples<I::IntoIter>
where
I: IntoIterator,
{
ConsTuples { iter: iterable.into_iter() }
ConsTuples {
iter: iterable.into_iter(),
f: ConsTuplesFn,
}
}

View File

@@ -1,10 +1,12 @@
//! "Diff"ing iterators for caching elements to sequential collections without requiring the new
//! elements' iterator to be `Clone`.
//!
//! - [`Diff`] (produced by the [`diff_with`] function)
//! [`Diff`] (produced by the [`diff_with`] function)
//! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from
//! a lock-step comparison.
use std::fmt;
use crate::free::put_back;
use crate::structs::PutBack;
@@ -13,8 +15,9 @@ use crate::structs::PutBack;
/// `Diff` represents the way in which the elements yielded by the iterator `I` differ to some
/// iterator `J`.
pub enum Diff<I, J>
where I: Iterator,
J: Iterator
where
I: Iterator,
J: Iterator,
{
/// The index of the first non-matching element along with both iterator's remaining elements
/// starting with the first mis-match.
@@ -25,6 +28,43 @@ pub enum Diff<I, J>
Longer(usize, PutBack<J>),
}
impl<I, J> fmt::Debug for Diff<I, J>
where
I: Iterator,
J: Iterator,
PutBack<I>: fmt::Debug,
PutBack<J>: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FirstMismatch(idx, i, j) => f
.debug_tuple("FirstMismatch")
.field(idx)
.field(i)
.field(j)
.finish(),
Self::Shorter(idx, i) => f.debug_tuple("Shorter").field(idx).field(i).finish(),
Self::Longer(idx, j) => f.debug_tuple("Longer").field(idx).field(j).finish(),
}
}
}
impl<I, J> Clone for Diff<I, J>
where
I: Iterator,
J: Iterator,
PutBack<I>: Clone,
PutBack<J>: Clone,
{
fn clone(&self) -> Self {
match self {
Self::FirstMismatch(idx, i, j) => Self::FirstMismatch(*idx, i.clone(), j.clone()),
Self::Shorter(idx, i) => Self::Shorter(*idx, i.clone()),
Self::Longer(idx, j) => Self::Longer(*idx, j.clone()),
}
}
}
/// Compares every element yielded by both `i` and `j` with the given function in lock-step and
/// returns a [`Diff`] which describes how `j` differs from `i`.
///
@@ -37,11 +77,11 @@ pub enum Diff<I, J>
///
/// If `i` becomes exhausted before `j` becomes exhausted, the number of elements in `i` along with
/// the remaining `j` elements will be returned as `Diff::Longer`.
pub fn diff_with<I, J, F>(i: I, j: J, is_equal: F)
-> Option<Diff<I::IntoIter, J::IntoIter>>
where I: IntoIterator,
J: IntoIterator,
F: Fn(&I::Item, &J::Item) -> bool
pub fn diff_with<I, J, F>(i: I, j: J, mut is_equal: F) -> Option<Diff<I::IntoIter, J::IntoIter>>
where
I: IntoIterator,
J: IntoIterator,
F: FnMut(&I::Item, &J::Item) -> bool,
{
let mut i = i.into_iter();
let mut j = j.into_iter();
@@ -49,13 +89,16 @@ pub fn diff_with<I, J, F>(i: I, j: J, is_equal: F)
while let Some(i_elem) = i.next() {
match j.next() {
None => return Some(Diff::Shorter(idx, put_back(i).with_value(i_elem))),
Some(j_elem) => if !is_equal(&i_elem, &j_elem) {
let remaining_i = put_back(i).with_value(i_elem);
let remaining_j = put_back(j).with_value(j_elem);
return Some(Diff::FirstMismatch(idx, remaining_i, remaining_j));
},
Some(j_elem) => {
if !is_equal(&i_elem, &j_elem) {
let remaining_i = put_back(i).with_value(i_elem);
let remaining_j = put_back(j).with_value(j_elem);
return Some(Diff::FirstMismatch(idx, remaining_i, remaining_j));
}
}
}
idx += 1;
}
j.next().map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem)))
j.next()
.map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem)))
}

View File

@@ -2,8 +2,8 @@ use std::hash::Hash;
mod private {
use std::collections::HashMap;
use std::hash::Hash;
use std::fmt;
use std::hash::Hash;
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
@@ -22,7 +22,7 @@ mod private {
impl<I: Iterator, Key: Eq + Hash, F> DuplicatesBy<I, Key, F> {
pub(crate) fn new(iter: I, key_method: F) -> Self {
DuplicatesBy {
Self {
iter,
meta: Meta {
used: HashMap::new(),
@@ -77,7 +77,7 @@ mod private {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
let DuplicatesBy { iter, meta } = self;
let Self { iter, meta } = self;
iter.find_map(|v| meta.filter(v))
}
@@ -109,7 +109,7 @@ mod private {
F: KeyMethod<Key, I::Item>,
{
fn next_back(&mut self) -> Option<Self::Item> {
let DuplicatesBy { iter, meta } = self;
let Self { iter, meta } = self;
iter.rev().find_map(|v| meta.filter(v))
}
}
@@ -122,7 +122,7 @@ mod private {
}
/// Apply the identity function to elements before checking them for equality.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ById;
impl<V> KeyMethod<V, V> for ById {
type Container = JustValue<V>;
@@ -133,6 +133,7 @@ mod private {
}
/// Apply a user-supplied function to elements before checking them for equality.
#[derive(Clone)]
pub struct ByFn<F>(pub(crate) F);
impl<F> fmt::Debug for ByFn<F> {
debug_fmt_fields!(ByFn,);
@@ -213,4 +214,3 @@ where
{
Duplicates::new(iter, private::ById)
}

View File

@@ -1,10 +1,12 @@
use core::ops::{Deref, DerefMut};
use crate::EitherOrBoth::*;
use either::Either;
/// Value that either holds a single A or B, or both.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum EitherOrBoth<A, B> {
pub enum EitherOrBoth<A, B = A> {
/// Both values are present.
Both(A, B),
/// Only the left value of type `A` is present.
@@ -14,7 +16,7 @@ pub enum EitherOrBoth<A, B> {
}
impl<A, B> EitherOrBoth<A, B> {
/// If `Left`, or `Both`, return true, otherwise, return false.
/// If `Left`, or `Both`, return true. Otherwise, return false.
pub fn has_left(&self) -> bool {
self.as_ref().left().is_some()
}
@@ -24,31 +26,24 @@ impl<A, B> EitherOrBoth<A, B> {
self.as_ref().right().is_some()
}
/// If Left, return true otherwise, return false.
/// If `Left`, return true. Otherwise, return false.
/// Exclusive version of [`has_left`](EitherOrBoth::has_left).
pub fn is_left(&self) -> bool {
match *self {
Left(_) => true,
_ => false,
}
matches!(self, Left(_))
}
/// If Right, return true otherwise, return false.
/// If `Right`, return true. Otherwise, return false.
/// Exclusive version of [`has_right`](EitherOrBoth::has_right).
pub fn is_right(&self) -> bool {
match *self {
Right(_) => true,
_ => false,
}
matches!(self, Right(_))
}
/// If Right, return true otherwise, return false.
/// Equivalent to `self.as_ref().both().is_some()`.
/// If `Both`, return true. Otherwise, return false.
pub fn is_both(&self) -> bool {
self.as_ref().both().is_some()
}
/// If `Left`, or `Both`, return `Some` with the left value, otherwise, return `None`.
/// If `Left`, or `Both`, return `Some` with the left value. Otherwise, return `None`.
pub fn left(self) -> Option<A> {
match self {
Left(left) | Both(left, _) => Some(left),
@@ -56,7 +51,7 @@ impl<A, B> EitherOrBoth<A, B> {
}
}
/// If `Right`, or `Both`, return `Some` with the right value, otherwise, return `None`.
/// If `Right`, or `Both`, return `Some` with the right value. Otherwise, return `None`.
pub fn right(self) -> Option<B> {
match self {
Right(right) | Both(_, right) => Some(right),
@@ -64,7 +59,65 @@ impl<A, B> EitherOrBoth<A, B> {
}
}
/// If Both, return `Some` tuple containing left and right.
/// Return tuple of options corresponding to the left and right value respectively
///
/// If `Left` return `(Some(..), None)`, if `Right` return `(None,Some(..))`, else return
/// `(Some(..),Some(..))`
pub fn left_and_right(self) -> (Option<A>, Option<B>) {
self.map_any(Some, Some).or_default()
}
/// If `Left`, return `Some` with the left value. If `Right` or `Both`, return `None`.
///
/// # Examples
///
/// ```
/// // On the `Left` variant.
/// # use itertools::{EitherOrBoth, EitherOrBoth::{Left, Right, Both}};
/// let x: EitherOrBoth<_, ()> = Left("bonjour");
/// assert_eq!(x.just_left(), Some("bonjour"));
///
/// // On the `Right` variant.
/// let x: EitherOrBoth<(), _> = Right("hola");
/// assert_eq!(x.just_left(), None);
///
/// // On the `Both` variant.
/// let x = Both("bonjour", "hola");
/// assert_eq!(x.just_left(), None);
/// ```
pub fn just_left(self) -> Option<A> {
match self {
Left(left) => Some(left),
_ => None,
}
}
/// If `Right`, return `Some` with the right value. If `Left` or `Both`, return `None`.
///
/// # Examples
///
/// ```
/// // On the `Left` variant.
/// # use itertools::{EitherOrBoth::{Left, Right, Both}, EitherOrBoth};
/// let x: EitherOrBoth<_, ()> = Left("auf wiedersehen");
/// assert_eq!(x.just_left(), Some("auf wiedersehen"));
///
/// // On the `Right` variant.
/// let x: EitherOrBoth<(), _> = Right("adios");
/// assert_eq!(x.just_left(), None);
///
/// // On the `Both` variant.
/// let x = Both("auf wiedersehen", "adios");
/// assert_eq!(x.just_left(), None);
/// ```
pub fn just_right(self) -> Option<B> {
match self {
Right(right) => Some(right),
_ => None,
}
}
/// If `Both`, return `Some` containing the left and right values. Otherwise, return `None`.
pub fn both(self) -> Option<(A, B)> {
match self {
Both(a, b) => Some((a, b)),
@@ -72,6 +125,28 @@ impl<A, B> EitherOrBoth<A, B> {
}
}
/// If `Left` or `Both`, return the left value. Otherwise, convert the right value and return it.
pub fn into_left(self) -> A
where
B: Into<A>,
{
match self {
Left(a) | Both(a, _) => a,
Right(b) => b.into(),
}
}
/// If `Right` or `Both`, return the right value. Otherwise, convert the left value and return it.
pub fn into_right(self) -> B
where
A: Into<B>,
{
match self {
Right(b) | Both(_, b) => b,
Left(a) => a.into(),
}
}
/// Converts from `&EitherOrBoth<A, B>` to `EitherOrBoth<&A, &B>`.
pub fn as_ref(&self) -> EitherOrBoth<&A, &B> {
match *self {
@@ -90,6 +165,32 @@ impl<A, B> EitherOrBoth<A, B> {
}
}
/// Converts from `&EitherOrBoth<A, B>` to `EitherOrBoth<&_, &_>` using the [`Deref`] trait.
pub fn as_deref(&self) -> EitherOrBoth<&A::Target, &B::Target>
where
A: Deref,
B: Deref,
{
match *self {
Left(ref left) => Left(left),
Right(ref right) => Right(right),
Both(ref left, ref right) => Both(left, right),
}
}
/// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut _, &mut _>` using the [`DerefMut`] trait.
pub fn as_deref_mut(&mut self) -> EitherOrBoth<&mut A::Target, &mut B::Target>
where
A: DerefMut,
B: DerefMut,
{
match *self {
Left(ref mut left) => Left(left),
Right(ref mut right) => Right(right),
Both(ref mut left, ref mut right) => Both(left, right),
}
}
/// Convert `EitherOrBoth<A, B>` to `EitherOrBoth<B, A>`.
pub fn flip(self) -> EitherOrBoth<B, A> {
match self {
@@ -200,9 +301,9 @@ impl<A, B> EitherOrBoth<A, B> {
B: Default,
{
match self {
EitherOrBoth::Left(l) => (l, B::default()),
EitherOrBoth::Right(r) => (A::default(), r),
EitherOrBoth::Both(l, r) => (l, r),
Left(l) => (l, B::default()),
Right(r) => (A::default(), r),
Both(l, r) => (l, r),
}
}
@@ -227,10 +328,160 @@ impl<A, B> EitherOrBoth<A, B> {
Both(inner_l, inner_r) => (inner_l, inner_r),
}
}
/// Returns a mutable reference to the left value. If the left value is not present,
/// it is replaced with `val`.
pub fn left_or_insert(&mut self, val: A) -> &mut A {
self.left_or_insert_with(|| val)
}
/// Returns a mutable reference to the right value. If the right value is not present,
/// it is replaced with `val`.
pub fn right_or_insert(&mut self, val: B) -> &mut B {
self.right_or_insert_with(|| val)
}
/// If the left value is not present, replace it the value computed by the closure `f`.
/// Returns a mutable reference to the now-present left value.
pub fn left_or_insert_with<F>(&mut self, f: F) -> &mut A
where
F: FnOnce() -> A,
{
match self {
Left(left) | Both(left, _) => left,
Right(_) => self.insert_left(f()),
}
}
/// If the right value is not present, replace it the value computed by the closure `f`.
/// Returns a mutable reference to the now-present right value.
pub fn right_or_insert_with<F>(&mut self, f: F) -> &mut B
where
F: FnOnce() -> B,
{
match self {
Right(right) | Both(_, right) => right,
Left(_) => self.insert_right(f()),
}
}
/// Sets the `left` value of this instance, and returns a mutable reference to it.
/// Does not affect the `right` value.
///
/// # Examples
/// ```
/// # use itertools::{EitherOrBoth, EitherOrBoth::{Left, Right, Both}};
///
/// // Overwriting a pre-existing value.
/// let mut either: EitherOrBoth<_, ()> = Left(0_u32);
/// assert_eq!(*either.insert_left(69), 69);
///
/// // Inserting a second value.
/// let mut either = Right("no");
/// assert_eq!(*either.insert_left("yes"), "yes");
/// assert_eq!(either, Both("yes", "no"));
/// ```
pub fn insert_left(&mut self, val: A) -> &mut A {
match self {
Left(left) | Both(left, _) => {
*left = val;
left
}
Right(right) => {
// This is like a map in place operation. We move out of the reference,
// change the value, and then move back into the reference.
unsafe {
// SAFETY: We know this pointer is valid for reading since we got it from a reference.
let right = std::ptr::read(right as *mut _);
// SAFETY: Again, we know the pointer is valid since we got it from a reference.
std::ptr::write(self as *mut _, Both(val, right));
}
if let Both(left, _) = self {
left
} else {
// SAFETY: The above pattern will always match, since we just
// set `self` equal to `Both`.
unsafe { std::hint::unreachable_unchecked() }
}
}
}
}
/// Sets the `right` value of this instance, and returns a mutable reference to it.
/// Does not affect the `left` value.
///
/// # Examples
/// ```
/// # use itertools::{EitherOrBoth, EitherOrBoth::{Left, Both}};
/// // Overwriting a pre-existing value.
/// let mut either: EitherOrBoth<_, ()> = Left(0_u32);
/// assert_eq!(*either.insert_left(69), 69);
///
/// // Inserting a second value.
/// let mut either = Left("what's");
/// assert_eq!(*either.insert_right(9 + 10), 21 - 2);
/// assert_eq!(either, Both("what's", 9+10));
/// ```
pub fn insert_right(&mut self, val: B) -> &mut B {
match self {
Right(right) | Both(_, right) => {
*right = val;
right
}
Left(left) => {
// This is like a map in place operation. We move out of the reference,
// change the value, and then move back into the reference.
unsafe {
// SAFETY: We know this pointer is valid for reading since we got it from a reference.
let left = std::ptr::read(left as *mut _);
// SAFETY: Again, we know the pointer is valid since we got it from a reference.
std::ptr::write(self as *mut _, Both(left, val));
}
if let Both(_, right) = self {
right
} else {
// SAFETY: The above pattern will always match, since we just
// set `self` equal to `Both`.
unsafe { std::hint::unreachable_unchecked() }
}
}
}
}
/// Set `self` to `Both(..)`, containing the specified left and right values,
/// and returns a mutable reference to those values.
pub fn insert_both(&mut self, left: A, right: B) -> (&mut A, &mut B) {
*self = Both(left, right);
if let Both(left, right) = self {
(left, right)
} else {
// SAFETY: The above pattern will always match, since we just
// set `self` equal to `Both`.
unsafe { std::hint::unreachable_unchecked() }
}
}
}
impl<T> EitherOrBoth<T, T> {
/// Return either value of left, right, or the product of `f` applied where `Both` are present.
/// Return either value of left, right, or apply a function `f` to both values if both are present.
/// The input function has to return the same type as both Right and Left carry.
///
/// This function can be used to preferrably extract the left resp. right value,
/// but fall back to the other (i.e. right resp. left) if the preferred one is not present.
///
/// # Examples
/// ```
/// # use itertools::EitherOrBoth;
/// assert_eq!(EitherOrBoth::Both(3, 7).reduce(u32::max), 7);
/// assert_eq!(EitherOrBoth::Left(3).reduce(u32::max), 3);
/// assert_eq!(EitherOrBoth::Right(7).reduce(u32::max), 7);
///
/// // Extract the left value if present, fall back to the right otherwise.
/// assert_eq!(EitherOrBoth::Left("left").reduce(|l, _r| l), "left");
/// assert_eq!(EitherOrBoth::Right("right").reduce(|l, _r| l), "right");
/// assert_eq!(EitherOrBoth::Both("left", "right").reduce(|l, _r| l), "left");
/// ```
pub fn reduce<F>(self, f: F) -> T
where
F: FnOnce(T, T) -> T,
@@ -243,12 +494,21 @@ impl<T> EitherOrBoth<T, T> {
}
}
impl<A, B> Into<Option<Either<A, B>>> for EitherOrBoth<A, B> {
fn into(self) -> Option<Either<A, B>> {
match self {
EitherOrBoth::Left(l) => Some(Either::Left(l)),
EitherOrBoth::Right(r) => Some(Either::Right(r)),
_ => None,
impl<A, B> From<EitherOrBoth<A, B>> for Option<Either<A, B>> {
fn from(value: EitherOrBoth<A, B>) -> Self {
match value {
Left(l) => Some(Either::Left(l)),
Right(r) => Some(Either::Right(r)),
Both(..) => None,
}
}
}
impl<A, B> From<Either<A, B>> for EitherOrBoth<A, B> {
fn from(either: Either<A, B>) -> Self {
match either {
Either::Left(l) => Left(l),
Either::Right(l) => Right(l),
}
}
}

View File

@@ -8,7 +8,7 @@ use either::Either;
use crate::size_hint;
/// Iterator returned for the error case of `IterTools::exactly_one()`
/// Iterator returned for the error case of `Itertools::exactly_one()`
/// This iterator yields exactly the same elements as the input iterator.
///
/// During the execution of `exactly_one` the iterator must be mutated. This wrapper
@@ -54,26 +54,37 @@ where
Some(Either::Left([first, second])) => {
self.first_two = Some(Either::Right(second));
Some(first)
},
Some(Either::Right(second)) => {
Some(second)
}
None => {
self.inner.next()
}
Some(Either::Right(second)) => Some(second),
None => self.inner.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
size_hint::add_scalar(self.inner.size_hint(), self.additional_len())
}
}
fn fold<B, F>(self, mut init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
match self.first_two {
Some(Either::Left([first, second])) => {
init = f(init, first);
init = f(init, second);
}
Some(Either::Right(second)) => init = f(init, second),
None => {}
}
self.inner.fold(init, f)
}
}
impl<I> ExactSizeIterator for ExactlyOneError<I> where I: ExactSizeIterator {}
impl<I> Display for ExactlyOneError<I>
where I: Iterator,
where
I: Iterator,
{
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let additional = self.additional_len();
@@ -86,25 +97,29 @@ impl<I> Display for ExactlyOneError<I>
}
impl<I> Debug for ExactlyOneError<I>
where I: Iterator + Debug,
I::Item: Debug,
where
I: Iterator + Debug,
I::Item: Debug,
{
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let mut dbg = f.debug_struct("ExactlyOneError");
match &self.first_two {
Some(Either::Left([first, second])) => {
write!(f, "ExactlyOneError[First: {:?}, Second: {:?}, RemainingIter: {:?}]", first, second, self.inner)
},
dbg.field("first", first).field("second", second);
}
Some(Either::Right(second)) => {
write!(f, "ExactlyOneError[Second: {:?}, RemainingIter: {:?}]", second, self.inner)
}
None => {
write!(f, "ExactlyOneError[RemainingIter: {:?}]", self.inner)
dbg.field("second", second);
}
None => {}
}
dbg.field("inner", &self.inner).finish()
}
}
#[cfg(feature = "use_std")]
impl<I> Error for ExactlyOneError<I> where I: Iterator + Debug, I::Item: Debug, {}
impl<I> Error for ExactlyOneError<I>
where
I: Iterator + Debug,
I::Item: Debug,
{
}

View File

@@ -1,3 +1,4 @@
use alloc::{vec, vec::Vec};
use std::cmp::Ordering;
/// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`.

View File

@@ -72,6 +72,29 @@ where
}
}
fn fold<B, F>(self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
// Front
let mut acc = match self.inner_front {
Some(x) => x.fold(init, |a, o| f(a, Ok(o))),
None => init,
};
acc = self.iter.fold(acc, |acc, x| match x {
Ok(it) => it.into_iter().fold(acc, |a, o| f(a, Ok(o))),
Err(e) => f(acc, Err(e)),
});
// Back
match self.inner_back {
Some(x) => x.fold(acc, |a, o| f(a, Ok(o))),
None => acc,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let inner_hint = |inner: &Option<T::IntoIter>| {
inner
@@ -130,6 +153,29 @@ where
}
}
}
fn rfold<B, F>(self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
// Back
let mut acc = match self.inner_back {
Some(x) => x.rfold(init, |a, o| f(a, Ok(o))),
None => init,
};
acc = self.iter.rfold(acc, |acc, x| match x {
Ok(it) => it.into_iter().rfold(acc, |a, o| f(a, Ok(o))),
Err(e) => f(acc, Err(e)),
});
// Front
match self.inner_front {
Some(x) => x.rfold(acc, |a, o| f(a, Ok(o))),
None => acc,
}
}
}
impl<I, T, E> Clone for FlattenOk<I, T, E>
@@ -147,13 +193,7 @@ where
T: IntoIterator,
T::IntoIter: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FlattenOk")
.field("iter", &self.iter)
.field("inner_front", &self.inner_front)
.field("inner_back", &self.inner_back)
.finish()
}
debug_fmt_fields!(FlattenOk, iter, inner_front, inner_back);
}
/// Only the iterator being flattened needs to implement [`FusedIterator`].

View File

@@ -1,5 +1,5 @@
use std::cell::Cell;
use std::fmt;
use std::cell::RefCell;
/// Format all iterator elements lazily, separated by `sep`.
///
@@ -7,11 +7,10 @@ use std::cell::RefCell;
/// exhausted.
///
/// See [`.format_with()`](crate::Itertools::format_with) for more information.
#[derive(Clone)]
pub struct FormatWith<'a, I, F> {
sep: &'a str,
/// FormatWith uses interior mutability because Display::fmt takes &self.
inner: RefCell<Option<(I, F)>>,
/// `FormatWith` uses interior mutability because `Display::fmt` takes `&self`.
inner: Cell<Option<(I, F)>>,
}
/// Format all iterator elements lazily, separated by `sep`.
@@ -21,38 +20,40 @@ pub struct FormatWith<'a, I, F> {
///
/// See [`.format()`](crate::Itertools::format)
/// for more information.
#[derive(Clone)]
pub struct Format<'a, I> {
sep: &'a str,
/// Format uses interior mutability because Display::fmt takes &self.
inner: RefCell<Option<I>>,
/// `Format` uses interior mutability because `Display::fmt` takes `&self`.
inner: Cell<Option<I>>,
}
pub fn new_format<I, F>(iter: I, separator: &str, f: F) -> FormatWith<'_, I, F>
where I: Iterator,
F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result
where
I: Iterator,
F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
{
FormatWith {
sep: separator,
inner: RefCell::new(Some((iter, f))),
inner: Cell::new(Some((iter, f))),
}
}
pub fn new_format_default<I>(iter: I, separator: &str) -> Format<'_, I>
where I: Iterator,
where
I: Iterator,
{
Format {
sep: separator,
inner: RefCell::new(Some(iter)),
inner: Cell::new(Some(iter)),
}
}
impl<'a, I, F> fmt::Display for FormatWith<'a, I, F>
where I: Iterator,
F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result
impl<I, F> fmt::Display for FormatWith<'_, I, F>
where
I: Iterator,
F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (mut iter, mut format) = match self.inner.borrow_mut().take() {
let (mut iter, mut format) = match self.inner.take() {
Some(t) => t,
None => panic!("FormatWith: was already formatted once"),
};
@@ -70,13 +71,26 @@ impl<'a, I, F> fmt::Display for FormatWith<'a, I, F>
}
}
impl<'a, I> Format<'a, I>
where I: Iterator,
impl<I, F> fmt::Debug for FormatWith<'_, I, F>
where
I: Iterator,
F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
{
fn format<F>(&self, f: &mut fmt::Formatter, mut cb: F) -> fmt::Result
where F: FnMut(&I::Item, &mut fmt::Formatter) -> fmt::Result,
{
let mut iter = match self.inner.borrow_mut().take() {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<I> Format<'_, I>
where
I: Iterator,
{
fn format(
&self,
f: &mut fmt::Formatter,
cb: fn(&I::Item, &mut fmt::Formatter) -> fmt::Result,
) -> fmt::Result {
let mut iter = match self.inner.take() {
Some(t) => t,
None => panic!("Format: was already formatted once"),
};
@@ -109,5 +123,56 @@ macro_rules! impl_format {
}
}
impl_format!{Display Debug
UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer}
impl_format! {Display Debug UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer}
impl<I, F> Clone for FormatWith<'_, I, F>
where
(I, F): Clone,
{
fn clone(&self) -> Self {
struct PutBackOnDrop<'r, 'a, I, F> {
into: &'r FormatWith<'a, I, F>,
inner: Option<(I, F)>,
}
// This ensures we preserve the state of the original `FormatWith` if `Clone` panics
impl<I, F> Drop for PutBackOnDrop<'_, '_, I, F> {
fn drop(&mut self) {
self.into.inner.set(self.inner.take())
}
}
let pbod = PutBackOnDrop {
inner: self.inner.take(),
into: self,
};
Self {
inner: Cell::new(pbod.inner.clone()),
sep: self.sep,
}
}
}
impl<I> Clone for Format<'_, I>
where
I: Clone,
{
fn clone(&self) -> Self {
struct PutBackOnDrop<'r, 'a, I> {
into: &'r Format<'a, I>,
inner: Option<I>,
}
// This ensures we preserve the state of the original `FormatWith` if `Clone` panics
impl<I> Drop for PutBackOnDrop<'_, '_, I> {
fn drop(&mut self) {
self.into.inner.set(self.inner.take())
}
}
let pbod = PutBackOnDrop {
inner: self.inner.take(),
into: self,
};
Self {
inner: Cell::new(pbod.inner.clone()),
sep: self.sep,
}
}
}

View File

@@ -10,30 +10,24 @@ use std::iter::{self, Zip};
type VecIntoIter<T> = alloc::vec::IntoIter<T>;
#[cfg(feature = "use_alloc")]
use alloc::{
string::String,
};
use alloc::string::String;
use crate::Itertools;
use crate::intersperse::{Intersperse, IntersperseWith};
use crate::Itertools;
pub use crate::adaptors::{
interleave,
merge,
put_back,
};
pub use crate::adaptors::{interleave, put_back};
#[cfg(feature = "use_alloc")]
pub use crate::put_back_n_impl::put_back_n;
pub use crate::kmerge_impl::kmerge;
pub use crate::merge_join::{merge, merge_join_by};
#[cfg(feature = "use_alloc")]
pub use crate::multipeek_impl::multipeek;
#[cfg(feature = "use_alloc")]
pub use crate::peek_nth::peek_nth;
#[cfg(feature = "use_alloc")]
pub use crate::kmerge_impl::kmerge;
pub use crate::zip_eq_impl::zip_eq;
pub use crate::merge_join::merge_join_by;
pub use crate::put_back_n_impl::put_back_n;
#[cfg(feature = "use_alloc")]
pub use crate::rciter_impl::rciter;
pub use crate::zip_eq_impl::zip_eq;
/// Iterate `iterable` with a particular value inserted between each element.
///
@@ -42,11 +36,12 @@ pub use crate::rciter_impl::rciter;
/// ```
/// use itertools::intersperse;
///
/// itertools::assert_equal(intersperse((0..3), 8), vec![0, 8, 1, 8, 2]);
/// itertools::assert_equal(intersperse(0..3, 8), vec![0, 8, 1, 8, 2]);
/// ```
pub fn intersperse<I>(iterable: I, element: I::Item) -> Intersperse<I::IntoIter>
where I: IntoIterator,
<I as IntoIterator>::Item: Clone
where
I: IntoIterator,
<I as IntoIterator>::Item: Clone,
{
Itertools::intersperse(iterable.into_iter(), element)
}
@@ -60,12 +55,13 @@ pub fn intersperse<I>(iterable: I, element: I::Item) -> Intersperse<I::IntoIter>
/// use itertools::intersperse_with;
///
/// let mut i = 10;
/// itertools::assert_equal(intersperse_with((0..3), || { i -= 1; i }), vec![0, 9, 1, 8, 2]);
/// itertools::assert_equal(intersperse_with(0..3, || { i -= 1; i }), vec![0, 9, 1, 8, 2]);
/// assert_eq!(i, 8);
/// ```
pub fn intersperse_with<I, F>(iterable: I, element: F) -> IntersperseWith<I::IntoIter, F>
where I: IntoIterator,
F: FnMut() -> I::Item
where
I: IntoIterator,
F: FnMut() -> I::Item,
{
Itertools::intersperse_with(iterable.into_iter(), element)
}
@@ -79,10 +75,12 @@ pub fn intersperse_with<I, F>(iterable: I, element: F) -> IntersperseWith<I::Int
///
/// for (i, elt) in enumerate(&[1, 2, 3]) {
/// /* loop body */
/// # let _ = (i, elt);
/// }
/// ```
pub fn enumerate<I>(iterable: I) -> iter::Enumerate<I::IntoIter>
where I: IntoIterator
where
I: IntoIterator,
{
iterable.into_iter().enumerate()
}
@@ -96,11 +94,13 @@ pub fn enumerate<I>(iterable: I) -> iter::Enumerate<I::IntoIter>
///
/// for elt in rev(&[1, 2, 3]) {
/// /* loop body */
/// # let _ = elt;
/// }
/// ```
pub fn rev<I>(iterable: I) -> iter::Rev<I::IntoIter>
where I: IntoIterator,
I::IntoIter: DoubleEndedIterator
where
I: IntoIterator,
I::IntoIter: DoubleEndedIterator,
{
iterable.into_iter().rev()
}
@@ -121,15 +121,18 @@ pub fn rev<I>(iterable: I) -> iter::Rev<I::IntoIter>
/// }
/// assert_eq!(result, vec![(1, 'a'),(2, 'b'),(3, 'c')]);
/// ```
#[deprecated(note="Use [std::iter::zip](https://doc.rust-lang.org/std/iter/fn.zip.html) instead", since="0.10.4")]
#[deprecated(
note = "Use [std::iter::zip](https://doc.rust-lang.org/std/iter/fn.zip.html) instead",
since = "0.10.4"
)]
pub fn zip<I, J>(i: I, j: J) -> Zip<I::IntoIter, J::IntoIter>
where I: IntoIterator,
J: IntoIterator
where
I: IntoIterator,
J: IntoIterator,
{
i.into_iter().zip(j)
}
/// Takes two iterables and creates a new iterator over both in sequence.
///
/// [`IntoIterator`] enabled version of [`Iterator::chain`].
@@ -145,14 +148,18 @@ pub fn zip<I, J>(i: I, j: J) -> Zip<I::IntoIter, J::IntoIter>
/// }
/// assert_eq!(result, vec![1, 2, 3, 4]);
/// ```
pub fn chain<I, J>(i: I, j: J) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
where I: IntoIterator,
J: IntoIterator<Item = I::Item>
pub fn chain<I, J>(
i: I,
j: J,
) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
where
I: IntoIterator,
J: IntoIterator<Item = I::Item>,
{
i.into_iter().chain(j)
}
/// Create an iterator that clones each element from &T to T
/// Create an iterator that clones each element from `&T` to `T`.
///
/// [`IntoIterator`] enabled version of [`Iterator::cloned`].
///
@@ -161,9 +168,10 @@ pub fn chain<I, J>(i: I, j: J) -> iter::Chain<<I as IntoIterator>::IntoIter, <J
///
/// assert_eq!(cloned(b"abc").next(), Some(b'a'));
/// ```
pub fn cloned<'a, I, T: 'a>(iterable: I) -> iter::Cloned<I::IntoIter>
where I: IntoIterator<Item=&'a T>,
T: Clone,
pub fn cloned<'a, I, T>(iterable: I) -> iter::Cloned<I::IntoIter>
where
I: IntoIterator<Item = &'a T>,
T: Clone + 'a,
{
iterable.into_iter().cloned()
}
@@ -178,8 +186,9 @@ pub fn cloned<'a, I, T: 'a>(iterable: I) -> iter::Cloned<I::IntoIter>
/// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.);
/// ```
pub fn fold<I, B, F>(iterable: I, init: B, f: F) -> B
where I: IntoIterator,
F: FnMut(B, I::Item) -> B
where
I: IntoIterator,
F: FnMut(B, I::Item) -> B,
{
iterable.into_iter().fold(init, f)
}
@@ -194,8 +203,9 @@ pub fn fold<I, B, F>(iterable: I, init: B, f: F) -> B
/// assert!(all(&[1, 2, 3], |elt| *elt > 0));
/// ```
pub fn all<I, F>(iterable: I, f: F) -> bool
where I: IntoIterator,
F: FnMut(I::Item) -> bool
where
I: IntoIterator,
F: FnMut(I::Item) -> bool,
{
iterable.into_iter().all(f)
}
@@ -210,8 +220,9 @@ pub fn all<I, F>(iterable: I, f: F) -> bool
/// assert!(any(&[0, -1, 2], |elt| *elt > 0));
/// ```
pub fn any<I, F>(iterable: I, f: F) -> bool
where I: IntoIterator,
F: FnMut(I::Item) -> bool
where
I: IntoIterator,
F: FnMut(I::Item) -> bool,
{
iterable.into_iter().any(f)
}
@@ -226,8 +237,9 @@ pub fn any<I, F>(iterable: I, f: F) -> bool
/// assert_eq!(max(0..10), Some(9));
/// ```
pub fn max<I>(iterable: I) -> Option<I::Item>
where I: IntoIterator,
I::Item: Ord
where
I: IntoIterator,
I::Item: Ord,
{
iterable.into_iter().max()
}
@@ -242,14 +254,14 @@ pub fn max<I>(iterable: I) -> Option<I::Item>
/// assert_eq!(min(0..10), Some(0));
/// ```
pub fn min<I>(iterable: I) -> Option<I::Item>
where I: IntoIterator,
I::Item: Ord
where
I: IntoIterator,
I::Item: Ord,
{
iterable.into_iter().min()
}
/// Combine all iterator elements into one String, separated by `sep`.
/// Combine all iterator elements into one `String`, separated by `sep`.
///
/// [`IntoIterator`] enabled version of [`Itertools::join`].
///
@@ -260,8 +272,9 @@ pub fn min<I>(iterable: I) -> Option<I::Item>
/// ```
#[cfg(feature = "use_alloc")]
pub fn join<I>(iterable: I, sep: &str) -> String
where I: IntoIterator,
I::Item: Display
where
I: IntoIterator,
I::Item: Display,
{
iterable.into_iter().join(sep)
}
@@ -278,9 +291,29 @@ pub fn join<I>(iterable: I, sep: &str) -> String
/// ```
#[cfg(feature = "use_alloc")]
pub fn sorted<I>(iterable: I) -> VecIntoIter<I::Item>
where I: IntoIterator,
I::Item: Ord
where
I: IntoIterator,
I::Item: Ord,
{
iterable.into_iter().sorted()
}
/// Sort all iterator elements into a new iterator in ascending order.
/// This sort is unstable (i.e., may reorder equal elements).
///
/// [`IntoIterator`] enabled version of [`Itertools::sorted_unstable`].
///
/// ```
/// use itertools::sorted_unstable;
/// use itertools::assert_equal;
///
/// assert_equal(sorted_unstable("rust".chars()), "rstu".chars());
/// ```
#[cfg(feature = "use_alloc")]
pub fn sorted_unstable<I>(iterable: I) -> VecIntoIter<I::Item>
where
I: IntoIterator,
I::Item: Ord,
{
iterable.into_iter().sorted_unstable()
}

View File

@@ -9,8 +9,9 @@ use std::iter::Iterator;
/// See [`.into_group_map()`](crate::Itertools::into_group_map)
/// for more information.
pub fn into_group_map<I, K, V>(iter: I) -> HashMap<K, Vec<V>>
where I: Iterator<Item=(K, V)>,
K: Hash + Eq,
where
I: Iterator<Item = (K, V)>,
K: Hash + Eq,
{
let mut lookup = HashMap::new();
@@ -21,12 +22,11 @@ pub fn into_group_map<I, K, V>(iter: I) -> HashMap<K, Vec<V>>
lookup
}
pub fn into_group_map_by<I, K, V>(iter: I, f: impl Fn(&V) -> K) -> HashMap<K, Vec<V>>
where
I: Iterator<Item=V>,
K: Hash + Eq,
pub fn into_group_map_by<I, K, V, F>(iter: I, mut f: F) -> HashMap<K, Vec<V>>
where
I: Iterator<Item = V>,
K: Hash + Eq,
F: FnMut(&V) -> K,
{
into_group_map(
iter.map(|v| (f(&v), v))
)
into_group_map(iter.map(|v| (f(&v), v)))
}

View File

@@ -1,14 +1,15 @@
use std::cell::{Cell, RefCell};
use alloc::vec::{self, Vec};
use std::cell::{Cell, RefCell};
/// A trait to unify `FnMut` for `GroupBy` with the chunk key in `IntoChunks`
/// A trait to unify `FnMut` for `ChunkBy` with the chunk key in `IntoChunks`
trait KeyFunction<A> {
type Key;
fn call_mut(&mut self, arg: A) -> Self::Key;
}
impl<A, K, F: ?Sized> KeyFunction<A> for F
where F: FnMut(A) -> K
impl<A, K, F> KeyFunction<A> for F
where
F: FnMut(A) -> K + ?Sized,
{
type Key = K;
#[inline]
@@ -17,9 +18,8 @@ impl<A, K, F: ?Sized> KeyFunction<A> for F
}
}
/// `ChunkIndex` acts like the grouping key function for `IntoChunks`
#[derive(Debug)]
#[derive(Debug, Clone)]
struct ChunkIndex {
size: usize,
index: usize,
@@ -29,7 +29,7 @@ struct ChunkIndex {
impl ChunkIndex {
#[inline(always)]
fn new(size: usize) -> Self {
ChunkIndex {
Self {
size,
index: 0,
key: 0,
@@ -50,9 +50,10 @@ impl<A> KeyFunction<A> for ChunkIndex {
}
}
#[derive(Clone)]
struct GroupInner<K, I, F>
where I: Iterator
where
I: Iterator,
{
key: F,
iter: I,
@@ -65,19 +66,21 @@ struct GroupInner<K, I, F>
/// Least index for which we still have elements buffered
oldest_buffered_group: usize,
/// Group index for `buffer[0]` -- the slots
/// bottom_group..oldest_buffered_group are unused and will be erased when
/// `bottom_group..oldest_buffered_group` are unused and will be erased when
/// that range is large enough.
bottom_group: usize,
/// Buffered groups, from `bottom_group` (index 0) to `top_group`.
buffer: Vec<vec::IntoIter<I::Item>>,
/// index of last group iter that was dropped, usize::MAX == none
/// index of last group iter that was dropped,
/// `usize::MAX` initially when no group was dropped
dropped_group: usize,
}
impl<K, I, F> GroupInner<K, I, F>
where I: Iterator,
F: for<'a> KeyFunction<&'a I::Item, Key=K>,
K: PartialEq,
where
I: Iterator,
F: for<'a> KeyFunction<&'a I::Item, Key = K>,
K: PartialEq,
{
/// `client`: Index of group that requests next element
#[inline(always)]
@@ -90,9 +93,8 @@ impl<K, I, F> GroupInner<K, I, F>
*/
if client < self.oldest_buffered_group {
None
} else if client < self.top_group ||
(client == self.top_group &&
self.buffer.len() > self.top_group - self.bottom_group)
} else if client < self.top_group
|| (client == self.top_group && self.buffer.len() > self.top_group - self.bottom_group)
{
self.lookup_buffer(client)
} else if self.done {
@@ -118,8 +120,10 @@ impl<K, I, F> GroupInner<K, I, F>
// `bottom_group..oldest_buffered_group` is unused, and if it's large enough, erase it.
self.oldest_buffered_group += 1;
// skip forward further empty queues too
while self.buffer.get(self.oldest_buffered_group - self.bottom_group)
.map_or(false, |buf| buf.len() == 0)
while self
.buffer
.get(self.oldest_buffered_group - self.bottom_group)
.map_or(false, |buf| buf.len() == 0)
{
self.oldest_buffered_group += 1;
}
@@ -144,12 +148,14 @@ impl<K, I, F> GroupInner<K, I, F>
fn next_element(&mut self) -> Option<I::Item> {
debug_assert!(!self.done);
match self.iter.next() {
None => { self.done = true; None }
None => {
self.done = true;
None
}
otherwise => otherwise,
}
}
#[inline(never)]
fn step_buffering(&mut self, client: usize) -> Option<I::Item> {
// requested a later group -- walk through the current group up to
@@ -171,11 +177,13 @@ impl<K, I, F> GroupInner<K, I, F>
let key = self.key.call_mut(&elt);
match self.current_key.take() {
None => {}
Some(old_key) => if old_key != key {
self.current_key = Some(key);
first_elt = Some(elt);
break;
},
Some(old_key) => {
if old_key != key {
self.current_key = Some(key);
first_elt = Some(elt);
break;
}
}
}
self.current_key = Some(key);
if self.top_group != self.dropped_group {
@@ -220,12 +228,14 @@ impl<K, I, F> GroupInner<K, I, F>
let key = self.key.call_mut(&elt);
match self.current_key.take() {
None => {}
Some(old_key) => if old_key != key {
self.current_key = Some(key);
self.current_elt = Some(elt);
self.top_group += 1;
return None;
},
Some(old_key) => {
if old_key != key {
self.current_key = Some(key);
self.current_elt = Some(elt);
self.top_group += 1;
return None;
}
}
}
self.current_key = Some(key);
Some(elt)
@@ -261,7 +271,8 @@ impl<K, I, F> GroupInner<K, I, F>
}
impl<K, I, F> GroupInner<K, I, F>
where I: Iterator,
where
I: Iterator,
{
/// Called when a group is dropped
fn drop_group(&mut self, client: usize) {
@@ -272,10 +283,14 @@ impl<K, I, F> GroupInner<K, I, F>
}
}
/// `GroupBy` is the storage for the lazy grouping operation.
#[deprecated(note = "Use `ChunkBy` instead", since = "0.13.0")]
/// See [`ChunkBy`](crate::structs::ChunkBy).
pub type GroupBy<K, I, F> = ChunkBy<K, I, F>;
/// `ChunkBy` is the storage for the lazy grouping operation.
///
/// If the groups are consumed in their original order, or if each
/// group is dropped without keeping it around, then `GroupBy` uses
/// group is dropped without keeping it around, then `ChunkBy` uses
/// no allocations. It needs allocations only if several group iterators
/// are alive at the same time.
///
@@ -284,10 +299,11 @@ impl<K, I, F> GroupInner<K, I, F>
/// value. It should be stored in a local variable or temporary and
/// iterated.
///
/// See [`.group_by()`](crate::Itertools::group_by) for more information.
/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct GroupBy<K, I, F>
where I: Iterator,
pub struct ChunkBy<K, I, F>
where
I: Iterator,
{
inner: RefCell<GroupInner<K, I, F>>,
// the group iterator's current index. Keep this in the main value
@@ -296,11 +312,12 @@ pub struct GroupBy<K, I, F>
}
/// Create a new
pub fn new<K, J, F>(iter: J, f: F) -> GroupBy<K, J::IntoIter, F>
where J: IntoIterator,
F: FnMut(&J::Item) -> K,
pub fn new<K, J, F>(iter: J, f: F) -> ChunkBy<K, J::IntoIter, F>
where
J: IntoIterator,
F: FnMut(&J::Item) -> K,
{
GroupBy {
ChunkBy {
inner: RefCell::new(GroupInner {
key: f,
iter: iter.into_iter(),
@@ -317,13 +334,15 @@ pub fn new<K, J, F>(iter: J, f: F) -> GroupBy<K, J::IntoIter, F>
}
}
impl<K, I, F> GroupBy<K, I, F>
where I: Iterator,
impl<K, I, F> ChunkBy<K, I, F>
where
I: Iterator,
{
/// `client`: Index of group that requests next element
fn step(&self, client: usize) -> Option<I::Item>
where F: FnMut(&I::Item) -> K,
K: PartialEq,
where
F: FnMut(&I::Item) -> K,
K: PartialEq,
{
self.inner.borrow_mut().step(client)
}
@@ -334,11 +353,12 @@ impl<K, I, F> GroupBy<K, I, F>
}
}
impl<'a, K, I, F> IntoIterator for &'a GroupBy<K, I, F>
where I: Iterator,
I::Item: 'a,
F: FnMut(&I::Item) -> K,
K: PartialEq
impl<'a, K, I, F> IntoIterator for &'a ChunkBy<K, I, F>
where
I: Iterator,
I::Item: 'a,
F: FnMut(&I::Item) -> K,
K: PartialEq,
{
type Item = (K, Group<'a, K, I, F>);
type IntoIter = Groups<'a, K, I, F>;
@@ -348,26 +368,29 @@ impl<'a, K, I, F> IntoIterator for &'a GroupBy<K, I, F>
}
}
/// An iterator that yields the Group iterators.
///
/// Iterator element type is `(K, Group)`:
/// the group's key `K` and the group's iterator.
///
/// See [`.group_by()`](crate::Itertools::group_by) for more information.
/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Groups<'a, K: 'a, I: 'a, F: 'a>
where I: Iterator,
I::Item: 'a
pub struct Groups<'a, K, I, F>
where
I: Iterator + 'a,
I::Item: 'a,
K: 'a,
F: 'a,
{
parent: &'a GroupBy<K, I, F>,
parent: &'a ChunkBy<K, I, F>,
}
impl<'a, K, I, F> Iterator for Groups<'a, K, I, F>
where I: Iterator,
I::Item: 'a,
F: FnMut(&I::Item) -> K,
K: PartialEq
where
I: Iterator,
I::Item: 'a,
F: FnMut(&I::Item) -> K,
K: PartialEq,
{
type Item = (K, Group<'a, K, I, F>);
@@ -378,11 +401,14 @@ impl<'a, K, I, F> Iterator for Groups<'a, K, I, F>
let inner = &mut *self.parent.inner.borrow_mut();
inner.step(index).map(|elt| {
let key = inner.group_key(index);
(key, Group {
parent: self.parent,
index,
first: Some(elt),
})
(
key,
Group {
parent: self.parent,
index,
first: Some(elt),
},
)
})
}
}
@@ -390,18 +416,22 @@ impl<'a, K, I, F> Iterator for Groups<'a, K, I, F>
/// An iterator for the elements in a single group.
///
/// Iterator element type is `I::Item`.
pub struct Group<'a, K: 'a, I: 'a, F: 'a>
where I: Iterator,
I::Item: 'a,
pub struct Group<'a, K, I, F>
where
I: Iterator + 'a,
I::Item: 'a,
K: 'a,
F: 'a,
{
parent: &'a GroupBy<K, I, F>,
parent: &'a ChunkBy<K, I, F>,
index: usize,
first: Option<I::Item>,
}
impl<'a, K, I, F> Drop for Group<'a, K, I, F>
where I: Iterator,
I::Item: 'a,
where
I: Iterator,
I::Item: 'a,
{
fn drop(&mut self) {
self.parent.drop_group(self.index);
@@ -409,10 +439,11 @@ impl<'a, K, I, F> Drop for Group<'a, K, I, F>
}
impl<'a, K, I, F> Iterator for Group<'a, K, I, F>
where I: Iterator,
I::Item: 'a,
F: FnMut(&I::Item) -> K,
K: PartialEq,
where
I: Iterator,
I::Item: 'a,
F: FnMut(&I::Item) -> K,
K: PartialEq,
{
type Item = I::Item;
#[inline]
@@ -428,7 +459,8 @@ impl<'a, K, I, F> Iterator for Group<'a, K, I, F>
/// Create a new
pub fn new_chunks<J>(iter: J, size: usize) -> IntoChunks<J::IntoIter>
where J: IntoIterator,
where
J: IntoIterator,
{
IntoChunks {
inner: RefCell::new(GroupInner {
@@ -447,10 +479,9 @@ pub fn new_chunks<J>(iter: J, size: usize) -> IntoChunks<J::IntoIter>
}
}
/// `ChunkLazy` is the storage for a lazy chunking operation.
///
/// `IntoChunks` behaves just like `GroupBy`: it is iterable, and
/// `IntoChunks` behaves just like `ChunkBy`: it is iterable, and
/// it only buffers if several chunk iterators are alive at the same time.
///
/// This type implements [`IntoIterator`] (it is **not** an iterator
@@ -463,7 +494,8 @@ pub fn new_chunks<J>(iter: J, size: usize) -> IntoChunks<J::IntoIter>
/// See [`.chunks()`](crate::Itertools::chunks) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct IntoChunks<I>
where I: Iterator,
where
I: Iterator,
{
inner: RefCell<GroupInner<usize, I, ChunkIndex>>,
// the chunk iterator's current index. Keep this in the main value
@@ -471,9 +503,17 @@ pub struct IntoChunks<I>
index: Cell<usize>,
}
impl<I> Clone for IntoChunks<I>
where
I: Clone + Iterator,
I::Item: Clone,
{
clone_fields!(inner, index);
}
impl<I> IntoChunks<I>
where I: Iterator,
where
I: Iterator,
{
/// `client`: Index of chunk that requests next element
fn step(&self, client: usize) -> Option<I::Item> {
@@ -487,36 +527,37 @@ impl<I> IntoChunks<I>
}
impl<'a, I> IntoIterator for &'a IntoChunks<I>
where I: Iterator,
I::Item: 'a,
where
I: Iterator,
I::Item: 'a,
{
type Item = Chunk<'a, I>;
type IntoIter = Chunks<'a, I>;
fn into_iter(self) -> Self::IntoIter {
Chunks {
parent: self,
}
Chunks { parent: self }
}
}
/// An iterator that yields the Chunk iterators.
///
/// Iterator element type is `Chunk`.
///
/// See [`.chunks()`](crate::Itertools::chunks) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Chunks<'a, I: 'a>
where I: Iterator,
I::Item: 'a,
#[derive(Clone)]
pub struct Chunks<'a, I>
where
I: Iterator + 'a,
I::Item: 'a,
{
parent: &'a IntoChunks<I>,
}
impl<'a, I> Iterator for Chunks<'a, I>
where I: Iterator,
I::Item: 'a,
where
I: Iterator,
I::Item: 'a,
{
type Item = Chunk<'a, I>;
@@ -525,12 +566,10 @@ impl<'a, I> Iterator for Chunks<'a, I>
let index = self.parent.index.get();
self.parent.index.set(index + 1);
let inner = &mut *self.parent.inner.borrow_mut();
inner.step(index).map(|elt| {
Chunk {
parent: self.parent,
index,
first: Some(elt),
}
inner.step(index).map(|elt| Chunk {
parent: self.parent,
index,
first: Some(elt),
})
}
}
@@ -538,9 +577,10 @@ impl<'a, I> Iterator for Chunks<'a, I>
/// An iterator for the elements in a single chunk.
///
/// Iterator element type is `I::Item`.
pub struct Chunk<'a, I: 'a>
where I: Iterator,
I::Item: 'a,
pub struct Chunk<'a, I>
where
I: Iterator + 'a,
I::Item: 'a,
{
parent: &'a IntoChunks<I>,
index: usize,
@@ -548,8 +588,9 @@ pub struct Chunk<'a, I: 'a>
}
impl<'a, I> Drop for Chunk<'a, I>
where I: Iterator,
I::Item: 'a,
where
I: Iterator,
I::Item: 'a,
{
fn drop(&mut self) {
self.parent.drop_group(self.index);
@@ -557,8 +598,9 @@ impl<'a, I> Drop for Chunk<'a, I>
}
impl<'a, I> Iterator for Chunk<'a, I>
where I: Iterator,
I::Item: 'a,
where
I: Iterator,
I::Item: 'a,
{
type Item = I::Item;
#[inline]

View File

@@ -1,37 +1,45 @@
#![cfg(feature = "use_std")]
use crate::MinMaxResult;
use std::collections::HashMap;
use crate::{
adaptors::map::{MapSpecialCase, MapSpecialCaseFn},
MinMaxResult,
};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;
use std::ops::{Add, Mul};
/// A wrapper to allow for an easy [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by)
#[derive(Clone, Debug)]
pub struct MapForGrouping<I, F>(I, F);
pub type MapForGrouping<I, F> = MapSpecialCase<I, GroupingMapFn<F>>;
impl<I, F> MapForGrouping<I, F> {
pub(crate) fn new(iter: I, key_mapper: F) -> Self {
Self(iter, key_mapper)
#[derive(Clone)]
pub struct GroupingMapFn<F>(F);
impl<F> std::fmt::Debug for GroupingMapFn<F> {
debug_fmt_fields!(GroupingMapFn,);
}
impl<V, K, F: FnMut(&V) -> K> MapSpecialCaseFn<V> for GroupingMapFn<F> {
type Out = (K, V);
fn call(&mut self, v: V) -> Self::Out {
((self.0)(&v), v)
}
}
impl<K, V, I, F> Iterator for MapForGrouping<I, F>
where I: Iterator<Item = V>,
K: Hash + Eq,
F: FnMut(&V) -> K,
{
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|val| ((self.1)(&val), val))
pub(crate) fn new_map_for_grouping<K, I: Iterator, F: FnMut(&I::Item) -> K>(
iter: I,
key_mapper: F,
) -> MapForGrouping<I, F> {
MapSpecialCase {
iter,
f: GroupingMapFn(key_mapper),
}
}
/// Creates a new `GroupingMap` from `iter`
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
where I: Iterator<Item = (K, V)>,
K: Hash + Eq,
where
I: Iterator<Item = (K, V)>,
K: Hash + Eq,
{
GroupingMap { iter }
}
@@ -53,8 +61,9 @@ pub struct GroupingMap<I> {
}
impl<I, K, V> GroupingMap<I>
where I: Iterator<Item = (K, V)>,
K: Hash + Eq,
where
I: Iterator<Item = (K, V)>,
K: Hash + Eq,
{
/// This is the generic way to perform any operation on a `GroupingMap`.
/// It's suggested to use this method only to implement custom operations
@@ -97,7 +106,8 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3); // The final keys are only 0, 1 and 2
/// ```
pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
where FO: FnMut(Option<R>, &K, V) -> Option<R>,
where
FO: FnMut(Option<R>, &K, V) -> Option<R>,
{
let mut destination_map = HashMap::new();
@@ -111,6 +121,50 @@ impl<I, K, V> GroupingMap<I>
destination_map
}
/// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
/// of each group sequentially, passing the previously accumulated value, a reference to the key
/// and the current element as arguments, and stores the results in a new map.
///
/// `init` is called to obtain the initial value of each accumulator.
///
/// `operation` is a function that is invoked on each element with the following parameters:
/// - the current value of the accumulator of the group;
/// - a reference to the key of the group this element belongs to;
/// - the element from the source being accumulated.
///
/// Return a `HashMap` associating the key of each group with the result of folding that group's elements.
///
/// ```
/// use itertools::Itertools;
///
/// #[derive(Debug, Default)]
/// struct Accumulator {
/// acc: usize,
/// }
///
/// let lookup = (1..=7)
/// .into_grouping_map_by(|&n| n % 3)
/// .fold_with(|_key, _val| Default::default(), |Accumulator { acc }, _key, val| {
/// let acc = acc + val;
/// Accumulator { acc }
/// });
///
/// assert_eq!(lookup[&0].acc, 3 + 6);
/// assert_eq!(lookup[&1].acc, 1 + 4 + 7);
/// assert_eq!(lookup[&2].acc, 2 + 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn fold_with<FI, FO, R>(self, mut init: FI, mut operation: FO) -> HashMap<K, R>
where
FI: FnMut(&K, &V) -> R,
FO: FnMut(R, &K, V) -> R,
{
self.aggregate(|acc, key, val| {
let acc = acc.unwrap_or_else(|| init(key, &val));
Some(operation(acc, key, val))
})
}
/// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
/// of each group sequentially, passing the previously accumulated value, a reference to the key
/// and the current element as arguments, and stores the results in a new map.
@@ -136,14 +190,12 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup[&2], 2 + 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
where R: Clone,
FO: FnMut(R, &K, V) -> R,
pub fn fold<FO, R>(self, init: R, operation: FO) -> HashMap<K, R>
where
R: Clone,
FO: FnMut(R, &K, V) -> R,
{
self.aggregate(|acc, key, val| {
let acc = acc.unwrap_or_else(|| init.clone());
Some(operation(acc, key, val))
})
self.fold_with(|_, _| init.clone(), operation)
}
/// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
@@ -166,15 +218,16 @@ impl<I, K, V> GroupingMap<I>
///
/// let lookup = (1..=7)
/// .into_grouping_map_by(|&n| n % 3)
/// .fold_first(|acc, _key, val| acc + val);
/// .reduce(|acc, _key, val| acc + val);
///
/// assert_eq!(lookup[&0], 3 + 6);
/// assert_eq!(lookup[&1], 1 + 4 + 7);
/// assert_eq!(lookup[&2], 2 + 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
where FO: FnMut(V, &K, V) -> V,
pub fn reduce<FO>(self, mut operation: FO) -> HashMap<K, V>
where
FO: FnMut(V, &K, V) -> V,
{
self.aggregate(|acc, key, val| {
Some(match acc {
@@ -184,6 +237,15 @@ impl<I, K, V> GroupingMap<I>
})
}
/// See [`.reduce()`](GroupingMap::reduce).
#[deprecated(note = "Use .reduce() instead", since = "0.13.0")]
pub fn fold_first<FO>(self, operation: FO) -> HashMap<K, V>
where
FO: FnMut(V, &K, V) -> V,
{
self.reduce(operation)
}
/// Groups elements from the `GroupingMap` source by key and collects the elements of each group in
/// an instance of `C`. The iteration order is preserved when inserting elements.
///
@@ -203,12 +265,16 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn collect<C>(self) -> HashMap<K, C>
where C: Default + Extend<V>,
where
C: Default + Extend<V>,
{
let mut destination_map = HashMap::new();
self.iter.for_each(|(key, val)| {
destination_map.entry(key).or_insert_with(C::default).extend(Some(val));
destination_map
.entry(key)
.or_insert_with(C::default)
.extend(Some(val));
});
destination_map
@@ -233,7 +299,8 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn max(self) -> HashMap<K, V>
where V: Ord,
where
V: Ord,
{
self.max_by(|_, v1, v2| V::cmp(v1, v2))
}
@@ -258,11 +325,12 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V>
where F: FnMut(&K, &V, &V) -> Ordering,
where
F: FnMut(&K, &V, &V) -> Ordering,
{
self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
self.reduce(|acc, key, val| match compare(key, &acc, &val) {
Ordering::Less | Ordering::Equal => val,
Ordering::Greater => acc
Ordering::Greater => acc,
})
}
@@ -286,8 +354,9 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
where F: FnMut(&K, &V) -> CK,
CK: Ord,
where
F: FnMut(&K, &V) -> CK,
CK: Ord,
{
self.max_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2)))
}
@@ -311,7 +380,8 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn min(self) -> HashMap<K, V>
where V: Ord,
where
V: Ord,
{
self.min_by(|_, v1, v2| V::cmp(v1, v2))
}
@@ -336,11 +406,12 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V>
where F: FnMut(&K, &V, &V) -> Ordering,
where
F: FnMut(&K, &V, &V) -> Ordering,
{
self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
self.reduce(|acc, key, val| match compare(key, &acc, &val) {
Ordering::Less | Ordering::Equal => acc,
Ordering::Greater => val
Ordering::Greater => val,
})
}
@@ -364,8 +435,9 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
where F: FnMut(&K, &V) -> CK,
CK: Ord,
where
F: FnMut(&K, &V) -> CK,
CK: Ord,
{
self.min_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2)))
}
@@ -376,7 +448,7 @@ impl<I, K, V> GroupingMap<I>
/// If several elements are equally maximum, the last element is picked.
/// If several elements are equally minimum, the first element is picked.
///
/// See [.minmax()](crate::Itertools::minmax) for the non-grouping version.
/// See [`Itertools::minmax`](crate::Itertools::minmax) for the non-grouping version.
///
/// Differences from the non grouping version:
/// - It never produces a `MinMaxResult::NoElements`
@@ -398,7 +470,8 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn minmax(self) -> HashMap<K, MinMaxResult<V>>
where V: Ord,
where
V: Ord,
{
self.minmax_by(|_, v1, v2| V::cmp(v1, v2))
}
@@ -427,7 +500,8 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>>
where F: FnMut(&K, &V, &V) -> Ordering,
where
F: FnMut(&K, &V, &V) -> Ordering,
{
self.aggregate(|acc, key, val| {
Some(match acc {
@@ -477,15 +551,16 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>>
where F: FnMut(&K, &V) -> CK,
CK: Ord,
where
F: FnMut(&K, &V) -> CK,
CK: Ord,
{
self.minmax_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2)))
}
/// Groups elements from the `GroupingMap` source by key and sums them.
///
/// This is just a shorthand for `self.fold_first(|acc, _, val| acc + val)`.
/// This is just a shorthand for `self.reduce(|acc, _, val| acc + val)`.
/// It is more limited than `Iterator::sum` since it doesn't use the `Sum` trait.
///
/// Returns a `HashMap` associating the key of each group with the sum of that group's elements.
@@ -503,14 +578,15 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn sum(self) -> HashMap<K, V>
where V: Add<V, Output = V>
where
V: Add<V, Output = V>,
{
self.fold_first(|acc, _, val| acc + val)
self.reduce(|acc, _, val| acc + val)
}
/// Groups elements from the `GroupingMap` source by key and multiply them.
///
/// This is just a shorthand for `self.fold_first(|acc, _, val| acc * val)`.
/// This is just a shorthand for `self.reduce(|acc, _, val| acc * val)`.
/// It is more limited than `Iterator::product` since it doesn't use the `Product` trait.
///
/// Returns a `HashMap` associating the key of each group with the product of that group's elements.
@@ -528,8 +604,9 @@ impl<I, K, V> GroupingMap<I>
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn product(self) -> HashMap<K, V>
where V: Mul<V, Output = V>,
where
V: Mul<V, Output = V>,
{
self.fold_first(|acc, _, val| acc * val)
self.reduce(|acc, _, val| acc * val)
}
}

View File

@@ -27,3 +27,8 @@ macro_rules! clone_fields {
macro_rules! ignore_ident{
($id:ident, $($t:tt)*) => {$($t)*};
}
macro_rules! count_ident {
() => {0};
($i0:ident $($i:ident)*) => {1 + count_ident!($($i)*)};
}

View File

@@ -1,5 +1,5 @@
use std::iter::{Fuse, FusedIterator};
use super::size_hint;
use std::iter::{Fuse, FusedIterator};
pub trait IntersperseElement<Item> {
fn generate(&mut self) -> Item;
@@ -26,12 +26,13 @@ pub type Intersperse<I> = IntersperseWith<I, IntersperseElementSimple<<I as Iter
/// Create a new Intersperse iterator
pub fn intersperse<I>(iter: I, elt: I::Item) -> Intersperse<I>
where I: Iterator,
where
I: Iterator,
{
intersperse_with(iter, IntersperseElementSimple(elt))
}
impl<Item, F: FnMut()->Item> IntersperseElement<Item> for F {
impl<Item, F: FnMut() -> Item> IntersperseElement<Item> for F {
fn generate(&mut self) -> Item {
self()
}
@@ -48,71 +49,94 @@ impl<Item, F: FnMut()->Item> IntersperseElement<Item> for F {
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct IntersperseWith<I, ElemF>
where I: Iterator,
where
I: Iterator,
{
element: ElemF,
iter: Fuse<I>,
peek: Option<I::Item>,
/// `peek` is None while no item have been taken out of `iter` (at definition).
/// Then `peek` will alternatively be `Some(None)` and `Some(Some(item))`,
/// where `None` indicates it's time to generate from `element` (unless `iter` is empty).
peek: Option<Option<I::Item>>,
}
/// Create a new `IntersperseWith` iterator
pub fn intersperse_with<I, ElemF>(iter: I, elt: ElemF) -> IntersperseWith<I, ElemF>
where I: Iterator,
where
I: Iterator,
{
let mut iter = iter.fuse();
IntersperseWith {
peek: iter.next(),
iter,
peek: None,
iter: iter.fuse(),
element: elt,
}
}
impl<I, ElemF> Iterator for IntersperseWith<I, ElemF>
where I: Iterator,
ElemF: IntersperseElement<I::Item>
where
I: Iterator,
ElemF: IntersperseElement<I::Item>,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.peek.is_some() {
self.peek.take()
} else {
self.peek = self.iter.next();
if self.peek.is_some() {
Some(self.element.generate())
} else {
None
let Self {
element,
iter,
peek,
} = self;
match peek {
Some(item @ Some(_)) => item.take(),
Some(None) => match iter.next() {
new @ Some(_) => {
*peek = Some(new);
Some(element.generate())
}
None => None,
},
None => {
*peek = Some(None);
iter.next()
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
// 2 * SH + { 1 or 0 }
let has_peek = self.peek.is_some() as usize;
let sh = self.iter.size_hint();
size_hint::add_scalar(size_hint::add(sh, sh), has_peek)
let mut sh = self.iter.size_hint();
sh = size_hint::add(sh, sh);
match self.peek {
Some(Some(_)) => size_hint::add_scalar(sh, 1),
Some(None) => sh,
None => size_hint::sub_scalar(sh, 1),
}
}
fn fold<B, F>(mut self, init: B, mut f: F) -> B where
Self: Sized, F: FnMut(B, Self::Item) -> B,
fn fold<B, F>(self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
let Self {
mut element,
mut iter,
peek,
} = self;
let mut accum = init;
if let Some(x) = self.peek.take() {
if let Some(x) = peek.unwrap_or_else(|| iter.next()) {
accum = f(accum, x);
}
let element = &mut self.element;
self.iter.fold(accum,
|accum, x| {
let accum = f(accum, element.generate());
f(accum, x)
iter.fold(accum, |accum, x| {
let accum = f(accum, element.generate());
f(accum, x)
})
}
}
impl<I, ElemF> FusedIterator for IntersperseWith<I, ElemF>
where I: Iterator,
ElemF: IntersperseElement<I::Item>
{}
where
I: Iterator,
ElemF: IntersperseElement<I::Item>,
{
}

View File

@@ -0,0 +1,116 @@
use core::iter::{Skip, Take};
use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
#[cfg(doc)]
use crate::Itertools;
mod private_iter_index {
use core::ops;
pub trait Sealed {}
impl Sealed for ops::Range<usize> {}
impl Sealed for ops::RangeInclusive<usize> {}
impl Sealed for ops::RangeTo<usize> {}
impl Sealed for ops::RangeToInclusive<usize> {}
impl Sealed for ops::RangeFrom<usize> {}
impl Sealed for ops::RangeFull {}
}
/// Used by [`Itertools::get`] to know which iterator
/// to turn different ranges into.
pub trait IteratorIndex<I>: private_iter_index::Sealed
where
I: Iterator,
{
/// The type returned for this type of index.
type Output: Iterator<Item = I::Item>;
/// Returns an adapted iterator for the current index.
///
/// Prefer calling [`Itertools::get`] instead
/// of calling this directly.
fn index(self, from: I) -> Self::Output;
}
impl<I> IteratorIndex<I> for Range<usize>
where
I: Iterator,
{
type Output = Skip<Take<I>>;
fn index(self, iter: I) -> Self::Output {
iter.take(self.end).skip(self.start)
}
}
impl<I> IteratorIndex<I> for RangeInclusive<usize>
where
I: Iterator,
{
type Output = Take<Skip<I>>;
fn index(self, iter: I) -> Self::Output {
// end - start + 1 without overflowing if possible
let length = if *self.end() == usize::MAX {
assert_ne!(*self.start(), 0);
self.end() - self.start() + 1
} else {
(self.end() + 1).saturating_sub(*self.start())
};
iter.skip(*self.start()).take(length)
}
}
impl<I> IteratorIndex<I> for RangeTo<usize>
where
I: Iterator,
{
type Output = Take<I>;
fn index(self, iter: I) -> Self::Output {
iter.take(self.end)
}
}
impl<I> IteratorIndex<I> for RangeToInclusive<usize>
where
I: Iterator,
{
type Output = Take<I>;
fn index(self, iter: I) -> Self::Output {
assert_ne!(self.end, usize::MAX);
iter.take(self.end + 1)
}
}
impl<I> IteratorIndex<I> for RangeFrom<usize>
where
I: Iterator,
{
type Output = Skip<I>;
fn index(self, iter: I) -> Self::Output {
iter.skip(self.start)
}
}
impl<I> IteratorIndex<I> for RangeFull
where
I: Iterator,
{
type Output = I;
fn index(self, iter: I) -> Self::Output {
iter
}
}
pub fn get<I, R>(iter: I, index: R) -> R::Output
where
I: IntoIterator,
R: IteratorIndex<I::IntoIter>,
{
index.index(iter.into_iter())
}

View File

@@ -1,20 +1,138 @@
use alloc::collections::BinaryHeap;
use core::cmp::Ord;
use alloc::vec::Vec;
use core::cmp::Ordering;
pub(crate) fn k_smallest<T: Ord, I: Iterator<Item = T>>(mut iter: I, k: usize) -> BinaryHeap<T> {
if k == 0 { return BinaryHeap::new(); }
/// Consumes a given iterator, returning the minimum elements in **ascending** order.
pub(crate) fn k_smallest_general<I, F>(iter: I, k: usize, mut comparator: F) -> Vec<I::Item>
where
I: Iterator,
F: FnMut(&I::Item, &I::Item) -> Ordering,
{
/// Sift the element currently at `origin` away from the root until it is properly ordered.
///
/// This will leave **larger** elements closer to the root of the heap.
fn sift_down<T, F>(heap: &mut [T], is_less_than: &mut F, mut origin: usize)
where
F: FnMut(&T, &T) -> bool,
{
#[inline]
fn children_of(n: usize) -> (usize, usize) {
(2 * n + 1, 2 * n + 2)
}
let mut heap = iter.by_ref().take(k).collect::<BinaryHeap<_>>();
while origin < heap.len() {
let (left_idx, right_idx) = children_of(origin);
if left_idx >= heap.len() {
return;
}
iter.for_each(|i| {
debug_assert_eq!(heap.len(), k);
// Equivalent to heap.push(min(i, heap.pop())) but more efficient.
// This should be done with a single `.peek_mut().unwrap()` but
// `PeekMut` sifts-down unconditionally on Rust 1.46.0 and prior.
if *heap.peek().unwrap() > i {
*heap.peek_mut().unwrap() = i;
let replacement_idx =
if right_idx < heap.len() && is_less_than(&heap[left_idx], &heap[right_idx]) {
right_idx
} else {
left_idx
};
if is_less_than(&heap[origin], &heap[replacement_idx]) {
heap.swap(origin, replacement_idx);
origin = replacement_idx;
} else {
return;
}
}
}
if k == 0 {
iter.last();
return Vec::new();
}
if k == 1 {
return iter.min_by(comparator).into_iter().collect();
}
let mut iter = iter.fuse();
let mut storage: Vec<I::Item> = iter.by_ref().take(k).collect();
let mut is_less_than = move |a: &_, b: &_| comparator(a, b) == Ordering::Less;
// Rearrange the storage into a valid heap by reordering from the second-bottom-most layer up to the root.
// Slightly faster than ordering on each insert, but only by a factor of lg(k).
// The resulting heap has the **largest** item on top.
for i in (0..=(storage.len() / 2)).rev() {
sift_down(&mut storage, &mut is_less_than, i);
}
iter.for_each(|val| {
debug_assert_eq!(storage.len(), k);
if is_less_than(&val, &storage[0]) {
// Treating this as an push-and-pop saves having to write a sift-up implementation.
// https://en.wikipedia.org/wiki/Binary_heap#Insert_then_extract
storage[0] = val;
// We retain the smallest items we've seen so far, but ordered largest first so we can drop the largest efficiently.
sift_down(&mut storage, &mut is_less_than, 0);
}
});
heap
// Ultimately the items need to be in least-first, strict order, but the heap is currently largest-first.
// To achieve this, repeatedly,
// 1) "pop" the largest item off the heap into the tail slot of the underlying storage,
// 2) shrink the logical size of the heap by 1,
// 3) restore the heap property over the remaining items.
let mut heap = &mut storage[..];
while heap.len() > 1 {
let last_idx = heap.len() - 1;
heap.swap(0, last_idx);
// Sifting over a truncated slice means that the sifting will not disturb already popped elements.
heap = &mut heap[..last_idx];
sift_down(heap, &mut is_less_than, 0);
}
storage
}
pub(crate) fn k_smallest_relaxed_general<I, F>(iter: I, k: usize, mut comparator: F) -> Vec<I::Item>
where
I: Iterator,
F: FnMut(&I::Item, &I::Item) -> Ordering,
{
if k == 0 {
iter.last();
return Vec::new();
}
let mut iter = iter.fuse();
let mut buf = iter.by_ref().take(2 * k).collect::<Vec<_>>();
if buf.len() < k {
buf.sort_unstable_by(&mut comparator);
return buf;
}
buf.select_nth_unstable_by(k - 1, &mut comparator);
buf.truncate(k);
iter.for_each(|val| {
if comparator(&val, &buf[k - 1]) != Ordering::Less {
return;
}
assert_ne!(buf.len(), buf.capacity());
buf.push(val);
if buf.len() == 2 * k {
buf.select_nth_unstable_by(k - 1, &mut comparator);
buf.truncate(k);
}
});
buf.sort_unstable_by(&mut comparator);
buf.truncate(k);
buf
}
#[inline]
pub(crate) fn key_to_cmp<T, K, F>(mut key: F) -> impl FnMut(&T, &T) -> Ordering
where
F: FnMut(&T) -> K,
K: Ord,
{
move |a, b| key(a).cmp(&key(b))
}

View File

@@ -1,10 +1,9 @@
use crate::size_hint;
use crate::Itertools;
use alloc::vec::Vec;
use std::fmt;
use std::iter::FusedIterator;
use std::mem::replace;
use std::fmt;
/// Head element and Tail iterator pair
///
@@ -15,24 +14,21 @@ use std::fmt;
/// `KMerge` into a min-heap.
#[derive(Debug)]
struct HeadTail<I>
where I: Iterator
where
I: Iterator,
{
head: I::Item,
tail: I,
}
impl<I> HeadTail<I>
where I: Iterator
where
I: Iterator,
{
/// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the `Iterator` is empty.
fn new(mut it: I) -> Option<HeadTail<I>> {
fn new(mut it: I) -> Option<Self> {
let head = it.next();
head.map(|h| {
HeadTail {
head: h,
tail: it,
}
})
head.map(|h| Self { head: h, tail: it })
}
/// Get the next element and update `head`, returning the old head in `Some`.
@@ -53,15 +49,17 @@ impl<I> HeadTail<I>
}
impl<I> Clone for HeadTail<I>
where I: Iterator + Clone,
I::Item: Clone
where
I: Iterator + Clone,
I::Item: Clone,
{
clone_fields!(head, tail);
}
/// Make `data` a heap (min-heap w.r.t the sorting).
fn heapify<T, S>(data: &mut [T], mut less_than: S)
where S: FnMut(&T, &T) -> bool
where
S: FnMut(&T, &T) -> bool,
{
for i in (0..data.len() / 2).rev() {
sift_down(data, i, &mut less_than);
@@ -70,7 +68,8 @@ fn heapify<T, S>(data: &mut [T], mut less_than: S)
/// Sift down element at `index` (`heap` is a min-heap wrt the ordering)
fn sift_down<T, S>(heap: &mut [T], index: usize, mut less_than: S)
where S: FnMut(&T, &T) -> bool
where
S: FnMut(&T, &T) -> bool,
{
debug_assert!(index <= heap.len());
let mut pos = index;
@@ -81,7 +80,7 @@ fn sift_down<T, S>(heap: &mut [T], index: usize, mut less_than: S)
while child + 1 < heap.len() {
// pick the smaller of the two children
// use arithmetic to avoid an unpredictable branch
child += less_than(&heap[child+1], &heap[child]) as usize;
child += less_than(&heap[child + 1], &heap[child]) as usize;
// sift down is done if we are already in order
if !less_than(&heap[child], &heap[pos]) {
@@ -119,7 +118,7 @@ impl<T: PartialOrd> KMergePredicate<T> for KMergeByLt {
}
}
impl<T, F: FnMut(&T, &T)->bool> KMergePredicate<T> for F {
impl<T, F: FnMut(&T, &T) -> bool> KMergePredicate<T> for F {
fn kmerge_pred(&mut self, a: &T, b: &T) -> bool {
self(a, b)
}
@@ -128,19 +127,21 @@ impl<T, F: FnMut(&T, &T)->bool> KMergePredicate<T> for F {
/// Create an iterator that merges elements of the contained iterators using
/// the ordering function.
///
/// [`IntoIterator`] enabled version of [`Itertools::kmerge`].
/// [`IntoIterator`] enabled version of [`Itertools::kmerge`](crate::Itertools::kmerge).
///
/// ```
/// use itertools::kmerge;
///
/// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) {
/// /* loop body */
/// # let _ = elt;
/// }
/// ```
pub fn kmerge<I>(iterable: I) -> KMerge<<I::Item as IntoIterator>::IntoIter>
where I: IntoIterator,
I::Item: IntoIterator,
<<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd
where
I: IntoIterator,
I::Item: IntoIterator,
<<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd,
{
kmerge_by(iterable, KMergeByLt)
}
@@ -152,29 +153,34 @@ pub fn kmerge<I>(iterable: I) -> KMerge<<I::Item as IntoIterator>::IntoIter>
///
/// See [`.kmerge_by()`](crate::Itertools::kmerge_by) for more
/// information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[must_use = "this iterator adaptor is not lazy but does nearly nothing unless consumed"]
pub struct KMergeBy<I, F>
where I: Iterator,
where
I: Iterator,
{
heap: Vec<HeadTail<I>>,
less_than: F,
}
impl<I, F> fmt::Debug for KMergeBy<I, F>
where I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
{
debug_fmt_fields!(KMergeBy, heap);
}
/// Create an iterator that merges elements of the contained iterators.
///
/// [`IntoIterator`] enabled version of [`Itertools::kmerge_by`].
pub fn kmerge_by<I, F>(iterable: I, mut less_than: F)
-> KMergeBy<<I::Item as IntoIterator>::IntoIter, F>
where I: IntoIterator,
I::Item: IntoIterator,
F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>,
/// [`IntoIterator`] enabled version of [`Itertools::kmerge_by`](crate::Itertools::kmerge_by).
pub fn kmerge_by<I, F>(
iterable: I,
mut less_than: F,
) -> KMergeBy<<I::Item as IntoIterator>::IntoIter, F>
where
I: IntoIterator,
I::Item: IntoIterator,
F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>,
{
let iter = iterable.into_iter();
let (lower, _) = iter.size_hint();
@@ -185,16 +191,18 @@ pub fn kmerge_by<I, F>(iterable: I, mut less_than: F)
}
impl<I, F> Clone for KMergeBy<I, F>
where I: Iterator + Clone,
I::Item: Clone,
F: Clone,
where
I: Iterator + Clone,
I::Item: Clone,
F: Clone,
{
clone_fields!(heap, less_than);
}
impl<I, F> Iterator for KMergeBy<I, F>
where I: Iterator,
F: KMergePredicate<I::Item>
where
I: Iterator,
F: KMergePredicate<I::Item>,
{
type Item = I::Item;
@@ -208,20 +216,24 @@ impl<I, F> Iterator for KMergeBy<I, F>
self.heap.swap_remove(0).head
};
let less_than = &mut self.less_than;
sift_down(&mut self.heap, 0, |a, b| less_than.kmerge_pred(&a.head, &b.head));
sift_down(&mut self.heap, 0, |a, b| {
less_than.kmerge_pred(&a.head, &b.head)
});
Some(result)
}
fn size_hint(&self) -> (usize, Option<usize>) {
#[allow(deprecated)] //TODO: once msrv hits 1.51. replace `fold1` with `reduce`
self.heap.iter()
.map(|i| i.size_hint())
.fold1(size_hint::add)
.unwrap_or((0, Some(0)))
self.heap
.iter()
.map(|i| i.size_hint())
.reduce(size_hint::add)
.unwrap_or((0, Some(0)))
}
}
impl<I, F> FusedIterator for KMergeBy<I, F>
where I: Iterator,
F: KMergePredicate<I::Item>
{}
where
I: Iterator,
F: KMergePredicate<I::Item>,
{
}

View File

@@ -1,10 +1,12 @@
use std::ops::Index;
use alloc::vec::Vec;
use std::iter::Fuse;
use std::ops::Index;
use crate::size_hint::{self, SizeHint};
#[derive(Debug, Clone)]
pub struct LazyBuffer<I: Iterator> {
pub it: I,
done: bool,
it: Fuse<I>,
buffer: Vec<I::Item>,
}
@@ -12,10 +14,9 @@ impl<I> LazyBuffer<I>
where
I: Iterator,
{
pub fn new(it: I) -> LazyBuffer<I> {
LazyBuffer {
it,
done: false,
pub fn new(it: I) -> Self {
Self {
it: it.fuse(),
buffer: Vec::new(),
}
}
@@ -24,36 +25,51 @@ where
self.buffer.len()
}
pub fn size_hint(&self) -> SizeHint {
size_hint::add_scalar(self.it.size_hint(), self.len())
}
pub fn count(self) -> usize {
self.len() + self.it.count()
}
pub fn get_next(&mut self) -> bool {
if self.done {
return false;
}
if let Some(x) = self.it.next() {
self.buffer.push(x);
true
} else {
self.done = true;
false
}
}
pub fn prefill(&mut self, len: usize) {
let buffer_len = self.buffer.len();
if !self.done && len > buffer_len {
if len > buffer_len {
let delta = len - buffer_len;
self.buffer.extend(self.it.by_ref().take(delta));
self.done = self.buffer.len() < len;
}
}
}
impl<I> LazyBuffer<I>
where
I: Iterator,
I::Item: Clone,
{
pub fn get_at(&self, indices: &[usize]) -> Vec<I::Item> {
indices.iter().map(|i| self.buffer[*i].clone()).collect()
}
pub fn get_array<const K: usize>(&self, indices: [usize; K]) -> [I::Item; K] {
indices.map(|i| self.buffer[i].clone())
}
}
impl<I, J> Index<J> for LazyBuffer<I>
where
I: Iterator,
I::Item: Sized,
Vec<I::Item>: Index<J>
Vec<I::Item>: Index<J>,
{
type Output = <Vec<I::Item> as Index<J>>::Output;

File diff suppressed because it is too large Load Diff

View File

@@ -1,151 +1,315 @@
use std::cmp::Ordering;
use std::iter::Fuse;
use std::fmt;
use std::iter::{Fuse, FusedIterator};
use std::marker::PhantomData;
use super::adaptors::{PutBack, put_back};
use either::Either;
use super::adaptors::{put_back, PutBack};
use crate::either_or_both::EitherOrBoth;
use crate::size_hint::{self, SizeHint};
#[cfg(doc)]
use crate::Itertools;
#[derive(Clone, Debug)]
pub struct MergeLte;
/// An iterator adaptor that merges the two base iterators in ascending order.
/// If both base iterators are sorted (ascending), the result is sorted.
///
/// Iterator element type is `I::Item`.
///
/// See [`.merge()`](crate::Itertools::merge_by) for more information.
pub type Merge<I, J> = MergeBy<I, J, MergeLte>;
/// Create an iterator that merges elements in `i` and `j`.
///
/// [`IntoIterator`] enabled version of [`Itertools::merge`](crate::Itertools::merge).
///
/// ```
/// use itertools::merge;
///
/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) {
/// /* loop body */
/// # let _ = elt;
/// }
/// ```
pub fn merge<I, J>(
i: I,
j: J,
) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
where
I: IntoIterator,
J: IntoIterator<Item = I::Item>,
I::Item: PartialOrd,
{
merge_by_new(i, j, MergeLte)
}
/// An iterator adaptor that merges the two base iterators in ascending order.
/// If both base iterators are sorted (ascending), the result is sorted.
///
/// Iterator element type is `I::Item`.
///
/// See [`.merge_by()`](crate::Itertools::merge_by) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MergeBy<I: Iterator, J: Iterator, F> {
left: PutBack<Fuse<I>>,
right: PutBack<Fuse<J>>,
cmp_fn: F,
}
/// Create a `MergeBy` iterator.
pub fn merge_by_new<I, J, F>(a: I, b: J, cmp: F) -> MergeBy<I::IntoIter, J::IntoIter, F>
where
I: IntoIterator,
J: IntoIterator<Item = I::Item>,
{
MergeBy {
left: put_back(a.into_iter().fuse()),
right: put_back(b.into_iter().fuse()),
cmp_fn: cmp,
}
}
/// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order.
///
/// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`].
pub fn merge_join_by<I, J, F>(left: I, right: J, cmp_fn: F)
-> MergeJoinBy<I::IntoIter, J::IntoIter, F>
where I: IntoIterator,
J: IntoIterator,
F: FnMut(&I::Item, &J::Item) -> Ordering
pub fn merge_join_by<I, J, F, T>(
left: I,
right: J,
cmp_fn: F,
) -> MergeJoinBy<I::IntoIter, J::IntoIter, F>
where
I: IntoIterator,
J: IntoIterator,
F: FnMut(&I::Item, &J::Item) -> T,
{
MergeJoinBy {
MergeBy {
left: put_back(left.into_iter().fuse()),
right: put_back(right.into_iter().fuse()),
cmp_fn,
cmp_fn: MergeFuncLR(cmp_fn, PhantomData),
}
}
/// An iterator adaptor that merge-joins items from the two base iterators in ascending order.
///
/// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MergeJoinBy<I: Iterator, J: Iterator, F> {
left: PutBack<Fuse<I>>,
right: PutBack<Fuse<J>>,
cmp_fn: F
pub type MergeJoinBy<I, J, F> =
MergeBy<I, J, MergeFuncLR<F, <F as FuncLR<<I as Iterator>::Item, <J as Iterator>::Item>>::T>>;
#[derive(Clone, Debug)]
pub struct MergeFuncLR<F, T>(F, PhantomData<T>);
pub trait FuncLR<L, R> {
type T;
}
impl<I, J, F> Clone for MergeJoinBy<I, J, F>
where I: Iterator,
J: Iterator,
PutBack<Fuse<I>>: Clone,
PutBack<Fuse<J>>: Clone,
F: Clone,
{
clone_fields!(left, right, cmp_fn);
impl<L, R, T, F: FnMut(&L, &R) -> T> FuncLR<L, R> for F {
type T = T;
}
impl<I, J, F> fmt::Debug for MergeJoinBy<I, J, F>
where I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
J: Iterator + fmt::Debug,
J::Item: fmt::Debug,
{
debug_fmt_fields!(MergeJoinBy, left, right);
pub trait OrderingOrBool<L, R> {
type MergeResult;
fn left(left: L) -> Self::MergeResult;
fn right(right: R) -> Self::MergeResult;
// "merge" never returns (Some(...), Some(...), ...) so Option<Either<I::Item, J::Item>>
// is appealing but it is always followed by two put_backs, so we think the compiler is
// smart enough to optimize it. Or we could move put_backs into "merge".
fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult);
fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint;
}
impl<I, J, F> Iterator for MergeJoinBy<I, J, F>
where I: Iterator,
J: Iterator,
F: FnMut(&I::Item, &J::Item) -> Ordering
{
type Item = EitherOrBoth<I::Item, J::Item>;
fn next(&mut self) -> Option<Self::Item> {
match (self.left.next(), self.right.next()) {
(None, None) => None,
(Some(left), None) =>
Some(EitherOrBoth::Left(left)),
(None, Some(right)) =>
Some(EitherOrBoth::Right(right)),
(Some(left), Some(right)) => {
match (self.cmp_fn)(&left, &right) {
Ordering::Equal =>
Some(EitherOrBoth::Both(left, right)),
Ordering::Less => {
self.right.put_back(right);
Some(EitherOrBoth::Left(left))
},
Ordering::Greater => {
self.left.put_back(left);
Some(EitherOrBoth::Right(right))
}
}
}
impl<L, R, F: FnMut(&L, &R) -> Ordering> OrderingOrBool<L, R> for MergeFuncLR<F, Ordering> {
type MergeResult = EitherOrBoth<L, R>;
fn left(left: L) -> Self::MergeResult {
EitherOrBoth::Left(left)
}
fn right(right: R) -> Self::MergeResult {
EitherOrBoth::Right(right)
}
fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult) {
match self.0(&left, &right) {
Ordering::Equal => (None, EitherOrBoth::Both(left, right)),
Ordering::Less => (Some(Either::Right(right)), EitherOrBoth::Left(left)),
Ordering::Greater => (Some(Either::Left(left)), EitherOrBoth::Right(right)),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (a_lower, a_upper) = self.left.size_hint();
let (b_lower, b_upper) = self.right.size_hint();
fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
let (a_lower, a_upper) = left;
let (b_lower, b_upper) = right;
let lower = ::std::cmp::max(a_lower, b_lower);
let upper = match (a_upper, b_upper) {
(Some(x), Some(y)) => x.checked_add(y),
_ => None,
};
(lower, upper)
}
}
fn count(mut self) -> usize {
let mut count = 0;
loop {
match (self.left.next(), self.right.next()) {
(None, None) => break count,
(Some(_left), None) => break count + 1 + self.left.into_parts().1.count(),
(None, Some(_right)) => break count + 1 + self.right.into_parts().1.count(),
(Some(left), Some(right)) => {
count += 1;
match (self.cmp_fn)(&left, &right) {
Ordering::Equal => {}
Ordering::Less => self.right.put_back(right),
Ordering::Greater => self.left.put_back(left),
impl<L, R, F: FnMut(&L, &R) -> bool> OrderingOrBool<L, R> for MergeFuncLR<F, bool> {
type MergeResult = Either<L, R>;
fn left(left: L) -> Self::MergeResult {
Either::Left(left)
}
fn right(right: R) -> Self::MergeResult {
Either::Right(right)
}
fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult) {
if self.0(&left, &right) {
(Some(Either::Right(right)), Either::Left(left))
} else {
(Some(Either::Left(left)), Either::Right(right))
}
}
fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
// Not ExactSizeIterator because size may be larger than usize
size_hint::add(left, right)
}
}
impl<T, F: FnMut(&T, &T) -> bool> OrderingOrBool<T, T> for F {
type MergeResult = T;
fn left(left: T) -> Self::MergeResult {
left
}
fn right(right: T) -> Self::MergeResult {
right
}
fn merge(&mut self, left: T, right: T) -> (Option<Either<T, T>>, Self::MergeResult) {
if self(&left, &right) {
(Some(Either::Right(right)), left)
} else {
(Some(Either::Left(left)), right)
}
}
fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
// Not ExactSizeIterator because size may be larger than usize
size_hint::add(left, right)
}
}
impl<T: PartialOrd> OrderingOrBool<T, T> for MergeLte {
type MergeResult = T;
fn left(left: T) -> Self::MergeResult {
left
}
fn right(right: T) -> Self::MergeResult {
right
}
fn merge(&mut self, left: T, right: T) -> (Option<Either<T, T>>, Self::MergeResult) {
if left <= right {
(Some(Either::Right(right)), left)
} else {
(Some(Either::Left(left)), right)
}
}
fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
// Not ExactSizeIterator because size may be larger than usize
size_hint::add(left, right)
}
}
impl<I, J, F> Clone for MergeBy<I, J, F>
where
I: Iterator,
J: Iterator,
PutBack<Fuse<I>>: Clone,
PutBack<Fuse<J>>: Clone,
F: Clone,
{
clone_fields!(left, right, cmp_fn);
}
impl<I, J, F> fmt::Debug for MergeBy<I, J, F>
where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
J: Iterator + fmt::Debug,
J::Item: fmt::Debug,
{
debug_fmt_fields!(MergeBy, left, right);
}
impl<I, J, F> Iterator for MergeBy<I, J, F>
where
I: Iterator,
J: Iterator,
F: OrderingOrBool<I::Item, J::Item>,
{
type Item = F::MergeResult;
fn next(&mut self) -> Option<Self::Item> {
match (self.left.next(), self.right.next()) {
(None, None) => None,
(Some(left), None) => Some(F::left(left)),
(None, Some(right)) => Some(F::right(right)),
(Some(left), Some(right)) => {
let (not_next, next) = self.cmp_fn.merge(left, right);
match not_next {
Some(Either::Left(l)) => {
self.left.put_back(l);
}
Some(Either::Right(r)) => {
self.right.put_back(r);
}
None => (),
}
Some(next)
}
}
}
fn last(mut self) -> Option<Self::Item> {
let mut previous_element = None;
fn fold<B, G>(mut self, init: B, mut f: G) -> B
where
Self: Sized,
G: FnMut(B, Self::Item) -> B,
{
let mut acc = init;
let mut left = self.left.next();
let mut right = self.right.next();
loop {
match (self.left.next(), self.right.next()) {
(None, None) => break previous_element,
(Some(left), None) => {
break Some(EitherOrBoth::Left(
self.left.into_parts().1.last().unwrap_or(left),
))
}
(None, Some(right)) => {
break Some(EitherOrBoth::Right(
self.right.into_parts().1.last().unwrap_or(right),
))
}
(Some(left), Some(right)) => {
previous_element = match (self.cmp_fn)(&left, &right) {
Ordering::Equal => Some(EitherOrBoth::Both(left, right)),
Ordering::Less => {
self.right.put_back(right);
Some(EitherOrBoth::Left(left))
}
Ordering::Greater => {
self.left.put_back(left);
Some(EitherOrBoth::Right(right))
}
match (left, right) {
(Some(l), Some(r)) => match self.cmp_fn.merge(l, r) {
(Some(Either::Right(r)), x) => {
acc = f(acc, x);
left = self.left.next();
right = Some(r);
}
(Some(Either::Left(l)), x) => {
acc = f(acc, x);
left = Some(l);
right = self.right.next();
}
(None, x) => {
acc = f(acc, x);
left = self.left.next();
right = self.right.next();
}
},
(Some(l), None) => {
self.left.put_back(l);
acc = self.left.fold(acc, |acc, x| f(acc, F::left(x)));
break;
}
(None, Some(r)) => {
self.right.put_back(r);
acc = self.right.fold(acc, |acc, x| f(acc, F::right(x)));
break;
}
(None, None) => {
break;
}
}
}
acc
}
fn size_hint(&self) -> SizeHint {
F::size_hint(self.left.size_hint(), self.right.size_hint())
}
fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
@@ -156,14 +320,29 @@ impl<I, J, F> Iterator for MergeJoinBy<I, J, F>
n -= 1;
match (self.left.next(), self.right.next()) {
(None, None) => break None,
(Some(_left), None) => break self.left.nth(n).map(EitherOrBoth::Left),
(None, Some(_right)) => break self.right.nth(n).map(EitherOrBoth::Right),
(Some(left), Some(right)) => match (self.cmp_fn)(&left, &right) {
Ordering::Equal => {}
Ordering::Less => self.right.put_back(right),
Ordering::Greater => self.left.put_back(left),
},
(Some(_left), None) => break self.left.nth(n).map(F::left),
(None, Some(_right)) => break self.right.nth(n).map(F::right),
(Some(left), Some(right)) => {
let (not_next, _) = self.cmp_fn.merge(left, right);
match not_next {
Some(Either::Left(l)) => {
self.left.put_back(l);
}
Some(Either::Right(r)) => {
self.right.put_back(r);
}
None => (),
}
}
}
}
}
}
impl<I, J, F> FusedIterator for MergeBy<I, J, F>
where
I: Iterator,
J: Iterator,
F: OrderingOrBool<I::Item, J::Item>,
{
}

View File

@@ -1,8 +1,7 @@
/// `MinMaxResult` is an enum returned by `minmax`.
///
/// See [`.minmax()`](crate::Itertools::minmax) for more detail.
#[derive(Copy, Clone, PartialEq, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum MinMaxResult<T> {
/// Empty iterator
NoElements,
@@ -12,7 +11,7 @@ pub enum MinMaxResult<T> {
/// More than one element in the iterator, the first element is not larger
/// than the second
MinMax(T, T)
MinMax(T, T),
}
impl<T: Clone> MinMaxResult<T> {
@@ -36,34 +35,36 @@ impl<T: Clone> MinMaxResult<T> {
/// let r = MinMax(1, 2);
/// assert_eq!(r.into_option(), Some((1, 2)));
/// ```
pub fn into_option(self) -> Option<(T,T)> {
pub fn into_option(self) -> Option<(T, T)> {
match self {
MinMaxResult::NoElements => None,
MinMaxResult::OneElement(x) => Some((x.clone(), x)),
MinMaxResult::MinMax(x, y) => Some((x, y))
Self::NoElements => None,
Self::OneElement(x) => Some((x.clone(), x)),
Self::MinMax(x, y) => Some((x, y)),
}
}
}
/// Implementation guts for `minmax` and `minmax_by_key`.
pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F,
mut lt: L) -> MinMaxResult<I::Item>
where I: Iterator,
F: FnMut(&I::Item) -> K,
L: FnMut(&I::Item, &I::Item, &K, &K) -> bool,
pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F, mut lt: L) -> MinMaxResult<I::Item>
where
I: Iterator,
F: FnMut(&I::Item) -> K,
L: FnMut(&I::Item, &I::Item, &K, &K) -> bool,
{
let (mut min, mut max, mut min_key, mut max_key) = match it.next() {
None => return MinMaxResult::NoElements,
Some(x) => {
match it.next() {
None => return MinMaxResult::OneElement(x),
Some(y) => {
let xk = key_for(&x);
let yk = key_for(&y);
if !lt(&y, &x, &yk, &xk) {(x, y, xk, yk)} else {(y, x, yk, xk)}
Some(x) => match it.next() {
None => return MinMaxResult::OneElement(x),
Some(y) => {
let xk = key_for(&x);
let yk = key_for(&y);
if !lt(&y, &x, &yk, &xk) {
(x, y, xk, yk)
} else {
(y, x, yk, xk)
}
}
}
},
};
loop {
@@ -74,7 +75,7 @@ pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F,
// for 2 elements.
let first = match it.next() {
None => break,
Some(x) => x
Some(x) => x,
};
let second = match it.next() {
None => {
@@ -86,7 +87,7 @@ pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F,
}
break;
}
Some(x) => x
Some(x) => x,
};
let first_key = key_for(&first);
let second_key = key_for(&second);

View File

@@ -1,14 +1,16 @@
use std::iter::Fuse;
use alloc::collections::VecDeque;
use crate::size_hint;
use crate::PeekingNext;
#[cfg(doc)]
use crate::Itertools;
use crate::PeekingNext;
use alloc::collections::VecDeque;
use std::iter::Fuse;
/// See [`multipeek()`] for more information.
#[derive(Clone, Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MultiPeek<I>
where I: Iterator
where
I: Iterator,
{
iter: Fuse<I>,
buf: VecDeque<I::Item>,
@@ -20,7 +22,8 @@ pub struct MultiPeek<I>
///
/// [`IntoIterator`] enabled version of [`Itertools::multipeek`].
pub fn multipeek<I>(iterable: I) -> MultiPeek<I::IntoIter>
where I: IntoIterator
where
I: IntoIterator,
{
MultiPeek {
iter: iterable.into_iter().fuse(),
@@ -30,7 +33,8 @@ pub fn multipeek<I>(iterable: I) -> MultiPeek<I::IntoIter>
}
impl<I> MultiPeek<I>
where I: Iterator
where
I: Iterator,
{
/// Reset the peeking “cursor”
pub fn reset_peek(&mut self) {
@@ -62,24 +66,31 @@ impl<I: Iterator> MultiPeek<I> {
}
impl<I> PeekingNext for MultiPeek<I>
where I: Iterator,
where
I: Iterator,
{
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where F: FnOnce(&Self::Item) -> bool
where
F: FnOnce(&Self::Item) -> bool,
{
if self.buf.is_empty() {
if let Some(r) = self.peek() {
if !accept(r) { return None }
if !accept(r) {
return None;
}
}
} else if let Some(r) = self.buf.front() {
if !accept(r) {
return None;
}
} else if let Some(r) = self.buf.get(0) {
if !accept(r) { return None }
}
self.next()
}
}
impl<I> Iterator for MultiPeek<I>
where I: Iterator
where
I: Iterator,
{
type Item = I::Item;
@@ -91,11 +102,15 @@ impl<I> Iterator for MultiPeek<I>
fn size_hint(&self) -> (usize, Option<usize>) {
size_hint::add_scalar(self.iter.size_hint(), self.buf.len())
}
fn fold<B, F>(self, mut init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
init = self.buf.into_iter().fold(init, &mut f);
self.iter.fold(init, f)
}
}
// Same size
impl<I> ExactSizeIterator for MultiPeek<I>
where I: ExactSizeIterator
{}
impl<I> ExactSizeIterator for MultiPeek<I> where I: ExactSizeIterator {}

View File

@@ -0,0 +1,269 @@
use core::mem::{self, MaybeUninit};
/// An array of at most `N` elements.
struct ArrayBuilder<T, const N: usize> {
/// The (possibly uninitialized) elements of the `ArrayBuilder`.
///
/// # Safety
///
/// The elements of `arr[..len]` are valid `T`s.
arr: [MaybeUninit<T>; N],
/// The number of leading elements of `arr` that are valid `T`s, len <= N.
len: usize,
}
impl<T, const N: usize> ArrayBuilder<T, N> {
/// Initializes a new, empty `ArrayBuilder`.
pub fn new() -> Self {
// SAFETY: The safety invariant of `arr` trivially holds for `len = 0`.
Self {
arr: [(); N].map(|_| MaybeUninit::uninit()),
len: 0,
}
}
/// Pushes `value` onto the end of the array.
///
/// # Panics
///
/// This panics if `self.len >= N`.
#[inline(always)]
pub fn push(&mut self, value: T) {
// PANICS: This will panic if `self.len >= N`.
let place = &mut self.arr[self.len];
// SAFETY: The safety invariant of `self.arr` applies to elements at
// indices `0..self.len` — not to the element at `self.len`. Writing to
// the element at index `self.len` therefore does not violate the safety
// invariant of `self.arr`. Even if this line panics, we have not
// created any intermediate invalid state.
*place = MaybeUninit::new(value);
// Lemma: `self.len < N`. By invariant, `self.len <= N`. Above, we index
// into `self.arr`, which has size `N`, at index `self.len`. If `self.len == N`
// at that point, that index would be out-of-bounds, and the index
// operation would panic. Thus, `self.len != N`, and since `self.len <= N`,
// that means that `self.len < N`.
//
// PANICS: Since `self.len < N`, and since `N <= usize::MAX`,
// `self.len + 1 <= usize::MAX`, and so `self.len += 1` will not
// overflow. Overflow is the only panic condition of `+=`.
//
// SAFETY:
// - We are required to uphold the invariant that `self.len <= N`.
// Since, by the preceding lemma, `self.len < N` at this point in the
// code, `self.len += 1` results in `self.len <= N`.
// - We are required to uphold the invariant that `self.arr[..self.len]`
// are valid instances of `T`. Since this invariant already held when
// this method was called, and since we only increment `self.len`
// by 1 here, we only need to prove that the element at
// `self.arr[self.len]` (using the value of `self.len` before incrementing)
// is valid. Above, we construct `place` to point to `self.arr[self.len]`,
// and then initialize `*place` to `MaybeUninit::new(value)`, which is
// a valid `T` by construction.
self.len += 1;
}
/// Consumes the elements in the `ArrayBuilder` and returns them as an array
/// `[T; N]`.
///
/// If `self.len() < N`, this returns `None`.
pub fn take(&mut self) -> Option<[T; N]> {
if self.len == N {
// SAFETY: Decreasing the value of `self.len` cannot violate the
// safety invariant on `self.arr`.
self.len = 0;
// SAFETY: Since `self.len` is 0, `self.arr` may safely contain
// uninitialized elements.
let arr = mem::replace(&mut self.arr, [(); N].map(|_| MaybeUninit::uninit()));
Some(arr.map(|v| {
// SAFETY: We know that all elements of `arr` are valid because
// we checked that `len == N`.
unsafe { v.assume_init() }
}))
} else {
None
}
}
}
impl<T, const N: usize> AsMut<[T]> for ArrayBuilder<T, N> {
fn as_mut(&mut self) -> &mut [T] {
let valid = &mut self.arr[..self.len];
// SAFETY: By invariant on `self.arr`, the elements of `self.arr` at
// indices `0..self.len` are in a valid state. Since `valid` references
// only these elements, the safety precondition of
// `slice_assume_init_mut` is satisfied.
unsafe { slice_assume_init_mut(valid) }
}
}
impl<T, const N: usize> Drop for ArrayBuilder<T, N> {
// We provide a non-trivial `Drop` impl, because the trivial impl would be a
// no-op; `MaybeUninit<T>` has no innate awareness of its own validity, and
// so it can only forget its contents. By leveraging the safety invariant of
// `self.arr`, we do know which elements of `self.arr` are valid, and can
// selectively run their destructors.
fn drop(&mut self) {
// SAFETY:
// - by invariant on `&mut [T]`, `self.as_mut()` is:
// - valid for reads and writes
// - properly aligned
// - non-null
// - the dropped `T` are valid for dropping; they do not have any
// additional library invariants that we've violated
// - no other pointers to `valid` exist (since we're in the context of
// `drop`)
unsafe { core::ptr::drop_in_place(self.as_mut()) }
}
}
/// Assuming all the elements are initialized, get a mutable slice to them.
///
/// # Safety
///
/// The caller guarantees that the elements `T` referenced by `slice` are in a
/// valid state.
unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] {
// SAFETY: Casting `&mut [MaybeUninit<T>]` to `&mut [T]` is sound, because
// `MaybeUninit<T>` is guaranteed to have the same size, alignment and ABI
// as `T`, and because the caller has guaranteed that `slice` is in the
// valid state.
unsafe { &mut *(slice as *mut [MaybeUninit<T>] as *mut [T]) }
}
/// Equivalent to `it.next_array()`.
pub(crate) fn next_array<I, const N: usize>(it: &mut I) -> Option<[I::Item; N]>
where
I: Iterator,
{
let mut builder = ArrayBuilder::new();
for _ in 0..N {
builder.push(it.next()?);
}
builder.take()
}
#[cfg(test)]
mod test {
use super::ArrayBuilder;
#[test]
fn zero_len_take() {
let mut builder = ArrayBuilder::<(), 0>::new();
let taken = builder.take();
assert_eq!(taken, Some([(); 0]));
}
#[test]
#[should_panic]
fn zero_len_push() {
let mut builder = ArrayBuilder::<(), 0>::new();
builder.push(());
}
#[test]
fn push_4() {
let mut builder = ArrayBuilder::<(), 4>::new();
assert_eq!(builder.take(), None);
builder.push(());
assert_eq!(builder.take(), None);
builder.push(());
assert_eq!(builder.take(), None);
builder.push(());
assert_eq!(builder.take(), None);
builder.push(());
assert_eq!(builder.take(), Some([(); 4]));
}
#[test]
fn tracked_drop() {
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::atomic::{AtomicU16, Ordering};
static DROPPED: AtomicU16 = AtomicU16::new(0);
#[derive(Debug, PartialEq)]
struct TrackedDrop;
impl Drop for TrackedDrop {
fn drop(&mut self) {
DROPPED.fetch_add(1, Ordering::Relaxed);
}
}
{
let builder = ArrayBuilder::<TrackedDrop, 0>::new();
assert_eq!(DROPPED.load(Ordering::Relaxed), 0);
drop(builder);
assert_eq!(DROPPED.load(Ordering::Relaxed), 0);
}
{
let mut builder = ArrayBuilder::<TrackedDrop, 2>::new();
builder.push(TrackedDrop);
assert_eq!(builder.take(), None);
assert_eq!(DROPPED.load(Ordering::Relaxed), 0);
drop(builder);
assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 1);
}
{
let mut builder = ArrayBuilder::<TrackedDrop, 2>::new();
builder.push(TrackedDrop);
builder.push(TrackedDrop);
assert!(matches!(builder.take(), Some(_)));
assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 2);
drop(builder);
assert_eq!(DROPPED.load(Ordering::Relaxed), 0);
}
{
let mut builder = ArrayBuilder::<TrackedDrop, 2>::new();
builder.push(TrackedDrop);
builder.push(TrackedDrop);
assert!(catch_unwind(AssertUnwindSafe(|| {
builder.push(TrackedDrop);
}))
.is_err());
assert_eq!(DROPPED.load(Ordering::Relaxed), 1);
drop(builder);
assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 3);
}
{
let mut builder = ArrayBuilder::<TrackedDrop, 2>::new();
builder.push(TrackedDrop);
builder.push(TrackedDrop);
assert!(catch_unwind(AssertUnwindSafe(|| {
builder.push(TrackedDrop);
}))
.is_err());
assert_eq!(DROPPED.load(Ordering::Relaxed), 1);
assert!(matches!(builder.take(), Some(_)));
assert_eq!(DROPPED.load(Ordering::Relaxed), 3);
builder.push(TrackedDrop);
builder.push(TrackedDrop);
assert!(matches!(builder.take(), Some(_)));
assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 5);
}
}
}

View File

@@ -1,5 +1,5 @@
use std::iter::{Fuse, FusedIterator};
use crate::size_hint;
use std::iter::{Fuse, FusedIterator};
/// An iterator adaptor that pads a sequence to a minimum length by filling
/// missing elements using a closure.
@@ -25,8 +25,9 @@ where
/// Create a new `PadUsing` iterator.
pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
where I: Iterator,
F: FnMut(usize) -> I::Item
where
I: Iterator,
F: FnMut(usize) -> I::Item,
{
PadUsing {
iter: iter.fuse(),
@@ -37,8 +38,9 @@ pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
}
impl<I, F> Iterator for PadUsing<I, F>
where I: Iterator,
F: FnMut(usize) -> I::Item
where
I: Iterator,
F: FnMut(usize) -> I::Item,
{
type Item = I::Item;
@@ -53,7 +55,7 @@ impl<I, F> Iterator for PadUsing<I, F>
} else {
None
}
},
}
e => {
self.pos += 1;
e
@@ -65,11 +67,24 @@ impl<I, F> Iterator for PadUsing<I, F>
let tail = self.min.saturating_sub(self.pos);
size_hint::max(self.iter.size_hint(), (tail, Some(tail)))
}
fn fold<B, G>(self, mut init: B, mut f: G) -> B
where
G: FnMut(B, Self::Item) -> B,
{
let mut pos = self.pos;
init = self.iter.fold(init, |acc, item| {
pos += 1;
f(acc, item)
});
(pos..self.min).map(self.filler).fold(init, f)
}
}
impl<I, F> DoubleEndedIterator for PadUsing<I, F>
where I: DoubleEndedIterator + ExactSizeIterator,
F: FnMut(usize) -> I::Item
where
I: DoubleEndedIterator + ExactSizeIterator,
F: FnMut(usize) -> I::Item,
{
fn next_back(&mut self) -> Option<Self::Item> {
if self.min == 0 {
@@ -82,15 +97,28 @@ impl<I, F> DoubleEndedIterator for PadUsing<I, F>
Some((self.filler)(self.min))
}
}
fn rfold<B, G>(self, mut init: B, mut f: G) -> B
where
G: FnMut(B, Self::Item) -> B,
{
init = (self.iter.len()..self.min)
.map(self.filler)
.rfold(init, &mut f);
self.iter.rfold(init, f)
}
}
impl<I, F> ExactSizeIterator for PadUsing<I, F>
where I: ExactSizeIterator,
F: FnMut(usize) -> I::Item
{}
where
I: ExactSizeIterator,
F: FnMut(usize) -> I::Item,
{
}
impl<I, F> FusedIterator for PadUsing<I, F>
where I: FusedIterator,
F: FnMut(usize) -> I::Item
{}
where
I: FusedIterator,
F: FnMut(usize) -> I::Item,
{
}

View File

@@ -5,6 +5,7 @@ use std::iter::Fuse;
/// See [`peek_nth()`] for more information.
#[derive(Clone, Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct PeekNth<I>
where
I: Iterator,
@@ -34,30 +35,35 @@ impl<I> PeekNth<I>
where
I: Iterator,
{
/// Works exactly like the `peek` method in `std::iter::Peekable`
/// Works exactly like the `peek` method in [`std::iter::Peekable`].
pub fn peek(&mut self) -> Option<&I::Item> {
self.peek_nth(0)
}
/// Works exactly like the `peek_mut` method in [`std::iter::Peekable`].
pub fn peek_mut(&mut self) -> Option<&mut I::Item> {
self.peek_nth_mut(0)
}
/// Returns a reference to the `nth` value without advancing the iterator.
///
/// # Examples
///
/// Basic usage:
///
/// ```rust
/// ```
/// use itertools::peek_nth;
///
/// let xs = vec![1,2,3];
/// let mut iter = peek_nth(xs.iter());
/// let xs = vec![1, 2, 3];
/// let mut iter = peek_nth(xs.into_iter());
///
/// assert_eq!(iter.peek_nth(0), Some(&&1));
/// assert_eq!(iter.next(), Some(&1));
/// assert_eq!(iter.peek_nth(0), Some(&1));
/// assert_eq!(iter.next(), Some(1));
///
/// // The iterator does not advance even if we call `peek_nth` multiple times
/// assert_eq!(iter.peek_nth(0), Some(&&2));
/// assert_eq!(iter.peek_nth(1), Some(&&3));
/// assert_eq!(iter.next(), Some(&2));
/// assert_eq!(iter.peek_nth(0), Some(&2));
/// assert_eq!(iter.peek_nth(1), Some(&3));
/// assert_eq!(iter.next(), Some(2));
///
/// // Calling `peek_nth` past the end of the iterator will return `None`
/// assert_eq!(iter.peek_nth(1), None);
@@ -69,6 +75,68 @@ where
self.buf.get(n)
}
/// Returns a mutable reference to the `nth` value without advancing the iterator.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use itertools::peek_nth;
///
/// let xs = vec![1, 2, 3, 4, 5];
/// let mut iter = peek_nth(xs.into_iter());
///
/// assert_eq!(iter.peek_nth_mut(0), Some(&mut 1));
/// assert_eq!(iter.next(), Some(1));
///
/// // The iterator does not advance even if we call `peek_nth_mut` multiple times
/// assert_eq!(iter.peek_nth_mut(0), Some(&mut 2));
/// assert_eq!(iter.peek_nth_mut(1), Some(&mut 3));
/// assert_eq!(iter.next(), Some(2));
///
/// // Peek into the iterator and set the value behind the mutable reference.
/// if let Some(p) = iter.peek_nth_mut(1) {
/// assert_eq!(*p, 4);
/// *p = 9;
/// }
///
/// // The value we put in reappears as the iterator continues.
/// assert_eq!(iter.next(), Some(3));
/// assert_eq!(iter.next(), Some(9));
///
/// // Calling `peek_nth_mut` past the end of the iterator will return `None`
/// assert_eq!(iter.peek_nth_mut(1), None);
/// ```
pub fn peek_nth_mut(&mut self, n: usize) -> Option<&mut I::Item> {
let unbuffered_items = (n + 1).saturating_sub(self.buf.len());
self.buf.extend(self.iter.by_ref().take(unbuffered_items));
self.buf.get_mut(n)
}
/// Works exactly like the `next_if` method in [`std::iter::Peekable`].
pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
match self.next() {
Some(item) if func(&item) => Some(item),
Some(item) => {
self.buf.push_front(item);
None
}
_ => None,
}
}
/// Works exactly like the `next_if_eq` method in [`std::iter::Peekable`].
pub fn next_if_eq<T>(&mut self, expected: &T) -> Option<I::Item>
where
T: ?Sized,
I::Item: PartialEq<T>,
{
self.next_if(|next| next == expected)
}
}
impl<I> Iterator for PeekNth<I>
@@ -84,6 +152,14 @@ where
fn size_hint(&self) -> (usize, Option<usize>) {
size_hint::add_scalar(self.iter.size_hint(), self.buf.len())
}
fn fold<B, F>(self, mut init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
init = self.buf.into_iter().fold(init, &mut f);
self.iter.fold(init, f)
}
}
impl<I> ExactSizeIterator for PeekNth<I> where I: ExactSizeIterator {}

View File

@@ -1,7 +1,8 @@
use std::iter::Peekable;
use crate::PutBack;
#[cfg(feature = "use_alloc")]
use crate::PutBackN;
use crate::RepeatN;
use std::iter::Peekable;
/// An iterator that allows peeking at an element before deciding to accept it.
///
@@ -10,20 +11,36 @@ use crate::PutBackN;
///
/// This is implemented by peeking adaptors like peekable and put back,
/// but also by a few iterators that can be peeked natively, like the slices
/// by reference iterator (`std::slice::Iter`).
pub trait PeekingNext : Iterator {
/// by reference iterator ([`std::slice::Iter`]).
pub trait PeekingNext: Iterator {
/// Pass a reference to the next iterator element to the closure `accept`;
/// if `accept` returns true, return it as the next element,
/// else None.
/// if `accept` returns `true`, return it as the next element,
/// else `None`.
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where F: FnOnce(&Self::Item) -> bool;
where
Self: Sized,
F: FnOnce(&Self::Item) -> bool;
}
impl<I> PeekingNext for &mut I
where
I: PeekingNext,
{
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where
F: FnOnce(&Self::Item) -> bool,
{
(*self).peeking_next(accept)
}
}
impl<I> PeekingNext for Peekable<I>
where I: Iterator,
where
I: Iterator,
{
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where F: FnOnce(&Self::Item) -> bool
where
F: FnOnce(&Self::Item) -> bool,
{
if let Some(r) = self.peek() {
if !accept(r) {
@@ -35,10 +52,12 @@ impl<I> PeekingNext for Peekable<I>
}
impl<I> PeekingNext for PutBack<I>
where I: Iterator,
where
I: Iterator,
{
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where F: FnOnce(&Self::Item) -> bool
where
F: FnOnce(&Self::Item) -> bool,
{
if let Some(r) = self.next() {
if !accept(&r) {
@@ -54,10 +73,12 @@ impl<I> PeekingNext for PutBack<I>
#[cfg(feature = "use_alloc")]
impl<I> PeekingNext for PutBackN<I>
where I: Iterator,
where
I: Iterator,
{
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where F: FnOnce(&Self::Item) -> bool
where
F: FnOnce(&Self::Item) -> bool,
{
if let Some(r) = self.next() {
if !accept(&r) {
@@ -71,39 +92,51 @@ impl<I> PeekingNext for PutBackN<I>
}
}
impl<T: Clone> PeekingNext for RepeatN<T> {
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where
F: FnOnce(&Self::Item) -> bool,
{
let r = self.elt.as_ref()?;
if !accept(r) {
return None;
}
self.next()
}
}
/// An iterator adaptor that takes items while a closure returns `true`.
///
/// See [`.peeking_take_while()`](crate::Itertools::peeking_take_while)
/// for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct PeekingTakeWhile<'a, I: 'a, F>
where I: Iterator,
pub struct PeekingTakeWhile<'a, I, F>
where
I: Iterator + 'a,
{
iter: &'a mut I,
f: F,
}
impl<'a, I: 'a, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F>
impl<'a, I, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F>
where
I: Iterator + std::fmt::Debug,
I: Iterator + std::fmt::Debug + 'a,
{
debug_fmt_fields!(PeekingTakeWhile, iter);
}
/// Create a `PeekingTakeWhile`
pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F>
where I: Iterator,
where
I: Iterator,
{
PeekingTakeWhile {
iter,
f,
}
PeekingTakeWhile { iter, f }
}
impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F>
where I: PeekingNext,
F: FnMut(&I::Item) -> bool,
impl<I, F> Iterator for PeekingTakeWhile<'_, I, F>
where
I: PeekingNext,
F: FnMut(&I::Item) -> bool,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
@@ -115,6 +148,20 @@ impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F>
}
}
impl<I, F> PeekingNext for PeekingTakeWhile<'_, I, F>
where
I: PeekingNext,
F: FnMut(&I::Item) -> bool,
{
fn peeking_next<G>(&mut self, g: G) -> Option<Self::Item>
where
G: FnOnce(&Self::Item) -> bool,
{
let f = &mut self.f;
self.iter.peeking_next(|r| f(r) && g(r))
}
}
// Some iterators are so lightweight we can simply clone them to save their
// state and use that for peeking.
macro_rules! peeking_next_by_clone {
@@ -151,4 +198,4 @@ peeking_next_by_clone! { ['a, T] alloc::collections::vec_deque::Iter<'a, T> }
// cloning a Rev has no extra overhead; peekable and put backs are never DEI.
peeking_next_by_clone! { [I: Clone + PeekingNext + DoubleEndedIterator]
::std::iter::Rev<I> }
::std::iter::Rev<I> }

View File

@@ -1,8 +1,11 @@
use alloc::boxed::Box;
use alloc::vec::Vec;
use std::fmt;
use std::iter::once;
use std::iter::FusedIterator;
use super::lazy_buffer::LazyBuffer;
use crate::size_hint::{self, SizeHint};
/// An iterator adaptor that iterates through all the `k`-permutations of the
/// elements from an iterator.
@@ -16,262 +19,168 @@ pub struct Permutations<I: Iterator> {
}
impl<I> Clone for Permutations<I>
where I: Clone + Iterator,
I::Item: Clone,
where
I: Clone + Iterator,
I::Item: Clone,
{
clone_fields!(vals, state);
}
#[derive(Clone, Debug)]
enum PermutationState {
StartUnknownLen {
k: usize,
/// No permutation generated yet.
Start { k: usize },
/// Values from the iterator are not fully loaded yet so `n` is still unknown.
Buffered { k: usize, min_n: usize },
/// All values from the iterator are known so `n` is known.
Loaded {
indices: Box<[usize]>,
cycles: Box<[usize]>,
},
OngoingUnknownLen {
k: usize,
min_n: usize,
},
Complete(CompleteState),
Empty,
}
#[derive(Clone, Debug)]
enum CompleteState {
Start {
n: usize,
k: usize,
},
Ongoing {
indices: Vec<usize>,
cycles: Vec<usize>,
}
}
enum CompleteStateRemaining {
Known(usize),
Overflow,
/// No permutation left to generate.
End,
}
impl<I> fmt::Debug for Permutations<I>
where I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
{
debug_fmt_fields!(Permutations, vals, state);
}
pub fn permutations<I: Iterator>(iter: I, k: usize) -> Permutations<I> {
let mut vals = LazyBuffer::new(iter);
if k == 0 {
// Special case, yields single empty vec; `n` is irrelevant
let state = PermutationState::Complete(CompleteState::Start { n: 0, k: 0 });
return Permutations {
vals,
state
};
}
let mut enough_vals = true;
while vals.len() < k {
if !vals.get_next() {
enough_vals = false;
break;
}
}
let state = if enough_vals {
PermutationState::StartUnknownLen { k }
} else {
PermutationState::Empty
};
Permutations {
vals,
state
vals: LazyBuffer::new(iter),
state: PermutationState::Start { k },
}
}
impl<I> Iterator for Permutations<I>
where
I: Iterator,
I::Item: Clone
I::Item: Clone,
{
type Item = Vec<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
self.advance();
let &mut Permutations { ref vals, ref state } = self;
match *state {
PermutationState::StartUnknownLen { .. } => panic!("unexpected iterator state"),
PermutationState::OngoingUnknownLen { k, min_n } => {
let latest_idx = min_n - 1;
let indices = (0..(k - 1)).chain(once(latest_idx));
Some(indices.map(|i| vals[i].clone()).collect())
let Self { vals, state } = self;
match state {
PermutationState::Start { k: 0 } => {
*state = PermutationState::End;
Some(Vec::new())
}
PermutationState::Complete(CompleteState::Ongoing { ref indices, ref cycles }) => {
&mut PermutationState::Start { k } => {
vals.prefill(k);
if vals.len() != k {
*state = PermutationState::End;
return None;
}
*state = PermutationState::Buffered { k, min_n: k };
Some(vals[0..k].to_vec())
}
PermutationState::Buffered { ref k, min_n } => {
if vals.get_next() {
let item = (0..*k - 1)
.chain(once(*min_n))
.map(|i| vals[i].clone())
.collect();
*min_n += 1;
Some(item)
} else {
let n = *min_n;
let prev_iteration_count = n - *k + 1;
let mut indices: Box<[_]> = (0..n).collect();
let mut cycles: Box<[_]> = (n - k..n).rev().collect();
// Advance the state to the correct point.
for _ in 0..prev_iteration_count {
if advance(&mut indices, &mut cycles) {
*state = PermutationState::End;
return None;
}
}
let item = vals.get_at(&indices[0..*k]);
*state = PermutationState::Loaded { indices, cycles };
Some(item)
}
}
PermutationState::Loaded { indices, cycles } => {
if advance(indices, cycles) {
*state = PermutationState::End;
return None;
}
let k = cycles.len();
Some(indices[0..k].iter().map(|&i| vals[i].clone()).collect())
},
PermutationState::Complete(CompleteState::Start { .. }) | PermutationState::Empty => None
Some(vals.get_at(&indices[0..k]))
}
PermutationState::End => None,
}
}
fn count(self) -> usize {
fn from_complete(complete_state: CompleteState) -> usize {
match complete_state.remaining() {
CompleteStateRemaining::Known(count) => count,
CompleteStateRemaining::Overflow => {
panic!("Iterator count greater than usize::MAX");
}
}
}
let Permutations { vals, state } = self;
match state {
PermutationState::StartUnknownLen { k } => {
let n = vals.len() + vals.it.count();
let complete_state = CompleteState::Start { n, k };
from_complete(complete_state)
}
PermutationState::OngoingUnknownLen { k, min_n } => {
let prev_iteration_count = min_n - k + 1;
let n = vals.len() + vals.it.count();
let complete_state = CompleteState::Start { n, k };
from_complete(complete_state) - prev_iteration_count
},
PermutationState::Complete(state) => from_complete(state),
PermutationState::Empty => 0
}
let Self { vals, state } = self;
let n = vals.count();
state.size_hint_for(n).1.unwrap()
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.state {
PermutationState::StartUnknownLen { .. } |
PermutationState::OngoingUnknownLen { .. } => (0, None), // TODO can we improve this lower bound?
PermutationState::Complete(ref state) => match state.remaining() {
CompleteStateRemaining::Known(count) => (count, Some(count)),
CompleteStateRemaining::Overflow => (::std::usize::MAX, None)
}
PermutationState::Empty => (0, Some(0))
}
fn size_hint(&self) -> SizeHint {
let (mut low, mut upp) = self.vals.size_hint();
low = self.state.size_hint_for(low).0;
upp = upp.and_then(|n| self.state.size_hint_for(n).1);
(low, upp)
}
}
impl<I> Permutations<I>
impl<I> FusedIterator for Permutations<I>
where
I: Iterator,
I::Item: Clone
I::Item: Clone,
{
fn advance(&mut self) {
let &mut Permutations { ref mut vals, ref mut state } = self;
}
*state = match *state {
PermutationState::StartUnknownLen { k } => {
PermutationState::OngoingUnknownLen { k, min_n: k }
}
PermutationState::OngoingUnknownLen { k, min_n } => {
if vals.get_next() {
PermutationState::OngoingUnknownLen { k, min_n: min_n + 1 }
} else {
let n = min_n;
let prev_iteration_count = n - k + 1;
let mut complete_state = CompleteState::Start { n, k };
fn advance(indices: &mut [usize], cycles: &mut [usize]) -> bool {
let n = indices.len();
let k = cycles.len();
// NOTE: if `cycles` are only zeros, then we reached the last permutation.
for i in (0..k).rev() {
if cycles[i] == 0 {
cycles[i] = n - i - 1;
indices[i..].rotate_left(1);
} else {
let swap_index = n - cycles[i];
indices.swap(i, swap_index);
cycles[i] -= 1;
return false;
}
}
true
}
// Advance the complete-state iterator to the correct point
for _ in 0..(prev_iteration_count + 1) {
complete_state.advance();
}
PermutationState::Complete(complete_state)
}
}
PermutationState::Complete(ref mut state) => {
state.advance();
return;
}
PermutationState::Empty => { return; }
impl PermutationState {
fn size_hint_for(&self, n: usize) -> SizeHint {
// At the beginning, there are `n!/(n-k)!` items to come.
let at_start = |n, k| {
debug_assert!(n >= k);
let total = (n - k + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i));
(total.unwrap_or(usize::MAX), total)
};
}
}
impl CompleteState {
fn advance(&mut self) {
*self = match *self {
CompleteState::Start { n, k } => {
let indices = (0..n).collect();
let cycles = ((n - k)..n).rev().collect();
CompleteState::Ongoing {
cycles,
indices
}
},
CompleteState::Ongoing { ref mut indices, ref mut cycles } => {
let n = indices.len();
let k = cycles.len();
for i in (0..k).rev() {
if cycles[i] == 0 {
cycles[i] = n - i - 1;
let to_push = indices.remove(i);
indices.push(to_push);
} else {
let swap_index = n - cycles[i];
indices.swap(i, swap_index);
cycles[i] -= 1;
return;
}
}
CompleteState::Start { n, k }
}
}
}
fn remaining(&self) -> CompleteStateRemaining {
use self::CompleteStateRemaining::{Known, Overflow};
match *self {
CompleteState::Start { n, k } => {
if n < k {
return Known(0);
}
let count: Option<usize> = (n - k + 1..n + 1).fold(Some(1), |acc, i| {
acc.and_then(|acc| acc.checked_mul(i))
Self::Start { k } if n < k => (0, Some(0)),
Self::Start { k } => at_start(n, k),
Self::Buffered { k, min_n } => {
// Same as `Start` minus the previously generated items.
size_hint::sub_scalar(at_start(n, k), min_n - k + 1)
}
Self::Loaded {
ref indices,
ref cycles,
} => {
let count = cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| {
acc.checked_mul(indices.len() - i)
.and_then(|count| count.checked_add(c))
});
match count {
Some(count) => Known(count),
None => Overflow
}
}
CompleteState::Ongoing { ref indices, ref cycles } => {
let mut count: usize = 0;
for (i, &c) in cycles.iter().enumerate() {
let radix = indices.len() - i;
let next_count = count.checked_mul(radix)
.and_then(|count| count.checked_add(c));
count = match next_count {
Some(count) => count,
None => { return Overflow; }
};
}
Known(count)
(count.unwrap_or(usize::MAX), count)
}
Self::End => (0, Some(0)),
}
}
}

View File

@@ -1,10 +1,10 @@
use alloc::vec::Vec;
use std::fmt;
use std::iter::FusedIterator;
use std::usize;
use alloc::vec::Vec;
use super::combinations::{Combinations, combinations};
use super::size_hint;
use super::combinations::{combinations, Combinations};
use crate::adaptors::checked_binomial;
use crate::size_hint::{self, SizeHint};
/// An iterator to iterate through the powerset of the elements from an iterator.
///
@@ -13,78 +13,119 @@ use super::size_hint;
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Powerset<I: Iterator> {
combs: Combinations<I>,
// Iterator `position` (equal to count of yielded elements).
pos: usize,
}
impl<I> Clone for Powerset<I>
where I: Clone + Iterator,
I::Item: Clone,
where
I: Clone + Iterator,
I::Item: Clone,
{
clone_fields!(combs, pos);
clone_fields!(combs);
}
impl<I> fmt::Debug for Powerset<I>
where I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
{
debug_fmt_fields!(Powerset, combs, pos);
debug_fmt_fields!(Powerset, combs);
}
/// Create a new `Powerset` from a clonable iterator.
pub fn powerset<I>(src: I) -> Powerset<I>
where I: Iterator,
I::Item: Clone,
where
I: Iterator,
I::Item: Clone,
{
Powerset {
combs: combinations(src, 0),
pos: 0,
}
}
impl<I: Iterator> Powerset<I> {
/// Returns true if `k` has been incremented, false otherwise.
fn increment_k(&mut self) -> bool {
if self.combs.k() < self.combs.n() || self.combs.k() == 0 {
self.combs.reset(self.combs.k() + 1);
true
} else {
false
}
}
}
impl<I> Iterator for Powerset<I>
where
I: Iterator,
I::Item: Clone,
where
I: Iterator,
I::Item: Clone,
{
type Item = Vec<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(elt) = self.combs.next() {
self.pos = self.pos.saturating_add(1);
Some(elt)
} else if self.combs.k() < self.combs.n()
|| self.combs.k() == 0
{
self.combs.reset(self.combs.k() + 1);
self.combs.next().map(|elt| {
self.pos = self.pos.saturating_add(1);
elt
})
} else if self.increment_k() {
self.combs.next()
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
// Total bounds for source iterator.
let src_total = size_hint::add_scalar(self.combs.src().size_hint(), self.combs.n());
// Total bounds for self ( length(powerset(set) == 2 ^ length(set) )
let self_total = size_hint::pow_scalar_base(2, src_total);
if self.pos < usize::MAX {
// Subtract count of elements already yielded from total.
size_hint::sub_scalar(self_total, self.pos)
} else {
// Fallback: self.pos is saturated and no longer reliable.
(0, self_total.1)
fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
loop {
match self.combs.try_nth(n) {
Ok(item) => return Some(item),
Err(steps) => {
if !self.increment_k() {
return None;
}
n -= steps;
}
}
}
}
fn size_hint(&self) -> SizeHint {
let k = self.combs.k();
// Total bounds for source iterator.
let (n_min, n_max) = self.combs.src().size_hint();
let low = remaining_for(n_min, k).unwrap_or(usize::MAX);
let upp = n_max.and_then(|n| remaining_for(n, k));
size_hint::add(self.combs.size_hint(), (low, upp))
}
fn count(self) -> usize {
let k = self.combs.k();
let (n, combs_count) = self.combs.n_and_count();
combs_count + remaining_for(n, k).unwrap()
}
fn fold<B, F>(self, mut init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
let mut it = self.combs;
if it.k() == 0 {
init = it.by_ref().fold(init, &mut f);
it.reset(1);
}
init = it.by_ref().fold(init, &mut f);
// n is now known for sure because k >= 1 and all k-combinations have been generated.
for k in it.k() + 1..=it.n() {
it.reset(k);
init = it.by_ref().fold(init, &mut f);
}
init
}
}
impl<I> FusedIterator for Powerset<I>
where
I: Iterator,
I::Item: Clone,
{}
where
I: Iterator,
I::Item: Clone,
{
}
fn remaining_for(n: usize, k: usize) -> Option<usize> {
(k + 1..=n).try_fold(0usize, |sum, i| sum.checked_add(checked_binomial(n, i)?))
}

View File

@@ -1,3 +1,5 @@
#[cfg(doc)]
use crate::Itertools;
/// An iterator that produces only the `T` values as long as the
/// inner iterator produces `Ok(T)`.
@@ -11,13 +13,10 @@ pub struct ProcessResults<'a, I, E: 'a> {
iter: I,
}
impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
where I: Iterator<Item = Result<T, E>>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
impl<I, E> ProcessResults<'_, I, E> {
#[inline(always)]
fn next_body<T>(&mut self, item: Option<Result<T, E>>) -> Option<T> {
match item {
Some(Ok(x)) => Some(x),
Some(Err(e)) => {
*self.error = Err(e);
@@ -26,6 +25,18 @@ impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
None => None,
}
}
}
impl<I, T, E> Iterator for ProcessResults<'_, I, E>
where
I: Iterator<Item = Result<T, E>>,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
let item = self.iter.next();
self.next_body(item)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.iter.size_hint().1)
@@ -49,49 +60,49 @@ impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
}
}
impl<I, T, E> DoubleEndedIterator for ProcessResults<'_, I, E>
where
I: Iterator<Item = Result<T, E>>,
I: DoubleEndedIterator,
{
fn next_back(&mut self) -> Option<Self::Item> {
let item = self.iter.next_back();
self.next_body(item)
}
fn rfold<B, F>(mut self, init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
let error = self.error;
self.iter
.try_rfold(init, |acc, opt| match opt {
Ok(x) => Ok(f(acc, x)),
Err(e) => {
*error = Err(e);
Err(acc)
}
})
.unwrap_or_else(|e| e)
}
}
/// “Lift” a function of the values of an iterator so that it can process
/// an iterator of `Result` values instead.
///
/// `iterable` is an iterator or iterable with `Result<T, E>` elements, where
/// `T` is the value type and `E` the error type.
///
/// `processor` is a closure that receives an adapted version of the iterable
/// as the only argument — the adapted iterator produces elements of type `T`,
/// as long as the original iterator produces `Ok` values.
///
/// If the original iterable produces an error at any point, the adapted
/// iterator ends and the `process_results` function will return the
/// error iself.
///
/// Otherwise, the return value from the closure is returned wrapped
/// inside `Ok`.
///
/// # Example
///
/// ```
/// use itertools::process_results;
///
/// type R = Result<i32, &'static str>;
///
/// let first_values: Vec<R> = vec![Ok(1), Ok(0), Ok(3)];
/// let second_values: Vec<R> = vec![Ok(2), Ok(1), Err("overflow")];
///
/// // “Lift” the iterator .max() method to work on the values in Results using process_results
///
/// let first_max = process_results(first_values, |iter| iter.max().unwrap_or(0));
/// let second_max = process_results(second_values, |iter| iter.max().unwrap_or(0));
///
/// assert_eq!(first_max, Ok(3));
/// assert!(second_max.is_err());
/// ```
/// [`IntoIterator`] enabled version of [`Itertools::process_results`].
pub fn process_results<I, F, T, E, R>(iterable: I, processor: F) -> Result<R, E>
where I: IntoIterator<Item = Result<T, E>>,
F: FnOnce(ProcessResults<I::IntoIter, E>) -> R
where
I: IntoIterator<Item = Result<T, E>>,
F: FnOnce(ProcessResults<I::IntoIter, E>) -> R,
{
let iter = iterable.into_iter();
let mut error = Ok(());
let result = processor(ProcessResults { error: &mut error, iter });
let result = processor(ProcessResults {
error: &mut error,
iter,
});
error.map(|_| result)
}

View File

@@ -7,6 +7,7 @@ use crate::size_hint;
///
/// Iterator element type is `I::Item`.
#[derive(Debug, Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct PutBackN<I: Iterator> {
top: Vec<I::Item>,
iter: I,
@@ -17,7 +18,8 @@ pub struct PutBackN<I: Iterator> {
///
/// Iterator element type is `I::Item`.
pub fn put_back_n<I>(iterable: I) -> PutBackN<I::IntoIter>
where I: IntoIterator
where
I: IntoIterator,
{
PutBackN {
top: Vec::new(),
@@ -26,7 +28,8 @@ pub fn put_back_n<I>(iterable: I) -> PutBackN<I::IntoIter>
}
impl<I: Iterator> PutBackN<I> {
/// Puts x in front of the iterator.
/// Puts `x` in front of the iterator.
///
/// The values are yielded in order of the most recently put back
/// values first.
///
@@ -57,5 +60,12 @@ impl<I: Iterator> Iterator for PutBackN<I> {
fn size_hint(&self) -> (usize, Option<usize>) {
size_hint::add_scalar(self.iter.size_hint(), self.top.len())
}
}
fn fold<B, F>(self, mut init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
init = self.top.into_iter().rfold(init, &mut f);
self.iter.fold(init, f)
}
}

View File

@@ -1,10 +1,10 @@
use std::iter::{FusedIterator, IntoIterator};
use alloc::rc::Rc;
use std::cell::RefCell;
use std::iter::{FusedIterator, IntoIterator};
/// A wrapper for `Rc<RefCell<I>>`, that implements the `Iterator` trait.
#[derive(Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct RcIter<I> {
/// The boxed iterator.
pub rciter: Rc<RefCell<I>>,
@@ -45,9 +45,12 @@ pub struct RcIter<I> {
/// `.next()`, i.e. if it somehow participates in an “iterator knot”
/// where it is an adaptor of itself.
pub fn rciter<I>(iterable: I) -> RcIter<I::IntoIter>
where I: IntoIterator
where
I: IntoIterator,
{
RcIter { rciter: Rc::new(RefCell::new(iterable.into_iter())) }
RcIter {
rciter: Rc::new(RefCell::new(iterable.into_iter())),
}
}
impl<I> Clone for RcIter<I> {
@@ -55,7 +58,8 @@ impl<I> Clone for RcIter<I> {
}
impl<A, I> Iterator for RcIter<I>
where I: Iterator<Item = A>
where
I: Iterator<Item = A>,
{
type Item = A;
#[inline]
@@ -73,7 +77,8 @@ impl<A, I> Iterator for RcIter<I>
}
impl<I> DoubleEndedIterator for RcIter<I>
where I: DoubleEndedIterator
where
I: DoubleEndedIterator,
{
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
@@ -82,8 +87,9 @@ impl<I> DoubleEndedIterator for RcIter<I>
}
/// Return an iterator from `&RcIter<I>` (by simply cloning it).
impl<'a, I> IntoIterator for &'a RcIter<I>
where I: Iterator
impl<I> IntoIterator for &RcIter<I>
where
I: Iterator,
{
type Item = I::Item;
type IntoIter = RcIter<I>;
@@ -93,7 +99,4 @@ impl<'a, I> IntoIterator for &'a RcIter<I>
}
}
impl<A, I> FusedIterator for RcIter<I>
where I: FusedIterator<Item = A>
{}
impl<A, I> FusedIterator for RcIter<I> where I: FusedIterator<Item = A> {}

View File

@@ -6,23 +6,28 @@ use std::iter::FusedIterator;
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct RepeatN<A> {
elt: Option<A>,
pub(crate) elt: Option<A>,
n: usize,
}
/// Create an iterator that produces `n` repetitions of `element`.
pub fn repeat_n<A>(element: A, n: usize) -> RepeatN<A>
where A: Clone,
where
A: Clone,
{
if n == 0 {
RepeatN { elt: None, n, }
RepeatN { elt: None, n }
} else {
RepeatN { elt: Some(element), n, }
RepeatN {
elt: Some(element),
n,
}
}
}
impl<A> Iterator for RepeatN<A>
where A: Clone
where
A: Clone,
{
type Item = A;
@@ -39,21 +44,40 @@ impl<A> Iterator for RepeatN<A>
fn size_hint(&self) -> (usize, Option<usize>) {
(self.n, Some(self.n))
}
fn fold<B, F>(self, mut init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
match self {
Self { elt: Some(elt), n } => {
debug_assert!(n > 0);
init = (1..n).map(|_| elt.clone()).fold(init, &mut f);
f(init, elt)
}
_ => init,
}
}
}
impl<A> DoubleEndedIterator for RepeatN<A>
where A: Clone
where
A: Clone,
{
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.next()
}
#[inline]
fn rfold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.fold(init, f)
}
}
impl<A> ExactSizeIterator for RepeatN<A>
where A: Clone
{}
impl<A> ExactSizeIterator for RepeatN<A> where A: Clone {}
impl<A> FusedIterator for RepeatN<A>
where A: Clone
{}
impl<A> FusedIterator for RepeatN<A> where A: Clone {}

View File

@@ -1,9 +1,7 @@
//! Arithmetic on `Iterator.size_hint()` values.
//!
use std::usize;
use std::cmp;
use std::u32;
/// `SizeHint` is the return type of `Iterator::size_hint()`.
pub type SizeHint = (usize, Option<usize>);
@@ -31,7 +29,6 @@ pub fn add_scalar(sh: SizeHint, x: usize) -> SizeHint {
/// Subtract `x` correctly from a `SizeHint`.
#[inline]
#[allow(dead_code)]
pub fn sub_scalar(sh: SizeHint, x: usize) -> SizeHint {
let (mut low, mut hi) = sh;
low = low.saturating_sub(x);
@@ -39,22 +36,7 @@ pub fn sub_scalar(sh: SizeHint, x: usize) -> SizeHint {
(low, hi)
}
/// Multiply `SizeHint` correctly
///
/// ```ignore
/// use std::usize;
/// use itertools::size_hint;
///
/// assert_eq!(size_hint::mul((3, Some(4)), (3, Some(4))),
/// (9, Some(16)));
///
/// assert_eq!(size_hint::mul((3, Some(4)), (usize::MAX, None)),
/// (usize::MAX, None));
///
/// assert_eq!(size_hint::mul((3, None), (0, Some(0))),
/// (0, Some(0)));
/// ```
#[inline]
pub fn mul(a: SizeHint, b: SizeHint) -> SizeHint {
let low = a.0.saturating_mul(b.0);
@@ -75,20 +57,6 @@ pub fn mul_scalar(sh: SizeHint, x: usize) -> SizeHint {
(low, hi)
}
/// Raise `base` correctly by a `SizeHint` exponent.
#[inline]
pub fn pow_scalar_base(base: usize, exp: SizeHint) -> SizeHint {
let exp_low = cmp::min(exp.0, u32::MAX as usize) as u32;
let low = base.saturating_pow(exp_low);
let hi = exp.1.and_then(|exp| {
let exp_hi = cmp::min(exp, u32::MAX as usize) as u32;
base.checked_pow(exp_hi)
});
(low, hi)
}
/// Return the maximum
#[inline]
pub fn max(a: SizeHint, b: SizeHint) -> SizeHint {
@@ -117,3 +85,10 @@ pub fn min(a: SizeHint, b: SizeHint) -> SizeHint {
};
(lower, upper)
}
#[test]
fn mul_size_hints() {
assert_eq!(mul((3, Some(4)), (3, Some(4))), (9, Some(16)));
assert_eq!(mul((3, Some(4)), (usize::MAX, None)), (usize::MAX, None));
assert_eq!(mul((3, None), (0, Some(0))), (0, Some(0)));
}

View File

@@ -5,62 +5,6 @@
use std::fmt;
use std::mem;
/// See [`repeat_call`](crate::repeat_call) for more information.
#[derive(Clone)]
#[deprecated(note="Use std repeat_with() instead", since="0.8.0")]
pub struct RepeatCall<F> {
f: F,
}
impl<F> fmt::Debug for RepeatCall<F>
{
debug_fmt_fields!(RepeatCall, );
}
/// An iterator source that produces elements indefinitely by calling
/// a given closure.
///
/// Iterator element type is the return type of the closure.
///
/// ```
/// use itertools::repeat_call;
/// use itertools::Itertools;
/// use std::collections::BinaryHeap;
///
/// let mut heap = BinaryHeap::from(vec![2, 5, 3, 7, 8]);
///
/// // extract each element in sorted order
/// for element in repeat_call(|| heap.pop()).while_some() {
/// print!("{}", element);
/// }
///
/// itertools::assert_equal(
/// repeat_call(|| 1).take(5),
/// vec![1, 1, 1, 1, 1]
/// );
/// ```
#[deprecated(note="Use std repeat_with() instead", since="0.8.0")]
pub fn repeat_call<F, A>(function: F) -> RepeatCall<F>
where F: FnMut() -> A
{
RepeatCall { f: function }
}
impl<A, F> Iterator for RepeatCall<F>
where F: FnMut() -> A
{
type Item = A;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
Some((self.f)())
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::max_value(), None)
}
}
/// Creates a new unfold source with the specified closure as the "iterator
/// function" and an initial state to eventually pass to the closure
///
@@ -97,8 +41,13 @@ impl<A, F> Iterator for RepeatCall<F>
/// vec![1, 1, 2, 3, 5, 8, 13, 21]);
/// assert_eq!(fibonacci.last(), Some(2_971_215_073))
/// ```
#[deprecated(
note = "Use [std::iter::from_fn](https://doc.rust-lang.org/std/iter/fn.from_fn.html) instead",
since = "0.13.0"
)]
pub fn unfold<A, St, F>(initial_state: St, f: F) -> Unfold<St, F>
where F: FnMut(&mut St) -> Option<A>
where
F: FnMut(&mut St) -> Option<A>,
{
Unfold {
f,
@@ -107,7 +56,8 @@ pub fn unfold<A, St, F>(initial_state: St, f: F) -> Unfold<St, F>
}
impl<St, F> fmt::Debug for Unfold<St, F>
where St: fmt::Debug,
where
St: fmt::Debug,
{
debug_fmt_fields!(Unfold, state);
}
@@ -115,6 +65,10 @@ impl<St, F> fmt::Debug for Unfold<St, F>
/// See [`unfold`](crate::unfold) for more information.
#[derive(Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[deprecated(
note = "Use [std::iter::FromFn](https://doc.rust-lang.org/std/iter/struct.FromFn.html) instead",
since = "0.13.0"
)]
pub struct Unfold<St, F> {
f: F,
/// Internal state that will be passed to the closure on the next iteration
@@ -122,7 +76,8 @@ pub struct Unfold<St, F> {
}
impl<A, St, F> Iterator for Unfold<St, F>
where F: FnMut(&mut St) -> Option<A>
where
F: FnMut(&mut St) -> Option<A>,
{
type Item = A;
@@ -144,13 +99,15 @@ pub struct Iterate<St, F> {
}
impl<St, F> fmt::Debug for Iterate<St, F>
where St: fmt::Debug,
where
St: fmt::Debug,
{
debug_fmt_fields!(Iterate, state);
}
impl<St, F> Iterator for Iterate<St, F>
where F: FnMut(&St) -> St
where
F: FnMut(&St) -> St,
{
type Item = St;
@@ -162,7 +119,7 @@ impl<St, F> Iterator for Iterate<St, F>
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::max_value(), None)
(usize::MAX, None)
}
}
@@ -171,10 +128,23 @@ impl<St, F> Iterator for Iterate<St, F>
/// ```
/// use itertools::iterate;
///
/// itertools::assert_equal(iterate(1, |&i| i * 3).take(5), vec![1, 3, 9, 27, 81]);
/// itertools::assert_equal(iterate(1, |i| i % 3 + 1).take(5), vec![1, 2, 3, 1, 2]);
/// ```
///
/// **Panics** if compute the next value does.
///
/// ```should_panic
/// # use itertools::iterate;
/// let mut it = iterate(25u32, |x| x - 10).take_while(|&x| x > 10);
/// assert_eq!(it.next(), Some(25)); // `Iterate` holds 15.
/// assert_eq!(it.next(), Some(15)); // `Iterate` holds 5.
/// it.next(); // `5 - 10` overflows.
/// ```
///
/// You can alternatively use [`core::iter::successors`] as it better describes a finite iterator.
pub fn iterate<St, F>(initial_value: St, f: F) -> Iterate<St, F>
where F: FnMut(&St) -> St
where
F: FnMut(&St) -> St,
{
Iterate {
state: initial_value,

View File

@@ -0,0 +1,96 @@
use core::iter::FusedIterator;
use std::fmt;
/// An iterator adaptor that consumes elements while the given predicate is
/// `true`, including the element for which the predicate first returned
/// `false`.
///
/// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive)
/// for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct TakeWhileInclusive<I, F> {
iter: I,
predicate: F,
done: bool,
}
impl<I, F> TakeWhileInclusive<I, F>
where
I: Iterator,
F: FnMut(&I::Item) -> bool,
{
/// Create a new [`TakeWhileInclusive`] from an iterator and a predicate.
pub(crate) fn new(iter: I, predicate: F) -> Self {
Self {
iter,
predicate,
done: false,
}
}
}
impl<I, F> fmt::Debug for TakeWhileInclusive<I, F>
where
I: Iterator + fmt::Debug,
{
debug_fmt_fields!(TakeWhileInclusive, iter, done);
}
impl<I, F> Iterator for TakeWhileInclusive<I, F>
where
I: Iterator,
F: FnMut(&I::Item) -> bool,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
None
} else {
self.iter.next().map(|item| {
if !(self.predicate)(&item) {
self.done = true;
}
item
})
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.done {
(0, Some(0))
} else {
(0, self.iter.size_hint().1)
}
}
fn fold<B, Fold>(mut self, init: B, mut f: Fold) -> B
where
Fold: FnMut(B, Self::Item) -> B,
{
if self.done {
init
} else {
let predicate = &mut self.predicate;
self.iter
.try_fold(init, |mut acc, item| {
let is_ok = predicate(&item);
acc = f(acc, item);
if is_ok {
Ok(acc)
} else {
Err(acc)
}
})
.unwrap_or_else(|err| err)
}
}
}
impl<I, F> FusedIterator for TakeWhileInclusive<I, F>
where
I: Iterator,
F: FnMut(&I::Item) -> bool,
{
}

View File

@@ -1,8 +1,8 @@
use super::size_hint;
use std::cell::RefCell;
use alloc::collections::VecDeque;
use alloc::rc::Rc;
use std::cell::RefCell;
/// Common buffer object for the two tee halves
#[derive(Debug)]
@@ -19,24 +19,37 @@ struct TeeBuffer<A, I> {
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug)]
pub struct Tee<I>
where I: Iterator
where
I: Iterator,
{
rcbuffer: Rc<RefCell<TeeBuffer<I::Item, I>>>,
id: bool,
}
pub fn new<I>(iter: I) -> (Tee<I>, Tee<I>)
where I: Iterator
where
I: Iterator,
{
let buffer = TeeBuffer{backlog: VecDeque::new(), iter, owner: false};
let t1 = Tee{rcbuffer: Rc::new(RefCell::new(buffer)), id: true};
let t2 = Tee{rcbuffer: t1.rcbuffer.clone(), id: false};
let buffer = TeeBuffer {
backlog: VecDeque::new(),
iter,
owner: false,
};
let t1 = Tee {
rcbuffer: Rc::new(RefCell::new(buffer)),
id: true,
};
let t2 = Tee {
rcbuffer: t1.rcbuffer.clone(),
id: false,
};
(t1, t2)
}
impl<I> Iterator for Tee<I>
where I: Iterator,
I::Item: Clone
where
I: Iterator,
I::Item: Clone,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
@@ -73,6 +86,8 @@ impl<I> Iterator for Tee<I>
}
impl<I> ExactSizeIterator for Tee<I>
where I: ExactSizeIterator,
I::Item: Clone
{}
where
I: ExactSizeIterator,
I::Item: Clone,
{
}

View File

@@ -1,10 +1,10 @@
//! Some iterator that produces tuples
use std::iter::Cycle;
use std::iter::Fuse;
use std::iter::FusedIterator;
use std::iter::Take;
use std::iter::Cycle;
use std::marker::PhantomData;
use crate::size_hint;
// `HomogeneousTuple` is a public facade for `TupleCollect`, allowing
// tuple-related methods to be used by clients in generic contexts, while
@@ -12,9 +12,7 @@ use std::marker::PhantomData;
// See https://github.com/rust-itertools/itertools/issues/387
/// Implemented for homogeneous tuples of size up to 12.
pub trait HomogeneousTuple
: TupleCollect
{}
pub trait HomogeneousTuple: TupleCollect {}
impl<T: TupleCollect> HomogeneousTuple for T {}
@@ -24,25 +22,25 @@ impl<T: TupleCollect> HomogeneousTuple for T {}
/// [`Tuples::into_buffer()`].
#[derive(Clone, Debug)]
pub struct TupleBuffer<T>
where T: HomogeneousTuple
where
T: HomogeneousTuple,
{
cur: usize,
buf: T::Buffer,
}
impl<T> TupleBuffer<T>
where T: HomogeneousTuple
where
T: HomogeneousTuple,
{
fn new(buf: T::Buffer) -> Self {
TupleBuffer {
cur: 0,
buf,
}
Self { cur: 0, buf }
}
}
impl<T> Iterator for TupleBuffer<T>
where T: HomogeneousTuple
where
T: HomogeneousTuple,
{
type Item = T::Item;
@@ -61,18 +59,16 @@ impl<T> Iterator for TupleBuffer<T>
let len = if buffer.is_empty() {
0
} else {
buffer.iter()
.position(|x| x.is_none())
.unwrap_or_else(|| buffer.len())
buffer
.iter()
.position(|x| x.is_none())
.unwrap_or(buffer.len())
};
(len, Some(len))
}
}
impl<T> ExactSizeIterator for TupleBuffer<T>
where T: HomogeneousTuple
{
}
impl<T> ExactSizeIterator for TupleBuffer<T> where T: HomogeneousTuple {}
/// An iterator that groups the items in tuples of a specific size.
///
@@ -80,8 +76,9 @@ impl<T> ExactSizeIterator for TupleBuffer<T>
#[derive(Clone, Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Tuples<I, T>
where I: Iterator<Item = T::Item>,
T: HomogeneousTuple
where
I: Iterator<Item = T::Item>,
T: HomogeneousTuple,
{
iter: Fuse<I>,
buf: T::Buffer,
@@ -89,8 +86,9 @@ pub struct Tuples<I, T>
/// Create a new tuples iterator.
pub fn tuples<I, T>(iter: I) -> Tuples<I, T>
where I: Iterator<Item = T::Item>,
T: HomogeneousTuple
where
I: Iterator<Item = T::Item>,
T: HomogeneousTuple,
{
Tuples {
iter: iter.fuse(),
@@ -99,19 +97,50 @@ pub fn tuples<I, T>(iter: I) -> Tuples<I, T>
}
impl<I, T> Iterator for Tuples<I, T>
where I: Iterator<Item = T::Item>,
T: HomogeneousTuple
where
I: Iterator<Item = T::Item>,
T: HomogeneousTuple,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
T::collect_from_iter(&mut self.iter, &mut self.buf)
}
fn size_hint(&self) -> (usize, Option<usize>) {
// The number of elts we've drawn from the underlying iterator, but have
// not yet produced as a tuple.
let buffered = T::buffer_len(&self.buf);
// To that, we must add the size estimates of the underlying iterator.
let (unbuffered_lo, unbuffered_hi) = self.iter.size_hint();
// The total low estimate is the sum of the already-buffered elements,
// plus the low estimate of remaining unbuffered elements, divided by
// the tuple size.
let total_lo = add_then_div(unbuffered_lo, buffered, T::num_items()).unwrap_or(usize::MAX);
// And likewise for the total high estimate, but using the high estimate
// of the remaining unbuffered elements.
let total_hi = unbuffered_hi.and_then(|hi| add_then_div(hi, buffered, T::num_items()));
(total_lo, total_hi)
}
}
/// `(n + a) / d` avoiding overflow when possible, returns `None` if it overflows.
fn add_then_div(n: usize, a: usize, d: usize) -> Option<usize> {
debug_assert_ne!(d, 0);
(n / d).checked_add(a / d)?.checked_add((n % d + a % d) / d)
}
impl<I, T> ExactSizeIterator for Tuples<I, T>
where
I: ExactSizeIterator<Item = T::Item>,
T: HomogeneousTuple,
{
}
impl<I, T> Tuples<I, T>
where I: Iterator<Item = T::Item>,
T: HomogeneousTuple
where
I: Iterator<Item = T::Item>,
T: HomogeneousTuple,
{
/// Return a buffer with the produced items that was not enough to be grouped in a tuple.
///
@@ -128,7 +157,6 @@ impl<I, T> Tuples<I, T>
}
}
/// An iterator over all contiguous windows that produces tuples of a specific size.
///
/// See [`.tuple_windows()`](crate::Itertools::tuple_windows) for more
@@ -136,125 +164,167 @@ impl<I, T> Tuples<I, T>
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct TupleWindows<I, T>
where I: Iterator<Item = T::Item>,
T: HomogeneousTuple
where
I: Iterator<Item = T::Item>,
T: HomogeneousTuple,
{
iter: I,
last: Option<T>,
}
/// Create a new tuple windows iterator.
pub fn tuple_windows<I, T>(mut iter: I) -> TupleWindows<I, T>
where I: Iterator<Item = T::Item>,
T: HomogeneousTuple,
T::Item: Clone
pub fn tuple_windows<I, T>(iter: I) -> TupleWindows<I, T>
where
I: Iterator<Item = T::Item>,
T: HomogeneousTuple,
T::Item: Clone,
{
use std::iter::once;
let mut last = None;
if T::num_items() != 1 {
// put in a duplicate item in front of the tuple; this simplifies
// .next() function.
if let Some(item) = iter.next() {
let iter = once(item.clone()).chain(once(item)).chain(&mut iter);
last = T::collect_from_iter_no_buf(iter);
}
}
TupleWindows {
iter,
last,
}
TupleWindows { last: None, iter }
}
impl<I, T> Iterator for TupleWindows<I, T>
where I: Iterator<Item = T::Item>,
T: HomogeneousTuple + Clone,
T::Item: Clone
where
I: Iterator<Item = T::Item>,
T: HomogeneousTuple + Clone,
T::Item: Clone,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if T::num_items() == 1 {
return T::collect_from_iter_no_buf(&mut self.iter)
return T::collect_from_iter_no_buf(&mut self.iter);
}
if let Some(ref mut last) = self.last {
if let Some(new) = self.iter.next() {
if let Some(new) = self.iter.next() {
if let Some(ref mut last) = self.last {
last.left_shift_push(new);
return Some(last.clone());
Some(last.clone())
} else {
use std::iter::once;
let iter = once(new).chain(&mut self.iter);
self.last = T::collect_from_iter_no_buf(iter);
self.last.clone()
}
} else {
None
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let mut sh = self.iter.size_hint();
// Adjust the size hint at the beginning
// OR when `num_items == 1` (but it does not change the size hint).
if self.last.is_none() {
sh = size_hint::sub_scalar(sh, T::num_items() - 1);
}
sh
}
}
impl<I, T> FusedIterator for TupleWindows<I, T>
where I: FusedIterator<Item = T::Item>,
T: HomogeneousTuple + Clone,
T::Item: Clone
{}
impl<I, T> ExactSizeIterator for TupleWindows<I, T>
where
I: ExactSizeIterator<Item = T::Item>,
T: HomogeneousTuple + Clone,
T::Item: Clone,
{
}
/// An iterator over all windows,wrapping back to the first elements when the
impl<I, T> FusedIterator for TupleWindows<I, T>
where
I: FusedIterator<Item = T::Item>,
T: HomogeneousTuple + Clone,
T::Item: Clone,
{
}
/// An iterator over all windows, wrapping back to the first elements when the
/// window would otherwise exceed the length of the iterator, producing tuples
/// of a specific size.
///
/// See [`.circular_tuple_windows()`](crate::Itertools::circular_tuple_windows) for more
/// information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug)]
pub struct CircularTupleWindows<I, T: Clone>
where I: Iterator<Item = T::Item> + Clone,
T: TupleCollect + Clone
#[derive(Debug, Clone)]
pub struct CircularTupleWindows<I, T>
where
I: Iterator<Item = T::Item> + Clone,
T: TupleCollect + Clone,
{
iter: Take<TupleWindows<Cycle<I>, T>>,
phantom_data: PhantomData<T>
iter: TupleWindows<Cycle<I>, T>,
len: usize,
}
pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T>
where I: Iterator<Item = T::Item> + Clone + ExactSizeIterator,
T: TupleCollect + Clone,
T::Item: Clone
where
I: Iterator<Item = T::Item> + Clone + ExactSizeIterator,
T: TupleCollect + Clone,
T::Item: Clone,
{
let len = iter.len();
let iter = tuple_windows(iter.cycle()).take(len);
let iter = tuple_windows(iter.cycle());
CircularTupleWindows {
iter,
phantom_data: PhantomData{}
}
CircularTupleWindows { iter, len }
}
impl<I, T> Iterator for CircularTupleWindows<I, T>
where I: Iterator<Item = T::Item> + Clone,
T: TupleCollect + Clone,
T::Item: Clone
where
I: Iterator<Item = T::Item> + Clone,
T: TupleCollect + Clone,
T::Item: Clone,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
if self.len != 0 {
self.len -= 1;
self.iter.next()
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
impl<I, T> ExactSizeIterator for CircularTupleWindows<I, T>
where
I: Iterator<Item = T::Item> + Clone,
T: TupleCollect + Clone,
T::Item: Clone,
{
}
impl<I, T> FusedIterator for CircularTupleWindows<I, T>
where
I: Iterator<Item = T::Item> + Clone,
T: TupleCollect + Clone,
T::Item: Clone,
{
}
pub trait TupleCollect: Sized {
type Item;
type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>;
fn buffer_len(buf: &Self::Buffer) -> usize {
let s = buf.as_ref();
s.iter().position(Option::is_none).unwrap_or(s.len())
}
fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
where I: IntoIterator<Item = Self::Item>;
where
I: IntoIterator<Item = Self::Item>;
fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
where I: IntoIterator<Item = Self::Item>;
where
I: IntoIterator<Item = Self::Item>;
fn num_items() -> usize;
fn left_shift_push(&mut self, item: Self::Item);
}
macro_rules! count_ident{
() => {0};
($i0:ident, $($i:ident,)*) => {1 + count_ident!($($i,)*)};
}
macro_rules! rev_for_each_ident{
($m:ident, ) => {};
($m:ident, $i0:ident, $($i:ident,)*) => {
@@ -269,7 +339,7 @@ macro_rules! impl_tuple_collect {
impl_tuple_collect!($($Y,)*);
impl<A> TupleCollect for ($(ignore_ident!($Y, A),)*) {
type Item = A;
type Buffer = [Option<A>; count_ident!($($Y,)*) - 1];
type Buffer = [Option<A>; count_ident!($($Y)*) - 1];
#[allow(unused_assignments, unused_mut)]
fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
@@ -312,7 +382,7 @@ macro_rules! impl_tuple_collect {
}
fn num_items() -> usize {
count_ident!($($Y,)*)
count_ident!($($Y)*)
}
fn left_shift_push(&mut self, mut item: A) {

View File

@@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::Hash;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::iter::FusedIterator;
/// An iterator adapter to filter out duplicate elements.
@@ -19,17 +19,19 @@ pub struct UniqueBy<I: Iterator, V, F> {
}
impl<I, V, F> fmt::Debug for UniqueBy<I, V, F>
where I: Iterator + fmt::Debug,
V: fmt::Debug + Hash + Eq,
where
I: Iterator + fmt::Debug,
V: fmt::Debug + Hash + Eq,
{
debug_fmt_fields!(UniqueBy, iter, used);
}
/// Create a new `UniqueBy` iterator.
pub fn unique_by<I, V, F>(iter: I, f: F) -> UniqueBy<I, V, F>
where V: Eq + Hash,
F: FnMut(&I::Item) -> V,
I: Iterator,
where
V: Eq + Hash,
F: FnMut(&I::Item) -> V,
I: Iterator,
{
UniqueBy {
iter,
@@ -40,8 +42,9 @@ pub fn unique_by<I, V, F>(iter: I, f: F) -> UniqueBy<I, V, 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
where I: IntoIterator<Item=K>,
K: Hash + Eq,
where
I: IntoIterator<Item = K>,
K: Hash + Eq,
{
let iter = iterable.into_iter();
let current_used = used.len();
@@ -50,20 +53,16 @@ fn count_new_keys<I, K>(mut used: HashMap<K, ()>, iterable: I) -> usize
}
impl<I, V, F> Iterator for UniqueBy<I, V, F>
where I: Iterator,
V: Eq + Hash,
F: FnMut(&I::Item) -> V
where
I: Iterator,
V: Eq + Hash,
F: FnMut(&I::Item) -> V,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
while let Some(v) = self.iter.next() {
let key = (self.f)(&v);
if self.used.insert(key, ()).is_none() {
return Some(v);
}
}
None
let Self { iter, used, f } = self;
iter.find(|v| used.insert(f(v), ()).is_none())
}
#[inline]
@@ -79,42 +78,42 @@ impl<I, V, F> Iterator for UniqueBy<I, V, F>
}
impl<I, V, F> DoubleEndedIterator for UniqueBy<I, V, F>
where I: DoubleEndedIterator,
V: Eq + Hash,
F: FnMut(&I::Item) -> V
where
I: DoubleEndedIterator,
V: Eq + Hash,
F: FnMut(&I::Item) -> V,
{
fn next_back(&mut self) -> Option<Self::Item> {
while let Some(v) = self.iter.next_back() {
let key = (self.f)(&v);
if self.used.insert(key, ()).is_none() {
return Some(v);
}
}
None
let Self { iter, used, f } = self;
iter.rfind(|v| used.insert(f(v), ()).is_none())
}
}
impl<I, V, F> FusedIterator for UniqueBy<I, V, F>
where I: FusedIterator,
V: Eq + Hash,
F: FnMut(&I::Item) -> V
{}
where
I: FusedIterator,
V: Eq + Hash,
F: FnMut(&I::Item) -> V,
{
}
impl<I> Iterator for Unique<I>
where I: Iterator,
I::Item: Eq + Hash + Clone
where
I: Iterator,
I::Item: Eq + Hash + Clone,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
while let Some(v) = self.iter.iter.next() {
if let Entry::Vacant(entry) = self.iter.used.entry(v) {
let UniqueBy { iter, used, .. } = &mut self.iter;
iter.find_map(|v| {
if let Entry::Vacant(entry) = used.entry(v) {
let elt = entry.key().clone();
entry.insert(());
return Some(elt);
}
}
None
None
})
}
#[inline]
@@ -129,51 +128,61 @@ impl<I> Iterator for Unique<I>
}
impl<I> DoubleEndedIterator for Unique<I>
where I: DoubleEndedIterator,
I::Item: Eq + Hash + Clone
where
I: DoubleEndedIterator,
I::Item: Eq + Hash + Clone,
{
fn next_back(&mut self) -> Option<Self::Item> {
while let Some(v) = self.iter.iter.next_back() {
if let Entry::Vacant(entry) = self.iter.used.entry(v) {
let UniqueBy { iter, used, .. } = &mut self.iter;
iter.rev().find_map(|v| {
if let Entry::Vacant(entry) = used.entry(v) {
let elt = entry.key().clone();
entry.insert(());
return Some(elt);
}
}
None
None
})
}
}
impl<I> FusedIterator for Unique<I>
where I: FusedIterator,
I::Item: Eq + Hash + Clone
{}
where
I: FusedIterator,
I::Item: Eq + Hash + Clone,
{
}
/// An iterator adapter to filter out duplicate elements.
///
/// See [`.unique()`](crate::Itertools::unique) for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Unique<I: Iterator> {
pub struct Unique<I>
where
I: Iterator,
I::Item: Eq + Hash + Clone,
{
iter: UniqueBy<I, I::Item, ()>,
}
impl<I> fmt::Debug for Unique<I>
where I: Iterator + fmt::Debug,
I::Item: Hash + Eq + fmt::Debug,
where
I: Iterator + fmt::Debug,
I::Item: Hash + Eq + fmt::Debug + Clone,
{
debug_fmt_fields!(Unique, iter);
}
pub fn unique<I>(iter: I) -> Unique<I>
where I: Iterator,
I::Item: Eq + Hash,
where
I: Iterator,
I::Item: Eq + Hash + Clone,
{
Unique {
iter: UniqueBy {
iter,
used: HashMap::new(),
f: (),
}
},
}
}

View File

@@ -1,6 +1,6 @@
/// Converts an iterator of tuples into a tuple of containers.
///
/// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
/// `multiunzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
/// column.
///
/// This function is, in some sense, the opposite of [`multizip`].

View File

@@ -1,28 +1,40 @@
use std::iter::{Fuse,Peekable, FusedIterator};
use std::fmt;
use std::iter::{Fuse, FusedIterator, Peekable};
/// An iterator adaptor that wraps each element in an [`Position`].
///
/// Iterator element type is `Position<I::Item>`.
/// Iterator element type is `(Position, I::Item)`.
///
/// See [`.with_position()`](crate::Itertools::with_position) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct WithPosition<I>
where I: Iterator,
where
I: Iterator,
{
handled_first: bool,
peekable: Peekable<Fuse<I>>,
}
impl<I> fmt::Debug for WithPosition<I>
where
I: Iterator,
Peekable<Fuse<I>>: fmt::Debug,
{
debug_fmt_fields!(WithPosition, handled_first, peekable);
}
impl<I> Clone for WithPosition<I>
where I: Clone + Iterator,
I::Item: Clone,
where
I: Clone + Iterator,
I::Item: Clone,
{
clone_fields!(handled_first, peekable);
}
/// Create a new `WithPosition` iterator.
pub fn with_position<I>(iter: I) -> WithPosition<I>
where I: Iterator,
where
I: Iterator,
{
WithPosition {
handled_first: false,
@@ -30,36 +42,24 @@ pub fn with_position<I>(iter: I) -> WithPosition<I>
}
}
/// A value yielded by `WithPosition`.
/// The first component of the value yielded by `WithPosition`.
/// Indicates the position of this element in the iterator results.
///
/// See [`.with_position()`](crate::Itertools::with_position) for more information.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Position<T> {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Position {
/// This is the first element.
First(T),
First,
/// This is neither the first nor the last element.
Middle(T),
Middle,
/// This is the last element.
Last(T),
Last,
/// This is the only element.
Only(T),
}
impl<T> Position<T> {
/// Return the inner value.
pub fn into_inner(self) -> T {
match self {
Position::First(x) |
Position::Middle(x) |
Position::Last(x) |
Position::Only(x) => x,
}
}
Only,
}
impl<I: Iterator> Iterator for WithPosition<I> {
type Item = Position<I::Item>;
type Item = (Position, I::Item);
fn next(&mut self) -> Option<Self::Item> {
match self.peekable.next() {
@@ -70,15 +70,15 @@ impl<I: Iterator> Iterator for WithPosition<I> {
// Peek to see if this is also the last item,
// in which case tag it as `Only`.
match self.peekable.peek() {
Some(_) => Some(Position::First(item)),
None => Some(Position::Only(item)),
Some(_) => Some((Position::First, item)),
None => Some((Position::Only, item)),
}
} else {
// Have seen the first item, and there's something left.
// Peek to see if this is the last item.
match self.peekable.peek() {
Some(_) => Some(Position::Middle(item)),
None => Some(Position::Last(item)),
Some(_) => Some((Position::Middle, item)),
None => Some((Position::Last, item)),
}
}
}
@@ -90,11 +90,35 @@ impl<I: Iterator> Iterator for WithPosition<I> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.peekable.size_hint()
}
fn fold<B, F>(mut self, mut init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
if let Some(mut head) = self.peekable.next() {
if !self.handled_first {
// The current head is `First` or `Only`,
// it depends if there is another item or not.
match self.peekable.next() {
Some(second) => {
let first = std::mem::replace(&mut head, second);
init = f(init, (Position::First, first));
}
None => return f(init, (Position::Only, head)),
}
}
// Have seen the first item, and there's something left.
init = self.peekable.fold(init, |acc, mut item| {
std::mem::swap(&mut head, &mut item);
f(acc, (Position::Middle, item))
});
// The "head" is now the last item.
init = f(init, (Position::Last, head));
}
init
}
}
impl<I> ExactSizeIterator for WithPosition<I>
where I: ExactSizeIterator,
{ }
impl<I> ExactSizeIterator for WithPosition<I> where I: ExactSizeIterator {}
impl<I: Iterator> FusedIterator for WithPosition<I>
{}
impl<I: Iterator> FusedIterator for WithPosition<I> {}

View File

@@ -1,6 +1,7 @@
use super::size_hint;
/// An iterator which iterates two other iterators simultaneously
/// and panic if they have different lengths.
///
/// See [`.zip_eq()`](crate::Itertools::zip_eq) for more information.
#[derive(Clone, Debug)]
@@ -10,9 +11,7 @@ pub struct ZipEq<I, J> {
b: J,
}
/// Iterate `i` and `j` in lock step.
///
/// **Panics** if the iterators are not of the same length.
/// Zips two iterators but **panics** if they are not of the same length.
///
/// [`IntoIterator`] enabled version of [`Itertools::zip_eq`](crate::Itertools::zip_eq).
///
@@ -22,11 +21,13 @@ pub struct ZipEq<I, J> {
/// let data = [1, 2, 3, 4, 5];
/// for (a, b) in zip_eq(&data[..data.len() - 1], &data[1..]) {
/// /* loop body */
/// # let _ = (a, b);
/// }
/// ```
pub fn zip_eq<I, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter>
where I: IntoIterator,
J: IntoIterator
where
I: IntoIterator,
J: IntoIterator,
{
ZipEq {
a: i.into_iter(),
@@ -35,8 +36,9 @@ pub fn zip_eq<I, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter>
}
impl<I, J> Iterator for ZipEq<I, J>
where I: Iterator,
J: Iterator
where
I: Iterator,
J: Iterator,
{
type Item = (I::Item, J::Item);
@@ -44,8 +46,9 @@ impl<I, J> Iterator for ZipEq<I, J>
match (self.a.next(), self.b.next()) {
(None, None) => None,
(Some(a), Some(b)) => Some((a, b)),
(None, Some(_)) | (Some(_), None) =>
panic!("itertools: .zip_eq() reached end of one iterator before the other")
(None, Some(_)) | (Some(_), None) => {
panic!("itertools: .zip_eq() reached end of one iterator before the other")
}
}
}
@@ -55,6 +58,8 @@ impl<I, J> Iterator for ZipEq<I, J>
}
impl<I, J> ExactSizeIterator for ZipEq<I, J>
where I: ExactSizeIterator,
J: ExactSizeIterator
{}
where
I: ExactSizeIterator,
J: ExactSizeIterator,
{
}

View File

@@ -1,5 +1,5 @@
use std::cmp::Ordering::{Equal, Greater, Less};
use super::size_hint;
use std::cmp::Ordering::{Equal, Greater, Less};
use std::iter::{Fuse, FusedIterator};
use crate::either_or_both::EitherOrBoth;
@@ -8,6 +8,7 @@ use crate::either_or_both::EitherOrBoth;
// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283
/// An iterator which iterates two other iterators simultaneously
/// and wraps the elements in [`EitherOrBoth`].
///
/// This iterator is *fused*.
///
@@ -21,8 +22,9 @@ pub struct ZipLongest<T, U> {
/// Create a new `ZipLongest` iterator.
pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U>
where T: Iterator,
U: Iterator
where
T: Iterator,
U: Iterator,
{
ZipLongest {
a: a.fuse(),
@@ -31,8 +33,9 @@ pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U>
}
impl<T, U> Iterator for ZipLongest<T, U>
where T: Iterator,
U: Iterator
where
T: Iterator,
U: Iterator,
{
type Item = EitherOrBoth<T::Item, U::Item>;
@@ -50,11 +53,29 @@ impl<T, U> Iterator for ZipLongest<T, U>
fn size_hint(&self) -> (usize, Option<usize>) {
size_hint::max(self.a.size_hint(), self.b.size_hint())
}
#[inline]
fn fold<B, F>(self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
let Self { mut a, mut b } = self;
let res = a.try_fold(init, |init, a| match b.next() {
Some(b) => Ok(f(init, EitherOrBoth::Both(a, b))),
None => Err(f(init, EitherOrBoth::Left(a))),
});
match res {
Ok(acc) => b.map(EitherOrBoth::Right).fold(acc, f),
Err(acc) => a.map(EitherOrBoth::Left).fold(acc, f),
}
}
}
impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
where T: DoubleEndedIterator + ExactSizeIterator,
U: DoubleEndedIterator + ExactSizeIterator
where
T: DoubleEndedIterator + ExactSizeIterator,
U: DoubleEndedIterator + ExactSizeIterator,
{
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
@@ -70,14 +91,49 @@ impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
Less => self.b.next_back().map(EitherOrBoth::Right),
}
}
fn rfold<B, F>(self, mut init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
let Self { mut a, mut b } = self;
let a_len = a.len();
let b_len = b.len();
match a_len.cmp(&b_len) {
Equal => {}
Greater => {
init = a
.by_ref()
.rev()
.take(a_len - b_len)
.map(EitherOrBoth::Left)
.fold(init, &mut f)
}
Less => {
init = b
.by_ref()
.rev()
.take(b_len - a_len)
.map(EitherOrBoth::Right)
.fold(init, &mut f)
}
}
a.rfold(init, |acc, item_a| {
f(acc, EitherOrBoth::Both(item_a, b.next_back().unwrap()))
})
}
}
impl<T, U> ExactSizeIterator for ZipLongest<T, U>
where T: ExactSizeIterator,
U: ExactSizeIterator
{}
where
T: ExactSizeIterator,
U: ExactSizeIterator,
{
}
impl<T, U> FusedIterator for ZipLongest<T, U>
where T: Iterator,
U: Iterator
{}
where
T: Iterator,
U: Iterator,
{
}

View File

@@ -7,7 +7,7 @@ pub struct Zip<T> {
t: T,
}
/// An iterator that generalizes *.zip()* and allows running multiple iterators in lockstep.
/// An iterator that generalizes `.zip()` and allows running multiple iterators in lockstep.
///
/// The iterator `Zip<(I, J, ..., M)>` is formed from a tuple of iterators (or values that
/// implement [`IntoIterator`]) and yields elements
@@ -16,11 +16,11 @@ pub struct Zip<T> {
/// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to `E` are the
/// element types of the subiterator.
///
/// **Note:** The result of this macro is a value of a named type (`Zip<(I, J,
/// **Note:** The result of this function is a value of a named type (`Zip<(I, J,
/// ..)>` of each component iterator `I, J, ...`) if each component iterator is
/// nameable.
///
/// Prefer [`izip!()`] over `multizip` for the performance benefits of using the
/// Prefer [`izip!()`](crate::izip) over `multizip` for the performance benefits of using the
/// standard library `.zip()`. Prefer `multizip` if a nameable type is needed.
///
/// ```
@@ -36,10 +36,9 @@ pub struct Zip<T> {
///
/// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]);
/// ```
/// [`izip!()`]: crate::izip
pub fn multizip<T, U>(t: U) -> Zip<T>
where Zip<T>: From<U>,
Zip<T>: Iterator,
where
Zip<T>: From<U> + Iterator,
{
Zip::from(t)
}
@@ -82,7 +81,7 @@ macro_rules! impl_zip_iter {
fn size_hint(&self) -> (usize, Option<usize>)
{
let sh = (::std::usize::MAX, None);
let sh = (usize::MAX, None);
let ($(ref $B,)*) = self.t;
$(
let sh = size_hint::min($B.size_hint(), sh);

View File

@@ -22,9 +22,14 @@ impl Iterator for PanickingCounter {
}
fn no_collect_test<A, T>(to_adaptor: T)
where A: Iterator, T: Fn(PanickingCounter) -> A
where
A: Iterator,
T: Fn(PanickingCounter) -> A,
{
let counter = PanickingCounter { curr: 0, max: 10_000 };
let counter = PanickingCounter {
curr: 0,
max: 10_000,
};
let adaptor = to_adaptor(counter);
for _ in adaptor.take(5) {}

View File

@@ -0,0 +1,283 @@
#![allow(unstable_name_collisions)]
use itertools::Itertools;
#[derive(Debug, Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
struct Panicking;
impl Iterator for Panicking {
type Item = u8;
fn next(&mut self) -> Option<u8> {
panic!("iterator adaptor is not lazy")
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(0))
}
}
impl ExactSizeIterator for Panicking {}
/// ## Usage example
/// ```compile_fail
/// must_use_tests! {
/// name {
/// Panicking.name(); // Add `let _ =` only if required (encountered error).
/// }
/// // ...
/// }
/// ```
///
/// **TODO:** test missing `must_use` attributes better, maybe with a new lint.
macro_rules! must_use_tests {
($($(#[$attr:meta])* $name:ident $body:block)*) => {
$(
/// `#[deny(unused_must_use)]` should force us to ignore the resulting iterators
/// by adding `let _ = ...;` on every iterator.
/// If it does not, then a `must_use` attribute is missing on the associated struct.
///
/// However, it's only helpful if we don't add `let _ =` before seeing if there is an error or not.
/// And it does not protect us against removed `must_use` attributes.
/// There is no simple way to test this yet.
#[deny(unused_must_use)]
#[test]
$(#[$attr])*
fn $name() $body
)*
};
}
must_use_tests! {
// Itertools trait:
interleave {
let _ = Panicking.interleave(Panicking);
}
interleave_shortest {
let _ = Panicking.interleave_shortest(Panicking);
}
intersperse {
let _ = Panicking.intersperse(0);
}
intersperse_with {
let _ = Panicking.intersperse_with(|| 0);
}
get {
let _ = Panicking.get(1..4);
let _ = Panicking.get(1..=4);
let _ = Panicking.get(1..);
let _ = Panicking.get(..4);
let _ = Panicking.get(..=4);
let _ = Panicking.get(..);
}
zip_longest {
let _ = Panicking.zip_longest(Panicking);
}
zip_eq {
let _ = Panicking.zip_eq(Panicking);
}
batching {
let _ = Panicking.batching(Iterator::next);
}
chunk_by {
// ChunkBy
let _ = Panicking.chunk_by(|x| *x);
// Groups
let _ = Panicking.chunk_by(|x| *x).into_iter();
}
chunks {
// IntoChunks
let _ = Panicking.chunks(1);
let _ = Panicking.chunks(2);
// Chunks
let _ = Panicking.chunks(1).into_iter();
let _ = Panicking.chunks(2).into_iter();
}
tuple_windows {
let _ = Panicking.tuple_windows::<(_,)>();
let _ = Panicking.tuple_windows::<(_, _)>();
let _ = Panicking.tuple_windows::<(_, _, _)>();
}
circular_tuple_windows {
let _ = Panicking.circular_tuple_windows::<(_,)>();
let _ = Panicking.circular_tuple_windows::<(_, _)>();
let _ = Panicking.circular_tuple_windows::<(_, _, _)>();
}
tuples {
let _ = Panicking.tuples::<(_,)>();
let _ = Panicking.tuples::<(_, _)>();
let _ = Panicking.tuples::<(_, _, _)>();
}
tee {
let _ = Panicking.tee();
}
map_into {
let _ = Panicking.map_into::<u16>();
}
map_ok {
let _ = Panicking.map(Ok::<u8, ()>).map_ok(|x| x + 1);
}
filter_ok {
let _ = Panicking.map(Ok::<u8, ()>).filter_ok(|x| x % 2 == 0);
}
filter_map_ok {
let _ = Panicking.map(Ok::<u8, ()>).filter_map_ok(|x| {
if x % 2 == 0 {
Some(x + 1)
} else {
None
}
});
}
flatten_ok {
let _ = Panicking.map(|x| Ok::<_, ()>([x])).flatten_ok();
}
merge {
let _ = Panicking.merge(Panicking);
}
merge_by {
let _ = Panicking.merge_by(Panicking, |_, _| true);
}
merge_join_by {
let _ = Panicking.merge_join_by(Panicking, |_, _| true);
let _ = Panicking.merge_join_by(Panicking, Ord::cmp);
}
#[should_panic]
kmerge {
let _ = Panicking.map(|_| Panicking).kmerge();
}
#[should_panic]
kmerge_by {
let _ = Panicking.map(|_| Panicking).kmerge_by(|_, _| true);
}
cartesian_product {
let _ = Panicking.cartesian_product(Panicking);
}
multi_cartesian_product {
let _ = vec![Panicking, Panicking, Panicking].into_iter().multi_cartesian_product();
}
coalesce {
let _ = Panicking.coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) });
}
dedup {
let _ = Panicking.dedup();
}
dedup_by {
let _ = Panicking.dedup_by(|_, _| true);
}
dedup_with_count {
let _ = Panicking.dedup_with_count();
}
dedup_by_with_count {
let _ = Panicking.dedup_by_with_count(|_, _| true);
}
duplicates {
let _ = Panicking.duplicates();
}
duplicates_by {
let _ = Panicking.duplicates_by(|x| *x);
}
unique {
let _ = Panicking.unique();
}
unique_by {
let _ = Panicking.unique_by(|x| *x);
}
peeking_take_while {
let _ = Panicking.peekable().peeking_take_while(|x| x % 2 == 0);
}
take_while_ref {
let _ = Panicking.take_while_ref(|x| x % 2 == 0);
}
take_while_inclusive {
let _ = Panicking.take_while_inclusive(|x| x % 2 == 0);
}
while_some {
let _ = Panicking.map(Some).while_some();
}
tuple_combinations1 {
let _ = Panicking.tuple_combinations::<(_,)>();
}
#[should_panic]
tuple_combinations2 {
let _ = Panicking.tuple_combinations::<(_, _)>();
}
#[should_panic]
tuple_combinations3 {
let _ = Panicking.tuple_combinations::<(_, _, _)>();
}
combinations {
let _ = Panicking.combinations(0);
let _ = Panicking.combinations(1);
let _ = Panicking.combinations(2);
}
combinations_with_replacement {
let _ = Panicking.combinations_with_replacement(0);
let _ = Panicking.combinations_with_replacement(1);
let _ = Panicking.combinations_with_replacement(2);
}
permutations {
let _ = Panicking.permutations(0);
let _ = Panicking.permutations(1);
let _ = Panicking.permutations(2);
}
powerset {
let _ = Panicking.powerset();
}
pad_using {
let _ = Panicking.pad_using(25, |_| 10);
}
with_position {
let _ = Panicking.with_position();
}
positions {
let _ = Panicking.positions(|v| v % 2 == 0);
}
update {
let _ = Panicking.update(|n| *n += 1);
}
multipeek {
let _ = Panicking.multipeek();
}
// Not iterator themselves but still lazy.
into_grouping_map {
let _ = Panicking.map(|x| (x, x + 1)).into_grouping_map();
}
into_grouping_map_by {
let _ = Panicking.into_grouping_map_by(|x| *x);
}
// Macros:
iproduct {
let _ = itertools::iproduct!(Panicking);
let _ = itertools::iproduct!(Panicking, Panicking);
let _ = itertools::iproduct!(Panicking, Panicking, Panicking);
}
izip {
let _ = itertools::izip!(Panicking);
let _ = itertools::izip!(Panicking, Panicking);
let _ = itertools::izip!(Panicking, Panicking, Panicking);
}
chain {
let _ = itertools::chain!(Panicking);
let _ = itertools::chain!(Panicking, Panicking);
let _ = itertools::chain!(Panicking, Panicking, Panicking);
}
// Free functions:
multizip {
let _ = itertools::multizip((Panicking, Panicking));
}
put_back {
let _ = itertools::put_back(Panicking);
let _ = itertools::put_back(Panicking).with_value(15);
}
peek_nth {
let _ = itertools::peek_nth(Panicking);
}
put_back_n {
let _ = itertools::put_back_n(Panicking);
}
rciter {
let _ = itertools::rciter(Panicking);
}
}

View File

@@ -1,5 +1,11 @@
mod alloc {}
mod core {}
mod either {}
mod std {}
#[test]
fn iproduct_hygiene() {
let _ = itertools::iproduct!();
let _ = itertools::iproduct!(0..6);
let _ = itertools::iproduct!(0..6, 0..9);
let _ = itertools::iproduct!(0..6, 0..9, 0..12);
@@ -11,3 +17,11 @@ fn izip_hygiene() {
let _ = itertools::izip!(0..6, 0..9);
let _ = itertools::izip!(0..6, 0..9, 0..12);
}
#[test]
fn chain_hygiene() {
let _: ::std::iter::Empty<i32> = itertools::chain!();
let _ = itertools::chain!(0..6);
let _ = itertools::chain!(0..6, 0..9);
let _ = itertools::chain!(0..6, 0..9, 0..12);
}

View File

@@ -1,108 +1,101 @@
use itertools::EitherOrBoth;
use itertools::free::merge_join_by;
use itertools::EitherOrBoth;
#[test]
fn empty() {
let left: Vec<u32> = vec![];
let right: Vec<u32> = vec![];
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![];
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
.collect::<Vec<_>>();
let expected_result: Vec<EitherOrBoth<u32>> = vec![];
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
assert_eq!(expected_result, actual_result);
}
#[test]
fn left_only() {
let left: Vec<u32> = vec![1,2,3];
let left: Vec<u32> = vec![1, 2, 3];
let right: Vec<u32> = vec![];
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
let expected_result: Vec<EitherOrBoth<u32>> = vec![
EitherOrBoth::Left(1),
EitherOrBoth::Left(2),
EitherOrBoth::Left(3)
EitherOrBoth::Left(3),
];
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
.collect::<Vec<_>>();
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
assert_eq!(expected_result, actual_result);
}
#[test]
fn right_only() {
let left: Vec<u32> = vec![];
let right: Vec<u32> = vec![1,2,3];
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
let right: Vec<u32> = vec![1, 2, 3];
let expected_result: Vec<EitherOrBoth<u32>> = vec![
EitherOrBoth::Right(1),
EitherOrBoth::Right(2),
EitherOrBoth::Right(3)
EitherOrBoth::Right(3),
];
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
.collect::<Vec<_>>();
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
assert_eq!(expected_result, actual_result);
}
#[test]
fn first_left_then_right() {
let left: Vec<u32> = vec![1,2,3];
let right: Vec<u32> = vec![4,5,6];
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
let left: Vec<u32> = vec![1, 2, 3];
let right: Vec<u32> = vec![4, 5, 6];
let expected_result: Vec<EitherOrBoth<u32>> = vec![
EitherOrBoth::Left(1),
EitherOrBoth::Left(2),
EitherOrBoth::Left(3),
EitherOrBoth::Right(4),
EitherOrBoth::Right(5),
EitherOrBoth::Right(6)
EitherOrBoth::Right(6),
];
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
.collect::<Vec<_>>();
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
assert_eq!(expected_result, actual_result);
}
#[test]
fn first_right_then_left() {
let left: Vec<u32> = vec![4,5,6];
let right: Vec<u32> = vec![1,2,3];
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
let left: Vec<u32> = vec![4, 5, 6];
let right: Vec<u32> = vec![1, 2, 3];
let expected_result: Vec<EitherOrBoth<u32>> = vec![
EitherOrBoth::Right(1),
EitherOrBoth::Right(2),
EitherOrBoth::Right(3),
EitherOrBoth::Left(4),
EitherOrBoth::Left(5),
EitherOrBoth::Left(6)
EitherOrBoth::Left(6),
];
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
.collect::<Vec<_>>();
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
assert_eq!(expected_result, actual_result);
}
#[test]
fn interspersed_left_and_right() {
let left: Vec<u32> = vec![1,3,5];
let right: Vec<u32> = vec![2,4,6];
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
let left: Vec<u32> = vec![1, 3, 5];
let right: Vec<u32> = vec![2, 4, 6];
let expected_result: Vec<EitherOrBoth<u32>> = vec![
EitherOrBoth::Left(1),
EitherOrBoth::Right(2),
EitherOrBoth::Left(3),
EitherOrBoth::Right(4),
EitherOrBoth::Left(5),
EitherOrBoth::Right(6)
EitherOrBoth::Right(6),
];
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
.collect::<Vec<_>>();
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
assert_eq!(expected_result, actual_result);
}
#[test]
fn overlapping_left_and_right() {
let left: Vec<u32> = vec![1,3,4,6];
let right: Vec<u32> = vec![2,3,4,5];
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
let left: Vec<u32> = vec![1, 3, 4, 6];
let right: Vec<u32> = vec![2, 3, 4, 5];
let expected_result: Vec<EitherOrBoth<u32>> = vec![
EitherOrBoth::Left(1),
EitherOrBoth::Right(2),
EitherOrBoth::Both(3, 3),
EitherOrBoth::Both(4, 4),
EitherOrBoth::Right(5),
EitherOrBoth::Left(6)
EitherOrBoth::Left(6),
];
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
.collect::<Vec<_>>();
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
assert_eq!(expected_result, actual_result);
}

View File

@@ -48,3 +48,22 @@ fn peeking_take_while_slice_iter_rev() {
r.peeking_take_while(|_| true).count();
assert_eq!(r.next(), None);
}
#[test]
fn peeking_take_while_nested() {
let mut xs = (0..10).peekable();
let ys: Vec<_> = xs
.peeking_take_while(|x| *x < 6)
.peeking_take_while(|x| *x != 3)
.collect();
assert_eq!(ys, vec![0, 1, 2]);
assert_eq!(xs.next(), Some(3));
let mut xs = (4..10).peekable();
let ys: Vec<_> = xs
.peeking_take_while(|x| *x != 3)
.peeking_take_while(|x| *x < 6)
.collect();
assert_eq!(ys, vec![4, 5]);
assert_eq!(xs.next(), Some(6));
}

View File

@@ -2,35 +2,28 @@
//! and adaptors.
//!
//! In particular we test the tedious size_hint and exact size correctness.
//!
//! **NOTE:** Due to performance limitations, these tests are not run with miri!
//! They cannot be relied upon to discover soundness issues.
#![cfg(not(miri))]
#![allow(deprecated, unstable_name_collisions)]
use itertools::free::{
cloned, enumerate, multipeek, peek_nth, put_back, put_back_n, rciter, zip, zip_eq,
};
use itertools::Itertools;
use itertools::{iproduct, izip, multizip, EitherOrBoth};
use quickcheck as qc;
use std::cmp::{max, min, Ordering};
use std::collections::{HashMap, HashSet};
use std::default::Default;
use std::num::Wrapping;
use std::ops::Range;
use std::cmp::{max, min, Ordering};
use std::collections::{HashMap, HashSet};
use itertools::Itertools;
use itertools::{
multizip,
EitherOrBoth,
iproduct,
izip,
};
use itertools::free::{
cloned,
enumerate,
multipeek,
peek_nth,
put_back,
put_back_n,
rciter,
zip,
zip_eq,
};
use rand::Rng;
use rand::seq::SliceRandom;
use quickcheck::TestResult;
use rand::seq::SliceRandom;
use rand::Rng;
/// Trait for size hint modifier types
trait HintKind: Copy + Send + qc::Arbitrary {
@@ -49,7 +42,7 @@ impl HintKind for Exact {
impl qc::Arbitrary for Exact {
fn arbitrary<G: qc::Gen>(_: &mut G) -> Self {
Exact {}
Self {}
}
}
@@ -66,8 +59,10 @@ struct Inexact {
impl HintKind for Inexact {
fn loosen_bounds(&self, org_hint: (usize, Option<usize>)) -> (usize, Option<usize>) {
let (org_lower, org_upper) = org_hint;
(org_lower.saturating_sub(self.underestimate),
org_upper.and_then(move |x| x.checked_add(self.overestimate)))
(
org_lower.saturating_sub(self.underestimate),
org_upper.and_then(move |x| x.checked_add(self.overestimate)),
)
}
}
@@ -76,27 +71,23 @@ impl qc::Arbitrary for Inexact {
let ue_value = usize::arbitrary(g);
let oe_value = usize::arbitrary(g);
// Compensate for quickcheck using extreme values too rarely
let ue_choices = &[0, ue_value, usize::max_value()];
let oe_choices = &[0, oe_value, usize::max_value()];
Inexact {
let ue_choices = &[0, ue_value, usize::MAX];
let oe_choices = &[0, oe_value, usize::MAX];
Self {
underestimate: *ue_choices.choose(g).unwrap(),
overestimate: *oe_choices.choose(g).unwrap(),
}
}
fn shrink(&self) -> Box<dyn Iterator<Item=Self>> {
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
let underestimate_value = self.underestimate;
let overestimate_value = self.overestimate;
Box::new(
underestimate_value.shrink().flat_map(move |ue_value|
overestimate_value.shrink().map(move |oe_value|
Inexact {
underestimate: ue_value,
overestimate: oe_value,
}
)
)
)
Box::new(underestimate_value.shrink().flat_map(move |ue_value| {
overestimate_value.shrink().map(move |oe_value| Self {
underestimate: ue_value,
overestimate: oe_value,
})
}))
}
}
@@ -116,10 +107,12 @@ struct Iter<T, SK: HintKind = Inexact> {
hint_kind: SK,
}
impl<T, HK> Iter<T, HK> where HK: HintKind
impl<T, HK> Iter<T, HK>
where
HK: HintKind,
{
fn new(it: Range<T>, hint_kind: HK) -> Self {
Iter {
Self {
iterator: it,
fuse_flag: 0,
hint_kind,
@@ -128,64 +121,66 @@ impl<T, HK> Iter<T, HK> where HK: HintKind
}
impl<T, HK> Iterator for Iter<T, HK>
where Range<T>: Iterator,
<Range<T> as Iterator>::Item: Default,
HK: HintKind,
where
Range<T>: Iterator,
<Range<T> as Iterator>::Item: Default,
HK: HintKind,
{
type Item = <Range<T> as Iterator>::Item;
fn next(&mut self) -> Option<Self::Item>
{
fn next(&mut self) -> Option<Self::Item> {
let elt = self.iterator.next();
if elt.is_none() {
self.fuse_flag += 1;
// check fuse flag
if self.fuse_flag == 2 {
return Some(Default::default())
return Some(Default::default());
}
}
elt
}
fn size_hint(&self) -> (usize, Option<usize>)
{
fn size_hint(&self) -> (usize, Option<usize>) {
let org_hint = self.iterator.size_hint();
self.hint_kind.loosen_bounds(org_hint)
}
}
impl<T, HK> DoubleEndedIterator for Iter<T, HK>
where Range<T>: DoubleEndedIterator,
<Range<T> as Iterator>::Item: Default,
HK: HintKind
where
Range<T>: DoubleEndedIterator,
<Range<T> as Iterator>::Item: Default,
HK: HintKind,
{
fn next_back(&mut self) -> Option<Self::Item> { self.iterator.next_back() }
fn next_back(&mut self) -> Option<Self::Item> {
self.iterator.next_back()
}
}
impl<T> ExactSizeIterator for Iter<T, Exact> where Range<T>: ExactSizeIterator,
impl<T> ExactSizeIterator for Iter<T, Exact>
where
Range<T>: ExactSizeIterator,
<Range<T> as Iterator>::Item: Default,
{ }
{
}
impl<T, HK> qc::Arbitrary for Iter<T, HK>
where T: qc::Arbitrary,
HK: HintKind,
where
T: qc::Arbitrary,
HK: HintKind,
{
fn arbitrary<G: qc::Gen>(g: &mut G) -> Self
{
Iter::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g))
fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
Self::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item=Iter<T, HK>>>
{
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
let r = self.iterator.clone();
let hint_kind = self.hint_kind;
Box::new(
r.start.shrink().flat_map(move |a|
r.end.shrink().map(move |b|
Iter::new(a.clone()..b, hint_kind)
)
)
)
Box::new(r.start.shrink().flat_map(move |a| {
r.end
.shrink()
.map(move |b| Self::new(a.clone()..b, hint_kind))
}))
}
}
@@ -201,7 +196,10 @@ struct ShiftRange<HK = Inexact> {
hint_kind: HK,
}
impl<HK> Iterator for ShiftRange<HK> where HK: HintKind {
impl<HK> Iterator for ShiftRange<HK>
where
HK: HintKind,
{
type Item = Iter<i32, HK>;
fn next(&mut self) -> Option<Self::Item> {
@@ -219,10 +217,11 @@ impl<HK> Iterator for ShiftRange<HK> where HK: HintKind {
}
}
impl ExactSizeIterator for ShiftRange<Exact> { }
impl ExactSizeIterator for ShiftRange<Exact> {}
impl<HK> qc::Arbitrary for ShiftRange<HK>
where HK: HintKind
where
HK: HintKind,
{
fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
const MAX_STARTING_RANGE_DIFF: i32 = 32;
@@ -236,7 +235,7 @@ impl<HK> qc::Arbitrary for ShiftRange<HK>
let iter_count = g.gen_range(0, MAX_ITER_COUNT + 1);
let hint_kind = qc::Arbitrary::arbitrary(g);
ShiftRange {
Self {
range_start,
range_end,
start_step,
@@ -250,7 +249,7 @@ impl<HK> qc::Arbitrary for ShiftRange<HK>
fn correct_count<I, F>(get_it: F) -> bool
where
I: Iterator,
F: Fn() -> I
F: Fn() -> I,
{
let mut counts = vec![get_it().count()];
@@ -258,7 +257,6 @@ where
let mut it = get_it();
for _ in 0..(counts.len() - 1) {
#[allow(clippy::manual_assert)]
if it.next().is_none() {
panic!("Iterator shouldn't be finished, may not be deterministic");
}
@@ -276,7 +274,10 @@ where
for (i, returned_count) in counts.into_iter().enumerate() {
let actual_count = total_actual_count - i;
if actual_count != returned_count {
println!("Total iterations: {} True count: {} returned count: {}", i, actual_count, returned_count);
println!(
"Total iterations: {} True count: {} returned count: {}",
i, actual_count, returned_count
);
return false;
}
@@ -299,12 +300,10 @@ fn correct_size_hint<I: Iterator>(mut it: I) -> bool {
// check all the size hints
for &(low, hi) in &hints {
true_count -= 1;
if low > true_count ||
(hi.is_some() && hi.unwrap() < true_count)
{
if low > true_count || (hi.is_some() && hi.unwrap() < true_count) {
println!("True size: {:?}, size hint: {:?}", true_count, (low, hi));
//println!("All hints: {:?}", hints);
return false
return false;
}
}
true
@@ -313,13 +312,19 @@ fn correct_size_hint<I: Iterator>(mut it: I) -> bool {
fn exact_size<I: ExactSizeIterator>(mut it: I) -> bool {
// check every iteration
let (mut low, mut hi) = it.size_hint();
if Some(low) != hi { return false; }
if Some(low) != hi {
return false;
}
while let Some(_) = it.next() {
let (xlow, xhi) = it.size_hint();
if low != xlow + 1 { return false; }
if low != xlow + 1 {
return false;
}
low = xlow;
hi = xhi;
if Some(low) != hi { return false; }
if Some(low) != hi {
return false;
}
}
let (low, hi) = it.size_hint();
low == 0 && hi == Some(0)
@@ -329,13 +334,19 @@ fn exact_size<I: ExactSizeIterator>(mut it: I) -> bool {
fn exact_size_for_this<I: Iterator>(mut it: I) -> bool {
// check every iteration
let (mut low, mut hi) = it.size_hint();
if Some(low) != hi { return false; }
if Some(low) != hi {
return false;
}
while let Some(_) = it.next() {
let (xlow, xhi) = it.size_hint();
if low != xlow + 1 { return false; }
if low != xlow + 1 {
return false;
}
low = xlow;
hi = xhi;
if Some(low) != hi { return false; }
if Some(low) != hi {
return false;
}
}
let (low, hi) = it.size_hint();
low == 0 && hi == Some(0)
@@ -442,43 +453,10 @@ quickcheck! {
assert_eq!(answer.into_iter().last(), a.multi_cartesian_product().last());
}
#[allow(deprecated)]
fn size_step(a: Iter<i16, Exact>, s: usize) -> bool {
let mut s = s;
if s == 0 {
s += 1; // never zero
}
let filt = a.clone().dedup();
correct_size_hint(filt.step(s)) &&
exact_size(a.step(s))
}
#[allow(deprecated)]
fn equal_step(a: Iter<i16>, s: usize) -> bool {
let mut s = s;
if s == 0 {
s += 1; // never zero
}
let mut i = 0;
itertools::equal(a.clone().step(s), a.filter(|_| {
let keep = i % s == 0;
i += 1;
keep
}))
}
#[allow(deprecated)]
fn equal_step_vec(a: Vec<i16>, s: usize) -> bool {
let mut s = s;
if s == 0 {
s += 1; // never zero
}
let mut i = 0;
itertools::equal(a.iter().step(s), a.iter().filter(|_| {
let keep = i % s == 0;
i += 1;
keep
}))
fn correct_empty_multi_product() -> () {
let empty = Vec::<std::vec::IntoIter<i32>>::new().into_iter().multi_cartesian_product();
assert!(correct_size_hint(empty.clone()));
itertools::assert_equal(empty, std::iter::once(Vec::new()))
}
fn size_multipeek(a: Iter<u16, Exact>, s: u8) -> bool {
@@ -596,6 +574,20 @@ quickcheck! {
let b = &b[..len];
itertools::equal(zip_eq(a, b), zip(a, b))
}
#[should_panic]
fn zip_eq_panics(a: Vec<u8>, b: Vec<u8>) -> TestResult {
if a.len() == b.len() { return TestResult::discard(); }
zip_eq(a.iter(), b.iter()).for_each(|_| {});
TestResult::passed() // won't come here
}
fn equal_positions(a: Vec<i32>) -> bool {
let with_pos = a.iter().positions(|v| v % 2 == 0);
let without = a.iter().enumerate().filter(|(_, v)| *v % 2 == 0).map(|(i, _)| i);
itertools::equal(with_pos.clone(), without.clone())
&& itertools::equal(with_pos.rev(), without.rev())
}
fn size_zip_longest(a: Iter<i16, Exact>, b: Iter<i16, Exact>) -> bool {
let filt = a.clone().dedup();
let filt2 = b.clone().dedup();
@@ -750,6 +742,56 @@ quickcheck! {
}
}
quickcheck! {
fn correct_peek_nth(mut a: Vec<u16>) -> () {
let mut it = peek_nth(a.clone());
for start_pos in 0..a.len() + 2 {
for real_idx in start_pos..a.len() + 2 {
let peek_idx = real_idx - start_pos;
assert_eq!(it.peek_nth(peek_idx), a.get(real_idx));
assert_eq!(it.peek_nth_mut(peek_idx), a.get_mut(real_idx));
}
assert_eq!(it.next(), a.get(start_pos).copied());
}
}
fn peek_nth_mut_replace(a: Vec<u16>, b: Vec<u16>) -> () {
let mut it = peek_nth(a.iter());
for (i, m) in b.iter().enumerate().take(a.len().min(b.len())) {
*it.peek_nth_mut(i).unwrap() = m;
}
for (i, m) in a.iter().enumerate() {
assert_eq!(it.next().unwrap(), b.get(i).unwrap_or(m));
}
assert_eq!(it.next(), None);
assert_eq!(it.next(), None);
}
fn peek_nth_next_if(a: Vec<u8>) -> () {
let mut it = peek_nth(a.clone());
for (idx, mut value) in a.iter().copied().enumerate() {
let should_be_none = it.next_if(|x| x != &value);
assert_eq!(should_be_none, None);
if value % 5 == 0 {
// Sometimes, peek up to 3 further.
let n = value as usize % 3;
let nth = it.peek_nth(n);
assert_eq!(nth, a.get(idx + n));
} else if value % 5 == 1 {
// Sometimes, peek next element mutably.
if let Some(v) = it.peek_mut() {
*v = v.wrapping_sub(1);
let should_be_none = it.next_if_eq(&value);
assert_eq!(should_be_none, None);
value = value.wrapping_sub(1);
}
}
let eq = it.next_if_eq(&value);
assert_eq!(eq, Some(value));
}
}
}
quickcheck! {
fn dedup_via_coalesce(a: Vec<i32>) -> bool {
let mut b = a.clone();
@@ -811,9 +853,8 @@ quickcheck! {
quickcheck! {
fn size_put_back(a: Vec<u8>, x: Option<u8>) -> bool {
let mut it = put_back(a.into_iter());
match x {
Some(t) => it.put_back(t),
None => {}
if let Some(t) = x {
it.put_back(t);
}
correct_size_hint(it)
}
@@ -829,6 +870,31 @@ quickcheck! {
}
}
quickcheck! {
fn merge_join_by_ordering_vs_bool(a: Vec<u8>, b: Vec<u8>) -> bool {
use either::Either;
use itertools::free::merge_join_by;
let mut has_equal = false;
let it_ord = merge_join_by(a.clone(), b.clone(), Ord::cmp).flat_map(|v| match v {
EitherOrBoth::Both(l, r) => {
has_equal = true;
vec![Either::Left(l), Either::Right(r)]
}
EitherOrBoth::Left(l) => vec![Either::Left(l)],
EitherOrBoth::Right(r) => vec![Either::Right(r)],
});
let it_bool = merge_join_by(a, b, PartialOrd::le);
itertools::equal(it_ord, it_bool) || has_equal
}
fn merge_join_by_bool_unwrapped_is_merge_by(a: Vec<u8>, b: Vec<u8>) -> bool {
use either::Either;
use itertools::free::merge_join_by;
let it = a.clone().into_iter().merge_by(b.clone(), PartialOrd::ge);
let it_join = merge_join_by(a, b, PartialOrd::ge).map(Either::into_inner);
itertools::equal(it, it_join)
}
}
quickcheck! {
fn size_tee(a: Vec<u8>) -> bool {
let (mut t1, mut t2) = a.iter().tee();
@@ -870,8 +936,31 @@ quickcheck! {
}
quickcheck! {
fn size_combinations(it: Iter<i16>) -> bool {
correct_size_hint(it.tuple_combinations::<(_, _)>())
fn size_combinations(a: Iter<i16>) -> bool {
let it = a.clone().tuple_combinations::<(_, _)>();
correct_size_hint(it.clone()) && it.count() == binomial(a.count(), 2)
}
fn exact_size_combinations_1(a: Vec<u8>) -> bool {
let it = a.iter().tuple_combinations::<(_,)>();
exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 1)
}
fn exact_size_combinations_2(a: Vec<u8>) -> bool {
let it = a.iter().tuple_combinations::<(_, _)>();
exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 2)
}
fn exact_size_combinations_3(mut a: Vec<u8>) -> bool {
a.truncate(15);
let it = a.iter().tuple_combinations::<(_, _, _)>();
exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 3)
}
}
fn binomial(n: usize, k: usize) -> usize {
if k > n {
0
} else {
(n - k + 1..=n).product::<usize>() / (1..=k).product::<usize>()
}
}
@@ -887,7 +976,7 @@ quickcheck! {
}
}
}
cmb.next() == None
cmb.next().is_none()
}
}
@@ -936,63 +1025,74 @@ quickcheck! {
}
quickcheck! {
fn fuzz_group_by_lazy_1(it: Iter<u8>) -> bool {
fn fuzz_chunk_by_lazy_1(it: Iter<u8>) -> bool {
let jt = it.clone();
let groups = it.group_by(|k| *k);
itertools::equal(jt, groups.into_iter().flat_map(|(_, x)| x))
let chunks = it.chunk_by(|k| *k);
itertools::equal(jt, chunks.into_iter().flat_map(|(_, x)| x))
}
}
quickcheck! {
fn fuzz_group_by_lazy_2(data: Vec<u8>) -> bool {
let groups = data.iter().group_by(|k| *k / 10);
let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
fn fuzz_chunk_by_lazy_2(data: Vec<u8>) -> bool {
let chunks = data.iter().chunk_by(|k| *k / 10);
let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
res
}
}
quickcheck! {
fn fuzz_group_by_lazy_3(data: Vec<u8>) -> bool {
let grouper = data.iter().group_by(|k| *k / 10);
let groups = grouper.into_iter().collect_vec();
let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
fn fuzz_chunk_by_lazy_3(data: Vec<u8>) -> bool {
let grouper = data.iter().chunk_by(|k| *k / 10);
let chunks = grouper.into_iter().collect_vec();
let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
res
}
}
quickcheck! {
fn fuzz_group_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
let grouper = data.iter().group_by(|k| *k / 3);
let mut groups1 = grouper.into_iter();
let mut groups2 = grouper.into_iter();
fn fuzz_chunk_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
let grouper = data.iter().chunk_by(|k| *k / 3);
let mut chunks1 = grouper.into_iter();
let mut chunks2 = grouper.into_iter();
let mut elts = Vec::<&u8>::new();
let mut old_groups = Vec::new();
let mut old_chunks = Vec::new();
let tup1 = |(_, b)| b;
for &(ord, consume_now) in &order {
let iter = &mut [&mut groups1, &mut groups2][ord as usize];
let iter = &mut [&mut chunks1, &mut chunks2][ord as usize];
match iter.next() {
Some((_, gr)) => if consume_now {
for og in old_groups.drain(..) {
for og in old_chunks.drain(..) {
elts.extend(og);
}
elts.extend(gr);
} else {
old_groups.push(gr);
old_chunks.push(gr);
},
None => break,
}
}
for og in old_groups.drain(..) {
for og in old_chunks.drain(..) {
elts.extend(og);
}
for gr in groups1.map(&tup1) { elts.extend(gr); }
for gr in groups2.map(&tup1) { elts.extend(gr); }
for gr in chunks1.map(&tup1) { elts.extend(gr); }
for gr in chunks2.map(&tup1) { elts.extend(gr); }
itertools::assert_equal(&data, elts);
true
}
}
quickcheck! {
fn chunk_clone_equal(a: Vec<u8>, size: u8) -> () {
let mut size = size;
if size == 0 {
size += 1;
}
let it = a.chunks(size as usize);
itertools::assert_equal(it.clone(), it);
}
}
quickcheck! {
fn equal_chunks_lazy(a: Vec<u8>, size: u8) -> bool {
let mut size = size;
@@ -1010,7 +1110,75 @@ quickcheck! {
}
}
// tuple iterators
quickcheck! {
fn equal_circular_tuple_windows_1(a: Vec<u8>) -> bool {
let x = a.iter().map(|e| (e,) );
let y = a.iter().circular_tuple_windows::<(_,)>();
itertools::assert_equal(x,y);
true
}
fn equal_circular_tuple_windows_2(a: Vec<u8>) -> bool {
let x = (0..a.len()).map(|start_idx| (
&a[start_idx],
&a[(start_idx + 1) % a.len()],
));
let y = a.iter().circular_tuple_windows::<(_, _)>();
itertools::assert_equal(x,y);
true
}
fn equal_circular_tuple_windows_3(a: Vec<u8>) -> bool {
let x = (0..a.len()).map(|start_idx| (
&a[start_idx],
&a[(start_idx + 1) % a.len()],
&a[(start_idx + 2) % a.len()],
));
let y = a.iter().circular_tuple_windows::<(_, _, _)>();
itertools::assert_equal(x,y);
true
}
fn equal_circular_tuple_windows_4(a: Vec<u8>) -> bool {
let x = (0..a.len()).map(|start_idx| (
&a[start_idx],
&a[(start_idx + 1) % a.len()],
&a[(start_idx + 2) % a.len()],
&a[(start_idx + 3) % a.len()],
));
let y = a.iter().circular_tuple_windows::<(_, _, _, _)>();
itertools::assert_equal(x,y);
true
}
fn equal_cloned_circular_tuple_windows(a: Vec<u8>) -> bool {
let x = a.iter().circular_tuple_windows::<(_, _, _, _)>();
let y = x.clone();
itertools::assert_equal(x,y);
true
}
fn equal_cloned_circular_tuple_windows_noninitial(a: Vec<u8>) -> bool {
let mut x = a.iter().circular_tuple_windows::<(_, _, _, _)>();
let _ = x.next();
let y = x.clone();
itertools::assert_equal(x,y);
true
}
fn equal_cloned_circular_tuple_windows_complete(a: Vec<u8>) -> bool {
let mut x = a.iter().circular_tuple_windows::<(_, _, _, _)>();
for _ in x.by_ref() {}
let y = x.clone();
itertools::assert_equal(x,y);
true
}
fn circular_tuple_windows_exact_size(a: Vec<u8>) -> bool {
exact_size(a.iter().circular_tuple_windows::<(_, _, _, _)>())
}
fn equal_tuple_windows_1(a: Vec<u8>) -> bool {
let x = a.windows(1).map(|s| (&s[0], ));
let y = a.iter().tuple_windows::<(_,)>();
@@ -1035,6 +1203,14 @@ quickcheck! {
itertools::equal(x, y)
}
fn tuple_windows_exact_size_1(a: Vec<u8>) -> bool {
exact_size(a.iter().tuple_windows::<(_,)>())
}
fn tuple_windows_exact_size_4(a: Vec<u8>) -> bool {
exact_size(a.iter().tuple_windows::<(_, _, _, _)>())
}
fn equal_tuples_1(a: Vec<u8>) -> bool {
let x = a.chunks(1).map(|s| (&s[0], ));
let y = a.iter().tuples::<(_,)>();
@@ -1066,6 +1242,18 @@ quickcheck! {
assert_eq!(buffer.len(), a.len() % 4);
exact_size(buffer)
}
fn tuples_size_hint_inexact(a: Iter<u8>) -> bool {
correct_size_hint(a.clone().tuples::<(_,)>())
&& correct_size_hint(a.clone().tuples::<(_, _)>())
&& correct_size_hint(a.tuples::<(_, _, _, _)>())
}
fn tuples_size_hint_exact(a: Iter<u8, Exact>) -> bool {
exact_size(a.clone().tuples::<(_,)>())
&& exact_size(a.clone().tuples::<(_, _)>())
&& exact_size(a.tuples::<(_, _, _, _)>())
}
}
// with_position
@@ -1097,14 +1285,14 @@ quickcheck! {
#[derive(Clone, Debug, PartialEq, Eq)]
struct Val(u32, u32);
impl PartialOrd<Val> for Val {
fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
impl PartialOrd<Self> for Val {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Val {
fn cmp(&self, other: &Val) -> Ordering {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
@@ -1112,10 +1300,10 @@ impl Ord for Val {
impl qc::Arbitrary for Val {
fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
let (x, y) = <(u32, u32)>::arbitrary(g);
Val(x, y)
Self(x, y)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new((self.0, self.1).shrink().map(|(x, y)| Val(x, y)))
Box::new((self.0, self.1).shrink().map(|(x, y)| Self(x, y)))
}
}
@@ -1157,13 +1345,12 @@ quickcheck! {
}
quickcheck! {
#[allow(deprecated)]
fn tree_fold1_f64(mut a: Vec<f64>) -> TestResult {
fn tree_reduce_f64(mut a: Vec<f64>) -> TestResult {
fn collapse_adjacent<F>(x: Vec<f64>, mut f: F) -> Vec<f64>
where F: FnMut(f64, f64) -> f64
{
let mut out = Vec::new();
for i in (0..x.len()).step(2) {
for i in (0..x.len()).step_by(2) {
if i == x.len()-1 {
out.push(x[i])
} else {
@@ -1177,7 +1364,7 @@ quickcheck! {
return TestResult::discard();
}
let actual = a.iter().cloned().tree_fold1(f64::atan2);
let actual = a.iter().cloned().tree_reduce(f64::atan2);
while a.len() > 1 {
a = collapse_adjacent(a, f64::atan2);
@@ -1202,7 +1389,7 @@ quickcheck! {
fn at_most_one_i32(a: Vec<i32>) -> TestResult {
let ret = a.iter().cloned().at_most_one();
match a.len() {
0 => TestResult::from_bool(ret.unwrap() == None),
0 => TestResult::from_bool(ret.unwrap().is_none()),
1 => TestResult::from_bool(ret.unwrap() == Some(a[0])),
_ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())),
}
@@ -1267,6 +1454,35 @@ quickcheck! {
}
}
fn correct_grouping_map_by_fold_with_modulo_key(a: Vec<u8>, modulo: u8) -> () {
#[derive(Debug, Default, PartialEq)]
struct Accumulator {
acc: u64,
}
let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.fold_with(|_key, _val| Default::default(), |Accumulator { acc }, &key, val| {
assert!(val % modulo == key);
let acc = acc + val;
Accumulator { acc }
});
let group_map_lookup = a.iter()
.map(|&b| b as u64)
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().sum())).map(|(key, acc)| (key,Accumulator { acc }))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
for (&key, &Accumulator { acc: sum }) in lookup.iter() {
assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::<u64>());
}
}
fn correct_grouping_map_by_fold_modulo_key(a: Vec<u8>, modulo: u8) -> () {
let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
@@ -1290,22 +1506,21 @@ quickcheck! {
}
}
fn correct_grouping_map_by_fold_first_modulo_key(a: Vec<u8>, modulo: u8) -> () {
fn correct_grouping_map_by_reduce_modulo_key(a: Vec<u8>, modulo: u8) -> () {
let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
.into_grouping_map_by(|i| i % modulo)
.fold_first(|acc, &key, val| {
.reduce(|acc, &key, val| {
assert!(val % modulo == key);
acc + val
});
// TODO: Swap `fold1` with stdlib's `fold_first` when it's stabilized
let group_map_lookup = a.iter()
.map(|&b| b as u64)
.map(|i| (i % modulo, i))
.into_group_map()
.into_iter()
.map(|(key, vals)| (key, vals.into_iter().fold1(|acc, val| acc + val).unwrap()))
.map(|(key, vals)| (key, vals.into_iter().reduce(|acc, val| acc + val).unwrap()))
.collect::<HashMap<_,_>>();
assert_eq!(lookup, group_map_lookup);
@@ -1594,12 +1809,10 @@ quickcheck! {
}
}
fn is_fused<I: Iterator>(mut it: I) -> bool
{
fn is_fused<I: Iterator>(mut it: I) -> bool {
for _ in it.by_ref() {}
for _ in 0..10{
if it.next().is_some(){
for _ in 0..10 {
if it.next().is_some() {
return false;
}
}
@@ -1746,4 +1959,11 @@ quickcheck! {
result_set.is_empty()
}
}
fn tail(v: Vec<i32>, n: u8) -> bool {
let n = n as usize;
let result = &v[v.len().saturating_sub(n)..];
itertools::equal(v.iter().tail(n), result)
&& itertools::equal(v.iter().filter(|_| true).tail(n), result)
}
}

View File

@@ -1,8 +1,20 @@
//! Test specializations of methods with default impls match the behavior of the
//! default impls.
//!
//! **NOTE:** Due to performance limitations, these tests are not run with miri!
//! They cannot be relied upon to discover soundness issues.
#![cfg(not(miri))]
#![allow(unstable_name_collisions)]
use itertools::Itertools;
use quickcheck::Arbitrary;
use quickcheck::{quickcheck, TestResult};
use rand::Rng;
use std::fmt::Debug;
use quickcheck::quickcheck;
struct Unspecialized<I>(I);
impl<I> Iterator for Unspecialized<I>
where
I: Iterator,
@@ -15,30 +27,41 @@ where
}
}
macro_rules! check_specialized {
($src:expr, |$it:pat| $closure:expr) => {
let $it = $src.clone();
let v1 = $closure;
let $it = Unspecialized($src.clone());
let v2 = $closure;
assert_eq!(v1, v2);
impl<I> DoubleEndedIterator for Unspecialized<I>
where
I: DoubleEndedIterator,
{
#[inline(always)]
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
}
fn test_specializations<IterItem, Iter>(
it: &Iter,
) where
IterItem: Eq + Debug + Clone,
Iter: Iterator<Item = IterItem> + Clone,
fn test_specializations<I>(it: &I)
where
I::Item: Eq + Debug + Clone,
I: Iterator + Clone,
{
macro_rules! check_specialized {
($src:expr, |$it:pat| $closure:expr) => {
// Many iterators special-case the first elements, so we test specializations for iterators that have already been advanced.
let mut src = $src.clone();
for _ in 0..5 {
let $it = src.clone();
let v1 = $closure;
let $it = Unspecialized(src.clone());
let v2 = $closure;
assert_eq!(v1, v2);
src.next();
}
}
}
check_specialized!(it, |i| i.count());
check_specialized!(it, |i| i.last());
check_specialized!(it, |i| i.collect::<Vec<_>>());
check_specialized!(it, |i| {
let mut parameters_from_fold = vec![];
let fold_result = i.fold(vec![], |mut acc, v: IterItem| {
let fold_result = i.fold(vec![], |mut acc, v: I::Item| {
parameters_from_fold.push((acc.clone(), v.clone()));
acc.push(v);
acc
@@ -50,7 +73,7 @@ fn test_specializations<IterItem, Iter>(
let first = i.next();
let all_result = i.all(|x| {
parameters_from_all.push(x.clone());
Some(x)==first
Some(x) == first
});
(parameters_from_all, all_result)
});
@@ -72,10 +95,270 @@ fn test_specializations<IterItem, Iter>(
}
}
fn test_double_ended_specializations<I>(it: &I)
where
I::Item: Eq + Debug + Clone,
I: DoubleEndedIterator + Clone,
{
macro_rules! check_specialized {
($src:expr, |$it:pat| $closure:expr) => {
// Many iterators special-case the first elements, so we test specializations for iterators that have already been advanced.
let mut src = $src.clone();
for step in 0..8 {
let $it = src.clone();
let v1 = $closure;
let $it = Unspecialized(src.clone());
let v2 = $closure;
assert_eq!(v1, v2);
if step % 2 == 0 {
src.next();
} else {
src.next_back();
}
}
}
}
check_specialized!(it, |i| {
let mut parameters_from_rfold = vec![];
let rfold_result = i.rfold(vec![], |mut acc, v: I::Item| {
parameters_from_rfold.push((acc.clone(), v.clone()));
acc.push(v);
acc
});
(parameters_from_rfold, rfold_result)
});
let size = it.clone().count();
for n in 0..size + 2 {
check_specialized!(it, |mut i| i.nth_back(n));
}
}
quickcheck! {
fn interleave(v: Vec<u8>, w: Vec<u8>) -> () {
test_specializations(&v.iter().interleave(w.iter()));
}
fn interleave_shortest(v: Vec<u8>, w: Vec<u8>) -> () {
test_specializations(&v.iter().interleave_shortest(w.iter()));
}
fn batching(v: Vec<u8>) -> () {
test_specializations(&v.iter().batching(Iterator::next));
}
fn tuple_windows(v: Vec<u8>) -> () {
test_specializations(&v.iter().tuple_windows::<(_,)>());
test_specializations(&v.iter().tuple_windows::<(_, _)>());
test_specializations(&v.iter().tuple_windows::<(_, _, _)>());
}
fn circular_tuple_windows(v: Vec<u8>) -> () {
test_specializations(&v.iter().circular_tuple_windows::<(_,)>());
test_specializations(&v.iter().circular_tuple_windows::<(_, _)>());
test_specializations(&v.iter().circular_tuple_windows::<(_, _, _)>());
}
fn tuples(v: Vec<u8>) -> () {
test_specializations(&v.iter().tuples::<(_,)>());
test_specializations(&v.iter().tuples::<(_, _)>());
test_specializations(&v.iter().tuples::<(_, _, _)>());
}
fn cartesian_product(a: Vec<u8>, b: Vec<u8>) -> TestResult {
if a.len() * b.len() > 100 {
return TestResult::discard();
}
test_specializations(&a.iter().cartesian_product(&b));
TestResult::passed()
}
fn multi_cartesian_product(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult {
if a.len() * b.len() * c.len() > 100 {
return TestResult::discard();
}
test_specializations(&vec![a, b, c].into_iter().multi_cartesian_product());
TestResult::passed()
}
fn coalesce(v: Vec<u8>) -> () {
test_specializations(&v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) }))
}
fn dedup(v: Vec<u8>) -> () {
test_specializations(&v.iter().dedup())
}
fn dedup_by(v: Vec<u8>) -> () {
test_specializations(&v.iter().dedup_by(PartialOrd::ge))
}
fn dedup_with_count(v: Vec<u8>) -> () {
test_specializations(&v.iter().dedup_with_count())
}
fn dedup_by_with_count(v: Vec<u8>) -> () {
test_specializations(&v.iter().dedup_by_with_count(PartialOrd::ge))
}
fn duplicates(v: Vec<u8>) -> () {
let it = v.iter().duplicates();
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn duplicates_by(v: Vec<u8>) -> () {
let it = v.iter().duplicates_by(|x| *x % 10);
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn unique(v: Vec<u8>) -> () {
let it = v.iter().unique();
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn unique_by(v: Vec<u8>) -> () {
let it = v.iter().unique_by(|x| *x % 50);
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn take_while_inclusive(v: Vec<u8>) -> () {
test_specializations(&v.iter().copied().take_while_inclusive(|&x| x < 100));
}
fn while_some(v: Vec<u8>) -> () {
test_specializations(&v.iter().map(|&x| if x < 100 { Some(2 * x) } else { None }).while_some());
}
fn pad_using(v: Vec<u8>) -> () {
use std::convert::TryFrom;
let it = v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX));
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn with_position(v: Vec<u8>) -> () {
test_specializations(&v.iter().with_position());
}
fn positions(v: Vec<u8>) -> () {
let it = v.iter().positions(|x| x % 5 == 0);
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn update(v: Vec<u8>) -> () {
let it = v.iter().copied().update(|x| *x = x.wrapping_mul(7));
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn tuple_combinations(v: Vec<u8>) -> TestResult {
if v.len() > 10 {
return TestResult::discard();
}
test_specializations(&v.iter().tuple_combinations::<(_,)>());
test_specializations(&v.iter().tuple_combinations::<(_, _)>());
test_specializations(&v.iter().tuple_combinations::<(_, _, _)>());
TestResult::passed()
}
fn intersperse(v: Vec<u8>) -> () {
test_specializations(&v.into_iter().intersperse(0));
}
fn intersperse_with(v: Vec<u8>) -> () {
test_specializations(&v.into_iter().intersperse_with(|| 0));
}
fn array_combinations(v: Vec<u8>) -> TestResult {
if v.len() > 10 {
return TestResult::discard();
}
test_specializations(&v.iter().array_combinations::<1>());
test_specializations(&v.iter().array_combinations::<2>());
test_specializations(&v.iter().array_combinations::<3>());
TestResult::passed()
}
fn combinations(a: Vec<u8>, n: u8) -> TestResult {
if n > 3 || a.len() > 8 {
return TestResult::discard();
}
test_specializations(&a.iter().combinations(n as usize));
TestResult::passed()
}
fn combinations_with_replacement(a: Vec<u8>, n: u8) -> TestResult {
if n > 3 || a.len() > 7 {
return TestResult::discard();
}
test_specializations(&a.iter().combinations_with_replacement(n as usize));
TestResult::passed()
}
fn permutations(a: Vec<u8>, n: u8) -> TestResult {
if n > 3 || a.len() > 8 {
return TestResult::discard();
}
test_specializations(&a.iter().permutations(n as usize));
TestResult::passed()
}
fn powerset(a: Vec<u8>) -> TestResult {
if a.len() > 6 {
return TestResult::discard();
}
test_specializations(&a.iter().powerset());
TestResult::passed()
}
fn zip_longest(a: Vec<u8>, b: Vec<u8>) -> () {
let it = a.into_iter().zip_longest(b);
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn zip_eq(a: Vec<u8>) -> () {
test_specializations(&a.iter().zip_eq(a.iter().rev()))
}
fn multizip(a: Vec<u8>) -> () {
let it = itertools::multizip((a.iter(), a.iter().rev(), a.iter().take(50)));
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn izip(a: Vec<u8>, b: Vec<u8>) -> () {
test_specializations(&itertools::izip!(b.iter(), a, b.iter().rev()));
}
fn iproduct(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult {
if a.len() * b.len() * c.len() > 200 {
return TestResult::discard();
}
test_specializations(&itertools::iproduct!(a, b.iter(), c));
TestResult::passed()
}
fn repeat_n(element: i8, n: u8) -> () {
let it = itertools::repeat_n(element, n as usize);
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn exactly_one_error(v: Vec<u8>) -> TestResult {
// Use `at_most_one` would be similar.
match v.iter().exactly_one() {
Ok(_) => TestResult::discard(),
Err(it) => {
test_specializations(&it);
TestResult::passed()
}
}
}
}
quickcheck! {
@@ -85,32 +368,128 @@ quickcheck! {
pb.put_back(1);
test_specializations(&pb);
}
fn put_back_n(v: Vec<u8>, n: u8) -> () {
let mut it = itertools::put_back_n(v);
for k in 0..n {
it.put_back(k);
}
test_specializations(&it);
}
fn multipeek(v: Vec<u8>, n: u8) -> () {
let mut it = v.into_iter().multipeek();
for _ in 0..n {
it.peek();
}
test_specializations(&it);
}
fn peek_nth_with_peek(v: Vec<u8>, n: u8) -> () {
let mut it = itertools::peek_nth(v);
for _ in 0..n {
it.peek();
}
test_specializations(&it);
}
fn peek_nth_with_peek_nth(v: Vec<u8>, n: u8) -> () {
let mut it = itertools::peek_nth(v);
it.peek_nth(n as usize);
test_specializations(&it);
}
fn peek_nth_with_peek_mut(v: Vec<u8>, n: u8) -> () {
let mut it = itertools::peek_nth(v);
for _ in 0..n {
if let Some(x) = it.peek_mut() {
*x = x.wrapping_add(50);
}
}
test_specializations(&it);
}
fn peek_nth_with_peek_nth_mut(v: Vec<u8>, n: u8) -> () {
let mut it = itertools::peek_nth(v);
if let Some(x) = it.peek_nth_mut(n as usize) {
*x = x.wrapping_add(50);
}
test_specializations(&it);
}
}
quickcheck! {
fn merge_join_by_qc(i1: Vec<usize>, i2: Vec<usize>) -> () {
test_specializations(&i1.into_iter().merge_join_by(i2.into_iter(), std::cmp::Ord::cmp));
fn merge(a: Vec<u8>, b: Vec<u8>) -> () {
test_specializations(&a.into_iter().merge(b))
}
fn merge_by(a: Vec<u8>, b: Vec<u8>) -> () {
test_specializations(&a.into_iter().merge_by(b, PartialOrd::ge))
}
fn merge_join_by_ordering(i1: Vec<u8>, i2: Vec<u8>) -> () {
test_specializations(&i1.into_iter().merge_join_by(i2, Ord::cmp));
}
fn merge_join_by_bool(i1: Vec<u8>, i2: Vec<u8>) -> () {
test_specializations(&i1.into_iter().merge_join_by(i2, PartialOrd::ge));
}
fn kmerge(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () {
test_specializations(&vec![a, b, c]
.into_iter()
.map(|v| v.into_iter().sorted())
.kmerge());
}
fn kmerge_by(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () {
test_specializations(&vec![a, b, c]
.into_iter()
.map(|v| v.into_iter().sorted_by_key(|a| a.abs()))
.kmerge_by(|a, b| a.abs() < b.abs()));
}
}
quickcheck! {
fn map_into(v: Vec<u8>) -> () {
test_specializations(&v.into_iter().map_into::<u32>());
let it = v.into_iter().map_into::<u32>();
test_specializations(&it);
test_double_ended_specializations(&it);
}
}
quickcheck! {
fn map_ok(v: Vec<Result<u8, char>>) -> () {
test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1)));
let it = v.into_iter().map_ok(|u| u.checked_add(1));
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn filter_ok(v: Vec<Result<u8, char>>) -> () {
let it = v.into_iter().filter_ok(|&i| i < 20);
test_specializations(&it);
test_double_ended_specializations(&it);
}
fn filter_map_ok(v: Vec<Result<u8, char>>) -> () {
let it = v.into_iter().filter_map_ok(|i| if i < 20 { Some(i * 2) } else { None });
test_specializations(&it);
test_double_ended_specializations(&it);
}
// `SmallIter2<u8>` because `Vec<u8>` is too slow and we get bad coverage from a singleton like Option<u8>
fn flatten_ok(v: Vec<Result<SmallIter2<u8>, char>>) -> () {
let it = v.into_iter().flatten_ok();
test_specializations(&it);
test_double_ended_specializations(&it);
}
}
quickcheck! {
// TODO Replace this function by a normal call to test_specializations
fn process_results(v: Vec<Result<u8, u8>>) -> () {
helper(v.iter().copied());
helper(v.iter().copied().filter(Result::is_ok));
fn helper(it: impl Iterator<Item = Result<u8, u8>> + Clone) {
fn helper(it: impl DoubleEndedIterator<Item = Result<u8, u8>> + Clone) {
macro_rules! check_results_specialized {
($src:expr, |$it:pat| $closure:expr) => {
assert_eq!(
@@ -126,6 +505,7 @@ quickcheck! {
check_results_specialized!(it, |i| i.count());
check_results_specialized!(it, |i| i.last());
check_results_specialized!(it, |i| i.collect::<Vec<_>>());
check_results_specialized!(it, |i| i.rev().collect::<Vec<_>>());
check_results_specialized!(it, |i| {
let mut parameters_from_fold = vec![];
let fold_result = i.fold(vec![], |mut acc, v| {
@@ -135,6 +515,15 @@ quickcheck! {
});
(parameters_from_fold, fold_result)
});
check_results_specialized!(it, |i| {
let mut parameters_from_rfold = vec![];
let rfold_result = i.rfold(vec![], |mut acc, v| {
parameters_from_rfold.push((acc.clone(), v));
acc.push(v);
acc
});
(parameters_from_rfold, rfold_result)
});
check_results_specialized!(it, |mut i| {
let mut parameters_from_all = vec![];
let first = i.next();
@@ -148,6 +537,67 @@ quickcheck! {
for n in 0..size + 2 {
check_results_specialized!(it, |mut i| i.nth(n));
}
for n in 0..size + 2 {
check_results_specialized!(it, |mut i| i.nth_back(n));
}
}
}
}
/// Like `VecIntoIter<T>` with maximum 2 elements.
#[derive(Debug, Clone, Default)]
enum SmallIter2<T> {
#[default]
Zero,
One(T),
Two(T, T),
}
impl<T: Arbitrary> Arbitrary for SmallIter2<T> {
fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self {
match g.gen_range(0u8, 3) {
0 => Self::Zero,
1 => Self::One(T::arbitrary(g)),
2 => Self::Two(T::arbitrary(g), T::arbitrary(g)),
_ => unreachable!(),
}
}
// maybe implement shrink too, maybe not
}
impl<T> Iterator for SmallIter2<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match std::mem::take(self) {
Self::Zero => None,
Self::One(val) => Some(val),
Self::Two(val, second) => {
*self = Self::One(second);
Some(val)
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = match self {
Self::Zero => 0,
Self::One(_) => 1,
Self::Two(_, _) => 2,
};
(len, Some(len))
}
}
impl<T> DoubleEndedIterator for SmallIter2<T> {
fn next_back(&mut self) -> Option<Self::Item> {
match std::mem::take(self) {
Self::Zero => None,
Self::One(val) => Some(val),
Self::Two(first, val) => {
*self = Self::One(first);
Some(val)
}
}
}
}

View File

@@ -4,18 +4,71 @@
//! option. This file may not be copied, modified, or distributed
//! except according to those terms.
#![no_std]
#![allow(deprecated)]
use core::iter;
use itertools as it;
use crate::it::Itertools;
use crate::it::chain;
use crate::it::free::put_back;
use crate::it::interleave;
use crate::it::intersperse;
use crate::it::intersperse_with;
use crate::it::multizip;
use crate::it::free::put_back;
use crate::it::iproduct;
use crate::it::izip;
use crate::it::chain;
use crate::it::multizip;
use crate::it::Itertools;
use core::iter;
use itertools as it;
#[allow(dead_code)]
fn get_esi_then_esi<I: ExactSizeIterator + Clone>(it: I) {
fn is_esi(_: impl ExactSizeIterator) {}
is_esi(it.clone().get(1..4));
is_esi(it.clone().get(1..=4));
is_esi(it.clone().get(1..));
is_esi(it.clone().get(..4));
is_esi(it.clone().get(..=4));
is_esi(it.get(..));
}
#[allow(dead_code)]
fn get_dei_esi_then_dei_esi<I: DoubleEndedIterator + ExactSizeIterator + Clone>(it: I) {
fn is_dei_esi(_: impl DoubleEndedIterator + ExactSizeIterator) {}
is_dei_esi(it.clone().get(1..4));
is_dei_esi(it.clone().get(1..=4));
is_dei_esi(it.clone().get(1..));
is_dei_esi(it.clone().get(..4));
is_dei_esi(it.clone().get(..=4));
is_dei_esi(it.get(..));
}
#[test]
fn get_1_max() {
let mut it = (0..5).get(1..=usize::MAX);
assert_eq!(it.next(), Some(1));
assert_eq!(it.next_back(), Some(4));
}
#[test]
#[should_panic]
fn get_full_range_inclusive() {
let _it = (0..5).get(0..=usize::MAX);
}
#[test]
fn product0() {
let mut prod = iproduct!();
assert_eq!(prod.next(), Some(()));
assert!(prod.next().is_none());
}
#[test]
fn iproduct1() {
let s = "αβ";
let mut prod = iproduct!(s.chars());
assert_eq!(prod.next(), Some(('α',)));
assert_eq!(prod.next(), Some(('β',)));
assert!(prod.next().is_none());
}
#[test]
fn product2() {
@@ -26,7 +79,7 @@ fn product2() {
assert!(prod.next() == Some(('α', 1)));
assert!(prod.next() == Some(('β', 0)));
assert!(prod.next() == Some(('β', 1)));
assert!(prod.next() == None);
assert!(prod.next().is_none());
}
#[test]
@@ -34,13 +87,12 @@ fn product_temporary() {
for (_x, _y, _z) in iproduct!(
[0, 1, 2].iter().cloned(),
[0, 1, 2].iter().cloned(),
[0, 1, 2].iter().cloned())
{
[0, 1, 2].iter().cloned()
) {
// ok
}
}
#[test]
fn izip_macro() {
let mut zip = izip!(2..3);
@@ -61,7 +113,7 @@ fn izip_macro() {
#[test]
fn izip2() {
let _zip1: iter::Zip<_, _> = izip!(1.., 2..);
let _zip2: iter::Zip<_, _> = izip!(1.., 2.., );
let _zip2: iter::Zip<_, _> = izip!(1.., 2..,);
}
#[test]
@@ -109,7 +161,7 @@ fn chain_macro() {
#[test]
fn chain2() {
let _ = chain!(1.., 2..);
let _ = chain!(1.., 2.., );
let _ = chain!(1.., 2..,);
}
#[test]
@@ -127,7 +179,7 @@ fn write_to() {
#[test]
fn test_interleave() {
let xs: [u8; 0] = [];
let xs: [u8; 0] = [];
let ys = [7u8, 9, 8, 10];
let zs = [2u8, 77];
let it = interleave(xs.iter(), ys.iter());
@@ -155,15 +207,6 @@ fn test_intersperse_with() {
it::assert_equal(it, ys.iter());
}
#[allow(deprecated)]
#[test]
fn foreach() {
let xs = [1i32, 2, 3];
let mut sum = 0;
xs.iter().foreach(|elt| sum += *elt);
assert!(sum == 6);
}
#[test]
fn dropping() {
let xs = [1, 2, 3];
@@ -197,21 +240,11 @@ fn test_put_back() {
it::assert_equal(pb, xs.iter().cloned());
}
#[allow(deprecated)]
#[test]
fn step() {
it::assert_equal((0..10).step(1), 0..10);
it::assert_equal((0..10).step(2), (0..10).filter(|x: &i32| *x % 2 == 0));
it::assert_equal((0..10).step(10), 0..1);
}
#[allow(deprecated)]
#[test]
fn merge() {
it::assert_equal((0..10).step(2).merge((1..10).step(2)), 0..10);
it::assert_equal((0..10).step_by(2).merge((1..10).step_by(2)), 0..10);
}
#[test]
fn repeatn() {
let s = "α";
@@ -231,29 +264,33 @@ fn count_clones() {
use core::cell::Cell;
#[derive(PartialEq, Debug)]
struct Foo {
n: Cell<usize>
n: Cell<usize>,
}
impl Clone for Foo
{
fn clone(&self) -> Self
{
impl Clone for Foo {
fn clone(&self) -> Self {
let n = self.n.get();
self.n.set(n + 1);
Foo { n: Cell::new(n + 1) }
Self {
n: Cell::new(n + 1),
}
}
}
for n in 0..10 {
let f = Foo{n: Cell::new(0)};
let f = Foo { n: Cell::new(0) };
let it = it::repeat_n(f, n);
// drain it
let last = it.last();
if n == 0 {
assert_eq!(last, None);
} else {
assert_eq!(last, Some(Foo{n: Cell::new(n - 1)}));
assert_eq!(
last,
Some(Foo {
n: Cell::new(n - 1)
})
);
}
}
}
@@ -276,25 +313,45 @@ fn part() {
}
#[test]
fn tree_fold1() {
fn tree_reduce() {
for i in 0..100 {
assert_eq!((0..i).tree_fold1(|x, y| x + y), (0..i).fold1(|x, y| x + y));
assert_eq!((0..i).tree_reduce(|x, y| x + y), (0..i).fold1(|x, y| x + y));
}
}
#[test]
fn exactly_one() {
assert_eq!((0..10).filter(|&x| x == 2).exactly_one().unwrap(), 2);
assert!((0..10).filter(|&x| x > 1 && x < 4).exactly_one().unwrap_err().eq(2..4));
assert!((0..10).filter(|&x| x > 1 && x < 5).exactly_one().unwrap_err().eq(2..5));
assert!((0..10).filter(|&_| false).exactly_one().unwrap_err().eq(0..0));
assert!((0..10)
.filter(|&x| x > 1 && x < 4)
.exactly_one()
.unwrap_err()
.eq(2..4));
assert!((0..10)
.filter(|&x| x > 1 && x < 5)
.exactly_one()
.unwrap_err()
.eq(2..5));
assert!((0..10)
.filter(|&_| false)
.exactly_one()
.unwrap_err()
.eq(0..0));
}
#[test]
fn at_most_one() {
assert_eq!((0..10).filter(|&x| x == 2).at_most_one().unwrap(), Some(2));
assert!((0..10).filter(|&x| x > 1 && x < 4).at_most_one().unwrap_err().eq(2..4));
assert!((0..10).filter(|&x| x > 1 && x < 5).at_most_one().unwrap_err().eq(2..5));
assert!((0..10)
.filter(|&x| x > 1 && x < 4)
.at_most_one()
.unwrap_err()
.eq(2..4));
assert!((0..10)
.filter(|&x| x > 1 && x < 5)
.at_most_one()
.unwrap_err()
.eq(2..5));
assert_eq!((0..10).filter(|&_| false).at_most_one().unwrap(), None);
}
@@ -315,3 +372,28 @@ fn product1() {
assert_eq!(v[1..3].iter().cloned().product1::<i32>(), Some(2));
assert_eq!(v[1..5].iter().cloned().product1::<i32>(), Some(24));
}
#[test]
fn next_array() {
let v = [1, 2, 3, 4, 5];
let mut iter = v.iter();
assert_eq!(iter.next_array(), Some([]));
assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([1, 2]));
assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([3, 4]));
assert_eq!(iter.next_array::<2>(), None);
}
#[test]
fn collect_array() {
let v = [1, 2];
let iter = v.iter().cloned();
assert_eq!(iter.collect_array(), Some([1, 2]));
let v = [1];
let iter = v.iter().cloned();
assert_eq!(iter.collect_array::<2>(), None);
let v = [1, 2, 3];
let iter = v.iter().cloned();
assert_eq!(iter.collect_array::<2>(), None);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,17 @@
use itertools::Itertools;
use itertools::EitherOrBoth::{Both, Left, Right};
use itertools::free::zip_eq;
use itertools::multizip;
use itertools::EitherOrBoth::{Both, Left, Right};
use itertools::Itertools;
#[test]
fn zip_longest_fused() {
let a = [Some(1), None, Some(3), Some(4)];
let b = [1, 2, 3];
let unfused = a.iter().batching(|it| *it.next().unwrap())
let unfused = a
.iter()
.batching(|it| *it.next().unwrap())
.zip_longest(b.iter().cloned());
itertools::assert_equal(unfused,
vec![Both(1, 1), Right(2), Right(3)]);
itertools::assert_equal(unfused, vec![Both(1, 1), Right(2), Right(3)]);
}
#[test]
@@ -20,7 +20,7 @@ fn test_zip_longest_size_hint() {
let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let v2 = &[10, 11, 12];
assert_eq!(c.zip_longest(v.iter()).size_hint(), (std::usize::MAX, None));
assert_eq!(c.zip_longest(v.iter()).size_hint(), (usize::MAX, None));
assert_eq!(v.iter().zip_longest(v2.iter()).size_hint(), (10, Some(10)));
}
@@ -54,24 +54,3 @@ fn test_double_ended_zip() {
assert_eq!(it.next_back(), Some((1, 1)));
assert_eq!(it.next_back(), None);
}
#[should_panic]
#[test]
fn zip_eq_panic1()
{
let a = [1, 2];
let b = [1, 2, 3];
zip_eq(&a, &b).count();
}
#[should_panic]
#[test]
fn zip_eq_panic2()
{
let a: [i32; 0] = [];
let b = [1, 2, 3];
zip_eq(&a, &b).count();
}