range_extd 1.0 → 2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,34 +1,22 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- ## Load required files.
4
- err1st = nil
5
- req_files = %w(lib/range_extd/infinity/infinity)
3
+ ## Load required files in this library. (At the end of this file, "range_extd/range" is also required.)
4
+ req_files = %w(range_extd/infinity range_extd/nowhere range_extd/nil_class)
6
5
  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
6
+ begin
7
+ require_relative req_file
8
+ rescue LoadError
9
+ require req_file
26
10
  end
27
- end # req_files.each do |req_file|
11
+ end
28
12
 
29
- ########################################
30
- # Initial set up of 2 constants in RangeExtd.
31
- ########################################
13
+ if $DEBUG
14
+ puts "NOTE: Library full paths:"
15
+ req_files.each do |elibbase|
16
+ ar = $LOADED_FEATURES.grep(/(^|\/)#{Regexp.quote(File.basename(elibbase))}(\.rb)?$/).uniq
17
+ print elibbase+": " if ar.empty?; p ar
18
+ end
19
+ end
32
20
 
33
21
  # =Class RangeExtd
34
22
  #
@@ -39,20 +27,22 @@ end # req_files.each do |req_file|
39
27
  #
40
28
  # Extended Range class that features:
41
29
  # 1. includes exclude_begin? (to exclude the "begin" boundary),
42
- # 2. allows open-ended range (to the infinity),
30
+ # 2. allows open-ended range to the infinity (very similar to beginless/endless Range),
43
31
  # 3. defines NONE and ALL constants,
44
32
  # 4. the first self-consistent logical structure,
45
- # 5. complete backward compatibility within the built-in Range.
33
+ # 5. complete compatibility with the built-in Range.
46
34
  #
47
35
  # The instance of this class is immutable, that is, you can not
48
36
  # alter the element once an instance is generated.
49
37
  #
38
+ # This class has some constants
39
+ #
50
40
  # What is valid is checked with the class method {RangeExtd.valid?}.
51
41
  # See the document of that method for the definition.
52
42
  #
53
- # To express open-ended ranges is simple; you just use either of
54
- # the two (negative and positive, or former and later) constants
55
- # in RangeExtd::Infinity class. See the document for detail.
43
+ # This class has two constants:
44
+ # {RangeExtd::NONE} representing an empty range and
45
+ # {RangeExtd::ALL} representing the entire range, both in the abstract sense.
56
46
  #
57
47
  # @example An instance of a range of 5 to 8 with both ends being exclusive is created as
58
48
  # r = RangeExtd(5...8, true)
@@ -60,6 +50,10 @@ end # req_files.each do |req_file|
60
50
  #
61
51
  class RangeExtd < Range
62
52
 
53
+ # To conrol how the {RangeExtd} should be displayed or set (in one form).
54
+ # It can be read and reset by {RangeExtd.middle_strings} and
55
+ # {RangeExtd.middle_strings=}
56
+ # Default is +['', '', '<', '..', '.', '', '']+
63
57
  @@middle_strings = []
64
58
 
65
59
  # Error messages
@@ -92,7 +86,7 @@ class RangeExtd < Range
92
86
  # @option opts [Boolean] :exclude_end If specified, this has the highest priority, or false in default.
93
87
  #
94
88
  # @overload new(obj_begin, obj_end, [exclude_begin=false, [exclude_end=false]], opts)
95
- # @param obj_begin [Object] Any object that is {Comparable} with end
89
+ # @param obj_begin [Object] Any object that is +Comparable+ with end
96
90
  # @param obj_end [Object] Any object that is Comparable with begin
97
91
  # @param exclude_begin [Boolean] If specified, this has the lower priority, or false in default.
98
92
  # @param exclude_end [Boolean] If specified, this has the lower priority, or false in default.
@@ -100,9 +94,9 @@ class RangeExtd < Range
100
94
  # @option opts [Boolean] :exclude_end If specified, this has the higher priority, or false in default.
101
95
  #
102
96
  # @overload new(obj_begin, string_form, obj_end, [exclude_begin=false, [exclude_end=false]], opts)
103
- # @param obj_begin [Object] Any object that is {Comparable} with end
97
+ # @param obj_begin [Object] Any object that is +Comparable+ with end
104
98
  # @param string_form [Object] String form (without pre/postfix) of range expression set by {RangeExtd.middle_strings=}()
105
- # @param obj_end [Object] Any object that is Comparable with begin
99
+ # @param obj_end [Object] Any object that is +Comparable+ with begin
106
100
  # @param exclude_begin [Boolean] If specified, this has the lower priority, or false in default.
107
101
  # @param exclude_end [Boolean] If specified, this has the lower priority, or false in default.
108
102
  # @option opts [Boolean] :exclude_begin If specified, this has the higher priority, or false in default.
@@ -125,17 +119,31 @@ class RangeExtd < Range
125
119
  # @raise [ArgumentError] particularly if the range to be created is not {#valid?}.
126
120
  def initialize(*inar, **hsopt) # **k expression from Ruby 1.9?
127
121
 
122
+ # This is true only for RangeExtd::NONE,
123
+ # which is identical to +RangeExtd(nil, nil, true, true)+ without this.
124
+ @is_none = false
125
+
128
126
  if inar[4] == :Constant
129
- # Special case to create two Constants
130
- super(*inar[0..2])
127
+ # Special case to create two Constants (NONE and ALL)
131
128
  @rangepart = (inar[2] ? (inar[0]...inar[1]) : (inar[0]..inar[1]))
132
129
  @exclude_end, @exclude_begin = inar[2..3]
130
+
131
+ # In Ruby-2.7+ and hence RangeExtd Ver.2+, RangeExtd::NONE looks very similar to (nil...nil)
132
+ # except RangeExtd::NONE.@exclude_begin == true
133
+ #@is_none = (@rangepart.begin.nil? && @rangepart.end.nil? && @exclude_begin && @exclude_end)
134
+ @is_none = (@rangepart.begin.respond_to?(:nowhere?) &&
135
+ @rangepart.begin.nowhere? &&
136
+ @rangepart.end.respond_to?(:nowhere?) &&
137
+ @rangepart.end.nowhere? &&
138
+ @exclude_begin &&
139
+ @exclude_end)
140
+ raise(ArgumentError, "NONE has been already defined.") if @is_none && self.class.const_defined?(:NONE)
141
+ super(*inar[0..2])
133
142
  return
134
143
  end
135
144
 
136
- # Note: the order of exclude_begin? and end? is reversed from the input!
137
- arout = RangeExtd.send(:_get_init_args, *inar, hsopt)
138
- # == [RangeBeginValue, RangeEndValue, exclude_end?, exclude_begin?]
145
+ arout = RangeExtd.send(:_get_init_args, *inar, **hsopt)
146
+ # == [RangeBeginValue, RangeEndValue, exclude_begin?, exclude_end?]
139
147
 
140
148
  ### The following routine is obsolete.
141
149
  ### Users, if they wish, should call RangeExtd::Infinity.overwrite_compare() beforehand.
@@ -170,30 +178,27 @@ class RangeExtd < Range
170
178
  raise RangeError, "the combination of the arguments does not constitute a valid RangeExtd instance."
171
179
  end
172
180
 
181
+ @exclude_end = arout.pop
173
182
  @exclude_begin = arout.pop
174
- @exclude_end = arout[-1]
175
- @rangepart = Range.new(*arout)
176
- super(*arout)
177
-
178
- end # def initialize(*inar)
183
+ artmp = [arout[0], arout[1], @exclude_end]
184
+ @rangepart = Range.new(*artmp)
185
+ super(*artmp)
186
+ end # def initialize(*inar, **hsopt)
179
187
 
180
188
 
181
189
  # true if self is identical to {RangeExtd::NONE}.
190
+ #
191
+ # Overwriting {Range#is_none?}
182
192
  # This is different from {#==} method!
193
+ #
183
194
  # @example
184
- # RangeExtd(0,0,false,false) == RangeExtd::NONE # => true
185
- # RangeExtd(0,0,false,false).empty? # => true
186
- # RangeExtd(0,0,false,false).is_none? # => false
195
+ # RangeExtd(0,0,true,true).valid? # => true
196
+ # RangeExtd(0,0,true,true) == RangeExtd::NONE # => true
197
+ # RangeExtd(0,0,true,true).empty? # => true
198
+ # RangeExtd(0,0,true,true).is_none? # => false
187
199
  # RangeExtd::NONE.is_none? # => true
188
200
  def is_none?
189
- self.begin.nil? && self.end.nil? && @exclude_begin && @exclude_end # Direct comparison with object_id should be OK?
190
- end
191
-
192
- # true if self is identical to {RangeExtd::ALL} ({#==} does not mean it at all!)
193
- # @example
194
- # (RangeExtd::Infinity::NEGATIVE..RangeExtd::Infinity::POSITIVE).is_all? # => false
195
- def is_all?
196
- 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.)
201
+ @is_none
197
202
  end
198
203
 
199
204
 
@@ -216,11 +221,10 @@ class RangeExtd < Range
216
221
  # this returns true, regardless of their boundary values.
217
222
  # And any empty range is equal to RangeExtd::Infinity::NONE.
218
223
  #
