| ... | ... | @@ -44,6 +44,11 @@ pub fn calcMulLimbsBufferLen(a_len: usize, b_len: usize, aliases: usize) usize { |
| 44 | 44 | return aliases * math.max(a_len, b_len); |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | pub fn calcMulWrapLimbsBufferLen(bit_count: usize, a_len: usize, b_len: usize, aliases: usize) usize { |
| 48 | const req_limbs = calcTwosCompLimbCount(bit_count); |
| 49 | return aliases * math.min(req_limbs, math.max(a_len, b_len)); |
| 50 | } |
| 51 | |
| 47 | 52 | pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize { |
| 48 | 53 | const limb_count = calcSetStringLimbCount(base, string_len); |
| 49 | 54 | return calcMulLimbsBufferLen(limb_count, limb_count, 2); |
| ... | ... | @@ -611,7 +616,7 @@ pub const Mutable = struct { |
| 611 | 616 | /// `a` and `b` may alias with each other. |
| 612 | 617 | /// |
| 613 | 618 | /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by |
| 614 | | /// rma is given by `a.limbs.len + b.limbs.len + 1`. |
| 619 | /// rma is given by `a.limbs.len + b.limbs.len`. |
| 615 | 620 | /// |
| 616 | 621 | /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcMulLimbsBufferLen`. |
| 617 | 622 | pub fn mul(rma: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void { |
| ... | ... | @@ -640,7 +645,7 @@ pub const Mutable = struct { |
| 640 | 645 | /// `a` and `b` may alias with each other. |
| 641 | 646 | /// |
| 642 | 647 | /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by |
| 643 | | /// rma is given by `a.limbs.len + b.limbs.len + 1`. |
| 648 | /// rma is given by `a.limbs.len + b.limbs.len`. |
| 644 | 649 | /// |
| 645 | 650 | /// If `allocator` is provided, it will be used for temporary storage to improve |
| 646 | 651 | /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm. |
| ... | ... | @@ -658,18 +663,69 @@ pub const Mutable = struct { |
| 658 | 663 | |
| 659 | 664 | mem.set(Limb, rma.limbs[0 .. a.limbs.len + b.limbs.len + 1], 0); |
| 660 | 665 | |
| 661 | | _ = llmulacc(.add, allocator, rma.limbs, a.limbs, b.limbs); |
| 666 | llmulacc(.add, allocator, rma.limbs, a.limbs, b.limbs); |
| 662 | 667 | |
| 663 | 668 | rma.normalize(a.limbs.len + b.limbs.len); |
| 664 | 669 | rma.positive = (a.positive == b.positive); |
| 665 | 670 | } |
| 666 | 671 | |
| 667 | | pub fn mulNoAliasWrap( |
| 672 | /// rma = a * b with 2s-complement wrapping semantics. |
| 673 | /// |
| 674 | /// `rma` may alias with `a` or `b`. |
| 675 | /// `a` and `b` may alias with each other. |
| 676 | /// |
| 677 | /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by |
| 678 | /// rma is given by `a.limbs.len + b.limbs.len`. |
| 679 | /// |
| 680 | /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcMulWrapLimbsBufferLen`. |
| 681 | pub fn mulWrap( |
| 682 | rma: *Mutable, |
| 683 | a: Const, |
| 684 | b: Const, |
| 685 | signedness: std.builtin.Signedness, |
| 686 | bit_count: usize, |
| 687 | limbs_buffer: []Limb, |
| 688 | allocator: ?*Allocator, |
| 689 | ) void { |
| 690 | var buf_index: usize = 0; |
| 691 | const req_limbs = calcTwosCompLimbCount(bit_count); |
| 692 | |
| 693 | const a_copy = if (rma.limbs.ptr == a.limbs.ptr) blk: { |
| 694 | const start = buf_index; |
| 695 | const a_len = math.min(req_limbs, a.limbs.len); |
| 696 | mem.copy(Limb, limbs_buffer[buf_index..], a.limbs[0..a_len]); |
| 697 | buf_index += a_len; |
| 698 | break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst(); |
| 699 | } else a; |
| 700 | |
| 701 | const b_copy = if (rma.limbs.ptr == b.limbs.ptr) blk: { |
| 702 | const start = buf_index; |
| 703 | const b_len = math.min(req_limbs, b.limbs.len); |
| 704 | mem.copy(Limb, limbs_buffer[buf_index..], b.limbs[0..b_len]); |
| 705 | buf_index += b_len; |
| 706 | break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst(); |
| 707 | } else b; |
| 708 | |
| 709 | return rma.mulWrapNoAlias(a_copy, b_copy, signedness, bit_count, allocator); |
| 710 | } |
| 711 | |
| 712 | /// rma = a * b with 2s-complement wrapping semantics. |
| 713 | /// |
| 714 | /// `rma` may not alias with `a` or `b`. |
| 715 | /// `a` and `b` may alias with each other. |
| 716 | /// |
| 717 | /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by |
| 718 | /// rma is given by `a.limbs.len + b.limbs.len`. |
| 719 | /// |
| 720 | /// If `allocator` is provided, it will be used for temporary storage to improve |
| 721 | /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm. |
| 722 | pub fn mulWrapNoAlias( |
| 668 | 723 | rma: *Mutable, |
| 669 | 724 | a: Const, |
| 670 | 725 | b: Const, |
| 671 | 726 | signedness: std.builtin.Signedness, |
| 672 | 727 | bit_count: usize, |
| 728 | allocator: ?*Allocator, |
| 673 | 729 | ) void { |
| 674 | 730 | assert(rma.limbs.ptr != a.limbs.ptr); // illegal aliasing |
| 675 | 731 | assert(rma.limbs.ptr != b.limbs.ptr); // illegal aliasing |
| ... | ... | @@ -682,15 +738,9 @@ pub const Mutable = struct { |
| 682 | 738 | |
| 683 | 739 | mem.set(Limb, rma.limbs[0..req_limbs], 0); |
| 684 | 740 | |
| 685 | | if (a_limbs.len >= b_limbs.len) { |
| 686 | | llmulaccLow(rma.limbs, a_limbs, b_limbs); |
| 687 | | } else { |
| 688 | | llmulaccLow(rma.limbs, b_limbs, a_limbs); |
| 689 | | } |
| 690 | | |
| 741 | llmulacc(.add, allocator, rma.limbs, a_limbs, b_limbs); |
| 691 | 742 | rma.normalize(math.min(req_limbs, a.limbs.len + b.limbs.len)); |
| 692 | 743 | rma.positive = (a.positive == b.positive); |
| 693 | | |
| 694 | 744 | rma.truncate(rma.toConst(), signedness, bit_count); |
| 695 | 745 | } |
| 696 | 746 | |
| ... | ... | @@ -2167,6 +2217,35 @@ pub const Managed = struct { |
| 2167 | 2217 | rma.setMetadata(m.positive, m.len); |
| 2168 | 2218 | } |
| 2169 | 2219 | |
| 2220 | /// rma = a * b with 2s-complement wrapping semantics. |
| 2221 | /// |
| 2222 | /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. |
| 2223 | /// If rma aliases a or b, then caller must call `rma.ensureCapacity(calcTwosCompLimbCount(bit_count))` |
| 2224 | /// prior to calling `mul`. |
| 2225 | /// |
| 2226 | /// Returns an error if memory could not be allocated. |
| 2227 | /// |
| 2228 | /// rma's allocator is used for temporary storage to speed up the multiplication. |
| 2229 | pub fn mulWrap(rma: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) !void { |
| 2230 | var alias_count: usize = 0; |
| 2231 | if (rma.limbs.ptr == a.limbs.ptr) |
| 2232 | alias_count += 1; |
| 2233 | if (rma.limbs.ptr == b.limbs.ptr) |
| 2234 | alias_count += 1; |
| 2235 | |
| 2236 | try rma.ensureCapacity(calcTwosCompLimbCount(bit_count)); |
| 2237 | var m = rma.toMutable(); |
| 2238 | if (alias_count == 0) { |
| 2239 | m.mulWrapNoAlias(a, b, signedness, bit_count, rma.allocator); |
| 2240 | } else { |
| 2241 | const limb_count = calcMulWrapLimbsBufferLen(bit_count, a.limbs.len, b.limbs.len, alias_count); |
| 2242 | const limbs_buffer = try rma.allocator.alloc(Limb, limb_count); |
| 2243 | defer rma.allocator.free(limbs_buffer); |
| 2244 | m.mulWrap(a, b, signedness, bit_count, limbs_buffer, rma.allocator); |
| 2245 | } |
| 2246 | rma.setMetadata(m.positive, m.len); |
| 2247 | } |
| 2248 | |
| 2170 | 2249 | pub fn ensureAddScalarCapacity(r: *Managed, a: Const, scalar: anytype) !void { |
| 2171 | 2250 | try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1); |
| 2172 | 2251 | } |
| ... | ... | @@ -2337,19 +2416,6 @@ pub const Managed = struct { |
| 2337 | 2416 | } |
| 2338 | 2417 | }; |
| 2339 | 2418 | |
| 2340 | | /// r = r + a * b, ignoring overflow |
| 2341 | | fn llmulaccLow(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2342 | | assert(r.len >= a.len); |
| 2343 | | assert(a.len >= b.len); |
| 2344 | | |
| 2345 | | // TODO: Improve performance. |
| 2346 | | |
| 2347 | | var i: usize = 0; |
| 2348 | | while (i < b.len) : (i += 1) { |
| 2349 | | llmulLimb(.add, r[i..], a, b[i]); |
| 2350 | | } |
| 2351 | | } |
| 2352 | | |
| 2353 | 2419 | /// Different operators which can be used in accumulation style functions |
| 2354 | 2420 | /// (llmulacc, llmulaccKaratsuba, llmulaccLong, llmulLimb). In all these functions, |
| 2355 | 2421 | /// a computed value is accumulated with an existing result. |