use core::num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
};
use crate::error::{IntegerErrors, NumeraErrors, NumeraResult};
pub trait Count {
fn is_countable(&self) -> bool;
fn is_uncountable(&self) -> bool {
!self.is_countable()
}
}
pub trait Countable: Count {
#[rustfmt::skip]
fn next(&self) -> NumeraResult<Self> where Self: Sized;
#[rustfmt::skip]
fn previous(&self) -> NumeraResult<Self> where Self: Sized;
}
pub trait Uncountable: Count {}
macro_rules! impl_countable {
(many_integer: $($t:ty),+) => { $( impl_countable![integer: $t]; )+ };
(integer: $t:ty) => {
impl Count for $t {
fn is_countable(&self) -> bool { true }
}
impl Countable for $t {
fn next(&self) -> NumeraResult<Self> {
self.checked_add(1).ok_or(IntegerErrors::Overflow.into())
}
fn previous(&self) -> NumeraResult<Self> {
self.checked_sub(1).ok_or(IntegerErrors::Underflow.into())
}
}
};
(many_nonzero: $($t:ty),+) => { $( impl_countable![nonzero: $t]; )+ };
(nonzero: $t:ty) => {
impl Count for $t {
fn is_countable(&self) -> bool { true }
}
impl Countable for $t {
fn next(&self) -> NumeraResult<Self> {
let mut value = self.get().checked_add(1)
.ok_or::<NumeraErrors>(IntegerErrors::Overflow.into())?;
if value == 0 {
value = value.checked_add(1)
.ok_or::<NumeraErrors>(IntegerErrors::Overflow.into())?;
}
#[cfg(feature = "safe")]
return Ok(<$t>::new(value).unwrap());
#[cfg(not(feature = "safe"))]
return Ok(unsafe { <$t>::new_unchecked(value) });
}
fn previous(&self) -> NumeraResult<Self> {
let mut value = self.get().checked_sub(1)
.ok_or::<NumeraErrors>(IntegerErrors::Underflow.into())?;
if value == 0 {
value = value.checked_sub(1)
.ok_or::<NumeraErrors>(IntegerErrors::Underflow.into())?;
}
#[cfg(feature = "safe")]
return Ok(<$t>::new(value).unwrap());
#[cfg(not(feature = "safe"))]
return Ok(unsafe { <$t>::new_unchecked(value) });
}
}
};
}
macro_rules! impl_uncountable {
(many_float: $($t:ty),+) => { $( impl_uncountable![float: $t]; )+ };
(float: $t:ty) => {
impl Count for $t {
fn is_countable(&self) -> bool { false }
}
impl Uncountable for $t { }
};
}
impl_uncountable![many_float: f32, f64];
#[rustfmt::skip]
impl_countable![many_integer:
i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize];
#[rustfmt::skip]
impl_countable![many_nonzero:
NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize,
NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize];
#[cfg(feature = "twofloat")]
impl_uncountable![many_float: twofloat::TwoFloat];
#[cfg(feature = "half")]
impl_uncountable![many_float: half::bf16, half::f16];
#[cfg(feature = "dashu-int")]
#[rustfmt::skip]
mod impl_big {
use core::ops::{Add, Sub};
use super::{Count, Countable};
use dashu_int::{IBig, UBig, ops::BitTest};
use crate::error::{IntegerErrors, NumeraResult};
impl Count for IBig {
fn is_countable(&self) -> bool { true }
}
impl Countable for IBig {
fn next(&self) -> NumeraResult<Self> { Ok(self.add(1)) }
fn previous(&self) -> NumeraResult<Self> { Ok(self.sub(1)) }
}
impl Count for UBig {
fn is_countable(&self) -> bool { true }
}
impl Countable for UBig {
fn next(&self) -> NumeraResult<Self> { Ok(self.add(1_u8)) }
fn previous(&self) -> NumeraResult<Self> {
if self.bit_len() > 0 { Ok(self.sub(1_u8)) }
else { Err(IntegerErrors::LessThanZero.into()) }
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use static_assertions::*;
macro_rules! assert_impl {
(uncountable: $($t:ty),+) => {
$( assert_impl_all![$t: Uncountable];)+
};
(countable: $($t:ty),+) => {
$( assert_impl_all![$t: Countable];)+
};
}
#[rustfmt::skip]
#[test]
fn countable() {
assert_impl![countable:
i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize];
#[cfg(feature = "dashu-int")]
assert_impl![countable: dashu_int::IBig, dashu_int::UBig];
}
#[test]
fn uncountable() {
assert_impl![uncountable: f32, f64];
#[cfg(feature = "half")]
assert_impl![uncountable: half::bf16, half::f16];
#[cfg(feature = "twofloat")]
assert_impl![uncountable: twofloat::TwoFloat];
}
}