219
- # Note the last example will return false for {#eql?} -- see {#eql?}
220
- #
221
- # See {#eql?}
224
+ # Note the last example will return false for +#eql?+
222
225
  #
223
226
  # @example
227
+ # (1...1) == RangeExtd::NONE # => false (b/c the Range is invalid)
224
228
  # (1<...1) == RangeExtd::NONE # => true
225
229
  # (?a<...?b) == RangeExtd::NONE # => true
226
230
  # (1<...1) == (2<...2) # => true
@@ -231,25 +235,27 @@ class RangeExtd < Range
231
235
  #
232
236
  # @return [Boolean]
233
237
  def ==(r)
234
- re_equal_core(r, :==)
238
+ _re_equal_core(r, :==)
235
239
  end # def ==(r)
236
240
 
237
- # The same as {#==} but it uses eql?() as each comparison.
238
- # For the empty ranges, it is similar to {#==}, except
239
- # the immediate class has to agree to return true.
240
- # Only the exception is the comparison with RangeExtd::Infinity::NONE.
241
- # Therefore,
242
- # @example
243
- # (1...5) == (1.0...5.0) # => true
244
- # (1...5).eql?(1.0...5.0) # => false
245
- # (1<...1).eql?( RangeExtd::NONE) # => true
246
- # (?a<...?b).eql?(RangeExtd::NONE) # => true
247
- # (1<...1).eql?( 3<...4) # => true
248
- # (1.0<...1.0).eql?(3<...4) # => false
249
- #
250
- def eql?(r)
251
- re_equal_core(r, :eql?)
252
- end # def eql?(r)
241
+ # Modification to {#eql?} is dropped in RangeExtd Ver.2
242
+ #
243
+ ## The same as {#==} but it uses eql?() as each comparison.
244
+ ## For the empty ranges, it is similar to {#==}, except
245
+ ## the immediate class has to agree to return true.
246
+ ## Only the exception is the comparison with RangeExtd::Infinity::NONE.
247
+ ## Therefore,
248
+ ## @example
249
+ ## (1...5) == (1.0...5.0) # => true
250
+ ## (1...5).eql?(1.0...5.0) # => false
251
+ ## (1<...1).eql?( RangeExtd::NONE) # => true
252
+ ## (?a<...?b).eql?(RangeExtd::NONE) # => true
253
+ ## (1<...1).eql?( 3<...4) # => true
254
+ ## (1.0<...1.0).eql?(3<...4) # => false
255
+ ##
256
+ #def eql?(r)
257
+ # _re_equal_core(r, :eql?)
258
+ #end # def eql?(r)
253
259
 
254
260
 
255
261
  # If the object is open-ended to the negative (Infinity),
@@ -263,7 +269,7 @@ class RangeExtd < Range
263
269
  # In the case of the former, after finite trials of [#succ] from ?c, it reaches the end (?z).
264
270
  # In the latter, after finit trials of [#succ] from the begin ?a, it reaches the end (?z).
265
271
  # Therefore it is theoretically possible to prove it (n.b., the actual
266
- # algorithm of built-in {Range#include?} is different and cheating!
272
+ # algorithm of built-in +Range#include?+ is different and cheating!
267
273
  # See below.).
268
274
  #
269
275
  # However, in the case of
@@ -275,11 +281,11 @@ class RangeExtd < Range
275
281
  #
276
282
  # Note
277
283
  # (?B..?z) === 'dd' # => false
278
- # as Ruby's {Range} knows the algorithm of {String#succ} and {String#<=>}
279
- # and specifically checks with it, before using {Enumerable#include?}.
284
+ # as Ruby's {Range} knows the algorithm of +String#succ+ and +String#<=>+
285
+ # and specifically checks with it, before using +Enumerable#include?+.
280
286
  # {https://github.com/ruby/ruby/blob/trunk/range.c}
281
287
  #
282
- # Therefore, even if you change the definition of {String#succ}
288
+ # Therefore, even if you change the definition of +String#succ+
283
289
  # so that 'B'.succ => 'dd', 'dd'.succ => 'z', as follows,
284
290
  # class String
285
291
  # alias :succ_orig :succ
@@ -293,7 +299,7 @@ class RangeExtd < Range
293
299
  # end
294
300
  # end
295
301
  # end
296
- # the resutl of {Range#===} will unchange;
302
+ # the resutl of +Range#===+ will unchange;
297
303
  # (?B..?z) === 'dd' # => false
298
304
  # (?B..?z).to_a # => ["B", "dd", "z"]
299
305
  #
@@ -306,29 +312,31 @@ class RangeExtd < Range
306
312
  def ===(obj)
307
313
  # ("a".."z")===("cc") # => false
308
314
 
309
- return false if is_none? # No need of null?(), supposedly!
315
+ return false if empty? # n.b, NONE includes nothing, even NOWHERE (because of exclude_begin/end)
310
316
 
311
- begin
312
- 1.0+(obj) # OK if Numeric.
317
+ rapart = _converted_rangepart
318
+ beg = rapart.begin
319
+ if beg.nil? && !beg.nowhere?
320
+ return rapart.send(__method__, obj)
321
+ end
313
322
 
323
+ begin
324
+ _ = 1.0+obj # OK if Numeric.
325
+ return cover?(obj) # This excludes begin() if need be.
314
326
  rescue TypeError
315
- # obj is not Numeric, hence runs brute-force check.
316
- beg = self.begin()
317
- if defined?(beg.infinity?) && beg.infinity? || beg == -Infinity::FLOAT_INFINITY
318
- return nil
319
- # raise TypeError "can't iterate from -Infinity"
320
- end
327
+ end
321
328
 
322
- each do |ei|
323
- if ei == obj
324
- return true
325
- end
326
- end
327
- false
329
+ # obj is not Numeric, hence runs brute-force check.
330
+ beg = self.begin()
331
+ if beg.respond_to?(:infinity?) && beg.infinity?
332
+ return nil
333
+ # raise TypeError "can't iterate from -Infinity"
334
+ end
328
335
 
329
- else
330
- cover?(obj)
336
+ each do |ei| # This excludes begin() if need be.
337
+ return true if ei == obj
331
338
  end
339
+ false
332
340
  end # def ===(obj)
333
341
 
334
342
  alias :include? :===
@@ -442,8 +450,8 @@ class RangeExtd < Range
442
450
  # (Rational(36,10)..5).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Rational (Ruby 2.1)
443
451
  # (3..Rational(61,10)).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Fixnum (Ruby 2.1)
444
452
  #
445
- # In short, bsearch works only with Integer and/or Float (as in Ruby 2.1).
446
- # If either of begin and end is an Float, the search is conducted in Float and the returned value will be Float, unless nil.
453
+ # +Range#bsearch+ works only with Integer and/or Float (as in Ruby 2.1), not even Rational (as in Ruby 3.1).
454
+ # If either of begin and end is a Float, the search is conducted in Float and the returned value will be a Float, unless nil.
447
455
  # If Float, it searches on the binary plane.
448
456
  # If Integer, the search is conducted on the descrete Integer points only,
449
457
  # and no search will be made in between the adjascent integers.
@@ -451,7 +459,7 @@ class RangeExtd < Range
451
459
  # Given that, {RangeExtd#bsearch} follows basically the same, even when exclude_begin? is true.
452
460
  # If either end is Float, it searches between begin*(1+Float::EPSILON) and end.
453
461
  # If both are Integer, it searches from begin+1.
454
- # When {#exclude_begin?} is false, {RangeExtd#bsearch} is identical to {Range#bsearch}.
462
+ # When {#exclude_begin?} is false, {RangeExtd#bsearch} is identical to +Range#bsearch+.
455
463
  #
456
464
  def bsearch(*rest, &bloc)
457
465
  if is_none? # No need of null?(), supposedly!
@@ -481,97 +489,110 @@ class RangeExtd < Range
481
489
  end # def bsearch(*rest, &bloc)
482
490
 
483
491
 
484
- # See {#include?} or {#===}, and {Range#cover?}
492
+ # This works without modification mostly.
493
+ #
494
+ # Presumably because Enumerable#count is called and #each is internally used.
495
+ # Exceptions are infinities and borderless (nil).
496
+ #
497
+ # (5..).count # => Float::INFINITY # exceptional case
498
+ # (..5).count # => Float::INFINITY # exceptional case
499
+ # (..nil).count # => Float::INFINITY # exceptional case
500
+ # (-Float::INFINITY..nil) # => Float::INFINITY # exceptional case
501
+ # (-Float::INFINITY..Float::INFINITY).count # raises (TypeError) "can't iterate from Float"
502
+ # (..5).count(4) # raises (TypeError)
503
+ # (..5).count{|i| i<3} # raises (TypeError)
504
+ # (1..).count{|i| i<3} # infinite loop!
505
+ #
506
+ # Here I define as another exceptional case:
507
+ # RangeExtd::ALL.count # => Float::INFINITY
508
+ #
509
+ # @return [Integer]
510
+ def count(*rest, &block)
511
+ return Float::INFINITY if self == RangeExtd::ALL
512
+ super
513
+ end
514
+
515
+
516
+ # See {#include?} or {#===}, and +Range#cover?+
485
517
  def cover?(i)
486
518
  # ("a".."z").cover?("cc") # => true
487
519
  # (?B..?z).cover?('dd') # => true (though 'dd'.succ would never reach ?z)
