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,850 @@
|
|
|
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
|
+
#include "dit.h"
|
|
18
|
+
|
|
19
|
+
#include <inttypes.h>
|
|
20
|
+
#include <stdarg.h>
|
|
21
|
+
#include <stdio.h>
|
|
22
|
+
#include <string.h>
|
|
23
|
+
|
|
24
|
+
#define DIT_MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
|
|
25
|
+
#define DIT_MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
|
|
26
|
+
|
|
27
|
+
/* [a..b[ and [c..d[ intersect - both must be non-empty */
|
|
28
|
+
#define DIT_INTERSECTS(A, B, C, D) ((A) < (D) && (C) < (B))
|
|
29
|
+
|
|
30
|
+
/* run the full coherency check after each mutation, see `DIT_PARANOID` */
|
|
31
|
+
#if DIT_PARANOID
|
|
32
|
+
# define DIT_CHECK_PARANOID(T) \
|
|
33
|
+
do { \
|
|
34
|
+
char __err[512]; \
|
|
35
|
+
if (dit_check((T), __err, sizeof(__err))) \
|
|
36
|
+
{ \
|
|
37
|
+
fprintf(stderr, "%s:%d: incoherent tree in `%s`: %s\n", \
|
|
38
|
+
__FILE__, __LINE__, __func__, __err); \
|
|
39
|
+
DIT_ASSERT(0 && "incoherent tree"); \
|
|
40
|
+
} \
|
|
41
|
+
} while (0)
|
|
42
|
+
#else /* DIT_PARANOID */
|
|
43
|
+
# define DIT_CHECK_PARANOID(T) ((void) 0)
|
|
44
|
+
#endif /* DIT_PARANOID */
|
|
45
|
+
|
|
46
|
+
////////////
|
|
47
|
+
// NODES //
|
|
48
|
+
////////////
|
|
49
|
+
|
|
50
|
+
static inline int32_t
|
|
51
|
+
dit_node_height(const dit_node_t * node)
|
|
52
|
+
{
|
|
53
|
+
return node ? node->augment.height : 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static inline uint32_t
|
|
57
|
+
dit_node_size(const dit_node_t * node)
|
|
58
|
+
{
|
|
59
|
+
return node ? node->augment.size : 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* balance factor: >0 means the left subtree is the deepest */
|
|
63
|
+
static inline int32_t
|
|
64
|
+
dit_node_balance(const dit_node_t * node)
|
|
65
|
+
{
|
|
66
|
+
DIT_ASSERT(node);
|
|
67
|
+
return dit_node_height(node->left) - dit_node_height(node->right);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Recompute every augment of `node` from its - assumed up to date - children.
|
|
71
|
+
* Must be called bottom-up after any structural change */
|
|
72
|
+
static inline void
|
|
73
|
+
dit_node_refresh_augment(dit_node_t * node)
|
|
74
|
+
{
|
|
75
|
+
DIT_ASSERT(node);
|
|
76
|
+
DIT_ASSERT(node->a < node->b);
|
|
77
|
+
|
|
78
|
+
const dit_node_t * l = node->left;
|
|
79
|
+
const dit_node_t * r = node->right;
|
|
80
|
+
|
|
81
|
+
const int32_t hl = dit_node_height(l);
|
|
82
|
+
const int32_t hr = dit_node_height(r);
|
|
83
|
+
|
|
84
|
+
node->augment.height = 1 + DIT_MAX(hl, hr);
|
|
85
|
+
node->augment.size = 1 + dit_node_size(l) + dit_node_size(r);
|
|
86
|
+
|
|
87
|
+
/* englobing interval of the subtree, as the lp-tree `includes.hyperrect` */
|
|
88
|
+
node->augment.hull.a = node->a;
|
|
89
|
+
node->augment.hull.b = node->b;
|
|
90
|
+
if (l)
|
|
91
|
+
{
|
|
92
|
+
node->augment.hull.a = DIT_MIN(node->augment.hull.a, l->augment.hull.a);
|
|
93
|
+
node->augment.hull.b = DIT_MAX(node->augment.hull.b, l->augment.hull.b);
|
|
94
|
+
}
|
|
95
|
+
if (r)
|
|
96
|
+
{
|
|
97
|
+
node->augment.hull.a = DIT_MIN(node->augment.hull.a, r->augment.hull.a);
|
|
98
|
+
node->augment.hull.b = DIT_MAX(node->augment.hull.b, r->augment.hull.b);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* intervals being disjoint and ordered on `a`, they are also ordered on
|
|
102
|
+
* `b`: the hull is exactly [leftmost->a .. rightmost->b[ */
|
|
103
|
+
DIT_ASSERT(node->augment.hull.a == (l ? l->augment.hull.a : node->a));
|
|
104
|
+
DIT_ASSERT(node->augment.hull.b == (r ? r->augment.hull.b : node->b));
|
|
105
|
+
DIT_ASSERT(node->augment.hull.a <= node->a);
|
|
106
|
+
DIT_ASSERT(node->b <= node->augment.hull.b);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static inline dit_node_t *
|
|
110
|
+
dit_node_new(dit_value_t a, dit_value_t b)
|
|
111
|
+
{
|
|
112
|
+
DIT_ASSERT(a < b);
|
|
113
|
+
|
|
114
|
+
dit_node_t * node = (dit_node_t *) DIT_MALLOC(sizeof(dit_node_t));
|
|
115
|
+
if (node == NULL)
|
|
116
|
+
return NULL;
|
|
117
|
+
|
|
118
|
+
node->a = a;
|
|
119
|
+
node->b = b;
|
|
120
|
+
node->left = NULL;
|
|
121
|
+
node->right = NULL;
|
|
122
|
+
node->augment.hull.a = a;
|
|
123
|
+
node->augment.hull.b = b;
|
|
124
|
+
node->augment.height = 1;
|
|
125
|
+
node->augment.size = 1;
|
|
126
|
+
|
|
127
|
+
return node;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static void
|
|
131
|
+
dit_node_release(dit_node_t * node)
|
|
132
|
+
{
|
|
133
|
+
if (node == NULL)
|
|
134
|
+
return ;
|
|
135
|
+
dit_node_release(node->left);
|
|
136
|
+
dit_node_release(node->right);
|
|
137
|
+
DIT_FREE(node);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
///////////////
|
|
141
|
+
// ROTATIONS //
|
|
142
|
+
///////////////
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* A B
|
|
146
|
+
* / \ / \
|
|
147
|
+
* B C -> D A
|
|
148
|
+
* / \ / \
|
|
149
|
+
* D E E C
|
|
150
|
+
*
|
|
151
|
+
* Returns the new subtree root `B`
|
|
152
|
+
*/
|
|
153
|
+
static inline dit_node_t *
|
|
154
|
+
dit_rotate_right(dit_node_t * A)
|
|
155
|
+
{
|
|
156
|
+
DIT_ASSERT(A && A->left);
|
|
157
|
+
|
|
158
|
+
dit_node_t * B = A->left;
|
|
159
|
+
dit_node_t * E = B->right;
|
|
160
|
+
|
|
161
|
+
B->right = A;
|
|
162
|
+
A->left = E;
|
|
163
|
+
|
|
164
|
+
dit_node_refresh_augment(A);
|
|
165
|
+
dit_node_refresh_augment(B);
|
|
166
|
+
|
|
167
|
+
return B;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* C A
|
|
172
|
+
* / \ / \
|
|
173
|
+
* A E <- B C
|
|
174
|
+
* / \ / \
|
|
175
|
+
* B D D E
|
|
176
|
+
*
|
|
177
|
+
* Returns the new subtree root `C`
|
|
178
|
+
*/
|
|
179
|
+
static inline dit_node_t *
|
|
180
|
+
dit_rotate_left(dit_node_t * A)
|
|
181
|
+
{
|
|
182
|
+
DIT_ASSERT(A && A->right);
|
|
183
|
+
|
|
184
|
+
dit_node_t * C = A->right;
|
|
185
|
+
dit_node_t * D = C->left;
|
|
186
|
+
|
|
187
|
+
C->left = A;
|
|
188
|
+
A->right = D;
|
|
189
|
+
|
|
190
|
+
dit_node_refresh_augment(A);
|
|
191
|
+
dit_node_refresh_augment(C);
|
|
192
|
+
|
|
193
|
+
return C;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Restore the AVL invariant on `node`, whose augments must already be up to
|
|
197
|
+
* date and whose children must be valid AVL subtrees. Returns the new root of
|
|
198
|
+
* the subtree */
|
|
199
|
+
static inline dit_node_t *
|
|
200
|
+
dit_rebalance(dit_node_t * node)
|
|
201
|
+
{
|
|
202
|
+
DIT_ASSERT(node);
|
|
203
|
+
|
|
204
|
+
const int32_t bf = dit_node_balance(node);
|
|
205
|
+
|
|
206
|
+
/* a single insertion/deletion can only break the balance by 1 */
|
|
207
|
+
DIT_ASSERT(-2 <= bf && bf <= 2);
|
|
208
|
+
|
|
209
|
+
if (bf > 1)
|
|
210
|
+
{
|
|
211
|
+
DIT_ASSERT(node->left);
|
|
212
|
+
if (dit_node_balance(node->left) < 0)
|
|
213
|
+
node->left = dit_rotate_left(node->left);
|
|
214
|
+
return dit_rotate_right(node);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (bf < -1)
|
|
218
|
+
{
|
|
219
|
+
DIT_ASSERT(node->right);
|
|
220
|
+
if (dit_node_balance(node->right) > 0)
|
|
221
|
+
node->right = dit_rotate_right(node->right);
|
|
222
|
+
return dit_rotate_left(node);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return node;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
//////////////////
|
|
229
|
+
// CONSTRUCTION //
|
|
230
|
+
//////////////////
|
|
231
|
+
|
|
232
|
+
void
|
|
233
|
+
dit_init(dit_t * tree)
|
|
234
|
+
{
|
|
235
|
+
DIT_ASSERT(tree);
|
|
236
|
+
tree->root = NULL;
|
|
237
|
+
tree->n = 0;
|
|
238
|
+
tree->traversing = 0;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
void
|
|
242
|
+
dit_clear(dit_t * tree)
|
|
243
|
+
{
|
|
244
|
+
DIT_ASSERT(tree);
|
|
245
|
+
DIT_ASSERT(tree->traversing == 0 && "cannot mutate the tree while traversing it");
|
|
246
|
+
|
|
247
|
+
dit_node_release(tree->root);
|
|
248
|
+
tree->root = NULL;
|
|
249
|
+
tree->n = 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
void
|
|
253
|
+
dit_destroy(dit_t * tree)
|
|
254
|
+
{
|
|
255
|
+
dit_clear(tree);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
size_t
|
|
259
|
+
dit_size(const dit_t * tree)
|
|
260
|
+
{
|
|
261
|
+
DIT_ASSERT(tree);
|
|
262
|
+
DIT_ASSERT(tree->n == (size_t) dit_node_size(tree->root));
|
|
263
|
+
return tree->n;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
int
|
|
267
|
+
dit_empty(const dit_t * tree)
|
|
268
|
+
{
|
|
269
|
+
DIT_ASSERT(tree);
|
|
270
|
+
return tree->root == NULL;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
int
|
|
274
|
+
dit_height(const dit_t * tree)
|
|
275
|
+
{
|
|
276
|
+
DIT_ASSERT(tree);
|
|
277
|
+
return (int) dit_node_height(tree->root);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
int
|
|
281
|
+
dit_hull(const dit_t * tree, dit_value_t * a, dit_value_t * b)
|
|
282
|
+
{
|
|
283
|
+
DIT_ASSERT(tree);
|
|
284
|
+
DIT_ASSERT(a && b);
|
|
285
|
+
|
|
286
|
+
if (tree->root == NULL)
|
|
287
|
+
return 0;
|
|
288
|
+
|
|
289
|
+
/* the root augment already englobes every stored interval */
|
|
290
|
+
*a = tree->root->augment.hull.a;
|
|
291
|
+
*b = tree->root->augment.hull.b;
|
|
292
|
+
|
|
293
|
+
DIT_ASSERT(*a < *b);
|
|
294
|
+
|
|
295
|
+
return 1;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
////////////
|
|
299
|
+
// INSERT //
|
|
300
|
+
////////////
|
|
301
|
+
|
|
302
|
+
static dit_node_t *
|
|
303
|
+
dit_insert_from(
|
|
304
|
+
dit_node_t * node,
|
|
305
|
+
dit_value_t a,
|
|
306
|
+
dit_value_t b,
|
|
307
|
+
dit_status_t * status
|
|
308
|
+
) {
|
|
309
|
+
if (node == NULL)
|
|
310
|
+
{
|
|
311
|
+
dit_node_t * created = dit_node_new(a, b);
|
|
312
|
+
*status = created ? DIT_OK : DIT_NOMEM;
|
|
313
|
+
return created;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/* case (1) - [a..b[ entirely before this node */
|
|
317
|
+
if (b <= node->a)
|
|
318
|
+
node->left = dit_insert_from(node->left, a, b, status);
|
|
319
|
+
|
|
320
|
+
/* case (2) - [a..b[ entirely after this node */
|
|
321
|
+
else if (a >= node->b)
|
|
322
|
+
node->right = dit_insert_from(node->right, a, b, status);
|
|
323
|
+
|
|
324
|
+
/* case (3) - contract violation, [a..b[ intersect this node */
|
|
325
|
+
else
|
|
326
|
+
{
|
|
327
|
+
DIT_ASSERT(DIT_INTERSECTS(a, b, node->a, node->b));
|
|
328
|
+
*status = DIT_OVERLAP;
|
|
329
|
+
return node;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/* nothing was inserted below: no augment to refresh, no rebalancing */
|
|
333
|
+
if (*status != DIT_OK)
|
|
334
|
+
return node;
|
|
335
|
+
|
|
336
|
+
dit_node_refresh_augment(node);
|
|
337
|
+
return dit_rebalance(node);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
dit_status_t
|
|
341
|
+
dit_insert(dit_t * tree, dit_value_t a, dit_value_t b)
|
|
342
|
+
{
|
|
343
|
+
DIT_ASSERT(tree);
|
|
344
|
+
DIT_ASSERT(tree->traversing == 0 && "cannot mutate the tree while traversing it");
|
|
345
|
+
|
|
346
|
+
if (a >= b)
|
|
347
|
+
return DIT_EMPTY;
|
|
348
|
+
|
|
349
|
+
dit_status_t status = DIT_OK;
|
|
350
|
+
dit_node_t * root = dit_insert_from(tree->root, a, b, &status);
|
|
351
|
+
|
|
352
|
+
if (status != DIT_OK)
|
|
353
|
+
return status;
|
|
354
|
+
|
|
355
|
+
tree->root = root;
|
|
356
|
+
tree->n += 1;
|
|
357
|
+
|
|
358
|
+
DIT_ASSERT(tree->n == (size_t) dit_node_size(tree->root));
|
|
359
|
+
DIT_CHECK_PARANOID(tree);
|
|
360
|
+
|
|
361
|
+
return DIT_OK;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
////////////
|
|
365
|
+
// SEARCH //
|
|
366
|
+
////////////
|
|
367
|
+
|
|
368
|
+
/* Intervals being pairwise disjoint, they are totally ordered: the descent
|
|
369
|
+
* towards an interval intersecting [a..b[ is deterministic */
|
|
370
|
+
static inline dit_node_t *
|
|
371
|
+
dit_intersecting_from(dit_node_t * node, dit_value_t a, dit_value_t b)
|
|
372
|
+
{
|
|
373
|
+
while (node)
|
|
374
|
+
{
|
|
375
|
+
if (b <= node->a)
|
|
376
|
+
node = node->left;
|
|
377
|
+
else if (a >= node->b)
|
|
378
|
+
node = node->right;
|
|
379
|
+
else
|
|
380
|
+
{
|
|
381
|
+
DIT_ASSERT(DIT_INTERSECTS(a, b, node->a, node->b));
|
|
382
|
+
return node;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return NULL;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const dit_node_t *
|
|
389
|
+
dit_intersecting(const dit_t * tree, dit_value_t a, dit_value_t b)
|
|
390
|
+
{
|
|
391
|
+
DIT_ASSERT(tree);
|
|
392
|
+
if (a >= b)
|
|
393
|
+
return NULL;
|
|
394
|
+
return dit_intersecting_from(tree->root, a, b);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const dit_node_t *
|
|
398
|
+
dit_at(const dit_t * tree, dit_value_t x)
|
|
399
|
+
{
|
|
400
|
+
DIT_ASSERT(tree);
|
|
401
|
+
|
|
402
|
+
/* intervals are half-open and bounded by DIT_VALUE_MAX, so no stored
|
|
403
|
+
* interval [a..b[ may ever contain DIT_VALUE_MAX */
|
|
404
|
+
if (x == DIT_VALUE_MAX)
|
|
405
|
+
return NULL;
|
|
406
|
+
|
|
407
|
+
return dit_intersecting_from(tree->root, x, x + 1);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
int
|
|
411
|
+
dit_intersect_p(const dit_t * tree, dit_value_t a, dit_value_t b)
|
|
412
|
+
{
|
|
413
|
+
return dit_intersecting(tree, a, b) != NULL;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
///////////////
|
|
417
|
+
// TRAVERSAL //
|
|
418
|
+
///////////////
|
|
419
|
+
|
|
420
|
+
static int
|
|
421
|
+
dit_intersect_from(
|
|
422
|
+
dit_node_t * node,
|
|
423
|
+
dit_value_t a,
|
|
424
|
+
dit_value_t b,
|
|
425
|
+
dit_cb_t cb,
|
|
426
|
+
void * user
|
|
427
|
+
) {
|
|
428
|
+
if (node == NULL)
|
|
429
|
+
return 0;
|
|
430
|
+
|
|
431
|
+
/* augment pruning: no interval of this subtree can intersect [a..b[ */
|
|
432
|
+
if (!DIT_INTERSECTS(a, b, node->augment.hull.a, node->augment.hull.b))
|
|
433
|
+
return 0;
|
|
434
|
+
|
|
435
|
+
int r;
|
|
436
|
+
|
|
437
|
+
/* in-order traversal, so that intervals are reported in increasing order */
|
|
438
|
+
if ((r = dit_intersect_from(node->left, a, b, cb, user)) != 0)
|
|
439
|
+
return r;
|
|
440
|
+
|
|
441
|
+
if (DIT_INTERSECTS(a, b, node->a, node->b))
|
|
442
|
+
{
|
|
443
|
+
if ((r = cb(node->a, node->b, user)) != 0)
|
|
444
|
+
return r;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return dit_intersect_from(node->right, a, b, cb, user);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
int
|
|
451
|
+
dit_intersect(dit_t * tree, dit_value_t a, dit_value_t b, dit_cb_t cb, void * user)
|
|
452
|
+
{
|
|
453
|
+
DIT_ASSERT(tree);
|
|
454
|
+
DIT_ASSERT(cb);
|
|
455
|
+
|
|
456
|
+
if (a >= b)
|
|
457
|
+
return 0;
|
|
458
|
+
|
|
459
|
+
tree->traversing += 1;
|
|
460
|
+
const int r = dit_intersect_from(tree->root, a, b, cb, user);
|
|
461
|
+
tree->traversing -= 1;
|
|
462
|
+
|
|
463
|
+
DIT_ASSERT(tree->traversing >= 0);
|
|
464
|
+
|
|
465
|
+
return r;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
static int
|
|
469
|
+
dit_each_from(dit_node_t * node, dit_cb_t cb, void * user)
|
|
470
|
+
{
|
|
471
|
+
if (node == NULL)
|
|
472
|
+
return 0;
|
|
473
|
+
|
|
474
|
+
int r;
|
|
475
|
+
|
|
476
|
+
if ((r = dit_each_from(node->left, cb, user)) != 0)
|
|
477
|
+
return r;
|
|
478
|
+
|
|
479
|
+
if ((r = cb(node->a, node->b, user)) != 0)
|
|
480
|
+
return r;
|
|
481
|
+
|
|
482
|
+
return dit_each_from(node->right, cb, user);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
int
|
|
486
|
+
dit_each(dit_t * tree, dit_cb_t cb, void * user)
|
|
487
|
+
{
|
|
488
|
+
DIT_ASSERT(tree);
|
|
489
|
+
DIT_ASSERT(cb);
|
|
490
|
+
|
|
491
|
+
tree->traversing += 1;
|
|
492
|
+
const int r = dit_each_from(tree->root, cb, user);
|
|
493
|
+
tree->traversing -= 1;
|
|
494
|
+
|
|
495
|
+
DIT_ASSERT(tree->traversing >= 0);
|
|
496
|
+
|
|
497
|
+
return r;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
////////////
|
|
501
|
+
// REMOVE //
|
|
502
|
+
////////////
|
|
503
|
+
|
|
504
|
+
/* Detach the leftmost node of the subtree, store it into `*out`, and return
|
|
505
|
+
* the new subtree root */
|
|
506
|
+
static dit_node_t *
|
|
507
|
+
dit_detach_min(dit_node_t * node, dit_node_t ** out)
|
|
508
|
+
{
|
|
509
|
+
DIT_ASSERT(node);
|
|
510
|
+
|
|
511
|
+
if (node->left == NULL)
|
|
512
|
+
{
|
|
513
|
+
*out = node;
|
|
514
|
+
return node->right;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
node->left = dit_detach_min(node->left, out);
|
|
518
|
+
dit_node_refresh_augment(node);
|
|
519
|
+
return dit_rebalance(node);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* Remove the node whose interval starts at `key`, which must exist, and return
|
|
523
|
+
* the new subtree root */
|
|
524
|
+
static dit_node_t *
|
|
525
|
+
dit_remove_from(dit_node_t * node, dit_value_t key)
|
|
526
|
+
{
|
|
527
|
+
DIT_ASSERT(node && "removing an interval that is not in the tree");
|
|
528
|
+
|
|
529
|
+
if (key < node->a)
|
|
530
|
+
node->left = dit_remove_from(node->left, key);
|
|
531
|
+
else if (key > node->a)
|
|
532
|
+
node->right = dit_remove_from(node->right, key);
|
|
533
|
+
else
|
|
534
|
+
{
|
|
535
|
+
/* at most one child: splice it in */
|
|
536
|
+
if (node->left == NULL || node->right == NULL)
|
|
537
|
+
{
|
|
538
|
+
dit_node_t * child = node->left ? node->left : node->right;
|
|
539
|
+
|
|
540
|
+
/* an AVL node with a single child has a leaf as child */
|
|
541
|
+
DIT_ASSERT(child == NULL || (child->left == NULL && child->right == NULL));
|
|
542
|
+
|
|
543
|
+
DIT_FREE(node);
|
|
544
|
+
return child;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* two children: replace the interval with its in-order successor's,
|
|
548
|
+
* then remove that successor from the right subtree */
|
|
549
|
+
dit_node_t * successor;
|
|
550
|
+
node->right = dit_detach_min(node->right, &successor);
|
|
551
|
+
|
|
552
|
+
DIT_ASSERT(successor && successor->left == NULL);
|
|
553
|
+
DIT_ASSERT(node->a < successor->a);
|
|
554
|
+
|
|
555
|
+
node->a = successor->a;
|
|
556
|
+
node->b = successor->b;
|
|
557
|
+
|
|
558
|
+
DIT_FREE(successor);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
dit_node_refresh_augment(node);
|
|
562
|
+
return dit_rebalance(node);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
size_t
|
|
566
|
+
dit_remove(dit_t * tree, dit_value_t a, dit_value_t b)
|
|
567
|
+
{
|
|
568
|
+
DIT_ASSERT(tree);
|
|
569
|
+
DIT_ASSERT(tree->traversing == 0 && "cannot mutate the tree while traversing it");
|
|
570
|
+
|
|
571
|
+
if (a >= b)
|
|
572
|
+
return 0;
|
|
573
|
+
|
|
574
|
+
size_t n = 0;
|
|
575
|
+
|
|
576
|
+
/* each iteration is a O(log n) descent plus a O(log n) deletion */
|
|
577
|
+
for (;;)
|
|
578
|
+
{
|
|
579
|
+
dit_node_t * node = dit_intersecting_from(tree->root, a, b);
|
|
580
|
+
if (node == NULL)
|
|
581
|
+
break ;
|
|
582
|
+
|
|
583
|
+
tree->root = dit_remove_from(tree->root, node->a);
|
|
584
|
+
|
|
585
|
+
DIT_ASSERT(tree->n > 0);
|
|
586
|
+
tree->n -= 1;
|
|
587
|
+
n += 1;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
DIT_ASSERT(tree->n == (size_t) dit_node_size(tree->root));
|
|
591
|
+
DIT_ASSERT(dit_intersecting_from(tree->root, a, b) == NULL);
|
|
592
|
+
DIT_CHECK_PARANOID(tree);
|
|
593
|
+
|
|
594
|
+
return n;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
///////////////////////
|
|
598
|
+
// COHERENCY CHECKS //
|
|
599
|
+
///////////////////////
|
|
600
|
+
|
|
601
|
+
typedef struct
|
|
602
|
+
{
|
|
603
|
+
char * err;
|
|
604
|
+
size_t errlen;
|
|
605
|
+
int failed;
|
|
606
|
+
|
|
607
|
+
/* number of nodes visited so far */
|
|
608
|
+
size_t count;
|
|
609
|
+
|
|
610
|
+
/* end of the previously visited interval, in-order */
|
|
611
|
+
dit_value_t prev_b;
|
|
612
|
+
int has_prev;
|
|
613
|
+
} dit_check_t;
|
|
614
|
+
|
|
615
|
+
#ifdef __GNUC__
|
|
616
|
+
__attribute__((format(printf, 2, 3)))
|
|
617
|
+
#endif /* __GNUC__ */
|
|
618
|
+
static void
|
|
619
|
+
dit_check_fail(dit_check_t * ctx, const char * fmt, ...)
|
|
620
|
+
{
|
|
621
|
+
if (ctx->failed)
|
|
622
|
+
return ;
|
|
623
|
+
|
|
624
|
+
ctx->failed = 1;
|
|
625
|
+
|
|
626
|
+
if (ctx->err && ctx->errlen)
|
|
627
|
+
{
|
|
628
|
+
va_list ap;
|
|
629
|
+
va_start(ap, fmt);
|
|
630
|
+
vsnprintf(ctx->err, ctx->errlen, fmt, ap);
|
|
631
|
+
va_end(ap);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
#define DIT_CHECK_THAT(CTX, COND, ...) \
|
|
636
|
+
do { \
|
|
637
|
+
if (!(COND)) \
|
|
638
|
+
{ \
|
|
639
|
+
dit_check_fail(CTX, __VA_ARGS__); \
|
|
640
|
+
return ; \
|
|
641
|
+
} \
|
|
642
|
+
} while (0)
|
|
643
|
+
|
|
644
|
+
/* Recursively check `node`, knowing every interval of that subtree must be
|
|
645
|
+
* included in [lo..hi[ */
|
|
646
|
+
static void
|
|
647
|
+
dit_check_from(
|
|
648
|
+
dit_check_t * ctx,
|
|
649
|
+
const dit_node_t * node,
|
|
650
|
+
dit_value_t lo,
|
|
651
|
+
dit_value_t hi,
|
|
652
|
+
int depth
|
|
653
|
+
) {
|
|
654
|
+
if (node == NULL || ctx->failed)
|
|
655
|
+
return ;
|
|
656
|
+
|
|
657
|
+
DIT_CHECK_THAT(ctx, depth < 128,
|
|
658
|
+
"tree is deeper than 128, it is probably cyclic");
|
|
659
|
+
|
|
660
|
+
/* 1. intervals are non-empty */
|
|
661
|
+
DIT_CHECK_THAT(ctx, node->a < node->b,
|
|
662
|
+
"node [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ is empty",
|
|
663
|
+
node->a, node->b);
|
|
664
|
+
|
|
665
|
+
/* 2. binary search tree ordering, which - given the bounds narrowing at
|
|
666
|
+
* each level - also proves that stored intervals are pairwise disjoint */
|
|
667
|
+
DIT_CHECK_THAT(ctx, lo <= node->a && node->b <= hi,
|
|
668
|
+
"node [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ is not within its "
|
|
669
|
+
"expected bounds [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[",
|
|
670
|
+
node->a, node->b, lo, hi);
|
|
671
|
+
|
|
672
|
+
dit_check_from(ctx, node->left, lo, node->a, depth + 1);
|
|
673
|
+
if (ctx->failed)
|
|
674
|
+
return ;
|
|
675
|
+
|
|
676
|
+
/* 3. in-order traversal yields increasing, non-overlapping intervals */
|
|
677
|
+
if (ctx->has_prev)
|
|
678
|
+
{
|
|
679
|
+
DIT_CHECK_THAT(ctx, ctx->prev_b <= node->a,
|
|
680
|
+
"node [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ overlaps its "
|
|
681
|
+
"in-order predecessor, which ends at %" DIT_VALUE_FMT,
|
|
682
|
+
node->a, node->b, ctx->prev_b);
|
|
683
|
+
}
|
|
684
|
+
ctx->has_prev = 1;
|
|
685
|
+
ctx->prev_b = node->b;
|
|
686
|
+
ctx->count += 1;
|
|
687
|
+
|
|
688
|
+
dit_check_from(ctx, node->right, node->b, hi, depth + 1);
|
|
689
|
+
if (ctx->failed)
|
|
690
|
+
return ;
|
|
691
|
+
|
|
692
|
+
const dit_node_t * l = node->left;
|
|
693
|
+
const dit_node_t * r = node->right;
|
|
694
|
+
|
|
695
|
+
/* 4. height augment */
|
|
696
|
+
const int32_t hl = dit_node_height(l);
|
|
697
|
+
const int32_t hr = dit_node_height(r);
|
|
698
|
+
DIT_CHECK_THAT(ctx, node->augment.height == 1 + DIT_MAX(hl, hr),
|
|
699
|
+
"node [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ has height %d "
|
|
700
|
+
"instead of %d",
|
|
701
|
+
node->a, node->b, (int) node->augment.height, (int) (1 + DIT_MAX(hl, hr)));
|
|
702
|
+
|
|
703
|
+
/* 5. AVL balance */
|
|
704
|
+
DIT_CHECK_THAT(ctx, -1 <= hl - hr && hl - hr <= 1,
|
|
705
|
+
"node [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ is unbalanced "
|
|
706
|
+
"(left height %d, right height %d)",
|
|
707
|
+
node->a, node->b, (int) hl, (int) hr);
|
|
708
|
+
|
|
709
|
+
/* 6. size augment */
|
|
710
|
+
const uint32_t size = 1 + dit_node_size(l) + dit_node_size(r);
|
|
711
|
+
DIT_CHECK_THAT(ctx, node->augment.size == size,
|
|
712
|
+
"node [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ has size %u "
|
|
713
|
+
"instead of %u",
|
|
714
|
+
node->a, node->b, node->augment.size, size);
|
|
715
|
+
|
|
716
|
+
/* 7. the hull augment englobes the whole subtree */
|
|
717
|
+
dit_value_t ha = node->a;
|
|
718
|
+
dit_value_t hb = node->b;
|
|
719
|
+
if (l)
|
|
720
|
+
{
|
|
721
|
+
ha = DIT_MIN(ha, l->augment.hull.a);
|
|
722
|
+
hb = DIT_MAX(hb, l->augment.hull.b);
|
|
723
|
+
}
|
|
724
|
+
if (r)
|
|
725
|
+
{
|
|
726
|
+
ha = DIT_MIN(ha, r->augment.hull.a);
|
|
727
|
+
hb = DIT_MAX(hb, r->augment.hull.b);
|
|
728
|
+
}
|
|
729
|
+
DIT_CHECK_THAT(ctx, node->augment.hull.a == ha && node->augment.hull.b == hb,
|
|
730
|
+
"node [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ has hull "
|
|
731
|
+
"[%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ instead of "
|
|
732
|
+
"[%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[",
|
|
733
|
+
node->a, node->b, node->augment.hull.a, node->augment.hull.b, ha, hb);
|
|
734
|
+
|
|
735
|
+
/* 8. the hull spans exactly from the leftmost to the rightmost interval */
|
|
736
|
+
DIT_CHECK_THAT(ctx, node->augment.hull.a == (l ? l->augment.hull.a : node->a),
|
|
737
|
+
"node [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ does not start its "
|
|
738
|
+
"hull at its leftmost descendant",
|
|
739
|
+
node->a, node->b);
|
|
740
|
+
DIT_CHECK_THAT(ctx, node->augment.hull.b == (r ? r->augment.hull.b : node->b),
|
|
741
|
+
"node [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[ does not end its "
|
|
742
|
+
"hull at its rightmost descendant",
|
|
743
|
+
node->a, node->b);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
int
|
|
747
|
+
dit_check(const dit_t * tree, char * err, size_t errlen)
|
|
748
|
+
{
|
|
749
|
+
if (err && errlen)
|
|
750
|
+
err[0] = '\0';
|
|
751
|
+
|
|
752
|
+
if (tree == NULL)
|
|
753
|
+
{
|
|
754
|
+
if (err && errlen)
|
|
755
|
+
snprintf(err, errlen, "tree is NULL");
|
|
756
|
+
return 1;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
dit_check_t ctx;
|
|
760
|
+
ctx.err = err;
|
|
761
|
+
ctx.errlen = errlen;
|
|
762
|
+
ctx.failed = 0;
|
|
763
|
+
ctx.count = 0;
|
|
764
|
+
ctx.prev_b = 0;
|
|
765
|
+
ctx.has_prev = 0;
|
|
766
|
+
|
|
767
|
+
dit_check_from(&ctx, tree->root, DIT_VALUE_MIN, DIT_VALUE_MAX, 0);
|
|
768
|
+
if (ctx.failed)
|
|
769
|
+
return 1;
|
|
770
|
+
|
|
771
|
+
/* 9. the cached cardinality matches the actual number of nodes */
|
|
772
|
+
if (ctx.count != tree->n)
|
|
773
|
+
{
|
|
774
|
+
dit_check_fail(&ctx, "tree holds %zu nodes but reports %zu",
|
|
775
|
+
ctx.count, tree->n);
|
|
776
|
+
return 1;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
if (tree->root && (size_t) tree->root->augment.size != tree->n)
|
|
780
|
+
{
|
|
781
|
+
dit_check_fail(&ctx, "root size augment is %u but the tree holds %zu nodes",
|
|
782
|
+
tree->root->augment.size, tree->n);
|
|
783
|
+
return 1;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/* 10. an AVL tree of n nodes is at most 1.4405*log2(n+2)-0.3277 deep, the
|
|
787
|
+
* bound below is looser but does not need any floating point arithmetic */
|
|
788
|
+
{
|
|
789
|
+
int log2n = 0;
|
|
790
|
+
while (((size_t) 1 << (log2n + 1)) <= ctx.count + 1)
|
|
791
|
+
++log2n;
|
|
792
|
+
|
|
793
|
+
const int height = (int) dit_node_height(tree->root);
|
|
794
|
+
if (height > 2 * (log2n + 1))
|
|
795
|
+
{
|
|
796
|
+
dit_check_fail(&ctx, "tree of %zu nodes is %d deep, expected at most %d",
|
|
797
|
+
ctx.count, height, 2 * (log2n + 1));
|
|
798
|
+
return 1;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/* 11. no traversal may be leaking */
|
|
803
|
+
if (tree->traversing < 0)
|
|
804
|
+
{
|
|
805
|
+
dit_check_fail(&ctx, "negative traversal counter (%d)", tree->traversing);
|
|
806
|
+
return 1;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
return 0;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
//////////
|
|
813
|
+
// DUMP //
|
|
814
|
+
//////////
|
|
815
|
+
|
|
816
|
+
static void
|
|
817
|
+
dit_dump_dot_from(const dit_node_t * node, FILE * f)
|
|
818
|
+
{
|
|
819
|
+
if (node == NULL)
|
|
820
|
+
return ;
|
|
821
|
+
|
|
822
|
+
fprintf(f, " N%p[shape=record, label=\"{[%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[",
|
|
823
|
+
(const void *) node, node->a, node->b);
|
|
824
|
+
fprintf(f, "|hull [%" DIT_VALUE_FMT "..%" DIT_VALUE_FMT "[",
|
|
825
|
+
node->augment.hull.a, node->augment.hull.b);
|
|
826
|
+
fprintf(f, "|h=%d, n=%u}\"] ;\n", (int) node->augment.height, node->augment.size);
|
|
827
|
+
|
|
828
|
+
for (int dir = DIT_LEFT ; dir < DIT_N_CHILDREN ; ++dir)
|
|
829
|
+
{
|
|
830
|
+
const dit_node_t * child = node->child[dir];
|
|
831
|
+
if (child)
|
|
832
|
+
{
|
|
833
|
+
dit_dump_dot_from(child, f);
|
|
834
|
+
fprintf(f, " N%p->N%p [label=\"%s\"] ;\n",
|
|
835
|
+
(const void *) node, (const void *) child,
|
|
836
|
+
(dir == DIT_LEFT) ? "l" : "r");
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
void
|
|
842
|
+
dit_dump_dot(const dit_t * tree, void * f)
|
|
843
|
+
{
|
|
844
|
+
FILE * stream = (FILE *) f;
|
|
845
|
+
|
|
846
|
+
fprintf(stream, "digraph dit {\n");
|
|
847
|
+
if (tree && tree->root)
|
|
848
|
+
dit_dump_dot_from(tree->root, stream);
|
|
849
|
+
fprintf(stream, "}\n");
|
|
850
|
+
}
|