disjoint_interval_tree 0.1.0 → 0.2.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 +4 -4
- data/README.md +82 -29
- data/Rakefile +49 -4
- data/ext/disjoint_interval_tree/dit.c +63 -14
- data/ext/disjoint_interval_tree/dit.h +36 -9
- data/ext/disjoint_interval_tree/dit_ruby.c +157 -40
- data/lib/disjoint_interval_tree/version.rb +1 -1
- data/lib/disjoint_interval_tree.rb +11 -6
- data/test/c/test_dit.c +335 -65
- data/test/test_disjoint_interval_tree.rb +225 -36
- metadata +1 -1
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
/* Ruby bindings for the disjoint interval tree */
|
|
18
18
|
|
|
19
19
|
#include <ruby.h>
|
|
20
|
+
#include <ruby/version.h>
|
|
20
21
|
|
|
21
22
|
#include <inttypes.h>
|
|
22
23
|
|
|
@@ -38,6 +39,55 @@ static VALUE rb_dit_size(VALUE self);
|
|
|
38
39
|
// TYPED DATA GLUE //
|
|
39
40
|
/////////////////////
|
|
40
41
|
|
|
42
|
+
/* The tree stores a VALUE per interval, so it is a ruby container: every
|
|
43
|
+
* object it holds has to be reachable by the garbage collector.
|
|
44
|
+
*
|
|
45
|
+
* `rb_gc_mark_movable` rather than `rb_gc_mark` keeps those objects
|
|
46
|
+
* relocatable by a compacting GC, at the cost of having to update them in
|
|
47
|
+
* `rb_dit_compact` afterwards.
|
|
48
|
+
*
|
|
49
|
+
* The type is deliberately *not* declared `RUBY_TYPED_WB_PROTECTED`: honouring
|
|
50
|
+
* the write barrier would require `RB_OBJ_WRITE()` on the slot of the node
|
|
51
|
+
* `dit_insert()` creates, hence handing that node back through the C API. The
|
|
52
|
+
* tree is therefore marked on every minor GC instead of only when it is
|
|
53
|
+
* written to, which is a fine trade for now */
|
|
54
|
+
|
|
55
|
+
static void
|
|
56
|
+
rb_dit_mark_node(dit_node_t * node)
|
|
57
|
+
{
|
|
58
|
+
if (node == NULL)
|
|
59
|
+
return ;
|
|
60
|
+
|
|
61
|
+
rb_gc_mark_movable((VALUE) node->obj);
|
|
62
|
+
|
|
63
|
+
rb_dit_mark_node(node->left);
|
|
64
|
+
rb_dit_mark_node(node->right);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static void
|
|
68
|
+
rb_dit_mark(void * ptr)
|
|
69
|
+
{
|
|
70
|
+
rb_dit_mark_node(((dit_t *) ptr)->root);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static void
|
|
74
|
+
rb_dit_compact_node(dit_node_t * node)
|
|
75
|
+
{
|
|
76
|
+
if (node == NULL)
|
|
77
|
+
return ;
|
|
78
|
+
|
|
79
|
+
node->obj = (dit_object_t) rb_gc_location((VALUE) node->obj);
|
|
80
|
+
|
|
81
|
+
rb_dit_compact_node(node->left);
|
|
82
|
+
rb_dit_compact_node(node->right);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static void
|
|
86
|
+
rb_dit_compact(void * ptr)
|
|
87
|
+
{
|
|
88
|
+
rb_dit_compact_node(((dit_t *) ptr)->root);
|
|
89
|
+
}
|
|
90
|
+
|
|
41
91
|
static void
|
|
42
92
|
rb_dit_free(void * ptr)
|
|
43
93
|
{
|
|
@@ -59,9 +109,10 @@ rb_dit_memsize(const void * ptr)
|
|
|
59
109
|
static const rb_data_type_t rb_dit_type = {
|
|
60
110
|
.wrap_struct_name = "DisjointIntervalTree",
|
|
61
111
|
.function = {
|
|
62
|
-
.dmark =
|
|
112
|
+
.dmark = rb_dit_mark,
|
|
63
113
|
.dfree = rb_dit_free,
|
|
64
114
|
.dsize = rb_dit_memsize,
|
|
115
|
+
.dcompact = rb_dit_compact,
|
|
65
116
|
},
|
|
66
117
|
.parent = NULL,
|
|
67
118
|
.data = NULL,
|
|
@@ -123,10 +174,12 @@ rb_dit_value(VALUE v)
|
|
|
123
174
|
rb_obj_class(v));
|
|
124
175
|
}
|
|
125
176
|
|
|
177
|
+
/* An interval, as handed back to ruby: [a, b, obj] */
|
|
126
178
|
static inline VALUE
|
|
127
|
-
rb_dit_interval(dit_value_t a, dit_value_t b)
|
|
179
|
+
rb_dit_interval(dit_value_t a, dit_value_t b, dit_object_t obj)
|
|
128
180
|
{
|
|
129
|
-
|
|
181
|
+
const VALUE triple[3] = { ULL2NUM(a), ULL2NUM(b), (VALUE) obj };
|
|
182
|
+
return rb_ary_new_from_values(3, triple);
|
|
130
183
|
}
|
|
131
184
|
|
|
132
185
|
static VALUE
|
|
@@ -143,17 +196,17 @@ rb_dit_alloc(VALUE klass)
|
|
|
143
196
|
////////////////
|
|
144
197
|
|
|
145
198
|
static int
|
|
146
|
-
rb_dit_cb_yield(dit_value_t a, dit_value_t b, void * user)
|
|
199
|
+
rb_dit_cb_yield(dit_value_t a, dit_value_t b, dit_object_t obj, void * user)
|
|
147
200
|
{
|
|
148
201
|
(void) user;
|
|
149
|
-
rb_yield_values(
|
|
202
|
+
rb_yield_values(3, ULL2NUM(a), ULL2NUM(b), (VALUE) obj);
|
|
150
203
|
return 0;
|
|
151
204
|
}
|
|
152
205
|
|
|
153
206
|
static int
|
|
154
|
-
rb_dit_cb_push(dit_value_t a, dit_value_t b, void * user)
|
|
207
|
+
rb_dit_cb_push(dit_value_t a, dit_value_t b, dit_object_t obj, void * user)
|
|
155
208
|
{
|
|
156
|
-
rb_ary_push((VALUE) user, rb_dit_interval(a, b));
|
|
209
|
+
rb_ary_push((VALUE) user, rb_dit_interval(a, b, obj));
|
|
157
210
|
return 0;
|
|
158
211
|
}
|
|
159
212
|
|
|
@@ -230,15 +283,21 @@ rb_dit_initialize(int argc, VALUE * argv, VALUE self)
|
|
|
230
283
|
{
|
|
231
284
|
intervals = rb_check_array_type(intervals);
|
|
232
285
|
if (NIL_P(intervals))
|
|
233
|
-
rb_raise(rb_eTypeError, "expected an array of [a, b] intervals");
|
|
286
|
+
rb_raise(rb_eTypeError, "expected an array of [a, b] or [a, b, obj] intervals");
|
|
234
287
|
|
|
235
288
|
for (long i = 0 ; i < RARRAY_LEN(intervals) ; ++i)
|
|
236
289
|
{
|
|
237
290
|
VALUE interval = rb_check_array_type(rb_ary_entry(intervals, i));
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
291
|
+
const long len = NIL_P(interval) ? 0 : RARRAY_LEN(interval);
|
|
292
|
+
|
|
293
|
+
if (len != 2 && len != 3)
|
|
294
|
+
rb_raise(rb_eArgError,
|
|
295
|
+
"expected an [a, b] or [a, b, obj] interval at index %ld", i);
|
|
296
|
+
|
|
297
|
+
rb_funcall(self, rb_intern("insert"), 3,
|
|
298
|
+
rb_ary_entry(interval, 0),
|
|
299
|
+
rb_ary_entry(interval, 1),
|
|
300
|
+
(len == 3) ? rb_ary_entry(interval, 2) : Qnil);
|
|
242
301
|
}
|
|
243
302
|
}
|
|
244
303
|
|
|
@@ -247,14 +306,17 @@ rb_dit_initialize(int argc, VALUE * argv, VALUE self)
|
|
|
247
306
|
|
|
248
307
|
/* Insert `[a..b[`, returns `DIT_OK`, or raises unless `soft` is set */
|
|
249
308
|
static dit_status_t
|
|
250
|
-
rb_dit_do_insert(
|
|
309
|
+
rb_dit_do_insert(int argc, VALUE * argv, VALUE self, int soft)
|
|
251
310
|
{
|
|
311
|
+
VALUE va, vb, vobj;
|
|
312
|
+
rb_scan_args(argc, argv, "21", &va, &vb, &vobj);
|
|
313
|
+
|
|
252
314
|
dit_t * tree = rb_dit_get_mutable(self);
|
|
253
315
|
|
|
254
316
|
const dit_value_t a = rb_dit_value(va);
|
|
255
317
|
const dit_value_t b = rb_dit_value(vb);
|
|
256
318
|
|
|
257
|
-
const dit_status_t status = dit_insert(tree, a, b);
|
|
319
|
+
const dit_status_t status = dit_insert(tree, a, b, (dit_object_t) vobj);
|
|
258
320
|
|
|
259
321
|
switch (status)
|
|
260
322
|
{
|
|
@@ -287,9 +349,11 @@ rb_dit_do_insert(VALUE self, VALUE va, VALUE vb, int soft)
|
|
|
287
349
|
|
|
288
350
|
/*
|
|
289
351
|
* call-seq:
|
|
290
|
-
* tree.insert(a, b)
|
|
352
|
+
* tree.insert(a, b) -> self
|
|
353
|
+
* tree.insert(a, b, obj) -> self
|
|
291
354
|
*
|
|
292
|
-
* Insert the half-open interval `[a..b[
|
|
355
|
+
* Insert the half-open interval `[a..b[`, associated with `obj` - which
|
|
356
|
+
* defaults to nil, and which every query and traversal hands back.
|
|
293
357
|
*
|
|
294
358
|
* It is a usage contract that `[a..b[` must not overlap an already inserted
|
|
295
359
|
* interval: an OverlapError is raised - and the tree is left unchanged - if
|
|
@@ -297,32 +361,33 @@ rb_dit_do_insert(VALUE self, VALUE va, VALUE vb, int soft)
|
|
|
297
361
|
* overlap.
|
|
298
362
|
*/
|
|
299
363
|
static VALUE
|
|
300
|
-
rb_dit_insert(
|
|
364
|
+
rb_dit_insert(int argc, VALUE * argv, VALUE self)
|
|
301
365
|
{
|
|
302
|
-
rb_dit_do_insert(
|
|
366
|
+
rb_dit_do_insert(argc, argv, self, 0);
|
|
303
367
|
return self;
|
|
304
368
|
}
|
|
305
369
|
|
|
306
370
|
/*
|
|
307
371
|
* call-seq:
|
|
308
|
-
* tree.insert?(a, b)
|
|
372
|
+
* tree.insert?(a, b) -> true or false
|
|
373
|
+
* tree.insert?(a, b, obj) -> true or false
|
|
309
374
|
*
|
|
310
375
|
* Same as #insert, but returns false instead of raising when `[a..b[`
|
|
311
376
|
* overlaps an already inserted interval.
|
|
312
377
|
*/
|
|
313
378
|
static VALUE
|
|
314
|
-
rb_dit_insert_p(
|
|
379
|
+
rb_dit_insert_p(int argc, VALUE * argv, VALUE self)
|
|
315
380
|
{
|
|
316
|
-
return (rb_dit_do_insert(
|
|
381
|
+
return (rb_dit_do_insert(argc, argv, self, 1) == DIT_OK) ? Qtrue : Qfalse;
|
|
317
382
|
}
|
|
318
383
|
|
|
319
384
|
/*
|
|
320
385
|
* call-seq:
|
|
321
|
-
* tree.intersect(a, b) { |x, y| ... } -> self
|
|
322
|
-
* tree.intersect(a, b)
|
|
386
|
+
* tree.intersect(a, b) { |x, y, obj| ... } -> self
|
|
387
|
+
* tree.intersect(a, b) -> array
|
|
323
388
|
*
|
|
324
389
|
* Yield every stored interval intersecting `[a..b[`, in increasing order.
|
|
325
|
-
* Without a block, return them as an array of `[x, y]`
|
|
390
|
+
* Without a block, return them as an array of `[x, y, obj]` triples.
|
|
326
391
|
*
|
|
327
392
|
* The tree must not be modified from within the block.
|
|
328
393
|
*/
|
|
@@ -360,12 +425,12 @@ rb_dit_intersect_p(VALUE self, VALUE va, VALUE vb)
|
|
|
360
425
|
|
|
361
426
|
/*
|
|
362
427
|
* call-seq:
|
|
363
|
-
* tree.remove(a, b)
|
|
364
|
-
* tree.remove(a, b) { |x, y| ... }
|
|
428
|
+
* tree.remove(a, b) -> integer
|
|
429
|
+
* tree.remove(a, b) { |x, y, obj| ... } -> integer
|
|
365
430
|
*
|
|
366
431
|
* 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
|
|
368
|
-
* the removal is done.
|
|
432
|
+
* removed. If a block is given, it is called with each removed interval - and
|
|
433
|
+
* its object - once the removal is done.
|
|
369
434
|
*
|
|
370
435
|
* Intervals are removed as a whole: an interval only partially covered by
|
|
371
436
|
* `[a..b[` is removed entirely, never split.
|
|
@@ -387,7 +452,7 @@ rb_dit_remove(VALUE self, VALUE va, VALUE vb)
|
|
|
387
452
|
rb_dit_traverse(tree, 0, a, b, rb_dit_cb_push, (void *) removed);
|
|
388
453
|
}
|
|
389
454
|
|
|
390
|
-
const size_t n = dit_remove(tree, a, b);
|
|
455
|
+
const size_t n = dit_remove(tree, a, b, NULL, NULL);
|
|
391
456
|
|
|
392
457
|
if (!NIL_P(removed))
|
|
393
458
|
{
|
|
@@ -395,7 +460,10 @@ rb_dit_remove(VALUE self, VALUE va, VALUE vb)
|
|
|
395
460
|
for (long i = 0 ; i < RARRAY_LEN(removed) ; ++i)
|
|
396
461
|
{
|
|
397
462
|
VALUE interval = rb_ary_entry(removed, i);
|
|
398
|
-
rb_yield_values(
|
|
463
|
+
rb_yield_values(3,
|
|
464
|
+
rb_ary_entry(interval, 0),
|
|
465
|
+
rb_ary_entry(interval, 1),
|
|
466
|
+
rb_ary_entry(interval, 2));
|
|
399
467
|
}
|
|
400
468
|
}
|
|
401
469
|
|
|
@@ -412,8 +480,8 @@ rb_dit_enum_size(VALUE self, VALUE args, VALUE eobj)
|
|
|
412
480
|
|
|
413
481
|
/*
|
|
414
482
|
* call-seq:
|
|
415
|
-
* tree.each { |a, b| ... } -> self
|
|
416
|
-
* tree.each
|
|
483
|
+
* tree.each { |a, b, obj| ... } -> self
|
|
484
|
+
* tree.each -> enumerator
|
|
417
485
|
*
|
|
418
486
|
* Yield every stored interval, in increasing order.
|
|
419
487
|
*/
|
|
@@ -433,7 +501,8 @@ rb_dit_each(VALUE self)
|
|
|
433
501
|
* call-seq:
|
|
434
502
|
* tree.to_a -> array
|
|
435
503
|
*
|
|
436
|
-
* Every stored interval, in increasing order, as an array of `[a, b]`
|
|
504
|
+
* Every stored interval, in increasing order, as an array of `[a, b, obj]`
|
|
505
|
+
* triples.
|
|
437
506
|
*/
|
|
438
507
|
static VALUE
|
|
439
508
|
rb_dit_to_a(VALUE self)
|
|
@@ -446,7 +515,7 @@ rb_dit_to_a(VALUE self)
|
|
|
446
515
|
|
|
447
516
|
/*
|
|
448
517
|
* call-seq:
|
|
449
|
-
* tree.at(x) -> [a, b] or nil
|
|
518
|
+
* tree.at(x) -> [a, b, obj] or nil
|
|
450
519
|
*
|
|
451
520
|
* The stored interval containing the point `x`, or nil. O(log n).
|
|
452
521
|
*/
|
|
@@ -455,7 +524,25 @@ rb_dit_at(VALUE self, VALUE vx)
|
|
|
455
524
|
{
|
|
456
525
|
const dit_t * tree = rb_dit_get(self);
|
|
457
526
|
const dit_node_t * node = dit_at(tree, rb_dit_value(vx));
|
|
458
|
-
return node ? rb_dit_interval(node->a, node->b) : Qnil;
|
|
527
|
+
return node ? rb_dit_interval(node->a, node->b, node->obj) : Qnil;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/*
|
|
531
|
+
* call-seq:
|
|
532
|
+
* tree[x] -> obj or nil
|
|
533
|
+
*
|
|
534
|
+
* The object of the stored interval containing the point `x`, or nil when no
|
|
535
|
+
* interval covers it. O(log n).
|
|
536
|
+
*
|
|
537
|
+
* A nil return is ambiguous: use #at or #cover? to tell "no interval here"
|
|
538
|
+
* from "an interval whose object is nil".
|
|
539
|
+
*/
|
|
540
|
+
static VALUE
|
|
541
|
+
rb_dit_aref(VALUE self, VALUE vx)
|
|
542
|
+
{
|
|
543
|
+
const dit_t * tree = rb_dit_get(self);
|
|
544
|
+
const dit_node_t * node = dit_at(tree, rb_dit_value(vx));
|
|
545
|
+
return node ? (VALUE) node->obj : Qnil;
|
|
459
546
|
}
|
|
460
547
|
|
|
461
548
|
/*
|
|
@@ -489,7 +576,7 @@ rb_dit_hull(VALUE self)
|
|
|
489
576
|
if (!dit_hull(tree, &a, &b))
|
|
490
577
|
return Qnil;
|
|
491
578
|
|
|
492
|
-
return
|
|
579
|
+
return rb_assoc_new(ULL2NUM(a), ULL2NUM(b));
|
|
493
580
|
}
|
|
494
581
|
|
|
495
582
|
/*
|
|
@@ -574,7 +661,8 @@ rb_dit_inspect(VALUE self)
|
|
|
574
661
|
* call-seq:
|
|
575
662
|
* tree.initialize_copy(other) -> self
|
|
576
663
|
*
|
|
577
|
-
* Called by #dup and #clone.
|
|
664
|
+
* Called by #dup and #clone. The copy is shallow: both trees end up sharing
|
|
665
|
+
* the same objects.
|
|
578
666
|
*/
|
|
579
667
|
static VALUE
|
|
580
668
|
rb_dit_initialize_copy(VALUE self, VALUE other)
|
|
@@ -597,16 +685,44 @@ rb_dit_initialize_copy(VALUE self, VALUE other)
|
|
|
597
685
|
VALUE interval = rb_ary_entry(intervals, i);
|
|
598
686
|
const dit_value_t a = rb_dit_value(rb_ary_entry(interval, 0));
|
|
599
687
|
const dit_value_t b = rb_dit_value(rb_ary_entry(interval, 1));
|
|
600
|
-
|
|
688
|
+
|
|
689
|
+
/* shallow copy: the two trees share the very same objects */
|
|
690
|
+
const dit_object_t obj = (dit_object_t) rb_ary_entry(interval, 2);
|
|
691
|
+
|
|
692
|
+
if (dit_insert(tree, a, b, obj) != DIT_OK)
|
|
601
693
|
rb_raise(eError, "could not copy interval [%"PRIu64"..%"PRIu64"[", a, b);
|
|
602
694
|
}
|
|
603
695
|
|
|
604
696
|
return self;
|
|
605
697
|
}
|
|
606
698
|
|
|
699
|
+
/* A C extension is only loadable by the ruby ABI it was compiled against.
|
|
700
|
+
* Nothing in the `require` path enforces that for a plain `.so` sitting in a
|
|
701
|
+
* load path - it just gets dlopen'd - and the mismatch then shows up as memory
|
|
702
|
+
* corruption somewhere else entirely. Fail loudly instead.
|
|
703
|
+
*
|
|
704
|
+
* `ruby_api_version` is the running interpreter's, `RUBY_API_VERSION_*` the
|
|
705
|
+
* headers this file was compiled with */
|
|
706
|
+
static void
|
|
707
|
+
rb_dit_check_abi(void)
|
|
708
|
+
{
|
|
709
|
+
if (ruby_api_version[0] == RUBY_API_VERSION_MAJOR &&
|
|
710
|
+
ruby_api_version[1] == RUBY_API_VERSION_MINOR)
|
|
711
|
+
return ;
|
|
712
|
+
|
|
713
|
+
rb_raise(rb_eLoadError,
|
|
714
|
+
"disjoint_interval_tree was compiled for ruby %d.%d but is being "
|
|
715
|
+
"loaded by ruby %d.%d. Rebuild the extension with that ruby: "
|
|
716
|
+
"`rake recompile`",
|
|
717
|
+
RUBY_API_VERSION_MAJOR, RUBY_API_VERSION_MINOR,
|
|
718
|
+
ruby_api_version[0], ruby_api_version[1]);
|
|
719
|
+
}
|
|
720
|
+
|
|
607
721
|
void
|
|
608
722
|
Init_disjoint_interval_tree(void)
|
|
609
723
|
{
|
|
724
|
+
rb_dit_check_abi();
|
|
725
|
+
|
|
610
726
|
cTree = rb_define_class("DisjointIntervalTree", rb_cObject);
|
|
611
727
|
rb_include_module(cTree, rb_mEnumerable);
|
|
612
728
|
|
|
@@ -623,14 +739,15 @@ Init_disjoint_interval_tree(void)
|
|
|
623
739
|
rb_define_method(cTree, "initialize", rb_dit_initialize, -1);
|
|
624
740
|
rb_define_method(cTree, "initialize_copy", rb_dit_initialize_copy, 1);
|
|
625
741
|
|
|
626
|
-
rb_define_method(cTree, "insert", rb_dit_insert,
|
|
627
|
-
rb_define_method(cTree, "insert?", rb_dit_insert_p,
|
|
742
|
+
rb_define_method(cTree, "insert", rb_dit_insert, -1);
|
|
743
|
+
rb_define_method(cTree, "insert?", rb_dit_insert_p, -1);
|
|
628
744
|
rb_define_method(cTree, "intersect", rb_dit_intersect, 2);
|
|
629
745
|
rb_define_method(cTree, "intersect?", rb_dit_intersect_p, 2);
|
|
630
746
|
rb_define_method(cTree, "remove", rb_dit_remove, 2);
|
|
631
747
|
rb_define_method(cTree, "each", rb_dit_each, 0);
|
|
632
748
|
rb_define_method(cTree, "to_a", rb_dit_to_a, 0);
|
|
633
749
|
rb_define_method(cTree, "at", rb_dit_at, 1);
|
|
750
|
+
rb_define_method(cTree, "[]", rb_dit_aref, 1);
|
|
634
751
|
rb_define_method(cTree, "cover?", rb_dit_cover_p, 1);
|
|
635
752
|
rb_define_method(cTree, "hull", rb_dit_hull, 0);
|
|
636
753
|
rb_define_method(cTree, "size", rb_dit_size, 0);
|
|
@@ -23,20 +23,25 @@ require 'disjoint_interval_tree/version'
|
|
|
23
23
|
# `[0 .. DisjointIntervalTree::MAX[`.
|
|
24
24
|
#
|
|
25
25
|
# tree = DisjointIntervalTree.new
|
|
26
|
-
# tree.insert(0, 10)
|
|
27
|
-
# tree.insert(20, 30)
|
|
26
|
+
# tree.insert(0, 10, :first)
|
|
27
|
+
# tree.insert(20, 30, :second)
|
|
28
28
|
#
|
|
29
|
-
# tree.intersect(5, 25) { |a, b| puts "[#{a}..#{b}[" }
|
|
30
|
-
# # => [0..10[
|
|
31
|
-
# # => [20..30[
|
|
29
|
+
# tree.intersect(5, 25) { |a, b, obj| puts "[#{a}..#{b}[ #{obj}" }
|
|
30
|
+
# # => [0..10[ first
|
|
31
|
+
# # => [20..30[ second
|
|
32
32
|
#
|
|
33
|
+
# tree[5] # => :first
|
|
33
34
|
# tree.remove(5, 25) # => 2
|
|
34
35
|
# tree.to_a # => []
|
|
35
36
|
#
|
|
37
|
+
# Each interval carries an object, given at insertion - it defaults to nil -
|
|
38
|
+
# and handed back by every query and traversal.
|
|
39
|
+
#
|
|
36
40
|
# Inserting an interval overlapping an already inserted one is a usage
|
|
37
41
|
# contract violation and raises DisjointIntervalTree::OverlapError.
|
|
38
42
|
class DisjointIntervalTree
|
|
39
|
-
# Two trees are equal when they hold the same intervals
|
|
43
|
+
# Two trees are equal when they hold the same intervals, associated with
|
|
44
|
+
# objects comparing equal.
|
|
40
45
|
def ==(other)
|
|
41
46
|
other.is_a?(DisjointIntervalTree) && to_a == other.to_a
|
|
42
47
|
end
|