range_set_blaze/float/not_nan.rs
1//! `NotNan` is a floating point type, suitable for use in ranges. Every value except NaN is
2//! valid, including `+infinity` and `-infinity`.
3//!
4//! Ordering and other semantics are as per normal floating point comparisons.
5//!
6//! The `NotNanF32`/`NotNanF64` wrappers are available by default. Enable
7//! `float_nightly_experimental` on nightly to add `NotNanF16`/`NotNanF128`.
8
9use core::{
10 cmp::Ordering,
11 fmt::Debug,
12 hash::{Hash, Hasher},
13 mem,
14 ops::RangeInclusive,
15 slice::from_raw_parts,
16};
17
18use super::not_nan_float::NotNanFloat;
19
20use crate::Integer;
21#[cfg(feature = "from_slice")]
22use crate::RangeSetBlaze;
23use num_traits::Zero;
24
25/// Total ordered f64, with `-0.0` normalized to `+0.0`, and excluding NaN.
26pub type NotNanF64 = NotNan<f64>;
27/// Total ordered f32, with `-0.0` normalized to `+0.0`, and excluding NaN.
28pub type NotNanF32 = NotNan<f32>;
29/// Total ordered f16, with `-0.0` normalized to `+0.0`, and excluding NaN.
30#[cfg(feature = "float_nightly_experimental")]
31pub type NotNanF16 = NotNan<f16>;
32/// Total ordered f128, with `-0.0` normalized to `+0.0`, and excluding NaN.
33#[cfg(feature = "float_nightly_experimental")]
34pub type NotNanF128 = NotNan<f128>;
35
36/// Construct a [`NotNanF64`] from an `f64`. Shorthand for [`NotNanF64::new`]
37#[must_use]
38pub const fn nnf64(x: f64) -> NotNanF64 {
39 not_nan_f64(x)
40}
41
42/// Construct a [`NotNanF32`] from an `f32`. Shorthand for [`NotNanF32::new`]
43#[must_use]
44pub const fn nnf32(x: f32) -> NotNanF32 {
45 not_nan_f32(x)
46}
47
48/// Construct a [`NotNanF16`] from an `f16`. Shorthand for [`NotNanF16::new`]
49#[cfg(feature = "float_nightly_experimental")]
50#[must_use]
51pub const fn nnf16(x: f16) -> NotNanF16 {
52 not_nan_f16(x)
53}
54
55/// Construct a [`NotNanF128`] from an `f128`. Shorthand for [`NotNanF128::new`]
56#[cfg(feature = "float_nightly_experimental")]
57#[must_use]
58pub const fn nnf128(x: f128) -> NotNanF128 {
59 not_nan_f128(x)
60}
61
62// TODO When const trait methods are stable, make the generic NotNan constructors and other
63// eligible methods const, then have these shorthands call NotNan::new directly. That will also
64// let their negative-zero normalization share `NotNanFloat::normalize` with runtime paths.
65macro_rules! not_nan_const_constructor {
66 ($name:ident, $primitive:ty, $not_nan:ty) => {
67 const fn $name(x: $primitive) -> $not_nan {
68 assert!(!x.is_nan(), "NotNan type requires a non-NaN value");
69 let normalized = if x == 0.0 && x.is_sign_negative() {
70 0.0
71 } else {
72 x
73 };
74 NotNan(normalized)
75 }
76 };
77}
78
79not_nan_const_constructor!(not_nan_f64, f64, NotNanF64);
80not_nan_const_constructor!(not_nan_f32, f32, NotNanF32);
81#[cfg(feature = "float_nightly_experimental")]
82not_nan_const_constructor!(not_nan_f16, f16, NotNanF16);
83#[cfg(feature = "float_nightly_experimental")]
84not_nan_const_constructor!(not_nan_f128, f128, NotNanF128);
85
86/// A transparent wrapper around [`f64`] and friends with total ordering.
87///
88/// Comparison, equality, and hashing all agree with `total_cmp` after zero normalization.
89/// Every value except NaN is legal, including `+infinity` and `-infinity`.
90///
91/// # Basic Usage
92/// ```
93/// use range_set_blaze::{RangeSetBlaze, NotNanF64, NotNanF32};
94/// let set = RangeSetBlaze::from_iter([NotNanF64::new(3.0)..=NotNanF64::new(5.0)]);
95/// assert!(set.contains(NotNanF64::new(3.1)));
96/// assert!(!set.contains(NotNanF64::new(2.9)));
97///
98/// let set = RangeSetBlaze::from(NotNanF64::from_primitive_range(3.0..=5.0));
99/// assert!(set.contains(NotNanF64::new(4.9)));
100/// assert!(!set.contains(NotNanF64::new(5.1)));
101///
102/// let set = RangeSetBlaze::from_iter(NotNanF32::from_primitive_ranges([3.0..=5.0, 7.0..=9.0]));
103/// assert!(set.contains(NotNanF32::new(4.0)));
104/// assert!(!set.contains(NotNanF32::new(6.0)));
105/// ```
106///
107/// # The Full Non-NaN Domain
108///
109/// The primitive `-∞..=+∞` range converts to `NotNanF64::MIN..=NotNanF64::MAX`,
110/// the complete ordered domain of legal values: every non-NaN `f64`, including
111/// both infinities, is in the range.
112///
113/// ```
114/// use range_set_blaze::{NotNanF64, RangeSetBlaze};
115///
116/// assert_eq!(NotNanF64::MIN, NotNanF64::new(f64::NEG_INFINITY));
117/// assert_eq!(NotNanF64::MAX, NotNanF64::new(f64::INFINITY));
118///
119/// let primitive_domain =
120/// NotNanF64::from_primitive_range(f64::NEG_INFINITY..=f64::INFINITY);
121/// let full_domain = NotNanF64::MIN..=NotNanF64::MAX;
122/// assert_eq!(primitive_domain, full_domain);
123///
124/// let full_domain = RangeSetBlaze::from(full_domain);
125/// assert!(full_domain.contains(NotNanF64::new(f64::NEG_INFINITY)));
126/// assert!(full_domain.contains(NotNanF64::new(-42.0)));
127/// assert!(full_domain.contains(NotNanF64::new(0.0)));
128/// assert!(full_domain.contains(NotNanF64::new(42.0)));
129/// assert!(full_domain.contains(NotNanF64::new(f64::INFINITY)));
130/// ```
131///
132/// The stable `NotNanF32` and `NotNanF64` types are available by default.
133/// On nightly, enable `float_nightly_experimental` to also use the
134/// `NotNanF16` and `NotNanF128` types.
135#[repr(transparent)]
136#[derive(Copy, Clone, Default, Debug)]
137pub struct NotNan<T: NotNanFloat>(T);
138
139impl<T: NotNanFloat> NotNan<T> {
140 /// The minimum value that can be represented by the type: negative infinity.\
141 /// Maps directly to `crate::Integer::min_value()`
142 ///
143 /// # Examples
144 /// ```
145 /// use range_set_blaze::NotNanF64;
146 ///
147 /// assert_eq!(NotNanF64::MIN, NotNanF64::new(f64::NEG_INFINITY));
148 /// ```
149 pub const MIN: Self = Self(T::MIN);
150
151 /// The maximum value that can be represented by the type: positive infinity.\
152 /// Maps directly to [`crate::Integer::max_value()`]
153 ///
154 /// # Examples
155 /// ```
156 /// use range_set_blaze::NotNanF64;
157 ///
158 /// assert_eq!(NotNanF64::MAX, NotNanF64::new(f64::INFINITY));
159 /// ```
160 pub const MAX: Self = Self(T::MAX);
161
162 /// The maximum possible size of a range, i.e. the size if `[MIN..=MAX]`
163 /// For `NotNan` types, this is unusual because NaN values are excluded, and
164 /// `-0.0` and `+0.0` share one slot after normalization.
165 ///
166 /// # Examples
167 /// ```
168 /// use range_set_blaze::NotNanF32;
169 ///
170 /// assert_eq!(NotNanF32::MAX_SIZE, 0xFF00_0000_u32 + 1);
171 /// ```
172 pub const MAX_SIZE: T::SafeLen = T::MAX_SIZE;
173
174 /// Creates a new [`NotNan`] from a primitive float.
175 /// Any value except NaN is legal, including the infinities.
176 ///
177 /// # Examples
178 /// ```
179 /// use range_set_blaze::NotNanF64;
180 ///
181 /// let _ = NotNanF64::new(1.0);
182 /// let _ = NotNanF64::new(f64::INFINITY);
183 /// ```
184 /// # Panics
185 ///
186 /// Panics if `x` is NaN.
187 #[must_use]
188 pub fn new(x: T) -> Self {
189 Self::try_new(x).expect("NotNan type requires a non-NaN value")
190 }
191
192 /// Creates a new [`NotNan`] from a primitive float.
193 ///
194 /// Returns `None` if the float is NaN.
195 ///
196 /// # Examples
197 /// ```
198 /// use range_set_blaze::NotNanF64;
199 ///
200 /// assert_eq!(NotNanF64::try_new(1.0), Some(NotNanF64::new(1.0)));
201 /// assert_eq!(NotNanF64::try_new(f64::INFINITY), Some(NotNanF64::new(f64::INFINITY)));
202 /// assert_eq!(NotNanF64::try_new(f64::NAN), None);
203 /// ```
204 #[must_use]
205 pub fn try_new(x: T) -> Option<Self> {
206 // SAFETY: `!T::is_nan` rules out NaN, and `T::normalize` canonicalizes -0.0.
207 (!T::is_nan(x)).then(|| unsafe { Self::new_unchecked(T::normalize(x)) })
208 }
209
210 /// Creates a new [`NotNan`] from a primitive float without validating it.
211 ///
212 /// This is the unchecked building block every validating constructor in this module
213 /// (`new`, `try_new`, `from_primitive_range`, `values`,
214 /// `from_primitive_slice`, ...) is defined in terms
215 /// of. Prefer those; only reach for this when you have already independently established
216 /// the safety precondition below and need to skip the redundant check.
217 ///
218 /// # Safety
219 ///
220 /// The caller must guarantee that:
221 /// - `x` is not NaN.
222 /// - `x` is not `-0.0`: zero must already be canonicalized to `+0.0`.
223 ///
224 /// [`NotNan`] has a public type invariant ("only non-NaN values, with zero canonicalized to
225 /// `+0.0`, are legal"). Even though today's implementation would only produce incorrect
226 /// results (wrong `MAX_SIZE`, a duplicated zero slot, `after`/`before` landing somewhere
227 /// unexpected) rather than immediate undefined behavior if this precondition is violated,
228 /// safe code must never be able to construct a value that breaks it. This preserves the
229 /// option for this crate, and downstream code, to rely on the invariant in future
230 /// (potentially unsafe) abstractions without an audit of every safe caller.
231 #[must_use]
232 pub const unsafe fn new_unchecked(x: T) -> Self {
233 Self(x)
234 }
235
236 /// Computes `self + (b - 1)` where `b` is of type `SafeLen`.
237 ///
238 /// # Panics
239 /// Panics if `b` is not small enough that the result stays within range for `T`
240 /// (checked unconditionally, in both debug and release builds, so safe code can
241 /// never construct a `NotNan` value that breaks its invariant this way).
242 #[must_use]
243 pub fn inclusive_end_from_start(self, b: T::SafeLen) -> Self {
244 let max_len = T::prim_safe_len(self.0, T::MAX);
245 assert!(
246 !b.is_zero() && b <= max_len,
247 "b must be in range 1..=max_len"
248 );
249 Self(T::inclusive_end_from_start(self.0, b))
250 }
251
252 /// Computes `self - (b - 1)` where `b` is of type `SafeLen`.
253 ///
254 /// # Panics
255 /// Panics if `b` is not small enough that the result stays within range for `T`
256 /// (checked unconditionally, in both debug and release builds, so safe code can
257 /// never construct a `NotNan` value that breaks its invariant this way).
258 #[must_use]
259 pub fn start_from_inclusive_end(self, b: T::SafeLen) -> Self {
260 let max_len = T::prim_safe_len(T::MIN, self.0);
261 assert!(
262 !b.is_zero() && b <= max_len,
263 "b must be in range 1..=max_len"
264 );
265 Self(T::start_from_inclusive_end(self.0, b))
266 }
267
268 /// Returns the wrapped value.
269 ///
270 /// # Examples
271 /// ```
272 /// use range_set_blaze::NotNanF64;
273 ///
274 /// assert_eq!(NotNanF64::new(42.0).into_inner(), 42.0);
275 /// ```
276 #[must_use]
277 pub const fn into_inner(self) -> T {
278 self.0
279 }
280
281 /// Returns the next float, in total order.
282 ///
283 /// # Examples
284 /// ```
285 /// use range_set_blaze::NotNanF64;
286 ///
287 /// assert_eq!(NotNanF64::new(42.0).after().before().into_inner(), 42.0);
288 /// ```
289 ///
290 /// # Panics
291 ///
292 /// Panics if `self` is the maximum value (checked unconditionally, in both debug
293 /// and release builds, so safe code can never construct a `NotNan` value that
294 /// breaks its invariant this way).
295 #[must_use]
296 pub fn after(self) -> Self {
297 assert!(self != Self::MAX, "after() called on maximum value");
298 Self(T::normalize(T::after(self.0)))
299 }
300
301 /// Returns the previous float, in total order.
302 ///
303 /// # Examples
304 /// ```
305 /// use range_set_blaze::NotNanF64;
306 ///
307 /// assert_eq!(NotNanF64::new(42.0).before().after().into_inner(), 42.0);
308 /// ```
309 ///
310 /// # Panics
311 ///
312 /// Panics if `self` is the minimum value (checked unconditionally, in both debug
313 /// and release builds, so safe code can never construct a `NotNan` value that
314 /// breaks its invariant this way).
315 #[must_use]
316 pub fn before(self) -> Self {
317 assert!(self != Self::MIN, "before() called on minimum value");
318 Self(T::normalize(T::before(self.0)))
319 }
320
321 /// Returns the next float, in total order.
322 ///
323 /// Returns [`None`] if `self` is the maximum value.
324 ///
325 /// # Examples
326 /// ```
327 /// use range_set_blaze::NotNanF64;
328 ///
329 /// let value = NotNanF64::new(42.0);
330 /// assert_eq!(value.checked_after(), Some(value.after()));
331 /// let value = NotNanF64::MAX;
332 /// assert_eq!(value.checked_after(), None);
333 /// ```
334 #[must_use]
335 pub fn checked_after(self) -> Option<Self> {
336 if self == Self::MAX {
337 None
338 } else {
339 Some(self.after())
340 }
341 }
342
343 /// Returns the previous float, in total order.
344 ///
345 /// Returns [`None`] if `self` is the minimum value.
346 ///
347 /// # Examples
348 /// ```
349 /// use range_set_blaze::NotNanF64;
350 ///
351 /// let value = NotNanF64::new(42.0);
352 /// assert_eq!(value.checked_before(), Some(value.before()));
353 /// let value = NotNanF64::MIN;
354 /// assert_eq!(value.checked_before(), None);
355 /// ```
356 #[must_use]
357 pub fn checked_before(self) -> Option<Self> {
358 if self == Self::MIN {
359 None
360 } else {
361 Some(self.before())
362 }
363 }
364
365 /// Converts an inclusive primitive range into an inclusive [`NotNan`] range.
366 ///
367 /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
368 ///
369 ///
370 /// # Examples
371 /// ```
372 /// use range_set_blaze::{RangeSetBlaze, NotNanF64};
373 ///
374 /// let short = RangeSetBlaze::from(NotNanF64::from_primitive_range(3.0..=5.0));
375 /// let long = RangeSetBlaze::from(NotNanF64::new(3.0)..=NotNanF64::new(5.0));
376 /// assert_eq!(short, long);
377 /// ```
378 /// # Panics
379 ///
380 /// Panics if `start` or `end` is NaN.
381 #[must_use]
382 pub fn from_primitive_range(range: RangeInclusive<T>) -> RangeInclusive<Self> {
383 let (start, end) = range.into_inner();
384 Self::new(start)..=Self::new(end)
385 }
386
387 /// Converts inclusive primitive ranges into inclusive [`NotNan`] ranges.
388 ///
389 /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
390 ///
391 ///
392 /// # Examples
393 /// ```
394 /// use range_set_blaze::{RangeSetBlaze, NotNanF64};
395 ///
396 /// let short = RangeSetBlaze::from_iter(NotNanF64::from_primitive_ranges([1.0..=2.0, 3.0..=4.0]));
397 /// let long = RangeSetBlaze::from_iter([NotNanF64::new(1.0)..=NotNanF64::new(2.0), NotNanF64::new(3.0)..=NotNanF64::new(4.0)]);
398 /// assert_eq!(short, long);
399 /// ```
400 /// # Panics
401 ///
402 /// Panics when the returned iterator is consumed if any range endpoint is NaN.
403 pub fn from_primitive_ranges<I>(ranges: I) -> impl Iterator<Item = RangeInclusive<Self>>
404 where
405 I: IntoIterator<Item = RangeInclusive<T>>,
406 {
407 ranges.into_iter().map(Self::from_primitive_range)
408 }
409
410 /// Convenience method to convert primitive values into ordered [`NotNan`] values.
411 /// # Examples
412 /// ```
413 /// use range_set_blaze::{RangeSetBlaze, NotNanF64};
414 ///
415 /// let short = RangeSetBlaze::from_iter(NotNanF64::values([1.0, 2.0, 3.0, 4.0]));
416 /// let long = RangeSetBlaze::from_iter([NotNanF64::new(1.0), NotNanF64::new(2.0), NotNanF64::new(3.0), NotNanF64::new(4.0)]);
417 /// assert_eq!(short, long);
418 /// ```
419 ///
420 /// # Panics
421 ///
422 /// Panics (when iterated) if any value is NaN.
423 pub fn values<I>(values: I) -> impl Iterator<Item = Self>
424 where
425 I: IntoIterator<Item = T>,
426 {
427 values.into_iter().map(Self::new)
428 }
429
430 /// Views primitive values as ordered [`NotNan`] values, validating as it goes.
431 ///
432 /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
433 ///
434 ///
435 /// This runs in `O(n)` (to validate every element) and does not allocate.
436 /// # Examples
437 /// ```
438 /// use range_set_blaze::{RangeSetBlaze, NotNanF64};
439 ///
440 /// let short = RangeSetBlaze::from_iter(NotNanF64::from_primitive_slice(&[1.0, 2.0, 3.0, 4.0]));
441 /// let long = RangeSetBlaze::from_iter([NotNanF64::new(1.0), NotNanF64::new(2.0), NotNanF64::new(3.0), NotNanF64::new(4.0)]);
442 /// assert_eq!(short, long);
443 /// ```
444 ///
445 /// # Panics
446 ///
447 /// Panics if any element is NaN, or is `-0.0` (which can't be normalized to `+0.0`
448 /// without copying — see [`NotNan::from_primitive_slice_unchecked`] if you need a true
449 /// zero-copy view and can guarantee your data already satisfies [`NotNan`]'s invariant).
450 #[must_use]
451 pub fn from_primitive_slice(values: &[T]) -> &[Self] {
452 assert!(
453 values.iter().all(|&v| !T::is_nan(v) && !T::is_neg_zero(v)),
454 "NotNan type requires non-NaN, non-negative-zero values"
455 );
456 // SAFETY: just validated every element is not NaN and not -0.0.
457 unsafe { Self::from_primitive_slice_unchecked(values) }
458 }
459
460 /// Views primitive values as ordered [`NotNan`] values, without validating them.
461 ///
462 /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
463 ///
464 ///
465 /// This runs in `O(1)` and does not allocate.
466 ///
467 /// # Safety
468 ///
469 /// The caller must guarantee that every element of `values` is not NaN and not `-0.0`
470 /// (zero must already be canonicalized to `+0.0`). Because the returned slice is a live
471 /// view over the same memory (not a copy), there is no opportunity to normalize `-0.0`
472 /// even if the caller wanted to; the data must already be clean.
473 ///
474 /// [`NotNan`] has a public type invariant that safe code must never be able to break, even
475 /// though violating it today would only produce incorrect results (see
476 /// [`NotNan::new_unchecked`] for the full rationale).
477 #[must_use]
478 pub const unsafe fn from_primitive_slice_unchecked(values: &[T]) -> &[Self] {
479 // SAFETY: NotNan is #[repr(transparent)] over T, making `&[T]`
480 // and `&[NotNan]` entirely interchangeable in layout and lifetimes; the caller is
481 // responsible for the value-level invariant per the safety doc above.
482 unsafe { mem::transmute::<&[T], &[Self]>(values) }
483 }
484}
485
486/// Extension trait for viewing a slice of [`NotNan`] values as primitive values.
487pub trait NotNanSliceExt<T: NotNanFloat> {
488 /// Views [`NotNan`] values as primitive values.
489 ///
490 /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
491 ///
492 ///
493 /// This runs in `O(1)` and does not allocate.
494 /// # Examples
495 /// ```
496 /// use range_set_blaze::NotNanF64;
497 /// use range_set_blaze::not_nan::NotNanSliceExt;
498 ///
499 /// let not_nans = [NotNanF64::new(1.0), NotNanF64::new(2.0), NotNanF64::new(3.0)];
500 /// assert_eq!(&[1.0, 2.0, 3.0], not_nans.as_primitive_slice());
501 /// ```
502 fn as_primitive_slice(&self) -> &[T];
503}
504
505impl<T: NotNanFloat> NotNanSliceExt<T> for [NotNan<T>] {
506 fn as_primitive_slice(&self) -> &[T] {
507 // SAFETY: NotNan<T> is #[repr(transparent)] over T, making `&[T]`
508 // and `&[NotNan<T>]` entirely interchangeable in layout and lifetimes.
509 unsafe { from_raw_parts(self.as_ptr().cast::<T>(), self.len()) }
510 }
511}
512
513/// Extension trait for converting an inclusive [`NotNan`] range into an inclusive primitive
514/// range (or a `(start, end)` primitive tuple).
515pub trait NotNanRangeExt<T: NotNanFloat> {
516 /// Converts an inclusive [`NotNan`] range into an inclusive primitive range.
517 ///
518 /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
519 ///
520 ///
521 /// This is the reverse of [`NotNan::from_primitive_range`].
522 ///
523 /// # Examples
524 /// ```
525 /// use range_set_blaze::NotNanF64;
526 /// use range_set_blaze::not_nan::NotNanRangeExt;
527 ///
528 /// let range = NotNanF64::new(3.0)..=NotNanF64::new(5.0);
529 /// assert_eq!(range.into_primitive_range(), 3.0..=5.0);
530 /// ```
531 #[must_use]
532 fn into_primitive_range(self) -> RangeInclusive<T>;
533
534 /// Converts an inclusive [`NotNan`] range into a `(start, end)` tuple of primitive values.
535 ///
536 /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
537 ///
538 ///
539 /// Mirrors [`RangeInclusive::into_inner`] from the standard library, which unwraps a
540 /// range into its `(start, end)` tuple; this additionally converts each endpoint to its
541 /// primitive type.
542 ///
543 /// # Examples
544 /// ```
545 /// use range_set_blaze::NotNanF64;
546 /// use range_set_blaze::not_nan::NotNanRangeExt;
547 ///
548 /// let range = NotNanF64::new(3.0)..=NotNanF64::new(5.0);
549 /// assert_eq!(range.into_primitive_inner(), (3.0, 5.0));
550 /// ```
551 #[must_use]
552 fn into_primitive_inner(self) -> (T, T);
553}
554
555impl<T: NotNanFloat> NotNanRangeExt<T> for RangeInclusive<NotNan<T>> {
556 fn into_primitive_range(self) -> RangeInclusive<T> {
557 let (start, end) = self.into_primitive_inner();
558 start..=end
559 }
560
561 fn into_primitive_inner(self) -> (T, T) {
562 let (start, end) = self.into_inner();
563 (start.into_inner(), end.into_inner())
564 }
565}
566
567impl<T: NotNanFloat> PartialEq for NotNan<T> {
568 fn eq(&self, other: &Self) -> bool {
569 T::total_cmp(self.0, other.0) == Ordering::Equal
570 }
571}
572
573impl<T: NotNanFloat> Eq for NotNan<T> {}
574
575impl<T: NotNanFloat> PartialOrd for NotNan<T> {
576 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
577 Some(self.cmp(other))
578 }
579}
580
581impl<T: NotNanFloat> Ord for NotNan<T> {
582 fn cmp(&self, other: &Self) -> Ordering {
583 T::total_cmp(self.0, other.0)
584 }
585}
586
587impl<T: NotNanFloat> Hash for NotNan<T> {
588 fn hash<H: Hasher>(&self, state: &mut H) {
589 T::hash(self.0, state);
590 }
591}
592
593impl<T: NotNanFloat> Integer for NotNan<T> {
594 type SafeLen = T::SafeLen;
595
596 #[inline]
597 fn checked_add_one(self) -> Option<Self> {
598 self.checked_after()
599 }
600
601 // This moves to the next representable float in total_cmp order, not a numeric + 1.0.
602 #[inline]
603 fn add_one(self) -> Self {
604 self.after()
605 }
606
607 #[inline]
608 // This moves to the previous representable float in total_cmp order, not a numeric - 1.0.
609 fn sub_one(self) -> Self {
610 self.before()
611 }
612
613 #[inline]
614 fn assign_sub_one(&mut self) {
615 *self = self.before();
616 }
617
618 // Ideally, we would `impl std::iter::Step for NotNanF64` and just call Range::next(), but that's still experimental.
619 #[inline]
620 fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self> {
621 if range.is_empty() {
622 None
623 } else if range.start() == range.end() && *range.start() == Self::MAX {
624 // Preserve the exhausted range sentinel without calling `after()` on MAX.
625 let next = *range.start();
626 *range = next..=range.end().before();
627 Some(next)
628 } else {
629 let next = *range.start();
630 *range = (next.after())..=*range.end();
631 Some(next)
632 }
633 }
634
635 #[inline]
636 fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self> {
637 if range.is_empty() {
638 None
639 } else if range.start() == range.end() && *range.start() == Self::MIN {
640 // Preserve the exhausted range sentinel without calling `before()` on MIN.
641 let last = *range.end();
642 *range = last.after()..=last;
643 Some(last)
644 } else {
645 let last = *range.end();
646 *range = *range.start()..=last.before();
647 Some(last)
648 }
649 }
650
651 #[inline]
652 fn min_value() -> Self {
653 Self::MIN
654 }
655
656 #[inline]
657 fn max_value() -> Self {
658 Self::MAX
659 }
660
661 #[cfg(feature = "from_slice")]
662 #[inline]
663 fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self> {
664 // TODO Investigate applying the ordered float transform in SIMD chunks here.
665 // no way to do the fancy thing
666 RangeSetBlaze::from_iter(slice.as_ref())
667 }
668
669 fn safe_len(r: &RangeInclusive<Self>) -> Self::SafeLen {
670 let (start, end) = r.clone().into_primitive_inner();
671 T::prim_safe_len(start, end)
672 }
673
674 fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
675 T::safe_len_to_f64_lossy(len)
676 }
677
678 fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
679 T::f64_to_safe_len_lossy(f)
680 }
681
682 fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self {
683 self.inclusive_end_from_start(b)
684 }
685
686 fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self {
687 self.start_from_inclusive_end(b)
688 }
689}
690
691#[cfg(test)]
692mod tests {
693 use super::*;
694 use crate::Integer;
695 #[cfg(not(target_arch = "wasm32"))]
696 use std::hint::black_box;
697 #[cfg(not(target_arch = "wasm32"))]
698 use std::panic::{AssertUnwindSafe, catch_unwind};
699 use std::vec;
700 use std::vec::Vec;
701
702 #[cfg(not(target_arch = "wasm32"))]
703 fn panics(f: impl FnOnce()) -> bool {
704 catch_unwind(AssertUnwindSafe(f)).is_err()
705 }
706
707 // WASM targets currently abort instead of unwinding, so `catch_unwind`
708 // cannot observe the expected constructor panics there.
709 #[cfg(not(target_arch = "wasm32"))]
710 #[test]
711 #[allow(clippy::float_cmp)]
712 fn safe_constructors_preserve_not_nan_invariant() {
713 assert_eq!(nnf32(-0.0).into_inner().to_bits(), 0);
714 assert_eq!(nnf64(-0.0).into_inner().to_bits(), 0);
715 assert_eq!(NotNanF64::new(-0.0), nnf64(0.0));
716 assert_eq!(NotNanF64::try_new(-0.0), Some(nnf64(0.0)));
717
718 // Infinities are legal values, not rejected.
719 for value in [f64::INFINITY, f64::NEG_INFINITY] {
720 assert_eq!(NotNanF64::new(value).into_inner(), value);
721 assert_eq!(NotNanF64::try_new(value), Some(NotNanF64::new(value)));
722 }
723
724 for invalid in [f64::NAN, -f64::NAN] {
725 assert!(panics(|| {
726 black_box(NotNanF64::new(invalid));
727 }));
728 assert_eq!(NotNanF64::try_new(invalid), None);
729 assert!(panics(|| drop(NotNanF64::from_primitive_range(
730 invalid..=1.0
731 ))));
732 assert!(panics(|| {
733 NotNanF64::values([invalid]).count();
734 }));
735 assert!(panics(|| {
736 black_box(NotNanF64::from_primitive_slice(&[invalid]));
737 }));
738 }
739
740 assert!(panics(|| {
741 black_box(NotNanF64::from_primitive_slice(&[-0.0]));
742 }));
743 assert!(panics(|| {
744 black_box(NotNanF64::from_primitive_slice(&[f64::NAN]));
745 }));
746
747 let values = [1.0, 2.0, 3.0];
748 let not_nans = NotNanF64::from_primitive_slice(&values);
749 assert_eq!(not_nans.as_primitive_slice(), &values);
750 assert_eq!(
751 NotNanF64::values(values).collect::<Vec<_>>(),
752 vec![nnf64(1.0), nnf64(2.0), nnf64(3.0)]
753 );
754 assert_eq!(
755 NotNanF64::from_primitive_ranges([1.0..=2.0]).collect::<Vec<_>>(),
756 vec![nnf64(1.0)..=nnf64(2.0)]
757 );
758 }
759
760 #[test]
761 fn ordering_agrees_with_total_cmp() {
762 let values = [
763 f64::NEG_INFINITY,
764 -f64::MAX,
765 -1.0,
766 0.0,
767 1.0,
768 f64::MAX,
769 f64::INFINITY,
770 ];
771
772 for left in values {
773 for right in values {
774 assert_eq!(nnf64(left).cmp(&nnf64(right)), left.total_cmp(&right));
775 }
776 }
777 assert_ne!(nnf64(0.0).cmp(&nnf64(-0.0)), 0.0_f64.total_cmp(&-0.0));
778 }
779
780 #[test]
781 fn converts_ranges() {
782 assert_eq!(
783 NotNanF64::from_primitive_range(10.0..=20.0),
784 nnf64(10.0)..=nnf64(20.0)
785 );
786 assert_eq!(
787 NotNanF64::from_primitive_ranges([10.0..=20.0, 30.0..=40.0]).collect::<Vec<_>>(),
788 vec![nnf64(10.0)..=nnf64(20.0), nnf64(30.0)..=nnf64(40.0)]
789 );
790 }
791
792 #[test]
793 fn after_and_before_step_through_zero_in_total_order() {
794 assert_eq!(nnf64(-0.0), nnf64(0.0));
795 assert_ne!(nnf64(0.0).before(), nnf64(-0.0));
796 assert_eq!(nnf64(0.0).after(), nnf64(f64::from_bits(1)));
797 assert_eq!(
798 nnf64(0.0).before(),
799 nnf64(f64::from_bits(0x8000_0000_0000_0001))
800 );
801 }
802
803 #[test]
804 fn after_and_before_panic_at_boundaries_in_all_build_modes() {
805 assert_eq!(NotNanF64::MAX.checked_after(), None);
806 assert_eq!(NotNanF64::MIN.checked_before(), None);
807 }
808
809 #[test]
810 #[should_panic(expected = "b must be in range 1..=max_len")]
811 fn not_nan_endpoint_offset_cannot_leave_domain() {
812 let _ = NotNanF32::MAX.inclusive_end_from_start(2);
813 }
814
815 #[test]
816 #[should_panic(expected = "after() called on maximum value")]
817 fn after_panics_at_max() {
818 let _ = NotNanF64::MAX.after();
819 }
820
821 #[test]
822 #[should_panic(expected = "before() called on minimum value")]
823 fn before_panics_at_min() {
824 let _ = NotNanF64::MIN.before();
825 }
826
827 #[test]
828 fn checked_after_and_before_stop_at_total_order_boundaries() {
829 assert_eq!(NotNanF64::MIN.checked_before(), None);
830 assert_eq!(NotNanF64::MAX.checked_after(), None);
831 assert_eq!(NotNanF64::MIN.checked_after(), Some(NotNanF64::MIN.after()));
832 assert_eq!(
833 NotNanF64::MAX.checked_before(),
834 Some(NotNanF64::MAX.before())
835 );
836 }
837
838 #[test]
839 fn min_and_max_are_total_order_boundaries() {
840 let values = [
841 nnf64(-f64::MAX),
842 nnf64(-1.0),
843 nnf64(-0.0),
844 nnf64(0.0),
845 nnf64(1.0),
846 nnf64(f64::MAX),
847 ];
848
849 for value in values {
850 assert!(NotNanF64::MIN <= value);
851 assert!(value <= NotNanF64::MAX);
852 }
853 }
854
855 /// `MIN`/`MAX` are the infinities, and they sit directly adjacent (in total order) to the
856 /// largest-magnitude finite values -- exactly the values reached by stepping `.before()`/
857 /// `.after()` once, and nowhere else.
858 #[test]
859 fn infinities_are_adjacent_to_finite_extremes() {
860 assert_eq!(NotNanF64::MIN, nnf64(f64::NEG_INFINITY));
861 assert_eq!(NotNanF64::MAX, nnf64(f64::INFINITY));
862 assert_eq!(NotNanF64::MIN.after(), nnf64(f64::MIN));
863 assert_eq!(NotNanF64::MAX.before(), nnf64(f64::MAX));
864 assert_eq!(nnf64(f64::MIN).before(), NotNanF64::MIN);
865 assert_eq!(nnf64(f64::MAX).after(), NotNanF64::MAX);
866
867 assert_eq!(NotNanF32::MIN, nnf32(f32::NEG_INFINITY));
868 assert_eq!(NotNanF32::MAX, nnf32(f32::INFINITY));
869 assert_eq!(NotNanF32::MIN.after(), nnf32(f32::MIN));
870 assert_eq!(NotNanF32::MAX.before(), nnf32(f32::MAX));
871 assert_eq!(nnf32(f32::MIN).before(), NotNanF32::MIN);
872 assert_eq!(nnf32(f32::MAX).after(), NotNanF32::MAX);
873 }
874
875 #[test]
876 fn infinities_are_valid_range_endpoints() {
877 use crate::RangeSetBlaze;
878
879 let set = RangeSetBlaze::from_iter([nnf64(f64::NEG_INFINITY)..=nnf64(0.0)]);
880 assert!(set.contains(NotNanF64::MIN));
881 assert!(set.contains(nnf64(f64::MIN)));
882 assert!(set.contains(nnf64(0.0)));
883 assert!(!set.contains(nnf64(0.0).after()));
884 assert!(!set.contains(NotNanF64::MAX));
885
886 let full = !RangeSetBlaze::<NotNanF64>::new();
887 assert!(full.contains(NotNanF64::MIN));
888 assert!(full.contains(NotNanF64::MAX));
889 assert_eq!(full.len(), NotNanF64::MAX_SIZE);
890 }
891
892 #[test]
893 fn after_and_before_are_neighbors_in_total_order() {
894 let values = [
895 NotNanF64::MIN,
896 nnf64(f64::MIN),
897 nnf64(-f64::MAX),
898 nnf64(-1.0),
899 nnf64(-0.0),
900 nnf64(0.0),
901 nnf64(1.0),
902 nnf64(f64::MAX),
903 NotNanF64::MAX,
904 ];
905
906 for value in values {
907 if value != NotNanF64::MAX {
908 assert_eq!(value.after().before(), value);
909 }
910 if value != NotNanF64::MIN {
911 assert_eq!(value.before().after(), value);
912 }
913 }
914 }
915
916 #[test]
917 fn adjacency_laws_cover_f32_and_f64_edges() {
918 macro_rules! check {
919 ($name:ident, $zero:expr, $negative_subnormal:expr, $positive_subnormal:expr, $min:expr, $max:expr) => {{
920 let values = [
921 nnf32($zero),
922 nnf32($negative_subnormal),
923 nnf32($positive_subnormal),
924 nnf32(-1.0),
925 nnf32(1.0),
926 nnf32($min),
927 nnf32($max),
928 ];
929 for value in values {
930 if value != NotNanF32::MAX {
931 assert_eq!(value.after().before(), value);
932 }
933 if value != NotNanF32::MIN {
934 assert_eq!(value.before().after(), value);
935 }
936 }
937 assert_eq!(NotNanF32::MIN.checked_before(), None);
938 assert_eq!(NotNanF32::MAX.checked_after(), None);
939 assert_eq!(nnf32($negative_subnormal).after(), nnf32($zero));
940 assert_eq!(nnf32($zero).after(), nnf32($positive_subnormal));
941 let _ = stringify!($name);
942 }};
943 }
944
945 check!(
946 f32_edges,
947 0.0_f32,
948 -f32::from_bits(1),
949 f32::from_bits(1),
950 f32::MIN,
951 f32::MAX
952 );
953
954 let values = [
955 nnf64(-f64::from_bits(1)),
956 nnf64(0.0),
957 nnf64(f64::from_bits(1)),
958 nnf64(-1.0),
959 nnf64(1.0),
960 NotNanF64::MIN,
961 NotNanF64::MAX,
962 ];
963 for value in values {
964 if value != NotNanF64::MAX {
965 assert_eq!(value.after().before(), value);
966 }
967 if value != NotNanF64::MIN {
968 assert_eq!(value.before().after(), value);
969 }
970 }
971 assert_eq!(NotNanF64::MIN.checked_before(), None);
972 assert_eq!(NotNanF64::MAX.checked_after(), None);
973 assert_eq!(nnf64(-f64::from_bits(1)).after(), nnf64(0.0));
974 assert_eq!(nnf64(0.0).after(), nnf64(f64::from_bits(1)));
975 }
976
977 #[test]
978 fn range_length_laws_cover_f32_and_f64() {
979 let start = nnf32(-f32::from_bits(1));
980 let end = nnf32(f32::from_bits(1));
981 assert_eq!(NotNanF32::safe_len(&(start..=start)), 1);
982 assert_eq!(NotNanF32::safe_len(&(start..=start.after())), 2);
983 assert_eq!(NotNanF32::safe_len(&(start..=end)), 3);
984 assert_eq!(
985 NotNanF32::MAX_SIZE,
986 NotNanF32::safe_len(&(NotNanF32::MIN..=NotNanF32::MAX))
987 );
988 let length = 17;
989 let endpoint = start.inclusive_end_from_start(length);
990 assert_eq!(endpoint.start_from_inclusive_end(length), start);
991 assert_eq!(start.inclusive_end_from_start(length), endpoint);
992
993 let start = nnf64(-f64::from_bits(1));
994 let end = nnf64(f64::from_bits(1));
995 assert_eq!(NotNanF64::safe_len(&(start..=start)), 1);
996 assert_eq!(NotNanF64::safe_len(&(start..=start.after())), 2);
997 assert_eq!(NotNanF64::safe_len(&(start..=end)), 3);
998 assert_eq!(
999 NotNanF64::MAX_SIZE,
1000 NotNanF64::safe_len(&(NotNanF64::MIN..=NotNanF64::MAX))
1001 );
1002 let length = 17;
1003 let endpoint = start.inclusive_end_from_start(length);
1004 assert_eq!(endpoint.start_from_inclusive_end(length), start);
1005 assert_eq!(start.inclusive_end_from_start(length), endpoint);
1006 }
1007
1008 #[cfg(feature = "float_nightly_experimental")]
1009 #[test]
1010 fn f16_not_nan_adjacency_and_lengths_are_exhaustive() {
1011 for bits in 0..=u16::MAX {
1012 let value = f16::from_bits(bits);
1013 let Some(value) = NotNanF16::try_new(value) else {
1014 continue;
1015 };
1016 if value != NotNanF16::MIN {
1017 assert_eq!(value.before().after(), value);
1018 }
1019 if value != NotNanF16::MAX {
1020 assert_eq!(value.after().before(), value);
1021 }
1022 assert_eq!(NotNanF16::safe_len(&(value..=value)), 1);
1023 }
1024 assert_eq!(
1025 NotNanF16::MAX_SIZE,
1026 NotNanF16::safe_len(&(NotNanF16::MIN..=NotNanF16::MAX))
1027 );
1028 }
1029}