authorgravatar for git.litmus427@passinbox.comunplanned <git.litmus427@passinbox.com> 2025-12-04 01:00:55+01:00
committergravatar for git.litmus427@passinbox.comunplanned <git.litmus427@passinbox.com> 2025-12-04 10:28:31+01:00
log73e82332d02e42a8b95f5c30652d4b7d0db7bea6
tree1cb01211daa38f1a106de0386bb73457faa6ced1
parentc6a14448646980cb1483bb17d38dcd9ae650c78e

big.Mutable.setString optimization and simplification


1 files changed, 52 insertions(+), 18 deletions(-)

lib/std/math/big/int.zig+52-18
......@@ -17,6 +17,27 @@ const Endian = std.builtin.Endian;
1717const Signedness = std.builtin.Signedness;
1818const native_endian = builtin.cpu.arch.endian();
1919
20// Comptime-computed constants for supported bases (2 - 36)
21// all values are set to 0 for bases 0 - 1, to make it possible to
22// access a constant for a given base b using `constants.value[b]`
23const Constants = struct {
24 // big_bases[b] is the biggest power of b that fit in a single Limb
25 // i.e. big_bases[b] = b^k < 2^@bitSizeOf(Limb) and b^(k+1) >= 2^@bitSizeOf(Limb)
26 big_bases: [37]Limb,
27 // digits_per_limb[b] is the value of k used in the previous field
28 digits_per_limb: [37]u8,
29};
30const constants: Constants = blk: {
31 @setEvalBranchQuota(2000);
32 var digits_per_limb = [_]u8{0} ** 37;
33 var bases = [_]Limb{0} ** 37;
34 for (2..37) |base| {
35 digits_per_limb[base] = @intCast(math.log(Limb, base, math.maxInt(Limb)));
36 bases[base] = std.math.pow(Limb, base, digits_per_limb[base]);
37 }
38 break :blk Constants{ .big_bases = bases, .digits_per_limb = digits_per_limb };
39};
40
2041/// Returns the number of limbs needed to store `scalar`, which must be a
2142/// primitive integer or float value.
2243/// Note: A comptime-known upper bound of this value that may be used
......@@ -329,23 +350,15 @@ pub const Mutable = struct {
329350 /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are
330351 /// ignored and can be used as digit separators.
331352 ///
332 /// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can
353 /// There must be enough memory for the value in `self.limbs`. An upper bound on number of limbs can
333354 /// be determined with `calcSetStringLimbCount`.
334355 /// Asserts the base is in the range [2, 36].
335356 ///
336357 /// Returns an error if the value has invalid digits for the requested base.
337 ///
338 /// `limbs_buffer` is used for temporary storage. The size required can be found with
339 /// `calcSetStringLimbsBufferLen`.
340 ///
341 /// If `allocator` is provided, it will be used for temporary storage to improve
342 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
343358 pub fn setString(
344359 self: *Mutable,
345360 base: u8,
346361 value: []const u8,
347 limbs_buffer: []Limb,
348 allocator: ?Allocator,
349362 ) error{InvalidCharacter}!void {
350363 assert(base >= 2);
351364 assert(base <= 36);
......@@ -357,18 +370,41 @@ pub const Mutable = struct {
357370 i += 1;
358371 }
359372
360 const ap_base: Const = .{ .limbs = &[_]Limb{base}, .positive = true };
361 self.set(0);
373 @memset(self.limbs, 0);
374 self.len = 1;
362375
376 var limb: Limb = 0;
377 var j: usize = 0;
363378 for (value[i..]) |ch| {
364379 if (ch == '_') {
365380 continue;
366381 }
367382 const d = try std.fmt.charToDigit(ch, base);
368 const ap_d: Const = .{ .limbs = &[_]Limb{d}, .positive = true };
369
370 self.mul(self.toConst(), ap_base, limbs_buffer, allocator);
371 self.add(self.toConst(), ap_d);
383 limb *= base;
384 limb += d;
385 j += 1;
386
387 if (j == constants.digits_per_limb[base]) {
388 const len = @min(self.len + 1, self.limbs.len);
389 // r = a * b = a + a * (b - 1)
390 // we assert when self.limbs is not large enough to store the number
391 assert(!llmulLimb(.add, self.limbs[0..len], self.limbs[0..len], constants.big_bases[base] - 1));
392 assert(lladdcarry(self.limbs[0..len], self.limbs[0..len], &[1]Limb{limb}) == 0);
393
394 if (self.limbs.len > self.len and self.limbs[self.len] != 0)
395 self.len += 1;
396 j = 0;
397 limb = 0;
398 }
399 }
400 if (j > 0) {
401 const len = @min(self.len + 1, self.limbs.len);
402 // we assert when self.limbs is not large enough to store the number
403 assert(!llmulLimb(.add, self.limbs[0..len], self.limbs[0..len], math.pow(Limb, base, j) - 1));
404 assert(lladdcarry(self.limbs[0..len], self.limbs[0..len], &[1]Limb{limb}) == 0);
405
406 if (self.limbs.len > self.len and self.limbs[self.len] != 0)
407 self.len += 1;
372408 }
373409 self.positive = positive;
374410 }
......@@ -2884,10 +2920,8 @@ pub const Managed = struct {
28842920 pub fn setString(self: *Managed, base: u8, value: []const u8) !void {
28852921 if (base < 2 or base > 36) return error.InvalidBase;
28862922 try self.ensureCapacity(calcSetStringLimbCount(base, value.len));
2887 const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len));
2888 defer self.allocator.free(limbs_buffer);
28892923 var m = self.toMutable();
2890 try m.setString(base, value, limbs_buffer, self.allocator);
2924 try m.setString(base, value);
28912925 self.setMetadata(m.positive, m.len);
28922926 }
28932927