interval_notation 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/README.md +163 -0
- data/Rakefile +2 -0
- data/TODO.md +3 -0
- data/interval_notation.gemspec +23 -0
- data/lib/interval_notation/basic_intervals.rb +263 -0
- data/lib/interval_notation/combiners.rb +120 -0
- data/lib/interval_notation/error.rb +3 -0
- data/lib/interval_notation/interval_set.rb +190 -0
- data/lib/interval_notation/operations.rb +62 -0
- data/lib/interval_notation/version.rb +3 -0
- data/lib/interval_notation.rb +117 -0
- data/spec/basic_intervals_spec.rb +140 -0
- data/spec/interval_notation_spec.rb +881 -0
- data/spec/spec_helper.rb +1 -0
- metadata +93 -0
@@ -0,0 +1,190 @@
|
|
1
|
+
require_relative 'basic_intervals'
|
2
|
+
require_relative 'error'
|
3
|
+
require_relative 'operations'
|
4
|
+
|
5
|
+
module IntervalNotation
|
6
|
+
class IntervalSet
|
7
|
+
attr_reader :intervals
|
8
|
+
|
9
|
+
# +IntervalSet.new+ accepts an ordered list of intervals.
|
10
|
+
# Intervals should be sorted from leftmost to rightmost and should not overlap.
|
11
|
+
# It's not recommended to use this constructor directly. Instead take a look at +IntervalNotation::Syntax+ module.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# # don't use this
|
15
|
+
# IntervalSet.new([OpenOpenInterval.new(1,3), Point.new(5)])
|
16
|
+
# # instead use
|
17
|
+
# oo(1,3) | pt(5)
|
18
|
+
|
19
|
+
def initialize(intervals)
|
20
|
+
unless IntervalSet.check_valid?(intervals)
|
21
|
+
raise Error, "IntervalSet.new accepts non-overlapping, sorted regions.\n" +
|
22
|
+
"Try to use IntervalNotation.union(...) to create interval from overlapping or not-sorted intervals"
|
23
|
+
end
|
24
|
+
@intervals = intervals.freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
# Method to create an interval set from string.
|
28
|
+
# It accepts strings obtained by +#to_s+ and many other formats.
|
29
|
+
# Spaces inside are ignored
|
30
|
+
# Intervals can be joined with +u+ or +U+ letters and unicode union character +∪+
|
31
|
+
# Points can go separately or be combined inside of single curly braces. Single point can go without braces at all
|
32
|
+
# +,+ and +;+ are both valid value separators.
|
33
|
+
# Infinity can be represented by +inf+, +infty+, +\infty+, +infinity+, +∞+ (each can go with or without sign)
|
34
|
+
# Empty set is empty string, word +Empty+ or unicode character +∅+
|
35
|
+
# +R+ represents whole 1-D line (-∞, ∞)
|
36
|
+
def self.from_string(str)
|
37
|
+
intervals = str.split(/[uU∪]/).flat_map{|interval_str|
|
38
|
+
BasicIntervals.from_string(interval_str)
|
39
|
+
}.map(&:to_interval_set)
|
40
|
+
Operations.union(intervals)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Output standard mathematical notation of interval set in left-to-right order.
|
44
|
+
# Each singular point is listed separately in curly braces.
|
45
|
+
def to_s
|
46
|
+
@intervals.empty? ? EMPTY_SET_SYMBOL : intervals.map(&:to_s).join(UNION_SYMBOL)
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect # :nodoc:
|
50
|
+
to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks whether an interval set contains certain position.
|
54
|
+
# Operation complexity is O(ln N), where N is a number of contiguous regions in an interval set
|
55
|
+
def include_position?(value)
|
56
|
+
interval = @intervals.bsearch{|interv| interv.to >= value }
|
57
|
+
interval && interval.include_position?(value)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Checks whether an interval set contains another interval set. Alias: +#include?+
|
61
|
+
def contain?(other)
|
62
|
+
self.intersection(other) == other
|
63
|
+
end
|
64
|
+
alias include? contain?
|
65
|
+
|
66
|
+
# Checks whether an interval set is covered by another interval set. Alias: +#covered_by?+
|
67
|
+
def contained_by?(other)
|
68
|
+
self.intersection(other) == self
|
69
|
+
end
|
70
|
+
alias covered_by? contained_by?
|
71
|
+
|
72
|
+
# TODO: optimize if possible.
|
73
|
+
# Checks whether an interval set intersects another interval set. Alias: +#intersect?+
|
74
|
+
def intersect?(other)
|
75
|
+
! intersection(other).empty?
|
76
|
+
end
|
77
|
+
alias overlap? intersect?
|
78
|
+
|
79
|
+
# Checks whether an interval set is empty
|
80
|
+
def empty?
|
81
|
+
@intervals.empty?
|
82
|
+
end
|
83
|
+
|
84
|
+
# Checks whether an interval set is contiguous (empty set treated contiguous)
|
85
|
+
def contiguous?
|
86
|
+
@intervals.size <= 1
|
87
|
+
end
|
88
|
+
|
89
|
+
# Total length of all intervals in set
|
90
|
+
def total_length
|
91
|
+
@intervals.map(&:length).inject(0, &:+)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Number of connected components
|
95
|
+
def num_connected_components
|
96
|
+
@intervals.size
|
97
|
+
end
|
98
|
+
|
99
|
+
# TODO: optimize.
|
100
|
+
# Closure of an interval set
|
101
|
+
def closure
|
102
|
+
Operations.union(@intervals.map(&:closure).map(&:to_interval_set))
|
103
|
+
end
|
104
|
+
|
105
|
+
# Minimal contiguous interval, covering interval set
|
106
|
+
def covering_interval
|
107
|
+
if @intervals.size == 0
|
108
|
+
Empty
|
109
|
+
elsif @intervals.size == 1
|
110
|
+
self
|
111
|
+
else
|
112
|
+
BasicIntervals.interval_by_boundary_inclusion(@intervals.first.include_from?, @intervals.first.from,
|
113
|
+
@intervals.last.include_to?, @intervals.last.to).to_interval_set
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def hash # :nodoc:
|
118
|
+
@intervals.hash
|
119
|
+
end
|
120
|
+
|
121
|
+
def eql?(other) # :nodoc:
|
122
|
+
other.class.equal?(self.class) && intervals == other.intervals
|
123
|
+
end
|
124
|
+
|
125
|
+
# Intervals are equal only if they contain exactly the same intervals.
|
126
|
+
# Point inclusion is also considered
|
127
|
+
def ==(other)
|
128
|
+
other.is_a?(IntervalSet) && intervals == other.intervals
|
129
|
+
end
|
130
|
+
|
131
|
+
# Union of an interval set with another interval set +other+. Alias: +|+
|
132
|
+
# To unite many (tens of thousands intervals) intervals use +IntervalNotation::Operations.unite+ method.
|
133
|
+
# (+Operations.unite+ is dramatically faster than sequentially uniting intervals one-by-one)
|
134
|
+
def union(other)
|
135
|
+
Operations.union([self, other])
|
136
|
+
end
|
137
|
+
|
138
|
+
# Intersection of an interval set with another interval set +other+. Alias: +&+
|
139
|
+
# To unite many (tens of thousands intervals) intervals use +IntervalNotation::Operations.intersection+ method.
|
140
|
+
# (+Operations.intersection+ is dramatically faster than sequentially intersecting intervals one-by-one)
|
141
|
+
def intersection(other)
|
142
|
+
Operations.intersection([self, other])
|
143
|
+
end
|
144
|
+
|
145
|
+
# Difference between an interval set and another interval set +other+. Alias: +-+
|
146
|
+
def subtract(other)
|
147
|
+
Operations.combine([self, other], SubtractCombiner.new)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Symmetric difference between an interval set and another interval set +other+. Alias: +^+
|
151
|
+
def symmetric_difference(other)
|
152
|
+
Operations.combine([self, other], SymmetricDifferenceCombiner.new)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Complement of an interval set in R. Alias: +~+
|
156
|
+
def complement
|
157
|
+
R.subtract(self)
|
158
|
+
end
|
159
|
+
|
160
|
+
alias :& :intersection
|
161
|
+
alias :| :union
|
162
|
+
alias :- :subtract
|
163
|
+
alias :^ :symmetric_difference
|
164
|
+
alias :~ :complement
|
165
|
+
|
166
|
+
|
167
|
+
# Auxiliary method to share part of common interface with basic intervals
|
168
|
+
def to_interval_set # :nodoc:
|
169
|
+
self
|
170
|
+
end
|
171
|
+
|
172
|
+
class << self
|
173
|
+
# auxiliary method to check that intervals are sorted and don't overlap
|
174
|
+
def check_valid?(intervals)
|
175
|
+
intervals.each_cons(2).all? do |interval_1, interval_2|
|
176
|
+
interval_1.to <= interval_2.from && !(interval_1.to == interval_2.from && (interval_1.include_to? || interval_2.include_from?))
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# An +IntervalSet.new_unsafe+ is a constructor which skips validation. It's designed mostly for internal use.
|
181
|
+
# It can be used when you are absolutely sure, that intervals are ordered and don't overlap.
|
182
|
+
def new_unsafe(intervals)
|
183
|
+
obj = allocate
|
184
|
+
obj.instance_variable_set(:@intervals, intervals.freeze)
|
185
|
+
obj
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
extend Operations
|
190
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'interval_set'
|
2
|
+
require_relative 'basic_intervals'
|
3
|
+
require_relative 'combiners'
|
4
|
+
|
5
|
+
module IntervalNotation
|
6
|
+
module Operations
|
7
|
+
# Internal method which combines intervals according to an algorithm given by a combiner.
|
8
|
+
# Combiner tells whether current section or point should be included to a new interval.
|
9
|
+
def combine(interval_sets, combiner)
|
10
|
+
points = interval_sets.each_with_index.flat_map{|interval_set, interval_set_index|
|
11
|
+
interval_set.intervals.flat_map{|interval|
|
12
|
+
interval.interval_boundaries(interval_set_index)
|
13
|
+
}
|
14
|
+
}.sort_by(&:value)
|
15
|
+
|
16
|
+
intervals = []
|
17
|
+
|
18
|
+
incl_from = nil
|
19
|
+
from = nil
|
20
|
+
|
21
|
+
points.chunk(&:value).each do |point_value, points_on_place|
|
22
|
+
combiner.pass(points_on_place)
|
23
|
+
|
24
|
+
if combiner.previous_state
|
25
|
+
if combiner.state
|
26
|
+
unless combiner.include_last_point
|
27
|
+
intervals << BasicIntervals.interval_by_boundary_inclusion(incl_from, from, false, point_value)
|
28
|
+
incl_from = false
|
29
|
+
from = point_value
|
30
|
+
end
|
31
|
+
else
|
32
|
+
to = point_value
|
33
|
+
incl_to = combiner.include_last_point
|
34
|
+
intervals << BasicIntervals.interval_by_boundary_inclusion(incl_from, from, incl_to, to)
|
35
|
+
from = nil # easier to find an error (but not necessary code)
|
36
|
+
incl_from = nil # ditto
|
37
|
+
end
|
38
|
+
else
|
39
|
+
if combiner.state
|
40
|
+
from = point_value
|
41
|
+
incl_from = combiner.include_last_point
|
42
|
+
else
|
43
|
+
intervals << BasicIntervals::Point.new(point_value) if combiner.include_last_point
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
IntervalSet.new_unsafe(intervals)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Union of multiple intervals.
|
51
|
+
def union(intervals)
|
52
|
+
combine(intervals, UnionCombiner.new(intervals.size))
|
53
|
+
end
|
54
|
+
|
55
|
+
# Intersection of multiple intervals
|
56
|
+
def intersection(intervals)
|
57
|
+
combine(intervals, IntersectCombiner.new(intervals.size))
|
58
|
+
end
|
59
|
+
|
60
|
+
module_function :combine, :union, :intersection
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require_relative 'interval_notation/version'
|
2
|
+
|
3
|
+
require_relative 'interval_notation/error'
|
4
|
+
require_relative 'interval_notation/basic_intervals'
|
5
|
+
require_relative 'interval_notation/combiners'
|
6
|
+
require_relative 'interval_notation/interval_set'
|
7
|
+
require_relative 'interval_notation/operations'
|
8
|
+
|
9
|
+
module IntervalNotation
|
10
|
+
UNION_SYMBOL = '∪'.freeze
|
11
|
+
PLUS_INFINITY_SYMBOL = '+∞'.freeze
|
12
|
+
MINUS_INFINITY_SYMBOL = '-∞'.freeze
|
13
|
+
EMPTY_SET_SYMBOL = '∅'.freeze
|
14
|
+
|
15
|
+
R = IntervalSet.new_unsafe( [BasicIntervals::OpenOpenInterval.new(-Float::INFINITY, Float::INFINITY)] )
|
16
|
+
Empty = IntervalSet.new_unsafe([])
|
17
|
+
|
18
|
+
module Syntax
|
19
|
+
# Long syntax for interval factory methods
|
20
|
+
module Short
|
21
|
+
R = ::IntervalNotation::R
|
22
|
+
Empty = ::IntervalNotation::Empty
|
23
|
+
|
24
|
+
def int(str)
|
25
|
+
IntervalSet.from_string(str)
|
26
|
+
end
|
27
|
+
|
28
|
+
def oo(from, to)
|
29
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenOpenInterval.new(from, to)] )
|
30
|
+
end
|
31
|
+
|
32
|
+
def co(from, to)
|
33
|
+
IntervalSet.new_unsafe( [BasicIntervals::ClosedOpenInterval.new(from, to)] )
|
34
|
+
end
|
35
|
+
|
36
|
+
def oc(from, to)
|
37
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenClosedInterval.new(from, to)] )
|
38
|
+
end
|
39
|
+
|
40
|
+
def cc(from, to)
|
41
|
+
IntervalSet.new_unsafe( [BasicIntervals::ClosedClosedInterval.new(from, to)] )
|
42
|
+
end
|
43
|
+
|
44
|
+
def pt(value)
|
45
|
+
IntervalSet.new_unsafe( [BasicIntervals::Point.new(value)] )
|
46
|
+
end
|
47
|
+
|
48
|
+
def lt(value)
|
49
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenOpenInterval.new(-Float::INFINITY, value)] )
|
50
|
+
end
|
51
|
+
|
52
|
+
def le(value)
|
53
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenClosedInterval.new(-Float::INFINITY, value)] )
|
54
|
+
end
|
55
|
+
|
56
|
+
def gt(value)
|
57
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenOpenInterval.new(value, Float::INFINITY)] )
|
58
|
+
end
|
59
|
+
|
60
|
+
def ge(value)
|
61
|
+
IntervalSet.new_unsafe( [BasicIntervals::ClosedOpenInterval.new(value, Float::INFINITY)] )
|
62
|
+
end
|
63
|
+
|
64
|
+
module_function :oo, :co, :oc, :cc, :pt, :lt, :le, :gt, :ge, :int
|
65
|
+
end
|
66
|
+
|
67
|
+
# Long syntax for interval factory methods
|
68
|
+
module Long
|
69
|
+
R = ::IntervalNotation::R
|
70
|
+
Empty = ::IntervalNotation::Empty
|
71
|
+
|
72
|
+
def interval(str)
|
73
|
+
IntervalSet.from_string(str)
|
74
|
+
end
|
75
|
+
|
76
|
+
def open_open(from, to)
|
77
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenOpenInterval.new(from, to)] )
|
78
|
+
end
|
79
|
+
|
80
|
+
def closed_open(from, to)
|
81
|
+
IntervalSet.new_unsafe( [BasicIntervals::ClosedOpenInterval.new(from, to)] )
|
82
|
+
end
|
83
|
+
|
84
|
+
def open_closed(from, to)
|
85
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenClosedInterval.new(from, to)] )
|
86
|
+
end
|
87
|
+
|
88
|
+
def closed_closed(from, to)
|
89
|
+
IntervalSet.new_unsafe( [BasicIntervals::ClosedClosedInterval.new(from, to)] )
|
90
|
+
end
|
91
|
+
|
92
|
+
def point(value)
|
93
|
+
IntervalSet.new_unsafe( [BasicIntervals::Point.new(value)] )
|
94
|
+
end
|
95
|
+
|
96
|
+
def less_than(value)
|
97
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenOpenInterval.new(-Float::INFINITY, value)] )
|
98
|
+
end
|
99
|
+
|
100
|
+
def less_than_or_equal_to(value)
|
101
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenClosedInterval.new(-Float::INFINITY, value)] )
|
102
|
+
end
|
103
|
+
|
104
|
+
def greater_than(value)
|
105
|
+
IntervalSet.new_unsafe( [BasicIntervals::OpenOpenInterval.new(value, Float::INFINITY)] )
|
106
|
+
end
|
107
|
+
|
108
|
+
def greater_than_or_equal_to(value)
|
109
|
+
IntervalSet.new_unsafe( [BasicIntervals::ClosedOpenInterval.new(value, Float::INFINITY)] )
|
110
|
+
end
|
111
|
+
|
112
|
+
module_function :open_open, :closed_open, :open_closed, :closed_closed, :point,
|
113
|
+
:less_than, :less_than_or_equal_to, :greater_than, :greater_than_or_equal_to,
|
114
|
+
:interval
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'interval_notation'
|
2
|
+
|
3
|
+
include IntervalNotation
|
4
|
+
include IntervalNotation::BasicIntervals
|
5
|
+
include IntervalNotation::Syntax::Short
|
6
|
+
|
7
|
+
describe IntervalNotation do
|
8
|
+
describe OpenOpenInterval do
|
9
|
+
describe '.new' do
|
10
|
+
{ [1,3] => :ok,
|
11
|
+
[1, Float::INFINITY] => :ok,
|
12
|
+
[-Float::INFINITY, 1] => :ok,
|
13
|
+
[-Float::INFINITY, Float::INFINITY] => :ok,
|
14
|
+
|
15
|
+
[1,1] => :fail,
|
16
|
+
[3,1] => :fail,
|
17
|
+
[1,-Float::INFINITY] => :fail,
|
18
|
+
[Float::INFINITY, 1] => :fail,
|
19
|
+
[-Float::INFINITY, -Float::INFINITY] => :fail,
|
20
|
+
[Float::INFINITY, Float::INFINITY] => :fail,
|
21
|
+
[Float::INFINITY, -Float::INFINITY] => :fail,
|
22
|
+
}.each do |(from, to), result|
|
23
|
+
if result == :ok
|
24
|
+
it "OpenOpenInterval.new(#{from}, #{to}) should not raise" do
|
25
|
+
expect{ OpenOpenInterval.new(from, to) }.not_to raise_error
|
26
|
+
end
|
27
|
+
elsif result == :fail
|
28
|
+
it "OpenOpenInterval.OpenOpenInterval(#{from}, #{to}) should raise" do
|
29
|
+
expect{ OpenOpenInterval.new(from, to) }.to raise_error Error
|
30
|
+
end
|
31
|
+
else
|
32
|
+
raise 'Incorrect test'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
describe OpenClosedInterval do
|
40
|
+
describe '.new' do
|
41
|
+
{ [1,3] => :ok,
|
42
|
+
[-Float::INFINITY, 1] => :ok,
|
43
|
+
|
44
|
+
[1, Float::INFINITY] => :fail,
|
45
|
+
[-Float::INFINITY, Float::INFINITY] => :fail,
|
46
|
+
[1,1] => :fail,
|
47
|
+
[3,1] => :fail,
|
48
|
+
[1,-Float::INFINITY] => :fail,
|
49
|
+
[Float::INFINITY, 1] => :fail,
|
50
|
+
[-Float::INFINITY, -Float::INFINITY] => :fail,
|
51
|
+
[Float::INFINITY, Float::INFINITY] => :fail,
|
52
|
+
[Float::INFINITY, -Float::INFINITY] => :fail,
|
53
|
+
}.each do |(from, to), result|
|
54
|
+
if result == :ok
|
55
|
+
it "OpenClosedInterval.new(#{from}, #{to}) should not raise" do
|
56
|
+
expect{ OpenClosedInterval.new(from, to) }.not_to raise_error
|
57
|
+
end
|
58
|
+
elsif result == :fail
|
59
|
+
it "OpenOpenInterval.new(#{from}, #{to}) should raise" do
|
60
|
+
expect{ OpenClosedInterval.new(from, to) }.to raise_error Error
|
61
|
+
end
|
62
|
+
else
|
63
|
+
raise 'Incorrect test'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
describe ClosedOpenInterval do
|
71
|
+
describe '.new' do
|
72
|
+
{ [1,3] => :ok,
|
73
|
+
[1, Float::INFINITY] => :ok,
|
74
|
+
|
75
|
+
[-Float::INFINITY, 1] => :fail,
|
76
|
+
[-Float::INFINITY, Float::INFINITY] => :fail,
|
77
|
+
[1,1] => :fail,
|
78
|
+
[3,1] => :fail,
|
79
|
+
[1,-Float::INFINITY] => :fail,
|
80
|
+
[Float::INFINITY, 1] => :fail,
|
81
|
+
[-Float::INFINITY, -Float::INFINITY] => :fail,
|
82
|
+
[Float::INFINITY, Float::INFINITY] => :fail,
|
83
|
+
[Float::INFINITY, -Float::INFINITY] => :fail,
|
84
|
+
}.each do |(from, to), result|
|
85
|
+
if result == :ok
|
86
|
+
it "ClosedOpenInterval.new(#{from}, #{to}) should not raise" do
|
87
|
+
expect{ ClosedOpenInterval.new(from, to) }.not_to raise_error
|
88
|
+
end
|
89
|
+
elsif result == :fail
|
90
|
+
it "OpenOpenInterval.new(#{from}, #{to}) should raise" do
|
91
|
+
expect{ ClosedOpenInterval.new(from, to) }.to raise_error Error
|
92
|
+
end
|
93
|
+
else
|
94
|
+
raise 'Incorrect test'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
describe ClosedClosedInterval do
|
102
|
+
describe '.new' do
|
103
|
+
{ [1,3] => :ok,
|
104
|
+
|
105
|
+
[1, Float::INFINITY] => :fail,
|
106
|
+
[-Float::INFINITY, 1] => :fail,
|
107
|
+
[-Float::INFINITY, Float::INFINITY] => :fail,
|
108
|
+
[1,1] => :fail,
|
109
|
+
[3,1] => :fail,
|
110
|
+
[1,-Float::INFINITY] => :fail,
|
111
|
+
[Float::INFINITY, 1] => :fail,
|
112
|
+
[-Float::INFINITY, -Float::INFINITY] => :fail,
|
113
|
+
[Float::INFINITY, Float::INFINITY] => :fail,
|
114
|
+
[Float::INFINITY, -Float::INFINITY] => :fail,
|
115
|
+
}.each do |(from, to), result|
|
116
|
+
if result == :ok
|
117
|
+
it "ClosedClosedInterval.new(#{from}, #{to}) should not raise" do
|
118
|
+
expect{ ClosedClosedInterval.new(from, to) }.not_to raise_error
|
119
|
+
end
|
120
|
+
elsif result == :fail
|
121
|
+
it "OpenOpenInterval.new(#{from}, #{to}) should raise" do
|
122
|
+
expect{ ClosedClosedInterval.new(from, to) }.to raise_error Error
|
123
|
+
end
|
124
|
+
else
|
125
|
+
raise 'Incorrect test'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe Point do
|
132
|
+
it 'should not raise if finite' do
|
133
|
+
expect{ Point.new(3) }.not_to raise_error
|
134
|
+
end
|
135
|
+
it 'should raise if infinite' do
|
136
|
+
expect{ Point.new(-Float::INFINITY) }.to raise_error
|
137
|
+
expect{ Point.new(Float::INFINITY) }.to raise_error
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|