rangeary 0.3.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.
@@ -1,1210 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- ## Load required files.
4
- # require "range_extd/range_extd"
5
- err1st = nil
6
- req_files = %w(lib/range_extd/range_extd)
7
- req_files.each do |req_file|
8
- while ! req_file.empty?
9
- begin
10
- require req_file
11
- rescue LoadError => errLoad
12
- err1st = errLoad if err1st.nil?
13
- if %r@/@ =~ req_file
14
- if req_file.sub!(%r@[^/]*/@, '').nil? # Will search for the next directory down.
15
- raise
16
- end
17
- else
18
- req_file = ''
19
- break
20
- end
21
- else
22
- break
23
- end
24
- end
25
- if req_file.empty?
26
- raise err1st
27
- end
28
- end # req_files.each do |req_file|
29
-
30
-
31
-
32
- def Rangeary(*rest)
33
- Rangeary.new(*rest)
34
- end
35
-
36
- # =Class Rangeary < Array
37
- #
38
- # Authors:: Masa Sakano
39
- # License:: MIT
40
- #
41
- # ==Summary
42
- #
43
- # Class to express the multiple ranges.
44
- #
45
- # The library package <tt>range_extd</tt> is required.
46
- # {https://rubygems.org/gems/range_extd}
47
- #
48
- class Rangeary < Array
49
- undef_method :*, :+, :length, :reverse
50
-
51
- # Hash with the keys of :negative and :positive
52
- attr_reader :infinities
53
-
54
- #
55
- # Arbitrary (positive) number of arguments can be given.
56
- # (r1, [r2, ...])
57
- #
58
- # It is possible to supply the user-defined infinity objects for both or either
59
- # positive and negative infinity. If one (or more) of the arguments is
60
- # a {Rangeary} object, their infinity values are inherited, unless explicitly
61
- # specified in the optional arguments.
62
- #
63
- # @param inarall [Object] An arbitrary number of either {Rangeary}, {RangeExtd} or {Range} objects (or its subclasses).
64
- # @option **opts [Object] :positive Object for positive infinity. In default {Float::INFINITY} for Numeric Real or else {RangeExtd::Infinity::POSITIVE}.
65
- # @option **opts [Object] :negative Object for negative infinity. In default -{Float::INFINITY} for Numeric Real or else {RangeExtd::Infinity::NEGATIVE}.
66
- def initialize(*inarall, **opts)
67
-
68
- if inarall.size < 1
69
- raise ArgumentError, "wrong number of arguments (#{inarall.size} for 1 or more)."
70
- end
71
-
72
- hsInheritedObj = {:negative =>nil, :positive =>nil}
73
- hsInheritedAry = {:negative => [], :positive => []}
74
- hsInheritedClass = {:negative => [], :positive => []}
75
- inarall = inarall.map{|i|
76
- if defined?(i.first_element) && defined?(i.infinities)
77
- begin
78
- [:negative, :positive].each do |nega_posi|
79
- hsInheritedAry[nega_posi].push( i.infinities[nega_posi])
80
- hsInheritedClass[nega_posi].push(i.infinities[nega_posi].class)
81
- end
82
- rescue
83
- warn "warning: Rangeary#infinities looks wrong in the input (#{i})."
84
- end
85
- i.to_a
86
- else
87
- i
88
- end
89
- }.flatten.map{|j|
90
- if (defined? j.exclude_begin?)
91
- j
92
- else
93
- begin
94
- RangeExtd.new(j)
95
- rescue ArgumentError # Just to change the error message.
96
- raise ArgumentError, "invalid parameter for RangeExtd, hence for Rangeary (#{j.inspect})."
97
- end
98
- end
99
- }
100
-
101
- # _merge_overlaps
102
- begin
103
- arRange = _merge_overlaps( convert2range(inarall) )
104
- rescue => err
105
- # Trap just to change the type of the exception.
106
- raise ArgumentError, err.message, err.backtrace
107
- end
108
-
109
- if arRange.empty?
110
- raise ArgumentError, 'no significant argument given for Rangeary.'
111
- end
112
-
113
- ## Setting @infinities[:negative, :positive]
114
-
115
- # Check inherited objects if there is any, namely if the argument includes any RangeAry object.
116
- # Priority: Float > Others > RangeExtd::Infinity
117
- if hsInheritedAry[:negative].size > 0
118
- [:negative, :positive].each do |es|
119
- iFloat = hsInheritedClass[es].find_index(Float)
120
- if iFloat.nil?
121
- iElse = hsInheritedClass[es].find_index{|i| (i != RangeExtd::Infinity) && (i != Float)}
122
- if iElse.nil?
123
- iRangeInf = hsInheritedClass[es].find_index(RangeExtd::Infinity)
124
- if iRangeInf.nil?
125
- raise "Rangeary#infinities is not set in the input." # Should not happen, as Rangeary#infinities must be set always.
126
- else
127
- hsInheritedObj[es] = hsInheritedAry[es][iRangeInf]
128
- end
129
- else
130
- hsInheritedObj[es] = hsInheritedAry[es][iElse]
131
- end
132
- else
133
- hsInheritedObj[es] = hsInheritedAry[es][iFloat]
134
- end # if iFloat.nil?
135
- end # [:negative, :positive].each do |es|
136
- end # if hsInheritedAry.size > 0
137
-
138
- hsFlag = { :found => {:negative => false, :positive => false} }
139
- hsCand = { :negative => arRange[0].begin, :positive => arRange[-1].end }
140
- infDef = { :negative => RangeExtd::Infinity::NEGATIVE, :positive => RangeExtd::Infinity::POSITIVE }
141
- @infinities={ :negative => nil, :positive => nil }
142
- [:negative, :positive].each do |es|
143
- if (infDef[es] == hsCand[es]) # Can be Float or whatever.
144
- @infinities[es] = hsCand[es] # highest priority
145
- hsFlag[:found][:negative] = true
146
- else
147
- strtmp = ""
148
- [opts[es], hsInheritedObj[es]].each do |opts_or_inherited|
149
- @infinities[es] ||= opts_or_inherited # uses ots[:****tive] or hsInheritedObj[:****tive] if not set.
150
- # Now, checking the compatibility of the infinity value specified (or inherited) with the given range.
151
- if (! opts_or_inherited.nil?) && (opts_or_inherited == @infinities[es]) && (! arRange[0].is_none?)
152
- begin
153
- _ = 0 * (opts_or_inherited <=> hsCand[es])
154
- rescue TypeError
155
- raise ArgumentError, "invalid #{strtmp}parameter for :#{es} => (#{opts_or_inherited.inspect}), incompatible with the range with begin=(#{hsCand[es].inspect})."
156
- end
157
- end
158
- strtmp = "inherited "
159
- end # [opts[es], hsInheritedObj[es]].each do |opts_or_inherited|
160
- end # if (infDef[es] == hsCand[es]) # else
161
- end # [:negative, :positive].each do |es|
162
-
163
- if ! (@infinities[:negative] && @infinities[:positive])
164
- # Either or both @infinities[:negative, :positive] is not set, yet.
165
- # Need to set it now. The possibilities are,
166
- # (1) arRange[0].null? && no opts/inheritance given.
167
- # (2) one of them is given by either arRange or opts or inheritance, but not the other.
168
- # (3) neither of them is given by arRange nor opts nor inheritance.
169
- if arRange[0].null?
170
- [:negative, :positive].each do |es|
171
- @infinities[es] ||= infDef[es]
172
- end
173
- else
174
- # There must be a non-infinity object - we will find it out.
175
- if hsFlag[:found][:negative]
176
- obj2refer = arRange[0].end
177
- else
178
- obj2refer = arRange[-1].begin
179
- end
180
-
181
- # Now, if Numeric === obj2refer, Float::INFINITY is the default.
182
- begin
183
- _dummy = (1.0 < obj2refer)
184
- rescue ArgumentError
185
- # Not Numeric, hence the current infDef is used as it is.
186
- else
187
- # Numeric
188
- infDef = { :negative => -Float::INFINITY, :positive => Float::INFINITY }
189
- end
190
- [:negative, :positive].each do |es|
191
- @infinities[es] ||= infDef[es] # uses default infinity if not set.
192
- end
193
- end # if arRange[0].null?
194
- end # if ! (@infinities[:negative] && @infinities[:positive])
195
-
196
- super(arRange)
197
- self.freeze
198
- end # def initialize(*inarall, **opts)
199
-
200
- alias_method :triple_equals_orig, :=== if ! self.method_defined?(:triple_equals_orig)
201
-
202
- # If self covers the entire range?
203
- #
204
- def all?
205
- rfirst = self[0]
206
- ((1 == size) &&
207
- (@infinities[:negative] == rfirst.begin) &&
208
- (@infinities[:positive] == rfirst.end) &&
209
- (!rfirst.exclude_begin?) &&
210
- (!rfirst.exclude_end?))
211
- end
212
-
213
- # True if the inObj is in the range.
214
- #
215
- # This method works on the basis of each element, that is,
216
- # if for any of the {RangeExtd} in the {Rangeary}, {RangeExtd#===}
217
- # returns true, this will return true. That means, if the argument is
218
- # a {Rangeary} (or {Range}) object, this always returns false.
219
- # Note {#include?} and {#member?} work the same as in the standard {Array},
220
- # whereas {#include_element?} and {#member_element?} are the alias of
221
- # this method.
222
- #
223
- # See {#cover?}. The difference between this method and {#cover?} is
224
- # the same as that in {Range}.
225
- # @return [Boolean]
226
- def ===(inObj)
227
- to_a.each do |ea|
228
- if ea === inObj
229
- return true
230
- end
231
- end
232
- return false
233
- end
234
-
235
- alias_method :include_element?, :===
236
- alias_method :member_element?, :===
237
-
238
-
239
- # @return [Object] The {RangeExtd#begin} of the first {RangeExtd}.
240
- def begin()
241
- if to_a.size > 0
242
- to_a[0].begin
243
- else
244
- nil # Should not happen!
245
- end
246
- end
247
- alias_method :begin_element, :begin
248
-
249
-
250
- # If inObj is within the ranges, it will return true.
251
- #
252
- # See {#===}.
253
- # The difference between this method and {#===} is
254
- # the same as that in {Range}.
255
- def cover?(inObj)
256
- to_a.each do |ea|
257
- if ea.cover? inObj
258
- return true
259
- elsif (ea.end <=> inObj) == 1
260
- return false # No point of carrying on searching.
261
- end
262
- end
263
- return false
264
- end # def cover?(inObj)
265
-
266
-
267
- # Iterator for each element in the ranges.
268
- # @return [self]
269
- def each_element
270
- each do |er|
271
- er.each do |ee|
272
- yield ee
273
- end
274
- end
275
- self
276
- end
277
-
278
-
279
- # If the range defined in this object is empty (as in {Range#empty?}), returns true.
280
- #
281
- def empty_element?
282
- each do |er|
283
- if ! er.empty?
284
- return false
285
- end
286
- end
287
- return true
288
- end
289
-
290
-
291
- # @return [Object] The {RangeExtd#end} of the last {RangeExtd}.
292
- def end()
293
- if to_a.size > 0
294
- to_a[-1].end
295
- else
296
- nil
297
- end
298
- end
299
- alias_method :end_element, :end
300
-
301
-
302
- # {Range#equiv?} method, defined in range_extd library, extended to this {Rangeary}.
303
- #
304
- # @example
305
- # Rangeary(RangeExtd(1,"<...",4), 5...8).equiv?(Rangeary(2..3, 5..7)) # => true
306
- #
307
- # @param other [Rangeary]
308
- def equiv?(other)
309
- return false if size() != other.size
310
-
311
- self.zip(other).each do |ear|
312
- if ! ear[0].equiv?(ear[1])
313
- return false
314
- end
315
- end
316
-
317
- true
318
- end # def equiv?(other)
319
-
320
-
321
- # Returns the first n elements of the entire range, the same as {Range#first}.
322
- #
323
- # If the argument is not given, this simply calls {Range#begin} for the first
324
- # {RangeExtd}.
325
- #
326
- # If not, and if the elements in the ranges are not discrete, like Float,
327
- # an exception is raised (see {Range#first}).
328
- # Note this works on the element basis, being different from {Rangeary#first},
329
- # which works on the array basis.
330
- #
331
- # @param n [Integer] (Optional) positive.
332
- # @return [Object] equivalent to {#begin} if no argument is given.
333
- # @return [Array] Array of the first n elements in the range.
334
- # @raise [TypeError] if the ranges has no iteration defined, such as, starting from Float.
335
- def first_element(n=nil)
336
- if n.nil?
337
- self.begin()
338
- elsif n < 0
339
- raise ArgumentError, "the argument #{n} has to be positive."
340
- else
341
- arRet = []
342
- m = n
343
- to_a.each do |eachr|
344
- ar = eachr.first(m)
345
- arRet += ar
346
- if arRet.size >= n
347
- break
348
- else
349
- m -= ar.size
350
- end
351
- end
352
-
353
- arRet
354
- end # if n.nil?
355
- end # def first_element(n=nil)
356
-
357
-
358
- # Return an array of objects that consist of the ranges.
359
- #
360
- # @return [Array]
361
- # @raise [TypeError] if any of the ranges has no iteration defined, such as, starting from Float.
362
- # @example
363
- # Rangeary(2...4, 5..6, 8..9).to_a # => [2...4, 5..6, 8..9]
364
- # Rangeary(2...4, 5..6, 8..9).flatten # => [2, 3, 5, 6, 8, 9]
365
- # Rangeary(2...4, 5..6, 8..9).flatten.reduce(:+) # => 33
366
- def flatten
367
- to_a.reduce([]){|a,b| a+=b.to_a}
368
- end # def flatten
369
-
370
-
371
- # @return [String]
372
- def inspect
373
- "<Rangeary:#{to_a.inspect}>"
374
- end
375
-
376
-
377
- # Can iterate?
378
- def iteratable?
379
- begin
380
- each do |i|
381
- i.each{break}
382
- end
383
- rescue TypeError
384
- return false
385
- end
386
- true
387
- end # def iteratable?
388
-
389
-
390
- # Returns the last n elements of the entire range, the same as {Range#last}.
391
- #
392
- # If the argument is not given, this simply calls {Range#end} for the last
393
- # {RangeExtd}.
394
- #
395
- # If not, and if the elements in the ranges are not discrete, like Float,
396
- # an exception is raised (see {Range#last}).
397
- # Note this works on the element basis, being different from {Rangeary#last},
398
- # which works on the array basis.
399
- #
400
- # @param n [Integer] (Optional) positive.
401
- # @return [Object] equivalent to {#end} if no argument is given.
402
- # @return [Array] Array of the last n elements in the range.
403
- # @raise [TypeError] if any of the ranges has no iteration defined, such as, starting from Float.
404
- def last_element(n=nil)
405
- if n.nil?
406
- self.end()
407
- elsif n < 0
408
- raise ArgumentError, "the argument #{n} has to be positive."
409
- else
410
- arRet = []
411
- m = n
412
- to_a.reverse.each do |eachr|
413
- ar = eachr.last(m)
414
- arRet = ar + arRet
415
- if arRet.size >= n
416
- break
417
- else
418
- m -= ar.size
419
- end
420
- end
421
-
422
- arRet
423
- end # if n.nil?
424
- end # def last_element(n=nil)
425
-
426
-
427
- # Practically equivalent to {#empty_element?}.
428
- def null_element?
429
- each do |er|
430
- if ! er.null?
431
- return false
432
- end
433
- end
434
- return true
435
- end
436
- alias_method :null?, :null_element?
437
-
438
-
439
- # Return the sum of {RangeExtd#size} of all the ranges in the object.
440
- #
441
- # @return [Integer] 0 if {RangeExtd::NONE}
442
- # @return [Float] Float::INFINITY if one of ranges is open-ended.
443
- # @return [nil] if any of the range is non-Numeric and not open-ended to the infinity.
444
- def size_element()
445
- begin
446
- to_a.map(&:size).reduce(:+)
447
- rescue TypeError
448
- nil
449
- end
450
- end
451
-
452
-
453
- # ======================= Operators =======================
454
-
455
- # Disjunction of a Rangeary (or RangeExtd or Range) and another
456
- #
457
- # @param r1 [Rangeary, RangeExtd, Range]
458
- # @param r2 [Rangeary, RangeExtd, Range]
459
- # @return [Rangeary]
460
- def self.disjunction(r1, r2)
461
- self.new(r1, r2)
462
- end
463
-
464
- # Add (Disjunction) a Rangeary (or RangeExtd or Range)
465
- #
466
- # @param inr [Rangeary, RangeExtd, Range]
467
- # @return [Rangeary]
468
- def disjunction(inr)
469
- self.class.new(self, inr)
470
- end
471
-
472
- alias_method :+, :disjunction
473
- alias_method :|, :disjunction # "|" (plus with Object#eql?) in general, but in this case it is identical.
474
-
475
- # Exclusive Disjunction (XOR) with a Rangeary (or RangeExtd or Range)
476
- #
477
- # @param r1 [Rangeary, RangeExtd, Range]
478
- # @param r2 [Rangeary, RangeExtd, Range]
479
- # @return [Rangeary]
480
- def self.exclusive_disjunction(r1, r2)
481
- Rangeary.new(r1).exclusive_disjunction(r2)
482
- end
483
-
484
- # Exclusive Disjunction (XOR) with a Rangeary (or RangeExtd or Range)
485
- #
486
- # @param inr [Rangeary, RangeExtd, Range]
487
- # @return [Rangeary]
488
- def exclusive_disjunction(inr)
489
- (disjunction(inr)).conjunction(conjunction(inr).negation)
490
- end
491
-
492
- alias_method :^, :exclusive_disjunction
493
- alias_method :xor, :exclusive_disjunction
494
-
495
- # Subtraction.
496
- #
497
- # @param r [Rangeary, RangeExtd, Range]
498
- # @return [Rangeary]
499
- def subtraction(r)
500
- conjunction( Rangeary.new(r).negation )
501
- end
502
-
503
- alias_method :-, :subtraction
504
-
505
-
506
- # Conjunction.
507
- #
508
- # @param r [Rangeary, RangeExtd, Range]
509
- # @return [Rangeary]
510
- def conjunction(r)
511
- self.class.conjunction(self, r)
512
- end
513
-
514
- alias_method :&, :conjunction
515
- alias_method :*, :conjunction
516
-
517
-
518
- # Negation.
519
- #
520
- # @param r [Rangeary, RangeExtd, Range]
521
- # @return [Rangeary]
522
- def self.negation(r)
523
- self.new(r).negation
524
- end
525
-
526
-
527
- # Negation.
528
- #
529
- # @return [Rangeary]
530
- def negation()
531
- if to_a.empty?
532
- raise "ERROR: No range is defined." # This should not happen.
533
- end
534
-
535
- arran = []
536
- prevend = nil
537
- prevst = nil
538
- to_a.each do |eachr|
539
-
540
- # ALL -> NONE
541
- return Rangeary.new(RangeExtd::NONE, :positive => @infinities[:positive], :negative => @infinities[:negative]) if RangeExtd::ALL == eachr
542
-
543
- # null(NONE) -> ALL
544
- if eachr.null?
545
- begin
546
- _beg = 1.0 * eachr.begin
547
- return (-Float::INFINITY..Float::INFINITY)
548
- rescue TypeError # XXXX can't be coerced into Float
549
- return Rangeary.new(@infinities[:negative]..@infinities[:positive])
550
- end
551
- end
552
-
553
- ea_b = eachr.begin
554
- if (RangeExtd::Infinity::NEGATIVE == ea_b) # Including -Float::INFINITY and other general negative infinities (nb., [#==] is not commutative!).
555
- # The existing first range starts from the negative infinity, so skip this one.
556
- else
557
- if prevend.nil?
558
- # The returned first range starts from the negative infinity.
559
- arran.push( RangeExtd(@infinities[:negative]..eachr.begin, :exclude_end => (! eachr.exclude_begin?)) )
560
- else
561
- arran.push( RangeExtd(prevend, eachr.begin, :exclude_begin => (! prevst), :exclude_end => (! eachr.exclude_begin?)) )
562
- end
563
- end # if (eachr.begin == -Float::INFINITY) || (RangeExtd::NONE == eachr.begin)
564
- prevend = eachr.end
565
- prevst = eachr.exclude_end?
566
- end # to_a.each do |eachr|
567
-
568
- if (RangeExtd::Infinity::POSITIVE == prevend) # Including Float::INFINITY and other general positive infinities (nb., [#==] is not commutative!).
569
- ## Do nothing
570
- else
571
- arran.push( RangeExtd.new(prevend..@infinities[:positive], :exclude_begin => (! prevst)) )
572
- end
573
-
574
- Rangeary.new(arran, :positive => @infinities[:positive], :negative => @infinities[:negative])
575
- end # def negation()
576
-
577
- alias_method :~@, :negation
578
-
579
-
580
- ####################
581
- # Public class methods
582
- ####################
583
-
584
- # Conjunction.
585
- #
586
- # @param r1 [Rangeary, RangeExtd, Range]
587
- # @param r2 [Rangeary, RangeExtd, Range]
588
- # @return [Rangeary]
589
- def self.conjunction(r1, r2)
590
-
591
- r1 = Rangeary.new(r1) if ! defined? r1.first_element
592
- r2 = Rangeary.new(r2) if ! defined? r2.first_element
593
- return Rangeary.new(RangeExtd::NONE) if r1.null? || r2.null?
594
- return Rangeary.new(r1) if r2.all?
595
- return Rangeary.new(r2) if r1.all?
596
-
597
- # Getting inherited options (if Rangeary is given) for the later use.
598
- hsInherited = {}
599
- [:negative, :positive].each do |ei|
600
- hsInherited[ei] = [r1.infinities[ei], r2.infinities[ei]].map{|j|
601
- (RangeExtd::Infinity === j) ? nil : j
602
- }.compact.sort{|a,b|
603
- if a.class == Float
604
- -1
605
- elsif b.class == Float
606
- 1
607
- else
608
- 0
609
- end
610
- }[0]
611
- end
612
-
613
- # Initialisation
614
- a1 = r1.to_a
615
- a2 = r2.to_a
616
- rc = Rangeary.new( RangeExtd::NONE, hsInherited )
617
-
618
- if a1.empty? || a2.empty?
619
- return rc
620
- end
621
-
622
- ### Algorithm
623
- # Conditions: Both a1 and a2 are sorted in order of #begin().
624
- # a1 is a reference array of RangeExtd
625
- # Then, Sigma(i=0..(a2.size-1)) a2[i]*a1[j=0..-1] => Returned Rangeary
626
- #
627
- # In reality, I put some tricks to avoid unnecessary calculation
628
- # for the sake of the processing speed. But essentially
629
- # it is a very simple algorithm.
630
- #
631
- last_a1index = 0
632
- a2.each do |ea2|
633
- a1.each_with_index do |ea1, ind|
634
- # A bit of trick just to avoid unnecessary process
635
- next if ind < last_a1index # skip
636
- break if ea2.end < ea1.begin # Completely out of range
637
- if ea1.end < ea2.begin # Completely out of range
638
- last_a1index = ind if last_a1index < ind
639
- next
640
- end
641
-
642
- # Core - Perform conjunction.
643
- pq1 = conjunctionRangeExtd(ea1, ea2) # => Rangeary.conjunctionRangeExtd()
644
- if ! pq1.empty?
645
- rc += Rangeary.new(pq1)
646
- last_a1index = ind
647
- end
648
- end # a1.each_with_index do |ea1, ind|
649
- end # a2.each do |ea2|
650
-
651
- rc
652
- end # def self.conjunction(r1, r2)
653
-
654
-
655
- # Returns the array sorted, based on (1) the begin objects and their boundary state,
656
- # (2) then the end objects and their boundary state.
657
- # {RangeExtd::NONE} comes first, if included in the input array.
658
- #
659
- # @param ar [<Range, RangeExtd>] Arbitrary number.
660
- # @return [Array<Range, RangeExtd>]
661
- def self.sort_ranges(*ar)
662
- ar.flatten.sort{ |a,b|
663
- err_msg_ab = "invalid parameter (#{a.inspect} or #{b.inspect})."
664
- ret = a.begin <=> b.begin
665
- case ret
666
- when -1, 1
667
- ret
668
- when 0
669
- a_exc_begin = (a.exclude_begin? rescue false)
670
- b_exc_begin = (b.exclude_begin? rescue false)
671
- if (a_exc_begin ^ b_exc_begin)
672
- if a_exc_begin # but not b
673
- 1
674
- else
675
- -1
676
- end
677
- else # <= (a.exclude_begin? == b.exclude_begin?)
678
- ret = a.end <=> b.end
679
- case ret
680
- when -1, 1
681
- ret
682
- when 0
683
- if (a.exclude_end? && b.exclude_end?)
684
- 0
685
- elsif a.exclude_end? # but not b
686
- -1
687
- else # <= b.exclude_end? but not a
688
- 1
689
- end # if (a.exclude_end? && b.exclude_end?)
690
- when nil
691
- # This should not happen for Range, let alone RangeExtd.
692
- # But could be the case for a user-defined class.
693
- if a.end.nil? && b.end.nil?
694
- 0
695
- elsif a.end.nil?
696
- -1
697
- elsif b.end.nil?
698
- 1
699
- else
700
- raise(TypeError, err_msg_ab)
701
- end
702
- else # case ret # a.end <=> b.end
703
- raise(TypeError, err_msg_ab)
704
- end # case ret # a.end <=> b.end
705
- end # if (a.exclude_begin? ^ b.exclude_begin?)
706
- when nil # case ret #(a.begin <=> b.begin)
707
- if a.begin.nil? && b.begin.nil?
708
- 0
709
- elsif a.begin.nil?
710
- -1
711
- elsif b.begin.nil?
712
- 1
713
- else
714
- raise(TypeError, err_msg_ab)
715
- end
716
- else # case ret #(a.begin <=> b.begin)
717
- raise(TypeError, err_msg_ab)
718
- end # case ret #(a.begin <=> b.begin)
719
- } # ar.sort{ |a,b|
720
- end # def self.sort_ranges(ar)
721
-
722
-
723
-
724
- ####################
725
- private
726
- ####################
727
-
728
- # Called from {Rangeary#initialize}
729
- def convert2range(inarall)
730
- inarall.flatten.map{|i|
731
- if defined? i.first_element
732
- i.to_a
733
- else
734
- i
735
- end
736
- }.flatten.map{|j|
737
- if (defined? j.exclude_begin?)
738
- j
739
- else
740
- RangeExtd(j)
741
- end
742
- }
743
- end
744
-
745
-
746
- # Called from {Rangeary#initialize}.
747
- # Process the array of RangeExtd and return the new one, in which
748
- # overlapped ranges are merged accordingly.
749
- #
750
- # If there is no non-"empty" range, one of them will be left.
751
- # As a priority, an empty range with a definite class is left,
752
- # but if there is none, RangeExtd::NONE will be left.
753
- #
754
- # @param inAr [Array<RangeExtd,Range>]
755
- # @return [Array]
756
- def _merge_overlaps(inAr)
757
- #def self.compact(inAr)
758
-
759
- ### Cases
760
- #[st means status.]
761
- #(0) (prev[0]) and (prev[0].status) unchanged.
762
- #(1) if (now[-1]< prev[-1]), do nothing. [Totally inclusive]
763
- # I---* => I---*
764
- # *--*
765
- #(2) if (now[-1]== prev[-1])
766
- # (2-1) AND if (now[-1].st? || prev[-1].st?), prev[-1].st=T [Nearly inclusive]
767
- # I--O => I--I
768
- # I--I
769
- # (2-2) ELSE do nothing. [Totally inclusive]
770
- #(3) ELSE [namely, if (now[-1] > prev[-1])]
771
- # (3-1) if (now[0] > prev[-1]), append. [Totally exclusive]
772
- # *--* => *--* *--*
773
- # *--*
774
- # (3-2) if (now[0] == prev[-1])
775
- # (3-2-1) (!now[0].st? && !prev[-1].st?), append. [Totally exclusive]
776
- # *--O => *--O--*
777
- # O--*
778
- # (3-2-2) ELSE [namely, now[1].st? || prev[-1].st?], connect. (prev[0],now[-1])
779
- # *--O => *-----*
780
- # I--*
781
- # *--I => *-----*
782
- # O--*
783
- # *--I => *-----*
784
- # I--*
785
- # (3-3) ELSE [namely, if (now[0] < prev[-1])], connect. (prev[0],now[-1])
786
- # *--* => *---*
787
- # *--*
788
- #
789
-
790
- # inRanges = sort_arrange( inAr )
791
- inRanges = self.class.sort_ranges( inAr ).map{|i| (RangeExtd === i) ? i : RangeExtd(i) } # => Rangeary.sort_ranges(ar)
792
-
793
- if inRanges.size < 1
794
- return inRanges
795
- end
796
-
797
- newRanges = [inRanges[0]]
798
-
799
- inRanges[1..-1].each do |eachr|
800
- prev = newRanges[-1]
801
- case eachr.end <=> prev.end
802
- when -1 # aka, eachr.end < prev.end
803
- # Do nothing [Totally inclusive]
804
- when 0
805
- if (!eachr.exclude_end?) && prev.exclude_end?
806
- # Change the status (:exclude_end => false) for prev
807
- newRanges[-1] = RangeExtd.new(prev.begin, prev.end, :exclude_begin => prev.exclude_begin?, :exclude_end => false)
808
- else
809
- # Do nothing [Totally inclusive]
810
- end
811
- when 1 # aka, eachr.end > prev.end
812
- case eachr.begin <=> prev.end
813
- when -1 # Connect by combining
814
- newRanges[-1] = RangeExtd.new(prev.begin, eachr.end, :exclude_begin => prev.exclude_begin?, :exclude_end => eachr.exclude_end?)
815
- when 0
816
- if (eachr.exclude_begin?) && (prev.exclude_end?)
817
- newRanges.push(eachr) # [Totally exclude]
818
- else # Connect by combining
819
- newRanges[-1] = RangeExtd.new(prev.begin, eachr.end, :exclude_begin => prev.exclude_begin?, :exclude_end => eachr.exclude_end?)
820
- end
821
- when 1
822
- newRanges.push(eachr) # [Totally exclude]
823
- when nil
824
- newRanges.push(eachr) # must be RangeExtd::NONE (or user-defined equivalent)
825
- else
826
- raise
827
- end # case eachr.begin <=> prev.end
828
- when nil # aka, eachr.end > prev.end
829
- newRanges.push(eachr) # must be RangeExtd::NONE (or user-defined equivalent)
830
- else
831
- raise
832
- end # case eachr.end <=> prev.end
833
-
834
- end # inRanges[1..-1].each do |eachr|
835
-
836
-
837
- ## Sort out empty Ranges in the array.
838
- # If there is at least one non-empty range, delete all empty ranges.
839
- # If not, leave one of them, preferably not RangeExtd::NONE,
840
- # unless there is no choice.
841
- hsFlag = {
842
- :empty? => true,
843
- :klass => nil,
844
- :found? => false,
845
- }
846
-
847
- # Search for non-empty range.
848
- newRanges.each do |er|
849
- if er.empty?
850
- if hsFlag[:klass].nil?
851
- obj = er.begin()
852
- if obj.nil?
853
- ## Do nothing
854
- else
855
- hsFlag[:klass] = obj.class
856
- end
857
- end
858
- else
859
- hsFlag[:empty?] = false
860
- break
861
- end
862
- end
863
-
864
- hsFlag[:found?] = false # Redundant, but for the sake of readability
865
- if hsFlag[:empty?]
866
- # It is all empty, hence delete all but one.
867
- hsFlag[:klass] = NilClass if hsFlag[:klass].nil?
868
- newRanges.delete_if do |er|
869
- if hsFlag[:found?]
870
- true
871
- elsif er.begin().class == hsFlag[:klass]
872
- hsFlag[:found?] = true
873
- false
874
- else
875
- true
876
- end
877
- end
878
- else
879
- # Deletes all the empty ones.
880
- newRanges.delete_if { |er| er.empty? }
881
- end
882
-
883
- newRanges
884
- end # def _merge_overlaps(inAr)
885
-
886
- private :_merge_overlaps
887
-
888
-
889
- ####################
890
- # private_class_method
891
- ####################
892
-
893
- #== Logical conjunction of two RangeExtd
894
- #
895
- # To assure this logical conjunction meaningful,
896
- # the objects that consist of RangeExtd objects have to be
897
- # monotonic (increase), namely, for any potential element,
898
- # x_n and x_m, within the given range,
899
- # (x_n <=> x_m) == 1 if (n > m),
900
- # have to be true. In other words, the derivative must be always
901
- # non-negative.
902
- #
903
- # For example, (?a..?d) and (?x..?z) satisfies this condition.
904
- # However, ('d'..'gg') does not, as follows.
905
- # rd = RangeExtd('d'..'gg')
906
- # rf = RangeExtd('f'..'h')
907
- # Rangeary.conjunctionRangeExtd(rd, rf) # => ('f'..'gg')
908
- #
909
- # @note If you give a built-in Range object(s) for the arguments,
910
- # make sure they are valid, that is, Range#valid? returns true.
911
- #
912
- #=== Algorithm
913
- #
914
- #[st means status. - true if excl; Estimate (Init(in|ex),Fini(in|ex))]
915
- #[b4 <= af] (sort!)
916
- #
917
- #(1) Init = af[0], Init.st=af[0].st
918
- # If (b4[0]==af[0]), then Init.st = ( b4[0].ex || af[0].ex)
919
- #(2) Fini = [b4[-1], af[-1]].min, which belongs to (b4|af)
920
- # If (b4[-1]==af[-1]), then Fini.st = (b4[-1].ex || af[-1].ex), otherwise nil for now.
921
- #(3) if (Init > Fini) => none.
922
- # *---* => ....
923
- # *--* ....
924
- #(4) if (Init == FiniMax), then Fini.st=b4[-1].st
925
- # (4-1) if (Init.in&&Fini.in), => Single-Number-Range(InitCand(in))
926
- # *--I => ...I
927
- # I--* I...
928
- # (4-2) else, => none
929
- #(5) if (Init < FiniMax)
930
- # (5-1) if Fini belongs to b4, Fini.st=b4[-1].st
931
- # *---* => .*--*
932
- # *---*
933
- # (5-2) if Fini belongs to af, Fini.st=af[-1].st
934
- # *---* => .*-*
935
- # *-*
936
- # (5-3) if Fini belongs to both, Fini.st is defined already.
937
- # *---* => .*--*
938
- # *--*
939
- #
940
- # @param r1 [RangeExtd] Can be Range
941
- # @param r2 [RangeExtd] Can be Range
942
- # @return [RangeExtd]
943
- #
944
- def self.conjunctionRangeExtd(r1, r2)
945
-
946
- [r1, r2].each do |er|
947
- return er if er.is_none?
948
- end
949
-
950
- r = *( sort_ranges([RangeExtd(r1), RangeExtd(r2)]) ) # => Rangeary.sort_ranges
951
- # r = *( sort_arrange([RangeExtd(r1), RangeExtd(r2)]) )
952
-
953
- ## Note: the end product will be (cBeg(:stBeg), cEnd(:stEnd))
954
- # where :stBeg and :stEnd mean exclude_(begin|end)?
955
-
956
- # Set the candidate begin value.
957
- cBeg = r[1].begin
958
- if r[0].begin == r[1].begin
959
- stBeg = (r[1].exclude_begin? || r[0].exclude_begin?)
960
- else
961
- stBeg = r[1].exclude_begin?
962
- end
963
-
964
- # Set the candidate end value.
965
- if r[0].end == r[1].end
966
- cEnd = r[1].end
967
- stEnd = (r[0].exclude_end? || r[1].exclude_end?)
968
- else
969
- a = [[r[0].end, 0], [r[1].end, 1]].min
970
- cEnd = a[0]
971
- cEndIndex = a[1] # r[cEndIndex] == RangeExtd obj that gives the end of the resultant range.
972
- stEnd = nil
973
- end
974
-
975
- case cBeg <=> cEnd
976
- when 1 # cBeg > cEnd
977
- RangeExtd::NONE
978
-
979
- when 0 # cBeg == cEnd
980
- stEnd = r[0].exclude_end?
981
- if (!stBeg) && (!stEnd)
982
- RangeExtd(cBeg..cBeg) # Point range
983
- else
984
- RangeExtd::NONE
985
- end
986
-
987
- when -1 # cBeg < cEnd
988
- # Now, the range must be (cBeg, cEnd). May need adjustment of the exclude status.
989
- if stEnd.nil?
990
- stEnd = r[cEndIndex].exclude_end?
991
- # else
992
- # # Already defined.
993
- end # if stEnd.nil?
994
-
995
- RangeExtd(cBeg, cEnd, :exclude_begin => stBeg, :exclude_end => stEnd)
996
- else
997
- raise
998
- end # case cBeg <=> cEnd
999
-
1000
- end # def self.conjunctionRangeExtd(r1, r2)
1001
-
1002
- private_class_method :conjunctionRangeExtd
1003
-
1004
-
1005
- # Logical conjunction of two Rangeary
1006
- #
1007
- #===Algorithm
1008
- #
1009
- # r1 == [p1, p2, p3, ...] # Array of RangeExtd
1010
- # r2 == [q1, q2, q3, ...]
1011
- # rc = [] # Initial value of Conjunction r1*r2
1012
- # 1. q1 = r2.shift
1013
- # 2. r1.delete_if(pi.end<q1.begin) -> (r1==[P1,P2(>q1),P3,...])
1014
- # 3. make conjunction between RangeExtd P1*q1 => pq1
1015
- # 4. rc+=Rangeary.new(pq1)
1016
- # 5a. if q1.end<P1.end, will do P1<->q2 next, hence return to 1 (will do r2.shift).
1017
- # *--* => *--* + nil
1018
- # *--* *--* .... *--*
1019
- # *---* => .O--* + -*
1020
- # *---* *--* ..... *--* ...-* ----
1021
- # 5b. if q1.end==P1.end, r1.shift, return to 1 (will do r2.shift).
1022
- # *--* *-- => .... *-- + ---* ---
1023
- # *---* *--* ..... *--* .---* ----
1024
- # 5c. if q1.end>P1.end, will do P2<->q1, hence r1.shift, return to 2.
1025
- # *---* *-- => ..... *--
1026
- # *---- .O---
1027
- #
1028
- # @return [Rangeary]
1029
- def self.conjunction_orig(r1, r2)
1030
-
1031
- r1 = Rangeary.new(r1) if ! defined? r1.first_element
1032
- r2 = Rangeary.new(r2) if ! defined? r2.first_element
1033
-
1034
- # Getting inherited options (if Rangeary is given) for the later use.
1035
- hsInherited = {}
1036
- [:negative, :positive].each do |ei|
1037
- hsInherited[ei] = [r1.infinities[ei], r2.infinities[ei]].map{|j|
1038
- (RangeExtd::Infinity === j) ? nil : j
1039
- }.compact.sort{|a,b|
1040
- if a.class == Float
1041
- -1
1042
- elsif b.class == Float
1043
- 1
1044
- else
1045
- 0
1046
- end
1047
- }[0]
1048
- end
1049
-
1050
- # Initialisation
1051
- a1 = r1.to_a
1052
- a2 = r2.to_a
1053
- rc = Rangeary.new( RangeExtd::NONE, hsInherited )
1054
-
1055
- if a1.empty? || a2.empty?
1056
- return rc
1057
- end
1058
-
1059
- # Main loop
1060
- hsFlag = {:a2shift => true}
1061
- while true
1062
- if hsFlag[:a2shift]
1063
- if a2.size > 0
1064
- q1 = a2.shift
1065
- else
1066
- break
1067
- end
1068
- end
1069
- index_valid = a1.find_index{|i| i.begin >= q1.begin }
1070
- if (! index_valid.nil?) && index_valid > 1
1071
- a1.shift(index_valid)
1072
- end
1073
-
1074
- if a1.size < 1
1075
- break
1076
- end
1077
- p1 = a1[0]
1078
-
1079
- # Core - Perform conjunction.
1080
- pq1 = Rangeary.conjunctionRangeExtd(p1, q1)
1081
- rc += Rangeary.new(pq1)
1082
-
1083
- case q1.end <=> p1.end
1084
- when -1 # q1.end < p1.end
1085
- hsFlag[:a2shift] = true
1086
- when 0
1087
- if a1.size > 0
1088
- a1.shift
1089
- else
1090
- break
1091
- end
1092
- hsFlag[:a2shift] = true
1093
- when 1 # q1.end > p1.end
1094
- if a1.size > 0
1095
- a1.shift
1096
- else
1097
- break
1098
- end
1099
- hsFlag[:a2shift] = false
1100
- else
1101
- raise
1102
- end # case q1.end <=> p1.end
1103
- end # while (a1.size > 0) || (a2.size > 0)
1104
-
1105
- rc
1106
- end
1107
- private_class_method :conjunction_orig
1108
-
1109
-
1110
- # Subtract a Range from an Array of Range objects.
1111
- def self.subtractRange(arRng, r2sub)
1112
-
1113
- ### Cases
1114
- #[st means status.]
1115
- #(1) if (now[0] < prev[0])
1116
- # (1-1) if (now[-1] < prev[0]), do nothing. [Totally exclusive]
1117
- # *--* => *--*
1118
- # *--*
1119
- # (1-2) if (now[-1] == prev[0])
1120
- # (1-2-1) AND if (now[-1].in?)&&(prev[-1].in?), Change prev[0].ex?=T [Nearly exclusive]
1121
- # I--* => O--*
1122
- # *--I
1123
- # (1-2-1) ELSE, do nothing. [Totally inclusive]
1124
- # O--* => *--*
1125
- # *--I
1126
- # *--* => *--*
1127
- # *--o
1128
- # (1-3) if (now[-1] > prev[0])
1129
- # (1-3-1) if (now[-1] < prev[-1]), => (now[-1],Prev[-1]).
1130
- # *---* => ..*-*
1131
- # *--*
1132
- # (1-3-2) if (now[-1] == prev[-1]), => (now[-1],Prev[-1]).
1133
- # (1-3-2-1) if (now[-1].ex?)&&(prev[-1].in?), (Prev[-1],Prev[-1])(Single_number)
1134
- # *---I => ....I
1135
- # *----O
1136
- # (1-3-2-2) ELSE, none.
1137
- # *---O => .....
1138
- # *----*
1139
- # *---* => .....
1140
- # *----I
1141
- # (1-3-3) if (now[-1] > prev[-1]), none
1142
- # *---* => .....
1143
- # *-----*
1144
- #(2) if (now[0] == prev[0])
1145
- # (2A-1) if (now[0].ex?)&&(prev[0].in?), => (Prev[0],Prev[0])(Single_number) + Any?
1146
- # I--- => I???
1147
- # O---
1148
- # (2A-2) ELSE, none for the first part.
1149
- # O--- => .???
1150
- # *---
1151
- # *--- => .???
1152
- # I---
1153
- # (2B) Follow (1) to get the New Array.
1154
- #(3) if (now[0] > prev[0])
1155
- # (3-1) if (now[-1] < prev[-1]), 2 Rangeary (Prev[0],now[0]),(now[-1],prev[-1])
1156
- # *-----* => *-*.*-*
1157
- # *-*
1158
- # (3-2) if (now[-1] == prev[-1])
1159
- # (3-2-1) if (now[-1].ex?)&&(prev[-1].in?), => (Prev[0],now[0]),(prev[-1],prev[-1])
1160
- # *----I => *-*...I
1161
- # *--O
1162
- # (3-2-2) ELSE, => (Prev[0],now[0])
1163
- # *----I => *-*....
1164
- # *--I
1165
- # *----O => *-*....
1166
- # *--*
1167
- # (3-3) if (now[-1] > prev[-1])
1168
- # (3-3-1) if (now[0] < prev[-1]), => (Prev[0],now[0]),(prev[-1],prev[-1])
1169
- # *---* => *-*....
1170
- # *---*
1171
- # (3-3-2) if (now[0] == prev[-1])
1172
- # (3-3-2-1) if (now[0].in?)&&(prev[0].in?), => (Prev[0],prev[-1].ex)
1173
- # *--I => *--O
1174
- # I--*
1175
- # (3-3-2-2) ELSE, unchanged.
1176
- # *--* => *--*
1177
- # O--*
1178
- # *--O => *--O
1179
- # *--*
1180
- # (3-3-3) if (now[0] > prev[-1]), unchanged.
1181
- # *--* => *--*
1182
- # *--*
1183
- #
1184
-
1185
- sort_ranges(arRng).map{ |er| # => Rangeary.sort_ranges
1186
- # sort_arrange(arRng).map{ |er|
1187
- if er.end < r2sub.begin
1188
- er
1189
- elsif er.begin < r2sub.begin && er.end < r2sub.end
1190
- (er.begin .. (r2sub.begin-1))
1191
- elsif er.begin < r2sub.begin && r2sub.end < er.end
1192
- [er.begin .. (r2sub.begin-1),
1193
- (r2sub.end+1) .. er.end ]
1194
- elsif r2sub.begin <= er.begin && er.end <= r2sub.end
1195
- nil
1196
- elsif r2sub.begin <= er.begin && er.begin <= r2sub.end && r2sub.end < er.end
1197
- (r2sub.end+1) .. er.end
1198
- elsif r2sub.end < er.begin
1199
- er
1200
- else
1201
- raise "This should not happen."
1202
- end
1203
- }.flatten.compact
1204
- end # def self.subtractRange(arRng, r2sub)
1205
- private_class_method :subtractRange
1206
-
1207
-
1208
- end
1209
-
1210
-