rework std.math.big.Int
Now there are 3 types: * std.math.big.int.Const - the memory is immutable, only stores limbs and is_positive - all methods operating on constant data go here * std.math.big.int.Mutable - the memory is mutable, stores capacity in addition to limbs and is_positive - methods here have some Mutable parameters and some Const parameters. These methods expect callers to pre-calculate the amount of resources required, and asserts that the resources are available. * std.math.big.int.Managed - the memory is mutable and additionally stores an allocator. - methods here perform the resource calculations for the programmer. - this is the high level abstraction from before Each of these 3 types can be converted to the other ones. You can see the use case for this in the self-hosted compiler, where we only store limbs, and construct the big ints as needed. This gets rid of the hack where the allocator was optional and the notion of "fixed" versions of the struct. Such things are now modeled with the `big.int.Const` type.
This commit is contained in:
parent
1d202008d8
commit
8766821157
@ -1058,7 +1058,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
|
||||
return value;
|
||||
}
|
||||
|
||||
fn digitToChar(digit: u8, uppercase: bool) u8 {
|
||||
pub fn digitToChar(digit: u8, uppercase: bool) u8 {
|
||||
return switch (digit) {
|
||||
0...9 => digit + '0',
|
||||
10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10),
|
||||
|
@ -1,7 +1,24 @@
|
||||
pub usingnamespace @import("big/int.zig");
|
||||
pub usingnamespace @import("big/rational.zig");
|
||||
const std = @import("../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
test "math.big" {
|
||||
_ = @import("big/int.zig");
|
||||
_ = @import("big/rational.zig");
|
||||
pub const Rational = @import("big/rational.zig").Rational;
|
||||
pub const int = @import("big/int.zig");
|
||||
pub const Limb = usize;
|
||||
pub const DoubleLimb = std.meta.IntType(false, 2 * Limb.bit_count);
|
||||
pub const SignedDoubleLimb = std.meta.IntType(true, DoubleLimb.bit_count);
|
||||
pub const Log2Limb = std.math.Log2Int(Limb);
|
||||
|
||||
comptime {
|
||||
assert(std.math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);
|
||||
assert(Limb.bit_count <= 64); // u128 set is unsupported
|
||||
assert(Limb.is_signed == false);
|
||||
}
|
||||
|
||||
test "" {
|
||||
_ = int;
|
||||
_ = Rational;
|
||||
_ = Limb;
|
||||
_ = DoubleLimb;
|
||||
_ = SignedDoubleLimb;
|
||||
_ = Log2Limb;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
1455
lib/std/math/big/int_test.zig
Normal file
1455
lib/std/math/big/int_test.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,10 +5,10 @@ const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
const bn = @import("int.zig");
|
||||
const Limb = bn.Limb;
|
||||
const DoubleLimb = bn.DoubleLimb;
|
||||
const Int = bn.Int;
|
||||
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.
|
||||
///
|
||||
@ -17,6 +17,9 @@ const Int = bn.Int;
|
||||
///
|
||||
/// 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,
|
||||
@ -98,20 +101,20 @@ pub const Rational = struct {
|
||||
if (point) |i| {
|
||||
try self.p.setString(10, str[0..i]);
|
||||
|
||||
const base = Int.initFixed(([_]Limb{10})[0..]);
|
||||
const base = IntConst{ .limbs = &[_]Limb{10}, .positive = true };
|
||||
|
||||
var j: usize = start;
|
||||
while (j < str.len - i - 1) : (j += 1) {
|
||||
try self.p.mul(self.p, base);
|
||||
try self.p.mul(self.p.toConst(), base);
|
||||
}
|
||||
|
||||
try self.q.setString(10, str[i + 1 ..]);
|
||||
try self.p.add(self.p, self.q);
|
||||
try self.p.add(self.p.toConst(), self.q.toConst());
|
||||
|
||||
try self.q.set(1);
|
||||
var k: usize = i + 1;
|
||||
while (k < str.len) : (k += 1) {
|
||||
try self.q.mul(self.q, base);
|
||||
try self.q.mul(self.q.toConst(), base);
|
||||
}
|
||||
|
||||
try self.reduce();
|
||||
@ -218,14 +221,14 @@ pub const Rational = struct {
|
||||
}
|
||||
|
||||
// 2. compute quotient and remainder
|
||||
var q = try Int.init(self.p.allocator.?);
|
||||
var q = try Int.init(self.p.allocator);
|
||||
defer q.deinit();
|
||||
|
||||
// unused
|
||||
var r = try Int.init(self.p.allocator.?);
|
||||
var r = try Int.init(self.p.allocator);
|
||||
defer r.deinit();
|
||||
|
||||
try Int.divTrunc(&q, &r, a2, b2);
|
||||
try Int.divTrunc(&q, &r, a2.toConst(), b2.toConst());
|
||||
|
||||
var mantissa = extractLowBits(q, BitReprType);
|
||||
var have_rem = r.len() > 0;
|
||||
@ -293,14 +296,14 @@ pub const Rational = struct {
|
||||
|
||||
/// Set a Rational directly from an Int.
|
||||
pub fn copyInt(self: *Rational, a: Int) !void {
|
||||
try self.p.copy(a);
|
||||
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);
|
||||
try self.q.copy(b);
|
||||
try self.p.copy(a.toConst());
|
||||
try self.q.copy(b.toConst());
|
||||
|
||||
self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
|
||||
self.q.setSign(true);
|
||||
@ -327,13 +330,13 @@ pub const Rational = struct {
|
||||
|
||||
/// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
|
||||
/// > b respectively.
|
||||
pub fn cmp(a: Rational, b: Rational) !math.Order {
|
||||
pub fn order(a: Rational, b: Rational) !math.Order {
|
||||
return cmpInternal(a, b, true);
|
||||
}
|
||||
|
||||
/// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
|
||||
/// |b| or |a| > |b| respectively.
|
||||
pub fn cmpAbs(a: Rational, b: Rational) !math.Order {
|
||||
pub fn orderAbs(a: Rational, b: Rational) !math.Order {
|
||||
return cmpInternal(a, b, false);
|
||||
}
|
||||
|
||||
@ -341,16 +344,16 @@ pub const Rational = struct {
|
||||
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.?);
|
||||
var q = try Int.init(a.p.allocator);
|
||||
defer q.deinit();
|
||||
|
||||
var p = try Int.init(b.p.allocator.?);
|
||||
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);
|
||||
try q.mul(a.p.toConst(), b.q.toConst());
|
||||
try p.mul(b.p.toConst(), a.q.toConst());
|
||||
|
||||
return if (is_abs) q.cmpAbs(p) else q.cmp(p);
|
||||
return if (is_abs) q.orderAbs(p) else q.order(p);
|
||||
}
|
||||
|
||||
/// rma = a + b.
|
||||
@ -364,7 +367,7 @@ pub const Rational = struct {
|
||||
|
||||
var sr: Rational = undefined;
|
||||
if (aliased) {
|
||||
sr = try Rational.init(rma.p.allocator.?);
|
||||
sr = try Rational.init(rma.p.allocator);
|
||||
r = &sr;
|
||||
aliased = true;
|
||||
}
|
||||
@ -373,11 +376,11 @@ pub const Rational = struct {
|
||||
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.p.mul(a.p.toConst(), b.q.toConst());
|
||||
try r.q.mul(b.p.toConst(), a.q.toConst());
|
||||
try r.p.add(r.p.toConst(), r.q.toConst());
|
||||
|
||||
try r.q.mul(a.q, b.q);
|
||||
try r.q.mul(a.q.toConst(), b.q.toConst());
|
||||
try r.reduce();
|
||||
}
|
||||
|
||||
@ -392,7 +395,7 @@ pub const Rational = struct {
|
||||
|
||||
var sr: Rational = undefined;
|
||||
if (aliased) {
|
||||
sr = try Rational.init(rma.p.allocator.?);
|
||||
sr = try Rational.init(rma.p.allocator);
|
||||
r = &sr;
|
||||
aliased = true;
|
||||
}
|
||||
@ -401,11 +404,11 @@ pub const Rational = struct {
|
||||
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.p.mul(a.p.toConst(), b.q.toConst());
|
||||
try r.q.mul(b.p.toConst(), a.q.toConst());
|
||||
try r.p.sub(r.p.toConst(), r.q.toConst());
|
||||
|
||||
try r.q.mul(a.q, b.q);
|
||||
try r.q.mul(a.q.toConst(), b.q.toConst());
|
||||
try r.reduce();
|
||||
}
|
||||
|
||||
@ -415,8 +418,8 @@ pub const Rational = struct {
|
||||
///
|
||||
/// 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.p.mul(a.p.toConst(), b.p.toConst());
|
||||
try r.q.mul(a.q.toConst(), b.q.toConst());
|
||||
try r.reduce();
|
||||
}
|
||||
|
||||
@ -430,8 +433,8 @@ pub const Rational = struct {
|
||||
@panic("division by zero");
|
||||
}
|
||||
|
||||
try r.p.mul(a.p, b.q);
|
||||
try r.q.mul(b.p, a.q);
|
||||
try r.p.mul(a.p.toConst(), b.q.toConst());
|
||||
try r.q.mul(b.p.toConst(), a.q.toConst());
|
||||
try r.reduce();
|
||||
}
|
||||
|
||||
@ -442,7 +445,7 @@ pub const Rational = struct {
|
||||
|
||||
// reduce r/q such that gcd(r, q) = 1
|
||||
fn reduce(r: *Rational) !void {
|
||||
var a = try Int.init(r.p.allocator.?);
|
||||
var a = try Int.init(r.p.allocator);
|
||||
defer a.deinit();
|
||||
|
||||
const sign = r.p.isPositive();
|
||||
@ -450,15 +453,15 @@ pub const Rational = struct {
|
||||
try a.gcd(r.p, r.q);
|
||||
r.p.setSign(sign);
|
||||
|
||||
const one = Int.initFixed(([_]Limb{1})[0..]);
|
||||
if (a.cmp(one) != .eq) {
|
||||
var unused = try Int.init(r.p.allocator.?);
|
||||
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);
|
||||
try Int.divTrunc(&r.p, &unused, r.p.toConst(), a.toConst());
|
||||
try Int.divTrunc(&r.q, &unused, r.q.toConst(), a.toConst());
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -596,25 +599,25 @@ test "big.rational copy" {
|
||||
var a = try Rational.init(testing.allocator);
|
||||
defer a.deinit();
|
||||
|
||||
const b = try Int.initSet(testing.allocator, 5);
|
||||
var b = try Int.initSet(testing.allocator, 5);
|
||||
defer b.deinit();
|
||||
|
||||
try a.copyInt(b);
|
||||
testing.expect((try a.p.to(u32)) == 5);
|
||||
testing.expect((try a.q.to(u32)) == 1);
|
||||
|
||||
const c = try Int.initSet(testing.allocator, 7);
|
||||
var c = try Int.initSet(testing.allocator, 7);
|
||||
defer c.deinit();
|
||||
const d = try Int.initSet(testing.allocator, 3);
|
||||
var d = try Int.initSet(testing.allocator, 3);
|
||||
defer d.deinit();
|
||||
|
||||
try a.copyRatio(c, d);
|
||||
testing.expect((try a.p.to(u32)) == 7);
|
||||
testing.expect((try a.q.to(u32)) == 3);
|
||||
|
||||
const e = try Int.initSet(testing.allocator, 9);
|
||||
var e = try Int.initSet(testing.allocator, 9);
|
||||
defer e.deinit();
|
||||
const f = try Int.initSet(testing.allocator, 3);
|
||||
var f = try Int.initSet(testing.allocator, 3);
|
||||
defer f.deinit();
|
||||
|
||||
try a.copyRatio(e, f);
|
||||
@ -680,7 +683,7 @@ test "big.rational swap" {
|
||||
testing.expect((try b.q.to(u32)) == 23);
|
||||
}
|
||||
|
||||
test "big.rational cmp" {
|
||||
test "big.rational order" {
|
||||
var a = try Rational.init(testing.allocator);
|
||||
defer a.deinit();
|
||||
var b = try Rational.init(testing.allocator);
|
||||
@ -688,11 +691,11 @@ test "big.rational cmp" {
|
||||
|
||||
try a.setRatio(500, 231);
|
||||
try b.setRatio(18903, 8584);
|
||||
testing.expect((try a.cmp(b)) == .lt);
|
||||
testing.expect((try a.order(b)) == .lt);
|
||||
|
||||
try a.setRatio(890, 10);
|
||||
try b.setRatio(89, 1);
|
||||
testing.expect((try a.cmp(b)) == .eq);
|
||||
testing.expect((try a.order(b)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational add single-limb" {
|
||||
@ -703,11 +706,11 @@ test "big.rational add single-limb" {
|
||||
|
||||
try a.setRatio(500, 231);
|
||||
try b.setRatio(18903, 8584);
|
||||
testing.expect((try a.cmp(b)) == .lt);
|
||||
testing.expect((try a.order(b)) == .lt);
|
||||
|
||||
try a.setRatio(890, 10);
|
||||
try b.setRatio(89, 1);
|
||||
testing.expect((try a.cmp(b)) == .eq);
|
||||
testing.expect((try a.order(b)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational add" {
|
||||
@ -723,7 +726,7 @@ test "big.rational add" {
|
||||
try a.add(a, b);
|
||||
|
||||
try r.setRatio(984786924199, 290395044174);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational sub" {
|
||||
@ -739,7 +742,7 @@ test "big.rational sub" {
|
||||
try a.sub(a, b);
|
||||
|
||||
try r.setRatio(979040510045, 290395044174);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational mul" {
|
||||
@ -755,7 +758,7 @@ test "big.rational mul" {
|
||||
try a.mul(a, b);
|
||||
|
||||
try r.setRatio(571481443, 17082061422);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational div" {
|
||||
@ -771,7 +774,7 @@ test "big.rational div" {
|
||||
try a.div(a, b);
|
||||
|
||||
try r.setRatio(75531824394, 221015929);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
||||
test "big.rational div" {
|
||||
@ -784,11 +787,11 @@ test "big.rational div" {
|
||||
a.invert();
|
||||
|
||||
try r.setRatio(23341, 78923);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
|
||||
try a.setRatio(-78923, 23341);
|
||||
a.invert();
|
||||
|
||||
try r.setRatio(-23341, 78923);
|
||||
testing.expect((try a.cmp(r)) == .eq);
|
||||
testing.expect((try a.order(r)) == .eq);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ pub const failing_allocator = &failing_allocator_instance.allocator;
|
||||
pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);
|
||||
|
||||
pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]);
|
||||
var allocator_mem: [1024 * 1024]u8 = undefined;
|
||||
var allocator_mem: [2 * 1024 * 1024]u8 = undefined;
|
||||
|
||||
/// This function is intended to be used only in tests. It prints diagnostics to stderr
|
||||
/// and then aborts when actual_error_union is not expected_error.
|
||||
|
@ -4,7 +4,8 @@ const Allocator = std.mem.Allocator;
|
||||
const Value = @import("value.zig").Value;
|
||||
const Type = @import("type.zig").Type;
|
||||
const assert = std.debug.assert;
|
||||
const BigInt = std.math.big.Int;
|
||||
const BigIntConst = std.math.big.int.Const;
|
||||
const BigIntMutable = std.math.big.int.Mutable;
|
||||
const Target = std.Target;
|
||||
|
||||
pub const text = @import("ir/text.zig");
|
||||
@ -483,29 +484,32 @@ const Analyze = struct {
|
||||
});
|
||||
}
|
||||
|
||||
fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigInt) !*Inst {
|
||||
if (big_int.isPositive()) {
|
||||
fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
|
||||
const val_payload = if (big_int.positive) blk: {
|
||||
if (big_int.to(u64)) |x| {
|
||||
return self.constIntUnsigned(src, ty, x);
|
||||
} else |err| switch (err) {
|
||||
error.NegativeIntoUnsigned => unreachable,
|
||||
error.TargetTooSmall => {}, // handled below
|
||||
}
|
||||
} else {
|
||||
const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive);
|
||||
big_int_payload.* = .{ .limbs = big_int.limbs };
|
||||
break :blk &big_int_payload.base;
|
||||
} else blk: {
|
||||
if (big_int.to(i64)) |x| {
|
||||
return self.constIntSigned(src, ty, x);
|
||||
} else |err| switch (err) {
|
||||
error.NegativeIntoUnsigned => unreachable,
|
||||
error.TargetTooSmall => {}, // handled below
|
||||
}
|
||||
}
|
||||
|
||||
const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBig);
|
||||
big_int_payload.* = .{ .big_int = big_int };
|
||||
const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative);
|
||||
big_int_payload.* = .{ .limbs = big_int.limbs };
|
||||
break :blk &big_int_payload.base;
|
||||
};
|
||||
|
||||
return self.constInst(src, .{
|
||||
.ty = ty,
|
||||
.val = Value.initPayload(&big_int_payload.base),
|
||||
.val = Value.initPayload(val_payload),
|
||||
});
|
||||
}
|
||||
|
||||
@ -745,19 +749,31 @@ const Analyze = struct {
|
||||
var rhs_space: Value.BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs_val.toBigInt(&lhs_space);
|
||||
const rhs_bigint = rhs_val.toBigInt(&rhs_space);
|
||||
var result_bigint = try BigInt.init(&self.arena.allocator);
|
||||
try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint);
|
||||
const limbs = try self.arena.allocator.alloc(
|
||||
std.math.big.Limb,
|
||||
std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
|
||||
);
|
||||
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result_bigint.add(lhs_bigint, rhs_bigint);
|
||||
const result_limbs = result_bigint.limbs[0..result_bigint.len];
|
||||
|
||||
if (!lhs.ty.eql(rhs.ty)) {
|
||||
return self.fail(inst.base.src, "TODO implement peer type resolution", .{});
|
||||
}
|
||||
|
||||
const val_payload = try self.arena.allocator.create(Value.Payload.IntBig);
|
||||
val_payload.* = .{ .big_int = result_bigint };
|
||||
const val_payload = if (result_bigint.positive) blk: {
|
||||
const val_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive);
|
||||
val_payload.* = .{ .limbs = result_limbs };
|
||||
break :blk &val_payload.base;
|
||||
} else blk: {
|
||||
const val_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative);
|
||||
val_payload.* = .{ .limbs = result_limbs };
|
||||
break :blk &val_payload.base;
|
||||
};
|
||||
|
||||
return self.constInst(inst.base.src, .{
|
||||
.ty = lhs.ty,
|
||||
.val = Value.initPayload(&val_payload.base),
|
||||
.val = Value.initPayload(val_payload),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1076,7 +1092,8 @@ const Analyze = struct {
|
||||
return self.constUndef(src, Type.initTag(.bool));
|
||||
const is_unsigned = if (lhs_is_float) x: {
|
||||
var bigint_space: Value.BigIntSpace = undefined;
|
||||
var bigint = lhs_val.toBigInt(&bigint_space);
|
||||
var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
|
||||
defer bigint.deinit();
|
||||
const zcmp = lhs_val.orderAgainstZero();
|
||||
if (lhs_val.floatHasFraction()) {
|
||||
switch (op) {
|
||||
@ -1085,12 +1102,12 @@ const Analyze = struct {
|
||||
else => {},
|
||||
}
|
||||
if (zcmp == .lt) {
|
||||
try bigint.addScalar(bigint, -1);
|
||||
try bigint.addScalar(bigint.toConst(), -1);
|
||||
} else {
|
||||
try bigint.addScalar(bigint, 1);
|
||||
try bigint.addScalar(bigint.toConst(), 1);
|
||||
}
|
||||
}
|
||||
lhs_bits = bigint.bitCountTwosComp();
|
||||
lhs_bits = bigint.toConst().bitCountTwosComp();
|
||||
break :x (zcmp != .lt);
|
||||
} else x: {
|
||||
lhs_bits = lhs_val.intBitCountTwosComp();
|
||||
@ -1110,7 +1127,8 @@ const Analyze = struct {
|
||||
return self.constUndef(src, Type.initTag(.bool));
|
||||
const is_unsigned = if (rhs_is_float) x: {
|
||||
var bigint_space: Value.BigIntSpace = undefined;
|
||||
var bigint = rhs_val.toBigInt(&bigint_space);
|
||||
var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
|
||||
defer bigint.deinit();
|
||||
const zcmp = rhs_val.orderAgainstZero();
|
||||
if (rhs_val.floatHasFraction()) {
|
||||
switch (op) {
|
||||
@ -1119,12 +1137,12 @@ const Analyze = struct {
|
||||
else => {},
|
||||
}
|
||||
if (zcmp == .lt) {
|
||||
try bigint.addScalar(bigint, -1);
|
||||
try bigint.addScalar(bigint.toConst(), -1);
|
||||
} else {
|
||||
try bigint.addScalar(bigint, 1);
|
||||
try bigint.addScalar(bigint.toConst(), 1);
|
||||
}
|
||||
}
|
||||
rhs_bits = bigint.bitCountTwosComp();
|
||||
rhs_bits = bigint.toConst().bitCountTwosComp();
|
||||
break :x (zcmp != .lt);
|
||||
} else x: {
|
||||
rhs_bits = rhs_val.intBitCountTwosComp();
|
||||
|
@ -4,7 +4,8 @@ const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const BigInt = std.math.big.Int;
|
||||
const BigIntConst = std.math.big.int.Const;
|
||||
const BigIntMutable = std.math.big.int.Mutable;
|
||||
const Type = @import("../type.zig").Type;
|
||||
const Value = @import("../value.zig").Value;
|
||||
const ir = @import("../ir.zig");
|
||||
@ -99,7 +100,7 @@ pub const Inst = struct {
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
int: BigInt,
|
||||
int: BigIntConst,
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
@ -521,7 +522,7 @@ pub const Module = struct {
|
||||
},
|
||||
bool => return stream.writeByte("01"[@boolToInt(param)]),
|
||||
[]u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
|
||||
BigInt => return stream.print("{}", .{param}),
|
||||
BigIntConst => return stream.print("{}", .{param}),
|
||||
else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
|
||||
}
|
||||
}
|
||||
@ -644,7 +645,7 @@ const Parser = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn parseIntegerLiteral(self: *Parser) !BigInt {
|
||||
fn parseIntegerLiteral(self: *Parser) !BigIntConst {
|
||||
const start = self.i;
|
||||
if (self.source[self.i] == '-') self.i += 1;
|
||||
while (true) : (self.i += 1) switch (self.source[self.i]) {
|
||||
@ -652,17 +653,21 @@ const Parser = struct {
|
||||
else => break,
|
||||
};
|
||||
const number_text = self.source[start..self.i];
|
||||
var result = try BigInt.init(&self.arena.allocator);
|
||||
result.setString(10, number_text) catch |err| {
|
||||
self.i = start;
|
||||
switch (err) {
|
||||
error.InvalidBase => unreachable,
|
||||
error.InvalidCharForDigit => return self.fail("invalid digit in integer literal", .{}),
|
||||
error.DigitTooLargeForBase => return self.fail("digit too large in integer literal", .{}),
|
||||
else => |e| return e,
|
||||
}
|
||||
const base = 10;
|
||||
// TODO reuse the same array list for this
|
||||
const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
|
||||
const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
|
||||
defer self.allocator.free(limbs_buffer);
|
||||
const limb_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
|
||||
const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
|
||||
var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
|
||||
result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
|
||||
error.InvalidCharacter => {
|
||||
self.i = start;
|
||||
return self.fail("invalid digit in integer literal", .{});
|
||||
},
|
||||
};
|
||||
return result;
|
||||
return result.toConst();
|
||||
}
|
||||
|
||||
fn parseRoot(self: *Parser) !void {
|
||||
@ -859,7 +864,7 @@ const Parser = struct {
|
||||
},
|
||||
*Inst => return parseParameterInst(self, body_ctx),
|
||||
[]u8, []const u8 => return self.parseStringLiteral(),
|
||||
BigInt => return self.parseIntegerLiteral(),
|
||||
BigIntConst => return self.parseIntegerLiteral(),
|
||||
else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
|
||||
}
|
||||
return self.fail("TODO parse parameter {}", .{@typeName(T)});
|
||||
|
@ -3913,18 +3913,20 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
|
||||
};
|
||||
var aps_int = int;
|
||||
const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
|
||||
if (is_negative)
|
||||
aps_int = ZigClangAPSInt_negate(aps_int);
|
||||
var big = try math.big.Int.initCapacity(c.a(), num_limbs);
|
||||
if (is_negative)
|
||||
big.negate();
|
||||
defer big.deinit();
|
||||
if (is_negative) aps_int = ZigClangAPSInt_negate(aps_int);
|
||||
defer if (is_negative) {
|
||||
ZigClangAPSInt_free(aps_int);
|
||||
};
|
||||
|
||||
const limbs = try c.a().alloc(math.big.Limb, num_limbs);
|
||||
defer c.a().free(limbs);
|
||||
|
||||
const data = ZigClangAPSInt_getRawData(aps_int);
|
||||
switch (@sizeOf(std.math.big.Limb)) {
|
||||
switch (@sizeOf(math.big.Limb)) {
|
||||
8 => {
|
||||
var i: usize = 0;
|
||||
while (i < num_limbs) : (i += 1) {
|
||||
big.limbs[i] = data[i];
|
||||
limbs[i] = data[i];
|
||||
}
|
||||
},
|
||||
4 => {
|
||||
@ -3934,23 +3936,23 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
|
||||
limb_i += 2;
|
||||
data_i += 1;
|
||||
}) {
|
||||
big.limbs[limb_i] = @truncate(u32, data[data_i]);
|
||||
big.limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
|
||||
limbs[limb_i] = @truncate(u32, data[data_i]);
|
||||
limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
|
||||
}
|
||||
},
|
||||
else => @compileError("unimplemented"),
|
||||
}
|
||||
const str = big.toString(c.a(), 10, false) catch |err| switch (err) {
|
||||
|
||||
const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative };
|
||||
const str = big.toStringAlloc(c.a(), 10, false) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => unreachable,
|
||||
};
|
||||
defer c.a().free(str);
|
||||
const token = try appendToken(c, .IntegerLiteral, str);
|
||||
const node = try c.a().create(ast.Node.IntegerLiteral);
|
||||
node.* = .{
|
||||
.token = token,
|
||||
};
|
||||
if (is_negative)
|
||||
ZigClangAPSInt_free(aps_int);
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,8 @@ const std = @import("std");
|
||||
const Type = @import("type.zig").Type;
|
||||
const log2 = std.math.log2;
|
||||
const assert = std.debug.assert;
|
||||
const BigInt = std.math.big.Int;
|
||||
const BigIntConst = std.math.big.int.Const;
|
||||
const BigIntMutable = std.math.big.int.Mutable;
|
||||
const Target = std.Target;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
@ -60,7 +61,8 @@ pub const Value = extern union {
|
||||
ty,
|
||||
int_u64,
|
||||
int_i64,
|
||||
int_big,
|
||||
int_big_positive,
|
||||
int_big_negative,
|
||||
function,
|
||||
ref,
|
||||
ref_val,
|
||||
@ -148,7 +150,8 @@ pub const Value = extern union {
|
||||
.ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
|
||||
.int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
|
||||
.int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
|
||||
.int_big => return out_stream.print("{}", .{val.cast(Payload.IntBig).?.big_int}),
|
||||
.int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
|
||||
.int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
|
||||
.function => return out_stream.writeAll("(function)"),
|
||||
.ref => return out_stream.writeAll("(ref)"),
|
||||
.ref_val => {
|
||||
@ -216,7 +219,8 @@ pub const Value = extern union {
|
||||
.null_value,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.function,
|
||||
.ref,
|
||||
.ref_val,
|
||||
@ -227,7 +231,7 @@ pub const Value = extern union {
|
||||
}
|
||||
|
||||
/// Asserts the value is an integer.
|
||||
pub fn toBigInt(self: Value, space: *BigIntSpace) BigInt {
|
||||
pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
|
||||
switch (self.tag()) {
|
||||
.ty,
|
||||
.u8_type,
|
||||
@ -272,11 +276,12 @@ pub const Value = extern union {
|
||||
|
||||
.the_one_possible_value, // An integer with one possible value is always zero.
|
||||
.zero,
|
||||
=> return BigInt.initSetFixed(&space.limbs, 0),
|
||||
=> return BigIntMutable.init(&space.limbs, 0).toConst(),
|
||||
|
||||
.int_u64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_u64).?.int),
|
||||
.int_i64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_i64).?.int),
|
||||
.int_big => return self.cast(Payload.IntBig).?.big_int,
|
||||
.int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
|
||||
.int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
|
||||
.int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
|
||||
.int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,7 +335,8 @@ pub const Value = extern union {
|
||||
|
||||
.int_u64 => return self.cast(Payload.Int_u64).?.int,
|
||||
.int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
|
||||
.int_big => return self.cast(Payload.IntBig).?.big_int.to(u64) catch unreachable,
|
||||
.int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
|
||||
.int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,7 +397,8 @@ pub const Value = extern union {
|
||||
.int_i64 => {
|
||||
@panic("TODO implement i64 intBitCountTwosComp");
|
||||
},
|
||||
.int_big => return self.cast(Payload.IntBig).?.big_int.bitCountTwosComp(),
|
||||
.int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().bitCountTwosComp(),
|
||||
.int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().bitCountTwosComp(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -466,10 +473,18 @@ pub const Value = extern union {
|
||||
.ComptimeInt => return true,
|
||||
else => unreachable,
|
||||
},
|
||||
.int_big => switch (ty.zigTypeTag()) {
|
||||
.int_big_positive => switch (ty.zigTypeTag()) {
|
||||
.Int => {
|
||||
const info = ty.intInfo(target);
|
||||
return self.cast(Payload.IntBig).?.big_int.fitsInTwosComp(info.signed, info.bits);
|
||||
return self.cast(Payload.IntBigPositive).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
|
||||
},
|
||||
.ComptimeInt => return true,
|
||||
else => unreachable,
|
||||
},
|
||||
.int_big_negative => switch (ty.zigTypeTag()) {
|
||||
.Int => {
|
||||
const info = ty.intInfo(target);
|
||||
return self.cast(Payload.IntBigNegative).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
|
||||
},
|
||||
.ComptimeInt => return true,
|
||||
else => unreachable,
|
||||
@ -521,7 +536,8 @@ pub const Value = extern union {
|
||||
.undef,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.the_one_possible_value,
|
||||
=> unreachable,
|
||||
|
||||
@ -578,7 +594,8 @@ pub const Value = extern union {
|
||||
|
||||
.int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
|
||||
.int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
|
||||
.int_big => return lhs.cast(Payload.IntBig).?.big_int.orderAgainstScalar(0),
|
||||
.int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
|
||||
.int_big_negative => return lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,7 +614,7 @@ pub const Value = extern union {
|
||||
var rhs_bigint_space: BigIntSpace = undefined;
|
||||
const lhs_bigint = lhs.toBigInt(&lhs_bigint_space);
|
||||
const rhs_bigint = rhs.toBigInt(&rhs_bigint_space);
|
||||
return BigInt.cmp(lhs_bigint, rhs_bigint);
|
||||
return lhs_bigint.order(rhs_bigint);
|
||||
}
|
||||
|
||||
/// Asserts the value is comparable.
|
||||
@ -658,7 +675,8 @@ pub const Value = extern union {
|
||||
.function,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
@ -712,7 +730,8 @@ pub const Value = extern union {
|
||||
.function,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.undef,
|
||||
=> unreachable,
|
||||
|
||||
@ -775,7 +794,8 @@ pub const Value = extern union {
|
||||
.function,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.ref,
|
||||
.ref_val,
|
||||
.bytes,
|
||||
@ -801,9 +821,22 @@ pub const Value = extern union {
|
||||
int: i64,
|
||||
};
|
||||
|
||||
pub const IntBig = struct {
|
||||
base: Payload = Payload{ .tag = .int_big },
|
||||
big_int: BigInt,
|
||||
pub const IntBigPositive = struct {
|
||||
base: Payload = Payload{ .tag = .int_big_positive },
|
||||
limbs: []const std.math.big.Limb,
|
||||
|
||||
pub fn asBigInt(self: IntBigPositive) BigIntConst {
|
||||
return BigIntConst{ .limbs = self.limbs, .positive = true };
|
||||
}
|
||||
};
|
||||
|
||||
pub const IntBigNegative = struct {
|
||||
base: Payload = Payload{ .tag = .int_big_negative },
|
||||
limbs: []const std.math.big.Limb,
|
||||
|
||||
pub fn asBigInt(self: IntBigNegative) BigIntConst {
|
||||
return BigIntConst{ .limbs = self.limbs, .positive = false };
|
||||
}
|
||||
};
|
||||
|
||||
pub const Function = struct {
|
||||
|
Loading…
x
Reference in New Issue
Block a user