Skip to main content

implicit_predicates_core/
sign.rs

1//! The sign of a quantity: the result type of every predicate.
2
3use core::cmp::Ordering;
4use core::ops::{Mul, Neg};
5
6/// The sign of a real quantity.
7///
8/// The discriminants are `-1`, `0` and `1`, so `sign as i8` (or
9/// `i8::from(sign)`) gives the usual integer sign, and signs are ordered as
10/// those integers: `Negative < Zero < Positive`.
11///
12/// # Examples
13///
14/// ```
15/// use implicit_predicates_core::Sign;
16///
17/// assert_eq!(Sign::Negative as i8, -1);
18/// assert_eq!(Sign::Zero as i8, 0);
19/// assert_eq!(Sign::Positive as i8, 1);
20/// assert!(Sign::Negative < Sign::Zero && Sign::Zero < Sign::Positive);
21/// ```
22#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
23#[repr(i8)]
24pub enum Sign {
25    /// Strictly less than zero.
26    Negative = -1,
27    /// Exactly zero.
28    Zero = 0,
29    /// Strictly greater than zero.
30    Positive = 1,
31}
32
33impl Sign {
34    /// Returns the sign of `x`. Both `0.0` and `-0.0` give [`Sign::Zero`].
35    ///
36    /// NaN is not a valid input: debug builds panic on it, and release builds
37    /// return [`Sign::Zero`], which is meaningless.
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use implicit_predicates_core::Sign;
43    ///
44    /// assert_eq!(Sign::of(2.5), Sign::Positive);
45    /// assert_eq!(Sign::of(-2.5), Sign::Negative);
46    /// assert_eq!(Sign::of(-0.0), Sign::Zero);
47    /// ```
48    #[must_use]
49    pub fn of(x: f64) -> Sign {
50        debug_assert!(!x.is_nan(), "Sign::of called with NaN");
51        if x > 0.0 {
52            Sign::Positive
53        } else if x < 0.0 {
54            Sign::Negative
55        } else {
56            Sign::Zero
57        }
58    }
59
60    /// Returns `true` if the sign is [`Sign::Zero`].
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use implicit_predicates_core::Sign;
66    ///
67    /// assert!(Sign::Zero.is_zero());
68    /// assert!(!Sign::Positive.is_zero());
69    /// ```
70    #[must_use]
71    pub fn is_zero(self) -> bool {
72        self == Sign::Zero
73    }
74
75    /// Returns `true` if the sign is [`Sign::Positive`].
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use implicit_predicates_core::Sign;
81    ///
82    /// assert!(Sign::Positive.is_positive());
83    /// assert!(!Sign::Zero.is_positive());
84    /// ```
85    #[must_use]
86    pub fn is_positive(self) -> bool {
87        self == Sign::Positive
88    }
89
90    /// Returns `true` if the sign is [`Sign::Negative`].
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use implicit_predicates_core::Sign;
96    ///
97    /// assert!(Sign::Negative.is_negative());
98    /// assert!(!Sign::Zero.is_negative());
99    /// ```
100    #[must_use]
101    pub fn is_negative(self) -> bool {
102        self == Sign::Negative
103    }
104}
105
106impl Neg for Sign {
107    type Output = Sign;
108
109    /// Flips the sign; [`Sign::Zero`] stays zero.
110    ///
111    /// # Examples
112    ///
113    /// ```
114    /// use implicit_predicates_core::Sign;
115    ///
116    /// assert_eq!(-Sign::Positive, Sign::Negative);
117    /// assert_eq!(-Sign::Zero, Sign::Zero);
118    /// ```
119    fn neg(self) -> Sign {
120        match self {
121            Sign::Negative => Sign::Positive,
122            Sign::Zero => Sign::Zero,
123            Sign::Positive => Sign::Negative,
124        }
125    }
126}
127
128impl Mul for Sign {
129    type Output = Sign;
130
131    /// The sign of a product: zero if either factor is zero, positive if the
132    /// signs agree, negative otherwise.
133    ///
134    /// # Examples
135    ///
136    /// ```
137    /// use implicit_predicates_core::Sign;
138    ///
139    /// assert_eq!(Sign::Negative * Sign::Negative, Sign::Positive);
140    /// assert_eq!(Sign::Negative * Sign::Positive, Sign::Negative);
141    /// assert_eq!(Sign::Zero * Sign::Positive, Sign::Zero);
142    /// ```
143    fn mul(self, rhs: Sign) -> Sign {
144        match (self, rhs) {
145            (Sign::Zero, _) | (_, Sign::Zero) => Sign::Zero,
146            (a, b) if a == b => Sign::Positive,
147            _ => Sign::Negative,
148        }
149    }
150}
151
152impl From<Sign> for i8 {
153    /// The integer sign: `-1`, `0` or `1`.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use implicit_predicates_core::Sign;
159    ///
160    /// assert_eq!(i8::from(Sign::Negative), -1);
161    /// assert_eq!(i8::from(Sign::Positive), 1);
162    /// ```
163    fn from(sign: Sign) -> i8 {
164        sign as i8
165    }
166}
167
168impl From<Ordering> for Sign {
169    /// The sign of `a - b` when `a.cmp(&b)` is `ordering`: `Less` is
170    /// negative, `Equal` zero and `Greater` positive.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use implicit_predicates_core::Sign;
176    ///
177    /// assert_eq!(Sign::from(3.cmp(&5)), Sign::Negative);
178    /// assert_eq!(Sign::from(5.cmp(&5)), Sign::Zero);
179    /// ```
180    fn from(ordering: Ordering) -> Sign {
181        match ordering {
182            Ordering::Less => Sign::Negative,
183            Ordering::Equal => Sign::Zero,
184            Ordering::Greater => Sign::Positive,
185        }
186    }
187}