sorted_array_binary 0.0.2 → 0.0.3

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