authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-09-23 04:08:27+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-09-23 04:08:27+02:00
logf5c27dd11aeac3bb4647396a0e420c649b30f468
tree4ba068d9a2c5d9b4ba0dc909701d7026f84cd3c6
parent3b09262c1252de0d9f946630e701be65bc5b2fc7

big ints: 2s complement signed or


2 files changed, 190 insertions(+), 14 deletions(-)

lib/std/math/big/int.zig+124-14
......@@ -552,21 +552,29 @@ pub const Mutable = struct {
552552 r.positive = a.positive;
553553 }
554554
555 /// r = a | b
555 /// r = a | b under 2s complement semantics.
556556 /// r may alias with a or b.
557557 ///
558558 /// a and b are zero-extended to the longer of a or b.
559559 ///
560560 /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.
561561 pub fn bitOr(r: *Mutable, a: Const, b: Const) void {
562 if (a.limbs.len > b.limbs.len) {
563 llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
564 r.len = a.limbs.len;
562 // Trivial cases, llsignedor does not support zero.
563 if (a.eqZero()) {
564 r.copy(b);
565 return;
566 } else if (b.eqZero()) {
567 r.copy(a);
568 return;
569 }
570
571 if (a.limbs.len >= b.limbs.len) {
572 r.positive = llsignedor(r.limbs, a.limbs, a.positive, b.limbs, b.positive);
573 r.normalize(a.limbs.len);
565574 } else {
566 llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
567 r.len = b.limbs.len;
575 r.positive = llsignedor(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
576 r.normalize(b.limbs.len);
568577 }
569 r.positive = a.positive or b.positive;
570578 }
571579
572580 /// r = a & b
......@@ -2236,17 +2244,119 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
22362244 }
22372245}
22382246
2239fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {
2247fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool {
22402248 @setRuntimeSafety(debug_safety);
22412249 assert(r.len >= a.len);
22422250 assert(a.len >= b.len);
22432251
2244 var i: usize = 0;
2245 while (i < b.len) : (i += 1) {
2246 r[i] = a[i] | b[i];
2247 }
2248 while (i < a.len) : (i += 1) {
2249 r[i] = a[i];
2252 if (a_positive and b_positive) {
2253 // Trivial case, result is positive.,
2254 var i: usize = 0;
2255 while (i < b.len) : (i += 1) {
2256 r[i] = a[i] | b[i];
2257 }
2258 while (i < a.len) : (i += 1) {
2259 r[i] = a[i];
2260 }
2261
2262 return true;
2263 } else if (!a_positive and b_positive) {
2264 // r = (--a) | b
2265 // = ~(-a - 1) | b
2266 // = ~(-a - 1) | ~~b
2267 // = ~((-a - 1) & ~b)
2268 // = -(((-a - 1) & ~b) + 1)
2269 // result is negative.
2270
2271 var i: usize = 0;
2272 var a_borrow: u1 = 1;
2273 var r_carry: u1 = 1;
2274
2275 while (i < b.len) : (i += 1) {
2276 var a_limb: Limb = undefined;
2277 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
2278
2279 r[i] = a_limb & ~b[i];
2280 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2281 }
2282
2283 // With b = 0, we get (-a - 1) & ~0 = -a - 1.
2284 while (i < a.len) : (i += 1) {
2285 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i]));
2286 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2287 }
2288
2289 assert(a_borrow == 0); // a was 0.
2290
2291 // Can never overflow because a_borrow would need to equal 1.
2292 assert(r_carry == 0);
2293
2294 return false;
2295 } else if (a_positive and !b_positive) {
2296 // r = a | (--b)
2297 // = a | ~(-b - 1)
2298 // = ~~a | ~(-b - 1)
2299 // = ~(~a & (-b - 1))
2300 // = -((~a & (-b - 1)) + 1)
2301 // result is negative.
2302
2303 var i: usize = 0;
2304 var b_borrow: u1 = 1;
2305 var r_carry: u1 = 1;
2306
2307 while (i < b.len) : (i += 1) {
2308 var b_limb: Limb = undefined;
2309 b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
2310
2311 r[i] = ~a[i] & b_limb;
2312 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2313 }
2314
2315 // b is at least 1, so this should never underflow.
2316 assert(b_borrow == 0); // b was 0
2317
2318 // Can never overflow because in order for b_limb to be maxInt(Limb),
2319 // b_borrow would need to equal 1.
2320 assert(r_carry == 0);
2321
2322 // With b = 0 and b_borrow = 0, we get ~a & (-0 - 0) = ~a & 0 = 0.
2323 mem.set(Limb, r[i..a.len], 0);
2324
2325 return false;
2326 } else {
2327 // r = (--a) | (--b)
2328 // = ~(-a - 1) | ~(-b - 1)
2329 // = ~((-a - 1) & (-b - 1))
2330 // = -(~(~((-a - 1) & (-b - 1))) + 1)
2331 // = -((-a - 1) & (-b - 1) + 1)
2332 // result is negative.
2333
2334 var i: usize = 0;
2335 var a_borrow: u1 = 1;
2336 var b_borrow: u1 = 1;
2337 var r_carry: u1 = 1;
2338
2339 while (i < b.len) : (i += 1) {
2340 var a_limb: Limb = undefined;
2341 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
2342
2343 var b_limb: Limb = undefined;
2344 b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
2345
2346 r[i] = a_limb & b_limb;
2347 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2348 }
2349
2350 // b is at least 1, so this should never underflow.
2351 assert(b_borrow == 0); // b was 0
2352
2353 // Can never overflow because in order for b_limb to be maxInt(Limb),
2354 // b_borrow would need to equal 1.
2355 assert(r_carry == 0);
2356
2357 // With b = 0 and b_borrow = 0 we get (-a - 1) & (-0 - 0) = (-a - 1) & 0 = 0.
2358 mem.set(Limb, r[i..a.len], 0);
2359 return false;
22502360 }
22512361}
22522362
lib/std/math/big/int_test.zig+66
......@@ -1476,6 +1476,72 @@ test "big.int bitwise or multi-limb" {
14761476 try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));
14771477}
14781478
1479test "big.int bitwise or negative-positive simple" {
1480 var a = try Managed.initSet(testing.allocator, -0xffffffff11111111);
1481 defer a.deinit();
1482 var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222);
1483 defer b.deinit();
1484
1485 try a.bitOr(a, b);
1486
1487 try testing.expect((try a.to(i64)) == -0x1111111111111111);
1488}
1489
1490test "big.int bitwise or negative-positive multi-limb" {
1491 var a = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1);
1492 defer a.deinit();
1493 var b = try Managed.initSet(testing.allocator, 1);
1494 defer b.deinit();
1495
1496 try a.bitOr(a, b);
1497
1498 try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb));
1499}
1500
1501test "big.int bitwise or positive-negative simple" {
1502 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1503 defer a.deinit();
1504 var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222);
1505 defer b.deinit();
1506
1507 try a.bitOr(a, b);
1508
1509 try testing.expect((try a.to(i64)) == -0x22222221);
1510}
1511
1512test "big.int bitwise or positive-negative multi-limb" {
1513 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
1514 defer a.deinit();
1515 var b = try Managed.initSet(testing.allocator, -1);
1516 defer b.deinit();
1517
1518 try a.bitOr(a, b);
1519
1520 try testing.expect((try a.to(SignedDoubleLimb)) == -1);
1521}
1522
1523test "big.int bitwise or negative-negative simple" {
1524 var a = try Managed.initSet(testing.allocator, -0x0fffffff11111111);
1525 defer a.deinit();
1526 var b = try Managed.initSet(testing.allocator, -0x0eeeeeee22222222);
1527 defer b.deinit();
1528
1529 try a.bitOr(a, b);
1530
1531 try testing.expect((try a.to(i64)) == -0xeeeeeee00000001);
1532}
1533
1534test "big.int bitwise or negative-negative multi-limb" {
1535 var a = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1);
1536 defer a.deinit();
1537 var b = try Managed.initSet(testing.allocator, -maxInt(Limb));
1538 defer b.deinit();
1539
1540 try a.bitOr(a, b);
1541
1542 try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb));
1543}
1544
14791545test "big.int var args" {
14801546 var a = try Managed.initSet(testing.allocator, 5);
14811547 defer a.deinit();