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