зеркало из https://github.com/mozilla/gecko-dev.git
No bug - Revendor rust dependencies
This commit is contained in:
Родитель
a13de494f4
Коммит
0e627abe60
|
@ -0,0 +1 @@
|
|||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".travis.yml":"5edd53edc70be60b7c6797204b540780d0ca2550d5b073eb99ee5255f3059682","Cargo.toml":"ba266ea33473c807fffe8e176bd1ec4297f5eb0b5fb583387f504ceb6f3fa896","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","Makefile":"13f1c5b88a7b946b5813f7231df2933b6b19b223e9e2d3fa63ad681192f984b5","README.rst":"2764f3dec0b6bc8270fdad6b765354b99da9996dacb58e2c2f706621d223c5ce","benches/bench1.rs":"982de314e181dc6d4da9530d37b72c4f8d8a02e3e204a90f91ea72bb22937cf7","benches/extra/mod.rs":"4c5b03e74fc5b02383500c9da9fd6550262706ee569d70d085700f6d0b5749ba","benches/extra/zipslices.rs":"108dd488de366b2d83fb6bcc603ecbf9a017e165ac19d03440074fa244af3fb2","benches/tuple_combinations.rs":"8c14e9341d92e5cfd5f9a067d11088b37b003e82635d1ab3a8e5290e3ef83eed","benches/tuples.rs":"412a952f08bb03695952d5cfd57949dcf28be8b99e3c6653994bdb8af9654653","custom.css":"03d2316d325a09f03f0fae54d24b64f784518a8249432edbd60e01436be900d5","examples/iris.data":"596ffd580471ca4d4880f8e439c7281f3b50d8249a5960353cb200b1490f63a0","examples/iris.rs":"74387bb7e81d4b975f796aa855788ff5b8e3c9ff69c42898ecf76c4a74e8504b","src/adaptors/mod.rs":"af2c2b96fb4d1c6024af78d2faf766e9870180622e1c5b603d1799564fffaa34","src/adaptors/multipeek.rs":"2b2f3b796b5664e3561a9ffabd16ea2971020cea13e1fb2ce5fd6e995934b4ab","src/cons_tuples_impl.rs":"371d58a3e0aaa9552219450863afd685b75fb7d4f2be69e14138e2c5d602591c","src/diff.rs":"921e2b867d7b32ffedc72a5eb780811322d14d1e0883a608b9028a2afcad0df2","src/format.rs":"412fbe02f12311c6fbcec1044f57ad6991783f5a3f323b9c391accfe4915106f","src/free.rs":"2c3e853dda297f4227cd7ecd37402a99c36169884ffbdfe823d296b0c8d16d33","src/groupbylazy.rs":"62957f8b15dd3083ac7077edc357a0bc0954828f467b60697a8801de57028d2d","src/impl_macros.rs":"eb0bb3f70ec1bcaffa6110ae4134c777951ed1e5f48d8c811dbf0a597dc48faa","src/intersperse.rs":"8338a5b61ff5d2eb306ef7142578406f3ae4e4c7b2a8adcaa293a07c2299735b","src/kmerge_impl.rs":"e7902ccf6b811417e4dd9314964944beb08c2908e76396ff977227a7a350a16f","src/lib.rs":"adb75487a37790bd086880b44fc65eff990ea3d7897ae323adf6967d87be8cf7","src/minmax.rs":"4668a7f824fbc133599f43ffb6f7283e5bd603e07df2d8176abc6f25d6af9db0","src/pad_tail.rs":"2b4c8961a18bc9685cfa4ac674b77f1f313e953e1398d08d681255b9e5f60ad7","src/peeking_take_while.rs":"e44361e2793db239d367ae3d052376b4fbc9dec472e7f89f10347cdd1e197de4","src/rciter_impl.rs":"9ecde85b56122db166ffd0a6cc8e9d819b3a2c4322c880ccd9bf0b77f5a8c985","src/repeatn.rs":"e60885e395eb8a348248fe8c0d05c325c74056198cc0242d25530c93520f9b25","src/size_hint.rs":"c624ab3ff04836372d98cbd597be134da283280be5378d06747759febe5c2ee7","src/sources.rs":"d6d4ac8980ede2975363a27431b6584af43cc495c10a21d6dfe2dcee6393716d","src/tee.rs":"86b1da0697360091ae5de53a64cd8efb927b88c41c7fff5dec5814702c5bac31","src/tuple_impl.rs":"767624e7c7db930fabf22542a3b48926e48a0b485bdbf03cfa131286bc6988de","src/with_position.rs":"8af04f26d9b89a2597fc10ad52c0feb61cb852dbf988b93d397f3300a6e70965","src/zip_eq_impl.rs":"95e493deeadd640751f5c49f55008bd31218978f38396967bc4a356f6f11d209","src/zip_longest.rs":"e463c58d5feebe5a0ed7964705ffedc6ec9a89ca2567a374cc8ceaf206249d5a","src/ziptuple.rs":"463f102add23ffa0702ec5ef110aae6c132267151fad66566b724fff7ebfa458","tests/peeking_take_while.rs":"a2ae6474e09620a47bb8a6e3c62929261e72c52881370adb2d22e89aa9e9aec8","tests/quick.rs":"a4f9faafbf2ee7fa76048634c84422c24e31a8c65489964017d1b100f9e1bbc0","tests/tests.rs":"a1fa1f0e5e70e6ef78ffce2e145422941e5aea18a1a5b66ef7365e7962511368","tests/tuples.rs":"f8c8892c3c44dde0910eaf26f1756ddc62e264498245e5b0070a6912dc8c101c","tests/zip.rs":"00749b70157da84dc3f0681b5fb8aaabfe412ab930da5fbaa3b4571f4c662fe9"},"package":"4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"}
|
|
@ -0,0 +1,18 @@
|
|||
language: rust
|
||||
sudo: false
|
||||
matrix:
|
||||
include:
|
||||
- rust: 1.11.0
|
||||
- rust: stable
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
env:
|
||||
- BENCH=1
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
script:
|
||||
- |
|
||||
cargo build --verbose --features "$FEATURES" &&
|
||||
cargo test --verbose --features "$FEATURES" &&
|
||||
([ "$BENCH" != 1 ] || cargo bench --verbose --features "$FEATURES")
|
|
@ -0,0 +1,35 @@
|
|||
[package]
|
||||
name = "itertools"
|
||||
version = "0.5.10"
|
||||
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/bluss/rust-itertools"
|
||||
documentation = "https://docs.rs/itertools/"
|
||||
authors = ["bluss"]
|
||||
|
||||
description = "Extra iterator adaptors, iterator methods, free functions, and macros."
|
||||
|
||||
keywords = ["iterator", "data-structure", "zip", "product", "group-by"]
|
||||
categories = ["algorithms", "rust-patterns"]
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
either = { version = "1.0", default-features = false }
|
||||
|
||||
[dev-dependencies.quickcheck]
|
||||
version = "0.4"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies.permutohedron]
|
||||
version = "0.2"
|
||||
|
||||
[features]
|
||||
|
||||
[profile]
|
||||
bench = { debug = true }
|
||||
|
||||
[package.metadata.release]
|
||||
no-dev-version = true
|
|
@ -0,0 +1,201 @@
|
|||
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.
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (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.
|
|
@ -0,0 +1,34 @@
|
|||
DOCCRATES = itertools
|
||||
|
||||
# deps to delete the generated docs
|
||||
RMDOCS =
|
||||
|
||||
FEATURES =
|
||||
|
||||
VERSIONS = $(patsubst %,target/VERS/%,$(DOCCRATES))
|
||||
|
||||
docs: mkdocs subst $(RMDOCS)
|
||||
|
||||
# https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
|
||||
$(VERSIONS): Cargo.toml
|
||||
mkdir -p $(@D)
|
||||
cargo pkgid $(@F) | sed -e "s/.*#\(\|.*:\)//" > "$@"
|
||||
|
||||
$(DOCCRATES): %: target/VERS/%
|
||||
# Put in the crate version into the docs
|
||||
find ./doc/$@ -name "*.html" -exec sed -i -e "s/<title>\(.*\) - Rust/<title>$@ $(shell cat $<) - \1 - Rust/g" {} \;
|
||||
|
||||
subst: $(DOCCRATES)
|
||||
|
||||
mkdocs: Cargo.toml
|
||||
cargo doc --features=$(FEATURES) --no-deps
|
||||
rm -rf ./doc
|
||||
cp -r ./target/doc ./doc
|
||||
- cat ./custom.css >> doc/main.css
|
||||
|
||||
$(RMDOCS): mkdocs
|
||||
rm -r ./doc/$@
|
||||
sed -i "/searchIndex\['$@'\]/d" doc/search-index.js
|
||||
|
||||
|
||||
.PHONY: docs mkdocs subst $(DOCCRATES) $(RMDOCS)
|
|
@ -0,0 +1,377 @@
|
|||
|
||||
Itertools
|
||||
=========
|
||||
|
||||
Extra iterator adaptors, functions and macros. Requires Rust 1.11 or later.
|
||||
|
||||
Please read the `API documentation here`__
|
||||
|
||||
__ https://docs.rs/itertools/
|
||||
|
||||
|build_status|_ |crates|_
|
||||
|
||||
.. |build_status| image:: https://travis-ci.org/bluss/rust-itertools.svg?branch=master
|
||||
.. _build_status: https://travis-ci.org/bluss/rust-itertools
|
||||
|
||||
.. |crates| image:: http://meritbadge.herokuapp.com/itertools
|
||||
.. _crates: https://crates.io/crates/itertools
|
||||
|
||||
How to use with cargo:
|
||||
|
||||
.. code:: toml
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.5.9"
|
||||
|
||||
How to use in your crate:
|
||||
|
||||
.. code:: rust
|
||||
|
||||
#[macro_use] extern crate itertools;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
How to contribute:
|
||||
|
||||
- Fix a bug or implement a new thing
|
||||
- Include tests for your new feature, preferably a quickcheck test
|
||||
- Make a Pull Request
|
||||
|
||||
|
||||
Recent Changes
|
||||
--------------
|
||||
|
||||
- 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::<T>()`` that iterates by two, three or four elements at
|
||||
a time (where ``T`` is a tuple type).
|
||||
- Add ``.tuple_windows::<T>()`` that iterates using a window of the
|
||||
two, three or four most recent elements.
|
||||
- Add ``.next_tuple::<T>()`` 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 <http://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<L, R>``
|
||||
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*
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Dual-licensed to be compatible with the Rust project.
|
||||
|
||||
Licensed under the Apache License, Version 2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
|
||||
http://opensource.org/licenses/MIT, at your
|
||||
option. This file may not be copied, modified, or distributed
|
||||
except according to those terms.
|
|
@ -0,0 +1,651 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate itertools;
|
||||
|
||||
use test::{black_box};
|
||||
use itertools::Itertools;
|
||||
|
||||
use itertools::free::cloned;
|
||||
|
||||
use std::iter::repeat;
|
||||
use std::cmp;
|
||||
use std::ops::Add;
|
||||
|
||||
mod extra;
|
||||
|
||||
use extra::ZipSlices;
|
||||
|
||||
#[bench]
|
||||
fn slice_iter(b: &mut test::Bencher)
|
||||
{
|
||||
let xs: Vec<_> = repeat(1i32).take(20).collect();
|
||||
b.iter(|| for elt in xs.iter() {
|
||||
test::black_box(elt);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn slice_iter_rev(b: &mut test::Bencher)
|
||||
{
|
||||
let xs: Vec<_> = repeat(1i32).take(20).collect();
|
||||
b.iter(|| for elt in xs.iter().rev() {
|
||||
test::black_box(elt);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zip_default_zip(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![0; 1024];
|
||||
let ys = vec![0; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
b.iter(|| {
|
||||
for (&x, &y) in xs.iter().zip(&ys) {
|
||||
test::black_box(x);
|
||||
test::black_box(y);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipdot_i32_default_zip(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![2; 1024];
|
||||
let ys = vec![2; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
b.iter(|| {
|
||||
let mut s = 0;
|
||||
for (&x, &y) in xs.iter().zip(&ys) {
|
||||
s += x * y;
|
||||
}
|
||||
s
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipdot_f32_default_zip(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![2f32; 1024];
|
||||
let ys = vec![2f32; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
b.iter(|| {
|
||||
let mut s = 0.;
|
||||
for (&x, &y) in xs.iter().zip(&ys) {
|
||||
s += x * y;
|
||||
}
|
||||
s
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zip_default_zip3(b: &mut test::Bencher)
|
||||
{
|
||||
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);
|
||||
|
||||
b.iter(|| {
|
||||
for ((&x, &y), &z) in xs.iter().zip(&ys).zip(&zs) {
|
||||
test::black_box(x);
|
||||
test::black_box(y);
|
||||
test::black_box(z);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
#[bench]
|
||||
fn zip_slices_ziptuple(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![0; 1024];
|
||||
let ys = vec![0; 768];
|
||||
|
||||
b.iter(|| {
|
||||
let xs = black_box(&xs);
|
||||
let ys = black_box(&ys);
|
||||
for (&x, &y) in Zip::new((xs, ys)) {
|
||||
test::black_box(x);
|
||||
test::black_box(y);
|
||||
}
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
#[bench]
|
||||
fn zipslices(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![0; 1024];
|
||||
let ys = vec![0; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
b.iter(|| {
|
||||
for (&x, &y) in ZipSlices::new(&xs, &ys) {
|
||||
test::black_box(x);
|
||||
test::black_box(y);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipslices_mut(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![0; 1024];
|
||||
let ys = vec![0; 768];
|
||||
let xs = black_box(xs);
|
||||
let mut ys = black_box(ys);
|
||||
|
||||
b.iter(|| {
|
||||
for (&x, &mut y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) {
|
||||
test::black_box(x);
|
||||
test::black_box(y);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipdot_i32_zipslices(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![2; 1024];
|
||||
let ys = vec![2; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
b.iter(|| {
|
||||
let mut s = 0i32;
|
||||
for (&x, &y) in ZipSlices::new(&xs, &ys) {
|
||||
s += x * y;
|
||||
}
|
||||
s
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipdot_f32_zipslices(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![2f32; 1024];
|
||||
let ys = vec![2f32; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
b.iter(|| {
|
||||
let mut s = 0.;
|
||||
for (&x, &y) in ZipSlices::new(&xs, &ys) {
|
||||
s += x * y;
|
||||
}
|
||||
s
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
#[bench]
|
||||
fn zip_checked_counted_loop(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![0; 1024];
|
||||
let ys = vec![0; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
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];
|
||||
test::black_box(x);
|
||||
test::black_box(y);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipdot_i32_checked_counted_loop(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![2; 1024];
|
||||
let ys = vec![2; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipdot_f32_checked_counted_loop(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![2f32; 1024];
|
||||
let ys = vec![2f32; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipdot_f32_checked_counted_unrolled_loop(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![2f32; 1024];
|
||||
let ys = vec![2f32; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zip_unchecked_counted_loop(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![0; 1024];
|
||||
let ys = vec![0; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
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);
|
||||
test::black_box(x);
|
||||
test::black_box(y);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipdot_i32_unchecked_counted_loop(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![2; 1024];
|
||||
let ys = vec![2; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zipdot_f32_unchecked_counted_loop(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![2.; 1024];
|
||||
let ys = vec![2.; 768];
|
||||
let xs = black_box(xs);
|
||||
let ys = black_box(ys);
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn zip_unchecked_counted_loop3(b: &mut test::Bencher)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
test::black_box(x);
|
||||
test::black_box(y);
|
||||
test::black_box(z);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn group_by_lazy_1(b: &mut test::Bencher) {
|
||||
let mut data = vec![0; 1024];
|
||||
for (index, elt) in data.iter_mut().enumerate() {
|
||||
*elt = index / 10;
|
||||
}
|
||||
|
||||
let data = test::black_box(data);
|
||||
|
||||
b.iter(|| {
|
||||
for (_key, group) in &data.iter().group_by(|elt| **elt) {
|
||||
for elt in group {
|
||||
test::black_box(elt);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn group_by_lazy_2(b: &mut test::Bencher) {
|
||||
let mut data = vec![0; 1024];
|
||||
for (index, elt) in data.iter_mut().enumerate() {
|
||||
*elt = index / 2;
|
||||
}
|
||||
|
||||
let data = test::black_box(data);
|
||||
|
||||
b.iter(|| {
|
||||
for (_key, group) in &data.iter().group_by(|elt| **elt) {
|
||||
for elt in group {
|
||||
test::black_box(elt);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn slice_chunks(b: &mut test::Bencher) {
|
||||
let data = vec![0; 1024];
|
||||
|
||||
let data = test::black_box(data);
|
||||
let sz = test::black_box(10);
|
||||
|
||||
b.iter(|| {
|
||||
for group in data.chunks(sz) {
|
||||
for elt in group {
|
||||
test::black_box(elt);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn chunks_lazy_1(b: &mut test::Bencher) {
|
||||
let data = vec![0; 1024];
|
||||
|
||||
let data = test::black_box(data);
|
||||
let sz = test::black_box(10);
|
||||
|
||||
b.iter(|| {
|
||||
for group in &data.iter().chunks(sz) {
|
||||
for elt in group {
|
||||
test::black_box(elt);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn equal(b: &mut test::Bencher) {
|
||||
let data = vec![7; 1024];
|
||||
let l = data.len();
|
||||
let alpha = test::black_box(&data[1..]);
|
||||
let beta = test::black_box(&data[..l - 1]);
|
||||
b.iter(|| {
|
||||
itertools::equal(alpha, beta)
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn merge_default(b: &mut test::Bencher) {
|
||||
let mut data1 = vec![0; 1024];
|
||||
let mut data2 = vec![0; 800];
|
||||
let mut x = 0;
|
||||
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 = test::black_box(data1);
|
||||
let data2 = test::black_box(data2);
|
||||
b.iter(|| {
|
||||
data1.iter().merge(&data2).count()
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn merge_by_cmp(b: &mut test::Bencher) {
|
||||
let mut data1 = vec![0; 1024];
|
||||
let mut data2 = vec![0; 800];
|
||||
let mut x = 0;
|
||||
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 = test::black_box(data1);
|
||||
let data2 = test::black_box(data2);
|
||||
b.iter(|| {
|
||||
data1.iter().merge_by(&data2, PartialOrd::le).count()
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn merge_by_lt(b: &mut test::Bencher) {
|
||||
let mut data1 = vec![0; 1024];
|
||||
let mut data2 = vec![0; 800];
|
||||
let mut x = 0;
|
||||
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 = test::black_box(data1);
|
||||
let data2 = test::black_box(data2);
|
||||
b.iter(|| {
|
||||
data1.iter().merge_by(&data2, |a, b| a <= b).count()
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn kmerge_default(b: &mut test::Bencher) {
|
||||
let mut data1 = vec![0; 1024];
|
||||
let mut data2 = vec![0; 800];
|
||||
let mut x = 0;
|
||||
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 = test::black_box(data1);
|
||||
let data2 = test::black_box(data2);
|
||||
let its = &[data1.iter(), data2.iter()];
|
||||
b.iter(|| {
|
||||
its.iter().cloned().kmerge().count()
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn kmerge_tenway(b: &mut test::Bencher) {
|
||||
let mut data = vec![0; 10240];
|
||||
|
||||
let mut state = 1729u16;
|
||||
fn rng(state: &mut u16) -> u16 {
|
||||
let new = state.wrapping_mul(31421) + 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.len() > 0 {
|
||||
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())));
|
||||
|
||||
b.iter(|| {
|
||||
chunks.iter().cloned().kmerge().count()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fn fast_integer_sum<I>(iter: I) -> I::Item
|
||||
where I: IntoIterator,
|
||||
I::Item: Default + Add<Output=I::Item>
|
||||
{
|
||||
iter.into_iter().fold(<_>::default(), |x, y| x + y)
|
||||
}
|
||||
|
||||
|
||||
#[bench]
|
||||
fn step_vec_2(b: &mut test::Bencher) {
|
||||
let v = vec![0; 1024];
|
||||
b.iter(|| {
|
||||
fast_integer_sum(cloned(v.iter().step(2)))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn step_vec_10(b: &mut test::Bencher) {
|
||||
let v = vec![0; 1024];
|
||||
b.iter(|| {
|
||||
fast_integer_sum(cloned(v.iter().step(10)))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn step_range_2(b: &mut test::Bencher) {
|
||||
let v = black_box(0..1024);
|
||||
b.iter(|| {
|
||||
fast_integer_sum(v.clone().step(2))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn step_range_10(b: &mut test::Bencher) {
|
||||
let v = black_box(0..1024);
|
||||
b.iter(|| {
|
||||
fast_integer_sum(v.clone().step(10))
|
||||
});
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
pub use self::zipslices::ZipSlices;
|
||||
mod zipslices;
|
|
@ -0,0 +1,189 @@
|
|||
use std::cmp;
|
||||
|
||||
// Note: There are different ways to implement ZipSlices.
|
||||
// This version performed the best in benchmarks.
|
||||
//
|
||||
// I also implemented a version with three pointes (tptr, tend, uptr),
|
||||
// that mimiced slice::Iter and only checked bounds by using tptr == tend,
|
||||
// but that was inferior to this solution.
|
||||
|
||||
/// An iterator which iterates two slices simultaneously.
|
||||
///
|
||||
/// `ZipSlices` acts like a double-ended `.zip()` iterator.
|
||||
///
|
||||
/// It was intended to be more efficient than `.zip()`, and it was, then
|
||||
/// rustc changed how it optimizes so it can not promise improved performance
|
||||
/// at this time.
|
||||
///
|
||||
/// Note that elements past the end of the shortest of the two slices are ignored.
|
||||
///
|
||||
/// Iterator element type for `ZipSlices<T, U>` is `(T::Item, U::Item)`. For example,
|
||||
/// for a `ZipSlices<&'a [A], &'b mut [B]>`, the element type is `(&'a A, &'b mut B)`.
|
||||
#[derive(Clone)]
|
||||
pub struct ZipSlices<T, U> {
|
||||
t: T,
|
||||
u: U,
|
||||
len: usize,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b, A, B> ZipSlices<&'a [A], &'b [B]> {
|
||||
/// Create a new `ZipSlices` from slices `a` and `b`.
|
||||
///
|
||||
/// Act like a double-ended `.zip()` iterator, but more efficiently.
|
||||
///
|
||||
/// Note that elements past the end of the shortest of the two slices are ignored.
|
||||
#[inline(always)]
|
||||
pub fn new(a: &'a [A], b: &'b [B]) -> Self {
|
||||
let minl = cmp::min(a.len(), b.len());
|
||||
ZipSlices {
|
||||
t: a,
|
||||
u: b,
|
||||
len: minl,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ZipSlices<T, U>
|
||||
where T: Slice,
|
||||
U: Slice
|
||||
{
|
||||
/// Create a new `ZipSlices` from slices `a` and `b`.
|
||||
///
|
||||
/// Act like a double-ended `.zip()` iterator, but more efficiently.
|
||||
///
|
||||
/// Note that elements past the end of the shortest of the two slices are ignored.
|
||||
#[inline(always)]
|
||||
pub fn from_slices(a: T, b: U) -> Self {
|
||||
let minl = cmp::min(a.len(), b.len());
|
||||
ZipSlices {
|
||||
t: a,
|
||||
u: b,
|
||||
len: minl,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Iterator for ZipSlices<T, U>
|
||||
where T: Slice,
|
||||
U: Slice
|
||||
{
|
||||
type Item = (T::Item, U::Item);
|
||||
|
||||
#[inline(always)]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
if self.index >= self.len {
|
||||
None
|
||||
} else {
|
||||
let i = self.index;
|
||||
self.index += 1;
|
||||
Some((
|
||||
self.t.get_unchecked(i),
|
||||
self.u.get_unchecked(i)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let len = self.len - self.index;
|
||||
(len, Some(len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> DoubleEndedIterator for ZipSlices<T, U>
|
||||
where T: Slice,
|
||||
U: Slice
|
||||
{
|
||||
#[inline(always)]
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
if self.index >= self.len {
|
||||
None
|
||||
} else {
|
||||
self.len -= 1;
|
||||
let i = self.len;
|
||||
Some((
|
||||
self.t.get_unchecked(i),
|
||||
self.u.get_unchecked(i)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ExactSizeIterator for ZipSlices<T, U>
|
||||
where T: Slice,
|
||||
U: Slice
|
||||
{}
|
||||
|
||||
unsafe impl<T, U> Slice for ZipSlices<T, U>
|
||||
where T: Slice,
|
||||
U: Slice
|
||||
{
|
||||
type Item = (T::Item, U::Item);
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.len - self.index
|
||||
}
|
||||
|
||||
unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item {
|
||||
(self.t.get_unchecked(i),
|
||||
self.u.get_unchecked(i))
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper trait to let `ZipSlices` accept both `&[T]` and `&mut [T]`.
|
||||
///
|
||||
/// Unsafe trait because:
|
||||
///
|
||||
/// - Implementors must guarantee that `get_unchecked` is valid for all indices `0..len()`.
|
||||
pub unsafe trait Slice {
|
||||
/// The type of a reference to the slice's elements
|
||||
type Item;
|
||||
#[doc(hidden)]
|
||||
fn len(&self) -> usize;
|
||||
#[doc(hidden)]
|
||||
unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item;
|
||||
}
|
||||
|
||||
unsafe impl<'a, T> Slice for &'a [T] {
|
||||
type Item = &'a T;
|
||||
#[inline(always)]
|
||||
fn len(&self) -> usize { (**self).len() }
|
||||
#[inline(always)]
|
||||
unsafe fn get_unchecked(&mut self, i: usize) -> &'a T {
|
||||
debug_assert!(i < self.len());
|
||||
(**self).get_unchecked(i)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T> Slice for &'a mut [T] {
|
||||
type Item = &'a mut T;
|
||||
#[inline(always)]
|
||||
fn len(&self) -> usize { (**self).len() }
|
||||
#[inline(always)]
|
||||
unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T {
|
||||
debug_assert!(i < self.len());
|
||||
// override the lifetime constraints of &mut &'a mut [T]
|
||||
(*(*self as *mut [T])).get_unchecked_mut(i)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zipslices() {
|
||||
|
||||
let xs = [1, 2, 3, 4, 5, 6];
|
||||
let ys = [1, 2, 3, 7];
|
||||
::itertools::assert_equal(ZipSlices::new(&xs, &ys), xs.iter().zip(&ys));
|
||||
|
||||
let xs = [1, 2, 3, 4, 5, 6];
|
||||
let mut ys = [0; 6];
|
||||
for (x, y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) {
|
||||
*y = *x;
|
||||
}
|
||||
::itertools::assert_equal(&xs, &ys);
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate itertools;
|
||||
|
||||
use test::{black_box, Bencher};
|
||||
use itertools::Itertools;
|
||||
|
||||
// aproximate 100_000 iterations for each combination
|
||||
const N1: usize = 100_000;
|
||||
const N2: usize = 448;
|
||||
const N3: usize = 86;
|
||||
const N4: usize = 41;
|
||||
|
||||
#[bench]
|
||||
fn comb_for1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
for i in 0..N1 {
|
||||
black_box(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn comb_for2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
for i in 0..N2 {
|
||||
for j in (i + 1)..N2 {
|
||||
black_box(i + j);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn comb_for3(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
for i in 0..N3 {
|
||||
for j in (i + 1)..N3 {
|
||||
for k in (j + 1)..N3 {
|
||||
black_box(i + j + k);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn comb_for4(b: &mut Bencher) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn comb_c1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
for (i,) in (0..N1).tuple_combinations() {
|
||||
black_box(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn comb_c2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
for (i, j) in (0..N2).tuple_combinations() {
|
||||
black_box(i + j);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn comb_c3(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
for (i, j, k) in (0..N3).tuple_combinations() {
|
||||
black_box(i + j + k);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn comb_c4(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
for (i, j, k, l) in (0..N4).tuple_combinations() {
|
||||
black_box(i + j + k + l);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate itertools;
|
||||
|
||||
use test::Bencher;
|
||||
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;
|
||||
$TUPLE_FUN:ident,
|
||||
$TUPLES:ident,
|
||||
$TUPLE_WINDOWS:ident;
|
||||
$SLICE_FUN:ident,
|
||||
$CHUNKS:ident,
|
||||
$WINDOWS:ident;
|
||||
$FOR_CHUNKS:ident,
|
||||
$FOR_WINDOWS:ident
|
||||
) => (
|
||||
#[bench]
|
||||
fn $FOR_CHUNKS(b: &mut Bencher) {
|
||||
let v: Vec<u32> = (0.. $N * 1_000).collect();
|
||||
let mut s = 0;
|
||||
b.iter(|| {
|
||||
let mut j = 0;
|
||||
for _ in 0..1_000 {
|
||||
s += $SLICE_FUN(&v[j..(j + $N)]);
|
||||
j += $N;
|
||||
}
|
||||
s
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn $FOR_WINDOWS(b: &mut Bencher) {
|
||||
let v: Vec<u32> = (0..1_000).collect();
|
||||
let mut s = 0;
|
||||
b.iter(|| {
|
||||
for i in 0..(1_000 - $N) {
|
||||
s += $SLICE_FUN(&v[i..(i + $N)]);
|
||||
}
|
||||
s
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn $TUPLES(b: &mut Bencher) {
|
||||
let v: Vec<u32> = (0.. $N * 1_000).collect();
|
||||
let mut s = 0;
|
||||
b.iter(|| {
|
||||
for x in v.iter().tuples() {
|
||||
s += $TUPLE_FUN(&x);
|
||||
}
|
||||
s
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn $CHUNKS(b: &mut Bencher) {
|
||||
let v: Vec<u32> = (0.. $N * 1_000).collect();
|
||||
let mut s = 0;
|
||||
b.iter(|| {
|
||||
for x in v.chunks($N) {
|
||||
s += $SLICE_FUN(x);
|
||||
}
|
||||
s
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn $TUPLE_WINDOWS(b: &mut Bencher) {
|
||||
let v: Vec<u32> = (0..1_000).collect();
|
||||
let mut s = 0;
|
||||
b.iter(|| {
|
||||
for x in v.iter().tuple_windows() {
|
||||
s += $TUPLE_FUN(&x);
|
||||
}
|
||||
s
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn $WINDOWS(b: &mut Bencher) {
|
||||
let v: Vec<u32> = (0..1_000).collect();
|
||||
let mut s = 0;
|
||||
b.iter(|| {
|
||||
for x in v.windows($N) {
|
||||
s += $SLICE_FUN(x);
|
||||
}
|
||||
s
|
||||
});
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def_benchs!{
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
sum_t4,
|
||||
tuple_chunks_4,
|
||||
tuple_windows_4;
|
||||
sum_s4,
|
||||
slice_chunks_4,
|
||||
slice_windows_4;
|
||||
for_chunks_4,
|
||||
for_windows_4
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
.docblock pre.rust { background: #eeeeff; }
|
||||
pre.trait, pre.fn, pre.struct, pre.enum, pre.typedef { background: #fcfefc; }
|
||||
|
||||
/* Small “example” label for doc examples */
|
||||
.docblock pre.rust::before {
|
||||
content: "example";
|
||||
float: right;
|
||||
font-style: italic;
|
||||
font-size: 0.8em;
|
||||
margin-top: -10px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
|
||||
|
||||
/* Fixup where display in trait listing */
|
||||
pre.trait .where::before {
|
||||
content: '\a ';
|
||||
}
|
||||
|
||||
.docblock code {
|
||||
background-color: inherit;
|
||||
font-weight: bold;
|
||||
padding: 0 0.1em;
|
||||
}
|
||||
|
||||
a.test-arrow {
|
||||
display: none;
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
5.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
|
|
@ -0,0 +1,141 @@
|
|||
///
|
||||
/// This example parses, sorts and groups the iris dataset
|
||||
/// and does some simple manipulations.
|
||||
///
|
||||
/// Iterators and itertools functionality are used throughout.
|
||||
///
|
||||
///
|
||||
|
||||
extern crate itertools;
|
||||
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashMap;
|
||||
use std::iter::repeat;
|
||||
use std::num::ParseFloatError;
|
||||
use std::str::FromStr;
|
||||
|
||||
static DATA: &'static str = include_str!("iris.data");
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Iris {
|
||||
name: String,
|
||||
data: [f32; 4],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum ParseError {
|
||||
Numeric(ParseFloatError),
|
||||
Other(&'static str),
|
||||
}
|
||||
|
||||
impl From<ParseFloatError> for ParseError {
|
||||
fn from(err: ParseFloatError) -> Self {
|
||||
ParseError::Numeric(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an Iris from a comma-separated line
|
||||
impl FromStr for Iris {
|
||||
type Err = ParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut iris = Iris { name: "".into(), data: [0.; 4] };
|
||||
let mut parts = s.split(",").map(str::trim);
|
||||
|
||||
// using Iterator::by_ref()
|
||||
for (index, part) in parts.by_ref().take(4).enumerate() {
|
||||
iris.data[index] = try!(part.parse::<f32>());
|
||||
}
|
||||
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_results(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::group_by
|
||||
for (species, species_group) in &irises.iter().group_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_group {
|
||||
// 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_by
|
||||
let min_max = |data: &[Iris], col| {
|
||||
data.iter()
|
||||
.map(|iris| iris.data[col])
|
||||
.minmax_by(|a, b| f32::partial_cmp(a, b).unwrap())
|
||||
.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(" "))
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,84 @@
|
|||
|
||||
|
||||
use std::iter::Fuse;
|
||||
use std::collections::VecDeque;
|
||||
use size_hint;
|
||||
|
||||
/// See [`multipeek()`](../fn.multipeek.html) for more information.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MultiPeek<I>
|
||||
where I: Iterator
|
||||
{
|
||||
iter: Fuse<I>,
|
||||
buf: VecDeque<I::Item>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
/// An iterator adaptor that allows the user to peek at multiple `.next()`
|
||||
/// values without advancing the base iterator.
|
||||
pub fn multipeek<I>(iterable: I) -> MultiPeek<I::IntoIter>
|
||||
where I: IntoIterator
|
||||
{
|
||||
MultiPeek {
|
||||
iter: iterable.into_iter().fuse(),
|
||||
buf: VecDeque::new(),
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> MultiPeek<I>
|
||||
where I: Iterator
|
||||
{
|
||||
/// Reset the peeking “cursor”
|
||||
pub fn reset_peek(&mut self) {
|
||||
self.index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator> MultiPeek<I> {
|
||||
/// Works exactly like `.next()` with the only difference that it doesn't
|
||||
/// advance itself. `.peek()` can be called multiple times, to peek
|
||||
/// further ahead.
|
||||
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<I> Iterator for MultiPeek<I>
|
||||
where I: Iterator
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
self.index = 0;
|
||||
if self.buf.is_empty() {
|
||||
self.iter.next()
|
||||
} else {
|
||||
self.buf.pop_front()
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
size_hint::add_scalar(self.iter.size_hint(), self.buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
// Same size
|
||||
impl<I> ExactSizeIterator for MultiPeek<I>
|
||||
where I: ExactSizeIterator
|
||||
{}
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
macro_rules! impl_cons_iter(
|
||||
($_A:ident, $_B:ident, ) => (); // stop
|
||||
|
||||
($A:ident, $($B:ident,)*) => (
|
||||
impl_cons_iter!($($B,)*);
|
||||
#[allow(non_snake_case)]
|
||||
impl<X, Iter, $($B),*> Iterator for ConsTuples<Iter, (($($B,)*), X)>
|
||||
where Iter: Iterator<Item = (($($B,)*), X)>,
|
||||
{
|
||||
type Item = ($($B,)* X, );
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl<X, Iter, $($B),*> DoubleEndedIterator for ConsTuples<Iter, (($($B,)*), X)>
|
||||
where Iter: DoubleEndedIterator<Item = (($($B,)*), X)>,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
);
|
||||
|
||||
impl_cons_iter!(A, B, C, D, E, F, G, H,);
|
||||
|
||||
/// An iterator that maps an iterator of tuples like
|
||||
/// `((A, B), C)` to an iterator of `(A, B, C)`.
|
||||
///
|
||||
/// Used by the `iproduct!()` macro.
|
||||
pub struct ConsTuples<I, J>
|
||||
where I: Iterator<Item=J>,
|
||||
{
|
||||
iter: I,
|
||||
}
|
||||
|
||||
impl<I, J> Clone for ConsTuples<I, J>
|
||||
where I: Clone + Iterator<Item=J>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
ConsTuples {
|
||||
iter: self.iter.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an iterator that maps for example iterators of
|
||||
/// `((A, B), C)` to `(A, B, C)`.
|
||||
pub fn cons_tuples<I, J>(iterable: I) -> ConsTuples<I, J>
|
||||
where I: Iterator<Item=J>
|
||||
{
|
||||
ConsTuples { iter: iterable.into_iter() }
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
//! "Diff"ing iterators for caching elements to sequential collections without requiring the new
|
||||
//! elements' iterator to be `Clone`.
|
||||
//!
|
||||
//! - [**Diff**](./enum.Diff.html) (produced by the [**diff_with**](./fn.diff_with.html) function)
|
||||
//! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from
|
||||
//! a lock-step comparison.
|
||||
|
||||
use free::put_back;
|
||||
use structs::PutBack;
|
||||
|
||||
/// A type returned by the [`diff_with`](./fn.diff_with.html) function.
|
||||
///
|
||||
/// `Diff` represents the way in which the elements yielded by the iterator `I` differ to some
|
||||
/// iterator `J`.
|
||||
pub enum Diff<I, J>
|
||||
where I: Iterator,
|
||||
J: Iterator
|
||||
{
|
||||
/// The index of the first non-matching element along with both iterator's remaining elements
|
||||
/// starting with the first mis-match.
|
||||
FirstMismatch(usize, PutBack<I>, PutBack<J>),
|
||||
/// The total number of elements that were in `J` along with the remaining elements of `I`.
|
||||
Shorter(usize, PutBack<I>),
|
||||
/// The total number of elements that were in `I` along with the remaining elements of `J`.
|
||||
Longer(usize, PutBack<J>),
|
||||
}
|
||||
|
||||
/// 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, J, F>(i: I, j: J, is_equal: F)
|
||||
-> Option<Diff<I::IntoIter, J::IntoIter>>
|
||||
where I: IntoIterator,
|
||||
J: IntoIterator,
|
||||
F: Fn(&I::Item, &J::Item) -> bool
|
||||
{
|
||||
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)))
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
use std::fmt;
|
||||
use std::cell::RefCell;
|
||||
|
||||
/// 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()`](../trait.Itertools.html#method.format_with) for more information.
|
||||
pub struct FormatWith<'a, I, F> {
|
||||
sep: &'a str,
|
||||
/// FormatWith uses interior mutability because Display::fmt takes &self.
|
||||
inner: RefCell<Option<(I, F)>>,
|
||||
}
|
||||
|
||||
/// Format all iterator elements lazily, separated by `sep`.
|
||||
///
|
||||
/// The format value can only be formatted once, after that the iterator is
|
||||
/// exhausted.
|
||||
///
|
||||
/// See [`.format()`](../trait.Itertools.html#method.format)
|
||||
/// for more information.
|
||||
#[derive(Clone)]
|
||||
pub struct Format<'a, I> {
|
||||
sep: &'a str,
|
||||
/// Format uses interior mutability because Display::fmt takes &self.
|
||||
inner: RefCell<Option<I>>,
|
||||
}
|
||||
|
||||
pub fn new_format<'a, I, F>(iter: I, separator: &'a str, f: F) -> FormatWith<'a, I, F>
|
||||
where I: Iterator,
|
||||
F: FnMut(I::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result
|
||||
{
|
||||
FormatWith {
|
||||
sep: separator,
|
||||
inner: RefCell::new(Some((iter, f))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_format_default<'a, I>(iter: I, separator: &'a str) -> Format<'a, I>
|
||||
where I: Iterator,
|
||||
{
|
||||
Format {
|
||||
sep: separator,
|
||||
inner: RefCell::new(Some(iter)),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I, F> fmt::Display for FormatWith<'a, I, F>
|
||||
where I: Iterator,
|
||||
F: FnMut(I::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let (mut iter, mut format) = match self.inner.borrow_mut().take() {
|
||||
Some(t) => t,
|
||||
None => panic!("FormatWith: was already formatted once"),
|
||||
};
|
||||
|
||||
if let Some(fst) = iter.next() {
|
||||
try!(format(fst, &mut |disp: &fmt::Display| disp.fmt(f)));
|
||||
for elt in iter {
|
||||
if self.sep.len() > 0 {
|
||||
|
||||
try!(f.write_str(self.sep));
|
||||
}
|
||||
try!(format(elt, &mut |disp: &fmt::Display| disp.fmt(f)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I> Format<'a, I>
|
||||
where I: Iterator,
|
||||
{
|
||||
fn format<F>(&self, f: &mut fmt::Formatter, mut cb: F) -> fmt::Result
|
||||
where F: FnMut(&I::Item, &mut fmt::Formatter) -> fmt::Result,
|
||||
{
|
||||
let mut iter = match self.inner.borrow_mut().take() {
|
||||
Some(t) => t,
|
||||
None => panic!("Format: was already formatted once"),
|
||||
};
|
||||
|
||||
if let Some(fst) = iter.next() {
|
||||
try!(cb(&fst, f));
|
||||
for elt in iter {
|
||||
if self.sep.len() > 0 {
|
||||
try!(f.write_str(self.sep));
|
||||
}
|
||||
try!(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}
|
|
@ -0,0 +1,222 @@
|
|||
//! 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.
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::iter::{self, Zip};
|
||||
use Itertools;
|
||||
|
||||
pub use adaptors::{
|
||||
interleave,
|
||||
merge,
|
||||
put_back,
|
||||
put_back_n,
|
||||
};
|
||||
pub use adaptors::multipeek::multipeek;
|
||||
pub use kmerge_impl::kmerge;
|
||||
pub use zip_eq_impl::zip_eq;
|
||||
pub use rciter_impl::rciter;
|
||||
|
||||
/// Iterate `iterable` with a running index.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `.enumerate()`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::enumerate;
|
||||
///
|
||||
/// for (i, elt) in enumerate(&[1, 2, 3]) {
|
||||
/// /* loop body */
|
||||
/// }
|
||||
/// ```
|
||||
pub fn enumerate<I>(iterable: I) -> iter::Enumerate<I::IntoIter>
|
||||
where I: IntoIterator
|
||||
{
|
||||
iterable.into_iter().enumerate()
|
||||
}
|
||||
|
||||
/// Iterate `iterable` in reverse.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `.rev()`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::rev;
|
||||
///
|
||||
/// for elt in rev(&[1, 2, 3]) {
|
||||
/// /* loop body */
|
||||
/// }
|
||||
/// ```
|
||||
pub fn rev<I>(iterable: I) -> iter::Rev<I::IntoIter>
|
||||
where I: IntoIterator,
|
||||
I::IntoIter: DoubleEndedIterator
|
||||
{
|
||||
iterable.into_iter().rev()
|
||||
}
|
||||
|
||||
/// Iterate `i` and `j` in lock step.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `i.zip(j)`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::zip;
|
||||
///
|
||||
/// let data = [1, 2, 3, 4, 5];
|
||||
/// for (a, b) in zip(&data, &data[1..]) {
|
||||
/// /* loop body */
|
||||
/// }
|
||||
/// ```
|
||||
pub fn zip<I, J>(i: I, j: J) -> Zip<I::IntoIter, J::IntoIter>
|
||||
where I: IntoIterator,
|
||||
J: IntoIterator
|
||||
{
|
||||
i.into_iter().zip(j)
|
||||
}
|
||||
|
||||
/// Create an iterator that first iterates `i` and then `j`.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `i.chain(j)`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::chain;
|
||||
///
|
||||
/// for elt in chain(&[1, 2, 3], &[4]) {
|
||||
/// /* loop body */
|
||||
/// }
|
||||
/// ```
|
||||
pub fn chain<I, J>(i: I, j: J) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
|
||||
where I: IntoIterator,
|
||||
J: IntoIterator<Item = I::Item>
|
||||
{
|
||||
i.into_iter().chain(j)
|
||||
}
|
||||
|
||||
/// Create an iterator that clones each element from &T to T
|
||||
///
|
||||
/// `IntoIterator` enabled version of `i.cloned()`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::cloned;
|
||||
///
|
||||
/// assert_eq!(cloned(b"abc").next(), Some(b'a'));
|
||||
/// ```
|
||||
pub fn cloned<'a, I, T: 'a>(iterable: I) -> iter::Cloned<I::IntoIter>
|
||||
where I: IntoIterator<Item=&'a T>,
|
||||
T: Clone,
|
||||
{
|
||||
iterable.into_iter().cloned()
|
||||
}
|
||||
|
||||
/// Perform a fold operation over the iterable.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `i.fold(init, f)`
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::fold;
|
||||
///
|
||||
/// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.);
|
||||
/// ```
|
||||
pub fn fold<I, B, F>(iterable: I, init: B, f: F) -> B
|
||||
where I: IntoIterator,
|
||||
F: FnMut(B, I::Item) -> B
|
||||
{
|
||||
iterable.into_iter().fold(init, f)
|
||||
}
|
||||
|
||||
/// Test whether the predicate holds for all elements in the iterable.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `i.all(f)`
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::all;
|
||||
///
|
||||
/// assert!(all(&[1, 2, 3], |elt| *elt > 0));
|
||||
/// ```
|
||||
pub fn all<I, F>(iterable: I, f: F) -> bool
|
||||
where I: IntoIterator,
|
||||
F: FnMut(I::Item) -> bool
|
||||
{
|
||||
iterable.into_iter().all(f)
|
||||
}
|
||||
|
||||
/// Test whether the predicate holds for any elements in the iterable.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `i.any(f)`
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::any;
|
||||
///
|
||||
/// assert!(any(&[0, -1, 2], |elt| *elt > 0));
|
||||
/// ```
|
||||
pub fn any<I, F>(iterable: I, f: F) -> bool
|
||||
where I: IntoIterator,
|
||||
F: FnMut(I::Item) -> bool
|
||||
{
|
||||
iterable.into_iter().any(f)
|
||||
}
|
||||
|
||||
/// Return the maximum value of the iterable.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `i.max()`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::max;
|
||||
///
|
||||
/// assert_eq!(max(0..10), Some(9));
|
||||
/// ```
|
||||
pub fn max<I>(iterable: I) -> Option<I::Item>
|
||||
where I: IntoIterator,
|
||||
I::Item: Ord
|
||||
{
|
||||
iterable.into_iter().max()
|
||||
}
|
||||
|
||||
/// Return the minimum value of the iterable.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `i.min()`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::min;
|
||||
///
|
||||
/// assert_eq!(min(0..10), Some(0));
|
||||
/// ```
|
||||
pub fn min<I>(iterable: I) -> Option<I::Item>
|
||||
where I: IntoIterator,
|
||||
I::Item: Ord
|
||||
{
|
||||
iterable.into_iter().min()
|
||||
}
|
||||
|
||||
|
||||
/// Combine all iterator elements into one String, seperated by `sep`.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `iterable.join(sep)`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::join;
|
||||
///
|
||||
/// assert_eq!(join(&[1, 2, 3], ", "), "1, 2, 3");
|
||||
/// ```
|
||||
pub fn join<I>(iterable: I, sep: &str) -> String
|
||||
where I: IntoIterator,
|
||||
I::Item: Display
|
||||
{
|
||||
iterable.into_iter().join(sep)
|
||||
}
|
||||
|
||||
/// Collect all the iterable's elements into a sorted vector in ascending order.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `iterable.sorted()`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::sorted;
|
||||
/// use itertools::assert_equal;
|
||||
///
|
||||
/// assert_equal(sorted("rust".chars()), "rstu".chars());
|
||||
/// ```
|
||||
pub fn sorted<I>(iterable: I) -> Vec<I::Item>
|
||||
where I: IntoIterator,
|
||||
I::Item: Ord
|
||||
{
|
||||
iterable.into_iter().sorted()
|
||||
}
|
||||
|
|
@ -0,0 +1,563 @@
|
|||
use std::cell::{Cell, RefCell};
|
||||
use std::vec;
|
||||
|
||||
/// A trait to unify FnMut for GroupBy with the chunk key in IntoChunks
|
||||
trait KeyFunction<A> {
|
||||
type Key;
|
||||
fn call_mut(&mut self, arg: A) -> Self::Key;
|
||||
}
|
||||
|
||||
impl<'a, A, K, F: ?Sized> KeyFunction<A> for F
|
||||
where F: FnMut(A) -> K
|
||||
{
|
||||
type Key = K;
|
||||
#[inline]
|
||||
fn call_mut(&mut self, arg: A) -> Self::Key {
|
||||
(*self)(arg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ChunkIndex acts like the grouping key function for IntoChunks
|
||||
struct ChunkIndex {
|
||||
size: usize,
|
||||
index: usize,
|
||||
key: usize,
|
||||
}
|
||||
|
||||
impl ChunkIndex {
|
||||
#[inline(always)]
|
||||
fn new(size: usize) -> Self {
|
||||
ChunkIndex {
|
||||
size: size,
|
||||
index: 0,
|
||||
key: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A> KeyFunction<A> 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct GroupInner<K, I, F>
|
||||
where I: Iterator
|
||||
{
|
||||
key: F,
|
||||
iter: I,
|
||||
current_key: Option<K>,
|
||||
current_elt: Option<I::Item>,
|
||||
/// flag set if iterator is exhausted
|
||||
done: bool,
|
||||
/// Index of group we are currently buffering or visiting
|
||||
top: usize,
|
||||
/// Least index for which we still have elements buffered
|
||||
bot: usize,
|
||||
/// Group index for `buffer[0]` -- the slots bufbot..bot are unused
|
||||
/// and will be erased when that range is large enough.
|
||||
bufbot: usize,
|
||||
/// Buffered groups, from `bufbot` (index 0) to `top`.
|
||||
buffer: Vec<vec::IntoIter<I::Item>>,
|
||||
/// index of last group iter that was dropped, usize::MAX == none
|
||||
dropped_group: usize,
|
||||
}
|
||||
|
||||
impl<K, I, F> GroupInner<K, I, F>
|
||||
where I: Iterator,
|
||||
F: for<'a> KeyFunction<&'a I::Item, Key=K>,
|
||||
K: PartialEq,
|
||||
{
|
||||
/// `client`: Index of group that requests next element
|
||||
#[inline(always)]
|
||||
fn step(&mut self, client: usize) -> Option<I::Item> {
|
||||
/*
|
||||
println!("client={}, bufbot={}, bot={}, top={}, buffers=[{}]",
|
||||
client, self.bufbot, self.bot, self.top,
|
||||
self.buffer.iter().map(|elt| elt.len()).format(", "));
|
||||
*/
|
||||
if client < self.bot {
|
||||
None
|
||||
} else if client < self.top ||
|
||||
(client == self.top && self.buffer.len() > self.top - self.bufbot)
|
||||
{
|
||||
self.lookup_buffer(client)
|
||||
} else if self.done {
|
||||
None
|
||||
} else if self.top == client {
|
||||
self.step_current()
|
||||
} else {
|
||||
self.step_buffering(client)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn lookup_buffer(&mut self, client: usize) -> Option<I::Item> {
|
||||
// if `bufidx` doesn't exist in self.buffer, it might be empty
|
||||
let bufidx = client - self.bufbot;
|
||||
if client < self.bot {
|
||||
return None;
|
||||
}
|
||||
let elt = self.buffer.get_mut(bufidx).and_then(|queue| queue.next());
|
||||
if elt.is_none() && client == self.bot {
|
||||
// FIXME: VecDeque is unfortunately not zero allocation when empty,
|
||||
// so we do this job manually.
|
||||
// `bufbot..bot` is unused, and if it's large enough, erase it.
|
||||
self.bot += 1;
|
||||
// skip forward further empty queues too
|
||||
while self.buffer.get(self.bot - self.bufbot)
|
||||
.map_or(false, |buf| buf.len() == 0)
|
||||
{
|
||||
self.bot += 1;
|
||||
}
|
||||
|
||||
let nclear = self.bot - self.bufbot;
|
||||
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.bufbot = self.bot;
|
||||
}
|
||||
}
|
||||
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<I::Item> {
|
||||
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<I::Item> {
|
||||
// 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.
|
||||
debug_assert!(self.top + 1 == client);
|
||||
let mut group = Vec::new();
|
||||
|
||||
if let Some(elt) = self.current_elt.take() {
|
||||
if self.top != 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 != self.dropped_group {
|
||||
group.push(elt);
|
||||
}
|
||||
}
|
||||
|
||||
if self.top != self.dropped_group {
|
||||
self.push_next_group(group);
|
||||
}
|
||||
if first_elt.is_some() {
|
||||
self.top += 1;
|
||||
debug_assert!(self.top == client);
|
||||
}
|
||||
first_elt
|
||||
}
|
||||
|
||||
fn push_next_group(&mut self, group: Vec<I::Item>) {
|
||||
// When we add a new buffered group, fill up slots between bot and top
|
||||
while self.top - self.bufbot > self.buffer.len() {
|
||||
if self.buffer.is_empty() {
|
||||
self.bufbot += 1;
|
||||
self.bot += 1;
|
||||
} else {
|
||||
self.buffer.push(Vec::new().into_iter());
|
||||
}
|
||||
}
|
||||
self.buffer.push(group.into_iter());
|
||||
debug_assert!(self.top + 1 - self.bufbot == self.buffer.len());
|
||||
}
|
||||
|
||||
/// This is the immediate case, where we use no buffering
|
||||
#[inline]
|
||||
fn step_current(&mut self) -> Option<I::Item> {
|
||||
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 += 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);
|
||||
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 += 1;
|
||||
}
|
||||
self.current_key = Some(key);
|
||||
self.current_elt = Some(elt);
|
||||
}
|
||||
old_key
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, I, F> GroupInner<K, I, F>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `GroupBy` is the storage for the lazy grouping operation.
|
||||
///
|
||||
/// If the groups are consumed in their original order, or if each
|
||||
/// group is dropped without keeping it around, then `GroupBy` uses
|
||||
/// 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 [`.group_by()`](../trait.Itertools.html#method.group_by) for more information.
|
||||
pub struct GroupBy<K, I, F>
|
||||
where I: Iterator,
|
||||
{
|
||||
inner: RefCell<GroupInner<K, I, F>>,
|
||||
// the group iterator's current index. Keep this in the main value
|
||||
// so that simultaneous iterators all use the same state.
|
||||
index: Cell<usize>,
|
||||
}
|
||||
|
||||
/// Create a new
|
||||
pub fn new<K, J, F>(iter: J, f: F) -> GroupBy<K, J::IntoIter, F>
|
||||
where J: IntoIterator,
|
||||
F: FnMut(&J::Item) -> K,
|
||||
{
|
||||
GroupBy {
|
||||
inner: RefCell::new(GroupInner {
|
||||
key: f,
|
||||
iter: iter.into_iter(),
|
||||
current_key: None,
|
||||
current_elt: None,
|
||||
done: false,
|
||||
top: 0,
|
||||
bot: 0,
|
||||
bufbot: 0,
|
||||
buffer: Vec::new(),
|
||||
dropped_group: !0,
|
||||
}),
|
||||
index: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, I, F> GroupBy<K, I, F>
|
||||
where I: Iterator,
|
||||
{
|
||||
/// `client`: Index of group that requests next element
|
||||
fn step(&self, client: usize) -> Option<I::Item>
|
||||
where F: FnMut(&I::Item) -> K,
|
||||
K: PartialEq,
|
||||
{
|
||||
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 GroupBy<K, I, F>
|
||||
where I: Iterator,
|
||||
I::Item: 'a,
|
||||
F: FnMut(&I::Item) -> K,
|
||||
K: PartialEq
|
||||
{
|
||||
type Item = (K, Group<'a, K, I, F>);
|
||||
type IntoIter = Groups<'a, K, I, F>;
|
||||
|
||||
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 [`.group_by()`](../trait.Itertools.html#method.group_by) for more information.
|
||||
pub struct Groups<'a, K: 'a, I: 'a, F: 'a>
|
||||
where I: Iterator,
|
||||
I::Item: 'a
|
||||
{
|
||||
parent: &'a GroupBy<K, I, F>,
|
||||
}
|
||||
|
||||
impl<'a, K, I, F> Iterator for Groups<'a, K, I, F>
|
||||
where I: Iterator,
|
||||
I::Item: 'a,
|
||||
F: FnMut(&I::Item) -> K,
|
||||
K: PartialEq
|
||||
{
|
||||
type Item = (K, Group<'a, K, I, F>);
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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: index,
|
||||
first: Some(elt),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator for the elements in a single group.
|
||||
///
|
||||
/// Iterator element type is `I::Item`.
|
||||
pub struct Group<'a, K: 'a, I: 'a, F: 'a>
|
||||
where I: Iterator,
|
||||
I::Item: 'a,
|
||||
{
|
||||
parent: &'a GroupBy<K, I, F>,
|
||||
index: usize,
|
||||
first: Option<I::Item>,
|
||||
}
|
||||
|
||||
impl<'a, K, I, F> Drop for Group<'a, K, I, F>
|
||||
where I: Iterator,
|
||||
I::Item: 'a,
|
||||
{
|
||||
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<Self::Item> {
|
||||
if let elt @ Some(..) = self.first.take() {
|
||||
return elt;
|
||||
}
|
||||
self.parent.step(self.index)
|
||||
}
|
||||
}
|
||||
|
||||
///// IntoChunks /////
|
||||
|
||||
/// Create a new
|
||||
pub fn new_chunks<J>(iter: J, size: usize) -> IntoChunks<J::IntoIter>
|
||||
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: 0,
|
||||
bot: 0,
|
||||
bufbot: 0,
|
||||
buffer: Vec::new(),
|
||||
dropped_group: !0,
|
||||
}),
|
||||
index: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// `ChunkLazy` is the storage for a lazy chunking operation.
|
||||
///
|
||||
/// `IntoChunks` behaves just like `GroupBy`: 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()`](../trait.Itertools.html#method.chunks) for more information.
|
||||
pub struct IntoChunks<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
inner: RefCell<GroupInner<usize, I, ChunkIndex>>,
|
||||
// the chunk iterator's current index. Keep this in the main value
|
||||
// so that simultaneous iterators all use the same state.
|
||||
index: Cell<usize>,
|
||||
}
|
||||
|
||||
|
||||
impl<I> IntoChunks<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
/// `client`: Index of chunk that requests next element
|
||||
fn step(&self, client: usize) -> Option<I::Item> {
|
||||
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<I>
|
||||
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()`](../trait.Itertools.html#method.chunks) for more information.
|
||||
pub struct Chunks<'a, I: 'a>
|
||||
where I: Iterator,
|
||||
I::Item: 'a,
|
||||
{
|
||||
parent: &'a IntoChunks<I>,
|
||||
}
|
||||
|
||||
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<Self::Item> {
|
||||
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: index,
|
||||
first: Some(elt),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator for the elements in a single chunk.
|
||||
///
|
||||
/// Iterator element type is `I::Item`.
|
||||
pub struct Chunk<'a, I: 'a>
|
||||
where I: Iterator,
|
||||
I::Item: 'a,
|
||||
{
|
||||
parent: &'a IntoChunks<I>,
|
||||
index: usize,
|
||||
first: Option<I::Item>,
|
||||
}
|
||||
|
||||
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<Self::Item> {
|
||||
if let elt @ Some(..) = self.first.take() {
|
||||
return elt;
|
||||
}
|
||||
self.parent.step(self.index)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
//!
|
||||
//! Implementation's internal macros
|
||||
|
||||
macro_rules! debug_fmt_fields {
|
||||
($tyname:ident, $($($field:ident).+),*) => {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
f.debug_struct(stringify!($tyname))
|
||||
$(
|
||||
.field(stringify!($($field).+), &self.$($field).+)
|
||||
)*
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
use std::iter::Fuse;
|
||||
use super::size_hint;
|
||||
|
||||
#[derive(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()`](../trait.Itertools.html#method.intersperse) for more information.
|
||||
pub struct Intersperse<I>
|
||||
where I: Iterator
|
||||
{
|
||||
element: I::Item,
|
||||
iter: Fuse<I>,
|
||||
peek: Option<I::Item>,
|
||||
}
|
||||
|
||||
/// Create a new Intersperse iterator
|
||||
pub fn intersperse<I>(iter: I, elt: I::Item) -> Intersperse<I>
|
||||
where I: Iterator
|
||||
{
|
||||
let mut iter = iter.fuse();
|
||||
Intersperse {
|
||||
peek: iter.next(),
|
||||
iter: iter,
|
||||
element: elt,
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for Intersperse<I>
|
||||
where I: Iterator,
|
||||
I::Item: Clone
|
||||
{
|
||||
type Item = I::Item;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
if self.peek.is_some() {
|
||||
self.peek.take()
|
||||
} else {
|
||||
self.peek = self.iter.next();
|
||||
if self.peek.is_some() {
|
||||
Some(self.element.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
// 2 * SH + { 1 or 0 }
|
||||
let has_peek = self.peek.is_some() as usize;
|
||||
let sh = self.iter.size_hint();
|
||||
size_hint::add_scalar(size_hint::add(sh, sh), has_peek)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
|
||||
use size_hint;
|
||||
use Itertools;
|
||||
|
||||
use std::mem::replace;
|
||||
|
||||
macro_rules! clone_fields {
|
||||
($name:ident, $base:expr, $($field:ident),+) => (
|
||||
$name {
|
||||
$(
|
||||
$field : $base . $field .clone()
|
||||
),*
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
struct HeadTail<I>
|
||||
where I: Iterator
|
||||
{
|
||||
head: I::Item,
|
||||
tail: I,
|
||||
}
|
||||
|
||||
impl<I> HeadTail<I>
|
||||
where I: Iterator
|
||||
{
|
||||
/// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the `Iterator` is empty.
|
||||
fn new(mut it: I) -> Option<HeadTail<I>> {
|
||||
let head = it.next();
|
||||
head.map(|h| {
|
||||
HeadTail {
|
||||
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<I::Item> {
|
||||
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<usize>) {
|
||||
size_hint::add_scalar(self.tail.size_hint(), 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Clone for HeadTail<I>
|
||||
where I: Iterator + Clone,
|
||||
I::Item: Clone
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
clone_fields!(HeadTail, self, head, tail)
|
||||
}
|
||||
}
|
||||
|
||||
/// Make `data` a heap (min-heap w.r.t the sorting).
|
||||
fn heapify<T, S>(data: &mut [T], mut less_than: S)
|
||||
where S: FnMut(&T, &T) -> bool
|
||||
{
|
||||
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<T, S>(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;
|
||||
// the `pos` conditional is to avoid a bounds check
|
||||
while pos < heap.len() && child < heap.len() {
|
||||
let right = child + 1;
|
||||
|
||||
// pick the smaller of the two children
|
||||
if right < heap.len() && less_than(&heap[right], &heap[child]) {
|
||||
child = right;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()`](../trait.Itertools.html#method.kmerge) for more information.
|
||||
pub struct KMerge<I>
|
||||
where I: Iterator
|
||||
{
|
||||
heap: Vec<HeadTail<I>>,
|
||||
}
|
||||
|
||||
/// Create an iterator that merges elements of the contained iterators using
|
||||
/// the ordering function.
|
||||
///
|
||||
/// Equivalent to `iterable.into_iter().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<I>(iterable: I) -> KMerge<<I::Item as IntoIterator>::IntoIter>
|
||||
where I: IntoIterator,
|
||||
I::Item: IntoIterator,
|
||||
<<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd
|
||||
{
|
||||
let iter = iterable.into_iter();
|
||||
let (lower, _) = iter.size_hint();
|
||||
let mut heap = Vec::with_capacity(lower);
|
||||
heap.extend(iter.filter_map(|it| HeadTail::new(it.into_iter())));
|
||||
heapify(&mut heap, |a, b| a.head < b.head);
|
||||
KMerge { heap: heap }
|
||||
}
|
||||
|
||||
impl<I> Clone for KMerge<I>
|
||||
where I: Iterator + Clone,
|
||||
I::Item: Clone
|
||||
{
|
||||
fn clone(&self) -> KMerge<I> {
|
||||
clone_fields!(KMerge, self, heap)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for KMerge<I>
|
||||
where I: Iterator,
|
||||
I::Item: PartialOrd
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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
|
||||
};
|
||||
sift_down(&mut self.heap, 0, |a, b| a.head < b.head);
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.heap.iter()
|
||||
.map(|i| i.size_hint())
|
||||
.fold1(size_hint::add)
|
||||
.unwrap_or((0, Some(0)))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()`](../trait.Itertools.html#method.kmerge_by) for more
|
||||
/// information.
|
||||
pub struct KMergeBy<I, F>
|
||||
where I: Iterator,
|
||||
{
|
||||
heap: Vec<HeadTail<I>>,
|
||||
less_than: F,
|
||||
}
|
||||
|
||||
/// Create an iterator that merges elements of the contained iterators.
|
||||
///
|
||||
/// Equivalent to `iterable.into_iter().kmerge_by(less_than)`.
|
||||
pub fn kmerge_by<I, F>(iterable: I, mut less_than: F)
|
||||
-> KMergeBy<<I::Item as IntoIterator>::IntoIter, F>
|
||||
where I: IntoIterator,
|
||||
I::Item: IntoIterator,
|
||||
F: FnMut(&<<I as IntoIterator>::Item as IntoIterator>::Item,
|
||||
&<<I as IntoIterator>::Item as IntoIterator>::Item) -> bool
|
||||
{
|
||||
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(&a.head, &b.head));
|
||||
KMergeBy { heap: heap, less_than: less_than }
|
||||
}
|
||||
|
||||
|
||||
impl<I, F> Iterator for KMergeBy<I, F>
|
||||
where I: Iterator,
|
||||
F: FnMut(&I::Item, &I::Item) -> bool
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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(&a.head, &b.head));
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.heap.iter()
|
||||
.map(|i| i.size_hint())
|
||||
.fold1(size_hint::add)
|
||||
.unwrap_or((0, Some(0)))
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,114 @@
|
|||
|
||||
/// `MinMaxResult` is an enum returned by `minmax`. See `Itertools::minmax()` for
|
||||
/// more detail.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum MinMaxResult<T> {
|
||||
/// 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<T: Clone> MinMaxResult<T> {
|
||||
/// `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<i32> = 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 {
|
||||
MinMaxResult::NoElements => None,
|
||||
MinMaxResult::OneElement(x) => Some((x.clone(), x)),
|
||||
MinMaxResult::MinMax(x, y) => Some((x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation guts for `minmax` and `minmax_by_key`.
|
||||
pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F,
|
||||
mut lt: L) -> MinMaxResult<I::Item>
|
||||
where I: Iterator,
|
||||
F: FnMut(&I::Item) -> K,
|
||||
L: FnMut(&I::Item, &I::Item, &K, &K) -> bool,
|
||||
{
|
||||
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)
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
use std::iter::Fuse;
|
||||
use size_hint;
|
||||
|
||||
/// 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()`](../trait.Itertools.html#method.pad_using) for more information.
|
||||
#[derive(Clone)]
|
||||
pub struct PadUsing<I, F> {
|
||||
iter: Fuse<I>,
|
||||
min: usize,
|
||||
pos: usize,
|
||||
filler: F,
|
||||
}
|
||||
|
||||
/// Create a new **PadUsing** iterator.
|
||||
pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
|
||||
where I: Iterator,
|
||||
F: FnMut(usize) -> I::Item
|
||||
{
|
||||
PadUsing {
|
||||
iter: iter.fuse(),
|
||||
min: min,
|
||||
pos: 0,
|
||||
filler: filler,
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, F> Iterator for PadUsing<I, F>
|
||||
where I: Iterator,
|
||||
F: FnMut(usize) -> I::Item
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
match self.iter.next() {
|
||||
None => {
|
||||
if self.pos < self.min {
|
||||
let e = Some((self.filler)(self.pos));
|
||||
self.pos += 1;
|
||||
e
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
e => {
|
||||
self.pos += 1;
|
||||
e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let tail = self.min.saturating_sub(self.pos);
|
||||
size_hint::max(self.iter.size_hint(), (tail, Some(tail)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, F> DoubleEndedIterator for PadUsing<I, F>
|
||||
where I: DoubleEndedIterator + ExactSizeIterator,
|
||||
F: FnMut(usize) -> I::Item
|
||||
{
|
||||
fn next_back(&mut self) -> Option<I::Item> {
|
||||
if self.min == 0 {
|
||||
self.iter.next_back()
|
||||
} else if self.iter.len() >= self.min {
|
||||
self.min -= 1;
|
||||
self.iter.next_back()
|
||||
} else {
|
||||
self.min -= 1;
|
||||
Some((self.filler)(self.min))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, F> ExactSizeIterator for PadUsing<I, F>
|
||||
where I: Iterator,
|
||||
F: FnMut(usize) -> I::Item
|
||||
{}
|
|
@ -0,0 +1,148 @@
|
|||
|
||||
use std::iter::Peekable;
|
||||
use PutBack;
|
||||
use PutBackN;
|
||||
|
||||
/// An iterator that allows peeking at an element before deciding to accept it.
|
||||
///
|
||||
/// See [`.peeking_take_while()`](trait.Itertools.html#method.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<F>(&mut self, accept: F) -> Option<Self::Item>
|
||||
where F: FnOnce(&Self::Item) -> bool;
|
||||
}
|
||||
|
||||
impl<I> PeekingNext for Peekable<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
|
||||
where F: FnOnce(&Self::Item) -> bool
|
||||
{
|
||||
if let Some(r) = self.peek() {
|
||||
if !accept(r) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> PeekingNext for PutBack<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
|
||||
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<I> PeekingNext for PutBackN<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator adaptor that takes items while a closure returns `true`.
|
||||
///
|
||||
/// See [`.peeking_take_while()`](../trait.Itertools.html#method.peeking_take_while)
|
||||
/// for more information.
|
||||
pub struct PeekingTakeWhile<'a, I: 'a, F>
|
||||
where I: Iterator,
|
||||
{
|
||||
iter: &'a mut I,
|
||||
f: F,
|
||||
}
|
||||
|
||||
/// Create a PeekingTakeWhile
|
||||
pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F>
|
||||
where I: Iterator,
|
||||
{
|
||||
PeekingTakeWhile {
|
||||
iter: iter,
|
||||
f: 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::Item> {
|
||||
self.iter.peeking_next(&mut self.f)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (_, hi) = self.iter.size_hint();
|
||||
(0, hi)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
(@as_item $x:item) => ($x);
|
||||
([$($typarm:tt)*] $type_:ty) => {
|
||||
// FIXME: Ast coercion is dead as soon as we can dep on Rust 1.12
|
||||
peeking_next_by_clone! { @as_item
|
||||
impl<$($typarm)*> PeekingNext for $type_ {
|
||||
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
|
||||
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<T> }
|
||||
peeking_next_by_clone! { ['a, T] ::std::collections::linked_list::Iter<'a, T> }
|
||||
peeking_next_by_clone! { ['a, T] ::std::collections::vec_deque::Iter<'a, T> }
|
||||
|
||||
// cloning a Rev has no extra overhead; peekable and put backs are never DEI.
|
||||
peeking_next_by_clone! { [I: Clone + PeekingNext + DoubleEndedIterator]
|
||||
::std::iter::Rev<I> }
|
|
@ -0,0 +1,89 @@
|
|||
|
||||
use std::iter::IntoIterator;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
/// A wrapper for `Rc<RefCell<I>>`, that implements the `Iterator` trait.
|
||||
pub struct RcIter<I> {
|
||||
/// The boxed iterator.
|
||||
pub rciter: Rc<RefCell<I>>,
|
||||
}
|
||||
|
||||
/// Return an iterator inside a `Rc<RefCell<_>>` 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.
|
||||
/// (If it is not obvious: this has a performance penalty.)
|
||||
///
|
||||
/// Iterator element type is `Self::Item`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::rciter;
|
||||
///
|
||||
/// let mut rit = rciter(0..9);
|
||||
/// let mut z = rit.clone().zip(rit.clone());
|
||||
/// assert_eq!(z.next(), Some((0, 1)));
|
||||
/// assert_eq!(z.next(), Some((2, 3)));
|
||||
/// assert_eq!(z.next(), Some((4, 5)));
|
||||
/// assert_eq!(rit.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,
|
||||
/// but it can only happen if the `RcIter` is reentered in for example `.next()`,
|
||||
/// i.e. if it somehow participates in an “iterator knot” where it is an adaptor of itself.
|
||||
pub fn rciter<I>(iterable: I) -> RcIter<I::IntoIter>
|
||||
where I: IntoIterator
|
||||
{
|
||||
RcIter { rciter: Rc::new(RefCell::new(iterable.into_iter())) }
|
||||
}
|
||||
|
||||
impl<I> Clone for RcIter<I> {
|
||||
#[inline]
|
||||
fn clone(&self) -> RcIter<I> {
|
||||
RcIter { rciter: self.rciter.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, I> Iterator for RcIter<I>
|
||||
where I: Iterator<Item = A>
|
||||
{
|
||||
type Item = A;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<A> {
|
||||
self.rciter.borrow_mut().next()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
// 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.
|
||||
let (_, hi) = self.rciter.borrow().size_hint();
|
||||
(0, hi)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> DoubleEndedIterator for RcIter<I>
|
||||
where I: DoubleEndedIterator
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<I::Item> {
|
||||
self.rciter.borrow_mut().next_back()
|
||||
}
|
||||
}
|
||||
/// Return an iterator from `&RcIter<I>` (by simply cloning it).
|
||||
impl<'a, I> IntoIterator for &'a RcIter<I>
|
||||
where I: Iterator
|
||||
{
|
||||
type Item = I::Item;
|
||||
type IntoIter = RcIter<I>;
|
||||
|
||||
fn into_iter(self) -> RcIter<I> {
|
||||
self.clone()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
/// An iterator that produces *n* repetitions of an element.
|
||||
///
|
||||
/// See [`repeat_n()`](../fn.repeat_n.html) for more information.
|
||||
pub struct RepeatN<A> {
|
||||
elt: Option<A>,
|
||||
n: usize,
|
||||
}
|
||||
|
||||
/// Create an iterator that produces `n` repetitions of `element`.
|
||||
pub fn repeat_n<A>(element: A, n: usize) -> RepeatN<A>
|
||||
where A: Clone,
|
||||
{
|
||||
if n == 0 {
|
||||
RepeatN { elt: None, n: n, }
|
||||
} else {
|
||||
RepeatN { elt: Some(element), n: n, }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> RepeatN<A> {
|
||||
#[deprecated(note = "The ::new constructor is deprecated. Use `repeat_n`")]
|
||||
///
|
||||
pub fn new(elt: A, n: usize) -> Self {
|
||||
// The code is duplicated here because the new version uses
|
||||
// the proper A: Clone bound.
|
||||
if n == 0 {
|
||||
RepeatN { elt: None, n: n }
|
||||
} else {
|
||||
RepeatN {
|
||||
elt: Some(elt),
|
||||
n: n,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Iterator for RepeatN<A>
|
||||
where A: Clone
|
||||
{
|
||||
type Item = A;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<usize>) {
|
||||
(self.n, Some(self.n))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> DoubleEndedIterator for RepeatN<A>
|
||||
where A: Clone
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> ExactSizeIterator for RepeatN<A>
|
||||
where A: Clone
|
||||
{}
|
|
@ -0,0 +1,95 @@
|
|||
//! Arithmetic on **Iterator** *.size_hint()* values.
|
||||
//!
|
||||
|
||||
use std::usize;
|
||||
use std::cmp;
|
||||
|
||||
/// **SizeHint** is the return type of **Iterator::size_hint()**.
|
||||
pub type SizeHint = (usize, Option<usize>);
|
||||
|
||||
/// Add **SizeHint** correctly.
|
||||
#[inline]
|
||||
pub fn add(a: SizeHint, b: SizeHint) -> SizeHint {
|
||||
let min = a.0.checked_add(b.0).unwrap_or(usize::MAX);
|
||||
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)
|
||||
}
|
||||
|
||||
/// Sbb **x** correctly to a **SizeHint**.
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn sub_scalar(sh: SizeHint, x: usize) -> SizeHint {
|
||||
let (mut low, mut hi) = sh;
|
||||
low = low.saturating_sub(x);
|
||||
hi = hi.map(|elt| elt.saturating_sub(x));
|
||||
(low, hi)
|
||||
}
|
||||
|
||||
|
||||
/// Multiply **SizeHint** correctly
|
||||
///
|
||||
/// ```ignore
|
||||
/// use std::usize;
|
||||
/// use itertools::size_hint;
|
||||
///
|
||||
/// assert_eq!(size_hint::mul((3, Some(4)), (3, Some(4))),
|
||||
/// (9, Some(16)));
|
||||
///
|
||||
/// assert_eq!(size_hint::mul((3, Some(4)), (usize::MAX, None)),
|
||||
/// (usize::MAX, None));
|
||||
///
|
||||
/// assert_eq!(size_hint::mul((3, None), (0, Some(0))),
|
||||
/// (0, Some(0)));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn mul(a: SizeHint, b: SizeHint) -> SizeHint {
|
||||
let low = a.0.checked_mul(b.0).unwrap_or(usize::MAX);
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
//! Iterators that are sources (produce elements from parameters,
|
||||
//! not from another iterator).
|
||||
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
/// See [`repeat_call`](../fn.repeat_call.html) for more information.
|
||||
pub struct RepeatCall<F> {
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<F> fmt::Debug for RepeatCall<F>
|
||||
{
|
||||
debug_fmt_fields!(RepeatCall, );
|
||||
}
|
||||
|
||||
/// An iterator source that produces elements indefinitely by calling
|
||||
/// a given closure.
|
||||
///
|
||||
/// Iterator element type is the return type of the closure.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::repeat_call;
|
||||
/// use itertools::Itertools;
|
||||
/// use std::collections::BinaryHeap;
|
||||
///
|
||||
/// let mut heap = BinaryHeap::from(vec![2, 5, 3, 7, 8]);
|
||||
///
|
||||
/// // extract each element in sorted order
|
||||
/// for element in repeat_call(|| heap.pop()).while_some() {
|
||||
/// print!("{}", element);
|
||||
/// }
|
||||
///
|
||||
/// itertools::assert_equal(
|
||||
/// repeat_call(|| 1).take(5),
|
||||
/// vec![1, 1, 1, 1, 1]
|
||||
/// );
|
||||
/// ```
|
||||
pub fn repeat_call<F>(function: F) -> RepeatCall<F> {
|
||||
RepeatCall { f: function }
|
||||
}
|
||||
|
||||
impl<A, F> Iterator for RepeatCall<F>
|
||||
where F: FnMut() -> A
|
||||
{
|
||||
type Item = A;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<A> {
|
||||
Some((self.f)())
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(usize::max_value(), None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new unfold source with the specified closure as the "iterator
|
||||
/// function" and an initial state to eventually pass to the closure
|
||||
///
|
||||
/// `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((1_u32, 1_u32), |state| {
|
||||
/// let (ref mut x1, ref mut x2) = *state;
|
||||
///
|
||||
/// // 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 {
|
||||
/// return None;
|
||||
/// }
|
||||
///
|
||||
/// 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))
|
||||
/// ```
|
||||
pub fn unfold<A, St, F>(initial_state: St, f: F) -> Unfold<St, F>
|
||||
where F: FnMut(&mut St) -> Option<A>
|
||||
{
|
||||
Unfold {
|
||||
f: f,
|
||||
state: initial_state,
|
||||
}
|
||||
}
|
||||
|
||||
impl<St, F> fmt::Debug for Unfold<St, F>
|
||||
where St: fmt::Debug,
|
||||
{
|
||||
debug_fmt_fields!(Unfold, state);
|
||||
}
|
||||
|
||||
/// See [`unfold`](../fn.unfold.html) for more information.
|
||||
#[derive(Clone)]
|
||||
pub struct Unfold<St, F> {
|
||||
f: F,
|
||||
/// Internal state that will be passed to the closure on the next iteration
|
||||
pub state: St,
|
||||
}
|
||||
|
||||
impl<A, St, F> Iterator for Unfold<St, F>
|
||||
where F: FnMut(&mut St) -> Option<A>
|
||||
{
|
||||
type Item = A;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<A> {
|
||||
(self.f)(&mut self.state)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
// no possible known bounds at this point
|
||||
(0, None)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that infinitely applies function to value and yields results.
|
||||
///
|
||||
/// This `struct` is created by the [`iterate()`] function. See its documentation for more.
|
||||
///
|
||||
/// [`iterate()`]: ../fn.iterate.html
|
||||
#[derive(Clone)]
|
||||
pub struct Iterate<St, F> {
|
||||
state: St,
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<St, F> fmt::Debug for Iterate<St, F>
|
||||
where St: fmt::Debug,
|
||||
{
|
||||
debug_fmt_fields!(Iterate, state);
|
||||
}
|
||||
|
||||
impl<St, F> Iterator for Iterate<St, F>
|
||||
where F: FnMut(&St) -> St
|
||||
{
|
||||
type Item = St;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next_state = (self.f)(&self.state);
|
||||
Some(mem::replace(&mut self.state, next_state))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(usize::max_value(), 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).take(5), vec![1, 3, 9, 27, 81]);
|
||||
/// ```
|
||||
pub fn iterate<St, F>(initial_value: St, f: F) -> Iterate<St, F>
|
||||
where F: FnMut(&St) -> St
|
||||
{
|
||||
Iterate {
|
||||
state: initial_value,
|
||||
f: f,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
use super::size_hint;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Common buffer object for the two tee halves
|
||||
struct TeeBuffer<A, I> {
|
||||
backlog: VecDeque<A>,
|
||||
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()`](../trait.Itertools.html#method.tee) for more information.
|
||||
pub struct Tee<I>
|
||||
where I: Iterator
|
||||
{
|
||||
rcbuffer: Rc<RefCell<TeeBuffer<I::Item, I>>>,
|
||||
id: bool,
|
||||
}
|
||||
|
||||
pub fn new<I>(iter: I) -> (Tee<I>, Tee<I>)
|
||||
where I: Iterator
|
||||
{
|
||||
let buffer = TeeBuffer{backlog: VecDeque::new(), iter: iter, owner: false};
|
||||
let t1 = Tee{rcbuffer: Rc::new(RefCell::new(buffer)), id: true};
|
||||
let t2 = Tee{rcbuffer: t1.rcbuffer.clone(), id: false};
|
||||
(t1, t2)
|
||||
}
|
||||
|
||||
impl<I> Iterator for Tee<I>
|
||||
where I: Iterator,
|
||||
I::Item: Clone
|
||||
{
|
||||
type Item = I::Item;
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
// .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<usize>) {
|
||||
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<I> ExactSizeIterator for Tee<I>
|
||||
where I: ExactSizeIterator,
|
||||
I::Item: Clone
|
||||
{}
|
|
@ -0,0 +1,262 @@
|
|||
//! Some iterator that produces tuples
|
||||
|
||||
use std::iter::Fuse;
|
||||
|
||||
/// An iterator over a incomplete tuple.
|
||||
///
|
||||
/// See [`.tuples()`](../trait.Itertools.html#method.tuples) and
|
||||
/// [`Tuples::into_buffer()`](struct.Tuples.html#method.into_buffer).
|
||||
pub struct TupleBuffer<T>
|
||||
where T: TupleCollect
|
||||
{
|
||||
cur: usize,
|
||||
buf: T::Buffer,
|
||||
}
|
||||
|
||||
impl<T> TupleBuffer<T>
|
||||
where T: TupleCollect
|
||||
{
|
||||
fn new(buf: T::Buffer) -> Self {
|
||||
TupleBuffer {
|
||||
cur: 0,
|
||||
buf: buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for TupleBuffer<T>
|
||||
where T: TupleCollect
|
||||
{
|
||||
type Item = T::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<usize>) {
|
||||
let buffer = &self.buf.as_ref()[self.cur..];
|
||||
let len = if buffer.len() == 0 {
|
||||
0
|
||||
} else {
|
||||
buffer.iter()
|
||||
.position(|x| x.is_none())
|
||||
.unwrap_or(buffer.len())
|
||||
};
|
||||
(len, Some(len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ExactSizeIterator for TupleBuffer<T>
|
||||
where T: TupleCollect
|
||||
{
|
||||
}
|
||||
|
||||
/// An iterator that groups the items in tuples of a specific size.
|
||||
///
|
||||
/// See [`.tuples()`](../trait.Itertools.html#method.tuples) for more information.
|
||||
pub struct Tuples<I, T>
|
||||
where I: Iterator<Item = T::Item>,
|
||||
T: TupleCollect
|
||||
{
|
||||
iter: Fuse<I>,
|
||||
buf: T::Buffer,
|
||||
}
|
||||
|
||||
/// Create a new tuples iterator.
|
||||
pub fn tuples<I, T>(iter: I) -> Tuples<I, T>
|
||||
where I: Iterator<Item = T::Item>,
|
||||
T: TupleCollect
|
||||
{
|
||||
Tuples {
|
||||
iter: iter.fuse(),
|
||||
buf: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> Iterator for Tuples<I, T>
|
||||
where I: Iterator<Item = T::Item>,
|
||||
T: TupleCollect
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<T> {
|
||||
T::collect_from_iter(&mut self.iter, &mut self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> Tuples<I, T>
|
||||
where I: Iterator<Item = T::Item>,
|
||||
T: TupleCollect
|
||||
{
|
||||
/// 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<T> {
|
||||
TupleBuffer::new(self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// An iterator over all contiguous windows that produces tuples of a specific size.
|
||||
///
|
||||
/// See [`.tuple_windows()`](../trait.Itertools.html#method.tuple_windows) for more
|
||||
/// information.
|
||||
pub struct TupleWindows<I, T>
|
||||
where I: Iterator<Item = T::Item>,
|
||||
T: TupleCollect
|
||||
{
|
||||
iter: I,
|
||||
last: Option<T>,
|
||||
}
|
||||
|
||||
/// Create a new tuple windows iterator.
|
||||
pub fn tuple_windows<I, T>(mut iter: I) -> TupleWindows<I, T>
|
||||
where I: Iterator<Item = T::Item>,
|
||||
T: TupleCollect,
|
||||
T::Item: Clone
|
||||
{
|
||||
use std::iter::once;
|
||||
|
||||
let mut last = None;
|
||||
if T::num_items() != 1 {
|
||||
// put in a duplicate item in front of the tuple; this simplifies
|
||||
// .next() function.
|
||||
if let Some(item) = iter.next() {
|
||||
let iter = once(item.clone()).chain(once(item)).chain(&mut iter);
|
||||
last = T::collect_from_iter_no_buf(iter);
|
||||
}
|
||||
}
|
||||
|
||||
TupleWindows {
|
||||
last: last,
|
||||
iter: iter,
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> Iterator for TupleWindows<I, T>
|
||||
where I: Iterator<Item = T::Item>,
|
||||
T: TupleCollect + Clone,
|
||||
T::Item: Clone
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<T> {
|
||||
if T::num_items() == 1 {
|
||||
return T::collect_from_iter_no_buf(&mut self.iter)
|
||||
}
|
||||
if let Some(ref mut last) = self.last {
|
||||
if let Some(new) = self.iter.next() {
|
||||
last.left_shift_push(new);
|
||||
return Some(last.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TupleCollect: Sized {
|
||||
type Item;
|
||||
type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>;
|
||||
|
||||
fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
|
||||
where I: IntoIterator<Item = Self::Item>;
|
||||
|
||||
fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
|
||||
where I: IntoIterator<Item = Self::Item>;
|
||||
|
||||
fn num_items() -> usize;
|
||||
|
||||
fn left_shift_push(&mut self, item: Self::Item);
|
||||
}
|
||||
|
||||
macro_rules! impl_tuple_collect {
|
||||
() => ();
|
||||
($N:expr; $A:ident ; $($X:ident),* ; $($Y:ident),* ; $($Y_rev:ident),*) => (
|
||||
impl<$A> TupleCollect for ($($X),*,) {
|
||||
type Item = $A;
|
||||
type Buffer = [Option<$A>; $N - 1];
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
|
||||
where I: IntoIterator<Item = $A>
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
|
||||
where I: IntoIterator<Item = $A>
|
||||
{
|
||||
let mut iter = iter.into_iter();
|
||||
loop {
|
||||
$(
|
||||
let $Y = if let Some($Y) = iter.next() {
|
||||
$Y
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
)*
|
||||
return Some(($($Y),*,))
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
fn num_items() -> usize {
|
||||
$N
|
||||
}
|
||||
|
||||
fn left_shift_push(&mut self, item: $A) {
|
||||
use std::mem::replace;
|
||||
|
||||
let &mut ($(ref mut $Y),*,) = self;
|
||||
let tmp = item;
|
||||
$(
|
||||
let tmp = replace($Y_rev, tmp);
|
||||
)*
|
||||
drop(tmp);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
impl_tuple_collect!(1; A; A; a; a);
|
||||
impl_tuple_collect!(2; A; A, A; a, b; b, a);
|
||||
impl_tuple_collect!(3; A; A, A, A; a, b, c; c, b, a);
|
||||
impl_tuple_collect!(4; A; A, A, A, A; a, b, c, d; d, c, b, a);
|
|
@ -0,0 +1,89 @@
|
|||
use std::iter::{Fuse,Peekable};
|
||||
|
||||
/// An iterator adaptor that wraps each element in an [`Position`](../enum.Position.html).
|
||||
///
|
||||
/// Iterator element type is `Position<I::Item>`.
|
||||
///
|
||||
/// See [`.with_position()`](../trait.Itertools.html#method.with_position) for more information.
|
||||
pub struct WithPosition<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
handled_first: bool,
|
||||
peekable: Peekable<Fuse<I>>,
|
||||
}
|
||||
|
||||
/// Create a new `WithPosition` iterator.
|
||||
pub fn with_position<I>(iter: I) -> WithPosition<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
WithPosition {
|
||||
handled_first: false,
|
||||
peekable: iter.fuse().peekable(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A value yielded by `WithPosition`.
|
||||
/// Indicates the position of this element in the iterator results.
|
||||
///
|
||||
/// See [`.with_position()`](trait.Itertools.html#method.with_position) for more information.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Position<T> {
|
||||
/// This is the first element.
|
||||
First(T),
|
||||
/// This is neither the first nor the last element.
|
||||
Middle(T),
|
||||
/// This is the last element.
|
||||
Last(T),
|
||||
/// This is the only element.
|
||||
Only(T),
|
||||
}
|
||||
|
||||
impl<T> Position<T> {
|
||||
/// Return the inner value.
|
||||
pub fn into_inner(self) -> T {
|
||||
match self {
|
||||
Position::First(x) |
|
||||
Position::Middle(x) |
|
||||
Position::Last(x) |
|
||||
Position::Only(x) => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator> Iterator for WithPosition<I> {
|
||||
type Item = Position<I::Item>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.peekable.next() {
|
||||
Some(item) => {
|
||||
if !self.handled_first {
|
||||
// Haven't seen the first item yet, and there is one to give.
|
||||
self.handled_first = true;
|
||||
// Peek to see if this is also the last item,
|
||||
// in which case tag it as `Only`.
|
||||
match self.peekable.peek() {
|
||||
Some(_) => Some(Position::First(item)),
|
||||
None => Some(Position::Only(item)),
|
||||
}
|
||||
} else {
|
||||
// Have seen the first item, and there's something left.
|
||||
// Peek to see if this is the last item.
|
||||
match self.peekable.peek() {
|
||||
Some(_) => Some(Position::Middle(item)),
|
||||
None => Some(Position::Last(item)),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Iterator is finished.
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.peekable.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> ExactSizeIterator for WithPosition<I>
|
||||
where I: ExactSizeIterator,
|
||||
{ }
|
|
@ -0,0 +1,60 @@
|
|||
use super::size_hint;
|
||||
|
||||
/// An iterator which iterates two other iterators simultaneously
|
||||
///
|
||||
/// See [`.zip_eq()`](../trait.Itertools.html#method.zip_eq) for more information.
|
||||
#[derive(Clone)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct ZipEq<I, J> {
|
||||
a: I,
|
||||
b: J,
|
||||
}
|
||||
|
||||
/// Iterate `i` and `j` in lock step.
|
||||
///
|
||||
/// **Panics** if the iterators are not of the same length.
|
||||
///
|
||||
/// `IntoIterator` enabled version of `i.zip_eq(j)`.
|
||||
///
|
||||
/// ```
|
||||
/// 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, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter>
|
||||
where I: IntoIterator,
|
||||
J: IntoIterator
|
||||
{
|
||||
ZipEq {
|
||||
a: i.into_iter(),
|
||||
b: j.into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, J> Iterator for ZipEq<I, J>
|
||||
where I: Iterator,
|
||||
J: Iterator
|
||||
{
|
||||
type Item = (I::Item, J::Item);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<usize>) {
|
||||
size_hint::min(self.a.size_hint(), self.b.size_hint())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, J> ExactSizeIterator for ZipEq<I, J>
|
||||
where I: ExactSizeIterator,
|
||||
J: ExactSizeIterator
|
||||
{}
|
|
@ -0,0 +1,94 @@
|
|||
use std::cmp::Ordering::{Equal, Greater, Less};
|
||||
use super::size_hint;
|
||||
use std::iter::Fuse;
|
||||
use self::EitherOrBoth::{Right, Left, Both};
|
||||
|
||||
// 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
|
||||
///
|
||||
/// This iterator is *fused*.
|
||||
///
|
||||
/// See [`.zip_longest()`](../trait.Itertools.html#method.zip_longest) for more information.
|
||||
#[derive(Clone)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct ZipLongest<T, U> {
|
||||
a: Fuse<T>,
|
||||
b: Fuse<U>,
|
||||
}
|
||||
|
||||
/// Create a new `ZipLongest` iterator.
|
||||
pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U>
|
||||
where T: Iterator,
|
||||
U: Iterator
|
||||
{
|
||||
ZipLongest {
|
||||
a: a.fuse(),
|
||||
b: b.fuse(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Iterator for ZipLongest<T, U>
|
||||
where T: Iterator,
|
||||
U: Iterator
|
||||
{
|
||||
type Item = EitherOrBoth<T::Item, U::Item>;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match (self.a.next(), self.b.next()) {
|
||||
(None, None) => None,
|
||||
(Some(a), None) => Some(Left(a)),
|
||||
(None, Some(b)) => Some(Right(b)),
|
||||
(Some(a), Some(b)) => Some(Both(a, b)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
size_hint::max(self.a.size_hint(), self.b.size_hint())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
|
||||
where T: DoubleEndedIterator + ExactSizeIterator,
|
||||
U: DoubleEndedIterator + ExactSizeIterator
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
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(Both(a, b)),
|
||||
// These can only happen if .len() is inconsistent with .next_back()
|
||||
(Some(a), None) => Some(Left(a)),
|
||||
(None, Some(b)) => Some(Right(b)),
|
||||
},
|
||||
Greater => self.a.next_back().map(Left),
|
||||
Less => self.b.next_back().map(Right),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ExactSizeIterator for ZipLongest<T, U>
|
||||
where T: ExactSizeIterator,
|
||||
U: ExactSizeIterator
|
||||
{}
|
||||
|
||||
|
||||
/// A value yielded by `ZipLongest`.
|
||||
/// Contains one or two values, depending on which of the input iterators are exhausted.
|
||||
///
|
||||
/// See [`.zip_longest()`](trait.Itertools.html#method.zip_longest) for more information.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum EitherOrBoth<A, B> {
|
||||
/// Neither input iterator is exhausted yet, yielding two values.
|
||||
Both(A, B),
|
||||
/// The parameter iterator of `.zip_longest()` is exhausted,
|
||||
/// only yielding a value from the `self` iterator.
|
||||
Left(A),
|
||||
/// The `self` iterator of `.zip_longest()` is exhausted,
|
||||
/// only yielding a value from the parameter iterator.
|
||||
Right(B),
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
use super::size_hint;
|
||||
|
||||
/// See [`multizip`](../fn.multizip.html) for more information.
|
||||
#[derive(Clone)]
|
||||
pub struct Zip<T> {
|
||||
t: T,
|
||||
}
|
||||
|
||||
impl<T> Zip<T> {
|
||||
/// Deprecated: renamed to multizip
|
||||
#[deprecated(note = "Renamed to multizip")]
|
||||
pub fn new<U>(t: U) -> Zip<T>
|
||||
where Zip<T>: From<U>,
|
||||
Zip<T>: Iterator,
|
||||
{
|
||||
multizip(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.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::multizip;
|
||||
///
|
||||
/// // Iterate over three sequences side-by-side
|
||||
/// let mut xs = [0, 0, 0];
|
||||
/// let ys = [69, 107, 101];
|
||||
///
|
||||
/// for (i, a, b) in multizip((0..100, &mut xs, &ys)) {
|
||||
/// *a = i ^ *b;
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(xs, [69, 106, 103]);
|
||||
/// ```
|
||||
pub fn multizip<T, U>(t: U) -> Zip<T>
|
||||
where Zip<T>: From<U>,
|
||||
Zip<T>: 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<Self::Item>
|
||||
{
|
||||
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<usize>)
|
||||
{
|
||||
let sh = (::std::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,
|
||||
)*
|
||||
{ }
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
extern crate itertools;
|
||||
|
||||
use 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);
|
||||
}
|
|
@ -0,0 +1,768 @@
|
|||
//! 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.
|
||||
|
||||
#[macro_use] extern crate itertools;
|
||||
|
||||
extern crate quickcheck;
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
use quickcheck as qc;
|
||||
use std::ops::Range;
|
||||
use std::cmp::Ordering;
|
||||
use itertools::Itertools;
|
||||
use itertools::{
|
||||
multizip,
|
||||
EitherOrBoth,
|
||||
};
|
||||
use itertools::free::{
|
||||
cloned,
|
||||
enumerate,
|
||||
multipeek,
|
||||
put_back,
|
||||
put_back_n,
|
||||
rciter,
|
||||
zip,
|
||||
zip_eq,
|
||||
};
|
||||
|
||||
use quickcheck::TestResult;
|
||||
|
||||
/// Our base iterator that we can impl Arbitrary for
|
||||
///
|
||||
/// 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<T>(Range<T>, i32); // with fuse/done flag
|
||||
|
||||
impl<T> Iter<T>
|
||||
{
|
||||
fn new(it: Range<T>) -> Self
|
||||
{
|
||||
Iter(it, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for Iter<T> where Range<T>: Iterator,
|
||||
<Range<T> as Iterator>::Item: Default,
|
||||
{
|
||||
type Item = <Range<T> as Iterator>::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item>
|
||||
{
|
||||
let elt = self.0.next();
|
||||
if elt.is_none() {
|
||||
self.1 += 1;
|
||||
// check fuse flag
|
||||
if self.1 == 2 {
|
||||
return Some(Default::default())
|
||||
}
|
||||
}
|
||||
elt
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>)
|
||||
{
|
||||
self.0.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DoubleEndedIterator for Iter<T> where Range<T>: DoubleEndedIterator,
|
||||
<Range<T> as Iterator>::Item: Default,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> { self.0.next_back() }
|
||||
}
|
||||
|
||||
impl<T> ExactSizeIterator for Iter<T> where Range<T>: ExactSizeIterator,
|
||||
<Range<T> as Iterator>::Item: Default,
|
||||
{ }
|
||||
|
||||
impl<T> qc::Arbitrary for Iter<T> where T: qc::Arbitrary
|
||||
{
|
||||
fn arbitrary<G: qc::Gen>(g: &mut G) -> Self
|
||||
{
|
||||
Iter::new(T::arbitrary(g)..T::arbitrary(g))
|
||||
}
|
||||
|
||||
fn shrink(&self) -> Box<Iterator<Item=Iter<T>>>
|
||||
{
|
||||
let r = self.0.clone();
|
||||
Box::new(
|
||||
r.start.shrink().flat_map(move |x| {
|
||||
r.end.shrink().map(move |y| (x.clone(), y))
|
||||
})
|
||||
.map(|(a, b)| Iter::new(a..b))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn correct_size_hint<I: Iterator>(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<I: ExactSizeIterator>(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<I: Iterator>(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<i8> is broken!
|
||||
* (all signed ranges are)
|
||||
#[quickcheck]
|
||||
fn size_range_i8(a: Iter<i8>) -> bool {
|
||||
exact_size(a)
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn size_range_i16(a: Iter<i16>) -> bool {
|
||||
exact_size(a)
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn size_range_u8(a: Iter<u8>) -> 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)* })*} => (
|
||||
quickcheck!{@as_items
|
||||
$(
|
||||
#[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)*]) => {
|
||||
quickcheck!(@as_expr $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)*)
|
||||
};
|
||||
(@as_items $($i:item)*) => ($($i)*);
|
||||
(@as_expr $i:expr) => ($i);
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
|
||||
fn size_product(a: Iter<u16>, b: Iter<u16>) -> bool {
|
||||
correct_size_hint(a.cartesian_product(b))
|
||||
}
|
||||
fn size_product3(a: Iter<u16>, b: Iter<u16>, c: Iter<u16>) -> bool {
|
||||
correct_size_hint(iproduct!(a, b, c))
|
||||
}
|
||||
|
||||
fn size_step(a: Iter<i16>, s: usize) -> bool {
|
||||
let mut s = s;
|
||||
if s == 0 {
|
||||
s += 1; // never zero
|
||||
}
|
||||
let filt = a.clone().dedup();
|
||||
correct_size_hint(filt.step(s)) &&
|
||||
exact_size(a.step(s))
|
||||
}
|
||||
fn equal_step(a: Iter<i16>, s: usize) -> bool {
|
||||
let mut s = s;
|
||||
if s == 0 {
|
||||
s += 1; // never zero
|
||||
}
|
||||
let mut i = 0;
|
||||
itertools::equal(a.clone().step(s), a.filter(|_| {
|
||||
let keep = i % s == 0;
|
||||
i += 1;
|
||||
keep
|
||||
}))
|
||||
}
|
||||
fn equal_step_vec(a: Vec<i16>, s: usize) -> bool {
|
||||
let mut s = s;
|
||||
if s == 0 {
|
||||
s += 1; // never zero
|
||||
}
|
||||
let mut i = 0;
|
||||
itertools::equal(a.iter().step(s), a.iter().filter(|_| {
|
||||
let keep = i % s == 0;
|
||||
i += 1;
|
||||
keep
|
||||
}))
|
||||
}
|
||||
|
||||
fn size_multipeek(a: Iter<u16>, s: u8) -> bool {
|
||||
let mut it = multipeek(a);
|
||||
// peek a few times
|
||||
for _ in 0..s {
|
||||
it.peek();
|
||||
}
|
||||
exact_size(it)
|
||||
}
|
||||
|
||||
fn equal_merge(a: Vec<i16>, b: Vec<i16>) -> bool {
|
||||
let mut sa = a.clone();
|
||||
let mut sb = b.clone();
|
||||
sa.sort();
|
||||
sb.sort();
|
||||
let mut merged = sa.clone();
|
||||
merged.extend(sb.iter().cloned());
|
||||
merged.sort();
|
||||
itertools::equal(&merged, sa.iter().merge(&sb))
|
||||
}
|
||||
fn size_merge(a: Iter<u16>, b: Iter<u16>) -> bool {
|
||||
correct_size_hint(a.merge(b))
|
||||
}
|
||||
fn size_zip(a: Iter<i16>, b: Iter<i16>, c: Iter<i16>) -> 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<i16>, b: Iter<i16>) -> bool {
|
||||
let rc = rciter(a.clone());
|
||||
correct_size_hint(multizip((&rc, &rc, b)))
|
||||
}
|
||||
|
||||
fn equal_kmerge(a: Vec<i16>, b: Vec<i16>, c: Vec<i16>) -> bool {
|
||||
use itertools::free::kmerge;
|
||||
let mut sa = a.clone();
|
||||
let mut sb = b.clone();
|
||||
let mut sc = c.clone();
|
||||
sa.sort();
|
||||
sb.sort();
|
||||
sc.sort();
|
||||
let mut merged = sa.clone();
|
||||
merged.extend(sb.iter().cloned());
|
||||
merged.extend(sc.iter().cloned());
|
||||
merged.sort();
|
||||
itertools::equal(merged.into_iter(), kmerge(vec![sa, sb, sc]))
|
||||
}
|
||||
|
||||
// Any number of input iterators
|
||||
fn equal_kmerge_2(mut inputs: Vec<Vec<i16>>) -> 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<Vec<i16>>) -> 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<Vec<i16>>) -> 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<Vec<i16>>) -> 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<i16>, b: Iter<i16>, c: Iter<i16>) -> bool {
|
||||
use itertools::free::kmerge;
|
||||
correct_size_hint(kmerge(vec![a, b, c]))
|
||||
}
|
||||
fn equal_zip_eq(a: Vec<i32>, b: Vec<i32>) -> 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))
|
||||
}
|
||||
fn size_zip_longest(a: Iter<i16>, b: Iter<i16>) -> 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<i16>, b: Iter<i16>) -> bool {
|
||||
let it = a.clone().zip_longest(b.clone());
|
||||
let jt = a.clone().zip_longest(b.clone());
|
||||
itertools::equal(a.clone(),
|
||||
it.filter_map(|elt| match elt {
|
||||
EitherOrBoth::Both(x, _) => Some(x),
|
||||
EitherOrBoth::Left(x) => Some(x),
|
||||
_ => None,
|
||||
}
|
||||
))
|
||||
&&
|
||||
itertools::equal(b.clone(),
|
||||
jt.filter_map(|elt| match elt {
|
||||
EitherOrBoth::Both(_, y) => Some(y),
|
||||
EitherOrBoth::Right(y) => Some(y),
|
||||
_ => None,
|
||||
}
|
||||
))
|
||||
}
|
||||
fn size_interleave(a: Iter<i16>, b: Iter<i16>) -> bool {
|
||||
correct_size_hint(a.interleave(b))
|
||||
}
|
||||
fn exact_interleave(a: Iter<i16>, b: Iter<i16>) -> bool {
|
||||
exact_size_for_this(a.interleave(b))
|
||||
}
|
||||
fn size_interleave_shortest(a: Iter<i16>, b: Iter<i16>) -> 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<i16>, x: i16) -> bool {
|
||||
correct_size_hint(a.intersperse(x))
|
||||
}
|
||||
fn equal_intersperse(a: Vec<i32>, 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_flatten(a: Vec<Option<i32>>) -> bool {
|
||||
itertools::equal(a.iter().flatten(),
|
||||
a.iter().filter_map(|x| x.as_ref()))
|
||||
}
|
||||
|
||||
fn equal_flatten_vec(a: Vec<Vec<u8>>) -> bool {
|
||||
itertools::equal(a.iter().flatten(),
|
||||
a.iter().flat_map(|x| x))
|
||||
}
|
||||
fn equal_flatten_vec_rev(a: Vec<Vec<u8>>) -> bool {
|
||||
itertools::equal(a.iter().flatten().rev(),
|
||||
a.iter().flat_map(|x| x).rev())
|
||||
}
|
||||
|
||||
fn equal_combinations_2(a: Vec<u8>) -> bool {
|
||||
let mut v = Vec::new();
|
||||
for (i, &x) in enumerate(&a) {
|
||||
for &y in &a[i + 1..] {
|
||||
v.push((x, y));
|
||||
}
|
||||
}
|
||||
itertools::equal(cloned(&a).tuple_combinations::<(_, _)>(), cloned(&v))
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn equal_dedup(a: Vec<i32>) -> bool {
|
||||
let mut b = a.clone();
|
||||
b.dedup();
|
||||
itertools::equal(&b, a.iter().dedup())
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn size_dedup(a: Vec<i32>) -> bool {
|
||||
correct_size_hint(a.iter().dedup())
|
||||
}
|
||||
}
|
||||
|
||||
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<u8>, x: Option<u8>) -> bool {
|
||||
let mut it = put_back(a.into_iter());
|
||||
match x {
|
||||
Some(t) => it.put_back(t),
|
||||
None => {}
|
||||
}
|
||||
correct_size_hint(it)
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn size_put_backn(a: Vec<u8>, b: Vec<u8>) -> bool {
|
||||
let mut it = put_back_n(a.into_iter());
|
||||
for elt in b {
|
||||
it.put_back(elt)
|
||||
}
|
||||
correct_size_hint(it)
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn size_tee(a: Vec<u8>) -> 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<u8>) -> 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<u8>, stop: u8) -> bool {
|
||||
correct_size_hint(a.iter().take_while_ref(|x| **x != stop))
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn equal_partition(a: Vec<i32>) -> 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(it: Iter<i16>) -> bool {
|
||||
correct_size_hint(it.tuple_combinations::<(_, _)>())
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn equal_combinations(it: Iter<i16>) -> 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() == None
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn size_pad_tail(it: Iter<i8>, 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<i8>, pad: u8) -> bool {
|
||||
exact_size(it.pad_using(pad as usize, |_| 0))
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn size_unique(it: Iter<i8>) -> bool {
|
||||
correct_size_hint(it.unique())
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn fuzz_group_by_lazy_1(it: Iter<u8>) -> bool {
|
||||
let jt = it.clone();
|
||||
let groups = it.group_by(|k| *k);
|
||||
let res = itertools::equal(jt, groups.into_iter().flat_map(|(_, x)| x));
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn fuzz_group_by_lazy_2(data: Vec<u8>) -> bool {
|
||||
let groups = data.iter().group_by(|k| *k / 10);
|
||||
let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn fuzz_group_by_lazy_3(data: Vec<u8>) -> bool {
|
||||
let grouper = data.iter().group_by(|k| *k / 10);
|
||||
let groups = grouper.into_iter().collect_vec();
|
||||
let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn fuzz_group_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
|
||||
let grouper = data.iter().group_by(|k| *k / 3);
|
||||
let mut groups1 = grouper.into_iter();
|
||||
let mut groups2 = grouper.into_iter();
|
||||
let mut elts = Vec::<&u8>::new();
|
||||
let mut old_groups = Vec::new();
|
||||
|
||||
let tup1 = |(_, b)| b;
|
||||
for &(ord, consume_now) in &order {
|
||||
let iter = &mut [&mut groups1, &mut groups2][ord as usize];
|
||||
match iter.next() {
|
||||
Some((_, gr)) => if consume_now {
|
||||
for og in old_groups.drain(..) {
|
||||
elts.extend(og);
|
||||
}
|
||||
elts.extend(gr);
|
||||
} else {
|
||||
old_groups.push(gr);
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
for og in old_groups.drain(..) {
|
||||
elts.extend(og);
|
||||
}
|
||||
for gr in groups1.map(&tup1) { elts.extend(gr); }
|
||||
for gr in groups2.map(&tup1) { elts.extend(gr); }
|
||||
itertools::assert_equal(&data, elts);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn equal_chunks_lazy(a: Vec<u8>, 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
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn equal_tuple_windows_1(a: Vec<u8>) -> 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<u8>) -> 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<u8>) -> 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<u8>) -> 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 equal_tuples_1(a: Vec<u8>) -> 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<u8>) -> 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<u8>) -> 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<u8>) -> 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<u8>) -> 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)
|
||||
}
|
||||
}
|
||||
|
||||
// with_position
|
||||
quickcheck! {
|
||||
fn with_position_exact_size_1(a: Vec<u8>) -> bool {
|
||||
exact_size_for_this(a.iter().with_position())
|
||||
}
|
||||
fn with_position_exact_size_2(a: Iter<u8>) -> bool {
|
||||
exact_size_for_this(a.with_position())
|
||||
}
|
||||
}
|
||||
|
||||
/// A peculiar type: Equality compares both tuple items, but ordering only the
|
||||
/// first item. This is so we can check the stability property easily.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct Val(u32, u32);
|
||||
|
||||
impl PartialOrd<Val> for Val {
|
||||
fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Val {
|
||||
fn cmp(&self, other: &Val) -> Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl qc::Arbitrary for Val {
|
||||
fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
|
||||
let (x, y) = <(u32, u32)>::arbitrary(g);
|
||||
Val(x, y)
|
||||
}
|
||||
fn shrink(&self) -> Box<Iterator<Item = Self>> {
|
||||
Box::new((self.0, self.1).shrink().map(|(x, y)| Val(x, y)))
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn minmax(a: Vec<Val>) -> 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<f64>) -> 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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,891 @@
|
|||
//! Licensed under the Apache License, Version 2.0
|
||||
//! http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
|
||||
//! http://opensource.org/licenses/MIT, at your
|
||||
//! option. This file may not be copied, modified, or distributed
|
||||
//! except according to those terms.
|
||||
|
||||
#[macro_use] extern crate itertools as it;
|
||||
extern crate permutohedron;
|
||||
|
||||
use it::Itertools;
|
||||
use it::interleave;
|
||||
use it::multizip;
|
||||
use it::multipeek;
|
||||
use it::FoldWhile;
|
||||
use it::free::rciter;
|
||||
use it::free::put_back;
|
||||
use it::free::put_back_n;
|
||||
use it::cloned;
|
||||
|
||||
#[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() == None);
|
||||
}
|
||||
|
||||
#[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 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!(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 izip3() {
|
||||
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 write_to() {
|
||||
let xs = [7, 9, 8];
|
||||
let mut ys = [0; 5];
|
||||
let cnt = ys.iter_mut().set_from(xs.iter().map(|x| *x));
|
||||
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 interleave_shortest() {
|
||||
let v0: Vec<i32> = vec![0, 2, 4];
|
||||
let v1: Vec<i32> = vec![1, 3, 5, 7];
|
||||
let it = v0.into_iter().interleave_shortest(v1.into_iter());
|
||||
assert_eq!(it.size_hint(), (6, Some(6)));
|
||||
assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5]);
|
||||
|
||||
let v0: Vec<i32> = vec![0, 2, 4, 6, 8];
|
||||
let v1: Vec<i32> = vec![1, 3, 5];
|
||||
let it = v0.into_iter().interleave_shortest(v1.into_iter());
|
||||
assert_eq!(it.size_hint(), (7, Some(7)));
|
||||
assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5, 6]);
|
||||
|
||||
let i0 = ::std::iter::repeat(0);
|
||||
let v1: Vec<_> = vec![1, 3, 5];
|
||||
let it = i0.interleave_shortest(v1.into_iter());
|
||||
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 foreach() {
|
||||
let xs = [1i32, 2, 3];
|
||||
let mut sum = 0;
|
||||
xs.iter().foreach(|elt| sum += *elt);
|
||||
assert!(sum == 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dropping() {
|
||||
let xs = [1, 2, 3];
|
||||
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 intersperse() {
|
||||
let xs = ["a", "", "b", "c"];
|
||||
let v: Vec<&str> = xs.iter().map(|x| x.clone()).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().map(|x| *x).intersperse(1);
|
||||
assert!(it.next() == 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 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()));
|
||||
}
|
||||
|
||||
#[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());
|
||||
let xs = [0, 1];
|
||||
let ys = [0, 1];
|
||||
it::assert_equal(ys.iter(), xs.iter().unique());
|
||||
}
|
||||
|
||||
#[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(|mut it| {
|
||||
match it.next() {
|
||||
None => None,
|
||||
Some(x) => match it.next() {
|
||||
None => None,
|
||||
Some(y) => Some((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 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 step() {
|
||||
it::assert_equal((0..10).step(1), (0..10));
|
||||
it::assert_equal((0..10).step(2), (0..10).filter(|x: &i32| *x % 2 == 0));
|
||||
it::assert_equal((0..10).step(10), 0..1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_pointers() {
|
||||
struct ByRef<'r, I: ?Sized>(&'r mut I) where I: 'r;
|
||||
|
||||
impl<'r, X, I: ?Sized> Iterator for ByRef<'r, I> where
|
||||
I: 'r + Iterator<Item=X>
|
||||
{
|
||||
type Item = X;
|
||||
fn next(&mut self) -> Option<X>
|
||||
{
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
let mut it = Box::new(0..10) as Box<Iterator<Item=i32>>;
|
||||
assert_eq!(it.next(), Some(0));
|
||||
|
||||
{
|
||||
/* make sure foreach works on non-Sized */
|
||||
let mut jt: &mut Iterator<Item=i32> = &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.foreach(|_| ());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge() {
|
||||
it::assert_equal((0..10).step(2).merge((1..10).step(2)), (0..10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_by() {
|
||||
let odd : Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")];
|
||||
let even = vec![(2, "foo"), (4, "bar"), (6, "baz")];
|
||||
let expected = vec![(1, "hello"), (2, "foo"), (3, "world"), (4, "bar"), (5, "!"), (6, "baz")];
|
||||
let 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.into_iter(), |a, b| a.0 <= b.0 );
|
||||
let expected = vec![("bar", 4), ("foo", 2), ("hello", 1), ("world", 3)];
|
||||
it::assert_equal(results, expected.into_iter());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kmerge() {
|
||||
let its = (0..4).map(|s| (s..10).step(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(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<i32> = vec![];
|
||||
|
||||
assert_eq!(many.iter().join(", "), "1, 2, 3");
|
||||
assert_eq!( one.iter().join(", "), "1");
|
||||
assert_eq!(none.iter().join(", "), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sorted_by() {
|
||||
let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| {
|
||||
a.cmp(&b)
|
||||
});
|
||||
assert_eq!(sc, vec![1, 2, 3, 4]);
|
||||
|
||||
let v = (0..5).sorted_by(|&a, &b| a.cmp(&b).reverse());
|
||||
assert_eq!(v, vec![4, 3, 2, 1, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multipeek() {
|
||||
let nums = vec![1u8,2,3,4,5];
|
||||
|
||||
let mp = multipeek(nums.iter().map(|&x| x));
|
||||
assert_eq!(nums, mp.collect::<Vec<_>>());
|
||||
|
||||
let mut mp = multipeek(nums.iter().map(|&x| x));
|
||||
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.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 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 std::cell::Cell;
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct Foo {
|
||||
n: Cell<usize>
|
||||
}
|
||||
|
||||
impl Clone for Foo
|
||||
{
|
||||
fn clone(&self) -> Self
|
||||
{
|
||||
let n = self.n.get();
|
||||
self.n.set(n + 1);
|
||||
Foo { 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 pad_using() {
|
||||
it::assert_equal((0..0).pad_using(1, |_| 1), (1..2));
|
||||
|
||||
let v: Vec<usize> = 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<usize> = vec![0, 1, 2];
|
||||
let r = v.into_iter().pad_using(1, |_| panic!());
|
||||
it::assert_equal(r, vec![0, 1, 2]);
|
||||
}
|
||||
|
||||
#[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 group_by() {
|
||||
for (ch1, sub) in &"AABBCCC".chars().group_by(|&x| x) {
|
||||
for ch2 in sub {
|
||||
assert_eq!(ch1, ch2);
|
||||
}
|
||||
}
|
||||
|
||||
for (ch1, sub) in &"AAABBBCCCCDDDD".chars().group_by(|&x| x) {
|
||||
for ch2 in sub {
|
||||
assert_eq!(ch1, ch2);
|
||||
if ch1 == 'C' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let toupper = |ch: &char| ch.to_uppercase().nth(0).unwrap();
|
||||
|
||||
// try all possible orderings
|
||||
for indices in permutohedron::Heap::new(&mut [0, 1, 2, 3]) {
|
||||
let groups = "AaaBbbccCcDDDD".chars().group_by(&toupper);
|
||||
let mut subs = groups.into_iter().collect_vec();
|
||||
|
||||
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 groups = "AAABBBCCCCDDDD".chars().group_by(|&x| x);
|
||||
let mut subs = groups.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().group_by(|&x| { ntimes += 1; x}) {
|
||||
for _ in sub {
|
||||
}
|
||||
}
|
||||
assert_eq!(ntimes, text.len());
|
||||
}
|
||||
|
||||
{
|
||||
let mut ntimes = 0;
|
||||
let text = "AABCCC";
|
||||
for _ in &text.chars().group_by(|&x| { ntimes += 1; x}) {
|
||||
}
|
||||
assert_eq!(ntimes, text.len());
|
||||
}
|
||||
|
||||
{
|
||||
let text = "ABCCCDEEFGHIJJKK";
|
||||
let gr = text.chars().group_by(|&x| x);
|
||||
it::assert_equal(gr.into_iter().flat_map(|(_, sub)| sub), text.chars());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_by_lazy_2() {
|
||||
let data = vec![0, 1];
|
||||
let groups = data.iter().group_by(|k| *k);
|
||||
let gs = groups.into_iter().collect_vec();
|
||||
it::assert_equal(data.iter(), gs.into_iter().flat_map(|(_k, g)| g));
|
||||
|
||||
let data = vec![0, 1, 1, 0, 0];
|
||||
let groups = data.iter().group_by(|k| *k);
|
||||
let mut gs = groups.into_iter().collect_vec();
|
||||
gs[1..].reverse();
|
||||
it::assert_equal(&[0, 0, 0, 1, 1], gs.into_iter().flat_map(|(_, g)| g));
|
||||
|
||||
let grouper = data.iter().group_by(|k| *k);
|
||||
let mut groups = Vec::new();
|
||||
for (k, group) in &grouper {
|
||||
if *k == 1 {
|
||||
groups.push(group);
|
||||
}
|
||||
}
|
||||
it::assert_equal(&mut groups[0], &[1, 1]);
|
||||
|
||||
let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
|
||||
let grouper = data.iter().group_by(|k| *k);
|
||||
let mut groups = Vec::new();
|
||||
for (i, (_, group)) in grouper.into_iter().enumerate() {
|
||||
if i < 2 {
|
||||
groups.push(group);
|
||||
} else if i < 4 {
|
||||
for _ in group {
|
||||
}
|
||||
} else {
|
||||
groups.push(group);
|
||||
}
|
||||
}
|
||||
it::assert_equal(&mut groups[0], &[0, 0, 0]);
|
||||
it::assert_equal(&mut groups[1], &[1, 1]);
|
||||
it::assert_equal(&mut groups[2], &[3, 3]);
|
||||
|
||||
// use groups as chunks
|
||||
let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
|
||||
let mut i = 0;
|
||||
let grouper = data.iter().group_by(move |_| { let k = i / 3; i += 1; k });
|
||||
for (i, group) in &grouper {
|
||||
match i {
|
||||
0 => it::assert_equal(group, &[0, 0, 0]),
|
||||
1 => it::assert_equal(group, &[1, 1, 0]),
|
||||
2 => it::assert_equal(group, &[0, 2, 2]),
|
||||
3 => it::assert_equal(group, &[3, 3]),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_by_lazy_3() {
|
||||
// test consuming each group on the lap after it was produced
|
||||
let data = vec![0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2];
|
||||
let grouper = data.iter().group_by(|elt| *elt);
|
||||
let mut last = None;
|
||||
for (key, group) in &grouper {
|
||||
if let Some(gr) = last.take() {
|
||||
for elt in gr {
|
||||
assert!(elt != key && i32::abs(elt - key) == 1);
|
||||
}
|
||||
}
|
||||
last = Some(group);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunks() {
|
||||
let data = vec![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 flatten_iter() {
|
||||
let data = vec![vec![1,2,3], vec![4,5,6]];
|
||||
let flattened = data.into_iter().flatten();
|
||||
|
||||
it::assert_equal(flattened, vec![1,2,3,4,5,6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_rev() {
|
||||
let data = vec![vec![1,2,3].into_iter(), vec![4,5,6].into_iter()];
|
||||
let flattened = data.into_iter().flatten().rev();
|
||||
it::assert_equal(flattened, vec![6,5,4,3,2,1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_clone() {
|
||||
let data = &[
|
||||
&[1,2,3],
|
||||
&[4,5,6]
|
||||
];
|
||||
let flattened1 = data.into_iter().cloned().flatten();
|
||||
let flattened2 = flattened1.clone();
|
||||
|
||||
it::assert_equal(flattened1, &[1,2,3,4,5,6]);
|
||||
it::assert_equal(flattened2, &[1,2,3,4,5,6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_fold() {
|
||||
let xs = [0, 1, 1, 1, 2, 1, 3, 3];
|
||||
let ch = xs.iter().chunks(3);
|
||||
let mut iter = ch.into_iter().flatten();
|
||||
iter.next();
|
||||
let mut xs_d = Vec::new();
|
||||
iter.fold((), |(), &elt| xs_d.push(elt));
|
||||
assert_eq!(&xs_d[..], &xs[1..]);
|
||||
}
|
||||
|
||||
#[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::<(_, _)>(), <Vec<_>>::new());
|
||||
it::assert_equal((0..1).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
|
||||
it::assert_equal((0..2).tuple_combinations::<(_, _)>(), vec![(0, 1)]);
|
||||
|
||||
it::assert_equal((0..0).combinations(2), <Vec<Vec<_>>>::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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn combinations_zero() {
|
||||
(1..3).combinations(0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_mismatch() {
|
||||
let a = vec![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<_>>() == vec![5, 3, 4],
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_longer() {
|
||||
let a = vec![1, 2, 3, 4];
|
||||
let b = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
|
||||
let b_map = b.into_iter().map(|f| f as i32);
|
||||
let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
|
||||
|
||||
assert!(match diff {
|
||||
Some(it::Diff::Longer(_, remaining)) =>
|
||||
remaining.collect::<Vec<_>>() == vec![5, 6],
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_shorter() {
|
||||
let a = vec![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 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.clone() + item;
|
||||
if new_sum <= 20 {
|
||||
FoldWhile::Continue(new_sum)
|
||||
} else {
|
||||
FoldWhile::Done(acc)
|
||||
}
|
||||
});
|
||||
assert_eq!(iterations, 6);
|
||||
assert_eq!(sum, 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minmax() {
|
||||
use std::cmp::Ordering;
|
||||
use it::MinMaxResult;
|
||||
|
||||
// A peculiar type: Equality compares both tuple items, but ordering only the
|
||||
// first item. This is so we can check the stability property easily.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct Val(u32, u32);
|
||||
|
||||
impl PartialOrd<Val> for Val {
|
||||
fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Val {
|
||||
fn cmp(&self, other: &Val) -> Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(None::<Option<u32>>.iter().minmax(), MinMaxResult::NoElements);
|
||||
|
||||
assert_eq!(Some(1u32).iter().minmax(), MinMaxResult::OneElement(&1));
|
||||
|
||||
let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
|
||||
|
||||
let 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, 2.71828, -22.];
|
||||
let t3 = format!("{:.2e}", dataf.iter().format(", "));
|
||||
assert_eq!(t3, "1.10e0, 2.72e0, -2.20e1");
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
extern crate itertools;
|
||||
|
||||
use 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);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
extern crate itertools;
|
||||
|
||||
use itertools::Itertools;
|
||||
use itertools::EitherOrBoth::{Both, Left, Right};
|
||||
use itertools::free::zip_eq;
|
||||
|
||||
#[test]
|
||||
fn zip_longest_fused()
|
||||
{
|
||||
let a = [Some(1), None, Some(3), Some(4)];
|
||||
let b = [1, 2, 3];
|
||||
|
||||
let unfused = a.iter().batching(|mut 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().map(|&x| x);
|
||||
let b = ys.iter().map(|&x| x);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn zip_eq_panic1()
|
||||
{
|
||||
let a = [1, 2];
|
||||
let b = [1, 2, 3];
|
||||
|
||||
zip_eq(&a, &b).count();
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn zip_eq_panic2()
|
||||
{
|
||||
let a: [i32; 0] = [];
|
||||
let b = [1, 2, 3];
|
||||
|
||||
zip_eq(&a, &b).count();
|
||||
}
|
||||
|
|
@ -453,6 +453,14 @@ dependencies = [
|
|||
"unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.3.1"
|
||||
|
@ -935,6 +943,7 @@ dependencies = [
|
|||
"cssparser 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1302,6 +1311,7 @@ dependencies = [
|
|||
"checksum heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5a376f7402b85be6e0ba504243ecbc0709c48019ecc6286d0540c2e359050c88"
|
||||
"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
|
||||
"checksum idna 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2233d4940b1f19f0418c158509cd7396b8d70a5db5705ce410914dc8fa603b37"
|
||||
"checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"
|
||||
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum khronos_api 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d5a08e2a31d665af8f1ca437eab6d00a93c9d62a549f73f9ed8fc2e55b5a91a7"
|
||||
|
|
|
@ -451,6 +451,14 @@ dependencies = [
|
|||
"unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.3.1"
|
||||
|
@ -922,6 +930,7 @@ dependencies = [
|
|||
"cssparser 0.18.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1289,6 +1298,7 @@ dependencies = [
|
|||
"checksum heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5a376f7402b85be6e0ba504243ecbc0709c48019ecc6286d0540c2e359050c88"
|
||||
"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
|
||||
"checksum idna 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2233d4940b1f19f0418c158509cd7396b8d70a5db5705ce410914dc8fa603b37"
|
||||
"checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"
|
||||
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum khronos_api 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d5a08e2a31d665af8f1ca437eab6d00a93c9d62a549f73f9ed8fc2e55b5a91a7"
|
||||
|
|
Загрузка…
Ссылка в новой задаче