interval_notation 0.1.3 → 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.
@@ -0,0 +1,177 @@
1
+ require_relative '../lib/interval_notation'
2
+
3
+ include IntervalNotation
4
+ include IntervalNotation::Syntax::Short
5
+
6
+ interval_set_vs_segmentation = {
7
+ 'for non-empty interval set' => [
8
+ cc(0,3) | oo(4,5) | oc(5,10) | co(12,15),
9
+ Segmentation.new([
10
+ Segmentation::Segment.new(lt_basic(0), false),
11
+ Segmentation::Segment.new(cc_basic(0,3), true),
12
+ Segmentation::Segment.new(oc_basic(3,4), false),
13
+ Segmentation::Segment.new(oo_basic(4,5), true),
14
+ Segmentation::Segment.new(pt_basic(5), false),
15
+ Segmentation::Segment.new(oc_basic(5,10), true),
16
+ Segmentation::Segment.new(oo_basic(10,12), false),
17
+ Segmentation::Segment.new(co_basic(12,15), true),
18
+ Segmentation::Segment.new(ge_basic(15), false),
19
+ ])
20
+ ],
21
+ 'for empty interval set' => [
22
+ Empty,
23
+ Segmentation.new([
24
+ Segmentation::Segment.new(oo_basic(-Float::INFINITY, Float::INFINITY), false)
25
+ ])
26
+ ],
27
+ 'for entire R interval set' => [
28
+ R,
29
+ Segmentation.new([
30
+ Segmentation::Segment.new(oo_basic(-Float::INFINITY, Float::INFINITY), true)
31
+ ])
32
+ ],
33
+ 'for a single point' => [
34
+ pt(3),
35
+ Segmentation.new([
36
+ Segmentation::Segment.new(lt_basic(3), false),
37
+ Segmentation::Segment.new(pt_basic(3), true),
38
+ Segmentation::Segment.new(gt_basic(3), false),
39
+ ])
40
+ ],
41
+ 'for left-infinite interval set' => [
42
+ lt(3) | oo(5,10),
43
+ Segmentation.new([
44
+ Segmentation::Segment.new(lt_basic(3), true),
45
+ Segmentation::Segment.new(cc_basic(3,5), false),
46
+ Segmentation::Segment.new(oo_basic(5,10), true),
47
+ Segmentation::Segment.new(ge_basic(10), false),
48
+ ])
49
+ ],
50
+ 'for right-infinite interval set' => [
51
+ oo(5,10) | gt(15),
52
+ Segmentation.new([
53
+ Segmentation::Segment.new(le_basic(5), false),
54
+ Segmentation::Segment.new(oo_basic(5,10), true),
55
+ Segmentation::Segment.new(cc_basic(10,15), false),
56
+ Segmentation::Segment.new(gt_basic(15), true),
57
+ ])
58
+ ],
59
+ 'for left- and right- infinite interval set' => [
60
+ lt(3) | oo(5,10) | ge(15),
61
+ Segmentation.new([
62
+ Segmentation::Segment.new(lt_basic(3), true),
63
+ Segmentation::Segment.new(cc_basic(3,5), false),
64
+ Segmentation::Segment.new(oo_basic(5,10), true),
65
+ Segmentation::Segment.new(co_basic(10,15), false),
66
+ Segmentation::Segment.new(ge_basic(15), true),
67
+ ])
68
+ ],
69
+ }
70
+
71
+ describe IntervalNotation::Segmentation do
72
+ describe '#make_interval_set' do
73
+ interval_set_vs_segmentation.each do |example, (interval_set, segmentation)|
74
+ it "#{example} returns corresponding segmentation" do
75
+ expect(interval_set.make_segmentation).to eq(segmentation)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ describe IntervalNotation::IntervalSet do
82
+ describe '#make_segmentation' do
83
+ interval_set_vs_segmentation.each do |example, (interval_set, segmentation)|
84
+ it "#{example} returns corresponding interval set" do
85
+ expect(segmentation.make_interval_set).to eq(interval_set)
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+
92
+ describe IntervalNotation::Segmentation do
93
+ let(:segmentation){
94
+ Segmentation.new([
95
+ Segmentation::Segment.new(lt_basic(0), :A),
96
+ Segmentation::Segment.new(co_basic(0,3), :B),
97
+ Segmentation::Segment.new(cc_basic(3,5), :C),
98
+ Segmentation::Segment.new(oo_basic(5,7), :D),
99
+ Segmentation::Segment.new(pt_basic(7), :E),
100
+ Segmentation::Segment.new(oc_basic(7,10), :F),
101
+ Segmentation::Segment.new(gt_basic(10), :G),
102
+ ])
103
+ }
104
+
105
+ let(:segmentation_le_ge_infinite_segments){
106
+ Segmentation.new([
107
+ Segmentation::Segment.new(le_basic(0), :A),
108
+ Segmentation::Segment.new(oo_basic(0,3), :B),
109
+ Segmentation::Segment.new(cc_basic(3,5), :C),
110
+ Segmentation::Segment.new(oo_basic(5,7), :D),
111
+ Segmentation::Segment.new(pt_basic(7), :E),
112
+ Segmentation::Segment.new(oo_basic(7,10), :F),
113
+ Segmentation::Segment.new(ge_basic(10), :G),
114
+ ])
115
+ }
116
+
117
+ describe '#segment_covering_point' do
118
+ specify 'returns segment' do
119
+ expect(segmentation.segment_covering_point(2)).to eq Segmentation::Segment.new(co_basic(0,3), :B)
120
+ end
121
+ specify 'works for general position case in closed-open interval' do
122
+ expect(segmentation.segment_covering_point(2).state).to eq(:B)
123
+ end
124
+ specify 'works for general position case in closed-closed interval' do
125
+ expect(segmentation.segment_covering_point(4).state).to eq(:C)
126
+ end
127
+ specify 'works for general position case in open-open interval' do
128
+ expect(segmentation.segment_covering_point(6).state).to eq(:D)
129
+ end
130
+ specify 'works for general position case in open-closed interval' do
131
+ expect(segmentation.segment_covering_point(8).state).to eq(:F)
132
+ end
133
+ specify 'works for general position case in less-than interval' do
134
+ expect(segmentation.segment_covering_point(-1).state).to eq(:A)
135
+ end
136
+ specify 'works for general position case in greater-than interval' do
137
+ expect(segmentation.segment_covering_point(11).state).to eq(:G)
138
+ end
139
+ specify 'works for general position case in less-than-or-equal-to interval' do
140
+ expect(segmentation_le_ge_infinite_segments.segment_covering_point(-1).state).to eq(:A)
141
+ end
142
+ specify 'works for general position case in greater-than-or-equal-to interval' do
143
+ expect(segmentation_le_ge_infinite_segments.segment_covering_point(11).state).to eq(:G)
144
+ end
145
+ specify 'works for singular point segment' do
146
+ expect(segmentation.segment_covering_point(7).state).to eq(:E)
147
+ end
148
+
149
+ specify 'works for leftmost boundary in closed-open interval' do
150
+ expect(segmentation.segment_covering_point(0).state).to eq(:B)
151
+ end
152
+ specify 'works for rightmost boundary in open-closed interval' do
153
+ expect(segmentation.segment_covering_point(10).state).to eq(:F)
154
+ end
155
+ specify 'works for leftmost boundary in closed-closed interval' do
156
+ expect(segmentation.segment_covering_point(3).state).to eq(:C)
157
+ end
158
+ specify 'works for rightmost boundary in closed-closed interval' do
159
+ expect(segmentation.segment_covering_point(5).state).to eq(:C)
160
+ end
161
+
162
+ specify 'works for rightmost boundary in less-than interval' do
163
+ expect(segmentation.segment_covering_point(0).state).to eq(:B)
164
+ end
165
+ specify 'works for leftmost boundary in greater-than interval' do
166
+ expect(segmentation.segment_covering_point(10).state).to eq(:F)
167
+ end
168
+
169
+
170
+ specify 'works for rightmost boundary in less-than-or-equal-to interval' do
171
+ expect(segmentation_le_ge_infinite_segments.segment_covering_point(0).state).to eq(:A)
172
+ end
173
+ specify 'works for leftmost boundary in greater-than-or-equal-to interval' do
174
+ expect(segmentation_le_ge_infinite_segments.segment_covering_point(10).state).to eq(:G)
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,69 @@
1
+ require_relative '../lib/interval_notation'
2
+
3
+ include IntervalNotation
4
+ include IntervalNotation::Syntax::Short
5
+
6
+ describe IntervalNotation::SweepLine do
7
+ describe '#make_segmentation' do
8
+
9
+ describe 'SingleTagging' do
10
+ specify 'When tags are all different' do
11
+ tagged_intervals = [[oo(0,10), :A], [cc(0,8), :B], [oo(5,15), :C]]
12
+ segmentation = SweepLine.make_tagging(tagged_intervals)
13
+ expected_result = Segmentation.new([
14
+ Segmentation::Segment.new(lt_basic(0), Set.new),
15
+ Segmentation::Segment.new(pt_basic(0), Set.new([:B])),
16
+ Segmentation::Segment.new(oc_basic(0,5), Set.new([:A,:B])),
17
+ Segmentation::Segment.new(oc_basic(5,8), Set.new([:A,:B,:C])),
18
+ Segmentation::Segment.new(oo_basic(8,10), Set.new([:A,:C])),
19
+ Segmentation::Segment.new(co_basic(10,15), Set.new([:C])),
20
+ Segmentation::Segment.new(ge_basic(15), Set.new),
21
+ ])
22
+ expect(segmentation).to eq(expected_result)
23
+ end
24
+ specify 'When some tags are the same' do
25
+ tagged_intervals = [[oo(0,10), :A], [cc(0,8), :B], [oo(5,15), :A]]
26
+ segmentation = SweepLine.make_tagging(tagged_intervals)
27
+ expected_result = Segmentation.new([
28
+ Segmentation::Segment.new(lt_basic(0), Set.new),
29
+ Segmentation::Segment.new(pt_basic(0), Set.new([:B])),
30
+ Segmentation::Segment.new(oc_basic(0,8), Set.new([:A,:B])),
31
+ Segmentation::Segment.new(oo_basic(8,15), Set.new([:A])),
32
+ Segmentation::Segment.new(ge_basic(15), Set.new),
33
+ ])
34
+ expect(segmentation).to eq(expected_result)
35
+ end
36
+ end
37
+
38
+ describe 'MultiTagging' do
39
+ specify 'When tags are all different' do
40
+ tagged_intervals = [[oo(0,10), :A], [cc(0,8), :B], [oo(5,15), :C]]
41
+ segmentation = SweepLine.make_multitagging(tagged_intervals)
42
+ expected_result = Segmentation.new([
43
+ Segmentation::Segment.new(lt_basic(0), {}),
44
+ Segmentation::Segment.new(pt_basic(0), {:B => 1}),
45
+ Segmentation::Segment.new(oc_basic(0,5), {:A => 1, :B => 1}),
46
+ Segmentation::Segment.new(oc_basic(5,8), {:A => 1, :B => 1, :C => 1}),
47
+ Segmentation::Segment.new(oo_basic(8,10), {:A => 1, :C => 1}),
48
+ Segmentation::Segment.new(co_basic(10,15), {:C => 1}),
49
+ Segmentation::Segment.new(ge_basic(15), {}),
50
+ ])
51
+ expect(segmentation).to eq(expected_result)
52
+ end
53
+ specify 'When some tags are the same' do
54
+ tagged_intervals = [[oo(0,10), :A], [cc(0,8), :B], [oo(5,15), :A]]
55
+ segmentation = SweepLine.make_multitagging(tagged_intervals)
56
+ expected_result = Segmentation.new([
57
+ Segmentation::Segment.new(lt_basic(0), {}),
58
+ Segmentation::Segment.new(pt_basic(0), {:B => 1}),
59
+ Segmentation::Segment.new(oc_basic(0,5), {:A => 1, :B => 1}),
60
+ Segmentation::Segment.new(oc_basic(5,8), {:A => 2, :B => 1}),
61
+ Segmentation::Segment.new(oo_basic(8,10), {:A => 2}),
62
+ Segmentation::Segment.new(co_basic(10,15), {:A => 1}),
63
+ Segmentation::Segment.new(ge_basic(15), {}),
64
+ ])
65
+ expect(segmentation).to eq(expected_result)
66
+ end
67
+ end
68
+ end
69
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: interval_notation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilya Vorontsov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-04 00:00:00.000000000 Z
11
+ date: 2015-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -70,15 +70,26 @@ files:
70
70
  - interval_notation.gemspec
