rmtools 1.3.3 → 2.0.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,103 +3,143 @@ unless defined? Inf
3
3
  Inf = 1.0/0
4
4
  end
5
5
 
6
+ # Range in Ruby can have at least 2 meanings:
7
+ # 1) interval of real numbers
8
+ # (0...2).include? 1.6 # => true
9
+ # 2) lazy list of array indices (included integers):
10
+ # [0,1,2,3,4,5][1..4] # => [1, 2, 3, 4]
11
+ #
12
+ # There is some basic problems.
13
+ # 1) For the first way of using, Range doesn't have Set operations. Further more Range can not be complex.
14
+ # There is "intervals" gem that partially solves these problems, but it's arithmetic is not Set compatible:
15
+ # -Interval[1,2] # => Interval[-2, -1]
16
+ # instead of
17
+ # # => Interval[[-Inf, -2], [-1, +Inf]]
18
+ # 2) Hardly we can use Range second way, when it defined by non-integers:
19
+ # [0,1,2,3,4,5][1.9..4] # => [1, 2, 3, 4]
20
+ # (1.9...4.1).include? 4 # => true, BUT
21
+ # [0,1,2,3,4,5][1.9...4.1] # => [1, 2, 3]
22
+ #
23
+ # This extension leaves the first problem for a purpose of solving the second, saving the capability of a Range syntactic sugar. The present extension applies Set operations to ranges considered as lists of contained integers.
24
+ # It means:
25
+ # (x..y) equivalent (x...y+0.5) equivalent (x...y+1) equivalent [x, x+1, ..., y]
26
+ # Note quantity of dots (end exclusion)
6
27
  class Range
7
28
 
8
- # -(1.0..2.0)
9
- ### => XRange(-∞..1.0, 2.0..∞)
10
- # BUT
11
- # -(1..2)
12
- ### => XRange(-∞..0, 3..∞)
13
- # i.e. all excluding these: (0; 1], [1; 2], [2; 3)
14
- def -@
15
- self_begin = self.begin
16
- self_begin -= 1 if Integer === self_begin
17
- self_end = include_end.end
18
- self_end += 1 if Integer === self_end
19
- XRange(-Inf..self_begin, self_end..Inf)
29
+ def included_end
30
+ exclude_end? ? last.integer? ? last - 1 : last.to_i : last
20
31
  end
21
- def &(range)
22
- return range&self if range.is XRange
23
- beg = [self.begin, range.begin].max
24
- end_ = [self.include_end.end, range.include_end.end].min
25
- beg > end_ ? nil : beg..end_
32
+
33
+ def include_end
34
+ exclude_end? ? first..included_end : self
26
35
  end
27
36
 
28
- def |(range)
29
- return range|self if range.is XRange
30
- range = range.include_end
31
- self_ = self.include_end
32
- return XRange.new self, range if !x?(range)
33
- [self.begin, range.begin].min..[self_.end, range.end].max
37
+ # Since it's not represent an interval...
38
+ # (0..0).size # => 1 (equivalent list of one zero)
39
+ # (0...0).size # => 0 (equivalent empty list)
40
+ # There is no empty ranges with end included (since it includes at least an end, right?)
41
+ def size
42
+ exclude_end? ? (last - first).abs : (last - first).abs+1
43
+ end
44
+
45
+ def empty?
46
+ exclude_end? && last == first
34
47
  end
35
48
 
49
+ def b
50
+ !empty? && self
51
+ end
52
+
53
+ # Let significant content of a range used this way be:
54
+ def integers
55
+ (first.ceil..included_end).to_a
56
+ end
57
+ alias :to_is :integers
58
+
59
+ # -(1..2)
60
+ # -(0.5..2.1)
61
+ # i.e. all excluding these lazy indices: [1, 2]
62
+ ### => XRange(-∞..0, 3..∞)
63
+ def -@
64
+ XRange(-Inf..first.ceil-1, (exclude_end? && last.integer? ? last : last.to_i+1)..Inf)
65
+ end
66
+
67
+ def &(range)
68
+ return range & self if range.is XRange
69
+ fst = [first, range.first].max
70
+ lst = [included_end, range.included_end].min
71
+ fst > lst ? nil : fst..lst
72
+ end
36
73
 
