#![no_std]
#![forbid(unsafe_code)]
#![cfg_attr(doctest, doc = include_str!("../README.md"))]
pub mod convert;
pub mod si;
pub mod su;
pub use convert::{SaturatingFrom, SaturatingInto};
pub use si::{Si, Si8, Si16, Si32, Si64, Si128, si8, si16, si32, si64, si128};
pub use su::{Su, Su8, Su16, Su32, Su64, Su128, su8, su16, su32, su64, su128};
#[expect(
clippy::exhaustive_enums,
reason = "division failures are limited to zero divisors and primitive overflow"
)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DivError {
DivisionByZero,
Overflow,
}
impl core::fmt::Display for DivError {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match *self {
Self::DivisionByZero => f.write_str("division by zero"),
Self::Overflow => f.write_str("arithmetic overflow"),
}
}
}
impl core::error::Error for DivError {}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct TryFromFloatError(pub(crate) ());
impl core::fmt::Display for TryFromFloatError {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("out of range float type conversion attempted")
}
}
impl core::error::Error for TryFromFloatError {}
pub trait TryDiv<Rhs = Self> {
type Output;
fn try_div(self, rhs: Rhs) -> Result<Self::Output, DivError>;
}
pub trait TryRem<Rhs = Self> {
type Output;
fn try_rem(self, rhs: Rhs) -> Result<Self::Output, DivError>;
}
pub trait TryDivAssign<Rhs = Self> {
fn try_div_assign(&mut self, rhs: Rhs) -> Result<(), DivError>;
}
pub trait TryRemAssign<Rhs = Self> {
fn try_rem_assign(&mut self, rhs: Rhs) -> Result<(), DivError>;
}
macro_rules! define_wrapper {
($wrapper:ident) => {
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)]
#[repr(transparent)]
pub struct $wrapper<T>(core::num::Saturating<T>);
impl<T: core::fmt::Debug> core::fmt::Debug for $wrapper<T> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple(stringify!($wrapper)).field(&self.0.0).finish()
}
}
impl<T> $wrapper<T> {
#[inline]
pub const fn new(value: T) -> Self {
Self(core::num::Saturating(value))
}
#[inline]
pub const fn into_inner(self) -> T
where
T: Copy,
{
self.0.0
}
}
#[cfg(feature = "serde")]
impl<T> serde::Serialize for $wrapper<T>
where
T: serde::Serialize,
{
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde::Serialize::serialize(&self.0.0, serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, T> serde::Deserialize<'de> for $wrapper<T>
where
T: serde::Deserialize<'de>,
{
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
<T as serde::Deserialize>::deserialize(deserializer).map(Self::new)
}
}
impl<T: core::fmt::Display> core::fmt::Display for $wrapper<T> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
impl<T> From<T> for $wrapper<T> {
#[inline]
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: PartialEq> PartialEq<T> for $wrapper<T> {
#[inline]
fn eq(&self, other: &T) -> bool {
self.0.0 == *other
}
}
impl<T: PartialOrd> PartialOrd<T> for $wrapper<T> {
#[inline]
fn partial_cmp(&self, other: &T) -> Option<core::cmp::Ordering> {
self.0.0.partial_cmp(other)
}
}
$crate::define_wrapper!(@op $wrapper, Add, AddAssign, add, add_assign);
$crate::define_wrapper!(@op $wrapper, Sub, SubAssign, sub, sub_assign);
$crate::define_wrapper!(@op $wrapper, Mul, MulAssign, mul, mul_assign);
impl<T> core::iter::Sum for $wrapper<T>
where
Self: core::ops::Add<Output = Self> + Default,
{
#[inline]
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::default(), |a, b| a + b)
}
}
impl<'a, T: 'a> core::iter::Sum<&'a Self> for $wrapper<T>
where
Self: core::ops::Add<Output = Self> + Default + Copy,
{
#[inline]
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.copied().fold(Self::default(), |a, b| a + b)
}
}
};
(@op $wrapper:ident, $op:ident, $op_assign:ident, $method:ident, $method_assign:ident) => {
impl<T> core::ops::$op for $wrapper<T>
where
core::num::Saturating<T>: core::ops::$op<Output = core::num::Saturating<T>>,
{
type Output = Self;
#[inline]
fn $method(self, rhs: Self) -> Self::Output {
Self(self.0.$method(rhs.0))
}
}
impl<T> core::ops::$op_assign for $wrapper<T>
where
core::num::Saturating<T>: core::ops::$op_assign,
{
#[inline]
fn $method_assign(&mut self, rhs: Self) {
self.0.$method_assign(rhs.0);
}
}
impl<T> core::ops::$op<T> for $wrapper<T>
where
core::num::Saturating<T>: core::ops::$op<Output = core::num::Saturating<T>>,
{
type Output = Self;
#[inline]
fn $method(self, rhs: T) -> Self::Output {
Self(self.0.$method(core::num::Saturating(rhs)))
}
}
impl<T> core::ops::$op_assign<T> for $wrapper<T>
where
core::num::Saturating<T>: core::ops::$op_assign,
{
#[inline]
fn $method_assign(&mut self, rhs: T) {
self.0.$method_assign(core::num::Saturating(rhs));
}
}
};
}
pub(crate) use define_wrapper;
macro_rules! scalars {
($($ty:ident, $alias:ident, $ctor:ident, $primitive:ty);+ $(;)?) => {
$(
pub type $alias = $ty<$primitive>;
impl From<$alias> for $primitive {
#[inline]
fn from(value: $alias) -> Self {
value.into_inner()
}
}
impl PartialEq<$alias> for $primitive {
#[inline]
fn eq(&self, other: &$alias) -> bool {
*self == other.into_inner()
}
}
impl PartialOrd<$alias> for $primitive {
#[inline]
fn partial_cmp(&self, other: &$alias) -> Option<core::cmp::Ordering> {
self.partial_cmp(&other.into_inner())
}
}
impl $ty<$primitive> {
pub const BITS: u32 = <$primitive>::BITS;
pub const MIN: Self = $ctor(<$primitive>::MIN);
pub const MAX: Self = $ctor(<$primitive>::MAX);
pub const ZERO: Self = $ctor(0);
pub const ONE: Self = $ctor(1);
#[inline]
#[must_use]
pub const fn count_ones(self) -> u32 {
self.into_inner().count_ones()
}
#[inline]
#[must_use]
pub const fn count_zeros(self) -> u32 {
self.into_inner().count_zeros()
}
#[inline]
#[must_use]
pub const fn leading_zeros(self) -> u32 {
self.into_inner().leading_zeros()
}
#[inline]
#[must_use]
pub const fn leading_ones(self) -> u32 {
self.into_inner().leading_ones()
}
#[inline]
#[must_use]
pub const fn trailing_zeros(self) -> u32 {
self.into_inner().trailing_zeros()
}
#[inline]
#[must_use]
pub const fn trailing_ones(self) -> u32 {
self.into_inner().trailing_ones()
}
#[inline]
#[must_use]
pub const fn reverse_bits(self) -> Self {
$ctor(self.into_inner().reverse_bits())
}
#[inline]
#[must_use]
pub const fn rotate_left(self, n: u32) -> Self {
$ctor(self.into_inner().rotate_left(n))
}
#[inline]
#[must_use]
pub const fn rotate_right(self, n: u32) -> Self {
$ctor(self.into_inner().rotate_right(n))
}
#[inline]
#[must_use]
pub const fn swap_bytes(self) -> Self {
$ctor(self.into_inner().swap_bytes())
}
#[inline]
#[must_use]
pub const fn from_be(value: Self) -> Self {
$ctor(<$primitive>::from_be(value.into_inner()))
}
#[inline]
#[must_use]
pub const fn from_le(value: Self) -> Self {
$ctor(<$primitive>::from_le(value.into_inner()))
}
#[inline]
#[must_use]
pub const fn to_be(self) -> Self {
$ctor(self.into_inner().to_be())
}
#[inline]
#[must_use]
pub const fn to_le(self) -> Self {
$ctor(self.into_inner().to_le())
}
#[inline]
#[must_use]
pub const fn to_be_bytes(self) -> [u8; core::mem::size_of::<$primitive>()] {
self.into_inner().to_be_bytes()
}
#[inline]
#[must_use]
pub const fn to_le_bytes(self) -> [u8; core::mem::size_of::<$primitive>()] {
self.into_inner().to_le_bytes()
}
#[inline]
#[must_use]
pub const fn to_ne_bytes(self) -> [u8; core::mem::size_of::<$primitive>()] {
self.into_inner().to_ne_bytes()
}
#[inline]
#[must_use]
pub const fn from_be_bytes(
bytes: [u8; core::mem::size_of::<$primitive>()],
) -> Self {
$ctor(<$primitive>::from_be_bytes(bytes))
}
#[inline]
#[must_use]
pub const fn from_le_bytes(
bytes: [u8; core::mem::size_of::<$primitive>()],
) -> Self {
$ctor(<$primitive>::from_le_bytes(bytes))
}
#[inline]
#[must_use]
pub const fn from_ne_bytes(
bytes: [u8; core::mem::size_of::<$primitive>()],
) -> Self {
$ctor(<$primitive>::from_ne_bytes(bytes))
}
#[inline]
#[must_use]
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
match self.into_inner().checked_div(rhs.into_inner()) {
Some(v) => Some($ctor(v)),
None => None,
}
}
#[inline]
#[must_use]
pub const fn checked_div_euclid(self, rhs: Self) -> Option<Self> {
match self.into_inner().checked_div_euclid(rhs.into_inner()) {
Some(v) => Some($ctor(v)),
None => None,
}
}
#[inline]
#[must_use]
pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
match self.into_inner().checked_rem(rhs.into_inner()) {
Some(v) => Some($ctor(v)),
None => None,
}
}
#[inline]
#[must_use]
pub const fn checked_rem_euclid(self, rhs: Self) -> Option<Self> {
match self.into_inner().checked_rem_euclid(rhs.into_inner()) {
Some(v) => Some($ctor(v)),
None => None,
}
}
#[inline]
#[must_use]
pub const fn pow(self, exp: u32) -> Self {
$ctor(self.into_inner().saturating_pow(exp))
}
#[inline]
#[must_use]
pub const fn checked_ilog(self, base: $primitive) -> Option<u32> {
self.into_inner().checked_ilog(base)
}
#[inline]
#[must_use]
pub const fn checked_ilog2(self) -> Option<u32> {
self.into_inner().checked_ilog2()
}
#[inline]
#[must_use]
pub const fn checked_ilog10(self) -> Option<u32> {
self.into_inner().checked_ilog10()
}
}
impl $crate::TryDiv for $alias {
type Output = Self;
#[inline]
fn try_div(self, rhs: Self) -> Result<Self::Output, $crate::DivError> {
if rhs == Self::ZERO {
return Err($crate::DivError::DivisionByZero);
}
self.checked_div(rhs).ok_or($crate::DivError::Overflow)
}
}
impl $crate::TryDiv<$primitive> for $alias {
type Output = Self;
#[inline]
fn try_div(self, rhs: $primitive) -> Result<Self::Output, $crate::DivError> {
if rhs == 0 {
return Err($crate::DivError::DivisionByZero);
}
self.checked_div($ctor(rhs)).ok_or($crate::DivError::Overflow)
}
}
impl $crate::TryRem for $alias {
type Output = Self;
#[inline]
fn try_rem(self, rhs: Self) -> Result<Self::Output, $crate::DivError> {
if rhs == Self::ZERO {
return Err($crate::DivError::DivisionByZero);
}
self.checked_rem(rhs).ok_or($crate::DivError::Overflow)
}
}
impl $crate::TryRem<$primitive> for $alias {
type Output = Self;
#[inline]
fn try_rem(self, rhs: $primitive) -> Result<Self::Output, $crate::DivError> {
if rhs == 0 {
return Err($crate::DivError::DivisionByZero);
}
self.checked_rem($ctor(rhs)).ok_or($crate::DivError::Overflow)
}
}
impl $crate::TryDivAssign for $alias {
#[inline]
fn try_div_assign(&mut self, rhs: Self) -> Result<(), $crate::DivError> {
$crate::TryDiv::try_div(*self, rhs).map(|value| *self = value)
}
}
impl $crate::TryDivAssign<$primitive> for $alias {
#[inline]
fn try_div_assign(&mut self, rhs: $primitive) -> Result<(), $crate::DivError> {
$crate::TryDiv::try_div(*self, rhs).map(|value| *self = value)
}
}
impl $crate::TryRemAssign for $alias {
#[inline]
fn try_rem_assign(&mut self, rhs: Self) -> Result<(), $crate::DivError> {
$crate::TryRem::try_rem(*self, rhs).map(|value| *self = value)
}
}
impl $crate::TryRemAssign<$primitive> for $alias {
#[inline]
fn try_rem_assign(&mut self, rhs: $primitive) -> Result<(), $crate::DivError> {
$crate::TryRem::try_rem(*self, rhs).map(|value| *self = value)
}
}
impl core::iter::Product for $alias {
#[inline]
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ONE, |a, b| a * b)
}
}
impl<'a> core::iter::Product<&'a Self> for $alias {
#[inline]
fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.copied().fold(Self::ONE, |a, b| a * b)
}
}
#[must_use]
#[inline]
pub const fn $ctor(value: $primitive) -> $alias {
$ty::new(value)
}
)+
};
}
pub(crate) use scalars;