diff --git a/src/boxed/uint.rs b/src/boxed/uint.rs index 34aac40da..85dabae26 100644 --- a/src/boxed/uint.rs +++ b/src/boxed/uint.rs @@ -2,11 +2,13 @@ mod add; mod cmp; +mod mul; mod sub; -use crate::{Limb, Word}; +use crate::{Limb, Uint, Word, Zero, U128, U64}; use alloc::{boxed::Box, vec, vec::Vec}; use core::fmt; +use subtle::Choice; #[cfg(feature = "zeroize")] use zeroize::Zeroize; @@ -40,6 +42,13 @@ impl BoxedUint { } } + /// Is this [`BoxedUint`] equal to zero? + pub fn is_zero(&self) -> Choice { + self.limbs + .iter() + .fold(Choice::from(1), |acc, limb| acc & limb.is_zero()) + } + /// 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 @@ -129,6 +138,11 @@ impl BoxedUint { self.limbs } + /// Get the number of limbs in this [`BoxedUint`]. + pub fn nlimbs(&self) -> usize { + self.limbs.len() + } + /// Get the precision of this [`BoxedUint`] in bits. pub fn bits(&self) -> usize { self.limbs.len() * Limb::BITS @@ -210,6 +224,54 @@ impl fmt::Display for BoxedUint { } } +impl From for BoxedUint { + fn from(n: u8) -> Self { + vec![Limb::from(n); 1].into() + } +} + +impl From for BoxedUint { + fn from(n: u16) -> Self { + vec![Limb::from(n); 1].into() + } +} + +impl From for BoxedUint { + fn from(n: u32) -> Self { + vec![Limb::from(n); 1].into() + } +} + +impl From for BoxedUint { + fn from(n: u64) -> Self { + U64::from(n).into() + } +} + +impl From for BoxedUint { + fn from(n: u128) -> Self { + U128::from(n).into() + } +} + +impl From> for BoxedUint { + fn from(limbs: Box<[Limb]>) -> BoxedUint { + Self { limbs } + } +} + +impl From> for BoxedUint { + fn from(limbs: Vec) -> BoxedUint { + limbs.into_boxed_slice().into() + } +} + +impl From> for BoxedUint { + fn from(uint: Uint) -> BoxedUint { + Vec::from(uint.to_limbs()).into() + } +} + impl fmt::LowerHex for BoxedUint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.limbs.is_empty() { diff --git a/src/boxed/uint/mul.rs b/src/boxed/uint/mul.rs new file mode 100644 index 000000000..3ae4d87f6 --- /dev/null +++ b/src/boxed/uint/mul.rs @@ -0,0 +1,61 @@ +//! [`BoxedUint`] multiplication operations. + +use crate::{BoxedUint, Limb}; + +impl BoxedUint { + /// Multiply `self` by `rhs`. + pub fn mul(&self, rhs: &Self) -> Self { + let mut ret = Self { + limbs: vec![Limb::ZERO; self.nlimbs() + rhs.nlimbs()].into(), + }; + + // Schoolbook multiplication. + // TODO(tarcieri): use Karatsuba for better performance? Share impl with `Uint::mul`? + for i in 0..self.nlimbs() { + let mut carry = Limb::ZERO; + + for j in 0..rhs.nlimbs() { + let k = i + j; + let (n, c) = ret.limbs[k].mac(self.limbs[i], rhs.limbs[j], carry); + ret.limbs[k] = n; + carry = c; + } + + ret.limbs[i + rhs.nlimbs()] = carry; + } + + ret + } +} + +#[cfg(test)] +mod tests { + use crate::BoxedUint; + + #[test] + fn mul_zero_and_one() { + assert!(bool::from( + BoxedUint::zero().mul(&BoxedUint::zero()).is_zero() + )); + assert!(bool::from( + BoxedUint::zero().mul(&BoxedUint::one()).is_zero() + )); + assert!(bool::from( + BoxedUint::one().mul(&BoxedUint::zero()).is_zero() + )); + assert_eq!(BoxedUint::one().mul(&BoxedUint::one()), BoxedUint::one()); + } + + #[test] + fn mul_primes() { + let primes: &[u32] = &[3, 5, 17, 257, 65537]; + + for &a_int in primes { + for &b_int in primes { + let actual = BoxedUint::from(a_int).mul(&BoxedUint::from(b_int)); + let expected = BoxedUint::from(a_int as u64 * b_int as u64); + assert_eq!(actual, expected); + } + } + } +} diff --git a/src/uint/mul.rs b/src/uint/mul.rs index fccdfbff9..1fc4724b1 100644 --- a/src/uint/mul.rs +++ b/src/uint/mul.rs @@ -1,4 +1,4 @@ -//! [`Uint`] addition operations. +//! [`Uint`] multiplication operations. use crate::{Checked, CheckedMul, Concat, ConcatMixed, Limb, Uint, WideWord, Word, Wrapping, Zero}; use core::ops::{Mul, MulAssign};