488
520
 
489
- return false if is_none? # No need of null?(), supposedly!
521
+ return false if empty? # equivalent to null? in this case because self is alwasy true==valid?
490
522
 
491
- if @exclude_begin
492
- if self.begin == i
493
- false
494
- else
495
- @rangepart.send(__method__, i)
496
- end
523
+ if @exclude_begin && self.begin == i
524
+ false
497
525
  else
498
526
  @rangepart.send(__method__, i)
499
527
  end
500
528
  end # def cover?(i)
501
529
 
502
530
 
503
- # @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.
531
+ # slightly modified for {#exclude_begin?} being true
532
+ #
533
+ # @raise [TypeError] If {#exclude_begin?} is true, and {#begin}() (+@rangepart+) does not have a method of [#succ], then even if no block is given, this method raises TypeError straightaway.
504
534
  # @return [RangeExtd] self
505
535
  # @return [Enumerator] if block is not given.
506
- #
507
536
  def each(*rest, &bloc)
508
537
  # (1...3.5).each{|i|print i} # => '123' to STDOUT
509
538
  # (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
510
539
  # (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
511
540
  # 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.
512
- if @exclude_begin # including RangeExtd::NONE
513
- if defined? self.begin.succ
514
- ret = Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc)
515
- if block_given?
516
- self
517
- else
518
- ret
519
- end
520
- elsif is_none?
521
- raise TypeError, "can't iterate for NONE range"
522
- else
523
- raise TypeError, "can't iterate from "+self.begin.class.name
524
- end
541
+
542
+ _step_each_core(__method__, *rest, &bloc)
543
+ end
544
+
545
+ # Core routine for {#each} and {#step}
546
+ #
547
+ # @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.
548
+ # @return [RangeExtd] self
549
+ # @return [Enumerator] if block is not given.
550
+ def _step_each_core(method, *rest, &bloc)
551
+ raise TypeError, "can't iterate for NONE range" if is_none?
552
+
553
+ if block_given?
554
+ # when a block is given to {#each}, self should be returned.
555
+ _converted_rangepart(consider_exclude_begin: true, raises: true ).send(method, *rest, &bloc)
556
+ self
525
557
  else
526
- @rangepart.send(__method__, *rest, &bloc)
558
+ _converted_rangepart(consider_exclude_begin: true, raises: false).send(method, *rest)
527
559
  end
528
560
  end
561
+ private :_step_each_core
529
562
 
530
-
531
- # Like {Range#last}, if no argument is given, it behaves like {#begin}(), that is, it returns the initial value, regardless of {#exclude_begin?}.
532
- # 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().
563
+ # Like +Range#last+, if no argument is given, it behaves like {#begin}(), that is, it returns the initial value, regardless of {#exclude_begin?}.
564
+ #
565
+ # If an argument is given (nb., acceptable since Ruby 1.9.2) when {#exclude_begin?} is true, it returns the array that starts from {#begin}().succ(), in the same way as +Range#last+ with {#exclude_end?} of +true+.
566
+ #
567
+ # The default behaviours are:
568
+ #
569
+ # (1...3.1).last # => 3.1
570
+ # (1...3.1).last(1) # => [3]
571
+ # (1...3.0).last(1) # => [2]
572
+ # (3.0..8).first(1) # raise: can't iterate from Float (TypeError)
573
+ #
533
574
  # @raise [TypeError] if the argument (Numeric) is given and if {#exclude_begin?} is true, yet if {#begin}().succ is not defined, or yet if {#is_none?}
534
- # @param rest [Integer] Optional. Must be non-negative. Consult {Range#first} for detail.
575
+ # @raise [RangeError] "cannot get the first element of beginless range" as per Range.
576
+ # @raise [ArgumentError] if more than 1 arguments are specified (delegated to {Range})
577
+ # @param rest [Integer] Optional. Must be non-negative. Consult +Range#first+ for detail.
535
578
  # @return [Object] if no argument is given, equivalent to {#end}.
536
579
  # @return [Array] if an argument is given.
537
580
  def first(*rest)
538
- # (1...3.1).last # => 3.1
539
- # (1...3.1).last(1) # => [3]
540
- if ! @exclude_begin # hence, not NONE.
541
- @rangepart.first(*rest)
542
- else
543
- case rest.size
544
- when 0
545
- self.begin
546
- when 1
547
- if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8.
548
- raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)."
549
- end
550
-
551
- ## Check the argument.
552
- Array.new[ rest[0] ] # Check Type of rest[0] (if invalid, it should raise TypeError)
553
-
554
- begin
555
- if rest[0] < 0
556
- raise ArgumentError, "negative array size (or size too big)"
557
- end
558
- rescue NoMethodError
559
- # Should not happen, but just to play safe.
560
- end
561
-
562
- ## Main
563
- if ! defined? self.begin.succ
564
- raise TypeError, "can't iterate from "+self.begin.class.name
565
- end
581
+ if is_none?
582
+ raise RangeError, "cannot get the first element of RangeExtd::NONE"
583
+ end
566
584
 
567
- Range.new(self.begin.succ, self.end, exclude_end?).send(__method__, *rest)
568
- else
569
- raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)"
570
- end
571
- end # if ! @exclude_begin
585
+ ran = _converted_rangepart(transform_to_nil: false, consider_exclude_begin: (1 == rest.size && exclude_begin?))
586
+ ran.send(__method__, *rest)
572
587
  end # def first(*rest)
573
588
 
574
589
 
590
+ # Redefines the hash definition
591
+ #
592
+ # Without re-definition, the hash value does NOT depend on {#exclude_begin?}
593
+ # presumably because the parent class method +Range#hash+ does not take it
594
+ # into account (of course).
595
+ #
575
596
  # 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.
576
597
  #
577
598
  def hash(*rest)
@@ -598,7 +619,8 @@ class RangeExtd < Range
598
619
  end
599
620
 
600
621
 
601
- # See {Range#last}.
622
+ # Updated version of +Range#last+, considering {#exclude_begin?}.
623
+ #
602
624
  # If either (let alone both) side of the edge is Infinity, you can not give
603
625
  # an argument in practice, the number of the members of the returned array.
604
626
  #
@@ -606,41 +628,55 @@ class RangeExtd < Range
606
628
  # @return [Object] if no argument is given, equivalent to {#end}.
607
629
  # @return [Array] if an argument is given.
608
630
  def last(*rest)
609
- return nil if null?
610
- nSize = rest.size
611
- case nSize
612
- when 0
613
- self.end
614
- when 1
615
- if (RUBY_VERSION < "1.9.1") && (1 == rest[0]) # Range#first() does not accept an argument in Ruby 1.8.
616
- raise ArgumentError, "wrong number of arguments (#{rest.size} for 0) (Use Ruby 1.9.2 or later)."
617
- end
631
+ if is_none?
632
+ raise RangeError, "cannot get the last element of RangeExtd::NONE"
633
+ end
618
634
 
619
- if defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY
620
- raise TypeError, "can't iterate from "+self.begin.to_s
621
- elsif defined?(self.end.infinity?) && self.end.infinity? || self.end == Infinity::FLOAT_INFINITY
622
- raise TypeError, "can't get elements to "+self.end.to_s
623
- elsif ! defined? self.begin.succ
624
- raise TypeError, "can't iterate from "+self.begin.class.name
625
- else
626
- @rangepart.send(__method__, *rest)
635
+ _converted_rangepart(transform_to_nil: false).send(__method__, *rest)
636
+ end # def last(*rest)
637
+
638
+
639
+ # Converts RangeExtd::Infinity to nil in @rangepart
640
+ #
641
+ # @param consider_exclude_begin [Boolean] If true (Default), and if {#exclude_begin?} is true, the first element is ignored. Note the resultant Range may be +invalid+.
642
+ # @param transform_to_nil [Boolean] If true (Default), {RangeExtd::Infinity} objects are transformed into nil when appropriate (i.e., {RangeExtd::Infinity::NEGATIVE} should be {RangeExtd#begin} and not at the end, and vice versa).
643
+ # @param raises [Boolean] If true (Def: false), and if {#exclude_begin?} is true but [#succ] is not defined for {#begin}, this routine raises an Exception as per (almost) Ruby default.
644
+ # @return [Range]
645
+ def _converted_rangepart(consider_exclude_begin: true, transform_to_nil: true, raises: false)
646
+ rbeg = @rangepart.begin
647
+ if consider_exclude_begin && exclude_begin?
648
+ if rbeg.respond_to? :succ
649
+ rbeg = rbeg.succ
650
+ elsif raises
651
+ if rbeg.nil?
652
+ raise RangeError, "cannot get the first element of beginless range"
653
+ elsif is_none? # maybe empty?() in some cases?
654
+ raise RangeError, "cannot get the first element of NONE range"
655
+ else
656
+ # This includes {RangeExtd::Infinity} class objects (RangeExtd::Infinity.infinity?(rbeg) == true) and Float::INFINITY.
657
+ raise TypeError, "can't iterate from "+self.begin.class.name
658
+ end
627
659
  end
628
- else
629
- raise ArgumentError, "wrong number of arguments (#{rest.size} for 0..1)"
630
660
  end
631
- end # def last(*rest)
632
661
 
