use as_repr::AsRepr;
use crate::{Error, ParsingError, ParsingResult, Quotient, RangedU32, Result};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct RangedU128<const MIN: u128, const MAX: u128>(pub(crate) u128);
impl<const MIN: u128, const MAX: u128> RangedU128<MIN, MAX> {
pub const BITS: u32 = u128::BITS;
pub const MAX: Self = Self(MAX);
pub const MIN: Self = Self(MIN);
#[must_use]
pub const fn new<const N: u128>() -> Self {
const {
Self::assert_range();
if N < MIN || N > MAX {
panic!("Out of bounds");
}
}
Self(N)
}
pub const fn with_u128(value: impl AsRepr<u128>) -> Result<Self> {
const { Self::assert_range() };
let value = as_repr::as_repr(value);
if value < MIN {
return Err(Error::NegOverflow);
}
if value > MAX {
return Err(Error::PosOverflow);
}
Ok(Self(value))
}
#[must_use]
pub const fn get(self) -> u128 {
self.0
}
#[must_use]
pub const fn leading_zeros(self) -> RangedU32<0, { u128::BITS }> {
RangedU32(self.get().leading_zeros())
}
#[must_use]
pub const fn trailing_zeros(self) -> RangedU32<0, { u128::BITS }> {
RangedU32(self.get().trailing_zeros())
}
#[must_use]
pub const fn count_ones(self) -> RangedU32<0, { u128::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<u128>) -> Option<Self> {
let other = as_repr::as_repr(other);
let Some(value) = self.get().checked_add(other) else {
return None;
};
match Self::with_u128(value) {
Ok(value) => Some(value),
Err(_) => None,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn saturating_add(self, other: impl AsRepr<u128>) -> Self {
let other = as_repr::as_repr(other);
match Self::with_u128(self.get().saturating_add(other)) {
Ok(value) => value,
Err(_) => Self::MAX,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_mul(self, other: impl AsRepr<u128>) -> Option<Self> {
let other = as_repr::as_repr(other);
let Some(value) = self.get().checked_mul(other) else {
return None;
};
match Self::with_u128(value) {
Ok(value) => Some(value),
Err(_) => None,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn saturating_mul(self, other: impl AsRepr<u128>) -> Self {
let other = as_repr::as_repr(other);
match Self::with_u128(self.get().saturating_mul(other)) {
Ok(value) => value,
Err(_) => Self::MAX,
}
}
#[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 other = as_repr::as_repr(other);
let Some(value) = self.get().checked_pow(other) else {
return None;
};
match Self::with_u128(value) {
Ok(value) => Some(value),
Err(_) => None,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn saturating_pow(self, other: impl AsRepr<u32>) -> Self {
let other = as_repr::as_repr(other);
match Self::with_u128(self.get().saturating_pow(other)) {
Ok(value) => value,
Err(_) => Self::MAX,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_div(
self,
rhs: impl AsRepr<u128>,
) -> Option<Quotient<Self>> {
let rhs = as_repr::as_repr(rhs);
let Some(value) = self.get().checked_div(rhs) else {
return Some(Quotient::Nan);
};
match Self::with_u128(value) {
Ok(value) => Some(Quotient::Number(value)),
Err(_) => None,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn saturating_div(
self,
rhs: impl AsRepr<u128>,
) -> Quotient<Self> {
let rhs = as_repr::as_repr(rhs);
if rhs == 0 {
return Quotient::Nan;
}
Quotient::Number(
match Self::with_u128(self.get().saturating_div(rhs)) {
Ok(value) => value,
Err(_) => Self::MIN,
},
)
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_sub(self, other: impl AsRepr<u128>) -> Option<Self> {
let other = as_repr::as_repr(other);
let Some(value) = self.get().checked_sub(other) else {
return None;
};
match Self::with_u128(value) {
Ok(value) => Some(value),
Err(_) => None,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn saturating_sub(self, other: impl AsRepr<u128>) -> Self {
let other = as_repr::as_repr(other);
match Self::with_u128(self.get().saturating_sub(other)) {
Ok(value) => value,
Err(_) => Self::MIN,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn midpoint(self, rhs: Self) -> Self {
let Ok(value) = Self::with_u128(self.get().midpoint(rhs.get())) else {
panic!("unexpected midpoint value")
};
value
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn add_ranged<
const RHS_MIN: u128,
const RHS_MAX: u128,
const OUTPUT_MIN: u128,
const OUTPUT_MAX: u128,
>(
self,
rhs: RangedU128<RHS_MIN, RHS_MAX>,
) -> RangedU128<OUTPUT_MIN, OUTPUT_MAX> {
const {
if MIN + RHS_MIN != OUTPUT_MIN {
panic!("Min mismatch");
}
if MAX + RHS_MAX != OUTPUT_MAX {
panic!("Max mismatch");
}
}
RangedU128(self.get() + rhs.get())
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn sub_ranged<
const RHS_MIN: u128,
const RHS_MAX: u128,
const OUTPUT_MIN: u128,
const OUTPUT_MAX: u128,
>(
self,
rhs: RangedU128<RHS_MIN, RHS_MAX>,
) -> RangedU128<OUTPUT_MIN, OUTPUT_MAX> {
const {
if MIN - RHS_MAX != OUTPUT_MIN {
panic!("Min mismatch");
}
if MAX - RHS_MIN != OUTPUT_MAX {
panic!("Max mismatch");
}
}
RangedU128(self.get() - rhs.get())
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn mul_ranged<
const RHS_MIN: u128,
const RHS_MAX: u128,
const OUTPUT_MIN: u128,
const OUTPUT_MAX: u128,
>(
self,
rhs: RangedU128<RHS_MIN, RHS_MAX>,
) -> RangedU128<OUTPUT_MIN, OUTPUT_MAX> {
const {
if MIN * RHS_MIN != OUTPUT_MIN {
panic!("Min mismatch");
}
if MAX * RHS_MAX != OUTPUT_MAX {
panic!("Max mismatch");
}
}
RangedU128(self.get() * rhs.get())
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn div_ranged<
const RHS_MIN: u128,
const RHS_MAX: u128,
const OUTPUT_MIN: u128,
const OUTPUT_MAX: u128,
>(
self,
rhs: RangedU128<RHS_MIN, RHS_MAX>,
) -> Quotient<RangedU128<OUTPUT_MIN, OUTPUT_MAX>> {
const {
if MIN / RHS_MAX != OUTPUT_MIN {
panic!("Min mismatch");
}
if MAX / RHS_MAX != OUTPUT_MAX {
panic!("Max mismatch");
}
}
if rhs.get() == 0 {
Quotient::Nan
} else {
Quotient::Number(RangedU128(self.get() / rhs.get()))
}
}
#[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: u128,
const OUTPUT_MAX: u128,
>(
self,
rhs: RangedU32<RHS_MIN, RHS_MAX>,
) -> RangedU128<OUTPUT_MIN, OUTPUT_MAX> {
const {
if MIN.pow(RHS_MIN) != OUTPUT_MIN {
panic!("Min mismatch");
}
if MAX.pow(RHS_MAX) != OUTPUT_MAX {
panic!("Max mismatch");
}
}
RangedU128(self.get().pow(rhs.get()))
}
}
impl<const MIN: u128, const MAX: u128> core::str::FromStr
for RangedU128<MIN, MAX>
{
type Err = ParsingError;
fn from_str(src: &str) -> ParsingResult<Self> {
let parsed = src.parse::<u128>()?;
Self::with_u128(parsed).map_err(From::from)
}
}
impl<const MIN: u128, const MAX: u128> crate::error::Clamp
for RangedU128<MIN, MAX>
{
const MAX: Self = Self::MAX;
const MIN: Self = Self::MIN;
}