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
52fn f32_to_i32(x: f32) -> i32 {
53    // This is sign-magnitude.  Convert to i32 by flipping all but
54    // the top bit when that bit is set.
55    let bits = x.to_bits();
56    let top = 1u32 << 31;
57    let mask = bits & top;
58
59    (bits ^ (mask - (mask >> 31))) as i32
60}
61
62fn i32_to_f32(bits: i32) -> f32 {
63    // Flip all but the top bit when that bit is set
64    let bits = bits as u32;
65    let top = 1u32 << 31;
66    let mask = bits & top;
67
68    f32::from_bits(bits ^ (mask - (mask >> 31)))
69}
70
71fn f64_to_i64(x: f64) -> i64 {
72    // This is sign-magnitude.  Convert to i32 by flipping all but
73    // the top bit when that bit is set.
74    let bits = x.to_bits();
75    let top = 1u64 << 63;
76    let mask = bits & top;
77
78    (bits ^ (mask - (mask >> 63))) as i64
79}
80
81fn i64_to_f64(bits: i64) -> f64 {
82    // Flip all but the top bit when that bit is set
83    let bits = bits as u64;
84    let top = 1u64 << 63;
85    let mask = bits & top;
86
87    f64::from_bits(bits ^ (mask - (mask >> 63)))
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                #[inline(always)]
110                fn cmp_end(self, other: Self) -> core::cmp::Ordering {
111                    $to_int(self).cmp(&$to_int(other))
112                }
113
114                #[inline(always)]
115                fn decrease_toward(self, other: $T) -> Option<$T> {
116                    let this = $to_int(self);
117                    let other = $to_int(other);
118                    if other < this {
119                        this.checked_sub(1).map($from_int)
120                    } else {
121                        None
122                    }
123                }
124
125                #[inline(always)]
126                fn increase_toward(self, other: $T) -> Option<$T> {
127                    let this = $to_int(self);
128                    let other = $to_int(other);
129                    if other > this {
130                        this.checked_add(1).map($from_int)
131                    } else {
132                        None
133                    }
134                }
135            }
136        )*
137    };
138}
139
140def_float_endpoint!(f32, f32_to_i32, i32_to_f32
141                    f64, f64_to_i64, i64_to_f64);
142
143#[cfg(test)]
144proptest::proptest! {
145    #[test]
146    fn test_f32_i32(x: f32, y: f32) {
147        use crate::Endpoint;
148
149        assert_eq!(x.to_bits(), i32_to_f32(f32_to_i32(x)).to_bits());
150        assert_eq!(y.to_bits(), i32_to_f32(f32_to_i32(y)).to_bits());
151
152        if x.partial_cmp(&0.0) == Some(core::cmp::Ordering::Equal) && y.partial_cmp(&0.0) == Some(core::cmp::Ordering::Equal)  {
153            assert_eq!(x.signum().partial_cmp(&y.signum()).unwrap(), x.cmp_end(y));
154        } else if let Some(ord) = x.partial_cmp(&y) {
155            assert_eq!(ord, x.cmp_end(y));
156        }
157    }
158
159    #[test]
160    fn test_f64_i64(x: f64, y: f64) {
161        use crate::Endpoint;
162
163        assert_eq!(x.to_bits(), i64_to_f64(f64_to_i64(x)).to_bits());
164        assert_eq!(y.to_bits(), i64_to_f64(f64_to_i64(y)).to_bits());
165
166        if x.partial_cmp(&0.0) == Some(core::cmp::Ordering::Equal) && y.partial_cmp(&0.0) == Some(core::cmp::Ordering::Equal)  {
167            assert_eq!(x.signum().partial_cmp(&y.signum()).unwrap(), x.cmp_end(y));
168        } else if let Some(ord) = x.partial_cmp(&y) {
169            assert_eq!(ord, x.cmp_end(y));
170        }
171    }
172}