Skip to main content

range_set_blaze/float/
total.rs

1//! Total is a floating point type, suitable for use in ranges. All values are valid.
2//!
3//! Ordering and other semantics are as per `total_cmp`.\
4//! Every distinct bit pattern is a separate valid value, even though quite a few of them are NaN.\
5//! For example, in a `TotalF32` all 16 million different NaN values are distinct from each other.
6//!
7//! Enable with `float_experimental` (stable, `TotalF32`/`TotalF64`) and
8//! `float_nightly_experimental` (nightly, adds `TotalF16`/`TotalF128`).
9//! ```
10//! use range_set_blaze::{RangeSetBlaze, TotalF64, TotalF32};
11//! let set = RangeSetBlaze::from_iter([TotalF64::new(3.0)..=TotalF64::new(5.0)]);
12//! assert!(set.contains(TotalF64::new(3.1)));
13//! assert!(!set.contains(TotalF64::new(2.9)));
14//!
15//! let set = RangeSetBlaze::from(TotalF64::from_primitive_range(3.0..=5.0));
16//! assert!(set.contains(TotalF64::new(4.9)));
17//! assert!(!set.contains(TotalF64::new(5.1)));
18//!
19//! let set = RangeSetBlaze::from_iter(TotalF32::from_primitive_ranges([3.0..=5.0, 7.0..=9.0]));
20//! assert!(set.contains(TotalF32::new(4.0)));
21//! assert!(!set.contains(TotalF32::new(6.0)));
22//! ```
23
24use super::total_float::TotalFloat;
25use crate::Integer;
26#[cfg(feature = "from_slice")]
27use crate::RangeSetBlaze;
28use core::{
29    cmp::Ordering,
30    fmt::Debug,
31    hash::{Hash, Hasher},
32    mem,
33    ops::RangeInclusive,
34    slice::from_raw_parts,
35};
36/// Total ordered f64, all values valid, including NaN, -0.0, +0.0, and infinities.
37pub type TotalF64 = Total<f64>;
38/// Total ordered f32, all values valid, including NaN, -0.0, +0.0, and infinities.
39pub type TotalF32 = Total<f32>;
40/// Total ordered f16, all values valid, including NaN, -0.0, +0.0, and infinities.
41#[cfg(feature = "float_nightly_experimental")]
42pub type TotalF16 = Total<f16>;
43/// Total ordered f128, all values valid, including NaN, -0.0, +0.0, and infinities.
44#[cfg(feature = "float_nightly_experimental")]
45pub type TotalF128 = Total<f128>;
46
47/// Construct a [`TotalF64`] from an `f64`. Shorthand for [`TotalF64::new`]
48#[must_use]
49pub const fn tf64(x: f64) -> TotalF64 {
50    TotalF64::new(x)
51}
52
53/// Construct a [`TotalF32`] from an `f32`. Shorthand for [`TotalF32::new`]
54#[must_use]
55pub const fn tf32(x: f32) -> TotalF32 {
56    TotalF32::new(x)
57}
58
59/// Construct a [`TotalF16`] from an `f16`. Shorthand for [`TotalF16::new`]
60#[cfg(feature = "float_nightly_experimental")]
61#[must_use]
62pub const fn tf16(x: f16) -> TotalF16 {
63    TotalF16::new(x)
64}
65
66/// Construct a [`TotalF128`] from an `f128`. Shorthand for [`TotalF128::new`]
67#[cfg(feature = "float_nightly_experimental")]
68#[must_use]
69pub const fn tf128(x: f128) -> TotalF128 {
70    TotalF128::new(x)
71}
72
73/// Experimental: A transparent wrapper around floating point values with total ordering.
74///
75/// Comparison, equality, and hashing all agree with `total_cmp`.
76///
77/// # Enabling
78///
79/// This type is experimental and must be enabled with the `float_experimental` feature.
80/// ```bash
81/// cargo add range-set-blaze --features "float_experimental"
82/// ```
83/// That provides the `TotalF32` and `TotalF64` types.
84///
85/// If you're building with nightly, you can instead use the `float_nightly_experimental` feature.
86/// ```bash
87/// cargo add range-set-blaze --features "float_nightly_experimental"
88/// ```
89/// To also use the `TotalF16` and `TotalF128` types.
90#[repr(transparent)]
91#[derive(Copy, Clone, Default, Debug)]
92pub struct Total<T: TotalFloat>(T);
93
94impl<T: TotalFloat> Total<T> {
95    /// The minimum value that can be represented by the type.
96    /// I.e., the smallest possible value according to `total_cmp`\
97    /// Maps directly to [`crate::Integer::min_value()`]
98    ///
99    /// # Examples
100    /// ```
101    /// use range_set_blaze::TotalF64;
102    ///
103    /// assert_eq!(TotalF64::MIN, TotalF64::new(f64::from_bits(u64::MAX)));
104    /// ```
105    pub const MIN: Self = Self(T::MIN);
106
107    /// The maximum value that can be represented by the type.
108    /// I.e., the largest possible value according to `total_cmp`\
109    /// Maps directly to [`crate::Integer::max_value()`]
110    ///
111    /// # Examples
112    /// ```
113    /// use range_set_blaze::TotalF64;
114    ///
115    /// assert_eq!(TotalF64::MAX, TotalF64::new(f64::from_bits(0x7fff_ffff_ffff_ffff)));
116    /// ```
117    pub const MAX: Self = Self(T::MAX);
118
119    /// The maximum possible size of a range, i.e. the size if `[MIN..=MAX]`
120    ///
121    /// # Examples
122    /// ```
123    /// use range_set_blaze::TotalF32;
124    ///
125    /// assert_eq!(TotalF32::MAX_SIZE, u32::MAX as i64 + 1);
126    /// ```
127    pub const MAX_SIZE: T::SafeLen = T::MAX_SIZE;
128
129    /// Creates a new [`Total`] from a primitive float.
130    /// All values are legal.
131    ///
132    /// # Examples
133    /// ```
134    /// use range_set_blaze::TotalF64;
135    ///
136    /// let _ = TotalF64::new(f64::INFINITY);
137    /// ```
138    #[must_use]
139    pub const fn new(x: T) -> Self {
140        Self(x)
141    }
142
143    /// Computes `self + (b - 1)` where `b` is of type `SafeLen`.
144    ///
145    /// # Precondition
146    /// `b` must be small enough that the result stays within range for `T`. This is
147    /// checked with `debug_assert!` and is *not* checked in release builds, where
148    /// violating it produces an unspecified (nonsense, but not unsafe) result rather
149    /// than a panic. Callers are expected to only ever pass a `b` that satisfies this.
150    #[must_use]
151    pub fn inclusive_end_from_start(self, b: T::SafeLen) -> Self {
152        Self(T::inclusive_end_from_start(self.0, b))
153    }
154
155    /// Computes `self - (b - 1)` where `b` is of type `SafeLen`.
156    ///
157    /// # Precondition
158    /// `b` must be small enough that the result stays within range for `T`. This is
159    /// checked with `debug_assert!` and is *not* checked in release builds, where
160    /// violating it produces an unspecified (nonsense, but not unsafe) result rather
161    /// than a panic. Callers are expected to only ever pass a `b` that satisfies this.
162    #[must_use]
163    pub fn start_from_inclusive_end(self, b: T::SafeLen) -> Self {
164        Self(T::start_from_inclusive_end(self.0, b))
165    }
166
167    /// Returns the wrapped value.
168    ///
169    /// # Examples
170    /// ```
171    /// use range_set_blaze::TotalF64;
172    ///
173    /// assert_eq!(TotalF64::new(42.0).into_inner(), 42.0);
174    /// ```
175    #[must_use]
176    pub const fn into_inner(self) -> T {
177        self.0
178    }
179
180    /// Returns the next float in total order.
181    ///
182    /// # Examples
183    /// ```
184    /// use range_set_blaze::TotalF64;
185    ///
186    /// assert_eq!(TotalF64::new(42.0).after().before().into_inner(), 42.0);
187    /// ```
188    ///
189    /// # Panics
190    ///
191    /// In debug builds, panics if `self` is the maximum value. In release
192    /// builds, wraps around to the minimum value instead.
193    #[must_use]
194    pub fn after(self) -> Self {
195        debug_assert!(self != Self::MAX, "after() called on maximum value");
196        Self(T::after(self.0))
197    }
198
199    /// Returns the previous float in total order.
200    ///
201    /// # Examples
202    /// ```
203    /// use range_set_blaze::TotalF64;
204    ///
205    /// assert_eq!(TotalF64::new(42.0).before().after().into_inner(), 42.0);
206    /// ```
207    ///
208    /// # Panics
209    ///
210    /// In debug builds, panics if `self` is the minimum value. In release
211    /// builds, wraps around to the maximum value instead.
212    #[must_use]
213    pub fn before(self) -> Self {
214        debug_assert!(self != Self::MIN, "before() called on minimum value");
215        Self(T::before(self.0))
216    }
217
218    /// Returns the next float.
219    ///
220    /// Returns [`None`] if `self` is the maximum value.
221    ///
222    /// # Examples
223    /// ```
224    /// use range_set_blaze::TotalF64;
225    ///
226    /// let value = TotalF64::new(42.0);
227    /// assert_eq!(value.checked_after(), Some(value.after()));
228    /// let value = TotalF64::MAX;
229    /// assert_eq!(value.checked_after(), None);
230    /// ```
231    #[must_use]
232    pub fn checked_after(self) -> Option<Self> {
233        if self == Self::MAX {
234            None
235        } else {
236            Some(self.after())
237        }
238    }
239
240    /// Returns the previous float.
241    ///
242    /// Returns [`None`] if `self` is the minimum value.
243    ///
244    /// # Examples
245    /// ```
246    /// use range_set_blaze::TotalF64;
247    ///
248    /// let value = TotalF64::new(42.0);
249    /// assert_eq!(value.checked_before(), Some(value.before()));
250    /// let value = TotalF64::MIN;
251    /// assert_eq!(value.checked_before(), None);
252    /// ```
253    #[must_use]
254    pub fn checked_before(self) -> Option<Self> {
255        if self == Self::MIN {
256            None
257        } else {
258            Some(self.before())
259        }
260    }
261
262    /// Converts an inclusive primitive range into an inclusive [`Total`] range.
263    ///
264    /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
265    ///
266    /// # Examples
267    /// ```
268    /// use range_set_blaze::{RangeSetBlaze, TotalF64};
269    ///
270    /// let short = RangeSetBlaze::from(TotalF64::from_primitive_range(3.0..=5.0));
271    /// let long = RangeSetBlaze::from(TotalF64::new(3.0)..=TotalF64::new(5.0));
272    /// assert_eq!(short, long);
273    /// ```
274    #[must_use]
275    pub fn from_primitive_range(range: RangeInclusive<T>) -> RangeInclusive<Self> {
276        let (start, end) = range.into_inner();
277        Self(start)..=Self(end)
278    }
279
280    /// Converts inclusive primitive ranges into inclusive [`Total`] ranges.
281    ///
282    /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
283    ///
284    /// # Examples
285    /// ```
286    /// use range_set_blaze::{RangeSetBlaze, TotalF64};
287    ///
288    /// let short = RangeSetBlaze::from_iter(TotalF64::from_primitive_ranges([1.0..=2.0, 3.0..=4.0]));
289    /// let long = RangeSetBlaze::from_iter([TotalF64::new(1.0)..=TotalF64::new(2.0), TotalF64::new(3.0)..=TotalF64::new(4.0)]);
290    /// assert_eq!(short, long);
291    /// ```
292    pub fn from_primitive_ranges<I>(ranges: I) -> impl Iterator<Item = RangeInclusive<Self>>
293    where
294        I: IntoIterator<Item = RangeInclusive<T>>,
295    {
296        ranges.into_iter().map(Self::from_primitive_range)
297    }
298
299    /// Convenience method to convert primitive values into ordered [`Total`] values.
300    /// # Examples
301    /// ```
302    /// use range_set_blaze::{RangeSetBlaze, TotalF64};
303    ///
304    /// let short = RangeSetBlaze::from_iter(TotalF64::values([1.0, 2.0, 3.0, 4.0]));
305    /// let long = RangeSetBlaze::from_iter([TotalF64::new(1.0), TotalF64::new(2.0), TotalF64::new(3.0), TotalF64::new(4.0)]);
306    /// assert_eq!(short, long);
307    /// ```
308    pub fn values<I>(values: I) -> impl Iterator<Item = Self>
309    where
310        I: IntoIterator<Item = T>,
311    {
312        values.into_iter().map(Self)
313    }
314
315    /// Views primitive values as ordered [`Total`] values.
316    ///
317    /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
318    ///
319    /// This runs in `O(1)` and does not allocate.
320    /// # Examples
321    /// ```
322    /// use range_set_blaze::{RangeSetBlaze, TotalF64};
323    ///
324    /// let short = RangeSetBlaze::from_iter(TotalF64::from_primitive_slice(&[1.0, 2.0, 3.0, 4.0]));
325    /// let long = RangeSetBlaze::from_iter([TotalF64::new(1.0), TotalF64::new(2.0), TotalF64::new(3.0), TotalF64::new(4.0)]);
326    /// assert_eq!(short, long);
327    /// ```
328    #[must_use]
329    pub const fn from_primitive_slice(values: &[T]) -> &[Self] {
330        // SAFETY: Total is #[repr(transparent)] over T, making `&[T]`
331        // and `&[Total]` entirely interchangeable in layout and lifetimes.
332        unsafe { mem::transmute::<&[T], &[Self]>(values) }
333    }
334}
335
336/// Extension trait for viewing a slice of [`Total`] values as primitive values.
337pub trait TotalSliceExt<T: TotalFloat> {
338    /// Views [`Total`] values as primitive values.
339    ///
340    /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
341    ///
342    /// This runs in `O(1)` and does not allocate.
343    /// # Examples
344    /// ```
345    /// use range_set_blaze::TotalF64;
346    /// use range_set_blaze::total::TotalSliceExt;
347    ///
348    /// let totals = [TotalF64::new(1.0), TotalF64::new(2.0), TotalF64::new(3.0)];
349    /// assert_eq!(&[1.0, 2.0, 3.0], totals.as_primitive_slice());
350    /// ```
351    fn as_primitive_slice(&self) -> &[T];
352}
353
354impl<T: TotalFloat> TotalSliceExt<T> for [Total<T>] {
355    fn as_primitive_slice(&self) -> &[T] {
356        // SAFETY: Total<T> is #[repr(transparent)] over T, making `&[T]`
357        // and `&[Total<T>]` entirely interchangeable in layout and lifetimes.
358        unsafe { from_raw_parts(self.as_ptr().cast::<T>(), self.len()) }
359    }
360}
361
362/// Extension trait for converting an inclusive [`Total`] range into an inclusive primitive
363/// range (or a `(start, end)` primitive tuple).
364pub trait TotalRangeExt<T: TotalFloat> {
365    /// Converts an inclusive [`Total`] range into an inclusive primitive range.
366    ///
367    /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
368    ///
369    /// This is the reverse of [`Total::from_primitive_range`].
370    ///
371    /// # Examples
372    /// ```
373    /// use range_set_blaze::TotalF64;
374    /// use range_set_blaze::total::TotalRangeExt;
375    ///
376    /// let range = TotalF64::new(3.0)..=TotalF64::new(5.0);
377    /// assert_eq!(range.into_primitive_range(), 3.0..=5.0);
378    /// ```
379    #[must_use]
380    fn into_primitive_range(self) -> RangeInclusive<T>;
381
382    /// Converts an inclusive [`Total`] range into a `(start, end)` tuple of primitive values.
383    ///
384    /// "Primitive" here means Rust's built-in float type (e.g. `f64`).
385    ///
386    /// Mirrors [`RangeInclusive::into_inner`] from the standard library, which unwraps a
387    /// range into its `(start, end)` tuple; this additionally converts each endpoint to its
388    /// primitive type.
389    ///
390    /// # Examples
391    /// ```
392    /// use range_set_blaze::TotalF64;
393    /// use range_set_blaze::total::TotalRangeExt;
394    ///
395    /// let range = TotalF64::new(3.0)..=TotalF64::new(5.0);
396    /// assert_eq!(range.into_primitive_inner(), (3.0, 5.0));
397    /// ```
398    #[must_use]
399    fn into_primitive_inner(self) -> (T, T);
400}
401
402impl<T: TotalFloat> TotalRangeExt<T> for RangeInclusive<Total<T>> {
403    fn into_primitive_range(self) -> RangeInclusive<T> {
404        let (start, end) = self.into_primitive_inner();
405        start..=end
406    }
407
408    fn into_primitive_inner(self) -> (T, T) {
409        let (start, end) = self.into_inner();
410        (start.into_inner(), end.into_inner())
411    }
412}
413
414impl<T: TotalFloat> PartialEq for Total<T> {
415    fn eq(&self, other: &Self) -> bool {
416        T::total_cmp(self.0, other.0) == Ordering::Equal
417    }
418}
419
420impl<T: TotalFloat> Eq for Total<T> {}
421
422impl<T: TotalFloat> PartialOrd for Total<T> {
423    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
424        Some(self.cmp(other))
425    }
426}
427
428impl<T: TotalFloat> Ord for Total<T> {
429    fn cmp(&self, other: &Self) -> Ordering {
430        T::total_cmp(self.0, other.0)
431    }
432}
433
434impl<T: TotalFloat> Hash for Total<T> {
435    fn hash<H: Hasher>(&self, state: &mut H) {
436        T::hash(self.0, state);
437    }
438}
439
440impl<T: TotalFloat> Integer for Total<T> {
441    type SafeLen = T::SafeLen;
442
443    #[inline]
444    fn checked_add_one(self) -> Option<Self> {
445        self.checked_after()
446    }
447
448    // This moves to the next representable float in total_cmp order, not a numeric + 1.0.
449    #[inline]
450    fn add_one(self) -> Self {
451        self.after()
452    }
453
454    #[inline]
455    // This moves to the previous representable float in total_cmp order, not a numeric - 1.0.
456    fn sub_one(self) -> Self {
457        self.before()
458    }
459
460    #[inline]
461    fn assign_sub_one(&mut self) {
462        *self = self.before();
463    }
464
465    // Ideally, we would `impl std::iter::Step for TotalF64` and just call Range::next(), but that's still experimental.
466    #[inline]
467    fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self> {
468        if range.is_empty() {
469            None
470        } else if range.start() == range.end() && *range.start() == Self::MAX {
471            // Preserve the exhausted range sentinel without calling `after()` on MAX.
472            let next = *range.start();
473            *range = next..=range.end().before();
474            Some(next)
475        } else {
476            let next = *range.start();
477            *range = (next.after())..=*range.end();
478            Some(next)
479        }
480    }
481
482    #[inline]
483    fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self> {
484        if range.is_empty() {
485            None
486        } else if range.start() == range.end() && *range.start() == Self::MIN {
487            // Preserve the exhausted range sentinel without calling `before()` on MIN.
488            let last = *range.end();
489            *range = last.after()..=last;
490            Some(last)
491        } else {
492            let last = *range.end();
493            *range = *range.start()..=last.before();
494            Some(last)
495        }
496    }
497
498    #[inline]
499    fn min_value() -> Self {
500        Self::MIN
501    }
502
503    #[inline]
504    fn max_value() -> Self {
505        Self::MAX
506    }
507
508    #[cfg(feature = "from_slice")]
509    #[inline]
510    fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self> {
511        // TODO Investigate applying the ordered float transform in SIMD chunks here.
512        // no way to do the fancy thing
513        RangeSetBlaze::from_iter(slice.as_ref())
514    }
515
516    fn safe_len(r: &RangeInclusive<Self>) -> Self::SafeLen {
517        let (start, end) = r.clone().into_primitive_inner();
518        T::prim_safe_len(start, end)
519    }
520
521    fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
522        T::safe_len_to_f64_lossy(len)
523    }
524
525    fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
526        T::f64_to_safe_len_lossy(f)
527    }
528
529    fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self {
530        self.inclusive_end_from_start(b)
531    }
532
533    fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self {
534        self.start_from_inclusive_end(b)
535    }
536}
537
538#[cfg(test)]
539mod tests {
540    use super::*;
541    use crate::Integer;
542    use crate::float::total_float::{
543        from_ordered_32, from_ordered_64, to_ordered_32, to_ordered_64,
544    };
545    use std::collections::hash_map::DefaultHasher;
546    use std::vec;
547    use std::vec::Vec;
548
549    #[test]
550    fn ordering_agrees_with_total_cmp() {
551        let values = [
552            f64::NEG_INFINITY,
553            -f64::MAX,
554            -1.0,
555            -0.0,
556            0.0,
557            1.0,
558            f64::MAX,
559            f64::INFINITY,
560            f64::NAN,
561            f64::from_bits(0x7ff8_0000_0000_0001),
562            f64::from_bits(0xfff8_0000_0000_0001),
563        ];
564
565        for left in values {
566            for right in values {
567                assert_eq!(tf64(left).cmp(&tf64(right)), left.total_cmp(&right));
568            }
569        }
570    }
571
572    #[test]
573    fn equality_agrees_with_total_cmp() {
574        assert_ne!(tf64(-0.0), tf64(0.0));
575        assert_eq!(tf64(f64::NAN), tf64(f64::NAN));
576    }
577
578    #[test]
579    fn equal_values_hash_equally() {
580        let left = hash(tf64(f64::NAN));
581        let right = hash(tf64(f64::NAN));
582
583        assert_eq!(left, right);
584    }
585
586    #[test]
587    fn converts_ranges() {
588        assert_eq!(
589            TotalF64::from_primitive_range(10.0..=20.0),
590            tf64(10.0)..=tf64(20.0)
591        );
592        assert_eq!(
593            TotalF64::from_primitive_ranges([10.0..=20.0, 30.0..=40.0]).collect::<Vec<_>>(),
594            vec![tf64(10.0)..=tf64(20.0), tf64(30.0)..=tf64(40.0)]
595        );
596    }
597
598    #[test]
599    fn after_and_before_step_through_zero_in_total_order() {
600        assert_eq!(tf64(-0.0).after(), tf64(0.0));
601        assert_eq!(tf64(0.0).before(), tf64(-0.0));
602        assert_eq!(tf64(0.0).after(), tf64(f64::from_bits(1)));
603        assert_eq!(
604            tf64(-0.0).before(),
605            tf64(f64::from_bits(0x8000_0000_0000_0001))
606        );
607    }
608
609    #[test]
610    fn checked_after_and_before_are_not_wrapping() {
611        assert_eq!(TotalF64::MAX.checked_after(), None);
612        assert_eq!(TotalF64::MIN.checked_before(), None);
613    }
614
615    #[test]
616    #[cfg(debug_assertions)]
617    #[should_panic(expected = "after() called on maximum value")]
618    fn total_after_panics_at_max_in_debug() {
619        let _ = TotalF64::MAX.after();
620    }
621
622    #[test]
623    #[cfg(not(debug_assertions))]
624    fn total_after_wraps_at_max_in_release() {
625        assert_eq!(TotalF64::MAX.after(), TotalF64::MIN);
626    }
627
628    #[test]
629    #[cfg(debug_assertions)]
630    #[should_panic(expected = "before() called on minimum value")]
631    fn total_before_panics_at_min_in_debug() {
632        let _ = TotalF64::MIN.before();
633    }
634
635    #[test]
636    #[cfg(not(debug_assertions))]
637    fn total_before_wraps_at_min_in_release() {
638        assert_eq!(TotalF64::MIN.before(), TotalF64::MAX);
639    }
640
641    #[test]
642    fn stable_ordered_round_trips() {
643        let edge_f64 = [
644            0,
645            1,
646            u64::MAX,
647            0x7ff0_0000_0000_0000,
648            0xfff0_0000_0000_0000,
649            0x7ff8_0000_0000_0001,
650            0xfff8_0000_0000_0001,
651        ];
652        for bits in edge_f64 {
653            let value = f64::from_bits(bits);
654            assert_eq!(from_ordered_64(to_ordered_64(value)).to_bits(), bits);
655        }
656
657        let edge_f32 = [
658            0,
659            1,
660            u32::MAX,
661            0x7f80_0000,
662            0xff80_0000,
663            0x7fc0_0001,
664            0xffc0_0001,
665        ];
666        for bits in edge_f32 {
667            let value = f32::from_bits(bits);
668            assert_eq!(from_ordered_32(to_ordered_32(value)).to_bits(), bits);
669        }
670
671        let mut state = 0x9e37_79b9_u64;
672        for _ in 0..10_000 {
673            state = state
674                .wrapping_mul(6_364_136_223_846_793_005)
675                .wrapping_add(1);
676            let value = f64::from_bits(state);
677            assert_eq!(from_ordered_64(to_ordered_64(value)).to_bits(), state);
678            let bytes = state.to_le_bytes();
679            let bits = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
680            let value = f32::from_bits(bits);
681            assert_eq!(from_ordered_32(to_ordered_32(value)).to_bits(), bits);
682        }
683    }
684
685    #[test]
686    fn after_and_before_step_around_infinities() {
687        assert_eq!(tf64(f64::MAX).after(), tf64(f64::INFINITY));
688        assert_eq!(tf64(f64::INFINITY).before(), tf64(f64::MAX));
689        assert_eq!(tf64(f64::NEG_INFINITY).after(), tf64(-f64::MAX));
690        assert_eq!(tf64(-f64::MAX).before(), tf64(f64::NEG_INFINITY));
691    }
692
693    #[test]
694    fn checked_after_and_before_stop_at_total_order_boundaries() {
695        assert_eq!(TotalF64::MIN.checked_before(), None);
696        assert_eq!(TotalF64::MAX.checked_after(), None);
697        assert_eq!(TotalF64::MIN.checked_after(), Some(TotalF64::MIN.after()));
698        assert_eq!(TotalF64::MAX.checked_before(), Some(TotalF64::MAX.before()));
699    }
700
701    #[test]
702    fn min_and_max_are_total_order_boundaries() {
703        let values = [
704            tf64(f64::NEG_INFINITY),
705            tf64(-f64::MAX),
706            tf64(-1.0),
707            tf64(-0.0),
708            tf64(0.0),
709            tf64(1.0),
710            tf64(f64::MAX),
711            tf64(f64::INFINITY),
712            tf64(f64::NAN),
713            tf64(f64::from_bits(0x7ff8_0000_0000_0001)),
714            tf64(f64::from_bits(0xfff8_0000_0000_0001)),
715        ];
716
717        for value in values {
718            assert!(TotalF64::MIN <= value);
719            assert!(value <= TotalF64::MAX);
720        }
721    }
722
723    #[test]
724    fn after_and_before_are_neighbors_in_total_order() {
725        let values = [
726            tf64(f64::NEG_INFINITY),
727            tf64(-f64::MAX),
728            tf64(-1.0),
729            tf64(-0.0),
730            tf64(0.0),
731            tf64(1.0),
732            tf64(f64::MAX),
733            tf64(f64::INFINITY),
734            tf64(f64::NAN),
735            tf64(f64::from_bits(0x7ff8_0000_0000_0001)),
736            tf64(f64::from_bits(0xfff8_0000_0000_0001)),
737        ];
738
739        for value in values {
740            assert_eq!(value.after().before(), value);
741            assert_eq!(value.before().after(), value);
742        }
743    }
744
745    #[test]
746    fn adjacency_laws_cover_f32_and_f64_edges() {
747        macro_rules! check {
748            ($wrapper:ident, $constructor:ident, $zero:expr, $negative_subnormal:expr, $positive_subnormal:expr, $min:expr, $max:expr) => {
749                let values = [
750                    $constructor($zero),
751                    $constructor($negative_subnormal),
752                    $constructor($positive_subnormal),
753                    $constructor(-1.0),
754                    $constructor(1.0),
755                    $constructor($min),
756                    $constructor($max),
757                    $constructor(f32::INFINITY),
758                    $constructor(f32::NAN),
759                ];
760                for value in values {
761                    assert_eq!(value.after().before(), value);
762                    assert_eq!(value.before().after(), value);
763                }
764                assert_eq!($wrapper::MIN.checked_before(), None);
765                assert_eq!($wrapper::MAX.checked_after(), None);
766            };
767        }
768        check!(
769            TotalF32,
770            tf32,
771            0.0_f32,
772            -f32::from_bits(1),
773            f32::from_bits(1),
774            f32::MIN,
775            f32::MAX
776        );
777
778        let values = [
779            tf64(-0.0),
780            tf64(0.0),
781            tf64(-f64::from_bits(1)),
782            tf64(f64::from_bits(1)),
783            tf64(-f64::MAX),
784            tf64(f64::MAX),
785            tf64(f64::NEG_INFINITY),
786            tf64(f64::INFINITY),
787            tf64(f64::from_bits(0x7ff8_0000_0000_0001)),
788        ];
789        for value in values {
790            assert_eq!(value.after().before(), value);
791            assert_eq!(value.before().after(), value);
792        }
793        assert_eq!(TotalF64::MIN.checked_before(), None);
794        assert_eq!(TotalF64::MAX.checked_after(), None);
795    }
796
797    #[test]
798    fn range_length_laws_cover_f32_and_f64() {
799        let start = tf32(-f32::from_bits(1));
800        assert_eq!(TotalF32::safe_len(&(start..=start)), 1);
801        assert_eq!(TotalF32::safe_len(&(start..=start.after())), 2);
802        assert_eq!(
803            TotalF32::MAX_SIZE,
804            TotalF32::safe_len(&(TotalF32::MIN..=TotalF32::MAX))
805        );
806        let length = 17;
807        let end = start.inclusive_end_from_start(length);
808        assert_eq!(end.start_from_inclusive_end(length), start);
809
810        let start = tf64(-f64::from_bits(1));
811        assert_eq!(TotalF64::safe_len(&(start..=start)), 1);
812        assert_eq!(TotalF64::safe_len(&(start..=start.after())), 2);
813        assert_eq!(
814            TotalF64::MAX_SIZE,
815            TotalF64::safe_len(&(TotalF64::MIN..=TotalF64::MAX))
816        );
817        let length = 17;
818        let end = start.inclusive_end_from_start(length);
819        assert_eq!(end.start_from_inclusive_end(length), start);
820    }
821
822    #[cfg(feature = "float_nightly_experimental")]
823    #[test]
824    fn f16_total_adjacency_and_lengths_are_exhaustive() {
825        for bits in 0..=u16::MAX {
826            let value = TotalF16::new(f16::from_bits(bits));
827            if value != TotalF16::MAX {
828                assert_eq!(value.after().before(), value);
829            }
830            if value != TotalF16::MIN {
831                assert_eq!(value.before().after(), value);
832            }
833            assert_eq!(TotalF16::safe_len(&(value..=value)), 1);
834        }
835        assert_eq!(
836            TotalF16::MAX_SIZE,
837            TotalF16::safe_len(&(TotalF16::MIN..=TotalF16::MAX))
838        );
839    }
840
841    fn hash(value: TotalF64) -> u64 {
842        let mut hasher = DefaultHasher::new();
843        value.hash(&mut hasher);
844        hasher.finish()
845    }
846    #[test]
847    #[cfg(feature = "float_nightly_experimental")]
848    fn ordered_round_trip() {
849        use crate::float::total_float::from_ordered_16;
850        use crate::float::total_float::to_ordered_16;
851        for x in i16::MIN..=i16::MAX {
852            assert_eq!(to_ordered_16(from_ordered_16(x)), x);
853        }
854    }
855}