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    fn negate(&mut self);
36}
37
38/// Sign with a zero variant.
39#[derive(Eq, PartialEq, Copy, Clone, Debug)]
40pub enum Sign {
41    /// x > 0
42    Positive = 1,
43    /// x == 0
44    Zero = 0,
45    /// x < 0
46    Negative = -1,
47}
48
49impl Signed for Sign {
50    #[inline]
51    fn signum(&self) -> Sign {
52        *self
53    }
54
55    #[inline]
56    fn is_positive(&self) -> bool {
57        *self == Sign::Positive
58    }
59
60    #[inline]
61    fn is_negative(&self) -> bool {
62        *self == Sign::Negative
63    }
64}
65
66impl Negateable for Sign {
67    #[inline]
68    fn negate(&mut self) {
69        match self {
70            Sign::Positive => *self = Sign::Negative,
71            Sign::Zero => {}
72            Sign::Negative => *self = Sign::Positive,
73        }
74    }
75}
76
77impl Neg for Sign {
78    type Output = Self;
79
80    #[must_use]
81    #[inline]
82    fn neg(self) -> Self::Output {
83        match self {
84            Sign::Positive => Sign::Negative,
85            Sign::Zero => Sign::Zero,
86            Sign::Negative => Sign::Positive,
87        }
88    }
89}
90
91impl Not for Sign {
92    type Output = Self;
93
94    #[must_use]
95    #[inline]
96    fn not(self) -> Self::Output {
97        match self {
98            Sign::Positive => Sign::Negative,
99            Sign::Zero => Sign::Zero,
100            Sign::Negative => Sign::Positive,
101        }
102    }
103}
104
105impl NonZero for Sign {
106    #[must_use]
107    #[inline]
108    fn is_not_zero(&self) -> bool {
109        *self != Sign::Zero
110    }
111}
112
113impl MulAssign for Sign {
114    #[inline]
115    fn mul_assign(&mut self, rhs: Self) {
116        *self = match (&self, rhs) {
117            (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => Sign::Positive,
118            (Sign::Zero, _) | (_, Sign::Zero) => Sign::Zero,
119            (Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => Sign::Negative,
120        };
121    }
122}
123
124impl Mul for Sign {
125    type Output = Self;
126
127    #[must_use]
128    #[inline]
129    fn mul(mut self, rhs: Self) -> Self::Output {
130        self *= rhs;
131        self
132    }
133}
134
135impl PartialOrd for Sign {
136    #[must_use]
137    #[inline]
138    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
139        match (self, other) {
140            (Sign::Negative, Sign::Zero | Sign::Positive) | (Sign::Zero, Sign::Positive) => Some(Ordering::Less),
141            (Sign::Zero, Sign::Zero) => Some(Ordering::Equal),
142            (Sign::Positive, Sign::Zero | Sign::Negative) | (Sign::Zero, Sign::Negative) => Some(Ordering::Greater),
143            (Sign::Negative, Sign::Negative) | (Sign::Positive, Sign::Positive) => None,
144        }
145    }
146}
147
148impl From<NonZeroSign> for Sign {
149    #[must_use]
150    #[inline]
151    fn from(sign: NonZeroSign) -> Self {
152        match sign {
153            NonZeroSign::Positive => Sign::Positive,
154            NonZeroSign::Negative => Sign::Negative,
155        }
156    }
157}
158
159impl fmt::Display for Sign {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        f.write_str(match self {
162            Sign::Negative => "-",
163            Sign::Zero => "0",
164            Sign::Positive => "+",
165        })
166    }
167}
168
169#[cfg(test)]
170mod test {
171    use crate::{Sign, Signed};
172    use crate::RB;
173
174    #[test]
175    fn test_integer() {
176        assert_eq!(Signed::signum(&0_i32), Sign::Zero);
177        assert_eq!(Signed::signum(&-1), Sign::Negative);
178        assert_eq!(Signed::signum(&1_i128), Sign::Positive);
179        assert_eq!(Signed::signum(&0_u32), Sign::Zero);
180        assert_eq!(Signed::signum(&1_u64), Sign::Positive);
181    }
182
183    #[test]
184    fn test_sign_boolean() {
185        assert!(6.is_positive());
186        assert!((-5).is_negative());
187        assert!(!0.is_positive());
188        assert!(!0.is_negative());
189        assert!(!6.is_negative());
190        assert!(!(-5).is_positive());
191    }
192
193    #[test]
194    fn test_sign() {
195        assert_eq!(!Sign::Zero, Sign::Zero);
196        assert_eq!(!Sign::Positive, Sign::Negative);
197        assert_eq!(!Sign::Negative, Sign::Positive);
198        assert_eq!(Sign::Positive, -Sign::Negative);
199        assert_eq!(Sign::Positive * Sign::Positive, Sign::Positive);
200        assert_eq!(Sign::Negative * Sign::Negative, Sign::Positive);
201        assert_eq!(Sign::Negative * Sign::Zero, -Sign::Zero);
202    }
203
204    #[test]
205    fn test_sign_ord() {
206        assert_eq!(Sign::Zero < Sign::Positive, true);
207        assert_eq!(Sign::Positive < Sign::Positive, false);
208        assert_eq!(Sign::Positive == Sign::Positive, true);
209        assert_eq!(Sign::Zero == Sign::Zero, true);
210        assert_eq!(Sign::Negative < Sign::Positive, true);
211        assert_eq!(Sign::Negative < Sign::Zero, true);
212        assert_eq!(Sign::Negative < Sign::Negative, false);
213    }
214
215    #[test]
216    fn test_sign_conversion() {
217        assert_eq!(Sign::Positive, crate::NonZeroSign::Positive.into());
218    }
219
220    #[test]
221    fn test_numbers() {
222        assert_eq!(Signed::signum(&1), Sign::Positive);
223        assert_eq!(Signed::signum(&0), Sign::Zero);
224        assert_eq!(Signed::signum(&(-1)), Sign::Negative);
225
226        assert_eq!(RB!(0).signum(), Sign::Zero);
227        assert_eq!(RB!(1).signum(), Sign::Positive);
228        assert_eq!(RB!(-1).signum(), Sign::Negative);
229
230        assert_eq!(RB!(-1).signum() * RB!(-1).signum(), Sign::Positive);
231        assert_eq!(RB!(1).signum() * RB!(1).signum(), Sign::Positive);
232        assert_eq!(RB!(-1).signum() * RB!(1).signum(), Sign::Negative);
233    }
234}