rangeary 1.0.1 → 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,1643 +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, **hs)
33
- Rangeary.new(*rest, **hs)
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
- # An arbitrary number of {Rangeary},
49
- # {RangeExtd}[http://rubygems.org/gems/range_extd] or Range objects (or its
50
- # descendant classes) can be supplied for the constructor {#initialize}.
51
- # Then, an operation of disjunction is performed to get the clean Array of
52
- # RangeExtd, none of which overlaps with the range.
53
- #
54
- # Once it is constructed, {Rangeary} objects are immutable. Any subsequent
55
- # operations return a new {Rangeary} object. All but few methods of {Array}
56
- # are inherited, though some methods, such as +push+ do not work because of
57
- # immutability of {Rangeary} objects.
58
- #
59
- class Rangeary < Array
60
- undef_method :*, :+, :length, :reverse
61
-
62
- # # Hash with the keys of :negative and :positive => becomes a method in Ver.1
63
- # attr_reader :infinities
64
-
65
- # Constructor
66
- #
67
- # Arbitrary (positive) number of arguments can be given.
68
- # (r1, [r2, ...])
69
- #
70
- # == Algorithm about the infinity
71
- #
72
- # In {Rangeary} operations, "infinity" is essential. Without it, negation
73
- # could not be definied. To determine what the positive and negative
74
- # infinities for the given elements is not a trivial task.
75
- #
76
- # Callers can supply user-defined infinity objects for both or either
77
- # positive and negative infinity and in that case they are accepted
78
- # as the infinities with the highest priority, though ArgumentError might be
79
- # issued if they contradict the elements; for example, if a {Rangeary}
80
- # instance consists of an array of Integer Ranges (RangeExtd) like (3..8),
81
- # and yet if String "abc" is specified as an infinity, it *contradicts*
82
- # with the elements in the sense they are not comparable.
83
- #
84
- # Alternatively, if the main argument contains one or moer {Rangeary}
85
- # instances, their infinities are inherited. If their infinities are not
86
- # identical to one another, warning may be issued if $VERBOSE is true, and
87
- # the infinities are selected in the following priority order:
88
- #
89
- # 1. If one or more user-supplied infinities are found, the smallest and
90
- # largest ones are accepted for the positive and negative infinities.
91
- # Note the consistency is not checked; that is, even if one or more of
92
- # the provided Range elements from which a {Rangeary} instance is about
93
- # to be constructed exceed the infinities, this does not warn. Obviously
94
- # subsequent operations may result in some unexpected result. Be warned.
95
- # 2. Float::INFINITY.
96
- # 3. RangeExtd::Infinity instances (RangeExtd::Infinity::POSITIVE
97
- # and RangeExtd::Infinity::NEGATIVE)
98
- #
99
- # If none of the main arguments contains {Rangeary}, the elements of each
100
- # Range are checked whether they have any comparable Numeric instances
101
- # like Integer (but not Complex), and if so, Float::INFINITY is set
102
- # as the infinities. Otherwise, it falls back to the defaults of
103
- # the RangeExtd::Infinity instances.
104
- #
105
- # The registered infinities for each instance is obtained as a Hash with
106
- # two keys of +:positive+ and +negative+ with the method {#infinities};
107
- # for example,
108
- #
109
- # ran.infinities # => { :positive => RangeExtd::Infinity::POSITIVE,
110
- # # :negative => "aa" }
111
- #
112
- # === Note for Developers about the infinity
113
- #
114
- # Instance variable @infinities (Hash) is defined. The elements can be nil;
115
- # for example, when only the element is RangeExtd::NONE.
116
- # So, it is recommended to access with the {#infinities} method rather
117
- # than accessing @infinities directly.
118
- #
119
- # At the moment, the infinities are guessed from the elements of the Range
120
- # it contains and set (+guess_strict: false+ is given to {#_get_infinities}).
121
- # If +guess_strict: true+ is given, unless a Range contains literally
122
- # +Float::INFINITY+ etc, (the element of) @infinities is not set (remains nil).
123
- # I hope it would work even if +guess_strict: true+ is given. However,
124
- # it is not tested properly! Anyway, to check the consistency of
125
- # the infinities is desirable, which is performed now, thanks to
126
- # +guess_strict: false+ given to {#_get_infinities}.
127
- #
128
- # @param inarall [Object] An arbitrary number of either {Rangeary}, RangeExtd or Range objects (or its subclasses).
129
- # @option **opts [Object] :positive Object for positive infinity. In default Float::INFINITY for comparable Numeric or else {RangeExtd::Infinity::POSITIVE}.
130
- # @option **opts [Object] :negative Object for negative infinity. In default -Float::INFINITY for comparable Numeric or else {RangeExtd::Infinity::NEGATIVE}.
131
- def initialize(*inarall, **opts)
132
- @infinities = { :negative => nil, :positive => nil }
133
-
134
- if inarall.size < 1
135
- super [RangeExtd::NONE] # Since Ver.1
136
- return
137
- # raise ArgumentError, "wrong number of arguments (#{inarall.size} for 1 or more)."
138
- end
139
-
140
- # Unfold Rangeary into Array of Arrays and convert Range-s into RangeExtd-s
141
- in_ranges = inarall.map{|i|
142
- self.class.send(:is_rangeary_type?, i) ? i.to_a : i
143
- }.flatten.map{|j|
144
- if (defined? j.exclude_begin?)
145
- j
146
- else
147
- begin
148
- RangeExtd.new(j)
149
- rescue ArgumentError, RangeError # Just to change the error message.
150
- raise ArgumentError, "invalid parameter for RangeExtd, hence for Rangeary (#{j.inspect})."
151
- end
152
- end
153
- }
154
-
155
- # NOTE: Simple map(&:infinities) is not ideal here, because map(&:infinities) tries to
156
- # supplement when the value is nil. However, in self.conjunction(), it uses map(&:infinities),
157
- # which is not ideal.
158
- inherited_infs = inarall.find_all{|i| self.class.send(:is_rangeary_type?, i)}.map{ |ec|
159
- ec.instance_variable_get(:@infinities)
160
- }
161
-
162
- @infinities = _get_infinities(in_ranges, inherited_infs: inherited_infs, guess_strict: false, **opts) # set @infinities (wherever possible)
163
-
164
- # Call _merge_overlaps
165
- begin
166
- arRange = _merge_overlaps( convert2range(in_ranges) )
167
- rescue => err
168
- # Trap just to change the type of the exception.
169
- raise ArgumentError, err.message, err.backtrace
170
- end
171
-
172
- if arRange.empty?
173
- raise ArgumentError, 'no significant argument given for Rangeary.'
174
- end
175
-
176
- ## Setting @infinities[:negative, :positive]
177
- # set_infinities_legacy(arRange, hsInheritedObj, hsInheritedAry, hsInheritedClass, **opts)
178
-
179
- super(arRange)
180
- self.freeze
181
- end # def initialize(*inarall, **opts)
182
-
183
- alias_method :triple_equals_orig, :=== if ! self.method_defined?(:triple_equals_orig)
184
-
185
- # Returns @infinities where nil values are replaced with something significant.
186
- #
187
- # Best guess approach is taken.
188
- #
189
- # @return [Hash] keys of :positive and :negative. The values are never nil.
190
- def infinities
191
- defhs = {
192
- :positive => RangeExtd::Infinity::POSITIVE,
193
- :negative => RangeExtd::Infinity::NEGATIVE,
194
- }
195
- hsret = {}.merge @infinities
196
- if hsret[:positive].nil?
197
- return defhs if hsret[:negative].nil?
198
- hsret[:positive] = (is_num_type?(hsret[:negative]) ? Float::INFINITY : defhs[:positive])
199
- return hsret
200
- end
201
- if hsret[:negative].nil?
202
- hsret[:negative] = (is_num_type?(hsret[:positive]) ? -Float::INFINITY : defhs[:negative])
203
- return hsret
204
- end
205
-
206
- hsret
207
- end
208
-
209
- # If self covers the entire range?
210
- #
211
- def all?
212
- rfirst = self[0]
213
- ((1 == size) &&
214
- !rfirst.is_none? &&
215
- (infinities[:negative] == rfirst.begin) &&
216
- ((infinities[:positive] == rfirst.end) || rfirst.end.nil?) && # Ruby 2.6 Endless Range
217
- (!rfirst.exclude_begin?) &&
218
- (!rfirst.exclude_end?))
219
- end
220
-
221
- # True if the inObj is in the range.
222
- #
223
- # This method works on the basis of each element, that is,
224
- # if for any of the {RangeExtd} in the {Rangeary}, {RangeExtd#===}
225
- # returns true, this will return true. That means, if the argument is
226
- # a {Rangeary} (or {Range}) object, this always returns false.
227
- # Note {#include?} and {#member?} work the same as in the standard {Array},
228
- # whereas {#include_element?} and {#member_element?} are the alias of
229
- # this method.
230
- #
231
- # See {#cover?}. The difference between this method and {#cover?} is
232
- # the same as that in {Range}.
233
- # @return [Boolean]
234
- def ===(inObj)
235
- to_a.each do |ea|
236
- if ea === inObj
237
- return true
238
- end
239
- end
240
- return false
241
- end
242
-
243
- alias_method :include_element?, :===
244
- alias_method :member_element?, :===
245
-
246
-
247
- # @return [Object] The {RangeExtd#begin} of the first {RangeExtd}.
248
- def begin()
249
- if to_a.size > 0
250
- to_a[0].begin
251
- else
252
- nil # Should not happen!
253
- end
254
- end
255
- alias_method :begin_element, :begin
256
-
257
-
258
- # If inObj is within the ranges, it will return true.
259
- #
260
- # See {#===}.
261
- # The difference between this method and {#===} is
262
- # the same as that in {Range}.
263
- def cover?(inObj)
264
- to_a.each do |ea|
265
- if ea.cover? inObj
266
- return true
267
- elsif (ea.end <=> inObj) == 1
268
- return false # No point of carrying on searching.
269
- end
270
- end
271
- return false
272
- end # def cover?(inObj)
273
-
274
-
275
- # Iterator for each element in the ranges.
276
- # @return [self]
277
- def each_element
278
- each do |er|
279
- er.each do |ee|
280
- yield ee
281
- end
282
- end
283
- self
284
- end
285
-
286
-
287
- # If the range defined in this object is empty (as in {Range#empty?}), returns true.
288
- #
289
- def empty_element?
290
- each do |er|
291
- if ! er.empty?
292
- return false
293
- end
294
- end
295
- return true
296
- end
297
-
298
-
299
- # @return [Object] The {RangeExtd#end} of the last {RangeExtd}.
300
- def end()
301
- if to_a.size > 0
302
- to_a[-1].end
303
- else
304
- nil
305
- end
306
- end
307
- alias_method :end_element, :end
308
-
309
-
310
- # {Range#equiv?} method, defined in range_extd library, extended to this {Rangeary}.
311
- #
312
- # @example
313
- # Rangeary(RangeExtd(1,"<...",4), 5...8).equiv?(Rangeary(2..3, 5..7)) # => true
314
- #
315
- # @param other [Rangeary]
316
- def equiv?(other)
317
- return false if size() != other.size
318
-
319
- self.zip(other).each do |ear|
320
- if ! ear[0].equiv?(ear[1])
321
- return false
322
- end
323
- end
324
-
325
- true
326
- end # def equiv?(other)
327
-
328
-
329
- # Returns the first n elements of the entire range, the same as {Range#first}.
330
- #
331
- # If the argument is not given, this simply calls {Range#begin} for the first
332
- # {RangeExtd}.
333
- #
334
- # If not, and if the elements in the ranges are not discrete, like Float,
335
- # an exception is raised (see {Range#first}).
336
- # Note this works on the element basis, being different from {Rangeary#first},
337
- # which works on the array basis.
338
- #
339
- # @param n [Integer] (Optional) positive.
340
- # @return [Object] equivalent to {#begin} if no argument is given.
341
- # @return [Array] Array of the first n elements in the range.
342
- # @raise [TypeError] if the ranges has no iteration defined, such as, starting from Float.
343
- def first_element(n=nil)
344
- if n.nil?
345
- self.begin()
346
- elsif n < 0
347
- raise ArgumentError, "the argument #{n} has to be positive."
348
- else
349
- arRet = []
350
- m = n
351
- to_a.each do |eachr|
352
- ar = eachr.first(m)
353
- arRet += ar
354
- if arRet.size >= n
355
- break
356
- else
357
- m -= ar.size
358
- end
359
- end
360
-
361
- arRet
362
- end # if n.nil?
363
- end # def first_element(n=nil)
364
-
365
-
366
- # Return an array of objects that consist of the ranges.
367
- #
368
- # @return [Array]
369
- # @raise [TypeError] if any of the ranges has no iteration defined, such as, starting from Float.
370
- # @example
371
- # Rangeary(2...4, 5..6, 8..9).to_a # => [2...4, 5..6, 8..9]
372
- # Rangeary(2...4, 5..6, 8..9).flatten # => [2, 3, 5, 6, 8, 9]
373
- # Rangeary(2...4, 5..6, 8..9).flatten.reduce(:+) # => 33
374
- def flatten_element
375
- to_a.reduce([]){|a,b| a+=b.to_a}
376
- end
377
-
378
-
379
- # @return [String]
380
- def inspect
381
- "<Rangeary:#{to_a.inspect}>"
382
- end
383
-
384
-
385
- # Can iterate?
386
- def iteratable?
387
- begin
388
- each do |i|
389
- i.each{break}
390
- end
391
- rescue TypeError
392
- return false
393
- end
394
- true
395
- end # def iteratable?
396
-
397
-
398
- # Returns the last n elements of the entire range, the same as {Range#last}.
399
- #
400
- # If the argument is not given, this simply calls {Range#end} for the last
401
- # {RangeExtd}.
402
- #
403
- # If not, and if the elements in the ranges are not discrete, like Float,
404
- # an exception is raised (see {Range#last}).
405
- # Note this works on the element basis, being different from {Rangeary#last},
406
- # which works on the array basis.
407
- #
408
- # @param n [Integer] (Optional) positive.
409
- # @return [Object] equivalent to {#end} if no argument is given.
410
- # @return [Array] Array of the last n elements in the range.
411
- # @raise [TypeError] if any of the ranges has no iteration defined, such as, starting from Float.
412
- def last_element(n=nil)
413
- if n.nil?
414
- self.end()
415
- elsif n < 0
416
- raise ArgumentError, "the argument #{n} has to be positive."
417
- else
418
- arRet = []
419
- m = n
420
- to_a.reverse.each do |eachr|
421
- ar = eachr.last(m)
422
- arRet = ar + arRet
423
- if arRet.size >= n
424
- break
425
- else
426
- m -= ar.size
427
- end
428
- end
429
-
430
- arRet
431
- end # if n.nil?
432
- end # def last_element(n=nil)
433
-
434
-
435
- # Practically equivalent to {#empty_element?}.
436
- def null_element?
437
- each do |er|
438
- if ! er.null?
439
- return false
440
- end
441
- end
442
- return true
443
- end
444
- alias_method :null?, :null_element?
445
-
446
-
447
- # Return the sum of {RangeExtd#size} of all the ranges in the object.
448
- #
449
- # @return [Integer] 0 if {RangeExtd::NONE}
450
- # @return [Float] Float::INFINITY if one of ranges is open-ended.
451
- # @return [nil] if any of the range is non-Numeric and not open-ended to the infinity.
452
- def size_element()
453
- begin
454
- to_a.map(&:size).reduce(:+)
455
- rescue TypeError
456
- nil
457
- end
458
- end
459
-
460
-
461
- # ======================= Operators =======================
462
-
463
- # Disjunction of a Rangeary (or RangeExtd or Range) and another
464
- #
465
- # @param r1 [Rangeary, RangeExtd, Range]
466
- # @param r2 [Rangeary, RangeExtd, Range]
467
- # @return [Rangeary]
468
- def self.disjunction(r1, r2)
469
- self.new(r1, r2)
470
- end
471
-
472
- # Add (Disjunction) a Rangeary (or RangeExtd or Range)
473
- #
474
- # @param inr [Rangeary, RangeExtd, Range]
475
- # @return [Rangeary]
476
- def disjunction(inr)
477
- self.class.new(self, inr)
478
- end
479
-
480
- alias_method :+, :disjunction
481
- alias_method :|, :disjunction # "|" (plus with Object#eql?) in general, but in this case it is identical.
482
-
483
- # Exclusive Disjunction (XOR) with a Rangeary (or RangeExtd or Range)
484
- #
485
- # @param r1 [Rangeary, RangeExtd, Range]
486
- # @param r2 [Rangeary, RangeExtd, Range]
487
- # @return [Rangeary]
488
- def self.exclusive_disjunction(r1, r2)
489
- Rangeary.new(r1).exclusive_disjunction(r2)
490
- end
491
-
492
- # Exclusive Disjunction (XOR) with a Rangeary (or RangeExtd or Range)
493
- #
494
- # @param inr [Rangeary, RangeExtd, Range]
495
- # @return [Rangeary]
496
- def exclusive_disjunction(inr)
497
- (disjunction(inr)).conjunction(conjunction(inr).negation)
498
- end
499
-
500
- alias_method :^, :exclusive_disjunction
501
- alias_method :xor, :exclusive_disjunction
502
-
503
- # Subtraction.
504
- #
505
- # @param r [Rangeary, RangeExtd, Range]
506
- # @return [Rangeary]
507
- def subtraction(r)
508
- conjunction( Rangeary.new(r).negation )
509
- end
510
-
511
- alias_method :-, :subtraction
512
-
513
-
514
- # Conjunction.
515
- #
516
- # @param r [Rangeary, RangeExtd, Range]
517
- # @return [Rangeary]
518
- def conjunction(r)
519
- self.class.conjunction(self, r)
520
- end
521
-
522
- alias_method :&, :conjunction
523
- alias_method :*, :conjunction
524
-
525
-
526
- # Negation (class method).
527
- #
528
- # @param r [Rangeary, RangeExtd, Range]
529
- # @return [Rangeary]
530
- def self.negation(r)
531
- self.new(r).negation
532
- end
533
-
534
-
535
- # Negation.
536
- #
537
- # @return [Rangeary]
538
- def negation()
539
- if to_a.empty?
540
- raise "ERROR: No range is defined." # This should not happen.
541
- end
542
-
543
- arran = []
544
- prevend = nil # if "end" is nil, this is substituted with Infinity.
545
- prevend_orig = Object # For Endless Range (Ruby 2.6), this can be nil.
546
- prevst = nil
547
- to_a.each do |eachr|
548
-
549
- # ALL -> NONE
550
- return Rangeary.new(RangeExtd::NONE, :positive => @infinities[:positive], :negative => @infinities[:negative]) if RangeExtd::ALL == eachr
551
-
552
- # null(NONE) -> ALL
553
- if eachr.null?
554
- begin
555
- _beg = 1.0 * eachr.begin
556
- return (-Float::INFINITY..Float::INFINITY)
557
- rescue TypeError # XXXX can't be coerced into Float
558
- return Rangeary.new(infinities[:negative]..infinities[:positive])
559
- end
560
- end
561
-
562
- ea_b = eachr.begin
563
- if (RangeExtd::Infinity::NEGATIVE == ea_b) # Including -Float::INFINITY and other general negative infinities (nb., [#==] is not commutative!).
564
- # The existing first range starts from the negative infinity, so skip this one.
565
- else
566
- if prevend.nil?
567
- # The returned first range starts from the negative infinity.
568
- ran_tmp = normalized_range_for_negation(infinities[:negative], eachr.begin)
569
- if RangeExtd::NONE != ran_tmp
570
- # Avoid (inf..inf) type, which would cause RangeError (in Ruby 2.6) anyway.
571
- arran.push( RangeExtd(ran_tmp, :exclude_end => (! eachr.exclude_begin?)) )
572
- end
573
- else
574
- arran.push( RangeExtd(prevend_orig, eachr.begin, :exclude_begin => (! prevst), :exclude_end => (! eachr.exclude_begin?)) )
575
- # arran.push( RangeExtd(prevend, eachr.begin, :exclude_begin => (! prevst), :exclude_end => (! eachr.exclude_begin?)) )
576
- end
577
- end # if (eachr.begin == -Float::INFINITY) || (RangeExtd::NONE == eachr.begin)
578
- prevend_orig = eachr.end
579
- prevend = _comparable_end(eachr) # For Ruby-2.6 Endless Range
580
- prevst = eachr.exclude_end?
581
- end # to_a.each do |eachr|
582
-
583
- if (RangeExtd::Infinity::POSITIVE == prevend) # Including Float::INFINITY and other general positive infinities (nb., [#==] is not commutative!).
584
- ## Do nothing
585
- else
586
- rbeg = (prevend_orig || prevend)
587
- ran_tmp = normalized_range_for_negation(rbeg, infinities[:positive])
588
-
589
- if RangeExtd::NONE != ran_tmp
590
- # Avoid (inf..inf) type, which would cause RangeError (in Ruby 2.6) anyway.
591
- arran.push( RangeExtd.new(ran_tmp, :exclude_begin => (! prevst)) )
592
- end
593
- end
594
-
595
- Rangeary.new(arran, :positive => infinities[:positive], :negative => infinities[:negative])
596
- end # def negation()
597
-
598
- alias_method :~@, :negation
599
-
600
-
601
- ####################
602
- # Public class methods
603
- ####################
604
-
605
- # Conjunction.
606
- #
607
- # @param r1 [Rangeary, RangeExtd, Range]
608
- # @param r2 [Rangeary, RangeExtd, Range]
609
- # @return [Rangeary]
610
- def self.conjunction(r1, r2)
611
-
612
- r1 = Rangeary.new(r1) if ! defined? r1.first_element
613
- r2 = Rangeary.new(r2) if ! defined? r2.first_element
614
- return Rangeary.new(RangeExtd::NONE) if r1.null? || r2.null?
615
- return Rangeary.new(r1) if r2.all?
616
- return Rangeary.new(r2) if r1.all?
617
-
618
- # Getting inherited options (if Rangeary is given) for the later use.
619
- hsInherited = _validate_select_infinities [r1, r2].map(&:infinities)
620
- # hsInherited = _best_inherited_infinities [r1, r2].map(&:infinities)
621
-
622
- # Initialisation
623
- a1 = r1.to_a
624
- a2 = r2.to_a
625
- rc = Rangeary.new( RangeExtd::NONE, **hsInherited )
626
-
627
- if a1.empty? || a2.empty?
628
- return rc
629
- end
630
-
631
- ### Algorithm
632
- # Conditions: Both a1 and a2 are sorted in order of #begin().
633
- # a1 is a reference array of RangeExtd
634
- # Then, Sigma(i=0..(a2.size-1)) a2[i]*a1[j=0..-1] => Returned Rangeary
635
- #
636
- # In reality, I put some tricks to avoid unnecessary calculation
637
- # for the sake of the processing speed. But essentially
638
- # it is a very simple algorithm.
639
- #
640
- last_a1index = 0
641
- a2.each do |ea2|
642
- a1.each_with_index do |ea1, ind|
643
- # A bit of trick just to avoid unnecessary process
644
- next if ind < last_a1index # skip
645
-
646
- # For Ruby-2.6 Endless Range, comparable_end() is employed.
647
- # Note: this comparison ignores @infinities even if set,
648
- # because @infinities may not be defined in the arguments!
649
- # Anyway, nothing should be larger than the upper limit (as tested below),
650
- # and so this should be fine.
651
- break if comparable_end(ea2) < ea1.begin # Completely out of range
652
- if comparable_end(ea1) < ea2.begin # Completely out of range
653
- last_a1index = ind if last_a1index < ind
654
- next
655
- end
656
-
657
- # Core - Perform conjunction.
658
- pq1 = conjunctionRangeExtd(ea1, ea2) # => Rangeary.conjunctionRangeExtd()
659
- if ! pq1.empty?
660
- rc += Rangeary.new(pq1)
661
- last_a1index = ind
662
- end
663
- end # a1.each_with_index do |ea1, ind|
664
- end # a2.each do |ea2|
665
-
666
- rc
667
- end # def self.conjunction(r1, r2)
668
-
669
-
670
- # Returns the infinity "end" that is comparable.
671
- #
672
- # Since Ruby-2.6, the Endless Range is introduced.
673
- # In Ruby definition, the Endless Range er takes a form of er=(5..nil),
674
- # and accordingly its "end" (or +last+) is nil:
675
- #
676
- # (5..nil).end # => nil
677
- #
678
- # Then, when you compare the "ends" of two Ranges with the method +<=>+,
679
- # the result too is nil.
680
- #
681
- # In fact, in Ruby, two Range objects are not comparable with eath other,
682
- # though the operator (or method) +<=>+ is defined for Range:
683
- #
684
- # (3..6) <=> (8..9) # => nil
685
- #
686
- # (Note this default behaviour is not unreasonable given what one compares
687
- # is equivocal for Ranges, for example, the size, start or end?
688
- # Indeed Range does not have a method +Range<=+ and hence +<=>+ is meaningless.)
689
- #
690
- # Then, when Range#end returns just nil, which should be interpreted
691
- # as *Endless* in Range's context, it does not matter for Range.
692
- # However, it is inconvenient for this class, {Rangeary}!
693
- # The heart of comparison in {Rangeary} is +<=>+ for the begin first,
694
- # and then the end if the first comparison results in equal.
695
- #
696
- # This method converts Range#end into something comparable when possible.
697
- # More specifically, if the "end" of the given Range is nil, this returns
698
- # Infinity (either +Float::INFINITY+ or +RangeExtd::Infinity::POSITIVE+),
699
- # unless both begin and end are nil (RangeExtd::NONE, nil..nil, nil...nil),
700
- # in which case nil is returned, and else returns simply +Range#end+.
701
- #
702
- # @param ran [Range, RangeExtd]
703
- # @return [Object]
704
- def self.comparable_end(ran)
705
- if ran.class.method_defined?(:is_none?) && ran.is_none?
706
- nil
707
- elsif ran.end.nil? && ran.begin.nil?
708
- nil
709
- elsif ! ran.end.nil?
710
- ran.end # Before Ruby 2.6, this is always the case.
711
- elsif ran.begin.class.method_defined? :to_int
712
- Float::INFINITY
713
- else
714
- RangeExtd::Infinity::POSITIVE
715
- end
716
- end
717
-
718
- # Returns the array sorted, based on (1) the begin objects and their boundary state,
719
- # (2) then the end objects and their boundary state.
720
- # {RangeExtd::NONE} comes first, if included in the input array.
721
- #
722
- # @param ar [<Range, RangeExtd>] Arbitrary number.
723
- # @return [Array<Range, RangeExtd>]
724
- def self.sort_ranges(*ar)
725
- ar.flatten.sort{ |a,b|
726
- err_msg_ab = "invalid parameter (#{a.inspect} or #{b.inspect})."
727
-
728
- # Since Ruby-2.6, the end can be nil (Endless Range).
729
- # Before Ruby-2.6, they used to raise Exception (ArgumentError) in Range
730
- # and so they would not have sneaked in to RangeExtd, either.
731
- # The following is in that sense meaningful only for Ruby 2.6 and later.
732
- ends = { :a => a, :b => b }
733
- ends.each_pair do |k, v|
734
- ends[k] = comparable_end(v)
735
- end
736
-
737
- ret = a.begin <=> b.begin
738
- case ret
739
- when -1, 1
740
- ret
741
- when 0
742
- a_exc_begin = (a.exclude_begin? rescue false)
743
- b_exc_begin = (b.exclude_begin? rescue false)
744
- if (a_exc_begin ^ b_exc_begin)
745
- if a_exc_begin # but not b
746
- 1
747
- else
748
- -1
749
- end
750
- else # <= (a.exclude_begin? == b.exclude_begin?)
751
- ret = ends[:a] <=> ends[:b]
752
- case ret
753
- when -1, 1
754
- ret
755
- when 0
756
- if (a.exclude_end? && b.exclude_end?)
757
- 0
758
- elsif a.exclude_end? # but not b
759
- -1
760
- else # <= b.exclude_end? but not a
761
- 1
762
- end # if (a.exclude_end? && b.exclude_end?)
763
- when nil
764
- # This should not happen for Range, let alone RangeExtd.
765
- # But could be the case for a user-defined class.
766
- if ends[:a].nil? && ends[:b].nil?
767
- 0
768
- elsif ends[:a].nil?
769
- -1
770
- elsif ends[:b].nil?
771
- 1
772
- else
773
- raise(TypeError, err_msg_ab)
774
- end
775
- else # case ret # ends[:a] <=> ends[:b]
776
- raise(TypeError, err_msg_ab)
777
- end # case ret # ends[:a] <=> ends[:b]
778
- end # if (a.exclude_begin? ^ b.exclude_begin?)
779
- when nil # case ret #(a.begin <=> b.begin)
780
- if a.begin.nil? && b.begin.nil?
781
- 0
782
- elsif a.begin.nil?
783
- -1
784
- elsif b.begin.nil?
785
- 1
786
- else
787
- raise(TypeError, err_msg_ab)
788
- end
789
- else # case ret #(a.begin <=> b.begin)
790
- raise(TypeError, err_msg_ab)
791
- end # case ret #(a.begin <=> b.begin)
792
- } # ar.sort{ |a,b|
793
- end # def self.sort_ranges(ar)
794
-
795
-
796
- ####################
797
- private
798
- ####################
799
-
800
- # Called from {Rangeary#initialize}
801
- def convert2range(inarall)
802
- inarall.flatten.map{|i|
803
- if defined? i.first_element
804
- i.to_a
805
- else
806
- i
807
- end
808
- }.flatten.map{|j|
809
- if (defined? j.exclude_begin?)
810
- j
811
- else
812
- RangeExtd(j)
813
- end
814
- }
815
- end
816
-
817
- # Get the instance variable (Hash) @infinities
818
- #
819
- # @param in_infs [Hash] infinities template with keys: :positive, :negative
820
- # @param arin [Array<Range, RangeExtd, Rangeary>]
821
- # @param strict: [Boolean] if true (Def: false), set only when the value satisfies RangeExtd::Infinity.infinite?
822
- # @param leave_existing: [Boolean] if true (Def: false), the existing non-nil values are not updated.
823
- # @return [Array] Best guess for @infinities
824
- def _best_guessed_infinities(in_infs, arin, strict: false, leave_existing: false)
825
- reths = {}.merge in_infs
826
- # Making the best effort to guess.
827
- ar_rae = [RangeExtd::Infinity::NEGATIVE, RangeExtd::Infinity::POSITIVE]
828
- ar_num = [-Float::INFINITY, Float::INFINITY]
829
- [arin].flatten.each do |ea|
830
- hs = { negative: ea.begin, positive: ea.end }
831
- reths.each_key do |ek|
832
- errmsg = "Contradictory reference value #{hs[ek].inspect} for infinity (Existent: #{reths[ek].inspect}) is found, but ignored."
833
- next if !hs[ek]
834
- case reths[ek]
835
- when nil
836
- reths[ek] = _guessed_infinity(ek, hs[ek], strict: strict)
837
- next
838
- when *ar_rae
839
- reths[ek] = (_guessed_infinity(ek, hs[ek], strict: strict) || reths[ek]) if !leave_existing # Update whatever.
840
- next
841
- when *ar_num
842
- # Once Float::INFINITY is set, it will unchange.
843
- next if ar_num.include? _guessed_infinity(ek, hs[ek]) # Consistent
844
- next if ar_rae.include? _guessed_infinity(ek, hs[ek]) # Inconsistent, but ignore (Prev: Range::Inf, Given: Float::INF)
845
- warn errmsg if !$VERBOSE.nil?
846
- next
847
- else
848
- # Once User's value is set, it will unchange.
849
- next if ar_rae.include? _guessed_infinity(ek, hs[ek]) # Inconsistent, but ignore (Prev: User, Given: Infinity)
850
- warn errmsg if !$VERBOSE.nil?
851
- next
852
- end
853
- end
854
- end
855
- reths
856
- end
857
- private :_best_guessed_infinities
858
-
859
- # Instance method version
860
- #
861
- # where @infinities are taken into account.
862
- #
863
- # @param ran [Range, RangeExtd]
864
- # @return [Object]
865
- def _comparable_end(ran)
866
- if ran.class.method_defined?(:is_none?) && ran.is_none?
867
- nil
868
- elsif ran.end.nil? && ran.begin.nil?
869
- nil
870
- elsif ! ran.end.nil?
871
- ran.end # Before Ruby 2.6, this is always the case.
872
- else
873
- infinities[:positive] || RangeExtd::Infinity::POSITIVE
874
- end
875
- end
876
- private :_comparable_end
877
-
878
- # Same as {#_comparable_end} but for begin
879
- #
880
- # Beyond the current Ruby (2.6).
881
- # Needed for {#negation}.
882
- #
883
- # @param rbeg [Object]
884
- # @param rend [Object]
885
- # @return [Object]
886
- def _comparable_begin(rbeg, rend)
887
- if rend.nil? && rbeg.nil?
888
- nil
889
- elsif ! rbeg.nil?
890
- rbeg
891
- elsif rend.class.method_defined? :to_int
892
- -Float::INFINITY
893
- else
894
- RangeExtd::Infinity::NEGATIVE
895
- end
896
- end
897
- private :_comparable_begin
898
-
899
-
900
- # Get the instance variable (Hash) @infinities
901
- #
902
- # @param arin [Array<Range, RangeExtd, Rangeary>]
903
- # @param inherited_infs [Array<Hash<Infinity, nil>>] Inherited infinities from the input Rangeary-s
904
- # @param guess_strict [Boolean] if true, make only strict guess for infinities, namely, unless the existing elements contain "infinity"-type objects, leave it nil; it is passed to {#_best_guessed_infinities}(strict: false)
905
- # @option **opts [Object] :positive Object for positive infinity. In default {Float::INFINITY} for Numeric Comparable or else {RangeExtd::Infinity::POSITIVE}.
906
- # @option **opts [Object] :negative Object for negative infinity. In default -{Float::INFINITY} for Numeric Comparable or else {RangeExtd::Infinity::NEGATIVE}.
907
- # @return [Array] guessed @infinities
908
- def _get_infinities(arin, inherited_infs: [], guess_strict: false, **opts)
909
- # Explicitly specified?
910
- reths = _get_infinities_from_opts(opts)
911
- if reths
912
- _validate_opts_infinities(arin, infs: reths)
913
- # return reths if (reths.all?{ |i| i[1] })
914
- end
915
- leave_existing = !!reths
916
-
917
- hs = {}.merge(@infinities)
918
- hs.each_key {|k| hs[k] = nil } # :positive, :negative => nil
919
- reths ||= hs
920
-
921
- # Read @infinities from Rangeary-s in arin, if exits
922
- reths = self.class.send(:_validate_select_infinities, inherited_infs, reths)
923
-
924
- return reths if (reths.all?{ |i| i[1] })
925
- leave_existing ||= reths.any?{ |i| i[1] }
926
-
927
- _best_guessed_infinities(reths, arin, strict: guess_strict, leave_existing: leave_existing)
928
- end
929
- private :_get_infinities
930
-
931
- # Returns @infinities from the options.
932
- #
933
- # If not specified actually, returns nil.
934
- #
935
- # @param opts [Hash]
936
- # @return [Hash, nil]
937
- def _get_infinities_from_opts(opts)
938
- hsret = {}
939
- if opts.keys.include?(:positive) || opts.keys.include?(:negative)
940
- if opts[:positive]
941
- hsret[:positive] = opts[:positive]
942
- opts[:negative] = -Float::INFINITY if is_num_type? opts[:positive]
943
- hsret[:negative] ||= opts[:negative]
944
- return hsret
945
- elsif opts[:negative]
946
- hsret[:negative] = opts[:negative]
947
- opts[:positive] = Float::INFINITY if is_num_type? opts[:negative]
948
- return hsret
949
- end
950
- end
951
- return nil
952
- end
953
- private :_get_infinities_from_opts
954
-
955
- # Returns the best guess Infinity from a given value (like 5.2 or "a")
956
- #
957
- # @param key [Symbol] either :positive or :negative
958
- # @param val [Object] from which Infinity is guessed.
959
- # @param strict [Boolean] if strict, unless the value is a kind of Infinite, return nil
960
- # @return [Hash<Array>] keys (:positive and :negative) Array#size may not agree between them.
961
- def _guessed_infinity(key, val, strict: false)
962
- return nil if !val
963
- return(RangeExtd::Infinity.infinite?(val) ? val : nil) if strict
964
- return(((key == :positive) ? 1 : -1) * Float::INFINITY) if is_num_type? val
965
- ((key == :positive) ? RangeExtd::Infinity::POSITIVE : RangeExtd::Infinity::NEGATIVE)
966
- end
967
- private :_guessed_infinity
968
-
969
-
970
- # Instance method version
971
- def is_num_type?(obj)
972
- self.class.send(__method__, obj)
973
- end
974
- private :is_num_type?
975
-
976
- # Normalize a Range, which is about to be used for new {Rangeary}
977
- #
978
- # @param rbeg [Object]
979
- # @param rend [Object]
980
- # @return [Range, RangeExtd::NONE]
981
- def normalized_range_for_negation(rbeg, rend)
982
- begin
983
- ret = rbeg..rend # Range
984
- rescue RangeError
985
- # the begin must be nil, after being converted from an Endless Range as in Ruby 2.6
986
- ret = (_comparable_begin(rbeg, rend)..rend) # Range
987
- # rescue ArgumentError
988
- ## NOTE: If this happens, something is gone wrong!!
989
- end
990
- return RangeExtd::NONE if same_infinities?(ret.begin, ret.end)
991
- ret
992
- end
993
- private :normalized_range_for_negation
994
-
995
- # True if both are infinities and in the same parity
996
- #
997
- # @param c1 [Object]
998
- # @param c2 [Object]
999
- def same_infinities?(c1, c2)
1000
- arin = [c1, c2]
1001
- arin.all?{ |ec| RangeExtd::Infinity.infinite?(ec) } &&
1002
- (arin.all?(&:positive?) || arin.all?(&:negative?))
1003
- end
1004
- private :same_infinities?
1005
-
1006
- # Validate @infinities from the options.
1007
- #
1008
- # If fails, raise Exception.
1009
- #
1010
- # @param arin [Array<Range, RangeExtd, Rangeary>]
1011
- # @param infs [Hash]
1012
- # @return [void]
1013
- # @raise [ArgumentError]
1014
- def _validate_opts_infinities(arin, infs: @infinities)
1015
- infs.each_pair do |ek, my_inf|
1016
- next if !my_inf #|| RangeExtd::Infinity.infinite?(my_inf)
1017
- arin.flatten.each do |er|
1018
- [er.begin, er.end].each do |ev|
1019
- next if !ev
1020
- next if (is_num_type?(ev) && is_num_type?(my_inf))
1021
- begin
1022
- case my_inf <=> ev
1023
- when -1, 0, 1
1024
- next
1025
- else # nil
1026
- end
1027
- rescue # NoMethodError
1028
- end
1029
- msg = "invalid parameter for :#{ek} => (#{my_inf.inspect}), incompatible with the range with Range=(#{er.inspect})."
1030
- raise ArgumentError, msg
1031
- end
1032
- end
1033
- end
1034
- end
1035
- private :_validate_opts_infinities
1036
-
1037
-
1038
- # Legacy routine to set the instance variable (Hash) @infinities
1039
- #
1040
- # Maybe more complete?
1041
- #
1042
- # @param [Array]
1043
- # @param [Hash]
1044
- # @param [Hash]
1045
- # @param [Hash]
1046
- # @return [void]
1047
- def set_infinities_old(arRange, hsInheritedObj, hsInheritedAry, hsInheritedClass, **opts)
1048
-
1049
- ### The following is required in the caller before calling this routine
1050
- ## inarall is the arguement received by initialize()
1051
- #
1052
- #
1053
- # hsInheritedObj = {:negative =>nil, :positive =>nil}
1054
- # hsInheritedAry = {:negative => [], :positive => []}
1055
- # hsInheritedClass = {:negative => [], :positive => []}
1056
- # inarall = inarall.map{|i|
1057
- # if defined?(i.first_element) && defined?(i.infinities)
1058
- # begin
1059
- # [:negative, :positive].each do |nega_posi|
1060
- # hsInheritedAry[nega_posi].push( i.infinities[nega_posi])
1061
- # hsInheritedClass[nega_posi].push(i.infinities[nega_posi].class)
1062
- # end
1063
- # rescue
1064
- # warn "warning: Rangeary#infinities looks wrong in the input (#{i})."
1065
- # end
1066
- # i.to_a
1067
- # else
1068
- # i
1069
- # end
1070
- # }.flatten.map{|j|
1071
- # if (defined? j.exclude_begin?)
1072
- # j
1073
- # else
1074
- # begin
1075
- # RangeExtd.new(j)
1076
- # rescue ArgumentError, RangeError # Just to change the error message.
1077
- # raise ArgumentError, "invalid parameter for RangeExtd, hence for Rangeary (#{j.inspect})."
1078
- # end
1079
- # end
1080
- # }
1081
-
1082
- # Check inherited objects if there is any, namely if the argument includes any RangeAry object.
1083
- # Priority: Float > Others > RangeExtd::Infinity
1084
- if hsInheritedAry[:negative].size > 0
1085
- [:negative, :positive].each do |es|
1086
- iFloat = hsInheritedClass[es].find_index(Float)
1087
- if iFloat.nil?
1088
- iElse = hsInheritedClass[es].find_index{|i| (i != RangeExtd::Infinity) && (i != Float)}
1089
- if iElse.nil?
1090
- iRangeInf = hsInheritedClass[es].find_index(RangeExtd::Infinity)
1091
- if iRangeInf.nil?
1092
- raise "Rangeary#infinities is not set in the input." # Should not happen, as Rangeary#infinities must be set always.
1093
- else
1094
- hsInheritedObj[es] = hsInheritedAry[es][iRangeInf]
1095
- end
1096
- else
1097
- hsInheritedObj[es] = hsInheritedAry[es][iElse]
1098
- end
1099
- else
1100
- hsInheritedObj[es] = hsInheritedAry[es][iFloat]
1101
- end # if iFloat.nil?
1102
- end # [:negative, :positive].each do |es|
1103
- end # if hsInheritedAry.size > 0
1104
-
1105
- # Determines what the infinities are: either Float::INFINITY or RangeExtd::Infinity::(POSI|NEGA)TIVE
1106
- hsFlag = { :found => {:negative => false, :positive => false} }
1107
- hsCand = {
1108
- :negative => arRange[0].begin,
1109
- :positive => _comparable_end(arRange[-1]),
1110
- # :pos_orig => arRange[-1].end, # may be nil in Ruby-2.6
1111
- }
1112
- infDef = { :negative => RangeExtd::Infinity::NEGATIVE, :positive => RangeExtd::Infinity::POSITIVE }
1113
- @infinities={ :negative => nil, :positive => nil }
1114
- [:negative, :positive].each do |es|
1115
- if (infDef[es] == hsCand[es]) # Can be Float or whatever.
1116
- @infinities[es] = hsCand[es] # highest priority
1117
- hsFlag[:found][:negative] = true
1118
- else
1119
- strtmp = ""
1120
- [opts[es], hsInheritedObj[es]].each do |opts_or_inherited|
1121
- @infinities[es] ||= opts_or_inherited # uses ots[:****tive] or hsInheritedObj[:****tive] if not set.
1122
- # Now, checking the compatibility of the infinity value specified (or inherited) with the given range.
1123
- if (! opts_or_inherited.nil?) && (opts_or_inherited == @infinities[es]) && (! arRange[0].is_none?)
1124
- begin
1125
- _ = 0 * (opts_or_inherited <=> hsCand[es])
1126
- rescue TypeError
1127
- raise ArgumentError, "invalid #{strtmp}parameter for :#{es} => (#{opts_or_inherited.inspect}), incompatible with the range with begin=(#{hsCand[es].inspect})."
1128
- end
1129
- end
1130
- strtmp = "inherited "
1131
- end # [opts[es], hsInheritedObj[es]].each do |opts_or_inherited|
1132
- end # if (infDef[es] == hsCand[es]) # else
1133
- end # [:negative, :positive].each do |es|
1134
-
1135
- if ! (@infinities[:negative] && @infinities[:positive])
1136
- # Either or both @infinities[:negative, :positive] is not set, yet.
1137
- # Need to set it now. The possibilities are,
1138
- # (1) arRange[0].null? && no opts/inheritance given.
1139
- # (2) one of them is given by either arRange or opts or inheritance, but not the other.
1140
- # (3) neither of them is given by arRange nor opts nor inheritance.
1141
- if arRange[0].null?
1142
- [:negative, :positive].each do |es|
1143
- @infinities[es] ||= infDef[es]
1144
- end
1145
- else
1146
- # There must be a non-infinity object - we will find it out.
1147
- if hsFlag[:found][:negative]
1148
- obj2refer = _comparable_end(arRange[0]) # For Ruby-2.6 Endless Range
1149
- # obj2refer = arRange[0].end
1150
- else
1151
- obj2refer = arRange[-1].begin
1152
- end
1153
-
1154
- # Now, if Numeric === obj2refer, Float::INFINITY is the default.
1155
- begin
1156
- _dummy = (1.0 < obj2refer)
1157
- rescue ArgumentError
1158
- # Not Numeric, hence the current infDef is used as it is.
1159
- else
1160
- # Numeric
1161
- infDef = { :negative => -Float::INFINITY, :positive => Float::INFINITY }
1162
- end
1163
- [:negative, :positive].each do |es|
1164
- @infinities[es] ||= infDef[es] # uses default infinity if not set.
1165
- end
1166
- end # if arRange[0].null?
1167
- end # if ! (@infinities[:negative] && @infinities[:positive])
1168
- end
1169
- private :set_infinities_old
1170
-
1171
- # Called from {Rangeary#initialize}.
1172
- #
1173
- # Process the array of RangeExtd and return the new one, in which
1174
- # overlapped ranges are merged accordingly.
1175
- #
1176
- # If there is no non-"empty" range, one of them will be left.
1177
- # As a priority, an empty range with a definite class is left,
1178
- # but if there is none, RangeExtd::NONE will be left.
1179
- #
1180
- # Note that (Inf..Inf) or (-Inf..-Inf) is replaced with +RangeExtd::NONE+,
1181
- # which then will be truncated.
1182
- #
1183
- # @param inAr [Array<RangeExtd,Range>]
1184
- # @return [Array]
1185
- def _merge_overlaps(inAr)
1186
- #def self.compact(inAr)
1187
-
1188
- ### Cases
1189
- #[st means status.]
1190
- #(0) (prev[0]) and (prev[0].status) unchanged.
1191
- #(1) if (now[-1]< prev[-1]), do nothing. [Totally inclusive]
1192
- # I---* => I---*
1193
- # *--*
1194
- #(2) if (now[-1]== prev[-1])
1195
- # (2-1) AND if (now[-1].st? || prev[-1].st?), prev[-1].st=T [Nearly inclusive]
1196
- # I--O => I--I
1197
- # I--I
1198
- # (2-2) ELSE do nothing. [Totally inclusive]
1199
- #(3) ELSE [namely, if (now[-1] > prev[-1])]
1200
- # (3-1) if (now[0] > prev[-1]), append. [Totally exclusive]
1201
- # *--* => *--* *--*
1202
- # *--*
1203
- # (3-2) if (now[0] == prev[-1])
1204
- # (3-2-1) (!now[0].st? && !prev[-1].st?), append. [Totally exclusive]
1205
- # *--O => *--O--*
1206
- # O--*
1207
- # (3-2-2) ELSE [namely, now[1].st? || prev[-1].st?], connect. (prev[0],now[-1])
1208
- # *--O => *-----*
1209
- # I--*
1210
- # *--I => *-----*
1211
- # O--*
1212
- # *--I => *-----*
1213
- # I--*
1214
- # (3-3) ELSE [namely, if (now[0] < prev[-1])], connect. (prev[0],now[-1])
1215
- # *--* => *---*
1216
- # *--*
1217
- #
1218
-
1219
- inRanges = _replace_inf_inf(inAr) # Replace meaningless inf..inf etc.
1220
- inRanges = self.class.sort_ranges(inRanges).map{|i| (RangeExtd === i) ? i : RangeExtd(i) } # => Rangeary.sort_ranges(ar)
1221
-
1222
- if inRanges.size < 1
1223
- return inRanges
1224
- end
1225
-
1226
- newRanges = [inRanges[0]]
1227
-
1228
- inRanges[1..-1].each do |eachr|
1229
- prev = newRanges[-1]
1230
-
1231
- # To deal with Ruby-2.6 Endless Range like (5..) (=(5..nil))
1232
- # *.end is guaranteed not to be false.
1233
- eachr_end = _comparable_end(eachr)
1234
- prev_end = _comparable_end(prev)
1235
-
1236
- case eachr_end <=> prev_end
1237
- when -1 # aka, eachr_end < prev_end
1238
- # Do nothing [Totally inclusive]
1239
- when 0
1240
- if (!eachr.exclude_end?) && prev.exclude_end?
1241
- # Change the status (:exclude_end => false) for prev
1242
- newRanges[-1] = RangeExtd.new(prev.begin, prev.end, :exclude_begin => prev.exclude_begin?, :exclude_end => false)
1243
- else
1244
- # Do nothing [Totally inclusive]
1245
- end
1246
- when 1 # aka, eachr_end > prev_end
1247
- case eachr.begin <=> prev_end
1248
- when -1 # Connect by combining
1249
- newRanges[-1] = RangeExtd.new(prev.begin, eachr.end, :exclude_begin => prev.exclude_begin?, :exclude_end => eachr.exclude_end?)
1250
- when 0
1251
- if (eachr.exclude_begin?) && (prev.exclude_end?)
1252
- newRanges.push(eachr) # [Totally exclude]
1253
- else # Connect by combining
1254
- newRanges[-1] = RangeExtd.new(prev.begin, eachr.end, :exclude_begin => prev.exclude_begin?, :exclude_end => eachr.exclude_end?)
1255
- end
1256
- when 1
1257
- newRanges.push(eachr) # [Totally exclude]
1258
- when nil
1259
- newRanges.push(eachr) # must be RangeExtd::NONE (or user-defined equivalent)
1260
- else
1261
- raise
1262
- end # case eachr.begin <=> prev_end
1263
- when nil # aka, eachr_end > prev_end
1264
- newRanges.push(eachr) # must be RangeExtd::NONE (or user-defined equivalent)
1265
- else
1266
- raise
1267
- end # case eachr_end <=> prev_end
1268
-
1269
- end # inRanges[1..-1].each do |eachr|
1270
-
1271
-
1272
- ## Sort out empty Ranges in the array.
1273
- # If there is at least one non-empty range, delete all empty ranges.
1274
- # If not, leave one of them, preferably not RangeExtd::NONE,
1275
- # unless there is no choice.
1276
- hsFlag = {
1277
- :empty? => true,
1278
- :klass => nil,
1279
- :found? => false,
1280
- }
1281
-
1282
- # Search for non-empty range.
1283
- newRanges.each do |er|
1284
- if er.empty?
1285
- if hsFlag[:klass].nil?
1286
- obj = er.begin()
1287
- if obj.nil?
1288
- ## Do nothing
1289
- else
1290
- hsFlag[:klass] = obj.class
1291
- end
1292
- end
1293
- else
1294
- hsFlag[:empty?] = false
1295
- break
1296
- end
1297
- end
1298
-
1299
- hsFlag[:found?] = false # Redundant, but for the sake of readability
1300
- if hsFlag[:empty?]
1301
- # It is all empty, hence delete all but one.
1302
- hsFlag[:klass] = NilClass if hsFlag[:klass].nil?
1303
- newRanges.delete_if do |er|
1304
- if hsFlag[:found?]
1305
- true
1306
- elsif er.begin().class == hsFlag[:klass]
1307
- hsFlag[:found?] = true
1308
- false
1309
- else
1310
- true
1311
- end
1312
- end
1313
- else
1314
- # Deletes all the empty ones.
1315
- newRanges.delete_if { |er| er.empty? }
1316
- end
1317
-
1318
- newRanges
1319
- end # def _merge_overlaps(inAr)
1320
-
1321
- private :_merge_overlaps
1322
-
1323
- # Replaces the invalid inf..inf Range with NONE
1324
- #
1325
- # @param arin [Array<Range, RangeExtd>]
1326
- # @return [Array]
1327
- def _replace_inf_inf(arin)
1328
- arin.map{ |er|
1329
- raise 'contact the code developer' if !defined? er.exclude_end? # Sanity check.
1330
- arran = [er.begin, er.end] # to_a raises RangeError for Ruby 2.6 Endless Range
1331
- if (( arran.all?{ |ea| RangeExtd::Infinity.infinite?(ea) } &&
1332
- (arran.all?(&:positive?) || arran.all?(&:negative?)) ) ||
1333
- (arran.all?(&:nil?)))
1334
- RangeExtd::NONE
1335
- else
1336
- er
1337
- end
1338
- }
1339
- end
1340
- private :_replace_inf_inf
1341
-
1342
- ####################
1343
- # private_class_method
1344
- ####################
1345
-
1346
- # Sort infinities obtained from inherited objects and returns the best one
1347
- #
1348
- # RangeExtd::Infinity is ignored. Float::INFINITY has the lowest priority.
1349
- #
1350
- # @param *ar_infs [Array] of Infinities Hash (inherited)
1351
- # @return [Hash] each key (:(posi|nega)tive) contains a single value (potentially null) for infinity.
1352
- def self._best_inherited_infinities(*ar_infs)
1353
- hsar = _sort_inherited_infinities_all( _get_cand_infinities(ar_infs.flatten) )
1354
- hsar.map{ |k, ev|
1355
- [k, ev[0]]
1356
- }.to_h # Ruby 2.1 or later
1357
- end
1358
- private_class_method :_best_inherited_infinities
1359
-
1360
- #== Logical conjunction of two RangeExtd
1361
- #
1362
- # To assure this logical conjunction meaningful,
1363
- # the objects that consist of RangeExtd objects have to be
1364
- # monotonic (increase), namely, for any potential element,
1365
- # x_n and x_m, within the given range,
1366
- # (x_n <=> x_m) == 1 if (n > m),
1367
- # have to be true. In other words, the derivative must be always
1368
- # non-negative.
1369
- #
1370
- # For example, (?a..?d) and (?x..?z) satisfies this condition.
1371
- # However, ('d'..'gg') does not, as follows.
1372
- # rd = RangeExtd('d'..'gg')
1373
- # rf = RangeExtd('f'..'h')
1374
- # Rangeary.conjunctionRangeExtd(rd, rf) # => ('f'..'gg')
1375
- #
1376
- # @note If you give a built-in Range object(s) for the arguments,
1377
- # make sure they are valid, that is, Range#valid? returns true.
1378
- #
1379
- #=== Algorithm
1380
- #
1381
- #[st means status. - true if excl; Estimate (Init(in|ex),Fini(in|ex))]
1382
- #[b4 <= af] (sort!)
1383
- #
1384
- #(1) Init = af[0], Init.st=af[0].st
1385
- # If (b4[0]==af[0]), then Init.st = ( b4[0].ex || af[0].ex)
1386
- #(2) Fini = [b4[-1], af[-1]].min, which belongs to (b4|af)
1387
- # If (b4[-1]==af[-1]), then Fini.st = (b4[-1].ex || af[-1].ex), otherwise nil for now.
1388
- #(3) if (Init > Fini) => none.
1389
- # *---* => ....
1390
- # *--* ....
1391
- #(4) if (Init == FiniMax), then Fini.st=b4[-1].st
1392
- # (4-1) if (Init.in&&Fini.in), => Single-Number-Range(InitCand(in))
1393
- # *--I => ...I
1394
- # I--* I...
1395
- # (4-2) else, => none
1396
- #(5) if (Init < FiniMax)
1397
- # (5-1) if Fini belongs to b4, Fini.st=b4[-1].st
1398
- # *---* => .*--*
1399
- # *---*
1400
- # (5-2) if Fini belongs to af, Fini.st=af[-1].st
1401
- # *---* => .*-*
1402
- # *-*
1403
- # (5-3) if Fini belongs to both, Fini.st is defined already.
1404
- # *---* => .*--*
1405
- # *--*
1406
- #
1407
- # @param r1 [RangeExtd] Can be Range
1408
- # @param r2 [RangeExtd] Can be Range
1409
- # @return [RangeExtd]
1410
- #
1411
- def self.conjunctionRangeExtd(r1, r2)
1412
-
1413
- [r1, r2].each do |er|
1414
- return er if er.is_none?
1415
- end
1416
-
1417
- r = *( sort_ranges([RangeExtd(r1), RangeExtd(r2)]) ) # => Rangeary.sort_ranges
1418
-
1419
- ## Note: the end product will be (cBeg(:stBeg), cEnd(:stEnd))
1420
- # where :stBeg and :stEnd mean exclude_(begin|end)?
1421
-
1422
- # Set the candidate begin value.
1423
- cBeg = r[1].begin
1424
- if r[0].begin == r[1].begin
1425
- stBeg = (r[1].exclude_begin? || r[0].exclude_begin?)
1426
- else
1427
- stBeg = r[1].exclude_begin?
1428
- end
1429
-
1430
- # Set the candidate end value. (comparable_end() for Ruby-2.6 Endless Range)
1431
- # Note: this comparison ignores @infinities even if set,
1432
- # because @infinities may not be defined in the arguments!
1433
- # Anyway, nothing should be larger than the upper limit
1434
- # and so this should be fine.
1435
- if comparable_end(r[0]) == comparable_end(r[1])
1436
- cEndOrig = r[1].end
1437
- cEnd = comparable_end(r[1])
1438
- stEnd = (r[0].exclude_end? || r[1].exclude_end?)
1439
- else
1440
- a = [[comparable_end(r[0]), 0, r[0].end], [comparable_end(r[1]), 1, r[1].end]].min
1441
- cEnd = a[0]
1442
- cEndIndex = a[1] # r[cEndIndex] == RangeExtd obj that gives the end of the resultant range.
1443
- cEndOrig = a[2]
1444
- stEnd = nil
1445
- end
1446
-
1447
- case cBeg <=> cEnd
1448
- when 1 # cBeg > cEnd
1449
- RangeExtd::NONE
1450
-
1451
- when 0 # cBeg == cEnd
1452
- stEnd = r[0].exclude_end?
1453
- if (!stBeg) && (!stEnd)
1454
- RangeExtd(cBeg..cBeg) # Point range
1455
- else
1456
- RangeExtd::NONE
1457
- end
1458
-
1459
- when -1 # cBeg < cEnd
1460
- # Now, the range must be (cBeg, cEnd). May need adjustment of the exclude status.
1461
- if stEnd.nil?
1462
- stEnd = r[cEndIndex].exclude_end?
1463
- # else
1464
- # # Already defined.
1465
- end # if stEnd.nil?
1466
-
1467
- RangeExtd(cBeg, cEndOrig, :exclude_begin => stBeg, :exclude_end => stEnd)
1468
- else
1469
- raise
1470
- end # case cBeg <=> cEnd
1471
-
1472
- end # def self.conjunctionRangeExtd(r1, r2)
1473
- private_class_method :conjunctionRangeExtd
1474
-
1475
- # Returns the candidate @infinities from the input Rangeary
1476
- #
1477
- # If there is any Rangeary in the input.
1478
- #
1479
- # Example return:
1480
- # { :positive => [nil, Float::INFINITY],
1481
- # :negative => [nil, -Float::INFINITY] }
1482
- #
1483
- # Note the standard @infinities is a Hash, but NOT a Hash of Array.
1484
- # This method returns the candidates.
1485
- #
1486
- # @param arin [Array<Hash<Infinity, nil>>] Inherited infinities from the input Rangeary-s
1487
- # @return [Hash<Array>] keys (:positive and :negative) Array#size may not agree between them.
1488
- def self._get_cand_infinities(arin)
1489
- hsret = { positive: [], negative: [] }
1490
- arin.each do |ec|
1491
- hsret.each_key do |k|
1492
- hsret[k] << ec[k]
1493
- end
1494
- end
1495
- hsret
1496
- end
1497
- private_class_method :_get_cand_infinities
1498
-
1499
- # True if object is a type of Rangeary
1500
- def self.is_rangeary_type?(obj)
1501
- obj.respond_to?(:infinities) && obj.class.method_defined?(:first_element)
1502
- end
1503
- private_class_method :is_rangeary_type?
1504
-
1505
- # True if object is a type of Numeric and comparable
1506
- def self.is_num_type?(obj)
1507
- Numeric === obj && obj.respond_to?(:between?) && obj.class.method_defined?(:<)
1508
- end
1509
- private_class_method :is_num_type?
1510
-
1511
- # Sort infinities obtained from inherited objects
1512
- #
1513
- # RangeExtd::Infinity is ignored. Float::INFINITY has the lowest priority.
1514
- #
1515
- # @param hs_inherit [Hash<Array>] :positive => Array, etc (From Rangeary-s in the main Array given)
1516
- # @return [Hash<Array>] each key (:(posi|nega)tive) contains the sorted candidate Array.
1517
- def self._sort_inherited_infinities_all(hs_inherit)
1518
- hs_inherit.map do |ek, ev|
1519
- [ek, _sort_inherited_infinities_each(ev, key=ek)]
1520
- end.to_h # Ruby 2.1 or later
1521
- end
1522
- private_class_method :_sort_inherited_infinities_all
1523
-
1524
- # Sort infinities obtained from inherited objects
1525
- #
1526
- # RangeExtd::Infinity is ignored. Float::INFINITY has the lowest priority.
1527
- #
1528
- # @param ar_infs [Array] of Infinities (inherited)
1529
- # @param key [Symbol] :positive or :negative
1530
- # @return [Array]
1531
- def self._sort_inherited_infinities_each(ar_infs, key=:positive)
1532
- ar_infs.map {|j|
1533
- (RangeExtd::Infinity === j) ? nil : j
1534
- }.compact.sort{|a,b|
1535
- if is_num_type?(a) && RangeExtd::Infinity.infinite?(a)
1536
- -1
1537
- elsif is_num_type?(b) && RangeExtd::Infinity.infinite?(b)
1538
- 1
1539
- else
1540
- begin
1541
- (key == :positive) ? (a<=>b) : (b<=>a)
1542
- rescue
1543
- 0
1544
- end
1545
- end
1546
- }
1547
- end
1548
- private_class_method :_sort_inherited_infinities_each
1549
-
1550
- # Validate the infinities by Options and inherited and select the best
1551
- #
1552
- # If Option is specified, that has the priority.
1553
- # Among the inherited, the youngest non-nil one has the highest priority.
1554
- #
1555
- # If there are any inconsistencies, issue a warning message, if $VERBOSE==true.
1556
- #
1557
- # Note this method does not look at the contents of the Range Array given.
1558
- #
1559
- # @param ar_inherit [Array<Hash<Infinity, nil>>] Inherited infinities from the input Rangeary-s
1560
- # @param hs_opts [Hash, nil] :positive, :negative, specified by the option to {Rangeary.initialize}
1561
- # @return [Hash] keys (:positive and :negative) with a single value for each
1562
- def self._validate_select_infinities(ar_inherit, hs_opts=nil)
1563
- hs_inherit = _get_cand_infinities(ar_inherit)
1564
- if !hs_opts
1565
- hs_opts = hs_inherit.map{ |ek, ev|
1566
- [hs_inherit[ek], nil]
1567
- }.to_h # Ruby 2.1 or later
1568
- end
1569
- # e.g., hs_inherit[:positive] == [ "z"(From-Option), nil(Inherited), "y"(Inherited), INFINITY(inherited) ]
1570
-
1571
- # Selection
1572
- hsret = _sort_inherited_infinities_all( hs_inherit ).map{ |ek, ev|
1573
- [ek, ([hs_opts[ek]]+ev).compact[0]]
1574
- }.to_h # Ruby 2.1 or later
1575
-
1576
- # Validation (for warning, issued when $VERBOSE==true)
1577
- hs_inherit.each_pair do |ek, ev|
1578
- ev_uniq = ([hs_opts[ek]]+ev).compact.uniq
1579
- msg = "Inconsistent %s infinities are found: %s (=> %s is used)"%[ek, ev_uniq.inspect, hsret[ek]]
1580
- #warn msg if $VERBOSE && (ev_uniq.size > 2) && (!ev_uniq.all?{ |c| RangeExtd::Infinity.infinite?(c) })
1581
- warn msg if (ev_uniq.size > 1) && (!ev_uniq.all?{ |c| RangeExtd::Infinity.infinite?(c) })
1582
- end
1583
-
1584
- hsret
1585
- end
1586
- private_class_method :_validate_select_infinities
1587
-
1588
- end # class Rangeary < Array
1589
-
1590
- # Overwrites its equal operator
1591
- class Array
1592
- alias_method :equals_before_rangeary, :== if ! self.method_defined?(:equals_before_rangeary)
1593
-
1594
- # Updated Array#==
1595
- #
1596
- # This returns true even if the standard #{==} is false, if both are *practically* empty,
1597
- # that is, if +#empty_element?+ and/or +empty?+ are true for both, they are equal.
1598
- # Also, this equates the "end" of Endless Range (Ruby 2.6) with Float::INFINITY or
1599
- # RangeExtd::Infinity::POSITIVE.
1600
- # Note by definition, it would appear at the last element in Rangeary only,
1601
- # if it does.
1602
- #
1603
- # @param other [Object]
1604
- def ==(other)
1605
- return true if equals_before_rangeary other
1606
- return false if !other.class.method_defined?(:to_ary)
1607
- return false if !self.class.method_defined?(:empty_element?) && !other.class.method_defined?(:empty_element?)
1608
-
1609
- # It was false. Is it?
1610
- # eg., (Rangeary[RangeExtd::NONE] == []) is true,
1611
- # because Rangeary[] with zero components does not exist!
1612
- # Now either other or self is guranteed to be Rangeary.
1613
- self_empt = (respond_to?(:empty_element?) ? empty_element? : empty?)
1614
- other_empt = (other.respond_to?(:empty_element?) ? other.empty_element? : other.empty?)
1615
- return true if self_empt && other_empt
1616
- return false if self_empt ^ other_empt
1617
- # return false if size != other.size # evaluated at the beginning.
1618
- return false if size >= 2 && self[0..-2] != other[0..-2]
1619
- return false if !self[-1].respond_to?(:exclude_end?) || !other[-1].respond_to?(:exclude_end?)
1620
-
1621
- # Now, both are guaranteed to have the same number of non-zero elements,
1622
- # all their elements except for the last one are equal,
1623
- # and their last elements are Range-type instances.
1624
- # Yet, the standard "equal" operator has failed.
1625
- # Only the potential they may be equal is their last elements differ
1626
- # between Endless Range (Ruby 2.6) and Infinity.
1627
- c_self = Rangeary.comparable_end self[-1]
1628
- c_other = Rangeary.comparable_end other[-1]
1629
- return false if c_self != c_other
1630
-
1631
- if !c_self.class.method_defined?(:infinite?) && !c_self.class.method_defined?(:infinity?)
1632
- # :infinite? for Float::INFINITY, :infinity? is defined in RangeExtd::Infinity
1633
- warn "sanity check failed. c_self should be infinite (#{c_self.inspect}). The result is not guranteed. Contact the code developer."
1634
- end
1635
-
1636
- # The end of their last elements are both positive Infinity.
1637
- # How about "begin"?
1638
- self_flag = (self[-1].class.method_defined?(:exclude_begin?) ? self[-1].exclude_begin? : false)
1639
- other_flag = (other[-1].class.method_defined?(:exclude_begin?) ? other[-1].exclude_begin? : false)
1640
- (self[-1].begin == other[-1].begin) && (self_flag == other_flag)
1641
- end
1642
- end # class Array
1643
-