| ... | @@ -86,6 +86,24 @@ pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { | ... | @@ -86,6 +86,24 @@ pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { |
| 86 | return r1; | 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 | /// Used to indicate either limit of a 2s-complement integer. | 107 | /// Used to indicate either limit of a 2s-complement integer. |
| 90 | pub const TwosCompIntLimit = enum { | 108 | pub const TwosCompIntLimit = enum { |
| 91 | // The low limit, either 0x00 (unsigned) or (-)0x80 (signed) for an 8-bit integer. | 109 | // The low limit, either 0x00 (unsigned) or (-)0x80 (signed) for an 8-bit integer. |
| ... | @@ -640,7 +658,7 @@ pub const Mutable = struct { | ... | @@ -640,7 +658,7 @@ pub const Mutable = struct { |
| 640 | | 658 | |
| 641 | mem.set(Limb, rma.limbs[0 .. a.limbs.len + b.limbs.len + 1], 0); | 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 | rma.normalize(a.limbs.len + b.limbs.len); | 663 | rma.normalize(a.limbs.len + b.limbs.len); |
| 646 | rma.positive = (a.positive == b.positive); | 664 | rma.positive = (a.positive == b.positive); |
| ... | @@ -665,9 +683,9 @@ pub const Mutable = struct { | ... | @@ -665,9 +683,9 @@ pub const Mutable = struct { |
| 665 | mem.set(Limb, rma.limbs[0..req_limbs], 0); | 683 | mem.set(Limb, rma.limbs[0..req_limbs], 0); |
| 666 | | 684 | |
| 667 | if (a_limbs.len >= b_limbs.len) { | 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 | } else { | 687 | } else { |
| 670 | llmulacc_lo(rma.limbs, b_limbs, a_limbs); | 688 | llmulaccLow(rma.limbs, b_limbs, a_limbs); |
| 671 | } | 689 | } |
| 672 | | 690 | |
| 673 | rma.normalize(math.min(req_limbs, a.limbs.len + b.limbs.len)); | 691 | rma.normalize(math.min(req_limbs, a.limbs.len + b.limbs.len)); |
| ... | @@ -691,7 +709,7 @@ pub const Mutable = struct { | ... | @@ -691,7 +709,7 @@ pub const Mutable = struct { |
| 691 | | 709 | |
| 692 | mem.set(Limb, rma.limbs, 0); | 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 | rma.normalize(2 * a.limbs.len + 1); | 714 | rma.normalize(2 * a.limbs.len + 1); |
| 697 | rma.positive = true; | 715 | rma.positive = true; |
| ... | @@ -1219,7 +1237,7 @@ pub const Mutable = struct { | ... | @@ -1219,7 +1237,7 @@ pub const Mutable = struct { |
| 1219 | /// Asserts `r` has enough storage to store the result. | 1237 | /// Asserts `r` has enough storage to store the result. |
| 1220 | /// The upper bound is `calcTwosCompLimbCount(a.len)`. | 1238 | /// The upper bound is `calcTwosCompLimbCount(a.len)`. |
| 1221 | pub fn truncate(r: *Mutable, a: Const, signedness: std.builtin.Signedness, bit_count: usize) void { | 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 | // Handle 0-bit integers. | 1242 | // Handle 0-bit integers. |
| 1225 | if (req_limbs == 0 or a.eqZero()) { | 1243 | if (req_limbs == 0 or a.eqZero()) { |
| ... | @@ -2319,8 +2337,8 @@ pub const Managed = struct { | ... | @@ -2319,8 +2337,8 @@ pub const Managed = struct { |
| 2319 | } | 2337 | } |
| 2320 | }; | 2338 | }; |
| 2321 | | 2339 | |
| 2322 | /// r = a * b, ignoring overflow | 2340 | /// r = r + a * b, ignoring overflow |
| 2323 | fn llmulacc_lo(r: []Limb, a: []const Limb, b: []const Limb) void { | 2341 | fn llmulaccLow(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2324 | assert(r.len >= a.len); | 2342 | assert(r.len >= a.len); |
| 2325 | assert(a.len >= b.len); | 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,32 +2346,41 @@ fn llmulacc_lo(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2328 | | 2346 | |
| 2329 | var i: usize = 0; | 2347 | var i: usize = 0; |
| 2330 | while (i < b.len) : (i += 1) { | 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 | /// Knuth 4.3.1, Algorithm M. | 2364 | /// Knuth 4.3.1, Algorithm M. |
| 2336 | /// | 2365 | /// |
| | 2366 | /// r = r (op) a * b |
| 2337 | /// r MUST NOT alias any of a or b. | 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 | @setRuntimeSafety(debug_safety); | 2369 | @setRuntimeSafety(debug_safety); |
| | 2370 | assert(r.len >= a.len + b.len); |
| 2340 | | 2371 | |
| 2341 | const a_norm = a[0..llnormalize(a)]; | 2372 | // Order greatest first. |
| 2342 | const b_norm = b[0..llnormalize(b)]; | 2373 | var x = a; |
| 2343 | var x = a_norm; | 2374 | var y = b; |
| 2344 | var y = b_norm; | 2375 | if (a.len < b.len) { |
| 2345 | if (a_norm.len > b_norm.len) { | 2376 | x = b; |
| 2346 | x = b_norm; | 2377 | y = a; |
| 2347 | y = a_norm; | | |
| 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 | k_mul: { | 2380 | k_mul: { |
| 2354 | if (x.len > 48) { | 2381 | if (y.len > 48) { |
| 2355 | if (opt_allocator) |allocator| { | 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 | error.OutOfMemory => break :k_mul, // handled below | 2384 | error.OutOfMemory => break :k_mul, // handled below |
| 2358 | }; | 2385 | }; |
| 2359 | return; | 2386 | return; |
| ... | @@ -2361,83 +2388,153 @@ fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const L | ... | @@ -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 | 2391 | llmulaccLong(op, r, x, y); |
| 2365 | var i: usize = 0; | | |
| 2366 | while (i < x.len) : (i += 1) { | | |
| 2367 | llmulDigit(r[i..], y, x[i]); | | |
| 2368 | } | | |
| 2369 | } | 2392 | } |
| 2370 | | 2393 | |
| 2371 | /// Knuth 4.3.1, Algorithm M. | 2394 | /// Knuth 4.3.1, Algorithm M. |
| 2372 | /// | 2395 | /// |
| | 2396 | /// r = r (op) a * b |
| 2373 | /// r MUST NOT alias any of a or b. | 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 | @setRuntimeSafety(debug_safety); | 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); | 2409 | // Classical karatsuba algorithm: |
| 2378 | | 2410 | // a = a1 * B + a0 |
| 2379 | const split = @divFloor(x.len, 2); | 2411 | // b = b1 * B + b0 |
| 2380 | var x0 = x[0..split]; | 2412 | // Where a0, b0 < B |
| 2381 | var x1 = x[split..x.len]; | 2413 | // |
| 2382 | var y0 = y[0..split]; | 2414 | // We then have: |
| 2383 | var y1 = y[split..y.len]; | 2415 | // ab = a * b |
| 2384 | | 2416 | // = (a1 * B + a0) * (b1 * B + b0) |
| 2385 | var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1); | 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 | defer allocator.free(tmp); | 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); | 2467 | // Add terms p2 * B^2 and p2 * B to the result. |
| 2392 | _ = llaccum(r[split..], tmp[0..length]); | 2468 | _ = llaccum(op, r[split..], p2); |
| 2393 | _ = llaccum(r[split * 2 ..], tmp[0..length]); | 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); | 2480 | // Finally, compute and add p1. |
| 2400 | _ = llaccum(r[0..], tmp[0..length]); | 2481 | const j0_sign = llcmp(a0, a1); |
| 2401 | _ = llaccum(r[split..], tmp[0..length]); | 2482 | const j1_sign = llcmp(b1, b0); |
| 2402 | | 2483 | |
| 2403 | const x_cmp = llcmp(x1, x0); | 2484 | if (j0_sign * j1_sign == 0) { |
| 2404 | const y_cmp = llcmp(y1, y0); | 2485 | // p1 is zero, we don't need to do any computation at all. |
| 2405 | if (x_cmp * y_cmp == 0) { | | |
| 2406 | return; | 2486 | return; |
| 2407 | } | 2487 | } |
| 2408 | const x0_len = llnormalize(x0); | 2488 | |
| 2409 | const x1_len = llnormalize(x1); | 2489 | mem.set(Limb, tmp, 0); |
| 2410 | var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len)); | 2490 | |
| 2411 | defer allocator.free(j0); | 2491 | // p1 is nonzero, so compute the intermediary terms j0 = a0 - a1 and j1 = b1 - b0. |
| 2412 | if (x_cmp == 1) { | 2492 | // Note that in this case, we again need some storage for intermediary results |
| 2413 | llsub(j0, x1[0..x1_len], x0[0..x0_len]); | 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 | } else { | 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); | 2507 | if (j1_sign == 1) { |
| 2419 | const y1_len = llnormalize(y1); | 2508 | // b1 > b0. |
| 2420 | var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len)); | 2509 | _ = llsubcarry(j1, b1, b0); |
| 2421 | defer allocator.free(j1); | | |
| 2422 | if (y_cmp == 1) { | | |
| 2423 | llsub(j1, y1[0..y1_len], y0[0..y0_len]); | | |
| 2424 | } else { | 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); | 2515 | if (j0_sign * j1_sign == 1) { |
| 2432 | llsub(r[split..], r[split..], tmp[0..length]); | 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 | } else { | 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 | 2531 | // r = r (op) a |
| 2439 | fn llaccum(r: []Limb, a: []const Limb) Limb { | 2532 | fn llaccum(comptime op: AccOp, r: []Limb, a: []const Limb) Limb { |
| 2440 | @setRuntimeSafety(debug_safety); | 2533 | @setRuntimeSafety(debug_safety); |
| | 2534 | if (op == .sub) { |
| | 2535 | return llsubcarry(r, r, a); |
| | 2536 | } |
| | 2537 | |
| 2441 | assert(r.len != 0 and a.len != 0); | 2538 | assert(r.len != 0 and a.len != 0); |
| 2442 | assert(r.len >= a.len); | 2539 | assert(r.len >= a.len); |
| 2443 | | 2540 | |
| ... | @@ -2486,24 +2583,53 @@ pub fn llcmp(a: []const Limb, b: []const Limb) i8 { | ... | @@ -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 | @setRuntimeSafety(debug_safety); | 2600 | @setRuntimeSafety(debug_safety); |
| 2491 | if (xi == 0) { | 2601 | if (xi == 0) { |
| 2492 | return; | 2602 | return; |
| 2493 | } | 2603 | } |
| 2494 | | 2604 | |
| 2495 | var carry: Limb = 0; | | |
| 2496 | var a_lo = acc[0..y.len]; | 2605 | var a_lo = acc[0..y.len]; |
| 2497 | var a_hi = acc[y.len..]; | 2606 | var a_hi = acc[y.len..]; |
| 2498 | | 2607 | |
| 2499 | var j: usize = 0; | 2608 | switch (op) { |
| 2500 | while (j < a_lo.len) : (j += 1) { | 2609 | .add => { |
| 2501 | a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry }); | 2610 | var carry: Limb = 0; |
| 2502 | } | 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; | 2616 | j = 0; |
| 2505 | while ((carry != 0) and (j < a_hi.len)) : (j += 1) { | 2617 | while ((carry != 0) and (j < a_hi.len)) : (j += 1) { |
| 2506 | carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j])); | 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,7 +3090,7 @@ fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_ |
| 2964 | } | 3090 | } |
| 2965 | | 3091 | |
| 2966 | /// r MUST NOT alias x. | 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 | @setRuntimeSafety(debug_safety); | 3094 | @setRuntimeSafety(debug_safety); |
| 2969 | | 3095 | |
| 2970 | const x_norm = x; | 3096 | const x_norm = x; |
| ... | @@ -2987,7 +3113,7 @@ fn llsquare_basecase(r: []Limb, x: []const Limb) void { | ... | @@ -2987,7 +3113,7 @@ fn llsquare_basecase(r: []Limb, x: []const Limb) void { |
| 2987 | | 3113 | |
| 2988 | for (x_norm) |v, i| { | 3114 | for (x_norm) |v, i| { |
| 2989 | // Accumulate all the x[i]*x[j] (with x!=j) products | 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 | // Each product appears twice, multiply by 2 | 3119 | // Each product appears twice, multiply by 2 |
| ... | @@ -2995,7 +3121,7 @@ fn llsquare_basecase(r: []Limb, x: []const Limb) void { | ... | @@ -2995,7 +3121,7 @@ fn llsquare_basecase(r: []Limb, x: []const Limb) void { |
| 2995 | | 3121 | |
| 2996 | for (x_norm) |v, i| { | 3122 | for (x_norm) |v, i| { |
| 2997 | // Compute and add the squares | 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,12 +3160,12 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void { |
| 3034 | while (i < exp_bits) : (i += 1) { | 3160 | while (i < exp_bits) : (i += 1) { |
| 3035 | // Square | 3161 | // Square |
| 3036 | mem.set(Limb, tmp2, 0); | 3162 | mem.set(Limb, tmp2, 0); |
| 3037 | llsquare_basecase(tmp2, tmp1[0..llnormalize(tmp1)]); | 3163 | llsquareBasecase(tmp2, tmp1[0..llnormalize(tmp1)]); |
| 3038 | mem.swap([]Limb, &tmp1, &tmp2); | 3164 | mem.swap([]Limb, &tmp1, &tmp2); |
| 3039 | // Multiply by a | 3165 | // Multiply by a |
| 3040 | if (@shlWithOverflow(u32, exp, 1, &exp)) { | 3166 | if (@shlWithOverflow(u32, exp, 1, &exp)) { |
| 3041 | mem.set(Limb, tmp2, 0); | 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 | mem.swap([]Limb, &tmp1, &tmp2); | 3169 | mem.swap([]Limb, &tmp1, &tmp2); |
| 3044 | } | 3170 | } |
| 3045 | } | 3171 | } |