use core::ops::{Add, Mul, Neg, Shl, Sub};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct WideInt<const N: usize> {
limbs: [u64; N],
}
pub(crate) type Orient2dInt = WideInt<66>;
pub(crate) type Incircle2dInt = WideInt<132>;
pub(crate) type Orient3dInt = WideInt<99>;
pub(crate) type InsphereInt = WideInt<165>;
pub(crate) type RationalCoordInt = WideInt<99>;
pub(crate) type RationalEqInt = WideInt<165>;
pub(crate) type RationalOrderInt = WideInt<198>;
pub(crate) type RationalOrientInt = WideInt<132>;
pub(crate) type RoundingInt = WideInt<100>;
impl<const N: usize> WideInt<N> {
#[inline]
pub(crate) const fn zero() -> Self {
Self { limbs: [0; N] }
}
#[inline]
pub(crate) fn from_u64(value: u64) -> Self {
let mut result = Self::zero();
result.limbs[0] = value;
result
}
#[inline]
pub(crate) fn one() -> Self {
Self::from_u64(1)
}
#[inline]
pub(crate) fn is_zero(&self) -> bool {
self.limbs.iter().all(|&x| x == 0)
}
#[inline]
pub(crate) fn widen<const M: usize>(&self) -> WideInt<M> {
debug_assert!(
M >= N,
"WideInt::widen called with a narrower target width; limbs would be discarded"
);
let fill = if self.is_negative() { u64::MAX } else { 0u64 };
let mut out = WideInt::<M> { limbs: [fill; M] };
let n = N.min(M);
out.limbs[..n].copy_from_slice(&self.limbs[..n]);
out
}
#[inline]
pub(crate) fn wrapping_abs(self) -> Self {
if self.is_negative() {
self.wrapping_neg()
} else {
self
}
}
#[inline]
fn significant_limbs(&self) -> usize {
let mut i = N;
while i > 0 && self.limbs[i - 1] == 0 {
i -= 1;
}
i
}
#[inline]
pub(crate) fn bit_length(&self) -> usize {
debug_assert!(
!self.is_negative(),
"bit_length measures a magnitude; take wrapping_abs first"
);
let mut i = N;
while i > 0 {
i -= 1;
if self.limbs[i] != 0 {
return i * 64 + (64 - self.limbs[i].leading_zeros() as usize);
}
}
0
}
#[inline]
pub(crate) fn bit_window(&self, shift: usize) -> u64 {
debug_assert!(
!self.is_negative(),
"bit_window reads a magnitude; take wrapping_abs first"
);
let w = shift / 64;
let b = shift % 64;
let lo = if w < N { self.limbs[w] } else { 0 };
if b == 0 {
return lo;
}
let hi = if w + 1 < N { self.limbs[w + 1] } else { 0 };
(lo >> b) | (hi << (64 - b))
}
#[inline]
pub(crate) fn is_negative(&self) -> bool {
(self.limbs[N - 1] >> 63) != 0
}
#[inline]
pub(crate) fn is_positive(&self) -> bool {
!self.is_zero() && !self.is_negative()
}
#[inline]
pub(crate) fn fits_in_signed_bits(&self, n: usize) -> bool {
debug_assert!(
n >= 1 && n <= N * 64,
"fits_in_signed_bits requires 1 <= n <= N*64"
);
let sign = self.is_negative();
let expected_limb = if sign { u64::MAX } else { 0u64 };
let first = n - 1;
let upper_limb = first / 64;
let upper_bit = first % 64;
if upper_bit != 0 && upper_limb < N {
let mask = u64::MAX << upper_bit;
if self.limbs[upper_limb] & mask != expected_limb & mask {
return false;
}
}
let full_start = if upper_bit != 0 {
upper_limb + 1
} else {
upper_limb
};
for idx in full_start..N {
if self.limbs[idx] != expected_limb {
return false;
}
}
true
}
#[inline]
fn wrapping_add(self, rhs: Self) -> Self {
let mut result = Self::zero();
let mut carry = false;
for i in 0..N {
let (a, c1) = self.limbs[i].overflowing_add(rhs.limbs[i]);
let (b, c2) = a.overflowing_add(u64::from(carry));
result.limbs[i] = b;
carry = c1 || c2;
}
result
}
#[inline]
fn wrapping_neg(self) -> Self {
let mut result = Self::zero();
for i in 0..N {
result.limbs[i] = !self.limbs[i];
}
result.wrapping_add(Self::from_u64(1))
}
#[inline]
fn wrapping_mul(self, rhs: Self) -> Self {
let negative = self.is_negative() != rhs.is_negative();
let a = self.wrapping_abs();
let b = rhs.wrapping_abs();
let a_len = a.significant_limbs();
let b_len = b.significant_limbs();
let mut result = Self::zero();
for i in 0..a_len {
if a.limbs[i] == 0 {
continue;
}
let mut carry = 0u128;
let j_end = b_len.min(N - i);
for j in 0..j_end {
let k = i + j;
let acc = u128::from(a.limbs[i]) * u128::from(b.limbs[j])
+ u128::from(result.limbs[k])
+ carry;
#[allow(clippy::cast_possible_truncation)]
let digit = acc as u64;
result.limbs[k] = digit;
carry = acc >> 64;
}
let mut k = i + j_end;
while carry != 0 && k < N {
let acc = u128::from(result.limbs[k]) + carry;
#[allow(clippy::cast_possible_truncation)]
let digit = acc as u64;
result.limbs[k] = digit;
carry = acc >> 64;
k += 1;
}
}
if negative {
result.wrapping_neg()
} else {
result
}
}
#[inline]
fn wrapping_shl(self, shift: usize) -> Self {
if shift >= N * 64 {
return Self::zero();
}
let word_shift = shift / 64;
let bit_shift = shift % 64;
let mut result = Self::zero();
for i in word_shift..N {
let src = i - word_shift;
result.limbs[i] |= self.limbs[src] << bit_shift;
if bit_shift != 0 && src > 0 {
result.limbs[i] |= self.limbs[src - 1] >> (64 - bit_shift);
}
}
result
}
}
impl<const N: usize> Add for WideInt<N> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
self.wrapping_add(rhs)
}
}
impl<const N: usize> Sub for WideInt<N> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
self.wrapping_add(rhs.wrapping_neg())
}
}
impl<const N: usize> Neg for WideInt<N> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
self.wrapping_neg()
}
}
impl<const N: usize> Mul for WideInt<N> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
self.wrapping_mul(rhs)
}
}
impl<const N: usize> Shl<usize> for WideInt<N> {
type Output = Self;
#[inline]
fn shl(self, rhs: usize) -> Self {
self.wrapping_shl(rhs)
}
}
fn f64_to_wide_int<const N: usize>(x: f64) -> WideInt<N> {
if x == 0.0 {
return WideInt::zero();
}
let bits = x.to_bits();
let negative = (bits >> 63) != 0;
let biased_exp = ((bits >> 52) & 0x7FF) as u32;
let mantissa = bits & 0x000F_FFFF_FFFF_FFFF;
let (integer_mantissa, shift) = if biased_exp > 0 {
((1u64 << 52) | mantissa, (biased_exp - 1) as usize)
} else {
(mantissa, 0)
};
let magnitude = WideInt::from_u64(integer_mantissa) << shift;
if negative { -magnitude } else { magnitude }
}
#[inline]
pub(crate) fn f64_to_orient_int(x: f64) -> Orient2dInt {
f64_to_wide_int(x)
}
#[inline]
pub(crate) fn f64_to_incircle_int(x: f64) -> Incircle2dInt {
f64_to_wide_int(x)
}
#[inline]
pub(crate) fn f64_to_orient3d_int(x: f64) -> Orient3dInt {
f64_to_wide_int(x)
}
#[inline]
pub(crate) fn f64_to_insphere_int(x: f64) -> InsphereInt {
f64_to_wide_int(x)
}
#[inline]
pub(crate) fn f64_to_rational_int(x: f64) -> RationalCoordInt {
f64_to_wide_int(x)
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_possible_wrap,
clippy::range_plus_one,
clippy::no_effect_underscore_binding
)]
mod tests {
use super::*;
type W = WideInt<66>;
fn one() -> W {
W::one()
}
fn pow2(k: usize) -> W {
one() << k
}
#[test]
fn fits_in_one_signed_bit_admits_only_zero_and_minus_one() {
assert!(W::zero().fits_in_signed_bits(1), "0 fits in 1 signed bit");
assert!((-one()).fits_in_signed_bits(1), "-1 fits in 1 signed bit");
assert!(
!one().fits_in_signed_bits(1),
"1 does NOT fit in 1 signed bit"
);
assert!(
!(-one() - one()).fits_in_signed_bits(1),
"-2 does NOT fit in 1 signed bit"
);
}
#[test]
fn fits_in_full_width_is_always_true() {
for v in [W::zero(), one(), -one(), pow2(4000), -pow2(4000)] {
assert!(
v.fits_in_signed_bits(66 * 64),
"every value fits in the allocated width"
);
}
}
#[test]
fn fits_tracks_the_exact_signed_width_across_limb_boundaries() {
for k in [0_usize, 1, 62, 63, 64, 65, 127, 128, 1073, 2097] {
let v = pow2(k);
assert!(
v.fits_in_signed_bits(k + 2),
"2^{k} must fit in {} signed bits",
k + 2
);
assert!(
!v.fits_in_signed_bits(k + 1),
"2^{k} must NOT fit in {} signed bits",
k + 1
);
}
}
#[test]
fn negative_power_of_two_fits_one_bit_tighter_than_positive() {
for k in [1_usize, 63, 64, 1073] {
let v = -pow2(k);
assert!(
v.fits_in_signed_bits(k + 1),
"-2^{k} must fit in {} signed bits",
k + 1
);
assert!(
!v.fits_in_signed_bits(k),
"-2^{k} must NOT fit in {k} signed bits"
);
}
}
#[test]
fn lemma_s_scaled_coordinate_needs_exactly_2099_signed_bits() {
let m = f64_to_orient_int(f64::MAX);
assert!(
m.fits_in_signed_bits(2099),
"Lemma S: a scaled finite f64 fits in 2099 signed bits"
);
assert!(
!m.fits_in_signed_bits(2098),
"Lemma S is tight: f64::MAX scaled needs all 2099 bits"
);
assert!(f64_to_orient_int(f64::MIN).fits_in_signed_bits(2099));
}
#[test]
fn corollary_s1_coordinate_difference_needs_exactly_2100_signed_bits() {
let d = f64_to_orient_int(f64::MAX) - f64_to_orient_int(f64::MIN);
assert!(
d.fits_in_signed_bits(2100),
"S1: a scaled coordinate difference fits in 2100 signed bits"
);
assert!(
!d.fits_in_signed_bits(2099),
"S1 is tight: MAX − MIN needs all 2100 bits"
);
}
#[test]
fn f64_conversion_is_exact_at_the_representable_extremes() {
assert!(f64_to_orient_int(0.0).is_zero());
assert!(
f64_to_orient_int(-0.0).is_zero(),
"negative zero scales to integer zero, not to -0"
);
assert_eq!(
f64_to_orient_int(f64::from_bits(1)),
one(),
"the minimum positive subnormal 2^-1074 scales to exactly 1; \
this is why the scaling factor is 2^1074"
);
assert_eq!(f64_to_orient_int(1.0), pow2(1074));
assert_eq!(f64_to_orient_int(-1.0), -pow2(1074));
assert_eq!(f64_to_orient_int(2.0), pow2(1075));
assert_eq!(f64_to_orient_int(0.5), pow2(1073));
}
#[test]
fn widen_sign_extends_rather_than_zero_extends() {
let narrow: WideInt<66> = -pow2(2000);
let wide: WideInt<198> = narrow.widen();
assert!(
wide.is_negative(),
"a negative value stays negative when widened"
);
let negated: WideInt<198> = (-narrow).widen();
assert!(
(wide + negated).is_zero(),
"widening must preserve the value"
);
let positive: WideInt<198> = pow2(2000).widen();
assert!(positive.is_positive());
assert!(positive.fits_in_signed_bits(2002));
assert!(!positive.fits_in_signed_bits(2001));
}
#[test]
fn mul_propagates_the_carry_past_the_trimmed_inner_loop() {
let a = W::from_u64(u64::MAX);
let expected = pow2(128) - pow2(65) + one();
assert_eq!(a * a, expected, "(2^64 - 1)^2 = 2^128 - 2^65 + 1");
}
#[test]
fn mul_of_powers_of_two_is_a_shift() {
for (a, b) in [
(0_usize, 0_usize),
(1, 1),
(63, 1),
(64, 64),
(1000, 1074),
(2097, 2097),
] {
assert_eq!(
pow2(a) * pow2(b),
pow2(a + b),
"2^{a} * 2^{b} must equal 2^{}",
a + b
);
}
}
#[test]
fn mul_applies_the_sign_of_the_product() {
let a = pow2(1000);
let b = pow2(74);
let p = pow2(1074);
assert_eq!(a * b, p);
assert_eq!((-a) * b, -p);
assert_eq!(a * (-b), -p);
assert_eq!((-a) * (-b), p, "two negatives give a positive product");
}
#[test]
fn mul_by_zero_and_one() {
let a = pow2(2000) - pow2(37) + one();
assert!((a * W::zero()).is_zero());
assert_eq!(a * one(), a);
assert_eq!(a * (-one()), -a);
}
#[test]
fn mul_is_commutative_for_asymmetric_operand_widths() {
let wide = pow2(2000) + pow2(1000) + one();
let narrow = W::from_u64(3);
assert_eq!(wide * narrow, narrow * wide);
let other = pow2(130) - one();
assert_eq!(wide * other, other * wide);
}
}