bin_search 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ doc/**
2
+ Gemfile.lock
3
+ .yardoc
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ Stefan Plantikow <stefanp@moviepilot.com>
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Stefan Plantikow
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,81 @@
1
+ # bin_search
2
+
3
+ This gem implements binary search for arrays and array-like data
4
+ structures. Binary search is a very fast method for looking up elements in pre-sorted arrays that is missing from the Ruby standard library. For large
5
+ enough arrays, it will always be more efficient than using regular `assoc` and `index` calls.
6
+
7
+ ## API
8
+
9
+ bin_search adds six methods to Ruby's Array class:
10
+
11
+ * `bin_index` - find and return index of given element using `<=>` or the provided comparator block to compare elements
12
+ * `bin_search` - find and return given element using `<=>` or the provided comparator block to compare elements
13
+ * `bin_assoc` - find given element and return an assoc pair [index, elem] using `<=>` or the provided comparator block to compare elements
14
+ * `bin_index_by` - same as `bin_index` but compares elements using `<=>` after mapping them with the provided block
15
+ * `bin_search_by` - same as `bin_search` but compares elements using `<=>` after mapping them with the provided block
16
+ * `bin_assoc_by` - same as `bin_assoc` but compares elements using `<=>` after mapping them with the provided block
17
+
18
+ Each of these methods needs to be called with the searched element, and a mode as second argument. Available modes are:
19
+
20
+ * `:asc` - array has been sorted in ascending order, find first element
21
+ that is greater than or equal to the given element (default mode)
22
+ * `:desc` - array has been sorted descending ascending order, find first element that is less than or equal to the given element
23
+ * `:asc_eq` - same as `:asc` but only retuns the found element if it is equal
24
+ * `:desc_eq` - same as `:desc` but only retuns the found element if it is equal
25
+
26
+ If the sorting requirements of a mode are not met by the used array, the behavior of the bin_search methods is unspecified (i.e. arbitrary weird things may happpen).
27
+
28
+ Example
29
+
30
+ arr = [1, 2, 2, 3, 8, 9]
31
+ arr.bin_index(0, :asc) => 0
32
+ arr.bin_index(1, :asc) => 0
33
+ arr.bin_index(2, :asc) => 1
34
+ arr.bin_index(2, :asc_eq) => 1
35
+ arr.bin_index(2.5, :asc_eq) => -1
36
+ arr.bin_index(9, :asc) => 5
37
+ arr.bin_index(10, :asc) => -1
38
+
39
+ ## Use as Mixin
40
+
41
+ class MyArrayClass
42
+ include ::BinSearch::Methods
43
+
44
+ def sort!
45
+ # ...
46
+ end
47
+ end
48
+
49
+ arr = MyArrayClass.new
50
+ arr.sort!
51
+ arr.bin_search(some_elem, :asc)
52
+
53
+ ## Implementation Notes
54
+
55
+ bin_search is a pure ruby implementation of binary search.
56
+
57
+ For arrays with less than `1 << ::BinSearch::LIN_BITS` elements, all methods
58
+ switch to linear search as that is slightly more efficient on moden CPUs
59
+ (due to processor caching and branch prediction effects).
60
+
61
+ ## Requirements
62
+
63
+ Ruby 1.9
64
+
65
+ ## Installation
66
+
67
+ gem install bin_search
68
+
69
+ ## Online Docs
70
+
71
+ Docs for the latest released gems are to be found in:
72
+
73
+ http://rubydoc.info/gems/bin_search
74
+
75
+ ## Development
76
+
77
+ Development happens on the devel branch, cf. boggle/bin_search/tree/devel, too.
78
+
79
+ ## Status
80
+
81
+ Fairly fresh but tested.
@@ -0,0 +1,34 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec'
4
+ require 'rspec/core/rake_task'
5
+
6
+ require 'yard'
7
+ require 'yard/rake/yardoc_task'
8
+
9
+ desc 'Run all rspecs'
10
+ RSpec::Core::RakeTask.new(:spec) do |spec|
11
+ spec.fail_on_error = true
12
+ spec.verbose = false
13
+ # spec.rspec_opts = ['--backtrace']
14
+ end
15
+
16
+ desc 'Run yardoc over project sources'
17
+ YARD::Rake::YardocTask.new(:ydoc) do |t|
18
+ t.options = ['--verbose']
19
+ t.files = ['lib/**/*.rb', '-', 'README.md', 'AUTHORS', 'LICENSE.txt']
20
+ end
21
+
22
+ desc 'Run irb in project environment'
23
+ task :console do
24
+ require 'irb'
25
+ ARGV.clear
26
+ IRB.conf[:USE_READLINE] = false if ENV['JRUBY_OPTS'] =~ /--ng/
27
+ IRB.start
28
+ end
29
+
30
+ task :doc => :ydoc
31
+ task :docs => :ydoc
32
+ task :test => :spec
33
+ task :tests => :spec
34
+ task :irb => :console
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'bin_search/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'bin_search'
7
+ s.version = ::BinSearch::VERSION
8
+ s.summary = 'Binary search in sorted arrays'
9
+ s.description = 'Pure ruby implementation of binary search for Ruby arrays and similiar data ' +
10
+ 'structures. Supports ascending and descending sort order, searching ' +
11
+ 'for exact and nearest matches, and has a versatile API. Uses linear search ' +
12
+ 'for small arrays to make use of the internal cache of moden CPUs.'
13
+ s.author = 'Stefan Plantikow'
14
+ s.email = 'stefan.plantikow@googlemail.com'
15
+ s.homepage = 'https://github.com/boggle/bin_search'
16
+ s.rubyforge_project = 'bin_search'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.bindir = 'script'
24
+ s.executables = `git ls-files -- script/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.licenses = ['PUBLIC DOMAIN WITHOUT ANY WARRANTY']
26
+
27
+ s.add_development_dependency 'rspec'
28
+ s.add_development_dependency 'simplecov'
29
+ s.add_development_dependency 'rake'
30
+ s.add_development_dependency 'yard'
31
+
32
+ case RUBY_ENGINE.to_sym
33
+ when :jruby then s.add_development_dependency 'maruku'
34
+ else s.add_development_dependency 'redcarpet'
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+
3
+ require 'bin_search/version'
4
+
5
+ module BinSearch
6
+
7
+ def self.files
8
+ f = []
9
+ f << 'bin_search/bin_search'
10
+ f
11
+ end
12
+
13
+ def self.load_relative(f)
14
+ path = "#{File.join(File.dirname(caller[0]), f)}.rb"
15
+ load path
16
+ end
17
+
18
+ def self.reload!
19
+ files.each { |f| load_relative f }
20
+ end
21
+
22
+ end
23
+
24
+ BinSearch.files.each { |f| require f }
@@ -0,0 +1,289 @@
1
+ module BinSearch
2
+
3
+ # BinSearch switches to linear search if the numer of elements to sorted
4
+ # is less than 1 << LIN_BITS (i.e. 2^LIN_BITS - 1)
5
+ LIN_BITS = 6
6
+
7
+ MODES = [ :asc, :desc, :asc_geq, :desc_leq, :asc_eq, :desc_eq ]
8
+ MODE_IS_ASC = [ :asc, :asc_eq, :asc_geq ]
9
+ MODE_IS_DESC = [ :desc, :desc_eq, :desc_leq ]
10
+ MODE_CHECK_EQ = [ :asc_eq, :desc_eq ]
11
+
12
+ module Methods
13
+
14
+ # Binary search for the first elem in this array matching according to mode in the range
15
+ # (low..high-1)
16
+ #
17
+ # By default, <=> is used to compare elements. Alternatively, a comparator
18
+ # may be specified as block parameter
19
+ #
20
+ # The supported modes are
21
+ # * :asc - array is expected to be sorted in ascending order, first geq elem is matched
22
+ # * :asc_eq - array expected to be sorted in ascending order, first eq elem is matched
23
+ # * :desc - array is expected to be sorted in descending order, first leq elem is matched
24
+ # * :desc_eq - array is expected to be sorted in descending order, first eq elem is matched
25
+ #
26
+ # @param [Object] elem elem to search for
27
+ # @param [Fixnum] low lower bound (inclusive)
28
+ # @param [Fixnum] high upper bound (inclusive, -1 for last element)
29
+ # @param [:asc, :desc, :asc_eq, :desc_eq] mode matching mode
30
+ # @return [Fixnum] index of first matching elem in self, or -1 if not found
31
+ #
32
+ def bin_index(elem, mode, low = 0, high = -1)
33
+ dir = if ::BinSearch::MODE_IS_ASC.include?(mode) then +1 else -1 end
34
+ check_eq = ::BinSearch::MODE_CHECK_EQ.include?(mode)
35
+ high = size - 1 if high < 0
36
+ if block_given?
37
+ then _bin_index(elem, low, high, dir, check_eq) { |b| yield elem, b }
38
+ else _bin_index(elem, low, high, dir, check_eq) { |b| elem <=> b } end
39
+ end
40
+
41
+ # Binary search for the first elem in this array matching according to mode in the range
42
+ # (low..high-1)
43
+ #
44
+ # Elements are compared using <=> after mapping them using the provided block
45
+ #
46
+ # The supported modes are
47
+ # * :asc - array is expected to be sorted in ascending order, first geq elem is matched
48
+ # * :asc_eq - array expected to be sorted in ascending order, first eq elem is matched
49
+ # * :desc - array is expected to be sorted in descending order, first leq elem is matched
50
+ # * :desc_eq - array is expected to be sorted in descending order, first eq elem is matched
51
+ #
52
+ # @param [Object] elem elem to search for
53
+ # @param [Fixnum] low lower bound (inclusive)
54
+ # @param [Fixnum] high upper bound (inclusive, -1 for last element)
55
+ # @param [:asc, :desc, :asc_eq, :desc_eq] mode matching mode
56
+ # @return [Fixnum] index of first matching elem in self, or -1 if not found
57
+ #
58
+ def bin_index_by(elem, mode, low = 0, high = -1)
59
+ dir = if ::BinSearch::MODE_IS_ASC.include?(mode) then +1 else -1 end
60
+ check_eq = ::BinSearch::MODE_CHECK_EQ.include?(mode)
61
+ high = size - 1 if high < 0
62
+ e = yield elem
63
+ _bin_index(elem, low, high, dir, check_eq) { |b| e <=> (yield b) }
64
+ end
65
+
66
+ # Binary search for the first elem in this array matching according to mode in the range
67
+ # (low..high-1)
68
+ #
69
+ # By default, <=> is used to compare elements. Alternatively, a comparator
70
+ # may be specified as block parameter
71
+ #
72
+ # The supported modes are
73
+ # * :asc - array is expected to be sorted in ascending order, first geq elem is matched
74
+ # * :asc_eq - array expected to be sorted in ascending order, first eq elem is matched
75
+ # * :desc - array is expected to be sorted in descending order, first leq elem is matched
76
+ # * :desc_eq - array is expected to be sorted in descending order, first eq elem is matched
77
+ #
78
+ # @param [Object] elem elem to search for
79
+ # @param [Fixnum] low lower bound (inclusive)
80
+ # @param [Fixnum] high upper bound (inclusive, nil for last element)
81
+ # @param [:asc, :desc, :asc_eq, :desc_eq] mode matching mode
82
+ # @return [Object] first matching elem in self, or nil if not found
83
+ #
84
+ def bin_search(elem, mode, low = 0, high = -1)
85
+ dir = if ::BinSearch::MODE_IS_ASC.include?(mode) then +1 else -1 end
86
+ check_eq = ::BinSearch::MODE_CHECK_EQ.include?(mode)
87
+ high = size - 1 if high < 0
88
+ if block_given?
89
+ then _bin_search(elem, low, high, dir, check_eq) { |b| yield elem, b }
90
+ else _bin_search(elem, low, high, dir, check_eq) { |b| elem <=> b } end
91
+ end
92
+
93
+ # Binary search for the first elem in this array matching according to mode in the range
94
+ # (low..high-1)
95
+ #
96
+ # Elements are compared using <=> after mapping them using the provided block
97
+ #
98
+ # The supported modes are
99
+ # * :asc - array is expected to be sorted in ascending order, first geq elem is matched
100
+ # * :asc_eq - array expected to be sorted in ascending order, first eq elem is matched
101
+ # * :desc - array is expected to be sorted in descending order, first leq elem is matched
102
+ # * :desc_eq - array is expected to be sorted in descending order, first eq elem is matched
103
+ #
104
+ # @param [Object] elem elem to search for
105
+ # @param [Fixnum] low lower bound (inclusive)
106
+ # @param [Fixnum] high upper bound (inclusive, nil for last element)
107
+ # @param [:asc, :desc, :asc_eq, :desc_eq] mode matching mode
108
+ # @return [Object] first matching elem in self, or nil if not found
109
+ #
110
+ def bin_search_by(elem, mode, low = 0, high = -1)
111
+ dir = if ::BinSearch::MODE_IS_ASC.include?(mode) then +1 else -1 end
112
+ check_eq = ::BinSearch::MODE_CHECK_EQ.include?(mode)
113
+ high = size - 1 if high < 0
114
+ e = yield elem
115
+ _bin_search(elem, low, high, dir, check_eq) { |b| e <=> (yield b) }
116
+ end
117
+
118
+ # Binary search for the first elem in this array matching according to mode in the range
119
+ # (low..high-1)
120
+ #
121
+ # By default, <=> is used to compare elements. Alternatively, a comparator
122
+ # may be specified as block parameter
123
+ #
124
+ # The supported modes are
125
+ # * :asc - array is expected to be sorted in ascending order, first geq elem is matched
126
+ # * :asc_eq - array expected to be sorted in ascending order, first eq elem is matched
127
+ # * :desc - array is expected to be sorted in descending order, first leq elem is matched
128
+ # * :desc_eq - array is expected to be sorted in descending order, first eq elem is matched
129
+ #
130
+ # @param [Object] elem elem to search for
131
+ # @param [Fixnum] low lower bound (inclusive)
132
+ # @param [Fixnum] high upper bound (inclusive, nil for last element)
133
+ # @param [:asc, :desc, :asc_eq, :desc_eq] mode matching mode
134
+ # @return [Array] [index, first matching elem] in self, or nil if not found
135
+ #
136
+ def bin_assoc(elem, mode, low = 0, high = -1)
137
+ dir = if ::BinSearch::MODE_IS_ASC.include?(mode) then +1 else -1 end
138
+ check_eq = ::BinSearch::MODE_CHECK_EQ.include?(mode)
139
+ high = size - 1 if high < 0
140
+ if block_given?
141
+ then _bin_assoc(elem, low, high, dir, check_eq) { |b| yield elem, b }
142
+ else _bin_assoc(elem, low, high, dir, check_eq) { |b| elem <=> b } end
143
+ end
144
+
145
+ # Binary search for the first elem in this array matching according to mode in the range
146
+ # (low..high-1)
147
+ #
148
+ # Elements are compared using <=> after mapping them using the provided block
149
+ #
150
+ # The supported modes are
151
+ # * :asc - array is expected to be sorted in ascending order, first geq elem is matched
152
+ # * :asc_eq - array expected to be sorted in ascending order, first eq elem is matched
153
+ # * :desc - array is expected to be sorted in descending order, first leq elem is matched
154
+ # * :desc_eq - array is expected to be sorted in descending order, first eq elem is matched
155
+ #
156
+ # @param [Object] elem elem to search for
157
+ # @param [Fixnum] low lower bound (inclusive)
158
+ # @param [Fixnum] high upper bound (inclusive, nil for last element)
159
+ # @param [:asc, :desc, :asc_eq, :desc_eq] mode matching mode
160
+ # @return [Array] [index, first matching elem] in self, or nil if not found
161
+ #
162
+ def bin_assoc_by(elem, mode, low = 0, high = -1)
163
+ dir = if ::BinSearch::MODE_IS_ASC.include?(mode) then +1 else -1 end
164
+ check_eq = ::BinSearch::MODE_CHECK_EQ.include?(mode)
165
+ high = size - 1 if high < 0
166
+ e = yield elem
167
+ _bin_assoc(elem, low, high, dir, check_eq) { |b| e <=> (yield b) }
168
+ end
169
+
170
+ private
171
+
172
+ def _bin_index(elem, low, high, dir, check_eq)
173
+ sz = high - low
174
+ if (sz >> LIN_BITS).zero?
175
+ # On 2012 cpus, linear search is slightly faster than binary search
176
+ # if the number of searched elements is in the range of 50-100 elts
177
+ cur_index = low
178
+ while cur_index <= high
179
+ cur = self[cur_index]
180
+ cmp = yield cur
181
+ if cmp == dir
182
+ then cur_index += 1
183
+ else return (if (check_eq && cmp.nonzero?) then -1 else cur_index end) end
184
+ end
185
+ return -1
186
+ else
187
+ # Classic binary search
188
+ cmp_ = 0
189
+ last = -1
190
+ while low <= high
191
+ mid_index = low + (sz >> 1)
192
+ mid = self[mid_index]
193
+ cmp = yield mid
194
+ if cmp == dir
195
+ low = mid_index + 1
196
+ else
197
+ cmp_ = cmp
198
+ last = mid_index
199
+ high = mid_index - 1
200
+ end
201
+ sz -= 1
202
+ end
203
+ return (if (check_eq && cmp_.nonzero?) then -1 else last end)
204
+ end
205
+ end
206
+
207
+ def _bin_search(elem, low, high, dir, check_eq)
208
+ sz = high - low
209
+ cur = nil
210
+ if (sz >> LIN_BITS).zero?
211
+ # On 2012 cpus, linear search is slightly faster than binary search
212
+ # if the number of searched elements is in the range of 50-100 elts
213
+ cur_index = low
214
+ while cur_index <= high
215
+ cur = self[cur_index]
216
+ cmp = yield cur
217
+ if cmp == dir
218
+ then cur_index += 1
219
+ else return (if (check_eq && cmp.nonzero?) then nil else cur end) end
220
+ end
221
+ return nil
222
+ else
223
+ # Classic binary search
224
+ cmp_ = 0
225
+ last = -1
226
+ while low <= high
227
+ mid_index = low + (sz >> 1)
228
+ mid = self[mid_index]
229
+ cmp = yield mid
230
+ if cmp == dir
231
+ low = mid_index + 1
232
+ else
233
+ cmp_ = cmp
234
+ cur = mid
235
+ last = mid_index
236
+ high = mid_index - 1
237
+ end
238
+ sz -= 1
239
+ end
240
+ return (if (check_eq && cmp_.nonzero?) then nil else cur end)
241
+ end
242
+ end
243
+
244
+ def _bin_assoc(elem, low, high, dir, check_eq)
245
+ sz = high - low
246
+ cur = nil
247
+ if (sz >> LIN_BITS).zero?
248
+ # On 2012 cpus, linear search is slightly faster than binary search
249
+ # if the number of searched elements is in the range of 50-100 elts
250
+ cur_index = low
251
+ while cur_index <= high
252
+ cur = self[cur_index]
253
+ cmp = yield cur
254
+ if cmp == dir
255
+ cur_index += 1
256
+ else
257
+ return (if (!cur || (check_eq && cmp.nonzero?)) then nil else [cur_index, cur] end)
258
+ end
259
+ end
260
+ return nil
261
+ else
262
+ # Classic binary search
263
+ cmp_ = 0
264
+ last = -1
265
+ while low <= high
266
+ mid_index = low + (sz >> 1)
267
+ mid = self[mid_index]
268
+ cmp = yield mid
269
+ if cmp == dir
270
+ low = mid_index + 1
271
+ else
272
+ cmp_ = cmp
273
+ cur = mid
274
+ last = mid_index
275
+ high = mid_index - 1
276
+ end
277
+ sz -= 1
278
+ end
279
+ return (if (!cur || (check_eq && cmp_.nonzero?)) then nil else [last, cur] end)
280
+ end
281
+ end
282
+
283
+ end # module Methods
284
+
285
+ end # module BinSeach
286
+
287
+ class ::Array
288
+ include ::BinSearch::Methods
289
+ end
@@ -0,0 +1,3 @@
1
+ module BinSearch
2
+ VERSION = '0.1'
3
+ end
@@ -0,0 +1,151 @@
1
+ require 'bin_search'
2
+
3
+ describe ::BinSearch do
4
+
5
+ context "Small arrays (:asc, :asc_eq)" do
6
+
7
+ it "should find the first element" do
8
+ [1, 2, 2, 3, 5].bin_index(1, :asc).should be_==(0)
9
+ end
10
+
11
+ it "should find the first element when queried using a smaller element" do
12
+ [1, 2, 2, 3, 5].bin_index(0, :asc).should be_==(0)
13
+ end
14
+
15
+ it "should find a middle element" do
16
+ [1, 2, 2, 3, 5].bin_index(2, :asc).should be_==(1)
17
+ end
18
+
19
+ it "should find the last element" do
20
+ [1, 2, 2, 3, 5].bin_index(5, :asc).should be_==(4)
21
+ end
22
+
23
+ it "should not find a missing element" do
24
+ [1, 2, 2, 3, 5].bin_index(6, :asc).should be_==(-1)
25
+ end
26
+
27
+ it "should not find an element that is not eq if called with equality checking turned on" do
28
+ [1, 2, 2, 3, 5].bin_index(2.5, :asc_eq).should be_==(-1)
29
+ end
30
+
31
+ end
32
+
33
+ context "Large arrays (:asc, :asc_eq)" do
34
+
35
+ it "should find the first element" do
36
+ Range.new(1, 10000).to_a.bin_index(1, :asc).should be_==(0)
37
+ end
38
+
39
+ it "should find the first element when queried using a smaller element" do
40
+ Range.new(1, 10000).to_a.bin_index(0, :asc).should be_==(0)
41
+ end
42
+
43
+ it "should find a middle element" do
44
+ Range.new(1, 10000).to_a.bin_index(5000, :asc).should be_==(4999)
45
+ end
46
+
47
+ it "should find the last element" do
48
+ Range.new(1, 10000).to_a.bin_index(10000, :asc).should be_==(9999)
49
+ end
50
+
51
+ it "should not find a missing element" do
52
+ Range.new(1, 10000).to_a.bin_index(10001, :asc).should be_==(-1)
53
+ end
54
+
55
+ it "should not find an element that is not eq if called with equality checking turned on" do
56
+ Range.new(1, 10000).to_a.bin_index(5000.5, :asc_eq).should be_==(-1)
57
+ end
58
+
59
+ end
60
+
61
+ context "Small arrays (:desc, :desc_eq)" do
62
+
63
+ it "should find the first element" do
64
+ [1, 2, 2, 3, 5].reverse.bin_index(5, :desc).should be_==(0)
65
+ end
66
+
67
+ it "should find the first element when queried using a larger element" do
68
+ [1, 2, 2, 3, 5].bin_index(6, :desc).should be_==(0)
69
+ end
70
+
71
+ it "should find a middle element" do
72
+ [1, 2, 2, 3, 5].reverse.bin_index(2, :desc).should be_==(2)
73
+ end
74
+
75
+ it "should find the last element" do
76
+ [1, 2, 2, 3, 5].reverse.bin_index(1, :desc).should be_==(4)
77
+ end
78
+
79
+ it "should not find a missing element" do
80
+ [1, 2, 2, 3, 5].reverse.bin_index(0, :desc).should be_==(-1)
81
+ end
82
+
83
+ it "should not find an element that is not eq if called with equality checking turned on" do
84
+ [1, 2, 2, 3, 5].reverse.bin_index(2.5, :desc_eq).should be_==(-1)
85
+ end
86
+
87
+ end
88
+
89
+ context "Large arrays (:desc, :desc_eq)" do
90
+
91
+ it "should find the first element" do
92
+ Range.new(1, 10000).to_a.reverse.bin_index(10000, :desc).should be_==(0)
93
+ end
94
+
95
+ it "should find the first element when queried using a larger element" do
96
+ Range.new(1, 10000).to_a.reverse.bin_index(10001, :desc).should be_==(0)
97
+ end
98
+
99
+ it "should find a middle element" do
100
+ Range.new(1, 10000).to_a.reverse.bin_index(5000, :desc).should be_==(5000)
101
+ end
102
+
103
+ it "should find the last element" do
104
+ Range.new(1, 10000).to_a.reverse.bin_index(1, :desc).should be_==(9999)
105
+ end
106
+
107
+ it "should not find a missing element" do
108
+ Range.new(1, 10000).to_a.reverse.bin_index(0, :desc).should be_==(-1)
109
+ end
110
+
111
+ it "should not find an element that is not eq if called with equality checking turned on" do
112
+ Range.new(1, 10000).to_a.reverse.bin_index(5000.5, :desc_eq).should be_==(-1)
113
+ end
114
+
115
+ end
116
+
117
+ context "API" do
118
+
119
+ it "should return the found element when called via bin_search" do
120
+ [1, 2, 2, 3, 5].bin_search(2, :asc).should be_==(2)
121
+ end
122
+
123
+ it "should return nil when called via bin_search with a missing element" do
124
+ [1, 2, 2, 3, 5].bin_search(10, :asc).should be_==(nil)
125
+ end
126
+
127
+ it "should return the found element's assoc pair when called via bin_assoc" do
128
+ [1, 2, 2, 3, 5].bin_assoc(2, :asc).should be_==([1, 2])
129
+ end
130
+
131
+ it "should return nil when called via bin_assoc with a missing element" do
132
+ [1, 2, 2, 3, 5].bin_assoc(10, :asc).should be_==(nil)
133
+ end
134
+
135
+ end
136
+
137
+ context "Comparator wrapping" do
138
+
139
+ it 'should use a provided comparator with bin_index' do
140
+ idx = [1, 2, 2, 3, 5].reverse.bin_index(2, :asc) { |a, b| b <=> a }
141
+ idx.should be_==(2)
142
+ end
143
+
144
+ it 'should use a provided mapper with bin_index_by' do
145
+ idx = [1, 2, 2, 3, 5].reverse.bin_index_by(2, :asc) { |a| -a }
146
+ idx.should be_==(2)
147
+ end
148
+
149
+ end
150
+
151
+ end
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler.require
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bin_search
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stefan Plantikow
9
+ autorequire:
10
+ bindir: script
11
+ cert_chain: []
12
+ date: 2012-05-28 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70278990982880 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70278990982880
25
+ - !ruby/object:Gem::Dependency
26
+ name: simplecov
27
+ requirement: &70278990982460 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70278990982460
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70278973891460 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70278973891460
47
+ - !ruby/object:Gem::Dependency
48
+ name: yard
49
+ requirement: &70278973891040 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70278973891040
58
+ - !ruby/object:Gem::Dependency
59
+ name: redcarpet
60
+ requirement: &70278973890620 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70278973890620
69
+ description: Pure ruby implementation of binary search for Ruby arrays and similiar
70
+ data structures. Supports ascending and descending sort order, searching for exact
71
+ and nearest matches, and has a versatile API. Uses linear search for small arrays
72
+ to make use of the internal cache of moden CPUs.
73
+ email: stefan.plantikow@googlemail.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - .gitignore
79
+ - AUTHORS
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin_search.gemspec
85
+ - lib/bin_search.rb
86
+ - lib/bin_search/bin_search.rb
87
+ - lib/bin_search/version.rb
88
+ - spec/lib/bin_search/bin_search_spec.rb
89
+ - spec/spec_helper.rb
90
+ homepage: https://github.com/boggle/bin_search
91
+ licenses:
92
+ - PUBLIC DOMAIN WITHOUT ANY WARRANTY
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project: bin_search
111
+ rubygems_version: 1.8.10
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Binary search in sorted arrays
115
+ test_files:
116
+ - spec/lib/bin_search/bin_search_spec.rb
117
+ - spec/spec_helper.rb
118
+ has_rdoc: