diff options
| author | Loris Cro <kappaloris@gmail.com> | 2023-06-18 09:06:40 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-06-18 09:06:40 +0200 |
| commit | 216ef10dc471e4db60a30208be178d6c59efeaaf (patch) | |
| tree | 8c239dab283ae9cb3b7fe099bae240bcc53f894e /lib/std/math | |
| parent | 0fc1d396495c1ab482197021dedac8bea3f9401c (diff) | |
| parent | 729a051e9e38674233190aea23c0ac8c134f2d67 (diff) | |
| download | zig-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.zig | 2 | ||||
| -rw-r--r-- | lib/std/math/big/int.zig | 239 | ||||
| -rw-r--r-- | lib/std/math/big/int_test.zig | 56 | ||||
| -rw-r--r-- | lib/std/math/big/rational.zig | 52 | ||||
| -rw-r--r-- | lib/std/math/complex/tan.zig | 2 | ||||
| -rw-r--r-- | lib/std/math/ilogb.zig | 2 | ||||
| -rw-r--r-- | lib/std/math/ldexp.zig | 2 | ||||
| -rw-r--r-- | lib/std/math/log10.zig | 2 | ||||
| -rw-r--r-- | lib/std/math/scalbn.zig | 4 | ||||
| -rw-r--r-- | lib/std/math/sqrt.zig | 2 |
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)) { |
