Performs r = a << shift and returns the amount of limbs affected
if a and r overlaps, then r.ptr >= a.ptr is asserted r must have the capacity to store a << shift
fn llshl(r: []Limb, a: []const Limb, shift: usize) usize
fn llshl(r: []Limb, a: []const Limb, shift: usize) usize {
std.debug.assert(a.len >= 1);
if (slicesOverlap(a, r))
std.debug.assert(@intFromPtr(r.ptr) >= @intFromPtr(a.ptr));
if (shift == 0) {
if (a.ptr != r.ptr) @memmove(r[0..a.len], a);
return a.len;
}
if (shift >= limb_bits) {
const limb_shift = shift / limb_bits;
const affected = llshl(r[limb_shift..], a, shift % limb_bits);
@memset(r[0..limb_shift], 0);
return limb_shift + affected;
}
// shift is guaranteed to be < limb_bits
const bit_shift: Log2Limb = @truncate(shift);
const opposite_bit_shift: Log2Limb = @truncate(limb_bits - bit_shift);
// We only need the extra limb if the shift of the last element overflows.
// This is useful for the implementation of `shiftLeftSat`.
const overflows = a[a.len - 1] >> opposite_bit_shift != 0;
if (overflows) {
std.debug.assert(r.len >= a.len + 1);
} else {
std.debug.assert(r.len >= a.len);
}
var i: usize = a.len;
if (overflows) {
// r is asserted to be large enough above
r[a.len] = a[a.len - 1] >> opposite_bit_shift;
}
while (i > 1) {
i -= 1;
r[i] = (a[i - 1] >> opposite_bit_shift) | (a[i] << bit_shift);
}
r[0] = a[0] << bit_shift;
return a.len + @intFromBool(overflows);
}