| ... | ... | @@ -86,6 +86,24 @@ pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { |
| 86 | 86 | return r1; |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | /// a - b * c - *carry, sets carry to the overflow bits |
| 90 | fn subMulLimbWithBorrow(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { |
| 91 | // r1 = a - *carry |
| 92 | var r1: Limb = undefined; |
| 93 | const c1: Limb = @boolToInt(@subWithOverflow(Limb, a, carry.*, &r1)); |
| 94 | |
| 95 | // r2 = b * c |
| 96 | const bc = @as(DoubleLimb, std.math.mulWide(Limb, b, c)); |
| 97 | const r2 = @truncate(Limb, bc); |
| 98 | const c2 = @truncate(Limb, bc >> limb_bits); |
| 99 | |
| 100 | // r1 = r1 - r2 |
| 101 | const c3: Limb = @boolToInt(@subWithOverflow(Limb, r1, r2, &r1)); |
| 102 | carry.* = c1 + c2 + c3; |
| 103 | |
| 104 | return r1; |
| 105 | } |
| 106 | |
| 89 | 107 | /// Used to indicate either limit of a 2s-complement integer. |
| 90 | 108 | pub const TwosCompIntLimit = enum { |
| 91 | 109 | // The low limit, either 0x00 (unsigned) or (-)0x80 (signed) for an 8-bit integer. |
| ... | ... | @@ -640,7 +658,7 @@ pub const Mutable = struct { |
| 640 | 658 | |
| 641 | 659 | mem.set(Limb, rma.limbs[0 .. a.limbs.len + b.limbs.len + 1], 0); |
| 642 | 660 | |
| 643 | | llmulacc(allocator, rma.limbs, a.limbs, b.limbs); |
| 661 | llmulacc(.add, allocator, rma.limbs, a.limbs, b.limbs); |
| 644 | 662 | |
| 645 | 663 | rma.normalize(a.limbs.len + b.limbs.len); |
| 646 | 664 | rma.positive = (a.positive == b.positive); |
| ... | ... | @@ -665,9 +683,9 @@ pub const Mutable = struct { |
| 665 | 683 | mem.set(Limb, rma.limbs[0..req_limbs], 0); |
| 666 | 684 | |
| 667 | 685 | if (a_limbs.len >= b_limbs.len) { |
| 668 | | llmulacc_lo(rma.limbs, a_limbs, b_limbs); |
| 686 | llmulaccLow(rma.limbs, a_limbs, b_limbs); |
| 669 | 687 | } else { |
| 670 | | llmulacc_lo(rma.limbs, b_limbs, a_limbs); |
| 688 | llmulaccLow(rma.limbs, b_limbs, a_limbs); |
| 671 | 689 | } |
| 672 | 690 | |
| 673 | 691 | rma.normalize(math.min(req_limbs, a.limbs.len + b.limbs.len)); |
| ... | ... | @@ -691,7 +709,7 @@ pub const Mutable = struct { |
| 691 | 709 | |
| 692 | 710 | mem.set(Limb, rma.limbs, 0); |
| 693 | 711 | |
| 694 | | llsquare_basecase(rma.limbs, a.limbs); |
| 712 | llsquareBasecase(rma.limbs, a.limbs); |
| 695 | 713 | |
| 696 | 714 | rma.normalize(2 * a.limbs.len + 1); |
| 697 | 715 | rma.positive = true; |
| ... | ... | @@ -1219,7 +1237,7 @@ pub const Mutable = struct { |
| 1219 | 1237 | /// Asserts `r` has enough storage to store the result. |
| 1220 | 1238 | /// The upper bound is `calcTwosCompLimbCount(a.len)`. |
| 1221 | 1239 | pub fn truncate(r: *Mutable, a: Const, signedness: std.builtin.Signedness, bit_count: usize) void { |
| 1222 | | const req_limbs = (bit_count + @bitSizeOf(Limb) - 1) / @bitSizeOf(Limb); |
| 1240 | const req_limbs = calcTwosCompLimbCount(bit_count); |
| 1223 | 1241 | |
| 1224 | 1242 | // Handle 0-bit integers. |
| 1225 | 1243 | if (req_limbs == 0 or a.eqZero()) { |
| ... | ... | @@ -2319,8 +2337,8 @@ pub const Managed = struct { |
| 2319 | 2337 | } |
| 2320 | 2338 | }; |
| 2321 | 2339 | |
| 2322 | | /// r = a * b, ignoring overflow |
| 2323 | | fn llmulacc_lo(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2340 | /// r = r + a * b, ignoring overflow |
| 2341 | fn llmulaccLow(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2324 | 2342 | assert(r.len >= a.len); |
| 2325 | 2343 | assert(a.len >= b.len); |
| 2326 | 2344 | |
| ... | ... | @@ -2328,32 +2346,41 @@ fn llmulacc_lo(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2328 | 2346 | |
| 2329 | 2347 | var i: usize = 0; |
| 2330 | 2348 | while (i < b.len) : (i += 1) { |
| 2331 | | llmulDigit(r[i..], a, b[i]); |
| 2349 | llmulLimb(.add, r[i..], a, b[i]); |
| 2332 | 2350 | } |
| 2333 | 2351 | } |
| 2334 | 2352 | |
| 2353 | /// Different operators which can be used in accumulation style functions |
| 2354 | /// (llmulacc, llmulaccKaratsuba, llmulaccLong, llmulLimb). In all these functions, |
| 2355 | /// a computed value is accumulated with an existing result. |
| 2356 | const AccOp = enum { |
| 2357 | /// The computed value is added to the result. |
| 2358 | add, |
| 2359 | |
| 2360 | /// The computed value is subtracted from the result. |
| 2361 | sub, |
| 2362 | }; |
| 2363 | |
| 2335 | 2364 | /// Knuth 4.3.1, Algorithm M. |
| 2336 | 2365 | /// |
| 2366 | /// r = r (op) a * b |
| 2337 | 2367 | /// r MUST NOT alias any of a or b. |
| 2338 | | fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2368 | fn llmulacc(comptime op: AccOp, opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2339 | 2369 | @setRuntimeSafety(debug_safety); |
| 2370 | assert(r.len >= a.len + b.len); |
| 2340 | 2371 | |
| 2341 | | const a_norm = a[0..llnormalize(a)]; |
| 2342 | | const b_norm = b[0..llnormalize(b)]; |
| 2343 | | var x = a_norm; |
| 2344 | | var y = b_norm; |
| 2345 | | if (a_norm.len > b_norm.len) { |
| 2346 | | x = b_norm; |
| 2347 | | y = a_norm; |
| 2372 | // Order greatest first. |
| 2373 | var x = a; |
| 2374 | var y = b; |
| 2375 | if (a.len < b.len) { |
| 2376 | x = b; |
| 2377 | y = a; |
| 2348 | 2378 | } |
| 2349 | 2379 | |
| 2350 | | assert(r.len >= x.len + y.len + 1); |
| 2351 | | |
| 2352 | | // 48 is a pretty abitrary size chosen based on performance of a factorial program. |
| 2353 | 2380 | k_mul: { |
| 2354 | | if (x.len > 48) { |
| 2381 | if (y.len > 48) { |
| 2355 | 2382 | if (opt_allocator) |allocator| { |
| 2356 | | llmulacc_karatsuba(allocator, r, x, y) catch |err| switch (err) { |
| 2383 | llmulaccKaratsuba(op, allocator, r, x, y) catch |err| switch (err) { |
| 2357 | 2384 | error.OutOfMemory => break :k_mul, // handled below |
| 2358 | 2385 | }; |
| 2359 | 2386 | return; |
| ... | ... | @@ -2361,83 +2388,153 @@ fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const L |
| 2361 | 2388 | } |
| 2362 | 2389 | } |
| 2363 | 2390 | |
| 2364 | | // Basecase multiplication |
| 2365 | | var i: usize = 0; |
| 2366 | | while (i < x.len) : (i += 1) { |
| 2367 | | llmulDigit(r[i..], y, x[i]); |
| 2368 | | } |
| 2391 | llmulaccLong(op, r, x, y); |
| 2369 | 2392 | } |
| 2370 | 2393 | |
| 2371 | 2394 | /// Knuth 4.3.1, Algorithm M. |
| 2372 | 2395 | /// |
| 2396 | /// r = r (op) a * b |
| 2373 | 2397 | /// r MUST NOT alias any of a or b. |
| 2374 | | fn llmulacc_karatsuba(allocator: *Allocator, r: []Limb, x: []const Limb, y: []const Limb) error{OutOfMemory}!void { |
| 2398 | fn llmulaccKaratsuba( |
| 2399 | comptime op: AccOp, |
| 2400 | allocator: *Allocator, |
| 2401 | r: []Limb, |
| 2402 | a: []const Limb, |
| 2403 | b: []const Limb, |
| 2404 | ) error{OutOfMemory}!void { |
| 2375 | 2405 | @setRuntimeSafety(debug_safety); |
| 2406 | assert(r.len >= a.len + b.len); |
| 2407 | assert(a.len >= b.len); |
| 2376 | 2408 | |
| 2377 | | assert(r.len >= x.len + y.len + 1); |
| 2378 | | |
| 2379 | | const split = @divFloor(x.len, 2); |
| 2380 | | var x0 = x[0..split]; |
| 2381 | | var x1 = x[split..x.len]; |
| 2382 | | var y0 = y[0..split]; |
| 2383 | | var y1 = y[split..y.len]; |
| 2384 | | |
| 2385 | | var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1); |
| 2409 | // Classical karatsuba algorithm: |
| 2410 | // a = a1 * B + a0 |
| 2411 | // b = b1 * B + b0 |
| 2412 | // Where a0, b0 < B |
| 2413 | // |
| 2414 | // We then have: |
| 2415 | // ab = a * b |
| 2416 | // = (a1 * B + a0) * (b1 * B + b0) |
| 2417 | // = a1 * b1 * B * B + a1 * B * b0 + a0 * b1 * B + a0 * b0 |
| 2418 | // = a1 * b1 * B * B + (a1 * b0 + a0 * b1) * B + a0 * b0 |
| 2419 | // |
| 2420 | // Note that: |
| 2421 | // a1 * b0 + a0 * b1 |
| 2422 | // = (a1 + a0)(b1 + b0) - a1 * b1 - a0 * b0 |
| 2423 | // = (a0 - a1)(b1 - b0) + a1 * b1 + a0 * b0 |
| 2424 | // |
| 2425 | // This yields: |
| 2426 | // ab = p2 * B^2 + (p0 + p1 + p2) * B + p0 |
| 2427 | // |
| 2428 | // Where: |
| 2429 | // p0 = a0 * b0 |
| 2430 | // p1 = (a0 - a1)(b1 - b0) |
| 2431 | // p2 = a1 * b1 |
| 2432 | // |
| 2433 | // Note, (a0 - a1) and (b1 - b0) produce values -B < x < B, and so we need to mind the sign here. |
| 2434 | // We also have: |
| 2435 | // 0 <= p0 <= 2B |
| 2436 | // -2B <= p1 <= 2B |
| 2437 | // |
| 2438 | // Note, when B is a multiple of the limb size, multiplies by B amount to shifts or |
| 2439 | // slices of a limbs array. |
| 2440 | |
| 2441 | const split = b.len / 2; // B |
| 2442 | const a0 = a[0..llnormalize(a[0..split])]; |
| 2443 | const a1 = a[split..][0..llnormalize(a[split..])]; |
| 2444 | const b0 = b[0..llnormalize(b[0..split])]; |
| 2445 | const b1 = b[split..][0..llnormalize(b[split..])]; |
| 2446 | |
| 2447 | // Note that the above slices work because we have a.len > b.len. |
| 2448 | // We now also have: |
| 2449 | // a1.len >= a0.len |
| 2450 | // a1.len >= b1.len >= b0.len |
| 2451 | // a0.len == b0.len |
| 2452 | |
| 2453 | // We need some temporary memory to store intermediate results. |
| 2454 | // Note, we can reduce the amount of temporaries we need by reordering the computation here: |
| 2455 | // ab = p2 * B^2 + (p0 + p1 + p2) * B + p0 |
| 2456 | // = p2 * B^2 + (p0 * B + p1 * B + p2 * B) + p0 |
| 2457 | // = (p2 * B^2 + p2 * B) + (p0 * B + p0) + p1 * B |
| 2458 | // By allocating a1.len * b1.len we can be sure that all the intermediary results fit. |
| 2459 | const tmp = try allocator.alloc(Limb, a.len - split + b.len - split); |
| 2386 | 2460 | defer allocator.free(tmp); |
| 2387 | | mem.set(Limb, tmp, 0); |
| 2388 | 2461 | |
| 2389 | | llmulacc(allocator, tmp, x1, y1); |
| 2462 | // Compute p2. |
| 2463 | mem.set(Limb, tmp, 0); |
| 2464 | llmulacc(.add, allocator, tmp, a1, b1); |
| 2465 | const p2 = tmp[0 .. llnormalize(tmp)]; |
| 2390 | 2466 | |
| 2391 | | var length = llnormalize(tmp); |
| 2392 | | _ = llaccum(r[split..], tmp[0..length]); |
| 2393 | | _ = llaccum(r[split * 2 ..], tmp[0..length]); |
| 2467 | // Add terms p2 * B^2 and p2 * B to the result. |
| 2468 | _ = llaccum(op, r[split..], p2); |
| 2469 | _ = llaccum(op, r[split * 2..], p2); |
| 2394 | 2470 | |
| 2395 | | mem.set(Limb, tmp[0..length], 0); |
| 2471 | // Compute p0. |
| 2472 | mem.set(Limb, p2, 0); |
| 2473 | llmulacc(.add, allocator, tmp, a0, b0); |
| 2474 | const p0 = tmp[0 .. llnormalize(tmp[0..a0.len + b0.len])]; |
| 2396 | 2475 | |
| 2397 | | llmulacc(allocator, tmp, x0, y0); |
| 2476 | // Add terms p0 * B and p0 to the result. |
| 2477 | _ = llaccum(op, r, p0); |
| 2478 | _ = llaccum(op, r[split..], p0); |
| 2398 | 2479 | |
| 2399 | | length = llnormalize(tmp); |
| 2400 | | _ = llaccum(r[0..], tmp[0..length]); |
| 2401 | | _ = llaccum(r[split..], tmp[0..length]); |
| 2480 | // Finally, compute and add p1. |
| 2481 | const j0_sign = llcmp(a0, a1); |
| 2482 | const j1_sign = llcmp(b1, b0); |
| 2402 | 2483 | |
| 2403 | | const x_cmp = llcmp(x1, x0); |
| 2404 | | const y_cmp = llcmp(y1, y0); |
| 2405 | | if (x_cmp * y_cmp == 0) { |
| 2484 | if (j0_sign * j1_sign == 0) { |
| 2485 | // p1 is zero, we don't need to do any computation at all. |
| 2406 | 2486 | return; |
| 2407 | 2487 | } |
| 2408 | | const x0_len = llnormalize(x0); |
| 2409 | | const x1_len = llnormalize(x1); |
| 2410 | | var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len)); |
| 2411 | | defer allocator.free(j0); |
| 2412 | | if (x_cmp == 1) { |
| 2413 | | llsub(j0, x1[0..x1_len], x0[0..x0_len]); |
| 2488 | |
| 2489 | mem.set(Limb, tmp, 0); |
| 2490 | |
| 2491 | // p1 is nonzero, so compute the intermediary terms j0 = a0 - a1 and j1 = b1 - b0. |
| 2492 | // Note that in this case, we again need some storage for intermediary results |
| 2493 | // j0 and j1. Since we have tmp.len >= 2B, we can store both |
| 2494 | // intermediaries in the already allocated array. |
| 2495 | const j0 = tmp[0..a1.len]; |
| 2496 | const j1 = tmp[a1.len..]; |
| 2497 | |
| 2498 | // Ensure that no subtraction overflows. |
| 2499 | if (j0_sign == 1) { |
| 2500 | // a0 > a1. |
| 2501 | _ = llsubcarry(j0, a0, a1); |
| 2414 | 2502 | } else { |
| 2415 | | llsub(j0, x0[0..x0_len], x1[0..x1_len]); |
| 2503 | // a0 < a1. |
| 2504 | _ = llsubcarry(j0, a1, a0); |
| 2416 | 2505 | } |
| 2417 | 2506 | |
| 2418 | | const y0_len = llnormalize(y0); |
| 2419 | | const y1_len = llnormalize(y1); |
| 2420 | | var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len)); |
| 2421 | | defer allocator.free(j1); |
| 2422 | | if (y_cmp == 1) { |
| 2423 | | llsub(j1, y1[0..y1_len], y0[0..y0_len]); |
| 2507 | if (j1_sign == 1) { |
| 2508 | // b1 > b0. |
| 2509 | _ = llsubcarry(j1, b1, b0); |
| 2424 | 2510 | } else { |
| 2425 | | llsub(j1, y0[0..y0_len], y1[0..y1_len]); |
| 2511 | // b1 > b0. |
| 2512 | _ = llsubcarry(j1, b0, b1); |
| 2426 | 2513 | } |
| 2427 | | if (x_cmp == y_cmp) { |
| 2428 | | mem.set(Limb, tmp[0..length], 0); |
| 2429 | | llmulacc(allocator, tmp, j0, j1); |
| 2430 | 2514 | |
| 2431 | | length = llnormalize(tmp); |
| 2432 | | llsub(r[split..], r[split..], tmp[0..length]); |
| 2515 | if (j0_sign * j1_sign == 1) { |
| 2516 | // If j0 and j1 are both positive, we now have: |
| 2517 | // p1 = j0 * j1 |
| 2518 | // If j0 and j1 are both negative, we now have: |
| 2519 | // p1 = -j0 * -j1 = j0 * j1 |
| 2520 | // In this case we can add p1 to the result using llmulacc. |
| 2521 | llmulacc(op, allocator, r[split..], j0[0..llnormalize(j0)], j1[0..llnormalize(j1)]); |
| 2433 | 2522 | } else { |
| 2434 | | llmulacc(allocator, r[split..], j0, j1); |
| 2523 | // In this case either j0 or j1 is negative, an we have: |
| 2524 | // p1 = -(j0 * j1) |
| 2525 | // Now we need to subtract instead of accumulate. |
| 2526 | const inverted_op = if (op == .add) .sub else .add; |
| 2527 | llmulacc(inverted_op, allocator, r[split..], j0[0..llnormalize(j0)], j1[0..llnormalize(j1)]); |
| 2435 | 2528 | } |
| 2436 | 2529 | } |
| 2437 | 2530 | |
| 2438 | | // r = r + a |
| 2439 | | fn llaccum(r: []Limb, a: []const Limb) Limb { |
| 2531 | // r = r (op) a |
| 2532 | fn llaccum(comptime op: AccOp, r: []Limb, a: []const Limb) Limb { |
| 2440 | 2533 | @setRuntimeSafety(debug_safety); |
| 2534 | if (op == .sub) { |
| 2535 | return llsubcarry(r, r, a); |
| 2536 | } |
| 2537 | |
| 2441 | 2538 | assert(r.len != 0 and a.len != 0); |
| 2442 | 2539 | assert(r.len >= a.len); |
| 2443 | 2540 | |
| ... | ... | @@ -2486,24 +2583,53 @@ pub fn llcmp(a: []const Limb, b: []const Limb) i8 { |
| 2486 | 2583 | } |
| 2487 | 2584 | } |
| 2488 | 2585 | |
| 2489 | | fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void { |
| 2586 | // r = r (op) y * xi |
| 2587 | fn llmulaccLong(comptime op: AccOp, r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2588 | @setRuntimeSafety(debug_safety); |
| 2589 | assert(r.len >= a.len + b.len); |
| 2590 | assert(a.len >= b.len); |
| 2591 | |
| 2592 | var i: usize = 0; |
| 2593 | while (i < a.len) : (i += 1) { |
| 2594 | llmulLimb(op, r[i..], b, a[i]); |
| 2595 | } |
| 2596 | } |
| 2597 | |
| 2598 | // r = r (op) y * xi |
| 2599 | fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) void { |
| 2490 | 2600 | @setRuntimeSafety(debug_safety); |
| 2491 | 2601 | if (xi == 0) { |
| 2492 | 2602 | return; |
| 2493 | 2603 | } |
| 2494 | 2604 | |
| 2495 | | var carry: Limb = 0; |
| 2496 | 2605 | var a_lo = acc[0..y.len]; |
| 2497 | 2606 | var a_hi = acc[y.len..]; |
| 2498 | 2607 | |
| 2499 | | var j: usize = 0; |
| 2500 | | while (j < a_lo.len) : (j += 1) { |
| 2501 | | a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry }); |
| 2502 | | } |
| 2608 | switch (op) { |
| 2609 | .add => { |
| 2610 | var carry: Limb = 0; |
| 2611 | var j: usize = 0; |
| 2612 | while (j < a_lo.len) : (j += 1) { |
| 2613 | a_lo[j] = addMulLimbWithCarry(a_lo[j], y[j], xi, &carry); |
| 2614 | } |
| 2503 | 2615 | |
| 2504 | | j = 0; |
| 2505 | | while ((carry != 0) and (j < a_hi.len)) : (j += 1) { |
| 2506 | | carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j])); |
| 2616 | j = 0; |
| 2617 | while ((carry != 0) and (j < a_hi.len)) : (j += 1) { |
| 2618 | carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j])); |
| 2619 | } |
| 2620 | }, |
| 2621 | .sub => { |
| 2622 | var borrow: Limb = 0; |
| 2623 | var j: usize = 0; |
| 2624 | while (j < a_lo.len) : (j += 1) { |
| 2625 | a_lo[j] = subMulLimbWithBorrow(a_lo[j], y[j], xi, &borrow); |
| 2626 | } |
| 2627 | |
| 2628 | j = 0; |
| 2629 | while ((borrow != 0) and (j < a_hi.len)) : (j += 1) { |
| 2630 | borrow = @boolToInt(@subWithOverflow(Limb, a_hi[j], borrow, &a_hi[j])); |
| 2631 | } |
| 2632 | }, |
| 2507 | 2633 | } |
| 2508 | 2634 | } |
| 2509 | 2635 | |
| ... | ... | @@ -2964,7 +3090,7 @@ fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_ |
| 2964 | 3090 | } |
| 2965 | 3091 | |
| 2966 | 3092 | /// r MUST NOT alias x. |
| 2967 | | fn llsquare_basecase(r: []Limb, x: []const Limb) void { |
| 3093 | fn llsquareBasecase(r: []Limb, x: []const Limb) void { |
| 2968 | 3094 | @setRuntimeSafety(debug_safety); |
| 2969 | 3095 | |
| 2970 | 3096 | const x_norm = x; |
| ... | ... | @@ -2987,7 +3113,7 @@ fn llsquare_basecase(r: []Limb, x: []const Limb) void { |
| 2987 | 3113 | |
| 2988 | 3114 | for (x_norm) |v, i| { |
| 2989 | 3115 | // Accumulate all the x[i]*x[j] (with x!=j) products |
| 2990 | | llmulDigit(r[2 * i + 1 ..], x_norm[i + 1 ..], v); |
| 3116 | llmulLimb(.add, r[2 * i + 1 ..], x_norm[i + 1 ..], v); |
| 2991 | 3117 | } |
| 2992 | 3118 | |
| 2993 | 3119 | // Each product appears twice, multiply by 2 |
| ... | ... | @@ -2995,7 +3121,7 @@ fn llsquare_basecase(r: []Limb, x: []const Limb) void { |
| 2995 | 3121 | |
| 2996 | 3122 | for (x_norm) |v, i| { |
| 2997 | 3123 | // Compute and add the squares |
| 2998 | | llmulDigit(r[2 * i ..], x[i .. i + 1], v); |
| 3124 | llmulLimb(.add, r[2 * i ..], x[i .. i + 1], v); |
| 2999 | 3125 | } |
| 3000 | 3126 | } |
| 3001 | 3127 | |
| ... | ... | @@ -3034,12 +3160,12 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void { |
| 3034 | 3160 | while (i < exp_bits) : (i += 1) { |
| 3035 | 3161 | // Square |
| 3036 | 3162 | mem.set(Limb, tmp2, 0); |
| 3037 | | llsquare_basecase(tmp2, tmp1[0..llnormalize(tmp1)]); |
| 3163 | llsquareBasecase(tmp2, tmp1[0..llnormalize(tmp1)]); |
| 3038 | 3164 | mem.swap([]Limb, &tmp1, &tmp2); |
| 3039 | 3165 | // Multiply by a |
| 3040 | 3166 | if (@shlWithOverflow(u32, exp, 1, &exp)) { |
| 3041 | 3167 | mem.set(Limb, tmp2, 0); |
| 3042 | | llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a); |
| 3168 | llmulacc(.add, null, tmp2, tmp1[0..llnormalize(tmp1)], a); |
| 3043 | 3169 | mem.swap([]Limb, &tmp1, &tmp2); |
| 3044 | 3170 | } |
| 3045 | 3171 | } |