71
71
  - lib/interval_notation.rb
72
72
  - lib/interval_notation/basic_intervals.rb
73
- - lib/interval_notation/combiners.rb
74
73
  - lib/interval_notation/error.rb
75
74
  - lib/interval_notation/interval_set.rb
76
75
  - lib/interval_notation/operations.rb
76
+ - lib/interval_notation/segmentation.rb
77
+ - lib/interval_notation/sweep_line.rb
78
+ - lib/interval_notation/sweep_line/sweep_line.rb
79
+ - lib/interval_notation/sweep_line/trace_state/intersection.rb
80
+ - lib/interval_notation/sweep_line/trace_state/multiple_state.rb
81
+ - lib/interval_notation/sweep_line/trace_state/subtract.rb
82
+ - lib/interval_notation/sweep_line/trace_state/symmetric_difference.rb
83
+ - lib/interval_notation/sweep_line/trace_state/tagging.rb
84
+ - lib/interval_notation/sweep_line/trace_state/union.rb
85
+ - lib/interval_notation/sweep_line/trace_states.rb
77
86
  - lib/interval_notation/version.rb
78
87
  - spec/basic_intervals_spec.rb
79
88
  - spec/intersection_spec.rb
80
89
  - spec/interval_notation_spec.rb
90
+ - spec/segmentation_spec.rb
81
91
  - spec/spec_helper.rb
