257 lines
6.9 KiB
Rust
257 lines
6.9 KiB
Rust
use super::{BigUint, IntDigits};
|
|
|
|
use crate::big_digit::{self, BigDigit};
|
|
use crate::UsizePromotion;
|
|
|
|
use core::iter::Sum;
|
|
use core::ops::{Add, AddAssign};
|
|
use num_traits::CheckedAdd;
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
use core::arch::x86_64 as arch;
|
|
|
|
#[cfg(target_arch = "x86")]
|
|
use core::arch::x86 as arch;
|
|
|
|
// Add with carry:
|
|
#[cfg(target_arch = "x86_64")]
|
|
cfg_64!(
|
|
#[inline]
|
|
fn adc(carry: u8, a: u64, b: u64, out: &mut u64) -> u8 {
|
|
// Safety: There are absolutely no safety concerns with calling `_addcarry_u64`.
|
|
// It's just unsafe for API consistency with other intrinsics.
|
|
unsafe { arch::_addcarry_u64(carry, a, b, out) }
|
|
}
|
|
);
|
|
|
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
cfg_32!(
|
|
#[inline]
|
|
fn adc(carry: u8, a: u32, b: u32, out: &mut u32) -> u8 {
|
|
// Safety: There are absolutely no safety concerns with calling `_addcarry_u32`.
|
|
// It's just unsafe for API consistency with other intrinsics.
|
|
unsafe { arch::_addcarry_u32(carry, a, b, out) }
|
|
}
|
|
);
|
|
|
|
// fallback for environments where we don't have an addcarry intrinsic
|
|
// (copied from the standard library's `carrying_add`)
|
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
|
#[inline]
|
|
fn adc(carry: u8, lhs: BigDigit, rhs: BigDigit, out: &mut BigDigit) -> u8 {
|
|
let (a, b) = lhs.overflowing_add(rhs);
|
|
let (c, d) = a.overflowing_add(carry as BigDigit);
|
|
*out = c;
|
|
u8::from(b || d)
|
|
}
|
|
|
|
/// Two argument addition of raw slices, `a += b`, returning the carry.
|
|
///
|
|
/// This is used when the data `Vec` might need to resize to push a non-zero carry, so we perform
|
|
/// the addition first hoping that it will fit.
|
|
///
|
|
/// The caller _must_ ensure that `a` is at least as long as `b`.
|
|
#[inline]
|
|
pub(super) fn __add2(a: &mut [BigDigit], b: &[BigDigit]) -> BigDigit {
|
|
debug_assert!(a.len() >= b.len());
|
|
|
|
let mut carry = 0;
|
|
let (a_lo, a_hi) = a.split_at_mut(b.len());
|
|
|
|
for (a, b) in a_lo.iter_mut().zip(b) {
|
|
carry = adc(carry, *a, *b, a);
|
|
}
|
|
|
|
if carry != 0 {
|
|
for a in a_hi {
|
|
carry = adc(carry, *a, 0, a);
|
|
if carry == 0 {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
carry as BigDigit
|
|
}
|
|
|
|
/// 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.
|
|
pub(super) fn add2(a: &mut [BigDigit], b: &[BigDigit]) {
|
|
let carry = __add2(a, b);
|
|
|
|
debug_assert!(carry == 0);
|
|
}
|
|
|
|
forward_all_binop_to_val_ref_commutative!(impl Add for BigUint, add);
|
|
forward_val_assign!(impl AddAssign for BigUint, add_assign);
|
|
|
|
impl Add<&BigUint> for BigUint {
|
|
type Output = BigUint;
|
|
|
|
fn add(mut self, other: &BigUint) -> BigUint {
|
|
self += other;
|
|
self
|
|
}
|
|
}
|
|
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[..])
|
|
};
|
|
if carry != 0 {
|
|
self.data.push(carry);
|
|
}
|
|
}
|
|
}
|
|
|
|
promote_unsigned_scalars!(impl Add for BigUint, add);
|
|
promote_unsigned_scalars_assign!(impl AddAssign for BigUint, add_assign);
|
|
forward_all_scalar_binop_to_val_val_commutative!(impl Add<u32> for BigUint, add);
|
|
forward_all_scalar_binop_to_val_val_commutative!(impl Add<u64> for BigUint, add);
|
|
forward_all_scalar_binop_to_val_val_commutative!(impl Add<u128> for BigUint, add);
|
|
|
|
impl Add<u32> for BigUint {
|
|
type Output = BigUint;
|
|
|
|
#[inline]
|
|
fn add(mut self, other: u32) -> BigUint {
|
|
self += other;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl AddAssign<u32> for BigUint {
|
|
#[inline]
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Add<u64> for BigUint {
|
|
type Output = BigUint;
|
|
|
|
#[inline]
|
|
fn add(mut self, other: u64) -> BigUint {
|
|
self += other;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl AddAssign<u64> for BigUint {
|
|
cfg_digit!(
|
|
#[inline]
|
|
fn add_assign(&mut self, other: u64) {
|
|
let (hi, lo) = big_digit::from_doublebigdigit(other);
|
|
if hi == 0 {
|
|
*self += lo;
|
|
} else {
|
|
while self.data.len() < 2 {
|
|
self.data.push(0);
|
|
}
|
|
|
|
let carry = __add2(&mut self.data, &[lo, hi]);
|
|
if carry != 0 {
|
|
self.data.push(carry);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
impl Add<u128> for BigUint {
|
|
type Output = BigUint;
|
|
|
|
#[inline]
|
|
fn add(mut self, other: u128) -> BigUint {
|
|
self += other;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl AddAssign<u128> for BigUint {
|
|
cfg_digit!(
|
|
#[inline]
|
|
fn add_assign(&mut self, other: u128) {
|
|
if other <= u128::from(u64::MAX) {
|
|
*self += other as u64
|
|
} else {
|
|
let (a, b, c, d) = super::u32_from_u128(other);
|
|
let carry = if a > 0 {
|
|
while self.data.len() < 4 {
|
|
self.data.push(0);
|
|
}
|
|
__add2(&mut self.data, &[d, c, b, a])
|
|
} else {
|
|
debug_assert!(b > 0);
|
|
while self.data.len() < 3 {
|
|
self.data.push(0);
|
|
}
|
|
__add2(&mut self.data, &[d, c, b])
|
|
};
|
|
|
|
if carry != 0 {
|
|
self.data.push(carry);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn add_assign(&mut self, other: u128) {
|
|
let (hi, lo) = big_digit::from_doublebigdigit(other);
|
|
if hi == 0 {
|
|
*self += lo;
|
|
} else {
|
|
while self.data.len() < 2 {
|
|
self.data.push(0);
|
|
}
|
|
|
|
let carry = __add2(&mut self.data, &[lo, hi]);
|
|
if carry != 0 {
|
|
self.data.push(carry);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
impl CheckedAdd for BigUint {
|
|
#[inline]
|
|
fn checked_add(&self, v: &BigUint) -> Option<BigUint> {
|
|
Some(self.add(v))
|
|
}
|
|
}
|
|
|
|
impl_sum_iter_type!(BigUint);
|