| ... | ... | @@ -58,6 +58,11 @@ pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize { |
| 58 | 58 | return 2 + (a_bit_count * y + (limb_bits - 1)) / limb_bits; |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | // Compute the number of limbs required to store a 2s-complement number of `bit_count` bits. |
| 62 | pub fn calcTwosCompLimbCount(bit_count: usize) usize { |
| 63 | return std.math.divCeil(usize, bit_count, @bitSizeOf(Limb)) catch unreachable; |
| 64 | } |
| 65 | |
| 61 | 66 | /// a + b * c + *carry, sets carry to the overflow bits |
| 62 | 67 | pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { |
| 63 | 68 | @setRuntimeSafety(debug_safety); |
| ... | ... | @@ -980,6 +985,91 @@ pub const Mutable = struct { |
| 980 | 985 | r.normalize(r.len); |
| 981 | 986 | } |
| 982 | 987 | |
| 988 | /// Truncate an integer to a number of bits, following 2s-complement semantics. |
| 989 | /// r may alias a. |
| 990 | /// |
| 991 | /// Asserts `r` has enough storage to store the result. |
| 992 | /// The upper bound is `calcTwosCompLimbCount(a.len)`. |
| 993 | pub fn truncate(r: *Mutable, a: Const, signedness: std.builtin.Signedness, bit_count: usize) void { |
| 994 | const req_limbs = (bit_count + @bitSizeOf(Limb) - 1) / @bitSizeOf(Limb); |
| 995 | |
| 996 | // Handle 0-bit integers. |
| 997 | if (req_limbs == 0 or a.eqZero()) { |
| 998 | r.set(0); |
| 999 | return; |
| 1000 | } |
| 1001 | |
| 1002 | const bit = @truncate(Log2Limb, bit_count - 1); |
| 1003 | const signmask = @as(Limb, 1) << bit; |
| 1004 | const mask = (signmask << 1) -% 1; |
| 1005 | |
| 1006 | if (!a.positive) { |
| 1007 | // Convert the integer from sign-magnitude into twos-complement. |
| 1008 | // -x = ~(x - 1) |
| 1009 | // Note, we simply take req_limbs * @bitSizeOf(Limb) as the |
| 1010 | // target bit count. |
| 1011 | |
| 1012 | r.addScalar(a.abs(), -1); |
| 1013 | |
| 1014 | // Zero-extend the result |
| 1015 | if (req_limbs > r.len) { |
| 1016 | mem.set(Limb, r.limbs[r.len .. req_limbs], 0); |
| 1017 | } |
| 1018 | |
| 1019 | // Truncate to required number of limbs. |
| 1020 | assert(r.limbs.len >= req_limbs); |
| 1021 | r.len = req_limbs; |
| 1022 | |
| 1023 | // Without truncating, we can already peek at the sign bit of the result here. |
| 1024 | // Note that it will be 0 if the result is negative, as we did not apply the flip here. |
| 1025 | // If the result is negative, we have |
| 1026 | // -(-x & mask) |
| 1027 | // = ~(~(x - 1) & mask) + 1 |
| 1028 | // = ~(~((x - 1) | ~mask)) + 1 |
| 1029 | // = ((x - 1) | ~mask)) + 1 |
| 1030 | // Note, this is only valid for the target bits and not the upper bits |
| 1031 | // of the most significant limb. Those still need to be cleared. |
| 1032 | // Also note that `mask` is zero for all other bits, reducing to the identity. |
| 1033 | // This means that we still need to use & mask to clear off the upper bits. |
| 1034 | |
| 1035 | if (signedness == .signed and r.limbs[r.len - 1] & signmask == 0) { |
| 1036 | // Re-add the one and negate to get the result. |
| 1037 | r.limbs[r.len - 1] &= mask; |
| 1038 | // Note, addition cannot require extra limbs here as we did a subtraction before. |
| 1039 | r.addScalar(r.toConst(), 1); |
| 1040 | r.normalize(r.len); |
| 1041 | r.positive = false; |
| 1042 | } else { |
| 1043 | llnot(r.limbs[0..r.len]); |
| 1044 | r.limbs[r.len - 1] &= mask; |
| 1045 | r.normalize(r.len); |
| 1046 | } |
| 1047 | } else { |
| 1048 | r.copy(a); |
| 1049 | if (r.len < req_limbs) { |
| 1050 | // Integer fits within target bits, no wrapping required. |
| 1051 | return; |
| 1052 | } |
| 1053 | |
| 1054 | r.len = req_limbs; |
| 1055 | r.limbs[r.len - 1] &= mask; |
| 1056 | r.normalize(r.len); |
| 1057 | |
| 1058 | if (signedness == .signed and r.limbs[r.len - 1] & signmask != 0) { |
| 1059 | // Convert 2s-complement back to sign-magnitude. |
| 1060 | // Sign-extend the upper bits so that they are inverted correctly. |
| 1061 | r.limbs[r.len - 1] |= ~mask; |
| 1062 | llnot(r.limbs[0..r.len]); |
| 1063 | |
| 1064 | // Note, can only overflow if r holds 0xFFF...F which can only happen if |
| 1065 | // a holds 0. |
| 1066 | r.addScalar(r.toConst(), 1); |
| 1067 | |
| 1068 | r.positive = false; |
| 1069 | } |
| 1070 | } |
| 1071 | } |
| 1072 | |
| 983 | 1073 | /// Normalize a possible sequence of leading zeros. |
| 984 | 1074 | /// |
| 985 | 1075 | /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4] |
| ... | ... | @@ -1941,6 +2031,14 @@ pub const Managed = struct { |
| 1941 | 2031 | rma.setMetadata(rma_mut.positive, rma_mut.len); |
| 1942 | 2032 | } |
| 1943 | 2033 | } |
| 2034 | |
| 2035 | // r = truncate(Int(signedness, bit_count), a) |
| 2036 | pub fn truncate(r: *Managed, a: Const, signedness: std.builtin.Signedness, bit_count: usize) !void { |
| 2037 | try r.ensureCapacity(calcTwosCompLimbCount(a.limbs.len)); |
| 2038 | var m = r.toMutable(); |
| 2039 | m.truncate(a, signedness, bit_count); |
| 2040 | r.setMetadata(m.positive, m.len); |
| 2041 | } |
| 1944 | 2042 | }; |
| 1945 | 2043 | |
| 1946 | 2044 | /// Knuth 4.3.1, Algorithm M. |
| ... | ... | @@ -2270,6 +2368,15 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { |
| 2270 | 2368 | } |
| 2271 | 2369 | } |
| 2272 | 2370 | |
| 2371 | // r = ~r |
| 2372 | fn llnot(r: []Limb) void { |
| 2373 | @setRuntimeSafety(debug_safety); |
| 2374 | |
| 2375 | for (r) |*elem| { |
| 2376 | elem.* = ~elem.*; |
| 2377 | } |
| 2378 | } |
| 2379 | |
| 2273 | 2380 | // r = a | b with 2s complement semantics. |
| 2274 | 2381 | // r may alias. |
| 2275 | 2382 | // a and b must not be 0. |