philiprehberger-interval 0.5.0 → 0.7.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 +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +30 -0
- data/lib/philiprehberger/interval/range.rb +21 -0
- data/lib/philiprehberger/interval/version.rb +1 -1
- data/lib/philiprehberger/interval.rb +17 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9c177128b92f238098513a447db1fd1ea888fad019048acb053e334621b9299a
|
|
4
|
+
data.tar.gz: ace5a8129bd130ec72d08412cf014835ee7ea8877627caf3fbce6539455b3be6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dcfc3793178ca911054202f5c6feeea44d952751c47a33cd3e5b9939fe26fd674d20fb2b4f62b687ae6b510e55ad9db64a0151aa285ffdd65205907fa79bf4f9
|
|
7
|
+
data.tar.gz: 05fd282975ef4dd41fdf9bc16990130395ba55aba456d9bf94dfef2df56fcf1d50e4587b8c0f5dcd761325eab9cf7bf0b6fef3d62179f62b29953fed7f01d1ae
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0] - 2026-05-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Interval.span(intervals)` — return the smallest closed interval containing every input interval; complements `merge` (multiple intervals on gaps) and `intersection` (common overlap)
|
|
14
|
+
|
|
15
|
+
## [0.6.0] - 2026-04-21
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `Range#touching?` — strict boundary-touching predicate
|
|
19
|
+
- `Range#length` — alias for `#size`
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- `bug_report.yml` — require Ruby version; add Gem version input per guide
|
|
23
|
+
|
|
10
24
|
## [0.5.0] - 2026-04-16
|
|
11
25
|
|
|
12
26
|
### Added
|
data/README.md
CHANGED
|
@@ -110,6 +110,33 @@ Philiprehberger::Interval.intersection([
|
|
|
110
110
|
# => [5, 8]
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
### Spanning Interval
|
|
114
|
+
|
|
115
|
+
Compute the smallest closed interval that contains every input — useful for charting and bounding-box queries. Spans across gaps too:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
Philiprehberger::Interval.span([
|
|
119
|
+
Philiprehberger::Interval.new(3, 7),
|
|
120
|
+
Philiprehberger::Interval.new(1, 4),
|
|
121
|
+
Philiprehberger::Interval.new(8, 10)
|
|
122
|
+
])
|
|
123
|
+
# => [1, 10]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Touching Intervals
|
|
127
|
+
|
|
128
|
+
Check whether two intervals meet at exactly one point with no overlap and no gap — useful when partitioning a range so every value is covered exactly once:
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
a = Philiprehberger::Interval.new(1, 5, type: :right_open) # [1, 5)
|
|
132
|
+
b = Philiprehberger::Interval.new(5, 10, type: :closed) # [5, 10]
|
|
133
|
+
a.touching?(b) # => true (5 covered once)
|
|
134
|
+
|
|
135
|
+
c = Philiprehberger::Interval.new(1, 5, type: :closed) # [1, 5]
|
|
136
|
+
d = Philiprehberger::Interval.new(5, 10, type: :closed) # [5, 10]
|
|
137
|
+
c.touching?(d) # => false (5 covered twice)
|
|
138
|
+
```
|
|
139
|
+
|
|
113
140
|
### With Time Values
|
|
114
141
|
|
|
115
142
|
```ruby
|
|
@@ -126,11 +153,13 @@ shift.include?(Time.new(2026, 1, 1, 12)) # => true
|
|
|
126
153
|
| `#overlaps?(other)` | Check if two intervals overlap (respects boundary types) |
|
|
127
154
|
| `#contains?(other)` | Check if interval fully contains another |
|
|
128
155
|
| `#adjacent?(other)` | Check if intervals are touching but not overlapping |
|
|
156
|
+
| `#touching?(other)` | Check if intervals meet at a single point with exactly one closed side |
|
|
129
157
|
| `#intersect(other)` | Return the overlap between two intervals |
|
|
130
158
|
| `#overlap_ratio(other)` | Fraction of self covered by other (0.0–1.0) |
|
|
131
159
|
| `#union(other)` | Return the combined interval |
|
|
132
160
|
| `#subtract(other)` | Remove another interval, returning remaining parts |
|
|
133
161
|
| `#size` | Length of the interval |
|
|
162
|
+
| `#length` | Alias for `#size` |
|
|
134
163
|
| `#include?(point)` | Check if a point falls within the interval (respects boundary types) |
|
|
135
164
|
| `#shift(delta)` | Return new interval shifted by delta, preserving type |
|
|
136
165
|
| `#scale(factor, anchor:)` | Scale width around anchor (`:center`, `:left`, `:right`) |
|
|
@@ -140,6 +169,7 @@ shift.include?(Time.new(2026, 1, 1, 12)) # => true
|
|
|
140
169
|
| `.merge(intervals)` | Merge overlapping intervals into non-overlapping set |
|
|
141
170
|
| `.gaps(intervals)` | Find gaps between a set of intervals |
|
|
142
171
|
| `.intersection(intervals)` | Common overlap of a collection of intervals, or `nil` |
|
|
172
|
+
| `.span(intervals)` | Smallest closed interval enclosing all inputs (or `nil` if empty) |
|
|
143
173
|
|
|
144
174
|
## Development
|
|
145
175
|
|
|
@@ -112,6 +112,27 @@ module Philiprehberger
|
|
|
112
112
|
def size
|
|
113
113
|
@finish - @start
|
|
114
114
|
end
|
|
115
|
+
alias length size
|
|
116
|
+
|
|
117
|
+
# Check if this interval strictly touches another at a single endpoint.
|
|
118
|
+
#
|
|
119
|
+
# Returns +true+ iff the two intervals share exactly one endpoint value
|
|
120
|
+
# and exactly one of the two boundaries at that point is closed — so the
|
|
121
|
+
# meeting point is covered exactly once, with no gap and no overlap.
|
|
122
|
+
#
|
|
123
|
+
# @param other [Range] the other interval
|
|
124
|
+
# @return [Boolean]
|
|
125
|
+
def touching?(other)
|
|
126
|
+
return false if overlaps?(other)
|
|
127
|
+
|
|
128
|
+
if @finish == other.start
|
|
129
|
+
right_closed? ^ other.left_closed?
|
|
130
|
+
elsif other.finish == @start
|
|
131
|
+
other.right_closed? ^ left_closed?
|
|
132
|
+
else
|
|
133
|
+
false
|
|
134
|
+
end
|
|
135
|
+
end
|
|
115
136
|
|
|
116
137
|
# Return the fraction of self that is covered by another interval.
|
|
117
138
|
#
|
|
@@ -67,5 +67,22 @@ module Philiprehberger
|
|
|
67
67
|
end
|
|
68
68
|
result
|
|
69
69
|
end
|
|
70
|
+
|
|
71
|
+
# The smallest closed interval that contains every input interval.
|
|
72
|
+
#
|
|
73
|
+
# The result spans from the minimum start to the maximum finish across
|
|
74
|
+
# all inputs and is always returned as a closed interval. Distinct from
|
|
75
|
+
# {.merge} (which yields multiple intervals when there are gaps) and
|
|
76
|
+
# {.intersection} (which yields the common overlap or nil).
|
|
77
|
+
#
|
|
78
|
+
# @param intervals [Array<Range>] the intervals to span
|
|
79
|
+
# @return [Range, nil] the enclosing interval, or `nil` if the input is empty
|
|
80
|
+
def self.span(intervals)
|
|
81
|
+
return nil if intervals.empty?
|
|
82
|
+
|
|
83
|
+
min_start = intervals.map(&:start).min
|
|
84
|
+
max_finish = intervals.map(&:finish).max
|
|
85
|
+
Range.new(min_start, max_finish)
|
|
86
|
+
end
|
|
70
87
|
end
|
|
71
88
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-interval
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Interval data type supporting closed, open, and half-open boundaries
|
|
14
14
|
with overlap detection, containment, intersection, union, subtraction, shift, scale,
|