use std::cmp::Ordering;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct BigUint {
limbs: Vec<u64>,
}
impl From<u64> for BigUint {
fn from(v: u64) -> BigUint {
let mut b = BigUint { limbs: vec![v] };
b.trim();
b
}
}
impl BigUint {
fn trim(&mut self) {
while self.limbs.last() == Some(&0) {
self.limbs.pop();
}
}
pub fn is_zero(&self) -> bool {
self.limbs.is_empty()
}
pub fn bits(&self) -> usize {
self.limbs.last().map_or(0, |top| 64 * (self.limbs.len() - 1) + (64 - top.leading_zeros() as usize))
}
#[must_use]
pub fn mul_small(&self, k: u64) -> BigUint {
let mut out = Vec::with_capacity(self.limbs.len() + 1);
let mut carry = 0u128;
for &l in &self.limbs {
let v = u128::from(l) * u128::from(k) + carry;
out.push(v as u64);
carry = v >> 64;
}
if carry > 0 {
out.push(carry as u64);
}
let mut b = BigUint { limbs: out };
b.trim();
b
}
#[must_use]
pub fn div_small(&self, k: u64) -> (BigUint, u64) {
assert!(k != 0, "division by zero");
let mut out = vec![0u64; self.limbs.len()];
let mut rem = 0u128;
for (i, &l) in self.limbs.iter().enumerate().rev() {
let v = (rem << 64) | u128::from(l);
out[i] = (v / u128::from(k)) as u64;
rem = v % u128::from(k);
}
let mut b = BigUint { limbs: out };
b.trim();
(b, rem as u64)
}
#[must_use]
pub fn add(&self, o: &BigUint) -> BigUint {
let n = self.limbs.len().max(o.limbs.len());
let mut out = Vec::with_capacity(n + 1);
let mut carry = 0u128;
for i in 0..n {
let v = u128::from(*self.limbs.get(i).unwrap_or(&0)) + u128::from(*o.limbs.get(i).unwrap_or(&0)) + carry;
out.push(v as u64);
carry = v >> 64;
}
if carry > 0 {
out.push(carry as u64);
}
let mut b = BigUint { limbs: out };
b.trim();
b
}
#[must_use]
pub fn sub(&self, o: &BigUint) -> BigUint {
assert!(*self >= *o, "negative difference");
let mut out = Vec::with_capacity(self.limbs.len());
let mut borrow = 0u64;
for (i, &l) in self.limbs.iter().enumerate() {
let r = *o.limbs.get(i).unwrap_or(&0);
let (d1, b1) = l.overflowing_sub(r);
let (d2, b2) = d1.overflowing_sub(borrow);
out.push(d2);
borrow = u64::from(b1 || b2);
}
let mut b = BigUint { limbs: out };
b.trim();
b
}
pub fn from_bits(bits: &[u8]) -> BigUint {
let mut limbs = vec![0u64; bits.len().div_ceil(64)];
for (i, &b) in bits.iter().rev().enumerate() {
if b != 0 {
limbs[i / 64] |= 1 << (i % 64);
}
}
let mut v = BigUint { limbs };
v.trim();
v
}
pub fn to_bits(&self, n: usize) -> Vec<u8> {
(0..n).rev().map(|i| self.limbs.get(i / 64).map_or(0, |l| ((l >> (i % 64)) & 1) as u8)).collect()
}
}
impl PartialOrd for BigUint {
fn partial_cmp(&self, o: &BigUint) -> Option<Ordering> {
Some(self.cmp(o))
}
}
impl Ord for BigUint {
fn cmp(&self, o: &BigUint) -> Ordering {
self.limbs.len().cmp(&o.limbs.len()).then_with(|| self.limbs.iter().rev().cmp(o.limbs.iter().rev()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn arithmetic_agrees_with_u128() {
let vals: [u128; 5] = [0, 1, 0xFFFF_FFFF_FFFF_FFFF, 0x1_0000_0000_0000_0001, 12_345_678_901_234_567_890_123];
let big = |v: u128| BigUint::from_bits(&(0..128).rev().map(|i| ((v >> i) & 1) as u8).collect::<Vec<_>>());
let val = |b: &BigUint| b.to_bits(128).iter().fold(0u128, |a, &x| (a << 1) | u128::from(x));
for &a in &vals {
for &b in &vals {
assert_eq!(val(&big(a).add(&big(b))), a.wrapping_add(b));
if a >= b {
assert_eq!(val(&big(a).sub(&big(b))), a - b);
}
assert_eq!(big(a).cmp(&big(b)), a.cmp(&b));
}
assert_eq!(val(&big(a).mul_small(977)), a * 977);
let (q, r) = big(a).div_small(977);
assert_eq!((val(&q), u128::from(r)), (a / 977, a % 977));
}
}
}