313: Scalar operations across all integer types r=cuviper

With my apologies for opening a new PR, and also for the 8 month delay, this continues the work started in #237 - the discussion there outlines the goals I was aiming for. I suppose this supersedes that PR and the other one can now be closed.

This PR adds support for Add, Sub, Mul, Div and Rem operations involving one BigInt/BigUint and one primitive integer, with operands in either order, and any combination of owned/borrowed arguments.
This commit is contained in:
bors[bot] 2017-07-12 05:16:29 +00:00
commit ebc36b3e55
7 changed files with 1088 additions and 5 deletions

View File

@ -85,6 +85,15 @@ pub fn mac_with_carry(a: BigDigit, b: BigDigit, c: BigDigit, carry: &mut BigDigi
lo lo
} }
#[inline]
pub fn mul_with_carry(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
}
/// Divide a two digit numerator by a one digit divisor, returns quotient and remainder: /// Divide a two digit numerator by a one digit divisor, returns quotient and remainder:
/// ///
/// Note: the caller must ensure that both the quotient and remainder will fit into a single digit. /// Note: the caller must ensure that both the quotient and remainder will fit into a single digit.
@ -377,6 +386,14 @@ pub fn mul3(x: &[BigDigit], y: &[BigDigit]) -> BigUint {
prod.normalize() prod.normalize()
} }
pub fn scalar_mul(a: &mut [BigDigit], b: BigDigit) -> BigDigit {
let mut carry = 0;
for a in a.iter_mut() {
*a = mul_with_carry(*a, b, &mut carry);
}
carry
}
pub fn div_rem(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { pub fn div_rem(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) {
if d.is_zero() { if d.is_zero() {
panic!() panic!()
@ -416,7 +433,7 @@ pub fn div_rem(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) {
// q0, our guess, is calculated by dividing the last few digits of a by the last digit of b // q0, our guess, is calculated by dividing the last few digits of a by the last digit of b
// - this should give us a guess that is "close" to the actual quotient, but is possibly // - this should give us a guess that is "close" to the actual quotient, but is possibly
// greater than the actual quotient. If q0 * b > a, we simply use iterated subtraction // greater than the actual quotient. If q0 * b > a, we simply use iterated subtraction
// until we have a guess such that q0 & b <= a. // until we have a guess such that q0 * b <= a.
// //
let bn = *b.data.last().unwrap(); let bn = *b.data.last().unwrap();

View File

@ -23,11 +23,14 @@ use self::Sign::{Minus, NoSign, Plus};
use super::ParseBigIntError; use super::ParseBigIntError;
use super::big_digit; use super::big_digit;
use super::big_digit::BigDigit; use super::big_digit::{BigDigit, DoubleBigDigit};
use biguint; use biguint;
use biguint::to_str_radix_reversed; use biguint::to_str_radix_reversed;
use biguint::BigUint; use biguint::BigUint;
use UsizePromotion;
use IsizePromotion;
#[cfg(test)] #[cfg(test)]
#[path = "tests/bigint.rs"] #[path = "tests/bigint.rs"]
mod bigint_tests; mod bigint_tests;
@ -299,6 +302,26 @@ impl Signed for BigInt {
} }
} }
// A convenience method for getting the absolute value of an i32 in a u32.
#[inline]
fn i32_abs_as_u32(a: i32) -> u32 {
if a == i32::min_value() {
a as u32
} else {
a.abs() as u32
}
}
// A convenience method for getting the absolute value of an i64 in a u64.
#[inline]
fn i64_abs_as_u64(a: i64) -> u64 {
if a == i64::min_value() {
a as u64
} else {
a.abs() as u64
}
}
// We want to forward to BigUint::add, but it's not clear how that will go until // We want to forward to BigUint::add, but it's not clear how that will go until
// we compare both sign and magnitude. So we duplicate this body for every // we compare both sign and magnitude. So we duplicate this body for every
// val/ref combination, deferring that decision to BigUint's own forwarding. // val/ref combination, deferring that decision to BigUint's own forwarding.
@ -362,6 +385,75 @@ impl Add<BigInt> for BigInt {
} }
} }
promote_all_scalars!(impl Add for BigInt, add);
forward_all_scalar_binop_to_val_val_commutative!(impl Add<BigDigit> for BigInt, add);
forward_all_scalar_binop_to_val_val_commutative!(impl Add<DoubleBigDigit> for BigInt, add);
impl Add<BigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn add(self, other: BigDigit) -> BigInt {
match self.sign {
NoSign => From::from(other),
Plus => BigInt::from_biguint(Plus, self.data + other),
Minus =>
match self.data.cmp(&From::from(other)) {
Equal => Zero::zero(),
Less => BigInt::from_biguint(Plus, other - self.data),
Greater => BigInt::from_biguint(Minus, self.data - other),
}
}
}
}
impl Add<DoubleBigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn add(self, other: DoubleBigDigit) -> BigInt {
match self.sign {
NoSign => From::from(other),
Plus => BigInt::from_biguint(Plus, self.data + other),
Minus =>
match self.data.cmp(&From::from(other)) {
Equal => Zero::zero(),
Less => BigInt::from_biguint(Plus, other - self.data),
Greater => BigInt::from_biguint(Minus, self.data - other),
}
}
}
}
forward_all_scalar_binop_to_val_val_commutative!(impl Add<i32> for BigInt, add);
forward_all_scalar_binop_to_val_val_commutative!(impl Add<i64> for BigInt, add);
impl Add<i32> for BigInt {
type Output = BigInt;
#[inline]
fn add(self, other: i32) -> BigInt {
if other >= 0 {
self + other as u32
} else {
self - i32_abs_as_u32(other)
}
}
}
impl Add<i64> for BigInt {
type Output = BigInt;
#[inline]
fn add(self, other: i64) -> BigInt {
if other >= 0 {
self + other as u64
} else {
self - i64_abs_as_u64(other)
}
}
}
// We want to forward to BigUint::sub, but it's not clear how that will go until // We want to forward to BigUint::sub, but it's not clear how that will go until
// we compare both sign and magnitude. So we duplicate this body for every // we compare both sign and magnitude. So we duplicate this body for every
// val/ref combination, deferring that decision to BigUint's own forwarding. // val/ref combination, deferring that decision to BigUint's own forwarding.
@ -425,6 +517,119 @@ impl Sub<BigInt> for BigInt {
} }
} }
promote_all_scalars!(impl Sub for BigInt, sub);
forward_all_scalar_binop_to_val_val!(impl Sub<BigDigit> for BigInt, sub);
forward_all_scalar_binop_to_val_val!(impl Sub<DoubleBigDigit> for BigInt, sub);
impl Sub<BigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn sub(self, other: BigDigit) -> BigInt {
match self.sign {
NoSign => BigInt::from_biguint(Minus, From::from(other)),
Minus => BigInt::from_biguint(Minus, self.data + other),
Plus =>
match self.data.cmp(&From::from(other)) {
Equal => Zero::zero(),
Greater => BigInt::from_biguint(Plus, self.data - other),
Less => BigInt::from_biguint(Minus, other - self.data),
}
}
}
}
impl Sub<BigInt> for BigDigit {
type Output = BigInt;
#[inline]
fn sub(self, other: BigInt) -> BigInt {
-(other - self)
}
}
impl Sub<DoubleBigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn sub(self, other: DoubleBigDigit) -> BigInt {
match self.sign {
NoSign => BigInt::from_biguint(Minus, From::from(other)),
Minus => BigInt::from_biguint(Minus, self.data + other),
Plus =>
match self.data.cmp(&From::from(other)) {
Equal => Zero::zero(),
Greater => BigInt::from_biguint(Plus, self.data - other),
Less => BigInt::from_biguint(Minus, other - self.data),
}
}
}
}
impl Sub<BigInt> for DoubleBigDigit {
type Output = BigInt;
#[inline]
fn sub(self, other: BigInt) -> BigInt {
-(other - self)
}
}
forward_all_scalar_binop_to_val_val!(impl Sub<i32> for BigInt, sub);
forward_all_scalar_binop_to_val_val!(impl Sub<i64> for BigInt, sub);
impl Sub<i32> for BigInt {
type Output = BigInt;
#[inline]
fn sub(self, other: i32) -> BigInt {
if other >= 0 {
self - other as u32
} else {
self + i32_abs_as_u32(other)
}
}
}
impl Sub<BigInt> for i32 {
type Output = BigInt;
#[inline]
fn sub(self, other: BigInt) -> BigInt {
if self >= 0 {
self as u32 - other
} else {
-other - i32_abs_as_u32(self)
}
}
}
impl Sub<i64> for BigInt {
type Output = BigInt;
#[inline]
fn sub(self, other: i64) -> BigInt {
if other >= 0 {
self - other as u64
} else {
self + i64_abs_as_u64(other)
}
}
}
impl Sub<BigInt> for i64 {
type Output = BigInt;
#[inline]
fn sub(self, other: BigInt) -> BigInt {
if self >= 0 {
self as u64 - other
} else {
-other - i64_abs_as_u64(self)
}
}
}
forward_all_binop_to_ref_ref!(impl Mul for BigInt, mul); forward_all_binop_to_ref_ref!(impl Mul for BigInt, mul);
impl<'a, 'b> Mul<&'b BigInt> for &'a BigInt { impl<'a, 'b> Mul<&'b BigInt> for &'a BigInt {
@ -436,6 +641,57 @@ impl<'a, 'b> Mul<&'b BigInt> for &'a BigInt {
} }
} }
promote_all_scalars!(impl Mul for BigInt, mul);
forward_all_scalar_binop_to_val_val_commutative!(impl Mul<BigDigit> for BigInt, mul);
forward_all_scalar_binop_to_val_val_commutative!(impl Mul<DoubleBigDigit> for BigInt, mul);
impl Mul<BigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn mul(self, other: BigDigit) -> BigInt {
BigInt::from_biguint(self.sign, self.data * other)
}
}
impl Mul<DoubleBigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn mul(self, other: DoubleBigDigit) -> BigInt {
BigInt::from_biguint(self.sign, self.data * other)
}
}
forward_all_scalar_binop_to_val_val_commutative!(impl Mul<i32> for BigInt, mul);
forward_all_scalar_binop_to_val_val_commutative!(impl Mul<i64> for BigInt, mul);
impl Mul<i32> for BigInt {
type Output = BigInt;
#[inline]
fn mul(self, other: i32) -> BigInt {
if other >= 0 {
self * other as u32
} else {
-(self * i32_abs_as_u32(other))
}
}
}
impl Mul<i64> for BigInt {
type Output = BigInt;
#[inline]
fn mul(self, other: i64) -> BigInt {
if other >= 0 {
self * other as u64
} else {
-(self * i64_abs_as_u64(other))
}
}
}
forward_all_binop_to_ref_ref!(impl Div for BigInt, div); forward_all_binop_to_ref_ref!(impl Div for BigInt, div);
impl<'a, 'b> Div<&'b BigInt> for &'a BigInt { impl<'a, 'b> Div<&'b BigInt> for &'a BigInt {
@ -448,6 +704,101 @@ impl<'a, 'b> Div<&'b BigInt> for &'a BigInt {
} }
} }
promote_all_scalars!(impl Div for BigInt, div);
forward_all_scalar_binop_to_val_val!(impl Div<BigDigit> for BigInt, div);
forward_all_scalar_binop_to_val_val!(impl Div<DoubleBigDigit> for BigInt, div);
impl Div<BigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn div(self, other: BigDigit) -> BigInt {
BigInt::from_biguint(self.sign, self.data / other)
}
}
impl Div<BigInt> for BigDigit {
type Output = BigInt;
#[inline]
fn div(self, other: BigInt) -> BigInt {
BigInt::from_biguint(other.sign, self / other.data)
}
}
impl Div<DoubleBigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn div(self, other: DoubleBigDigit) -> BigInt {
BigInt::from_biguint(self.sign, self.data / other)
}
}
impl Div<BigInt> for DoubleBigDigit {
type Output = BigInt;
#[inline]
fn div(self, other: BigInt) -> BigInt {
BigInt::from_biguint(other.sign, self / other.data)
}
}
forward_all_scalar_binop_to_val_val!(impl Div<i32> for BigInt, div);
forward_all_scalar_binop_to_val_val!(impl Div<i64> for BigInt, div);
impl Div<i32> for BigInt {
type Output = BigInt;
#[inline]
fn div(self, other: i32) -> BigInt {
if other >= 0 {
self / other as u32
} else {
-(self / i32_abs_as_u32(other))
}
}
}
impl Div<BigInt> for i32 {
type Output = BigInt;
#[inline]
fn div(self, other: BigInt) -> BigInt {
if self >= 0 {
self as u32 / other
} else {
-(i32_abs_as_u32(self) / other)
}
}
}
impl Div<i64> for BigInt {
type Output = BigInt;
#[inline]
fn div(self, other: i64) -> BigInt {
if other >= 0 {
self / other as u64
} else {
-(self / i64_abs_as_u64(other))
}
}
}
impl Div<BigInt> for i64 {
type Output = BigInt;
#[inline]
fn div(self, other: BigInt) -> BigInt {
if self >= 0 {
self as u64 / other
} else {
-(i64_abs_as_u64(self) / other)
}
}
}
forward_all_binop_to_ref_ref!(impl Rem for BigInt, rem); forward_all_binop_to_ref_ref!(impl Rem for BigInt, rem);
impl<'a, 'b> Rem<&'b BigInt> for &'a BigInt { impl<'a, 'b> Rem<&'b BigInt> for &'a BigInt {
@ -460,6 +811,101 @@ impl<'a, 'b> Rem<&'b BigInt> for &'a BigInt {
} }
} }
promote_all_scalars!(impl Rem for BigInt, rem);
forward_all_scalar_binop_to_val_val!(impl Rem<BigDigit> for BigInt, rem);
forward_all_scalar_binop_to_val_val!(impl Rem<DoubleBigDigit> for BigInt, rem);
impl Rem<BigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn rem(self, other: BigDigit) -> BigInt {
BigInt::from_biguint(self.sign, self.data % other)
}
}
impl Rem<BigInt> for BigDigit {
type Output = BigInt;
#[inline]
fn rem(self, other: BigInt) -> BigInt {
BigInt::from_biguint(Plus, self % other.data)
}
}
impl Rem<DoubleBigDigit> for BigInt {
type Output = BigInt;
#[inline]
fn rem(self, other: DoubleBigDigit) -> BigInt {
BigInt::from_biguint(self.sign, self.data % other)
}
}
impl Rem<BigInt> for DoubleBigDigit {
type Output = BigInt;
#[inline]
fn rem(self, other: BigInt) -> BigInt {
BigInt::from_biguint(Plus, self % other.data)
}
}
forward_all_scalar_binop_to_val_val!(impl Rem<i32> for BigInt, rem);
forward_all_scalar_binop_to_val_val!(impl Rem<i64> for BigInt, rem);
impl Rem<i32> for BigInt {
type Output = BigInt;
#[inline]
fn rem(self, other: i32) -> BigInt {
if other >= 0 {
self % other as u32
} else {
self % i32_abs_as_u32(other)
}
}
}
impl Rem<BigInt> for i32 {
type Output = BigInt;
#[inline]
fn rem(self, other: BigInt) -> BigInt {
if self >= 0 {
self as u32 % other
} else {
-(i32_abs_as_u32(self) % other)
}
}
}
impl Rem<i64> for BigInt {
type Output = BigInt;
#[inline]
fn rem(self, other: i64) -> BigInt {
if other >= 0 {
self % other as u64
} else {
self % i64_abs_as_u64(other)
}
}
}
impl Rem<BigInt> for i64 {
type Output = BigInt;
#[inline]
fn rem(self, other: BigInt) -> BigInt {
if self >= 0 {
self as u64 % other
} else {
-(i64_abs_as_u64(self) % other)
}
}
}
impl Neg for BigInt { impl Neg for BigInt {
type Output = BigInt; type Output = BigInt;

View File

@ -22,11 +22,13 @@ mod algorithms;
pub use self::algorithms::big_digit; pub use self::algorithms::big_digit;
pub use self::big_digit::{BigDigit, DoubleBigDigit, ZERO_BIG_DIGIT}; pub use self::big_digit::{BigDigit, DoubleBigDigit, ZERO_BIG_DIGIT};
use self::algorithms::{mac_with_carry, mul3, div_rem, div_rem_digit}; use self::algorithms::{mac_with_carry, mul3, scalar_mul, div_rem, div_rem_digit};
use self::algorithms::{__add2, add2, sub2, sub2rev}; use self::algorithms::{__add2, add2, sub2, sub2rev};
use self::algorithms::{biguint_shl, biguint_shr}; use self::algorithms::{biguint_shl, biguint_shr};
use self::algorithms::{cmp_slice, fls, ilog2}; use self::algorithms::{cmp_slice, fls, ilog2};
use UsizePromotion;
use ParseBigIntError; use ParseBigIntError;
#[cfg(test)] #[cfg(test)]
@ -394,6 +396,51 @@ impl<'a> Add<&'a BigUint> for BigUint {
} }
} }
promote_unsigned_scalars!(impl Add for BigUint, add);
forward_all_scalar_binop_to_val_val_commutative!(impl Add<BigDigit> for BigUint, add);
forward_all_scalar_binop_to_val_val_commutative!(impl Add<DoubleBigDigit> for BigUint, add);
impl Add<BigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn add(mut self, other: BigDigit) -> BigUint {
if other != 0 {
if self.data.len() == 0 {
self.data.push(0);
}
let carry = __add2(&mut self.data, &[other]);
if carry != 0 {
self.data.push(carry);
}
}
self
}
}
impl Add<DoubleBigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn add(mut self, other: DoubleBigDigit) -> BigUint {
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);
}
self
}
}
}
forward_val_val_binop!(impl Sub for BigUint, sub); forward_val_val_binop!(impl Sub for BigUint, sub);
forward_ref_ref_binop!(impl Sub for BigUint, sub); forward_ref_ref_binop!(impl Sub for BigUint, sub);
@ -420,6 +467,60 @@ impl<'a> Sub<BigUint> for &'a BigUint {
} }
} }
promote_unsigned_scalars!(impl Sub for BigUint, sub);
forward_all_scalar_binop_to_val_val!(impl Sub<BigDigit> for BigUint, sub);
forward_all_scalar_binop_to_val_val!(impl Sub<DoubleBigDigit> for BigUint, sub);
impl Sub<BigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn sub(mut self, other: BigDigit) -> BigUint {
sub2(&mut self.data[..], &[other]);
self.normalize()
}
}
impl Sub<BigUint> for BigDigit {
type Output = BigUint;
#[inline]
fn sub(self, mut other: BigUint) -> BigUint {
if other.data.len() == 0 {
other.data.push(0);
}
sub2rev(&[self], &mut other.data[..]);
other.normalize()
}
}
impl Sub<DoubleBigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn sub(mut self, other: DoubleBigDigit) -> BigUint {
let (hi, lo) = big_digit::from_doublebigdigit(other);
sub2(&mut self.data[..], &[lo, hi]);
self.normalize()
}
}
impl Sub<BigUint> for DoubleBigDigit {
type Output = BigUint;
#[inline]
fn sub(self, mut other: BigUint) -> BigUint {
while other.data.len() < 2 {
other.data.push(0);
}
let (hi, lo) = big_digit::from_doublebigdigit(self);
sub2rev(&[lo, hi], &mut other.data[..]);
other.normalize()
}
}
forward_all_binop_to_ref_ref!(impl Mul for BigUint, mul); forward_all_binop_to_ref_ref!(impl Mul for BigUint, mul);
impl<'a, 'b> Mul<&'b BigUint> for &'a BigUint { impl<'a, 'b> Mul<&'b BigUint> for &'a BigUint {
@ -431,6 +532,44 @@ impl<'a, 'b> Mul<&'b BigUint> for &'a BigUint {
} }
} }
promote_unsigned_scalars!(impl Mul for BigUint, mul);
forward_all_scalar_binop_to_val_val_commutative!(impl Mul<BigDigit> for BigUint, mul);
forward_all_scalar_binop_to_val_val_commutative!(impl Mul<DoubleBigDigit> for BigUint, mul);
impl Mul<BigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn mul(mut self, other: BigDigit) -> BigUint {
if other == 0 {
self.data.clear();
} else {
let carry = scalar_mul(&mut self.data[..], other);
if carry != 0 {
self.data.push(carry);
}
}
self
}
}
impl Mul<DoubleBigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn mul(mut self, other: DoubleBigDigit) -> BigUint {
if other == 0 {
self.data.clear();
self
} else if other <= BigDigit::max_value() as DoubleBigDigit {
self * other as BigDigit
} else {
let (hi, lo) = big_digit::from_doublebigdigit(other);
mul3(&self.data[..], &[lo, hi])
}
}
}
forward_all_binop_to_ref_ref!(impl Div for BigUint, div); forward_all_binop_to_ref_ref!(impl Div for BigUint, div);
impl<'a, 'b> Div<&'b BigUint> for &'a BigUint { impl<'a, 'b> Div<&'b BigUint> for &'a BigUint {
@ -439,7 +578,58 @@ impl<'a, 'b> Div<&'b BigUint> for &'a BigUint {
#[inline] #[inline]
fn div(self, other: &BigUint) -> BigUint { fn div(self, other: &BigUint) -> BigUint {
let (q, _) = self.div_rem(other); let (q, _) = self.div_rem(other);
return q; q
}
}
promote_unsigned_scalars!(impl Div for BigUint, div);
forward_all_scalar_binop_to_val_val!(impl Div<BigDigit> for BigUint, div);
forward_all_scalar_binop_to_val_val!(impl Div<DoubleBigDigit> for BigUint, div);
impl Div<BigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn div(self, other: BigDigit) -> BigUint {
let (q, _) = div_rem_digit(self, other);
q
}
}
impl Div<BigUint> for BigDigit {
type Output = BigUint;
#[inline]
fn div(self, other: BigUint) -> BigUint {
match other.data.len() {
0 => panic!(),
1 => From::from(self / other.data[0]),
_ => Zero::zero(),
}
}
}
impl Div<DoubleBigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn div(self, other: DoubleBigDigit) -> BigUint {
let (q, _) = self.div_rem(&From::from(other));
q
}
}
impl Div<BigUint> for DoubleBigDigit {
type Output = BigUint;
#[inline]
fn div(self, other: BigUint) -> BigUint {
match other.data.len() {
0 => panic!(),
1 => From::from(self / other.data[0] as u64),
2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])),
_ => Zero::zero(),
}
} }
} }
@ -451,7 +641,58 @@ impl<'a, 'b> Rem<&'b BigUint> for &'a BigUint {
#[inline] #[inline]
fn rem(self, other: &BigUint) -> BigUint { fn rem(self, other: &BigUint) -> BigUint {
let (_, r) = self.div_rem(other); let (_, r) = self.div_rem(other);
return r; r
}
}
promote_unsigned_scalars!(impl Rem for BigUint, rem);
forward_all_scalar_binop_to_val_val!(impl Rem<BigDigit> for BigUint, rem);
forward_all_scalar_binop_to_val_val!(impl Rem<DoubleBigDigit> for BigUint, rem);
impl Rem<BigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn rem(self, other: BigDigit) -> BigUint {
let (_, r) = div_rem_digit(self, other);
From::from(r)
}
}
impl Rem<BigUint> for BigDigit {
type Output = BigUint;
#[inline]
fn rem(self, other: BigUint) -> BigUint {
match other.data.len() {
0 => panic!(),
1 => From::from(self % other.data[0]),
_ => From::from(self)
}
}
}
impl Rem<DoubleBigDigit> for BigUint {
type Output = BigUint;
#[inline]
fn rem(self, other: DoubleBigDigit) -> BigUint {
let (_, r) = self.div_rem(&From::from(other));
r
}
}
impl Rem<BigUint> for DoubleBigDigit {
type Output = BigUint;
#[inline]
fn rem(self, other: BigUint) -> BigUint {
match other.data.len() {
0 => panic!(),
1 => From::from(self % other.data[0] as u64),
2 => From::from(self % big_digit::to_doublebigdigit(other.data[0], other.data[1])),
_ => From::from(self),
}
} }
} }