662
+ rbeg = nil if RangeExtd::Infinity::NEGATIVE == rbeg && transform_to_nil
663
+ rend = @rangepart.end
664
+ rend = nil if RangeExtd::Infinity::POSITIVE == rend && transform_to_nil
665
+
666
+ Range.new(rbeg, rend, exclude_end?)
667
+ end
668
+ private :_converted_rangepart
633
669
 
634
670
  # See {#first} for the definition when {#exclude_begin?} is true.
635
671
  #
636
672
  def min(*rest, &bloc)
637
- re_min_max_core(__method__, *rest, &bloc)
673
+ _re_min_max_core(__method__, *rest, &bloc)
638
674
  end
639
675
 
640
676
  # See {#first} for the definition when {#exclude_begin?} is true.
641
677
  #
642
678
  def min_by(*rest, &bloc)
643
- re_min_max_core(__method__, *rest, &bloc)
679
+ _re_min_max_core(__method__, *rest, &bloc)
644
680
  end
645
681
 
646
682
 
@@ -651,7 +687,7 @@ class RangeExtd < Range
651
687
  # (1.3...5).minmax # => TypeError: can't iterate from Float
652
688
  # Note that max() for the same Range raises an exception.
653
689
  # In that sense, it is inconsistent!
654
- re_min_max_core(__method__, *rest, &bloc)
690
+ _re_min_max_core(__method__, *rest, &bloc)
655
691
  end
656
692
 
657
693
  # See {#first} for the definition when {#exclude_begin?} is true.
@@ -660,31 +696,31 @@ class RangeExtd < Range
660
696
  # (0...3.5).minmax # => [0, 3]
661
697
  # Note that max() for the same Range raises an exception.
662
698
  # In that sense, it is inconsistent!
663
- re_min_max_core(__method__, *rest, &bloc)
699
+ _re_min_max_core(__method__, *rest, &bloc)
664
700
  end
665
701
 
666
702
 
667
703
  # See {#first} for the definition when {#exclude_begin?} is true.
668
704
  #
669
705
  def max(*rest, &bloc)
670
- re_min_max_core(__method__, *rest, &bloc)
706
+ _re_min_max_core(__method__, *rest, &bloc)
671
707
  end
672
708
 
673
709
  # See {#first} for the definition when {#exclude_begin?} is true.
674
710
  #
675
711
  def max_by(*rest, &bloc)
676
- re_min_max_core(__method__, *rest, &bloc)
712
+ _re_min_max_core(__method__, *rest, &bloc)
677
713
  end
678
714
 
679
715
 
680
- # Implementation of {Range#size} to this class.
716
+ # Implementation of +Range#size+ to this class.
681
717
  #
682
718
  # It is essentially the same, but the behaviour when {#exclude_begin?} is true
683
719
  # may not always be natural.
684
720
  # See {#first} for the definition when {#exclude_begin?} is true.
685
721
  #
686
- # {Range#size} only works for Numeric ranges.
687
- # And in {Range#size}, the value is calculated when the initial value is
722
+ # +Range#size+ only works for Numeric ranges.
723
+ # And in +Range#size+, the value is calculated when the initial value is
688
724
  # non-Integer, by stepping by 1.0 from the {#begin} value, and the returned
689
725
  # value is an integer.
690
726
  # For example,
@@ -726,15 +762,38 @@ class RangeExtd < Range
726
762
  # in the range whereas 4.8 is not in the range by definition,
727
763
  # but not the example right above.
728
764
  #
765
+ # === Ruby 2.6 Endless Range and Infinity.
766
+ #
767
+ # Before RangeExtd Ver.1.1, if a {RangeExtd} object contains
768
+ # {RangeExtd::Infinity} objects for either begin or end, {#size} used to
769
+ # be always +Float::INFINITY+ no matter what the other object is
770
+ # (except when the other object is also a {RangeExtd::Infinity} object).
771
+ # However, since the introduction of the endless Range in Ruby 2.6,
772
+ # Ruby returns as follows:
773
+ #
774
+ # (5..).size # => Float::INFINITY
775
+ # (?a..).size # => nil
776
+ #
777
+ # Accordingly, this class {RangeExtd} now behaves the same as Ruby (2.6 or later).
778
+ #
779
+ # Similarly,
780
+ #
781
+ # (Float::INFINITY..Float::INFINITY).size
782
+ #
783
+ # has changed (I do not know in which Ruby version)!
784
+ # It used to be 0 (in Ruby-2.1). However, As of Ruby 2.6, it raises +FloatDomainError: NaN+
785
+ # Again this class now follows Ruby's default ({RangeExtd} Ver.1.0 or later).
786
+ #
729
787
  # @note When both ends n are the same INFINITY (of the same parity),
730
788
  # +(n..n).size+ used to be 0. As of Ruby 2.6, it is FloatDomainError: NaN.
731
789
  # This routine follows what Ruby produces, depending on Ruby's version it is run on.
732
790
  #
733
- # @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).
791
+ # @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).
734
792
  #
735
793
  # @return [Integer] 0 if {RangeExtd::NONE}
736
794
  # @return [Float] Float::INFINITY if either (or both) the end is infinity, regardless of the class of the elements.
737
795
  # @return [nil] if the range is non-Numeric.
796
+ # @raise [FloatDomainError] (Infinity..Infinity).size (as in Ruby-3.1, though it used to be 0 in Ruby-2.1)
738
797
  def size(*rest)
739
798
  # (1..5).size # => 5
740
799
  # (1...5).size # => 4
@@ -747,87 +806,60 @@ class RangeExtd < Range
747
806
  # (1.5...4.5).size # => 3
748
807
  # (0...Float::INFINITY).size # => Infinity
749
808
 
750
- if is_none? # No need of null?(), supposedly!
751
- return 0
752
-
753
- ### (Infinity..Infinity) => 0 (as in Ruby 2.1)
754
- # elsif self.begin().infinity? || self.end().infinity?
755
- # return Infinity::FLOAT_INFINITY
756
-
757
- # Checking Infinity.
758
- # Note (Infinity..Infinity) => 0 (Range as in Ruby 2.1)
759
- # however,
760
- elsif (defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY) ||
761
- (defined?(self.end.infinity?) && self.end.infinity? || self.end == Infinity::FLOAT_INFINITY)
762
- if self.begin == self.end
763
- # This varies, depending on Ruby's version! It used to be 0. As of Ruby 2.6, it is FloatDomainError: NaN.
764
- return (Float::INFINITY..Float::INFINITY).size
765
- # return 0
766
- else
767
- return Infinity::FLOAT_INFINITY
768
- end
809
+ return 0 if is_none? # No need of null?(), supposedly!
810
+
811
+ if self.begin.nil? || self.end.nil? # RangeExtd#begin/end can be nil only in Ruby-2.7+/2.6+
812
+ # Behaves as Ruby does -
813
+ # Infinity::FLOAT_INFINITY for Numeric and nil, but nil for any other
814
+ # {#exclude_end?} does not matter.
815
+ return (self.begin..self.end).size
816
+ end
769
817
 
770
- elsif @exclude_begin
818
+ rbeg = self.begin
819
+ rend = self.end
771
820
 
772
- begin
773
- _dummy = 1.0 + self.begin() # _dummy to suppress warning: possibly useless use of + in void context
821
+ # Either or both sides are (general or Float) Infinity
822
+ if RangeExtd::Infinity.infinite?(rbeg) || RangeExtd::Infinity.infinite?(rend)
823
+ return @rangepart.send(__method__, *rest) # delegates to {Range#size}
824
+ end
774
825
 
775
- # Numeric
776
- if defined? (self.begin().succ)
777
- Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest)
778
- else
779
- size_no_exclude = Range.new(self.begin, self.end).send(__method__, *rest) # exclude_end? == true, ie., Range with both ends inclusinve.
780
- diff = self.end - self.begin
781
- if diff.to_i == diff # Integer difference
782
- return size_no_exclude - 1 # At least exclude_begin?==true (so exclude_end? does not matter)
783
- else
784
- return size_no_exclude
785
- end
786
- end
787
- rescue TypeError
788
- # Non-Numeric
789
- if defined? self.begin().succ
790
- Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest) # => nil in Ruby 2.1
791
- else
792
- nil # See the line above.
793
- # raise TypeError, "can't iterate from "+self.begin.class.name
794
- end
826
+ return @rangepart.send(__method__, *rest) if !exclude_begin?
795
827
 
828
+ # Now, {#exclude_begin?} is true:
829
+ begin
830
+ _dummy = 1.0 + rbeg # _dummy to suppress warning: possibly useless use of + in void context
831
+ rescue TypeError
832
+ # Non-Numeric
833
+ if defined? rbeg.succ
834
+ return Range.new(rbeg.succ, rend, exclude_end?).send(__method__, *rest) # => nil in Ruby 2.1+
835
+ else
836
+ return nil # See the line above.
837
+ # raise TypeError, "can't iterate from "+self.begin.class.name
796
838
  end
839
+ end
797
840
 
841
+ # Numeric
842
+ if rbeg.respond_to? :succ
843
+ Range.new(rbeg.succ, rend, exclude_end?).send(__method__, *rest)
798
844
  else
