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.
- checksums.yaml +4 -4
- data/ChangeLog +18 -0
- data/Makefile +2 -1
- data/News +4 -0
- data/README.ja.rdoc +461 -260
- data/lib/rangeary/util/hash_inf.rb +233 -0
- data/lib/rangeary/util.rb +727 -0
- data/lib/rangeary.rb +1420 -0
- data/rangeary.gemspec +51 -0
- data/test/tee_io.rb +111 -0
- data/test/test_rangeary.rb +400 -79
- metadata +25 -19
- data/lib/rangeary/rangeary.rb +0 -1643
data/lib/rangeary/rangeary.rb
DELETED
@@ -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
|
-
|