| author | |
| committer | |
| log | 1ee59c5c31efca394d7e6d9da3f64c289a996b99 |
| tree | 87c81299ed1adfc22f3e34901bbfcd3062dcf752 |
| parent | a20f3e3f02b24219f43c2da4568b794051e7d348 |
2 files changed, 188 insertions(+), 188 deletions(-)
lib/std/math/big/int.zig+187| ... | ... | @@ -10,6 +10,7 @@ const minInt = std.math.minInt; |
| 10 | 10 | |
| 11 | 11 | pub const Limb = usize; |
| 12 | 12 | pub const DoubleLimb = std.meta.IntType(false, 2 * Limb.bit_count); |
| 13 | pub const SignedDoubleLimb = std.meta.IntType(true, DoubleLimb.bit_count); | |
| 13 | 14 | pub const Log2Limb = math.Log2Int(Limb); |
| 14 | 15 | |
| 15 | 16 | comptime { |
| ... | ... | @@ -1359,8 +1360,129 @@ pub const Int = struct { |
| 1359 | 1360 | r[i] = a[i]; |
| 1360 | 1361 | } |
| 1361 | 1362 | } |
| 1363 | ||
| 1364 | pub fn gcd(rma: *Int, x: Int, y: Int) !void { | |
| 1365 | rma.assertWritable(); | |
| 1366 | var r = rma; | |
| 1367 | var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr; | |
| 1368 | ||
| 1369 | var sr: Int = undefined; | |
| 1370 | if (aliased) { | |
| 1371 | sr = try Int.initCapacity(rma.allocator.?, math.max(x.len(), y.len())); | |
| 1372 | r = &sr; | |
| 1373 | aliased = true; | |
| 1374 | } | |
| 1375 | defer if (aliased) { | |
| 1376 | rma.swap(r); | |
| 1377 | r.deinit(); | |
| 1378 | }; | |
| 1379 | ||
| 1380 | try gcdLehmer(r, x, y); | |
| 1381 | } | |
| 1382 | ||
| 1383 | fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void { | |
| 1384 | var x = try xa.clone(); | |
| 1385 | x.abs(); | |
| 1386 | defer x.deinit(); | |
| 1387 | ||
| 1388 | var y = try ya.clone(); | |
| 1389 | y.abs(); | |
| 1390 | defer y.deinit(); | |
| 1391 | ||
| 1392 | if (x.cmp(y) == .lt) { | |
| 1393 | x.swap(&y); | |
| 1394 | } | |
| 1395 | ||
| 1396 | var T = try Int.init(r.allocator.?); | |
| 1397 | defer T.deinit(); | |
| 1398 | ||
| 1399 | while (y.len() > 1) { | |
| 1400 | debug.assert(x.isPositive() and y.isPositive()); | |
| 1401 | debug.assert(x.len() >= y.len()); | |
| 1402 | ||
| 1403 | var xh: SignedDoubleLimb = x.limbs[x.len() - 1]; | |
| 1404 | var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1]; | |
| 1405 | ||
| 1406 | var A: SignedDoubleLimb = 1; | |
| 1407 | var B: SignedDoubleLimb = 0; | |
| 1408 | var C: SignedDoubleLimb = 0; | |
| 1409 | var D: SignedDoubleLimb = 1; | |
| 1410 | ||
| 1411 | while (yh + C != 0 and yh + D != 0) { | |
| 1412 | const q = @divFloor(xh + A, yh + C); | |
| 1413 | const qp = @divFloor(xh + B, yh + D); | |
| 1414 | if (q != qp) { | |
| 1415 | break; | |
| 1416 | } | |
| 1417 | ||
| 1418 | var t = A - q * C; | |
| 1419 | A = C; | |
| 1420 | C = t; | |
| 1421 | t = B - q * D; | |
| 1422 | B = D; | |
| 1423 | D = t; | |
| 1424 | ||
| 1425 | t = xh - q * yh; | |
| 1426 | xh = yh; | |
| 1427 | yh = t; | |
| 1428 | } | |
| 1429 | ||
| 1430 | if (B == 0) { | |
| 1431 | // T = x % y, r is unused | |
| 1432 | try Int.divTrunc(r, &T, x, y); | |
| 1433 | debug.assert(T.isPositive()); | |
| 1434 | ||
| 1435 | x.swap(&y); | |
| 1436 | y.swap(&T); | |
| 1437 | } else { | |
| 1438 | var storage: [8]Limb = undefined; | |
| 1439 | const Ap = FixedIntFromSignedDoubleLimb(A, storage[0..2]); | |
| 1440 | const Bp = FixedIntFromSignedDoubleLimb(B, storage[2..4]); | |
| 1441 | const Cp = FixedIntFromSignedDoubleLimb(C, storage[4..6]); | |
| 1442 | const Dp = FixedIntFromSignedDoubleLimb(D, storage[6..8]); | |
| 1443 | ||
| 1444 | // T = Ax + By | |
| 1445 | try r.mul(x, Ap); | |
| 1446 | try T.mul(y, Bp); | |
| 1447 | try T.add(r.*, T); | |
| 1448 | ||
| 1449 | // u = Cx + Dy, r as u | |
| 1450 | try x.mul(x, Cp); | |
| 1451 | try r.mul(y, Dp); | |
| 1452 | try r.add(x, r.*); | |
| 1453 | ||
| 1454 | x.swap(&T); | |
| 1455 | y.swap(r); | |
| 1456 | } | |
| 1457 | } | |
| 1458 | ||
| 1459 | // euclidean algorithm | |
| 1460 | debug.assert(x.cmp(y) != .lt); | |
| 1461 | ||
| 1462 | while (!y.eqZero()) { | |
| 1463 | try Int.divTrunc(&T, r, x, y); | |
| 1464 | x.swap(&y); | |
| 1465 | y.swap(r); | |
| 1466 | } | |
| 1467 | ||
| 1468 | r.swap(&x); | |
| 1469 | } | |
| 1470 | ||
| 1362 | 1471 | }; |
| 1363 | 1472 | |
| 1473 | // Storage must live for the lifetime of the returned value | |
| 1474 | fn FixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Int { | |
| 1475 | std.debug.assert(storage.len >= 2); | |
| 1476 | ||
| 1477 | var A_is_positive = A >= 0; | |
| 1478 | const Au = @intCast(DoubleLimb, if (A < 0) -A else A); | |
| 1479 | storage[0] = @truncate(Limb, Au); | |
| 1480 | storage[1] = @truncate(Limb, Au >> Limb.bit_count); | |
| 1481 | var Ap = Int.initFixed(storage[0..2]); | |
| 1482 | Ap.setSign(A_is_positive); | |
| 1483 | return Ap; | |
| 1484 | } | |
| 1485 | ||
| 1364 | 1486 | // NOTE: All the following tests assume the max machine-word will be 64-bit. |
| 1365 | 1487 | // |
| 1366 | 1488 | // They will still run on larger than this and should pass, but the multi-limb code-paths |
| ... | ... | @@ -2738,3 +2860,68 @@ test "big.int var args" { |
| 2738 | 2860 | defer d.deinit(); |
| 2739 | 2861 | testing.expect(a.cmp(d) != .gt); |
| 2740 | 2862 | } |
| 2863 | ||
| 2864 | test "big.int gcd non-one small" { | |
| 2865 | var a = try Int.initSet(testing.allocator, 17); | |
| 2866 | defer a.deinit(); | |
| 2867 | var b = try Int.initSet(testing.allocator, 97); | |
| 2868 | defer b.deinit(); | |
| 2869 | var r = try Int.init(testing.allocator); | |
| 2870 | defer r.deinit(); | |
| 2871 | ||
| 2872 | try r.gcd(a, b); | |
| 2873 | ||
| 2874 | testing.expect((try r.to(u32)) == 1); | |
| 2875 | } | |
| 2876 | ||
| 2877 | test "big.int gcd non-one small" { | |
| 2878 | var a = try Int.initSet(testing.allocator, 4864); | |
| 2879 | defer a.deinit(); | |
| 2880 | var b = try Int.initSet(testing.allocator, 3458); | |
| 2881 | defer b.deinit(); | |
| 2882 | var r = try Int.init(testing.allocator); | |
| 2883 | defer r.deinit(); | |
| 2884 | ||
| 2885 | try r.gcd(a, b); | |
| 2886 | ||
| 2887 | testing.expect((try r.to(u32)) == 38); | |
| 2888 | } | |
| 2889 | ||
| 2890 | test "big.int gcd non-one large" { | |
| 2891 | var a = try Int.initSet(testing.allocator, 0xffffffffffffffff); | |
| 2892 | defer a.deinit(); | |
| 2893 | var b = try Int.initSet(testing.allocator, 0xffffffffffffffff7777); | |
| 2894 | defer b.deinit(); | |
| 2895 | var r = try Int.init(testing.allocator); | |
| 2896 | defer r.deinit(); | |
| 2897 | ||
| 2898 | try r.gcd(a, b); | |
| 2899 | ||
| 2900 | testing.expect((try r.to(u32)) == 4369); | |
| 2901 | } | |
| 2902 | ||
| 2903 | test "big.int gcd large multi-limb result" { | |
| 2904 | var a = try Int.initSet(testing.allocator, 0x12345678123456781234567812345678123456781234567812345678); | |
| 2905 | defer a.deinit(); | |
| 2906 | var b = try Int.initSet(testing.allocator, 0x12345671234567123456712345671234567123456712345671234567); | |
| 2907 | defer b.deinit(); | |
| 2908 | var r = try Int.init(testing.allocator); | |
| 2909 | defer r.deinit(); | |
| 2910 | ||
| 2911 | try r.gcd(a, b); | |
| 2912 | ||
| 2913 | testing.expect((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1); | |
| 2914 | } | |
| 2915 | ||
| 2916 | test "big.int gcd one large" { | |
| 2917 | var a = try Int.initSet(testing.allocator, 1897056385327307); | |
| 2918 | defer a.deinit(); | |
| 2919 | var b = try Int.initSet(testing.allocator, 2251799813685248); | |
| 2920 | defer b.deinit(); | |
| 2921 | var r = try Int.init(testing.allocator); | |
| 2922 | defer r.deinit(); | |
| 2923 | ||
| 2924 | try r.gcd(a, b); | |
| 2925 | ||
| 2926 | testing.expect((try r.to(u64)) == 1); | |
| 2927 | } |
lib/std/math/big/rational.zig+1-188| ... | ... | @@ -447,7 +447,7 @@ pub const Rational = struct { |
| 447 | 447 | |
| 448 | 448 | const sign = r.p.isPositive(); |
| 449 | 449 | r.p.abs(); |
| 450 | try gcd(&a, r.p, r.q); | |
| 450 | try a.gcd(r.p, r.q); | |
| 451 | 451 | r.p.setSign(sign); |
| 452 | 452 | |
| 453 | 453 | const one = Int.initFixed(([_]Limb{1})[0..]); |
| ... | ... | @@ -463,193 +463,6 @@ pub const Rational = struct { |
| 463 | 463 | } |
| 464 | 464 | }; |
| 465 | 465 | |
| 466 | const SignedDoubleLimb = std.meta.IntType(true, DoubleLimb.bit_count); | |
| 467 | ||
| 468 | fn gcd(rma: *Int, x: Int, y: Int) !void { | |
| 469 | rma.assertWritable(); | |
| 470 | var r = rma; | |
| 471 | var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr; | |
| 472 | ||
| 473 | var sr: Int = undefined; | |
| 474 | if (aliased) { | |
| 475 | sr = try Int.initCapacity(rma.allocator.?, math.max(x.len(), y.len())); | |
| 476 | r = &sr; | |
| 477 | aliased = true; | |
| 478 | } | |
| 479 | defer if (aliased) { | |
| 480 | rma.swap(r); | |
| 481 | r.deinit(); | |
| 482 | }; | |
| 483 | ||
| 484 | try gcdLehmer(r, x, y); | |
| 485 | } | |
| 486 | ||
| 487 | // Storage must live for the lifetime of the returned value | |
| 488 | fn FixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Int { | |
| 489 | std.debug.assert(storage.len >= 2); | |
| 490 | ||
| 491 | var A_is_positive = A >= 0; | |
| 492 | const Au = @intCast(DoubleLimb, if (A < 0) -A else A); | |
| 493 | storage[0] = @truncate(Limb, Au); | |
| 494 | storage[1] = @truncate(Limb, Au >> Limb.bit_count); | |
| 495 | var Ap = Int.initFixed(storage[0..2]); | |
| 496 | Ap.setSign(A_is_positive); | |
| 497 | return Ap; | |
| 498 | } | |
| 499 | ||
| 500 | fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void { | |
| 501 | var x = try xa.clone(); | |
| 502 | x.abs(); | |
| 503 | defer x.deinit(); | |
| 504 | ||
| 505 | var y = try ya.clone(); | |
| 506 | y.abs(); | |
| 507 | defer y.deinit(); | |
| 508 | ||
| 509 | if (x.cmp(y) == .lt) { | |
| 510 | x.swap(&y); | |
| 511 | } | |
| 512 | ||
| 513 | var T = try Int.init(r.allocator.?); | |
| 514 | defer T.deinit(); | |
| 515 | ||
| 516 | while (y.len() > 1) { | |
| 517 | debug.assert(x.isPositive() and y.isPositive()); | |
| 518 | debug.assert(x.len() >= y.len()); | |
| 519 | ||
| 520 | var xh: SignedDoubleLimb = x.limbs[x.len() - 1]; | |
| 521 | var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1]; | |
| 522 | ||
| 523 | var A: SignedDoubleLimb = 1; | |
| 524 | var B: SignedDoubleLimb = 0; | |
| 525 | var C: SignedDoubleLimb = 0; | |
| 526 | var D: SignedDoubleLimb = 1; | |
| 527 | ||
| 528 | while (yh + C != 0 and yh + D != 0) { | |
| 529 | const q = @divFloor(xh + A, yh + C); | |
| 530 | const qp = @divFloor(xh + B, yh + D); | |
| 531 | if (q != qp) { | |
| 532 | break; | |
| 533 | } | |
| 534 | ||
| 535 | var t = A - q * C; | |
| 536 | A = C; | |
| 537 | C = t; | |
| 538 | t = B - q * D; | |
| 539 | B = D; | |
| 540 | D = t; | |
| 541 | ||
| 542 | t = xh - q * yh; | |
| 543 | xh = yh; | |
| 544 | yh = t; | |
| 545 | } | |
| 546 | ||
| 547 | if (B == 0) { | |
| 548 | // T = x % y, r is unused | |
| 549 | try Int.divTrunc(r, &T, x, y); | |
| 550 | debug.assert(T.isPositive()); | |
| 551 | ||
| 552 | x.swap(&y); | |
| 553 | y.swap(&T); | |
| 554 | } else { | |
| 555 | var storage: [8]Limb = undefined; | |
| 556 | const Ap = FixedIntFromSignedDoubleLimb(A, storage[0..2]); | |
| 557 | const Bp = FixedIntFromSignedDoubleLimb(B, storage[2..4]); | |
| 558 | const Cp = FixedIntFromSignedDoubleLimb(C, storage[4..6]); | |
| 559 | const Dp = FixedIntFromSignedDoubleLimb(D, storage[6..8]); | |
| 560 | ||
| 561 | // T = Ax + By | |
| 562 | try r.mul(x, Ap); | |
| 563 | try T.mul(y, Bp); | |
| 564 | try T.add(r.*, T); | |
| 565 | ||
| 566 | // u = Cx + Dy, r as u | |
| 567 | try x.mul(x, Cp); | |
| 568 | try r.mul(y, Dp); | |
| 569 | try r.add(x, r.*); | |
| 570 | ||
| 571 | x.swap(&T); | |
| 572 | y.swap(r); | |
| 573 | } | |
| 574 | } | |
| 575 | ||
| 576 | // euclidean algorithm | |
| 577 | debug.assert(x.cmp(y) != .lt); | |
| 578 | ||
| 579 | while (!y.eqZero()) { | |
| 580 | try Int.divTrunc(&T, r, x, y); | |
| 581 | x.swap(&y); | |
| 582 | y.swap(r); | |
| 583 | } | |
| 584 | ||
| 585 | r.swap(&x); | |
| 586 | } | |
| 587 | ||
| 588 | test "big.rational gcd non-one small" { | |
| 589 | var a = try Int.initSet(testing.allocator, 17); | |
| 590 | defer a.deinit(); | |
| 591 | var b = try Int.initSet(testing.allocator, 97); | |
| 592 | defer b.deinit(); | |
| 593 | var r = try Int.init(testing.allocator); | |
| 594 | defer r.deinit(); | |
| 595 | ||
| 596 | try gcd(&r, a, b); | |
| 597 | ||
| 598 | testing.expect((try r.to(u32)) == 1); | |
| 599 | } | |
| 600 | ||
| 601 | test "big.rational gcd non-one small" { | |
| 602 | var a = try Int.initSet(testing.allocator, 4864); | |
| 603 | defer a.deinit(); | |
| 604 | var b = try Int.initSet(testing.allocator, 3458); | |
| 605 | defer b.deinit(); | |
| 606 | var r = try Int.init(testing.allocator); | |
| 607 | defer r.deinit(); | |
| 608 | ||
| 609 | try gcd(&r, a, b); | |
| 610 | ||
| 611 | testing.expect((try r.to(u32)) == 38); | |
| 612 | } | |
| 613 | ||
| 614 | test "big.rational gcd non-one large" { | |
| 615 | var a = try Int.initSet(testing.allocator, 0xffffffffffffffff); | |
| 616 | defer a.deinit(); | |
| 617 | var b = try Int.initSet(testing.allocator, 0xffffffffffffffff7777); | |
| 618 | defer b.deinit(); | |
| 619 | var r = try Int.init(testing.allocator); | |
| 620 | defer r.deinit(); | |
| 621 | ||
| 622 | try gcd(&r, a, b); | |
| 623 | ||
| 624 | testing.expect((try r.to(u32)) == 4369); | |
| 625 | } | |
| 626 | ||
| 627 | test "big.rational gcd large multi-limb result" { | |
| 628 | var a = try Int.initSet(testing.allocator, 0x12345678123456781234567812345678123456781234567812345678); | |
| 629 | defer a.deinit(); | |
| 630 | var b = try Int.initSet(testing.allocator, 0x12345671234567123456712345671234567123456712345671234567); | |
| 631 | defer b.deinit(); | |
| 632 | var r = try Int.init(testing.allocator); | |
| 633 | defer r.deinit(); | |
| 634 | ||
| 635 | try gcd(&r, a, b); | |
| 636 | ||
| 637 | testing.expect((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1); | |
| 638 | } | |
| 639 | ||
| 640 | test "big.rational gcd one large" { | |
| 641 | var a = try Int.initSet(testing.allocator, 1897056385327307); | |
| 642 | defer a.deinit(); | |
| 643 | var b = try Int.initSet(testing.allocator, 2251799813685248); | |
| 644 | defer b.deinit(); | |
| 645 | var r = try Int.init(testing.allocator); | |
| 646 | defer r.deinit(); | |
| 647 | ||
| 648 | try gcd(&r, a, b); | |
| 649 | ||
| 650 | testing.expect((try r.to(u64)) == 1); | |
| 651 | } | |
| 652 | ||
| 653 | 466 | fn extractLowBits(a: Int, comptime T: type) T { |
| 654 | 467 | testing.expect(@typeInfo(T) == .Int); |
| 655 | 468 |