1use core::{
4 cmp::Ordering,
5 ops::{Mul, MulAssign, Neg},
6};
7
8pub trait Abs {
16 type Output;
18
19 fn abs(self) -> Self::Output;
21}
22
23pub trait UnsignedAbs {
31 type Output;
33
34 fn unsigned_abs(self) -> Self::Output;
36}
37
38pub trait AbsOrd<Rhs = Self> {
50 fn abs_cmp(&self, rhs: &Rhs) -> Ordering;
52}
53
54pub trait Signed {
72 fn sign(&self) -> Sign;
74
75 #[inline]
77 fn is_positive(&self) -> bool {
78 self.sign() == Sign::Positive
79 }
80 #[inline]
82 fn is_negative(&self) -> bool {
83 self.sign() == Sign::Negative
84 }
85}
86
87macro_rules! impl_abs_ops_prim {
88 ($($signed:ty;)*) => {$( impl Abs for $signed {
90 type Output = $signed;
91 #[inline]
92 fn abs(self) -> Self::Output {
93 if self.is_nan() || self >= 0. {
94 self
95 } else {
96 -self
97 }
98 }
99 }
100 )*};
101 ($($signed:ty => $unsigned:ty;)*) => {$(
102 impl Abs for $signed {
103 type Output = $signed;
104 #[inline]
105 fn abs(self) -> Self::Output {
106 <$signed>::abs(self)
107 }
108 }
109
110 impl UnsignedAbs for $signed {
111 type Output = $unsigned;
112 #[inline]
113 fn unsigned_abs(self) -> Self::Output {
114 <$signed>::unsigned_abs(self)
115 }
116 }
117 )*}
118}
119impl_abs_ops_prim!(i8 => u8; i16 => u16; i32 => u32; i64 => u64; i128 => u128; isize => usize;);
120impl_abs_ops_prim!(f32; f64;);
121
122#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
126pub enum Sign {
127 Positive,
129 Negative,
131}
132
133use Sign::*;
134
135impl Sign {
136 #[inline]
150 pub const fn as_sign_str(self, sign_plus: bool) -> &'static str {
151 match self {
152 Sign::Negative => "-",
153 Sign::Positive if sign_plus => "+",
154 _ => "",
155 }
156 }
157}
158
159impl From<bool> for Sign {
160 #[inline]
162 fn from(v: bool) -> Self {
163 match v {
164 true => Self::Negative,
165 false => Self::Positive,
166 }
167 }
168}
169
170impl From<Sign> for bool {
171 #[inline]
173 fn from(v: Sign) -> Self {
174 match v {
175 Sign::Negative => true,
176 Sign::Positive => false,
177 }
178 }
179}
180
181impl Neg for Sign {
182 type Output = Sign;
183
184 #[inline]
185 fn neg(self) -> Sign {
186 match self {
187 Positive => Negative,
188 Negative => Positive,
189 }
190 }
191}
192
193impl Mul<Sign> for Sign {
194 type Output = Sign;
195
196 #[inline]
197 fn mul(self, rhs: Sign) -> Sign {
198 match (self, rhs) {
199 (Positive, Positive) => Positive,
200 (Positive, Negative) => Negative,
201 (Negative, Positive) => Negative,
202 (Negative, Negative) => Positive,
203 }
204 }
205}
206
207impl Mul<Ordering> for Sign {
208 type Output = Ordering;
209 #[inline]
210 fn mul(self, rhs: Ordering) -> Self::Output {
211 match self {
212 Positive => rhs,
213 Negative => rhs.reverse(),
214 }
215 }
216}
217
218impl Mul<Sign> for Ordering {
219 type Output = Ordering;
220 #[inline]
221 fn mul(self, rhs: Sign) -> Self::Output {
222 match rhs {
223 Positive => self,
224 Negative => self.reverse(),
225 }
226 }
227}
228
229impl MulAssign<Sign> for Sign {
230 #[inline]
231 fn mul_assign(&mut self, rhs: Sign) {
232 *self = *self * rhs;
233 }
234}
235
236impl PartialOrd for Sign {
237 #[inline]
238 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
239 Some(self.cmp(other))
240 }
241}
242
243impl Ord for Sign {
244 #[inline]
245 fn cmp(&self, other: &Self) -> Ordering {
246 match (self, other) {
247 (Positive, Negative) => Ordering::Greater,
248 (Negative, Positive) => Ordering::Less,
249 _ => Ordering::Equal,
250 }
251 }
252}
253
254macro_rules! impl_sign_ops_for_primitives {
255 ($($t:ty)*) => {$(
256 impl Mul<$t> for Sign {
257 type Output = $t;
258
259 #[inline]
260 fn mul(self, rhs: $t) -> Self::Output {
261 match self {
262 Positive => rhs,
263 Negative => -rhs
264 }
265 }
266 }
267
268 impl Mul<Sign> for $t {
269 type Output = $t;
270
271 #[inline]
272 fn mul(self, rhs: Sign) -> Self::Output {
273 match rhs {
274 Positive => self,
275 Negative => -self
276 }
277 }
278 }
279 )*};
280}
281impl_sign_ops_for_primitives!(i8 i16 i32 i64 i128 isize f32 f64);
282
283macro_rules! impl_signed_for_int {
284 ($($t:ty)*) => {$(
285 impl Signed for $t {
286 #[inline]
287 fn sign(&self) -> Sign {
288 Sign::from(*self < 0)
289 }
290 }
291
292 impl AbsOrd for $t {
293 #[inline]
294 fn abs_cmp(&self, rhs: &Self) -> Ordering {
295 self.abs().cmp(&rhs.abs())
296 }
297 }
298 )*};
299}
300impl_signed_for_int!(i8 i16 i32 i64 i128 isize);
301
302macro_rules! impl_signed_for_float {
303 ($t:ty, $shift:literal) => {
304 impl Signed for $t {
305 #[inline]
306 fn sign(&self) -> Sign {
307 if self.is_nan() {
308 panic!("nan doesn't have a sign")
309 } else if *self == -0. {
310 return Sign::Positive;
311 }
312 Sign::from(self.to_bits() >> $shift > 0)
313 }
314 }
315
316 impl AbsOrd for $t {
317 #[inline]
318 fn abs_cmp(&self, rhs: &Self) -> Ordering {
319 self.abs()
320 .partial_cmp(&rhs.abs())
321 .expect("abs_cmp is not allowed on NaNs!")
322 }
323 }
324 };
325}
326impl_signed_for_float!(f32, 31);
327impl_signed_for_float!(f64, 63);
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332
333 #[test]
334 fn test_signed() {
335 assert_eq!(0i32.sign(), Sign::Positive);
336 assert_eq!(1i32.sign(), Sign::Positive);
337 assert_eq!((-1i32).sign(), Sign::Negative);
338
339 assert_eq!(0f32.sign(), Sign::Positive);
340 assert_eq!((-0f32).sign(), Sign::Positive);
341 assert_eq!(1f32.sign(), Sign::Positive);
342 assert_eq!((-1f32).sign(), Sign::Negative);
343 }
344
345 #[test]
346 #[should_panic]
347 fn test_signed_nan() {
348 let _ = f32::NAN.sign();
349 }
350
351 #[test]
352 #[should_panic]
353 fn test_abs_cmp_nan() {
354 let _ = f32::NAN.abs_cmp(&f32::NAN);
355 }
356}