authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-10-05 20:36:09+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-05 22:16:26-04:00
logdbc11be038ddb7b9a04535d069a9ee4001f91b51
tree58904c9ac86d9ba08dee741be1d58b0236ea6a23
parent7f7e2d608adb81cd00e54fd7fe5e7035a890565f

std: Fix two bugs in bigint pow

* Correctly scan all the exponent bits, this caused the incorrect result to be computed for exponents being powers of two. * Allocate enough limbs to make llmulacc stop whining.

2 files changed, 45 insertions(+), 34 deletions(-)

lib/std/math/big/int.zig+38-34
...@@ -59,8 +59,8 @@ pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {...@@ -59,8 +59,8 @@ pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {
59}59}
6060
61pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize {61pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize {
62 // The 1 accounts for the multiplication carry62 // The 2 accounts for the minimum space requirement for llmulacc
63 return 1 + (a_bit_count * y + (limb_bits - 1)) / limb_bits;63 return 2 + (a_bit_count * y + (limb_bits - 1)) / limb_bits;
64}64}
6565
66/// a + b * c + *carry, sets carry to the overflow bits66/// a + b * c + *carry, sets carry to the overflow bits
...@@ -2205,47 +2205,51 @@ fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {...@@ -2205,47 +2205,51 @@ fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
22052205
2206/// Knuth 4.6.32206/// Knuth 4.6.3
2207fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {2207fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
2208 mem.copy(Limb, r, a);2208 var tmp1: []Limb = undefined;
2209 mem.set(Limb, r[a.len..], 0);2209 var tmp2: []Limb = undefined;
22102210
2211 // Multiplication requires no aliasing between the operand and the result2211 // Multiplication requires no aliasing between the operand and the result
2212 // variable, use the output limbs and another temporary set to overcome this2212 // variable, use the output limbs and another temporary set to overcome this
2213 // limit.2213 // limitation.
2214 // Note that the order is important in the code below.2214 // The initial assignment makes the result end in `r` so an extra memory
2215 var list = [_][]Limb{ r, tmp_limbs };2215 // copy is saved, each 1 flips the index twice so it's a no-op so count the
2216 var index: usize = 0;2216 // 0.
2217 const b_leading_zeros = @intCast(u5, @clz(u32, b));
2218 const exp_zeros = @popCount(u32, ~b) - b_leading_zeros;
2219 if (exp_zeros & 1 != 0) {
2220 tmp1 = tmp_limbs;
2221 tmp2 = r;
2222 } else {
2223 tmp1 = r;
2224 tmp2 = tmp_limbs;
2225 }
2226
2227 const a_norm = a[0..llnormalize(a)];
2228
2229 mem.copy(Limb, tmp1, a_norm);
2230 mem.set(Limb, tmp1[a_norm.len..], 0);
22172231
2218 // Scan the exponent as a binary number, from left to right, dropping the2232 // Scan the exponent as a binary number, from left to right, dropping the
2219 // most significant bit set2233 // most significant bit set.
2220 var exp = @bitReverse(u32, b) >> (1 + @intCast(u5, @clz(u32, b)));2234 const exp_bits = @intCast(u5, 31 - b_leading_zeros);
2221 while (exp != 0) : (exp >>= 1) {2235 var exp = @bitReverse(u32, b) >> 1 + b_leading_zeros;
2236
2237 var i: u5 = 0;
2238 while (i < exp_bits) : (i += 1) {
2222 // Square2239 // Square
2223 {2240 {
2224 const cur_buf = list[index];2241 mem.set(Limb, tmp2, 0);
2225 const cur_buf_len = llnormalize(cur_buf);2242 const op = tmp1[0..llnormalize(tmp1)];
2226 const cur_buf_out = list[index ^ 1];2243 llmulacc(null, tmp2, op, op);
22272244 mem.swap([]Limb, &tmp1, &tmp2);
2228 mem.set(Limb, cur_buf_out, 0);
2229 llmulacc(null, cur_buf_out, cur_buf[0..cur_buf_len], cur_buf[0..cur_buf_len]);
2230
2231 index ^= 1;
2232 }2245 }
22332246 // Multiply by a
2234 if ((exp & 1) != 0) {2247 if (exp & 1 != 0) {
2235 // Multiply2248 mem.set(Limb, tmp2, 0);
2236 const cur_buf = list[index];2249 llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a_norm);
2237 const cur_buf_len = llnormalize(cur_buf);2250 mem.swap([]Limb, &tmp1, &tmp2);
2238 const cur_buf_out = list[index ^ 1];
2239
2240 mem.set(Limb, cur_buf_out, 0);
2241 llmulacc(null, cur_buf_out, cur_buf, a);
2242
2243 index ^= 1;
2244 }2251 }
2245 }2252 exp >>= 1;
2246
2247 if (index != 0) {
2248 mem.copy(Limb, r, tmp_limbs);
2249 }2253 }
2250}2254}
22512255
lib/std/math/big/int_test.zig+7
...@@ -1482,6 +1482,13 @@ test "big.int const to managed" {...@@ -1482,6 +1482,13 @@ test "big.int const to managed" {
1482}1482}
14831483
1484test "big.int pow" {1484test "big.int pow" {
1485 {
1486 var a = try Managed.initSet(testing.allocator, 10);
1487 defer a.deinit();
1488
1489 try a.pow(a, 8);
1490 testing.expectEqual(@as(u32, 100000000), try a.to(u32));
1491 }
1485 {1492 {
1486 var a = try Managed.initSet(testing.allocator, 10);1493 var a = try Managed.initSet(testing.allocator, 10);
1487 defer a.deinit();1494 defer a.deinit();