rmtools 1.3.3 → 2.0.0.rc5

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.
@@ -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