range_set 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ coverage/*
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm use ruby-1.8.7@range_set
2
+ #rvm use ruby-1.9.2@range_set
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rangeset.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,14 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'bundler' do
5
+ watch('Gemfile')
6
+ watch(/^.+\.gemspec/)
7
+ end
8
+
9
+ guard 'rspec', :cli => '-c --format documentation -r ./spec/spec_helper.rb --debug',
10
+ :version => 2 do
11
+ watch(%r{^spec/.+_spec\.rb})
12
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
13
+ watch('spec/spec_helper.rb') { "spec" }
14
+ end
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run all specs"
5
+ RSpec::Core::RakeTask.new(:spec) do |t|
6
+ t.rspec_opts = [
7
+ '-c',
8
+ '--format documentation',
9
+ '-r ./spec/spec_helper.rb'
10
+ ]
11
+ t.pattern = 'spec/**/*_spec.rb'
12
+ end
13
+
14
+ desc "open coverage report"
15
+ task :coverage do
16
+ system 'rake spec'
17
+ system 'open coverage/index.html'
18
+ end
19
+
20
+ desc "Open development console"
21
+ task :console do
22
+ puts "Loading development console..."
23
+ system "irb -I #{File.join('.', 'lib')} -r #{File.join('.', 'lib', 'range_set')}"
24
+ end
@@ -0,0 +1,3 @@
1
+ class RangeSet
2
+ VERSION = "0.0.1"
3
+ end
data/lib/range_set.rb ADDED
@@ -0,0 +1,173 @@
1
+ class RangeSet
2
+ attr_accessor :ranges
3
+
4
+ def initialize(*ranges)
5
+ @ranges = []
6
+ ranges.each do |r|
7
+ union_with_range!(r)
8
+ end
9
+ self
10
+ end
11
+
12
+ def intersect!(r)
13
+ if r.is_a?(Range)
14
+ intersect_with_range!(r)
15
+ elsif r.is_a?(RangeSet)
16
+ @ranges = r.ranges.inject(RangeSet.new) do |accum, range|
17
+ accum + (self & range)
18
+ end.ranges
19
+ end
20
+ self
21
+ end
22
+
23
+ def intersect(r)
24
+ self.clone.intersect!(r)
25
+ end
26
+
27
+ def subtract!(r)
28
+ if r.is_a?(Range)
29
+ subtract_range!(r)
30
+ elsif r.is_a?(RangeSet)
31
+ r.each {|r| subtract_range!(r)}
32
+ end
33
+ self
34
+ end
35
+
36
+ def subtract(r)
37
+ self.clone.subtract!(r)
38
+ end
39
+
40
+ def union!(r)
41
+ if r.is_a?(Range)
42
+ union_with_range!(r)
43
+ elsif r.is_a?(RangeSet)
44
+ r.each {|r| union_with_range!(r)}
45
+ end
46
+ self
47
+ end
48
+
49
+ def union(r)
50
+ self.clone.union!(r)
51
+ end
52
+
53
+ def each
54
+ @ranges.each {|r| yield r}
55
+ end
56
+
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
85
+
86
+ # TODO : need to be more explicit and careful about
87
+ # whether or not a range's end is inclusive. For now,
88
+ # we just assume that it's NOT inclusive.
89
+ 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
99
+ else
100
+ each do |range|
101
+ return true if range.begin <= v && v < range.end
102
+ end
103
+ end
104
+ false
105
+ end
106
+
107
+ def empty?
108
+ @ranges.empty?
109
+ end
110
+
111
+ alias :- :subtract
112
+ alias :| :union
113
+ alias :+ :union
114
+ alias :& :intersect
115
+
116
+ private
117
+
118
+ def subtract_range!(r)
119
+ return if r.begin == r.end
120
+ 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
131
+ end
132
+ @ranges = result
133
+ end
134
+
135
+ def intersect_with_range!(r)
136
+ if r.begin == r.end
137
+ @ranges = []
138
+ return
139
+ elsif r.begin > r.end
140
+ r = (r.end .. r.begin)
141
+ 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
148
+ end
149
+ @ranges = result
150
+ end
151
+
152
+ def union_with_range!(r)
153
+ 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
169
+ end
170
+ result << joined if still_need_joined
171
+ @ranges = result
172
+ end
173
+ end
data/range_set.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "range_set/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "range_set"
7
+ s.version = RangeSet::VERSION
8
+ s.authors = ["Chris Johnson"]
9
+ s.email = ["chris@kindkid.com"]
10
+ s.homepage = "https://github.com/kindkid/range_set"
11
+ s.summary = "Set implementation based on ranges"
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = "range_set"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.add_development_dependency "rspec", "~> 2.6"
21
+ s.add_development_dependency "simplecov", "~> 0.4"
22
+ s.add_development_dependency("rb-fsevent", "~> 0.4") if RUBY_PLATFORM =~ /darwin/i
23
+ s.add_development_dependency "guard", "~> 0.5"
24
+ s.add_development_dependency "guard-bundler", "~> 0.1"
25
+ s.add_development_dependency "guard-rspec", "~> 0.4"
26
+ end
@@ -0,0 +1,197 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe RangeSet do
4
+ before(:each) do
5
+ @rs = RangeSet.new(0..10)
6
+ end
7
+
8
+ it "can be initialized with a range" do
9
+ @rs.ranges.should == [(0..10)]
10
+ end
11
+
12
+ it "can be initialized with a backwards range" do
13
+ @rs = RangeSet.new(10..0)
14
+ @rs.ranges.should == [(0..10)]
15
+ end
16
+
17
+ it "can be initialized with multiple ranges" do
18
+ @rs = RangeSet.new(1..2, 99..100)
19
+ @rs.ranges.should == [(1..2),(99..100)]
20
+ end
21
+
22
+ describe ".union" do
23
+ it "handles a contiguous range" do
24
+ @rs += 10..20
25
+ @rs.ranges.should == [(0..20)]
26
+ end
27
+
28
+ it "handles an overlapping range" do
29
+ @rs += 5..15
30
+ @rs.ranges.should == [(0..15)]
31
+ end
32
+
33
+ it "handles a non-contiguous range in back" do
34
+ @rs += 20..30
35
+ @rs.ranges.should == [(0..10), (20..30)]
36
+ end
37
+
38
+ it "handles a non-contiguous range in front" do
39
+ @rs += -10..-5
40
+ @rs.ranges.should == [(-10..-5), (0..10)]
41
+ end
42
+
43
+ it "handles this funky case" do
44
+ @rs = RangeSet.new(7..8, 9..10)
45
+ @rs += 5..6
46
+ @rs.ranges.should == [(5..6), (7..8), (9..10)]
47
+ end
48
+
49
+ it "merges internal ranges as needed" do
50
+ @rs = RangeSet.new(0..10, 20..30)
51
+ @rs += 10..20
52
+ @rs.ranges.should == [(0..30)]
53
+ end
54
+
55
+ it "handles reversed ranges" do
56
+ @rs += 20..10
57
+ @rs.ranges.should == [(0..20)]
58
+ end
59
+
60
+ it "handles a RangeSet" do
61
+ @rs += RangeSet.new(5..15, 25..30)
62
+ @rs.ranges.should == [(0..15), (25..30)]
63
+ end
64
+ end
65
+
66
+ describe ".subtract" do
67
+ it "can shorten a range" do
68
+ @rs -= 0..5
69
+ @rs.ranges.should == [(5..10)]
70
+ end
71
+
72
+ it "can remove a range and shorten others at the same time" do
73
+ @rs = RangeSet.new(0..10, 20..30, 40..50)
74
+ @rs -= 5..45
75
+ @rs.ranges.should == [(0..5),(45..50)]
76
+ end
77
+
78
+ it "handles a contained range" do
79
+ @rs -= 3..8
80
+ @rs.ranges.should == [(0..3),(8..10)]
81
+ end
82
+
83
+ it "handles a containing range" do
84
+ @rs -= -1..10
85
+ @rs.ranges.should == []
86
+ end
87
+
88
+ it "handles an empty range" do
89
+ @rs -= 5..5
90
+ @rs.ranges.should == [(0..10)]
91
+ end
92
+
93
+ it "handles a missing range" do
94
+ @rs = RangeSet.new(0..10, 20..30)
95
+ @rs -= 12..18
96
+ @rs.ranges.should == [(0..10),(20..30)]
97
+ end
98
+
99
+ it "handles a border-case missing range" do
100
+ @rs = RangeSet.new(0..10, 20..30)
101
+ @rs -= 10..20
102
+ @rs.ranges.should == [(0..10),(20..30)]
103
+ end
104
+
105
+ it "handles a RangeSet" do
106
+ @rs = RangeSet.new(0..10, 20..30)
107
+ @rs -= RangeSet.new(0..5, 8..21)
108
+ @rs.ranges.should == [(5..8), (21..30)]
109
+ end
110
+ end
111
+
112
+ describe ".intersect" do
113
+ it "handles overlapping ranges" do
114
+ @rs &= 5..15
115
+ @rs.ranges.should == [(5..10)]
116
+ end
117
+
118
+ it "handles multiple overlaps" do
119
+ @rs = RangeSet.new(0..10, 20..30, 40..50)
120
+ @rs &= 5..45
121
+ @rs.ranges.should == [(5..10), (20..30), (40..45)]
122
+ end
123
+
124
+ it "handles contained ranges" do
125
+ @rs &= 3..8
126
+ @rs.ranges.should == [(3..8)]
127
+ end
128
+
129
+ it "handles containing ranges" do
130
+ @rs &= -1..11
131
+ @rs.ranges.should == [(0..10)]
132
+ end
133
+
134
+ it "handles empty ranges" do
135
+ @rs &= 20..20
136
+ @rs.ranges.should == []
137
+ end
138
+
139
+ it "handles reversed ranges" do
140
+ @rs &= 15..5
141
+ @rs.ranges.should == [(5..10)]
142
+ end
143
+
144
+ it "handles a RangeSet" do
145
+ @rs = RangeSet.new(0..10, 20..30, 40..50)
146
+ @rs &= RangeSet.new(5..25, 35..55)
147
+ @rs.ranges.should == [(5..10), (20..25), (40..50)]
148
+ end
149
+ end
150
+
151
+ describe ".empty?" do
152
+ it "returns true for an empty range" do
153
+ RangeSet.new(1..1).empty?.should be_true
154
+ end
155
+
156
+ it "returns false for a non-empty range" do
157
+ RangeSet.new(1..2).empty?.should_not be_true
158
+ end
159
+
160
+ it "returns false for multiple ranges" do
161
+ RangeSet.new(-2..-1, 1..2).empty?.should_not be_true
162
+ end
163
+ end
164
+
165
+ describe ".include?" do
166
+ it "handles ranges" do
167
+ rs = RangeSet.new(1..3,4..6,7..9)
168
+ rs.include?(1..3).should == true
169
+ rs.include?(1..2).should == true
170
+ rs.include?(2..2).should == true
171
+ rs.include?(3..4).should == false
172
+ rs.include?(1..4).should == false
173
+ rs.include?(10..11).should == false
174
+ end
175
+
176
+ it "handles RangeSets" do
177
+ rs = RangeSet.new(1..3,4..6,7..9)
178
+ rs.include?(RangeSet.new(1..3)).should == true
179
+ rs.include?(rs).should == true
180
+ rs.include?(RangeSet.new(1..1)).should == true
181
+ rs.include?(RangeSet.new(1..3,4..6)).should == true
182
+ rs.include?(RangeSet.new(1..3,4..6,10..12)).should == false
183
+ rs.include?(RangeSet.new(1..2,5..6)).should == true
184
+ end
185
+
186
+ it "handles scalars" do
187
+ rs = RangeSet.new(1..3,4..6,7..9)
188
+ rs.include?(0).should be_false
189
+ rs.include?(1).should be_true
190
+ rs.include?(2).should be_true
191
+ rs.include?(3).should be_false
192
+ rs.include?(4).should be_true
193
+ rs.include?(9).should be_false
194
+ rs.include?(10).should be_false
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,7 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'rubygems'
5
+ require 'bundler'
6
+
7
+ Bundler.require(:default, :test, :development)
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: range_set
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Chris Johnson
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-02 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 2
32
+ - 6
33
+ version: "2.6"
34
+ type: :development
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: simplecov
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ - 4
48
+ version: "0.4"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: rb-fsevent
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ - 4
63
+ version: "0.4"
64
+ type: :development
65
+ version_requirements: *id003
66
+ - !ruby/object:Gem::Dependency
67
+ name: guard
68
+ prerelease: false
69
+ requirement: &id004 !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ~>
73
+ - !ruby/object:Gem::Version
74
+ hash: 1
75
+ segments:
76
+ - 0
77
+ - 5
78
+ version: "0.5"
79
+ type: :development
80
+ version_requirements: *id004
81
+ - !ruby/object:Gem::Dependency
82
+ name: guard-bundler
83
+ prerelease: false
84
+ requirement: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ hash: 9
90
+ segments:
91
+ - 0
92
+ - 1
93
+ version: "0.1"
94
+ type: :development
95
+ version_requirements: *id005
96
+ - !ruby/object:Gem::Dependency
97
+ name: guard-rspec
98
+ prerelease: false
99
+ requirement: &id006 !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ~>
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ - 4
108
+ version: "0.4"
109
+ type: :development
110
+ version_requirements: *id006
111
+ description: Set implementation based on ranges
112
+ email:
113
+ - chris@kindkid.com
114
+ executables: []
115
+
116
+ extensions: []
117
+
118
+ extra_rdoc_files: []
119
+
120
+ files:
121
+ - .gitignore
122
+ - .rvmrc
123
+ - Gemfile
124
+ - Guardfile
125
+ - Rakefile
126
+ - lib/range_set.rb
127
+ - lib/range_set/version.rb
128
+ - range_set.gemspec
129
+ - spec/lib/range_set_spec.rb
130
+ - spec/spec_helper.rb
131
+ has_rdoc: true
132
+ homepage: https://github.com/kindkid/range_set
133
+ licenses: []
134
+
135
+ post_install_message:
136
+ rdoc_options: []
137
+
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ hash: 3
146
+ segments:
147
+ - 0
148
+ version: "0"
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 3
155
+ segments:
156
+ - 0
157
+ version: "0"
158
+ requirements: []
159
+
160
+ rubyforge_project: range_set
161
+ rubygems_version: 1.6.2
162
+ signing_key:
163
+ specification_version: 3
164
+ summary: Set implementation based on ranges
165
+ test_files:
166
+ - spec/lib/range_set_spec.rb
167
+ - spec/spec_helper.rb