authorgravatar for tinusgraglin@gmail.comtinusgraglin <tinusgraglin@gmail.com> 2024-03-11 00:44:32+08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-11 16:52:03-07:00
log26e895e3dc4ff1b7ac235414a356840bccb4fb1e
tree8ce8b0d490bc5f73939472d84984ba705b3c7543
parentf8f43ca3e1042b6a1848ec8f2d5c4797f41683c2

math.big.int: fix incorrect `bitAnd` behavior


2 files changed, 36 insertions(+), 16 deletions(-)

lib/std/math/big/int.zig+23-16
...@@ -1236,7 +1236,9 @@ pub const Mutable = struct {...@@ -1236,7 +1236,9 @@ pub const Mutable = struct {
1236 /// r may alias with a or b.1236 /// r may alias with a or b.
1237 ///1237 ///
1238 /// Asserts that r has enough limbs to store the result.1238 /// Asserts that r has enough limbs to store the result.
1239 /// If a or b is positive, the upper bound is `@min(a.limbs.len, b.limbs.len)`.1239 /// If only a is positive, the upper bound is `a.limbs.len`.
1240 /// If only b is positive, the upper bound is `b.limbs.len`.
1241 /// If a and b are positive, the upper bound is `@min(a.limbs.len, b.limbs.len)`.
1240 /// If a and b are negative, the upper bound is `@max(a.limbs.len, b.limbs.len) + 1`.1242 /// If a and b are negative, the upper bound is `@max(a.limbs.len, b.limbs.len) + 1`.
1241 pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {1243 pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {
1242 // Trivial cases, llsignedand does not support zero.1244 // Trivial cases, llsignedand does not support zero.
...@@ -1250,10 +1252,10 @@ pub const Mutable = struct {...@@ -1250,10 +1252,10 @@ pub const Mutable = struct {
12501252
1251 if (a.limbs.len >= b.limbs.len) {1253 if (a.limbs.len >= b.limbs.len) {
1252 r.positive = llsignedand(r.limbs, a.limbs, a.positive, b.limbs, b.positive);1254 r.positive = llsignedand(r.limbs, a.limbs, a.positive, b.limbs, b.positive);
1253 r.normalize(if (a.positive or b.positive) b.limbs.len else a.limbs.len + 1);1255 r.normalize(if (b.positive) b.limbs.len else if (a.positive) a.limbs.len else a.limbs.len + 1);
1254 } else {1256 } else {
1255 r.positive = llsignedand(r.limbs, b.limbs, b.positive, a.limbs, a.positive);1257 r.positive = llsignedand(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
1256 r.normalize(if (a.positive or b.positive) a.limbs.len else b.limbs.len + 1);1258 r.normalize(if (a.positive) a.limbs.len else if (b.positive) b.limbs.len else b.limbs.len + 1);
1257 }1259 }
1258 }1260 }
12591261
...@@ -3136,10 +3138,10 @@ pub const Managed = struct {...@@ -3136,10 +3138,10 @@ pub const Managed = struct {
31363138
3137 /// r = a & b3139 /// r = a & b
3138 pub fn bitAnd(r: *Managed, a: *const Managed, b: *const Managed) !void {3140 pub fn bitAnd(r: *Managed, a: *const Managed, b: *const Managed) !void {
3139 const cap = if (a.isPositive() or b.isPositive())3141 const cap = if (a.len() >= b.len())
3140 @min(a.len(), b.len())3142 if (b.isPositive()) b.len() else if (a.isPositive()) a.len() else a.len() + 1
3141 else3143 else if (a.isPositive()) a.len() else if (b.isPositive()) b.len() else b.len() + 1;
3142 @max(a.len(), b.len()) + 1;3144
3143 try r.ensureCapacity(cap);3145 try r.ensureCapacity(cap);
3144 var m = r.toMutable();3146 var m = r.toMutable();
3145 m.bitAnd(a.toConst(), b.toConst());3147 m.bitAnd(a.toConst(), b.toConst());
...@@ -3885,7 +3887,7 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p...@@ -3885,7 +3887,7 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
3885 // x & ~a can only clear bits, so (x & ~a) <= x, meaning (-b - 1) + 1 never overflows.3887 // x & ~a can only clear bits, so (x & ~a) <= x, meaning (-b - 1) + 1 never overflows.
3886 assert(r_carry == 0);3888 assert(r_carry == 0);
38873889
3888 // With b = 0 and b_borrow = 0, we get ~a & (-0 - 0) = ~a & 0 = 0.3890 // With b = 0 and b_borrow = 0, we get ~a & (0 - 0) = ~a & 0 = 0.
3889 // Omit setting the upper bytes, just deal with those when calling llsignedor.3891 // Omit setting the upper bytes, just deal with those when calling llsignedor.
38903892
3891 return false;3893 return false;
...@@ -3922,7 +3924,7 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p...@@ -3922,7 +3924,7 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
3922 // for x = a - 1 and y = b - 1, the +1 term would never cause an overflow.3924 // for x = a - 1 and y = b - 1, the +1 term would never cause an overflow.
3923 assert(r_carry == 0);3925 assert(r_carry == 0);
39243926
3925 // With b = 0 and b_borrow = 0 we get (-a - 1) & (-0 - 0) = (-a - 1) & 0 = 0.3927 // With b = 0 and b_borrow = 0 we get (-a - 1) & (0 - 0) = (-a - 1) & 0 = 0.
3926 // Omit setting the upper bytes, just deal with those when calling llsignedor.3928 // Omit setting the upper bytes, just deal with those when calling llsignedor.
3927 return false;3929 return false;
3928 }3930 }
...@@ -3932,13 +3934,15 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p...@@ -3932,13 +3934,15 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
3932// r may alias.3934// r may alias.
3933// a and b must not be 0.3935// a and b must not be 0.
3934// Returns `true` when the result is positive.3936// Returns `true` when the result is positive.
3935// When either or both of a and b are positive, r requires at least `b.len` limbs of storage.3937// We assume `a.len >= b.len` here, so:
3936// When both a and b are negative, r requires at least `a.limbs.len + 1` limbs of storage.3938// 1. when b is positive, r requires at least `b.len` limbs of storage,
3939// 2. when b is negative but a is positive, r requires at least `a.len` limbs of storage,
3940// 3. when both a and b are negative, r requires at least `a.len + 1` limbs of storage.
3937fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool {3941fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool {
3938 @setRuntimeSafety(debug_safety);3942 @setRuntimeSafety(debug_safety);
3939 assert(a.len != 0 and b.len != 0);3943 assert(a.len != 0 and b.len != 0);
3940 assert(a.len >= b.len);3944 assert(a.len >= b.len);
3941 assert(r.len >= if (!a_positive and !b_positive) a.len + 1 else b.len);3945 assert(r.len >= if (b_positive) b.len else if (a_positive) a.len else a.len + 1);
39423946
3943 if (a_positive and b_positive) {3947 if (a_positive and b_positive) {
3944 // Trivial case, result is positive.3948 // Trivial case, result is positive.
...@@ -3987,9 +3991,12 @@ fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_...@@ -3987,9 +3991,12 @@ fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_
39873991
3988 assert(b_borrow == 0); // b was 03992 assert(b_borrow == 0); // b was 0
39893993
3990 // With b = 0 and b_borrow = 0 we have a & ~(-0 - 0) = a & 0 = 0, so3994 // With b = 0 and b_borrow = 0 we have a & ~(0 - 0) = a & ~0 = a, so
3991 // the upper bytes are zero. Omit setting them here and simply discard3995 // the upper bytes are the same as those of a.
3992 // them whenever llsignedand is called.3996
3997 while (i < a.len) : (i += 1) {
3998 r[i] = a[i];
3999 }
39934000
3994 return true;4001 return true;
3995 } else {4002 } else {
...@@ -4017,7 +4024,7 @@ fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_...@@ -4017,7 +4024,7 @@ fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_
4017 // b is at least 1, so this should never underflow.4024 // b is at least 1, so this should never underflow.
4018 assert(b_borrow == 0); // b was 04025 assert(b_borrow == 0); // b was 0
40194026
4020 // With b = 0 and b_borrow = 0 we get (-a - 1) | (-0 - 0) = (-a - 1) | 0 = -a - 1.4027 // With b = 0 and b_borrow = 0 we get (-a - 1) | (0 - 0) = (-a - 1) | 0 = -a - 1.
4021 while (i < a.len) : (i += 1) {4028 while (i < a.len) : (i += 1) {
4022 const ov1 = @subWithOverflow(a[i], a_borrow);4029 const ov1 = @subWithOverflow(a[i], a_borrow);
4023 a_borrow = ov1[1];4030 a_borrow = ov1[1];
lib/std/math/big/int_test.zig+13
...@@ -1487,6 +1487,19 @@ test "bitAnd #10932" {...@@ -1487,6 +1487,19 @@ test "bitAnd #10932" {
1487 try testing.expect((try res.to(i32)) == 0);1487 try testing.expect((try res.to(i32)) == 0);
1488}1488}
14891489
1490test "bit And #19235" {
1491 var a = try Managed.initSet(testing.allocator, -0xffffffffffffffff);
1492 defer a.deinit();
1493 var b = try Managed.initSet(testing.allocator, 0x10000000000000000);
1494 defer b.deinit();
1495 var r = try Managed.init(testing.allocator);
1496 defer r.deinit();
1497
1498 try r.bitAnd(&a, &b);
1499
1500 try testing.expect((try r.to(i128)) == 0x10000000000000000);
1501}
1502
1490test "div floor single-single +/+" {1503test "div floor single-single +/+" {
1491 const u: i32 = 5;1504 const u: i32 = 5;
1492 const v: i32 = 3;1505 const v: i32 = 3;