interval_notation 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/benchmark/benchmark.rb +51 -0
- data/lib/interval_notation/basic_intervals.rb +10 -0
- data/lib/interval_notation/interval_set.rb +80 -4
- data/lib/interval_notation/version.rb +1 -1
- data/spec/basic_intervals_spec.rb +1 -1
- data/spec/intersection_spec.rb +275 -0
- data/spec/interval_notation_spec.rb +9 -157
- data/spec/spec_helper.rb +35 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4160164096cbfcc4f171ce43a2c460fb1d3df70c
|
4
|
+
data.tar.gz: 3b4f604be240d5d714bd1eacfc3b158c41de7287
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5988e37f501ec499213a5eee17c63012c99d5d55df1f3e076e62bab39f11b03f3a5b60958e109b8b07237499bab442bb0688f3da0a176da827d3f487fa62fb8
|
7
|
+
data.tar.gz: c89451349d28a973a45ec6ed7017be6502be18bb9e72745031d638b2763aaa825d57266abc708696e8c5fbb7bc5ab0eee44bd2ee9a1038beb9082ef994d5fe4d
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require_relative '../lib/interval_notation'
|
3
|
+
include IntervalNotation::Syntax::Long
|
4
|
+
|
5
|
+
num_intervals_to_unite = 10_000
|
6
|
+
srand(13)
|
7
|
+
random_intervals_1 = num_intervals_to_unite.times.map{|i| 2.times.map{ 0.3*i + rand }.sort }.reject{|a,b| a==b }.map{|a,b| closed_closed(a,b) }
|
8
|
+
random_intervals_2 = num_intervals_to_unite.times.map{|i| 2.times.map{ 3000 + 0.3*i + rand }.sort }.reject{|a,b| a==b }.map{|a,b| closed_closed(a,b) }
|
9
|
+
|
10
|
+
N = 10
|
11
|
+
M = 100_000
|
12
|
+
|
13
|
+
dispersed_interval_1 = IntervalNotation::Operations.union(random_intervals_1)
|
14
|
+
dispersed_interval_2 = IntervalNotation::Operations.union(random_intervals_2)
|
15
|
+
|
16
|
+
singular_interval_1 = closed_closed(500 + rand, 1000 + rand)
|
17
|
+
singular_interval_2 = closed_closed(700 + rand, 1700 + rand)
|
18
|
+
|
19
|
+
Benchmark.bm do |benchmark_report|
|
20
|
+
|
21
|
+
benchmark_report.report("old intersection check for dispersed intervals to singular interval (#{N} times)") do
|
22
|
+
N.times do
|
23
|
+
dispersed_interval_1.intersection(singular_interval_1).empty?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
benchmark_report.report("intersect? dispersed intervals to singular interval (#{M} times)") do
|
28
|
+
M.times do
|
29
|
+
dispersed_interval_1.intersect?(singular_interval_1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
benchmark_report.report("intersect? singular interval to dispersed intervals (#{M} times)") do
|
34
|
+
M.times do
|
35
|
+
singular_interval_1.intersect?(dispersed_interval_1)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
benchmark_report.report("intersect? two dispersed intervals (#{M} times)") do
|
40
|
+
M.times do
|
41
|
+
dispersed_interval_2.intersect?(dispersed_interval_1)
|
42
|
+
dispersed_interval_1.intersect?(dispersed_interval_2)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
benchmark_report.report("Unite #{num_intervals_to_unite} intervals (#{N} times)") do
|
47
|
+
N.times do
|
48
|
+
IntervalNotation::Operations.union(random_intervals_1)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -47,6 +47,11 @@ module IntervalNotation
|
|
47
47
|
def singular_point?; false; end
|
48
48
|
def hash; [@from, @to, include_from?, include_to?].hash; end;
|
49
49
|
def eql?(other); other.class.equal?(self.class) && from.eql?(other.from) && to.eql?(other.to); end
|
50
|
+
|
51
|
+
# include position and its vicinity
|
52
|
+
def deep_include_position?(pos)
|
53
|
+
from < pos && pos < to
|
54
|
+
end
|
50
55
|
end
|
51
56
|
|
52
57
|
class OpenOpenInterval
|
@@ -190,6 +195,11 @@ module IntervalNotation
|
|
190
195
|
def interval_boundaries(interval_index)
|
191
196
|
BoundaryPoint.new(from, true, nil, interval_index, false)
|
192
197
|
end
|
198
|
+
|
199
|
+
# include position and its vicinity (point can't include vicinity of a position)
|
200
|
+
def deep_include_position?(pos)
|
201
|
+
false
|
202
|
+
end
|
193
203
|
end
|
194
204
|
|
195
205
|
def self.interval_by_boundary_inclusion(include_from, from, include_to, to)
|
@@ -53,10 +53,15 @@ module IntervalNotation
|
|
53
53
|
# Checks whether an interval set contains certain position.
|
54
54
|
# Operation complexity is O(ln N), where N is a number of contiguous regions in an interval set
|
55
55
|
def include_position?(value)
|
56
|
-
interval = @intervals.bsearch{|interv| interv.to
|
56
|
+
interval = @intervals.bsearch{|interv| value <= interv.to }
|
57
57
|
interval && interval.include_position?(value)
|
58
58
|
end
|
59
59
|
|
60
|
+
def deep_include_position?(value)
|
61
|
+
interval = @intervals.bsearch{|interv| value <= interv.to }
|
62
|
+
interval && interval.deep_include_position?(value)
|
63
|
+
end
|
64
|
+
|
60
65
|
# Checks whether an interval set contains another interval set. Alias: +#include?+
|
61
66
|
def contain?(other)
|
62
67
|
self.intersection(other) == other
|
@@ -69,10 +74,81 @@ module IntervalNotation
|
|
69
74
|
end
|
70
75
|
alias covered_by? contained_by?
|
71
76
|
|
72
|
-
|
73
|
-
|
77
|
+
|
78
|
+
def bsearch_last_not_meeting_condition(arr)
|
79
|
+
found_ind = (0...arr.size).bsearch{|idx| yield(arr[idx]) } # find first not meeting condition
|
80
|
+
if found_ind
|
81
|
+
found_ind == 0 ? nil : arr[found_ind - 1]
|
82
|
+
else
|
83
|
+
arr.last
|
84
|
+
end
|
85
|
+
end
|
86
|
+
private :bsearch_last_not_meeting_condition
|
87
|
+
|
88
|
+
# Checks intersection with a single (basic) interval in a O(log N) time.
|
89
|
+
def intersect_single_interval?(interval) # :nodoc:
|
90
|
+
from = interval.from
|
91
|
+
to = interval.to
|
92
|
+
|
93
|
+
# If from is against a singular point, then ignore it.
|
94
|
+
#..............................pos............................
|
95
|
+
# interval_left_to_pos...................interval_right_to_pos
|
96
|
+
#
|
97
|
+
#..............................pos.............................
|
98
|
+
# interval_left_to_pos........point.......interval_right_to_pos
|
99
|
+
# reversed_intervals = @intervals.reverse
|
100
|
+
|
101
|
+
# find last interval +interv+ such that (interv.from < from)
|
102
|
+
left_to_start = bsearch_last_not_meeting_condition(@intervals){|interv| interv.from >= from }
|
103
|
+
# find last interval +interv+ such that (interv.from < to)
|
104
|
+
left_to_finish = bsearch_last_not_meeting_condition(@intervals){|interv| interv.from >= to }
|
105
|
+
|
106
|
+
# find first interval +interv+ such that from < interv.to
|
107
|
+
right_to_start = @intervals.bsearch{|interv| from < interv.to }
|
108
|
+
# find first interval +interv+ such that to < interv.to
|
109
|
+
right_to_finish = @intervals.bsearch{|interv| to < interv.to }
|
110
|
+
|
111
|
+
# If +from+ or +to+ is included in an interval, it is either
|
112
|
+
# a) deeply immersed (i.e. lie within interval with its vicinity) in it or
|
113
|
+
# b) just adjoins an interval(then it matters, whether it's included)
|
114
|
+
# If neither of points lie on an interval set, then it can still intersect an interval
|
115
|
+
# if +from+ and +to+ points are between different pairs of intervals.
|
116
|
+
# Problems come with singular points. If one is against interval's from or to,
|
117
|
+
# it is either treated as included, or is a deleted point going as either left or right boundary.
|
118
|
+
# It's hard to distinguish which boundary it is, so we just ignore a point if it is against +from+ or +to+
|
119
|
+
# (see a trick above) and treat intervals going the same side from a point as non-intersecting it.
|
120
|
+
# If no singular point exist against +from+ and +to+ positions, the non-overlapping interval have both
|
121
|
+
# +left_to_start == left_to_finish+ and +right_to_start == right_to_finish+.
|
122
|
+
# Otherwise +interval+ overlap (cover) some interval.
|
123
|
+
include_position?(from) && (deep_include_position?(from) || interval.include_from?) ||
|
124
|
+
include_position?(to) && (deep_include_position?(to) || interval.include_to?) ||
|
125
|
+
!(left_to_start == left_to_finish || right_to_start == right_to_finish)
|
126
|
+
end
|
127
|
+
protected :intersect_single_interval?
|
128
|
+
|
129
|
+
# Checks whether intervals intersect in O(M*log N) where M and N are interval set sizes
|
130
|
+
def intersect_n_log_n?(other)
|
131
|
+
# sz_1 = num_connected_components + 2
|
132
|
+
# sz_2 = other.num_connected_components + 2
|
133
|
+
# if sz_1*Math.log2(sz_2) < sz_2 * Math.log2(sz_1)
|
134
|
+
|
135
|
+
# each of N intervals intersection check takes log(M). We prefer to take small N, large M than vice-versa
|
136
|
+
if @intervals.size < other.intervals.size
|
137
|
+
@intervals.any? do |segment|
|
138
|
+
other.intersect_single_interval?(segment)
|
139
|
+
end
|
140
|
+
else
|
141
|
+
other.intervals.any? do |segment|
|
142
|
+
intersect_single_interval?(segment)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
protected :intersect_n_log_n?
|
147
|
+
|
148
|
+
# Checks whether an interval set intersects another interval set. Alias: +#overlap?+
|
74
149
|
def intersect?(other)
|
75
|
-
|
150
|
+
intersect_n_log_n?(other) # ToDo: balance different implementations for different interval set sizes
|
151
|
+
# ! intersection(other).empty? # Simplest and too slow implementation
|
76
152
|
end
|
77
153
|
alias overlap? intersect?
|
78
154
|
|
@@ -0,0 +1,275 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe IntervalNotation do
|
4
|
+
describe IntervalSet do
|
5
|
+
|
6
|
+
describe '#intersection' do
|
7
|
+
{
|
8
|
+
[oo(1,3), oo(1,3)] => oo(1,3),
|
9
|
+
[oo(1,3), oc(1,3)] => oo(1,3),
|
10
|
+
[oo(1,3), co(1,3)] => oo(1,3),
|
11
|
+
[oo(1,3), cc(1,3)] => oo(1,3),
|
12
|
+
|
13
|
+
[pt(2), pt(3)] => Empty,
|
14
|
+
[pt(3), pt(3)] => pt(3),
|
15
|
+
[pt(3)|pt(5), pt(3)|pt(5)] => pt(3)|pt(5),
|
16
|
+
[pt(3)|pt(5), pt(3)|pt(7)] => pt(3),
|
17
|
+
|
18
|
+
[oo(1,3)|oo(5,7), oo(1,3)|oo(5,7)] => oo(1,3)|oo(5,7),
|
19
|
+
[oo(1,3)|co(5,7), oo(1,3)|oo(5,7)] => oo(1,3)|oo(5,7),
|
20
|
+
[oo(1,3)|oo(5,7), oo(1,3)|co(5,7)] => oo(1,3)|oo(5,7),
|
21
|
+
[oo(1,3)|co(5,7), oo(1,3)|co(5,7)] => oo(1,3)|co(5,7),
|
22
|
+
|
23
|
+
[oo(1,3)|oo(3,5), oo(1,3)] => oo(1,3),
|
24
|
+
[oo(1,3)|oo(3,5), oo(1,3)|oo(7,9)] => oo(1,3),
|
25
|
+
[oo(1,3)|oo(3,5), oo(3,5)] => oo(3,5),
|
26
|
+
[oo(1,3)|oo(3,5), oo(1,3)|oo(3,5)] => oo(1,3)|oo(3,5),
|
27
|
+
|
28
|
+
[oo(1,3), oo(4,5)] => Empty,
|
29
|
+
[oo(1,3), oo(3,5)] => Empty,
|
30
|
+
[oo(1,3), co(3,5)] => Empty,
|
31
|
+
[oc(1,3), oo(3,5)] => Empty,
|
32
|
+
[oc(1,3), co(3,5)] => pt(3),
|
33
|
+
|
34
|
+
[oo(1,3), oo(0,5)] => oo(1,3),
|
35
|
+
[oc(1,3), oo(0,5)] => oc(1,3),
|
36
|
+
[co(1,3), oo(0,5)] => co(1,3),
|
37
|
+
[cc(1,3), oo(0,5)] => cc(1,3),
|
38
|
+
|
39
|
+
[oo(1,3), oo(1,5)] => oo(1,3),
|
40
|
+
[co(1,3), oo(1,5)] => oo(1,3),
|
41
|
+
[oc(1,3), oo(1,5)] => oc(1,3),
|
42
|
+
[cc(1,3), oo(1,5)] => oc(1,3),
|
43
|
+
[co(3,5), oo(1,5)] => co(3,5),
|
44
|
+
|
45
|
+
[oo(1,3), oo(2,5)] => oo(2,3),
|
46
|
+
[oc(1,3), oo(2,5)] => oc(2,3),
|
47
|
+
[oo(1,3), co(2,5)] => co(2,3),
|
48
|
+
[oc(1,3), co(2,5)] => cc(2,3),
|
49
|
+
|
50
|
+
[oo(1,3), pt(2)] => pt(2),
|
51
|
+
[oo(1,3), pt(3)] => Empty,
|
52
|
+
[oc(1,3), pt(3)] => pt(3),
|
53
|
+
[oo(1,3), pt(4)] => Empty,
|
54
|
+
[oc(1,3), pt(4)] => Empty,
|
55
|
+
|
56
|
+
[oo(1,3)|oo(3,5), oo(2,3)|oo(3,7)] => oo(2,3)|oo(3,5),
|
57
|
+
[oo(1,3)|oo(3,5), oo(2,7)] => oo(2,3)|oo(3,5),
|
58
|
+
|
59
|
+
[oo(2,6), oo(1,3)|co(5,7)] => oo(2,3)|co(5,6),
|
60
|
+
[oo(1,6), oo(1,3)|co(5,7)] => oo(1,3)|co(5,6),
|
61
|
+
[oo(0,6), oo(1,3)|co(5,7)] => oo(1,3)|co(5,6),
|
62
|
+
[oo(0,6), oo(1,3)|co(5,6)] => oo(1,3)|co(5,6),
|
63
|
+
|
64
|
+
|
65
|
+
}.each do |intervals, answer|
|
66
|
+
it "IntervalNotation::Operations.intersection(#{intervals.map(&:to_s).join(',')} should equal #{answer}" do
|
67
|
+
expect( IntervalNotation::Operations.intersection(intervals) ).to eq answer
|
68
|
+
end
|
69
|
+
|
70
|
+
it "IntervalNotation::Operations.intersection(#{intervals.map(&:to_s).join(',')} should equal consequent unite: #{intervals.map(&:to_s).join('&')}" do
|
71
|
+
expect( IntervalNotation::Operations.intersection(intervals) ).to eq intervals.inject(&:intersection)
|
72
|
+
end
|
73
|
+
|
74
|
+
if intervals.size == 2
|
75
|
+
interval_1, interval_2 = intervals
|
76
|
+
it "#{interval_1} & #{interval_2} should equal #{answer}" do
|
77
|
+
expect( interval_1.intersection(interval_2) ).to eq answer
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
{
|
83
|
+
[oo(1,3), oo(1,3), oo(1,3)] => oo(1,3),
|
84
|
+
[oo(1,3), cc(1,3), oo(1,3)] => oo(1,3),
|
85
|
+
[oo(1,3), cc(0,5), cc(1,3)] => oo(1,3),
|
86
|
+
[cc(1,5), cc(3,7), cc(1,3)|cc(5,7)] => pt(3)|pt(5),
|
87
|
+
[oo(1,5), oo(3,7), oo(1,3)|oo(5,7)] => Empty,
|
88
|
+
[oo(1,5), oc(1,3), co(3,5)] => pt(3)
|
89
|
+
}.each do |intervals, answer|
|
90
|
+
it "#{intervals.map(&:to_s).join('&')} should equal #{answer}" do
|
91
|
+
expect( IntervalNotation::Operations.intersection(intervals) ).to eq answer
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Very extensive automatic testing
|
97
|
+
describe '#intersect?', too_extensive_testing: true, slow: true do
|
98
|
+
examples = []
|
99
|
+
examples += [
|
100
|
+
[[oo(1,3)|oo(4,6)|oo(8,10), oo(3,8)], true],
|
101
|
+
[[oo(1,3)|oo(4,6)|oo(8,10)|cc(12,14) , oo(3,8)], true],
|
102
|
+
[[oo(1,3)|cc(4,6)|oo(8,10), oo(3,8)], true],
|
103
|
+
[[oo(1,3)|pt(5)|oo(8,10), oo(3,8)], true],
|
104
|
+
[[oo(1,3)|oo(8,10), oo(3,8)], false],
|
105
|
+
[[cc(1,3)|cc(8,10), oo(3,8)], false],
|
106
|
+
|
107
|
+
[[oo(1,3)|oo(8,10), cc(3,8)], false],
|
108
|
+
[[co(1,3)|oc(8,10), cc(3,8)], false],
|
109
|
+
[[oc(1,3)|oo(8,10), cc(3,8)], true],
|
110
|
+
[[oo(1,3)|co(8,10), cc(3,8)], true],
|
111
|
+
[[oc(1,3)|co(8,10), cc(3,8)], true],
|
112
|
+
[[cc(1,3)|cc(8,10), cc(3,8)], true],
|
113
|
+
|
114
|
+
[[oc(1,3)|oo(8,10), co(3,8)], true],
|
115
|
+
[[oc(1,3)|cc(8,10), co(3,8)], true],
|
116
|
+
[[oo(1,3)|oo(8,10), co(3,8)], false],
|
117
|
+
[[oo(1,3)|cc(8,10), co(3,8)], false],
|
118
|
+
|
119
|
+
[[oo(1,3)|oo(8,10), oc(3,8)], false],
|
120
|
+
[[oo(1,3)|oc(8,10), oc(3,8)], false],
|
121
|
+
[[oo(1,3)|co(8,10), oc(3,8)], true],
|
122
|
+
[[oo(1,3)|cc(8,10), oc(3,8)], true],
|
123
|
+
|
124
|
+
[[oo(1,3)|oo(8,10)|cc(12,14), oo(3,8)], false],
|
125
|
+
[[oo(1,3)|oo(8,10)|cc(12,14), cc(3,8)], false],
|
126
|
+
[[oc(1,3)|oo(8,10)|cc(12,14), cc(3,8)], true],
|
127
|
+
[[oc(1,3)|oo(8,10)|cc(12,14), co(3,8)], true],
|
128
|
+
[[oo(1,3)|co(8,10)|cc(12,14), cc(3,8)], true],
|
129
|
+
[[oo(1,3)|co(8,10)|cc(12,14), oc(3,8)], true],
|
130
|
+
]
|
131
|
+
examples += interval_for_each_boundary_type(1,3).flat_map{|interval|
|
132
|
+
[
|
133
|
+
[[interval, pt(2)], true],
|
134
|
+
[[interval, pt(4)], false],
|
135
|
+
]
|
136
|
+
}
|
137
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, 1,3, true)
|
138
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, 1,2, true)
|
139
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, 2,3, true)
|
140
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, 1.5,2.5, true)
|
141
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, 2,4, true)
|
142
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, 0,2, true)
|
143
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, 0,4, true)
|
144
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, 0,4, true)
|
145
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, 4,5, false)
|
146
|
+
examples += pairs_of_intervals_for_each_boundary_with_answer(1,3, -1,0, false)
|
147
|
+
|
148
|
+
examples += [
|
149
|
+
[[oo(1,3), oo(3,4)], false],
|
150
|
+
[[oo(1,3), oc(3,4)], false],
|
151
|
+
[[oo(1,3), co(3,4)], false],
|
152
|
+
[[oo(1,3), cc(3,4)], false],
|
153
|
+
[[oo(1,3), oo(0,1)], false],
|
154
|
+
[[oo(1,3), oc(0,1)], false],
|
155
|
+
[[oo(1,3), co(0,1)], false],
|
156
|
+
[[oo(1,3), cc(0,1)], false],
|
157
|
+
[[oo(1,3), pt(3)], false],
|
158
|
+
[[oo(1,3), pt(1)], false],
|
159
|
+
|
160
|
+
[[co(1,3), oo(3,4)], false],
|
161
|
+
[[co(1,3), oc(3,4)], false],
|
162
|
+
[[co(1,3), co(3,4)], false],
|
163
|
+
[[co(1,3), cc(3,4)], false],
|
164
|
+
[[co(1,3), oo(0,1)], false],
|
165
|
+
[[co(1,3), oc(0,1)], true],
|
166
|
+
[[co(1,3), co(0,1)], false],
|
167
|
+
[[co(1,3), cc(0,1)], true],
|
168
|
+
[[co(1,3), pt(1)], true],
|
169
|
+
[[co(1,3), pt(3)], false],
|
170
|
+
|
171
|
+
[[oc(1,3), oo(3,4)], false],
|
172
|
+
[[oc(1,3), oc(3,4)], false],
|
173
|
+
[[oc(1,3), co(3,4)], true],
|
174
|
+
[[oc(1,3), cc(3,4)], true],
|
175
|
+
[[oc(1,3), oo(0,1)], false],
|
176
|
+
[[oc(1,3), oc(0,1)], false],
|
177
|
+
[[oc(1,3), co(0,1)], false],
|
178
|
+
[[oc(1,3), cc(0,1)], false],
|
179
|
+
[[oc(1,3), pt(1)], false],
|
180
|
+
[[oc(1,3), pt(3)], true],
|
181
|
+
|
182
|
+
[[cc(1,3), oo(3,4)], false],
|
183
|
+
[[cc(1,3), oc(3,4)], false],
|
184
|
+
[[cc(1,3), co(3,4)], true],
|
185
|
+
[[cc(1,3), cc(3,4)], true],
|
186
|
+
[[cc(1,3), oo(0,1)], false],
|
187
|
+
[[cc(1,3), oc(0,1)], true],
|
188
|
+
[[cc(1,3), co(0,1)], false],
|
189
|
+
[[cc(1,3), cc(0,1)], true],
|
190
|
+
[[cc(1,3), pt(1)], true],
|
191
|
+
[[cc(1,3), pt(3)], true],
|
192
|
+
|
193
|
+
|
194
|
+
[[oo(1,3), oo(4,5)], false],
|
195
|
+
[[cc(1,3), cc(4,5)], false],
|
196
|
+
[[cc(1,3), pt(2)|cc(4,5)], true],
|
197
|
+
[[co(1,3), pt(2)|cc(4,5)], true],
|
198
|
+
[[cc(1,3), pt(3)|cc(4,5)], true],
|
199
|
+
[[co(1,3), pt(3)|cc(4,5)], false],
|
200
|
+
[[cc(1,3), cc(4,5)|pt(6)], false],
|
201
|
+
]
|
202
|
+
|
203
|
+
# Enable to add extensive tests for #intersect? testing
|
204
|
+
examples = examples.flat_map{|(interval_1,interval_2), ans|
|
205
|
+
[
|
206
|
+
[[interval_1, interval_2], ans],
|
207
|
+
[[interval_1 | pt(100), interval_2], ans],
|
208
|
+
[[interval_1, interval_2 | pt(-100)], ans],
|
209
|
+
[[interval_1 | pt(100), interval_2 | pt(-100)], ans],
|
210
|
+
] +
|
211
|
+
interval_for_each_boundary_type(-150, -100).flat_map {|interval_3| # distant intervals doesn't interfere intersection property
|
212
|
+
[
|
213
|
+
[[interval_1, interval_2 | interval_3], ans],
|
214
|
+
[[interval_1 | interval_3, interval_2], ans]
|
215
|
+
]
|
216
|
+
} +
|
217
|
+
interval_for_each_boundary_type(100, 150).flat_map {|interval_3|
|
218
|
+
[
|
219
|
+
[[interval_1, interval_2 | interval_3], ans],
|
220
|
+
[[interval_1 | interval_3, interval_2], ans]
|
221
|
+
]
|
222
|
+
} +
|
223
|
+
interval_for_each_boundary_type(-150, -100).flat_map {|interval_3|
|
224
|
+
interval_for_each_boundary_type(100, 150).flat_map {|interval_4|
|
225
|
+
[
|
226
|
+
[[interval_1, interval_2|interval_3|interval_4], ans],
|
227
|
+
[[interval_1|interval_3|interval_4, interval_2], ans],
|
228
|
+
[[interval_1|interval_3, interval_2|interval_4], ans],
|
229
|
+
[[interval_1|interval_4, interval_2|interval_3], ans],
|
230
|
+
]
|
231
|
+
}
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
examples += [
|
236
|
+
[[lt(10), oo(1,3)], true],
|
237
|
+
[[lt(10), oo(11,12)], false],
|
238
|
+
[[lt(10), oo(10,11)], false],
|
239
|
+
[[le(10), co(10,11)], true],
|
240
|
+
[[le(10), le(9)], true],
|
241
|
+
[[le(10), le(11)], true],
|
242
|
+
[[le(10), ge(11)], false],
|
243
|
+
[[le(10), ge(10)], true],
|
244
|
+
[[le(10), gt(10)], false],
|
245
|
+
[[le(10), gt(11)], false],
|
246
|
+
]
|
247
|
+
examples += examples.flat_map{|(interval_1, interval_2), ans|
|
248
|
+
[interval_1, interval_2]
|
249
|
+
}.uniq.flat_map{|interval|
|
250
|
+
[
|
251
|
+
[[interval, Empty], false],
|
252
|
+
[[interval, R], interval != Empty],
|
253
|
+
]
|
254
|
+
}
|
255
|
+
|
256
|
+
examples.each do |(interval_1, interval_2), answer|
|
257
|
+
if answer
|
258
|
+
it "#{interval_1}.intersect?(#{interval_2}) should be truthy" do
|
259
|
+
expect( interval_1.intersect?(interval_2) ).to be_truthy
|
260
|
+
end
|
261
|
+
it "#{interval_2}.intersect?(#{interval_1}) should be truthy" do
|
262
|
+
expect( interval_2.intersect?(interval_1) ).to be_truthy
|
263
|
+
end
|
264
|
+
else
|
265
|
+
it "#{interval_1}.intersect?(#{interval_2}) should be falsy" do
|
266
|
+
expect( interval_1.intersect?(interval_2) ).to be_falsy
|
267
|
+
end
|
268
|
+
it "#{interval_2}.intersect?(#{interval_1}) should be falsy" do
|
269
|
+
expect( interval_2.intersect?(interval_1) ).to be_falsy
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
@@ -1,21 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
include IntervalNotation
|
4
|
-
include IntervalNotation::BasicIntervals
|
5
|
-
include IntervalNotation::Syntax::Short
|
6
|
-
|
7
|
-
def each_combination_of_intervals(intervals)
|
8
|
-
basic_intervals = intervals.flat_map(&:intervals)
|
9
|
-
(1..basic_intervals.size / 2).each do |chunk_1_size|
|
10
|
-
indices = basic_intervals.size.times.to_a
|
11
|
-
indices.combination(chunk_1_size).each do |chunk_1_indices|
|
12
|
-
chunk_2_indices = indices - chunk_1_indices
|
13
|
-
chunk_1 = IntervalNotation::Operations.union(chunk_1_indices.map{|i| IntervalSet.new([basic_intervals[i]]) })
|
14
|
-
chunk_2 = IntervalNotation::Operations.union(chunk_2_indices.map{|i| IntervalSet.new([basic_intervals[i]]) })
|
15
|
-
yield chunk_1, chunk_2
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
1
|
+
require_relative 'spec_helper'
|
19
2
|
|
20
3
|
describe IntervalNotation do
|
21
4
|
describe IntervalSet do
|
@@ -53,14 +36,14 @@ describe IntervalNotation do
|
|
53
36
|
[OpenOpenInterval.new(-Float::INFINITY, 1), Point.new(0)],
|
54
37
|
[OpenOpenInterval.new(-Float::INFINITY, 1), Point.new(1)],
|
55
38
|
[Point.new(2), OpenOpenInterval.new(-Float::INFINITY, 1)],
|
56
|
-
|
39
|
+
|
57
40
|
[Point.new(11), OpenOpenInterval.new(10, Float::INFINITY)],
|
58
41
|
[Point.new(10), OpenOpenInterval.new(10, Float::INFINITY)],
|
59
42
|
[OpenOpenInterval.new(10, Float::INFINITY), Point.new(11)],
|
60
43
|
[OpenOpenInterval.new(10, Float::INFINITY), Point.new(9)],
|
61
44
|
[OpenOpenInterval.new(10, Float::INFINITY), OpenOpenInterval.new(9,13)],
|
62
45
|
[OpenOpenInterval.new(10, Float::INFINITY), OpenOpenInterval.new(11,13)],
|
63
|
-
|
46
|
+
|
64
47
|
[OpenOpenInterval.new(1,3), Point.new(3)],
|
65
48
|
[OpenOpenInterval.new(1,3), ClosedOpenInterval.new(3,4)],
|
66
49
|
[OpenClosedInterval.new(1,3), ClosedOpenInterval.new(3,4)],
|
@@ -201,7 +184,7 @@ describe IntervalNotation do
|
|
201
184
|
[oo(1,3), oo(4,5)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(4,5)]),
|
202
185
|
[oo(3,5), oo(1,3)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5)]),
|
203
186
|
[oo(4,5), oo(1,3)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(4,5)]),
|
204
|
-
|
187
|
+
|
205
188
|
[oo(1,3), oo(3,5), cc(7,9)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), ClosedClosedInterval.new(7,9)]),
|
206
189
|
[oo(1,3), oo(3,5), oo(5,7)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), OpenOpenInterval.new(5,7)]),
|
207
190
|
[oo(1,3), cc(7,9), oo(3,5)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), ClosedClosedInterval.new(7,9)]),
|
@@ -259,13 +242,13 @@ describe IntervalNotation do
|
|
259
242
|
[ge(3), co(1,3)] => ge(1),
|
260
243
|
[ge(3), oc(1,3)] => gt(1),
|
261
244
|
[ge(3), cc(1,3)] => ge(1),
|
262
|
-
|
245
|
+
|
263
246
|
[ge(3), oo(2,5)] => gt(2),
|
264
247
|
[ge(3), co(2,5)] => ge(2),
|
265
248
|
[ge(3), co(4,5)] => ge(3),
|
266
249
|
[ge(3), oo(4,5)] => ge(3),
|
267
250
|
[ge(3), oo(3,5)] => ge(3),
|
268
|
-
[ge(3), co(3,5)] => ge(3),
|
251
|
+
[ge(3), co(3,5)] => ge(3),
|
269
252
|
[ge(3), pt(2)] => IntervalSet.new([Point.new(2), ClosedOpenInterval.new(3, Float::INFINITY)]),
|
270
253
|
[ge(3), pt(3)] => ge(3),
|
271
254
|
[ge(3), pt(4)] => ge(3),
|
@@ -314,7 +297,7 @@ describe IntervalNotation do
|
|
314
297
|
}).merge({ # too long hash to be one hash
|
315
298
|
# non-adjacent
|
316
299
|
[oo(1,3), oo(5,6)] => IntervalSet.new([OpenOpenInterval.new(1, 3), OpenOpenInterval.new(5, 6)]),
|
317
|
-
|
300
|
+
|
318
301
|
# adjacent
|
319
302
|
[oo(1,3), oo(3,6)] => IntervalSet.new([OpenOpenInterval.new(1, 3), OpenOpenInterval.new(3, 6)]),
|
320
303
|
[oo(1,3), co(3,6)] => oo(1,6),
|
@@ -420,97 +403,6 @@ describe IntervalNotation do
|
|
420
403
|
end
|
421
404
|
end
|
422
405
|
|
423
|
-
|
424
|
-
describe '#intersection' do
|
425
|
-
{
|
426
|
-
[oo(1,3), oo(1,3)] => oo(1,3),
|
427
|
-
[oo(1,3), oc(1,3)] => oo(1,3),
|
428
|
-
[oo(1,3), co(1,3)] => oo(1,3),
|
429
|
-
[oo(1,3), cc(1,3)] => oo(1,3),
|
430
|
-
|
431
|
-
[pt(2), pt(3)] => Empty,
|
432
|
-
[pt(3), pt(3)] => pt(3),
|
433
|
-
[pt(3)|pt(5), pt(3)|pt(5)] => pt(3)|pt(5),
|
434
|
-
[pt(3)|pt(5), pt(3)|pt(7)] => pt(3),
|
435
|
-
|
436
|
-
[oo(1,3)|oo(5,7), oo(1,3)|oo(5,7)] => oo(1,3)|oo(5,7),
|
437
|
-
[oo(1,3)|co(5,7), oo(1,3)|oo(5,7)] => oo(1,3)|oo(5,7),
|
438
|
-
[oo(1,3)|oo(5,7), oo(1,3)|co(5,7)] => oo(1,3)|oo(5,7),
|
439
|
-
[oo(1,3)|co(5,7), oo(1,3)|co(5,7)] => oo(1,3)|co(5,7),
|
440
|
-
|
441
|
-
[oo(1,3)|oo(3,5), oo(1,3)] => oo(1,3),
|
442
|
-
[oo(1,3)|oo(3,5), oo(1,3)|oo(7,9)] => oo(1,3),
|
443
|
-
[oo(1,3)|oo(3,5), oo(3,5)] => oo(3,5),
|
444
|
-
[oo(1,3)|oo(3,5), oo(1,3)|oo(3,5)] => oo(1,3)|oo(3,5),
|
445
|
-
|
446
|
-
[oo(1,3), oo(4,5)] => Empty,
|
447
|
-
[oo(1,3), oo(3,5)] => Empty,
|
448
|
-
[oo(1,3), co(3,5)] => Empty,
|
449
|
-
[oc(1,3), oo(3,5)] => Empty,
|
450
|
-
[oc(1,3), co(3,5)] => pt(3),
|
451
|
-
|
452
|
-
[oo(1,3), oo(0,5)] => oo(1,3),
|
453
|
-
[oc(1,3), oo(0,5)] => oc(1,3),
|
454
|
-
[co(1,3), oo(0,5)] => co(1,3),
|
455
|
-
[cc(1,3), oo(0,5)] => cc(1,3),
|
456
|
-
|
457
|
-
[oo(1,3), oo(1,5)] => oo(1,3),
|
458
|
-
[co(1,3), oo(1,5)] => oo(1,3),
|
459
|
-
[oc(1,3), oo(1,5)] => oc(1,3),
|
460
|
-
[cc(1,3), oo(1,5)] => oc(1,3),
|
461
|
-
[co(3,5), oo(1,5)] => co(3,5),
|
462
|
-
|
463
|
-
[oo(1,3), oo(2,5)] => oo(2,3),
|
464
|
-
[oc(1,3), oo(2,5)] => oc(2,3),
|
465
|
-
[oo(1,3), co(2,5)] => co(2,3),
|
466
|
-
[oc(1,3), co(2,5)] => cc(2,3),
|
467
|
-
|
468
|
-
[oo(1,3), pt(2)] => pt(2),
|
469
|
-
[oo(1,3), pt(3)] => Empty,
|
470
|
-
[oc(1,3), pt(3)] => pt(3),
|
471
|
-
[oo(1,3), pt(4)] => Empty,
|
472
|
-
[oc(1,3), pt(4)] => Empty,
|
473
|
-
|
474
|
-
[oo(1,3)|oo(3,5), oo(2,3)|oo(3,7)] => oo(2,3)|oo(3,5),
|
475
|
-
[oo(1,3)|oo(3,5), oo(2,7)] => oo(2,3)|oo(3,5),
|
476
|
-
|
477
|
-
[oo(2,6), oo(1,3)|co(5,7)] => oo(2,3)|co(5,6),
|
478
|
-
[oo(1,6), oo(1,3)|co(5,7)] => oo(1,3)|co(5,6),
|
479
|
-
[oo(0,6), oo(1,3)|co(5,7)] => oo(1,3)|co(5,6),
|
480
|
-
[oo(0,6), oo(1,3)|co(5,6)] => oo(1,3)|co(5,6),
|
481
|
-
|
482
|
-
|
483
|
-
}.each do |intervals, answer|
|
484
|
-
it "IntervalNotation::Operations.intersection(#{intervals.map(&:to_s).join(',')} should equal #{answer}" do
|
485
|
-
expect( IntervalNotation::Operations.intersection(intervals) ).to eq answer
|
486
|
-
end
|
487
|
-
|
488
|
-
it "IntervalNotation::Operations.intersection(#{intervals.map(&:to_s).join(',')} should equal consequent unite: #{intervals.map(&:to_s).join('&')}" do
|
489
|
-
expect( IntervalNotation::Operations.intersection(intervals) ).to eq intervals.inject(&:intersection)
|
490
|
-
end
|
491
|
-
|
492
|
-
if intervals.size == 2
|
493
|
-
interval_1, interval_2 = intervals
|
494
|
-
it "#{interval_1} & #{interval_2} should equal #{answer}" do
|
495
|
-
expect( interval_1.intersection(interval_2) ).to eq answer
|
496
|
-
end
|
497
|
-
end
|
498
|
-
end
|
499
|
-
|
500
|
-
{
|
501
|
-
[oo(1,3), oo(1,3), oo(1,3)] => oo(1,3),
|
502
|
-
[oo(1,3), cc(1,3), oo(1,3)] => oo(1,3),
|
503
|
-
[oo(1,3), cc(0,5), cc(1,3)] => oo(1,3),
|
504
|
-
[cc(1,5), cc(3,7), cc(1,3)|cc(5,7)] => pt(3)|pt(5),
|
505
|
-
[oo(1,5), oo(3,7), oo(1,3)|oo(5,7)] => Empty,
|
506
|
-
[oo(1,5), oc(1,3), co(3,5)] => pt(3)
|
507
|
-
}.each do |intervals, answer|
|
508
|
-
it "#{intervals.map(&:to_s).join('&')} should equal #{answer}" do
|
509
|
-
expect( IntervalNotation::Operations.intersection(intervals) ).to eq answer
|
510
|
-
end
|
511
|
-
end
|
512
|
-
end
|
513
|
-
|
514
406
|
describe '#subtract' do
|
515
407
|
{
|
516
408
|
[oo(1,5)|oo(6,8),(oo(1,5))] => oo(6,8),
|
@@ -663,7 +555,7 @@ describe IntervalNotation do
|
|
663
555
|
end
|
664
556
|
end
|
665
557
|
|
666
|
-
describe '#contain? / contained_by?' do
|
558
|
+
describe '#contain? / contained_by?' do
|
667
559
|
{
|
668
560
|
[oo(1,3), oo(1,2)] => true,
|
669
561
|
[oo(1,3), oo(1.5,2.5)] => true,
|
@@ -700,53 +592,13 @@ describe IntervalNotation do
|
|
700
592
|
end
|
701
593
|
end
|
702
594
|
|
703
|
-
describe '#intersect?' do
|
704
|
-
{
|
705
|
-
[oo(1,3), oo(1,2)] => true,
|
706
|
-
[oo(1,3), oo(1,3)] => true,
|
707
|
-
[oo(1,3), cc(1.5,2.5)] => true,
|
708
|
-
|
709
|
-
[oo(1,3), oo(3,4)] => false,
|
710
|
-
[oc(1,3), oo(3,4)] => false,
|
711
|
-
[oo(1,3), co(3,4)] => false,
|
712
|
-
[oc(1,3), co(3,4)] => true,
|
713
|
-
|
714
|
-
[oo(1,3), oo(4,5)] => false,
|
715
|
-
[cc(1,3), cc(4,5)] => false,
|
716
|
-
[cc(1,3), pt(2)|cc(4,5)] => true,
|
717
|
-
[co(1,3), pt(2)|cc(4,5)] => true,
|
718
|
-
[cc(1,3), pt(3)|cc(4,5)] => true,
|
719
|
-
[co(1,3), pt(3)|cc(4,5)] => false,
|
720
|
-
[cc(1,3), cc(4,5)|pt(6)] => false,
|
721
595
|
|
722
|
-
[lt(10), oo(1,3)] => true,
|
723
|
-
[lt(10), oo(11,12)] => false,
|
724
|
-
[lt(10), oo(10,11)] => false,
|
725
|
-
[le(10), co(10,11)] => true,
|
726
|
-
[le(10), le(9)] => true,
|
727
|
-
[le(10), le(11)] => true,
|
728
|
-
[le(10), ge(11)] => false,
|
729
|
-
[le(10), ge(10)] => true,
|
730
|
-
[le(10), gt(10)] => false,
|
731
|
-
[le(10), gt(11)] => false,
|
732
|
-
}.each do |(interval_1, interval_2), answer|
|
733
|
-
if answer
|
734
|
-
it "#{interval_1}.intersect?(#{interval_2}) should be truthy" do
|
735
|
-
expect( interval_1.intersect?(interval_2) ).to be_truthy
|
736
|
-
end
|
737
|
-
else
|
738
|
-
it "#{interval_1}.intersect?(#{interval_2}) should be falsy" do
|
739
|
-
expect( interval_1.intersect?(interval_2) ).to be_falsy
|
740
|
-
end
|
741
|
-
end
|
742
|
-
end
|
743
|
-
end
|
744
596
|
|
745
597
|
describe '#contiguous?' do
|
746
598
|
it 'Empty interval is treated as contiguous' do
|
747
599
|
expect(Empty).to be_contiguous
|
748
600
|
end
|
749
|
-
|
601
|
+
|
750
602
|
it 'Single component intervals are treated as contiguous' do
|
751
603
|
expect(R).to be_contiguous
|
752
604
|
expect(oo(1,3)).to be_contiguous
|
data/spec/spec_helper.rb
CHANGED
@@ -1 +1,36 @@
|
|
1
1
|
require 'rspec'
|
2
|
+
|
3
|
+
require_relative '../lib/interval_notation'
|
4
|
+
|
5
|
+
include IntervalNotation
|
6
|
+
include IntervalNotation::BasicIntervals
|
7
|
+
include IntervalNotation::Syntax::Short
|
8
|
+
|
9
|
+
def interval_for_each_boundary_type(from, to)
|
10
|
+
return enum_for(:interval_for_each_boundary_type, from, to) unless block_given?
|
11
|
+
[true, false].product([true,false]).each do |include_from, include_to|
|
12
|
+
yield BasicIntervals.interval_by_boundary_inclusion(include_from, from, include_to, to).to_interval_set
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def pairs_of_intervals_for_each_boundary_with_answer(from_1, to_1, from_2, to_2, answer)
|
18
|
+
interval_for_each_boundary_type(from_1, to_1).flat_map { |interval_1|
|
19
|
+
interval_for_each_boundary_type(from_2, to_2).map { |interval_2|
|
20
|
+
[[interval_1, interval_2], answer]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def each_combination_of_intervals(intervals)
|
26
|
+
basic_intervals = intervals.flat_map(&:intervals)
|
27
|
+
(1..basic_intervals.size / 2).each do |chunk_1_size|
|
28
|
+
indices = basic_intervals.size.times.to_a
|
29
|
+
indices.combination(chunk_1_size).each do |chunk_1_indices|
|
30
|
+
chunk_2_indices = indices - chunk_1_indices
|
31
|
+
chunk_1 = IntervalNotation::Operations.union(chunk_1_indices.map{|i| IntervalSet.new([basic_intervals[i]]) })
|
32
|
+
chunk_2 = IntervalNotation::Operations.union(chunk_2_indices.map{|i| IntervalSet.new([basic_intervals[i]]) })
|
33
|
+
yield chunk_1, chunk_2
|
34
|
+
end
|
35
|
+
end
|
36
|
+
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.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Vorontsov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- README.md
|
53
53
|
- Rakefile
|
54
54
|
- TODO.md
|
55
|
+
- benchmark/benchmark.rb
|
55
56
|
- interval_notation.gemspec
|
56
57
|
- lib/interval_notation.rb
|
57
58
|
- lib/interval_notation/basic_intervals.rb
|
@@ -61,6 +62,7 @@ files:
|
|
61
62
|
- lib/interval_notation/operations.rb
|
62
63
|
- lib/interval_notation/version.rb
|
63
64
|
- spec/basic_intervals_spec.rb
|
65
|
+
- spec/intersection_spec.rb
|
64
66
|
- spec/interval_notation_spec.rb
|
65
67
|
- spec/spec_helper.rb
|
66
68
|
homepage: https://github.com/prijutme4ty/interval_notation
|
@@ -89,5 +91,6 @@ specification_version: 4
|
|
89
91
|
summary: interval_notation allows one to work with 1D-intervals.
|
90
92
|
test_files:
|
91
93
|
- spec/basic_intervals_spec.rb
|
94
|
+
- spec/intersection_spec.rb
|
92
95
|
- spec/interval_notation_spec.rb
|
93
96
|
- spec/spec_helper.rb
|