37
74
  # On the basis of #-@ for non-integers,
38
- # (0..1) - (1..2)
39
- ### => XRange(0..0)
40
- # (0..1.0) - (1..2)
41
- ### => XRange(0..0)
42
- # (0..1) - (1.0..2)
43
- ### => XRange(0..1.0)
44
- # (0..1.0) - (1.0..2)
45
- ### => XRange(0..1.0)
75
+ # (0..3) - (0.5..2.1)
76
+ # (0..3) - (1..2)
77
+ ### => XRange(0..0, 3..3.0)
46
78
  def -(range)
47
79
  self & -range
48
80
  end
49
81
 
50
- def ^(range)
51
- common = self & range
52
- self - common | range - common
82
+ alias :include_number? :include?
83
+ # This function corresponds with ruby's default one in that we consider any number as a point on a segment.
84
+ # Thus, any of these 0..1, 0..1.0
85
+ # would return true as for 1 so as for 1.0
86
+ def include?(number_or_range)
87
+ if Numeric === number_or_range
88
+ include_number? number_or_range
89
+ elsif XRange === number_or_range
90
+ number_or_range.include? self
91
+ else
92
+ include_number? number_or_range.first and include_number? number_or_range.last
93
+ end
53
94
  end
54
95
 
55
- # Statement about non-integers is made with presumption that float presentation of number is a neighborhood of it.
56
- # Thus, "1.0" lies in the neighborhood of "1"; [0..1.0] is, mathematically, [0; 1) that not intersects with (1; 2]
57
- # and thereby (0..1.0).x?(1.0..2) should be false, although (0..1).x?(1..2) should be true
58
96
  def x?(range)
97
+ return false if empty?
59
98
  return range.x? self if range.is XRange
60
- range_end = range.include_end.end
61
- self_end = self.include_end.end
62
- if self_end < range_end
63
- if Integer === self_end and Integer === range.begin
64
- self_end >= range.begin
65
- else
66
- self_end > range.begin
67
- end
99
+ range_end = range.included_end
100
+ if range_end < range.first
101
+ return x?(range_end..range.first)
102
+ end
103
+ self_end = included_end
104
+ if self_end < first
105
+ return (first..self_end).x?(range)
106
+ end
107
+ case self_end <=> range_end
108
+ when -1
109
+ self_end >= range.first
110
+ when 1
111
+ first <= range_end >= first
68
112
  else
69
- if Integer === self.begin and Integer === range_end
70
- range_end >= self.begin
71
- else
72
- range_end > self.begin
73
- end
113
+ true
74
114
  end
75
115
  end
76
116
  alias :intersects? :x?
77
-
78
- def <=>(range)
79
- (self.begin <=> range.begin).b || self.include_end.end <=> range.include_end.end
117
+
118
+ def |(range)
119
+ return range | self if range.is XRange
120
+ range = range.include_end
121
+ self_ = self.include_end
122
+ return XRange.new self, range if !x?(range)
123
+ [self.begin, range.begin].min..[self_.end, range.end].max
80
124
  end
81
125
 
82
- def include_end
83
- exclude_end? ? self.begin..(self.end - 1) : self
126
+ def ^(range)
127
+ common = self & range
128
+ self - common | range - common
129
+ end
130
+
131
+ def <=>(range)
132
+ (first <=> range.first).b || included_end <=> range.included_end
84
133
  end
85
134
 
86
135
  def center
87
- (first + last + (!exclude_end?).to_i)/2
136
+ (first + include_end.last)/2
88
137
  end
89
138
 
