use alloc::vec;
use alloc::vec::Vec;
use core::cmp::Ordering;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct BigUint {
limbs: Vec<u32>,
}
impl BigUint {
pub(crate) fn zero() -> Self {
Self { limbs: Vec::new() }
}
pub(crate) fn from_u128(mut value: u128) -> Self {
let mut limbs = Vec::new();
while value != 0 {
limbs.push((value & 0xffff_ffff) as u32);
value >>= 32;
}
Self { limbs }
}
pub(crate) fn is_zero(&self) -> bool {
self.limbs.is_empty()
}
pub(crate) fn bit_length(&self) -> usize {
match self.limbs.last() {
None => 0,
Some(&top) => (self.limbs.len() - 1) * 32 + (32 - top.leading_zeros() as usize),
}
}
pub(crate) fn to_u128(&self) -> (u128, bool) {
let mut value: u128 = 0;
for (index, &limb) in self.limbs.iter().take(4).enumerate() {
value |= u128::from(limb) << (32 * index);
}
(value, self.limbs.len() > 4)
}
fn trim(&mut self) {
while self.limbs.last() == Some(&0) {
self.limbs.pop();
}
}
pub(crate) fn mul_add_small(&mut self, factor: u32, addend: u32) {
let mut carry = u64::from(addend);
for limb in &mut self.limbs {
let product = u64::from(*limb) * u64::from(factor) + carry;
*limb = (product & 0xffff_ffff) as u32;
carry = product >> 32;
}
while carry != 0 {
self.limbs.push((carry & 0xffff_ffff) as u32);
carry >>= 32;
}
self.trim();
}
pub(crate) fn div_rem_small(&mut self, divisor: u32) -> u32 {
debug_assert!(
divisor != 0,
"a zero divisor is the caller's bug, not a value"
);
let mut remainder: u64 = 0;
for limb in self.limbs.iter_mut().rev() {
let current = (remainder << 32) | u64::from(*limb);
*limb = (current / u64::from(divisor)) as u32;
remainder = current % u64::from(divisor);
}
self.trim();
remainder as u32
}
pub(crate) fn shl(&mut self, bits: usize) {
if self.is_zero() || bits == 0 {
return;
}
let whole = bits / 32;
let part = (bits % 32) as u32;
if part != 0 {
let mut carry = 0u32;
for limb in &mut self.limbs {
let shifted = (u64::from(*limb) << part) | u64::from(carry);
*limb = (shifted & 0xffff_ffff) as u32;
carry = (shifted >> 32) as u32;
}
if carry != 0 {
self.limbs.push(carry);
}
}
if whole != 0 {
let mut limbs = vec![0u32; whole];
limbs.extend_from_slice(&self.limbs);
self.limbs = limbs;
}
}
pub(crate) fn shr(&mut self, bits: usize) -> bool {
if self.is_zero() || bits == 0 {
return false;
}
let whole = bits / 32;
let part = (bits % 32) as u32;
let mut lost = false;
if whole >= self.limbs.len() {
lost = !self.is_zero();
self.limbs.clear();
return lost;
}
for limb in self.limbs.iter().take(whole) {
if *limb != 0 {
lost = true;
}
}
self.limbs.drain(..whole);
if part != 0 {
let mask = (1u32 << part) - 1;
if self.limbs.first().is_some_and(|first| first & mask != 0) {
lost = true;
}
let mut carry = 0u32;
for limb in self.limbs.iter_mut().rev() {
let value = *limb;
*limb = (value >> part) | (carry << (32 - part));
carry = value & mask;
}
}
self.trim();
lost
}
pub(crate) fn mul_pow5(&mut self, power: usize) {
const CHUNK: u32 = 1_220_703_125;
const CHUNK_POWER: usize = 13;
let mut left = power;
while left >= CHUNK_POWER {
self.mul_add_small(CHUNK, 0);
left -= CHUNK_POWER;
}
if left != 0 {
let mut factor = 1u32;
for _ in 0..left {
factor *= 5;
}
self.mul_add_small(factor, 0);
}
}
pub(crate) fn mul_pow10(&mut self, power: usize) {
self.mul_pow5(power);
self.shl(power);
}
pub(crate) fn to_decimal_digits(&self) -> Vec<u8> {
if self.is_zero() {
return Vec::new();
}
const CHUNK: u32 = 1_000_000_000;
let mut work = self.clone();
let mut groups = Vec::new();
while !work.is_zero() {
groups.push(work.div_rem_small(CHUNK));
}
let mut digits = Vec::new();
if let Some(&top) = groups.last() {
let mut buffer = [0u8; 9];
let mut length = 0;
let mut value = top;
while value != 0 {
buffer[length] = (value % 10) as u8;
value /= 10;
length += 1;
}
for index in (0..length).rev() {
digits.push(buffer[index]);
}
}
for &group in groups.iter().rev().skip(1) {
let mut value = group;
let mut buffer = [0u8; 9];
for slot in buffer.iter_mut().rev() {
*slot = (value % 10) as u8;
value /= 10;
}
digits.extend_from_slice(&buffer);
}
digits
}
}
impl Ord for BigUint {
fn cmp(&self, other: &Self) -> Ordering {
match self.limbs.len().cmp(&other.limbs.len()) {
Ordering::Equal => {}
other_order => return other_order,
}
for (left, right) in self.limbs.iter().rev().zip(other.limbs.iter().rev()) {
match left.cmp(right) {
Ordering::Equal => {}
other_order => return other_order,
}
}
Ordering::Equal
}
}
impl PartialOrd for BigUint {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}