799
- @rangepart.send(__method__, *rest)
845
+ size_no_exclude = Range.new(rbeg, rend).send(__method__, *rest) # exclude_end? == true, ie., Range with both ends inclusinve.
846
+ diff = self.end - self.begin
847
+ if diff.to_i == diff # Integer difference
848
+ return size_no_exclude - 1 # At least exclude_begin?==true (so exclude_end? does not matter)
849
+ else
850
+ return size_no_exclude
851
+ end
800
852
  end
801
853
  end # def size
802
854
 
803
855
 
804
856
  # See {#each}.
857
+ #
805
858
  # @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.
806
859
  # @return [RangeExtd] self
807
860
  # @return [Enumerator] if block is not given.
808
- #
809
861
  def step(*rest, &bloc)
810
- # (1...3.5).each{|i|print i} # => '123' to STDOUT
811
- # (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
812
- # (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
813
- # 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.
814
-
815
- if @exclude_begin # including RangeExtd::NONE
816
- if defined? self.begin.succ
817
- ret = Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc)
818
- if block_given?
819
- self
820
- else
821
- ret
822
- end
823
- elsif is_none? # No need of null?(), supposedly!
824
- raise TypeError, "can't iterate for NONE range"
825
- else
826
- raise TypeError, "can't iterate from "+self.begin.class.name
827
- end
828
- else
829
- @rangepart.send(__method__, *rest, &bloc)
830
- end
862
+ _step_each_core(__method__, *rest, &bloc)
831
863
  end
832
864
 
833
865
 
@@ -837,7 +869,15 @@ class RangeExtd < Range
837
869
 
838
870
  # Private class method to evaluate the arguments.
839
871
  #
872
+ # @note The specification changed from RangeExtd Ver.1 to Ver.2.
873
+ # In Ver.1 or earlier, this returns [begin, end, exclude_end, exclude_begin]
874
+ # In Ver.2+, this returns [begin, end, exclude_begin, exclude_end]
875
+ # Notice the third and fourth elements are swapped. Now it is in line
876
+ # with {RangeExtd.new}.
877
+ #
878
+ # @param (see RangeExtd#initialize) (or valid)
840
879
  # @raise [ArgumentError] if the input format is invalid (otherwise the caller may raise RangeError (it depends))
880
+ # @return [Array<Object, Object, Boolean, Boolean>] 4-compoents: [begin, end, exclude_begin, exclude_end]
841
881
  def self._get_init_args(*inar, **hsopt)
842
882
  nMin = 1; nMax = 5
843
883
  if inar.size < nMin || nMax < inar.size
@@ -875,8 +915,6 @@ class RangeExtd < Range
875
915
  end
876
916
 
877
917
  beginend = [inar[0].begin, inar[0].end]
878
- # arRet = [inar[0].begin, inar[0].end, exclude_end, exclude_begin]
879
- # @rangepart = Range.new(inar[0].begin, inar[0].end, exclude_end)
880
918
 
881
919
  when :object
882
920
  nMin = 2; nMax = 5
@@ -896,8 +934,8 @@ class RangeExtd < Range
896
934
  # Originally, defined?(inar[1].=~) seemed enough. But as of Ruby 2.6 (maybe even before),
897
935
  # Numeric has :=~ method as well!
898
936
  if (inar.size > 2 &&
899
- inar[1].class.method_defined?(:=~) &&
900
- inar[1].class.method_defined?(:to_str))
937
+ inar[1].respond_to?(:=~) &&
938
+ inar[1].respond_to?(:to_str))
901
939
  begin
902
940
  cmp = (inar[0] <=> inar[2]).abs
903
941
  rescue
@@ -908,16 +946,8 @@ class RangeExtd < Range
908
946
  # Hence all the default values are overwritten.
909
947
  beginend = [inar[0], inar[2]]
910
948
  hsFlag[:excl_offset] = 1
911
- if $1 == @@middle_strings[1]
912
- exclude_begin = false
913
- else
914
- exclude_begin = true
915
- end
916
- if $2 == @@middle_strings[4]
917
- exclude_end = true
918
- else
919
- exclude_end = false
920
- end
949
+ exclude_begin = ($1 != @@middle_strings[1])
950
+ exclude_end = ($2 == @@middle_strings[4])
921
951
  else
922
952
  nMin = 2; nMax = 4
923
953
  if inar.size > nMax
@@ -939,9 +969,6 @@ class RangeExtd < Range
939
969
  exclude_end = (true ^! inar[3+hsFlag[:excl_offset]]) # 4th or 5th argument
940
970
  end
941
971
 
942
- # arRet = [inar[0], inar[1], exclude_end, exclude_begin]
943
- # @rangepart = Range.new(inar[0], inar[1], exclude_end)
944
-
945
972
  else
946
973
  raise # (for coding safety)
947
974
  end # case hsFlag[:prm1st]
@@ -952,18 +979,45 @@ class RangeExtd < Range
952
979
  if hsopt.has_key?(:exclude_end)
953
980
  exclude_end = (hsopt[:exclude_end] && true)
954
981
  end
955
- # [RangeBeginValue, RangeEndValue, exclude_end?, exclude_begin?]
956
- beginend + [exclude_end, exclude_begin]
957
982
 
983
+ # [RangeBeginValue, RangeEndValue, exclude_begin?, exclude_end?]
984
+ _normalize_infinity_float(beginend) + [exclude_begin, exclude_end]
958
985
  end # def self._get_init_args(*inar)
959
-
960
986
  private_class_method :_get_init_args # From Ruby 1.8.7 (?)
961
987
 
988
+ # Replaces {RangeExtd::Infinity} with {Float::INFINITY} when appropriate
989
+ #
990
+ # @param beginend [Array] 2-compoents(begin, end)
991
+ # @return [Array] 2-compoents(begin, end)
992
+ def self._normalize_infinity_float(beginend)
993
+ is_begin_inf = Infinity.infinity?(beginend[0])
994
+ return beginend if is_begin_inf ^! Infinity.infinity?(beginend[1])
995
+
996
+ # Now, only one of them is a {RangeExtd::Infinity} type object.
997
+ if is_begin_inf && beginend[1].respond_to?(:divmod)
998
+ [_normalize_infinity_float_core(beginend[0]), beginend[1]] # "begin" is Infinity
999
+ elsif beginend[0].respond_to?(:divmod)
1000
+ [beginend[0], _normalize_infinity_float_core(beginend[1])] # "end" is Infinity
1001
+ else
1002
+ beginend
1003
+ end
1004
+ end
1005
+ private_class_method :_normalize_infinity_float # From Ruby 1.8.7 (?)
1006
+
1007
+ # @param inf [RangeExtd::Infinity]
1008
+ # @return [RangeExtd::Infinity, Float] +/-Float::INFINITY if Float
1009
+ def self._normalize_infinity_float_core(inf)
1010
+ msg = 'RangeExtd component of the RangeExtd::Infinity object replaced with Float::INFINITY.'
1011
+ warn msg if $DEBUG || $VERBOSE
1012
+ (inf.positive? ? 1 : -1) * Float::INFINITY
1013
+ end
1014
+ private_class_method :_normalize_infinity_float_core # From Ruby 1.8.7 (?)
1015
+
962
1016
 
963
1017
  # Returns true if the range to be constructed (or given) is valid,
964
1018
  # as a range, accepted in {RangeExtd}.
965
1019
  #
966
- # This routine is also impremented as a method in {Range},
1020
+ # This routine is also implemented as a method in {Range},
967
1021
  # and accordingly its sub-classes.
968
1022
  #
969
1023
  # This routine is called from {RangeExtd.new}, hence
@@ -973,23 +1027,31 @@ class RangeExtd < Range
973
1027
  #
974
1028
  # 1. The {#begin} and {#end} elements must be Comparable to each other,
975
1029
  # and the comparison results must be consistent betwen the two.
976
- # The two sole exceptions are {RangeExtd::NONE} and Endless Range
977
- # introduced in Ruby 2.6 (see below for the exceptions), both of which are valid.
978
- # For example, (nil..nil) is NOT valid (nb., it raised Exception in Ruby 1.8).
979
- # 2. Except for {RangeExtd::NONE}, {#begin} must have the method +<=+.
980
- # Therefore, some Endless Ranges (Ruby 2.6 and upwards) like (true..) are *not* valid.
981
- # Note even "true" has the method +<=>+ and hence checking +<=+ is essential.
982
- # 3. {#begin} must be smaller than or equal to {#end},
1030
+ # The three exceptions are {RangeExtd::NONE} and Beginless and Endless Ranges
1031
+ # introduced in Ruby 2.7 and 2.6, respectively (see below for the exceptions),
1032
+ # which are all valid. Accordingly, +(nil..nil)+ is
1033
+ # valid in {RangeExtd} Ver.1.0+ (nb., it used to raise Exception in Ruby 1.8).
1034
+ # 2. Except for {RangeExtd::NONE} and Beginless Range, {#begin} must have the method +<=+.
1035
+ # Therefore, some Endless Ranges (Ruby 2.6 and later) like +(true..)+ are *not* valid.
1036
+ # Note even "+true+" has the method +<=>+ and hence checking +<=+ is essential.
1037
+ # 3. Similarly, except for {RangeExtd::NONE} and Endless Range, {#end} must have the method +<=+.
1038
+ # Therefore, some Beginless Ranges (Ruby 2.7 and later) like +(..true)+ are *not* valid.
1039
+ # 4. {#begin} must be smaller than or equal to {#end},
983
1040
  # that is, ({#begin} <=> {#end}) must be either -1 or 0.
