segment_tree 0.1.0 → 0.2.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.
- checksums.yaml +7 -0
- data/lib/segment_tree/version.rb +2 -2
- data/lib/segment_tree.rb +55 -3
- data/segment_tree.gemspec +7 -6
- data/spec/segment_tree_spec.rb +125 -47
- data/spec/spec_helper.rb +4 -4
- metadata +32 -25
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 59ea55a8bf39f04d9d5d8fc248c8d8eaa5cb2c65692fd25c22ccc3b5a5ac14d0
|
4
|
+
data.tar.gz: a9b683819704f99d933a04bbf89272dcb00e87908a2a876aebd798268ee1c35d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5bf8d0dbbad097d1e9f2ea887eaa71f02f253c27ee2d27bed792781ebc858a9e7e116cb42176939a10bc30a670ce820ad2eeb26be67ad40b752ce31f2d1eb455
|
7
|
+
data.tar.gz: 1cd80e31aaf356a599dea612fb6b0e21bb6551b02205a02eb982eb47a9ce6ea6d61336c17060c8b0444bc3764f8c6eb0a8fc0d0fa272c7ab90ce530605cbf1f1
|
data/lib/segment_tree/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
class SegmentTree
|
2
|
-
VERSION =
|
3
|
-
end
|
2
|
+
VERSION = '0.2.0'
|
3
|
+
end
|
data/lib/segment_tree.rb
CHANGED
@@ -31,8 +31,38 @@ class SegmentTree
|
|
31
31
|
else cmp
|
32
32
|
end
|
33
33
|
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
other.is_a?(self.class) &&
|
37
|
+
@range == other.range &&
|
38
|
+
@value == other.value
|
39
|
+
end
|
40
|
+
|
41
|
+
def eql?(other)
|
42
|
+
other.is_a?(self.class) &&
|
43
|
+
@range.eql?(other.range) &&
|
44
|
+
@value.eql?(other.value)
|
45
|
+
end
|
46
|
+
|
47
|
+
def hash
|
48
|
+
[@range, @value].hash
|
49
|
+
end
|
50
|
+
|
51
|
+
def marshal_dump
|
52
|
+
{
|
53
|
+
range: @range,
|
54
|
+
value: @value,
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def marshal_load(serialized_tree)
|
59
|
+
@range = serialized_tree[:range]
|
60
|
+
@value = serialized_tree[:value]
|
61
|
+
end
|
34
62
|
end
|
35
63
|
|
64
|
+
attr_reader :segments
|
65
|
+
|
36
66
|
# Build a segment tree from +data+.
|
37
67
|
#
|
38
68
|
# Data can be one of the following:
|
@@ -83,6 +113,28 @@ class SegmentTree
|
|
83
113
|
end
|
84
114
|
end
|
85
115
|
|
116
|
+
def ==(other)
|
117
|
+
other.is_a?(self.class) && @segments == other.segments
|
118
|
+
end
|
119
|
+
|
120
|
+
def eql?(other)
|
121
|
+
other.is_a?(self.class) && @segments.eql?(other.segments)
|
122
|
+
end
|
123
|
+
|
124
|
+
def hash
|
125
|
+
@segments.hash
|
126
|
+
end
|
127
|
+
|
128
|
+
def marshal_dump
|
129
|
+
{
|
130
|
+
segments: @segments,
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
def marshal_load(serialized_tree)
|
135
|
+
@segments = serialized_tree[:segments]
|
136
|
+
end
|
137
|
+
|
86
138
|
private
|
87
139
|
def matches?(x, low_idx, high_idx, idx) #:nodoc:
|
88
140
|
low, high = @segments[low_idx], @segments[high_idx]
|
@@ -91,9 +143,9 @@ class SegmentTree
|
|
91
143
|
right = idx < @segments.size - 1 && @segments[idx + 1]
|
92
144
|
|
93
145
|
case
|
94
|
-
when left &&
|
95
|
-
when segment.range.
|
96
|
-
when right &&
|
146
|
+
when left && low.range.begin <= x && x <= left.range.end then -1
|
147
|
+
when segment.range.begin <=x && x <= segment.range.end then 0
|
148
|
+
when right && right.range.begin <=x && x <= high.range.end then 1
|
97
149
|
else nil
|
98
150
|
end
|
99
151
|
end
|
data/segment_tree.gemspec
CHANGED
@@ -2,18 +2,19 @@
|
|
2
2
|
require File.expand_path('../lib/segment_tree/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.author =
|
6
|
-
gem.email =
|
5
|
+
gem.author = 'Alexei Mikhailov'
|
6
|
+
gem.email = 'amikhailov83@gmail.com'
|
7
7
|
gem.description = %q{Tree data structure for storing segments. It allows querying which of the stored segments contain a given point.}
|
8
8
|
gem.summary = %q{Tree data structure for storing segments. It allows querying which of the stored segments contain a given point.}
|
9
|
-
gem.homepage =
|
9
|
+
gem.homepage = 'https://github.com/take-five/segment_tree'
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split($\).grep(/lib|spec/)
|
12
12
|
gem.test_files = gem.files.grep(/spec/)
|
13
|
-
gem.name =
|
13
|
+
gem.name = 'segment_tree'
|
14
14
|
gem.require_paths = %W(lib)
|
15
15
|
gem.version = SegmentTree::VERSION
|
16
16
|
|
17
|
-
gem.add_development_dependency
|
18
|
-
gem.add_development_dependency
|
17
|
+
gem.add_development_dependency 'bundler', '>= 1.0'
|
18
|
+
gem.add_development_dependency 'rspec', '>= 3.1.0'
|
19
|
+
gem.add_development_dependency 'rake'
|
19
20
|
end
|
data/spec/segment_tree_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'segment_tree'
|
3
3
|
|
4
4
|
# subject { tree }
|
5
5
|
# it { should query(12).and_return("b") }
|
@@ -13,10 +13,10 @@ RSpec::Matchers.define :query do |key|
|
|
13
13
|
result = tree.find(key)
|
14
14
|
result &&= result.value
|
15
15
|
|
16
|
-
result.
|
16
|
+
expect(result).to eq @expected
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
failure_message do |tree|
|
20
20
|
result = tree.find(key)
|
21
21
|
result &&= result.value
|
22
22
|
|
@@ -28,15 +28,15 @@ end
|
|
28
28
|
describe SegmentTree do
|
29
29
|
# some fixtures
|
30
30
|
# [[0..9, "a"], [10..19, "b"], ..., [90..99, "j"]] - spanned intervals
|
31
|
-
let(:sample_spanned) { (0..9).zip(
|
31
|
+
let(:sample_spanned) { (0..9).zip('a'..'j').map { |num, letter| [(num * 10)..(num + 1) * 10 - 1, letter] }.shuffle }
|
32
32
|
# [[0..10, "a"], [10..20, "b"], ..., [90..100, "j"]] - partially overlapping intervals
|
33
|
-
let(:sample_overlapping) { (0..9).zip(
|
33
|
+
let(:sample_overlapping) { (0..9).zip('a'..'j').map { |num, letter| [(num * 10)..(num + 1) * 10 + 2, letter] }.shuffle }
|
34
34
|
# [[0..5, "a"], [10..15, "b"], ..., [90..95, "j"]] - sparsed intervals
|
35
|
-
let(:sample_sparsed) { (0..9).zip(
|
35
|
+
let(:sample_sparsed) { (0..9).zip('a'..'j').map { |num, letter| [(num * 10)..(num + 1) * 10 - 5, letter] }.shuffle }
|
36
36
|
|
37
37
|
# [[0..5, "a"], [0..7, "aa"], [10..15, "b"], [10..17, "bb"], ..., [90..97, "jj"]]
|
38
38
|
let(:sample_overlapping2) do
|
39
|
-
(0..9).zip(
|
39
|
+
(0..9).zip('a'..'j').map do |num, letter|
|
40
40
|
[(num * 10)..(num + 1) * 10 - 5, letter,
|
41
41
|
(num * 10)..(num + 1) * 10 - 3, letter * 2]
|
42
42
|
end.
|
@@ -46,89 +46,167 @@ describe SegmentTree do
|
|
46
46
|
shuffle
|
47
47
|
end
|
48
48
|
|
49
|
-
describe
|
50
|
-
context
|
49
|
+
describe '.new' do
|
50
|
+
context 'given a hash with ranges as keys' do
|
51
51
|
let :data do
|
52
|
-
{7..9 =>
|
53
|
-
4..6 =>
|
54
|
-
0..3 =>
|
55
|
-
10..12 =>
|
52
|
+
{7..9 => 'a',
|
53
|
+
4..6 => 'b',
|
54
|
+
0..3 => 'c',
|
55
|
+
10..12 => 'd'}
|
56
56
|
end
|
57
57
|
|
58
58
|
subject(:tree) { SegmentTree.new(data) }
|
59
59
|
|
60
|
-
it {
|
60
|
+
it { is_expected.to be_a SegmentTree }
|
61
61
|
end
|
62
62
|
|
63
|
-
context
|
63
|
+
context 'given an array of arrays' do
|
64
64
|
let :data do
|
65
|
-
[[0..3,
|
66
|
-
[4..6,
|
67
|
-
[7..9,
|
68
|
-
[10..12,
|
65
|
+
[[0..3, 'a'],
|
66
|
+
[4..6, 'b'],
|
67
|
+
[7..9, 'c'],
|
68
|
+
[10..12, 'd']].shuffle
|
69
69
|
end
|
70
70
|
|
71
71
|
subject(:tree) { SegmentTree.new(data) }
|
72
72
|
|
73
|
-
it {
|
73
|
+
it { is_expected.to be_a SegmentTree }
|
74
74
|
end
|
75
75
|
|
76
|
-
context
|
76
|
+
context 'given preordered data' do
|
77
77
|
let :data do
|
78
|
-
[[0..3,
|
79
|
-
[4..6,
|
80
|
-
[7..9,
|
81
|
-
[10..12,
|
78
|
+
[[0..3, 'a'],
|
79
|
+
[4..6, 'b'],
|
80
|
+
[7..9, 'c'],
|
81
|
+
[10..12, 'd']]
|
82
82
|
end
|
83
83
|
|
84
84
|
subject(:tree) { SegmentTree.new(data, true) }
|
85
85
|
|
86
|
-
it {
|
87
|
-
it {
|
86
|
+
it { is_expected.to be_a SegmentTree }
|
87
|
+
it { is_expected.to query(8).and_return('c') }
|
88
88
|
end
|
89
89
|
|
90
|
-
context
|
90
|
+
context 'given nor hash neither array' do
|
91
91
|
it { expect{ SegmentTree.new(Object.new) }.to raise_error(ArgumentError) }
|
92
92
|
end
|
93
93
|
|
94
|
-
context
|
94
|
+
context 'given 1-dimensional array' do
|
95
95
|
let :data do
|
96
|
-
[0..3,
|
97
|
-
4..6,
|
98
|
-
7..9,
|
99
|
-
10..12,
|
96
|
+
[0..3, 'a',
|
97
|
+
4..6, 'b',
|
98
|
+
7..9, 'c',
|
99
|
+
10..12, 'd']
|
100
100
|
end
|
101
101
|
|
102
102
|
it { expect{ SegmentTree.new(data) }.to raise_error(ArgumentError) }
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
106
|
-
describe
|
107
|
-
context
|
106
|
+
describe 'querying' do
|
107
|
+
context 'given spanned intervals' do
|
108
108
|
subject { SegmentTree.new(sample_spanned) }
|
109
109
|
|
110
|
-
it {
|
111
|
-
it {
|
110
|
+
it { is_expected.to query(12).and_return('b') }
|
111
|
+
it { is_expected.to query(101).and_return(:nothing) }
|
112
112
|
end
|
113
113
|
|
114
|
-
context
|
114
|
+
context 'given partially overlapping intervals' do
|
115
115
|
subject { SegmentTree.new(sample_overlapping) }
|
116
116
|
|
117
|
-
it {
|
117
|
+
it { is_expected.to query(11).and_return('a') }
|
118
118
|
end
|
119
119
|
|
120
|
-
context
|
120
|
+
context 'given sparsed intervals' do
|
121
121
|
subject { SegmentTree.new(sample_sparsed) }
|
122
122
|
|
123
|
-
it {
|
124
|
-
it {
|
123
|
+
it { is_expected.to query(12).and_return('b') }
|
124
|
+
it { is_expected.to query(8).and_return(:nothing) }
|
125
125
|
end
|
126
126
|
|
127
|
-
context
|
127
|
+
context 'given hardly overlapping intervals' do
|
128
128
|
subject { SegmentTree.new(sample_overlapping2) }
|
129
129
|
|
130
|
-
it {
|
131
|
-
it {
|
130
|
+
it { is_expected.to query(12).and_return('b') }
|
131
|
+
it { is_expected.to query(8).and_return(:nothing) }
|
132
132
|
end
|
133
133
|
end
|
134
|
-
|
134
|
+
|
135
|
+
describe '#==' do
|
136
|
+
subject { SegmentTree.new(sample_overlapping) }
|
137
|
+
|
138
|
+
it { is_expected.to eq(SegmentTree.new(sample_overlapping)) }
|
139
|
+
it { is_expected.not_to eq(SegmentTree.new(sample_overlapping2)) }
|
140
|
+
|
141
|
+
it 'is equal when a range coerces' do
|
142
|
+
expect(SegmentTree.new((1..2) => "a")).to eq(SegmentTree.new(((1.0)..(2.0)) => "a"))
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'is equal when a value coerces' do
|
146
|
+
expect(SegmentTree.new((1..2) => 1)).to eq(SegmentTree.new((1..2) => 1.0))
|
147
|
+
end
|
148
|
+
|
149
|
+
it "isn't equal when only a range is different" do
|
150
|
+
expect(SegmentTree.new((1..2) => "a")).not_to eq(SegmentTree.new((1..3) => "a"))
|
151
|
+
end
|
152
|
+
|
153
|
+
it "isn't equal when only a value is different" do
|
154
|
+
expect(SegmentTree.new((1..2) => "a")).not_to eq(SegmentTree.new((1..2) => "b"))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe '#eql?' do
|
159
|
+
subject { SegmentTree.new(sample_overlapping) }
|
160
|
+
|
161
|
+
it { is_expected.to be_eql(SegmentTree.new(sample_overlapping)) }
|
162
|
+
it { is_expected.not_to be_eql(SegmentTree.new(sample_overlapping2)) }
|
163
|
+
|
164
|
+
it "isn't equal when a range coerces" do
|
165
|
+
expect(SegmentTree.new((1..2) => "a")).not_to be_eql(SegmentTree.new(((1.0)..(2.0)) => "a"))
|
166
|
+
end
|
167
|
+
|
168
|
+
it "isn't equal when a value coerces" do
|
169
|
+
expect(SegmentTree.new((1..2) => 1)).not_to be_eql(SegmentTree.new((1..2) => 1.0))
|
170
|
+
end
|
171
|
+
|
172
|
+
it "isn't equal when only a range is different" do
|
173
|
+
expect(SegmentTree.new((1..2) => "a")).not_to be_eql(SegmentTree.new((1..3) => "a"))
|
174
|
+
end
|
175
|
+
|
176
|
+
it "isn't equal when only a value is different" do
|
177
|
+
expect(SegmentTree.new((1..2) => "a")).not_to be_eql(SegmentTree.new((1..2) => "b"))
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe '#hash' do
|
182
|
+
subject { SegmentTree.new(sample_overlapping).hash }
|
183
|
+
|
184
|
+
it { is_expected.to eq(SegmentTree.new(sample_overlapping).hash) }
|
185
|
+
it { is_expected.not_to eq(SegmentTree.new(sample_overlapping2).hash) }
|
186
|
+
|
187
|
+
it "isn't equal when only a range is different" do
|
188
|
+
expect(SegmentTree.new((1..2) => "a").hash).not_to eq(SegmentTree.new((1..3) => "a").hash)
|
189
|
+
end
|
190
|
+
|
191
|
+
it "isn't equal when only a value is different" do
|
192
|
+
expect(SegmentTree.new((1..2) => "a").hash).not_to eq(SegmentTree.new((1..2) => "b").hash)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe 'marshaling' do
|
197
|
+
it 'dumps and loads successfully' do
|
198
|
+
aggregate_failures do
|
199
|
+
[
|
200
|
+
sample_spanned,
|
201
|
+
sample_sparsed,
|
202
|
+
sample_overlapping,
|
203
|
+
sample_overlapping2,
|
204
|
+
].each do |sample|
|
205
|
+
tree = SegmentTree.new(sample)
|
206
|
+
dumped = Marshal.dump(tree)
|
207
|
+
expect(Marshal.load(dumped)).to eq(tree)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/setup'
|
2
2
|
|
3
3
|
RSpec.configure do |config|
|
4
4
|
# Run specs in random order to surface order dependencies. If you find an
|
@@ -9,8 +9,8 @@ RSpec.configure do |config|
|
|
9
9
|
end
|
10
10
|
|
11
11
|
if defined?(RUBY_ENGINE) &&
|
12
|
-
RUBY_ENGINE ==
|
13
|
-
RUBY_VERSION >
|
14
|
-
require
|
12
|
+
RUBY_ENGINE == 'ruby' &&
|
13
|
+
RUBY_VERSION > '1.9'
|
14
|
+
require 'simplecov'
|
15
15
|
SimpleCov.start
|
16
16
|
end
|
metadata
CHANGED
@@ -1,48 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: segment_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Alexei Mikhailov
|
9
|
-
autorequire:
|
8
|
+
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2023-03-04 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bundler
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '1.0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '1.0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rspec
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
|
-
version:
|
33
|
+
version: 3.1.0
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
|
-
version:
|
40
|
+
version: 3.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
46
55
|
description: Tree data structure for storing segments. It allows querying which of
|
47
56
|
the stored segments contain a given point.
|
48
57
|
email: amikhailov83@gmail.com
|
@@ -50,7 +59,7 @@ executables: []
|
|
50
59
|
extensions: []
|
51
60
|
extra_rdoc_files: []
|
52
61
|
files:
|
53
|
-
- .rspec
|
62
|
+
- ".rspec"
|
54
63
|
- lib/segment_tree.rb
|
55
64
|
- lib/segment_tree/version.rb
|
56
65
|
- segment_tree.gemspec
|
@@ -58,31 +67,29 @@ files:
|
|
58
67
|
- spec/spec_helper.rb
|
59
68
|
homepage: https://github.com/take-five/segment_tree
|
60
69
|
licenses: []
|
61
|
-
|
70
|
+
metadata: {}
|
71
|
+
post_install_message:
|
62
72
|
rdoc_options: []
|
63
73
|
require_paths:
|
64
74
|
- lib
|
65
75
|
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
76
|
requirements:
|
68
|
-
- -
|
77
|
+
- - ">="
|
69
78
|
- !ruby/object:Gem::Version
|
70
79
|
version: '0'
|
71
80
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
-
none: false
|
73
81
|
requirements:
|
74
|
-
- -
|
82
|
+
- - ">="
|
75
83
|
- !ruby/object:Gem::Version
|
76
84
|
version: '0'
|
77
85
|
requirements: []
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
specification_version: 3
|
86
|
+
rubygems_version: 3.3.7
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
82
89
|
summary: Tree data structure for storing segments. It allows querying which of the
|
83
90
|
stored segments contain a given point.
|
84
91
|
test_files:
|
85
|
-
- .rspec
|
92
|
+
- ".rspec"
|
86
93
|
- segment_tree.gemspec
|
87
94
|
- spec/segment_tree_spec.rb
|
88
95
|
- spec/spec_helper.rb
|