treemendous 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- treemendous-0.1.0/LICENSE +28 -0
- treemendous-0.1.0/PKG-INFO +22 -0
- treemendous-0.1.0/README.md +2 -0
- treemendous-0.1.0/build.py +55 -0
- treemendous-0.1.0/pyproject.toml +35 -0
- treemendous-0.1.0/setup.py +31 -0
- treemendous-0.1.0/treemendous/__init__.py +0 -0
- treemendous-0.1.0/treemendous/basic/__init__.py +0 -0
- treemendous-0.1.0/treemendous/basic/avl.py +283 -0
- treemendous-0.1.0/treemendous/basic/avl_earliest.py +127 -0
- treemendous-0.1.0/treemendous/basic/base.py +76 -0
- treemendous-0.1.0/treemendous/basic/boundary.py +171 -0
- treemendous-0.1.0/treemendous/basic/segment.py +100 -0
- treemendous-0.1.0/treemendous/cpp/__init__.py +0 -0
- treemendous-0.1.0/treemendous/cpp/boundary.cpp +129 -0
- treemendous-0.1.0/treemendous/cpp/boundary_bindings.cpp +31 -0
- treemendous-0.1.0/treemendous/cpp/boundary_ic.cpp +65 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Joseph Cox
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: treemendous
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Exploring a diverse collection of interval tree implementations across multiple programming languages to identify the most efficient configurations for varied use cases, such as optimizing for query speed, memory usage, or dynamic updates.
|
|
5
|
+
License: BSD 3-Clause License
|
|
6
|
+
Author: Joseph Cox
|
|
7
|
+
Author-email: joseph@codensity.io
|
|
8
|
+
Requires-Python: >=3.9,<4.0
|
|
9
|
+
Classifier: License :: Other/Proprietary License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Requires-Dist: sortedcollections (>=2.1.0,<3.0.0)
|
|
17
|
+
Requires-Dist: sortedcontainers (>=2.4.0,<3.0.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# Tree-Mendous
|
|
21
|
+
Exploring a diverse collection of interval tree implementations across multiple programming languages to identify the most efficient configurations for varied use cases, such as optimizing for query speed, memory usage, or dynamic updates.
|
|
22
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from pybind11.setup_helpers import Pybind11Extension
|
|
7
|
+
from setuptools.command.build_ext import build_ext
|
|
8
|
+
from setuptools.dist import Distribution
|
|
9
|
+
|
|
10
|
+
def build(setup_kwargs: Dict[str, Any], with_ic: bool = False) -> None:
|
|
11
|
+
compile_args = ["-O3"]
|
|
12
|
+
include_dirs = []
|
|
13
|
+
libraries = []
|
|
14
|
+
extra_link_args = []
|
|
15
|
+
|
|
16
|
+
if with_ic:
|
|
17
|
+
compile_args.append("-DWITH_IC_MANAGER")
|
|
18
|
+
include_dirs.append("/opt/homebrew/Cellar/boost/1.86.0_2/include")
|
|
19
|
+
libraries.append("boost_system")
|
|
20
|
+
extra_link_args.append("-L/opt/homebrew/Cellar/boost/1.86.0_2/lib")
|
|
21
|
+
|
|
22
|
+
ext_modules: List[Pybind11Extension] = [
|
|
23
|
+
Pybind11Extension(
|
|
24
|
+
"treemendous.cpp.boundary",
|
|
25
|
+
["treemendous/cpp/boundary_bindings.cpp"],
|
|
26
|
+
cxx_std=20,
|
|
27
|
+
extra_compile_args=compile_args,
|
|
28
|
+
include_dirs=include_dirs,
|
|
29
|
+
libraries=libraries,
|
|
30
|
+
extra_link_args=extra_link_args,
|
|
31
|
+
),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
distribution = Distribution({
|
|
35
|
+
"name": "treemendous",
|
|
36
|
+
"ext_modules": ext_modules
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
cmd = build_ext(distribution)
|
|
40
|
+
cmd.ensure_finalized()
|
|
41
|
+
cmd.run()
|
|
42
|
+
|
|
43
|
+
# Copy built extensions back to the project
|
|
44
|
+
for output in cmd.get_outputs():
|
|
45
|
+
output = Path(output)
|
|
46
|
+
relative_extension = output.relative_to(cmd.build_lib)
|
|
47
|
+
|
|
48
|
+
shutil.copyfile(output, relative_extension)
|
|
49
|
+
mode = os.stat(relative_extension).st_mode
|
|
50
|
+
mode |= (mode & 0o444) >> 2
|
|
51
|
+
os.chmod(relative_extension, mode)
|
|
52
|
+
# setup_kwargs.update({
|
|
53
|
+
# "ext_modules": ext_modules,
|
|
54
|
+
# "cmdclass": {"build_ext": build_ext},
|
|
55
|
+
# })
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "treemendous"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Exploring a diverse collection of interval tree implementations across multiple programming languages to identify the most efficient configurations for varied use cases, such as optimizing for query speed, memory usage, or dynamic updates."
|
|
5
|
+
authors = ["Joseph Cox <joseph@codensity.io>"]
|
|
6
|
+
license = "BSD 3-Clause License"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
[tool.poetry.dependencies]
|
|
12
|
+
python = "^3.9"
|
|
13
|
+
sortedcollections = "^2.1.0"
|
|
14
|
+
sortedcontainers = "^2.4.0"
|
|
15
|
+
|
|
16
|
+
[tool.poetry.group.dev.dependencies]
|
|
17
|
+
sortedcontainers-stubs = "^2.4.2"
|
|
18
|
+
flameprof = "^0.4"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
[tool.poetry.group.test.dependencies]
|
|
22
|
+
hypothesis = "^6.119.4"
|
|
23
|
+
pytest = "^8.3.3"
|
|
24
|
+
|
|
25
|
+
[tool.pytest.ini_options]
|
|
26
|
+
addopts = "--hypothesis-explain -vv"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["poetry-core>=1.0.0", "pybind11>=2.13.6", "setuptools>=75.6.0"]
|
|
31
|
+
# build-backend = "poetry.core.masonry.api"
|
|
32
|
+
|
|
33
|
+
[tool.poetry.build]
|
|
34
|
+
script = "build.py"
|
|
35
|
+
generate-setup-file = true
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from setuptools import setup
|
|
3
|
+
|
|
4
|
+
packages = \
|
|
5
|
+
['treemendous', 'treemendous.basic', 'treemendous.cpp']
|
|
6
|
+
|
|
7
|
+
package_data = \
|
|
8
|
+
{'': ['*']}
|
|
9
|
+
|
|
10
|
+
install_requires = \
|
|
11
|
+
['sortedcollections>=2.1.0,<3.0.0', 'sortedcontainers>=2.4.0,<3.0.0']
|
|
12
|
+
|
|
13
|
+
setup_kwargs = {
|
|
14
|
+
'name': 'treemendous',
|
|
15
|
+
'version': '0.1.0',
|
|
16
|
+
'description': 'Exploring a diverse collection of interval tree implementations across multiple programming languages to identify the most efficient configurations for varied use cases, such as optimizing for query speed, memory usage, or dynamic updates.',
|
|
17
|
+
'long_description': '# Tree-Mendous\nExploring a diverse collection of interval tree implementations across multiple programming languages to identify the most efficient configurations for varied use cases, such as optimizing for query speed, memory usage, or dynamic updates.\n',
|
|
18
|
+
'author': 'Joseph Cox',
|
|
19
|
+
'author_email': 'joseph@codensity.io',
|
|
20
|
+
'maintainer': 'None',
|
|
21
|
+
'maintainer_email': 'None',
|
|
22
|
+
'url': 'None',
|
|
23
|
+
'packages': packages,
|
|
24
|
+
'package_data': package_data,
|
|
25
|
+
'install_requires': install_requires,
|
|
26
|
+
'python_requires': '>=3.9,<4.0',
|
|
27
|
+
}
|
|
28
|
+
from build import *
|
|
29
|
+
build(setup_kwargs)
|
|
30
|
+
|
|
31
|
+
setup(**setup_kwargs)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
from typing import Generic, Optional, List, Tuple, TypeVar, cast, overload
|
|
2
|
+
from treemendous.basic.base import IntervalNodeBase, IntervalNodeProtocol, IntervalTreeBase
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IntervalNode(IntervalNodeBase[IntervalNodeProtocol]):
|
|
7
|
+
def __init__(self, start: int, end: int) -> None:
|
|
8
|
+
super().__init__(start, end)
|
|
9
|
+
self.total_length: int = self.length
|
|
10
|
+
self.height: int = 1
|
|
11
|
+
|
|
12
|
+
def update_stats(self) -> None:
|
|
13
|
+
self.update_length()
|
|
14
|
+
self.total_length = self.length
|
|
15
|
+
if self.left:
|
|
16
|
+
self.total_length += self.left.total_length
|
|
17
|
+
if self.right:
|
|
18
|
+
self.total_length += self.right.total_length
|
|
19
|
+
self.height = 1 + max(
|
|
20
|
+
self.get_height(self.left),
|
|
21
|
+
self.get_height(self.right)
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def get_height(node: Optional['IntervalNode']) -> int:
|
|
26
|
+
return node.height if node else 0
|
|
27
|
+
|
|
28
|
+
R = TypeVar('R', bound=IntervalNode)
|
|
29
|
+
class IntervalTree(Generic[R], IntervalTreeBase[R]):
|
|
30
|
+
def __init__(self, node_class: type[R]) -> None:
|
|
31
|
+
super().__init__()
|
|
32
|
+
self.node_class = node_class
|
|
33
|
+
self.root: Optional[R] = None
|
|
34
|
+
|
|
35
|
+
def _print_node(self, node: R, indent: str, prefix: str) -> None:
|
|
36
|
+
print(f"{indent}{prefix}{node.start}-{node.end} (len={node.length}, total_len={node.total_length})")
|
|
37
|
+
|
|
38
|
+
def release_interval(self, start: int, end: int) -> None:
|
|
39
|
+
overlapping_nodes: List[R] = []
|
|
40
|
+
self.root = self._delete_overlaps(self.root, start, end, overlapping_nodes)
|
|
41
|
+
# Merge overlapping intervals with the new interval
|
|
42
|
+
for node in overlapping_nodes:
|
|
43
|
+
start = min(start, node.start)
|
|
44
|
+
end = max(end, node.end)
|
|
45
|
+
# Insert the merged interval using the constructor
|
|
46
|
+
self.root = self._insert(self.root, self.node_class(start, end))
|
|
47
|
+
|
|
48
|
+
def reserve_interval(self, start: int, end: int) -> None:
|
|
49
|
+
self.root = self._delete_interval(self.root, start, end)
|
|
50
|
+
|
|
51
|
+
def _delete_interval(
|
|
52
|
+
self, node: Optional[R], start: int, end: int
|
|
53
|
+
) -> Optional[R]:
|
|
54
|
+
if not node:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
if node.end <= start:
|
|
58
|
+
# Interval to delete is after the current node
|
|
59
|
+
node.right = self._delete_interval(node.right, start, end)
|
|
60
|
+
elif node.start >= end:
|
|
61
|
+
# Interval to delete is before the current node
|
|
62
|
+
node.left = self._delete_interval(node.left, start, end)
|
|
63
|
+
else:
|
|
64
|
+
# The current node overlaps with the interval to delete
|
|
65
|
+
# We may need to split the node into up to two intervals
|
|
66
|
+
|
|
67
|
+
nodes_to_insert = []
|
|
68
|
+
|
|
69
|
+
if node.start < start:
|
|
70
|
+
# Left part remains
|
|
71
|
+
left_node = self.node_class(node.start, start)
|
|
72
|
+
nodes_to_insert.append(left_node)
|
|
73
|
+
|
|
74
|
+
if node.end > end:
|
|
75
|
+
# Right part remains
|
|
76
|
+
right_node = self.node_class(end, node.end)
|
|
77
|
+
nodes_to_insert.append(right_node)
|
|
78
|
+
|
|
79
|
+
# Delete the current node and replace it with left and right parts
|
|
80
|
+
node = self._merge_subtrees(
|
|
81
|
+
self._delete_interval(node.left, start, end),
|
|
82
|
+
self._delete_interval(node.right, start, end)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Insert any remaining parts
|
|
86
|
+
for n in nodes_to_insert:
|
|
87
|
+
node = self._insert(node, n)
|
|
88
|
+
|
|
89
|
+
if node:
|
|
90
|
+
node.update_stats()
|
|
91
|
+
node = self._rebalance(node)
|
|
92
|
+
return node
|
|
93
|
+
|
|
94
|
+
def _delete_overlaps(
|
|
95
|
+
self, node: Optional[R], start: int, end: int, overlapping_nodes: List[R]
|
|
96
|
+
) -> Optional[R]:
|
|
97
|
+
if not node:
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
if node.end <= start:
|
|
101
|
+
# No overlap, move to the right
|
|
102
|
+
node.right = self._delete_overlaps(node.right, start, end, overlapping_nodes)
|
|
103
|
+
elif node.start >= end:
|
|
104
|
+
# No overlap, move to the left
|
|
105
|
+
node.left = self._delete_overlaps(node.left, start, end, overlapping_nodes)
|
|
106
|
+
else:
|
|
107
|
+
# Overlap detected
|
|
108
|
+
overlapping_nodes.append(node)
|
|
109
|
+
# Remove this node and continue searching in both subtrees
|
|
110
|
+
node = self._merge_subtrees(
|
|
111
|
+
self._delete_overlaps(node.left, start, end, overlapping_nodes),
|
|
112
|
+
self._delete_overlaps(node.right, start, end, overlapping_nodes)
|
|
113
|
+
)
|
|
114
|
+
return node
|
|
115
|
+
|
|
116
|
+
if node:
|
|
117
|
+
node.update_stats()
|
|
118
|
+
node = self._rebalance(node)
|
|
119
|
+
return node
|
|
120
|
+
|
|
121
|
+
def _merge_subtrees(
|
|
122
|
+
self, left: Optional[R], right: Optional[R]
|
|
123
|
+
) -> Optional[R]:
|
|
124
|
+
if not left:
|
|
125
|
+
return right
|
|
126
|
+
if not right:
|
|
127
|
+
return left
|
|
128
|
+
|
|
129
|
+
# Find the node with the minimum start in the right subtree
|
|
130
|
+
min_node = self._get_min(right)
|
|
131
|
+
right = self._delete_min(right)
|
|
132
|
+
min_node.left = left
|
|
133
|
+
min_node.right = right
|
|
134
|
+
min_node.update_stats()
|
|
135
|
+
return self._rebalance(min_node)
|
|
136
|
+
|
|
137
|
+
def _delete_min(self, node: R) -> Optional[R]:
|
|
138
|
+
if node.left is None:
|
|
139
|
+
return node.right
|
|
140
|
+
node.left = self._delete_min(node.left)
|
|
141
|
+
node.update_stats()
|
|
142
|
+
return self._rebalance(node)
|
|
143
|
+
|
|
144
|
+
def _insert(self, node: Optional[R], new_node: R) -> R:
|
|
145
|
+
if not node:
|
|
146
|
+
return new_node
|
|
147
|
+
|
|
148
|
+
if new_node.start < node.start:
|
|
149
|
+
node.left = self._insert(node.left, new_node)
|
|
150
|
+
else:
|
|
151
|
+
node.right = self._insert(node.right, new_node)
|
|
152
|
+
|
|
153
|
+
node.update_stats()
|
|
154
|
+
node = self._rebalance(node)
|
|
155
|
+
return node
|
|
156
|
+
|
|
157
|
+
def _get_min(self, node: IntervalNode) -> IntervalNode:
|
|
158
|
+
current = node
|
|
159
|
+
while current.left:
|
|
160
|
+
current = current.left
|
|
161
|
+
return current
|
|
162
|
+
|
|
163
|
+
def _rebalance(self, node: R) -> R:
|
|
164
|
+
balance = self._get_balance(node)
|
|
165
|
+
if balance > 1:
|
|
166
|
+
# Left heavy
|
|
167
|
+
if self._get_balance(node.left) < 0:
|
|
168
|
+
# Left-Right case
|
|
169
|
+
node.left = self._rotate_left(node.left)
|
|
170
|
+
# Left-Left case
|
|
171
|
+
node = self._rotate_right(node)
|
|
172
|
+
elif balance < -1:
|
|
173
|
+
# Right heavy
|
|
174
|
+
if self._get_balance(node.right) > 0:
|
|
175
|
+
# Right-Left case
|
|
176
|
+
node.right = self._rotate_right(node.right)
|
|
177
|
+
# Right-Right case
|
|
178
|
+
node = self._rotate_left(node)
|
|
179
|
+
return node
|
|
180
|
+
|
|
181
|
+
def _get_balance(self, node: Optional[IntervalNode]) -> int:
|
|
182
|
+
if not node:
|
|
183
|
+
return 0
|
|
184
|
+
return IntervalNode.get_height(node.left) - IntervalNode.get_height(node.right)
|
|
185
|
+
|
|
186
|
+
@overload
|
|
187
|
+
def _rotate_left(self, z: None) -> None: ...
|
|
188
|
+
|
|
189
|
+
@overload
|
|
190
|
+
def _rotate_left(self, z: R) -> R: ...
|
|
191
|
+
|
|
192
|
+
def _rotate_left(self, z: Optional[R]) -> Optional[R]:
|
|
193
|
+
if not z or not z.right:
|
|
194
|
+
return z
|
|
195
|
+
y: R = z.right
|
|
196
|
+
subtree: Optional[R] = y.left
|
|
197
|
+
|
|
198
|
+
# Perform rotation
|
|
199
|
+
y.left = z
|
|
200
|
+
z.right = subtree
|
|
201
|
+
|
|
202
|
+
# Update heights and stats
|
|
203
|
+
z.update_stats()
|
|
204
|
+
y.update_stats()
|
|
205
|
+
return y
|
|
206
|
+
|
|
207
|
+
@overload
|
|
208
|
+
def _rotate_right(self, z: None) -> None: ...
|
|
209
|
+
|
|
210
|
+
@overload
|
|
211
|
+
def _rotate_right(self, z: R) -> R: ...
|
|
212
|
+
|
|
213
|
+
def _rotate_right(self, z: Optional[R]) -> Optional[R]:
|
|
214
|
+
if not z or not z.left:
|
|
215
|
+
return z
|
|
216
|
+
y: R = z.left
|
|
217
|
+
subtree: Optional[R] = y.right
|
|
218
|
+
|
|
219
|
+
# Perform rotation
|
|
220
|
+
y.right = z
|
|
221
|
+
z.left = subtree
|
|
222
|
+
|
|
223
|
+
# Update heights and stats
|
|
224
|
+
z.update_stats()
|
|
225
|
+
y.update_stats()
|
|
226
|
+
return y
|
|
227
|
+
|
|
228
|
+
def get_intervals(self) -> List[Tuple[int, int]]:
|
|
229
|
+
intervals: List[Tuple[int, int]] = []
|
|
230
|
+
self._get_intervals(self.root, intervals)
|
|
231
|
+
return intervals
|
|
232
|
+
|
|
233
|
+
def _get_intervals(self, node: Optional[R], intervals: List[Tuple[int, int]]) -> None:
|
|
234
|
+
if not node:
|
|
235
|
+
return
|
|
236
|
+
intervals.append((node.start, node.end))
|
|
237
|
+
self._get_intervals(node.left, intervals)
|
|
238
|
+
self._get_intervals(node.right, intervals)
|
|
239
|
+
|
|
240
|
+
# Example usage:
|
|
241
|
+
if __name__ == "__main__":
|
|
242
|
+
tree = IntervalTree[IntervalNode](IntervalNode)
|
|
243
|
+
# Initially, the whole interval [0, 100] is available
|
|
244
|
+
tree.release_interval(0, 100)
|
|
245
|
+
print("Initial tree:")
|
|
246
|
+
tree.print_tree()
|
|
247
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
248
|
+
|
|
249
|
+
# Schedule interval [0, 1
|
|
250
|
+
tree.reserve_interval(0, 1)
|
|
251
|
+
print("\nAfter scheduling [0, 1]:")
|
|
252
|
+
tree.print_tree()
|
|
253
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
254
|
+
|
|
255
|
+
# Unschedule interval [0, 1]
|
|
256
|
+
tree.release_interval(0, 1)
|
|
257
|
+
print("\nAfter unscheduling [0, 1]:")
|
|
258
|
+
tree.print_tree()
|
|
259
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
260
|
+
|
|
261
|
+
# Schedule interval [10, 20]
|
|
262
|
+
tree.reserve_interval(10, 20)
|
|
263
|
+
print("\nAfter scheduling [10, 20]:")
|
|
264
|
+
tree.print_tree()
|
|
265
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
266
|
+
|
|
267
|
+
# Schedule interval [30, 40]
|
|
268
|
+
tree.reserve_interval(30, 40)
|
|
269
|
+
print("\nAfter scheduling [30, 40]:")
|
|
270
|
+
tree.print_tree()
|
|
271
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
272
|
+
|
|
273
|
+
# Unschedule interval [10, 20]
|
|
274
|
+
tree.release_interval(10, 20)
|
|
275
|
+
print("\nAfter unscheduling [10, 20]:")
|
|
276
|
+
tree.print_tree()
|
|
277
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
278
|
+
|
|
279
|
+
# Split at pivot 50 (delete [50, 50])
|
|
280
|
+
tree.reserve_interval(50, 50)
|
|
281
|
+
print("\nAfter splitting at pivot 50:")
|
|
282
|
+
tree.print_tree()
|
|
283
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from treemendous.basic.base import IntervalNodeProtocol
|
|
4
|
+
from treemendous.basic.avl import IntervalNode, IntervalTree
|
|
5
|
+
|
|
6
|
+
class EarliestIntervalNode(IntervalNode, IntervalNodeProtocol):
|
|
7
|
+
def __init__(self, start: int, end: int) -> None:
|
|
8
|
+
super().__init__(start, end)
|
|
9
|
+
self.min_start: int = start
|
|
10
|
+
self.max_end: int = end
|
|
11
|
+
self.max_length: int = end - start
|
|
12
|
+
|
|
13
|
+
def update_stats(self) -> None:
|
|
14
|
+
super().update_stats()
|
|
15
|
+
|
|
16
|
+
self.min_start = self.start
|
|
17
|
+
self.max_end = self.end
|
|
18
|
+
self.max_length = self.end - self.start
|
|
19
|
+
|
|
20
|
+
if self.left:
|
|
21
|
+
assert isinstance(self.left, EarliestIntervalNode)
|
|
22
|
+
self.min_start = min(self.min_start, self.left.min_start)
|
|
23
|
+
self.max_end = max(self.max_end, self.left.max_end)
|
|
24
|
+
self.max_length = max(self.max_length, self.left.max_length)
|
|
25
|
+
if self.right:
|
|
26
|
+
assert isinstance(self.right, EarliestIntervalNode)
|
|
27
|
+
self.min_start = min(self.min_start, self.right.min_start)
|
|
28
|
+
self.max_end = max(self.max_end, self.right.max_end)
|
|
29
|
+
self.max_length = max(self.max_length, self.right.max_length)
|
|
30
|
+
|
|
31
|
+
class EarliestIntervalTree(IntervalTree[EarliestIntervalNode]):
|
|
32
|
+
def __init__(self) -> None:
|
|
33
|
+
super().__init__(EarliestIntervalNode)
|
|
34
|
+
|
|
35
|
+
def _print_node(self, node: EarliestIntervalNode, indent: str, prefix: str) -> None:
|
|
36
|
+
print(f"{indent}{prefix}{node.start}-{node.end} "
|
|
37
|
+
f"(min_start={node.min_start}, max_end={node.max_end}, max_length={node.max_length})")
|
|
38
|
+
|
|
39
|
+
def find_interval(self, point: int, length: int) -> Optional[EarliestIntervalNode]:
|
|
40
|
+
return self._find_interval(self.root, point, length)
|
|
41
|
+
|
|
42
|
+
def _find_interval(self, node: Optional[EarliestIntervalNode], point: int,
|
|
43
|
+
length: int) -> Optional[EarliestIntervalNode]:
|
|
44
|
+
if not node:
|
|
45
|
+
return None
|
|
46
|
+
if node.start >= point and (node.end - node.start) >= length:
|
|
47
|
+
# Potential candidate
|
|
48
|
+
left_candidate = self._find_interval(node.left, point, length)
|
|
49
|
+
return left_candidate if left_candidate else node
|
|
50
|
+
elif node.start < point:
|
|
51
|
+
# Search right subtree
|
|
52
|
+
return self._find_interval(node.right, point, length)
|
|
53
|
+
else:
|
|
54
|
+
# Node's interval is too short; search right subtree
|
|
55
|
+
return self._find_interval(node.right, point, length)
|
|
56
|
+
|
|
57
|
+
def _insert(self, node: Optional[EarliestIntervalNode],
|
|
58
|
+
new_node: EarliestIntervalNode) -> EarliestIntervalNode:
|
|
59
|
+
node = super()._insert(node, new_node)
|
|
60
|
+
node.update_stats() # Update the earliest-specific stats
|
|
61
|
+
return node
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# Example usage:
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
tree = EarliestIntervalTree()
|
|
67
|
+
# Initially, the whole interval [0, 100) is available
|
|
68
|
+
tree.release_interval(0, 100)
|
|
69
|
+
print("Initial tree:")
|
|
70
|
+
tree.print_tree()
|
|
71
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
72
|
+
|
|
73
|
+
# Schedule interval [0, 1
|
|
74
|
+
tree.reserve_interval(0, 1)
|
|
75
|
+
print("\nAfter scheduling [0, 1]:")
|
|
76
|
+
tree.print_tree()
|
|
77
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
78
|
+
|
|
79
|
+
# Unschedule interval [0, 1]
|
|
80
|
+
tree.release_interval(0, 1)
|
|
81
|
+
print("\nAfter unscheduling [0, 1]:")
|
|
82
|
+
tree.print_tree()
|
|
83
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
84
|
+
|
|
85
|
+
# Schedule interval [1, 2]
|
|
86
|
+
tree.reserve_interval(1, 3)
|
|
87
|
+
print("\nAfter scheduling [1, 3]:")
|
|
88
|
+
tree.print_tree()
|
|
89
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
90
|
+
|
|
91
|
+
# Schedule interval [2, 3]
|
|
92
|
+
tree.reserve_interval(2, 5)
|
|
93
|
+
print("\nAfter scheduling [2, 5]:")
|
|
94
|
+
tree.print_tree()
|
|
95
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# Schedule interval [10, 20)
|
|
99
|
+
tree.reserve_interval(10, 20)
|
|
100
|
+
print("\nAfter scheduling [10, 20):")
|
|
101
|
+
tree.print_tree()
|
|
102
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
103
|
+
|
|
104
|
+
# Schedule interval [15, 25)
|
|
105
|
+
tree.reserve_interval(15, 25)
|
|
106
|
+
print("\nAfter scheduling [15, 25):")
|
|
107
|
+
tree.print_tree()
|
|
108
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
109
|
+
|
|
110
|
+
# Find interval starting at or after 18 with length at least 5
|
|
111
|
+
result = tree.find_interval(18, 5)
|
|
112
|
+
if result:
|
|
113
|
+
print(f"\nFound interval: [{result.start}, {result.end})")
|
|
114
|
+
else:
|
|
115
|
+
print("\nNo suitable interval found.")
|
|
116
|
+
|
|
117
|
+
# Unschedule interval [10, 20)
|
|
118
|
+
tree.release_interval(10, 20)
|
|
119
|
+
print("\nAfter unscheduling [10, 20):")
|
|
120
|
+
tree.print_tree()
|
|
121
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
122
|
+
|
|
123
|
+
# Delete interval overlapping multiple intervals
|
|
124
|
+
tree.reserve_interval(5, 15)
|
|
125
|
+
print("\nAfter deleting interval [5, 15):")
|
|
126
|
+
tree.print_tree()
|
|
127
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Generic, List, Optional, Tuple, TypeVar, Protocol
|
|
3
|
+
|
|
4
|
+
class IntervalNodeProtocol(Protocol):
|
|
5
|
+
start: int
|
|
6
|
+
end: int
|
|
7
|
+
length: int
|
|
8
|
+
height: int
|
|
9
|
+
total_length: int
|
|
10
|
+
left: Optional['IntervalNodeProtocol']
|
|
11
|
+
right: Optional['IntervalNodeProtocol']
|
|
12
|
+
|
|
13
|
+
def update_stats(self) -> None: ...
|
|
14
|
+
def update_length(self) -> None: ...
|
|
15
|
+
|
|
16
|
+
T = TypeVar('T', bound=IntervalNodeProtocol)
|
|
17
|
+
|
|
18
|
+
class IntervalNodeBase(Generic[T]):
|
|
19
|
+
def __init__(self, start: int, end: int) -> None:
|
|
20
|
+
self.start: int = start
|
|
21
|
+
self.end: int = end
|
|
22
|
+
self.length: int = end - start
|
|
23
|
+
self._height: int = 1
|
|
24
|
+
self._total_length: int = self.length
|
|
25
|
+
|
|
26
|
+
self.left: Optional[T] = None
|
|
27
|
+
self.right: Optional[T] = None
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def height(self) -> int:
|
|
32
|
+
return self._height
|
|
33
|
+
|
|
34
|
+
@height.setter
|
|
35
|
+
def height(self, value: int) -> None:
|
|
36
|
+
self._height = value
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def total_length(self) -> int:
|
|
41
|
+
return self._total_length
|
|
42
|
+
|
|
43
|
+
@total_length.setter
|
|
44
|
+
def total_length(self, value: int) -> None:
|
|
45
|
+
self._total_length = value
|
|
46
|
+
|
|
47
|
+
def update_length(self) -> None:
|
|
48
|
+
self.length = self.end - self.start
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class IntervalTreeBase(Generic[T], ABC):
|
|
52
|
+
def __init__(self, root: Optional[T] = None) -> None:
|
|
53
|
+
self.root: Optional[T] = root
|
|
54
|
+
|
|
55
|
+
def print_tree(self) -> None:
|
|
56
|
+
self._print_tree(self.root)
|
|
57
|
+
|
|
58
|
+
def _print_tree(self, node: Optional[T], indent: str = "", prefix: str = "") -> None:
|
|
59
|
+
if node is None:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
self._print_tree(node.right, indent + " ", "┌── ") # type: ignore
|
|
63
|
+
self._print_node(node, indent, prefix)
|
|
64
|
+
self._print_tree(node.left, indent + " ", "└── ") # type: ignore
|
|
65
|
+
|
|
66
|
+
def get_total_available_length(self) -> int:
|
|
67
|
+
if not self.root:
|
|
68
|
+
return 0
|
|
69
|
+
return self.root.total_length
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def _print_node(self, node: T, indent: str, prefix: str) -> None: ...
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def get_intervals(self) -> List[Tuple[int, int]]: ...
|
|
76
|
+
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from sortedcontainers import SortedDict
|
|
2
|
+
from typing import List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
class IntervalManager:
|
|
5
|
+
def __init__(self) -> None:
|
|
6
|
+
# Intervals are stored as {start: end}
|
|
7
|
+
self.intervals: SortedDict[int, int] = SortedDict()
|
|
8
|
+
self.total_available_length: int = 0
|
|
9
|
+
|
|
10
|
+
def release_interval(self, start: int, end: int) -> None:
|
|
11
|
+
if start >= end:
|
|
12
|
+
return
|
|
13
|
+
|
|
14
|
+
# Find position to insert or merge
|
|
15
|
+
idx = self.intervals.bisect_left(start)
|
|
16
|
+
|
|
17
|
+
# Check and merge with previous interval if overlapping or adjacent
|
|
18
|
+
if idx > 0:
|
|
19
|
+
prev_start = self.intervals.keys()[idx - 1]
|
|
20
|
+
prev_end = self.intervals[prev_start]
|
|
21
|
+
if prev_end >= start:
|
|
22
|
+
start = prev_start
|
|
23
|
+
end = max(end, prev_end)
|
|
24
|
+
idx -= 1
|
|
25
|
+
del self.intervals[prev_start]
|
|
26
|
+
self.total_available_length -= prev_end - prev_start
|
|
27
|
+
|
|
28
|
+
# Merge with next intervals if overlapping
|
|
29
|
+
while idx < len(self.intervals):
|
|
30
|
+
curr_start = self.intervals.keys()[idx]
|
|
31
|
+
curr_end = self.intervals[curr_start]
|
|
32
|
+
if curr_start > end:
|
|
33
|
+
break
|
|
34
|
+
end = max(end, curr_end)
|
|
35
|
+
del self.intervals[curr_start]
|
|
36
|
+
self.total_available_length -= curr_end - curr_start
|
|
37
|
+
|
|
38
|
+
# Insert the new merged interval
|
|
39
|
+
self.intervals[start] = end
|
|
40
|
+
self.total_available_length += end - start
|
|
41
|
+
|
|
42
|
+
def reserve_interval(self, start: int, end: int) -> None:
|
|
43
|
+
if start >= end:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
idx = self.intervals.bisect_left(start)
|
|
47
|
+
|
|
48
|
+
if idx > 0:
|
|
49
|
+
prev_start = self.intervals.keys()[idx - 1]
|
|
50
|
+
prev_end = self.intervals[prev_start]
|
|
51
|
+
if prev_end > start:
|
|
52
|
+
idx -= 1
|
|
53
|
+
|
|
54
|
+
intervals_to_add: List[Tuple[int, int]] = []
|
|
55
|
+
keys_to_delete: List[int] = []
|
|
56
|
+
|
|
57
|
+
while idx < len(self.intervals):
|
|
58
|
+
curr_start = self.intervals.keys()[idx]
|
|
59
|
+
curr_end = self.intervals[curr_start]
|
|
60
|
+
|
|
61
|
+
if curr_start >= end:
|
|
62
|
+
break
|
|
63
|
+
|
|
64
|
+
overlap_start = max(start, curr_start)
|
|
65
|
+
overlap_end = min(end, curr_end)
|
|
66
|
+
|
|
67
|
+
if overlap_start < overlap_end:
|
|
68
|
+
# Mark current interval for removal
|
|
69
|
+
keys_to_delete.append(curr_start)
|
|
70
|
+
self.total_available_length -= curr_end - curr_start
|
|
71
|
+
|
|
72
|
+
# Add non-overlapping intervals
|
|
73
|
+
if curr_start < start:
|
|
74
|
+
intervals_to_add.append((curr_start, start))
|
|
75
|
+
if curr_end > end:
|
|
76
|
+
intervals_to_add.append((end, curr_end))
|
|
77
|
+
|
|
78
|
+
idx += 1
|
|
79
|
+
|
|
80
|
+
# Remove intervals after iteration
|
|
81
|
+
for key in keys_to_delete:
|
|
82
|
+
del self.intervals[key]
|
|
83
|
+
|
|
84
|
+
# Add new intervals
|
|
85
|
+
for s, e in intervals_to_add:
|
|
86
|
+
self.intervals[s] = e
|
|
87
|
+
self.total_available_length += e - s
|
|
88
|
+
|
|
89
|
+
def find_interval(self, point: int, length: int) -> Optional[Tuple[int, int]]:
|
|
90
|
+
idx = self.intervals.bisect_left(point)
|
|
91
|
+
intervals_keys = self.intervals.keys()
|
|
92
|
+
|
|
93
|
+
# Check the interval at idx
|
|
94
|
+
if idx < len(intervals_keys):
|
|
95
|
+
s = intervals_keys[idx]
|
|
96
|
+
e = self.intervals[s]
|
|
97
|
+
if s <= point < e and e - point >= length:
|
|
98
|
+
return point, point + length
|
|
99
|
+
elif s > point and e - s >= length:
|
|
100
|
+
return s, s + length
|
|
101
|
+
|
|
102
|
+
# Check the previous interval
|
|
103
|
+
if idx > 0:
|
|
104
|
+
idx -= 1
|
|
105
|
+
s = intervals_keys[idx]
|
|
106
|
+
e = self.intervals[s]
|
|
107
|
+
if s <= point < e and e - point >= length:
|
|
108
|
+
return point, point + length
|
|
109
|
+
elif point < s and e - s >= length:
|
|
110
|
+
return s, s + length
|
|
111
|
+
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
def get_total_available_length(self) -> int:
|
|
115
|
+
return self.total_available_length
|
|
116
|
+
|
|
117
|
+
def print_intervals(self) -> None:
|
|
118
|
+
print("Available intervals:")
|
|
119
|
+
for s, e in self.intervals.items():
|
|
120
|
+
print(f"[{s}, {e})")
|
|
121
|
+
print(f"Total available length: {self.total_available_length}")
|
|
122
|
+
|
|
123
|
+
def get_intervals(self) -> List[Tuple[int, int]]:
|
|
124
|
+
return list(self.intervals.items())
|
|
125
|
+
|
|
126
|
+
# Example usage:
|
|
127
|
+
if __name__ == "__main__":
|
|
128
|
+
manager = IntervalManager()
|
|
129
|
+
# Initially, the whole interval [0, 100) is available
|
|
130
|
+
manager.release_interval(0, 100)
|
|
131
|
+
print("Initial intervals:")
|
|
132
|
+
manager.print_intervals()
|
|
133
|
+
|
|
134
|
+
# Schedule interval [10, 20)
|
|
135
|
+
manager.reserve_interval(10, 20)
|
|
136
|
+
print("\nAfter scheduling [10, 20):")
|
|
137
|
+
manager.print_intervals()
|
|
138
|
+
|
|
139
|
+
# Schedule interval [15, 25)
|
|
140
|
+
manager.reserve_interval(15, 25)
|
|
141
|
+
print("\nAfter scheduling [15, 25):")
|
|
142
|
+
manager.print_intervals()
|
|
143
|
+
|
|
144
|
+
# Unschedule interval [10, 20)
|
|
145
|
+
manager.release_interval(10, 20)
|
|
146
|
+
print("\nAfter unscheduling [10, 20):")
|
|
147
|
+
manager.print_intervals()
|
|
148
|
+
|
|
149
|
+
# Schedule two adjacent intervals [30, 40) and [40, 50)
|
|
150
|
+
manager.reserve_interval(30, 40)
|
|
151
|
+
manager.reserve_interval(40, 50)
|
|
152
|
+
print("\nAfter scheduling two adjacent intervals [30, 40) and [40, 50):")
|
|
153
|
+
manager.print_intervals()
|
|
154
|
+
|
|
155
|
+
# Release overlapping interval [35, 45)
|
|
156
|
+
manager.release_interval(35, 45)
|
|
157
|
+
print("\nAfter releasing overlapping interval [35, 45):")
|
|
158
|
+
manager.print_intervals()
|
|
159
|
+
|
|
160
|
+
# Test finding intervals
|
|
161
|
+
result = manager.find_interval(0, 15)
|
|
162
|
+
print(f"\nFinding interval of length 15 starting from 0: {result}")
|
|
163
|
+
|
|
164
|
+
# Test complete overlap
|
|
165
|
+
manager.reserve_interval(60, 80)
|
|
166
|
+
print("\nAfter reserving [60, 80):")
|
|
167
|
+
manager.print_intervals()
|
|
168
|
+
|
|
169
|
+
manager.release_interval(65, 75)
|
|
170
|
+
print("\nAfter releasing internal interval [65, 75):")
|
|
171
|
+
manager.print_intervals()
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from base import IntervalNodeBase, IntervalTreeBase
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SegmentTreeNode(IntervalNodeBase['SegmentTreeNode']):
|
|
6
|
+
def __init__(self, start: int, end: int) -> None:
|
|
7
|
+
super().__init__(start, end)
|
|
8
|
+
self.total_length: int = self.length
|
|
9
|
+
self.is_full: bool = True
|
|
10
|
+
|
|
11
|
+
def update_node(self) -> None:
|
|
12
|
+
if self.left is not None and self.right is not None:
|
|
13
|
+
self.total_length = self.left.total_length + self.right.total_length
|
|
14
|
+
self.is_full = self.left.is_full and self.right.is_full
|
|
15
|
+
else:
|
|
16
|
+
self.total_length = self.length if self.is_full else 0
|
|
17
|
+
|
|
18
|
+
class SegmentTree(IntervalTreeBase[SegmentTreeNode]):
|
|
19
|
+
def __init__(self, start: int, end: int) -> None:
|
|
20
|
+
super().__init__(SegmentTreeNode(start, end))
|
|
21
|
+
|
|
22
|
+
def _print_node(self, node: SegmentTreeNode, indent: str, prefix: str) -> None:
|
|
23
|
+
print(f"{indent}{prefix}{node.start}-{node.end} (len={node.length}, total_len={node.total_length}, is_full={node.is_full})")
|
|
24
|
+
|
|
25
|
+
def _build(self, node: SegmentTreeNode | None) -> None:
|
|
26
|
+
if node is None:
|
|
27
|
+
return
|
|
28
|
+
if node.end - node.start <= 1:
|
|
29
|
+
return
|
|
30
|
+
mid: int = (node.start + node.end) // 2
|
|
31
|
+
node.left = SegmentTreeNode(node.start, mid)
|
|
32
|
+
node.right = SegmentTreeNode(mid, node.end)
|
|
33
|
+
self._build(node.left)
|
|
34
|
+
self._build(node.right)
|
|
35
|
+
|
|
36
|
+
def build(self) -> None:
|
|
37
|
+
self._build(self.root)
|
|
38
|
+
|
|
39
|
+
def _update(self, node: Optional[SegmentTreeNode], start: int, end: int, is_full: bool) -> None:
|
|
40
|
+
if node is None:
|
|
41
|
+
return
|
|
42
|
+
if node.end <= start or node.start >= end:
|
|
43
|
+
return
|
|
44
|
+
if node.start >= start and node.end <= end:
|
|
45
|
+
node.is_full = is_full
|
|
46
|
+
node.total_length = node.length if is_full else 0
|
|
47
|
+
node.left = None
|
|
48
|
+
node.right = None
|
|
49
|
+
else:
|
|
50
|
+
if node.left is None or node.right is None:
|
|
51
|
+
mid: int = (node.start + node.end) // 2
|
|
52
|
+
node.left = SegmentTreeNode(node.start, mid)
|
|
53
|
+
node.right = SegmentTreeNode(mid, node.end)
|
|
54
|
+
node.left.is_full = node.right.is_full = node.is_full
|
|
55
|
+
node.left.total_length = (node.left.end - node.left.start) if node.left.is_full else 0
|
|
56
|
+
node.right.total_length = (node.right.end - node.right.start) if node.right.is_full else 0
|
|
57
|
+
self._update(node.left, start, end, is_full)
|
|
58
|
+
self._update(node.right, start, end, is_full)
|
|
59
|
+
node.update_node()
|
|
60
|
+
|
|
61
|
+
def schedule_interval(self, start: int, end: int) -> None:
|
|
62
|
+
self._update(self.root, start, end, False)
|
|
63
|
+
|
|
64
|
+
def unschedule_interval(self, start: int, end: int) -> None:
|
|
65
|
+
self._update(self.root, start, end, True)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Example usage:
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
# Initialize segment tree with interval [0, 100)
|
|
72
|
+
tree = SegmentTree(0, 100)
|
|
73
|
+
tree.build()
|
|
74
|
+
print("Initial tree:")
|
|
75
|
+
tree.print_tree()
|
|
76
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
77
|
+
|
|
78
|
+
# Schedule interval [10, 20)
|
|
79
|
+
tree.schedule_interval(10, 20)
|
|
80
|
+
print("\nAfter scheduling [10, 20):")
|
|
81
|
+
tree.print_tree()
|
|
82
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
83
|
+
|
|
84
|
+
# Schedule interval [30, 40)
|
|
85
|
+
tree.schedule_interval(30, 40)
|
|
86
|
+
print("\nAfter scheduling [30, 40):")
|
|
87
|
+
tree.print_tree()
|
|
88
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
89
|
+
|
|
90
|
+
# Unschedule interval [10, 20)
|
|
91
|
+
tree.unschedule_interval(10, 20)
|
|
92
|
+
print("\nAfter unscheduling [10, 20):")
|
|
93
|
+
tree.print_tree()
|
|
94
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
95
|
+
|
|
96
|
+
# Split at pivot 50 (schedule [50, 50))
|
|
97
|
+
tree.schedule_interval(50, 50)
|
|
98
|
+
print("\nAfter splitting at pivot 50:")
|
|
99
|
+
tree.print_tree()
|
|
100
|
+
print(f"Total available length: {tree.get_total_available_length()}")
|
|
File without changes
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// IntervalManager class implementation
|
|
2
|
+
#include <map>
|
|
3
|
+
#include <vector>
|
|
4
|
+
#include <optional>
|
|
5
|
+
#include <iostream>
|
|
6
|
+
|
|
7
|
+
class IntervalManager {
|
|
8
|
+
public:
|
|
9
|
+
IntervalManager() : total_available_length(0) {}
|
|
10
|
+
|
|
11
|
+
void release_interval(int start, int end) {
|
|
12
|
+
if (start >= end) return;
|
|
13
|
+
|
|
14
|
+
auto it = intervals.lower_bound(start);
|
|
15
|
+
|
|
16
|
+
// Merge with previous interval if overlapping or adjacent
|
|
17
|
+
if (it != intervals.begin()) {
|
|
18
|
+
auto prev_it = std::prev(it);
|
|
19
|
+
if (prev_it->second >= start) {
|
|
20
|
+
start = prev_it->first;
|
|
21
|
+
end = std::max(end, prev_it->second);
|
|
22
|
+
total_available_length -= prev_it->second - prev_it->first;
|
|
23
|
+
intervals.erase(prev_it);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Merge with overlapping intervals
|
|
28
|
+
while (it != intervals.end() && it->first <= end) {
|
|
29
|
+
end = std::max(end, it->second);
|
|
30
|
+
total_available_length -= it->second - it->first;
|
|
31
|
+
it = intervals.erase(it);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
intervals[start] = end;
|
|
35
|
+
total_available_length += end - start;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
void reserve_interval(int start, int end) {
|
|
39
|
+
if (start >= end) return;
|
|
40
|
+
|
|
41
|
+
auto it = intervals.lower_bound(start);
|
|
42
|
+
|
|
43
|
+
if (it != intervals.begin()) {
|
|
44
|
+
auto prev_it = std::prev(it);
|
|
45
|
+
if (prev_it->second > start) {
|
|
46
|
+
it = prev_it;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
std::vector<std::map<int, int>::iterator> to_erase;
|
|
51
|
+
std::vector<std::pair<int, int>> to_add;
|
|
52
|
+
|
|
53
|
+
while (it != intervals.end() && it->first < end) {
|
|
54
|
+
int curr_start = it->first;
|
|
55
|
+
int curr_end = it->second;
|
|
56
|
+
|
|
57
|
+
int overlap_start = std::max(start, curr_start);
|
|
58
|
+
int overlap_end = std::min(end, curr_end);
|
|
59
|
+
|
|
60
|
+
if (overlap_start < overlap_end) {
|
|
61
|
+
to_erase.push_back(it);
|
|
62
|
+
total_available_length -= curr_end - curr_start;
|
|
63
|
+
|
|
64
|
+
if (curr_start < start) {
|
|
65
|
+
to_add.emplace_back(curr_start, start);
|
|
66
|
+
}
|
|
67
|
+
if (curr_end > end) {
|
|
68
|
+
to_add.emplace_back(end, curr_end);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
++it;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (auto& eit : to_erase) {
|
|
75
|
+
intervals.erase(eit);
|
|
76
|
+
}
|
|
77
|
+
for (const auto& interval : to_add) {
|
|
78
|
+
intervals[interval.first] = interval.second;
|
|
79
|
+
total_available_length += interval.second - interval.first;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
std::optional<std::pair<int, int>> find_interval(int point, int length) {
|
|
84
|
+
auto it = intervals.lower_bound(point);
|
|
85
|
+
|
|
86
|
+
if (it != intervals.end()) {
|
|
87
|
+
int s = it->first;
|
|
88
|
+
int e = it->second;
|
|
89
|
+
if (s <= point && e - point >= length) {
|
|
90
|
+
return std::make_pair(point, point + length);
|
|
91
|
+
} else if (s > point && e - s >= length) {
|
|
92
|
+
return std::make_pair(s, s + length);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (it != intervals.begin()) {
|
|
97
|
+
--it;
|
|
98
|
+
int s = it->first;
|
|
99
|
+
int e = it->second;
|
|
100
|
+
if (s <= point && e - point >= length) {
|
|
101
|
+
return std::make_pair(point, point + length);
|
|
102
|
+
} else if (point < s && e - s >= length) {
|
|
103
|
+
return std::make_pair(s, s + length);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return std::nullopt;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
int get_total_available_length() const {
|
|
111
|
+
return total_available_length;
|
|
112
|
+
}
|
|
113
|
+
void print_intervals() const {
|
|
114
|
+
std::ostream& out = std::cout;
|
|
115
|
+
out << "Available intervals:\n";
|
|
116
|
+
for (const auto& [s, e] : intervals) {
|
|
117
|
+
out << "[" << s << ", " << e << ")\n";
|
|
118
|
+
}
|
|
119
|
+
out << "Total available length: " << total_available_length << "\n";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
std::vector<std::pair<int, int>> get_intervals() const {
|
|
123
|
+
return std::vector<std::pair<int, int>>(intervals.begin(), intervals.end());
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private:
|
|
127
|
+
std::map<int, int> intervals;
|
|
128
|
+
int total_available_length;
|
|
129
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Pybind11 bindings for IntervalManager
|
|
2
|
+
#include <pybind11/pybind11.h>
|
|
3
|
+
#include <pybind11/stl.h>
|
|
4
|
+
#ifdef WITH_IC_MANAGER
|
|
5
|
+
#include "boundary_ic.cpp" // Include the IntervalManager implementation
|
|
6
|
+
#endif
|
|
7
|
+
#include "boundary.cpp" // Include the IntervalManager implementation
|
|
8
|
+
|
|
9
|
+
namespace py = pybind11;
|
|
10
|
+
|
|
11
|
+
PYBIND11_MODULE(boundary, m) {
|
|
12
|
+
py::class_<IntervalManager>(m, "IntervalManager")
|
|
13
|
+
.def(py::init<>())
|
|
14
|
+
.def("release_interval", &IntervalManager::release_interval)
|
|
15
|
+
.def("reserve_interval", &IntervalManager::reserve_interval)
|
|
16
|
+
.def("find_interval", &IntervalManager::find_interval)
|
|
17
|
+
.def("get_total_available_length", &IntervalManager::get_total_available_length)
|
|
18
|
+
.def("print_intervals", &IntervalManager::print_intervals)
|
|
19
|
+
.def("get_intervals", &IntervalManager::get_intervals);
|
|
20
|
+
|
|
21
|
+
#ifdef WITH_IC_MANAGER
|
|
22
|
+
py::class_<ICIntervalManager>(m, "ICIntervalManager")
|
|
23
|
+
.def(py::init<>())
|
|
24
|
+
.def("release_interval", &ICIntervalManager::release_interval)
|
|
25
|
+
.def("reserve_interval", &ICIntervalManager::reserve_interval)
|
|
26
|
+
.def("find_interval", &ICIntervalManager::find_interval)
|
|
27
|
+
.def("get_total_available_length", &ICIntervalManager::get_total_available_length)
|
|
28
|
+
.def("print_intervals", &ICIntervalManager::print_intervals)
|
|
29
|
+
.def("get_intervals", &ICIntervalManager::get_intervals);
|
|
30
|
+
#endif
|
|
31
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// IntervalManager class implementation using Boost Interval Containers
|
|
2
|
+
#include <boost/icl/interval_set.hpp>
|
|
3
|
+
#include <boost/icl/interval.hpp>
|
|
4
|
+
#include <iostream>
|
|
5
|
+
#include <vector>
|
|
6
|
+
#include <optional>
|
|
7
|
+
|
|
8
|
+
class ICIntervalManager {
|
|
9
|
+
public:
|
|
10
|
+
ICIntervalManager() : total_available_length(0) {}
|
|
11
|
+
|
|
12
|
+
void release_interval(int start, int end) {
|
|
13
|
+
if (start >= end) return;
|
|
14
|
+
auto interval = boost::icl::interval<int>::right_open(start, end);
|
|
15
|
+
|
|
16
|
+
intervals.add(interval);
|
|
17
|
+
|
|
18
|
+
total_available_length = intervals.size();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
void reserve_interval(int start, int end) {
|
|
22
|
+
if (start >= end) return;
|
|
23
|
+
auto interval = boost::icl::interval<int>::right_open(start, end);
|
|
24
|
+
|
|
25
|
+
intervals.subtract(interval);
|
|
26
|
+
|
|
27
|
+
total_available_length = intervals.size();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
std::optional<std::pair<int, int>> find_interval(int point, int length) {
|
|
31
|
+
auto it = intervals.find(point);
|
|
32
|
+
if (it != intervals.end()) {
|
|
33
|
+
int s = it->lower();
|
|
34
|
+
int e = it->upper();
|
|
35
|
+
if (e - point >= length) {
|
|
36
|
+
return std::make_pair(point, point + length);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return std::nullopt;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
int get_total_available_length() const {
|
|
43
|
+
return total_available_length;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void print_intervals() const {
|
|
47
|
+
std::cout << "Available intervals:\n";
|
|
48
|
+
for (const auto& interval : intervals) {
|
|
49
|
+
std::cout << "[" << interval.lower() << ", " << interval.upper() << ")\n";
|
|
50
|
+
}
|
|
51
|
+
std::cout << "Total available length: " << total_available_length << "\n";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
std::vector<std::pair<int, int>> get_intervals() const {
|
|
55
|
+
std::vector<std::pair<int, int>> result;
|
|
56
|
+
for (const auto& interval : intervals) {
|
|
57
|
+
result.emplace_back(interval.lower(), interval.upper());
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private:
|
|
63
|
+
boost::icl::interval_set<int> intervals;
|
|
64
|
+
int total_available_length;
|
|
65
|
+
};
|