From e179fcd884e1cb4e26f7627aa2f1a0b91d18e4ca Mon Sep 17 00:00:00 2001 From: feathercyc Date: Mon, 27 May 2024 18:56:11 +0800 Subject: [PATCH] feat: add some doc Signed-off-by: feathercyc --- Cargo.lock | 648 +++++++++++++++++++++++++ Cargo.toml | 23 + README.md | 36 ++ benches/bench.rs | 113 +++++ src/entry.rs | 97 ++++ src/index.rs | 64 +++ src/interval.rs | 76 +++ src/intervalmap.rs | 1141 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 32 ++ src/node.rs | 124 +++++ 10 files changed, 2354 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 benches/bench.rs create mode 100644 src/entry.rs create mode 100644 src/index.rs create mode 100644 src/interval.rs create mode 100644 src/intervalmap.rs create mode 100644 src/lib.rs create mode 100644 src/node.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..78c1808 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,648 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "interval_map" +version = "0.1.0" +dependencies = [ + "criterion", + "rand", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8a89c0e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "interval_map" +version = "0.1.0" +edition = "2021" +authors = ["feathercyc feathercyc@163.com"] +description = "`interval_map` is a map based on interval tree." +license = "Apache-2.0" +keywords = ["Interval Tree", "Augmented Tree", "Red-Black Tree"] + +[dependencies] + +[dev-dependencies] +criterion = "0.5.1" +rand = "0.8.5" + +[features] +default = [] +interval_tree_find_overlap_ordered = [] + +[[bench]] +name = "bench" +path = "benches/bench.rs" +harness = false diff --git a/README.md b/README.md new file mode 100644 index 0000000..0fdb9b5 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# interval_map + +`interval_map` is a map based on interval tree. It fully implements the insertion and deletion functionality of a red-black tree, ensuring that each modification operation requires at most $O(logN)$ time complexity. + +The implementation of the interval tree in interval_map references "Introduction to Algorithms" (3rd ed., Section 14.3: Interval trees, pp. 348–354). + +To safely and efficiently handle insertion and deletion operations in Rust, `interval_map` innovatively **uses arrays to simulate pointers** for managing the parent-child references in the red-black tree. This approach also ensures that interval_map has the `Send` and `Unpin` traits, allowing it to be safely transferred between threads and to maintain a fixed memory location during asynchronous operations. + +`interval_map` implements an `IntervalMap` struct: +- It accepts `Interval` as the key, where `T` can be any type that implements `Ord+Clone` trait. Therefore, intervals such as $[1, 2)$ and $["aaa", "bbb")$ are allowed +- The value can be of any type + +`interval_map` supports `insert`, `delete`, and `iter` fns. Traversal is performed in the order of `Interval` . For instance, with intervals of type `Interval`: +- $[1,4)<[2,5)$, because $1<2$ +- $[1,4)<[1,5)$, because $4<5$ + +So the order of intervals in `IntervalMap` is $[1,4)<[1,5)<[2,5)$. + +Currently, `interval_map` only supports half-open intervals, i.e., $[...,...)$. + +## Benchmark + +The benchmark was conducted on a platform with `AMD R7 7840H + DDR5 5600MHz`. The result are as follows: +1. Only insert + | insert | 100 | 1000 | 10, 000 | 100, 000 | + | --------------- | --------- | --------- | --------- | --------- | + | Time per insert | 5.4168 µs | 80.518 µs | 2.2823 ms | 36.528 ms | +2. Insert N and remove N + | insert_and_remove | 100 | 1000 | 10, 000 | 100, 000 | + | ------------------ | --------- | --------- | --------- | --------- | + | Time per operation | 10.333 µs | 223.43 µs | 4.9358 ms | 81.634 ms | + +## TODO +- [] Support for $(...,...)$, $[...,...]$ and $(...,...]$ interval types. +- [] Add more tests like [etcd](https://github.com/etcd-io/etcd/blob/main/pkg/adt/interval_tree_test.go) +- [] Add Point type for Interval diff --git a/benches/bench.rs b/benches/bench.rs new file mode 100644 index 0000000..f7f7435 --- /dev/null +++ b/benches/bench.rs @@ -0,0 +1,113 @@ +use criterion::{criterion_group, criterion_main, Bencher, Criterion}; +use interval_map::{Interval, IntervalMap}; +use std::hint::black_box; + +struct Rng { + state: u32, +} +impl Rng { + fn new() -> Self { + Self { state: 0x87654321 } + } + + fn gen_u32(&mut self) -> u32 { + self.state ^= self.state << 13; + self.state ^= self.state >> 17; + self.state ^= self.state << 5; + self.state + } + + fn gen_range_i32(&mut self, low: i32, high: i32) -> i32 { + let d = (high - low) as u32; + low + (self.gen_u32() % d) as i32 + } +} + +struct IntervalGenerator { + rng: Rng, + limit: i32, +} +impl IntervalGenerator { + fn new() -> Self { + const LIMIT: i32 = 100000; + Self { + rng: Rng::new(), + limit: LIMIT, + } + } + + fn next(&mut self) -> Interval { + let low = self.rng.gen_range_i32(0, self.limit - 1); + let high = self.rng.gen_range_i32(low + 1, self.limit); + Interval::new(low, high) + } +} + +// insert helper fn +fn interval_map_insert(count: usize, bench: &mut Bencher) { + let mut gen = IntervalGenerator::new(); + let intervals: Vec<_> = std::iter::repeat_with(|| gen.next()).take(count).collect(); + bench.iter(|| { + let mut map = IntervalMap::new(); + for i in intervals.clone() { + black_box(map.insert(i, ())); + } + }); +} + +// insert and remove helper fn +fn interval_map_insert_remove(count: usize, bench: &mut Bencher) { + let mut gen = IntervalGenerator::new(); + let intervals: Vec<_> = std::iter::repeat_with(|| gen.next()).take(count).collect(); + bench.iter(|| { + let mut map = IntervalMap::new(); + for i in intervals.clone() { + black_box(map.insert(i, ())); + } + for i in &intervals { + black_box(map.remove(&i)); + } + }); +} + +fn bench_interval_map_insert(c: &mut Criterion) { + c.bench_function("bench_interval_map_insert_100", |b| { + interval_map_insert(100, b) + }); + c.bench_function("bench_interval_map_insert_1000", |b| { + interval_map_insert(1000, b) + }); + c.bench_function("bench_interval_map_insert_10,000", |b| { + interval_map_insert(10_000, b) + }); + c.bench_function("bench_interval_map_insert_100,000", |b| { + interval_map_insert(100_000, b) + }); +} + +fn bench_interval_map_insert_remove(c: &mut Criterion) { + c.bench_function("bench_interval_map_insert_remove_100", |b| { + interval_map_insert_remove(100, b) + }); + c.bench_function("bench_interval_map_insert_remove_1000", |b| { + interval_map_insert_remove(1000, b) + }); + c.bench_function("bench_interval_map_insert_remove_10,000", |b| { + interval_map_insert_remove(10_000, b) + }); + c.bench_function("bench_interval_map_insert_remove_100,000", |b| { + interval_map_insert_remove(100_000, b) + }); +} + +fn criterion_config() -> Criterion { + Criterion::default().configure_from_args().without_plots() +} + +criterion_group! { + name = benches; + config = criterion_config(); + targets = bench_interval_map_insert, bench_interval_map_insert_remove +} + +criterion_main!(benches); diff --git a/src/entry.rs b/src/entry.rs new file mode 100644 index 0000000..731a55a --- /dev/null +++ b/src/entry.rs @@ -0,0 +1,97 @@ +use crate::index::{IndexType, NodeIndex}; +use crate::interval::Interval; +use crate::intervalmap::IntervalMap; +use crate::node::Node; + +/// A view into a single entry in a map, which may either be vacant or occupied. +#[derive(Debug)] +pub enum Entry<'a, T, V, Ix> { + /// An occupied entry. + Occupied(OccupiedEntry<'a, T, V, Ix>), + /// A vacant entry. + Vacant(VacantEntry<'a, T, V, Ix>), +} + +/// A view into an occupied entry in a `IntervalMap`. +/// It is part of the [`Entry`] enum. +#[derive(Debug)] +pub struct OccupiedEntry<'a, T, V, Ix> { + /// Reference to the map + pub map_ref: &'a mut IntervalMap, + /// The entry node + pub node: NodeIndex, +} + +/// A view into a vacant entry in a `IntervalMap`. +/// It is part of the [`Entry`] enum. +#[derive(Debug)] +pub struct VacantEntry<'a, T, V, Ix> { + /// Mutable reference to the map + pub map_ref: &'a mut IntervalMap, + /// The interval of this entry + pub interval: Interval, +} + +impl<'a, T, V, Ix> Entry<'a, T, V, Ix> +where + T: Ord, + Ix: IndexType, +{ + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// a mutable reference to the value in the entry. + /// + /// # Example + /// ```rust + /// use interval_map::{Interval, IntervalMap, Entry}; + /// + /// let mut map = IntervalMap::new(); + /// assert!(matches!(map.entry(Interval::new(1, 2)), Entry::Vacant(_))); + /// map.entry(Interval::new(1, 2)).or_insert(3); + /// assert!(matches!(map.entry(Interval::new(1, 2)), Entry::Occupied(_))); + /// assert_eq!(map.get(&Interval::new(1, 2)), Some(&3)); + /// ``` + #[inline] + pub fn or_insert(self, default: V) -> &'a mut V { + match self { + Entry::Occupied(entry) => entry.map_ref.node_mut(entry.node, Node::value_mut), + Entry::Vacant(entry) => { + let entry_idx = NodeIndex::new(entry.map_ref.nodes.len()); + let _ignore = entry.map_ref.insert(entry.interval, default); + entry.map_ref.node_mut(entry_idx, Node::value_mut) + } + } + } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Panics + /// + /// This method panics when the node is a sentinel node + /// + /// # Example + /// ```rust + /// use interval_map::{Interval, IntervalMap, Entry}; + /// + /// let mut map = IntervalMap::new(); + /// + /// map.insert(Interval::new(6, 7), 3); + /// assert!(matches!(map.entry(Interval::new(6, 7)), Entry::Occupied(_))); + /// map.entry(Interval::new(6, 7)).and_modify(|v| *v += 1); + /// assert_eq!(map.get(&Interval::new(6, 7)), Some(&4)); + /// ``` + #[inline] + #[must_use] + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut V), + { + match self { + Entry::Occupied(entry) => { + f(entry.map_ref.node_mut(entry.node, Node::value_mut)); + Self::Occupied(entry) + } + Entry::Vacant(entry) => Self::Vacant(entry), + } + } +} diff --git a/src/index.rs b/src/index.rs new file mode 100644 index 0000000..657f955 --- /dev/null +++ b/src/index.rs @@ -0,0 +1,64 @@ +use std::fmt; +use std::hash::Hash; + +pub type DefaultIx = u32; + +pub unsafe trait IndexType: Copy + Default + Hash + Ord + fmt::Debug + 'static { + fn new(x: usize) -> Self; + fn index(&self) -> usize; + fn max() -> Self; +} + +unsafe impl IndexType for u32 { + #[inline(always)] + fn new(x: usize) -> Self { + x as u32 + } + #[inline(always)] + fn index(&self) -> usize { + *self as usize + } + #[inline(always)] + fn max() -> Self { + ::std::u32::MAX + } +} + +/// Node identifier. +#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)] +pub struct NodeIndex(Ix); + +impl NodeIndex { + #[inline] + pub fn new(x: usize) -> Self { + NodeIndex(IndexType::new(x)) + } + + #[inline] + pub fn index(self) -> usize { + self.0.index() + } + + #[inline] + pub fn end() -> Self { + NodeIndex(IndexType::max()) + } +} + +unsafe impl IndexType for NodeIndex { + fn index(&self) -> usize { + self.0.index() + } + fn new(x: usize) -> Self { + NodeIndex::new(x) + } + fn max() -> Self { + NodeIndex(::max()) + } +} + +impl fmt::Debug for NodeIndex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "NodeIndex({:?})", self.0) + } +} diff --git a/src/interval.rs b/src/interval.rs new file mode 100644 index 0000000..e7d78bc --- /dev/null +++ b/src/interval.rs @@ -0,0 +1,76 @@ +//! The `Interval` stored in `IntervalMap` and represents the interval [low, high) +//! +//! `interval_map` supports `insert`, `delete`, and `iter` fns. Traversal is performed in the order of `Interval` . For instance, with intervals of type `Interval`: +//! - [1,4)<[2,5), because 1<2 +//! - [1,4)<[1,5), because 4<5 +//! +//! So the order of intervals in `IntervalMap` is [1,4)<[1,5)<[2,5). +//! +//! Currently, `interval_map` only supports half-open intervals, i.e., [...,...). + +/// The interval stored in `IntervalMap` represents [low, high) +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub struct Interval { + /// Low value + pub low: T, + /// high value + pub high: T, +} + +impl Interval { + /// Create a new `Interval` + /// + /// # Panics + /// + /// This method panics when low >= high + #[inline] + pub fn new(low: T, high: T) -> Self { + assert!(low < high, "invalid range"); + Self { low, high } + } + + /// Checks if self overlaps with other interval + #[inline] + pub fn overlap(&self, other: &Self) -> bool { + self.high > other.low && other.high > self.low + } +} + +/// Reference type of `Interval` +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct IntervalRef<'a, T> { + /// Low value + low: &'a T, + /// high value + high: &'a T, +} + +impl<'a, T: Ord> IntervalRef<'a, T> { + /// Create a new `IntervalRef` + /// + /// # Panics + /// + /// This method panics when low >= high + #[inline] + pub fn new(low: &'a T, high: &'a T) -> Self { + assert!(low < high, "invalid range"); + Self { low, high } + } + + /// Check if self overlaps with a `Interval` + pub fn overlap(&self, other: &Interval) -> bool { + self.high > &other.low && &other.high > self.low + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[should_panic(expected = "invalid range")] + fn invalid_range_should_panic() { + let _interval = Interval::new(3, 1); + } +} diff --git a/src/intervalmap.rs b/src/intervalmap.rs new file mode 100644 index 0000000..51dcb24 --- /dev/null +++ b/src/intervalmap.rs @@ -0,0 +1,1141 @@ +use crate::entry::{Entry, OccupiedEntry, VacantEntry}; +use crate::index::{DefaultIx, IndexType, NodeIndex}; +use crate::interval::{Interval, IntervalRef}; +use crate::node::{Color, Node}; +use std::collections::VecDeque; + +/// An interval-value map, which support operations on dynamic sets of intervals. +#[derive(Debug)] +pub struct IntervalMap { + /// Vector that stores nodes + pub(crate) nodes: Vec>, + /// Root of the interval tree + pub(crate) root: NodeIndex, + /// Number of elements in the map + pub(crate) len: usize, +} + +impl IntervalMap +where + T: Ord, + Ix: IndexType, +{ + /// Creates a new `IntervalMap` with estimated capacity. + #[inline] + #[must_use] + pub fn with_capacity(capacity: usize) -> Self { + let mut nodes = vec![Self::new_sentinel()]; + nodes.reserve(capacity); + IntervalMap { + nodes, + root: Self::sentinel(), + len: 0, + } + } + + /// Insert an interval-value pair into the map. + /// If the interval exists, overwrite and return the previous value. + /// + /// # Panics + /// + /// This method panics when the tree is at the maximum number of nodes for its index + /// + /// # Example + /// ```rust + /// use interval_map::{Interval, IntervalMap}; + /// + /// let mut map = IntervalMap::new(); + /// assert_eq!(map.insert(Interval::new(1, 3), 1), None); + /// assert_eq!(map.insert(Interval::new(1, 3), 2), Some(1)); + /// assert_eq!(map.insert(Interval::new(1, 3), 3), Some(2)); + /// ``` + #[inline] + pub fn insert(&mut self, interval: Interval, value: V) -> Option { + let node_idx = NodeIndex::new(self.nodes.len()); + let node = Self::new_node(interval, value, node_idx); + // check for max capacity, except if we use usize + assert!( + ::max().index() == !0 || NodeIndex::end() != node_idx, + "Reached maximum number of nodes" + ); + self.nodes.push(node); + self.insert_inner(node_idx) + } + + /// Remove an interval from the map, returning the value at the interval if the interval exists + /// + /// # Example + /// ```rust + /// use interval_map::{Interval, IntervalMap}; + /// + /// let mut map = IntervalMap::new(); + /// map.insert(Interval::new(1, 3), 1); + /// map.insert(Interval::new(2, 4), 2); + /// assert_eq!(map.len(), 2); + /// assert_eq!(map.remove(&Interval::new(3, 6)), None); + /// assert_eq!(map.len(), 2); + /// assert_eq!(map.remove(&Interval::new(2, 4)), Some(2)); + /// assert_eq!(map.len(), 1); + /// ``` + #[inline] + pub fn remove(&mut self, interval: &Interval) -> Option { + if let Some(node_idx) = self.search_exact(interval) { + self.remove_inner(node_idx); + // Swap the node with the last node stored in the vector and update indices + let mut node = self.nodes.swap_remove(node_idx.index()); + let old = NodeIndex::::new(self.nodes.len()); + self.update_idx(old, node_idx); + + return node.value.take(); + } + None + } + + /// Check if an interval in the map overlaps with the given interval. + /// + /// # Example + /// ```rust + /// use interval_map::{Interval, IntervalMap}; + /// + /// let mut map = IntervalMap::new(); + /// map.insert(Interval::new(1, 3), ()); + /// map.insert(Interval::new(6, 7), ()); + /// map.insert(Interval::new(9, 11), ()); + /// assert!(map.overlap(&Interval::new(2, 5))); + /// assert!(map.overlap(&Interval::new(1, 17))); + /// assert!(!map.overlap(&Interval::new(3, 6))); + /// assert!(!map.overlap(&Interval::new(11, 23))); + /// ``` + #[inline] + pub fn overlap(&self, interval: &Interval) -> bool { + let node_idx = self.search(interval); + !self.node_ref(node_idx, Node::is_sentinel) + } + + /// Find all intervals in the map that overlaps with the given interval. + /// + /// # Example + /// ```rust + /// use interval_map::{Interval, IntervalMap}; + /// + /// let mut map = IntervalMap::new(); + /// map.insert(Interval::new(1, 3), ()); + /// map.insert(Interval::new(2, 4), ()); + /// map.insert(Interval::new(6, 7), ()); + /// map.insert(Interval::new(7, 11), ()); + /// assert_eq!(map.find_all_overlap(&Interval::new(2, 7)).len(), 3); + /// map.remove(&Interval::new(1, 3)); + /// assert_eq!(map.find_all_overlap(&Interval::new(2, 7)).len(), 2); + /// ``` + #[inline] + pub fn find_all_overlap(&self, interval: &Interval) -> Vec<(&Interval, &V)> { + if self.node_ref(self.root, Node::is_sentinel) { + Vec::new() + } else { + self.find_all_overlap_inner_unordered(self.root, interval) + } + } + + /// Return reference to the value corresponding to the key. + /// + /// # Example + /// ```rust + /// use interval_map::{Interval, IntervalMap}; + /// + /// let mut map = IntervalMap::new(); + /// map.insert(Interval::new(1, 3), 1); + /// map.insert(Interval::new(7, 11), 4); + /// assert_eq!(map.get(&Interval::new(1, 3)), Some(&1)); + /// assert_eq!(map.get(&Interval::new(7, 11)), Some(&4)); + /// assert_eq!(map.get(&Interval::new(5, 17)), None); + /// ``` + #[inline] + pub fn get(&self, interval: &Interval) -> Option<&V> { + self.search_exact(interval) + .map(|idx| self.node_ref(idx, Node::value)) + } + + /// Return a reference to the value corresponding to the key. + /// + /// # Example + /// ```rust + /// use interval_map::{Interval, IntervalMap}; + /// + /// let mut map = IntervalMap::new(); + /// map.insert(Interval::new(3, 5), 0); + /// map.get_mut(&Interval::new(3, 5)).map(|v| *v += 1); + /// assert_eq!(map.get(&Interval::new(3, 5)), Some(&1)); + /// ``` + #[inline] + pub fn get_mut(&mut self, interval: &Interval) -> Option<&mut V> { + self.search_exact(interval) + .map(|idx| self.node_mut(idx, Node::value_mut)) + } + + /// Get an iterator over the entries of the map, sorted by key. + #[inline] + #[must_use] + pub fn iter(&self) -> Iter<'_, T, V, Ix> { + Iter { + map_ref: self, + stack: None, + } + } + + /// Get the given key's corresponding entry in the map for in-place manipulation. + /// + /// # Example + /// ```rust + /// use interval_map::{Interval, IntervalMap, Entry}; + /// + /// let mut map = IntervalMap::new(); + /// + /// assert!(matches!(map.entry(Interval::new(1, 2)), Entry::Vacant(_))); + /// map.entry(Interval::new(1, 2)).or_insert(0); + /// assert!(matches!(map.entry(Interval::new(1, 2)), Entry::Occupied(_))); + /// map.entry(Interval::new(1, 2)).and_modify(|v| *v += 1); + /// assert_eq!(map.get(&Interval::new(1, 2)), Some(&1)); + /// ``` + #[inline] + pub fn entry(&mut self, interval: Interval) -> Entry<'_, T, V, Ix> { + match self.search_exact(&interval) { + Some(node) => Entry::Occupied(OccupiedEntry { + map_ref: self, + node, + }), + None => Entry::Vacant(VacantEntry { + map_ref: self, + interval, + }), + } + } + + /// Remove all elements from the map + #[inline] + pub fn clear(&mut self) { + self.nodes.clear(); + self.nodes.push(Self::new_sentinel()); + self.root = Self::sentinel(); + self.len = 0; + } + + /// Return the number of elements in the map. + #[inline] + #[must_use] + pub fn len(&self) -> usize { + self.len + } + + /// Return `true` if the map contains no elements. + #[inline] + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl IntervalMap +where + T: Ord, +{ + /// Create an empty `IntervalMap` + #[inline] + #[must_use] + pub fn new() -> Self { + Self { + nodes: vec![Self::new_sentinel()], + root: Self::sentinel(), + len: 0, + } + } +} + +impl Default for IntervalMap +where + T: Ord, +{ + #[inline] + fn default() -> Self { + Self::with_capacity(0) + } +} + +impl IntervalMap +where + T: Ord, + Ix: IndexType, +{ + /// Create a new sentinel node + fn new_sentinel() -> Node { + Node { + interval: None, + value: None, + max_index: None, + left: None, + right: None, + parent: None, + color: Color::Black, + } + } + + /// Create a new tree node + fn new_node(interval: Interval, value: V, index: NodeIndex) -> Node { + Node { + max_index: Some(index), + interval: Some(interval), + value: Some(value), + left: Some(Self::sentinel()), + right: Some(Self::sentinel()), + parent: Some(Self::sentinel()), + color: Color::Red, + } + } + + /// Get the sentinel node index + fn sentinel() -> NodeIndex { + NodeIndex::new(0) + } +} + +impl IntervalMap +where + T: Ord, + Ix: IndexType, +{ + /// Insert a node into the tree. + fn insert_inner(&mut self, z: NodeIndex) -> Option { + let mut y = Self::sentinel(); + let mut x = self.root; + + while !self.node_ref(x, Node::is_sentinel) { + y = x; + if self.node_ref(z, Node::interval) == self.node_ref(y, Node::interval) { + let zval = self.node_mut(z, Node::take_value); + let old_value = self.node_mut(y, Node::set_value(zval)); + return Some(old_value); + } + if self.node_ref(z, Node::interval) < self.node_ref(x, Node::interval) { + x = self.node_ref(x, Node::left); + } else { + x = self.node_ref(x, Node::right); + } + } + self.node_mut(z, Node::set_parent(y)); + if self.node_ref(y, Node::is_sentinel) { + self.root = z; + } else { + if self.node_ref(z, Node::interval) < self.node_ref(y, Node::interval) { + self.node_mut(y, Node::set_left(z)); + } else { + self.node_mut(y, Node::set_right(z)); + } + self.update_max_bottom_up(y); + } + self.node_mut(z, Node::set_color(Color::Red)); + + self.insert_fixup(z); + + self.len = self.len.wrapping_add(1); + None + } + + /// Remove a node from the tree. + fn remove_inner(&mut self, z: NodeIndex) { + let mut y = z; + let mut y_orig_color = self.node_ref(y, Node::color); + let x; + if self.left_ref(z, Node::is_sentinel) { + x = self.node_ref(z, Node::right); + self.transplant(z, x); + self.update_max_bottom_up(self.node_ref(z, Node::parent)); + } else if self.right_ref(z, Node::is_sentinel) { + x = self.node_ref(z, Node::left); + self.transplant(z, x); + self.update_max_bottom_up(self.node_ref(z, Node::parent)); + } else { + y = self.tree_minimum(self.node_ref(z, Node::right)); + let mut p = y; + y_orig_color = self.node_ref(y, Node::color); + x = self.node_ref(y, Node::right); + if self.node_ref(y, Node::parent) == z { + self.node_mut(x, Node::set_parent(y)); + } else { + self.transplant(y, x); + p = self.node_ref(y, Node::parent); + self.node_mut(y, Node::set_right(self.node_ref(z, Node::right))); + self.right_mut(y, Node::set_parent(y)); + } + self.transplant(z, y); + self.node_mut(y, Node::set_left(self.node_ref(z, Node::left))); + self.left_mut(y, Node::set_parent(y)); + self.node_mut(y, Node::set_color(self.node_ref(z, Node::color))); + + self.update_max_bottom_up(p); + } + + if matches!(y_orig_color, Color::Black) { + self.remove_fixup(x); + } + + self.len = self.len.wrapping_sub(1); + } + + /// Find all intervals in the map that overlaps with the given interval. + #[cfg(interval_tree_find_overlap_ordered)] + fn find_all_overlap_inner( + &self, + x: NodeIndex, + interval: &Interval, + ) -> Vec<(&Interval, &V)> { + let mut list = vec![]; + if self.node_ref(x, Node::interval).overlap(interval) { + list.push(self.node_ref(x, |nx| (nx.interval(), nx.value()))); + } + if self.max(self.node_ref(x, Node::left)) >= Some(&interval.low) { + list.extend(self.find_all_overlap_inner(self.node_ref(x, Node::left), interval)); + } + if self + .max(self.node_ref(x, Node::right)) + .map(|rmax| IntervalRef::new(&self.node_ref(x, Node::interval).low, rmax)) + .is_some_and(|i| i.overlap(interval)) + { + list.extend(self.find_all_overlap_inner(self.node_ref(x, Node::right), interval)); + } + list + } + + /// Find all intervals in the map that overlaps with the given interval. + /// + /// The result is unordered because of breadth-first search to save stack size + fn find_all_overlap_inner_unordered( + &self, + x: NodeIndex, + interval: &Interval, + ) -> Vec<(&Interval, &V)> { + let mut list = Vec::new(); + let mut queue = VecDeque::new(); + queue.push_back(x); + while let Some(p) = queue.pop_front() { + if self.node_ref(p, Node::interval).overlap(interval) { + list.push(self.node_ref(p, |np| (np.interval(), np.value()))); + } + let p_left = self.node_ref(p, Node::left); + let p_right = self.node_ref(p, Node::right); + if self.max(p_left) >= Some(&interval.low) { + queue.push_back(p_left); + } + if self + .max(self.node_ref(p, Node::right)) + .map(|rmax| IntervalRef::new(&self.node_ref(p, Node::interval).low, rmax)) + .is_some_and(|i| i.overlap(interval)) + { + queue.push_back(p_right); + } + } + + list + } + + /// Search for an interval that overlaps with the given interval. + fn search(&self, interval: &Interval) -> NodeIndex { + let mut x = self.root; + while self + .node_ref(x, Node::sentinel) + .map(Node::interval) + .is_some_and(|xi| !xi.overlap(interval)) + { + if self.max(self.node_ref(x, Node::left)) > Some(&interval.low) { + x = self.node_ref(x, Node::left); + } else { + x = self.node_ref(x, Node::right); + } + } + x + } + + /// Search for the node with exact the given interval + fn search_exact(&self, interval: &Interval) -> Option> { + let mut x = self.root; + while !self.node_ref(x, Node::is_sentinel) { + if self.node_ref(x, Node::interval) == interval { + return Some(x); + } + if self.max(x) < Some(&interval.high) { + return None; + } + if self.node_ref(x, Node::interval) > interval { + x = self.node_ref(x, Node::left); + } else { + x = self.node_ref(x, Node::right); + } + } + None + } + + /// Restore red-black tree properties after an insert. + fn insert_fixup(&mut self, mut z: NodeIndex) { + while self.parent_ref(z, Node::is_red) { + if self.grand_parent_ref(z, Node::is_sentinel) { + break; + } + if self.is_left_child(self.node_ref(z, Node::parent)) { + let y = self.grand_parent_ref(z, Node::right); + if self.node_ref(y, Node::is_red) { + self.parent_mut(z, Node::set_color(Color::Black)); + self.node_mut(y, Node::set_color(Color::Black)); + self.grand_parent_mut(z, Node::set_color(Color::Red)); + z = self.parent_ref(z, Node::parent); + } else { + if self.is_right_child(z) { + z = self.node_ref(z, Node::parent); + self.left_rotate(z); + } + self.parent_mut(z, Node::set_color(Color::Black)); + self.grand_parent_mut(z, Node::set_color(Color::Red)); + self.right_rotate(self.parent_ref(z, Node::parent)); + } + } else { + let y = self.grand_parent_ref(z, Node::left); + if self.node_ref(y, Node::is_red) { + self.parent_mut(z, Node::set_color(Color::Black)); + self.node_mut(y, Node::set_color(Color::Black)); + self.grand_parent_mut(z, Node::set_color(Color::Red)); + z = self.parent_ref(z, Node::parent); + } else { + if self.is_left_child(z) { + z = self.node_ref(z, Node::parent); + self.right_rotate(z); + } + self.parent_mut(z, Node::set_color(Color::Black)); + self.grand_parent_mut(z, Node::set_color(Color::Red)); + self.left_rotate(self.parent_ref(z, Node::parent)); + } + } + } + self.node_mut(self.root, Node::set_color(Color::Black)); + } + + /// Restore red-black tree properties after a remove. + fn remove_fixup(&mut self, mut x: NodeIndex) { + while x != self.root && self.node_ref(x, Node::is_black) { + let mut w; + if self.is_left_child(x) { + w = self.parent_ref(x, Node::right); + if self.node_ref(w, Node::is_red) { + self.node_mut(w, Node::set_color(Color::Black)); + self.parent_mut(x, Node::set_color(Color::Red)); + self.left_rotate(self.node_ref(x, Node::parent)); + w = self.parent_ref(x, Node::right); + } + if self.node_ref(w, Node::is_sentinel) { + break; + } + if self.left_ref(w, Node::is_black) && self.right_ref(w, Node::is_black) { + self.node_mut(w, Node::set_color(Color::Red)); + x = self.node_ref(x, Node::parent); + } else { + if self.right_ref(w, Node::is_black) { + self.left_mut(w, Node::set_color(Color::Black)); + self.node_mut(w, Node::set_color(Color::Red)); + self.right_rotate(w); + w = self.parent_ref(x, Node::right); + } + self.node_mut(w, Node::set_color(self.parent_ref(x, Node::color))); + self.parent_mut(x, Node::set_color(Color::Black)); + self.right_mut(w, Node::set_color(Color::Black)); + self.left_rotate(self.node_ref(x, Node::parent)); + x = self.root; + } + } else { + w = self.parent_ref(x, Node::left); + if self.node_ref(w, Node::is_red) { + self.node_mut(w, Node::set_color(Color::Black)); + self.parent_mut(x, Node::set_color(Color::Red)); + self.right_rotate(self.node_ref(x, Node::parent)); + w = self.parent_ref(x, Node::left); + } + if self.node_ref(w, Node::is_sentinel) { + break; + } + if self.right_ref(w, Node::is_black) && self.left_ref(w, Node::is_black) { + self.node_mut(w, Node::set_color(Color::Red)); + x = self.node_ref(x, Node::parent); + } else { + if self.left_ref(w, Node::is_black) { + self.right_mut(w, Node::set_color(Color::Black)); + self.node_mut(w, Node::set_color(Color::Red)); + self.left_rotate(w); + w = self.parent_ref(x, Node::left); + } + self.node_mut(w, Node::set_color(self.parent_ref(x, Node::color))); + self.parent_mut(x, Node::set_color(Color::Black)); + self.left_mut(w, Node::set_color(Color::Black)); + self.right_rotate(self.node_ref(x, Node::parent)); + x = self.root; + } + } + } + self.node_mut(x, Node::set_color(Color::Black)); + } + + /// Binary tree left rotate. + fn left_rotate(&mut self, x: NodeIndex) { + if self.right_ref(x, Node::is_sentinel) { + return; + } + let y = self.node_ref(x, Node::right); + self.node_mut(x, Node::set_right(self.node_ref(y, Node::left))); + if !self.left_ref(y, Node::is_sentinel) { + self.left_mut(y, Node::set_parent(x)); + } + + self.replace_parent(x, y); + self.node_mut(y, Node::set_left(x)); + + self.rotate_update_max(x, y); + } + + /// Binary tree right rotate. + fn right_rotate(&mut self, x: NodeIndex) { + if self.left_ref(x, Node::is_sentinel) { + return; + } + let y = self.node_ref(x, Node::left); + self.node_mut(x, Node::set_left(self.node_ref(y, Node::right))); + if !self.right_ref(y, Node::is_sentinel) { + self.right_mut(y, Node::set_parent(x)); + } + + self.replace_parent(x, y); + self.node_mut(y, Node::set_right(x)); + + self.rotate_update_max(x, y); + } + + /// Replace parent during a rotation. + fn replace_parent(&mut self, x: NodeIndex, y: NodeIndex) { + self.node_mut(y, Node::set_parent(self.node_ref(x, Node::parent))); + if self.parent_ref(x, Node::is_sentinel) { + self.root = y; + } else if self.is_left_child(x) { + self.parent_mut(x, Node::set_left(y)); + } else { + self.parent_mut(x, Node::set_right(y)); + } + self.node_mut(x, Node::set_parent(y)); + } + + /// Update the max value after a rotation. + fn rotate_update_max(&mut self, x: NodeIndex, y: NodeIndex) { + self.node_mut(y, Node::set_max_index(self.node_ref(x, Node::max_index))); + self.recaculate_max(x); + } + + /// Update the max value towards the root + fn update_max_bottom_up(&mut self, x: NodeIndex) { + let mut p = x; + while !self.node_ref(p, Node::is_sentinel) { + self.recaculate_max(p); + p = self.node_ref(p, Node::parent); + } + } + + /// Recaculate max value from left and right childrens + fn recaculate_max(&mut self, x: NodeIndex) { + self.node_mut(x, Node::set_max_index(x)); + let x_left = self.node_ref(x, Node::left); + let x_right = self.node_ref(x, Node::right); + if self.max(x_left) > self.max(x) { + self.node_mut( + x, + Node::set_max_index(self.node_ref(x_left, Node::max_index)), + ); + } + if self.max(x_right) > self.max(x) { + self.node_mut( + x, + Node::set_max_index(self.node_ref(x_right, Node::max_index)), + ); + } + } + + /// Find the node with the minimum interval. + fn tree_minimum(&self, mut x: NodeIndex) -> NodeIndex { + while !self.left_ref(x, Node::is_sentinel) { + x = self.node_ref(x, Node::left); + } + x + } + + /// Replace one subtree as a child of its parent with another subtree. + fn transplant(&mut self, u: NodeIndex, v: NodeIndex) { + if self.parent_ref(u, Node::is_sentinel) { + self.root = v; + } else if self.is_left_child(u) { + self.parent_mut(u, Node::set_left(v)); + } else { + self.parent_mut(u, Node::set_right(v)); + } + self.node_mut(v, Node::set_parent(self.node_ref(u, Node::parent))); + } + + /// Check if a node is a left child of its parent. + fn is_left_child(&self, node: NodeIndex) -> bool { + self.parent_ref(node, Node::left) == node + } + + /// Check if a node is a right child of its parent. + fn is_right_child(&self, node: NodeIndex) -> bool { + self.parent_ref(node, Node::right) == node + } + + /// Update nodes indices after remove + /// + /// This method has a time complexity of `O(logn)`, as we need to + /// update the max index from bottom to top. + fn update_idx(&mut self, old: NodeIndex, new: NodeIndex) { + if self.root == old { + self.root = new; + } + if self.nodes.get(new.index()).is_some() { + if !self.parent_ref(new, Node::is_sentinel) { + if self.parent_ref(new, Node::left) == old { + self.parent_mut(new, Node::set_left(new)); + } else { + self.parent_mut(new, Node::set_right(new)); + } + } + self.left_mut(new, Node::set_parent(new)); + self.right_mut(new, Node::set_parent(new)); + + let mut p = new; + while !self.node_ref(p, Node::is_sentinel) { + if self.node_ref(p, Node::max_index) == old { + self.node_mut(p, Node::set_max_index(new)); + } + p = self.node_ref(p, Node::parent); + } + } + } +} + +// Convenient methods for reference or mutate current/parent/left/right node +impl<'a, T, V, Ix> IntervalMap +where + Ix: IndexType, +{ + fn node_ref(&'a self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a Node) -> R, + { + op(&self.nodes[node.index()]) + } + + pub(crate) fn node_mut(&'a mut self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a mut Node) -> R, + { + op(&mut self.nodes[node.index()]) + } + + fn left_ref(&'a self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a Node) -> R, + { + let idx = self.nodes[node.index()].left().index(); + op(&self.nodes[idx]) + } + + fn right_ref(&'a self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a Node) -> R, + { + let idx = self.nodes[node.index()].right().index(); + op(&self.nodes[idx]) + } + + fn parent_ref(&'a self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a Node) -> R, + { + let idx = self.nodes[node.index()].parent().index(); + op(&self.nodes[idx]) + } + + fn grand_parent_ref(&'a self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a Node) -> R, + { + let parent_idx = self.nodes[node.index()].parent().index(); + let grand_parent_idx = self.nodes[parent_idx].parent().index(); + op(&self.nodes[grand_parent_idx]) + } + + fn left_mut(&'a mut self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a mut Node) -> R, + { + let idx = self.nodes[node.index()].left().index(); + op(&mut self.nodes[idx]) + } + + fn right_mut(&'a mut self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a mut Node) -> R, + { + let idx = self.nodes[node.index()].right().index(); + op(&mut self.nodes[idx]) + } + + fn parent_mut(&'a mut self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a mut Node) -> R, + { + let idx = self.nodes[node.index()].parent().index(); + op(&mut self.nodes[idx]) + } + + fn grand_parent_mut(&'a mut self, node: NodeIndex, op: F) -> R + where + R: 'a, + F: FnOnce(&'a mut Node) -> R, + { + let parent_idx = self.nodes[node.index()].parent().index(); + let grand_parent_idx = self.nodes[parent_idx].parent().index(); + op(&mut self.nodes[grand_parent_idx]) + } + + fn max(&self, node: NodeIndex) -> Option<&T> { + let max_index = self.nodes[node.index()].max_index?.index(); + self.nodes[max_index].interval.as_ref().map(|i| &i.high) + } +} + +/// An iterator over the entries of a `IntervalMap`. +#[derive(Debug)] +pub struct Iter<'a, T, V, Ix> { + /// Reference to the map + map_ref: &'a IntervalMap, + /// Stack for iteration + stack: Option>>, +} + +impl Iter<'_, T, V, Ix> +where + Ix: IndexType, +{ + /// Initializes the stack + fn init_stack(&mut self) { + self.stack = Some(Self::left_link(self.map_ref, self.map_ref.root)); + } + + /// Pushes a link of nodes on the left to stack. + fn left_link(map_ref: &IntervalMap, mut x: NodeIndex) -> Vec> { + let mut nodes = vec![]; + while !map_ref.node_ref(x, Node::is_sentinel) { + nodes.push(x); + x = map_ref.node_ref(x, Node::left); + } + nodes + } +} + +impl<'a, T, V, Ix> Iterator for Iter<'a, T, V, Ix> +where + Ix: IndexType, +{ + type Item = (&'a Interval, &'a V); + + #[inline] + fn next(&mut self) -> Option { + if self.stack.is_none() { + self.init_stack(); + } + let stack = self.stack.as_mut().unwrap(); + if stack.is_empty() { + return None; + } + let x = stack.pop().unwrap(); + stack.extend(Self::left_link( + self.map_ref, + self.map_ref.node_ref(x, Node::right), + )); + Some(self.map_ref.node_ref(x, |xn| (xn.interval(), xn.value()))) + } +} + +#[cfg(test)] +mod test { + use std::collections::HashSet; + + use rand::{rngs::StdRng, Rng, SeedableRng}; + + use super::*; + + struct IntervalGenerator { + rng: StdRng, + unique: HashSet>, + limit: i32, + } + + impl IntervalGenerator { + fn new(seed: [u8; 32]) -> Self { + const LIMIT: i32 = 1000; + Self { + rng: SeedableRng::from_seed(seed), + unique: HashSet::new(), + limit: LIMIT, + } + } + + fn next(&mut self) -> Interval { + let low = self.rng.gen_range(0..self.limit - 1); + let high = self.rng.gen_range((low + 1)..self.limit); + Interval::new(low, high) + } + + fn next_unique(&mut self) -> Interval { + let mut interval = self.next(); + while self.unique.contains(&interval) { + interval = self.next(); + } + self.unique.insert(interval.clone()); + interval + } + + fn next_with_range(&mut self, range: i32) -> Interval { + let low = self.rng.gen_range(0..self.limit - 1); + let high = self + .rng + .gen_range((low + 1)..self.limit.min(low + 1 + range)); + Interval::new(low, high) + } + } + + impl IntervalMap { + fn check_max(&self) { + let _ignore = self.check_max_inner(self.root); + } + + fn check_max_inner(&self, x: NodeIndex) -> i32 { + if self.node_ref(x, Node::is_sentinel) { + return 0; + } + let l_max = self.check_max_inner(self.node_ref(x, Node::left)); + let r_max = self.check_max_inner(self.node_ref(x, Node::right)); + let max = self.node_ref(x, |x| x.interval().high.max(l_max).max(r_max)); + assert_eq!(self.max(x), Some(&max)); + max + } + + /// 1. Every node is either red or black. + /// 2. The root is black. + /// 3. Every leaf (NIL) is black. + /// 4. If a node is red, then both its children are black. + /// 5. For each node, all simple paths from the node to descendant leaves contain the + /// same number of black nodes. + fn check_rb_properties(&self) { + assert!(matches!( + self.node_ref(self.root, Node::color), + Color::Black + )); + self.check_children_color(self.root); + self.check_black_height(self.root); + } + + fn check_children_color(&self, x: NodeIndex) { + if self.node_ref(x, Node::is_sentinel) { + return; + } + self.check_children_color(self.node_ref(x, Node::left)); + self.check_children_color(self.node_ref(x, Node::right)); + if self.node_ref(x, Node::is_red) { + assert!(matches!(self.left_ref(x, Node::color), Color::Black)); + assert!(matches!(self.right_ref(x, Node::color), Color::Black)); + } + } + + fn check_black_height(&self, x: NodeIndex) -> usize { + if self.node_ref(x, Node::is_sentinel) { + return 0; + } + let lefth = self.check_black_height(self.node_ref(x, Node::left)); + let righth = self.check_black_height(self.node_ref(x, Node::right)); + assert_eq!(lefth, righth); + if self.node_ref(x, Node::is_black) { + return lefth + 1; + } + lefth + } + } + + fn with_map_and_generator(test_fn: impl Fn(IntervalMap, IntervalGenerator)) { + let seeds = vec![[0; 32], [1; 32], [2; 32]]; + for seed in seeds { + let gen = IntervalGenerator::new(seed); + let map = IntervalMap::new(); + test_fn(map, gen); + } + } + + #[test] + fn red_black_tree_properties_is_satisfied() { + with_map_and_generator(|mut map, mut gen| { + let intervals: Vec<_> = std::iter::repeat_with(|| gen.next_unique()) + .take(1000) + .collect(); + for i in intervals.clone() { + let _ignore = map.insert(i, ()); + } + map.check_rb_properties(); + }); + } + + #[test] + fn map_len_will_update() { + with_map_and_generator(|mut map, mut gen| { + let intervals: Vec<_> = std::iter::repeat_with(|| gen.next_unique()) + .take(100) + .collect(); + for i in intervals.clone() { + let _ignore = map.insert(i, ()); + } + assert_eq!(map.len(), 100); + for i in intervals { + let _ignore = map.remove(&i); + } + assert_eq!(map.len(), 0); + }); + } + + #[test] + fn check_overlap_is_ok() { + with_map_and_generator(|mut map, mut gen| { + let intervals: Vec<_> = std::iter::repeat_with(|| gen.next_with_range(10)) + .take(100) + .collect(); + for i in intervals.clone() { + let _ignore = map.insert(i, ()); + } + let to_check: Vec<_> = std::iter::repeat_with(|| gen.next_with_range(10)) + .take(1000) + .collect(); + let expects: Vec<_> = to_check + .iter() + .map(|ci| intervals.iter().any(|i| ci.overlap(i))) + .collect(); + + for (ci, expect) in to_check.into_iter().zip(expects.into_iter()) { + assert_eq!(map.overlap(&ci), expect); + } + }); + } + + #[test] + fn check_max_is_ok() { + with_map_and_generator(|mut map, mut gen| { + let intervals: Vec<_> = std::iter::repeat_with(|| gen.next_unique()) + .take(1000) + .collect(); + for i in intervals.clone() { + let _ignore = map.insert(i, ()); + map.check_max(); + } + assert_eq!(map.len(), 1000); + for i in intervals { + let _ignore = map.remove(&i); + map.check_max(); + } + }); + } + + #[test] + fn remove_non_exist_interval_will_do_nothing() { + with_map_and_generator(|mut map, mut gen| { + let intervals: Vec<_> = std::iter::repeat_with(|| gen.next_unique()) + .take(1000) + .collect(); + for i in intervals { + let _ignore = map.insert(i, ()); + } + assert_eq!(map.len(), 1000); + let to_remove: Vec<_> = std::iter::repeat_with(|| gen.next_unique()) + .take(1000) + .collect(); + for i in to_remove { + let _ignore = map.remove(&i); + } + assert_eq!(map.len(), 1000); + }); + } + + #[test] + fn find_all_overlap_is_ok() { + with_map_and_generator(|mut map, mut gen| { + let intervals: Vec<_> = std::iter::repeat_with(|| gen.next_unique()) + .take(1000) + .collect(); + for i in intervals.clone() { + let _ignore = map.insert(i, ()); + } + let to_find: Vec<_> = std::iter::repeat_with(|| gen.next()).take(1000).collect(); + + let expects: Vec> = to_find + .iter() + .map(|ti| intervals.iter().filter(|i| ti.overlap(i)).collect()) + .collect(); + + for (ti, mut expect) in to_find.into_iter().zip(expects.into_iter()) { + let mut result = map.find_all_overlap(&ti); + expect.sort_unstable(); + result.sort_unstable(); + assert_eq!(expect.len(), result.len()); + for (e, r) in expect.into_iter().zip(result.into_iter()) { + assert_eq!(e, r.0); + } + } + }); + } + + #[test] + fn iterate_through_map_is_sorted() { + with_map_and_generator(|mut map, mut gen| { + let mut intervals: Vec<_> = std::iter::repeat_with(|| gen.next_unique()) + .enumerate() + .take(1000) + .collect(); + for (v, i) in intervals.clone() { + let _ignore = map.insert(i, v); + } + intervals.sort_unstable_by(|a, b| a.1.cmp(&b.1)); + + for ((ei, ev), (v, i)) in map.iter().zip(intervals.iter()) { + assert_eq!(ei, i); + assert_eq!(ev, v); + } + }); + } + + #[test] + fn interval_map_clear_is_ok() { + let mut map = IntervalMap::new(); + map.insert(Interval::new(1, 3), 1); + map.insert(Interval::new(2, 4), 2); + map.insert(Interval::new(6, 7), 3); + assert_eq!(map.len(), 3); + map.clear(); + assert_eq!(map.len(), 0); + assert!(map.is_empty()); + assert_eq!(map.nodes.len(), 1); + assert!(map.nodes[0].is_sentinel()); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..13e5141 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,32 @@ +//! `interval_map` is a thread-safe map based on interval tree. +//! +//! It fully implements the insertion and deletion functionality of a red-black tree, +//! ensuring that each modification operation requires at most O(logN) time complexity. +//! +//! To safely and efficiently handle insertion and deletion operations in Rust, +//! `interval_map` innovatively uses arrays to simulate pointers for managing the parent-child +//! references in the red-black tree. This approach also ensures that interval_map has the +//! `Send` and `Unpin` traits, allowing it to be safely transferred between threads and +//! to maintain a fixed memory location during asynchronous operations. +//! +//! # Example +//! +//! ```rust +//! use interval_map::{Interval, IntervalMap}; +//! +//! let mut map = IntervalMap::new(); +//! let int = Interval::new(1, 2); +//! map.insert(int.clone(), 123456); +//! assert_eq!(map.get(&int), Some(&123456)); +//! ``` +//! + +mod entry; +mod index; +mod interval; +mod intervalmap; +mod node; + +pub use entry::{Entry, OccupiedEntry, VacantEntry}; +pub use interval::Interval; +pub use intervalmap::{IntervalMap, Iter}; diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 0000000..63cbb03 --- /dev/null +++ b/src/node.rs @@ -0,0 +1,124 @@ +use crate::interval::Interval; + +use crate::index::{IndexType, NodeIndex}; + +/// Node of the interval tree +#[derive(Debug)] +pub struct Node { + /// Left children + pub left: Option>, + /// Right children + pub right: Option>, + /// Parent + pub parent: Option>, + /// Color of the node + pub color: Color, + + /// Interval of the node + pub interval: Option>, + /// The index that point to the node with the max value + pub max_index: Option>, + /// Value of the node + pub value: Option, +} + +// Convenient getter/setter methods +impl Node +where + Ix: IndexType, +{ + pub fn color(&self) -> Color { + self.color + } + + pub fn interval(&self) -> &Interval { + self.interval.as_ref().unwrap() + } + + pub fn max_index(&self) -> NodeIndex { + self.max_index.unwrap() + } + + pub fn left(&self) -> NodeIndex { + self.left.unwrap() + } + + pub fn right(&self) -> NodeIndex { + self.right.unwrap() + } + + pub fn parent(&self) -> NodeIndex { + self.parent.unwrap() + } + + pub fn is_sentinel(&self) -> bool { + self.interval.is_none() + } + + pub fn sentinel(&self) -> Option<&Self> { + self.interval.is_some().then_some(self) + } + + pub fn is_black(&self) -> bool { + matches!(self.color, Color::Black) + } + + pub fn is_red(&self) -> bool { + matches!(self.color, Color::Red) + } + + pub fn value(&self) -> &V { + self.value.as_ref().unwrap() + } + + pub fn value_mut(&mut self) -> &mut V { + self.value.as_mut().unwrap() + } + + pub fn take_value(&mut self) -> V { + self.value.take().unwrap() + } + + pub fn set_value(value: V) -> impl FnOnce(&mut Node) -> V { + move |node: &mut Node| node.value.replace(value).unwrap() + } + + pub fn set_color(color: Color) -> impl FnOnce(&mut Node) { + move |node: &mut Node| { + node.color = color; + } + } + + pub fn set_max_index(max_index: NodeIndex) -> impl FnOnce(&mut Node) { + move |node: &mut Node| { + let _ignore = node.max_index.replace(max_index); + } + } + + pub fn set_left(left: NodeIndex) -> impl FnOnce(&mut Node) { + move |node: &mut Node| { + let _ignore = node.left.replace(left); + } + } + + pub fn set_right(right: NodeIndex) -> impl FnOnce(&mut Node) { + move |node: &mut Node| { + let _ignore = node.right.replace(right); + } + } + + pub fn set_parent(parent: NodeIndex) -> impl FnOnce(&mut Node) { + move |node: &mut Node| { + let _ignore = node.parent.replace(parent); + } + } +} + +/// The color of the node +#[derive(Debug, Clone, Copy)] +pub enum Color { + /// Red node + Red, + /// Black node + Black, +}