use super::sign::{NonNegative, NonPositive};
use core::num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
};
pub trait Ident {
fn can_zero(&self) -> bool;
fn can_one(&self) -> bool;
fn can_neg_one(&self) -> bool;
fn is_zero(&self) -> bool;
fn is_one(&self) -> bool;
fn is_neg_one(&self) -> bool;
}
pub trait Zero: Ident {
fn new_zero() -> Self;
#[rustfmt::skip]
#[inline]
fn set_zero(&mut self) where Self: Sized { *self = Self::new_zero(); }
}
pub trait NonZero: Ident {}
pub trait ConstZero: Ident {
const ZERO: Self;
}
pub trait One: Ident {
fn new_one() -> Self;
#[rustfmt::skip]
#[inline]
fn set_one(&mut self) where Self: Sized { *self = Self::new_one(); }
}
pub trait NonOne: Ident {}
pub trait ConstOne: Ident {
const ONE: Self;
}
pub trait NegOne: Ident {
fn new_neg_one() -> Self;
#[rustfmt::skip]
fn set_neg_one(&mut self) where Self: Sized { *self = Self::new_neg_one(); }
}
pub trait NonNegOne: Ident {}
pub trait ConstNegOne: Ident {
const NEG_ONE: Self;
}
macro_rules! impl_ident {
(many_float: $($t:ty, $zero:expr, $one:expr, $neg_one:expr),+) => {
$( impl_ident![float: $t, $zero, $one, $neg_one]; )+
};
(float: $t:ty, $zero:expr, $one:expr, $neg_one:expr) => {
impl Ident for $t {
#[inline]
fn can_zero(&self) -> bool { true }
#[inline]
fn can_one(&self) -> bool { true }
#[inline]
fn can_neg_one(&self) -> bool { true }
#[inline]
fn is_zero(&self) -> bool {
#[cfg(feature = "std")]
return (*self).abs() <= <$t>::EPSILON;
#[cfg(not(feature = "std"))]
if self.is_sign_positive() {
*self <= <$t>::EPSILON
} else {
*self >= <$t>::EPSILON
}
}
#[inline]
fn is_one(&self) -> bool {
#[cfg(feature = "std")]
return (*self - 1.0).abs() <= <$t>::EPSILON;
#[cfg(not(feature = "std"))]
if self.is_sign_positive() {
*self -1. <= <$t>::EPSILON
} else {
*self -1. >= <$t>::EPSILON
}
}
#[inline]
fn is_neg_one(&self) -> bool {
#[cfg(feature = "std")]
return (*self + 1.0).abs() <= <$t>::EPSILON;
#[cfg(not(feature = "std"))]
if self.is_sign_positive() {
*self +1. <= <$t>::EPSILON
} else {
*self +1. >= <$t>::EPSILON
}
}
}
impl ConstZero for $t { const ZERO: Self = $zero; }
impl Zero for $t { fn new_zero() -> Self { $zero } }
impl ConstOne for $t { const ONE: Self = $one; }
impl One for $t { fn new_one() -> Self { $one } }
impl ConstNegOne for $t { const NEG_ONE: Self = $neg_one; }
impl NegOne for $t { fn new_neg_one() -> Self { $neg_one } }
};
(many_signed_int: $($t:ty, $zero:expr, $one:expr, $neg_one:expr),+) => {
$( impl_ident![signed_int: $t, $zero, $one, $neg_one]; )+
};
(signed_int: $t:ty, $zero:expr, $one:expr, $neg_one:expr) => {
impl Ident for $t {
#[inline]
fn can_zero(&self) -> bool { true }
#[inline]
fn can_one(&self) -> bool { true }
#[inline]
fn can_neg_one(&self) -> bool { true }
#[inline]
fn is_zero(&self) -> bool { *self == $zero }
#[inline]
fn is_one(&self) -> bool { *self == $one }
#[inline]
fn is_neg_one(&self) -> bool { *self == $neg_one }
}
impl ConstZero for $t { const ZERO: Self = $zero; }
impl ConstOne for $t { const ONE: Self = $one; }
impl ConstNegOne for $t { const NEG_ONE: Self = $neg_one; }
impl Zero for $t {
#[inline]
fn new_zero() -> Self { $zero }
}
impl One for $t {
#[inline]
fn new_one() -> Self { $one }
}
impl NegOne for $t {
#[inline]
fn new_neg_one() -> Self { $neg_one }
}
};
(many_unsigned_int: $($t:ty, $zero:expr, $one:expr),+) => {
$( impl_ident![unsigned_int: $t, $zero, $one]; )+
};
(unsigned_int: $t:ty, $zero:expr, $one:expr) => {
impl Ident for $t {
#[inline]
fn can_zero(&self) -> bool { true }
#[inline]
fn can_one(&self) -> bool { true }
#[inline]
fn can_neg_one(&self) -> bool { false }
#[inline]
fn is_zero(&self) -> bool { *self == $zero }
#[inline]
fn is_one(&self) -> bool { *self == $one }
#[inline]
fn is_neg_one(&self) -> bool { false }
}
impl ConstZero for $t { const ZERO: Self = $zero; }
impl Zero for $t {
#[inline]
fn new_zero() -> Self { $zero }
}
impl ConstOne for $t { const ONE: Self = $one; }
impl One for $t {
#[inline]
fn new_one() -> Self { $one }
}
};
(many_signed_nonzero: $($t:ty, $one:expr, $neg_one:expr),+) => {
$( impl_ident![signed_nonzero: $t, $one, $neg_one]; )+
};
(signed_nonzero: $t:ty, $one:expr, $neg_one:expr) => {
impl Ident for $t {
fn can_zero(&self) -> bool { false }
fn can_one(&self) -> bool { true }
fn can_neg_one(&self) -> bool { true }
fn is_zero(&self) -> bool { false }
fn is_one(&self) -> bool { self.get() == $one }
fn is_neg_one(&self) -> bool { self.get() == $neg_one }
}
impl NonZero for $t {}
impl ConstOne for $t {
#[cfg(feature = "safe")]
const ONE: Self = if let Some(n) = <$t>::new($one)
{ n } else { unreachable!() };
#[cfg(not(feature = "safe"))]
const ONE: Self = unsafe { <$t>::new_unchecked($one) };
}
impl One for $t {
#[cfg(feature = "safe")]
fn new_one() -> Self { <$t>::new($one).unwrap() }
#[cfg(not(feature = "safe"))]
fn new_one() -> Self { unsafe { <$t>::new_unchecked($one) } }
}
impl ConstNegOne for $t {
#[cfg(feature = "safe")]
const NEG_ONE: Self = if let Some(n) = <$t>::new($neg_one)
{ n } else { unreachable!() };
#[cfg(not(feature = "safe"))]
const NEG_ONE: Self = unsafe { <$t>::new_unchecked($neg_one) };
}
impl NegOne for $t {
#[inline]
#[cfg(feature = "safe")]
fn new_neg_one() -> Self { <$t>::new($neg_one).unwrap() }
#[inline]
#[cfg(not(feature = "safe"))]
fn new_neg_one() -> Self { unsafe { <$t>::new_unchecked($neg_one) } }
}
};
(many_unsigned_nonzero: $($t:ty, $one:expr),+) => {
$( impl_ident![unsigned_nonzero: $t, $one]; )+
};
(unsigned_nonzero: $t:ty, $one:expr) => {
impl Ident for $t {
#[inline]
fn can_zero(&self) -> bool { false }
#[inline]
fn can_one(&self) -> bool { true }
#[inline]
fn can_neg_one(&self) -> bool { false }
#[inline]
fn is_zero(&self) -> bool { false }
#[inline]
fn is_one(&self) -> bool { self.get() == $one }
#[inline]
fn is_neg_one(&self) -> bool { false }
}
impl NonZero for $t {}
impl ConstOne for $t {
#[cfg(feature = "safe")]
const ONE: Self = if let Some(n) = <$t>::new($one)
{ n } else { unreachable!() };
#[cfg(not(feature = "safe"))]
const ONE: Self = unsafe { <$t>::new_unchecked($one) };
}
impl One for $t {
#[inline]
#[cfg(feature = "safe")]
fn new_one() -> Self { <$t>::new($one).unwrap() }
#[inline]
#[cfg(not(feature = "safe"))]
fn new_one() -> Self { unsafe { <$t>::new_unchecked($one) } }
}
};
(many_signed_nonconst: $($t:ty, $zero:expr, $one:expr, $neg_one:expr),+) => {
$( impl_ident![signed_nonconst: $t, $zero, $one, $neg_one]; )+
};
(signed_nonconst: $t:ty, $zero:expr, $one:expr, $neg_one:expr) => {
impl Ident for $t {
#[inline]
fn can_zero(&self) -> bool { true }
#[inline]
fn can_one(&self) -> bool { true }
#[inline]
fn can_neg_one(&self) -> bool { true }
#[inline]
fn is_zero(&self) -> bool { *self == $zero }
#[inline]
fn is_one(&self) -> bool { *self == $one }
#[inline]
fn is_neg_one(&self) -> bool { *self == $neg_one }
}
impl Zero for $t {
#[inline]
fn new_zero() -> Self { $zero }
}
impl One for $t {
#[inline]
fn new_one() -> Self { $one }
}
impl NegOne for $t {
#[inline]
fn new_neg_one() -> Self { $neg_one }
}
};
(many_unsigned_nonconst: $($t:ty, $zero:expr, $one:expr),+) => {
$( impl_ident![unsigned_nonconst: $t, $zero, $one]; )+
};
(unsigned_nonconst: $t:ty, $zero:expr, $one:expr) => {
impl Ident for $t {
#[inline]
fn can_zero(&self) -> bool { true }
#[inline]
fn can_one(&self) -> bool { true }
#[inline]
fn can_neg_one(&self) -> bool { false }
#[inline]
fn is_zero(&self) -> bool { *self == $zero }
#[inline]
fn is_one(&self) -> bool { *self == $one }
#[inline]
fn is_neg_one(&self) -> bool { false }
}
impl Zero for $t { fn new_zero() -> Self { $zero } }
impl One for $t { fn new_one() -> Self { $one } }
};
}
impl<T: Ident + NonNegative> NonNegOne for T {}
impl<T: Ident + NonPositive> NonOne for T {}
#[rustfmt::skip]
impl_ident![many_signed_int:
i8, 0, 1, -1, i16, 0, 1, -1, i32, 0, 1, -1, i64, 0, 1, -1, i128, 0, 1, -1, isize, 0, 1, -1];
#[rustfmt::skip]
impl_ident![many_unsigned_int:
u8, 0, 1, u16, 0, 1, u32, 0, 1, u64, 0, 1, u128, 0, 1, usize, 0, 1];
#[rustfmt::skip]
impl_ident![many_float: f32, 0.0, 1.0, -1.0, f64, 0.0, 1.0, -1.0];
#[rustfmt::skip]
impl_ident![many_signed_nonzero:
NonZeroI8, 1, -1, NonZeroI16, 1, -1, NonZeroI32, 1, -1,
NonZeroI64, 1, -1, NonZeroI128, 1, -1, NonZeroIsize, 1, -1];
#[rustfmt::skip]
impl_ident![many_unsigned_nonzero:
NonZeroU8, 1, NonZeroU16, 1, NonZeroU32, 1, NonZeroU64, 1, NonZeroU128, 1, NonZeroUsize, 1];
#[rustfmt::skip]
#[cfg(feature = "dashu-int")]
mod impl_big {
use super::*;
use dashu_int::{IBig, UBig};
impl_ident![signed_nonconst: IBig, IBig::from(0_i8), IBig::from(1_i8), IBig::from(-1_i8)];
impl_ident![unsigned_nonconst: UBig, UBig::from(0_u8), UBig::from(1_u8)];
}
#[rustfmt::skip]
#[cfg(feature = "twofloat")]
mod impl_twofloat {
use super::*;
use twofloat::TwoFloat;
impl Ident for TwoFloat {
fn can_zero(&self) -> bool { true }
fn can_one(&self) -> bool { true }
fn can_neg_one(&self) -> bool { true }
fn is_zero(&self) -> bool { self == &Self::new_zero() }
fn is_one(&self) -> bool { self == &Self::new_one() }
fn is_neg_one(&self) -> bool { self == &Self::new_neg_one() }
}
impl ConstZero for TwoFloat { const ZERO: Self = Self::from_f64(0.0); }
impl ConstOne for TwoFloat { const ONE: Self = Self::from_f64(1.0); }
impl ConstNegOne for TwoFloat { const NEG_ONE: Self = Self::from_f64(-1.0); }
impl Zero for TwoFloat {
fn new_zero() -> Self { TwoFloat::from(0.0) }
}
impl One for TwoFloat {
fn new_one() -> Self { TwoFloat::from(1.0) }
}
impl NegOne for TwoFloat {
fn new_neg_one() -> Self { TwoFloat::from(-1.0) }
}
}
#[rustfmt::skip]
#[cfg(feature = "half")]
mod impl_half {
use super::*;
use half::{bf16, f16};
macro_rules! impl_const_onezero {
($($t:ty),+) => {
$(
impl Ident for $t {
fn can_zero(&self) -> bool { true }
fn can_one(&self) -> bool { true }
fn can_neg_one(&self) -> bool { true }
fn is_zero(&self) -> bool { self != &Self::new_zero() }
fn is_one(&self) -> bool { self != &Self::new_one() }
fn is_neg_one(&self) -> bool { self != &Self::new_neg_one() }
}
impl ConstZero for $t { const ZERO: Self = Self::from_f32_const(0.0); }
impl ConstOne for $t { const ONE: Self = Self::from_f32_const(1.0); }
impl ConstNegOne for $t { const NEG_ONE: Self = Self::from_f32_const(-1.0); }
impl Zero for $t {
fn new_zero() -> Self { <$t>::from_f32_const(0.0) }
}
impl One for $t {
fn new_one() -> Self { <$t>::from_f32_const(1.0) }
}
impl NegOne for $t {
fn new_neg_one() -> Self { <$t>::from_f32_const(-1.0) }
}
)+
};
}
impl_const_onezero![bf16, f16];
}
#[cfg(test)]
mod tests {
use super::*;
use static_assertions::assert_impl_all;
#[test]
fn onezero_primitives() {
macro_rules! assert_impl_onezero {
(both: $($t:ty),+) => {
assert_impl_onezero![@const: $($t),+];
assert_impl_onezero![@nonconst: $($t),+];
};
(@const: $($t:ty),+) => {
$( assert_impl_all![$t: ConstOne, ConstZero];)+
};
(@nonconst: $($t:ty),+) => {
$( assert_impl_all![$t: One, Zero];)+
};
}
assert_impl_onezero![both: i8, i16, i32, i64, i128, isize];
assert_impl_onezero![both: u8, u16, u32, u64, u128, usize];
assert_impl_onezero![both: f32, f64];
#[cfg(feature = "twofloat")]
assert_impl_onezero![@nonconst: twofloat::TwoFloat];
#[cfg(feature = "half")]
assert_impl_onezero![both: half::f16, half::bf16];
}
#[test]
fn neg1_primitives() {
macro_rules! assert_impl_neg1 {
(both: $($t:ty),+) => {
assert_impl_neg1![@const: $($t),+];
assert_impl_neg1![@nonconst: $($t),+];
};
(@const: $($t:ty),+) => {
$( assert_impl_all![$t: ConstNegOne];)+
};
(@nonconst: $($t:ty),+) => {
$( assert_impl_all![$t: NegOne];)+
};
}
assert_impl_neg1![both: i8, i16, i32, i64, i128, isize];
assert_impl_neg1![both: f32, f64];
#[cfg(feature = "twofloat")]
assert_impl_neg1![@nonconst: twofloat::TwoFloat];
#[cfg(feature = "half")]
assert_impl_neg1![@nonconst: half::f16, half::bf16];
}
}