984
- # 4. If {#begin} is equal to {#end}, namely, ({#begin} <=> {#end}) == 0,
985
- # the exclude status of the both ends must agree.
986
- # That is, if the {#begin} is excluded, {#end} must be also excluded,
1041
+ # 5. If {#begin} is equal to {#end}, namely, ({#begin} <=> {#end}) == 0,
1042
+ # the exclude status of the both ends must agree, except for the cases
1043
+ # where both {#begin} and {#end} ani +nil+ (beginless and endless Range).
1044
+ # In other words, if the {#begin} is excluded, {#end} must be also excluded,
987
1045
  # and vice versa.
988
- # For example, (1...1) is NOT valid for that reason,
1046
+ # For example, +(1...1)+ is NOT valid for this reason,
989
1047
  # because any built-in Range object has the exclude status
990
- # of false (namely, inclusive) for {#begin}.
1048
+ # of +false+ (namely, inclusive) for {#begin}, whereas
1049
+ # +RangeExtd(1...1, true)+ is valid and equal (+==+) to
1050
+ # {RangeExtd::NONE}.
1051
+ # 6. If either or both of {#begin} and {#end} is {RangeExtd::Nowhere::NOWHERE},
1052
+ # the range has to be {RangeExtd::NONE}.
991
1053
  #
992
- # Note the last example may change in the future release.
1054
+ # Note the second last point may change in the future release.
993
1055
  #
994
1056
  # Note ([2]..[5]) is NOT valid, because Array does not include Comparable
995
1057
  # for some reason, as of Ruby 2.1.1, even though it has the redefined
@@ -998,8 +1060,10 @@ class RangeExtd < Range
998
1060
  #
999
1061
  # @example
1000
1062
  #
1001
- # RangeExtd.valid?(nil..nil) # => false
1002
- # RangeExtd.valid?(nil...nil) # => false
1063
+ # RangeExtd.valid?(nil..nil) # => true
1064
+ # RangeExtd.valid?(nil...nil) # => true
1065
+ # RangeExtd.valid?(nil<..nil) # => true
1066
+ # RangeExtd.valid?(nil<...nil) # => true
1003
1067
  # RangeExtd.valid?(0..0) # => true
1004
1068
  # RangeExtd.valid?(0...0) # => false
1005
1069
  # RangeExtd.valid?(0...) # => true
@@ -1013,6 +1077,7 @@ class RangeExtd < Range
1013
1077
  # RangeExtd.valid?(3..Float::INFINITY, true) # => true
1014
1078
  # RangeExtd.valid?(RangeExtd::Infinity::NEGATIVE..?d) # => true
1015
1079
  # RangeExtd.valid?(RangeExtd::Infinity::NEGATIVE..?d, true) # => true
1080
+ # RangeExtd.valid?(RangeExtd::Nowhere::NOWHERE..nil) # => false
1016
1081
  #
1017
1082
  # @note The flag of exclude_begin|end can be given in the arguments in a couple of ways.
1018
1083
  # If there is any duplication, those specified in the optional hash have the highest
@@ -1020,14 +1085,14 @@ class RangeExtd < Range
1020
1085
  # If not, the values embeded in the {Range} or {RangeExtd} object
1021
1086
  # in the parameter are used. In default, both of them are false.
1022
1087
  #
1023
- # @overload new(range, [exclude_begin=false, [exclude_end=false]])
1088
+ # @overload valid?(range, [exclude_begin=false, [exclude_end=false]])
1024
1089
  # @param range [Object] Instance of Range or its subclasses, including RangeExtd
1025
1090
  # @param exclude_begin [Boolean] If specified, this has the higher priority, or false in default.
1026
1091
  # @param exclude_end [Boolean] If specified, this has the higher priority, or false in default.
1027
1092
  #
1028
- # @overload new(obj_begin, obj_end, [exclude_begin=false, [exclude_end=false]])
1029
- # @param obj_begin [Object] Any object that is {Comparable} with end
1030
- # @param obj_end [Object] Any object that is Comparable with begin (or nil, for Ruby 2.6 onwards)
1093
+ # @overload valid?(obj_begin, obj_end, [exclude_begin=false, [exclude_end=false]])
1094
+ # @param obj_begin [Object] Any object that is +Comparable+ with end
1095
+ # @param obj_end [Object] Any object that is +Comparable+ with begin (or nil, for Ruby 2.6 onwards)
1031
1096
  # @param exclude_begin [Boolean] If specified, this has the lower priority, or false in default.
1032
1097
  # @param exclude_end [Boolean] If specified, this has the lower priority, or false in default.
1033
1098
  #
@@ -1035,29 +1100,41 @@ class RangeExtd < Range
1035
1100
  (vbeg, vend, exc_beg, exc_end) = _get_init_args(*inar)
1036
1101
 
1037
1102
  if defined?(inar[0].is_none?) && inar[0].is_none? && exc_beg && exc_end
1103
+ # inar[0] is RangeExtd::NONE
1104
+ return true
1105
+ elsif vbeg.nil? && vbeg.nowhere? || vend.nil? && vend.nowhere?
1106
+ # RangeExtd::Nowhere::NOWHERE should not reside anywhere but in RangeExtd::NONE
1107
+ return false
1108
+ elsif vbeg.nil? && vend.nil?
1038
1109
  return true
1039
1110
  end
1111
+
1112
+ return false if !vbeg.respond_to?(:<=>)
1040
1113
  begin
1041
1114
  t = (vbeg <=> vend)
1042
1115
  begin
1043
- if vend.nil?
1116
+ if vbeg.nil? # Beginless Range introduced in Ruby 2.7
1117
+ return vend.respond_to?(:<=)
1118
+ elsif vend.nil?
1044
1119
  begin
1045
1120
  _ = (vbeg..nil) # Endless Range introduced in Ruby 2.6
1046
- return vbeg.class.method_defined?(:<=)
1121
+ return vbeg.respond_to?(:<=)
1047
1122
  rescue ArgumentError
1048
1123
  # Before Ruby 2.6
1049
1124
  return false
1050
1125
  end
1051
1126
  end
1052
- return false if t != -1*(vend <=> vbeg) # false if not commutative, or possibly exception (such as -1*nil).
1127
+ return false if !vend.respond_to?(:<=>)
1128
+ return false if t != -1*(vend <=> vbeg) # false if not commutative (n.b., an exception should not happen).
1053
1129
  rescue NoMethodError, TypeError
1054
1130
  if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) ||
1055
1131
  (Float === vbeg && defined?(vend.infinity?) && vend.infinity?)
1056
- warn self.const_get(:ERR_MSGS)[:infinity_compare] # one of the tests comes here.
1132
+ warn self.const_get(:ERR_MSGS)[:infinity_compare] if !$VERBOSE.nil? # one of the tests comes here.
1057
1133
  end
1058
1134
  return false # return
1059
1135
  end
1060
- rescue # NoMethodError
1136
+ rescue
1137
+ warn "This should not happen. Contact the code developer (warn01)."
1061
1138
  false # return
1062
1139
  else
1063
1140
  case t
@@ -1072,9 +1149,10 @@ class RangeExtd < Range
1072
1149
  when 1
1073
1150
  false
1074
1151
  else
1152
+ warn "This should not happen. Contact the code developer (warn02)."
1075
1153
  if (Float === vend && defined?(vbeg.infinity?) && vbeg.infinity?) ||
1076
1154
  (Float === vbeg && defined?(vend.infinity?) && vend.infinity?)
1077
- warn self.const_get(:ERR_MSGS)[:infinity_compare] # not tested so far?
1155
+ warn self.const_get(:ERR_MSGS)[:infinity_compare] if !$VERBOSE.nil? # not tested so far?
1078
1156
  end
1079
1157
  false # Not Comparable.
1080
1158
  end # case t
@@ -1082,7 +1160,6 @@ class RangeExtd < Range
1082
1160
  end
1083
1161
  end # def valid?
1084
1162
 
1085
-
1086
1163
  # Set the class variable to be used in {RangeExtd#to_s} and {RangeExtd#inspect}
1087
1164
  # to configure the format of their returned values.
1088
1165
  #
@@ -1211,362 +1288,97 @@ class RangeExtd < Range
1211
1288
  end # def re_inspect_core_orig(method)
1212
1289
 
1213
1290
 
1214
- # Core routine for {#===} and {#eql?}
1291
+ # Core routine for {#===}
1292
+ #
1215
1293
  # @param [Object] r to compare.
1216
1294
  # @param [Symbol] method of the method name.