90
139
  def part(i, j)
91
140
  first + (i-1)*size/j .. first - 1 + i*size/j unless i < 1 or j < 1 or j < i
92
141
  end
93
142
 
94
- def size
95
- (last - first).abs + (!exclude_end?).to_i
96
- end
97
-
98
- # Irrespective of include_end to be able to determne ranges created in any way
99
- def b
100
- self.begin != self.end && self
101
- end
102
-
103
143
  def div(n)
104
144
  unless n < 1
105
145
  rarray = []
@@ -141,7 +181,7 @@ class Range
141
181
  ie*(ie+1)/2 - (1..self.begin-1).sum
142
182
  end
143
183
 
144
- # monotone function definition interval min/max border
184
+ # minimum of monotone function definition interval
145
185
  def min(&fun)
146
186
  return first if yield first
147
187
  return unless yield last
@@ -152,6 +192,7 @@ class Range
152
192
  end
153
193
  end
154
194
 
195
+ # maximum of monotone function definition interval
155
196
  def max(&fun)
156
197
  return last if yield last
157
198
  return unless yield first
@@ -190,7 +231,7 @@ class XRange
190
231
  if range.is Range
191
232
  XRange.new *intersect(range)
192
233
  else
193
- @ranges.map {|r| range & r}.foldl(:|)
234
+ @ranges.map {|r| range & r}.foldl(:|) || XRange.new
194
235
  end
195
236
  end
196
237
 
@@ -204,7 +245,7 @@ class XRange
204
245
  end
205
246
 
206
247
  def -@
207
- @ranges.map {|r| -r}.foldl(:&)
248
+ @ranges.map {|r| -r}.foldl(:&) || XRange.new(-Inf..+Inf)
208
249
  end
209
250
 
210
251
  def -(range)
@@ -240,31 +281,41 @@ public
240
281
  end
241
282
 
242
283
  def of(ary)
243
- @ranges.foldl(:+) {|r| ary[r]}
284
+ @ranges.sum_of(ary)
285
+ end
286
+
287
+ def size
288
+ @size ||= @ranges.sum_size
244
289
  end
245
290
 
291
+ # XRange doesn't support ranges with end excluded, so it's empty only if contains nothing
246
292
  def empty?
247
293
  @ranges.empty?
248
294
  end
249
295
 
296
+ def b
297
+ !@ranges.empty? && self
298
+ end
299
+
250
300
  def include?(number_or_range)
251
- @ranges.find {|r| r.include?(number_or_range)}
301
+ @ranges.find_include?(number_or_range)
252
302
  end
253
303
 
254
304
  def begin
255
- @ranges[0].begin
305
+ @begin ||= @ranges.first && @ranges.first.begin
256
306
  end
257
307
 
258
308
  def end
259
- @ranges[-1].end
309
+ @end ||= @ranges.last && @ranges.last.end
260
310
  end
261
311
 
262
- def size
263
- @ranges.sum {|r| r.size}
312
+ alias :to_a_first :first
313
+ def first(count=nil)
314
+ count ? to_a_first(count) : self.begin
264
315
  end
265
-
266
- def b
267
- size != 0 && self
316
+
317
+ def last(count=nil)
318
+ count ? to_a.last(count) : self.end
268
319
  end
269
320
 
270
321
  def div(n)
@@ -125,7 +125,7 @@ module RMTools
125
125
 
126
126
  class ValueTraversable < Array
127
127
  include ValueTraversal
128
- __init__ if respond_to? :__init__
128
+ __init__
129
129
 
130
130
  private
131
131
  alias :array_plus :+
@@ -145,7 +145,7 @@ module RMTools
145
145
 
146
146
  class KeyValueTraversable < Hash
147
147
  include KeyValueTraversal
148
- __init__ if respond_to? :__init__
148
+ __init__
149
149
 
150
150
  def +(enum)
151
151
  if Hash === enum
