From d3e1f323626ba5955aa98628c9c817a19baa35fb Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 27 Mar 2019 22:28:38 +1300 Subject: [PATCH] Small fixes for big.Rational and corrections for gcdLehmer The int div code still causes some edge cases to fail right now. --- std/math/big/int.zig | 7 +- std/math/big/rational.zig | 160 ++++++++++++++++++++------------------ 2 files changed, 91 insertions(+), 76 deletions(-) diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 89be663a4a..6f4ce1b54e 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -93,6 +93,7 @@ pub const Int = struct { } pub fn clone(other: Int) !Int { + other.assertWritable(); return Int{ .allocator = other.allocator, .positive = other.positive, @@ -804,11 +805,13 @@ pub const Int = struct { rem.positive = true; } else { // x and y are modified during division - var x = try a.clone(); + var x = try Int.initCapacity(quo.allocator.?, a.len); defer x.deinit(); + try x.copy(a); - var y = try b.clone(); + var y = try Int.initCapacity(quo.allocator.?, b.len); defer y.deinit(); + try y.copy(b); // x may grow one limb during normalization try quo.ensureCapacity(a.len + y.len); diff --git a/std/math/big/rational.zig b/std/math/big/rational.zig index 188d36d909..ca3b3a5926 100644 --- a/std/math/big/rational.zig +++ b/std/math/big/rational.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const debug = std.debug; const math = std.math; const mem = std.mem; +const testing = std.testing; const Allocator = mem.Allocator; const ArrayList = std.ArrayList; @@ -425,6 +426,7 @@ var al = debug.global_allocator; const SignedDoubleLimb = @IntType(true, DoubleLimb.bit_count); fn gcd(rma: *Int, x: Int, y: Int) !void { + rma.assertWritable(); var r = rma; var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr; @@ -463,19 +465,23 @@ fn FixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Int { // // r = gcd(x, y) where x, y > 0 fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void { - debug.assert(xa.positive and ya.positive); - debug.assert(xa.cmp(ya) >= 0); - var x = try xa.clone(); + x.abs(); defer x.deinit(); var y = try ya.clone(); + y.abs(); defer y.deinit(); + if (x.cmp(y) < 0) { + x.swap(&y); + } + var T = try Int.init(r.allocator.?); defer T.deinit(); while (y.len > 1) { + debug.assert(x.positive and y.positive); debug.assert(x.len >= y.len); // chop the leading zeros of the limbs and normalize @@ -540,7 +546,13 @@ fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void { try r.add(x, r.*); x.swap(&T); + x.abs(); y.swap(r); + y.abs(); + + if (x.cmp(y) < 0) { + x.swap(&y); + } } } @@ -563,7 +575,7 @@ test "big.rational gcd non-one small" { try gcd(&r, a, b); - debug.assert((try r.to(u32)) == 1); + testing.expect((try r.to(u32)) == 1); } test "big.rational gcd non-one small" { @@ -573,7 +585,7 @@ test "big.rational gcd non-one small" { try gcd(&r, a, b); - debug.assert((try r.to(u32)) == 38); + testing.expect((try r.to(u32)) == 38); } test "big.rational gcd non-one large" { @@ -583,7 +595,7 @@ test "big.rational gcd non-one large" { try gcd(&r, a, b); - debug.assert((try r.to(u32)) == 4369); + testing.expect((try r.to(u32)) == 4369); } test "big.rational gcd large multi-limb result" { @@ -593,11 +605,11 @@ test "big.rational gcd large multi-limb result" { try gcd(&r, a, b); - debug.assert((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1); + testing.expect((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1); } fn extractLowBits(a: Int, comptime T: type) T { - debug.assert(@typeId(T) == builtin.TypeId.Int); + testing.expect(@typeId(T) == builtin.TypeId.Int); if (T.bit_count <= Limb.bit_count) { return @truncate(T, a.limbs[0]); @@ -619,71 +631,71 @@ test "big.rational extractLowBits" { var a = try Int.initSet(al, 0x11112222333344441234567887654321); const a1 = extractLowBits(a, u8); - debug.assert(a1 == 0x21); + testing.expect(a1 == 0x21); const a2 = extractLowBits(a, u16); - debug.assert(a2 == 0x4321); + testing.expect(a2 == 0x4321); const a3 = extractLowBits(a, u32); - debug.assert(a3 == 0x87654321); + testing.expect(a3 == 0x87654321); const a4 = extractLowBits(a, u64); - debug.assert(a4 == 0x1234567887654321); + testing.expect(a4 == 0x1234567887654321); const a5 = extractLowBits(a, u128); - debug.assert(a5 == 0x11112222333344441234567887654321); + testing.expect(a5 == 0x11112222333344441234567887654321); } test "big.rational set" { var a = try Rational.init(al); try a.setInt(5); - debug.assert((try a.p.to(u32)) == 5); - debug.assert((try a.q.to(u32)) == 1); + testing.expect((try a.p.to(u32)) == 5); + testing.expect((try a.q.to(u32)) == 1); try a.setRatio(7, 3); - debug.assert((try a.p.to(u32)) == 7); - debug.assert((try a.q.to(u32)) == 3); + testing.expect((try a.p.to(u32)) == 7); + testing.expect((try a.q.to(u32)) == 3); try a.setRatio(9, 3); - debug.assert((try a.p.to(i32)) == 3); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == 3); + testing.expect((try a.q.to(i32)) == 1); try a.setRatio(-9, 3); - debug.assert((try a.p.to(i32)) == -3); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == -3); + testing.expect((try a.q.to(i32)) == 1); try a.setRatio(9, -3); - debug.assert((try a.p.to(i32)) == -3); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == -3); + testing.expect((try a.q.to(i32)) == 1); try a.setRatio(-9, -3); - debug.assert((try a.p.to(i32)) == 3); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == 3); + testing.expect((try a.q.to(i32)) == 1); } test "big.rational setFloat" { var a = try Rational.init(al); try a.setFloat(f64, 2.5); - debug.assert((try a.p.to(i32)) == 5); - debug.assert((try a.q.to(i32)) == 2); + testing.expect((try a.p.to(i32)) == 5); + testing.expect((try a.q.to(i32)) == 2); try a.setFloat(f32, -2.5); - debug.assert((try a.p.to(i32)) == -5); - debug.assert((try a.q.to(i32)) == 2); + testing.expect((try a.p.to(i32)) == -5); + testing.expect((try a.q.to(i32)) == 2); try a.setFloat(f32, 3.141593); // = 3.14159297943115234375 - debug.assert((try a.p.to(u32)) == 3294199); - debug.assert((try a.q.to(u32)) == 1048576); + testing.expect((try a.p.to(u32)) == 3294199); + testing.expect((try a.q.to(u32)) == 1048576); try a.setFloat(f64, 72.141593120712409172417410926841290461290467124); // = 72.1415931207124145885245525278151035308837890625 - debug.assert((try a.p.to(u128)) == 5076513310880537); - debug.assert((try a.q.to(u128)) == 70368744177664); + testing.expect((try a.p.to(u128)) == 5076513310880537); + testing.expect((try a.q.to(u128)) == 70368744177664); } test "big.rational setFloatString" { @@ -692,8 +704,8 @@ test "big.rational setFloatString" { try a.setFloatString("72.14159312071241458852455252781510353"); // = 72.1415931207124145885245525278151035308837890625 - debug.assert((try a.p.to(u128)) == 7214159312071241458852455252781510353); - debug.assert((try a.q.to(u128)) == 100000000000000000000000000000000000); + testing.expect((try a.p.to(u128)) == 7214159312071241458852455252781510353); + testing.expect((try a.q.to(u128)) == 100000000000000000000000000000000000); } test "big.rational toFloat" { @@ -701,11 +713,11 @@ test "big.rational toFloat" { // = 3.14159297943115234375 try a.setRatio(3294199, 1048576); - debug.assert((try a.toFloat(f64)) == 3.14159297943115234375); + testing.expect((try a.toFloat(f64)) == 3.14159297943115234375); // = 72.1415931207124145885245525278151035308837890625 try a.setRatio(5076513310880537, 70368744177664); - debug.assert((try a.toFloat(f64)) == 72.141593120712409172417410926841290461290467124); + testing.expect((try a.toFloat(f64)) == 72.141593120712409172417410926841290461290467124); } test "big.rational set/to Float round-trip" { @@ -720,7 +732,7 @@ test "big.rational set/to Float round-trip" { while (i < 512) : (i += 1) { const r = prng.random.float(f64); try a.setFloat(f64, r); - debug.assert((try a.toFloat(f64)) == r); + testing.expect((try a.toFloat(f64)) == r); } } @@ -730,54 +742,54 @@ test "big.rational copy" { const b = try Int.initSet(al, 5); try a.copyInt(b); - debug.assert((try a.p.to(u32)) == 5); - debug.assert((try a.q.to(u32)) == 1); + testing.expect((try a.p.to(u32)) == 5); + testing.expect((try a.q.to(u32)) == 1); const c = try Int.initSet(al, 7); const d = try Int.initSet(al, 3); try a.copyRatio(c, d); - debug.assert((try a.p.to(u32)) == 7); - debug.assert((try a.q.to(u32)) == 3); + testing.expect((try a.p.to(u32)) == 7); + testing.expect((try a.q.to(u32)) == 3); const e = try Int.initSet(al, 9); const f = try Int.initSet(al, 3); try a.copyRatio(e, f); - debug.assert((try a.p.to(u32)) == 3); - debug.assert((try a.q.to(u32)) == 1); + testing.expect((try a.p.to(u32)) == 3); + testing.expect((try a.q.to(u32)) == 1); } test "big.rational negate" { var a = try Rational.init(al); try a.setInt(-50); - debug.assert((try a.p.to(i32)) == -50); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == -50); + testing.expect((try a.q.to(i32)) == 1); a.negate(); - debug.assert((try a.p.to(i32)) == 50); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == 50); + testing.expect((try a.q.to(i32)) == 1); a.negate(); - debug.assert((try a.p.to(i32)) == -50); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == -50); + testing.expect((try a.q.to(i32)) == 1); } test "big.rational abs" { var a = try Rational.init(al); try a.setInt(-50); - debug.assert((try a.p.to(i32)) == -50); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == -50); + testing.expect((try a.q.to(i32)) == 1); a.abs(); - debug.assert((try a.p.to(i32)) == 50); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == 50); + testing.expect((try a.q.to(i32)) == 1); a.abs(); - debug.assert((try a.p.to(i32)) == 50); - debug.assert((try a.q.to(i32)) == 1); + testing.expect((try a.p.to(i32)) == 50); + testing.expect((try a.q.to(i32)) == 1); } test "big.rational swap" { @@ -787,19 +799,19 @@ test "big.rational swap" { try a.setRatio(50, 23); try b.setRatio(17, 3); - debug.assert((try a.p.to(u32)) == 50); - debug.assert((try a.q.to(u32)) == 23); + testing.expect((try a.p.to(u32)) == 50); + testing.expect((try a.q.to(u32)) == 23); - debug.assert((try b.p.to(u32)) == 17); - debug.assert((try b.q.to(u32)) == 3); + testing.expect((try b.p.to(u32)) == 17); + testing.expect((try b.q.to(u32)) == 3); a.swap(&b); - debug.assert((try a.p.to(u32)) == 17); - debug.assert((try a.q.to(u32)) == 3); + testing.expect((try a.p.to(u32)) == 17); + testing.expect((try a.q.to(u32)) == 3); - debug.assert((try b.p.to(u32)) == 50); - debug.assert((try b.q.to(u32)) == 23); + testing.expect((try b.p.to(u32)) == 50); + testing.expect((try b.q.to(u32)) == 23); } test "big.rational cmp" { @@ -808,11 +820,11 @@ test "big.rational cmp" { try a.setRatio(500, 231); try b.setRatio(18903, 8584); - debug.assert((try a.cmp(b)) < 0); + testing.expect((try a.cmp(b)) < 0); try a.setRatio(890, 10); try b.setRatio(89, 1); - debug.assert((try a.cmp(b)) == 0); + testing.expect((try a.cmp(b)) == 0); } test "big.rational add single-limb" { @@ -821,11 +833,11 @@ test "big.rational add single-limb" { try a.setRatio(500, 231); try b.setRatio(18903, 8584); - debug.assert((try a.cmp(b)) < 0); + testing.expect((try a.cmp(b)) < 0); try a.setRatio(890, 10); try b.setRatio(89, 1); - debug.assert((try a.cmp(b)) == 0); + testing.expect((try a.cmp(b)) == 0); } test "big.rational add" { @@ -838,7 +850,7 @@ test "big.rational add" { try a.add(a, b); try r.setRatio(984786924199, 290395044174); - debug.assert((try a.cmp(r)) == 0); + testing.expect((try a.cmp(r)) == 0); } test "big.rational sub" { @@ -851,7 +863,7 @@ test "big.rational sub" { try a.sub(a, b); try r.setRatio(979040510045, 290395044174); - debug.assert((try a.cmp(r)) == 0); + testing.expect((try a.cmp(r)) == 0); } test "big.rational mul" { @@ -864,7 +876,7 @@ test "big.rational mul" { try a.mul(a, b); try r.setRatio(571481443, 17082061422); - debug.assert((try a.cmp(r)) == 0); + testing.expect((try a.cmp(r)) == 0); } test "big.rational div" { @@ -877,7 +889,7 @@ test "big.rational div" { try a.div(a, b); try r.setRatio(75531824394, 221015929); - debug.assert((try a.cmp(r)) == 0); + testing.expect((try a.cmp(r)) == 0); } test "big.rational div" { @@ -888,11 +900,11 @@ test "big.rational div" { a.invert(); try r.setRatio(23341, 78923); - debug.assert((try a.cmp(r)) == 0); + testing.expect((try a.cmp(r)) == 0); try a.setRatio(-78923, 23341); a.invert(); try r.setRatio(-23341, 78923); - debug.assert((try a.cmp(r)) == 0); + testing.expect((try a.cmp(r)) == 0); }