├── .gitignore ├── rustfmt.toml ├── .travis.yml ├── tests ├── iterator_regression.rs └── iterator.rs ├── .github ├── stale.yml ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE.md ├── workflows │ └── ci.yml ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── Cargo.toml ├── README.md ├── LICENSE-MIT ├── CERTIFICATE ├── benches └── bench.rs ├── CHANGELOG.md ├── LICENSE-APACHE └── src ├── lib.rs └── iterator.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | tab_spaces = 2 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | rust: stable 4 | 5 | before_script: 6 | - rustup component add rustfmt 7 | - rustup component add clippy 8 | - cargo fmt --version 9 | - cargo clippy --version 10 | 11 | script: 12 | - cargo fmt -- --check 13 | - cargo build --verbose 14 | - cargo test --verbose 15 | - cargo clippy -- -D clippy::all 16 | -------------------------------------------------------------------------------- /tests/iterator_regression.rs: -------------------------------------------------------------------------------- 1 | extern crate flat_tree; 2 | 3 | #[test] 4 | /// Postmortem: offset was incorrectly calculated when finding a parent for a 5 | /// node with an odd offset. 6 | fn parent_and_odd_offset() { 7 | let mut iterator = flat_tree::Iterator::new(10); 8 | assert_eq!(iterator.index(), 10); 9 | assert_eq!(iterator.offset(), 5); 10 | assert_eq!(iterator.parent(), 9); 11 | assert_eq!(iterator.offset(), 2); 12 | assert_eq!(iterator.parent(), 11); 13 | assert_eq!(iterator.offset(), 1); 14 | } 15 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | daysUntilStale: 90 4 | daysUntilClose: 7 5 | exemptLabels: 6 | - pinned 7 | - security 8 | exemptProjects: false 9 | exemptMilestones: false 10 | staleLabel: wontfix 11 | markComment: > 12 | This issue has been automatically marked as stale because it has not had 13 | recent activity. It will be closed if no further activity occurs. Thank you 14 | for your contributions. 15 | unmarkComment: false 16 | closeComment: false 17 | limitPerRun: 30 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flat-tree" 3 | version = "6.1.0-alpha.0" 4 | description = "Series of functions to map a binary tree to a list" 5 | authors = ["Mathias Buus ", "Timo Tiuraniemi "] 6 | repository = "https://github.com/datrs/flat-tree" 7 | keywords = ["flat", "tree", "binary"] 8 | readme = "README.md" 9 | license = "MIT OR Apache-2.0" 10 | edition = "2021" 11 | 12 | [lib] 13 | bench = false 14 | 15 | [dev-dependencies] 16 | criterion = "0.3" 17 | 18 | [[bench]] 19 | name = "bench" 20 | harness = false 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | **Choose one:** is this a 🐛 bug fix, a 🙋 feature, or a 🔦 documentation change? 8 | 9 | 10 | 11 | ## Checklist 12 | 13 | - [ ] tests pass 14 | - [ ] tests and/or benchmarks are included 15 | - [ ] documentation is changed or added 16 | 17 | ## Context 18 | 19 | 20 | ## Semver Changes 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flat-tree 2 | 3 | [![crates.io version][1]][2] [![build status][3]][4] 4 | [![downloads][5]][6] [![docs.rs docs][7]][8] 5 | 6 | Map a binary tree to a list. Adapted from 7 | [mafintosh/flat-tree](https://github.com/mafintosh/flat-tree). 8 | 9 | - [Documentation][8] 10 | - [Crates.io][2] 11 | 12 | ## License 13 | [MIT](./LICENSE-MIT) OR [Apache-2.0](./LICENSE-APACHE) 14 | 15 | [1]: https://img.shields.io/crates/v/flat-tree.svg?style=flat-square 16 | [2]: https://crates.io/crates/flat-tree 17 | [3]: https://github.com/datrs/flat-tree/actions/workflows/ci.yml/badge.svg 18 | [4]: https://github.com/datrs/flat-tree/actions 19 | [5]: https://img.shields.io/crates/d/flat-tree.svg?style=flat-square 20 | [6]: https://crates.io/crates/flat-tree 21 | [7]: https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square 22 | [8]: https://docs.rs/flat-tree 23 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Yoshua Wuyts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CERTIFICATE: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | 13 | Developer's Certificate of Origin 1.1 14 | 15 | By making a contribution to this project, I certify that: 16 | 17 | (a) The contribution was created in whole or in part by me and I 18 | have the right to submit it under the open source license 19 | indicated in the file; or 20 | 21 | (b) The contribution is based upon previous work that, to the best 22 | of my knowledge, is covered under an appropriate open source 23 | license and I have the right under that license to submit that 24 | work with modifications, whether created in whole or in part 25 | by me, under the same open source license (unless I am 26 | permitted to submit under a different license), as indicated 27 | in the file; or 28 | 29 | (c) The contribution was provided directly to me by some other 30 | person who certified (a), (b) or (c) and I have not modified 31 | it. 32 | 33 | (d) I understand and agree that this project and the contribution 34 | are public and that a record of the contribution (including all 35 | personal information I submit with it, including my sign-off) is 36 | maintained indefinitely and may be redistributed consistent with 37 | this project or the open source license(s) involved. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | **Choose one:** is this a 🐛 bug report, a 🙋 feature request, or a 🔦 question? 9 | 10 | 11 | 12 | ## Expected Behavior 13 | 14 | 15 | 16 | 17 | ## Current Behavior 18 | 19 | 20 | 21 | 22 | ## Possible Solution 23 | 24 | 25 | 26 | ## Context 27 | 28 | 29 | 30 | ## Code Sample 31 | 32 | 33 | ## Your Environment 34 | 35 | 36 | | Software | Version(s) | 37 | | ---------------- | ---------- | 38 | | Package | 39 | | Runtime | 40 | | Package Manager | 41 | | Operating System | 42 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | extern crate flat_tree; 4 | 5 | use criterion::Criterion; 6 | 7 | fn criterion_benchmark(c: &mut Criterion) { 8 | c.bench_function("children_9", |b| b.iter(|| flat_tree::children(9))); 9 | c.bench_function("children_256", |b| b.iter(|| flat_tree::children(256))); 10 | c.bench_function("count_128", |b| b.iter(|| flat_tree::count(128))); 11 | c.bench_function("depth_128", |b| b.iter(|| flat_tree::depth(128))); 12 | // c.bench_function("full_roots_16", |b| { 13 | // let mut nodes: Vec = Vec::with_capacity(16); 14 | // b.iter(|| { 15 | // flat_tree::full_roots(16, &mut nodes); 16 | // }) 17 | // }); 18 | // c.bench_function("full_roots_256", |b| { 19 | // let mut nodes: Vec = Vec::with_capacity(16); 20 | // b.iter(|| { 21 | // flat_tree::full_roots(256, &mut nodes); 22 | // }) 23 | // }); 24 | c.bench_function("index_3_1", |b| b.iter(|| flat_tree::index(3, 1))); 25 | c.bench_function("left_child_128", |b| b.iter(|| flat_tree::left_child(128))); 26 | c.bench_function("left_span_128", |b| b.iter(|| flat_tree::left_span(128))); 27 | c.bench_function("offset_128", |b| b.iter(|| flat_tree::offset(128))); 28 | c.bench_function("parent_128", |b| b.iter(|| flat_tree::parent(128))); 29 | c.bench_function("right_child_128", |b| { 30 | b.iter(|| flat_tree::right_child(128)) 31 | }); 32 | c.bench_function("right_span_128", |b| b.iter(|| flat_tree::right_span(128))); 33 | c.bench_function("sibling_128", |b| b.iter(|| flat_tree::sibling(128))); 34 | c.bench_function("spans_128", |b| b.iter(|| flat_tree::spans(128))); 35 | c.bench_function("uncle_128", |b| b.iter(|| flat_tree::uncle(128))); 36 | } 37 | 38 | criterion_group!(benches, criterion_benchmark); 39 | criterion_main!(benches); 40 | -------------------------------------------------------------------------------- /tests/iterator.rs: -------------------------------------------------------------------------------- 1 | extern crate flat_tree; 2 | 3 | #[test] 4 | fn iterator() { 5 | let mut iterator = flat_tree::Iterator::default(); 6 | assert_eq!(iterator.index(), 0); 7 | assert_eq!(iterator.prev(), 0); 8 | assert!(iterator.is_left()); 9 | assert!(!iterator.is_right()); 10 | assert_eq!(iterator.parent(), 1); 11 | assert_eq!(iterator.offset(), 0); 12 | assert_eq!(iterator.parent(), 3); 13 | assert_eq!(iterator.offset(), 0); 14 | assert_eq!(iterator.parent(), 7); 15 | assert_eq!(iterator.offset(), 0); 16 | assert_eq!(iterator.right_child(), 11); 17 | assert_eq!(iterator.left_child(), 9); 18 | assert_eq!(iterator.next(), Some(13)); 19 | assert!(!iterator.is_left()); 20 | assert!(iterator.is_right()); 21 | assert_eq!(iterator.left_span(), 12); 22 | assert_eq!(iterator.next(), Some(14)); 23 | assert_eq!(iterator.next(), Some(16)); 24 | assert_eq!(iterator.offset(), 8); 25 | assert_eq!(iterator.parent(), 17); 26 | assert_eq!(iterator.offset(), 4); 27 | assert_eq!(iterator.parent(), 19); 28 | assert_eq!(iterator.offset(), 2); 29 | assert_eq!(iterator.parent(), 23); 30 | assert_eq!(iterator.offset(), 1); 31 | assert_eq!(iterator.right_span(), 30); 32 | 33 | iterator.seek(23); 34 | 35 | assert_eq!(iterator.index(), 23); 36 | assert_eq!(iterator.offset(), 1); 37 | assert_eq!(iterator.right_child(), 27); 38 | assert_eq!(iterator.sibling(), 19); 39 | assert_eq!(iterator.prev(), 11); 40 | assert_eq!(iterator.left_child(), 9); 41 | assert_eq!(iterator.prev(), 5); 42 | assert_eq!(iterator.left_child(), 4); 43 | assert_eq!(iterator.prev(), 2); 44 | assert_eq!(iterator.prev(), 0); 45 | } 46 | 47 | #[test] 48 | fn non_leaf_start() { 49 | let mut iterator = flat_tree::Iterator::new(1); 50 | assert_eq!(iterator.index(), 1); 51 | assert_eq!(iterator.parent(), 3); 52 | assert_eq!(iterator.parent(), 7); 53 | assert_eq!(iterator.right_child(), 11); 54 | assert_eq!(iterator.left_child(), 9); 55 | assert_eq!(iterator.next(), Some(13)); 56 | assert_eq!(iterator.left_span(), 12); 57 | } 58 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: 'CI' 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | 8 | env: 9 | RUST_BACKTRACE: 1 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | ci-pass: 14 | name: CI is green 15 | runs-on: ubuntu-latest 16 | needs: 17 | - test-linux 18 | - test-windows 19 | - test-macos 20 | - build-extra 21 | - lint 22 | steps: 23 | - run: exit 0 24 | 25 | test-linux: 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - uses: actions/checkout@v3 30 | - uses: dtolnay/rust-toolchain@stable 31 | with: 32 | components: rustfmt 33 | - name: Run tests 34 | run: | 35 | cargo check 36 | cargo test 37 | 38 | test-windows: 39 | runs-on: windows-latest 40 | 41 | steps: 42 | - uses: actions/checkout@v3 43 | - uses: dtolnay/rust-toolchain@stable 44 | with: 45 | components: rustfmt 46 | - name: Run tests 47 | run: | 48 | cargo check 49 | cargo test 50 | 51 | test-macos: 52 | runs-on: macos-latest 53 | 54 | steps: 55 | - uses: actions/checkout@v3 56 | - uses: dtolnay/rust-toolchain@stable 57 | with: 58 | components: rustfmt 59 | - name: Run tests 60 | run: | 61 | cargo check 62 | cargo test 63 | 64 | build-extra: 65 | runs-on: ubuntu-latest 66 | 67 | steps: 68 | - uses: actions/checkout@v3 69 | - uses: dtolnay/rust-toolchain@stable 70 | with: 71 | targets: wasm32-unknown-unknown 72 | - name: Build WASM 73 | run: | 74 | cargo build --target=wasm32-unknown-unknown 75 | - name: Build benches 76 | run: | 77 | cargo build --benches 78 | - name: Build release 79 | run: | 80 | cargo build --release 81 | 82 | lint: 83 | runs-on: ubuntu-latest 84 | 85 | steps: 86 | - uses: actions/checkout@v3 87 | - uses: dtolnay/rust-toolchain@stable 88 | with: 89 | components: rustfmt, clippy 90 | - uses: actions-rs/clippy-check@v1 91 | with: 92 | token: ${{ secrets.GITHUB_TOKEN }} 93 | - name: Format check 94 | run: | 95 | cargo fmt -- --check 96 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Contributions include code, documentation, answering user questions, running the 3 | project's infrastructure, and advocating for all types of users. 4 | 5 | The project welcomes all contributions from anyone willing to work in good faith 6 | with other contributors and the community. No contribution is too small and all 7 | contributions are valued. 8 | 9 | This guide explains the process for contributing to the project's GitHub 10 | Repository. 11 | 12 | - [Code of Conduct](#code-of-conduct) 13 | - [Bad Actors](#bad-actors) 14 | - [Developer Certificate of Origin](#developer-certificate-of-origin) 15 | 16 | ## Code of Conduct 17 | The project has a [Code of Conduct][./CODE_OF_CONDUCT.md] that *all* 18 | contributors are expected to follow. This code describes the *minimum* behavior 19 | expectations for all contributors. 20 | 21 | As a contributor, how you choose to act and interact towards your 22 | fellow contributors, as well as to the community, will reflect back not only 23 | on yourself but on the project as a whole. The Code of Conduct is designed and 24 | intended, above all else, to help establish a culture within the project that 25 | allows anyone and everyone who wants to contribute to feel safe doing so. 26 | 27 | Should any individual act in any way that is considered in violation of the 28 | [Code of Conduct][./CODE_OF_CONDUCT.md], corrective actions will be taken. It is 29 | possible, however, for any individual to *act* in such a manner that is not in 30 | violation of the strict letter of the Code of Conduct guidelines while still 31 | going completely against the spirit of what that Code is intended to accomplish. 32 | 33 | Open, diverse, and inclusive communities live and die on the basis of trust. 34 | Contributors can disagree with one another so long as they trust that those 35 | disagreements are in good faith and everyone is working towards a common 36 | goal. 37 | 38 | ## Bad Actors 39 | All contributors to tacitly agree to abide by both the letter and 40 | spirit of the [Code of Conduct][./CODE_OF_CONDUCT.md]. Failure, or 41 | unwillingness, to do so will result in contributions being respectfully 42 | declined. 43 | 44 | A *bad actor* is someone who repeatedly violates the *spirit* of the Code of 45 | Conduct through consistent failure to self-regulate the way in which they 46 | interact with other contributors in the project. In doing so, bad actors 47 | alienate other contributors, discourage collaboration, and generally reflect 48 | poorly on the project as a whole. 49 | 50 | Being a bad actor may be intentional or unintentional. Typically, unintentional 51 | bad behavior can be easily corrected by being quick to apologize and correct 52 | course *even if you are not entirely convinced you need to*. Giving other 53 | contributors the benefit of the doubt and having a sincere willingness to admit 54 | that you *might* be wrong is critical for any successful open collaboration. 55 | 56 | Don't be a bad actor. 57 | 58 | ## Developer Certificate of Origin 59 | All contributors must read and agree to the [Developer Certificate of 60 | Origin (DCO)](../CERTIFICATE). 61 | 62 | The DCO allows us to accept contributions from people to the project, similarly 63 | to how a license allows us to distribute our code. 64 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, 10 | education, socio-economic status, nationality, personal appearance, race, 11 | religion, or sexual identity and orientation. 12 | 13 | ## Our Standards 14 | 15 | Examples of behavior that contributes to creating a positive environment 16 | include: 17 | 18 | - Using welcoming and inclusive language 19 | - Being respectful of differing viewpoints and experiences 20 | - Gracefully accepting constructive criticism 21 | - Focusing on what is best for the community 22 | - Showing empathy towards other community members 23 | 24 | Examples of unacceptable behavior by participants include: 25 | 26 | - The use of sexualized language or imagery and unwelcome sexual attention or 27 | advances 28 | - Trolling, insulting/derogatory comments, and personal or political attacks 29 | - Public or private harassment 30 | - Publishing others' private information, such as a physical or electronic 31 | address, without explicit permission 32 | - Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | 36 | ## Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying the standards of acceptable 39 | behavior and are expected to take appropriate and fair corrective action in 40 | response to any instances of unacceptable behavior. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, or 43 | reject comments, commits, code, wiki edits, issues, and other contributions 44 | that are not aligned to this Code of Conduct, or to ban temporarily or 45 | permanently any contributor for other behaviors that they deem inappropriate, 46 | threatening, offensive, or harmful. 47 | 48 | ## Scope 49 | 50 | This Code of Conduct applies both within project spaces and in public spaces 51 | when an individual is representing the project or its community. Examples of 52 | representing a project or community include using an official project e-mail 53 | address, posting via an official social media account, or acting as an appointed 54 | representative at an online or offline event. Representation of a project may be 55 | further defined and clarified by project maintainers. 56 | 57 | ## Enforcement 58 | 59 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 60 | reported by contacting the project team at yoshuawuyts@gmail.com, or through 61 | IRC. All complaints will be reviewed and investigated and will result in a 62 | response that is deemed necessary and appropriate to the circumstances. The 63 | project team is obligated to maintain confidentiality with regard to the 64 | reporter of an incident. 65 | Further details of specific enforcement policies may be posted separately. 66 | 67 | Project maintainers who do not follow or enforce the Code of Conduct in good 68 | faith may face temporary or permanent repercussions as determined by other 69 | members of the project's leadership. 70 | 71 | ## Attribution 72 | 73 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 74 | available at 75 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 76 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2023-04-12, Version 6.0.0 2 | ### Commits 3 | 4 | - [[`6a7bbb5272`](https://github.com/datrs/flat-tree/commit/6a7bbb5272b51754ce47ee12430c5221ad22aa7b)] Release 6.0.0 (Timo Tiuraniemi) 5 | - [[`539ed6eb0d`](https://github.com/datrs/flat-tree/commit/539ed6eb0de7a1939c1c2ab65444cf23ecc47049)] Run CI on master branch instead of main (Timo Tiuraniemi) 6 | - [[`694c6ef688`](https://github.com/datrs/flat-tree/commit/694c6ef688b61eefb338dce79d07fdbfee108141)] Switch from Travis to GHA (Timo Tiuraniemi) 7 | - [[`597c8caa29`](https://github.com/datrs/flat-tree/commit/597c8caa29a047d05b3eb34dcc942c2579e8ade0)] Fix clippy warnings (Timo Tiuraniemi) 8 | - [[`e707e790fb`](https://github.com/datrs/flat-tree/commit/e707e790fb0219d9516055c44711488962f12c53)] Move documentation to lib.rs for easier maintainability (Timo Tiuraniemi) 9 | - [[`179a44d8bb`](https://github.com/datrs/flat-tree/commit/179a44d8bb1af953db5e7b60e8c32fb1d49557b8)] Fix Cargo.toml, bump edition to 2021 and, because of that, version to next major 6 (Timo Tiuraniemi) 10 | - [[`27277762f6`](https://github.com/datrs/flat-tree/commit/27277762f62bb46ec18599775f2fab8713f6e986)] Use a new version number to avoid resolution mixups (Timo Tiuraniemi) 11 | - [[`3c669d8c70`](https://github.com/datrs/flat-tree/commit/3c669d8c70ac42c5dfc5fa0ea46b9ec2a203a5e0)] Fix duplicated slashes in contains docs (Timo Tiuraniemi) 12 | - [[`ec7a72ef14`](https://github.com/datrs/flat-tree/commit/ec7a72ef14f3cdf7460e87976f009960f8af5370)] Add Iterator::full_tree() (Timo Tiuraniemi) 13 | - [[`8bc607394a`](https://github.com/datrs/flat-tree/commit/8bc607394af5303c229fe43326913ffc6ce41920)] Use a bigger tree in README to ease understanding of tests. Add a few more tests. (Timo Tiuraniemi) 14 | - [[`3acd091d7e`](https://github.com/datrs/flat-tree/commit/3acd091d7e482299a430620e0f84a04451ffc4ad)] Add Iterator::next_tree() and Iterator::prev_tree() (Timo Tiuraniemi) 15 | - [[`7048931423`](https://github.com/datrs/flat-tree/commit/7048931423738a7e3b3318ffe3cc625249b9ea79)] Add count_leaves(), Iterator::count_nodes() and Iterator::count_leaves() (Timo Tiuraniemi) 16 | - [[`3b136873ce`](https://github.com/datrs/flat-tree/commit/3b136873ceedb3c6af5de6c00d5e59cfbce9bc6a)] Fix README picture which had confused indexes (Timo Tiuraniemi) 17 | - [[`2bb6bf478d`](https://github.com/datrs/flat-tree/commit/2bb6bf47811d33051f3fe632c5736b4bacbf608a)] Add a contains() method to the iterator (Timo Tiuraniemi) 18 | - [[`b1cb6ab914`](https://github.com/datrs/flat-tree/commit/b1cb6ab914e946cf3404769a782ec4442dc28e4d)] Update changelog (Bruno Tavares) 19 | 20 | ### Stats 21 | ```diff 22 | .github/CODE_OF_CONDUCT.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 23 | .github/CONTRIBUTING.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 24 | .github/ISSUE_TEMPLATE.md | 41 +++++++++++++++++++++++++++++++++++++++++ 25 | .github/PULL_REQUEST_TEMPLATE.md | 21 +++++++++++++++++++++ 26 | .github/stale.yml | 17 +++++++++++++++++ 27 | .github/workflows/ci.yml | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 28 | CERTIFICATE | 37 +++++++++++++++++++++++++++++++++++++ 29 | CHANGELOG.md | 20 ++++++++++++++++++++ 30 | Cargo.toml | 11 ++++++----- 31 | README.md | 61 ++----------------------------------------------------------- 32 | src/iterator.rs | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 33 | src/lib.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------- 34 | tests/iterator.rs | 8 ++++---- 35 | 13 files changed, 634 insertions(+), 84 deletions(-) 36 | ``` 37 | 38 | 39 | ## 2020-03-03, Version 5.0.0 40 | ### Commits 41 | - [[`05ffe8699d`](https://github.com/mafintosh/flat-tree-rs/commit/05ffe8699d452b01152a596e5cb0a652e7915503)] (cargo-release) version 5.0.0 (Bruno Tavares) 42 | - [[`709b2d5635`](https://github.com/mafintosh/flat-tree-rs/commit/709b2d5635c4d2f9fdad48e23d55d257294dbdd9)] Merge pull request #44 from bltavares/usize-to-u64 (Bruno Tavares) 43 | - [[`bc9c866bdb`](https://github.com/mafintosh/flat-tree-rs/commit/bc9c866bdb2101aa892b4bf917a8b1a30be1da45)] Fix travis script (Bruno Tavares) 44 | - [[`68f7e67ed4`](https://github.com/mafintosh/flat-tree-rs/commit/68f7e67ed4c76fc6c11bb45ea78e9ec41b3d2449)] Change from usize to u64 (Bruno Tavares) 45 | - [[`f9ca2ef68e`](https://github.com/mafintosh/flat-tree-rs/commit/f9ca2ef68e19a861117788bb6624e9529b1d6419)] Update criterion requirement from 0.2 to 0.3 (dependabot-preview[bot]) 46 | - [[`b23328579d`](https://github.com/mafintosh/flat-tree-rs/commit/b23328579d00947d6480c3e894e18e1598905204)] Update changelog (Yoshua Wuyts) 47 | 48 | ### Stats 49 | ```diff 50 | .travis.yml | 6 +++--- 51 | CHANGELOG.md | 15 +++++++++++++++ 52 | Cargo.toml | 4 ++-- 53 | src/iterator.rs | 36 ++++++++++++++++++------------------ 54 | src/lib.rs | 34 +++++++++++++++++----------------- 55 | 5 files changed, 55 insertions(+), 40 deletions(-) 56 | ``` 57 | 58 | 59 | ## 2016-03-09, Version v1.0.0 60 | ### Commits 61 | - [[`b256517aef`](https://github.com/mafintosh/flat-tree-rs/commit/b256517aefb4ce2092e2920931d4c78cca6590af)] add badge (Mathias Buus) 62 | - [[`017436cbae`](https://github.com/mafintosh/flat-tree-rs/commit/017436cbaec00ef57b7e8311a5f674fd0cf418e4)] add desc (Mathias Buus) 63 | - [[`388d2445bc`](https://github.com/mafintosh/flat-tree-rs/commit/388d2445bc567148867a79f315aeee3ca492b100)] first commit (Mathias Buus) 64 | 65 | ### Stats 66 | ```diff 67 | Cargo.toml | 2 +- 68 | rustfmt.toml | 2 +- 69 | src/lib.rs | 328 ++++++++++++++++++++++++++---------------------------------- 70 | 3 files changed, 144 insertions(+), 188 deletions(-) 71 | ``` 72 | 73 | 74 | ## 2016-03-09, Version v1.0.0 75 | ### Commits 76 | - [[`b256517aef`](https://github.com/mafintosh/flat-tree-rs/commit/b256517aefb4ce2092e2920931d4c78cca6590af)] add badge (Mathias Buus) 77 | - [[`017436cbae`](https://github.com/mafintosh/flat-tree-rs/commit/017436cbaec00ef57b7e8311a5f674fd0cf418e4)] add desc (Mathias Buus) 78 | - [[`388d2445bc`](https://github.com/mafintosh/flat-tree-rs/commit/388d2445bc567148867a79f315aeee3ca492b100)] first commit (Mathias Buus) 79 | 80 | ### Stats 81 | ```diff 82 | Cargo.toml | 2 +- 83 | rustfmt.toml | 2 +- 84 | src/lib.rs | 328 ++++++++++++++++++++++++++---------------------------------- 85 | 3 files changed, 144 insertions(+), 188 deletions(-) 86 | ``` 87 | 88 | 89 | ## 2016-03-09, Version v1.0.0 90 | ### Commits 91 | - [[`b256517aef`](https://github.com/mafintosh/flat-tree-rs/commit/b256517aefb4ce2092e2920931d4c78cca6590af)] add badge (Mathias Buus) 92 | - [[`017436cbae`](https://github.com/mafintosh/flat-tree-rs/commit/017436cbaec00ef57b7e8311a5f674fd0cf418e4)] add desc (Mathias Buus) 93 | - [[`388d2445bc`](https://github.com/mafintosh/flat-tree-rs/commit/388d2445bc567148867a79f315aeee3ca492b100)] first commit (Mathias Buus) 94 | 95 | ### Stats 96 | ```diff 97 | Cargo.toml | 2 +- 98 | rustfmt.toml | 2 +- 99 | src/lib.rs | 328 ++++++++++++++++++++++++++---------------------------------- 100 | 3 files changed, 144 insertions(+), 188 deletions(-) 101 | ``` 102 | 103 | 104 | ## 2016-03-09, Version v1.0.0 105 | ### Commits 106 | - [[`b256517aef`](https://github.com/mafintosh/flat-tree-rs/commit/b256517aefb4ce2092e2920931d4c78cca6590af)] add badge (Mathias Buus) 107 | - [[`017436cbae`](https://github.com/mafintosh/flat-tree-rs/commit/017436cbaec00ef57b7e8311a5f674fd0cf418e4)] add desc (Mathias Buus) 108 | - [[`388d2445bc`](https://github.com/mafintosh/flat-tree-rs/commit/388d2445bc567148867a79f315aeee3ca492b100)] first commit (Mathias Buus) 109 | 110 | ### Stats 111 | ```diff 112 | Cargo.toml | 2 +- 113 | rustfmt.toml | 2 +- 114 | src/lib.rs | 328 ++++++++++++++++++++++++++---------------------------------- 115 | 3 files changed, 144 insertions(+), 188 deletions(-) 116 | ``` 117 | 118 | 119 | ## 2016-03-09, Version v1.0.0 120 | ### Commits 121 | - [[`b256517aef`](https://github.com/mafintosh/flat-tree-rs/commit/b256517aefb4ce2092e2920931d4c78cca6590af)] add badge (Mathias Buus) 122 | - [[`017436cbae`](https://github.com/mafintosh/flat-tree-rs/commit/017436cbaec00ef57b7e8311a5f674fd0cf418e4)] add desc (Mathias Buus) 123 | - [[`388d2445bc`](https://github.com/mafintosh/flat-tree-rs/commit/388d2445bc567148867a79f315aeee3ca492b100)] first commit (Mathias Buus) 124 | 125 | ### Stats 126 | ```diff 127 | Cargo.toml | 2 +- 128 | rustfmt.toml | 2 +- 129 | src/lib.rs | 328 ++++++++++++++++++++++++++---------------------------------- 130 | 3 files changed, 144 insertions(+), 188 deletions(-) 131 | ``` 132 | 133 | 134 | ## 2016-03-09, Version v1.0.0 135 | ### Commits 136 | - [[`b256517aef`](https://github.com/mafintosh/flat-tree-rs/commit/b256517aefb4ce2092e2920931d4c78cca6590af)] add badge (Mathias Buus) 137 | - [[`017436cbae`](https://github.com/mafintosh/flat-tree-rs/commit/017436cbaec00ef57b7e8311a5f674fd0cf418e4)] add desc (Mathias Buus) 138 | - [[`388d2445bc`](https://github.com/mafintosh/flat-tree-rs/commit/388d2445bc567148867a79f315aeee3ca492b100)] first commit (Mathias Buus) 139 | 140 | ### Stats 141 | ```diff 142 | Cargo.toml | 2 +- 143 | rustfmt.toml | 2 +- 144 | src/lib.rs | 328 ++++++++++++++++++++++++++---------------------------------- 145 | 3 files changed, 144 insertions(+), 188 deletions(-) 146 | ``` 147 | 148 | 149 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2018 Yoshua Wuyts 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(missing_docs)] 2 | #![cfg_attr(test, deny(warnings))] 3 | #![doc(test(attr(deny(warnings))))] 4 | //! # Series of functions to map a binary tree to a list 5 | //! 6 | //! You can represent a binary tree in a simple flat list using the following 7 | //! structure: 8 | //! 9 | //! ```text 10 | //! 15 11 | //! 7 23 12 | //! 3 11 19 27 13 | //! 1 5 9 13 17 21 25 29 14 | //! 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30... 15 | //! ``` 16 | //! 17 | //! Each number represents an **index** in a flat list. So a tree: 18 | //! 19 | //! ```text 20 | //! A 21 | //! B C 22 | //! D E F G ... 23 | //! ``` 24 | //! 25 | //! would be represented as a list: `[D B E A F C G]` 26 | //! 27 | //! Furthermore, indexes `0`, `2`, `4`, `6` are on **depth** `0`. `1`, `5`, `9` on depth `1`. And so forth. 28 | //! 29 | //! ```text 30 | //! depth = 2 ^ 3 31 | //! depth = 1 | 1 5 32 | //! depth = 0 | 0 2 4 6 ... 33 | //! ``` 34 | //! 35 | //! In some cases it is also useful to calculate an **offset**. Indexes `0`, `1`, `3`, `7` have an offset `0`: 36 | //! 37 | //! ```text 38 | //! (7) 39 | //! (3) 40 | //! (1) 5 41 | //! (0) 2 4 6 ... 42 | //! ``` 43 | //! 44 | //! `2`, `5`, `11`, `23` offset `1`: 45 | //! 46 | //! ```text 47 | //! 7 48 | //! 3 (11) 49 | //! 1 (5) 9 13 50 | //! 0 (2) 4 6 8 10 12 14 51 | //! ``` 52 | //! 53 | //! This module exposes a series of functions to help you build and maintain 54 | //! this data structure. 55 | 56 | mod iterator; 57 | 58 | pub use iterator::Iterator; 59 | 60 | /// Returns the flat-tree of the tree node at the specified depth and offset. 61 | /// 62 | /// ## Examples 63 | /// ```rust 64 | /// assert_eq!(flat_tree::index(0, 0), 0); 65 | /// assert_eq!(flat_tree::index(0, 1), 2); 66 | /// assert_eq!(flat_tree::index(0, 2), 4); 67 | /// assert_eq!(flat_tree::index(1, 2), 9); 68 | /// assert_eq!(flat_tree::index(1, 3), 13); 69 | /// assert_eq!(flat_tree::index(2, 1), 11); 70 | /// assert_eq!(flat_tree::index(2, 2), 19); 71 | /// assert_eq!(flat_tree::index(3, 0), 7); 72 | /// assert_eq!(flat_tree::index(3, 1), 23); 73 | /// ``` 74 | #[inline] 75 | pub const fn index(depth: u64, offset: u64) -> u64 { 76 | (offset << (depth + 1)) | ((1 << depth) - 1) 77 | } 78 | 79 | /// Returns the depth of a node. 80 | /// 81 | /// ## Examples 82 | /// ```rust 83 | /// assert_eq!(flat_tree::depth(0), 0); 84 | /// assert_eq!(flat_tree::depth(1), 1); 85 | /// assert_eq!(flat_tree::depth(2), 0); 86 | /// assert_eq!(flat_tree::depth(3), 2); 87 | /// assert_eq!(flat_tree::depth(4), 0); 88 | /// ``` 89 | #[inline] 90 | pub const fn depth(i: u64) -> u64 { 91 | // Count trailing `1`s of the binary representation of the number. 92 | (!i).trailing_zeros() as u64 93 | } 94 | 95 | /// Returns the offset of a node. 96 | /// 97 | /// ## Examples 98 | /// ```rust 99 | /// assert_eq!(flat_tree::offset(0), 0); 100 | /// assert_eq!(flat_tree::offset(1), 0); 101 | /// assert_eq!(flat_tree::offset(2), 1); 102 | /// assert_eq!(flat_tree::offset(3), 0); 103 | /// assert_eq!(flat_tree::offset(4), 2); 104 | /// ``` 105 | #[inline] 106 | pub fn offset(i: u64) -> u64 { 107 | let depth = self::depth(i); 108 | if is_even(i) { 109 | i / 2 110 | } else { 111 | i >> (depth + 1) 112 | } 113 | } 114 | 115 | /// Returns the parent of a node with a depth. 116 | /// 117 | /// ## Examples 118 | /// ```rust 119 | /// assert_eq!(flat_tree::parent(0), 1); 120 | /// assert_eq!(flat_tree::parent(1), 3); 121 | /// assert_eq!(flat_tree::parent(2), 1); 122 | /// assert_eq!(flat_tree::parent(3), 7); 123 | /// assert_eq!(flat_tree::parent(4), 5); 124 | /// ``` 125 | #[inline] 126 | pub fn parent(i: u64) -> u64 { 127 | let depth = self::depth(i); 128 | index(depth + 1, offset(i) >> 1) 129 | } 130 | 131 | /// Returns the sibling of a node. 132 | /// 133 | /// ## Examples 134 | /// ```rust 135 | /// assert_eq!(flat_tree::sibling(0), 2); 136 | /// assert_eq!(flat_tree::sibling(2), 0); 137 | /// assert_eq!(flat_tree::sibling(1), 5); 138 | /// assert_eq!(flat_tree::sibling(5), 1); 139 | /// ``` 140 | #[inline] 141 | pub fn sibling(i: u64) -> u64 { 142 | let depth = self::depth(i); 143 | index(depth, offset(i) ^ 1) 144 | } 145 | 146 | /// Returns the parent's sibling, of a node. 147 | /// 148 | /// ## Examples 149 | /// ```rust 150 | /// assert_eq!(flat_tree::uncle(0), 5); 151 | /// assert_eq!(flat_tree::uncle(2), 5); 152 | /// assert_eq!(flat_tree::uncle(1), 11); 153 | /// assert_eq!(flat_tree::uncle(5), 11); 154 | /// ``` 155 | #[inline] 156 | pub fn uncle(i: u64) -> u64 { 157 | let depth = self::depth(i); 158 | index(depth + 1, offset(parent(i)) ^ 1) 159 | } 160 | 161 | /// Returns both children of a node. 162 | /// 163 | /// ## Examples 164 | /// ```rust 165 | /// assert_eq!(flat_tree::children(0), None); 166 | /// assert_eq!(flat_tree::children(1), Some((0, 2))); 167 | /// assert_eq!(flat_tree::children(3), Some((1, 5))); 168 | /// assert_eq!(flat_tree::children(9), Some((8, 10))); 169 | /// ``` 170 | #[inline] 171 | pub fn children(i: u64) -> Option<(u64, u64)> { 172 | let depth = self::depth(i); 173 | if is_even(i) { 174 | None 175 | } else if depth == 0 { 176 | Some((i, i)) 177 | } else { 178 | let offset = offset(i) * 2; 179 | Some((index(depth - 1, offset), index(depth - 1, offset + 1))) 180 | } 181 | } 182 | 183 | /// Returns only the left child of a node. 184 | /// 185 | /// ## Examples 186 | /// ```rust 187 | /// assert_eq!(flat_tree::left_child(0), None); 188 | /// assert_eq!(flat_tree::left_child(1), Some(0)); 189 | /// assert_eq!(flat_tree::left_child(3), Some(1)); 190 | /// ``` 191 | #[inline] 192 | pub fn left_child(i: u64) -> Option { 193 | let depth = self::depth(i); 194 | if is_even(i) { 195 | None 196 | } else if depth == 0 { 197 | Some(i) 198 | } else { 199 | Some(index(depth - 1, offset(i) << 1)) 200 | } 201 | } 202 | 203 | /// Returns only the left child of a node. 204 | /// 205 | /// ## Examples 206 | /// ```rust 207 | /// assert_eq!(flat_tree::right_child(0), None); 208 | /// assert_eq!(flat_tree::right_child(1), Some(2)); 209 | /// assert_eq!(flat_tree::right_child(3), Some(5)); 210 | /// ``` 211 | // TODO: handle errors 212 | #[inline] 213 | pub fn right_child(i: u64) -> Option { 214 | let depth = self::depth(i); 215 | if is_even(i) { 216 | None 217 | } else if depth == 0 { 218 | Some(i) 219 | } else { 220 | Some(index(depth - 1, (offset(i) << 1) + 1)) 221 | } 222 | } 223 | 224 | /// Returns the right most node in the tree that the node spans. 225 | /// 226 | /// ## Examples 227 | /// ```rust 228 | /// assert_eq!(flat_tree::right_span(0), 0); 229 | /// assert_eq!(flat_tree::right_span(1), 2); 230 | /// assert_eq!(flat_tree::right_span(3), 6); 231 | /// assert_eq!(flat_tree::right_span(23), 30); 232 | /// assert_eq!(flat_tree::right_span(27), 30); 233 | /// ``` 234 | #[inline] 235 | pub fn right_span(i: u64) -> u64 { 236 | let depth = self::depth(i); 237 | if depth == 0 { 238 | i 239 | } else { 240 | (offset(i) + 1) * (2 << depth) - 2 241 | } 242 | } 243 | 244 | /// Returns the left most node in the tree that it spans. 245 | /// 246 | /// ## Examples 247 | /// ```rust 248 | /// assert_eq!(flat_tree::left_span(0), 0); 249 | /// assert_eq!(flat_tree::left_span(1), 0); 250 | /// assert_eq!(flat_tree::left_span(3), 0); 251 | /// assert_eq!(flat_tree::left_span(23), 16); 252 | /// assert_eq!(flat_tree::left_span(27), 24); 253 | /// ``` 254 | #[inline] 255 | pub fn left_span(i: u64) -> u64 { 256 | let depth = self::depth(i); 257 | if depth == 0 { 258 | i 259 | } else { 260 | offset(i) * (2 << depth) 261 | } 262 | } 263 | 264 | /// Returns the left and right most nodes in the tree that the node spans. 265 | /// 266 | /// ## Examples 267 | /// ```rust 268 | /// assert_eq!(flat_tree::spans(0), (0, 0)); 269 | /// assert_eq!(flat_tree::spans(1), (0, 2)); 270 | /// assert_eq!(flat_tree::spans(3), (0, 6)); 271 | /// assert_eq!(flat_tree::spans(23), (16, 30)); 272 | /// assert_eq!(flat_tree::spans(27), (24, 30)); 273 | /// ``` 274 | #[inline] 275 | pub fn spans(i: u64) -> (u64, u64) { 276 | (left_span(i), right_span(i)) 277 | } 278 | 279 | /// Returns how many nodes are in the tree that the node spans. 280 | /// 281 | /// ## Examples 282 | /// ```rust 283 | /// assert_eq!(flat_tree::count(0), 1); 284 | /// assert_eq!(flat_tree::count(1), 3); 285 | /// assert_eq!(flat_tree::count(3), 7); 286 | /// assert_eq!(flat_tree::count(5), 3); 287 | /// assert_eq!(flat_tree::count(7), 15); 288 | /// assert_eq!(flat_tree::count(15), 31); 289 | /// assert_eq!(flat_tree::count(23), 15); 290 | /// assert_eq!(flat_tree::count(27), 7); 291 | /// ``` 292 | #[inline] 293 | pub const fn count(i: u64) -> u64 { 294 | let depth = self::depth(i); 295 | (2 << depth) - 1 296 | } 297 | 298 | /// Returns how many leaves are in the tree that the node spans. 299 | /// 300 | /// ## Examples 301 | /// ```rust 302 | /// assert_eq!(flat_tree::count_leaves(0), 1); 303 | /// assert_eq!(flat_tree::count_leaves(1), 2); 304 | /// assert_eq!(flat_tree::count_leaves(3), 4); 305 | /// assert_eq!(flat_tree::count_leaves(5), 2); 306 | /// assert_eq!(flat_tree::count_leaves(15), 16); 307 | /// assert_eq!(flat_tree::count_leaves(23), 8); 308 | /// assert_eq!(flat_tree::count_leaves(27), 4); 309 | /// ``` 310 | #[inline] 311 | pub const fn count_leaves(i: u64) -> u64 { 312 | (count(i) + 1) / 2 313 | } 314 | 315 | /// Returns a list of all the full roots (subtrees where all nodes have either 2 316 | /// or 0 children) `<` index. 317 | /// 318 | /// For example `fullRoots(8)` returns `[3]` since the subtree rooted at `3` 319 | /// spans `0 -> 6`, and the tree rooted at `7` has a child located at `9` which 320 | /// is `>= 8`. 321 | /// 322 | /// ## Panics 323 | /// If an uneven index is passed. 324 | /// 325 | /// ## Examples 326 | /// ```rust 327 | /// use flat_tree::full_roots; 328 | /// 329 | /// let mut nodes = Vec::with_capacity(16); 330 | /// full_roots(0, &mut nodes); 331 | /// assert_eq!(nodes, []); 332 | /// 333 | /// let mut nodes = Vec::with_capacity(16); 334 | /// full_roots(2, &mut nodes); 335 | /// assert_eq!(nodes, [0]); 336 | /// 337 | /// let mut nodes = Vec::with_capacity(16); 338 | /// full_roots(8, &mut nodes); 339 | /// assert_eq!(nodes, [3]); 340 | /// 341 | /// let mut nodes = Vec::with_capacity(16); 342 | /// full_roots(20, &mut nodes); 343 | /// assert_eq!(nodes, [7, 17]); 344 | /// 345 | /// let mut nodes = Vec::with_capacity(16); 346 | /// full_roots(18, &mut nodes); 347 | /// assert_eq!(nodes, [7, 16]); 348 | /// 349 | /// let mut nodes = Vec::with_capacity(16); 350 | /// full_roots(16, &mut nodes); 351 | /// assert_eq!(nodes, [7]); 352 | /// 353 | /// let mut nodes = Vec::with_capacity(16); 354 | /// full_roots(30, &mut nodes); 355 | /// assert_eq!(nodes, [7, 19, 25, 28]); 356 | 357 | /// ``` 358 | #[inline] 359 | pub fn full_roots(i: u64, nodes: &mut Vec) { 360 | assert!( 361 | is_even(i), 362 | "You can only look up roots for depth 0 blocks, got index {}", 363 | i 364 | ); 365 | let mut tmp = i >> 1; 366 | let mut offset = 0; 367 | let mut factor = 1; 368 | 369 | loop { 370 | if tmp == 0 { 371 | break; 372 | } 373 | while factor * 2 <= tmp { 374 | factor *= 2; 375 | } 376 | nodes.push(offset + factor - 1); 377 | offset += 2 * factor; 378 | tmp -= factor; 379 | factor = 1; 380 | } 381 | } 382 | 383 | #[inline] 384 | pub(crate) const fn is_even(num: u64) -> bool { 385 | (num & 1) == 0 386 | } 387 | 388 | #[inline] 389 | pub(crate) const fn is_odd(num: u64) -> bool { 390 | (num & 1) != 0 391 | } 392 | 393 | #[cfg(test)] 394 | mod tests { 395 | use super::*; 396 | 397 | #[test] 398 | fn test_is_even() { 399 | assert!(is_even(0)); 400 | assert!(!is_even(1)); 401 | assert!(is_even(2)); 402 | assert!(!is_even(3)); 403 | } 404 | 405 | #[test] 406 | fn test_is_odd() { 407 | assert!(!is_odd(0)); 408 | assert!(is_odd(1)); 409 | assert!(!is_odd(2)); 410 | assert!(is_odd(3)); 411 | } 412 | 413 | #[test] 414 | fn test_parent_gt_int32() { 415 | assert_eq!(parent(10_000_000_000), 10_000_000_001); 416 | } 417 | 418 | #[test] 419 | fn test_child_to_parent_to_child() { 420 | let mut child = 0; 421 | for _ in 0..50 { 422 | child = parent(child) 423 | } 424 | assert_eq!(child, 1_125_899_906_842_623); 425 | for _ in 0..50 { 426 | child = left_child(child).expect("no valid number returned"); 427 | } 428 | assert_eq!(child, 0); 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /src/iterator.rs: -------------------------------------------------------------------------------- 1 | //! ## Usage 2 | //! ```rust 3 | //! let mut iter = flat_tree::Iterator::new(0); 4 | //! assert_eq!(iter.next(), Some(2)); 5 | //! assert_eq!(iter.next(), Some(4)); 6 | //! assert_eq!(iter.next(), Some(6)); 7 | //! assert_eq!(iter.parent(), 5); 8 | //! ``` 9 | use super::*; 10 | 11 | use std::iter; 12 | 13 | /// Iterator over a flat-tree. 14 | #[derive(Debug)] 15 | pub struct Iterator { 16 | index: u64, 17 | offset: u64, 18 | factor: u64, 19 | } 20 | 21 | impl Iterator { 22 | /// Create a new iterator. 23 | /// 24 | /// ## Examples 25 | /// ```rust 26 | /// use flat_tree::Iterator; 27 | /// assert_eq!(Iterator::new(0).take(3).collect::>(), [2, 4, 6]); 28 | /// ``` 29 | #[inline] 30 | pub fn new(index: u64) -> Self { 31 | let mut instance = Self { 32 | index: 0, 33 | offset: 0, 34 | factor: 0, 35 | }; 36 | 37 | instance.seek(index); 38 | instance 39 | } 40 | 41 | /// Get the current index. 42 | #[inline] 43 | pub fn index(&self) -> u64 { 44 | self.index 45 | } 46 | 47 | /// Get the current offset. 48 | #[inline] 49 | pub fn offset(&self) -> u64 { 50 | self.offset 51 | } 52 | 53 | /// Get the current factor. 54 | #[inline] 55 | pub fn factor(&self) -> u64 { 56 | self.factor 57 | } 58 | 59 | /// Seek to a position in the iterator. 60 | /// 61 | /// ## Examples 62 | /// ```rust 63 | /// let mut iter = flat_tree::Iterator::new(0); 64 | /// iter.seek(4); 65 | /// assert_eq!(iter.next(), Some(6)); 66 | /// iter.seek(2); 67 | /// assert_eq!(iter.next(), Some(4)); 68 | /// ``` 69 | #[inline] 70 | pub fn seek(&mut self, index: u64) { 71 | self.index = index; 72 | if is_odd(self.index) { 73 | self.offset = offset(index); 74 | self.factor = two_pow(depth(index) + 1); 75 | } else { 76 | self.offset = index / 2; 77 | self.factor = 2; 78 | } 79 | } 80 | 81 | /// Check if the position of the iterator is currently on a left node. 82 | /// 83 | /// ## Examples 84 | /// ```rust 85 | /// assert_eq!(flat_tree::Iterator::new(0).is_left(), true); 86 | /// assert_eq!(flat_tree::Iterator::new(2).is_left(), false); 87 | /// assert_eq!(flat_tree::Iterator::new(1).is_left(), true); 88 | /// ``` 89 | #[inline] 90 | pub fn is_left(&self) -> bool { 91 | is_even(self.offset) 92 | } 93 | 94 | /// Check if the position of the iterator is currently on a right node. 95 | /// 96 | /// ## Examples 97 | /// ```rust 98 | /// assert_eq!(flat_tree::Iterator::new(0).is_right(), false); 99 | /// assert_eq!(flat_tree::Iterator::new(2).is_right(), true); 100 | /// assert_eq!(flat_tree::Iterator::new(1).is_right(), false); 101 | /// ``` 102 | #[inline] 103 | pub fn is_right(&self) -> bool { 104 | is_odd(self.offset) 105 | } 106 | 107 | /// Check if the the iterator contains given index. 108 | /// 109 | /// ## Examples 110 | /// ```rust 111 | /// let iter = flat_tree::Iterator::new(3); 112 | /// assert_eq!(iter.contains(0), true); 113 | /// assert_eq!(iter.contains(1), true); 114 | /// assert_eq!(iter.contains(2), true); 115 | /// assert_eq!(iter.contains(3), true); 116 | /// assert_eq!(iter.contains(4), true); 117 | /// assert_eq!(iter.contains(5), true); 118 | /// assert_eq!(iter.contains(6), true); 119 | /// assert_eq!(iter.contains(7), false); 120 | /// assert_eq!(iter.contains(8), false); 121 | /// 122 | /// let iter = flat_tree::Iterator::new(9); 123 | /// assert_eq!(iter.contains(8), true); 124 | /// assert_eq!(iter.contains(9), true); 125 | /// assert_eq!(iter.contains(10), true); 126 | /// assert_eq!(iter.contains(6), false); 127 | /// assert_eq!(iter.contains(7), false); 128 | /// assert_eq!(iter.contains(12), false); 129 | /// assert_eq!(iter.contains(13), false); 130 | /// 131 | /// let iter = flat_tree::Iterator::new(8); 132 | /// assert_eq!(iter.contains(8), true); 133 | /// assert_eq!(iter.contains(6), false); 134 | /// assert_eq!(iter.contains(7), false); 135 | /// assert_eq!(iter.contains(9), false); 136 | /// assert_eq!(iter.contains(10), false); 137 | /// ``` 138 | #[inline] 139 | #[allow(clippy::comparison_chain)] 140 | pub fn contains(&self, index: u64) -> bool { 141 | if index > self.index { 142 | index < (self.index + self.factor / 2) 143 | } else if index < self.index { 144 | let comp = self.factor / 2; 145 | self.index < comp || index > (self.index - comp) 146 | } else { 147 | true 148 | } 149 | } 150 | 151 | /// Returns how many nodes are in the tree of the current position. 152 | /// 153 | /// ## Examples 154 | /// ```rust 155 | /// assert_eq!(flat_tree::Iterator::new(0).count_nodes(), 1); 156 | /// assert_eq!(flat_tree::Iterator::new(1).count_nodes(), 3); 157 | /// assert_eq!(flat_tree::Iterator::new(3).count_nodes(), 7); 158 | /// assert_eq!(flat_tree::Iterator::new(5).count_nodes(), 3); 159 | /// assert_eq!(flat_tree::Iterator::new(23).count_nodes(), 15); 160 | /// assert_eq!(flat_tree::Iterator::new(27).count_nodes(), 7); 161 | /// ``` 162 | #[inline] 163 | pub fn count_nodes(&self) -> u64 { 164 | if is_even(self.index) { 165 | 1 166 | } else { 167 | self.factor - 1 168 | } 169 | } 170 | 171 | /// Returns how many leaves are in the tree of the current position. 172 | /// 173 | /// ## Examples 174 | /// ```rust 175 | /// assert_eq!(flat_tree::Iterator::new(0).count_leaves(), 1); 176 | /// assert_eq!(flat_tree::Iterator::new(1).count_leaves(), 2); 177 | /// assert_eq!(flat_tree::Iterator::new(2).count_leaves(), 1); 178 | /// assert_eq!(flat_tree::Iterator::new(3).count_leaves(), 4); 179 | /// assert_eq!(flat_tree::Iterator::new(5).count_leaves(), 2); 180 | /// assert_eq!(flat_tree::Iterator::new(23).count_leaves(), 8); 181 | /// assert_eq!(flat_tree::Iterator::new(27).count_leaves(), 4); 182 | /// ``` 183 | #[inline] 184 | pub fn count_leaves(&self) -> u64 { 185 | (self.count_nodes() + 1) / 2 186 | } 187 | 188 | /// Move the cursor and get the previous item from the current position. 189 | /// 190 | /// ## Examples 191 | /// ```rust 192 | /// let mut iter = flat_tree::Iterator::new(6); 193 | /// assert_eq!(iter.prev(), 4); 194 | /// assert_eq!(iter.prev(), 2); 195 | /// assert_eq!(iter.prev(), 0); 196 | /// ``` 197 | #[inline] 198 | pub fn prev(&mut self) -> u64 { 199 | if self.offset == 0 { 200 | return self.index; 201 | } 202 | self.offset -= 1; 203 | self.index -= self.factor; 204 | self.index 205 | } 206 | 207 | /// Get the sibling for the current position and move the cursor. 208 | /// 209 | /// ## Examples 210 | /// ```rust 211 | /// assert_eq!(flat_tree::Iterator::new(0).sibling(), 2); 212 | /// assert_eq!(flat_tree::Iterator::new(1).sibling(), 5); 213 | /// assert_eq!(flat_tree::Iterator::new(4).sibling(), 6); 214 | /// ``` 215 | #[inline] 216 | pub fn sibling(&mut self) -> u64 { 217 | if self.is_left() { 218 | self.next().unwrap() // this is always safe 219 | } else { 220 | self.prev() 221 | } 222 | } 223 | 224 | /// Get the parent for the current position and move the cursor. 225 | /// 226 | /// ## Examples 227 | /// ```rust 228 | /// assert_eq!(flat_tree::Iterator::new(0).parent(), 1); 229 | /// assert_eq!(flat_tree::Iterator::new(1).parent(), 3); 230 | /// assert_eq!(flat_tree::Iterator::new(4).parent(), 5); 231 | /// ``` 232 | #[inline] 233 | pub fn parent(&mut self) -> u64 { 234 | if is_odd(self.offset) { 235 | self.index -= self.factor / 2; 236 | self.offset = (self.offset - 1) / 2; 237 | } else { 238 | self.index += self.factor / 2; 239 | self.offset /= 2; 240 | } 241 | self.factor *= 2; 242 | self.index 243 | } 244 | 245 | /// Get the left_span for the current position and move the cursor. 246 | /// 247 | /// ## Examples 248 | /// ```rust 249 | /// assert_eq!(flat_tree::Iterator::new(0).left_span(), 0); 250 | /// assert_eq!(flat_tree::Iterator::new(1).left_span(), 0); 251 | /// assert_eq!(flat_tree::Iterator::new(3).left_span(), 0); 252 | /// assert_eq!(flat_tree::Iterator::new(23).left_span(), 16); 253 | /// assert_eq!(flat_tree::Iterator::new(27).left_span(), 24); 254 | /// ``` 255 | #[inline] 256 | pub fn left_span(&mut self) -> u64 { 257 | self.index = self.index + 1 - self.factor / 2; 258 | self.offset = self.index / 2; 259 | self.factor = 2; 260 | self.index 261 | } 262 | 263 | /// Get the right_span for the current position and move the cursor. 264 | /// 265 | /// ## Examples 266 | /// ```rust 267 | /// assert_eq!(flat_tree::Iterator::new(0).right_span(), 0); 268 | /// assert_eq!(flat_tree::Iterator::new(1).right_span(), 2); 269 | /// assert_eq!(flat_tree::Iterator::new(3).right_span(), 6); 270 | /// assert_eq!(flat_tree::Iterator::new(23).right_span(), 30); 271 | /// assert_eq!(flat_tree::Iterator::new(27).right_span(), 30); 272 | /// ``` 273 | #[inline] 274 | pub fn right_span(&mut self) -> u64 { 275 | self.index = self.index + self.factor / 2 - 1; 276 | self.offset = self.index / 2; 277 | self.factor = 2; 278 | self.index 279 | } 280 | 281 | /// Get the left_child for the current position and move the cursor. 282 | /// 283 | /// ## Examples 284 | /// ```rust 285 | /// assert_eq!(flat_tree::Iterator::new(1).left_child(), 0); 286 | /// assert_eq!(flat_tree::Iterator::new(3).left_child(), 1); 287 | /// assert_eq!(flat_tree::Iterator::new(7).left_child(), 3); 288 | /// ``` 289 | #[inline] 290 | pub fn left_child(&mut self) -> u64 { 291 | if self.factor == 2 { 292 | return self.index; 293 | } 294 | self.factor /= 2; 295 | self.index -= self.factor / 2; 296 | self.offset *= 2; 297 | self.index 298 | } 299 | 300 | /// Get the right_child for the current position and move the cursor. 301 | /// 302 | /// ## Examples 303 | /// ```rust 304 | /// assert_eq!(flat_tree::Iterator::new(1).right_child(), 2); 305 | /// assert_eq!(flat_tree::Iterator::new(3).right_child(), 5); 306 | /// assert_eq!(flat_tree::Iterator::new(7).right_child(), 11); 307 | /// ``` 308 | #[inline] 309 | pub fn right_child(&mut self) -> u64 { 310 | if self.factor == 2 { 311 | return self.index; 312 | } 313 | self.factor /= 2; 314 | self.index += self.factor / 2; 315 | self.offset = 2 * self.offset + 1; 316 | self.index 317 | } 318 | 319 | /// Move to the next tree from the current position and return 320 | /// new index. 321 | /// 322 | /// ## Examples 323 | /// ```rust 324 | /// assert_eq!(flat_tree::Iterator::new(0).next_tree(), 2); 325 | /// assert_eq!(flat_tree::Iterator::new(1).next_tree(), 4); 326 | /// assert_eq!(flat_tree::Iterator::new(3).next_tree(), 8); 327 | /// assert_eq!(flat_tree::Iterator::new(7).next_tree(), 16); 328 | /// ``` 329 | #[inline] 330 | pub fn next_tree(&mut self) -> u64 { 331 | self.index = self.index + self.factor / 2 + 1; 332 | self.offset = self.index / 2; 333 | self.factor = 2; 334 | self.index 335 | } 336 | 337 | /// Move to the previous tree from the current position and return 338 | /// new index. 339 | /// 340 | /// ## Examples 341 | /// ```rust 342 | /// assert_eq!(flat_tree::Iterator::new(0).prev_tree(), 0); 343 | /// assert_eq!(flat_tree::Iterator::new(1).prev_tree(), 0); 344 | /// assert_eq!(flat_tree::Iterator::new(2).prev_tree(), 0); 345 | /// assert_eq!(flat_tree::Iterator::new(3).prev_tree(), 0); 346 | /// assert_eq!(flat_tree::Iterator::new(7).prev_tree(), 0); 347 | /// assert_eq!(flat_tree::Iterator::new(5).prev_tree(), 2); 348 | /// assert_eq!(flat_tree::Iterator::new(9).prev_tree(), 6); 349 | /// assert_eq!(flat_tree::Iterator::new(11).prev_tree(), 6); 350 | /// ``` 351 | #[inline] 352 | pub fn prev_tree(&mut self) -> u64 { 353 | if self.offset == 0 { 354 | self.index = 0; 355 | self.factor = 2; 356 | } else { 357 | self.index = self.index - self.factor / 2 - 1; 358 | self.offset = self.index / 2; 359 | self.factor = 2; 360 | } 361 | self.index 362 | } 363 | 364 | /// Move cursor to the full root of given leaf index that the iterator 365 | /// index is a part of. If the cursor is already there, nothing is 366 | /// changed. 367 | /// 368 | /// Returns true if a full root exists (moved or not), false if 369 | /// there are no full roots for the cursor or if given index is not a 370 | /// leaf. 371 | /// 372 | /// See full_roots() for what is meant by a full root. 373 | /// 374 | /// ## Examples 375 | /// ```rust 376 | /// let mut iter = flat_tree::Iterator::new(0); 377 | /// assert_eq!(iter.full_root(0), false); 378 | /// assert_eq!(iter.full_root(22), true); 379 | /// assert_eq!(iter.index(), 7); 380 | /// assert_eq!(iter.next_tree(), 16); 381 | /// assert_eq!(iter.full_root(22), true); 382 | /// assert_eq!(iter.index(), 17); 383 | /// assert_eq!(iter.next_tree(), 20); 384 | /// assert_eq!(iter.full_root(22), true); 385 | /// assert_eq!(iter.index(), 20); 386 | /// assert_eq!(iter.next_tree(), 22); 387 | /// assert_eq!(iter.full_root(22), false); 388 | /// ``` 389 | #[inline] 390 | pub fn full_root(&mut self, index: u64) -> bool { 391 | if index <= self.index || is_odd(self.index) { 392 | return false; 393 | } 394 | while index > self.index + self.factor + self.factor / 2 { 395 | self.index += self.factor / 2; 396 | self.factor *= 2; 397 | self.offset /= 2; 398 | } 399 | true 400 | } 401 | } 402 | 403 | impl iter::Iterator for Iterator { 404 | type Item = u64; 405 | 406 | #[inline] 407 | fn next(&mut self) -> Option { 408 | self.offset += 1; 409 | self.index += self.factor; 410 | Some(self.index) 411 | } 412 | } 413 | 414 | impl Default for Iterator { 415 | #[inline] 416 | fn default() -> Self { 417 | Self::new(0) 418 | } 419 | } 420 | 421 | #[inline] 422 | fn two_pow(n: u64) -> u64 { 423 | if n < 31 { 424 | 1 << n 425 | } else { 426 | (1 << 30) * (1 << (n - 30)) 427 | } 428 | } 429 | 430 | #[cfg(test)] 431 | mod tests { 432 | use super::*; 433 | 434 | #[test] 435 | fn test_two_pow() { 436 | assert_eq!(two_pow(0), 1); 437 | assert_eq!(two_pow(1), 2); 438 | assert_eq!(two_pow(2), 4); 439 | assert_eq!(two_pow(3), 8); 440 | assert_eq!(two_pow(31), 2147483648); 441 | } 442 | 443 | #[cfg(target_pointer_width = "64")] 444 | #[test] 445 | fn test_two_pow_64bit() { 446 | assert_eq!(two_pow(34), 17179869184); 447 | assert_eq!(two_pow(63), 9223372036854775808); 448 | } 449 | } 450 | --------------------------------------------------------------------------------