From bfae1b0ebbbf3d0f7969d596271d308be396e827 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 10 May 2024 15:43:59 -0700 Subject: [PATCH] WIP inline single-digit values --- .github/workflows/ci.yaml | 2 +- Cargo.toml | 3 +- ci/test_full.sh | 4 +- src/big_digit.rs | 55 +++++++ src/big_digit/inline.rs | 282 ++++++++++++++++++++++++++++++++++ src/big_digit/vec.rs | 154 +++++++++++++++++++ src/bigint.rs | 47 +++++- src/bigint/bits.rs | 17 +- src/biguint.rs | 132 +++++++++------- src/biguint/addition.rs | 38 ++--- src/biguint/arbitrary.rs | 2 +- src/biguint/convert.rs | 65 +++++--- src/biguint/division.rs | 137 +++++++---------- src/biguint/monty.rs | 44 +++--- src/biguint/multiplication.rs | 23 ++- src/biguint/shift.rs | 32 ++-- src/biguint/subtraction.rs | 66 ++++---- src/lib.rs | 56 +------ 18 files changed, 839 insertions(+), 320 deletions(-) create mode 100644 src/big_digit.rs create mode 100644 src/big_digit/inline.rs create mode 100644 src/big_digit/vec.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ede5aaed..ddfc89dd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -69,7 +69,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - - run: cargo doc --features std,serde,rand,quickcheck,arbitrary + - run: cargo doc --features std,inline,serde,rand,quickcheck,arbitrary env: RUSTDOCFLAGS: --cfg docsrs diff --git a/Cargo.toml b/Cargo.toml index ff41e8a9..2134eb4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ rust-version = "1.60" [features] default = ["std"] +inline = [] std = ["num-integer/std", "num-traits/std"] arbitrary = ["dep:arbitrary"] quickcheck = ["dep:quickcheck"] @@ -23,7 +24,7 @@ rand = ["dep:rand"] serde = ["dep:serde"] [package.metadata.docs.rs] -features = ["std", "serde", "rand", "quickcheck", "arbitrary"] +features = ["std", "inline", "serde", "rand", "quickcheck", "arbitrary"] rustdoc-args = ["--cfg", "docsrs"] [[bench]] diff --git a/ci/test_full.sh b/ci/test_full.sh index 3924be90..ab86c9cd 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -27,8 +27,8 @@ if ! check_version $MSRV ; then exit 1 fi -STD_FEATURES=(arbitrary quickcheck rand serde) -NO_STD_FEATURES=(serde rand) +STD_FEATURES=(inline arbitrary quickcheck rand serde) +NO_STD_FEATURES=(inline serde rand) echo "Testing supported features: ${STD_FEATURES[*]}" if [ -n "${NO_STD_FEATURES[*]}" ]; then echo " no_std supported features: ${NO_STD_FEATURES[*]}" diff --git a/src/big_digit.rs b/src/big_digit.rs new file mode 100644 index 00000000..00f686a2 --- /dev/null +++ b/src/big_digit.rs @@ -0,0 +1,55 @@ +mod inline; +mod vec; + +#[cfg(feature = "inline")] +pub(crate) use inline::BigDigits; + +#[cfg(not(feature = "inline"))] +pub(crate) use vec::BigDigits; + +// A [`BigDigit`] is a [`BigUint`]'s composing element. +cfg_digit!( + pub(crate) type BigDigit = u32; + pub(crate) type BigDigit = u64; +); + +// A [`DoubleBigDigit`] is the internal type used to do the computations. Its +// size is the double of the size of [`BigDigit`]. +cfg_digit!( + pub(crate) type DoubleBigDigit = u64; + pub(crate) type DoubleBigDigit = u128; +); + +// A [`SignedDoubleBigDigit`] is the signed version of [`DoubleBigDigit`]. +cfg_digit!( + pub(crate) type SignedDoubleBigDigit = i64; + pub(crate) type SignedDoubleBigDigit = i128; +); + +pub(crate) const BITS: u8 = BigDigit::BITS as u8; +pub(crate) const HALF_BITS: u8 = BITS / 2; +pub(crate) const HALF: BigDigit = (1 << HALF_BITS) - 1; + +pub(crate) const MAX: BigDigit = BigDigit::MAX; +const LO_MASK: DoubleBigDigit = MAX as DoubleBigDigit; + +#[inline] +fn get_hi(n: DoubleBigDigit) -> BigDigit { + (n >> BITS) as BigDigit +} +#[inline] +fn get_lo(n: DoubleBigDigit) -> BigDigit { + (n & LO_MASK) as BigDigit +} + +/// Split one [`DoubleBigDigit`] into two [`BigDigit`]s. +#[inline] +pub(crate) fn from_doublebigdigit(n: DoubleBigDigit) -> (BigDigit, BigDigit) { + (get_hi(n), get_lo(n)) +} + +/// Join two [`BigDigit`]s into one [`DoubleBigDigit`]. +#[inline] +pub(crate) fn to_doublebigdigit(hi: BigDigit, lo: BigDigit) -> DoubleBigDigit { + DoubleBigDigit::from(lo) | (DoubleBigDigit::from(hi) << BITS) +} diff --git a/src/big_digit/inline.rs b/src/big_digit/inline.rs new file mode 100644 index 00000000..537bdbac --- /dev/null +++ b/src/big_digit/inline.rs @@ -0,0 +1,282 @@ +#![cfg(feature = "inline")] + +use super::BigDigit; +use alloc::vec::Vec; + +// #[derive(Debug)] +pub(crate) enum BigDigits { + Inline(Option), + Heap(Vec), +} + +impl BigDigits { + pub(crate) const ZERO: Self = BigDigits::Inline(None); + pub(crate) const ONE: Self = BigDigits::Inline(Some(1)); + + #[inline] + pub(crate) const fn from_digit(x: BigDigit) -> Self { + if x == 0 { + BigDigits::ZERO + } else { + BigDigits::Inline(Some(x)) + } + } + + #[inline] + pub(crate) fn from_slice(slice: &[BigDigit]) -> Self { + match slice { + &[] => BigDigits::ZERO, + &[x] => BigDigits::Inline(Some(x)), + xs => BigDigits::Heap(xs.to_vec()), + } + } + + #[inline] + pub(crate) fn from_vec(xs: Vec) -> Self { + BigDigits::Heap(xs) + } + + #[inline] + pub(crate) fn clear(&mut self) { + match self { + BigDigits::Inline(x) => *x = None, + BigDigits::Heap(xs) => xs.clear(), + } + } + + #[inline] + pub(crate) fn push(&mut self, y: BigDigit) { + match &mut *self { + BigDigits::Inline(x @ None) => *x = Some(y), + BigDigits::Inline(Some(x)) => *self = BigDigits::Heap([*x, y].to_vec()), + BigDigits::Heap(xs) => xs.push(y), + } + } + + #[inline] + pub(crate) fn pop(&mut self) -> Option { + match self { + BigDigits::Inline(x) => x.take(), + BigDigits::Heap(xs) => xs.pop(), + } + } + + #[inline] + pub(crate) fn last(&self) -> Option<&BigDigit> { + match self { + BigDigits::Inline(x) => x.as_ref(), + BigDigits::Heap(xs) => xs.last(), + } + } + + #[inline] + pub(crate) fn len(&self) -> usize { + match self { + BigDigits::Inline(None) => 0, + BigDigits::Inline(Some(_)) => 1, + BigDigits::Heap(xs) => xs.len(), + } + } + + #[inline] + pub(crate) fn is_empty(&self) -> bool { + match self { + BigDigits::Inline(None) => true, + BigDigits::Inline(Some(_)) => false, + BigDigits::Heap(xs) => xs.is_empty(), + } + } + + #[inline] + pub(crate) fn capacity(&self) -> usize { + match self { + BigDigits::Inline(_) => 1, + BigDigits::Heap(xs) => xs.capacity(), + } + } + + #[inline] + pub(crate) fn shrink(&mut self) { + if let BigDigits::Heap(xs) = self { + if xs.len() < xs.capacity() / 2 { + match **xs { + [] => *self = BigDigits::ZERO, + [x] => *self = BigDigits::Inline(Some(x)), + _ => xs.shrink_to(xs.len() + 1), + } + } + } + } + + /// Returns `true` if the most-significant digit (if any) is nonzero. + #[inline] + pub(crate) fn is_normal(&self) -> bool { + match self { + BigDigits::Inline(Some(0)) => false, + BigDigits::Inline(_) => true, + BigDigits::Heap(xs) => !matches!(**xs, [.., 0]), + } + } + + /// Strips off trailing zero bigdigits - most algorithms require + /// the most significant digit in the number to be nonzero. + #[inline] + pub(crate) fn normalize(&mut self) { + match self { + BigDigits::Inline(x) => { + if let Some(0) = *x { + *x = None; + } + } + BigDigits::Heap(xs) => { + if let [.., 0] = **xs { + let len = xs.iter().rposition(|&d| d != 0).map_or(0, |i| i + 1); + xs.truncate(len); + } + if xs.len() < xs.capacity() / 2 { + match **xs { + [] => *self = BigDigits::ZERO, + [x] => *self = BigDigits::Inline(Some(x)), + _ => xs.shrink_to(xs.len() + 1), + } + } + } + } + } + + #[inline] + pub(crate) fn truncate(&mut self, len: usize) { + match self { + BigDigits::Inline(x) => { + if len == 0 { + *x = None; + } + } + BigDigits::Heap(xs) => xs.truncate(len), + } + } + + #[inline] + pub(crate) fn drain_front(&mut self, len: usize) { + match self { + BigDigits::Inline(x) => { + assert!(len <= 1); + if len == 1 { + *x = None; + } + } + BigDigits::Heap(xs) => { + xs.drain(..len); + } + } + } + + pub(crate) fn resize(&mut self, len: usize, value: BigDigit) { + match &mut *self { + BigDigits::Inline(x) => match len { + 0 => *x = None, + 1 => { + if x.is_none() { + *x = Some(value); + } + } + _ => { + let mut xs = Vec::with_capacity(len); + if let Some(x) = *x { + xs.push(x); + } + xs.resize(len, value); + *self = BigDigits::Heap(xs); + } + }, + BigDigits::Heap(xs) => xs.resize(len, value), + } + } + + pub(crate) fn extend_from_slice(&mut self, ys: &[BigDigit]) { + match &mut *self { + BigDigits::Inline(None) => *self = BigDigits::from_slice(ys), + BigDigits::Inline(Some(x)) => { + let len = ys.len() + 1; + if len > 1 { + let mut xs = Vec::with_capacity(len); + xs.push(*x); + xs.extend_from_slice(ys); + *self = BigDigits::Heap(xs); + } + } + BigDigits::Heap(xs) => xs.extend_from_slice(ys), + } + } + + pub(crate) fn extend(&mut self, mut iter: I) + where + I: ExactSizeIterator, + { + match &mut *self { + BigDigits::Inline(x) => { + if x.is_none() { + match iter.next() { + Some(y) => *x = Some(y), + None => return, + } + } + if let Some(y) = iter.next() { + let len = iter.len().saturating_add(2); + let mut xs = Vec::with_capacity(len); + xs.push(x.unwrap()); + xs.push(y); + xs.extend(iter); + *self = BigDigits::Heap(xs); + } + } + BigDigits::Heap(xs) => xs.extend(iter), + } + } +} + +impl Clone for BigDigits { + #[inline] + fn clone(&self) -> Self { + match self { + BigDigits::Inline(x) => BigDigits::Inline(*x), + BigDigits::Heap(xs) => BigDigits::from_slice(xs), + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + match &mut *self { + BigDigits::Heap(xs) if xs.capacity() != 0 => { + xs.clear(); + xs.extend_from_slice(source); + } + #[allow(clippy::assigning_clones)] + _ => *self = source.clone(), + } + } +} + +impl core::ops::Deref for BigDigits { + type Target = [BigDigit]; + + #[inline] + fn deref(&self) -> &Self::Target { + match self { + BigDigits::Inline(None) => &[], + BigDigits::Inline(Some(x)) => core::slice::from_ref(x), + BigDigits::Heap(xs) => xs, + } + } +} + +impl core::ops::DerefMut for BigDigits { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + BigDigits::Inline(None) => &mut [], + BigDigits::Inline(Some(x)) => core::slice::from_mut(x), + BigDigits::Heap(xs) => xs, + } + } +} diff --git a/src/big_digit/vec.rs b/src/big_digit/vec.rs new file mode 100644 index 00000000..3a84271a --- /dev/null +++ b/src/big_digit/vec.rs @@ -0,0 +1,154 @@ +#![cfg(not(feature = "inline"))] + +use super::BigDigit; +use alloc::vec::Vec; + +// #[derive(Debug)] +#[repr(transparent)] +pub(crate) struct BigDigits { + vec: Vec, +} + +impl BigDigits { + pub(crate) const ZERO: Self = Self { vec: Vec::new() }; + + #[inline] + pub(crate) fn from_digit(x: BigDigit) -> Self { + if x == 0 { + Self::ZERO + } else { + Self { vec: vec![x] } + } + } + + #[inline] + pub(crate) fn from_slice(slice: &[BigDigit]) -> Self { + Self { + vec: slice.to_vec(), + } + } + + #[inline] + pub(crate) fn from_vec(vec: Vec) -> Self { + Self { vec } + } + + #[inline] + pub(crate) fn clear(&mut self) { + self.vec.clear(); + } + + #[inline] + pub(crate) fn push(&mut self, y: BigDigit) { + self.vec.push(y); + } + + #[inline] + pub(crate) fn pop(&mut self) -> Option { + self.vec.pop() + } + + #[inline] + pub(crate) fn last(&self) -> Option<&BigDigit> { + self.vec.last() + } + + #[inline] + pub(crate) fn len(&self) -> usize { + self.vec.len() + } + + #[inline] + pub(crate) fn is_empty(&self) -> bool { + self.vec.is_empty() + } + + #[inline] + pub(crate) fn capacity(&self) -> usize { + self.vec.capacity() + } + + #[inline] + pub(crate) fn shrink(&mut self) { + let xs = &mut self.vec; + if xs.len() < xs.capacity() / 2 { + xs.shrink_to(xs.len() + 1); + } + } + + /// Returns `true` if the most-significant digit (if any) is nonzero. + #[inline] + pub(crate) fn is_normal(&self) -> bool { + !matches!(*self.vec, [.., 0]) + } + + /// Strips off trailing zero bigdigits - most algorithms require + /// the most significant digit in the number to be nonzero. + #[inline] + pub(crate) fn normalize(&mut self) { + let xs = &mut self.vec; + if let [.., 0] = **xs { + let len = xs.iter().rposition(|&d| d != 0).map_or(0, |i| i + 1); + xs.truncate(len); + } + self.shrink() + } + + #[inline] + pub(crate) fn truncate(&mut self, len: usize) { + self.vec.truncate(len); + } + + #[inline] + pub(crate) fn drain_front(&mut self, len: usize) { + self.vec.drain(..len); + } + + #[inline] + pub(crate) fn resize(&mut self, len: usize, value: BigDigit) { + self.vec.resize(len, value); + } + + #[inline] + pub(crate) fn extend_from_slice(&mut self, ys: &[BigDigit]) { + self.vec.extend_from_slice(ys); + } + + #[inline] + pub(crate) fn extend(&mut self, iter: I) + where + I: ExactSizeIterator, + { + self.vec.extend(iter); + } +} + +impl Clone for BigDigits { + #[inline] + fn clone(&self) -> Self { + Self { + vec: self.vec.clone(), + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + self.vec.clone_from(&source.vec); + } +} + +impl core::ops::Deref for BigDigits { + type Target = [BigDigit]; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.vec + } +} + +impl core::ops::DerefMut for BigDigits { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.vec + } +} diff --git a/src/bigint.rs b/src/bigint.rs index b4f84b9e..7018190e 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -15,7 +15,7 @@ use num_traits::{ConstZero, Num, One, Pow, Signed, Zero}; use self::Sign::{Minus, NoSign, Plus}; -use crate::big_digit::BigDigit; +use crate::big_digit::{BigDigit, BigDigits}; use crate::biguint::to_str_radix_reversed; use crate::biguint::{BigUint, IntDigits, U32Digits, U64Digits}; @@ -247,6 +247,12 @@ impl One for BigInt { } } +#[cfg(feature = "inline")] +impl num_traits::ConstOne for BigInt { + // forward to the inherent const + const ONE: Self = Self::ONE; +} + impl Signed for BigInt { #[inline] fn abs(&self) -> BigInt { @@ -534,7 +540,7 @@ impl IntDigits for BigInt { self.data.digits() } #[inline] - fn digits_mut(&mut self) -> &mut Vec { + fn digits_mut(&mut self) -> &mut BigDigits { self.data.digits_mut() } #[inline] @@ -569,6 +575,22 @@ impl BigInt { data: BigUint::ZERO, }; + /// A constant `BigInt` with value 1, useful for static initialization. + #[cfg(feature = "inline")] + #[cfg_attr(docsrs, doc(cfg(feature = "inline")))] + pub const ONE: Self = BigInt { + sign: Plus, + data: BigUint::ONE, + }; + + /// A constant `BigInt` with value -1, useful for static initialization. + #[cfg(feature = "inline")] + #[cfg_attr(docsrs, doc(cfg(feature = "inline")))] + pub const NEG_ONE: Self = BigInt { + sign: Minus, + data: BigUint::ONE, + }; + /// Creates and initializes a [`BigInt`]. /// /// The base 232 digits are ordered least significant digit first. @@ -577,6 +599,24 @@ impl BigInt { BigInt::from_biguint(sign, BigUint::new(digits)) } + /// Creates a constant [`BigInt`] from a primitive [`i32`] value. + /// + /// Non-`const` callers should use [`From`] instead. + #[inline] + #[cfg(feature = "inline")] + #[cfg_attr(docsrs, doc(cfg(feature = "inline")))] + pub const fn new_const(n: i32) -> Self { + let (sign, u) = match n { + 1.. => (Plus, n as u32), + 0 => (NoSign, 0), + _ => (Minus, n.wrapping_neg() as u32), + }; + BigInt { + sign, + data: BigUint::new_const(u), + } + } + /// Creates and initializes a [`BigInt`]. /// /// The base 232 digits are ordered least significant digit first. @@ -940,11 +980,10 @@ impl BigInt { /// /// ``` /// use num_bigint::{BigInt, BigUint}; - /// use num_traits::Zero; /// /// assert_eq!(BigInt::from(1234).magnitude(), &BigUint::from(1234u32)); /// assert_eq!(BigInt::from(-4321).magnitude(), &BigUint::from(4321u32)); - /// assert!(BigInt::ZERO.magnitude().is_zero()); + /// assert_eq!(BigInt::ZERO.magnitude(), &BigUint::ZERO); /// ``` #[inline] pub fn magnitude(&self) -> &BigUint { diff --git a/src/bigint/bits.rs b/src/bigint/bits.rs index ac4fe1de..0260f1e1 100644 --- a/src/bigint/bits.rs +++ b/src/bigint/bits.rs @@ -1,10 +1,9 @@ use super::BigInt; use super::Sign::{Minus, NoSign, Plus}; -use crate::big_digit::{self, BigDigit, DoubleBigDigit}; +use crate::big_digit::{self, BigDigit, BigDigits, DoubleBigDigit}; use crate::biguint::IntDigits; -use alloc::vec::Vec; use core::cmp::Ordering::{Equal, Greater, Less}; use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign}; use num_traits::{ToPrimitive, Zero}; @@ -48,7 +47,7 @@ fn bitand_pos_neg(a: &mut [BigDigit], b: &[BigDigit]) { // - 1 & +ff = ...f ff & ...0 ff = ...0 ff = +ff // -ff & + 1 = ...f 01 & ...0 01 = ...0 01 = + 1 // answer is pos, has length of b -fn bitand_neg_pos(a: &mut Vec, b: &[BigDigit]) { +fn bitand_neg_pos(a: &mut BigDigits, b: &[BigDigit]) { let mut carry_a = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { let twos_a = negate_carry(*ai, &mut carry_a); @@ -69,7 +68,7 @@ fn bitand_neg_pos(a: &mut Vec, b: &[BigDigit]) { // -ff & - 1 = ...f 01 & ...f ff = ...f 01 = - ff // -ff & -fe = ...f 01 & ...f 02 = ...f 00 = -100 // answer is neg, has length of longest with a possible carry -fn bitand_neg_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitand_neg_neg(a: &mut BigDigits, b: &[BigDigit]) { let mut carry_a = 1; let mut carry_b = 1; let mut carry_and = 1; @@ -173,7 +172,7 @@ impl BitAndAssign<&BigInt> for BigInt { // + 1 | -ff = ...0 01 | ...f 01 = ...f 01 = -ff // +ff | - 1 = ...0 ff | ...f ff = ...f ff = - 1 // answer is neg, has length of b -fn bitor_pos_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitor_pos_neg(a: &mut BigDigits, b: &[BigDigit]) { let mut carry_b = 1; let mut carry_or = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { @@ -224,7 +223,7 @@ fn bitor_neg_pos(a: &mut [BigDigit], b: &[BigDigit]) { // - 1 | -ff = ...f ff | ...f 01 = ...f ff = -1 // -ff | - 1 = ...f 01 | ...f ff = ...f ff = -1 // answer is neg, has length of shortest -fn bitor_neg_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitor_neg_neg(a: &mut BigDigits, b: &[BigDigit]) { let mut carry_a = 1; let mut carry_b = 1; let mut carry_or = 1; @@ -308,7 +307,7 @@ impl BitOrAssign<&BigInt> for BigInt { // + 1 ^ -ff = ...0 01 ^ ...f 01 = ...f 00 = -100 // +ff ^ - 1 = ...0 ff ^ ...f ff = ...f 00 = -100 // answer is neg, has length of longest with a possible carry -fn bitxor_pos_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitxor_pos_neg(a: &mut BigDigits, b: &[BigDigit]) { let mut carry_b = 1; let mut carry_xor = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { @@ -341,7 +340,7 @@ fn bitxor_pos_neg(a: &mut Vec, b: &[BigDigit]) { // - 1 ^ +ff = ...f ff ^ ...0 ff = ...f 00 = -100 // -ff ^ + 1 = ...f 01 ^ ...0 01 = ...f 00 = -100 // answer is neg, has length of longest with a possible carry -fn bitxor_neg_pos(a: &mut Vec, b: &[BigDigit]) { +fn bitxor_neg_pos(a: &mut BigDigits, b: &[BigDigit]) { let mut carry_a = 1; let mut carry_xor = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { @@ -374,7 +373,7 @@ fn bitxor_neg_pos(a: &mut Vec, b: &[BigDigit]) { // - 1 ^ -ff = ...f ff ^ ...f 01 = ...0 fe = +fe // -ff & - 1 = ...f 01 ^ ...f ff = ...0 fe = +fe // answer is pos, has length of longest -fn bitxor_neg_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitxor_neg_neg(a: &mut BigDigits, b: &[BigDigit]) { let mut carry_a = 1; let mut carry_b = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { diff --git a/src/biguint.rs b/src/biguint.rs index 196fa323..51bc55ca 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -1,4 +1,4 @@ -use crate::big_digit::{self, BigDigit}; +use crate::big_digit::{self, BigDigit, BigDigits}; use alloc::string::String; use alloc::vec::Vec; @@ -32,7 +32,7 @@ pub use self::iter::{U32Digits, U64Digits}; /// A big unsigned integer type. pub struct BigUint { - data: Vec, + data: BigDigits, } // Note: derived `Clone` doesn't specialize `clone_from`, @@ -54,7 +54,7 @@ impl Clone for BigUint { impl hash::Hash for BigUint { #[inline] fn hash(&self, state: &mut H) { - debug_assert!(self.data.last() != Some(&0)); + debug_assert!(self.data.is_normal()); self.data.hash(state); } } @@ -62,9 +62,9 @@ impl hash::Hash for BigUint { impl PartialEq for BigUint { #[inline] fn eq(&self, other: &BigUint) -> bool { - debug_assert!(self.data.last() != Some(&0)); - debug_assert!(other.data.last() != Some(&0)); - self.data == other.data + debug_assert!(self.data.is_normal()); + debug_assert!(other.data.is_normal()); + *self.data == *other.data } } impl Eq for BigUint {} @@ -79,15 +79,14 @@ impl PartialOrd for BigUint { impl Ord for BigUint { #[inline] fn cmp(&self, other: &BigUint) -> Ordering { - cmp_slice(&self.data[..], &other.data[..]) + debug_assert!(self.data.is_normal()); + debug_assert!(other.data.is_normal()); + cmp_slice(&self.data, &other.data) } } #[inline] fn cmp_slice(a: &[BigDigit], b: &[BigDigit]) -> Ordering { - debug_assert!(a.last() != Some(&0)); - debug_assert!(b.last() != Some(&0)); - match Ord::cmp(&a.len(), &b.len()) { Ordering::Equal => Iterator::cmp(a.iter().rev(), b.iter().rev()), other => other, @@ -158,13 +157,15 @@ impl Zero for BigUint { impl ConstZero for BigUint { // forward to the inherent const - const ZERO: Self = Self::ZERO; // BigUint { data: Vec::new() }; + const ZERO: Self = Self::ZERO; } impl One for BigUint { #[inline] fn one() -> BigUint { - BigUint { data: vec![1] } + BigUint { + data: BigDigits::from_digit(1), + } } #[inline] @@ -175,10 +176,16 @@ impl One for BigUint { #[inline] fn is_one(&self) -> bool { - self.data[..] == [1] + *self.data == [1] } } +#[cfg(feature = "inline")] +impl num_traits::ConstOne for BigUint { + // forward to the inherent const + const ONE: Self = Self::ONE; +} + impl Unsigned for BigUint {} impl Integer for BigUint { @@ -524,12 +531,25 @@ pub trait ToBigUint { /// The digits are in little-endian base matching `BigDigit`. #[inline] pub(crate) fn biguint_from_vec(digits: Vec) -> BigUint { - BigUint { data: digits }.normalized() + let mut n = BigUint { + data: BigDigits::from_vec(digits), + }; + n.normalize(); + n } impl BigUint { /// A constant `BigUint` with value 0, useful for static initialization. - pub const ZERO: Self = BigUint { data: Vec::new() }; + pub const ZERO: Self = BigUint { + data: BigDigits::ZERO, + }; + + /// A constant `BigUint` with value 1, useful for static initialization. + #[cfg(feature = "inline")] + #[cfg_attr(docsrs, doc(cfg(feature = "inline")))] + pub const ONE: Self = BigUint { + data: BigDigits::ONE, + }; /// Creates and initializes a [`BigUint`]. /// @@ -540,7 +560,7 @@ impl BigUint { cfg_digit_expr!( { - big.data = digits; + big.data = BigDigits::from_vec(digits); big.normalize(); }, big.assign_from_slice(&digits) @@ -549,6 +569,18 @@ impl BigUint { big } + /// Creates a constant [`BigUint`] from a primitive [`u32`] value. + /// + /// Non-`const` callers should use [`From`] instead. + #[inline] + #[cfg(feature = "inline")] + #[cfg_attr(docsrs, doc(cfg(feature = "inline")))] + pub const fn new_const(n: u32) -> Self { + BigUint { + data: BigDigits::from_digit(n as BigDigit), + } + } + /// Creates and initializes a [`BigUint`]. /// /// The base 232 digits are ordered least significant digit first. @@ -765,7 +797,7 @@ impl BigUint { /// ``` #[inline] pub fn iter_u32_digits(&self) -> U32Digits<'_> { - U32Digits::new(self.data.as_slice()) + U32Digits::new(&self.data) } /// Returns an iterator of `u64` digits representation of the [`BigUint`] ordered least @@ -784,7 +816,7 @@ impl BigUint { /// ``` #[inline] pub fn iter_u64_digits(&self) -> U64Digits<'_> { - U64Digits::new(self.data.as_slice()) + U64Digits::new(&self.data) } /// Returns the integer formatted as a string in the given radix. @@ -848,33 +880,15 @@ impl BigUint { /// Determines the fewest bits necessary to express the [`BigUint`]. #[inline] pub fn bits(&self) -> u64 { - if self.is_zero() { - return 0; - } - let zeros: u64 = self.data.last().unwrap().leading_zeros().into(); - self.data.len() as u64 * u64::from(big_digit::BITS) - zeros - } - - /// Strips off trailing zero bigdigits - comparisons require the last element in the vector to - /// be nonzero. - #[inline] - fn normalize(&mut self) { - if let Some(&0) = self.data.last() { - let len = self.data.iter().rposition(|&d| d != 0).map_or(0, |i| i + 1); - self.data.truncate(len); - } - if self.data.len() < self.data.capacity() / 4 { - self.data.shrink_to_fit(); + match self.data.last() { + Some(x) => { + let zeros: u64 = x.leading_zeros().into(); + self.data.len() as u64 * u64::from(big_digit::BITS) - zeros + } + None => 0, } } - /// Returns a normalized [`BigUint`]. - #[inline] - fn normalized(mut self) -> BigUint { - self.normalize(); - self - } - /// Returns `self ^ exponent`. pub fn pow(&self, exponent: u32) -> Self { Pow::pow(self, exponent) @@ -988,18 +1002,20 @@ impl BigUint { /// Returns the number of least-significant bits that are zero, /// or `None` if the entire number is zero. pub fn trailing_zeros(&self) -> Option { - let i = self.data.iter().position(|&digit| digit != 0)?; - let zeros: u64 = self.data[i].trailing_zeros().into(); + let data = &*self.data; + let i = data.iter().position(|&digit| digit != 0)?; + let zeros: u64 = data[i].trailing_zeros().into(); Some(i as u64 * u64::from(big_digit::BITS) + zeros) } /// Returns the number of least-significant bits that are ones. pub fn trailing_ones(&self) -> u64 { - if let Some(i) = self.data.iter().position(|&digit| !digit != 0) { - let ones: u64 = self.data[i].trailing_ones().into(); + let data = &*self.data; + if let Some(i) = data.iter().position(|&digit| !digit != 0) { + let ones: u64 = data[i].trailing_ones().into(); i as u64 * u64::from(big_digit::BITS) + ones } else { - self.data.len() as u64 * u64::from(big_digit::BITS) + data.len() as u64 * u64::from(big_digit::BITS) } } @@ -1036,10 +1052,12 @@ impl BigUint { self.data.resize(new_len, 0); } self.data[digit_index] |= bit_mask; - } else if digit_index < self.data.len() { - self.data[digit_index] &= !bit_mask; - // the top bit may have been cleared, so normalize - self.normalize(); + } else if let Some(digit) = self.data.get_mut(digit_index) { + *digit &= !bit_mask; + // if the top digit was cleared, we need to normalize + if *digit == 0 && digit_index + 1 == self.data.len() { + self.data.normalize(); + } } } } @@ -1070,7 +1088,7 @@ impl num_traits::ToBytes for BigUint { pub(crate) trait IntDigits { fn digits(&self) -> &[BigDigit]; - fn digits_mut(&mut self) -> &mut Vec; + fn digits_mut(&mut self) -> &mut BigDigits; fn normalize(&mut self); fn capacity(&self) -> usize; fn len(&self) -> usize; @@ -1082,12 +1100,12 @@ impl IntDigits for BigUint { &self.data } #[inline] - fn digits_mut(&mut self) -> &mut Vec { + fn digits_mut(&mut self) -> &mut BigDigits { &mut self.data } #[inline] fn normalize(&mut self) { - self.normalize(); + self.data.normalize(); } #[inline] fn capacity(&self) -> usize { @@ -1135,7 +1153,7 @@ cfg_digit!( #[test] fn test_from_slice() { fn check(slice: &[u32], data: &[BigDigit]) { - assert_eq!(BigUint::from_slice(slice).data, data); + assert_eq!(*BigUint::from_slice(slice).data, *data); } check(&[1], &[1]); check(&[0, 0, 0], &[]); @@ -1149,8 +1167,8 @@ cfg_digit!( fn test_from_slice() { fn check(slice: &[u32], data: &[BigDigit]) { assert_eq!( - BigUint::from_slice(slice).data, - data, + *BigUint::from_slice(slice).data, + *data, "from {:?}, to {:?}", slice, data diff --git a/src/biguint/addition.rs b/src/biguint/addition.rs index a054e66d..074e3271 100644 --- a/src/biguint/addition.rs +++ b/src/biguint/addition.rs @@ -90,13 +90,13 @@ impl AddAssign<&BigUint> for BigUint { #[inline] fn add_assign(&mut self, other: &BigUint) { let self_len = self.data.len(); - let carry = if self_len < other.data.len() { - let lo_carry = __add2(&mut self.data[..], &other.data[..self_len]); - self.data.extend_from_slice(&other.data[self_len..]); - __add2(&mut self.data[self_len..], &[lo_carry]) - } else { - __add2(&mut self.data[..], &other.data[..]) - }; + let mut other = &*other.data; + if self_len < other.len() { + let (low, high) = other.split_at(self_len); + self.data.extend_from_slice(high); + other = low; + } + let carry = __add2(&mut self.data, other); if carry != 0 { self.data.push(carry); } @@ -124,12 +124,12 @@ impl AddAssign for BigUint { fn add_assign(&mut self, other: u32) { if other != 0 { if self.data.is_empty() { - self.data.push(0); - } - - let carry = __add2(&mut self.data, &[other as BigDigit]); - if carry != 0 { - self.data.push(carry); + self.data.push(other as BigDigit); + } else { + let carry = __add2(&mut self.data, &[other as BigDigit]); + if carry != 0 { + self.data.push(carry); + } } } } @@ -168,12 +168,12 @@ impl AddAssign for BigUint { fn add_assign(&mut self, other: u64) { if other != 0 { if self.data.is_empty() { - self.data.push(0); - } - - let carry = __add2(&mut self.data, &[other as BigDigit]); - if carry != 0 { - self.data.push(carry); + self.data.push(other as BigDigit); + } else { + let carry = __add2(&mut self.data, &[other as BigDigit]); + if carry != 0 { + self.data.push(carry); + } } } } diff --git a/src/biguint/arbitrary.rs b/src/biguint/arbitrary.rs index 9d5c5ccf..b956d81e 100644 --- a/src/biguint/arbitrary.rs +++ b/src/biguint/arbitrary.rs @@ -17,7 +17,7 @@ impl quickcheck::Arbitrary for BigUint { fn shrink(&self) -> Box> { // Use shrinker from Vec - Box::new(self.data.shrink().map(biguint_from_vec)) + Box::new(self.data.to_vec().shrink().map(biguint_from_vec)) } } diff --git a/src/biguint/convert.rs b/src/biguint/convert.rs index a81778c8..70c7f483 100644 --- a/src/biguint/convert.rs +++ b/src/biguint/convert.rs @@ -7,7 +7,7 @@ use super::addition::add2; use super::division::{div_rem_digit, FAST_DIV_WIDE}; use super::multiplication::mac_with_carry; -use crate::big_digit::{self, BigDigit}; +use crate::big_digit::{self, BigDigit, BigDigits}; use crate::ParseBigIntError; use crate::TryFromBigIntError; @@ -488,18 +488,44 @@ impl FromPrimitive for BigUint { } } -impl From for BigUint { +impl From for BigUint { #[inline] - fn from(mut n: u64) -> Self { - let mut ret: BigUint = Self::ZERO; + fn from(n: u8) -> Self { + BigUint::from(u32::from(n)) + } +} - while n != 0 { - ret.data.push(n as BigDigit); - // don't overflow if BITS is 64: - n = (n >> 1) >> (big_digit::BITS - 1); +impl From for BigUint { + #[inline] + fn from(n: u16) -> Self { + BigUint::from(u32::from(n)) + } +} + +impl From for BigUint { + #[inline] + fn from(n: u32) -> Self { + BigUint { + data: BigDigits::from_digit(n as BigDigit), } + } +} - ret +impl From for BigUint { + #[inline] + fn from(n: u64) -> Self { + cfg_digit_expr!( + return if n > big_digit::MAX as u64 { + BigUint::new(vec![n as BigDigit, (n >> big_digit::BITS) as BigDigit]) + } else { + BigUint { + data: BigDigits::from_digit(n as BigDigit), + } + }, + return BigUint { + data: BigDigits::from_digit(n), + } + ); } } @@ -517,22 +543,13 @@ impl From for BigUint { } } -macro_rules! impl_biguint_from_uint { - ($T:ty) => { - impl From<$T> for BigUint { - #[inline] - fn from(n: $T) -> Self { - BigUint::from(n as u64) - } - } - }; +impl From for BigUint { + #[inline] + fn from(n: usize) -> Self { + BigUint::from(n as crate::UsizePromotion) + } } -impl_biguint_from_uint!(u8); -impl_biguint_from_uint!(u16); -impl_biguint_from_uint!(u32); -impl_biguint_from_uint!(usize); - macro_rules! impl_biguint_try_from_int { ($T:ty, $from_ty:path) => { impl TryFrom<$T> for BigUint { @@ -639,7 +656,7 @@ fn to_inexact_bitwise_digits_le(u: &BigUint, bits: u8) -> Vec { let mut r = 0; let mut rbits = 0; - for c in &u.data { + for c in &*u.data { r |= *c << rbits; rbits += big_digit::BITS; diff --git a/src/biguint/division.rs b/src/biguint/division.rs index adf9db89..c6cb7398 100644 --- a/src/biguint/division.rs +++ b/src/biguint/division.rs @@ -1,9 +1,11 @@ use super::addition::__add2; +use super::shift::biguint_shl; use super::{cmp_slice, BigUint}; -use crate::big_digit::{self, BigDigit, DoubleBigDigit}; +use crate::big_digit::{self, BigDigit, BigDigits, DoubleBigDigit}; use crate::UsizePromotion; +use alloc::borrow::Cow; use core::cmp::Ordering::{Equal, Greater, Less}; use core::mem; use core::ops::{Div, DivAssign, Rem, RemAssign}; @@ -90,7 +92,8 @@ pub(super) fn div_rem_digit(mut a: BigUint, b: BigDigit) -> (BigUint, BigDigit) } } - (a.normalized(), rem) + a.data.normalize(); + (a, rem) } #[inline] @@ -145,54 +148,15 @@ fn sub_mul_digit_same_len(a: &mut [BigDigit], b: &[BigDigit], c: BigDigit) -> Bi big_digit::MAX - offset_carry } -fn div_rem(mut u: BigUint, mut d: BigUint) -> (BigUint, BigUint) { - if d.is_zero() { - panic!("attempt to divide by zero") - } - if u.is_zero() { - return (BigUint::ZERO, BigUint::ZERO); - } - - if d.data.len() == 1 { - if d.data == [1] { - return (u, BigUint::ZERO); - } - let (div, rem) = div_rem_digit(u, d.data[0]); - // reuse d - d.data.clear(); - d += rem; - return (div, d); - } - - // Required or the q_len calculation below can underflow: - match u.cmp(&d) { - Less => return (BigUint::ZERO, u), - Equal => { - u.set_one(); - return (u, BigUint::ZERO); - } - Greater => {} // Do nothing - } - - // This algorithm is from Knuth, TAOCP vol 2 section 4.3, algorithm D: - // - // First, normalize the arguments so the highest bit in the highest digit of the divisor is - // set: the main loop uses the highest digit of the divisor for generating guesses, so we - // want it to be the largest number we can efficiently divide by. - // - let shift = d.data.last().unwrap().leading_zeros() as usize; - - if shift == 0 { - // no need to clone d - div_rem_core(u, &d.data) - } else { - let (q, r) = div_rem_core(u << shift, &(d << shift).data); - // renormalize the remainder - (q, r >> shift) - } +fn div_rem(u: BigUint, d: BigUint) -> (BigUint, BigUint) { + div_rem_cow(Cow::Owned(u), Cow::Owned(d)) } pub(super) fn div_rem_ref(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { + div_rem_cow(Cow::Borrowed(u), Cow::Borrowed(d)) +} + +fn div_rem_cow(u: Cow<'_, BigUint>, d: Cow<'_, BigUint>) -> (BigUint, BigUint) { if d.is_zero() { panic!("attempt to divide by zero") } @@ -200,18 +164,18 @@ pub(super) fn div_rem_ref(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { return (BigUint::ZERO, BigUint::ZERO); } - if d.data.len() == 1 { - if d.data == [1] { - return (u.clone(), BigUint::ZERO); + if let [digit] = *d.data { + let u = u.into_owned(); + if digit == 1 { + return (u, BigUint::ZERO); } - - let (div, rem) = div_rem_digit(u.clone(), d.data[0]); + let (div, rem) = div_rem_digit(u, digit); return (div, rem.into()); } // Required or the q_len calculation below can underflow: - match u.cmp(d) { - Less => return (BigUint::ZERO, u.clone()), + match u.cmp(&d) { + Less => return (BigUint::ZERO, u.into_owned()), Equal => return (One::one(), BigUint::ZERO), Greater => {} // Do nothing } @@ -222,13 +186,15 @@ pub(super) fn div_rem_ref(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { // set: the main loop uses the highest digit of the divisor for generating guesses, so we // want it to be the largest number we can efficiently divide by. // - let shift = d.data.last().unwrap().leading_zeros() as usize; + let shift = d.data.last().unwrap().leading_zeros(); if shift == 0 { // no need to clone d - div_rem_core(u.clone(), &d.data) + div_rem_core(u.into_owned(), &d.data) } else { - let (q, r) = div_rem_core(u << shift, &(d << shift).data); + let u = biguint_shl(u, shift); + let d = biguint_shl(d, shift); + let (q, r) = div_rem_core(u, &d.data); // renormalize the remainder (q, r >> shift) } @@ -266,7 +232,7 @@ fn div_rem_core(mut a: BigUint, b: &[BigDigit]) -> (BigUint, BigUint) { let q_len = a.data.len() - b.len() + 1; let mut q = BigUint { - data: vec![0; q_len], + data: BigDigits::from_vec(vec![0; q_len]), }; for j in (0..q_len).rev() { @@ -321,12 +287,17 @@ fn div_rem_core(mut a: BigUint, b: &[BigDigit]) -> (BigUint, BigUint) { a0 = a.data.pop().unwrap(); } - a.data.push(a0); - a.normalize(); + if a0 != 0 { + a.data.push(a0); + a.data.shrink(); + } else { + a.data.normalize(); + } debug_assert_eq!(cmp_slice(&a.data, b), Less); - (q.normalized(), a) + q.data.normalize(); + (q, a) } forward_val_ref_binop!(impl Div for BigUint, div); @@ -386,9 +357,9 @@ impl Div for u32 { #[inline] fn div(self, other: BigUint) -> BigUint { - match other.data.len() { - 0 => panic!("attempt to divide by zero"), - 1 => From::from(self as BigDigit / other.data[0]), + match *other.data { + [] => panic!("attempt to divide by zero"), + [x] => BigUint::from(self as BigDigit / x), _ => BigUint::ZERO, } } @@ -418,19 +389,19 @@ impl Div for u64 { cfg_digit!( #[inline] fn div(self, other: BigUint) -> BigUint { - match other.data.len() { - 0 => panic!("attempt to divide by zero"), - 1 => From::from(self / u64::from(other.data[0])), - 2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])), + match *other.data { + [] => panic!("attempt to divide by zero"), + [x] => BigUint::from(self / u64::from(x)), + [x, y] => BigUint::from(self / big_digit::to_doublebigdigit(y, x)), _ => BigUint::ZERO, } } #[inline] fn div(self, other: BigUint) -> BigUint { - match other.data.len() { - 0 => panic!("attempt to divide by zero"), - 1 => From::from(self / other.data[0]), + match *other.data { + [] => panic!("attempt to divide by zero"), + [x] => BigUint::from(self / x), _ => BigUint::ZERO, } } @@ -461,26 +432,22 @@ impl Div for u128 { #[inline] fn div(self, other: BigUint) -> BigUint { use super::u32_to_u128; - match other.data.len() { - 0 => panic!("attempt to divide by zero"), - 1 => From::from(self / u128::from(other.data[0])), - 2 => From::from( - self / u128::from(big_digit::to_doublebigdigit(other.data[1], other.data[0])), - ), - 3 => From::from(self / u32_to_u128(0, other.data[2], other.data[1], other.data[0])), - 4 => From::from( - self / u32_to_u128(other.data[3], other.data[2], other.data[1], other.data[0]), - ), + match *other.data { + [] => panic!("attempt to divide by zero"), + [x] => BigUint::from(self / u128::from(x)), + [x, y] => BigUint::from(self / u128::from(big_digit::to_doublebigdigit(y, x))), + [x, y, z] => BigUint::from(self / u32_to_u128(0, z, y, x)), + [w, x, y, z] => BigUint::from(self / u32_to_u128(z, y, x, w)), _ => BigUint::ZERO, } } #[inline] fn div(self, other: BigUint) -> BigUint { - match other.data.len() { - 0 => panic!("attempt to divide by zero"), - 1 => From::from(self / other.data[0] as u128), - 2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])), + match *other.data { + [] => panic!("attempt to divide by zero"), + [x] => BigUint::from(self / u128::from(x)), + [x, y] => BigUint::from(self / big_digit::to_doublebigdigit(y, x)), _ => BigUint::ZERO, } } diff --git a/src/biguint/monty.rs b/src/biguint/monty.rs index eeb0791e..799b1669 100644 --- a/src/biguint/monty.rs +++ b/src/biguint/monty.rs @@ -3,7 +3,7 @@ use core::mem; use core::ops::Shl; use num_traits::One; -use crate::big_digit::{self, BigDigit, DoubleBigDigit, SignedDoubleBigDigit}; +use crate::big_digit::{self, BigDigit, BigDigits, DoubleBigDigit, SignedDoubleBigDigit}; use crate::biguint::BigUint; struct MontyReducer { @@ -56,17 +56,19 @@ fn montgomery(x: &BigUint, y: &BigUint, m: &BigUint, k: BigDigit, n: usize) -> B n ); - let mut z = BigUint::ZERO; - z.data.resize(n * 2, 0); + let (x, y, m) = (&*x.data, &*y.data, &*m.data); + + let mut z = vec![0; n * 2]; let mut c: BigDigit = 0; for i in 0..n { - let c2 = add_mul_vvw(&mut z.data[i..n + i], &x.data, y.data[i]); - let t = z.data[i].wrapping_mul(k); - let c3 = add_mul_vvw(&mut z.data[i..n + i], &m.data, t); + let z = &mut z[i..]; + let c2 = add_mul_vvw(&mut z[..n], x, y[i]); + let t = z[0].wrapping_mul(k); + let c3 = add_mul_vvw(&mut z[..n], m, t); let cx = c.wrapping_add(c2); let cy = cx.wrapping_add(c3); - z.data[n + i] = cy; + z[n] = cy; if cx < c2 || cy < c3 { c = 1; } else { @@ -74,17 +76,14 @@ fn montgomery(x: &BigUint, y: &BigUint, m: &BigUint, k: BigDigit, n: usize) -> B } } - if c == 0 { - z.data = z.data[n..].to_vec(); + let data = if c == 0 { + BigDigits::from_slice(&z[n..]) } else { - { - let (first, second) = z.data.split_at_mut(n); - sub_vv(first, second, &m.data); - } - z.data = z.data[..n].to_vec(); - } - - z + let (first, second) = z.split_at_mut(n); + sub_vv(first, second, m); + BigDigits::from_slice(first) + }; + BigUint { data } } #[inline(always)] @@ -177,11 +176,12 @@ pub(super) fn monty_modpow(x: &BigUint, y: &BigUint, m: &BigUint) -> BigUint { zz.data.resize(num_words, 0); // same windowed exponent, but with Montgomery multiplications - for i in (0..y.data.len()).rev() { - let mut yi = y.data[i]; + let y = &*y.data; + for i in (0..y.len()).rev() { + let mut yi = y[i]; let mut j = 0; while j < big_digit::BITS { - if i != y.data.len() - 1 || j != 0 { + if i != y.len() - 1 || j != 0 { zz = montgomery(&z, &z, m, mr.n0inv, num_words); z = montgomery(&zz, &zz, m, mr.n0inv, num_words); zz = montgomery(&z, &z, m, mr.n0inv, num_words); @@ -203,7 +203,7 @@ pub(super) fn monty_modpow(x: &BigUint, y: &BigUint, m: &BigUint) -> BigUint { // convert to regular number zz = montgomery(&z, &one, m, mr.n0inv, num_words); - zz.normalize(); + zz.data.normalize(); // One last reduction, just in case. // See golang.org/issue/13907. if zz >= *m { @@ -220,6 +220,6 @@ pub(super) fn monty_modpow(x: &BigUint, y: &BigUint, m: &BigUint) -> BigUint { } } - zz.normalize(); + zz.data.normalize(); zz } diff --git a/src/biguint/multiplication.rs b/src/biguint/multiplication.rs index e9d21384..44abe435 100644 --- a/src/biguint/multiplication.rs +++ b/src/biguint/multiplication.rs @@ -2,7 +2,7 @@ use super::addition::{__add2, add2}; use super::subtraction::sub2; use super::{biguint_from_vec, cmp_slice, BigUint, IntDigits}; -use crate::big_digit::{self, BigDigit, DoubleBigDigit}; +use crate::big_digit::{self, BigDigit, BigDigits, DoubleBigDigit}; use crate::Sign::{self, Minus, NoSign, Plus}; use crate::{BigInt, UsizePromotion}; @@ -58,7 +58,11 @@ fn mac_digit(acc: &mut [BigDigit], b: &[BigDigit], c: BigDigit) { } fn bigint_from_slice(slice: &[BigDigit]) -> BigInt { - BigInt::from(biguint_from_vec(slice.to_vec())) + let mut u = BigUint { + data: BigDigits::from_slice(slice), + }; + u.normalize(); + BigInt::from(u) } /// Three argument multiply accumulate: @@ -233,7 +237,9 @@ fn mac3(mut acc: &mut [BigDigit], mut b: &[BigDigit], mut c: &[BigDigit]) { // We reuse the same BigUint for all the intermediate multiplies and have to size p // appropriately here: x1.len() >= x0.len and y1.len() >= y0.len(): let len = x1.len() + y1.len() + 1; - let mut p = BigUint { data: vec![0; len] }; + let mut p = BigUint { + data: BigDigits::from_vec(vec![0; len]), + }; // p2 = x1 * y1 mac3(&mut p.data, x1, y1); @@ -245,7 +251,7 @@ fn mac3(mut acc: &mut [BigDigit], mut b: &[BigDigit], mut c: &[BigDigit]) { add2(&mut acc[b * 2..], &p.data); // Zero out p before the next multiply: - p.data.truncate(0); + p.data.clear(); p.data.resize(len, 0); // p0 = x0 * y0 @@ -262,7 +268,7 @@ fn mac3(mut acc: &mut [BigDigit], mut b: &[BigDigit], mut c: &[BigDigit]) { match j0_sign * j1_sign { Plus => { - p.data.truncate(0); + p.data.clear(); p.data.resize(len, 0); mac3(&mut p.data, &j0.data, &j1.data); @@ -409,10 +415,13 @@ fn mac3(mut acc: &mut [BigDigit], mut b: &[BigDigit], mut c: &[BigDigit]) { fn mul3(x: &[BigDigit], y: &[BigDigit]) -> BigUint { let len = x.len() + y.len() + 1; - let mut prod = BigUint { data: vec![0; len] }; + let mut prod = BigUint { + data: BigDigits::from_vec(vec![0; len]), + }; mac3(&mut prod.data, x, y); - prod.normalized() + prod.normalize(); + prod } fn scalar_mul(a: &mut BigUint, b: BigDigit) { diff --git a/src/biguint/shift.rs b/src/biguint/shift.rs index 51483cd0..5ce6b487 100644 --- a/src/biguint/shift.rs +++ b/src/biguint/shift.rs @@ -1,6 +1,6 @@ -use super::{biguint_from_vec, BigUint}; +use super::BigUint; -use crate::big_digit; +use crate::big_digit::{self, BigDigits}; use alloc::borrow::Cow; use alloc::vec::Vec; @@ -9,7 +9,7 @@ use core::ops::{Shl, ShlAssign, Shr, ShrAssign}; use num_traits::{PrimInt, Zero}; #[inline] -fn biguint_shl(n: Cow<'_, BigUint>, shift: T) -> BigUint { +pub(super) fn biguint_shl(n: Cow<'_, BigUint>, shift: T) -> BigUint { if shift < T::zero() { panic!("attempt to shift left with negative"); } @@ -30,7 +30,7 @@ fn biguint_shl2(n: Cow<'_, BigUint>, digits: usize, shift: u8) -> BigUint { let mut data = Vec::with_capacity(len); data.resize(digits, 0); data.extend(n.data.iter()); - data + BigDigits::from_vec(data) } }; @@ -47,7 +47,7 @@ fn biguint_shl2(n: Cow<'_, BigUint>, digits: usize, shift: u8) -> BigUint { } } - biguint_from_vec(data) + BigUint { data } } #[inline] @@ -66,14 +66,19 @@ fn biguint_shr(n: Cow<'_, BigUint>, shift: T) -> BigUint { fn biguint_shr2(n: Cow<'_, BigUint>, digits: usize, shift: u8) -> BigUint { if digits >= n.data.len() { - let mut n = n.into_owned(); - n.set_zero(); - return n; + return match n { + Cow::Borrowed(_) => BigUint::ZERO, + Cow::Owned(mut n) => { + n.set_zero(); + n + } + }; } let mut data = match n { - Cow::Borrowed(n) => n.data[digits..].to_vec(), + Cow::Borrowed(n) => BigDigits::from_slice(&n.data[digits..]), Cow::Owned(mut n) => { - n.data.drain(..digits); + n.data.drain_front(digits); + n.data.shrink(); n.data } }; @@ -86,9 +91,14 @@ fn biguint_shr2(n: Cow<'_, BigUint>, digits: usize, shift: u8) -> BigUint { *elem = (*elem >> shift) | borrow; borrow = new_borrow; } + // Assuming we were normal before, only one + // most-significant digit might be off now. + if !data.is_normal() { + data.pop(); + } } - biguint_from_vec(data) + BigUint { data } } macro_rules! impl_shift { diff --git a/src/biguint/subtraction.rs b/src/biguint/subtraction.rs index 58839403..0ed47196 100644 --- a/src/biguint/subtraction.rs +++ b/src/biguint/subtraction.rs @@ -110,8 +110,8 @@ impl Sub<&BigUint> for BigUint { } impl SubAssign<&BigUint> for BigUint { fn sub_assign(&mut self, other: &BigUint) { - sub2(&mut self.data[..], &other.data[..]); - self.normalize(); + sub2(&mut self.data, &other.data); + self.data.normalize(); } } @@ -121,15 +121,17 @@ impl Sub for &BigUint { fn sub(self, mut other: BigUint) -> BigUint { let other_len = other.data.len(); if other_len < self.data.len() { - let lo_borrow = __sub2rev(&self.data[..other_len], &mut other.data); - other.data.extend_from_slice(&self.data[other_len..]); + let (lo, hi) = self.data.split_at(other_len); + let lo_borrow = __sub2rev(lo, &mut other.data); + other.data.extend_from_slice(hi); if lo_borrow != 0 { sub2(&mut other.data[other_len..], &[1]) } } else { - sub2rev(&self.data[..], &mut other.data[..]); + sub2rev(&self.data, &mut other.data); } - other.normalized() + other.data.normalize(); + other } } @@ -151,8 +153,8 @@ impl Sub for BigUint { impl SubAssign for BigUint { fn sub_assign(&mut self, other: u32) { - sub2(&mut self.data[..], &[other as BigDigit]); - self.normalize(); + sub2(&mut self.data, &[other as BigDigit]); + self.data.normalize(); } } @@ -162,12 +164,13 @@ impl Sub for u32 { cfg_digit!( #[inline] fn sub(self, mut other: BigUint) -> BigUint { - if other.data.len() == 0 { + if other.data.is_empty() { other.data.push(self); } else { - sub2rev(&[self], &mut other.data[..]); + sub2rev(&[self], &mut other.data); } - other.normalized() + other.data.normalize(); + other } #[inline] @@ -175,9 +178,10 @@ impl Sub for u32 { if other.data.is_empty() { other.data.push(self as BigDigit); } else { - sub2rev(&[self as BigDigit], &mut other.data[..]); + sub2rev(&[self as BigDigit], &mut other.data); } - other.normalized() + other.data.normalize(); + other } ); } @@ -197,14 +201,14 @@ impl SubAssign for BigUint { #[inline] fn sub_assign(&mut self, other: u64) { let (hi, lo) = big_digit::from_doublebigdigit(other); - sub2(&mut self.data[..], &[lo, hi]); - self.normalize(); + sub2(&mut self.data, &[lo, hi]); + self.data.normalize(); } #[inline] fn sub_assign(&mut self, other: u64) { - sub2(&mut self.data[..], &[other as BigDigit]); - self.normalize(); + sub2(&mut self.data, &[other as BigDigit]); + self.data.normalize(); } ); } @@ -220,8 +224,9 @@ impl Sub for u64 { } let (hi, lo) = big_digit::from_doublebigdigit(self); - sub2rev(&[lo, hi], &mut other.data[..]); - other.normalized() + sub2rev(&[lo, hi], &mut other.data); + other.data.normalize(); + other } #[inline] @@ -229,9 +234,10 @@ impl Sub for u64 { if other.data.is_empty() { other.data.push(self); } else { - sub2rev(&[self], &mut other.data[..]); + sub2rev(&[self], &mut other.data); } - other.normalized() + other.data.normalize(); + other } ); } @@ -251,15 +257,15 @@ impl SubAssign for BigUint { #[inline] fn sub_assign(&mut self, other: u128) { let (a, b, c, d) = super::u32_from_u128(other); - sub2(&mut self.data[..], &[d, c, b, a]); - self.normalize(); + sub2(&mut self.data, &[d, c, b, a]); + self.data.normalize(); } #[inline] fn sub_assign(&mut self, other: u128) { let (hi, lo) = big_digit::from_doublebigdigit(other); - sub2(&mut self.data[..], &[lo, hi]); - self.normalize(); + sub2(&mut self.data, &[lo, hi]); + self.data.normalize(); } ); } @@ -275,8 +281,9 @@ impl Sub for u128 { } let (a, b, c, d) = super::u32_from_u128(self); - sub2rev(&[d, c, b, a], &mut other.data[..]); - other.normalized() + sub2rev(&[d, c, b, a], &mut other.data); + other.data.normalize(); + other } #[inline] @@ -286,8 +293,9 @@ impl Sub for u128 { } let (hi, lo) = big_digit::from_doublebigdigit(self); - sub2rev(&[lo, hi], &mut other.data[..]); - other.normalized() + sub2rev(&[lo, hi], &mut other.data); + other.data.normalize(); + other } ); } diff --git a/src/lib.rs b/src/lib.rs index 671d544b..6c4bd8f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,13 @@ //! depending on `num-bigint` with `default-features = false`. Either way, the `alloc` crate is //! always required for heap allocation of the `BigInt`/`BigUint` digits. //! +//! ### Inline Small Integers +//! +//! The `inline` crate feature enables small values to be stored directly in `BigInt` and `BigUint` +//! without a heap allocation. This also add new `const` values like [`BigUint::ONE`] and +//! [`BigInt::NEG_ONE`], as well as [`BigUint::new_const`] and [`BigInt::new_const`] methods for +//! 32-bit constant initialization. +//! //! ### Random Generation //! //! `num-bigint` supports the generation of random big integers when the `rand` @@ -224,51 +231,4 @@ pub use crate::bigint::ToBigInt; #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] pub use crate::bigrand::{RandBigInt, RandomBits, UniformBigInt, UniformBigUint}; -mod big_digit { - // A [`BigDigit`] is a [`BigUint`]'s composing element. - cfg_digit!( - pub(crate) type BigDigit = u32; - pub(crate) type BigDigit = u64; - ); - - // A [`DoubleBigDigit`] is the internal type used to do the computations. Its - // size is the double of the size of [`BigDigit`]. - cfg_digit!( - pub(crate) type DoubleBigDigit = u64; - pub(crate) type DoubleBigDigit = u128; - ); - - // A [`SignedDoubleBigDigit`] is the signed version of [`DoubleBigDigit`]. - cfg_digit!( - pub(crate) type SignedDoubleBigDigit = i64; - pub(crate) type SignedDoubleBigDigit = i128; - ); - - pub(crate) const BITS: u8 = BigDigit::BITS as u8; - pub(crate) const HALF_BITS: u8 = BITS / 2; - pub(crate) const HALF: BigDigit = (1 << HALF_BITS) - 1; - - pub(crate) const MAX: BigDigit = BigDigit::MAX; - const LO_MASK: DoubleBigDigit = MAX as DoubleBigDigit; - - #[inline] - fn get_hi(n: DoubleBigDigit) -> BigDigit { - (n >> BITS) as BigDigit - } - #[inline] - fn get_lo(n: DoubleBigDigit) -> BigDigit { - (n & LO_MASK) as BigDigit - } - - /// Split one [`DoubleBigDigit`] into two [`BigDigit`]s. - #[inline] - pub(crate) fn from_doublebigdigit(n: DoubleBigDigit) -> (BigDigit, BigDigit) { - (get_hi(n), get_lo(n)) - } - - /// Join two [`BigDigit`]s into one [`DoubleBigDigit`]. - #[inline] - pub(crate) fn to_doublebigdigit(hi: BigDigit, lo: BigDigit) -> DoubleBigDigit { - DoubleBigDigit::from(lo) | (DoubleBigDigit::from(hi) << BITS) - } -} +mod big_digit;