use crate::{ConstInit, InvalidValue, is, whilst};
#[doc = crate::_tags!(quant)]
#[doc = crate::_doc_location!("num/quant")]
#[must_use]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Sign {
Negative = -1,
#[default]
Zero = 0,
Positive = 1,
}
impl ConstInit for Sign {
const INIT: Self = Sign::Zero;
}
impl Sign {
#[inline(always)]
pub const fn eq(self, other: Self) -> bool {
self as i8 == other as i8
}
#[inline(always)]
pub const fn is_negative(self) -> bool {
matches!(self, Self::Negative)
}
#[inline(always)]
pub const fn is_positive(self) -> bool {
matches!(self, Self::Positive)
}
#[inline(always)]
pub const fn is_zero(self) -> bool {
matches!(self, Self::Zero)
}
#[inline(always)]
pub const fn is_nonzero(self) -> bool {
!self.is_zero()
}
#[inline(always)]
pub const fn invert(self) -> Self {
match self {
Self::Positive => Self::Negative,
Self::Negative => Self::Positive,
Self::Zero => Self::Zero,
}
}
#[inline(always)]
pub const fn same_direction(self, other: Self) -> bool {
matches!((self, other), (Self::Positive, Self::Positive) | (Self::Negative, Self::Negative))
}
pub const fn combine(self, other: Self) -> Self {
match (self, other) {
(Self::Zero, _) | (_, Self::Zero) => Self::Zero,
(Self::Positive, s) | (s, Self::Positive) => s,
(Self::Negative, Self::Negative) => Self::Positive,
}
}
pub const fn pow(self, n: u32) -> Self {
match self {
Self::Zero => Self::Zero,
Self::Positive => Self::Positive,
Self::Negative => is![n & 1 == 0, Self::Positive, Self::Negative],
}
}
#[inline(always)]
pub const fn abs(self) -> Self {
match self {
Self::Negative => Self::Positive,
_ => self,
}
}
#[inline(always)]
pub const fn neg_abs(self) -> Self {
match self {
Self::Positive => Self::Negative,
_ => self,
}
}
pub fn fold<I: IntoIterator<Item = Sign>>(iter: I) -> Self {
let mut acc = Self::Positive;
for s in iter.into_iter() {
acc = acc.combine(s);
if matches!(acc, Self::Zero) {
return Self::Zero;
}
}
acc
}
pub const fn fold_slice(slice: &[Sign]) -> Self {
let mut acc = Self::Positive;
whilst! { i in 0..slice.len(); {
let s = slice[i];
acc = acc.combine(s);
if matches!(acc, Self::Zero) {
return Self::Zero;
}
}}
acc
}
}
macro_rules! impl_into_sign {
(int: $($int:ty),+) => { $( impl_into_sign![@int: $int]; )+ };
(@int: $int:ty) => {
impl From<$int> for Sign {
fn from(n: $int) -> Sign {
match n {
0 => Sign::Zero,
1.. => Sign::Positive,
#[allow(unreachable_patterns, reason = "for unsigned")]
_ => Sign::Negative,
}
}
}
};
(float: $($float:ty),+) => { $( impl_into_sign![@float: $float]; )+ };
(@float: $float:ty) => {
impl From<$float> for Sign {
fn from(n: $float) -> Sign {
if n.is_sign_positive() {
Sign::Positive
} else {
Sign::Negative
}
}
}
};
(bool) => {
impl From<bool> for Sign {
fn from(n: bool) -> Sign {
match n {
true => Sign::Positive,
false => Sign::Negative,
}
}
}
};
}
impl_into_sign![int: u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize];
impl_into_sign![float: f32, f64];
impl_into_sign![bool];
macro_rules! impl_from_sign {
(sint: $($sint:ty),+) => { $( impl_from_sign![@sint: $sint]; )+ };
(@sint: $sint:ty) => {
impl From<Sign> for $sint {
fn from(s: Sign) -> $sint {
match s {
Sign::Zero => 0,
Sign::Positive => 1,
Sign::Negative => -1,
}
}
}
};
(uint: $($uint:ty),+) => { $( impl_from_sign![@uint: $uint]; )+ };
(@uint: $uint:ty) => {
impl TryFrom<Sign> for $uint {
type Error = InvalidValue;
fn try_from(s: Sign) -> Result<$uint, InvalidValue> {
match s {
Sign::Zero => Ok(0),
Sign::Positive => Ok(1),
Sign::Negative => Err(InvalidValue),
}
}
}
};
(float: $($float:ty),+) => { $( impl_from_sign![@float: $float]; )+ };
(@float: $float:ty) => {
impl From<Sign> for $float {
fn from(s: Sign) -> $float {
match s {
Sign::Zero => 0.0,
Sign::Positive => 1.0,
Sign::Negative => -1.0,
}
}
}
};
(bool) => {
impl TryFrom<Sign> for bool {
type Error = InvalidValue;
fn try_from(s: Sign) -> Result<bool, InvalidValue> {
match s {
Sign::Positive => Ok(true),
Sign::Negative => Ok(false),
Sign::Zero => Err(InvalidValue),
}
}
}
};
}
impl_from_sign![sint: i8, i16, i32, i64, i128, isize];
impl_from_sign![uint: u8, u16, u32, u64, u128, usize];
impl_from_sign![float: f32, f64];
impl_from_sign![bool];