closed_interval_set/
primitive_endpoint.rs

1//! Define the `Endpoint` trait for primitive integer types.
2
3macro_rules! def_endpoint {
4    ($($T:ty)*) => {
5        $(
6            impl crate::Endpoint for $T {
7                #[inline(always)]
8                fn min_value() -> $T {
9                    <$T>::MIN
10                }
11
12                #[inline(always)]
13                fn max_value() -> $T {
14                    <$T>::MAX
15                }
16
17                #[inline(always)]
18                fn is_valid(self) -> bool {
19                    true
20                }
21
22                #[inline(always)]
23                fn cmp_end(self, other: Self) -> core::cmp::Ordering {
24                    core::cmp::Ord::cmp(&self, &other)
25                }
26
27                #[inline(always)]
28                fn decrease_toward(self, other: $T) -> Option<$T> {
29                    if other < self {
30                        self.checked_sub(1)
31                    } else {
32                        None
33                    }
34                }
35
36                #[inline(always)]
37                fn increase_toward(self, other: $T) -> Option<$T> {
38                    if other > self {
39                        self.checked_add(1)
40                    } else {
41                        None
42                    }
43                }
44            }
45        )*
46    };
47}
48
49def_endpoint!(i8 i16 i32 i64 i128 isize
50              u8 u16 u32 u64 u128 usize);
51
52#[inline]
53fn f32_to_i32(x: f32) -> i32 {
54    // This is sign-magnitude.  Convert to i32 by flipping all but
55    // the top bit when that bit is set.
56    //
57    // Compare with the implementation of
58    // https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.total_cmp
59    let bits = x.to_bits() as i32;
60    let mask = ((bits >> 31) as u32) >> 1;
61    bits ^ (mask as i32)
62}
63
64#[inline]
65fn i32_to_f32(bits: i32) -> f32 {
66    // Flip all but the top bit when that bit is set
67    let mask = ((bits >> 31) as u32) >> 1;
68
69    f32::from_bits((bits as u32) ^ mask)
70}
71
72#[inline]
73fn f64_to_i64(x: f64) -> i64 {
74    // This is sign-magnitude.  Convert to i64 by flipping all but
75    // the top bit when that bit is set.
76    let bits = x.to_bits() as i64;
77    let mask = ((bits >> 63) as u64) >> 1;
78
79    bits ^ (mask as i64)
80}
81
82#[inline]
83fn i64_to_f64(bits: i64) -> f64 {
84    // Flip all but the top bit when that bit is set
85    let mask = ((bits >> 63) as u64) >> 1;
86
87    f64::from_bits((bits as u64) ^ mask)
88}
89
90macro_rules! def_float_endpoint {
91    ($($T:ty, $to_int:ident, $from_int:ident)*) => {
92        $(
93            impl crate::Endpoint for $T {
94                #[inline(always)]
95                fn min_value() -> $T {
96                    <$T>::NEG_INFINITY
97                }
98
99                #[inline(always)]
100                fn max_value() -> $T {
101                    <$T>::INFINITY
102                }
103
104                #[inline(always)]
105                fn is_valid(self) -> bool {
106                    !self.is_nan()
107                }
108
109                fn cmp_end(self, other: Self) -> core::cmp::Ordering {
110                    $to_int(self).cmp(&$to_int(other))
111                }
112
113                fn decrease_toward(self, other: $T) -> Option<$T> {
114                    let this = $to_int(self);
115                    let other = $to_int(other);
116                    if other < this {
117                        this.checked_sub(1).map($from_int)
118                    } else {
119                        None
120                    }
121                }
122
123                fn increase_toward(self, other: $T) -> Option<$T> {
124                    let this = $to_int(self);
125                    let other = $to_int(other);
126                    if other > this {
127                        this.checked_add(1).map($from_int)
128                    } else {
129                        None
130                    }
131                }
132            }
133        )*
134    };
135}
136
137def_float_endpoint!(f32, f32_to_i32, i32_to_f32
138                    f64, f64_to_i64, i64_to_f64);
139
140#[cfg(test)]
141proptest::proptest! {
142    #[test]
143    fn test_f32_i32(x: f32, y: f32) {
144        use crate::Endpoint;
145
146        assert_eq!(x.to_bits(), i32_to_f32(f32_to_i32(x)).to_bits());
147        assert_eq!(y.to_bits(), i32_to_f32(f32_to_i32(y)).to_bits());
148
149        if x.partial_cmp(&0.0) == Some(core::cmp::Ordering::Equal) && y.partial_cmp(&0.0) == Some(core::cmp::Ordering::Equal)  {
150            assert_eq!(x.signum().partial_cmp(&y.signum()).unwrap(), x.cmp_end(y));
151        } else if let Some(ord) = x.partial_cmp(&y) {
152            assert_eq!(ord, x.cmp_end(y));
153        }
154    }
155
156    #[test]
157    fn test_f64_i64(x: f64, y: f64) {
158        use crate::Endpoint;
159
160        assert_eq!(x.to_bits(), i64_to_f64(f64_to_i64(x)).to_bits());
161        assert_eq!(y.to_bits(), i64_to_f64(f64_to_i64(y)).to_bits());
162
163        if x.partial_cmp(&0.0) == Some(core::cmp::Ordering::Equal) && y.partial_cmp(&0.0) == Some(core::cmp::Ordering::Equal)  {
164            assert_eq!(x.signum().partial_cmp(&y.signum()).unwrap(), x.cmp_end(y));
165        } else if let Some(ord) = x.partial_cmp(&y) {
166            assert_eq!(ord, x.cmp_end(y));
167        }
168    }
169}