use std::cmp::Ordering;
use std::fmt;
use std::ops::{Mul, MulAssign, Neg, Not};
use crate::non_zero::NonZeroSign;
use crate::NonZero;
pub trait Signed {
fn signum(&self) -> Sign;
#[inline]
fn is_positive(&self) -> bool {
self.signum() == Sign::Positive
}
#[inline]
fn is_negative(&self) -> bool {
self.signum() == Sign::Negative
}
}
pub trait Negateable: Signed {
fn negate(&mut self);
}
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum Sign {
Positive = 1,
Zero = 0,
Negative = -1,
}
impl Signed for Sign {
#[inline]
fn signum(&self) -> Sign {
*self
}
#[inline]
fn is_positive(&self) -> bool {
*self == Sign::Positive
}
#[inline]
fn is_negative(&self) -> bool {
*self == Sign::Negative
}
}
impl Negateable for Sign {
#[inline]
fn negate(&mut self) {
match self {
Sign::Positive => *self = Sign::Negative,
Sign::Zero => {}
Sign::Negative => *self = Sign::Positive,
}
}
}
impl Neg for Sign {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
match self {
Sign::Positive => Sign::Negative,
Sign::Zero => Sign::Zero,
Sign::Negative => Sign::Positive,
}
}
}
impl Not for Sign {
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
match self {
Sign::Positive => Sign::Negative,
Sign::Zero => Sign::Zero,
Sign::Negative => Sign::Positive,
}
}
}
impl NonZero for Sign {
#[inline]
fn is_not_zero(&self) -> bool {
*self != Sign::Zero
}
}
impl MulAssign for Sign {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = match (&self, rhs) {
(Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => Sign::Positive,
(Sign::Zero, _) | (_, Sign::Zero) => Sign::Zero,
(Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => Sign::Negative,
};
}
}
impl Mul for Sign {
type Output = Self;
#[inline]
fn mul(mut self, rhs: Self) -> Self::Output {
self *= rhs;
self
}
}
impl Ord for Sign {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(*self as i8).cmp(&(*other as i8))
}
}
impl PartialOrd for Sign {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl From<NonZeroSign> for Sign {
#[inline]
fn from(sign: NonZeroSign) -> Self {
match sign {
NonZeroSign::Positive => Sign::Positive,
NonZeroSign::Negative => Sign::Negative,
}
}
}
impl fmt::Display for Sign {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Sign::Negative => "-",
Sign::Zero => "0",
Sign::Positive => "+",
})
}
}
#[cfg(test)]
mod test {
use std::cmp::Ordering;
use crate::{Sign, Signed};
use crate::RB;
#[test]
fn test_integer() {
assert_eq!(Signed::signum(&0_i32), Sign::Zero);
assert_eq!(Signed::signum(&-1), Sign::Negative);
assert_eq!(Signed::signum(&1_i128), Sign::Positive);
assert_eq!(Signed::signum(&0_u32), Sign::Zero);
assert_eq!(Signed::signum(&1_u64), Sign::Positive);
}
#[test]
fn test_sign_boolean() {
assert!(6.is_positive());
assert!((-5).is_negative());
assert!(!0.is_positive());
assert!(!0.is_negative());
assert!(!6.is_negative());
assert!(!(-5).is_positive());
}
#[test]
fn test_sign() {
assert_eq!(!Sign::Zero, Sign::Zero);
assert_eq!(!Sign::Positive, Sign::Negative);
assert_eq!(!Sign::Negative, Sign::Positive);
assert_eq!(Sign::Positive, -Sign::Negative);
assert_eq!(Sign::Positive * Sign::Positive, Sign::Positive);
assert_eq!(Sign::Negative * Sign::Negative, Sign::Positive);
assert_eq!(Sign::Negative * Sign::Zero, -Sign::Zero);
}
#[test]
fn test_sign_ord_contract() {
for a in [Sign::Negative, Sign::Zero, Sign::Positive] {
assert_eq!(a.partial_cmp(&a), Some(Ordering::Equal));
assert!(a <= a);
assert!(a >= a);
for b in [Sign::Negative, Sign::Zero, Sign::Positive] {
assert_eq!(a == b, a.partial_cmp(&b) == Some(Ordering::Equal));
assert_eq!(a.partial_cmp(&b), Some(a.cmp(&b)));
assert_eq!(a.cmp(&b), b.cmp(&a).reverse());
}
}
assert!(Sign::Negative < Sign::Zero && Sign::Zero < Sign::Positive);
}
#[test]
fn test_sign_ord() {
assert_eq!(Sign::Zero < Sign::Positive, true);
assert_eq!(Sign::Positive < Sign::Positive, false);
assert_eq!(Sign::Positive == Sign::Positive, true);
assert_eq!(Sign::Zero == Sign::Zero, true);
assert_eq!(Sign::Negative < Sign::Positive, true);
assert_eq!(Sign::Negative < Sign::Zero, true);
assert_eq!(Sign::Negative < Sign::Negative, false);
}
#[test]
fn test_sign_conversion() {
assert_eq!(Sign::Positive, crate::NonZeroSign::Positive.into());
}
#[test]
fn test_numbers() {
assert_eq!(Signed::signum(&1), Sign::Positive);
assert_eq!(Signed::signum(&0), Sign::Zero);
assert_eq!(Signed::signum(&(-1)), Sign::Negative);
assert_eq!(RB!(0).signum(), Sign::Zero);
assert_eq!(RB!(1).signum(), Sign::Positive);
assert_eq!(RB!(-1).signum(), Sign::Negative);
assert_eq!(RB!(-1).signum() * RB!(-1).signum(), Sign::Positive);
assert_eq!(RB!(1).signum() * RB!(1).signum(), Sign::Positive);
assert_eq!(RB!(-1).signum() * RB!(1).signum(), Sign::Negative);
}
}