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