Skip to main content

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 Sign {
140    /// Return the sign as a string slice for formatting.
141    ///
142    /// Returns `"-"` for [`Negative`], `"+"` for [`Positive`] when `sign_plus`
143    /// is true, or `""` for [`Positive`] when `sign_plus` is false.
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// # use dashu_base::Sign;
149    /// assert_eq!(Sign::Negative.as_sign_str(false), "-");
150    /// assert_eq!(Sign::Positive.as_sign_str(true), "+");
151    /// assert_eq!(Sign::Positive.as_sign_str(false), "");
152    /// ```
153    #[inline]
154    pub const fn as_sign_str(self, sign_plus: bool) -> &'static str {
155        match self {
156            Sign::Negative => "-",
157            Sign::Positive if sign_plus => "+",
158            _ => "",
159        }
160    }
161}
162
163impl From<bool> for Sign {
164    /// Convert boolean value to [Sign], returns [Negative] for `true`
165    #[inline]
166    fn from(v: bool) -> Self {
167        match v {
168            true => Self::Negative,
169            false => Self::Positive,
170        }
171    }
172}
173
174impl From<Sign> for bool {
175    /// Convert [Sign] to boolean value, returns `true` for [Negative]
176    #[inline]
177    fn from(v: Sign) -> Self {
178        match v {
179            Sign::Negative => true,
180            Sign::Positive => false,
181        }
182    }
183}
184
185impl Neg for Sign {
186    type Output = Sign;
187
188    #[inline]
189    fn neg(self) -> Sign {
190        match self {
191            Positive => Negative,
192            Negative => Positive,
193        }
194    }
195}
196
197impl Mul<Sign> for Sign {
198    type Output = Sign;
199
200    #[inline]
201    fn mul(self, rhs: Sign) -> Sign {
202        match (self, rhs) {
203            (Positive, Positive) => Positive,
204            (Positive, Negative) => Negative,
205            (Negative, Positive) => Negative,
206            (Negative, Negative) => Positive,
207        }
208    }
209}
210
211impl Mul<Ordering> for Sign {
212    type Output = Ordering;
213    #[inline]
214    fn mul(self, rhs: Ordering) -> Self::Output {
215        match self {
216            Positive => rhs,
217            Negative => rhs.reverse(),
218        }
219    }
220}
221
222impl Mul<Sign> for Ordering {
223    type Output = Ordering;
224    #[inline]
225    fn mul(self, rhs: Sign) -> Self::Output {
226        match rhs {
227            Positive => self,
228            Negative => self.reverse(),
229        }
230    }
231}
232
233impl MulAssign<Sign> for Sign {
234    #[inline]
235    fn mul_assign(&mut self, rhs: Sign) {
236        *self = *self * rhs;
237    }
238}
239
240impl PartialOrd for Sign {
241    #[inline]
242    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
243        Some(self.cmp(other))
244    }
245}
246
247impl Ord for Sign {
248    #[inline]
249    fn cmp(&self, other: &Self) -> Ordering {
250        match (self, other) {
251            (Positive, Negative) => Ordering::Greater,
252            (Negative, Positive) => Ordering::Less,
253            _ => Ordering::Equal,
254        }
255    }
256}
257
258macro_rules! impl_sign_ops_for_primitives {
259    ($($t:ty)*) => {$(
260        impl Mul<$t> for Sign {
261            type Output = $t;
262
263            #[inline]
264            fn mul(self, rhs: $t) -> Self::Output {
265                match self {
266                    Positive => rhs,
267                    Negative => -rhs
268                }
269            }
270        }
271
272        impl Mul<Sign> for $t {
273            type Output = $t;
274
275            #[inline]
276            fn mul(self, rhs: Sign) -> Self::Output {
277                match rhs {
278                    Positive => self,
279                    Negative => -self
280                }
281            }
282        }
283    )*};
284}
285impl_sign_ops_for_primitives!(i8 i16 i32 i64 i128 isize f32 f64);
286
287macro_rules! impl_signed_for_int {
288    ($($t:ty)*) => {$(
289        impl Signed for $t {
290            #[inline]
291            fn sign(&self) -> Sign {
292                Sign::from(*self < 0)
293            }
294        }
295
296        #[allow(deprecated)]
297        impl AbsEq for $t {
298            #[inline]
299            fn abs_eq(&self, rhs: &Self) -> bool {
300                self.abs() == rhs.abs()
301            }
302        }
303
304        impl AbsOrd for $t {
305            #[inline]
306            fn abs_cmp(&self, rhs: &Self) -> Ordering {
307                self.abs().cmp(&rhs.abs())
308            }
309        }
310    )*};
311}
312impl_signed_for_int!(i8 i16 i32 i64 i128 isize);
313
314macro_rules! impl_signed_for_float {
315    ($t:ty, $shift:literal) => {
316        impl Signed for $t {
317            #[inline]
318            fn sign(&self) -> Sign {
319                if self.is_nan() {
320                    panic!("nan doesn't have a sign")
321                } else if *self == -0. {
322                    return Sign::Positive;
323                }
324                Sign::from(self.to_bits() >> $shift > 0)
325            }
326        }
327
328        #[allow(deprecated)]
329        impl AbsEq for $t {
330            #[inline]
331            fn abs_eq(&self, rhs: &Self) -> bool {
332                self.abs() == rhs.abs()
333            }
334        }
335
336        impl AbsOrd for $t {
337            #[inline]
338            fn abs_cmp(&self, rhs: &Self) -> Ordering {
339                self.abs()
340                    .partial_cmp(&rhs.abs())
341                    .expect("abs_cmp is not allowed on NaNs!")
342            }
343        }
344    };
345}
346impl_signed_for_float!(f32, 31);
347impl_signed_for_float!(f64, 63);
348
349#[cfg(test)]
350mod tests {
351    use super::*;
352
353    #[test]
354    fn test_signed() {
355        assert_eq!(0i32.sign(), Sign::Positive);
356        assert_eq!(1i32.sign(), Sign::Positive);
357        assert_eq!((-1i32).sign(), Sign::Negative);
358
359        assert_eq!(0f32.sign(), Sign::Positive);
360        assert_eq!((-0f32).sign(), Sign::Positive);
361        assert_eq!(1f32.sign(), Sign::Positive);
362        assert_eq!((-1f32).sign(), Sign::Negative);
363    }
364
365    #[test]
366    #[should_panic]
367    fn test_signed_nan() {
368        let _ = f32::NAN.sign();
369    }
370
371    #[test]
372    #[should_panic]
373    fn test_abs_cmp_nan() {
374        let _ = f32::NAN.abs_cmp(&f32::NAN);
375    }
376}