use as_repr::AsRepr;
use crate::{Error, ParsingError, ParsingResult, Quotient, RangedU32, Result};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct RangedI128<const MIN: i128, const MAX: i128>(pub(crate) i128);
impl<const MIN: i128, const MAX: i128> RangedI128<MIN, MAX> {
pub const BITS: u32 = i128::BITS;
pub const MAX: Self = Self(MAX);
pub const MIN: Self = Self(MIN);
#[must_use]
pub const fn new<const N: i128>() -> Self {
const {
Self::assert_range();
if N < MIN || N > MAX {
panic!("Out of bounds");
}
}
Self(N)
}
pub const fn with_i128(value: impl AsRepr<i128>) -> 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) -> i128 {
self.0
}
#[must_use]
pub const fn leading_zeros(self) -> RangedU32<0, { i128::BITS }> {
RangedU32(self.get().leading_zeros())
}
#[must_use]
pub const fn trailing_zeros(self) -> RangedU32<0, { i128::BITS }> {
RangedU32(self.get().trailing_zeros())
}
#[must_use]
pub const fn count_ones(self) -> RangedU32<0, { i128::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<i128>) -> Result<Self> {
let other = as_repr::as_repr(other);
let Some(value) = self.get().checked_add(other) else {
return Err(
if self.get().saturating_add(other) == Self::MAX.get() {
Error::PosOverflow
} else {
Error::NegOverflow
},
);
};
Self::with_i128(value)
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn saturating_add(self, other: impl AsRepr<i128>) -> Self {
let other = as_repr::as_repr(other);
match Self::with_i128(self.get().saturating_add(other)) {
Ok(value) => value,
Err(Error::NegOverflow) => Self::MIN,
Err(Error::PosOverflow) => Self::MAX,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_mul(self, other: impl AsRepr<i128>) -> Result<Self> {
let other = as_repr::as_repr(other);
let Some(value) = self.get().checked_mul(other) else {
return Err(if self.is_negative() ^ other.is_negative() {
Error::NegOverflow
} else {
Error::PosOverflow
});
};
Self::with_i128(value)
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn saturating_mul(self, other: impl AsRepr<i128>) -> Self {
let other = as_repr::as_repr(other);
match Self::with_i128(self.get().saturating_mul(other)) {
Ok(value) => value,
Err(Error::NegOverflow) => Self::MIN,
Err(Error::PosOverflow) => 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>) -> Result<Self> {
let other = as_repr::as_repr(other);
let Some(value) = self.get().checked_pow(other) else {
return Err(if self.is_negative() && other % 2 == 1 {
Error::NegOverflow
} else {
Error::PosOverflow
});
};
Self::with_i128(value)
}
#[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_i128(self.get().saturating_pow(other)) {
Ok(value) => value,
Err(Error::NegOverflow) => Self::MIN,
Err(Error::PosOverflow) => Self::MAX,
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_div(
self,
rhs: impl AsRepr<i128>,
) -> Result<Quotient<Self>> {
let rhs = as_repr::as_repr(rhs);
if rhs == 0 {
return Ok(Quotient::Nan);
}
let Some(value) = self.get().checked_div(rhs) else {
return Err(if self.is_negative() ^ rhs.is_negative() {
Error::PosOverflow
} else {
Error::NegOverflow
});
};
match Self::with_i128(value) {
Ok(v) => Ok(Quotient::Number(v)),
Err(e) => Err(e),
}
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn saturating_div(
self,
rhs: impl AsRepr<i128>,
) -> Quotient<Self> {
let rhs = as_repr::as_repr(rhs);
if rhs == 0 {
return Quotient::Nan;
}
Quotient::Number(
match Self::with_i128(self.get().saturating_div(rhs)) {
Ok(value) => value,
Err(Error::NegOverflow) => Self::MIN,
Err(Error::PosOverflow) => Self::MAX,
},
)
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn checked_sub(self, other: impl AsRepr<i128>) -> Result<Self> {
let other = as_repr::as_repr(other);
let Some(value) = self.get().checked_sub(other) else {
return Err(if other.is_negative() {
Error::PosOverflow
} else {
Error::NegOverflow
});
};
Self::with_i128(value)
}
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub const fn saturating_sub(self, other: impl AsRepr<i128>) -> Self {
let other = as_repr::as_repr(other);
match Self::with_i128(self.get().saturating_sub(other)) {
Ok(value) => value,
Err(Error::NegOverflow) => Self::MIN,
Err(Error::PosOverflow) => Self::MAX,
}
}
#[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 midpoint(self, rhs: Self) -> Self {
let Ok(value) = Self::with_i128(midpoint(self.get(), 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: i128,
const RHS_MAX: i128,
const OUTPUT_MIN: i128,
const OUTPUT_MAX: i128,
>(
self,
rhs: RangedI128<RHS_MIN, RHS_MAX>,
) -> RangedI128<OUTPUT_MIN, OUTPUT_MAX> {
const {
if MIN + RHS_MIN != OUTPUT_MIN {
panic!("Min mismatch");
}
if MAX + RHS_MAX != OUTPUT_MAX {
panic!("Max mismatch");
}
}
RangedI128(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: i128,
const RHS_MAX: i128,
const OUTPUT_MIN: i128,
const OUTPUT_MAX: i128,
>(
self,
rhs: RangedI128<RHS_MIN, RHS_MAX>,
) -> RangedI128<OUTPUT_MIN, OUTPUT_MAX> {
const {
if MIN - RHS_MAX != OUTPUT_MIN {
panic!("Min mismatch");
}
if MAX - RHS_MIN != OUTPUT_MAX {
panic!("Max mismatch");
}
}
RangedI128(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: i128,
const RHS_MAX: i128,
const OUTPUT_MIN: i128,
const OUTPUT_MAX: i128,
>(
self,
rhs: RangedI128<RHS_MIN, RHS_MAX>,
) -> RangedI128<OUTPUT_MIN, OUTPUT_MAX> {
const {
let (min_min, min_max) = (MIN * RHS_MIN, MIN * RHS_MAX);
let min = if min_min < min_max { min_min } else { min_max };
let (max_min, max_max) = (MAX * RHS_MIN, MAX * RHS_MAX);
let max = if max_min > max_max { max_min } else { max_max };
if min != OUTPUT_MIN {
panic!("Min mismatch");
}
if max != OUTPUT_MAX {
panic!("Max mismatch");
}
}
RangedI128(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: i128,
const RHS_MAX: i128,
const OUTPUT_MIN: i128,
const OUTPUT_MAX: i128,
>(
self,
rhs: RangedI128<RHS_MIN, RHS_MAX>,
) -> Quotient<RangedI128<OUTPUT_MIN, OUTPUT_MAX>> {
const {
let (min_min, min_max) = (MIN / RHS_MIN, MIN / RHS_MAX);
let (max_min, max_max) = (MAX / RHS_MIN, MAX / RHS_MAX);
let min = if min_min < min_max { min_min } else { min_max };
let min = if max_min < min { max_min } else { min };
let min = if max_max < min { max_max } else { min };
let max = if max_min > max_max { max_min } else { max_max };
let max = if min_min > min { min_min } else { max };
let max = if min_max > min { min_max } else { max };
if min != OUTPUT_MIN {
panic!("Min mismatch");
}
if max != OUTPUT_MAX {
panic!("Max mismatch");
}
}
if rhs.get() == 0 {
Quotient::Nan
} else {
Quotient::Number(RangedI128(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: i128,
const OUTPUT_MAX: i128,
>(
self,
rhs: RangedU32<RHS_MIN, RHS_MAX>,
) -> RangedI128<OUTPUT_MIN, OUTPUT_MAX> {
const {
if MIN.is_negative() {
let min = MIN.pow(RHS_MIN);
let max = MAX.pow(RHS_MAX);
let rhs_max = if RHS_MAX % 2 == 0 {
RHS_MAX - 1
} else {
RHS_MAX
};
let rhs_min = if RHS_MIN % 2 == 0 {
RHS_MIN - 1
} else {
RHS_MIN
};
let min_min = MIN.pow(rhs_min);
let min_max = MAX.pow(rhs_max);
let min = if min_min < min { min_min } else { min };
let min = if min_max < min { min_max } else { min };
if min != OUTPUT_MIN {
panic!("Min mismatch");
} else if max != OUTPUT_MAX {
panic!("Max mismatch");
}
} else if MIN.pow(RHS_MIN) != OUTPUT_MIN {
panic!("Min mismatch");
} else if MAX.pow(RHS_MAX) != OUTPUT_MAX {
panic!("Max mismatch");
}
}
RangedI128(self.get().pow(rhs.get()))
}
}
impl<const MIN: i128, const MAX: i128> core::str::FromStr
for RangedI128<MIN, MAX>
{
type Err = ParsingError;
fn from_str(src: &str) -> ParsingResult<Self> {
let parsed = src.parse::<i128>()?;
Self::with_i128(parsed).map_err(From::from)
}
}
impl<const MIN: i128, const MAX: i128> crate::error::Clamp
for RangedI128<MIN, MAX>
{
const MAX: Self = Self::MAX;
const MIN: Self = Self::MIN;
}
const fn midpoint(a: i128, b: i128) -> i128 {
let t = ((a ^ b) >> 1) + (a & b);
t + (if t < 0 { 1 } else { 0 } & (a ^ b))
}