Skip to content

Commit

Permalink
Add RangedX::exact and RangedUx::rem
Browse files Browse the repository at this point in the history
  • Loading branch information
jhpratt committed Dec 30, 2023
1 parent bef3528 commit 77bc616
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 2 deletions.
30 changes: 29 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ macro_rules! if_signed {
(false $($x:tt)*) => {};
}

macro_rules! if_unsigned {
(true $($x:tt)*) => {};
(false $($x:tt)*) => { $($x)* };
}

macro_rules! article {
(true) => {
"An"
Expand Down Expand Up @@ -205,6 +210,14 @@ macro_rules! impl_ranged {
$internal,
);

impl $type<0, 0> {
#[inline(always)]
pub const fn exact<const VALUE: $internal>() -> $type<VALUE, VALUE> {
// Safety: The value is the only one in range.
unsafe { $type::new_unchecked(VALUE) }
}
}

impl<const MIN: $internal, const MAX: $internal> $type<MIN, MAX> {
/// The smallest value that can be represented by this type.
// Safety: `MIN` is in range by definition.
Expand Down Expand Up @@ -485,6 +498,21 @@ macro_rules! impl_ranged {
}
}

if_unsigned!($is_signed
/// Remainder. Computes `self % rhs`, statically guaranteeing that the returned value
/// is in range.
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline]
pub const fn rem<const RHS_VALUE: $internal>(
self,
rhs: $type<RHS_VALUE, RHS_VALUE>,
) -> $type<0, RHS_VALUE> {
<Self as $crate::traits::RangeIsValid>::ASSERT;
// Safety: The result is guaranteed to be in range due to the nature of remainder on
// unsigned integers.
unsafe { $type::new_unchecked(self.get() % rhs.get()) }
});

/// Checked integer remainder. Computes `self % rhs`, returning `None` if `rhs == 0` or
/// if the resulting value is out of range.
#[must_use = "this returns the result of the operation, without modifying the original"]
Expand Down Expand Up @@ -563,7 +591,7 @@ macro_rules! impl_ranged {
unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_neg())) }
}

/// Absolute value. Computes `self.neg()`, **failing to compile** if the result is not
/// Negation. Computes `self.neg()`, **failing to compile** if the result is not
/// guaranteed to be in range.
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline(always)]
Expand Down
13 changes: 12 additions & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ use crate::{
};

macro_rules! if_signed {
(signed $($x:tt)*) => { $($x)*};
(signed $($x:tt)*) => { $($x)* };
(unsigned $($x:tt)*) => {};
}

macro_rules! if_unsigned {
(signed $($x:tt)*) => {};
(unsigned $($x:tt)*) => { $($x)* };
}

#[test]
fn errors() {
assert_eq!(
Expand Down Expand Up @@ -264,6 +269,12 @@ macro_rules! tests {
}
)*}

#[test]
fn rem() {$(if_unsigned! { $signed
assert_eq!($t::<5, 10>::MAX.rem($t::exact::<3>()), $t::<0, 3>::new_static::<1>());
assert_eq!($t::<5, 10>::MAX.rem($t::exact::<5>()), $t::<0, 5>::MIN);
})*}

#[test]
fn checked_rem() {$(
assert_eq!($t::<5, 10>::MAX.checked_rem(11), Some($t::<5, 10>::MAX));
Expand Down

0 comments on commit 77bc616

Please sign in to comment.