mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
* Add missing period in Stack's description This looks fine in the source, but looks bad when seen on the documentation website. * Correct documentation for attachSegfaultHandler() The description for attachSegfaultHandler() looks pretty bad without indicating that the stuff at the end is code * Added missing 'the's in Queue.put's documentation * Fixed several errors in Stack's documentation `push()` and `pop()` were not styled as code There was no period after `pop()`, which looks bad on the documentation. * Fix multiple problems in base64.zig Both "invalid"s in Base64.decoder were not capitalized. Missing period in documentation of Base64DecoderWithIgnore.calcSizeUpperBound. * Fix capitalization typos in bit_set.zig In DynamicBitSetUnmanaged.deinit's and DynamicBitSet.deinit's documentation, "deinitializes" was uncapitalized. * Fix typos in fifo.zig's documentation Added a previously missing period to the end of the first line of LinearFifo.writableSlice's documentation. Added missing periods to both lines of LinearFifo.pump's documentation. * Fix typos in fmt.bufPrint's documentation The starts of both lines were not capitalized. * Fix minor documentation problems in fs/file.zig Missing periods in documentation for Permissions.setReadOnly, PermissionsWindows.setReadOnly, MetadataUnix.created, MetadataLinux.created, and MetadataWindows.created. * Fix a glaring typo in enums.zig * Correct errors in fs.zig * Fixed documentation problems in hash_map.zig The added empty line in verify_context's documentation is needed, otherwise autodoc for some reason assumes that the list hasn't been terminated and continues reading off the rest of the documentation as if it were part of the second list item. * Added lines between consecutive URLs in http.zig Makes the documentation conform closer to what was intended. * Fix wrongfully ended sentence in Uri.zig * Handle wrongly entered comma in valgrind.zig. * Add missing periods in wasm.zig's documentation * Fix odd spacing in event/loop.zig * Add missing period in http/Headers.zig * Added missing period in io/limited_reader.zig This isn't in the documentation due to what I guess is a limitation of autodoc, but it's clearly supposed to be. If it was, it would look pretty bad. * Correct documentation in math/big/int.zig * Correct formatting in math/big/rational.zig * Create an actual link to ZIGNOR's paper. * Fixed grammatical issues in sort/block.zig This will not show up in the documentation currently. * Fix typo in hash_map.zig
819 lines
24 KiB
Zig
819 lines
24 KiB
Zig
const std = @import("../../std.zig");
|
|
const debug = std.debug;
|
|
const math = std.math;
|
|
const mem = std.mem;
|
|
const testing = std.testing;
|
|
const Allocator = mem.Allocator;
|
|
|
|
const Limb = std.math.big.Limb;
|
|
const DoubleLimb = std.math.big.DoubleLimb;
|
|
const Int = std.math.big.int.Managed;
|
|
const IntConst = std.math.big.int.Const;
|
|
|
|
/// An arbitrary-precision rational number.
|
|
///
|
|
/// Memory is allocated as needed for operations to ensure full precision is kept. The precision
|
|
/// of a Rational is only bounded by memory.
|
|
///
|
|
/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,
|
|
/// gcd(p, q) = 1 always.
|
|
///
|
|
/// TODO rework this to store its own allocator and use a non-managed big int, to avoid double
|
|
/// allocator storage.
|
|
pub const Rational = struct {
|
|
/// Numerator. Determines the sign of the Rational.
|
|
p: Int,
|
|
|
|
/// Denominator. Sign is ignored.
|
|
q: Int,
|
|
|
|
/// Create a new Rational. A small amount of memory will be allocated on initialization.
|
|
/// This will be 2 * Int.default_capacity.
|
|
pub fn init(a: Allocator) !Rational {
|
|
var p = try Int.init(a);
|
|
errdefer p.deinit();
|
|
return Rational{
|
|
.p = p,
|
|
.q = try Int.initSet(a, 1),
|
|
};
|
|
}
|
|
|
|
/// Frees all memory associated with a Rational.
|
|
pub fn deinit(self: *Rational) void {
|
|
self.p.deinit();
|
|
self.q.deinit();
|
|
}
|
|
|
|
/// Set a Rational from a primitive integer type.
|
|
pub fn setInt(self: *Rational, a: anytype) !void {
|
|
try self.p.set(a);
|
|
try self.q.set(1);
|
|
}
|
|
|
|
/// Set a Rational from a string of the form `A/B` where A and B are base-10 integers.
|
|
pub fn setFloatString(self: *Rational, str: []const u8) !void {
|
|
// TODO: Accept a/b fractions and exponent form
|
|
if (str.len == 0) {
|
|
return error.InvalidFloatString;
|
|
}
|
|
|
|
const State = enum {
|
|
Integer,
|
|
Fractional,
|
|
};
|
|
|
|
var state = State.Integer;
|
|
var point: ?usize = null;
|
|
|
|
var start: usize = 0;
|
|
if (str[0] == '-') {
|
|
start += 1;
|
|
}
|
|
|
|
for (str, 0..) |c, i| {
|
|
switch (state) {
|
|
State.Integer => {
|
|
switch (c) {
|
|
'.' => {
|
|
state = State.Fractional;
|
|
point = i;
|
|
},
|
|
'0'...'9' => {
|
|
// okay
|
|
},
|
|
else => {
|
|
return error.InvalidFloatString;
|
|
},
|
|
}
|
|
},
|
|
State.Fractional => {
|
|
switch (c) {
|
|
'0'...'9' => {
|
|
// okay
|
|
},
|
|
else => {
|
|
return error.InvalidFloatString;
|
|
},
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
// TODO: batch the multiplies by 10
|
|
if (point) |i| {
|
|
try self.p.setString(10, str[0..i]);
|
|
|
|
const base = IntConst{ .limbs = &[_]Limb{10}, .positive = true };
|
|
var local_buf: [@sizeOf(Limb) * Int.default_capacity]u8 align(@alignOf(Limb)) = undefined;
|
|
var fba = std.heap.FixedBufferAllocator.init(&local_buf);
|
|
const base_managed = try base.toManaged(fba.allocator());
|
|
|
|
var j: usize = start;
|
|
while (j < str.len - i - 1) : (j += 1) {
|
|
try self.p.ensureMulCapacity(self.p.toConst(), base);
|
|
try self.p.mul(&self.p, &base_managed);
|
|
}
|
|
|
|
try self.q.setString(10, str[i + 1 ..]);
|
|
try self.p.add(&self.p, &self.q);
|
|
|
|
try self.q.set(1);
|
|
var k: usize = i + 1;
|
|
while (k < str.len) : (k += 1) {
|
|
try self.q.mul(&self.q, &base_managed);
|
|
}
|
|
|
|
try self.reduce();
|
|
} else {
|
|
try self.p.setString(10, str[0..]);
|
|
try self.q.set(1);
|
|
}
|
|
}
|
|
|
|
/// Set a Rational from a floating-point value. The rational will have enough precision to
|
|
/// completely represent the provided float.
|
|
pub fn setFloat(self: *Rational, comptime T: type, f: T) !void {
|
|
// Translated from golang.go/src/math/big/rat.go.
|
|
debug.assert(@typeInfo(T) == .Float);
|
|
|
|
const UnsignedInt = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
|
|
const f_bits = @as(UnsignedInt, @bitCast(f));
|
|
|
|
const exponent_bits = math.floatExponentBits(T);
|
|
const exponent_bias = (1 << (exponent_bits - 1)) - 1;
|
|
const mantissa_bits = math.floatMantissaBits(T);
|
|
|
|
const exponent_mask = (1 << exponent_bits) - 1;
|
|
const mantissa_mask = (1 << mantissa_bits) - 1;
|
|
|
|
var exponent = @as(i16, @intCast((f_bits >> mantissa_bits) & exponent_mask));
|
|
var mantissa = f_bits & mantissa_mask;
|
|
|
|
switch (exponent) {
|
|
exponent_mask => {
|
|
return error.NonFiniteFloat;
|
|
},
|
|
0 => {
|
|
// denormal
|
|
exponent -= exponent_bias - 1;
|
|
},
|
|
else => {
|
|
// normal
|
|
mantissa |= 1 << mantissa_bits;
|
|
exponent -= exponent_bias;
|
|
},
|
|
}
|
|
|
|
var shift: i16 = mantissa_bits - exponent;
|
|
|
|
// factor out powers of two early from rational
|
|
while (mantissa & 1 == 0 and shift > 0) {
|
|
mantissa >>= 1;
|
|
shift -= 1;
|
|
}
|
|
|
|
try self.p.set(mantissa);
|
|
self.p.setSign(f >= 0);
|
|
|
|
try self.q.set(1);
|
|
if (shift >= 0) {
|
|
try self.q.shiftLeft(&self.q, @as(usize, @intCast(shift)));
|
|
} else {
|
|
try self.p.shiftLeft(&self.p, @as(usize, @intCast(-shift)));
|
|
}
|
|
|
|
try self.reduce();
|
|
}
|
|
|
|
/// Return a floating-point value that is the closest value to a Rational.
|
|
///
|
|
/// The result may not be exact if the Rational is too precise or too large for the
|
|
/// target type.
|
|
pub fn toFloat(self: Rational, comptime T: type) !T {
|
|
// Translated from golang.go/src/math/big/rat.go.
|
|
// TODO: Indicate whether the result is not exact.
|
|
debug.assert(@typeInfo(T) == .Float);
|
|
|
|
const fsize = @typeInfo(T).Float.bits;
|
|
const BitReprType = std.meta.Int(.unsigned, fsize);
|
|
|
|
const msize = math.floatMantissaBits(T);
|
|
const msize1 = msize + 1;
|
|
const msize2 = msize1 + 1;
|
|
|
|
const esize = math.floatExponentBits(T);
|
|
const ebias = (1 << (esize - 1)) - 1;
|
|
const emin = 1 - ebias;
|
|
|
|
if (self.p.eqlZero()) {
|
|
return 0;
|
|
}
|
|
|
|
// 1. left-shift a or sub so that a/b is in [1 << msize1, 1 << (msize2 + 1)]
|
|
var exp = @as(isize, @intCast(self.p.bitCountTwosComp())) - @as(isize, @intCast(self.q.bitCountTwosComp()));
|
|
|
|
var a2 = try self.p.clone();
|
|
defer a2.deinit();
|
|
|
|
var b2 = try self.q.clone();
|
|
defer b2.deinit();
|
|
|
|
const shift = msize2 - exp;
|
|
if (shift >= 0) {
|
|
try a2.shiftLeft(&a2, @as(usize, @intCast(shift)));
|
|
} else {
|
|
try b2.shiftLeft(&b2, @as(usize, @intCast(-shift)));
|
|
}
|
|
|
|
// 2. compute quotient and remainder
|
|
var q = try Int.init(self.p.allocator);
|
|
defer q.deinit();
|
|
|
|
// unused
|
|
var r = try Int.init(self.p.allocator);
|
|
defer r.deinit();
|
|
|
|
try Int.divTrunc(&q, &r, &a2, &b2);
|
|
|
|
var mantissa = extractLowBits(q, BitReprType);
|
|
var have_rem = r.len() > 0;
|
|
|
|
// 3. q didn't fit in msize2 bits, redo division b2 << 1
|
|
if (mantissa >> msize2 == 1) {
|
|
if (mantissa & 1 == 1) {
|
|
have_rem = true;
|
|
}
|
|
mantissa >>= 1;
|
|
exp += 1;
|
|
}
|
|
if (mantissa >> msize1 != 1) {
|
|
// NOTE: This can be hit if the limb size is small (u8/16).
|
|
@panic("unexpected bits in result");
|
|
}
|
|
|
|
// 4. Rounding
|
|
if (emin - msize <= exp and exp <= emin) {
|
|
// denormal
|
|
const shift1 = @as(math.Log2Int(BitReprType), @intCast(emin - (exp - 1)));
|
|
const lost_bits = mantissa & ((@as(BitReprType, @intCast(1)) << shift1) - 1);
|
|
have_rem = have_rem or lost_bits != 0;
|
|
mantissa >>= shift1;
|
|
exp = 2 - ebias;
|
|
}
|
|
|
|
// round q using round-half-to-even
|
|
var exact = !have_rem;
|
|
if (mantissa & 1 != 0) {
|
|
exact = false;
|
|
if (have_rem or (mantissa & 2 != 0)) {
|
|
mantissa += 1;
|
|
if (mantissa >= 1 << msize2) {
|
|
// 11...1 => 100...0
|
|
mantissa >>= 1;
|
|
exp += 1;
|
|
}
|
|
}
|
|
}
|
|
mantissa >>= 1;
|
|
|
|
const f = math.scalbn(@as(T, @floatFromInt(mantissa)), @as(i32, @intCast(exp - msize1)));
|
|
if (math.isInf(f)) {
|
|
exact = false;
|
|
}
|
|
|
|
return if (self.p.isPositive()) f else -f;
|
|
}
|
|
|
|
/// Set a rational from an integer ratio.
|
|
pub fn setRatio(self: *Rational, p: anytype, q: anytype) !void {
|
|
try self.p.set(p);
|
|
try self.q.set(q);
|
|
|
|
self.p.setSign(@intFromBool(self.p.isPositive()) ^ @intFromBool(self.q.isPositive()) == 0);
|
|
self.q.setSign(true);
|
|
|
|
try self.reduce();
|
|
|
|
if (self.q.eqlZero()) {
|
|
@panic("cannot set rational with denominator = 0");
|
|
}
|
|
}
|
|
|
|
/// Set a Rational directly from an Int.
|
|
pub fn copyInt(self: *Rational, a: Int) !void {
|
|
try self.p.copy(a.toConst());
|
|
try self.q.set(1);
|
|
}
|
|
|
|
/// Set a Rational directly from a ratio of two Int's.
|
|
pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
|
|
try self.p.copy(a.toConst());
|
|
try self.q.copy(b.toConst());
|
|
|
|
self.p.setSign(@intFromBool(self.p.isPositive()) ^ @intFromBool(self.q.isPositive()) == 0);
|
|
self.q.setSign(true);
|
|
|
|
try self.reduce();
|
|
}
|
|
|
|
/// Make a Rational positive.
|
|
pub fn abs(r: *Rational) void {
|
|
r.p.abs();
|
|
}
|
|
|
|
/// Negate the sign of a Rational.
|
|
pub fn negate(r: *Rational) void {
|
|
r.p.negate();
|
|
}
|
|
|
|
/// Efficiently swap a Rational with another. This swaps the limb pointers and a full copy is not
|
|
/// performed. The address of the limbs field will not be the same after this function.
|
|
pub fn swap(r: *Rational, other: *Rational) void {
|
|
r.p.swap(&other.p);
|
|
r.q.swap(&other.q);
|
|
}
|
|
|
|
/// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or
|
|
/// a > b respectively.
|
|
pub fn order(a: Rational, b: Rational) !math.Order {
|
|
return cmpInternal(a, b, false);
|
|
}
|
|
|
|
/// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
|
|
/// |b| or |a| > |b| respectively.
|
|
pub fn orderAbs(a: Rational, b: Rational) !math.Order {
|
|
return cmpInternal(a, b, true);
|
|
}
|
|
|
|
// p/q > x/y iff p*y > x*q
|
|
fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order {
|
|
// TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid
|
|
// the memory allocations here?
|
|
var q = try Int.init(a.p.allocator);
|
|
defer q.deinit();
|
|
|
|
var p = try Int.init(b.p.allocator);
|
|
defer p.deinit();
|
|
|
|
try q.mul(&a.p, &b.q);
|
|
try p.mul(&b.p, &a.q);
|
|
|
|
return if (is_abs) q.orderAbs(p) else q.order(p);
|
|
}
|
|
|
|
/// rma = a + b.
|
|
///
|
|
/// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
|
|
///
|
|
/// Returns an error if memory could not be allocated.
|
|
pub fn add(rma: *Rational, a: Rational, b: Rational) !void {
|
|
var r = rma;
|
|
var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
|
|
|
|
var sr: Rational = undefined;
|
|
if (aliased) {
|
|
sr = try Rational.init(rma.p.allocator);
|
|
r = &sr;
|
|
aliased = true;
|
|
}
|
|
defer if (aliased) {
|
|
rma.swap(r);
|
|
r.deinit();
|
|
};
|
|
|
|
try r.p.mul(&a.p, &b.q);
|
|
try r.q.mul(&b.p, &a.q);
|
|
try r.p.add(&r.p, &r.q);
|
|
|
|
try r.q.mul(&a.q, &b.q);
|
|
try r.reduce();
|
|
}
|
|
|
|
/// rma = a - b.
|
|
///
|
|
/// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
|
|
///
|
|
/// Returns an error if memory could not be allocated.
|
|
pub fn sub(rma: *Rational, a: Rational, b: Rational) !void {
|
|
var r = rma;
|
|
var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
|
|
|
|
var sr: Rational = undefined;
|
|
if (aliased) {
|
|
sr = try Rational.init(rma.p.allocator);
|
|
r = &sr;
|
|
aliased = true;
|
|
}
|
|
defer if (aliased) {
|
|
rma.swap(r);
|
|
r.deinit();
|
|
};
|
|
|
|
try r.p.mul(&a.p, &b.q);
|
|
try r.q.mul(&b.p, &a.q);
|
|
try r.p.sub(&r.p, &r.q);
|
|
|
|
try r.q.mul(&a.q, &b.q);
|
|
try r.reduce();
|
|
}
|
|
|
|
/// rma = a * b.
|
|
///
|
|
/// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
|
|
///
|
|
/// Returns an error if memory could not be allocated.
|
|
pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
|
|
try r.p.mul(&a.p, &b.p);
|
|
try r.q.mul(&a.q, &b.q);
|
|
try r.reduce();
|
|
}
|
|
|
|
/// rma = a / b.
|
|
///
|
|
/// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
|
|
///
|
|
/// Returns an error if memory could not be allocated.
|
|
pub fn div(r: *Rational, a: Rational, b: Rational) !void {
|
|
if (b.p.eqlZero()) {
|
|
@panic("division by zero");
|
|
}
|
|
|
|
try r.p.mul(&a.p, &b.q);
|
|
try r.q.mul(&b.p, &a.q);
|
|
try r.reduce();
|
|
}
|
|
|
|
/// Invert the numerator and denominator fields of a Rational. p/q => q/p.
|
|
pub fn invert(r: *Rational) void {
|
|
Int.swap(&r.p, &r.q);
|
|
}
|
|
|
|
// reduce r/q such that gcd(r, q) = 1
|
|
fn reduce(r: *Rational) !void {
|
|
var a = try Int.init(r.p.allocator);
|
|
defer a.deinit();
|
|
|
|
const sign = r.p.isPositive();
|
|
r.p.abs();
|
|
try a.gcd(&r.p, &r.q);
|
|
r.p.setSign(sign);
|
|
|
|
const one = IntConst{ .limbs = &[_]Limb{1}, .positive = true };
|
|
if (a.toConst().order(one) != .eq) {
|
|
var unused = try Int.init(r.p.allocator);
|
|
defer unused.deinit();
|
|
|
|
// TODO: divexact would be useful here
|
|
// TODO: don't copy r.q for div
|
|
try Int.divTrunc(&r.p, &unused, &r.p, &a);
|
|
try Int.divTrunc(&r.q, &unused, &r.q, &a);
|
|
}
|
|
}
|
|
};
|
|
|
|
fn extractLowBits(a: Int, comptime T: type) T {
|
|
debug.assert(@typeInfo(T) == .Int);
|
|
|
|
const t_bits = @typeInfo(T).Int.bits;
|
|
const limb_bits = @typeInfo(Limb).Int.bits;
|
|
if (t_bits <= limb_bits) {
|
|
return @as(T, @truncate(a.limbs[0]));
|
|
} else {
|
|
var r: T = 0;
|
|
comptime var i: usize = 0;
|
|
|
|
// Remainder is always 0 since if t_bits >= limb_bits -> Limb | T and both
|
|
// are powers of two.
|
|
inline while (i < t_bits / limb_bits) : (i += 1) {
|
|
r |= math.shl(T, a.limbs[i], i * limb_bits);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
}
|
|
|
|
test "big.rational extractLowBits" {
|
|
var a = try Int.initSet(testing.allocator, 0x11112222333344441234567887654321);
|
|
defer a.deinit();
|
|
|
|
const a1 = extractLowBits(a, u8);
|
|
try testing.expect(a1 == 0x21);
|
|
|
|
const a2 = extractLowBits(a, u16);
|
|
try testing.expect(a2 == 0x4321);
|
|
|
|
const a3 = extractLowBits(a, u32);
|
|
try testing.expect(a3 == 0x87654321);
|
|
|
|
const a4 = extractLowBits(a, u64);
|
|
try testing.expect(a4 == 0x1234567887654321);
|
|
|
|
const a5 = extractLowBits(a, u128);
|
|
try testing.expect(a5 == 0x11112222333344441234567887654321);
|
|
}
|
|
|
|
test "big.rational set" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
|
|
try a.setInt(5);
|
|
try testing.expect((try a.p.to(u32)) == 5);
|
|
try testing.expect((try a.q.to(u32)) == 1);
|
|
|
|
try a.setRatio(7, 3);
|
|
try testing.expect((try a.p.to(u32)) == 7);
|
|
try testing.expect((try a.q.to(u32)) == 3);
|
|
|
|
try a.setRatio(9, 3);
|
|
try testing.expect((try a.p.to(i32)) == 3);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
|
|
try a.setRatio(-9, 3);
|
|
try testing.expect((try a.p.to(i32)) == -3);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
|
|
try a.setRatio(9, -3);
|
|
try testing.expect((try a.p.to(i32)) == -3);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
|
|
try a.setRatio(-9, -3);
|
|
try testing.expect((try a.p.to(i32)) == 3);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
}
|
|
|
|
test "big.rational setFloat" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
|
|
try a.setFloat(f64, 2.5);
|
|
try testing.expect((try a.p.to(i32)) == 5);
|
|
try testing.expect((try a.q.to(i32)) == 2);
|
|
|
|
try a.setFloat(f32, -2.5);
|
|
try testing.expect((try a.p.to(i32)) == -5);
|
|
try testing.expect((try a.q.to(i32)) == 2);
|
|
|
|
try a.setFloat(f32, 3.141593);
|
|
|
|
// = 3.14159297943115234375
|
|
try testing.expect((try a.p.to(u32)) == 3294199);
|
|
try testing.expect((try a.q.to(u32)) == 1048576);
|
|
|
|
try a.setFloat(f64, 72.141593120712409172417410926841290461290467124);
|
|
|
|
// = 72.1415931207124145885245525278151035308837890625
|
|
try testing.expect((try a.p.to(u128)) == 5076513310880537);
|
|
try testing.expect((try a.q.to(u128)) == 70368744177664);
|
|
}
|
|
|
|
test "big.rational setFloatString" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
|
|
try a.setFloatString("72.14159312071241458852455252781510353");
|
|
|
|
// = 72.1415931207124145885245525278151035308837890625
|
|
try testing.expect((try a.p.to(u128)) == 7214159312071241458852455252781510353);
|
|
try testing.expect((try a.q.to(u128)) == 100000000000000000000000000000000000);
|
|
}
|
|
|
|
test "big.rational toFloat" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
|
|
// = 3.14159297943115234375
|
|
try a.setRatio(3294199, 1048576);
|
|
try testing.expect((try a.toFloat(f64)) == 3.14159297943115234375);
|
|
|
|
// = 72.1415931207124145885245525278151035308837890625
|
|
try a.setRatio(5076513310880537, 70368744177664);
|
|
try testing.expect((try a.toFloat(f64)) == 72.141593120712409172417410926841290461290467124);
|
|
}
|
|
|
|
test "big.rational set/to Float round-trip" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
var prng = std.rand.DefaultPrng.init(0x5EED);
|
|
const random = prng.random();
|
|
var i: usize = 0;
|
|
while (i < 512) : (i += 1) {
|
|
const r = random.float(f64);
|
|
try a.setFloat(f64, r);
|
|
try testing.expect((try a.toFloat(f64)) == r);
|
|
}
|
|
}
|
|
|
|
test "big.rational copy" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
|
|
var b = try Int.initSet(testing.allocator, 5);
|
|
defer b.deinit();
|
|
|
|
try a.copyInt(b);
|
|
try testing.expect((try a.p.to(u32)) == 5);
|
|
try testing.expect((try a.q.to(u32)) == 1);
|
|
|
|
var c = try Int.initSet(testing.allocator, 7);
|
|
defer c.deinit();
|
|
var d = try Int.initSet(testing.allocator, 3);
|
|
defer d.deinit();
|
|
|
|
try a.copyRatio(c, d);
|
|
try testing.expect((try a.p.to(u32)) == 7);
|
|
try testing.expect((try a.q.to(u32)) == 3);
|
|
|
|
var e = try Int.initSet(testing.allocator, 9);
|
|
defer e.deinit();
|
|
var f = try Int.initSet(testing.allocator, 3);
|
|
defer f.deinit();
|
|
|
|
try a.copyRatio(e, f);
|
|
try testing.expect((try a.p.to(u32)) == 3);
|
|
try testing.expect((try a.q.to(u32)) == 1);
|
|
}
|
|
|
|
test "big.rational negate" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
|
|
try a.setInt(-50);
|
|
try testing.expect((try a.p.to(i32)) == -50);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
|
|
a.negate();
|
|
try testing.expect((try a.p.to(i32)) == 50);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
|
|
a.negate();
|
|
try testing.expect((try a.p.to(i32)) == -50);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
}
|
|
|
|
test "big.rational abs" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
|
|
try a.setInt(-50);
|
|
try testing.expect((try a.p.to(i32)) == -50);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
|
|
a.abs();
|
|
try testing.expect((try a.p.to(i32)) == 50);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
|
|
a.abs();
|
|
try testing.expect((try a.p.to(i32)) == 50);
|
|
try testing.expect((try a.q.to(i32)) == 1);
|
|
}
|
|
|
|
test "big.rational swap" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
var b = try Rational.init(testing.allocator);
|
|
defer b.deinit();
|
|
|
|
try a.setRatio(50, 23);
|
|
try b.setRatio(17, 3);
|
|
|
|
try testing.expect((try a.p.to(u32)) == 50);
|
|
try testing.expect((try a.q.to(u32)) == 23);
|
|
|
|
try testing.expect((try b.p.to(u32)) == 17);
|
|
try testing.expect((try b.q.to(u32)) == 3);
|
|
|
|
a.swap(&b);
|
|
|
|
try testing.expect((try a.p.to(u32)) == 17);
|
|
try testing.expect((try a.q.to(u32)) == 3);
|
|
|
|
try testing.expect((try b.p.to(u32)) == 50);
|
|
try testing.expect((try b.q.to(u32)) == 23);
|
|
}
|
|
|
|
test "big.rational order" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
var b = try Rational.init(testing.allocator);
|
|
defer b.deinit();
|
|
|
|
try a.setRatio(500, 231);
|
|
try b.setRatio(18903, 8584);
|
|
try testing.expect((try a.order(b)) == .lt);
|
|
|
|
try a.setRatio(890, 10);
|
|
try b.setRatio(89, 1);
|
|
try testing.expect((try a.order(b)) == .eq);
|
|
}
|
|
|
|
test "big.rational order/orderAbs with negative" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
var b = try Rational.init(testing.allocator);
|
|
defer b.deinit();
|
|
|
|
try a.setRatio(1, 1);
|
|
try b.setRatio(-2, 1);
|
|
try testing.expect((try a.order(b)) == .gt);
|
|
try testing.expect((try a.orderAbs(b)) == .lt);
|
|
}
|
|
|
|
test "big.rational add single-limb" {
|
|
var a = try Rational.init(testing.allocator);
|
|
defer a.deinit();
|
|
var b = try Rational.init(testing.allocator);
|
|
defer b.deinit();
|
|
|
|
try a.setRatio(500, 231);
|
|
try b.setRatio(18903, 8584);
|
|
try testing.expect((try a.order(b)) == .lt);
|
|
|
|
try a.setRatio(890, 10);
|
|
try b.setRatio(89, 1);
|
|
try testing.expect((try a.order(b)) == .eq);
|
|
}
|
|
|
|
test "big.rational add" {
|
|
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.add(a, b);
|
|
|
|
try r.setRatio(984786924199, 290395044174);
|
|
try testing.expect((try a.order(r)) == .eq);
|
|
}
|
|
|
|
test "big.rational sub" {
|
|
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.sub(a, b);
|
|
|
|
try r.setRatio(979040510045, 290395044174);
|
|
try testing.expect((try a.order(r)) == .eq);
|
|
}
|
|
|
|
test "big.rational mul" {
|
|
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.mul(a, b);
|
|
|
|
try r.setRatio(571481443, 17082061422);
|
|
try testing.expect((try a.order(r)) == .eq);
|
|
}
|
|
|
|
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();
|
|
|
|
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);
|
|
}
|
|
|
|
{
|
|
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 r.setRatio(23341, 78923);
|
|
try testing.expect((try a.order(r)) == .eq);
|
|
|
|
try a.setRatio(-78923, 23341);
|
|
a.invert();
|
|
|
|
try r.setRatio(-23341, 78923);
|
|
try testing.expect((try a.order(r)) == .eq);
|
|
}
|
|
}
|