range_extd 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 +14 -0
- data/README.en.rdoc +317 -0
- data/README.ja.rdoc +300 -0
- data/Rakefile +9 -0
- data/lib/range_extd/infinity/infinity.rb +386 -0
- data/lib/range_extd/range_extd.rb +1137 -0
- data/range_extd.gemspec +47 -0
- data/test/test_range_extd.rb +1253 -0
- metadata +61 -0
data/Rakefile
ADDED
@@ -0,0 +1,386 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
if ! defined?(Rational)
|
4
|
+
require 'rational' # For Ruby 1.8
|
5
|
+
end
|
6
|
+
|
7
|
+
# This file is required from range_open/range_open.rb
|
8
|
+
class RangeExtd < Range
|
9
|
+
#
|
10
|
+
# =Class RangeExtd::Infinity
|
11
|
+
#
|
12
|
+
# Authors:: Masa Sakano
|
13
|
+
# License:: MIT
|
14
|
+
#
|
15
|
+
# ==Summary
|
16
|
+
#
|
17
|
+
# Class to hold just two constants:
|
18
|
+
# * RangeExtd::Infinity::NEGATIVE
|
19
|
+
# * RangeExtd::Infinity::POSITIVE
|
20
|
+
# and two more:
|
21
|
+
# * FLOAT_INFINITY (OBSOLETE; workaround for Ruby 1.8 to represent Float::INFINITY)
|
22
|
+
# * CLASSES_ACCEPTABLE (see below)
|
23
|
+
#
|
24
|
+
# There is no other object in this class (you can not create a new one).
|
25
|
+
#
|
26
|
+
# This class includes Comparable module.
|
27
|
+
#
|
28
|
+
# ==Description
|
29
|
+
#
|
30
|
+
# Both the two constant are an abstract value which is always smaller/larger,
|
31
|
+
# respectively, than any other Comparable objects (1 or -1 by i{#<=>}(obj))
|
32
|
+
# except for infinities with the same polarity, that is, positive or negative,
|
33
|
+
# in which case 0 is returned.
|
34
|
+
# See the document of the method #{==} for the definition of "infinity".
|
35
|
+
# Also, {#succ} is defined, which just returns self.
|
36
|
+
#
|
37
|
+
# There is a note of caution.
|
38
|
+
# The method {#<=>} is defined in this class as mentioned above.
|
39
|
+
# However any operator is, by Ruby's definition, not commutative,
|
40
|
+
# unless both the classes define so.
|
41
|
+
#
|
42
|
+
# There are only two built-in classes that are Comparable: String and Numeric
|
43
|
+
# (except for Complex).
|
44
|
+
# For String class objects, the #{<=>} operator work as expected
|
45
|
+
# in the commutative way.
|
46
|
+
# ?z <=> RangeExtd::Infinity::POSITIVE # => nil
|
47
|
+
# RangeExtd::Infinity::POSITIVE <=> ?z # => 1.
|
48
|
+
#
|
49
|
+
# For Numeric, it does not.
|
50
|
+
# 50 <=> RangeExtd::Infinity::POSITIVE # => nil
|
51
|
+
# RangeExtd::Infinity::POSITIVE <=> 50 # => 1.
|
52
|
+
#
|
53
|
+
# For that reason, for example,
|
54
|
+
# ( 50 .. RangeExtd::Infinity::POSITIVE)
|
55
|
+
# raises an exception, because the Numeric instance 50 does not
|
56
|
+
# know how to compare itself with a RangeExtd::Infinity instance,
|
57
|
+
# and Range class does not allow such a case.
|
58
|
+
#
|
59
|
+
# For Numeric, this is deliberately so.
|
60
|
+
# Please use {Float::INFINITY} instead in principle;
|
61
|
+
# it will be a lot faster in run-time, though it is
|
62
|
+
# perfectly possible for you to implement the feature
|
63
|
+
# in Numeric sub-classes, if need be.
|
64
|
+
#
|
65
|
+
# Any other Comparable classes are defined by users by definition,
|
66
|
+
# whether you or authors of libraries.
|
67
|
+
# The comparison with {RangeExtd::Infinity} instances are
|
68
|
+
# implemented in {Object#<=>} in this library. Hence, as long as
|
69
|
+
# the method <=> in the classes is written sensibly, that is, if it
|
70
|
+
# respects the method of the super-class when it does not know
|
71
|
+
# how to deal with an unknown object, there is no need for
|
72
|
+
# modification. Any object in your class (say, YourComparable)
|
73
|
+
# is immediately comparable with the {RangeExtd::Infinity} instances,
|
74
|
+
# YourComparable.new <=> RangeExtd::Infinity::POSITIVE # => -1
|
75
|
+
# RangeExtd::Infinity::POSITIVE <=> YourComparable.new # => 1
|
76
|
+
# except for the infinity inscances in YourComparable (see #{==}).
|
77
|
+
#
|
78
|
+
# See the document in {Object#<=>} in this code/package for detail.
|
79
|
+
#
|
80
|
+
# However, some existing Comparable classes, perhaps written by some
|
81
|
+
# one else may not be so polite, and has disabled comparison
|
82
|
+
# with any object but those intended. Unlucky you!
|
83
|
+
#
|
84
|
+
# For that sort of circumstances,
|
85
|
+
# the class method {RangeExtd::Infinity.overwrite_compare} provides
|
86
|
+
# a convenient way to overcome this problem to make
|
87
|
+
# the operator <=> commutative for a given Comparable class.
|
88
|
+
#
|
89
|
+
# Note {RangeExtd::Infinity.overwrite_compare} does nothing for the classes
|
90
|
+
# registered in the Class constant Array {RangeExtd::Infinity::CLASSES_ACCEPTABLE}.
|
91
|
+
# So, if you want to avoid such modification of the method <=>, perhaps
|
92
|
+
# by some other end users, you can register the class in that array.
|
93
|
+
#
|
94
|
+
# Only the methods defined in this class are
|
95
|
+
# {#===}, {#==}, {#<=>}, {#succ}, {#to_s}, {#inspect},
|
96
|
+
# {#infinity?}, {#positive?} and {#negative?}.
|
97
|
+
#
|
98
|
+
# Note that the unary operand {#-@} is not defined.
|
99
|
+
#
|
100
|
+
class Infinity
|
101
|
+
|
102
|
+
include Comparable
|
103
|
+
|
104
|
+
# Obsolete Constant FLOAT_INFINITY (for the sake of Ruby 1.8 or earlier).
|
105
|
+
# Please do not use it - it will be removed some time in the future.
|
106
|
+
# n.b., Module#private_constant is introduced in Ruby 1.9.3.
|
107
|
+
begin
|
108
|
+
FLOAT_INFINITY = Float::INFINITY
|
109
|
+
rescue # Ruby 1.8 or earlier.
|
110
|
+
FLOAT_INFINITY = 1/0.0
|
111
|
+
end
|
112
|
+
|
113
|
+
# Class that accept to be compared with Infinity instances.
|
114
|
+
CLASSES_ACCEPTABLE = [self, Float, Fixnum, Bignum, Rational, Numeric, String] # , BigFloat
|
115
|
+
|
116
|
+
def infinity?
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
def positive?
|
121
|
+
@positive
|
122
|
+
end
|
123
|
+
|
124
|
+
def negative?
|
125
|
+
!@positive
|
126
|
+
end
|
127
|
+
|
128
|
+
alias :cmp_before_rangeextd_infinity? :== if ! self.method_defined?(:cmp_before_rangeextd_infinity?) # No overwriting.
|
129
|
+
|
130
|
+
# Always -1 or 1 except for itself and the corresponding infinities (== 0). See {#==}.
|
131
|
+
# Or, nil (as defined by Object), if the argument is not Comparable, such as, nil and IO.
|
132
|
+
# @return [Integer] or possibly nil.
|
133
|
+
def <=>(c)
|
134
|
+
if c.nil?
|
135
|
+
super
|
136
|
+
elsif !defined?(c.<=) # Not Comparable?
|
137
|
+
super
|
138
|
+
elsif @positive
|
139
|
+
if self == c
|
140
|
+
0
|
141
|
+
else
|
142
|
+
1
|
143
|
+
end
|
144
|
+
else # aka negative
|
145
|
+
if self == c
|
146
|
+
0
|
147
|
+
else
|
148
|
+
-1
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Always false except for itself and the corresponding {Float::INFINITY}
|
154
|
+
# and those that have methods of {#infinity?} and {#positive?}
|
155
|
+
# with the corresponding true/false values, in which case this returns true.
|
156
|
+
def ==(c)
|
157
|
+
if (Infinity === c)
|
158
|
+
(@positive ^! c.positive?) # It should be OK to compare object_id?
|
159
|
+
elsif c == FLOAT_INFINITY && @positive
|
160
|
+
true
|
161
|
+
elsif c == -FLOAT_INFINITY && !@positive
|
162
|
+
true
|
163
|
+
elsif defined?(c.infinity?) && defined?(c.positive?)
|
164
|
+
(c.infinity? && (@positive ^! c.positive?))
|
165
|
+
else
|
166
|
+
false
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Equivalent to {#==}
|
171
|
+
def ===(c)
|
172
|
+
self == c
|
173
|
+
end
|
174
|
+
|
175
|
+
# @return [Infinity] self
|
176
|
+
def succ
|
177
|
+
self
|
178
|
+
end
|
179
|
+
|
180
|
+
# @return [String]
|
181
|
+
def inspect
|
182
|
+
if @positive
|
183
|
+
"INFINITY"
|
184
|
+
else
|
185
|
+
"-INFINITY"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
alias :to_s :inspect
|
190
|
+
|
191
|
+
|
192
|
+
# Overwrite "<=>" method of the given class, if necessary,
|
193
|
+
# to make its instances be comparable with RangeExtd::Infinity objects (constants).
|
194
|
+
# For example,
|
195
|
+
# RangeExtd::Infinity::NEGATIVE.<=>(any_comparable)
|
196
|
+
# always gives back -1 (except for same infinities). However the other way around,
|
197
|
+
# SomeClass.new.<=>(RangeExtd::Infinity::NEGATIVE)
|
198
|
+
# usually returns nil, which is not handy.
|
199
|
+
# Therefore, this function (Class method) provides a convenient
|
200
|
+
# way to overcome it, that is, if the given class
|
201
|
+
# (or the class of the given object) is Comparable,
|
202
|
+
# its "<=>" method is modified (and true is returned),
|
203
|
+
# unless it has been already done so, or some classes as listed below,
|
204
|
+
# such as Numeric and String, in which case nil is returned.
|
205
|
+
# If it is not Comparable, false is returned.
|
206
|
+
# The judgement whether it is Comparable or not is based
|
207
|
+
# whether the class has an instance method ThatClass#<=
|
208
|
+
#
|
209
|
+
# In processing, this method first looks up at an Array
|
210
|
+
# {RangeExtd::Infinity::CLASSES_ACCEPTABLE},
|
211
|
+
# and if the given class is registered in it,
|
212
|
+
# it does nothing. If not, and if all the othe conditions
|
213
|
+
# are met, it overwrites its <=> method and register
|
214
|
+
# the class in the array.
|
215
|
+
#
|
216
|
+
# @param obj [Object] Either Class or its object.
|
217
|
+
# @return [Boolean] or possibly nil (see the description).
|
218
|
+
def self.overwrite_compare(obj)
|
219
|
+
if defined? obj.instance_methods
|
220
|
+
klass = obj
|
221
|
+
else
|
222
|
+
klass = obj.class
|
223
|
+
|
224
|
+
begin
|
225
|
+
1.0 + obj # Use "rescue ArgumentError" if using "1.0<obj"
|
226
|
+
return nil # No change for Numeric
|
227
|
+
rescue TypeError
|
228
|
+
end
|
229
|
+
end # if defined? obj.instance_methods
|
230
|
+
|
231
|
+
# [Numeric, Fixnum, Bignum, Float, Rational, String, Complex].each do |i| # , BigFloat
|
232
|
+
(self::CLASSES_ACCEPTABLE+[self]).each do |i| # , BigFloat
|
233
|
+
# The class itself (RangeExtd::Infinity) must be rejected!
|
234
|
+
# Otherwise the rewrites itself, and may cause an infinite loop.
|
235
|
+
# In fact it is pre-defined in RangeExtd::Infinity, so the above addition is a duplication - just to make sure.
|
236
|
+
return nil if i == klass # No change for Numeric etc
|
237
|
+
# Built-in String, Numeric etc try to flip over "<=>" if it doesn't know the object!
|
238
|
+
end
|
239
|
+
self::CLASSES_ACCEPTABLE.push(klass) # The class is registered, so it would not come here again for the class.
|
240
|
+
|
241
|
+
a = klass.instance_methods
|
242
|
+
if !a.include?( :<= ) # NOT Comparable
|
243
|
+
return false
|
244
|
+
elsif a.include?(:compare_before_infinity)
|
245
|
+
return nil
|
246
|
+
else
|
247
|
+
# Overwrite the definition of "<=>" so that it is fliped over for Infinity.
|
248
|
+
|
249
|
+
code = <<__EOF__
|
250
|
+
alias :compare_before_infinity :== if ! self.method_defined?(:compare_before_infinity)
|
251
|
+
def <=>(c)
|
252
|
+
if defined?(self.<=) && RangeExtd::Infinity === c
|
253
|
+
if defined?(self.infinity?) && defined?(self.positive?)
|
254
|
+
if (self.positive? ^! c.positive?)
|
255
|
+
0
|
256
|
+
elsif self.positive?
|
257
|
+
1
|
258
|
+
else
|
259
|
+
-1
|
260
|
+
end
|
261
|
+
else
|
262
|
+
if c.positive?
|
263
|
+
-1
|
264
|
+
else
|
265
|
+
1
|
266
|
+
end
|
267
|
+
end
|
268
|
+
else
|
269
|
+
compare_before_infinity(c)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
__EOF__
|
273
|
+
#<<__EOF__ # for Emacs hilit.
|
274
|
+
|
275
|
+
klass.class_eval(code)
|
276
|
+
|
277
|
+
true
|
278
|
+
end
|
279
|
+
end # def self.overwrite_compare(obj)
|
280
|
+
|
281
|
+
|
282
|
+
private
|
283
|
+
|
284
|
+
def initialize(t)
|
285
|
+
@positive = (t && true)
|
286
|
+
end
|
287
|
+
|
288
|
+
NEGATIVE = new(false)
|
289
|
+
POSITIVE = new(true)
|
290
|
+
|
291
|
+
#NEGATIVE.freeze
|
292
|
+
#POSITIVE.freeze
|
293
|
+
|
294
|
+
end # class Infinity
|
295
|
+
|
296
|
+
|
297
|
+
class Infinity
|
298
|
+
# Disable new() so no other object will be created.
|
299
|
+
private_class_method :new
|
300
|
+
|
301
|
+
warn_level = $VERBOSE
|
302
|
+
begin
|
303
|
+
$VERBOSE = nil # Suppress the warning in the following line.
|
304
|
+
remove_method :initialize
|
305
|
+
ensure
|
306
|
+
$VERBOSE = warn_level
|
307
|
+
end
|
308
|
+
#undef_method :initialize
|
309
|
+
|
310
|
+
end # class Infinity
|
311
|
+
|
312
|
+
end # class RangeExtd < Range
|
313
|
+
|
314
|
+
|
315
|
+
#
|
316
|
+
# = class Object
|
317
|
+
#
|
318
|
+
# Overwrite Object#<=>() so all its sub-classes can be
|
319
|
+
# aware of RangeExtd::Infinity objects (the two constants).
|
320
|
+
#
|
321
|
+
class Object
|
322
|
+
alias :compare_obj_before_infinity :== if ! self.method_defined?(:compare_obj_before_infinity) # No overwriting.
|
323
|
+
|
324
|
+
# Overwrite #{Object#<=>}(). Then, all its sub-classes can be
|
325
|
+
# aware of RangeExtd::Infinity objects (the two constants).
|
326
|
+
#
|
327
|
+
# In this definition of "<=>", if self is Comparable
|
328
|
+
# (by judging whether it has the method "<="),
|
329
|
+
# it always returns, unless infinity? and positive? are set
|
330
|
+
# accordingly, either -1 or 1, depending which of
|
331
|
+
# RangeExtd::Infinity::(NEGATIVE|POSITIVE)
|
332
|
+
# is compared. If self is not Comparable, the original "<=>"
|
333
|
+
# is called, which should return nil (unless both the object_id
|
334
|
+
# agree, eg., nil and nil, in which case 0 is returned).
|
335
|
+
#
|
336
|
+
# If you define your own class, which is Comparable, you should
|
337
|
+
# define the method "<=>" as follows, as in the standard practice
|
338
|
+
# when you redefine a method that exists in a superclass;
|
339
|
+
#
|
340
|
+
# class MyComparableClass
|
341
|
+
# include Comparable
|
342
|
+
# # alias :cmp_orig :<=> if !self.method_defined?(:cmp_orig) # if you want
|
343
|
+
# def <=>(c)
|
344
|
+
# if c._is_what_i_expect?
|
345
|
+
# # Write your definition.
|
346
|
+
# else # When self does not know what to do with c.
|
347
|
+
# super # to call Object#<=>
|
348
|
+
# end
|
349
|
+
# end
|
350
|
+
# end
|
351
|
+
#
|
352
|
+
def <=>(c)
|
353
|
+
if defined?(self.<=) && RangeExtd::Infinity === c
|
354
|
+
# if defined?(self.<=) && defined?(c.infinity?) && defined?(c.positive?)
|
355
|
+
# NOTE: Duck-typing is inappropriate here.
|
356
|
+
# Only the objects that self wants to deal with here are
|
357
|
+
# the instances of RangeExtd::Infinity, and not other
|
358
|
+
# "infinity" object, such as, Float::INFINITY. So,
|
359
|
+
# (self <=> RangeExtd::Infinity::POSITIVE) # => -1
|
360
|
+
# (self <=> Float::INFINITY) # => nil
|
361
|
+
# in default.
|
362
|
+
if defined?(self.infinity?) && defined?(self.positive?)
|
363
|
+
if (self.positive? ^! c.positive?)
|
364
|
+
0
|
365
|
+
elsif self.positive?
|
366
|
+
1
|
367
|
+
else
|
368
|
+
-1
|
369
|
+
end
|
370
|
+
else
|
371
|
+
# (c <=> self) * (-1)
|
372
|
+
if c.positive?
|
373
|
+
-1
|
374
|
+
else
|
375
|
+
1
|
376
|
+
end
|
377
|
+
end
|
378
|
+
elsif object_id == c.object_id # (nil <=> nil) # => 0
|
379
|
+
0
|
380
|
+
else
|
381
|
+
nil
|
382
|
+
end # if defined?(self.<=) && RangeExtd::Infinity === c
|
383
|
+
end # def <=>(c)
|
384
|
+
end # class Object
|
385
|
+
|
386
|
+
|
@@ -0,0 +1,1137 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
## Load required files.
|
4
|
+
err1st = nil
|
5
|
+
req_files = %w(lib/range_extd/infinity/infinity)
|
6
|
+
req_files.each do |req_file|
|
7
|
+
while ! req_file.empty?
|
8
|
+
begin
|
9
|
+
require req_file
|
10
|
+
rescue LoadError => errLoad
|
11
|
+
err1st = errLoad if err1st.nil?
|
12
|
+
if %r@/@ =~ req_file
|
13
|
+
if req_file.sub!(%r@[^/]*/@, '').nil? # Will search for the next directory down.
|
14
|
+
raise
|
15
|
+
end
|
16
|
+
else
|
17
|
+
req_file = ''
|
18
|
+
break
|
19
|
+
end
|
20
|
+
else
|
21
|
+
break
|
22
|
+
end
|
23
|
+
end
|
24
|
+
if req_file.empty?
|
25
|
+
raise err1st
|
26
|
+
end
|
27
|
+
end # req_files.each do |req_file|
|
28
|
+
|
29
|
+
########################################
|
30
|
+
# Initial set up of 2 constants in RangeExtd.
|
31
|
+
########################################
|
32
|
+
|
33
|
+
class RangeExtd < Range
|
34
|
+
## Temporary initialize() just to define the two constants.
|
35
|
+
def initialize(rangepart, ex_begin, ex_end)
|
36
|
+
@rangepart = rangepart
|
37
|
+
@exclude_begin = ex_begin
|
38
|
+
@exclude_end = ex_end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Two constants
|
42
|
+
NONE = RangeExtd.new(nil...nil, true, true) # In Ruby1.8, this causes ArgumentError: bad value for range (because (nil..nil) is unaccepted).
|
43
|
+
EVERYTHING = RangeExtd.new(Infinity::NEGATIVE..Infinity::POSITIVE, false, false)
|
44
|
+
|
45
|
+
#NONE.freeze
|
46
|
+
#EVERYTHING.freeze
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# =Class RangeExtd
|
51
|
+
#
|
52
|
+
# Authors:: Masa Sakano
|
53
|
+
# License:: MIT
|
54
|
+
#
|
55
|
+
# ==Summary
|
56
|
+
#
|
57
|
+
# Extended Range class that features:
|
58
|
+
# 1. includes exclude_begin? (to exclude the "begin" boundary),
|
59
|
+
# 2. allows open-ended range (to the infinity),
|
60
|
+
# 3. defines NONE and EVERYTHING constants,
|
61
|
+
# 4. the first self-consistent logical structure,
|
62
|
+
# 5. complete backward compatibility within the built-in Range.
|
63
|
+
#
|
64
|
+
# The instance of this class is immutable, that is, you can not
|
65
|
+
# alter the element once an instance is generated.
|
66
|
+
#
|
67
|
+
# What is valid is checked with the class method {RangeExtd.valid?}.
|
68
|
+
# See the document of that method for the definition.
|
69
|
+
#
|
70
|
+
# To express open-ended ranges is simple; you just use either of
|
71
|
+
# the two (negative and positive, or former and later) constants
|
72
|
+
# in RangeExtd::Infinity class. See the document for detail.
|
73
|
+
#
|
74
|
+
# ==Examples
|
75
|
+
#
|
76
|
+
# An instance of a range of 5 to 8 with both ends being exclusive is created as
|
77
|
+
#
|
78
|
+
# r = RangeExtd(5...8, true)
|
79
|
+
# r.exclude_begin? # => true
|
80
|
+
#
|
81
|
+
class RangeExtd < Range
|
82
|
+
|
83
|
+
# @note If the given optional parameter(s) of exclude_begin|end
|
84
|
+
# do not agree with those in the first parameter (range),
|
85
|
+
# the value(s) of the optional parameter(s) is used.
|
86
|
+
#
|
87
|
+
# @overload new(range, [exclude_begin=false, [exclude_end=false, [opts]]])
|
88
|
+
# @param [Object] range Instance of Range or its subclasses, including RangeExtd
|
89
|
+
# @param exclude_begin [Object] true or false(Default) or any
|
90
|
+
# @param exclude_end [Object] true or false(Default) or any.
|
91
|
+
#
|
92
|
+
# @overload new(obj_begin, obj_end, [exclude_begin=false, [exclude_end=false, [opts]]])
|
93
|
+
# @param obj_begin [Object] Any object that is {Comparable} with end
|
94
|
+
# @param obj_end [Object] Any object that is Comparable with begin
|
95
|
+
# @param exclude_begin [Object] true or false(Default) or any
|
96
|
+
# @param exclude_end [Object] true or false(Default) or any.
|
97
|
+
#
|
98
|
+
# Note no possible opts are defined at present.
|
99
|
+
# Any opts given as Hash are simply ignored.
|
100
|
+
#
|
101
|
+
# @raise [ArgumentError] particularly if the range to be created is not {#valid?}.
|
102
|
+
def initialize(*inar)
|
103
|
+
|
104
|
+
arout = RangeExtd.class_eval{ _get_init_args(*inar) } # From Ruby 1.8.7 (?)
|
105
|
+
|
106
|
+
### The following routine is obsolete.
|
107
|
+
### Users, if they wish, should call RangeExtd::Infinity.overwrite_compare() beforehand.
|
108
|
+
### Or better, design their class properly in the first place!
|
109
|
+
### See the document in Object#<=> in this code for detail.
|
110
|
+
#
|
111
|
+
# # Modify (<=>) method for the given object, so that
|
112
|
+
# # it becomes comparable with RangeExtd::Infinity,
|
113
|
+
# # if the object is already Comparable.
|
114
|
+
# #
|
115
|
+
# # This must come first.
|
116
|
+
# # Otherwise it may raise ArgumentError "bad value for range",
|
117
|
+
# # because the native Range does not accept
|
118
|
+
# # (Obj.new..RangeExtd::Infinity::POSITIVE)
|
119
|
+
# #
|
120
|
+
# boundary = nil
|
121
|
+
# aroutid0 = arout[0].object_id
|
122
|
+
# aroutid1 = arout[1].object_id
|
123
|
+
# if aroutid0 == RangeExtd::Infinity::NEGATIVE.object_id ||
|
124
|
+
# aroutid0 == RangeExtd::Infinity::POSITIVE.object_id
|
125
|
+
# boundary = arout[1]
|
126
|
+
# elsif aroutid1 == RangeExtd::Infinity::NEGATIVE.object_id ||
|
127
|
+
# aroutid1 == RangeExtd::Infinity::POSITIVE.object_id
|
128
|
+
# boundary = arout[0]
|
129
|
+
# end
|
130
|
+
# if (! boundary.nil?) && !defined?(boundary.infinity?)
|
131
|
+
# RangeExtd::Infinity.overwrite_compare(boundary) # To modify (<=>) method for the given object.
|
132
|
+
# # Infinity::CLASSES_ACCEPTABLE ...
|
133
|
+
# end
|
134
|
+
|
135
|
+
if ! RangeExtd.valid?(*arout)
|
136
|
+
raise ArgumentError, "the argument can not consist of a RangeExtd instance."
|
137
|
+
end
|
138
|
+
|
139
|
+
@exclude_begin = arout.pop
|
140
|
+
@exclude_end = arout[-1]
|
141
|
+
@rangepart = Range.new(*arout)
|
142
|
+
|
143
|
+
end # def initialize(*inar)
|
144
|
+
|
145
|
+
|
146
|
+
# true if self is identical to {RangeExtd::NONE}.
|
147
|
+
# This is different from {#==} method!
|
148
|
+
# RangeExtd(0,0,false,false) == RangeExtd::NONE # => true
|
149
|
+
# RangeExtd(0,0,false,false).empty? # => true
|
150
|
+
# RangeExtd(0,0,false,false).is_none? # => false
|
151
|
+
# RangeExtd::NONE.is_none? # => true
|
152
|
+
def is_none?
|
153
|
+
self.begin.nil? && self.end.nil? && @exclude_begin && @exclude_end # Direct comparison with object_id should be OK?
|
154
|
+
end
|
155
|
+
|
156
|
+
# true if self is identical to RangeExtd::EVERYTHING ({#==} does not mean it at all!)
|
157
|
+
# (RangeExtd::Infinity::NEGATIVE..RangeExtd::Infinity::POSITIVE).is_everything? # => false
|
158
|
+
def is_everything?
|
159
|
+
self.begin.object_id == Infinity::NEGATIVE.object_id && self.end.object_id == Infinity::POSITIVE.object_id && !@exclude_begin && !@exclude_end # Direct comparison with object_id should not work for this one!! (because users can create an identical one.)
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
# Returns true if the "begin" boundary is excluded, or false otherwise.
|
164
|
+
def exclude_begin?
|
165
|
+
@exclude_begin
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns true if the "end" boundary is excluded, or false otherwise.
|
169
|
+
def exclude_end?
|
170
|
+
@exclude_end
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
# Like {Range}, returns true only if both of them are {Range} (or its subclasses), and
|
175
|
+
# in addition if both {#exclude_begin?} and {#exclude_end?} match (==) between the two objects.
|
176
|
+
# For the empty ranges they are somewhat different. In short, when both
|
177
|
+
# of them are empty and they belong to the same Class or have common ancestors
|
178
|
+
# (apart from Object and BasicObject, excluding all the included modules),
|
179
|
+
# this returns true, regardless of their boundary values.
|
180
|
+
# And any empty range is equal to RangeExtd::Infinity::NONE.
|
181
|
+
#
|
182
|
+
# For example,
|
183
|
+
# (1<...1) == RangeExtd::NONE # => true
|
184
|
+
# (?a<...?b) == RangeExtd::NONE # => true
|
185
|
+
# (1<...1) == (2<...2) # => true
|
186
|
+
# (1<...1) == (3<...4) # => true
|
187
|
+
# (?a<...?b) == (?c<...?c) # => true
|
188
|
+
# (1<...1) != (?c<...?c) # - because of Fixnum and String
|
189
|
+
# (1.0<...1.0) == (3<...4) # => true
|
190
|
+
#
|
191
|
+
# Note the last example will return false for {#eql?} -- see {#eql?}
|
192
|
+
#
|
193
|
+
# See {#eql?}
|
194
|
+
# @return [Boolean]
|
195
|
+
def ==(r)
|
196
|
+
re_equal_core(r, :==)
|
197
|
+
end # def ==(r)
|
198
|
+
|
199
|
+
# The same as {#==} but it uses eql?() as each comparison.
|
200
|
+
# For the empty ranges, it is similar to {#==}, except
|
201
|
+
# the immediate class has to agree to return true.
|
202
|
+
# Only the exception is the comparison with RangeExtd::Infinity::NONE.
|
203
|
+
# Therefore,
|
204
|
+
# (1...5) == (1.0...5.0) # => true
|
205
|
+
# (1...5).eql?(1.0...5.0) # => false
|
206
|
+
# (1<...1).eql?( RangeExtd::NONE) # => true
|
207
|
+
# (?a<...?b).eql?(RangeExtd::NONE) # => true
|
208
|
+
# (1<...1).eql?( 3<...4) # => true
|
209
|
+
# (1.0<...1.0).eql?(3<...4) # => false
|
210
|
+
#
|
211
|
+
def eql?(r)
|
212
|
+
re_equal_core(r, :eql?)
|
213
|
+
end # def eql?(r)
|
214
|
+
|
215
|
+
|
216
|
+
# If the object is open-ended to the negative (Infinity),
|
217
|
+
# this returns nil in default, unless the given object is Numeric
|
218
|
+
# (and comparable of Real), in which case this calls {#cover?},
|
219
|
+
# or if self is {RangeExtd::EVERYTHING} and the object is Comparable.
|
220
|
+
#
|
221
|
+
# In the standard Range, this checks whether the given object is a member, hence,
|
222
|
+
# (?D..?z) === ?c # => true
|
223
|
+
# (?a..?z) === "cc" # => false
|
224
|
+
# In the case of the former, after finite trials of succ() from ?c, it reaches the end (?z).
|
225
|
+
# In the latter, after finit trials of succ() from the begin ?a, it reaches the end (?z).
|
226
|
+
# Therefore it is theoretically possible to prove it (n.b., the actual
|
227
|
+
# algorithm of built-in {Range#include?} is different and cheating!
|
228
|
+
# See below.).
|
229
|
+
#
|
230
|
+
# However, in the case of
|
231
|
+
# (?D..Infinity) === ?c
|
232
|
+
# it can never prove ?c is a member after infinite trials of succ(),
|
233
|
+
# whether it starts the trials from the begin (?D) or the object (?c).
|
234
|
+
#
|
235
|
+
# For anything but Numeric, use {#cover?} instead.
|
236
|
+
#
|
237
|
+
# Note
|
238
|
+
# (?B..?z) === 'dd' # => false
|
239
|
+
# as Ruby's {Range} knows the algorithm of {String#succ} and {String#<=>}
|
240
|
+
# and specifically checks with it, before using {Enumerable#include?}.
|
241
|
+
# @see https://github.com/ruby/ruby/blob/trunk/range.c
|
242
|
+
#
|
243
|
+
# Therefore, even if you change the definition of {String#succ}
|
244
|
+
# so that 'B'.succ => 'dd', 'dd'.succ => 'z', as follows,
|
245
|
+
# class String
|
246
|
+
# alias :succ_orig :succ
|
247
|
+
# def succ
|
248
|
+
# if self == 'B'
|
249
|
+
# 'dd'
|
250
|
+
# elsif self == 'dd'
|
251
|
+
# 'z'
|
252
|
+
# else
|
253
|
+
# :succ_orig
|
254
|
+
# end
|
255
|
+
# end
|
256
|
+
# end
|
257
|
+
# the resutl of {Range#===} will unchange;
|
258
|
+
# (?B..?z) === 'dd' # => false
|
259
|
+
# (?B..?z).to_a # => ["B", "dd", "z"]
|
260
|
+
#
|
261
|
+
# Similarly {Range} treats String differently;
|
262
|
+
# (?X..?z).each do |i| print i;end # => "XYZ[\]^_`abcdefghijklmnopqrstuvwxyz"
|
263
|
+
# ?Z.succ # => 'AA'
|
264
|
+
#
|
265
|
+
# @param obj [Object] If this Object is a member?
|
266
|
+
def ===(obj)
|
267
|
+
# ("a".."z")===("cc") # => false
|
268
|
+
|
269
|
+
return false if is_none? # No need of null?(), supposedly!
|
270
|
+
|
271
|
+
begin
|
272
|
+
1.0+(obj) # OK if Numeric.
|
273
|
+
|
274
|
+
rescue TypeError
|
275
|
+
# obj is not Numeric, hence runs brute-force check.
|
276
|
+
beg = self.begin()
|
277
|
+
if defined?(beg.infinity?) && beg.infinity? || beg == -Infinity::FLOAT_INFINITY
|
278
|
+
return nil
|
279
|
+
# raise TypeError "can't iterate from -Infinity"
|
280
|
+
end
|
281
|
+
|
282
|
+
each do |ei|
|
283
|
+
if ei == obj
|
284
|
+
return true
|
285
|
+
end
|
286
|
+
end
|
287
|
+
false
|
288
|
+
|
289
|
+
else
|
290
|
+
cover?(obj)
|
291
|
+
end
|
292
|
+
end # def ===(obj)
|
293
|
+
|
294
|
+
alias :include? :===
|
295
|
+
alias :member? :===
|
296
|
+
|
297
|
+
|
298
|
+
# @return [Object]
|
299
|
+
def begin()
|
300
|
+
@rangepart.begin()
|
301
|
+
end
|
302
|
+
|
303
|
+
# @return [Object]
|
304
|
+
def end()
|
305
|
+
@rangepart.end()
|
306
|
+
end
|
307
|
+
|
308
|
+
# bsearch is internally implemented by converting a float into 64-bit integer.
|
309
|
+
# The following examples demonstrate what is going on.
|
310
|
+
#
|
311
|
+
# ary = [0, 4, 7, 10, 12]
|
312
|
+
# (3...4).bsearch{ |i| ary[i] >= 11} # => nil
|
313
|
+
# (3...5).bsearch{ |i| ary[i] >= 11} # => 4 (Integer)
|
314
|
+
# (3..5.1).bsearch{ |i| ary[i] >= 11} # => 4.0 (Float)
|
315
|
+
# (3.6..4).bsearch{ |i| ary[i] >= 11} # => 4.0 (Float)
|
316
|
+
# (3.6...4).bsearch{ |i| ary[i] >= 11} # => nil
|
317
|
+
# (3.6...4.1).bsearch{|i| ary[i] >= 11} # => 4.0 (Float)
|
318
|
+
#
|
319
|
+
# class Special
|
320
|
+
# def [](f)
|
321
|
+
# (f>3.5 && f<4) ? true : false
|
322
|
+
# end
|
323
|
+
# end
|
324
|
+
# sp = Special.new
|
325
|
+
# (3..4).bsearch{ |i| sp[i]} # => nil
|
326
|
+
# (3...4).bsearch{ |i| sp[i]} # => nil
|
327
|
+
# (3.0...4).bsearch{|i| sp[i]} # => 3.5000000000000004
|
328
|
+
# (3...4.0).bsearch{|i| sp[i]} # => 3.5000000000000004
|
329
|
+
# (3.3..4).bsearch{ |i| sp[i]} # => 3.5000000000000004
|
330
|
+
#
|
331
|
+
# (Rational(36,10)..5).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Rational (Ruby 2.1)
|
332
|
+
# (3..Rational(61,10)).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Fixnum (Ruby 2.1)
|
333
|
+
#
|
334
|
+
# In short, bsearch works only with Integer and/or Float (as in Ruby 2.1).
|
335
|
+
# If either of begin and end is an Float, the search is conducted in Float and the returned value will be Float, unless nil.
|
336
|
+
# If Float, it searches on the binary plane.
|
337
|
+
# If Integer, the search is conducted on the descrete Integer points only,
|
338
|
+
# and no search will be made in between the adjascent integers.
|
339
|
+
#
|
340
|
+
# Given that, RangeExtd#bsearch follows basically the same, even when exclude_begin? is true.
|
341
|
+
# If either end is Float, it searches between begin*(1+Float::EPSILON) and end.
|
342
|
+
# If both are Integer, it searches from begin+1.
|
343
|
+
# When {#exclude_begin?} is false, {RangeExtd#bsearch} is identical to {Range#bsearch}.
|
344
|
+
#
|
345
|
+
def bsearch(*rest, &bloc)
|
346
|
+
if is_none? # No need of null?(), supposedly!
|
347
|
+
raise TypeError, "can't do binary search for NONE range"
|
348
|
+
end
|
349
|
+
|
350
|
+
if @exclude_begin
|
351
|
+
if ((Float === self.begin()) ||
|
352
|
+
(Integer === self.begin()) && (Float === self.end()))
|
353
|
+
#NOTE: Range#bsearch accepts Infinity, whether it makes sense or not.
|
354
|
+
# if Infinity::FLOAT_INFINITY == self.begin()
|
355
|
+
# raise TypeError, "can't do binary search from -Infinity"
|
356
|
+
# else
|
357
|
+
Range.new(self.begin()*(Float::EPSILON+1.0), self.end, exclude_end?).send(__method__, *rest, &bloc)
|
358
|
+
# @note Technically, if begin is Rational, there is no strong reason it should not work.
|
359
|
+
# However Range#bsearch does not accept Rational (at Ruby 2.1), hence this code.
|
360
|
+
# Users should give a RangeExtd with begin being Rational.to_f in that case.
|
361
|
+
# end
|
362
|
+
elsif (defined? self.begin().succ) # Both non-Float
|
363
|
+
Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest, &bloc) # In practice it will not raise an Exception, only when both are Integer.
|
364
|
+
else
|
365
|
+
@rangepart.send(__method__, *rest, &bloc) # It will raise an exception anyway! Such as, (Rational..Rational)
|
366
|
+
end
|
367
|
+
else
|
368
|
+
@rangepart.send(__method__, *rest, &bloc)
|
369
|
+
end
|
370
|
+
end # def bsearch(*rest, &bloc)
|
371
|
+
|
372
|
+
|
373
|
+
# See {#include?} or {#===}, and {Range#cover?}
|
374
|
+
def cover?(i)
|
375
|
+
# ("a".."z").cover?("cc") # => true
|
376
|
+
# (?B..?z).cover?('dd') # => true (though 'dd'.succ would never reach ?z)
|
377
|
+
|
378
|
+
return false if is_none? # No need of null?(), supposedly!
|
379
|
+
|
380
|
+
if @exclude_begin
|
381
|
+
if self.begin == i
|
382
|
+
false
|
383
|
+
else
|
384
|
+
@rangepart.send(__method__, i)
|
385
|
+
end
|
386
|
+
else
|
387
|
+
@rangepart.send(__method__, i)
|
388
|
+
end
|
389
|
+
end # def cover?(i)
|
390
|
+
|
391
|
+
|
392
|
+
# @raise [TypeError] If {#exclude_begin?} is true, and {#begin}() or {#rangepart} does not have a method of {#succ}, then even if no block is given, this method raises TypeError straightaway.
|
393
|
+
# @return [RangeExtd] self
|
394
|
+
# @return [Enumerator] if block is not given.
|
395
|
+
#
|
396
|
+
def each(*rest, &bloc)
|
397
|
+
# (1...3.5).each{|i|print i} # => '123' to STDOUT
|
398
|
+
# (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
|
399
|
+
# (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
|
400
|
+
# Note: If the block is not given and if @exclude_begin is true, the self in the returned Enumerator is not the same as self here.
|
401
|
+
if @exclude_begin # including RangeExtd::NONE
|
402
|
+
if defined? self.begin.succ
|
403
|
+
ret = Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc)
|
404
|
+
if block_given?
|
405
|
+
self
|
406
|
+
else
|
407
|
+
ret
|
408
|
+
end
|
409
|
+
elsif is_none?
|
410
|
+
raise TypeError, "can't iterate for NONE range"
|
411
|
+
else
|
412
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
413
|
+
end
|
414
|
+
else
|
415
|
+
@rangepart.send(__method__, *rest, &bloc)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
|
420
|
+
# Like {Range#last}, if no argument is given, it behaves like {#begin()}, that is, it returns the initial value, regardless of {#exclude_begin?}.
|
421
|
+
# However, if an argument is given (nb., acceptable since Ruby 1.9) when {#exclude_begin?} is true, it returns the array that starts from {#begin}().succ().
|
422
|
+
# @raise [TypeError] if the argument (Numeric) is given and if {#exclude_begin?} is true, yet if {#begin}().succ is not defined, or yet if {#none}?
|
423
|
+
# @param [Numeric] Optional. Must be non-negative. Consult {Range#first} for detail.
|
424
|
+
#
|
425
|
+
def first(*rest)
|
426
|
+
# (1...3.1).last # => 3.1
|
427
|
+
# (1...3.1).last(1) # => [3]
|
428
|
+
if ! @exclude_begin # hence, not NONE.
|
429
|
+
@rangepart.first(*rest)
|
430
|
+
else
|
431
|
+
case rest.size
|
432
|
+
when 0
|
433
|
+
self.begin
|
434
|
+
when 1
|
435
|
+
if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8.
|
436
|
+
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)."
|
437
|
+
end
|
438
|
+
|
439
|
+
## Check the argument.
|
440
|
+
Array.new[ rest[0] ] # Check Type of rest[0] (if invalid, it should raise TypeError)
|
441
|
+
|
442
|
+
begin
|
443
|
+
if rest[0] < 0
|
444
|
+
raise ArgumentError, "negative array size (or size too big)"
|
445
|
+
end
|
446
|
+
rescue NoMethodError
|
447
|
+
# Should not happen, but just to play safe.
|
448
|
+
end
|
449
|
+
|
450
|
+
## Main
|
451
|
+
if ! defined? self.begin.succ
|
452
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
453
|
+
end
|
454
|
+
|
455
|
+
Range.new(self.begin.succ, self.end, exclude_end?).send(__method__, *rest)
|
456
|
+
else
|
457
|
+
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)"
|
458
|
+
end
|
459
|
+
end # if ! @exclude_begin
|
460
|
+
end # def first(*rest)
|
461
|
+
|
462
|
+
|
463
|
+
# When {#exclude_begin?} is true, the returned value is not strictly guaranteed to be unique, though in pracrtice it is most likely to be so.
|
464
|
+
#
|
465
|
+
def hash(*rest)
|
466
|
+
if @exclude_begin
|
467
|
+
@rangepart.send(__method__, *rest) - 1
|
468
|
+
else
|
469
|
+
@rangepart.send(__method__, *rest)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
|
474
|
+
# Return eg., '("a"<..."c")', '("a"<.."c")', if {#exclude_begin?} is true,
|
475
|
+
# or else, identical to those for {Range}.
|
476
|
+
# @return [String]
|
477
|
+
def inspect
|
478
|
+
re_inspect_core(__method__)
|
479
|
+
end
|
480
|
+
|
481
|
+
# Return eg., "(a<...c)", "(a<..c)", if {#exclude_begin?} is true,
|
482
|
+
# or else, identical to those for {Range}.
|
483
|
+
# @return [String]
|
484
|
+
def to_s
|
485
|
+
re_inspect_core(__method__)
|
486
|
+
end
|
487
|
+
|
488
|
+
|
489
|
+
# See {Range#last}.
|
490
|
+
# If either (let alone both) side of the edge is Infinity, you can not give
|
491
|
+
# an argument in practice, the number of the members of the returned array.
|
492
|
+
#
|
493
|
+
# @raise [TypeError] If self.begin.succ is not defined, or if either side is Infinity.
|
494
|
+
# @return [Object] if no argument is given, equivalent to {#end}. Otherwise Array.
|
495
|
+
def last(*rest)
|
496
|
+
return nil if null?
|
497
|
+
nSize = rest.size
|
498
|
+
case nSize
|
499
|
+
when 0
|
500
|
+
self.end
|
501
|
+
when 1
|
502
|
+
if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8.
|
503
|
+
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)."
|
504
|
+
end
|
505
|
+
|
506
|
+
if defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY
|
507
|
+
raise TypeError, "can't iterate from "+self.begin.to_s
|
508
|
+
elsif defined?(self.end.infinity?) && self.end.infinity? || self.end == Infinity::FLOAT_INFINITY
|
509
|
+
raise TypeError, "can't get elements to "+self.end.to_s
|
510
|
+
elsif ! defined? self.begin.succ
|
511
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
512
|
+
else
|
513
|
+
@rangepart.send(__method__, *rest)
|
514
|
+
end
|
515
|
+
else
|
516
|
+
raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)"
|
517
|
+
end
|
518
|
+
end # def last(*rest)
|
519
|
+
|
520
|
+
|
521
|
+
# See {#first} for the definition when {#exclude_begin?} is true.
|
522
|
+
#
|
523
|
+
def min(*rest, &bloc)
|
524
|
+
re_min_max_core(__method__, *rest, &bloc)
|
525
|
+
end
|
526
|
+
|
527
|
+
# See {#first} for the definition when {#exclude_begin?} is true.
|
528
|
+
#
|
529
|
+
def min_by(*rest, &bloc)
|
530
|
+
re_min_max_core(__method__, *rest, &bloc)
|
531
|
+
end
|
532
|
+
|
533
|
+
|
534
|
+
# See {#first} for the definition when {#exclude_begin?} is true.
|
535
|
+
#
|
536
|
+
def minmax(*rest, &bloc)
|
537
|
+
# (0...3.5).minmax # => [0, 3]
|
538
|
+
# (1.3...5).minmax # => TypeError: can't iterate from Float
|
539
|
+
# Note that max() for the same Range raises an exception.
|
540
|
+
# In that sense, it is inconsistent!
|
541
|
+
re_min_max_core(__method__, *rest, &bloc)
|
542
|
+
end
|
543
|
+
|
544
|
+
# See {#first} for the definition when {#exclude_begin?} is true.
|
545
|
+
#
|
546
|
+
def minmax_by(*rest, &bloc)
|
547
|
+
# (0...3.5).minmax # => [0, 3]
|
548
|
+
# Note that max() for the same Range raises an exception.
|
549
|
+
# In that sense, it is inconsistent!
|
550
|
+
re_min_max_core(__method__, *rest, &bloc)
|
551
|
+
end
|
552
|
+
|
553
|
+
|
554
|
+
# See {#first} for the definition when {#exclude_begin?} is true.
|
555
|
+
#
|
556
|
+
def max(*rest, &bloc)
|
557
|
+
re_min_max_core(__method__, *rest, &bloc)
|
558
|
+
end
|
559
|
+
|
560
|
+
# See {#first} for the definition when {#exclude_begin?} is true.
|
561
|
+
#
|
562
|
+
def max_by(*rest, &bloc)
|
563
|
+
re_min_max_core(__method__, *rest, &bloc)
|
564
|
+
end
|
565
|
+
|
566
|
+
|
567
|
+
|
568
|
+
# See {#first} for the definition when {#exclude_begin?} is true.
|
569
|
+
# @see http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/49797 [ruby-list:49797] from matz for how {Range#size} behaves (in Japanese).
|
570
|
+
#
|
571
|
+
# @return [Integer] or nil if the range is non-Numeric. 0 if RangeExtd::NONE.
|
572
|
+
def size(*rest)
|
573
|
+
# (1..5).size # => 5
|
574
|
+
# (1...5).size # => 4
|
575
|
+
# (0.8...5).size # => 5 # Why???
|
576
|
+
# (1.2...5).size # => 4 # Why???
|
577
|
+
# (1.2..5).size # => 4 # Why???
|
578
|
+
# (Rational(3,2)...5).size # => 3
|
579
|
+
# (1.5...5).size # => 4 # Why not 3??
|
580
|
+
# (1.5...4.9).size # => 4 # Why not 3??
|
581
|
+
# (1.5...4.5).size # => 3
|
582
|
+
# (0...Float::INFINITY).size # => Infinity
|
583
|
+
|
584
|
+
if is_none? # No need of null?(), supposedly!
|
585
|
+
return 0
|
586
|
+
|
587
|
+
### (Infinity..Infinity) => 0 (as in Ruby 2.1)
|
588
|
+
# elsif self.begin().infinity? || self.end().infinity?
|
589
|
+
# return Infinity::FLOAT_INFINITY
|
590
|
+
|
591
|
+
# Checking Infinity.
|
592
|
+
# Note (Infinity..Infinity) => 0 (Range as in Ruby 2.1)
|
593
|
+
# however,
|
594
|
+
elsif (defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY) ||
|
595
|
+
(defined?(self.end.infinity?) && self.end.infinity? || self.end == Infinity::FLOAT_INFINITY)
|
596
|
+
if self.begin == self.end
|
597
|
+
return 0
|
598
|
+
else
|
599
|
+
return Infinity::FLOAT_INFINITY
|
600
|
+
end
|
601
|
+
|
602
|
+
elsif @exclude_begin
|
603
|
+
|
604
|
+
begin
|
605
|
+
1.0 + self.begin()
|
606
|
+
# Numeric
|
607
|
+
Range.new(self.begin()+1, self.end, exclude_end?).send(__method__, *rest) # Swap the order of '+' from the above, so that Integer/Rational is calculated as it is.
|
608
|
+
rescue TypeError
|
609
|
+
# Non-Numeric
|
610
|
+
if defined? self.begin().succ
|
611
|
+
Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest) # => nil in Ruby 2.1
|
612
|
+
else
|
613
|
+
nil # See the line above.
|
614
|
+
# raise TypeError, "can't iterate from "+self.begin.class.name
|
615
|
+
end
|
616
|
+
|
617
|
+
end
|
618
|
+
|
619
|
+
else
|
620
|
+
@rangepart.send(__method__, *rest)
|
621
|
+
end
|
622
|
+
end # def size
|
623
|
+
|
624
|
+
|
625
|
+
# See {#each}.
|
626
|
+
# @raise [TypeError] If {#exclude_begin?} is true, and {#begin}() does not have the method {#succ}, then even if no block is given, this method raises TypeError straightaway.
|
627
|
+
# @return [RangeExtd] self
|
628
|
+
# @return [Enumerator] if block is not given.
|
629
|
+
#
|
630
|
+
def step(*rest, &bloc)
|
631
|
+
# (1...3.5).each{|i|print i} # => '123' to STDOUT
|
632
|
+
# (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
|
633
|
+
# (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
|
634
|
+
# Note: If the block is not given and if exclude_begin?() is true, the self in the returned Enumerator is not the same as self here.
|
635
|
+
|
636
|
+
if @exclude_begin # including RangeExtd::NONE
|
637
|
+
if defined? self.begin.succ
|
638
|
+
ret = Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc)
|
639
|
+
if block_given?
|
640
|
+
self
|
641
|
+
else
|
642
|
+
ret
|
643
|
+
end
|
644
|
+
elsif is_none? # No need of null?(), supposedly!
|
645
|
+
raise TypeError, "can't iterate for NONE range"
|
646
|
+
else
|
647
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
648
|
+
end
|
649
|
+
else
|
650
|
+
@rangepart.send(__method__, *rest, &bloc)
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
|
655
|
+
# Private class method to evaluate the arguments.
|
656
|
+
def self._get_init_args(*inar)
|
657
|
+
nMin = 1; nMax = 4
|
658
|
+
if inar.size < nMin || nMax < inar.size
|
659
|
+
raise ArgumentError, "wrong number of arguments (#{inar.size} for #{nMin}..#{nMax})"
|
660
|
+
end
|
661
|
+
|
662
|
+
hsFlag = { :prm1st => nil }
|
663
|
+
if defined? inar[0].exclude_begin?
|
664
|
+
hsFlag[:prm1st] = :rangeextd
|
665
|
+
elsif defined? inar[0].exclude_end?
|
666
|
+
hsFlag[:prm1st] = :range
|
667
|
+
else
|
668
|
+
hsFlag[:prm1st] = :object
|
669
|
+
end
|
670
|
+
|
671
|
+
case hsFlag[:prm1st]
|
672
|
+
when :rangeextd, :range
|
673
|
+
if inar.size > 1
|
674
|
+
exclude_begin = (true ^! inar[1])
|
675
|
+
elsif :rangeextd == hsFlag[:prm1st]
|
676
|
+
exclude_begin = inar[0].exclude_begin?
|
677
|
+
else
|
678
|
+
exclude_begin = false
|
679
|
+
end
|
680
|
+
|
681
|
+
if inar.size > 2
|
682
|
+
exclude_end = (true ^! inar[2])
|
683
|
+
else
|
684
|
+
exclude_end = inar[0].exclude_end?
|
685
|
+
end
|
686
|
+
|
687
|
+
if inar.size > 3
|
688
|
+
nMin = 1; nMax = 3
|
689
|
+
raise ArgumentError, "wrong number of arguments (#{inar.size} for #{nMin}..#{nMax})"
|
690
|
+
end
|
691
|
+
|
692
|
+
arRet = [inar[0].begin, inar[0].end, exclude_end, exclude_begin]
|
693
|
+
# @rangepart = Range.new(inar[0].begin, inar[0].end, exclude_end)
|
694
|
+
|
695
|
+
when :object
|
696
|
+
if inar.size > 2
|
697
|
+
exclude_begin = (true ^! inar[2])
|
698
|
+
else
|
699
|
+
exclude_begin = false
|
700
|
+
end
|
701
|
+
|
702
|
+
if inar.size > 3
|
703
|
+
exclude_end = (true ^! inar[3])
|
704
|
+
else
|
705
|
+
exclude_end = false
|
706
|
+
end
|
707
|
+
|
708
|
+
arRet = [inar[0], inar[1], exclude_end, exclude_begin]
|
709
|
+
# @rangepart = Range.new(inar[0], inar[1], exclude_end)
|
710
|
+
|
711
|
+
else
|
712
|
+
raise # (for coding safety)
|
713
|
+
end # case hsFlag[:prm1st]
|
714
|
+
|
715
|
+
arRet
|
716
|
+
|
717
|
+
end # def self._get_init_args(*inar)
|
718
|
+
|
719
|
+
private_class_method :_get_init_args # From Ruby 1.8.7 (?)
|
720
|
+
|
721
|
+
|
722
|
+
# Returns true if the range to be constructed (or given) is valid,
|
723
|
+
# as a range, accepted in {RangeExtd}.
|
724
|
+
#
|
725
|
+
# This routine is also impremented as a method in {Range},
|
726
|
+
# and accordingly its sub-classes.
|
727
|
+
#
|
728
|
+
# This routine is called from {RangeExtd#new}, hence
|
729
|
+
# for any instance of {RangeExtd} class, its {#valid?} returns true.
|
730
|
+
#
|
731
|
+
# What is valid is defined as follows:
|
732
|
+
#
|
733
|
+
# 1. Both {#begin} and {#end} elements must be Comparable to each other,
|
734
|
+
# and the comparison results must be consistent betwen the two.
|
735
|
+
# The sole exception is {RangeExtd::NONE}, which is valid.
|
736
|
+
# For example, (nil..nil) is NOT valid (nb., it raised Exception in Ruby 1.8).
|
737
|
+
# 2. {#begin} must be smaller than or equal to {#end},
|
738
|
+
# that is, ({#begin} <=> {#end}) must be either -1 or 0.
|
739
|
+
# 3. If {#begin} is equal to {#end}, namely, ({#begin} <=> {#end}) == 0,
|
740
|
+
# the exclude status of the both ends must agree.
|
741
|
+
# That is, if the {#begin} is excluded, {#end} must be also excluded,
|
742
|
+
# and vice versa.
|
743
|
+
# For example, (1...1) is NOT valid for that reason,
|
744
|
+
# because any built-in Range object has the exclude status
|
745
|
+
# of false (namely, inclusive) for {#begin}.
|
746
|
+
#
|
747
|
+
# === Examples
|
748
|
+
#
|
749
|
+
# RangeExtd.valid?(nil..nil) # => false
|
750
|
+
# RangeExtd.valid?(nil...nil) # => false
|
751
|
+
# RangeExtd.valid?(0..0) # => true
|
752
|
+
# RangeExtd.valid?(0...0) # => false
|
753
|
+
# RangeExtd.valid?(0..0, true) # => false
|
754
|
+
# RangeExtd.valid?(0...0, true) # => true
|
755
|
+
# RangeExtd.valid?(2..-1) # => false
|
756
|
+
# RangeExtd.valid?(RangeExtd::NONE) # => true
|
757
|
+
# RangeExtd.valid?(RangeExtd::EVERYTHING) # => true
|
758
|
+
# RangeExtd.valid?(3..Float::INFINITY) # => true
|
759
|
+
# RangeExtd.valid?(3..Float::INFINITY, true) # => true
|
760
|
+
# RangeExtd.valid?(RangeExtd::Infinity::NEGATIVE..?d) # => true
|
761
|
+
# RangeExtd.valid?(RangeExtd::Infinity::NEGATIVE..?d, true) # => true
|
762
|
+
#
|
763
|
+
# Note the last example may change in the future release.
|
764
|
+
#
|
765
|
+
# @overload new(range, [exclude_begin=false, [exclude_end=false]])
|
766
|
+
# @param [Object] range Instance of Range or its subclasses, including RangeExtd
|
767
|
+
# @param exclude_begin [Object] true or false(Default) or any
|
768
|
+
# @param exclude_end [Object] true or false(Default) or any.
|
769
|
+
#
|
770
|
+
# @overload new(obj_begin, obj_end, [exclude_begin=false, [exclude_end=false]])
|
771
|
+
# @param obj_begin [Object] Any object that is {Comparable} with end
|
772
|
+
# @param obj_end [Object] Any object that is Comparable with begin
|
773
|
+
# @param exclude_begin [Object] true or false(Default) or any
|
774
|
+
# @param exclude_end [Object] true or false(Default) or any.
|
775
|
+
#
|
776
|
+
def self.valid?(*inar)
|
777
|
+
(vbeg, vend, exc_beg, exc_end) = _get_init_args(*inar)
|
778
|
+
|
779
|
+
if (vbeg.nil? && vend.nil? && exc_beg && exc_end)
|
780
|
+
return true # equivalent to self.is_none?
|
781
|
+
# But this routine is called from new(), hence is_none?() is not used.
|
782
|
+
end
|
783
|
+
|
784
|
+
begin
|
785
|
+
t = (vbeg <=> vend)
|
786
|
+
begin
|
787
|
+
return false if t != -1*(vend <=> vbeg) # false if not commutative, or possibly exception (such as -1*nil).
|
788
|
+
rescue NoMethodError, TypeError
|
789
|
+
if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) ||
|
790
|
+
(Float === vbeg && defined?(vend.infinity?) && vend.infinity?)
|
791
|
+
warn "Float::INFINITY is not comparable with other Infinity."
|
792
|
+
end
|
793
|
+
return false # return
|
794
|
+
end
|
795
|
+
rescue # NoMethodError
|
796
|
+
false # return
|
797
|
+
else
|
798
|
+
case t
|
799
|
+
when -1
|
800
|
+
true
|
801
|
+
when 0
|
802
|
+
if defined?(vbeg.<=) && defined?(vend.<=) # Comparable?
|
803
|
+
((true && exc_beg) ^! exc_end) # True if single value or empty, false if eg, (1...1)
|
804
|
+
else
|
805
|
+
false # Not Comparable
|
806
|
+
end
|
807
|
+
when 1
|
808
|
+
false
|
809
|
+
else
|
810
|
+
if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) ||
|
811
|
+
(Float === vbeg && defined?(vend.infinity?) && vend.infinity?)
|
812
|
+
warn "Float::INFINITY is not comparable with other Infinity."
|
813
|
+
end
|
814
|
+
false # Not Comparable.
|
815
|
+
end # case t
|
816
|
+
# All statements of return above.
|
817
|
+
end
|
818
|
+
end # def valid?
|
819
|
+
|
820
|
+
|
821
|
+
private
|
822
|
+
|
823
|
+
# Core routine for {#inspect} and {#to_s}
|
824
|
+
# @param r [Object] to compare.
|
825
|
+
# @param method [Symbol] of the method name.
|
826
|
+
def re_inspect_core(method)
|
827
|
+
if @exclude_end
|
828
|
+
midStr = "..."
|
829
|
+
else
|
830
|
+
midStr = ".."
|
831
|
+
end
|
832
|
+
if is_none?
|
833
|
+
'Null' + '<' + midStr + 'Null' # Null<...Null
|
834
|
+
elsif @exclude_begin
|
835
|
+
self.begin.send(method) + '<' + midStr + self.end.send(method)
|
836
|
+
else
|
837
|
+
self.begin.send(method) + midStr + self.end.send(method)
|
838
|
+
end
|
839
|
+
end # def re_inspect_core(method)
|
840
|
+
|
841
|
+
|
842
|
+
# Core routine for {#===} and {#eql?}
|
843
|
+
# @param r [Object] to compare.
|
844
|
+
# @param method [Symbol] of the method name.
|
845
|
+
def re_equal_core(r, method)
|
846
|
+
if defined? r.empty?
|
847
|
+
is_r_empty = r.empty?
|
848
|
+
else
|
849
|
+
return false # Not Range family.
|
850
|
+
end
|
851
|
+
if ! defined? r.exclude_end?
|
852
|
+
false # Not Range family.
|
853
|
+
elsif is_none? && is_r_empty # RangeExtd::NONE
|
854
|
+
true
|
855
|
+
elsif empty? && defined?(r.is_none?) && r.is_none? # r is RangeExtd::NONE
|
856
|
+
true
|
857
|
+
elsif empty? && is_r_empty
|
858
|
+
if method == :eql?
|
859
|
+
# More strict
|
860
|
+
if self.begin().class == r.begin().class
|
861
|
+
true # (1<...1).eql? (2<...2) # => true (Fixnum <-> Fixnum) Yes!
|
862
|
+
else
|
863
|
+
false # (1.0<...1.0).eql? (2<...2) # => false (Float <-> Fixnum) No!
|
864
|
+
end
|
865
|
+
else
|
866
|
+
(self.begin.class.ancestors - self.begin.class.included_modules - [Object, BasicObject]).each do |ec|
|
867
|
+
if ec === r.begin
|
868
|
+
return true # (1.0<...1.0) == (2<...2) # (Float<Numeric <-> Fixnum<Numeric) Yes!
|
869
|
+
end
|
870
|
+
end
|
871
|
+
false # (?a...?a) != (2<...2) # (String <-> Numeric) No!
|
872
|
+
end
|
873
|
+
|
874
|
+
elsif defined? r.exclude_begin?
|
875
|
+
(self.exclude_begin? ^! r.exclude_begin?) &&
|
876
|
+
(self.exclude_end? ^! r.exclude_end?) &&
|
877
|
+
(self.begin.send(method, r.begin)) &&
|
878
|
+
(self.end.send( method, r.end))
|
879
|
+
# (self.begin == r.begin) &&
|
880
|
+
# (self.end == r.end)
|
881
|
+
else
|
882
|
+
# r is Range
|
883
|
+
if self.exclude_begin?
|
884
|
+
false
|
885
|
+
else
|
886
|
+
@rangepart.send(method, r) # Comparison as two Range-s.
|
887
|
+
end
|
888
|
+
end
|
889
|
+
end # def re_equal_core(r, method)
|
890
|
+
|
891
|
+
# Core routine for {#min}, {#max}, {#minmax} etc.
|
892
|
+
# @param method [Symbol] of the method name.
|
893
|
+
# @param rest [Object]
|
894
|
+
def re_min_max_core(method, *rest, &bloc)
|
895
|
+
# (1...3.5).max # => TypeError: cannot exclude non Integer end value
|
896
|
+
if is_none?
|
897
|
+
raise TypeError, "no meaningful range."
|
898
|
+
elsif @exclude_begin
|
899
|
+
if defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY
|
900
|
+
raise TypeError, "can't exclude "+self.begin.to_s
|
901
|
+
elsif ! defined? self.begin.succ
|
902
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
903
|
+
else
|
904
|
+
Range.new(self.begin.succ, self.end, exclude_end?).send(method, *rest, &bloc)
|
905
|
+
end
|
906
|
+
else
|
907
|
+
@rangepart.send(method, *rest)
|
908
|
+
end
|
909
|
+
end # def re_min_max_core(method, *rest, &bloc)
|
910
|
+
|
911
|
+
end # class RangeExtd < Range
|
912
|
+
|
913
|
+
|
914
|
+
#= Class Range
|
915
|
+
#
|
916
|
+
#== Summary
|
917
|
+
#
|
918
|
+
# Modifies {#==}, {#eql?} and add methods of
|
919
|
+
# {#valid?}, {#empty?}, {#null?}, {#is_none?} and {#is_everything?}.
|
920
|
+
#
|
921
|
+
class Range
|
922
|
+
|
923
|
+
alias :equal_prerangeextd? :== if ! self.method_defined?(:equal_prerangeextd?) # No overwriting.
|
924
|
+
|
925
|
+
# It is extended to handle {RangeExtd} objects.
|
926
|
+
# For each element, that is, {#begin} and {#end},
|
927
|
+
# this uses their method of ==(). See {#eql?}.
|
928
|
+
#
|
929
|
+
# As long as the comparison is limited within {Range} objects,
|
930
|
+
# the returned value of this method has unchanged.
|
931
|
+
#
|
932
|
+
# A note of caution is, some ranges which the built-in Range accepts,
|
933
|
+
# are now regarded as NOT valid, such as, (1...1) and (nil..nil)
|
934
|
+
# (the latter was not permitted in Ruby 1.8), though you can still
|
935
|
+
# use them;
|
936
|
+
# (1...1).valid? # => false
|
937
|
+
# On the other hand, {RangeExtd} class does not accept or create
|
938
|
+
# any invalid range; for any {RangeExtd} object, {RangeExtd#valid}
|
939
|
+
# returns true. For example, there is no {RangeExtd} object
|
940
|
+
# that is expressed as (1...1) (See {#valid} for detail).
|
941
|
+
#
|
942
|
+
# For that reason, when those non-valid Range objects are compared
|
943
|
+
# with a {RangeExtd} object, the returned value may not be what
|
944
|
+
# you would expect. For example,
|
945
|
+
# (1...1) == RangeExtd(1, 1, true, true) # => false.
|
946
|
+
# The former is an invalid range, while the latter is
|
947
|
+
# a rigidly-defined empty range.
|
948
|
+
#
|
949
|
+
# Consult {#valid} and {RangeExtd#==} for more detail.
|
950
|
+
def ==(r)
|
951
|
+
equal_core(r, :==, :equal_prerangeextd?)
|
952
|
+
end
|
953
|
+
|
954
|
+
|
955
|
+
alias :eql_prerangeextd? :eql? if ! self.method_defined?(:eql_prerangeextd?) # No overwriting.
|
956
|
+
|
957
|
+
# Same as {#==}, but the comparison is made with eql?() method.
|
958
|
+
def eql?(r)
|
959
|
+
equal_core(r, :eql?, :eql_prerangeextd?)
|
960
|
+
end
|
961
|
+
|
962
|
+
|
963
|
+
# Returns true if self is valid as a comparable range.
|
964
|
+
#
|
965
|
+
# See {RangeExtd.valid?} for the definition of what is valid
|
966
|
+
# and more examples.
|
967
|
+
#
|
968
|
+
# === Examples
|
969
|
+
#
|
970
|
+
# (nil..nil).valid? # => false
|
971
|
+
# (0..0).valid? # => true
|
972
|
+
# (0...0).valid? # => false
|
973
|
+
# (2..-1).valid? # => false
|
974
|
+
# RangeExtd(0...0, true) # => true
|
975
|
+
# (3..Float::INFINITY).valid? # => true
|
976
|
+
# RangeExtd::NONE.valid? # => true
|
977
|
+
# RangeExtd::EVERYTHING.valid? # => true
|
978
|
+
#
|
979
|
+
# See {#empty?} and {#null?}, too.
|
980
|
+
#
|
981
|
+
# @note By definition, all the {RangeExtd} instances are valid,
|
982
|
+
# because {RangeExtd#new} checks the validity.
|
983
|
+
def valid?
|
984
|
+
RangeExtd.valid?(self)
|
985
|
+
end # def valid?
|
986
|
+
|
987
|
+
|
988
|
+
# Returns true if self is empty.
|
989
|
+
# Returns nil if self is not valid (nb., any RangeExtd instance is valid.)
|
990
|
+
# Otherwise false.
|
991
|
+
#
|
992
|
+
# The definition of what is empty is as follow.
|
993
|
+
#
|
994
|
+
# 1. the range must be valid: {#valid?} => true
|
995
|
+
# 2. if the range id discrete, that is, {#begin} has
|
996
|
+
# {#succ} method, there must be no member within the range:
|
997
|
+
# {#to_a}.empty? => true
|
998
|
+
# 3. if the range is continuous, that is, {#begin} does not have
|
999
|
+
# {#succ} method, {#begin} and {#end} must be equal
|
1000
|
+
# (({#begin} <=> {#end}) => 0) and both the boundaries must
|
1001
|
+
# be excluded: ({#exclude_begin?} && {#exclude_end?}) => true.
|
1002
|
+
# Note that ranges with equal {#begin} and {#end} with
|
1003
|
+
# inconsistent two exclude status are not valid, and the built-in
|
1004
|
+
# Range always has the {#begin}-exclude status of false.
|
1005
|
+
#
|
1006
|
+
# In these conditions, none of Range instance would return true in {#empty}.
|
1007
|
+
#
|
1008
|
+
# === Examples
|
1009
|
+
#
|
1010
|
+
# (nil..nil).empty? # => nil
|
1011
|
+
# (1...1).empty? # => nil
|
1012
|
+
# (1..1).empty? # => false
|
1013
|
+
# RangeExtd(1...1, true).empty? # => true
|
1014
|
+
# RangeExtd(1...2, true).empty? # => true
|
1015
|
+
# RangeExtd(1.0...2, true).empty? # => false
|
1016
|
+
# RangeExtd(?a...?b, true).empty? # => true
|
1017
|
+
# RangeExtd::NONE.empty? # => true
|
1018
|
+
#
|
1019
|
+
# @note to check whether it is either empty or invalid, use {#null?}.
|
1020
|
+
# See {#valid?} and {RangeExtd.valid}, too.
|
1021
|
+
def empty?
|
1022
|
+
# This is basically for the sake of sub-classes, as any built-in Range instance
|
1023
|
+
# always returns either nil or false.
|
1024
|
+
|
1025
|
+
if !valid?
|
1026
|
+
return nil
|
1027
|
+
elsif defined?(self.is_none?) && self.is_none?
|
1028
|
+
return true
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
t = (self.begin() <=> self.end())
|
1032
|
+
case t
|
1033
|
+
when -1
|
1034
|
+
if (defined?(self.exclude_begin?)) &&
|
1035
|
+
exclude_begin? &&
|
1036
|
+
exclude_end? &&
|
1037
|
+
defined?(self.begin().succ) &&
|
1038
|
+
(self.begin().succ == self.end())
|
1039
|
+
true # e.g., ("a"<..."b")
|
1040
|
+
else
|
1041
|
+
false
|
1042
|
+
end
|
1043
|
+
when 0
|
1044
|
+
if defined?(self.boundary) && self.boundary.nil? # for RangeOpen
|
1045
|
+
# RangeExtd::NONE or RangeExtd::All
|
1046
|
+
if self.exclude_end?
|
1047
|
+
true # RangeOpen::NONE
|
1048
|
+
else
|
1049
|
+
false # RangeOpen::EVERYTHING
|
1050
|
+
end
|
1051
|
+
else
|
1052
|
+
if defined?(self.exclude_begin?)
|
1053
|
+
t2 = self.exclude_begin?
|
1054
|
+
else
|
1055
|
+
t2 = false # == return false
|
1056
|
+
end
|
1057
|
+
(t2 && exclude_end?)
|
1058
|
+
end
|
1059
|
+
when 1
|
1060
|
+
nil # redundant, as it should not be valid in the first place.
|
1061
|
+
else
|
1062
|
+
nil # redundant, as it should not be valid in the first place.
|
1063
|
+
end
|
1064
|
+
end # def empty?
|
1065
|
+
|
1066
|
+
|
1067
|
+
# Returns true if it is either empty or invalid. false otherwise.
|
1068
|
+
# See {#empty?} and {#valid?}.
|
1069
|
+
def null?
|
1070
|
+
(! valid?) || empty?
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
# @return [FalseClass]
|
1074
|
+
def is_none?
|
1075
|
+
false
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
# @return [FalseClass]
|
1079
|
+
def is_everything?
|
1080
|
+
false
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
############## pravate methods of Range ##############
|
1084
|
+
|
1085
|
+
private
|
1086
|
+
|
1087
|
+
# True if obj is Comparable.
|
1088
|
+
def is_comparable?(obj)
|
1089
|
+
if defined?(obj.<=) # Comparable?
|
1090
|
+
true
|
1091
|
+
else
|
1092
|
+
false
|
1093
|
+
end
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
# @param r [Object] to compare.
|
1097
|
+
# @param method [Symbol] of the method name.
|
1098
|
+
# @param method_pre [Symbol] of the backed-up original method name.
|
1099
|
+
def equal_core(r, method, method_pre)
|
1100
|
+
if (! defined? r.exclude_end?) || (! defined? r.is_none?) || (! defined? r.empty?)
|
1101
|
+
false # Not Range family.
|
1102
|
+
# elsif empty? && defined?(r.is_none?) && r.is_none? # r is RangeExtd::NONE
|
1103
|
+
# true
|
1104
|
+
# elsif empty? && r.empty?
|
1105
|
+
# # None of built-in Range class object can be #empty?==true - This is for sub-class.
|
1106
|
+
#
|
1107
|
+
elsif defined? r.exclude_begin?
|
1108
|
+
# Either RangeExtd or RangeOpen object.
|
1109
|
+
if r.exclude_begin?
|
1110
|
+
false # self(Range) has always the inclusive begin.
|
1111
|
+
else
|
1112
|
+
# It could do with a single line,
|
1113
|
+
# self.begin.send(method_pre, r)
|
1114
|
+
# if this was for RangeExtd===r, but not for RangeOpen.
|
1115
|
+
if (self.exclude_end? ^ r.exclude_end?)
|
1116
|
+
false
|
1117
|
+
elsif (self.begin.send(method, r.begin) && self.end.send(method, r.end))
|
1118
|
+
true
|
1119
|
+
else
|
1120
|
+
false
|
1121
|
+
end
|
1122
|
+
end
|
1123
|
+
else
|
1124
|
+
self.send(method_pre, r) # r is Range.
|
1125
|
+
end
|
1126
|
+
end # def equal_core(r, method, method_pre)
|
1127
|
+
|
1128
|
+
end # class Range
|
1129
|
+
|
1130
|
+
|
1131
|
+
# Constant-form of {#RangeExtd}.
|
1132
|
+
# {#RangeExtd(*)} is equivalent to {#RangeExtd.new}.
|
1133
|
+
#
|
1134
|
+
def RangeExtd(*rest, &b)
|
1135
|
+
RangeExtd.new(*rest, &b)
|
1136
|
+
end
|
1137
|
+
|