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