blob: 4771a69e4ed4c046f2e04f0215ffed0178900e2a [file]
//! 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<Limb>,
}
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<Self> {
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<Self> {
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<Word> {
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<Limb> {
self.limbs.clone()
}
/// Convert this [`BoxedUint`] into its inner limbs.
pub fn into_limbs(self) -> Vec<Limb> {
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<F>(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();
}
}