1217
- def re_equal_core(r, method)
1218
- if defined? r.empty?
1219
- is_r_empty = r.empty?
1220
- else
1221
- return false # Not Range family.
1222
- end
1223
- if ! defined? r.exclude_end?
1224
- false # Not Range family.
1225
- elsif is_none? && is_r_empty # RangeExtd::NONE
1226
- true
1227
- elsif empty? && defined?(r.is_none?) && r.is_none? # r is RangeExtd::NONE
1228
- true
1229
- elsif empty? && is_r_empty
1230
- if method == :eql?
1231
- # More strict
1232
- if self.begin().class == r.begin().class
1233
- true # (1<...1).eql? (2<...2) # => true (Fixnum <-> Fixnum) Yes!
1234
- else
1235
- false # (1.0<...1.0).eql? (2<...2) # => false (Float <-> Fixnum) No!
1295
+ def _re_equal_core(r, method=:==)
1296
+ return false if !r.respond_to? :exclude_end? # Not Range family.
1297
+ return false if !r.respond_to? :empty? # Not Range family.
1298
+ return false if !r.respond_to? :valid? # Not Range family.
1299
+
1300
+ return false if !r.valid? # always returns false with an invalid Range; n.b., RangeExtd#valid? is always true, hence they cannot be identical (eql?).
1301
+
1302
+ is_r_empty = r.empty?
1303
+ return !!is_r_empty if is_none? # RangeExtd::NONE==(1<...1); n.b. "!!" is redundant because r must be valid.
1304
+ return true if empty? && r.respond_to?(:is_none?) && r.is_none? # r is RangeExtd::NONE
1305
+ return false if !_both_same_nowhere_parity?(r) # inconsistent nil, non-nil, NOWHERE combination
1306
+
1307
+ if empty? && is_r_empty
1308
+ (self.begin.class.ancestors - self.begin.class.included_modules - [Object, BasicObject]).each do |ec|
1309
+ if ec === r.begin
1310
+ return true # (1.0<...1.0) == (2<...2) # (Float<Numeric <-> Fixnum<Numeric) Yes!
1236
1311
  end
1237
- else
1238
- (self.begin.class.ancestors - self.begin.class.included_modules - [Object, BasicObject]).each do |ec|
1239
- if ec === r.begin
1240
- return true # (1.0<...1.0) == (2<...2) # (Float<Numeric <-> Fixnum<Numeric) Yes!
1241
- end
1242
- end
1243
- false # (?a...?a) != (2<...2) # (String <-> Numeric) No!
1244
1312
  end
1313
+ return false # (?a...?a) != (2<...2) # (String <-> Numeric) No!
1314
+ end
1315
+
1316
+ return false if !(self.exclude_end? ^! r.exclude_end?)
1245
1317
 
1246
- elsif defined? r.exclude_begin?
1318
+ # Neither self nor r is guaranteed to be RangeExtd::NONE
1319
+ is_nil_equal = _both_eqleql_nil?(r, method)
1320
+
1321
+ if defined? r.exclude_begin? # r is RangeExtd
1247
1322
  (self.exclude_begin? ^! r.exclude_begin?) &&
1248
1323
  (self.exclude_end? ^! r.exclude_end?) &&
1249
- (self.begin.send(method, r.begin)) &&
1250
- (self.end.send( method, r.end))
1251
- # (self.begin == r.begin) &&
1252
- # (self.end == r.end)
1324
+ (self.begin.send(method, r.begin) && self.end.send(method, r.end) || is_nil_equal)
1253
1325
  else
1254
1326
  # r is Range
1255
1327
  if self.exclude_begin?
1256
1328
  false
1257
1329
  else
1258
- @rangepart.send(method, r) # Comparison as two Range-s.
1330
+ is_nil_equal || @rangepart.send(method, r) # Comparison as two Range-s.
1259
1331
  end
1260
1332
  end
1261
- end # def re_equal_core(r, method)
1333
+ end # def _re_equal_core(r, method)
1334
+ private :_re_equal_core
1262
1335
 
1263
1336
  # Core routine for {#min}, {#max}, {#minmax} etc.
1264
1337
  # @param method [Symbol] of the method name.
1265
1338
  # @param rest [Object]
1266
- def re_min_max_core(method, *rest, &bloc)
1339
+ def _re_min_max_core(method, *rest, &bloc)
1267
1340
  # (1...3.5).max # => TypeError: cannot exclude non Integer end value
1268
- if is_none?
1341
+ if is_none? || self.begin.nil? && self.begin.nowhere? || self.end.nil? && self.end.nowhere?
1342
+ # In fact, Range#minmax etc should be modified to deal with RangeExtd::Nowhere::NOWHERE (it is treated as nil at the moment)...
1269
1343
  raise TypeError, "no meaningful range."
1270
- elsif @exclude_begin
1271
- if defined?(self.begin.infinity?) && self.begin.infinity? || self.begin == -Infinity::FLOAT_INFINITY
1272
- raise TypeError, "can't exclude "+self.begin.to_s
1273
- elsif ! defined? self.begin.succ
1274
- raise TypeError, "can't iterate from "+self.begin.class.name
1275
- else
1276
- Range.new(self.begin.succ, self.end, exclude_end?).send(method, *rest, &bloc)
1277
- end
1278
- else
1279
- @rangepart.send(method, *rest)
1280
1344
  end
1281
- end # def re_min_max_core(method, *rest, &bloc)
1282
1345
 
1346
+ if !@exclude_begin ||
1347
+ [:max, :max_by].include?(method) ||
1348
+ self.begin.respond_to?(:succ) ||
1349
+ [:min_by, :minmax_by].include?(method) && !block_given?
1350
+ # For the last one, if Range starts from something uniterable, it returns
1351
+ # Enumerator (without errors); but once it is executed, it will raise TypeError
1352
+ # "can't iterate from Float" or RangeError "cannot get the minimum of beginless range"
1353
+ return _converted_rangepart(transform_to_nil: false).send(method, *rest, &bloc)
1354
+ end
1355
+
1356
+ # Now @exclude_begin is true, "begin" is not discrete, and block is given (if _by).
1357
+ raise TypeError, "can't exclude non-iterable begin value" #+self.begin.to_s
1358
+ end # def _re_min_max_core(method, *rest, &bloc)
1359
+ private :_re_min_max_core
1360
+
1361
+ self.remove_const :NONE if defined? self::NONE # tricky manoeuvre for documentation purposes... (see infinity.rb for the explanatory document)
1283
1362
  # No range.
1284
1363
  # In Ruby1.8, this causes ArgumentError: bad value for range (because (nil..nil) is unaccepted).
1285
- NONE = RangeExtd.new(nil, nil, true, true, :Constant)
1286
- #NONE = RangeExtd.new(nil...nil, true, true, :Constant)
1364
+ #NONE = RangeExtd.new(nil, nil, true, true, :Constant)
1365
+ NONE = RangeExtd.new(RangeExtd::Nowhere::NOWHERE, RangeExtd::Nowhere::NOWHERE, true, true, :Constant)
1287
1366
 
1367
+ self.remove_const :ALL if defined? self::ALL # tricky manoeuvre for documentation purposes... (see infinity.rb for the explanatory document)
1288
1368
  # Range covers everything.
1289
1369
  ALL = RangeExtd.new(Infinity::NEGATIVE, Infinity::POSITIVE, false, false, :Constant)
1290
- #ALL = RangeExtd.new(Infinity::NEGATIVE..Infinity::POSITIVE, false, false, :Constant)
1291
1370
 
1292
1371
  end # class RangeExtd < Range
1293
1372
 
