disjoint_interval_tree 0.1.0

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.
@@ -0,0 +1,244 @@
1
+ /*
2
+ ** Copyright 2025 INRIA
3
+ **
4
+ ** Contributors :
5
+ ** Romain PEREIRA, romain.pereira@inria.fr + rpereira@anl.gov
6
+ **
7
+ ** This software is governed by the CeCILL-C license under French law and
8
+ ** abiding by the rules of distribution of free software. You can use,
9
+ ** modify and/ or redistribute the software under the terms of the CeCILL-C
10
+ ** license as circulated by CEA, CNRS and INRIA at the following URL
11
+ ** "http://www.cecill.info".
12
+ **
13
+ ** The fact that you are presently reading this means that you have had
14
+ ** knowledge of the CeCILL-C license and that you accept its terms.
15
+ */
16
+
17
+ /*
18
+ ** dit - disjoint interval tree
19
+ **
20
+ ** A self-balancing (AVL) binary search tree whose nodes are pairwise
21
+ ** disjoint half-open intervals `[a..b[`, augmented - as in the lp-tree - with
22
+ ** the hull of the subtree they root (`augment.hull`), so that intersection
23
+ ** queries can prune entire subtrees in O(1).
24
+ **
25
+ ** Ordering
26
+ ** Since stored intervals are pairwise disjoint, ordering them by `a` also
27
+ ** orders them by `b`, and the tree is a plain BST on `a`. Searching for
28
+ ** the (unique) interval intersecting a point or a range is therefore a
29
+ ** deterministic O(log n) descent.
30
+ **
31
+ ** Contract
32
+ ** `dit_insert()` must not be given an interval intersecting an already
33
+ ** inserted one. That contract is *checked* at no extra cost during the
34
+ ** insertion descent: `DIT_OVERLAP` is returned and the tree is left
35
+ ** untouched.
36
+ **
37
+ ** Complexities, with `n` intervals stored and `k` intervals reported
38
+ ** dit_insert O(log n)
39
+ ** dit_intersect O(k + log n)
40
+ ** dit_remove O(k.log n)
41
+ ** dit_at O(log n)
42
+ ** dit_each O(n)
43
+ ** dit_check O(n)
44
+ */
45
+
46
+ #ifndef __DIT_H__
47
+ # define __DIT_H__
48
+
49
+ # include <inttypes.h>
50
+ # include <stddef.h>
51
+ # include <stdint.h>
52
+
53
+ # ifdef __cplusplus
54
+ extern "C" {
55
+ # endif
56
+
57
+ /* Interval bound type. Unsigned so that the whole address space is usable
58
+ * when intervals are memory ranges */
59
+ # ifndef DIT_VALUE_T
60
+ # define DIT_VALUE_T uint64_t
61
+ # define DIT_VALUE_MIN ((dit_value_t) 0)
62
+ # define DIT_VALUE_MAX ((dit_value_t) UINT64_MAX)
63
+ # define DIT_VALUE_FMT PRIu64
64
+ # endif /* DIT_VALUE_T */
65
+
66
+ typedef DIT_VALUE_T dit_value_t;
67
+
68
+ /* Internal consistency assertions. Those only ever fire on a dit bug, never on
69
+ * a caller mistake - caller mistakes are reported through `dit_status_t`.
70
+ * Define `DIT_ASSERT` to override, or `NDEBUG` to compile them out */
71
+ # ifndef DIT_ASSERT
72
+ # include <assert.h>
73
+ # define DIT_ASSERT(X) assert(X)
74
+ # endif /* DIT_ASSERT */
75
+
76
+ /* When set, `dit_check()` runs after every mutation: this makes every
77
+ * operation O(n) but catches structural corruptions at the exact call that
78
+ * caused them. Only meant for tests and debugging */
79
+ # ifndef DIT_PARANOID
80
+ # define DIT_PARANOID 0
81
+ # endif /* DIT_PARANOID */
82
+
83
+ /* Allocation hooks */
84
+ # ifndef DIT_MALLOC
85
+ # include <stdlib.h>
86
+ # define DIT_MALLOC(S) malloc(S)
87
+ # define DIT_FREE(P) free(P)
88
+ # endif /* DIT_MALLOC */
89
+
90
+ typedef enum
91
+ {
92
+ DIT_LEFT = 0,
93
+ DIT_RIGHT = 1,
94
+ DIT_N_CHILDREN = 2
95
+ } dit_direction_t;
96
+
97
+ typedef enum
98
+ {
99
+ /* the operation succeeded */
100
+ DIT_OK = 0,
101
+
102
+ /* `a >= b`: the interval is empty, nothing was done */
103
+ DIT_EMPTY = 1,
104
+
105
+ /* the interval intersects an already inserted one: contract violation,
106
+ * nothing was done */
107
+ DIT_OVERLAP = 2,
108
+
109
+ /* out of memory */
110
+ DIT_NOMEM = 3
111
+ } dit_status_t;
112
+
113
+ /* Everything a node caches about the subtree it roots.
114
+ *
115
+ * Augments are derived from the subtree only: they are recomputed bottom-up
116
+ * after every structural change, and are what makes the queries sublinear */
117
+ typedef struct dit_augment_s
118
+ {
119
+ /* the englobing interval of the subtree, i.e. the smallest interval
120
+ * including every interval stored in that subtree. Since stored intervals
121
+ * are pairwise disjoint and ordered, it spans exactly from the `a` of the
122
+ * leftmost descendant to the `b` of the rightmost one.
123
+ *
124
+ * This is what lets a query prune a whole subtree in O(1) */
125
+ struct {
126
+ dit_value_t a, b;
127
+ } hull;
128
+
129
+ /* height of the subtree, a leaf has 1 */
130
+ int32_t height;
131
+
132
+ /* number of nodes in the subtree. 32 bits caps a tree to 2^32 intervals,
133
+ * which already is 224 GiB of nodes */
134
+ uint32_t size;
135
+ } dit_augment_t;
136
+
137
+ typedef struct dit_node_s
138
+ {
139
+ /* the interval [a..b[ represented by this node, with a < b */
140
+ dit_value_t a, b;
141
+
142
+ /* children - `child[DIT_LEFT]` holds intervals entirely before `a`,
143
+ * `child[DIT_RIGHT]` holds intervals entirely after `b` */
144
+ union {
145
+ struct dit_node_s * child[DIT_N_CHILDREN];
146
+ struct {
147
+ struct dit_node_s * left;
148
+ struct dit_node_s * right;
149
+ };
150
+ };
151
+
152
+ /* what this node caches about the subtree it roots */
153
+ dit_augment_t augment;
154
+ } dit_node_t;
155
+
156
+ typedef struct dit_s
157
+ {
158
+ dit_node_t * root;
159
+
160
+ /* number of intervals stored */
161
+ size_t n;
162
+
163
+ /* >0 while a traversal is in progress: mutating the tree from within an
164
+ * `dit_intersect()` or `dit_each()` callback is forbidden and detected */
165
+ int traversing;
166
+ } dit_t;
167
+
168
+ /* Interval callback.
169
+ * `[a..b[` is the stored interval, `user` the opaque pointer given to the
170
+ * traversal. Return 0 to keep going, non-zero to stop the traversal early -
171
+ * that value is then returned by the traversal routine */
172
+ typedef int (*dit_cb_t)(dit_value_t a, dit_value_t b, void * user);
173
+
174
+ /* Initialize an empty tree. `dit_t` may also be zero-initialized */
175
+ void dit_init(dit_t * tree);
176
+
177
+ /* Free every node. The tree is left initialized and empty */
178
+ void dit_clear(dit_t * tree);
179
+
180
+ /* Alias of `dit_clear()`, for symmetry with `dit_init()` */
181
+ void dit_destroy(dit_t * tree);
182
+
183
+ /* Number of intervals stored */
184
+ size_t dit_size(const dit_t * tree);
185
+
186
+ /* 1 if no interval is stored, 0 otherwise */
187
+ int dit_empty(const dit_t * tree);
188
+
189
+ /* Height of the tree, 0 if empty */
190
+ int dit_height(const dit_t * tree);
191
+
192
+ /* The englobing interval of the whole tree, read from the root augment.
193
+ * Returns 1 and writes it to `a` and `b`, or returns 0 and leaves them
194
+ * untouched when the tree is empty. O(1) */
195
+ int dit_hull(const dit_t * tree, dit_value_t * a, dit_value_t * b);
196
+
197
+ /* Insert `[a..b[`.
198
+ * Returns DIT_OK, DIT_EMPTY if `a >= b`, DIT_OVERLAP if `[a..b[` intersects an
199
+ * already inserted interval, DIT_NOMEM on allocation failure. The tree is left
200
+ * unchanged unless DIT_OK is returned */
201
+ dit_status_t dit_insert(dit_t * tree, dit_value_t a, dit_value_t b);
202
+
203
+ /* Return the stored interval intersecting `[a..b[`, or NULL if there is none.
204
+ * If several intervals intersect `[a..b[`, which one is returned is
205
+ * unspecified. The returned node is owned by the tree and invalidated by the
206
+ * next mutation */
207
+ const dit_node_t * dit_intersecting(const dit_t * tree, dit_value_t a, dit_value_t b);
208
+
209
+ /* Return the stored interval containing the point `x`, or NULL */
210
+ const dit_node_t * dit_at(const dit_t * tree, dit_value_t x);
211
+
212
+ /* 1 if at least one stored interval intersects `[a..b[`, 0 otherwise */
213
+ int dit_intersect_p(const dit_t * tree, dit_value_t a, dit_value_t b);
214
+
215
+ /* Invoke `cb` on every stored interval intersecting `[a..b[`, in increasing
216
+ * order. Returns 0, or the first non-zero value returned by `cb`.
217
+ * `cb` must not mutate the tree */
218
+ int dit_intersect(dit_t * tree, dit_value_t a, dit_value_t b, dit_cb_t cb, void * user);
219
+
220
+ /* Invoke `cb` on every stored interval, in increasing order. Returns 0, or the
221
+ * first non-zero value returned by `cb`. `cb` must not mutate the tree */
222
+ int dit_each(dit_t * tree, dit_cb_t cb, void * user);
223
+
224
+ /* Remove every stored interval intersecting `[a..b[`. Returns how many
225
+ * intervals were removed. Removed intervals are removed as a whole: an
226
+ * interval merely overlapping `[a..b[` is *not* split */
227
+ size_t dit_remove(dit_t * tree, dit_value_t a, dit_value_t b);
228
+
229
+ /* Verify every structural invariant of the tree.
230
+ * Returns 0 if the tree is coherent. Otherwise returns a non-zero value and,
231
+ * if `err` is not NULL, writes a NUL-terminated description of the first
232
+ * violation found into it.
233
+ * This never aborts, so that it can be used from test suites */
234
+ int dit_check(const dit_t * tree, char * err, size_t errlen);
235
+
236
+ /* Dump the tree to `f` (a `FILE *`, void * here to avoid <stdio.h>) in
237
+ * graphviz dot format */
238
+ void dit_dump_dot(const dit_t * tree, void * f);
239
+
240
+ # ifdef __cplusplus
241
+ }
242
+ # endif
243
+
244
+ #endif /* __DIT_H__ */