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.
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
9
+
@@ -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
+