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.
- checksums.yaml +7 -0
- data/CITATION.cff +67 -0
- data/README.md +340 -0
- data/Rakefile +232 -0
- data/disjoint_interval_tree.gemspec +42 -0
- data/ext/disjoint_interval_tree/dit.c +850 -0
- data/ext/disjoint_interval_tree/dit.h +244 -0
- data/ext/disjoint_interval_tree/dit_ruby.c +648 -0
- data/ext/disjoint_interval_tree/extconf.rb +17 -0
- data/lib/disjoint_interval_tree/version.rb +5 -0
- data/lib/disjoint_interval_tree.rb +52 -0
- data/test/c/Makefile +58 -0
- data/test/c/test_dit.c +1072 -0
- data/test/test_disjoint_interval_tree.rb +649 -0
- metadata +65 -0
|
@@ -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__ */
|