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.
@@ -1,21 +1,30 @@
1
+ require 'rbtree'
2
+
1
3
  class RangeSet
2
- attr_accessor :ranges
4
+
5
+ def ranges
6
+ @rbtree.values
7
+ end
3
8
 
4
9
  def initialize(*ranges)
5
- @ranges = []
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
- @ranges = r.ranges.inject(RangeSet.new) do |accum, range|
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
- r.each {|r| subtract_range!(r)}
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
- r.each {|r| union_with_range!(r)}
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
- @ranges.each {|r| yield r}
63
+ @rbtree.each_value {|r| yield r}
55
64
  end
56
65
 
57
- #returns an array of RangeSets
58
- def split_at(*boundaries)
59
- result = []
60
- rs = RangeSet.new
61
- ranges = @ranges.clone
62
- boundaries.compact.sort.uniq.each do |boundary|
63
-
64
- while (range = ranges.shift)
65
- if range.end <= boundary
66
- rs += range
67
- elsif range.begin >= boundary
68
- ranges.unshift(range)
69
- break
70
- else
71
- rs += (range.begin .. boundary)
72
- ranges.unshift(boundary .. range.end)
73
- break
74
- end
75
- end
76
-
77
- unless rs.empty?
78
- result << rs
79
- rs = RangeSet.new
80
- end
81
- end
82
- result << RangeSet.new(*ranges) unless ranges.empty?
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
- if v.is_a?(Range)
91
- each do |range|
92
- return true if range.begin <= v.begin && v.begin <= v.end && v.end <= range.end
93
- end
94
- elsif v.is_a?(RangeSet)
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
- each do |range|
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
- @ranges.empty?
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
- result = []
122
- each do |range|
123
- if range.end < r.begin
124
- result << range
125
- elsif range.begin > r.end
126
- result << range
127
- else
128
- result << (range.begin .. r.begin) if range.begin < r.begin
129
- result << (r.end .. range.end) if range.end > r.end
130
- end
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
- @ranges = []
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
- result = []
143
- each do |range|
144
- r2 = ([range.begin, r.begin].max .. [range.end, r.end].min)
145
- if (r2.begin < r2.end)
146
- result << r2
147
- end
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
- @ranges = result
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
- joined = (r.begin < r.end) ? r : (r.end .. r.begin)
155
- still_need_joined = true
156
- result = []
157
- each do |range|
158
- if range.end < joined.begin
159
- result << range
160
- elsif range.end == joined.begin
161
- joined = (range.begin .. joined.end)
162
- elsif range.begin > joined.end
163
- result << joined if still_need_joined
164
- still_need_joined = false
165
- result << range
166
- else
167
- joined = ([range.begin,joined.begin].min .. [range.end,joined.end].max)
168
- end
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
- result << joined if still_need_joined
171
- @ranges = result
199
+ true
172
200
  end
201
+
173
202
  end
@@ -1,3 +1,3 @@
1
1
  class RangeSet
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -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: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
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-02 00:00:00 -05:00
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: rspec
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: *id001
50
+ version_requirements: *id002
36
51
  - !ruby/object:Gem::Dependency
37
52
  name: simplecov
38
53
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::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: *id002
65
+ version_requirements: *id003
51
66
  - !ruby/object:Gem::Dependency
52
67
  name: rb-fsevent
53
68
  prerelease: false
54
- requirement: &id003 !ruby/object:Gem::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: *id003
80
+ version_requirements: *id004
66
81
  - !ruby/object:Gem::Dependency
67
82
  name: guard
68
83
  prerelease: false
69
- requirement: &id004 !ruby/object:Gem::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: *id004
95
+ version_requirements: *id005
81
96
  - !ruby/object:Gem::Dependency
82
97
  name: guard-bundler
83
98
  prerelease: false
84
- requirement: &id005 !ruby/object:Gem::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: *id005
110
+ version_requirements: *id006
96
111
  - !ruby/object:Gem::Dependency
97
112
  name: guard-rspec
98
113
  prerelease: false
99
- requirement: &id006 !ruby/object:Gem::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: *id006
125
+ version_requirements: *id007
111
126
  description: Set implementation based on ranges
112
127
  email:
113
128
  - chris@kindkid.com