authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-03-28 20:39:57+13:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-04-11 19:36:35+12:00
log30788a98b1bb58886d409464baf7eb01e428d939
treece86c4de456800da9e2f2d773eb0fc2e05aefa0c
parentd3e1f323626ba5955aa98628c9c817a19baa35fb

Handle zero-limb trailing div case in big.Int


1 files changed, 95 insertions(+), 5 deletions(-)

std/math/big/int.zig+95-5
......@@ -794,11 +794,30 @@ pub const Int = struct {
794794 return;
795795 }
796796
797 if (b.len == 1) {
797 // Handle trailing zero-words of divisor/dividend. These are not handled in the following
798 // algorithms.
799 const a_zero_limb_count = blk: {
800 var i: usize = 0;
801 while (i < a.len) : (i += 1) {
802 if (a.limbs[i] != 0) break;
803 }
804 break :blk i;
805 };
806 const b_zero_limb_count = blk: {
807 var i: usize = 0;
808 while (i < b.len) : (i += 1) {
809 if (b.limbs[i] != 0) break;
810 }
811 break :blk i;
812 };
813
814 const ab_zero_limb_count = std.math.min(a_zero_limb_count, b_zero_limb_count);
815
816 if (b.len - ab_zero_limb_count == 1) {
798817 try quo.ensureCapacity(a.len);
799818
800 lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[0..a.len], b.limbs[0]);
801 quo.norm1(a.len);
819 lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.len], b.limbs[b.len - 1]);
820 quo.norm1(a.len - ab_zero_limb_count);
802821 quo.positive = a.positive == b.positive;
803822
804823 rem.len = 1;
......@@ -815,9 +834,25 @@ pub const Int = struct {
815834
816835 // x may grow one limb during normalization
817836 try quo.ensureCapacity(a.len + y.len);
837
838 // Shrink x, y such that the trailing zero limbs shared between are removed.
839 if (ab_zero_limb_count != 0) {
840 std.mem.copy(Limb, x.limbs[0..], x.limbs[ab_zero_limb_count..]);
841 std.mem.copy(Limb, y.limbs[0..], y.limbs[ab_zero_limb_count..]);
842 x.len -= ab_zero_limb_count;
843 y.len -= ab_zero_limb_count;
844 }
845
818846 try divN(quo.allocator.?, quo, rem, &x, &y);
819847
820848 quo.positive = a.positive == b.positive;
849
850 // If dividend had trailing zeros beyond divisor, add extra trailing limbs.
851 // Single-limb division never has multi-limb remainder so nothing to add.
852 if (a_zero_limb_count > b_zero_limb_count) {
853 const shift = a_zero_limb_count - b_zero_limb_count;
854 try rem.shiftLeft(rem.*, shift * Limb.bit_count);
855 }
821856 }
822857 }
823858
......@@ -860,8 +895,11 @@ pub const Int = struct {
860895 var tmp = try Int.init(allocator);
861896 defer tmp.deinit();
862897
863 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set)
864 const norm_shift = @clz(y.limbs[y.len - 1]);
898 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even
899 var norm_shift = @clz(y.limbs[y.len - 1]);
900 if (norm_shift == 0 and y.isOdd()) {
901 norm_shift = Limb.bit_count;
902 }
865903 try x.shiftLeft(x.*, norm_shift);
866904 try y.shiftLeft(y.*, norm_shift);
867905
......@@ -2005,6 +2043,58 @@ test "big.int div multi-multi (3.1/3.3 branch)" {
20052043 testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);
20062044}
20072045
2046test "big.int div multi-single zero-limb trailing" {
2047 var a = try Int.initSet(al, 0x60000000000000000000000000000000000000000000000000000000000000000);
2048 var b = try Int.initSet(al, 0x10000000000000000);
2049
2050 var q = try Int.init(al);
2051 var r = try Int.init(al);
2052 try Int.divTrunc(&q, &r, a, b);
2053
2054 var expected = try Int.initSet(al, 0x6000000000000000000000000000000000000000000000000);
2055 testing.expect(q.eq(expected));
2056 testing.expect(r.eqZero());
2057}
2058
2059test "big.int div multi-multi zero-limb trailing (with rem)" {
2060 var a = try Int.initSet(al, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
2061 var b = try Int.initSet(al, 0x8666666655555555444444443333333300000000000000000000000000000000);
2062
2063 var q = try Int.init(al);
2064 var r = try Int.init(al);
2065 try Int.divTrunc(&q, &r, a, b);
2066
2067 testing.expect((try q.to(u128)) == 0x10000000000000000);
2068 testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111);
2069}
2070
2071test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" {
2072 var a = try Int.initSet(al, 0x8666666655555555888888877777777611111111111111110000000000000000);
2073 var b = try Int.initSet(al, 0x8666666655555555444444443333333300000000000000000000000000000000);
2074
2075 var q = try Int.init(al);
2076 var r = try Int.init(al);
2077 try Int.divTrunc(&q, &r, a, b);
2078
2079 testing.expect((try q.to(u128)) == 0x1);
2080 testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111);
2081}
2082
2083test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" {
2084 var a = try Int.initSet(al, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
2085 var b = try Int.initSet(al, 0x866666665555555544444444333333330000000000000000);
2086
2087 var q = try Int.init(al);
2088 var r = try Int.init(al);
2089 try Int.divTrunc(&q, &r, a, b);
2090
2091 const qs = try q.toString(al, 16);
2092 testing.expect(std.mem.eql(u8, qs, "10000000000000000820820803105186f"));
2093
2094 const rs = try r.toString(al, 16);
2095 testing.expect(std.mem.eql(u8, rs, "4e11f2baa5896a321d463b543d0104e30000000000000000"));
2096}
2097
20082098test "big.int shift-right single" {
20092099 var a = try Int.initSet(al, 0xffff0000);
20102100 try a.shiftRight(a, 16);