mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 15:42:49 +00:00
16b3d1004e
Follow up to #19079, which made test names fully qualified. This fixes tests that now-redundant information in their test names. For example here's a fully qualified test name before the changes in this commit: "priority_queue.test.std.PriorityQueue: shrinkAndFree" and the same test's name after the changes in this commit: "priority_queue.test.shrinkAndFree"
129 lines
4.1 KiB
Zig
129 lines
4.1 KiB
Zig
const std = @import("../std.zig");
|
|
const math = std.math;
|
|
const testing = std.testing;
|
|
const assert = std.debug.assert;
|
|
const Log2Int = math.Log2Int;
|
|
|
|
/// Returns the logarithm of `x` for the provided `base`, rounding down to the nearest integer.
|
|
/// Asserts that `base > 1` and `x > 0`.
|
|
pub fn log_int(comptime T: type, base: T, x: T) Log2Int(T) {
|
|
const valid = switch (@typeInfo(T)) {
|
|
.ComptimeInt => true,
|
|
.Int => |IntType| IntType.signedness == .unsigned,
|
|
else => false,
|
|
};
|
|
if (!valid) @compileError("log_int requires an unsigned integer, found " ++ @typeName(T));
|
|
|
|
assert(base > 1 and x > 0);
|
|
if (base == 2) return math.log2_int(T, x);
|
|
|
|
// Let's denote by [y] the integer part of y.
|
|
|
|
// Throughout the iteration the following invariant is preserved:
|
|
// power = base ^ exponent
|
|
|
|
// Safety and termination.
|
|
//
|
|
// We never overflow inside the loop because when we enter the loop we have
|
|
// power <= [maxInt(T) / base]
|
|
// therefore
|
|
// power * base <= maxInt(T)
|
|
// is a valid multiplication for type `T` and
|
|
// exponent + 1 <= log(base, maxInt(T)) <= log2(maxInt(T)) <= maxInt(Log2Int(T))
|
|
// is a valid addition for type `Log2Int(T)`.
|
|
//
|
|
// This implies also termination because power is strictly increasing,
|
|
// hence it must eventually surpass [x / base] < maxInt(T) and we then exit the loop.
|
|
|
|
var exponent: Log2Int(T) = 0;
|
|
var power: T = 1;
|
|
while (power <= x / base) {
|
|
power *= base;
|
|
exponent += 1;
|
|
}
|
|
|
|
// If we never entered the loop we must have
|
|
// [x / base] < 1
|
|
// hence
|
|
// x <= [x / base] * base < base
|
|
// thus the result is 0. We can then return exponent, which is still 0.
|
|
//
|
|
// Otherwise, if we entered the loop at least once,
|
|
// when we exit the loop we have that power is exactly divisible by base and
|
|
// power / base <= [x / base] < power
|
|
// hence
|
|
// power <= [x / base] * base <= x < power * base
|
|
// This means that
|
|
// base^exponent <= x < base^(exponent+1)
|
|
// hence the result is exponent.
|
|
|
|
return exponent;
|
|
}
|
|
|
|
test "log_int" {
|
|
// Test all unsigned integers with 2, 3, ..., 64 bits.
|
|
// We cannot test 0 or 1 bits since base must be > 1.
|
|
inline for (2..64 + 1) |bits| {
|
|
const T = @Type(std.builtin.Type{
|
|
.Int = std.builtin.Type.Int{ .signedness = .unsigned, .bits = @intCast(bits) },
|
|
});
|
|
|
|
// for base = 2, 3, ..., min(maxInt(T),1024)
|
|
var base: T = 1;
|
|
while (base < math.maxInt(T) and base <= 1024) {
|
|
base += 1;
|
|
|
|
// test that `log_int(T, base, 1) == 0`
|
|
try testing.expectEqual(@as(Log2Int(T), 0), log_int(T, base, 1));
|
|
|
|
// For powers `pow = base^exp > 1` that fit inside T,
|
|
// test that `log_int` correctly detects the jump in the logarithm
|
|
// from `log(pow-1) == exp-1` to `log(pow) == exp`.
|
|
var exp: Log2Int(T) = 0;
|
|
var pow: T = 1;
|
|
while (pow <= math.maxInt(T) / base) {
|
|
exp += 1;
|
|
pow *= base;
|
|
|
|
try testing.expectEqual(exp - 1, log_int(T, base, pow - 1));
|
|
try testing.expectEqual(exp, log_int(T, base, pow));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
test "log_int vs math.log2" {
|
|
const types = [_]type{ u2, u3, u4, u8, u16 };
|
|
inline for (types) |T| {
|
|
var n: T = 0;
|
|
while (n < math.maxInt(T)) {
|
|
n += 1;
|
|
const special = math.log2_int(T, n);
|
|
const general = log_int(T, 2, n);
|
|
try testing.expectEqual(special, general);
|
|
}
|
|
}
|
|
}
|
|
|
|
test "log_int vs math.log10" {
|
|
const types = [_]type{ u4, u5, u6, u8, u16 };
|
|
inline for (types) |T| {
|
|
var n: T = 0;
|
|
while (n < math.maxInt(T)) {
|
|
n += 1;
|
|
const special = math.log10_int(n);
|
|
const general = log_int(T, 10, n);
|
|
try testing.expectEqual(special, general);
|
|
}
|
|
}
|
|
}
|
|
|
|
test "log_int at comptime" {
|
|
const x = 59049; // 9 ** 5;
|
|
comptime {
|
|
if (math.log_int(comptime_int, 9, x) != 5) {
|
|
@compileError("log(9, 59049) should be 5");
|
|
}
|
|
}
|
|
}
|