330: Implement *Assign for BigUint r=cuviper a=Darksonn

Not only does this change increase convenience of use, it also allows adding a `&BigUint` to a `&mut BigUint` without allocating (if not necessary) or tricks such as:

    fn add(a: &mut BigUint, b: &BigUint) {
        let aa = mem::replace(a, BigUint::from_slice(&[])); // BigUint::from_slice(&[]) does not allocate
        *a = aa + b;
    }

With this change:

    fn add(a: &mut BigUint, b: &BigUint) {
        *a += b;
    }

It would make sense to add the same functionality to `BigInt`, but it uses some macros to handle the signs, and I'm not sure how to change the macros in order to perform this change.
This commit is contained in:
bors[bot] 2017-09-20 01:11:28 +00:00
commit 8646be5a95
3 changed files with 284 additions and 49 deletions

View File

@ -1,7 +1,9 @@
use std::borrow::Cow;
use std::default::Default;
use std::iter::repeat;
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub};
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub,
AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign,
MulAssign, RemAssign, ShlAssign, ShrAssign, SubAssign};
use std::str::{self, FromStr};
use std::fmt;
use std::cmp;
@ -268,54 +270,74 @@ impl Num for BigUint {
}
forward_all_binop_to_val_ref_commutative!(impl BitAnd for BigUint, bitand);
forward_val_assign!(impl BitAndAssign for BigUint, bitand_assign);
impl<'a> BitAnd<&'a BigUint> for BigUint {
type Output = BigUint;
#[inline]
fn bitand(self, other: &BigUint) -> BigUint {
let mut data = self.data;
for (ai, &bi) in data.iter_mut().zip(other.data.iter()) {
fn bitand(mut self, other: &BigUint) -> BigUint {
self &= other;
self
}
}
impl<'a> BitAndAssign<&'a BigUint> for BigUint {
#[inline]
fn bitand_assign(&mut self, other: &BigUint) {
for (ai, &bi) in self.data.iter_mut().zip(other.data.iter()) {
*ai &= bi;
}
data.truncate(other.data.len());
BigUint::new(data)
self.data.truncate(other.data.len());
self.normalize();
}
}
forward_all_binop_to_val_ref_commutative!(impl BitOr for BigUint, bitor);
forward_val_assign!(impl BitOrAssign for BigUint, bitor_assign);
impl<'a> BitOr<&'a BigUint> for BigUint {
type Output = BigUint;
fn bitor(self, other: &BigUint) -> BigUint {
let mut data = self.data;
for (ai, &bi) in data.iter_mut().zip(other.data.iter()) {
fn bitor(mut self, other: &BigUint) -> BigUint {
self |= other;
self
}
}
impl<'a> BitOrAssign<&'a BigUint> for BigUint {
#[inline]
fn bitor_assign(&mut self, other: &BigUint) {
for (ai, &bi) in self.data.iter_mut().zip(other.data.iter()) {
*ai |= bi;
}
if other.data.len() > data.len() {
let extra = &other.data[data.len()..];
data.extend(extra.iter().cloned());
if other.data.len() > self.data.len() {
let extra = &other.data[self.data.len()..];
self.data.extend(extra.iter().cloned());
}
BigUint::new(data)
}
}
forward_all_binop_to_val_ref_commutative!(impl BitXor for BigUint, bitxor);
forward_val_assign!(impl BitXorAssign for BigUint, bitxor_assign);
impl<'a> BitXor<&'a BigUint> for BigUint {
type Output = BigUint;
fn bitxor(self, other: &BigUint) -> BigUint {
let mut data = self.data;
for (ai, &bi) in data.iter_mut().zip(other.data.iter()) {
fn bitxor(mut self, other: &BigUint) -> BigUint {
self ^= other;
self
}
}
impl<'a> BitXorAssign<&'a BigUint> for BigUint {
#[inline]
fn bitxor_assign(&mut self, other: &BigUint) {
for (ai, &bi) in self.data.iter_mut().zip(other.data.iter()) {
*ai ^= bi;
}
if other.data.len() > data.len() {
let extra = &other.data[data.len()..];
data.extend(extra.iter().cloned());
if other.data.len() > self.data.len() {
let extra = &other.data[self.data.len()..];
self.data.extend(extra.iter().cloned());
}
BigUint::new(data)
self.normalize();
}
}
@ -327,7 +349,6 @@ impl Shl<usize> for BigUint {
biguint_shl(Cow::Owned(self), rhs)
}
}
impl<'a> Shl<usize> for &'a BigUint {
type Output = BigUint;
@ -337,6 +358,13 @@ impl<'a> Shl<usize> for &'a BigUint {
}
}
impl ShlAssign<usize> for BigUint {
#[inline]
fn shl_assign(&mut self, rhs: usize) {
*self = biguint_shl(Cow::Borrowed(&*self), rhs);
}
}
impl Shr<usize> for BigUint {
type Output = BigUint;
@ -345,7 +373,6 @@ impl Shr<usize> for BigUint {
biguint_shr(Cow::Owned(self), rhs)
}
}
impl<'a> Shr<usize> for &'a BigUint {
type Output = BigUint;
@ -355,6 +382,13 @@ impl<'a> Shr<usize> for &'a BigUint {
}
}
impl ShrAssign<usize> for BigUint {
#[inline]
fn shr_assign(&mut self, rhs: usize) {
*self = biguint_shr(Cow::Borrowed(&*self), rhs);
}
}
impl Zero for BigUint {
#[inline]
fn zero() -> BigUint {
@ -377,11 +411,19 @@ impl One for BigUint {
impl Unsigned for BigUint {}
forward_all_binop_to_val_ref_commutative!(impl Add for BigUint, add);
forward_val_assign!(impl AddAssign for BigUint, add_assign);
impl<'a> Add<&'a BigUint> for BigUint {
type Output = BigUint;
fn add(mut self, other: &BigUint) -> BigUint {
self += other;
self
}
}
impl<'a> AddAssign<&'a BigUint> for BigUint {
#[inline]
fn add_assign(&mut self, other: &BigUint) {
if self.data.len() < other.data.len() {
let extra = other.data.len() - self.data.len();
self.data.extend(repeat(0).take(extra));
@ -391,12 +433,11 @@ impl<'a> Add<&'a BigUint> for BigUint {
if carry != 0 {
self.data.push(carry);
}
self
}
}
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<BigDigit> for BigUint, add);
forward_all_scalar_binop_to_val_val_commutative!(impl Add<DoubleBigDigit> for BigUint, add);
@ -405,6 +446,13 @@ impl Add<BigDigit> for BigUint {
#[inline]
fn add(mut self, other: BigDigit) -> BigUint {
self += other;
self
}
}
impl AddAssign<BigDigit> for BigUint {
#[inline]
fn add_assign(&mut self, other: BigDigit) {
if other != 0 {
if self.data.len() == 0 {
self.data.push(0);
@ -415,7 +463,6 @@ impl Add<BigDigit> for BigUint {
self.data.push(carry);
}
}
self
}
}
@ -424,9 +471,16 @@ impl Add<DoubleBigDigit> for BigUint {
#[inline]
fn add(mut self, other: DoubleBigDigit) -> BigUint {
self += other;
self
}
}
impl AddAssign<DoubleBigDigit> for BigUint {
#[inline]
fn add_assign(&mut self, other: DoubleBigDigit) {
let (hi, lo) = big_digit::from_doublebigdigit(other);
if hi == 0 {
self + lo
*self += lo;
} else {
while self.data.len() < 2 {
self.data.push(0);
@ -436,20 +490,26 @@ impl Add<DoubleBigDigit> for BigUint {
if carry != 0 {
self.data.push(carry);
}
self
}
}
}
forward_val_val_binop!(impl Sub for BigUint, sub);
forward_ref_ref_binop!(impl Sub for BigUint, sub);
forward_val_assign!(impl SubAssign for BigUint, sub_assign);
impl<'a> Sub<&'a BigUint> for BigUint {
type Output = BigUint;
fn sub(mut self, other: &BigUint) -> BigUint {
self -= other;
self
}
}
impl<'a> SubAssign<&'a BigUint> for BigUint {
fn sub_assign(&mut self, other: &'a BigUint) {
sub2(&mut self.data[..], &other.data[..]);
self.normalized()
self.normalize();
}
}
@ -468,6 +528,7 @@ impl<'a> Sub<BigUint> for &'a BigUint {
}
promote_unsigned_scalars!(impl Sub for BigUint, sub);
promote_unsigned_scalars_assign!(impl SubAssign for BigUint, sub_assign);
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);
@ -476,8 +537,14 @@ impl Sub<BigDigit> for BigUint {
#[inline]
fn sub(mut self, other: BigDigit) -> BigUint {
self -= other;
self
}
}
impl SubAssign<BigDigit> for BigUint {
fn sub_assign(&mut self, other: BigDigit) {
sub2(&mut self.data[..], &[other]);
self.normalized()
self.normalize();
}
}
@ -500,9 +567,15 @@ impl Sub<DoubleBigDigit> for BigUint {
#[inline]
fn sub(mut self, other: DoubleBigDigit) -> BigUint {
self -= other;
self
}
}
impl SubAssign<DoubleBigDigit> for BigUint {
fn sub_assign(&mut self, other: DoubleBigDigit) {
let (hi, lo) = big_digit::from_doublebigdigit(other);
sub2(&mut self.data[..], &[lo, hi]);
self.normalized()
self.normalize();
}
}
@ -522,6 +595,7 @@ impl Sub<BigUint> for DoubleBigDigit {
}
forward_all_binop_to_ref_ref!(impl Mul for BigUint, mul);
forward_val_assign!(impl MulAssign for BigUint, mul_assign);
impl<'a, 'b> Mul<&'b BigUint> for &'a BigUint {
type Output = BigUint;
@ -531,8 +605,15 @@ impl<'a, 'b> Mul<&'b BigUint> for &'a BigUint {
mul3(&self.data[..], &other.data[..])
}
}
impl<'a> MulAssign<&'a BigUint> for BigUint {
#[inline]
fn mul_assign(&mut self, other: &'a BigUint) {
*self = &*self * other
}
}
promote_unsigned_scalars!(impl Mul for BigUint, mul);
promote_unsigned_scalars_assign!(impl MulAssign for BigUint, mul_assign);
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);
@ -541,6 +622,13 @@ impl Mul<BigDigit> for BigUint {
#[inline]
fn mul(mut self, other: BigDigit) -> BigUint {
self *= other;
self
}
}
impl MulAssign<BigDigit> for BigUint {
#[inline]
fn mul_assign(&mut self, other: BigDigit) {
if other == 0 {
self.data.clear();
} else {
@ -549,7 +637,6 @@ impl Mul<BigDigit> for BigUint {
self.data.push(carry);
}
}
self
}
}
@ -558,19 +645,26 @@ impl Mul<DoubleBigDigit> for BigUint {
#[inline]
fn mul(mut self, other: DoubleBigDigit) -> BigUint {
self *= other;
self
}
}
impl MulAssign<DoubleBigDigit> for BigUint {
#[inline]
fn mul_assign(&mut self, other: DoubleBigDigit) {
if other == 0 {
self.data.clear();
self
} else if other <= BigDigit::max_value() as DoubleBigDigit {
self * other as BigDigit
*self *= other as BigDigit
} else {
let (hi, lo) = big_digit::from_doublebigdigit(other);
mul3(&self.data[..], &[lo, hi])
*self = mul3(&self.data[..], &[lo, hi])
}
}
}
forward_all_binop_to_ref_ref!(impl Div for BigUint, div);
forward_val_assign!(impl DivAssign for BigUint, div_assign);
impl<'a, 'b> Div<&'b BigUint> for &'a BigUint {
type Output = BigUint;
@ -581,8 +675,15 @@ impl<'a, 'b> Div<&'b BigUint> for &'a BigUint {
q
}
}
impl<'a> DivAssign<&'a BigUint> for BigUint {
#[inline]
fn div_assign(&mut self, other: &'a BigUint) {
*self = &*self / other;
}
}
promote_unsigned_scalars!(impl Div for BigUint, div);
promote_unsigned_scalars_assign!(impl DivAssign for BigUint, div_assign);
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);
@ -595,6 +696,12 @@ impl Div<BigDigit> for BigUint {
q
}
}
impl DivAssign<BigDigit> for BigUint {
#[inline]
fn div_assign(&mut self, other: BigDigit) {
*self = &*self / other;
}
}
impl Div<BigUint> for BigDigit {
type Output = BigUint;
@ -618,6 +725,12 @@ impl Div<DoubleBigDigit> for BigUint {
q
}
}
impl DivAssign<DoubleBigDigit> for BigUint {
#[inline]
fn div_assign(&mut self, other: DoubleBigDigit) {
*self = &*self / other;
}
}
impl Div<BigUint> for DoubleBigDigit {
type Output = BigUint;
@ -634,6 +747,7 @@ impl Div<BigUint> for DoubleBigDigit {
}
forward_all_binop_to_ref_ref!(impl Rem for BigUint, rem);
forward_val_assign!(impl RemAssign for BigUint, rem_assign);
impl<'a, 'b> Rem<&'b BigUint> for &'a BigUint {
type Output = BigUint;
@ -644,8 +758,15 @@ impl<'a, 'b> Rem<&'b BigUint> for &'a BigUint {
r
}
}
impl<'a> RemAssign<&'a BigUint> for BigUint {
#[inline]
fn rem_assign(&mut self, other: &BigUint) {
*self = &*self % other;
}
}
promote_unsigned_scalars!(impl Rem for BigUint, rem);
promote_unsigned_scalars_assign!(impl RemAssign for BigUint, rem_assign);
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);
@ -658,19 +779,49 @@ impl Rem<BigDigit> for BigUint {
From::from(r)
}
}
impl RemAssign<BigDigit> for BigUint {
#[inline]
fn rem_assign(&mut self, other: BigDigit) {
*self = &*self % other;
}
}
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)
fn rem(mut self, other: BigUint) -> BigUint {
self %= other;
From::from(self)
}
}
macro_rules! impl_rem_assign_scalar {
($scalar:ty, $to_scalar:ident) => {
forward_val_assign_scalar!(impl RemAssign for BigUint, $scalar, rem_assign);
impl<'a> RemAssign<&'a BigUint> for $scalar {
#[inline]
fn rem_assign(&mut self, other: &BigUint) {
*self = match other.$to_scalar() {
None => *self,
Some(0) => panic!(),
Some(v) => *self % v
};
}
}
}
}
// we can scalar %= BigUint for any scalar, including signed types
impl_rem_assign_scalar!(usize, to_usize);
impl_rem_assign_scalar!(u64, to_u64);
impl_rem_assign_scalar!(u32, to_u32);
impl_rem_assign_scalar!(u16, to_u16);
impl_rem_assign_scalar!(u8, to_u8);
impl_rem_assign_scalar!(isize, to_isize);
impl_rem_assign_scalar!(i64, to_i64);
impl_rem_assign_scalar!(i32, to_i32);
impl_rem_assign_scalar!(i16, to_i16);
impl_rem_assign_scalar!(i8, to_i8);
impl Rem<DoubleBigDigit> for BigUint {
type Output = BigUint;
@ -681,18 +832,20 @@ impl Rem<DoubleBigDigit> for BigUint {
r
}
}
impl RemAssign<DoubleBigDigit> for BigUint {
#[inline]
fn rem_assign(&mut self, other: DoubleBigDigit) {
*self = &*self % other;
}
}
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),
}
fn rem(mut self, other: BigUint) -> BigUint {
self %= other;
From::from(self)
}
}

View File

@ -105,6 +105,27 @@ macro_rules! forward_ref_ref_binop_commutative {
}
}
macro_rules! forward_val_assign {
(impl $imp:ident for $res:ty, $method:ident) => {
impl $imp<$res> for $res {
#[inline]
fn $method(&mut self, other: $res) {
self.$method(&other);
}
}
}
}
macro_rules! forward_val_assign_scalar {
(impl $imp:ident for $res:ty, $scalar:ty, $method:ident) => {
impl $imp<$res> for $scalar {
#[inline]
fn $method(&mut self, other: $res) {
self.$method(&other);
}
}
}
}
macro_rules! forward_scalar_val_val_binop_commutative {
(impl $imp:ident<$scalar:ty> for $res:ty, $method: ident) => {
impl $imp<$res> for $scalar {
@ -209,6 +230,18 @@ macro_rules! promote_scalars {
)*
}
}
macro_rules! promote_scalars_assign {
(impl $imp:ident<$promo:ty> for $res:ty, $method:ident, $( $scalar:ty ),*) => {
$(
impl $imp<$scalar> for $res {
#[inline]
fn $method(&mut self, other: $scalar) {
self.$method(other as $promo);
}
}
)*
}
}
macro_rules! promote_unsigned_scalars {
(impl $imp:ident for $res:ty, $method:ident) => {
@ -217,6 +250,13 @@ macro_rules! promote_unsigned_scalars {
}
}
macro_rules! promote_unsigned_scalars_assign {
(impl $imp:ident for $res:ty, $method:ident) => {
promote_scalars_assign!(impl $imp<u32> for $res, $method, u8, u16);
promote_scalars_assign!(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);
@ -271,4 +311,4 @@ macro_rules! promote_all_scalars {
promote_unsigned_scalars!(impl $imp for $res, $method);
promote_signed_scalars!(impl $imp for $res, $method);
}
}
}

View File

@ -24,6 +24,18 @@ macro_rules! assert_op {
assert_eq!($left.clone() $op $right.clone(), $expected);
};
}
/// Assert that an assign-op works for all val/ref combinations
macro_rules! assert_assign_op {
($left:ident $op:tt $right:ident == $expected:expr) => {
{
let mut tmp12384 = $left.clone();
assert_eq!({ tmp12384 $op &$right; tmp12384}, $expected);
let mut tmp12384 = $left.clone();
assert_eq!({ tmp12384 $op $right.clone(); tmp12384}, $expected);
}
};
}
/// Assert that an op works for scalar left or right
macro_rules! assert_scalar_op {
@ -191,6 +203,8 @@ const BIT_TESTS: &'static [(&'static [BigDigit],
&'static [BigDigit],
&'static [BigDigit])] = &[// LEFT RIGHT AND OR XOR
(&[], &[], &[], &[], &[]),
(&[1, 0, 1], &[1, 1], &[1], &[1, 1, 1], &[0, 1, 1]),
(&[1, 0, 1], &[0, 1, 1], &[0, 0, 1], &[1, 1, 1], &[1, 1]),
(&[268, 482, 17],
&[964, 54],
&[260, 34],
@ -207,6 +221,8 @@ fn test_bitand() {
assert_op!(a & b == c);
assert_op!(b & a == c);
assert_assign_op!(a &= b == c);
assert_assign_op!(b &= a == c);
}
}
@ -220,6 +236,8 @@ fn test_bitor() {
assert_op!(a | b == c);
assert_op!(b | a == c);
assert_assign_op!(a |= b == c);
assert_assign_op!(b |= a == c);
}
}
@ -237,6 +255,12 @@ fn test_bitxor() {
assert_op!(c ^ a == b);
assert_op!(b ^ c == a);
assert_op!(c ^ b == a);
assert_assign_op!(a ^= b == c);
assert_assign_op!(b ^= a == c);
assert_assign_op!(a ^= c == b);
assert_assign_op!(c ^= a == b);
assert_assign_op!(b ^= c == a);
assert_assign_op!(c ^= b == a);
}
}
@ -244,8 +268,11 @@ fn test_bitxor() {
fn test_shl() {
fn check(s: &str, shift: usize, ans: &str) {
let opt_biguint = BigUint::from_str_radix(s, 16).ok();
let bu = (opt_biguint.unwrap() << shift).to_str_radix(16);
let mut bu_assign = opt_biguint.unwrap();
let bu = (bu_assign.clone() << shift).to_str_radix(16);
assert_eq!(bu, ans);
bu_assign <<= shift;
assert_eq!(bu_assign.to_str_radix(16), ans);
}
check("0", 3, "0");
@ -366,8 +393,11 @@ fn test_shl() {
fn test_shr() {
fn check(s: &str, shift: usize, ans: &str) {
let opt_biguint = BigUint::from_str_radix(s, 16).ok();
let bu = (opt_biguint.unwrap() >> shift).to_str_radix(16);
let mut bu_assign = opt_biguint.unwrap();
let bu = (bu_assign.clone() >> shift).to_str_radix(16);
assert_eq!(bu, ans);
bu_assign >>= shift;
assert_eq!(bu_assign.to_str_radix(16), ans);
}
check("0", 3, "0");
@ -720,6 +750,8 @@ fn test_add() {
assert_op!(a + b == c);
assert_op!(b + a == c);
assert_assign_op!(a += b == c);
assert_assign_op!(b += a == c);
}
}
@ -746,6 +778,8 @@ fn test_sub() {
assert_op!(c - a == b);
assert_op!(c - b == a);
assert_assign_op!(c -= a == b);
assert_assign_op!(c -= b == a);
}
}
@ -816,6 +850,8 @@ fn test_mul() {
assert_op!(a * b == c);
assert_op!(b * a == c);
assert_assign_op!(a *= b == c);
assert_assign_op!(b *= a == c);
}
for elm in DIV_REM_QUADRUPLES.iter() {
@ -854,11 +890,15 @@ fn test_div_rem() {
if !a.is_zero() {
assert_op!(c / a == b);
assert_op!(c % a == Zero::zero());
assert_assign_op!(c /= a == b);
assert_assign_op!(c %= a == Zero::zero());
assert_eq!(c.div_rem(&a), (b.clone(), Zero::zero()));
}
if !b.is_zero() {
assert_op!(c / b == a);
assert_op!(c % b == Zero::zero());
assert_assign_op!(c /= b == a);
assert_assign_op!(c %= b == Zero::zero());
assert_eq!(c.div_rem(&b), (a.clone(), Zero::zero()));
}
}
@ -873,6 +913,8 @@ fn test_div_rem() {
if !b.is_zero() {
assert_op!(a / b == c);
assert_op!(a % b == d);
assert_assign_op!(a /= b == c);
assert_assign_op!(a %= b == d);
assert!(a.div_rem(&b) == (c, d));
}
}