Skip to main content

relp_num/
sign.rs

1/// # Signs
2///
3/// Existing sign traits often use the values `1`, `0` and `-1` to represent the sign of a number.
4/// This is limiting because some types should never be zero is certain code sections, and having
5/// to match on the `0` value is then unidiomatic. Moreover, such a sign is bulky for ratios, which
6/// have a separate sign field anyway.
7use std::cmp::Ordering;
8use std::fmt;
9use std::ops::{Mul, MulAssign, Neg, Not};
10
11use crate::non_zero::NonZeroSign;
12use crate::NonZero;
13
14/// # Signed numbers
15///
16/// A number that is positive, negative or zero.
17pub trait Signed {
18    /// Returns the sign of the number.
19    fn signum(&self) -> Sign;
20    /// Whether the number is (strictly) greater than zero.
21    #[inline]
22    fn is_positive(&self) -> bool {
23        self.signum() == Sign::Positive
24    }
25    /// Whether the number is (strictly) smaller than zero.
26    #[inline]
27    fn is_negative(&self) -> bool {
28        self.signum() == Sign::Negative
29    }
30}
31
32/// A number that can be negated, that is, who's sign can be flipped.
33pub trait Negateable: Signed {
34    /// Negate the number, e.g. go from 1 to -1.
35    ///
36    /// # Panics
37    ///
38    /// When the negation is not representable. A two's complement integer has one more negative
39    /// value than positive ones, so `MIN` has no counterpart; every other implementation in this
40    /// crate is total, because a sign and magnitude representation negates by writing the sign.
41    fn negate(&mut self);
42}
43
44/// Sign with a zero variant.
45#[derive(Eq, PartialEq, Copy, Clone, Debug)]
46pub enum Sign {
47    /// x > 0
48    Positive = 1,
49    /// x == 0
50    Zero = 0,
51    /// x < 0
52    Negative = -1,
53}
54
55impl Signed for Sign {
56    #[inline]
57    fn signum(&self) -> Sign {
58        *self
59    }
60
61    #[inline]
62    fn is_positive(&self) -> bool {
63        *self == Sign::Positive
64    }
65
66    #[inline]
67    fn is_negative(&self) -> bool {
68        *self == Sign::Negative
69    }
70}
71
72impl Negateable for Sign {
73    #[inline]
74    fn negate(&mut self) {
75        match self {
76            Sign::Positive => *self = Sign::Negative,
77            Sign::Zero => {}
78            Sign::Negative => *self = Sign::Positive,
79        }
80    }
81}
82
83impl Neg for Sign {
84    type Output = Self;
85
86    #[inline]
87    fn neg(self) -> Self::Output {
88        match self {
89            Sign::Positive => Sign::Negative,
90            Sign::Zero => Sign::Zero,
91            Sign::Negative => Sign::Positive,
92        }
93    }
94}
95
96impl Not for Sign {
97    type Output = Self;
98
99    #[inline]
100    fn not(self) -> Self::Output {
101        match self {
102            Sign::Positive => Sign::Negative,
103            Sign::Zero => Sign::Zero,
104            Sign::Negative => Sign::Positive,
105        }
106    }
107}
108
109impl NonZero for Sign {
110    #[inline]
111    fn is_not_zero(&self) -> bool {
112        *self != Sign::Zero
113    }
114}
115
116impl MulAssign for Sign {
117    #[inline]
118    fn mul_assign(&mut self, rhs: Self) {
119        *self = match (&self, rhs) {
120            (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => Sign::Positive,
121            (Sign::Zero, _) | (_, Sign::Zero) => Sign::Zero,
122            (Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => Sign::Negative,
123        };
124    }
125}
126
127impl Mul for Sign {
128    type Output = Self;
129
130    #[inline]
131    fn mul(mut self, rhs: Self) -> Self::Output {
132        self *= rhs;
133        self
134    }
135}
136
137/// Signs are totally ordered as `Negative < Zero < Positive`, matching the discriminants.
138impl Ord for Sign {
139    #[inline]
140    fn cmp(&self, other: &Self) -> Ordering {
141        (*self as i8).cmp(&(*other as i8))
142    }
143}
144
145impl PartialOrd for Sign {
146    #[inline]
147    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
148        Some(self.cmp(other))
149    }
150}
151
152impl From<NonZeroSign> for Sign {
153    #[inline]
154    fn from(sign: NonZeroSign) -> Self {
155        match sign {
156            NonZeroSign::Positive => Sign::Positive,
157            NonZeroSign::Negative => Sign::Negative,
158        }
159    }
160}
161
162impl fmt::Display for Sign {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        f.write_str(match self {
165            Sign::Negative => "-",
166            Sign::Zero => "0",
167            Sign::Positive => "+",
168        })
169    }
170}
171
172#[cfg(test)]
173mod test {
174    use std::cmp::Ordering;
175
176    use crate::{Sign, Signed};
177    use crate::RB;
178
179    #[test]
180    fn test_integer() {
181        assert_eq!(Signed::signum(&0_i32), Sign::Zero);
182        assert_eq!(Signed::signum(&-1), Sign::Negative);
183        assert_eq!(Signed::signum(&1_i128), Sign::Positive);
184        assert_eq!(Signed::signum(&0_u32), Sign::Zero);
185        assert_eq!(Signed::signum(&1_u64), Sign::Positive);
186    }
187
188    #[test]
189    fn test_sign_boolean() {
190        assert!(6.is_positive());
191        assert!((-5).is_negative());
192        assert!(!0.is_positive());
193        assert!(!0.is_negative());
194        assert!(!6.is_negative());
195        assert!(!(-5).is_positive());
196    }
197
198    #[test]
199    fn test_sign() {
200        assert_eq!(!Sign::Zero, Sign::Zero);
201        assert_eq!(!Sign::Positive, Sign::Negative);
202        assert_eq!(!Sign::Negative, Sign::Positive);
203        assert_eq!(Sign::Positive, -Sign::Negative);
204        assert_eq!(Sign::Positive * Sign::Positive, Sign::Positive);
205        assert_eq!(Sign::Negative * Sign::Negative, Sign::Positive);
206        assert_eq!(Sign::Negative * Sign::Zero, -Sign::Zero);
207    }
208
209    /// `a == b` must imply `partial_cmp(a, b) == Some(Equal)`.
210    #[test]
211    fn test_sign_ord_contract() {
212        for a in [Sign::Negative, Sign::Zero, Sign::Positive] {
213            assert_eq!(a.partial_cmp(&a), Some(Ordering::Equal));
214            assert!(a <= a);
215            assert!(a >= a);
216            for b in [Sign::Negative, Sign::Zero, Sign::Positive] {
217                assert_eq!(a == b, a.partial_cmp(&b) == Some(Ordering::Equal));
218                assert_eq!(a.partial_cmp(&b), Some(a.cmp(&b)));
219                assert_eq!(a.cmp(&b), b.cmp(&a).reverse());
220            }
221        }
222        assert!(Sign::Negative < Sign::Zero && Sign::Zero < Sign::Positive);
223    }
224
225    #[test]
226    fn test_sign_ord() {
227        assert_eq!(Sign::Zero < Sign::Positive, true);
228        assert_eq!(Sign::Positive < Sign::Positive, false);
229        assert_eq!(Sign::Positive == Sign::Positive, true);
230        assert_eq!(Sign::Zero == Sign::Zero, true);
231        assert_eq!(Sign::Negative < Sign::Positive, true);
232        assert_eq!(Sign::Negative < Sign::Zero, true);
233        assert_eq!(Sign::Negative < Sign::Negative, false);
234    }
235
236    #[test]
237    fn test_sign_conversion() {
238        assert_eq!(Sign::Positive, crate::NonZeroSign::Positive.into());
239    }
240
241    #[test]
242    fn test_numbers() {
243        assert_eq!(Signed::signum(&1), Sign::Positive);
244        assert_eq!(Signed::signum(&0), Sign::Zero);
245        assert_eq!(Signed::signum(&(-1)), Sign::Negative);
246
247        assert_eq!(RB!(0).signum(), Sign::Zero);
248        assert_eq!(RB!(1).signum(), Sign::Positive);
249        assert_eq!(RB!(-1).signum(), Sign::Negative);
250
251        assert_eq!(RB!(-1).signum() * RB!(-1).signum(), Sign::Positive);
252        assert_eq!(RB!(1).signum() * RB!(1).signum(), Sign::Positive);
253        assert_eq!(RB!(-1).signum() * RB!(1).signum(), Sign::Negative);
254    }
255}