rangesmaller 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.rdoc +91 -0
- data/Rakefile +9 -0
- data/lib/rangesmaller/rangesmaller.rb +561 -0
- data/rangesmaller.gemspec +41 -0
- data/test/test_rangesmaller.rb +522 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fc2c175fd0a6852787d486eb15569707b6120222
|
4
|
+
data.tar.gz: ffeacff7e8c94e70a96723a34525f6a1a31ba055
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e2e4c8422b98bde05a3caae8c8e4c143ad6a495b30f0930f69f972f5a0ac4085a87d3f2b1b55ebb0d35ba4ffc95edd90ae162917097ee6e8397dbb117328660a
|
7
|
+
data.tar.gz: 1b7f0a09ccd0e66acee41ea4ef08a9d87c5579a4dc47bcbaf2830cae14974bf71d47ca7eee176f28689e6e0ce29acb7b5b4c56799c6c0608cffe9b47de86eebe
|
data/README.rdoc
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
= Rangesmaller - Range with exclude_begin
|
2
|
+
|
3
|
+
This package contains Rangesmaller class, a sub-class of Range and that behaves almost exactly like the super-class Range class, but holds an additional status of whether the boundary of the beginning of the object is included or excluded.
|
4
|
+
|
5
|
+
Rangesmaller objects are immutable, just like Range. Hence once the object is created, it would not change.
|
6
|
+
|
7
|
+
Any (pair of) objects that can consist of Range can be given in initialising, though that does not mean it would always successfully run any methods, like (nil..nil).each().
|
8
|
+
|
9
|
+
Two methods are added from the original Range:
|
10
|
+
|
11
|
+
:exclude_begin? # => true or false(Default)
|
12
|
+
:rangepart # Range object without the flag of :exclude_begin?
|
13
|
+
|
14
|
+
In default, both the begin and end boundaries are inclusive, unless specified otherwise in creating it.
|
15
|
+
|
16
|
+
== Install
|
17
|
+
|
18
|
+
gem install rangesmaller
|
19
|
+
|
20
|
+
A file
|
21
|
+
rangesmaller/rangesmaller.rb
|
22
|
+
should be installed in one of your $LOAD_PATH
|
23
|
+
|
24
|
+
Then all you need to do now is
|
25
|
+
require 'rangesmaller'
|
26
|
+
in your Ruby script (or irb).
|
27
|
+
|
28
|
+
Have fun!
|
29
|
+
|
30
|
+
== Simple Example
|
31
|
+
|
32
|
+
=== How to create an instance
|
33
|
+
|
34
|
+
Almost all you need to know is this:
|
35
|
+
|
36
|
+
r = Rangesmaller(5...8, :exclude_begin => true)
|
37
|
+
r.exclude_begin? # => true
|
38
|
+
|
39
|
+
Other forms are as follows:
|
40
|
+
|
41
|
+
r1 = Rangesmaller.new((r0=(1..5)), :exclude_begin => false)
|
42
|
+
r2 = Rangesmaller.new(r1)
|
43
|
+
r3 = Rangesmaller.new(1, 5, exclude_end=false) # This form is not recomended (only for compatibility with Range).
|
44
|
+
r4 = Rangesmaller.new(1, 5, :exclude_begin => true, :exclude_end => false)
|
45
|
+
r5 = Rangesmaller( 1, 5, :exclude_begin => true)
|
46
|
+
r7 = Rangesmaller.new((?a...?z), :exclude_begin => true)
|
47
|
+
r8 = Rangesmaller.new(r0, :exclude_end => true) # => warning (r0.exclude_end? is used)
|
48
|
+
|
49
|
+
Note: in the last example of new(), it issues a warning.
|
50
|
+
In this case a Range object is given in new(); accordingly the status of :exclude_end? of the created Rangesmaller object is inherited from the given Range object, whereas the option given contradicts to it. What you should do instead is,
|
51
|
+
|
52
|
+
r8 = Rangesmaller.new(r0.begin, r0.end, :exclude_end => true)
|
53
|
+
|
54
|
+
They are evaluated as follows:
|
55
|
+
|
56
|
+
r1 == r0 # => true
|
57
|
+
r1 == r2 # => true
|
58
|
+
r1 == r3 # => true
|
59
|
+
r1 == r8 # => true
|
60
|
+
r1 == r4 # => false
|
61
|
+
r4 == r5 # => true
|
62
|
+
|
63
|
+
r1.exclude_end? # => false
|
64
|
+
r1.exclude_begin? # => false
|
65
|
+
r4.exclude_begin? # => true
|
66
|
+
r1.rangepart # => (1..5)
|
67
|
+
r4.rangepart # => (1..5)
|
68
|
+
|
69
|
+
=== General operation
|
70
|
+
|
71
|
+
s1 = Rangesmaller(1..5, :exclude_begin => true) # => 1<...5
|
72
|
+
s1.size # => 3
|
73
|
+
s1.to_a # => [2, 3, 4]
|
74
|
+
s1.begin # => 1
|
75
|
+
s1.first(2) # => [2, 3]
|
76
|
+
s1.max{|a,b| -a<=>-b} # => 2
|
77
|
+
|
78
|
+
|
79
|
+
== Known bugs
|
80
|
+
|
81
|
+
* hash() method does not always guarantee to creat a unique number for the equal Rangesmall object, though such an exception is extremely unlikely to happen.
|
82
|
+
* No thorough test for Ruby 1.8 or earlier has been done, although there is no known fault.
|
83
|
+
|
84
|
+
|
85
|
+
== Copyright etc
|
86
|
+
|
87
|
+
Author:: Masa Sakano < imagine a_t sakano dot co dot uk >
|
88
|
+
License:: The same as Ruby. www.ruby-lang.org
|
89
|
+
Warranty:: No warranty whatsoever.
|
90
|
+
|
91
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,561 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# =Class Rangesmaller
|
4
|
+
#
|
5
|
+
# Authors:: Masa Sakano
|
6
|
+
# License:: MIT
|
7
|
+
#
|
8
|
+
# ==Summary
|
9
|
+
#
|
10
|
+
# Range with inclusion of include_begin?().
|
11
|
+
#
|
12
|
+
# Namely, the first element can be tagged as excluded, if specified so.
|
13
|
+
# It behaves the same as Range. Note the method first() (hence each(), size() etc) behaves accordingly, depending whether the smallest boundary is excluded or not (default).
|
14
|
+
#
|
15
|
+
# ==Examples
|
16
|
+
#
|
17
|
+
# An instance of a range of 5 to 8 with both ends being exclusive is created as
|
18
|
+
#
|
19
|
+
# r = Rangesmaller(5...8, :exclude_begin => true)
|
20
|
+
# r.exclude_begin? # => true
|
21
|
+
#
|
22
|
+
class Rangesmaller < Range
|
23
|
+
|
24
|
+
# @overload new(rangesmaller)
|
25
|
+
# @param [Rangesmaller] key describe key param
|
26
|
+
#
|
27
|
+
# @overload new(range, opts)
|
28
|
+
# @param [Range] standard Range object
|
29
|
+
# @option opts [Object] :exclude_begin the begin boundary is excluded, if false (Default)
|
30
|
+
#
|
31
|
+
# @overload new(begin, end, exclude_end=false)
|
32
|
+
# This form is not recomended, but for the sake of compatibility with Range.
|
33
|
+
# @param [Object] Any object (preferably {#Comparable})
|
34
|
+
# @param [Object] Any object, compatible with begin
|
35
|
+
# @param [Object] true or false(Default) or any.
|
36
|
+
#
|
37
|
+
# @overload new(begin, end, opts)
|
38
|
+
# @param [Object] Any object (preferably {#Comparable})
|
39
|
+
# @param [Object] Any object, compatible with begin
|
40
|
+
# @param [Hash] see below
|
41
|
+
# @option opts [Object] :exclude_end the end boundary is excluded, if false (Default)
|
42
|
+
# @option opts [Object] :exclude_begin the begin boundary is excluded, if false (Default)
|
43
|
+
#
|
44
|
+
# @note The above-mentioned "any" object means any object that can consist of Range.
|
45
|
+
# For example, (nil..nil) is accepted, but (Complex(2,3)..Complex(2,3)) is not.
|
46
|
+
#
|
47
|
+
def initialize(*inar)
|
48
|
+
|
49
|
+
@rangepart = nil # Defined here only if Range is given as
|
50
|
+
# (a part of) the arguments, and in that case it will be
|
51
|
+
# the same object. If not, left undefined,
|
52
|
+
# but will be defined once rangepart() is called.
|
53
|
+
# Note: it is entirely possible to set @rangepart here if wanted,
|
54
|
+
# but there is no point to do that, providing no method
|
55
|
+
# uses @rangepart explicitly, but only via rangepart().
|
56
|
+
@exclude_begin = false # Default
|
57
|
+
|
58
|
+
case inar.size
|
59
|
+
when 1 # Arg: (Rangesmaller or Range)
|
60
|
+
r = inar[0]
|
61
|
+
if defined?(r.rangepart) && defined?(r.exclude_begin?)
|
62
|
+
# Rangesmaller
|
63
|
+
@exclude_begin = r.exclude_begin?
|
64
|
+
@rangepart = r.rangepart
|
65
|
+
r = @rangepart
|
66
|
+
super(r.begin, r.end, r.exclude_end?)
|
67
|
+
else
|
68
|
+
# Range
|
69
|
+
if defined?(r.begin) && defined?(r.end) && defined?(r.exclude_end?)
|
70
|
+
super(r.begin, r.end, r.exclude_end?)
|
71
|
+
@rangepart = r
|
72
|
+
else
|
73
|
+
raise ArgumentError, "bad (first) value for Rangesmaller"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
when 2
|
78
|
+
r = inar[0]
|
79
|
+
if defined? r.exclude_end?
|
80
|
+
# Arg: (Range, :exclude_begin => false)
|
81
|
+
begin
|
82
|
+
@exclude_begin = inar[1][:exclude_begin]
|
83
|
+
rescue
|
84
|
+
# cf., Rangesmaller.new(nil,5) # => ArgumentError: bad value for range
|
85
|
+
raise ArgumentError, "bad (second) value for Rangesmaller"
|
86
|
+
end
|
87
|
+
|
88
|
+
if defined?(r.begin) && defined?(r.end) # && defined?(r.exclude_end?)
|
89
|
+
super(r.begin, r.end, r.exclude_end?)
|
90
|
+
@rangepart = r
|
91
|
+
begin
|
92
|
+
if inar[1].has_key?(:exclude_end)
|
93
|
+
if inar[1][:exclude_end] != r.exclude_end?
|
94
|
+
warn "Warning(Rangesmaller.new): Option :exclude_end is given, but is meaningless, as Range is also given."
|
95
|
+
end
|
96
|
+
end
|
97
|
+
rescue
|
98
|
+
# Very unlikely.
|
99
|
+
# The above is just a warning, hence no exception should be raised here, whatever the reason.
|
100
|
+
end
|
101
|
+
else # Very unlikely, but possible.
|
102
|
+
raise ArgumentError, "bad (first) value for Rangesmaller"
|
103
|
+
end
|
104
|
+
else # Arg: (Num(Begin), Num(End))
|
105
|
+
super
|
106
|
+
end
|
107
|
+
|
108
|
+
when 3
|
109
|
+
flag_end = false
|
110
|
+
begin
|
111
|
+
@exclude_begin = inar[2][:exclude_begin]
|
112
|
+
|
113
|
+
if inar[2].has_key?(:exclude_end)
|
114
|
+
flag_end = true
|
115
|
+
end
|
116
|
+
rescue NoMethodError, TypeError # TypeError can be raised if the third argument (inar[2]) is an array or Integer or alike.
|
117
|
+
# Likely conventional argument list as in Range.new().
|
118
|
+
super # Arg: (begin, end, exclude_end=false)
|
119
|
+
else
|
120
|
+
if flag_end
|
121
|
+
super(inar[0], inar[1], inar[2][:exclude_end]) # Arg: (begin, end, :exclude_end => SOMETHING, :exclude_begin => false)
|
122
|
+
else
|
123
|
+
super(inar[0], inar[1]) # Arg: (begin, end, opt_without_exclude_end)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
else
|
128
|
+
raise ArgumentError, "wrong number of arguments (#{inar.size} for 1..3)"
|
129
|
+
end # case inar.size
|
130
|
+
|
131
|
+
# Adjust @exclude_begin (as it can be anything).
|
132
|
+
if @exclude_begin
|
133
|
+
@exclude_begin = true
|
134
|
+
else
|
135
|
+
@exclude_begin = false
|
136
|
+
end
|
137
|
+
|
138
|
+
end # def initialize(*inar)
|
139
|
+
|
140
|
+
|
141
|
+
# Returns false if the "begin" boundary is excluded, or true otherwise.
|
142
|
+
# @return [TrueClass]
|
143
|
+
# @return [FalseClass]
|
144
|
+
def exclude_begin?
|
145
|
+
@exclude_begin
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
# Return the object equivalent to the Range part (namely, without the definition of exclude_begin?)
|
150
|
+
# @return [Range]
|
151
|
+
def rangepart
|
152
|
+
@rangepart ||= Range.new(self.begin, self.end, self.exclude_end?)
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
# Like Range, returns true only if it is either {#Rangesmaller} or {#Range}, and in addition if both {#exclude_begin?} and {#exclude_end?} match between the two objects.
|
157
|
+
def ==(r)
|
158
|
+
if defined? r.rangepart
|
159
|
+
rangepart = r.rangepart
|
160
|
+
else
|
161
|
+
rangepart = r
|
162
|
+
end
|
163
|
+
|
164
|
+
if super(rangepart) # Check the Range part, ie., ignoring exclude_begin?
|
165
|
+
if defined? r.exclude_begin?
|
166
|
+
(@exclude_begin == r.exclude_begin?) # && (exclude_end? == r.exclude_end?)
|
167
|
+
else
|
168
|
+
! @exclude_begin
|
169
|
+
end
|
170
|
+
else
|
171
|
+
false
|
172
|
+
end
|
173
|
+
end # def ==(r)
|
174
|
+
|
175
|
+
alias :eql? :==
|
176
|
+
|
177
|
+
|
178
|
+
# @see {#cover?} and {#Range#===}
|
179
|
+
def ===(obj)
|
180
|
+
# ("a".."z")===("cc") # => false
|
181
|
+
begin
|
182
|
+
1.0+(obj) # OK if Numeric.
|
183
|
+
|
184
|
+
rescue TypeError
|
185
|
+
# obj is not Numeric, hence runs brute-force check.
|
186
|
+
each do |ei|
|
187
|
+
if ei == obj
|
188
|
+
return true
|
189
|
+
end
|
190
|
+
end
|
191
|
+
false
|
192
|
+
|
193
|
+
else
|
194
|
+
cover?(obj)
|
195
|
+
end
|
196
|
+
end # def ===(obj)
|
197
|
+
|
198
|
+
alias :include? :===
|
199
|
+
alias :member? :===
|
200
|
+
|
201
|
+
|
202
|
+
# @note Comment on Rangesmaller#bsearch() :
|
203
|
+
# bsearch is internally implemented by converting a float into 64-bit integer.
|
204
|
+
# The following examples demonstrate what is going on.
|
205
|
+
#
|
206
|
+
# ary = [0, 4, 7, 10, 12]
|
207
|
+
# (3...4).bsearch{ |i| ary[i] >= 11} # => nil
|
208
|
+
# (3...5).bsearch{ |i| ary[i] >= 11} # => 4 (Integer)
|
209
|
+
# (3..5.1).bsearch{ |i| ary[i] >= 11} # => 4.0 (Float)
|
210
|
+
# (3.6..4).bsearch{ |i| ary[i] >= 11} # => 4.0 (Float)
|
211
|
+
# (3.6...4).bsearch{ |i| ary[i] >= 11} # => nil
|
212
|
+
# (3.6...4.1).bsearch{|i| ary[i] >= 11} # => 4.0 (Float)
|
213
|
+
#
|
214
|
+
# class Special
|
215
|
+
# def [](f)
|
216
|
+
# (f>3.5 && f<4) ? true : false
|
217
|
+
# end
|
218
|
+
# end
|
219
|
+
# sp = special.new
|
220
|
+
# (3..4).bsearch{ |i| sp[i]} # => nil
|
221
|
+
# (3...4).bsearch{ |i| sp[i]} # => nil
|
222
|
+
# (3.0...4).bsearch{|i| sp[i]} # => 3.5000000000000004
|
223
|
+
# (3...4.0).bsearch{|i| sp[i]} # => 3.5000000000000004
|
224
|
+
# (3.3..4).bsearch{ |i| sp[i]} # => 3.5000000000000004
|
225
|
+
#
|
226
|
+
# (Rational(36,10)..5).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Rational (Ruby 2.1)
|
227
|
+
# (3..Rational(61,10)).bsearch{|i| ary[i] >= 11} => # TypeError: can't do binary search for Fixnum (Ruby 2.1)
|
228
|
+
#
|
229
|
+
# In short, bsearch works only with Integer and/or Float (as in Ruby 2.1).
|
230
|
+
# If either of begin and end is an Float, the search is conducted in Float and the returned value will be Float, unless nil.
|
231
|
+
# If Float, it searches on the binary plane.
|
232
|
+
# If Integer, the search is conducted on the descrete Integer points only,
|
233
|
+
# and no search will be made in between the adjascent integers.
|
234
|
+
#
|
235
|
+
# Given that, Rangesmaller#bsearch follows basically the same, even when exclude_begin? is true.
|
236
|
+
# If either end is Float, it searches between begin*(1+Float::EPSILON) and end.
|
237
|
+
# If both are Integer, it searches from begin+1.
|
238
|
+
# When exclude_begin? is false, Rangesmaller#bsearch is identical to Range#bsearch.
|
239
|
+
#
|
240
|
+
def bsearch(*rest, &bloc)
|
241
|
+
if @exclude_begin
|
242
|
+
if ((Float === self.begin()) ||
|
243
|
+
(Integer === self.begin()) && (Float === self.end()))
|
244
|
+
Range.new(self.begin()*(Float::EPSILON+1.0), self.end, exclude_end?).send(__method__, *rest, &bloc)
|
245
|
+
# @note Technically, if begin is Rational, there is no strong reason it should not work.
|
246
|
+
# However Range#bsearch does not accept Rational, hence this code.
|
247
|
+
# Users should give a Rangesmaller with begin being Rational.to_f in that case.
|
248
|
+
elsif (defined? self.begin().succ) # Both non-Float
|
249
|
+
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.
|
250
|
+
else
|
251
|
+
rangepart.send(__method__, *rest, &bloc) # It will raise an exception anyway! Such as, (Rational..Rational)
|
252
|
+
end
|
253
|
+
else
|
254
|
+
super
|
255
|
+
end
|
256
|
+
end # def bsearch(*rest, &bloc)
|
257
|
+
|
258
|
+
|
259
|
+
# @see {#include?} or {#===}, and {#Range#cover?}
|
260
|
+
def cover?(i)
|
261
|
+
# ("a".."z").cover?("cc") # => true
|
262
|
+
if @exclude_begin
|
263
|
+
if super
|
264
|
+
if self.begin == i
|
265
|
+
false
|
266
|
+
else
|
267
|
+
true
|
268
|
+
end
|
269
|
+
else
|
270
|
+
false
|
271
|
+
end
|
272
|
+
else
|
273
|
+
super
|
274
|
+
end
|
275
|
+
end # def cover?(i)
|
276
|
+
|
277
|
+
|
278
|
+
# @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.
|
279
|
+
#
|
280
|
+
def each(*rest, &bloc)
|
281
|
+
# (1...3.5).each{|i|print i} # => '123' to STDOUT
|
282
|
+
# (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
|
283
|
+
# (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
|
284
|
+
if @exclude_begin
|
285
|
+
if defined? self.begin.succ
|
286
|
+
Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc)
|
287
|
+
else
|
288
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
289
|
+
# if block_given?
|
290
|
+
end
|
291
|
+
else
|
292
|
+
super
|
293
|
+
end
|
294
|
+
self
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
# @param [Numeric] Optional. Must be non-negative. Consult {#Range#first} for detail.
|
299
|
+
# @note Like {#Range#last}, if no argument is given, it behaves like {#begin()}, that is, it returns the initial value, regardless of {#exclude_begin?}.
|
300
|
+
# 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}.
|
301
|
+
# @raise [TypeError] if the argument (Numeric) is given, yet if {#begin()}.{#succ} is not defined.
|
302
|
+
#
|
303
|
+
def first(*rest)
|
304
|
+
# (1...3.1).last # => 3.1
|
305
|
+
# (1...3.1).last(1) # => [3]
|
306
|
+
if ! @exclude_begin
|
307
|
+
super
|
308
|
+
else
|
309
|
+
case rest.size
|
310
|
+
when 0
|
311
|
+
self.begin
|
312
|
+
when 1
|
313
|
+
## Check the argument.
|
314
|
+
Array.new[ rest[0] ] # Check Type of rest[0] (if invalid, it should cause TypeError)
|
315
|
+
|
316
|
+
begin
|
317
|
+
if rest[0] < 0
|
318
|
+
raise ArgumentError, "negative array size (or size too big)"
|
319
|
+
end
|
320
|
+
rescue NoMethodError
|
321
|
+
# Should not happen, but just to play safe.
|
322
|
+
end
|
323
|
+
|
324
|
+
if (RUBY_VERSION < "1.9.1") && (1 == rest[0])
|
325
|
+
flag_18 = true
|
326
|
+
else
|
327
|
+
flag_18 = false
|
328
|
+
end
|
329
|
+
|
330
|
+
## Main
|
331
|
+
begin
|
332
|
+
b = self.begin.succ
|
333
|
+
rescue NoMethodError
|
334
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
335
|
+
end
|
336
|
+
|
337
|
+
begin
|
338
|
+
Range.new(b, self.end, exclude_end?).send(__method__, *rest)
|
339
|
+
rescue ArgumentError => err
|
340
|
+
if flag_18 # first() does not accept an argument in Ruby 1.8.
|
341
|
+
Range.new(b, self.end, exclude_end?).send(__method__)
|
342
|
+
else
|
343
|
+
raise err
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
else
|
348
|
+
raise ArgumentError, "wrong number of arguments (#{rest.size} for 2..3)"
|
349
|
+
end
|
350
|
+
end # if ! @exclude_begin
|
351
|
+
end # def first(*rest)
|
352
|
+
|
353
|
+
|
354
|
+
# @note When {#exclude_begin?} == true, the returned value for (a<...b) is not strictly guaranteed to be unique, though in pracrtice it is most likely to be so.
|
355
|
+
#
|
356
|
+
def hash(*rest)
|
357
|
+
if @exclude_begin
|
358
|
+
Range.new(self.begin, self.end, exclude_end?).send(__method__, *rest) - 1
|
359
|
+
else
|
360
|
+
super
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
def inspect
|
366
|
+
if @exclude_begin
|
367
|
+
if exclude_end?
|
368
|
+
midStr = "<..."
|
369
|
+
else
|
370
|
+
midStr = "<.."
|
371
|
+
end
|
372
|
+
self.begin.to_s + midStr + self.end.to_s
|
373
|
+
else
|
374
|
+
super
|
375
|
+
end
|
376
|
+
end
|
377
|
+
alias :to_s :inspect
|
378
|
+
|
379
|
+
|
380
|
+
# @see {#first} for the definition when {#exclude_begin?} is true.
|
381
|
+
#
|
382
|
+
def min(*rest, &bloc)
|
383
|
+
# (1...3.5).max # => TypeError: cannot exclude non Integer end value
|
384
|
+
if @exclude_begin
|
385
|
+
begin
|
386
|
+
first_val = self.begin.succ
|
387
|
+
rescue NoMethodError
|
388
|
+
raise TypeError, 'cannot exclude non Integer begin value'
|
389
|
+
end
|
390
|
+
Range.new(first_val, self.end, exclude_end?).send(__method__, *rest, &bloc)
|
391
|
+
else
|
392
|
+
super
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
|
397
|
+
# @see {#first} for the definition when {#exclude_begin?} is true.
|
398
|
+
#
|
399
|
+
def min_by(*rest, &bloc)
|
400
|
+
# (1...3.5).max # => TypeError: cannot exclude non Integer end value
|
401
|
+
if @exclude_begin
|
402
|
+
begin
|
403
|
+
first_val = self.begin.succ
|
404
|
+
rescue NoMethodError
|
405
|
+
raise TypeError, 'cannot exclude non Integer begin value'
|
406
|
+
end
|
407
|
+
Range.new(first_val, self.end, exclude_end?).send(__method__, *rest, &bloc)
|
408
|
+
else
|
409
|
+
super
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
|
414
|
+
# @see {#first} for the definition when {#exclude_begin?} is true.
|
415
|
+
#
|
416
|
+
def minmax(*rest, &bloc)
|
417
|
+
# (0...3.5).minmax # => [0, 3]
|
418
|
+
# (1.3...5).minmax # => TypeError: can't iterate from Float
|
419
|
+
# Note that max() for the same Range raises an exception.
|
420
|
+
# In that sense, it is inconsistent!
|
421
|
+
if @exclude_begin
|
422
|
+
begin
|
423
|
+
first_val = self.begin.succ
|
424
|
+
rescue NoMethodError
|
425
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
426
|
+
# Trap it, just in order to issue the error message that is consisntent with max().
|
427
|
+
# raise TypeError, 'cannot exclude non Integer begin value'
|
428
|
+
end
|
429
|
+
Range.new(first_val, self.end, exclude_end?).send(__method__, *rest, &bloc)
|
430
|
+
else
|
431
|
+
super
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
|
436
|
+
# @see {#first} for the definition when {#exclude_begin?} is true.
|
437
|
+
#
|
438
|
+
def minmax_by(*rest, &bloc)
|
439
|
+
# (0...3.5).minmax # => [0, 3]
|
440
|
+
# Note that max() for the same Range raises an exception.
|
441
|
+
# In that sense, it is inconsistent!
|
442
|
+
if @exclude_begin
|
443
|
+
begin
|
444
|
+
first_val = self.begin.succ
|
445
|
+
rescue NoMethodError
|
446
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
447
|
+
# Trap it, just in order to issue the error message that is consisntent with max().
|
448
|
+
# raise TypeError, 'cannot exclude non Integer begin value'
|
449
|
+
end
|
450
|
+
Range.new(first_val, self.end, exclude_end?).send(__method__, *rest, &bloc)
|
451
|
+
else
|
452
|
+
super
|
453
|
+
end
|
454
|
+
end # def minmax_by(*rest)
|
455
|
+
|
456
|
+
|
457
|
+
# @see {#first} for the definition when {#exclude_begin?} is true.
|
458
|
+
# @see [ruby-list:49797] from matz for how {#size} behaves.
|
459
|
+
#
|
460
|
+
def size(*rest)
|
461
|
+
# (1..5).size # => 5
|
462
|
+
# (1...5).size # => 4
|
463
|
+
# (0.8...5).size # => 5 # Why???
|
464
|
+
# (1.2...5).size # => 4 # Why???
|
465
|
+
# (1.2..5).size # => 4 # Why???
|
466
|
+
# (Rational(3,2)...5).size => 3
|
467
|
+
# (1.5...5).size # => 4 # Why not 3??
|
468
|
+
# (1.5...4.9).size # => 4 # Why not 3??
|
469
|
+
# (1.5...4.5).size # => 3
|
470
|
+
# (0...Float::INFINITY).size # => Infinity
|
471
|
+
|
472
|
+
if @exclude_begin
|
473
|
+
# Dealing with Infinity.
|
474
|
+
begin
|
475
|
+
inf = Float::INFINITY
|
476
|
+
rescue # Ruby 1.8 or earlier.
|
477
|
+
inf = 1/0.0
|
478
|
+
end
|
479
|
+
if -inf == self.begin
|
480
|
+
if -inf == self.end
|
481
|
+
return 0
|
482
|
+
else
|
483
|
+
return inf
|
484
|
+
end
|
485
|
+
elsif inf == self.begin
|
486
|
+
if inf == self.end
|
487
|
+
return 0
|
488
|
+
else
|
489
|
+
return inf
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
begin
|
494
|
+
1.0 + self.begin()
|
495
|
+
# Numeric
|
496
|
+
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.
|
497
|
+
rescue TypeError
|
498
|
+
# Non-Numeric
|
499
|
+
Range.new(self.begin().succ, self.end, exclude_end?).send(__method__, *rest) # => nil in Ruby 2.1
|
500
|
+
end
|
501
|
+
|
502
|
+
else
|
503
|
+
super
|
504
|
+
end
|
505
|
+
end # def size
|
506
|
+
|
507
|
+
|
508
|
+
# @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.
|
509
|
+
# @see {#each}.
|
510
|
+
#
|
511
|
+
def step(*rest, &bloc)
|
512
|
+
# (1...3.5).each{|i|print i} # => '123' to STDOUT
|
513
|
+
# (1.3...3.5).each # => #<Enumerator: 1.3...3.5:each>
|
514
|
+
# (1.3...3.5).each{|i|print i} # => TypeError: can't iterate from Float
|
515
|
+
if @exclude_begin
|
516
|
+
if defined? self.begin.succ
|
517
|
+
Range.new(self.begin.succ,self.end,exclude_end?).send(__method__, *rest, &bloc)
|
518
|
+
else
|
519
|
+
raise TypeError, "can't iterate from "+self.begin.class.name
|
520
|
+
# if block_given?
|
521
|
+
end
|
522
|
+
else
|
523
|
+
super
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
|
528
|
+
end # class Rangesmaller < Range
|
529
|
+
|
530
|
+
|
531
|
+
#
|
532
|
+
#=== Redefine a method {#==} so as to handle {#Rangesmaller}.
|
533
|
+
#
|
534
|
+
class Range
|
535
|
+
|
536
|
+
alias :eql_orig? :==
|
537
|
+
|
538
|
+
def eql?(r)
|
539
|
+
if defined? r.exclude_begin?
|
540
|
+
if r.exclude_begin?
|
541
|
+
false
|
542
|
+
else
|
543
|
+
eql_orig?(r)
|
544
|
+
end
|
545
|
+
else
|
546
|
+
eql_orig?(r)
|
547
|
+
end
|
548
|
+
end # def :eql?(r)
|
549
|
+
alias :== :eql?
|
550
|
+
|
551
|
+
end # class Range
|
552
|
+
|
553
|
+
|
554
|
+
# Constant-form of {#Rangesmaller}.
|
555
|
+
# {#Rangesmaller(*)} is equivalent to {#Rangesmaller.new}.
|
556
|
+
#
|
557
|
+
def Rangesmaller(*rest, &b)
|
558
|
+
Rangesmaller.new(*rest, &b)
|
559
|
+
end
|
560
|
+
|
561
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{rangesmaller}
|
5
|
+
s.version = "1.0.0"
|
6
|
+
# s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
7
|
+
# s.executables << 'hola'
|
8
|
+
# s.bindir = 'bin'
|
9
|
+
s.authors = ["Masa Sakano"]
|
10
|
+
s.date = %q{2014-04-22}
|
11
|
+
s.summary = %q{Rangesmaller class -- Range with include_begin?()}
|
12
|
+
s.description = %q{This defines a subclass of Range, Rangesmaller class. Users can define whether the begin boundary is exclusive or inclusive, in addition to the end boundary as in the standard Range.}
|
13
|
+
# s.email = %q{abc@example.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
# "LICENSE",
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.license = 'MIT'
|
19
|
+
s.files = [
|
20
|
+
#".document",
|
21
|
+
#".gitignore",
|
22
|
+
#"VERSION",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"rangesmaller.gemspec",
|
26
|
+
"lib/rangesmaller/rangesmaller.rb",
|
27
|
+
"test/test_rangesmaller.rb",
|
28
|
+
]
|
29
|
+
# s.homepage = %q{http://}
|
30
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
31
|
+
# s.require_paths = ["lib"]
|
32
|
+
# s.required_ruby_version = '>= 1.8.6'
|
33
|
+
s.test_files = [
|
34
|
+
"test/test_rangesmaller.rb",
|
35
|
+
]
|
36
|
+
# s.test_files = Dir.glob('test/tc_*.rb')
|
37
|
+
# s.requirements << 'libmagick, v6.0' # Simply, info to users.
|
38
|
+
# s.rubygems_version = %q{1.3.5} # This is always set automatically!!
|
39
|
+
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,522 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
libfilebase = 'rangesmaller.rb'
|
4
|
+
libfilepath = nil
|
5
|
+
er=nil
|
6
|
+
pathcand = %w(../lib/rangesmaller/ lib/rangesmaller/ rangesmaller/) + ['']
|
7
|
+
pathcand.each do |dir|
|
8
|
+
begin
|
9
|
+
s = dir+libfilebase
|
10
|
+
require s
|
11
|
+
libfilepath = s
|
12
|
+
break
|
13
|
+
rescue LoadError => er
|
14
|
+
end
|
15
|
+
end
|
16
|
+
if libfilepath.nil?
|
17
|
+
raise er
|
18
|
+
end
|
19
|
+
|
20
|
+
printf "NOTE: Library relative path: %s\n", libfilepath
|
21
|
+
print "NOTE: Library full path: "
|
22
|
+
p $LOADED_FEATURES.grep(/#{Regexp.quote(libfilebase)}$/)
|
23
|
+
|
24
|
+
#################################################
|
25
|
+
# Unit Test
|
26
|
+
#################################################
|
27
|
+
|
28
|
+
#if $0 == __FILE__
|
29
|
+
require 'minitest/unit'
|
30
|
+
require 'minitest/autorun'
|
31
|
+
# MiniTest::Unit.autorun
|
32
|
+
|
33
|
+
# Taken from ((<URL:http://www.ruby-doc.org/core-2.1.1/Range.html>))
|
34
|
+
class Xs # represent a string of 'x's
|
35
|
+
include Comparable
|
36
|
+
attr :length
|
37
|
+
def initialize(n)
|
38
|
+
@length = n
|
39
|
+
end
|
40
|
+
def succ
|
41
|
+
Xs.new(@length + 1)
|
42
|
+
end
|
43
|
+
def <=>(other)
|
44
|
+
@length <=> other.length
|
45
|
+
end
|
46
|
+
def to_s
|
47
|
+
sprintf "%2d #{inspect}", @length
|
48
|
+
end
|
49
|
+
def inspect
|
50
|
+
'x' * @length
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Used in test_bsearch_special
|
55
|
+
class Special
|
56
|
+
def [](f)
|
57
|
+
(f>3.5 && f<4) ? true : false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class TestFoo < MiniTest::Unit::TestCase
|
62
|
+
def setup
|
63
|
+
@ib = 1
|
64
|
+
@ie = 6
|
65
|
+
@r11 = (@ib..@ie) # incl, incl
|
66
|
+
@r12 = (@ib...@ie) # incl, excl
|
67
|
+
@s11 = Rangesmaller.new(@r11, :exclude_begin => false) # (1..6) incl, incl
|
68
|
+
@s21 = Rangesmaller.new(@r11, :exclude_begin => true) # (1<..6) excl, incl
|
69
|
+
@s12 = Rangesmaller.new(@r12, :exclude_begin => false) # (1...6) incl, excl
|
70
|
+
@s22 = Rangesmaller.new(@r12, :exclude_begin => true) # (1<...6) excl, excl
|
71
|
+
end
|
72
|
+
# teardown is not often used.
|
73
|
+
def teardown
|
74
|
+
@foo = nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_new
|
78
|
+
e22 = Rangesmaller.new(@s22)
|
79
|
+
assert e22.exclude_begin?
|
80
|
+
assert e22.exclude_end?
|
81
|
+
assert_equal @ib, e22.begin
|
82
|
+
assert_equal @ie, e22.end
|
83
|
+
|
84
|
+
f12 = Rangesmaller.new(@r12)
|
85
|
+
assert !f12.exclude_begin?
|
86
|
+
assert f12.exclude_end?
|
87
|
+
assert_equal @ib, f12.begin
|
88
|
+
assert_equal @ie, f12.end
|
89
|
+
|
90
|
+
g11 = Rangesmaller.new(@ib, @ie)
|
91
|
+
assert !g11.exclude_begin?
|
92
|
+
assert !g11.exclude_end?
|
93
|
+
assert_equal @ib, g11.begin
|
94
|
+
assert_equal @ie, g11.end
|
95
|
+
|
96
|
+
h12 = Rangesmaller.new(@ib, @ie, true)
|
97
|
+
assert !h12.exclude_begin?
|
98
|
+
assert h12.exclude_end?
|
99
|
+
assert_equal @ib, h12.begin
|
100
|
+
assert_equal @ie, h12.end
|
101
|
+
|
102
|
+
j22 = Rangesmaller.new(-@ie, -@ib, :exclude_end => true, :exclude_begin => true)
|
103
|
+
assert j22.exclude_begin?
|
104
|
+
assert j22.exclude_end?
|
105
|
+
assert_equal(-@ie, j22.begin)
|
106
|
+
assert_equal(-@ib, j22.end)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_new_const
|
110
|
+
assert_equal @s22, Rangesmaller(@r12, :exclude_begin => true)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_exclude_begin
|
114
|
+
b = 1
|
115
|
+
e = 4
|
116
|
+
r1 = (b...e)
|
117
|
+
assert !Rangesmaller.new(r1).exclude_begin?
|
118
|
+
assert Rangesmaller.new(r1).exclude_end?
|
119
|
+
assert Rangesmaller.new(r1, :exclude_begin => 5).exclude_begin?
|
120
|
+
assert Rangesmaller.new(r1, :exclude_begin => 5).exclude_end?
|
121
|
+
assert !Rangesmaller.new(b,e).exclude_begin?
|
122
|
+
assert !Rangesmaller.new(b,e).exclude_end?
|
123
|
+
assert !Rangesmaller.new(b,e,nil).exclude_begin?
|
124
|
+
assert !Rangesmaller.new(b,e,nil).exclude_end?
|
125
|
+
assert Rangesmaller.new(b,e,'c').exclude_end?
|
126
|
+
assert !Rangesmaller.new(b,e,:a =>5).exclude_begin?
|
127
|
+
assert !Rangesmaller.new(b,e,:exclude_end => false, :exclude_begin => false).exclude_begin?
|
128
|
+
assert !Rangesmaller.new(b,e,:exclude_end => false, :exclude_begin => false).exclude_end?
|
129
|
+
assert !Rangesmaller.new(b,e,:exclude_end => true, :exclude_begin => false).exclude_begin?
|
130
|
+
assert Rangesmaller.new(b,e,:exclude_end => true, :exclude_begin => false).exclude_end?
|
131
|
+
assert Rangesmaller.new(b,e,:exclude_end => true, :exclude_begin => true ).exclude_begin?
|
132
|
+
assert Rangesmaller.new(b,e,:exclude_end => true, :exclude_begin => true ).exclude_end?
|
133
|
+
assert Rangesmaller.new(b,e,:exclude_end => false, :exclude_begin => true ).exclude_begin?
|
134
|
+
assert !Rangesmaller.new(b,e,:exclude_end => false, :exclude_begin => true ).exclude_end?
|
135
|
+
|
136
|
+
assert_raises ArgumentError do
|
137
|
+
Rangesmaller.new()
|
138
|
+
end
|
139
|
+
assert_raises ArgumentError do
|
140
|
+
Rangesmaller.new(5)
|
141
|
+
end
|
142
|
+
assert_raises ArgumentError do
|
143
|
+
Rangesmaller.new(nil,5)
|
144
|
+
end
|
145
|
+
assert_raises ArgumentError do
|
146
|
+
Rangesmaller.new(nil,5,true)
|
147
|
+
end
|
148
|
+
assert_raises ArgumentError do
|
149
|
+
Rangesmaller.new(3,5,8,9)
|
150
|
+
end
|
151
|
+
end # def test_exclude_begin
|
152
|
+
|
153
|
+
# Test of Range#== because it has changed first!
|
154
|
+
def test_eql_range
|
155
|
+
assert (@r11 == (@ib..@ie))
|
156
|
+
assert_equal @r11, (@ib..@ie)
|
157
|
+
assert (@r11 != @r12)
|
158
|
+
assert_equal @r12, (@ib...@ie)
|
159
|
+
assert_equal @r11, @s11
|
160
|
+
assert (@r11 != @s12)
|
161
|
+
assert (@r11 != @s21)
|
162
|
+
assert (@r11 != @s22)
|
163
|
+
assert_equal @r12, @s12
|
164
|
+
assert (@r12 != @s11)
|
165
|
+
assert (@r12 != @s21)
|
166
|
+
assert (@r12 != @s22)
|
167
|
+
assert (@r12 != 'K')
|
168
|
+
assert (@r12 != @ib)
|
169
|
+
end # def test_eql_range
|
170
|
+
|
171
|
+
def test_eql
|
172
|
+
t21 = Rangesmaller.new(@ib..@ie, :exclude_begin => true)
|
173
|
+
u11 = Rangesmaller.new(3, @ie, :exclude_begin => false)
|
174
|
+
v11 = Rangesmaller.new(@ib, 4, :exclude_begin => false)
|
175
|
+
v21 = Rangesmaller.new(@ib..4, :exclude_begin => true)
|
176
|
+
v22 = Rangesmaller.new(@ib...4,:exclude_begin => true)
|
177
|
+
|
178
|
+
assert (@s11 == @r11)
|
179
|
+
assert_equal @s11, @r11
|
180
|
+
assert !(@s11 == @r12)
|
181
|
+
assert (@s11 != @r12)
|
182
|
+
assert_equal @s21, t21
|
183
|
+
assert (@s21 == t21)
|
184
|
+
assert (@s21 != @s11)
|
185
|
+
assert (@s21 != @s12)
|
186
|
+
assert (@s21 != @s22)
|
187
|
+
assert (@s12 == @r12)
|
188
|
+
assert @s12.eql?(@r12)
|
189
|
+
assert (@s11 != u11)
|
190
|
+
assert (@s11 != v11)
|
191
|
+
assert (@s21 != v21)
|
192
|
+
assert (@s22 != v22)
|
193
|
+
assert (@s22 != u11)
|
194
|
+
assert (@s22 != 'a')
|
195
|
+
|
196
|
+
sc1 = Rangesmaller.new("D".."F", :exclude_begin => false)
|
197
|
+
sc2 = Rangesmaller.new("D".."F", :exclude_begin => true)
|
198
|
+
assert (sc1 == ("D".."F"))
|
199
|
+
assert (sc1 == Rangesmaller.new(("D".."F")))
|
200
|
+
assert (sc1 != sc2)
|
201
|
+
end # def test_eql
|
202
|
+
|
203
|
+
def test_begin
|
204
|
+
assert_equal(@ib, @s11.begin)
|
205
|
+
assert_equal(@ib, @s12.begin)
|
206
|
+
assert_equal(@ib, @s21.begin)
|
207
|
+
assert_equal(@ib, @s22.begin)
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_end
|
211
|
+
assert_equal(@ie, @s11.end)
|
212
|
+
assert_equal(@ie, @s12.end)
|
213
|
+
assert_equal(@ie, @s21.end)
|
214
|
+
assert_equal(@ie, @s22.end)
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_first
|
218
|
+
# This is the key method of this class.
|
219
|
+
|
220
|
+
# irb> (5...8.9).last(1) # => [8]
|
221
|
+
# irb> (5.2..9).last(1) # => TypeError: can't iterate from Float
|
222
|
+
# irb> (5.2..9).first(1) # => TypeError: can't iterate from Float
|
223
|
+
# irb> (5.2...9).first(1) # => TypeError: can't iterate from Float
|
224
|
+
# irb> Rangesmaller(5.2,9,:exclude_begin=>true).first(1) # => [6] # Not right!
|
225
|
+
#
|
226
|
+
|
227
|
+
assert_equal(@ib, @s11.first)
|
228
|
+
assert_equal(@ib, @s12.first)
|
229
|
+
assert_equal(@ib, @s21.first)
|
230
|
+
assert_equal(@ib, @s22.first)
|
231
|
+
assert_equal(@ib, @s12.first(1)[0])
|
232
|
+
assert_equal(@ib+1, @s21.first(1)[0])
|
233
|
+
assert_equal(@ib+1, @s22.first(1)[0])
|
234
|
+
|
235
|
+
assert_equal(nil, Rangesmaller.new(9, 3, :exclude_begin => true).first(1)[0])
|
236
|
+
|
237
|
+
assert_raises ArgumentError do
|
238
|
+
@s22.first(1, 3)
|
239
|
+
end
|
240
|
+
|
241
|
+
## String
|
242
|
+
sc1 = Rangesmaller.new("D".."F", :exclude_begin => false)
|
243
|
+
sc2 = Rangesmaller.new("D".."F", :exclude_begin => true)
|
244
|
+
assert_equal('D', sc1.first)
|
245
|
+
assert_equal('D', sc2.first)
|
246
|
+
assert_equal('E', sc2.first(1)[0])
|
247
|
+
|
248
|
+
## Arbitrary Class
|
249
|
+
sx1 = Rangesmaller.new((Xs.new(3)..Xs.new(6)), :exclude_begin => true)
|
250
|
+
assert_equal(Xs.new(3), sx1.first)
|
251
|
+
assert_equal(Xs.new(4), sx1.first(1)[0])
|
252
|
+
|
253
|
+
## Float
|
254
|
+
sf1 = Rangesmaller.new((-1.4)..8, :exclude_begin => false)
|
255
|
+
sf2 = Rangesmaller.new((-1.4)..8, :exclude_begin => true)
|
256
|
+
assert_equal(-1.4, sf1.first)
|
257
|
+
assert_equal(-1.4, sf2.first)
|
258
|
+
assert_raises TypeError do
|
259
|
+
sf1.first(1)
|
260
|
+
end
|
261
|
+
assert_raises TypeError do
|
262
|
+
sf2.first(1)
|
263
|
+
end
|
264
|
+
|
265
|
+
## Else
|
266
|
+
sn = Rangesmaller.new(nil..nil, :exclude_begin => true)
|
267
|
+
assert_raises TypeError do
|
268
|
+
sn.first(1)[0]
|
269
|
+
end
|
270
|
+
|
271
|
+
assert_raises ArgumentError do
|
272
|
+
@s22.first(-7) # "negative array size (or size too big)"
|
273
|
+
end
|
274
|
+
assert_raises TypeError do
|
275
|
+
@s22.first('a')
|
276
|
+
end
|
277
|
+
assert_raises TypeError do
|
278
|
+
@s22.first(nil)
|
279
|
+
end
|
280
|
+
end # def test_first
|
281
|
+
|
282
|
+
|
283
|
+
def test_each
|
284
|
+
ns=0; @s11.each{|i| ns+=i}
|
285
|
+
assert_equal(@r11.reduce(:+), ns)
|
286
|
+
ns=0; @s12.each{|i| ns+=i}
|
287
|
+
assert_equal(@r12.reduce(:+), ns)
|
288
|
+
ns=0; @s21.each{|i| ns+=i}
|
289
|
+
assert_equal(((@ib+1)..@ie).reduce(:+), ns)
|
290
|
+
ns=0; @s22.each{|i| ns+=i}
|
291
|
+
assert_equal(((@ib+1)...@ie).reduce(:+), ns)
|
292
|
+
|
293
|
+
## Arbitrary Class
|
294
|
+
sx1 = Rangesmaller.new(Xs.new(3), Xs.new(6), :exclude_begin => true, :exclude_end => true)
|
295
|
+
a=[]; sx1.each{|i| a.push(i)}
|
296
|
+
assert_equal([Xs.new(4), Xs.new(5)], a)
|
297
|
+
end # def test_each
|
298
|
+
|
299
|
+
|
300
|
+
def test_last # Apparently it uses each() internally.
|
301
|
+
assert_equal(@ie, @s11.last)
|
302
|
+
assert_equal(@ie-1, @s12.last(1)[0])
|
303
|
+
assert_equal(@ie, @s21.last)
|
304
|
+
assert_equal(@ie-1, @s22.last(1)[0])
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
def test_include
|
309
|
+
assert @s11.include?(@ib)
|
310
|
+
assert @s11.include?(@ie)
|
311
|
+
assert @s12.include?(@ib)
|
312
|
+
assert !@s12.include?(@ie)
|
313
|
+
assert @s12.include?(@ie-1)
|
314
|
+
assert !@s21.include?(@ib)
|
315
|
+
assert @s21.include?(@ib+1)
|
316
|
+
assert @s21.include?(@ie)
|
317
|
+
assert !@s22.include?(@ib)
|
318
|
+
assert !@s22.include?(@ie)
|
319
|
+
assert (@s11 === @ib)
|
320
|
+
assert !(@s21 === @ib)
|
321
|
+
assert (@s21 === @ib+1)
|
322
|
+
assert !(@s22 === @ie)
|
323
|
+
|
324
|
+
assert (Rangesmaller.new("a", "z") === "c")
|
325
|
+
assert !(Rangesmaller.new("a", "z") === "cc") # Key! (see cover?)
|
326
|
+
assert (@s22 === (@ib+@ie)/2.0+0.1) # Key! (see cover?)
|
327
|
+
assert (Rangesmaller.new("a", "z").member?("c"))
|
328
|
+
assert !(Rangesmaller.new("a", "z").member?("cc"))
|
329
|
+
assert !(Rangesmaller.new("a", "z", :exclude_begin => 777) === "a")
|
330
|
+
assert (Rangesmaller.new("a", "z", :exclude_begin => nil) === "a")
|
331
|
+
assert (Rangesmaller.new("a", "z", :exclude_begin => 777) === "b")
|
332
|
+
end # def test_include
|
333
|
+
|
334
|
+
|
335
|
+
def test_bsearch
|
336
|
+
ary = [0, 4, 7, 10, 12]
|
337
|
+
assert_equal(2, Rangesmaller(0, ary.size).bsearch{|i| ary[i] >= 6})
|
338
|
+
# http://www.ruby-doc.org/core-2.1.1/Range.html#method-i-bsearch
|
339
|
+
|
340
|
+
assert_equal(nil, Rangesmaller(3...4).bsearch{ |i| ary[i] >= 11})
|
341
|
+
assert_equal(nil, Rangesmaller(3.6...4).bsearch{|i| ary[i] >= 11})
|
342
|
+
assert_equal(4, Rangesmaller(3...5).bsearch{ |i| ary[i] >= 11})
|
343
|
+
assert_equal(4.0, Rangesmaller(3...5.1).bsearch{|i| ary[i] >= 11})
|
344
|
+
assert_equal(nil, Rangesmaller(3.6...4).bsearch{|i| ary[i] >= 11})
|
345
|
+
|
346
|
+
assert_equal(4, Rangesmaller(3...5, :exclude_begin => 1).bsearch{ |i| ary[i] >= 11})
|
347
|
+
assert_equal(nil, Rangesmaller(4...5, :exclude_begin => 1).bsearch{ |i| ary[i] >= 11})
|
348
|
+
assert_equal(4.0, Rangesmaller(3...5.1, :exclude_begin => 1).bsearch{|i| ary[i] >= 11})
|
349
|
+
assert_equal(nil, Rangesmaller(3.6...4, :exclude_begin => 1).bsearch{|i| ary[i] >= 11})
|
350
|
+
|
351
|
+
assert_raises TypeError do
|
352
|
+
Rangesmaller.new((?a..?b), :exclude_begin => true).bsearch{|i| ary[i] >= 11}
|
353
|
+
end
|
354
|
+
end # def test_bsearch
|
355
|
+
|
356
|
+
def test_bsearch_special
|
357
|
+
sp = Special.new
|
358
|
+
|
359
|
+
# Standard Range
|
360
|
+
assert_equal(nil, Rangesmaller(3..4).bsearch{ |i| sp[i]})
|
361
|
+
assert_equal(nil, Rangesmaller(3...4).bsearch{|i| sp[i]})
|
362
|
+
assert(1e-8 > (Rangesmaller(3.0...4).bsearch{|i| sp[i]} - 3.5).abs)
|
363
|
+
assert(1e-8 > (Rangesmaller(3...4.0).bsearch{|i| sp[i]} - 3.5).abs)
|
364
|
+
assert(1e-8 > (Rangesmaller(3.3..4).bsearch{ |i| sp[i]} - 3.5).abs)
|
365
|
+
|
366
|
+
# Rangesmaller
|
367
|
+
assert(1e-8 > (Rangesmaller(3...4.1, :exclude_begin => 1).bsearch{|i| sp[i]} - 3.5).abs)
|
368
|
+
assert(1e-8 > (Rangesmaller(3.7...4, :exclude_begin => 1).bsearch{|i| sp[i]} - 3.7).abs)
|
369
|
+
assert(1e-8 > (Rangesmaller(3.7...4.2, :exclude_begin => 1).bsearch{|i| sp[i]} - 3.7).abs) # If end is 4.7, it will be nil (presumably due to the algorithm), whereas still 3.7 if 4.5.
|
370
|
+
end # def test_bsearch_special
|
371
|
+
|
372
|
+
|
373
|
+
def test_cover
|
374
|
+
assert @s11.cover?(@ib)
|
375
|
+
assert @s11.cover?(@ie)
|
376
|
+
assert @s12.cover?(@ib)
|
377
|
+
assert !@s12.cover?(@ie)
|
378
|
+
assert @s12.cover?(@ie-1)
|
379
|
+
assert !@s21.cover?(@ib)
|
380
|
+
assert @s21.cover?(@ib+1)
|
381
|
+
assert @s21.cover?(@ie)
|
382
|
+
assert !@s22.cover?(@ib)
|
383
|
+
assert !@s22.cover?(@ie)
|
384
|
+
st = Rangesmaller.new((1.4..7), :exclude_begin => true)
|
385
|
+
assert !(st.cover?(1.4))
|
386
|
+
assert (st.cover?(1.5))
|
387
|
+
|
388
|
+
assert (Rangesmaller.new("a", "z").cover?("c"))
|
389
|
+
assert (Rangesmaller.new("a", "z").cover?("cc")) # Key! (see include?)
|
390
|
+
assert (@s22.cover?((@ib+@ie)/2.0+0.1)) # Key! (see cover?)
|
391
|
+
su = Rangesmaller.new("a", "z", :exclude_begin => 777)
|
392
|
+
sv = Rangesmaller.new("a", "z", :exclude_begin => nil)
|
393
|
+
assert !(su.cover?("a"))
|
394
|
+
assert (sv.cover?("a"))
|
395
|
+
assert (su.cover?("b"))
|
396
|
+
end # def test_cover
|
397
|
+
|
398
|
+
|
399
|
+
def test_hash
|
400
|
+
assert_equal(@r11.hash, @s11.hash)
|
401
|
+
assert_equal(@r12.hash, @s12.hash)
|
402
|
+
assert (@r12.hash != @s22.hash)
|
403
|
+
end # def test_hash
|
404
|
+
|
405
|
+
|
406
|
+
def test_min
|
407
|
+
assert_equal(@ib, @s11.min)
|
408
|
+
assert_equal(@ib, @s12.min)
|
409
|
+
assert_equal(@ib+1, @s21.min)
|
410
|
+
assert_equal(@ib+1, @s22.min)
|
411
|
+
|
412
|
+
assert_equal(@ie-1, @s22.min{|a,b| -a <=> -b})
|
413
|
+
|
414
|
+
assert_raises TypeError do
|
415
|
+
Rangesmaller.new(1.0, 5, :exclude_begin => true).min
|
416
|
+
end
|
417
|
+
end # def test_min
|
418
|
+
|
419
|
+
|
420
|
+
def test_min_by
|
421
|
+
assert_equal(@ib+1, @s22.min_by.each{|i| i})
|
422
|
+
|
423
|
+
assert_raises TypeError do
|
424
|
+
Rangesmaller.new(1.0, 5, :exclude_begin => true).min_by
|
425
|
+
end
|
426
|
+
|
427
|
+
assert_equal(@ie-1, @s22.min_by{|a| -a })
|
428
|
+
end # def test_min_by
|
429
|
+
|
430
|
+
|
431
|
+
def test_minmax
|
432
|
+
assert_equal([@ib,@ie], @s11.minmax)
|
433
|
+
assert_equal([@ib,@ie-1], @s12.minmax)
|
434
|
+
assert_equal([@ib+1,@ie], @s21.minmax)
|
435
|
+
assert_equal([@ib+1,@ie-1], @s22.minmax)
|
436
|
+
|
437
|
+
assert_equal([@ie-1,@ib+1], @s22.minmax{|a,b| -a <=> -b}) # Not the best test...
|
438
|
+
|
439
|
+
assert_raises TypeError do
|
440
|
+
Rangesmaller.new(1.0, 5, :exclude_begin => true).minmax
|
441
|
+
end
|
442
|
+
end # def test_minmax
|
443
|
+
|
444
|
+
|
445
|
+
def test_minmax_by
|
446
|
+
assert_equal([@ib+1,@ie-1], @s22.minmax_by.each{|i| i})
|
447
|
+
|
448
|
+
assert_raises TypeError do
|
449
|
+
Rangesmaller.new(1.0, 5, :exclude_begin => true).minmax_by
|
450
|
+
end
|
451
|
+
|
452
|
+
assert_equal([@ie-1,@ib+1], @s22.minmax_by{|a| -a })
|
453
|
+
end # def test_minmax_by
|
454
|
+
|
455
|
+
|
456
|
+
def test_size
|
457
|
+
assert_equal(@ie-@ib+1, @s11.size)
|
458
|
+
assert_equal(@ie-@ib, @s12.size)
|
459
|
+
assert_equal(@ie-@ib, @s21.size)
|
460
|
+
assert_equal(@ie-@ib-1, @s22.size)
|
461
|
+
Rangesmaller.new(9, -5, :exclude_begin => true)
|
462
|
+
assert_equal 0, Rangesmaller.new(9, -5, :exclude_begin => true).size
|
463
|
+
assert_equal nil, Rangesmaller.new("a", "c").size
|
464
|
+
assert_equal nil, Rangesmaller.new(nil, nil).size
|
465
|
+
|
466
|
+
# Infinity
|
467
|
+
inf = Float::INFINITY
|
468
|
+
excl_ini = { :exclude_begin => true }
|
469
|
+
assert_equal inf, Rangesmaller.new(-inf, 1).size
|
470
|
+
assert_equal 0, Rangesmaller.new(-inf, -inf, excl_ini).size
|
471
|
+
assert_equal inf, Rangesmaller.new(-inf, 1, excl_ini).size
|
472
|
+
assert_equal 0, Rangesmaller.new( inf, inf, excl_ini).size
|
473
|
+
assert_equal inf, Rangesmaller.new( 1, inf, excl_ini).size
|
474
|
+
|
475
|
+
# Float
|
476
|
+
rfi = (2.4..4.4) # size() => 3 see [ruby-list:49797] from matz
|
477
|
+
rfe = (2.4...4.4) # size() => 2
|
478
|
+
siz = rfi.size
|
479
|
+
assert_equal siz, Rangesmaller(rfi).size
|
480
|
+
assert_equal siz-1, Rangesmaller(rfe).size
|
481
|
+
assert_equal siz-1, Rangesmaller(rfi, excl_ini).size
|
482
|
+
assert_equal siz-2, Rangesmaller(rfe, excl_ini).size
|
483
|
+
assert_equal siz-1, Rangesmaller(Rational(24,10)..4.4, excl_ini).size
|
484
|
+
assert_equal siz-2, Rangesmaller(Rational(24,10)...4.4, excl_ini).size
|
485
|
+
# (0.5...5).size # => 5 (Ruby 2.1)
|
486
|
+
# (Rational(1,2)...5).size # => 4 (Ruby 2.1) => Bug!
|
487
|
+
|
488
|
+
# String
|
489
|
+
rsi = (?a..?d)
|
490
|
+
if rsi.size.nil?
|
491
|
+
assert_equal nil, Rangesmaller(rsi, excl_ini).size # Ruby 2.1
|
492
|
+
else
|
493
|
+
assert_equal 3, Rangesmaller(rsi, excl_ini).size # If the specification ever changes?
|
494
|
+
end
|
495
|
+
end # def test_size
|
496
|
+
|
497
|
+
|
498
|
+
def test_step
|
499
|
+
ns=0; @s11.step(2){|i| ns+=i}
|
500
|
+
assert_equal(1+3+5, ns)
|
501
|
+
ns=0; @s12.step(2){|i| ns+=i}
|
502
|
+
assert_equal(1+3+5, ns)
|
503
|
+
ns=0; @s21.step(2){|i| ns+=i}
|
504
|
+
assert_equal(2+4+6, ns)
|
505
|
+
ns=0; @s22.step(2){|i| ns+=i}
|
506
|
+
assert_equal(2+4, ns)
|
507
|
+
|
508
|
+
## Arbitrary Class
|
509
|
+
sx1 = Rangesmaller.new(Xs.new(3), Xs.new(6), :exclude_begin => true, :exclude_end => true)
|
510
|
+
a=[]; sx1.step(2){|i| a.push(i)}
|
511
|
+
assert_equal([Xs.new(4)], a)
|
512
|
+
end # def test_step
|
513
|
+
|
514
|
+
|
515
|
+
end # class TestFoo < MiniTest::Unit::TestCase
|
516
|
+
|
517
|
+
#end # if $0 == __FILE__
|
518
|
+
|
519
|
+
|
520
|
+
# % ruby1.8 -rlib/rangesmaller/rangesmaller.rb -e 'p Rangesmaller(2.3...7,:exclude_begin=>1).first==2.3'
|
521
|
+
# ruby1.8 -rlib/rangesmaller/rangesmaller.rb -e 'i=0;p Rangesmaller(1...4,:exclude_begin=>true).each{|j|i+=j};p i==5'
|
522
|
+
# ruby1.8 -rlib/rangesmaller/rangesmaller.rb -e 'p Rangesmaller(-1...7,:exclude_begin=>1).min==0'
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rangesmaller
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masa Sakano
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-22 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: This defines a subclass of Range, Rangesmaller class. Users can define
|
14
|
+
whether the begin boundary is exclusive or inclusive, in addition to the end boundary
|
15
|
+
as in the standard Range.
|
16
|
+
email:
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files:
|
20
|
+
- README.rdoc
|
21
|
+
files:
|
22
|
+
- README.rdoc
|
23
|
+
- Rakefile
|
24
|
+
- rangesmaller.gemspec
|
25
|
+
- lib/rangesmaller/rangesmaller.rb
|
26
|
+
- test/test_rangesmaller.rb
|
27
|
+
homepage:
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options:
|
33
|
+
- --charset=UTF-8
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.0.3
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: Rangesmaller class -- Range with include_begin?()
|
52
|
+
test_files:
|
53
|
+
- test/test_rangesmaller.rb
|