semver_dialects 2.0.2 → 3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8afd964094235e63624c76231df69d2723cab03721b06c35aac704b2c1284bc
4
- data.tar.gz: a51b11c2b5121fb34dda52d94824ca9713e634691d0aa212cd1c29197b40a8e4
3
+ metadata.gz: 19c2562420fb945c665db99cfeb6ac46186f65b10b4a84ba073aa8e0b443666e
4
+ data.tar.gz: 73d80ecdaedfc906f4e24f1c9f5921f18e126c19d73722991de7f3d61d5b2f24
5
5
  SHA512:
6
- metadata.gz: ce87db5b620f138cd1058c5fb134bc33f7ea136d5dc5f5ba45626e97071d0eba5727e35ecf07cb509d23e08fbbe0838970e0480ba86d17843f416b3b409a6ea7
7
- data.tar.gz: 2194ee64c814f01bdfd51418e3cae3acc451277fab265f5212049dc304ea378bacc6fef76d6786ad9495d23f6a0e2f1fe0eeb809496eb39a0c70725929fad6f3
6
+ metadata.gz: 28e168351b5eda9bf282416c58b52d5fb60ea768f5cb4ecc59c75a16c212b1425878d7a68133538eefea885dd4d430e7fc2d47359b103b31e72970fc69b88643
7
+ data.tar.gz: b20653d1d36eb4e21de3ae2e5962e1435885dd4a40988279394f733876eb6b4de9d6e6c04badd0222c14cb1af5ce00b9ac0abd747db7eab1908a3d5db3fa1f0b
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Class that describes a version that can be compared to another version in a generic way.
4
+ #
5
+ # A version is made of +tokens+ and an optional +addition+.
6
+ # Tokens and additions must be comparable using the spaceship operator.
7
+ # An addition behaves like a version and must respond to +tokens+.
8
+ #
9
+ # Tokens are used to represent the dot separated list of segments
10
+ # like 1.2.3 (semantic version) or alpha.1 (pre-release tag).
11
+ #
12
+ # Version 1.2.3-alpha.1-2024.03.25 is made of 3 +Version+ objects
13
+ # whose tokens are represented as 1.2.3, alpha.1, 2024.03.25.
14
+ # Version alpha.1 is the addition of version 1.2.3,
15
+ # and version 2024.03.25 is the addition of version alpha.1.
16
+ #
17
+ # This class can support of the comparison logic of many syntaxes
18
+ # by implementing specific token classes.
19
+ #
20
+ module SemverDialects
21
+ class BaseVersion
22
+ include Comparable
23
+
24
+ attr_reader :tokens, :addition
25
+
26
+ def initialize(tokens, addition: nil)
27
+ @tokens = tokens
28
+ @addition = addition
29
+ end
30
+
31
+ def to_s
32
+ main = tokens.join('.')
33
+ main += "-#{addition}" if addition
34
+ main
35
+ end
36
+
37
+ def <=>(other)
38
+ cmp = compare_tokens(tokens, other.tokens)
39
+ return cmp unless cmp == 0
40
+
41
+ compare_additions(addition, other.addition)
42
+ end
43
+
44
+ # Returns true if the version tokens are equivalent to zero
45
+ # and the addition is also equivalent to zero.
46
+ def is_zero?
47
+ return false if compare_tokens(tokens, [0]) != 0
48
+
49
+ return true if addition.nil?
50
+
51
+ addition.is_zero?
52
+ end
53
+
54
+ private
55
+
56
+ def compare_tokens(a, b) # rubocop:disable Naming/MethodParameterName
57
+ max_idx = [a.size, b.size].max - 1
58
+ (0..max_idx).each do |idx|
59
+ cmp = compare_token_pair(a[idx], b[idx])
60
+ return cmp unless cmp == 0
61
+ end
62
+ 0
63
+ end
64
+
65
+ def compare_token_pair(a, b) # rubocop:disable Naming/MethodParameterName
66
+ (a || 0) <=> (b || 0)
67
+ end
68
+
69
+ def compare_additions(a, b) # rubocop:disable Naming/MethodParameterName
70
+ return 0 if a.nil? && b.nil?
71
+
72
+ (a || empty_addition).<=>(b || empty_addition)
73
+ end
74
+
75
+ def empty_addition
76
+ self.class.new([])
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Boundary is a boundary used in an interval.
4
+ # It can either be above all versions (infinity),
5
+ # below all versions (negative infinity), or any version.
6
+ module SemverDialects
7
+ class Boundary
8
+ attr_accessor :semver
9
+
10
+ def initialize(semver)
11
+ @semver = semver
12
+ end
13
+
14
+ def to_s
15
+ @semver.to_s
16
+ end
17
+
18
+ def <(other)
19
+ if other.instance_of?(BelowAll)
20
+ false
21
+ else
22
+ other.instance_of?(AboveAll) ? true : @semver < other.semver
23
+ end
24
+ end
25
+
26
+ def >(other)
27
+ if other.instance_of?(BelowAll)
28
+ true
29
+ else
30
+ other.instance_of?(AboveAll) ? false : @semver > other.semver
31
+ end
32
+ end
33
+
34
+ def <=(other)
35
+ self < other || self == other
36
+ end
37
+
38
+ def >=(other)
39
+ self > other || self == other
40
+ end
41
+
42
+ def ==(other)
43
+ # self cannot be BelowAll or AboveAll
44
+ if other.instance_of?(BelowAll) || other.instance_of?(AboveAll)
45
+ false
46
+ else
47
+ @semver == other.semver
48
+ end
49
+ end
50
+
51
+ def !=(other)
52
+ # self cannot be BelowAll or AboveAll
53
+ if other.instance_of?(BelowAll) || other.instance_of?(AboveAll)
54
+ false
55
+ else
56
+ @semver != other.semver
57
+ end
58
+ end
59
+
60
+ def is_initial_version?
61
+ @semver.is_zero?
62
+ end
63
+ end
64
+
65
+ # BelowAll represents a boundary below all possible versions.
66
+ # When used as the lower boundary of an interval, any version
67
+ # that is smaller than the upper boundary is in the interval.
68
+ class BelowAll < Boundary
69
+ def initialize; end
70
+
71
+ def to_s
72
+ '-inf'
73
+ end
74
+
75
+ def is_initial_version?
76
+ false
77
+ end
78
+
79
+ def <(other)
80
+ other.instance_of?(BelowAll) ? false : true
81
+ end
82
+
83
+ def >(_other)
84
+ false
85
+ end
86
+
87
+ def <=(other)
88
+ self < other || self == other
89
+ end
90
+
91
+ def >=(other)
92
+ self > other || self == other
93
+ end
94
+
95
+ def ==(other)
96
+ (other.instance_of? BelowAll) ? true : false
97
+ end
98
+
99
+ def !=(other)
100
+ !(self == other)
101
+ end
102
+ end
103
+
104
+ # AboveAll represents a boundary above all possible versions.
105
+ # When used as the upper boundary of an interval, any version
106
+ # that is greater than the lower boundary is in the interval.
107
+ class AboveAll < Boundary
108
+ def initialize; end
109
+
110
+ def to_s
111
+ '+inf'
112
+ end
113
+
114
+ def is_initial_version?
115
+ false
116
+ end
117
+
118
+ def <(_other)
119
+ false
120
+ end
121
+
122
+ def >(other)
123
+ other.instance_of?(AboveAll) ? false : true
124
+ end
125
+
126
+ def <=(other)
127
+ self < other || self == other
128
+ end
129
+
130
+ def >=(other)
131
+ self > other || self == other
132
+ end
133
+
134
+ def ==(other)
135
+ (other.instance_of? AboveAll) ? true : false
136
+ end
137
+
138
+ def !=(other)
139
+ !(self == other)
140
+ end
141
+ end
142
+ end
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../command'
4
- require_relative '../../semver_dialects.rb'
5
- require_relative '../semantic_version/version_translator.rb'
6
- require_relative '../semantic_version/version_parser.rb'
7
- require_relative '../semantic_version/version_range.rb'
4
+ require_relative '../../semver_dialects'
8
5
 
