use core::num::NonZero;
use as_repr::AsRepr;
use crate::{Error, Quotient, RangedU32, RangedU64, Result};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct RangedNonZeroU64<const MIN: u64, const MAX: u64>(
pub(crate) NonZero<u64>,
);
impl<const MIN: u64, const MAX: u64> RangedNonZeroU64<MIN, MAX> {
pub const BITS: u32 = u64::BITS;
pub const MAX: Self = Self::new::<MAX>();
pub const MIN: Self = Self::new::<MIN>();
#[must_use]
pub const fn new<const N: u64>() -> Self {
const {
Self::assert_range();
if N < MIN || N > MAX {
panic!("Out of bounds");
}
Self(NonZero::new(N).unwrap())
}
}
pub const fn with_u64(value: impl AsRepr<u64>) -> Result<Option<Self>> {
const { Self::assert_range() };
let value = as_repr::as_repr(value);
let Some(value) = NonZero::new(value) else {
return Ok(None);
};
match Self::with_nonzero(value) {
Ok(v) => Ok(Some(v)),
Err(e) => Err(e),
}
}
pub const fn with_nonzero(
nonzero: impl AsRepr<NonZero<u64>>,
) -> Result<Self> {
const { Self::assert_range() };
let nonzero = as_repr::as_repr(nonzero);
if nonzero.get() < MIN {
return Err(Error::NegOverflow);
}
if nonzero.get() > MAX {
return Err(Error::PosOverflow);
}
Ok(Self(nonzero))
}
#[must_use]
pub const fn get(self) -> u64 {
self.0.get()
}
#[must_use]
pub const fn to_nonzero(self) -> NonZero<u64> {
self.0
}
#[must_use]
pub const fn to_ranged(self) -> RangedU64<MIN, MAX> {
RangedU64(self.get())
}
#[must_use]
pub const fn leading_zeros(self) -> RangedU32<0, { u64::BITS }> {
RangedU32(self.get().leading_zeros())
}
#[must_use]
pub const fn trailing_zeros(self) -> RangedU32<0, { u64::BITS }> {
RangedU32(self.get().trailing_zeros())
}
#[must_use]
pub const fn count_ones(self) -> RangedU32<0, { u64::BITS }> {
RangedU32(self.get().count_ones())
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_add(self, other: impl AsRepr<u64>) -> Option<Self> {
let Some(value) = self.to_ranged().checked_add(other) else {
return None;
};
value.to_ranged_nonzero()
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_mul(self, other: impl AsRepr<u64>) -> Option<Self> {
let Some(value) = self.to_ranged().checked_mul(other) else {
return None;
};
value.to_ranged_nonzero()
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_pow(self, other: impl AsRepr<u32>) -> Option<Self> {
let Some(value) = self.to_ranged().checked_pow(other) else {
return None;
};
value.to_ranged_nonzero()
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_div(
self,
rhs: impl AsRepr<u64>,
) -> Option<Quotient<Self>> {
let Some(value) = self.to_ranged().checked_div(rhs) else {
return None;
};
let Quotient::Number(number) = value else {
return Some(Quotient::Nan);
};
let Some(number) = number.to_ranged_nonzero() else {
return None;
};
Some(Quotient::Number(number))
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_sub(self, other: impl AsRepr<u64>) -> Option<Self> {
let Some(value) = self.to_ranged().checked_sub(other) else {
return None;
};
value.to_ranged_nonzero()
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn add_ranged<
const RHS_MIN: u64,
const RHS_MAX: u64,
const OUTPUT_MIN: u64,
const OUTPUT_MAX: u64,
>(
self,
rhs: RangedNonZeroU64<RHS_MIN, RHS_MAX>,
) -> RangedNonZeroU64<OUTPUT_MIN, OUTPUT_MAX> {
self.to_ranged()
.add_ranged::<RHS_MIN, RHS_MAX, OUTPUT_MIN, OUTPUT_MAX>(
rhs.to_ranged(),
)
.to_ranged_nonzero()
.unwrap()
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn mul_ranged<
const RHS_MIN: u64,
const RHS_MAX: u64,
const OUTPUT_MIN: u64,
const OUTPUT_MAX: u64,
>(
self,
rhs: RangedNonZeroU64<RHS_MIN, RHS_MAX>,
) -> RangedNonZeroU64<OUTPUT_MIN, OUTPUT_MAX> {
self.to_ranged()
.mul_ranged::<RHS_MIN, RHS_MAX, OUTPUT_MIN, OUTPUT_MAX>(
rhs.to_ranged(),
)
.to_ranged_nonzero()
.unwrap()
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn pow_ranged<
const RHS_MIN: u32,
const RHS_MAX: u32,
const OUTPUT_MIN: u64,
const OUTPUT_MAX: u64,
>(
self,
rhs: RangedU32<RHS_MIN, RHS_MAX>,
) -> RangedNonZeroU64<OUTPUT_MIN, OUTPUT_MAX> {
self.to_ranged()
.pow_ranged::<RHS_MIN, RHS_MAX, OUTPUT_MIN, OUTPUT_MAX>(rhs)
.to_ranged_nonzero()
.unwrap()
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn midpoint(self, rhs: Self) -> Self {
let Ok(Some(value)) = Self::with_u64(self.get().midpoint(rhs.get()))
else {
panic!("unexpected midpoint value")
};
value
}
}
impl<const MIN: u64, const MAX: u64> crate::error::Clamp
for RangedNonZeroU64<MIN, MAX>
{
const MAX: Self = Self::MAX;
const MIN: Self = Self::MIN;
}