Repository: rust-num/num-bigint (Rust)

Issue:
BigInt AddAssign<isize> with negative addend is off by 2^32 or 2^64
```rust
let mut x: BigInt = 0.into();
let y: isize = -1;
x += y;
assert_eq!(BigInt::from(-1), x); // fails
// x is actually 2^64-1
```
`x + y` gives the proper result (`-1`), and if `y` is `i32` or `i64` it works as expected, but `isize` somehow breaks things. When compiled for 64-bit the result is off by 2^64, and for 32-bit it is off by 2^32.


Discussion:
Thank you for the fix in #133 -- I've also released this as num-bigint 0.2.6.
Thank you very much!



Patch:
diff --git a/src/macros.rs b/src/macros.rs
index 34a290f7..a03cb676 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -340,7 +340,7 @@ macro_rules! promote_signed_scalars {
 macro_rules! promote_signed_scalars_assign {
     (impl $imp:ident for $res:ty, $method:ident) => {
         promote_scalars_assign!(impl $imp<i32> for $res, $method, i8, i16);
-        promote_scalars_assign!(impl $imp<UsizePromotion> for $res, $method, isize);
+        promote_scalars_assign!(impl $imp<IsizePromotion> for $res, $method, isize);
     }
 }
 


Test patch:
diff --git a/tests/bigint_scalar.rs b/tests/bigint_scalar.rs
index 6d79dfb8..4581253b 100644
--- a/tests/bigint_scalar.rs
+++ b/tests/bigint_scalar.rs
@@ -15,6 +15,7 @@ fn test_scalar_add() {
     fn check(x: &BigInt, y: &BigInt, z: &BigInt) {
         let (x, y, z) = (x.clone(), y.clone(), z.clone());
         assert_signed_scalar_op!(x + y == z);
+        assert_signed_scalar_assign_op!(x += y == z);
     }
 
     for elm in SUM_TRIPLES.iter() {
@@ -40,6 +41,7 @@ fn test_scalar_sub() {
     fn check(x: &BigInt, y: &BigInt, z: &BigInt) {
         let (x, y, z) = (x.clone(), y.clone(), z.clone());
         assert_signed_scalar_op!(x - y == z);
+        assert_signed_scalar_assign_op!(x -= y == z);
     }
 
     for elm in SUM_TRIPLES.iter() {
@@ -65,6 +67,7 @@ fn test_scalar_mul() {
     fn check(x: &BigInt, y: &BigInt, z: &BigInt) {
         let (x, y, z) = (x.clone(), y.clone(), z.clone());
         assert_signed_scalar_op!(x * y == z);
+        assert_signed_scalar_assign_op!(x *= y == z);
     }
 
     for elm in MUL_TRIPLES.iter() {
@@ -99,10 +102,14 @@ fn test_scalar_div_rem() {
         let (a, ans_q, ans_r) = (a.clone(), ans_q.clone(), ans_r.clone());
         assert_signed_scalar_op!(a / b == ans_q);
         assert_signed_scalar_op!(a % b == ans_r);
+        assert_signed_scalar_assign_op!(a /= b == ans_q);
+        assert_signed_scalar_assign_op!(a %= b == ans_r);
 
         let nb = -b;
         assert_signed_scalar_op!(a / nb == -ans_q.clone());
         assert_signed_scalar_op!(a % nb == ans_r);
+        assert_signed_scalar_assign_op!(a /= nb == -ans_q.clone());
+        assert_signed_scalar_assign_op!(a %= nb == ans_r);
     }
 
     fn check(a: &BigInt, b: u32, q: &BigInt, r: &BigInt) {
diff --git a/tests/biguint_scalar.rs b/tests/biguint_scalar.rs
index 20bb77c1..eff51f97 100644
--- a/tests/biguint_scalar.rs
+++ b/tests/biguint_scalar.rs
@@ -12,6 +12,7 @@ fn test_scalar_add() {
     fn check(x: &BigUint, y: &BigUint, z: &BigUint) {
         let (x, y, z) = (x.clone(), y.clone(), z.clone());
         assert_unsigned_scalar_op!(x + y == z);
+        assert_unsigned_scalar_assign_op!(x += y == z);
     }
 
     for elm in SUM_TRIPLES.iter() {
@@ -30,6 +31,7 @@ fn test_scalar_sub() {
     fn check(x: &BigUint, y: &BigUint, z: &BigUint) {
         let (x, y, z) = (x.clone(), y.clone(), z.clone());
         assert_unsigned_scalar_op!(x - y == z);
+        assert_unsigned_scalar_assign_op!(x -= y == z);
     }
 
     for elm in SUM_TRIPLES.iter() {
@@ -48,6 +50,7 @@ fn test_scalar_mul() {
     fn check(x: &BigUint, y: &BigUint, z: &BigUint) {
         let (x, y, z) = (x.clone(), y.clone(), z.clone());
         assert_unsigned_scalar_op!(x * y == z);
+        assert_unsigned_scalar_assign_op!(x *= y == z);
     }
 
     for elm in MUL_TRIPLES.iter() {
@@ -73,6 +76,8 @@ fn test_scalar_div_rem() {
         let (x, y, z, r) = (x.clone(), y.clone(), z.clone(), r.clone());
         assert_unsigned_scalar_op!(x / y == z);
         assert_unsigned_scalar_op!(x % y == r);
+        assert_unsigned_scalar_assign_op!(x /= y == z);
+        assert_unsigned_scalar_assign_op!(x %= y == r);
     }
 
     for elm in MUL_TRIPLES.iter() {
@@ -101,6 +106,8 @@ fn test_scalar_div_rem() {
             check(&a, &b, &c, &d);
             assert_unsigned_scalar_op!(a / b == c);
             assert_unsigned_scalar_op!(a % b == d);
+            assert_unsigned_scalar_assign_op!(a /= b == c);
+            assert_unsigned_scalar_assign_op!(a %= b == d);
         }
     }
 }
diff --git a/tests/macros/mod.rs b/tests/macros/mod.rs
index 367af0aa..b14cd577 100644
--- a/tests/macros/mod.rs
+++ b/tests/macros/mod.rs
@@ -49,3 +49,30 @@ macro_rules! assert_signed_scalar_op {
                           $left $op $right == $expected);
     };
 }
+
+/// Assert that an op works for scalar right
+macro_rules! assert_scalar_assign_op {
+    (($($to:ident),*) $left:ident $op:tt $right:ident == $expected:expr) => {
+        $(
+            if let Some(right) = $right.$to() {
+                let mut left = $left.clone();
+                assert_eq!({ left $op right; left}, $expected);
+            }
+        )*
+    };
+}
+
+macro_rules! assert_unsigned_scalar_assign_op {
+    ($left:ident $op:tt $right:ident == $expected:expr) => {
+        assert_scalar_assign_op!((to_u8, to_u16, to_u32, to_u64, to_usize, to_u128)
+                                 $left $op $right == $expected);
+    };
+}
+
+macro_rules! assert_signed_scalar_assign_op {
+    ($left:ident $op:tt $right:ident == $expected:expr) => {
+        assert_scalar_assign_op!((to_u8, to_u16, to_u32, to_u64, to_usize, to_u128,
+                                  to_i8, to_i16, to_i32, to_i64, to_isize, to_i128)
+                                 $left $op $right == $expected);
+    };
+}