| ... | @@ -794,11 +794,30 @@ pub const Int = struct { | ... | @@ -794,11 +794,30 @@ pub const Int = struct { |
| 794 | return; | 794 | return; |
| 795 | } | 795 | } |
| 796 | | 796 | |
| 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) { |
| 798 | try quo.ensureCapacity(a.len); | 817 | try quo.ensureCapacity(a.len); |
| 799 | | 818 | |
| 800 | lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[0..a.len], b.limbs[0]); | 819 | lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.len], b.limbs[b.len - 1]); |
| 801 | quo.norm1(a.len); | 820 | quo.norm1(a.len - ab_zero_limb_count); |
| 802 | quo.positive = a.positive == b.positive; | 821 | quo.positive = a.positive == b.positive; |
| 803 | | 822 | |
| 804 | rem.len = 1; | 823 | rem.len = 1; |
| ... | @@ -815,9 +834,25 @@ pub const Int = struct { | ... | @@ -815,9 +834,25 @@ pub const Int = struct { |
| 815 | | 834 | |
| 816 | // x may grow one limb during normalization | 835 | // x may grow one limb during normalization |
| 817 | try quo.ensureCapacity(a.len + y.len); | 836 | 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 | |
| 818 | try divN(quo.allocator.?, quo, rem, &x, &y); | 846 | try divN(quo.allocator.?, quo, rem, &x, &y); |
| 819 | | 847 | |
| 820 | quo.positive = a.positive == b.positive; | 848 | 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 | } |
| 821 | } | 856 | } |
| 822 | } | 857 | } |
| 823 | | 858 | |
| ... | @@ -860,8 +895,11 @@ pub const Int = struct { | ... | @@ -860,8 +895,11 @@ pub const Int = struct { |
| 860 | var tmp = try Int.init(allocator); | 895 | var tmp = try Int.init(allocator); |
| 861 | defer tmp.deinit(); | 896 | defer tmp.deinit(); |
| 862 | | 897 | |
| 863 | // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) | 898 | // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even |
| 864 | const norm_shift = @clz(y.limbs[y.len - 1]); | 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 | } |
| 865 | try x.shiftLeft(x.*, norm_shift); | 903 | try x.shiftLeft(x.*, norm_shift); |
| 866 | try y.shiftLeft(y.*, norm_shift); | 904 | try y.shiftLeft(y.*, norm_shift); |
| 867 | | 905 | |
| ... | @@ -2005,6 +2043,58 @@ test "big.int div multi-multi (3.1/3.3 branch)" { | ... | @@ -2005,6 +2043,58 @@ test "big.int div multi-multi (3.1/3.3 branch)" { |
| 2005 | testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); | 2043 | testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); |
| 2006 | } | 2044 | } |
| 2007 | | 2045 | |
| | 2046 | test "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 | |
| | 2059 | test "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 | |
| | 2071 | test "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 | |
| | 2083 | test "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 | |
| 2008 | test "big.int shift-right single" { | 2098 | test "big.int shift-right single" { |
| 2009 | var a = try Int.initSet(al, 0xffff0000); | 2099 | var a = try Int.initSet(al, 0xffff0000); |
| 2010 | try a.shiftRight(a, 16); | 2100 | try a.shiftRight(a, 16); |