interval_notation 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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