authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-09-23 06:19:08+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-09-23 06:19:08+02:00
log351e4f07cebc8299c107bd3de760efe17d184af7
tree570d658f7b83c2f76676523f1cb85969b33acbdd
parentf5c27dd11aeac3bb4647396a0e420c649b30f468

big ints: 2s complement signed and + or fixes


2 files changed, 229 insertions(+), 32 deletions(-)

lib/std/math/big/int.zig+149-29
......@@ -570,26 +570,36 @@ pub const Mutable = struct {
570570
571571 if (a.limbs.len >= b.limbs.len) {
572572 r.positive = llsignedor(r.limbs, a.limbs, a.positive, b.limbs, b.positive);
573 r.normalize(a.limbs.len);
573 r.normalize(if (b.positive) a.limbs.len else b.limbs.len);
574574 } else {
575575 r.positive = llsignedor(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
576 r.normalize(b.limbs.len);
576 r.normalize(if (a.positive) b.limbs.len else a.limbs.len);
577577 }
578578 }
579579
580 /// r = a & b
580 /// r = a & b under 2s complement semantics.
581581 /// r may alias with a or b.
582582 ///
583 /// Asserts that r has enough limbs to store the result. Upper bound is `math.min(a.limbs.len, b.limbs.len)`.
583 /// Asserts that r has enough limbs to store the result.
584 /// If a or b is positive, the upper bound is `math.min(a.limbs.len, b.limbs.len)`.
585 /// If a and b are negative, the upper bound is `math.max(a.limbs.len, b.limbs.len) + 1`.
584586 pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {
585 if (a.limbs.len > b.limbs.len) {
586 lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
587 r.normalize(b.limbs.len);
587 // Trivial cases, llsignedand does not support zero.
588 if (a.eqZero()) {
589 r.copy(a);
590 return;
591 } else if (b.eqZero()) {
592 r.copy(b);
593 return;
594 }
595
596 if (a.limbs.len >= b.limbs.len) {
597 r.positive = llsignedand(r.limbs, a.limbs, a.positive, b.limbs, b.positive);
598 r.normalize(if (a.positive or b.positive) b.limbs.len else a.limbs.len + 1);
588599 } else {
589 lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
590 r.normalize(a.limbs.len);
600 r.positive = llsignedand(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
601 r.normalize(if (a.positive or b.positive) a.limbs.len else b.limbs.len + 1);
591602 }
592 r.positive = a.positive and b.positive;
593603 }
594604
595605 /// r = a ^ b under 2s complement semantics.
......@@ -1855,7 +1865,11 @@ pub const Managed = struct {
18551865
18561866 /// r = a & b
18571867 pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void {
1858 try r.ensureCapacity(math.min(a.len(), b.len()));
1868 const cap = if (a.isPositive() or b.isPositive())
1869 math.min(a.len(), b.len())
1870 else
1871 math.max(a.len(), b.len()) + 1;
1872 try r.ensureCapacity(cap);
18591873 var m = r.toMutable();
18601874 m.bitAnd(a.toConst(), b.toConst());
18611875 r.setMetadata(m.positive, m.len);
......@@ -2244,13 +2258,19 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
22442258 }
22452259}
22462260
2261// r = a | b with 2s complement semantics.
2262// r may alias.
2263// a and b must not be 0.
2264// Returns `true` when the result is positive.
2265// When b is positive, r requires at least `a.len` limbs of storage.
2266// When b is negative, r requires at least `b.len` limbs of storage.
22472267fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool {
22482268 @setRuntimeSafety(debug_safety);
22492269 assert(r.len >= a.len);
22502270 assert(a.len >= b.len);
22512271
22522272 if (a_positive and b_positive) {
2253 // Trivial case, result is positive.,
2273 // Trivial case, result is positive.
22542274 var i: usize = 0;
22552275 while (i < b.len) : (i += 1) {
22562276 r[i] = a[i] | b[i];
......@@ -2261,12 +2281,12 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
22612281
22622282 return true;
22632283 } else if (!a_positive and b_positive) {
2284 // Result is negative.
22642285 // r = (--a) | b
22652286 // = ~(-a - 1) | b
22662287 // = ~(-a - 1) | ~~b
22672288 // = ~((-a - 1) & ~b)
22682289 // = -(((-a - 1) & ~b) + 1)
2269 // result is negative.
22702290
22712291 var i: usize = 0;
22722292 var a_borrow: u1 = 1;
......@@ -2280,25 +2300,29 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
22802300 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
22812301 }
22822302
2303 // In order for r_carry to be nonzero at this point, ~b[i] would need to be
2304 // all ones, which would require b[i] to be zero. This cannot be when
2305 // b is normalized, so there cannot be a carry here.
2306 // Also, x & ~b can only clear bits, so (x & ~b) <= x, meaning (-a - 1) + 1 never overflows.
2307 assert(r_carry == 0);
2308
22832309 // With b = 0, we get (-a - 1) & ~0 = -a - 1.
2284 while (i < a.len) : (i += 1) {
2310 // Note, if a_borrow is zero we do not need to compute anything for
2311 // the higher limbs so we can early return here.
2312 while (i < a.len and a_borrow == 1) : (i += 1) {
22852313 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i]));
2286 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
22872314 }
22882315
22892316 assert(a_borrow == 0); // a was 0.
22902317
2291 // Can never overflow because a_borrow would need to equal 1.
2292 assert(r_carry == 0);
2293
22942318 return false;
22952319 } else if (a_positive and !b_positive) {
2320 // Result is negative.
22962321 // r = a | (--b)
22972322 // = a | ~(-b - 1)
22982323 // = ~~a | ~(-b - 1)
22992324 // = ~(~a & (-b - 1))
23002325 // = -((~a & (-b - 1)) + 1)
2301 // result is negative.
23022326
23032327 var i: usize = 0;
23042328 var b_borrow: u1 = 1;
......@@ -2315,21 +2339,20 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
23152339 // b is at least 1, so this should never underflow.
23162340 assert(b_borrow == 0); // b was 0
23172341
2318 // Can never overflow because in order for b_limb to be maxInt(Limb),
2319 // b_borrow would need to equal 1.
2342 // x & ~a can only clear bits, so (x & ~a) <= x, meaning (-b - 1) + 1 never overflows.
23202343 assert(r_carry == 0);
23212344
23222345 // With b = 0 and b_borrow = 0, we get ~a & (-0 - 0) = ~a & 0 = 0.
2323 mem.set(Limb, r[i..a.len], 0);
2346 // Omit setting the upper bytes, just deal with those when calling llsignedor.
23242347
23252348 return false;
23262349 } else {
2350 // Result is negative.
23272351 // r = (--a) | (--b)
23282352 // = ~(-a - 1) | ~(-b - 1)
23292353 // = ~((-a - 1) & (-b - 1))
23302354 // = -(~(~((-a - 1) & (-b - 1))) + 1)
23312355 // = -((-a - 1) & (-b - 1) + 1)
2332 // result is negative.
23332356
23342357 var i: usize = 0;
23352358 var a_borrow: u1 = 1;
......@@ -2352,22 +2375,119 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
23522375
23532376 // Can never overflow because in order for b_limb to be maxInt(Limb),
23542377 // b_borrow would need to equal 1.
2378
2379 // x & y can only clear bits, meaning x & y <= x and x & y <= y. This implies that
2380 // for x = a - 1 and y = b - 1, the +1 term would never cause an overflow.
23552381 assert(r_carry == 0);
23562382
23572383 // 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);
2384 // Omit setting the upper bytes, just deal with those when calling llsignedor.
23592385 return false;
23602386 }
23612387}
23622388
2363fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {
2389// r = a & b with 2s complement semantics.
2390// r may alias.
2391// a and b must not be 0.
2392// Returns `true` when the result is positive.
2393// When either or both of a and b are positive, r requires at least `b.len` limbs of storage.
2394// When both a and b are negative, r requires at least `a.limbs.len + 1` limbs of storage.
2395fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool {
23642396 @setRuntimeSafety(debug_safety);
2365 assert(r.len >= b.len);
2397 assert(a.len != 0 and b.len != 0);
2398 assert(r.len >= a.len);
23662399 assert(a.len >= b.len);
23672400
2368 var i: usize = 0;
2369 while (i < b.len) : (i += 1) {
2370 r[i] = a[i] & b[i];
2401 if (a_positive and b_positive) {
2402 // Trivial case, result is positive.
2403 var i: usize = 0;
2404 while (i < b.len) : (i += 1) {
2405 r[i] = a[i] & b[i];
2406 }
2407
2408 // With b = 0 we have a & 0 = 0, so the upper bytes are zero.
2409 // Omit setting them here and simply discard them whenever
2410 // llsignedand is called.
2411
2412 return true;
2413 } else if (!a_positive and b_positive) {
2414 // Result is positive.
2415 // r = (--a) & b
2416 // = ~(-a - 1) & b
2417
2418 var i: usize = 0;
2419 var a_borrow: u1 = 1;
2420
2421 while (i < b.len) : (i += 1) {
2422 var a_limb: Limb = undefined;
2423 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
2424 r[i] = ~a_limb & b[i];
2425 }
2426
2427 // With b = 0 we have ~(a - 1) & 0 = 0, so the upper bytes are zero.
2428 // Omit setting them here and simply discard them whenever
2429 // llsignedand is called.
2430
2431 return true;
2432 } else if (a_positive and !b_positive) {
2433 // Result is positive.
2434 // r = a & (--b)
2435 // = a & ~(-b - 1)
2436
2437 var i: usize = 0;
2438 var b_borrow: u1 = 1;
2439
2440 while (i < b.len) : (i += 1) {
2441 var a_limb: Limb = undefined;
2442 b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &a_limb));
2443 r[i] = a[i] & ~a_limb;
2444 }
2445
2446 assert(b_borrow == 0); // b was 0
2447
2448 // With b = 0 and b_borrow = 0 we have a & ~(-0 - 0) = a & 0 = 0, so
2449 // the upper bytes are zero. Omit setting them here and simply discard
2450 // them whenever llsignedand is called.
2451
2452 return true;
2453 } else {
2454 // Result is negative.
2455 // r = (--a) & (--b)
2456 // = ~(-a - 1) & ~(-b - 1)
2457 // = ~((-a - 1) | (-b - 1))
2458 // = -(((-a - 1) | (-b - 1)) + 1)
2459
2460 var i: usize = 0;
2461 var a_borrow: u1 = 1;
2462 var b_borrow: u1 = 1;
2463 var r_carry: u1 = 1;
2464
2465 while (i < b.len) : (i += 1) {
2466 var a_limb: Limb = undefined;
2467 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
2468
2469 var b_limb: Limb = undefined;
2470 b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
2471
2472 r[i] = a_limb | b_limb;
2473 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2474 }
2475
2476 // b is at least 1, so this should never underflow.
2477 assert(b_borrow == 0); // b was 0
2478
2479 // With b = 0 and b_borrow = 0 we get (-a - 1) | (-0 - 0) = (-a - 1) | 0 = -a - 1.
2480 while (i < a.len) : (i += 1) {
2481 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i]));
2482 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2483 }
2484
2485 assert(a_borrow == 0); // a was 0.
2486
2487 // The final addition can overflow here, so we need to keep that in mind.
2488 r[i] = r_carry;
2489
2490 return false;
23712491 }
23722492}
23732493
lib/std/math/big/int_test.zig+80-3
......@@ -1365,6 +1365,83 @@ test "big.int bitwise and multi-limb" {
13651365 try testing.expect((try a.to(u128)) == 0);
13661366}
13671367
1368test "big.int bitwise and negative-positive simple" {
1369 var a = try Managed.initSet(testing.allocator, -0xffffffff11111111);
1370 defer a.deinit();
1371 var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222);
1372 defer b.deinit();
1373
1374 try a.bitAnd(a, b);
1375
1376 try testing.expect((try a.to(u64)) == 0x22222222);
1377}
1378
1379test "big.int bitwise and negative-positive multi-limb" {
1380 var a = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1);
1381 defer a.deinit();
1382 var b = try Managed.initSet(testing.allocator, maxInt(Limb));
1383 defer b.deinit();
1384
1385 try a.bitAnd(a, b);
1386
1387 try testing.expect(a.eqZero());
1388}
1389
1390test "big.int bitwise and positive-negative simple" {
1391 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1392 defer a.deinit();
1393 var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222);
1394 defer b.deinit();
1395
1396 try a.bitAnd(a, b);
1397
1398 try testing.expect((try a.to(u64)) == 0x1111111111111110);
1399}
1400
1401test "big.int bitwise and positive-negative multi-limb" {
1402 var a = try Managed.initSet(testing.allocator, maxInt(Limb));
1403 defer a.deinit();
1404 var b = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1);
1405 defer b.deinit();
1406
1407 try a.bitAnd(a, b);
1408
1409 try testing.expect(a.eqZero());
1410}
1411
1412test "big.int bitwise and negative-negative simple" {
1413 var a = try Managed.initSet(testing.allocator, -0xffffffff11111111);
1414 defer a.deinit();
1415 var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222);
1416 defer b.deinit();
1417
1418 try a.bitAnd(a, b);
1419
1420 try testing.expect((try a.to(i128)) == -0xffffffff33333332);
1421}
1422
1423test "big.int bitwise and negative-negative multi-limb" {
1424 var a = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1);
1425 defer a.deinit();
1426 var b = try Managed.initSet(testing.allocator, -maxInt(Limb) - 2);
1427 defer b.deinit();
1428
1429 try a.bitAnd(a, b);
1430
1431 try testing.expect((try a.to(i128)) == -maxInt(Limb) * 2 - 2);
1432}
1433
1434test "big.int bitwise and negative overflow" {
1435 var a = try Managed.initSet(testing.allocator, -maxInt(Limb));
1436 defer a.deinit();
1437 var b = try Managed.initSet(testing.allocator, -2);
1438 defer b.deinit();
1439
1440 try a.bitAnd(a, b);
1441
1442 try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb) - 1);
1443}
1444
13681445test "big.int bitwise xor simple" {
13691446 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
13701447 defer a.deinit();
......@@ -1521,14 +1598,14 @@ test "big.int bitwise or positive-negative multi-limb" {
15211598}
15221599
15231600test "big.int bitwise or negative-negative simple" {
1524 var a = try Managed.initSet(testing.allocator, -0x0fffffff11111111);
1601 var a = try Managed.initSet(testing.allocator, -0xffffffff11111111);
15251602 defer a.deinit();
1526 var b = try Managed.initSet(testing.allocator, -0x0eeeeeee22222222);
1603 var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222);
15271604 defer b.deinit();
15281605
15291606 try a.bitOr(a, b);
15301607
1531 try testing.expect((try a.to(i64)) == -0xeeeeeee00000001);
1608 try testing.expect((try a.to(i128)) == -0xeeeeeeee00000001);
15321609}
15331610
15341611test "big.int bitwise or negative-negative multi-limb" {