| ... | ... | @@ -59,8 +59,8 @@ pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize { |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | 61 | pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize { |
| 62 | | // The 1 accounts for the multiplication carry |
| 63 | | return 1 + (a_bit_count * y + (limb_bits - 1)) / limb_bits; |
| 62 | // The 2 accounts for the minimum space requirement for llmulacc |
| 63 | return 2 + (a_bit_count * y + (limb_bits - 1)) / limb_bits; |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | 66 | /// 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 | 2205 | |
| 2206 | 2206 | /// Knuth 4.6.3 |
| 2207 | 2207 | fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void { |
| 2208 | | mem.copy(Limb, r, a); |
| 2209 | | mem.set(Limb, r[a.len..], 0); |
| 2208 | var tmp1: []Limb = undefined; |
| 2209 | var tmp2: []Limb = undefined; |
| 2210 | 2210 | |
| 2211 | 2211 | // Multiplication requires no aliasing between the operand and the result |
| 2212 | 2212 | // variable, use the output limbs and another temporary set to overcome this |
| 2213 | | // limit. |
| 2214 | | // Note that the order is important in the code below. |
| 2215 | | var list = [_][]Limb{ r, tmp_limbs }; |
| 2216 | | var index: usize = 0; |
| 2213 | // limitation. |
| 2214 | // The initial assignment makes the result end in `r` so an extra memory |
| 2215 | // copy is saved, each 1 flips the index twice so it's a no-op so count the |
| 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); |
| 2217 | 2231 | |
| 2218 | 2232 | // Scan the exponent as a binary number, from left to right, dropping the |
| 2219 | | // most significant bit set |
| 2220 | | var exp = @bitReverse(u32, b) >> (1 + @intCast(u5, @clz(u32, b))); |
| 2221 | | while (exp != 0) : (exp >>= 1) { |
| 2233 | // most significant bit set. |
| 2234 | const exp_bits = @intCast(u5, 31 - b_leading_zeros); |
| 2235 | var exp = @bitReverse(u32, b) >> 1 + b_leading_zeros; |
| 2236 | |
| 2237 | var i: u5 = 0; |
| 2238 | while (i < exp_bits) : (i += 1) { |
| 2222 | 2239 | // Square |
| 2223 | 2240 | { |
| 2224 | | const cur_buf = list[index]; |
| 2225 | | const cur_buf_len = llnormalize(cur_buf); |
| 2226 | | const cur_buf_out = list[index ^ 1]; |
| 2227 | | |
| 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; |
| 2241 | mem.set(Limb, tmp2, 0); |
| 2242 | const op = tmp1[0..llnormalize(tmp1)]; |
| 2243 | llmulacc(null, tmp2, op, op); |
| 2244 | mem.swap([]Limb, &tmp1, &tmp2); |
| 2232 | 2245 | } |
| 2233 | | |
| 2234 | | if ((exp & 1) != 0) { |
| 2235 | | // Multiply |
| 2236 | | const cur_buf = list[index]; |
| 2237 | | const cur_buf_len = llnormalize(cur_buf); |
| 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; |
| 2246 | // Multiply by a |
| 2247 | if (exp & 1 != 0) { |
| 2248 | mem.set(Limb, tmp2, 0); |
| 2249 | llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a_norm); |
| 2250 | mem.swap([]Limb, &tmp1, &tmp2); |
| 2244 | 2251 | } |
| 2245 | | } |
| 2246 | | |
| 2247 | | if (index != 0) { |
| 2248 | | mem.copy(Limb, r, tmp_limbs); |
| 2252 | exp >>= 1; |
| 2249 | 2253 | } |
| 2250 | 2254 | } |
| 2251 | 2255 | |