philiprehberger-interval 0.3.0 → 0.5.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 +10 -0
- data/README.md +16 -0
- data/lib/philiprehberger/interval/range.rb +53 -0
- data/lib/philiprehberger/interval/version.rb +1 -1
- 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: 2cc42dea01af0c3d94ccd1473f57540edadacd0da1763cfb29cfcceaca105900
|
|
4
|
+
data.tar.gz: 0455405d87eda7c2a20b913bcd9d26477247dfd69a99adc2f00be29c340aaf5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fd54e350ee506ed02827ad6c23f4eec3d492eadc9b68cee0d4a4608773e1611a799ceb4ca2e56409ba0e27145852fe5c6d8adb168875f93c3818f25e82ef4c55
|
|
7
|
+
data.tar.gz: 626544c2d371d2c1de5a1a1b4f4ac7f181af2b8397ae85168e617b7d77a8445f0f3f6d7e66a01fc81586bce8f144692410d8c0a677d86cffbf44b323bfc4498a
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-04-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Range#sample(n = nil)` — return a random Float within the interval, or an array of n random Floats; respects open/closed boundaries via rejection sampling; raises on empty intervals; returns start for point intervals
|
|
14
|
+
|
|
15
|
+
## [0.4.0] - 2026-04-15
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `Range#overlap_ratio(other)` — fraction of self covered by another interval (Float in 0.0–1.0)
|
|
19
|
+
|
|
10
20
|
## [0.3.0] - 2026-04-15
|
|
11
21
|
|
|
12
22
|
### Added
|
data/README.md
CHANGED
|
@@ -36,6 +36,7 @@ a.overlaps?(b) # => true
|
|
|
36
36
|
a.intersect(b) # => [3, 5]
|
|
37
37
|
a.union(b) # => [1, 8]
|
|
38
38
|
a.include?(4) # => true
|
|
39
|
+
a.overlap_ratio(b) # => 0.5
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
### Interval Types
|
|
@@ -83,6 +84,19 @@ Philiprehberger::Interval.gaps(intervals)
|
|
|
83
84
|
# => [[7, 10]]
|
|
84
85
|
```
|
|
85
86
|
|
|
87
|
+
### Sampling Random Values
|
|
88
|
+
|
|
89
|
+
Return one or more random Floats from within an interval. Open boundaries are respected via rejection sampling:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
interval = Philiprehberger::Interval.new(1.0, 5.0)
|
|
93
|
+
interval.sample # => 3.247... (single Float)
|
|
94
|
+
interval.sample(3) # => [1.823..., 4.501..., 2.964...]
|
|
95
|
+
|
|
96
|
+
open = Philiprehberger::Interval.new(1.0, 5.0, type: :open)
|
|
97
|
+
open.sample # => always strictly between 1.0 and 5.0
|
|
98
|
+
```
|
|
99
|
+
|
|
86
100
|
### Finding a Common Intersection
|
|
87
101
|
|
|
88
102
|
Compute the overlap shared by every interval, or `nil` if any pair is disjoint:
|
|
@@ -113,6 +127,7 @@ shift.include?(Time.new(2026, 1, 1, 12)) # => true
|
|
|
113
127
|
| `#contains?(other)` | Check if interval fully contains another |
|
|
114
128
|
| `#adjacent?(other)` | Check if intervals are touching but not overlapping |
|
|
115
129
|
| `#intersect(other)` | Return the overlap between two intervals |
|
|
130
|
+
| `#overlap_ratio(other)` | Fraction of self covered by other (0.0–1.0) |
|
|
116
131
|
| `#union(other)` | Return the combined interval |
|
|
117
132
|
| `#subtract(other)` | Remove another interval, returning remaining parts |
|
|
118
133
|
| `#size` | Length of the interval |
|
|
@@ -121,6 +136,7 @@ shift.include?(Time.new(2026, 1, 1, 12)) # => true
|
|
|
121
136
|
| `#scale(factor, anchor:)` | Scale width around anchor (`:center`, `:left`, `:right`) |
|
|
122
137
|
| `#split(n)` | Split into n equal sub-intervals |
|
|
123
138
|
| `#clamp(value)` | Clamp value to interval bounds |
|
|
139
|
+
| `#sample(n = nil)` | Return a random Float, or array of n Floats, within the interval (respects boundaries) |
|
|
124
140
|
| `.merge(intervals)` | Merge overlapping intervals into non-overlapping set |
|
|
125
141
|
| `.gaps(intervals)` | Find gaps between a set of intervals |
|
|
126
142
|
| `.intersection(intervals)` | Common overlap of a collection of intervals, or `nil` |
|
|
@@ -113,6 +113,23 @@ module Philiprehberger
|
|
|
113
113
|
@finish - @start
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
+
# Return the fraction of self that is covered by another interval.
|
|
117
|
+
#
|
|
118
|
+
# Returns a Float in 0.0..1.0. Returns 0.0 if disjoint, 1.0 if self is
|
|
119
|
+
# fully contained in other. If self has zero length (a point), returns
|
|
120
|
+
# 1.0 when the point is within other, else 0.0.
|
|
121
|
+
#
|
|
122
|
+
# @param other [Range] the other interval
|
|
123
|
+
# @return [Float] the fraction of self covered by other
|
|
124
|
+
def overlap_ratio(other)
|
|
125
|
+
return other.include?(@start) ? 1.0 : 0.0 if size.zero?
|
|
126
|
+
|
|
127
|
+
overlap = intersect(other)
|
|
128
|
+
return 0.0 if overlap.nil?
|
|
129
|
+
|
|
130
|
+
overlap.size.to_f / size
|
|
131
|
+
end
|
|
132
|
+
|
|
116
133
|
# Check if a point is within the interval.
|
|
117
134
|
#
|
|
118
135
|
# @param point [Comparable] the point to check
|
|
@@ -169,6 +186,26 @@ module Philiprehberger
|
|
|
169
186
|
end
|
|
170
187
|
end
|
|
171
188
|
|
|
189
|
+
# Return one or more random Floats within the interval.
|
|
190
|
+
#
|
|
191
|
+
# Without an argument, returns a single random Float. With an integer
|
|
192
|
+
# argument, returns an array of that many random Floats. Respects boundary
|
|
193
|
+
# types: open boundaries are excluded via rejection sampling. Returns
|
|
194
|
+
# +start+ immediately for point intervals (start == finish, closed).
|
|
195
|
+
#
|
|
196
|
+
# @param n [Integer, nil] number of samples, or nil for a single value
|
|
197
|
+
# @return [Float, Array<Float>]
|
|
198
|
+
# @raise [Error] if the interval is empty
|
|
199
|
+
def sample(n = nil)
|
|
200
|
+
raise Error, 'cannot sample an empty interval' if empty?
|
|
201
|
+
|
|
202
|
+
if n.nil?
|
|
203
|
+
sample_one
|
|
204
|
+
else
|
|
205
|
+
Array.new(n) { sample_one }
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
172
209
|
# Clamp a value to the interval bounds.
|
|
173
210
|
#
|
|
174
211
|
# @param value [Comparable] the value to clamp
|
|
@@ -222,6 +259,22 @@ module Philiprehberger
|
|
|
222
259
|
def empty?
|
|
223
260
|
@start == @finish && @type != :closed
|
|
224
261
|
end
|
|
262
|
+
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
def sample_one
|
|
266
|
+
return @start.to_f if @start == @finish
|
|
267
|
+
|
|
268
|
+
lo = @start.to_f
|
|
269
|
+
hi = @finish.to_f
|
|
270
|
+
|
|
271
|
+
loop do
|
|
272
|
+
value = lo + (rand * (hi - lo))
|
|
273
|
+
left_ok = left_closed? ? value >= lo : value > lo
|
|
274
|
+
right_ok = right_closed? ? value <= hi : value < hi
|
|
275
|
+
return value if left_ok && right_ok
|
|
276
|
+
end
|
|
277
|
+
end
|
|
225
278
|
end
|
|
226
279
|
end
|
|
227
280
|
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.5.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-04-
|
|
11
|
+
date: 2026-04-17 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,
|