92
+ - spec/tagging_spec.rb
82
93
  homepage: https://github.com/prijutme4ty/interval_notation
83
94
  licenses:
84
95
  - MIT
@@ -107,4 +118,6 @@ test_files:
107
118
  - spec/basic_intervals_spec.rb
108
119
  - spec/intersection_spec.rb
109
120
  - spec/interval_notation_spec.rb
121
+ - spec/segmentation_spec.rb
110
122
  - spec/spec_helper.rb
123
+ - spec/tagging_spec.rb
@@ -1,166 +0,0 @@
1
- require 'set'
2
-
3
- module IntervalNotation
4
- # Combiner is an internal helper class for combining interval sets using sweep line.
5
- # It starts moving from -∞ to +∞ and keep which intervals are crossed by sweep line.
6
- # Class helps to effectively recalculate number of crossed intervals without rechecking
7
- # all intervals each time, and dramatically reduces speed of operations on large number of intervals.
8
- #
9
- # Usage example:
10
- # UnionCombiner.new(3).combine([interval_1, interval_2, interval_3])
11
- class Combiner
12
- attr_reader :num_interval_sets
13
- attr_reader :previous_state
14
-
15
- def initialize(num_interval_sets)
16
- @num_interval_sets = num_interval_sets
17
- @inside = Array.new(num_interval_sets, false)
18
- @num_intervals_inside = 0 # number of intervals, we are inside (for efficiency)
19
- end
20
-
21
- # Combines intervals according on an information given by #state/#include_last_point functions
22
- # which tell whether current section or point should be included to a new interval.
23
- def combine(interval_sets)
24
- points = interval_sets.each_with_index.flat_map{|interval_set, interval_set_index|
25
- interval_set.intervals.flat_map{|interval|
26
- interval.interval_boundaries(interval_set_index)
27
- }
28
- }.sort_by(&:value)
29
-
30
- intervals = []
31
-
32
- incl_from = nil
33
- from = nil
34
-
35
- points.chunk(&:value).each do |point_value, points_on_place|
36
- pass(points_on_place)
37
-
38
- if previous_state
39
- if state
40
- unless include_last_point
41
- intervals << BasicIntervals.interval_by_boundary_inclusion(incl_from, from, false, point_value)
42
- incl_from = false
43
- from = point_value
44
- end
45
- else
46
- to = point_value
47
- incl_to = include_last_point
48
- intervals << BasicIntervals.interval_by_boundary_inclusion(incl_from, from, incl_to, to)
49
- from = nil # easier to find an error (but not necessary code)
50
- incl_from = nil # ditto
51
- end
52
- else
53
- if state
54
- from = point_value
55
- incl_from = include_last_point
56
- else
57
- intervals << BasicIntervals::Point.new(point_value) if include_last_point
58
- end
59
- end
60
- end
61
- IntervalSet.new_unsafe(intervals)
62
- end
63
-
64
- # When sweep line pass several interval boundaries, +#pass+ should get all those points at once
65
- # and update status of crossing sweep line.
66
- # It also stores previous state, because it's actively used downstream.
67
- def pass(points_on_place)
68
- @previous_state = state
69
- pass_recalculation(points_on_place)
70
- end
71
-
72
- # See +#pass+ for details
73
- def pass_recalculation(points_on_place) # :nodoc:
74
- num_spanning_intervals = @num_intervals_inside
75
- num_covering_boundaries = 0
76
-
77
- points_on_place.each do |point|
78
- num_covering_boundaries += 1 if point.included
79
-
80
- if point.interval_boundary
81
- num_spanning_intervals -= 1 if point.closing
82
- @num_intervals_inside += (@inside[point.interval_index] ? -1 : 1)
83
- @inside[point.interval_index] ^= true
84
- end
85
- end
86
- @num_interval_sets_covering_last_point = num_spanning_intervals + num_covering_boundaries
87
- end
88
- private :pass_recalculation
89
- end
90
-
91
- class UnionCombiner < Combiner
92
- # checks whether current section should be included
93
- def state
94
- @num_intervals_inside > 0
95
- end
96
-
97
- # checks whether last passed point should be included
98
- def include_last_point
99
- @num_interval_sets_covering_last_point > 0
100
- end
101
- end
102
-
103
- class IntersectCombiner < Combiner
104
- # checks whether current section should be included
105
- def state
106
- @num_intervals_inside == num_interval_sets
107
- end
108
-
109
- # checks whether last passed point should be included
110
- def include_last_point
111
- @num_interval_sets_covering_last_point == num_interval_sets
112
- end
113
- end
114
-
115
- class SubtractCombiner < Combiner
116
- # checks whether last passed point should be included
117
- attr_reader :include_last_point
118
-
119
- def initialize
120
- @include_last_point = nil
121
- @inside = [false, false]
122
- end
123
-
124
- # checks whether current section should be included
125
- def state
126
- @inside[0] && ! @inside[1]
127
- end
128
-
129
- # See +#pass+ for details
130
- def pass_recalculation(points_on_place) # :nodoc:
131
- included = @inside.dup
132
- points_on_place.each do |point|
133
- @inside[point.interval_index] ^= point.interval_boundary # doesn't change on singular points
134
- included[point.interval_index] = point.included
135
- end
136
- @include_last_point = included[0] && !included[1]
137
- end
138
- private :pass_recalculation
139
- end
140
-
141
- class SymmetricDifferenceCombiner < Combiner
142
- # checks whether last passed point should be included
143
- attr_reader :include_last_point
144
-
145
- def initialize
146
- @include_last_point = nil
147
- @inside = [false, false]
148
- end
149
-
150
- # checks whether current section should be included
151
- def state
152
- @inside[0] ^ @inside[1]
153
- end
154
-
155
- # See +#pass+ for details
156
- def pass_recalculation(points_on_place) # :nodoc:
157
- included = @inside.dup
158
- points_on_place.each do |point|
159
- @inside[point.interval_index] ^= point.interval_boundary # doesn't change on singular points
160
- included[point.interval_index] = point.included
161
- end
162
- @include_last_point = included[0] ^ included[1]
163
- end
164
- private :pass_recalculation
165
- end
166
- end