dashu_base/
sign.rs

1//! Trait definitions for sign related operations.
2
3use core::{
4    cmp::Ordering,
5    ops::{Mul, MulAssign, Neg},
6};
7
8/// Absolute value.
9///
10/// # Examples
11/// ```
12/// use dashu_base::Abs;
13/// assert_eq!((-5).abs(), 5);
14/// ```
15pub trait Abs {
16    type Output;
17
18    fn abs(self) -> Self::Output;
19}
20
21/// Unsigned absolute value.
22///
23/// # Examples
24/// ```
25/// # use dashu_base::UnsignedAbs;
26/// assert_eq!((-5i8).unsigned_abs(), 5u8);
27/// ```
28pub trait UnsignedAbs {
29    type Output;
30
31    fn unsigned_abs(self) -> Self::Output;
32}
33
34/// Check whether the magnitude of this number is equal the magnitude of the other number
35///
36/// # Examples
37///
38/// ```
39/// # use dashu_base::AbsEq;
40/// assert!(5.abs_eq(&-5));
41/// assert!(12.3.abs_eq(&-12.3));
42/// ```
43#[deprecated(since = "0.5.0", note = "AbsEq will be moved in AbsOrd in v0.5")] // TODO(v0.5): deprecate
44pub trait AbsEq<Rhs = Self> {
45    fn abs_eq(&self, rhs: &Rhs) -> bool;
46}
47
48/// Compare the magnitude of this number to the magnitude of the other number
49///
50/// Note that this function will panic if either of the numbers is NaN.
51///
52/// # Examples
53///
54/// ```
55/// # use dashu_base::AbsOrd;
56/// assert!(5.abs_cmp(&-6).is_le());
57/// assert!(12.3.abs_cmp(&-12.3).is_eq());
58/// ```
59pub trait AbsOrd<Rhs = Self> {
60    fn abs_cmp(&self, rhs: &Rhs) -> Ordering;
61}
62
63/// This trait marks the number is signed.
64///
65/// Notice that the negative zeros (of [f32] and [f64]) are still considered
66/// to have a positive sign.
67///
68/// # Examples
69///
70/// ```
71/// # use dashu_base::{Signed, Sign};
72/// assert_eq!((-2).sign(), Sign::Negative);
73/// assert_eq!((-2.4).sign(), Sign::Negative);
74/// assert_eq!((0.).sign(), Sign::Positive);
75///
76/// assert!(2.is_positive());
77/// assert!((-2.4).is_negative());
78/// assert!((0.).is_positive());
79/// ```
80pub trait Signed {
81    fn sign(&self) -> Sign;
82
83    #[inline]
84    fn is_positive(&self) -> bool {
85        self.sign() == Sign::Positive
86    }
87    #[inline]
88    fn is_negative(&self) -> bool {
89        self.sign() == Sign::Negative
90    }
91}
92
93macro_rules! impl_abs_ops_prim {
94    ($($signed:ty;)*) => {$( // this branch is only for float
95        impl Abs for $signed {
96            type Output = $signed;
97            #[inline]
98            fn abs(self) -> Self::Output {
99                if self.is_nan() || self >= 0. {
100                    self
101                } else {
102                    -self
103                }
104            }
105        }
106    )*};
107    ($($signed:ty => $unsigned:ty;)*) => {$(
108        impl Abs for $signed {
109            type Output = $signed;
110            #[inline]
111            fn abs(self) -> Self::Output {
112                <$signed>::abs(self)
113            }
114        }
115
116        impl UnsignedAbs for $signed {
117            type Output = $unsigned;
118            #[inline]
119            fn unsigned_abs(self) -> Self::Output {
120                <$signed>::unsigned_abs(self)
121            }
122        }
123    )*}
124}
125impl_abs_ops_prim!(i8 => u8; i16 => u16; i32 => u32; i64 => u64; i128 => u128; isize => usize;);
126impl_abs_ops_prim!(f32; f64;);
127
128/// An enum representing the sign of a number
129///
130/// A sign can be converted to or from a boolean value, assuming `true` is [Negative].
131#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
132pub enum Sign {
133    Positive,
134    Negative,
135}
136
137use Sign::*;
138
139impl From<bool> for Sign {
140    /// Convert boolean value to [Sign], returns [Negative] for `true`
141    #[inline]
142    fn from(v: bool) -> Self {
143        match v {
144            true => Self::Negative,
145            false => Self::Positive,
146        }
147    }
148}
149
150impl From<Sign> for bool {
151    /// Convert [Sign] to boolean value, returns `true` for [Negative]
152    #[inline]
153    fn from(v: Sign) -> Self {
154        match v {
155            Sign::Negative => true,
156            Sign::Positive => false,
157        }
158    }
159}
160
161impl Neg for Sign {
162    type Output = Sign;
163
164    #[inline]
165    fn neg(self) -> Sign {
166        match self {
167            Positive => Negative,
168            Negative => Positive,
169        }
170    }
171}
172
173impl Mul<Sign> for Sign {
174    type Output = Sign;
175
176    #[inline]
177    fn mul(self, rhs: Sign) -> Sign {
178        match (self, rhs) {
179            (Positive, Positive) => Positive,
180            (Positive, Negative) => Negative,
181            (Negative, Positive) => Negative,
182            (Negative, Negative) => Positive,
183        }
184    }
185}
186
187impl Mul<Ordering> for Sign {
188    type Output = Ordering;
189    #[inline]
190    fn mul(self, rhs: Ordering) -> Self::Output {
191        match self {
192            Positive => rhs,
193            Negative => rhs.reverse(),
194        }
195    }
196}
197
198impl Mul<Sign> for Ordering {
199    type Output = Ordering;
200    #[inline]
201    fn mul(self, rhs: Sign) -> Self::Output {
202        match rhs {
203            Positive => self,
204            Negative => self.reverse(),
205        }
206    }
207}
208
209impl MulAssign<Sign> for Sign {
210    #[inline]
211    fn mul_assign(&mut self, rhs: Sign) {
212        *self = *self * rhs;
213    }
214}
215
216impl PartialOrd for Sign {
217    #[inline]
218    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
219        Some(self.cmp(other))
220    }
221}
222
223impl Ord for Sign {
224    #[inline]
225    fn cmp(&self, other: &Self) -> Ordering {
226        match (self, other) {
227            (Positive, Negative) => Ordering::Greater,
228            (Negative, Positive) => Ordering::Less,
229            _ => Ordering::Equal,
230        }
231    }
232}
233
234macro_rules! impl_sign_ops_for_primitives {
235    ($($t:ty)*) => {$(
236        impl Mul<$t> for Sign {
237            type Output = $t;
238
239            #[inline]
240            fn mul(self, rhs: $t) -> Self::Output {
241                match self {
242                    Positive => rhs,
243                    Negative => -rhs
244                }
245            }
246        }
247
248        impl Mul<Sign> for $t {
249            type Output = $t;
250
251            #[inline]
252            fn mul(self, rhs: Sign) -> Self::Output {
253                match rhs {
254                    Positive => self,
255                    Negative => -self
256                }
257            }
258        }
259    )*};
260}
261impl_sign_ops_for_primitives!(i8 i16 i32 i64 i128 isize f32 f64);
262
263macro_rules! impl_signed_for_int {
264    ($($t:ty)*) => {$(
265        impl Signed for $t {
266            #[inline]
267            fn sign(&self) -> Sign {
268                Sign::from(*self < 0)
269            }
270        }
271
272        #[allow(deprecated)]
273        impl AbsEq for $t {
274            #[inline]
275            fn abs_eq(&self, rhs: &Self) -> bool {
276                self.abs() == rhs.abs()
277            }
278        }
279
280        impl AbsOrd for $t {
281            #[inline]
282            fn abs_cmp(&self, rhs: &Self) -> Ordering {
283                self.abs().cmp(&rhs.abs())
284            }
285        }
286    )*};
287}
288impl_signed_for_int!(i8 i16 i32 i64 i128 isize);
289
290macro_rules! impl_signed_for_float {
291    ($t:ty, $shift:literal) => {
292        impl Signed for $t {
293            #[inline]
294            fn sign(&self) -> Sign {
295                if self.is_nan() {
296                    panic!("nan doesn't have a sign")
297                } else if *self == -0. {
298                    return Sign::Positive;
299                }
300                Sign::from(self.to_bits() >> $shift > 0)
301            }
302        }
303
304        #[allow(deprecated)]
305        impl AbsEq for $t {
306            #[inline]
307            fn abs_eq(&self, rhs: &Self) -> bool {
308                self.abs() == rhs.abs()
309            }
310        }
311
312        impl AbsOrd for $t {
313            #[inline]
314            fn abs_cmp(&self, rhs: &Self) -> Ordering {
315                self.abs()
316                    .partial_cmp(&rhs.abs())
317                    .expect("abs_cmp is not allowed on NaNs!")
318            }
319        }
320    };
321}
322impl_signed_for_float!(f32, 31);
323impl_signed_for_float!(f64, 63);
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328
329    #[test]
330    fn test_signed() {
331        assert_eq!(0i32.sign(), Sign::Positive);
332        assert_eq!(1i32.sign(), Sign::Positive);
333        assert_eq!((-1i32).sign(), Sign::Negative);
334
335        assert_eq!(0f32.sign(), Sign::Positive);
336        assert_eq!((-0f32).sign(), Sign::Positive);
337        assert_eq!(1f32.sign(), Sign::Positive);
338        assert_eq!((-1f32).sign(), Sign::Negative);
339    }
340
341    #[test]
342    #[should_panic]
343    fn test_signed_nan() {
344        let _ = f32::NAN.sign();
345    }
346
347    #[test]
348    #[should_panic]
349    fn test_abs_cmp_nan() {
350        let _ = f32::NAN.abs_cmp(&f32::NAN);
351    }
352}