interval_notation 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|