sorted_array_binary 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -31,8 +31,8 @@ array.push 'b', 'a' #=> ['a', 'b']
31
31
  array = SortedArrayBinary.new { |a, b| b <=> a }
32
32
  array.push 'a', 'b' #=> ['b', 'a']
33
33
 
34
- # Nils not allowed.
35
- array.push nil #=> ArgumentError is raised
34
+ # Take care to only add items that can be compared with <=>.
35
+ array.push nil, 1 #=> exception is raised
36
36
  ```
37
37
 
38
38
  ## Performance
@@ -41,5 +41,11 @@ When #push'ing 1000 random numbers into an array:
41
41
  ```
42
42
  sorted_array (0.0.5) 1.179088
43
43
  array-sorted (1.1.2) 0.076348
44
- sorted_array_binary (0.0.1) 0.015969
44
+ sorted_array_binary (0.0.3) 0.005244
45
+ ```
46
+
47
+ ## Installation
48
+
49
+ ```
50
+ gem install sorted_array_binary
45
51
  ```
@@ -1,5 +1,7 @@
1
1
  # Copyright (C) 2014 by Dmitry Maksyoma <ledestin@gmail.com>
2
2
 
3
+ require 'bsearch'
4
+
3
5
  # Automatically sorted array (by using binary search). Nils aren't allowed.
4
6
  # Methods that reorder elements are not implemented, as well as #[]= and #fill.
5
7
  #
@@ -14,37 +16,6 @@
14
16
  # array = SortedArrayBinary.new { |a, b| b <=> a }
15
17
  # array.push 'a', 'b' #=> ['b', 'a']
16
18
  class SortedArrayBinary < Array
17
- # {{{2 ComparisonState
18
- class ComparisonState #:nodoc:
19
- def initialize state
20
- raise ArgumentError, "invalid state: #{state.inspect}" \
21
- unless state && state >= -1 && state <= 1
22
-
23
- @state = state
24
- end
25
-
26
- def equal?
27
- @state == 0
28
- end
29
-
30
- def greater?
31
- @state == 1
32
- end
33
-
34
- def less?
35
- @state == -1
36
- end
37
- end
38
- # }}}2
39
-
40
- class InvalidSortBlock < RuntimeError #:nodoc:
41
- end
42
-
43
- def self._check_for_nil *objs #:nodoc:
44
- raise ArgumentError, "nils aren't allowed into sorted array" \
45
- if objs.include?(nil)
46
- end
47
-
48
19
  alias :old_insert :insert
49
20
  private :old_insert
50
21
  alias :old_sort! :sort!
@@ -63,7 +34,6 @@ class SortedArrayBinary < Array
63
34
  if args.size == 1
64
35
  # Passed initial array.
65
36
  if args.first.respond_to? :each
66
- self.class._check_for_nil *args.first
67
37
  super *args
68
38
  old_sort!
69
39
  return
@@ -72,14 +42,9 @@ class SortedArrayBinary < Array
72
42
  # Passed size and block.
73
43
  if block_given?
74
44
  super *args, &b
75
- self.class._check_for_nil *self
76
45
  old_sort!
77
46
  return
78
47
  end
79
-
80
- # Passed size, but not obj, which means fill with nils.
81
- raise ArgumentError, "can't fill array with nils" \
82
- if args.first.is_a? Numeric
83
48
  end
84
49
 
85
50
  super
@@ -94,123 +59,54 @@ class SortedArrayBinary < Array
94
59
  raise NotImplementedError
95
60
  end
96
61
 
97
- [:[]=, :fill, :insert, :reverse!, :rotate!, :shuffle!, :sort!, :unshift].
62
+ [:[]=, :fill, :insert, :reverse!, :rotate!, :shuffle!].
98
63
  each { |m|
99
64
  alias_method m, :_not_implemented
100
65
  }
101
66
 
102
- # Same as Array#collect!, but:
103
- # * Disallow nils in the resulting array.
104
- # * The resulting array is sorted.
105
- def collect! &b
67
+ def collect! &b #:nodoc:
106
68
  replace(collect &b)
107
69
  end
108
70
  alias :map! :collect!
109
71
 
110
- # Same as Array#concat, but:
111
- # * Disallow nils in the passed array.
112
- # * The resulting array is sorted.
113
- def concat other_ary
72
+ def concat other_ary #:nodoc:
114
73
  _add *other_ary
115
74
  end
116
75
 
117
- # Same as Array#flatten!, but:
118
- # * Disallow nils in the resulting array.
119
- # * The resulting array is sorted.
120
- def flatten! *args
76
+ def flatten! *args #:nodoc:
121
77
  replace(flatten *args)
122
78
  end
123
79
 
124
- # Add objects to array, automatically placing them according to sort order.
125
- # Disallow nils.
80
+ # Add objects to array, automatically placing them according to sort order
81
+ # (via <=> by default).
126
82
  def push *objs
127
83
  _add *objs
128
84
  end
129
85
  alias :<< :push
86
+ alias :unshift :push
130
87
 
131
- # Same as Array#replace, but:
132
- # * Disallow nils in @other_ary.
133
- # * The resulting array is sorted.
134
- def replace other_ary
135
- self.class._check_for_nil *other_ary
88
+ def replace other_ary #:nodoc:
136
89
  super
137
90
  old_sort! &@sort_block
138
91
  self
139
92
  end
140
93
 
94
+ def sort! #:nodoc:
95
+ end
96
+
141
97
  #private
142
98
  # Name the following methods starting with underscore so as not to pollute
143
99
  # Array namespace. They are considered private, but for testing purposes are
144
100
  # left public.
145
101
 
146
102
  def _add *objs #:nodoc:
147
- self.class._check_for_nil *objs
148
103
  objs.each { |obj|
149
104
  old_insert _find_insert_position(obj), obj
150
105
  }
151
106
  self
152
107
  end
153
108
 
154
- def _check_can_calc_boundary? #:nodoc:
155
- raise "can't calc boundary on empty array" if empty?
156
- end
157
-
158
- def _compare a, b #:nodoc:
159
- ComparisonState.new(ret = @sort_block.call(a, b))
160
- rescue ArgumentError
161
- raise InvalidSortBlock,
162
- "sort block returned invalid value: #{ret.inspect}"
163
- end
164
-
165
109
  def _find_insert_position element_to_place #:nodoc:
166
- return 0 if empty?
167
-
168
- start_idx, end_idx = 0, size - 1
169
- loop {
170
- middle_idx = _middle_element_index(start_idx, end_idx)
171
- middle_el = self[middle_idx]
172
- after_middle_idx = middle_idx + 1
173
-
174
- comparison_state = _compare(element_to_place, middle_el)
175
-
176
- # 1. Equals to the middle element. Insert after el.
177
- return after_middle_idx if comparison_state.equal?
178
-
179
- # 2. Less than the middle element.
180
- if comparison_state.less?
181
- # There's nothing to the left. So insert it as the first element.
182
- return 0 if _left_boundary? middle_idx
183
-
184
- end_idx = middle_idx - 1
185
- next
186
- end
187
-
188
- # 3. Greater than the middle element.
189
- #
190
- # Right boundary? Put element_to_place after the last (middle) element.
191
- return after_middle_idx if _right_boundary? middle_idx
192
-
193
- # Less than after middle element? Put it right before it!
194
- after_middle_el = self[after_middle_idx]
195
- ret = _compare(element_to_place, after_middle_el)
196
- return after_middle_idx if ret.equal? || ret.less?
197
-
198
- # Proceeed to divide the right part.
199
- start_idx = after_middle_idx
200
- }
201
- end
202
-
203
- def _left_boundary? idx #:nodoc:
204
- _check_can_calc_boundary?
205
- idx == 0
206
- end
207
-
208
- def _middle_element_index start, ending #:nodoc:
209
- start + (ending - start)/2
210
- end
211
-
212
- def _right_boundary? idx #:nodoc:
213
- _check_can_calc_boundary?
214
- idx == size - 1
110
+ bsearch_upper_boundary { |el| @sort_block.call el, element_to_place }
215
111
  end
216
112
  end
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require '../lib/sorted_array_binary'
4
+
5
+ @ar = []
6
+ COUNT = 10000
7
+ COUNT.times {
8
+ @ar << rand(COUNT)
9
+ }
10
+
11
+ sorted_array = SortedArrayBinary.new
12
+ @ar.each { |el| sorted_array.push el }
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'sorted_array_binary'
3
- s.version = '0.0.2'
4
- s.date = '2014-01-29'
3
+ s.version = '0.0.3'
4
+ s.date = '2014-01-31'
5
5
  s.summary = 'Sorted array'
6
6
  s.description = 'Sorted array using binary search'
7
7
  s.authors = ['Dmitry Maksyoma']
@@ -13,4 +13,5 @@ Gem::Specification.new do |s|
13
13
 
14
14
  s.add_development_dependency 'rspec', '>= 2.13'
15
15
  s.add_development_dependency 'hitimes', '~> 1.2'
16
+ s.add_runtime_dependency 'bsearch', '~> 1.5'
16
17
  end
@@ -12,21 +12,6 @@ describe SortedArrayBinary do
12
12
  @ar = SortedArrayBinary.new
13
13
  end
14
14
 
15
- # {{{2 self._check_for_nil
16
- context 'self._check_for_nil' do
17
- it "raises exception if there's nil in passed objs" do
18
- expect {
19
- SortedArrayBinary._check_for_nil 'a', nil
20
- }.to raise_error ArgumentError
21
- end
22
-
23
- it "doesn't raise exception if there's no nil in passed objs" do
24
- expect {
25
- SortedArrayBinary._check_for_nil 'a', 'b'
26
- }.not_to raise_error
27
- end
28
- end
29
-
30
15
  # {{{2 self.new
31
16
  context 'self.new' do
32
17
  it 'if passed size and obj, fills it' do
@@ -34,23 +19,13 @@ describe SortedArrayBinary do
34
19
  @ar.should == ['a']*5
35
20
  end
36
21
 
37
- it 'if passed just size, raises exception' do
38
- expect { SortedArrayBinary.new 5 }.to raise_error ArgumentError
39
- end
40
-
41
22
  it 'if passed single non-numeric argument, calls Array#new' do
42
23
  expect { SortedArrayBinary.new 'a' }.to raise_error TypeError
43
24
  end
44
25
 
45
- context 'if passed array,' do
46
- it 'sorts it' do
47
- @ar = SortedArrayBinary.new ['b', 'a']
48
- @ar.should == ['a', 'b']
49
- end
50
-
51
- it 'raises exception if nil found in passed array' do
52
- expect { SortedArrayBinary.new [nil] }.to raise_error ArgumentError
53
- end
26
+ it 'if passed array, it sorts it' do
27
+ @ar = SortedArrayBinary.new ['b', 'a']
28
+ @ar.should == ['a', 'b']
54
29
  end
55
30
 
56
31
  it 'if passed size and block, fills it and sorts it' do
@@ -75,7 +50,7 @@ describe SortedArrayBinary do
75
50
  expect { @ar.fill nil }.to raise_error NotImplementedError
76
51
  end
77
52
 
78
- [:insert, :reverse!, :rotate!, :shuffle!, :sort!, :unshift].
53
+ [:insert, :reverse!, :rotate!, :shuffle!].
79
54
  each { |m|
80
55
  it "##{m}" do
81
56
  expect { @ar.send m }.to raise_error NotImplementedError
@@ -104,12 +79,6 @@ describe SortedArrayBinary do
104
79
  end
105
80
  end
106
81
  }
107
-
108
- it 'raises exception if one of resulting elements is nil' do
109
- @ar.push 'a'
110
- expect { @ar.send(method) { nil } }.to raise_error ArgumentError
111
- @ar.should == ['a']
112
- end
113
82
  end
114
83
  }
115
84
 
@@ -143,16 +112,10 @@ describe SortedArrayBinary do
143
112
  end
144
113
  end
145
114
  }
146
-
147
- it 'raises exception if resulting array contains nil' do
148
- @ar.push [nil, 1]
149
- expect { @ar.flatten! }.to raise_error ArgumentError
150
- @ar.should == [[nil, 1]]
151
- end
152
115
  end
153
116
 
154
117
  # {{{2 #push
155
- [:<<, :push].each { |method|
118
+ [:<<, :push, :unshift].each { |method|
156
119
  context "##{method}" do
157
120
  it 'adds an element to array' do
158
121
  @ar.send method, 'a'
@@ -177,10 +140,6 @@ describe SortedArrayBinary do
177
140
  @ar.push 'a', 'b'
178
141
  @ar.should == ['d', 'c', 'b', 'a']
179
142
  end
180
-
181
- it 'raises exception if nil is passed' do
182
- expect { @ar.send method, nil }.to raise_error ArgumentError
183
- end
184
143
  end
185
144
  }
186
145
 
@@ -198,52 +157,6 @@ describe SortedArrayBinary do
198
157
  end
199
158
  end
200
159
  }
201
-
202
- it "doesn't allow nils" do
203
- expect { @ar.replace [nil] }.to raise_error ArgumentError
204
- end
205
- end
206
-
207
- # {{{2 #_compare
208
- context '#_compare' do
209
- context 'sort block not given' do
210
- it 'returns :equal if arguments are equal' do
211
- @ar._compare(1, 1).equal?.should be_true
212
- end
213
-
214
- it 'returns :less if arg1 < arg2' do
215
- @ar._compare(1, 2).less?.should be_true
216
- end
217
-
218
- it 'returns :greater if arg1 > arg2' do
219
- @ar._compare(2, 1).greater?.should be_true
220
- end
221
- end
222
-
223
- context 'sort block given' do
224
- before :each do
225
- @ar = SortedArrayBinary.new { |a, b| b <=> a }
226
- end
227
-
228
- it 'returns :equal if arguments are equal' do
229
- @ar._compare(1, 1).equal?.should be_true
230
- end
231
-
232
- it 'returns :less if arg1 < arg2' do
233
- @ar._compare(2, 1).less?.should be_true
234
- end
235
-
236
- it 'returns :greater if arg1 > arg2' do
237
- @ar._compare(1, 2).greater?.should be_true
238
- end
239
-
240
- it 'raises exception if block returns value outside of -1, 0, 1' do
241
- @ar = SortedArrayBinary.new { 'a' }
242
- expect {
243
- @ar.push 1, 2
244
- }.to raise_error SortedArrayBinary::InvalidSortBlock
245
- end
246
- end
247
160
  end
248
161
 
249
162
  # {{{2 #_find_insert_position
@@ -293,96 +206,5 @@ describe SortedArrayBinary do
293
206
  end
294
207
  end
295
208
  end
296
-
297
- # {{{2 #_middle_element_index
298
- context '_middle_element_index' do
299
- it "returns first element index if there's one element" do
300
- @ar._middle_element_index(0, 0).should == 0
301
- @ar._middle_element_index(1, 1).should == 1
302
- end
303
-
304
- it 'returns 0 if there are 2 elements in the array' do
305
- @ar._middle_element_index(0, 1).should == 0
306
- end
307
-
308
- it 'returns 1 if there are 3 elements in the array' do
309
- @ar._middle_element_index(0, 2).should == 1
310
- end
311
-
312
- it 'returns 1 if there are 4 elements in the array' do
313
- @ar._middle_element_index(0, 3).should == 1
314
- end
315
- end
316
-
317
- # {{{2 #_left_boundary, #_right_boundary
318
- context '#_left_boundary, #_right_boundary' do
319
- context 'left' do
320
- it "returns true if it's left boundary?" do
321
- @ar.push 'a'
322
- @ar._left_boundary?(0).should == true
323
- end
324
-
325
- it "returns false if it's not left boundary?" do
326
- @ar.push 'a'
327
- @ar._left_boundary?(1).should == false
328
- end
329
-
330
- it 'raises exception if array is empty' do
331
- expect {
332
- @ar._left_boundary?(0)
333
- }.to raise_error
334
- end
335
- end
336
-
337
- context 'right' do
338
- it "returns true if it's right boundary?" do
339
- @ar.push 'a', 'b'
340
- @ar._right_boundary?(1).should == true
341
- end
342
-
343
- it "returns false if it's not right boundary?" do
344
- @ar.push 'a', 'b'
345
- @ar._right_boundary?(0).should == false
346
- end
347
-
348
- it 'raises exception if array is empty' do
349
- expect {
350
- @ar._right_boundary?(0)
351
- }.to raise_error
352
- end
353
- end
354
- end
355
209
  # }}}2
356
210
  end
357
-
358
- # {{{1 ComparisonState
359
- describe SortedArrayBinary::ComparisonState do
360
- [nil, -2, 2].each { |state|
361
- it "allows only valid state in" do
362
- expect {
363
- SortedArrayBinary::ComparisonState.new state
364
- }.to raise_error ArgumentError
365
- end
366
- }
367
-
368
- it 'is equal? if state is 0' do
369
- state = SortedArrayBinary::ComparisonState.new 0
370
- state.equal?.should be_true
371
- state.less?.should be_false
372
- state.greater?.should be_false
373
- end
374
-
375
- it 'is greater? if state is 1' do
376
- state = SortedArrayBinary::ComparisonState.new 1
377
- state.greater?.should be_true
378
- state.equal?.should be_false
379
- state.less?.should be_false
380
- end
381
-
382
- it 'is less? if state is -1' do
383
- state = SortedArrayBinary::ComparisonState.new -1
384
- state.less?.should be_true
385
- state.equal?.should be_false
386
- state.greater?.should be_false
387
- end
388
- end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorted_array_binary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-01-29 00:00:00.000000000 Z
12
+ date: 2014-01-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -43,6 +43,22 @@ dependencies:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
45
  version: '1.2'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bsearch
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.5'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.5'
46
62
  description: Sorted array using binary search
47
63
  email: ledestin@gmail.com
48
64
  executables: []
@@ -61,6 +77,7 @@ files:
61
77
  - perf-tests/common.rb
62
78
  - perf-tests/perf-vs-sort
63
79
  - perf-tests/perf-vs-sorted_array
80
+ - perf-tests/profile
64
81
  - sorted_array_binary.gemspec
65
82
  - spec/sorted_array_binary_spec.rb
66
83
  - spec/spec_helper.rb