philiprehberger-interval 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecb8d7c68d3603d44c8df9a87a224c52300377809ad98ab1d19fe1a89ec13449
4
- data.tar.gz: 75b3438103596f8a4de7f358a9f1cc25cd39fe5e80a47dfb6f25d38852e26964
3
+ metadata.gz: 2cc42dea01af0c3d94ccd1473f57540edadacd0da1763cfb29cfcceaca105900
4
+ data.tar.gz: 0455405d87eda7c2a20b913bcd9d26477247dfd69a99adc2f00be29c340aaf5e
5
5
  SHA512:
6
- metadata.gz: 78198caa2a0cf621b3545997e8fdfb58ad7424d7304e7d4367873dd69eaa02db2c91f1ed5a95cd365074be886063fb91941dea72180b2ce4e099c6e1982fddcf
7
- data.tar.gz: 2dbb700fce8cd9cf5d973ab0baabece8e8b94e8d79f71939a805418f7fdfdbf626ca183ae9ab0fc18c2c036f21a2c0de9ddd28d90e1978da02139ac8eb3d1178
6
+ metadata.gz: fd54e350ee506ed02827ad6c23f4eec3d492eadc9b68cee0d4a4608773e1611a799ceb4ca2e56409ba0e27145852fe5c6d8adb168875f93c3818f25e82ef4c55
7
+ data.tar.gz: 626544c2d371d2c1de5a1a1b4f4ac7f181af2b8397ae85168e617b7d77a8445f0f3f6d7e66a01fc81586bce8f144692410d8c0a677d86cffbf44b323bfc4498a
data/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ 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
+
10
15
  ## [0.4.0] - 2026-04-15
11
16
 
12
17
  ### Added
data/README.md CHANGED
@@ -84,6 +84,19 @@ Philiprehberger::Interval.gaps(intervals)
84
84
  # => [[7, 10]]
85
85
  ```
86
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
+
87
100
  ### Finding a Common Intersection
88
101
 
89
102
  Compute the overlap shared by every interval, or `nil` if any pair is disjoint:
@@ -123,6 +136,7 @@ shift.include?(Time.new(2026, 1, 1, 12)) # => true
123
136
  | `#scale(factor, anchor:)` | Scale width around anchor (`:center`, `:left`, `:right`) |
124
137
  | `#split(n)` | Split into n equal sub-intervals |
125
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) |
126
140
  | `.merge(intervals)` | Merge overlapping intervals into non-overlapping set |
127
141
  | `.gaps(intervals)` | Find gaps between a set of intervals |
128
142
  | `.intersection(intervals)` | Common overlap of a collection of intervals, or `nil` |
@@ -186,6 +186,26 @@ module Philiprehberger
186
186
  end
187
187
  end
188
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
+
189
209
  # Clamp a value to the interval bounds.
190
210
  #
191
211
  # @param value [Comparable] the value to clamp
@@ -239,6 +259,22 @@ module Philiprehberger
239
259
  def empty?
240
260
  @start == @finish && @type != :closed
241
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
242
278
  end
243
279
  end
244
280
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module Interval
5
- VERSION = '0.4.0'
5
+ VERSION = '0.5.0'
6
6
  end
7
7
  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.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-15 00:00:00.000000000 Z
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,