Add add/subtract functions that work in place, on slices
This is needed for the multiply optimizations in the next patch.
This commit is contained in:
parent
a1e57a48b2
commit
496ae0337c
193
src/bigint.rs
193
src/bigint.rs
|
@ -116,6 +116,38 @@ pub mod big_digit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic functions for add/subtract/multiply with carry/borrow:
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Add with carry:
|
||||||
|
#[inline]
|
||||||
|
fn adc(a: BigDigit, b: BigDigit, carry: &mut BigDigit) -> BigDigit {
|
||||||
|
let (hi, lo) = big_digit::from_doublebigdigit(
|
||||||
|
(a as DoubleBigDigit) +
|
||||||
|
(b as DoubleBigDigit) +
|
||||||
|
(*carry as DoubleBigDigit));
|
||||||
|
|
||||||
|
*carry = hi;
|
||||||
|
lo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract with borrow:
|
||||||
|
#[inline]
|
||||||
|
fn sbb(a: BigDigit, b: BigDigit, borrow: &mut BigDigit) -> BigDigit {
|
||||||
|
let (hi, lo) = big_digit::from_doublebigdigit(
|
||||||
|
big_digit::BASE
|
||||||
|
+ (a as DoubleBigDigit)
|
||||||
|
- (b as DoubleBigDigit)
|
||||||
|
- (*borrow as DoubleBigDigit));
|
||||||
|
/*
|
||||||
|
hi * (base) + lo == 1*(base) + ai - bi - borrow
|
||||||
|
=> ai - bi - borrow < 0 <=> hi == 0
|
||||||
|
*/
|
||||||
|
*borrow = if hi == 0 { 1 } else { 0 };
|
||||||
|
lo
|
||||||
|
}
|
||||||
|
|
||||||
/// A big unsigned integer type.
|
/// A big unsigned integer type.
|
||||||
///
|
///
|
||||||
/// A `BigUint`-typed value `BigUint { data: vec!(a, b, c) }` represents a number
|
/// A `BigUint`-typed value `BigUint { data: vec!(a, b, c) }` represents a number
|
||||||
|
@ -467,69 +499,112 @@ impl Unsigned for BigUint {}
|
||||||
|
|
||||||
forward_all_binop_to_val_ref_commutative!(impl Add for BigUint, add);
|
forward_all_binop_to_val_ref_commutative!(impl Add for BigUint, add);
|
||||||
|
|
||||||
|
// Only for the Add impl:
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
fn __add2(a: &mut [BigDigit], b: &[BigDigit]) -> BigDigit {
|
||||||
|
let mut b_iter = b.iter();
|
||||||
|
let mut carry = 0;
|
||||||
|
|
||||||
|
for ai in a.iter_mut() {
|
||||||
|
if let Some(bi) = b_iter.next() {
|
||||||
|
*ai = adc(*ai, *bi, &mut carry);
|
||||||
|
} else if carry != 0 {
|
||||||
|
*ai = adc(*ai, 0, &mut carry);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(b_iter.next() == None);
|
||||||
|
carry
|
||||||
|
}
|
||||||
|
|
||||||
|
/// /Two argument addition of raw slices:
|
||||||
|
/// a += b
|
||||||
|
///
|
||||||
|
/// The caller _must_ ensure that a is big enough to store the result - typically this means
|
||||||
|
/// resizing a to max(a.len(), b.len()) + 1, to fit a possible carry.
|
||||||
|
fn add2(a: &mut [BigDigit], b: &[BigDigit]) {
|
||||||
|
let carry = __add2(a, b);
|
||||||
|
|
||||||
|
debug_assert!(carry == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We'd really prefer to avoid using add2/sub2 directly as much as possible - since they make the
|
||||||
|
* caller entirely responsible for ensuring a's vector is big enough, and that the result is
|
||||||
|
* normalized, they're rather error prone and verbose:
|
||||||
|
*
|
||||||
|
* We could implement the Add and Sub traits for BigUint + BigDigit slices, like below - this works
|
||||||
|
* great, except that then it becomes the module's public interface, which we probably don't want:
|
||||||
|
*
|
||||||
|
* I'm keeping the code commented out, because I think this is worth revisiting:
|
||||||
|
|
||||||
|
impl<'a> Add<&'a [BigDigit]> for BigUint {
|
||||||
|
type Output = BigUint;
|
||||||
|
|
||||||
|
fn add(mut self, other: &[BigDigit]) -> BigUint {
|
||||||
|
if self.data.len() < other.len() {
|
||||||
|
let extra = other.len() - self.data.len();
|
||||||
|
self.data.extend(repeat(0).take(extra));
|
||||||
|
}
|
||||||
|
|
||||||
|
let carry = __add2(&mut self.data[..], other);
|
||||||
|
if carry != 0 {
|
||||||
|
self.data.push(carry);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
impl<'a> Add<&'a BigUint> for BigUint {
|
impl<'a> Add<&'a BigUint> for BigUint {
|
||||||
type Output = BigUint;
|
type Output = BigUint;
|
||||||
|
|
||||||
fn add(self, other: &BigUint) -> BigUint {
|
fn add(mut self, other: &BigUint) -> BigUint {
|
||||||
let mut sum = self.data;
|
if self.data.len() < other.data.len() {
|
||||||
|
let extra = other.data.len() - self.data.len();
|
||||||
if other.data.len() > sum.len() {
|
self.data.extend(repeat(0).take(extra));
|
||||||
let additional = other.data.len() - sum.len();
|
|
||||||
sum.reserve(additional);
|
|
||||||
sum.extend(repeat(ZERO_BIG_DIGIT).take(additional));
|
|
||||||
}
|
|
||||||
let other_iter = other.data.iter().cloned().chain(repeat(ZERO_BIG_DIGIT));
|
|
||||||
|
|
||||||
let mut carry = 0;
|
|
||||||
for (a, b) in sum.iter_mut().zip(other_iter) {
|
|
||||||
let d = (*a as DoubleBigDigit)
|
|
||||||
+ (b as DoubleBigDigit)
|
|
||||||
+ (carry as DoubleBigDigit);
|
|
||||||
let (hi, lo) = big_digit::from_doublebigdigit(d);
|
|
||||||
carry = hi;
|
|
||||||
*a = lo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if carry != 0 { sum.push(carry); }
|
let carry = __add2(&mut self.data[..], &other.data[..]);
|
||||||
BigUint::new(sum)
|
if carry != 0 {
|
||||||
|
self.data.push(carry);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forward_all_binop_to_val_ref!(impl Sub for BigUint, sub);
|
forward_all_binop_to_val_ref!(impl Sub for BigUint, sub);
|
||||||
|
|
||||||
|
fn sub2(a: &mut [BigDigit], b: &[BigDigit]) {
|
||||||
|
let mut b_iter = b.iter();
|
||||||
|
let mut borrow = 0;
|
||||||
|
|
||||||
|
for ai in a.iter_mut() {
|
||||||
|
if let Some(bi) = b_iter.next() {
|
||||||
|
*ai = sbb(*ai, *bi, &mut borrow);
|
||||||
|
} else if borrow != 0 {
|
||||||
|
*ai = sbb(*ai, 0, &mut borrow);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* note: we're _required_ to fail on underflow */
|
||||||
|
assert!(borrow == 0 && b_iter.all(|x| *x == 0),
|
||||||
|
"Cannot subtract b from a because b is larger than a.");
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Sub<&'a BigUint> for BigUint {
|
impl<'a> Sub<&'a BigUint> for BigUint {
|
||||||
type Output = BigUint;
|
type Output = BigUint;
|
||||||
|
|
||||||
fn sub(self, other: &BigUint) -> BigUint {
|
fn sub(mut self, other: &BigUint) -> BigUint {
|
||||||
let mut diff = self.data;
|
sub2(&mut self.data[..], &other.data[..]);
|
||||||
let other = &other.data;
|
self.normalize()
|
||||||
assert!(diff.len() >= other.len(), "arithmetic operation overflowed");
|
|
||||||
|
|
||||||
let mut borrow: DoubleBigDigit = 0;
|
|
||||||
for (a, &b) in diff.iter_mut().zip(other.iter()) {
|
|
||||||
let d = big_digit::BASE - borrow
|
|
||||||
+ (*a as DoubleBigDigit)
|
|
||||||
- (b as DoubleBigDigit);
|
|
||||||
let (hi, lo) = big_digit::from_doublebigdigit(d);
|
|
||||||
/*
|
|
||||||
hi * (base) + lo == 1*(base) + ai - bi - borrow
|
|
||||||
=> ai - bi - borrow < 0 <=> hi == 0
|
|
||||||
*/
|
|
||||||
borrow = if hi == 0 { 1 } else { 0 };
|
|
||||||
*a = lo;
|
|
||||||
}
|
|
||||||
|
|
||||||
for a in &mut diff[other.len()..] {
|
|
||||||
if borrow == 0 { break }
|
|
||||||
let d = big_digit::BASE - borrow
|
|
||||||
+ (*a as DoubleBigDigit);
|
|
||||||
let (hi, lo) = big_digit::from_doublebigdigit(d);
|
|
||||||
borrow = if hi == 0 { 1 } else { 0 };
|
|
||||||
*a = lo;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(borrow == 0, "arithmetic operation overflowed");
|
|
||||||
BigUint::new(diff)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -982,12 +1057,8 @@ impl BigUint {
|
||||||
///
|
///
|
||||||
/// The digits are in little-endian base 2^32.
|
/// The digits are in little-endian base 2^32.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(mut digits: Vec<BigDigit>) -> BigUint {
|
pub fn new(digits: Vec<BigDigit>) -> BigUint {
|
||||||
// omit trailing zeros
|
BigUint { data: digits }.normalize()
|
||||||
while let Some(&0) = digits.last() {
|
|
||||||
digits.pop();
|
|
||||||
}
|
|
||||||
BigUint { data: digits }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates and initializes a `BigUint`.
|
/// Creates and initializes a `BigUint`.
|
||||||
|
@ -1176,6 +1247,16 @@ impl BigUint {
|
||||||
let zeros = self.data.last().unwrap().leading_zeros();
|
let zeros = self.data.last().unwrap().leading_zeros();
|
||||||
return self.data.len()*big_digit::BITS - zeros as usize;
|
return self.data.len()*big_digit::BITS - zeros as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Strips off trailing zero bigdigits - comparisons require the last element in the vector to
|
||||||
|
/// be nonzero.
|
||||||
|
#[inline]
|
||||||
|
fn normalize(mut self) -> BigUint {
|
||||||
|
while let Some(&0) = self.data.last() {
|
||||||
|
self.data.pop();
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `DoubleBigDigit` size dependent
|
// `DoubleBigDigit` size dependent
|
||||||
|
|
Loading…
Reference in New Issue