Skip to content

Commit c44f450

Browse files
linusgsamy-00007
andcommitted
std.math.big.int: Support strings up to base 36
Co-Authored-By: samy007 <[email protected]>
1 parent 5e20e9b commit c44f450

File tree

2 files changed

+32
-9
lines changed

2 files changed

+32
-9
lines changed

lib/std/math/big/int.zig

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,11 @@ pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {
5757
return calcMulLimbsBufferLen(limb_count, limb_count, 2);
5858
}
5959

60+
/// Assumes `string_len` doesn't account for minus signs if the number is negative.
6061
pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {
61-
return (string_len + (limb_bits / base - 1)) / (limb_bits / base);
62+
const base_f: f32 = @floatFromInt(base);
63+
const string_len_f: f32 = @floatFromInt(string_len);
64+
return 1 + @as(usize, @intFromFloat(@ceil(string_len_f * std.math.log2(base_f) / limb_bits)));
6265
}
6366

6467
pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize {
@@ -280,7 +283,7 @@ pub const Mutable = struct {
280283
///
281284
/// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can
282285
/// be determined with `calcSetStringLimbCount`.
283-
/// Asserts the base is in the range [2, 16].
286+
/// Asserts the base is in the range [2, 36].
284287
///
285288
/// Returns an error if the value has invalid digits for the requested base.
286289
///
@@ -296,7 +299,8 @@ pub const Mutable = struct {
296299
limbs_buffer: []Limb,
297300
allocator: ?Allocator,
298301
) error{InvalidCharacter}!void {
299-
assert(base >= 2 and base <= 16);
302+
assert(base >= 2);
303+
assert(base <= 36);
300304

301305
var i: usize = 0;
302306
var positive = true;
@@ -2283,11 +2287,11 @@ pub const Const = struct {
22832287

22842288
/// Converts self to a string in the requested base.
22852289
/// Caller owns returned memory.
2286-
/// Asserts that `base` is in the range [2, 16].
2290+
/// Asserts that `base` is in the range [2, 36].
22872291
/// See also `toString`, a lower level function than this.
22882292
pub fn toStringAlloc(self: Const, allocator: Allocator, base: u8, case: std.fmt.Case) Allocator.Error![]u8 {
22892293
assert(base >= 2);
2290-
assert(base <= 16);
2294+
assert(base <= 36);
22912295

22922296
if (self.eqlZero()) {
22932297
return allocator.dupe(u8, "0");
@@ -2302,7 +2306,7 @@ pub const Const = struct {
23022306
}
23032307

23042308
/// Converts self to a string in the requested base.
2305-
/// Asserts that `base` is in the range [2, 16].
2309+
/// Asserts that `base` is in the range [2, 36].
23062310
/// `string` is a caller-provided slice of at least `sizeInBaseUpperBound` bytes,
23072311
/// where the result is written to.
23082312
/// Returns the length of the string.
@@ -2312,7 +2316,7 @@ pub const Const = struct {
23122316
/// See also `toStringAlloc`, a higher level function than this.
23132317
pub fn toString(self: Const, string: []u8, base: u8, case: std.fmt.Case, limbs_buffer: []Limb) usize {
23142318
assert(base >= 2);
2315-
assert(base <= 16);
2319+
assert(base <= 36);
23162320

23172321
if (self.eqlZero()) {
23182322
string[0] = '0';
@@ -2816,7 +2820,7 @@ pub const Managed = struct {
28162820
///
28172821
/// self's allocator is used for temporary storage to boost multiplication performance.
28182822
pub fn setString(self: *Managed, base: u8, value: []const u8) !void {
2819-
if (base < 2 or base > 16) return error.InvalidBase;
2823+
if (base < 2 or base > 36) return error.InvalidBase;
28202824
try self.ensureCapacity(calcSetStringLimbCount(base, value.len));
28212825
const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len));
28222826
defer self.allocator.free(limbs_buffer);
@@ -2843,7 +2847,7 @@ pub const Managed = struct {
28432847
/// Converts self to a string in the requested base. Memory is allocated from the provided
28442848
/// allocator and not the one present in self.
28452849
pub fn toString(self: Managed, allocator: Allocator, base: u8, case: std.fmt.Case) ![]u8 {
2846-
if (base < 2 or base > 16) return error.InvalidBase;
2850+
if (base < 2 or base > 36) return error.InvalidBase;
28472851
return self.toConst().toStringAlloc(allocator, base, case);
28482852
}
28492853

lib/std/math/big/int_test.zig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,14 @@ test "string set case insensitive number" {
275275
try testing.expect((try a.toInt(u32)) == 0xabcdef);
276276
}
277277

278+
test "string set base 36" {
279+
var a = try Managed.init(testing.allocator);
280+
defer a.deinit();
281+
282+
try a.setString(36, "fifvthrv1mzt79ez9");
283+
try testing.expect((try a.to(u128)) == 123456789123456789123456789);
284+
}
285+
278286
test "string set bad char error" {
279287
var a = try Managed.init(testing.allocator);
280288
defer a.deinit();
@@ -353,6 +361,17 @@ test "string to base 16" {
353361
try testing.expect(mem.eql(u8, as, es));
354362
}
355363

364+
test "string to base 36" {
365+
var a = try Managed.initSet(testing.allocator, 123456789123456789123456789);
366+
defer a.deinit();
367+
368+
const as = try a.toString(testing.allocator, 36, .lower);
369+
defer testing.allocator.free(as);
370+
const es = "fifvthrv1mzt79ez9";
371+
372+
try testing.expect(mem.eql(u8, as, es));
373+
}
374+
356375
test "neg string to" {
357376
var a = try Managed.initSet(testing.allocator, -123907434);
358377
defer a.deinit();

0 commit comments

Comments
 (0)