use derive_more::{Binary, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex};
#[cfg(any(test, feature = "proptest"))]
use proptest::prelude::*;
#[allow(unused)]
#[cfg(test)]
use similar_asserts::assert_eq;
#[cfg(doc)]
use std::ops::{Range, RangeFrom, RangeInclusive};
use std::{
borrow::Borrow,
cmp::Ordering,
ffi::{c_int, c_uint},
fmt::{self, Debug, Formatter},
iter::{FusedIterator, Product, Sum},
num::{ParseIntError, TryFromIntError},
ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div,
DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub,
SubAssign,
},
str::FromStr,
};
pub(crate) fn expect_isize(x: c_int) -> isize {
x.try_into()
.expect("expected on any platform supported by hwloc")
}
pub(crate) fn expect_usize(x: c_uint) -> usize {
x.try_into()
.expect("expected on any platform supported by hwloc")
}
pub(crate) fn assert_slice_len<T>(len: usize) {
assert!(
len.checked_mul(std::mem::size_of::<T>())
.is_some_and(|prod| isize::try_from(prod).is_ok()),
"got an unsupported slice length from hwloc"
)
}
#[derive(
Binary,
Clone,
Copy,
Default,
Display,
Eq,
Hash,
LowerExp,
LowerHex,
Octal,
Ord,
PartialEq,
PartialOrd,
UpperExp,
UpperHex,
)]
pub struct PositiveInt(c_uint);
impl PositiveInt {
pub const MIN: Self = Self(0);
pub const ZERO: Self = Self(0);
pub const ONE: Self = Self(1);
pub const MAX: Self = Self(c_int::MAX as c_uint);
pub const EFFECTIVE_BITS: u32 = c_uint::BITS - 1;
pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
c_int::from_str_radix(src, radix).and_then(|_| c_uint::from_str_radix(src, radix).map(Self))
}
pub const fn count_ones(self) -> u32 {
self.0.count_ones()
}
pub const fn count_zeros(self) -> u32 {
self.0.count_zeros() - 1
}
pub const fn leading_zeros(self) -> u32 {
self.0.leading_zeros() - 1
}
pub const fn trailing_zeros(self) -> u32 {
if self.0 == 0 {
Self::EFFECTIVE_BITS
} else {
self.0.trailing_zeros()
}
}
pub const fn leading_ones(self) -> u32 {
(self.0 << 1).leading_ones()
}
pub const fn trailing_ones(self) -> u32 {
self.0.trailing_ones()
}
pub const fn rotate_left(self, n: u32) -> Self {
self.rotate_impl::<true>(n)
}
pub const fn rotate_right(self, n: u32) -> Self {
self.rotate_impl::<false>(n)
}
const fn rotate_impl<const LEFT: bool>(self, n: u32) -> Self {
let direct_shift = n % Self::EFFECTIVE_BITS;
let opposite_shift = Self::EFFECTIVE_BITS - direct_shift;
let (left_shift, right_shift) = if LEFT {
(direct_shift, opposite_shift)
} else {
(opposite_shift, direct_shift)
};
let high_order_bits = (self.0 << left_shift) & Self::MAX.0;
let low_order_bits = self.0 >> right_shift;
Self(high_order_bits | low_order_bits)
}
pub const fn reverse_bits(self) -> Self {
Self(self.0.reverse_bits() >> 1)
}
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
Self::const_try_from_c_uint(self.0 + rhs.0)
}
pub const fn checked_add_signed(self, rhs: isize) -> Option<Self> {
let Some(rhs) = Self::try_c_int_from_isize(rhs) else {
return None;
};
let Some(inner) = self.0.checked_add_signed(rhs) else {
return None;
};
Self::const_try_from_c_uint(inner)
}
#[allow(clippy::cast_possible_truncation, clippy::if_then_some_else_none)]
const fn try_c_int_from_isize(x: isize) -> Option<c_int> {
if x >= c_int::MIN as isize && x <= c_int::MAX as isize {
Some(x as c_int)
} else {
None
}
}
#[allow(clippy::if_then_some_else_none)]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
if let Some(inner) = self.0.checked_sub(rhs.0) {
Some(Self(inner))
} else {
None
}
}
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
if let Some(inner) = self.0.checked_mul(rhs.0) {
Self::const_try_from_c_uint(inner)
} else {
None
}
}
#[allow(clippy::if_then_some_else_none)]
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
if let Some(inner) = self.0.checked_div(rhs.0) {
Some(Self(inner))
} else {
None
}
}
pub const fn checked_div_euclid(self, rhs: Self) -> Option<Self> {
self.checked_div(rhs)
}
#[allow(clippy::if_then_some_else_none)]
pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
if let Some(inner) = self.0.checked_rem(rhs.0) {
Some(Self(inner))
} else {
None
}
}
pub const fn checked_rem_euclid(self, rhs: Self) -> Option<Self> {
self.checked_rem(rhs)
}
pub const fn ilog(self, base: Self) -> u32 {
self.0.ilog(base.0)
}
pub const fn ilog2(self) -> u32 {
self.0.ilog2()
}
pub const fn ilog10(self) -> u32 {
self.0.ilog10()
}
pub const fn checked_ilog(self, base: Self) -> Option<u32> {
self.0.checked_ilog(base.0)
}
pub const fn checked_ilog2(self) -> Option<u32> {
self.0.checked_ilog2()
}
pub const fn checked_ilog10(self) -> Option<u32> {
self.0.checked_ilog10()
}
#[allow(clippy::if_then_some_else_none)]
pub const fn checked_neg(self) -> Option<Self> {
if self.0 == 0 { Some(Self(0)) } else { None }
}
#[allow(clippy::if_then_some_else_none)]
pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
if rhs < Self::EFFECTIVE_BITS {
Some(Self((self.0 << rhs) & Self::MAX.0))
} else {
None
}
}
#[allow(clippy::if_then_some_else_none)]
pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
if rhs < Self::EFFECTIVE_BITS {
Some(Self(self.0 >> rhs))
} else {
None
}
}
#[allow(clippy::if_then_some_else_none)]
pub const fn checked_pow(self, exp: u32) -> Option<Self> {
if let Some(inner) = self.0.checked_pow(exp) {
Self::const_try_from_c_uint(inner)
} else {
None
}
}
pub const fn saturating_add(self, rhs: Self) -> Self {
let inner = self.0.saturating_add(rhs.0);
Self::const_sat_from_c_uint(inner)
}
#[allow(
clippy::cast_possible_truncation,
clippy::missing_docs_in_private_items
)]
pub const fn saturating_add_signed(self, rhs: isize) -> Self {
const C_INT_MIN: isize = c_int::MIN as isize;
const C_INT_MAX: isize = c_int::MAX as isize;
let rhs = match rhs {
C_INT_MIN..=C_INT_MAX => rhs as c_int,
under if under < 0 => c_int::MIN,
_over => c_int::MAX,
};
let inner = self.0.saturating_add_signed(rhs);
Self::const_sat_from_c_uint(inner)
}
pub const fn saturating_sub(self, rhs: Self) -> Self {
Self(self.0.saturating_sub(rhs.0))
}
pub const fn saturating_mul(self, rhs: Self) -> Self {
let inner = self.0.saturating_mul(rhs.0);
Self::const_sat_from_c_uint(inner)
}
pub const fn saturating_div(self, rhs: Self) -> Self {
Self(self.0 / rhs.0)
}
pub const fn saturating_pow(self, exp: u32) -> Self {
let inner = self.0.saturating_pow(exp);
Self::const_sat_from_c_uint(inner)
}
const fn const_sat_from_c_uint(inner: c_uint) -> Self {
if inner <= Self::MAX.0 {
Self(inner)
} else {
Self::MAX
}
}
pub const fn wrapping_add(self, rhs: Self) -> Self {
Self(self.0.wrapping_add(rhs.0) & Self::MAX.0)
}
#[allow(clippy::cast_possible_truncation)]
pub const fn wrapping_add_signed(self, rhs: isize) -> Self {
Self(self.0.wrapping_add_signed(rhs as c_int) & Self::MAX.0)
}
pub const fn wrapping_sub(self, rhs: Self) -> Self {
Self(self.0.wrapping_sub(rhs.0) & Self::MAX.0)
}
pub const fn wrapping_mul(self, rhs: Self) -> Self {
Self(self.0.wrapping_mul(rhs.0) & Self::MAX.0)
}
pub const fn wrapping_div(self, rhs: Self) -> Self {
Self(self.0 / rhs.0)
}
pub const fn wrapping_div_euclid(self, rhs: Self) -> Self {
Self(self.0 / rhs.0)
}
pub const fn wrapping_rem(self, rhs: Self) -> Self {
Self(self.0 % rhs.0)
}
pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self {
Self(self.0 % rhs.0)
}
pub const fn wrapping_neg(self) -> Self {
Self(self.0.wrapping_neg() & Self::MAX.0)
}
pub const fn wrapping_shl(self, rhs: u32) -> Self {
let wrapped_rhs = rhs % Self::EFFECTIVE_BITS;
Self((self.0 << wrapped_rhs) & Self::MAX.0)
}
pub const fn wrapping_shr(self, rhs: u32) -> Self {
Self(self.0 >> (rhs % Self::EFFECTIVE_BITS))
}
pub const fn wrapping_pow(self, exp: u32) -> Self {
Self(self.0.wrapping_pow(exp) & Self::MAX.0)
}
pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
let overflow = rhs.0 > Self::MAX.0 - self.0;
(self.wrapping_add(rhs), overflow)
}
#[allow(clippy::cast_possible_wrap)]
pub const fn overflowing_add_signed(self, rhs: isize) -> (Self, bool) {
let overflow = (rhs > (Self::MAX.0 - self.0) as isize) || (rhs < -(self.0 as isize));
(self.wrapping_add_signed(rhs), overflow)
}
pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
(self.wrapping_sub(rhs), rhs.0 > self.0)
}
pub const fn abs_diff(self, other: Self) -> Self {
Self(self.0.abs_diff(other.0))
}
pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
let (inner, mut overflow) = self.0.overflowing_mul(rhs.0);
overflow |= inner > Self::MAX.0;
(Self(inner & Self::MAX.0), overflow)
}
pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) {
(Self(self.0 / rhs.0), false)
}
pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) {
(Self(self.0 / rhs.0), false)
}
pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
(Self(self.0 % rhs.0), false)
}
pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) {
(Self(self.0 % rhs.0), false)
}
pub const fn overflowing_neg(self) -> (Self, bool) {
(self.wrapping_neg(), self.0 != 0)
}
pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) {
(self.wrapping_shl(rhs), rhs >= Self::EFFECTIVE_BITS)
}
pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
(self.wrapping_shr(rhs), rhs >= Self::EFFECTIVE_BITS)
}
pub const fn overflowing_pow(self, exp: u32) -> (Self, bool) {
let (inner, mut overflow) = self.0.overflowing_pow(exp);
overflow |= inner > Self::MAX.0;
(Self(inner & Self::MAX.0), overflow)
}
pub const fn pow(self, exp: u32) -> Self {
if cfg!(debug_assertions) {
if let Some(res) = self.checked_pow(exp) {
res
} else {
panic!("Attempted to call PositiveInt::pow() with overflow")
}
} else {
self.wrapping_pow(exp)
}
}
pub const fn div_euclid(self, rhs: Self) -> Self {
Self(self.0 / rhs.0)
}
pub const fn rem_euclid(self, rhs: Self) -> Self {
Self(self.0 % rhs.0)
}
pub const fn is_power_of_two(self) -> bool {
self.0.is_power_of_two()
}
pub const fn next_power_of_two(self) -> Self {
if cfg!(debug_assertions) {
if let Some(res) = self.checked_next_power_of_two() {
res
} else {
panic!("Attempted to compute next power of two with overflow")
}
} else {
Self(self.0.next_power_of_two() & Self::MAX.0)
}
}
#[allow(clippy::if_then_some_else_none)]
pub const fn checked_next_power_of_two(self) -> Option<Self> {
let inner = self.0.next_power_of_two();
if inner <= Self::MAX.0 {
Some(Self(inner))
} else {
None
}
}
pub fn iter_range(
start: Self,
end: Self,
) -> impl DoubleEndedIterator<Item = Self> + Clone + ExactSizeIterator + FusedIterator {
PositiveIntRangeIter { start, end }
}
pub fn iter_range_inclusive(
start: Self,
end: Self,
) -> impl DoubleEndedIterator<Item = Self> + Clone + ExactSizeIterator + FusedIterator {
PositiveIntRangeInclusiveIter {
start,
end,
exhausted: start > end,
}
}
pub fn iter_range_from(start: Self) -> impl FusedIterator<Item = Self> + Clone {
PositiveIntRangeFromIter(start)
}
pub(crate) fn try_from_c_int(x: c_int) -> Result<Self, TryFromIntError> {
x.try_into().map(Self)
}
#[allow(clippy::if_then_some_else_none)]
const fn const_try_from_c_uint(x: c_uint) -> Option<Self> {
if x <= Self::MAX.0 {
Some(Self(x))
} else {
None
}
}
#[allow(clippy::cast_possible_wrap)]
pub(crate) const fn to_c_int(self) -> c_int {
self.0 as c_int
}
pub(crate) const fn to_c_uint(self) -> c_uint {
self.0
}
}
impl<B: Borrow<Self>> Add<B> for PositiveInt {
type Output = Self;
fn add(self, rhs: B) -> Self {
if cfg!(debug_assertions) {
self.checked_add(*rhs.borrow())
.expect("Attempted to add with overflow")
} else {
self.wrapping_add(*rhs.borrow())
}
}
}
impl Add<isize> for PositiveInt {
type Output = Self;
fn add(self, rhs: isize) -> Self {
if cfg!(debug_assertions) {
self.checked_add_signed(rhs)
.expect("Attempted to add with overflow")
} else {
self.wrapping_add_signed(rhs)
}
}
}
impl Add<PositiveInt> for isize {
type Output = PositiveInt;
fn add(self, rhs: PositiveInt) -> PositiveInt {
rhs + self
}
}
impl Add<&isize> for PositiveInt {
type Output = Self;
fn add(self, rhs: &isize) -> Self {
self + (*rhs)
}
}
impl Add<PositiveInt> for &isize {
type Output = PositiveInt;
fn add(self, rhs: PositiveInt) -> PositiveInt {
rhs + (*self)
}
}
impl<B: Borrow<PositiveInt>> Add<B> for &PositiveInt {
type Output = PositiveInt;
fn add(self, rhs: B) -> PositiveInt {
*self + rhs
}
}
impl Add<isize> for &PositiveInt {
type Output = PositiveInt;
fn add(self, rhs: isize) -> PositiveInt {
*self + rhs
}
}
impl Add<&PositiveInt> for isize {
type Output = PositiveInt;
fn add(self, rhs: &PositiveInt) -> PositiveInt {
*rhs + self
}
}
impl Add<&isize> for &PositiveInt {
type Output = PositiveInt;
fn add(self, rhs: &isize) -> PositiveInt {
*self + (*rhs)
}
}
impl Add<&PositiveInt> for &isize {
type Output = PositiveInt;
fn add(self, rhs: &PositiveInt) -> PositiveInt {
*rhs + (*self)
}
}
impl<Rhs> AddAssign<Rhs> for PositiveInt
where
Self: Add<Rhs, Output = Self>,
{
fn add_assign(&mut self, rhs: Rhs) {
*self = *self + rhs
}
}
#[cfg(any(test, feature = "proptest"))]
impl Arbitrary for PositiveInt {
type Parameters = ();
type Strategy = prop::strategy::Map<std::ops::RangeInclusive<c_uint>, fn(c_uint) -> Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(Self::MIN.0..=Self::MAX.0).prop_map(Self)
}
}
impl<B: Borrow<Self>> BitAnd<B> for PositiveInt {
type Output = Self;
fn bitand(self, rhs: B) -> Self {
Self(self.0 & rhs.borrow().0)
}
}
impl BitAnd<usize> for PositiveInt {
type Output = Self;
#[allow(clippy::cast_possible_truncation)]
fn bitand(self, rhs: usize) -> Self {
Self(self.0 & (rhs as c_uint))
}
}
impl BitAnd<PositiveInt> for usize {
type Output = PositiveInt;
fn bitand(self, rhs: PositiveInt) -> PositiveInt {
rhs & self
}
}
impl BitAnd<&usize> for PositiveInt {
type Output = Self;
fn bitand(self, rhs: &usize) -> Self {
self & (*rhs)
}
}
impl BitAnd<PositiveInt> for &usize {
type Output = PositiveInt;
fn bitand(self, rhs: PositiveInt) -> PositiveInt {
rhs & (*self)
}
}
impl<B: Borrow<PositiveInt>> BitAnd<B> for &PositiveInt {
type Output = PositiveInt;
fn bitand(self, rhs: B) -> PositiveInt {
*self & rhs
}
}
impl BitAnd<usize> for &PositiveInt {
type Output = PositiveInt;
fn bitand(self, rhs: usize) -> PositiveInt {
*self & rhs
}
}
impl BitAnd<&PositiveInt> for usize {
type Output = PositiveInt;
fn bitand(self, rhs: &PositiveInt) -> PositiveInt {
*rhs & self
}
}
impl BitAnd<&usize> for &PositiveInt {
type Output = PositiveInt;
fn bitand(self, rhs: &usize) -> PositiveInt {
*self & (*rhs)
}
}
impl BitAnd<&PositiveInt> for &usize {
type Output = PositiveInt;
fn bitand(self, rhs: &PositiveInt) -> PositiveInt {
*rhs & (*self)
}
}
impl<Rhs> BitAndAssign<Rhs> for PositiveInt
where
Self: BitAnd<Rhs, Output = Self>,
{
fn bitand_assign(&mut self, rhs: Rhs) {
*self = *self & rhs
}
}
impl<B: Borrow<Self>> BitOr<B> for PositiveInt {
type Output = Self;
fn bitor(self, rhs: B) -> Self {
Self(self.0 | rhs.borrow().0)
}
}
impl BitOr<usize> for PositiveInt {
type Output = Self;
#[allow(clippy::cast_possible_truncation)]
fn bitor(self, rhs: usize) -> Self {
debug_assert!(rhs <= usize::from(Self::MAX), "RHS out of range");
Self(self.0 | ((rhs as c_uint) & Self::MAX.0))
}
}
impl BitOr<PositiveInt> for usize {
type Output = PositiveInt;
fn bitor(self, rhs: PositiveInt) -> PositiveInt {
rhs | self
}
}
impl BitOr<&usize> for PositiveInt {
type Output = Self;
fn bitor(self, rhs: &usize) -> Self {
self | (*rhs)
}
}
impl BitOr<PositiveInt> for &usize {
type Output = PositiveInt;
fn bitor(self, rhs: PositiveInt) -> PositiveInt {
rhs | (*self)
}
}
impl<B: Borrow<PositiveInt>> BitOr<B> for &PositiveInt {
type Output = PositiveInt;
fn bitor(self, rhs: B) -> PositiveInt {
*self | rhs
}
}
impl BitOr<usize> for &PositiveInt {
type Output = PositiveInt;
fn bitor(self, rhs: usize) -> PositiveInt {
*self | rhs
}
}
impl BitOr<&PositiveInt> for usize {
type Output = PositiveInt;
fn bitor(self, rhs: &PositiveInt) -> PositiveInt {
*rhs | self
}
}
impl BitOr<&usize> for &PositiveInt {
type Output = PositiveInt;
fn bitor(self, rhs: &usize) -> PositiveInt {
*self | (*rhs)
}
}
impl BitOr<&PositiveInt> for &usize {
type Output = PositiveInt;
fn bitor(self, rhs: &PositiveInt) -> PositiveInt {
*rhs | (*self)
}
}
impl<Rhs> BitOrAssign<Rhs> for PositiveInt
where
Self: BitOr<Rhs, Output = Self>,
{
fn bitor_assign(&mut self, rhs: Rhs) {
*self = *self | rhs
}
}
impl<B: Borrow<Self>> BitXor<B> for PositiveInt {
type Output = Self;
fn bitxor(self, rhs: B) -> Self {
Self(self.0 ^ rhs.borrow().0)
}
}
impl BitXor<usize> for PositiveInt {
type Output = Self;
#[allow(clippy::cast_possible_truncation)]
fn bitxor(self, rhs: usize) -> Self {
debug_assert!(rhs <= usize::from(Self::MAX), "RHS out of range");
Self(self.0 ^ ((rhs as c_uint) & Self::MAX.0))
}
}
impl BitXor<PositiveInt> for usize {
type Output = PositiveInt;
fn bitxor(self, rhs: PositiveInt) -> PositiveInt {
rhs ^ self
}
}
impl BitXor<&usize> for PositiveInt {
type Output = Self;
fn bitxor(self, rhs: &usize) -> Self {
self ^ (*rhs)
}
}
impl BitXor<PositiveInt> for &usize {
type Output = PositiveInt;
fn bitxor(self, rhs: PositiveInt) -> PositiveInt {
rhs ^ (*self)
}
}
impl<B: Borrow<PositiveInt>> BitXor<B> for &PositiveInt {
type Output = PositiveInt;
fn bitxor(self, rhs: B) -> PositiveInt {
*self ^ rhs
}
}
impl BitXor<usize> for &PositiveInt {
type Output = PositiveInt;
fn bitxor(self, rhs: usize) -> PositiveInt {
*self ^ rhs
}
}
impl BitXor<&PositiveInt> for usize {
type Output = PositiveInt;
fn bitxor(self, rhs: &PositiveInt) -> PositiveInt {
*rhs ^ self
}
}
impl BitXor<&usize> for &PositiveInt {
type Output = PositiveInt;
fn bitxor(self, rhs: &usize) -> PositiveInt {
*self ^ (*rhs)
}
}
impl BitXor<&PositiveInt> for &usize {
type Output = PositiveInt;
fn bitxor(self, rhs: &PositiveInt) -> PositiveInt {
*rhs ^ (*self)
}
}
impl<Rhs> BitXorAssign<Rhs> for PositiveInt
where
Self: BitXor<Rhs, Output = Self>,
{
fn bitxor_assign(&mut self, rhs: Rhs) {
*self = *self ^ rhs
}
}
impl Debug for PositiveInt {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let s = format!("PositiveInt({})", self.0);
f.pad(&s)
}
}
impl<B: Borrow<Self>> Div<B> for PositiveInt {
type Output = Self;
fn div(self, rhs: B) -> Self {
Self(self.0 / rhs.borrow().0)
}
}
impl Div<usize> for PositiveInt {
type Output = Self;
fn div(self, rhs: usize) -> Self {
Self::try_from(rhs).map_or(Self::ZERO, |rhs| self / rhs)
}
}
impl Div<&usize> for PositiveInt {
type Output = Self;
fn div(self, rhs: &usize) -> Self {
self / *rhs
}
}
impl<B: Borrow<PositiveInt>> Div<B> for &PositiveInt {
type Output = PositiveInt;
fn div(self, rhs: B) -> PositiveInt {
*self / rhs
}
}
impl Div<usize> for &PositiveInt {
type Output = PositiveInt;
fn div(self, rhs: usize) -> PositiveInt {
*self / rhs
}
}
impl Div<&usize> for &PositiveInt {
type Output = PositiveInt;
fn div(self, rhs: &usize) -> PositiveInt {
*self / *rhs
}
}
impl<Rhs> DivAssign<Rhs> for PositiveInt
where
Self: Div<Rhs, Output = Self>,
{
fn div_assign(&mut self, rhs: Rhs) {
*self = *self / rhs
}
}
impl From<PositiveInt> for isize {
fn from(x: PositiveInt) -> Self {
expect_isize(x.to_c_int())
}
}
impl From<PositiveInt> for usize {
fn from(x: PositiveInt) -> Self {
expect_usize(x.to_c_uint())
}
}
impl FromStr for PositiveInt {
type Err = ParseIntError;
fn from_str(src: &str) -> Result<Self, ParseIntError> {
Self::from_str_radix(src, 10)
}
}
impl<B: Borrow<Self>> Mul<B> for PositiveInt {
type Output = Self;
fn mul(self, rhs: B) -> Self {
if cfg!(debug_assertions) {
self.checked_mul(*rhs.borrow())
.expect("Attempted to multiply with overflow")
} else {
self.wrapping_mul(*rhs.borrow())
}
}
}
impl Mul<usize> for PositiveInt {
type Output = Self;
#[allow(clippy::cast_possible_truncation)]
fn mul(self, rhs: usize) -> Self {
if cfg!(debug_assertions) {
if self == Self::ZERO {
Self::ZERO
} else {
Self::try_from(rhs)
.ok()
.and_then(|rhs| self.checked_mul(rhs))
.expect("Attempted to multiply with overflow")
}
} else {
let usize_result = usize::from(self) * rhs;
let truncated = usize_result & (Self::MAX.0 as usize);
Self(truncated as c_uint)
}
}
}
impl Mul<PositiveInt> for usize {
type Output = PositiveInt;
fn mul(self, rhs: PositiveInt) -> PositiveInt {
rhs * self
}
}
impl Mul<&usize> for PositiveInt {
type Output = Self;
fn mul(self, rhs: &usize) -> Self {
self * (*rhs)
}
}
impl Mul<PositiveInt> for &usize {
type Output = PositiveInt;
fn mul(self, rhs: PositiveInt) -> PositiveInt {
rhs * (*self)
}
}
impl<B: Borrow<PositiveInt>> Mul<B> for &PositiveInt {
type Output = PositiveInt;
fn mul(self, rhs: B) -> PositiveInt {
*self * rhs
}
}
impl Mul<usize> for &PositiveInt {
type Output = PositiveInt;
fn mul(self, rhs: usize) -> PositiveInt {
*self * rhs
}
}
impl Mul<&PositiveInt> for usize {
type Output = PositiveInt;
fn mul(self, rhs: &PositiveInt) -> PositiveInt {
*rhs * self
}
}
impl Mul<&usize> for &PositiveInt {
type Output = PositiveInt;
fn mul(self, rhs: &usize) -> PositiveInt {
(*self) * (*rhs)
}
}
impl Mul<&PositiveInt> for &usize {
type Output = PositiveInt;
fn mul(self, rhs: &PositiveInt) -> PositiveInt {
(*rhs) * (*self)
}
}
impl<Rhs> MulAssign<Rhs> for PositiveInt
where
Self: Mul<Rhs, Output = Self>,
{
fn mul_assign(&mut self, rhs: Rhs) {
*self = *self * rhs
}
}
impl Not for PositiveInt {
type Output = Self;
fn not(self) -> Self::Output {
Self(self.0 ^ Self::MAX.0)
}
}
impl Not for &PositiveInt {
type Output = PositiveInt;
fn not(self) -> Self::Output {
!*self
}
}
impl PartialEq<usize> for PositiveInt {
fn eq(&self, other: &usize) -> bool {
usize::from(*self) == *other
}
}
impl PartialEq<PositiveInt> for usize {
#[allow(clippy::use_self)]
fn eq(&self, other: &PositiveInt) -> bool {
*self == usize::from(*other)
}
}
impl PartialOrd<usize> for PositiveInt {
fn partial_cmp(&self, other: &usize) -> Option<Ordering> {
usize::from(*self).partial_cmp(other)
}
}
impl PartialOrd<PositiveInt> for usize {
#[allow(clippy::use_self)]
fn partial_cmp(&self, other: &PositiveInt) -> Option<Ordering> {
self.partial_cmp(&usize::from(*other))
}
}
impl<B: Borrow<Self>> Product<B> for PositiveInt {
fn product<I: Iterator<Item = B>>(iter: I) -> Self {
iter.fold(Self::ONE, |acc, contrib| acc * contrib.borrow())
}
}
impl<B: Borrow<Self>> Rem<B> for PositiveInt {
type Output = Self;
fn rem(self, rhs: B) -> Self {
Self(self.0 % rhs.borrow().0)
}
}
impl Rem<usize> for PositiveInt {
type Output = Self;
fn rem(self, rhs: usize) -> Self {
Self::try_from(rhs).map_or(self, |rhs| self % rhs)
}
}
impl Rem<&usize> for PositiveInt {
type Output = Self;
fn rem(self, rhs: &usize) -> Self {
self % (*rhs)
}
}
impl<B: Borrow<PositiveInt>> Rem<B> for &PositiveInt {
type Output = PositiveInt;
fn rem(self, rhs: B) -> PositiveInt {
*self % rhs
}
}
impl Rem<usize> for &PositiveInt {
type Output = PositiveInt;
fn rem(self, rhs: usize) -> PositiveInt {
*self % rhs
}
}
impl Rem<&usize> for &PositiveInt {
type Output = PositiveInt;
fn rem(self, rhs: &usize) -> PositiveInt {
*self % (*rhs)
}
}
impl<Rhs> RemAssign<Rhs> for PositiveInt
where
Self: Rem<Rhs, Output = Self>,
{
fn rem_assign(&mut self, rhs: Rhs) {
*self = *self % rhs
}
}
impl Shl<Self> for PositiveInt {
type Output = Self;
fn shl(self, rhs: Self) -> Self {
self << usize::from(rhs)
}
}
impl Shl<&Self> for PositiveInt {
type Output = Self;
fn shl(self, rhs: &Self) -> Self {
self << *rhs
}
}
impl Shl<PositiveInt> for &PositiveInt {
type Output = PositiveInt;
fn shl(self, rhs: PositiveInt) -> PositiveInt {
*self << rhs
}
}
#[allow(clippy::use_self)]
impl Shl<&PositiveInt> for &PositiveInt {
type Output = PositiveInt;
fn shl(self, rhs: &PositiveInt) -> PositiveInt {
*self << *rhs
}
}
macro_rules! shl_with_int {
( $( $int:ty ),* ) => { $(
impl Shl<$int> for PositiveInt {
type Output = Self;
#[allow(
trivial_numeric_casts,
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap
)]
fn shl(self, mut rhs: $int) -> Self {
if cfg!(debug_assertions) {
assert_eq!(
rhs.div_euclid(Self::EFFECTIVE_BITS as $int), 0,
"Attempted to shift left with overflow"
);
} else {
rhs = rhs.rem_euclid(Self::EFFECTIVE_BITS as $int);
}
Self((self.0 << rhs) & Self::MAX.0)
}
}
impl Shl<&$int> for PositiveInt {
type Output = Self;
fn shl(self, rhs: &$int) -> Self {
self << *rhs
}
}
impl Shl<$int> for &PositiveInt {
type Output = PositiveInt;
fn shl(self, rhs: $int) -> PositiveInt {
*self << rhs
}
}
impl Shl<&$int> for &PositiveInt {
type Output = PositiveInt;
fn shl(self, rhs: &$int) -> PositiveInt {
*self << *rhs
}
}
)* };
}
shl_with_int!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
impl<Rhs> ShlAssign<Rhs> for PositiveInt
where
Self: Shl<Rhs, Output = Self>,
{
fn shl_assign(&mut self, rhs: Rhs) {
*self = *self << rhs
}
}
impl Shr<Self> for PositiveInt {
type Output = Self;
fn shr(self, rhs: Self) -> Self {
self >> usize::from(rhs)
}
}
impl Shr<&Self> for PositiveInt {
type Output = Self;
fn shr(self, rhs: &Self) -> Self {
self >> *rhs
}
}
impl Shr<PositiveInt> for &PositiveInt {
type Output = PositiveInt;
fn shr(self, rhs: PositiveInt) -> PositiveInt {
*self >> rhs
}
}
#[allow(clippy::use_self)]
impl Shr<&PositiveInt> for &PositiveInt {
type Output = PositiveInt;
fn shr(self, rhs: &PositiveInt) -> PositiveInt {
*self >> *rhs
}
}
macro_rules! shr_with_int {
( $( $int:ty ),* ) => { $(
impl Shr<$int> for PositiveInt {
type Output = Self;
#[allow(
trivial_numeric_casts,
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap
)]
fn shr(self, mut rhs: $int) -> Self {
if cfg!(debug_assertions) {
assert_eq!(
rhs.div_euclid(Self::EFFECTIVE_BITS as $int), 0,
"Attempted to shift right with overflow"
);
} else {
rhs = rhs.rem_euclid(Self::EFFECTIVE_BITS as $int);
}
Self((self.0 >> rhs) & Self::MAX.0)
}
}
impl Shr<&$int> for PositiveInt {
type Output = Self;
fn shr(self, rhs: &$int) -> Self {
self >> *rhs
}
}
impl Shr<$int> for &PositiveInt {
type Output = PositiveInt;
fn shr(self, rhs: $int) -> PositiveInt {
*self >> rhs
}
}
impl Shr<&$int> for &PositiveInt {
type Output = PositiveInt;
fn shr(self, rhs: &$int) -> PositiveInt {
*self >> *rhs
}
}
)* };
}
shr_with_int!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
impl<Rhs> ShrAssign<Rhs> for PositiveInt
where
Self: Shr<Rhs, Output = Self>,
{
fn shr_assign(&mut self, rhs: Rhs) {
*self = *self >> rhs
}
}
impl<B: Borrow<Self>> Sub<B> for PositiveInt {
type Output = Self;
fn sub(self, rhs: B) -> Self {
if cfg!(debug_assertions) {
self.checked_sub(*rhs.borrow())
.expect("Attempted to subtract with overflow")
} else {
self.wrapping_sub(*rhs.borrow())
}
}
}
impl Sub<isize> for PositiveInt {
type Output = Self;
fn sub(self, rhs: isize) -> Self {
if cfg!(debug_assertions) {
rhs.checked_neg()
.and_then(|minus_rhs| self.checked_add_signed(minus_rhs))
.expect("Attempted to subtract with overflow")
} else if rhs != isize::MIN {
self.wrapping_add_signed(-rhs)
} else {
self
}
}
}
impl Sub<&isize> for PositiveInt {
type Output = Self;
fn sub(self, rhs: &isize) -> Self {
self - (*rhs)
}
}
impl<B: Borrow<PositiveInt>> Sub<B> for &PositiveInt {
type Output = PositiveInt;
fn sub(self, rhs: B) -> PositiveInt {
*self - rhs
}
}
impl Sub<isize> for &PositiveInt {
type Output = PositiveInt;
fn sub(self, rhs: isize) -> PositiveInt {
*self - rhs
}
}
impl Sub<&isize> for &PositiveInt {
type Output = PositiveInt;
fn sub(self, rhs: &isize) -> PositiveInt {
*self - (*rhs)
}
}
impl<Rhs> SubAssign<Rhs> for PositiveInt
where
Self: Sub<Rhs, Output = Self>,
{
fn sub_assign(&mut self, rhs: Rhs) {
*self = *self - rhs
}
}
impl<B: Borrow<Self>> Sum<B> for PositiveInt {
fn sum<I: Iterator<Item = B>>(iter: I) -> Self {
iter.fold(Self::ZERO, |acc, contrib| acc + contrib.borrow())
}
}
impl TryFrom<usize> for PositiveInt {
type Error = TryFromIntError;
fn try_from(value: usize) -> Result<Self, TryFromIntError> {
Self::try_from_c_int(value.try_into()?)
}
}
macro_rules! try_into {
( $( $int:ty ),* ) => { $(
impl TryFrom<PositiveInt> for $int {
type Error = TryFromIntError;
#[allow(clippy::needless_question_mark)]
fn try_from(value: PositiveInt) -> Result<Self, TryFromIntError> {
Ok(value.0.try_into()?)
}
}
)* };
}
try_into!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
struct PositiveIntRangeIter {
start: PositiveInt,
end: PositiveInt,
}
impl DoubleEndedIterator for PositiveIntRangeIter {
fn next_back(&mut self) -> Option<Self::Item> {
self.nth_back(0)
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
if self.len() > n {
self.end -= PositiveInt::try_from(n + 1).expect(
"Cannot happen: len > n implies len >= n+1 \
and len < PositiveInt::MAX by construction, \
therefore n+1 < PositiveInt::MAX",
);
Some(self.end)
} else {
self.end = self.start;
None
}
}
}
impl ExactSizeIterator for PositiveIntRangeIter {
fn len(&self) -> usize {
usize::from(self.end).saturating_sub(usize::from(self.start))
}
}
impl FusedIterator for PositiveIntRangeIter {}
impl Iterator for PositiveIntRangeIter {
type Item = PositiveInt;
fn next(&mut self) -> Option<PositiveInt> {
self.nth(0)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
fn count(self) -> usize {
self.len()
}
fn last(self) -> Option<PositiveInt> {
(self.len() != 0).then(|| self.end - 1)
}
fn nth(&mut self, n: usize) -> Option<PositiveInt> {
if self.len() > n {
self.start += PositiveInt::try_from(n + 1).expect(
"Cannot happen: len > n implies len >= n+1 \
and len < PositiveInt::MAX by construction, \
therefore n+1 < PositiveInt::MAX",
);
Some(self.start - 1)
} else {
self.start = self.end;
None
}
}
}
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
struct PositiveIntRangeInclusiveIter {
start: PositiveInt,
end: PositiveInt,
exhausted: bool,
}
impl PositiveIntRangeInclusiveIter {
fn next_unchecked(&mut self) -> PositiveInt {
debug_assert!(
!self.exhausted,
"Should not be called on an exhausted iterator"
);
if self.start == self.end {
self.exhausted = true;
self.start
} else {
self.start += 1;
self.start - 1
}
}
fn next_back_unchecked(&mut self) -> PositiveInt {
debug_assert!(
!self.exhausted,
"Should not be called on an exhausted iterator"
);
if self.start == self.end {
self.exhausted = true;
self.end
} else {
self.end -= 1;
self.end + 1
}
}
}
impl DoubleEndedIterator for PositiveIntRangeInclusiveIter {
fn next_back(&mut self) -> Option<Self::Item> {
(!self.exhausted).then(|| self.next_back_unchecked())
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
if self.len() > n {
self.end -= PositiveInt::try_from(n).expect(
"Cannot happen: len > n and len <= PositiveInt::MAX, \
therefore n < PositiveInt::MAX",
);
Some(self.next_back_unchecked())
} else {
self.end = self.start;
self.exhausted = true;
None
}
}
}
impl ExactSizeIterator for PositiveIntRangeInclusiveIter {
fn len(&self) -> usize {
if self.exhausted {
0
} else {
usize::from(self.end) - usize::from(self.start) + 1
}
}
}
impl FusedIterator for PositiveIntRangeInclusiveIter {}
impl Iterator for PositiveIntRangeInclusiveIter {
type Item = PositiveInt;
fn next(&mut self) -> Option<PositiveInt> {
(!self.exhausted).then(|| self.next_unchecked())
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
fn count(self) -> usize {
self.len()
}
fn last(self) -> Option<PositiveInt> {
(!self.exhausted).then_some(self.end)
}
fn nth(&mut self, n: usize) -> Option<PositiveInt> {
if self.len() > n {
self.start += PositiveInt::try_from(n).expect(
"Cannot happen: len > n and len <= PositiveInt::MAX, \
therefore n < PositiveInt::MAX",
);
Some(self.next_unchecked())
} else {
self.start = self.end;
self.exhausted = true;
None
}
}
}
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
struct PositiveIntRangeFromIter(PositiveInt);
impl FusedIterator for PositiveIntRangeFromIter {}
impl Iterator for PositiveIntRangeFromIter {
type Item = PositiveInt;
#[inline]
fn next(&mut self) -> Option<PositiveInt> {
self.0 += 1;
Some(self.0 - 1)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::MAX, None)
}
fn count(self) -> usize {
panic!("Attempted to consume an iterator with infinite elements")
}
fn last(self) -> Option<PositiveInt> {
panic!("Attempted to consume an iterator with infinite elements")
}
#[inline]
fn nth(&mut self, n: usize) -> Option<PositiveInt> {
self.0 += PositiveInt::try_from(n).expect("Increment is out of range");
self.next()
}
}
#[allow(clippy::cognitive_complexity, clippy::op_ref, clippy::too_many_lines)]
#[cfg(test)]
mod tests {
use super::*;
use crate::{strategies::any_string, tests::assert_panics};
#[allow(unused)]
use similar_asserts::assert_eq;
use static_assertions::{assert_impl_all, assert_not_impl_any};
use std::{
collections::hash_map::DefaultHasher,
error::Error,
fmt::{Binary, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex},
hash::{Hash, Hasher},
io::{self, Read},
num::IntErrorKind,
ops::Deref,
panic::{RefUnwindSafe, UnwindSafe},
};
assert_impl_all!(PositiveInt:
Add<PositiveInt>, Add<&'static PositiveInt>,
Add<isize>, Add<&'static isize>,
AddAssign<PositiveInt>, AddAssign<&'static PositiveInt>,
AddAssign<isize>, AddAssign<&'static isize>,
Binary,
BitAnd<PositiveInt>, BitAnd<&'static PositiveInt>,
BitAndAssign<PositiveInt>, BitAndAssign<&'static PositiveInt>,
BitAnd<usize>, BitAnd<&'static usize>,
BitAndAssign<usize>, BitAndAssign<&'static usize>,
BitOr<PositiveInt>, BitOr<&'static PositiveInt>,
BitOrAssign<PositiveInt>, BitOrAssign<&'static PositiveInt>,
BitOr<usize>, BitOr<&'static usize>,
BitOrAssign<usize>, BitOrAssign<&'static usize>,
BitXor<PositiveInt>, BitXor<&'static PositiveInt>,
BitXorAssign<PositiveInt>, BitXorAssign<&'static PositiveInt>,
BitXor<usize>, BitXor<&'static usize>,
BitXorAssign<usize>, BitXorAssign<&'static usize>,
Copy, Debug, Default, Display,
Div<PositiveInt>, Div<&'static PositiveInt>,
DivAssign<PositiveInt>, DivAssign<&'static PositiveInt>,
Div<usize>, Div<&'static usize>,
DivAssign<usize>, DivAssign<&'static usize>,
FromStr, Hash, Into<isize>, Into<usize>, LowerExp, LowerHex,
Mul<PositiveInt>, Mul<&'static PositiveInt>,
MulAssign<PositiveInt>, MulAssign<&'static PositiveInt>,
Mul<usize>, Mul<&'static usize>,
MulAssign<usize>, MulAssign<&'static usize>,
Not, Octal, Ord, PartialEq<usize>, PartialOrd<usize>, Product, Sized,
Rem<PositiveInt>, Rem<&'static PositiveInt>,
RemAssign<PositiveInt>, RemAssign<&'static PositiveInt>,
Rem<usize>, Rem<&'static usize>,
RemAssign<usize>, RemAssign<&'static usize>,
Shl<PositiveInt>, Shl<&'static PositiveInt>,
ShlAssign<PositiveInt>, ShlAssign<&'static PositiveInt>,
Shl<i8>, Shl<&'static i8>,
ShlAssign<i8>, ShlAssign<&'static i8>,
Shl<u8>, Shl<&'static u8>,
ShlAssign<u8>, ShlAssign<&'static u8>,
Shl<i16>, Shl<&'static i16>,
ShlAssign<i16>, ShlAssign<&'static i16>,
Shl<u16>, Shl<&'static u16>,
ShlAssign<u16>, ShlAssign<&'static u16>,
Shl<i32>, Shl<&'static i32>,
ShlAssign<i32>, ShlAssign<&'static i32>,
Shl<u32>, Shl<&'static u32>,
ShlAssign<u32>, ShlAssign<&'static u32>,
Shl<i64>, Shl<&'static i64>,
ShlAssign<i64>, ShlAssign<&'static i64>,
Shl<u64>, Shl<&'static u64>,
ShlAssign<u64>, ShlAssign<&'static u64>,
Shl<i128>, Shl<&'static i128>,
ShlAssign<i128>, ShlAssign<&'static i128>,
Shl<u128>, Shl<&'static u128>,
ShlAssign<u128>, ShlAssign<&'static u128>,
Shl<isize>, Shl<&'static isize>,
ShlAssign<isize>, ShlAssign<&'static isize>,
Shl<usize>, Shl<&'static usize>,
ShlAssign<usize>, ShlAssign<&'static usize>,
Shr<PositiveInt>, Shr<&'static PositiveInt>,
ShrAssign<PositiveInt>, ShrAssign<&'static PositiveInt>,
Shr<i8>, Shr<&'static i8>,
ShrAssign<i8>, ShrAssign<&'static i8>,
Shr<u8>, Shr<&'static u8>,
ShrAssign<u8>, ShrAssign<&'static u8>,
Shr<i16>, Shr<&'static i16>,
ShrAssign<i16>, ShrAssign<&'static i16>,
Shr<u16>, Shr<&'static u16>,
ShrAssign<u16>, ShrAssign<&'static u16>,
Shr<i32>, Shr<&'static i32>,
ShrAssign<i32>, ShrAssign<&'static i32>,
Shr<u32>, Shr<&'static u32>,
ShrAssign<u32>, ShrAssign<&'static u32>,
Shr<i64>, Shr<&'static i64>,
ShrAssign<i64>, ShrAssign<&'static i64>,
Shr<u64>, Shr<&'static u64>,
ShrAssign<u64>, ShrAssign<&'static u64>,
Shr<i128>, Shr<&'static i128>,
ShrAssign<i128>, ShrAssign<&'static i128>,
Shr<u128>, Shr<&'static u128>,
ShrAssign<u128>, ShrAssign<&'static u128>,
Shr<isize>, Shr<&'static isize>,
ShrAssign<isize>, ShrAssign<&'static isize>,
Shr<usize>, Shr<&'static usize>,
ShrAssign<usize>, ShrAssign<&'static usize>,
Sub<PositiveInt>, Sub<&'static PositiveInt>,
SubAssign<PositiveInt>, SubAssign<&'static PositiveInt>,
Sub<isize>, Sub<&'static isize>,
SubAssign<isize>, SubAssign<&'static isize>,
Sum<PositiveInt>, Sum<&'static PositiveInt>,
Sync, TryFrom<usize>,
TryInto<i8>, TryInto<u8>,
TryInto<i16>, TryInto<u16>,
TryInto<i32>, TryInto<u32>,
TryInto<i64>, TryInto<u64>,
TryInto<i128>, TryInto<u128>,
Unpin, UnwindSafe, UpperExp, UpperHex
);
assert_not_impl_any!(PositiveInt:
Deref, Drop, Error, IntoIterator, Pointer, Read, fmt::Write, io::Write
);
assert_impl_all!(&PositiveInt:
Add<PositiveInt>, Add<&'static PositiveInt>,
Add<isize>, Add<&'static isize>,
BitAnd<PositiveInt>, BitAnd<&'static PositiveInt>,
BitAnd<usize>, BitAnd<&'static usize>,
BitOr<PositiveInt>, BitOr<&'static PositiveInt>,
BitOr<usize>, BitOr<&'static usize>,
BitXor<PositiveInt>, BitXor<&'static PositiveInt>,
BitXor<usize>, BitXor<&'static usize>,
Div<PositiveInt>, Div<&'static PositiveInt>,
Div<usize>, Div<&'static usize>,
Mul<PositiveInt>, Mul<&'static PositiveInt>,
Mul<usize>, Mul<&'static usize>,
Rem<PositiveInt>, Rem<&'static PositiveInt>,
Rem<usize>, Rem<&'static usize>,
Shl<PositiveInt>, Shl<&'static PositiveInt>,
Shl<i8>, Shl<&'static i8>,
Shl<u8>, Shl<&'static u8>,
Shl<i16>, Shl<&'static i16>,
Shl<u16>, Shl<&'static u16>,
Shl<i32>, Shl<&'static i32>,
Shl<u32>, Shl<&'static u32>,
Shl<i64>, Shl<&'static i64>,
Shl<u64>, Shl<&'static u64>,
Shl<i128>, Shl<&'static i128>,
Shl<u128>, Shl<&'static u128>,
Shl<isize>, Shl<&'static isize>,
Shl<usize>, Shl<&'static usize>,
Shr<PositiveInt>, Shr<&'static PositiveInt>,
Shr<i8>, Shr<&'static i8>,
Shr<u8>, Shr<&'static u8>,
Shr<i16>, Shr<&'static i16>,
Shr<u16>, Shr<&'static u16>,
Shr<i32>, Shr<&'static i32>,
Shr<u32>, Shr<&'static u32>,
Shr<i64>, Shr<&'static i64>,
Shr<u64>, Shr<&'static u64>,
Shr<i128>, Shr<&'static i128>,
Shr<u128>, Shr<&'static u128>,
Shr<isize>, Shr<&'static isize>,
Shr<usize>, Shr<&'static usize>,
Shr<PositiveInt>, Shr<&'static PositiveInt>,
Sub<PositiveInt>, Sub<&'static PositiveInt>,
Sub<isize>, Sub<&'static isize>,
);
const UNUSED_BITS: c_uint = 1 << PositiveInt::EFFECTIVE_BITS;
const NUM_UNUSED_BITS: u32 = UNUSED_BITS.count_ones();
const INFINITE_ITERS: usize = 10;
#[track_caller]
fn assert_debug_panics<R: Debug + Eq>(
f: impl FnOnce() -> R + UnwindSafe,
release_result: R,
) -> Result<(), TestCaseError> {
if cfg!(debug_assertions) {
assert_panics(f)?;
} else {
prop_assert_eq!(
f(), release_result,
"Operation does not produce the expected result in release builds"
);
}
Ok(())
}
#[track_caller]
fn compare_iters_infinite<Actual, Expected>(
mut actual: Actual,
mut next_actual: impl FnMut(&mut Actual) -> Option<PositiveInt>,
mut expected: Expected,
mut next_expected: impl FnMut(&mut Expected) -> Option<c_uint>,
) -> Result<(), TestCaseError> {
for _ in 0..INFINITE_ITERS {
match (next_actual(&mut actual), next_expected(&mut expected)) {
(Some(actual), Some(expected)) => prop_assert_eq!(actual, PositiveInt(expected)),
(None, None) => prop_assert!(false, "Infinite iterators shouldn't end"),
(Some(_), None) | (None, Some(_)) => prop_assert!(false, "Length shouldn't differ"),
}
}
Ok(())
}
#[track_caller]
fn compare_iters_finite<Actual, Expected>(
mut actual: Actual,
mut next_actual: impl FnMut(&mut Actual) -> Option<PositiveInt>,
mut expected: Expected,
mut next_expected: impl FnMut(&mut Expected) -> Option<c_uint>,
) -> Result<(), TestCaseError> {
loop {
match (next_actual(&mut actual), next_expected(&mut expected)) {
(Some(actual), Some(expected)) => prop_assert_eq!(actual, PositiveInt(expected)),
(None, None) => break,
(Some(_), None) | (None, Some(_)) => prop_assert!(false, "Length shouldn't differ"),
}
}
Ok(())
}
fn hash(x: impl Hash) -> u64 {
let mut hasher = DefaultHasher::new();
x.hash(&mut hasher);
hasher.finish()
}
#[test]
fn constants() -> Result<(), TestCaseError> {
prop_assert_eq!(PositiveInt::MIN, PositiveInt::ZERO);
prop_assert_eq!(PositiveInt::ZERO, 0);
prop_assert_eq!(PositiveInt::ONE, 1);
prop_assert_eq!(
PositiveInt::MAX,
(1usize << PositiveInt::EFFECTIVE_BITS) - 1
);
prop_assert_eq!(PositiveInt::default(), 0);
let zero = PositiveInt::ZERO;
prop_assert_eq!(zero.trailing_zeros(), PositiveInt::EFFECTIVE_BITS);
prop_assert_eq!(zero.checked_neg(), Some(zero));
assert_panics(|| zero.ilog2())?;
assert_panics(|| zero.ilog10())?;
prop_assert_eq!(zero.checked_ilog2(), None);
prop_assert_eq!(zero.checked_ilog10(), None);
prop_assert_eq!(zero.wrapping_neg(), zero);
prop_assert_eq!(zero.checked_neg(), Some(zero));
prop_assert_eq!(zero.overflowing_neg(), (zero, false));
Ok(())
}
proptest! {
#[test]
fn unary_int(int: PositiveInt) {
let set_unused = int.0 | UNUSED_BITS;
prop_assert_eq!(int.count_ones(), int.0.count_ones());
prop_assert_eq!(int.count_zeros(), set_unused.count_zeros());
prop_assert_eq!(int.leading_zeros(), int.0.leading_zeros() - NUM_UNUSED_BITS);
prop_assert_eq!(int.trailing_zeros(), set_unused.trailing_zeros());
prop_assert_eq!(
int.leading_ones(),
set_unused.leading_ones() - NUM_UNUSED_BITS
);
prop_assert_eq!(int.trailing_ones(), int.0.trailing_ones());
prop_assert_eq!(
int.reverse_bits().0,
int.0.reverse_bits() >> NUM_UNUSED_BITS
);
prop_assert_eq!(int.is_power_of_two(), int.0.is_power_of_two());
prop_assert_eq!((!int).0, !(int.0 | set_unused));
prop_assert_eq!((!&int).0, !(int.0 | set_unused));
prop_assert_eq!(isize::from(int), isize::try_from(int.0).unwrap());
prop_assert_eq!(usize::from(int), usize::try_from(int.0).unwrap());
#[allow(clippy::unnecessary_fallible_conversions, clippy::useless_conversion)]
{
prop_assert_eq!(i8::try_from(int).ok(), i8::try_from(int.0).ok());
prop_assert_eq!(u8::try_from(int).ok(), u8::try_from(int.0).ok());
prop_assert_eq!(i16::try_from(int).ok(), i16::try_from(int.0).ok());
prop_assert_eq!(u16::try_from(int).ok(), u16::try_from(int.0).ok());
prop_assert_eq!(i32::try_from(int).ok(), i32::try_from(int.0).ok());
prop_assert_eq!(u32::try_from(int).ok(), u32::try_from(int.0).ok());
prop_assert_eq!(i64::try_from(int).ok(), i64::try_from(int.0).ok());
prop_assert_eq!(u64::try_from(int).ok(), u64::try_from(int.0).ok());
prop_assert_eq!(i128::try_from(int).ok(), i128::try_from(int.0).ok());
prop_assert_eq!(u128::try_from(int).ok(), u128::try_from(int.0).ok());
}
prop_assert_eq!(format!("{int:?}"), format!("PositiveInt({:})", int.0));
prop_assert_eq!(format!("{int}"), format!("{:}", int.0));
prop_assert_eq!(format!("{int:b}"), format!("{:b}", int.0));
prop_assert_eq!(format!("{int:e}"), format!("{:e}", int.0));
prop_assert_eq!(format!("{int:o}"), format!("{:o}", int.0));
prop_assert_eq!(format!("{int:x}"), format!("{:x}", int.0));
prop_assert_eq!(format!("{int:E}"), format!("{:E}", int.0));
prop_assert_eq!(format!("{int:X}"), format!("{:X}", int.0));
prop_assert_eq!(hash(int), hash(int.0));
assert_debug_panics(|| int - isize::MIN, int)?;
{
let zero = PositiveInt::ZERO;
prop_assert_eq!(int.checked_div(zero), None);
assert_panics(|| int.overflowing_div(zero))?;
assert_panics(|| int.saturating_div(zero))?;
assert_panics(|| int.wrapping_div(zero))?;
assert_panics(|| int / zero)?;
assert_panics(|| &int / zero)?;
assert_panics(|| int / &zero)?;
assert_panics(|| &int / &zero)?;
assert_panics(|| {
let mut tmp = int;
tmp /= zero
})?;
assert_panics(|| {
let mut tmp = int;
tmp /= &zero
})?;
prop_assert_eq!(int.checked_div_euclid(zero), None);
assert_panics(|| int.overflowing_div_euclid(zero))?;
assert_panics(|| int.wrapping_div_euclid(zero))?;
prop_assert_eq!(int.checked_rem(zero), None);
assert_panics(|| int.overflowing_rem(zero))?;
assert_panics(|| int.wrapping_rem(zero))?;
assert_panics(|| int % zero)?;
assert_panics(|| &int % zero)?;
assert_panics(|| int % &zero)?;
assert_panics(|| &int % &zero)?;
assert_panics(|| {
let mut tmp = int;
tmp %= zero
})?;
assert_panics(|| {
let mut tmp = int;
tmp %= &zero
})?;
prop_assert_eq!(int.checked_rem_euclid(zero), None);
assert_panics(|| int.overflowing_rem_euclid(zero))?;
assert_panics(|| int.wrapping_rem_euclid(zero))?;
assert_panics(|| int / 0)?;
assert_panics(|| &int / 0)?;
assert_panics(|| int / &0)?;
assert_panics(|| &int / &0)?;
assert_panics(|| {
let mut tmp = int;
tmp /= 0
})?;
assert_panics(|| {
let mut tmp = int;
tmp /= &0
})?;
assert_panics(|| int % 0)?;
assert_panics(|| &int % 0)?;
assert_panics(|| int % &0)?;
assert_panics(|| &int % &0)?;
assert_panics(|| {
let mut tmp = int;
tmp %= 0
})?;
assert_panics(|| {
let mut tmp = int;
tmp %= &0
})?;
}
{
let base_above2 = int.saturating_add(PositiveInt(2));
assert_panics(|| PositiveInt::ZERO.ilog(base_above2))?;
prop_assert_eq!(PositiveInt::ZERO.checked_ilog(base_above2), None);
let base_below2 = int % 2;
assert_panics(|| PositiveInt::ZERO.ilog(base_below2))?;
prop_assert_eq!(PositiveInt::ZERO.checked_ilog(base_below2), None);
}
if int != 0 {
let expected_log2 = int.0.ilog2();
let expected_log10 = int.0.ilog10();
prop_assert_eq!(int.ilog2(), expected_log2);
prop_assert_eq!(int.ilog10(), expected_log10);
prop_assert_eq!(int.checked_ilog2(), Some(expected_log2));
prop_assert_eq!(int.checked_ilog10(), Some(expected_log10));
let wrapping_neg = (!int).wrapping_add(PositiveInt(1));
prop_assert_eq!(int.checked_neg(), None);
prop_assert_eq!(int.wrapping_neg(), wrapping_neg);
prop_assert_eq!(int.overflowing_neg(), (wrapping_neg, true));
}
let last_pow2 = PositiveInt::ONE << (PositiveInt::EFFECTIVE_BITS - 1);
let has_next_pow2 = int & (!last_pow2);
let next_pow2 = PositiveInt(has_next_pow2.0.next_power_of_two());
prop_assert_eq!(has_next_pow2.next_power_of_two(), next_pow2);
prop_assert_eq!(has_next_pow2.checked_next_power_of_two(), Some(next_pow2));
let mut no_next_pow2 = int | last_pow2;
if no_next_pow2 == last_pow2 {
no_next_pow2 += 1;
}
assert_debug_panics(|| no_next_pow2.next_power_of_two(), PositiveInt::ZERO)?;
prop_assert_eq!(no_next_pow2.checked_next_power_of_two(), None);
let actual = PositiveInt::iter_range_from(int);
let expected = (int.0)..;
prop_assert_eq!(actual.size_hint(), expected.size_hint());
assert_panics(|| actual.clone().count())?;
assert_panics(|| actual.clone().last())?;
compare_iters_infinite(actual, Iterator::next, expected, Iterator::next)?;
}
#[test]
fn unary_usize(x: usize) {
prop_assert_eq!(
PositiveInt::try_from(x),
c_int::try_from(x).map(|i| PositiveInt(i.try_into().unwrap()))
);
let zero = PositiveInt::ZERO;
prop_assert_eq!(zero * x, zero);
prop_assert_eq!(x * zero, zero);
prop_assert_eq!(&zero * x, zero);
prop_assert_eq!(x * (&zero), zero);
prop_assert_eq!(zero * (&x), zero);
prop_assert_eq!(&x * zero, zero);
prop_assert_eq!(&zero * (&x), zero);
prop_assert_eq!(&x * (&zero), zero);
let mut tmp = zero;
tmp *= x;
prop_assert_eq!(tmp, zero);
tmp = zero;
tmp *= &x;
prop_assert_eq!(tmp, zero);
}
}
fn test_from_str_radix(
src: &str,
radix: u32,
parse: impl FnOnce() -> Result<PositiveInt, ParseIntError> + UnwindSafe,
) -> Result<(), TestCaseError> {
if !(2..=36).contains(&radix) {
return assert_panics(parse);
}
let result = parse();
let Ok(as_usize) = usize::from_str_radix(src, radix) else {
prop_assert!(result.is_err());
return Ok(());
};
const MAX: usize = PositiveInt::MAX.0 as usize;
match as_usize {
0..=MAX => {
prop_assert_eq!(result.unwrap(), as_usize);
}
_overflow => {
prop_assert!(matches!(result, Err(e) if e.kind() == &IntErrorKind::PosOverflow));
}
}
Ok(())
}
proptest! {
#[test]
fn from_str(src in any_string()) {
test_from_str_radix(&src, 10, || PositiveInt::from_str(&src))?;
}
#[test]
fn from_str_radix(src in any_string(), radix: u32) {
let radix = radix % 37;
test_from_str_radix(&src, radix, || PositiveInt::from_str_radix(&src, radix))?;
}
}
fn test_faillible<Rhs: Copy + RefUnwindSafe, const LEN: usize>(
int: PositiveInt,
rhs: Rhs,
checked_op: impl FnOnce(PositiveInt, Rhs) -> Option<PositiveInt>,
faillible_ops: [Box<dyn Fn(PositiveInt, Rhs) -> PositiveInt + RefUnwindSafe>; LEN],
release_result: Option<PositiveInt>,
) -> Result<Option<PositiveInt>, TestCaseError> {
let checked_result = checked_op(int, rhs);
match (checked_result, release_result) {
(Some(result), _) => {
for faillible_op in faillible_ops {
prop_assert_eq!(faillible_op(int, rhs), result);
}
}
(None, Some(release_result)) => {
for faillible_op in faillible_ops {
assert_debug_panics(|| faillible_op(int, rhs), release_result)?;
}
}
(None, None) => {
for faillible_op in faillible_ops {
assert_panics(|| faillible_op(int, rhs))?;
}
}
}
Ok(checked_result)
}
fn test_overflowing<Rhs: Copy + RefUnwindSafe, const LEN: usize>(
int: PositiveInt,
rhs: Rhs,
checked_op: impl Fn(PositiveInt, Rhs) -> Option<PositiveInt>,
overflowing_op: impl Fn(PositiveInt, Rhs) -> (PositiveInt, bool),
wrapping_op: impl Fn(PositiveInt, Rhs) -> PositiveInt,
faillible_ops: [Box<dyn Fn(PositiveInt, Rhs) -> PositiveInt + RefUnwindSafe>; LEN],
) -> Result<(PositiveInt, bool), TestCaseError> {
let overflowing_result = overflowing_op(int, rhs);
let (wrapped_result, overflow) = overflowing_result;
prop_assert_eq!(wrapping_op(int, rhs), wrapped_result);
let checked_result =
test_faillible(int, rhs, checked_op, faillible_ops, Some(wrapped_result))?;
if overflow {
prop_assert_eq!(checked_result, None);
} else {
prop_assert_eq!(checked_result, Some(wrapped_result));
}
Ok(overflowing_result)
}
fn predict_overflowing_result<IntRhs, UsizeRhs: From<IntRhs>>(
i1: PositiveInt,
i2: IntRhs,
usize_op: fn(usize, UsizeRhs) -> (usize, bool),
) -> (PositiveInt, bool) {
let used_bits_usize = (1usize << PositiveInt::EFFECTIVE_BITS) - 1;
let max_usize = usize::from(PositiveInt::MAX);
let (wrapped_usize, overflow_usize) = usize_op(usize::from(i1), UsizeRhs::from(i2));
let expected_wrapped = PositiveInt::try_from(wrapped_usize & used_bits_usize).unwrap();
let expected_overflow = overflow_usize || (wrapped_usize > max_usize);
(expected_wrapped, expected_overflow)
}
proptest! {
#[test]
fn int_int(i1: PositiveInt, i2: PositiveInt) {
prop_assert_eq!(i1 == i2, i1.0 == i2.0);
prop_assert_eq!(i1.cmp(&i2), i1.0.cmp(&i2.0));
let expected = PositiveInt(i1.0 & i2.0);
prop_assert_eq!(i1 & i2, expected);
prop_assert_eq!(&i1 & i2, expected);
prop_assert_eq!(i1 & (&i2), expected);
prop_assert_eq!(&i1 & (&i2), expected);
let mut tmp = i1;
tmp &= i2;
prop_assert_eq!(tmp, expected);
tmp = i1;
tmp &= &i2;
prop_assert_eq!(tmp, expected);
let expected = PositiveInt(i1.0 | i2.0);
prop_assert_eq!(i1 | i2, expected);
prop_assert_eq!(&i1 | i2, expected);
prop_assert_eq!(i1 | (&i2), expected);
prop_assert_eq!(&i1 | (&i2), expected);
tmp = i1;
tmp |= i2;
prop_assert_eq!(tmp, expected);
tmp = i1;
tmp |= &i2;
prop_assert_eq!(tmp, expected);
let expected = PositiveInt(i1.0 ^ i2.0);
prop_assert_eq!(i1 ^ i2, expected);
prop_assert_eq!(&i1 ^ i2, expected);
prop_assert_eq!(i1 ^ (&i2), expected);
prop_assert_eq!(&i1 ^ (&i2), expected);
let mut tmp = i1;
tmp ^= i2;
prop_assert_eq!(tmp, expected);
tmp = i1;
tmp ^= &i2;
prop_assert_eq!(tmp, expected);
let (expected_wrapped, expected_overflow) =
predict_overflowing_result(i1, i2, usize::overflowing_add);
let (wrapped, overflow) = test_overflowing(
i1,
i2,
PositiveInt::checked_add,
PositiveInt::overflowing_add,
PositiveInt::wrapping_add,
[
Box::new(|i1, i2| i1 + i2),
Box::new(|i1, i2| &i1 + i2),
Box::new(|i1, i2| i1 + &i2),
Box::new(|i1, i2| &i1 + &i2),
Box::new(|mut i1, i2| {
i1 += i2;
i1
}),
Box::new(|mut i1, i2| {
i1 += &i2;
i1
}),
],
)?;
prop_assert_eq!(wrapped, expected_wrapped);
prop_assert_eq!(overflow, expected_overflow);
if overflow {
prop_assert_eq!(i1.saturating_add(i2), PositiveInt::MAX);
} else {
prop_assert_eq!(i1.saturating_add(i2), wrapped);
}
let (expected_wrapped, expected_overflow) =
predict_overflowing_result(i1, i2, usize::overflowing_sub);
let (wrapped, overflow) = test_overflowing(
i1,
i2,
PositiveInt::checked_sub,
PositiveInt::overflowing_sub,
PositiveInt::wrapping_sub,
[
Box::new(|i1, i2| i1 - i2),
Box::new(|i1, i2| &i1 - i2),
Box::new(|i1, i2| i1 - &i2),
Box::new(|i1, i2| &i1 - &i2),
Box::new(|mut i1, i2| {
i1 -= i2;
i1
}),
Box::new(|mut i1, i2| {
i1 -= &i2;
i1
}),
],
)?;
prop_assert_eq!(wrapped, expected_wrapped);
prop_assert_eq!(overflow, expected_overflow);
if overflow {
prop_assert_eq!(i1.saturating_sub(i2), PositiveInt::MIN);
} else {
prop_assert_eq!(i1.saturating_sub(i2), wrapped);
}
prop_assert_eq!(i1.abs_diff(i2), PositiveInt(i1.0.abs_diff(i2.0)));
let (expected_wrapped, expected_overflow) =
predict_overflowing_result(i1, i2, usize::overflowing_mul);
let (wrapped, overflow) = test_overflowing(
i1,
i2,
PositiveInt::checked_mul,
PositiveInt::overflowing_mul,
PositiveInt::wrapping_mul,
[
Box::new(|i1, i2| i1 * i2),
Box::new(|i1, i2| &i1 * i2),
Box::new(|i1, i2| i1 * &i2),
Box::new(|i1, i2| &i1 * &i2),
Box::new(|mut i1, i2| {
i1 *= i2;
i1
}),
Box::new(|mut i1, i2| {
i1 *= &i2;
i1
}),
],
)?;
prop_assert_eq!(wrapped, expected_wrapped);
prop_assert_eq!(overflow, expected_overflow);
if overflow {
prop_assert_eq!(i1.saturating_mul(i2), PositiveInt::MAX);
} else {
prop_assert_eq!(i1.saturating_mul(i2), wrapped);
}
{
let nonzero = i2.saturating_add(PositiveInt::ONE);
let (expected_wrapped, expected_overflow) =
predict_overflowing_result(i1, nonzero, usize::overflowing_div);
let res1 = test_overflowing(
i1,
nonzero,
PositiveInt::checked_div,
PositiveInt::overflowing_div,
PositiveInt::wrapping_div,
[
Box::new(|i1, nonzero| i1 / nonzero),
Box::new(|i1, nonzero| &i1 / nonzero),
Box::new(|i1, nonzero| i1 / &nonzero),
Box::new(|i1, nonzero| &i1 / &nonzero),
Box::new(|mut i1, nonzero| {
i1 /= nonzero;
i1
}),
Box::new(|mut i1, nonzero| {
i1 /= &nonzero;
i1
}),
],
)?;
let res2 = test_overflowing(
i1,
nonzero,
PositiveInt::checked_div_euclid,
PositiveInt::overflowing_div_euclid,
PositiveInt::wrapping_div_euclid,
[Box::new(PositiveInt::div_euclid)],
)?;
prop_assert_eq!(i1.saturating_div(nonzero), expected_wrapped);
for (wrapped, overflow) in [res1, res2] {
prop_assert_eq!(wrapped, expected_wrapped);
prop_assert_eq!(overflow, expected_overflow);
}
let (expected_wrapped, expected_overflow) =
predict_overflowing_result(i1, nonzero, usize::overflowing_rem);
let res1 = test_overflowing(
i1,
nonzero,
PositiveInt::checked_rem,
PositiveInt::overflowing_rem,
PositiveInt::wrapping_rem,
[
Box::new(|i1, nonzero| i1 % nonzero),
Box::new(|i1, nonzero| &i1 % nonzero),
Box::new(|i1, nonzero| i1 % &nonzero),
Box::new(|i1, nonzero| &i1 % &nonzero),
Box::new(|mut i1, nonzero| {
i1 %= nonzero;
i1
}),
Box::new(|mut i1, nonzero| {
i1 %= &nonzero;
i1
}),
],
)?;
let res2 = test_overflowing(
i1,
nonzero,
PositiveInt::checked_rem_euclid,
PositiveInt::overflowing_rem_euclid,
PositiveInt::wrapping_rem_euclid,
[Box::new(PositiveInt::rem_euclid)],
)?;
for (wrapped, overflow) in [res1, res2] {
prop_assert_eq!(wrapped, expected_wrapped);
prop_assert_eq!(overflow, expected_overflow);
}
}
{
let number_above0 = i1.saturating_add(PositiveInt::ONE);
let base_above2 = i2.saturating_add(PositiveInt(2));
let expected = number_above0.0.ilog(base_above2.0);
prop_assert_eq!(number_above0.ilog(base_above2), expected);
prop_assert_eq!(number_above0.checked_ilog(base_above2), Some(expected));
let base_below2 = i2 % 2;
assert_panics(|| PositiveInt::ZERO.ilog(base_below2))?;
prop_assert_eq!(PositiveInt::ZERO.checked_ilog(base_below2), None);
}
#[allow(trivial_numeric_casts)]
let effective_bits = PositiveInt(PositiveInt::EFFECTIVE_BITS as c_uint);
let wrapped_shift = i2 % effective_bits;
let wrapped_result = PositiveInt((i1.0 << wrapped_shift.0) & PositiveInt::MAX.0);
prop_assert_eq!(i1 << wrapped_shift, wrapped_result);
prop_assert_eq!(&i1 << wrapped_shift, wrapped_result);
prop_assert_eq!(i1 << (&wrapped_shift), wrapped_result);
prop_assert_eq!(&i1 << (&wrapped_shift), wrapped_result);
tmp = i1;
tmp <<= wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
tmp = i1;
tmp <<= &wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
let overflown_shift = i2.saturating_add(effective_bits);
assert_debug_panics(|| i1 << overflown_shift, wrapped_result)?;
assert_debug_panics(|| &i1 << overflown_shift, wrapped_result)?;
assert_debug_panics(|| i1 << (&overflown_shift), wrapped_result)?;
assert_debug_panics(|| &i1 << (&overflown_shift), wrapped_result)?;
assert_debug_panics(
|| {
let mut tmp = i1;
tmp <<= overflown_shift;
tmp
},
wrapped_result,
)?;
assert_debug_panics(
|| {
let mut tmp = i1;
tmp <<= &overflown_shift;
tmp
},
wrapped_result,
)?;
let wrapped_result = PositiveInt(i1.0 >> wrapped_shift.0);
prop_assert_eq!(i1 >> wrapped_shift, wrapped_result);
prop_assert_eq!(&i1 >> wrapped_shift, wrapped_result);
prop_assert_eq!(i1 >> (&wrapped_shift), wrapped_result);
prop_assert_eq!(&i1 >> (&wrapped_shift), wrapped_result);
tmp = i1;
tmp >>= wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
tmp = i1;
tmp >>= &wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
assert_debug_panics(|| i1 >> overflown_shift, wrapped_result)?;
assert_debug_panics(|| &i1 >> overflown_shift, wrapped_result)?;
assert_debug_panics(|| i1 >> (&overflown_shift), wrapped_result)?;
assert_debug_panics(|| &i1 >> (&overflown_shift), wrapped_result)?;
assert_debug_panics(
|| {
let mut tmp = i1;
tmp >>= overflown_shift;
tmp
},
wrapped_result,
)?;
assert_debug_panics(
|| {
let mut tmp = i1;
tmp >>= &overflown_shift;
tmp
},
wrapped_result,
)?;
}
}
fn int_range_bounds() -> impl Strategy<Value = [PositiveInt; 2]> {
let size_range = prop::collection::SizeRange::default();
let max_size = isize::try_from(size_range.end_excl()).unwrap();
any::<PositiveInt>()
.prop_flat_map(move |start| {
let end_uint = prop_oneof![
4 => start.0..start.saturating_add_signed(max_size).0,
1 => 0..start.0
];
(Just(start), end_uint)
})
.prop_map(|(start, end_uint)| {
[start, PositiveInt::const_try_from_c_uint(end_uint).unwrap()]
})
}
proptest! {
#[test]
fn int_range([start, end] in int_range_bounds()) {
let actual = PositiveInt::iter_range(start, end);
let expected = (start.0)..(end.0);
prop_assert_eq!(actual.len(), expected.len());
prop_assert_eq!(actual.size_hint(), expected.size_hint());
prop_assert_eq!(actual.clone().count(), expected.len());
prop_assert_eq!(
actual.clone().last(),
expected.clone().last().map(PositiveInt)
);
compare_iters_finite(
actual.clone(),
Iterator::next,
expected.clone(),
Iterator::next,
)?;
compare_iters_finite(
actual,
DoubleEndedIterator::next_back,
expected,
DoubleEndedIterator::next_back,
)?;
let actual = PositiveInt::iter_range_inclusive(start, end);
let expected = (start.0)..=(end.0);
prop_assert_eq!(actual.len(), expected.clone().count());
prop_assert_eq!(actual.size_hint(), expected.size_hint());
prop_assert_eq!(actual.clone().count(), expected.clone().count());
prop_assert_eq!(
actual.clone().last(),
expected.clone().last().map(PositiveInt)
);
compare_iters_finite(
actual.clone(),
Iterator::next,
expected.clone(),
Iterator::next,
)?;
compare_iters_finite(
actual,
DoubleEndedIterator::next_back,
expected,
DoubleEndedIterator::next_back,
)?;
}
}
fn exponent() -> impl Strategy<Value = u32> {
prop_oneof![
4 => 0..=PositiveInt::EFFECTIVE_BITS,
1 => any::<u32>(),
]
}
proptest! {
#[test]
fn pow_shift_rotate(
int: PositiveInt,
rhs in exponent()
) {
let (expected_wrapped, expected_overflow) =
predict_overflowing_result(int, rhs, usize::overflowing_pow);
let (wrapped, overflow) = test_overflowing(
int,
rhs,
PositiveInt::checked_pow,
PositiveInt::overflowing_pow,
PositiveInt::wrapping_pow,
[Box::new(PositiveInt::pow)],
)?;
prop_assert_eq!(wrapped, expected_wrapped);
prop_assert_eq!(overflow, expected_overflow);
if overflow {
prop_assert_eq!(int.saturating_pow(rhs), PositiveInt::MAX);
} else {
prop_assert_eq!(int.saturating_pow(rhs), wrapped);
}
let wrapped_shift = rhs % PositiveInt::EFFECTIVE_BITS;
let expected_wrapped = PositiveInt((int.0 << wrapped_shift) & PositiveInt::MAX.0);
let expected_overflow = rhs >= PositiveInt::EFFECTIVE_BITS;
let (wrapped, overflow) = test_overflowing(
int,
rhs,
PositiveInt::checked_shl,
PositiveInt::overflowing_shl,
PositiveInt::wrapping_shl,
[
Box::new(|int, rhs| int << rhs),
Box::new(|int, rhs| &int << rhs),
Box::new(|int, rhs| int << &rhs),
Box::new(|int, rhs| &int << &rhs),
Box::new(|mut int, rhs| {
int <<= rhs;
int
}),
Box::new(|mut int, rhs| {
int <<= &rhs;
int
}),
],
)?;
prop_assert_eq!(wrapped, expected_wrapped);
prop_assert_eq!(overflow, expected_overflow);
let expected_wrapped = PositiveInt(int.0 >> wrapped_shift);
let (wrapped, overflow) = test_overflowing(
int,
rhs,
PositiveInt::checked_shr,
PositiveInt::overflowing_shr,
PositiveInt::wrapping_shr,
[
Box::new(|int, rhs| int >> rhs),
Box::new(|int, rhs| &int >> rhs),
Box::new(|int, rhs| int >> &rhs),
Box::new(|int, rhs| &int >> &rhs),
Box::new(|mut int, rhs| {
int >>= rhs;
int
}),
Box::new(|mut int, rhs| {
int >>= &rhs;
int
}),
],
)?;
prop_assert_eq!(wrapped, expected_wrapped);
prop_assert_eq!(overflow, expected_overflow);
prop_assert_eq!(
int.rotate_left(rhs),
(int << wrapped_shift) | int.wrapping_shr(PositiveInt::EFFECTIVE_BITS - wrapped_shift)
);
prop_assert_eq!(
int.rotate_right(rhs),
(int >> wrapped_shift) | int.wrapping_shl(PositiveInt::EFFECTIVE_BITS - wrapped_shift)
);
}
#[test]
fn int_usize(int: PositiveInt, other: usize) {
prop_assert_eq!(int == other, usize::from(int) == other);
prop_assert_eq!(
int.partial_cmp(&other),
usize::from(int).partial_cmp(&other)
);
prop_assert_eq!(other == int, other == usize::from(int));
prop_assert_eq!(
other.partial_cmp(&int),
other.partial_cmp(&usize::from(int))
);
#[allow(clippy::cast_possible_truncation)]
let expected = PositiveInt(int.0 & (other as c_uint));
prop_assert_eq!(int & other, expected);
prop_assert_eq!(other & int, expected);
prop_assert_eq!(&int & other, expected);
prop_assert_eq!(&other & int, expected);
prop_assert_eq!(int & (&other), expected);
prop_assert_eq!(other & (&int), expected);
prop_assert_eq!(&int & (&other), expected);
prop_assert_eq!(&other & (&int), expected);
let mut tmp = int;
tmp &= other;
prop_assert_eq!(tmp, expected);
tmp = int;
tmp &= &other;
prop_assert_eq!(tmp, expected);
let small_other = other & usize::from(PositiveInt::MAX);
let small_other_unsigned = c_uint::try_from(small_other).unwrap();
let expected = PositiveInt(int.0 | small_other_unsigned);
prop_assert_eq!(int | small_other, expected);
prop_assert_eq!(small_other | int, expected);
prop_assert_eq!(&int | small_other, expected);
prop_assert_eq!(small_other | &int, expected);
prop_assert_eq!(int | &small_other, expected);
prop_assert_eq!(&small_other | int, expected);
prop_assert_eq!(&int | &small_other, expected);
prop_assert_eq!(&small_other | &int, expected);
tmp = int;
tmp |= small_other;
prop_assert_eq!(tmp, expected);
tmp = int;
tmp |= &small_other;
prop_assert_eq!(tmp, expected);
let first_large_bit = 1usize << PositiveInt::EFFECTIVE_BITS;
let mut large_other = other;
if other < first_large_bit {
large_other |= first_large_bit
};
assert_debug_panics(|| int | large_other, expected)?;
assert_debug_panics(|| large_other | int, expected)?;
assert_debug_panics(|| &int | large_other, expected)?;
assert_debug_panics(|| large_other | &int, expected)?;
assert_debug_panics(|| int | &large_other, expected)?;
assert_debug_panics(|| &large_other | int, expected)?;
assert_debug_panics(|| &int | &large_other, expected)?;
assert_debug_panics(|| &large_other | &int, expected)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp |= large_other;
tmp
},
expected,
)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp |= &large_other;
tmp
},
expected,
)?;
let expected = PositiveInt(int.0 ^ small_other_unsigned);
prop_assert_eq!(int ^ small_other, expected);
prop_assert_eq!(small_other ^ int, expected);
prop_assert_eq!(&int ^ small_other, expected);
prop_assert_eq!(small_other ^ &int, expected);
prop_assert_eq!(int ^ &small_other, expected);
prop_assert_eq!(&small_other ^ int, expected);
prop_assert_eq!(&int ^ &small_other, expected);
prop_assert_eq!(&small_other ^ &int, expected);
tmp = int;
tmp ^= small_other;
prop_assert_eq!(tmp, expected);
tmp = int;
tmp ^= &small_other;
prop_assert_eq!(tmp, expected);
assert_debug_panics(|| int ^ large_other, expected)?;
assert_debug_panics(|| large_other ^ int, expected)?;
assert_debug_panics(|| &int ^ large_other, expected)?;
assert_debug_panics(|| large_other ^ &int, expected)?;
assert_debug_panics(|| int ^ &large_other, expected)?;
assert_debug_panics(|| &large_other ^ int, expected)?;
assert_debug_panics(|| &int ^ &large_other, expected)?;
assert_debug_panics(|| &large_other ^ &int, expected)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp ^= large_other;
tmp
},
expected,
)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp ^= &large_other;
tmp
},
expected,
)?;
let small_other_int = PositiveInt::try_from(small_other).unwrap();
let (wrapped, overflow) = int.overflowing_mul(small_other_int);
if overflow {
assert_debug_panics(|| int * small_other, wrapped)?;
assert_debug_panics(|| small_other * int, wrapped)?;
assert_debug_panics(|| &int * small_other, wrapped)?;
assert_debug_panics(|| small_other * (&int), wrapped)?;
assert_debug_panics(|| int * (&small_other), wrapped)?;
assert_debug_panics(|| &small_other * int, wrapped)?;
assert_debug_panics(|| &int * (&small_other), wrapped)?;
assert_debug_panics(|| &small_other * (&int), wrapped)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp *= small_other;
tmp
},
wrapped,
)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp *= &small_other;
tmp
},
wrapped,
)?;
} else {
prop_assert_eq!(int * small_other, wrapped);
prop_assert_eq!(small_other * int, wrapped);
prop_assert_eq!(&int * small_other, wrapped);
prop_assert_eq!(small_other * (&int), wrapped);
prop_assert_eq!(int * (&small_other), wrapped);
prop_assert_eq!(&small_other * int, wrapped);
prop_assert_eq!(&int * (&small_other), wrapped);
prop_assert_eq!(&small_other * (&int), wrapped);
tmp = int;
tmp *= small_other;
prop_assert_eq!(tmp, wrapped);
tmp = int;
tmp *= &small_other;
prop_assert_eq!(tmp, wrapped);
}
let zero = PositiveInt::ZERO;
if int != zero {
assert_debug_panics(|| int * large_other, wrapped)?;
assert_debug_panics(|| large_other * int, wrapped)?;
assert_debug_panics(|| &int * large_other, wrapped)?;
assert_debug_panics(|| large_other * (&int), wrapped)?;
assert_debug_panics(|| int * (&large_other), wrapped)?;
assert_debug_panics(|| &large_other * int, wrapped)?;
assert_debug_panics(|| &int * (&large_other), wrapped)?;
assert_debug_panics(|| &large_other * (&int), wrapped)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp *= large_other;
tmp
},
wrapped,
)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp *= &large_other;
tmp
},
wrapped,
)?;
}
if other != 0 {
let expected = int / small_other_int;
prop_assert_eq!(int / small_other, expected);
prop_assert_eq!(&int / small_other, expected);
prop_assert_eq!(int / &small_other, expected);
prop_assert_eq!(&int / &small_other, expected);
tmp = int;
tmp /= small_other;
prop_assert_eq!(tmp, expected);
tmp = int;
tmp /= &small_other;
prop_assert_eq!(tmp, expected);
}
prop_assert_eq!(int / large_other, zero);
prop_assert_eq!(&int / large_other, zero);
prop_assert_eq!(int / &large_other, zero);
prop_assert_eq!(&int / &large_other, zero);
tmp = int;
tmp /= large_other;
prop_assert_eq!(tmp, zero);
tmp = int;
tmp /= &large_other;
prop_assert_eq!(tmp, zero);
if other != 0 {
let expected = int % small_other_int;
prop_assert_eq!(int % small_other, expected);
prop_assert_eq!(&int % small_other, expected);
prop_assert_eq!(int % &small_other, expected);
prop_assert_eq!(&int % &small_other, expected);
tmp = int;
tmp %= small_other;
prop_assert_eq!(tmp, expected);
tmp = int;
tmp %= &small_other;
prop_assert_eq!(tmp, expected);
}
prop_assert_eq!(int % large_other, int);
prop_assert_eq!(&int % large_other, int);
prop_assert_eq!(int % &large_other, int);
prop_assert_eq!(&int % &large_other, int);
tmp = int;
tmp %= large_other;
prop_assert_eq!(tmp, int);
tmp = int;
tmp %= &large_other;
prop_assert_eq!(tmp, int);
let effective_bits = PositiveInt::EFFECTIVE_BITS as usize;
let wrapped_shift = other % effective_bits;
let wrapped_result = PositiveInt((int.0 << wrapped_shift) & PositiveInt::MAX.0);
prop_assert_eq!(int << wrapped_shift, wrapped_result);
prop_assert_eq!(&int << wrapped_shift, wrapped_result);
prop_assert_eq!(int << (&wrapped_shift), wrapped_result);
prop_assert_eq!(&int << (&wrapped_shift), wrapped_result);
tmp = int;
tmp <<= wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
tmp = int;
tmp <<= &wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
let overflown_shift = other.saturating_add(effective_bits);
assert_debug_panics(|| int << overflown_shift, wrapped_result)?;
assert_debug_panics(|| &int << overflown_shift, wrapped_result)?;
assert_debug_panics(|| int << (&overflown_shift), wrapped_result)?;
assert_debug_panics(|| &int << (&overflown_shift), wrapped_result)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp <<= overflown_shift;
tmp
},
wrapped_result,
)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp <<= &overflown_shift;
tmp
},
wrapped_result,
)?;
let wrapped_result = PositiveInt(int.0 >> wrapped_shift);
prop_assert_eq!(int >> wrapped_shift, wrapped_result);
prop_assert_eq!(&int >> wrapped_shift, wrapped_result);
prop_assert_eq!(int >> (&wrapped_shift), wrapped_result);
prop_assert_eq!(&int >> (&wrapped_shift), wrapped_result);
tmp = int;
tmp >>= wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
tmp = int;
tmp >>= &wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
assert_debug_panics(|| int >> overflown_shift, wrapped_result)?;
assert_debug_panics(|| &int >> overflown_shift, wrapped_result)?;
assert_debug_panics(|| int >> (&overflown_shift), wrapped_result)?;
assert_debug_panics(|| &int >> (&overflown_shift), wrapped_result)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp >>= overflown_shift;
tmp
},
wrapped_result,
)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp >>= &overflown_shift;
tmp
},
wrapped_result,
)?;
let actual = PositiveInt::iter_range_from(int);
let expected = (int.0)..;
let remaining_iters = usize::from(PositiveInt::MAX - int);
let max_stride = remaining_iters / INFINITE_ITERS;
let stride = other % max_stride;
compare_iters_infinite(actual, |i| i.nth(stride), expected, |i| i.nth(stride))?;
}
#[test]
fn int_isize(int: PositiveInt, other: isize) {
let (expected_wrapped, expected_overflow) =
predict_overflowing_result(int, other, usize::overflowing_add_signed);
let (wrapped, overflow) = test_overflowing(
int,
other,
PositiveInt::checked_add_signed,
PositiveInt::overflowing_add_signed,
PositiveInt::wrapping_add_signed,
[
Box::new(|int, other| int + other),
Box::new(|int, other| other + int),
Box::new(|int, other| &int + other),
Box::new(|int, other| other + &int),
Box::new(|int, other| int + &other),
Box::new(|int, other| &other + int),
Box::new(|int, other| &int + &other),
Box::new(|int, other| &other + &int),
Box::new(|mut int, other| {
int += other;
int
}),
Box::new(|mut int, other| {
int += &other;
int
}),
],
)?;
prop_assert_eq!(wrapped, expected_wrapped);
prop_assert_eq!(overflow, expected_overflow);
if overflow {
if other > 0 {
prop_assert_eq!(int.saturating_add_signed(other), PositiveInt::MAX);
} else {
prop_assert_eq!(int.saturating_add_signed(other), PositiveInt::MIN);
}
} else {
prop_assert_eq!(int.saturating_add_signed(other), wrapped);
}
let (wrapped, overflow) = if other == isize::MIN {
predict_overflowing_result(
int,
1usize << (isize::BITS - 1),
usize::overflowing_sub,
)
} else {
predict_overflowing_result(int, -other, usize::overflowing_add_signed)
};
if overflow {
assert_debug_panics(|| int - other, wrapped)?;
assert_debug_panics(|| &int - other, wrapped)?;
assert_debug_panics(|| int - &other, wrapped)?;
assert_debug_panics(|| &int - &other, wrapped)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp -= other;
tmp
},
wrapped,
)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp -= &other;
tmp
},
wrapped,
)?;
} else {
prop_assert_eq!(int - other, wrapped);
prop_assert_eq!(&int - other, wrapped);
prop_assert_eq!(int - &other, wrapped);
prop_assert_eq!(&int - &other, wrapped);
let mut tmp = int;
tmp -= other;
prop_assert_eq!(tmp, wrapped);
tmp = int;
tmp -= &other;
prop_assert_eq!(tmp, wrapped);
}
let effective_bits = isize::try_from(PositiveInt::EFFECTIVE_BITS).unwrap();
let wrapped_shift = other.rem_euclid(effective_bits);
let wrapped_result = PositiveInt((int.0 << wrapped_shift) & PositiveInt::MAX.0);
prop_assert_eq!(int << wrapped_shift, wrapped_result);
prop_assert_eq!(&int << wrapped_shift, wrapped_result);
prop_assert_eq!(int << (&wrapped_shift), wrapped_result);
prop_assert_eq!(&int << (&wrapped_shift), wrapped_result);
let mut tmp = int;
tmp <<= wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
tmp = int;
tmp <<= &wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
let overflown_shift = other.saturating_add(effective_bits);
assert_debug_panics(|| int << overflown_shift, wrapped_result)?;
assert_debug_panics(|| &int << overflown_shift, wrapped_result)?;
assert_debug_panics(|| int << (&overflown_shift), wrapped_result)?;
assert_debug_panics(|| &int << (&overflown_shift), wrapped_result)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp <<= overflown_shift;
tmp
},
wrapped_result,
)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp <<= &overflown_shift;
tmp
},
wrapped_result,
)?;
let wrapped_result = PositiveInt((int.0 >> wrapped_shift) & PositiveInt::MAX.0);
prop_assert_eq!(int >> wrapped_shift, wrapped_result);
prop_assert_eq!(&int >> wrapped_shift, wrapped_result);
prop_assert_eq!(int >> (&wrapped_shift), wrapped_result);
prop_assert_eq!(&int >> (&wrapped_shift), wrapped_result);
tmp = int;
tmp >>= wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
tmp = int;
tmp >>= &wrapped_shift;
prop_assert_eq!(tmp, wrapped_result);
assert_debug_panics(|| int >> overflown_shift, wrapped_result)?;
assert_debug_panics(|| &int >> overflown_shift, wrapped_result)?;
assert_debug_panics(|| int >> (&overflown_shift), wrapped_result)?;
assert_debug_panics(|| &int >> (&overflown_shift), wrapped_result)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp >>= overflown_shift;
tmp
},
wrapped_result,
)?;
assert_debug_panics(
|| {
let mut tmp = int;
tmp >>= &overflown_shift;
tmp
},
wrapped_result,
)?;
}
}
fn iter_step() -> impl Strategy<Value = usize> {
let max_normal_stride = prop::collection::SizeRange::default().end_excl();
prop_oneof![
4 => 0..max_normal_stride,
1 => any::<usize>()
]
}
proptest! {
#[test]
fn int_range_with_step(
[start, end] in int_range_bounds(),
step in iter_step()
) {
let actual = PositiveInt::iter_range(start, end);
let expected = (start.0)..(end.0);
compare_iters_finite(
actual.clone(),
|i| i.nth(step),
expected.clone(),
|i| i.nth(step),
)?;
compare_iters_finite(actual, |i| i.nth_back(step), expected, |i| i.nth_back(step))?;
let actual = PositiveInt::iter_range_inclusive(start, end);
let expected = (start.0)..=(end.0);
compare_iters_finite(
actual.clone(),
|i| i.nth(step),
expected.clone(),
|i| i.nth(step),
)?;
compare_iters_finite(actual, |i| i.nth_back(step), expected, |i| i.nth_back(step))?;
}
#[allow(clippy::redundant_closure_for_method_calls)]
#[test]
fn reductions(ints: Vec<PositiveInt>) {
use std::iter::Copied;
type IntRefIter<'a> = std::slice::Iter<'a, PositiveInt>;
fn test_reduction(
ints: &[PositiveInt],
neutral: PositiveInt,
overflowing_op: impl Fn(PositiveInt, PositiveInt) -> (PositiveInt, bool),
reduce_by_ref: impl Fn(IntRefIter<'_>) -> PositiveInt + RefUnwindSafe,
reduce_by_value: impl Fn(Copied<IntRefIter<'_>>) -> PositiveInt + RefUnwindSafe,
) -> Result<(), TestCaseError> {
let (wrapping_result, overflow) = ints.iter().copied().fold(
(neutral, false),
|(wrapping_acc, prev_overflow), elem| {
let (wrapping_acc, new_overflow) = overflowing_op(wrapping_acc, elem);
(wrapping_acc, prev_overflow || new_overflow)
},
);
let reductions: [&(dyn Fn() -> PositiveInt + RefUnwindSafe); 2] =
[&|| reduce_by_ref(ints.iter()), &|| {
reduce_by_value(ints.iter().copied())
}];
for reduction in reductions {
if overflow {
assert_debug_panics(reduction, wrapping_result)?;
} else {
prop_assert_eq!(reduction(), wrapping_result);
}
}
Ok(())
}
test_reduction(
&ints,
PositiveInt::ZERO,
PositiveInt::overflowing_add,
|ref_iter| ref_iter.sum::<PositiveInt>(),
|value_iter| value_iter.sum::<PositiveInt>(),
)?;
test_reduction(
&ints,
PositiveInt::ONE,
PositiveInt::overflowing_mul,
|ref_iter| ref_iter.product::<PositiveInt>(),
|value_iter| value_iter.product::<PositiveInt>(),
)?;
}
}
}