itertools-0.13.0/.cargo_vcs_info.json0000644000000001360000000000100131410ustar { "git": { "sha1": "d5084d15e959b85d89a49e5cd33ad6267bc541a3" }, "path_in_vcs": "" }itertools-0.13.0/.codecov.yml000064400000000000000000000002711046102023000141540ustar 00000000000000coverage: status: project: default: target: auto # Allow a tiny drop of overall project coverage in PR to reduce spurious failures. threshold: 0.25% itertools-0.13.0/.github/dependabot.yml000064400000000000000000000001511046102023000161160ustar 00000000000000version: 2 updates: - package-ecosystem: github-actions directory: "/" schedule: interval: daily itertools-0.13.0/.github/workflows/ci.yml000064400000000000000000000040621046102023000164460ustar 00000000000000name: CI on: pull_request: paths-ignore: - "**.md" merge_group: paths-ignore: - "**.md" jobs: check: runs-on: ubuntu-latest strategy: matrix: features: [ "", "--no-default-features", "--no-default-features --features use_alloc", "--all-targets --all-features", ] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: clippy - run: RUSTFLAGS="--deny warnings" cargo clippy ${{ matrix.features }} doc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: RUSTDOCFLAGS="-Dwarnings" cargo doc --all-features msrv: runs-on: ubuntu-latest env: CARGO_NET_GIT_FETCH_WITH_CLI: true steps: - uses: actions/checkout@v4 - uses: taiki-e/install-action@cargo-no-dev-deps - uses: dtolnay/rust-toolchain@master with: # Here, it does not trigger a PR from dependabot. toolchain: 1.43.1 - run: cargo no-dev-deps check test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo test --all-features check-format: name: check format runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable components: rustfmt - run: cargo fmt --check semver-checks: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: obi1kenobi/cargo-semver-checks-action@v2.4 with: rust-toolchain: stable feature-group: all-features # Used to signal to branch protections that all other jobs have succeeded. all-jobs-succeed: name: All checks succeeded if: success() runs-on: ubuntu-latest needs: [check, msrv, test, check-format, doc] steps: - name: Mark the job as successful run: exit 0 itertools-0.13.0/.github/workflows/coverage.yml000064400000000000000000000013731046102023000176500ustar 00000000000000on: push: branches: [master] paths-ignore: - "**.md" pull_request: paths-ignore: - "**.md" name: Code Coverage jobs: coverage: name: coverage runs-on: ubuntu-latest steps: - name: checkout source uses: actions/checkout@v4 - name: Install nightly toolchain uses: dtolnay/rust-toolchain@nightly with: components: llvm-tools-preview - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: Run llvm-cov run: cargo llvm-cov --all-features --doctests --workspace --lcov --output-path lcov.info - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: files: lcov.info itertools-0.13.0/.gitignore000064400000000000000000000000231046102023000137140ustar 00000000000000/target Cargo.lock itertools-0.13.0/CHANGELOG.md000064400000000000000000000650541046102023000135540ustar 00000000000000# Changelog ## 0.13.0 ### Breaking - Removed implementation of `DoubleEndedIterator` for `ConsTuples` (#853) - Made `MultiProduct` fused and fixed on an empty iterator (#835, #834) - Changed `iproduct!` to return tuples for maxi one iterator too (#870) - Changed `PutBack::put_back` to return the old value (#880) - Removed deprecated `repeat_call, Itertools::{foreach, step, map_results, fold_results}` (#878) - Removed `TakeWhileInclusive::new` (#912) ### Added - Added `Itertools::{smallest_by, smallest_by_key, largest, largest_by, largest_by_key}` (#654, #885) - Added `Itertools::tail` (#899) - Implemented `DoubleEndedIterator` for `ProcessResults` (#910) - Implemented `Debug` for `FormatWith` (#931) - Added `Itertools::get` (#891) ### Changed - Deprecated `Itertools::group_by` (renamed `chunk_by`) (#866, #879) - Deprecated `unfold` (use `std::iter::from_fn` instead) (#871) - Optimized `GroupingMapBy` (#873, #876) - Relaxed `Fn` bounds to `FnMut` in `diff_with, Itertools::into_group_map_by` (#886) - Relaxed `Debug/Clone` bounds for `MapInto` (#889) - Documented the `use_alloc` feature (#887) - Optimized `Itertools::set_from` (#888) - Removed badges in `README.md` (#890) - Added "no-std" categories in `Cargo.toml` (#894) - Fixed `Itertools::k_smallest` on short unfused iterators (#900) - Deprecated `Itertools::tree_fold1` (renamed `tree_reduce`) (#895) - Deprecated `GroupingMap::fold_first` (renamed `reduce`) (#902) - Fixed `Itertools::k_smallest(0)` to consume the iterator, optimized `Itertools::k_smallest(1)` (#909) - Specialized `Combinations::nth` (#914) - Specialized `MergeBy::fold` (#920) - Specialized `CombinationsWithReplacement::nth` (#923) - Specialized `FlattenOk::{fold, rfold}` (#927) - Specialized `Powerset::nth` (#924) - Documentation fixes (#882, #936) - Fixed `assert_equal` for iterators longer than `i32::MAX` (#932) - Updated the `must_use` message of non-lazy `KMergeBy` and `TupleCombinations` (#939) ### Notable Internal Changes - Tested iterator laziness (#792) - Created `CONTRIBUTING.md` (#767) ## 0.12.1 ### Added - Documented iteration order guarantee for `Itertools::[tuple_]combinations` (#822) - Documented possible panic in `iterate` (#842) - Implemented `Clone` and `Debug` for `Diff` (#845) - Implemented `Debug` for `WithPosition` (#859) - Implemented `Eq` for `MinMaxResult` (#838) - Implemented `From>` for `Option>` (#843) - Implemented `PeekingNext` for `RepeatN` (#855) ### Changed - Made `CoalesceBy` lazy (#801) - Optimized `Filter[Map]Ok::next`, `Itertools::partition`, `Unique[By]::next[_back]` (#818) - Optimized `Itertools::find_position` (#837) - Optimized `Positions::next[_back]` (#816) - Optimized `ZipLongest::fold` (#854) - Relaxed `Debug` bounds for `GroupingMapBy` (#860) - Specialized `ExactlyOneError::fold` (#826) - Specialized `Interleave[Shortest]::fold` (#849) - Specialized `MultiPeek::fold` (#820) - Specialized `PadUsing::[r]fold` (#825) - Specialized `PeekNth::fold` (#824) - Specialized `Positions::[r]fold` (#813) - Specialized `PutBackN::fold` (#823) - Specialized `RepeatN::[r]fold` (#821) - Specialized `TakeWhileInclusive::fold` (#851) - Specialized `ZipLongest::rfold` (#848) ### Notable Internal Changes - Added test coverage in CI (#847, #856) - Added semver check in CI (#784) - Enforced `clippy` in CI (#740) - Enforced `rustdoc` in CI (#840) - Improved specialization tests (#807) - More specialization benchmarks (#806) ## 0.12.0 ### Breaking - Made `take_while_inclusive` consume iterator by value (#709) - Added `Clone` bound to `Unique` (#777) ### Added - Added `Itertools::try_len` (#723) - Added free function `sort_unstable` (#796) - Added `GroupMap::fold_with` (#778, #785) - Added `PeekNth::{peek_mut, peek_nth_mut}` (#716) - Added `PeekNth::{next_if, next_if_eq}` (#734) - Added conversion into `(Option,Option)` to `EitherOrBoth` (#713) - Added conversion from `Either` to `EitherOrBoth` (#715) - Implemented `ExactSizeIterator` for `Tuples` (#761) - Implemented `ExactSizeIterator` for `(Circular)TupleWindows` (#752) - Made `EitherOrBoth` a shorthand for `EitherOrBoth` (#719) ### Changed - Added missing `#[must_use]` annotations on iterator adaptors (#794) - Made `Combinations` lazy (#795) - Made `Intersperse(With)` lazy (#797) - Made `Permutations` lazy (#793) - Made `Product` lazy (#800) - Made `TupleWindows` lazy (#602) - Specialized `Combinations::{count, size_hint}` (#729) - Specialized `CombinationsWithReplacement::{count, size_hint}` (#737) - Specialized `Powerset::fold` (#765) - Specialized `Powerset::count` (#735) - Specialized `TupleCombinations::{count, size_hint}` (#763) - Specialized `TupleCombinations::fold` (#775) - Specialized `WhileSome::fold` (#780) - Specialized `WithPosition::fold` (#772) - Specialized `ZipLongest::fold` (#774) - Changed `{min, max}_set*` operations require `alloc` feature, instead of `std` (#760) - Improved documentation of `tree_fold1` (#787) - Improved documentation of `permutations` (#724) - Fixed typo in documentation of `multiunzip` (#770) ### Notable Internal Changes - Improved specialization tests (#799, #786, #782) - Simplified implementation of `Permutations` (#739, #748, #790) - Combined `Merge`/`MergeBy`/`MergeJoinBy` implementations (#736) - Simplified `Permutations::size_hint` (#739) - Fix wrapping arithmetic in benchmarks (#770) - Enforced `rustfmt` in CI (#751) - Disallowed compile warnings in CI (#720) - Used `cargo hack` to check MSRV (#754) ## 0.11.0 ### Breaking - Make `Itertools::merge_join_by` also accept functions returning bool (#704) - Implement `PeekingNext` transitively over mutable references (#643) - Change `with_position` to yield `(Position, Item)` instead of `Position` (#699) ### Added - Add `Itertools::take_while_inclusive` (#616) - Implement `PeekingNext` for `PeekingTakeWhile` (#644) - Add `EitherOrBoth::{just_left, just_right, into_left, into_right, as_deref, as_deref_mut, left_or_insert, right_or_insert, left_or_insert_with, right_or_insert_with, insert_left, insert_right, insert_both}` (#629) - Implement `Clone` for `CircularTupleWindows` (#686) - Implement `Clone` for `Chunks` (#683) - Add `Itertools::process_results` (#680) ### Changed - Use `Cell` instead of `RefCell` in `Format` and `FormatWith` (#608) - CI tweaks (#674, #675) - Document and test the difference between stable and unstable sorts (#653) - Fix documentation error on `Itertools::max_set_by_key` (#692) - Move MSRV metadata to `Cargo.toml` (#672) - Implement `equal` with `Iterator::eq` (#591) ## 0.10.5 - Maintenance ## 0.10.4 - Add `EitherOrBoth::or` and `EitherOrBoth::or_else` (#593) - Add `min_set`, `max_set` et al. (#613, #323) - Use `either/use_std` (#628) - 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) - Add `Itertools::sorted_by_cached_key` (#424, #575) - Specialize `ProcessResults::fold` (#563) - Fix subtraction overflow in `DuplicatesBy::size_hint` (#552) - Fix specialization tests (#574) - More `Debug` impls (#573) - Deprecate `fold1` (use `reduce` instead) (#580) - Documentation fixes (`HomogenousTuple`, `into_group_map`, `into_group_map_by`, `MultiPeek::peek`) (#543 et al.) ## 0.10.1 - Add `Itertools::contains` (#514) - Add `Itertools::counts_by` (#515) - Add `Itertools::partition_result` (#511) - Add `Itertools::all_unique` (#241) - Add `Itertools::duplicates` and `Itertools::duplicates_by` (#502) - Add `chain!` (#525) - Add `Itertools::at_most_one` (#523) - Add `Itertools::flatten_ok` (#527) - Add `EitherOrBoth::or_default` (#583) - Add `Itertools::find_or_last` and `Itertools::find_or_first` (#535) - Implement `FusedIterator` for `FilterOk`, `FilterMapOk`, `InterleaveShortest`, `KMergeBy`, `MergeBy`, `PadUsing`, `Positions`, `Product` , `RcIter`, `TupleWindows`, `Unique`, `UniqueBy`, `Update`, `WhileSome`, `Combinations`, `CombinationsWithReplacement`, `Powerset`, `RepeatN`, and `WithPosition` (#550) - Implement `FusedIterator` for `Interleave`, `IntersperseWith`, and `ZipLongest` (#548) ## 0.10.0 - **Increase minimum supported Rust version to 1.32.0** - Improve macro hygiene (#507) - Add `Itertools::powerset` (#335) - Add `Itertools::sorted_unstable`, `Itertools::sorted_unstable_by`, and `Itertools::sorted_unstable_by_key` (#494) - Implement `Error` for `ExactlyOneError` (#484) - Undeprecate `Itertools::fold_while` (#476) - Tuple-related adapters work for tuples of arity up to 12 (#475) - `use_alloc` feature for users who have `alloc`, but not `std` (#474) - Add `Itertools::k_smallest` (#473) - Add `Itertools::into_grouping_map` and `GroupingMap` (#465) - Add `Itertools::into_grouping_map_by` and `GroupingMapBy` (#465) - Add `Itertools::counts` (#468) - Add implementation of `DoubleEndedIterator` for `Unique` (#442) - Add implementation of `DoubleEndedIterator` for `UniqueBy` (#442) - Add implementation of `DoubleEndedIterator` for `Zip` (#346) - Add `Itertools::multipeek` (#435) - Add `Itertools::dedup_with_count` and `DedupWithCount` (#423) - Add `Itertools::dedup_by_with_count` and `DedupByWithCount` (#423) - Add `Itertools::intersperse_with` and `IntersperseWith` (#381) - Add `Itertools::filter_ok` and `FilterOk` (#377) - Add `Itertools::filter_map_ok` and `FilterMapOk` (#377) - Deprecate `Itertools::fold_results`, use `Itertools::fold_ok` instead (#377) - Deprecate `Itertools::map_results`, use `Itertools::map_ok` instead (#377) - Deprecate `FoldResults`, use `FoldOk` instead (#377) - Deprecate `MapResults`, use `MapOk` instead (#377) - Add `Itertools::circular_tuple_windows` and `CircularTupleWindows` (#350) - Add `peek_nth` and `PeekNth` (#303) ## 0.9.0 - Fix potential overflow in `MergeJoinBy::size_hint` (#385) - Add `derive(Clone)` where possible (#382) - Add `try_collect` method (#394) - Add `HomogeneousTuple` trait (#389) - Fix `combinations(0)` and `combinations_with_replacement(0)` (#383) - Don't require `ParitalEq` to the `Item` of `DedupBy` (#397) - Implement missing specializations on the `PutBack` adaptor and on the `MergeJoinBy` iterator (#372) - Add `position_*` methods (#412) - Derive `Hash` for `EitherOrBoth` (#417) - Increase minimum supported Rust version to 1.32.0 ## 0.8.2 - Use `slice::iter` instead of `into_iter` to avoid future breakage (#378, by @LukasKalbertodt) ## 0.8.1 - Added a [`.exactly_one()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.exactly_one) iterator method that, on success, extracts the single value of an iterator ; by @Xaeroxe - Added combinatory iterator adaptors: - [`.permutations(k)`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.permutations): `[0, 1, 2].iter().permutations(2)` yields ```rust [ vec![0, 1], vec![0, 2], vec![1, 0], vec![1, 2], vec![2, 0], vec![2, 1], ] ``` ; by @tobz1000 - [`.combinations_with_replacement(k)`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.combinations_with_replacement): `[0, 1, 2].iter().combinations_with_replacement(2)` yields ```rust [ vec![0, 0], vec![0, 1], vec![0, 2], vec![1, 1], vec![1, 2], vec![2, 2], ] ``` ; by @tommilligan - For reference, these methods join the already existing [`.combinations(k)`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.combinations): `[0, 1, 2].iter().combinations(2)` yields ```rust [ vec![0, 1], vec![0, 2], vec![1, 2], ] ``` - Improved the performance of [`.fold()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.fold)-based internal iteration for the [`.intersperse()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.intersperse) iterator ; by @jswrenn - Added [`.dedup_by()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.dedup_by), [`.merge_by()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.merge_by) and [`.kmerge_by()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.kmerge_by) adaptors that work like [`.dedup()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.dedup), [`.merge()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.merge) and [`.kmerge()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.kmerge), but taking an additional custom comparison closure parameter. ; by @phimuemue - Improved the performance of [`.all_equal()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.all_equal) ; by @fyrchik - Loosened the bounds on [`.partition_map()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.partition_map) to take just a `FnMut` closure rather than a `Fn` closure, and made its implementation use internal iteration for better performance ; by @danielhenrymantilla - Added convenience methods to [`EitherOrBoth`](https://docs.rs/itertools/0.8.1/itertools/enum.EitherOrBoth.html) elements yielded from the [`.zip_longest()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.zip_longest) iterator adaptor ; by @Avi-D-coder - Added [`.sum1()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.sum1) and [`.product1()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.product1) iterator methods that respectively try to return the sum and the product of the elements of an iterator **when it is not empty**, otherwise they return `None` ; by @Emerentius ## 0.8.0 - Added new adaptor `.map_into()` for conversions using `Into` by @vorner - Improved `Itertools` docs by @JohnHeitmann - The return type of `.sorted_by_by_key()` is now an iterator, not a Vec. - The return type of the `izip!(x, y)` macro with exactly two arguments is now the usual `Iterator::zip`. - Remove `.flatten()` in favour of std's `.flatten()` - Deprecate `.foreach()` in favour of std's `.for_each()` - Deprecate `.step()` in favour of std's `.step_by()` - Deprecate `repeat_call` in favour of std's `repeat_with` - Deprecate `.fold_while()` in favour of std's `.try_fold()` - Require Rust 1.24 as minimal version. ## 0.7.11 - Add convenience methods to `EitherOrBoth`, making it more similar to `Option` and `Either` by @jethrogb ## 0.7.10 - No changes. ## 0.7.9 - New inclusion policy: See the readme about suggesting features for std before accepting them in itertools. - The `FoldWhile` type now implements `Eq` and `PartialEq` by @jturner314 ## 0.7.8 - Add new iterator method `.tree_fold1()` which is like `.fold1()` except items are combined in a tree structure (see its docs). By @scottmcm - Add more `Debug` impls by @phimuemue: KMerge, KMergeBy, MergeJoinBy, ConsTuples, Intersperse, ProcessResults, RcIter, Tee, TupleWindows, Tee, ZipLongest, ZipEq, Zip. ## 0.7.7 - Add new iterator method `.into_group_map() -> HashMap>` which turns an iterator of `(K, V)` elements into such a hash table, where values are grouped by key. By @tobz1000 - Add new free function `flatten` for the `.flatten()` adaptor. **NOTE:** recent Rust nightlies have `Iterator::flatten` and thus a clash with our flatten adaptor. One workaround is to use the itertools `flatten` free function. ## 0.7.6 - Add new adaptor `.multi_cartesian_product()` which is an n-ary product iterator by @tobz1000 - Add new method `.sorted_by_key()` by @Xion - Provide simpler and faster `.count()` for `.unique()` and `.unique_by()` ## 0.7.5 - `.multipeek()` now implements `PeekingNext`, by @nicopap. ## 0.7.4 - Add new adaptor `.update()` by @lucasem; this adaptor is used to modify an element before passing it on in an iterator chain. ## 0.7.3 - Add new method `.collect_tuple()` by @matklad; it makes a tuple out of the iterator's elements if the number of them matches **exactly**. - Implement `fold` and `collect` for `.map_results()` which means it reuses the code of the standard `.map()` for these methods. ## 0.7.2 - Add new adaptor `.merge_join_by` by @srijs; a heterogeneous merge join for two ordered sequences. ## 0.7.1 - Iterator adaptors and iterators in itertools now use the same `must_use` reminder that the standard library adaptors do, by @matematikaedit and @bluss *“iterator adaptors are lazy and do nothing unless consumed”*. ## 0.7.0 - Faster `izip!()` by @krdln - `izip!()` is now a wrapper for repeated regular `.zip()` and a single `.map()`. This means it optimizes as well as the standard library `.zip()` it uses. **Note:** `multizip` and `izip!()` are now different! The former has a named type but the latter optimizes better. - Faster `.unique()` - `no_std` support, which is opt-in! - Many lovable features are still there without std, like `izip!()` or `.format()` or `.merge()`, but not those that use collections. - Trait bounds were required up front instead of just on the type: `group_by`'s `PartialEq` by @Phlosioneer and `repeat_call`'s `FnMut`. - Removed deprecated constructor `Zip::new` — use `izip!()` or `multizip()` ## 0.6.5 - Fix bug in `.cartesian_product()`'s fold (which only was visible for unfused iterators). ## 0.6.4 - Add specific `fold` implementations for `.cartesian_product()` and `cons_tuples()`, which improves their performance in fold, foreach, and iterator consumers derived from them. ## 0.6.3 - Add iterator adaptor `.positions(predicate)` by @tmccombs ## 0.6.2 - Add function `process_results` which can “lift” a function of the regular values of an iterator so that it can process the `Ok` values from an iterator of `Results` instead, by @shepmaster - Add iterator method `.concat()` which combines all iterator elements into a single collection using the `Extend` trait, by @srijs ## 0.6.1 - Better size hint testing and subsequent size hint bugfixes by @rkarp. Fixes bugs in product, `interleave_shortest` size hints. - New iterator method `.all_equal()` by @phimuemue ## 0.6.0 - Deprecated names were removed in favour of their replacements - `.flatten()` does not implement double ended iteration anymore - `.fold_while()` uses `&mut self` and returns `FoldWhile`, for composability #168 - `.foreach()` and `.fold1()` use `self`, like `.fold()` does. - `.combinations(0)` now produces a single empty vector. #174 ## 0.5.10 - Add itertools method `.kmerge_by()` (and corresponding free function) - Relaxed trait requirement of `.kmerge()` and `.minmax()` to PartialOrd. ## 0.5.9 - Add multipeek method `.reset_peek()` - Add categories ## 0.5.8 - Add iterator adaptor `.peeking_take_while()` and its trait `PeekingNext`. ## 0.5.7 - Add iterator adaptor `.with_position()` - Fix multipeek's performance for long peeks by using `VecDeque`. ## 0.5.6 - Add `.map_results()` ## 0.5.5 - Many more adaptors now implement `Debug` - Add free function constructor `repeat_n`. `RepeatN::new` is now deprecated. ## 0.5.4 - Add infinite generator function `iterate`, that takes a seed and a closure. ## 0.5.3 - Special-cased `.fold()` for flatten and put back. `.foreach()` now uses fold on the iterator, to pick up any iterator specific loop implementation. - `.combinations(n)` asserts up front that `n != 0`, instead of running into an error on the second iterator element. ## 0.5.2 - Add `.tuples::()` that iterates by two, three or four elements at a time (where `T` is a tuple type). - Add `.tuple_windows::()` that iterates using a window of the two, three or four most recent elements. - Add `.next_tuple::()` method, that picks the next two, three or four elements in one go. - `.interleave()` now has an accurate size hint. ## 0.5.1 - Workaround module/function name clash that made racer crash on completing itertools. Only internal changes needed. ## 0.5.0 - [Release announcement](https://bluss.github.io/rust/2016/09/26/itertools-0.5.0/) - Renamed: - `combinations` is now `tuple_combinations` - `combinations_n` to `combinations` - `group_by_lazy`, `chunks_lazy` to `group_by`, `chunks` - `Unfold::new` to `unfold()` - `RepeatCall::new` to `repeat_call()` - `Zip::new` to `multizip` - `PutBack::new`, `PutBackN::new` to `put_back`, `put_back_n` - `PutBack::with_value` is now a builder setter, not a constructor - `MultiPeek::new`, `.multipeek()` to `multipeek()` - `format` to `format_with` and `format_default` to `format` - `.into_rc()` to `rciter` - `Partition` enum is now `Either` - Module reorganization: - All iterator structs are under `itertools::structs` but also reexported to the top level, for backwards compatibility - All free functions are reexported at the root, `itertools::free` will be removed in the next version - Removed: - `ZipSlices`, use `.zip()` instead - `.enumerate_from()`, `ZipTrusted`, due to being unstable - `.mend_slices()`, moved to crate `odds` - Stride, StrideMut, moved to crate `odds` - `linspace()`, moved to crate `itertools-num` - `.sort_by()`, use `.sorted_by()` - `.is_empty_hint()`, use `.size_hint()` - `.dropn()`, use `.dropping()` - `.map_fn()`, use `.map()` - `.slice()`, use `.take()` / `.skip()` - helper traits in `misc` - `new` constructors on iterator structs, use `Itertools` trait or free functions instead - `itertools::size_hint` is now private - Behaviour changes: - `format` and `format_with` helpers now panic if you try to format them more than once. - `repeat_call` is not double ended anymore - New features: - tuple flattening iterator is constructible with `cons_tuples` - itertools reexports `Either` from the `either` crate. `Either` is an iterator when `L, R` are. - `MinMaxResult` now implements `Copy` and `Clone` - `tuple_combinations` supports 1-4 tuples of combinations (previously just 2) ## 0.4.19 - Add `.minmax_by()` - Add `itertools::free::cloned` - Add `itertools::free::rciter` - Improve `.step(n)` slightly to take advantage of specialized Fuse better. ## 0.4.18 - Only changes related to the "unstable" crate feature. This feature is more or less deprecated. - Use deprecated warnings when unstable is enabled. `.enumerate_from()` will be removed imminently since it's using a deprecated libstd trait. ## 0.4.17 - Fix bug in `.kmerge()` that caused it to often produce the wrong order #134 ## 0.4.16 - Improve precision of the `interleave_shortest` adaptor's size hint (it is now computed exactly when possible). ## 0.4.15 - Fixup on top of the workaround in 0.4.14. A function in `itertools::free` was removed by mistake and now it is added back again. ## 0.4.14 - Workaround an upstream regression in a Rust nightly build that broke compilation of of `itertools::free::{interleave, merge}` ## 0.4.13 - Add `.minmax()` and `.minmax_by_key()`, iterator methods for finding both minimum and maximum in one scan. - Add `.format_default()`, a simpler version of `.format()` (lazy formatting for iterators). ## 0.4.12 - Add `.zip_eq()`, an adaptor like `.zip()` except it ensures iterators of inequal length don't pass silently (instead it panics). - Add `.fold_while()`, an iterator method that is a fold that can short-circuit. - Add `.partition_map()`, an iterator method that can separate elements into two collections. ## 0.4.11 - Add `.get()` for `Stride{,Mut}` and `.get_mut()` for `StrideMut` ## 0.4.10 - Improve performance of `.kmerge()` ## 0.4.9 - Add k-ary merge adaptor `.kmerge()` - Fix a bug in `.islice()` with ranges `a..b` where a `> b`. ## 0.4.8 - Implement `Clone`, `Debug` for `Linspace` ## 0.4.7 - Add function `diff_with()` that compares two iterators - Add `.combinations_n()`, an n-ary combinations iterator - Add methods `PutBack::with_value` and `PutBack::into_parts`. ## 0.4.6 - Add method `.sorted()` - Add module `itertools::free` with free function variants of common iterator adaptors and methods. For example `enumerate(iterable)`, `rev(iterable)`, and so on. ## 0.4.5 - Add `.flatten()` ## 0.4.4 - Allow composing `ZipSlices` with itself ## 0.4.3 - Write `iproduct!()` as a single expression; this allows temporary values in its arguments. ## 0.4.2 - Add `.fold_options()` - Require Rust 1.1 or later ## 0.4.1 - Update `.dropping()` to take advantage of `.nth()` ## 0.4.0 - `.merge()`, `.unique()` and `.dedup()` now perform better due to not using function pointers - Add free functions `enumerate()` and `rev()` - Breaking changes: - Return types of `.merge()` and `.merge_by()` renamed and changed - Method `Merge::new` removed - `.merge_by()` now takes a closure that returns bool. - Return type of `.dedup()` changed - Return type of `.mend_slices()` changed - Return type of `.unique()` changed - Removed function `times()`, struct `Times`: use a range instead - Removed deprecated macro `icompr!()` - Removed deprecated `FnMap` and method `.fn_map()`: use `.map_fn()` - `.interleave_shortest()` is no longer guaranteed to act like fused ## 0.3.25 - Rename `.sort_by()` to `.sorted_by()`. Old name is deprecated. - Fix well-formedness warnings from RFC 1214, no user visible impact ## 0.3.24 - Improve performance of `.merge()`'s ordering function slightly ## 0.3.23 - Added `.chunks()`, similar to (and based on) `.group_by_lazy()`. - Tweak linspace to match numpy.linspace and make it double ended. ## 0.3.22 - Added `ZipSlices`, a fast zip for slices ## 0.3.21 - Remove `Debug` impl for `Format`, it will have different use later ## 0.3.20 - Optimize `.group_by_lazy()` ## 0.3.19 - Added `.group_by_lazy()`, a possibly nonallocating group by - Added `.format()`, a nonallocating formatting helper for iterators - Remove uses of `RandomAccessIterator` since it has been deprecated in Rust. ## 0.3.17 - Added (adopted) `Unfold` from Rust ## 0.3.16 - Added adaptors `.unique()`, `.unique_by()` ## 0.3.15 - Added method `.sort_by()` ## 0.3.14 - Added adaptor `.while_some()` ## 0.3.13 - Added adaptor `.interleave_shortest()` - Added adaptor `.pad_using()` ## 0.3.11 - Added `assert_equal` function ## 0.3.10 - Bugfix `.combinations()` `size_hint`. ## 0.3.8 - Added source `RepeatCall` ## 0.3.7 - Added adaptor `PutBackN` - Added adaptor `.combinations()` ## 0.3.6 - Added `itertools::partition`, partition a sequence in place based on a predicate. - Deprecate `icompr!()` with no replacement. ## 0.3.5 - `.map_fn()` replaces deprecated `.fn_map()`. ## 0.3.4 - `.take_while_ref()` *by-ref adaptor* - `.coalesce()` *adaptor* - `.mend_slices()` *adaptor* ## 0.3.3 - `.dropping_back()` *method* - `.fold1()` *method* - `.is_empty_hint()` *method* itertools-0.13.0/CONTRIBUTING.md000064400000000000000000000211031046102023000141570ustar 00000000000000# Contributing to itertools We use stable Rust only. Please check the minimum version of Rust we use in `Cargo.toml`. _If you are proposing a major change to CI or a new iterator adaptor for this crate, then **please first file an issue** describing your proposal._ [Usual concerns about new methods](https://github.com/rust-itertools/itertools/issues/413#issuecomment-657670781). To pass CI tests successfully, your code must be free of "compiler warnings" and "clippy warnings" and be "rustfmt" formatted. Note that small PRs are easier to review and therefore are more easily merged. ## Write a new method/adaptor for `Itertools` trait In general, the code logic should be tested with [quickcheck](https://crates.io/crates/quickcheck) tests in `tests/quick.rs` which allow us to test properties about the code with randomly generated inputs. ### Behind `use_std`/`use_alloc` feature? If it needs the "std" (such as using hashes) then it should be behind the `use_std` feature, or if it requires heap allocation (such as using vectors) then it should be behind the `use_alloc` feature. Otherwise it should be able to run in `no_std` context. This mostly applies to your new module, each import from it, and to your new `Itertools` method. ### Pick the right receiver `self`, `&mut self` or `&self`? From [#710](https://github.com/rust-itertools/itertools/pull/710): - Take by value when: - It transfers ownership to another iterator type, such as `filter`, `map`... - It consumes the iterator completely, such as `count`, `last`, `max`... - Mutably borrow when it consumes only part of the iterator, such as `find`, `all`, `try_collect`... - Immutably borrow when there is no change, such as `size_hint`. ### Laziness Iterators are [lazy](https://doc.rust-lang.org/std/iter/index.html#laziness): - structs of iterator adaptors should have `#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]` ; - structs of iterators should have `#[must_use = "iterators are lazy and do nothing unless consumed"]`. Those behaviors are **tested** in `tests/laziness.rs`. ## Specialize `Iterator` methods It might be more performant to specialize some methods. However, each specialization should be thoroughly tested. Correctly specializing methods can be difficult, and _we do not require that you do it on your initial PR_. Most of the time, we want specializations of: - [`size_hint`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.size_hint): It mostly allows allocation optimizations. When always exact, it also enables to implement `ExactSizeIterator`. See our private module `src/size_hint.rs` for helpers. - [`fold`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold) might make iteration faster than calling `next` repeatedly. - [`count`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.count), [`last`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.last), [`nth`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.nth) as we might be able to avoid iterating on every item with `next`. Additionally, - `for_each`, `reduce`, `max/min[_by[_key]]` and `partition` all rely on `fold` so you should specialize it instead. - `all`, `any`, `find`, `find_map`, `cmp`, `partial_cmp`, `eq`, `ne`, `lt`, `le`, `gt`, `ge` and `position` all rely (by default) on `try_fold` which we can not specialize on stable rust, so you might want to wait it stabilizes or specialize each of them. - `DoubleEndedIterator::{nth_back, rfold, rfind}`: similar reasoning. An adaptor might use the inner iterator specializations for its own specializations. They are **tested** in `tests/specializations.rs` and **benchmarked** in `benches/specializations.rs` (build those benchmarks is slow so you might want to temporarily remove the ones you do not want to measure). ## Additional implementations ### The [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) implementation All our iterators should implement `Debug`. When one of the field is not debuggable (such as _functions_), you must not derive `Debug`. Instead, manually implement it and _ignore this field_ in our helper macro `debug_fmt_fields`.
4 examples (click to expand) ```rust use std::fmt; /* ===== Simple derive. ===== */ #[derive(Debug)] struct Name1 { iter: I, } /* ===== With an unclonable field. ===== */ struct Name2 { iter: I, func: F, } // No `F: Debug` bound and the field `func` is ignored. impl fmt::Debug for Name2 { // it defines the `fmt` function from a struct name and the fields you want to debug. debug_fmt_fields!(Name2, iter); } /* ===== With an unclonable field, but another bound to add. ===== */ struct Name3 { iter: I, item: Option, func: F, } // Same about `F` and `func`, similar about `I` but we must add the `I::Item: Debug` bound. impl fmt::Debug for Name3 where I::Item: fmt::Debug, { debug_fmt_fields!(Name3, iter, item); } /* ===== With an unclonable field for which we can provide some information. ===== */ struct Name4 { iter: I, func: Option, } // If ignore a field is not good enough, implement Debug fully manually. impl fmt::Debug for Name4 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let func = if self.func.is_some() { "Some(_)" } else { "None" }; f.debug_struct("Name4") .field("iter", &self.iter) .field("func", &func) .finish() } } ```
### When/How to implement [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html) All our iterators should implement `Clone` when possible. Note that a mutable reference is never clonable so `struct Name<'a, I: 'a> { iter: &'a mut I }` can not implement `Clone`. Derive `Clone` on a generic struct adds the bound `Clone` on each generic parameter. It might be an issue in which case you should manually implement it with our helper macro `clone_fields` (it defines the `clone` function calling `clone` on each field) and be careful about the bounds. ### When to implement [`std::iter::FusedIterator`](https://doc.rust-lang.org/std/iter/trait.FusedIterator.html) This trait should be implemented _by all iterators that always return `None` after returning `None` once_, because it allows to optimize `Iterator::fuse()`. The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, eventually refined to ensure it behaves in a fused way. ### When to implement [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html) _When we are always able to return an exact non-overflowing length._ Therefore, we do not implement it on adaptors that makes the iterator longer as the resulting length could overflow. One should not override `ExactSizeIterator::len` method but rely on an exact `Iterator::size_hint` implementation, meaning it returns `(length, Some(length))` (unless you could make `len` more performant than the default). The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure the size hint is exact. ### When to implement [`DoubleEndedIterator`](https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html) When the iterator structure allows to handle _iterating on both fronts simultaneously_. The iteration might stop in the middle when both fronts meet. The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure we can iterate on both fronts simultaneously. ### When to implement [`itertools::PeekingNext`](https://docs.rs/itertools/latest/itertools/trait.PeekingNext.html) TODO This is currently **tested** in `tests/test_std.rs`. ## About lending iterators TODO ## Other notes No guideline about using `#[inline]` yet. ### `.fold` / `.for_each` / `.try_fold` / `.try_for_each` In the Rust standard library, it's quite common for `fold` to be implemented in terms of `try_fold`. But it's not something we do yet because we can not specialize `try_fold` methods yet (it uses the unstable `Try`). From [#781](https://github.com/rust-itertools/itertools/pull/781), the general rule to follow is something like this: - If you need to completely consume an iterator: - Use `fold` if you need an _owned_ access to an accumulator. - Use `for_each` otherwise. - If you need to partly consume an iterator, the same applies with `try_` versions: - Use `try_fold` if you need an _owned_ access to an accumulator. - Use `try_for_each` otherwise. itertools-0.13.0/Cargo.lock0000644000000462210000000000100111210ustar # 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.13.0" dependencies = [ "criterion", "either", "paste", "permutohedron", "quickcheck", "rand", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" 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" itertools-0.13.0/Cargo.toml0000644000000036120000000000100111410ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # 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. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.43.1" name = "itertools" version = "0.13.0" authors = ["bluss"] description = "Extra iterator adaptors, iterator methods, free functions, and macros." documentation = "https://docs.rs/itertools/" readme = "README.md" 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" [profile.bench] debug = 2 [lib] test = false bench = false [[bench]] name = "tuple_combinations" harness = false [[bench]] name = "tuples" harness = false [[bench]] name = "fold_specialization" harness = false [[bench]] name = "combinations_with_replacement" harness = false [[bench]] name = "tree_reduce" harness = false [[bench]] name = "bench1" harness = false [[bench]] name = "combinations" harness = false [[bench]] name = "powerset" harness = false [[bench]] name = "specializations" harness = false [dependencies.either] version = "1.0" default-features = false [dev-dependencies.criterion] version = "0.4.0" [dev-dependencies.paste] version = "1.0.0" [dev-dependencies.permutohedron] version = "0.2" [dev-dependencies.quickcheck] version = "0.9" default_features = false [dev-dependencies.rand] version = "0.7" [features] default = ["use_std"] use_alloc = [] use_std = [ "use_alloc", "either/use_std", ] itertools-0.13.0/Cargo.toml.orig000064400000000000000000000026671046102023000146330ustar 00000000000000[package] name = "itertools" version = "0.13.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-itertools/itertools" documentation = "https://docs.rs/itertools/" authors = ["bluss"] readme = "README.md" description = "Extra iterator adaptors, iterator methods, free functions, and macros." keywords = ["iterator", "data-structure", "zip", "product"] categories = ["algorithms", "rust-patterns", "no-std", "no-std::no-alloc"] edition = "2018" # When bumping, please resolve all `#[allow(clippy::*)]` that are newly resolvable. rust-version = "1.43.1" [lib] bench = false test = false [dependencies] either = { version = "1.0", default-features = false } [dev-dependencies] rand = "0.7" criterion = "0.4.0" paste = "1.0.0" # Used in test_std to instantiate generic tests permutohedron = "0.2" quickcheck = { version = "0.9", default_features = false } [features] default = ["use_std"] use_std = ["use_alloc", "either/use_std"] use_alloc = [] [profile] bench = { debug = true } [[bench]] name = "tuple_combinations" harness = false [[bench]] name = "tuples" harness = false [[bench]] name = "fold_specialization" harness = false [[bench]] name = "combinations_with_replacement" harness = false [[bench]] name = "tree_reduce" harness = false [[bench]] name = "bench1" harness = false [[bench]] name = "combinations" harness = false [[bench]] name = "powerset" harness = false [[bench]] name = "specializations" harness = false itertools-0.13.0/LICENSE-APACHE000064400000000000000000000251371046102023000136650ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. itertools-0.13.0/LICENSE-MIT000064400000000000000000000020231046102023000133620ustar 00000000000000Copyright (c) 2015 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. itertools-0.13.0/README.md000064400000000000000000000016131046102023000132110ustar 00000000000000# Itertools Extra iterator adaptors, functions and macros. Please read the [API documentation here](https://docs.rs/itertools/). How to use with Cargo: ```toml [dependencies] itertools = "0.13.0" ``` How to use in your crate: ```rust 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. See our [CONTRIBUTING.md](https://github.com/rust-itertools/itertools/blob/master/CONTRIBUTING.md) for a detailed guide. ## License Dual-licensed to be compatible with the Rust project. Licensed under the Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 or the MIT license https://opensource.org/licenses/MIT, at your option. This file may not be copied, modified, or distributed except according to those terms. itertools-0.13.0/benches/bench1.rs000064400000000000000000000457731046102023000150660ustar 00000000000000use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; use itertools::free::cloned; use itertools::iproduct; use itertools::Itertools; use std::cmp; use std::iter::repeat; use std::ops::{Add, Range}; 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); } }) }); } 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); } }) }); } fn zip_default_zip(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("zip default zip", move |b| { b.iter(|| { for (&x, &y) in xs.iter().zip(&ys) { black_box(x); black_box(y); } }) }); } fn zipdot_i32_default_zip(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 default zip", move |b| { b.iter(|| { let mut s = 0; for (&x, &y) in xs.iter().zip(&ys) { s += x * y; } s }) }); } fn zipdot_f32_default_zip(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 default zip", move |b| { b.iter(|| { let mut s = 0.; for (&x, &y) in xs.iter().zip(&ys) { s += x * y; } s }) }); } fn zip_default_zip3(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; let zs = vec![0; 766]; let xs = black_box(xs); let ys = black_box(ys); let zs = black_box(zs); c.bench_function("zip default zip3", move |b| { b.iter(|| { for ((&x, &y), &z) in xs.iter().zip(&ys).zip(&zs) { black_box(x); black_box(y); black_box(z); } }) }); } fn zip_slices_ziptuple(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; c.bench_function("zip slices ziptuple", move |b| { b.iter(|| { let xs = black_box(&xs); let ys = black_box(&ys); for (&x, &y) in itertools::multizip((xs, ys)) { black_box(x); black_box(y); } }) }); } fn zip_checked_counted_loop(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("zip checked counted loop", move |b| { b.iter(|| { // Must slice to equal lengths, and then bounds checks are eliminated! let len = cmp::min(xs.len(), ys.len()); let xs = &xs[..len]; let ys = &ys[..len]; for i in 0..len { let x = xs[i]; let y = ys[i]; black_box(x); black_box(y); } }) }); } fn zipdot_i32_checked_counted_loop(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 checked counted loop", move |b| { b.iter(|| { // Must slice to equal lengths, and then bounds checks are eliminated! let len = cmp::min(xs.len(), ys.len()); let xs = &xs[..len]; let ys = &ys[..len]; let mut s = 0i32; for i in 0..len { s += xs[i] * ys[i]; } s }) }); } fn zipdot_f32_checked_counted_loop(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 checked counted loop", move |b| { b.iter(|| { // Must slice to equal lengths, and then bounds checks are eliminated! let len = cmp::min(xs.len(), ys.len()); let xs = &xs[..len]; let ys = &ys[..len]; let mut s = 0.; for i in 0..len { s += xs[i] * ys[i]; } s }) }); } fn zipdot_f32_checked_counted_unrolled_loop(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 checked counted unrolled loop", move |b| { b.iter(|| { // Must slice to equal lengths, and then bounds checks are eliminated! let len = cmp::min(xs.len(), ys.len()); let mut xs = &xs[..len]; let mut ys = &ys[..len]; let mut s = 0.; let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = (0., 0., 0., 0., 0., 0., 0., 0.); // how to unroll and have bounds checks eliminated (by cristicbz) // split sum into eight parts to enable vectorization (by bluss) while xs.len() >= 8 { p0 += xs[0] * ys[0]; p1 += xs[1] * ys[1]; p2 += xs[2] * ys[2]; p3 += xs[3] * ys[3]; p4 += xs[4] * ys[4]; p5 += xs[5] * ys[5]; p6 += xs[6] * ys[6]; p7 += xs[7] * ys[7]; xs = &xs[8..]; ys = &ys[8..]; } s += p0 + p4; s += p1 + p5; s += p2 + p6; s += p3 + p7; for i in 0..xs.len() { s += xs[i] * ys[i]; } s }) }); } fn zip_unchecked_counted_loop(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("zip unchecked counted loop", move |b| { b.iter(|| { 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); } } }) }); } fn zipdot_i32_unchecked_counted_loop(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 unchecked counted loop", move |b| { b.iter(|| { let len = cmp::min(xs.len(), ys.len()); 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; } } s }) }); } fn zipdot_f32_unchecked_counted_loop(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 f32 unchecked counted loop", move |b| { b.iter(|| { let len = cmp::min(xs.len(), ys.len()); 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; } } s }) }); } fn zip_unchecked_counted_loop3(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; let zs = vec![0; 766]; let xs = black_box(xs); let ys = black_box(ys); let zs = black_box(zs); c.bench_function("zip unchecked counted loop3", move |b| { b.iter(|| { 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); } } }) }); } 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; } let data = black_box(data); c.bench_function("chunk by lazy 1", move |b| { b.iter(|| { for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) { for elt in chunk { black_box(elt); } } }) }); } 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; } let data = black_box(data); c.bench_function("chunk by lazy 2", move |b| { b.iter(|| { for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) { for elt in chunk { black_box(elt); } } }) }); } fn slice_chunks(c: &mut Criterion) { let data = vec![0; 1024]; let data = black_box(data); let sz = black_box(10); c.bench_function("slice chunks", move |b| { b.iter(|| { for chunk in data.chunks(sz) { for elt in chunk { black_box(elt); } } }) }); } fn chunks_lazy_1(c: &mut Criterion) { let data = vec![0; 1024]; let data = black_box(data); let sz = black_box(10); c.bench_function("chunks lazy 1", move |b| { b.iter(|| { for chunk in &data.iter().chunks(sz) { for elt in chunk { black_box(elt); } } }) }); } fn equal(c: &mut Criterion) { let data = vec![7; 1024]; let l = data.len(); 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))); } 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; } let mut y = 0; for (i, elt) in data2.iter_mut().enumerate() { *elt += y; if i % 3 == 0 { y += 3; } else { y += 0; } } let data1 = black_box(data1); let data2 = black_box(data2); c.bench_function("merge default", move |b| { b.iter(|| data1.iter().merge(&data2).count()) }); } 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; } let mut y = 0; for (i, elt) in data2.iter_mut().enumerate() { *elt += y; if i % 3 == 0 { y += 3; } else { y += 0; } } let data1 = black_box(data1); let data2 = black_box(data2); c.bench_function("merge by cmp", move |b| { b.iter(|| data1.iter().merge_by(&data2, PartialOrd::le).count()) }); } 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; } let mut y = 0; for (i, elt) in data2.iter_mut().enumerate() { *elt += y; if i % 3 == 0 { y += 3; } else { y += 0; } } let data1 = black_box(data1); 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()) }); } 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; } let mut y = 0; for (i, elt) in data2.iter_mut().enumerate() { *elt += y; if i % 3 == 0 { y += 3; } else { y += 0; } } let data1 = black_box(data1); let data2 = black_box(data2); let its = &[data1.iter(), data2.iter()]; c.bench_function("kmerge default", move |b| { b.iter(|| its.iter().cloned().kmerge().count()) }); } fn kmerge_tenway(c: &mut Criterion) { let mut data = vec![0; 10240]; let mut state = 1729u16; fn rng(state: &mut u16) -> u16 { let new = state.wrapping_mul(31421).wrapping_add(6927); *state = new; new } for elt in &mut data { *elt = rng(&mut state); } let mut chunks = Vec::new(); let mut rest = &mut data[..]; 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); fst.sort(); chunks.push(fst.iter().cloned()); rest = tail; } // 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()) }); } fn fast_integer_sum(iter: I) -> I::Item where I: IntoIterator, I::Item: Default + Add, { iter.into_iter().fold(<_>::default(), |x, y| x + y) } 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)))) }); } 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)))) }); } 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))) }); } 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))) }); } 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, ) }); } fn cartesian_product_iterator(c: &mut Criterion) { let xs = vec![0; 16]; c.bench_function("cartesian product iterator", move |b| { b.iter(|| { let mut sum = 0; for (&x, &y, &z) in iproduct!(&xs, &xs, &xs) { 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]]; c.bench_function("multi cartesian product iterator", move |b| { b.iter(|| { let mut sum = 0; for x in xs.iter().multi_cartesian_product() { sum += x[0]; sum += x[1]; sum += x[2]; } sum }) }); } fn cartesian_product_nested_for(c: &mut Criterion) { let xs = vec![0; 16]; c.bench_function("cartesian product nested for", move |b| { b.iter(|| { let mut sum = 0; for &x in &xs { for &y in &xs { for &z in &xs { sum += x; sum += y; sum += z; } } } sum }) }); } 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())); } fn all_equal_for(c: &mut Criterion) { let mut xs = vec![0; 5_000_000]; xs.extend(vec![1; 5_000_000]); c.bench_function("all equal for", move |b| { b.iter(|| { for &x in &xs { if x != xs[0] { return false; } } true }) }); } fn all_equal_default(c: &mut Criterion) { let mut xs = vec![0; 5_000_000]; xs.extend(vec![1; 5_000_000]); c.bench_function("all equal default", move |b| { b.iter(|| xs.iter().dedup().nth(1).is_none()) }); } const PERM_COUNT: usize = 6; fn permutations_iter(c: &mut Criterion) { struct NewIterator(Range); impl Iterator for NewIterator { type Item = usize; fn next(&mut self) -> Option { self.0.next() } } c.bench_function("permutations iter", move |b| { 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) {}) }); } 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) {}) }); } criterion_group!( benches, slice_iter, slice_iter_rev, zip_default_zip, zipdot_i32_default_zip, zipdot_f32_default_zip, zip_default_zip3, zip_slices_ziptuple, zip_checked_counted_loop, zipdot_i32_checked_counted_loop, zipdot_f32_checked_counted_loop, zipdot_f32_checked_counted_unrolled_loop, zip_unchecked_counted_loop, zipdot_i32_unchecked_counted_loop, zipdot_f32_unchecked_counted_loop, zip_unchecked_counted_loop3, chunk_by_lazy_1, chunk_by_lazy_2, slice_chunks, chunks_lazy_1, equal, merge_default, merge_by_cmp, merge_by_lt, kmerge_default, kmerge_tenway, step_vec_2, step_vec_10, step_range_2, step_range_10, vec_iter_mut_partition, cartesian_product_iterator, multi_cartesian_product_iterator, cartesian_product_nested_for, all_equal, all_equal_for, all_equal_default, permutations_iter, permutations_range, permutations_slice, ); criterion_main!(benches); itertools-0.13.0/benches/combinations.rs000064400000000000000000000052461046102023000164020ustar 00000000000000use criterion::{black_box, criterion_group, criterion_main, Criterion}; use itertools::Itertools; // approximate 100_000 iterations for each combination const N1: usize = 100_000; const N2: usize = 448; const N3: usize = 86; const N4: usize = 41; const N14: usize = 21; fn comb_for1(c: &mut Criterion) { c.bench_function("comb for1", move |b| { b.iter(|| { for i in 0..N1 { black_box(vec![i]); } }) }); } fn comb_for2(c: &mut Criterion) { c.bench_function("comb for2", move |b| { b.iter(|| { for i in 0..N2 { for j in (i + 1)..N2 { black_box(vec![i, j]); } } }) }); } fn comb_for3(c: &mut Criterion) { c.bench_function("comb for3", move |b| { b.iter(|| { for i in 0..N3 { for j in (i + 1)..N3 { for k in (j + 1)..N3 { black_box(vec![i, j, k]); } } } }) }); } fn comb_for4(c: &mut Criterion) { c.bench_function("comb for4", move |b| { b.iter(|| { for i in 0..N4 { for j in (i + 1)..N4 { for k in (j + 1)..N4 { for l in (k + 1)..N4 { black_box(vec![i, j, k, l]); } } } } }) }); } fn comb_c1(c: &mut Criterion) { c.bench_function("comb c1", move |b| { b.iter(|| { for combo in (0..N1).combinations(1) { black_box(combo); } }) }); } fn comb_c2(c: &mut Criterion) { c.bench_function("comb c2", move |b| { b.iter(|| { for combo in (0..N2).combinations(2) { black_box(combo); } }) }); } fn comb_c3(c: &mut Criterion) { c.bench_function("comb c3", move |b| { b.iter(|| { for combo in (0..N3).combinations(3) { black_box(combo); } }) }); } fn comb_c4(c: &mut Criterion) { c.bench_function("comb c4", move |b| { b.iter(|| { for combo in (0..N4).combinations(4) { black_box(combo); } }) }); } fn comb_c14(c: &mut Criterion) { c.bench_function("comb c14", move |b| { b.iter(|| { for combo in (0..N14).combinations(14) { black_box(combo); } }) }); } criterion_group!( benches, comb_for1, comb_for2, comb_for3, comb_for4, comb_c1, comb_c2, comb_c3, comb_c4, comb_c14, ); criterion_main!(benches); itertools-0.13.0/benches/combinations_with_replacement.rs000064400000000000000000000017741046102023000220160ustar 00000000000000use criterion::{black_box, criterion_group, criterion_main, Criterion}; use itertools::Itertools; fn comb_replacement_n10_k5(c: &mut Criterion) { c.bench_function("comb replacement n10k5", move |b| { b.iter(|| { for i in (0..10).combinations_with_replacement(5) { black_box(i); } }) }); } fn comb_replacement_n5_k10(c: &mut Criterion) { c.bench_function("comb replacement n5 k10", move |b| { b.iter(|| { for i in (0..5).combinations_with_replacement(10) { black_box(i); } }) }); } fn comb_replacement_n10_k10(c: &mut Criterion) { c.bench_function("comb replacement n10 k10", move |b| { b.iter(|| { for i in (0..10).combinations_with_replacement(10) { black_box(i); } }) }); } criterion_group!( benches, comb_replacement_n10_k5, comb_replacement_n5_k10, comb_replacement_n10_k10, ); criterion_main!(benches); itertools-0.13.0/benches/fold_specialization.rs000064400000000000000000000035251046102023000177350ustar 00000000000000#![allow(unstable_name_collisions)] use criterion::{criterion_group, criterion_main, Criterion}; use itertools::Itertools; struct Unspecialized(I); impl Iterator for Unspecialized where I: Iterator, { type Item = I::Item; #[inline(always)] fn next(&mut self) -> Option { self.0.next() } #[inline(always)] fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } mod specialization { use super::*; pub mod intersperse { use super::*; pub fn external(c: &mut Criterion) { let arr = [1; 1024]; c.bench_function("external", move |b| { b.iter(|| { let mut sum = 0; for &x in arr.iter().intersperse(&0) { sum += x; } sum }) }); } 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) { 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) }) }); } } } criterion_group!( benches, specialization::intersperse::external, specialization::intersperse::internal_specialized, specialization::intersperse::internal_unspecialized, ); criterion_main!(benches); itertools-0.13.0/benches/powerset.rs000064400000000000000000000037171046102023000155660ustar 00000000000000use criterion::{black_box, criterion_group, criterion_main, Criterion}; use itertools::Itertools; // Keep aggregate generated elements the same, regardless of powerset length. const TOTAL_ELEMENTS: usize = 1 << 12; const fn calc_iters(n: usize) -> usize { TOTAL_ELEMENTS / (1 << n) } fn powerset_n(c: &mut Criterion, n: usize) { let id = format!("powerset {}", n); c.bench_function(id.as_str(), move |b| { b.iter(|| { for _ in 0..calc_iters(n) { for elt in (0..n).powerset() { black_box(elt); } } }) }); } 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_0(c: &mut Criterion) { powerset_n(c, 0); } fn powerset_1(c: &mut Criterion) { powerset_n(c, 1); } fn powerset_2(c: &mut Criterion) { powerset_n(c, 2); } fn powerset_4(c: &mut Criterion) { powerset_n(c, 4); } 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, powerset_0, powerset_1, powerset_2, 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); itertools-0.13.0/benches/specializations.rs000064400000000000000000000427151046102023000171200ustar 00000000000000#![allow(unstable_name_collisions, clippy::incompatible_msrv)] use criterion::black_box; use criterion::BenchmarkId; use itertools::Itertools; const NTH_INPUTS: &[usize] = &[0, 1, 2, 4, 8]; /// Create multiple functions each defining a benchmark group about iterator methods. /// /// Each created group has functions with the following ids: /// /// - `next`, `size_hint`, `count`, `last`, `nth`, `collect`, `fold` /// - and when marked as `DoubleEndedIterator`: `next_back`, `nth_back`, `rfold` /// - and when marked as `ExactSizeIterator`: `len` /// /// Note that this macro can be called only once. macro_rules! bench_specializations { ( $( $name:ident { $($extra:ident)* {$( $init:stmt; )*} $iterator:expr } )* ) => { $( #[allow(unused_must_use)] fn $name(c: &mut ::criterion::Criterion) { let mut bench_group = c.benchmark_group(stringify!($name)); $( $init )* let bench_first_its = { let mut bench_idx = 0; [0; 1000].map(|_| { let mut it = $iterator; if bench_idx != 0 { it.nth(bench_idx - 1); } bench_idx += 1; it }) }; bench_specializations!(@Iterator bench_group bench_first_its: $iterator); $( bench_specializations!(@$extra bench_group bench_first_its: $iterator); )* bench_group.finish(); } )* ::criterion::criterion_group!(benches, $($name, )*); ::criterion::criterion_main!(benches); }; (@Iterator $group:ident $first_its:ident: $iterator:expr) => { $group.bench_function("next", |bencher| bencher.iter(|| { let mut it = $iterator; while let Some(x) = it.next() { black_box(x); } })); $group.bench_function("size_hint", |bencher| bencher.iter(|| { $first_its.iter().for_each(|it| { black_box(it.size_hint()); }) })); $group.bench_function("count", |bencher| bencher.iter(|| { $iterator.count() })); $group.bench_function("last", |bencher| bencher.iter(|| { $iterator.last() })); for n in NTH_INPUTS { $group.bench_with_input(BenchmarkId::new("nth", n), n, |bencher, n| bencher.iter(|| { for start in 0_usize..10 { let mut it = $iterator; if let Some(s) = start.checked_sub(1) { black_box(it.nth(s)); } while let Some(x) = it.nth(*n) { black_box(x); } } })); } $group.bench_function("collect", |bencher| bencher.iter(|| { $iterator.collect::>() })); $group.bench_function("fold", |bencher| bencher.iter(|| { $iterator.fold((), |(), x| { black_box(x); }) })); }; (@DoubleEndedIterator $group:ident $_first_its:ident: $iterator:expr) => { $group.bench_function("next_back", |bencher| bencher.iter(|| { let mut it = $iterator; while let Some(x) = it.next_back() { black_box(x); } })); for n in NTH_INPUTS { $group.bench_with_input(BenchmarkId::new("nth_back", n), n, |bencher, n| bencher.iter(|| { for start in 0_usize..10 { let mut it = $iterator; if let Some(s) = start.checked_sub(1) { black_box(it.nth_back(s)); } while let Some(x) = it.nth_back(*n) { black_box(x); } } })); } $group.bench_function("rfold", |bencher| bencher.iter(|| { $iterator.rfold((), |(), x| { black_box(x); }) })); }; (@ExactSizeIterator $group:ident $first_its:ident: $_iterator:expr) => { $group.bench_function("len", |bencher| bencher.iter(|| { $first_its.iter().for_each(|it| { black_box(it.len()); }) })); }; } // Usage examples: // - For `ZipLongest::fold` only: // cargo bench --bench specializations zip_longest/fold // - For `.combinations(k).nth(8)`: // cargo bench --bench specializations combinations./nth/8 bench_specializations! { interleave { { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 768]); } v1.iter().interleave(&v2) } interleave_shortest { { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 768]); } v1.iter().interleave_shortest(&v2) } batching { { let v = black_box(vec![0; 1024]); } v.iter().batching(Iterator::next) } tuple_windows1 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuple_windows::<(_,)>() } tuple_windows2 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuple_windows::<(_, _)>() } tuple_windows3 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuple_windows::<(_, _, _)>() } tuple_windows4 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuple_windows::<(_, _, _, _)>() } circular_tuple_windows1 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().circular_tuple_windows::<(_,)>() } circular_tuple_windows2 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().circular_tuple_windows::<(_, _)>() } circular_tuple_windows3 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().circular_tuple_windows::<(_, _, _)>() } circular_tuple_windows4 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().circular_tuple_windows::<(_, _, _, _)>() } tuples1 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuples::<(_,)>() } tuples2 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuples::<(_, _)>() } tuples3 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuples::<(_, _, _)>() } tuples4 { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().tuples::<(_, _, _, _)>() } tuple_buffer { ExactSizeIterator { let v = black_box(vec![0; 11]); // Short but the buffer can't have 12 or more elements. } { let mut it = v.iter().tuples::<(_, _, _, _, _, _, _, _, _, _, _, _)>(); it.next(); // No element but it fills the buffer. it.into_buffer() } } cartesian_product { { let v = black_box(vec![0; 16]); } itertools::iproduct!(&v, &v, &v) } multi_cartesian_product { { let vs = black_box([0; 3].map(|_| vec![0; 16])); } vs.iter().multi_cartesian_product() } coalesce { { let v = black_box(vec![0; 1024]); } v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) }) } dedup { { let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); } v.iter().dedup() } dedup_by { { let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); } v.iter().dedup_by(PartialOrd::ge) } dedup_with_count { { let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); } v.iter().dedup_with_count() } dedup_by_with_count { { let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec()); } v.iter().dedup_by_with_count(PartialOrd::ge) } duplicates { DoubleEndedIterator { let v = black_box((0..32).cycle().take(1024).collect_vec()); } v.iter().duplicates() } duplicates_by { DoubleEndedIterator { let v = black_box((0..1024).collect_vec()); } v.iter().duplicates_by(|x| *x % 10) } unique { DoubleEndedIterator { let v = black_box((0..32).cycle().take(1024).collect_vec()); } v.iter().unique() } unique_by { DoubleEndedIterator { let v = black_box((0..1024).collect_vec()); } v.iter().unique_by(|x| *x % 50) } take_while_inclusive { { let v = black_box((0..1024).collect_vec()); } v.iter().take_while_inclusive(|x| **x < 1000) } pad_using { DoubleEndedIterator ExactSizeIterator { let v = black_box((0..1024).collect_vec()); } v.iter().copied().pad_using(2048, |i| 5 * i) } positions { DoubleEndedIterator { let v = black_box((0..1024).collect_vec()); } v.iter().positions(|x| x % 5 == 0) } update { DoubleEndedIterator ExactSizeIterator { let v = black_box((0_i32..1024).collect_vec()); } v.iter().copied().update(|x| *x *= 7) } tuple_combinations1 { { let v = black_box(vec![0; 1024]); } v.iter().tuple_combinations::<(_,)>() } tuple_combinations2 { { let v = black_box(vec![0; 64]); } v.iter().tuple_combinations::<(_, _)>() } tuple_combinations3 { { let v = black_box(vec![0; 64]); } v.iter().tuple_combinations::<(_, _, _)>() } tuple_combinations4 { { let v = black_box(vec![0; 64]); } v.iter().tuple_combinations::<(_, _, _, _)>() } intersperse { { let v = black_box(vec![0; 1024]); let n = black_box(0); } v.iter().intersperse(&n) } intersperse_with { { let v = black_box(vec![0; 1024]); let n = black_box(0); } v.iter().intersperse_with(|| &n) } combinations1 { { let v = black_box(vec![0; 1792]); } v.iter().combinations(1) } combinations2 { { let v = black_box(vec![0; 60]); } v.iter().combinations(2) } combinations3 { { let v = black_box(vec![0; 23]); } v.iter().combinations(3) } combinations4 { { let v = black_box(vec![0; 16]); } v.iter().combinations(4) } combinations_with_replacement1 { { let v = black_box(vec![0; 4096]); } v.iter().combinations_with_replacement(1) } combinations_with_replacement2 { { let v = black_box(vec![0; 90]); } v.iter().combinations_with_replacement(2) } combinations_with_replacement3 { { let v = black_box(vec![0; 28]); } v.iter().combinations_with_replacement(3) } combinations_with_replacement4 { { let v = black_box(vec![0; 16]); } v.iter().combinations_with_replacement(4) } permutations1 { { let v = black_box(vec![0; 1024]); } v.iter().permutations(1) } permutations2 { { let v = black_box(vec![0; 36]); } v.iter().permutations(2) } permutations3 { { let v = black_box(vec![0; 12]); } v.iter().permutations(3) } permutations4 { { let v = black_box(vec![0; 8]); } v.iter().permutations(4) } powerset { { let v = black_box(vec![0; 10]); } v.iter().powerset() } while_some { {} (0..) .map(black_box) .map(|i| char::from_digit(i, 16)) .while_some() } with_position { ExactSizeIterator { let v = black_box((0..10240).collect_vec()); } v.iter().with_position() } zip_longest { DoubleEndedIterator ExactSizeIterator { let xs = black_box(vec![0; 1024]); let ys = black_box(vec![0; 768]); } xs.iter().zip_longest(ys.iter()) } zip_eq { ExactSizeIterator { let v = black_box(vec![0; 1024]); } v.iter().zip_eq(v.iter().rev()) } multizip { DoubleEndedIterator ExactSizeIterator { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 768]); let v3 = black_box(vec![0; 2048]); } itertools::multizip((&v1, &v2, &v3)) } izip { DoubleEndedIterator ExactSizeIterator { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 768]); let v3 = black_box(vec![0; 2048]); } itertools::izip!(&v1, &v2, &v3) } put_back { { let v = black_box(vec![0; 1024]); } itertools::put_back(&v).with_value(black_box(&0)) } put_back_n { { let v1 = black_box(vec![0; 1024]); let v2 = black_box(vec![0; 16]); } { let mut it = itertools::put_back_n(&v1); for n in &v2 { it.put_back(n); } it } } exactly_one_error { ExactSizeIterator { let v = black_box(vec![0; 1024]); } // Use `at_most_one` would be similar. v.iter().exactly_one().unwrap_err() } multipeek { ExactSizeIterator { let v = black_box(vec![0; 1024]); let n = black_box(16); } { let mut it = v.iter().multipeek(); for _ in 0..n { it.peek(); } it } } peek_nth { ExactSizeIterator { let v = black_box(vec![0; 1024]); let n = black_box(16); } { let mut it = itertools::peek_nth(&v); it.peek_nth(n); it } } repeat_n { DoubleEndedIterator ExactSizeIterator {} itertools::repeat_n(black_box(0), black_box(1024)) } merge { { let v1 = black_box((0..1024).collect_vec()); let v2 = black_box((0..768).collect_vec()); } v1.iter().merge(&v2) } merge_by { { let v1 = black_box((0..1024).collect_vec()); let v2 = black_box((0..768).collect_vec()); } v1.iter().merge_by(&v2, PartialOrd::ge) } merge_join_by_ordering { { let v1 = black_box((0..1024).collect_vec()); let v2 = black_box((0..768).collect_vec()); } v1.iter().merge_join_by(&v2, Ord::cmp) } merge_join_by_bool { { let v1 = black_box((0..1024).collect_vec()); let v2 = black_box((0..768).collect_vec()); } v1.iter().merge_join_by(&v2, PartialOrd::ge) } kmerge { { let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]); } vs.iter().kmerge() } kmerge_by { { let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]); } vs.iter().kmerge_by(PartialOrd::ge) } map_into { DoubleEndedIterator ExactSizeIterator { let v = black_box(vec![0_u8; 1024]); } v.iter().copied().map_into::() } map_ok { DoubleEndedIterator ExactSizeIterator { let v = black_box((0_u32..1024) .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) }) .collect_vec()); } v.iter().copied().map_ok(|x| x + 1) } filter_ok { { let v = black_box((0_u32..1024) .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) }) .collect_vec()); } v.iter().copied().filter_ok(|x| x % 3 == 0) } filter_map_ok { { let v = black_box((0_u32..1024) .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) }) .collect_vec()); } v.iter().copied().filter_map_ok(|x| if x % 3 == 0 { Some(x + 1) } else { None }) } flatten_ok { DoubleEndedIterator { let d = black_box(vec![0; 8]); let v = black_box((0..512) .map(|x| if x % 2 == 0 { Ok(&d) } else { Err(x) }) .collect_vec()); } v.iter().copied().flatten_ok() } } itertools-0.13.0/benches/tree_reduce.rs000064400000000000000000000070151046102023000161770ustar 00000000000000#![allow(deprecated)] use criterion::{criterion_group, criterion_main, Criterion}; use itertools::{cloned, Itertools}; trait IterEx: Iterator { // Another efficient implementation against which to compare, // but needs `std` so is less desirable. fn tree_reduce_vec(self, mut f: F) -> Option where F: FnMut(Self::Item, Self::Item) -> Self::Item, Self: Sized, { let hint = self.size_hint().0; let cap = std::mem::size_of::() * 8 - hint.leading_zeros() as usize; let mut stack = Vec::with_capacity(cap); self.enumerate().for_each(|(mut i, mut x)| { while (i & 1) != 0 { x = f(stack.pop().unwrap(), x); i >>= 1; } stack.push(x); }); stack.into_iter().fold1(f) } } impl IterEx for T {} macro_rules! def_benchs { ($N:expr, $FUN:ident, $BENCH_NAME:ident, ) => { mod $BENCH_NAME { use super::*; pub fn sum(c: &mut Criterion) { let v: Vec = (0..$N).collect(); c.bench_function( &(stringify!($BENCH_NAME).replace('_', " ") + " sum"), move |b| b.iter(|| cloned(&v).$FUN(|x, y| x + y)), ); } pub fn complex_iter(c: &mut Criterion) { let u = (3..).take($N / 2); let v = (5..).take($N / 2); let it = u.chain(v); c.bench_function( &(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"), move |b| b.iter(|| it.clone().map(|x| x as f32).$FUN(f32::atan2)), ); } pub fn string_format(c: &mut Criterion) { // This goes quadratic with linear `fold1`, so use a smaller // size to not waste too much time in travis. The allocations // in here are so expensive anyway that it'll still take // way longer per iteration than the other two benchmarks. let v: Vec = (0..($N / 4)).collect(); c.bench_function( &(stringify!($BENCH_NAME).replace('_', " ") + " string format"), move |b| { b.iter(|| { cloned(&v) .map(|x| x.to_string()) .$FUN(|x, y| format!("{} + {}", x, y)) }) }, ); } } criterion_group!( $BENCH_NAME, $BENCH_NAME::sum, $BENCH_NAME::complex_iter, $BENCH_NAME::string_format, ); }; } def_benchs! { 10_000, fold1, fold1_10k, } def_benchs! { 10_000, tree_reduce, tree_reduce_stack_10k, } def_benchs! { 10_000, tree_reduce_vec, tree_reduce_vec_10k, } def_benchs! { 100, fold1, fold1_100, } def_benchs! { 100, tree_reduce, tree_reduce_stack_100, } def_benchs! { 100, tree_reduce_vec, tree_reduce_vec_100, } def_benchs! { 8, fold1, fold1_08, } def_benchs! { 8, tree_reduce, tree_reduce_stack_08, } def_benchs! { 8, tree_reduce_vec, tree_reduce_vec_08, } criterion_main!( fold1_10k, tree_reduce_stack_10k, tree_reduce_vec_10k, fold1_100, tree_reduce_stack_100, tree_reduce_vec_100, fold1_08, tree_reduce_stack_08, tree_reduce_vec_08, ); itertools-0.13.0/benches/tuple_combinations.rs000064400000000000000000000051521046102023000176070ustar 00000000000000use criterion::{black_box, criterion_group, criterion_main, Criterion}; use itertools::Itertools; // approximate 100_000 iterations for each combination const N1: usize = 100_000; const N2: usize = 448; const N3: usize = 86; const N4: usize = 41; fn tuple_comb_for1(c: &mut Criterion) { c.bench_function("tuple comb for1", move |b| { b.iter(|| { for i in 0..N1 { black_box(i); } }) }); } fn tuple_comb_for2(c: &mut Criterion) { c.bench_function("tuple comb for2", move |b| { b.iter(|| { for i in 0..N2 { for j in (i + 1)..N2 { black_box(i + j); } } }) }); } fn tuple_comb_for3(c: &mut Criterion) { c.bench_function("tuple comb for3", move |b| { b.iter(|| { for i in 0..N3 { for j in (i + 1)..N3 { for k in (j + 1)..N3 { black_box(i + j + k); } } } }) }); } fn tuple_comb_for4(c: &mut Criterion) { c.bench_function("tuple comb for4", move |b| { b.iter(|| { for i in 0..N4 { for j in (i + 1)..N4 { for k in (j + 1)..N4 { for l in (k + 1)..N4 { black_box(i + j + k + l); } } } } }) }); } fn tuple_comb_c1(c: &mut Criterion) { c.bench_function("tuple comb c1", move |b| { b.iter(|| { for (i,) in (0..N1).tuple_combinations() { black_box(i); } }) }); } fn tuple_comb_c2(c: &mut Criterion) { c.bench_function("tuple comb c2", move |b| { b.iter(|| { for (i, j) in (0..N2).tuple_combinations() { black_box(i + j); } }) }); } fn tuple_comb_c3(c: &mut Criterion) { c.bench_function("tuple comb c3", move |b| { b.iter(|| { for (i, j, k) in (0..N3).tuple_combinations() { black_box(i + j + k); } }) }); } fn tuple_comb_c4(c: &mut Criterion) { c.bench_function("tuple comb c4", move |b| { b.iter(|| { for (i, j, k, l) in (0..N4).tuple_combinations() { black_box(i + j + k + l); } }) }); } criterion_group!( benches, tuple_comb_for1, tuple_comb_for2, tuple_comb_for3, tuple_comb_for4, tuple_comb_c1, tuple_comb_c2, tuple_comb_c3, tuple_comb_c4, ); criterion_main!(benches); itertools-0.13.0/benches/tuples.rs000064400000000000000000000110201046102023000152140ustar 00000000000000use criterion::{criterion_group, criterion_main, Criterion}; use itertools::Itertools; fn s1(a: u32) -> u32 { a } fn s2(a: u32, b: u32) -> u32 { a + b } fn s3(a: u32, b: u32, c: u32) -> u32 { a + b + c } fn s4(a: u32, b: u32, c: u32, d: u32) -> u32 { a + b + c + d } fn sum_s1(s: &[u32]) -> u32 { s1(s[0]) } fn sum_s2(s: &[u32]) -> u32 { s2(s[0], s[1]) } fn sum_s3(s: &[u32]) -> u32 { s3(s[0], s[1], s[2]) } fn sum_s4(s: &[u32]) -> u32 { s4(s[0], s[1], s[2], s[3]) } fn sum_t1(s: &(&u32,)) -> u32 { s1(*s.0) } fn sum_t2(s: &(&u32, &u32)) -> u32 { s2(*s.0, *s.1) } fn sum_t3(s: &(&u32, &u32, &u32)) -> u32 { s3(*s.0, *s.1, *s.2) } fn sum_t4(s: &(&u32, &u32, &u32, &u32)) -> u32 { s4(*s.0, *s.1, *s.2, *s.3) } macro_rules! def_benchs { ($N:expr; $BENCH_GROUP:ident, $TUPLE_FUN:ident, $TUPLES:ident, $TUPLE_WINDOWS:ident; $SLICE_FUN:ident, $CHUNKS:ident, $WINDOWS:ident; $FOR_CHUNKS:ident, $FOR_WINDOWS:ident ) => { fn $FOR_CHUNKS(c: &mut Criterion) { let v: Vec = (0..$N * 1_000).collect(); let mut s = 0; c.bench_function(&stringify!($FOR_CHUNKS).replace('_', " "), move |b| { b.iter(|| { let mut j = 0; for _ in 0..1_000 { s += $SLICE_FUN(&v[j..(j + $N)]); j += $N; } s }) }); } fn $FOR_WINDOWS(c: &mut Criterion) { let v: Vec = (0..1_000).collect(); let mut s = 0; c.bench_function(&stringify!($FOR_WINDOWS).replace('_', " "), move |b| { b.iter(|| { for i in 0..(1_000 - $N) { s += $SLICE_FUN(&v[i..(i + $N)]); } s }) }); } fn $TUPLES(c: &mut Criterion) { let v: Vec = (0..$N * 1_000).collect(); let mut s = 0; c.bench_function(&stringify!($TUPLES).replace('_', " "), move |b| { b.iter(|| { for x in v.iter().tuples() { s += $TUPLE_FUN(&x); } s }) }); } fn $CHUNKS(c: &mut Criterion) { let v: Vec = (0..$N * 1_000).collect(); let mut s = 0; c.bench_function(&stringify!($CHUNKS).replace('_', " "), move |b| { b.iter(|| { for x in v.chunks($N) { s += $SLICE_FUN(x); } s }) }); } fn $TUPLE_WINDOWS(c: &mut Criterion) { let v: Vec = (0..1_000).collect(); let mut s = 0; c.bench_function(&stringify!($TUPLE_WINDOWS).replace('_', " "), move |b| { b.iter(|| { for x in v.iter().tuple_windows() { s += $TUPLE_FUN(&x); } s }) }); } fn $WINDOWS(c: &mut Criterion) { let v: Vec = (0..1_000).collect(); let mut s = 0; c.bench_function(&stringify!($WINDOWS).replace('_', " "), move |b| { b.iter(|| { for x in v.windows($N) { s += $SLICE_FUN(x); } s }) }); } criterion_group!( $BENCH_GROUP, $FOR_CHUNKS, $FOR_WINDOWS, $TUPLES, $CHUNKS, $TUPLE_WINDOWS, $WINDOWS, ); }; } def_benchs! { 1; benches_1, sum_t1, tuple_chunks_1, tuple_windows_1; sum_s1, slice_chunks_1, slice_windows_1; for_chunks_1, for_windows_1 } def_benchs! { 2; benches_2, sum_t2, tuple_chunks_2, tuple_windows_2; sum_s2, slice_chunks_2, slice_windows_2; for_chunks_2, for_windows_2 } def_benchs! { 3; benches_3, sum_t3, tuple_chunks_3, tuple_windows_3; sum_s3, slice_chunks_3, slice_windows_3; for_chunks_3, for_windows_3 } def_benchs! { 4; benches_4, sum_t4, tuple_chunks_4, tuple_windows_4; sum_s4, slice_chunks_4, slice_windows_4; for_chunks_4, for_windows_4 } criterion_main!(benches_1, benches_2, benches_3, benches_4,); itertools-0.13.0/examples/iris.data000064400000000000000000000107061046102023000153540ustar 000000000000005.1,3.5,1.4,0.2,Iris-setosa 4.9,3.0,1.4,0.2,Iris-setosa 4.7,3.2,1.3,0.2,Iris-setosa 4.6,3.1,1.5,0.2,Iris-setosa 5.0,3.6,1.4,0.2,Iris-setosa 5.4,3.9,1.7,0.4,Iris-setosa 4.6,3.4,1.4,0.3,Iris-setosa 5.0,3.4,1.5,0.2,Iris-setosa 4.4,2.9,1.4,0.2,Iris-setosa 4.9,3.1,1.5,0.1,Iris-setosa 5.4,3.7,1.5,0.2,Iris-setosa 4.8,3.4,1.6,0.2,Iris-setosa 4.8,3.0,1.4,0.1,Iris-setosa 4.3,3.0,1.1,0.1,Iris-setosa 5.8,4.0,1.2,0.2,Iris-setosa 5.7,4.4,1.5,0.4,Iris-setosa 5.4,3.9,1.3,0.4,Iris-setosa 5.1,3.5,1.4,0.3,Iris-setosa 5.7,3.8,1.7,0.3,Iris-setosa 5.1,3.8,1.5,0.3,Iris-setosa 5.4,3.4,1.7,0.2,Iris-setosa 5.1,3.7,1.5,0.4,Iris-setosa 4.6,3.6,1.0,0.2,Iris-setosa 5.1,3.3,1.7,0.5,Iris-setosa 4.8,3.4,1.9,0.2,Iris-setosa 5.0,3.0,1.6,0.2,Iris-setosa 5.0,3.4,1.6,0.4,Iris-setosa 5.2,3.5,1.5,0.2,Iris-setosa 5.2,3.4,1.4,0.2,Iris-setosa 4.7,3.2,1.6,0.2,Iris-setosa 4.8,3.1,1.6,0.2,Iris-setosa 5.4,3.4,1.5,0.4,Iris-setosa 5.2,4.1,1.5,0.1,Iris-setosa 5.5,4.2,1.4,0.2,Iris-setosa 4.9,3.1,1.5,0.1,Iris-setosa 5.0,3.2,1.2,0.2,Iris-setosa 5.5,3.5,1.3,0.2,Iris-setosa 4.9,3.1,1.5,0.1,Iris-setosa 4.4,3.0,1.3,0.2,Iris-setosa 5.1,3.4,1.5,0.2,Iris-setosa 5.0,3.5,1.3,0.3,Iris-setosa 4.5,2.3,1.3,0.3,Iris-setosa 4.4,3.2,1.3,0.2,Iris-setosa 5.0,3.5,1.6,0.6,Iris-setosa 5.1,3.8,1.9,0.4,Iris-setosa 4.8,3.0,1.4,0.3,Iris-setosa 5.1,3.8,1.6,0.2,Iris-setosa 4.6,3.2,1.4,0.2,Iris-setosa 5.3,3.7,1.5,0.2,Iris-setosa 5.0,3.3,1.4,0.2,Iris-setosa 7.0,3.2,4.7,1.4,Iris-versicolor 6.4,3.2,4.5,1.5,Iris-versicolor 6.9,3.1,4.9,1.5,Iris-versicolor 5.5,2.3,4.0,1.3,Iris-versicolor 6.5,2.8,4.6,1.5,Iris-versicolor 5.7,2.8,4.5,1.3,Iris-versicolor 6.3,3.3,4.7,1.6,Iris-versicolor 4.9,2.4,3.3,1.0,Iris-versicolor 6.6,2.9,4.6,1.3,Iris-versicolor 5.2,2.7,3.9,1.4,Iris-versicolor 5.0,2.0,3.5,1.0,Iris-versicolor 5.9,3.0,4.2,1.5,Iris-versicolor 6.0,2.2,4.0,1.0,Iris-versicolor 6.1,2.9,4.7,1.4,Iris-versicolor 5.6,2.9,3.6,1.3,Iris-versicolor 6.7,3.1,4.4,1.4,Iris-versicolor 5.6,3.0,4.5,1.5,Iris-versicolor 5.8,2.7,4.1,1.0,Iris-versicolor 6.2,2.2,4.5,1.5,Iris-versicolor 5.6,2.5,3.9,1.1,Iris-versicolor 5.9,3.2,4.8,1.8,Iris-versicolor 6.1,2.8,4.0,1.3,Iris-versicolor 6.3,2.5,4.9,1.5,Iris-versicolor 6.1,2.8,4.7,1.2,Iris-versicolor 6.4,2.9,4.3,1.3,Iris-versicolor 6.6,3.0,4.4,1.4,Iris-versicolor 6.8,2.8,4.8,1.4,Iris-versicolor 6.7,3.0,5.0,1.7,Iris-versicolor 6.0,2.9,4.5,1.5,Iris-versicolor 5.7,2.6,3.5,1.0,Iris-versicolor 5.5,2.4,3.8,1.1,Iris-versicolor 5.5,2.4,3.7,1.0,Iris-versicolor 5.8,2.7,3.9,1.2,Iris-versicolor 6.0,2.7,5.1,1.6,Iris-versicolor 5.4,3.0,4.5,1.5,Iris-versicolor 6.0,3.4,4.5,1.6,Iris-versicolor 6.7,3.1,4.7,1.5,Iris-versicolor 6.3,2.3,4.4,1.3,Iris-versicolor 5.6,3.0,4.1,1.3,Iris-versicolor 5.5,2.5,4.0,1.3,Iris-versicolor 5.5,2.6,4.4,1.2,Iris-versicolor 6.1,3.0,4.6,1.4,Iris-versicolor 5.8,2.6,4.0,1.2,Iris-versicolor 5.0,2.3,3.3,1.0,Iris-versicolor 5.6,2.7,4.2,1.3,Iris-versicolor 5.7,3.0,4.2,1.2,Iris-versicolor 5.7,2.9,4.2,1.3,Iris-versicolor 6.2,2.9,4.3,1.3,Iris-versicolor 5.1,2.5,3.0,1.1,Iris-versicolor 5.7,2.8,4.1,1.3,Iris-versicolor 6.3,3.3,6.0,2.5,Iris-virginica 5.8,2.7,5.1,1.9,Iris-virginica 7.1,3.0,5.9,2.1,Iris-virginica 6.3,2.9,5.6,1.8,Iris-virginica 6.5,3.0,5.8,2.2,Iris-virginica 7.6,3.0,6.6,2.1,Iris-virginica 4.9,2.5,4.5,1.7,Iris-virginica 7.3,2.9,6.3,1.8,Iris-virginica 6.7,2.5,5.8,1.8,Iris-virginica 7.2,3.6,6.1,2.5,Iris-virginica 6.5,3.2,5.1,2.0,Iris-virginica 6.4,2.7,5.3,1.9,Iris-virginica 6.8,3.0,5.5,2.1,Iris-virginica 5.7,2.5,5.0,2.0,Iris-virginica 5.8,2.8,5.1,2.4,Iris-virginica 6.4,3.2,5.3,2.3,Iris-virginica 6.5,3.0,5.5,1.8,Iris-virginica 7.7,3.8,6.7,2.2,Iris-virginica 7.7,2.6,6.9,2.3,Iris-virginica 6.0,2.2,5.0,1.5,Iris-virginica 6.9,3.2,5.7,2.3,Iris-virginica 5.6,2.8,4.9,2.0,Iris-virginica 7.7,2.8,6.7,2.0,Iris-virginica 6.3,2.7,4.9,1.8,Iris-virginica 6.7,3.3,5.7,2.1,Iris-virginica 7.2,3.2,6.0,1.8,Iris-virginica 6.2,2.8,4.8,1.8,Iris-virginica 6.1,3.0,4.9,1.8,Iris-virginica 6.4,2.8,5.6,2.1,Iris-virginica 7.2,3.0,5.8,1.6,Iris-virginica 7.4,2.8,6.1,1.9,Iris-virginica 7.9,3.8,6.4,2.0,Iris-virginica 6.4,2.8,5.6,2.2,Iris-virginica 6.3,2.8,5.1,1.5,Iris-virginica 6.1,2.6,5.6,1.4,Iris-virginica 7.7,3.0,6.1,2.3,Iris-virginica 6.3,3.4,5.6,2.4,Iris-virginica 6.4,3.1,5.5,1.8,Iris-virginica 6.0,3.0,4.8,1.8,Iris-virginica 6.9,3.1,5.4,2.1,Iris-virginica 6.7,3.1,5.6,2.4,Iris-virginica 6.9,3.1,5.1,2.3,Iris-virginica 5.8,2.7,5.1,1.9,Iris-virginica 6.8,3.2,5.9,2.3,Iris-virginica 6.7,3.3,5.7,2.5,Iris-virginica 6.7,3.0,5.2,2.3,Iris-virginica 6.3,2.5,5.0,1.9,Iris-virginica 6.5,3.0,5.2,2.0,Iris-virginica 6.2,3.4,5.4,2.3,Iris-virginica 5.9,3.0,5.1,1.8,Iris-virginica itertools-0.13.0/examples/iris.rs000064400000000000000000000075751046102023000151010ustar 00000000000000/// /// This example parses, sorts and groups the iris dataset /// 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: &str = include_str!("iris.data"); #[derive(Clone, Debug)] struct Iris { name: String, data: [f32; 4], } #[allow(dead_code)] // fields are currently ignored #[derive(Clone, Debug)] enum ParseError { Numeric(ParseFloatError), Other(&'static str), } impl From for ParseError { fn from(err: ParseFloatError) -> Self { Self::Numeric(err) } } /// Parse an Iris from a comma-separated line impl FromStr for Iris { type Err = ParseError; fn from_str(s: &str) -> Result { 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() { iris.data[index] = part.parse::()?; } if let Some(name) = parts.next() { iris.name = name.into(); } else { return Err(ParseError::Other("Missing name")); } Ok(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 mut irises = match irises { Err(e) => { println!("Error parsing: {:?}", e); std::process::exit(1); } Ok(data) => data, }; // Sort them and group them irises.sort_by(|a, b| Ord::cmp(&a.name, &b.name)); // using Iterator::cycle() let mut plot_symbols = "+ox".chars().cycle(); let mut symbolmap = HashMap::new(); // 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()); println!("{} (symbol={})", species, symbolmap[species]); 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 // // See https://en.wikipedia.org/wiki/Iris_flower_data_set // let n = 30; // plot size let mut plot = vec![' '; n * n]; // using Itertools::tuple_combinations for (a, b) in (0..4).tuple_combinations() { println!("Column {} vs {}:", a, b); // Clear plot // // using std::iter::repeat; // using Itertools::set_from plot.iter_mut().set_from(repeat(' ')); // using Itertools::minmax let min_max = |data: &[Iris], col| { data.iter() .map(|iris| iris.data[col]) .minmax() .into_option() .expect("Can't find min/max of empty iterator") }; let (min_x, max_x) = min_max(&irises, a); let (min_y, max_y) = min_max(&irises, b); // Plot the data points let round_to_grid = |x, min, max| ((x - min) / (max - min) * ((n - 1) as f32)) as usize; let flip = |ix| n - 1 - ix; // reverse axis direction for iris in &irises { let ix = round_to_grid(iris.data[a], min_x, max_x); let iy = flip(round_to_grid(iris.data[b], min_y, max_y)); plot[n * iy + ix] = symbolmap[&iris.name]; } // render plot // // using Itertools::join for line in plot.chunks(n) { println!("{}", line.iter().join(" ")) } } } itertools-0.13.0/src/adaptors/coalesce.rs000064400000000000000000000161161046102023000164660ustar 00000000000000use std::fmt; use std::iter::FusedIterator; use crate::size_hint; #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct CoalesceBy where I: Iterator, C: CountItem, { iter: I, /// `last` is `None` while no item have been taken out of `iter` (at definition). /// Then `last` will be `Some(Some(item))` until `iter` is exhausted, /// in which case `last` will be `Some(None)`. last: Option>, f: F, } impl Clone for CoalesceBy where I: Clone + Iterator, F: Clone, C: CountItem, C::CItem: Clone, { clone_fields!(last, iter, f); } impl fmt::Debug for CoalesceBy where I: Iterator + fmt::Debug, C: CountItem, C::CItem: fmt::Debug, { debug_fmt_fields!(CoalesceBy, iter, last); } pub trait CoalescePredicate { fn coalesce_pair(&mut self, t: T, item: Item) -> Result; } impl Iterator for CoalesceBy where I: Iterator, F: CoalescePredicate, C: CountItem, { type Item = C::CItem; fn next(&mut self) -> Option { let Self { iter, last, f } = self; // this fuses the iterator let init = match last { Some(elt) => elt.take(), None => { *last = Some(None); iter.next().map(C::new) } }?; Some( iter.try_fold(init, |accum, next| match f.coalesce_pair(accum, next) { Ok(joined) => Ok(joined), Err((last_, next_)) => { *last = Some(Some(next_)); Err(last_) } }) .unwrap_or_else(|x| x), ) } fn size_hint(&self) -> (usize, Option) { let (low, hi) = size_hint::add_scalar( self.iter.size_hint(), matches!(self.last, Some(Some(_))) as usize, ); ((low > 0) as usize, hi) } fn fold(self, acc: Acc, mut fn_acc: FnAcc) -> Acc where FnAcc: FnMut(Acc, Self::Item) -> Acc, { 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_)), } }); fn_acc(acc, last) } else { acc } } } impl FusedIterator for CoalesceBy where I: Iterator, F: CoalescePredicate, C: CountItem, { } pub struct NoCount; pub struct WithCount; pub trait CountItem { type CItem; fn new(t: T) -> Self::CItem; } impl CountItem for NoCount { type CItem = T; #[inline(always)] fn new(t: T) -> T { t } } impl CountItem for WithCount { type CItem = (usize, T); #[inline(always)] fn new(t: T) -> (usize, T) { (1, t) } } /// An iterator adaptor that may join together adjacent elements. /// /// See [`.coalesce()`](crate::Itertools::coalesce) for more information. pub type Coalesce = CoalesceBy; impl CoalescePredicate for F where F: FnMut(T, Item) -> Result, { fn coalesce_pair(&mut self, t: T, item: Item) -> Result { self(t, item) } } /// Create a new `Coalesce`. pub fn coalesce(iter: I, f: F) -> Coalesce where I: Iterator, { Coalesce { last: None, iter, f, } } /// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function. /// /// See [`.dedup_by()`](crate::Itertools::dedup_by) or [`.dedup()`](crate::Itertools::dedup) for more information. pub type DedupBy = CoalesceBy, NoCount>; #[derive(Clone)] pub struct DedupPred2CoalescePred(DP); impl fmt::Debug for DedupPred2CoalescePred { debug_fmt_fields!(DedupPred2CoalescePred,); } pub trait DedupPredicate { // TODO replace by Fn(&T, &T)->bool once Rust supports it fn dedup_pair(&mut self, a: &T, b: &T) -> bool; } impl CoalescePredicate for DedupPred2CoalescePred where DP: DedupPredicate, { fn coalesce_pair(&mut self, t: T, item: T) -> Result { if self.0.dedup_pair(&t, &item) { Ok(t) } else { Err((t, item)) } } } #[derive(Clone, Debug)] pub struct DedupEq; impl DedupPredicate for DedupEq { fn dedup_pair(&mut self, a: &T, b: &T) -> bool { a == b } } impl bool> DedupPredicate for F { fn dedup_pair(&mut self, a: &T, b: &T) -> bool { self(a, b) } } /// Create a new `DedupBy`. pub fn dedup_by(iter: I, dedup_pred: Pred) -> DedupBy where I: Iterator, { DedupBy { last: None, iter, f: DedupPred2CoalescePred(dedup_pred), } } /// An iterator adaptor that removes repeated duplicates. /// /// See [`.dedup()`](crate::Itertools::dedup) for more information. pub type Dedup = DedupBy; /// Create a new `Dedup`. pub fn dedup(iter: I) -> Dedup where I: Iterator, { dedup_by(iter, DedupEq) } /// An iterator adaptor that removes repeated duplicates, while keeping a count of how many /// repeated elements were present. This will determine equality using a comparison function. /// /// See [`.dedup_by_with_count()`](crate::Itertools::dedup_by_with_count) or /// [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information. pub type DedupByWithCount = CoalesceBy, WithCount>; #[derive(Clone, Debug)] pub struct DedupPredWithCount2CoalescePred(DP); impl CoalescePredicate for DedupPredWithCount2CoalescePred where DP: DedupPredicate, { fn coalesce_pair( &mut self, (c, t): (usize, T), item: T, ) -> Result<(usize, T), ((usize, T), (usize, T))> { if self.0.dedup_pair(&t, &item) { Ok((c + 1, t)) } else { Err(((c, t), (1, item))) } } } /// An iterator adaptor that removes repeated duplicates, while keeping a count of how many /// repeated elements were present. /// /// See [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information. pub type DedupWithCount = DedupByWithCount; /// Create a new `DedupByWithCount`. pub fn dedup_by_with_count(iter: I, dedup_pred: Pred) -> DedupByWithCount where I: Iterator, { DedupByWithCount { last: None, iter, f: DedupPredWithCount2CoalescePred(dedup_pred), } } /// Create a new `DedupWithCount`. pub fn dedup_with_count(iter: I) -> DedupWithCount where I: Iterator, { dedup_by_with_count(iter, DedupEq) } itertools-0.13.0/src/adaptors/map.rs000064400000000000000000000060231046102023000154610ustar 00000000000000use std::iter::FromIterator; use std::marker::PhantomData; #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MapSpecialCase { pub(crate) iter: I, pub(crate) f: F, } pub trait MapSpecialCaseFn { type Out; fn call(&mut self, t: T) -> Self::Out; } impl Iterator for MapSpecialCase where I: Iterator, R: MapSpecialCaseFn, { type Item = R::Out; fn next(&mut self) -> Option { self.iter.next().map(|i| self.f.call(i)) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } fn fold(self, init: Acc, mut fold_f: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, { let mut f = self.f; self.iter.fold(init, move |acc, v| fold_f(acc, f.call(v))) } fn collect(self) -> C where C: FromIterator, { let mut f = self.f; self.iter.map(move |v| f.call(v)).collect() } } impl DoubleEndedIterator for MapSpecialCase where I: DoubleEndedIterator, R: MapSpecialCaseFn, { fn next_back(&mut self) -> Option { self.iter.next_back().map(|i| self.f.call(i)) } } impl ExactSizeIterator for MapSpecialCase where I: ExactSizeIterator, R: MapSpecialCaseFn, { } /// An iterator adapter to apply a transformation within a nested `Result::Ok`. /// /// See [`.map_ok()`](crate::Itertools::map_ok) for more information. pub type MapOk = MapSpecialCase>; impl MapSpecialCaseFn> for MapSpecialCaseFnOk where F: FnMut(T) -> U, { type Out = Result; fn call(&mut self, t: Result) -> Self::Out { t.map(|v| self.0(v)) } } #[derive(Clone)] pub struct MapSpecialCaseFnOk(F); impl std::fmt::Debug for MapSpecialCaseFnOk { debug_fmt_fields!(MapSpecialCaseFnOk,); } /// Create a new `MapOk` iterator. pub fn map_ok(iter: I, f: F) -> MapOk where I: Iterator>, F: FnMut(T) -> U, { MapSpecialCase { iter, f: MapSpecialCaseFnOk(f), } } /// An iterator adapter to apply `Into` conversion to each element. /// /// See [`.map_into()`](crate::Itertools::map_into) for more information. pub type MapInto = MapSpecialCase>; impl, U> MapSpecialCaseFn for MapSpecialCaseFnInto { type Out = U; fn call(&mut self, t: T) -> Self::Out { t.into() } } pub struct MapSpecialCaseFnInto(PhantomData); impl std::fmt::Debug for MapSpecialCaseFnInto { debug_fmt_fields!(MapSpecialCaseFnInto, 0); } impl Clone for MapSpecialCaseFnInto { #[inline] fn clone(&self) -> Self { Self(PhantomData) } } /// Create a new [`MapInto`] iterator. pub fn map_into(iter: I) -> MapInto { MapSpecialCase { iter, f: MapSpecialCaseFnInto(PhantomData), } } itertools-0.13.0/src/adaptors/mod.rs000064400000000000000000000746061046102023000154770ustar 00000000000000//! Licensed under the Apache License, Version 2.0 //! or the MIT license //! , at your //! option. This file may not be copied, modified, or distributed //! except according to those terms. mod coalesce; pub(crate) mod map; mod multi_product; pub use self::coalesce::*; pub use self::map::{map_into, map_ok, MapInto, MapOk}; #[cfg(feature = "use_alloc")] pub use self::multi_product::*; use crate::size_hint::{self, SizeHint}; use std::fmt; use std::iter::{Enumerate, FromIterator, Fuse, FusedIterator}; use std::marker::PhantomData; /// An iterator adaptor that alternates elements from two iterators until both /// run out. /// /// This iterator is *fused*. /// /// See [`.interleave()`](crate::Itertools::interleave) for more information. #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Interleave { i: Fuse, j: Fuse, next_coming_from_j: bool, } /// Create an iterator that interleaves elements in `i` and `j`. /// /// [`IntoIterator`] enabled version of [`Itertools::interleave`](crate::Itertools::interleave). pub fn interleave( i: I, j: J, ) -> Interleave<::IntoIter, ::IntoIter> where I: IntoIterator, J: IntoIterator, { Interleave { i: i.into_iter().fuse(), j: j.into_iter().fuse(), next_coming_from_j: false, } } impl Iterator for Interleave where I: Iterator, J: Iterator, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { self.next_coming_from_j = !self.next_coming_from_j; if self.next_coming_from_j { match self.i.next() { None => self.j.next(), r => r, } } else { match self.j.next() { None => self.i.next(), r => r, } } } fn size_hint(&self) -> (usize, Option) { size_hint::add(self.i.size_hint(), self.j.size_hint()) } fn fold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { let Self { mut i, mut j, next_coming_from_j, } = self; if next_coming_from_j { match j.next() { Some(y) => init = f(init, y), None => return i.fold(init, f), } } let res = i.try_fold(init, |mut acc, x| { acc = f(acc, x); match j.next() { Some(y) => Ok(f(acc, y)), None => Err(acc), } }); match res { Ok(acc) => j.fold(acc, f), Err(acc) => i.fold(acc, f), } } } impl FusedIterator for Interleave where I: Iterator, J: Iterator, { } /// An iterator adaptor that alternates elements from the two iterators until /// one of them runs out. /// /// This iterator is *fused*. /// /// See [`.interleave_shortest()`](crate::Itertools::interleave_shortest) /// for more information. #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct InterleaveShortest where I: Iterator, J: Iterator, { i: I, j: J, next_coming_from_j: bool, } /// Create a new `InterleaveShortest` iterator. pub fn interleave_shortest(i: I, j: J) -> InterleaveShortest where I: Iterator, J: Iterator, { InterleaveShortest { i, j, next_coming_from_j: false, } } impl Iterator for InterleaveShortest where I: Iterator, J: Iterator, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { let e = if self.next_coming_from_j { self.j.next() } else { self.i.next() }; if e.is_some() { self.next_coming_from_j = !self.next_coming_from_j; } e } #[inline] fn size_hint(&self) -> (usize, Option) { let (curr_hint, next_hint) = { let i_hint = self.i.size_hint(); let j_hint = self.j.size_hint(); if self.next_coming_from_j { (j_hint, i_hint) } else { (i_hint, j_hint) } }; let (curr_lower, curr_upper) = curr_hint; let (next_lower, next_upper) = next_hint; let (combined_lower, combined_upper) = size_hint::mul_scalar(size_hint::min(curr_hint, next_hint), 2); let lower = if curr_lower > next_lower { combined_lower + 1 } else { combined_lower }; let upper = { let extra_elem = match (curr_upper, next_upper) { (_, None) => false, (None, Some(_)) => true, (Some(curr_max), Some(next_max)) => curr_max > next_max, }; if extra_elem { combined_upper.and_then(|x| x.checked_add(1)) } else { combined_upper } }; (lower, upper) } fn fold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { let Self { mut i, mut j, next_coming_from_j, } = self; if next_coming_from_j { match j.next() { Some(y) => init = f(init, y), None => return init, } } let res = i.try_fold(init, |mut acc, x| { acc = f(acc, x); match j.next() { Some(y) => Ok(f(acc, y)), None => Err(acc), } }); match res { Ok(val) => val, Err(val) => val, } } } impl FusedIterator for InterleaveShortest where I: FusedIterator, J: FusedIterator, { } #[derive(Clone, Debug)] /// An iterator adaptor that allows putting back a single /// item to the front of the iterator. /// /// Iterator element type is `I::Item`. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct PutBack where I: Iterator, { top: Option, iter: I, } /// Create an iterator where you can put back a single item pub fn put_back(iterable: I) -> PutBack where I: IntoIterator, { PutBack { top: None, iter: iterable.into_iter(), } } impl PutBack where I: Iterator, { /// put back value `value` (builder method) pub fn with_value(mut self, value: I::Item) -> Self { self.put_back(value); self } /// Split the `PutBack` into its parts. #[inline] pub fn into_parts(self) -> (Option, I) { let Self { top, iter } = self; (top, iter) } /// Put back a single value to the front of the iterator. /// /// If a value is already in the put back slot, it is returned. #[inline] pub fn put_back(&mut self, x: I::Item) -> Option { self.top.replace(x) } } impl Iterator for PutBack where I: Iterator, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { match self.top { None => self.iter.next(), ref mut some => some.take(), } } #[inline] fn size_hint(&self) -> (usize, Option) { // Not ExactSizeIterator because size may be larger than usize size_hint::add_scalar(self.iter.size_hint(), self.top.is_some() as usize) } fn count(self) -> usize { self.iter.count() + (self.top.is_some() as usize) } fn last(self) -> Option { self.iter.last().or(self.top) } fn nth(&mut self, n: usize) -> Option { match self.top { None => self.iter.nth(n), ref mut some => { if n == 0 { some.take() } else { *some = None; self.iter.nth(n - 1) } } } } fn all(&mut self, mut f: G) -> bool where G: FnMut(Self::Item) -> bool, { if let Some(elt) = self.top.take() { if !f(elt) { return false; } } self.iter.all(f) } fn fold(mut self, init: Acc, mut f: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc, { let mut accum = init; if let Some(elt) = self.top.take() { accum = f(accum, elt); } self.iter.fold(accum, f) } } #[derive(Debug, Clone)] /// An iterator adaptor that iterates over the cartesian product of /// the element sets of two iterators `I` and `J`. /// /// Iterator element type is `(I::Item, J::Item)`. /// /// See [`.cartesian_product()`](crate::Itertools::cartesian_product) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Product where I: Iterator, { a: I, /// `a_cur` is `None` while no item have been taken out of `a` (at definition). /// Then `a_cur` will be `Some(Some(item))` until `a` is exhausted, /// in which case `a_cur` will be `Some(None)`. a_cur: Option>, b: J, b_orig: J, } /// Create a new cartesian product iterator /// /// Iterator element type is `(I::Item, J::Item)`. pub fn cartesian_product(i: I, j: J) -> Product where I: Iterator, J: Clone + Iterator, I::Item: Clone, { Product { a_cur: None, a: i, b: j.clone(), b_orig: j, } } impl Iterator for Product where I: Iterator, J: Clone + Iterator, I::Item: Clone, { type Item = (I::Item, J::Item); fn next(&mut self) -> Option { let Self { a, a_cur, b, b_orig, } = self; let elt_b = match b.next() { None => { *b = b_orig.clone(); match b.next() { None => return None, Some(x) => { *a_cur = Some(a.next()); x } } } Some(x) => x, }; a_cur .get_or_insert_with(|| a.next()) .as_ref() .map(|a| (a.clone(), elt_b)) } fn size_hint(&self) -> (usize, Option) { // Not ExactSizeIterator because size may be larger than usize // Compute a * b_orig + b for both lower and upper bound let mut sh = size_hint::mul(self.a.size_hint(), self.b_orig.size_hint()); if matches!(self.a_cur, Some(Some(_))) { sh = size_hint::add(sh, self.b.size_hint()); } sh } fn fold(self, mut accum: Acc, mut f: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc, { // use a split loop to handle the loose a_cur as well as avoiding to // clone b_orig at the end. let Self { mut a, a_cur, mut b, b_orig, } = self; if let Some(mut elt_a) = a_cur.unwrap_or_else(|| a.next()) { loop { accum = b.fold(accum, |acc, elt| f(acc, (elt_a.clone(), elt))); // we can only continue iterating a if we had a first element; if let Some(next_elt_a) = a.next() { b = b_orig.clone(); elt_a = next_elt_a; } else { break; } } } accum } } impl FusedIterator for Product where I: FusedIterator, J: Clone + FusedIterator, I::Item: Clone, { } /// A “meta iterator adaptor”. Its closure receives a reference to the iterator /// and may pick off as many elements as it likes, to produce the next iterator element. /// /// Iterator element type is `X` if the return type of `F` is `Option`. /// /// See [`.batching()`](crate::Itertools::batching) for more information. #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Batching { f: F, iter: I, } impl fmt::Debug for Batching where I: fmt::Debug, { debug_fmt_fields!(Batching, iter); } /// Create a new Batching iterator. pub fn batching(iter: I, f: F) -> Batching { Batching { f, iter } } impl Iterator for Batching where I: Iterator, F: FnMut(&mut I) -> Option, { type Item = B; #[inline] fn next(&mut self) -> Option { (self.f)(&mut self.iter) } } /// An iterator adaptor that borrows from a `Clone`-able iterator /// to only pick off elements while the predicate returns `true`. /// /// See [`.take_while_ref()`](crate::Itertools::take_while_ref) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct TakeWhileRef<'a, I: 'a, F> { iter: &'a mut I, f: F, } impl<'a, I, F> fmt::Debug for TakeWhileRef<'a, I, F> where I: Iterator + fmt::Debug, { debug_fmt_fields!(TakeWhileRef, iter); } /// Create a new `TakeWhileRef` from a reference to clonable iterator. pub fn take_while_ref(iter: &mut I, f: F) -> TakeWhileRef where I: Iterator + Clone, { TakeWhileRef { iter, f } } impl<'a, I, F> Iterator for TakeWhileRef<'a, I, F> where I: Iterator + Clone, F: FnMut(&I::Item) -> bool, { type Item = I::Item; fn next(&mut self) -> Option { let old = self.iter.clone(); match self.iter.next() { None => None, Some(elt) => { if (self.f)(&elt) { Some(elt) } else { *self.iter = old; None } } } } fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) } } /// An iterator adaptor that filters `Option
` iterator elements /// and produces `A`. Stops on the first `None` encountered. /// /// See [`.while_some()`](crate::Itertools::while_some) for more information. #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct WhileSome { iter: I, } /// Create a new `WhileSome`. pub fn while_some(iter: I) -> WhileSome { WhileSome { iter } } impl Iterator for WhileSome where I: Iterator>, { type Item = A; fn next(&mut self) -> Option { match self.iter.next() { None | Some(None) => None, Some(elt) => elt, } } fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) } fn fold(mut self, acc: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { let res = self.iter.try_fold(acc, |acc, item| match item { Some(item) => Ok(f(acc, item)), None => Err(acc), }); match res { Ok(val) => val, Err(val) => val, } } } /// An iterator to iterate through all combinations in a `Clone`-able iterator that produces tuples /// of a specific size. /// /// See [`.tuple_combinations()`](crate::Itertools::tuple_combinations) for more /// information. #[derive(Clone, Debug)] #[must_use = "this iterator adaptor is not lazy but does nearly nothing unless consumed"] pub struct TupleCombinations where I: Iterator, T: HasCombination, { iter: T::Combination, _mi: PhantomData, } pub trait HasCombination: Sized { type Combination: From + Iterator; } /// Create a new `TupleCombinations` from a clonable iterator. pub fn tuple_combinations(iter: I) -> TupleCombinations where I: Iterator + Clone, I::Item: Clone, T: HasCombination, { TupleCombinations { iter: T::Combination::from(iter), _mi: PhantomData, } } impl Iterator for TupleCombinations where I: Iterator, T: HasCombination, { type Item = T; fn next(&mut self) -> Option { self.iter.next() } fn size_hint(&self) -> SizeHint { self.iter.size_hint() } fn count(self) -> usize { self.iter.count() } fn fold(self, init: B, f: F) -> B where F: FnMut(B, Self::Item) -> B, { self.iter.fold(init, f) } } impl FusedIterator for TupleCombinations where I: FusedIterator, T: HasCombination, { } #[derive(Clone, Debug)] pub struct Tuple1Combination { iter: I, } impl From for Tuple1Combination { fn from(iter: I) -> Self { Self { iter } } } impl Iterator for Tuple1Combination { type Item = (I::Item,); fn next(&mut self) -> Option { self.iter.next().map(|x| (x,)) } fn size_hint(&self) -> SizeHint { self.iter.size_hint() } fn count(self) -> usize { self.iter.count() } fn fold(self, init: B, f: F) -> B where F: FnMut(B, Self::Item) -> B, { self.iter.map(|x| (x,)).fold(init, f) } } impl HasCombination for (I::Item,) { type Combination = Tuple1Combination; } macro_rules! impl_tuple_combination { ($C:ident $P:ident ; $($X:ident)*) => ( #[derive(Clone, Debug)] pub struct $C { item: Option, iter: I, c: $P, } impl From for $C { fn from(mut iter: I) -> Self { Self { item: iter.next(), iter: iter.clone(), c: iter.into(), } } } impl From for $C> { fn from(iter: I) -> Self { Self::from(iter.fuse()) } } impl Iterator for $C where I: Iterator + Clone, A: Clone, { type Item = (A, $(ignore_ident!($X, A)),*); fn next(&mut self) -> Option { if let Some(($($X,)*)) = self.c.next() { let z = self.item.clone().unwrap(); Some((z, $($X),*)) } else { self.item = self.iter.next(); self.item.clone().and_then(|z| { self.c = self.iter.clone().into(); self.c.next().map(|($($X,)*)| (z, $($X),*)) }) } } fn size_hint(&self) -> SizeHint { const K: usize = 1 + count_ident!($($X)*); let (mut n_min, mut n_max) = self.iter.size_hint(); n_min = checked_binomial(n_min, K).unwrap_or(usize::MAX); n_max = n_max.and_then(|n| checked_binomial(n, K)); size_hint::add(self.c.size_hint(), (n_min, n_max)) } fn count(self) -> usize { const K: usize = 1 + count_ident!($($X)*); let n = self.iter.count(); checked_binomial(n, K).unwrap() + self.c.count() } fn fold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { let Self { c, item, mut iter } = self; if let Some(z) = item.as_ref() { init = c .map(|($($X,)*)| (z.clone(), $($X),*)) .fold(init, &mut f); } while let Some(z) = iter.next() { let c: $P = iter.clone().into(); init = c .map(|($($X,)*)| (z.clone(), $($X),*)) .fold(init, &mut f); } init } } impl HasCombination for (A, $(ignore_ident!($X, A)),*) where I: Iterator + Clone, I::Item: Clone { type Combination = $C>; } ) } // This snippet generates the twelve `impl_tuple_combination!` invocations: // use core::iter; // use itertools::Itertools; // // for i in 2..=12 { // println!("impl_tuple_combination!(Tuple{arity}Combination Tuple{prev}Combination; {idents});", // arity = i, // prev = i - 1, // idents = ('a'..'z').take(i - 1).join(" "), // ); // } // It could probably be replaced by a bit more macro cleverness. impl_tuple_combination!(Tuple2Combination Tuple1Combination; a); impl_tuple_combination!(Tuple3Combination Tuple2Combination; a b); impl_tuple_combination!(Tuple4Combination Tuple3Combination; a b c); impl_tuple_combination!(Tuple5Combination Tuple4Combination; a b c d); impl_tuple_combination!(Tuple6Combination Tuple5Combination; a b c d e); impl_tuple_combination!(Tuple7Combination Tuple6Combination; a b c d e f); impl_tuple_combination!(Tuple8Combination Tuple7Combination; a b c d e f g); impl_tuple_combination!(Tuple9Combination Tuple8Combination; a b c d e f g h); impl_tuple_combination!(Tuple10Combination Tuple9Combination; a b c d e f g h i); impl_tuple_combination!(Tuple11Combination Tuple10Combination; a b c d e f g h i j); impl_tuple_combination!(Tuple12Combination Tuple11Combination; a b c d e f g h i j k); // https://en.wikipedia.org/wiki/Binomial_coefficient#In_programming_languages pub(crate) fn checked_binomial(mut n: usize, mut k: usize) -> Option { if n < k { return Some(0); } // `factorial(n) / factorial(n - k) / factorial(k)` but trying to avoid it overflows: k = (n - k).min(k); // symmetry let mut c = 1; for i in 1..=k { c = (c / i) .checked_mul(n)? .checked_add((c % i).checked_mul(n)? / i)?; n -= 1; } Some(c) } #[test] fn test_checked_binomial() { // With the first row: [1, 0, 0, ...] and the first column full of 1s, we check // row by row the recurrence relation of binomials (which is an equivalent definition). // For n >= 1 and k >= 1 we have: // binomial(n, k) == binomial(n - 1, k - 1) + binomial(n - 1, k) const LIMIT: usize = 500; let mut row = vec![Some(0); LIMIT + 1]; row[0] = Some(1); for n in 0..=LIMIT { for k in 0..=LIMIT { assert_eq!(row[k], checked_binomial(n, k)); } row = std::iter::once(Some(1)) .chain((1..=LIMIT).map(|k| row[k - 1]?.checked_add(row[k]?))) .collect(); } } /// An iterator adapter to filter values within a nested `Result::Ok`. /// /// See [`.filter_ok()`](crate::Itertools::filter_ok) for more information. #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct FilterOk { iter: I, f: F, } impl fmt::Debug for FilterOk where I: fmt::Debug, { debug_fmt_fields!(FilterOk, iter); } /// Create a new `FilterOk` iterator. pub fn filter_ok(iter: I, f: F) -> FilterOk where I: Iterator>, F: FnMut(&T) -> bool, { FilterOk { iter, f } } impl Iterator for FilterOk where I: Iterator>, F: FnMut(&T) -> bool, { type Item = Result; fn next(&mut self) -> Option { let f = &mut self.f; self.iter.find(|res| match res { Ok(t) => f(t), _ => true, }) } fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) } fn fold(self, init: Acc, fold_f: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, { let mut f = self.f; self.iter .filter(|v| v.as_ref().map(&mut f).unwrap_or(true)) .fold(init, fold_f) } fn collect(self) -> C where C: FromIterator, { let mut f = self.f; self.iter .filter(|v| v.as_ref().map(&mut f).unwrap_or(true)) .collect() } } impl FusedIterator for FilterOk where I: FusedIterator>, F: FnMut(&T) -> bool, { } /// An iterator adapter to filter and apply a transformation on values within a nested `Result::Ok`. /// /// See [`.filter_map_ok()`](crate::Itertools::filter_map_ok) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Clone)] pub struct FilterMapOk { iter: I, f: F, } impl fmt::Debug for FilterMapOk where I: fmt::Debug, { debug_fmt_fields!(FilterMapOk, iter); } fn transpose_result(result: Result, E>) -> Option> { match result { Ok(Some(v)) => Some(Ok(v)), Ok(None) => None, Err(e) => Some(Err(e)), } } /// Create a new `FilterOk` iterator. pub fn filter_map_ok(iter: I, f: F) -> FilterMapOk where I: Iterator>, F: FnMut(T) -> Option, { FilterMapOk { iter, f } } impl Iterator for FilterMapOk where I: Iterator>, F: FnMut(T) -> Option, { type Item = Result; fn next(&mut self) -> Option { let f = &mut self.f; self.iter.find_map(|res| match res { Ok(t) => f(t).map(Ok), Err(e) => Some(Err(e)), }) } fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) } fn fold(self, init: Acc, fold_f: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, { let mut f = self.f; self.iter .filter_map(|v| transpose_result(v.map(&mut f))) .fold(init, fold_f) } fn collect(self) -> C where C: FromIterator, { let mut f = self.f; self.iter .filter_map(|v| transpose_result(v.map(&mut f))) .collect() } } impl FusedIterator for FilterMapOk where I: FusedIterator>, F: FnMut(T) -> Option, { } /// An iterator adapter to get the positions of each element that matches a predicate. /// /// See [`.positions()`](crate::Itertools::positions) for more information. #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Positions { iter: Enumerate, f: F, } impl fmt::Debug for Positions where I: fmt::Debug, { debug_fmt_fields!(Positions, iter); } /// Create a new `Positions` iterator. pub fn positions(iter: I, f: F) -> Positions where I: Iterator, F: FnMut(I::Item) -> bool, { let iter = iter.enumerate(); Positions { iter, f } } impl Iterator for Positions where I: Iterator, F: FnMut(I::Item) -> bool, { type Item = usize; fn next(&mut self) -> Option { let f = &mut self.f; // TODO: once MSRV >= 1.62, use `then_some`. self.iter .find_map(|(count, val)| if f(val) { Some(count) } else { None }) } fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) } fn fold(self, init: B, mut func: G) -> B where G: FnMut(B, Self::Item) -> B, { let mut f = self.f; self.iter.fold(init, |mut acc, (count, val)| { if f(val) { acc = func(acc, count); } acc }) } } impl DoubleEndedIterator for Positions where I: DoubleEndedIterator + ExactSizeIterator, F: FnMut(I::Item) -> bool, { fn next_back(&mut self) -> Option { let f = &mut self.f; // TODO: once MSRV >= 1.62, use `then_some`. self.iter .by_ref() .rev() .find_map(|(count, val)| if f(val) { Some(count) } else { None }) } fn rfold(self, init: B, mut func: G) -> B where G: FnMut(B, Self::Item) -> B, { let mut f = self.f; self.iter.rfold(init, |mut acc, (count, val)| { if f(val) { acc = func(acc, count); } acc }) } } impl FusedIterator for Positions where I: FusedIterator, F: FnMut(I::Item) -> bool, { } /// An iterator adapter to apply a mutating function to each element before yielding it. /// /// See [`.update()`](crate::Itertools::update) for more information. #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Update { iter: I, f: F, } impl fmt::Debug for Update where I: fmt::Debug, { debug_fmt_fields!(Update, iter); } /// Create a new `Update` iterator. pub fn update(iter: I, f: F) -> Update where I: Iterator, F: FnMut(&mut I::Item), { Update { iter, f } } impl Iterator for Update where I: Iterator, F: FnMut(&mut I::Item), { type Item = I::Item; fn next(&mut self) -> Option { if let Some(mut v) = self.iter.next() { (self.f)(&mut v); Some(v) } else { None } } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } fn fold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc, { let mut f = self.f; self.iter.fold(init, move |acc, mut v| { f(&mut v); g(acc, v) }) } // if possible, re-use inner iterator specializations in collect fn collect(self) -> C where C: FromIterator, { let mut f = self.f; self.iter .map(move |mut v| { f(&mut v); v }) .collect() } } impl ExactSizeIterator for Update where I: ExactSizeIterator, F: FnMut(&mut I::Item), { } impl DoubleEndedIterator for Update where I: DoubleEndedIterator, F: FnMut(&mut I::Item), { fn next_back(&mut self) -> Option { if let Some(mut v) = self.iter.next_back() { (self.f)(&mut v); Some(v) } else { None } } } impl FusedIterator for Update where I: FusedIterator, F: FnMut(&mut I::Item), { } itertools-0.13.0/src/adaptors/multi_product.rs000064400000000000000000000160561046102023000176050ustar 00000000000000#![cfg(feature = "use_alloc")] 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`. /// /// 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(State>) where I: Iterator + Clone, I::Item: Clone; #[derive(Clone)] /// Internals for `MultiProduct`. struct MultiProductInner where I: Iterator + Clone, I::Item: Clone, { /// Holds the iterators. iters: Vec>, /// Not populated at the beginning then it holds the current item of each iterator. cur: CurrentItems>, } impl std::fmt::Debug for MultiProduct where I: Iterator + Clone + std::fmt::Debug, I::Item: Clone + std::fmt::Debug, { debug_fmt_fields!(MultiProduct, 0); } impl std::fmt::Debug for MultiProductInner where I: Iterator + Clone + std::fmt::Debug, I::Item: Clone + std::fmt::Debug, { debug_fmt_fields!(MultiProductInner, iters, cur); } /// Create a new cartesian product iterator over an arbitrary number /// of iterators of the same type. /// /// Iterator element is of type `Vec`. pub fn multi_cartesian_product(iters: H) -> MultiProduct<::IntoIter> where H: Iterator, H::Item: IntoIterator, ::IntoIter: Clone, ::Item: Clone, { let inner = MultiProductInner { iters: iters .map(|i| MultiProductIter::new(i.into_iter())) .collect(), cur: NotYetPopulated, }; MultiProduct(ProductInProgress(inner)) } #[derive(Clone, Debug)] /// Holds the state of a single iterator within a `MultiProduct`. struct MultiProductIter where I: Iterator + Clone, I::Item: Clone, { iter: I, iter_orig: I, } impl MultiProductIter where I: Iterator + Clone, I::Item: Clone, { fn new(iter: I) -> Self { Self { iter: iter.clone(), iter_orig: iter, } } } impl Iterator for MultiProduct where I: Iterator + Clone, I::Item: Clone, { type Item = Vec; fn next(&mut self) -> Option { // This fuses the iterator. let inner = self.0.as_mut()?; match &mut inner.cur { Populated(values) => { debug_assert!(!inner.iters.is_empty()); // Find (from the right) a non-finished iterator and // reset the finished ones encountered. for (iter, item) in inner.iters.iter_mut().zip(values.iter_mut()).rev() { if let Some(new) = iter.iter.next() { *item = new; return Some(values.clone()); } else { iter.iter = iter.iter_orig.clone(); // `cur` is populated so the untouched `iter_orig` can not be empty. *item = iter.iter.next().unwrap(); } } self.0 = ProductEnded; None } // Only the first time. NotYetPopulated => { let next: Option> = inner.iters.iter_mut().map(|i| i.iter.next()).collect(); if next.is_none() || inner.iters.is_empty() { // This cartesian product had at most one item to generate and now ends. self.0 = ProductEnded; } else { inner.cur.clone_from(&next); } next } } } fn count(self) -> usize { 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() }), } } fn size_hint(&self) -> (usize, Option) { match &self.0 { ProductEnded => (0, Some(0)), ProductInProgress(MultiProductInner { iters, cur: NotYetPopulated, }) => iters .iter() .map(|iter| iter.iter_orig.size_hint()) .fold((1, Some(1)), size_hint::mul), ProductInProgress(MultiProductInner { iters, cur: Populated(_), }) => { if let [first, tail @ ..] = &iters[..] { tail.iter().fold(first.iter.size_hint(), |mut sh, iter| { sh = size_hint::mul(sh, iter.iter_orig.size_hint()); size_hint::add(sh, iter.iter.size_hint()) }) } else { // Since it is populated, this cartesian product has started so `iters` is not empty. unreachable!() } } } } fn last(self) -> Option { let 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 { iters.into_iter().map(|i| i.iter.last()).collect() } } } impl std::iter::FusedIterator for MultiProduct where I: Iterator + Clone, I::Item: Clone, { } itertools-0.13.0/src/combinations.rs000064400000000000000000000165141046102023000155620ustar 00000000000000use std::fmt; use std::iter::FusedIterator; use super::lazy_buffer::LazyBuffer; use alloc::vec::Vec; use crate::adaptors::checked_binomial; /// An iterator to iterate through all the `k`-length combinations in an iterator. /// /// See [`.combinations()`](crate::Itertools::combinations) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Combinations { indices: Vec, pool: LazyBuffer, first: bool, } impl Clone for Combinations where I: Clone + Iterator, I::Item: Clone, { clone_fields!(indices, pool, first); } impl fmt::Debug for Combinations where I: Iterator + fmt::Debug, I::Item: fmt::Debug, { debug_fmt_fields!(Combinations, indices, pool, first); } /// Create a new `Combinations` from a clonable iterator. pub fn combinations(iter: I, k: usize) -> Combinations where I: Iterator, { Combinations { indices: (0..k).collect(), pool: LazyBuffer::new(iter), first: true, } } impl Combinations { /// Returns the length of a combination produced by this iterator. #[inline] pub fn k(&self) -> usize { self.indices.len() } /// 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() } /// Returns a reference to the source pool. #[inline] pub(crate) fn src(&self) -> &LazyBuffer { &self.pool } /// Resets this `Combinations` back to an initial state for combinations of length /// `k` over the same pool data source. If `k` is larger than the current length /// of the data pool an attempt is made to prefill the pool so that it holds `k` /// elements. pub(crate) fn reset(&mut self, k: usize) { self.first = true; if k < self.indices.len() { self.indices.truncate(k); for i in 0..k { self.indices[i] = i; } } else { for i in 0..self.indices.len() { self.indices[i] = i; } self.indices.extend(self.indices.len()..k); self.pool.prefill(k); } } pub(crate) fn n_and_count(self) -> (usize, usize) { let Self { indices, pool, first, } = self; let n = pool.count(); (n, remaining_for(n, first, &indices).unwrap()) } /// Initialises the iterator by filling a buffer with elements from the /// iterator. Returns true if there are no combinations, false otherwise. fn init(&mut self) -> bool { self.pool.prefill(self.k()); let done = self.k() > self.n(); if !done { self.first = false; } 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 { if self.indices.is_empty() { return true; // Done } // Scan from the end, looking for an index to increment let mut i: usize = self.indices.len() - 1; // Check if we need to consume more from the iterator if self.indices[i] == self.pool.len() - 1 { self.pool.get_next(); // may change pool size } while self.indices[i] == i + self.pool.len() - self.indices.len() { if i > 0 { i -= 1; } else { // Reached the last combination return true; } } // Increment index, and reset the ones to its right self.indices[i] += 1; for j in i + 1..self.indices.len() { self.indices[j] = self.indices[j - 1] + 1; } // If we've made it this far, we haven't run out of combos false } /// Returns the n-th item or the number of successful steps. pub(crate) fn try_nth(&mut self, n: usize) -> Result<::Item, usize> where I::Item: Clone, { let done = if self.first { self.init() } else { self.increment_indices() }; if done { return Err(0); } for i in 0..n { if self.increment_indices() { return Err(i + 1); } } Ok(self.pool.get_at(&self.indices)) } } impl Iterator for Combinations where I: Iterator, I::Item: Clone, { type Item = Vec; fn next(&mut self) -> Option { let done = if self.first { self.init() } else { self.increment_indices() }; if done { return None; } Some(self.pool.get_at(&self.indices)) } fn nth(&mut self, n: usize) -> Option { self.try_nth(n).ok() } fn size_hint(&self) -> (usize, Option) { let (mut low, mut upp) = self.pool.size_hint(); low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX); upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices)); (low, upp) } #[inline] fn count(self) -> usize { self.n_and_count().1 } } impl FusedIterator for Combinations where I: Iterator, I::Item: Clone, { } /// For a given size `n`, return the count of remaining combinations or None if it would overflow. fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option { let k = indices.len(); if n < k { Some(0) } else if first { checked_binomial(n, k) } else { // https://en.wikipedia.org/wiki/Combinatorial_number_system // http://www.site.uottawa.ca/~lucia/courses/5165-09/GenCombObj.pdf // The combinations generated after the current one can be counted by counting as follows: // - The subsequent combinations that differ in indices[0]: // If subsequent combinations differ in indices[0], then their value for indices[0] // must be at least 1 greater than the current indices[0]. // As indices is strictly monotonically sorted, this means we can effectively choose k values // from (n - 1 - indices[0]), leading to binomial(n - 1 - indices[0], k) possibilities. // - The subsequent combinations with same indices[0], but differing indices[1]: // Here we can choose k - 1 values from (n - 1 - indices[1]) values, // leading to binomial(n - 1 - indices[1], k - 1) possibilities. // - (...) // - The subsequent combinations with same indices[0..=i], but differing indices[i]: // Here we can choose k - i values from (n - 1 - indices[i]) values: binomial(n - 1 - indices[i], k - i). // Since subsequent combinations can in any index, we must sum up the aforementioned binomial coefficients. // Below, `n0` resembles indices[i]. indices.iter().enumerate().try_fold(0usize, |sum, (i, n0)| { sum.checked_add(checked_binomial(n - 1 - *n0, k - i)?) }) } } itertools-0.13.0/src/combinations_with_replacement.rs000064400000000000000000000151261046102023000211720ustar 00000000000000use alloc::boxed::Box; use alloc::vec::Vec; use std::fmt; use std::iter::FusedIterator; use super::lazy_buffer::LazyBuffer; use crate::adaptors::checked_binomial; /// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement. /// /// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement) /// for more information. #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct CombinationsWithReplacement where I: Iterator, I::Item: Clone, { indices: Box<[usize]>, pool: LazyBuffer, first: bool, } impl fmt::Debug for CombinationsWithReplacement where I: Iterator + fmt::Debug, I::Item: fmt::Debug + Clone, { debug_fmt_fields!(CombinationsWithReplacement, indices, pool, first); } /// Create a new `CombinationsWithReplacement` from a clonable iterator. pub fn combinations_with_replacement(iter: I, k: usize) -> CombinationsWithReplacement where I: Iterator, I::Item: Clone, { let indices = alloc::vec![0; k].into_boxed_slice(); let pool: LazyBuffer = LazyBuffer::new(iter); CombinationsWithReplacement { indices, pool, first: true, } } impl CombinationsWithReplacement where I: Iterator, I::Item: Clone, { /// Increments indices representing the combination to advance to the next /// (in lexicographic order by increasing sequence) combination. /// /// Returns true if we've run out of combinations, false otherwise. fn increment_indices(&mut self) -> bool { // Check if we need to consume more from the iterator // This will run while we increment our first index digit self.pool.get_next(); // Work out where we need to update our indices let mut increment = None; for (i, indices_int) in self.indices.iter().enumerate().rev() { if *indices_int < self.pool.len() - 1 { increment = Some((i, indices_int + 1)); break; } } match increment { // If we can update the indices further Some((increment_from, increment_value)) => { // We need to update the rightmost non-max value // and all those to the right for i in &mut self.indices[increment_from..] { *i = increment_value; } // TODO: once MSRV >= 1.50, use `fill` instead: // self.indices[increment_from..].fill(increment_value); false } // Otherwise, we're done None => true, } } } impl Iterator for CombinationsWithReplacement where I: Iterator, I::Item: Clone, { type Item = Vec; fn next(&mut self) -> Option { if self.first { // In empty edge cases, stop iterating immediately if !(self.indices.is_empty() || self.pool.get_next()) { return None; } self.first = false; } else if self.increment_indices() { return None; } Some(self.pool.get_at(&self.indices)) } fn nth(&mut self, n: usize) -> Option { if self.first { // In empty edge cases, stop iterating immediately if !(self.indices.is_empty() || self.pool.get_next()) { return None; } self.first = false; } else if self.increment_indices() { return None; } for _ in 0..n { if self.increment_indices() { return None; } } Some(self.pool.get_at(&self.indices)) } fn size_hint(&self) -> (usize, Option) { let (mut low, mut upp) = self.pool.size_hint(); low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX); upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices)); (low, upp) } fn count(self) -> usize { let Self { indices, pool, first, } = self; let n = pool.count(); remaining_for(n, first, &indices).unwrap() } } impl FusedIterator for CombinationsWithReplacement where I: Iterator, I::Item: Clone, { } /// For a given size `n`, return the count of remaining combinations with replacement or None if it would overflow. fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option { // With a "stars and bars" representation, choose k values with replacement from n values is // like choosing k out of k + n − 1 positions (hence binomial(k + n - 1, k) possibilities) // to place k stars and therefore n - 1 bars. // Example (n=4, k=6): ***|*||** represents [0,0,0,1,3,3]. let count = |n: usize, k: usize| { let positions = if n == 0 { k.saturating_sub(1) } else { (n - 1).checked_add(k)? }; checked_binomial(positions, k) }; let k = indices.len(); if first { count(n, k) } else { // The algorithm is similar to the one for combinations *without replacement*, // except we choose values *with replacement* and indices are *non-strictly* monotonically sorted. // The combinations generated after the current one can be counted by counting as follows: // - The subsequent combinations that differ in indices[0]: // If subsequent combinations differ in indices[0], then their value for indices[0] // must be at least 1 greater than the current indices[0]. // As indices is monotonically sorted, this means we can effectively choose k values with // replacement from (n - 1 - indices[0]), leading to count(n - 1 - indices[0], k) possibilities. // - The subsequent combinations with same indices[0], but differing indices[1]: // Here we can choose k - 1 values with replacement from (n - 1 - indices[1]) values, // leading to count(n - 1 - indices[1], k - 1) possibilities. // - (...) // - The subsequent combinations with same indices[0..=i], but differing indices[i]: // Here we can choose k - i values with replacement from (n - 1 - indices[i]) values: count(n - 1 - indices[i], k - i). // Since subsequent combinations can in any index, we must sum up the aforementioned binomial coefficients. // Below, `n0` resembles indices[i]. indices.iter().enumerate().try_fold(0usize, |sum, (i, n0)| { sum.checked_add(count(n - 1 - *n0, k - i)?) }) } } itertools-0.13.0/src/concat_impl.rs000064400000000000000000000016161046102023000153620ustar 00000000000000use crate::Itertools; /// Combine all an iterator's elements into one element by using [`Extend`]. /// /// [`IntoIterator`]-enabled version of [`Itertools::concat`]. /// /// This combinator will extend the first item with each of the rest of the /// items of the iterator. If the iterator is empty, the default value of /// `I::Item` is returned. /// /// ```rust /// use itertools::concat; /// /// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]]; /// assert_eq!(concat(input), vec![1, 2, 3, 4, 5, 6]); /// ``` pub fn concat(iterable: I) -> I::Item where I: IntoIterator, I::Item: Extend<<::Item as IntoIterator>::Item> + IntoIterator + Default, { #[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() } itertools-0.13.0/src/cons_tuples_impl.rs000064400000000000000000000031141046102023000164440ustar 00000000000000macro_rules! impl_cons_iter( ($_A:ident, $_B:ident, ) => (); // stop ($A:ident, $($B:ident,)*) => ( impl_cons_iter!($($B,)*); #[allow(non_snake_case)] impl Iterator for ConsTuples where Iter: Iterator, { type Item = ($($B,)* X, ); fn next(&mut self) -> Option { self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, )) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } fn fold(self, accum: Acc, mut f: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, { self.iter.fold(accum, move |acc, (($($B,)*), x)| f(acc, ($($B,)* x, ))) } } ); ); impl_cons_iter!(A, B, C, D, E, F, G, H, I, J, K, L,); /// An iterator that maps an iterator of tuples like /// `((A, B), C)` to an iterator of `(A, B, C)`. /// /// Used by the `iproduct!()` macro. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Debug)] pub struct ConsTuples where I: Iterator, { iter: I, } impl Clone for ConsTuples where I: Clone + Iterator, { clone_fields!(iter); } /// Create an iterator that maps for example iterators of /// `((A, B), C)` to `(A, B, C)`. pub fn cons_tuples(iterable: I) -> ConsTuples where I: IntoIterator, { ConsTuples { iter: iterable.into_iter(), } } itertools-0.13.0/src/diff.rs000064400000000000000000000072051046102023000140020ustar 00000000000000//! "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) //! 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; /// A type returned by the [`diff_with`] function. /// /// `Diff` represents the way in which the elements yielded by the iterator `I` differ to some /// iterator `J`. pub enum Diff 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. FirstMismatch(usize, PutBack, PutBack), /// The total number of elements that were in `J` along with the remaining elements of `I`. Shorter(usize, PutBack), /// The total number of elements that were in `I` along with the remaining elements of `J`. Longer(usize, PutBack), } impl fmt::Debug for Diff where I: Iterator, J: Iterator, PutBack: fmt::Debug, PutBack: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::FirstMismatch(idx, i, j) => f .debug_tuple("FirstMismatch") .field(idx) .field(i) .field(j) .finish(), Self::Shorter(idx, i) => f.debug_tuple("Shorter").field(idx).field(i).finish(), Self::Longer(idx, j) => f.debug_tuple("Longer").field(idx).field(j).finish(), } } } impl Clone for Diff where I: Iterator, J: Iterator, PutBack: Clone, PutBack: Clone, { fn clone(&self) -> Self { match self { Self::FirstMismatch(idx, i, j) => Self::FirstMismatch(*idx, i.clone(), j.clone()), Self::Shorter(idx, i) => Self::Shorter(*idx, i.clone()), Self::Longer(idx, j) => Self::Longer(*idx, j.clone()), } } } /// Compares every element yielded by both `i` and `j` with the given function in lock-step and /// returns a [`Diff`] which describes how `j` differs from `i`. /// /// If the number of elements yielded by `j` is less than the number of elements yielded by `i`, /// the number of `j` elements yielded will be returned along with `i`'s remaining elements as /// `Diff::Shorter`. /// /// If the two elements of a step differ, the index of those elements along with the remaining /// elements of both `i` and `j` are returned as `Diff::FirstMismatch`. /// /// If `i` becomes exhausted before `j` becomes exhausted, the number of elements in `i` along with /// the remaining `j` elements will be returned as `Diff::Longer`. pub fn diff_with(i: I, j: J, mut is_equal: F) -> Option> where I: IntoIterator, J: IntoIterator, F: FnMut(&I::Item, &J::Item) -> bool, { let mut i = i.into_iter(); let mut j = j.into_iter(); let mut idx = 0; 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)); } } } idx += 1; } j.next() .map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem))) } itertools-0.13.0/src/duplicates_impl.rs000064400000000000000000000141151046102023000162460ustar 00000000000000use std::hash::Hash; mod private { use std::collections::HashMap; use std::fmt; use std::hash::Hash; #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct DuplicatesBy { pub(crate) iter: I, pub(crate) meta: Meta, } impl fmt::Debug for DuplicatesBy where I: Iterator + fmt::Debug, V: fmt::Debug + Hash + Eq, { debug_fmt_fields!(DuplicatesBy, iter, meta.used); } impl DuplicatesBy { pub(crate) fn new(iter: I, key_method: F) -> Self { Self { iter, meta: Meta { used: HashMap::new(), pending: 0, key_method, }, } } } #[derive(Clone)] pub struct Meta { used: HashMap, pending: usize, key_method: F, } impl Meta where Key: Eq + Hash, { /// Takes an item and returns it back to the caller if it's the second time we see it. /// Otherwise the item is consumed and None is returned #[inline(always)] fn filter(&mut self, item: I) -> Option where F: KeyMethod, { let kv = self.key_method.make(item); match self.used.get_mut(kv.key_ref()) { None => { self.used.insert(kv.key(), false); self.pending += 1; None } Some(true) => None, Some(produced) => { *produced = true; self.pending -= 1; Some(kv.value()) } } } } impl Iterator for DuplicatesBy where I: Iterator, Key: Eq + Hash, F: KeyMethod, { type Item = I::Item; fn next(&mut self) -> Option { let Self { iter, meta } = self; iter.find_map(|v| meta.filter(v)) } #[inline] fn size_hint(&self) -> (usize, Option) { let (_, hi) = self.iter.size_hint(); let hi = hi.map(|hi| { if hi <= self.meta.pending { // fewer or equally many iter-remaining elements than pending elements // => at most, each iter-remaining element is matched hi } else { // fewer pending elements than iter-remaining elements // => at most: // * each pending element is matched // * the other iter-remaining elements come in pairs self.meta.pending + (hi - self.meta.pending) / 2 } }); // The lower bound is always 0 since we might only get unique items from now on (0, hi) } } impl DoubleEndedIterator for DuplicatesBy where I: DoubleEndedIterator, Key: Eq + Hash, F: KeyMethod, { fn next_back(&mut self) -> Option { let Self { iter, meta } = self; iter.rev().find_map(|v| meta.filter(v)) } } /// A keying method for use with `DuplicatesBy` pub trait KeyMethod { type Container: KeyXorValue; fn make(&mut self, value: V) -> Self::Container; } /// Apply the identity function to elements before checking them for equality. #[derive(Debug, Clone)] pub struct ById; impl KeyMethod for ById { type Container = JustValue; fn make(&mut self, v: V) -> Self::Container { JustValue(v) } } /// Apply a user-supplied function to elements before checking them for equality. #[derive(Clone)] pub struct ByFn(pub(crate) F); impl fmt::Debug for ByFn { debug_fmt_fields!(ByFn,); } impl KeyMethod for ByFn where F: FnMut(&V) -> K, { type Container = KeyValue; fn make(&mut self, v: V) -> Self::Container { KeyValue((self.0)(&v), v) } } // Implementors of this trait can hold onto a key and a value but only give access to one of them // at a time. This allows the key and the value to be the same value internally pub trait KeyXorValue { fn key_ref(&self) -> &K; fn key(self) -> K; fn value(self) -> V; } #[derive(Debug)] pub struct KeyValue(K, V); impl KeyXorValue for KeyValue { fn key_ref(&self) -> &K { &self.0 } fn key(self) -> K { self.0 } fn value(self) -> V { self.1 } } #[derive(Debug)] pub struct JustValue(V); impl KeyXorValue for JustValue { fn key_ref(&self) -> &V { &self.0 } fn key(self) -> V { self.0 } fn value(self) -> V { self.0 } } } /// An iterator adapter to filter for duplicate elements. /// /// See [`.duplicates_by()`](crate::Itertools::duplicates_by) for more information. pub type DuplicatesBy = private::DuplicatesBy>; /// Create a new `DuplicatesBy` iterator. pub fn duplicates_by(iter: I, f: F) -> DuplicatesBy where Key: Eq + Hash, F: FnMut(&I::Item) -> Key, I: Iterator, { DuplicatesBy::new(iter, private::ByFn(f)) } /// An iterator adapter to filter out duplicate elements. /// /// See [`.duplicates()`](crate::Itertools::duplicates) for more information. pub type Duplicates = private::DuplicatesBy::Item, private::ById>; /// Create a new `Duplicates` iterator. pub fn duplicates(iter: I) -> Duplicates where I: Iterator, I::Item: Eq + Hash, { Duplicates::new(iter, private::ById) } itertools-0.13.0/src/either_or_both.rs000064400000000000000000000426301046102023000160670ustar 00000000000000use 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 { /// Both values are present. Both(A, B), /// Only the left value of type `A` is present. Left(A), /// Only the right value of type `B` is present. Right(B), } impl EitherOrBoth { /// If `Left`, or `Both`, return true. Otherwise, return false. pub fn has_left(&self) -> bool { self.as_ref().left().is_some() } /// If `Right`, or `Both`, return true, otherwise, return false. pub fn has_right(&self) -> bool { self.as_ref().right().is_some() } /// If `Left`, return true. Otherwise, return false. /// Exclusive version of [`has_left`](EitherOrBoth::has_left). pub fn is_left(&self) -> bool { matches!(self, Left(_)) } /// If `Right`, return true. Otherwise, return false. /// Exclusive version of [`has_right`](EitherOrBoth::has_right). pub fn is_right(&self) -> bool { matches!(self, Right(_)) } /// 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`. pub fn left(self) -> Option { match self { Left(left) | Both(left, _) => Some(left), _ => None, } } /// If `Right`, or `Both`, return `Some` with the right value. Otherwise, return `None`. pub fn right(self) -> Option { match self { Right(right) | Both(_, right) => Some(right), _ => None, } } /// Return tuple of options corresponding to the left and right value respectively /// /// If `Left` return `(Some(..), None)`, if `Right` return `(None,Some(..))`, else return /// `(Some(..),Some(..))` pub fn left_and_right(self) -> (Option, Option) { self.map_any(Some, Some).or_default() } /// If `Left`, return `Some` with the left value. If `Right` or `Both`, return `None`. /// /// # Examples /// /// ``` /// // On the `Left` variant. /// # use itertools::{EitherOrBoth, EitherOrBoth::{Left, Right, Both}}; /// let x: EitherOrBoth<_, ()> = Left("bonjour"); /// assert_eq!(x.just_left(), Some("bonjour")); /// /// // On the `Right` variant. /// let x: EitherOrBoth<(), _> = Right("hola"); /// assert_eq!(x.just_left(), None); /// /// // On the `Both` variant. /// let x = Both("bonjour", "hola"); /// assert_eq!(x.just_left(), None); /// ``` pub fn just_left(self) -> Option { match self { Left(left) => Some(left), _ => None, } } /// If `Right`, return `Some` with the right value. If `Left` or `Both`, return `None`. /// /// # Examples /// /// ``` /// // On the `Left` variant. /// # use itertools::{EitherOrBoth::{Left, Right, Both}, EitherOrBoth}; /// let x: EitherOrBoth<_, ()> = Left("auf wiedersehen"); /// assert_eq!(x.just_left(), Some("auf wiedersehen")); /// /// // On the `Right` variant. /// let x: EitherOrBoth<(), _> = Right("adios"); /// assert_eq!(x.just_left(), None); /// /// // On the `Both` variant. /// let x = Both("auf wiedersehen", "adios"); /// assert_eq!(x.just_left(), None); /// ``` pub fn just_right(self) -> Option { match self { Right(right) => Some(right), _ => None, } } /// If `Both`, return `Some` containing the left and right values. Otherwise, return `None`. pub fn both(self) -> Option<(A, B)> { match self { Both(a, b) => Some((a, b)), _ => None, } } /// If `Left` or `Both`, return the left value. Otherwise, convert the right value and return it. pub fn into_left(self) -> A where B: Into, { match self { Left(a) | Both(a, _) => a, Right(b) => b.into(), } } /// If `Right` or `Both`, return the right value. Otherwise, convert the left value and return it. pub fn into_right(self) -> B where A: Into, { match self { Right(b) | Both(_, b) => b, Left(a) => a.into(), } } /// Converts from `&EitherOrBoth` to `EitherOrBoth<&A, &B>`. pub fn as_ref(&self) -> EitherOrBoth<&A, &B> { match *self { Left(ref left) => Left(left), Right(ref right) => Right(right), Both(ref left, ref right) => Both(left, right), } } /// Converts from `&mut EitherOrBoth` to `EitherOrBoth<&mut A, &mut B>`. pub fn as_mut(&mut self) -> EitherOrBoth<&mut A, &mut B> { match *self { Left(ref mut left) => Left(left), Right(ref mut right) => Right(right), Both(ref mut left, ref mut right) => Both(left, right), } } /// Converts from `&EitherOrBoth` to `EitherOrBoth<&_, &_>` using the [`Deref`] trait. pub fn as_deref(&self) -> EitherOrBoth<&A::Target, &B::Target> where A: Deref, B: Deref, { match *self { Left(ref left) => Left(left), Right(ref right) => Right(right), Both(ref left, ref right) => Both(left, right), } } /// Converts from `&mut EitherOrBoth` to `EitherOrBoth<&mut _, &mut _>` using the [`DerefMut`] trait. pub fn as_deref_mut(&mut self) -> EitherOrBoth<&mut A::Target, &mut B::Target> where A: DerefMut, B: DerefMut, { match *self { Left(ref mut left) => Left(left), Right(ref mut right) => Right(right), Both(ref mut left, ref mut right) => Both(left, right), } } /// Convert `EitherOrBoth` to `EitherOrBoth`. pub fn flip(self) -> EitherOrBoth { match self { Left(a) => Right(a), Right(b) => Left(b), Both(a, b) => Both(b, a), } } /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, b)` variants. If it is /// present rewrapping the result in `self`'s original variant. pub fn map_left(self, f: F) -> EitherOrBoth where F: FnOnce(A) -> M, { match self { Both(a, b) => Both(f(a), b), Left(a) => Left(f(a)), Right(b) => Right(b), } } /// Apply the function `f` on the value `b` in `Right(b)` or `Both(a, b)` variants. /// If it is present rewrapping the result in `self`'s original variant. pub fn map_right(self, f: F) -> EitherOrBoth where F: FnOnce(B) -> M, { match self { Left(a) => Left(a), Right(b) => Right(f(b)), Both(a, b) => Both(a, f(b)), } } /// Apply the functions `f` and `g` on the value `a` and `b` respectively; /// found in `Left(a)`, `Right(b)`, or `Both(a, b)` variants. /// The Result is rewrapped `self`'s original variant. pub fn map_any(self, f: F, g: G) -> EitherOrBoth where F: FnOnce(A) -> L, G: FnOnce(B) -> R, { match self { Left(a) => Left(f(a)), Right(b) => Right(g(b)), Both(a, b) => Both(f(a), g(b)), } } /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, _)` variants if it is /// present. pub fn left_and_then(self, f: F) -> EitherOrBoth where F: FnOnce(A) -> EitherOrBoth, { match self { Left(a) | Both(a, _) => f(a), Right(b) => Right(b), } } /// Apply the function `f` on the value `b` /// in `Right(b)` or `Both(_, b)` variants if it is present. pub fn right_and_then(self, f: F) -> EitherOrBoth where F: FnOnce(B) -> EitherOrBoth, { match self { Left(a) => Left(a), Right(b) | Both(_, b) => f(b), } } /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present. /// Otherwise, returns the wrapped value for the present element, and the supplied /// value for the other. The first (`l`) argument is used for a missing `Left` /// value. The second (`r`) argument is used for a missing `Right` value. /// /// Arguments passed to `or` are eagerly evaluated; if you are passing /// the result of a function call, it is recommended to use [`or_else`], /// which is lazily evaluated. /// /// [`or_else`]: EitherOrBoth::or_else /// /// # Examples /// /// ``` /// # use itertools::EitherOrBoth; /// assert_eq!(EitherOrBoth::Both("tree", 1).or("stone", 5), ("tree", 1)); /// assert_eq!(EitherOrBoth::Left("tree").or("stone", 5), ("tree", 5)); /// assert_eq!(EitherOrBoth::Right(1).or("stone", 5), ("stone", 1)); /// ``` pub fn or(self, l: A, r: B) -> (A, B) { match self { Left(inner_l) => (inner_l, r), Right(inner_r) => (l, inner_r), Both(inner_l, inner_r) => (inner_l, inner_r), } } /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present. /// Otherwise, returns the wrapped value for the present element, and the [`default`](Default::default) /// for the other. pub fn or_default(self) -> (A, B) where A: Default, B: Default, { match self { Left(l) => (l, B::default()), Right(r) => (A::default(), r), Both(l, r) => (l, r), } } /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present. /// Otherwise, returns the wrapped value for the present element, and computes the /// missing value with the supplied closure. The first argument (`l`) is used for a /// missing `Left` value. The second argument (`r`) is used for a missing `Right` value. /// /// # Examples /// /// ``` /// # use itertools::EitherOrBoth; /// let k = 10; /// assert_eq!(EitherOrBoth::Both("tree", 1).or_else(|| "stone", || 2 * k), ("tree", 1)); /// assert_eq!(EitherOrBoth::Left("tree").or_else(|| "stone", || 2 * k), ("tree", 20)); /// assert_eq!(EitherOrBoth::Right(1).or_else(|| "stone", || 2 * k), ("stone", 1)); /// ``` pub fn or_else A, R: FnOnce() -> B>(self, l: L, r: R) -> (A, B) { match self { Left(inner_l) => (inner_l, r()), Right(inner_r) => (l(), inner_r), Both(inner_l, inner_r) => (inner_l, inner_r), } } /// Returns a mutable reference to the left value. If the left value is not present, /// it is replaced with `val`. pub fn left_or_insert(&mut self, val: A) -> &mut A { self.left_or_insert_with(|| val) } /// Returns a mutable reference to the right value. If the right value is not present, /// it is replaced with `val`. pub fn right_or_insert(&mut self, val: B) -> &mut B { self.right_or_insert_with(|| val) } /// If the left value is not present, replace it the value computed by the closure `f`. /// Returns a mutable reference to the now-present left value. pub fn left_or_insert_with(&mut self, f: F) -> &mut A where F: FnOnce() -> A, { match self { Left(left) | Both(left, _) => left, Right(_) => self.insert_left(f()), } } /// If the right value is not present, replace it the value computed by the closure `f`. /// Returns a mutable reference to the now-present right value. pub fn right_or_insert_with(&mut self, f: F) -> &mut B where F: FnOnce() -> B, { match self { Right(right) | Both(_, right) => right, Left(_) => self.insert_right(f()), } } /// Sets the `left` value of this instance, and returns a mutable reference to it. /// Does not affect the `right` value. /// /// # Examples /// ``` /// # use itertools::{EitherOrBoth, EitherOrBoth::{Left, Right, Both}}; /// /// // Overwriting a pre-existing value. /// let mut either: EitherOrBoth<_, ()> = Left(0_u32); /// assert_eq!(*either.insert_left(69), 69); /// /// // Inserting a second value. /// let mut either = Right("no"); /// assert_eq!(*either.insert_left("yes"), "yes"); /// assert_eq!(either, Both("yes", "no")); /// ``` pub fn insert_left(&mut self, val: A) -> &mut A { match self { Left(left) | Both(left, _) => { *left = val; left } Right(right) => { // This is like a map in place operation. We move out of the reference, // change the value, and then move back into the reference. unsafe { // SAFETY: We know this pointer is valid for reading since we got it from a reference. let right = std::ptr::read(right as *mut _); // SAFETY: Again, we know the pointer is valid since we got it from a reference. std::ptr::write(self as *mut _, Both(val, right)); } if let Both(left, _) = self { left } else { // SAFETY: The above pattern will always match, since we just // set `self` equal to `Both`. unsafe { std::hint::unreachable_unchecked() } } } } } /// Sets the `right` value of this instance, and returns a mutable reference to it. /// Does not affect the `left` value. /// /// # Examples /// ``` /// # use itertools::{EitherOrBoth, EitherOrBoth::{Left, Both}}; /// // Overwriting a pre-existing value. /// let mut either: EitherOrBoth<_, ()> = Left(0_u32); /// assert_eq!(*either.insert_left(69), 69); /// /// // Inserting a second value. /// let mut either = Left("what's"); /// assert_eq!(*either.insert_right(9 + 10), 21 - 2); /// assert_eq!(either, Both("what's", 9+10)); /// ``` pub fn insert_right(&mut self, val: B) -> &mut B { match self { Right(right) | Both(_, right) => { *right = val; right } Left(left) => { // This is like a map in place operation. We move out of the reference, // change the value, and then move back into the reference. unsafe { // SAFETY: We know this pointer is valid for reading since we got it from a reference. let left = std::ptr::read(left as *mut _); // SAFETY: Again, we know the pointer is valid since we got it from a reference. std::ptr::write(self as *mut _, Both(left, val)); } if let Both(_, right) = self { right } else { // SAFETY: The above pattern will always match, since we just // set `self` equal to `Both`. unsafe { std::hint::unreachable_unchecked() } } } } } /// Set `self` to `Both(..)`, containing the specified left and right values, /// and returns a mutable reference to those values. pub fn insert_both(&mut self, left: A, right: B) -> (&mut A, &mut B) { *self = Both(left, right); if let Both(left, right) = self { (left, right) } else { // SAFETY: The above pattern will always match, since we just // set `self` equal to `Both`. unsafe { std::hint::unreachable_unchecked() } } } } impl EitherOrBoth { /// Return either value of left, right, or apply a function `f` to both values if both are present. /// The input function has to return the same type as both Right and Left carry. /// /// This function can be used to preferrably extract the left resp. right value, /// but fall back to the other (i.e. right resp. left) if the preferred one is not present. /// /// # Examples /// ``` /// # use itertools::EitherOrBoth; /// assert_eq!(EitherOrBoth::Both(3, 7).reduce(u32::max), 7); /// assert_eq!(EitherOrBoth::Left(3).reduce(u32::max), 3); /// assert_eq!(EitherOrBoth::Right(7).reduce(u32::max), 7); /// /// // Extract the left value if present, fall back to the right otherwise. /// assert_eq!(EitherOrBoth::Left("left").reduce(|l, _r| l), "left"); /// assert_eq!(EitherOrBoth::Right("right").reduce(|l, _r| l), "right"); /// assert_eq!(EitherOrBoth::Both("left", "right").reduce(|l, _r| l), "left"); /// ``` pub fn reduce(self, f: F) -> T where F: FnOnce(T, T) -> T, { match self { Left(a) => a, Right(b) => b, Both(a, b) => f(a, b), } } } impl From> for Option> { fn from(value: EitherOrBoth) -> Self { match value { Left(l) => Some(Either::Left(l)), Right(r) => Some(Either::Right(r)), Both(..) => None, } } } impl From> for EitherOrBoth { fn from(either: Either) -> Self { match either { Either::Left(l) => Left(l), Either::Right(l) => Right(l), } } } itertools-0.13.0/src/exactly_one_err.rs000064400000000000000000000064261046102023000162600ustar 00000000000000#[cfg(feature = "use_std")] use std::error::Error; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::iter::ExactSizeIterator; use either::Either; use crate::size_hint; /// 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 /// effectively "restores" the state of the input iterator when it's handed back. /// /// This is very similar to `PutBackN` except this iterator only supports 0-2 elements and does not /// use a `Vec`. #[derive(Clone)] pub struct ExactlyOneError where I: Iterator, { first_two: Option>, inner: I, } impl ExactlyOneError where I: Iterator, { /// Creates a new `ExactlyOneErr` iterator. pub(crate) fn new(first_two: Option>, inner: I) -> Self { Self { first_two, inner } } fn additional_len(&self) -> usize { match self.first_two { Some(Either::Left(_)) => 2, Some(Either::Right(_)) => 1, None => 0, } } } impl Iterator for ExactlyOneError where I: Iterator, { type Item = I::Item; fn next(&mut self) -> Option { match self.first_two.take() { Some(Either::Left([first, second])) => { self.first_two = Some(Either::Right(second)); Some(first) } Some(Either::Right(second)) => Some(second), None => self.inner.next(), } } fn size_hint(&self) -> (usize, Option) { size_hint::add_scalar(self.inner.size_hint(), self.additional_len()) } fn fold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { match self.first_two { Some(Either::Left([first, second])) => { init = f(init, first); init = f(init, second); } Some(Either::Right(second)) => init = f(init, second), None => {} } self.inner.fold(init, f) } } impl ExactSizeIterator for ExactlyOneError where I: ExactSizeIterator {} impl Display for ExactlyOneError where I: Iterator, { fn fmt(&self, f: &mut Formatter) -> FmtResult { let additional = self.additional_len(); if additional > 0 { write!(f, "got at least 2 elements when exactly one was expected") } else { write!(f, "got zero elements when exactly one was expected") } } } impl Debug for ExactlyOneError where I: Iterator + Debug, I::Item: Debug, { fn fmt(&self, f: &mut Formatter) -> FmtResult { let mut dbg = f.debug_struct("ExactlyOneError"); match &self.first_two { Some(Either::Left([first, second])) => { dbg.field("first", first).field("second", second); } Some(Either::Right(second)) => { dbg.field("second", second); } None => {} } dbg.field("inner", &self.inner).finish() } } #[cfg(feature = "use_std")] impl Error for ExactlyOneError where I: Iterator + Debug, I::Item: Debug, { } itertools-0.13.0/src/extrema_set.rs000064400000000000000000000027611046102023000154140ustar 00000000000000#![cfg(feature = "use_alloc")] use alloc::{vec, vec::Vec}; use std::cmp::Ordering; /// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`. pub fn min_set_impl( mut it: I, mut key_for: F, mut compare: Compare, ) -> Vec where I: Iterator, F: FnMut(&I::Item) -> K, Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering, { match it.next() { None => Vec::new(), Some(element) => { let mut current_key = key_for(&element); let mut result = vec![element]; it.for_each(|element| { let key = key_for(&element); match compare(&element, &result[0], &key, ¤t_key) { Ordering::Less => { result.clear(); result.push(element); current_key = key; } Ordering::Equal => { result.push(element); } Ordering::Greater => {} } }); result } } } /// Implementation guts for `ax_set`, `max_set_by`, and `max_set_by_key`. pub fn max_set_impl(it: I, key_for: F, mut compare: Compare) -> Vec where I: Iterator, F: FnMut(&I::Item) -> K, Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering, { min_set_impl(it, key_for, |it1, it2, key1, key2| { compare(it2, it1, key2, key1) }) } itertools-0.13.0/src/flatten_ok.rs000064400000000000000000000140141046102023000152140ustar 00000000000000use crate::size_hint; use std::{ fmt, iter::{DoubleEndedIterator, FusedIterator}, }; pub fn flatten_ok(iter: I) -> FlattenOk where I: Iterator>, T: IntoIterator, { FlattenOk { iter, inner_front: None, inner_back: None, } } /// An iterator adaptor that flattens `Result::Ok` values and /// allows `Result::Err` values through unchanged. /// /// See [`.flatten_ok()`](crate::Itertools::flatten_ok) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct FlattenOk where I: Iterator>, T: IntoIterator, { iter: I, inner_front: Option, inner_back: Option, } impl Iterator for FlattenOk where I: Iterator>, T: IntoIterator, { type Item = Result; fn next(&mut self) -> Option { loop { // Handle the front inner iterator. if let Some(inner) = &mut self.inner_front { if let Some(item) = inner.next() { return Some(Ok(item)); } // This is necessary for the iterator to implement `FusedIterator` // with only the original iterator being fused. self.inner_front = None; } match self.iter.next() { Some(Ok(ok)) => self.inner_front = Some(ok.into_iter()), Some(Err(e)) => return Some(Err(e)), None => { // Handle the back inner iterator. if let Some(inner) = &mut self.inner_back { if let Some(item) = inner.next() { return Some(Ok(item)); } // This is necessary for the iterator to implement `FusedIterator` // with only the original iterator being fused. self.inner_back = None; } else { return None; } } } } } fn fold(self, init: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { // Front let mut acc = match self.inner_front { Some(x) => x.fold(init, |a, o| f(a, Ok(o))), None => init, }; acc = self.iter.fold(acc, |acc, x| match x { Ok(it) => it.into_iter().fold(acc, |a, o| f(a, Ok(o))), Err(e) => f(acc, Err(e)), }); // Back match self.inner_back { Some(x) => x.fold(acc, |a, o| f(a, Ok(o))), None => acc, } } fn size_hint(&self) -> (usize, Option) { let inner_hint = |inner: &Option| { inner .as_ref() .map(Iterator::size_hint) .unwrap_or((0, Some(0))) }; let inner_front = inner_hint(&self.inner_front); let inner_back = inner_hint(&self.inner_back); // The outer iterator `Ok` case could be (0, None) as we don't know its size_hint yet. let outer = match self.iter.size_hint() { (0, Some(0)) => (0, Some(0)), _ => (0, None), }; size_hint::add(size_hint::add(inner_front, inner_back), outer) } } impl DoubleEndedIterator for FlattenOk where I: DoubleEndedIterator>, T: IntoIterator, T::IntoIter: DoubleEndedIterator, { fn next_back(&mut self) -> Option { loop { // Handle the back inner iterator. if let Some(inner) = &mut self.inner_back { if let Some(item) = inner.next_back() { return Some(Ok(item)); } // This is necessary for the iterator to implement `FusedIterator` // with only the original iterator being fused. self.inner_back = None; } match self.iter.next_back() { Some(Ok(ok)) => self.inner_back = Some(ok.into_iter()), Some(Err(e)) => return Some(Err(e)), None => { // Handle the front inner iterator. if let Some(inner) = &mut self.inner_front { if let Some(item) = inner.next_back() { return Some(Ok(item)); } // This is necessary for the iterator to implement `FusedIterator` // with only the original iterator being fused. self.inner_front = None; } else { return None; } } } } } fn rfold(self, init: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { // Back let mut acc = match self.inner_back { Some(x) => x.rfold(init, |a, o| f(a, Ok(o))), None => init, }; acc = self.iter.rfold(acc, |acc, x| match x { Ok(it) => it.into_iter().rfold(acc, |a, o| f(a, Ok(o))), Err(e) => f(acc, Err(e)), }); // Front match self.inner_front { Some(x) => x.rfold(acc, |a, o| f(a, Ok(o))), None => acc, } } } impl Clone for FlattenOk where I: Iterator> + Clone, T: IntoIterator, T::IntoIter: Clone, { clone_fields!(iter, inner_front, inner_back); } impl fmt::Debug for FlattenOk where I: Iterator> + fmt::Debug, T: IntoIterator, T::IntoIter: fmt::Debug, { debug_fmt_fields!(FlattenOk, iter, inner_front, inner_back); } /// Only the iterator being flattened needs to implement [`FusedIterator`]. impl FusedIterator for FlattenOk where I: FusedIterator>, T: IntoIterator, { } itertools-0.13.0/src/format.rs000064400000000000000000000114401046102023000143560ustar 00000000000000use std::cell::Cell; use std::fmt; /// Format all iterator elements lazily, separated by `sep`. /// /// The format value can only be formatted once, after that the iterator is /// exhausted. /// /// See [`.format_with()`](crate::Itertools::format_with) for more information. pub struct FormatWith<'a, I, F> { sep: &'a str, /// `FormatWith` uses interior mutability because `Display::fmt` takes `&self`. inner: Cell>, } /// Format all iterator elements lazily, separated by `sep`. /// /// The format value can only be formatted once, after that the iterator is /// exhausted. /// /// See [`.format()`](crate::Itertools::format) /// for more information. pub struct Format<'a, I> { sep: &'a str, /// `Format` uses interior mutability because `Display::fmt` takes `&self`. inner: Cell>, } pub fn new_format(iter: I, separator: &str, f: F) -> FormatWith<'_, I, F> where I: Iterator, F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, { FormatWith { sep: separator, inner: Cell::new(Some((iter, f))), } } pub fn new_format_default(iter: I, separator: &str) -> Format<'_, I> where I: Iterator, { Format { sep: separator, 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, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let (mut iter, mut format) = match self.inner.take() { Some(t) => t, None => panic!("FormatWith: was already formatted once"), }; if let Some(fst) = iter.next() { format(fst, &mut |disp: &dyn fmt::Display| disp.fmt(f))?; iter.try_for_each(|elt| { if !self.sep.is_empty() { f.write_str(self.sep)?; } format(elt, &mut |disp: &dyn fmt::Display| disp.fmt(f)) })?; } Ok(()) } } impl<'a, I, F> fmt::Debug for FormatWith<'a, I, F> where I: Iterator, F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } } impl<'a, I> Format<'a, I> where I: Iterator, { fn format( &self, f: &mut fmt::Formatter, cb: fn(&I::Item, &mut fmt::Formatter) -> fmt::Result, ) -> fmt::Result { let mut iter = match self.inner.take() { Some(t) => t, None => panic!("Format: was already formatted once"), }; if let Some(fst) = iter.next() { cb(&fst, f)?; iter.try_for_each(|elt| { if !self.sep.is_empty() { f.write_str(self.sep)?; } cb(&elt, f) })?; } Ok(()) } } macro_rules! impl_format { ($($fmt_trait:ident)*) => { $( impl<'a, I> fmt::$fmt_trait for Format<'a, I> where I: Iterator, I::Item: fmt::$fmt_trait, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.format(f, fmt::$fmt_trait::fmt) } } )* } } impl_format! {Display Debug UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer} impl<'a, I, F> Clone for FormatWith<'a, I, F> where (I, F): Clone, { fn clone(&self) -> Self { struct PutBackOnDrop<'r, 'a, I, F> { into: &'r FormatWith<'a, I, F>, inner: Option<(I, F)>, } // This ensures we preserve the state of the original `FormatWith` if `Clone` panics impl<'r, 'a, I, F> Drop for PutBackOnDrop<'r, 'a, I, F> { fn drop(&mut self) { self.into.inner.set(self.inner.take()) } } let pbod = PutBackOnDrop { inner: self.inner.take(), into: self, }; Self { inner: Cell::new(pbod.inner.clone()), sep: self.sep, } } } impl<'a, I> Clone for Format<'a, I> where I: Clone, { fn clone(&self) -> Self { struct PutBackOnDrop<'r, 'a, I> { into: &'r Format<'a, I>, inner: Option, } // This ensures we preserve the state of the original `FormatWith` if `Clone` panics impl<'r, 'a, I> Drop for PutBackOnDrop<'r, 'a, I> { fn drop(&mut self) { self.into.inner.set(self.inner.take()) } } let pbod = PutBackOnDrop { inner: self.inner.take(), into: self, }; Self { inner: Cell::new(pbod.inner.clone()), sep: self.sep, } } } itertools-0.13.0/src/free.rs000064400000000000000000000167201046102023000140150ustar 00000000000000//! Free functions that create iterator adaptors or call iterator methods. //! //! The benefit of free functions is that they accept any [`IntoIterator`] as //! argument, so the resulting code may be easier to read. #[cfg(feature = "use_alloc")] use std::fmt::Display; use std::iter::{self, Zip}; #[cfg(feature = "use_alloc")] type VecIntoIter = alloc::vec::IntoIter; #[cfg(feature = "use_alloc")] use alloc::string::String; use crate::intersperse::{Intersperse, IntersperseWith}; use crate::Itertools; pub use crate::adaptors::{interleave, put_back}; #[cfg(feature = "use_alloc")] 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::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. /// /// [`IntoIterator`] enabled version of [`Iterator::intersperse`]. /// /// ``` /// use itertools::intersperse; /// /// itertools::assert_equal(intersperse((0..3), 8), vec![0, 8, 1, 8, 2]); /// ``` pub fn intersperse(iterable: I, element: I::Item) -> Intersperse where I: IntoIterator, ::Item: Clone, { Itertools::intersperse(iterable.into_iter(), element) } /// Iterate `iterable` with a particular value created by a function inserted /// between each element. /// /// [`IntoIterator`] enabled version of [`Iterator::intersperse_with`]. /// /// ``` /// use itertools::intersperse_with; /// /// let mut i = 10; /// itertools::assert_equal(intersperse_with((0..3), || { i -= 1; i }), vec![0, 9, 1, 8, 2]); /// assert_eq!(i, 8); /// ``` pub fn intersperse_with(iterable: I, element: F) -> IntersperseWith where I: IntoIterator, F: FnMut() -> I::Item, { Itertools::intersperse_with(iterable.into_iter(), element) } /// Iterate `iterable` with a running index. /// /// [`IntoIterator`] enabled version of [`Iterator::enumerate`]. /// /// ``` /// use itertools::enumerate; /// /// for (i, elt) in enumerate(&[1, 2, 3]) { /// /* loop body */ /// } /// ``` pub fn enumerate(iterable: I) -> iter::Enumerate where I: IntoIterator, { iterable.into_iter().enumerate() } /// Iterate `iterable` in reverse. /// /// [`IntoIterator`] enabled version of [`Iterator::rev`]. /// /// ``` /// use itertools::rev; /// /// for elt in rev(&[1, 2, 3]) { /// /* loop body */ /// } /// ``` pub fn rev(iterable: I) -> iter::Rev where I: IntoIterator, I::IntoIter: DoubleEndedIterator, { iterable.into_iter().rev() } /// Converts the arguments to iterators and zips them. /// /// [`IntoIterator`] enabled version of [`Iterator::zip`]. /// /// ## Example /// /// ``` /// use itertools::zip; /// /// let mut result: Vec<(i32, char)> = Vec::new(); /// /// for (a, b) in zip(&[1, 2, 3, 4, 5], &['a', 'b', 'c']) { /// result.push((*a, *b)); /// } /// assert_eq!(result, vec![(1, 'a'),(2, 'b'),(3, 'c')]); /// ``` #[deprecated( note = "Use [std::iter::zip](https://doc.rust-lang.org/std/iter/fn.zip.html) instead", since = "0.10.4" )] pub fn zip(i: I, j: J) -> Zip where I: IntoIterator, J: IntoIterator, { i.into_iter().zip(j) } /// Takes two iterables and creates a new iterator over both in sequence. /// /// [`IntoIterator`] enabled version of [`Iterator::chain`]. /// /// ## Example /// ``` /// use itertools::chain; /// /// let mut result:Vec = Vec::new(); /// /// for element in chain(&[1, 2, 3], &[4]) { /// result.push(*element); /// } /// assert_eq!(result, vec![1, 2, 3, 4]); /// ``` pub fn chain( i: I, j: J, ) -> iter::Chain<::IntoIter, ::IntoIter> where I: IntoIterator, J: IntoIterator, { i.into_iter().chain(j) } /// Create an iterator that clones each element from `&T` to `T`. /// /// [`IntoIterator`] enabled version of [`Iterator::cloned`]. /// /// ``` /// use itertools::cloned; /// /// assert_eq!(cloned(b"abc").next(), Some(b'a')); /// ``` pub fn cloned<'a, I, T>(iterable: I) -> iter::Cloned where I: IntoIterator, T: Clone + 'a, { iterable.into_iter().cloned() } /// Perform a fold operation over the iterable. /// /// [`IntoIterator`] enabled version of [`Iterator::fold`]. /// /// ``` /// use itertools::fold; /// /// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.); /// ``` pub fn fold(iterable: I, init: B, f: F) -> B where I: IntoIterator, F: FnMut(B, I::Item) -> B, { iterable.into_iter().fold(init, f) } /// Test whether the predicate holds for all elements in the iterable. /// /// [`IntoIterator`] enabled version of [`Iterator::all`]. /// /// ``` /// use itertools::all; /// /// assert!(all(&[1, 2, 3], |elt| *elt > 0)); /// ``` pub fn all(iterable: I, f: F) -> bool where I: IntoIterator, F: FnMut(I::Item) -> bool, { iterable.into_iter().all(f) } /// Test whether the predicate holds for any elements in the iterable. /// /// [`IntoIterator`] enabled version of [`Iterator::any`]. /// /// ``` /// use itertools::any; /// /// assert!(any(&[0, -1, 2], |elt| *elt > 0)); /// ``` pub fn any(iterable: I, f: F) -> bool where I: IntoIterator, F: FnMut(I::Item) -> bool, { iterable.into_iter().any(f) } /// Return the maximum value of the iterable. /// /// [`IntoIterator`] enabled version of [`Iterator::max`]. /// /// ``` /// use itertools::max; /// /// assert_eq!(max(0..10), Some(9)); /// ``` pub fn max(iterable: I) -> Option where I: IntoIterator, I::Item: Ord, { iterable.into_iter().max() } /// Return the minimum value of the iterable. /// /// [`IntoIterator`] enabled version of [`Iterator::min`]. /// /// ``` /// use itertools::min; /// /// assert_eq!(min(0..10), Some(0)); /// ``` pub fn min(iterable: I) -> Option where I: IntoIterator, I::Item: Ord, { iterable.into_iter().min() } /// Combine all iterator elements into one `String`, separated by `sep`. /// /// [`IntoIterator`] enabled version of [`Itertools::join`]. /// /// ``` /// use itertools::join; /// /// assert_eq!(join(&[1, 2, 3], ", "), "1, 2, 3"); /// ``` #[cfg(feature = "use_alloc")] pub fn join(iterable: I, sep: &str) -> String where I: IntoIterator, I::Item: Display, { iterable.into_iter().join(sep) } /// Sort all iterator elements into a new iterator in ascending order. /// /// [`IntoIterator`] enabled version of [`Itertools::sorted`]. /// /// ``` /// use itertools::sorted; /// use itertools::assert_equal; /// /// assert_equal(sorted("rust".chars()), "rstu".chars()); /// ``` #[cfg(feature = "use_alloc")] pub fn sorted(iterable: I) -> VecIntoIter where I: IntoIterator, I::Item: Ord, { iterable.into_iter().sorted() } /// Sort all iterator elements into a new iterator in ascending order. /// This sort is unstable (i.e., may reorder equal elements). /// /// [`IntoIterator`] enabled version of [`Itertools::sorted_unstable`]. /// /// ``` /// use itertools::sorted_unstable; /// use itertools::assert_equal; /// /// assert_equal(sorted_unstable("rust".chars()), "rstu".chars()); /// ``` #[cfg(feature = "use_alloc")] pub fn sorted_unstable(iterable: I) -> VecIntoIter where I: IntoIterator, I::Item: Ord, { iterable.into_iter().sorted_unstable() } itertools-0.13.0/src/group_map.rs000064400000000000000000000013661046102023000150650ustar 00000000000000#![cfg(feature = "use_std")] use std::collections::HashMap; use std::hash::Hash; use std::iter::Iterator; /// Return a `HashMap` of keys mapped to a list of their corresponding values. /// /// See [`.into_group_map()`](crate::Itertools::into_group_map) /// for more information. pub fn into_group_map(iter: I) -> HashMap> where I: Iterator, K: Hash + Eq, { let mut lookup = HashMap::new(); iter.for_each(|(key, val)| { lookup.entry(key).or_insert_with(Vec::new).push(val); }); lookup } pub fn into_group_map_by(iter: I, mut f: F) -> HashMap> where I: Iterator, K: Hash + Eq, F: FnMut(&V) -> K, { into_group_map(iter.map(|v| (f(&v), v))) } itertools-0.13.0/src/groupbylazy.rs000064400000000000000000000414061046102023000154620ustar 00000000000000use alloc::vec::{self, Vec}; use std::cell::{Cell, RefCell}; /// A trait to unify `FnMut` for `ChunkBy` with the chunk key in `IntoChunks` trait KeyFunction { type Key; fn call_mut(&mut self, arg: A) -> Self::Key; } impl KeyFunction for F where F: FnMut(A) -> K + ?Sized, { type Key = K; #[inline] fn call_mut(&mut self, arg: A) -> Self::Key { (*self)(arg) } } /// `ChunkIndex` acts like the grouping key function for `IntoChunks` #[derive(Debug, Clone)] struct ChunkIndex { size: usize, index: usize, key: usize, } impl ChunkIndex { #[inline(always)] fn new(size: usize) -> Self { Self { size, index: 0, key: 0, } } } impl KeyFunction for ChunkIndex { type Key = usize; #[inline(always)] fn call_mut(&mut self, _arg: A) -> Self::Key { if self.index == self.size { self.key += 1; self.index = 0; } self.index += 1; self.key } } #[derive(Clone)] struct GroupInner where I: Iterator, { key: F, iter: I, current_key: Option, current_elt: Option, /// flag set if iterator is exhausted done: bool, /// Index of group we are currently buffering or visiting top_group: usize, /// 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 /// that range is large enough. bottom_group: usize, /// Buffered groups, from `bottom_group` (index 0) to `top_group`. buffer: Vec>, /// index of last group iter that was dropped, /// `usize::MAX` initially when no group was dropped dropped_group: usize, } impl GroupInner where I: Iterator, F: for<'a> KeyFunction<&'a I::Item, Key = K>, K: PartialEq, { /// `client`: Index of group that requests next element #[inline(always)] fn step(&mut self, client: usize) -> Option { /* println!("client={}, bottom_group={}, oldest_buffered_group={}, top_group={}, buffers=[{}]", client, self.bottom_group, self.oldest_buffered_group, self.top_group, self.buffer.iter().map(|elt| elt.len()).format(", ")); */ 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) { self.lookup_buffer(client) } else if self.done { None } else if self.top_group == client { self.step_current() } else { self.step_buffering(client) } } #[inline(never)] fn lookup_buffer(&mut self, client: usize) -> Option { // if `bufidx` doesn't exist in self.buffer, it might be empty let bufidx = client - self.bottom_group; if client < self.oldest_buffered_group { return None; } let elt = self.buffer.get_mut(bufidx).and_then(|queue| queue.next()); if elt.is_none() && client == self.oldest_buffered_group { // FIXME: VecDeque is unfortunately not zero allocation when empty, // so we do this job manually. // `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) { self.oldest_buffered_group += 1; } let nclear = self.oldest_buffered_group - self.bottom_group; if nclear > 0 && nclear >= self.buffer.len() / 2 { let mut i = 0; self.buffer.retain(|buf| { i += 1; debug_assert!(buf.len() == 0 || i > nclear); i > nclear }); self.bottom_group = self.oldest_buffered_group; } } elt } /// Take the next element from the iterator, and set the done /// flag if exhausted. Must not be called after done. #[inline(always)] fn next_element(&mut self) -> Option { debug_assert!(!self.done); match self.iter.next() { None => { self.done = true; None } otherwise => otherwise, } } #[inline(never)] fn step_buffering(&mut self, client: usize) -> Option { // requested a later group -- walk through the current group up to // the requested group index, and buffer the elements (unless // the group is marked as dropped). // Because the `Groups` iterator is always the first to request // each group index, client is the next index efter top_group. debug_assert!(self.top_group + 1 == client); let mut group = Vec::new(); if let Some(elt) = self.current_elt.take() { if self.top_group != self.dropped_group { group.push(elt); } } let mut first_elt = None; // first element of the next group while let Some(elt) = self.next_element() { let key = self.key.call_mut(&elt); match self.current_key.take() { None => {} Some(old_key) => { if old_key != key { self.current_key = Some(key); first_elt = Some(elt); break; } } } self.current_key = Some(key); if self.top_group != self.dropped_group { group.push(elt); } } if self.top_group != self.dropped_group { self.push_next_group(group); } if first_elt.is_some() { self.top_group += 1; debug_assert!(self.top_group == client); } first_elt } fn push_next_group(&mut self, group: Vec) { // When we add a new buffered group, fill up slots between oldest_buffered_group and top_group while self.top_group - self.bottom_group > self.buffer.len() { if self.buffer.is_empty() { self.bottom_group += 1; self.oldest_buffered_group += 1; } else { self.buffer.push(Vec::new().into_iter()); } } self.buffer.push(group.into_iter()); debug_assert!(self.top_group + 1 - self.bottom_group == self.buffer.len()); } /// This is the immediate case, where we use no buffering #[inline] fn step_current(&mut self) -> Option { debug_assert!(!self.done); if let elt @ Some(..) = self.current_elt.take() { return elt; } match self.next_element() { None => None, Some(elt) => { let key = self.key.call_mut(&elt); match self.current_key.take() { None => {} Some(old_key) => { if old_key != key { self.current_key = Some(key); self.current_elt = Some(elt); self.top_group += 1; return None; } } } self.current_key = Some(key); Some(elt) } } } /// Request the just started groups' key. /// /// `client`: Index of group /// /// **Panics** if no group key is available. fn group_key(&mut self, client: usize) -> K { // This can only be called after we have just returned the first // element of a group. // Perform this by simply buffering one more element, grabbing the // next key. debug_assert!(!self.done); debug_assert!(client == self.top_group); debug_assert!(self.current_key.is_some()); debug_assert!(self.current_elt.is_none()); let old_key = self.current_key.take().unwrap(); if let Some(elt) = self.next_element() { let key = self.key.call_mut(&elt); if old_key != key { self.top_group += 1; } self.current_key = Some(key); self.current_elt = Some(elt); } old_key } } impl GroupInner where I: Iterator, { /// Called when a group is dropped fn drop_group(&mut self, client: usize) { // It's only useful to track the maximal index if self.dropped_group == !0 || client > self.dropped_group { self.dropped_group = client; } } } #[deprecated(note = "Use `ChunkBy` instead", since = "0.13.0")] /// See [`ChunkBy`](crate::structs::ChunkBy). pub type GroupBy = ChunkBy; /// `ChunkBy` is the storage for the lazy grouping operation. /// /// If the groups are consumed in their original order, or if each /// group is dropped without keeping it around, then `ChunkBy` uses /// no allocations. It needs allocations only if several group iterators /// are alive at the same time. /// /// This type implements [`IntoIterator`] (it is **not** an iterator /// itself), because the group iterators need to borrow from this /// value. It should be stored in a local variable or temporary and /// iterated. /// /// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct ChunkBy where I: Iterator, { inner: RefCell>, // the group iterator's current index. Keep this in the main value // so that simultaneous iterators all use the same state. index: Cell, } /// Create a new pub fn new(iter: J, f: F) -> ChunkBy where J: IntoIterator, F: FnMut(&J::Item) -> K, { ChunkBy { inner: RefCell::new(GroupInner { key: f, iter: iter.into_iter(), current_key: None, current_elt: None, done: false, top_group: 0, oldest_buffered_group: 0, bottom_group: 0, buffer: Vec::new(), dropped_group: !0, }), index: Cell::new(0), } } impl ChunkBy where I: Iterator, { /// `client`: Index of group that requests next element fn step(&self, client: usize) -> Option where F: FnMut(&I::Item) -> K, K: PartialEq, { self.inner.borrow_mut().step(client) } /// `client`: Index of group fn drop_group(&self, client: usize) { self.inner.borrow_mut().drop_group(client); } } impl<'a, K, I, F> IntoIterator for &'a ChunkBy where I: Iterator, I::Item: 'a, F: FnMut(&I::Item) -> K, K: PartialEq, { type Item = (K, Group<'a, K, I, F>); type IntoIter = Groups<'a, K, I, F>; fn into_iter(self) -> Self::IntoIter { Groups { parent: self } } } /// An iterator that yields the Group iterators. /// /// Iterator element type is `(K, Group)`: /// the group's key `K` and the group's iterator. /// /// 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, I, F> where I: Iterator + 'a, I::Item: 'a, K: 'a, F: 'a, { parent: &'a ChunkBy, } impl<'a, K, I, F> Iterator for Groups<'a, K, I, F> where I: Iterator, I::Item: 'a, F: FnMut(&I::Item) -> K, K: PartialEq, { type Item = (K, Group<'a, K, I, F>); #[inline] fn next(&mut self) -> Option { 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| { let key = inner.group_key(index); ( key, Group { parent: self.parent, index, first: Some(elt), }, ) }) } } /// An iterator for the elements in a single group. /// /// Iterator element type is `I::Item`. pub struct Group<'a, K, I, F> where I: Iterator + 'a, I::Item: 'a, K: 'a, F: 'a, { parent: &'a ChunkBy, index: usize, first: Option, } impl<'a, K, I, F> Drop for Group<'a, K, I, F> where I: Iterator, I::Item: 'a, { fn drop(&mut self) { self.parent.drop_group(self.index); } } 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, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { if let elt @ Some(..) = self.first.take() { return elt; } self.parent.step(self.index) } } ///// IntoChunks ///// /// Create a new pub fn new_chunks(iter: J, size: usize) -> IntoChunks where J: IntoIterator, { IntoChunks { inner: RefCell::new(GroupInner { key: ChunkIndex::new(size), iter: iter.into_iter(), current_key: None, current_elt: None, done: false, top_group: 0, oldest_buffered_group: 0, bottom_group: 0, buffer: Vec::new(), dropped_group: !0, }), index: Cell::new(0), } } /// `ChunkLazy` is the storage for a lazy chunking operation. /// /// `IntoChunks` behaves just like `ChunkBy`: it is iterable, and /// it only buffers if several chunk iterators are alive at the same time. /// /// This type implements [`IntoIterator`] (it is **not** an iterator /// itself), because the chunk iterators need to borrow from this /// value. It should be stored in a local variable or temporary and /// iterated. /// /// Iterator element type is `Chunk`, each chunk's iterator. /// /// See [`.chunks()`](crate::Itertools::chunks) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct IntoChunks where I: Iterator, { inner: RefCell>, // the chunk iterator's current index. Keep this in the main value // so that simultaneous iterators all use the same state. index: Cell, } impl Clone for IntoChunks where I: Clone + Iterator, I::Item: Clone, { clone_fields!(inner, index); } impl IntoChunks where I: Iterator, { /// `client`: Index of chunk that requests next element fn step(&self, client: usize) -> Option { self.inner.borrow_mut().step(client) } /// `client`: Index of chunk fn drop_group(&self, client: usize) { self.inner.borrow_mut().drop_group(client); } } impl<'a, I> IntoIterator for &'a IntoChunks 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 } } } /// 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"] #[derive(Clone)] pub struct Chunks<'a, I> where I: Iterator + 'a, I::Item: 'a, { parent: &'a IntoChunks, } impl<'a, I> Iterator for Chunks<'a, I> where I: Iterator, I::Item: 'a, { type Item = Chunk<'a, I>; #[inline] fn next(&mut self) -> Option { 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), }) } } /// An iterator for the elements in a single chunk. /// /// Iterator element type is `I::Item`. pub struct Chunk<'a, I> where I: Iterator + 'a, I::Item: 'a, { parent: &'a IntoChunks, index: usize, first: Option, } impl<'a, I> Drop for Chunk<'a, I> where I: Iterator, I::Item: 'a, { fn drop(&mut self) { self.parent.drop_group(self.index); } } impl<'a, I> Iterator for Chunk<'a, I> where I: Iterator, I::Item: 'a, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { if let elt @ Some(..) = self.first.take() { return elt; } self.parent.step(self.index) } } itertools-0.13.0/src/grouping_map.rs000064400000000000000000000533561046102023000155710ustar 00000000000000#![cfg(feature = "use_std")] 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) pub type MapForGrouping = MapSpecialCase>; #[derive(Clone)] pub struct GroupingMapFn(F); impl std::fmt::Debug for GroupingMapFn { debug_fmt_fields!(GroupingMapFn,); } impl K> MapSpecialCaseFn for GroupingMapFn { type Out = (K, V); fn call(&mut self, v: V) -> Self::Out { ((self.0)(&v), v) } } pub(crate) fn new_map_for_grouping K>( iter: I, key_mapper: F, ) -> MapForGrouping { MapSpecialCase { iter, f: GroupingMapFn(key_mapper), } } /// Creates a new `GroupingMap` from `iter` pub fn new(iter: I) -> GroupingMap where I: Iterator, K: Hash + Eq, { GroupingMap { iter } } /// `GroupingMapBy` is an intermediate struct for efficient group-and-fold operations. /// /// See [`GroupingMap`] for more informations. pub type GroupingMapBy = GroupingMap>; /// `GroupingMap` is an intermediate struct for efficient group-and-fold operations. /// It groups elements by their key and at the same time fold each group /// using some aggregating operation. /// /// No method on this struct performs temporary allocations. #[derive(Clone, Debug)] #[must_use = "GroupingMap is lazy and do nothing unless consumed"] pub struct GroupingMap { iter: I, } impl GroupingMap where I: Iterator, K: Hash + Eq, { /// This is the generic way to perform any operation on a `GroupingMap`. /// It's suggested to use this method only to implement custom operations /// when the already provided ones are not enough. /// /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements /// of each group sequentially, passing the previously accumulated value, a reference to the key /// and the current element as arguments, and stores the results in an `HashMap`. /// /// The `operation` function is invoked on each element with the following parameters: /// - the current value of the accumulator of the group if there is currently one; /// - a reference to the key of the group this element belongs to; /// - the element from the source being aggregated; /// /// If `operation` returns `Some(element)` then the accumulator is updated with `element`, /// otherwise the previous accumulation is discarded. /// /// Return a `HashMap` associating the key of each group with the result of aggregation of /// that group's elements. If the aggregation of the last element of a group discards the /// accumulator then there won't be an entry associated to that group's key. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![2, 8, 5, 7, 9, 0, 4, 10]; /// let lookup = data.into_iter() /// .into_grouping_map_by(|&n| n % 4) /// .aggregate(|acc, _key, val| { /// if val == 0 || val == 10 { /// None /// } else { /// Some(acc.unwrap_or(0) + val) /// } /// }); /// /// assert_eq!(lookup[&0], 4); // 0 resets the accumulator so only 4 is summed /// assert_eq!(lookup[&1], 5 + 9); /// assert_eq!(lookup.get(&2), None); // 10 resets the accumulator and nothing is summed afterward /// assert_eq!(lookup[&3], 7); /// assert_eq!(lookup.len(), 3); // The final keys are only 0, 1 and 2 /// ``` pub fn aggregate(self, mut operation: FO) -> HashMap where FO: FnMut(Option, &K, V) -> Option, { let mut destination_map = HashMap::new(); self.iter.for_each(|(key, val)| { let acc = destination_map.remove(&key); if let Some(op_res) = operation(acc, &key, val) { destination_map.insert(key, op_res); } }); destination_map } /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements /// of each group sequentially, passing the previously accumulated value, a reference to the key /// and the current element as arguments, and stores the results in a new map. /// /// `init` is called to obtain the initial value of each accumulator. /// /// `operation` is a function that is invoked on each element with the following parameters: /// - the current value of the accumulator of the group; /// - a reference to the key of the group this element belongs to; /// - the element from the source being accumulated. /// /// Return a `HashMap` associating the key of each group with the result of folding that group's elements. /// /// ``` /// use itertools::Itertools; /// /// #[derive(Debug, Default)] /// struct Accumulator { /// acc: usize, /// } /// /// let lookup = (1..=7) /// .into_grouping_map_by(|&n| n % 3) /// .fold_with(|_key, _val| Default::default(), |Accumulator { acc }, _key, val| { /// let acc = acc + val; /// Accumulator { acc } /// }); /// /// assert_eq!(lookup[&0].acc, 3 + 6); /// assert_eq!(lookup[&1].acc, 1 + 4 + 7); /// assert_eq!(lookup[&2].acc, 2 + 5); /// assert_eq!(lookup.len(), 3); /// ``` pub fn fold_with(self, mut init: FI, mut operation: FO) -> HashMap where FI: FnMut(&K, &V) -> R, FO: FnMut(R, &K, V) -> R, { self.aggregate(|acc, key, val| { let acc = acc.unwrap_or_else(|| init(key, &val)); Some(operation(acc, key, val)) }) } /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements /// of each group sequentially, passing the previously accumulated value, a reference to the key /// and the current element as arguments, and stores the results in a new map. /// /// `init` is the value from which will be cloned the initial value of each accumulator. /// /// `operation` is a function that is invoked on each element with the following parameters: /// - 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; /// /// let lookup = (1..=7) /// .into_grouping_map_by(|&n| n % 3) /// .fold(0, |acc, _key, val| acc + val); /// /// assert_eq!(lookup[&0], 3 + 6); /// assert_eq!(lookup[&1], 1 + 4 + 7); /// assert_eq!(lookup[&2], 2 + 5); /// assert_eq!(lookup.len(), 3); /// ``` pub fn fold(self, init: R, operation: FO) -> HashMap where R: Clone, FO: FnMut(R, &K, V) -> R, { self.fold_with(|_, _| init.clone(), operation) } /// 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. /// /// This is similar to [`fold`] but the initial value of the accumulator is the first element of the group. /// /// `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. /// /// [`fold`]: GroupingMap::fold /// /// ``` /// use itertools::Itertools; /// /// let lookup = (1..=7) /// .into_grouping_map_by(|&n| n % 3) /// .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 reduce(self, mut operation: FO) -> HashMap where FO: FnMut(V, &K, V) -> V, { self.aggregate(|acc, key, val| { Some(match acc { Some(acc) => operation(acc, key, val), None => val, }) }) } /// See [`.reduce()`](GroupingMap::reduce). #[deprecated(note = "Use .reduce() instead", since = "0.13.0")] pub fn fold_first(self, operation: FO) -> HashMap where FO: FnMut(V, &K, V) -> V, { self.reduce(operation) } /// Groups elements from the `GroupingMap` source by key and collects the elements of each group in /// an instance of `C`. The iteration order is preserved when inserting elements. /// /// Return a `HashMap` associating the key of each group with the collection containing that group's elements. /// /// ``` /// use itertools::Itertools; /// use std::collections::HashSet; /// /// let lookup = vec![0, 1, 2, 3, 4, 5, 6, 2, 3, 6].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .collect::>(); /// /// assert_eq!(lookup[&0], vec![0, 3, 6].into_iter().collect::>()); /// assert_eq!(lookup[&1], vec![1, 4].into_iter().collect::>()); /// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect::>()); /// assert_eq!(lookup.len(), 3); /// ``` pub fn collect(self) -> HashMap where C: Default + Extend, { 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 } /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group. /// /// If several elements are equally maximum, the last element is picked. /// /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements. /// /// ``` /// use itertools::Itertools; /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .max(); /// /// assert_eq!(lookup[&0], 12); /// assert_eq!(lookup[&1], 7); /// assert_eq!(lookup[&2], 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn max(self) -> HashMap where V: Ord, { self.max_by(|_, v1, v2| V::cmp(v1, v2)) } /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group /// with respect to the specified comparison function. /// /// If several elements are equally maximum, the last element is picked. /// /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements. /// /// ``` /// use itertools::Itertools; /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .max_by(|_key, x, y| y.cmp(x)); /// /// assert_eq!(lookup[&0], 3); /// assert_eq!(lookup[&1], 1); /// assert_eq!(lookup[&2], 5); /// assert_eq!(lookup.len(), 3); /// ``` pub fn max_by(self, mut compare: F) -> HashMap where F: FnMut(&K, &V, &V) -> Ordering, { self.reduce(|acc, key, val| match compare(key, &acc, &val) { Ordering::Less | Ordering::Equal => val, Ordering::Greater => acc, }) } /// Groups elements from the `GroupingMap` source by key and finds the element of each group /// that gives the maximum from the specified function. /// /// If several elements are equally maximum, the last element is picked. /// /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements. /// /// ``` /// use itertools::Itertools; /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .max_by_key(|_key, &val| val % 4); /// /// assert_eq!(lookup[&0], 3); /// assert_eq!(lookup[&1], 7); /// assert_eq!(lookup[&2], 5); /// assert_eq!(lookup.len(), 3); /// ``` pub fn max_by_key(self, mut f: F) -> HashMap where F: FnMut(&K, &V) -> CK, CK: Ord, { self.max_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2))) } /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group. /// /// If several elements are equally minimum, the first element is picked. /// /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements. /// /// ``` /// use itertools::Itertools; /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .min(); /// /// assert_eq!(lookup[&0], 3); /// assert_eq!(lookup[&1], 1); /// assert_eq!(lookup[&2], 5); /// assert_eq!(lookup.len(), 3); /// ``` pub fn min(self) -> HashMap where V: Ord, { self.min_by(|_, v1, v2| V::cmp(v1, v2)) } /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group /// with respect to the specified comparison function. /// /// If several elements are equally minimum, the first element is picked. /// /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements. /// /// ``` /// use itertools::Itertools; /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .min_by(|_key, x, y| y.cmp(x)); /// /// assert_eq!(lookup[&0], 12); /// assert_eq!(lookup[&1], 7); /// assert_eq!(lookup[&2], 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn min_by(self, mut compare: F) -> HashMap where F: FnMut(&K, &V, &V) -> Ordering, { self.reduce(|acc, key, val| match compare(key, &acc, &val) { Ordering::Less | Ordering::Equal => acc, Ordering::Greater => val, }) } /// Groups elements from the `GroupingMap` source by key and finds the element of each group /// that gives the minimum from the specified function. /// /// If several elements are equally minimum, the first element is picked. /// /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements. /// /// ``` /// use itertools::Itertools; /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .min_by_key(|_key, &val| val % 4); /// /// assert_eq!(lookup[&0], 12); /// assert_eq!(lookup[&1], 4); /// assert_eq!(lookup[&2], 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn min_by_key(self, mut f: F) -> HashMap where F: FnMut(&K, &V) -> CK, CK: Ord, { self.min_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2))) } /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of /// each group. /// /// If several elements are equally maximum, the last element is picked. /// If several elements are equally minimum, the first element is picked. /// /// See [`Itertools::minmax`](crate::Itertools::minmax) for the non-grouping version. /// /// Differences from the non grouping version: /// - It never produces a `MinMaxResult::NoElements` /// - It doesn't have any speedup /// /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements. /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{OneElement, MinMax}; /// /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .minmax(); /// /// assert_eq!(lookup[&0], MinMax(3, 12)); /// assert_eq!(lookup[&1], MinMax(1, 7)); /// assert_eq!(lookup[&2], OneElement(5)); /// assert_eq!(lookup.len(), 3); /// ``` pub fn minmax(self) -> HashMap> where V: Ord, { self.minmax_by(|_, v1, v2| V::cmp(v1, v2)) } /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of /// each group with respect to the specified comparison function. /// /// If several elements are equally maximum, the last element is picked. /// If several elements are equally minimum, the first element is picked. /// /// It has the same differences from the non-grouping version as `minmax`. /// /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements. /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{OneElement, MinMax}; /// /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .minmax_by(|_key, x, y| y.cmp(x)); /// /// assert_eq!(lookup[&0], MinMax(12, 3)); /// assert_eq!(lookup[&1], MinMax(7, 1)); /// assert_eq!(lookup[&2], OneElement(5)); /// assert_eq!(lookup.len(), 3); /// ``` pub fn minmax_by(self, mut compare: F) -> HashMap> where F: FnMut(&K, &V, &V) -> Ordering, { self.aggregate(|acc, key, val| { Some(match acc { Some(MinMaxResult::OneElement(e)) => { if compare(key, &val, &e) == Ordering::Less { MinMaxResult::MinMax(val, e) } else { MinMaxResult::MinMax(e, val) } } Some(MinMaxResult::MinMax(min, max)) => { if compare(key, &val, &min) == Ordering::Less { MinMaxResult::MinMax(val, max) } else if compare(key, &val, &max) != Ordering::Less { MinMaxResult::MinMax(min, val) } else { MinMaxResult::MinMax(min, max) } } None => MinMaxResult::OneElement(val), Some(MinMaxResult::NoElements) => unreachable!(), }) }) } /// Groups elements from the `GroupingMap` source by key and find the elements of each group /// that gives the minimum and maximum from the specified function. /// /// If several elements are equally maximum, the last element is picked. /// If several elements are equally minimum, the first element is picked. /// /// It has the same differences from the non-grouping version as `minmax`. /// /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements. /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{OneElement, MinMax}; /// /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .minmax_by_key(|_key, &val| val % 4); /// /// assert_eq!(lookup[&0], MinMax(12, 3)); /// assert_eq!(lookup[&1], MinMax(4, 7)); /// assert_eq!(lookup[&2], OneElement(5)); /// assert_eq!(lookup.len(), 3); /// ``` pub fn minmax_by_key(self, mut f: F) -> HashMap> where F: FnMut(&K, &V) -> CK, CK: Ord, { 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.reduce(|acc, _, val| acc + val)`. /// It is more limited than `Iterator::sum` since it doesn't use the `Sum` trait. /// /// Returns a `HashMap` associating the key of each group with the sum of that group's elements. /// /// ``` /// use itertools::Itertools; /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .sum(); /// /// assert_eq!(lookup[&0], 3 + 9 + 12); /// assert_eq!(lookup[&1], 1 + 4 + 7); /// assert_eq!(lookup[&2], 5 + 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn sum(self) -> HashMap where V: Add, { self.reduce(|acc, _, val| acc + val) } /// Groups elements from the `GroupingMap` source by key and multiply them. /// /// This is just a shorthand for `self.reduce(|acc, _, val| acc * val)`. /// It is more limited than `Iterator::product` since it doesn't use the `Product` trait. /// /// Returns a `HashMap` associating the key of each group with the product of that group's elements. /// /// ``` /// use itertools::Itertools; /// /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter() /// .into_grouping_map_by(|&n| n % 3) /// .product(); /// /// assert_eq!(lookup[&0], 3 * 9 * 12); /// assert_eq!(lookup[&1], 1 * 4 * 7); /// assert_eq!(lookup[&2], 5 * 8); /// assert_eq!(lookup.len(), 3); /// ``` pub fn product(self) -> HashMap where V: Mul, { self.reduce(|acc, _, val| acc * val) } } itertools-0.13.0/src/impl_macros.rs000064400000000000000000000015331046102023000153750ustar 00000000000000//! //! Implementation's internal macros macro_rules! debug_fmt_fields { ($tyname:ident, $($($field:tt/*TODO ideally we would accept ident or tuple element here*/).+),*) => { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { f.debug_struct(stringify!($tyname)) $( .field(stringify!($($field).+), &self.$($field).+) )* .finish() } } } macro_rules! clone_fields { ($($field:ident),*) => { #[inline] // TODO is this sensible? fn clone(&self) -> Self { Self { $($field: self.$field.clone(),)* } } } } macro_rules! ignore_ident{ ($id:ident, $($t:tt)*) => {$($t)*}; } macro_rules! count_ident { () => {0}; ($i0:ident $($i:ident)*) => {1 + count_ident!($($i)*)}; } itertools-0.13.0/src/intersperse.rs000064400000000000000000000072211046102023000154330ustar 00000000000000use super::size_hint; use std::iter::{Fuse, FusedIterator}; pub trait IntersperseElement { fn generate(&mut self) -> Item; } #[derive(Debug, Clone)] pub struct IntersperseElementSimple(Item); impl IntersperseElement for IntersperseElementSimple { fn generate(&mut self) -> Item { self.0.clone() } } /// An iterator adaptor to insert a particular value /// between each element of the adapted iterator. /// /// Iterator element type is `I::Item` /// /// This iterator is *fused*. /// /// See [`.intersperse()`](crate::Itertools::intersperse) for more information. pub type Intersperse = IntersperseWith::Item>>; /// Create a new Intersperse iterator pub fn intersperse(iter: I, elt: I::Item) -> Intersperse where I: Iterator, { intersperse_with(iter, IntersperseElementSimple(elt)) } impl Item> IntersperseElement for F { fn generate(&mut self) -> Item { self() } } /// An iterator adaptor to insert a particular value created by a function /// between each element of the adapted iterator. /// /// Iterator element type is `I::Item` /// /// This iterator is *fused*. /// /// See [`.intersperse_with()`](crate::Itertools::intersperse_with) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] pub struct IntersperseWith where I: Iterator, { element: ElemF, iter: Fuse, /// `peek` is None while no item have been taken out of `iter` (at definition). /// Then `peek` will alternatively be `Some(None)` and `Some(Some(item))`, /// where `None` indicates it's time to generate from `element` (unless `iter` is empty). peek: Option>, } /// Create a new `IntersperseWith` iterator pub fn intersperse_with(iter: I, elt: ElemF) -> IntersperseWith where I: Iterator, { IntersperseWith { peek: None, iter: iter.fuse(), element: elt, } } impl Iterator for IntersperseWith where I: Iterator, ElemF: IntersperseElement, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { 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) { 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(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) = peek.unwrap_or_else(|| iter.next()) { accum = f(accum, x); } iter.fold(accum, |accum, x| { let accum = f(accum, element.generate()); f(accum, x) }) } } impl FusedIterator for IntersperseWith where I: Iterator, ElemF: IntersperseElement, { } itertools-0.13.0/src/iter_index.rs000064400000000000000000000047731046102023000152330ustar 00000000000000use core::iter::{Skip, Take}; use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; #[cfg(doc)] use crate::Itertools; mod private_iter_index { use core::ops; pub trait Sealed {} impl Sealed for ops::Range {} impl Sealed for ops::RangeInclusive {} impl Sealed for ops::RangeTo {} impl Sealed for ops::RangeToInclusive {} impl Sealed for ops::RangeFrom {} impl Sealed for ops::RangeFull {} } /// Used by [`Itertools::get`] to know which iterator /// to turn different ranges into. pub trait IteratorIndex: private_iter_index::Sealed where I: Iterator, { /// The type returned for this type of index. type Output: Iterator; /// Returns an adapted iterator for the current index. /// /// Prefer calling [`Itertools::get`] instead /// of calling this directly. fn index(self, from: I) -> Self::Output; } impl IteratorIndex for Range where I: Iterator, { type Output = Skip>; fn index(self, iter: I) -> Self::Output { iter.take(self.end).skip(self.start) } } impl IteratorIndex for RangeInclusive where I: Iterator, { type Output = Take>; fn index(self, iter: I) -> Self::Output { // end - start + 1 without overflowing if possible let length = if *self.end() == usize::MAX { assert_ne!(*self.start(), 0); self.end() - self.start() + 1 } else { (self.end() + 1).saturating_sub(*self.start()) }; iter.skip(*self.start()).take(length) } } impl IteratorIndex for RangeTo where I: Iterator, { type Output = Take; fn index(self, iter: I) -> Self::Output { iter.take(self.end) } } impl IteratorIndex for RangeToInclusive where I: Iterator, { type Output = Take; fn index(self, iter: I) -> Self::Output { assert_ne!(self.end, usize::MAX); iter.take(self.end + 1) } } impl IteratorIndex for RangeFrom where I: Iterator, { type Output = Skip; fn index(self, iter: I) -> Self::Output { iter.skip(self.start) } } impl IteratorIndex for RangeFull where I: Iterator, { type Output = I; fn index(self, iter: I) -> Self::Output { iter } } pub fn get(iter: I, index: R) -> R::Output where I: IntoIterator, R: IteratorIndex, { index.index(iter.into_iter()) } itertools-0.13.0/src/k_smallest.rs000064400000000000000000000066201046102023000152300ustar 00000000000000use alloc::vec::Vec; use core::cmp::Ordering; /// Consumes a given iterator, returning the minimum elements in **ascending** order. pub(crate) fn k_smallest_general(iter: I, k: usize, mut comparator: F) -> Vec where I: Iterator, F: FnMut(&I::Item, &I::Item) -> Ordering, { /// Sift the element currently at `origin` away from the root until it is properly ordered. /// /// This will leave **larger** elements closer to the root of the heap. fn sift_down(heap: &mut [T], is_less_than: &mut F, mut origin: usize) where F: FnMut(&T, &T) -> bool, { #[inline] fn children_of(n: usize) -> (usize, usize) { (2 * n + 1, 2 * n + 2) } while origin < heap.len() { let (left_idx, right_idx) = children_of(origin); if left_idx >= heap.len() { return; } let replacement_idx = if right_idx < heap.len() && is_less_than(&heap[left_idx], &heap[right_idx]) { right_idx } else { left_idx }; if is_less_than(&heap[origin], &heap[replacement_idx]) { heap.swap(origin, replacement_idx); origin = replacement_idx; } else { return; } } } if k == 0 { iter.last(); return Vec::new(); } if k == 1 { return iter.min_by(comparator).into_iter().collect(); } let mut iter = iter.fuse(); let mut storage: Vec = iter.by_ref().take(k).collect(); let mut is_less_than = move |a: &_, b: &_| comparator(a, b) == Ordering::Less; // Rearrange the storage into a valid heap by reordering from the second-bottom-most layer up to the root. // Slightly faster than ordering on each insert, but only by a factor of lg(k). // The resulting heap has the **largest** item on top. for i in (0..=(storage.len() / 2)).rev() { sift_down(&mut storage, &mut is_less_than, i); } iter.for_each(|val| { debug_assert_eq!(storage.len(), k); if is_less_than(&val, &storage[0]) { // Treating this as an push-and-pop saves having to write a sift-up implementation. // https://en.wikipedia.org/wiki/Binary_heap#Insert_then_extract storage[0] = val; // We retain the smallest items we've seen so far, but ordered largest first so we can drop the largest efficiently. sift_down(&mut storage, &mut is_less_than, 0); } }); // Ultimately the items need to be in least-first, strict order, but the heap is currently largest-first. // To achieve this, repeatedly, // 1) "pop" the largest item off the heap into the tail slot of the underlying storage, // 2) shrink the logical size of the heap by 1, // 3) restore the heap property over the remaining items. let mut heap = &mut storage[..]; while heap.len() > 1 { let last_idx = heap.len() - 1; heap.swap(0, last_idx); // Sifting over a truncated slice means that the sifting will not disturb already popped elements. heap = &mut heap[..last_idx]; sift_down(heap, &mut is_less_than, 0); } storage } #[inline] pub(crate) fn key_to_cmp(mut key: F) -> impl FnMut(&T, &T) -> Ordering where F: FnMut(&T) -> K, K: Ord, { move |a, b| key(a).cmp(&key(b)) } itertools-0.13.0/src/kmerge_impl.rs000064400000000000000000000145451046102023000153720ustar 00000000000000use crate::size_hint; use crate::Itertools; use alloc::vec::Vec; use std::fmt; use std::iter::FusedIterator; use std::mem::replace; /// Head element and Tail iterator pair /// /// `PartialEq`, `Eq`, `PartialOrd` and `Ord` are implemented by comparing sequences based on /// first items (which are guaranteed to exist). /// /// The meanings of `PartialOrd` and `Ord` are reversed so as to turn the heap used in /// `KMerge` into a min-heap. #[derive(Debug)] struct HeadTail where I: Iterator, { head: I::Item, tail: I, } impl HeadTail where I: Iterator, { /// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the `Iterator` is empty. fn new(mut it: I) -> Option { let head = it.next(); head.map(|h| Self { head: h, tail: it }) } /// Get the next element and update `head`, returning the old head in `Some`. /// /// Returns `None` when the tail is exhausted (only `head` then remains). fn next(&mut self) -> Option { if let Some(next) = self.tail.next() { Some(replace(&mut self.head, next)) } else { None } } /// Hints at the size of the sequence, same as the `Iterator` method. fn size_hint(&self) -> (usize, Option) { size_hint::add_scalar(self.tail.size_hint(), 1) } } impl Clone for HeadTail where I: Iterator + Clone, I::Item: Clone, { clone_fields!(head, tail); } /// Make `data` a heap (min-heap w.r.t the sorting). fn heapify(data: &mut [T], mut less_than: S) where S: FnMut(&T, &T) -> bool, { for i in (0..data.len() / 2).rev() { sift_down(data, i, &mut less_than); } } /// Sift down element at `index` (`heap` is a min-heap wrt the ordering) fn sift_down(heap: &mut [T], index: usize, mut less_than: S) where S: FnMut(&T, &T) -> bool, { debug_assert!(index <= heap.len()); let mut pos = index; let mut child = 2 * pos + 1; // Require the right child to be present // This allows to find the index of the smallest child without a branch // that wouldn't be predicted if present while child + 1 < heap.len() { // pick the smaller of the two children // use arithmetic to avoid an unpredictable branch child += less_than(&heap[child + 1], &heap[child]) as usize; // sift down is done if we are already in order if !less_than(&heap[child], &heap[pos]) { return; } heap.swap(pos, child); pos = child; child = 2 * pos + 1; } // Check if the last (left) child was an only child // if it is then it has to be compared with the parent if child + 1 == heap.len() && less_than(&heap[child], &heap[pos]) { heap.swap(pos, child); } } /// An iterator adaptor that merges an abitrary number of base iterators in ascending order. /// If all base iterators are sorted (ascending), the result is sorted. /// /// Iterator element type is `I::Item`. /// /// See [`.kmerge()`](crate::Itertools::kmerge) for more information. pub type KMerge = KMergeBy; pub trait KMergePredicate { fn kmerge_pred(&mut self, a: &T, b: &T) -> bool; } #[derive(Clone, Debug)] pub struct KMergeByLt; impl KMergePredicate for KMergeByLt { fn kmerge_pred(&mut self, a: &T, b: &T) -> bool { a < b } } impl bool> KMergePredicate for F { fn kmerge_pred(&mut self, a: &T, b: &T) -> bool { self(a, b) } } /// Create an iterator that merges elements of the contained iterators using /// the ordering function. /// /// [`IntoIterator`] enabled version of [`Itertools::kmerge`]. /// /// ``` /// use itertools::kmerge; /// /// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) { /// /* loop body */ /// } /// ``` pub fn kmerge(iterable: I) -> KMerge<::IntoIter> where I: IntoIterator, I::Item: IntoIterator, <::Item as IntoIterator>::Item: PartialOrd, { kmerge_by(iterable, KMergeByLt) } /// An iterator adaptor that merges an abitrary number of base iterators /// according to an ordering function. /// /// Iterator element type is `I::Item`. /// /// See [`.kmerge_by()`](crate::Itertools::kmerge_by) for more /// information. #[must_use = "this iterator adaptor is not lazy but does nearly nothing unless consumed"] pub struct KMergeBy where I: Iterator, { heap: Vec>, less_than: F, } impl fmt::Debug for KMergeBy where I: Iterator + fmt::Debug, I::Item: fmt::Debug, { debug_fmt_fields!(KMergeBy, heap); } /// Create an iterator that merges elements of the contained iterators. /// /// [`IntoIterator`] enabled version of [`Itertools::kmerge_by`]. pub fn kmerge_by( iterable: I, mut less_than: F, ) -> KMergeBy<::IntoIter, F> where I: IntoIterator, I::Item: IntoIterator, F: KMergePredicate<<::Item as IntoIterator>::Item>, { let iter = iterable.into_iter(); let (lower, _) = iter.size_hint(); let mut heap: Vec<_> = Vec::with_capacity(lower); heap.extend(iter.filter_map(|it| HeadTail::new(it.into_iter()))); heapify(&mut heap, |a, b| less_than.kmerge_pred(&a.head, &b.head)); KMergeBy { heap, less_than } } impl Clone for KMergeBy where I: Iterator + Clone, I::Item: Clone, F: Clone, { clone_fields!(heap, less_than); } impl Iterator for KMergeBy where I: Iterator, F: KMergePredicate, { type Item = I::Item; fn next(&mut self) -> Option { if self.heap.is_empty() { return None; } let result = if let Some(next) = self.heap[0].next() { next } else { 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) }); Some(result) } fn size_hint(&self) -> (usize, Option) { #[allow(deprecated)] //TODO: once msrv hits 1.51. replace `fold1` with `reduce` self.heap .iter() .map(|i| i.size_hint()) .fold1(size_hint::add) .unwrap_or((0, Some(0))) } } impl FusedIterator for KMergeBy where I: Iterator, F: KMergePredicate, { } itertools-0.13.0/src/lazy_buffer.rs000064400000000000000000000027641046102023000154070ustar 00000000000000use alloc::vec::Vec; use std::iter::Fuse; use std::ops::Index; use crate::size_hint::{self, SizeHint}; #[derive(Debug, Clone)] pub struct LazyBuffer { it: Fuse, buffer: Vec, } impl LazyBuffer where I: Iterator, { pub fn new(it: I) -> Self { Self { it: it.fuse(), buffer: Vec::new(), } } pub fn len(&self) -> usize { 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 let Some(x) = self.it.next() { self.buffer.push(x); true } else { false } } pub fn prefill(&mut self, len: usize) { let buffer_len = self.buffer.len(); if len > buffer_len { let delta = len - buffer_len; self.buffer.extend(self.it.by_ref().take(delta)); } } } impl LazyBuffer where I: Iterator, I::Item: Clone, { pub fn get_at(&self, indices: &[usize]) -> Vec { indices.iter().map(|i| self.buffer[*i].clone()).collect() } } impl Index for LazyBuffer where I: Iterator, I::Item: Sized, Vec: Index, { type Output = as Index>::Output; fn index(&self, index: J) -> &Self::Output { self.buffer.index(index) } } itertools-0.13.0/src/lib.rs000064400000000000000000004401451046102023000136440ustar 00000000000000#![warn(missing_docs, clippy::default_numeric_fallback)] #![crate_name = "itertools"] #![cfg_attr(not(feature = "use_std"), no_std)] //! Extra iterator adaptors, functions and macros. //! //! To extend [`Iterator`] with methods in this crate, import //! the [`Itertools`] trait: //! //! ``` //! use itertools::Itertools; //! ``` //! //! Now, new methods like [`interleave`](Itertools::interleave) //! are available on all iterators: //! //! ``` //! use itertools::Itertools; //! //! let it = (1..3).interleave(vec![-1, -2]); //! itertools::assert_equal(it, vec![1, -1, 2, -2]); //! ``` //! //! Most iterator methods are also provided as functions (with the benefit //! that they convert parameters using [`IntoIterator`]): //! //! ``` //! use itertools::interleave; //! //! for elt in interleave(&[1, 2, 3], &[2, 3, 4]) { //! /* loop body */ //! } //! ``` //! //! ## Crate Features //! //! - `use_std` //! - Enabled by default. //! - Disable to compile itertools using `#![no_std]`. This disables //! any item that depend on allocations (see the `use_alloc` feature) //! and hash maps (like `unique`, `counts`, `into_grouping_map` and more). //! - `use_alloc` //! - Enabled by default. //! - Enables any item that depend on allocations (like `chunk_by`, //! `kmerge`, `join` and many more). //! //! ## Rust Version //! //! This version of itertools requires Rust 1.43.1 or later. #[cfg(not(feature = "use_std"))] extern crate core as std; #[cfg(feature = "use_alloc")] extern crate alloc; #[cfg(feature = "use_alloc")] use alloc::{collections::VecDeque, string::String, vec::Vec}; pub use either::Either; use core::borrow::Borrow; use std::cmp::Ordering; #[cfg(feature = "use_std")] use std::collections::HashMap; #[cfg(feature = "use_std")] use std::collections::HashSet; use std::fmt; #[cfg(feature = "use_alloc")] use std::fmt::Write; #[cfg(feature = "use_std")] use std::hash::Hash; use std::iter::{once, IntoIterator}; #[cfg(feature = "use_alloc")] type VecDequeIntoIter = alloc::collections::vec_deque::IntoIter; #[cfg(feature = "use_alloc")] type VecIntoIter = alloc::vec::IntoIter; use std::iter::FromIterator; #[macro_use] mod impl_macros; // for compatibility with no std and macros #[doc(hidden)] pub use std::iter as __std_iter; /// The concrete iterator types. pub mod structs { #[cfg(feature = "use_alloc")] pub use crate::adaptors::MultiProduct; pub use crate::adaptors::{ Batching, Coalesce, Dedup, DedupBy, DedupByWithCount, DedupWithCount, FilterMapOk, FilterOk, Interleave, InterleaveShortest, MapInto, MapOk, Positions, Product, PutBack, TakeWhileRef, TupleCombinations, Update, WhileSome, }; #[cfg(feature = "use_alloc")] pub use crate::combinations::Combinations; #[cfg(feature = "use_alloc")] pub use crate::combinations_with_replacement::CombinationsWithReplacement; pub use crate::cons_tuples_impl::ConsTuples; #[cfg(feature = "use_std")] pub use crate::duplicates_impl::{Duplicates, DuplicatesBy}; pub use crate::exactly_one_err::ExactlyOneError; pub use crate::flatten_ok::FlattenOk; pub use crate::format::{Format, FormatWith}; #[allow(deprecated)] #[cfg(feature = "use_alloc")] pub use crate::groupbylazy::GroupBy; #[cfg(feature = "use_alloc")] pub use crate::groupbylazy::{Chunk, ChunkBy, Chunks, Group, Groups, IntoChunks}; #[cfg(feature = "use_std")] pub use crate::grouping_map::{GroupingMap, GroupingMapBy}; pub use crate::intersperse::{Intersperse, IntersperseWith}; #[cfg(feature = "use_alloc")] pub use crate::kmerge_impl::{KMerge, KMergeBy}; pub use crate::merge_join::{Merge, MergeBy, MergeJoinBy}; #[cfg(feature = "use_alloc")] pub use crate::multipeek_impl::MultiPeek; pub use crate::pad_tail::PadUsing; #[cfg(feature = "use_alloc")] pub use crate::peek_nth::PeekNth; pub use crate::peeking_take_while::PeekingTakeWhile; #[cfg(feature = "use_alloc")] pub use crate::permutations::Permutations; #[cfg(feature = "use_alloc")] pub use crate::powerset::Powerset; pub use crate::process_results_impl::ProcessResults; #[cfg(feature = "use_alloc")] pub use crate::put_back_n_impl::PutBackN; #[cfg(feature = "use_alloc")] pub use crate::rciter_impl::RcIter; pub use crate::repeatn::RepeatN; #[allow(deprecated)] pub use crate::sources::{Iterate, Unfold}; pub use crate::take_while_inclusive::TakeWhileInclusive; #[cfg(feature = "use_alloc")] pub use crate::tee::Tee; pub use crate::tuple_impl::{CircularTupleWindows, TupleBuffer, TupleWindows, Tuples}; #[cfg(feature = "use_std")] pub use crate::unique_impl::{Unique, UniqueBy}; pub use crate::with_position::WithPosition; pub use crate::zip_eq_impl::ZipEq; pub use crate::zip_longest::ZipLongest; pub use crate::ziptuple::Zip; } /// Traits helpful for using certain `Itertools` methods in generic contexts. pub mod traits { pub use crate::iter_index::IteratorIndex; pub use crate::tuple_impl::HomogeneousTuple; } pub use crate::concat_impl::concat; pub use crate::cons_tuples_impl::cons_tuples; pub use crate::diff::diff_with; pub use crate::diff::Diff; #[cfg(feature = "use_alloc")] pub use crate::kmerge_impl::kmerge_by; pub use crate::minmax::MinMaxResult; pub use crate::peeking_take_while::PeekingNext; pub use crate::process_results_impl::process_results; pub use crate::repeatn::repeat_n; #[allow(deprecated)] pub use crate::sources::{iterate, unfold}; #[allow(deprecated)] pub use crate::structs::*; pub use crate::unziptuple::{multiunzip, MultiUnzip}; pub use crate::with_position::Position; pub use crate::ziptuple::multizip; mod adaptors; mod either_or_both; pub use crate::either_or_both::EitherOrBoth; #[doc(hidden)] pub mod free; #[doc(inline)] pub use crate::free::*; #[cfg(feature = "use_alloc")] mod combinations; #[cfg(feature = "use_alloc")] mod combinations_with_replacement; mod concat_impl; mod cons_tuples_impl; mod diff; #[cfg(feature = "use_std")] mod duplicates_impl; mod exactly_one_err; #[cfg(feature = "use_alloc")] mod extrema_set; mod flatten_ok; mod format; #[cfg(feature = "use_alloc")] mod group_map; #[cfg(feature = "use_alloc")] mod groupbylazy; #[cfg(feature = "use_std")] mod grouping_map; mod intersperse; mod iter_index; #[cfg(feature = "use_alloc")] mod k_smallest; #[cfg(feature = "use_alloc")] mod kmerge_impl; #[cfg(feature = "use_alloc")] mod lazy_buffer; mod merge_join; mod minmax; #[cfg(feature = "use_alloc")] mod multipeek_impl; mod pad_tail; #[cfg(feature = "use_alloc")] mod peek_nth; mod peeking_take_while; #[cfg(feature = "use_alloc")] mod permutations; #[cfg(feature = "use_alloc")] mod powerset; mod process_results_impl; #[cfg(feature = "use_alloc")] mod put_back_n_impl; #[cfg(feature = "use_alloc")] mod rciter_impl; mod repeatn; mod size_hint; mod sources; mod take_while_inclusive; #[cfg(feature = "use_alloc")] mod tee; mod tuple_impl; #[cfg(feature = "use_std")] mod unique_impl; mod unziptuple; mod with_position; mod zip_eq_impl; mod zip_longest; mod ziptuple; #[macro_export] /// Create an iterator over the “cartesian product” of iterators. /// /// Iterator element type is like `(A, B, ..., E)` if formed /// from iterators `(I, J, ..., M)` with element types `I::Item = A`, `J::Item = B`, etc. /// /// ``` /// # use itertools::iproduct; /// # /// # fn main() { /// // Iterate over the coordinates of a 4 x 4 x 4 grid /// // from (0, 0, 0), (0, 0, 1), .., (0, 1, 0), (0, 1, 1), .. etc until (3, 3, 3) /// for (i, j, k) in iproduct!(0..4, 0..4, 0..4) { /// // .. /// } /// # } /// ``` macro_rules! iproduct { (@flatten $I:expr,) => ( $I ); (@flatten $I:expr, $J:expr, $($K:expr,)*) => ( $crate::iproduct!(@flatten $crate::cons_tuples($crate::iproduct!($I, $J)), $($K,)*) ); () => ( $crate::__std_iter::once(()) ); ($I:expr $(,)?) => ( $crate::__std_iter::IntoIterator::into_iter($I).map(|elt| (elt,)) ); ($I:expr, $J:expr $(,)?) => ( $crate::Itertools::cartesian_product( $crate::__std_iter::IntoIterator::into_iter($I), $crate::__std_iter::IntoIterator::into_iter($J), ) ); ($I:expr, $J:expr, $($K:expr),+ $(,)?) => ( $crate::iproduct!(@flatten $crate::iproduct!($I, $J), $($K,)+) ); } #[macro_export] /// Create an iterator running multiple iterators in lockstep. /// /// The `izip!` iterator yields elements until any subiterator /// returns `None`. /// /// This is a version of the standard ``.zip()`` that's supporting more than /// two iterators. The iterator element type is a tuple with one element /// from each of the input iterators. Just like ``.zip()``, the iteration stops /// when the shortest of the inputs reaches its end. /// /// **Note:** The result of this macro is in the general case an iterator /// composed of repeated `.zip()` and a `.map()`; it has an anonymous type. /// The special cases of one and two arguments produce the equivalent of /// `$a.into_iter()` and `$a.into_iter().zip($b)` respectively. /// /// Prefer this macro `izip!()` over [`multizip`] for the performance benefits /// of using the standard library `.zip()`. /// /// ``` /// # use itertools::izip; /// # /// # fn main() { /// /// // iterate over three sequences side-by-side /// let mut results = [0, 0, 0, 0]; /// let inputs = [3, 7, 9, 6]; /// /// for (r, index, input) in izip!(&mut results, 0..10, &inputs) { /// *r = index * 10 + input; /// } /// /// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]); /// # } /// ``` macro_rules! izip { // @closure creates a tuple-flattening closure for .map() call. usage: // @closure partial_pattern => partial_tuple , rest , of , iterators // eg. izip!( @closure ((a, b), c) => (a, b, c) , dd , ee ) ( @closure $p:pat => $tup:expr ) => { |$p| $tup }; // The "b" identifier is a different identifier on each recursion level thanks to hygiene. ( @closure $p:pat => ( $($tup:tt)* ) , $_iter:expr $( , $tail:expr )* ) => { $crate::izip!(@closure ($p, b) => ( $($tup)*, b ) $( , $tail )*) }; // unary ($first:expr $(,)*) => { $crate::__std_iter::IntoIterator::into_iter($first) }; // binary ($first:expr, $second:expr $(,)*) => { $crate::izip!($first) .zip($second) }; // n-ary where n > 2 ( $first:expr $( , $rest:expr )* $(,)* ) => { $crate::izip!($first) $( .zip($rest) )* .map( $crate::izip!(@closure a => (a) $( , $rest )*) ) }; } #[macro_export] /// [Chain][`chain`] zero or more iterators together into one sequence. /// /// The comma-separated arguments must implement [`IntoIterator`]. /// The final argument may be followed by a trailing comma. /// /// [`chain`]: Iterator::chain /// /// # Examples /// /// Empty invocations of `chain!` expand to an invocation of [`std::iter::empty`]: /// ``` /// use std::iter; /// use itertools::chain; /// /// let _: iter::Empty<()> = chain!(); /// let _: iter::Empty = chain!(); /// ``` /// /// Invocations of `chain!` with one argument expand to [`arg.into_iter()`](IntoIterator): /// ``` /// use std::{ops::Range, slice}; /// use itertools::chain; /// let _: as IntoIterator>::IntoIter = chain!((2..6),); // trailing comma optional! /// let _: <&[_] as IntoIterator>::IntoIter = chain!(&[2, 3, 4]); /// ``` /// /// Invocations of `chain!` with multiple arguments [`.into_iter()`](IntoIterator) each /// argument, and then [`chain`] them together: /// ``` /// use std::{iter::*, ops::Range, slice}; /// use itertools::{assert_equal, chain}; /// /// // e.g., this: /// let with_macro: Chain, Take>>, slice::Iter<_>> = /// chain![once(&0), repeat(&1).take(2), &[2, 3, 5],]; /// /// // ...is equivalent to this: /// let with_method: Chain, Take>>, slice::Iter<_>> = /// once(&0) /// .chain(repeat(&1).take(2)) /// .chain(&[2, 3, 5]); /// /// assert_equal(with_macro, with_method); /// ``` macro_rules! chain { () => { core::iter::empty() }; ($first:expr $(, $rest:expr )* $(,)?) => { { let iter = core::iter::IntoIterator::into_iter($first); $( let iter = core::iter::Iterator::chain( iter, core::iter::IntoIterator::into_iter($rest)); )* iter } }; } /// An [`Iterator`] blanket implementation that provides extra adaptors and /// methods. /// /// This trait defines a number of methods. They are divided into two groups: /// /// * *Adaptors* take an iterator and parameter as input, and return /// a new iterator value. These are listed first in the trait. An example /// of an adaptor is [`.interleave()`](Itertools::interleave) /// /// * *Regular methods* are those that don't return iterators and instead /// return a regular value of some other kind. /// [`.next_tuple()`](Itertools::next_tuple) is an example and the first regular /// method in the list. pub trait Itertools: Iterator { // adaptors /// Alternate elements from two iterators until both have run out. /// /// Iterator element type is `Self::Item`. /// /// This iterator is *fused*. /// /// ``` /// use itertools::Itertools; /// /// let it = (1..7).interleave(vec![-1, -2]); /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3, 4, 5, 6]); /// ``` fn interleave(self, other: J) -> Interleave where J: IntoIterator, Self: Sized, { interleave(self, other) } /// Alternate elements from two iterators until at least one of them has run /// out. /// /// Iterator element type is `Self::Item`. /// /// ``` /// use itertools::Itertools; /// /// let it = (1..7).interleave_shortest(vec![-1, -2]); /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3]); /// ``` fn interleave_shortest(self, other: J) -> InterleaveShortest where J: IntoIterator, Self: Sized, { adaptors::interleave_shortest(self, other.into_iter()) } /// An iterator adaptor to insert a particular value /// between each element of the adapted iterator. /// /// Iterator element type is `Self::Item`. /// /// This iterator is *fused*. /// /// ``` /// use itertools::Itertools; /// /// itertools::assert_equal((0..3).intersperse(8), vec![0, 8, 1, 8, 2]); /// ``` fn intersperse(self, element: Self::Item) -> Intersperse where Self: Sized, Self::Item: Clone, { intersperse::intersperse(self, element) } /// An iterator adaptor to insert a particular value created by a function /// between each element of the adapted iterator. /// /// Iterator element type is `Self::Item`. /// /// This iterator is *fused*. /// /// ``` /// use itertools::Itertools; /// /// let mut i = 10; /// itertools::assert_equal((0..3).intersperse_with(|| { i -= 1; i }), vec![0, 9, 1, 8, 2]); /// assert_eq!(i, 8); /// ``` fn intersperse_with(self, element: F) -> IntersperseWith where Self: Sized, F: FnMut() -> Self::Item, { intersperse::intersperse_with(self, element) } /// Returns an iterator over a subsection of the iterator. /// /// Works similarly to [`slice::get`](https://doc.rust-lang.org/std/primitive.slice.html#method.get). /// /// **Panics** for ranges `..=usize::MAX` and `0..=usize::MAX`. /// /// It's a generalisation of [`Iterator::take`] and [`Iterator::skip`], /// and uses these under the hood. /// Therefore, the resulting iterator is: /// - [`ExactSizeIterator`] if the adapted iterator is [`ExactSizeIterator`]. /// - [`DoubleEndedIterator`] if the adapted iterator is [`DoubleEndedIterator`] and [`ExactSizeIterator`]. /// /// # Unspecified Behavior /// The result of indexing with an exhausted [`core::ops::RangeInclusive`] is unspecified. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let vec = vec![3, 1, 4, 1, 5]; /// /// let mut range: Vec<_> = /// vec.iter().get(1..=3).copied().collect(); /// assert_eq!(&range, &[1, 4, 1]); /// /// // It works with other types of ranges, too /// range = vec.iter().get(..2).copied().collect(); /// assert_eq!(&range, &[3, 1]); /// /// range = vec.iter().get(0..1).copied().collect(); /// assert_eq!(&range, &[3]); /// /// range = vec.iter().get(2..).copied().collect(); /// assert_eq!(&range, &[4, 1, 5]); /// /// range = vec.iter().get(..=2).copied().collect(); /// assert_eq!(&range, &[3, 1, 4]); /// /// range = vec.iter().get(..).copied().collect(); /// assert_eq!(range, vec); /// ``` fn get(self, index: R) -> R::Output where Self: Sized, R: traits::IteratorIndex, { iter_index::get(self, index) } /// Create an iterator which iterates over both this and the specified /// iterator simultaneously, yielding pairs of two optional elements. /// /// This iterator is *fused*. /// /// As long as neither input iterator is exhausted yet, it yields two values /// via `EitherOrBoth::Both`. /// /// When the parameter iterator is exhausted, it only yields a value from the /// `self` iterator via `EitherOrBoth::Left`. /// /// When the `self` iterator is exhausted, it only yields a value from the /// parameter iterator via `EitherOrBoth::Right`. /// /// When both iterators return `None`, all further invocations of `.next()` /// will return `None`. /// /// Iterator element type is /// [`EitherOrBoth`](EitherOrBoth). /// /// ```rust /// use itertools::EitherOrBoth::{Both, Right}; /// use itertools::Itertools; /// let it = (0..1).zip_longest(1..3); /// itertools::assert_equal(it, vec![Both(0, 1), Right(2)]); /// ``` #[inline] fn zip_longest(self, other: J) -> ZipLongest where J: IntoIterator, Self: Sized, { zip_longest::zip_longest(self, other.into_iter()) } /// Create an iterator which iterates over both this and the specified /// iterator simultaneously, yielding pairs of elements. /// /// **Panics** if the iterators reach an end and they are not of equal /// lengths. #[inline] fn zip_eq(self, other: J) -> ZipEq where J: IntoIterator, Self: Sized, { zip_eq(self, other) } /// A “meta iterator adaptor”. Its closure receives a reference to the /// iterator and may pick off as many elements as it likes, to produce the /// next iterator element. /// /// Iterator element type is `B`. /// /// ``` /// use itertools::Itertools; /// /// // An adaptor that gathers elements in pairs /// let pit = (0..4).batching(|it| { /// match it.next() { /// None => None, /// Some(x) => match it.next() { /// None => None, /// Some(y) => Some((x, y)), /// } /// } /// }); /// /// itertools::assert_equal(pit, vec![(0, 1), (2, 3)]); /// ``` /// fn batching(self, f: F) -> Batching where F: FnMut(&mut Self) -> Option, Self: Sized, { adaptors::batching(self, f) } /// Return an *iterable* that can group iterator elements. /// Consecutive elements that map to the same key (“runs”), are assigned /// to the same group. /// /// `ChunkBy` is the storage for the lazy grouping operation. /// /// If the groups are consumed in order, or if each group's iterator is /// dropped without keeping it around, then `ChunkBy` uses no /// allocations. It needs allocations only if several group iterators /// are alive at the same time. /// /// This type implements [`IntoIterator`] (it is **not** an iterator /// itself), because the group iterators need to borrow from this /// value. It should be stored in a local variable or temporary and /// iterated. /// /// Iterator element type is `(K, Group)`: the group's key and the /// group iterator. /// /// ``` /// use itertools::Itertools; /// /// // chunk data into runs of larger than zero or not. /// let data = vec![1, 3, -2, -2, 1, 0, 1, 2]; /// // chunks: |---->|------>|--------->| /// /// // Note: The `&` is significant here, `ChunkBy` is iterable /// // only by reference. You can also call `.into_iter()` explicitly. /// let mut data_grouped = Vec::new(); /// for (key, chunk) in &data.into_iter().chunk_by(|elt| *elt >= 0) { /// data_grouped.push((key, chunk.collect())); /// } /// assert_eq!(data_grouped, vec![(true, vec![1, 3]), (false, vec![-2, -2]), (true, vec![1, 0, 1, 2])]); /// ``` #[cfg(feature = "use_alloc")] fn chunk_by(self, key: F) -> ChunkBy where Self: Sized, F: FnMut(&Self::Item) -> K, K: PartialEq, { groupbylazy::new(self, key) } /// See [`.chunk_by()`](Itertools::chunk_by). #[deprecated(note = "Use .chunk_by() instead", since = "0.13.0")] #[cfg(feature = "use_alloc")] fn group_by(self, key: F) -> ChunkBy where Self: Sized, F: FnMut(&Self::Item) -> K, K: PartialEq, { self.chunk_by(key) } /// Return an *iterable* that can chunk the iterator. /// /// Yield subiterators (chunks) that each yield a fixed number elements, /// determined by `size`. The last chunk will be shorter if there aren't /// enough elements. /// /// `IntoChunks` is based on `ChunkBy`: it is iterable (implements /// `IntoIterator`, **not** `Iterator`), and it only buffers if several /// chunk iterators are alive at the same time. /// /// Iterator element type is `Chunk`, each chunk's iterator. /// /// **Panics** if `size` is 0. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![1, 1, 2, -2, 6, 0, 3, 1]; /// //chunk size=3 |------->|-------->|--->| /// /// // Note: The `&` is significant here, `IntoChunks` is iterable /// // only by reference. You can also call `.into_iter()` explicitly. /// for chunk in &data.into_iter().chunks(3) { /// // Check that the sum of each chunk is 4. /// assert_eq!(4, chunk.sum()); /// } /// ``` #[cfg(feature = "use_alloc")] fn chunks(self, size: usize) -> IntoChunks where Self: Sized, { assert!(size != 0); groupbylazy::new_chunks(self, size) } /// Return an iterator over all contiguous windows producing tuples of /// a specific size (up to 12). /// /// `tuple_windows` clones the iterator elements so that they can be /// part of successive windows, this makes it most suited for iterators /// of references and other values that are cheap to copy. /// /// ``` /// use itertools::Itertools; /// let mut v = Vec::new(); /// /// // pairwise iteration /// for (a, b) in (1..5).tuple_windows() { /// v.push((a, b)); /// } /// assert_eq!(v, vec![(1, 2), (2, 3), (3, 4)]); /// /// let mut it = (1..5).tuple_windows(); /// assert_eq!(Some((1, 2, 3)), it.next()); /// assert_eq!(Some((2, 3, 4)), it.next()); /// assert_eq!(None, it.next()); /// /// // this requires a type hint /// let it = (1..5).tuple_windows::<(_, _, _)>(); /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4)]); /// /// // you can also specify the complete type /// use itertools::TupleWindows; /// use std::ops::Range; /// /// let it: TupleWindows, (u32, u32, u32)> = (1..5).tuple_windows(); /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4)]); /// ``` fn tuple_windows(self) -> TupleWindows where Self: Sized + Iterator, T: traits::HomogeneousTuple, T::Item: Clone, { tuple_impl::tuple_windows(self) } /// Return an iterator over all windows, wrapping back to the first /// elements when the window would otherwise exceed the length of the /// iterator, producing tuples of a specific size (up to 12). /// /// `circular_tuple_windows` clones the iterator elements so that they can be /// part of successive windows, this makes it most suited for iterators /// of references and other values that are cheap to copy. /// /// ``` /// use itertools::Itertools; /// let mut v = Vec::new(); /// for (a, b) in (1..5).circular_tuple_windows() { /// v.push((a, b)); /// } /// assert_eq!(v, vec![(1, 2), (2, 3), (3, 4), (4, 1)]); /// /// let mut it = (1..5).circular_tuple_windows(); /// assert_eq!(Some((1, 2, 3)), it.next()); /// assert_eq!(Some((2, 3, 4)), it.next()); /// assert_eq!(Some((3, 4, 1)), it.next()); /// assert_eq!(Some((4, 1, 2)), it.next()); /// assert_eq!(None, it.next()); /// /// // this requires a type hint /// let it = (1..5).circular_tuple_windows::<(_, _, _)>(); /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4), (3, 4, 1), (4, 1, 2)]); /// ``` fn circular_tuple_windows(self) -> CircularTupleWindows where Self: Sized + Clone + Iterator + ExactSizeIterator, T: tuple_impl::TupleCollect + Clone, T::Item: Clone, { tuple_impl::circular_tuple_windows(self) } /// Return an iterator that groups the items in tuples of a specific size /// (up to 12). /// /// See also the method [`.next_tuple()`](Itertools::next_tuple). /// /// ``` /// use itertools::Itertools; /// let mut v = Vec::new(); /// for (a, b) in (1..5).tuples() { /// v.push((a, b)); /// } /// assert_eq!(v, vec![(1, 2), (3, 4)]); /// /// let mut it = (1..7).tuples(); /// assert_eq!(Some((1, 2, 3)), it.next()); /// assert_eq!(Some((4, 5, 6)), it.next()); /// assert_eq!(None, it.next()); /// /// // this requires a type hint /// let it = (1..7).tuples::<(_, _, _)>(); /// itertools::assert_equal(it, vec![(1, 2, 3), (4, 5, 6)]); /// /// // you can also specify the complete type /// use itertools::Tuples; /// use std::ops::Range; /// /// let it: Tuples, (u32, u32, u32)> = (1..7).tuples(); /// itertools::assert_equal(it, vec![(1, 2, 3), (4, 5, 6)]); /// ``` /// /// See also [`Tuples::into_buffer`]. fn tuples(self) -> Tuples where Self: Sized + Iterator, T: traits::HomogeneousTuple, { tuple_impl::tuples(self) } /// Split into an iterator pair that both yield all elements from /// the original iterator. /// /// **Note:** If the iterator is clonable, prefer using that instead /// of using this method. Cloning is likely to be more efficient. /// /// Iterator element type is `Self::Item`. /// /// ``` /// use itertools::Itertools; /// let xs = vec![0, 1, 2, 3]; /// /// let (mut t1, t2) = xs.into_iter().tee(); /// itertools::assert_equal(t1.next(), Some(0)); /// itertools::assert_equal(t2, 0..4); /// itertools::assert_equal(t1, 1..4); /// ``` #[cfg(feature = "use_alloc")] fn tee(self) -> (Tee, Tee) where Self: Sized, Self::Item: Clone, { tee::new(self) } /// Convert each item of the iterator using the [`Into`] trait. /// /// ```rust /// use itertools::Itertools; /// /// (1i32..42i32).map_into::().collect_vec(); /// ``` fn map_into(self) -> MapInto where Self: Sized, Self::Item: Into, { adaptors::map_into(self) } /// Return an iterator adaptor that applies the provided closure /// to every `Result::Ok` value. `Result::Err` values are /// unchanged. /// /// ``` /// use itertools::Itertools; /// /// let input = vec![Ok(41), Err(false), Ok(11)]; /// let it = input.into_iter().map_ok(|i| i + 1); /// itertools::assert_equal(it, vec![Ok(42), Err(false), Ok(12)]); /// ``` fn map_ok(self, f: F) -> MapOk where Self: Iterator> + Sized, F: FnMut(T) -> U, { adaptors::map_ok(self, f) } /// Return an iterator adaptor that filters every `Result::Ok` /// value with the provided closure. `Result::Err` values are /// unchanged. /// /// ``` /// use itertools::Itertools; /// /// let input = vec![Ok(22), Err(false), Ok(11)]; /// let it = input.into_iter().filter_ok(|&i| i > 20); /// itertools::assert_equal(it, vec![Ok(22), Err(false)]); /// ``` fn filter_ok(self, f: F) -> FilterOk where Self: Iterator> + Sized, F: FnMut(&T) -> bool, { adaptors::filter_ok(self, f) } /// Return an iterator adaptor that filters and transforms every /// `Result::Ok` value with the provided closure. `Result::Err` /// values are unchanged. /// /// ``` /// use itertools::Itertools; /// /// let input = vec![Ok(22), Err(false), Ok(11)]; /// let it = input.into_iter().filter_map_ok(|i| if i > 20 { Some(i * 2) } else { None }); /// itertools::assert_equal(it, vec![Ok(44), Err(false)]); /// ``` fn filter_map_ok(self, f: F) -> FilterMapOk where Self: Iterator> + Sized, F: FnMut(T) -> Option, { adaptors::filter_map_ok(self, f) } /// Return an iterator adaptor that flattens every `Result::Ok` value into /// a series of `Result::Ok` values. `Result::Err` values are unchanged. /// /// This is useful when you have some common error type for your crate and /// need to propagate it upwards, but the `Result::Ok` case needs to be flattened. /// /// ``` /// use itertools::Itertools; /// /// let input = vec![Ok(0..2), Err(false), Ok(2..4)]; /// let it = input.iter().cloned().flatten_ok(); /// itertools::assert_equal(it.clone(), vec![Ok(0), Ok(1), Err(false), Ok(2), Ok(3)]); /// /// // This can also be used to propagate errors when collecting. /// let output_result: Result, bool> = it.collect(); /// assert_eq!(output_result, Err(false)); /// ``` fn flatten_ok(self) -> FlattenOk where Self: Iterator> + Sized, T: IntoIterator, { flatten_ok::flatten_ok(self) } /// “Lift” a function of the values of the current iterator so as to process /// an iterator of `Result` values instead. /// /// `processor` is a closure that receives an adapted version of the iterator /// as the only argument — the adapted iterator produces elements of type `T`, /// as long as the original iterator produces `Ok` values. /// /// If the original iterable produces an error at any point, the adapted /// iterator ends and it will return the error iself. /// /// Otherwise, the return value from the closure is returned wrapped /// inside `Ok`. /// /// # Example /// /// ``` /// use itertools::Itertools; /// /// type Item = Result; /// /// let first_values: Vec = vec![Ok(1), Ok(0), Ok(3)]; /// let second_values: Vec = vec![Ok(2), Ok(1), Err("overflow")]; /// /// // “Lift” the iterator .max() method to work on the Ok-values. /// let first_max = first_values.into_iter().process_results(|iter| iter.max().unwrap_or(0)); /// let second_max = second_values.into_iter().process_results(|iter| iter.max().unwrap_or(0)); /// /// assert_eq!(first_max, Ok(3)); /// assert!(second_max.is_err()); /// ``` fn process_results(self, processor: F) -> Result where Self: Iterator> + Sized, F: FnOnce(ProcessResults) -> R, { process_results(self, processor) } /// Return an iterator adaptor that merges the two base iterators in /// ascending order. If both base iterators are sorted (ascending), the /// result is sorted. /// /// Iterator element type is `Self::Item`. /// /// ``` /// use itertools::Itertools; /// /// let a = (0..11).step_by(3); /// let b = (0..11).step_by(5); /// let it = a.merge(b); /// itertools::assert_equal(it, vec![0, 0, 3, 5, 6, 9, 10]); /// ``` fn merge(self, other: J) -> Merge where Self: Sized, Self::Item: PartialOrd, J: IntoIterator, { merge(self, other) } /// Return an iterator adaptor that merges the two base iterators in order. /// This is much like [`.merge()`](Itertools::merge) but allows for a custom ordering. /// /// This can be especially useful for sequences of tuples. /// /// Iterator element type is `Self::Item`. /// /// ``` /// use itertools::Itertools; /// /// let a = (0..).zip("bc".chars()); /// let b = (0..).zip("ad".chars()); /// let it = a.merge_by(b, |x, y| x.1 <= y.1); /// itertools::assert_equal(it, vec![(0, 'a'), (0, 'b'), (1, 'c'), (1, 'd')]); /// ``` fn merge_by(self, other: J, is_first: F) -> MergeBy where Self: Sized, J: IntoIterator, F: FnMut(&Self::Item, &Self::Item) -> bool, { merge_join::merge_by_new(self, other, is_first) } /// Create an iterator that merges items from both this and the specified /// iterator in ascending order. /// /// The function can either return an `Ordering` variant or a boolean. /// /// If `cmp_fn` returns `Ordering`, /// it chooses whether to pair elements based on the `Ordering` returned by the /// specified compare function. At any point, inspecting the tip of the /// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type /// `J::Item` respectively, the resulting iterator will: /// /// - Emit `EitherOrBoth::Left(i)` when `i < j`, /// and remove `i` from its source iterator /// - Emit `EitherOrBoth::Right(j)` when `i > j`, /// and remove `j` from its source iterator /// - Emit `EitherOrBoth::Both(i, j)` when `i == j`, /// and remove both `i` and `j` from their respective source iterators /// /// ``` /// use itertools::Itertools; /// use itertools::EitherOrBoth::{Left, Right, Both}; /// /// let a = vec![0, 2, 4, 6, 1].into_iter(); /// let b = (0..10).step_by(3); /// /// itertools::assert_equal( /// a.merge_join_by(b, |i, j| i.cmp(j)), /// vec![Both(0, 0), Left(2), Right(3), Left(4), Both(6, 6), Left(1), Right(9)] /// ); /// ``` /// /// If `cmp_fn` returns `bool`, /// it chooses whether to pair elements based on the boolean returned by the /// specified function. At any point, inspecting the tip of the /// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type /// `J::Item` respectively, the resulting iterator will: /// /// - Emit `Either::Left(i)` when `true`, /// and remove `i` from its source iterator /// - Emit `Either::Right(j)` when `false`, /// and remove `j` from its source iterator /// /// It is similar to the `Ordering` case if the first argument is considered /// "less" than the second argument. /// /// ``` /// use itertools::Itertools; /// use itertools::Either::{Left, Right}; /// /// let a = vec![0, 2, 4, 6, 1].into_iter(); /// let b = (0..10).step_by(3); /// /// itertools::assert_equal( /// a.merge_join_by(b, |i, j| i <= j), /// vec![Left(0), Right(0), Left(2), Right(3), Left(4), Left(6), Left(1), Right(6), Right(9)] /// ); /// ``` #[inline] fn merge_join_by(self, other: J, cmp_fn: F) -> MergeJoinBy where J: IntoIterator, F: FnMut(&Self::Item, &J::Item) -> T, Self: Sized, { merge_join_by(self, other, cmp_fn) } /// Return an iterator adaptor that flattens an iterator of iterators by /// merging them in ascending order. /// /// If all base iterators are sorted (ascending), the result is sorted. /// /// Iterator element type is `Self::Item`. /// /// ``` /// use itertools::Itertools; /// /// let a = (0..6).step_by(3); /// let b = (1..6).step_by(3); /// let c = (2..6).step_by(3); /// let it = vec![a, b, c].into_iter().kmerge(); /// itertools::assert_equal(it, vec![0, 1, 2, 3, 4, 5]); /// ``` #[cfg(feature = "use_alloc")] fn kmerge(self) -> KMerge<::IntoIter> where Self: Sized, Self::Item: IntoIterator, ::Item: PartialOrd, { kmerge(self) } /// Return an iterator adaptor that flattens an iterator of iterators by /// merging them according to the given closure. /// /// The closure `first` is called with two elements *a*, *b* and should /// return `true` if *a* is ordered before *b*. /// /// If all base iterators are sorted according to `first`, the result is /// sorted. /// /// Iterator element type is `Self::Item`. /// /// ``` /// use itertools::Itertools; /// /// let a = vec![-1f64, 2., 3., -5., 6., -7.]; /// let b = vec![0., 2., -4.]; /// let mut it = vec![a, b].into_iter().kmerge_by(|a, b| a.abs() < b.abs()); /// assert_eq!(it.next(), Some(0.)); /// assert_eq!(it.last(), Some(-7.)); /// ``` #[cfg(feature = "use_alloc")] fn kmerge_by(self, first: F) -> KMergeBy<::IntoIter, F> where Self: Sized, Self::Item: IntoIterator, F: FnMut(&::Item, &::Item) -> bool, { kmerge_by(self, first) } /// Return an iterator adaptor that iterates over the cartesian product of /// the element sets of two iterators `self` and `J`. /// /// Iterator element type is `(Self::Item, J::Item)`. /// /// ``` /// use itertools::Itertools; /// /// let it = (0..2).cartesian_product("αβ".chars()); /// itertools::assert_equal(it, vec![(0, 'α'), (0, 'β'), (1, 'α'), (1, 'β')]); /// ``` fn cartesian_product(self, other: J) -> Product where Self: Sized, Self::Item: Clone, J: IntoIterator, J::IntoIter: Clone, { adaptors::cartesian_product(self, other.into_iter()) } /// Return an iterator adaptor that iterates over the cartesian product of /// all subiterators returned by meta-iterator `self`. /// /// All provided iterators must yield the same `Item` type. To generate /// the product of iterators yielding multiple types, use the /// [`iproduct`] macro instead. /// /// The iterator element type is `Vec`, where `T` is the iterator element /// of the subiterators. /// /// Note that the iterator is fused. /// /// ``` /// use itertools::Itertools; /// let mut multi_prod = (0..3).map(|i| (i * 2)..(i * 2 + 2)) /// .multi_cartesian_product(); /// assert_eq!(multi_prod.next(), Some(vec![0, 2, 4])); /// assert_eq!(multi_prod.next(), Some(vec![0, 2, 5])); /// assert_eq!(multi_prod.next(), Some(vec![0, 3, 4])); /// assert_eq!(multi_prod.next(), Some(vec![0, 3, 5])); /// assert_eq!(multi_prod.next(), Some(vec![1, 2, 4])); /// assert_eq!(multi_prod.next(), Some(vec![1, 2, 5])); /// assert_eq!(multi_prod.next(), Some(vec![1, 3, 4])); /// assert_eq!(multi_prod.next(), Some(vec![1, 3, 5])); /// assert_eq!(multi_prod.next(), None); /// ``` /// /// If the adapted iterator is empty, the result is an iterator yielding a single empty vector. /// This is known as the [nullary cartesian product](https://en.wikipedia.org/wiki/Empty_product#Nullary_Cartesian_product). /// /// ``` /// use itertools::Itertools; /// let mut nullary_cartesian_product = (0..0).map(|i| (i * 2)..(i * 2 + 2)).multi_cartesian_product(); /// assert_eq!(nullary_cartesian_product.next(), Some(vec![])); /// assert_eq!(nullary_cartesian_product.next(), None); /// ``` #[cfg(feature = "use_alloc")] fn multi_cartesian_product(self) -> MultiProduct<::IntoIter> where Self: Sized, Self::Item: IntoIterator, ::IntoIter: Clone, ::Item: Clone, { adaptors::multi_cartesian_product(self) } /// Return an iterator adaptor that uses the passed-in closure to /// optionally merge together consecutive elements. /// /// The closure `f` is passed two elements, `previous` and `current` and may /// return either (1) `Ok(combined)` to merge the two values or /// (2) `Err((previous', current'))` to indicate they can't be merged. /// In (2), the value `previous'` is emitted by the iterator. /// Either (1) `combined` or (2) `current'` becomes the previous value /// when coalesce continues with the next pair of elements to merge. The /// value that remains at the end is also emitted by the iterator. /// /// Iterator element type is `Self::Item`. /// /// This iterator is *fused*. /// /// ``` /// use itertools::Itertools; /// /// // sum same-sign runs together /// let data = vec![-1., -2., -3., 3., 1., 0., -1.]; /// itertools::assert_equal(data.into_iter().coalesce(|x, y| /// if (x >= 0.) == (y >= 0.) { /// Ok(x + y) /// } else { /// Err((x, y)) /// }), /// vec![-6., 4., -1.]); /// ``` fn coalesce(self, f: F) -> Coalesce where Self: Sized, F: FnMut(Self::Item, Self::Item) -> Result, { adaptors::coalesce(self, f) } /// Remove duplicates from sections of consecutive identical elements. /// If the iterator is sorted, all elements will be unique. /// /// Iterator element type is `Self::Item`. /// /// This iterator is *fused*. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![1., 1., 2., 3., 3., 2., 2.]; /// itertools::assert_equal(data.into_iter().dedup(), /// vec![1., 2., 3., 2.]); /// ``` fn dedup(self) -> Dedup where Self: Sized, Self::Item: PartialEq, { adaptors::dedup(self) } /// Remove duplicates from sections of consecutive identical elements, /// determining equality using a comparison function. /// If the iterator is sorted, all elements will be unique. /// /// Iterator element type is `Self::Item`. /// /// This iterator is *fused*. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![(0, 1.), (1, 1.), (0, 2.), (0, 3.), (1, 3.), (1, 2.), (2, 2.)]; /// itertools::assert_equal(data.into_iter().dedup_by(|x, y| x.1 == y.1), /// vec![(0, 1.), (0, 2.), (0, 3.), (1, 2.)]); /// ``` fn dedup_by(self, cmp: Cmp) -> DedupBy where Self: Sized, Cmp: FnMut(&Self::Item, &Self::Item) -> bool, { adaptors::dedup_by(self, cmp) } /// Remove duplicates from sections of consecutive identical elements, while keeping a count of /// how many repeated elements were present. /// If the iterator is sorted, all elements will be unique. /// /// Iterator element type is `(usize, Self::Item)`. /// /// This iterator is *fused*. /// /// ``` /// use itertools::Itertools; /// /// let data = vec!['a', 'a', 'b', 'c', 'c', 'b', 'b']; /// itertools::assert_equal(data.into_iter().dedup_with_count(), /// vec![(2, 'a'), (1, 'b'), (2, 'c'), (2, 'b')]); /// ``` fn dedup_with_count(self) -> DedupWithCount where Self: Sized, { adaptors::dedup_with_count(self) } /// Remove duplicates from sections of consecutive identical elements, while keeping a count of /// how many repeated elements were present. /// This will determine equality using a comparison function. /// If the iterator is sorted, all elements will be unique. /// /// Iterator element type is `(usize, Self::Item)`. /// /// This iterator is *fused*. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![(0, 'a'), (1, 'a'), (0, 'b'), (0, 'c'), (1, 'c'), (1, 'b'), (2, 'b')]; /// itertools::assert_equal(data.into_iter().dedup_by_with_count(|x, y| x.1 == y.1), /// vec![(2, (0, 'a')), (1, (0, 'b')), (2, (0, 'c')), (2, (1, 'b'))]); /// ``` fn dedup_by_with_count(self, cmp: Cmp) -> DedupByWithCount where Self: Sized, Cmp: FnMut(&Self::Item, &Self::Item) -> bool, { adaptors::dedup_by_with_count(self, cmp) } /// Return an iterator adaptor that produces elements that appear more than once during the /// iteration. Duplicates are detected using hash and equality. /// /// The iterator is stable, returning the duplicate items in the order in which they occur in /// the adapted iterator. Each duplicate item is returned exactly once. If an item appears more /// than twice, the second item is the item retained and the rest are discarded. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![10, 20, 30, 20, 40, 10, 50]; /// itertools::assert_equal(data.into_iter().duplicates(), /// vec![20, 10]); /// ``` #[cfg(feature = "use_std")] fn duplicates(self) -> Duplicates where Self: Sized, Self::Item: Eq + Hash, { duplicates_impl::duplicates(self) } /// Return an iterator adaptor that produces elements that appear more than once during the /// iteration. Duplicates are detected using hash and equality. /// /// Duplicates are detected by comparing the key they map to with the keying function `f` by /// hash and equality. The keys are stored in a hash map in the iterator. /// /// The iterator is stable, returning the duplicate items in the order in which they occur in /// the adapted iterator. Each duplicate item is returned exactly once. If an item appears more /// than twice, the second item is the item retained and the rest are discarded. /// /// ``` /// use itertools::Itertools; /// /// let data = vec!["a", "bb", "aa", "c", "ccc"]; /// itertools::assert_equal(data.into_iter().duplicates_by(|s| s.len()), /// vec!["aa", "c"]); /// ``` #[cfg(feature = "use_std")] fn duplicates_by(self, f: F) -> DuplicatesBy where Self: Sized, V: Eq + Hash, F: FnMut(&Self::Item) -> V, { duplicates_impl::duplicates_by(self, f) } /// Return an iterator adaptor that filters out elements that have /// already been produced once during the iteration. Duplicates /// are detected using hash and equality. /// /// Clones of visited elements are stored in a hash set in the /// iterator. /// /// The iterator is stable, returning the non-duplicate items in the order /// in which they occur in the adapted iterator. In a set of duplicate /// items, the first item encountered is the item retained. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![10, 20, 30, 20, 40, 10, 50]; /// itertools::assert_equal(data.into_iter().unique(), /// vec![10, 20, 30, 40, 50]); /// ``` #[cfg(feature = "use_std")] fn unique(self) -> Unique where Self: Sized, Self::Item: Clone + Eq + Hash, { unique_impl::unique(self) } /// Return an iterator adaptor that filters out elements that have /// already been produced once during the iteration. /// /// Duplicates are detected by comparing the key they map to /// with the keying function `f` by hash and equality. /// The keys are stored in a hash set in the iterator. /// /// The iterator is stable, returning the non-duplicate items in the order /// in which they occur in the adapted iterator. In a set of duplicate /// items, the first item encountered is the item retained. /// /// ``` /// use itertools::Itertools; /// /// let data = vec!["a", "bb", "aa", "c", "ccc"]; /// itertools::assert_equal(data.into_iter().unique_by(|s| s.len()), /// vec!["a", "bb", "ccc"]); /// ``` #[cfg(feature = "use_std")] fn unique_by(self, f: F) -> UniqueBy where Self: Sized, V: Eq + Hash, F: FnMut(&Self::Item) -> V, { unique_impl::unique_by(self, f) } /// Return an iterator adaptor that borrows from this iterator and /// takes items while the closure `accept` returns `true`. /// /// This adaptor can only be used on iterators that implement `PeekingNext` /// like `.peekable()`, `put_back` and a few other collection iterators. /// /// The last and rejected element (first `false`) is still available when /// `peeking_take_while` is done. /// /// /// See also [`.take_while_ref()`](Itertools::take_while_ref) /// which is a similar adaptor. fn peeking_take_while(&mut self, accept: F) -> PeekingTakeWhile where Self: Sized + PeekingNext, F: FnMut(&Self::Item) -> bool, { peeking_take_while::peeking_take_while(self, accept) } /// Return an iterator adaptor that borrows from a `Clone`-able iterator /// to only pick off elements while the predicate `accept` returns `true`. /// /// It uses the `Clone` trait to restore the original iterator so that the /// last and rejected element (first `false`) is still available when /// `take_while_ref` is done. /// /// ``` /// use itertools::Itertools; /// /// let mut hexadecimals = "0123456789abcdef".chars(); /// /// let decimals = hexadecimals.take_while_ref(|c| c.is_numeric()) /// .collect::(); /// assert_eq!(decimals, "0123456789"); /// assert_eq!(hexadecimals.next(), Some('a')); /// /// ``` fn take_while_ref(&mut self, accept: F) -> TakeWhileRef where Self: Clone, F: FnMut(&Self::Item) -> bool, { adaptors::take_while_ref(self, accept) } /// Returns an iterator adaptor that consumes elements while the given /// predicate is `true`, *including* the element for which the predicate /// first returned `false`. /// /// The [`.take_while()`][std::iter::Iterator::take_while] adaptor is useful /// when you want items satisfying a predicate, but to know when to stop /// taking elements, we have to consume that first element that doesn't /// satisfy the predicate. This adaptor includes that element where /// [`.take_while()`][std::iter::Iterator::take_while] would drop it. /// /// The [`.take_while_ref()`][crate::Itertools::take_while_ref] adaptor /// serves a similar purpose, but this adaptor doesn't require [`Clone`]ing /// the underlying elements. /// /// ```rust /// # use itertools::Itertools; /// let items = vec![1, 2, 3, 4, 5]; /// let filtered: Vec<_> = items /// .into_iter() /// .take_while_inclusive(|&n| n % 3 != 0) /// .collect(); /// /// assert_eq!(filtered, vec![1, 2, 3]); /// ``` /// /// ```rust /// # use itertools::Itertools; /// let items = vec![1, 2, 3, 4, 5]; /// /// let take_while_inclusive_result: Vec<_> = items /// .iter() /// .copied() /// .take_while_inclusive(|&n| n % 3 != 0) /// .collect(); /// let take_while_result: Vec<_> = items /// .into_iter() /// .take_while(|&n| n % 3 != 0) /// .collect(); /// /// assert_eq!(take_while_inclusive_result, vec![1, 2, 3]); /// assert_eq!(take_while_result, vec![1, 2]); /// // both iterators have the same items remaining at this point---the 3 /// // is lost from the `take_while` vec /// ``` /// /// ```rust /// # use itertools::Itertools; /// #[derive(Debug, PartialEq)] /// struct NoCloneImpl(i32); /// /// let non_clonable_items: Vec<_> = vec![1, 2, 3, 4, 5] /// .into_iter() /// .map(NoCloneImpl) /// .collect(); /// let filtered: Vec<_> = non_clonable_items /// .into_iter() /// .take_while_inclusive(|n| n.0 % 3 != 0) /// .collect(); /// let expected: Vec<_> = vec![1, 2, 3].into_iter().map(NoCloneImpl).collect(); /// assert_eq!(filtered, expected); fn take_while_inclusive(self, accept: F) -> TakeWhileInclusive where Self: Sized, F: FnMut(&Self::Item) -> bool, { take_while_inclusive::TakeWhileInclusive::new(self, accept) } /// Return an iterator adaptor that filters `Option` iterator elements /// and produces `A`. Stops on the first `None` encountered. /// /// Iterator element type is `A`, the unwrapped element. /// /// ``` /// use itertools::Itertools; /// /// // List all hexadecimal digits /// itertools::assert_equal( /// (0..).map(|i| std::char::from_digit(i, 16)).while_some(), /// "0123456789abcdef".chars()); /// /// ``` fn while_some(self) -> WhileSome where Self: Sized + Iterator>, { adaptors::while_some(self) } /// Return an iterator adaptor that iterates over the combinations of the /// elements from an iterator. /// /// Iterator element can be any homogeneous tuple of type `Self::Item` with /// size up to 12. /// /// # Guarantees /// /// If the adapted iterator is deterministic, /// this iterator adapter yields items in a reliable order. /// /// ``` /// use itertools::Itertools; /// /// let mut v = Vec::new(); /// for (a, b) in (1..5).tuple_combinations() { /// v.push((a, b)); /// } /// assert_eq!(v, vec![(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]); /// /// let mut it = (1..5).tuple_combinations(); /// assert_eq!(Some((1, 2, 3)), it.next()); /// assert_eq!(Some((1, 2, 4)), it.next()); /// assert_eq!(Some((1, 3, 4)), it.next()); /// assert_eq!(Some((2, 3, 4)), it.next()); /// assert_eq!(None, it.next()); /// /// // this requires a type hint /// let it = (1..5).tuple_combinations::<(_, _, _)>(); /// itertools::assert_equal(it, vec![(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]); /// /// // you can also specify the complete type /// use itertools::TupleCombinations; /// use std::ops::Range; /// /// let it: TupleCombinations, (u32, u32, u32)> = (1..5).tuple_combinations(); /// itertools::assert_equal(it, vec![(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]); /// ``` fn tuple_combinations(self) -> TupleCombinations where Self: Sized + Clone, Self::Item: Clone, T: adaptors::HasCombination, { adaptors::tuple_combinations(self) } /// Return an iterator adaptor that iterates over the `k`-length combinations of /// the elements from an iterator. /// /// Iterator element type is `Vec`. The iterator produces a new `Vec` per iteration, /// and clones the iterator elements. /// /// # Guarantees /// /// If the adapted iterator is deterministic, /// this iterator adapter yields items in a reliable order. /// /// ``` /// use itertools::Itertools; /// /// let it = (1..5).combinations(3); /// itertools::assert_equal(it, vec![ /// vec![1, 2, 3], /// vec![1, 2, 4], /// vec![1, 3, 4], /// vec![2, 3, 4], /// ]); /// ``` /// /// Note: Combinations does not take into account the equality of the iterated values. /// ``` /// use itertools::Itertools; /// /// let it = vec![1, 2, 2].into_iter().combinations(2); /// itertools::assert_equal(it, vec![ /// vec![1, 2], // Note: these are the same /// vec![1, 2], // Note: these are the same /// vec![2, 2], /// ]); /// ``` #[cfg(feature = "use_alloc")] fn combinations(self, k: usize) -> Combinations where Self: Sized, Self::Item: Clone, { combinations::combinations(self, k) } /// Return an iterator that iterates over the `k`-length combinations of /// the elements from an iterator, with replacement. /// /// Iterator element type is `Vec`. The iterator produces a new `Vec` per iteration, /// and clones the iterator elements. /// /// ``` /// use itertools::Itertools; /// /// let it = (1..4).combinations_with_replacement(2); /// itertools::assert_equal(it, vec![ /// vec![1, 1], /// vec![1, 2], /// vec![1, 3], /// vec![2, 2], /// vec![2, 3], /// vec![3, 3], /// ]); /// ``` #[cfg(feature = "use_alloc")] fn combinations_with_replacement(self, k: usize) -> CombinationsWithReplacement where Self: Sized, Self::Item: Clone, { combinations_with_replacement::combinations_with_replacement(self, k) } /// Return an iterator adaptor that iterates over all k-permutations of the /// elements from an iterator. /// /// Iterator element type is `Vec` with length `k`. The iterator /// produces a new `Vec` per iteration, and clones the iterator elements. /// /// If `k` is greater than the length of the input iterator, the resultant /// iterator adaptor will be empty. /// /// If you are looking for permutations with replacements, /// use `repeat_n(iter, k).multi_cartesian_product()` instead. /// /// ``` /// use itertools::Itertools; /// /// let perms = (5..8).permutations(2); /// itertools::assert_equal(perms, vec![ /// vec![5, 6], /// vec![5, 7], /// vec![6, 5], /// vec![6, 7], /// vec![7, 5], /// vec![7, 6], /// ]); /// ``` /// /// Note: Permutations does not take into account the equality of the iterated values. /// /// ``` /// use itertools::Itertools; /// /// let it = vec![2, 2].into_iter().permutations(2); /// itertools::assert_equal(it, vec![ /// vec![2, 2], // Note: these are the same /// vec![2, 2], // Note: these are the same /// ]); /// ``` /// /// Note: The source iterator is collected lazily, and will not be /// re-iterated if the permutations adaptor is completed and re-iterated. #[cfg(feature = "use_alloc")] fn permutations(self, k: usize) -> Permutations where Self: Sized, Self::Item: Clone, { permutations::permutations(self, k) } /// Return an iterator that iterates through the powerset of the elements from an /// iterator. /// /// Iterator element type is `Vec`. The iterator produces a new `Vec` /// per iteration, and clones the iterator elements. /// /// The powerset of a set contains all subsets including the empty set and the full /// input set. A powerset has length _2^n_ where _n_ is the length of the input /// set. /// /// Each `Vec` produced by this iterator represents a subset of the elements /// produced by the source iterator. /// /// ``` /// use itertools::Itertools; /// /// let sets = (1..4).powerset().collect::>(); /// itertools::assert_equal(sets, vec![ /// vec![], /// vec![1], /// vec![2], /// vec![3], /// vec![1, 2], /// vec![1, 3], /// vec![2, 3], /// vec![1, 2, 3], /// ]); /// ``` #[cfg(feature = "use_alloc")] fn powerset(self) -> Powerset where Self: Sized, Self::Item: Clone, { powerset::powerset(self) } /// Return an iterator adaptor that pads the sequence to a minimum length of /// `min` by filling missing elements using a closure `f`. /// /// Iterator element type is `Self::Item`. /// /// ``` /// use itertools::Itertools; /// /// let it = (0..5).pad_using(10, |i| 2*i); /// itertools::assert_equal(it, vec![0, 1, 2, 3, 4, 10, 12, 14, 16, 18]); /// /// let it = (0..10).pad_using(5, |i| 2*i); /// itertools::assert_equal(it, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); /// /// let it = (0..5).pad_using(10, |i| 2*i).rev(); /// itertools::assert_equal(it, vec![18, 16, 14, 12, 10, 4, 3, 2, 1, 0]); /// ``` fn pad_using(self, min: usize, f: F) -> PadUsing where Self: Sized, F: FnMut(usize) -> Self::Item, { pad_tail::pad_using(self, min, f) } /// Return an iterator adaptor that combines each element with a `Position` to /// ease special-case handling of the first or last elements. /// /// Iterator element type is /// [`(Position, Self::Item)`](Position) /// /// ``` /// use itertools::{Itertools, Position}; /// /// let it = (0..4).with_position(); /// itertools::assert_equal(it, /// vec![(Position::First, 0), /// (Position::Middle, 1), /// (Position::Middle, 2), /// (Position::Last, 3)]); /// /// let it = (0..1).with_position(); /// itertools::assert_equal(it, vec![(Position::Only, 0)]); /// ``` fn with_position(self) -> WithPosition where Self: Sized, { with_position::with_position(self) } /// Return an iterator adaptor that yields the indices of all elements /// satisfying a predicate, counted from the start of the iterator. /// /// Equivalent to `iter.enumerate().filter(|(_, v)| predicate(*v)).map(|(i, _)| i)`. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![1, 2, 3, 3, 4, 6, 7, 9]; /// itertools::assert_equal(data.iter().positions(|v| v % 2 == 0), vec![1, 4, 5]); /// /// itertools::assert_equal(data.iter().positions(|v| v % 2 == 1).rev(), vec![7, 6, 3, 2, 0]); /// ``` fn positions

(self, predicate: P) -> Positions where Self: Sized, P: FnMut(Self::Item) -> bool, { adaptors::positions(self, predicate) } /// Return an iterator adaptor that applies a mutating function /// to each element before yielding it. /// /// ``` /// use itertools::Itertools; /// /// let input = vec![vec![1], vec![3, 2, 1]]; /// let it = input.into_iter().update(|mut v| v.push(0)); /// itertools::assert_equal(it, vec![vec![1, 0], vec![3, 2, 1, 0]]); /// ``` fn update(self, updater: F) -> Update where Self: Sized, F: FnMut(&mut Self::Item), { adaptors::update(self, updater) } // non-adaptor methods /// Advances the iterator and returns the next items grouped in a tuple of /// a specific size (up to 12). /// /// If there are enough elements to be grouped in a tuple, then the tuple is /// returned inside `Some`, otherwise `None` is returned. /// /// ``` /// use itertools::Itertools; /// /// let mut iter = 1..5; /// /// assert_eq!(Some((1, 2)), iter.next_tuple()); /// ``` fn next_tuple(&mut self) -> Option where Self: Sized + Iterator, T: traits::HomogeneousTuple, { T::collect_from_iter_no_buf(self) } /// Collects all items from the iterator into a tuple of a specific size /// (up to 12). /// /// If the number of elements inside the iterator is **exactly** equal to /// the tuple size, then the tuple is returned inside `Some`, otherwise /// `None` is returned. /// /// ``` /// use itertools::Itertools; /// /// let iter = 1..3; /// /// if let Some((x, y)) = iter.collect_tuple() { /// assert_eq!((x, y), (1, 2)) /// } else { /// panic!("Expected two elements") /// } /// ``` fn collect_tuple(mut self) -> Option where Self: Sized + Iterator, T: traits::HomogeneousTuple, { match self.next_tuple() { elt @ Some(_) => match self.next() { Some(_) => None, None => elt, }, _ => None, } } /// Find the position and value of the first element satisfying a predicate. /// /// The iterator is not advanced past the first element found. /// /// ``` /// use itertools::Itertools; /// /// let text = "Hα"; /// assert_eq!(text.chars().find_position(|ch| ch.is_lowercase()), Some((1, 'α'))); /// ``` fn find_position

(&mut self, mut pred: P) -> Option<(usize, Self::Item)> where P: FnMut(&Self::Item) -> bool, { self.enumerate().find(|(_, elt)| pred(elt)) } /// Find the value of the first element satisfying a predicate or return the last element, if any. /// /// The iterator is not advanced past the first element found. /// /// ``` /// use itertools::Itertools; /// /// let numbers = [1, 2, 3, 4]; /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 5), Some(&4)); /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 2), Some(&3)); /// assert_eq!(std::iter::empty::().find_or_last(|&x| x > 5), None); /// ``` fn find_or_last

(mut self, mut predicate: P) -> Option where Self: Sized, P: FnMut(&Self::Item) -> bool, { let mut prev = None; self.find_map(|x| { if predicate(&x) { Some(x) } else { prev = Some(x); None } }) .or(prev) } /// Find the value of the first element satisfying a predicate or return the first element, if any. /// /// The iterator is not advanced past the first element found. /// /// ``` /// use itertools::Itertools; /// /// let numbers = [1, 2, 3, 4]; /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 5), Some(&1)); /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 2), Some(&3)); /// assert_eq!(std::iter::empty::().find_or_first(|&x| x > 5), None); /// ``` fn find_or_first

(mut self, mut predicate: P) -> Option where Self: Sized, P: FnMut(&Self::Item) -> bool, { let first = self.next()?; Some(if predicate(&first) { first } else { self.find(|x| predicate(x)).unwrap_or(first) }) } /// Returns `true` if the given item is present in this iterator. /// /// This method is short-circuiting. If the given item is present in this /// iterator, this method will consume the iterator up-to-and-including /// the item. If the given item is not present in this iterator, the /// iterator will be exhausted. /// /// ``` /// use itertools::Itertools; /// /// #[derive(PartialEq, Debug)] /// enum Enum { A, B, C, D, E, } /// /// let mut iter = vec![Enum::A, Enum::B, Enum::C, Enum::D].into_iter(); /// /// // search `iter` for `B` /// assert_eq!(iter.contains(&Enum::B), true); /// // `B` was found, so the iterator now rests at the item after `B` (i.e, `C`). /// assert_eq!(iter.next(), Some(Enum::C)); /// /// // search `iter` for `E` /// assert_eq!(iter.contains(&Enum::E), false); /// // `E` wasn't found, so `iter` is now exhausted /// assert_eq!(iter.next(), None); /// ``` fn contains(&mut self, query: &Q) -> bool where Self: Sized, Self::Item: Borrow, Q: PartialEq, { self.any(|x| x.borrow() == query) } /// Check whether all elements compare equal. /// /// Empty iterators are considered to have equal elements: /// /// ``` /// use itertools::Itertools; /// /// let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 4, 5, 5]; /// assert!(!data.iter().all_equal()); /// assert!(data[0..3].iter().all_equal()); /// assert!(data[3..5].iter().all_equal()); /// assert!(data[5..8].iter().all_equal()); /// /// let data : Option = None; /// assert!(data.into_iter().all_equal()); /// ``` fn all_equal(&mut self) -> bool where Self: Sized, Self::Item: PartialEq, { match self.next() { None => true, Some(a) => self.all(|x| a == x), } } /// If there are elements and they are all equal, return a single copy of that element. /// If there are no elements, return an Error containing None. /// If there are elements and they are not all equal, return a tuple containing the first /// two non-equal elements found. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 4, 5, 5]; /// assert_eq!(data.iter().all_equal_value(), Err(Some((&1, &2)))); /// assert_eq!(data[0..3].iter().all_equal_value(), Ok(&1)); /// assert_eq!(data[3..5].iter().all_equal_value(), Ok(&2)); /// assert_eq!(data[5..8].iter().all_equal_value(), Ok(&3)); /// /// let data : Option = None; /// assert_eq!(data.into_iter().all_equal_value(), Err(None)); /// ``` #[allow(clippy::type_complexity)] fn all_equal_value(&mut self) -> Result> where Self: Sized, Self::Item: PartialEq, { let first = self.next().ok_or(None)?; let other = self.find(|x| x != &first); if let Some(other) = other { Err(Some((first, other))) } else { Ok(first) } } /// Check whether all elements are unique (non equal). /// /// Empty iterators are considered to have unique elements: /// /// ``` /// use itertools::Itertools; /// /// let data = vec![1, 2, 3, 4, 1, 5]; /// assert!(!data.iter().all_unique()); /// assert!(data[0..4].iter().all_unique()); /// assert!(data[1..6].iter().all_unique()); /// /// let data : Option = None; /// assert!(data.into_iter().all_unique()); /// ``` #[cfg(feature = "use_std")] fn all_unique(&mut self) -> bool where Self: Sized, Self::Item: Eq + Hash, { let mut used = HashSet::new(); self.all(move |elt| used.insert(elt)) } /// Consume the first `n` elements from the iterator eagerly, /// and return the same iterator again. /// /// It works similarly to `.skip(n)` except it is eager and /// preserves the iterator type. /// /// ``` /// use itertools::Itertools; /// /// let mut iter = "αβγ".chars().dropping(2); /// itertools::assert_equal(iter, "γ".chars()); /// ``` /// /// *Fusing notes: if the iterator is exhausted by dropping, /// the result of calling `.next()` again depends on the iterator implementation.* fn dropping(mut self, n: usize) -> Self where Self: Sized, { if n > 0 { self.nth(n - 1); } self } /// Consume the last `n` elements from the iterator eagerly, /// and return the same iterator again. /// /// This is only possible on double ended iterators. `n` may be /// larger than the number of elements. /// /// Note: This method is eager, dropping the back elements immediately and /// preserves the iterator type. /// /// ``` /// use itertools::Itertools; /// /// let init = vec![0, 3, 6, 9].into_iter().dropping_back(1); /// itertools::assert_equal(init, vec![0, 3, 6]); /// ``` fn dropping_back(mut self, n: usize) -> Self where Self: Sized + DoubleEndedIterator, { if n > 0 { (&mut self).rev().nth(n - 1); } self } /// Combine all an iterator's elements into one element by using [`Extend`]. /// /// This combinator will extend the first item with each of the rest of the /// items of the iterator. If the iterator is empty, the default value of /// `I::Item` is returned. /// /// ```rust /// use itertools::Itertools; /// /// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]]; /// assert_eq!(input.into_iter().concat(), /// vec![1, 2, 3, 4, 5, 6]); /// ``` fn concat(self) -> Self::Item where Self: Sized, Self::Item: Extend<<::Item as IntoIterator>::Item> + IntoIterator + Default, { concat(self) } /// `.collect_vec()` is simply a type specialization of [`Iterator::collect`], /// for convenience. #[cfg(feature = "use_alloc")] fn collect_vec(self) -> Vec where Self: Sized, { self.collect() } /// `.try_collect()` is more convenient way of writing /// `.collect::>()` /// /// # Example /// /// ``` /// use std::{fs, io}; /// use itertools::Itertools; /// /// fn process_dir_entries(entries: &[fs::DirEntry]) { /// // ... /// } /// /// fn do_stuff() -> std::io::Result<()> { /// let entries: Vec<_> = fs::read_dir(".")?.try_collect()?; /// process_dir_entries(&entries); /// /// Ok(()) /// } /// ``` fn try_collect(self) -> Result where Self: Sized + Iterator>, Result: FromIterator>, { self.collect() } /// Assign to each reference in `self` from the `from` iterator, /// stopping at the shortest of the two iterators. /// /// The `from` iterator is queried for its next element before the `self` /// iterator, and if either is exhausted the method is done. /// /// Return the number of elements written. /// /// ``` /// use itertools::Itertools; /// /// let mut xs = [0; 4]; /// xs.iter_mut().set_from(1..); /// assert_eq!(xs, [1, 2, 3, 4]); /// ``` #[inline] fn set_from<'a, A: 'a, J>(&mut self, from: J) -> usize where Self: Iterator, J: IntoIterator, { from.into_iter() .zip(self) .map(|(new, old)| *old = new) .count() } /// Combine all iterator elements into one `String`, separated by `sep`. /// /// Use the `Display` implementation of each element. /// /// ``` /// use itertools::Itertools; /// /// assert_eq!(["a", "b", "c"].iter().join(", "), "a, b, c"); /// assert_eq!([1, 2, 3].iter().join(", "), "1, 2, 3"); /// ``` #[cfg(feature = "use_alloc")] fn join(&mut self, sep: &str) -> String where Self::Item: std::fmt::Display, { match self.next() { None => String::new(), Some(first_elt) => { // estimate lower bound of capacity needed let (lower, _) = self.size_hint(); let mut result = String::with_capacity(sep.len() * lower); write!(&mut result, "{}", first_elt).unwrap(); self.for_each(|elt| { result.push_str(sep); write!(&mut result, "{}", elt).unwrap(); }); result } } } /// Format all iterator elements, separated by `sep`. /// /// All elements are formatted (any formatting trait) /// with `sep` inserted between each element. /// /// **Panics** if the formatter helper is formatted more than once. /// /// ``` /// use itertools::Itertools; /// /// let data = [1.1, 2.71828, -3.]; /// assert_eq!( /// format!("{:.2}", data.iter().format(", ")), /// "1.10, 2.72, -3.00"); /// ``` fn format(self, sep: &str) -> Format where Self: Sized, { format::new_format_default(self, sep) } /// Format all iterator elements, separated by `sep`. /// /// This is a customizable version of [`.format()`](Itertools::format). /// /// The supplied closure `format` is called once per iterator element, /// with two arguments: the element and a callback that takes a /// `&Display` value, i.e. any reference to type that implements `Display`. /// /// Using `&format_args!(...)` is the most versatile way to apply custom /// element formatting. The callback can be called multiple times if needed. /// /// **Panics** if the formatter helper is formatted more than once. /// /// ``` /// use itertools::Itertools; /// /// let data = [1.1, 2.71828, -3.]; /// let data_formatter = data.iter().format_with(", ", |elt, f| f(&format_args!("{:.2}", elt))); /// assert_eq!(format!("{}", data_formatter), /// "1.10, 2.72, -3.00"); /// /// // .format_with() is recursively composable /// let matrix = [[1., 2., 3.], /// [4., 5., 6.]]; /// let matrix_formatter = matrix.iter().format_with("\n", |row, f| { /// f(&row.iter().format_with(", ", |elt, g| g(&elt))) /// }); /// assert_eq!(format!("{}", matrix_formatter), /// "1, 2, 3\n4, 5, 6"); /// /// /// ``` fn format_with(self, sep: &str, format: F) -> FormatWith where Self: Sized, F: FnMut(Self::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, { format::new_format(self, sep, format) } /// Fold `Result` values from an iterator. /// /// Only `Ok` values are folded. If no error is encountered, the folded /// value is returned inside `Ok`. Otherwise, the operation terminates /// and returns the first `Err` value it encounters. No iterator elements are /// consumed after the first error. /// /// The first accumulator value is the `start` parameter. /// Each iteration passes the accumulator value and the next value inside `Ok` /// to the fold function `f` and its return value becomes the new accumulator value. /// /// For example the sequence *Ok(1), Ok(2), Ok(3)* will result in a /// computation like this: /// /// ```no_run /// # let start = 0; /// # let f = |x, y| x + y; /// let mut accum = start; /// accum = f(accum, 1); /// accum = f(accum, 2); /// accum = f(accum, 3); /// ``` /// /// With a `start` value of 0 and an addition as folding function, /// this effectively results in *((0 + 1) + 2) + 3* /// /// ``` /// use std::ops::Add; /// use itertools::Itertools; /// /// let values = [1, 2, -2, -1, 2, 1]; /// assert_eq!( /// values.iter() /// .map(Ok::<_, ()>) /// .fold_ok(0, Add::add), /// Ok(3) /// ); /// assert!( /// values.iter() /// .map(|&x| if x >= 0 { Ok(x) } else { Err("Negative number") }) /// .fold_ok(0, Add::add) /// .is_err() /// ); /// ``` fn fold_ok(&mut self, mut start: B, mut f: F) -> Result where Self: Iterator>, F: FnMut(B, A) -> B, { for elt in self { match elt { Ok(v) => start = f(start, v), Err(u) => return Err(u), } } Ok(start) } /// Fold `Option` values from an iterator. /// /// Only `Some` values are folded. If no `None` is encountered, the folded /// value is returned inside `Some`. Otherwise, the operation terminates /// and returns `None`. No iterator elements are consumed after the `None`. /// /// This is the `Option` equivalent to [`fold_ok`](Itertools::fold_ok). /// /// ``` /// use std::ops::Add; /// use itertools::Itertools; /// /// let mut values = vec![Some(1), Some(2), Some(-2)].into_iter(); /// assert_eq!(values.fold_options(5, Add::add), Some(5 + 1 + 2 - 2)); /// /// let mut more_values = vec![Some(2), None, Some(0)].into_iter(); /// assert!(more_values.fold_options(0, Add::add).is_none()); /// assert_eq!(more_values.next().unwrap(), Some(0)); /// ``` fn fold_options(&mut self, mut start: B, mut f: F) -> Option where Self: Iterator>, F: FnMut(B, A) -> B, { for elt in self { match elt { Some(v) => start = f(start, v), None => return None, } } Some(start) } /// Accumulator of the elements in the iterator. /// /// Like `.fold()`, without a base case. If the iterator is /// empty, return `None`. With just one element, return it. /// Otherwise elements are accumulated in sequence using the closure `f`. /// /// ``` /// use itertools::Itertools; /// /// assert_eq!((0..10).fold1(|x, y| x + y).unwrap_or(0), 45); /// assert_eq!((0..0).fold1(|x, y| x * y), None); /// ``` #[deprecated( note = "Use [`Iterator::reduce`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.reduce) instead", since = "0.10.2" )] fn fold1(mut self, f: F) -> Option where F: FnMut(Self::Item, Self::Item) -> Self::Item, Self: Sized, { self.next().map(move |x| self.fold(x, f)) } /// Accumulate the elements in the iterator in a tree-like manner. /// /// You can think of it as, while there's more than one item, repeatedly /// combining adjacent items. It does so in bottom-up-merge-sort order, /// however, so that it needs only logarithmic stack space. /// /// This produces a call tree like the following (where the calls under /// an item are done after reading that item): /// /// ```text /// 1 2 3 4 5 6 7 /// │ │ │ │ │ │ │ /// └─f └─f └─f │ /// │ │ │ │ /// └───f └─f /// │ │ /// └─────f /// ``` /// /// Which, for non-associative functions, will typically produce a different /// result than the linear call tree used by [`Iterator::reduce`]: /// /// ```text /// 1 2 3 4 5 6 7 /// │ │ │ │ │ │ │ /// └─f─f─f─f─f─f /// ``` /// /// If `f` is associative you should also decide carefully: /// /// - if `f` is a trivial operation like `u32::wrapping_add`, prefer the normal /// [`Iterator::reduce`] instead since it will most likely result in the generation of simpler /// code because the compiler is able to optimize it /// - otherwise if `f` is non-trivial like `format!`, you should use `tree_reduce` since it /// reduces the number of operations from `O(n)` to `O(ln(n))` /// /// Here "non-trivial" means: /// /// - any allocating operation /// - any function that is a composition of many operations /// /// ``` /// use itertools::Itertools; /// /// // The same tree as above /// let num_strings = (1..8).map(|x| x.to_string()); /// assert_eq!(num_strings.tree_reduce(|x, y| format!("f({}, {})", x, y)), /// Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))"))); /// /// // Like fold1, an empty iterator produces None /// assert_eq!((0..0).tree_reduce(|x, y| x * y), None); /// /// // tree_reduce matches fold1 for associative operations... /// assert_eq!((0..10).tree_reduce(|x, y| x + y), /// (0..10).fold1(|x, y| x + y)); /// // ...but not for non-associative ones /// assert_ne!((0..10).tree_reduce(|x, y| x - y), /// (0..10).fold1(|x, y| x - y)); /// ``` fn tree_reduce(mut self, mut f: F) -> Option where F: FnMut(Self::Item, Self::Item) -> Self::Item, Self: Sized, { type State = Result>; fn inner0(it: &mut II, f: &mut FF) -> State where II: Iterator, FF: FnMut(T, T) -> T, { // This function could be replaced with `it.next().ok_or(None)`, // but half the useful tree_reduce work is combining adjacent items, // so put that in a form that LLVM is more likely to optimize well. let a = if let Some(v) = it.next() { v } else { return Err(None); }; let b = if let Some(v) = it.next() { v } else { return Err(Some(a)); }; Ok(f(a, b)) } fn inner(stop: usize, it: &mut II, f: &mut FF) -> State where II: Iterator, FF: FnMut(T, T) -> T, { let mut x = inner0(it, f)?; for height in 0..stop { // Try to get another tree the same size with which to combine it, // creating a new tree that's twice as big for next time around. let next = if height == 0 { inner0(it, f) } else { inner(height, it, f) }; match next { Ok(y) => x = f(x, y), // If we ran out of items, combine whatever we did manage // to get. It's better combined with the current value // than something in a parent frame, because the tree in // the parent is always as least as big as this one. Err(None) => return Err(Some(x)), Err(Some(y)) => return Err(Some(f(x, y))), } } Ok(x) } match inner(usize::MAX, &mut self, &mut f) { Err(x) => x, _ => unreachable!(), } } /// See [`.tree_reduce()`](Itertools::tree_reduce). #[deprecated(note = "Use .tree_reduce() instead", since = "0.13.0")] fn tree_fold1(self, f: F) -> Option where F: FnMut(Self::Item, Self::Item) -> Self::Item, Self: Sized, { self.tree_reduce(f) } /// An iterator method that applies a function, producing a single, final value. /// /// `fold_while()` is basically equivalent to [`Iterator::fold`] but with additional support for /// early exit via short-circuiting. /// /// ``` /// use itertools::Itertools; /// use itertools::FoldWhile::{Continue, Done}; /// /// let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; /// /// let mut result = 0; /// /// // for loop: /// for i in &numbers { /// if *i > 5 { /// break; /// } /// result = result + i; /// } /// /// // fold: /// let result2 = numbers.iter().fold(0, |acc, x| { /// if *x > 5 { acc } else { acc + x } /// }); /// /// // fold_while: /// let result3 = numbers.iter().fold_while(0, |acc, x| { /// if *x > 5 { Done(acc) } else { Continue(acc + x) } /// }).into_inner(); /// /// // they're the same /// assert_eq!(result, result2); /// assert_eq!(result2, result3); /// ``` /// /// The big difference between the computations of `result2` and `result3` is that while /// `fold()` called the provided closure for every item of the callee iterator, /// `fold_while()` actually stopped iterating as soon as it encountered `Fold::Done(_)`. fn fold_while(&mut self, init: B, mut f: F) -> FoldWhile where Self: Sized, F: FnMut(B, Self::Item) -> FoldWhile, { use Result::{Err as Break, Ok as Continue}; let result = self.try_fold( init, #[inline(always)] |acc, v| match f(acc, v) { FoldWhile::Continue(acc) => Continue(acc), FoldWhile::Done(acc) => Break(acc), }, ); match result { Continue(acc) => FoldWhile::Continue(acc), Break(acc) => FoldWhile::Done(acc), } } /// Iterate over the entire iterator and add all the elements. /// /// An empty iterator returns `None`, otherwise `Some(sum)`. /// /// # Panics /// /// When calling `sum1()` and a primitive integer type is being returned, this /// method will panic if the computation overflows and debug assertions are /// enabled. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let empty_sum = (1..1).sum1::(); /// assert_eq!(empty_sum, None); /// /// let nonempty_sum = (1..11).sum1::(); /// assert_eq!(nonempty_sum, Some(55)); /// ``` fn sum1(mut self) -> Option where Self: Sized, S: std::iter::Sum, { self.next().map(|first| once(first).chain(self).sum()) } /// Iterate over the entire iterator and multiply all the elements. /// /// An empty iterator returns `None`, otherwise `Some(product)`. /// /// # Panics /// /// When calling `product1()` and a primitive integer type is being returned, /// method will panic if the computation overflows and debug assertions are /// enabled. /// /// # Examples /// ``` /// use itertools::Itertools; /// /// let empty_product = (1..1).product1::(); /// assert_eq!(empty_product, None); /// /// let nonempty_product = (1..11).product1::(); /// assert_eq!(nonempty_product, Some(3628800)); /// ``` fn product1

(mut self) -> Option

where Self: Sized, P: std::iter::Product, { self.next().map(|first| once(first).chain(self).product()) } /// Sort all iterator elements into a new iterator in ascending order. /// /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_unstable`] method and returns the result as a new /// iterator that owns its elements. /// /// This sort is unstable (i.e., may reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// ``` /// use itertools::Itertools; /// /// // sort the letters of the text in ascending order /// let text = "bdacfe"; /// itertools::assert_equal(text.chars().sorted_unstable(), /// "abcdef".chars()); /// ``` #[cfg(feature = "use_alloc")] fn sorted_unstable(self) -> VecIntoIter where Self: Sized, Self::Item: Ord, { // Use .sort_unstable() directly since it is not quite identical with // .sort_by(Ord::cmp) let mut v = Vec::from_iter(self); v.sort_unstable(); v.into_iter() } /// Sort all iterator elements into a new iterator in ascending order. /// /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_unstable_by`] method and returns the result as a new /// iterator that owns its elements. /// /// This sort is unstable (i.e., may reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// ``` /// use itertools::Itertools; /// /// // sort people in descending order by age /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)]; /// /// let oldest_people_first = people /// .into_iter() /// .sorted_unstable_by(|a, b| Ord::cmp(&b.1, &a.1)) /// .map(|(person, _age)| person); /// /// itertools::assert_equal(oldest_people_first, /// vec!["Jill", "Jack", "Jane", "John"]); /// ``` #[cfg(feature = "use_alloc")] fn sorted_unstable_by(self, cmp: F) -> VecIntoIter where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { let mut v = Vec::from_iter(self); v.sort_unstable_by(cmp); v.into_iter() } /// Sort all iterator elements into a new iterator in ascending order. /// /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_unstable_by_key`] method and returns the result as a new /// iterator that owns its elements. /// /// This sort is unstable (i.e., may reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// ``` /// use itertools::Itertools; /// /// // sort people in descending order by age /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)]; /// /// let oldest_people_first = people /// .into_iter() /// .sorted_unstable_by_key(|x| -x.1) /// .map(|(person, _age)| person); /// /// itertools::assert_equal(oldest_people_first, /// vec!["Jill", "Jack", "Jane", "John"]); /// ``` #[cfg(feature = "use_alloc")] fn sorted_unstable_by_key(self, f: F) -> VecIntoIter where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K, { let mut v = Vec::from_iter(self); v.sort_unstable_by_key(f); v.into_iter() } /// Sort all iterator elements into a new iterator in ascending order. /// /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort`] method and returns the result as a new /// iterator that owns its elements. /// /// This sort is stable (i.e., does not reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// ``` /// use itertools::Itertools; /// /// // sort the letters of the text in ascending order /// let text = "bdacfe"; /// itertools::assert_equal(text.chars().sorted(), /// "abcdef".chars()); /// ``` #[cfg(feature = "use_alloc")] fn sorted(self) -> VecIntoIter where Self: Sized, Self::Item: Ord, { // Use .sort() directly since it is not quite identical with // .sort_by(Ord::cmp) let mut v = Vec::from_iter(self); v.sort(); v.into_iter() } /// Sort all iterator elements into a new iterator in ascending order. /// /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_by`] method and returns the result as a new /// iterator that owns its elements. /// /// This sort is stable (i.e., does not reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// ``` /// use itertools::Itertools; /// /// // sort people in descending order by age /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 30)]; /// /// let oldest_people_first = people /// .into_iter() /// .sorted_by(|a, b| Ord::cmp(&b.1, &a.1)) /// .map(|(person, _age)| person); /// /// itertools::assert_equal(oldest_people_first, /// vec!["Jill", "Jack", "Jane", "John"]); /// ``` #[cfg(feature = "use_alloc")] fn sorted_by(self, cmp: F) -> VecIntoIter where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { let mut v = Vec::from_iter(self); v.sort_by(cmp); v.into_iter() } /// Sort all iterator elements into a new iterator in ascending order. /// /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_by_key`] method and returns the result as a new /// iterator that owns its elements. /// /// This sort is stable (i.e., does not reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// ``` /// use itertools::Itertools; /// /// // sort people in descending order by age /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 30)]; /// /// let oldest_people_first = people /// .into_iter() /// .sorted_by_key(|x| -x.1) /// .map(|(person, _age)| person); /// /// itertools::assert_equal(oldest_people_first, /// vec!["Jill", "Jack", "Jane", "John"]); /// ``` #[cfg(feature = "use_alloc")] fn sorted_by_key(self, f: F) -> VecIntoIter where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K, { let mut v = Vec::from_iter(self); v.sort_by_key(f); v.into_iter() } /// Sort all iterator elements into a new iterator in ascending order. The key function is /// called exactly once per key. /// /// **Note:** This consumes the entire iterator, uses the /// [`slice::sort_by_cached_key`] method and returns the result as a new /// iterator that owns its elements. /// /// This sort is stable (i.e., does not reorder equal elements). /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// ``` /// use itertools::Itertools; /// /// // sort people in descending order by age /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 30)]; /// /// let oldest_people_first = people /// .into_iter() /// .sorted_by_cached_key(|x| -x.1) /// .map(|(person, _age)| person); /// /// itertools::assert_equal(oldest_people_first, /// vec!["Jill", "Jack", "Jane", "John"]); /// ``` #[cfg(feature = "use_alloc")] fn sorted_by_cached_key(self, f: F) -> VecIntoIter where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K, { let mut v = Vec::from_iter(self); v.sort_by_cached_key(f); v.into_iter() } /// Sort the k smallest elements into a new iterator, in ascending order. /// /// **Note:** This consumes the entire iterator, and returns the result /// as a new iterator that owns its elements. If the input contains /// less than k elements, the result is equivalent to `self.sorted()`. /// /// This is guaranteed to use `k * sizeof(Self::Item) + O(1)` memory /// and `O(n log k)` time, with `n` the number of elements in the input. /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// **Note:** This is functionally-equivalent to `self.sorted().take(k)` /// but much more efficient. /// /// ``` /// use itertools::Itertools; /// /// // A random permutation of 0..15 /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; /// /// let five_smallest = numbers /// .into_iter() /// .k_smallest(5); /// /// itertools::assert_equal(five_smallest, 0..5); /// ``` #[cfg(feature = "use_alloc")] fn k_smallest(self, k: usize) -> VecIntoIter where Self: Sized, Self::Item: Ord, { // The stdlib heap has optimised handling of "holes", which is not included in our heap implementation in k_smallest_general. // While the difference is unlikely to have practical impact unless `Self::Item` is very large, this method uses the stdlib structure // to maintain performance compared to previous versions of the crate. use alloc::collections::BinaryHeap; if k == 0 { self.last(); return Vec::new().into_iter(); } if k == 1 { return self.min().into_iter().collect_vec().into_iter(); } let mut iter = self.fuse(); let mut heap: BinaryHeap<_> = iter.by_ref().take(k).collect(); iter.for_each(|i| { debug_assert_eq!(heap.len(), k); // Equivalent to heap.push(min(i, heap.pop())) but more efficient. // This should be done with a single `.peek_mut().unwrap()` but // `PeekMut` sifts-down unconditionally on Rust 1.46.0 and prior. if *heap.peek().unwrap() > i { *heap.peek_mut().unwrap() = i; } }); heap.into_sorted_vec().into_iter() } /// Sort the k smallest elements into a new iterator using the provided comparison. /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// This corresponds to `self.sorted_by(cmp).take(k)` in the same way that /// [`k_smallest`](Itertools::k_smallest) corresponds to `self.sorted().take(k)`, /// in both semantics and complexity. /// /// Particularly, a custom heap implementation ensures the comparison is not cloned. /// /// ``` /// use itertools::Itertools; /// /// // A random permutation of 0..15 /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; /// /// let five_smallest = numbers /// .into_iter() /// .k_smallest_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b))); /// /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]); /// ``` #[cfg(feature = "use_alloc")] fn k_smallest_by(self, k: usize, cmp: F) -> VecIntoIter where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { k_smallest::k_smallest_general(self, k, cmp).into_iter() } /// Return the elements producing the k smallest outputs of the provided function. /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// This corresponds to `self.sorted_by_key(key).take(k)` in the same way that /// [`k_smallest`](Itertools::k_smallest) corresponds to `self.sorted().take(k)`, /// in both semantics and complexity. /// /// Particularly, a custom heap implementation ensures the comparison is not cloned. /// /// ``` /// use itertools::Itertools; /// /// // A random permutation of 0..15 /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; /// /// let five_smallest = numbers /// .into_iter() /// .k_smallest_by_key(5, |n| (n % 7, *n)); /// /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]); /// ``` #[cfg(feature = "use_alloc")] fn k_smallest_by_key(self, k: usize, key: F) -> VecIntoIter where Self: Sized, F: FnMut(&Self::Item) -> K, K: Ord, { self.k_smallest_by(k, k_smallest::key_to_cmp(key)) } /// Sort the k largest elements into a new iterator, in descending order. /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// It is semantically equivalent to [`k_smallest`](Itertools::k_smallest) /// with a reversed `Ord`. /// However, this is implemented with a custom binary heap which does not /// have the same performance characteristics for very large `Self::Item`. /// /// ``` /// use itertools::Itertools; /// /// // A random permutation of 0..15 /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; /// /// let five_largest = numbers /// .into_iter() /// .k_largest(5); /// /// itertools::assert_equal(five_largest, vec![14, 13, 12, 11, 10]); /// ``` #[cfg(feature = "use_alloc")] fn k_largest(self, k: usize) -> VecIntoIter where Self: Sized, Self::Item: Ord, { self.k_largest_by(k, Self::Item::cmp) } /// Sort the k largest elements into a new iterator using the provided comparison. /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// Functionally equivalent to [`k_smallest_by`](Itertools::k_smallest_by) /// with a reversed `Ord`. /// /// ``` /// use itertools::Itertools; /// /// // A random permutation of 0..15 /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; /// /// let five_largest = numbers /// .into_iter() /// .k_largest_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b))); /// /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]); /// ``` #[cfg(feature = "use_alloc")] fn k_largest_by(self, k: usize, mut cmp: F) -> VecIntoIter where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { self.k_smallest_by(k, move |a, b| cmp(b, a)) } /// Return the elements producing the k largest outputs of the provided function. /// /// The sorted iterator, if directly collected to a `Vec`, is converted /// without any extra copying or allocation cost. /// /// Functionally equivalent to [`k_smallest_by_key`](Itertools::k_smallest_by_key) /// with a reversed `Ord`. /// /// ``` /// use itertools::Itertools; /// /// // A random permutation of 0..15 /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5]; /// /// let five_largest = numbers /// .into_iter() /// .k_largest_by_key(5, |n| (n % 7, *n)); /// /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]); /// ``` #[cfg(feature = "use_alloc")] fn k_largest_by_key(self, k: usize, key: F) -> VecIntoIter where Self: Sized, F: FnMut(&Self::Item) -> K, K: Ord, { self.k_largest_by(k, k_smallest::key_to_cmp(key)) } /// Consumes the iterator and return an iterator of the last `n` elements. /// /// The iterator, if directly collected to a `VecDeque`, is converted /// without any extra copying or allocation cost. /// If directly collected to a `Vec`, it may need some data movement /// but no re-allocation. /// /// ``` /// use itertools::{assert_equal, Itertools}; /// /// let v = vec![5, 9, 8, 4, 2, 12, 0]; /// assert_equal(v.iter().tail(3), &[2, 12, 0]); /// assert_equal(v.iter().tail(10), &v); /// /// assert_equal(v.iter().tail(1), v.iter().last()); /// /// assert_equal((0..100).tail(10), 90..100); /// /// assert_equal((0..100).filter(|x| x % 3 == 0).tail(10), (72..100).step_by(3)); /// ``` /// /// For double ended iterators without side-effects, you might prefer /// `.rev().take(n).rev()` to have a similar result (lazy and non-allocating) /// without consuming the entire iterator. #[cfg(feature = "use_alloc")] fn tail(self, n: usize) -> VecDequeIntoIter where Self: Sized, { match n { 0 => { self.last(); VecDeque::new() } 1 => self.last().into_iter().collect(), _ => { // Skip the starting part of the iterator if possible. let (low, _) = self.size_hint(); let mut iter = self.fuse().skip(low.saturating_sub(n)); // TODO: If VecDeque has a more efficient method than // `.pop_front();.push_back(val)` in the future then maybe revisit this. let mut data: Vec<_> = iter.by_ref().take(n).collect(); // Update `data` cyclically. let idx = iter.fold(0, |i, val| { debug_assert_eq!(data.len(), n); data[i] = val; if i + 1 == n { 0 } else { i + 1 } }); // Respect the insertion order, efficiently. let mut data = VecDeque::from(data); data.rotate_left(idx); data } } .into_iter() } /// Collect all iterator elements into one of two /// partitions. Unlike [`Iterator::partition`], each partition may /// have a distinct type. /// /// ``` /// use itertools::{Itertools, Either}; /// /// let successes_and_failures = vec![Ok(1), Err(false), Err(true), Ok(2)]; /// /// let (successes, failures): (Vec<_>, Vec<_>) = successes_and_failures /// .into_iter() /// .partition_map(|r| { /// match r { /// Ok(v) => Either::Left(v), /// Err(v) => Either::Right(v), /// } /// }); /// /// assert_eq!(successes, [1, 2]); /// assert_eq!(failures, [false, true]); /// ``` fn partition_map(self, mut predicate: F) -> (A, B) where Self: Sized, F: FnMut(Self::Item) -> Either, A: Default + Extend, B: Default + Extend, { let mut left = A::default(); let mut right = B::default(); self.for_each(|val| match predicate(val) { Either::Left(v) => left.extend(Some(v)), Either::Right(v) => right.extend(Some(v)), }); (left, right) } /// Partition a sequence of `Result`s into one list of all the `Ok` elements /// and another list of all the `Err` elements. /// /// ``` /// use itertools::Itertools; /// /// let successes_and_failures = vec![Ok(1), Err(false), Err(true), Ok(2)]; /// /// let (successes, failures): (Vec<_>, Vec<_>) = successes_and_failures /// .into_iter() /// .partition_result(); /// /// assert_eq!(successes, [1, 2]); /// assert_eq!(failures, [false, true]); /// ``` fn partition_result(self) -> (A, B) where Self: Iterator> + Sized, A: Default + Extend, B: Default + Extend, { self.partition_map(|r| match r { Ok(v) => Either::Left(v), Err(v) => Either::Right(v), }) } /// Return a `HashMap` of keys mapped to `Vec`s of values. Keys and values /// are taken from `(Key, Value)` tuple pairs yielded by the input iterator. /// /// Essentially a shorthand for `.into_grouping_map().collect::>()`. /// /// ``` /// use itertools::Itertools; /// /// let data = vec![(0, 10), (2, 12), (3, 13), (0, 20), (3, 33), (2, 42)]; /// let lookup = data.into_iter().into_group_map(); /// /// assert_eq!(lookup[&0], vec![10, 20]); /// assert_eq!(lookup.get(&1), None); /// assert_eq!(lookup[&2], vec![12, 42]); /// assert_eq!(lookup[&3], vec![13, 33]); /// ``` #[cfg(feature = "use_std")] fn into_group_map(self) -> HashMap> where Self: Iterator + Sized, K: Hash + Eq, { group_map::into_group_map(self) } /// Return an `Iterator` on a `HashMap`. Keys mapped to `Vec`s of values. The key is specified /// in the closure. /// /// Essentially a shorthand for `.into_grouping_map_by(f).collect::>()`. /// /// ``` /// use itertools::Itertools; /// use std::collections::HashMap; /// /// let data = vec![(0, 10), (2, 12), (3, 13), (0, 20), (3, 33), (2, 42)]; /// let lookup: HashMap> = /// data.clone().into_iter().into_group_map_by(|a| a.0); /// /// assert_eq!(lookup[&0], vec![(0,10),(0,20)]); /// assert_eq!(lookup.get(&1), None); /// assert_eq!(lookup[&2], vec![(2,12), (2,42)]); /// assert_eq!(lookup[&3], vec![(3,13), (3,33)]); /// /// assert_eq!( /// data.into_iter() /// .into_group_map_by(|x| x.0) /// .into_iter() /// .map(|(key, values)| (key, values.into_iter().fold(0,|acc, (_,v)| acc + v ))) /// .collect::>()[&0], /// 30, /// ); /// ``` #[cfg(feature = "use_std")] fn into_group_map_by(self, f: F) -> HashMap> where Self: Iterator + Sized, K: Hash + Eq, F: FnMut(&V) -> K, { group_map::into_group_map_by(self, f) } /// Constructs a `GroupingMap` to be used later with one of the efficient /// group-and-fold operations it allows to perform. /// /// The input iterator must yield item in the form of `(K, V)` where the /// value of type `K` will be used as key to identify the groups and the /// value of type `V` as value for the folding operation. /// /// See [`GroupingMap`] for more informations /// on what operations are available. #[cfg(feature = "use_std")] fn into_grouping_map(self) -> GroupingMap where Self: Iterator + Sized, K: Hash + Eq, { grouping_map::new(self) } /// Constructs a `GroupingMap` to be used later with one of the efficient /// group-and-fold operations it allows to perform. /// /// The values from this iterator will be used as values for the folding operation /// while the keys will be obtained from the values by calling `key_mapper`. /// /// See [`GroupingMap`] for more informations /// on what operations are available. #[cfg(feature = "use_std")] fn into_grouping_map_by(self, key_mapper: F) -> GroupingMapBy where Self: Iterator + Sized, K: Hash + Eq, F: FnMut(&V) -> K, { grouping_map::new(grouping_map::new_map_for_grouping(self, key_mapper)) } /// Return all minimum elements of an iterator. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().min_set(), Vec::<&i32>::new()); /// /// let a = [1]; /// assert_eq!(a.iter().min_set(), vec![&1]); /// /// let a = [1, 2, 3, 4, 5]; /// assert_eq!(a.iter().min_set(), vec![&1]); /// /// let a = [1, 1, 1, 1]; /// assert_eq!(a.iter().min_set(), vec![&1, &1, &1, &1]); /// ``` /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. #[cfg(feature = "use_alloc")] fn min_set(self) -> Vec where Self: Sized, Self::Item: Ord, { extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x.cmp(y)) } /// Return all minimum elements of an iterator, as determined by /// the specified function. /// /// # Examples /// /// ``` /// # use std::cmp::Ordering; /// use itertools::Itertools; /// /// let a: [(i32, i32); 0] = []; /// assert_eq!(a.iter().min_set_by(|_, _| Ordering::Equal), Vec::<&(i32, i32)>::new()); /// /// let a = [(1, 2)]; /// assert_eq!(a.iter().min_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), vec![&(1, 2)]); /// /// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)]; /// assert_eq!(a.iter().min_set_by(|&&(_,k1), &&(_,k2)| k1.cmp(&k2)), vec![&(1, 2), &(2, 2)]); /// /// let a = [(1, 2), (1, 3), (1, 4), (1, 5)]; /// assert_eq!(a.iter().min_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]); /// ``` /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. #[cfg(feature = "use_alloc")] fn min_set_by(self, mut compare: F) -> Vec where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { extrema_set::min_set_impl(self, |_| (), |x, y, _, _| compare(x, y)) } /// Return all minimum elements of an iterator, as determined by /// the specified function. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [(i32, i32); 0] = []; /// assert_eq!(a.iter().min_set_by_key(|_| ()), Vec::<&(i32, i32)>::new()); /// /// let a = [(1, 2)]; /// assert_eq!(a.iter().min_set_by_key(|&&(k,_)| k), vec![&(1, 2)]); /// /// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)]; /// assert_eq!(a.iter().min_set_by_key(|&&(_, k)| k), vec![&(1, 2), &(2, 2)]); /// /// let a = [(1, 2), (1, 3), (1, 4), (1, 5)]; /// assert_eq!(a.iter().min_set_by_key(|&&(k, _)| k), vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]); /// ``` /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. #[cfg(feature = "use_alloc")] fn min_set_by_key(self, key: F) -> Vec where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K, { extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky)) } /// Return all maximum elements of an iterator. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().max_set(), Vec::<&i32>::new()); /// /// let a = [1]; /// assert_eq!(a.iter().max_set(), vec![&1]); /// /// let a = [1, 2, 3, 4, 5]; /// assert_eq!(a.iter().max_set(), vec![&5]); /// /// let a = [1, 1, 1, 1]; /// assert_eq!(a.iter().max_set(), vec![&1, &1, &1, &1]); /// ``` /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. #[cfg(feature = "use_alloc")] fn max_set(self) -> Vec where Self: Sized, Self::Item: Ord, { extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x.cmp(y)) } /// Return all maximum elements of an iterator, as determined by /// the specified function. /// /// # Examples /// /// ``` /// # use std::cmp::Ordering; /// use itertools::Itertools; /// /// let a: [(i32, i32); 0] = []; /// assert_eq!(a.iter().max_set_by(|_, _| Ordering::Equal), Vec::<&(i32, i32)>::new()); /// /// let a = [(1, 2)]; /// assert_eq!(a.iter().max_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), vec![&(1, 2)]); /// /// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)]; /// assert_eq!(a.iter().max_set_by(|&&(_,k1), &&(_,k2)| k1.cmp(&k2)), vec![&(3, 9), &(5, 9)]); /// /// let a = [(1, 2), (1, 3), (1, 4), (1, 5)]; /// assert_eq!(a.iter().max_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]); /// ``` /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. #[cfg(feature = "use_alloc")] fn max_set_by(self, mut compare: F) -> Vec where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { extrema_set::max_set_impl(self, |_| (), |x, y, _, _| compare(x, y)) } /// Return all maximum elements of an iterator, as determined by /// the specified function. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [(i32, i32); 0] = []; /// assert_eq!(a.iter().max_set_by_key(|_| ()), Vec::<&(i32, i32)>::new()); /// /// let a = [(1, 2)]; /// assert_eq!(a.iter().max_set_by_key(|&&(k,_)| k), vec![&(1, 2)]); /// /// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)]; /// assert_eq!(a.iter().max_set_by_key(|&&(_, k)| k), vec![&(3, 9), &(5, 9)]); /// /// let a = [(1, 2), (1, 3), (1, 4), (1, 5)]; /// assert_eq!(a.iter().max_set_by_key(|&&(k, _)| k), vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]); /// ``` /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. #[cfg(feature = "use_alloc")] fn max_set_by_key(self, key: F) -> Vec where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K, { extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky)) } /// Return the minimum and maximum elements in the iterator. /// /// The return type `MinMaxResult` is an enum of three variants: /// /// - `NoElements` if the iterator is empty. /// - `OneElement(x)` if the iterator has exactly one element. /// - `MinMax(x, y)` is returned otherwise, where `x <= y`. Two /// values are equal if and only if there is more than one /// element in the iterator and all elements are equal. /// /// On an iterator of length `n`, `minmax` does `1.5 * n` comparisons, /// and so is faster than calling `min` and `max` separately which does /// `2 * n` comparisons. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{NoElements, OneElement, MinMax}; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().minmax(), NoElements); /// /// let a = [1]; /// assert_eq!(a.iter().minmax(), OneElement(&1)); /// /// let a = [1, 2, 3, 4, 5]; /// assert_eq!(a.iter().minmax(), MinMax(&1, &5)); /// /// let a = [1, 1, 1, 1]; /// assert_eq!(a.iter().minmax(), MinMax(&1, &1)); /// ``` /// /// The elements can be floats but no particular result is guaranteed /// if an element is NaN. fn minmax(self) -> MinMaxResult where Self: Sized, Self::Item: PartialOrd, { minmax::minmax_impl(self, |_| (), |x, y, _, _| x < y) } /// Return the minimum and maximum element of an iterator, as determined by /// the specified function. /// /// The return value is a variant of [`MinMaxResult`] like for [`.minmax()`](Itertools::minmax). /// /// For the minimum, the first minimal element is returned. For the maximum, /// the last maximal element wins. This matches the behavior of the standard /// [`Iterator::min`] and [`Iterator::max`] methods. /// /// The keys can be floats but no particular result is guaranteed /// if a key is NaN. fn minmax_by_key(self, key: F) -> MinMaxResult where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K, { minmax::minmax_impl(self, key, |_, _, xk, yk| xk < yk) } /// Return the minimum and maximum element of an iterator, as determined by /// the specified comparison function. /// /// The return value is a variant of [`MinMaxResult`] like for [`.minmax()`](Itertools::minmax). /// /// For the minimum, the first minimal element is returned. For the maximum, /// the last maximal element wins. This matches the behavior of the standard /// [`Iterator::min`] and [`Iterator::max`] methods. fn minmax_by(self, mut compare: F) -> MinMaxResult where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { minmax::minmax_impl(self, |_| (), |x, y, _, _| Ordering::Less == compare(x, y)) } /// Return the position of the maximum element in the iterator. /// /// If several elements are equally maximum, the position of the /// last of them is returned. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().position_max(), None); /// /// let a = [-3, 0, 1, 5, -10]; /// assert_eq!(a.iter().position_max(), Some(3)); /// /// let a = [1, 1, -1, -1]; /// assert_eq!(a.iter().position_max(), Some(1)); /// ``` fn position_max(self) -> Option where Self: Sized, Self::Item: Ord, { self.enumerate() .max_by(|x, y| Ord::cmp(&x.1, &y.1)) .map(|x| x.0) } /// Return the position of the maximum element in the iterator, as /// determined by the specified function. /// /// If several elements are equally maximum, the position of the /// last of them is returned. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), None); /// /// let a = [-3_i32, 0, 1, 5, -10]; /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(4)); /// /// let a = [1_i32, 1, -1, -1]; /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(3)); /// ``` fn position_max_by_key(self, mut key: F) -> Option where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K, { self.enumerate() .max_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))) .map(|x| x.0) } /// Return the position of the maximum element in the iterator, as /// determined by the specified comparison function. /// /// If several elements are equally maximum, the position of the /// last of them is returned. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), None); /// /// let a = [-3_i32, 0, 1, 5, -10]; /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(3)); /// /// let a = [1_i32, 1, -1, -1]; /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(1)); /// ``` fn position_max_by(self, mut compare: F) -> Option where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { self.enumerate() .max_by(|x, y| compare(&x.1, &y.1)) .map(|x| x.0) } /// Return the position of the minimum element in the iterator. /// /// If several elements are equally minimum, the position of the /// first of them is returned. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().position_min(), None); /// /// let a = [-3, 0, 1, 5, -10]; /// assert_eq!(a.iter().position_min(), Some(4)); /// /// let a = [1, 1, -1, -1]; /// assert_eq!(a.iter().position_min(), Some(2)); /// ``` fn position_min(self) -> Option where Self: Sized, Self::Item: Ord, { self.enumerate() .min_by(|x, y| Ord::cmp(&x.1, &y.1)) .map(|x| x.0) } /// Return the position of the minimum element in the iterator, as /// determined by the specified function. /// /// If several elements are equally minimum, the position of the /// first of them is returned. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), None); /// /// let a = [-3_i32, 0, 1, 5, -10]; /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(1)); /// /// let a = [1_i32, 1, -1, -1]; /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(0)); /// ``` fn position_min_by_key(self, mut key: F) -> Option where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K, { self.enumerate() .min_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))) .map(|x| x.0) } /// Return the position of the minimum element in the iterator, as /// determined by the specified comparison function. /// /// If several elements are equally minimum, the position of the /// first of them is returned. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), None); /// /// let a = [-3_i32, 0, 1, 5, -10]; /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(4)); /// /// let a = [1_i32, 1, -1, -1]; /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(2)); /// ``` fn position_min_by(self, mut compare: F) -> Option where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { self.enumerate() .min_by(|x, y| compare(&x.1, &y.1)) .map(|x| x.0) } /// Return the positions of the minimum and maximum elements in /// the iterator. /// /// The return type [`MinMaxResult`] is an enum of three variants: /// /// - `NoElements` if the iterator is empty. /// - `OneElement(xpos)` if the iterator has exactly one element. /// - `MinMax(xpos, ypos)` is returned otherwise, where the /// element at `xpos` ≤ the element at `ypos`. While the /// referenced elements themselves may be equal, `xpos` cannot /// be equal to `ypos`. /// /// On an iterator of length `n`, `position_minmax` does `1.5 * n` /// comparisons, and so is faster than calling `position_min` and /// `position_max` separately which does `2 * n` comparisons. /// /// For the minimum, if several elements are equally minimum, the /// position of the first of them is returned. For the maximum, if /// several elements are equally maximum, the position of the last /// of them is returned. /// /// The elements can be floats but no particular result is /// guaranteed if an element is NaN. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{NoElements, OneElement, MinMax}; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().position_minmax(), NoElements); /// /// let a = [10]; /// assert_eq!(a.iter().position_minmax(), OneElement(0)); /// /// let a = [-3, 0, 1, 5, -10]; /// assert_eq!(a.iter().position_minmax(), MinMax(4, 3)); /// /// let a = [1, 1, -1, -1]; /// assert_eq!(a.iter().position_minmax(), MinMax(2, 1)); /// ``` fn position_minmax(self) -> MinMaxResult where Self: Sized, Self::Item: PartialOrd, { use crate::MinMaxResult::{MinMax, NoElements, OneElement}; match minmax::minmax_impl(self.enumerate(), |_| (), |x, y, _, _| x.1 < y.1) { NoElements => NoElements, OneElement(x) => OneElement(x.0), MinMax(x, y) => MinMax(x.0, y.0), } } /// Return the postions of the minimum and maximum elements of an /// iterator, as determined by the specified function. /// /// The return value is a variant of [`MinMaxResult`] like for /// [`position_minmax`]. /// /// For the minimum, if several elements are equally minimum, the /// position of the first of them is returned. For the maximum, if /// several elements are equally maximum, the position of the last /// of them is returned. /// /// The keys can be floats but no particular result is guaranteed /// if a key is NaN. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{NoElements, OneElement, MinMax}; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), NoElements); /// /// let a = [10_i32]; /// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), OneElement(0)); /// /// let a = [-3_i32, 0, 1, 5, -10]; /// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), MinMax(1, 4)); /// /// let a = [1_i32, 1, -1, -1]; /// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), MinMax(0, 3)); /// ``` /// /// [`position_minmax`]: Self::position_minmax fn position_minmax_by_key(self, mut key: F) -> MinMaxResult where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K, { use crate::MinMaxResult::{MinMax, NoElements, OneElement}; match self.enumerate().minmax_by_key(|e| key(&e.1)) { NoElements => NoElements, OneElement(x) => OneElement(x.0), MinMax(x, y) => MinMax(x.0, y.0), } } /// Return the postions of the minimum and maximum elements of an /// iterator, as determined by the specified comparison function. /// /// The return value is a variant of [`MinMaxResult`] like for /// [`position_minmax`]. /// /// For the minimum, if several elements are equally minimum, the /// position of the first of them is returned. For the maximum, if /// several elements are equally maximum, the position of the last /// of them is returned. /// /// # Examples /// /// ``` /// use itertools::Itertools; /// use itertools::MinMaxResult::{NoElements, OneElement, MinMax}; /// /// let a: [i32; 0] = []; /// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), NoElements); /// /// let a = [10_i32]; /// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), OneElement(0)); /// /// let a = [-3_i32, 0, 1, 5, -10]; /// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), MinMax(4, 3)); /// /// let a = [1_i32, 1, -1, -1]; /// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), MinMax(2, 1)); /// ``` /// /// [`position_minmax`]: Self::position_minmax fn position_minmax_by(self, mut compare: F) -> MinMaxResult where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { use crate::MinMaxResult::{MinMax, NoElements, OneElement}; match self.enumerate().minmax_by(|x, y| compare(&x.1, &y.1)) { NoElements => NoElements, OneElement(x) => OneElement(x.0), MinMax(x, y) => MinMax(x.0, y.0), } } /// If the iterator yields exactly one element, that element will be returned, otherwise /// an error will be returned containing an iterator that has the same output as the input /// iterator. /// /// This provides an additional layer of validation over just calling `Iterator::next()`. /// If your assumption that there should only be one element yielded is false this provides /// the opportunity to detect and handle that, preventing errors at a distance. /// /// # Examples /// ``` /// use itertools::Itertools; /// /// assert_eq!((0..10).filter(|&x| x == 2).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)); /// ``` fn exactly_one(mut self) -> Result> where Self: Sized, { match self.next() { Some(first) => match self.next() { Some(second) => Err(ExactlyOneError::new( Some(Either::Left([first, second])), self, )), None => Ok(first), }, None => Err(ExactlyOneError::new(None, self)), } } /// If the iterator yields no elements, `Ok(None)` will be returned. If the iterator yields /// exactly one element, that element will be returned, otherwise an error will be returned /// containing an iterator that has the same output as the input iterator. /// /// This provides an additional layer of validation over just calling `Iterator::next()`. /// If your assumption that there should be at most one element yielded is false this provides /// the opportunity to detect and handle that, preventing errors at a distance. /// /// # Examples /// ``` /// use itertools::Itertools; /// /// assert_eq!((0..10).filter(|&x| x == 2).at_most_one().unwrap(), Some(2)); /// assert!((0..10).filter(|&x| x > 1 && x < 4).at_most_one().unwrap_err().eq(2..4)); /// assert!((0..10).filter(|&x| x > 1 && x < 5).at_most_one().unwrap_err().eq(2..5)); /// assert_eq!((0..10).filter(|&_| false).at_most_one().unwrap(), None); /// ``` fn at_most_one(mut self) -> Result, ExactlyOneError> where Self: Sized, { match self.next() { Some(first) => match self.next() { Some(second) => Err(ExactlyOneError::new( Some(Either::Left([first, second])), self, )), None => Ok(Some(first)), }, None => Ok(None), } } /// An iterator adaptor that allows the user to peek at multiple `.next()` /// values without advancing the base iterator. /// /// # Examples /// ``` /// use itertools::Itertools; /// /// let mut iter = (0..10).multipeek(); /// assert_eq!(iter.peek(), Some(&0)); /// assert_eq!(iter.peek(), Some(&1)); /// assert_eq!(iter.peek(), Some(&2)); /// assert_eq!(iter.next(), Some(0)); /// assert_eq!(iter.peek(), Some(&1)); /// ``` #[cfg(feature = "use_alloc")] fn multipeek(self) -> MultiPeek where Self: Sized, { multipeek_impl::multipeek(self) } /// Collect the items in this iterator and return a `HashMap` which /// contains each item that appears in the iterator and the number /// of times it appears. /// /// # Examples /// ``` /// # use itertools::Itertools; /// let counts = [1, 1, 1, 3, 3, 5].into_iter().counts(); /// assert_eq!(counts[&1], 3); /// assert_eq!(counts[&3], 2); /// assert_eq!(counts[&5], 1); /// assert_eq!(counts.get(&0), None); /// ``` #[cfg(feature = "use_std")] fn counts(self) -> HashMap where Self: Sized, Self::Item: Eq + Hash, { let mut counts = HashMap::new(); self.for_each(|item| *counts.entry(item).or_default() += 1); counts } /// Collect the items in this iterator and return a `HashMap` which /// contains each item that appears in the iterator and the number /// of times it appears, /// determining identity using a keying function. /// /// ``` /// # use itertools::Itertools; /// struct Character { /// first_name: &'static str, /// last_name: &'static str, /// } /// /// let characters = /// vec![ /// Character { first_name: "Amy", last_name: "Pond" }, /// Character { first_name: "Amy", last_name: "Wong" }, /// Character { first_name: "Amy", last_name: "Santiago" }, /// Character { first_name: "James", last_name: "Bond" }, /// Character { first_name: "James", last_name: "Sullivan" }, /// Character { first_name: "James", last_name: "Norington" }, /// Character { first_name: "James", last_name: "Kirk" }, /// ]; /// /// let first_name_frequency = /// characters /// .into_iter() /// .counts_by(|c| c.first_name); /// /// assert_eq!(first_name_frequency["Amy"], 3); /// assert_eq!(first_name_frequency["James"], 4); /// assert_eq!(first_name_frequency.contains_key("Asha"), false); /// ``` #[cfg(feature = "use_std")] fn counts_by(self, f: F) -> HashMap where Self: Sized, K: Eq + Hash, F: FnMut(Self::Item) -> K, { self.map(f).counts() } /// Converts an iterator of tuples into a tuple of containers. /// /// It consumes an entire iterator of n-ary tuples, producing `n` collections, one for each /// column. /// /// This function is, in some sense, the opposite of [`multizip`]. /// /// ``` /// use itertools::Itertools; /// /// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; /// /// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = inputs /// .into_iter() /// .multiunzip(); /// /// assert_eq!(a, vec![1, 4, 7]); /// assert_eq!(b, vec![2, 5, 8]); /// assert_eq!(c, vec![3, 6, 9]); /// ``` fn multiunzip(self) -> FromI where Self: Sized + MultiUnzip, { MultiUnzip::multiunzip(self) } /// Returns the length of the iterator if one exists. /// Otherwise return `self.size_hint()`. /// /// Fallible [`ExactSizeIterator::len`]. /// /// Inherits guarantees and restrictions from [`Iterator::size_hint`]. /// /// ``` /// use itertools::Itertools; /// /// assert_eq!([0; 10].iter().try_len(), Ok(10)); /// assert_eq!((10..15).try_len(), Ok(5)); /// assert_eq!((15..10).try_len(), Ok(0)); /// assert_eq!((10..).try_len(), Err((usize::MAX, None))); /// assert_eq!((10..15).filter(|x| x % 2 == 0).try_len(), Err((0, Some(5)))); /// ``` fn try_len(&self) -> Result { let sh = self.size_hint(); match sh { (lo, Some(hi)) if lo == hi => Ok(lo), _ => Err(sh), } } } impl Itertools for T where T: Iterator + ?Sized {} /// Return `true` if both iterables produce equal sequences /// (elements pairwise equal and sequences of the same length), /// `false` otherwise. /// /// [`IntoIterator`] enabled version of [`Iterator::eq`]. /// /// ``` /// assert!(itertools::equal(vec![1, 2, 3], 1..4)); /// assert!(!itertools::equal(&[0, 0], &[0, 0, 0])); /// ``` pub fn equal(a: I, b: J) -> bool where I: IntoIterator, J: IntoIterator, I::Item: PartialEq, { a.into_iter().eq(b) } /// Assert that two iterables produce equal sequences, with the same /// semantics as [`equal(a, b)`](equal). /// /// **Panics** on assertion failure with a message that shows the /// two different elements and the iteration index. /// /// ```should_panic /// # use itertools::assert_equal; /// assert_equal("exceed".split('c'), "excess".split('c')); /// // ^PANIC: panicked at 'Failed assertion Some("eed") == Some("ess") for iteration 1'. /// ``` pub fn assert_equal(a: I, b: J) where I: IntoIterator, J: IntoIterator, I::Item: fmt::Debug + PartialEq, J::Item: fmt::Debug, { let mut ia = a.into_iter(); let mut ib = b.into_iter(); let mut i: usize = 0; loop { match (ia.next(), ib.next()) { (None, None) => return, (a, b) => { let equal = match (&a, &b) { (Some(a), Some(b)) => a == b, _ => false, }; assert!( equal, "Failed assertion {a:?} == {b:?} for iteration {i}", i = i, a = a, b = b ); i += 1; } } } } /// Partition a sequence using predicate `pred` so that elements /// that map to `true` are placed before elements which map to `false`. /// /// The order within the partitions is arbitrary. /// /// Return the index of the split point. /// /// ``` /// use itertools::partition; /// /// # // use repeated numbers to not promise any ordering /// let mut data = [7, 1, 1, 7, 1, 1, 7]; /// let split_index = partition(&mut data, |elt| *elt >= 3); /// /// assert_eq!(data, [7, 7, 7, 1, 1, 1, 1]); /// assert_eq!(split_index, 3); /// ``` pub fn partition<'a, A: 'a, I, F>(iter: I, mut pred: F) -> usize where I: IntoIterator, I::IntoIter: DoubleEndedIterator, F: FnMut(&A) -> bool, { let mut split_index = 0; let mut iter = iter.into_iter(); while let Some(front) = iter.next() { if !pred(front) { match iter.rfind(|back| pred(back)) { Some(back) => std::mem::swap(front, back), None => break, } } split_index += 1; } split_index } /// An enum used for controlling the execution of `fold_while`. /// /// See [`.fold_while()`](Itertools::fold_while) for more information. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum FoldWhile { /// Continue folding with this value Continue(T), /// Fold is complete and will return this value Done(T), } impl FoldWhile { /// Return the value in the continue or done. pub fn into_inner(self) -> T { match self { Self::Continue(x) | Self::Done(x) => x, } } /// Return true if `self` is `Done`, false if it is `Continue`. pub fn is_done(&self) -> bool { match *self { Self::Continue(_) => false, Self::Done(_) => true, } } } itertools-0.13.0/src/merge_join.rs000064400000000000000000000247061046102023000152150ustar 00000000000000use std::cmp::Ordering; use std::fmt; use std::iter::{Fuse, FusedIterator}; use std::marker::PhantomData; use either::Either; use super::adaptors::{put_back, PutBack}; use crate::either_or_both::EitherOrBoth; use crate::size_hint::{self, SizeHint}; #[cfg(doc)] use crate::Itertools; #[derive(Clone, Debug)] pub struct MergeLte; /// An iterator adaptor that merges the two base iterators in ascending order. /// If both base iterators are sorted (ascending), the result is sorted. /// /// Iterator element type is `I::Item`. /// /// See [`.merge()`](crate::Itertools::merge_by) for more information. pub type Merge = MergeBy; /// Create an iterator that merges elements in `i` and `j`. /// /// [`IntoIterator`] enabled version of [`Itertools::merge`](crate::Itertools::merge). /// /// ``` /// use itertools::merge; /// /// for elt in merge(&[1, 2, 3], &[2, 3, 4]) { /// /* loop body */ /// } /// ``` pub fn merge( i: I, j: J, ) -> Merge<::IntoIter, ::IntoIter> where I: IntoIterator, J: IntoIterator, I::Item: PartialOrd, { merge_by_new(i, j, MergeLte) } /// An iterator adaptor that merges the two base iterators in ascending order. /// If both base iterators are sorted (ascending), the result is sorted. /// /// Iterator element type is `I::Item`. /// /// See [`.merge_by()`](crate::Itertools::merge_by) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MergeBy { left: PutBack>, right: PutBack>, cmp_fn: F, } /// Create a `MergeBy` iterator. pub fn merge_by_new(a: I, b: J, cmp: F) -> MergeBy where I: IntoIterator, J: IntoIterator, { MergeBy { left: put_back(a.into_iter().fuse()), right: put_back(b.into_iter().fuse()), cmp_fn: cmp, } } /// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order. /// /// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`]. pub fn merge_join_by( left: I, right: J, cmp_fn: F, ) -> MergeJoinBy where I: IntoIterator, J: IntoIterator, F: FnMut(&I::Item, &J::Item) -> T, { MergeBy { left: put_back(left.into_iter().fuse()), right: put_back(right.into_iter().fuse()), 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. pub type MergeJoinBy = MergeBy::Item, ::Item>>::T>>; #[derive(Clone, Debug)] pub struct MergeFuncLR(F, PhantomData); pub trait FuncLR { type T; } impl T> FuncLR for F { type T = T; } pub trait OrderingOrBool { type MergeResult; fn left(left: L) -> Self::MergeResult; fn right(right: R) -> Self::MergeResult; // "merge" never returns (Some(...), Some(...), ...) so Option> // is appealing but it is always followed by two put_backs, so we think the compiler is // smart enough to optimize it. Or we could move put_backs into "merge". fn merge(&mut self, left: L, right: R) -> (Option>, Self::MergeResult); fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint; } impl Ordering> OrderingOrBool for MergeFuncLR { type MergeResult = EitherOrBoth; fn left(left: L) -> Self::MergeResult { EitherOrBoth::Left(left) } fn right(right: R) -> Self::MergeResult { EitherOrBoth::Right(right) } fn merge(&mut self, left: L, right: R) -> (Option>, Self::MergeResult) { match self.0(&left, &right) { Ordering::Equal => (None, EitherOrBoth::Both(left, right)), Ordering::Less => (Some(Either::Right(right)), EitherOrBoth::Left(left)), Ordering::Greater => (Some(Either::Left(left)), EitherOrBoth::Right(right)), } } fn size_hint(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) } } impl bool> OrderingOrBool for MergeFuncLR { type MergeResult = Either; fn left(left: L) -> Self::MergeResult { Either::Left(left) } fn right(right: R) -> Self::MergeResult { Either::Right(right) } fn merge(&mut self, left: L, right: R) -> (Option>, Self::MergeResult) { if self.0(&left, &right) { (Some(Either::Right(right)), Either::Left(left)) } else { (Some(Either::Left(left)), Either::Right(right)) } } fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { // Not ExactSizeIterator because size may be larger than usize size_hint::add(left, right) } } impl bool> OrderingOrBool for F { type MergeResult = T; fn left(left: T) -> Self::MergeResult { left } fn right(right: T) -> Self::MergeResult { right } fn merge(&mut self, left: T, right: T) -> (Option>, Self::MergeResult) { if self(&left, &right) { (Some(Either::Right(right)), left) } else { (Some(Either::Left(left)), right) } } fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { // Not ExactSizeIterator because size may be larger than usize size_hint::add(left, right) } } impl OrderingOrBool for MergeLte { type MergeResult = T; fn left(left: T) -> Self::MergeResult { left } fn right(right: T) -> Self::MergeResult { right } fn merge(&mut self, left: T, right: T) -> (Option>, Self::MergeResult) { if left <= right { (Some(Either::Right(right)), left) } else { (Some(Either::Left(left)), right) } } fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { // Not ExactSizeIterator because size may be larger than usize size_hint::add(left, right) } } impl Clone for MergeBy where I: Iterator, J: Iterator, PutBack>: Clone, PutBack>: Clone, F: Clone, { clone_fields!(left, right, cmp_fn); } impl fmt::Debug for MergeBy where I: Iterator + fmt::Debug, I::Item: fmt::Debug, J: Iterator + fmt::Debug, J::Item: fmt::Debug, { debug_fmt_fields!(MergeBy, left, right); } impl Iterator for MergeBy where I: Iterator, J: Iterator, F: OrderingOrBool, { type Item = F::MergeResult; fn next(&mut self) -> Option { match (self.left.next(), self.right.next()) { (None, None) => None, (Some(left), None) => Some(F::left(left)), (None, Some(right)) => Some(F::right(right)), (Some(left), Some(right)) => { let (not_next, next) = self.cmp_fn.merge(left, right); match not_next { Some(Either::Left(l)) => { self.left.put_back(l); } Some(Either::Right(r)) => { self.right.put_back(r); } None => (), } Some(next) } } } fn fold(mut self, init: B, mut f: G) -> B where Self: Sized, G: FnMut(B, Self::Item) -> B, { let mut acc = init; let mut left = self.left.next(); let mut right = self.right.next(); loop { match (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 { loop { if n == 0 { break self.next(); } n -= 1; match (self.left.next(), self.right.next()) { (None, None) => break None, (Some(_left), None) => break self.left.nth(n).map(F::left), (None, Some(_right)) => break self.right.nth(n).map(F::right), (Some(left), Some(right)) => { let (not_next, _) = self.cmp_fn.merge(left, right); match not_next { Some(Either::Left(l)) => { self.left.put_back(l); } Some(Either::Right(r)) => { self.right.put_back(r); } None => (), } } } } } } impl FusedIterator for MergeBy where I: Iterator, J: Iterator, F: OrderingOrBool, { } itertools-0.13.0/src/minmax.rs000064400000000000000000000074011046102023000143610ustar 00000000000000/// `MinMaxResult` is an enum returned by `minmax`. /// /// See [`.minmax()`](crate::Itertools::minmax) for more detail. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum MinMaxResult { /// Empty iterator NoElements, /// Iterator with one element, so the minimum and maximum are the same OneElement(T), /// More than one element in the iterator, the first element is not larger /// than the second MinMax(T, T), } impl MinMaxResult { /// `into_option` creates an `Option` of type `(T, T)`. The returned `Option` /// has variant `None` if and only if the `MinMaxResult` has variant /// `NoElements`. Otherwise `Some((x, y))` is returned where `x <= y`. /// If the `MinMaxResult` has variant `OneElement(x)`, performing this /// operation will make one clone of `x`. /// /// # Examples /// /// ``` /// use itertools::MinMaxResult::{self, NoElements, OneElement, MinMax}; /// /// let r: MinMaxResult = NoElements; /// assert_eq!(r.into_option(), None); /// /// let r = OneElement(1); /// assert_eq!(r.into_option(), Some((1, 1))); /// /// let r = MinMax(1, 2); /// assert_eq!(r.into_option(), Some((1, 2))); /// ``` pub fn into_option(self) -> Option<(T, T)> { match self { Self::NoElements => None, Self::OneElement(x) => Some((x.clone(), x)), Self::MinMax(x, y) => Some((x, y)), } } } /// Implementation guts for `minmax` and `minmax_by_key`. pub fn minmax_impl(mut it: I, mut key_for: F, mut lt: L) -> MinMaxResult where I: Iterator, F: FnMut(&I::Item) -> K, L: FnMut(&I::Item, &I::Item, &K, &K) -> bool, { 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) } } }, }; loop { // `first` and `second` are the two next elements we want to look // at. We first compare `first` and `second` (#1). The smaller one // is then compared to current minimum (#2). The larger one is // compared to current maximum (#3). This way we do 3 comparisons // for 2 elements. let first = match it.next() { None => break, Some(x) => x, }; let second = match it.next() { None => { let first_key = key_for(&first); if lt(&first, &min, &first_key, &min_key) { min = first; } else if !lt(&first, &max, &first_key, &max_key) { max = first; } break; } Some(x) => x, }; let first_key = key_for(&first); let second_key = key_for(&second); if !lt(&second, &first, &second_key, &first_key) { if lt(&first, &min, &first_key, &min_key) { min = first; min_key = first_key; } if !lt(&second, &max, &second_key, &max_key) { max = second; max_key = second_key; } } else { if lt(&second, &min, &second_key, &min_key) { min = second; min_key = second_key; } if !lt(&first, &max, &first_key, &max_key) { max = first; max_key = first_key; } } } MinMaxResult::MinMax(min, max) } itertools-0.13.0/src/multipeek_impl.rs000064400000000000000000000054011046102023000161060ustar 00000000000000use crate::size_hint; #[cfg(doc)] use crate::Itertools; use crate::PeekingNext; use alloc::collections::VecDeque; use std::iter::Fuse; /// See [`multipeek()`] for more information. #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MultiPeek where I: Iterator, { iter: Fuse, buf: VecDeque, index: usize, } /// An iterator adaptor that allows the user to peek at multiple `.next()` /// values without advancing the base iterator. /// /// [`IntoIterator`] enabled version of [`Itertools::multipeek`]. pub fn multipeek(iterable: I) -> MultiPeek where I: IntoIterator, { MultiPeek { iter: iterable.into_iter().fuse(), buf: VecDeque::new(), index: 0, } } impl MultiPeek where I: Iterator, { /// Reset the peeking “cursor” pub fn reset_peek(&mut self) { self.index = 0; } } impl MultiPeek { /// Works exactly like `.next()` with the only difference that it doesn't /// advance itself. `.peek()` can be called multiple times, to peek /// further ahead. /// When `.next()` is called, reset the peeking “cursor”. pub fn peek(&mut self) -> Option<&I::Item> { let ret = if self.index < self.buf.len() { Some(&self.buf[self.index]) } else { match self.iter.next() { Some(x) => { self.buf.push_back(x); Some(&self.buf[self.index]) } None => return None, } }; self.index += 1; ret } } impl PeekingNext for MultiPeek where I: Iterator, { fn peeking_next(&mut self, accept: F) -> Option where F: FnOnce(&Self::Item) -> bool, { if self.buf.is_empty() { if let Some(r) = self.peek() { if !accept(r) { return None; } } } else if let Some(r) = self.buf.front() { if !accept(r) { return None; } } self.next() } } impl Iterator for MultiPeek where I: Iterator, { type Item = I::Item; fn next(&mut self) -> Option { self.index = 0; self.buf.pop_front().or_else(|| self.iter.next()) } fn size_hint(&self) -> (usize, Option) { size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) } fn fold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { init = self.buf.into_iter().fold(init, &mut f); self.iter.fold(init, f) } } // Same size impl ExactSizeIterator for MultiPeek where I: ExactSizeIterator {} itertools-0.13.0/src/pad_tail.rs000064400000000000000000000055131046102023000146470ustar 00000000000000use 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. /// /// Iterator element type is `I::Item`. /// /// See [`.pad_using()`](crate::Itertools::pad_using) for more information. #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct PadUsing { iter: Fuse, min: usize, pos: usize, filler: F, } impl std::fmt::Debug for PadUsing where I: std::fmt::Debug, { debug_fmt_fields!(PadUsing, iter, min, pos); } /// Create a new `PadUsing` iterator. pub fn pad_using(iter: I, min: usize, filler: F) -> PadUsing where I: Iterator, F: FnMut(usize) -> I::Item, { PadUsing { iter: iter.fuse(), min, pos: 0, filler, } } impl Iterator for PadUsing where I: Iterator, F: FnMut(usize) -> I::Item, { type Item = I::Item; #[inline] fn next(&mut self) -> Option { match self.iter.next() { None => { if self.pos < self.min { let e = Some((self.filler)(self.pos)); self.pos += 1; e } else { None } } e => { self.pos += 1; e } } } fn size_hint(&self) -> (usize, Option) { let tail = self.min.saturating_sub(self.pos); size_hint::max(self.iter.size_hint(), (tail, Some(tail))) } fn fold(self, mut init: B, mut f: G) -> B where G: FnMut(B, Self::Item) -> B, { let mut pos = self.pos; init = self.iter.fold(init, |acc, item| { pos += 1; f(acc, item) }); (pos..self.min).map(self.filler).fold(init, f) } } impl DoubleEndedIterator for PadUsing where I: DoubleEndedIterator + ExactSizeIterator, F: FnMut(usize) -> I::Item, { fn next_back(&mut self) -> Option { if self.min == 0 { self.iter.next_back() } else if self.iter.len() >= self.min { self.min -= 1; self.iter.next_back() } else { self.min -= 1; Some((self.filler)(self.min)) } } fn rfold(self, mut init: B, mut f: G) -> B where G: FnMut(B, Self::Item) -> B, { init = (self.iter.len()..self.min) .map(self.filler) .rfold(init, &mut f); self.iter.rfold(init, f) } } impl ExactSizeIterator for PadUsing where I: ExactSizeIterator, F: FnMut(usize) -> I::Item, { } impl FusedIterator for PadUsing where I: FusedIterator, F: FnMut(usize) -> I::Item, { } itertools-0.13.0/src/peek_nth.rs000064400000000000000000000120701046102023000146630ustar 00000000000000use crate::size_hint; use crate::PeekingNext; use alloc::collections::VecDeque; use std::iter::Fuse; /// See [`peek_nth()`] for more information. #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct PeekNth where I: Iterator, { iter: Fuse, buf: VecDeque, } /// A drop-in replacement for [`std::iter::Peekable`] which adds a `peek_nth` /// method allowing the user to `peek` at a value several iterations forward /// without advancing the base iterator. /// /// This differs from `multipeek` in that subsequent calls to `peek` or /// `peek_nth` will always return the same value until `next` is called /// (making `reset_peek` unnecessary). pub fn peek_nth(iterable: I) -> PeekNth where I: IntoIterator, { PeekNth { iter: iterable.into_iter().fuse(), buf: VecDeque::new(), } } impl PeekNth where I: Iterator, { /// 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: /// /// ``` /// use itertools::peek_nth; /// /// 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)); /// /// // 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)); /// /// // Calling `peek_nth` past the end of the iterator will return `None` /// assert_eq!(iter.peek_nth(1), None); /// ``` pub fn peek_nth(&mut self, n: usize) -> Option<&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(n) } /// Returns a mutable reference to the `nth` value without advancing the iterator. /// /// # Examples /// /// Basic usage: /// /// ``` /// use itertools::peek_nth; /// /// let xs = vec![1, 2, 3, 4, 5]; /// let mut iter = peek_nth(xs.into_iter()); /// /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 1)); /// assert_eq!(iter.next(), Some(1)); /// /// // The iterator does not advance even if we call `peek_nth_mut` multiple times /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 2)); /// assert_eq!(iter.peek_nth_mut(1), Some(&mut 3)); /// assert_eq!(iter.next(), Some(2)); /// /// // Peek into the iterator and set the value behind the mutable reference. /// if let Some(p) = iter.peek_nth_mut(1) { /// assert_eq!(*p, 4); /// *p = 9; /// } /// /// // The value we put in reappears as the iterator continues. /// assert_eq!(iter.next(), Some(3)); /// assert_eq!(iter.next(), Some(9)); /// /// // Calling `peek_nth_mut` past the end of the iterator will return `None` /// assert_eq!(iter.peek_nth_mut(1), None); /// ``` pub fn peek_nth_mut(&mut self, n: usize) -> Option<&mut I::Item> { let unbuffered_items = (n + 1).saturating_sub(self.buf.len()); self.buf.extend(self.iter.by_ref().take(unbuffered_items)); self.buf.get_mut(n) } /// Works exactly like the `next_if` method in [`std::iter::Peekable`]. pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option { match self.next() { Some(item) if func(&item) => Some(item), Some(item) => { self.buf.push_front(item); None } _ => None, } } /// Works exactly like the `next_if_eq` method in [`std::iter::Peekable`]. pub fn next_if_eq(&mut self, expected: &T) -> Option where T: ?Sized, I::Item: PartialEq, { self.next_if(|next| next == expected) } } impl Iterator for PeekNth where I: Iterator, { type Item = I::Item; fn next(&mut self) -> Option { self.buf.pop_front().or_else(|| self.iter.next()) } fn size_hint(&self) -> (usize, Option) { size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) } fn fold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { init = self.buf.into_iter().fold(init, &mut f); self.iter.fold(init, f) } } impl ExactSizeIterator for PeekNth where I: ExactSizeIterator {} impl PeekingNext for PeekNth where I: Iterator, { fn peeking_next(&mut self, accept: F) -> Option where F: FnOnce(&Self::Item) -> bool, { self.peek().filter(|item| accept(item))?; self.next() } } itertools-0.13.0/src/peeking_take_while.rs000064400000000000000000000124231046102023000167060ustar 00000000000000use 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. /// /// See [`.peeking_take_while()`](crate::Itertools::peeking_take_while) /// for more information. /// /// This is implemented by peeking adaptors like peekable and put back, /// but also by a few iterators that can be peeked natively, like the slice’s /// by reference iterator ([`std::slice::Iter`]). pub trait PeekingNext: Iterator { /// Pass a reference to the next iterator element to the closure `accept`; /// if `accept` returns `true`, return it as the next element, /// else `None`. fn peeking_next(&mut self, accept: F) -> Option where Self: Sized, F: FnOnce(&Self::Item) -> bool; } impl<'a, I> PeekingNext for &'a mut I where I: PeekingNext, { fn peeking_next(&mut self, accept: F) -> Option where F: FnOnce(&Self::Item) -> bool, { (*self).peeking_next(accept) } } impl PeekingNext for Peekable where I: Iterator, { fn peeking_next(&mut self, accept: F) -> Option where F: FnOnce(&Self::Item) -> bool, { if let Some(r) = self.peek() { if !accept(r) { return None; } } self.next() } } impl PeekingNext for PutBack where I: Iterator, { fn peeking_next(&mut self, accept: F) -> Option where F: FnOnce(&Self::Item) -> bool, { if let Some(r) = self.next() { if !accept(&r) { self.put_back(r); return None; } Some(r) } else { None } } } #[cfg(feature = "use_alloc")] impl PeekingNext for PutBackN where I: Iterator, { fn peeking_next(&mut self, accept: F) -> Option where F: FnOnce(&Self::Item) -> bool, { if let Some(r) = self.next() { if !accept(&r) { self.put_back(r); return None; } Some(r) } else { None } } } impl PeekingNext for RepeatN { fn peeking_next(&mut self, accept: F) -> Option where F: FnOnce(&Self::Item) -> bool, { let r = self.elt.as_ref()?; if !accept(r) { return None; } self.next() } } /// An iterator adaptor that takes items while a closure returns `true`. /// /// See [`.peeking_take_while()`](crate::Itertools::peeking_take_while) /// for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct PeekingTakeWhile<'a, I, F> where I: Iterator + 'a, { iter: &'a mut I, f: F, } impl<'a, I, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F> where I: Iterator + std::fmt::Debug + 'a, { debug_fmt_fields!(PeekingTakeWhile, iter); } /// Create a `PeekingTakeWhile` pub fn peeking_take_while(iter: &mut I, f: F) -> PeekingTakeWhile where I: Iterator, { PeekingTakeWhile { iter, f } } impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F> where I: PeekingNext, F: FnMut(&I::Item) -> bool, { type Item = I::Item; fn next(&mut self) -> Option { self.iter.peeking_next(&mut self.f) } fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) } } impl<'a, I, F> PeekingNext for PeekingTakeWhile<'a, I, F> where I: PeekingNext, F: FnMut(&I::Item) -> bool, { fn peeking_next(&mut self, g: G) -> Option where G: FnOnce(&Self::Item) -> bool, { let f = &mut self.f; self.iter.peeking_next(|r| f(r) && g(r)) } } // Some iterators are so lightweight we can simply clone them to save their // state and use that for peeking. macro_rules! peeking_next_by_clone { ([$($typarm:tt)*] $type_:ty) => { impl<$($typarm)*> PeekingNext for $type_ { fn peeking_next(&mut self, accept: F) -> Option where F: FnOnce(&Self::Item) -> bool { let saved_state = self.clone(); if let Some(r) = self.next() { if !accept(&r) { *self = saved_state; } else { return Some(r) } } None } } } } peeking_next_by_clone! { ['a, T] ::std::slice::Iter<'a, T> } peeking_next_by_clone! { ['a] ::std::str::Chars<'a> } peeking_next_by_clone! { ['a] ::std::str::CharIndices<'a> } peeking_next_by_clone! { ['a] ::std::str::Bytes<'a> } peeking_next_by_clone! { ['a, T] ::std::option::Iter<'a, T> } peeking_next_by_clone! { ['a, T] ::std::result::Iter<'a, T> } peeking_next_by_clone! { [T] ::std::iter::Empty } #[cfg(feature = "use_alloc")] peeking_next_by_clone! { ['a, T] alloc::collections::linked_list::Iter<'a, T> } #[cfg(feature = "use_alloc")] peeking_next_by_clone! { ['a, T] alloc::collections::vec_deque::Iter<'a, T> } // 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 } itertools-0.13.0/src/permutations.rs000064400000000000000000000132371046102023000156260ustar 00000000000000use 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. /// /// See [`.permutations()`](crate::Itertools::permutations) for /// more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Permutations { vals: LazyBuffer, state: PermutationState, } impl Clone for Permutations where I: Clone + Iterator, I::Item: Clone, { clone_fields!(vals, state); } #[derive(Clone, Debug)] enum PermutationState { /// 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]>, }, /// No permutation left to generate. End, } impl fmt::Debug for Permutations where I: Iterator + fmt::Debug, I::Item: fmt::Debug, { debug_fmt_fields!(Permutations, vals, state); } pub fn permutations(iter: I, k: usize) -> Permutations { Permutations { vals: LazyBuffer::new(iter), state: PermutationState::Start { k }, } } impl Iterator for Permutations where I: Iterator, I::Item: Clone, { type Item = Vec; fn next(&mut self) -> Option { let Self { vals, state } = self; match state { PermutationState::Start { k: 0 } => { *state = PermutationState::End; Some(Vec::new()) } &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(vals.get_at(&indices[0..k])) } PermutationState::End => None, } } fn count(self) -> usize { let Self { vals, state } = self; let n = vals.count(); state.size_hint_for(n).1.unwrap() } 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 FusedIterator for Permutations where I: Iterator, I::Item: Clone, { } 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 } impl PermutationState { fn size_hint_for(&self, n: usize) -> SizeHint { // At the beginning, there are `n!/(n-k)!` items to come. let at_start = |n, k| { debug_assert!(n >= k); let total = (n - k + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i)); (total.unwrap_or(usize::MAX), total) }; match *self { 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)) }); (count.unwrap_or(usize::MAX), count) } Self::End => (0, Some(0)), } } } itertools-0.13.0/src/powerset.rs000064400000000000000000000063711046102023000147450ustar 00000000000000use alloc::vec::Vec; use std::fmt; use std::iter::FusedIterator; 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. /// /// See [`.powerset()`](crate::Itertools::powerset) for more /// information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Powerset { combs: Combinations, } impl Clone for Powerset where I: Clone + Iterator, I::Item: Clone, { clone_fields!(combs); } impl fmt::Debug for Powerset where I: Iterator + fmt::Debug, I::Item: fmt::Debug, { debug_fmt_fields!(Powerset, combs); } /// Create a new `Powerset` from a clonable iterator. pub fn powerset(src: I) -> Powerset where I: Iterator, I::Item: Clone, { Powerset { combs: combinations(src, 0), } } impl Powerset { /// Returns true if `k` has been incremented, false otherwise. fn increment_k(&mut self) -> bool { if self.combs.k() < self.combs.n() || self.combs.k() == 0 { self.combs.reset(self.combs.k() + 1); true } else { false } } } impl Iterator for Powerset where I: Iterator, I::Item: Clone, { type Item = Vec; fn next(&mut self) -> Option { if let Some(elt) = self.combs.next() { Some(elt) } else if self.increment_k() { self.combs.next() } else { None } } fn nth(&mut self, mut n: usize) -> Option { loop { match self.combs.try_nth(n) { Ok(item) => return Some(item), Err(steps) => { if !self.increment_k() { return None; } n -= steps; } } } } fn size_hint(&self) -> SizeHint { let k = self.combs.k(); // Total bounds for source iterator. let (n_min, n_max) = self.combs.src().size_hint(); let low = remaining_for(n_min, k).unwrap_or(usize::MAX); let upp = n_max.and_then(|n| remaining_for(n, k)); size_hint::add(self.combs.size_hint(), (low, upp)) } fn count(self) -> usize { let k = self.combs.k(); let (n, combs_count) = self.combs.n_and_count(); combs_count + remaining_for(n, k).unwrap() } fn fold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { let mut it = self.combs; if it.k() == 0 { init = it.by_ref().fold(init, &mut f); it.reset(1); } init = it.by_ref().fold(init, &mut f); // n is now known for sure because k >= 1 and all k-combinations have been generated. for k in it.k() + 1..=it.n() { it.reset(k); init = it.by_ref().fold(init, &mut f); } init } } impl FusedIterator for Powerset where I: Iterator, I::Item: Clone, { } fn remaining_for(n: usize, k: usize) -> Option { (k + 1..=n).try_fold(0usize, |sum, i| sum.checked_add(checked_binomial(n, i)?)) } itertools-0.13.0/src/process_results_impl.rs000064400000000000000000000053601046102023000173520ustar 00000000000000#[cfg(doc)] use crate::Itertools; /// An iterator that produces only the `T` values as long as the /// inner iterator produces `Ok(T)`. /// /// Used by [`process_results`](crate::process_results), see its docs /// for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Debug)] pub struct ProcessResults<'a, I, E: 'a> { error: &'a mut Result<(), E>, iter: I, } impl<'a, I, E> ProcessResults<'a, I, E> { #[inline(always)] fn next_body(&mut self, item: Option>) -> Option { match item { Some(Ok(x)) => Some(x), Some(Err(e)) => { *self.error = Err(e); None } None => None, } } } impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E> where I: Iterator>, { type Item = T; fn next(&mut self) -> Option { let item = self.iter.next(); self.next_body(item) } fn size_hint(&self) -> (usize, Option) { (0, self.iter.size_hint().1) } fn fold(mut self, init: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { let error = self.error; self.iter .try_fold(init, |acc, opt| match opt { Ok(x) => Ok(f(acc, x)), Err(e) => { *error = Err(e); Err(acc) } }) .unwrap_or_else(|e| e) } } impl<'a, I, T, E> DoubleEndedIterator for ProcessResults<'a, I, E> where I: Iterator>, I: DoubleEndedIterator, { fn next_back(&mut self) -> Option { let item = self.iter.next_back(); self.next_body(item) } fn rfold(mut self, init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { let error = self.error; self.iter .try_rfold(init, |acc, opt| match opt { Ok(x) => Ok(f(acc, x)), Err(e) => { *error = Err(e); Err(acc) } }) .unwrap_or_else(|e| e) } } /// “Lift” a function of the values of an iterator so that it can process /// an iterator of `Result` values instead. /// /// [`IntoIterator`] enabled version of [`Itertools::process_results`]. pub fn process_results(iterable: I, processor: F) -> Result where I: IntoIterator>, F: FnOnce(ProcessResults) -> R, { let iter = iterable.into_iter(); let mut error = Ok(()); let result = processor(ProcessResults { error: &mut error, iter, }); error.map(|_| result) } itertools-0.13.0/src/put_back_n_impl.rs000064400000000000000000000032521046102023000162160ustar 00000000000000use alloc::vec::Vec; use crate::size_hint; /// An iterator adaptor that allows putting multiple /// items in front of the iterator. /// /// Iterator element type is `I::Item`. #[derive(Debug, Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct PutBackN { top: Vec, iter: I, } /// Create an iterator where you can put back multiple values to the front /// of the iteration. /// /// Iterator element type is `I::Item`. pub fn put_back_n(iterable: I) -> PutBackN where I: IntoIterator, { PutBackN { top: Vec::new(), iter: iterable.into_iter(), } } impl PutBackN { /// Puts `x` in front of the iterator. /// /// The values are yielded in order of the most recently put back /// values first. /// /// ```rust /// use itertools::put_back_n; /// /// let mut it = put_back_n(1..5); /// it.next(); /// it.put_back(1); /// it.put_back(0); /// /// assert!(itertools::equal(it, 0..5)); /// ``` #[inline] pub fn put_back(&mut self, x: I::Item) { self.top.push(x); } } impl Iterator for PutBackN { type Item = I::Item; #[inline] fn next(&mut self) -> Option { self.top.pop().or_else(|| self.iter.next()) } #[inline] fn size_hint(&self) -> (usize, Option) { size_hint::add_scalar(self.iter.size_hint(), self.top.len()) } fn fold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { init = self.top.into_iter().rfold(init, &mut f); self.iter.fold(init, f) } } itertools-0.13.0/src/rciter_impl.rs000064400000000000000000000055011046102023000154000ustar 00000000000000use alloc::rc::Rc; use std::cell::RefCell; use std::iter::{FusedIterator, IntoIterator}; /// A wrapper for `Rc>`, that implements the `Iterator` trait. #[derive(Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct RcIter { /// The boxed iterator. pub rciter: Rc>, } /// Return an iterator inside a `Rc>` wrapper. /// /// The returned `RcIter` can be cloned, and each clone will refer back to the /// same original iterator. /// /// `RcIter` allows doing interesting things like using `.zip()` on an iterator with /// itself, at the cost of runtime borrow checking which may have a performance /// penalty. /// /// Iterator element type is `Self::Item`. /// /// ``` /// use itertools::rciter; /// use itertools::zip; /// /// // In this example a range iterator is created and we iterate it using /// // three separate handles (two of them given to zip). /// // We also use the IntoIterator implementation for `&RcIter`. /// /// let mut iter = rciter(0..9); /// let mut z = zip(&iter, &iter); /// /// assert_eq!(z.next(), Some((0, 1))); /// assert_eq!(z.next(), Some((2, 3))); /// assert_eq!(z.next(), Some((4, 5))); /// assert_eq!(iter.next(), Some(6)); /// assert_eq!(z.next(), Some((7, 8))); /// assert_eq!(z.next(), None); /// ``` /// /// **Panics** in iterator methods if a borrow error is encountered in the /// iterator methods. It can only happen if the `RcIter` is reentered in /// `.next()`, i.e. if it somehow participates in an “iterator knot” /// where it is an adaptor of itself. pub fn rciter(iterable: I) -> RcIter where I: IntoIterator, { RcIter { rciter: Rc::new(RefCell::new(iterable.into_iter())), } } impl Clone for RcIter { clone_fields!(rciter); } impl Iterator for RcIter where I: Iterator, { type Item = A; #[inline] fn next(&mut self) -> Option { self.rciter.borrow_mut().next() } #[inline] fn size_hint(&self) -> (usize, Option) { // To work sanely with other API that assume they own an iterator, // so it can't change in other places, we can't guarantee as much // in our size_hint. Other clones may drain values under our feet. (0, self.rciter.borrow().size_hint().1) } } impl DoubleEndedIterator for RcIter where I: DoubleEndedIterator, { #[inline] fn next_back(&mut self) -> Option { self.rciter.borrow_mut().next_back() } } /// Return an iterator from `&RcIter` (by simply cloning it). impl<'a, I> IntoIterator for &'a RcIter where I: Iterator, { type Item = I::Item; type IntoIter = RcIter; fn into_iter(self) -> RcIter { self.clone() } } impl FusedIterator for RcIter where I: FusedIterator {} itertools-0.13.0/src/repeatn.rs000064400000000000000000000033661046102023000145340ustar 00000000000000use std::iter::FusedIterator; /// An iterator that produces *n* repetitions of an element. /// /// See [`repeat_n()`](crate::repeat_n) for more information. #[must_use = "iterators are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] pub struct RepeatN { pub(crate) elt: Option, n: usize, } /// Create an iterator that produces `n` repetitions of `element`. pub fn repeat_n(element: A, n: usize) -> RepeatN where A: Clone, { if n == 0 { RepeatN { elt: None, n } } else { RepeatN { elt: Some(element), n, } } } impl Iterator for RepeatN where A: Clone, { type Item = A; fn next(&mut self) -> Option { if self.n > 1 { self.n -= 1; self.elt.as_ref().cloned() } else { self.n = 0; self.elt.take() } } fn size_hint(&self) -> (usize, Option) { (self.n, Some(self.n)) } fn fold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { match self { Self { elt: Some(elt), n } => { debug_assert!(n > 0); init = (1..n).map(|_| elt.clone()).fold(init, &mut f); f(init, elt) } _ => init, } } } impl DoubleEndedIterator for RepeatN where A: Clone, { #[inline] fn next_back(&mut self) -> Option { self.next() } #[inline] fn rfold(self, init: B, f: F) -> B where F: FnMut(B, Self::Item) -> B, { self.fold(init, f) } } impl ExactSizeIterator for RepeatN where A: Clone {} impl FusedIterator for RepeatN where A: Clone {} itertools-0.13.0/src/size_hint.rs000064400000000000000000000045171046102023000150710ustar 00000000000000//! Arithmetic on `Iterator.size_hint()` values. //! use std::cmp; /// `SizeHint` is the return type of `Iterator::size_hint()`. pub type SizeHint = (usize, Option); /// Add `SizeHint` correctly. #[inline] pub fn add(a: SizeHint, b: SizeHint) -> SizeHint { let min = a.0.saturating_add(b.0); let max = match (a.1, b.1) { (Some(x), Some(y)) => x.checked_add(y), _ => None, }; (min, max) } /// Add `x` correctly to a `SizeHint`. #[inline] pub fn add_scalar(sh: SizeHint, x: usize) -> SizeHint { let (mut low, mut hi) = sh; low = low.saturating_add(x); hi = hi.and_then(|elt| elt.checked_add(x)); (low, hi) } /// Subtract `x` correctly from a `SizeHint`. #[inline] pub fn sub_scalar(sh: SizeHint, x: usize) -> SizeHint { let (mut low, mut hi) = sh; low = low.saturating_sub(x); hi = hi.map(|elt| elt.saturating_sub(x)); (low, hi) } /// Multiply `SizeHint` correctly #[inline] pub fn mul(a: SizeHint, b: SizeHint) -> SizeHint { let low = a.0.saturating_mul(b.0); let hi = match (a.1, b.1) { (Some(x), Some(y)) => x.checked_mul(y), (Some(0), None) | (None, Some(0)) => Some(0), _ => None, }; (low, hi) } /// Multiply `x` correctly with a `SizeHint`. #[inline] pub fn mul_scalar(sh: SizeHint, x: usize) -> SizeHint { let (mut low, mut hi) = sh; low = low.saturating_mul(x); hi = hi.and_then(|elt| elt.checked_mul(x)); (low, hi) } /// Return the maximum #[inline] pub fn max(a: SizeHint, b: SizeHint) -> SizeHint { let (a_lower, a_upper) = a; let (b_lower, b_upper) = b; let lower = cmp::max(a_lower, b_lower); let upper = match (a_upper, b_upper) { (Some(x), Some(y)) => Some(cmp::max(x, y)), _ => None, }; (lower, upper) } /// Return the minimum #[inline] pub fn min(a: SizeHint, b: SizeHint) -> SizeHint { let (a_lower, a_upper) = a; let (b_lower, b_upper) = b; let lower = cmp::min(a_lower, b_lower); let upper = match (a_upper, b_upper) { (Some(u1), Some(u2)) => Some(cmp::min(u1, u2)), _ => a_upper.or(b_upper), }; (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))); } itertools-0.13.0/src/sources.rs000064400000000000000000000077361046102023000145660ustar 00000000000000//! Iterators that are sources (produce elements from parameters, //! not from another iterator). #![allow(deprecated)] use std::fmt; use std::mem; /// Creates a new unfold source with the specified closure as the "iterator /// function" and an initial state to eventually pass to the closure /// /// `unfold` is a general iterator builder: it has a mutable state value, /// and a closure with access to the state that produces the next value. /// /// This more or less equivalent to a regular struct with an [`Iterator`] /// implementation, and is useful for one-off iterators. /// /// ``` /// // an iterator that yields sequential Fibonacci numbers, /// // and stops at the maximum representable value. /// /// use itertools::unfold; /// /// let mut fibonacci = unfold((1u32, 1u32), |(x1, x2)| { /// // Attempt to get the next Fibonacci number /// let next = x1.saturating_add(*x2); /// /// // Shift left: ret <- x1 <- x2 <- next /// let ret = *x1; /// *x1 = *x2; /// *x2 = next; /// /// // If addition has saturated at the maximum, we are finished /// if ret == *x1 && ret > 1 { /// None /// } else { /// Some(ret) /// } /// }); /// /// itertools::assert_equal(fibonacci.by_ref().take(8), /// vec![1, 1, 2, 3, 5, 8, 13, 21]); /// assert_eq!(fibonacci.last(), Some(2_971_215_073)) /// ``` #[deprecated( note = "Use [std::iter::from_fn](https://doc.rust-lang.org/std/iter/fn.from_fn.html) instead", since = "0.13.0" )] pub fn unfold(initial_state: St, f: F) -> Unfold where F: FnMut(&mut St) -> Option, { Unfold { f, state: initial_state, } } impl fmt::Debug for Unfold where St: fmt::Debug, { debug_fmt_fields!(Unfold, state); } /// See [`unfold`](crate::unfold) for more information. #[derive(Clone)] #[must_use = "iterators are lazy and do nothing unless consumed"] #[deprecated( note = "Use [std::iter::FromFn](https://doc.rust-lang.org/std/iter/struct.FromFn.html) instead", since = "0.13.0" )] pub struct Unfold { f: F, /// Internal state that will be passed to the closure on the next iteration pub state: St, } impl Iterator for Unfold where F: FnMut(&mut St) -> Option, { type Item = A; #[inline] fn next(&mut self) -> Option { (self.f)(&mut self.state) } } /// An iterator that infinitely applies function to value and yields results. /// /// This `struct` is created by the [`iterate()`](crate::iterate) function. /// See its documentation for more. #[derive(Clone)] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct Iterate { state: St, f: F, } impl fmt::Debug for Iterate where St: fmt::Debug, { debug_fmt_fields!(Iterate, state); } impl Iterator for Iterate where F: FnMut(&St) -> St, { type Item = St; #[inline] fn next(&mut self) -> Option { let next_state = (self.f)(&self.state); Some(mem::replace(&mut self.state, next_state)) } #[inline] fn size_hint(&self) -> (usize, Option) { (usize::MAX, None) } } /// Creates a new iterator that infinitely applies function to value and yields results. /// /// ``` /// use itertools::iterate; /// /// itertools::assert_equal(iterate(1, |i| i % 3 + 1).take(5), vec![1, 2, 3, 1, 2]); /// ``` /// /// **Panics** if compute the next value does. /// /// ```should_panic /// # use itertools::iterate; /// let mut it = iterate(25u32, |x| x - 10).take_while(|&x| x > 10); /// assert_eq!(it.next(), Some(25)); // `Iterate` holds 15. /// assert_eq!(it.next(), Some(15)); // `Iterate` holds 5. /// it.next(); // `5 - 10` overflows. /// ``` /// /// You can alternatively use [`core::iter::successors`] as it better describes a finite iterator. pub fn iterate(initial_value: St, f: F) -> Iterate where F: FnMut(&St) -> St, { Iterate { state: initial_value, f, } } itertools-0.13.0/src/take_while_inclusive.rs000064400000000000000000000043721046102023000172710ustar 00000000000000use core::iter::FusedIterator; use std::fmt; /// An iterator adaptor that consumes elements while the given predicate is /// `true`, including the element for which the predicate first returned /// `false`. /// /// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive) /// for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Clone)] pub struct TakeWhileInclusive { iter: I, predicate: F, done: bool, } impl TakeWhileInclusive where I: Iterator, F: FnMut(&I::Item) -> bool, { /// Create a new [`TakeWhileInclusive`] from an iterator and a predicate. pub(crate) fn new(iter: I, predicate: F) -> Self { Self { iter, predicate, done: false, } } } impl fmt::Debug for TakeWhileInclusive where I: Iterator + fmt::Debug, { debug_fmt_fields!(TakeWhileInclusive, iter, done); } impl Iterator for TakeWhileInclusive where I: Iterator, F: FnMut(&I::Item) -> bool, { type Item = I::Item; fn next(&mut self) -> Option { if self.done { None } else { self.iter.next().map(|item| { if !(self.predicate)(&item) { self.done = true; } item }) } } fn size_hint(&self) -> (usize, Option) { if self.done { (0, Some(0)) } else { (0, self.iter.size_hint().1) } } fn fold(mut self, init: B, mut f: Fold) -> B where Fold: FnMut(B, Self::Item) -> B, { if self.done { init } else { let predicate = &mut self.predicate; self.iter .try_fold(init, |mut acc, item| { let is_ok = predicate(&item); acc = f(acc, item); if is_ok { Ok(acc) } else { Err(acc) } }) .unwrap_or_else(|err| err) } } } impl FusedIterator for TakeWhileInclusive where I: Iterator, F: FnMut(&I::Item) -> bool, { } itertools-0.13.0/src/tee.rs000064400000000000000000000042421046102023000136450ustar 00000000000000use super::size_hint; use alloc::collections::VecDeque; use alloc::rc::Rc; use std::cell::RefCell; /// Common buffer object for the two tee halves #[derive(Debug)] struct TeeBuffer { backlog: VecDeque, iter: I, /// The owner field indicates which id should read from the backlog owner: bool, } /// One half of an iterator pair where both return the same elements. /// /// See [`.tee()`](crate::Itertools::tee) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Debug)] pub struct Tee where I: Iterator, { rcbuffer: Rc>>, id: bool, } pub fn new(iter: I) -> (Tee, Tee) 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, }; (t1, t2) } impl Iterator for Tee where I: Iterator, I::Item: Clone, { type Item = I::Item; fn next(&mut self) -> Option { // .borrow_mut may fail here -- but only if the user has tied some kind of weird // knot where the iterator refers back to itself. let mut buffer = self.rcbuffer.borrow_mut(); if buffer.owner == self.id { match buffer.backlog.pop_front() { None => {} some_elt => return some_elt, } } match buffer.iter.next() { None => None, Some(elt) => { buffer.backlog.push_back(elt.clone()); buffer.owner = !self.id; Some(elt) } } } fn size_hint(&self) -> (usize, Option) { let buffer = self.rcbuffer.borrow(); let sh = buffer.iter.size_hint(); if buffer.owner == self.id { let log_len = buffer.backlog.len(); size_hint::add_scalar(sh, log_len) } else { sh } } } impl ExactSizeIterator for Tee where I: ExactSizeIterator, I::Item: Clone, { } itertools-0.13.0/src/tuple_impl.rs000064400000000000000000000251661046102023000152520ustar 00000000000000//! Some iterator that produces tuples use std::iter::Cycle; use std::iter::Fuse; use std::iter::FusedIterator; use crate::size_hint; // `HomogeneousTuple` is a public facade for `TupleCollect`, allowing // tuple-related methods to be used by clients in generic contexts, while // hiding the implementation details of `TupleCollect`. // See https://github.com/rust-itertools/itertools/issues/387 /// Implemented for homogeneous tuples of size up to 12. pub trait HomogeneousTuple: TupleCollect {} impl HomogeneousTuple for T {} /// An iterator over a incomplete tuple. /// /// See [`.tuples()`](crate::Itertools::tuples) and /// [`Tuples::into_buffer()`]. #[derive(Clone, Debug)] pub struct TupleBuffer where T: HomogeneousTuple, { cur: usize, buf: T::Buffer, } impl TupleBuffer where T: HomogeneousTuple, { fn new(buf: T::Buffer) -> Self { Self { cur: 0, buf } } } impl Iterator for TupleBuffer where T: HomogeneousTuple, { type Item = T::Item; fn next(&mut self) -> Option { let s = self.buf.as_mut(); if let Some(ref mut item) = s.get_mut(self.cur) { self.cur += 1; item.take() } else { None } } fn size_hint(&self) -> (usize, Option) { let buffer = &self.buf.as_ref()[self.cur..]; let len = if buffer.is_empty() { 0 } else { buffer .iter() .position(|x| x.is_none()) .unwrap_or(buffer.len()) }; (len, Some(len)) } } impl ExactSizeIterator for TupleBuffer where T: HomogeneousTuple {} /// An iterator that groups the items in tuples of a specific size. /// /// See [`.tuples()`](crate::Itertools::tuples) for more information. #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Tuples where I: Iterator, T: HomogeneousTuple, { iter: Fuse, buf: T::Buffer, } /// Create a new tuples iterator. pub fn tuples(iter: I) -> Tuples where I: Iterator, T: HomogeneousTuple, { Tuples { iter: iter.fuse(), buf: Default::default(), } } impl Iterator for Tuples where I: Iterator, T: HomogeneousTuple, { type Item = T; fn next(&mut self) -> Option { T::collect_from_iter(&mut self.iter, &mut self.buf) } fn size_hint(&self) -> (usize, Option) { // The number of elts we've drawn from the underlying iterator, but have // not yet produced as a tuple. let buffered = T::buffer_len(&self.buf); // To that, we must add the size estimates of the underlying iterator. let (unbuffered_lo, unbuffered_hi) = self.iter.size_hint(); // The total low estimate is the sum of the already-buffered elements, // plus the low estimate of remaining unbuffered elements, divided by // the tuple size. let total_lo = add_then_div(unbuffered_lo, buffered, T::num_items()).unwrap_or(usize::MAX); // And likewise for the total high estimate, but using the high estimate // of the remaining unbuffered elements. let total_hi = unbuffered_hi.and_then(|hi| add_then_div(hi, buffered, T::num_items())); (total_lo, total_hi) } } /// `(n + a) / d` avoiding overflow when possible, returns `None` if it overflows. fn add_then_div(n: usize, a: usize, d: usize) -> Option { debug_assert_ne!(d, 0); (n / d).checked_add(a / d)?.checked_add((n % d + a % d) / d) } impl ExactSizeIterator for Tuples where I: ExactSizeIterator, T: HomogeneousTuple, { } impl Tuples where I: Iterator, T: HomogeneousTuple, { /// Return a buffer with the produced items that was not enough to be grouped in a tuple. /// /// ``` /// use itertools::Itertools; /// /// let mut iter = (0..5).tuples(); /// assert_eq!(Some((0, 1, 2)), iter.next()); /// assert_eq!(None, iter.next()); /// itertools::assert_equal(vec![3, 4], iter.into_buffer()); /// ``` pub fn into_buffer(self) -> TupleBuffer { TupleBuffer::new(self.buf) } } /// An iterator over all contiguous windows that produces tuples of a specific size. /// /// See [`.tuple_windows()`](crate::Itertools::tuple_windows) for more /// information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] pub struct TupleWindows where I: Iterator, T: HomogeneousTuple, { iter: I, last: Option, } /// Create a new tuple windows iterator. pub fn tuple_windows(iter: I) -> TupleWindows where I: Iterator, T: HomogeneousTuple, T::Item: Clone, { TupleWindows { last: None, iter } } impl Iterator for TupleWindows where I: Iterator, T: HomogeneousTuple + Clone, T::Item: Clone, { type Item = T; fn next(&mut self) -> Option { if T::num_items() == 1 { return T::collect_from_iter_no_buf(&mut self.iter); } if let Some(new) = self.iter.next() { if let Some(ref mut last) = self.last { last.left_shift_push(new); 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 } } fn size_hint(&self) -> (usize, Option) { let mut sh = self.iter.size_hint(); // Adjust the size hint at the beginning // OR when `num_items == 1` (but it does not change the size hint). if self.last.is_none() { sh = size_hint::sub_scalar(sh, T::num_items() - 1); } sh } } impl ExactSizeIterator for TupleWindows where I: ExactSizeIterator, T: HomogeneousTuple + Clone, T::Item: Clone, { } impl FusedIterator for TupleWindows where I: FusedIterator, T: HomogeneousTuple + Clone, T::Item: Clone, { } /// An iterator over all windows, wrapping back to the first elements when the /// window would otherwise exceed the length of the iterator, producing tuples /// of a specific size. /// /// See [`.circular_tuple_windows()`](crate::Itertools::circular_tuple_windows) for more /// information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Debug, Clone)] pub struct CircularTupleWindows where I: Iterator + Clone, T: TupleCollect + Clone, { iter: TupleWindows, T>, len: usize, } pub fn circular_tuple_windows(iter: I) -> CircularTupleWindows where I: Iterator + Clone + ExactSizeIterator, T: TupleCollect + Clone, T::Item: Clone, { let len = iter.len(); let iter = tuple_windows(iter.cycle()); CircularTupleWindows { iter, len } } impl Iterator for CircularTupleWindows where I: Iterator + Clone, T: TupleCollect + Clone, T::Item: Clone, { type Item = T; fn next(&mut self) -> Option { if self.len != 0 { self.len -= 1; self.iter.next() } else { None } } fn size_hint(&self) -> (usize, Option) { (self.len, Some(self.len)) } } impl ExactSizeIterator for CircularTupleWindows where I: Iterator + Clone, T: TupleCollect + Clone, T::Item: Clone, { } impl FusedIterator for CircularTupleWindows where I: Iterator + Clone, T: TupleCollect + Clone, T::Item: Clone, { } pub trait TupleCollect: Sized { type Item; type Buffer: Default + AsRef<[Option]> + AsMut<[Option]>; fn buffer_len(buf: &Self::Buffer) -> usize { let s = buf.as_ref(); s.iter().position(Option::is_none).unwrap_or(s.len()) } fn collect_from_iter(iter: I, buf: &mut Self::Buffer) -> Option where I: IntoIterator; fn collect_from_iter_no_buf(iter: I) -> Option where I: IntoIterator; fn num_items() -> usize; fn left_shift_push(&mut self, item: Self::Item); } macro_rules! rev_for_each_ident{ ($m:ident, ) => {}; ($m:ident, $i0:ident, $($i:ident,)*) => { rev_for_each_ident!($m, $($i,)*); $m!($i0); }; } macro_rules! impl_tuple_collect { ($dummy:ident,) => {}; // stop ($dummy:ident, $($Y:ident,)*) => ( impl_tuple_collect!($($Y,)*); impl TupleCollect for ($(ignore_ident!($Y, A),)*) { type Item = A; type Buffer = [Option; count_ident!($($Y)*) - 1]; #[allow(unused_assignments, unused_mut)] fn collect_from_iter(iter: I, buf: &mut Self::Buffer) -> Option where I: IntoIterator { let mut iter = iter.into_iter(); $( let mut $Y = None; )* loop { $( $Y = iter.next(); if $Y.is_none() { break } )* return Some(($($Y.unwrap()),*,)) } let mut i = 0; let mut s = buf.as_mut(); $( if i < s.len() { s[i] = $Y; i += 1; } )* return None; } fn collect_from_iter_no_buf(iter: I) -> Option where I: IntoIterator { let mut iter = iter.into_iter(); Some(($( { let $Y = iter.next()?; $Y }, )*)) } fn num_items() -> usize { count_ident!($($Y)*) } fn left_shift_push(&mut self, mut item: A) { use std::mem::replace; let &mut ($(ref mut $Y),*,) = self; macro_rules! replace_item{($i:ident) => { item = replace($i, item); }} rev_for_each_ident!(replace_item, $($Y,)*); drop(item); } } ) } impl_tuple_collect!(dummy, a, b, c, d, e, f, g, h, i, j, k, l,); itertools-0.13.0/src/unique_impl.rs000064400000000000000000000107021046102023000154150ustar 00000000000000use std::collections::hash_map::Entry; use std::collections::HashMap; use std::fmt; use std::hash::Hash; use std::iter::FusedIterator; /// An iterator adapter to filter out duplicate elements. /// /// See [`.unique_by()`](crate::Itertools::unique) for more information. #[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct UniqueBy { iter: I, // Use a Hashmap for the Entry API in order to prevent hashing twice. // This can maybe be replaced with a HashSet once `get_or_insert_with` // or a proper Entry API for Hashset is stable and meets this msrv used: HashMap, f: F, } impl fmt::Debug for UniqueBy where I: Iterator + fmt::Debug, V: fmt::Debug + Hash + Eq, { debug_fmt_fields!(UniqueBy, iter, used); } /// Create a new `UniqueBy` iterator. pub fn unique_by(iter: I, f: F) -> UniqueBy where V: Eq + Hash, F: FnMut(&I::Item) -> V, I: Iterator, { UniqueBy { iter, used: HashMap::new(), f, } } // count the number of new unique keys in iterable (`used` is the set already seen) fn count_new_keys(mut used: HashMap, iterable: I) -> usize where I: IntoIterator, K: Hash + Eq, { let iter = iterable.into_iter(); let current_used = used.len(); used.extend(iter.map(|key| (key, ()))); used.len() - current_used } impl Iterator for UniqueBy where I: Iterator, V: Eq + Hash, F: FnMut(&I::Item) -> V, { type Item = I::Item; fn next(&mut self) -> Option { let Self { iter, used, f } = self; iter.find(|v| used.insert(f(v), ()).is_none()) } #[inline] fn size_hint(&self) -> (usize, Option) { let (low, hi) = self.iter.size_hint(); ((low > 0 && self.used.is_empty()) as usize, hi) } fn count(self) -> usize { let mut key_f = self.f; count_new_keys(self.used, self.iter.map(move |elt| key_f(&elt))) } } impl DoubleEndedIterator for UniqueBy where I: DoubleEndedIterator, V: Eq + Hash, F: FnMut(&I::Item) -> V, { fn next_back(&mut self) -> Option { let Self { iter, used, f } = self; iter.rfind(|v| used.insert(f(v), ()).is_none()) } } impl FusedIterator for UniqueBy where I: FusedIterator, V: Eq + Hash, F: FnMut(&I::Item) -> V, { } impl Iterator for Unique where I: Iterator, I::Item: Eq + Hash + Clone, { type Item = I::Item; fn next(&mut self) -> Option { 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 }) } #[inline] fn size_hint(&self) -> (usize, Option) { let (low, hi) = self.iter.iter.size_hint(); ((low > 0 && self.iter.used.is_empty()) as usize, hi) } fn count(self) -> usize { count_new_keys(self.iter.used, self.iter.iter) } } impl DoubleEndedIterator for Unique where I: DoubleEndedIterator, I::Item: Eq + Hash + Clone, { fn next_back(&mut self) -> Option { 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 }) } } impl FusedIterator for Unique 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 where I: Iterator, I::Item: Eq + Hash + Clone, { iter: UniqueBy, } impl fmt::Debug for Unique where I: Iterator + fmt::Debug, I::Item: Hash + Eq + fmt::Debug + Clone, { debug_fmt_fields!(Unique, iter); } pub fn unique(iter: I) -> Unique where I: Iterator, I::Item: Eq + Hash + Clone, { Unique { iter: UniqueBy { iter, used: HashMap::new(), f: (), }, } } itertools-0.13.0/src/unziptuple.rs000064400000000000000000000067331046102023000153160ustar 00000000000000/// Converts an iterator of tuples into a tuple of containers. /// /// `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`]. /// /// ``` /// use itertools::multiunzip; /// /// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; /// /// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(inputs); /// /// assert_eq!(a, vec![1, 4, 7]); /// assert_eq!(b, vec![2, 5, 8]); /// assert_eq!(c, vec![3, 6, 9]); /// ``` /// /// [`multizip`]: crate::multizip pub fn multiunzip(i: I) -> FromI where I: IntoIterator, I::IntoIter: MultiUnzip, { i.into_iter().multiunzip() } /// An iterator that can be unzipped into multiple collections. /// /// See [`.multiunzip()`](crate::Itertools::multiunzip) for more information. pub trait MultiUnzip: Iterator { /// Unzip this iterator into multiple collections. fn multiunzip(self) -> FromI; } macro_rules! impl_unzip_iter { ($($T:ident => $FromT:ident),*) => ( #[allow(non_snake_case)] impl, $($T, $FromT: Default + Extend<$T>),* > MultiUnzip<($($FromT,)*)> for IT { fn multiunzip(self) -> ($($FromT,)*) { // This implementation mirrors the logic of Iterator::unzip resp. Extend for (A, B) as close as possible. // Unfortunately a lot of the used api there is still unstable (https://github.com/rust-lang/rust/issues/72631). // // Iterator::unzip: https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#2825-2865 // Extend for (A, B): https://doc.rust-lang.org/src/core/iter/traits/collect.rs.html#370-411 let mut res = ($($FromT::default(),)*); let ($($FromT,)*) = &mut res; // Still unstable #72631 // let (lower_bound, _) = self.size_hint(); // if lower_bound > 0 { // $($FromT.extend_reserve(lower_bound);)* // } self.fold((), |(), ($($T,)*)| { // Still unstable #72631 // $( $FromT.extend_one($T); )* $( $FromT.extend(std::iter::once($T)); )* }); res } } ); } impl_unzip_iter!(); impl_unzip_iter!(A => FromA); impl_unzip_iter!(A => FromA, B => FromB); impl_unzip_iter!(A => FromA, B => FromB, C => FromC); impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD); impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE); impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF); impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG); impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH); impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI); impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ); impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK); impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK, L => FromL); itertools-0.13.0/src/with_position.rs000064400000000000000000000075041046102023000157730ustar 00000000000000use 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)`. /// /// See [`.with_position()`](crate::Itertools::with_position) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct WithPosition where I: Iterator, { handled_first: bool, peekable: Peekable>, } impl fmt::Debug for WithPosition where I: Iterator, Peekable>: fmt::Debug, { debug_fmt_fields!(WithPosition, handled_first, peekable); } impl Clone for WithPosition where I: Clone + Iterator, I::Item: Clone, { clone_fields!(handled_first, peekable); } /// Create a new `WithPosition` iterator. pub fn with_position(iter: I) -> WithPosition where I: Iterator, { WithPosition { handled_first: false, peekable: iter.fuse().peekable(), } } /// 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, Eq)] pub enum Position { /// This is the first element. First, /// This is neither the first nor the last element. Middle, /// This is the last element. Last, /// This is the only element. Only, } impl Iterator for WithPosition { type Item = (Position, I::Item); fn next(&mut self) -> Option { match self.peekable.next() { Some(item) => { if !self.handled_first { // Haven't seen the first item yet, and there is one to give. self.handled_first = true; // Peek to see if this is also the last item, // in which case tag it as `Only`. match self.peekable.peek() { Some(_) => Some((Position::First, item)), None => Some((Position::Only, item)), } } else { // Have seen the first item, and there's something left. // Peek to see if this is the last item. match self.peekable.peek() { Some(_) => Some((Position::Middle, item)), None => Some((Position::Last, item)), } } } // Iterator is finished. None => None, } } fn size_hint(&self) -> (usize, Option) { self.peekable.size_hint() } fn fold(mut self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { if let Some(mut head) = self.peekable.next() { if !self.handled_first { // The current head is `First` or `Only`, // it depends if there is another item or not. match self.peekable.next() { Some(second) => { let first = std::mem::replace(&mut head, second); init = f(init, (Position::First, first)); } None => return f(init, (Position::Only, head)), } } // Have seen the first item, and there's something left. init = self.peekable.fold(init, |acc, mut item| { std::mem::swap(&mut head, &mut item); f(acc, (Position::Middle, item)) }); // The "head" is now the last item. init = f(init, (Position::Last, head)); } init } } impl ExactSizeIterator for WithPosition where I: ExactSizeIterator {} impl FusedIterator for WithPosition {} itertools-0.13.0/src/zip_eq_impl.rs000064400000000000000000000030611046102023000153760ustar 00000000000000use 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)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct ZipEq { a: I, b: J, } /// Zips two iterators but **panics** if they are not of the same length. /// /// [`IntoIterator`] enabled version of [`Itertools::zip_eq`](crate::Itertools::zip_eq). /// /// ``` /// use itertools::zip_eq; /// /// let data = [1, 2, 3, 4, 5]; /// for (a, b) in zip_eq(&data[..data.len() - 1], &data[1..]) { /// /* loop body */ /// } /// ``` pub fn zip_eq(i: I, j: J) -> ZipEq where I: IntoIterator, J: IntoIterator, { ZipEq { a: i.into_iter(), b: j.into_iter(), } } impl Iterator for ZipEq where I: Iterator, J: Iterator, { type Item = (I::Item, J::Item); fn next(&mut self) -> Option { 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") } } } fn size_hint(&self) -> (usize, Option) { size_hint::min(self.a.size_hint(), self.b.size_hint()) } } impl ExactSizeIterator for ZipEq where I: ExactSizeIterator, J: ExactSizeIterator, { } itertools-0.13.0/src/zip_longest.rs000064400000000000000000000075631046102023000154360ustar 00000000000000use super::size_hint; use std::cmp::Ordering::{Equal, Greater, Less}; use std::iter::{Fuse, FusedIterator}; use crate::either_or_both::EitherOrBoth; // ZipLongest originally written by SimonSapin, // 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*. /// /// See [`.zip_longest()`](crate::Itertools::zip_longest) for more information. #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct ZipLongest { a: Fuse, b: Fuse, } /// Create a new `ZipLongest` iterator. pub fn zip_longest(a: T, b: U) -> ZipLongest where T: Iterator, U: Iterator, { ZipLongest { a: a.fuse(), b: b.fuse(), } } impl Iterator for ZipLongest where T: Iterator, U: Iterator, { type Item = EitherOrBoth; #[inline] fn next(&mut self) -> Option { match (self.a.next(), self.b.next()) { (None, None) => None, (Some(a), None) => Some(EitherOrBoth::Left(a)), (None, Some(b)) => Some(EitherOrBoth::Right(b)), (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)), } } #[inline] fn size_hint(&self) -> (usize, Option) { size_hint::max(self.a.size_hint(), self.b.size_hint()) } #[inline] fn fold(self, init: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { let Self { mut a, mut b } = self; let res = a.try_fold(init, |init, a| match b.next() { Some(b) => Ok(f(init, EitherOrBoth::Both(a, b))), None => Err(f(init, EitherOrBoth::Left(a))), }); match res { Ok(acc) => b.map(EitherOrBoth::Right).fold(acc, f), Err(acc) => a.map(EitherOrBoth::Left).fold(acc, f), } } } impl DoubleEndedIterator for ZipLongest where T: DoubleEndedIterator + ExactSizeIterator, U: DoubleEndedIterator + ExactSizeIterator, { #[inline] fn next_back(&mut self) -> Option { match self.a.len().cmp(&self.b.len()) { Equal => match (self.a.next_back(), self.b.next_back()) { (None, None) => None, (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)), // These can only happen if .len() is inconsistent with .next_back() (Some(a), None) => Some(EitherOrBoth::Left(a)), (None, Some(b)) => Some(EitherOrBoth::Right(b)), }, Greater => self.a.next_back().map(EitherOrBoth::Left), Less => self.b.next_back().map(EitherOrBoth::Right), } } fn rfold(self, mut init: B, mut f: F) -> B where F: FnMut(B, Self::Item) -> B, { let Self { mut a, mut b } = self; let a_len = a.len(); let b_len = b.len(); match a_len.cmp(&b_len) { Equal => {} Greater => { init = a .by_ref() .rev() .take(a_len - b_len) .map(EitherOrBoth::Left) .fold(init, &mut f) } Less => { init = b .by_ref() .rev() .take(b_len - a_len) .map(EitherOrBoth::Right) .fold(init, &mut f) } } a.rfold(init, |acc, item_a| { f(acc, EitherOrBoth::Both(item_a, b.next_back().unwrap())) }) } } impl ExactSizeIterator for ZipLongest where T: ExactSizeIterator, U: ExactSizeIterator, { } impl FusedIterator for ZipLongest where T: Iterator, U: Iterator, { } itertools-0.13.0/src/ziptuple.rs000064400000000000000000000101731046102023000147440ustar 00000000000000use super::size_hint; /// See [`multizip`] for more information. #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Zip { t: T, } /// 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 /// until any of the subiterators yields `None`. /// /// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to `E` are the /// element types of the subiterator. /// /// **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!()`](crate::izip) over `multizip` for the performance benefits of using the /// standard library `.zip()`. Prefer `multizip` if a nameable type is needed. /// /// ``` /// use itertools::multizip; /// /// // iterate over three sequences side-by-side /// let mut results = [0, 0, 0, 0]; /// let inputs = [3, 7, 9, 6]; /// /// for (r, index, input) in multizip((&mut results, 0..10, &inputs)) { /// *r = index * 10 + input; /// } /// /// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]); /// ``` pub fn multizip(t: U) -> Zip where Zip: From + Iterator, { Zip::from(t) } macro_rules! impl_zip_iter { ($($B:ident),*) => ( #[allow(non_snake_case)] impl<$($B: IntoIterator),*> From<($($B,)*)> for Zip<($($B::IntoIter,)*)> { fn from(t: ($($B,)*)) -> Self { let ($($B,)*) = t; Zip { t: ($($B.into_iter(),)*) } } } #[allow(non_snake_case)] #[allow(unused_assignments)] impl<$($B),*> Iterator for Zip<($($B,)*)> where $( $B: Iterator, )* { type Item = ($($B::Item,)*); fn next(&mut self) -> Option { let ($(ref mut $B,)*) = self.t; // NOTE: Just like iter::Zip, we check the iterators // for None in order. We may finish unevenly (some // iterators gave n + 1 elements, some only n). $( let $B = match $B.next() { None => return None, Some(elt) => elt }; )* Some(($($B,)*)) } fn size_hint(&self) -> (usize, Option) { let sh = (usize::MAX, None); let ($(ref $B,)*) = self.t; $( let sh = size_hint::min($B.size_hint(), sh); )* sh } } #[allow(non_snake_case)] impl<$($B),*> ExactSizeIterator for Zip<($($B,)*)> where $( $B: ExactSizeIterator, )* { } #[allow(non_snake_case)] impl<$($B),*> DoubleEndedIterator for Zip<($($B,)*)> where $( $B: DoubleEndedIterator + ExactSizeIterator, )* { #[inline] fn next_back(&mut self) -> Option { let ($(ref mut $B,)*) = self.t; let size = *[$( $B.len(), )*].iter().min().unwrap(); $( if $B.len() != size { for _ in 0..$B.len() - size { $B.next_back(); } } )* match ($($B.next_back(),)*) { ($(Some($B),)*) => Some(($($B,)*)), _ => None, } } } ); } impl_zip_iter!(A); impl_zip_iter!(A, B); impl_zip_iter!(A, B, C); impl_zip_iter!(A, B, C, D); impl_zip_iter!(A, B, C, D, E); impl_zip_iter!(A, B, C, D, E, F); impl_zip_iter!(A, B, C, D, E, F, G); impl_zip_iter!(A, B, C, D, E, F, G, H); impl_zip_iter!(A, B, C, D, E, F, G, H, I); impl_zip_iter!(A, B, C, D, E, F, G, H, I, J); impl_zip_iter!(A, B, C, D, E, F, G, H, I, J, K); impl_zip_iter!(A, B, C, D, E, F, G, H, I, J, K, L); itertools-0.13.0/tests/adaptors_no_collect.rs000064400000000000000000000017221046102023000174610ustar 00000000000000use itertools::Itertools; struct PanickingCounter { curr: usize, max: usize, } impl Iterator for PanickingCounter { type Item = (); fn next(&mut self) -> Option { self.curr += 1; assert_ne!( self.curr, self.max, "Input iterator reached maximum of {} suggesting collection by adaptor", self.max ); Some(()) } } fn no_collect_test(to_adaptor: T) where A: Iterator, T: Fn(PanickingCounter) -> A, { let counter = PanickingCounter { curr: 0, max: 10_000, }; let adaptor = to_adaptor(counter); for _ in adaptor.take(5) {} } #[test] fn permutations_no_collect() { no_collect_test(|iter| iter.permutations(5)) } #[test] fn combinations_no_collect() { no_collect_test(|iter| iter.combinations(5)) } #[test] fn combinations_with_replacement_no_collect() { no_collect_test(|iter| iter.combinations_with_replacement(5)) } itertools-0.13.0/tests/flatten_ok.rs000064400000000000000000000030451046102023000155710ustar 00000000000000use itertools::{assert_equal, Itertools}; use std::{ops::Range, vec::IntoIter}; fn mix_data() -> IntoIter, bool>> { vec![Ok(0..2), Err(false), Ok(2..4), Err(true), Ok(4..6)].into_iter() } fn ok_data() -> IntoIter, bool>> { vec![Ok(0..2), Ok(2..4), Ok(4..6)].into_iter() } #[test] fn flatten_ok_mixed_expected_forward() { assert_equal( mix_data().flatten_ok(), vec![ Ok(0), Ok(1), Err(false), Ok(2), Ok(3), Err(true), Ok(4), Ok(5), ], ); } #[test] fn flatten_ok_mixed_expected_reverse() { assert_equal( mix_data().flatten_ok().rev(), vec![ Ok(5), Ok(4), Err(true), Ok(3), Ok(2), Err(false), Ok(1), Ok(0), ], ); } #[test] fn flatten_ok_collect_mixed_forward() { assert_eq!( mix_data().flatten_ok().collect::, _>>(), Err(false) ); } #[test] fn flatten_ok_collect_mixed_reverse() { assert_eq!( mix_data().flatten_ok().rev().collect::, _>>(), Err(true) ); } #[test] fn flatten_ok_collect_ok_forward() { assert_eq!( ok_data().flatten_ok().collect::, _>>(), Ok((0..6).collect()) ); } #[test] fn flatten_ok_collect_ok_reverse() { assert_eq!( ok_data().flatten_ok().rev().collect::, _>>(), Ok((0..6).rev().collect()) ); } itertools-0.13.0/tests/laziness.rs000064400000000000000000000175721046102023000153050ustar 00000000000000#![allow(unstable_name_collisions)] use itertools::Itertools; #[derive(Debug, Clone)] #[must_use = "iterators are lazy and do nothing unless consumed"] struct Panicking; impl Iterator for Panicking { type Item = u8; fn next(&mut self) -> Option { panic!("iterator adaptor is not lazy") } fn size_hint(&self) -> (usize, Option) { (0, Some(0)) } } impl ExactSizeIterator for Panicking {} /// ## Usage example /// ```compile_fail /// must_use_tests! { /// name { /// Panicking.name(); // Add `let _ =` only if required (encountered error). /// } /// // ... /// } /// ``` /// /// **TODO:** test missing `must_use` attributes better, maybe with a new lint. macro_rules! must_use_tests { ($($(#[$attr:meta])* $name:ident $body:block)*) => { $( /// `#[deny(unused_must_use)]` should force us to ignore the resulting iterators /// by adding `let _ = ...;` on every iterator. /// If it does not, then a `must_use` attribute is missing on the associated struct. /// /// However, it's only helpful if we don't add `let _ =` before seeing if there is an error or not. /// And it does not protect us against removed `must_use` attributes. /// There is no simple way to test this yet. #[deny(unused_must_use)] #[test] $(#[$attr])* fn $name() $body )* }; } must_use_tests! { // Itertools trait: interleave { let _ = Panicking.interleave(Panicking); } interleave_shortest { let _ = Panicking.interleave_shortest(Panicking); } intersperse { let _ = Panicking.intersperse(0); } intersperse_with { let _ = Panicking.intersperse_with(|| 0); } get { let _ = Panicking.get(1..4); let _ = Panicking.get(1..=4); let _ = Panicking.get(1..); let _ = Panicking.get(..4); let _ = Panicking.get(..=4); let _ = Panicking.get(..); } zip_longest { let _ = Panicking.zip_longest(Panicking); } zip_eq { let _ = Panicking.zip_eq(Panicking); } batching { let _ = Panicking.batching(Iterator::next); } chunk_by { // ChunkBy let _ = Panicking.chunk_by(|x| *x); // Groups let _ = Panicking.chunk_by(|x| *x).into_iter(); } chunks { // IntoChunks let _ = Panicking.chunks(1); let _ = Panicking.chunks(2); // Chunks let _ = Panicking.chunks(1).into_iter(); let _ = Panicking.chunks(2).into_iter(); } tuple_windows { let _ = Panicking.tuple_windows::<(_,)>(); let _ = Panicking.tuple_windows::<(_, _)>(); let _ = Panicking.tuple_windows::<(_, _, _)>(); } circular_tuple_windows { let _ = Panicking.circular_tuple_windows::<(_,)>(); let _ = Panicking.circular_tuple_windows::<(_, _)>(); let _ = Panicking.circular_tuple_windows::<(_, _, _)>(); } tuples { let _ = Panicking.tuples::<(_,)>(); let _ = Panicking.tuples::<(_, _)>(); let _ = Panicking.tuples::<(_, _, _)>(); } tee { let _ = Panicking.tee(); } map_into { let _ = Panicking.map_into::(); } map_ok { let _ = Panicking.map(Ok::).map_ok(|x| x + 1); } filter_ok { let _ = Panicking.map(Ok::).filter_ok(|x| x % 2 == 0); } filter_map_ok { let _ = Panicking.map(Ok::).filter_map_ok(|x| { if x % 2 == 0 { Some(x + 1) } else { None } }); } flatten_ok { let _ = Panicking.map(|x| Ok::<_, ()>([x])).flatten_ok(); } merge { let _ = Panicking.merge(Panicking); } merge_by { let _ = Panicking.merge_by(Panicking, |_, _| true); } merge_join_by { let _ = Panicking.merge_join_by(Panicking, |_, _| true); let _ = Panicking.merge_join_by(Panicking, Ord::cmp); } #[should_panic] kmerge { let _ = Panicking.map(|_| Panicking).kmerge(); } #[should_panic] kmerge_by { let _ = Panicking.map(|_| Panicking).kmerge_by(|_, _| true); } cartesian_product { let _ = Panicking.cartesian_product(Panicking); } multi_cartesian_product { let _ = vec![Panicking, Panicking, Panicking].into_iter().multi_cartesian_product(); } coalesce { let _ = Panicking.coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) }); } dedup { let _ = Panicking.dedup(); } dedup_by { let _ = Panicking.dedup_by(|_, _| true); } dedup_with_count { let _ = Panicking.dedup_with_count(); } dedup_by_with_count { let _ = Panicking.dedup_by_with_count(|_, _| true); } duplicates { let _ = Panicking.duplicates(); } duplicates_by { let _ = Panicking.duplicates_by(|x| *x); } unique { let _ = Panicking.unique(); } unique_by { let _ = Panicking.unique_by(|x| *x); } peeking_take_while { let _ = Panicking.peekable().peeking_take_while(|x| x % 2 == 0); } take_while_ref { let _ = Panicking.take_while_ref(|x| x % 2 == 0); } take_while_inclusive { let _ = Panicking.take_while_inclusive(|x| x % 2 == 0); } while_some { let _ = Panicking.map(Some).while_some(); } tuple_combinations1 { let _ = Panicking.tuple_combinations::<(_,)>(); } #[should_panic] tuple_combinations2 { let _ = Panicking.tuple_combinations::<(_, _)>(); } #[should_panic] tuple_combinations3 { let _ = Panicking.tuple_combinations::<(_, _, _)>(); } combinations { let _ = Panicking.combinations(0); let _ = Panicking.combinations(1); let _ = Panicking.combinations(2); } combinations_with_replacement { let _ = Panicking.combinations_with_replacement(0); let _ = Panicking.combinations_with_replacement(1); let _ = Panicking.combinations_with_replacement(2); } permutations { let _ = Panicking.permutations(0); let _ = Panicking.permutations(1); let _ = Panicking.permutations(2); } powerset { let _ = Panicking.powerset(); } pad_using { let _ = Panicking.pad_using(25, |_| 10); } with_position { let _ = Panicking.with_position(); } positions { let _ = Panicking.positions(|v| v % 2 == 0); } update { let _ = Panicking.update(|n| *n += 1); } multipeek { let _ = Panicking.multipeek(); } // Not iterator themselves but still lazy. into_grouping_map { let _ = Panicking.map(|x| (x, x + 1)).into_grouping_map(); } into_grouping_map_by { let _ = Panicking.into_grouping_map_by(|x| *x); } // Macros: iproduct { let _ = itertools::iproduct!(Panicking); let _ = itertools::iproduct!(Panicking, Panicking); let _ = itertools::iproduct!(Panicking, Panicking, Panicking); } izip { let _ = itertools::izip!(Panicking); let _ = itertools::izip!(Panicking, Panicking); let _ = itertools::izip!(Panicking, Panicking, Panicking); } chain { let _ = itertools::chain!(Panicking); let _ = itertools::chain!(Panicking, Panicking); let _ = itertools::chain!(Panicking, Panicking, Panicking); } // Free functions: multizip { let _ = itertools::multizip((Panicking, Panicking)); } put_back { let _ = itertools::put_back(Panicking); let _ = itertools::put_back(Panicking).with_value(15); } peek_nth { let _ = itertools::peek_nth(Panicking); } put_back_n { let _ = itertools::put_back_n(Panicking); } rciter { let _ = itertools::rciter(Panicking); } } itertools-0.13.0/tests/macros_hygiene.rs000064400000000000000000000005571046102023000164440ustar 00000000000000#[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); } #[test] fn izip_hygiene() { let _ = itertools::izip!(0..6); let _ = itertools::izip!(0..6, 0..9); let _ = itertools::izip!(0..6, 0..9, 0..12); } itertools-0.13.0/tests/merge_join.rs000064400000000000000000000062071046102023000155640ustar 00000000000000use itertools::free::merge_join_by; use itertools::EitherOrBoth; #[test] fn empty() { let left: Vec = vec![]; let right: Vec = vec![]; let expected_result: Vec> = vec![]; let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn left_only() { let left: Vec = vec![1, 2, 3]; let right: Vec = vec![]; let expected_result: Vec> = vec![ EitherOrBoth::Left(1), EitherOrBoth::Left(2), EitherOrBoth::Left(3), ]; let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn right_only() { let left: Vec = vec![]; let right: Vec = vec![1, 2, 3]; let expected_result: Vec> = vec![ EitherOrBoth::Right(1), EitherOrBoth::Right(2), EitherOrBoth::Right(3), ]; let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn first_left_then_right() { let left: Vec = vec![1, 2, 3]; let right: Vec = vec![4, 5, 6]; let expected_result: Vec> = vec![ EitherOrBoth::Left(1), EitherOrBoth::Left(2), EitherOrBoth::Left(3), EitherOrBoth::Right(4), EitherOrBoth::Right(5), EitherOrBoth::Right(6), ]; let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn first_right_then_left() { let left: Vec = vec![4, 5, 6]; let right: Vec = vec![1, 2, 3]; let expected_result: Vec> = vec![ EitherOrBoth::Right(1), EitherOrBoth::Right(2), EitherOrBoth::Right(3), EitherOrBoth::Left(4), EitherOrBoth::Left(5), EitherOrBoth::Left(6), ]; let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn interspersed_left_and_right() { let left: Vec = vec![1, 3, 5]; let right: Vec = vec![2, 4, 6]; let expected_result: Vec> = vec![ EitherOrBoth::Left(1), EitherOrBoth::Right(2), EitherOrBoth::Left(3), EitherOrBoth::Right(4), EitherOrBoth::Left(5), EitherOrBoth::Right(6), ]; let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } #[test] fn overlapping_left_and_right() { let left: Vec = vec![1, 3, 4, 6]; let right: Vec = vec![2, 3, 4, 5]; let expected_result: Vec> = vec![ EitherOrBoth::Left(1), EitherOrBoth::Right(2), EitherOrBoth::Both(3, 3), EitherOrBoth::Both(4, 4), EitherOrBoth::Right(5), EitherOrBoth::Left(6), ]; let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::>(); assert_eq!(expected_result, actual_result); } itertools-0.13.0/tests/peeking_take_while.rs000064400000000000000000000034321046102023000172610ustar 00000000000000use itertools::Itertools; use itertools::{put_back, put_back_n}; #[test] fn peeking_take_while_peekable() { let mut r = (0..10).peekable(); r.peeking_take_while(|x| *x <= 3).count(); assert_eq!(r.next(), Some(4)); } #[test] fn peeking_take_while_put_back() { let mut r = put_back(0..10); r.peeking_take_while(|x| *x <= 3).count(); assert_eq!(r.next(), Some(4)); r.peeking_take_while(|_| true).count(); assert_eq!(r.next(), None); } #[test] fn peeking_take_while_put_back_n() { let mut r = put_back_n(6..10); for elt in (0..6).rev() { r.put_back(elt); } r.peeking_take_while(|x| *x <= 3).count(); assert_eq!(r.next(), Some(4)); r.peeking_take_while(|_| true).count(); assert_eq!(r.next(), None); } #[test] fn peeking_take_while_slice_iter() { let v = [1, 2, 3, 4, 5, 6]; let mut r = v.iter(); r.peeking_take_while(|x| **x <= 3).count(); assert_eq!(r.next(), Some(&4)); r.peeking_take_while(|_| true).count(); assert_eq!(r.next(), None); } #[test] fn peeking_take_while_slice_iter_rev() { let v = [1, 2, 3, 4, 5, 6]; let mut r = v.iter().rev(); r.peeking_take_while(|x| **x >= 3).count(); assert_eq!(r.next(), Some(&2)); 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)); } itertools-0.13.0/tests/quick.rs000064400000000000000000001724341046102023000145700ustar 00000000000000//! The purpose of these tests is to cover corner cases of iterators //! and adaptors. //! //! In particular we test the tedious size_hint and exact size correctness. #![allow(deprecated, unstable_name_collisions)] use itertools::free::{ cloned, enumerate, multipeek, peek_nth, put_back, put_back_n, rciter, zip, zip_eq, }; use itertools::Itertools; use itertools::{iproduct, izip, multizip, EitherOrBoth}; use quickcheck as qc; use std::cmp::{max, min, Ordering}; use std::collections::{HashMap, HashSet}; use std::default::Default; use std::num::Wrapping; use std::ops::Range; use quickcheck::TestResult; use rand::seq::SliceRandom; use rand::Rng; /// Trait for size hint modifier types trait HintKind: Copy + Send + qc::Arbitrary { fn loosen_bounds(&self, org_hint: (usize, Option)) -> (usize, Option); } /// Exact size hint variant that leaves hints unchanged #[derive(Clone, Copy, Debug)] struct Exact {} impl HintKind for Exact { fn loosen_bounds(&self, org_hint: (usize, Option)) -> (usize, Option) { org_hint } } impl qc::Arbitrary for Exact { fn arbitrary(_: &mut G) -> Self { Self {} } } /// Inexact size hint variant to simulate imprecise (but valid) size hints /// /// Will always decrease the lower bound and increase the upper bound /// of the size hint by set amounts. #[derive(Clone, Copy, Debug)] struct Inexact { underestimate: usize, overestimate: usize, } impl HintKind for Inexact { fn loosen_bounds(&self, org_hint: (usize, Option)) -> (usize, Option) { let (org_lower, org_upper) = org_hint; ( org_lower.saturating_sub(self.underestimate), org_upper.and_then(move |x| x.checked_add(self.overestimate)), ) } } impl qc::Arbitrary for Inexact { fn arbitrary(g: &mut G) -> Self { 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]; 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> { 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| Self { underestimate: ue_value, overestimate: oe_value, }) })) } } /// Our base iterator that we can impl Arbitrary for /// /// By default we'll return inexact bounds estimates for size_hint /// to make tests harder to pass. /// /// NOTE: Iter is tricky and is not fused, to help catch bugs. /// At the end it will return None once, then return Some(0), /// then return None again. #[derive(Clone, Debug)] struct Iter { iterator: Range, // fuse/done flag fuse_flag: i32, hint_kind: SK, } impl Iter where HK: HintKind, { fn new(it: Range, hint_kind: HK) -> Self { Self { iterator: it, fuse_flag: 0, hint_kind, } } } impl Iterator for Iter where Range: Iterator, as Iterator>::Item: Default, HK: HintKind, { type Item = as Iterator>::Item; fn next(&mut self) -> Option { let elt = self.iterator.next(); if elt.is_none() { self.fuse_flag += 1; // check fuse flag if self.fuse_flag == 2 { return Some(Default::default()); } } elt } fn size_hint(&self) -> (usize, Option) { let org_hint = self.iterator.size_hint(); self.hint_kind.loosen_bounds(org_hint) } } impl DoubleEndedIterator for Iter where Range: DoubleEndedIterator, as Iterator>::Item: Default, HK: HintKind, { fn next_back(&mut self) -> Option { self.iterator.next_back() } } impl ExactSizeIterator for Iter where Range: ExactSizeIterator, as Iterator>::Item: Default, { } impl qc::Arbitrary for Iter where T: qc::Arbitrary, HK: HintKind, { fn arbitrary(g: &mut G) -> Self { Self::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g)) } fn shrink(&self) -> Box> { let r = self.iterator.clone(); let hint_kind = self.hint_kind; Box::new(r.start.shrink().flat_map(move |a| { r.end .shrink() .map(move |b| Self::new(a.clone()..b, hint_kind)) })) } } /// A meta-iterator which yields `Iter`s whose start/endpoints are /// increased or decreased linearly on each iteration. #[derive(Clone, Debug)] struct ShiftRange { range_start: i32, range_end: i32, start_step: i32, end_step: i32, iter_count: u32, hint_kind: HK, } impl Iterator for ShiftRange where HK: HintKind, { type Item = Iter; fn next(&mut self) -> Option { if self.iter_count == 0 { return None; } let iter = Iter::new(self.range_start..self.range_end, self.hint_kind); self.range_start += self.start_step; self.range_end += self.end_step; self.iter_count -= 1; Some(iter) } } impl ExactSizeIterator for ShiftRange {} impl qc::Arbitrary for ShiftRange where HK: HintKind, { fn arbitrary(g: &mut G) -> Self { const MAX_STARTING_RANGE_DIFF: i32 = 32; const MAX_STEP_MODULO: i32 = 8; const MAX_ITER_COUNT: u32 = 3; let range_start = qc::Arbitrary::arbitrary(g); let range_end = range_start + g.gen_range(0, MAX_STARTING_RANGE_DIFF + 1); let start_step = g.gen_range(-MAX_STEP_MODULO, MAX_STEP_MODULO + 1); let end_step = g.gen_range(-MAX_STEP_MODULO, MAX_STEP_MODULO + 1); let iter_count = g.gen_range(0, MAX_ITER_COUNT + 1); let hint_kind = qc::Arbitrary::arbitrary(g); Self { range_start, range_end, start_step, end_step, iter_count, hint_kind, } } } fn correct_count(get_it: F) -> bool where I: Iterator, F: Fn() -> I, { let mut counts = vec![get_it().count()]; 'outer: loop { 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"); } } if it.next().is_none() { break 'outer; } counts.push(it.count()); } let total_actual_count = counts.len() - 1; 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 ); return false; } } true } fn correct_size_hint(mut it: I) -> bool { // record size hint at each iteration let initial_hint = it.size_hint(); let mut hints = Vec::with_capacity(initial_hint.0 + 1); hints.push(initial_hint); while let Some(_) = it.next() { hints.push(it.size_hint()) } let mut true_count = hints.len(); // start off +1 too much // check all the size hints for &(low, hi) in &hints { true_count -= 1; 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; } } true } fn exact_size(mut it: I) -> bool { // check every iteration let (mut low, mut hi) = it.size_hint(); if Some(low) != hi { return false; } while let Some(_) = it.next() { let (xlow, xhi) = it.size_hint(); if low != xlow + 1 { return false; } low = xlow; hi = xhi; if Some(low) != hi { return false; } } let (low, hi) = it.size_hint(); low == 0 && hi == Some(0) } // Exact size for this case, without ExactSizeIterator fn exact_size_for_this(mut it: I) -> bool { // check every iteration let (mut low, mut hi) = it.size_hint(); if Some(low) != hi { return false; } while let Some(_) = it.next() { let (xlow, xhi) = it.size_hint(); if low != xlow + 1 { return false; } low = xlow; hi = xhi; if Some(low) != hi { return false; } } let (low, hi) = it.size_hint(); low == 0 && hi == Some(0) } /* * NOTE: Range is broken! * (all signed ranges are) #[quickcheck] fn size_range_i8(a: Iter) -> bool { exact_size(a) } #[quickcheck] fn size_range_i16(a: Iter) -> bool { exact_size(a) } #[quickcheck] fn size_range_u8(a: Iter) -> bool { exact_size(a) } */ macro_rules! quickcheck { // accept several property function definitions // The property functions can use pattern matching and `mut` as usual // in the function arguments, but the functions can not be generic. {$($(#$attr:tt)* fn $fn_name:ident($($arg:tt)*) -> $ret:ty { $($code:tt)* })*} => ( $( #[test] $(#$attr)* fn $fn_name() { fn prop($($arg)*) -> $ret { $($code)* } ::quickcheck::quickcheck(quickcheck!(@fn prop [] $($arg)*)); } )* ); // parse argument list (with patterns allowed) into prop as fn(_, _) -> _ (@fn $f:ident [$($t:tt)*]) => { $f as fn($($t),*) -> _ }; (@fn $f:ident [$($p:tt)*] : $($tail:tt)*) => { quickcheck!(@fn $f [$($p)* _] $($tail)*) }; (@fn $f:ident [$($p:tt)*] $t:tt $($tail:tt)*) => { quickcheck!(@fn $f [$($p)*] $($tail)*) }; } quickcheck! { fn size_product(a: Iter, b: Iter) -> bool { correct_size_hint(a.cartesian_product(b)) } fn size_product3(a: Iter, b: Iter, c: Iter) -> bool { correct_size_hint(iproduct!(a, b, c)) } fn correct_cartesian_product3(a: Iter, b: Iter, c: Iter, take_manual: usize) -> () { // test correctness of iproduct through regular iteration (take) // and through fold. let ac = a.clone(); let br = &b.clone(); let cr = &c.clone(); let answer: Vec<_> = ac.flat_map(move |ea| br.clone().flat_map(move |eb| cr.clone().map(move |ec| (ea, eb, ec)))).collect(); let mut product_iter = iproduct!(a, b, c); let mut actual = Vec::new(); actual.extend((&mut product_iter).take(take_manual)); if actual.len() == take_manual { product_iter.fold((), |(), elt| actual.push(elt)); } assert_eq!(answer, actual); } fn size_multi_product(a: ShiftRange) -> bool { correct_size_hint(a.multi_cartesian_product()) } fn correct_multi_product3(a: ShiftRange, take_manual: usize) -> () { // Fix no. of iterators at 3 let a = ShiftRange { iter_count: 3, ..a }; // test correctness of MultiProduct through regular iteration (take) // and through fold. let mut iters = a.clone(); let i0 = iters.next().unwrap(); let i1r = &iters.next().unwrap(); let i2r = &iters.next().unwrap(); let answer: Vec<_> = i0.flat_map(move |ei0| i1r.clone().flat_map(move |ei1| i2r.clone().map(move |ei2| vec![ei0, ei1, ei2]))).collect(); let mut multi_product = a.clone().multi_cartesian_product(); let mut actual = Vec::new(); actual.extend((&mut multi_product).take(take_manual)); if actual.len() == take_manual { multi_product.fold((), |(), elt| actual.push(elt)); } assert_eq!(answer, actual); assert_eq!(answer.into_iter().last(), a.multi_cartesian_product().last()); } fn correct_empty_multi_product() -> () { let empty = Vec::>::new().into_iter().multi_cartesian_product(); assert!(correct_size_hint(empty.clone())); itertools::assert_equal(empty, std::iter::once(Vec::new())) } fn size_multipeek(a: Iter, s: u8) -> bool { let mut it = multipeek(a); // peek a few times for _ in 0..s { it.peek(); } exact_size(it) } fn size_peek_nth(a: Iter, s: u8) -> bool { let mut it = peek_nth(a); // peek a few times for n in 0..s { it.peek_nth(n as usize); } exact_size(it) } fn equal_merge(mut a: Vec, mut b: Vec) -> bool { a.sort(); b.sort(); let mut merged = a.clone(); merged.extend(b.iter().cloned()); merged.sort(); itertools::equal(&merged, a.iter().merge(&b)) } fn size_merge(a: Iter, b: Iter) -> bool { correct_size_hint(a.merge(b)) } fn size_zip(a: Iter, b: Iter, c: Iter) -> bool { let filt = a.clone().dedup(); correct_size_hint(multizip((filt, b.clone(), c.clone()))) && exact_size(multizip((a, b, c))) } fn size_zip_rc(a: Iter, b: Iter) -> bool { let rc = rciter(a); correct_size_hint(multizip((&rc, &rc, b))) } fn size_zip_macro(a: Iter, b: Iter, c: Iter) -> bool { let filt = a.clone().dedup(); correct_size_hint(izip!(filt, b.clone(), c.clone())) && exact_size(izip!(a, b, c)) } fn equal_kmerge(mut a: Vec, mut b: Vec, mut c: Vec) -> bool { use itertools::free::kmerge; a.sort(); b.sort(); c.sort(); let mut merged = a.clone(); merged.extend(b.iter().cloned()); merged.extend(c.iter().cloned()); merged.sort(); itertools::equal(merged.into_iter(), kmerge(vec![a, b, c])) } // Any number of input iterators fn equal_kmerge_2(mut inputs: Vec>) -> bool { use itertools::free::kmerge; // sort the inputs for input in &mut inputs { input.sort(); } let mut merged = inputs.concat(); merged.sort(); itertools::equal(merged.into_iter(), kmerge(inputs)) } // Any number of input iterators fn equal_kmerge_by_ge(mut inputs: Vec>) -> bool { // sort the inputs for input in &mut inputs { input.sort(); input.reverse(); } let mut merged = inputs.concat(); merged.sort(); merged.reverse(); itertools::equal(merged.into_iter(), inputs.into_iter().kmerge_by(|x, y| x >= y)) } // Any number of input iterators fn equal_kmerge_by_lt(mut inputs: Vec>) -> bool { // sort the inputs for input in &mut inputs { input.sort(); } let mut merged = inputs.concat(); merged.sort(); itertools::equal(merged.into_iter(), inputs.into_iter().kmerge_by(|x, y| x < y)) } // Any number of input iterators fn equal_kmerge_by_le(mut inputs: Vec>) -> bool { // sort the inputs for input in &mut inputs { input.sort(); } let mut merged = inputs.concat(); merged.sort(); itertools::equal(merged.into_iter(), inputs.into_iter().kmerge_by(|x, y| x <= y)) } fn size_kmerge(a: Iter, b: Iter, c: Iter) -> bool { use itertools::free::kmerge; correct_size_hint(kmerge(vec![a, b, c])) } fn equal_zip_eq(a: Vec, b: Vec) -> bool { let len = std::cmp::min(a.len(), b.len()); let a = &a[..len]; let b = &b[..len]; itertools::equal(zip_eq(a, b), zip(a, b)) } #[should_panic] fn zip_eq_panics(a: Vec, b: Vec) -> TestResult { if a.len() == b.len() { return TestResult::discard(); } zip_eq(a.iter(), b.iter()).for_each(|_| {}); TestResult::passed() // won't come here } fn equal_positions(a: Vec) -> bool { let with_pos = a.iter().positions(|v| v % 2 == 0); let without = a.iter().enumerate().filter(|(_, v)| *v % 2 == 0).map(|(i, _)| i); itertools::equal(with_pos.clone(), without.clone()) && itertools::equal(with_pos.rev(), without.rev()) } fn size_zip_longest(a: Iter, b: Iter) -> bool { let filt = a.clone().dedup(); let filt2 = b.clone().dedup(); correct_size_hint(filt.zip_longest(b.clone())) && correct_size_hint(a.clone().zip_longest(filt2)) && exact_size(a.zip_longest(b)) } fn size_2_zip_longest(a: Iter, b: Iter) -> bool { let it = a.clone().zip_longest(b.clone()); let jt = a.clone().zip_longest(b.clone()); itertools::equal(a, it.filter_map(|elt| match elt { EitherOrBoth::Both(x, _) => Some(x), EitherOrBoth::Left(x) => Some(x), _ => None, } )) && itertools::equal(b, jt.filter_map(|elt| match elt { EitherOrBoth::Both(_, y) => Some(y), EitherOrBoth::Right(y) => Some(y), _ => None, } )) } fn size_interleave(a: Iter, b: Iter) -> bool { correct_size_hint(a.interleave(b)) } fn exact_interleave(a: Iter, b: Iter) -> bool { exact_size_for_this(a.interleave(b)) } fn size_interleave_shortest(a: Iter, b: Iter) -> bool { correct_size_hint(a.interleave_shortest(b)) } fn exact_interleave_shortest(a: Vec<()>, b: Vec<()>) -> bool { exact_size_for_this(a.iter().interleave_shortest(&b)) } fn size_intersperse(a: Iter, x: i16) -> bool { correct_size_hint(a.intersperse(x)) } fn equal_intersperse(a: Vec, x: i32) -> bool { let mut inter = false; let mut i = 0; for elt in a.iter().cloned().intersperse(x) { if inter { if elt != x { return false } } else { if elt != a[i] { return false } i += 1; } inter = !inter; } true } fn equal_combinations_2(a: Vec) -> bool { let mut v = Vec::new(); for (i, x) in enumerate(&a) { for y in &a[i + 1..] { v.push((x, y)); } } itertools::equal(a.iter().tuple_combinations::<(_, _)>(), v) } fn collect_tuple_matches_size(a: Iter) -> bool { let size = a.clone().count(); a.collect_tuple::<(_, _, _)>().is_some() == (size == 3) } fn correct_permutations(vals: HashSet, k: usize) -> () { // Test permutations only on iterators of distinct integers, to prevent // false positives. const MAX_N: usize = 5; let n = min(vals.len(), MAX_N); let vals: HashSet = vals.into_iter().take(n).collect(); let perms = vals.iter().permutations(k); let mut actual = HashSet::new(); for perm in perms { assert_eq!(perm.len(), k); let all_items_valid = perm.iter().all(|p| vals.contains(p)); assert!(all_items_valid, "perm contains value not from input: {:?}", perm); // Check that all perm items are distinct let distinct_len = { let perm_set: HashSet<_> = perm.iter().collect(); perm_set.len() }; assert_eq!(perm.len(), distinct_len); // Check that the perm is new assert!(actual.insert(perm.clone()), "perm already encountered: {:?}", perm); } } fn permutations_lexic_order(a: usize, b: usize) -> () { let a = a % 6; let b = b % 6; let n = max(a, b); let k = min (a, b); let expected_first: Vec = (0..k).collect(); let expected_last: Vec = ((n - k)..n).rev().collect(); let mut perms = (0..n).permutations(k); let mut curr_perm = match perms.next() { Some(p) => p, None => { return; } }; assert_eq!(expected_first, curr_perm); for next_perm in perms { assert!( next_perm > curr_perm, "next perm isn't greater-than current; next_perm={:?} curr_perm={:?} n={}", next_perm, curr_perm, n ); curr_perm = next_perm; } assert_eq!(expected_last, curr_perm); } fn permutations_count(n: usize, k: usize) -> bool { let n = n % 6; correct_count(|| (0..n).permutations(k)) } fn permutations_size(a: Iter, k: usize) -> bool { correct_size_hint(a.take(5).permutations(k)) } fn permutations_k0_yields_once(n: usize) -> () { let k = 0; let expected: Vec> = vec![vec![]]; let actual = (0..n).permutations(k).collect_vec(); assert_eq!(expected, actual); } } quickcheck! { fn correct_peek_nth(mut a: Vec) -> () { let mut it = peek_nth(a.clone()); for start_pos in 0..a.len() + 2 { for real_idx in start_pos..a.len() + 2 { let peek_idx = real_idx - start_pos; assert_eq!(it.peek_nth(peek_idx), a.get(real_idx)); assert_eq!(it.peek_nth_mut(peek_idx), a.get_mut(real_idx)); } assert_eq!(it.next(), a.get(start_pos).copied()); } } fn peek_nth_mut_replace(a: Vec, b: Vec) -> () { let mut it = peek_nth(a.iter()); for (i, m) in b.iter().enumerate().take(a.len().min(b.len())) { *it.peek_nth_mut(i).unwrap() = m; } for (i, m) in a.iter().enumerate() { assert_eq!(it.next().unwrap(), b.get(i).unwrap_or(m)); } assert_eq!(it.next(), None); assert_eq!(it.next(), None); } fn peek_nth_next_if(a: Vec) -> () { let mut it = peek_nth(a.clone()); for (idx, mut value) in a.iter().copied().enumerate() { let should_be_none = it.next_if(|x| x != &value); assert_eq!(should_be_none, None); if value % 5 == 0 { // Sometimes, peek up to 3 further. let n = value as usize % 3; let nth = it.peek_nth(n); assert_eq!(nth, a.get(idx + n)); } else if value % 5 == 1 { // Sometimes, peek next element mutably. if let Some(v) = it.peek_mut() { *v = v.wrapping_sub(1); let should_be_none = it.next_if_eq(&value); assert_eq!(should_be_none, None); value = value.wrapping_sub(1); } } let eq = it.next_if_eq(&value); assert_eq!(eq, Some(value)); } } } quickcheck! { fn dedup_via_coalesce(a: Vec) -> bool { let mut b = a.clone(); b.dedup(); itertools::equal( &b, a .iter() .coalesce(|x, y| { if x==y { Ok(x) } else { Err((x, y)) } }) .fold(vec![], |mut v, n| { v.push(n); v }) ) } } quickcheck! { fn equal_dedup(a: Vec) -> bool { let mut b = a.clone(); b.dedup(); itertools::equal(&b, a.iter().dedup()) } } quickcheck! { fn equal_dedup_by(a: Vec<(i32, i32)>) -> bool { let mut b = a.clone(); b.dedup_by(|x, y| x.0==y.0); itertools::equal(&b, a.iter().dedup_by(|x, y| x.0==y.0)) } } quickcheck! { fn size_dedup(a: Vec) -> bool { correct_size_hint(a.iter().dedup()) } } quickcheck! { fn size_dedup_by(a: Vec<(i32, i32)>) -> bool { correct_size_hint(a.iter().dedup_by(|x, y| x.0==y.0)) } } quickcheck! { fn exact_repeatn((n, x): (usize, i32)) -> bool { let it = itertools::repeat_n(x, n); exact_size(it) } } quickcheck! { fn size_put_back(a: Vec, x: Option) -> bool { let mut it = put_back(a.into_iter()); if let Some(t) = x { it.put_back(t); } correct_size_hint(it) } } quickcheck! { fn size_put_backn(a: Vec, b: Vec) -> bool { let mut it = put_back_n(a.into_iter()); for elt in b { it.put_back(elt) } correct_size_hint(it) } } quickcheck! { fn merge_join_by_ordering_vs_bool(a: Vec, b: Vec) -> bool { use either::Either; use itertools::free::merge_join_by; let mut has_equal = false; let it_ord = merge_join_by(a.clone(), b.clone(), Ord::cmp).flat_map(|v| match v { EitherOrBoth::Both(l, r) => { has_equal = true; vec![Either::Left(l), Either::Right(r)] } EitherOrBoth::Left(l) => vec![Either::Left(l)], EitherOrBoth::Right(r) => vec![Either::Right(r)], }); let it_bool = merge_join_by(a, b, PartialOrd::le); itertools::equal(it_ord, it_bool) || has_equal } fn merge_join_by_bool_unwrapped_is_merge_by(a: Vec, b: Vec) -> bool { use either::Either; use itertools::free::merge_join_by; let it = a.clone().into_iter().merge_by(b.clone(), PartialOrd::ge); let it_join = merge_join_by(a, b, PartialOrd::ge).map(Either::into_inner); itertools::equal(it, it_join) } } quickcheck! { fn size_tee(a: Vec) -> bool { let (mut t1, mut t2) = a.iter().tee(); t1.next(); t1.next(); t2.next(); exact_size(t1) && exact_size(t2) } } quickcheck! { fn size_tee_2(a: Vec) -> bool { let (mut t1, mut t2) = a.iter().dedup().tee(); t1.next(); t1.next(); t2.next(); correct_size_hint(t1) && correct_size_hint(t2) } } quickcheck! { fn size_take_while_ref(a: Vec, stop: u8) -> bool { correct_size_hint(a.iter().take_while_ref(|x| **x != stop)) } } quickcheck! { fn equal_partition(a: Vec) -> bool { let mut a = a; let mut ap = a.clone(); let split_index = itertools::partition(&mut ap, |x| *x >= 0); let parted = (0..split_index).all(|i| ap[i] >= 0) && (split_index..a.len()).all(|i| ap[i] < 0); a.sort(); ap.sort(); parted && (a == ap) } } quickcheck! { fn size_combinations(a: Iter) -> bool { let it = a.clone().tuple_combinations::<(_, _)>(); correct_size_hint(it.clone()) && it.count() == binomial(a.count(), 2) } fn exact_size_combinations_1(a: Vec) -> bool { let it = a.iter().tuple_combinations::<(_,)>(); exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 1) } fn exact_size_combinations_2(a: Vec) -> bool { let it = a.iter().tuple_combinations::<(_, _)>(); exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 2) } fn exact_size_combinations_3(mut a: Vec) -> bool { a.truncate(15); let it = a.iter().tuple_combinations::<(_, _, _)>(); exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 3) } } fn binomial(n: usize, k: usize) -> usize { if k > n { 0 } else { (n - k + 1..=n).product::() / (1..=k).product::() } } quickcheck! { fn equal_combinations(it: Iter) -> bool { let values = it.clone().collect_vec(); let mut cmb = it.tuple_combinations(); for i in 0..values.len() { for j in i+1..values.len() { let pair = (values[i], values[j]); if pair != cmb.next().unwrap() { return false; } } } cmb.next().is_none() } } quickcheck! { fn size_pad_tail(it: Iter, pad: u8) -> bool { correct_size_hint(it.clone().pad_using(pad as usize, |_| 0)) && correct_size_hint(it.dropping(1).rev().pad_using(pad as usize, |_| 0)) } } quickcheck! { fn size_pad_tail2(it: Iter, pad: u8) -> bool { exact_size(it.pad_using(pad as usize, |_| 0)) } } quickcheck! { fn size_powerset(it: Iter) -> bool { // Powerset cardinality gets large very quickly, limit input to keep test fast. correct_size_hint(it.take(12).powerset()) } } quickcheck! { fn size_duplicates(it: Iter) -> bool { correct_size_hint(it.duplicates()) } } quickcheck! { fn size_unique(it: Iter) -> bool { correct_size_hint(it.unique()) } fn count_unique(it: Vec, take_first: u8) -> () { let answer = { let mut v = it.clone(); v.sort(); v.dedup(); v.len() }; let mut iter = cloned(&it).unique(); let first_count = (&mut iter).take(take_first as usize).count(); let rest_count = iter.count(); assert_eq!(answer, first_count + rest_count); } } quickcheck! { fn fuzz_chunk_by_lazy_1(it: Iter) -> bool { let jt = it.clone(); let chunks = it.chunk_by(|k| *k); itertools::equal(jt, chunks.into_iter().flat_map(|(_, x)| x)) } } quickcheck! { fn fuzz_chunk_by_lazy_2(data: Vec) -> bool { let chunks = data.iter().chunk_by(|k| *k / 10); let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x)); res } } quickcheck! { fn fuzz_chunk_by_lazy_3(data: Vec) -> bool { let grouper = data.iter().chunk_by(|k| *k / 10); let chunks = grouper.into_iter().collect_vec(); let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x)); res } } quickcheck! { fn fuzz_chunk_by_lazy_duo(data: Vec, order: Vec<(bool, bool)>) -> bool { let grouper = data.iter().chunk_by(|k| *k / 3); let mut chunks1 = grouper.into_iter(); let mut chunks2 = grouper.into_iter(); let mut elts = Vec::<&u8>::new(); let mut old_chunks = Vec::new(); let tup1 = |(_, b)| b; for &(ord, consume_now) in &order { let iter = &mut [&mut chunks1, &mut chunks2][ord as usize]; match iter.next() { Some((_, gr)) => if consume_now { for og in old_chunks.drain(..) { elts.extend(og); } elts.extend(gr); } else { old_chunks.push(gr); }, None => break, } } for og in old_chunks.drain(..) { elts.extend(og); } for gr in chunks1.map(&tup1) { elts.extend(gr); } for gr in chunks2.map(&tup1) { elts.extend(gr); } itertools::assert_equal(&data, elts); true } } quickcheck! { fn chunk_clone_equal(a: Vec, size: u8) -> () { let mut size = size; if size == 0 { size += 1; } let it = a.chunks(size as usize); itertools::assert_equal(it.clone(), it); } } quickcheck! { fn equal_chunks_lazy(a: Vec, size: u8) -> bool { let mut size = size; if size == 0 { size += 1; } let chunks = a.iter().chunks(size as usize); let it = a.chunks(size as usize); for (a, b) in chunks.into_iter().zip(it) { if !itertools::equal(a, b) { return false; } } true } } // tuple iterators quickcheck! { fn equal_circular_tuple_windows_1(a: Vec) -> bool { let x = a.iter().map(|e| (e,) ); let y = a.iter().circular_tuple_windows::<(_,)>(); itertools::assert_equal(x,y); true } fn equal_circular_tuple_windows_2(a: Vec) -> bool { let x = (0..a.len()).map(|start_idx| ( &a[start_idx], &a[(start_idx + 1) % a.len()], )); let y = a.iter().circular_tuple_windows::<(_, _)>(); itertools::assert_equal(x,y); true } fn equal_circular_tuple_windows_3(a: Vec) -> bool { let x = (0..a.len()).map(|start_idx| ( &a[start_idx], &a[(start_idx + 1) % a.len()], &a[(start_idx + 2) % a.len()], )); let y = a.iter().circular_tuple_windows::<(_, _, _)>(); itertools::assert_equal(x,y); true } fn equal_circular_tuple_windows_4(a: Vec) -> bool { let x = (0..a.len()).map(|start_idx| ( &a[start_idx], &a[(start_idx + 1) % a.len()], &a[(start_idx + 2) % a.len()], &a[(start_idx + 3) % a.len()], )); let y = a.iter().circular_tuple_windows::<(_, _, _, _)>(); itertools::assert_equal(x,y); true } fn equal_cloned_circular_tuple_windows(a: Vec) -> bool { let x = a.iter().circular_tuple_windows::<(_, _, _, _)>(); let y = x.clone(); itertools::assert_equal(x,y); true } fn equal_cloned_circular_tuple_windows_noninitial(a: Vec) -> bool { let mut x = a.iter().circular_tuple_windows::<(_, _, _, _)>(); let _ = x.next(); let y = x.clone(); itertools::assert_equal(x,y); true } fn equal_cloned_circular_tuple_windows_complete(a: Vec) -> bool { let mut x = a.iter().circular_tuple_windows::<(_, _, _, _)>(); for _ in x.by_ref() {} let y = x.clone(); itertools::assert_equal(x,y); true } fn circular_tuple_windows_exact_size(a: Vec) -> bool { exact_size(a.iter().circular_tuple_windows::<(_, _, _, _)>()) } fn equal_tuple_windows_1(a: Vec) -> bool { let x = a.windows(1).map(|s| (&s[0], )); let y = a.iter().tuple_windows::<(_,)>(); itertools::equal(x, y) } fn equal_tuple_windows_2(a: Vec) -> bool { let x = a.windows(2).map(|s| (&s[0], &s[1])); let y = a.iter().tuple_windows::<(_, _)>(); itertools::equal(x, y) } fn equal_tuple_windows_3(a: Vec) -> bool { let x = a.windows(3).map(|s| (&s[0], &s[1], &s[2])); let y = a.iter().tuple_windows::<(_, _, _)>(); itertools::equal(x, y) } fn equal_tuple_windows_4(a: Vec) -> bool { let x = a.windows(4).map(|s| (&s[0], &s[1], &s[2], &s[3])); let y = a.iter().tuple_windows::<(_, _, _, _)>(); itertools::equal(x, y) } fn tuple_windows_exact_size_1(a: Vec) -> bool { exact_size(a.iter().tuple_windows::<(_,)>()) } fn tuple_windows_exact_size_4(a: Vec) -> bool { exact_size(a.iter().tuple_windows::<(_, _, _, _)>()) } fn equal_tuples_1(a: Vec) -> bool { let x = a.chunks(1).map(|s| (&s[0], )); let y = a.iter().tuples::<(_,)>(); itertools::equal(x, y) } fn equal_tuples_2(a: Vec) -> bool { let x = a.chunks(2).filter(|s| s.len() == 2).map(|s| (&s[0], &s[1])); let y = a.iter().tuples::<(_, _)>(); itertools::equal(x, y) } fn equal_tuples_3(a: Vec) -> bool { let x = a.chunks(3).filter(|s| s.len() == 3).map(|s| (&s[0], &s[1], &s[2])); let y = a.iter().tuples::<(_, _, _)>(); itertools::equal(x, y) } fn equal_tuples_4(a: Vec) -> bool { let x = a.chunks(4).filter(|s| s.len() == 4).map(|s| (&s[0], &s[1], &s[2], &s[3])); let y = a.iter().tuples::<(_, _, _, _)>(); itertools::equal(x, y) } fn exact_tuple_buffer(a: Vec) -> bool { let mut iter = a.iter().tuples::<(_, _, _, _)>(); (&mut iter).last(); let buffer = iter.into_buffer(); assert_eq!(buffer.len(), a.len() % 4); exact_size(buffer) } fn tuples_size_hint_inexact(a: Iter) -> bool { correct_size_hint(a.clone().tuples::<(_,)>()) && correct_size_hint(a.clone().tuples::<(_, _)>()) && correct_size_hint(a.tuples::<(_, _, _, _)>()) } fn tuples_size_hint_exact(a: Iter) -> bool { exact_size(a.clone().tuples::<(_,)>()) && exact_size(a.clone().tuples::<(_, _)>()) && exact_size(a.tuples::<(_, _, _, _)>()) } } // with_position quickcheck! { fn with_position_exact_size_1(a: Vec) -> bool { exact_size_for_this(a.iter().with_position()) } fn with_position_exact_size_2(a: Iter) -> bool { exact_size_for_this(a.with_position()) } } quickcheck! { fn correct_group_map_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let count = a.len(); let lookup = a.into_iter().map(|i| (i % modulo, i)).into_group_map(); assert_eq!(lookup.values().flat_map(|vals| vals.iter()).count(), count); for (&key, vals) in lookup.iter() { assert!(vals.iter().all(|&val| val % modulo == key)); } } } /// A peculiar type: Equality compares both tuple items, but ordering only the /// first item. This is so we can check the stability property easily. #[derive(Clone, Debug, PartialEq, Eq)] struct Val(u32, u32); impl PartialOrd for Val { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Val { fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) } } impl qc::Arbitrary for Val { fn arbitrary(g: &mut G) -> Self { let (x, y) = <(u32, u32)>::arbitrary(g); Self(x, y) } fn shrink(&self) -> Box> { Box::new((self.0, self.1).shrink().map(|(x, y)| Self(x, y))) } } quickcheck! { fn minmax(a: Vec) -> bool { use itertools::MinMaxResult; let minmax = a.iter().minmax(); let expected = match a.len() { 0 => MinMaxResult::NoElements, 1 => MinMaxResult::OneElement(&a[0]), _ => MinMaxResult::MinMax(a.iter().min().unwrap(), a.iter().max().unwrap()), }; minmax == expected } } quickcheck! { fn minmax_f64(a: Vec) -> TestResult { use itertools::MinMaxResult; if a.iter().any(|x| x.is_nan()) { return TestResult::discard(); } let min = cloned(&a).fold1(f64::min); let max = cloned(&a).fold1(f64::max); let minmax = cloned(&a).minmax(); let expected = match a.len() { 0 => MinMaxResult::NoElements, 1 => MinMaxResult::OneElement(min.unwrap()), _ => MinMaxResult::MinMax(min.unwrap(), max.unwrap()), }; TestResult::from_bool(minmax == expected) } } quickcheck! { fn tree_reduce_f64(mut a: Vec) -> TestResult { fn collapse_adjacent(x: Vec, mut f: F) -> Vec where F: FnMut(f64, f64) -> f64 { let mut out = Vec::new(); for i in (0..x.len()).step_by(2) { if i == x.len()-1 { out.push(x[i]) } else { out.push(f(x[i], x[i+1])); } } out } if a.iter().any(|x| x.is_nan()) { return TestResult::discard(); } let actual = a.iter().cloned().tree_reduce(f64::atan2); while a.len() > 1 { a = collapse_adjacent(a, f64::atan2); } let expected = a.pop(); TestResult::from_bool(actual == expected) } } quickcheck! { fn exactly_one_i32(a: Vec) -> TestResult { let ret = a.iter().cloned().exactly_one(); match a.len() { 1 => TestResult::from_bool(ret.unwrap() == a[0]), _ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())), } } } quickcheck! { fn at_most_one_i32(a: Vec) -> TestResult { let ret = a.iter().cloned().at_most_one(); match a.len() { 0 => TestResult::from_bool(ret.unwrap().is_none()), 1 => TestResult::from_bool(ret.unwrap() == Some(a[0])), _ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())), } } } quickcheck! { fn consistent_grouping_map_with_by(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup_grouping_map = a.iter().copied().map(|i| (i % modulo, i)).into_grouping_map().collect::>(); let lookup_grouping_map_by = a.iter().copied().into_grouping_map_by(|i| i % modulo).collect::>(); assert_eq!(lookup_grouping_map, lookup_grouping_map_by); } fn correct_grouping_map_by_aggregate_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo < 2 { 2 } else { modulo } as u64; // Avoid `% 0` let lookup = a.iter() .map(|&b| b as u64) // Avoid overflows .into_grouping_map_by(|i| i % modulo) .aggregate(|acc, &key, val| { assert!(val % modulo == key); if val % (modulo - 1) == 0 { None } else { Some(acc.unwrap_or(0) + val) } }); let group_map_lookup = a.iter() .map(|&b| b as u64) .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .filter_map(|(key, vals)| { vals.into_iter().fold(None, |acc, val| { if val % (modulo - 1) == 0 { None } else { Some(acc.unwrap_or(0) + val) } }).map(|new_val| (key, new_val)) }) .collect::>(); assert_eq!(lookup, group_map_lookup); for m in 0..modulo { assert_eq!( lookup.get(&m).copied(), a.iter() .map(|&b| b as u64) .filter(|&val| val % modulo == m) .fold(None, |acc, val| { if val % (modulo - 1) == 0 { None } else { Some(acc.unwrap_or(0) + val) } }) ); } } fn correct_grouping_map_by_fold_with_modulo_key(a: Vec, modulo: u8) -> () { #[derive(Debug, Default, PartialEq)] struct Accumulator { acc: u64, } let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0` let lookup = a.iter().map(|&b| b as u64) // Avoid overflows .into_grouping_map_by(|i| i % modulo) .fold_with(|_key, _val| Default::default(), |Accumulator { acc }, &key, val| { assert!(val % modulo == key); let acc = acc + val; Accumulator { acc } }); let group_map_lookup = a.iter() .map(|&b| b as u64) .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().sum())).map(|(key, acc)| (key,Accumulator { acc })) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &Accumulator { acc: sum }) in lookup.iter() { assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::()); } } fn correct_grouping_map_by_fold_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0` let lookup = a.iter().map(|&b| b as u64) // Avoid overflows .into_grouping_map_by(|i| i % modulo) .fold(0u64, |acc, &key, val| { assert!(val % modulo == key); acc + val }); 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())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &sum) in lookup.iter() { assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::()); } } fn correct_grouping_map_by_reduce_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0` let lookup = a.iter().map(|&b| b as u64) // Avoid overflows .into_grouping_map_by(|i| i % modulo) .reduce(|acc, &key, val| { assert!(val % modulo == key); acc + val }); // TODO: Swap `fold1` with stdlib's `reduce` when it's stabilized let group_map_lookup = a.iter() .map(|&b| b as u64) .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().fold1(|acc, val| acc + val).unwrap())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &sum) in lookup.iter() { assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::()); } } fn correct_grouping_map_by_collect_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup_grouping_map = a.iter().copied().into_grouping_map_by(|i| i % modulo).collect::>(); let lookup_group_map = a.iter().copied().map(|i| (i % modulo, i)).into_group_map(); assert_eq!(lookup_grouping_map, lookup_group_map); } fn correct_grouping_map_by_max_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).max(); let group_map_lookup = a.iter().copied() .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().max().unwrap())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &max) in lookup.iter() { assert_eq!(Some(max), a.iter().copied().filter(|&val| val % modulo == key).max()); } } fn correct_grouping_map_by_max_by_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).max_by(|_, v1, v2| v1.cmp(v2)); let group_map_lookup = a.iter().copied() .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().max_by(|v1, v2| v1.cmp(v2)).unwrap())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &max) in lookup.iter() { assert_eq!(Some(max), a.iter().copied().filter(|&val| val % modulo == key).max_by(|v1, v2| v1.cmp(v2))); } } fn correct_grouping_map_by_max_by_key_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).max_by_key(|_, &val| val); let group_map_lookup = a.iter().copied() .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().max_by_key(|&val| val).unwrap())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &max) in lookup.iter() { assert_eq!(Some(max), a.iter().copied().filter(|&val| val % modulo == key).max_by_key(|&val| val)); } } fn correct_grouping_map_by_min_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).min(); let group_map_lookup = a.iter().copied() .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().min().unwrap())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &min) in lookup.iter() { assert_eq!(Some(min), a.iter().copied().filter(|&val| val % modulo == key).min()); } } fn correct_grouping_map_by_min_by_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).min_by(|_, v1, v2| v1.cmp(v2)); let group_map_lookup = a.iter().copied() .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().min_by(|v1, v2| v1.cmp(v2)).unwrap())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &min) in lookup.iter() { assert_eq!(Some(min), a.iter().copied().filter(|&val| val % modulo == key).min_by(|v1, v2| v1.cmp(v2))); } } fn correct_grouping_map_by_min_by_key_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).min_by_key(|_, &val| val); let group_map_lookup = a.iter().copied() .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().min_by_key(|&val| val).unwrap())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &min) in lookup.iter() { assert_eq!(Some(min), a.iter().copied().filter(|&val| val % modulo == key).min_by_key(|&val| val)); } } fn correct_grouping_map_by_minmax_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).minmax(); let group_map_lookup = a.iter().copied() .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().minmax())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &minmax) in lookup.iter() { assert_eq!(minmax, a.iter().copied().filter(|&val| val % modulo == key).minmax()); } } fn correct_grouping_map_by_minmax_by_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).minmax_by(|_, v1, v2| v1.cmp(v2)); let group_map_lookup = a.iter().copied() .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().minmax_by(|v1, v2| v1.cmp(v2)))) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &minmax) in lookup.iter() { assert_eq!(minmax, a.iter().copied().filter(|&val| val % modulo == key).minmax_by(|v1, v2| v1.cmp(v2))); } } fn correct_grouping_map_by_minmax_by_key_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).minmax_by_key(|_, &val| val); let group_map_lookup = a.iter().copied() .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().minmax_by_key(|&val| val))) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &minmax) in lookup.iter() { assert_eq!(minmax, a.iter().copied().filter(|&val| val % modulo == key).minmax_by_key(|&val| val)); } } fn correct_grouping_map_by_sum_modulo_key(a: Vec, modulo: u8) -> () { let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0` let lookup = a.iter().map(|&b| b as u64) // Avoid overflows .into_grouping_map_by(|i| i % modulo) .sum(); 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())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &sum) in lookup.iter() { assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::()); } } fn correct_grouping_map_by_product_modulo_key(a: Vec, modulo: u8) -> () { let modulo = Wrapping(if modulo == 0 { 1 } else { modulo } as u64); // Avoid `% 0` let lookup = a.iter().map(|&b| Wrapping(b as u64)) // Avoid overflows .into_grouping_map_by(|i| i % modulo) .product(); let group_map_lookup = a.iter().map(|&b| Wrapping(b as u64)) .map(|i| (i % modulo, i)) .into_group_map() .into_iter() .map(|(key, vals)| (key, vals.into_iter().product::>())) .collect::>(); assert_eq!(lookup, group_map_lookup); for (&key, &prod) in lookup.iter() { assert_eq!( prod, a.iter() .map(|&b| Wrapping(b as u64)) .filter(|&val| val % modulo == key) .product::>() ); } } // This should check that if multiple elements are equally minimum or maximum // then `max`, `min` and `minmax` pick the first minimum and the last maximum. // This is to be consistent with `std::iter::max` and `std::iter::min`. fn correct_grouping_map_by_min_max_minmax_order_modulo_key() -> () { use itertools::MinMaxResult; let lookup = (0..=10) .into_grouping_map_by(|_| 0) .max_by(|_, _, _| Ordering::Equal); assert_eq!(lookup[&0], 10); let lookup = (0..=10) .into_grouping_map_by(|_| 0) .min_by(|_, _, _| Ordering::Equal); assert_eq!(lookup[&0], 0); let lookup = (0..=10) .into_grouping_map_by(|_| 0) .minmax_by(|_, _, _| Ordering::Equal); assert_eq!(lookup[&0], MinMaxResult::MinMax(0, 10)); } } quickcheck! { fn counts(nums: Vec) -> TestResult { let counts = nums.iter().counts(); for (&item, &count) in counts.iter() { #[allow(clippy::absurd_extreme_comparisons)] if count <= 0 { return TestResult::failed(); } if count != nums.iter().filter(|&x| x == item).count() { return TestResult::failed(); } } for item in nums.iter() { if !counts.contains_key(item) { return TestResult::failed(); } } TestResult::passed() } } quickcheck! { fn test_double_ended_zip_2(a: Vec, b: Vec) -> TestResult { let mut x = multizip((a.clone().into_iter(), b.clone().into_iter())) .collect_vec(); x.reverse(); let y = multizip((a.into_iter(), b.into_iter())) .rfold(Vec::new(), |mut vec, e| { vec.push(e); vec }); TestResult::from_bool(itertools::equal(x, y)) } fn test_double_ended_zip_3(a: Vec, b: Vec, c: Vec) -> TestResult { let mut x = multizip((a.clone().into_iter(), b.clone().into_iter(), c.clone().into_iter())) .collect_vec(); x.reverse(); let y = multizip((a.into_iter(), b.into_iter(), c.into_iter())) .rfold(Vec::new(), |mut vec, e| { vec.push(e); vec }); TestResult::from_bool(itertools::equal(x, y)) } } fn is_fused(mut it: I) -> bool { for _ in it.by_ref() {} for _ in 0..10 { if it.next().is_some() { return false; } } true } quickcheck! { fn fused_combination(a: Iter) -> bool { is_fused(a.clone().combinations(1)) && is_fused(a.combinations(3)) } fn fused_combination_with_replacement(a: Iter) -> bool { is_fused(a.clone().combinations_with_replacement(1)) && is_fused(a.combinations_with_replacement(3)) } fn fused_tuple_combination(a: Iter) -> bool { is_fused(a.clone().fuse().tuple_combinations::<(_,)>()) && is_fused(a.fuse().tuple_combinations::<(_,_,_)>()) } fn fused_unique(a: Iter) -> bool { is_fused(a.fuse().unique()) } fn fused_unique_by(a: Iter) -> bool { is_fused(a.fuse().unique_by(|x| x % 100)) } fn fused_interleave_shortest(a: Iter, b: Iter) -> bool { !is_fused(a.clone().interleave_shortest(b.clone())) && is_fused(a.fuse().interleave_shortest(b.fuse())) } fn fused_product(a: Iter, b: Iter) -> bool { is_fused(a.fuse().cartesian_product(b.fuse())) } fn fused_merge(a: Iter, b: Iter) -> bool { is_fused(a.fuse().merge(b.fuse())) } fn fused_filter_ok(a: Iter) -> bool { is_fused(a.map(|x| if x % 2 == 0 {Ok(x)} else {Err(x)} ) .filter_ok(|x| x % 3 == 0) .fuse()) } fn fused_filter_map_ok(a: Iter) -> bool { is_fused(a.map(|x| if x % 2 == 0 {Ok(x)} else {Err(x)} ) .filter_map_ok(|x| if x % 3 == 0 {Some(x / 3)} else {None}) .fuse()) } fn fused_positions(a: Iter) -> bool { !is_fused(a.clone().positions(|x|x%2==0)) && is_fused(a.fuse().positions(|x|x%2==0)) } fn fused_update(a: Iter) -> bool { !is_fused(a.clone().update(|x|*x+=1)) && is_fused(a.fuse().update(|x|*x+=1)) } fn fused_tuple_windows(a: Iter) -> bool { is_fused(a.fuse().tuple_windows::<(_,_)>()) } fn fused_pad_using(a: Iter) -> bool { is_fused(a.fuse().pad_using(100,|_|0)) } } quickcheck! { fn min_set_contains_min(a: Vec<(usize, char)>) -> bool { let result_set = a.iter().min_set(); if let Some(result_element) = a.iter().min() { result_set.contains(&result_element) } else { result_set.is_empty() } } fn min_set_by_contains_min(a: Vec<(usize, char)>) -> bool { let compare = |x: &&(usize, char), y: &&(usize, char)| x.1.cmp(&y.1); let result_set = a.iter().min_set_by(compare); if let Some(result_element) = a.iter().min_by(compare) { result_set.contains(&result_element) } else { result_set.is_empty() } } fn min_set_by_key_contains_min(a: Vec<(usize, char)>) -> bool { let key = |x: &&(usize, char)| x.1; let result_set = a.iter().min_set_by_key(&key); if let Some(result_element) = a.iter().min_by_key(&key) { result_set.contains(&result_element) } else { result_set.is_empty() } } fn max_set_contains_max(a: Vec<(usize, char)>) -> bool { let result_set = a.iter().max_set(); if let Some(result_element) = a.iter().max() { result_set.contains(&result_element) } else { result_set.is_empty() } } fn max_set_by_contains_max(a: Vec<(usize, char)>) -> bool { let compare = |x: &&(usize, char), y: &&(usize, char)| x.1.cmp(&y.1); let result_set = a.iter().max_set_by(compare); if let Some(result_element) = a.iter().max_by(compare) { result_set.contains(&result_element) } else { result_set.is_empty() } } fn max_set_by_key_contains_max(a: Vec<(usize, char)>) -> bool { let key = |x: &&(usize, char)| x.1; let result_set = a.iter().max_set_by_key(&key); if let Some(result_element) = a.iter().max_by_key(&key) { result_set.contains(&result_element) } else { result_set.is_empty() } } fn tail(v: Vec, n: u8) -> bool { let n = n as usize; let result = &v[v.len().saturating_sub(n)..]; itertools::equal(v.iter().tail(n), result) && itertools::equal(v.iter().filter(|_| true).tail(n), result) } } itertools-0.13.0/tests/specializations.rs000064400000000000000000000433731046102023000166540ustar 00000000000000#![allow(unstable_name_collisions)] use itertools::Itertools; use quickcheck::Arbitrary; use quickcheck::{quickcheck, TestResult}; use rand::Rng; use std::fmt::Debug; struct Unspecialized(I); impl Iterator for Unspecialized where I: Iterator, { type Item = I::Item; #[inline(always)] fn next(&mut self) -> Option { self.0.next() } } impl DoubleEndedIterator for Unspecialized where I: DoubleEndedIterator, { #[inline(always)] fn next_back(&mut self) -> Option { self.0.next_back() } } fn test_specializations(it: &I) where I::Item: Eq + Debug + Clone, I: Iterator + Clone, { macro_rules! check_specialized { ($src:expr, |$it:pat| $closure:expr) => { // Many iterators special-case the first elements, so we test specializations for iterators that have already been advanced. let mut src = $src.clone(); for _ in 0..5 { let $it = src.clone(); let v1 = $closure; let $it = Unspecialized(src.clone()); let v2 = $closure; assert_eq!(v1, v2); src.next(); } } } check_specialized!(it, |i| i.count()); check_specialized!(it, |i| i.last()); check_specialized!(it, |i| i.collect::>()); check_specialized!(it, |i| { let mut parameters_from_fold = vec![]; let fold_result = i.fold(vec![], |mut acc, v: I::Item| { parameters_from_fold.push((acc.clone(), v.clone())); acc.push(v); acc }); (parameters_from_fold, fold_result) }); check_specialized!(it, |mut i| { let mut parameters_from_all = vec![]; let first = i.next(); let all_result = i.all(|x| { parameters_from_all.push(x.clone()); Some(x) == first }); (parameters_from_all, all_result) }); let size = it.clone().count(); for n in 0..size + 2 { check_specialized!(it, |mut i| i.nth(n)); } // size_hint is a bit harder to check let mut it_sh = it.clone(); for n in 0..size + 2 { let len = it_sh.clone().count(); let (min, max) = it_sh.size_hint(); assert_eq!(size - n.min(size), len); assert!(min <= len); if let Some(max) = max { assert!(len <= max); } it_sh.next(); } } fn test_double_ended_specializations(it: &I) where I::Item: Eq + Debug + Clone, I: DoubleEndedIterator + Clone, { macro_rules! check_specialized { ($src:expr, |$it:pat| $closure:expr) => { // Many iterators special-case the first elements, so we test specializations for iterators that have already been advanced. let mut src = $src.clone(); for step in 0..8 { let $it = src.clone(); let v1 = $closure; let $it = Unspecialized(src.clone()); let v2 = $closure; assert_eq!(v1, v2); if step % 2 == 0 { src.next(); } else { src.next_back(); } } } } check_specialized!(it, |i| { let mut parameters_from_rfold = vec![]; let rfold_result = i.rfold(vec![], |mut acc, v: I::Item| { parameters_from_rfold.push((acc.clone(), v.clone())); acc.push(v); acc }); (parameters_from_rfold, rfold_result) }); let size = it.clone().count(); for n in 0..size + 2 { check_specialized!(it, |mut i| i.nth_back(n)); } } quickcheck! { fn interleave(v: Vec, w: Vec) -> () { test_specializations(&v.iter().interleave(w.iter())); } fn interleave_shortest(v: Vec, w: Vec) -> () { test_specializations(&v.iter().interleave_shortest(w.iter())); } fn batching(v: Vec) -> () { test_specializations(&v.iter().batching(Iterator::next)); } fn tuple_windows(v: Vec) -> () { test_specializations(&v.iter().tuple_windows::<(_,)>()); test_specializations(&v.iter().tuple_windows::<(_, _)>()); test_specializations(&v.iter().tuple_windows::<(_, _, _)>()); } fn circular_tuple_windows(v: Vec) -> () { test_specializations(&v.iter().circular_tuple_windows::<(_,)>()); test_specializations(&v.iter().circular_tuple_windows::<(_, _)>()); test_specializations(&v.iter().circular_tuple_windows::<(_, _, _)>()); } fn tuples(v: Vec) -> () { test_specializations(&v.iter().tuples::<(_,)>()); test_specializations(&v.iter().tuples::<(_, _)>()); test_specializations(&v.iter().tuples::<(_, _, _)>()); } fn cartesian_product(a: Vec, b: Vec) -> TestResult { if a.len() * b.len() > 100 { return TestResult::discard(); } test_specializations(&a.iter().cartesian_product(&b)); TestResult::passed() } fn multi_cartesian_product(a: Vec, b: Vec, c: Vec) -> TestResult { if a.len() * b.len() * c.len() > 100 { return TestResult::discard(); } test_specializations(&vec![a, b, c].into_iter().multi_cartesian_product()); TestResult::passed() } fn coalesce(v: Vec) -> () { test_specializations(&v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) })) } fn dedup(v: Vec) -> () { test_specializations(&v.iter().dedup()) } fn dedup_by(v: Vec) -> () { test_specializations(&v.iter().dedup_by(PartialOrd::ge)) } fn dedup_with_count(v: Vec) -> () { test_specializations(&v.iter().dedup_with_count()) } fn dedup_by_with_count(v: Vec) -> () { test_specializations(&v.iter().dedup_by_with_count(PartialOrd::ge)) } fn duplicates(v: Vec) -> () { let it = v.iter().duplicates(); test_specializations(&it); test_double_ended_specializations(&it); } fn duplicates_by(v: Vec) -> () { let it = v.iter().duplicates_by(|x| *x % 10); test_specializations(&it); test_double_ended_specializations(&it); } fn unique(v: Vec) -> () { let it = v.iter().unique(); test_specializations(&it); test_double_ended_specializations(&it); } fn unique_by(v: Vec) -> () { let it = v.iter().unique_by(|x| *x % 50); test_specializations(&it); test_double_ended_specializations(&it); } fn take_while_inclusive(v: Vec) -> () { test_specializations(&v.iter().copied().take_while_inclusive(|&x| x < 100)); } fn while_some(v: Vec) -> () { test_specializations(&v.iter().map(|&x| if x < 100 { Some(2 * x) } else { None }).while_some()); } fn pad_using(v: Vec) -> () { use std::convert::TryFrom; let it = v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX)); test_specializations(&it); test_double_ended_specializations(&it); } fn with_position(v: Vec) -> () { test_specializations(&v.iter().with_position()); } fn positions(v: Vec) -> () { let it = v.iter().positions(|x| x % 5 == 0); test_specializations(&it); test_double_ended_specializations(&it); } fn update(v: Vec) -> () { let it = v.iter().copied().update(|x| *x = x.wrapping_mul(7)); test_specializations(&it); test_double_ended_specializations(&it); } fn tuple_combinations(v: Vec) -> TestResult { if v.len() > 10 { return TestResult::discard(); } test_specializations(&v.iter().tuple_combinations::<(_,)>()); test_specializations(&v.iter().tuple_combinations::<(_, _)>()); test_specializations(&v.iter().tuple_combinations::<(_, _, _)>()); TestResult::passed() } fn intersperse(v: Vec) -> () { test_specializations(&v.into_iter().intersperse(0)); } fn intersperse_with(v: Vec) -> () { test_specializations(&v.into_iter().intersperse_with(|| 0)); } fn combinations(a: Vec, n: u8) -> TestResult { if n > 3 || a.len() > 8 { return TestResult::discard(); } test_specializations(&a.iter().combinations(n as usize)); TestResult::passed() } fn combinations_with_replacement(a: Vec, n: u8) -> TestResult { if n > 3 || a.len() > 7 { return TestResult::discard(); } test_specializations(&a.iter().combinations_with_replacement(n as usize)); TestResult::passed() } fn permutations(a: Vec, n: u8) -> TestResult { if n > 3 || a.len() > 8 { return TestResult::discard(); } test_specializations(&a.iter().permutations(n as usize)); TestResult::passed() } fn powerset(a: Vec) -> TestResult { if a.len() > 6 { return TestResult::discard(); } test_specializations(&a.iter().powerset()); TestResult::passed() } fn zip_longest(a: Vec, b: Vec) -> () { let it = a.into_iter().zip_longest(b); test_specializations(&it); test_double_ended_specializations(&it); } fn zip_eq(a: Vec) -> () { test_specializations(&a.iter().zip_eq(a.iter().rev())) } fn multizip(a: Vec) -> () { let it = itertools::multizip((a.iter(), a.iter().rev(), a.iter().take(50))); test_specializations(&it); test_double_ended_specializations(&it); } fn izip(a: Vec, b: Vec) -> () { test_specializations(&itertools::izip!(b.iter(), a, b.iter().rev())); } fn iproduct(a: Vec, b: Vec, c: Vec) -> TestResult { if a.len() * b.len() * c.len() > 200 { return TestResult::discard(); } test_specializations(&itertools::iproduct!(a, b.iter(), c)); TestResult::passed() } fn repeat_n(element: i8, n: u8) -> () { let it = itertools::repeat_n(element, n as usize); test_specializations(&it); test_double_ended_specializations(&it); } fn exactly_one_error(v: Vec) -> TestResult { // Use `at_most_one` would be similar. match v.iter().exactly_one() { Ok(_) => TestResult::discard(), Err(it) => { test_specializations(&it); TestResult::passed() } } } } quickcheck! { fn put_back_qc(test_vec: Vec) -> () { test_specializations(&itertools::put_back(test_vec.iter())); let mut pb = itertools::put_back(test_vec.into_iter()); pb.put_back(1); test_specializations(&pb); } fn put_back_n(v: Vec, n: u8) -> () { let mut it = itertools::put_back_n(v); for k in 0..n { it.put_back(k); } test_specializations(&it); } fn multipeek(v: Vec, n: u8) -> () { let mut it = v.into_iter().multipeek(); for _ in 0..n { it.peek(); } test_specializations(&it); } fn peek_nth_with_peek(v: Vec, n: u8) -> () { let mut it = itertools::peek_nth(v); for _ in 0..n { it.peek(); } test_specializations(&it); } fn peek_nth_with_peek_nth(v: Vec, n: u8) -> () { let mut it = itertools::peek_nth(v); it.peek_nth(n as usize); test_specializations(&it); } fn peek_nth_with_peek_mut(v: Vec, n: u8) -> () { let mut it = itertools::peek_nth(v); for _ in 0..n { if let Some(x) = it.peek_mut() { *x = x.wrapping_add(50); } } test_specializations(&it); } fn peek_nth_with_peek_nth_mut(v: Vec, n: u8) -> () { let mut it = itertools::peek_nth(v); if let Some(x) = it.peek_nth_mut(n as usize) { *x = x.wrapping_add(50); } test_specializations(&it); } } quickcheck! { fn merge(a: Vec, b: Vec) -> () { test_specializations(&a.into_iter().merge(b)) } fn merge_by(a: Vec, b: Vec) -> () { test_specializations(&a.into_iter().merge_by(b, PartialOrd::ge)) } fn merge_join_by_ordering(i1: Vec, i2: Vec) -> () { test_specializations(&i1.into_iter().merge_join_by(i2, Ord::cmp)); } fn merge_join_by_bool(i1: Vec, i2: Vec) -> () { test_specializations(&i1.into_iter().merge_join_by(i2, PartialOrd::ge)); } fn kmerge(a: Vec, b: Vec, c: Vec) -> () { test_specializations(&vec![a, b, c] .into_iter() .map(|v| v.into_iter().sorted()) .kmerge()); } fn kmerge_by(a: Vec, b: Vec, c: Vec) -> () { test_specializations(&vec![a, b, c] .into_iter() .map(|v| v.into_iter().sorted_by_key(|a| a.abs())) .kmerge_by(|a, b| a.abs() < b.abs())); } } quickcheck! { fn map_into(v: Vec) -> () { let it = v.into_iter().map_into::(); test_specializations(&it); test_double_ended_specializations(&it); } fn map_ok(v: Vec>) -> () { 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>) -> () { test_specializations(&v.into_iter().filter_ok(|&i| i < 20)); } fn filter_map_ok(v: Vec>) -> () { test_specializations(&v.into_iter().filter_map_ok(|i| if i < 20 { Some(i * 2) } else { None })); } // `SmallIter2` because `Vec` is too slow and we get bad coverage from a singleton like Option fn flatten_ok(v: Vec, char>>) -> () { let it = v.into_iter().flatten_ok(); test_specializations(&it); test_double_ended_specializations(&it); } } quickcheck! { // TODO Replace this function by a normal call to test_specializations fn process_results(v: Vec>) -> () { helper(v.iter().copied()); helper(v.iter().copied().filter(Result::is_ok)); fn helper(it: impl DoubleEndedIterator> + Clone) { macro_rules! check_results_specialized { ($src:expr, |$it:pat| $closure:expr) => { assert_eq!( itertools::process_results($src.clone(), |$it| $closure), itertools::process_results($src.clone(), |i| { let $it = Unspecialized(i); $closure }), ) } } check_results_specialized!(it, |i| i.count()); check_results_specialized!(it, |i| i.last()); check_results_specialized!(it, |i| i.collect::>()); check_results_specialized!(it, |i| i.rev().collect::>()); check_results_specialized!(it, |i| { let mut parameters_from_fold = vec![]; let fold_result = i.fold(vec![], |mut acc, v| { parameters_from_fold.push((acc.clone(), v)); acc.push(v); acc }); (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(); let all_result = i.all(|x| { parameters_from_all.push(x); Some(x)==first }); (parameters_from_all, all_result) }); let size = it.clone().count(); for n in 0..size + 2 { check_results_specialized!(it, |mut i| i.nth(n)); } for n in 0..size + 2 { check_results_specialized!(it, |mut i| i.nth_back(n)); } } } } /// Like `VecIntoIter` with maximum 2 elements. #[derive(Debug, Clone, Default)] enum SmallIter2 { #[default] Zero, One(T), Two(T, T), } impl Arbitrary for SmallIter2 { fn arbitrary(g: &mut G) -> Self { match g.gen_range(0u8, 3) { 0 => Self::Zero, 1 => Self::One(T::arbitrary(g)), 2 => Self::Two(T::arbitrary(g), T::arbitrary(g)), _ => unreachable!(), } } // maybe implement shrink too, maybe not } impl Iterator for SmallIter2 { type Item = T; fn next(&mut self) -> Option { match std::mem::take(self) { Self::Zero => None, Self::One(val) => Some(val), Self::Two(val, second) => { *self = Self::One(second); Some(val) } } } fn size_hint(&self) -> (usize, Option) { let len = match self { Self::Zero => 0, Self::One(_) => 1, Self::Two(_, _) => 2, }; (len, Some(len)) } } impl DoubleEndedIterator for SmallIter2 { fn next_back(&mut self) -> Option { match std::mem::take(self) { Self::Zero => None, Self::One(val) => Some(val), Self::Two(first, val) => { *self = Self::One(first); Some(val) } } } } itertools-0.13.0/tests/test_core.rs000064400000000000000000000217761046102023000154450ustar 00000000000000//! Licensed under the Apache License, Version 2.0 //! https://www.apache.org/licenses/LICENSE-2.0 or the MIT license //! https://opensource.org/licenses/MIT, at your //! option. This file may not be copied, modified, or distributed //! except according to those terms. #![no_std] #![allow(deprecated)] 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::iproduct; use crate::it::izip; use crate::it::multizip; use crate::it::Itertools; use core::iter; use itertools as it; #[allow(dead_code)] fn get_esi_then_esi(it: I) { fn is_esi(_: impl ExactSizeIterator) {} is_esi(it.clone().get(1..4)); is_esi(it.clone().get(1..=4)); is_esi(it.clone().get(1..)); is_esi(it.clone().get(..4)); is_esi(it.clone().get(..=4)); is_esi(it.get(..)); } #[allow(dead_code)] fn get_dei_esi_then_dei_esi(it: I) { fn is_dei_esi(_: impl DoubleEndedIterator + ExactSizeIterator) {} is_dei_esi(it.clone().get(1..4)); is_dei_esi(it.clone().get(1..=4)); is_dei_esi(it.clone().get(1..)); is_dei_esi(it.clone().get(..4)); is_dei_esi(it.clone().get(..=4)); is_dei_esi(it.get(..)); } #[test] fn get_1_max() { let mut it = (0..5).get(1..=usize::MAX); assert_eq!(it.next(), Some(1)); assert_eq!(it.next_back(), Some(4)); } #[test] #[should_panic] fn get_full_range_inclusive() { let _it = (0..5).get(0..=usize::MAX); } #[test] fn product0() { let mut prod = iproduct!(); assert_eq!(prod.next(), Some(())); assert!(prod.next().is_none()); } #[test] fn iproduct1() { let s = "αβ"; let mut prod = iproduct!(s.chars()); assert_eq!(prod.next(), Some(('α',))); assert_eq!(prod.next(), Some(('β',))); assert!(prod.next().is_none()); } #[test] fn product2() { let s = "αβ"; let mut prod = iproduct!(s.chars(), 0..2); assert!(prod.next() == Some(('α', 0))); assert!(prod.next() == Some(('α', 1))); assert!(prod.next() == Some(('β', 0))); assert!(prod.next() == Some(('β', 1))); assert!(prod.next().is_none()); } #[test] fn product_temporary() { for (_x, _y, _z) in iproduct!( [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); assert!(zip.next() == Some(2)); assert!(zip.next().is_none()); let mut zip = izip!(0..3, 0..2, 0..2i8); for i in 0..2 { assert!((i as usize, i, i as i8) == zip.next().unwrap()); } assert!(zip.next().is_none()); let xs: [isize; 0] = []; let mut zip = izip!(0..3, 0..2, 0..2i8, &xs); assert!(zip.next().is_none()); } #[test] fn izip2() { let _zip1: iter::Zip<_, _> = izip!(1.., 2..); let _zip2: iter::Zip<_, _> = izip!(1.., 2..,); } #[test] fn izip3() { let mut zip: iter::Map, _> = izip!(0..3, 0..2, 0..2i8); for i in 0..2 { assert!((i as usize, i, i as i8) == zip.next().unwrap()); } assert!(zip.next().is_none()); } #[test] fn multizip3() { let mut zip = multizip((0..3, 0..2, 0..2i8)); for i in 0..2 { assert!((i as usize, i, i as i8) == zip.next().unwrap()); } assert!(zip.next().is_none()); let xs: [isize; 0] = []; let mut zip = multizip((0..3, 0..2, 0..2i8, xs.iter())); assert!(zip.next().is_none()); for (_, _, _, _, _) in multizip((0..3, 0..2, xs.iter(), &xs, xs.to_vec())) { /* test compiles */ } } #[test] fn chain_macro() { let mut chain = chain!(2..3); assert!(chain.next() == Some(2)); assert!(chain.next().is_none()); let mut chain = chain!(0..2, 2..3, 3..5i8); for i in 0..5i8 { assert_eq!(Some(i), chain.next()); } assert!(chain.next().is_none()); let mut chain = chain!(); assert_eq!(chain.next(), Option::<()>::None); } #[test] fn chain2() { let _ = chain!(1.., 2..); let _ = chain!(1.., 2..,); } #[test] fn write_to() { let xs = [7, 9, 8]; let mut ys = [0; 5]; let cnt = ys.iter_mut().set_from(xs.iter().copied()); assert!(cnt == xs.len()); assert!(ys == [7, 9, 8, 0, 0]); let cnt = ys.iter_mut().set_from(0..10); assert!(cnt == ys.len()); assert!(ys == [0, 1, 2, 3, 4]); } #[test] fn test_interleave() { let xs: [u8; 0] = []; let ys = [7u8, 9, 8, 10]; let zs = [2u8, 77]; let it = interleave(xs.iter(), ys.iter()); it::assert_equal(it, ys.iter()); let rs = [7u8, 2, 9, 77, 8, 10]; let it = interleave(ys.iter(), zs.iter()); it::assert_equal(it, rs.iter()); } #[test] fn test_intersperse() { let xs = [1u8, 2, 3]; let ys = [1u8, 0, 2, 0, 3]; let it = intersperse(&xs, &0); it::assert_equal(it, ys.iter()); } #[test] fn test_intersperse_with() { let xs = [1u8, 2, 3]; let ys = [1u8, 10, 2, 10, 3]; let i = 10; let it = intersperse_with(&xs, || &i); it::assert_equal(it, ys.iter()); } #[test] fn dropping() { let xs = [1, 2, 3]; let mut it = xs.iter().dropping(2); assert_eq!(it.next(), Some(&3)); assert!(it.next().is_none()); let mut it = xs.iter().dropping(5); assert!(it.next().is_none()); } #[test] fn batching() { let xs = [0, 1, 2, 1, 3]; let ys = [(0, 1), (2, 1)]; // An iterator that gathers elements up in pairs let pit = xs .iter() .cloned() .batching(|it| it.next().and_then(|x| it.next().map(|y| (x, y)))); it::assert_equal(pit, ys.iter().cloned()); } #[test] fn test_put_back() { let xs = [0, 1, 1, 1, 2, 1, 3, 3]; let mut pb = put_back(xs.iter().cloned()); pb.next(); pb.put_back(1); pb.put_back(0); it::assert_equal(pb, xs.iter().cloned()); } #[test] fn merge() { it::assert_equal((0..10).step_by(2).merge((1..10).step_by(2)), 0..10); } #[test] fn repeatn() { let s = "α"; let mut it = it::repeat_n(s, 3); assert_eq!(it.len(), 3); assert_eq!(it.next(), Some(s)); assert_eq!(it.next(), Some(s)); assert_eq!(it.next(), Some(s)); assert_eq!(it.next(), None); assert_eq!(it.next(), None); } #[test] fn count_clones() { // Check that RepeatN only clones N - 1 times. use core::cell::Cell; #[derive(PartialEq, Debug)] struct Foo { n: Cell, } impl Clone for Foo { fn clone(&self) -> Self { let n = self.n.get(); self.n.set(n + 1); Self { n: Cell::new(n + 1), } } } for n in 0..10 { 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) }) ); } } } #[test] fn part() { let mut data = [7, 1, 1, 9, 1, 1, 3]; let i = it::partition(&mut data, |elt| *elt >= 3); assert_eq!(i, 3); assert_eq!(data, [7, 3, 9, 1, 1, 1, 1]); let i = it::partition(&mut data, |elt| *elt == 1); assert_eq!(i, 4); assert_eq!(data, [1, 1, 1, 1, 9, 3, 7]); let mut data = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let i = it::partition(&mut data, |elt| *elt % 3 == 0); assert_eq!(i, 3); assert_eq!(data, [9, 6, 3, 4, 5, 2, 7, 8, 1]); } #[test] fn tree_reduce() { for i in 0..100 { 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)); } #[test] fn at_most_one() { assert_eq!((0..10).filter(|&x| x == 2).at_most_one().unwrap(), Some(2)); assert!((0..10) .filter(|&x| x > 1 && x < 4) .at_most_one() .unwrap_err() .eq(2..4)); assert!((0..10) .filter(|&x| x > 1 && x < 5) .at_most_one() .unwrap_err() .eq(2..5)); assert_eq!((0..10).filter(|&_| false).at_most_one().unwrap(), None); } #[test] fn sum1() { let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; assert_eq!(v[..0].iter().cloned().sum1::(), None); assert_eq!(v[1..2].iter().cloned().sum1::(), Some(1)); assert_eq!(v[1..3].iter().cloned().sum1::(), Some(3)); assert_eq!(v.iter().cloned().sum1::(), Some(55)); } #[test] fn product1() { let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; assert_eq!(v[..0].iter().cloned().product1::(), None); assert_eq!(v[..1].iter().cloned().product1::(), Some(0)); assert_eq!(v[1..3].iter().cloned().product1::(), Some(2)); assert_eq!(v[1..5].iter().cloned().product1::(), Some(24)); } itertools-0.13.0/tests/test_std.rs000064400000000000000000001254251046102023000153030ustar 00000000000000#![allow(unstable_name_collisions)] use crate::it::cloned; use crate::it::free::put_back_n; use crate::it::free::rciter; use crate::it::iproduct; use crate::it::izip; use crate::it::multipeek; use crate::it::multizip; use crate::it::peek_nth; use crate::it::repeat_n; use crate::it::ExactlyOneError; use crate::it::FoldWhile; use crate::it::Itertools; use itertools as it; use quickcheck as qc; use rand::{ distributions::{Distribution, Standard}, rngs::StdRng, Rng, SeedableRng, }; use rand::{seq::SliceRandom, thread_rng}; use std::{cmp::min, fmt::Debug, marker::PhantomData}; #[test] fn product3() { let prod = iproduct!(0..3, 0..2, 0..2); assert_eq!(prod.size_hint(), (12, Some(12))); let v = prod.collect_vec(); for i in 0..3 { for j in 0..2 { for k in 0..2 { assert!((i, j, k) == v[(i * 2 * 2 + j * 2 + k) as usize]); } } } for (_, _, _, _) in iproduct!(0..3, 0..2, 0..2, 0..3) { /* test compiles */ } } #[test] fn interleave_shortest() { let v0: Vec = vec![0, 2, 4]; let v1: Vec = vec![1, 3, 5, 7]; let it = v0.into_iter().interleave_shortest(v1); assert_eq!(it.size_hint(), (6, Some(6))); assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5]); let v0: Vec = vec![0, 2, 4, 6, 8]; let v1: Vec = vec![1, 3, 5]; let it = v0.into_iter().interleave_shortest(v1); assert_eq!(it.size_hint(), (7, Some(7))); assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5, 6]); let i0 = ::std::iter::repeat(0); let v1: Vec<_> = vec![1, 3, 5]; let it = i0.interleave_shortest(v1); assert_eq!(it.size_hint(), (7, Some(7))); let v0: Vec<_> = vec![0, 2, 4]; let i1 = ::std::iter::repeat(1); let it = v0.into_iter().interleave_shortest(i1); assert_eq!(it.size_hint(), (6, Some(6))); } #[test] fn duplicates_by() { let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"]; let ys = ["aa", "bbbb", "cccc"]; it::assert_equal(ys.iter(), xs.iter().duplicates_by(|x| x[..2].to_string())); it::assert_equal( ys.iter(), xs.iter().rev().duplicates_by(|x| x[..2].to_string()).rev(), ); let ys_rev = ["ccc", "aa", "bbbbb"]; it::assert_equal( ys_rev.iter(), xs.iter().duplicates_by(|x| x[..2].to_string()).rev(), ); } #[test] fn duplicates() { let xs = [0, 1, 2, 3, 2, 1, 3]; let ys = [2, 1, 3]; it::assert_equal(ys.iter(), xs.iter().duplicates()); it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev()); let ys_rev = [3, 2, 1]; it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev()); let xs = [0, 1, 0, 1]; let ys = [0, 1]; it::assert_equal(ys.iter(), xs.iter().duplicates()); it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev()); let ys_rev = [1, 0]; it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev()); let xs = [0, 1, 2, 1, 2]; let ys = vec![1, 2]; assert_eq!(ys, xs.iter().duplicates().cloned().collect_vec()); assert_eq!( ys, xs.iter().rev().duplicates().rev().cloned().collect_vec() ); let ys_rev = vec![2, 1]; assert_eq!(ys_rev, xs.iter().duplicates().rev().cloned().collect_vec()); } #[test] fn unique_by() { let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"]; let ys = ["aaa", "bbbbb", "ccc"]; it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string())); it::assert_equal( ys.iter(), xs.iter().rev().unique_by(|x| x[..2].to_string()).rev(), ); let ys_rev = ["cccc", "aaaaa", "bbbb"]; it::assert_equal( ys_rev.iter(), xs.iter().unique_by(|x| x[..2].to_string()).rev(), ); } #[test] fn unique() { let xs = [0, 1, 2, 3, 2, 1, 3]; let ys = [0, 1, 2, 3]; it::assert_equal(ys.iter(), xs.iter().unique()); it::assert_equal(ys.iter(), xs.iter().rev().unique().rev()); let ys_rev = [3, 1, 2, 0]; it::assert_equal(ys_rev.iter(), xs.iter().unique().rev()); let xs = [0, 1]; let ys = [0, 1]; it::assert_equal(ys.iter(), xs.iter().unique()); it::assert_equal(ys.iter(), xs.iter().rev().unique().rev()); let ys_rev = [1, 0]; it::assert_equal(ys_rev.iter(), xs.iter().unique().rev()); } #[test] fn intersperse() { let xs = ["a", "", "b", "c"]; let v: Vec<&str> = xs.iter().cloned().intersperse(", ").collect(); let text: String = v.concat(); assert_eq!(text, "a, , b, c".to_string()); let ys = [0, 1, 2, 3]; let mut it = ys[..0].iter().copied().intersperse(1); assert!(it.next().is_none()); } #[test] fn dedup() { let xs = [0, 1, 1, 1, 2, 1, 3, 3]; let ys = [0, 1, 2, 1, 3]; it::assert_equal(ys.iter(), xs.iter().dedup()); let xs = [0, 0, 0, 0, 0]; let ys = [0]; it::assert_equal(ys.iter(), xs.iter().dedup()); let xs = [0, 1, 1, 1, 2, 1, 3, 3]; let ys = [0, 1, 2, 1, 3]; let mut xs_d = Vec::new(); xs.iter().dedup().fold((), |(), &elt| xs_d.push(elt)); assert_eq!(&xs_d, &ys); } #[test] fn coalesce() { let data = [-1., -2., -3., 3., 1., 0., -1.]; let it = data.iter().cloned().coalesce(|x, y| { if (x >= 0.) == (y >= 0.) { Ok(x + y) } else { Err((x, y)) } }); itertools::assert_equal(it.clone(), vec![-6., 4., -1.]); assert_eq!( it.fold(vec![], |mut v, n| { v.push(n); v }), vec![-6., 4., -1.] ); } #[test] fn dedup_by() { let xs = [ (0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3), ]; let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)]; it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.1 == y.1)); let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]; let ys = [(0, 1)]; it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.0 == y.0)); let xs = [ (0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3), ]; let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)]; let mut xs_d = Vec::new(); xs.iter() .dedup_by(|x, y| x.1 == y.1) .fold((), |(), &elt| xs_d.push(elt)); assert_eq!(&xs_d, &ys); } #[test] fn dedup_with_count() { let xs: [i32; 8] = [0, 1, 1, 1, 2, 1, 3, 3]; let ys: [(usize, &i32); 5] = [(1, &0), (3, &1), (1, &2), (1, &1), (2, &3)]; it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count()); let xs: [i32; 5] = [0, 0, 0, 0, 0]; let ys: [(usize, &i32); 1] = [(5, &0)]; it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count()); } #[test] fn dedup_by_with_count() { let xs = [ (0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3), ]; let ys = [ (1, &(0, 0)), (3, &(0, 1)), (1, &(0, 2)), (1, &(3, 1)), (2, &(0, 3)), ]; it::assert_equal( ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.1 == y.1), ); let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]; let ys = [(5, &(0, 1))]; it::assert_equal( ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.0 == y.0), ); } #[test] fn all_equal() { assert!("".chars().all_equal()); assert!("A".chars().all_equal()); assert!(!"AABBCCC".chars().all_equal()); assert!("AAAAAAA".chars().all_equal()); for (_key, mut sub) in &"AABBCCC".chars().chunk_by(|&x| x) { assert!(sub.all_equal()); } } #[test] fn all_equal_value() { assert_eq!("".chars().all_equal_value(), Err(None)); assert_eq!("A".chars().all_equal_value(), Ok('A')); assert_eq!("AABBCCC".chars().all_equal_value(), Err(Some(('A', 'B')))); assert_eq!("AAAAAAA".chars().all_equal_value(), Ok('A')); { let mut it = [1, 2, 3].iter().copied(); let result = it.all_equal_value(); assert_eq!(result, Err(Some((1, 2)))); let remaining = it.next(); assert_eq!(remaining, Some(3)); assert!(it.next().is_none()); } } #[test] fn all_unique() { assert!("ABCDEFGH".chars().all_unique()); assert!(!"ABCDEFGA".chars().all_unique()); assert!(::std::iter::empty::().all_unique()); } #[test] fn test_put_back_n() { let xs = [0, 1, 1, 1, 2, 1, 3, 3]; let mut pb = put_back_n(xs.iter().cloned()); pb.next(); pb.next(); pb.put_back(1); pb.put_back(0); it::assert_equal(pb, xs.iter().cloned()); } #[test] fn tee() { let xs = [0, 1, 2, 3]; let (mut t1, mut t2) = xs.iter().cloned().tee(); assert_eq!(t1.next(), Some(0)); assert_eq!(t2.next(), Some(0)); assert_eq!(t1.next(), Some(1)); assert_eq!(t1.next(), Some(2)); assert_eq!(t1.next(), Some(3)); assert_eq!(t1.next(), None); assert_eq!(t2.next(), Some(1)); assert_eq!(t2.next(), Some(2)); assert_eq!(t1.next(), None); assert_eq!(t2.next(), Some(3)); assert_eq!(t2.next(), None); assert_eq!(t1.next(), None); assert_eq!(t2.next(), None); let (t1, t2) = xs.iter().cloned().tee(); it::assert_equal(t1, xs.iter().cloned()); it::assert_equal(t2, xs.iter().cloned()); let (t1, t2) = xs.iter().cloned().tee(); it::assert_equal(t1.zip(t2), xs.iter().cloned().zip(xs.iter().cloned())); } #[test] fn test_rciter() { let xs = [0, 1, 1, 1, 2, 1, 3, 5, 6]; let mut r1 = rciter(xs.iter().cloned()); let mut r2 = r1.clone(); assert_eq!(r1.next(), Some(0)); assert_eq!(r2.next(), Some(1)); let mut z = r1.zip(r2); assert_eq!(z.next(), Some((1, 1))); assert_eq!(z.next(), Some((2, 1))); assert_eq!(z.next(), Some((3, 5))); assert_eq!(z.next(), None); // test intoiterator let r1 = rciter(0..5); let mut z = izip!(&r1, r1); assert_eq!(z.next(), Some((0, 1))); } #[test] fn trait_pointers() { struct ByRef<'r, I: ?Sized>(&'r mut I); impl<'r, X, I> Iterator for ByRef<'r, I> where I: ?Sized + 'r + Iterator, { type Item = X; fn next(&mut self) -> Option { self.0.next() } } let mut it = Box::new(0..10) as Box>; assert_eq!(it.next(), Some(0)); { let jt: &mut dyn Iterator = &mut *it; assert_eq!(jt.next(), Some(1)); { let mut r = ByRef(jt); assert_eq!(r.next(), Some(2)); } assert_eq!(jt.find_position(|x| *x == 4), Some((1, 4))); jt.for_each(|_| ()); } } #[test] fn merge_by() { let odd: Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")]; let even = [(2, "foo"), (4, "bar"), (6, "baz")]; let expected = [ (1, "hello"), (2, "foo"), (3, "world"), (4, "bar"), (5, "!"), (6, "baz"), ]; let results = odd.iter().merge_by(even.iter(), |a, b| a.0 <= b.0); it::assert_equal(results, expected.iter()); } #[test] fn merge_by_btree() { use std::collections::BTreeMap; let mut bt1 = BTreeMap::new(); bt1.insert("hello", 1); bt1.insert("world", 3); let mut bt2 = BTreeMap::new(); bt2.insert("foo", 2); bt2.insert("bar", 4); let results = bt1.into_iter().merge_by(bt2, |a, b| a.0 <= b.0); let expected = vec![("bar", 4), ("foo", 2), ("hello", 1), ("world", 3)]; it::assert_equal(results, expected); } #[test] fn kmerge() { let its = (0..4).map(|s| (s..10).step_by(4)); it::assert_equal(its.kmerge(), 0..10); } #[test] fn kmerge_2() { let its = vec![3, 2, 1, 0].into_iter().map(|s| (s..10).step_by(4)); it::assert_equal(its.kmerge(), 0..10); } #[test] fn kmerge_empty() { let its = (0..4).map(|_| 0..0); assert_eq!(its.kmerge().next(), None); } #[test] fn kmerge_size_hint() { let its = (0..5).map(|_| (0..10)); assert_eq!(its.kmerge().size_hint(), (50, Some(50))); } #[test] fn kmerge_empty_size_hint() { let its = (0..5).map(|_| (0..0)); assert_eq!(its.kmerge().size_hint(), (0, Some(0))); } #[test] fn join() { let many = [1, 2, 3]; let one = [1]; let none: Vec = vec![]; assert_eq!(many.iter().join(", "), "1, 2, 3"); assert_eq!(one.iter().join(", "), "1"); assert_eq!(none.iter().join(", "), ""); } #[test] fn sorted_unstable_by() { let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| a.cmp(&b)); it::assert_equal(sc, vec![1, 2, 3, 4]); let v = (0..5).sorted_unstable_by(|&a, &b| a.cmp(&b).reverse()); it::assert_equal(v, vec![4, 3, 2, 1, 0]); } #[test] fn sorted_unstable_by_key() { let sc = [3, 4, 1, 2].iter().cloned().sorted_unstable_by_key(|&x| x); it::assert_equal(sc, vec![1, 2, 3, 4]); let v = (0..5).sorted_unstable_by_key(|&x| -x); it::assert_equal(v, vec![4, 3, 2, 1, 0]); } #[test] fn sorted_by() { let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| a.cmp(&b)); it::assert_equal(sc, vec![1, 2, 3, 4]); let v = (0..5).sorted_by(|&a, &b| a.cmp(&b).reverse()); it::assert_equal(v, vec![4, 3, 2, 1, 0]); } qc::quickcheck! { fn k_smallest_range(n: i64, m: u16, k: u16) -> () { // u16 is used to constrain k and m to 0..2¹⁶, // otherwise the test could use too much memory. let (k, m) = (k as usize, m as u64); let mut v: Vec<_> = (n..n.saturating_add(m as _)).collect(); // Generate a random permutation of n..n+m v.shuffle(&mut thread_rng()); // Construct the right answers for the top and bottom elements let mut sorted = v.clone(); sorted.sort(); // how many elements are we checking let num_elements = min(k, m as _); // Compute the top and bottom k in various combinations let sorted_smallest = sorted[..num_elements].iter().cloned(); let smallest = v.iter().cloned().k_smallest(k); let smallest_by = v.iter().cloned().k_smallest_by(k, Ord::cmp); let smallest_by_key = v.iter().cloned().k_smallest_by_key(k, |&x| x); let sorted_largest = sorted[sorted.len() - num_elements..].iter().rev().cloned(); let largest = v.iter().cloned().k_largest(k); let largest_by = v.iter().cloned().k_largest_by(k, Ord::cmp); let largest_by_key = v.iter().cloned().k_largest_by_key(k, |&x| x); // Check the variations produce the same answers and that they're right it::assert_equal(smallest, sorted_smallest.clone()); it::assert_equal(smallest_by, sorted_smallest.clone()); it::assert_equal(smallest_by_key, sorted_smallest); it::assert_equal(largest, sorted_largest.clone()); it::assert_equal(largest_by, sorted_largest.clone()); it::assert_equal(largest_by_key, sorted_largest); } } #[derive(Clone, Debug)] struct RandIter { idx: usize, len: usize, rng: R, _t: PhantomData, } impl Iterator for RandIter where Standard: Distribution, { type Item = T; fn next(&mut self) -> Option { if self.idx == self.len { None } else { self.idx += 1; Some(self.rng.gen()) } } } impl qc::Arbitrary for RandIter { fn arbitrary(g: &mut G) -> Self { Self { idx: 0, len: g.size(), rng: R::seed_from_u64(g.next_u64()), _t: PhantomData {}, } } } // Check that taking the k smallest is the same as // sorting then taking the k first elements fn k_smallest_sort(i: I, k: u16) where I: Iterator + Clone, I::Item: Ord + Debug, { let j = i.clone(); let k = k as usize; it::assert_equal(i.k_smallest(k), j.sorted().take(k)) } // Similar to `k_smallest_sort` but for our custom heap implementation. fn k_smallest_by_sort(i: I, k: u16) where I: Iterator + Clone, I::Item: Ord + Debug, { let j = i.clone(); let k = k as usize; it::assert_equal(i.k_smallest_by(k, Ord::cmp), j.sorted().take(k)) } macro_rules! generic_test { ($f:ident, $($t:ty),+) => { $(paste::item! { qc::quickcheck! { fn [< $f _ $t >](i: RandIter<$t>, k: u16) -> () { $f(i, k) } } })+ }; } generic_test!(k_smallest_sort, u8, u16, u32, u64, i8, i16, i32, i64); generic_test!(k_smallest_by_sort, u8, u16, u32, u64, i8, i16, i32, i64); #[test] fn sorted_by_key() { let sc = [3, 4, 1, 2].iter().cloned().sorted_by_key(|&x| x); it::assert_equal(sc, vec![1, 2, 3, 4]); let v = (0..5).sorted_by_key(|&x| -x); it::assert_equal(v, vec![4, 3, 2, 1, 0]); } #[test] fn sorted_by_cached_key() { // Track calls to key function let mut ncalls = 0; let sorted = [3, 4, 1, 2].iter().cloned().sorted_by_cached_key(|&x| { ncalls += 1; x.to_string() }); it::assert_equal(sorted, vec![1, 2, 3, 4]); // Check key function called once per element assert_eq!(ncalls, 4); let mut ncalls = 0; let sorted = (0..5).sorted_by_cached_key(|&x| { ncalls += 1; -x }); it::assert_equal(sorted, vec![4, 3, 2, 1, 0]); // Check key function called once per element assert_eq!(ncalls, 5); } #[test] fn test_multipeek() { let nums = vec![1u8, 2, 3, 4, 5]; let mp = multipeek(nums.iter().copied()); assert_eq!(nums, mp.collect::>()); let mut mp = multipeek(nums.iter().copied()); assert_eq!(mp.peek(), Some(&1)); assert_eq!(mp.next(), Some(1)); assert_eq!(mp.peek(), Some(&2)); assert_eq!(mp.peek(), Some(&3)); assert_eq!(mp.next(), Some(2)); assert_eq!(mp.peek(), Some(&3)); assert_eq!(mp.peek(), Some(&4)); assert_eq!(mp.peek(), Some(&5)); assert_eq!(mp.peek(), None); assert_eq!(mp.next(), Some(3)); assert_eq!(mp.next(), Some(4)); assert_eq!(mp.peek(), Some(&5)); assert_eq!(mp.peek(), None); assert_eq!(mp.next(), Some(5)); assert_eq!(mp.next(), None); assert_eq!(mp.peek(), None); } #[test] fn test_multipeek_reset() { let data = [1, 2, 3, 4]; let mut mp = multipeek(cloned(&data)); assert_eq!(mp.peek(), Some(&1)); assert_eq!(mp.next(), Some(1)); assert_eq!(mp.peek(), Some(&2)); assert_eq!(mp.peek(), Some(&3)); mp.reset_peek(); assert_eq!(mp.peek(), Some(&2)); assert_eq!(mp.next(), Some(2)); } #[test] fn test_multipeek_peeking_next() { use crate::it::PeekingNext; let nums = [1u8, 2, 3, 4, 5, 6, 7]; let mut mp = multipeek(nums.iter().copied()); assert_eq!(mp.peeking_next(|&x| x != 0), Some(1)); assert_eq!(mp.next(), Some(2)); assert_eq!(mp.peek(), Some(&3)); assert_eq!(mp.peek(), Some(&4)); assert_eq!(mp.peeking_next(|&x| x == 3), Some(3)); assert_eq!(mp.peek(), Some(&4)); assert_eq!(mp.peeking_next(|&x| x != 4), None); assert_eq!(mp.peeking_next(|&x| x == 4), Some(4)); assert_eq!(mp.peek(), Some(&5)); assert_eq!(mp.peek(), Some(&6)); assert_eq!(mp.peeking_next(|&x| x != 5), None); assert_eq!(mp.peek(), Some(&7)); assert_eq!(mp.peeking_next(|&x| x == 5), Some(5)); assert_eq!(mp.peeking_next(|&x| x == 6), Some(6)); assert_eq!(mp.peek(), Some(&7)); assert_eq!(mp.peek(), None); assert_eq!(mp.next(), Some(7)); assert_eq!(mp.peek(), None); } #[test] fn test_repeat_n_peeking_next() { use crate::it::PeekingNext; let mut rn = repeat_n(0, 5); assert_eq!(rn.peeking_next(|&x| x != 0), None); assert_eq!(rn.peeking_next(|&x| x <= 0), Some(0)); assert_eq!(rn.next(), Some(0)); assert_eq!(rn.peeking_next(|&x| x <= 0), Some(0)); assert_eq!(rn.peeking_next(|&x| x != 0), None); assert_eq!(rn.peeking_next(|&x| x >= 0), Some(0)); assert_eq!(rn.next(), Some(0)); assert_eq!(rn.peeking_next(|&x| x <= 0), None); assert_eq!(rn.next(), None); } #[test] fn test_peek_nth() { let nums = vec![1u8, 2, 3, 4, 5]; let iter = peek_nth(nums.iter().copied()); assert_eq!(nums, iter.collect::>()); let mut iter = peek_nth(nums.iter().copied()); assert_eq!(iter.peek_nth(0), Some(&1)); assert_eq!(iter.peek_nth(0), Some(&1)); assert_eq!(iter.next(), Some(1)); 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(&3)); assert_eq!(iter.peek_nth(1), Some(&4)); assert_eq!(iter.peek_nth(2), Some(&5)); assert_eq!(iter.peek_nth(3), None); assert_eq!(iter.next(), Some(3)); assert_eq!(iter.next(), Some(4)); assert_eq!(iter.peek_nth(0), Some(&5)); assert_eq!(iter.peek_nth(1), None); assert_eq!(iter.next(), Some(5)); assert_eq!(iter.next(), None); assert_eq!(iter.peek_nth(0), None); assert_eq!(iter.peek_nth(1), None); } #[test] fn test_peek_nth_peeking_next() { use it::PeekingNext; let nums = [1u8, 2, 3, 4, 5, 6, 7]; let mut iter = peek_nth(nums.iter().copied()); assert_eq!(iter.peeking_next(|&x| x != 0), Some(1)); assert_eq!(iter.next(), Some(2)); assert_eq!(iter.peek_nth(0), Some(&3)); assert_eq!(iter.peek_nth(1), Some(&4)); assert_eq!(iter.peeking_next(|&x| x == 3), Some(3)); assert_eq!(iter.peek(), Some(&4)); assert_eq!(iter.peeking_next(|&x| x != 4), None); assert_eq!(iter.peeking_next(|&x| x == 4), Some(4)); assert_eq!(iter.peek_nth(0), Some(&5)); assert_eq!(iter.peek_nth(1), Some(&6)); assert_eq!(iter.peeking_next(|&x| x != 5), None); assert_eq!(iter.peek(), Some(&5)); assert_eq!(iter.peeking_next(|&x| x == 5), Some(5)); assert_eq!(iter.peeking_next(|&x| x == 6), Some(6)); assert_eq!(iter.peek_nth(0), Some(&7)); assert_eq!(iter.peek_nth(1), None); assert_eq!(iter.next(), Some(7)); assert_eq!(iter.peek(), None); } #[test] fn test_peek_nth_next_if() { let nums = [1u8, 2, 3, 4, 5, 6, 7]; let mut iter = peek_nth(nums.iter().copied()); assert_eq!(iter.next_if(|&x| x != 0), Some(1)); assert_eq!(iter.next(), Some(2)); assert_eq!(iter.peek_nth(0), Some(&3)); assert_eq!(iter.peek_nth(1), Some(&4)); assert_eq!(iter.next_if_eq(&3), Some(3)); assert_eq!(iter.peek(), Some(&4)); assert_eq!(iter.next_if(|&x| x != 4), None); assert_eq!(iter.next_if_eq(&4), Some(4)); assert_eq!(iter.peek_nth(0), Some(&5)); assert_eq!(iter.peek_nth(1), Some(&6)); assert_eq!(iter.next_if(|&x| x != 5), None); assert_eq!(iter.peek(), Some(&5)); assert_eq!(iter.next_if(|&x| x % 2 == 1), Some(5)); assert_eq!(iter.next_if_eq(&6), Some(6)); assert_eq!(iter.peek_nth(0), Some(&7)); assert_eq!(iter.peek_nth(1), None); assert_eq!(iter.next(), Some(7)); assert_eq!(iter.peek(), None); } #[test] fn pad_using() { it::assert_equal((0..0).pad_using(1, |_| 1), 1..2); let v: Vec = vec![0, 1, 2]; let r = v.into_iter().pad_using(5, |n| n); it::assert_equal(r, vec![0, 1, 2, 3, 4]); let v: Vec = vec![0, 1, 2]; let r = v.into_iter().pad_using(1, |_| panic!()); it::assert_equal(r, vec![0, 1, 2]); } #[test] fn chunk_by() { for (ch1, sub) in &"AABBCCC".chars().chunk_by(|&x| x) { for ch2 in sub { assert_eq!(ch1, ch2); } } for (ch1, sub) in &"AAABBBCCCCDDDD".chars().chunk_by(|&x| x) { for ch2 in sub { assert_eq!(ch1, ch2); if ch1 == 'C' { break; } } } let toupper = |ch: &char| ch.to_uppercase().next().unwrap(); // try all possible orderings for indices in permutohedron::Heap::new(&mut [0, 1, 2, 3]) { let chunks = "AaaBbbccCcDDDD".chars().chunk_by(&toupper); let mut subs = chunks.into_iter().collect_vec(); for &idx in &indices[..] { let (key, text) = match idx { 0 => ('A', "Aaa".chars()), 1 => ('B', "Bbb".chars()), 2 => ('C', "ccCc".chars()), 3 => ('D', "DDDD".chars()), _ => unreachable!(), }; assert_eq!(key, subs[idx].0); it::assert_equal(&mut subs[idx].1, text); } } let chunks = "AAABBBCCCCDDDD".chars().chunk_by(|&x| x); let mut subs = chunks.into_iter().map(|(_, g)| g).collect_vec(); let sd = subs.pop().unwrap(); let sc = subs.pop().unwrap(); let sb = subs.pop().unwrap(); let sa = subs.pop().unwrap(); for (a, b, c, d) in multizip((sa, sb, sc, sd)) { assert_eq!(a, 'A'); assert_eq!(b, 'B'); assert_eq!(c, 'C'); assert_eq!(d, 'D'); } // check that the key closure is called exactly n times { let mut ntimes = 0; let text = "AABCCC"; for (_, sub) in &text.chars().chunk_by(|&x| { ntimes += 1; x }) { for _ in sub {} } assert_eq!(ntimes, text.len()); } { let mut ntimes = 0; let text = "AABCCC"; for _ in &text.chars().chunk_by(|&x| { ntimes += 1; x }) {} assert_eq!(ntimes, text.len()); } { let text = "ABCCCDEEFGHIJJKK"; let gr = text.chars().chunk_by(|&x| x); it::assert_equal(gr.into_iter().flat_map(|(_, sub)| sub), text.chars()); } } #[test] fn chunk_by_lazy_2() { let data = [0, 1]; let chunks = data.iter().chunk_by(|k| *k); let gs = chunks.into_iter().collect_vec(); it::assert_equal(data.iter(), gs.into_iter().flat_map(|(_k, g)| g)); let data = [0, 1, 1, 0, 0]; let chunks = data.iter().chunk_by(|k| *k); let mut gs = chunks.into_iter().collect_vec(); gs[1..].reverse(); it::assert_equal(&[0, 0, 0, 1, 1], gs.into_iter().flat_map(|(_, g)| g)); let grouper = data.iter().chunk_by(|k| *k); let mut chunks = Vec::new(); for (k, chunk) in &grouper { if *k == 1 { chunks.push(chunk); } } it::assert_equal(&mut chunks[0], &[1, 1]); let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; let grouper = data.iter().chunk_by(|k| *k); let mut chunks = Vec::new(); for (i, (_, chunk)) in grouper.into_iter().enumerate() { if i < 2 { chunks.push(chunk); } else if i < 4 { for _ in chunk {} } else { chunks.push(chunk); } } it::assert_equal(&mut chunks[0], &[0, 0, 0]); it::assert_equal(&mut chunks[1], &[1, 1]); it::assert_equal(&mut chunks[2], &[3, 3]); let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; let mut i = 0; let grouper = data.iter().chunk_by(move |_| { let k = i / 3; i += 1; k }); for (i, chunk) in &grouper { match i { 0 => it::assert_equal(chunk, &[0, 0, 0]), 1 => it::assert_equal(chunk, &[1, 1, 0]), 2 => it::assert_equal(chunk, &[0, 2, 2]), 3 => it::assert_equal(chunk, &[3, 3]), _ => unreachable!(), } } } #[test] fn chunk_by_lazy_3() { // test consuming each chunk on the lap after it was produced let data = [0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2]; let grouper = data.iter().chunk_by(|elt| *elt); let mut last = None; for (key, chunk) in &grouper { if let Some(gr) = last.take() { for elt in gr { assert!(elt != key && i32::abs(elt - key) == 1); } } last = Some(chunk); } } #[test] fn chunks() { let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; let grouper = data.iter().chunks(3); for (i, chunk) in grouper.into_iter().enumerate() { match i { 0 => it::assert_equal(chunk, &[0, 0, 0]), 1 => it::assert_equal(chunk, &[1, 1, 0]), 2 => it::assert_equal(chunk, &[0, 2, 2]), 3 => it::assert_equal(chunk, &[3, 3]), _ => unreachable!(), } } } #[test] fn concat_empty() { let data: Vec> = Vec::new(); assert_eq!(data.into_iter().concat(), Vec::new()) } #[test] fn concat_non_empty() { let data = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; assert_eq!(data.into_iter().concat(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9]) } #[test] fn combinations() { assert!((1..3).combinations(5).next().is_none()); let it = (1..3).combinations(2); it::assert_equal(it, vec![vec![1, 2]]); let it = (1..5).combinations(2); it::assert_equal( it, vec![ vec![1, 2], vec![1, 3], vec![1, 4], vec![2, 3], vec![2, 4], vec![3, 4], ], ); it::assert_equal((0..0).tuple_combinations::<(_, _)>(), >::new()); it::assert_equal((0..1).tuple_combinations::<(_, _)>(), >::new()); it::assert_equal((0..2).tuple_combinations::<(_, _)>(), vec![(0, 1)]); it::assert_equal((0..0).combinations(2), >>::new()); it::assert_equal((0..1).combinations(1), vec![vec![0]]); it::assert_equal((0..2).combinations(1), vec![vec![0], vec![1]]); it::assert_equal((0..2).combinations(2), vec![vec![0, 1]]); } #[test] fn combinations_of_too_short() { for i in 1..10 { assert!((0..0).combinations(i).next().is_none()); assert!((0..i - 1).combinations(i).next().is_none()); } } #[test] fn combinations_zero() { it::assert_equal((1..3).combinations(0), vec![vec![]]); it::assert_equal((0..0).combinations(0), vec![vec![]]); } fn binomial(n: usize, k: usize) -> usize { if k > n { 0 } else { (n - k + 1..=n).product::() / (1..=k).product::() } } #[test] fn combinations_range_count() { for n in 0..=10 { for k in 0..=10 { let len = binomial(n, k); let mut it = (0..n).combinations(k); assert_eq!(len, it.clone().count()); assert_eq!(len, it.size_hint().0); assert_eq!(Some(len), it.size_hint().1); for count in (0..len).rev() { let elem = it.next(); assert!(elem.is_some()); assert_eq!(count, it.clone().count()); assert_eq!(count, it.size_hint().0); assert_eq!(Some(count), it.size_hint().1); } let should_be_none = it.next(); assert!(should_be_none.is_none()); } } } #[test] fn combinations_inexact_size_hints() { for k in 0..=10 { let mut numbers = (0..18).filter(|i| i % 2 == 0); // 9 elements let mut it = numbers.clone().combinations(k); let real_n = numbers.clone().count(); let len = binomial(real_n, k); assert_eq!(len, it.clone().count()); let mut nb_loaded = 0; let sh = numbers.size_hint(); assert_eq!(binomial(sh.0 + nb_loaded, k), it.size_hint().0); assert_eq!(sh.1.map(|n| binomial(n + nb_loaded, k)), it.size_hint().1); for next_count in 1..=len { let elem = it.next(); assert!(elem.is_some()); assert_eq!(len - next_count, it.clone().count()); if next_count == 1 { // The very first time, the lazy buffer is prefilled. nb_loaded = numbers.by_ref().take(k).count(); } else { // Then it loads one item each time until exhausted. let nb = numbers.next(); if nb.is_some() { nb_loaded += 1; } } let sh = numbers.size_hint(); if next_count > real_n - k + 1 { assert_eq!(0, sh.0); assert_eq!(Some(0), sh.1); assert_eq!(real_n, nb_loaded); // Once it's fully loaded, size hints of `it` are exacts. } assert_eq!(binomial(sh.0 + nb_loaded, k) - next_count, it.size_hint().0); assert_eq!( sh.1.map(|n| binomial(n + nb_loaded, k) - next_count), it.size_hint().1 ); } let should_be_none = it.next(); assert!(should_be_none.is_none()); } } #[test] fn permutations_zero() { it::assert_equal((1..3).permutations(0), vec![vec![]]); it::assert_equal((0..0).permutations(0), vec![vec![]]); } #[test] fn permutations_range_count() { for n in 0..=7 { for k in 0..=7 { let len = if k <= n { (n - k + 1..=n).product() } else { 0 }; let mut it = (0..n).permutations(k); assert_eq!(len, it.clone().count()); assert_eq!(len, it.size_hint().0); assert_eq!(Some(len), it.size_hint().1); for count in (0..len).rev() { let elem = it.next(); assert!(elem.is_some()); assert_eq!(count, it.clone().count()); assert_eq!(count, it.size_hint().0); assert_eq!(Some(count), it.size_hint().1); } let should_be_none = it.next(); assert!(should_be_none.is_none()); } } } #[test] fn permutations_overflowed_size_hints() { let mut it = std::iter::repeat(()).permutations(2); assert_eq!(it.size_hint().0, usize::MAX); assert_eq!(it.size_hint().1, None); for nb_generated in 1..=1000 { it.next(); assert!(it.size_hint().0 >= usize::MAX - nb_generated); assert_eq!(it.size_hint().1, None); } } #[test] fn combinations_with_replacement() { // Pool smaller than n it::assert_equal((0..1).combinations_with_replacement(2), vec![vec![0, 0]]); // Pool larger than n it::assert_equal( (0..3).combinations_with_replacement(2), vec![ vec![0, 0], vec![0, 1], vec![0, 2], vec![1, 1], vec![1, 2], vec![2, 2], ], ); // Zero size it::assert_equal((0..3).combinations_with_replacement(0), vec![vec![]]); // Zero size on empty pool it::assert_equal((0..0).combinations_with_replacement(0), vec![vec![]]); // Empty pool it::assert_equal( (0..0).combinations_with_replacement(2), >>::new(), ); } #[test] fn combinations_with_replacement_range_count() { for n in 0..=7 { for k in 0..=7 { let len = binomial(usize::saturating_sub(n + k, 1), k); let mut it = (0..n).combinations_with_replacement(k); assert_eq!(len, it.clone().count()); assert_eq!(len, it.size_hint().0); assert_eq!(Some(len), it.size_hint().1); for count in (0..len).rev() { let elem = it.next(); assert!(elem.is_some()); assert_eq!(count, it.clone().count()); assert_eq!(count, it.size_hint().0); assert_eq!(Some(count), it.size_hint().1); } let should_be_none = it.next(); assert!(should_be_none.is_none()); } } } #[test] fn powerset() { it::assert_equal((0..0).powerset(), vec![vec![]]); it::assert_equal((0..1).powerset(), vec![vec![], vec![0]]); it::assert_equal( (0..2).powerset(), vec![vec![], vec![0], vec![1], vec![0, 1]], ); it::assert_equal( (0..3).powerset(), vec![ vec![], vec![0], vec![1], vec![2], vec![0, 1], vec![0, 2], vec![1, 2], vec![0, 1, 2], ], ); assert_eq!((0..4).powerset().count(), 1 << 4); assert_eq!((0..8).powerset().count(), 1 << 8); assert_eq!((0..16).powerset().count(), 1 << 16); for n in 0..=10 { let mut it = (0..n).powerset(); let len = 2_usize.pow(n); assert_eq!(len, it.clone().count()); assert_eq!(len, it.size_hint().0); assert_eq!(Some(len), it.size_hint().1); for count in (0..len).rev() { let elem = it.next(); assert!(elem.is_some()); assert_eq!(count, it.clone().count()); assert_eq!(count, it.size_hint().0); assert_eq!(Some(count), it.size_hint().1); } let should_be_none = it.next(); assert!(should_be_none.is_none()); } } #[test] fn diff_mismatch() { let a = [1, 2, 3, 4]; let b = vec![1.0, 5.0, 3.0, 4.0]; let b_map = b.into_iter().map(|f| f as i32); let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); assert!(match diff { Some(it::Diff::FirstMismatch(1, _, from_diff)) => from_diff.collect::>() == vec![5, 3, 4], _ => false, }); } #[test] fn diff_longer() { let a = [1, 2, 3, 4]; let b = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; let b_map = b.into_iter().map(|f| f as i32); let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); assert!(match diff { Some(it::Diff::Longer(_, remaining)) => remaining.collect::>() == vec![5, 6], _ => false, }); } #[test] fn diff_shorter() { let a = [1, 2, 3, 4]; let b = vec![1.0, 2.0]; let b_map = b.into_iter().map(|f| f as i32); let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); assert!(match diff { Some(it::Diff::Shorter(len, _)) => len == 2, _ => false, }); } #[test] fn extrema_set() { use std::cmp::Ordering; // A peculiar type: Equality compares both tuple items, but ordering only the // first item. Used to distinguish equal elements. #[derive(Clone, Debug, PartialEq, Eq)] struct Val(u32, u32); impl PartialOrd for Val { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Val { fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) } } assert_eq!(None::.iter().min_set(), Vec::<&u32>::new()); assert_eq!(None::.iter().max_set(), Vec::<&u32>::new()); assert_eq!(Some(1u32).iter().min_set(), vec![&1]); assert_eq!(Some(1u32).iter().max_set(), vec![&1]); let data = [Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)]; let min_set = data.iter().min_set(); assert_eq!(min_set, vec![&Val(0, 1), &Val(0, 2)]); let min_set_by_key = data.iter().min_set_by_key(|v| v.1); assert_eq!(min_set_by_key, vec![&Val(2, 0), &Val(1, 0)]); let min_set_by = data.iter().min_set_by(|x, y| x.1.cmp(&y.1)); assert_eq!(min_set_by, vec![&Val(2, 0), &Val(1, 0)]); let max_set = data.iter().max_set(); assert_eq!(max_set, vec![&Val(2, 0), &Val(2, 1)]); let max_set_by_key = data.iter().max_set_by_key(|v| v.1); assert_eq!(max_set_by_key, vec![&Val(0, 2)]); let max_set_by = data.iter().max_set_by(|x, y| x.1.cmp(&y.1)); assert_eq!(max_set_by, vec![&Val(0, 2)]); } #[test] fn minmax() { use crate::it::MinMaxResult; use std::cmp::Ordering; // A peculiar type: Equality compares both tuple items, but ordering only the // first item. This is so we can check the stability property easily. #[derive(Clone, Debug, PartialEq, Eq)] struct Val(u32, u32); impl PartialOrd for Val { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Val { fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) } } assert_eq!( None::>.iter().minmax(), MinMaxResult::NoElements ); assert_eq!(Some(1u32).iter().minmax(), MinMaxResult::OneElement(&1)); let data = [Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)]; let minmax = data.iter().minmax(); assert_eq!(minmax, MinMaxResult::MinMax(&Val(0, 1), &Val(2, 1))); let (min, max) = data.iter().minmax_by_key(|v| v.1).into_option().unwrap(); assert_eq!(min, &Val(2, 0)); assert_eq!(max, &Val(0, 2)); let (min, max) = data .iter() .minmax_by(|x, y| x.1.cmp(&y.1)) .into_option() .unwrap(); assert_eq!(min, &Val(2, 0)); assert_eq!(max, &Val(0, 2)); } #[test] fn format() { let data = [0, 1, 2, 3]; let ans1 = "0, 1, 2, 3"; let ans2 = "0--1--2--3"; let t1 = format!("{}", data.iter().format(", ")); assert_eq!(t1, ans1); let t2 = format!("{:?}", data.iter().format("--")); assert_eq!(t2, ans2); let dataf = [1.1, 5.71828, -22.]; let t3 = format!("{:.2e}", dataf.iter().format(", ")); assert_eq!(t3, "1.10e0, 5.72e0, -2.20e1"); } #[test] fn while_some() { let ns = (1..10) .map(|x| if x % 5 != 0 { Some(x) } else { None }) .while_some(); it::assert_equal(ns, vec![1, 2, 3, 4]); } #[test] fn fold_while() { let mut iterations = 0; let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let sum = vec .into_iter() .fold_while(0, |acc, item| { iterations += 1; let new_sum = acc + item; if new_sum <= 20 { FoldWhile::Continue(new_sum) } else { FoldWhile::Done(acc) } }) .into_inner(); assert_eq!(iterations, 6); assert_eq!(sum, 15); } #[test] fn tree_reduce() { let x = [ "", "0", "0 1 x", "0 1 x 2 x", "0 1 x 2 3 x x", "0 1 x 2 3 x x 4 x", "0 1 x 2 3 x x 4 5 x x", "0 1 x 2 3 x x 4 5 x 6 x x", "0 1 x 2 3 x x 4 5 x 6 7 x x x", "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 x", "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x x", "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 x x", "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x x", "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 x x", "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x x x", "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 x x x", "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 15 x x x x", ]; for (i, &s) in x.iter().enumerate() { let expected = if s.is_empty() { None } else { Some(s.to_string()) }; let num_strings = (0..i).map(|x| x.to_string()); let actual = num_strings.tree_reduce(|a, b| format!("{} {} x", a, b)); assert_eq!(actual, expected); } } #[test] fn exactly_one_question_mark_syntax_works() { exactly_one_question_mark_return().unwrap_err(); } fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError>> { [].iter().exactly_one()?; Ok(()) } #[test] fn multiunzip() { let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2), (3, 4, 5), (6, 7, 8)] .iter() .cloned() .multiunzip(); assert_eq!((a, b, c), (vec![0, 3, 6], vec![1, 4, 7], vec![2, 5, 8])); let (): () = [(), (), ()].iter().cloned().multiunzip(); #[allow(clippy::type_complexity)] let t: ( Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, ) = [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)] .iter() .cloned() .multiunzip(); assert_eq!( t, ( vec![0], vec![1], vec![2], vec![3], vec![4], vec![5], vec![6], vec![7], vec![8], vec![9], vec![10], vec![11] ) ); } itertools-0.13.0/tests/tuples.rs000064400000000000000000000052231046102023000147570ustar 00000000000000use itertools::Itertools; #[test] fn tuples() { let v = [1, 2, 3, 4, 5]; let mut iter = v.iter().cloned().tuples(); assert_eq!(Some((1,)), iter.next()); assert_eq!(Some((2,)), iter.next()); assert_eq!(Some((3,)), iter.next()); assert_eq!(Some((4,)), iter.next()); assert_eq!(Some((5,)), iter.next()); assert_eq!(None, iter.next()); assert_eq!(None, iter.into_buffer().next()); let mut iter = v.iter().cloned().tuples(); assert_eq!(Some((1, 2)), iter.next()); assert_eq!(Some((3, 4)), iter.next()); assert_eq!(None, iter.next()); itertools::assert_equal(vec![5], iter.into_buffer()); let mut iter = v.iter().cloned().tuples(); assert_eq!(Some((1, 2, 3)), iter.next()); assert_eq!(None, iter.next()); itertools::assert_equal(vec![4, 5], iter.into_buffer()); let mut iter = v.iter().cloned().tuples(); assert_eq!(Some((1, 2, 3, 4)), iter.next()); assert_eq!(None, iter.next()); itertools::assert_equal(vec![5], iter.into_buffer()); } #[test] fn tuple_windows() { let v = [1, 2, 3, 4, 5]; let mut iter = v.iter().cloned().tuple_windows(); assert_eq!(Some((1,)), iter.next()); assert_eq!(Some((2,)), iter.next()); assert_eq!(Some((3,)), iter.next()); let mut iter = v.iter().cloned().tuple_windows(); assert_eq!(Some((1, 2)), iter.next()); assert_eq!(Some((2, 3)), iter.next()); assert_eq!(Some((3, 4)), iter.next()); assert_eq!(Some((4, 5)), iter.next()); assert_eq!(None, iter.next()); let mut iter = v.iter().cloned().tuple_windows(); assert_eq!(Some((1, 2, 3)), iter.next()); assert_eq!(Some((2, 3, 4)), iter.next()); assert_eq!(Some((3, 4, 5)), iter.next()); assert_eq!(None, iter.next()); let mut iter = v.iter().cloned().tuple_windows(); assert_eq!(Some((1, 2, 3, 4)), iter.next()); assert_eq!(Some((2, 3, 4, 5)), iter.next()); assert_eq!(None, iter.next()); let v = [1, 2, 3]; let mut iter = v.iter().cloned().tuple_windows::<(_, _, _, _)>(); assert_eq!(None, iter.next()); } #[test] fn next_tuple() { let v = [1, 2, 3, 4, 5]; let mut iter = v.iter(); assert_eq!(iter.next_tuple().map(|(&x, &y)| (x, y)), Some((1, 2))); assert_eq!(iter.next_tuple().map(|(&x, &y)| (x, y)), Some((3, 4))); assert_eq!(iter.next_tuple::<(_, _)>(), None); } #[test] fn collect_tuple() { let v = [1, 2]; let iter = v.iter().cloned(); assert_eq!(iter.collect_tuple(), Some((1, 2))); let v = [1]; let iter = v.iter().cloned(); assert_eq!(iter.collect_tuple::<(_, _)>(), None); let v = [1, 2, 3]; let iter = v.iter().cloned(); assert_eq!(iter.collect_tuple::<(_, _)>(), None); } itertools-0.13.0/tests/zip.rs000064400000000000000000000031671046102023000142520ustar 00000000000000use 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()) .zip_longest(b.iter().cloned()); itertools::assert_equal(unfused, vec![Both(1, 1), Right(2), Right(3)]); } #[test] fn test_zip_longest_size_hint() { let c = (1..10).cycle(); 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!(v.iter().zip_longest(v2.iter()).size_hint(), (10, Some(10))); } #[test] fn test_double_ended_zip_longest() { let xs = [1, 2, 3, 4, 5, 6]; let ys = [1, 2, 3, 7]; let a = xs.iter().copied(); let b = ys.iter().copied(); let mut it = a.zip_longest(b); assert_eq!(it.next(), Some(Both(1, 1))); assert_eq!(it.next(), Some(Both(2, 2))); assert_eq!(it.next_back(), Some(Left(6))); assert_eq!(it.next_back(), Some(Left(5))); assert_eq!(it.next_back(), Some(Both(4, 7))); assert_eq!(it.next(), Some(Both(3, 3))); assert_eq!(it.next(), None); } #[test] fn test_double_ended_zip() { let xs = [1, 2, 3, 4, 5, 6]; let ys = [1, 2, 3, 7]; let a = xs.iter().copied(); let b = ys.iter().copied(); let mut it = multizip((a, b)); assert_eq!(it.next_back(), Some((4, 7))); assert_eq!(it.next_back(), Some((3, 3))); assert_eq!(it.next_back(), Some((2, 2))); assert_eq!(it.next_back(), Some((1, 1))); assert_eq!(it.next_back(), None); }