ms-msrun 0.0.1 → 0.1.0
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 +3 -4
- data/Rakefile +20 -5
- data/bin/base64_to_array.rb +3 -4
- data/bin/ms_to_obiwarp.rb +6 -15
- data/bin/ms_to_search.rb +15 -22
- data/lib/lmat.rb +47 -53
- data/lib/ms/msrun.rb +98 -108
- data/lib/ms/msrun/axml/mzxml.rb +6 -8
- data/lib/ms/msrun/index.rb +130 -0
- data/lib/ms/msrun/mzxml.rb +12 -0
- data/lib/ms/msrun/nokogiri.rb +12 -0
- data/lib/ms/msrun/nokogiri/mzxml.rb +168 -0
- data/lib/ms/msrun/regexp/mzxml.rb +126 -0
- data/lib/ms/msrun/search.rb +25 -21
- data/lib/ms/msrun/sha1.rb +36 -0
- data/lib/ms/mzxml.rb +12 -0
- data/lib/ms/precursor.rb +3 -2
- data/lib/ms/precursor/lazy_parent.rb +28 -0
- data/lib/ms/scan.rb +2 -29
- data/lib/ms/spectrum/compare.rb +42 -2
- data/lib/ms/spectrum/filter.rb +1 -1
- data/spec/ms/msrun/index_spec.rb +60 -0
- data/spec/ms/msrun/scan_spec.rb +78 -0
- data/spec/ms/msrun/search_spec.rb +6 -7
- data/spec/ms/msrun/sha1_spec.rb +23 -0
- data/spec/ms/msrun_spec.rb +111 -3
- data/spec/ms/scan_spec.rb +2 -2
- data/spec/ms/spectrum/compare_spec.rb +13 -6
- data/spec/ms/spectrum/filter_spec.rb +3 -3
- metadata +42 -21
- data/lib/bsearch.rb +0 -120
- data/lib/ms/spectrum.rb +0 -373
@@ -4,8 +4,8 @@ require 'ms/msrun'
|
|
4
4
|
|
5
5
|
class SearchSpec < MiniTest::Spec
|
6
6
|
|
7
|
-
|
8
|
-
@file = '/
|
7
|
+
it 'creates mgf formatted files' do
|
8
|
+
@file = TESTFILES + '/opd1/000.v1.mzXML'
|
9
9
|
params = {
|
10
10
|
:bottom_mh => 300.0,
|
11
11
|
:top_mh => 4500.0,
|
@@ -27,7 +27,7 @@ class SearchSpec < MiniTest::Spec
|
|
27
27
|
]
|
28
28
|
Ms::Msrun.open(@file) do |ms|
|
29
29
|
no_scans.each do |k,v|
|
30
|
-
ms.to_mgf(
|
30
|
+
ms.to_mgf( k => v).must_equal ""
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -43,14 +43,13 @@ class SearchSpec < MiniTest::Spec
|
|
43
43
|
[:bottom_mh, 500],
|
44
44
|
]
|
45
45
|
Ms::Msrun.open(@file) do |ms|
|
46
|
-
|
47
|
-
reply = ms.to_mgf(
|
48
|
-
puts reply
|
46
|
+
some_scans.each do |k,v|
|
47
|
+
reply = ms.to_mgf(k => v)
|
49
48
|
reply.must_match(/BEGIN.IONS/)
|
50
49
|
reply.must_match(/END.IONS/)
|
51
50
|
end
|
52
51
|
end
|
53
|
-
|
52
|
+
# TODO: should write some more specs here
|
54
53
|
end
|
55
54
|
|
56
55
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
require 'ms/msrun/sha1'
|
4
|
+
|
5
|
+
class Sha1Spec < MiniTest::Spec
|
6
|
+
def initialize(*args)
|
7
|
+
@files = %w(000.v1.mzXML 020.v2.0.readw.mzXML 000.v2.1.mzXML).map do |file|
|
8
|
+
TESTFILES + "/opd1/#{file}"
|
9
|
+
end
|
10
|
+
super(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
## NOTE: this does NOT match up to real files yet!
|
14
|
+
xit 'can determine a sha1 for an mzxml file' do
|
15
|
+
@files.each do |file|
|
16
|
+
(actual, recorded) = Ms::Msrun::Sha1.digest_mzxml_file file
|
17
|
+
[actual, recorded].each {|v| assert !v.nil? }
|
18
|
+
actual.must_equal recorded
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
end
|
data/spec/ms/msrun_spec.rb
CHANGED
@@ -8,27 +8,111 @@ module MsrunSpec
|
|
8
8
|
before do
|
9
9
|
@file = nil # you need to define this!
|
10
10
|
@key = nil # <- do nothing with this.
|
11
|
+
@nums = (1..20).to_a # define scan numbers
|
11
12
|
end
|
12
13
|
|
13
14
|
def key
|
14
15
|
@key || @key = YAML.load_file(@file + '.key.yml')
|
15
16
|
end
|
16
17
|
|
18
|
+
def hash_match(hash, obj)
|
19
|
+
#$DEBUG = 1
|
20
|
+
puts "SCAN: #{obj.num}" if $DEBUG && obj.respond_to?(:num)
|
21
|
+
hash.each do |k,v|
|
22
|
+
if v.is_a?(Hash)
|
23
|
+
hash_match(v, obj.send(k.to_sym))
|
24
|
+
else
|
25
|
+
puts "#{k}: #{v} but was #{obj.send(k.to_sym)}" if obj.send(k.to_sym) != v
|
26
|
+
obj.send(k.to_sym).must_equal v
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
17
31
|
it 'reads header information' do
|
18
32
|
Ms::Msrun.open(@file) do |ms|
|
19
33
|
key['header'].each do |k,v|
|
34
|
+
#puts "K: #{k} Vexp: #{v} Vact: #{ms.send(k.to_sym)}"
|
20
35
|
ms.send(k.to_sym).must_equal v
|
21
36
|
end
|
22
37
|
end
|
23
38
|
end
|
24
39
|
|
40
|
+
it 'can access random scans' do
|
41
|
+
Ms::Msrun.open(@file) do |ms|
|
42
|
+
scan = ms.scan(20)
|
43
|
+
hash_match(key['scans'][20], scan)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can read all scans' do
|
48
|
+
num_required_scans = key['scans'].size
|
49
|
+
Ms::Msrun.open(@file) do |ms|
|
50
|
+
ms.each do |scan|
|
51
|
+
if hash = key['scans'][scan.num]
|
52
|
+
hash_match(hash, scan)
|
53
|
+
num_required_scans -= 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
num_required_scans.must_equal 0
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'can read scans of a certain ms_level' do
|
61
|
+
nums = [1,5,9,13,17]
|
62
|
+
Ms::Msrun.open(@file) do |ms|
|
63
|
+
ms.each(:ms_level => 1) do |scan|
|
64
|
+
scan.num.must_equal nums.shift
|
65
|
+
end
|
66
|
+
end
|
67
|
+
nums = [2,3,4,6,7,8,10,11,12,14,15,16,18,19,20]
|
68
|
+
Ms::Msrun.foreach(@file, :ms_level => 2) do |scan|
|
69
|
+
scan.num.must_equal nums.shift
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'can avoid reading spectra' do
|
74
|
+
nums = @nums.map
|
75
|
+
Ms::Msrun.foreach(@file, :spectrum => false) do |scan|
|
76
|
+
assert scan.spectrum.nil?
|
77
|
+
scan.num.must_equal nums.shift
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'can avoid reading precursor information' do
|
82
|
+
Ms::Msrun.foreach(@file, :precursor => false) do |scan|
|
83
|
+
assert scan.precursor.nil?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'gives scan counts for different ms levels' do
|
88
|
+
Ms::Msrun.open(@file) do |ms|
|
89
|
+
key['scan_count'].each do |index, count|
|
90
|
+
ms.scan_count(index).must_equal count
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'gives start and end mz even if the information is not given' do
|
96
|
+
Ms::Msrun.open(@file) do |ms|
|
97
|
+
ms.start_and_end_mz_brute_force.must_equal(key['start_and_end_mz'][1])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
25
101
|
end
|
26
102
|
|
27
103
|
class Mzxml_v1 < MiniTest::Spec
|
28
104
|
include MsrunSpec
|
29
105
|
before do
|
30
106
|
super
|
31
|
-
@file = '/
|
107
|
+
@file = TESTFILES + '/opd1/000.v1.mzXML'
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'can give start and end mz' do
|
111
|
+
# scan has attributes startMz endMz
|
112
|
+
Ms::Msrun.open(@file) do |ms|
|
113
|
+
#ms.start_and_end_mz.must_equal([300.0, 1500.0])
|
114
|
+
ms.start_and_end_mz.must_equal(key['start_and_end_mz'][1])
|
115
|
+
end
|
32
116
|
end
|
33
117
|
end
|
34
118
|
|
@@ -36,7 +120,7 @@ class Mzxml_v2_0 < MiniTest::Spec
|
|
36
120
|
include MsrunSpec
|
37
121
|
before do
|
38
122
|
super
|
39
|
-
@file = '/
|
123
|
+
@file = TESTFILES + '/opd1/020.v2.0.readw.mzXML'
|
40
124
|
end
|
41
125
|
end
|
42
126
|
|
@@ -44,7 +128,31 @@ class Mzxml_v2_1 < MiniTest::Spec
|
|
44
128
|
include MsrunSpec
|
45
129
|
before do
|
46
130
|
super
|
47
|
-
@file = '/
|
131
|
+
@file = TESTFILES + '/opd1/000.v2.1.mzXML'
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'gives nil if scans do not have start and end mz info' do
|
135
|
+
# scans do not have startMz endMz or filterLine
|
136
|
+
Ms::Msrun.open(@file) do |ms|
|
137
|
+
ms.start_and_end_mz.must_equal([nil, nil])
|
138
|
+
end
|
48
139
|
end
|
140
|
+
|
141
|
+
it 'gives start and end mz if filterLine present' do
|
142
|
+
newname = @file + ".TMP.mzXML"
|
143
|
+
File.open(newname, 'w') do |out|
|
144
|
+
IO.foreach(@file) do |line|
|
145
|
+
if line =~ /msLevel="1"/
|
146
|
+
out.puts %Q{ filterLine="FTMS + p NSI Full ms [300.00-1500.00]"}
|
147
|
+
end
|
148
|
+
out.print line
|
149
|
+
end
|
150
|
+
end
|
151
|
+
Ms::Msrun.open(newname) do |ms|
|
152
|
+
ms.start_and_end_mz.must_equal([300.0, 1500.0])
|
153
|
+
end
|
154
|
+
File.unlink(newname) if File.exist?(newname)
|
155
|
+
end
|
156
|
+
|
49
157
|
end
|
50
158
|
|
data/spec/ms/scan_spec.rb
CHANGED
@@ -7,11 +7,11 @@ class ScanUnitSpec < MiniTest::Spec
|
|
7
7
|
before do
|
8
8
|
@scan = Ms::Scan.new
|
9
9
|
@scan.precursor = Ms::Precursor.new
|
10
|
-
@scan.spectrum = Ms::Spectrum.new([1,2,3,4], [2,4,4,2])
|
10
|
+
@scan.spectrum = Ms::Spectrum.new([[1,2,3,4], [2,4,4,2]])
|
11
11
|
end
|
12
12
|
|
13
13
|
|
14
|
-
|
14
|
+
it 'determines if its +1 or not' do
|
15
15
|
# these have not been checked for accuracy, just sanity
|
16
16
|
reply = [0.1,2.5, 3.5, 5].map do |prec_mz|
|
17
17
|
@scan.precursor.mz = prec_mz
|
@@ -13,11 +13,11 @@ class CompareSpec < MiniTest::Spec
|
|
13
13
|
include Ms
|
14
14
|
|
15
15
|
before do
|
16
|
-
@a = Spectrum.new([0,2,3,4], [5,6,7,8])
|
17
|
-
@b = Spectrum.new([0, 1.5, 3.5, 5.5], [9,10,11,12])
|
16
|
+
@a = Spectrum.new([[0,2,3,4], [5,6,7,8]])
|
17
|
+
@b = Spectrum.new([[0, 1.5, 3.5, 5.5], [9,10,11,12]])
|
18
18
|
|
19
|
-
@c = Spectrum.new([0, 1], [8,9])
|
20
|
-
@d = Spectrum.new([0.6, 0.75], [10,11])
|
19
|
+
@c = Spectrum.new([[0, 1], [8,9]])
|
20
|
+
@d = Spectrum.new([[0.6, 0.75], [10,11]])
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'compares spectra' do
|
@@ -35,10 +35,17 @@ class CompareSpec < MiniTest::Spec
|
|
35
35
|
end
|
36
36
|
|
37
37
|
it 'computes similarity score' do
|
38
|
-
|
39
|
-
|
38
|
+
@a.sim_score(@a, :radius => 0.1).must_equal 1.0
|
39
|
+
# this is just frozen, not verified:
|
40
|
+
@a.sim_score(@b).must_be_close_to 0.702945603476432, 0.000001
|
40
41
|
end
|
41
42
|
|
43
|
+
it 'computes a pic score' do
|
44
|
+
@a.pic_score(@a, :radius => 0.01).must_equal 100.0
|
45
|
+
@a.pic_score(@d, :radius => 0.01).must_equal 0.0
|
46
|
+
# frozen:
|
47
|
+
@a.pic_score(@b).must_be_close_to 68.4981684981685, 0.000001
|
48
|
+
end
|
42
49
|
end
|
43
50
|
|
44
51
|
|
@@ -14,14 +14,14 @@ class FilterSpec < MiniTest::Spec
|
|
14
14
|
include Ms
|
15
15
|
|
16
16
|
before do
|
17
|
-
@a = Spectrum.new([0,5,10, 15,16,17,18, 20.1], [0,1,2, 3,8,10,4, 0])
|
18
|
-
@null = Spectrum.new
|
17
|
+
@a = Spectrum.new([[0,5,10, 15,16,17,18, 20.1], [0,1,2, 3,8,10,4, 0]])
|
18
|
+
@null = Spectrum.new([[],[]])
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'filters spectra' do
|
22
22
|
spec = @a.filter(:bins, :bin_width => 10, :num_peaks => 2)
|
23
23
|
spec.mzs.must_equal [5,10,16,17,20.1]
|
24
|
-
spec.
|
24
|
+
spec.intensities.must_equal [1,2,8,10,0]
|
25
25
|
|
26
26
|
@a.filter(:bins, :bin_width => 100, :num_peaks => 8).must_equal @a
|
27
27
|
@a.filter(:bins, :bin_width => 1, :num_peaks => 1).must_equal @a
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ms-msrun
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Prince
|
@@ -9,18 +9,28 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-11-11 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
16
|
+
name: ms-core
|
17
17
|
type: :runtime
|
18
18
|
version_requirement:
|
19
19
|
version_requirements: !ruby/object:Gem::Requirement
|
20
20
|
requirements:
|
21
|
-
- -
|
21
|
+
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: nokogiri
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
24
34
|
version:
|
25
35
|
- !ruby/object:Gem::Dependency
|
26
36
|
name: runarray
|
@@ -35,30 +45,38 @@ dependencies:
|
|
35
45
|
description: A library for working with LC/MS runs. Part of mspire. Has parsers for mzXML v1, 2, and 3, mzData and mzML. Can convert to commonly desired search output (such as mgf)
|
36
46
|
email: jtprince@gmail.com
|
37
47
|
executables:
|
38
|
-
- ms_to_search.rb
|
39
48
|
- base64_to_array.rb
|
40
49
|
- ms_to_obiwarp.rb
|
50
|
+
- ms_to_search.rb
|
41
51
|
extensions: []
|
42
52
|
|
43
53
|
extra_rdoc_files:
|
44
54
|
- README
|
45
55
|
- LICENSE
|
46
56
|
files:
|
47
|
-
- lib/
|
48
|
-
- lib/ms/msrun/axml/mzxml.rb
|
49
|
-
- lib/ms/msrun/search.rb
|
50
|
-
- lib/ms/msrun.rb
|
51
|
-
- lib/ms/scan.rb
|
52
|
-
- lib/ms/spectrum/compare.rb
|
57
|
+
- lib/lmat.rb
|
53
58
|
- lib/ms/spectrum/filter.rb
|
59
|
+
- lib/ms/spectrum/compare.rb
|
60
|
+
- lib/ms/scan.rb
|
61
|
+
- lib/ms/msrun/sha1.rb
|
62
|
+
- lib/ms/msrun/regexp/mzxml.rb
|
63
|
+
- lib/ms/msrun/search.rb
|
64
|
+
- lib/ms/msrun/index.rb
|
65
|
+
- lib/ms/msrun/nokogiri/mzxml.rb
|
66
|
+
- lib/ms/msrun/axml/mzxml.rb
|
67
|
+
- lib/ms/msrun/mzxml.rb
|
68
|
+
- lib/ms/msrun/nokogiri.rb
|
54
69
|
- lib/ms/precursor.rb
|
55
|
-
- lib/
|
56
|
-
- lib/
|
70
|
+
- lib/ms/mzxml.rb
|
71
|
+
- lib/ms/msrun.rb
|
72
|
+
- lib/ms/precursor/lazy_parent.rb
|
57
73
|
- README
|
58
74
|
- LICENSE
|
59
75
|
- Rakefile
|
60
76
|
has_rdoc: true
|
61
77
|
homepage: http://mspire.rubyforge.org/projects/ms-msrun
|
78
|
+
licenses: []
|
79
|
+
|
62
80
|
post_install_message:
|
63
81
|
rdoc_options:
|
64
82
|
- --main
|
@@ -81,17 +99,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
99
|
- !ruby/object:Gem::Version
|
82
100
|
version: "0"
|
83
101
|
version:
|
84
|
-
requirements:
|
85
|
-
|
102
|
+
requirements: []
|
103
|
+
|
86
104
|
rubyforge_project: mspire
|
87
|
-
rubygems_version: 1.3.
|
105
|
+
rubygems_version: 1.3.2
|
88
106
|
signing_key:
|
89
|
-
specification_version:
|
107
|
+
specification_version: 3
|
90
108
|
summary: A library for working with LC/MS runs
|
91
109
|
test_files:
|
92
|
-
- spec/ms/msrun_spec.rb
|
93
|
-
- spec/ms/msrun/search_spec.rb
|
94
|
-
- spec/ms/spectrum/compare_spec.rb
|
95
110
|
- spec/ms/spectrum/filter_spec.rb
|
111
|
+
- spec/ms/spectrum/compare_spec.rb
|
112
|
+
- spec/ms/msrun/search_spec.rb
|
113
|
+
- spec/ms/msrun/sha1_spec.rb
|
114
|
+
- spec/ms/msrun/index_spec.rb
|
115
|
+
- spec/ms/msrun/scan_spec.rb
|
116
|
+
- spec/ms/msrun_spec.rb
|
96
117
|
- spec/ms/scan_spec.rb
|
97
118
|
- spec/lmat_spec.rb
|
data/lib/bsearch.rb
DELETED
@@ -1,120 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Ruby/Bsearch - a binary search library for Ruby.
|
3
|
-
#
|
4
|
-
# Copyright (C) 2001 Satoru Takabayashi <satoru@namazu.org>
|
5
|
-
# All rights reserved.
|
6
|
-
# This is free software with ABSOLUTELY NO WARRANTY.
|
7
|
-
#
|
8
|
-
# You can redistribute it and/or modify it under the terms of
|
9
|
-
# the Ruby's licence.
|
10
|
-
#
|
11
|
-
# Example:
|
12
|
-
#
|
13
|
-
# % irb -r ./bsearch.rb
|
14
|
-
# >> %w(a b c c c d e f).bsearch_first {|x| x <=> "c"}
|
15
|
-
# => 2
|
16
|
-
# >> %w(a b c c c d e f).bsearch_last {|x| x <=> "c"}
|
17
|
-
# => 4
|
18
|
-
# >> %w(a b c e f).bsearch_first {|x| x <=> "c"}
|
19
|
-
# => 2
|
20
|
-
# >> %w(a b e f).bsearch_first {|x| x <=> "c"}
|
21
|
-
# => nil
|
22
|
-
# >> %w(a b e f).bsearch_last {|x| x <=> "c"}
|
23
|
-
# => nil
|
24
|
-
# >> %w(a b e f).bsearch_lower_boundary {|x| x <=> "c"}
|
25
|
-
# => 2
|
26
|
-
# >> %w(a b e f).bsearch_upper_boundary {|x| x <=> "c"}
|
27
|
-
# => 2
|
28
|
-
# >> %w(a b c c c d e f).bsearch_range {|x| x <=> "c"}
|
29
|
-
# => 2...5
|
30
|
-
# >> %w(a b c d e f).bsearch_range {|x| x <=> "c"}
|
31
|
-
# => 2...3
|
32
|
-
# >> %w(a b d e f).bsearch_range {|x| x <=> "c"}
|
33
|
-
# => 2...2
|
34
|
-
|
35
|
-
module Bsearch
|
36
|
-
VERSION = '1.5'
|
37
|
-
end
|
38
|
-
|
39
|
-
class Array
|
40
|
-
#
|
41
|
-
# The binary search algorithm is extracted from Jon Bentley's
|
42
|
-
# Programming Pearls 2nd ed. p.93
|
43
|
-
#
|
44
|
-
|
45
|
-
#
|
46
|
-
# Return the lower boundary. (inside)
|
47
|
-
#
|
48
|
-
def bsearch_lower_boundary (range = 0 ... self.length, &block)
|
49
|
-
lower = range.first() -1
|
50
|
-
upper = if range.exclude_end? then range.last else range.last + 1 end
|
51
|
-
while lower + 1 != upper
|
52
|
-
mid = ((lower + upper) / 2).to_i # for working with mathn.rb (Rational)
|
53
|
-
if yield(self[mid]) < 0
|
54
|
-
lower = mid
|
55
|
-
else
|
56
|
-
upper = mid
|
57
|
-
end
|
58
|
-
end
|
59
|
-
return upper
|
60
|
-
end
|
61
|
-
|
62
|
-
#
|
63
|
-
# This method searches the FIRST occurrence which satisfies a
|
64
|
-
# condition given by a block in binary fashion and return the
|
65
|
-
# index of the first occurrence. Return nil if not found.
|
66
|
-
#
|
67
|
-
def bsearch_first (range = 0 ... self.length, &block)
|
68
|
-
boundary = bsearch_lower_boundary(range, &block)
|
69
|
-
if boundary >= self.length || yield(self[boundary]) != 0
|
70
|
-
return nil
|
71
|
-
else
|
72
|
-
return boundary
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
alias bsearch bsearch_first
|
77
|
-
|
78
|
-
#
|
79
|
-
# Return the upper boundary. (outside)
|
80
|
-
#
|
81
|
-
def bsearch_upper_boundary (range = 0 ... self.length, &block)
|
82
|
-
lower = range.first() -1
|
83
|
-
upper = if range.exclude_end? then range.last else range.last + 1 end
|
84
|
-
while lower + 1 != upper
|
85
|
-
mid = ((lower + upper) / 2).to_i # for working with mathn.rb (Rational)
|
86
|
-
if yield(self[mid]) <= 0
|
87
|
-
lower = mid
|
88
|
-
else
|
89
|
-
upper = mid
|
90
|
-
end
|
91
|
-
end
|
92
|
-
return lower + 1 # outside of the matching range.
|
93
|
-
end
|
94
|
-
|
95
|
-
#
|
96
|
-
# This method searches the LAST occurrence which satisfies a
|
97
|
-
# condition given by a block in binary fashion and return the
|
98
|
-
# index of the last occurrence. Return nil if not found.
|
99
|
-
#
|
100
|
-
def bsearch_last (range = 0 ... self.length, &block)
|
101
|
-
# `- 1' for canceling `lower + 1' in bsearch_upper_boundary.
|
102
|
-
boundary = bsearch_upper_boundary(range, &block) - 1
|
103
|
-
|
104
|
-
if (boundary <= -1 || yield(self[boundary]) != 0)
|
105
|
-
return nil
|
106
|
-
else
|
107
|
-
return boundary
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
#
|
112
|
-
# Return the search result as a Range object.
|
113
|
-
#
|
114
|
-
def bsearch_range (range = 0 ... self.length, &block)
|
115
|
-
lower = bsearch_lower_boundary(range, &block)
|
116
|
-
upper = bsearch_upper_boundary(range, &block)
|
117
|
-
return lower ... upper
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|