//! Heap-allocated big unsigned integers. mod add; mod cmp; use crate::{Limb, Word}; use alloc::{vec, vec::Vec}; use core::fmt; #[cfg(feature = "zeroize")] use zeroize::Zeroize; /// Fixed-precision heap-allocated big unsigned integer. /// /// Alternative to the stack-allocated [`Uint`][`crate::Uint`] but with a /// fixed precision chosen at runtime instead of compile time. /// /// Unlike many other heap-allocated big integer libraries, this type is not /// arbitrary precision and will wrap at its fixed-precision rather than /// automatically growing. #[derive(Clone, Default)] pub struct BoxedUint { /// Inner limb vector. Stored from least significant to most significant. limbs: Vec, } impl BoxedUint { /// Get the value `0`, represented as succinctly as possible. pub fn zero() -> Self { Self::default() } /// Get the value `1`, represented as succinctly as possible. pub fn one() -> Self { Self { limbs: vec![Limb::ONE; 1], } } /// Create a new [`BoxedUint`] with the given number of bits of precision. /// /// Returns `None` if the number of bits is not a multiple of the /// [`Limb`] size. pub fn new(bits_precision: usize) -> Option { if bits_precision == 0 || bits_precision % Limb::BITS != 0 { return None; } let nlimbs = bits_precision / Limb::BITS; Some(Self { limbs: vec![Limb::ZERO; nlimbs], }) } /// Get the maximum value for a given number of bits of precision. /// /// Returns `None` if the number of bits is not a multiple of the /// [`Limb`] size. pub fn max(bits_precision: usize) -> Option { let mut ret = Self::new(bits_precision)?; for limb in &mut ret.limbs { *limb = Limb::MAX; } Some(ret) } /// Create a [`BoxedUint`] from an array of [`Word`]s (i.e. word-sized unsigned /// integers). #[inline] pub fn from_words(words: &[Word]) -> Self { Self { limbs: words.iter().copied().map(Into::into).collect(), } } /// Create an array of [`Word`]s (i.e. word-sized unsigned integers) from /// a [`BoxedUint`]. #[inline] pub fn to_words(&self) -> Vec { self.limbs.iter().copied().map(Into::into).collect() } /// Borrow the inner limbs as a slice of [`Word`]s. pub fn as_words(&self) -> &[Word] { // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word` #[allow(trivial_casts, unsafe_code)] unsafe { &*((self.limbs.as_slice() as *const _) as *const [Word]) } } /// Borrow the inner limbs as a mutable array of [`Word`]s. pub fn as_words_mut(&mut self) -> &mut [Word] { // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word` #[allow(trivial_casts, unsafe_code)] unsafe { &mut *((self.limbs.as_mut_slice() as *mut _) as *mut [Word]) } } /// Borrow the limbs of this [`BoxedUint`]. pub fn as_limbs(&self) -> &[Limb] { self.limbs.as_ref() } /// Borrow the limbs of this [`BoxedUint`] mutably. pub fn as_limbs_mut(&mut self) -> &mut [Limb] { self.limbs.as_mut() } /// Convert this [`BoxedUint`] into its inner limbs. pub fn to_limbs(&self) -> Vec { self.limbs.clone() } /// Convert this [`BoxedUint`] into its inner limbs. pub fn into_limbs(self) -> Vec { self.limbs } /// Get the precision of this [`BoxedUint`] in bits. pub fn bits(&self) -> usize { self.limbs.len() * Limb::BITS } /// Sort two [`BoxedUint`]s by precision, returning a tuple of the shorter /// followed by the longer, or the original order if their precision is /// equal. fn sort_by_precision<'a>(a: &'a Self, b: &'a Self) -> (&'a Self, &'a Self) { if a.limbs.len() <= b.limbs.len() { (a, b) } else { (b, a) } } /// Perform a carry chain-like operation over the limbs of the inputs, /// constructing a result from the returned limbs and carry. /// /// If one of the two values has fewer limbs than the other, passes /// [`Limb::ZERO`] as the value for that limb. fn chain(a: &Self, b: &Self, mut carry: Limb, f: F) -> (Self, Limb) where F: Fn(Limb, Limb, Limb) -> (Limb, Limb), { let (shorter, longer) = Self::sort_by_precision(a, b); let mut limbs = Vec::with_capacity(longer.limbs.len()); for i in 0..longer.limbs.len() { let &a = shorter.limbs.get(i).unwrap_or(&Limb::ZERO); let &b = longer.limbs.get(i).unwrap_or(&Limb::ZERO); let (limb, c) = f(a, b, carry); limbs.push(limb); carry = c; } (Self { limbs }, carry) } } impl AsRef<[Word]> for BoxedUint { fn as_ref(&self) -> &[Word] { self.as_words() } } impl AsMut<[Word]> for BoxedUint { fn as_mut(&mut self) -> &mut [Word] { self.as_words_mut() } } impl AsRef<[Limb]> for BoxedUint { fn as_ref(&self) -> &[Limb] { self.as_limbs() } } impl AsMut<[Limb]> for BoxedUint { fn as_mut(&mut self) -> &mut [Limb] { self.as_limbs_mut() } } impl fmt::Debug for BoxedUint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "BoxedUint(0x{self:X})") } } impl fmt::Display for BoxedUint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::UpperHex::fmt(self, f) } } impl fmt::LowerHex for BoxedUint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.limbs.is_empty() { return fmt::LowerHex::fmt(&Limb::ZERO, f); } for limb in self.limbs.iter().rev() { fmt::LowerHex::fmt(limb, f)?; } Ok(()) } } impl fmt::UpperHex for BoxedUint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.limbs.is_empty() { return fmt::LowerHex::fmt(&Limb::ZERO, f); } for limb in self.limbs.iter().rev() { fmt::UpperHex::fmt(limb, f)?; } Ok(()) } } #[cfg(feature = "zeroize")] impl Zeroize for BoxedUint { fn zeroize(&mut self) { self.limbs.zeroize(); } }