sorted_array_binary 0.0.1
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/.coveralls.yml +1 -0
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE +20 -0
- data/README.md +42 -0
- data/Rakefile +10 -0
- data/lib/sorted_array_binary.rb +216 -0
- data/perf-tests/common.rb +13 -0
- data/perf-tests/perf-vs-sort +18 -0
- data/perf-tests/perf-vs-sorted_array +21 -0
- data/sorted_array_binary.gemspec +16 -0
- data/spec/sorted_array_binary_spec.rb +352 -0
- data/spec/spec_helper.rb +2 -0
- metadata +94 -0
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Dmitry Maksyoma
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
sorted_array_binary
|
2
|
+
===================
|
3
|
+
|
4
|
+
[](https://travis-ci.org/ledestin/sorted_array_binary)
|
5
|
+
[![Coverage Status] (https://coveralls.io/repos/ledestin/sorted_array_binary/badge.png)] (https://coveralls.io/r/ledestin/sorted_array_binary)
|
6
|
+
[](https://codeclimate.com/github/ledestin/sorted_array_binary)
|
7
|
+
|
8
|
+
A sorted array using binary search
|
9
|
+
|
10
|
+
## Why
|
11
|
+
|
12
|
+
1. Neither of the existing sorted arrays gems use binary search (i.e. slow or
|
13
|
+
very slow).
|
14
|
+
2. I don't like their source code, so I decided to roll my own, instead of
|
15
|
+
contributing.
|
16
|
+
|
17
|
+
Existing sorted array gems as of Jan 2014:
|
18
|
+
* sorted_array (0.0.5)
|
19
|
+
* array-sorted (1.1.2)
|
20
|
+
|
21
|
+
## Example
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'sorted_array_binary'
|
25
|
+
|
26
|
+
# Use standard sorting via <=>.
|
27
|
+
array = SortedArrayBinary.new
|
28
|
+
array.push 'b', 'a' #=> ['a', 'b']
|
29
|
+
|
30
|
+
# Use custom sorting block.
|
31
|
+
array = SortedArrayBinary.new { |a, b| b <=> a }
|
32
|
+
array.push 'a', 'b' #=> ['b', 'a']
|
33
|
+
```
|
34
|
+
|
35
|
+
## Performance
|
36
|
+
|
37
|
+
When #push'ing 1000 random numbers into an array:
|
38
|
+
```
|
39
|
+
sorted_array (0.0.5) 1.179088
|
40
|
+
array-sorted (1.1.2) 0.076348
|
41
|
+
sorted_array_binary (0.0.1) 0.015969
|
42
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
# Copyright (C) 2014 by Dmitry Maksyoma <ledestin@gmail.com>
|
2
|
+
|
3
|
+
# Automatically sorted array (by using binary search). Nils aren't allowed.
|
4
|
+
# Methods that reorder elements are not implemented, as well as #[]= and #fill.
|
5
|
+
#
|
6
|
+
# = Example
|
7
|
+
# require 'sorted_array_binary'
|
8
|
+
#
|
9
|
+
# # Use standard sorting via <=>.
|
10
|
+
# array = SortedArrayBinary.new
|
11
|
+
# array.push 'b', 'a' #=> ['a', 'b']
|
12
|
+
#
|
13
|
+
# # Use custom sorting block.
|
14
|
+
# array = SortedArrayBinary.new { |a, b| b <=> a }
|
15
|
+
# array.push 'a', 'b' #=> ['b', 'a']
|
16
|
+
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
|
+
alias :old_insert :insert
|
49
|
+
private :old_insert
|
50
|
+
alias :old_sort! :sort!
|
51
|
+
private :old_sort!
|
52
|
+
|
53
|
+
def initialize *args, &b
|
54
|
+
@sort_block = proc { |a, b| a <=> b }
|
55
|
+
|
56
|
+
# Passed sort block.
|
57
|
+
if args.size == 0 && block_given?
|
58
|
+
@sort_block = b
|
59
|
+
super()
|
60
|
+
return
|
61
|
+
end
|
62
|
+
|
63
|
+
if args.size == 1
|
64
|
+
# Passed initial array.
|
65
|
+
if args.first.respond_to? :each
|
66
|
+
self.class._check_for_nil *args.first
|
67
|
+
super *args
|
68
|
+
old_sort!
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
# Passed size and block.
|
73
|
+
if block_given?
|
74
|
+
super *args, &b
|
75
|
+
self.class._check_for_nil *self
|
76
|
+
old_sort!
|
77
|
+
return
|
78
|
+
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
|
+
end
|
84
|
+
|
85
|
+
super
|
86
|
+
end
|
87
|
+
|
88
|
+
# Not implemented methods.
|
89
|
+
#
|
90
|
+
# The following methods are not implemented mostly because they change order
|
91
|
+
# of elements. The rest ([]= and fill) arguably aren't useful on a sorted
|
92
|
+
# array.
|
93
|
+
def _not_implemented *args #:nodoc:
|
94
|
+
raise NotImplementedError
|
95
|
+
end
|
96
|
+
|
97
|
+
[:[]=, :fill, :insert, :reverse!, :rotate!, :shuffle!, :sort!, :unshift].
|
98
|
+
each { |m|
|
99
|
+
alias_method m, :_not_implemented
|
100
|
+
}
|
101
|
+
|
102
|
+
# Same as Array#collect!, but:
|
103
|
+
# * Disallow nils in the resulting array.
|
104
|
+
# * The resulting array is sorted.
|
105
|
+
def collect! &b
|
106
|
+
replace(collect &b)
|
107
|
+
end
|
108
|
+
alias :map! :collect!
|
109
|
+
|
110
|
+
# Same as Array#concat, but:
|
111
|
+
# * Disallow nils in the passed array.
|
112
|
+
# * The resulting array is sorted.
|
113
|
+
def concat other_ary
|
114
|
+
_add *other_ary
|
115
|
+
end
|
116
|
+
|
117
|
+
# Same as Array#flatten!, but:
|
118
|
+
# * Disallow nils in the resulting array.
|
119
|
+
# * The resulting array is sorted.
|
120
|
+
def flatten! *args
|
121
|
+
replace(flatten *args)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Add objects to array, automatically placing them according to sort order.
|
125
|
+
# Disallow nils.
|
126
|
+
def push *objs
|
127
|
+
_add *objs
|
128
|
+
end
|
129
|
+
alias :<< :push
|
130
|
+
|
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
|
136
|
+
super
|
137
|
+
old_sort!
|
138
|
+
self
|
139
|
+
end
|
140
|
+
|
141
|
+
#private
|
142
|
+
# Name the following methods starting with underscore so as not to pollute
|
143
|
+
# Array namespace. They are considered private, but for testing purposes are
|
144
|
+
# left public.
|
145
|
+
|
146
|
+
def _add *objs #:nodoc:
|
147
|
+
self.class._check_for_nil *objs
|
148
|
+
objs.each { |obj|
|
149
|
+
old_insert _find_insert_position(obj), obj
|
150
|
+
}
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
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
|
+
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
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require './common'
|
4
|
+
require '../lib/sorted_array_binary'
|
5
|
+
|
6
|
+
sec = MonotonicTime.measure { @ar.sort }
|
7
|
+
say 'sorting', sec
|
8
|
+
|
9
|
+
sec = MonotonicTime.measure {
|
10
|
+
sa = SortedArrayBinary.new
|
11
|
+
sa.push *@ar
|
12
|
+
}
|
13
|
+
say 'pushing into SortedArray', sec
|
14
|
+
|
15
|
+
sec = MonotonicTime.measure {
|
16
|
+
SortedArrayBinary.new @ar
|
17
|
+
}
|
18
|
+
say 'SortedArray.new', sec
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'frugal_timeout'
|
4
|
+
require 'sorted_array'
|
5
|
+
require 'array-sorted'
|
6
|
+
require './common'
|
7
|
+
require '../lib/sorted_array_binary'
|
8
|
+
|
9
|
+
def measure sorted_array_name, sorted_array_instance
|
10
|
+
sec = MonotonicTime.measure {
|
11
|
+
@ar.each { |el|
|
12
|
+
sorted_array_instance.push el
|
13
|
+
}
|
14
|
+
}
|
15
|
+
say sorted_array_name, sec
|
16
|
+
end
|
17
|
+
|
18
|
+
measure 'sorted_array',
|
19
|
+
SortedArray::SortedArray.new(SortedArray::DefaultSorter.new(:to_i))
|
20
|
+
measure 'array-sorted', Array::Sorted.new
|
21
|
+
measure 'sorted_array_binary', SortedArrayBinary.new
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'sorted_array_binary'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.date = '2014-01-29'
|
5
|
+
s.summary = 'Sorted array'
|
6
|
+
s.description = 'Sorted array using binary search'
|
7
|
+
s.authors = ['Dmitry Maksyoma']
|
8
|
+
s.email = 'ledestin@gmail.com'
|
9
|
+
s.files = `git ls-files`.split($\)
|
10
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
11
|
+
s.require_paths = ['lib']
|
12
|
+
s.homepage = 'https://github.com/ledestin/sorted_array_binary'
|
13
|
+
|
14
|
+
s.add_development_dependency 'rspec', '>= 2.13'
|
15
|
+
s.add_development_dependency 'hitimes', '~> 1.2'
|
16
|
+
end
|
@@ -0,0 +1,352 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'sorted_array_binary'
|
4
|
+
|
5
|
+
describe SortedArrayBinary do
|
6
|
+
before :each do
|
7
|
+
@ar = SortedArrayBinary.new
|
8
|
+
end
|
9
|
+
|
10
|
+
# {{{2 self._check_for_nil
|
11
|
+
context 'self._check_for_nil' do
|
12
|
+
it "raises exception if there's nil in passed objs" do
|
13
|
+
expect {
|
14
|
+
SortedArrayBinary._check_for_nil 'a', nil
|
15
|
+
}.to raise_error ArgumentError
|
16
|
+
end
|
17
|
+
|
18
|
+
it "doesn't raise exception if there's no nil in passed objs" do
|
19
|
+
expect {
|
20
|
+
SortedArrayBinary._check_for_nil 'a', 'b'
|
21
|
+
}.not_to raise_error
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# {{{2 self.new
|
26
|
+
context 'self.new' do
|
27
|
+
it 'if passed size and obj, fills it' do
|
28
|
+
@ar = SortedArrayBinary.new 5, 'a'
|
29
|
+
@ar.should == ['a']*5
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'if passed just size, raises exception' do
|
33
|
+
expect { SortedArrayBinary.new 5 }.to raise_error ArgumentError
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'if passed single non-numeric argument, calls Array#new' do
|
37
|
+
expect { SortedArrayBinary.new 'a' }.to raise_error TypeError
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'if passed array,' do
|
41
|
+
it 'sorts it' do
|
42
|
+
@ar = SortedArrayBinary.new ['b', 'a']
|
43
|
+
@ar.should == ['a', 'b']
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'raises exception if nil found in passed array' do
|
47
|
+
expect { SortedArrayBinary.new [nil] }.to raise_error ArgumentError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'if passed size and block, fills it and sorts it' do
|
52
|
+
@ar = SortedArrayBinary.new(5) { |i| i == 0 ? 10 : i }
|
53
|
+
@ar.should == [1, 2, 3, 4, 10]
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'if passed sorting block, resulting array is sorted using it' do
|
57
|
+
@ar = SortedArrayBinary.new { |a, b| b <=> a }
|
58
|
+
@ar.push 'a', 'b'
|
59
|
+
@ar.should == ['b', 'a']
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# {{{2 Not implemented
|
64
|
+
context 'raises NotImplementedError exception when called' do
|
65
|
+
it '#[]=' do
|
66
|
+
expect { @ar[0] = 'a' }.to raise_error NotImplementedError
|
67
|
+
end
|
68
|
+
|
69
|
+
it '#fill' do
|
70
|
+
expect { @ar.fill nil }.to raise_error NotImplementedError
|
71
|
+
end
|
72
|
+
|
73
|
+
[:insert, :reverse!, :rotate!, :shuffle!, :sort!, :unshift].
|
74
|
+
each { |m|
|
75
|
+
it "##{m}" do
|
76
|
+
expect { @ar.send m }.to raise_error NotImplementedError
|
77
|
+
end
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
# {{{2 #collect!
|
82
|
+
[:collect!, :map!].each { |method|
|
83
|
+
context "##{method}" do
|
84
|
+
it 'after it, array is sorted' do
|
85
|
+
@ar.push 'a', 'b', 'c'
|
86
|
+
@ar.send(method) { |el|
|
87
|
+
case el
|
88
|
+
when 'a' then 9
|
89
|
+
when 'b' then 3
|
90
|
+
when 'c' then 1
|
91
|
+
end
|
92
|
+
}
|
93
|
+
@ar.should == [1, 3, 9]
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'raises exception if one of resulting elements is nil' do
|
97
|
+
@ar.push 'a'
|
98
|
+
expect { @ar.send(method) { nil } }.to raise_error ArgumentError
|
99
|
+
@ar.should == ['a']
|
100
|
+
end
|
101
|
+
end
|
102
|
+
}
|
103
|
+
|
104
|
+
# {{{2 #concat
|
105
|
+
it '#concat adds another array and everything is sorted' do
|
106
|
+
@ar.push 'c'
|
107
|
+
@ar.concat ['a', 'b']
|
108
|
+
@ar.should == ['a', 'b', 'c']
|
109
|
+
end
|
110
|
+
|
111
|
+
# {{{2 #flatten!
|
112
|
+
context '#flatten!' do
|
113
|
+
it 'flattens array' do
|
114
|
+
@ar.push [1, 2], [4, 3]
|
115
|
+
@ar.flatten!
|
116
|
+
@ar.should == [1, 2, 3, 4]
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'raises exception if resulting array contains nil' do
|
120
|
+
@ar.push [nil, 1]
|
121
|
+
expect { @ar.flatten! }.to raise_error ArgumentError
|
122
|
+
@ar.should == [[nil, 1]]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# {{{2 #push
|
127
|
+
[:<<, :push].each { |method|
|
128
|
+
context "##{method}" do
|
129
|
+
it 'adds an element to array' do
|
130
|
+
@ar.send method, 'a'
|
131
|
+
@ar.should == ['a']
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'adds 2 elements to array' do
|
135
|
+
@ar.send method, 'a', 'b'
|
136
|
+
@ar.should == ['a', 'b']
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'adds elements in random order, but the result is sorted' do
|
140
|
+
@ar.send method, 'b', 'a', 'd', 'c'
|
141
|
+
@ar.should == ['a', 'b', 'c', 'd']
|
142
|
+
@ar.send method, 'e'
|
143
|
+
@ar.should == ['a', 'b', 'c', 'd', 'e']
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'adds element in random order, but sorting is done via sort block' do
|
147
|
+
@ar = SortedArrayBinary.new { |a, b| b <=> a }
|
148
|
+
@ar.push 'c', 'd'
|
149
|
+
@ar.push 'a', 'b'
|
150
|
+
@ar.should == ['d', 'c', 'b', 'a']
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'raises exception if nil is passed' do
|
154
|
+
expect { @ar.send method, nil }.to raise_error ArgumentError
|
155
|
+
end
|
156
|
+
end
|
157
|
+
}
|
158
|
+
|
159
|
+
# {{{2 #replace
|
160
|
+
context '#replace' do
|
161
|
+
it '#replace replaces array, the resulting array is sorted' do
|
162
|
+
@ar.push 'a'
|
163
|
+
@ar.replace ['c', 'b']
|
164
|
+
@ar.should == ['b', 'c']
|
165
|
+
end
|
166
|
+
|
167
|
+
it "doesn't allow nils" do
|
168
|
+
expect { @ar.replace [nil] }.to raise_error ArgumentError
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# {{{2 #_compare
|
173
|
+
context '#_compare' do
|
174
|
+
context 'sort block not given' do
|
175
|
+
it 'returns :equal if arguments are equal' do
|
176
|
+
@ar._compare(1, 1).equal?.should be_true
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'returns :less if arg1 < arg2' do
|
180
|
+
@ar._compare(1, 2).less?.should be_true
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'returns :greater if arg1 > arg2' do
|
184
|
+
@ar._compare(2, 1).greater?.should be_true
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'sort block given' do
|
189
|
+
before :each do
|
190
|
+
@ar = SortedArrayBinary.new { |a, b| b <=> a }
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'returns :equal if arguments are equal' do
|
194
|
+
@ar._compare(1, 1).equal?.should be_true
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'returns :less if arg1 < arg2' do
|
198
|
+
@ar._compare(2, 1).less?.should be_true
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'returns :greater if arg1 > arg2' do
|
202
|
+
@ar._compare(1, 2).greater?.should be_true
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'raises exception if block returns value outside of -1, 0, 1' do
|
206
|
+
@ar = SortedArrayBinary.new { 'a' }
|
207
|
+
expect {
|
208
|
+
@ar.push 1, 2
|
209
|
+
}.to raise_error SortedArrayBinary::InvalidSortBlock
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# {{{2 #_find_insert_position
|
215
|
+
context '#_find_insert_position' do
|
216
|
+
it 'returns 0 if array is empty' do
|
217
|
+
@ar._find_insert_position('a').should == 0
|
218
|
+
end
|
219
|
+
|
220
|
+
context 'and array contains single element,' do
|
221
|
+
it 'returns 1 if the first element is less than the passed elementt' do
|
222
|
+
@ar.push 'a'
|
223
|
+
@ar._find_insert_position('b').should == 1
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'returns 0 if the first element is greater than the passed elementt' do
|
227
|
+
@ar.push 'b'
|
228
|
+
@ar._find_insert_position('a').should == 0
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'returns 1 if the first element is equal to the passed elementt' do
|
232
|
+
@ar.push 'a'
|
233
|
+
@ar._find_insert_position('a').should == 1
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context 'and array contains 2 elements,' do
|
238
|
+
it 'returns 2 if passed element is greater than 2nd element' do
|
239
|
+
@ar.push 'a', 'b'
|
240
|
+
@ar._find_insert_position('c').should == 2
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'returns 1 if passed element is less than 2nd element' do
|
244
|
+
@ar.push 'a', 'c'
|
245
|
+
@ar._find_insert_position('b').should == 1
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'returns 0 if passed element is less than 1st element' do
|
249
|
+
@ar.push 'b', 'c'
|
250
|
+
@ar._find_insert_position('a').should == 0
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'and array context 4 elements,' do
|
255
|
+
it 'returns 1 if passed element is greater than 1st element' do
|
256
|
+
@ar.push 'a', 'c', 'd'
|
257
|
+
@ar._find_insert_position('b').should == 1
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# {{{2 #_middle_element_index
|
263
|
+
context '_middle_element_index' do
|
264
|
+
it "returns first element index if there's one element" do
|
265
|
+
@ar._middle_element_index(0, 0).should == 0
|
266
|
+
@ar._middle_element_index(1, 1).should == 1
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'returns 0 if there are 2 elements in the array' do
|
270
|
+
@ar._middle_element_index(0, 1).should == 0
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'returns 1 if there are 3 elements in the array' do
|
274
|
+
@ar._middle_element_index(0, 2).should == 1
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'returns 1 if there are 4 elements in the array' do
|
278
|
+
@ar._middle_element_index(0, 3).should == 1
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# {{{2 #_left_boundary, #_right_boundary
|
283
|
+
context '#_left_boundary, #_right_boundary' do
|
284
|
+
context 'left' do
|
285
|
+
it "returns true if it's left boundary?" do
|
286
|
+
@ar.push 'a'
|
287
|
+
@ar._left_boundary?(0).should == true
|
288
|
+
end
|
289
|
+
|
290
|
+
it "returns false if it's not left boundary?" do
|
291
|
+
@ar.push 'a'
|
292
|
+
@ar._left_boundary?(1).should == false
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'raises exception if array is empty' do
|
296
|
+
expect {
|
297
|
+
@ar._left_boundary?(0)
|
298
|
+
}.to raise_error
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context 'right' do
|
303
|
+
it "returns true if it's right boundary?" do
|
304
|
+
@ar.push 'a', 'b'
|
305
|
+
@ar._right_boundary?(1).should == true
|
306
|
+
end
|
307
|
+
|
308
|
+
it "returns false if it's not right boundary?" do
|
309
|
+
@ar.push 'a', 'b'
|
310
|
+
@ar._right_boundary?(0).should == false
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'raises exception if array is empty' do
|
314
|
+
expect {
|
315
|
+
@ar._right_boundary?(0)
|
316
|
+
}.to raise_error
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
# }}}2
|
321
|
+
end
|
322
|
+
|
323
|
+
describe SortedArrayBinary::ComparisonState do
|
324
|
+
[nil, -2, 2].each { |state|
|
325
|
+
it "allows only valid state in" do
|
326
|
+
expect {
|
327
|
+
SortedArrayBinary::ComparisonState.new state
|
328
|
+
}.to raise_error ArgumentError
|
329
|
+
end
|
330
|
+
}
|
331
|
+
|
332
|
+
it 'is equal? if state is 0' do
|
333
|
+
state = SortedArrayBinary::ComparisonState.new 0
|
334
|
+
state.equal?.should be_true
|
335
|
+
state.less?.should be_false
|
336
|
+
state.greater?.should be_false
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'is greater? if state is 1' do
|
340
|
+
state = SortedArrayBinary::ComparisonState.new 1
|
341
|
+
state.greater?.should be_true
|
342
|
+
state.equal?.should be_false
|
343
|
+
state.less?.should be_false
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'is less? if state is -1' do
|
347
|
+
state = SortedArrayBinary::ComparisonState.new -1
|
348
|
+
state.less?.should be_true
|
349
|
+
state.equal?.should be_false
|
350
|
+
state.greater?.should be_false
|
351
|
+
end
|
352
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sorted_array_binary
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dmitry Maksyoma
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-01-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.13'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.13'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: hitimes
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.2'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.2'
|
46
|
+
description: Sorted array using binary search
|
47
|
+
email: ledestin@gmail.com
|
48
|
+
executables: []
|
49
|
+
extensions: []
|
50
|
+
extra_rdoc_files: []
|
51
|
+
files:
|
52
|
+
- .coveralls.yml
|
53
|
+
- .gitignore
|
54
|
+
- .rspec
|
55
|
+
- .travis.yml
|
56
|
+
- Gemfile
|
57
|
+
- LICENSE
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- lib/sorted_array_binary.rb
|
61
|
+
- perf-tests/common.rb
|
62
|
+
- perf-tests/perf-vs-sort
|
63
|
+
- perf-tests/perf-vs-sorted_array
|
64
|
+
- sorted_array_binary.gemspec
|
65
|
+
- spec/sorted_array_binary_spec.rb
|
66
|
+
- spec/spec_helper.rb
|
67
|
+
homepage: https://github.com/ledestin/sorted_array_binary
|
68
|
+
licenses: []
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.8.23
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Sorted array
|
91
|
+
test_files:
|
92
|
+
- spec/sorted_array_binary_spec.rb
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
has_rdoc:
|