mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Merge 9a30054ff0 into d0ba6642b5
This commit is contained in:
commit
23bf24071d
3 changed files with 262 additions and 17 deletions
|
|
@ -238,6 +238,7 @@ pub const sinh = @import("math/sinh.zig").sinh;
|
||||||
pub const cosh = @import("math/cosh.zig").cosh;
|
pub const cosh = @import("math/cosh.zig").cosh;
|
||||||
pub const tanh = @import("math/tanh.zig").tanh;
|
pub const tanh = @import("math/tanh.zig").tanh;
|
||||||
pub const gcd = @import("math/gcd.zig").gcd;
|
pub const gcd = @import("math/gcd.zig").gcd;
|
||||||
|
pub const egcd = @import("math/egcd.zig").egcd;
|
||||||
pub const lcm = @import("math/lcm.zig").lcm;
|
pub const lcm = @import("math/lcm.zig").lcm;
|
||||||
pub const gamma = @import("math/gamma.zig").gamma;
|
pub const gamma = @import("math/gamma.zig").gamma;
|
||||||
pub const lgamma = @import("math/gamma.zig").lgamma;
|
pub const lgamma = @import("math/gamma.zig").lgamma;
|
||||||
|
|
|
||||||
240
lib/std/math/egcd.zig
Normal file
240
lib/std/math/egcd.zig
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
//! Extended Greatest Common Divisor (https://mathworld.wolfram.com/ExtendedGreatestCommonDivisor.html)
|
||||||
|
const std = @import("../std.zig");
|
||||||
|
|
||||||
|
/// Result type of `egcd`.
|
||||||
|
pub fn Result(S: anytype) type {
|
||||||
|
const N = switch (S) {
|
||||||
|
comptime_int => comptime_int,
|
||||||
|
else => |T| std.meta.Int(.unsigned, @bitSizeOf(T)),
|
||||||
|
};
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
gcd: N,
|
||||||
|
bezout_coeff_1: S,
|
||||||
|
bezout_coeff_2: S,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the Extended Greatest Common Divisor (EGCD) of two signed integers (`a` and `b`) which are not both zero.
|
||||||
|
pub fn egcd(a: anytype, b: anytype) Result(@TypeOf(a, b)) {
|
||||||
|
const S = switch (@TypeOf(a, b)) {
|
||||||
|
comptime_int => b: {
|
||||||
|
const n = @max(@abs(a), @abs(b));
|
||||||
|
break :b std.math.IntFittingRange(-n, n);
|
||||||
|
},
|
||||||
|
else => |T| T,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (@typeInfo(S) != .int or @typeInfo(S).int.signedness != .signed)
|
||||||
|
@compileError("`a` and `b` must be signed integers");
|
||||||
|
|
||||||
|
std.debug.assert(a != 0 or b != 0);
|
||||||
|
|
||||||
|
if (a == 0) return .{ .gcd = @abs(b), .bezout_coeff_1 = 0, .bezout_coeff_2 = std.math.sign(b) };
|
||||||
|
if (b == 0) return .{ .gcd = @abs(a), .bezout_coeff_1 = std.math.sign(a), .bezout_coeff_2 = 0 };
|
||||||
|
|
||||||
|
const other: S, const odd: S, const shift, const switch_coeff = b: {
|
||||||
|
const xz = @ctz(@as(S, a));
|
||||||
|
const yz = @ctz(@as(S, b));
|
||||||
|
break :b if (xz < yz) .{ b, a, xz, true } else .{ a, b, yz, false };
|
||||||
|
};
|
||||||
|
const toinv = @shrExact(other, @intCast(shift));
|
||||||
|
const ctrl = @shrExact(odd, @intCast(shift)); // Invariant: |s|, |t|, |ctrl| < |MIN_OF(S)|
|
||||||
|
const abs_ctrl = @abs(ctrl);
|
||||||
|
const half_ctrl: S = @intCast(1 + abs_ctrl >> 1);
|
||||||
|
|
||||||
|
var s: S = std.math.sign(toinv);
|
||||||
|
var t: S = 0;
|
||||||
|
|
||||||
|
var x = @abs(toinv);
|
||||||
|
var y = abs_ctrl;
|
||||||
|
|
||||||
|
{
|
||||||
|
const xz = @ctz(x);
|
||||||
|
x = @shrExact(x, @intCast(xz));
|
||||||
|
for (0..xz) |_| {
|
||||||
|
const half_s = s >> 1;
|
||||||
|
if (s & 1 == 0)
|
||||||
|
s = half_s
|
||||||
|
else
|
||||||
|
s = half_s + half_ctrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var y_minus_x = y -% x;
|
||||||
|
while (y_minus_x != 0) : (y_minus_x = y -% x) {
|
||||||
|
const t_minus_s = t - s;
|
||||||
|
const copy_x = x;
|
||||||
|
const copy_s = s;
|
||||||
|
const xz = @ctz(y_minus_x);
|
||||||
|
|
||||||
|
s -= t;
|
||||||
|
const carry = x < y;
|
||||||
|
x -%= y;
|
||||||
|
if (carry) {
|
||||||
|
x = y_minus_x;
|
||||||
|
y = copy_x;
|
||||||
|
s = t_minus_s;
|
||||||
|
t = copy_s;
|
||||||
|
}
|
||||||
|
x = @shrExact(x, @intCast(xz));
|
||||||
|
for (0..xz) |_| {
|
||||||
|
const half_s = s >> 1;
|
||||||
|
if (s & 1 == 0)
|
||||||
|
s = half_s
|
||||||
|
else
|
||||||
|
s = half_s + half_ctrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s < 0) s = @intCast(abs_ctrl - @abs(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using integer widening is only a temporary solution.
|
||||||
|
const W = std.meta.Int(.signed, @bitSizeOf(S) * 2);
|
||||||
|
t = @intCast(@divExact(y - @as(W, s) * toinv, ctrl));
|
||||||
|
const final_s, const final_t = if (switch_coeff) .{ t, s } else .{ s, t };
|
||||||
|
return .{
|
||||||
|
.gcd = @shlExact(y, @intCast(shift)),
|
||||||
|
.bezout_coeff_1 = final_s,
|
||||||
|
.bezout_coeff_2 = final_t,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
{
|
||||||
|
const a: i2 = 0;
|
||||||
|
const b: i2 = 1;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s: i2 = r.bezout_coeff_1;
|
||||||
|
const t: i2 = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i8 = -128;
|
||||||
|
const b: i8 = 127;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s: i16 = r.bezout_coeff_1;
|
||||||
|
const t: i16 = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i16 = -32768;
|
||||||
|
const b: i16 = -32768;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s: i32 = r.bezout_coeff_1;
|
||||||
|
const t: i32 = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i32 = 128;
|
||||||
|
const b: i32 = 112;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s: i64 = r.bezout_coeff_1;
|
||||||
|
const t: i64 = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i32 = 4 * 89;
|
||||||
|
const b: i32 = 2 * 17;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s: i64 = r.bezout_coeff_1;
|
||||||
|
const t: i64 = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i8 = 127;
|
||||||
|
const b: i8 = 126;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s: i16 = r.bezout_coeff_1;
|
||||||
|
const t: i16 = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i4 = -8;
|
||||||
|
const b: i4 = 1;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s = r.bezout_coeff_1;
|
||||||
|
const t = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i4 = -8;
|
||||||
|
const b: i4 = 5;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s: i8 = r.bezout_coeff_1;
|
||||||
|
const t: i8 = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i32 = 0;
|
||||||
|
const b: i32 = 5;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s = r.bezout_coeff_1;
|
||||||
|
const t = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i32 = 5;
|
||||||
|
const b: i32 = 0;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s = r.bezout_coeff_1;
|
||||||
|
const t = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const a: i32 = 21;
|
||||||
|
const b: i32 = 15;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s = r.bezout_coeff_1;
|
||||||
|
const t = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a: i32 = -21;
|
||||||
|
const b: i32 = 15;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s = r.bezout_coeff_1;
|
||||||
|
const t = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a = -21;
|
||||||
|
const b = 15;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s = r.bezout_coeff_1;
|
||||||
|
const t = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a = 927372692193078999176;
|
||||||
|
const b = 573147844013817084101;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s = r.bezout_coeff_1;
|
||||||
|
const t = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const a = 453973694165307953197296969697410619233826;
|
||||||
|
const b = 280571172992510140037611932413038677189525;
|
||||||
|
const r = egcd(a, b);
|
||||||
|
const g = r.gcd;
|
||||||
|
const s = r.bezout_coeff_1;
|
||||||
|
const t = r.bezout_coeff_2;
|
||||||
|
try std.testing.expect(s * a + t * b == g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! Greatest common divisor (https://mathworld.wolfram.com/GreatestCommonDivisor.html)
|
//! Greatest Common Divisor (https://mathworld.wolfram.com/GreatestCommonDivisor.html)
|
||||||
const std = @import("std");
|
const std = @import("../std.zig");
|
||||||
|
|
||||||
/// Returns the greatest common divisor (GCD) of two unsigned integers (`a` and `b`) which are not both zero.
|
/// Returns the Greatest Common Divisor (GCD) of two unsigned integers (`a` and `b`) which are not both zero.
|
||||||
/// For example, the GCD of `8` and `12` is `4`, that is, `gcd(8, 12) == 4`.
|
/// For example, the GCD of `8` and `12` is `4`, that is, `gcd(8, 12) == 4`.
|
||||||
pub fn gcd(a: anytype, b: anytype) @TypeOf(a, b) {
|
pub fn gcd(a: anytype, b: anytype) @TypeOf(a, b) {
|
||||||
const N = switch (@TypeOf(a, b)) {
|
const N = switch (@TypeOf(a, b)) {
|
||||||
|
|
@ -9,12 +9,14 @@ pub fn gcd(a: anytype, b: anytype) @TypeOf(a, b) {
|
||||||
comptime_int => std.math.IntFittingRange(@min(a, b), @max(a, b)),
|
comptime_int => std.math.IntFittingRange(@min(a, b), @max(a, b)),
|
||||||
else => |T| T,
|
else => |T| T,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (@typeInfo(N) != .int or @typeInfo(N).int.signedness != .unsigned) {
|
if (@typeInfo(N) != .int or @typeInfo(N).int.signedness != .unsigned) {
|
||||||
@compileError("`a` and `b` must be usigned integers");
|
@compileError("`a` and `b` must be unsigned integers");
|
||||||
}
|
}
|
||||||
|
|
||||||
// using an optimised form of Stein's algorithm:
|
// using an optimised form of Stein's algorithm:
|
||||||
// https://en.wikipedia.org/wiki/Binary_GCD_algorithm
|
// https://en.wikipedia.org/wiki/Binary_GCD_algorithm
|
||||||
|
|
||||||
std.debug.assert(a != 0 or b != 0);
|
std.debug.assert(a != 0 or b != 0);
|
||||||
|
|
||||||
if (a == 0) return b;
|
if (a == 0) return b;
|
||||||
|
|
@ -26,25 +28,27 @@ pub fn gcd(a: anytype, b: anytype) @TypeOf(a, b) {
|
||||||
const xz = @ctz(x);
|
const xz = @ctz(x);
|
||||||
const yz = @ctz(y);
|
const yz = @ctz(y);
|
||||||
const shift = @min(xz, yz);
|
const shift = @min(xz, yz);
|
||||||
x >>= @intCast(xz);
|
x = @shrExact(x, @intCast(xz));
|
||||||
y >>= @intCast(yz);
|
y = @shrExact(y, @intCast(yz));
|
||||||
|
|
||||||
var diff = y -% x;
|
var y_minus_x = y -% x;
|
||||||
while (diff != 0) : (diff = y -% x) {
|
while (y_minus_x != 0) : (y_minus_x = y -% x) {
|
||||||
// ctz is invariant under negation, we
|
const copy_x = x;
|
||||||
// put it here to ease data dependencies,
|
const zeros = @ctz(y_minus_x);
|
||||||
// makes the CPU happy.
|
const carry = x < y;
|
||||||
const zeros = @ctz(diff);
|
x -%= y;
|
||||||
if (x > y) diff = -%diff;
|
if (carry) {
|
||||||
y = @min(x, y);
|
x = y_minus_x;
|
||||||
x = diff >> @intCast(zeros);
|
y = copy_x;
|
||||||
|
}
|
||||||
|
x = @shrExact(x, @intCast(zeros));
|
||||||
}
|
}
|
||||||
return y << @intCast(shift);
|
|
||||||
|
return @shlExact(y, @intCast(shift));
|
||||||
}
|
}
|
||||||
|
|
||||||
test gcd {
|
test gcd {
|
||||||
const expectEqual = std.testing.expectEqual;
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
|
||||||
try expectEqual(gcd(0, 5), 5);
|
try expectEqual(gcd(0, 5), 5);
|
||||||
try expectEqual(gcd(5, 0), 5);
|
try expectEqual(gcd(5, 0), 5);
|
||||||
try expectEqual(gcd(8, 12), 4);
|
try expectEqual(gcd(8, 12), 4);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue