mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 02:44:43 +01:00
Merge pull request 'Bigint improvements' (#30100) from unplanned/zig:bigint-improvements into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30100 Reviewed-by: jedisct1 <jedisct1@noreply.codeberg.org>
This commit is contained in:
commit
80b5917fad
2 changed files with 83 additions and 42 deletions
|
|
@ -17,6 +17,27 @@ const Endian = std.builtin.Endian;
|
|||
const Signedness = std.builtin.Signedness;
|
||||
const native_endian = builtin.cpu.arch.endian();
|
||||
|
||||
// Comptime-computed constants for supported bases (2 - 36)
|
||||
// all values are set to 0 for bases 0 - 1, to make it possible to
|
||||
// access a constant for a given base b using `constants.value[b]`
|
||||
const Constants = struct {
|
||||
// big_bases[b] is the biggest power of b that fit in a single Limb
|
||||
// i.e. big_bases[b] = b^k < 2^@bitSizeOf(Limb) and b^(k+1) >= 2^@bitSizeOf(Limb)
|
||||
big_bases: [37]Limb,
|
||||
// digits_per_limb[b] is the value of k used in the previous field
|
||||
digits_per_limb: [37]u8,
|
||||
};
|
||||
const constants: Constants = blk: {
|
||||
@setEvalBranchQuota(2000);
|
||||
var digits_per_limb = [_]u8{0} ** 37;
|
||||
var bases = [_]Limb{0} ** 37;
|
||||
for (2..37) |base| {
|
||||
digits_per_limb[base] = @intCast(math.log(Limb, base, math.maxInt(Limb)));
|
||||
bases[base] = std.math.pow(Limb, base, digits_per_limb[base]);
|
||||
}
|
||||
break :blk Constants{ .big_bases = bases, .digits_per_limb = digits_per_limb };
|
||||
};
|
||||
|
||||
/// Returns the number of limbs needed to store `scalar`, which must be a
|
||||
/// primitive integer or float value.
|
||||
/// Note: A comptime-known upper bound of this value that may be used
|
||||
|
|
@ -329,23 +350,15 @@ pub const Mutable = struct {
|
|||
/// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are
|
||||
/// ignored and can be used as digit separators.
|
||||
///
|
||||
/// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can
|
||||
/// There must be enough memory for the value in `self.limbs`. An upper bound on number of limbs can
|
||||
/// be determined with `calcSetStringLimbCount`.
|
||||
/// Asserts the base is in the range [2, 36].
|
||||
///
|
||||
/// Returns an error if the value has invalid digits for the requested base.
|
||||
///
|
||||
/// `limbs_buffer` is used for temporary storage. The size required can be found with
|
||||
/// `calcSetStringLimbsBufferLen`.
|
||||
///
|
||||
/// If `allocator` is provided, it will be used for temporary storage to improve
|
||||
/// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
|
||||
pub fn setString(
|
||||
self: *Mutable,
|
||||
base: u8,
|
||||
value: []const u8,
|
||||
limbs_buffer: []Limb,
|
||||
allocator: ?Allocator,
|
||||
) error{InvalidCharacter}!void {
|
||||
assert(base >= 2);
|
||||
assert(base <= 36);
|
||||
|
|
@ -357,18 +370,41 @@ pub const Mutable = struct {
|
|||
i += 1;
|
||||
}
|
||||
|
||||
const ap_base: Const = .{ .limbs = &[_]Limb{base}, .positive = true };
|
||||
self.set(0);
|
||||
@memset(self.limbs, 0);
|
||||
self.len = 1;
|
||||
|
||||
var limb: Limb = 0;
|
||||
var j: usize = 0;
|
||||
for (value[i..]) |ch| {
|
||||
if (ch == '_') {
|
||||
continue;
|
||||
}
|
||||
const d = try std.fmt.charToDigit(ch, base);
|
||||
const ap_d: Const = .{ .limbs = &[_]Limb{d}, .positive = true };
|
||||
limb *= base;
|
||||
limb += d;
|
||||
j += 1;
|
||||
|
||||
self.mul(self.toConst(), ap_base, limbs_buffer, allocator);
|
||||
self.add(self.toConst(), ap_d);
|
||||
if (j == constants.digits_per_limb[base]) {
|
||||
const len = @min(self.len + 1, self.limbs.len);
|
||||
// r = a * b = a + a * (b - 1)
|
||||
// we assert when self.limbs is not large enough to store the number
|
||||
assert(!llmulLimb(.add, self.limbs[0..len], self.limbs[0..len], constants.big_bases[base] - 1));
|
||||
assert(lladdcarry(self.limbs[0..len], self.limbs[0..len], &[1]Limb{limb}) == 0);
|
||||
|
||||
if (self.limbs.len > self.len and self.limbs[self.len] != 0)
|
||||
self.len += 1;
|
||||
j = 0;
|
||||
limb = 0;
|
||||
}
|
||||
}
|
||||
if (j > 0) {
|
||||
const len = @min(self.len + 1, self.limbs.len);
|
||||
// we assert when self.limbs is not large enough to store the number
|
||||
assert(!llmulLimb(.add, self.limbs[0..len], self.limbs[0..len], math.pow(Limb, base, j) - 1));
|
||||
assert(lladdcarry(self.limbs[0..len], self.limbs[0..len], &[1]Limb{limb}) == 0);
|
||||
|
||||
if (self.limbs.len > self.len and self.limbs[self.len] != 0)
|
||||
self.len += 1;
|
||||
}
|
||||
self.positive = positive;
|
||||
}
|
||||
|
|
@ -2081,7 +2117,7 @@ pub const Const = struct {
|
|||
for (self.limbs[0..self.limbs.len]) |limb| {
|
||||
std.debug.print("{x} ", .{limb});
|
||||
}
|
||||
std.debug.print("len={} positive={}\n", .{ self.len, self.positive });
|
||||
std.debug.print("len={} positive={}\n", .{ self.limbs.len, self.positive });
|
||||
}
|
||||
|
||||
pub fn abs(self: Const) Const {
|
||||
|
|
@ -2884,10 +2920,8 @@ pub const Managed = struct {
|
|||
pub fn setString(self: *Managed, base: u8, value: []const u8) !void {
|
||||
if (base < 2 or base > 36) return error.InvalidBase;
|
||||
try self.ensureCapacity(calcSetStringLimbCount(base, value.len));
|
||||
const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len));
|
||||
defer self.allocator.free(limbs_buffer);
|
||||
var m = self.toMutable();
|
||||
try m.setString(base, value, limbs_buffer, self.allocator);
|
||||
try m.setString(base, value);
|
||||
self.setMetadata(m.positive, m.len);
|
||||
}
|
||||
|
||||
|
|
@ -3596,6 +3630,7 @@ fn llmulaccKaratsuba(
|
|||
/// r = r (op) a.
|
||||
/// The result is computed modulo `r.len`.
|
||||
fn llaccum(comptime op: AccOp, r: []Limb, a: []const Limb) void {
|
||||
assert(!slicesOverlap(r, a) or @intFromPtr(r.ptr) <= @intFromPtr(a.ptr));
|
||||
if (op == .sub) {
|
||||
_ = llsubcarry(r, r, a);
|
||||
return;
|
||||
|
|
@ -3665,6 +3700,8 @@ fn llmulaccLong(comptime op: AccOp, r: []Limb, a: []const Limb, b: []const Limb)
|
|||
/// The result is computed modulo `r.len`.
|
||||
/// Returns whether the operation overflowed.
|
||||
fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) bool {
|
||||
assert(!slicesOverlap(acc, y) or @intFromPtr(acc.ptr) <= @intFromPtr(y.ptr));
|
||||
|
||||
if (xi == 0) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3727,6 +3764,8 @@ fn llsubcarry(r: []Limb, a: []const Limb, b: []const Limb) Limb {
|
|||
assert(a.len != 0 and b.len != 0);
|
||||
assert(a.len >= b.len);
|
||||
assert(r.len >= a.len);
|
||||
assert(!slicesOverlap(r, a) or @intFromPtr(r.ptr) <= @intFromPtr(a.ptr));
|
||||
assert(!slicesOverlap(r, b) or @intFromPtr(r.ptr) <= @intFromPtr(b.ptr));
|
||||
|
||||
var i: usize = 0;
|
||||
var borrow: Limb = 0;
|
||||
|
|
@ -3758,6 +3797,8 @@ fn lladdcarry(r: []Limb, a: []const Limb, b: []const Limb) Limb {
|
|||
assert(a.len != 0 and b.len != 0);
|
||||
assert(a.len >= b.len);
|
||||
assert(r.len >= a.len);
|
||||
assert(!slicesOverlap(r, a) or @intFromPtr(r.ptr) <= @intFromPtr(a.ptr));
|
||||
assert(!slicesOverlap(r, b) or @intFromPtr(r.ptr) <= @intFromPtr(b.ptr));
|
||||
|
||||
var i: usize = 0;
|
||||
var carry: Limb = 0;
|
||||
|
|
|
|||
|
|
@ -737,7 +737,7 @@ test "string to" {
|
|||
defer testing.allocator.free(as);
|
||||
const es = "120317241209124781241290847124";
|
||||
|
||||
try testing.expect(mem.eql(u8, as, es));
|
||||
try testing.expectEqualSlices(u8, es, as);
|
||||
}
|
||||
|
||||
test "string to base base error" {
|
||||
|
|
@ -755,7 +755,7 @@ test "string to base 2" {
|
|||
defer testing.allocator.free(as);
|
||||
const es = "-1011";
|
||||
|
||||
try testing.expect(mem.eql(u8, as, es));
|
||||
try testing.expectEqualSlices(u8, es, as);
|
||||
}
|
||||
|
||||
test "string to base 16" {
|
||||
|
|
@ -766,7 +766,7 @@ test "string to base 16" {
|
|||
defer testing.allocator.free(as);
|
||||
const es = "efffffff00000001eeeeeeefaaaaaaab";
|
||||
|
||||
try testing.expect(mem.eql(u8, as, es));
|
||||
try testing.expectEqualSlices(u8, es, as);
|
||||
}
|
||||
|
||||
test "string to base 36" {
|
||||
|
|
@ -777,7 +777,7 @@ test "string to base 36" {
|
|||
defer testing.allocator.free(as);
|
||||
const es = "fifvthrv1mzt79ez9";
|
||||
|
||||
try testing.expect(mem.eql(u8, as, es));
|
||||
try testing.expectEqualSlices(u8, es, as);
|
||||
}
|
||||
|
||||
test "neg string to" {
|
||||
|
|
@ -788,7 +788,7 @@ test "neg string to" {
|
|||
defer testing.allocator.free(as);
|
||||
const es = "-123907434";
|
||||
|
||||
try testing.expect(mem.eql(u8, as, es));
|
||||
try testing.expectEqualSlices(u8, es, as);
|
||||
}
|
||||
|
||||
test "zero string to" {
|
||||
|
|
@ -799,7 +799,7 @@ test "zero string to" {
|
|||
defer testing.allocator.free(as);
|
||||
const es = "0";
|
||||
|
||||
try testing.expect(mem.eql(u8, as, es));
|
||||
try testing.expectEqualSlices(u8, es, as);
|
||||
}
|
||||
|
||||
test "clone" {
|
||||
|
|
@ -3404,26 +3404,26 @@ test "big int conversion read twos complement with padding" {
|
|||
|
||||
var bit_count: usize = 12 * 8 + 1;
|
||||
a.toConst().writeTwosComplement(buffer1[0..13], .little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0xaa, 0xaa, 0xaa }));
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0xaa, 0xaa, 0xaa }, buffer1);
|
||||
a.toConst().writeTwosComplement(buffer1[0..13], .big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xaa, 0xaa, 0xaa }));
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xaa, 0xaa, 0xaa }, buffer1);
|
||||
a.toConst().writeTwosComplement(buffer1[0..16], .little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0 }));
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0 }, buffer1);
|
||||
a.toConst().writeTwosComplement(buffer1[0..16], .big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd }));
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd }, buffer1);
|
||||
|
||||
@memset(buffer1, 0xaa);
|
||||
try a.set(-0x01_02030405_06070809_0a0b0c0d);
|
||||
bit_count = 12 * 8 + 2;
|
||||
|
||||
a.toConst().writeTwosComplement(buffer1[0..13], .little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xaa, 0xaa, 0xaa }));
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xaa, 0xaa, 0xaa }, buffer1);
|
||||
a.toConst().writeTwosComplement(buffer1[0..13], .big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3, 0xaa, 0xaa, 0xaa }));
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3, 0xaa, 0xaa, 0xaa }, buffer1);
|
||||
a.toConst().writeTwosComplement(buffer1[0..16], .little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff }));
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff }, buffer1);
|
||||
a.toConst().writeTwosComplement(buffer1[0..16], .big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 }));
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 }, buffer1);
|
||||
}
|
||||
|
||||
test "big int write twos complement +/- zero" {
|
||||
|
|
@ -3438,13 +3438,13 @@ test "big int write twos complement +/- zero" {
|
|||
// Test zero
|
||||
|
||||
m.toConst().writeTwosComplement(buffer1[0..13], .little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
|
||||
try testing.expectEqualSlices(u8, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3)), buffer1);
|
||||
m.toConst().writeTwosComplement(buffer1[0..13], .big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
|
||||
try testing.expectEqualSlices(u8, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3)), buffer1);
|
||||
m.toConst().writeTwosComplement(buffer1[0..16], .little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
|
||||
try testing.expectEqualSlices(u8, &(([_]u8{0} ** 16)), buffer1);
|
||||
m.toConst().writeTwosComplement(buffer1[0..16], .big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
|
||||
try testing.expectEqualSlices(u8, &(([_]u8{0} ** 16)), buffer1);
|
||||
|
||||
@memset(buffer1, 0xaa);
|
||||
m.positive = false;
|
||||
|
|
@ -3452,13 +3452,13 @@ test "big int write twos complement +/- zero" {
|
|||
// Test negative zero
|
||||
|
||||
m.toConst().writeTwosComplement(buffer1[0..13], .little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
|
||||
try testing.expectEqualSlices(u8, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3)), buffer1);
|
||||
m.toConst().writeTwosComplement(buffer1[0..13], .big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
|
||||
try testing.expectEqualSlices(u8, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3)), buffer1);
|
||||
m.toConst().writeTwosComplement(buffer1[0..16], .little);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
|
||||
try testing.expectEqualSlices(u8, &(([_]u8{0} ** 16)), buffer1);
|
||||
m.toConst().writeTwosComplement(buffer1[0..16], .big);
|
||||
try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
|
||||
try testing.expectEqualSlices(u8, &(([_]u8{0} ** 16)), buffer1);
|
||||
}
|
||||
|
||||
test "big int conversion write twos complement with padding" {
|
||||
|
|
@ -3816,7 +3816,7 @@ test "(BigInt) positive" {
|
|||
|
||||
const b_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{b});
|
||||
defer testing.allocator.free(b_fmt);
|
||||
try testing.expect(!mem.eql(u8, b_fmt, "(BigInt)"));
|
||||
try testing.expect(!mem.eql(u8, "(BigInt)", b_fmt));
|
||||
}
|
||||
|
||||
test "(BigInt) negative" {
|
||||
|
|
@ -3840,7 +3840,7 @@ test "(BigInt) negative" {
|
|||
const b_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{b});
|
||||
defer testing.allocator.free(b_fmt);
|
||||
|
||||
try testing.expect(mem.eql(u8, a_fmt, "(BigInt)"));
|
||||
try testing.expectEqualSlices(u8, "(BigInt)", a_fmt);
|
||||
try testing.expect(!mem.eql(u8, b_fmt, "(BigInt)"));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue