use core::{
convert::TryFrom,
fmt,
num::{NonZeroU32, NonZeroU64},
ops::{Add, Mul},
};
use serde::{Deserialize, Deserializer, Serialize};
use crate::{ClosedUnitF64, OffByOneU32};
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct OffByOneU64Error(u128);
impl fmt::Display for OffByOneU64Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{} is not in {{1, .., 2^64}}.", self.0)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, TypeLayout)]
#[repr(transparent)]
#[serde(try_from = "u128", into = "u128")]
pub struct OffByOneU64(u64);
impl fmt::Display for OffByOneU64 {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.get(), fmt)
}
}
impl OffByOneU64 {
pub const fn new(value: u128) -> Result<Self, OffByOneU64Error> {
match value.wrapping_sub(1) {
#[allow(clippy::cast_possible_truncation)]
value if value < (u64::MAX as u128) => Ok(Self(value as u64)),
_ => Err(OffByOneU64Error(value)),
}
}
#[must_use]
pub const unsafe fn new_unchecked(value: u128) -> Self {
#[allow(clippy::cast_possible_truncation)]
Self(value.wrapping_sub(1) as u64)
}
#[must_use]
pub const fn get(self) -> u128 {
(self.0 as u128) + 1_u128
}
#[must_use]
pub const fn add_incl(self, other: u64) -> u64 {
other.wrapping_add(self.0)
}
#[must_use]
pub const fn add_excl(self, other: u64) -> u64 {
other.wrapping_add(self.0).wrapping_add(1)
}
#[must_use]
pub const fn one() -> Self {
Self(0)
}
#[must_use]
pub const fn max() -> Self {
Self(u64::MAX)
}
#[must_use]
pub const fn inv(self) -> u64 {
u64::MAX - self.0
}
}
impl TryFrom<u128> for OffByOneU64 {
type Error = OffByOneU64Error;
fn try_from(value: u128) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl From<NonZeroU32> for OffByOneU64 {
fn from(val: NonZeroU32) -> Self {
Self(u64::from(val.get()) - 1)
}
}
impl From<OffByOneU32> for OffByOneU64 {
fn from(val: OffByOneU32) -> Self {
Self(val.get() - 1)
}
}
impl From<NonZeroU64> for OffByOneU64 {
fn from(val: NonZeroU64) -> Self {
Self(val.get() - 1)
}
}
impl From<OffByOneU64> for NonZeroU64 {
fn from(val: OffByOneU64) -> Self {
unsafe { NonZeroU64::new_unchecked(val.0 + 1) }
}
}
impl From<OffByOneU64> for f64 {
#[allow(clippy::cast_precision_loss)]
fn from(val: OffByOneU64) -> Self {
(val.0 as f64) + 1.0_f64
}
}
impl From<OffByOneU64> for u128 {
fn from(val: OffByOneU64) -> Self {
u128::from(val.0) + 1_u128
}
}
impl<'de> Deserialize<'de> for OffByOneU64 {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Self::new(u128::deserialize(deserializer)?).map_err(serde::de::Error::custom)
}
}
impl Add for OffByOneU64 {
type Output = Self;
fn add(self, other: Self) -> Self {
Self((self.0 + 1) + (other.0 + 1) - 1)
}
}
impl Mul for OffByOneU64 {
type Output = Self;
fn mul(self, other: Self) -> Self {
Self((self.0 + 1) * (other.0 + 1) - 1)
}
}
impl Mul<ClosedUnitF64> for OffByOneU64 {
type Output = Self;
fn mul(self, other: ClosedUnitF64) -> Self::Output {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_precision_loss)]
Self(((((self.get() as f64) * other.get()) as u128) - 1) as u64)
}
}