num_sign/
lib.rs

1//! ```
2//! use num_sign::*;
3//! assert_eq!(Positive as i32, 1);
4//! assert_eq!(Negative as i32, -1);
5//! assert!(Negative < Positive);
6//! assert_eq!(Positive.cmp(&Negative), std::cmp::Ordering::Greater);
7//! assert_eq!(1_i32, Positive.into());
8//! assert_eq!(-1_i32, Negative.into());
9//! assert_eq!(1.0, Positive.into());
10//! assert_eq!(-1.0, Negative.into());
11//! assert_eq!(-Positive, Negative);
12//! assert_eq!(Positive, -Negative);
13//! assert_eq!(Positive * Positive, Positive);
14//! assert_eq!(Positive * Negative, Negative);
15//! assert_eq!(Negative * Positive, Negative);
16//! assert_eq!(Negative * Negative, Positive);
17//! assert_eq!(Positive * 123, 123);
18//! assert_eq!(Negative * 123, -123);
19//! assert_eq!(Positive * 3.14, 3.14);
20//! assert_eq!(Negative * 3.14, -3.14);
21//! assert_eq!((123).sign(), Some(Positive));
22//! assert_eq!((-123).sign(), Some(Negative));
23//! assert_eq!((0).sign(), None);
24//! assert_eq!((3.14).sign(), Some(Positive));
25//! assert_eq!((-3.14).sign(), Some(Negative));
26//! assert_eq!((0.0).sign(), Some(Positive));
27//! assert_eq!((-0.0).sign(), Some(Negative));
28//! assert_eq!(std::f64::NAN.sign(), None);
29//! assert_eq!(std::f64::INFINITY.sign(), Some(Positive));
30//! assert_eq!(std::f64::NEG_INFINITY.sign(), Some(Negative));
31//! assert_eq!("+".parse::<Sign>().unwrap(), Positive);
32//! assert_eq!("-".parse::<Sign>().unwrap(), Negative);
33//! assert_eq!(Positive.to_string(), "+");
34//! assert_eq!(Negative.to_string(), "-");
35//! ```
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
38pub enum Sign {
39    Positive = 1,
40    Negative = -1,
41}
42
43pub use Sign::*;
44
45impl Sign {
46    pub fn to_isize(self) -> isize {
47        self.into()
48    }
49    pub fn to_i64(self) -> i64 {
50        self.into()
51    }
52    pub fn to_i32(self) -> i32 {
53        self.into()
54    }
55    pub fn to_i16(self) -> i16 {
56        self.into()
57    }
58    pub fn to_i8(self) -> i16 {
59        self.into()
60    }
61
62    /// (-1)^n (i.e. `if n % 2 == 0 { Positive } else { Negative }`)
63    /// ```
64    /// use num_sign::*;
65    /// assert_eq!(Sign::parity(0), Positive);
66    /// assert_eq!(Sign::parity(7), Negative);
67    /// assert_eq!(Sign::parity(-2), Positive);
68    /// assert_eq!(Sign::parity(false), Positive);
69    /// assert_eq!(Sign::parity(true), Negative);
70    /// ```
71    pub fn parity(n: impl Into<i32>) -> Self {
72        if n.into() % 2 == 0 {
73            Positive
74        } else {
75            Negative
76        }
77    }
78}
79
80impl std::ops::Neg for Sign {
81    type Output = Self;
82    fn neg(self) -> Self {
83        match self {
84            Positive => Negative,
85            Negative => Positive,
86        }
87    }
88}
89
90impl<T: std::ops::Neg<Output = T>> std::ops::Mul<T> for Sign {
91    type Output = T;
92    fn mul(self, rhs: T) -> T {
93        match self {
94            Positive => rhs,
95            Negative => -rhs,
96        }
97    }
98}
99
100impl std::str::FromStr for Sign {
101    type Err = ();
102    fn from_str(s: &str) -> Result<Self, Self::Err> {
103        match s {
104            "+" => Ok(Positive),
105            "-" => Ok(Negative),
106            _ => Err(()),
107        }
108    }
109}
110
111impl std::fmt::Display for Sign {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        f.write_str(match self {
114            Positive => "+",
115            Negative => "-",
116        })
117    }
118}
119
120pub trait Signed {
121    fn sign(&self) -> Option<Sign>;
122}
123
124macro_rules! impl_traits_for_int {
125    ($($int_type: ty),*) => {
126        $(
127            impl From<Sign> for $int_type {
128                fn from(sign: Sign) -> $int_type {
129                    match sign {
130                        Positive => 1,
131                        Negative => -1,
132                    }
133                }
134            }
135            impl Signed for $int_type {
136                fn sign(&self) -> Option<Sign> {
137                    match self.cmp(&0) {
138                        std::cmp::Ordering::Equal => None,
139                        std::cmp::Ordering::Greater => Some(Positive),
140                        std::cmp::Ordering::Less => Some(Negative),
141                    }
142                }
143            }
144        )*
145    };
146}
147
148impl_traits_for_int!(isize, i64, i32, i16, i8);
149
150impl From<Sign> for f64 {
151    fn from(sign: Sign) -> f64 {
152        match sign {
153            Positive => 1.0,
154            Negative => -1.0,
155        }
156    }
157}
158impl From<Sign> for f32 {
159    fn from(sign: Sign) -> f32 {
160        match sign {
161            Positive => 1.0,
162            Negative => -1.0,
163        }
164    }
165}
166
167impl Signed for f64 {
168    fn sign(&self) -> Option<Sign> {
169        match self.signum().partial_cmp(&0.0) {
170            Some(std::cmp::Ordering::Greater) => Some(Positive),
171            Some(std::cmp::Ordering::Less) => Some(Negative),
172            _ => None,
173        }
174    }
175}
176impl Signed for f32 {
177    fn sign(&self) -> Option<Sign> {
178        match self.signum().partial_cmp(&0.0) {
179            Some(std::cmp::Ordering::Greater) => Some(Positive),
180            Some(std::cmp::Ordering::Less) => Some(Negative),
181            _ => None,
182        }
183    }
184}
185
186#[cfg(feature = "serde")]
187mod impl_serde {
188    use super::*;
189    impl serde::Serialize for Sign {
190        fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
191            serializer.serialize_str(match self {
192                Positive => "+",
193                Negative => "-",
194            })
195        }
196    }
197    struct SignVisitor;
198    impl<'de> serde::de::Visitor<'de> for SignVisitor {
199        type Value = Sign;
200        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
201            f.write_str("\"+\" or \"-\"")
202        }
203        fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
204            match value {
205                "+" => Ok(Positive),
206                "-" => Ok(Negative),
207                _ => Err(E::custom("Sign must be + or -")),
208            }
209        }
210    }
211    impl<'de> serde::Deserialize<'de> for Sign {
212        fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
213            deserializer.deserialize_str(SignVisitor)
214        }
215    }
216    #[test]
217    fn test_serde() {
218        assert_eq!(serde_json::to_string(&Positive).unwrap(), "\"+\"");
219        assert_eq!(serde_json::to_string(&Negative).unwrap(), "\"-\"");
220        assert_eq!(Positive, serde_json::from_str("\"+\"").unwrap());
221        assert_eq!(Negative, serde_json::from_str("\"-\"").unwrap());
222    }
223}