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.
- checksums.yaml +4 -4
- data/README.md +92 -4
- data/TODO.md +1 -0
- data/lib/interval_notation.rb +72 -48
- data/lib/interval_notation/basic_intervals.rb +4 -1
- data/lib/interval_notation/interval_set.rb +22 -2
- data/lib/interval_notation/operations.rb +3 -3
- data/lib/interval_notation/segmentation.rb +113 -0
- data/lib/interval_notation/sweep_line.rb +3 -0
- data/lib/interval_notation/sweep_line/sweep_line.rb +97 -0
- data/lib/interval_notation/sweep_line/trace_state/intersection.rb +56 -0
- data/lib/interval_notation/sweep_line/trace_state/multiple_state.rb +46 -0
- data/lib/interval_notation/sweep_line/trace_state/subtract.rb +20 -0
- data/lib/interval_notation/sweep_line/trace_state/symmetric_difference.rb +20 -0
- data/lib/interval_notation/sweep_line/trace_state/tagging.rb +72 -0
- data/lib/interval_notation/sweep_line/trace_state/union.rb +57 -0
- data/lib/interval_notation/sweep_line/trace_states.rb +6 -0
- data/lib/interval_notation/version.rb +1 -1
- data/spec/interval_notation_spec.rb +42 -0
- data/spec/segmentation_spec.rb +177 -0
- data/spec/tagging_spec.rb +69 -0
- metadata +16 -3
- data/lib/interval_notation/combiners.rb +0 -166
@@ -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.
|
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-
|
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
|