| ... | @@ -18,18 +18,12 @@ const debug_safety = false; | ... | @@ -18,18 +18,12 @@ const debug_safety = false; |
| 18 | /// Returns the number of limbs needed to store `scalar`, which must be a | 18 | /// Returns the number of limbs needed to store `scalar`, which must be a |
| 19 | /// primitive integer value. | 19 | /// primitive integer value. |
| 20 | pub fn calcLimbLen(scalar: anytype) usize { | 20 | pub fn calcLimbLen(scalar: anytype) usize { |
| 21 | const T = @TypeOf(scalar); | 21 | if (scalar == 0) { |
| 22 | switch (@typeInfo(T)) { | 22 | return 1; |
| 23 | .Int => |info| { | | |
| 24 | const UT = if (info.signedness == .signed) std.meta.Int(.unsigned, info.bits - 1) else T; | | |
| 25 | return @sizeOf(UT) / @sizeOf(Limb); | | |
| 26 | }, | | |
| 27 | .ComptimeInt => { | | |
| 28 | const w_value = if (scalar < 0) -scalar else scalar; | | |
| 29 | return @divFloor(math.log2(w_value), limb_bits) + 1; | | |
| 30 | }, | | |
| 31 | else => @compileError("parameter must be a primitive integer type"), | | |
| 32 | } | 23 | } |
| | 24 | |
| | 25 | const w_value = std.math.absCast(scalar); |
| | 26 | return @divFloor(@intCast(Limb, math.log2(w_value)), limb_bits) + 1; |
| 33 | } | 27 | } |
| 34 | | 28 | |
| 35 | pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize { | 29 | pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize { |
| ... | @@ -218,26 +212,22 @@ pub const Mutable = struct { | ... | @@ -218,26 +212,22 @@ pub const Mutable = struct { |
| 218 | /// needs to be to store a specific value. | 212 | /// needs to be to store a specific value. |
| 219 | pub fn set(self: *Mutable, value: anytype) void { | 213 | pub fn set(self: *Mutable, value: anytype) void { |
| 220 | const T = @TypeOf(value); | 214 | const T = @TypeOf(value); |
| | 215 | const needed_limbs = calcLimbLen(value); |
| | 216 | assert(needed_limbs <= self.limbs.len); // value too big |
| | 217 | |
| | 218 | self.len = needed_limbs; |
| | 219 | self.positive = value >= 0; |
| 221 | | 220 | |
| 222 | switch (@typeInfo(T)) { | 221 | switch (@typeInfo(T)) { |
| 223 | .Int => |info| { | 222 | .Int => |info| { |
| 224 | const UT = if (info.signedness == .signed) std.meta.Int(.unsigned, info.bits - 1) else T; | 223 | var w_value = std.math.absCast(value); |
| 225 | | | |
| 226 | const needed_limbs = @sizeOf(UT) / @sizeOf(Limb); | | |
| 227 | assert(needed_limbs <= self.limbs.len); // value too big | | |
| 228 | self.len = 0; | | |
| 229 | self.positive = value >= 0; | | |
| 230 | | | |
| 231 | var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value); | | |
| 232 | | 224 | |
| 233 | if (info.bits <= limb_bits) { | 225 | if (info.bits <= limb_bits) { |
| 234 | self.limbs[0] = @as(Limb, w_value); | 226 | self.limbs[0] = w_value; |
| 235 | self.len += 1; | | |
| 236 | } else { | 227 | } else { |
| 237 | var i: usize = 0; | 228 | var i: usize = 0; |
| 238 | while (w_value != 0) : (i += 1) { | 229 | while (w_value != 0) : (i += 1) { |
| 239 | self.limbs[i] = @truncate(Limb, w_value); | 230 | self.limbs[i] = @truncate(Limb, w_value); |
| 240 | self.len += 1; | | |
| 241 | | 231 | |
| 242 | // TODO: shift == 64 at compile-time fails. Fails on u128 limbs. | 232 | // TODO: shift == 64 at compile-time fails. Fails on u128 limbs. |
| 243 | w_value >>= limb_bits / 2; | 233 | w_value >>= limb_bits / 2; |
| ... | @@ -246,13 +236,7 @@ pub const Mutable = struct { | ... | @@ -246,13 +236,7 @@ pub const Mutable = struct { |
| 246 | } | 236 | } |
| 247 | }, | 237 | }, |
| 248 | .ComptimeInt => { | 238 | .ComptimeInt => { |
| 249 | comptime var w_value = if (value < 0) -value else value; | 239 | comptime var w_value = std.math.absCast(value); |
| 250 | | | |
| 251 | const req_limbs = @divFloor(math.log2(w_value), limb_bits) + 1; | | |
| 252 | assert(req_limbs <= self.limbs.len); // value too big | | |
| 253 | | | |
| 254 | self.len = req_limbs; | | |
| 255 | self.positive = value >= 0; | | |
| 256 | | 240 | |
| 257 | if (w_value <= maxInt(Limb)) { | 241 | if (w_value <= maxInt(Limb)) { |
| 258 | self.limbs[0] = w_value; | 242 | self.limbs[0] = w_value; |
| ... | @@ -835,6 +819,75 @@ pub const Mutable = struct { | ... | @@ -835,6 +819,75 @@ pub const Mutable = struct { |
| 835 | r.positive = a.positive; | 819 | r.positive = a.positive; |
| 836 | } | 820 | } |
| 837 | | 821 | |
| | 822 | /// r = a <<| shift with 2s-complement saturating semantics. |
| | 823 | /// |
| | 824 | /// r and a may alias. |
| | 825 | /// |
| | 826 | /// Asserts there is enough memory to fit the result. The upper bound Limb count is |
| | 827 | /// r is `calcTwosCompLimbCount(bit_count)`. |
| | 828 | pub fn shiftLeftSat(r: *Mutable, a: Const, shift: usize, signedness: std.builtin.Signedness, bit_count: usize) void { |
| | 829 | // Special case: When the argument is negative, but the result is supposed to be unsigned, |
| | 830 | // return 0 in all cases. |
| | 831 | if (!a.positive and signedness == .unsigned) { |
| | 832 | r.set(0); |
| | 833 | return; |
| | 834 | } |
| | 835 | |
| | 836 | // Check whether the shift is going to overflow. This is the case |
| | 837 | // when (in 2s complement) any bit above `bit_count - shift` is set in the unshifted value. |
| | 838 | // Note, the sign bit is not counted here. |
| | 839 | |
| | 840 | // Handle shifts larger than the target type. This also deals with |
| | 841 | // 0-bit integers. |
| | 842 | if (bit_count <= shift) { |
| | 843 | // In this case, there is only no overflow if `a` is zero. |
| | 844 | if (a.eqZero()) { |
| | 845 | r.set(0); |
| | 846 | } else { |
| | 847 | r.setTwosCompIntLimit(if (a.positive) .max else .min, signedness, bit_count); |
| | 848 | } |
| | 849 | return; |
| | 850 | } |
| | 851 | |
| | 852 | const checkbit = bit_count - shift - @boolToInt(signedness == .signed); |
| | 853 | // If `checkbit` and more significant bits are zero, no overflow will take place. |
| | 854 | |
| | 855 | if (checkbit >= a.limbs.len * limb_bits) { |
| | 856 | // `checkbit` is outside the range of a, so definitely no overflow will take place. We |
| | 857 | // can defer to a normal shift. |
| | 858 | // Note that if `a` is normalized (which we assume), this checks for set bits in the upper limbs. |
| | 859 | |
| | 860 | // Note, in this case r should already have enough limbs required to perform the normal shift. |
| | 861 | // In this case the shift of the most significant limb may still overflow. |
| | 862 | r.shiftLeft(a, shift); |
| | 863 | return; |
| | 864 | } else if (checkbit < (a.limbs.len - 1) * limb_bits) { |
| | 865 | // `checkbit` is not in the most significant limb. If `a` is normalized the most significant |
| | 866 | // limb will not be zero, so in this case we need to saturate. Note that `a.limbs.len` must be |
| | 867 | // at least one according to normalization rules. |
| | 868 | |
| | 869 | r.setTwosCompIntLimit(if (a.positive) .max else .min, signedness, bit_count); |
| | 870 | return; |
| | 871 | } |
| | 872 | |
| | 873 | // Generate a mask with the bits to check in the most signficant limb. We'll need to check |
| | 874 | // all bits with equal or more significance than checkbit. |
| | 875 | // const msb = @truncate(Log2Limb, checkbit); |
| | 876 | // const checkmask = (@as(Limb, 1) << msb) -% 1; |
| | 877 | |
| | 878 | if (a.limbs[a.limbs.len - 1] >> @truncate(Log2Limb, checkbit) != 0) { |
| | 879 | // Need to saturate. |
| | 880 | r.setTwosCompIntLimit(if (a.positive) .max else .min, signedness, bit_count); |
| | 881 | return; |
| | 882 | } |
| | 883 | |
| | 884 | // This shift should not be able to overflow, so invoke llshl and normalize manually |
| | 885 | // to avoid the extra required limb. |
| | 886 | llshl(r.limbs[0..], a.limbs[0..a.limbs.len], shift); |
| | 887 | r.normalize(a.limbs.len + (shift / limb_bits)); |
| | 888 | r.positive = a.positive; |
| | 889 | } |
| | 890 | |
| 838 | /// r = a >> shift | 891 | /// r = a >> shift |
| 839 | /// r and a may alias. | 892 | /// r and a may alias. |
| 840 | /// | 893 | /// |
| ... | @@ -2401,6 +2454,14 @@ pub const Managed = struct { | ... | @@ -2401,6 +2454,14 @@ pub const Managed = struct { |
| 2401 | r.setMetadata(m.positive, m.len); | 2454 | r.setMetadata(m.positive, m.len); |
| 2402 | } | 2455 | } |
| 2403 | | 2456 | |
| | 2457 | /// r = a <<| shift with 2s-complement saturating semantics. |
| | 2458 | pub fn shiftLeftSat(r: *Managed, a: Managed, shift: usize, signedness: std.builtin.Signedness, bit_count: usize) !void { |
| | 2459 | try r.ensureTwosCompCapacity(bit_count); |
| | 2460 | var m = r.toMutable(); |
| | 2461 | m.shiftLeftSat(a.toConst(), shift, signedness, bit_count); |
| | 2462 | r.setMetadata(m.positive, m.len); |
| | 2463 | } |
| | 2464 | |
| 2404 | /// r = a >> shift | 2465 | /// r = a >> shift |
| 2405 | pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void { | 2466 | pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void { |
| 2406 | if (a.len() <= shift / limb_bits) { | 2467 | if (a.len() <= shift / limb_bits) { |
| ... | @@ -2949,10 +3010,18 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void { | ... | @@ -2949,10 +3010,18 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void { |
| 2949 | fn llshl(r: []Limb, a: []const Limb, shift: usize) void { | 3010 | fn llshl(r: []Limb, a: []const Limb, shift: usize) void { |
| 2950 | @setRuntimeSafety(debug_safety); | 3011 | @setRuntimeSafety(debug_safety); |
| 2951 | assert(a.len >= 1); | 3012 | assert(a.len >= 1); |
| 2952 | assert(r.len >= a.len + (shift / limb_bits) + 1); | 3013 | |
| | 3014 | const interior_limb_shift = @truncate(Log2Limb, shift); |
| | 3015 | |
| | 3016 | // We only need the extra limb if the shift of the last element overflows. |
| | 3017 | // This is useful for the implementation of `shiftLeftSat`. |
| | 3018 | if (a[a.len - 1] << interior_limb_shift >> interior_limb_shift != a[a.len - 1]) { |
| | 3019 | assert(r.len >= a.len + (shift / limb_bits) + 1); |
| | 3020 | } else { |
| | 3021 | assert(r.len >= a.len + (shift / limb_bits)); |
| | 3022 | } |
| 2953 | | 3023 | |
| 2954 | const limb_shift = shift / limb_bits + 1; | 3024 | const limb_shift = shift / limb_bits + 1; |
| 2955 | const interior_limb_shift = @intCast(Log2Limb, shift % limb_bits); | | |
| 2956 | | 3025 | |
| 2957 | var carry: Limb = 0; | 3026 | var carry: Limb = 0; |
| 2958 | var i: usize = 0; | 3027 | var i: usize = 0; |
| ... | @@ -2979,7 +3048,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { | ... | @@ -2979,7 +3048,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { |
| 2979 | assert(r.len >= a.len - (shift / limb_bits)); | 3048 | assert(r.len >= a.len - (shift / limb_bits)); |
| 2980 | | 3049 | |
| 2981 | const limb_shift = shift / limb_bits; | 3050 | const limb_shift = shift / limb_bits; |
| 2982 | const interior_limb_shift = @intCast(Log2Limb, shift % limb_bits); | 3051 | const interior_limb_shift = @truncate(Log2Limb, shift); |
| 2983 | | 3052 | |
| 2984 | var carry: Limb = 0; | 3053 | var carry: Limb = 0; |
| 2985 | var i: usize = 0; | 3054 | var i: usize = 0; |