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}