const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
const TypeId = std.builtin.TypeId;
const maxInt = std.math.maxInt;
pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) {
const T = @TypeOf(x);
switch (@typeInfo(T)) {
.Float, .ComptimeFloat => return @sqrt(x),
.ComptimeInt => comptime {
if (x > maxInt(u128)) {
@compileError("sqrt not implemented for comptime_int greater than 128 bits");
}
if (x < 0) {
@compileError("sqrt on negative number");
}
return @as(T, sqrt_int(u128, x));
},
.Int => |IntType| switch (IntType.signedness) {
.signed => @compileError("sqrt not implemented for signed integers"),
.unsigned => return sqrt_int(T, x),
},
else => @compileError("sqrt not implemented for " ++ @typeName(T)),
}
}
fn sqrt_int(comptime T: type, value: T) Sqrt(T) {
if (@typeInfo(T).Int.bits <= 2) {
return if (value == 0) 0 else 1;
} else {
const bits = @typeInfo(T).Int.bits;
const max = math.maxInt(T);
const minustwo = (@as(T, 2) ^ max) + 1;
var op = value;
var res: T = 0;
var one: T = 1 << ((bits - 1) & minustwo);
while (one > op) {
one >>= 2;
}
while (one != 0) {
var c = op >= res + one;
if (c) op -= res + one;
res >>= 1;
if (c) res += one;
one >>= 2;
}
return @intCast(Sqrt(T), res);
}
}
test "math.sqrt_int" {
try expect(sqrt_int(u32, 3) == 1);
try expect(sqrt_int(u32, 4) == 2);
try expect(sqrt_int(u32, 5) == 2);
try expect(sqrt_int(u32, 8) == 2);
try expect(sqrt_int(u32, 9) == 3);
try expect(sqrt_int(u32, 10) == 3);
try expect(sqrt_int(u0, 0) == 0);
try expect(sqrt_int(u1, 1) == 1);
try expect(sqrt_int(u2, 3) == 1);
try expect(sqrt_int(u3, 4) == 2);
try expect(sqrt_int(u4, 8) == 2);
try expect(sqrt_int(u4, 9) == 3);
}
pub fn Sqrt(comptime T: type) type {
return switch (@typeInfo(T)) {
.Int => |int| std.meta.Int(.unsigned, (int.bits + 1) / 2),
else => T,
};
}