interval_set 0.1.0.pre.RC1

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,995 @@
1
+ require_relative 'interval_set/version'
2
+ require 'treemap-fork'
3
+
4
+ # IntervalSet implements a set of sorted non-overlapping ranges.
5
+ # A range's start is always interpreted as inclusive while the end is exclusive
6
+ class IntervalSet
7
+ include Enumerable
8
+
9
+ # Builds a new IntervalSet from the supplied ranges. Overlapping ranges will be merged.
10
+ # IntervalSet[] # -> []
11
+ # IntervalSet[0...1] # -> [0...1]
12
+ # IntervalSet[0...1, 2...3] # -> [0...1, 2...3]
13
+ # IntervalSet[0...1, 1...2] # -> [0...2]
14
+ #
15
+ # array = [0...1, 2...3]
16
+ # IntervalSet[*array] # -> [0...1, 2...3]
17
+ #
18
+ # @param ranges [Range[]] a list of ranges to be added to the new IntervalSet
19
+ # @return [IntervalSet] a new IntervalSet containing the supplied ranges.
20
+ def self.[](*ranges)
21
+ IntervalSet.new.tap do |interval_set|
22
+ ranges.each {|range| interval_set << range}
23
+ end
24
+ end
25
+
26
+ # Returns an empty instance of IntervalSet.
27
+ # @param range_map [TreeMap] a TreeMap of ranges. For internal use only.
28
+ def initialize(range_map = TreeMap.new)
29
+ unless range_map.instance_of?(TreeMap) || range_map.instance_of?(TreeMap::BoundedMap)
30
+ raise ArgumentError.new("invalid range_map #{range_map}")
31
+ end
32
+
33
+ @range_map = range_map
34
+
35
+ update_bounds
36
+ end
37
+
38
+ # Returns +true+ if this IntervalSet contains no ranges.
39
+ def empty?
40
+ @range_map.empty?
41
+ end
42
+
43
+ # Returns the lower bound of this IntervalSet.
44
+ #
45
+ # IntervalSet[0...1, 2...3].min # -> 0
46
+ # IntervalSet[].min # -> nil
47
+ #
48
+ # @return the lower bound or +nil+ if empty.
49
+ def min
50
+ @min
51
+ end
52
+
53
+ # Returns the upper bound of this IntervalSet.
54
+ #
55
+ # IntervalSet[0...1, 2...3].max # -> 3
56
+ # IntervalSet[].max # -> nil
57
+ #
58
+ # @return the upper bound or +nil+ if empty.
59
+ def max
60
+ @max
61
+ end
62
+
63
+ # Returns the bounds of this IntervalSet.
64
+ #
65
+ # IntervalSet[0...1, 2...3].bounds # -> 0...3
66
+ # IntervalSet[].bounds # -> nil
67
+ #
68
+ # @return [Range] a range from lower to upper bound or +nil+ if empty.
69
+ def bounds
70
+ empty? ? nil : min...max
71
+ end
72
+
73
+ # Returns +true+ if two IntervalSets are equal.
74
+ #
75
+ # IntervalSet[0...1] == IntervalSet[0...1] # -> true
76
+ # IntervalSet[0...1] == IntervalSet[1...2] # -> false
77
+ #
78
+ # @param other [Object] the other object.
79
+ def eql?(other)
80
+ return false if other.nil? || !other.is_a?(IntervalSet) || count != other.count || bounds != other.bounds
81
+
82
+ lhs_iter = enum_for
83
+ rhs_iter = other.enum_for
84
+
85
+ count.times.all? {lhs_iter.next == rhs_iter.next}
86
+ end
87
+
88
+ alias_method :==, :eql?
89
+
90
+ # Returns +true+ if the other object represents a equal
91
+ # set of ranges as this IntervalSet.
92
+ #
93
+ # IntervalSet[1...2].eql_set?(1...2) # -> true
94
+ # IntervalSet[1...2].eql_set?(IntervalSet[1...2]) # -> true
95
+ #
96
+ # @param other [Range | IntervalSet] the other object.
97
+ def eql_set?(other)
98
+ case other
99
+ when Range
100
+ eql_range?(other)
101
+ when IntervalSet
102
+ eql?(other)
103
+ else
104
+ IntervalSet.unexpected_object(other)
105
+ end
106
+ end
107
+
108
+ # Returns +true+ if this IntervalSet contains the given element.
109
+ #
110
+ # i = IntervalSet[0...1] # -> [0...1]
111
+ #
112
+ # i.include?(0) # -> true
113
+ # i.include?(0.5) # -> true
114
+ # i.include?(1) # -> false ; a range's end is exclusive
115
+ #
116
+ # Note that the given element must be comparable to elements already in this
117
+ # set. Otherwise, the behavior is undefined.
118
+ #
119
+ # @param element [Object]
120
+ def include?(element)
121
+ return false if element.nil?
122
+
123
+ floor_entry = @range_map.floor_entry(element)
124
+
125
+ !floor_entry.nil? && floor_entry.value.last > element
126
+ end
127
+
128
+ alias_method :===, :include?
129
+
130
+ # Returns +true+ if this IntervalSet includes all elements
131
+ # of the other object.
132
+ #
133
+ # IntervalSet[0...1] >= IntervalSet[0...1] # -> true
134
+ # IntervalSet[0...2] >= IntervalSet[0...1] # -> true
135
+ # IntervalSet[0...1] >= IntervalSet[0...1, 2...3] # -> false
136
+ # IntervalSet[0...3] >= IntervalSet[0...1, 2...3] # -> true
137
+ #
138
+ # # You can also supply ranges
139
+ # IntervalSet[0...2].superset?(0...1) # -> true
140
+ #
141
+ # @param other [Range | IntervalSet] the other object.
142
+ def superset?(other)
143
+ case other
144
+ when Range
145
+ superset_range?(other)
146
+ when IntervalSet
147
+ superset_interval_set?(other)
148
+ else
149
+ IntervalSet.unexpected_object(other)
150
+ end
151
+ end
152
+
153
+ alias_method :>=, :superset?
154
+
155
+ # Returns +true+ if all elements of this IntervalSet are
156
+ # included by the other object.
157
+ #
158
+ # IntervalSet[0...1] <= IntervalSet[0...1] # -> true
159
+ # IntervalSet[0...1] <= IntervalSet[0...1, 2...3] # -> true
160
+ # IntervalSet[0...1, 2...3] <= IntervalSet[0...1] # -> false
161
+ # IntervalSet[0...1, 2...3] <= IntervalSet[0...3] # -> true
162
+ #
163
+ # # You can also supply ranges
164
+ # IntervalSet[0...1, 2...3].subset?(0...3) # -> true
165
+ #
166
+ # @param other [Range | IntervalSet] the other object.
167
+ def subset?(other)
168
+ case other
169
+ when Range
170
+ subset_range?(other)
171
+ when IntervalSet
172
+ other.superset_interval_set?(self)
173
+ else
174
+ IntervalSet.unexpected_object(other)
175
+ end
176
+ end
177
+
178
+ alias_method :<=, :subset?
179
+
180
+ # Returns +true+ if this IntervalSet is a proper superset of the other.
181
+ #
182
+ # IntervalSet[0...2] > IntervalSet[0...1] # -> true
183
+ # IntervalSet[0...2] > IntervalSet[0...2] # -> false
184
+ # IntervalSet[0...2] > IntervalSet[1...3] # -> false
185
+ #
186
+ # # Compare to ranges
187
+ # IntervalSet[0...3].superset?(1...2) # -> true
188
+ #
189
+ # @param other [Range | IntervalSet] the other object.
190
+ def proper_superset?(other)
191
+ !eql_set?(other) && superset?(other)
192
+ end
193
+
194
+ alias_method :>, :proper_superset?
195
+
196
+ # Return +true+ if this IntervalSet is a proper subset of the other.
197
+ #
198
+ # IntervalSet[0...1] < IntervalSet[0...2] # -> true
199
+ # IntervalSet[1...3] < IntervalSet[0...2] # -> false
200
+ # IntervalSet[1...3] < IntervalSet[0...2] # -> false
201
+ #
202
+ # # Compare to ranges
203
+ # IntervalSet[1...2].subset?(0...3) # -> false
204
+ #
205
+ # @param other [Range | IntervalSet] the other object.
206
+ def proper_subset?(other)
207
+ !eql_set?(other) && subset?(other)
208
+ end
209
+
210
+ alias_method :<, :proper_subset?
211
+
212
+ # Returns +true+ if the given range has common elements with the
213
+ # bounding range of this IntervalSet.
214
+ #
215
+ # IntervalSet[1...2].bounds_intersected_by?(2...3) # -> false
216
+ # IntervalSet[1...2, 5...6].bounds_intersected_by?(3...4) # -> true
217
+ #
218
+ # @param range [Range]
219
+ def bounds_intersected_by?(range)
220
+ return false if IntervalSet.range_empty?(range)
221
+
222
+ !empty? && range.first < max && range.last > min
223
+ end
224
+
225
+ # Returns +true+ if the given range has common elements with the
226
+ # bounding range or the bounds of this IntervalSet.
227
+ #
228
+ # IntervalSet[1...2].bounds_intersected_or_touched_by?(2...3) # -> true
229
+ # IntervalSet[1...2].bounds_intersected_or_touched_by?(3...4) # -> false
230
+ # IntervalSet[1...2, 5...6].bounds_intersected_or_touched_by?(3...4) # -> true
231
+ #
232
+ # @param range [Range]
233
+ def bounds_intersected_or_touched_by?(range)
234
+ return false if IntervalSet.range_empty?(range)
235
+
236
+ !empty? && range.first <= max && range.last >= min
237
+ end
238
+
239
+ # Returns +true+ if the given object has any common elements with
240
+ # this IntervalSet.
241
+ #
242
+ # i = IntervalSet[0...1] # -> [0...1]
243
+ #
244
+ # # Ranges only need a single common element with the interval set
245
+ # i.intersect?(0...1) # -> true
246
+ # i.intersect?(0...2) # -> true
247
+ # i.intersect?(1...2) # -> false ; the start of a range is inclusive but the end exclusive
248
+ #
249
+ # # The same applies for interval sets
250
+ # i.intersect?(IntervalSet[0...1]) # -> true
251
+ # i.intersect?(IntervalSet[0...1, 2...3]) # -> true
252
+ # i.intersect?(IntervalSet[2...3]) # -> false
253
+ #
254
+ # @param other [Range | IntervalSet] the other object.
255
+ def intersect?(other)
256
+ case other
257
+ when Range
258
+ intersect_range?(other)
259
+ when IntervalSet
260
+ intersect_interval_set?(other)
261
+ else
262
+ IntervalSet.unexpected_object(other)
263
+ end
264
+ end
265
+
266
+ # Counts the number of ranges contained by this IntervalSet.
267
+ #
268
+ # i = IntervalSet[] # -> []
269
+ # i.count # -> 0
270
+ # i << (0...1) # -> [0...1]
271
+ # i.count # -> 1
272
+ # i << (2...3) # -> [0...1, 2...3]
273
+ # i.count # -> 2
274
+ # i << (1...2) # -> [0...3]
275
+ # i.count # -> 1
276
+ #
277
+ # @return [Fixnum] the number of ranges.
278
+ def count
279
+ @range_map.count
280
+ end
281
+
282
+ # Adds the other object's elements to this IntervalSet.
283
+ # The result is stored in this IntervalSet.
284
+ #
285
+ # IntervalSet.new.add(0...1) # -> [0...1]
286
+ # IntervalSet.new << (0...1) # -> [0...1]
287
+ #
288
+ # i = IntervalSet.new # -> []
289
+ # i << (0...1) # -> [0...1]
290
+ # i << (2...3) # -> [0...1, 2...3]
291
+ # i << (1...2) # -> [0...3]
292
+ # i << (-1...4) # -> [-1...4]
293
+ #
294
+ # @param other [Range, IntervalSet] the other object.
295
+ # @return [IntervalSet] self.
296
+ def add(other)
297
+ case other
298
+ when Range
299
+ add_range(other)
300
+ when IntervalSet
301
+ add_interval_set(other)
302
+ else
303
+ IntervalSet.unexpected_object(other)
304
+ end
305
+ end
306
+
307
+ alias_method :<<, :add
308
+ alias_method :union!, :add
309
+
310
+ # Removes the other object's elements from this IntervalSet.
311
+ # The result is stored in this IntervalSet.
312
+ #
313
+ # i = IntervalSet[0...10] # -> [0...10]
314
+ # i.remove(0...2) # -> [8...10]
315
+ # i >> (2...8) # -> [0...2, 8...10]
316
+ #
317
+ # @param other [Range, IntervalSet] the other object.
318
+ # @return [IntervalSet] self.
319
+ def remove(other)
320
+ case other
321
+ when Range
322
+ remove_range(other)
323
+ when IntervalSet
324
+ remove_interval_set(other)
325
+ else
326
+ IntervalSet.unexpected_object(other)
327
+ end
328
+ end
329
+
330
+ alias_method :>>, :remove
331
+ alias_method :difference!, :remove
332
+
333
+ # Intersects the other object's elements with this IntervalSet.
334
+ # The result is stored in this IntervalSet.
335
+ #
336
+ # i = IntervalSet[0...2, 3...5].intersect(1...5) # -> [1...2, 3...5]
337
+ # i # -> [1...2, 3...5]
338
+ #
339
+ # @param other [Range, IntervalSet] the other object.
340
+ # @return [IntervalSet] self.
341
+ def intersect(other)
342
+ case other
343
+ when Range
344
+ intersect_range(other)
345
+ when IntervalSet
346
+ intersect_interval_set(other)
347
+ else
348
+ IntervalSet.unexpected_object(other)
349
+ end
350
+ end
351
+
352
+ alias_method :intersection!, :intersect
353
+
354
+ # Intersects the other object's elements with this IntervalSet.
355
+ # The result is stored in a new IntervalSet.
356
+ #
357
+ # IntervalSet[0...2, 3...5] & IntervalSet[1...4, 5...6] # -> [1...2, 3...4]
358
+ #
359
+ # @param other [Range, IntervalSet] the other object.
360
+ # @return [IntervalSet] a new IntervalSet containing the intersection.
361
+ def intersection(other)
362
+ case other
363
+ when Range
364
+ intersection_range(other)
365
+ when IntervalSet
366
+ intersection_interval_set(other)
367
+ else
368
+ IntervalSet.unexpected_object(other)
369
+ end
370
+ end
371
+
372
+ alias_method :&, :intersection
373
+
374
+ # Joins the other object's elements with this IntervalSet.
375
+ # The result is stored in a new IntervalSet.
376
+ #
377
+ # IntervalSet[0...1, 2...3] | IntervalSet[1...2, 4...5] # -> [0...3, 4...5]
378
+ #
379
+ # Note that using +add+ or +union!+ is more efficient than
380
+ # <code>+=</code> or <code>|=</code>.
381
+ #
382
+ # @param other [Range, IntervalSet] the other object.
383
+ # @return [IntervalSet] a new IntervalSet containing the union.
384
+ def union(other)
385
+ case other
386
+ when Range
387
+ union_range(other)
388
+ when IntervalSet
389
+ union_interval_set(other)
390
+ else
391
+ IntervalSet.unexpected_object(other)
392
+ end
393
+ end
394
+
395
+ alias_method :|, :union
396
+ alias_method :+, :union
397
+
398
+ # Subtracts the other object's elements from this IntervalSet.
399
+ # The result is stored in a new IntervalSet.
400
+ #
401
+ # IntervalSet[0...2, 3...5] - IntervalSet[1...4, 5...6] # -> [0...1, 4...5]
402
+ #
403
+ # Note that using +remove+ or +difference!+ is more efficient
404
+ # than <code>-=</code>.
405
+ #
406
+ # @param other [Range, IntervalSet] the other object.
407
+ # @return [IntervalSet] a new IntervalSet containing the difference.
408
+ def difference(other)
409
+ case other
410
+ when Range
411
+ difference_range(other)
412
+ when IntervalSet
413
+ difference_interval_set(other)
414
+ else
415
+ IntervalSet.unexpected_object(other)
416
+ end
417
+ end
418
+
419
+ alias_method :-, :difference
420
+
421
+ # Calculates a new IntervalSet which only contains elements exclusively from
422
+ # either this or the given object.
423
+ #
424
+ # This operation is equivalent to <code>(self | other) - (self & other)</code>
425
+ #
426
+ # IntervalSet[0...1] ^ IntervalSet[1...2] # -> [0...2]
427
+ # IntervalSet[0...2, 4...6] ^ IntervalSet[1...5, 7...8] # -> [0...1, 2...4, 5...6, 7...8]
428
+ # IntervalSet[0...1] ^ IntervalSet[0...1] # -> []
429
+ #
430
+ # @param other [Range, IntervalSet]
431
+ # @return [IntervalSet] a new IntervalSet containing the exclusive set.
432
+ def xor(other)
433
+ clone.xor!(other)
434
+ end
435
+
436
+ alias_method :^, :xor
437
+
438
+ # Calculates the set which contains elements exclusively from
439
+ # either this or the given object. The result of this operation
440
+ # is stored in this set.
441
+ #
442
+ # The resulting set is equivalent to <code>(self | other) - (self & other)</code>
443
+ #
444
+ # IntervalSet[0...1].xor!(IntervalSet[1...2]) # -> [0...2]
445
+ # IntervalSet[0...2, 4...6].xor!(IntervalSet[1...5, 7...8]) # -> [0...1, 2...4, 5...6, 7...8]
446
+ # IntervalSet[0...1].xor!(IntervalSet[0...1]) # -> []
447
+ #
448
+ # @param other [Range, IntervalSet]
449
+ # @return [IntervalSet] a new IntervalSet containing the exclusive set.
450
+ def xor!(other)
451
+ intersection = self & other
452
+
453
+ add(other).remove(intersection)
454
+ end
455
+
456
+ # Convolves the other object's elements with this IntervalSet.
457
+ # The result is stored in this IntervalSet.
458
+ #
459
+ # The result will contain all elements which can be obtained by adding
460
+ # any pair of elements from both sets. A ∗ B = { a + b | a ∈ A ∧ b ∈ B }
461
+ #
462
+ # # Convolve with a range (effectively buffers the set)
463
+ # IntervalSet[0...4].convolve!(-1...2) # -> [-1...6]
464
+ #
465
+ # # Convolving with empty or reversed ranges result in an empty set.
466
+ # IntervalSet[0...4].convolve!(0...0) # -> []
467
+ # IntervalSet[0...4].convolve!(1...0) # -> []
468
+ #
469
+ # # Convolve with a interval set
470
+ # IntervalSet[0...1, 10...12].convolve!(IntervalSet[-2...1, 1...2]) # -> [-2...3, 8...14]
471
+ #
472
+ # @param other [Range | IntervalSet] the other object.
473
+ # @return [IntervalSet] self
474
+ def convolve!(other)
475
+ case other
476
+ when Range
477
+ convolve_range!(other)
478
+ when IntervalSet
479
+ convolve_interval_set!(other)
480
+ else
481
+ IntervalSet.unexpected_object(other)
482
+ end
483
+ end
484
+
485
+ # Convolves the other object's elements with this IntervalSet.
486
+ # The result is stored in a new IntervalSet.
487
+ #
488
+ # The result will contain all elements which can be obtained by adding
489
+ # any pair of elements from both sets. A ∗ B = { a + b | a ∈ A ∧ b ∈ B }
490
+ #
491
+ # # Convolve with a range (effectively buffers the set)
492
+ # IntervalSet[0...4] * (-1...2) # -> [-1...6]
493
+ #
494
+ # # Convolving with empty or reversed ranges result in an empty set.
495
+ # IntervalSet[0...4] * (0...0) # -> []
496
+ # IntervalSet[0...4] * (1...0) # -> []
497
+ #
498
+ # # Convolve with a interval set
499
+ # IntervalSet[0...1, 10...12] * IntervalSet[-2...1, 1...2] # -> [-2...3, 8...14]
500
+ #
501
+ # @param other [Range | IntervalSet] the other object.
502
+ # @return [IntervalSet] a new IntervalSet containing the convolution.
503
+ def convolve(other)
504
+ clone.convolve!(other)
505
+ end
506
+
507
+ alias_method :*, :convolve
508
+
509
+ # Shifts this IntervalSet by the given amount.
510
+ # The result is stored in this IntervalSet.
511
+ #
512
+ # IntervalSet[0...1].shift(1) # -> [1...2]
513
+ #
514
+ # Note that +shift(0)+ will not be optimized since IntervalSet does
515
+ # not assume numbers as element type.
516
+ #
517
+ # @param amount [Object]
518
+ # @return [IntervalSet] self.
519
+ def shift!(amount)
520
+ ranges = map {|range| range.first + amount...range.last + amount}
521
+ clear
522
+ ranges.each {|range| put(range)}
523
+ update_bounds
524
+
525
+ self
526
+ end
527
+
528
+ # Shifts this IntervalSet by the given amount.
529
+ # The result is stored in a new IntervalSet.
530
+ #
531
+ # IntervalSet[0...1].shift!(1) # -> [1...2]
532
+ #
533
+ # Note that +shift!(0)+ will not be optimized since IntervalSet does
534
+ # not assume numbers as element type.
535
+ #
536
+ # @param amount [Object]
537
+ # @return [IntervalSet] a new IntervalSet shifted by +amount+.
538
+ def shift(amount)
539
+ clone.shift!(amount)
540
+ end
541
+
542
+ # Buffers this IntervalSet by adding a left and right margin to each range.
543
+ # The result is stored in this IntervalSet.
544
+ #
545
+ # IntervalSet[1...2].buffer!(1, 2) # -> [0...4]
546
+ #
547
+ # # negative values will shrink the ranges
548
+ # IntervalSet[0...4].buffer!(-1, -2) # -> [1...2]
549
+ # IntervalSet[1...2].buffer!(-0.5, -0.5) # -> []
550
+ #
551
+ # @param left [Object] margin added to the left side of each range.
552
+ # @param right [Object] margin added to the right side of each range.
553
+ # @return [IntervalSet] self.
554
+ def buffer!(left, right)
555
+ ranges = map do |range|
556
+ range.first - left...range.last + right
557
+ end.select do |range|
558
+ range.first < range.last
559
+ end
560
+
561
+ clear
562
+ ranges.each {|r| add_range(r)}
563
+
564
+ self
565
+ end
566
+
567
+ # Buffers this IntervalSet by adding a left and right margin to each range.
568
+ # The result is stored in a new IntervalSet.
569
+ #
570
+ # IntervalSet[1...2].buffer(1, 2) # -> [0...4]
571
+ #
572
+ # # negative values will shrink the ranges
573
+ # IntervalSet[0...4].buffer(-1, -2) # -> [1...2]
574
+ # IntervalSet[1...2].buffer(-0.5, -0.5) # -> []
575
+ #
576
+ # @param left [Object] margin added to the left side of each range.
577
+ # @param right [Object] margin added to the right side of each range.
578
+ # @return [IntervalSet] a new IntervalSet containing the buffered ranges.
579
+ def buffer(left, right)
580
+ clone.buffer!(left, right)
581
+ end
582
+
583
+ # Removes all elements from this IntervalSet.
584
+ # @return [IntervalSet] self.
585
+ def clear
586
+ @range_map.clear
587
+ @min = nil
588
+ @max = nil
589
+
590
+ self
591
+ end
592
+
593
+ # Iterates over all ranges of this set in ascending order.
594
+ # @yield all ranges.
595
+ # @yieldparam [Range] range.
596
+ # @return [void]
597
+ def each
598
+ @range_map.each_node {|node| yield node.value}
599
+ end
600
+
601
+ # Returns a new IntervalSet instance containing all ranges of this IntervalSet.
602
+ # @return [IntervalSet] the clone.
603
+ def clone
604
+ IntervalSet.new.copy(self)
605
+ end
606
+
607
+ # Replaces the content of this IntervalSet by the content of the given IntervalSet.
608
+ # @param interval_set [IntervalSet] the other IntervalSet to be copied
609
+ # @return [IntervalSet] self.
610
+ def copy(interval_set)
611
+ clear
612
+ interval_set.each {|range| put(range)}
613
+ @min = interval_set.min
614
+ @max = interval_set.max
615
+
616
+ self
617
+ end
618
+
619
+ # Returns a String representation of this IntervalSet.
620
+ #
621
+ # IntervalSet[].to_s # -> "[]"
622
+ # IntervalSet[0...1].to_s # -> "[0...1]"
623
+ # IntervalSet[0...1, 2...3].to_s # -> "[0...1, 2...3]"
624
+ #
625
+ # @return [String] the String representation.
626
+ def to_s
627
+ string_io = StringIO.new
628
+
629
+ last_index = count - 1
630
+
631
+ string_io << '['
632
+ each_with_index do |range, i|
633
+ string_io << range
634
+ string_io << ', ' if i < last_index
635
+ end
636
+ string_io << ']'
637
+
638
+ string_io.string
639
+ end
640
+
641
+ alias_method :inspect, :to_s
642
+
643
+ protected
644
+
645
+ def range_map
646
+ @range_map
647
+ end
648
+
649
+ def put(range)
650
+ @range_map.put(range.first, IntervalSet.normalize_range(range))
651
+ end
652
+
653
+ def put_and_update_bounds(range)
654
+ put(range)
655
+
656
+ if @min.nil? && @max.nil?
657
+ @min = range.first
658
+ @max = range.last
659
+ else
660
+ @min = [range.first, @min].min
661
+ @max = [range.last, @max].max
662
+ end
663
+ end
664
+
665
+ def superset_range?(range)
666
+ return true if IntervalSet.range_empty?(range)
667
+ return false if empty? || !bounds_intersected_by?(range)
668
+
669
+ # left.min <= range.first
670
+ left_entry = @range_map.floor_entry(range.first)
671
+
672
+ # left.max >= range.last
673
+ !left_entry.nil? && left_entry.value.last >= range.last
674
+ end
675
+
676
+ def superset_interval_set?(interval_set)
677
+ return true if interval_set == self || interval_set.empty?
678
+ return false if empty? || !interval_set.bounds_intersected_by?(bounds)
679
+
680
+ interval_set.all? {|range| superset_range?(range)}
681
+ end
682
+
683
+ def subset_range?(range)
684
+ return true if empty?
685
+ return false if IntervalSet.range_empty?(range)
686
+
687
+ empty? || (range.first <= min && range.last >= max)
688
+ end
689
+
690
+ def intersect_range?(range)
691
+ return false unless bounds_intersected_by?(range)
692
+
693
+ # left.min < range.last
694
+ left_entry = @range_map.lower_entry(range.last)
695
+
696
+ # left.max > range.first
697
+ !left_entry.nil? && left_entry.value.last > range.first
698
+ end
699
+
700
+ def intersect_interval_set?(interval_set)
701
+ return false if empty? || !bounds_intersected_by?(interval_set.bounds)
702
+
703
+ sub_set(interval_set.bounds).any? {|range| intersect_range?(range)}
704
+ end
705
+
706
+ def eql_range?(range)
707
+ return true if empty? && IntervalSet.range_empty?(range)
708
+
709
+ count == 1 && bounds == range
710
+ end
711
+
712
+ def sub_set(range)
713
+ # left.min < range.first
714
+ left_entry = @range_map.lower_entry(range.first)
715
+
716
+ # left.max > range.first
717
+ include_left = !left_entry.nil? && left_entry.value.last > range.first
718
+
719
+ bound_min = include_left ? left_entry.value.first : range.first
720
+ sub_map = @range_map.sub_map(bound_min, range.last)
721
+
722
+ IntervalSet.new(sub_map)
723
+ end
724
+
725
+ def head_set(value)
726
+ head_map = @range_map.head_map(value)
727
+
728
+ IntervalSet.new(head_map)
729
+ end
730
+
731
+ def tail_set(value)
732
+ # left.min < value
733
+ left_entry = @range_map.lower_entry(value)
734
+
735
+ # left.max > value
736
+ include_left = !left_entry.nil? && left_entry.value.last > value
737
+
738
+ bound_min = include_left ? left_entry.value.first : value
739
+ tail_map = @range_map.tail_map(bound_min)
740
+
741
+ IntervalSet.new(tail_map)
742
+ end
743
+
744
+ def add_range(range)
745
+ # ignore empty or reversed ranges
746
+ return self if IntervalSet.range_empty?(range)
747
+
748
+ # short cut
749
+ unless bounds_intersected_or_touched_by?(range)
750
+ put_and_update_bounds(range)
751
+ return self
752
+ end
753
+
754
+ # short cut
755
+ if subset_range?(range)
756
+ clear
757
+ put_and_update_bounds(range)
758
+ return self
759
+ end
760
+
761
+ # range.first <= core.min <= range.last
762
+ core = @range_map.sub_map(range.first, true, range.last, true)
763
+
764
+ # short cut if range already included
765
+ if !core.empty? && core.first_entry == core.last_entry
766
+ core_range = core.first_entry.value
767
+
768
+ return self if core_range.first == range.first && core_range.last == range.last
769
+ end
770
+
771
+ # left.min < range.first
772
+ left_entry = @range_map.lower_entry(range.first)
773
+ # right.min <= range.last
774
+ right_entry = core.empty? ? left_entry : core.last_entry
775
+
776
+ # determine boundaries
777
+
778
+ # left.max >= range.first
779
+ include_left = !left_entry.nil? && left_entry.value.last >= range.first
780
+ # right.max > range.last
781
+ include_right = !right_entry.nil? && right_entry.value.last > range.last
782
+
783
+ left_boundary = include_left ? left_entry.key : range.first
784
+ right_boundary = include_right ? right_entry.value.last : range.last
785
+
786
+ @range_map.remove(left_boundary) if include_left
787
+
788
+ core.keys.each {|key| @range_map.remove(key)}
789
+
790
+ # add range
791
+
792
+ if !include_left && !include_right
793
+ put_and_update_bounds(range)
794
+ else
795
+ put_and_update_bounds(left_boundary...right_boundary)
796
+ end
797
+
798
+ self
799
+ end
800
+
801
+ def add_interval_set(interval_set)
802
+ return self if interval_set == self || interval_set.empty?
803
+
804
+ interval_set.each {|range| add_range(range)}
805
+
806
+ self
807
+ end
808
+
809
+ def remove_range(range)
810
+ return self unless bounds_intersected_by?(range)
811
+
812
+ # range.first <= core.min <= range.last
813
+ core = @range_map.sub_map(range.first, true, range.last, false)
814
+
815
+ # left.min < range.first
816
+ left_entry = @range_map.lower_entry(range.first)
817
+ # right.min < range.last
818
+ right_entry = core.empty? ? left_entry : core.last_entry
819
+
820
+ # left.max > range.to
821
+ include_left = !left_entry.nil? && left_entry.value.last > range.first
822
+ # right.max > range.last
823
+ include_right = !right_entry.nil? && right_entry.value.last > range.last
824
+
825
+ core.keys.each {|key| @range_map.remove(key)}
826
+
827
+ # right first since right might be same as left
828
+ put(range.last...right_entry.value.last) if include_right
829
+ put(left_entry.key...range.first) if include_left
830
+ update_bounds
831
+
832
+ self
833
+ end
834
+
835
+ def remove_interval_set(interval_set)
836
+ if interval_set == self
837
+ clear
838
+ else
839
+ interval_set.each {|range| remove_range(range)}
840
+ end
841
+
842
+ self
843
+ end
844
+
845
+ def intersect_range(range)
846
+ unless bounds_intersected_by?(range)
847
+ clear
848
+ return self
849
+ end
850
+
851
+ return self if subset_range?(range)
852
+
853
+ # left_map.min < range.first
854
+ left_map = @range_map.head_map(range.first, false)
855
+ # right_map.min >= range.last
856
+ right_map = @range_map.tail_map(range.last, true)
857
+
858
+ # left.min < range.first
859
+ left_entry = left_map.last_entry
860
+ # right.min < range.last
861
+ right_entry = @range_map.lower_entry(range.last)
862
+
863
+ # left.max > range.first
864
+ include_left = !left_entry.nil? && left_entry.value.last > range.first
865
+ # right.max > right.max
866
+ include_right = !right_entry.nil? && right_entry.value.last > range.last
867
+
868
+ left_map.keys.each {|key| @range_map.remove(key)}
869
+ right_map.keys.each {|key| @range_map.remove(key)}
870
+
871
+ put(range.first...[left_entry.value.last, range.last].min) if include_left
872
+ put([right_entry.key, range.first].max...range.last) if include_right
873
+ update_bounds
874
+
875
+ self
876
+ end
877
+
878
+ def intersect_interval_set(interval_set)
879
+ return self if interval_set == self
880
+
881
+ if interval_set.empty? || !bounds_intersected_by?(interval_set.bounds)
882
+ clear
883
+ return self
884
+ end
885
+
886
+ intersection = interval_set.sub_set(bounds).map do |range|
887
+ IntervalSet.new.tap do |interval_set_item|
888
+ interval_set_item.add_interval_set(sub_set(range))
889
+ interval_set_item.intersect_range(range)
890
+ end
891
+ end.reduce do |acc, interval_set_item|
892
+ acc.add_interval_set(interval_set_item); acc
893
+ end
894
+
895
+ @range_map = intersection.range_map
896
+ @min = intersection.min
897
+ @max = intersection.max
898
+
899
+ self
900
+ end
901
+
902
+ def union_range(range)
903
+ new_interval_set = IntervalSet.new
904
+ new_interval_set.add_interval_set(self) unless subset_range?(range)
905
+ new_interval_set.add_range(range)
906
+ end
907
+
908
+ def union_interval_set(interval_set)
909
+ new_interval_set = clone
910
+ new_interval_set.add_interval_set(interval_set)
911
+ end
912
+
913
+ def difference_range(range)
914
+ new_interval_set = IntervalSet.new
915
+
916
+ return new_interval_set if subset_range?(range)
917
+ return new_interval_set.copy(self) unless bounds_intersected_by?(range)
918
+
919
+ unless IntervalSet.range_empty?(range)
920
+ new_interval_set.add_interval_set(head_set(range.first))
921
+ new_interval_set.add_interval_set(tail_set(range.last))
922
+ new_interval_set.remove_range(range)
923
+ end
924
+ end
925
+
926
+ def difference_interval_set(interval_set)
927
+ new_interval_set = IntervalSet.new
928
+
929
+ return new_interval_set if interval_set == self || empty?
930
+
931
+ new_interval_set.copy(self)
932
+ new_interval_set.remove_interval_set(interval_set) if !interval_set.empty? && bounds_intersected_by?(interval_set.bounds)
933
+ new_interval_set
934
+ end
935
+
936
+ def intersection_range(range)
937
+ new_interval_set = IntervalSet.new
938
+
939
+ return new_interval_set unless bounds_intersected_by?(range)
940
+ return new_interval_set.copy(self) if subset_range?(range)
941
+
942
+ new_interval_set.add(sub_set(range))
943
+ new_interval_set.intersect_range(range)
944
+ end
945
+
946
+ def intersection_interval_set(interval_set)
947
+ new_interval_set = IntervalSet.new
948
+
949
+ return new_interval_set if interval_set.empty? || !bounds_intersected_by?(interval_set.bounds)
950
+
951
+ new_interval_set.add_interval_set(self)
952
+ new_interval_set.intersect_interval_set(interval_set.sub_set(bounds))
953
+ end
954
+
955
+ def convolve_range!(range)
956
+ if IntervalSet.range_empty?(range)
957
+ clear
958
+ else
959
+ buffer!(-range.first, range.last)
960
+ end
961
+ end
962
+
963
+ def convolve_interval_set!(interval_set)
964
+ interval_sets = interval_set.map {|range| clone.convolve_range!(range)}
965
+ clear
966
+ interval_sets.each {|rs| add_interval_set(rs)}
967
+
968
+ self
969
+ end
970
+
971
+ private
972
+
973
+ def update_bounds
974
+ if empty?
975
+ @min = nil
976
+ @max = nil
977
+ else
978
+ @min = @range_map.first_entry.value.first
979
+ @max = @range_map.last_entry.value.last
980
+ end
981
+ end
982
+
983
+ def self.range_empty?(range)
984
+ range.first >= range.last
985
+ end
986
+
987
+ def self.normalize_range(range)
988
+ range.exclude_end? ? range : range.first...range.last
989
+ end
990
+
991
+ def self.unexpected_object(object)
992
+ raise ArgumentError.new("unexpected object #{object}")
993
+ end
994
+
995
+ end