use std::cmp::Ordering;
use std::ops::{Mul, MulAssign, Neg, Not};
use crate::{Negateable, Signed};
use crate::non_zero::NonZero;
use crate::sign::Sign;
pub trait NonZeroSigned: NonZero + Signed {
fn non_zero_signum(&self) -> NonZeroSign;
#[inline]
fn non_zero_is_positive(&self) -> bool {
self.non_zero_signum() == NonZeroSign::Positive
}
#[inline]
fn non_zero_is_negative(&self) -> bool {
self.non_zero_signum() == NonZeroSign::Negative
}
}
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum NonZeroSign {
Positive = 1,
Negative = -1,
}
macro_rules! non_zero_signed_by_panic {
($($t:ty),+ $(,)?) => {$(
impl NonZeroSigned for $t {
#[inline]
#[track_caller]
fn non_zero_signum(&self) -> NonZeroSign {
match Signed::signum(self) {
Sign::Positive => NonZeroSign::Positive,
Sign::Negative => NonZeroSign::Negative,
Sign::Zero => panic!(
"attempt to take the non zero sign of a zero value of type {}",
std::any::type_name::<$t>(),
),
}
}
}
)+}
}
non_zero_signed_by_panic!(i8, i16, i32, i64, i128, isize);
non_zero_signed_by_panic!(u8, u16, u32, u64, u128, usize);
non_zero_signed_by_panic!(Sign);
macro_rules! non_zero_signed_total {
($t:ty, $sign:expr) => {
impl NonZeroSigned for $t {
#[inline]
fn non_zero_signum(&self) -> NonZeroSign {
$sign(self)
}
}
}
}
non_zero_signed_total!(crate::fixed::One, |_: &crate::fixed::One| NonZeroSign::Positive);
non_zero_signed_total!(crate::fixed::SignedOne, |value: &crate::fixed::SignedOne| match value {
crate::fixed::SignedOne::PlusOne => NonZeroSign::Positive,
crate::fixed::SignedOne::MinusOne => NonZeroSign::Negative,
});
impl Signed for NonZeroSign {
fn signum(&self) -> Sign {
match self {
NonZeroSign::Positive => Sign::Positive,
NonZeroSign::Negative => Sign::Negative,
}
}
}
impl NonZero for NonZeroSign {
fn is_not_zero(&self) -> bool {
true
}
}
impl Negateable for NonZeroSign {
fn negate(&mut self) {
*self = match self {
NonZeroSign::Positive => NonZeroSign::Negative,
NonZeroSign::Negative => NonZeroSign::Positive,
}
}
}
impl Not for NonZeroSign {
type Output = Self;
fn not(mut self) -> Self::Output {
Negateable::negate(&mut self);
self
}
}
impl Neg for NonZeroSign {
type Output = Self;
fn neg(mut self) -> Self::Output {
Negateable::negate(&mut self);
self
}
}
impl From<Sign> for NonZeroSign {
fn from(other: Sign) -> Self {
debug_assert!(other.is_not_zero());
match other {
Sign::Positive => NonZeroSign::Positive,
Sign::Zero => panic!("attempt to convert a zero sign into a non zero sign"),
Sign::Negative => NonZeroSign::Negative,
}
}
}
impl Ord for NonZeroSign {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(*self as i8).cmp(&(*other as i8))
}
}
impl PartialOrd for NonZeroSign {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Mul for NonZeroSign {
type Output = Self;
fn mul(mut self, rhs: Self) -> Self::Output {
MulAssign::mul_assign(&mut self, rhs);
self
}
}
impl MulAssign for NonZeroSign {
fn mul_assign(&mut self, rhs: Self) {
*self = match (*self, rhs) {
(NonZeroSign::Positive, NonZeroSign::Positive) => NonZeroSign::Positive,
(NonZeroSign::Positive, NonZeroSign::Negative) => NonZeroSign::Negative,
(NonZeroSign::Negative, NonZeroSign::Positive) => NonZeroSign::Negative,
(NonZeroSign::Negative, NonZeroSign::Negative) => NonZeroSign::Positive,
}
}
}
#[cfg(test)]
mod test {
use std::cmp::Ordering;
use crate::{NonZeroSign, NonZeroSigned, Sign};
use crate::{R64, RB};
#[test]
fn test_cmp() {
assert!(NonZeroSign::Positive > NonZeroSign::Negative);
assert_eq!(NonZeroSign::Positive.partial_cmp(&NonZeroSign::Positive), Some(Ordering::Equal));
assert_eq!(NonZeroSign::Negative.partial_cmp(&NonZeroSign::Negative), Some(Ordering::Equal));
assert_eq!(NonZeroSign::Negative.partial_cmp(&NonZeroSign::Positive), Some(Ordering::Less));
}
#[test]
fn test_cmp_contract() {
for a in [NonZeroSign::Negative, NonZeroSign::Positive] {
assert_eq!(a.partial_cmp(&a), Some(Ordering::Equal));
assert!(a <= a);
assert!(a >= a);
for b in [NonZeroSign::Negative, NonZeroSign::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!(NonZeroSign::Negative < NonZeroSign::Positive);
}
#[test]
fn test_numbers() {
use crate::Signed;
assert_eq!(Signed::signum(&RB!(1)), Sign::Positive);
assert_eq!(Signed::signum(&RB!(-1)), Sign::Negative);
assert!(RB!(1).is_positive());
assert!(RB!(-1).is_negative());
}
#[test]
fn test_numbers_non_zero() {
assert_eq!(1.non_zero_signum(), NonZeroSign::Positive);
assert_eq!((-1).non_zero_signum(), NonZeroSign::Negative);
assert_eq!(RB!(-1).non_zero_signum() * RB!(-1).non_zero_signum(), NonZeroSign::Positive);
assert_eq!(RB!(1).non_zero_signum() * RB!(1).non_zero_signum(), NonZeroSign::Positive);
assert_eq!(RB!(-1).non_zero_signum() * RB!(1).non_zero_signum(), NonZeroSign::Negative);
assert_eq!(R64!(1).non_zero_signum(), NonZeroSign::Positive);
assert_eq!(R64!(-1).non_zero_signum(), NonZeroSign::Negative);
assert_eq!(R64!(-1).non_zero_signum() * R64!(-1).non_zero_signum(), NonZeroSign::Positive);
assert_eq!(R64!(1).non_zero_signum() * R64!(1).non_zero_signum(), NonZeroSign::Positive);
assert_eq!(R64!(-1).non_zero_signum() * R64!(1).non_zero_signum(), NonZeroSign::Negative);
}
#[test]
#[should_panic]
fn test_zero() {
RB!(0).non_zero_signum();
}
}