Module: FatCore::Range
Defined Under Namespace
Modules: ClassMethods
Operations collapse
-
#difference(other) ⇒ Array<Range>
(also: #-)
The difference method, -, removes the overlapping part of the other argument from self.
-
#gaps(ranges) ⇒ Array<Range>
If this range is not spanned by the
rangescollectively, return an Array of ranges representing the gaps in coverage. -
#intersection(other) ⇒ Range?
(also: #&)
Return a Range that represents the intersection between this range and the
otherrange. -
#join(other) ⇒ Range?
Return a range that concatenates this range with other if it is contiguous with this range on the left or right; return nil if the ranges are not contiguous.
-
#overlaps(ranges) ⇒ Array<Range>
Within this range return an Array of Ranges representing the overlaps among the given Array of Ranges
ranges. -
#tex_quote ⇒ String
Allow erb or erubis documents to directly interpolate a Range.
-
#union(other) ⇒ Range?
(also: #+)
Return a Range that represents the union between this range and the
otherrange.
Queries collapse
-
#contiguous?(other) ⇒ Boolean
Is self contiguous to other either on the left or on the right? First, the two ranges are sorted by their min values, and the range with the lowest min value is considered to be on the "left" and the other range on the "right".
-
#left_contiguous?(other) ⇒ Boolean
Is other on the left of and contiguous to self? Whether one range is "contiguous" to another has two cases:.
-
#overlaps?(other) ⇒ Boolean
Return whether self overlaps with other Range.
-
#overlaps_among?(ranges) ⇒ Boolean
Return whether any of the
rangesthat overlap self have overlaps among one another. -
#proper_subset_of?(other) ⇒ Boolean
Return whether self is contained within
otherrange, with at most one boundary touching. -
#proper_superset_of?(other) ⇒ Boolean
Return whether self contains
otherrange, with at most one boundary touching. -
#right_contiguous?(other) ⇒ Boolean
Is other on the right of and contiguous to self? Whether one range is "contiguous" to another has two cases:.
-
#spanned_by?(ranges) ⇒ Boolean
Return true if the given ranges collectively cover this range without overlaps and without gaps.
-
#subset_of?(other) ⇒ Boolean
Return whether self is contained within
otherrange, even if their boundaries touch. -
#superset_of?(other) ⇒ Boolean
Return whether self contains
otherrange, even if their boundaries touch.
Sorting collapse
- .included(base) ⇒ Object
-
#<=>(other) ⇒ Integer, ...
Compare this range with other first by min values, then by max values.
- #compatible?(ranges) ⇒ Boolean
Class Method Details
.included(base) ⇒ Object
461 462 463 |
# File 'lib/fat_core/range.rb', line 461 def self.included(base) base.extend(ClassMethods) end |
Instance Method Details
#<=>(other) ⇒ Integer, ...
Compare this range with other first by min values, then by max values.
This causes a sort of Ranges with Comparable elements to sort from left to right on the number line, then for Ranges that start on the same number, from smallest to largest.
427 428 429 |
# File 'lib/fat_core/range.rb', line 427 def <=>(other) [min, max] <=> other.minmax end |
#compatible?(ranges) ⇒ Boolean
431 432 433 434 435 436 437 438 439 |
# File 'lib/fat_core/range.rb', line 431 def compatible?(ranges) numeric_ok = min.is_a?(Numeric) && max.is_a?(Numeric) if numeric_ok ranges.map.all? { |r| r.min.is_a?(Numeric) && r.max.is_a?(Numeric) } else self_class = min.class ranges.map.all? { |r| r.min.is_a?(self_class) && r.max.is_a?(self_class) } end end |
#contiguous?(other) ⇒ Boolean
Is self contiguous to other either on the left or on the right? First, the two ranges are sorted by their min values, and the range with the lowest min value is considered to be on the "left" and the other range on the "right". Whether one range is "contiguous" to another then has two cases:
- If the max element of the Range on the left respond to the #succ method (that is, its value is a discrete value such as Integer or Date) test whether the succ to the max value of the Range on the left is equal to the min value of the Range on the right.
- If the max element of the Range on the left does not respond to the #succ method (that is, its values are continuous values such as Floats) test whether the max value of the Range on the left is equal to the min value of the Range on the right
318 319 320 |
# File 'lib/fat_core/range.rb', line 318 def contiguous?(other) left_contiguous?(other) || right_contiguous?(other) end |
#difference(other) ⇒ Array<Range> Also known as: -
The difference method, -, removes the overlapping part of the other argument from self. Because in the case where self is a superset of the other range, this will result in the difference being two non-contiguous ranges, this returns an array of ranges. If there is no overlap or if self is a subset of the other range, return an array of self
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/fat_core/range.rb', line 189 def difference(other) unless max.respond_to?(:succ) && min.respond_to?(:pred) && other.max.respond_to?(:succ) && other.min.respond_to?(:pred) raise 'Range difference requires objects have pred and succ methods' end # Remove the intersection of self and other from self. The intersection # is either (a) empty, so return self, (b) coincides with self, so # return nothing, isec = self & other return [self] if isec.nil? return [] if isec == self # (c) touches self on the right, (d) touches self on the left, or (e) # touches on neither the left or right, in which case the difference is # two ranges. if isec.max == max && isec.min > min # Return the part to the left of isec [(min..isec.min.pred)] elsif isec.min == min && isec.max < max # Return the part to the right of isec [(isec.max.succ..max)] else # Return the parts to the left and right of isec [(min..isec.min.pred), (isec.max.succ..max)] end end |
#gaps(ranges) ⇒ Array<Range>
If this range is not spanned by the ranges collectively, return an Array
of ranges representing the gaps in coverage. The ranges can over-cover
this range on the left or right without affecting the result, that is,
each range in the returned array of gap ranges will always be subsets of
this range.
If the ranges span this range, return an empty array.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/fat_core/range.rb', line 60 def gaps(ranges) return [clone] if ranges.empty? msg = "#{ranges.first.min.class} range incompatible with #{min.class} Range" raise ArgumentError, msg unless compatible?(ranges) return [] if spanned_by?(ranges) ranges = ranges.select { |r| r.overlaps?(self) }.sort self_is_continuous = ranges.map(&:minmax).flatten.any? { |p| !p.respond_to?(:succ) } gaps = [] cur_point = min ranges.each do |rr| # Loop Invariant: cur_point is the last element in the self Range # NOT covered by the given ranges or the gaps so far. break if cur_point == max if (self_is_continuous && rr.min > cur_point) || (!self_is_continuous && rr.min > cur_point) # There is a gap between the cur_point within self and the start # of this range, rr, so we need to record it. start_point = cur_point end_point = self_is_continuous ? rr.min : rr.min.pred gaps << (start_point..end_point) end cur_point = if rr.max.is_a?(String) && rr.max[-1].match?(/[Zz9]/) # This is a real kludge that stems from the fact that 'z'.succ < # 'z', so the test for gaps at the end of the self range # believes there is a gap when there is none. This ensures that # cur_point is set to something > rr.max when it is one of the # problematic strings ending in 'Z', 'z', or '9', all of whose # successors sort less than them. rr.max + rr.max[-1] else self_is_continuous ? rr.max : rr.max.succ end end # Add any gap between the last of the ranges and the end of self. if cur_point <= max gaps << (cur_point..max) end gaps end |
#intersection(other) ⇒ Range? Also known as: &
Return a Range that represents the intersection between this range and the
other range. If there is no intersection, return nil.
156 157 158 159 160 |
# File 'lib/fat_core/range.rb', line 156 def intersection(other) return unless overlaps?(other) ([min, other.min].max..[max, other.max].min) end |
#join(other) ⇒ Range?
Return a range that concatenates this range with other if it is contiguous with this range on the left or right; return nil if the ranges are not contiguous.
35 36 37 38 39 40 41 |
# File 'lib/fat_core/range.rb', line 35 def join(other) if left_contiguous?(other) ::Range.new(other.min, max) elsif right_contiguous?(other) ::Range.new(min, other.max) end end |
#left_contiguous?(other) ⇒ Boolean
Is other on the left of and contiguous to self? Whether one range is "contiguous" to another has two cases:
- If the elements of the Range on the left respond to the #succ method (that is, its values are discrete values such as Integers or Dates) test whether the succ to the max value of the Range on the left is equal to the min value of the Range on the right.
- If the elements of the Range on the left do not respond to the #succ method (that is, its values are continuous values such as Floats) test whether the max value of the Range on the left is equal to the min value of the Range on the right
259 260 261 262 263 264 265 |
# File 'lib/fat_core/range.rb', line 259 def left_contiguous?(other) if other.max.respond_to?(:succ) other.max.succ == min else other.max == min end end |
#overlaps(ranges) ⇒ Array<Range>
Within this range return an Array of Ranges representing the overlaps
among the given Array of Ranges ranges. If there are no overlaps, return
an empty array. Don't consider overlaps in the ranges that occur outside
of self.
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/fat_core/range.rb', line 115 def overlaps(ranges) return [] if ranges.empty? msg = "#{ranges.first.min.class} range incompatible with #{min.class} Range" raise ArgumentError, msg unless compatible?(ranges) return [] if spanned_by?(ranges) ranges = ranges.sort_by(&:min) overlaps = [] cur_point = nil ranges.each do |rr| # Skip ranges outside of self next if rr.max < min || rr.min > max # Initialize cur_point to max of first range if cur_point.nil? cur_point = rr.max next end # We are on the second or later range if rr.min < cur_point start_point = rr.min end_point = cur_point overlaps << (start_point..end_point) end cur_point = rr.max end overlaps end |
#overlaps?(other) ⇒ Boolean
Return whether self overlaps with other Range.
364 365 366 367 |
# File 'lib/fat_core/range.rb', line 364 def overlaps?(other) cover?(other.min) || cover?(other.max) || other.cover?(min) || other.cover?(max) end |
#overlaps_among?(ranges) ⇒ Boolean
Return whether any of the ranges that overlap self have overlaps among one
another.
This does the same thing as Range.overlaps_among?, except that it filters
the ranges to only those overlapping self before testing for overlaps
among them.
378 379 380 381 |
# File 'lib/fat_core/range.rb', line 378 def overlaps_among?(ranges) iranges = ranges.select { |r| overlaps?(r) } ::Range.overlaps_among?(iranges) end |
#proper_subset_of?(other) ⇒ Boolean
Return whether self is contained within other range, with at most one
boundary touching.
336 337 338 339 |
# File 'lib/fat_core/range.rb', line 336 def proper_subset_of?(other) subset_of?(other) && (min > other.min || max < other.max) end |
#proper_superset_of?(other) ⇒ Boolean
Return whether self contains other range, with at most one
boundary touching.
355 356 357 358 |
# File 'lib/fat_core/range.rb', line 355 def proper_superset_of?(other) superset_of?(other) && (min < other.min || max > other.max) end |
#right_contiguous?(other) ⇒ Boolean
Is other on the right of and contiguous to self? Whether one range is "contiguous" to another has two cases:
- If the elements of the Range on the left respond to the #succ method (that is, its values are discrete values such as Integers or Dates) test whether the succ to the max value of the Range on the left is equal to the min value of the Range on the right.
- If the elements of the Range on the left do not respond to the #succ method (that is, its values are continuous values such as Floats) test whether the max value of the Range on the left is equal to the min value of the Range on the right
287 288 289 290 291 292 293 |
# File 'lib/fat_core/range.rb', line 287 def right_contiguous?(other) if max.respond_to?(:succ) max.succ == other.min else max == other.min end end |
#spanned_by?(ranges) ⇒ Boolean
Return true if the given ranges collectively cover this range without overlaps and without gaps.
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
# File 'lib/fat_core/range.rb', line 388 def spanned_by?(ranges) return empty? if ranges.empty? msg = "#{ranges.first.min.class} range incompatible with #{min.class} Range" raise ArgumentError, msg unless compatible?(ranges) joined_range = nil ranges.sort.each do |r| unless joined_range joined_range = r next end joined_range = joined_range.join(r) break if joined_range.nil? end if joined_range.nil? false else joined_range.min <= min && joined_range.max >= max end end |
#subset_of?(other) ⇒ Boolean
Return whether self is contained within other range, even if their
boundaries touch.
327 328 329 |
# File 'lib/fat_core/range.rb', line 327 def subset_of?(other) min >= other.min && max <= other.max end |
#superset_of?(other) ⇒ Boolean
Return whether self contains other range, even if their
boundaries touch.
346 347 348 |
# File 'lib/fat_core/range.rb', line 346 def superset_of?(other) min <= other.min && max >= other.max end |
#tex_quote ⇒ String
Allow erb or erubis documents to directly interpolate a Range.
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/fat_core/range.rb', line 221 def tex_quote minq = if min.respond_to?(:tex_quote) min.tex_quote else min.to_s end maxq = if max.respond_to?(:tex_quote) max.tex_quote else max.to_s end "(#{minq}..#{maxq})" end |
#union(other) ⇒ Range? Also known as: +
Return a Range that represents the union between this range and the
other range. If there is no overlap and self is not contiguous with
other, return nil.
174 175 176 177 178 |
# File 'lib/fat_core/range.rb', line 174 def union(other) return unless overlaps?(other) || contiguous?(other) ([min, other.min].min..[max, other.max].max) end |