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