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:
Andrew Kelley 2020-05-01 06:15:58 -04:00
parent 1d202008d8
commit 8766821157
10 changed files with 3515 additions and 2837 deletions

View File

@ -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),

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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.

View File

@ -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();

View File

@ -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)});

View File

@ -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;
}

View File

@ -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 {