1294
-
1295
- #= Class Range
1296
- #
1297
- #== Summary
1298
- #
1299
- # Modifies {#==}, {#eql?} and add methods of
1300
- # {#valid?}, {#empty?}, {#null?}, {#is_none?} and {#is_all?}.
1301
- #
1302
- class Range
1303
-
1304
- alias_method :equal_prerangeextd?, :== if ! self.method_defined?(:equal_prerangeextd?) # No overwriting.
1305
-
1306
- # It is extended to handle {RangeExtd} objects.
1307
- # For each element, that is, {#begin} and {#end},
1308
- # this uses their method of ==(). See {#eql?}.
1309
- #
1310
- # As long as the comparison is limited within {Range} objects,
1311
- # the returned value of this method has unchanged.
1312
- #
1313
- # A note of caution is, some ranges which the built-in Range accepts,
1314
- # are now regarded as NOT valid, such as, (1...1) and (nil..nil)
1315
- # (the latter was not permitted in Ruby 1.8), though you can still
1316
- # use them;
1317
- # (1...1).valid? # => false
1318
- # On the other hand, {RangeExtd} class does not accept or create
1319
- # any invalid range; for any {RangeExtd} object, RangeExtd#valid?
1320
- # returns true. For example, there is no {RangeExtd} object
1321
- # that is expressed as (1...1) (See {#valid?} for detail).
1322
- #
1323
- # For that reason, when those non-valid Range objects are compared
1324
- # with a {RangeExtd} object, the returned value may not be what
1325
- # you would expect. For example,
1326
- # (1...1) == RangeExtd(1, 1, true, true) # => false.
1327
- # The former is an invalid range, while the latter is
1328
- # a rigidly-defined empty range.
1329
- #
1330
- # Consult {#valid?} and {RangeExtd#==} for more detail.
1331
- def ==(r)
1332
- equal_core(r, :==, :equal_prerangeextd?)
1333
- end
1334
-
1335
-
1336
- alias :eql_prerangeextd? :eql? if ! self.method_defined?(:eql_prerangeextd?) # No overwriting.
1337
-
1338
- # Same as {#==}, but the comparison is made with eql?() method.
1339
- def eql?(r)
1340
- equal_core(r, :eql?, :eql_prerangeextd?)
1341
- end
1342
-
1343
-
1344
- # Returns true if self is valid as a comparable range.
1345
- #
1346
- # See {RangeExtd.valid?} for the definition of what is valid
1347
- # and more examples.
1348
- #
1349
- # See {#empty?} and {#null?}, too.
1350
- #
1351
- # @example
1352
- # (nil..nil).valid? # => false
1353
- # (0..0).valid? # => true
1354
- # (0...0).valid? # => false
1355
- # (2..-1).valid? # => false
1356
- # RangeExtd(0...0, true) # => true
1357
- # (3..Float::INFINITY).valid? # => true
1358
- # RangeExtd::NONE.valid? # => true
1359
- # RangeExtd::ALL.valid? # => true
1360
- #
1361
- # @note By definition, all the {RangeExtd} instances are valid,
1362
- # because {RangeExtd.new} checks the validity.
1363
- def valid?
1364
- RangeExtd.valid?(self)
1365
- end # def valid?
1366
-
1367
-
1368
- # Returns true if self is empty.
1369
- # Returns nil if self is not valid (nb., any RangeExtd instance is valid.)
1370
- # Otherwise false.
1371
- #
1372
- # The definition of what is empty is as follow.
1373
- #
1374
- # 1. the range must be valid: {#valid?} => true
1375
- # 2. if the range id discrete, that is, {#begin} has
1376
- # [#succ] method, there must be no member within the range:
1377
- # {#to_a}.empty? => true
1378
- # 3. if the range is continuous, that is, {#begin} does not have
1379
- # [#succ] method, {#begin} and {#end} must be equal
1380
- # (({#begin} <=> {#end}) => 0) and both the boundaries must
1381
- # be excluded: ({#exclude_begin?} && {#exclude_end?}) => true.
1382
- # Note that ranges with equal {#begin} and {#end} with
1383
- # inconsistent two exclude status are not valid, and the built-in
1384
- # Range always has the {#begin}-exclude status of false.
1385
- #
1386
- # In these conditions, none of Range instance would return true in {#empty?}.
1387
- #
1388
- # @example
1389
- # (nil..nil).empty? # => nil
1390
- # (1...1).empty? # => nil
1391
- # (1..1).empty? # => false
1392
- # RangeExtd(1...1, true).empty? # => true
1393
- # RangeExtd(1...2, true).empty? # => true
1394
- # RangeExtd(1.0...2, true).empty? # => false
1395
- # RangeExtd(?a...?b, true).empty? # => true
1396
- # RangeExtd::NONE.empty? # => true
1397
- #
1398
- # @note to check whether it is either empty or invalid, use {#null?}.
1399
- # See {#valid?} and {RangeExtd.valid?}, too.
1400
- #
1401
- # @return [Boolean, nil]
1402
- def empty?
1403
- # This is basically for the sake of sub-classes, as any built-in Range instance
1404
- # always returns either nil or false.
1405
-
1406
- if !valid?
1407
- return nil
1408
- elsif defined?(self.is_none?) && self.is_none?
1409
- return true
1410
- end
1411
-
1412
- t = (self.begin() <=> self.end())
1413
- case t
1414
- when -1
1415
- if (defined?(self.exclude_begin?)) &&
1416
- exclude_begin? &&
1417
- exclude_end? &&
1418
- defined?(self.begin().succ) &&
1419
- (self.begin().succ == self.end())
1420
- true # e.g., ("a"<..."b")
1421
- else
1422
- false
1423
- end
1424
- when 0
1425
- if defined?(self.boundary) && self.boundary.nil? # for RangeOpen
1426
- # RangeExtd::NONE or RangeExtd::All
1427
- if self.exclude_end?
1428
- true # RangeOpen::NONE
1429
- else
1430
- false # RangeOpen::ALL
1431
- end
1432
- else
1433
- if defined?(self.exclude_begin?)
1434
- t2 = self.exclude_begin?
1435
- else
1436
- t2 = false # == return false
1437
- end
1438
- (t2 && exclude_end?)
1439
- end
1440
- when 1
1441
- nil # redundant, as it should not be valid in the first place.
1442
- else
1443
- nil # redundant, as it should not be valid in the first place.
1444
- end
1445
- end # def empty?
1446
-
1447
-
1448
- # Returns true if it is either empty or invalid. false otherwise.
1449
- # See {#empty?} and {#valid?}.
1450
- def null?
1451
- (! valid?) || empty?
1452
- end
1453
-
1454
- # @return [FalseClass]
1455
- def is_none?
1456
- false
1457
- end
1458
-
1459
- # @return [FalseClass]
1460
- def is_all?
1461
- false
1462
- end
1463
-
1464
-
1465
- # Return true if self and the other are equivalent; if [#to_a] is defined, it is similar to
1466
- # (self.to_a == other.to_a)
1467
- # (though the ends are checked more rigorously), and if not, equivalent to
1468
- # (self == other)
1469
- #
1470
- # @example
1471
- # (3...7).equiv?(3..6) # => true
1472
- # (3...7).equiv?(3..6.0) # => false
1473
- # (3...7).equiv?(3.0..6.0) # => false
1474
- # (3...7).equiv?(3..6.5) # => false
1475
- # (3...7).equiv?(3.0...7.0) # => true
1476
- # (3...7.0).equiv?(3..6) # => true
1477
- # (3...7.0).equiv?(3.0..6) # => false
1478
- #
1479
- # @param other [Range, RangeExtd]
1480
- def equiv?(other)
1481
- t_or_f = (defined?(self.begin.succ) && defined?(other.begin.succ) && defined?(other.end) && defined?(other.exclude_end?))
1482
- if ! t_or_f
1483
- return(self == other) # succ() for begin is not defined.
1484
- else
1485
- # Checking the begins.
1486
- if defined?(other.exclude_begin?) && other.exclude_begin? # The other is RangeExtd with exclude_begin?==true.
1487
- if self.begin != other.begin.succ
1488
- return false
1489
- else
1490
- # Pass
1491
- end
1492
- elsif (self.begin != other.begin)
1493
- return false
1494
- end
1495
-
1496
- # Now, the begins agreed. Checking the ends.
1497
- if (self.end == other.end)
1498
- if (exclude_end? ^! other.exclude_end?)
1499
- return true
1500
- else
1501
- return false
1502
- end
1503
- else # if (self.end == other.end)
1504
- if (exclude_end? ^! other.exclude_end?)
1505
- return false
1506
- elsif ( exclude_end? && defined?(other.end.succ) && (self.end == other.end.succ)) ||
1507
- (other.exclude_end? && defined?( self.end.succ) && (self.end.succ == other.end))
1508
- return true
1509
- else
1510
- return false
1511
- end
1512
- end # if (self.end == other.end)
1513
- end # if ! t_or_f
1514
-
1515
- end # def equiv?(other)
1516
-
1517
-
1518
- ############## pravate methods of Range ##############
1519
-
1520
- private
1521
-
1522
- # True if obj is Comparable.
1523
- def is_comparable?(obj)
1524
- if defined?(obj.<=) # Comparable?
1525
- true
1526
- else
1527
- false
1528
- end
1529
- end
1530
-
1531
- # @param r [Object] to compare.
1532
- # @param method [Symbol] of the method name.
1533
- # @param method_pre [Symbol] of the backed-up original method name.
1534
- def equal_core(r, method, method_pre)
1535
- if (! defined? r.exclude_end?) || (! defined? r.is_none?) || (! defined? r.empty?)
1536
- false # Not Range family.
1537
- # elsif empty? && defined?(r.is_none?) && r.is_none? # r is RangeExtd::NONE
1538
- # true
1539
- # elsif empty? && r.empty?
1540
- # # None of built-in Range class object can be #empty?==true - This is for sub-class.
1541
- #
1542
- elsif defined? r.exclude_begin?
1543
- # Either RangeExtd or RangeOpen object.
1544
- if r.exclude_begin?
1545
- false # self(Range) has always the inclusive begin.
1546
- else
1547
- # It could do with a single line,
1548
- # self.begin.send(method_pre, r)
1549
- # if this was for RangeExtd===r, but not for RangeOpen.
1550
- if (self.exclude_end? ^ r.exclude_end?)
1551
- false
1552
- elsif (self.begin.send(method, r.begin) && self.end.send(method, r.end))
1553
- true
1554
- else
1555
- false
1556
- end
1557
- end
1558
- else
1559
- self.send(method_pre, r) # r is Range.
1560
- end
1561
- end # def equal_core(r, method, method_pre)
1562
-
1563
- end # class Range
1564
-
1373
+ # require Range; it is necessary to modify Range if in a backward-compatible way.
1374
+ require_relative "range_extd/range" if !Range.method_defined?(:eql_prerangeextd?)
1565
1375
 
1566
1376
  # Constant-form of {#RangeExtd}.
1567
- # #RangeExtd(*) is equivalent to {#RangeExtd.new}.
1568
1377
  #
1569
- def RangeExtd(*rest, &b)
1570
- RangeExtd.new(*rest, &b)
1378
+ # +RangeExtd()+ is equivalent to {#RangeExtd.new}().
1379
+ #
1380
+ # @return [RangeExtd]
1381
+ def RangeExtd(*rest, **hs, &b)
1382
+ RangeExtd.new(*rest, **hs, &b)
1571
1383
  end
1572
1384