sorted-iter-0.1.11/.cargo_vcs_info.json0000644000000001360000000000100133550ustar { "git": { "sha1": "e905d82cf6f057b9b665a669720ab48d805a9680" }, "path_in_vcs": "" }sorted-iter-0.1.11/.gitignore000064400000000000000000000001651046102023000141370ustar 00000000000000/target **/*.rs.bk #Added by cargo # #already existing elements are commented out #/target #**/*.rs.bk Cargo.lock sorted-iter-0.1.11/.travis.yml000064400000000000000000000001631046102023000142560ustar 00000000000000language: rust rust: - stable - beta - nightly matrix: allow_failures: - rust: nightly cache: cargo sorted-iter-0.1.11/Cargo.lock0000644000000120200000000000100113230ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "env_logger" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ "log", "regex", ] [[package]] name = "getrandom" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "maplit" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ "unicode-xid", ] [[package]] name = "quickcheck" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" dependencies = [ "env_logger", "log", "rand", "rand_core", ] [[package]] name = "quickcheck_macros" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7dfc1c4a1e048f5cc7d36a4c4118dfcf31d217c79f4b9a61bad65d68185752c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "quote" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom", "libc", "rand_chacha", "rand_core", "rand_hc", ] [[package]] name = "rand_chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ "rand_core", ] [[package]] name = "regex" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "sorted-iter" version = "0.1.11" dependencies = [ "maplit", "quickcheck", "quickcheck_macros", ] [[package]] name = "syn" version = "0.15.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" sorted-iter-0.1.11/Cargo.toml0000644000000020700000000000100113520ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "sorted-iter" version = "0.1.11" authors = ["Rüdiger Klaehn "] description = "Typesafe extensions for sorted iterators, including set and relational operations" homepage = "https://github.com/rklaehn" readme = "README.md" keywords = [ "hash", "digest", "streaming", ] categories = ["cryptography"] license = "MIT OR Apache-2.0" repository = "https://github.com/rklaehn/sorted-iter" [dependencies] [dev-dependencies.maplit] version = "1.0" [dev-dependencies.quickcheck] version = "0.9" [dev-dependencies.quickcheck_macros] version = "0.8" sorted-iter-0.1.11/Cargo.toml.orig000064400000000000000000000010251046102023000150320ustar 00000000000000[package] name = "sorted-iter" version = "0.1.11" authors = ["Rüdiger Klaehn "] description = "Typesafe extensions for sorted iterators, including set and relational operations" repository = "https://github.com/rklaehn/sorted-iter" license = "MIT OR Apache-2.0" keywords = ["hash", "digest", "streaming"] categories = ["cryptography"] edition = "2018" readme = "README.md" homepage = "https://github.com/rklaehn" [dependencies] [dev-dependencies] quickcheck = "0.9" quickcheck_macros = "0.8" maplit = "1.0" sorted-iter-0.1.11/LICENSE-APACHE2.md000064400000000000000000000261351046102023000145610ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. sorted-iter-0.1.11/LICENSE-MIT.md000064400000000000000000000020601046102023000141760ustar 00000000000000MIT License Copyright (c) 2019 Rüdiger Klaehn 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. sorted-iter-0.1.11/README.md000064400000000000000000000126611046102023000134320ustar 00000000000000 [![Build Status]][travis] [![Latest Version]][crates.io] [![Docs Badge]][docs.rs] # Sorted Iter [Build Status]: https://api.travis-ci.org/rklaehn/sorted-iter.svg?branch=master [travis]: https://travis-ci.org/rklaehn/sorted-iter [Latest Version]: https://img.shields.io/crates/v/sorted-iter.svg [crates.io]: https://crates.io/crates/sorted-iter [Docs Badge]: https://img.shields.io/badge/docs-docs.rs-green [docs.rs]: https://docs.rs/sorted-iter # About This crate provides set and relational operations for all iterators in the standard library that are known at compile time to be sorted. # Set operations ```rust use sorted_iter::SortedIterator; let primes = btreeset! { 2, 3, 5, 7, 11, 13u64 }.into_iter(); let fibs = btreeset! { 1, 2, 3, 5, 8, 13u64 }.into_iter(); let fib_primes = primes.intersection(fibs); ``` It is possible to efficiently define set operations on sorted iterators. Sorted iterators are very common in the standard library. E.g. the elements of a [BTreeSet] or the keys of a [BTreeMap] are guaranteed to be sorted according to the element order, as are iterable ranges like `0..100`. There are also a number of operations on iterators that preserve the sort order. E.g. if an iterator is sorted, [take], [take_while] etc. are going to result in a sorted iterator as well. Since the complete types of iterators are typically visible in rust, it is possible to encode these rules at type level. This is what this crate does. For available set operations, see [SortedIterator]. For sorted iterators in the std lib, see instances the for [SortedByItem] marker trait. # Relational operations ```rust use sorted_iter::SortedPairIterator; let cities = btreemap! { 1 => "New York", 2 => "Tokyo", 3u8 => "Berlin" }.into_iter(); let countries = btreemap! { 1 => "USA", 2 => "Japan", 3u8 => "Germany" }.into_iter(); let cities_and_countries = cities.join(countries); ``` Iterators of pairs that are sorted according to the first element / key are also very common in the standard library and elsewhere. E.g. the elements of a [BTreeMap] are guaranteed to be sorted according to the key order. The same rules as for sorted iterators apply for preservation of the sort order, except that there are some additional operations that preserve sort order. Anything that only operates on the value, like e.g. map or filter_map on the value, is guaranteed to preserve the sort order. The operations that can be defined on sorted pair operations are the relational operations known from relational algebra / SQL, namely join, left_join, right_join and outer_join. For available relational operations, see [SortedPairIterator]. For sorted iterators in the std lib, see instances the for [SortedByKey] marker trait. # Transformations that retain order are allowed ```rust use sorted_iter::*; let odd = (1..31).step_by(2); let multiples_of_3 = (3..30).step_by(3); let either = odd.union(multiples_of_3); ``` # Transformations that can change the order lose the sorted property ```compile_fail use sorted_iter::*; // we have no idea what map does to the order. could be anything! let a = (1..31).map(|x| -x); let b = (3..30).step_by(3); let either = a.union(b); // does not compile! ``` # Assuming sort ordering For most std lib iterators, this library already provides instances. But there will occasionally be an iterator from a third party library where you *know* that it is properly sorted. For this case, there is an escape hatch: ```rust // the assume_ extensions have to be implicitly imported use sorted_iter::*; use sorted_iter::assume::*; let odd = vec![1,3,5,7u8].into_iter().assume_sorted_by_item(); let even = vec![2,4,6,8u8].into_iter().assume_sorted_by_item(); let all = odd.union(even); let cities = vec![(1u8, "New York")].into_iter().assume_sorted_by_key(); let countries = vec![(1u8, "USA")].into_iter().assume_sorted_by_key(); let cities_and_countries = cities.join(countries); ``` # Marking your own iterators If you have a library and want to mark some iterators as sorted, this is possible by implementing the appropriate marker trait, [SortedByItem] or [SortedByKey]. ```rust // marker traits are not at top level, since usually you don't need them use sorted_iter::sorted_iterator::SortedByItem; use sorted_iter::sorted_pair_iterator::SortedByKey; pub struct MySortedIter { whatever: T } pub struct MySortedPairIter { whatever: (K, V) } impl SortedByItem for MySortedIter {} impl SortedByKey for MySortedPairIter {} ``` By reexporting the extension traits, you get a seamless experience for people using your library. ```rust extern crate sorted_iter; pub use sorted_iter::{SortedIterator, SortedPairIterator}; ``` ## Tests Tests are done using the fantastic [quickcheck] crate, by comparing against the operations defined on [BTreeSet] and [BTreeMap]. [SortedIterator]: trait.SortedIterator.html [SortedPairIterator]: trait.SortedPairIterator.html [SortedByItem]: sorted_iterator/trait.SortedByItem.html [SortedByKey]: sorted_pair_iterator/trait.SortedByKey.html [quickcheck]: https://github.com/BurntSushi/quickcheck [BTreeSet]: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html [BTreeMap]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html [take]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take [take_while]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take_while [Ord]: https://doc.rust-lang.org/std/cmp/trait.Ord.html sorted-iter-0.1.11/examples/relational.rs000064400000000000000000000005601046102023000164640ustar 00000000000000extern crate maplit; extern crate sorted_iter; use maplit::*; use sorted_iter::SortedPairIterator; fn main() { let city = btreemap! { 1 => "New York", 2 => "Tokyo", }; let country = btreemap! { 1 => "USA", 2 => "Japan", }; let res: Vec<_> = city.iter().join(country.iter()).collect(); println!("{:?}", res); } sorted-iter-0.1.11/examples/set.rs000064400000000000000000000013101046102023000151170ustar 00000000000000extern crate maplit; extern crate sorted_iter; use maplit::*; use sorted_iter::SortedIterator; fn v(x: impl Iterator) -> Vec { x.take(10).collect() } fn main() { let primes = btreeset! { 2u64, 3, 5, 7, 11, 13 }; let fibs = btreeset! { 1u64, 2, 3, 5, 8, 13 }; let primes = primes.iter(); let fibs = fibs.iter(); let nats = 1u64..; // both primes and fibs let both = primes.clone().intersection(fibs.clone()); // either primes or fibs let either = primes.union(fibs).cloned(); // natural numbers that are neither let neither = nats.difference(either); println!("Fibonacci primes: {:?}", v(both)); println!("Neither: {:?}", v(neither)); } sorted-iter-0.1.11/src/lib.rs000064400000000000000000000323111046102023000140500ustar 00000000000000//! This crate provides set and relational operations for all iterators in the standard library that are known //! at compile time to be sorted. //! //! # Set operations //! ``` //! # extern crate maplit; //! # use maplit::*; //! # extern crate sorted_iter; //! use sorted_iter::SortedIterator; //! //! let primes = btreeset! { 2, 3, 5, 7, 11, 13u64 }.into_iter(); //! let fibs = btreeset! { 1, 2, 3, 5, 8, 13u64 }.into_iter(); //! let fib_primes = primes.intersection(fibs); //! ``` //! //! It is possible to efficiently define set operations on sorted iterators. Sorted iterators are //! very common in the standard library. E.g. the elements of a [BTreeSet] or the keys of a [BTreeMap] //! are guaranteed to be sorted according to the element order, as are iterable ranges like `0..100`. //! //! There are also a number of operations on iterators that preserve the sort order. E.g. if an //! iterator is sorted, [take], [take_while] etc. are going to result in a sorted iterator as well. //! //! Since the complete types of iterators are typically visible in rust, it is possible to encode these //! rules at type level. This is what this crate does. //! //! For available set operations, see [SortedIterator]. //! For sorted iterators in the std lib, see instances for the [SortedByItem] marker trait. //! //! # Relational operations //! ``` //! # extern crate maplit; //! # use maplit::*; //! # extern crate sorted_iter; //! use sorted_iter::SortedPairIterator; //! //! let cities = btreemap! { //! 1 => "New York", //! 2 => "Tokyo", //! 3u8 => "Berlin" //! }.into_iter(); //! let countries = btreemap! { //! 1 => "USA", //! 2 => "Japan", //! 3u8 => "Germany" //! }.into_iter(); //! let cities_and_countries = cities.join(countries); //! ``` //! //! Iterators of pairs that are sorted according to the first element / key are also very common in //! the standard library and elsewhere. E.g. the elements of a [BTreeMap] are guaranteed to be sorted //! according to the key order. //! //! The same rules as for sorted iterators apply for preservation of the sort order, except that there //! are some additional operations that preserve sort order. Anything that only operates on the value, //! like e.g. map or filter_map on the value, is guaranteed to preserve the sort order. //! //! The operations that can be defined on sorted pair operations are the relational operations known //! from relational algebra / SQL, namely join, left_join, right_join and outer_join. //! //! For available relational operations, see [SortedPairIterator]. //! For sorted iterators in the std lib, see instances the for [SortedByKey] marker trait. //! //! # Transformations that retain order are allowed //! ``` //! # extern crate sorted_iter; //! use sorted_iter::*; //! //! let odd = (1..31).step_by(2); //! let multiples_of_3 = (3..30).step_by(3); //! let either = odd.union(multiples_of_3); //! ``` //! //! # Transformations that can change the order lose the sorted property //! ```compile_fail //! # extern crate sorted_iter; //! use sorted_iter::*; //! //! // we have no idea what map does to the order. could be anything! //! let a = (1..31).map(|x| -x); //! let b = (3..30).step_by(3); //! let either = a.union(b); // does not compile! //! ``` //! //! # Assuming sort ordering //! //! For most std lib iterators, this library already provides instances. But there will occasionally be an iterator //! from a third party library where you *know* that it is properly sorted. //! //! For this case, there is an escape hatch: //! //! ``` //! // the assume_ extensions have to be implicitly imported //! use sorted_iter::*; //! use sorted_iter::assume::*; //! let odd = vec![1,3,5,7u8].into_iter().assume_sorted_by_item(); //! let even = vec![2,4,6,8u8].into_iter().assume_sorted_by_item(); //! let all = odd.union(even); //! //! let cities = vec![(1u8, "New York")].into_iter().assume_sorted_by_key(); //! let countries = vec![(1u8, "USA")].into_iter().assume_sorted_by_key(); //! let cities_and_countries = cities.join(countries); //! ``` //! //! # Marking your own iterators //! //! If you have a library and want to mark some iterators as sorted, this is possible by implementing the //! appropriate marker trait, [SortedByItem] or [SortedByKey]. //! //! ``` //! # extern crate sorted_iter; //! // marker traits are not at top level, since usually you don't need them //! use sorted_iter::sorted_iterator::SortedByItem; //! use sorted_iter::sorted_pair_iterator::SortedByKey; //! //! pub struct MySortedIter { whatever: T } //! pub struct MySortedPairIter { whatever: (K, V) } //! //! impl SortedByItem for MySortedIter {} //! impl SortedByKey for MySortedPairIter {} //! ``` //! //! By reexporting the extension traits, you get a seamless experience for people using your library. //! //! ``` //! extern crate sorted_iter; //! pub use sorted_iter::{SortedIterator, SortedPairIterator}; //! ``` //! //! ## Tests //! //! Tests are done using the fantastic [quickcheck] crate, by comparing against the operations defined on //! [BTreeSet] and [BTreeMap]. //! //! [SortedIterator]: trait.SortedIterator.html //! [SortedPairIterator]: trait.SortedPairIterator.html //! [SortedByItem]: sorted_iterator/trait.SortedByItem.html //! [SortedByKey]: sorted_pair_iterator/trait.SortedByKey.html //! [quickcheck]: https://github.com/BurntSushi/quickcheck //! [BTreeSet]: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html //! [BTreeMap]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html //! [take]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take //! [take_while]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take_while //! [Ord]: https://doc.rust-lang.org/std/cmp/trait.Ord.html #[cfg(test)] extern crate quickcheck; #[cfg(test)] #[macro_use(quickcheck)] extern crate quickcheck_macros; use core::cmp::Ordering; pub mod one_or_less_iterator; pub mod sorted_iterator; pub mod sorted_pair_iterator; pub use one_or_less_iterator::OneOrLess; use crate::sorted_iterator::*; use crate::sorted_pair_iterator::*; #[deny(missing_docs)] /// set operations for iterators where the items are sorted according to the natural order pub trait SortedIterator: Iterator + Sized { /// Visits the values representing the union, i.e., all the values in `self` or `other`, /// without duplicates. fn union(self, other: J) -> Union where J: SortedIterator, { Union { a: self.peekable(), b: other.peekable(), } } /// Visits the values representing the intersection, i.e., the values that are both in `self` /// and `other`. fn intersection(self, other: J) -> Intersection where J: SortedIterator, { Intersection { a: self, b: other.peekable(), } } /// Visits the values representing the difference, i.e., the values that are in `self` but not /// in `other`. fn difference(self, other: J) -> Difference where J: SortedIterator, { Difference { a: self, b: other.peekable(), } } /// Visits the values representing the symmetric difference, i.e., the values that are in /// `self` or in `other` but not in both. fn symmetric_difference(self, other: J) -> SymmetricDifference where J: SortedIterator, { SymmetricDifference { a: self.peekable(), b: other.peekable(), } } /// Creates an iterator that pairs each element of `self` with `()`. This transforms a /// `SortedIterator` into a [`SortedPairIterator`]. fn pairs(self) -> Pairs { Pairs { i: self } } /// Returns `true` if `self` has no elements in common with `other`. This is equivalent to /// checking for an empty intersection. fn is_disjoint(self, mut other: J) -> bool where J: SortedIterator, Self::Item: Ord, { let mut next_b = other.next(); 'next_a: for a in self { while let Some(b) = &next_b { match a.cmp(b) { Ordering::Less => continue 'next_a, Ordering::Equal => return false, Ordering::Greater => next_b = other.next(), } } break; } true } /// Returns `true` if this sorted iterator is a subset of another, i.e., `other` contains at /// least all the values in `self`. fn is_subset(self, mut other: J) -> bool where J: SortedIterator, Self::Item: Ord, { 'next_a: for a in self { while let Some(b) = other.next() { match a.cmp(&b) { Ordering::Less => break, Ordering::Equal => continue 'next_a, Ordering::Greater => continue, } } return false; } true } /// Returns `true` if this sorted iterator is a superset of another, i.e., `self` contains at /// least all the values in `other`. fn is_superset(self, other: J) -> bool where J: SortedIterator, Self::Item: Ord, { other.is_subset(self) } } impl SortedIterator for I where I: Iterator + SortedByItem {} /// Union of multiple sorted iterators. /// /// An advantage of this function over multiple calls to `SortedIterator::union` /// is that the number of merged sequences does not need to be known at the /// compile time. The drawback lies in the fact that all iterators have to be /// of the same type. /// /// The algorithmic complexity of fully consuming the resulting iterator is /// *O(N log(K))* where *N* is the total number of items that the input iterators /// yield and *K* is the number of input iterators. /// /// # Examples /// /// ``` /// # extern crate maplit; /// # use maplit::*; /// # extern crate sorted_iter; /// # use std::collections::BTreeSet; /// use sorted_iter::multiway_union; /// /// let sequences = vec![ /// btreeset! { 0, 5, 10, 15, 20, 25 }.into_iter(), /// btreeset! { 0, 1, 4, 9, 16, 25, 36 }.into_iter(), /// btreeset! { 4, 7, 11, 15, 18 }.into_iter(), /// ]; /// /// assert_eq!( /// multiway_union(sequences).collect::>(), /// btreeset! { 0, 1, 4, 5, 7, 9, 10, 11, 15, 16, 18, 20, 25, 36 } /// ); /// ``` pub fn multiway_union(iters: T) -> MultiwayUnion where I: SortedIterator, T: IntoIterator, I::Item: Ord, { MultiwayUnion::from_iter(iters) } /// relational operations for iterators of pairs where the items are sorted according to the key pub trait SortedPairIterator: Iterator + Sized { fn join>(self, that: J) -> Join { Join { a: self.peekable(), b: that.peekable(), } } fn left_join>(self, that: J) -> LeftJoin { LeftJoin { a: self.peekable(), b: that.peekable(), } } fn right_join>(self, that: J) -> RightJoin { RightJoin { a: self.peekable(), b: that.peekable(), } } fn outer_join>(self, that: J) -> OuterJoin { OuterJoin { a: self.peekable(), b: that.peekable(), } } fn map_values W)>(self, f: F) -> MapValues { MapValues { i: self, f } } fn filter_map_values W)>(self, f: F) -> FilterMapValues { FilterMapValues { i: self, f } } fn keys(self) -> Keys { Keys { i: self } } } impl SortedPairIterator for I where I: Iterator + SortedByKey {} pub mod assume { //! extension traits for unchecked conversions from iterators to sorted iterators use super::*; /// extension trait for any iterator to add a assume_sorted_by_item method pub trait AssumeSortedByItemExt: Iterator + Sized { /// assume that the iterator is sorted by its item order fn assume_sorted_by_item(self) -> AssumeSortedByItem { AssumeSortedByItem { i: self } } } impl AssumeSortedByItemExt for I {} /// extension trait for any iterator of pairs to add a assume_sorted_by_key method pub trait AssumeSortedByKeyExt: Iterator + Sized { fn assume_sorted_by_key(self) -> AssumeSortedByKey { AssumeSortedByKey { i: self } } } impl + Sized> AssumeSortedByKeyExt for I {} } #[cfg(test)] mod tests { use super::*; type Element = i64; type Reference = std::collections::BTreeSet; #[quickcheck] fn disjoint(a: Reference, b: Reference) -> bool { a.is_disjoint(&b) == a.iter().is_disjoint(b.iter()) } #[quickcheck] fn subset(a: Reference, b: Reference) -> bool { a.is_subset(&b) == a.iter().is_subset(b.iter()) } #[quickcheck] fn superset(a: Reference, b: Reference) -> bool { a.is_superset(&b) == a.iter().is_superset(b.iter()) } } sorted-iter-0.1.11/src/one_or_less_iterator.rs000064400000000000000000000033071046102023000175250ustar 00000000000000//! Implementation of [`OneOrLess`] iterators. use core::{iter, option, result}; /// Iterators that return either 1 or 0 item. /// /// An iterator that returns nothing or one thing is always sorted. /// /// More interestingly, `one_or_less_iterator.flatten()` is sorted if the inner /// iterator is itself sorted. This isn't true of non-one_or_less iterators. pub trait OneOrLess {} impl OneOrLess for iter::Empty {} impl OneOrLess for iter::Once {} impl<'a, T> OneOrLess for option::Iter<'a, T> {} impl<'a, T> OneOrLess for result::Iter<'a, T> {} impl OneOrLess for option::IntoIter {} impl OneOrLess for result::IntoIter {} impl OneOrLess for Box {} impl OneOrLess for iter::Take {} impl OneOrLess for iter::Skip {} impl OneOrLess for iter::StepBy {} impl OneOrLess for iter::Cloned {} impl OneOrLess for iter::Copied {} impl OneOrLess for iter::Fuse {} impl OneOrLess for iter::Inspect {} impl OneOrLess for iter::TakeWhile {} impl OneOrLess for iter::SkipWhile {} impl OneOrLess for iter::Filter {} impl OneOrLess for iter::FilterMap {} impl OneOrLess for iter::Map {} impl OneOrLess for iter::Peekable {} impl OneOrLess for iter::FlatMap where Iin: OneOrLess, J: IntoIterator, Iout: OneOrLess, { } impl OneOrLess for iter::Flatten where Iin: OneOrLess + Iterator, J: IntoIterator, Iout: OneOrLess, { } sorted-iter-0.1.11/src/sorted_iterator.rs000064400000000000000000000427301046102023000165210ustar 00000000000000//! implementation of the sorted_iterator set operations use super::*; use core::cmp::Ordering::*; use core::cmp::{max, min, Ordering, Reverse}; use core::iter::Peekable; use core::{iter, ops, option, result}; use std::collections; use std::collections::BinaryHeap; use std::iter::FusedIterator; /// marker trait for iterators that are sorted by their Item pub trait SortedByItem {} pub struct Union { pub(crate) a: Peekable, pub(crate) b: Peekable, } impl Clone for Union where I::Item: Clone, J::Item: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), } } } impl, J: Iterator> Iterator for Union { type Item = K; fn next(&mut self) -> Option { if let (Some(ak), Some(bk)) = (self.a.peek(), self.b.peek()) { match ak.cmp(&bk) { Less => self.a.next(), Greater => self.b.next(), Equal => { self.b.next(); self.a.next() } } } else { self.a.next().or_else(|| self.b.next()) } } fn size_hint(&self) -> (usize, Option) { let (amin, amax) = self.a.size_hint(); let (bmin, bmax) = self.b.size_hint(); // full overlap let rmin = max(amin, bmin); // no overlap let rmax = amax.and_then(|amax| bmax.and_then(|bmax| amax.checked_add(bmax))); (rmin, rmax) } } // An iterator with the first item pulled out. pub(crate) struct Peeked { h: Reverse, t: I, } impl Peeked { fn new(mut i: I) -> Option> { i.next().map(|x| Peeked { h: Reverse(x), t: i, }) } fn size_hint(&self) -> (usize, Option) { let (lo, hi) = self.t.size_hint(); (lo + 1, hi.map(|hi| hi + 1)) } } // Delegate comparisons to the head element. impl PartialEq for Peeked where I::Item: PartialEq, { fn eq(&self, that: &Self) -> bool { self.h.eq(&that.h) } } impl Eq for Peeked where I::Item: Eq {} impl PartialOrd for Peeked where I::Item: PartialOrd, { fn partial_cmp(&self, that: &Self) -> Option { self.h.partial_cmp(&that.h) } } impl Ord for Peeked where I::Item: Ord, { fn cmp(&self, that: &Self) -> core::cmp::Ordering { self.h.cmp(&that.h) } } impl Clone for Peeked where I::Item: Clone, { fn clone(&self) -> Self { Self { h: self.h.clone(), t: self.t.clone(), } } } pub struct MultiwayUnion { pub(crate) bh: BinaryHeap>, } impl MultiwayUnion where I::Item: Ord, { pub(crate) fn from_iter>(x: T) -> MultiwayUnion { MultiwayUnion { bh: x.into_iter().filter_map(Peeked::new).collect(), } } } impl Clone for MultiwayUnion where I::Item: Clone, { fn clone(&self) -> Self { Self { bh: self.bh.clone(), } } } impl Iterator for MultiwayUnion where I::Item: Ord, { type Item = I::Item; fn next(&mut self) -> Option { // Extract the current minimum element. self.bh.pop().map( |Peeked { h: Reverse(item), t: top_tail, }| { // Advance the iterator and re-insert it into the heap. Peeked::new(top_tail).map(|i| self.bh.push(i)); // Remove equivalent elements and advance corresponding iterators. while self.bh.peek().filter(|x| x.h.0 == item).is_some() { let tail = self.bh.pop().unwrap().t; Peeked::new(tail).map(|i| self.bh.push(i)); } item }, ) } fn size_hint(&self) -> (usize, Option) { self.bh.iter().fold((0, Some(0)), |(lo, hi), it| { let (ilo, ihi) = it.size_hint(); ( max(lo, ilo), hi.and_then(|hi| ihi.and_then(|ihi| hi.checked_add(ihi))), ) }) } } pub struct Intersection { pub(crate) a: I, pub(crate) b: Peekable, } impl Clone for Intersection where I::Item: Clone, J::Item: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), } } } impl, J: Iterator> Iterator for Intersection { type Item = K; fn next(&mut self) -> Option { while let Some(a) = self.a.next() { while let Some(b) = self.b.peek() { let order = a.cmp(b); if order == Less { break; } self.b.next(); if order == Equal { return Some(a); } } } None } fn size_hint(&self) -> (usize, Option) { let (_, amax) = self.a.size_hint(); let (_, bmax) = self.b.size_hint(); // no overlap let rmin = 0; // full overlap let rmax = amax.and_then(|amax| bmax.map(|bmax| min(amax, bmax))); (rmin, rmax) } } pub struct Difference { pub(crate) a: I, pub(crate) b: Peekable, } impl Clone for Difference where I::Item: Clone, J::Item: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), } } } impl, J: Iterator> Iterator for Difference { type Item = K; fn next(&mut self) -> Option { 'next_a: while let Some(a) = self.a.next() { while let Some(b) = self.b.peek() { let order = a.cmp(b); if order == Less { break; } self.b.next(); if order == Equal { continue 'next_a; } } return Some(a); } None } fn size_hint(&self) -> (usize, Option) { let (amin, amax) = self.a.size_hint(); let (_, bmax) = self.b.size_hint(); // no overlap let rmax = amax; // if the other has at most bmax elements, and we have at least amin elements let rmin = bmax.map_or(0, |bmax| amin.saturating_sub(bmax)); (rmin, rmax) } } pub struct SymmetricDifference { pub(crate) a: Peekable, pub(crate) b: Peekable, } impl Clone for SymmetricDifference where I::Item: Clone, J::Item: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), } } } impl, J: Iterator> Iterator for SymmetricDifference { type Item = K; fn next(&mut self) -> Option { while let (Some(ak), Some(bk)) = (self.a.peek(), self.b.peek()) { match ak.cmp(&bk) { Less => return self.a.next(), Greater => return self.b.next(), Equal => { self.b.next(); self.a.next(); } } } self.a.next().or_else(|| self.b.next()) } fn size_hint(&self) -> (usize, Option) { let (amin, amax) = self.a.size_hint(); let (bmin, bmax) = self.b.size_hint(); // full overlap let rmin = match (amax, bmax) { (Some(amax), _) if bmin >= amax => bmin - amax, (_, Some(bmax)) if amin >= bmax => amin - bmax, _ => 0, }; // no overlap let rmax = amax.and_then(|amax| bmax.and_then(|bmax| amax.checked_add(bmax))); (rmin, rmax) } } #[derive(Clone, Debug)] pub struct Pairs { pub(crate) i: I, } impl Iterator for Pairs { type Item = (I::Item, ()); fn next(&mut self) -> Option { self.i.next().map(|k| (k, ())) } fn size_hint(&self) -> (usize, Option) { self.i.size_hint() } } #[derive(Clone, Debug)] pub struct AssumeSortedByItem { pub(crate) i: I, } impl Iterator for AssumeSortedByItem { type Item = I::Item; fn next(&mut self) -> Option { self.i.next() } fn size_hint(&self) -> (usize, Option) { self.i.size_hint() } } impl ExactSizeIterator for AssumeSortedByItem where I: ExactSizeIterator {} impl FusedIterator for AssumeSortedByItem where I: FusedIterator {} impl DoubleEndedIterator for AssumeSortedByItem where I: DoubleEndedIterator { fn next_back(&mut self) -> Option { self.i.next_back() } } // mark common std traits impl SortedByItem for iter::Empty {} impl SortedByItem for iter::Once {} impl<'a, T> SortedByItem for option::Iter<'a, T> {} impl<'a, T> SortedByItem for result::Iter<'a, T> {} impl SortedByItem for option::IntoIter {} impl SortedByItem for result::IntoIter {} impl SortedByItem for iter::Take {} impl SortedByItem for iter::Skip {} impl SortedByItem for iter::StepBy {} impl SortedByItem for iter::Cloned {} impl SortedByItem for iter::Copied {} impl SortedByItem for iter::Fuse {} impl SortedByItem for iter::Inspect {} impl SortedByItem for iter::TakeWhile {} impl SortedByItem for iter::SkipWhile {} impl SortedByItem for iter::Filter {} impl SortedByItem for iter::Peekable {} impl SortedByItem for collections::btree_set::IntoIter {} impl<'a, T> SortedByItem for collections::btree_set::Iter<'a, T> {} impl<'a, T> SortedByItem for collections::btree_set::Intersection<'a, T> {} impl<'a, T> SortedByItem for collections::btree_set::Union<'a, T> {} impl<'a, T> SortedByItem for collections::btree_set::Difference<'a, T> {} impl<'a, T> SortedByItem for collections::btree_set::SymmetricDifference<'a, T> {} impl<'a, T> SortedByItem for collections::btree_set::Range<'a, T> {} impl<'a, K, V> SortedByItem for collections::btree_map::Keys<'a, K, V> {} impl SortedByItem for ops::Range {} impl SortedByItem for ops::RangeInclusive {} impl SortedByItem for ops::RangeFrom {} impl SortedByItem for Keys {} impl SortedByItem for AssumeSortedByItem {} impl SortedByItem for Union {} impl SortedByItem for Intersection {} impl SortedByItem for Difference {} impl SortedByItem for SymmetricDifference {} impl SortedByItem for MultiwayUnion {} impl SortedByItem for Box {} impl SortedByItem for iter::Map {} impl SortedByItem for iter::FlatMap where Iin: OneOrLess, J: IntoIterator, Iout: SortedByItem, { } impl SortedByItem for iter::Flatten where Iin: OneOrLess + Iterator, J: IntoIterator, Iout: SortedByItem, { } #[cfg(test)] mod tests { use super::*; use core::fmt::Debug; use std::collections::BTreeMap; /// just a helper to get good output when a check fails fn binary_op(a: E, b: E, expected: R, actual: R) -> bool { let res = expected == actual; if !res { println!( "a:{:?} b:{:?} expected:{:?} actual:{:?}", a, b, expected, actual ); } res } type Element = i64; type Reference = collections::BTreeSet; #[quickcheck] fn intersection(a: Reference, b: Reference) -> bool { let expected: Reference = a.intersection(&b).cloned().collect(); let actual: Reference = a .clone() .into_iter() .intersection(b.clone().into_iter()) .collect(); binary_op(a, b, expected, actual) } #[quickcheck] fn union(a: Reference, b: Reference) -> bool { let expected: Reference = a.union(&b).cloned().collect(); let actual: Reference = a.clone().into_iter().union(b.clone().into_iter()).collect(); binary_op(a, b, expected, actual) } #[quickcheck] fn multi_union(inputs: Vec) -> bool { let expected: Reference = inputs.iter().flatten().copied().collect(); let actual = MultiwayUnion::from_iter(inputs.iter().map(|i| i.iter())); let res = actual.clone().eq(expected.iter()); if !res { let actual: Reference = actual.copied().collect(); println!("in:{:?} expected:{:?} out:{:?}", inputs, expected, actual); } res } #[quickcheck] fn difference(a: Reference, b: Reference) -> bool { let expected: Reference = a.difference(&b).cloned().collect(); let actual: Reference = a .clone() .into_iter() .difference(b.clone().into_iter()) .collect(); binary_op(a, b, expected, actual) } #[quickcheck] fn symmetric_difference(a: Reference, b: Reference) -> bool { let expected: Reference = a.symmetric_difference(&b).cloned().collect(); let actual: Reference = a .clone() .into_iter() .symmetric_difference(b.clone().into_iter()) .collect(); binary_op(a, b, expected, actual) } /// just a helper to get good output when a check fails fn check_size_hint( input: E, expected: usize, (min, max): (usize, Option), ) -> bool { let res = min <= expected && max.map_or(true, |max| expected <= max && min <= max); if !res { println!( "input:{:?} expected:{:?} min:{:?} max:{:?}", input, expected, min, max ); } res } #[quickcheck] fn intersection_size_hint(a: Reference, b: Reference) -> bool { let expected = a.intersection(&b).count(); let actual = a.iter().intersection(b.iter()).size_hint(); check_size_hint((a, b), expected, actual) } #[quickcheck] fn union_size_hint(a: Reference, b: Reference) -> bool { let expected = a.union(&b).count(); let actual = a.iter().union(b.iter()).size_hint(); check_size_hint((a, b), expected, actual) } #[quickcheck] fn multi_union_size_hint(inputs: Vec) -> bool { let expected: Reference = inputs.iter().flatten().copied().collect(); let actual = MultiwayUnion::from_iter(inputs.iter().map(|i| i.iter())).size_hint(); check_size_hint(inputs, expected.len(), actual) } #[quickcheck] fn difference_size_hint(a: Reference, b: Reference) -> bool { let expected = a.difference(&b).count(); let actual = a.iter().difference(b.iter()).size_hint(); check_size_hint((a, b), expected, actual) } #[quickcheck] fn symmetric_difference_size_hint(a: Reference, b: Reference) -> bool { let expected = a.symmetric_difference(&b).count(); let actual = a.iter().symmetric_difference(b.iter()).size_hint(); check_size_hint((a, b), expected, actual) } fn s() -> impl Iterator + SortedByItem { 0i64..10 } fn r<'a>() -> impl Iterator + SortedByItem { iter::empty() } fn is_s + SortedByItem>(_v: I) {} #[test] fn instances() { is_s(iter::empty::()); is_s(iter::once(0u64)); // ranges is_s(0i64..10); is_s(0i64..=10); is_s(0i64..); // wrappers is_s(Box::new(0i64..10)); // identity is_s(s().fuse()); is_s(r().cloned()); is_s(r().copied()); is_s(r().peekable()); is_s(s().inspect(|_| {})); // removing items is_s(s().step_by(2)); is_s(s().take(1)); is_s(s().take_while(|_| true)); is_s(s().skip(1)); is_s(s().skip_while(|_| true)); is_s(s().filter(|_| true)); // set ops is_s(s().union(s())); is_s(s().intersection(s())); is_s(s().difference(s())); is_s(s().symmetric_difference(s())); is_s(multiway_union(vec![s(), s(), s()])); is_s(multiway_union(iter::once(s()))); // one_or_less let a_btree = BTreeMap::::new(); is_s( Some(()) .iter() .map(|_| &a_btree) .filter(|b| !b.is_empty()) .flat_map(|m| m.keys()), ); is_s( iter::once(Some(())) .flatten() .map(|_| &a_btree) .filter(|b| !b.is_empty()) .flat_map(|m| m.keys()), ); } } sorted-iter-0.1.11/src/sorted_pair_iterator.rs000064400000000000000000000412651046102023000175360ustar 00000000000000//! implementation of the sorted_pair_iterator relational operations use super::*; use crate::sorted_iterator::SortedByItem; use core::{ cmp::Ordering::*, cmp::{max, min}, fmt::Debug, iter, iter::Peekable, option, result, }; use std::{collections, iter::FusedIterator}; /// marker trait for iterators that are sorted by the key of their Item pub trait SortedByKey {} pub struct Join { pub(crate) a: Peekable, pub(crate) b: Peekable, } impl Iterator for Join where K: Ord, I: Iterator + SortedByKey, J: Iterator + SortedByKey, { type Item = (K, (A, B)); fn next(&mut self) -> Option { while let (Some((ak, _)), Some((bk, _))) = (self.a.peek(), self.b.peek()) { match ak.cmp(&bk) { Less => { self.a.next(); } Greater => { self.b.next(); } Equal => { if let (Some((ak, av)), Some((_, bv))) = (self.a.next(), self.b.next()) { return Some((ak, (av, bv))); } else { unreachable!(); } } } } None } fn size_hint(&self) -> (usize, Option) { let (_, amax) = self.a.size_hint(); let (_, bmax) = self.b.size_hint(); let rmin = 0; let rmax = amax.and_then(|amax| bmax.map(|bmax| min(amax, bmax))); (rmin, rmax) } } impl Clone for Join where I::Item: Clone, J::Item: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), } } } pub struct LeftJoin { pub(crate) a: Peekable, pub(crate) b: Peekable, } impl Iterator for LeftJoin where K: Ord, I: Iterator, J: Iterator, { type Item = (K, (A, Option)); fn next(&mut self) -> Option { let (ak, av) = self.a.next()?; while let Some((bk, _)) = self.b.peek() { match ak.cmp(bk) { Less => break, Greater => { self.b.next(); } Equal => { let (_, bv) = self.b.next()?; return Some((ak, (av, Some(bv)))); } } } Some((ak, (av, None))) } fn size_hint(&self) -> (usize, Option) { self.a.size_hint() } } impl Clone for LeftJoin where I::Item: Clone, J::Item: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), } } } pub struct RightJoin { pub(crate) a: Peekable, pub(crate) b: Peekable, } impl Iterator for RightJoin where K: Ord, I: Iterator, J: Iterator, { type Item = (K, (Option, B)); fn next(&mut self) -> Option { let (bk, bv) = self.b.next()?; while let Some((ak, _)) = self.a.peek() { match bk.cmp(ak) { Less => break, Greater => { self.a.next(); } Equal => { let (_, av) = self.a.next()?; return Some((bk, (Some(av), bv))); } } } Some((bk, (None, bv))) } fn size_hint(&self) -> (usize, Option) { self.b.size_hint() } } impl Clone for RightJoin where I::Item: Clone, J::Item: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), } } } pub struct OuterJoin { pub(crate) a: Peekable, pub(crate) b: Peekable, } // all this just so I could avoid having this expression twice in the iterator. // Sometimes making things DRY in rust is hard... impl OuterJoin where K: Ord, I: Iterator, J: Iterator, { // type alias does not help much here... #[allow(clippy::type_complexity)] fn next_a(&mut self) -> Option<(K, (Option, Option))> { self.a.next().map(|(ak, av)| (ak, (Some(av), None))) } #[allow(clippy::type_complexity)] fn next_b(&mut self) -> Option<(K, (Option, Option))> { self.b.next().map(|(bk, bv)| (bk, (None, Some(bv)))) } } impl Iterator for OuterJoin where K: Ord, I: Iterator, J: Iterator, { type Item = (K, (Option, Option)); fn next(&mut self) -> Option { if let (Some((ak, _)), Some((bk, _))) = (self.a.peek(), self.b.peek()) { match ak.cmp(&bk) { Less => self.next_a(), Greater => self.next_b(), Equal => self .a .next() .and_then(|(ak, av)| self.b.next().map(|(_, bv)| (ak, (Some(av), Some(bv))))), } } else { self.next_a().or_else(|| self.next_b()) } } fn size_hint(&self) -> (usize, Option) { let (amin, amax) = self.a.size_hint(); let (bmin, bmax) = self.b.size_hint(); let rmin = max(amin, bmin); let rmax = amax.and_then(|amax| bmax.map(|bmax| amax + bmax)); (rmin, rmax) } } impl Clone for OuterJoin where I::Item: Clone, J::Item: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), } } } #[derive(Clone, Debug)] pub struct Keys { pub(crate) i: I, } impl Iterator for Keys where K: Ord, I: Iterator, { type Item = K; fn next(&mut self) -> Option { self.i.next().map(|(k, _)| k) } fn size_hint(&self) -> (usize, Option) { self.i.size_hint() } } impl> ExactSizeIterator for Keys { fn len(&self) -> usize { self.i.len() } } impl> DoubleEndedIterator for Keys { fn next_back(&mut self) -> Option { self.i.next_back().map(|(k, _)| k) } } #[derive(Clone, Debug)] pub struct MapValues { pub(crate) i: I, pub(crate) f: F, } impl Iterator for MapValues where K: Ord, I: Iterator, F: FnMut(V) -> W, { type Item = (K, W); fn next(&mut self) -> Option { self.i.next().map(|(k, v)| (k, (self.f)(v))) } fn size_hint(&self) -> (usize, Option) { self.i.size_hint() } } impl ExactSizeIterator for MapValues where I: ExactSizeIterator + Iterator, F: FnMut(V) -> W, { fn len(&self) -> usize { self.i.len() } } impl DoubleEndedIterator for MapValues where I: DoubleEndedIterator + Iterator, F: FnMut(V) -> W, { fn next_back(&mut self) -> Option { self.i.next_back().map(|(k, v)| (k, (self.f)(v))) } } #[derive(Clone, Debug)] pub struct FilterMapValues { pub(crate) i: I, pub(crate) f: F, } impl Iterator for FilterMapValues where K: Ord, I: Iterator, F: FnMut(V) -> Option, { type Item = (K, W); fn next(&mut self) -> Option { while let Some((k, v)) = self.i.next() { if let Some(w) = (self.f)(v) { return Some((k, w)); } } None } fn size_hint(&self) -> (usize, Option) { let (_, imax) = self.i.size_hint(); (0, imax) } } #[derive(Clone, Debug)] pub struct AssumeSortedByKey { pub(crate) i: I, } impl Iterator for AssumeSortedByKey { type Item = I::Item; fn next(&mut self) -> Option { self.i.next() } fn size_hint(&self) -> (usize, Option) { self.i.size_hint() } } impl ExactSizeIterator for AssumeSortedByKey where I: ExactSizeIterator {} impl FusedIterator for AssumeSortedByKey where I: FusedIterator {} impl DoubleEndedIterator for AssumeSortedByKey where I: DoubleEndedIterator, { fn next_back(&mut self) -> Option { self.i.next_back() } } // not sure how to model this: an iterator that is sorted by key automatically sorted by item as well, // since the key comes first in the order of a pair. The reverse is not true, since e.g. (0,1), (0,2) // are sorted by item, but not strictly sorted by key! // mark common std traits impl SortedByKey for iter::Empty {} impl SortedByKey for iter::Once {} impl<'a, T> SortedByKey for option::Iter<'a, T> {} impl<'a, T> SortedByKey for result::Iter<'a, T> {} impl SortedByKey for option::IntoIter {} impl SortedByKey for result::IntoIter {} impl SortedByKey for iter::Enumerate {} impl SortedByKey for Pairs {} impl SortedByKey for MapValues {} impl SortedByKey for FilterMapValues {} impl SortedByKey for iter::Take {} impl SortedByKey for iter::Skip {} impl SortedByKey for iter::StepBy {} impl SortedByKey for iter::Cloned {} impl SortedByKey for iter::Copied {} impl SortedByKey for iter::Fuse {} impl SortedByKey for iter::Inspect {} impl SortedByKey for iter::TakeWhile {} impl SortedByKey for iter::SkipWhile {} impl SortedByKey for iter::Filter {} impl SortedByKey for iter::Peekable {} impl SortedByKey for iter::Zip {} impl SortedByKey for Join {} impl SortedByKey for LeftJoin {} impl SortedByKey for RightJoin {} impl SortedByKey for OuterJoin {} impl SortedByKey for AssumeSortedByKey {} impl SortedByKey for collections::btree_map::IntoIter {} impl<'a, K, V> SortedByKey for collections::btree_map::Iter<'a, K, V> {} impl<'a, K, V> SortedByKey for collections::btree_map::IterMut<'a, K, V> {} impl<'a, K, V> SortedByKey for collections::btree_map::Range<'a, K, V> {} impl<'a, K, V> SortedByKey for collections::btree_map::RangeMut<'a, K, V> {} impl SortedByKey for Box {} impl SortedByKey for iter::Map {} impl SortedByKey for iter::FlatMap where Iin: OneOrLess, J: IntoIterator, Iout: SortedByKey, { } impl SortedByKey for iter::Flatten where Iin: OneOrLess + Iterator, J: IntoIterator, Iout: SortedByKey, { } #[cfg(test)] mod tests { extern crate maplit; use super::*; use collections::BTreeMap; use core::fmt::Debug; use maplit::*; /// just a helper to get good output when a check fails fn unary_op(x: E, expected: R, actual: R) -> bool { let res = expected == actual; if !res { println!("x:{:?} expected:{:?} actual:{:?}", x, expected, actual); } res } /// just a helper to get good output when a check fails fn binary_op(a: E, b: E, expected: R, actual: R) -> bool { let res = expected == actual; if !res { println!( "a:{:?} b:{:?} expected:{:?} actual:{:?}", a, b, expected, actual ); } res } type Element = i64; type Reference = BTreeMap; #[quickcheck] fn join(a: Reference, b: Reference) -> bool { type Result = BTreeMap; let expected: Result = a .keys() .intersection(b.keys()) .map(|k| (k.clone(), (a[k], b[k]))) .collect(); let actual: Result = a.clone().into_iter().join(b.clone().into_iter()).collect(); binary_op(a, b, expected, actual) } #[quickcheck] fn left_join(a: Reference, b: Reference) -> bool { type Result = BTreeMap)>; let expected: Result = a .keys() .map(|k| (k.clone(), (a[k], b.get(k).cloned()))) .collect(); let actual: Result = a .clone() .into_iter() .left_join(b.clone().into_iter()) .collect(); binary_op(a, b, expected, actual) } #[quickcheck] fn right_join(a: Reference, b: Reference) -> bool { type Result = BTreeMap, Element)>; let expected: Result = b .keys() .map(|k| (k.clone(), (a.get(k).cloned(), b[k]))) .collect(); let actual: Result = a .clone() .into_iter() .right_join(b.clone().into_iter()) .collect(); binary_op(a, b, expected, actual) } #[quickcheck] fn outer_join(a: Reference, b: Reference) -> bool { type Result = BTreeMap, Option)>; let expected: Result = a .keys() .union(b.keys()) .map(|k| (k.clone(), (a.get(k).cloned(), b.get(k).cloned()))) .collect(); let actual: Result = a .clone() .into_iter() .outer_join(b.clone().into_iter()) .collect(); binary_op(a, b, expected, actual) } #[quickcheck] fn map_values(x: Reference) -> bool { type Result = BTreeMap; let expected: Result = x.clone().into_iter().map(|(k, v)| (k, v * 2)).collect(); let actual: Result = x.clone().into_iter().map_values(|v| v * 2).collect(); unary_op(x, expected, actual) } #[quickcheck] fn filter_map_values(x: Reference) -> bool { type Result = BTreeMap; let expected: Result = x .clone() .into_iter() .filter_map(|(k, v)| if v % 2 != 0 { Some((k, v * 2)) } else { None }) .collect(); let actual: Result = x .clone() .into_iter() .filter_map_values(|v| if v % 2 != 0 { Some(v * 2) } else { None }) .collect(); unary_op(x, expected, actual) } fn is_s + SortedByKey>(_v: I) {} fn s() -> impl Iterator + SortedByKey { (0i64..10).pairs() } #[test] fn instances() { // creation is_s(iter::empty::<(i64, ())>()); is_s(iter::once((0, ()))); is_s([1, 2, 3, 4].iter().enumerate()); // ranges is_s((0i64..10).pairs()); is_s((0i64..=10).pairs()); is_s((0i64..).pairs()); // wrappers is_s(Box::new((0i64..).pairs())); // skip/take/step/filter is_s(s().step_by(1)); is_s(s().take(1)); is_s(s().skip(1)); is_s(s().take_while(|_| true)); is_s(s().skip_while(|_| true)); is_s(s().filter(|_| true)); // identity is_s(s().peekable()); is_s(s().fuse()); is_s(s().inspect(|_| {})); // relational is_s(s().join(s())); is_s(s().left_join(s())); is_s(s().right_join(s())); is_s(s().outer_join(s())); // btreeset is_s(btreemap! { 0i64 => "" }.iter()); is_s(btreemap! { 0i64 => "" }.into_iter()); is_s(btreemap! { 0i64 => "" }.iter_mut()); is_s(btreemap! { 0i64 => "" }.range(..)); is_s(btreemap! { 0i64 => "" }.range_mut(..)); // one_or_less let a_btree = BTreeMap::::new(); is_s( Some(()) .iter() .map(|_| &a_btree) .filter(|b| !b.is_empty()) .flatten(), ); is_s( iter::once(Result::::Ok(12)) .flatten() .map(|_| &a_btree) .filter(|b| !b.is_empty()) .flat_map(|m| m.iter()), ); } }