interval_notation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,881 @@
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
19
+
20
+ describe IntervalNotation do
21
+ describe IntervalSet do
22
+ describe '.new' do
23
+ [ [OpenOpenInterval.new(1,3)],
24
+ [ClosedOpenInterval.new(1,3)],
25
+ [OpenClosedInterval.new(1,3)],
26
+ [ClosedClosedInterval.new(1,3)],
27
+ [Point.new(3)],
28
+ [OpenOpenInterval.new(-Float::INFINITY, 1)],
29
+ [OpenOpenInterval.new(-Float::INFINITY, 1), OpenOpenInterval.new(2,3)],
30
+ [OpenOpenInterval.new(10, Float::INFINITY)],
31
+ [OpenOpenInterval.new(2,3), OpenOpenInterval.new(10, Float::INFINITY)],
32
+ [OpenOpenInterval.new(1,3), Point.new(4)],
33
+ [OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,6)],
34
+ [OpenOpenInterval.new(1,3), OpenOpenInterval.new(4,6)],
35
+ [OpenOpenInterval.new(1,3), ClosedOpenInterval.new(4,6)],
36
+ [OpenClosedInterval.new(1,3), OpenOpenInterval.new(4,6)],
37
+ [OpenClosedInterval.new(1,3), ClosedOpenInterval.new(4,6)],
38
+ [ClosedClosedInterval.new(1,3), ClosedClosedInterval.new(4,6)],
39
+ [OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,6), ClosedClosedInterval.new(7,10), Point.new(11)]
40
+ ].each do |intervals|
41
+ it("IntervalSet.new(#{intervals.map(&:to_s).join(',')}) should not fail") do
42
+ expect{ IntervalSet.new(intervals) }.not_to raise_error
43
+ end
44
+ end
45
+
46
+ [ [OpenOpenInterval.new(3,6), OpenOpenInterval.new(1,3)],
47
+ [OpenOpenInterval.new(1,3), Point.new(1)],
48
+ [OpenOpenInterval.new(1,3), Point.new(0)],
49
+
50
+ [OpenOpenInterval.new(-Float::INFINITY, 1), OpenOpenInterval.new(0,3)],
51
+ [OpenOpenInterval.new(-Float::INFINITY, 1), OpenOpenInterval.new(-1,0)],
52
+ [OpenOpenInterval.new(-Float::INFINITY, 1), OpenOpenInterval.new(-1,1)],
53
+ [OpenOpenInterval.new(-Float::INFINITY, 1), Point.new(0)],
54
+ [OpenOpenInterval.new(-Float::INFINITY, 1), Point.new(1)],
55
+ [Point.new(2), OpenOpenInterval.new(-Float::INFINITY, 1)],
56
+
57
+ [Point.new(11), OpenOpenInterval.new(10, Float::INFINITY)],
58
+ [Point.new(10), OpenOpenInterval.new(10, Float::INFINITY)],
59
+ [OpenOpenInterval.new(10, Float::INFINITY), Point.new(11)],
60
+ [OpenOpenInterval.new(10, Float::INFINITY), Point.new(9)],
61
+ [OpenOpenInterval.new(10, Float::INFINITY), OpenOpenInterval.new(9,13)],
62
+ [OpenOpenInterval.new(10, Float::INFINITY), OpenOpenInterval.new(11,13)],
63
+
64
+ [OpenOpenInterval.new(1,3), Point.new(3)],
65
+ [OpenOpenInterval.new(1,3), ClosedOpenInterval.new(3,4)],
66
+ [OpenClosedInterval.new(1,3), ClosedOpenInterval.new(3,4)],
67
+ [OpenClosedInterval.new(1,3), OpenOpenInterval.new(3,4)],
68
+ [OpenOpenInterval.new(1,4), OpenOpenInterval.new(1,4)],
69
+ [OpenOpenInterval.new(1,4), OpenOpenInterval.new(2,3)],
70
+ [OpenOpenInterval.new(1,4), OpenOpenInterval.new(2,5)]
71
+ ].each do |intervals|
72
+ it("IntervalSet.new(#{intervals.map(&:to_s).join(',')}) should fail") do
73
+ expect{ IntervalSet.new(intervals) }.to raise_error(Error)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ describe '.to_s' do
80
+ {
81
+ Empty => '∅',
82
+ R => '(-∞;+∞)',
83
+ oo(1,3) => '(1;3)',
84
+ oc(1,3) => '(1;3]',
85
+ co(1,3) => '[1;3)',
86
+ cc(1,3) => '[1;3]',
87
+ lt(3) => '(-∞;3)',
88
+ le(3) => '(-∞;3]',
89
+ gt(3) => '(3;+∞)',
90
+ ge(3) => '[3;+∞)',
91
+ pt(2) => '{2}',
92
+ oo(1,3) | cc(4,5) => '(1;3)∪[4;5]',
93
+ oo(1,3) | pt(4) => '(1;3)∪{4}',
94
+ pt(-1) | oo(1,3) | pt(4) => '{-1}∪(1;3)∪{4}',
95
+ pt(1) | pt(4) => '{1}∪{4}',
96
+ }.each do |interval, str|
97
+ it "String representation #{interval.to_s} should eq #{str}" do
98
+ expect(interval.to_s).to eq str
99
+ end
100
+ it "Interval created by string representation #{interval.to_s} is the same as a source interval" do
101
+ expect(IntervalNotation::IntervalSet.from_string(interval.to_s)).to eq interval
102
+ end
103
+ end
104
+ end
105
+
106
+ describe 'IntervalNotation::IntervalSet.from_string' do
107
+ {
108
+ '∅' => Empty,
109
+ 'Empty' => Empty,
110
+ 'empty' => Empty,
111
+ '' => Empty,
112
+ 'R' => R,
113
+ '3' => pt(3),
114
+ '-3' => pt(-3),
115
+ '{3}' => pt(3),
116
+ '{3,5}' => pt(3) | pt(5),
117
+ '{3;5}' => pt(3) | pt(5),
118
+ '{3}u{5}' => pt(3) | pt(5),
119
+ '{3}U{5}' => pt(3) | pt(5),
120
+ '{3}∪{5}' => pt(3) | pt(5),
121
+ '(0,1)' => oo(0,1),
122
+ '(0,1]' => oc(0,1),
123
+ '[0,1)' => co(0,1),
124
+ '[0,1]' => cc(0,1),
125
+ '(0,1)U(2,5.5)' => oo(0,1) | oo(2,5.5),
126
+ '(0;1)U(2;5.5)' => oo(0,1) | oo(2,5.5),
127
+ '(- 2; 5.5)' => oo(-2, 5.5),
128
+ '(-∞; ∞)' => R,
129
+ '(-∞; 1)' => lt(1),
130
+ '(-inf; 1)' => lt(1),
131
+ '(-infty; 1)' => lt(1),
132
+ '(-\infty; 1)' => lt(1),
133
+ '(-infinity; 1)' => lt(1),
134
+ '(1;∞)' => gt(1),
135
+ '(1;inf)' => gt(1),
136
+ '(1;infty)' => gt(1),
137
+ '(1;\infty)' => gt(1),
138
+ '(1;infinity)' => gt(1),
139
+ '(1;+∞)' => gt(1),
140
+ '[1;+∞)' => ge(1),
141
+ '[1;+inf)' => ge(1),
142
+ '[1;+infty)' => ge(1),
143
+ '[1;+\infty)' => ge(1),
144
+ '[1;+infinity)' => ge(1),
145
+ }.each do |str, interval|
146
+ it "IntervalNotation::IntervalSet.from_string from string #{str} should eq #{interval}" do
147
+ expect(IntervalNotation::IntervalSet.from_string(str)).to eq interval
148
+ end
149
+ end
150
+ end
151
+
152
+ describe '.union' do
153
+ [ [Empty],
154
+ [oo(1,3)],
155
+ [Empty, Empty],
156
+ [Empty, oo(1,3)],
157
+ [oo(1,3), Empty],
158
+ [oo(1,3), pt(3)],
159
+ [oo(1,3), pt(4)],
160
+ [oo(1,3), oo(5,6)],
161
+ [oo(1,3), oo(2,6)],
162
+ [oo(1,3), oo(3,6)],
163
+ [oo(1,3), co(3,6)],
164
+ [oc(1,3), oo(3,6)],
165
+ [oc(1,3), co(3,6)],
166
+ [oo(1,3), oo(3,6), oo(7,9)],
167
+ [oc(1,3), co(3,6), oo(7,9)],
168
+ [oo(1,3), pt(3), oo(3,5)],
169
+ [oo(1,3), oo(3,6), oo(7,9), oo(15,19), oc(123,190)],
170
+ [oo(1,3), oo(3,6), oo(7,9), oo(15,19), oc(123,190),cc(4,18)],
171
+ [lt(0),oo(1,3), oo(3,6), oo(7,9), oo(15,19), oc(123,190),cc(4,18),ge(180)],
172
+ ].each do |intervals|
173
+ it "IntervalNotation::Operations.union(#{intervals.map(&:to_s).join(',')}) should be equal to #{intervals.map(&:to_s).join('|')}" do
174
+ expect( IntervalNotation::Operations.union(intervals) ).to eq intervals.inject(&:|)
175
+ end
176
+ end
177
+ end
178
+
179
+ describe '#empty?' do
180
+ {
181
+ Empty => true,
182
+ pt(1) => false,
183
+ pt(1)|pt(3) => false,
184
+ oo(1,5) => false,
185
+ oo(1,5)|pt(7) => false,
186
+ oo(1,3)|oo(3,5) => false,
187
+ oo(1,3)|oo(3,5)|cc(6,7) => false
188
+ }.each do |interval, answer|
189
+ it "#{interval}.empty? should be #{answer}" do
190
+ expect( interval.empty? ).to eq answer
191
+ end
192
+ end
193
+ end
194
+
195
+ describe '#union' do
196
+ {
197
+ # Union of non-overlapping intervals
198
+ [pt(3), pt(5)] => IntervalSet.new([Point.new(3), Point.new(5)]),
199
+ [oo(1,3), pt(5)] => IntervalSet.new([OpenOpenInterval.new(1,3), Point.new(5)]),
200
+ [oo(1,3), oo(3,5)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5)]),
201
+ [oo(1,3), oo(4,5)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(4,5)]),
202
+ [oo(3,5), oo(1,3)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5)]),
203
+ [oo(4,5), oo(1,3)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(4,5)]),
204
+
205
+ [oo(1,3), oo(3,5), cc(7,9)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), ClosedClosedInterval.new(7,9)]),
206
+ [oo(1,3), oo(3,5), oo(5,7)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), OpenOpenInterval.new(5,7)]),
207
+ [oo(1,3), cc(7,9), oo(3,5)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), ClosedClosedInterval.new(7,9)]),
208
+ [oo(1,3), oc(7,9), oo(3,5)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), OpenClosedInterval.new(7,9)]),
209
+ [oo(1,3), co(7,9), oo(3,5)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), ClosedOpenInterval.new(7,9)]),
210
+ [oo(1,3), ge(7), oo(3,5)] => IntervalSet.new([OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), ClosedOpenInterval.new(7,Float::INFINITY)]),
211
+ [oo(1,3), gt(7), oo(3,5),le(0)] => IntervalSet.new([OpenClosedInterval.new(-Float::INFINITY,0), OpenOpenInterval.new(1,3), OpenOpenInterval.new(3,5), OpenOpenInterval.new(7,Float::INFINITY)]),
212
+
213
+ # union with empty interval
214
+ [oo(1,3), Empty] => oo(1,3),
215
+ [oo(1,3)|oo(3,5)|cc(7,9), Empty] => oo(1,3)|oo(3,5)|cc(7,9),
216
+
217
+ # interval united with point
218
+ [oo(1,3), pt(2)] => oo(1,3),
219
+ [oo(1,3), pt(1)] => co(1,3),
220
+ [co(1,3), pt(1)] => co(1,3),
221
+ [oo(1,3), pt(0)] => pt(0)|oo(1,3),
222
+
223
+ # union with almost the same interval
224
+ [pt(3), pt(3)] => pt(3),
225
+ [pt(3)|pt(5), pt(3)|pt(5)] => pt(3)|pt(5),
226
+ [oo(1,3), oo(1,3)] => oo(1,3),
227
+ [oo(1,3), oc(1,3)] => oc(1,3),
228
+ [oo(1,3), co(1,3)] => co(1,3),
229
+ [oo(1,3), cc(1,3)] => cc(1,3),
230
+
231
+ [oo(1,3)|oo(5,7), oo(1,3)|oo(5,7)] => oo(1,3)|oo(5,7),
232
+ [oo(1,3)|co(5,7), oo(1,3)|oo(5,7)] => oo(1,3)|co(5,7),
233
+ [oo(1,3)|oo(5,7), oo(1,3)|co(5,7)] => oo(1,3)|co(5,7),
234
+ [oo(1,3)|co(5,7), oo(1,3)|co(5,7)] => oo(1,3)|co(5,7),
235
+
236
+ [pt(3)|pt(5), pt(3)|pt(7)] => pt(3)|pt(5)|pt(7),
237
+ }.merge({ # too long hash to be one hash
238
+ # infinite intervals
239
+ # gt
240
+ [gt(3), oo(1,2)] => IntervalSet.new([OpenOpenInterval.new(1, 2), OpenOpenInterval.new(3, Float::INFINITY)]),
241
+ [gt(3), oo(1,3)] => IntervalSet.new([OpenOpenInterval.new(1, 3), OpenOpenInterval.new(3, Float::INFINITY)]),
242
+ [gt(3), co(1,3)] => IntervalSet.new([ClosedOpenInterval.new(1, 3), OpenOpenInterval.new(3, Float::INFINITY)]),
243
+ [gt(3), oc(1,3)] => gt(1),
244
+ [gt(3), cc(1,3)] => ge(1),
245
+
246
+ [gt(3), oo(2,5)] => gt(2),
247
+ [gt(3), co(2,5)] => ge(2),
248
+ [gt(3), co(4,5)] => gt(3),
249
+ [gt(3), oo(4,5)] => gt(3),
250
+ [gt(3), oo(3,5)] => gt(3),
251
+ [gt(3), co(3,5)] => ge(3),
252
+ [gt(3), pt(2)] => IntervalSet.new([Point.new(2), OpenOpenInterval.new(3, Float::INFINITY)]),
253
+ [gt(3), pt(3)] => ge(3),
254
+ [gt(3), pt(4)] => gt(3),
255
+
256
+ # ge
257
+ [ge(3), oo(1,2)] => IntervalSet.new([OpenOpenInterval.new(1, 2), ClosedOpenInterval.new(3, Float::INFINITY)]),
258
+ [ge(3), oo(1,3)] => gt(1),
259
+ [ge(3), co(1,3)] => ge(1),
260
+ [ge(3), oc(1,3)] => gt(1),
261
+ [ge(3), cc(1,3)] => ge(1),
262
+
263
+ [ge(3), oo(2,5)] => gt(2),
264
+ [ge(3), co(2,5)] => ge(2),
265
+ [ge(3), co(4,5)] => ge(3),
266
+ [ge(3), oo(4,5)] => ge(3),
267
+ [ge(3), oo(3,5)] => ge(3),
268
+ [ge(3), co(3,5)] => ge(3),
269
+ [ge(3), pt(2)] => IntervalSet.new([Point.new(2), ClosedOpenInterval.new(3, Float::INFINITY)]),
270
+ [ge(3), pt(3)] => ge(3),
271
+ [ge(3), pt(4)] => ge(3),
272
+
273
+ # lt
274
+ [lt(3), oo(4,5)] => IntervalSet.new([OpenOpenInterval.new(-Float::INFINITY, 3), OpenOpenInterval.new(4, 5)]),
275
+ [lt(3), oo(3,5)] => IntervalSet.new([OpenOpenInterval.new(-Float::INFINITY, 3), OpenOpenInterval.new(3, 5)]),
276
+ [lt(3), co(3,5)] => lt(5),
277
+ [lt(3), oc(3,5)] => IntervalSet.new([OpenOpenInterval.new(-Float::INFINITY, 3), OpenClosedInterval.new(3, 5)]),
278
+ [lt(3), cc(3,5)] => le(5),
279
+
280
+ [lt(3), oo(1,2)] => lt(3),
281
+ [lt(3), oo(1,3)] => lt(3),
282
+ [lt(3), oo(1,4)] => lt(4),
283
+ [lt(3), oc(1,2)] => lt(3),
284
+ [lt(3), oc(1,3)] => le(3),
285
+ [lt(3), oc(1,4)] => le(4),
286
+ [lt(3), pt(2)] => lt(3),
287
+ [lt(3), pt(3)] => le(3),
288
+ [lt(3), pt(4)] => IntervalSet.new([OpenOpenInterval.new(-Float::INFINITY, 3), Point.new(4)]),
289
+
290
+ # le
291
+ [le(3), oo(4,5)] => IntervalSet.new([OpenClosedInterval.new(-Float::INFINITY, 3), OpenOpenInterval.new(4, 5)]),
292
+ [le(3), oo(3,5)] => lt(5),
293
+ [le(3), co(3,5)] => lt(5),
294
+ [le(3), oc(3,5)] => le(5),
295
+ [le(3), cc(3,5)] => le(5),
296
+
297
+ [le(3), oo(1,2)] => le(3),
298
+ [le(3), oo(1,3)] => le(3),
299
+ [le(3), oo(1,4)] => lt(4),
300
+ [le(3), oc(1,2)] => le(3),
301
+ [le(3), oc(1,3)] => le(3),
302
+ [le(3), oc(1,4)] => le(4),
303
+ [le(3), pt(2)] => le(3),
304
+ [le(3), pt(3)] => le(3),
305
+ [le(3), pt(4)] => IntervalSet.new([OpenClosedInterval.new(-Float::INFINITY, 3), Point.new(4)]),
306
+
307
+ # both infinite
308
+ [lt(3), gt(4)] => IntervalSet.new([OpenOpenInterval.new(-Float::INFINITY, 3), OpenOpenInterval.new(4, Float::INFINITY)]),
309
+ [lt(3), gt(3)] => IntervalSet.new([OpenOpenInterval.new(-Float::INFINITY, 3), OpenOpenInterval.new(3, Float::INFINITY)]),
310
+ [lt(3), ge(3)] => R,
311
+ [le(3), gt(3)] => R,
312
+ [le(3), ge(3)] => R,
313
+ [lt(3), gt(2)] => R,
314
+ }).merge({ # too long hash to be one hash
315
+ # non-adjacent
316
+ [oo(1,3), oo(5,6)] => IntervalSet.new([OpenOpenInterval.new(1, 3), OpenOpenInterval.new(5, 6)]),
317
+
318
+ # adjacent
319
+ [oo(1,3), oo(3,6)] => IntervalSet.new([OpenOpenInterval.new(1, 3), OpenOpenInterval.new(3, 6)]),
320
+ [oo(1,3), co(3,6)] => oo(1,6),
321
+ [oc(1,3), oo(3,6)] => oo(1,6),
322
+ [oc(1,3), co(3,6)] => oo(1,6),
323
+ [oo(1,3), oo(0,1)] => IntervalSet.new([OpenOpenInterval.new(0, 1), OpenOpenInterval.new(1, 3)]),
324
+ [oo(1,3), oc(0,1)] => oo(0,3),
325
+ [co(1,3), oo(0,1)] => oo(0,3),
326
+ [co(1,3), oc(0,1)] => oo(0,3),
327
+
328
+ # overlapping
329
+ [oo(1,3), oo(2,6)] => oo(1,6),
330
+ [oo(1,3), co(2,6)] => oo(1,6),
331
+ [oc(1,3), oo(2,6)] => oo(1,6),
332
+ [oc(1,3), co(2,6)] => oo(1,6),
333
+ [oo(1,3), oo(0,2)] => oo(0,3),
334
+ [oo(1,3), oc(0,2)] => oo(0,3),
335
+ [co(1,3), oo(0,2)] => oo(0,3),
336
+ [co(1,3), oc(0,2)] => oo(0,3),
337
+
338
+ # inside
339
+ [oo(1,4), oo(1,4)] => oo(1,4),
340
+ [oo(1,4), oo(1,3)] => oo(1,4),
341
+ [oo(1,4), oo(2,4)] => oo(1,4),
342
+
343
+ [oo(1,4), oo(2,3)] => oo(1,4),
344
+ [oo(1,4), co(2,3)] => oo(1,4),
345
+ [oo(1,4), oc(2,3)] => oo(1,4),
346
+ [oo(1,4), cc(2,3)] => oo(1,4),
347
+
348
+
349
+ # almost inside
350
+ [oo(1,4), oc(1,4)] => oc(1,4),
351
+ [oo(1,4), co(1,4)] => co(1,4),
352
+ [oo(1,4), oc(1,4)] => oc(1,4),
353
+ [oo(1,4), cc(1,4)] => cc(1,4),
354
+ [oo(1,4), co(1,3)] => co(1,4),
355
+ [oo(1,4), oc(2,4)] => oc(1,4),
356
+
357
+ # outside and almost outside
358
+ [oo(1,4), oo(0,5)] => oo(0,5),
359
+ [oo(1,4), cc(0,5)] => cc(0,5),
360
+ [oo(1,4), oo(0,4)] => oo(0,4),
361
+ [oo(1,4), oo(1,5)] => oo(1,5),
362
+ [oo(1,4), co(1,5)] => co(1,5),
363
+ [oo(1,4), oc(0,4)] => oc(0,4),
364
+
365
+ # union of interval set with the deleted point with another interval
366
+ [oo(1,3)|oo(3,5), pt(2)] => oo(1,3)|oo(3,5),
367
+ [oo(1,3)|oo(3,5), pt(3)] => oo(1,5),
368
+ [oo(1,3)|oo(3,5), oo(1,3)] => oo(1,3)|oo(3,5),
369
+ [oo(1,3)|oo(3,5), oo(3,5)] => oo(1,3)|oo(3,5),
370
+ [oo(1,3)|oo(3,5), oo(1.5,2.5)] => oo(1,3)|oo(3,5),
371
+ [oo(1,3)|oo(3,5), oo(3,6)] => oo(1,3)|oo(3,6),
372
+ [oo(1,3)|oo(3,5), oo(4,6)] => oo(1,3)|oo(3,6),
373
+ [oo(1,3)|oo(3,5), oo(0,3)] => oo(0,3)|oo(3,5),
374
+ [oo(1,3)|oo(3,5), oo(0,2)] => oo(0,3)|oo(3,5),
375
+ [oo(1,3)|oo(3,5), oo(0,6)] => oo(0,6),
376
+ [oo(1,3)|oo(3,5), oo(1,5)] => oo(1,5),
377
+ [oo(1,3)|oo(3,5), oo(2,4)] => oo(1,5),
378
+ [oo(1,3)|oo(3,5), cc(2,4)] => oo(1,5),
379
+ [oo(1,3)|oo(3,5), oc(1,3)] => oo(1,5),
380
+ [oo(1,3)|oo(3,5), oc(2,3)] => oo(1,5),
381
+ [oo(1,3)|oo(3,5), co(3,4)] => oo(1,5),
382
+ }).merge({ # too long hash to be one hash
383
+ [oo(1,3)|oo(5,7), oo(3,5)] => oo(1,3) | oo(3,5) | oo(5,7),
384
+ [oo(1,3)|oo(5,7), co(3,5)] => oo(1,5) | oo(5,7),
385
+ [oo(1,3)|oo(5,7), oc(3,5)] => oo(1,3) | oo(3,7),
386
+ [oo(1,3)|oo(5,7), cc(3,5)] => oo(1,7),
387
+ [oo(1,3)|oo(5,7)|oo(11,13), oo(3,5)|oo(7,11)] => oo(1,3)|oo(3,5)|oo(5,7)|oo(7,11)|oo(11,13),
388
+ [oo(1,3)|cc(5,7)|oo(11,13), oo(3,5)|oo(7,11)] => oo(1,3)|oo(3,11)|oo(11,13),
389
+
390
+ # each interval is interval set
391
+ [oo(1,3)|oo(3,5), oo(2,3)|oo(3,7)] => oo(1,3)|oo(3,7),
392
+ [oo(1,5), oo(2,3)|oo(3,7)] => (oo(1,7)),
393
+ [oo(1,3)|oo(3,5), oo(2,7)] => (oo(1,7)),
394
+
395
+
396
+ [oo(1,3)|oo(5,7), oo(3,5)] => oo(1,3)|oo(3,5)|oo(5,7),
397
+ [oo(1,3)|oo(5,7), co(3,5)] => oo(1,5)|oo(5,7),
398
+ [oo(1,3)|oo(5,7), cc(3,5)] => oo(1,7),
399
+ }).each do |intervals, answer|
400
+ it "IntervalNotation::Operations.union(#{intervals.map(&:to_s).join(',')} should equal #{answer}" do
401
+ expect( IntervalNotation::Operations.union(intervals) ).to eq answer
402
+ end
403
+
404
+ it "IntervalNotation::Operations.union(#{intervals.map(&:to_s).join(',')} should equal consequent unite: #{intervals.map(&:to_s).join('|')}" do
405
+ expect( IntervalNotation::Operations.union(intervals) ).to eq intervals.inject(&:union)
406
+ end
407
+
408
+ if intervals.size == 2
409
+ interval_1, interval_2 = intervals
410
+ it "#{interval_1} | #{interval_2} should equal #{answer}" do
411
+ expect( interval_1.union(interval_2) ).to eq answer
412
+ end
413
+ end
414
+
415
+ each_combination_of_intervals(intervals) do |chunk_1, chunk_2|
416
+ it "#{chunk_1}.union(#{chunk_2}) should be equal to #{answer}" do
417
+ expect( chunk_1.union(chunk_2) ).to eq answer
418
+ end
419
+ end
420
+ end
421
+ end
422
+
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
+ describe '#subtract' do
515
+ {
516
+ [oo(1,5)|oo(6,8),(oo(1,5))] => oo(6,8),
517
+ [oo(1,5)|oo(6,8),(oo(1,8))] => Empty,
518
+ [oo(1,5)|oo(6,8),(cc(1,5))] => oo(6,8),
519
+ [oo(1,5),cc(2,3)] => oo(1,2)|oo(3,5),
520
+ [oo(1,5),oo(2,3)] => oc(1,2)|co(3,5),
521
+ [oo(1,5),oo(1,3)] => co(3,5),
522
+ [oo(1,5),pt(0)] => oo(1,5),
523
+ [oo(1,5),pt(1)] => oo(1,5),
524
+ [co(1,5),pt(1)] => oo(1,5),
525
+ [oo(1,5),pt(3)] => oo(1,3)|oo(3,5),
526
+ [cc(1,5),oo(1,3)] => pt(1)|cc(3,5),
527
+ [cc(1,5),co(1,3)] => cc(3,5),
528
+ [oo(1,5),cc(1,3)] => oo(3,5),
529
+ [oo(1,5),oo(0,3)] => co(3,5),
530
+ [oo(1,5),oo(0,2)|oo(3,4)] => cc(2,3)|co(4,5),
531
+ [R,oo(1,5)] => le(1)|ge(5),
532
+ [R,oc(1,5)] => le(1)|gt(5),
533
+ [R,co(1,5)] => lt(1)|ge(5),
534
+ [R,pt(3)] => lt(3)|gt(3),
535
+ [R,Empty] => R,
536
+ [R,R] => Empty,
537
+ [oo(1,5),R] => Empty,
538
+ [pt(3),R] => Empty,
539
+ [oo(1,3)|pt(5)|cc(7,10),R] => Empty,
540
+ [Empty,R] => Empty,
541
+ [Empty,pt(3)] => Empty,
542
+ [Empty,oo(1,3)] => Empty,
543
+ }.each do |(interval_1, interval_2), answer|
544
+ it "#{interval_1} - #{interval_2} should equal #{answer}" do
545
+ expect( interval_1.subtract(interval_2) ).to eq answer
546
+ end
547
+ end
548
+ end
549
+
550
+ describe '#symmetric_difference' do
551
+ {
552
+ [oo(1,3),oo(1,3)] => Empty,
553
+ [cc(1,3),cc(1,3)] => Empty,
554
+ [cc(1,3),pt(2)] => co(1,2)|oc(2,3),
555
+ [cc(1,3),oo(1,3)] => pt(1)|pt(3),
556
+ [oo(1,4),oo(2,3)] => oc(1,2)|co(3,4),
557
+ [oo(1,4),cc(2,3)] => oo(1,2)|oo(3,4),
558
+ [oo(1,4),Empty] => oo(1,4),
559
+ [oo(1,4),R] => le(1)|ge(4),
560
+ }.each do |(interval_1, interval_2), answer|
561
+ it "#{interval_1} ^ #{interval_2} should equal #{answer}" do
562
+ expect( interval_1.symmetric_difference(interval_2) ).to eq answer
563
+ end
564
+ it "#{interval_2} ^ #{interval_1} should equal #{answer}" do
565
+ expect( interval_2.symmetric_difference(interval_1) ).to eq answer
566
+ end
567
+ it "#{interval_1} ^ #{answer} should equal #{interval_2}" do
568
+ expect( interval_1.symmetric_difference(answer) ).to eq interval_2
569
+ end
570
+ it "#{interval_2} ^ #{answer} should equal #{interval_1}" do
571
+ expect( interval_2.symmetric_difference(answer) ).to eq interval_1
572
+ end
573
+ end
574
+ end
575
+
576
+ describe '#complement' do
577
+ {
578
+ oo(1,3)|cc(5,6) => le(1)|co(3,5)|gt(6),
579
+ oo(1,5) => le(1)|ge(5),
580
+ oc(1,5) => le(1)|gt(5),
581
+ co(1,5) => lt(1)|ge(5),
582
+ pt(3) => lt(3)|gt(3),
583
+ Empty => R,
584
+ R => Empty,
585
+ }.each do |interval, answer|
586
+ it "#{interval}.complement should equal #{answer}" do
587
+ expect( interval.complement ).to eq answer
588
+ end
589
+ end
590
+ end
591
+
592
+ describe '#include_position?' do
593
+ {
594
+ [oo(1,3), -100] => false,
595
+ [oo(1,3), 0] => false,
596
+ [oo(1,3), 1] => false,
597
+ [oo(1,3), 2] => true,
598
+ [oo(1,3), 3] => false,
599
+ [oo(1,3), 4] => false,
600
+ [oo(1,3), 100] => false,
601
+
602
+ [co(1,3), -100] => false,
603
+ [co(1,3), 0] => false,
604
+ [co(1,3), 1] => true,
605
+ [co(1,3), 2] => true,
606
+ [co(1,3), 3] => false,
607
+ [co(1,3), 4] => false,
608
+ [co(1,3), 100] => false,
609
+
610
+ [oc(1,3), -100] => false,
611
+ [oc(1,3), 0] => false,
612
+ [oc(1,3), 1] => false,
613
+ [oc(1,3), 2] => true,
614
+ [oc(1,3), 3] => true,
615
+ [oc(1,3), 4] => false,
616
+ [oc(1,3), 100] => false,
617
+
618
+ [cc(1,3), -100] => false,
619
+ [cc(1,3), 0] => false,
620
+ [cc(1,3), 1] => true,
621
+ [cc(1,3), 2] => true,
622
+ [cc(1,3), 3] => true,
623
+ [cc(1,3), 4] => false,
624
+ [cc(1,3), 100] => false,
625
+
626
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), -1] => false,
627
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 0] => true,
628
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 1] => true,
629
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 2] => true,
630
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 3] => false,
631
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 4] => true,
632
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 5] => false,
633
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 6] => true,
634
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 7] => false,
635
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 8] => true,
636
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 9] => true,
637
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 10] => true,
638
+ [cc(0,2)|pt(4)|pt(6)|cc(8,10), 11] => false,
639
+
640
+ [lt(-10)|cc(1,3)|ge(10), -1000] => true,
641
+ [lt(-10)|cc(1,3)|ge(10), -11] => true,
642
+ [lt(-10)|cc(1,3)|ge(10), -10] => false,
643
+ [lt(-10)|cc(1,3)|ge(10), -9] => false,
644
+ [lt(-10)|cc(1,3)|ge(10), 0] => false,
645
+ [lt(-10)|cc(1,3)|ge(10), 1] => true,
646
+ [lt(-10)|cc(1,3)|ge(10), 2] => true,
647
+ [lt(-10)|cc(1,3)|ge(10), 3] => true,
648
+ [lt(-10)|cc(1,3)|ge(10), 4] => false,
649
+ [lt(-10)|cc(1,3)|ge(10), 9] => false,
650
+ [lt(-10)|cc(1,3)|ge(10), 10] => true,
651
+ [lt(-10)|cc(1,3)|ge(10), 11] => true,
652
+ [lt(-10)|cc(1,3)|ge(10), 1000] => true,
653
+ }.each do |(interval, point), answer|
654
+ if answer
655
+ it "#{interval}.include_position?(#{point}) should be truthy" do
656
+ expect( interval.include_position?(point) ).to be_truthy
657
+ end
658
+ else
659
+ it "#{interval}.include_position?(#{point}) should be falsy" do
660
+ expect( interval.include_position?(point) ).to be_falsy
661
+ end
662
+ end
663
+ end
664
+ end
665
+
666
+ describe '#contain? / contained_by?' do
667
+ {
668
+ [oo(1,3), oo(1,2)] => true,
669
+ [oo(1,3), oo(1.5,2.5)] => true,
670
+ [oo(1,3), cc(1.5,2.5)] => true,
671
+ [oo(1,3), oo(2,3)] => true,
672
+ [oo(1,3), oo(1,3)] => true,
673
+ [cc(1,3), oo(1,3)] => true,
674
+ [cc(1,3), cc(1,3)] => true,
675
+ [oo(1,3), cc(1,3)] => false,
676
+ [oo(1,3), co(1,3)] => false,
677
+ [oo(1,3)|pt(4)|cc(8,10), oo(1,3)|oc(9,10) ] => true,
678
+ [oo(1,3)|pt(4)|cc(8,10), oo(1,3)|pt(4) ] => true,
679
+ [oo(1,3)|pt(4)|cc(8,10), oo(1,3)|pt(5) ] => false,
680
+ [oo(1,3)|pt(4)|cc(8,10), oo(1,3)|pt(8) ] => true,
681
+ [lt(10), oo(1,3)] => true,
682
+ [lt(10), oo(10,11)] => false,
683
+ [le(10), oo(10,11)] => false,
684
+ }.each do |(interval_1, interval_2), answer|
685
+ if answer
686
+ it "#{interval_1}.contain?(#{interval_2}) should be truthy" do
687
+ expect( interval_1.contain?(interval_2) ).to be_truthy
688
+ end
689
+ it "#{interval_2}.contained_by?(#{interval_1}) should be truthy" do
690
+ expect( interval_2.contained_by?(interval_1) ).to be_truthy
691
+ end
692
+ else
693
+ it "#{interval_1}.contain?(#{interval_2}) should be falsy" do
694
+ expect( interval_1.contain?(interval_2) ).to be_falsy
695
+ end
696
+ it "#{interval_2}.contained_by?(#{interval_1}) should be falsy" do
697
+ expect( interval_2.contained_by?(interval_1) ).to be_falsy
698
+ end
699
+ end
700
+ end
701
+ end
702
+
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
+
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
+
745
+ describe '#contiguous?' do
746
+ it 'Empty interval is treated as contiguous' do
747
+ expect(Empty).to be_contiguous
748
+ end
749
+
750
+ it 'Single component intervals are treated as contiguous' do
751
+ expect(R).to be_contiguous
752
+ expect(oo(1,3)).to be_contiguous
753
+ expect(cc(1,3)).to be_contiguous
754
+ expect(lt(3)).to be_contiguous
755
+ expect(ge(3)).to be_contiguous
756
+ expect(pt(3)).to be_contiguous
757
+ end
758
+
759
+ it 'Several components intervals are treated as contiguous' do
760
+ expect(oo(1,3)|oo(3,5)).not_to be_contiguous
761
+ expect(oo(1,3)|oo(3,5)|oo(10,15)).not_to be_contiguous
762
+ expect(oo(1,3)|pt(5)).not_to be_contiguous
763
+ expect(cc(1,3)|pt(5)).not_to be_contiguous
764
+ expect(cc(1,3)|ge(5)).not_to be_contiguous
765
+ expect(pt(3)|ge(5)).not_to be_contiguous
766
+ expect(lt(3)|ge(5)).not_to be_contiguous
767
+ expect(pt(3)|pt(5)).not_to be_contiguous
768
+ end
769
+ end
770
+
771
+ describe '#num_connected_components' do
772
+ {
773
+ oo(1,3) => 1,
774
+ oc(1,3) => 1,
775
+ co(1,3) => 1,
776
+ cc(1,3) => 1,
777
+ oo(1,3) | oo(3,6) => 2,
778
+ oo(1,3) | oo(5,8) => 2,
779
+ oo(1,3) | oo(3,6) | cc(10,15) => 3,
780
+ oo(1,3) | pt(4) | oo(5,8) => 3,
781
+ Empty => 0,
782
+ pt(3) => 1,
783
+ pt(3) | pt(5) => 2,
784
+ lt(3) => 1,
785
+ le(3) => 1,
786
+ gt(3) => 1,
787
+ ge(3) => 1,
788
+ R => 1,
789
+ }.each do |interval, answer|
790
+ it "#{interval}.num_connected_components should equal #{answer}" do
791
+ expect(interval.num_connected_components).to eq answer
792
+ end
793
+ end
794
+ end
795
+
796
+ describe '#total_length' do
797
+ {
798
+ oo(1,3) => 2,
799
+ oc(1,3) => 2,
800
+ co(1,3) => 2,
801
+ cc(1,3) => 2,
802
+ oo(1,3) | oo(3,6) => 5,
803
+ oo(1,3) | oo(5,8) => 5,
804
+ oo(1,3) | pt(4) | oo(5,8) => 5,
805
+ Empty => 0,
806
+ pt(3) => 0,
807
+ pt(3) | pt(5) => 0,
808
+ lt(3) => Float::INFINITY,
809
+ le(3) => Float::INFINITY,
810
+ gt(3) => Float::INFINITY,
811
+ ge(3) => Float::INFINITY,
812
+ R => Float::INFINITY,
813
+ }.each do |interval, answer|
814
+ it "#{interval}.total_length should equal #{answer}" do
815
+ expect(interval.total_length).to eq answer
816
+ end
817
+ end
818
+ end
819
+
820
+ describe '#covering_interval' do
821
+ {
822
+ Empty => Empty,
823
+ oo(1,3) => oo(1,3),
824
+ oc(1,3) => oc(1,3),
825
+ co(1,3) => co(1,3),
826
+ cc(1,3) => cc(1,3),
827
+ pt(3) => pt(3),
828
+ lt(3) => lt(3),
829
+ le(3) => le(3),
830
+ gt(3) => gt(3),
831
+ ge(3) => ge(3),
832
+ R => R,
833
+ pt(3) | pt(4) => cc(3,4),
834
+ oo(1,3) | pt(4) => oc(1,4),
835
+ pt(0) | oo(1,3) | pt(4) => cc(0,4),
836
+ pt(0) | oc(1,3) => cc(0,3),
837
+ oo(1,3) | oo(3,6) => oo(1,6),
838
+ oo(1,3) | oc(3,6) => oc(1,6),
839
+ oo(1,3) | oc(5,8) => oc(1,8),
840
+ co(1,3) | oc(5,8) => cc(1,8),
841
+ cc(1,3) | cc(5,8) => cc(1,8),
842
+ oo(1,3) | oo(3,6) | cc(10,15) => oc(1,15),
843
+ oo(1,3) | pt(4) | oo(5,8) => oo(1,8),
844
+ }.each do |interval, answer|
845
+ it "#{interval}.covering_interval should equal #{answer}" do
846
+ expect(interval.covering_interval).to eq answer
847
+ end
848
+ end
849
+ end
850
+
851
+ describe '#closure' do
852
+ {
853
+ Empty => Empty,
854
+ oo(1,3) => cc(1,3),
855
+ oc(1,3) => cc(1,3),
856
+ co(1,3) => cc(1,3),
857
+ cc(1,3) => cc(1,3),
858
+ pt(3) => pt(3),
859
+ lt(3) => le(3),
860
+ le(3) => le(3),
861
+ gt(3) => ge(3),
862
+ ge(3) => ge(3),
863
+ lt(3) | gt(3) => R,
864
+ lt(3) | gt(4) => le(3) | ge(4),
865
+ oo(1,3) | oo(4,5) => cc(1,3) | cc(4,5),
866
+ oc(1,3) | co(4,5) => cc(1,3) | cc(4,5),
867
+ R => R,
868
+ oo(1,3) | pt(4) => cc(1,3) | pt(4),
869
+ pt(3) | pt(4) => pt(3) | pt(4),
870
+ oo(1,3) | oo(3,4) => cc(1,4),
871
+ co(1,3) | oc(3,4) => cc(1,4),
872
+ oo(1,3) | oo(3,6) | cc(10,15) => cc(1,6) | cc(10,15),
873
+ oo(1,3) | oo(3,6) | gt(10) => cc(1,6) | ge(10),
874
+ oo(1,3) | pt(4) | oo(5,8) => cc(1,3) | pt(4) | cc(5,8),
875
+ }.each do |interval, answer|
876
+ it "#{interval}.covering_interval should equal #{answer}" do
877
+ expect(interval.closure).to eq answer
878
+ end
879
+ end
880
+ end
881
+ end