use core::cmp::Ordering;
use core::fmt;
use core::str::FromStr;
use alloc::vec::Vec;
use crate::error::{Error, Result};
use crate::limb::{LIMB_BITS, Limb, adc, mac, sbb};
#[derive(Clone, PartialEq, Eq, Default)]
pub struct Nat {
limbs: Vec<Limb>,
}
impl Nat {
#[inline]
pub fn zero() -> Self {
Nat { limbs: Vec::new() }
}
#[inline]
pub fn one() -> Self {
Nat::from_u64(1)
}
#[inline]
pub fn from_u64(v: u64) -> Self {
let mut n = Nat {
limbs: if v == 0 { Vec::new() } else { alloc::vec![v] },
};
n.normalize();
n
}
pub fn from_u128(v: u128) -> Self {
let lo = v as Limb;
let hi = (v >> LIMB_BITS) as Limb;
let mut n = Nat {
limbs: alloc::vec![lo, hi],
};
n.normalize();
n
}
#[inline]
pub fn is_zero(&self) -> bool {
self.limbs.is_empty()
}
#[inline]
pub fn is_even(&self) -> bool {
self.limbs.first().is_none_or(|&l| l & 1 == 0)
}
pub fn bit_len(&self) -> u64 {
match self.limbs.last() {
None => 0,
Some(&top) => {
(self.limbs.len() as u64 - 1) * LIMB_BITS as u64
+ (LIMB_BITS - top.leading_zeros()) as u64
}
}
}
pub fn trailing_zeros(&self) -> u64 {
for (i, &l) in self.limbs.iter().enumerate() {
if l != 0 {
return i as u64 * LIMB_BITS as u64 + l.trailing_zeros() as u64;
}
}
0
}
fn normalize(&mut self) {
while matches!(self.limbs.last(), Some(&0)) {
self.limbs.pop();
}
}
fn cmp_ref(&self, other: &Nat) -> Ordering {
match self.limbs.len().cmp(&other.limbs.len()) {
Ordering::Equal => {}
non_eq => return non_eq,
}
for (a, b) in self.limbs.iter().rev().zip(other.limbs.iter().rev()) {
match a.cmp(b) {
Ordering::Equal => continue,
non_eq => return non_eq,
}
}
Ordering::Equal
}
pub fn add(&self, rhs: &Nat) -> Nat {
let (long, short) = if self.limbs.len() >= rhs.limbs.len() {
(self, rhs)
} else {
(rhs, self)
};
let mut out = Vec::with_capacity(long.limbs.len() + 1);
let mut carry = 0;
for (i, &a) in long.limbs.iter().enumerate() {
let b = short.limbs.get(i).copied().unwrap_or(0);
let (s, c) = adc(a, b, carry);
out.push(s);
carry = c;
}
if carry != 0 {
out.push(carry);
}
Nat { limbs: out }
}
pub fn checked_sub(&self, rhs: &Nat) -> Option<Nat> {
if self.cmp_ref(rhs) == Ordering::Less {
return None;
}
let mut out = Vec::with_capacity(self.limbs.len());
let mut borrow = 0;
for (i, &a) in self.limbs.iter().enumerate() {
let b = rhs.limbs.get(i).copied().unwrap_or(0);
let (d, bb) = sbb(a, b, borrow);
out.push(d);
borrow = bb;
}
debug_assert_eq!(borrow, 0, "checked_sub borrow escaped after a >= b check");
let mut n = Nat { limbs: out };
n.normalize();
Some(n)
}
pub fn mul(&self, rhs: &Nat) -> Nat {
if self.is_zero() || rhs.is_zero() {
return Nat::zero();
}
let mut out = alloc::vec![0 as Limb; self.limbs.len() + rhs.limbs.len()];
for (i, &a) in self.limbs.iter().enumerate() {
let mut carry = 0;
for (j, &b) in rhs.limbs.iter().enumerate() {
let (lo, hi) = mac(out[i + j], a, b, carry);
out[i + j] = lo;
carry = hi;
}
out[i + rhs.limbs.len()] = carry;
}
let mut n = Nat { limbs: out };
n.normalize();
n
}
pub fn shl(&self, bits: u64) -> Nat {
if self.is_zero() || bits == 0 {
return self.clone();
}
let limb_shift = (bits / LIMB_BITS as u64) as usize;
let bit_shift = (bits % LIMB_BITS as u64) as u32;
let mut out = alloc::vec![0 as Limb; limb_shift];
if bit_shift == 0 {
out.extend_from_slice(&self.limbs);
} else {
let mut carry = 0;
for &l in &self.limbs {
out.push((l << bit_shift) | carry);
carry = l >> (LIMB_BITS - bit_shift);
}
if carry != 0 {
out.push(carry);
}
}
let mut n = Nat { limbs: out };
n.normalize();
n
}
pub fn shr(&self, bits: u64) -> Nat {
if self.is_zero() || bits == 0 {
return self.clone();
}
let limb_shift = (bits / LIMB_BITS as u64) as usize;
let bit_shift = (bits % LIMB_BITS as u64) as u32;
if limb_shift >= self.limbs.len() {
return Nat::zero();
}
let src = &self.limbs[limb_shift..];
let mut out = Vec::with_capacity(src.len());
if bit_shift == 0 {
out.extend_from_slice(src);
} else {
for i in 0..src.len() {
let lo = src[i] >> bit_shift;
let hi = src
.get(i + 1)
.map(|&h| h << (LIMB_BITS - bit_shift))
.unwrap_or(0);
out.push(lo | hi);
}
}
let mut n = Nat { limbs: out };
n.normalize();
n
}
pub fn gcd(&self, rhs: &Nat) -> Nat {
if self.is_zero() {
return rhs.clone();
}
if rhs.is_zero() {
return self.clone();
}
let mut u = self.clone();
let mut v = rhs.clone();
let shift = u.trailing_zeros().min(v.trailing_zeros());
u = u.shr(u.trailing_zeros());
v = v.shr(v.trailing_zeros());
loop {
v = v.shr(v.trailing_zeros());
if u.cmp_ref(&v) == Ordering::Greater {
core::mem::swap(&mut u, &mut v);
}
v = v
.checked_sub(&u)
.expect("binary gcd: v >= u by construction");
if v.is_zero() {
break;
}
}
u.shl(shift)
}
#[inline]
pub fn bit(&self, i: u64) -> bool {
let limb = (i / LIMB_BITS as u64) as usize;
match self.limbs.get(limb) {
Some(&l) => (l >> (i % LIMB_BITS as u64)) & 1 == 1,
None => false,
}
}
pub fn div_rem(&self, rhs: &Nat) -> Option<(Nat, Nat)> {
if rhs.is_zero() {
return None;
}
if self.cmp_ref(rhs) == Ordering::Less {
return Some((Nat::zero(), self.clone()));
}
let one = Nat::one();
let mut q = Nat::zero();
let mut r = Nat::zero();
for i in (0..self.bit_len()).rev() {
r = r.shl(1);
if self.bit(i) {
r = r.add(&one);
}
q = q.shl(1);
if r.cmp_ref(rhs) != Ordering::Less {
r = r.checked_sub(rhs).expect("r >= rhs checked");
q = q.add(&one);
}
}
Some((q, r))
}
fn divmod_small(&self, d: Limb) -> (Nat, Limb) {
debug_assert!(d != 0, "divmod_small by zero");
let dd = d as u128;
let mut rem: u128 = 0;
let mut q = alloc::vec![0 as Limb; self.limbs.len()];
for i in (0..self.limbs.len()).rev() {
let cur = (rem << LIMB_BITS) | self.limbs[i] as u128;
q[i] = (cur / dd) as Limb;
rem = cur % dd;
}
let mut n = Nat { limbs: q };
n.normalize();
(n, rem as Limb)
}
fn mul_add_small(&self, mul: Limb, add: Limb) -> Nat {
let mut out = Vec::with_capacity(self.limbs.len() + 1);
let mut carry = add as u128;
for &l in &self.limbs {
let t = l as u128 * mul as u128 + carry;
out.push(t as Limb);
carry = t >> LIMB_BITS;
}
while carry != 0 {
out.push(carry as Limb);
carry >>= LIMB_BITS;
}
let mut n = Nat { limbs: out };
n.normalize();
n
}
}
impl PartialOrd for Nat {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Nat {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.cmp_ref(other)
}
}
impl From<u64> for Nat {
#[inline]
fn from(v: u64) -> Self {
Nat::from_u64(v)
}
}
impl From<u128> for Nat {
#[inline]
fn from(v: u128) -> Self {
Nat::from_u128(v)
}
}
impl FromStr for Nat {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
if s.is_empty() {
return Err(Error::Parse);
}
let mut n = Nat::zero();
for b in s.bytes() {
if !b.is_ascii_digit() {
return Err(Error::Parse);
}
n = n.mul_add_small(10, (b - b'0') as Limb);
}
Ok(n)
}
}
impl fmt::Display for Nat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_zero() {
return f.write_str("0");
}
let mut n = self.clone();
let mut buf = Vec::new();
while !n.is_zero() {
let (q, r) = n.divmod_small(10);
buf.push(b'0' + r as u8);
n = q;
}
buf.reverse();
f.write_str(core::str::from_utf8(&buf).unwrap_or("<nan>"))
}
}
impl fmt::LowerHex for Nat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_zero() {
return f.write_str("0");
}
let mut it = self.limbs.iter().rev();
write!(f, "{:x}", it.next().expect("non-empty checked above"))?;
for limb in it {
write!(f, "{limb:016x}")?;
}
Ok(())
}
}
impl fmt::Debug for Nat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Nat({self})")
}
}
impl core::ops::Add for &Nat {
type Output = Nat;
#[inline]
fn add(self, rhs: &Nat) -> Nat {
Nat::add(self, rhs)
}
}
impl core::ops::Mul for &Nat {
type Output = Nat;
#[inline]
fn mul(self, rhs: &Nat) -> Nat {
Nat::mul(self, rhs)
}
}