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,648 @@
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
+ /* Ruby bindings for the disjoint interval tree */
18
+
19
+ #include <ruby.h>
20
+
21
+ #include <inttypes.h>
22
+
23
+ #include "dit.h"
24
+
25
+ static VALUE cTree;
26
+ static VALUE eError;
27
+ static VALUE eOverlapError;
28
+ static VALUE eCorruptedError;
29
+
30
+ static VALUE rb_dit_size(VALUE self);
31
+
32
+ /* not exposed as such by every supported ruby version */
33
+ #ifndef RBIGNUM_NEGATIVE_P
34
+ # define RBIGNUM_NEGATIVE_P(B) (rb_big_sign(B) == 0)
35
+ #endif /* RBIGNUM_NEGATIVE_P */
36
+
37
+ /////////////////////
38
+ // TYPED DATA GLUE //
39
+ /////////////////////
40
+
41
+ static void
42
+ rb_dit_free(void * ptr)
43
+ {
44
+ dit_t * tree = (dit_t *) ptr;
45
+
46
+ /* a traversal cannot be in progress here: the tree is unreachable */
47
+ tree->traversing = 0;
48
+ dit_destroy(tree);
49
+ xfree(tree);
50
+ }
51
+
52
+ static size_t
53
+ rb_dit_memsize(const void * ptr)
54
+ {
55
+ const dit_t * tree = (const dit_t *) ptr;
56
+ return sizeof(dit_t) + tree->n * sizeof(dit_node_t);
57
+ }
58
+
59
+ static const rb_data_type_t rb_dit_type = {
60
+ .wrap_struct_name = "DisjointIntervalTree",
61
+ .function = {
62
+ .dmark = NULL,
63
+ .dfree = rb_dit_free,
64
+ .dsize = rb_dit_memsize,
65
+ },
66
+ .parent = NULL,
67
+ .data = NULL,
68
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
69
+ };
70
+
71
+ static inline dit_t *
72
+ rb_dit_get(VALUE self)
73
+ {
74
+ dit_t * tree;
75
+ TypedData_Get_Struct(self, dit_t, &rb_dit_type, tree);
76
+ return tree;
77
+ }
78
+
79
+ /* Mutating the tree from within `each`/`intersect` would invalidate the
80
+ * traversal cursor, and possibly free the node being visited */
81
+ static inline dit_t *
82
+ rb_dit_get_mutable(VALUE self)
83
+ {
84
+ dit_t * tree = rb_dit_get(self);
85
+
86
+ rb_check_frozen(self);
87
+
88
+ if (tree->traversing)
89
+ rb_raise(eError, "cannot modify a DisjointIntervalTree while traversing it");
90
+
91
+ return tree;
92
+ }
93
+
94
+ /* Interval bounds are unsigned 64 bits integers.
95
+ *
96
+ * `NUM2ULL` cannot be used as-is: it mirrors C conversion rules and silently
97
+ * wraps negative values around, which would turn a `-1` typo into a
98
+ * `18446744073709551615` bound. Floats are rejected too, as truncating them
99
+ * silently would be just as surprising */
100
+ static inline dit_value_t
101
+ rb_dit_value(VALUE v)
102
+ {
103
+ if (RB_FIXNUM_P(v))
104
+ {
105
+ const long l = FIX2LONG(v);
106
+ if (l < 0)
107
+ rb_raise(rb_eRangeError,
108
+ "interval bounds must be in [0..2**64[, got %ld", l);
109
+ return (dit_value_t) l;
110
+ }
111
+
112
+ if (RB_TYPE_P(v, T_BIGNUM))
113
+ {
114
+ if (RBIGNUM_NEGATIVE_P(v))
115
+ rb_raise(rb_eRangeError,
116
+ "interval bounds must be in [0..2**64[, got %"PRIsVALUE, v);
117
+
118
+ /* raises RangeError if it does not fit in 64 bits */
119
+ return (dit_value_t) rb_big2ull(v);
120
+ }
121
+
122
+ rb_raise(rb_eTypeError, "no implicit conversion of %"PRIsVALUE" into Integer",
123
+ rb_obj_class(v));
124
+ }
125
+
126
+ static inline VALUE
127
+ rb_dit_interval(dit_value_t a, dit_value_t b)
128
+ {
129
+ return rb_assoc_new(ULL2NUM(a), ULL2NUM(b));
130
+ }
131
+
132
+ static VALUE
133
+ rb_dit_alloc(VALUE klass)
134
+ {
135
+ dit_t * tree;
136
+ VALUE self = TypedData_Make_Struct(klass, dit_t, &rb_dit_type, tree);
137
+ dit_init(tree);
138
+ return self;
139
+ }
140
+
141
+ ////////////////
142
+ // CALLBACKS //
143
+ ////////////////
144
+
145
+ static int
146
+ rb_dit_cb_yield(dit_value_t a, dit_value_t b, void * user)
147
+ {
148
+ (void) user;
149
+ rb_yield_values(2, ULL2NUM(a), ULL2NUM(b));
150
+ return 0;
151
+ }
152
+
153
+ static int
154
+ rb_dit_cb_push(dit_value_t a, dit_value_t b, void * user)
155
+ {
156
+ rb_ary_push((VALUE) user, rb_dit_interval(a, b));
157
+ return 0;
158
+ }
159
+
160
+ /* Traversals must restore `tree->traversing` even if the block raises or
161
+ * breaks, hence the `rb_ensure` dance */
162
+ typedef struct
163
+ {
164
+ dit_t * tree;
165
+ dit_value_t a, b;
166
+ dit_cb_t cb;
167
+ void * user;
168
+
169
+ /* whole tree instead of the [a..b[ range */
170
+ int all;
171
+
172
+ /* value of `tree->traversing` before the traversal started */
173
+ int traversing;
174
+ } rb_dit_traversal_t;
175
+
176
+ static VALUE
177
+ rb_dit_traverse_body(VALUE arg)
178
+ {
179
+ rb_dit_traversal_t * t = (rb_dit_traversal_t *) arg;
180
+
181
+ if (t->all)
182
+ dit_each(t->tree, t->cb, t->user);
183
+ else
184
+ dit_intersect(t->tree, t->a, t->b, t->cb, t->user);
185
+
186
+ return Qnil;
187
+ }
188
+
189
+ static VALUE
190
+ rb_dit_traverse_ensure(VALUE arg)
191
+ {
192
+ rb_dit_traversal_t * t = (rb_dit_traversal_t *) arg;
193
+ t->tree->traversing = t->traversing;
194
+ return Qnil;
195
+ }
196
+
197
+ static void
198
+ rb_dit_traverse(dit_t * tree, int all, dit_value_t a, dit_value_t b, dit_cb_t cb, void * user)
199
+ {
200
+ rb_dit_traversal_t t;
201
+ t.tree = tree;
202
+ t.a = a;
203
+ t.b = b;
204
+ t.cb = cb;
205
+ t.user = user;
206
+ t.all = all;
207
+ t.traversing = tree->traversing;
208
+
209
+ rb_ensure(rb_dit_traverse_body, (VALUE) &t, rb_dit_traverse_ensure, (VALUE) &t);
210
+ }
211
+
212
+ /////////////
213
+ // METHODS //
214
+ /////////////
215
+
216
+ /*
217
+ * call-seq:
218
+ * DisjointIntervalTree.new -> tree
219
+ * DisjointIntervalTree.new([[a, b], [c, d]]) -> tree
220
+ *
221
+ * Create a tree, optionally filled with the given intervals.
222
+ */
223
+ static VALUE
224
+ rb_dit_initialize(int argc, VALUE * argv, VALUE self)
225
+ {
226
+ VALUE intervals;
227
+ rb_scan_args(argc, argv, "01", &intervals);
228
+
229
+ if (!NIL_P(intervals))
230
+ {
231
+ intervals = rb_check_array_type(intervals);
232
+ if (NIL_P(intervals))
233
+ rb_raise(rb_eTypeError, "expected an array of [a, b] intervals");
234
+
235
+ for (long i = 0 ; i < RARRAY_LEN(intervals) ; ++i)
236
+ {
237
+ VALUE interval = rb_check_array_type(rb_ary_entry(intervals, i));
238
+ if (NIL_P(interval) || RARRAY_LEN(interval) != 2)
239
+ rb_raise(rb_eArgError, "expected an [a, b] interval at index %ld", i);
240
+ rb_funcall(self, rb_intern("insert"), 2,
241
+ rb_ary_entry(interval, 0), rb_ary_entry(interval, 1));
242
+ }
243
+ }
244
+
245
+ return self;
246
+ }
247
+
248
+ /* Insert `[a..b[`, returns `DIT_OK`, or raises unless `soft` is set */
249
+ static dit_status_t
250
+ rb_dit_do_insert(VALUE self, VALUE va, VALUE vb, int soft)
251
+ {
252
+ dit_t * tree = rb_dit_get_mutable(self);
253
+
254
+ const dit_value_t a = rb_dit_value(va);
255
+ const dit_value_t b = rb_dit_value(vb);
256
+
257
+ const dit_status_t status = dit_insert(tree, a, b);
258
+
259
+ switch (status)
260
+ {
261
+ case DIT_OK:
262
+ break ;
263
+
264
+ case DIT_EMPTY:
265
+ rb_raise(rb_eArgError,
266
+ "empty interval [%"PRIu64"..%"PRIu64"[, expected a < b", a, b);
267
+
268
+ case DIT_OVERLAP:
269
+ if (!soft)
270
+ {
271
+ const dit_node_t * other = dit_intersecting(tree, a, b);
272
+ rb_raise(eOverlapError,
273
+ "[%"PRIu64"..%"PRIu64"[ overlaps [%"PRIu64"..%"PRIu64"[",
274
+ a, b, other ? other->a : 0, other ? other->b : 0);
275
+ }
276
+ break ;
277
+
278
+ case DIT_NOMEM:
279
+ rb_memerror();
280
+
281
+ default:
282
+ rb_raise(eError, "unexpected insertion status %d", (int) status);
283
+ }
284
+
285
+ return status;
286
+ }
287
+
288
+ /*
289
+ * call-seq:
290
+ * tree.insert(a, b) -> self
291
+ *
292
+ * Insert the half-open interval `[a..b[`.
293
+ *
294
+ * It is a usage contract that `[a..b[` must not overlap an already inserted
295
+ * interval: an OverlapError is raised - and the tree is left unchanged - if
296
+ * it does. Adjacent intervals such as `[0..10[` and `[10..20[` do not
297
+ * overlap.
298
+ */
299
+ static VALUE
300
+ rb_dit_insert(VALUE self, VALUE va, VALUE vb)
301
+ {
302
+ rb_dit_do_insert(self, va, vb, 0);
303
+ return self;
304
+ }
305
+
306
+ /*
307
+ * call-seq:
308
+ * tree.insert?(a, b) -> true or false
309
+ *
310
+ * Same as #insert, but returns false instead of raising when `[a..b[`
311
+ * overlaps an already inserted interval.
312
+ */
313
+ static VALUE
314
+ rb_dit_insert_p(VALUE self, VALUE va, VALUE vb)
315
+ {
316
+ return (rb_dit_do_insert(self, va, vb, 1) == DIT_OK) ? Qtrue : Qfalse;
317
+ }
318
+
319
+ /*
320
+ * call-seq:
321
+ * tree.intersect(a, b) { |x, y| ... } -> self
322
+ * tree.intersect(a, b) -> array
323
+ *
324
+ * Yield every stored interval intersecting `[a..b[`, in increasing order.
325
+ * Without a block, return them as an array of `[x, y]` pairs.
326
+ *
327
+ * The tree must not be modified from within the block.
328
+ */
329
+ static VALUE
330
+ rb_dit_intersect(VALUE self, VALUE va, VALUE vb)
331
+ {
332
+ dit_t * tree = rb_dit_get(self);
333
+
334
+ const dit_value_t a = rb_dit_value(va);
335
+ const dit_value_t b = rb_dit_value(vb);
336
+
337
+ if (rb_block_given_p())
338
+ {
339
+ rb_dit_traverse(tree, 0, a, b, rb_dit_cb_yield, NULL);
340
+ return self;
341
+ }
342
+
343
+ VALUE ary = rb_ary_new();
344
+ rb_dit_traverse(tree, 0, a, b, rb_dit_cb_push, (void *) ary);
345
+ return ary;
346
+ }
347
+
348
+ /*
349
+ * call-seq:
350
+ * tree.intersect?(a, b) -> true or false
351
+ *
352
+ * Whether at least one stored interval intersect `[a..b[`. O(log n).
353
+ */
354
+ static VALUE
355
+ rb_dit_intersect_p(VALUE self, VALUE va, VALUE vb)
356
+ {
357
+ const dit_t * tree = rb_dit_get(self);
358
+ return dit_intersect_p(tree, rb_dit_value(va), rb_dit_value(vb)) ? Qtrue : Qfalse;
359
+ }
360
+
361
+ /*
362
+ * call-seq:
363
+ * tree.remove(a, b) -> integer
364
+ * tree.remove(a, b) { |x, y| ... } -> integer
365
+ *
366
+ * Remove every stored interval intersecting `[a..b[` and return how many were
367
+ * removed. If a block is given, it is called with each removed interval once
368
+ * the removal is done.
369
+ *
370
+ * Intervals are removed as a whole: an interval only partially covered by
371
+ * `[a..b[` is removed entirely, never split.
372
+ */
373
+ static VALUE
374
+ rb_dit_remove(VALUE self, VALUE va, VALUE vb)
375
+ {
376
+ dit_t * tree = rb_dit_get_mutable(self);
377
+
378
+ const dit_value_t a = rb_dit_value(va);
379
+ const dit_value_t b = rb_dit_value(vb);
380
+
381
+ /* snapshot the intervals first, so that the block cannot observe - nor
382
+ * corrupt - a tree being modified */
383
+ VALUE removed = Qnil;
384
+ if (rb_block_given_p())
385
+ {
386
+ removed = rb_ary_new();
387
+ rb_dit_traverse(tree, 0, a, b, rb_dit_cb_push, (void *) removed);
388
+ }
389
+
390
+ const size_t n = dit_remove(tree, a, b);
391
+
392
+ if (!NIL_P(removed))
393
+ {
394
+ RUBY_ASSERT((size_t) RARRAY_LEN(removed) == n);
395
+ for (long i = 0 ; i < RARRAY_LEN(removed) ; ++i)
396
+ {
397
+ VALUE interval = rb_ary_entry(removed, i);
398
+ rb_yield_values(2, rb_ary_entry(interval, 0), rb_ary_entry(interval, 1));
399
+ }
400
+ }
401
+
402
+ return ULL2NUM((unsigned long long) n);
403
+ }
404
+
405
+ static VALUE
406
+ rb_dit_enum_size(VALUE self, VALUE args, VALUE eobj)
407
+ {
408
+ (void) args;
409
+ (void) eobj;
410
+ return rb_dit_size(self);
411
+ }
412
+
413
+ /*
414
+ * call-seq:
415
+ * tree.each { |a, b| ... } -> self
416
+ * tree.each -> enumerator
417
+ *
418
+ * Yield every stored interval, in increasing order.
419
+ */
420
+ static VALUE
421
+ rb_dit_each(VALUE self)
422
+ {
423
+ dit_t * tree = rb_dit_get(self);
424
+
425
+ RETURN_SIZED_ENUMERATOR(self, 0, 0, rb_dit_enum_size);
426
+
427
+ rb_dit_traverse(tree, 1, 0, 0, rb_dit_cb_yield, NULL);
428
+
429
+ return self;
430
+ }
431
+
432
+ /*
433
+ * call-seq:
434
+ * tree.to_a -> array
435
+ *
436
+ * Every stored interval, in increasing order, as an array of `[a, b]` pairs.
437
+ */
438
+ static VALUE
439
+ rb_dit_to_a(VALUE self)
440
+ {
441
+ dit_t * tree = rb_dit_get(self);
442
+ VALUE ary = rb_ary_new_capa((long) dit_size(tree));
443
+ rb_dit_traverse(tree, 1, 0, 0, rb_dit_cb_push, (void *) ary);
444
+ return ary;
445
+ }
446
+
447
+ /*
448
+ * call-seq:
449
+ * tree.at(x) -> [a, b] or nil
450
+ *
451
+ * The stored interval containing the point `x`, or nil. O(log n).
452
+ */
453
+ static VALUE
454
+ rb_dit_at(VALUE self, VALUE vx)
455
+ {
456
+ const dit_t * tree = rb_dit_get(self);
457
+ const dit_node_t * node = dit_at(tree, rb_dit_value(vx));
458
+ return node ? rb_dit_interval(node->a, node->b) : Qnil;
459
+ }
460
+
461
+ /*
462
+ * call-seq:
463
+ * tree.cover?(x) -> true or false
464
+ *
465
+ * Whether the point `x` is covered by a stored interval. O(log n).
466
+ */
467
+ static VALUE
468
+ rb_dit_cover_p(VALUE self, VALUE vx)
469
+ {
470
+ const dit_t * tree = rb_dit_get(self);
471
+ return dit_at(tree, rb_dit_value(vx)) ? Qtrue : Qfalse;
472
+ }
473
+
474
+ /*
475
+ * call-seq:
476
+ * tree.hull -> [a, b] or nil
477
+ *
478
+ * The smallest interval including every stored interval, or nil when the tree
479
+ * is empty.
480
+ *
481
+ * This is the hull augment of the root node, so it is O(1).
482
+ */
483
+ static VALUE
484
+ rb_dit_hull(VALUE self)
485
+ {
486
+ const dit_t * tree = rb_dit_get(self);
487
+
488
+ dit_value_t a, b;
489
+ if (!dit_hull(tree, &a, &b))
490
+ return Qnil;
491
+
492
+ return rb_dit_interval(a, b);
493
+ }
494
+
495
+ /*
496
+ * call-seq:
497
+ * tree.size -> integer
498
+ *
499
+ * The number of stored intervals.
500
+ */
501
+ static VALUE
502
+ rb_dit_size(VALUE self)
503
+ {
504
+ const dit_t * tree = rb_dit_get(self);
505
+ return ULL2NUM((unsigned long long) dit_size(tree));
506
+ }
507
+
508
+ /*
509
+ * call-seq:
510
+ * tree.empty? -> true or false
511
+ */
512
+ static VALUE
513
+ rb_dit_empty_p(VALUE self)
514
+ {
515
+ const dit_t * tree = rb_dit_get(self);
516
+ return dit_empty(tree) ? Qtrue : Qfalse;
517
+ }
518
+
519
+ /*
520
+ * call-seq:
521
+ * tree.height -> integer
522
+ *
523
+ * The height of the underlying AVL tree, mostly useful for testing.
524
+ */
525
+ static VALUE
526
+ rb_dit_height(VALUE self)
527
+ {
528
+ const dit_t * tree = rb_dit_get(self);
529
+ return INT2NUM(dit_height(tree));
530
+ }
531
+
532
+ /*
533
+ * call-seq:
534
+ * tree.clear -> self
535
+ *
536
+ * Remove every stored interval.
537
+ */
538
+ static VALUE
539
+ rb_dit_clear(VALUE self)
540
+ {
541
+ dit_t * tree = rb_dit_get_mutable(self);
542
+ dit_clear(tree);
543
+ return self;
544
+ }
545
+
546
+ /*
547
+ * call-seq:
548
+ * tree.check! -> self
549
+ *
550
+ * Verify every structural invariant of the underlying tree, and raise
551
+ * CorruptedError if one is broken. Mostly useful for testing, it is O(n).
552
+ */
553
+ static VALUE
554
+ rb_dit_check(VALUE self)
555
+ {
556
+ const dit_t * tree = rb_dit_get(self);
557
+
558
+ char err[512];
559
+ if (dit_check(tree, err, sizeof(err)))
560
+ rb_raise(eCorruptedError, "%s", err);
561
+
562
+ return self;
563
+ }
564
+
565
+ static VALUE
566
+ rb_dit_inspect(VALUE self)
567
+ {
568
+ const dit_t * tree = rb_dit_get(self);
569
+ return rb_sprintf("#<%"PRIsVALUE" size=%lu height=%d>",
570
+ rb_obj_class(self), (unsigned long) dit_size(tree), dit_height(tree));
571
+ }
572
+
573
+ /*
574
+ * call-seq:
575
+ * tree.initialize_copy(other) -> self
576
+ *
577
+ * Called by #dup and #clone.
578
+ */
579
+ static VALUE
580
+ rb_dit_initialize_copy(VALUE self, VALUE other)
581
+ {
582
+ dit_t * tree = rb_dit_get_mutable(self);
583
+ dit_t * src = rb_dit_get(other);
584
+
585
+ if (tree == src)
586
+ return self;
587
+
588
+ dit_clear(tree);
589
+
590
+ /* in-order insertions would degenerate into a rotation at every step, but
591
+ * the AVL rebalancing keeps it O(n.log n) overall */
592
+ VALUE intervals = rb_ary_new_capa((long) dit_size(src));
593
+ rb_dit_traverse(src, 1, 0, 0, rb_dit_cb_push, (void *) intervals);
594
+
595
+ for (long i = 0 ; i < RARRAY_LEN(intervals) ; ++i)
596
+ {
597
+ VALUE interval = rb_ary_entry(intervals, i);
598
+ const dit_value_t a = rb_dit_value(rb_ary_entry(interval, 0));
599
+ const dit_value_t b = rb_dit_value(rb_ary_entry(interval, 1));
600
+ if (dit_insert(tree, a, b) != DIT_OK)
601
+ rb_raise(eError, "could not copy interval [%"PRIu64"..%"PRIu64"[", a, b);
602
+ }
603
+
604
+ return self;
605
+ }
606
+
607
+ void
608
+ Init_disjoint_interval_tree(void)
609
+ {
610
+ cTree = rb_define_class("DisjointIntervalTree", rb_cObject);
611
+ rb_include_module(cTree, rb_mEnumerable);
612
+
613
+ eError = rb_define_class_under(cTree, "Error", rb_eStandardError);
614
+ eOverlapError = rb_define_class_under(cTree, "OverlapError", eError);
615
+ eCorruptedError = rb_define_class_under(cTree, "CorruptedError", eError);
616
+
617
+ /* the largest representable bound, exclusive: intervals live in
618
+ * [0 .. DisjointIntervalTree::MAX[ */
619
+ rb_define_const(cTree, "MAX", ULL2NUM((unsigned long long) DIT_VALUE_MAX));
620
+
621
+ rb_define_alloc_func(cTree, rb_dit_alloc);
622
+
623
+ rb_define_method(cTree, "initialize", rb_dit_initialize, -1);
624
+ rb_define_method(cTree, "initialize_copy", rb_dit_initialize_copy, 1);
625
+
626
+ rb_define_method(cTree, "insert", rb_dit_insert, 2);
627
+ rb_define_method(cTree, "insert?", rb_dit_insert_p, 2);
628
+ rb_define_method(cTree, "intersect", rb_dit_intersect, 2);
629
+ rb_define_method(cTree, "intersect?", rb_dit_intersect_p, 2);
630
+ rb_define_method(cTree, "remove", rb_dit_remove, 2);
631
+ rb_define_method(cTree, "each", rb_dit_each, 0);
632
+ rb_define_method(cTree, "to_a", rb_dit_to_a, 0);
633
+ rb_define_method(cTree, "at", rb_dit_at, 1);
634
+ rb_define_method(cTree, "cover?", rb_dit_cover_p, 1);
635
+ rb_define_method(cTree, "hull", rb_dit_hull, 0);
636
+ rb_define_method(cTree, "size", rb_dit_size, 0);
637
+ rb_define_method(cTree, "empty?", rb_dit_empty_p, 0);
638
+ rb_define_method(cTree, "height", rb_dit_height, 0);
639
+ rb_define_method(cTree, "clear", rb_dit_clear, 0);
640
+ rb_define_method(cTree, "check!", rb_dit_check, 0);
641
+ rb_define_method(cTree, "inspect", rb_dit_inspect, 0);
642
+
643
+ rb_define_alias(cTree, "each_intersecting", "intersect");
644
+ rb_define_alias(cTree, "overlaps?", "intersect?");
645
+ rb_define_alias(cTree, "length", "size");
646
+ rb_define_alias(cTree, "entries", "to_a");
647
+ rb_define_alias(cTree, "to_s", "inspect");
648
+ }
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mkmf'
4
+
5
+ # `--enable-paranoid` runs a full O(n) coherency check of the tree after every
6
+ # mutation. Only useful to debug the extension itself.
7
+ if enable_config('paranoid', false)
8
+ $defs << '-DDIT_PARANOID=1'
9
+ warn 'disjoint_interval_tree: building with paranoid coherency checks'
10
+ end
11
+
12
+ # `--disable-assertions` compiles out every internal consistency assertion.
13
+ $defs << '-DNDEBUG' unless enable_config('assertions', true)
14
+
15
+ append_cflags(['-std=gnu11', '-Wall', '-Wextra'])
16
+
17
+ create_makefile('disjoint_interval_tree/disjoint_interval_tree')
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DisjointIntervalTree
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2025 INRIA
5
+ #
6
+ # Contributors :
7
+ # Romain PEREIRA, romain.pereira@inria.fr + rpereira@anl.gov
8
+ #
9
+ # This software is governed by the CeCILL-C license under French law and
10
+ # abiding by the rules of distribution of free software. You can use,
11
+ # modify and/ or redistribute the software under the terms of the CeCILL-C
12
+ # license as circulated by CEA, CNRS and INRIA at the following URL
13
+ # "http://www.cecill.info".
14
+ #
15
+
16
+ require 'disjoint_interval_tree/disjoint_interval_tree'
17
+ require 'disjoint_interval_tree/version'
18
+
19
+ # A set of pairwise disjoint half-open intervals `[a..b[`, backed by an
20
+ # augmented AVL tree written in C.
21
+ #
22
+ # Bounds are unsigned 64 bits integers, so intervals live in
23
+ # `[0 .. DisjointIntervalTree::MAX[`.
24
+ #
25
+ # tree = DisjointIntervalTree.new
26
+ # tree.insert(0, 10)
27
+ # tree.insert(20, 30)
28
+ #
29
+ # tree.intersect(5, 25) { |a, b| puts "[#{a}..#{b}[" }
30
+ # # => [0..10[
31
+ # # => [20..30[
32
+ #
33
+ # tree.remove(5, 25) # => 2
34
+ # tree.to_a # => []
35
+ #
36
+ # Inserting an interval overlapping an already inserted one is a usage
37
+ # contract violation and raises DisjointIntervalTree::OverlapError.
38
+ class DisjointIntervalTree
39
+ # Two trees are equal when they hold the same intervals.
40
+ def ==(other)
41
+ other.is_a?(DisjointIntervalTree) && to_a == other.to_a
42
+ end
43
+
44
+ alias eql? ==
45
+
46
+ # The total length covered by the stored intervals.
47
+ def coverage
48
+ sum = 0
49
+ each { |a, b| sum += b - a }
50
+ sum
51
+ end
52
+ end