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    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    #[inline]
81    fn neg(self) -> Self::Output {
82        match self {
83            Sign::Positive => Sign::Negative,
84            Sign::Zero => Sign::Zero,
85            Sign::Negative => Sign::Positive,
86        }
87    }
88}
89
90impl Not for Sign {
91    type Output = Self;
92
93    #[inline]
94    fn not(self) -> Self::Output {
95        match self {
96            Sign::Positive => Sign::Negative,
97            Sign::Zero => Sign::Zero,
98            Sign::Negative => Sign::Positive,
99        }
100    }
101}
102
103impl NonZero for Sign {
104    #[inline]
105    fn is_not_zero(&self) -> bool {
106        *self != Sign::Zero
107    }
108}
109
110impl MulAssign for Sign {
111    #[inline]
112    fn mul_assign(&mut self, rhs: Self) {
113        *self = match (&self, rhs) {
114            (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => Sign::Positive,
115            (Sign::Zero, _) | (_, Sign::Zero) => Sign::Zero,
116            (Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => Sign::Negative,
117        };
118    }
119}
120
121impl Mul for Sign {
122    type Output = Self;
123
124    #[inline]
125    fn mul(mut self, rhs: Self) -> Self::Output {
126        self *= rhs;
127        self
128    }
129}
130
131impl PartialOrd for Sign {
132    #[inline]
133    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
134        match (self, other) {
135            (Sign::Negative, Sign::Zero | Sign::Positive) | (Sign::Zero, Sign::Positive) => Some(Ordering::Less),
136            (Sign::Zero, Sign::Zero) => Some(Ordering::Equal),
137            (Sign::Positive, Sign::Zero | Sign::Negative) | (Sign::Zero, Sign::Negative) => Some(Ordering::Greater),
138            (Sign::Negative, Sign::Negative) | (Sign::Positive, Sign::Positive) => None,
139        }
140    }
141}
142
143impl From<NonZeroSign> for Sign {
144    #[inline]
145    fn from(sign: NonZeroSign) -> Self {
146        match sign {
147            NonZeroSign::Positive => Sign::Positive,
148            NonZeroSign::Negative => Sign::Negative,
149        }
150    }
151}
152
153impl fmt::Display for Sign {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        f.write_str(match self {
156            Sign::Negative => "-",
157            Sign::Zero => "0",
158            Sign::Positive => "+",
159        })
160    }
161}
162
163#[cfg(test)]
164mod test {
165    use crate::{Sign, Signed};
166    use crate::RB;
167
168    #[test]
169    fn test_integer() {
170        assert_eq!(Signed::signum(&0_i32), Sign::Zero);
171        assert_eq!(Signed::signum(&-1), Sign::Negative);
172        assert_eq!(Signed::signum(&1_i128), Sign::Positive);
173        assert_eq!(Signed::signum(&0_u32), Sign::Zero);
174        assert_eq!(Signed::signum(&1_u64), Sign::Positive);
175    }
176
177    #[test]
178    fn test_sign_boolean() {
179        assert!(6.is_positive());
180        assert!((-5).is_negative());
181        assert!(!0.is_positive());
182        assert!(!0.is_negative());
183        assert!(!6.is_negative());
184        assert!(!(-5).is_positive());
185    }
186
187    #[test]
188    fn test_sign() {
189        assert_eq!(!Sign::Zero, Sign::Zero);
190        assert_eq!(!Sign::Positive, Sign::Negative);
191        assert_eq!(!Sign::Negative, Sign::Positive);
192        assert_eq!(Sign::Positive, -Sign::Negative);
193        assert_eq!(Sign::Positive * Sign::Positive, Sign::Positive);
194        assert_eq!(Sign::Negative * Sign::Negative, Sign::Positive);
195        assert_eq!(Sign::Negative * Sign::Zero, -Sign::Zero);
196    }
197
198    #[test]
199    fn test_sign_ord() {
200        assert_eq!(Sign::Zero < Sign::Positive, true);
201        assert_eq!(Sign::Positive < Sign::Positive, false);
202        assert_eq!(Sign::Positive == Sign::Positive, true);
203        assert_eq!(Sign::Zero == Sign::Zero, true);
204        assert_eq!(Sign::Negative < Sign::Positive, true);
205        assert_eq!(Sign::Negative < Sign::Zero, true);
206        assert_eq!(Sign::Negative < Sign::Negative, false);
207    }
208
209    #[test]
210    fn test_sign_conversion() {
211        assert_eq!(Sign::Positive, crate::NonZeroSign::Positive.into());
212    }
213
214    #[test]
215    fn test_numbers() {
216        assert_eq!(Signed::signum(&1), Sign::Positive);
217        assert_eq!(Signed::signum(&0), Sign::Zero);
218        assert_eq!(Signed::signum(&(-1)), Sign::Negative);
219
220        assert_eq!(RB!(0).signum(), Sign::Zero);
221        assert_eq!(RB!(1).signum(), Sign::Positive);
222        assert_eq!(RB!(-1).signum(), Sign::Negative);
223
224        assert_eq!(RB!(-1).signum() * RB!(-1).signum(), Sign::Positive);
225        assert_eq!(RB!(1).signum() * RB!(1).signum(), Sign::Positive);
226        assert_eq!(RB!(-1).signum() * RB!(1).signum(), Sign::Negative);
227    }
228}