9
6
  module SemverDialects
10
7
  module Commands
@@ -22,7 +19,7 @@ module SemverDialects
22
19
  typ = @type.downcase
23
20
  raise SemverDialects::Error, 'wrong package type' unless @avail_types.include?(typ)
24
21
 
25
- if VersionChecker.version_sat?(typ, @version, @constraint)
22
+ if SemverDialects.version_satisfies?(typ, @version, @constraint)
26
23
  output.puts "#{@version} matches #{@constraint} for #{@type}"
27
24
  0
28
25
  else
@@ -0,0 +1,256 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SemverDialects
4
+ module IntervalType
5
+ UNKNOWN = 0
6
+ LEFT_OPEN = 1
7
+ LEFT_CLOSED = 2
8
+ RIGHT_OPEN = 4
9
+ RIGHT_CLOSED = 8
10
+ end
11
+
12
+ # Interval is an interval that starts with a lower boundary
13
+ # and ends with an upper boundary. The interval includes the boundaries
14
+ # or not depending on its type.
15
+ class Interval
16
+ # Returns an interval that only includes the given version.
17
+ def self.from_version(version)
18
+ boundary = Boundary.new(version)
19
+ Interval.new(IntervalType::LEFT_CLOSED | IntervalType::RIGHT_CLOSED, boundary, boundary)
20
+ end
21
+
22
+ attr_accessor :type, :start_cut, :end_cut
23
+
24
+ def initialize(type, start_cut, end_cut)
25
+ @type = type
26
+ @start_cut = start_cut
27
+ @end_cut = end_cut
28
+ end
29
+
30
+ def intersect(other_interval)
31
+ return EmptyInterval.new if empty?
32
+
33
+ # this look odd -- we have to use it here though, because it may be that placeholders are present inside
34
+ # the version for which > and < would yield true
35
+ return EmptyInterval.new if !(@start_cut <= other_interval.end_cut) || !(other_interval.start_cut <= @end_cut)
36
+
37
+ start_cut_new = max(@start_cut, other_interval.start_cut)
38
+ end_cut_new = min(@end_cut, other_interval.end_cut)
39
+
40
+ # compute the boundaries for the intersection
41
+ type = compute_intersection_boundary(self, other_interval, start_cut_new, end_cut_new)
42
+ interval = Interval.new(type, start_cut_new, end_cut_new)
43
+ half_open = !(interval.bit_set?(IntervalType::RIGHT_CLOSED) && interval.bit_set?(IntervalType::LEFT_CLOSED))
44
+
45
+ interval.singleton? && half_open ? EmptyInterval.new : interval
46
+ end
47
+
48
+ def special(cut)
49
+ cut.instance_of?(AboveAll) || cut.instance_of?(BelowAll)
50
+ end
51
+
52
+ def to_s
53
+ s = ''
54
+ s += bit_set?(IntervalType::LEFT_CLOSED) ? '[' : ''
55
+ s += bit_set?(IntervalType::LEFT_OPEN) ? '(' : ''
56
+ s += [@start_cut, @end_cut].join(',')
57
+ s += bit_set?(IntervalType::RIGHT_CLOSED) ? ']' : ''
58
+ s += bit_set?(IntervalType::RIGHT_OPEN) ? ')' : ''
59
+ s
60
+ end
61
+
62
+ # this function returns a human-readable descriptions of the version strings
63
+ def to_description_s
64
+ s = ''
65
+ if distinct?
66
+ s = "version #{@start_cut}"
67
+ elsif universal?
68
+ s = 'all versions '
69
+ else
70
+ s = 'all versions '
71
+ s += if start_cut.instance_of?(BelowAll)
72
+ ''
73
+ elsif bit_set?(IntervalType::LEFT_OPEN)
74
+ "after #{@start_cut} "
75
+ else
76
+ bit_set?(IntervalType::LEFT_CLOSED) ? "starting from #{@start_cut} " : ''
77
+ end
78
+ s += if end_cut.instance_of?(AboveAll)
79
+ ''
80
+ elsif bit_set?(IntervalType::RIGHT_OPEN)
81
+ "before #{@end_cut}"
82
+ else
83
+ bit_set?(IntervalType::RIGHT_CLOSED) ? "up to #{@end_cut}" : ''
84
+ end
85
+ end
86
+ s.strip
87
+ end
88
+
89
+ def to_nuget_s
90
+ to_maven_s
91
+ end
92
+
93
+ def to_maven_s
94
+ s = ''
95
+ # special case -- distinct version
96
+ if distinct?
97
+ s += "[#{@start_cut}]"
98
+ else
99
+ s += if start_cut.instance_of?(BelowAll)
100
+ '(,'
101
+ elsif bit_set?(IntervalType::LEFT_OPEN)
102
+ "[#{@start_cut},"
103
+ else
104
+ bit_set?(IntervalType::LEFT_CLOSED) ? "[#{@start_cut}," : ''
105
+ end
106
+ s += if end_cut.instance_of?(AboveAll)
107
+ ')'
108
+ elsif bit_set?(IntervalType::RIGHT_OPEN)
109
+ "#{@end_cut})"
110
+ else
111
+ bit_set?(IntervalType::RIGHT_CLOSED) ? "#{@end_cut}]" : ''
112
+ end
113
+ end
114
+ s
115
+ end
116
+
117
+ def distinct?
118
+ bit_set?(IntervalType::LEFT_CLOSED) && bit_set?(IntervalType::RIGHT_CLOSED) && @start_cut == @end_cut
119
+ end
120
+
121
+ def subsumes?(other)
122
+ @start_cut <= other.start_cut && @end_cut >= other.end_cut
123
+ end
124
+
125
+ def universal?
126
+ (bit_set?(IntervalType::LEFT_OPEN) && bit_set?(IntervalType::RIGHT_OPEN) && @start_cut.instance_of?(BelowAll) && @end_cut.instance_of?(AboveAll)) || @start_cut.is_initial_version? && @end_cut.instance_of?(AboveAll)
127
+ end
128
+
129
+ def to_gem_s
130
+ get_canoncial_s
131
+ end
132
+
133
+ def to_ruby_s
134
+ get_canoncial_s
135
+ end
136
+
137
+ def to_npm_s
138
+ get_canoncial_s
139
+ end
140
+
141
+ def to_conan_s
142
+ get_canoncial_s
143
+ end
144
+
145
+ def to_go_s
146
+ get_canoncial_s
147
+ end
148
+
149
+ def to_pypi_s
150
+ get_canoncial_s(',', '==')
151
+ end
152
+
153
+ def to_packagist_s
154
+ get_canoncial_s(',')
155
+ end
156
+
157
+ def empty?
158
+ instance_of?(EmptyInterval)
159
+ end
160
+
161
+ def singleton?
162
+ @start_cut == @end_cut && @start_cut.semver == @end_cut.semver
163
+ end
164
+
165
+ def ==(other)
166
+ @start_cut == other.start_cut && @end_cut == other.end_cut && @type == other.type
167
+ end
168
+
169
+ def bit_set?(interval_type)
170
+ @type & interval_type != 0
171
+ end
172
+
173
+ protected
174
+
175
+ def compute_intersection_boundary(interval_a, interval_b, start_cut_new, end_cut_new)
176
+ compute_boundary(interval_a, interval_b, start_cut_new, end_cut_new, IntervalType::LEFT_OPEN,
177
+ IntervalType::RIGHT_OPEN)
178
+ end
179
+
180
+ def compute_boundary(interval_a, interval_b, start_cut_new, end_cut_new, left_check, right_check)
181
+ start_cut_a = interval_a.start_cut
182
+ end_cut_a = interval_a.end_cut
183
+ type_a = interval_a.type
184
+
185
+ start_cut_b = interval_b.start_cut
186
+ end_cut_b = interval_b.end_cut
187
+ type_b = interval_b.type
188
+
189
+ left_fill = left_check == IntervalType::LEFT_OPEN ? IntervalType::LEFT_CLOSED : IntervalType::LEFT_OPEN
190
+ right_fill = right_check == IntervalType::RIGHT_OPEN ? IntervalType::RIGHT_CLOSED : IntervalType::RIGHT_OPEN
191
+
192
+ # compute the boundaries for the union
193
+ if start_cut_b == start_cut_a
194
+ one_left_closed = left_type(type_a) == left_check || left_type(type_b) == left_check
195
+ left_type = one_left_closed ? left_check : left_fill
196
+ else
197
+ left_type = start_cut_new == start_cut_a ? left_type(type_a) : left_type(type_b)
198
+ end
199
+
200
+ if end_cut_b == end_cut_a
201
+ one_right_closed = right_type(type_a) == right_check || right_type(type_b) == right_check
202
+ right_type = one_right_closed ? right_check : right_fill
203
+ else
204
+ right_type = end_cut_new == end_cut_a ? right_type(type_a) : right_type(type_b)
205
+ end
206
+
207
+ left_type | right_type
208
+ end
209
+
210
+ def get_canoncial_s(delimiter = ' ', eq = '=')
211
+ if distinct?
212
+ "#{eq}#{@start_cut}"
213
+ else
214
+ first = if start_cut.instance_of?(BelowAll)
215
+ ''
216
+ elsif bit_set?(IntervalType::LEFT_OPEN)
217
+ ">#{@start_cut}"
218
+ else
219
+ bit_set?(IntervalType::LEFT_CLOSED) ? ">=#{@start_cut}" : ''
220
+ end
221
+ second = if end_cut.instance_of?(AboveAll)
222
+ ''
223
+ elsif bit_set?(IntervalType::RIGHT_OPEN)
224
+ "<#{@end_cut}"
225
+ else
226
+ bit_set?(IntervalType::RIGHT_CLOSED) ? "<=#{@end_cut}" : ''
227
+ end
228
+ !first.empty? && !second.empty? ? "#{first}#{delimiter}#{second}" : first + second
229
+ end
230
+ end
231
+
232
+ def max(cut_a, cut_b)
233
+ cut_a > cut_b ? cut_a : cut_b
234
+ end
235
+
236
+ def min(cut_a, cut_b)
237
+ cut_a < cut_b ? cut_a : cut_b
238
+ end
239
+
240
+ def right_type(type)
241
+ (IntervalType::RIGHT_OPEN | IntervalType::RIGHT_CLOSED) & type
242
+ end
243
+
244
+ def left_type(type)
245
+ (IntervalType::LEFT_OPEN | IntervalType::LEFT_CLOSED) & type
246
+ end
247
+ end
248
+
249
+ class EmptyInterval < Interval
250
+ def initialize; end
251
+
252
+ def to_s
253
+ 'empty'
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # IntervalParser parses a simple constraint expressed in the npm syntax
4
+ # (or equivalent) and returns a Interval that has an upper boundary
5
+ # or a lower boundary.
6
+ #
7
+ # The constraint is a string that can either be:
8
+ # - an operator (>, <, >=, <=, =) followed by a version
9
+ # - a version; the interval starts and ends with that version
10
+ # - "=*"; the interval has no boundaries and includes any version
11
+ #
12
+ # Technically IntervalParser returns a Interval such as
13
+ # start_cut is BelowAll or end_cut is AboveAll.
14
+ # The type of the Interval matches the operator
15
+ # that's been detected.
16
+ #
17
+ module SemverDialects
18
+ module IntervalParser
19
+ # A constraint is made of an operator followed by a version string.
20
+ # Use the regular expression of the SemanticVersion because this is the most generic one.
21
+ CONSTRAINT_REGEXP = Regexp.new("(?<op>[><=]+) *(?<version>#{SemanticVersion::VERSION_PATTERN})").freeze
22
+
23
+ def self.parse(typ, versionstring)
24
+ if versionstring == '=*'
25
+ # special case = All Versions
26
+ return Interval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_OPEN, BelowAll.new, AboveAll.new)
27
+ end
28
+
29
+ version_items = versionstring.split(' ')
30
+ interval = Interval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_OPEN, BelowAll.new, AboveAll.new)
31
+ version_items.each do |version_item|
32
+ matches = version_item.match CONSTRAINT_REGEXP
33
+ raise InvalidConstraintError, versionstring if matches.nil?
34
+
35
+ version = SemverDialects.parse_version(typ, matches[:version])
36
+ boundary = Boundary.new(version)
37
+ case matches[:op]
38
+ when '>='
39
+ new_interval = Interval.new(IntervalType::LEFT_CLOSED | IntervalType::RIGHT_OPEN, boundary, AboveAll.new)
40
+ interval = interval.intersect(new_interval)
41
+ when '<='
42
+ new_interval = Interval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_CLOSED, BelowAll.new, boundary)
43
+ interval = interval.intersect(new_interval)
44
+ when '<'
45
+ new_interval = Interval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_OPEN, BelowAll.new, boundary)
46
+ interval = interval.intersect(new_interval)
47
+ when '>'
48
+ new_interval = Interval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_OPEN, boundary, AboveAll.new)
49
+ interval = interval.intersect(new_interval)
50
+ when '=', '=='
51
+ new_interval = Interval.new(IntervalType::LEFT_CLOSED | IntervalType::RIGHT_CLOSED, boundary, boundary)
52
+ interval = interval.intersect(new_interval)
53
+ end
54
+ end
55
+ interval
56
+ rescue InvalidVersionError
57
+ raise InvalidConstraintError, versionstring
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ # IntervalSet is a disjunction of version intervals.
4
+ # It can express a range like "[1.0,2.0],[3.0,4.0]" (Maven syntax),
5
+ # that is between 1.0 and 2.0 (included) OR between 3.0 and 4.0 (included).
6
+ module SemverDialects
7
+ class IntervalSet
8
+ attr_reader :intervals
9
+
10
+ def initialize
11
+ @intervals = []
12
+ @interval_set = Set.new
13
+ end
14
+
15
+ def add(interval)
16
+ @intervals << interval
17
+ @interval_set.add(interval)
18
+ end
19
+
20
+ def <<(item)
21
+ add(item)
22
+ end
23
+
24
+ def size
25
+ @intervals.size
26
+ end
27
+
28
+ def to_s
29
+ @intervals.map(&:to_s).join(',')
30
+ end
31
+
32
+ def to_description_s
33
+ @intervals.map(&:to_description_s).join(', ').capitalize
34
+ end
35
+
36
+ def to_npm_s
37
+ @intervals.map(&:to_npm_s).join('||')
38
+ end
39
+
40
+ def to_conan_s
41
+ to_npm_s
42
+ end
43
+
44
+ def to_nuget_s
45
+ to_maven_s
46
+ end
47
+
48
+ def to_maven_s
49
+ @intervals.map(&:to_maven_s).join(',')
50
+ end
51
+
52
+ def to_gem_s
53
+ @intervals.map(&:to_gem_s).join('||')
54
+ end
55
+
56
+ def to_pypi_s
57
+ @intervals.map(&:to_pypi_s).join('||')
58
+ end
59
+
60
+ def to_go_s
61
+ @intervals.map(&:to_go_s).join('||')
62
+ end
63
+
64
+ def to_packagist_s
65
+ @intervals.map(&:to_packagist_s).join('||')
66
+ end
67
+
68
+ def to_version_s(package_type)
69
+ case package_type
70
+ when 'npm'
71
+ to_npm_s
72
+ when 'nuget'
73
+ to_nuget_s
74
+ when 'maven'
75
+ to_maven_s
76
+ when 'gem'
77
+ to_gem_s
78
+ when 'pypi'
79
+ to_pypi_s
80
+ when 'packagist'
81
+ to_packagist_s
82
+ when 'go'
83
+ to_go_s
84
+ when 'conan'
85
+ to_conan_s
86
+ else
87
+ ''
88
+ end
89
+ end
90
+
91
+ def includes?(other)
92
+ @interval_set.include?(other)
93
+ end
94
+
95
+ def overlaps_with?(other)
96
+ @interval_set.each do |interval|
97
+ return true unless interval.intersect(other).instance_of?(EmptyInterval)
98
+ end
99
+ false
100
+ end
101
+
102
+ def first
103
+ @intervals.first
104
+ end
105
+
106
+ def empty?
107
+ @intervals.empty?
108
+ end
109
+
110
+ def any?
111
+ @intervals.any?
112
+ end
113
+
114
+ def universal?
115
+ @intervals.each do |interval|
116
+ return true if interval.universal?
117
+ end
118
+ false
119
+ end
120
+ end
121
+ end