range_set 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/range_set.rb +115 -86
- data/lib/range_set/version.rb +1 -1
- data/range_set.gemspec +1 -0
- metadata +31 -16
data/lib/range_set.rb
CHANGED
@@ -1,21 +1,30 @@
|
|
1
|
+
require 'rbtree'
|
2
|
+
|
1
3
|
class RangeSet
|
2
|
-
|
4
|
+
|
5
|
+
def ranges
|
6
|
+
@rbtree.values
|
7
|
+
end
|
3
8
|
|
4
9
|
def initialize(*ranges)
|
5
|
-
@
|
10
|
+
@rbtree = RBTree.new
|
6
11
|
ranges.each do |r|
|
7
12
|
union_with_range!(r)
|
8
13
|
end
|
9
14
|
self
|
10
15
|
end
|
16
|
+
|
17
|
+
def clone
|
18
|
+
result = super
|
19
|
+
result.send(:rbtree=, result.send(:rbtree).clone)
|
20
|
+
result
|
21
|
+
end
|
11
22
|
|
12
23
|
def intersect!(r)
|
13
24
|
if r.is_a?(Range)
|
14
25
|
intersect_with_range!(r)
|
15
26
|
elsif r.is_a?(RangeSet)
|
16
|
-
|
17
|
-
accum + (self & range)
|
18
|
-
end.ranges
|
27
|
+
intersect_with_rangeset!(r)
|
19
28
|
end
|
20
29
|
self
|
21
30
|
end
|
@@ -28,7 +37,7 @@ class RangeSet
|
|
28
37
|
if r.is_a?(Range)
|
29
38
|
subtract_range!(r)
|
30
39
|
elsif r.is_a?(RangeSet)
|
31
|
-
|
40
|
+
subtract_rangeset!(r)
|
32
41
|
end
|
33
42
|
self
|
34
43
|
end
|
@@ -41,7 +50,7 @@ class RangeSet
|
|
41
50
|
if r.is_a?(Range)
|
42
51
|
union_with_range!(r)
|
43
52
|
elsif r.is_a?(RangeSet)
|
44
|
-
|
53
|
+
union_with_rangeset!(r)
|
45
54
|
end
|
46
55
|
self
|
47
56
|
end
|
@@ -51,61 +60,52 @@ class RangeSet
|
|
51
60
|
end
|
52
61
|
|
53
62
|
def each
|
54
|
-
@
|
63
|
+
@rbtree.each_value {|r| yield r}
|
55
64
|
end
|
56
65
|
|
57
|
-
#returns an array of RangeSets
|
58
|
-
def split_at(*boundaries)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
result
|
84
|
-
end
|
66
|
+
# #returns an array of RangeSets
|
67
|
+
# def split_at(*boundaries)
|
68
|
+
# result = []
|
69
|
+
# rs = RangeSet.new
|
70
|
+
# ranges = @rbtree.clone
|
71
|
+
# boundaries.compact.sort.uniq.each do |boundary|
|
72
|
+
# while (range = ranges.shift)
|
73
|
+
# if range.end <= boundary
|
74
|
+
# rs += range
|
75
|
+
# elsif range.begin >= boundary
|
76
|
+
# ranges.unshift(range)
|
77
|
+
# break
|
78
|
+
# else
|
79
|
+
# rs += (range.begin .. boundary)
|
80
|
+
# ranges.unshift(boundary .. range.end)
|
81
|
+
# break
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
# unless rs.empty?
|
85
|
+
# result << rs
|
86
|
+
# rs = RangeSet.new
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
# result << RangeSet.new(*ranges) unless ranges.empty?
|
90
|
+
# result
|
91
|
+
# end
|
85
92
|
|
86
93
|
# TODO : need to be more explicit and careful about
|
87
94
|
# whether or not a range's end is inclusive. For now,
|
88
95
|
# we just assume that it's NOT inclusive.
|
89
96
|
def include?(v)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
v.ranges.each do |range|
|
96
|
-
return false unless self.include?(range)
|
97
|
-
end
|
98
|
-
return true
|
97
|
+
case v
|
98
|
+
when Range
|
99
|
+
include_range?(v)
|
100
|
+
when RangeSet
|
101
|
+
include_rangeset?(v)
|
99
102
|
else
|
100
|
-
|
101
|
-
return true if range.begin <= v && v < range.end
|
102
|
-
end
|
103
|
+
include_scalar?(v)
|
103
104
|
end
|
104
|
-
false
|
105
105
|
end
|
106
106
|
|
107
107
|
def empty?
|
108
|
-
@
|
108
|
+
@rbtree.empty?
|
109
109
|
end
|
110
110
|
|
111
111
|
alias :- :subtract
|
@@ -114,60 +114,89 @@ class RangeSet
|
|
114
114
|
alias :& :intersect
|
115
115
|
|
116
116
|
private
|
117
|
+
|
118
|
+
attr_accessor :rbtree
|
117
119
|
|
118
120
|
def subtract_range!(r)
|
119
121
|
return if r.begin == r.end
|
120
122
|
r = (r.end .. r.begin) unless r.begin < r.end
|
121
|
-
|
122
|
-
each do |range|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
123
|
+
x, previous_range = @rbtree.upper_bound(r.begin)
|
124
|
+
@rbtree.bound([x,r.begin].compact.min, r.end).each do |key, range|
|
125
|
+
@rbtree.delete(key)
|
126
|
+
front_range = (range.begin .. [r.begin, range.end].min)
|
127
|
+
back_range = ([range.begin,r.end].max .. range.end)
|
128
|
+
@rbtree[front_range.begin] = front_range if front_range.begin < front_range.end
|
129
|
+
@rbtree[back_range.begin] = back_range if back_range.begin < back_range.end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def subtract_rangeset!(r)
|
134
|
+
r.each do |range|
|
135
|
+
subtract_range!(range)
|
131
136
|
end
|
132
|
-
@ranges = result
|
133
137
|
end
|
134
138
|
|
135
139
|
def intersect_with_range!(r)
|
136
140
|
if r.begin == r.end
|
137
|
-
@
|
141
|
+
@rbtree = RBTree.new
|
138
142
|
return
|
139
143
|
elsif r.begin > r.end
|
140
144
|
r = (r.end .. r.begin)
|
141
145
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
146
|
+
replacement = RBTree.new
|
147
|
+
x, previous_range = @rbtree.upper_bound(r.begin)
|
148
|
+
@rbtree.bound([x,r.begin].compact.min, r.end) do |key, range|
|
149
|
+
range = ([range.begin,r.begin].max .. [range.end,r.end].min)
|
150
|
+
replacement[range.begin] = range if range.begin < range.end
|
151
|
+
end
|
152
|
+
@rbtree = replacement
|
153
|
+
end
|
154
|
+
|
155
|
+
def intersect_with_rangeset!(r)
|
156
|
+
#TODO: This could be greatly optimized!
|
157
|
+
replacement = RangeSet.new
|
158
|
+
r.each do |range|
|
159
|
+
replacement += self & range
|
148
160
|
end
|
149
|
-
@
|
161
|
+
@rbtree = replacement.send(:rbtree)
|
150
162
|
end
|
151
163
|
|
152
164
|
def union_with_range!(r)
|
153
165
|
return if r.begin == r.end
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
166
|
+
r = (r.begin < r.end) ? r : (r.end .. r.begin)
|
167
|
+
@rbtree.bound(r.begin, r.end) do |key, range|
|
168
|
+
range = @rbtree.delete(key)
|
169
|
+
r = ([r.begin, range.begin].min .. [r.end, range.end].max)
|
170
|
+
end
|
171
|
+
x, previous_range = @rbtree.upper_bound(r.begin)
|
172
|
+
if previous_range && previous_range.end >= r.begin
|
173
|
+
@rbtree.delete(x)
|
174
|
+
r = (x .. r.end)
|
175
|
+
end
|
176
|
+
@rbtree[r.begin] = r
|
177
|
+
end
|
178
|
+
|
179
|
+
def union_with_rangeset!(r)
|
180
|
+
r.each do |range|
|
181
|
+
union_with_range!(range)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def include_scalar?(v)
|
186
|
+
x, previous_range = @rbtree.upper_bound(v)
|
187
|
+
previous_range && v < previous_range.end
|
188
|
+
end
|
189
|
+
|
190
|
+
def include_range?(r)
|
191
|
+
x, previous_range = @rbtree.upper_bound(r.begin)
|
192
|
+
previous_range && r.end <= previous_range.end
|
193
|
+
end
|
194
|
+
|
195
|
+
def include_rangeset?(r)
|
196
|
+
r.each do |range|
|
197
|
+
return false unless include_range?(range)
|
169
198
|
end
|
170
|
-
|
171
|
-
@ranges = result
|
199
|
+
true
|
172
200
|
end
|
201
|
+
|
173
202
|
end
|
data/lib/range_set/version.rb
CHANGED
data/range_set.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
|
+
s.add_dependency "rbtree-pure", "~> 0.1"
|
20
21
|
s.add_development_dependency "rspec", "~> 2.6"
|
21
22
|
s.add_development_dependency "simplecov", "~> 0.4"
|
22
23
|
s.add_development_dependency("rb-fsevent", "~> 0.4") if RUBY_PLATFORM =~ /darwin/i
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: range_set
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Chris Johnson
|
@@ -15,13 +15,28 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-08-
|
18
|
+
date: 2011-08-03 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
name:
|
22
|
+
name: rbtree-pure
|
23
23
|
prerelease: false
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 9
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 1
|
33
|
+
version: "0.1"
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
25
40
|
none: false
|
26
41
|
requirements:
|
27
42
|
- - ~>
|
@@ -32,11 +47,11 @@ dependencies:
|
|
32
47
|
- 6
|
33
48
|
version: "2.6"
|
34
49
|
type: :development
|
35
|
-
version_requirements: *
|
50
|
+
version_requirements: *id002
|
36
51
|
- !ruby/object:Gem::Dependency
|
37
52
|
name: simplecov
|
38
53
|
prerelease: false
|
39
|
-
requirement: &
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
40
55
|
none: false
|
41
56
|
requirements:
|
42
57
|
- - ~>
|
@@ -47,11 +62,11 @@ dependencies:
|
|
47
62
|
- 4
|
48
63
|
version: "0.4"
|
49
64
|
type: :development
|
50
|
-
version_requirements: *
|
65
|
+
version_requirements: *id003
|
51
66
|
- !ruby/object:Gem::Dependency
|
52
67
|
name: rb-fsevent
|
53
68
|
prerelease: false
|
54
|
-
requirement: &
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
55
70
|
none: false
|
56
71
|
requirements:
|
57
72
|
- - ~>
|
@@ -62,11 +77,11 @@ dependencies:
|
|
62
77
|
- 4
|
63
78
|
version: "0.4"
|
64
79
|
type: :development
|
65
|
-
version_requirements: *
|
80
|
+
version_requirements: *id004
|
66
81
|
- !ruby/object:Gem::Dependency
|
67
82
|
name: guard
|
68
83
|
prerelease: false
|
69
|
-
requirement: &
|
84
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
70
85
|
none: false
|
71
86
|
requirements:
|
72
87
|
- - ~>
|
@@ -77,11 +92,11 @@ dependencies:
|
|
77
92
|
- 5
|
78
93
|
version: "0.5"
|
79
94
|
type: :development
|
80
|
-
version_requirements: *
|
95
|
+
version_requirements: *id005
|
81
96
|
- !ruby/object:Gem::Dependency
|
82
97
|
name: guard-bundler
|
83
98
|
prerelease: false
|
84
|
-
requirement: &
|
99
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
85
100
|
none: false
|
86
101
|
requirements:
|
87
102
|
- - ~>
|
@@ -92,11 +107,11 @@ dependencies:
|
|
92
107
|
- 1
|
93
108
|
version: "0.1"
|
94
109
|
type: :development
|
95
|
-
version_requirements: *
|
110
|
+
version_requirements: *id006
|
96
111
|
- !ruby/object:Gem::Dependency
|
97
112
|
name: guard-rspec
|
98
113
|
prerelease: false
|
99
|
-
requirement: &
|
114
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
100
115
|
none: false
|
101
116
|
requirements:
|
102
117
|
- - ~>
|
@@ -107,7 +122,7 @@ dependencies:
|
|
107
122
|
- 4
|
108
123
|
version: "0.4"
|
109
124
|
type: :development
|
110
|
-
version_requirements: *
|
125
|
+
version_requirements: *id007
|
111
126
|
description: Set implementation based on ranges
|
112
127
|
email:
|
113
128
|
- chris@kindkid.com
|