use std::cmp::Ordering;
use std::num::NonZero;
use crate::Negateable;
use crate::{NonZeroSign, NonZeroSigned};
use crate::Sign;
use crate::Signed;
macro_rules! unsigned {
($($ty:ty),+ $(,)?) => {$(
impl Signed for $ty {
#[inline]
fn signum(&self) -> Sign {
if *self == 0 {
Sign::Zero
} else {
Sign::Positive
}
}
}
)+}
}
unsigned!(u8, u16, u32, u64, u128, usize);
macro_rules! signed {
($($ty:ty),+ $(,)?) => {$(
impl Signed for $ty {
#[inline]
fn signum(&self) -> Sign {
match self.cmp(&0) {
Ordering::Less => Sign::Negative,
Ordering::Equal => Sign::Zero,
Ordering::Greater => Sign::Positive,
}
}
}
impl Negateable for $ty {
#[inline]
#[track_caller]
fn negate(&mut self) {
match self.checked_neg() {
Some(negated) => *self = negated,
None => panic!(concat!("cannot negate ", stringify!($ty), "::MIN")),
}
}
}
)+}
}
signed!(i8, i16, i32, i64, i128, isize);
macro_rules! non_zero_unsigned {
($($ty:ty),+ $(,)?) => {$(
impl Signed for NonZero<$ty> {
#[inline]
fn signum(&self) -> Sign {
Sign::Positive
}
}
impl NonZeroSigned for NonZero<$ty> {
#[inline]
fn non_zero_signum(&self) -> NonZeroSign {
NonZeroSign::Positive
}
}
)+}
}
non_zero_unsigned!(u8, u16, u32, u64, u128, usize);
macro_rules! non_zero_signed {
($($ty:ty),+ $(,)?) => {$(
impl Signed for NonZero<$ty> {
#[inline]
fn signum(&self) -> Sign {
if self.get() > 0 {
Sign::Positive
} else {
Sign::Negative
}
}
}
impl Negateable for NonZero<$ty> {
#[inline]
#[track_caller]
fn negate(&mut self) {
match self.get().checked_neg() {
Some(negated) => *self = unsafe {
NonZero::new_unchecked(negated)
},
None => panic!(concat!("cannot negate NonZero<", stringify!($ty), ">::MIN")),
}
}
}
impl NonZeroSigned for NonZero<$ty> {
#[inline]
fn non_zero_signum(&self) -> NonZeroSign {
if self.get() > 0 {
NonZeroSign::Positive
} else {
NonZeroSign::Negative
}
}
}
)+}
}
non_zero_signed!(i8, i16, i32, i64, i128, isize);
#[cfg(test)]
mod test {
use std::num::{NonZeroI8, NonZeroU8};
use crate::{Negateable, NonZeroSign, NonZeroSigned};
#[test]
fn test_zero_sign() {
assert_eq!(1_u32.non_zero_signum(), NonZeroSign::Positive);
assert_eq!(-1_u32.non_zero_signum(), NonZeroSign::Negative);
assert_eq!(NonZeroU8::new(1).unwrap().non_zero_signum(), NonZeroSign::Positive);
assert_eq!(NonZeroI8::new(1).unwrap().non_zero_signum(), NonZeroSign::Positive);
assert_eq!(NonZeroI8::new(-1).unwrap().non_zero_signum(), NonZeroSign::Negative);
}
#[test]
#[should_panic]
fn test_non_zero_on_zero() {
0_u32.non_zero_signum();
}
#[test]
#[should_panic]
fn test_non_zero_on_zero_signed() {
0_i8.non_zero_signum();
}
#[test]
fn test_negate_minimum_panics() {
macro_rules! check {
($ty:ty, $nz:ty) => {
let mut value = <$ty>::MIN;
assert!(std::panic::catch_unwind(move || value.negate()).is_err());
let mut value = <$nz>::new(<$ty>::MIN).unwrap();
assert!(std::panic::catch_unwind(move || value.negate()).is_err());
for start in [<$ty>::MIN + 1, -1, 1, <$ty>::MAX] {
let mut value = start;
value.negate();
assert_eq!(value, -start, "{} {start}", stringify!($ty));
value.negate();
assert_eq!(value, start, "{} {start}", stringify!($ty));
}
}
}
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
check!(i8, std::num::NonZeroI8);
check!(i16, std::num::NonZeroI16);
check!(i32, std::num::NonZeroI32);
check!(i64, std::num::NonZeroI64);
check!(i128, std::num::NonZeroI128);
check!(isize, std::num::NonZeroIsize);
std::panic::set_hook(hook);
}
}