diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index e3dcb8d71d..fc79c4c3a7 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -63,6 +63,11 @@ pub fn calcLimbLen(scalar: anytype) usize { } } +/// Same as `calcToStringLimbsBufferLen`, without the useless base check. +pub fn calcLog10LimbsBufferLen(a_len: usize) usize { + return a_len + 2 + a_len + calcDivLimbsBufferLen(a_len, 1); +} + pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize { if (math.isPowerOfTwo(base)) return 0; @@ -2670,6 +2675,62 @@ pub const Const = struct { } return @min(result, bits); } + + /// Calculate the base 2 logarithm, rounded down. + pub fn log2(a: Const) Limb { + return a.limbs.len * @bitSizeOf(Limb) - 1 - @clz(a.limbs[a.limbs.len - 1]); + } + + /// Calculate the base 10 logarithm, rounded down. + /// + /// The allocator is used to allocate a temporary buffer. + pub fn log10Alloc(a: Const, allocator: Allocator) Allocator.Error!Limb { + const limbs_buffer = try allocator.alloc(Limb, calcLog10LimbsBufferLen(a.limbs.len)); + defer allocator.free(limbs_buffer); + + return a.log10(limbs_buffer); + } + + /// Calculate the base 10 logarithm, rounded down. + /// + /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcLog10LimbsBufferLen`. + pub fn log10(a: Const, limbs_buffer: []Limb) Limb { + const max_digits_per_limb = std.math.log10(std.math.maxInt(Limb)); + const limb_base = comptime calc: { + var limb_base: comptime_int = 1; + for (0..max_digits_per_limb) |_| limb_base *= 10; + break :calc limb_base; + }; + const limb_base_as_bigint: Const = .{ .limbs = &.{limb_base}, .positive = true }; + + var q: Mutable = .{ + .limbs = limbs_buffer[0 .. a.limbs.len + 2], + .positive = true, + .len = a.limbs.len, + }; + @memcpy(q.limbs[0..a.limbs.len], a.limbs); + + var remainder: Mutable = .{ + .limbs = limbs_buffer[q.limbs.len..][0..a.limbs.len], + .positive = true, + .len = 1, + }; + + const division_buf = limbs_buffer[q.limbs.len + remainder.limbs.len ..]; + + var num_digits: Limb = 0; + while (q.len >= 2) { + q.divTrunc(&remainder, q.toConst(), limb_base_as_bigint, division_buf); + num_digits += max_digits_per_limb; + } + var remaining_limb = q.limbs[0]; + while (remaining_limb != 0) { + remaining_limb /= 10; + num_digits += 1; + } + + return num_digits - 1; + } }; /// An arbitrary-precision big integer along with an allocator which manages the memory. diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 34dec3fdde..d549402f1e 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -4072,3 +4072,40 @@ test "ctz" { try testing.expectEqual(0, limb_max_squared.ctz(@bitSizeOf(Limb) * 2)); try testing.expectEqual(0, limb_max_squared.ctz(@bitSizeOf(Limb) * 2 + 1)); } + +test "log2" { + var a = try Managed.init(testing.allocator); + defer a.deinit(); + + try a.setString(2, "1"); + try testing.expectEqual(0, a.toConst().log2()); + + try a.setString(2, "1111011"); + try testing.expectEqual(6, a.toConst().log2()); + + try a.setString(2, "10100111011101010"); + try testing.expectEqual(16, a.toConst().log2()); + + try a.setString(16, "a22d71c87a9ce406da4f5895f9f3cc3d603192baf6c8a2b5c32649d0465bf188fe799b3618085e49d71bdaec01"); + try testing.expectEqual(359, a.toConst().log2()); +} + +test "log10" { + var a = try Managed.init(testing.allocator); + defer a.deinit(); + + try a.setString(10, "1"); + try testing.expectEqual(0, a.toConst().log10Alloc(testing.allocator)); + + try a.setString(10, "1234"); + try testing.expectEqual(3, a.toConst().log10Alloc(testing.allocator)); + + try a.setString(10, "123456789"); + try testing.expectEqual(8, a.toConst().log10Alloc(testing.allocator)); + + try a.setString(10, "57594534510580048222343352832931567593656037535732288627581929757527496850784"); + try testing.expectEqual(76, a.toConst().log10Alloc(testing.allocator)); + + try a.setString(10, "504758845984192913149382719638135788792820830414213085834451043864912744833203879823150928260925344757009154551640690830148486352800955148298533547472300"); + try testing.expectEqual(152, a.toConst().log10Alloc(testing.allocator)); +}