pub(crate) mod traits;
pub(crate) mod compute_limbs;
pub use traits::BigInt;
use crate::int::algos::div::div_fixed::div_rem_mag_fixed;
use crate::int::algos::div::div_rem::div_rem;
use crate::int::algos::support::limbs::{
add_assign_fixed, bit_len_fixed, cmp_cross, cmp_fixed, is_zero_fixed, max_n_limbs, shl,
shl_fixed, shr_fixed, sub_assign_fixed,
};
use crate::int::algos::mul::mul_schoolbook::{mul_low_fixed, mul_schoolbook};
use crate::int::policy::add::dispatch as add_dispatch;
use crate::int::policy::cmp::dispatch as cmp_dispatch;
use crate::int::policy::cube::dispatch as cube_dispatch;
use crate::int::policy::div_rem::dispatch as div_rem_dispatch;
use crate::int::policy::eq::dispatch as eq_dispatch;
use crate::int::policy::icbrt::dispatch as icbrt_dispatch;
use crate::int::policy::hypot::dispatch as hypot_dispatch;
use crate::int::policy::isqrt::dispatch as isqrt_dispatch;
use crate::int::policy::mul::dispatch as mul_fast;
use crate::int::policy::neg::dispatch as neg_dispatch;
use crate::int::policy::sum_sq::dispatch as sum_sq_dispatch;
use crate::int::policy::pow::dispatch as pow_dispatch;
use crate::int::policy::rem::dispatch as rem_dispatch;
use crate::int::policy::sqr::dispatch as sqr_dispatch;
use crate::int::policy::sub::dispatch as sub_dispatch;
use crate::support::int_fmt::fmt_into;
use core::cmp::Ordering;
use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Uint<const N: usize> {
limbs: [u64; N],
}
#[derive(Clone, Copy, Eq, Hash, Debug)]
pub struct Int<const N: usize> {
limbs: [u64; N],
}
impl<const N: usize> Uint<N> {
pub const LIMBS: usize = N;
pub const BITS: u32 = (N as u32) * 64;
pub const ZERO: Self = Self { limbs: [0; N] };
pub const ONE: Self = {
let mut limbs = [0u64; N];
limbs[0] = 1;
Self { limbs }
};
pub const MAX: Self = Self {
limbs: [u64::MAX; N],
};
#[inline]
pub const fn from_limbs(limbs: [u64; N]) -> Self {
Self { limbs }
}
#[inline]
pub const fn as_limbs(&self) -> &[u64; N] {
&self.limbs
}
#[inline]
pub fn wrapping_add(mut self, rhs: Self) -> Self {
add_assign_fixed(&mut self.limbs, &rhs.limbs);
self
}
#[inline]
pub fn wrapping_sub(mut self, rhs: Self) -> Self {
sub_assign_fixed(&mut self.limbs, &rhs.limbs);
self
}
#[inline]
pub fn wrapping_mul(self, rhs: Self) -> Self {
let mut out = [0u64; N];
mul_low_fixed(&self.limbs, &rhs.limbs, &mut out);
Self { limbs: out }
}
#[inline]
pub const fn wrapping_sqr(self) -> Self {
sqr_dispatch(self)
}
#[inline]
pub const fn wrapping_cube(self) -> Self {
cube_dispatch(self)
}
#[inline]
pub fn checked_add(mut self, rhs: Self) -> Option<Self> {
let carry = add_assign_fixed(&mut self.limbs, &rhs.limbs);
if carry { None } else { Some(self) }
}
#[inline]
pub fn checked_sub(mut self, rhs: Self) -> Option<Self> {
let borrow = sub_assign_fixed(&mut self.limbs, &rhs.limbs);
if borrow { None } else { Some(self) }
}
#[inline]
pub fn checked_mul(self, rhs: Self) -> Option<Self> {
let (a, b) = (&self.limbs, &rhs.limbs);
let mut out = [0u64; N];
let mut overflow = false;
let mut i = 0;
while i < N {
let ai = a[i];
if ai != 0 {
let mut carry: u64 = 0;
let mut j = 0;
while j < N {
let prod = (ai as u128) * (b[j] as u128);
if i + j < N {
let v = prod + (out[i + j] as u128) + (carry as u128);
out[i + j] = v as u64;
carry = (v >> 64) as u64;
} else if prod != 0 || carry != 0 {
overflow = true;
carry = 0;
}
j += 1;
}
if carry != 0 {
overflow = true;
}
}
i += 1;
}
if overflow {
None
} else {
Some(Self { limbs: out })
}
}
#[inline]
pub fn wrapping_div(self, rhs: Self) -> Self {
assert!(!rhs.is_zero(), "attempt to divide by zero");
let mut quot = [0u64; N];
let mut rem = [0u64; N];
div_rem(&self.limbs, &rhs.limbs, &mut quot, &mut rem);
Self { limbs: quot }
}
#[inline]
pub fn wrapping_rem(self, rhs: Self) -> Self {
assert!(
!rhs.is_zero(),
"attempt to calculate the remainder with a divisor of zero"
);
let mut quot = [0u64; N];
let mut rem = [0u64; N];
div_rem(&self.limbs, &rhs.limbs, &mut quot, &mut rem);
Self { limbs: rem }
}
#[allow(clippy::should_implement_trait)]
#[inline]
pub fn bitand(self, rhs: Self) -> Self {
let mut out = [0u64; N];
let mut i = 0;
while i < N {
out[i] = self.limbs[i] & rhs.limbs[i];
i += 1;
}
Self { limbs: out }
}
#[allow(clippy::should_implement_trait)]
#[inline]
pub fn bitor(self, rhs: Self) -> Self {
let mut out = [0u64; N];
let mut i = 0;
while i < N {
out[i] = self.limbs[i] | rhs.limbs[i];
i += 1;
}
Self { limbs: out }
}
#[allow(clippy::should_implement_trait)]
#[inline]
pub fn bitxor(self, rhs: Self) -> Self {
let mut out = [0u64; N];
let mut i = 0;
while i < N {
out[i] = self.limbs[i] ^ rhs.limbs[i];
i += 1;
}
Self { limbs: out }
}
#[allow(clippy::should_implement_trait)]
#[inline]
pub fn not(self) -> Self {
let mut out = [0u64; N];
let mut i = 0;
while i < N {
out[i] = !self.limbs[i];
i += 1;
}
Self { limbs: out }
}
#[allow(clippy::should_implement_trait)]
#[inline]
pub fn shl(self, shift: u32) -> Self {
let mut out = [0u64; N];
shl_fixed(&self.limbs, shift, &mut out);
Self { limbs: out }
}
#[allow(clippy::should_implement_trait)]
#[inline]
pub fn shr(self, shift: u32) -> Self {
let mut out = [0u64; N];
shr_fixed(&self.limbs, shift, &mut out);
Self { limbs: out }
}
#[inline]
pub fn is_zero(&self) -> bool {
is_zero_fixed(&self.limbs)
}
#[inline]
pub const fn bit_length(&self) -> u32 {
bit_len_fixed(&self.limbs)
}
#[inline]
pub const fn leading_zeros(&self) -> u32 {
Self::BITS - self.bit_length()
}
#[inline]
pub fn is_one(&self) -> bool {
if N == 0 || self.limbs[0] != 1 {
return false;
}
let mut i = 1;
while i < N {
if self.limbs[i] != 0 {
return false;
}
i += 1;
}
true
}
#[inline]
pub const fn wrapping_pow(self, exp: u32) -> Self {
pow_dispatch(self, exp)
}
#[inline]
pub fn checked_pow(self, mut exp: u32) -> Option<Self> {
let mut acc = Self::ONE;
let mut base = self;
loop {
if exp & 1 == 1 {
acc = acc.checked_mul(base)?;
}
exp >>= 1;
if exp == 0 {
break;
}
base = base.checked_mul(base)?;
}
Some(acc)
}
#[inline]
pub fn pow(self, exp: u32) -> Self {
pow_dispatch(self, exp)
}
#[inline]
pub fn isqrt(self) -> Self {
isqrt_dispatch(self)
}
#[inline]
pub fn sqr(self) -> Self {
sqr_dispatch(self)
}
#[inline]
pub fn cube(self) -> Self {
cube_dispatch(self)
}
#[inline]
pub fn icbrt(self) -> Self {
icbrt_dispatch(self)
}
pub fn root_int(self, k: u32) -> (Self, bool) {
debug_assert!(k >= 1, "root_int requires k >= 1");
if k == 1 {
return (self, true);
}
if self.is_zero() {
return (Self::ZERO, true);
}
if self.is_one() {
return (Self::ONE, true);
}
if k == 2 {
let r = self.isqrt();
return (r, r.wrapping_sqr() == self);
}
let len = self.bit_length();
let seed_shift = len.div_ceil(k);
let mut s = Self::ONE.shl(seed_shift);
loop {
let pow_km1 = s.wrapping_pow(k - 1);
let quot = self.wrapping_div(pow_km1);
let mut t = Self::ZERO;
let mut c = 0;
while c < k - 1 {
t = t.wrapping_add(s);
c += 1;
}
t = t.wrapping_add(quot);
let u = t.wrapping_div(Self::from_u64(k as u64));
if u >= s {
break;
}
s = u;
}
let exact = s.checked_pow(k).is_some_and(|p| p == self);
(s, exact)
}
#[inline]
pub(crate) fn from_u64(value: u64) -> Self {
let mut limbs = [0u64; N];
if N > 0 {
limbs[0] = value;
}
Self { limbs }
}
#[inline]
pub(crate) const fn from_u128(v: u128) -> Self {
let mut limbs = [0u64; N];
if N > 0 {
limbs[0] = v as u64;
}
if N > 1 {
limbs[1] = (v >> 64) as u64;
}
Self { limbs }
}
#[inline]
pub(crate) const fn try_from_u128(v: u128) -> Option<Self> {
if N >= 2 || v <= u64::MAX as u128 {
Some(Self::from_u128(v))
} else {
None
}
}
#[inline]
pub const fn cast_signed(self) -> Int<N> {
Int::from_limbs(self.limbs)
}
pub fn as_f64(self) -> f64 {
let radix: f64 = 18_446_744_073_709_551_616.0; let mut acc = 0.0f64;
let mut i = N;
while i > 0 {
i -= 1;
acc = acc * radix + self.limbs[i] as f64;
}
acc
}
#[inline]
pub const fn count_ones(self) -> u32 {
let mut total = 0;
let mut i = 0;
while i < N {
total += self.limbs[i].count_ones();
i += 1;
}
total
}
#[inline]
pub const fn is_power_of_two(self) -> bool {
self.count_ones() == 1
}
pub fn next_power_of_two(self) -> Self {
if self.is_zero() {
return Self::ONE;
}
if self.is_power_of_two() {
return self;
}
let bits = self.bit_length();
let mut out = [0u64; N];
if (bits as usize) < N * 64 {
out[(bits / 64) as usize] = 1u64 << (bits % 64);
}
Self { limbs: out }
}
pub const fn from_str_radix(s: &str, radix: u32) -> Result<Self, ()> {
if radix != 10 {
return Err(());
}
let bytes = s.as_bytes();
if bytes.is_empty() {
return Err(());
}
let mut acc = [0u64; N];
let mut k = 0;
while k < bytes.len() {
let ch = bytes[k];
if ch < b'0' || ch > b'9' {
return Err(());
}
let d = (ch - b'0') as u64;
let mut carry: u64 = d;
let mut j = 0;
while j < N {
let p = (acc[j] as u128) * 10u128 + (carry as u128);
acc[j] = p as u64;
carry = (p >> 64) as u64;
j += 1;
}
k += 1;
}
Ok(Self { limbs: acc })
}
}
impl<const N: usize> Add for Uint<N> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
self.wrapping_add(rhs)
}
}
impl<const N: usize> Sub for Uint<N> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
self.wrapping_sub(rhs)
}
}
impl<const N: usize> Mul for Uint<N> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
self.wrapping_mul(rhs)
}
}
impl<const N: usize> BitAnd for Uint<N> {
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self {
Uint::bitand(self, rhs)
}
}
impl<const N: usize> BitOr for Uint<N> {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Uint::bitor(self, rhs)
}
}
impl<const N: usize> BitXor for Uint<N> {
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self {
Uint::bitxor(self, rhs)
}
}
impl<const N: usize> Not for Uint<N> {
type Output = Self;
#[inline]
fn not(self) -> Self {
Uint::not(self)
}
}
impl<const N: usize> Shl<u32> for Uint<N> {
type Output = Self;
#[inline]
fn shl(self, shift: u32) -> Self {
Uint::shl(self, shift)
}
}
impl<const N: usize> Shr<u32> for Uint<N> {
type Output = Self;
#[inline]
fn shr(self, shift: u32) -> Self {
Uint::shr(self, shift)
}
}
impl<const N: usize> Div for Uint<N> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
let mut q = [0u64; N];
let mut r = [0u64; N];
div_rem_dispatch(&self.limbs, &rhs.limbs, &mut q, &mut r);
Self { limbs: q }
}
}
impl<const N: usize> Rem for Uint<N> {
type Output = Self;
#[inline]
fn rem(self, rhs: Self) -> Self {
let mut q = [0u64; N];
let mut r = [0u64; N];
div_rem_dispatch(&self.limbs, &rhs.limbs, &mut q, &mut r);
Self { limbs: r }
}
}
impl<const N: usize> PartialOrd for Uint<N> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<const N: usize> Ord for Uint<N> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
match cmp_fixed(&self.limbs, &other.limbs) {
-1 => Ordering::Less,
1 => Ordering::Greater,
_ => Ordering::Equal,
}
}
}
impl<const N: usize> Int<N> {
pub const LIMBS: usize = N;
pub const BITS: u32 = (N as u32) * 64;
pub const ZERO: Self = Self { limbs: [0; N] };
pub const ONE: Self = {
let mut limbs = [0u64; N];
limbs[0] = 1;
Self { limbs }
};
pub const MAX: Self = {
let mut limbs = [u64::MAX; N];
limbs[N - 1] = i64::MAX as u64;
Self { limbs }
};
pub const MIN: Self = {
let mut limbs = [0u64; N];
limbs[N - 1] = 1u64 << 63;
Self { limbs }
};
#[inline]
pub const fn from_limbs(limbs: [u64; N]) -> Self {
Self { limbs }
}
#[inline]
pub const fn as_limbs(&self) -> &[u64; N] {
&self.limbs
}
#[inline]
pub const fn is_zero(&self) -> bool {
is_zero_fixed(&self.limbs)
}
#[inline]
pub const fn is_negative(&self) -> bool {
N > 0 && (self.limbs[N - 1] >> 63) == 1
}
#[inline]
pub const fn is_positive(&self) -> bool {
!self.is_negative() && !self.is_zero()
}
#[inline]
pub const fn wrapping_neg(self) -> Self {
neg_dispatch(self)
}
#[inline]
pub const fn wrapping_add(self, rhs: Self) -> Self {
add_dispatch(self, rhs)
}
#[inline]
pub const fn wrapping_sub(self, rhs: Self) -> Self {
sub_dispatch(self, rhs)
}
#[inline]
pub const fn wrapping_mul(self, rhs: Self) -> Self {
let mut out = [0u64; N];
mul_low_fixed(&self.limbs, &rhs.limbs, &mut out);
Self { limbs: out }
}
#[inline]
pub const fn abs(self) -> Self {
if self.is_negative() {
self.wrapping_neg()
} else {
self
}
}
#[inline]
pub fn signum(&self) -> i32 {
if self.is_zero() {
0
} else if self.is_negative() {
-1
} else {
1
}
}
#[inline]
pub(crate) const fn from_i64(value: i64) -> Self {
let fill = if value < 0 { u64::MAX } else { 0 };
let mut limbs = [fill; N];
if N > 0 {
limbs[0] = value as u64;
}
Self { limbs }
}
#[inline]
pub(crate) const fn from_i8(value: i8) -> Self {
Self::from_i64(value as i64)
}
#[inline]
pub(crate) const fn from_i16(value: i16) -> Self {
Self::from_i64(value as i64)
}
#[inline]
pub(crate) const fn from_i32(value: i32) -> Self {
Self::from_i64(value as i64)
}
#[inline]
pub(crate) const fn from_u8(value: u8) -> Self {
Self::from_u64_unsigned(value as u64)
}
#[inline]
pub(crate) const fn from_u16(value: u16) -> Self {
Self::from_u64_unsigned(value as u64)
}
#[inline]
pub(crate) const fn from_u32(value: u32) -> Self {
Self::from_u64_unsigned(value as u64)
}
#[inline]
const fn from_u64_unsigned(value: u64) -> Self {
let mut limbs = [0u64; N];
if N > 0 {
limbs[0] = value;
}
Self { limbs }
}
#[inline]
pub(crate) const fn try_from_u64(value: u64) -> Option<Self> {
if N >= 2 || value <= i64::MAX as u64 {
Some(Self::from_u64_unsigned(value))
} else {
None
}
}
#[inline]
pub(crate) const fn try_from_i128(v: i128) -> Option<Self> {
let mag = v.unsigned_abs();
let built = Self::from_mag_limbs(&[mag as u64, (mag >> 64) as u64], v < 0);
if N >= 2 || built.as_i128() == v {
Some(built)
} else {
None
}
}
#[inline]
pub(crate) const fn try_from_u128(v: u128) -> Option<Self> {
let built = Self::from_mag_limbs(&[v as u64, (v >> 64) as u64], false);
if N >= 3 {
Some(built)
} else if built.is_negative() {
None
} else if N >= 2 || built.as_i128() as u128 == v {
Some(built)
} else {
None
}
}
#[inline]
pub(crate) const fn to_i64(self) -> i64 {
self.as_i128() as i64
}
#[inline]
pub(crate) const fn to_i128(self) -> i128 {
self.as_i128()
}
#[inline]
pub(crate) fn try_to_i32(self) -> Option<i32> {
match self.to_i128_checked() {
Some(v) if v >= i32::MIN as i128 && v <= i32::MAX as i128 => Some(v as i32),
_ => None,
}
}
#[inline]
pub(crate) fn try_to_u32(self) -> Option<u32> {
match self.to_u128_checked() {
Some(v) if v <= u32::MAX as u128 => Some(v as u32),
_ => None,
}
}
#[inline]
pub(crate) fn try_to_i64(self) -> Option<i64> {
match self.to_i128_checked() {
Some(v) if v >= i64::MIN as i128 && v <= i64::MAX as i128 => Some(v as i64),
_ => None,
}
}
#[inline]
pub(crate) fn try_to_u64(self) -> Option<u64> {
match self.to_u128_checked() {
Some(v) if v <= u64::MAX as u128 => Some(v as u64),
_ => None,
}
}
#[inline]
pub(crate) fn try_to_i128(self) -> Option<i128> {
self.to_i128_checked()
}
#[inline]
pub(crate) fn try_to_u128(self) -> Option<u128> {
self.to_u128_checked()
}
#[inline]
pub fn is_one(&self) -> bool {
if N == 0 || self.limbs[0] != 1 {
return false;
}
let mut i = 1;
while i < N {
if self.limbs[i] != 0 {
return false;
}
i += 1;
}
true
}
#[inline]
pub fn min_value() -> Self {
let mut limbs = [0u64; N];
if N > 0 {
limbs[N - 1] = 1u64 << 63;
}
Self { limbs }
}
#[inline]
pub fn max_value() -> Self {
let mut limbs = [u64::MAX; N];
if N > 0 {
limbs[N - 1] = u64::MAX >> 1;
}
Self { limbs }
}
#[inline]
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
crate::int::policy::add::dispatch_checked(self, rhs)
}
#[inline]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
crate::int::policy::sub::dispatch_checked(self, rhs)
}
#[inline]
pub fn checked_mul(self, rhs: Self) -> Option<Self> {
if self.is_zero() || rhs.is_zero() {
return Some(Self::ZERO);
}
let neg = self.is_negative() ^ rhs.is_negative();
let ma = Uint::<N>::from_limbs(*self.abs().as_limbs());
let mb = Uint::<N>::from_limbs(*rhs.abs().as_limbs());
let prod = ma.checked_mul(mb)?;
let signed = Self::from_limbs(*prod.as_limbs());
if neg {
let r = signed.wrapping_neg();
if r.is_negative() || r.is_zero() {
Some(r)
} else {
None
}
} else if signed.is_negative() {
None
} else {
Some(signed)
}
}
#[inline]
pub const fn wrapping_pow(self, exp: u32) -> Self {
Self::from_limbs(*pow_dispatch(self.cast_unsigned(), exp).as_limbs())
}
#[inline]
pub fn checked_pow(self, mut exp: u32) -> Option<Self> {
let mut acc = Self::ONE;
let mut base = self;
loop {
if exp & 1 == 1 {
acc = acc.checked_mul(base)?;
}
exp >>= 1;
if exp == 0 {
break;
}
base = base.checked_mul(base)?;
}
Some(acc)
}
#[inline]
pub const fn wrapping_sqr(self) -> Self {
Self::from_limbs(*sqr_dispatch(self.cast_unsigned()).as_limbs())
}
#[inline]
pub const fn wrapping_cube(self) -> Self {
Self::from_limbs(*cube_dispatch(self.cast_unsigned()).as_limbs())
}
#[inline]
pub const fn bit_length(&self) -> u32 {
bit_len_fixed(self.abs().as_limbs())
}
#[inline]
pub const fn leading_zeros(&self) -> u32 {
if self.is_negative() {
0
} else {
Self::BITS - self.bit_length()
}
}
pub const TEN: Self = {
let mut limbs = [0u64; N];
if N > 0 {
limbs[0] = 10;
}
Self { limbs }
};
#[inline]
pub const fn unsigned_abs(self) -> Uint<N> {
Uint::from_limbs(*self.abs().as_limbs())
}
#[inline]
pub fn negate(self) -> Self {
self.wrapping_neg()
}
#[inline]
pub fn div_rem(self, rhs: Self) -> (Self, Self) {
assert!(!rhs.is_zero(), "attempt to divide by zero");
let neg_q = self.is_negative() ^ rhs.is_negative();
let neg_r = self.is_negative();
let mut quot = [0u64; N];
let mut rem = [0u64; N];
div_rem_mag_fixed::<N>(
self.unsigned_abs().as_limbs(),
rhs.unsigned_abs().as_limbs(),
&mut quot,
&mut rem,
);
let q = Self::from_mag_limbs(", neg_q);
let r = Self::from_mag_limbs(&rem, neg_r);
(q, r)
}
#[inline]
pub(crate) const fn from_mag_limbs(mag: &[u64], negative: bool) -> Self {
let mut out = [0u64; N];
let n = if mag.len() < N { mag.len() } else { N };
let mut i = 0;
while i < n {
out[i] = mag[i];
i += 1;
}
let v = Self { limbs: out };
if negative && !is_zero_fixed(&v.limbs) {
v.wrapping_neg()
} else {
v
}
}
#[inline]
pub const fn bit(self, idx: u32) -> bool {
let limb = (idx / 64) as usize;
if limb >= N {
return self.is_negative();
}
(self.limbs[limb] >> (idx % 64)) & 1 == 1
}
#[inline]
pub(crate) const fn from_i128(v: i128) -> Self {
if N <= 2 {
let mut limbs = [0u64; N];
limbs[0] = v as u64;
if N == 2 {
limbs[1] = (v >> 64) as u64;
}
return Self { limbs };
}
let mag = v.unsigned_abs();
Self::from_mag_limbs(&[mag as u64, (mag >> 64) as u64], v < 0)
}
#[inline]
pub(crate) const fn from_u128(v: u128) -> Self {
Self::from_mag_limbs(&[v as u64, (v >> 64) as u64], false)
}
#[inline]
pub const fn from_limbs_le(limbs: [u64; N]) -> Self {
Self { limbs }
}
#[inline]
pub const fn limbs_le(self) -> [u64; N] {
self.limbs
}
#[inline]
pub fn mul_u64(self, n: u64) -> Self {
let mag = *self.unsigned_abs().as_limbs();
let mut prod = [0u64; N];
let mut carry: u64 = 0;
let mut i = 0;
while i < N {
let p = (mag[i] as u128) * (n as u128) + (carry as u128);
prod[i] = p as u64;
carry = (p >> 64) as u64;
i += 1;
}
if carry != 0 {
panic!("Int: mul overflow");
}
let negative = self.is_negative();
let r = Self::from_mag_limbs(&prod, negative);
if !r.is_zero() && r.is_negative() != negative {
panic!("Int: mul overflow");
}
r
}
pub fn to_i128_checked(self) -> Option<i128> {
let negative = self.is_negative();
let mag = *self.unsigned_abs().as_limbs();
let mut i = 2;
while i < N {
if mag[i] != 0 {
return None;
}
i += 1;
}
let lo = if N > 0 { mag[0] as u128 } else { 0 };
let hi = if N > 1 { mag[1] as u128 } else { 0 };
let lo_u128 = lo | (hi << 64);
if negative {
if lo_u128 <= (i128::MAX as u128) + 1 {
Some((lo_u128 as i128).wrapping_neg())
} else {
None
}
} else if lo_u128 <= i128::MAX as u128 {
Some(lo_u128 as i128)
} else {
None
}
}
pub fn to_u128_checked(self) -> Option<u128> {
if self.is_negative() {
return None;
}
let mut i = 2;
while i < N {
if self.limbs[i] != 0 {
return None;
}
i += 1;
}
let lo = if N > 0 { self.limbs[0] as u128 } else { 0 };
let hi = if N > 1 { self.limbs[1] as u128 } else { 0 };
Some(lo | (hi << 64))
}
pub fn to_f64(self) -> f64 {
let mag = *self.unsigned_abs().as_limbs();
let radix: f64 = 18_446_744_073_709_551_616.0; let mut acc = 0.0f64;
let mut i = N;
while i > 0 {
i -= 1;
acc = acc * radix + mag[i] as f64;
}
if self.is_negative() { -acc } else { acc }
}
pub fn to_f32(self) -> f32 {
self.to_f64() as f32
}
pub(crate) const fn try_from_f64(v: f64) -> Option<Self> {
let bits = v.to_bits();
let negative = (bits >> 63) & 1 == 1;
let exp = ((bits >> 52) & 0x7ff) as i32;
let mant = bits & 0x000f_ffff_ffff_ffff;
if exp == 0x7ff {
return None;
}
if exp == 0 {
return if mant == 0 { Some(Self::ZERO) } else { None };
}
let significand = (1u64 << 52) | mant; let shift = exp - 1075; Self::from_significand_shift(significand, shift, negative)
}
pub(crate) const fn try_from_f32(v: f32) -> Option<Self> {
let bits = v.to_bits();
let negative = (bits >> 31) & 1 == 1;
let exp = ((bits >> 23) & 0xff) as i32;
let mant = (bits & 0x007f_ffff) as u64;
if exp == 0xff {
return None;
}
if exp == 0 {
return if mant == 0 { Some(Self::ZERO) } else { None };
}
let significand = (1u64 << 23) | mant; let shift = exp - 150; Self::from_significand_shift(significand, shift, negative)
}
const fn from_significand_shift(significand: u64, shift: i32, negative: bool) -> Option<Self> {
if shift < 0 {
let s = (-shift) as u32;
if s >= 64 {
return None;
}
if significand & ((1u64 << s) - 1) != 0 {
return None;
}
let mag = significand >> s; let built = Self::from_mag_limbs(&[mag], negative);
Self::accept_signed(built, negative)
} else {
let s = shift as u32;
let limb_off = (s / 64) as usize;
let bit_off = s % 64;
let mut mag = [0u64; N];
let lo = significand << bit_off;
let hi = if bit_off == 0 {
0
} else {
significand >> (64 - bit_off)
};
if limb_off < N {
mag[limb_off] = lo;
} else if lo != 0 {
return None;
}
if hi != 0 {
if limb_off + 1 < N {
mag[limb_off + 1] = hi;
} else {
return None;
}
}
let built = Self::from_mag_limbs(&mag, negative);
Self::accept_signed(built, negative)
}
}
#[inline]
const fn accept_signed(built: Self, negative: bool) -> Option<Self> {
if is_zero_fixed(&built.limbs) {
Some(built)
} else if built.is_negative() != negative {
None
} else {
Some(built)
}
}
pub fn from_f64(v: f64) -> Self {
if !v.is_finite() {
return Self::ZERO;
}
let negative = v < 0.0;
let mut m = if negative { -v } else { v };
let radix: f64 = 18_446_744_073_709_551_616.0; let mut limbs = [0u64; N];
let mut i = 0;
while m >= 1.0 && i < N {
let rem = m % radix;
limbs[i] = rem as u64;
m = (m - rem) / radix;
i += 1;
}
if m >= 1.0 {
return if negative {
Self::min_value()
} else {
Self::max_value()
};
}
Self::from_mag_limbs(&limbs, negative)
}
pub const fn from_str_radix(s: &str, radix: u32) -> Result<Self, ()> {
if radix != 10 {
return Err(());
}
let bytes = s.as_bytes();
let (negative, start): (bool, usize) = if !bytes.is_empty() && bytes[0] == b'-' {
(true, 1)
} else {
(false, 0)
};
if start >= bytes.len() {
return Err(());
}
let mut acc = [0u64; N];
let mut k = start;
while k < bytes.len() {
let ch = bytes[k];
if ch < b'0' || ch > b'9' {
return Err(());
}
let d = (ch - b'0') as u64;
let mut carry: u64 = d;
let mut j = 0;
while j < N {
let p = (acc[j] as u128) * 10u128 + (carry as u128);
acc[j] = p as u64;
carry = (p >> 64) as u64;
j += 1;
}
k += 1;
}
Ok(Self::from_mag_limbs(&acc, negative))
}
#[inline]
pub const fn pow(self, exp: u32) -> Self {
self.wrapping_pow(exp)
}
#[inline]
pub fn isqrt(self) -> Self {
Self::from_limbs(*self.unsigned_abs().isqrt().as_limbs())
}
#[inline]
pub fn sqr(self) -> Self {
self.wrapping_sqr()
}
#[inline]
pub fn cube(self) -> Self {
self.wrapping_cube()
}
#[inline]
pub fn icbrt(self) -> Self {
Self::from_limbs(*self.unsigned_abs().icbrt().as_limbs())
}
#[inline]
#[must_use]
pub(crate) fn hypot(
self,
other: Self,
mode: crate::support::rounding::RoundingMode,
) -> Option<Self>
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
hypot_dispatch::<N>(self, other, mode)
}
#[inline]
#[must_use]
pub(crate) fn sum_sq(self, other: Self) -> Option<Self>
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
sum_sq_dispatch::<N>(self, other)
}
#[inline]
pub const fn cast_unsigned(self) -> Uint<N> {
Uint::from_limbs(self.limbs)
}
#[inline]
pub fn as_f64(self) -> f64 {
self.to_f64()
}
#[inline]
pub const fn count_ones(self) -> u32 {
let mut total = 0;
let mut i = 0;
while i < N {
total += self.limbs[i].count_ones();
i += 1;
}
total
}
#[inline]
pub const fn count_zeros(self) -> u32 {
Self::BITS - self.count_ones()
}
#[inline]
pub const fn trailing_zeros(self) -> u32 {
let mut i = 0;
while i < N {
if self.limbs[i] != 0 {
return i as u32 * 64 + self.limbs[i].trailing_zeros();
}
i += 1;
}
Self::BITS
}
#[inline]
pub const fn checked_neg(self) -> Option<Self> {
if eq_dispatch(self, Self::MIN) {
None
} else {
Some(self.wrapping_neg())
}
}
#[inline]
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
if is_zero_fixed(&rhs.limbs) {
None
} else {
Some(self.wrapping_div(rhs))
}
}
#[inline]
pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
if is_zero_fixed(&rhs.limbs) || self.is_min_neg_one(rhs) {
None
} else {
Some(self.wrapping_rem(rhs))
}
}
#[inline]
const fn is_min_neg_one(self, rhs: Self) -> bool {
cmp_fixed(&self.limbs, &Self::MIN.limbs) == 0
&& cmp_fixed(&rhs.wrapping_neg().limbs, &Self::ONE.limbs) == 0
}
#[inline]
pub const fn div_euclid(self, rhs: Self) -> Self {
let q = self.wrapping_div(rhs);
let r = self.wrapping_rem(rhs);
if r.is_negative() {
if rhs.is_negative() {
q.wrapping_add(Self::ONE)
} else {
q.wrapping_sub(Self::ONE)
}
} else {
q
}
}
#[inline]
pub const fn rem_euclid(self, rhs: Self) -> Self {
let r = self.wrapping_rem(rhs);
if r.is_negative() {
r.wrapping_add(rhs.abs())
} else {
r
}
}
#[inline]
pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
let r = self.wrapping_add(rhs);
let sa = self.is_negative();
let sb = rhs.is_negative();
let sr = r.is_negative();
(r, sa == sb && sr != sa)
}
#[inline]
pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
let r = self.wrapping_sub(rhs);
let sa = self.is_negative();
let sb = rhs.is_negative();
let sr = r.is_negative();
(r, sa != sb && sr != sa)
}
#[inline]
pub const fn overflowing_neg(self) -> (Self, bool) {
let ov = cmp_fixed(&self.limbs, &Self::MIN.limbs) == 0;
(self.wrapping_neg(), ov)
}
#[inline]
pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
if self.is_min_neg_one(rhs) {
(Self::ZERO, true)
} else {
(self.wrapping_rem(rhs), false)
}
}
#[inline]
pub const fn saturating_add(self, rhs: Self) -> Self {
match self.checked_add(rhs) {
Some(v) => v,
None => {
if self.is_negative() {
Self::MIN
} else {
Self::MAX
}
}
}
}
#[inline]
pub const fn saturating_sub(self, rhs: Self) -> Self {
match self.checked_sub(rhs) {
Some(v) => v,
None => {
if self.is_negative() {
Self::MIN
} else {
Self::MAX
}
}
}
}
#[inline]
pub const fn saturating_neg(self) -> Self {
match self.checked_neg() {
Some(v) => v,
None => Self::MAX,
}
}
#[inline]
pub fn rotate_left(self, n: u32) -> Self {
let bits = Self::BITS;
let n = n % bits;
if n == 0 {
return self;
}
let u = self.cast_unsigned();
Self::from_limbs(((u.shl(n)) | (u.shr(bits - n))).limbs)
}
#[inline]
pub fn rotate_right(self, n: u32) -> Self {
self.rotate_left(Self::BITS - (n % Self::BITS))
}
#[inline]
pub(crate) const fn as_u128(self) -> u128 {
let mag = *self.unsigned_abs().as_limbs();
let lo = if N > 0 { mag[0] as u128 } else { 0 };
let hi = if N > 1 { mag[1] as u128 } else { 0 };
lo | (hi << 64)
}
#[inline]
pub(crate) const fn as_i128(self) -> i128 {
if N <= 2 {
if N == 1 {
return (self.limbs[0] as i64) as i128;
}
let lo = self.limbs[0] as u128;
let hi = self.limbs[1] as u128;
return (lo | (hi << 64)) as i128;
}
let mag = *self.unsigned_abs().as_limbs();
let lo = if N > 0 { mag[0] as u128 } else { 0 };
let hi = if N > 1 { mag[1] as u128 } else { 0 };
let combined = lo | (hi << 64);
if self.is_negative() {
(combined as i128).wrapping_neg()
} else {
combined as i128
}
}
#[inline]
pub(crate) fn resize<T: crate::int::types::traits::BigInt>(self) -> T {
use crate::int::types::traits::BigInt as _;
self.resize_to::<T>()
}
#[inline]
pub const fn wrapping_div(self, rhs: Self) -> Self {
if is_zero_fixed(&rhs.limbs) {
panic!("attempt to divide by zero");
}
let negative = self.is_negative() ^ rhs.is_negative();
let mut q = [0u64; N];
let mut r = [0u64; N];
div_rem(
self.unsigned_abs().as_limbs(),
rhs.unsigned_abs().as_limbs(),
&mut q,
&mut r,
);
Self::from_mag_limbs(&q, negative)
}
#[inline]
pub const fn wrapping_rem(self, rhs: Self) -> Self {
if is_zero_fixed(&rhs.limbs) {
panic!("attempt to calculate the remainder with a divisor of zero");
}
let mut q = [0u64; N];
let mut r = [0u64; N];
div_rem(
self.unsigned_abs().as_limbs(),
rhs.unsigned_abs().as_limbs(),
&mut q,
&mut r,
);
Self::from_mag_limbs(&r, self.is_negative())
}
#[inline]
pub(crate) fn widen_mul<W>(self, rhs: Self) -> W
where
W: crate::int::types::traits::BigInt,
W::Scratch: crate::int::types::compute_limbs::ComputeLimbs,
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
use crate::int::types::compute_limbs::{ComputeLimbs, Limbs};
let negative = self.is_negative() ^ rhs.is_negative();
let a = *self.unsigned_abs().as_limbs();
let b = *rhs.unsigned_abs().as_limbs();
let mut prod_buf = <Limbs<N> as ComputeLimbs>::double_buffered_u64();
let prod = prod_buf.as_mut();
mul_fast::<N>(&a, &b, &mut prod[..2 * N]);
let mut u128_buf = <W::Scratch as ComputeLimbs>::single_u128();
let u128_prod = u128_buf.as_mut();
let mut i = 0;
while i < N {
u128_prod[i] = (prod[2 * i] as u128) | ((prod[2 * i + 1] as u128) << 64);
i += 1;
}
W::from_mag_sign_u128(&u128_prod[..N], negative)
}
}
impl<const N: usize> Int<N> {
#[inline]
pub(crate) const fn cmp_cross<const M: usize>(self, other: Int<M>) -> Ordering {
let sn = self.is_negative();
let so = other.is_negative();
if sn && !so {
return Ordering::Less;
}
if !sn && so {
return Ordering::Greater;
}
let a = self.unsigned_abs();
let b = other.unsigned_abs();
let c = cmp_cross(a.as_limbs(), b.as_limbs());
let c = if sn { -c } else { c };
if c < 0 {
Ordering::Less
} else if c > 0 {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
impl<const N: usize> Int<N> {
pub(crate) const fn cmp_cross_scaled<const M: usize>(
self,
other: Int<M>,
scale_diff: u32,
) -> Ordering {
let sn = self.is_negative();
let so = other.is_negative();
if sn && !so {
return Ordering::Less;
}
if !sn && so {
return Ordering::Greater;
}
let a = self.unsigned_abs();
let b = other.unsigned_abs();
let mag_cmp = if scale_diff == 0 {
cmp_cross(a.as_limbs(), b.as_limbs())
} else {
let mut pow = [0u64; max_n_limbs(4)];
pow[0] = 1;
let mut e = 0;
while e < scale_diff {
let mut carry: u128 = 0;
let mut i = 0;
while i < pow.len() {
let prod = (pow[i] as u128) * 10u128 + carry;
pow[i] = prod as u64;
carry = prod >> 64;
i += 1;
}
e += 1;
}
let mut q = [0u64; max_n_limbs(4)];
let mut r = [0u64; max_n_limbs(4)];
div_rem(a.as_limbs(), &pow, &mut q, &mut r);
let c = cmp_cross(&q, b.as_limbs());
if c != 0 {
c
} else {
let mut rk = 0;
let mut nz = false;
while rk < r.len() {
if r[rk] != 0 {
nz = true;
break;
}
rk += 1;
}
if nz {
1
} else {
0
}
}
};
let c = if sn { -mag_cmp } else { mag_cmp };
if c < 0 {
Ordering::Less
} else if c > 0 {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
impl<const N: usize> Int<N> {
pub(crate) const fn cmp_f64_exact(self, scale: u32, value: f64) -> Ordering {
let bits = value.to_bits();
let fsign = (bits >> 63) != 0;
let exp_field = ((bits >> 52) & 0x7ff) as i32;
let frac = bits & 0x000f_ffff_ffff_ffff;
let (m, e): (u64, i32) = if exp_field == 0 {
(frac, -1074)
} else {
(frac | 0x0010_0000_0000_0000, exp_field - 1075)
};
let sn = self.is_negative();
let fzero = m == 0;
if fzero {
let self_limbs = self.as_limbs();
let mut nz = false;
let mut k = 0;
while k < self_limbs.len() {
if self_limbs[k] != 0 {
nz = true;
break;
}
k += 1;
}
if !nz {
return Ordering::Equal;
}
return if sn { Ordering::Less } else { Ordering::Greater };
}
if sn && !fsign {
return Ordering::Less;
}
if !sn && fsign {
return Ordering::Greater;
}
let a = self.unsigned_abs();
let a_limbs = a.as_limbs();
let mut pow10 = [0u64; 288];
pow10[0] = 1;
let mut pe = 0;
while pe < scale {
let mut carry: u128 = 0;
let mut i = 0;
while i < pow10.len() {
let prod = (pow10[i] as u128) * 10u128 + carry;
pow10[i] = prod as u64;
carry = prod >> 64;
i += 1;
}
pe += 1;
}
let m_limbs = [m];
let mut lhs = [0u64; 288];
let mut rhs = [0u64; 288];
if e >= 0 {
let mut i = 0;
while i < a_limbs.len() {
lhs[i] = a_limbs[i];
i += 1;
}
let mut tmp = [0u64; 288];
mul_schoolbook(&m_limbs, &pow10, &mut tmp);
shl(&tmp, e as u32, &mut rhs);
} else {
shl(a_limbs, (-e) as u32, &mut lhs);
mul_schoolbook(&m_limbs, &pow10, &mut rhs);
}
let mag_cmp = cmp_cross(&lhs, &rhs);
let c = if sn { -mag_cmp } else { mag_cmp };
if c < 0 {
Ordering::Less
} else if c > 0 {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
impl<const N: usize, const M: usize> PartialEq<Int<M>> for Int<N> {
#[inline]
fn eq(&self, other: &Int<M>) -> bool {
self.cmp_cross(*other) == Ordering::Equal
}
}
impl<const N: usize, const M: usize> PartialOrd<Int<M>> for Int<N> {
#[inline]
fn partial_cmp(&self, other: &Int<M>) -> Option<Ordering> {
Some(self.cmp_cross(*other))
}
}
impl From<Int<2>> for i128 {
#[inline]
fn from(v: Int<2>) -> i128 {
v.as_i128()
}
}
impl PartialEq<i128> for Int<2> {
#[inline]
fn eq(&self, other: &i128) -> bool {
self.as_i128() == *other
}
}
impl PartialEq<Int<2>> for i128 {
#[inline]
fn eq(&self, other: &Int<2>) -> bool {
*self == other.as_i128()
}
}
impl PartialOrd<i128> for Int<2> {
#[inline]
fn partial_cmp(&self, other: &i128) -> Option<Ordering> {
self.as_i128().partial_cmp(other)
}
}
impl PartialOrd<Int<2>> for i128 {
#[inline]
fn partial_cmp(&self, other: &Int<2>) -> Option<Ordering> {
self.partial_cmp(&other.as_i128())
}
}
impl From<Int<1>> for i64 {
#[inline]
fn from(v: Int<1>) -> i64 {
v.as_i128() as i64
}
}
impl From<Int<1>> for i128 {
#[inline]
fn from(v: Int<1>) -> i128 {
v.as_i128()
}
}
impl PartialEq<i64> for Int<1> {
#[inline]
fn eq(&self, other: &i64) -> bool {
self.as_i128() == *other as i128
}
}
impl PartialEq<Int<1>> for i64 {
#[inline]
fn eq(&self, other: &Int<1>) -> bool {
*self as i128 == other.as_i128()
}
}
impl PartialOrd<i64> for Int<1> {
#[inline]
fn partial_cmp(&self, other: &i64) -> Option<Ordering> {
self.as_i128().partial_cmp(&(i128::from(*other)))
}
}
impl PartialOrd<Int<1>> for i64 {
#[inline]
fn partial_cmp(&self, other: &Int<1>) -> Option<Ordering> {
i128::from(*self).partial_cmp(&other.as_i128())
}
}
impl<const N: usize> Ord for Int<N> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
cmp_dispatch(*self, *other)
}
}
impl<const N: usize> Add for Int<N> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
add_dispatch(self, rhs)
}
}
impl<const N: usize> Sub for Int<N> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
self.wrapping_sub(rhs)
}
}
impl<const N: usize> Mul for Int<N> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
self.wrapping_mul(rhs)
}
}
impl<const N: usize> BitAnd for Int<N> {
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self {
let mut out = [0u64; N];
let mut i = 0;
while i < N {
out[i] = self.limbs[i] & rhs.limbs[i];
i += 1;
}
Self { limbs: out }
}
}
impl<const N: usize> BitOr for Int<N> {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
let mut out = [0u64; N];
let mut i = 0;
while i < N {
out[i] = self.limbs[i] | rhs.limbs[i];
i += 1;
}
Self { limbs: out }
}
}
impl<const N: usize> BitXor for Int<N> {
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self {
let mut out = [0u64; N];
let mut i = 0;
while i < N {
out[i] = self.limbs[i] ^ rhs.limbs[i];
i += 1;
}
Self { limbs: out }
}
}
impl<const N: usize> Not for Int<N> {
type Output = Self;
#[inline]
fn not(self) -> Self {
let mut out = [0u64; N];
let mut i = 0;
while i < N {
out[i] = !self.limbs[i];
i += 1;
}
Self { limbs: out }
}
}
impl<const N: usize> Shl<u32> for Int<N> {
type Output = Self;
#[inline]
fn shl(self, shift: u32) -> Self {
let mut out = [0u64; N];
shl_fixed(&self.limbs, shift, &mut out);
Self { limbs: out }
}
}
impl<const N: usize> Shr<u32> for Int<N> {
type Output = Self;
#[inline]
fn shr(self, shift: u32) -> Self {
let neg = self.is_negative();
let src = if neg { !self } else { self };
let mut out = [0u64; N];
shr_fixed(&src.limbs, shift, &mut out);
let shifted = Self { limbs: out };
if neg { !shifted } else { shifted }
}
}
impl<const N: usize> Neg for Int<N> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
self.wrapping_neg()
}
}
impl<const N: usize> Div for Int<N> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
self.div_rem(rhs).0
}
}
impl<const N: usize> Rem for Int<N> {
type Output = Self;
#[inline]
fn rem(self, rhs: Self) -> Self {
rem_dispatch(self, rhs)
}
}
impl<const N: usize> core::fmt::Display for Uint<N>
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut buf =
<crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
let s = fmt_into::<N>(&self.limbs, 10, true, buf.as_mut());
f.pad_integral(true, "", s)
}
}
impl<const N: usize> core::fmt::Display for Int<N>
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mag = *self.unsigned_abs().as_limbs();
let mut buf =
<crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
let s = fmt_into::<N>(&mag, 10, true, buf.as_mut());
f.pad_integral(!self.is_negative() || self.is_zero(), "", s)
}
}
impl<const N: usize> core::str::FromStr for Int<N> {
type Err = ();
#[inline]
fn from_str(s: &str) -> Result<Self, ()> {
Self::from_str_radix(s, 10)
}
}
impl<const N: usize> core::fmt::LowerHex for Int<N>
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut buf =
<crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
let s = fmt_into::<N>(&self.limbs, 16, true, buf.as_mut());
f.pad_integral(true, "0x", s)
}
}
impl<const N: usize> core::fmt::UpperHex for Int<N>
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut buf =
<crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
let s = fmt_into::<N>(&self.limbs, 16, false, buf.as_mut());
f.pad_integral(true, "0x", s)
}
}
impl<const N: usize> core::fmt::Octal for Int<N>
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut buf =
<crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::bit_formatting_limbs_u8();
let s = fmt_into::<N>(&self.limbs, 8, true, buf.as_mut());
f.pad_integral(true, "0o", s)
}
}
impl<const N: usize> core::fmt::Binary for Int<N>
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut buf =
<crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::bit_formatting_limbs_u8();
let s = fmt_into::<N>(&self.limbs, 2, true, buf.as_mut());
f.pad_integral(true, "0b", s)
}
}
impl<const N: usize> Uint<N> {
#[inline]
pub const fn resize_n<const M: usize>(self) -> Uint<M> {
let mut out = [0u64; M];
let mut i = 0;
while i < M {
if i < N {
out[i] = self.limbs[i];
}
i += 1;
}
Uint::from_limbs(out)
}
#[inline]
pub fn widen<const M: usize>(self) -> Uint<M> {
debug_assert!(M >= N, "widen requires M >= N");
self.resize_n::<M>()
}
#[inline]
pub fn narrow<const M: usize>(self) -> Option<Uint<M>> {
debug_assert!(M <= N, "narrow requires M <= N");
let keep = if M < N { M } else { N };
let mut i = keep;
while i < N {
if self.limbs[i] != 0 {
return None;
}
i += 1;
}
Some(self.resize_n::<M>())
}
}
impl<const N: usize> Int<N> {
#[inline]
pub(crate) const fn resize_n<const M: usize>(self) -> Int<M> {
let fill = if self.is_negative() { u64::MAX } else { 0 };
let mut out = [0u64; M];
let mut i = 0;
while i < M {
out[i] = if i < N { self.limbs[i] } else { fill };
i += 1;
}
Int::from_limbs(out)
}
#[inline]
pub(crate) const fn try_narrow<const M: usize>(self) -> Option<Int<M>> {
debug_assert!(M >= 1 && M <= N, "try_narrow requires 1 <= M <= N");
let sign_fill = if (self.limbs[M - 1] >> 63) == 1 {
u64::MAX
} else {
0
};
let mut i = M;
while i < N {
if self.limbs[i] != sign_fill {
return None;
}
i += 1;
}
Some(self.resize_n::<M>())
}
#[inline]
pub fn widen<const M: usize>(self) -> Int<M> {
debug_assert!(M >= N, "widen requires M >= N");
self.resize_n::<M>()
}
#[inline]
pub fn narrow<const M: usize>(self) -> Option<Int<M>> {
self.try_narrow::<M>()
}
}
macro_rules! int_from_signed {
($($prim:ty => $base:ident),+) => {$(
impl<const N: usize> From<$prim> for Int<N> {
#[inline]
fn from(v: $prim) -> Self { Self::$base(v) }
}
)+};
}
int_from_signed!(i8 => from_i8, i16 => from_i16, i32 => from_i32, i64 => from_i64);
macro_rules! int_from_unsigned {
($($prim:ty => $base:ident),+) => {$(
impl<const N: usize> From<$prim> for Int<N> {
#[inline]
fn from(v: $prim) -> Self { Self::$base(v) }
}
)+};
}
int_from_unsigned!(u8 => from_u8, u16 => from_u16, u32 => from_u32);
macro_rules! uint_from_unsigned {
($($prim:ty),+) => {$(
impl<const N: usize> From<$prim> for Uint<N> {
#[inline]
fn from(v: $prim) -> Self { Self::from_u64(v as u64) }
}
)+};
}
uint_from_unsigned!(u8, u16, u32, u64);
impl<const N: usize> TryFrom<u64> for Int<N> {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: u64) -> Result<Self, Self::Error> {
Self::try_from_u64(v).ok_or(crate::support::error::ConvertError::Overflow)
}
}
impl<const N: usize> TryFrom<i128> for Int<N> {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: i128) -> Result<Self, Self::Error> {
Self::try_from_i128(v).ok_or(crate::support::error::ConvertError::Overflow)
}
}
impl<const N: usize> TryFrom<u128> for Int<N> {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: u128) -> Result<Self, Self::Error> {
Self::try_from_u128(v).ok_or(crate::support::error::ConvertError::Overflow)
}
}
impl<const N: usize> TryFrom<Int<N>> for i32 {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: Int<N>) -> Result<Self, Self::Error> {
v.try_to_i32().ok_or(crate::support::error::ConvertError::Overflow)
}
}
impl<const N: usize> TryFrom<Int<N>> for u32 {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: Int<N>) -> Result<Self, Self::Error> {
v.try_to_u32().ok_or(crate::support::error::ConvertError::Overflow)
}
}
impl<const N: usize> TryFrom<Int<N>> for u64 {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: Int<N>) -> Result<Self, Self::Error> {
v.try_to_u64().ok_or(crate::support::error::ConvertError::Overflow)
}
}
impl<const N: usize> TryFrom<Int<N>> for u128 {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: Int<N>) -> Result<Self, Self::Error> {
v.try_to_u128().ok_or(crate::support::error::ConvertError::Overflow)
}
}
impl<const N: usize> TryFrom<u128> for Uint<N> {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: u128) -> Result<Self, Self::Error> {
Self::try_from_u128(v).ok_or(crate::support::error::ConvertError::Overflow)
}
}
impl<const N: usize> TryFrom<f64> for Int<N> {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: f64) -> Result<Self, Self::Error> {
Self::try_from_f64(v).ok_or(crate::support::error::ConvertError::Overflow)
}
}
impl<const N: usize> TryFrom<f32> for Int<N> {
type Error = crate::support::error::ConvertError;
#[inline]
fn try_from(v: f32) -> Result<Self, Self::Error> {
Self::try_from_f32(v).ok_or(crate::support::error::ConvertError::Overflow)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::int::algos::mul::mul_schoolbook::{mul_low_fixed, mul_schoolbook_fixed};
#[test]
fn try_from_i128_detects_narrow_overflow() {
assert_eq!(Int::<2>::try_from_i128(i128::MAX), Some(Int::<2>::from_i128(i128::MAX)));
assert_eq!(Int::<2>::try_from_i128(i128::MIN), Some(Int::<2>::from_i128(i128::MIN)));
assert_eq!(Int::<1>::try_from_i128(i64::MAX as i128 + 1), None);
assert_eq!(Int::<1>::try_from_i128(i64::MIN as i128 - 1), None);
assert_eq!(
Int::<1>::try_from_i128(i64::MAX as i128),
Some(Int::<1>::from_i64(i64::MAX))
);
assert!(<Int<1> as TryFrom<i128>>::try_from(i64::MAX as i128 + 1).is_err());
assert_eq!(<Int<2> as TryFrom<i128>>::try_from(-5_i128), Ok(Int::<2>::from_i64(-5)));
}
#[test]
fn try_from_u128_detects_narrow_overflow() {
assert_eq!(Uint::<2>::try_from_u128(u128::MAX), Some(Uint::<2>::from_u128(u128::MAX)));
assert_eq!(Uint::<1>::try_from_u128(u64::MAX as u128 + 1), None);
assert_eq!(Uint::<1>::try_from_u128(u64::MAX as u128), Some(Uint::<1>::from_u64(u64::MAX)));
assert!(<Uint<1> as TryFrom<u128>>::try_from(u64::MAX as u128 + 1).is_err());
assert_eq!(Int::<1>::try_from_u128(u64::MAX as u128), None);
assert_eq!(Int::<2>::try_from_u128(u128::MAX), None);
assert_eq!(Int::<3>::try_from_u128(u128::MAX), Some(Int::<3>::from_u128(u128::MAX)));
assert!(<Int<2> as TryFrom<u128>>::try_from(u128::MAX).is_err());
}
#[test]
fn try_from_u64_and_out_conversions_at_edges() {
assert!(<Int<1> as TryFrom<u64>>::try_from(u64::MAX).is_err());
assert_eq!(<Int<2> as TryFrom<u64>>::try_from(u64::MAX), Ok(Int::<2>::from_u128(u64::MAX as u128)));
assert_eq!(i128::from(Int::<2>::MAX), i128::MAX);
assert_eq!(i128::from(Int::<2>::MIN), i128::MIN);
assert_eq!(i64::from(Int::<1>::from_i64(-123)), -123_i64);
let big = Int::<3>::from_u128(u128::MAX);
assert_eq!(big.try_to_i128(), None);
assert_eq!(<i32 as TryFrom<Int<4>>>::try_from(Int::<4>::from_i64(-7)), Ok(-7_i32));
assert!(<u32 as TryFrom<Int<4>>>::try_from(Int::<4>::from_i64(-1)).is_err());
}
#[test]
fn float_conversions_reject_and_round_trip() {
assert_eq!(Int::<2>::try_from_f64(f64::NAN), None);
assert_eq!(Int::<2>::try_from_f64(f64::INFINITY), None);
assert_eq!(Int::<2>::try_from_f64(f64::NEG_INFINITY), None);
assert_eq!(Int::<2>::try_from_f64(0.5), None);
assert_eq!(Int::<2>::try_from_f64(3.5), None);
assert_eq!(Int::<2>::try_from_f32(f32::NAN), None);
assert_eq!(Int::<2>::try_from_f32(f32::INFINITY), None);
assert_eq!(Int::<2>::try_from_f32(0.5), None);
assert_eq!(Int::<2>::try_from_f32(3.5), None);
assert_eq!(Int::<1>::try_from_f64(1e30), None); assert_eq!(Int::<2>::try_from_f64(0.0), Some(Int::<2>::ZERO));
assert_eq!(Int::<2>::try_from_f64(-0.0), Some(Int::<2>::ZERO));
assert_eq!(Int::<2>::try_from_f32(0.0), Some(Int::<2>::ZERO));
assert_eq!(Int::<2>::try_from_f32(-0.0), Some(Int::<2>::ZERO));
assert_eq!(Int::<4>::try_from_f64(42.0), Some(Int::<4>::from_i64(42)));
assert_eq!(Int::<4>::try_from_f64(-42.0), Some(Int::<4>::from_i64(-42)));
assert_eq!(Int::<4>::try_from_f32(7.0), Some(Int::<4>::from_i64(7)));
assert_eq!(Int::<4>::try_from_f32(-7.0), Some(Int::<4>::from_i64(-7)));
assert!(<Int<2> as TryFrom<f64>>::try_from(f64::NAN).is_err());
assert_eq!(<Int<2> as TryFrom<f64>>::try_from(-9.0), Ok(Int::<2>::from_i64(-9)));
assert_eq!(<Int<2> as TryFrom<f32>>::try_from(-9.0), Ok(Int::<2>::from_i64(-9)));
assert_eq!(Int::<4>::from_i64(123).to_f64(), 123.0);
assert_eq!(Int::<4>::from_i64(-123).to_f64(), -123.0);
assert_eq!(Int::<4>::from_i64(123).to_f32(), 123.0_f32);
}
#[test]
fn float_conversions_wide_and_boundaries() {
let big = 2f64.powi(64); assert_eq!(Int::<1>::try_from_f64(big), None);
assert_eq!(
Int::<2>::try_from_f64(big),
Some(Int::<2>::from_u128(1u128 << 64))
);
let two_63 = 2f64.powi(63);
assert_eq!(Int::<1>::try_from_f64(two_63), None);
let near_max = (i64::MAX - 1023) as f64; assert_eq!(near_max as i64, i64::MAX - 1023);
assert_eq!(
Int::<1>::try_from_f64(near_max),
Some(Int::<1>::from_i64(i64::MAX - 1023))
);
let min_edge = -(2f64.powi(63));
assert_eq!(
Int::<1>::try_from_f64(min_edge),
Some(Int::<1>::from_i64(i64::MIN))
);
assert_eq!(Int::<1>::try_from_f64(-(2f64.powi(64))), None);
let two_24 = 2f32.powi(24);
assert_eq!(
Int::<1>::try_from_f32(two_24),
Some(Int::<1>::from_i64(1 << 24))
);
}
#[test]
fn float_conversions_are_const() {
const X: Option<Int<2>> = Int::<2>::try_from_f64(42.0);
const Y: Option<Int<2>> = Int::<2>::try_from_f32(42.0);
assert_eq!(X, Some(Int::<2>::from_i64(42)));
assert_eq!(Y, Some(Int::<2>::from_i64(42)));
}
#[test]
fn from_traits_are_infallible_for_fitting_prims() {
assert_eq!(Int::<1>::from(-7_i32), Int::<1>::from_i64(-7));
assert_eq!(Int::<4>::from(42_i64), Int::<4>::from_i64(42));
assert_eq!(Uint::<1>::from(7_u32), Uint::<1>::from_u64(7));
assert_eq!(Uint::<4>::from(u64::MAX), Uint::<4>::from_u64(u64::MAX));
}
#[test]
fn fixed_int_trait_surface() {
fn exercises<T: BigInt>(seven: T, three: T) {
assert_eq!(T::LIMBS as u32 * 64, T::BITS);
assert!(T::ZERO.is_zero());
assert!(T::ONE.is_one());
assert!(!T::ZERO.is_one());
let ten = seven + three;
assert_eq!(ten - three, seven);
assert_eq!(ten, seven.wrapping_add(three));
assert_eq!(seven.wrapping_sub(three) + three, seven);
let _ = (seven & three) | (seven ^ three);
let _ = !T::ZERO;
assert_eq!((T::ONE << 4) >> 4, T::ONE);
assert_eq!(seven.checked_add(three), Some(ten));
assert!(seven.checked_mul(three).is_some());
assert_eq!(three.sqr(), three * three);
assert_eq!(three.cube(), three * three * three);
assert_eq!(three.pow(2), three.sqr());
assert_eq!(three.wrapping_pow(3), three.cube());
assert_eq!(three.checked_pow(2), Some(three.sqr()));
assert_eq!(T::ONE.bit_length(), 1);
assert_eq!(T::ZERO.bit_length(), 0);
assert_eq!(T::ONE.leading_zeros(), T::BITS - 1);
let items = [T::ONE, T::ONE, T::ONE];
assert_eq!(T::sum(items), three);
assert_eq!(T::product([three, T::ONE]), three);
assert_eq!(T::from_limbs(seven.to_limbs()), seven);
}
exercises(Int::<4>::from_i64(7), Int::<4>::from_i64(3));
exercises(Int::<6>::from_i64(7), Int::<6>::from_i64(3));
}
#[test]
fn limbs_mul_low_matches_full_product_low_half() {
fn check<const N: usize, const D: usize>(a: [u64; N], b: [u64; N]) {
debug_assert!(D == 2 * N);
let mut full = [0u64; D];
mul_schoolbook_fixed::<N, D>(&a, &b, &mut full);
let mut low = [0u64; N];
mul_low_fixed::<N>(&a, &b, &mut low);
let mut expected = [0u64; N];
expected.copy_from_slice(&full[..N]);
assert_eq!(low, expected, "low-half mismatch for {a:?} * {b:?}");
}
check::<4, 8>([0, 0, 0, 0], [0, 0, 0, 0]);
check::<4, 8>([1, 0, 0, 0], [u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
check::<4, 8>([u64::MAX; 4], [u64::MAX; 4]);
check::<4, 8>([0, 1, 0, 0], [0, 1, 0, 0]); check::<4, 8>(
[0xDEAD_BEEF, 0xCAFE_F00D, 0x1234, 0x5678_9ABC],
[0xFEED_FACE, 0x0BAD_C0DE, 0x9999, 0x0000_0001],
);
check::<2, 4>([u64::MAX, u64::MAX], [3, 0]);
check::<6, 12>([7, 8, 9, 10, 11, 12], [1, 2, 3, 4, 5, 6]);
}
#[test]
fn dedicated_sqr_matches_general_mul() {
fn check<const N: usize>(x: [u64; N]) {
let a = Uint::<N>::from_limbs(x);
assert_eq!(
a.wrapping_sqr(),
a.wrapping_mul(a),
"sqr != mul(self,self) for {x:?}"
);
}
check::<4>([0, 0, 0, 0]);
check::<4>([1, 0, 0, 0]);
check::<4>([u64::MAX; 4]);
check::<4>([0x1234_5678, 0, 0, 0]);
check::<4>([u64::MAX, u64::MAX, 0, 0]);
check::<4>([0xDEAD_BEEF_CAFE_F00D, 0x0123_4567_89AB_CDEF, 0xFEDC, 0x99]);
check::<4>([u64::MAX, u64::MAX, u64::MAX, 0]);
check::<1>([u64::MAX]);
check::<2>([u64::MAX, u64::MAX]);
check::<6>([7, 8, 9, 10, 11, 12]);
check::<8>([u64::MAX, 1, u64::MAX, 2, u64::MAX, 3, u64::MAX, 4]);
let mut state = 0x9E37_79B9_7F4A_7C15u64;
let mut next = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
for _ in 0..64 {
check::<5>([next(), next(), next(), next(), next()]);
}
}
#[test]
fn uint_sqr_cube_match_naive() {
let x = Uint::<4>::from_limbs([123_456_789, 0, 0, 0]);
assert_eq!(x.wrapping_sqr(), x.wrapping_mul(x));
assert_eq!(x.wrapping_cube(), x.wrapping_mul(x).wrapping_mul(x));
let y = Uint::<4>::from_limbs([0xDEAD_BEEF_CAFE, 0x1234_5678, 0, 0]);
assert_eq!(y.wrapping_sqr(), y.wrapping_mul(y));
assert_eq!(y.wrapping_cube(), y.wrapping_mul(y).wrapping_mul(y));
}
#[cfg(feature = "_wide-support")]
#[test]
fn root_int_floor_and_exactness() {
fn brute(m: u128, k: u32) -> (u128, bool) {
if m == 0 {
return (0, true);
}
let mut r: u128 = 0;
while {
let next = r + 1;
next.checked_pow(k).is_some_and(|p| p <= m)
} {
r += 1;
}
(r, r.pow(k) == m)
}
fn check<const N: usize>(m: u128, k: u32) {
let lo = (m & 0xFFFF_FFFF_FFFF_FFFF) as u64;
let hi = (m >> 64) as u64;
let mut limbs = [0u64; N];
limbs[0] = lo;
if N > 1 {
limbs[1] = hi;
}
let n = Uint::<N>::from_limbs(limbs);
let (root, exact) = n.root_int(k);
let (eroot, eexact) = brute(m, k);
let root_lo = root.as_limbs()[0] as u128
| ((if N > 1 { root.as_limbs()[1] as u128 } else { 0 }) << 64);
assert_eq!(root_lo, eroot, "root mismatch for m={m}, k={k}");
assert_eq!(exact, eexact, "exact flag mismatch for m={m}, k={k}");
let rk = root.pow(k);
assert!(rk <= n, "root^k > m for m={m}, k={k}");
let next = root.wrapping_add(Uint::<N>::ONE);
if let Some(p) = next.checked_pow(k) { assert!(p > n, "(root+1)^k <= m for m={m}, k={k}") }
}
let samples: [u128; 14] = [
0,
1,
2,
7,
8,
9,
26,
27,
28,
1000,
1023,
1024,
1_000_000,
u64::MAX as u128,
];
for &m in &samples {
for k in [2u32, 3, 5] {
check::<4>(m, k);
check::<2>(m, k);
check::<8>(m, k);
}
}
let big = Uint::<4>::from_limbs([0xFFFF_FFFF_FFFF_FFFF, 0x1234_5678, 0, 0]);
assert_eq!(big.root_int(2).0, big.isqrt());
let base = Uint::<4>::ONE.shl(40);
let cube = base.wrapping_cube();
let (r, exact) = cube.root_int(3);
assert_eq!(r, base);
assert!(exact);
let (r2, exact2) = cube.wrapping_sub(Uint::<4>::ONE).root_int(3);
assert_eq!(r2, base.wrapping_sub(Uint::<4>::ONE));
assert!(!exact2);
}
#[test]
fn uint_widen_zero_extends() {
let a = Uint::<2>::from_limbs([7, 8]);
let w = a.widen::<4>();
assert_eq!(*w.as_limbs(), [7, 8, 0, 0]);
assert_eq!(a.resize_n::<4>(), w);
}
#[test]
fn uint_narrow_checks_dropped_limbs() {
let fits = Uint::<4>::from_limbs([7, 8, 0, 0]);
assert_eq!(*fits.narrow::<2>().unwrap().as_limbs(), [7, 8]);
let too_big = Uint::<4>::from_limbs([7, 8, 1, 0]);
assert!(too_big.narrow::<2>().is_none());
let a = Uint::<2>::from_limbs([3, 4]);
assert_eq!(a.widen::<4>().narrow::<2>().unwrap(), a);
}
#[test]
fn int_widen_sign_extends() {
let neg = Int::<2>::from_i64(-1);
assert_eq!(*neg.widen::<4>().as_limbs(), [u64::MAX; 4]);
assert_eq!(neg.widen::<4>(), Int::<4>::from_i64(-1));
let pos = Int::<2>::from_i64(5);
assert_eq!(*pos.widen::<4>().as_limbs(), [5, 0, 0, 0]);
}
#[test]
fn int_narrow_requires_sign_consistency() {
let neg = Int::<4>::from_i64(-1);
assert_eq!(neg.narrow::<2>().unwrap(), Int::<2>::from_i64(-1));
let big = Int::<4>::from_limbs([0, 0, 1, 0]);
assert!(big.narrow::<2>().is_none());
let weird = Int::<4>::from_limbs([1, 0, 0, u64::MAX]);
assert!(weird.narrow::<2>().is_none());
let p = Int::<2>::from_i64(42);
assert_eq!(p.widen::<4>().narrow::<2>().unwrap(), p);
}
#[test]
fn int_resize_const_two_complement() {
let pos = Int::<2>::from_i64(123);
let wide: Int<4> = pos.resize_n::<4>();
assert_eq!(*wide.as_limbs(), [123, 0, 0, 0]);
assert_eq!(wide.resize_n::<2>(), pos);
let neg = Int::<2>::from_i64(-7);
let wneg: Int<4> = neg.resize_n::<4>();
assert_eq!(*wneg.as_limbs(), [(-7i64) as u64, u64::MAX, u64::MAX, u64::MAX]);
assert_eq!(wneg, Int::<4>::from_i64(-7));
let v = Int::<4>::from_limbs([1, 2, 3, 4]);
assert_eq!(*v.resize_n::<2>().as_limbs(), [1, 2]);
assert_eq!(Int::<4>::from_i64(-1).try_narrow::<2>().unwrap(), Int::<2>::from_i64(-1));
assert!(Int::<4>::from_limbs([0, 0, 1, 0]).try_narrow::<2>().is_none());
let too_big = Int::<2>::from_limbs([0x8000_0000_0000_0000, 0]);
assert!(too_big.try_narrow::<1>().is_none());
let min2 = Int::<2>::from_i64(i64::MIN);
assert_eq!(min2.try_narrow::<1>().unwrap(), Int::<1>::from_i64(i64::MIN));
assert_eq!(min2.resize_n::<4>().try_narrow::<2>().unwrap(), min2);
const W: Int<4> = Int::<2>::from_i64(-7).resize_n::<4>();
assert_eq!(W, Int::<4>::from_i64(-7));
}
#[test]
fn bit_count_is_const() {
const Z: u32 = Int::<2>::leading_zeros(&Int::<2>::MAX);
const BL: u32 = Int::<2>::bit_length(&Int::<2>::MAX);
const UZ: u32 = Uint::<2>::leading_zeros(&Uint::<2>::MAX);
const UBL: u32 = Uint::<2>::bit_length(&Uint::<2>::MAX);
assert_eq!(Z, 1);
assert_eq!(BL, 127);
assert_eq!(UZ, 0);
assert_eq!(UBL, 128);
}
#[test]
fn int_cross_width_comparison() {
let a = Int::<1>::from_i64(5);
let b = Int::<2>::from_i64(5);
assert!(a == b);
assert!(b == a);
assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
let small = Int::<1>::from_i64(3);
let big = Int::<2>::from_i64(100);
assert!(small < big);
assert!(big > small);
let huge = Int::<2>::from_limbs([0, 64]); assert!(huge > Int::<1>::MAX);
assert!(Int::<1>::from_i64(-1) < huge);
assert!(Int::<1>::from_i64(-1) < Int::<2>::from_i64(1));
assert!(Int::<1>::from_i64(-100) < Int::<2>::from_i64(-3));
assert!(Int::<2>::from_i64(-3) > Int::<1>::from_i64(-100));
assert!(Int::<1>::from_i64(-7) == Int::<2>::from_i64(-7));
let mut v = vec![
Int::<2>::from_i64(3),
Int::<2>::from_i64(-10),
Int::<2>::from_i64(0),
Int::<2>::from_i64(7),
];
v.sort();
assert_eq!(
v,
vec![
Int::<2>::from_i64(-10),
Int::<2>::from_i64(0),
Int::<2>::from_i64(3),
Int::<2>::from_i64(7),
]
);
}
#[test]
fn consts_and_round_trip() {
assert_eq!(Uint::<4>::LIMBS, 4);
assert_eq!(Uint::<4>::BITS, 256);
assert_eq!(*Uint::<4>::ZERO.as_limbs(), [0, 0, 0, 0]);
assert_eq!(*Uint::<4>::ONE.as_limbs(), [1, 0, 0, 0]);
assert_eq!(*Uint::<4>::MAX.as_limbs(), [u64::MAX; 4]);
let v = Uint::<4>::from_limbs([7, 8, 9, 10]);
assert_eq!(*v.as_limbs(), [7, 8, 9, 10]);
}
#[test]
fn widths_have_expected_bits() {
assert_eq!(Int::<4>::BITS, 256);
assert_eq!(Int::<64>::BITS, 4096);
assert_eq!(Uint::<16>::LIMBS, 16);
}
#[test]
fn wrapping_sub_borrows_across_limbs() {
let a = Uint::<4>::from_limbs([0, 1, 0, 0]);
let d = a.wrapping_sub(Uint::<4>::ONE);
assert_eq!(*d.as_limbs(), [u64::MAX, 0, 0, 0]);
let wrap = Uint::<4>::ZERO.wrapping_sub(Uint::<4>::ONE);
assert_eq!(*wrap.as_limbs(), [u64::MAX; 4]);
}
#[test]
fn unsigned_ordering() {
let small = Uint::<4>::from_limbs([5, 0, 0, 0]);
let big = Uint::<4>::from_limbs([0, 1, 0, 0]); assert!(small < big);
assert!(big > small);
assert_eq!(small, small);
assert!(Uint::<4>::ZERO < Uint::<4>::MAX);
assert_eq!(small.max(big), big);
}
#[test]
fn wrapping_add_carries_across_limbs() {
let a = Uint::<4>::from_limbs([u64::MAX, 0, 0, 0]);
let sum = a.wrapping_add(Uint::<4>::ONE);
assert_eq!(*sum.as_limbs(), [0, 1, 0, 0]);
let wrap = Uint::<4>::MAX.wrapping_add(Uint::<4>::ONE);
assert_eq!(*wrap.as_limbs(), [0, 0, 0, 0]);
}
#[test]
fn uint_wrapping_mul_cross_limb_product() {
let a = Uint::<4>::from_limbs([0, 1, 0, 0]);
let p = a.wrapping_mul(a);
assert_eq!(*p.as_limbs(), [0, 0, 1, 0]);
let m = Uint::<4>::from_limbs([u64::MAX, 0, 0, 0]);
let three = Uint::<4>::from_limbs([3, 0, 0, 0]);
let q = m.wrapping_mul(three);
assert_eq!(*q.as_limbs(), [u64::MAX - 2, 2, 0, 0]);
let v = Uint::<4>::from_limbs([7, 8, 9, 10]);
assert_eq!(v.wrapping_mul(Uint::<4>::ONE), v);
}
#[test]
fn uint_wrapping_mul_truncates_modulo_width() {
let hi = Uint::<4>::from_limbs([0, 0, 0, 1]);
assert_eq!(hi.wrapping_mul(hi), Uint::<4>::ZERO);
let r = Uint::<4>::MAX.wrapping_mul(Uint::<4>::MAX);
assert_eq!(*r.as_limbs(), [1, 0, 0, 0]);
}
#[test]
fn uint_checked_add_sub_overflow() {
assert_eq!(
Uint::<4>::ONE.checked_add(Uint::<4>::ONE),
Some(Uint::<4>::from_limbs([2, 0, 0, 0]))
);
assert_eq!(Uint::<4>::MAX.checked_add(Uint::<4>::ONE), None);
assert_eq!(
Uint::<4>::from_limbs([5, 0, 0, 0]).checked_sub(Uint::<4>::from_limbs([3, 0, 0, 0])),
Some(Uint::<4>::from_limbs([2, 0, 0, 0]))
);
assert_eq!(Uint::<4>::ZERO.checked_sub(Uint::<4>::ONE), None);
}
#[test]
fn uint_checked_mul_overflow() {
let a = Uint::<4>::from_limbs([0, 1, 0, 0]);
assert_eq!(a.checked_mul(a), Some(Uint::<4>::from_limbs([0, 0, 1, 0])));
let hi = Uint::<4>::from_limbs([0, 0, 0, 1]);
assert_eq!(hi.checked_mul(hi), None);
assert_eq!(
Uint::<4>::MAX.checked_mul(Uint::<4>::from_limbs([2, 0, 0, 0])),
None
);
}
#[test]
fn uint_div_rem_with_remainder() {
let n = Uint::<4>::from_limbs([1000, 0, 0, 0]);
let d = Uint::<4>::from_limbs([7, 0, 0, 0]);
assert_eq!(*n.wrapping_div(d).as_limbs(), [142, 0, 0, 0]);
assert_eq!(*n.wrapping_rem(d).as_limbs(), [6, 0, 0, 0]);
let big = Uint::<4>::from_limbs([0, 0, 1, 0]);
let by = Uint::<4>::from_limbs([0, 1, 0, 0]);
assert_eq!(*big.wrapping_div(by).as_limbs(), [0, 1, 0, 0]);
assert!(big.wrapping_rem(by).is_zero());
}
#[test]
#[should_panic(expected = "divide by zero")]
fn uint_div_by_zero_panics() {
let _ = Uint::<4>::ONE.wrapping_div(Uint::<4>::ZERO);
}
#[test]
fn uint_bitwise_ops() {
let a = Uint::<4>::from_limbs([0b1100, 0xFF, 0, 0]);
let b = Uint::<4>::from_limbs([0b1010, 0x0F, 0, 0]);
assert_eq!(*(a & b).as_limbs(), [0b1000, 0x0F, 0, 0]);
assert_eq!(*(a | b).as_limbs(), [0b1110, 0xFF, 0, 0]);
assert_eq!(*(a ^ b).as_limbs(), [0b0110, 0xF0, 0, 0]);
assert_eq!(*(!Uint::<4>::ZERO).as_limbs(), [u64::MAX; 4]);
}
#[test]
fn uint_shifts() {
let one = Uint::<4>::ONE;
assert_eq!(*(one << 64).as_limbs(), [0, 1, 0, 0]);
assert_eq!(*(one << 130).as_limbs(), [0, 0, 0b100, 0]);
let v = Uint::<4>::from_limbs([0, 0, 0b100, 0]);
assert_eq!(*(v >> 130).as_limbs(), [1, 0, 0, 0]);
assert_eq!(one << 256, Uint::<4>::ZERO);
}
#[test]
fn uint_is_zero_bitlen_leading_zeros() {
assert!(Uint::<4>::ZERO.is_zero());
assert!(!Uint::<4>::ONE.is_zero());
assert_eq!(Uint::<4>::ZERO.bit_length(), 0);
assert_eq!(Uint::<4>::ONE.bit_length(), 1);
let b = Uint::<4>::from_limbs([0, 1, 0, 0]);
assert_eq!(b.bit_length(), 65);
assert_eq!(b.leading_zeros(), 256 - 65);
assert_eq!(Uint::<4>::ZERO.leading_zeros(), 256);
assert_eq!(Uint::<4>::MAX.leading_zeros(), 0);
}
#[test]
fn uint_operator_traits_delegate() {
let a = Uint::<4>::from_limbs([10, 0, 0, 0]);
let b = Uint::<4>::from_limbs([3, 0, 0, 0]);
assert_eq!(*(a + b).as_limbs(), [13, 0, 0, 0]);
assert_eq!(*(a - b).as_limbs(), [7, 0, 0, 0]);
assert_eq!(*(a * b).as_limbs(), [30, 0, 0, 0]);
}
#[test]
fn int_from_i64_sign_extends() {
assert_eq!(*Int::<4>::from_i64(5).as_limbs(), [5, 0, 0, 0]);
assert_eq!(*Int::<4>::from_i64(-1).as_limbs(), [u64::MAX; 4]);
assert_eq!(
*Int::<4>::from_i64(-2).as_limbs(),
[u64::MAX - 1, u64::MAX, u64::MAX, u64::MAX]
);
assert!(Int::<4>::from_i64(-1).is_negative());
assert!(Int::<4>::from_i64(1).is_positive());
assert!(Int::<4>::from_i64(0).is_zero());
}
#[test]
fn int_wrapping_neg_two_complement() {
let five = Int::<4>::from_i64(5);
let neg_five = five.wrapping_neg();
assert_eq!(neg_five, Int::<4>::from_i64(-5));
assert_eq!(neg_five.wrapping_neg(), five);
assert_eq!(Int::<4>::from_i64(-1).wrapping_neg(), Int::<4>::ONE);
assert_eq!(-five, neg_five);
}
#[test]
fn int_add_sub_mul_with_signs() {
let a = Int::<4>::from_i64(7);
let b = Int::<4>::from_i64(-3);
assert_eq!(a.wrapping_add(b), Int::<4>::from_i64(4));
assert_eq!(a.wrapping_sub(b), Int::<4>::from_i64(10));
assert_eq!(a.wrapping_mul(b), Int::<4>::from_i64(-21));
assert_eq!(b.wrapping_mul(b), Int::<4>::from_i64(9));
assert_eq!(a + b, Int::<4>::from_i64(4));
assert_eq!(a - b, Int::<4>::from_i64(10));
assert_eq!(a * b, Int::<4>::from_i64(-21));
}
#[test]
fn int_mul_crosses_limbs_with_sign() {
let big = Int::<4>::from_i64(0).wrapping_add(Int::<4>::from_limbs([0, 1, 0, 0]));
let neg = big.wrapping_mul(Int::<4>::from_i64(-1));
assert_eq!(neg, big.wrapping_neg());
assert_eq!(*neg.as_limbs(), [0, u64::MAX, u64::MAX, u64::MAX]);
}
#[test]
fn int_abs_signum() {
assert_eq!(Int::<4>::from_i64(-9).abs(), Int::<4>::from_i64(9));
assert_eq!(Int::<4>::from_i64(9).abs(), Int::<4>::from_i64(9));
assert_eq!(Int::<4>::from_i64(-9).signum(), -1);
assert_eq!(Int::<4>::from_i64(9).signum(), 1);
assert_eq!(Int::<4>::from_i64(0).signum(), 0);
}
#[test]
fn int_from_i128_round_trips() {
for v in [
0i128,
1,
-1,
42,
-42,
i64::MAX as i128,
i64::MIN as i128,
i128::MAX,
i128::MIN,
123_456_789_012_345_678,
] {
let a = Int::<4>::from_i128(v);
assert_eq!(a.to_i128_checked(), Some(v), "round-trip i128 {v}");
let b = Int::<8>::from_i128(v);
assert_eq!(b.to_i128_checked(), Some(v), "round-trip i128 {v} (N=8)");
}
let big = Int::<4>::from_u128(u128::MAX);
assert_eq!(big.to_i128_checked(), None);
assert_eq!(big.to_u128_checked(), Some(u128::MAX));
assert_eq!(Int::<4>::from_i128(-1).to_u128_checked(), None);
}
#[test]
fn int_from_str_radix_and_display_round_trip() {
let cases = [
"0",
"1",
"-1",
"42",
"-42",
"1000000000000000000000",
"-340282366920938463463374607431768211455",
];
for s in cases {
let v = Int::<4>::from_str_radix(s, 10).unwrap();
assert_eq!(format!("{v}"), s, "display round-trip {s}");
let v2: Int<4> = s.parse().unwrap();
assert_eq!(v, v2);
}
assert!(Int::<4>::from_str_radix("10", 16).is_err());
assert!(Int::<4>::from_str_radix("12x", 10).is_err());
assert!(Int::<4>::from_str_radix("", 10).is_err());
assert!(Int::<4>::from_str_radix("-", 10).is_err());
}
#[test]
fn int_div_rem_signs_match_truncating() {
let cases: [(i128, i128); 6] = [
(1000, 7),
(-1000, 7),
(1000, -7),
(-1000, -7),
(7, 1000),
(-7, 1000),
];
for (a, b) in cases {
let (q, r) = Int::<4>::from_i128(a).div_rem(Int::<4>::from_i128(b));
assert_eq!(q.to_i128_checked(), Some(a / b), "quot {a}/{b}");
assert_eq!(r.to_i128_checked(), Some(a % b), "rem {a}%{b}");
}
}
#[test]
#[should_panic(expected = "divide by zero")]
fn int_div_rem_by_zero_panics() {
let _ = Int::<4>::ONE.div_rem(Int::<4>::ZERO);
}
#[test]
fn int_bit_reads_twos_complement() {
let v = Int::<4>::from_i128(0b1010);
assert!(!v.bit(0));
assert!(v.bit(1));
assert!(!v.bit(2));
assert!(v.bit(3));
assert!(!v.bit(200));
let neg = Int::<4>::from_i128(-1);
assert!(neg.bit(0));
assert!(neg.bit(255));
assert!(neg.bit(1000)); }
#[test]
fn int_mul_u64_matches_wide_mul() {
let v = Int::<4>::from_i128(123_456_789);
assert_eq!(
v.mul_u64(1000),
Int::<4>::from_i128(123_456_789_000)
);
let n = Int::<4>::from_i128(-123_456_789);
assert_eq!(
n.mul_u64(1000),
Int::<4>::from_i128(-123_456_789_000)
);
assert_eq!(v.mul_u64(0), Int::<4>::ZERO);
assert_eq!(v.mul_u64(1), v);
}
#[test]
#[should_panic(expected = "mul overflow")]
fn int_mul_u64_overflow_panics() {
let _ = Int::<4>::max_value().mul_u64(2);
}
#[test]
fn int_div_rem_operators_match_div_rem() {
let a = Int::<4>::from_i128(-1000);
let b = Int::<4>::from_i128(7);
assert_eq!(a / b, Int::<4>::from_i128(-1000 / 7));
assert_eq!(a % b, Int::<4>::from_i128(-1000 % 7));
let (q, r) = a.div_rem(b);
assert_eq!(a / b, q);
assert_eq!(a % b, r);
}
#[cfg(feature = "_wide-support")]
#[test]
fn int_wide_storage_surface() {
use crate::int::types::traits::BigInt;
fn exercises<T: BigInt>() {
assert!(<T as BigInt>::ZERO == <T as BigInt>::from_i128(0));
assert!(<T as BigInt>::ONE == <T as BigInt>::from_i128(1));
assert!(<T as BigInt>::TEN == <T as BigInt>::from_i128(10));
let twelve = <T as BigInt>::from_i128(12);
let three = <T as BigInt>::from_i128(3);
assert!(three.pow(3) == <T as BigInt>::from_i128(27));
assert!(<T as BigInt>::from_i128(144).isqrt() == <T as BigInt>::from_i128(12));
let (q, r) = twelve.div_rem(<T as BigInt>::from_i128(5));
assert!(q == <T as BigInt>::from_i128(2));
assert!(r == <T as BigInt>::from_i128(2));
assert!(twelve.bit(2) && twelve.bit(3) && !twelve.bit(0));
assert!(<T as BigInt>::ONE.leading_zeros() == <T as BigInt>::BITS - 1);
assert!(twelve.mul_u64(10) == <T as BigInt>::from_i128(120));
assert!(twelve.to_f64() == 12.0);
assert!(<T as BigInt>::from_f64_val(7.9) == <T as BigInt>::from_i128(7));
}
exercises::<Int<4>>();
exercises::<Int<8>>();
let v = Int::<4>::from_i128(-123_456_789);
let w: Int<8> = BigInt::resize_to(v);
assert_eq!(w.to_i128_checked(), Some(-123_456_789));
let back: Int<4> = BigInt::resize_to(w);
assert_eq!(back, v);
}
#[cfg(feature = "_wide-support")]
#[test]
fn int_isqrt_matches_uint_magnitude() {
use crate::int::types::traits::BigInt;
let n = Int::<4>::from_i128(1_000_000_000_000);
let r = BigInt::isqrt(n);
assert_eq!(r, Int::<4>::from_i128(1_000_000));
let big = Int::<8>::from_i128(987_654_321);
let sq = big.checked_mul(big).unwrap();
assert_eq!(BigInt::isqrt(sq), big);
}
#[test]
fn uint_sqr_policy_matches_wrapping_sqr() {
fn check<const N: usize>(limbs: [u64; N]) {
let x = Uint::<N>::from_limbs(limbs);
assert_eq!(x.sqr(), x.wrapping_sqr(), "sqr mismatch at {limbs:?}");
assert_eq!(x.sqr(), x.wrapping_mul(x), "sqr != x*x at {limbs:?}");
}
check::<1>([0]);
check::<1>([1]);
check::<1>([u64::MAX]);
check::<1>([12345]);
check::<2>([0, 0]);
check::<2>([1, 0]);
check::<2>([u64::MAX, u64::MAX]);
check::<2>([0xDEAD_BEEF, 0x1234]);
check::<4>([u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
check::<4>([0x1234_5678_9ABC_DEF0, 0xFEDC_BA98, 0, 0]);
check::<6>([7, 8, 9, 10, 11, 12]);
}
#[test]
fn uint_cube_policy_matches_wrapping_cube() {
fn check<const N: usize>(limbs: [u64; N]) {
let x = Uint::<N>::from_limbs(limbs);
assert_eq!(x.cube(), x.wrapping_cube(), "cube mismatch at {limbs:?}");
assert_eq!(
x.cube(),
x.wrapping_mul(x).wrapping_mul(x),
"cube != x*x*x at {limbs:?}"
);
}
check::<1>([0]);
check::<1>([1]);
check::<1>([3]);
check::<1>([u64::MAX]);
check::<2>([0, 0]);
check::<2>([1, 0]);
check::<2>([100, 0]);
check::<2>([u64::MAX, u64::MAX]);
check::<4>([5, 0, 0, 0]);
check::<4>([0x1234, 0x5678, 0, 0]);
}
#[test]
fn int_sqr_cube_match_wrapping() {
fn check<const N: usize>(v: i128) {
let x = Int::<N>::from_i128(v);
assert_eq!(x.sqr(), x.wrapping_sqr(), "int sqr mismatch v={v}");
assert_eq!(x.cube(), x.wrapping_cube(), "int cube mismatch v={v}");
}
for v in [0i128, 1, -1, 12, -12, 100, -100, 1_000_000, -1_000_000] {
check::<4>(v);
check::<8>(v);
}
}
#[test]
fn uint_icbrt_floor_correctness() {
fn brute_cbrt(n: u64) -> u64 {
if n == 0 {
return 0;
}
let mut r: u64 = 0;
while (r + 1).checked_mul((r + 1) * (r + 1) + r + 1)
.is_some_and(|p| p <= n)
|| (r + 1).checked_pow(3).is_some_and(|p| p <= n)
{
r += 1;
}
r
}
fn check<const N: usize>(n: u64) {
let x = Uint::<N>::from_u64(n);
let root = x.icbrt();
let root_u64 = root.as_limbs()[0];
let expected = brute_cbrt(n);
assert_eq!(root_u64, expected, "icbrt({n}) = {root_u64}, expected {expected}");
for i in 1..N {
assert_eq!(root.as_limbs()[i], 0, "icbrt({n}) high limb {i} nonzero");
}
}
for n in [0u64, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000,
1_000_000_000u64, 8_000_000_000u64] {
check::<1>(n);
check::<2>(n);
check::<4>(n);
}
for n in [2u64, 3, 4, 5, 6, 7, 9, 10, 26, 28, 63, 65, 999,
1_000_000_001u64] {
check::<1>(n);
check::<2>(n);
check::<4>(n);
}
check::<1>(0);
check::<1>(1);
check::<4>(0);
check::<4>(1);
for n in [u64::MAX, u64::MAX - 1, u64::MAX - 100, 1 << 60, 1 << 48] {
check::<2>(n);
check::<4>(n);
}
}
#[test]
fn uint_icbrt_wide_floor_identity() {
fn check_wide<const N: usize>(limbs: [u64; N]) {
let x = Uint::<N>::from_limbs(limbs);
let r = x.icbrt();
let r3 = r.wrapping_pow(3);
assert!(
r3 <= x,
"icbrt violated: r³ > x for {limbs:?}"
);
let r1 = r.wrapping_add(Uint::<N>::ONE);
let r1_3 = r1.wrapping_pow(3);
assert!(
r1_3 > x || r1 == Uint::<N>::ZERO,
"icbrt not floor: (r+1)³ <= x for {limbs:?}"
);
}
let m = 1_000_000u64;
let m3 = m * m * m; check_wide::<4>([m3, 0, 0, 0]);
check_wide::<4>([m3 + 1, 0, 0, 0]);
check_wide::<4>([m3 - 1, 0, 0, 0]);
let hi = 1u64;
let lo = 0u64;
check_wide::<4>([lo, hi, 0, 0]);
check_wide::<4>([u64::MAX, u64::MAX, 0, 0]);
check_wide::<6>([m3, 0, 0, 0, 0, 0]);
check_wide::<6>([u64::MAX, u64::MAX, u64::MAX, 0, 0, 0]); }
#[test]
fn int_icbrt_magnitude() {
for v in [0i128, 1, -1, 8, -8, 27, -27, 1000, -1000] {
let x = Int::<4>::from_i128(v);
let expected = Int::<4>::from_i128((v.unsigned_abs() as f64).cbrt() as i128);
let got = x.icbrt();
assert_eq!(got, expected, "int icbrt({v}) mismatch");
}
}
#[test]
fn int_wideint_mag_sign_round_trips() {
use crate::int::types::traits::BigInt;
for v in [0i128, 1, -1, 123_456_789_012_345_678, -987_654_321] {
let a = Int::<4>::from_i128(v);
let mut mag = [0u128; 2];
let neg = a.mag_into_u128(&mut mag);
assert_eq!(neg, a.is_negative());
assert_eq!(Int::<4>::from_mag_sign_u128(&mag, neg), a, "mag/sign {v}");
}
assert_eq!(<Int<4> as BigInt>::U128_LIMBS, 2);
assert_eq!(<Int<3> as BigInt>::U128_LIMBS, 2);
assert_eq!(<Int<8> as BigInt>::U128_LIMBS, 4);
let v3 = Int::<3>::from_i128(-170_141_183_460_469_231_731);
let mut buf = [0u128; 2];
let neg = v3.mag_into_u128(&mut buf);
assert_eq!(Int::<3>::from_mag_sign_u128(&buf, neg), v3);
let v4 = Int::<4>::from_i128(i128::MIN);
let mut buf4 = [0u128; 2];
let neg4 = v4.mag_into_u128(&mut buf4);
assert_eq!(Int::<4>::from_mag_sign_u128(&buf4, neg4), v4);
}
#[test]
fn int_to_from_f64_and_negate_ten() {
assert_eq!(Int::<4>::from_i64(5).to_f64(), 5.0);
assert_eq!(Int::<4>::from_i64(-5).to_f64(), -5.0);
assert_eq!(Int::<4>::from_f64(42.9), Int::<4>::from_i64(42));
assert_eq!(Int::<4>::from_f64(-42.9), Int::<4>::from_i64(-42));
assert_eq!(Int::<4>::from_f64(f64::NAN), Int::<4>::ZERO);
assert_eq!(Int::<4>::from_i64(5).negate(), Int::<4>::from_i64(-5));
assert_eq!(Int::<4>::TEN, Int::<4>::from_i64(10));
let v = Int::<4>::from_i128(-9_876_543_210);
assert_eq!(Int::<4>::from_limbs_le(v.limbs_le()), v);
}
#[test]
fn int_signed_ordering() {
let neg = Int::<4>::from_i64(-5);
let zero = Int::<4>::ZERO;
let pos = Int::<4>::from_i64(5);
assert!(neg < zero);
assert!(zero < pos);
assert!(neg < pos);
assert!(Int::<4>::from_i64(-10) < Int::<4>::from_i64(-1));
assert_eq!(neg.max(pos), pos);
assert_eq!(neg.min(pos), neg);
assert_eq!(pos.cmp(&pos), Ordering::Equal);
}
}
#[cfg(all(test, feature = "wide"))]
mod unified_mg_feasibility {
use super::Int;
use crate::algos::support::mg_divide::div_wide_pow10;
use crate::int::types::compute_limbs::{ComputeLimbs, Limbs};
use crate::int::types::traits::BigInt;
use crate::support::rounding::RoundingMode;
fn scaled<const N: usize, const M: usize>(a: Int<N>, b: Int<N>, scale: u32) -> Int<M>
where
Limbs<N>: ComputeLimbs,
Int<M>: BigInt,
Limbs<M>: ComputeLimbs,
<Int<M> as BigInt>::Scratch: ComputeLimbs,
{
let prod: Int<M> = a.widen_mul::<Int<M>>(b);
div_wide_pow10::<Int<M>>(prod, scale, RoundingMode::HalfToEven)
}
#[test]
fn n2_widen4_scale5() {
let a = Int::<2>::from_i64(123456789);
let b = Int::<2>::from_i64(987654321);
let got = scaled::<2, 4>(a, b, 5);
assert_eq!(got, Int::<4>::from_i64(1219326311126));
}
#[test]
fn n1_widen2_scale3() {
let a = Int::<1>::from_i64(123456);
let b = Int::<1>::from_i64(654321);
let got = scaled::<1, 2>(a, b, 3);
assert_eq!(got, Int::<2>::from_i64(80779853));
}
#[test]
fn round_trip_mul_then_div() {
let x1 = 4242i64;
let ten_pow_4 = Int::<1>::from_i64(10_000);
let rt1 = scaled::<1, 2>(Int::<1>::from_i64(x1), ten_pow_4, 4);
assert_eq!(rt1, Int::<2>::from_i64(x1));
let x2 = 9_876_543_210i64;
let ten_pow_7 = Int::<2>::from_i64(10_000_000);
let rt2 = scaled::<2, 4>(Int::<2>::from_i64(x2), ten_pow_7, 7);
assert_eq!(rt2, Int::<4>::from_i64(x2));
}
#[test]
fn scale_zero_is_widen_mul_identity() {
let a1 = Int::<1>::from_i64(123456);
let b1 = Int::<1>::from_i64(654321);
let prod1: Int<2> = a1.widen_mul::<Int<2>>(b1);
assert_eq!(prod1, Int::<2>::from_i64(123456 * 654321));
let a2 = Int::<2>::from_i64(123456789);
let b2 = Int::<2>::from_i64(987654321);
let prod2: Int<4> = a2.widen_mul::<Int<4>>(b2);
assert_eq!(prod2, Int::<4>::from_i64(123456789i64 * 987654321i64));
}
#[test]
fn max_operand_each_width() {
let a1 = Int::<1>::from_i64(i64::MAX);
let b1 = Int::<1>::from_i64(i64::MAX);
let got1 = scaled::<1, 2>(a1, b1, 9);
let exact1 = (i64::MAX as i128) * (i64::MAX as i128);
let pow9 = 1_000_000_000i128;
let q1 = exact1 / pow9;
let r1 = exact1 % pow9;
let half = pow9 / 2;
let expect1 = if r1 > half || (r1 == half && (q1 & 1) == 1) {
q1 + 1
} else {
q1
};
assert_eq!(got1, Int::<2>::from_i128(expect1));
let big = Int::<2>::MAX; let got2 = scaled::<2, 4>(big, big, 20);
let prod2: Int<4> = big.widen_mul::<Int<4>>(big);
let pow20 = Int::<4>::TEN.pow(20);
let (q2, r2) = prod2.div_rem(pow20);
let half20 = pow20.div_rem(Int::<4>::from_i64(2)).0;
let expect2 = match r2.cmp(&half20) {
core::cmp::Ordering::Greater => q2.wrapping_add(Int::<4>::ONE),
core::cmp::Ordering::Equal => {
if (q2.as_limbs()[0] & 1) == 1 {
q2.wrapping_add(Int::<4>::ONE)
} else {
q2
}
}
core::cmp::Ordering::Less => q2,
};
assert_eq!(got2, expect2);
}
}
#[cfg(test)]
mod bit_count_contract_tests {
use super::{Int, Uint};
#[test]
fn int_bit_counts_match_signed_contract_at_edges() {
for v in [0_i64, 1, -1, i64::MAX, i64::MIN, 42, -42, 1 << 40, -(1 << 40)] {
let x = Int::<1>::from_i64(v);
assert_eq!(x.leading_zeros(), v.leading_zeros(), "i64 lz {v}");
assert_eq!(x.trailing_zeros(), v.trailing_zeros(), "i64 tz {v}");
assert_eq!(x.count_ones(), v.count_ones(), "i64 popcount {v}");
assert_eq!(x.count_zeros(), v.count_zeros(), "i64 cz {v}");
let mag_bits = 64 - v.unsigned_abs().leading_zeros();
assert_eq!(x.bit_length(), mag_bits, "i64 bit_length {v}");
}
for v in [
0_i128,
1,
-1,
i128::MAX,
i128::MIN,
(1_i128 << 64), (1_i128 << 64) - 1, -(1_i128 << 64),
i64::MAX as i128,
i64::MIN as i128,
] {
let x = Int::<2>::from_i128(v);
assert_eq!(x.leading_zeros(), v.leading_zeros(), "i128 lz {v}");
assert_eq!(x.trailing_zeros(), v.trailing_zeros(), "i128 tz {v}");
assert_eq!(x.count_ones(), v.count_ones(), "i128 popcount {v}");
assert_eq!(x.count_zeros(), v.count_zeros(), "i128 cz {v}");
let mag_bits = 128 - v.unsigned_abs().leading_zeros();
assert_eq!(x.bit_length(), mag_bits, "i128 bit_length {v}");
}
const B: u32 = Int::<4>::BITS;
let z = Int::<4>::ZERO;
assert_eq!(z.leading_zeros(), B);
assert_eq!(z.trailing_zeros(), B);
assert_eq!(z.count_ones(), 0);
assert_eq!(z.count_zeros(), B);
assert_eq!(z.bit_length(), 0);
let one = Int::<4>::ONE;
assert_eq!(one.leading_zeros(), B - 1);
assert_eq!(one.trailing_zeros(), 0);
assert_eq!(one.count_ones(), 1);
assert_eq!(one.count_zeros(), B - 1);
assert_eq!(one.bit_length(), 1);
let neg_one = Int::<4>::from_i64(-1);
assert_eq!(*neg_one.as_limbs(), [u64::MAX; 4]);
assert_eq!(neg_one.leading_zeros(), 0);
assert_eq!(neg_one.trailing_zeros(), 0);
assert_eq!(neg_one.count_ones(), B);
assert_eq!(neg_one.count_zeros(), 0);
assert_eq!(neg_one.bit_length(), 1);
let max = Int::<4>::MAX;
assert_eq!(max.leading_zeros(), 1);
assert_eq!(max.trailing_zeros(), 0);
assert_eq!(max.count_ones(), B - 1);
assert_eq!(max.count_zeros(), 1);
assert_eq!(max.bit_length(), B - 1);
let min = Int::<4>::MIN;
assert_eq!(min.leading_zeros(), 0);
assert_eq!(min.trailing_zeros(), B - 1);
assert_eq!(min.count_ones(), 1);
assert_eq!(min.count_zeros(), B - 1);
assert_eq!(min.bit_length(), B);
let two_64 = Int::<4>::from_u128(1u128 << 64);
assert_eq!(two_64.trailing_zeros(), 64);
assert_eq!(two_64.count_ones(), 1);
assert_eq!(two_64.leading_zeros(), B - 65);
assert_eq!(two_64.bit_length(), 65);
let two_64_m1 = Int::<4>::from_u128((1u128 << 64) - 1);
assert_eq!(two_64_m1.trailing_zeros(), 0);
assert_eq!(two_64_m1.count_ones(), 64);
assert_eq!(two_64_m1.leading_zeros(), B - 64);
assert_eq!(two_64_m1.bit_length(), 64);
}
#[test]
fn uint_bit_counts_match_unsigned_contract_at_edges() {
for v in [0_u64, 1, u64::MAX, 42, 1 << 40] {
let x = Uint::<1>::from_u64(v);
assert_eq!(x.leading_zeros(), v.leading_zeros(), "u64 lz {v}");
assert_eq!(x.count_ones(), v.count_ones(), "u64 popcount {v}");
assert_eq!(x.bit_length(), 64 - v.leading_zeros(), "u64 bit_length {v}");
}
for v in [0_u128, 1, u128::MAX, 1u128 << 64, (1u128 << 64) - 1] {
let x = Uint::<2>::from_u128(v);
assert_eq!(x.leading_zeros(), v.leading_zeros(), "u128 lz {v}");
assert_eq!(x.count_ones(), v.count_ones(), "u128 popcount {v}");
assert_eq!(x.bit_length(), 128 - v.leading_zeros(), "u128 bit_length {v}");
}
const B: u32 = Uint::<4>::BITS; let zero = Uint::<4>::ZERO;
assert_eq!(zero.leading_zeros(), B);
assert_eq!(zero.count_ones(), 0);
assert_eq!(zero.bit_length(), 0);
let umax = Uint::<4>::from_limbs([u64::MAX; 4]);
assert_eq!(umax.leading_zeros(), 0);
assert_eq!(umax.count_ones(), B);
assert_eq!(umax.bit_length(), B);
let two_64 = Uint::<4>::from_u128(1u128 << 64);
assert_eq!(two_64.count_ones(), 1);
assert_eq!(two_64.leading_zeros(), B - 65);
assert_eq!(two_64.bit_length(), 65);
}
}
#[cfg(test)]
mod hint_tests {
use super::{Int, Uint};
#[test]
fn signed_add_sub_neg() {
let a = Int::<4>::from_i128(5);
let b = Int::<4>::from_i128(3);
assert_eq!(a.wrapping_add(b), Int::<4>::from_i128(8));
assert_eq!(a.wrapping_sub(b), Int::<4>::from_i128(2));
assert_eq!(b.wrapping_sub(a), Int::<4>::from_i128(-2));
assert_eq!(a.negate(), Int::<4>::from_i128(-5));
assert_eq!(Int::<4>::ZERO.negate(), Int::<4>::ZERO);
}
#[test]
fn signed_mul_div_rem() {
let six = Int::<8>::from_i128(6);
let two = Int::<8>::from_i128(2);
let three = Int::<8>::from_i128(3);
assert_eq!(six.wrapping_mul(three), Int::<8>::from_i128(18));
assert_eq!(six.wrapping_div(two), three);
assert_eq!(
Int::<8>::from_i128(7).wrapping_rem(three),
Int::<8>::from_i128(1)
);
assert_eq!(
Int::<8>::from_i128(-7).wrapping_rem(three),
Int::<8>::from_i128(-1)
);
assert_eq!(six.negate().wrapping_mul(three), Int::<8>::from_i128(-18));
}
#[test]
fn checked_overflow() {
assert_eq!(Int::<4>::MAX.checked_add(Int::<4>::ONE), None);
assert_eq!(Int::<4>::MIN.checked_sub(Int::<4>::ONE), None);
assert_eq!(Int::<4>::MIN.checked_neg(), None);
assert_eq!(
Int::<4>::from_i128(2).checked_add(Int::<4>::from_i128(3)),
Some(Int::<4>::from_i128(5))
);
}
#[test]
fn from_str_and_pow() {
let ten = Int::<16>::from_str_radix("10", 10).unwrap();
assert_eq!(ten, Int::<16>::from_i128(10));
assert_eq!(ten.pow(3), Int::<16>::from_i128(1000));
let big = Int::<16>::from_str_radix("10", 10).unwrap().pow(40);
let from_str =
Int::<16>::from_str_radix("10000000000000000000000000000000000000000", 10).unwrap();
assert_eq!(big, from_str);
assert_eq!(
Int::<4>::from_str_radix("-42", 10).unwrap(),
Int::<4>::from_i128(-42)
);
}
#[test]
fn ordering_and_resize() {
assert!(Int::<4>::from_i128(-1) < Int::<4>::ZERO);
assert!(Int::<4>::MIN < Int::<4>::MAX);
let v = Int::<4>::from_i128(-123_456_789);
let wide: Int<16> = v.resize();
let back: Int<4> = wide.resize();
assert_eq!(back, v);
assert_eq!(wide, Int::<16>::from_i128(-123_456_789));
}
#[cfg(feature = "_wide-support")]
#[test]
fn isqrt_and_f64() {
assert_eq!(Int::<8>::from_i128(144).isqrt(), Int::<8>::from_i128(12));
assert_eq!(Int::<4>::from_i128(1_000_000).as_f64(), 1_000_000.0);
assert_eq!(Int::<4>::from_f64(-2_500.0), Int::<4>::from_i128(-2500));
}
#[test]
fn uint256_is_zero_and_bit_helpers() {
let zero = Uint::<4>::ZERO;
let one = Uint::<4>::from_str_radix("1", 10).unwrap();
let two = Uint::<4>::from_str_radix("2", 10).unwrap();
assert!(zero.is_zero());
assert!(!one.is_zero());
assert!(one.is_power_of_two());
assert!(two.is_power_of_two());
let three = Uint::<4>::from_str_radix("3", 10).unwrap();
assert!(!three.is_power_of_two());
assert_eq!(zero.next_power_of_two(), one);
assert_eq!(one.next_power_of_two(), one);
let four = Uint::<4>::from_str_radix("4", 10).unwrap();
assert_eq!(three.next_power_of_two(), four);
assert_eq!(zero.count_ones(), 0);
assert_eq!(one.count_ones(), 1);
assert_eq!(zero.leading_zeros(), Uint::<4>::BITS);
assert_eq!(one.leading_zeros(), Uint::<4>::BITS - 1);
}
#[test]
fn uint256_parse_arithmetic_and_pow() {
assert!(Uint::<4>::from_str_radix("10", 2).is_err());
assert!(Uint::<4>::from_str_radix("1a", 10).is_err());
let two = Uint::<4>::from_str_radix("2", 10).unwrap();
let three = Uint::<4>::from_str_radix("3", 10).unwrap();
let six = Uint::<4>::from_str_radix("6", 10).unwrap();
let seven = Uint::<4>::from_str_radix("7", 10).unwrap();
assert_eq!(three - two, Uint::<4>::from_str_radix("1", 10).unwrap());
assert_eq!(six / two, three);
assert_eq!(seven % three, Uint::<4>::from_str_radix("1", 10).unwrap());
let five = Uint::<4>::from_str_radix("5", 10).unwrap();
let four = Uint::<4>::from_str_radix("4", 10).unwrap();
let one = Uint::<4>::from_str_radix("1", 10).unwrap();
assert_eq!(five & four, four);
assert_eq!(five | one, five);
assert_eq!(five ^ four, one);
let p10 = two.pow(10);
assert_eq!(p10, Uint::<4>::from_str_radix("1024", 10).unwrap());
let signed = three.cast_signed();
assert_eq!(signed, Int::<4>::from_i128(3));
}
#[test]
fn signed_bit_and_trailing_zeros() {
let v = Int::<4>::from_i128(0b1100);
assert!(v.bit(2));
assert!(v.bit(3));
assert!(!v.bit(0));
assert!(!v.bit(1));
assert!(!v.bit(1000));
let n = Int::<4>::from_i128(-1);
assert!(n.bit(1000));
assert_eq!(Int::<4>::from_i128(8).trailing_zeros(), 3);
assert_eq!(Int::<4>::ZERO.trailing_zeros(), Int::<4>::BITS);
}
#[test]
fn as_u128_returns_low_magnitude_bits() {
let src = Int::<4>::from_i128(123_456_789);
let dst: u128 = src.as_u128();
assert_eq!(dst, 123_456_789);
let dst: u128 = Int::<4>::ZERO.as_u128();
assert_eq!(dst, 0);
}
}