View File

@ -88,6 +88,16 @@ use std::error::Error;
use std::num::ParseIntError; use std::num::ParseIntError;
use std::fmt; use std::fmt;
#[cfg(target_pointer_width = "32")]
type UsizePromotion = u32;
#[cfg(target_pointer_width = "64")]
type UsizePromotion = u64;
#[cfg(target_pointer_width = "32")]
type IsizePromotion = i32;
#[cfg(target_pointer_width = "64")]
type IsizePromotion = i64;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum ParseBigIntError { pub enum ParseBigIntError {
ParseInt(ParseIntError), ParseInt(ParseIntError),

View File

@ -105,6 +105,125 @@ macro_rules! forward_ref_ref_binop_commutative {
} }
} }
macro_rules! forward_scalar_val_val_binop_commutative {
(impl $imp:ident<$scalar:ty> for $res:ty, $method: ident) => {
impl $imp<$res> for $scalar {
type Output = $res;
#[inline]
fn $method(self, other: $res) -> $res {
$imp::$method(other, self)
}
}
}
}
macro_rules! forward_scalar_val_ref_binop {
(impl $imp:ident<$scalar:ty> for $res:ty, $method:ident) => {
impl<'a> $imp<&'a $scalar> for $res {
type Output = $res;
#[inline]
fn $method(self, other: &$scalar) -> $res {
$imp::$method(self, *other)
}
}
impl<'a> $imp<$res> for &'a $scalar {
type Output = $res;
#[inline]
fn $method(self, other: $res) -> $res {
$imp::$method(*self, other)
}
}
}
}
macro_rules! forward_scalar_ref_val_binop {
(impl $imp:ident<$scalar:ty> for $res:ty, $method:ident) => {
impl<'a> $imp<$scalar> for &'a $res {
type Output = $res;
#[inline]
fn $method(self, other: $scalar) -> $res {
$imp::$method(self.clone(), other)
}
}
impl<'a> $imp<&'a $res> for $scalar {
type Output = $res;
#[inline]
fn $method(self, other: &$res) -> $res {
$imp::$method(self, other.clone())
}
}
}
}
macro_rules! forward_scalar_ref_ref_binop {
(impl $imp:ident<$scalar:ty> for $res:ty, $method:ident) => {
impl<'a, 'b> $imp<&'b $scalar> for &'a $res {
type Output = $res;
#[inline]
fn $method(self, other: &$scalar) -> $res {
$imp::$method(self.clone(), *other)
}
}
impl<'a, 'b> $imp<&'a $res> for &'b $scalar {
type Output = $res;
#[inline]
fn $method(self, other: &$res) -> $res {
$imp::$method(*self, other.clone())
}
}
}
}
macro_rules! promote_scalars {
(impl $imp:ident<$promo:ty> for $res:ty, $method:ident, $( $scalar:ty ),*) => {
$(
forward_all_scalar_binop_to_val_val!(impl $imp<$scalar> for $res, $method);
impl $imp<$scalar> for $res {
type Output = $res;
#[inline]
fn $method(self, other: $scalar) -> $res {
$imp::$method(self, other as $promo)
}
}
impl $imp<$res> for $scalar {
type Output = $res;
#[inline]
fn $method(self, other: $res) -> $res {
$imp::$method(self as $promo, other)
}
}
)*
}
}
macro_rules! promote_unsigned_scalars {
(impl $imp:ident for $res:ty, $method:ident) => {
promote_scalars!(impl $imp<u32> for $res, $method, u8, u16);
promote_scalars!(impl $imp<UsizePromotion> for $res, $method, usize);
}
}
macro_rules! promote_signed_scalars {
(impl $imp:ident for $res:ty, $method:ident) => {
promote_scalars!(impl $imp<i32> for $res, $method, i8, i16);
promote_scalars!(impl $imp<IsizePromotion> for $res, $method, isize);
}
}
// Forward everything to ref-ref, when reusing storage is not helpful // Forward everything to ref-ref, when reusing storage is not helpful
macro_rules! forward_all_binop_to_ref_ref { macro_rules! forward_all_binop_to_ref_ref {
(impl $imp:ident for $res:ty, $method:ident) => { (impl $imp:ident for $res:ty, $method:ident) => {
@ -131,3 +250,25 @@ macro_rules! forward_all_binop_to_val_ref_commutative {
forward_ref_ref_binop_commutative!(impl $imp for $res, $method); forward_ref_ref_binop_commutative!(impl $imp for $res, $method);
}; };
} }
macro_rules! forward_all_scalar_binop_to_val_val {
(impl $imp:ident<$scalar:ty> for $res:ty, $method:ident) => {
forward_scalar_val_ref_binop!(impl $imp<$scalar> for $res, $method);
forward_scalar_ref_val_binop!(impl $imp<$scalar> for $res, $method);
forward_scalar_ref_ref_binop!(impl $imp<$scalar> for $res, $method);
}
}
macro_rules! forward_all_scalar_binop_to_val_val_commutative {
(impl $imp:ident<$scalar:ty> for $res:ty, $method:ident) => {
forward_scalar_val_val_binop_commutative!(impl $imp<$scalar> for $res, $method);
forward_all_scalar_binop_to_val_val!(impl $imp<$scalar> for $res, $method);
}
}
macro_rules! promote_all_scalars {
(impl $imp:ident for $res:ty, $method:ident) => {
promote_unsigned_scalars!(impl $imp for $res, $method);
promote_signed_scalars!(impl $imp for $res, $method);
}
}

View File

@ -24,6 +24,25 @@ macro_rules! assert_op {
}; };
} }
/// Assert that an op works for scalar left or right
macro_rules! assert_scalar_op {
(($($to:ident),*) $left:ident $op:tt $right:ident == $expected:expr) => {
$(
if let Some(left) = $left.$to() {
assert_op!(left $op $right == $expected);
}
if let Some(right) = $right.$to() {
assert_op!($left $op right == $expected);
}
)*
};
($left:ident $op:tt $right:ident == $expected:expr) => {
assert_scalar_op!((to_u8, to_u16, to_u32, to_u64, to_usize,
to_i8, to_i16, to_i32, to_i64, to_isize)
$left $op $right == $expected);
};
}
#[test] #[test]
fn test_from_biguint() { fn test_from_biguint() {
fn check(inp_s: Sign, inp_n: usize, ans_s: Sign, ans_n: usize) { fn check(inp_s: Sign, inp_n: usize, ans_s: Sign, ans_n: usize) {
@ -552,6 +571,26 @@ fn test_add() {
} }
} }
#[test]
fn test_scalar_add() {
for elm in SUM_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigInt::from_slice(Plus, a_vec);
let b = BigInt::from_slice(Plus, b_vec);
let c = BigInt::from_slice(Plus, c_vec);
let (na, nb, nc) = (-&a, -&b, -&c);
assert_scalar_op!(a + b == c);
assert_scalar_op!(b + a == c);
assert_scalar_op!(c + na == b);
assert_scalar_op!(c + nb == a);
assert_scalar_op!(a + nc == nb);
assert_scalar_op!(b + nc == na);
assert_scalar_op!(na + nb == nc);
assert_scalar_op!(a + na == Zero::zero());
}
}
#[test] #[test]
fn test_sub() { fn test_sub() {
for elm in SUM_TRIPLES.iter() { for elm in SUM_TRIPLES.iter() {
@ -572,6 +611,26 @@ fn test_sub() {
} }
} }
#[test]
fn test_scalar_sub() {
for elm in SUM_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigInt::from_slice(Plus, a_vec);
let b = BigInt::from_slice(Plus, b_vec);
let c = BigInt::from_slice(Plus, c_vec);
let (na, nb, nc) = (-&a, -&b, -&c);
assert_scalar_op!(c - a == b);
assert_scalar_op!(c - b == a);
assert_scalar_op!(nb - a == nc);
assert_scalar_op!(na - b == nc);
assert_scalar_op!(b - na == c);
assert_scalar_op!(a - nb == c);
assert_scalar_op!(nc - na == nb);
assert_scalar_op!(a - a == Zero::zero());
}
}
const M: u32 = ::std::u32::MAX; const M: u32 = ::std::u32::MAX;
static MUL_TRIPLES: &'static [(&'static [BigDigit], static MUL_TRIPLES: &'static [(&'static [BigDigit],
&'static [BigDigit], &'static [BigDigit],
@ -603,6 +662,7 @@ static DIV_REM_QUADRUPLES: &'static [(&'static [BigDigit],
&'static [BigDigit], &'static [BigDigit],
&'static [BigDigit], &'static [BigDigit],
&'static [BigDigit])] = &[(&[1], &[2], &[], &[1]), &'static [BigDigit])] = &[(&[1], &[2], &[], &[1]),
(&[3], &[2], &[1], &[1]),
(&[1, 1], &[2], &[M / 2 + 1], &[1]), (&[1, 1], &[2], &[M / 2 + 1], &[1]),
(&[1, 1, 1], &[2], &[M / 2 + 1, M / 2 + 1], &[1]), (&[1, 1, 1], &[2], &[M / 2 + 1, M / 2 + 1], &[1]),
(&[0, 1], &[N1], &[1], &[1]), (&[0, 1], &[N1], &[1], &[1]),
@ -637,6 +697,24 @@ fn test_mul() {
} }
} }
#[test]
fn test_scalar_mul() {
for elm in MUL_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigInt::from_slice(Plus, a_vec);
let b = BigInt::from_slice(Plus, b_vec);
let c = BigInt::from_slice(Plus, c_vec);
let (na, nb, nc) = (-&a, -&b, -&c);
assert_scalar_op!(a * b == c);
assert_scalar_op!(b * a == c);
assert_scalar_op!(na * nb == c);
assert_scalar_op!(na * b == nc);
assert_scalar_op!(nb * a == nc);
}
}
#[test] #[test]
fn test_div_mod_floor() { fn test_div_mod_floor() {
fn check_sub(a: &BigInt, b: &BigInt, ans_d: &BigInt, ans_m: &BigInt) { fn check_sub(a: &BigInt, b: &BigInt, ans_d: &BigInt, ans_m: &BigInt) {
@ -743,6 +821,65 @@ fn test_div_rem() {
} }
} }
#[test]
fn test_scalar_div_rem() {
fn check_sub(a: &BigInt, b: BigDigit, ans_q: &BigInt, ans_r: &BigInt) {
let (q, r) = (a / b, a % b);
if !r.is_zero() {
assert_eq!(r.sign, a.sign);
}
assert!(r.abs() <= From::from(b));
assert!(*a == b * &q + &r);
assert!(q == *ans_q);
assert!(r == *ans_r);
let (a, b, ans_q, ans_r) = (a.clone(), b.clone(), ans_q.clone(), ans_r.clone());
assert_op!(a / b == ans_q);
assert_op!(a % b == ans_r);
if b <= i32::max_value() as u32 {
let nb = -(b as i32);
assert_op!(a / nb == -ans_q.clone());
assert_op!(a % nb == ans_r);
}
}
fn check(a: &BigInt, b: BigDigit, q: &BigInt, r: &BigInt) {
check_sub(a, b, q, r);
check_sub(&a.neg(), b, &q.neg(), &r.neg());
}
for elm in MUL_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigInt::from_slice(Plus, a_vec);
let b = BigInt::from_slice(Plus, b_vec);
let c = BigInt::from_slice(Plus, c_vec);
if a_vec.len() == 1 && a_vec[0] != 0 {
let a = a_vec[0];
check(&c, a, &b, &Zero::zero());
}
if b_vec.len() == 1 && b_vec[0] != 0 {
let b = b_vec[0];
check(&c, b, &a, &Zero::zero());
}
}
for elm in DIV_REM_QUADRUPLES.iter() {
let (a_vec, b_vec, c_vec, d_vec) = *elm;
let a = BigInt::from_slice(Plus, a_vec);
let c = BigInt::from_slice(Plus, c_vec);
let d = BigInt::from_slice(Plus, d_vec);
if b_vec.len() == 1 && b_vec[0] != 0 {
let b = b_vec[0];
check(&a, b, &c, &d);
}
}
}
#[test] #[test]
fn test_checked_add() { fn test_checked_add() {
for elm in SUM_TRIPLES.iter() { for elm in SUM_TRIPLES.iter() {

View File

@ -25,6 +25,24 @@ macro_rules! assert_op {
}; };
} }
/// Assert that an op works for scalar left or right
macro_rules! assert_scalar_op {
(($($to:ident),*) $left:ident $op:tt $right:ident == $expected:expr) => {
$(
if let Some(left) = $left.$to() {
assert_op!(left $op $right == $expected);
}
if let Some(right) = $right.$to() {
assert_op!($left $op right == $expected);
}
)*
};
($left:ident $op:tt $right:ident == $expected:expr) => {
assert_scalar_op!((to_u8, to_u16, to_u32, to_u64, to_usize)
$left $op $right == $expected);
};
}
#[test] #[test]
fn test_from_slice() { fn test_from_slice() {
fn check(slice: &[BigDigit], data: &[BigDigit]) { fn check(slice: &[BigDigit], data: &[BigDigit]) {
@ -690,6 +708,19 @@ fn test_add() {
} }
} }
#[test]
fn test_scalar_add() {
for elm in SUM_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigUint::from_slice(a_vec);
let b = BigUint::from_slice(b_vec);
let c = BigUint::from_slice(c_vec);
assert_scalar_op!(a + b == c);
assert_scalar_op!(b + a == c);
}
}
#[test] #[test]
fn test_sub() { fn test_sub() {
for elm in SUM_TRIPLES.iter() { for elm in SUM_TRIPLES.iter() {
@ -703,6 +734,19 @@ fn test_sub() {
} }
} }
#[test]
fn test_scalar_sub() {
for elm in SUM_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigUint::from_slice(a_vec);
let b = BigUint::from_slice(b_vec);
let c = BigUint::from_slice(c_vec);
assert_scalar_op!(c - a == b);
assert_scalar_op!(c - b == a);
}
}
#[test] #[test]
#[should_panic] #[should_panic]
fn test_sub_fail_on_underflow() { fn test_sub_fail_on_underflow() {
@ -741,6 +785,7 @@ const DIV_REM_QUADRUPLES: &'static [(&'static [BigDigit],
&'static [BigDigit], &'static [BigDigit],
&'static [BigDigit], &'static [BigDigit],
&'static [BigDigit])] = &[(&[1], &[2], &[], &[1]), &'static [BigDigit])] = &[(&[1], &[2], &[], &[1]),
(&[3], &[2], &[1], &[1]),
(&[1, 1], &[2], &[M / 2 + 1], &[1]), (&[1, 1], &[2], &[M / 2 + 1], &[1]),
(&[1, 1, 1], &[2], &[M / 2 + 1, M / 2 + 1], &[1]), (&[1, 1, 1], &[2], &[M / 2 + 1, M / 2 + 1], &[1]),
(&[0, 1], &[N1], &[1], &[1]), (&[0, 1], &[N1], &[1], &[1]),
@ -770,6 +815,19 @@ fn test_mul() {
} }
} }
#[test]
fn test_scalar_mul() {
for elm in MUL_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigUint::from_slice(a_vec);
let b = BigUint::from_slice(b_vec);
let c = BigUint::from_slice(c_vec);
assert_scalar_op!(a * b == c);
assert_scalar_op!(b * a == c);
}
}
#[test] #[test]
fn test_div_rem() { fn test_div_rem() {
for elm in MUL_TRIPLES.iter() { for elm in MUL_TRIPLES.iter() {
@ -805,6 +863,39 @@ fn test_div_rem() {
} }
} }
#[test]
fn test_scalar_div_rem() {
for elm in MUL_TRIPLES.iter() {
let (a_vec, b_vec, c_vec) = *elm;
let a = BigUint::from_slice(a_vec);
let b = BigUint::from_slice(b_vec);
let c = BigUint::from_slice(c_vec);
if !a.is_zero() {
assert_scalar_op!(c / a == b);
assert_scalar_op!(c % a == Zero::zero());
}
if !b.is_zero() {
assert_scalar_op!(c / b == a);
assert_scalar_op!(c % b == Zero::zero());
}
}
for elm in DIV_REM_QUADRUPLES.iter() {
let (a_vec, b_vec, c_vec, d_vec) = *elm;
let a = BigUint::from_slice(a_vec);
let b = BigUint::from_slice(b_vec);
let c = BigUint::from_slice(c_vec);
let d = BigUint::from_slice(d_vec);
if !b.is_zero() {
assert_scalar_op!(a / b == c);
assert_scalar_op!(a % b == d);
}
}
}
#[test] #[test]
fn test_checked_add() { fn test_checked_add() {
for elm in SUM_TRIPLES.iter() { for elm in SUM_TRIPLES.iter() {