@@ -1,15 +1,13 @@
1
1
  # encoding: utf-8
2
2
  class Object
3
3
 
4
- # &splitter must return a pair
5
- def unfold(break_if=lambda{|x|x==0}, &splitter)
4
+ # @ breaker must be callable that returns true value if that's it
5
+ # @ &splitter must return a pair
6
+ def unfold(breaker=lambda{|x|x.b}, &splitter)
6
7
  obj, container = self, []
7
- until begin
8
- result = splitter[obj]
9
- container.unshift result[1]
10
- break_if[result[0]]
11
- end
12
- obj = result[0]
8
+ until breaker.call obj
9
+ obj, next_element = splitter[obj]
10
+ container.unshift next_element
13
11
  end
14
12
  container
15
13
  end
@@ -18,26 +18,28 @@ class Array
18
18
  end
19
19
 
20
20
  def rand
21
- if block_given?
22
- h, ua = {}, uniq
23
- s = ua.size
24
- loop {
25
- i = Kernel.rand size
26
- if h[i]
27
- return if h.size == s
28
- elsif yield(e = ua[i])
29
- return e
30
- else h[i] = true
31
- end
32
- }
33
- else self[Kernel.rand(size)]
34
- end
21
+ self[Kernel.rand(size)]
35
22
  end
36
23
 
37
24
  def rand!
38
25
  delete_at Kernel.rand size
39
26
  end
40
27
 
28
+ def rand_by
29
+ return if empty?
30
+ set, ua = Set.new, uniq
31
+ s = ua.size
32
+ loop {
33
+ i = Kernel.rand size
34
+ if set.include? i
35
+ return if set.size == s
36
+ elsif yield(e = ua[i])
37
+ return e
38
+ else set << i
39
+ end
40
+ }
41
+ end
42
+
41
43
  def randdiv(int)
42
44
  dup.randdiv!(int)
43
45
  end
File without changes
@@ -0,0 +1,3 @@
1
+ module RMTools
2
+ VERSION = '2.0.0.rc5'
3
+ end
data/lib/rmtools.rb CHANGED
@@ -1,2 +1,10 @@
1
- require 'rmtools/init'
2
- RMTools::require 'dev_min'
1
+ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
2
+ $__MAIN__ = self
3
+ require 'active_support'
4
+ require 'rmtools/require'
5
+ %w[version core enumerable text time functional
6
+ conversions lang rand console
7
+ fs db xml dev
8
+ ../rmtools.so
9
+ ].each {|file| RMTools::require file}
10
+ class Object; include RMTools end
data/lib/rmtools_dev.rb CHANGED
@@ -1,6 +1,3 @@
1
- SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
2
- $__MAIN__ = self
3
- require 'rmtools/init'
4
- RMTools::require 'dev'
5
-
6
- unless defined? Rails; class Object; include RMTools end end
1
+ $stderr.puts 'require "rmtools_dev" is deprecated since 2.0.0. Please require "rmtools"'
2
+ $stderr.puts "called from #{caller(1).find {|line| line !~ /dependencies.rb/}}"
3
+ require 'rmtools'
data/rmtools.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rmtools/version'
5
+ #require 'rmtools/install'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "rmtools"
9
+ spec.version = RMTools::VERSION
10
+ spec.authors = ["Sergey Baev"]
11
+ spec.email = ["tinbka@gmail.com"]
12
+ spec.description = %q{RMTools is a collection of helpers for debug, text/array/file processing and simply easing a coding process}
13
+ spec.summary = %q{Collection of helpers for debug, text/array/file processing and simply easing a coding process}
14
+ spec.homepage = "https://github.com/tinbka/rmtools"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ # nonetheless, for this gem spec.files() returns [] after installation
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "activesupport"
22
+ #unless ext_files_not_modified 'rmtools', RMTools::VERSION
23
+ spec.extensions << 'ext/extconf.rb'
24
+ #end
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rake"
28
+ end