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