diff --git a/Cargo.toml b/Cargo.toml index 86df9df..1c73692 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "num-primitive" -version = "0.3.7" +version = "0.3.8" description = "Traits for primitive numeric types" repository = "https://github.com/rust-num/num-primitive" license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 3e1e690..3eb59e6 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ Each trait includes supertraits for everything implemented in common by these types, as well as associated constants and methods matching their inherent items. `PrimitiveFloat` also adds the contents of `core::{float}::consts`. +`NonZero` integer wrappers are similarly supported by `NonZeroPrimitiveInteger`, +`NonZeroPrimitiveSigned`, and `NonZeroPrimitiveUnsigned`. + It is not a goal of this crate to *add* any functionality to the primitive types, only to expose what is already available in the standard library in a more generic way. The traits are also [sealed] against downstream diff --git a/RELEASES.md b/RELEASES.md index 183a610..3d4c215 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,9 @@ +# Release 0.3.8 (2026-07-09) + +- Added traits for `NonZero`-wrapped primitive integers: `NonZeroPrimitiveInteger`, + `NonZeroPrimitiveSigned`, and `NonZeroPrimitiveUnsigned`. +- Extended `PrimitiveInteger` and `PrimitiveUnsigned` for `NonZero` interactions. + # Release 0.3.7 (2026-03-15) - Updated to MSRV 1.94. diff --git a/src/integer.rs b/src/integer.rs index 4ce0f0e..f51c6c7 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -1,7 +1,9 @@ -use core::num::ParseIntError; +use core::num::{NonZero, ParseIntError, TryFromIntError}; use crate::{PrimitiveError, PrimitiveNumber, PrimitiveNumberRef}; +trait NonZeroSealed {} + /// Trait for all primitive [integer types], including the supertrait [`PrimitiveNumber`]. /// /// This encapsulates trait implementations, constants, and inherent methods that are common among @@ -40,6 +42,7 @@ pub trait PrimitiveInteger: PrimitiveNumber + core::cmp::Eq + core::cmp::Ord + + core::convert::From + core::convert::TryFrom + core::convert::TryFrom + core::convert::TryFrom @@ -52,6 +55,7 @@ pub trait PrimitiveInteger: + core::convert::TryFrom + core::convert::TryFrom + core::convert::TryFrom + + core::convert::TryInto + core::convert::TryInto + core::convert::TryInto + core::convert::TryInto @@ -72,6 +76,7 @@ pub trait PrimitiveInteger: + core::ops::BitAnd + core::ops::BitAndAssign + core::ops::BitOr + + core::ops::BitOr + core::ops::BitOrAssign + core::ops::BitXor + core::ops::BitXorAssign @@ -188,6 +193,11 @@ pub trait PrimitiveInteger: + for<'a> core::ops::ShrAssign<&'a u128> + for<'a> core::ops::ShrAssign<&'a usize> { + /// The non-zero integer type wrapping this primitive integer. + /// + /// This is always `core::num::NonZero`. + type NonZero: NonZeroPrimitiveInteger; + /// The size of this integer type in bits. const BITS: u32; @@ -594,9 +604,140 @@ pub trait PrimitiveIntegerRef: { } +/// Trait for [`NonZero`] primitive integers. +/// +/// This encapsulates trait implementations, constants, and inherent methods that are common among +/// all of the implementations of `NonZero`, where `T` is a [`PrimitiveInteger`]. +/// +/// See the corresponding items on the individual types for more documentation and examples. +/// +/// This trait is sealed with a private trait to prevent downstream implementations, so we may +/// continue to expand along with the standard library without worrying about breaking changes for +/// implementors. +/// +/// # Examples +/// +/// ``` +/// use num_primitive::NonZeroPrimitiveInteger; +/// use core::num::NonZero; +/// +/// fn bits_and_zeros(n: T) -> (u32, u32, u32) { +/// (T::BITS, n.leading_zeros(), n.trailing_zeros()) +/// } +/// +/// assert_eq!(bits_and_zeros(NonZero::new(0b0010_1000u8).unwrap()), (8, 2, 3)); +/// assert_eq!(bits_and_zeros(NonZero::new(1i64).unwrap()), (64, 63, 0)); +/// ``` +#[expect(private_bounds)] +pub trait NonZeroPrimitiveInteger: + 'static + + NonZeroSealed + + core::cmp::Eq + + core::cmp::Ord + + core::convert::Into + + core::convert::TryFrom + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryFrom, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::convert::TryInto, Error: PrimitiveError> + + core::fmt::Binary + + core::fmt::Debug + + core::fmt::Display + + core::fmt::LowerExp + + core::fmt::LowerHex + + core::fmt::Octal + + core::fmt::UpperExp + + core::fmt::UpperHex + + core::hash::Hash + + core::marker::Copy + + core::marker::Send + + core::marker::Sync + + core::marker::Unpin + + core::ops::BitOr + + core::ops::BitOr + + core::ops::BitOrAssign + + core::ops::BitOrAssign + + core::panic::RefUnwindSafe + + core::panic::UnwindSafe + + core::str::FromStr +{ + /// The primitive integer type that this non-zero type wraps. + /// + /// For `core::num::NonZero`, this is `T`. + type Integer: PrimitiveInteger; + + /// The size of this non-zero integer type in bits. + const BITS: u32; + + /// The largest value that can be represented by this non-zero integer type. + const MAX: Self; + + /// The smallest value that can be represented by this non-zero integer type. + const MIN: Self; + + /// Multiplies two non-zero integers together. Returns [`None`] on overflow. + fn checked_mul(self, other: Self) -> Option; + + /// Raises non-zero value to an integer power. Returns [`None`] on overflow. + fn checked_pow(self, other: u32) -> Option; + + /// Returns the number of ones in the binary representation of `self`. + fn count_ones(self) -> NonZero; + + /// Returns the contained value as a primitive type. + fn get(self) -> Self::Integer; + + /// Returns the number of leading zeros in the binary representation of `self`. + fn leading_zeros(self) -> u32; + + /// Creates a non-zero if the given value is not zero. + fn new(n: Self::Integer) -> Option; + + /// Multiplies two non-zero integers together, saturating at the numeric bounds + /// instead of overflowing. + fn saturating_mul(self, other: Self) -> Self; + + /// Raise non-zero value to an integer power, saturating at the numeric bounds + /// instead of overflowing. + fn saturating_pow(self, other: u32) -> Self; + + /// Returns the number of trailing zeros in the binary representation of `self`. + fn trailing_zeros(self) -> u32; + + /// Creates a non-zero without checking whether the value is non-zero. + /// This results in undefined behavior if the value is zero. + /// + /// # Safety + /// + /// The value must not be zero. + unsafe fn new_unchecked(n: Self::Integer) -> Self; +} + macro_rules! impl_integer { ($($Integer:ident),*) => {$( impl PrimitiveInteger for $Integer { + type NonZero = NonZero; + use_consts!(Self::{ BITS: u32, MAX: Self, @@ -693,6 +834,35 @@ macro_rules! impl_integer { } impl PrimitiveIntegerRef<$Integer> for &$Integer {} + + impl NonZeroSealed for NonZero<$Integer> {} + + impl NonZeroPrimitiveInteger for NonZero<$Integer> { + type Integer = $Integer; + + use_consts!(Self::{ + BITS: u32, + MAX: Self, + MIN: Self, + }); + + forward! { + fn new(n: Self::Integer) -> Option; + } + forward! { + fn checked_mul(self, other: Self) -> Option; + fn checked_pow(self, other: u32) -> Option; + fn count_ones(self) -> NonZero; + fn get(self) -> Self::Integer; + fn leading_zeros(self) -> u32; + fn saturating_mul(self, other: Self) -> Self; + fn saturating_pow(self, other: u32) -> Self; + fn trailing_zeros(self) -> u32; + } + forward! { + unsafe fn new_unchecked(n: Self::Integer) -> Self; + } + } )*} } diff --git a/src/lib.rs b/src/lib.rs index 2901999..a8ddf6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,9 @@ //! types, as well as associated constants and methods matching their inherent //! items. `PrimitiveFloat` also adds the contents of `core::{float}::consts`. //! +//! [`NonZero`][core::num::NonZero] integer wrappers are similarly supported by +//! [`NonZeroPrimitiveInteger`], [`NonZeroPrimitiveSigned`], and [`NonZeroPrimitiveUnsigned`]. +//! //! It is not a goal of this crate to *add* any functionality to the primitive //! types, only to expose what is already available in the standard library in a //! more generic way. The traits are also [sealed] against downstream @@ -73,7 +76,7 @@ mod tests; pub use self::bytes::PrimitiveBytes; pub use self::error::PrimitiveError; pub use self::float::{PrimitiveFloat, PrimitiveFloatRef, PrimitiveFloatToInt}; -pub use self::integer::{PrimitiveInteger, PrimitiveIntegerRef}; +pub use self::integer::{NonZeroPrimitiveInteger, PrimitiveInteger, PrimitiveIntegerRef}; pub use self::number::{PrimitiveNumber, PrimitiveNumberAs, PrimitiveNumberRef}; -pub use self::signed::{PrimitiveSigned, PrimitiveSignedRef}; -pub use self::unsigned::{PrimitiveUnsigned, PrimitiveUnsignedRef}; +pub use self::signed::{NonZeroPrimitiveSigned, PrimitiveSigned, PrimitiveSignedRef}; +pub use self::unsigned::{NonZeroPrimitiveUnsigned, PrimitiveUnsigned, PrimitiveUnsignedRef}; diff --git a/src/macros.rs b/src/macros.rs index 07e8187..81fae5d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -29,6 +29,14 @@ macro_rules! forward { unsafe { Self::$method(self $( , $arg )* ) } } )*}; + ($(unsafe fn $method:ident ( $( $arg:ident : $ty:ty ),* ) -> $ret:ty ; )*) => {$( + #[doc = forward_doc!($method)] + #[inline] + unsafe fn $method($( $arg : $ty ),* ) -> $ret { + // SAFETY: we're just passing through here! + unsafe { Self::$method($( $arg ),* ) } + } + )*}; } /// A string suitable for method `#[doc = ...]` diff --git a/src/signed.rs b/src/signed.rs index 01af8ca..9bf8552 100644 --- a/src/signed.rs +++ b/src/signed.rs @@ -1,6 +1,10 @@ use core::convert::Infallible; +use core::num::NonZero; -use crate::{PrimitiveInteger, PrimitiveIntegerRef, PrimitiveUnsigned}; +use crate::{ + NonZeroPrimitiveInteger, NonZeroPrimitiveUnsigned, PrimitiveInteger, PrimitiveIntegerRef, + PrimitiveUnsigned, +}; /// Trait for all primitive [signed integer types], including the supertraits [`PrimitiveInteger`] /// and [`PrimitiveNumber`][crate::PrimitiveNumber]. @@ -162,6 +166,87 @@ pub trait PrimitiveSigned: /// e.g. `where &T: PrimitiveSignedRef`. pub trait PrimitiveSignedRef: PrimitiveIntegerRef + core::ops::Neg {} +/// Trait for [`NonZero`] primitive signed integers, including the supertrait +/// [`NonZeroPrimitiveInteger`]. +/// +/// This encapsulates trait implementations and inherent methods that are common among all of the +/// implementations of `NonZero`, where `T` is a [`PrimitiveSigned`]. +/// +/// See the corresponding items on the individual types for more documentation and examples. +/// +/// This trait is sealed with a private trait to prevent downstream implementations, so we may +/// continue to expand along with the standard library without worrying about breaking changes for +/// implementors. +/// +/// # Examples +/// +/// ``` +/// use num_primitive::NonZeroPrimitiveSigned; +/// use core::num::NonZero; +/// +/// fn sign_and_magnitude(n: T) -> (bool, T::NonZeroUnsigned) { +/// (n.is_negative(), n.unsigned_abs()) +/// } +/// +/// let n = NonZero::new(-42i16).unwrap(); +/// assert_eq!(sign_and_magnitude(n), (true, NonZero::new(42u16).unwrap())); +/// ``` +pub trait NonZeroPrimitiveSigned: + NonZeroPrimitiveInteger + + core::convert::From> + + core::ops::Neg +{ + /// The unsigned non-zero integer type used by methods like + /// [`cast_unsigned`][Self::cast_unsigned]. + /// + /// For `core::num::NonZero`, this is `NonZero`. + type NonZeroUnsigned: NonZeroPrimitiveUnsigned; + + /// Computes the absolute value of self. + fn abs(self) -> Self; + + /// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size. + fn cast_unsigned(self) -> Self::NonZeroUnsigned; + + /// Checked absolute value. Checks for overflow and returns [`None`] if `self == Self::MIN`. + fn checked_abs(self) -> Option; + + /// Checked negation. Computes `-self`, returning `None` if `self == Self::MIN`. + fn checked_neg(self) -> Option; + + /// Returns `true` if `self` is positive and `false` if the number is negative. + fn is_positive(self) -> bool; + + /// Returns `true` if `self` is negative and `false` if the number is positive. + fn is_negative(self) -> bool; + + /// Computes the absolute value of self, with overflow information. + fn overflowing_abs(self) -> (Self, bool); + + /// Negates self, overflowing if this is equal to the minimum value. + fn overflowing_neg(self) -> (Self, bool); + + /// Saturating absolute value. + fn saturating_abs(self) -> Self; + + /// Saturating negation. Computes `-self`, returning `Self::MAX` + /// if `self == Self::MIN` instead of overflowing. + fn saturating_neg(self) -> Self; + + /// Computes the absolute value of self without any wrapping or panicking. + fn unsigned_abs(self) -> Self::NonZeroUnsigned; + + /// Wrapping absolute value. + fn wrapping_abs(self) -> Self; + + /// Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary + /// of the type. + fn wrapping_neg(self) -> Self; +} + +// TODO: consider a NonZero*Ref hierarchy, including Neg here. +// pub trait NonZeroPrimitiveSignedRef: core::ops::Neg {} + macro_rules! impl_signed { ($Signed:ident, $Unsigned:ty) => { impl PrimitiveSigned for $Signed { @@ -199,6 +284,26 @@ macro_rules! impl_signed { } impl PrimitiveSignedRef<$Signed> for &$Signed {} + + impl NonZeroPrimitiveSigned for NonZero<$Signed> { + type NonZeroUnsigned = NonZero<$Unsigned>; + + forward! { + fn abs(self) -> Self; + fn cast_unsigned(self) -> Self::NonZeroUnsigned; + fn checked_abs(self) -> Option; + fn checked_neg(self) -> Option; + fn is_negative(self) -> bool; + fn is_positive(self) -> bool; + fn overflowing_abs(self) -> (Self, bool); + fn overflowing_neg(self) -> (Self, bool); + fn saturating_abs(self) -> Self; + fn saturating_neg(self) -> Self; + fn unsigned_abs(self) -> Self::NonZeroUnsigned; + fn wrapping_abs(self) -> Self; + fn wrapping_neg(self) -> Self; + } + } }; } diff --git a/src/unsigned.rs b/src/unsigned.rs index 6bb21b9..dd2655e 100644 --- a/src/unsigned.rs +++ b/src/unsigned.rs @@ -1,6 +1,10 @@ use core::convert::Infallible; +use core::num::NonZero; -use crate::{PrimitiveInteger, PrimitiveIntegerRef, PrimitiveSigned}; +use crate::{ + NonZeroPrimitiveInteger, NonZeroPrimitiveSigned, PrimitiveInteger, PrimitiveIntegerRef, + PrimitiveSigned, +}; /// Trait for all primitive [unsigned integer types], including the supertraits /// [`PrimitiveInteger`] and [`PrimitiveNumber`][crate::PrimitiveNumber]. @@ -34,7 +38,15 @@ use crate::{PrimitiveInteger, PrimitiveIntegerRef, PrimitiveSigned}; /// assert_eq!(gcd::(1071, 462), 21); /// assert_eq!(gcd::(6_700_417, 2_147_483_647), 1); /// ``` -pub trait PrimitiveUnsigned: PrimitiveInteger + From + TryFrom { +pub trait PrimitiveUnsigned: + PrimitiveInteger + + core::convert::From + + core::convert::TryFrom + + core::ops::Div + + core::ops::DivAssign + + core::ops::Rem + + core::ops::RemAssign +{ /// The signed integer type used by methods like /// [`checked_add_signed`][Self::checked_add_signed]. type Signed: PrimitiveSigned; @@ -135,6 +147,76 @@ pub trait PrimitiveUnsigned: PrimitiveInteger + From + TryFrom`. pub trait PrimitiveUnsignedRef: PrimitiveIntegerRef {} +/// Trait for [`NonZero`] primitive unsigned integers, including the supertrait +/// [`NonZeroPrimitiveInteger`]. +/// +/// This encapsulates trait implementations and inherent methods that are common among all of the +/// implementations of `NonZero`, where `T` is a [`PrimitiveUnsigned`]. +/// +/// See the corresponding items on the individual types for more documentation and examples. +/// +/// This trait is sealed with a private trait to prevent downstream implementations, so we may +/// continue to expand along with the standard library without worrying about breaking changes for +/// implementors. +/// +/// # Examples +/// +/// ``` +/// use num_primitive::NonZeroPrimitiveUnsigned; +/// use core::num::NonZero; +/// +/// fn gcd(mut a: T, mut b: T) -> T { +/// while let Some(r) = T::new(b.get() % a) { +/// (a, b) = (r, a); +/// } +/// a +/// } +/// +/// let a = NonZero::new(48u32).unwrap(); +/// let b = NonZero::new(18u32).unwrap(); +/// assert_eq!(gcd(a, b).get(), 6); +/// ``` +pub trait NonZeroPrimitiveUnsigned: + NonZeroPrimitiveInteger + core::convert::From> +{ + /// The signed non-zero integer type used by methods like + /// [`cast_signed`][Self::cast_signed]. + /// + /// For `core::num::NonZero`, this is `NonZero`. + type NonZeroSigned: NonZeroPrimitiveSigned; + + /// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size. + fn cast_signed(self) -> Self::NonZeroSigned; + + /// Adds an unsigned integer to a non-zero value. Returns [`None`] on overflow. + fn checked_add(self, other: Self::Integer) -> Option; + + /// Returns the smallest power of two greater than or equal to `self`. Checks for overflow and + /// returns [`None`] if the next power of two is greater than the type’s maximum value. + fn checked_next_power_of_two(self) -> Option; + + /// Calculates the quotient of `self` and `rhs`, rounding the result towards positive infinity. + fn div_ceil(self, rhs: Self) -> Self; + + /// Returns the base 10 logarithm of the number, rounded down. + fn ilog10(self) -> u32; + + /// Returns the base 2 logarithm of the number, rounded down. + fn ilog2(self) -> u32; + + /// Returns `true` if and only if `self == (1 << k)` for some `k`. + fn is_power_of_two(self) -> bool; + + /// Returns the square root of the number, rounded down. + fn isqrt(self) -> Self; + + /// Calculates the midpoint (average) between `self` and `rhs`. + fn midpoint(self, rhs: Self) -> Self; + + /// Adds an unsigned integer to a non-zero value. Returns `Self::MAX` on overflow. + fn saturating_add(self, other: Self::Integer) -> Self; +} + macro_rules! impl_unsigned { ($Unsigned:ident, $Signed:ty) => { impl PrimitiveUnsigned for $Unsigned { @@ -169,6 +251,23 @@ macro_rules! impl_unsigned { } impl PrimitiveUnsignedRef<$Unsigned> for &$Unsigned {} + + impl NonZeroPrimitiveUnsigned for NonZero<$Unsigned> { + type NonZeroSigned = NonZero<$Signed>; + + forward! { + fn cast_signed(self) -> Self::NonZeroSigned; + fn checked_add(self, other: Self::Integer) -> Option; + fn checked_next_power_of_two(self) -> Option; + fn div_ceil(self, rhs: Self) -> Self; + fn ilog10(self) -> u32; + fn ilog2(self) -> u32; + fn is_power_of_two(self) -> bool; + fn isqrt(self) -> Self; + fn midpoint(self, rhs: Self) -> Self; + fn saturating_add(self, other: Self::Integer) -> Self; + } + } }; }