range_set 0.0.1 → 0.0.2
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/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
|