use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt;
use std::str::FromStr;
const KARATSUBA_THRESHOLD: usize = 32;
pub const MAX_RESULT_BITS: u64 = 1 << 24;
const DECIMAL_CHUNK_RADIX: u64 = 10_000_000_000_000_000_000;
const DECIMAL_CHUNK_DIGITS: usize = 19;
#[derive(Clone, PartialEq, Eq, Hash, Default)]
pub struct BigNat {
limbs: Vec<u64>,
}
impl BigNat {
pub const fn zero() -> Self {
BigNat { limbs: Vec::new() }
}
pub fn one() -> Self {
BigNat { limbs: vec![1] }
}
pub fn from_limbs(mut limbs: Vec<u64>) -> Self {
while limbs.last() == Some(&0) {
limbs.pop();
}
BigNat { limbs }
}
pub fn as_limbs(&self) -> &[u64] {
&self.limbs
}
pub fn limb_count(&self) -> usize {
self.limbs.len()
}
pub fn is_zero(&self) -> bool {
self.limbs.is_empty()
}
pub fn is_one(&self) -> bool {
self.limbs.len() == 1 && self.limbs[0] == 1
}
pub fn to_u64(&self) -> Option<u64> {
match self.limbs.len() {
0 => Some(0),
1 => Some(self.limbs[0]),
_ => None,
}
}
pub fn to_u32(&self) -> Option<u32> {
self.to_u64().and_then(|n| u32::try_from(n).ok())
}
pub fn bit_length(&self) -> u64 {
match self.limbs.last() {
None => 0,
Some(&top) => {
let full = (self.limbs.len() as u64 - 1) * 64;
full + (64 - u64::from(top.leading_zeros()))
}
}
}
pub fn succ(&self) -> Self {
self.add_u64(1)
}
pub fn pred(&self) -> Self {
if self.is_zero() {
BigNat::zero()
} else {
self.sub_u64(1)
}
}
pub fn add(&self, rhs: &Self) -> Self {
BigNat::from_limbs(add_limbs(&self.limbs, &rhs.limbs))
}
pub fn add_u64(&self, rhs: u64) -> Self {
self.add(&BigNat::from(rhs))
}
pub fn sub(&self, rhs: &Self) -> Self {
if self <= rhs {
BigNat::zero()
} else {
BigNat::from_limbs(sub_limbs(&self.limbs, &rhs.limbs))
}
}
pub fn sub_u64(&self, rhs: u64) -> Self {
self.sub(&BigNat::from(rhs))
}
pub fn mul(&self, rhs: &Self) -> Self {
BigNat::from_limbs(mul_limbs(&self.limbs, &rhs.limbs))
}
pub fn mul_u64(&self, rhs: u64) -> Self {
if rhs == 0 || self.is_zero() {
return BigNat::zero();
}
let mut out = Vec::with_capacity(self.limbs.len() + 1);
let mut carry: u128 = 0;
for &limb in &self.limbs {
let t = u128::from(limb) * u128::from(rhs) + carry;
out.push(t as u64);
carry = t >> 64;
}
if carry != 0 {
out.push(carry as u64);
}
BigNat::from_limbs(out)
}
pub fn div(&self, rhs: &Self) -> Self {
self.div_rem(rhs).0
}
pub fn rem(&self, rhs: &Self) -> Self {
self.div_rem(rhs).1
}
pub fn div_rem(&self, rhs: &Self) -> (Self, Self) {
if rhs.is_zero() {
return (BigNat::zero(), self.clone());
}
if self < rhs {
return (BigNat::zero(), self.clone());
}
if rhs.limbs.len() == 1 {
let (q, r) = div_rem_limbs_by_u64(&self.limbs, rhs.limbs[0]);
return (BigNat::from_limbs(q), BigNat::from(r));
}
let (q, r) = div_rem_knuth(&self.limbs, &rhs.limbs);
(BigNat::from_limbs(q), BigNat::from_limbs(r))
}
pub fn div_rem_u64(&self, rhs: u64) -> (Self, u64) {
if rhs == 0 {
return (BigNat::zero(), 0);
}
let (q, r) = div_rem_limbs_by_u64(&self.limbs, rhs);
(BigNat::from_limbs(q), r)
}
pub fn pow(&self, exp: &Self) -> Option<Self> {
if exp.is_zero() {
return Some(BigNat::one());
}
if self.is_zero() {
return Some(BigNat::zero());
}
if self.is_one() {
return Some(BigNat::one());
}
let e = exp.to_u64()?;
let base_bits = self.bit_length();
if u128::from(e) * u128::from(base_bits) > u128::from(MAX_RESULT_BITS) {
return None;
}
let mut result = BigNat::one();
let mut base = self.clone();
let mut e = e;
loop {
if e & 1 == 1 {
result = result.mul(&base);
}
e >>= 1;
if e == 0 {
break;
}
base = base.mul(&base);
}
Some(result)
}
pub fn gcd(&self, rhs: &Self) -> Self {
let mut a = self.clone();
let mut b = rhs.clone();
while !b.is_zero() {
let r = a.rem(&b);
a = b;
b = r;
}
a
}
pub fn beq(&self, rhs: &Self) -> bool {
self == rhs
}
pub fn ble(&self, rhs: &Self) -> bool {
self <= rhs
}
pub fn land(&self, rhs: &Self) -> Self {
let n = self.limbs.len().min(rhs.limbs.len());
let out: Vec<u64> = (0..n).map(|i| self.limbs[i] & rhs.limbs[i]).collect();
BigNat::from_limbs(out)
}
pub fn lor(&self, rhs: &Self) -> Self {
let (long, short) = if self.limbs.len() >= rhs.limbs.len() {
(&self.limbs, &rhs.limbs)
} else {
(&rhs.limbs, &self.limbs)
};
let mut out = long.clone();
for (o, s) in out.iter_mut().zip(short.iter()) {
*o |= *s;
}
BigNat::from_limbs(out)
}
pub fn lxor(&self, rhs: &Self) -> Self {
let (long, short) = if self.limbs.len() >= rhs.limbs.len() {
(&self.limbs, &rhs.limbs)
} else {
(&rhs.limbs, &self.limbs)
};
let mut out = long.clone();
for (o, s) in out.iter_mut().zip(short.iter()) {
*o ^= *s;
}
BigNat::from_limbs(out)
}
pub fn checked_shl(&self, n: &Self) -> Option<Self> {
if self.is_zero() {
return Some(BigNat::zero());
}
let n = n.to_u64()?;
if u128::from(self.bit_length()) + u128::from(n) > u128::from(MAX_RESULT_BITS) {
return None;
}
Some(self.shl_bits(n))
}
pub fn shr(&self, n: &Self) -> Self {
match n.to_u64() {
None => BigNat::zero(),
Some(n) => self.shr_bits(n),
}
}
pub fn log2(&self) -> Self {
if self.is_zero() {
BigNat::zero()
} else {
BigNat::from(self.bit_length() - 1)
}
}
fn shl_bits(&self, bits: u64) -> Self {
if self.is_zero() || bits == 0 {
return self.clone();
}
let limb_shift = (bits / 64) as usize;
let bit_shift = (bits % 64) as u32;
let mut out = vec![0u64; limb_shift];
if bit_shift == 0 {
out.extend_from_slice(&self.limbs);
} else {
let mut carry = 0u64;
for &limb in &self.limbs {
out.push((limb << bit_shift) | carry);
carry = limb >> (64 - bit_shift);
}
if carry != 0 {
out.push(carry);
}
}
BigNat::from_limbs(out)
}
fn shr_bits(&self, bits: u64) -> Self {
if bits >= self.bit_length() {
return BigNat::zero();
}
let limb_shift = (bits / 64) as usize;
let bit_shift = (bits % 64) as u32;
let src = &self.limbs[limb_shift..];
if bit_shift == 0 {
return BigNat::from_limbs(src.to_vec());
}
let mut out = Vec::with_capacity(src.len());
for i in 0..src.len() {
let lo = src[i] >> bit_shift;
let hi = if i + 1 < src.len() {
src[i + 1] << (64 - bit_shift)
} else {
0
};
out.push(lo | hi);
}
BigNat::from_limbs(out)
}
pub fn from_decimal_str(s: &str) -> Option<Self> {
if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
let bytes = s.as_bytes();
let mut acc = BigNat::zero();
let mut pos = 0;
let first_len = {
let rem = bytes.len() % DECIMAL_CHUNK_DIGITS;
if rem == 0 {
DECIMAL_CHUNK_DIGITS
} else {
rem
}
};
while pos < bytes.len() {
let len = if pos == 0 {
first_len
} else {
DECIMAL_CHUNK_DIGITS
};
let mut chunk: u64 = 0;
for &b in &bytes[pos..pos + len] {
chunk = chunk * 10 + u64::from(b - b'0');
}
let radix = if len == DECIMAL_CHUNK_DIGITS {
DECIMAL_CHUNK_RADIX
} else {
10u64.pow(len as u32)
};
acc = acc.mul_u64(radix).add_u64(chunk);
pos += len;
}
Some(acc)
}
}
fn add_limbs(a: &[u64], b: &[u64]) -> Vec<u64> {
let (long, short) = if a.len() >= b.len() { (a, b) } else { (b, a) };
let mut out = Vec::with_capacity(long.len() + 1);
let mut carry = 0u64;
for i in 0..long.len() {
let s = u128::from(long[i])
+ u128::from(if i < short.len() { short[i] } else { 0 })
+ u128::from(carry);
out.push(s as u64);
carry = (s >> 64) as u64;
}
if carry != 0 {
out.push(carry);
}
out
}
fn sub_limbs(a: &[u64], b: &[u64]) -> Vec<u64> {
debug_assert!(cmp_limbs(a, b) != Ordering::Less);
let mut out = Vec::with_capacity(a.len());
let mut borrow = 0u64;
for i in 0..a.len() {
let bi = if i < b.len() { b[i] } else { 0 };
let (d1, b1) = a[i].overflowing_sub(bi);
let (d2, b2) = d1.overflowing_sub(borrow);
out.push(d2);
borrow = u64::from(b1) + u64::from(b2);
}
debug_assert_eq!(borrow, 0);
out
}
fn cmp_limbs(a: &[u64], b: &[u64]) -> Ordering {
if a.len() != b.len() {
return a.len().cmp(&b.len());
}
for i in (0..a.len()).rev() {
match a[i].cmp(&b[i]) {
Ordering::Equal => {}
ord => return ord,
}
}
Ordering::Equal
}
fn mul_limbs(a: &[u64], b: &[u64]) -> Vec<u64> {
if a.is_empty() || b.is_empty() {
return Vec::new();
}
if a.len().min(b.len()) < KARATSUBA_THRESHOLD {
mul_schoolbook(a, b)
} else {
mul_karatsuba(a, b)
}
}
fn mul_schoolbook(a: &[u64], b: &[u64]) -> Vec<u64> {
let mut out = vec![0u64; a.len() + b.len()];
for (i, &ai) in a.iter().enumerate() {
if ai == 0 {
continue;
}
let ai = u128::from(ai);
let mut carry: u128 = 0;
for (j, &bj) in b.iter().enumerate() {
let t = ai * u128::from(bj) + u128::from(out[i + j]) + carry;
out[i + j] = t as u64;
carry = t >> 64;
}
out[i + b.len()] = carry as u64;
}
out
}
fn mul_karatsuba(a: &[u64], b: &[u64]) -> Vec<u64> {
let m = a.len().max(b.len()) / 2;
let (a0, a1) = a.split_at(a.len().min(m));
let (b0, b1) = b.split_at(b.len().min(m));
let z0 = mul_limbs(&normalized(a0), &normalized(b0));
let z2 = mul_limbs(&normalized(a1), &normalized(b1));
let a01 = add_limbs(&normalized(a0), &normalized(a1));
let b01 = add_limbs(&normalized(b0), &normalized(b1));
let z11 = mul_limbs(&normalized(&a01), &normalized(&b01));
let z1 = sub_limbs(&sub_limbs(&pad_to(&z11, z0.len().max(z2.len())), &z0), &z2);
let mut out = vec![0u64; a.len() + b.len() + 1];
add_into(&mut out, &normalized(&z0), 0);
add_into(&mut out, &normalized(&z1), m);
add_into(&mut out, &normalized(&z2), 2 * m);
out
}
fn normalized(a: &[u64]) -> Vec<u64> {
let mut v = a.to_vec();
while v.last() == Some(&0) {
v.pop();
}
v
}
fn pad_to(a: &[u64], len: usize) -> Vec<u64> {
let mut v = a.to_vec();
while v.len() < len {
v.push(0);
}
v
}
fn add_into(acc: &mut [u64], x: &[u64], shift: usize) {
let mut carry = 0u64;
let mut i = 0;
while i < x.len() || carry != 0 {
let idx = shift + i;
debug_assert!(idx < acc.len());
let xi = if i < x.len() { x[i] } else { 0 };
let s = u128::from(acc[idx]) + u128::from(xi) + u128::from(carry);
acc[idx] = s as u64;
carry = (s >> 64) as u64;
i += 1;
}
}
fn div_rem_limbs_by_u64(a: &[u64], d: u64) -> (Vec<u64>, u64) {
debug_assert!(d != 0);
let d128 = u128::from(d);
let mut q = vec![0u64; a.len()];
let mut rem: u128 = 0;
for i in (0..a.len()).rev() {
let cur = (rem << 64) | u128::from(a[i]);
q[i] = (cur / d128) as u64;
rem = cur % d128;
}
(q, rem as u64)
}
fn div_rem_knuth(u_in: &[u64], v_in: &[u64]) -> (Vec<u64>, Vec<u64>) {
let n = v_in.len();
debug_assert!(n >= 2);
let shift = v_in[n - 1].leading_zeros();
let v = shl_slice(v_in, shift);
let mut u = shl_slice(u_in, shift);
debug_assert_eq!(v.len(), n);
u.push(0);
let m = u.len() - 1 - n;
let mut q = vec![0u64; m + 1];
let b: u128 = 1 << 64;
for j in (0..=m).rev() {
let top = (u128::from(u[j + n]) << 64) | u128::from(u[j + n - 1]);
let mut qhat = top / u128::from(v[n - 1]);
let mut rhat = top % u128::from(v[n - 1]);
loop {
if qhat >= b || qhat * u128::from(v[n - 2]) > (rhat << 64) | u128::from(u[j + n - 2]) {
qhat -= 1;
rhat += u128::from(v[n - 1]);
if rhat < b {
continue;
}
}
break;
}
let mut mul_carry: u128 = 0;
let mut borrow: u64 = 0;
for i in 0..n {
let p = qhat * u128::from(v[i]) + mul_carry;
mul_carry = p >> 64;
let (d1, b1) = u[j + i].overflowing_sub(p as u64);
let (d2, b2) = d1.overflowing_sub(borrow);
u[j + i] = d2;
borrow = u64::from(b1) + u64::from(b2);
}
let (d1, b1) = u[j + n].overflowing_sub(mul_carry as u64);
let (d2, b2) = d1.overflowing_sub(borrow);
u[j + n] = d2;
if b1 || b2 {
qhat -= 1;
let mut carry = 0u64;
for i in 0..n {
let s = u128::from(u[j + i]) + u128::from(v[i]) + u128::from(carry);
u[j + i] = s as u64;
carry = (s >> 64) as u64;
}
u[j + n] = u[j + n].wrapping_add(carry);
}
q[j] = qhat as u64;
}
let rem = shr_slice(&u[..n], shift);
(q, rem)
}
fn shl_slice(a: &[u64], shift: u32) -> Vec<u64> {
if shift == 0 {
return a.to_vec();
}
let mut out = Vec::with_capacity(a.len() + 1);
let mut carry = 0u64;
for &limb in a {
out.push((limb << shift) | carry);
carry = limb >> (64 - shift);
}
if carry != 0 {
out.push(carry);
}
out
}
fn shr_slice(a: &[u64], shift: u32) -> Vec<u64> {
if shift == 0 {
return a.to_vec();
}
let mut out = Vec::with_capacity(a.len());
for i in 0..a.len() {
let lo = a[i] >> shift;
let hi = if i + 1 < a.len() {
a[i + 1] << (64 - shift)
} else {
0
};
out.push(lo | hi);
}
out
}
impl Ord for BigNat {
fn cmp(&self, other: &Self) -> Ordering {
cmp_limbs(&self.limbs, &other.limbs)
}
}
impl PartialOrd for BigNat {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq<u64> for BigNat {
fn eq(&self, other: &u64) -> bool {
self.to_u64() == Some(*other)
}
}
impl PartialOrd<u64> for BigNat {
fn partial_cmp(&self, other: &u64) -> Option<Ordering> {
Some(match self.to_u64() {
Some(n) => n.cmp(other),
None => Ordering::Greater,
})
}
}
impl From<u64> for BigNat {
fn from(n: u64) -> Self {
if n == 0 {
BigNat::zero()
} else {
BigNat { limbs: vec![n] }
}
}
}
impl From<u32> for BigNat {
fn from(n: u32) -> Self {
BigNat::from(u64::from(n))
}
}
impl From<u128> for BigNat {
fn from(n: u128) -> Self {
BigNat::from_limbs(vec![n as u64, (n >> 64) as u64])
}
}
impl From<usize> for BigNat {
fn from(n: usize) -> Self {
BigNat::from(n as u64)
}
}
impl fmt::Display for BigNat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_zero() {
return f.write_str("0");
}
let mut chunks: Vec<u64> = Vec::new();
let mut cur = Cow::Borrowed(self);
while !cur.is_zero() {
let (q, r) = cur.div_rem_u64(DECIMAL_CHUNK_RADIX);
chunks.push(r);
cur = Cow::Owned(q);
}
let mut s = String::new();
for (i, chunk) in chunks.iter().rev().enumerate() {
if i == 0 {
s.push_str(&chunk.to_string());
} else {
s.push_str(&format!("{:0width$}", chunk, width = DECIMAL_CHUNK_DIGITS));
}
}
f.write_str(&s)
}
}
impl fmt::Debug for BigNat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BigNat({})", self)
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ParseBigNatError;
impl fmt::Display for ParseBigNatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid decimal natural number")
}
}
impl std::error::Error for ParseBigNatError {}
impl FromStr for BigNat {
type Err = ParseBigNatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
BigNat::from_decimal_str(s).ok_or(ParseBigNatError)
}
}
#[cfg(test)]
mod tests;