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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a937def556100b39f5ceda9350746e0bd4d8ecb7
4
- data.tar.gz: 41f639a8c41a16cbecb65dce7bfa8862429196da
3
+ metadata.gz: 4160164096cbfcc4f171ce43a2c460fb1d3df70c
4
+ data.tar.gz: 3b4f604be240d5d714bd1eacfc3b158c41de7287
5
5
  SHA512:
6
- metadata.gz: ef63264b1222cc9014f420b3dcf35528dec369db7d2515ac6ff7fa66e96714ec1b091e51a8d7c77c5dd9052b2856d6332adcf92c9a117f71bd67bb1dcbb7ac27
7
- data.tar.gz: f453f8d84ed5900c06713ede45ef045e0fe562ab8e469e4522db0be2daedb4aad1c593dd1f3970d5cdc40793942ea40ef412a809bd325a3f1e27e95894f2b566
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 >= value }
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
- # TODO: optimize if possible.
73
- # Checks whether an interval set intersects another interval set. Alias: +#intersect?+
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
- ! intersection(other).empty?
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
 
@@ -1,3 +1,3 @@
1
1
  module IntervalNotation
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -1,4 +1,4 @@
1
- require 'interval_notation'
1
+ require_relative '../lib/interval_notation'
2
2
 
3
3
  include IntervalNotation
4
4
  include IntervalNotation::BasicIntervals
@@ -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
- require 'interval_notation'
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.0
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-25 00:00:00.000000000 Z
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