{"id":16937007,"url":"https://github.com/ripytide/nodit","last_synced_at":"2025-03-04T03:13:34.232Z","repository":{"id":64646996,"uuid":"571051583","full_name":"ripytide/nodit","owner":"ripytide","description":"This crate provides Discrete Interval Tree Data-Structures, which are based off BTreeMap","archived":false,"fork":false,"pushed_at":"2024-09-02T09:24:11.000Z","size":700,"stargazers_count":42,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-25T02:15:19.760Z","etag":null,"topics":["data-structures","intervals","library","rust"],"latest_commit_sha":null,"homepage":"https://docs.rs/nodit","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ripytide.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-27T01:46:20.000Z","updated_at":"2025-02-20T02:15:47.000Z","dependencies_parsed_at":"2023-07-14T08:56:43.326Z","dependency_job_id":"9dbd60e8-aebf-4e92-88ea-61a367bfbe30","html_url":"https://github.com/ripytide/nodit","commit_stats":null,"previous_names":["ripytide/range_bounds_map","ripytide/nodit","ripytide/discrete_range_map"],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripytide%2Fnodit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripytide%2Fnodit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripytide%2Fnodit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripytide%2Fnodit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ripytide","download_url":"https://codeload.github.com/ripytide/nodit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241387924,"owners_count":19955089,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-structures","intervals","library","rust"],"created_at":"2024-10-13T20:58:26.991Z","updated_at":"2025-03-04T03:13:34.204Z","avatar_url":"https://github.com/ripytide.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nodit\n\n![License](https://img.shields.io/github/license/ripytide/nodit)\n[![Docs](https://docs.rs/nodit/badge.svg)](https://docs.rs/nodit)\n[![Maintained](https://img.shields.io/maintenance/yes/2024)](https://github.com/ripytide)\n[![Crates.io](https://img.shields.io/crates/v/nodit)](https://crates.io/crates/nodit)\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"logo.png\" alt=\"nodit_logo\" width=\"350\"\u003e\n\u003c/p\u003e\n\nThis crate provides Discrete Interval Tree Data-Structures, which are based\noff [`BTreeMap`].\n\n`no_std` is supported and should work with the default features.\n\nSeveral Discrete Interval Tree data-structures have been implemented, here\nis a brief summary of each of them and why you might use them:\n\n| Struct|Abbreviation|Use-Case|\n|-----|------|------|\n|[`NoditMap`]|Non-Overlapping Discrete Interval Tree Map| General purpose way of associating data with intervals that do not overlap|\n|[`NoditSet`]|Non-Overlapping Discrete Interval Tree Set| Useful for when you want to store intervals but don't want/need to associate data with each interval|\n|[`ZosditMap`]|Zero-Overlap Sequential Discrete Interval Tree Map| Useful for time-graph traversal algorithms and possibly other things|\n|[`Gqdit`]|Gap-Query Discrete Interval Tree| Useful for when you have a set of different non-overlapping intervals and want to perform efficient gap-query searches over all the sets of intervals|\n\n## `Copy` is partially required\n\nDue to implementation complications with non-`Copy` types the\ndata-structures currently require both the interval type and the points the\nintervals are over to be `Copy`. However, the value type used when using\nthe [`NoditMap`] does not have to be `Copy`. In fact the only required\ntraits on the value type are sometimes `Clone` or `Eq` but only for some\nmethods so if in doubt check a methods trait bounds.\n\n## Example using an Inclusive-Exclusive interval\n\n```rust\nuse nodit::interval::ie;\nuse nodit::NoditMap;\n\nlet mut map = NoditMap::new();\n\nmap.insert_strict(ie(0, 5), true);\nmap.insert_strict(ie(5, 10), false);\n\nassert_eq!(map.overlaps(ie(-2, 12)), true);\nassert_eq!(map.contains_point(20), false);\nassert_eq!(map.contains_point(5), true);\n```\n\n## Example using a custom interval type\n\n```rust\nuse std::ops::{Bound, RangeBounds};\n\nuse nodit::interval::ie;\nuse nodit::{\n\tDiscreteFinite, InclusiveInterval, Interval, NoditMap,\n};\n\n#[derive(Debug, Copy, Clone)]\nenum Reservation {\n\t// Start, End (Inclusive-Inclusive)\n\tFinite(i8, i8),\n\t// Start (Inclusive-Infinity)\n\tInfinite(i8),\n}\n\n// First, we need to implement InclusiveInterval\nimpl InclusiveInterval\u003ci8\u003e for Reservation {\n\tfn start(\u0026self) -\u003e i8 {\n\t\tmatch self {\n\t\t\tReservation::Finite(start, _) =\u003e *start,\n\t\t\tReservation::Infinite(start) =\u003e *start,\n\t\t}\n\t}\n\tfn end(\u0026self) -\u003e i8 {\n\t\tmatch self {\n\t\t\tReservation::Finite(_, end) =\u003e *end,\n\t\t\tReservation::Infinite(_) =\u003e i8::MAX,\n\t\t}\n\t}\n}\n\n// Second, we need to implement From\u003cInterval\u003ci8\u003e\u003e\nimpl From\u003cInterval\u003ci8\u003e\u003e for Reservation {\n\tfn from(value: Interval\u003ci8\u003e) -\u003e Self {\n\t\tif value.end() == i8::MAX {\n\t\t\tReservation::Infinite(value.start())\n\t\t} else {\n\t\t\tReservation::Finite(\n\t\t\t\tvalue.start(),\n\t\t\t\tvalue.end().up().unwrap(),\n\t\t\t)\n\t\t}\n\t}\n}\n\n// Next we can create a custom typed NoditMap\nlet reservation_map = NoditMap::from_slice_strict([\n\t(Reservation::Finite(10, 20), \"Ferris\".to_string()),\n\t(Reservation::Infinite(21), \"Corro\".to_string()),\n])\n.unwrap();\n\nfor (reservation, name) in reservation_map.overlapping(ie(16, 17))\n{\n\tprintln!(\n\t\t\"{name} has reserved {reservation:?} inside the interval 16..17\"\n\t);\n}\n\nfor (reservation, name) in reservation_map.iter() {\n\tprintln!(\"{name} has reserved {reservation:?}\");\n}\n\nassert_eq!(\n\treservation_map.overlaps(Reservation::Infinite(0)),\n\ttrue\n);\n```\n\n## Key Understandings and Philosophies\n\n### Discrete-ness\n\nThis crate is designed to work with [`Discrete`] types as compared to\n[`Continuous`] types. For example, `u8` is a `Discrete` type, but\n`String` is a `Continuous` if you try to parse it as a decimal value.\n\nThe reason for this is that common [`interval-Mathematics`] operations\ndiffer depending on whether the underlying type is `Discrete` or\n`Continuous`. For example `5..=6` touches `7..=8` since integers are\n`Discrete` but `5.0..=6.0` does **not** touch `7.0..=8.0` since the\nvalue `6.5` exists.\n\nImportantly, this also makes Inclusive/Exclusive ended intervals really\neasy to work with as they can be losslessly converted between one\nanother. For example, `3..6` is equivalent to `3..=5`.\n\n### Finite-ness\n\nAt the moment this crate is also designed to work only with [`Finite`]\ntypes such as `u8` or `i128`, but not with `Infinite` types such as\n[`BigInt`] from the [`num_bigint`] crate. This is because the\n[`get_key_value_at_point()`] method would not be able to return anything\nfrom an empty map if the type was an infinite type such as `BigInt`\nsince it has no maximum value.\n\nA handy trick you can use to pretend to have infinite types when you\ndon't expect to reach to top end of your type is to use [`Actual Infinity`] to pretend you have an `Infinity`. For example, if you were\nusing `u8` as your point type then you could create a wrapper type such\nas this:\n\n```rust\nuse std::cmp::Ordering;\n\nuse nodit::DiscreteFinite;\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum WithInfinity\u003cT\u003e {\n\tFinite(T),\n\tInfinity,\n}\n\nimpl\u003cT\u003e Ord for WithInfinity\u003cT\u003e\nwhere\n\tT: Ord,\n{\n\tfn cmp(\u0026self, other: \u0026Self) -\u003e Ordering {\n\t\tmatch (self, other) {\n\t\t\t(\n\t\t\t\tWithInfinity::Finite(x),\n\t\t\t\tWithInfinity::Finite(y),\n\t\t\t) =\u003e x.cmp(y),\n\t\t\t(WithInfinity::Finite(_), WithInfinity::Infinity) =\u003e {\n\t\t\t\tOrdering::Less\n\t\t\t}\n\t\t\t(WithInfinity::Infinity, WithInfinity::Finite(_)) =\u003e {\n\t\t\t\tOrdering::Greater\n\t\t\t}\n\t\t\t(WithInfinity::Infinity, WithInfinity::Infinity) =\u003e {\n\t\t\t\tOrdering::Equal\n\t\t\t}\n\t\t}\n\t}\n}\n\nimpl\u003cT\u003e PartialOrd for WithInfinity\u003cT\u003e\nwhere\n\tT: Ord,\n{\n\tfn partial_cmp(\u0026self, other: \u0026Self) -\u003e Option\u003cOrdering\u003e {\n\t\tSome(self.cmp(other))\n\t}\n}\n\nimpl\u003cT\u003e DiscreteFinite for WithInfinity\u003cT\u003e\nwhere\n\tT: DiscreteFinite,\n{\n\tconst MIN: Self = WithInfinity::Finite(T::MIN);\n\tconst MAX: Self = WithInfinity::Infinity;\n\n\tfn up(self) -\u003e Option\u003cSelf\u003e\n\twhere\n\t\tSelf: Sized,\n\t{\n\t\tmatch self {\n\t\t\tWithInfinity::Finite(x) =\u003e match x.up() {\n\t\t\t\tSome(y) =\u003e Some(WithInfinity::Finite(y)),\n\t\t\t\tNone =\u003e Some(WithInfinity::Infinity),\n\t\t\t},\n\t\t\tWithInfinity::Infinity =\u003e None,\n\t\t}\n\t}\n\tfn down(self) -\u003e Option\u003cSelf\u003e\n\twhere\n\t\tSelf: Sized,\n\t{\n\t\tmatch self {\n\t\t\tWithInfinity::Finite(x) =\u003e {\n\t\t\t\tSome(WithInfinity::Finite(x.down()?))\n\t\t\t}\n\t\t\tWithInfinity::Infinity =\u003e {\n\t\t\t\tSome(WithInfinity::Finite(T::MAX))\n\t\t\t}\n\t\t}\n\t}\n}\n\n// And then you this means you can be explicit with when\n// Infinity is encountered such as when it might be\n// returned by `get_key_value_at_point()`, for example:\n\nuse nodit::interval::uu;\nuse nodit::{Interval, NoditMap};\n\nlet map: NoditMap\u003c\n\tWithInfinity\u003cu8\u003e,\n\tInterval\u003cWithInfinity\u003cu8\u003e\u003e,\n\tbool,\n\u003e = NoditMap::new();\n\nlet mut gap = map.get_key_value_at_point(WithInfinity::Finite(4));\n\nassert_eq!(gap, Err(uu()));\n```\n\n### Invalid Intervals\n\nWithin this crate, not all intervals are considered valid intervals. The\ndefinition of the validity of a interval used within this crate is that a\ninterval is only valid if it contains at least one value of the underlying\ndomain.\n\nFor example, `4..6` is considered valid as it contains the values `4`\nand `5`, however, `4..4` is considered invalid as it contains no\nvalues. Another example of invalid interval are those whose start values\nare greater than their end values. such as `5..2` or `100..=40`.\n\nHere are a few examples of intervals and whether they are valid:\n\n| interval                                  | valid |\n| -------------------------------------- | ----- |\n| 0..=0                                  | YES   |\n| 0..0                                   | NO    |\n| 0..1                                   | YES   |\n| 9..8                                   | NO    |\n| (Bound::Excluded(3), Bound::Excluded(4)) | NO    |\n| 400..=400                              | YES   |\n\n### Overlap\n\nTwo intervals are \"overlapping\" if there exists a point that is contained\nwithin both intervals. For example, `2..4` and `2..6` overlap but `2..4`\nand `4..8` do not.\n\n### Touching\n\nTwo intervals are \"touching\" if they do not overlap and there exists no\nvalue between them. For example, `2..4` and `4..6` are touching but\n`2..4` and `6..8` are not, neither are `2..6` and `4..8`.\n\n### Further Reading\n\nSee Wikipedia's article on mathematical Intervals:\n\u003chttps://en.wikipedia.org/wiki/Interval_(mathematics)\u003e\n\n## Features\n\n|Feature Name| Description|\n|-----------|-----|\n|`default`|The implicit default feature enabled by default which currently does not activate any other features|\n|`serde`|Enables the optional `serde` dependency and implements `serde::Serialize` and `serde::Deserialize` on all the types in this crate|\n\n## Credit\n\nLots of my inspiration came from the [`rangemap`] crate.\n\nThe BTreeMap implementation ([`btree_monstrousity`]) used under the\nhood was inspired and forked from the [`copse`] crate.\n\n## Name Changes\n\nThis crate was previously named [`range_bounds_map`] it was renamed around about 2023-04-24 to\n[`discrete_range_map`] due to it no longer being an accurate name.\n\nThis crate was renamed again on 2023-01-02 from [`discrete_range_map`] to [`nodit`] for a\nsimilar reason, hopefully given the abstractness of the new name it will never need to change\nagain.\n\n## Similar Crates\n\nHere are some relevant crates I found whilst searching around the\ntopic area, beware my biases when reading:\n\n- \u003chttps://docs.rs/rangemap\u003e\n  Very similar to this crate but can only use std [`Range`]s and\n  [`RangeInclusive`]s as keys in it's `map` and `set` structs (separately).\n- \u003chttps://docs.rs/btree-range-map\u003e\n- \u003chttps://docs.rs/ranges\u003e\n  Cool library for fully-generic ranges (unlike std::ops ranges), along\n  with a `Ranges` data-structure for storing them (Vec-based\n  unfortunately)\n- \u003chttps://docs.rs/intervaltree\u003e\n  Allows overlapping intervals but is immutable unfortunately\n- \u003chttps://docs.rs/nonoverlapping_interval_tree\u003e\n  Very similar to `rangemap` except without a `gaps()` function and only\n  for [`Range`]s and not [`RangeInclusive`]s. And also no fancy\n  merging functions.\n- \u003chttps://docs.rs/unbounded-interval-tree\u003e\n  A data structure based off of a 2007 published paper! It supports\n  any range as keys, unfortunately, it is implemented with a\n  non-balancing `Box\u003cNode\u003e` based tree, however it also supports\n  overlapping ranges which my library does not.\n- \u003chttps://docs.rs/rangetree\u003e\n  I'm not entirely sure what this library is or isn't, but it looks like\n  a custom red-black tree/BTree implementation used specifically for a\n  Range Tree. Interesting but also quite old (5 years) and uses\n  unsafe.\n- \u003chttps://docs.rs/rust-lapper\u003e\n  Another sort-of immutable (can insert but its very expensive)\n  interval data-structure optimised for lots of intervals of the same\n  size such as their staple use-case of genomic datasets.\n- \u003chttps://docs.rs/store-interval-tree\u003e\n  An interval tree very similar to this crate and `rangemap` with many\n  of the same methods (and lots of doc examples!) except using a custom\n  in-house self-balancing tree implementation. It is not exactly clear\n  from my reading of the docs whether they support overlapping intervals\n  or not. On the one hand their examples show overlapping intervals but\n  then their `insert()` method says \"if interval already exists,\n  interval will be ignored\", so perhaps it allows overlapping but not\n  duplicate intervals? A bit of an odd choice in my opinion.\n- \u003chttps://docs.rs/bio\u003e and \u003chttps://docs.rs/rudac\u003e\n  Both essentially identical to `store-interval-tree` as it looks like\n  `store-interval-tree` is a fork of `rudac`'s interval tree. `bio` in\n  particular seems targeted at bio-infographics.\n\n[`actual infinity`]: https://en.wikipedia.org/wiki/Actual_infinity\n[`bigint`]: https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html\n[`btreemap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html\n[`btree_monstrousity`]: https://github.com/ripytide/btree_monstrousity\n[`continuous`]: https://en.wikipedia.org/wiki/List_of_continuity-related_mathematical_topics\n[`copse`]: https://github.com/eggyal/copse\n[`discrete_range_map`]: https://docs.rs/discrete_range_map\n[`discrete`]: https://en.wikipedia.org/wiki/Discrete_mathematics\n[`finite`]: https://en.wiktionary.org/wiki/finite#Adjective\n[`get_key_value_at_point()`]: https://docs.rs/nodit/latest/nodit/nodit/map/struct.NoditMap.html#method.get_key_value_at_point\n[`gqdit`]: https://docs.rs/nodit/latest/nodit/gqdit/struct.Gqdit.html\n[`interval-mathematics`]: https://en.wikipedia.org/wiki/Interval_(mathematics)\n[`noditmap`]: https://docs.rs/nodit/latest/nodit/nodit/map/struct.NoditMap.html\n[`noditset`]: https://docs.rs/nodit/latest/nodit/nodit/set/struct.NoditSet.html\n[`nodit`]: https://docs.rs/nodit\n[`num_bigint`]: https://docs.rs/num-bigint\n[`rangeinclusive`]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html\n[`rangemap`]: https://docs.rs/rangemap/latest/rangemap/\n[`range_bounds_map`]: https://docs.rs/range_bounds_map\n[`range`]: https://doc.rust-lang.org/std/ops/struct.Range.html\n[`zosditmap`]: https://docs.rs/nodit/latest/nodit/zosdit/map/struct.ZosditMap.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fripytide%2Fnodit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fripytide%2Fnodit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fripytide%2Fnodit/lists"}