aboutsummaryrefslogtreecommitdiff
path: root/lib/std/math
diff options
context:
space:
mode:
authorLoris Cro <kappaloris@gmail.com>2023-06-18 09:06:40 +0200
committerGitHub <noreply@github.com>2023-06-18 09:06:40 +0200
commit216ef10dc471e4db60a30208be178d6c59efeaaf (patch)
tree8c239dab283ae9cb3b7fe099bae240bcc53f894e /lib/std/math
parent0fc1d396495c1ab482197021dedac8bea3f9401c (diff)
parent729a051e9e38674233190aea23c0ac8c134f2d67 (diff)
downloadzig-216ef10dc471e4db60a30208be178d6c59efeaaf.tar.gz
zig-216ef10dc471e4db60a30208be178d6c59efeaaf.zip
Merge branch 'master' into autodoc-searchkey
Diffstat (limited to 'lib/std/math')
-rw-r--r--lib/std/math/atan.zig2
-rw-r--r--lib/std/math/big/int.zig239
-rw-r--r--lib/std/math/big/int_test.zig56
-rw-r--r--lib/std/math/big/rational.zig52
-rw-r--r--lib/std/math/complex/tan.zig2
-rw-r--r--lib/std/math/ilogb.zig2
-rw-r--r--lib/std/math/ldexp.zig2
-rw-r--r--lib/std/math/log10.zig2
-rw-r--r--lib/std/math/scalbn.zig4
-rw-r--r--lib/std/math/sqrt.zig2
10 files changed, 242 insertions, 121 deletions
diff --git a/lib/std/math/atan.zig b/lib/std/math/atan.zig
index 3a13d943e8..41caae11a6 100644
--- a/lib/std/math/atan.zig
+++ b/lib/std/math/atan.zig
@@ -161,7 +161,7 @@ fn atan64(x_: f64) f64 {
var id: ?usize = undefined;
// |x| < 0.4375
- if (ix < 0x3DFC0000) {
+ if (ix < 0x3FDC0000) {
// |x| < 2^(-27)
if (ix < 0x3E400000) {
if (ix < 0x00100000) {
diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig
index 2406d669ec..487812e1de 100644
--- a/lib/std/math/big/int.zig
+++ b/lib/std/math/big/int.zig
@@ -44,12 +44,12 @@ pub fn calcDivLimbsBufferLen(a_len: usize, b_len: usize) usize {
}
pub fn calcMulLimbsBufferLen(a_len: usize, b_len: usize, aliases: usize) usize {
- return aliases * math.max(a_len, b_len);
+ return aliases * @max(a_len, b_len);
}
pub fn calcMulWrapLimbsBufferLen(bit_count: usize, a_len: usize, b_len: usize, aliases: usize) usize {
const req_limbs = calcTwosCompLimbCount(bit_count);
- return aliases * math.min(req_limbs, math.max(a_len, b_len));
+ return aliases * @min(req_limbs, @max(a_len, b_len));
}
pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {
@@ -66,6 +66,13 @@ pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize {
return 2 + (a_bit_count * y + (limb_bits - 1)) / limb_bits;
}
+pub fn calcSqrtLimbsBufferLen(a_bit_count: usize) usize {
+ const a_limb_count = (a_bit_count - 1) / limb_bits + 1;
+ const shift = (a_bit_count + 1) / 2;
+ const u_s_rem_limb_count = 1 + ((shift - 1) / limb_bits + 1);
+ return a_limb_count + 3 * u_s_rem_limb_count + calcDivLimbsBufferLen(a_limb_count, u_s_rem_limb_count);
+}
+
// Compute the number of limbs required to store a 2s-complement number of `bit_count` bits.
pub fn calcTwosCompLimbCount(bit_count: usize) usize {
return std.math.divCeil(usize, bit_count, @bitSizeOf(Limb)) catch unreachable;
@@ -389,7 +396,7 @@ pub const Mutable = struct {
/// scalar is a primitive integer type.
///
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
- /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`.
+ /// r is `@max(a.limbs.len, calcLimbLen(scalar)) + 1`.
pub fn addScalar(r: *Mutable, a: Const, scalar: anytype) void {
// Normally we could just determine the number of limbs needed with calcLimbLen,
// but that is not comptime-known when scalar is not a comptime_int. Instead, we
@@ -407,11 +414,11 @@ pub const Mutable = struct {
return add(r, a, operand);
}
- /// Base implementation for addition. Adds `max(a.limbs.len, b.limbs.len)` elements from a and b,
- /// and returns whether any overflow occured.
+ /// Base implementation for addition. Adds `@max(a.limbs.len, b.limbs.len)` elements from a and b,
+ /// and returns whether any overflow occurred.
/// r, a and b may be aliases.
///
- /// Asserts r has enough elements to hold the result. The upper bound is `max(a.limbs.len, b.limbs.len)`.
+ /// Asserts r has enough elements to hold the result. The upper bound is `@max(a.limbs.len, b.limbs.len)`.
fn addCarry(r: *Mutable, a: Const, b: Const) bool {
if (a.eqZero()) {
r.copy(b);
@@ -445,12 +452,12 @@ pub const Mutable = struct {
/// r, a and b may be aliases.
///
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
- /// r is `math.max(a.limbs.len, b.limbs.len) + 1`.
+ /// r is `@max(a.limbs.len, b.limbs.len) + 1`.
pub fn add(r: *Mutable, a: Const, b: Const) void {
if (r.addCarry(a, b)) {
// Fix up the result. Note that addCarry normalizes by a.limbs.len or b.limbs.len,
// so we need to set the length here.
- const msl = math.max(a.limbs.len, b.limbs.len);
+ const msl = @max(a.limbs.len, b.limbs.len);
// `[add|sub]Carry` normalizes by `msl`, so we need to fix up the result manually here.
// Note, the fact that it normalized means that the intermediary limbs are zero here.
r.len = msl + 1;
@@ -467,15 +474,15 @@ pub const Mutable = struct {
const req_limbs = calcTwosCompLimbCount(bit_count);
// Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
- // if an overflow occured.
+ // if an overflow occurred.
const x = Const{
.positive = a.positive,
- .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)],
+ .limbs = a.limbs[0..@min(req_limbs, a.limbs.len)],
};
const y = Const{
.positive = b.positive,
- .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
+ .limbs = b.limbs[0..@min(req_limbs, b.limbs.len)],
};
var carry_truncated = false;
@@ -485,7 +492,7 @@ pub const Mutable = struct {
// truncate anyway.
// - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
// Note: after this we still might need to wrap.
- const msl = math.max(a.limbs.len, b.limbs.len);
+ const msl = @max(a.limbs.len, b.limbs.len);
if (msl < req_limbs) {
r.limbs[msl] = 1;
r.len = req_limbs;
@@ -512,15 +519,15 @@ pub const Mutable = struct {
const req_limbs = calcTwosCompLimbCount(bit_count);
// Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
- // if an overflow occured.
+ // if an overflow occurred.
const x = Const{
.positive = a.positive,
- .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)],
+ .limbs = a.limbs[0..@min(req_limbs, a.limbs.len)],
};
const y = Const{
.positive = b.positive,
- .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
+ .limbs = b.limbs[0..@min(req_limbs, b.limbs.len)],
};
if (r.addCarry(x, y)) {
@@ -528,7 +535,7 @@ pub const Mutable = struct {
// - We overflowed req_limbs, in which case we need to saturate.
// - a and b had less elements than req_limbs, and those were overflowed.
// Note: In this case, might _also_ need to saturate.
- const msl = math.max(a.limbs.len, b.limbs.len);
+ const msl = @max(a.limbs.len, b.limbs.len);
if (msl < req_limbs) {
r.limbs[msl] = 1;
r.len = req_limbs;
@@ -543,11 +550,11 @@ pub const Mutable = struct {
r.saturate(r.toConst(), signedness, bit_count);
}
- /// Base implementation for subtraction. Subtracts `max(a.limbs.len, b.limbs.len)` elements from a and b,
- /// and returns whether any overflow occured.
+ /// Base implementation for subtraction. Subtracts `@max(a.limbs.len, b.limbs.len)` elements from a and b,
+ /// and returns whether any overflow occurred.
/// r, a and b may be aliases.
///
- /// Asserts r has enough elements to hold the result. The upper bound is `max(a.limbs.len, b.limbs.len)`.
+ /// Asserts r has enough elements to hold the result. The upper bound is `@max(a.limbs.len, b.limbs.len)`.
fn subCarry(r: *Mutable, a: Const, b: Const) bool {
if (a.eqZero()) {
r.copy(b);
@@ -600,12 +607,12 @@ pub const Mutable = struct {
/// r, a and b may be aliases.
///
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
- /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.
+ /// r is `@max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.
pub fn sub(r: *Mutable, a: Const, b: Const) void {
r.add(a, b.negate());
}
- /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occured.
+ /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occurred.
///
/// r, a and b may be aliases
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
@@ -707,7 +714,7 @@ pub const Mutable = struct {
const a_copy = if (rma.limbs.ptr == a.limbs.ptr) blk: {
const start = buf_index;
- const a_len = math.min(req_limbs, a.limbs.len);
+ const a_len = @min(req_limbs, a.limbs.len);
@memcpy(limbs_buffer[buf_index..][0..a_len], a.limbs[0..a_len]);
buf_index += a_len;
break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst();
@@ -715,7 +722,7 @@ pub const Mutable = struct {
const b_copy = if (rma.limbs.ptr == b.limbs.ptr) blk: {
const start = buf_index;
- const b_len = math.min(req_limbs, b.limbs.len);
+ const b_len = @min(req_limbs, b.limbs.len);
@memcpy(limbs_buffer[buf_index..][0..b_len], b.limbs[0..b_len]);
buf_index += b_len;
break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst();
@@ -748,13 +755,13 @@ pub const Mutable = struct {
const req_limbs = calcTwosCompLimbCount(bit_count);
// We can ignore the upper bits here, those results will be discarded anyway.
- const a_limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)];
- const b_limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)];
+ const a_limbs = a.limbs[0..@min(req_limbs, a.limbs.len)];
+ const b_limbs = b.limbs[0..@min(req_limbs, b.limbs.len)];
@memset(rma.limbs[0..req_limbs], 0);
llmulacc(.add, allocator, rma.limbs, a_limbs, b_limbs);
- rma.normalize(math.min(req_limbs, a.limbs.len + b.limbs.len));
+ rma.normalize(@min(req_limbs, a.limbs.len + b.limbs.len));
rma.positive = (a.positive == b.positive);
rma.truncate(rma.toConst(), signedness, bit_count);
}
@@ -1141,7 +1148,7 @@ pub const Mutable = struct {
return;
}
- // Generate a mask with the bits to check in the most signficant limb. We'll need to check
+ // Generate a mask with the bits to check in the most significant limb. We'll need to check
// all bits with equal or more significance than checkbit.
// const msb = @truncate(Log2Limb, checkbit);
// const checkmask = (@as(Limb, 1) << msb) -% 1;
@@ -1204,7 +1211,7 @@ pub const Mutable = struct {
///
/// a and b are zero-extended to the longer of a or b.
///
- /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.
+ /// Asserts that r has enough limbs to store the result. Upper bound is `@max(a.limbs.len, b.limbs.len)`.
pub fn bitOr(r: *Mutable, a: Const, b: Const) void {
// Trivial cases, llsignedor does not support zero.
if (a.eqZero()) {
@@ -1228,8 +1235,8 @@ pub const Mutable = struct {
/// r may alias with a or b.
///
/// Asserts that r has enough limbs to store the result.
- /// If a or b is positive, the upper bound is `math.min(a.limbs.len, b.limbs.len)`.
- /// If a and b are negative, the upper bound is `math.max(a.limbs.len, b.limbs.len) + 1`.
+ /// If a or b is positive, the upper bound is `@min(a.limbs.len, b.limbs.len)`.
+ /// If a and b are negative, the upper bound is `@max(a.limbs.len, b.limbs.len) + 1`.
pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {
// Trivial cases, llsignedand does not support zero.
if (a.eqZero()) {
@@ -1253,8 +1260,8 @@ pub const Mutable = struct {
/// r may alias with a or b.
///
/// Asserts that r has enough limbs to store the result. If a and b share the same signedness, the
- /// upper bound is `math.max(a.limbs.len, b.limbs.len)`. Otherwise, if either a or b is negative
- /// but not both, the upper bound is `math.max(a.limbs.len, b.limbs.len) + 1`.
+ /// upper bound is `@max(a.limbs.len, b.limbs.len)`. Otherwise, if either a or b is negative
+ /// but not both, the upper bound is `@max(a.limbs.len, b.limbs.len) + 1`.
pub fn bitXor(r: *Mutable, a: Const, b: Const) void {
// Trivial cases, because llsignedxor does not support negative zero.
if (a.eqZero()) {
@@ -1277,7 +1284,7 @@ pub const Mutable = struct {
/// rma may alias x or y.
/// x and y may alias each other.
/// Asserts that `rma` has enough limbs to store the result. Upper bound is
- /// `math.min(x.limbs.len, y.limbs.len)`.
+ /// `@min(x.limbs.len, y.limbs.len)`.
///
/// `limbs_buffer` is used for temporary storage during the operation. When this function returns,
/// it will have the same length as it had when the function was called.
@@ -1344,6 +1351,64 @@ pub const Mutable = struct {
r.positive = a.positive or (b & 1) == 0;
}
+ /// r = ⌊√a⌋
+ ///
+ /// r may alias a.
+ ///
+ /// Asserts that `r` has enough limbs to store the result. Upper bound is
+ /// `(a.limbs.len - 1) / 2 + 1`.
+ ///
+ /// `limbs_buffer` is used for temporary storage.
+ /// The amount required is given by `calcSqrtLimbsBufferLen`.
+ pub fn sqrt(
+ r: *Mutable,
+ a: Const,
+ limbs_buffer: []Limb,
+ ) void {
+ // Brent and Zimmermann, Modern Computer Arithmetic, Algorithm 1.13 SqrtInt
+ // https://members.loria.fr/PZimmermann/mca/pub226.html
+ var buf_index: usize = 0;
+ var t = b: {
+ const start = buf_index;
+ buf_index += a.limbs.len;
+ break :b Mutable.init(limbs_buffer[start..buf_index], 0);
+ };
+ var u = b: {
+ const start = buf_index;
+ const shift = (a.bitCountAbs() + 1) / 2;
+ buf_index += 1 + ((shift - 1) / limb_bits + 1);
+ var m = Mutable.init(limbs_buffer[start..buf_index], 1);
+ m.shiftLeft(m.toConst(), shift); // u must be >= ⌊√a⌋, and should be as small as possible for efficiency
+ break :b m;
+ };
+ var s = b: {
+ const start = buf_index;
+ buf_index += u.limbs.len;
+ break :b u.toConst().toMutable(limbs_buffer[start..buf_index]);
+ };
+ var rem = b: {
+ const start = buf_index;
+ buf_index += s.limbs.len;
+ break :b Mutable.init(limbs_buffer[start..buf_index], 0);
+ };
+
+ while (true) {
+ t.divFloor(&rem, a, s.toConst(), limbs_buffer[buf_index..]);
+ t.add(t.toConst(), s.toConst());
+ u.shiftRight(t.toConst(), 1);
+
+ if (u.toConst().order(s.toConst()).compare(.gte)) {
+ r.copy(s.toConst());
+ return;
+ }
+
+ // Avoid copying u to s by swapping u and s
+ var tmp_s = s;
+ s = u;
+ u = tmp_s;
+ }
+ }
+
/// rma may not alias x or y.
/// x and y may alias each other.
/// Asserts that `rma` has enough limbs to store the result. Upper bound is given by `calcGcdNoAliasLimbLen`.
@@ -1481,7 +1546,7 @@ pub const Mutable = struct {
if (yi != 0) break i;
} else unreachable;
- const xy_trailing = math.min(x_trailing, y_trailing);
+ const xy_trailing = @min(x_trailing, y_trailing);
if (y.len - xy_trailing == 1) {
const divisor = y.limbs[y.len - 1];
@@ -1519,7 +1584,7 @@ pub const Mutable = struct {
r.positive = r_positive;
}
- if (xy_trailing != 0) {
+ if (xy_trailing != 0 and r.limbs[r.len - 1] != 0) {
// Manually shift here since we know its limb aligned.
mem.copyBackwards(Limb, r.limbs[xy_trailing..], r.limbs[0..r.len]);
@memset(r.limbs[0..xy_trailing], 0);
@@ -1562,7 +1627,7 @@ pub const Mutable = struct {
// while x >= y * b^(n - t):
// x -= y * b^(n - t)
// q[n - t] += 1
- // Note, this algorithm is performed only once if y[t] > radix/2 and y is even, which we
+ // Note, this algorithm is performed only once if y[t] > base/2 and y is even, which we
// enforced in step 0. This means we can replace the while with an if.
// Note, multiplication by b^(n - t) comes down to shifting to the right by n - t limbs.
// We can also replace x >= y * b^(n - t) by x/b^(n - t) >= y, and use shifts for that.
@@ -1825,7 +1890,7 @@ pub const Mutable = struct {
/// if it were a field in packed memory at the provided bit offset.
pub fn readPackedTwosComplement(
x: *Mutable,
- bytes: []const u8,
+ buffer: []const u8,
bit_offset: usize,
bit_count: usize,
endian: Endian,
@@ -1844,11 +1909,11 @@ pub const Mutable = struct {
const total_bits = bit_offset + bit_count;
var last_byte = switch (endian) {
.Little => ((total_bits + 7) / 8) - 1,
- .Big => bytes.len - ((total_bits + 7) / 8),
+ .Big => buffer.len - ((total_bits + 7) / 8),
};
const sign_bit = @as(u8, 1) << @intCast(u3, (total_bits - 1) % 8);
- positive = ((bytes[last_byte] & sign_bit) == 0);
+ positive = ((buffer[last_byte] & sign_bit) == 0);
}
// Copy all complete limbs
@@ -1857,7 +1922,7 @@ pub const Mutable = struct {
var bit_index: usize = 0;
while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) {
// Read one Limb of bits
- var limb = mem.readPackedInt(Limb, bytes, bit_index + bit_offset, endian);
+ var limb = mem.readPackedInt(Limb, buffer, bit_index + bit_offset, endian);
bit_index += @bitSizeOf(Limb);
// 2's complement (bitwise not, then add carry bit)
@@ -1873,10 +1938,10 @@ pub const Mutable = struct {
if (bit_count != bit_index) {
// Read all remaining bits
var limb = switch (signedness) {
- .unsigned => mem.readVarPackedInt(Limb, bytes, bit_index + bit_offset, bit_count - bit_index, endian, .unsigned),
+ .unsigned => mem.readVarPackedInt(Limb, buffer, bit_index + bit_offset, bit_count - bit_index, endian, .unsigned),
.signed => b: {
const SLimb = std.meta.Int(.signed, @bitSizeOf(Limb));
- const limb = mem.readVarPackedInt(SLimb, bytes, bit_index + bit_offset, bit_count - bit_index, endian, .signed);
+ const limb = mem.readVarPackedInt(SLimb, buffer, bit_index + bit_offset, bit_count - bit_index, endian, .signed);
break :b @bitCast(Limb, limb);
},
};
@@ -2037,7 +2102,7 @@ pub const Const = struct {
add_res = ov[0];
carry = ov[1];
sum += @popCount(add_res);
- remaining_bits -= limb_bits; // Asserted not to undeflow by fitsInTwosComp
+ remaining_bits -= limb_bits; // Asserted not to underflow by fitsInTwosComp
}
// The most significant limb may have fewer than @bitSizeOf(Limb) meaningful bits,
@@ -2093,6 +2158,9 @@ pub const Const = struct {
pub fn to(self: Const, comptime T: type) ConvertError!T {
switch (@typeInfo(T)) {
.Int => |info| {
+ // Make sure -0 is handled correctly.
+ if (self.eqZero()) return 0;
+
const UT = std.meta.Int(.unsigned, info.bits);
if (!self.fitsInTwosComp(info.signedness, info.bits)) {
@@ -2141,20 +2209,20 @@ pub const Const = struct {
out_stream: anytype,
) !void {
_ = options;
- comptime var radix = 10;
+ comptime var base = 10;
comptime var case: std.fmt.Case = .lower;
if (fmt.len == 0 or comptime mem.eql(u8, fmt, "d")) {
- radix = 10;
+ base = 10;
case = .lower;
} else if (comptime mem.eql(u8, fmt, "b")) {
- radix = 2;
+ base = 2;
case = .lower;
} else if (comptime mem.eql(u8, fmt, "x")) {
- radix = 16;
+ base = 16;
case = .lower;
} else if (comptime mem.eql(u8, fmt, "X")) {
- radix = 16;
+ base = 16;
case = .upper;
} else {
std.fmt.invalidFmtError(fmt, self);
@@ -2172,8 +2240,8 @@ pub const Const = struct {
.limbs = &([1]Limb{comptime math.maxInt(Limb)} ** available_len),
.positive = false,
};
- var buf: [biggest.sizeInBaseUpperBound(radix)]u8 = undefined;
- const len = self.toString(&buf, radix, case, &limbs);
+ var buf: [biggest.sizeInBaseUpperBound(base)]u8 = undefined;
+ const len = self.toString(&buf, base, case, &limbs);
return out_stream.writeAll(buf[0..len]);
}
@@ -2318,7 +2386,7 @@ pub const Const = struct {
///
/// This is equivalent to storing the value of an integer with `bit_count` bits as
/// if it were a field in packed memory at the provided bit offset.
- pub fn writePackedTwosComplement(x: Const, bytes: []u8, bit_offset: usize, bit_count: usize, endian: Endian) void {
+ pub fn writePackedTwosComplement(x: Const, buffer: []u8, bit_offset: usize, bit_count: usize, endian: Endian) void {
assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count));
// Copy all complete limbs
@@ -2336,7 +2404,7 @@ pub const Const = struct {
}
// Write one Limb of bits
- mem.writePackedInt(Limb, bytes, bit_index + bit_offset, limb, endian);
+ mem.writePackedInt(Limb, buffer, bit_index + bit_offset, limb, endian);
bit_index += @bitSizeOf(Limb);
}
@@ -2348,7 +2416,7 @@ pub const Const = struct {
if (!x.positive) limb = ~limb +% carry;
// Write all remaining bits
- mem.writeVarPackedInt(bytes, bit_index + bit_offset, bit_count - bit_index, limb, endian);
+ mem.writeVarPackedInt(buffer, bit_index + bit_offset, bit_count - bit_index, limb, endian);
}
}
@@ -2444,7 +2512,7 @@ pub const Const = struct {
return total_limb_lz + bits - total_limb_bits;
}
- pub fn ctz(a: Const) Limb {
+ pub fn ctz(a: Const, bits: Limb) Limb {
// Limbs are stored in little-endian order.
var result: Limb = 0;
for (a.limbs) |limb| {
@@ -2452,7 +2520,7 @@ pub const Const = struct {
result += limb_tz;
if (limb_tz != @sizeOf(Limb) * 8) break;
}
- return result;
+ return @min(result, bits);
}
};
@@ -2521,7 +2589,7 @@ pub const Managed = struct {
.allocator = allocator,
.metadata = 1,
.limbs = block: {
- const limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));
+ const limbs = try allocator.alloc(Limb, @max(default_capacity, capacity));
limbs[0] = 0;
break :block limbs;
},
@@ -2813,7 +2881,7 @@ pub const Managed = struct {
r.setMetadata(m.positive, m.len);
}
- /// r = a + b with 2s-complement wrapping semantics. Returns whether any overflow occured.
+ /// r = a + b with 2s-complement wrapping semantics. Returns whether any overflow occurred.
///
/// r, a and b may be aliases.
///
@@ -2850,13 +2918,13 @@ pub const Managed = struct {
///
/// Returns an error if memory could not be allocated.
pub fn sub(r: *Managed, a: *const Managed, b: *const Managed) !void {
- try r.ensureCapacity(math.max(a.len(), b.len()) + 1);
+ try r.ensureCapacity(@max(a.len(), b.len()) + 1);
var m = r.toMutable();
m.sub(a.toConst(), b.toConst());
r.setMetadata(m.positive, m.len);
}
- /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occured.
+ /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occurred.
///
/// r, a and b may be aliases.
///
@@ -2957,11 +3025,11 @@ pub const Managed = struct {
}
pub fn ensureAddScalarCapacity(r: *Managed, a: Const, scalar: anytype) !void {
- try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1);
+ try r.ensureCapacity(@max(a.limbs.len, calcLimbLen(scalar)) + 1);
}
pub fn ensureAddCapacity(r: *Managed, a: Const, b: Const) !void {
- try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1);
+ try r.ensureCapacity(@max(a.limbs.len, b.limbs.len) + 1);
}
pub fn ensureMulCapacity(rma: *Managed, a: Const, b: Const) !void {
@@ -3055,7 +3123,7 @@ pub const Managed = struct {
///
/// a and b are zero-extended to the longer of a or b.
pub fn bitOr(r: *Managed, a: *const Managed, b: *const Managed) !void {
- try r.ensureCapacity(math.max(a.len(), b.len()));
+ try r.ensureCapacity(@max(a.len(), b.len()));
var m = r.toMutable();
m.bitOr(a.toConst(), b.toConst());
r.setMetadata(m.positive, m.len);
@@ -3064,9 +3132,9 @@ pub const Managed = struct {
/// r = a & b
pub fn bitAnd(r: *Managed, a: *const Managed, b: *const Managed) !void {
const cap = if (a.isPositive() or b.isPositive())
- math.min(a.len(), b.len())
+ @min(a.len(), b.len())
else
- math.max(a.len(), b.len()) + 1;
+ @max(a.len(), b.len()) + 1;
try r.ensureCapacity(cap);
var m = r.toMutable();
m.bitAnd(a.toConst(), b.toConst());
@@ -3075,7 +3143,7 @@ pub const Managed = struct {
/// r = a ^ b
pub fn bitXor(r: *Managed, a: *const Managed, b: *const Managed) !void {
- var cap = math.max(a.len(), b.len()) + @boolToInt(a.isPositive() != b.isPositive());
+ var cap = @max(a.len(), b.len()) + @boolToInt(a.isPositive() != b.isPositive());
try r.ensureCapacity(cap);
var m = r.toMutable();
@@ -3088,7 +3156,7 @@ pub const Managed = struct {
///
/// rma's allocator is used for temporary storage to boost multiplication performance.
pub fn gcd(rma: *Managed, x: *const Managed, y: *const Managed) !void {
- try rma.ensureCapacity(math.min(x.len(), y.len()));
+ try rma.ensureCapacity(@min(x.len(), y.len()));
var m = rma.toMutable();
var limbs_buffer = std.ArrayList(Limb).init(rma.allocator);
defer limbs_buffer.deinit();
@@ -3140,6 +3208,19 @@ pub const Managed = struct {
}
}
+ /// r = ⌊√a⌋
+ pub fn sqrt(rma: *Managed, a: *const Managed) !void {
+ const needed_limbs = calcSqrtLimbsBufferLen(a.bitCountAbs());
+
+ const limbs_buffer = try rma.allocator.alloc(Limb, needed_limbs);
+ defer rma.allocator.free(limbs_buffer);
+
+ try rma.ensureCapacity((a.len() - 1) / 2 + 1);
+ var m = rma.toMutable();
+ m.sqrt(a.toConst(), limbs_buffer);
+ rma.setMetadata(m.positive, m.len);
+ }
+
/// r = truncate(Int(signedness, bit_count), a)
pub fn truncate(r: *Managed, a: *const Managed, signedness: Signedness, bit_count: usize) !void {
try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
@@ -3275,13 +3356,13 @@ fn llmulaccKaratsuba(
// For a1 and b1 we only need `limbs_after_split` limbs.
const a1 = blk: {
var a1 = a[split..];
- a1.len = math.min(llnormalize(a1), limbs_after_split);
+ a1.len = @min(llnormalize(a1), limbs_after_split);
break :blk a1;
};
const b1 = blk: {
var b1 = b[split..];
- b1.len = math.min(llnormalize(b1), limbs_after_split);
+ b1.len = @min(llnormalize(b1), limbs_after_split);
break :blk b1;
};
@@ -3300,10 +3381,10 @@ fn llmulaccKaratsuba(
// Compute p2.
// Note, we don't need to compute all of p2, just enough limbs to satisfy r.
- const p2_limbs = math.min(limbs_after_split, a1.len + b1.len);
+ const p2_limbs = @min(limbs_after_split, a1.len + b1.len);
@memset(tmp[0..p2_limbs], 0);
- llmulacc(.add, allocator, tmp[0..p2_limbs], a1[0..math.min(a1.len, p2_limbs)], b1[0..math.min(b1.len, p2_limbs)]);
+ llmulacc(.add, allocator, tmp[0..p2_limbs], a1[0..@min(a1.len, p2_limbs)], b1[0..@min(b1.len, p2_limbs)]);
const p2 = tmp[0..llnormalize(tmp[0..p2_limbs])];
// Add p2 * B to the result.
@@ -3311,7 +3392,7 @@ fn llmulaccKaratsuba(
// Add p2 * B^2 to the result if required.
if (limbs_after_split2 > 0) {
- llaccum(op, r[split * 2 ..], p2[0..math.min(p2.len, limbs_after_split2)]);
+ llaccum(op, r[split * 2 ..], p2[0..@min(p2.len, limbs_after_split2)]);
}
// Compute p0.
@@ -3325,13 +3406,13 @@ fn llmulaccKaratsuba(
llaccum(op, r, p0);
// Add p0 * B to the result. In this case, we may not need all of it.
- llaccum(op, r[split..], p0[0..math.min(limbs_after_split, p0.len)]);
+ llaccum(op, r[split..], p0[0..@min(limbs_after_split, p0.len)]);
// Finally, compute and add p1.
// From now on we only need `limbs_after_split` limbs for a0 and b0, since the result of the
// following computation will be added * B.
- const a0x = a0[0..std.math.min(a0.len, limbs_after_split)];
- const b0x = b0[0..std.math.min(b0.len, limbs_after_split)];
+ const a0x = a0[0..@min(a0.len, limbs_after_split)];
+ const b0x = b0[0..@min(b0.len, limbs_after_split)];
const j0_sign = llcmp(a0x, a1);
const j1_sign = llcmp(b1, b0x);
@@ -3463,7 +3544,7 @@ fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) bool {
return false;
}
- const split = std.math.min(y.len, acc.len);
+ const split = @min(y.len, acc.len);
var a_lo = acc[0..split];
var a_hi = acc[split..];
@@ -3942,8 +4023,8 @@ fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_
// r may alias.
// a and b must not be -0.
// Returns `true` when the result is positive.
-// If the sign of a and b is equal, then r requires at least `max(a.len, b.len)` limbs are required.
-// Otherwise, r requires at least `max(a.len, b.len) + 1` limbs.
+// If the sign of a and b is equal, then r requires at least `@max(a.len, b.len)` limbs are required.
+// Otherwise, r requires at least `@max(a.len, b.len) + 1` limbs.
fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool {
@setRuntimeSafety(debug_safety);
assert(a.len != 0 and b.len != 0);
@@ -4010,7 +4091,7 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {
assert(r.len >= 2 * x_norm.len + 1);
// Compute the square of a N-limb bigint with only (N^2 + N)/2
- // multiplications by exploting the symmetry of the coefficients around the
+ // multiplications by exploiting the symmetry of the coefficients around the
// diagonal:
//
// a b c *
@@ -4035,7 +4116,7 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {
for (x_norm, 0..) |v, i| {
// Compute and add the squares
- const overflow = llmulLimb(.add, r[2 * i ..], x[i .. i + 1], v);
+ const overflow = llmulLimb(.add, r[2 * i ..], x[i..][0..1], v);
assert(!overflow);
}
}
diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig
index 0066ce9940..9c3c1b6881 100644
--- a/lib/std/math/big/int_test.zig
+++ b/lib/std/math/big/int_test.zig
@@ -1373,6 +1373,19 @@ test "big.int div trunc single-single -/-" {
try testing.expect((try r.to(i32)) == er);
}
+test "big.int divTrunc #15535" {
+ var one = try Managed.initSet(testing.allocator, 1);
+ defer one.deinit();
+ var x = try Managed.initSet(testing.allocator, std.math.pow(u128, 2, 64));
+ defer x.deinit();
+ var r = try Managed.init(testing.allocator);
+ defer r.deinit();
+ var q = try Managed.init(testing.allocator);
+ defer q.deinit();
+ try q.divTrunc(&r, &x, &x);
+ try testing.expect(r.order(one) == std.math.Order.lt);
+}
+
test "big.int divFloor #10932" {
var a = try Managed.init(testing.allocator);
defer a.deinit();
@@ -2012,15 +2025,10 @@ test "big.int shift-right negative" {
defer arg2.deinit();
try a.shiftRight(&arg2, 10);
try testing.expect((try a.to(i32)) == -1); // -5 >> 10 == -1
-}
-
-test "big.int shift-right negative" {
- var a = try Managed.init(testing.allocator);
- defer a.deinit();
- var arg = try Managed.initSet(testing.allocator, -10);
- defer arg.deinit();
- try a.shiftRight(&arg, 1232);
+ var arg3 = try Managed.initSet(testing.allocator, -10);
+ defer arg3.deinit();
+ try a.shiftRight(&arg3, 1232);
try testing.expect((try a.to(i32)) == -1); // -10 >> 1232 == -1
}
@@ -2483,7 +2491,7 @@ test "big.int gcd non-one small" {
try testing.expect((try r.to(u32)) == 1);
}
-test "big.int gcd non-one small" {
+test "big.int gcd non-one medium" {
var a = try Managed.initSet(testing.allocator, 4864);
defer a.deinit();
var b = try Managed.initSet(testing.allocator, 3458);
@@ -2614,6 +2622,36 @@ test "big.int pow" {
}
}
+test "big.int sqrt" {
+ var r = try Managed.init(testing.allocator);
+ defer r.deinit();
+ var a = try Managed.init(testing.allocator);
+ defer a.deinit();
+
+ // not aliased
+ try r.set(0);
+ try a.set(25);
+ try r.sqrt(&a);
+ try testing.expectEqual(@as(i32, 5), try r.to(i32));
+
+ // aliased
+ try a.set(25);
+ try a.sqrt(&a);
+ try testing.expectEqual(@as(i32, 5), try a.to(i32));
+
+ // bottom
+ try r.set(0);
+ try a.set(24);
+ try r.sqrt(&a);
+ try testing.expectEqual(@as(i32, 4), try r.to(i32));
+
+ // large number
+ try r.set(0);
+ try a.set(0x1_0000_0000_0000);
+ try r.sqrt(&a);
+ try testing.expectEqual(@as(i32, 0x100_0000), try r.to(i32));
+}
+
test "big.int regression test for 1 limb overflow with alias" {
// Note these happen to be two consecutive Fibonacci sequence numbers, the
// first two whose sum exceeds 2**64.
diff --git a/lib/std/math/big/rational.zig b/lib/std/math/big/rational.zig
index c3609a6fa2..cdc33e351d 100644
--- a/lib/std/math/big/rational.zig
+++ b/lib/std/math/big/rational.zig
@@ -782,36 +782,38 @@ test "big.rational mul" {
}
test "big.rational div" {
- var a = try Rational.init(testing.allocator);
- defer a.deinit();
- var b = try Rational.init(testing.allocator);
- defer b.deinit();
- var r = try Rational.init(testing.allocator);
- defer r.deinit();
+ {
+ var a = try Rational.init(testing.allocator);
+ defer a.deinit();
+ var b = try Rational.init(testing.allocator);
+ defer b.deinit();
+ var r = try Rational.init(testing.allocator);
+ defer r.deinit();
- try a.setRatio(78923, 23341);
- try b.setRatio(123097, 12441414);
- try a.div(a, b);
+ try a.setRatio(78923, 23341);
+ try b.setRatio(123097, 12441414);
+ try a.div(a, b);
- try r.setRatio(75531824394, 221015929);
- try testing.expect((try a.order(r)) == .eq);
-}
+ try r.setRatio(75531824394, 221015929);
+ try testing.expect((try a.order(r)) == .eq);
+ }
-test "big.rational div" {
- var a = try Rational.init(testing.allocator);
- defer a.deinit();
- var r = try Rational.init(testing.allocator);
- defer r.deinit();
+ {
+ var a = try Rational.init(testing.allocator);
+ defer a.deinit();
+ var r = try Rational.init(testing.allocator);
+ defer r.deinit();
- try a.setRatio(78923, 23341);
- a.invert();
+ try a.setRatio(78923, 23341);
+ a.invert();
- try r.setRatio(23341, 78923);
- try testing.expect((try a.order(r)) == .eq);
+ try r.setRatio(23341, 78923);
+ try testing.expect((try a.order(r)) == .eq);
- try a.setRatio(-78923, 23341);
- a.invert();
+ try a.setRatio(-78923, 23341);
+ a.invert();
- try r.setRatio(-23341, 78923);
- try testing.expect((try a.order(r)) == .eq);
+ try r.setRatio(-23341, 78923);
+ try testing.expect((try a.order(r)) == .eq);
+ }
}
diff --git a/lib/std/math/complex/tan.zig b/lib/std/math/complex/tan.zig
index 9e1025f74f..a0ce5d18e0 100644
--- a/lib/std/math/complex/tan.zig
+++ b/lib/std/math/complex/tan.zig
@@ -4,7 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
-/// Returns the tanget of z.
+/// Returns the tangent of z.
pub fn tan(z: anytype) Complex(@TypeOf(z.re)) {
const T = @TypeOf(z.re);
const q = Complex(T).init(-z.im, z.re);
diff --git a/lib/std/math/ilogb.zig b/lib/std/math/ilogb.zig
index 88ae168406..c091619f3a 100644
--- a/lib/std/math/ilogb.zig
+++ b/lib/std/math/ilogb.zig
@@ -48,7 +48,7 @@ fn ilogbX(comptime T: type, x: T) i32 {
}
// offset sign bit, exponent bits, and integer bit (if present) + bias
- const offset = 1 + exponentBits + @boolToInt(T == f80) - exponentBias;
+ const offset = 1 + exponentBits + @as(comptime_int, @boolToInt(T == f80)) - exponentBias;
return offset - @intCast(i32, @clz(u));
}
diff --git a/lib/std/math/ldexp.zig b/lib/std/math/ldexp.zig
index d2fd8db9b7..8947475159 100644
--- a/lib/std/math/ldexp.zig
+++ b/lib/std/math/ldexp.zig
@@ -48,7 +48,7 @@ pub fn ldexp(x: anytype, n: i32) @TypeOf(x) {
return @bitCast(T, sign_bit); // Severe underflow. Return +/- 0
// Result underflowed, we need to shift and round
- const shift = @intCast(Log2Int(TBits), math.min(-n, -(exponent + n) + 1));
+ const shift = @intCast(Log2Int(TBits), @min(-n, -(exponent + n) + 1));
const exact_tie: bool = @ctz(repr) == shift - 1;
var result = repr & mantissa_mask;
diff --git a/lib/std/math/log10.zig b/lib/std/math/log10.zig
index b1ecb9ad2b..6b5758763c 100644
--- a/lib/std/math/log10.zig
+++ b/lib/std/math/log10.zig
@@ -58,7 +58,7 @@ pub fn log10_int(x: anytype) Log2Int(@TypeOf(x)) {
var log: u32 = 0;
inline for (0..11) |i| {
- // Unnecesary branches should be removed by the compiler
+ // Unnecessary branches should be removed by the compiler
if (bit_size > (1 << (11 - i)) * 5 * @log2(10.0) and val >= pow10((1 << (11 - i)) * 5)) {
const num_digits = (1 << (11 - i)) * 5;
val /= pow10(num_digits);
diff --git a/lib/std/math/scalbn.zig b/lib/std/math/scalbn.zig
index 294ee4abf0..2c8c3733fa 100644
--- a/lib/std/math/scalbn.zig
+++ b/lib/std/math/scalbn.zig
@@ -3,11 +3,11 @@ const expect = std.testing.expect;
/// Returns a * FLT_RADIX ^ exp.
///
-/// Zig only supports binary radix IEEE-754 floats. Hence FLT_RADIX=2, and this is an alias for ldexp.
+/// Zig only supports binary base IEEE-754 floats. Hence FLT_RADIX=2, and this is an alias for ldexp.
pub const scalbn = @import("ldexp.zig").ldexp;
test "math.scalbn" {
- // Verify we are using radix 2.
+ // Verify we are using base 2.
try expect(scalbn(@as(f16, 1.5), 4) == 24.0);
try expect(scalbn(@as(f32, 1.5), 4) == 24.0);
try expect(scalbn(@as(f64, 1.5), 4) == 24.0);
diff --git a/lib/std/math/sqrt.zig b/lib/std/math/sqrt.zig
index e642f8a309..926582034e 100644
--- a/lib/std/math/sqrt.zig
+++ b/lib/std/math/sqrt.zig
@@ -11,7 +11,7 @@ const maxInt = std.math.maxInt;
/// - sqrt(+-0) = +-0
/// - sqrt(x) = nan if x < 0
/// - sqrt(nan) = nan
-/// TODO Decide if all this logic should be implemented directly in the @sqrt bultin function.
+/// TODO Decide if all this logic should be implemented directly in the @sqrt builtin function.
pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) {
const T = @TypeOf(x);
switch (@typeInfo(T)) {