authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-01 06:15:58-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-01 06:47:56-04:00
log87668211578b843571d6819fddde944328a05f89
tree68cfe7fcdeaab2fef3d56260c23e0d965a056d56
parent1d202008d8008681988effdf25be2c6a753cf067

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.

10 files changed, 3341 insertions(+), 2663 deletions(-)

lib/std/fmt.zig+1-1
...@@ -1058,7 +1058,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {...@@ -1058,7 +1058,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
1058 return value;1058 return value;
1059}1059}
10601060
1061fn digitToChar(digit: u8, uppercase: bool) u8 {1061pub fn digitToChar(digit: u8, uppercase: bool) u8 {
1062 return switch (digit) {1062 return switch (digit) {
1063 0...9 => digit + '0',1063 0...9 => digit + '0',
1064 10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10),1064 10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10),
lib/std/math/big.zig+22-5
...@@ -1,7 +1,24 @@...@@ -1,7 +1,24 @@
1pub usingnamespace @import("big/int.zig");1const std = @import("../std.zig");
2pub usingnamespace @import("big/rational.zig");2const assert = std.debug.assert;
33
4test "math.big" {4pub const Rational = @import("big/rational.zig").Rational;
5 _ = @import("big/int.zig");5pub const int = @import("big/int.zig");
6 _ = @import("big/rational.zig");6pub const Limb = usize;
7pub const DoubleLimb = std.meta.IntType(false, 2 * Limb.bit_count);
8pub const SignedDoubleLimb = std.meta.IntType(true, DoubleLimb.bit_count);
9pub const Log2Limb = std.math.Log2Int(Limb);
10
11comptime {
12 assert(std.math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);
13 assert(Limb.bit_count <= 64); // u128 set is unsupported
14 assert(Limb.is_signed == false);
15}
16
17test "" {
18 _ = int;
19 _ = Rational;
20 _ = Limb;
21 _ = DoubleLimb;
22 _ = SignedDoubleLimb;
23 _ = Log2Limb;
7}24}
lib/std/math/big/int.zig+1671-2526
...@@ -1,298 +1,196 @@...@@ -1,298 +1,196 @@
1const std = @import("../../std.zig");1const std = @import("../../std.zig");
2const debug = std.debug;
3const testing = std.testing;
4const math = std.math;2const math = std.math;
3const Limb = std.math.big.Limb;
4const DoubleLimb = std.math.big.DoubleLimb;
5const SignedDoubleLimb = std.math.big.SignedDoubleLimb;
6const Log2Limb = std.math.big.Log2Limb;
7const Allocator = std.mem.Allocator;
5const mem = std.mem;8const mem = std.mem;
6const Allocator = mem.Allocator;
7const ArrayList = std.ArrayList;
8const maxInt = std.math.maxInt;9const maxInt = std.math.maxInt;
9const minInt = std.math.minInt;10const minInt = std.math.minInt;
11const assert = std.debug.assert;
1012
11pub const Limb = usize;13/// Returns the number of limbs needed to store `scalar`, which must be a
12pub const DoubleLimb = std.meta.Int(false, 2 * Limb.bit_count);14/// primitive integer value.
13pub const SignedDoubleLimb = std.meta.Int(true, DoubleLimb.bit_count);15pub fn calcLimbLen(scalar: var) usize {
14pub const Log2Limb = math.Log2Int(Limb);16 const T = @TypeOf(scalar);
17 switch (@typeInfo(T)) {
18 .Int => |info| {
19 const UT = if (info.is_signed) std.meta.IntType(false, info.bits - 1) else T;
20 return @sizeOf(UT) / @sizeOf(Limb);
21 },
22 .ComptimeInt => {
23 const w_value = if (scalar < 0) -scalar else scalar;
24 return @divFloor(math.log2(w_value), Limb.bit_count) + 1;
25 },
26 else => @compileError("parameter must be a primitive integer type"),
27 }
28}
1529
16comptime {30pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
17 debug.assert(math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);31 if (math.isPowerOfTwo(base))
18 debug.assert(Limb.bit_count <= 64); // u128 set is unsupported32 return 0;
19 debug.assert(Limb.is_signed == false);33 return a_len + 2 + a_len + calcDivLimbsBufferLen(a_len, 1);
20}34}
2135
22/// An arbitrary-precision big integer.36pub fn calcDivLimbsBufferLen(a_len: usize, b_len: usize) usize {
23///37 return calcMulLimbsBufferLen(a_len, b_len, 2) * 4;
24/// Memory is allocated by an Int as needed to ensure operations never overflow. The range of an38}
25/// Int is bounded only by available memory.
26pub const Int = struct {
27 const sign_bit: usize = 1 << (usize.bit_count - 1);
2839
29 /// Default number of limbs to allocate on creation of an Int.40pub fn calcMulLimbsBufferLen(a_len: usize, b_len: usize, aliases: usize) usize {
30 pub const default_capacity = 4;41 return aliases * math.max(a_len, b_len);
42}
43
44pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {
45 const limb_count = calcSetStringLimbCount(base, string_len);
46 return calcMulLimbsBufferLen(limb_count, limb_count, 2);
47}
3148
32 /// Allocator used by the Int when requesting memory.49pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {
33 allocator: ?*Allocator,50 return (string_len + (Limb.bit_count / base - 1)) / (Limb.bit_count / base);
51}
52
53/// a + b * c + *carry, sets carry to the overflow bits
54pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
55 @setRuntimeSafety(false);
56 var r1: Limb = undefined;
57
58 // r1 = a + *carry
59 const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1));
60
61 // r2 = b * c
62 const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));
63 const r2 = @truncate(Limb, bc);
64 const c2 = @truncate(Limb, bc >> Limb.bit_count);
65
66 // r1 = r1 + r2
67 const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));
68
69 // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then
70 // c2 is at least <= maxInt(Limb) - 2.
71 carry.* = c1 + c2 + c3;
72
73 return r1;
74}
3475
76/// A arbitrary-precision big integer, with a fixed set of mutable limbs.
77pub const Mutable = struct {
35 /// Raw digits. These are:78 /// Raw digits. These are:
36 ///79 ///
37 /// * Little-endian ordered80 /// * Little-endian ordered
38 /// * limbs.len >= 181 /// * limbs.len >= 1
39 /// * Zero is represent as Int.len() == 1 with limbs[0] == 0.82 /// * Zero is represented as limbs.len == 1 with limbs[0] == 0.
40 ///83 ///
41 /// Accessing limbs directly should be avoided.84 /// Accessing limbs directly should be avoided.
85 /// These are allocated limbs; the `len` field tells the valid range.
42 limbs: []Limb,86 limbs: []Limb,
87 len: usize,
88 positive: bool,
4389
44 /// High bit is the sign bit. If set, Int is negative, else Int is positive.90 pub fn toConst(self: Mutable) Const {
45 /// The remaining bits represent the number of limbs used by Int.91 return .{
46 metadata: usize,92 .limbs = self.limbs[0..self.len],
4793 .positive = self.positive,
48 /// Creates a new Int. default_capacity limbs will be allocated immediately.
49 /// Int will be zeroed.
50 pub fn init(allocator: *Allocator) !Int {
51 return try Int.initCapacity(allocator, default_capacity);
52 }
53
54 /// Creates a new Int. Int will be set to `value`.
55 ///
56 /// This is identical to an `init`, followed by a `set`.
57 pub fn initSet(allocator: *Allocator, value: var) !Int {
58 var s = try Int.init(allocator);
59 try s.set(value);
60 return s;
61 }
62
63 /// Hint: use `calcLimbLen` to figure out how big an array to allocate for `limbs`.
64 pub fn initSetFixed(limbs: []Limb, value: var) Int {
65 mem.set(Limb, limbs, 0);
66 var s = Int.initFixed(limbs);
67 s.set(value) catch unreachable;
68 return s;
69 }
70
71 /// Creates a new Int with a specific capacity. If capacity < default_capacity then the
72 /// default capacity will be used instead.
73 pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int {
74 return Int{
75 .allocator = allocator,
76 .metadata = 1,
77 .limbs = block: {
78 var limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));
79 limbs[0] = 0;
80 break :block limbs;
81 },
82 };94 };
83 }95 }
8496
85 /// Returns the number of limbs currently in use.97 /// Asserts that the allocator owns the limbs memory. If this is not the case,
86 pub fn len(self: Int) usize {98 /// use `toConst().toManaged()`.
87 return self.metadata & ~sign_bit;99 pub fn toManaged(self: Mutable, allocator: *Allocator) Managed {
88 }100 return .{
89101 .allocator = allocator,
90 /// Returns whether an Int is positive.
91 pub fn isPositive(self: Int) bool {
92 return self.metadata & sign_bit == 0;
93 }
94
95 /// Sets the sign of an Int.
96 pub fn setSign(self: *Int, positive: bool) void {
97 if (positive) {
98 self.metadata &= ~sign_bit;
99 } else {
100 self.metadata |= sign_bit;
101 }
102 }
103
104 /// Sets the length of an Int.
105 ///
106 /// If setLen is used, then the Int must be normalized to suit.
107 pub fn setLen(self: *Int, new_len: usize) void {
108 self.metadata &= sign_bit;
109 self.metadata |= new_len;
110 }
111
112 /// Returns an Int backed by a fixed set of limb values.
113 /// This is read-only and cannot be used as a result argument. If the Int tries to allocate
114 /// memory a runtime panic will occur.
115 pub fn initFixed(limbs: []Limb) Int {
116 var self = Int{
117 .allocator = null,
118 .metadata = limbs.len,
119 .limbs = limbs,102 .limbs = limbs,
103 .metadata = if (self.positive)
104 self.len & ~Managed.sign_bit
105 else
106 self.len | Managed.sign_bit,
120 };107 };
121
122 self.normalize(limbs.len);
123 return self;
124 }
125
126 /// Ensures an Int has enough space allocated for capacity limbs. If the Int does not have
127 /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested
128 /// capacity is only greater than the current capacity by one limb.
129 pub fn ensureCapacity(self: *Int, capacity: usize) !void {
130 if (capacity <= self.limbs.len) {
131 return;
132 }
133 self.assertWritable();
134 self.limbs = try self.allocator.?.realloc(self.limbs, capacity);
135 }
136
137 fn assertWritable(self: Int) void {
138 if (self.allocator == null) {
139 @panic("provided Int value is read-only but must be writable");
140 }
141 }
142
143 /// Frees all memory associated with an Int.
144 pub fn deinit(self: Int) void {
145 self.assertWritable();
146 self.allocator.?.free(self.limbs);
147 }
148
149 /// Clones an Int and returns a new Int with the same value. The new Int is a deep copy and
150 /// can be modified separately from the original.
151 pub fn clone(other: Int) !Int {
152 return other.clone2(other.allocator.?);
153 }108 }
154109
155 pub fn clone2(other: Int, allocator: *Allocator) !Int {110 /// `value` is a primitive integer type.
156 return Int{111 /// Asserts the value fits within the provided `limbs_buffer`.
157 .allocator = allocator,112 /// Note: `calcLimbLen` can be used to figure out how big an array to allocate for `limbs_buffer`.
158 .metadata = other.metadata,113 pub fn init(limbs_buffer: []Limb, value: var) Mutable {
159 .limbs = block: {114 limbs_buffer[0] = 0;
160 var limbs = try allocator.alloc(Limb, other.len());115 var self: Mutable = .{
161 mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]);116 .limbs = limbs_buffer,
162 break :block limbs;117 .len = 1,
163 },118 .positive = true,
164 };119 };
120 self.set(value);
121 return self;
165 }122 }
166123
167 /// Copies the value of an Int to an existing Int so that they both have the same value.124 /// Copies the value of a Const to an existing Mutable so that they both have the same value.
168 /// Extra memory will be allocated if the receiver does not have enough capacity.125 /// Asserts the value fits in the limbs buffer.
169 pub fn copy(self: *Int, other: Int) !void {126 pub fn copy(self: *Mutable, other: Const) void {
170 self.assertWritable();127 if (self.limbs.ptr != other.limbs.ptr) {
171 if (self.limbs.ptr == other.limbs.ptr) {128 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.limbs.len]);
172 return;
173 }129 }
174130 self.positive = other.positive;
175 try self.ensureCapacity(other.len());131 self.len = other.limbs.len;
176 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.len()]);
177 self.metadata = other.metadata;
178 }132 }
179133
180 /// Efficiently swap an Int with another. This swaps the limb pointers and a full copy is not134 /// Efficiently swap an Mutable with another. This swaps the limb pointers and a full copy is not
181 /// performed. The address of the limbs field will not be the same after this function.135 /// performed. The address of the limbs field will not be the same after this function.
182 pub fn swap(self: *Int, other: *Int) void {136 pub fn swap(self: *Mutable, other: *Mutable) void {
183 self.assertWritable();137 mem.swap(Mutable, self, other);
184 mem.swap(Int, self, other);
185 }
186
187 pub fn dump(self: Int) void {
188 for (self.limbs) |limb| {
189 debug.warn("{x} ", .{limb});
190 }
191 debug.warn("\n", .{});
192 }
193
194 /// Negate the sign of an Int.
195 pub fn negate(self: *Int) void {
196 self.metadata ^= sign_bit;
197 }
198
199 /// Make an Int positive.
200 pub fn abs(self: *Int) void {
201 self.metadata &= ~sign_bit;
202 }
203
204 /// Returns true if an Int is odd.
205 pub fn isOdd(self: Int) bool {
206 return self.limbs[0] & 1 != 0;
207 }138 }
208139
209 /// Returns true if an Int is even.140 pub fn dump(self: Mutable) void {
210 pub fn isEven(self: Int) bool {141 for (self.limbs[0..self.len]) |limb| {
211 return !self.isOdd();142 std.debug.warn("{x} ", .{limb});
212 }
213
214 /// Returns the number of bits required to represent the absolute value an Int.
215 fn bitCountAbs(self: Int) usize {
216 return (self.len() - 1) * Limb.bit_count + (Limb.bit_count - @clz(Limb, self.limbs[self.len() - 1]));
217 }
218
219 /// Returns the number of bits required to represent the integer in twos-complement form.
220 ///
221 /// If the integer is negative the value returned is the number of bits needed by a signed
222 /// integer to represent the value. If positive the value is the number of bits for an
223 /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
224 /// one greater than the returned value.
225 ///
226 /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
227 pub fn bitCountTwosComp(self: Int) usize {
228 var bits = self.bitCountAbs();
229
230 // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
231 // complement requires one less bit.
232 if (!self.isPositive()) block: {
233 bits += 1;
234
235 if (@popCount(Limb, self.limbs[self.len() - 1]) == 1) {
236 for (self.limbs[0 .. self.len() - 1]) |limb| {
237 if (@popCount(Limb, limb) != 0) {
238 break :block;
239 }
240 }
241
242 bits -= 1;
243 }
244 }143 }
245144 std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.positive });
246 return bits;
247 }145 }
248146
249 pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {147 /// Clones an Mutable and returns a new Mutable with the same value. The new Mutable is a deep copy and
250 if (self.eqZero()) {148 /// can be modified separately from the original.
251 return true;149 /// Asserts that limbs is big enough to store the value.
252 }150 pub fn clone(other: Mutable, limbs: []Limb) Mutable {
253 if (!is_signed and !self.isPositive()) {151 mem.copy(Limb, limbs, other.limbs[0..other.len]);
254 return false;152 return .{
255 }153 .limbs = limbs,
256154 .len = other.len,
257 const req_bits = self.bitCountTwosComp() + @boolToInt(self.isPositive() and is_signed);155 .positive = other.positive,
258 return bit_count >= req_bits;156 };
259 }157 }
260158
261 /// Returns whether self can fit into an integer of the requested type.159 pub fn negate(self: *Mutable) void {
262 pub fn fits(self: Int, comptime T: type) bool {160 self.positive = !self.positive;
263 return self.fitsInTwosComp(T.is_signed, T.bit_count);
264 }161 }
265162
266 /// Returns the approximate size of the integer in the given base. Negative values accommodate for163 /// Modify to become the absolute value
267 /// the minus sign. This is used for determining the number of characters needed to print the164 pub fn abs(self: *Mutable) void {
268 /// value. It is inexact and may exceed the given value by ~1-2 bytes.165 self.positive = true;
269 pub fn sizeInBase(self: Int, base: usize) usize {
270 const bit_count = @as(usize, @boolToInt(!self.isPositive())) + self.bitCountAbs();
271 return (bit_count / math.log2(base)) + 1;
272 }166 }
273167
274 /// Sets an Int to value. Value must be an primitive integer type.168 /// Sets the Mutable to value. Value must be an primitive integer type.
275 pub fn set(self: *Int, value: var) Allocator.Error!void {169 /// Asserts the value fits within the limbs buffer.
170 /// Note: `calcLimbLen` can be used to figure out how big the limbs buffer
171 /// needs to be to store a specific value.
172 pub fn set(self: *Mutable, value: var) void {
276 const T = @TypeOf(value);173 const T = @TypeOf(value);
277174
278 switch (@typeInfo(T)) {175 switch (@typeInfo(T)) {
279 .Int => |info| {176 .Int => |info| {
280 const UT = if (T.is_signed) std.meta.Int(false, T.bit_count - 1) else T;177 const UT = if (T.is_signed) std.meta.IntType(false, T.bit_count - 1) else T;
281178
282 try self.ensureCapacity(@sizeOf(UT) / @sizeOf(Limb));179 const needed_limbs = @sizeOf(UT) / @sizeOf(Limb);
283 self.metadata = 0;180 assert(needed_limbs <= self.limbs.len); // value too big
284 self.setSign(value >= 0);181 self.len = 0;
182 self.positive = value >= 0;
285183
286 var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);184 var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);
287185
288 if (info.bits <= Limb.bit_count) {186 if (info.bits <= Limb.bit_count) {
289 self.limbs[0] = @as(Limb, w_value);187 self.limbs[0] = @as(Limb, w_value);
290 self.metadata += 1;188 self.len += 1;
291 } else {189 } else {
292 var i: usize = 0;190 var i: usize = 0;
293 while (w_value != 0) : (i += 1) {191 while (w_value != 0) : (i += 1) {
294 self.limbs[i] = @truncate(Limb, w_value);192 self.limbs[i] = @truncate(Limb, w_value);
295 self.metadata += 1;193 self.len += 1;
296194
297 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.195 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
298 w_value >>= Limb.bit_count / 2;196 w_value >>= Limb.bit_count / 2;
...@@ -304,10 +202,10 @@ pub const Int = struct {...@@ -304,10 +202,10 @@ pub const Int = struct {
304 comptime var w_value = if (value < 0) -value else value;202 comptime var w_value = if (value < 0) -value else value;
305203
306 const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;204 const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;
307 try self.ensureCapacity(req_limbs);205 assert(req_limbs <= self.limbs.len); // value too big
308206
309 self.metadata = req_limbs;207 self.len = req_limbs;
310 self.setSign(value >= 0);208 self.positive = value >= 0;
311209
312 if (w_value <= maxInt(Limb)) {210 if (w_value <= maxInt(Limb)) {
313 self.limbs[0] = w_value;211 self.limbs[0] = w_value;
...@@ -323,83 +221,8 @@ pub const Int = struct {...@@ -323,83 +221,8 @@ pub const Int = struct {
323 }221 }
324 }222 }
325 },223 },
326 else => {224 else => @compileError("cannot set Mutable using type " ++ @typeName(T)),
327 @compileError("cannot set Int using type " ++ @typeName(T));
328 },
329 }
330 }
331
332 pub const ConvertError = error{
333 NegativeIntoUnsigned,
334 TargetTooSmall,
335 };
336
337 /// Convert self to type T.
338 ///
339 /// Returns an error if self cannot be narrowed into the requested type without truncation.
340 pub fn to(self: Int, comptime T: type) ConvertError!T {
341 switch (@typeInfo(T)) {
342 .Int => {
343 const UT = std.meta.Int(false, T.bit_count);
344
345 if (self.bitCountTwosComp() > T.bit_count) {
346 return error.TargetTooSmall;
347 }
348
349 var r: UT = 0;
350
351 if (@sizeOf(UT) <= @sizeOf(Limb)) {
352 r = @intCast(UT, self.limbs[0]);
353 } else {
354 for (self.limbs[0..self.len()]) |_, ri| {
355 const limb = self.limbs[self.len() - ri - 1];
356 r <<= Limb.bit_count;
357 r |= limb;
358 }
359 }
360
361 if (!T.is_signed) {
362 return if (self.isPositive()) @intCast(T, r) else error.NegativeIntoUnsigned;
363 } else {
364 if (self.isPositive()) {
365 return @intCast(T, r);
366 } else {
367 if (math.cast(T, r)) |ok| {
368 return -ok;
369 } else |_| {
370 return minInt(T);
371 }
372 }
373 }
374 },
375 else => {
376 @compileError("cannot convert Int to type " ++ @typeName(T));
377 },
378 }
379 }
380
381 fn charToDigit(ch: u8, base: u8) !u8 {
382 const d = switch (ch) {
383 '0'...'9' => ch - '0',
384 'a'...'f' => (ch - 'a') + 0xa,
385 'A'...'F' => (ch - 'A') + 0xa,
386 else => return error.InvalidCharForDigit,
387 };
388
389 return if (d < base) d else return error.DigitTooLargeForBase;
390 }
391
392 fn digitToChar(d: u8, base: u8, uppercase: bool) !u8 {
393 if (d >= base) {
394 return error.DigitTooLargeForBase;
395 }225 }
396
397 const a: u8 = if (uppercase) 'A' else 'a';
398 return switch (d) {
399 0...9 => '0' + d,
400 0xa...0xf => (a - 0xa) + d,
401 else => unreachable,
402 };
403 }226 }
404227
405 /// Set self from the string representation `value`.228 /// Set self from the string representation `value`.
...@@ -408,13 +231,25 @@ pub const Int = struct {...@@ -408,13 +231,25 @@ pub const Int = struct {
408 /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are231 /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are
409 /// ignored and can be used as digit separators.232 /// ignored and can be used as digit separators.
410 ///233 ///
411 /// Returns an error if memory could not be allocated or `value` has invalid digits for the234 /// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can
412 /// requested base.235 /// be determined with `calcSetStringLimbCount`.
413 pub fn setString(self: *Int, base: u8, value: []const u8) !void {236 /// Asserts the base is in the range [2, 16].
414 self.assertWritable();237 ///
415 if (base < 2 or base > 16) {238 /// Returns an error if the value has invalid digits for the requested base.
416 return error.InvalidBase;239 ///
417 }240 /// `limbs_buffer` is used for temporary storage. The size required can be found with
241 /// `calcSetStringLimbsBufferLen`.
242 ///
243 /// If `allocator` is provided, it will be used for temporary storage to improve
244 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
245 pub fn setString(
246 self: *Mutable,
247 base: u8,
248 value: []const u8,
249 limbs_buffer: []Limb,
250 allocator: ?*Allocator,
251 ) error{InvalidCharacter}!void {
252 assert(base >= 2 and base <= 16);
418253
419 var i: usize = 0;254 var i: usize = 0;
420 var positive = true;255 var positive = true;
...@@ -423,787 +258,561 @@ pub const Int = struct {...@@ -423,787 +258,561 @@ pub const Int = struct {
423 i += 1;258 i += 1;
424 }259 }
425260
426 const ap_base = Int.initFixed(([_]Limb{base})[0..]);261 const ap_base: Const = .{ .limbs = &[_]Limb{base}, .positive = true };
427 try self.set(0);262 self.set(0);
428263
429 for (value[i..]) |ch| {264 for (value[i..]) |ch| {
430 if (ch == '_') {265 if (ch == '_') {
431 continue;266 continue;
432 }267 }
433 const d = try charToDigit(ch, base);268 const d = try std.fmt.charToDigit(ch, base);
269 const ap_d: Const = .{ .limbs = &[_]Limb{d}, .positive = true };
434270
435 const ap_d = Int.initFixed(([_]Limb{d})[0..]);271 self.mul(self.toConst(), ap_base, limbs_buffer, allocator);
436272 self.add(self.toConst(), ap_d);
437 try self.mul(self.*, ap_base);
438 try self.add(self.*, ap_d);
439 }273 }
440 self.setSign(positive);274 self.positive = positive;
441 }275 }
442276
443 /// Converts self to a string in the requested base. Memory is allocated from the provided277 /// r = a + scalar
444 /// allocator and not the one present in self.278 ///
445 /// TODO make this call format instead of the other way around279 /// r and a may be aliases.
446 pub fn toString(self: Int, allocator: *Allocator, base: u8, uppercase: bool) ![]const u8 {280 /// scalar is a primitive integer type.
447 if (base < 2 or base > 16) {281 ///
448 return error.InvalidBase;282 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
449 }283 /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`.
450284 pub fn addScalar(r: *Mutable, a: Const, scalar: var) void {
451 var digits = ArrayList(u8).init(allocator);285 var limbs: [calcLimbLen(scalar)]Limb = undefined;
452 try digits.ensureCapacity(self.sizeInBase(base) + 1);286 const operand = init(&limbs, scalar).toConst();
453 defer digits.deinit();287 return add(r, a, operand);
288 }
454289
455 if (self.eqZero()) {290 /// r = a + b
456 try digits.append('0');291 ///
457 return digits.toOwnedSlice();292 /// r, a and b may be aliases.
293 ///
294 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
295 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`.
296 pub fn add(r: *Mutable, a: Const, b: Const) void {
297 if (a.eqZero()) {
298 r.copy(b);
299 return;
300 } else if (b.eqZero()) {
301 r.copy(a);
302 return;
458 }303 }
459304
460 // Power of two: can do a single pass and use masks to extract digits.305 if (a.limbs.len == 1 and b.limbs.len == 1 and a.positive == b.positive) {
461 if (math.isPowerOfTwo(base)) {306 if (!@addWithOverflow(Limb, a.limbs[0], b.limbs[0], &r.limbs[0])) {
462 const base_shift = math.log2_int(Limb, base);307 r.len = 1;
463308 r.positive = a.positive;
464 for (self.limbs[0..self.len()]) |limb| {309 return;
465 var shift: usize = 0;
466 while (shift < Limb.bit_count) : (shift += base_shift) {
467 const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1));
468 const ch = try digitToChar(r, base, uppercase);
469 try digits.append(ch);
470 }
471 }310 }
311 }
472312
473 while (true) {313 if (a.positive != b.positive) {
474 // always will have a non-zero digit somewhere314 if (a.positive) {
475 const c = digits.pop();315 // (a) + (-b) => a - b
476 if (c != '0') {316 r.sub(a, b.abs());
477 digits.append(c) catch unreachable;317 } else {
478 break;318 // (-a) + (b) => b - a
479 }319 r.sub(b, a.abs());
480 }320 }
481 } else {321 } else {
482 // Non power-of-two: batch divisions per word size.322 if (a.limbs.len >= b.limbs.len) {
483 const digits_per_limb = math.log(Limb, base, maxInt(Limb));323 lladd(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
484 var limb_base: Limb = 1;324 r.normalize(a.limbs.len + 1);
485 var j: usize = 0;325 } else {
486 while (j < digits_per_limb) : (j += 1) {326 lladd(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
487 limb_base *= base;327 r.normalize(b.limbs.len + 1);
488 }328 }
489329
490 var q = try self.clone2(allocator);330 r.positive = a.positive;
491 defer q.deinit();331 }
492 q.abs();332 }
493 var r = try Int.init(allocator);
494 defer r.deinit();
495 var b = try Int.initSet(allocator, limb_base);
496 defer b.deinit();
497
498 while (q.len() >= 2) {
499 try Int.divTrunc(&q, &r, q, b);
500333
501 var r_word = r.limbs[0];334 /// r = a - b
502 var i: usize = 0;335 ///
503 while (i < digits_per_limb) : (i += 1) {336 /// r, a and b may be aliases.
504 const ch = try digitToChar(@intCast(u8, r_word % base), base, uppercase);337 ///
505 r_word /= base;338 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
506 try digits.append(ch);339 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.
507 }340 pub fn sub(r: *Mutable, a: Const, b: Const) void {
341 if (a.positive != b.positive) {
342 if (a.positive) {
343 // (a) - (-b) => a + b
344 r.add(a, b.abs());
345 } else {
346 // (-a) - (b) => -(a + b)
347 r.add(a.abs(), b);
348 r.positive = false;
508 }349 }
509350 } else {
510 {351 if (a.positive) {
511 debug.assert(q.len() == 1);352 // (a) - (b) => a - b
512353 if (a.order(b) != .lt) {
513 var r_word = q.limbs[0];354 llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
514 while (r_word != 0) {355 r.normalize(a.limbs.len);
515 const ch = try digitToChar(@intCast(u8, r_word % base), base, uppercase);356 r.positive = true;
516 r_word /= base;357 } else {
517 try digits.append(ch);358 llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
359 r.normalize(b.limbs.len);
360 r.positive = false;
361 }
362 } else {
363 // (-a) - (-b) => -(a - b)
364 if (a.order(b) == .lt) {
365 llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
366 r.normalize(a.limbs.len);
367 r.positive = false;
368 } else {
369 llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
370 r.normalize(b.limbs.len);
371 r.positive = true;
518 }372 }
519 }373 }
520 }374 }
521
522 if (!self.isPositive()) {
523 try digits.append('-');
524 }
525
526 var s = digits.toOwnedSlice();
527 mem.reverse(u8, s);
528 return s;
529 }375 }
530376
531 /// To allow `std.fmt.printf` to work with Int.377 /// rma = a * b
532 /// TODO make this non-allocating378 ///
533 /// TODO support read-only fixed integers379 /// `rma` may alias with `a` or `b`.
534 pub fn format(380 /// `a` and `b` may alias with each other.
535 self: Int,381 ///
536 comptime fmt: []const u8,382 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
537 options: std.fmt.FormatOptions,383 /// rma is given by `a.limbs.len + b.limbs.len + 1`.
538 out_stream: var,384 ///
539 ) !void {385 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcMulLimbsBufferLen`.
540 comptime var radix = 10;386 pub fn mul(rma: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void {
541 comptime var uppercase = false;387 var buf_index: usize = 0;
542
543 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) {
544 radix = 10;
545 uppercase = false;
546 } else if (comptime std.mem.eql(u8, fmt, "b")) {
547 radix = 2;
548 uppercase = false;
549 } else if (comptime std.mem.eql(u8, fmt, "x")) {
550 radix = 16;
551 uppercase = false;
552 } else if (comptime std.mem.eql(u8, fmt, "X")) {
553 radix = 16;
554 uppercase = true;
555 } else {
556 @compileError("Unknown format string: '" ++ fmt ++ "'");
557 }
558
559 var buf: [4096]u8 = undefined;
560 var fba = std.heap.FixedBufferAllocator.init(&buf);
561 const str = self.toString(&fba.allocator, radix, uppercase) catch @panic("TODO make this non allocating");
562 return out_stream.writeAll(str);
563 }
564388
565 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==389 const a_copy = if (rma.limbs.ptr == a.limbs.ptr) blk: {
566 /// |b| or |a| > |b| respectively.390 const start = buf_index;
567 pub fn cmpAbs(a: Int, b: Int) math.Order {391 mem.copy(Limb, limbs_buffer[buf_index..], a.limbs);
568 if (a.len() < b.len()) {392 buf_index += a.limbs.len;
569 return .lt;393 break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst();
570 }394 } else a;
571 if (a.len() > b.len()) {
572 return .gt;
573 }
574395
575 var i: usize = a.len() - 1;396 const b_copy = if (rma.limbs.ptr == b.limbs.ptr) blk: {
576 while (i != 0) : (i -= 1) {397 const start = buf_index;
577 if (a.limbs[i] != b.limbs[i]) {398 mem.copy(Limb, limbs_buffer[buf_index..], b.limbs);
578 break;399 buf_index += b.limbs.len;
579 }400 break :blk b.toMutable(limbs_buffer[start..buf_index]).toConst();
580 }401 } else b;
581402
582 if (a.limbs[i] < b.limbs[i]) {403 return rma.mulNoAlias(a_copy, b_copy, allocator);
583 return .lt;
584 } else if (a.limbs[i] > b.limbs[i]) {
585 return .gt;
586 } else {
587 return .eq;
588 }
589 }404 }
590405
591 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a406 /// rma = a * b
592 /// > b respectively.407 ///
593 pub fn cmp(a: Int, b: Int) math.Order {408 /// `rma` may not alias with `a` or `b`.
594 if (a.isPositive() != b.isPositive()) {409 /// `a` and `b` may alias with each other.
595 return if (a.isPositive()) .gt else .lt;410 ///
596 } else {411 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
597 const r = cmpAbs(a, b);412 /// rma is given by `a.limbs.len + b.limbs.len + 1`.
598 return if (a.isPositive()) r else switch (r) {413 ///
599 .lt => math.Order.gt,414 /// If `allocator` is provided, it will be used for temporary storage to improve
600 .eq => math.Order.eq,415 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
601 .gt => math.Order.lt,416 pub fn mulNoAlias(rma: *Mutable, a: Const, b: Const, allocator: ?*Allocator) void {
602 };417 assert(rma.limbs.ptr != a.limbs.ptr); // illegal aliasing
418 assert(rma.limbs.ptr != b.limbs.ptr); // illegal aliasing
419
420 if (a.limbs.len == 1 and b.limbs.len == 1) {
421 if (!@mulWithOverflow(Limb, a.limbs[0], b.limbs[0], &rma.limbs[0])) {
422 rma.len = 1;
423 rma.positive = (a.positive == b.positive);
424 return;
425 }
603 }426 }
604 }
605
606 /// Same as `cmp` but the right-hand operand is a primitive integer.
607 pub fn orderAgainstScalar(lhs: Int, scalar: var) math.Order {
608 var limbs: [calcLimbLen(scalar)]Limb = undefined;
609 const rhs = initSetFixed(&limbs, scalar);
610 return cmp(lhs, rhs);
611 }
612
613 /// Returns true if a == 0.
614 pub fn eqZero(a: Int) bool {
615 return a.len() == 1 and a.limbs[0] == 0;
616 }
617
618 /// Returns true if |a| == |b|.
619 pub fn eqAbs(a: Int, b: Int) bool {
620 return cmpAbs(a, b) == .eq;
621 }
622427
623 /// Returns true if a == b.428 mem.set(Limb, rma.limbs[0 .. a.limbs.len + b.limbs.len + 1], 0);
624 pub fn eq(a: Int, b: Int) bool {
625 return cmp(a, b) == .eq;
626 }
627429
628 // Normalize a possible sequence of leading zeros.430 llmulacc(allocator, rma.limbs, a.limbs, b.limbs);
629 //
630 // [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
631 // [1, 2, 0, 0, 0] -> [1, 2]
632 // [0, 0, 0, 0, 0] -> [0]
633 fn normalize(r: *Int, length: usize) void {
634 debug.assert(length > 0);
635 debug.assert(length <= r.limbs.len);
636
637 var j = length;
638 while (j > 0) : (j -= 1) {
639 if (r.limbs[j - 1] != 0) {
640 break;
641 }
642 }
643431
644 // Handle zero432 rma.normalize(a.limbs.len + b.limbs.len);
645 r.setLen(if (j != 0) j else 1);433 rma.positive = (a.positive == b.positive);
646 }434 }
647435
648 // Cannot be used as a result argument to any function.436 /// q = a / b (rem r)
649 fn readOnlyPositive(a: Int) Int {437 ///
650 return Int{438 /// a / b are floored (rounded towards 0).
651 .allocator = null,439 /// q may alias with a or b.
652 .metadata = a.len(),440 ///
653 .limbs = a.limbs,441 /// Asserts there is enough memory to store q and r.
654 };442 /// The upper bound for r limb count is a.limbs.len.
655 }443 /// The upper bound for q limb count is given by `a.limbs.len + b.limbs.len + 1`.
444 ///
445 /// If `allocator` is provided, it will be used for temporary storage to improve
446 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
447 ///
448 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`.
449 pub fn divFloor(
450 q: *Mutable,
451 r: *Mutable,
452 a: Const,
453 b: Const,
454 limbs_buffer: []Limb,
455 allocator: ?*Allocator,
456 ) void {
457 div(q, r, a, b, limbs_buffer, allocator);
656458
657 /// Returns the number of limbs needed to store `scalar`, which must be a459 // Trunc -> Floor.
658 /// primitive integer value.460 if (!q.positive) {
659 pub fn calcLimbLen(scalar: var) usize {461 const one: Const = .{ .limbs = &[_]Limb{1}, .positive = true };
660 switch (@typeInfo(@TypeOf(scalar))) {462 q.sub(q.toConst(), one);
661 .Int => return @sizeOf(scalar) / @sizeOf(Limb),463 r.add(q.toConst(), one);
662 .ComptimeInt => {
663 const w_value = if (scalar < 0) -scalar else scalar;
664 const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;
665 return req_limbs;
666 },
667 else => @compileError("parameter must be a primitive integer type"),
668 }464 }
465 r.positive = b.positive;
669 }466 }
670467
671 /// r = a + scalar468 /// q = a / b (rem r)
672 ///469 ///
673 /// r and a may be aliases.470 /// a / b are truncated (rounded towards -inf).
674 /// scalar is a primitive integer type.471 /// q may alias with a or b.
675 ///472 ///
676 /// Returns an error if memory could not be allocated.473 /// Asserts there is enough memory to store q and r.
677 pub fn addScalar(r: *Int, a: Int, scalar: var) Allocator.Error!void {474 /// The upper bound for r limb count is a.limbs.len.
678 var limbs: [calcLimbLen(scalar)]Limb = undefined;475 /// The upper bound for q limb count is given by `calcQuotientLimbLen`. This accounts
679 var operand = initFixed(&limbs);476 /// for temporary space used by the division algorithm.
680 operand.set(scalar) catch unreachable;477 ///
681 return add(r, a, operand);478 /// If `allocator` is provided, it will be used for temporary storage to improve
479 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
480 ///
481 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`.
482 pub fn divTrunc(
483 q: *Mutable,
484 r: *Mutable,
485 a: Const,
486 b: Const,
487 limbs_buffer: []Limb,
488 allocator: ?*Allocator,
489 ) void {
490 div(q, r, a, b, limbs_buffer, allocator);
491 r.positive = a.positive;
682 }492 }
683493
684 /// r = a + b494 /// r = a << shift, in other words, r = a * 2^shift
685 ///495 ///
686 /// r, a and b may be aliases.496 /// r and a may alias.
687 ///497 ///
688 /// Returns an error if memory could not be allocated.498 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
689 pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void {499 /// `a.limbs.len + (shift / (@sizeOf(Limb) * 8))`.
690 r.assertWritable();500 pub fn shiftLeft(r: *Mutable, a: Const, shift: usize) void {
691 if (a.eqZero()) {501 llshl(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
692 try r.copy(b);502 r.normalize(a.limbs.len + (shift / Limb.bit_count) + 1);
693 return;503 r.positive = a.positive;
694 } else if (b.eqZero()) {
695 try r.copy(a);
696 return;
697 }
698
699 if (a.isPositive() != b.isPositive()) {
700 if (a.isPositive()) {
701 // (a) + (-b) => a - b
702 try r.sub(a, readOnlyPositive(b));
703 } else {
704 // (-a) + (b) => b - a
705 try r.sub(b, readOnlyPositive(a));
706 }
707 } else {
708 if (a.len() >= b.len()) {
709 try r.ensureCapacity(a.len() + 1);
710 lladd(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
711 r.normalize(a.len() + 1);
712 } else {
713 try r.ensureCapacity(b.len() + 1);
714 lladd(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
715 r.normalize(b.len() + 1);
716 }
717
718 r.setSign(a.isPositive());
719 }
720 }504 }
721505
722 // Knuth 4.3.1, Algorithm A.506 /// r = a >> shift
723 fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {507 /// r and a may alias.
724 @setRuntimeSafety(false);508 ///
725 debug.assert(a.len != 0 and b.len != 0);509 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
726 debug.assert(a.len >= b.len);510 /// `a.limbs.len - (shift / (@sizeOf(Limb) * 8))`.
727 debug.assert(r.len >= a.len + 1);511 pub fn shiftRight(r: *Mutable, a: Const, shift: usize) void {
728512 if (a.limbs.len <= shift / Limb.bit_count) {
729 var i: usize = 0;513 r.len = 1;
730 var carry: Limb = 0;514 r.positive = true;
731515 r.limbs[0] = 0;
732 while (i < b.len) : (i += 1) {516 return;
733 var c: Limb = 0;
734 c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i]));
735 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
736 carry = c;
737 }
738
739 while (i < a.len) : (i += 1) {
740 carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i]));
741 }517 }
742518
743 r[i] = carry;519 const r_len = llshr(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
520 r.len = a.limbs.len - (shift / Limb.bit_count);
521 r.positive = a.positive;
744 }522 }
745523
746 /// r = a - b524 /// r = a | b
525 /// r may alias with a or b.
747 ///526 ///
748 /// r, a and b may be aliases.527 /// a and b are zero-extended to the longer of a or b.
749 ///528 ///
750 /// Returns an error if memory could not be allocated.529 /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.
751 pub fn sub(r: *Int, a: Int, b: Int) !void {530 pub fn bitOr(r: *Mutable, a: Const, b: Const) void {
752 r.assertWritable();531 if (a.limbs.len > b.limbs.len) {
753 if (a.isPositive() != b.isPositive()) {532 llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
754 if (a.isPositive()) {533 r.len = a.limbs.len;
755 // (a) - (-b) => a + b
756 try r.add(a, readOnlyPositive(b));
757 } else {
758 // (-a) - (b) => -(a + b)
759 try r.add(readOnlyPositive(a), b);
760 r.setSign(false);
761 }
762 } else {534 } else {
763 if (a.isPositive()) {535 llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
764 // (a) - (b) => a - b536 r.len = b.limbs.len;
765 if (a.cmp(b) != .lt) {
766 try r.ensureCapacity(a.len() + 1);
767 llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
768 r.normalize(a.len());
769 r.setSign(true);
770 } else {
771 try r.ensureCapacity(b.len() + 1);
772 llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
773 r.normalize(b.len());
774 r.setSign(false);
775 }
776 } else {
777 // (-a) - (-b) => -(a - b)
778 if (a.cmp(b) == .lt) {
779 try r.ensureCapacity(a.len() + 1);
780 llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
781 r.normalize(a.len());
782 r.setSign(false);
783 } else {
784 try r.ensureCapacity(b.len() + 1);
785 llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
786 r.normalize(b.len());
787 r.setSign(true);
788 }
789 }
790 }537 }
791 }538 }
792539
793 // Knuth 4.3.1, Algorithm S.540 /// r = a & b
794 fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {541 /// r may alias with a or b.
795 @setRuntimeSafety(false);542 ///
796 debug.assert(a.len != 0 and b.len != 0);543 /// Asserts that r has enough limbs to store the result. Upper bound is `math.min(a.limbs.len, b.limbs.len)`.
797 debug.assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));544 pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {
798 debug.assert(r.len >= a.len);545 if (a.limbs.len > b.limbs.len) {
799546 lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
800 var i: usize = 0;547 r.normalize(b.limbs.len);
801 var borrow: Limb = 0;548 } else {
802549 lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
803 while (i < b.len) : (i += 1) {550 r.normalize(a.limbs.len);
804 var c: Limb = 0;
805 c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i]));
806 c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i]));
807 borrow = c;
808 }551 }
552 }
809553
810 while (i < a.len) : (i += 1) {554 /// r = a ^ b
811 borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i]));555 /// r may alias with a or b.
556 ///
557 /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.
558 pub fn bitXor(r: *Mutable, a: Const, b: Const) void {
559 if (a.limbs.len > b.limbs.len) {
560 llxor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
561 r.normalize(a.limbs.len);
562 } else {
563 llxor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
564 r.normalize(b.limbs.len);
812 }565 }
813
814 debug.assert(borrow == 0);
815 }566 }
816567
817 /// rma = a * b568 /// rma may alias x or y.
569 /// x and y may alias each other.
570 /// Asserts that `rma` has enough limbs to store the result. Upper bound is
571 /// `math.min(x.limbs.len, y.limbs.len)`.
818 ///572 ///
819 /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.573 /// `limbs_buffer` is used for temporary storage during the operation. When this function returns,
574 /// it will have the same length as it had when the function was called.
575 pub fn gcd(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
576 const prev_len = limbs_buffer.items.len;
577 defer limbs_buffer.shrink(prev_len);
578 const x_copy = if (rma.limbs.ptr == x.limbs.ptr) blk: {
579 const start = limbs_buffer.items.len;
580 try limbs_buffer.appendSlice(x.limbs);
581 break :blk x.toMutable(limbs_buffer.items[start..]).toConst();
582 } else x;
583 const y_copy = if (rma.limbs.ptr == y.limbs.ptr) blk: {
584 const start = limbs_buffer.items.len;
585 try limbs_buffer.appendSlice(y.limbs);
586 break :blk y.toMutable(limbs_buffer.items[start..]).toConst();
587 } else y;
588
589 return gcdLehmer(rma, x_copy, y_copy, limbs_buffer);
590 }
591
592 /// rma may not alias x or y.
593 /// x and y may alias each other.
594 /// Asserts that `rma` has enough limbs to store the result. Upper bound is given by `calcGcdNoAliasLimbLen`.
820 ///595 ///
821 /// Returns an error if memory could not be allocated.596 /// `limbs_buffer` is used for temporary storage during the operation.
822 pub fn mul(rma: *Int, a: Int, b: Int) !void {597 pub fn gcdNoAlias(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
823 rma.assertWritable();598 assert(rma.limbs.ptr != x.limbs.ptr); // illegal aliasing
599 assert(rma.limbs.ptr != y.limbs.ptr); // illegal aliasing
600 return gcdLehmer(rma, x, y, allocator);
601 }
602
603 fn gcdLehmer(result: *Mutable, xa: Const, ya: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
604 var x = try xa.toManaged(limbs_buffer.allocator);
605 defer x.deinit();
606 x.abs();
824607
825 var r = rma;608 var y = try ya.toManaged(limbs_buffer.allocator);
826 var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr;609 defer y.deinit();
610 y.abs();
827611
828 var sr: Int = undefined;612 if (x.toConst().order(y.toConst()) == .lt) {
829 if (aliased) {613 x.swap(&y);
830 sr = try Int.initCapacity(rma.allocator.?, a.len() + b.len());
831 r = &sr;
832 aliased = true;
833 }614 }
834 defer if (aliased) {
835 rma.swap(r);
836 r.deinit();
837 };
838615
839 try r.ensureCapacity(a.len() + b.len() + 1);616 var t_big = try Managed.init(limbs_buffer.allocator);
617 defer t_big.deinit();
840618
841 mem.set(Limb, r.limbs[0 .. a.len() + b.len() + 1], 0);619 var r = try Managed.init(limbs_buffer.allocator);
620 defer r.deinit();
842621
843 try llmulacc(rma.allocator.?, r.limbs, a.limbs[0..a.len()], b.limbs[0..b.len()]);622 while (y.len() > 1) {
623 assert(x.isPositive() and y.isPositive());
624 assert(x.len() >= y.len());
844625
845 r.normalize(a.len() + b.len());626 var xh: SignedDoubleLimb = x.limbs[x.len() - 1];
846 r.setSign(a.isPositive() == b.isPositive());627 var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1];
847 }628
629 var A: SignedDoubleLimb = 1;
630 var B: SignedDoubleLimb = 0;
631 var C: SignedDoubleLimb = 0;
632 var D: SignedDoubleLimb = 1;
633
634 while (yh + C != 0 and yh + D != 0) {
635 const q = @divFloor(xh + A, yh + C);
636 const qp = @divFloor(xh + B, yh + D);
637 if (q != qp) {
638 break;
639 }
848640
849 // a + b * c + *carry, sets carry to the overflow bits641 var t = A - q * C;
850 pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {642 A = C;
851 @setRuntimeSafety(false);643 C = t;
852 var r1: Limb = undefined;644 t = B - q * D;
645 B = D;
646 D = t;
853647
854 // r1 = a + *carry648 t = xh - q * yh;
855 const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1));649 xh = yh;
650 yh = t;
651 }
856652
857 // r2 = b * c653 if (B == 0) {
858 const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));654 // t_big = x % y, r is unused
859 const r2 = @truncate(Limb, bc);655 try r.divTrunc(&t_big, x.toConst(), y.toConst());
860 const c2 = @truncate(Limb, bc >> Limb.bit_count);656 assert(t_big.isPositive());
861657
862 // r1 = r1 + r2658 x.swap(&y);
863 const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));659 y.swap(&t_big);
660 } else {
661 var storage: [8]Limb = undefined;
662 const Ap = fixedIntFromSignedDoubleLimb(A, storage[0..2]).toConst();
663 const Bp = fixedIntFromSignedDoubleLimb(B, storage[2..4]).toConst();
664 const Cp = fixedIntFromSignedDoubleLimb(C, storage[4..6]).toConst();
665 const Dp = fixedIntFromSignedDoubleLimb(D, storage[6..8]).toConst();
864666
865 // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then667 // t_big = Ax + By
866 // c2 is at least <= maxInt(Limb) - 2.668 try r.mul(x.toConst(), Ap);
867 carry.* = c1 + c2 + c3;669 try t_big.mul(y.toConst(), Bp);
670 try t_big.add(r.toConst(), t_big.toConst());
868671
869 return r1;672 // u = Cx + Dy, r as u
870 }673 try x.mul(x.toConst(), Cp);
674 try r.mul(y.toConst(), Dp);
675 try r.add(x.toConst(), r.toConst());
871676
872 fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void {677 x.swap(&t_big);
873 @setRuntimeSafety(false);678 y.swap(&r);
874 if (xi == 0) {679 }
875 return;
876 }680 }
877681
878 var carry: usize = 0;682 // euclidean algorithm
879 var a_lo = acc[0..y.len];683 assert(x.toConst().order(y.toConst()) != .lt);
880 var a_hi = acc[y.len..];
881684
882 var j: usize = 0;685 while (!y.toConst().eqZero()) {
883 while (j < a_lo.len) : (j += 1) {686 try t_big.divTrunc(&r, x.toConst(), y.toConst());
884 a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry });687 x.swap(&y);
688 y.swap(&r);
885 }689 }
886690
887 j = 0;691 result.copy(x.toConst());
888 while ((carry != 0) and (j < a_hi.len)) : (j += 1) {
889 carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));
890 }
891 }692 }
892693
893 // Knuth 4.3.1, Algorithm M.694 /// Truncates by default.
894 //695 fn div(quo: *Mutable, rem: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void {
895 // r MUST NOT alias any of a or b.696 assert(!b.eqZero()); // division by zero
896 fn llmulacc(allocator: *Allocator, r: []Limb, a: []const Limb, b: []const Limb) error{OutOfMemory}!void {697 assert(quo != rem); // illegal aliasing
897 @setRuntimeSafety(false);
898698
899 const a_norm = a[0..llnormalize(a)];699 if (a.orderAbs(b) == .lt) {
900 const b_norm = b[0..llnormalize(b)];700 // quo may alias a so handle rem first
901 var x = a_norm;701 rem.copy(a);
902 var y = b_norm;702 rem.positive = a.positive == b.positive;
903 if (a_norm.len > b_norm.len) {
904 x = b_norm;
905 y = a_norm;
906 }
907703
908 debug.assert(r.len >= x.len + y.len + 1);704 quo.positive = true;
705 quo.len = 1;
706 quo.limbs[0] = 0;
707 return;
708 }
909709
910 // 48 is a pretty abitrary size chosen based on performance of a factorial program.710 // Handle trailing zero-words of divisor/dividend. These are not handled in the following
911 if (x.len <= 48) {711 // algorithms.
912 // Basecase multiplication712 const a_zero_limb_count = blk: {
913 var i: usize = 0;713 var i: usize = 0;
914 while (i < x.len) : (i += 1) {714 while (i < a.limbs.len) : (i += 1) {
915 llmulDigit(r[i..], y, x[i]);715 if (a.limbs[i] != 0) break;
916 }716 }
917 } else {717 break :blk i;
918 // Karatsuba multiplication718 };
919 const split = @divFloor(x.len, 2);719 const b_zero_limb_count = blk: {
920 var x0 = x[0..split];720 var i: usize = 0;
921 var x1 = x[split..x.len];721 while (i < b.limbs.len) : (i += 1) {
922 var y0 = y[0..split];722 if (b.limbs[i] != 0) break;
923 var y1 = y[split..y.len];723 }
924724 break :blk i;
925 var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1);725 };
926 defer allocator.free(tmp);
927 mem.set(Limb, tmp, 0);
928
929 try llmulacc(allocator, tmp, x1, y1);
930726
931 var length = llnormalize(tmp);727 const ab_zero_limb_count = math.min(a_zero_limb_count, b_zero_limb_count);
932 _ = llaccum(r[split..], tmp[0..length]);
933 _ = llaccum(r[split * 2 ..], tmp[0..length]);
934728
935 mem.set(Limb, tmp[0..length], 0);729 if (b.limbs.len - ab_zero_limb_count == 1) {
730 lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.limbs.len], b.limbs[b.limbs.len - 1]);
731 quo.normalize(a.limbs.len - ab_zero_limb_count);
732 quo.positive = (a.positive == b.positive);
936733
937 try llmulacc(allocator, tmp, x0, y0);734 rem.len = 1;
735 rem.positive = true;
736 } else {
737 // x and y are modified during division
738 const sep_len = calcMulLimbsBufferLen(a.limbs.len, b.limbs.len, 2);
739 const x_limbs = limbs_buffer[0 * sep_len ..][0..sep_len];
740 const y_limbs = limbs_buffer[1 * sep_len ..][0..sep_len];
741 const t_limbs = limbs_buffer[2 * sep_len ..][0..sep_len];
742 const mul_limbs_buf = limbs_buffer[3 * sep_len ..][0..sep_len];
743
744 var x: Mutable = .{
745 .limbs = x_limbs,
746 .positive = a.positive,
747 .len = a.limbs.len - ab_zero_limb_count,
748 };
749 var y: Mutable = .{
750 .limbs = y_limbs,
751 .positive = b.positive,
752 .len = b.limbs.len - ab_zero_limb_count,
753 };
938754
939 length = llnormalize(tmp);755 // Shrink x, y such that the trailing zero limbs shared between are removed.
940 _ = llaccum(r[0..], tmp[0..length]);756 mem.copy(Limb, x.limbs, a.limbs[ab_zero_limb_count..a.limbs.len]);
941 _ = llaccum(r[split..], tmp[0..length]);757 mem.copy(Limb, y.limbs, b.limbs[ab_zero_limb_count..b.limbs.len]);
942758
943 const x_cmp = llcmp(x1, x0);759 divN(quo, rem, &x, &y, t_limbs, mul_limbs_buf, allocator);
944 const y_cmp = llcmp(y1, y0);760 quo.positive = (a.positive == b.positive);
945 if (x_cmp * y_cmp == 0) {761 }
946 return;
947 }
948 const x0_len = llnormalize(x0);
949 const x1_len = llnormalize(x1);
950 var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len));
951 defer allocator.free(j0);
952 if (x_cmp == 1) {
953 llsub(j0, x1[0..x1_len], x0[0..x0_len]);
954 } else {
955 llsub(j0, x0[0..x0_len], x1[0..x1_len]);
956 }
957762
958 const y0_len = llnormalize(y0);763 if (ab_zero_limb_count != 0) {
959 const y1_len = llnormalize(y1);764 rem.shiftLeft(rem.toConst(), ab_zero_limb_count * Limb.bit_count);
960 var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len));
961 defer allocator.free(j1);
962 if (y_cmp == 1) {
963 llsub(j1, y1[0..y1_len], y0[0..y0_len]);
964 } else {
965 llsub(j1, y0[0..y0_len], y1[0..y1_len]);
966 }
967 const j0_len = llnormalize(j0);
968 const j1_len = llnormalize(j1);
969 if (x_cmp == y_cmp) {
970 mem.set(Limb, tmp[0..length], 0);
971 try llmulacc(allocator, tmp, j0, j1);
972
973 length = Int.llnormalize(tmp);
974 llsub(r[split..], r[split..], tmp[0..length]);
975 } else {
976 try llmulacc(allocator, r[split..], j0, j1);
977 }
978 }765 }
979 }766 }
980767
981 // r = r + a768 /// Handbook of Applied Cryptography, 14.20
982 fn llaccum(r: []Limb, a: []const Limb) Limb {
983 @setRuntimeSafety(false);
984 debug.assert(r.len != 0 and a.len != 0);
985 debug.assert(r.len >= a.len);
986
987 var i: usize = 0;
988 var carry: Limb = 0;
989
990 while (i < a.len) : (i += 1) {
991 var c: Limb = 0;
992 c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i]));
993 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
994 carry = c;
995 }
996
997 while ((carry != 0) and i < r.len) : (i += 1) {
998 carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
999 }
1000
1001 return carry;
1002 }
1003
1004 /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs.
1005 pub fn llcmp(a: []const Limb, b: []const Limb) i8 {
1006 @setRuntimeSafety(false);
1007 const a_len = llnormalize(a);
1008 const b_len = llnormalize(b);
1009 if (a_len < b_len) {
1010 return -1;
1011 }
1012 if (a_len > b_len) {
1013 return 1;
1014 }
1015
1016 var i: usize = a_len - 1;
1017 while (i != 0) : (i -= 1) {
1018 if (a[i] != b[i]) {
1019 break;
1020 }
1021 }
1022
1023 if (a[i] < b[i]) {
1024 return -1;
1025 } else if (a[i] > b[i]) {
1026 return 1;
1027 } else {
1028 return 0;
1029 }
1030 }
1031
1032 // returns the min length the limb could be.
1033 fn llnormalize(a: []const Limb) usize {
1034 @setRuntimeSafety(false);
1035 var j = a.len;
1036 while (j > 0) : (j -= 1) {
1037 if (a[j - 1] != 0) {
1038 break;
1039 }
1040 }
1041
1042 // Handle zero
1043 return if (j != 0) j else 1;
1044 }
1045
1046 /// q = a / b (rem r)
1047 ///769 ///
1048 /// a / b are floored (rounded towards 0).770 /// x = qy + r where 0 <= r < y
1049 pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void {771 fn divN(
1050 try div(q, r, a, b);772 q: *Mutable,
1051773 r: *Mutable,
1052 // Trunc -> Floor.774 x: *Mutable,
1053 if (!q.isPositive()) {775 y: *Mutable,
1054 const one = Int.initFixed(([_]Limb{1})[0..]);776 tmp_limbs: []Limb,
1055 try q.sub(q.*, one);777 mul_limb_buf: []Limb,
1056 try r.add(q.*, one);778 allocator: ?*Allocator,
1057 }779 ) void {
1058 r.setSign(b.isPositive());780 assert(y.len >= 2);
1059 }781 assert(x.len >= y.len);
1060782 assert(q.limbs.len >= x.len + y.len - 1);
1061 /// q = a / b (rem r)783
1062 ///784 // See 3.2
1063 /// a / b are truncated (rounded towards -inf).785 var backup_tmp_limbs: [3]Limb = undefined;
1064 pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void {786 const t_limbs = if (tmp_limbs.len < 3) &backup_tmp_limbs else tmp_limbs;
1065 try div(q, r, a, b);787
1066 r.setSign(a.isPositive());788 var tmp: Mutable = .{
1067 }789 .limbs = t_limbs,
1068790 .len = 1,
1069 // Truncates by default.791 .positive = true,
1070 fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void {
1071 quo.assertWritable();
1072 rem.assertWritable();
1073
1074 if (b.eqZero()) {
1075 @panic("division by zero");
1076 }
1077 if (quo == rem) {
1078 @panic("quo and rem cannot be same variable");
1079 }
1080
1081 if (a.cmpAbs(b) == .lt) {
1082 // quo may alias a so handle rem first
1083 try rem.copy(a);
1084 rem.setSign(a.isPositive() == b.isPositive());
1085
1086 quo.metadata = 1;
1087 quo.limbs[0] = 0;
1088 return;
1089 }
1090
1091 // Handle trailing zero-words of divisor/dividend. These are not handled in the following
1092 // algorithms.
1093 const a_zero_limb_count = blk: {
1094 var i: usize = 0;
1095 while (i < a.len()) : (i += 1) {
1096 if (a.limbs[i] != 0) break;
1097 }
1098 break :blk i;
1099 };
1100 const b_zero_limb_count = blk: {
1101 var i: usize = 0;
1102 while (i < b.len()) : (i += 1) {
1103 if (b.limbs[i] != 0) break;
1104 }
1105 break :blk i;
1106 };792 };
1107793 tmp.limbs[0] = 0;
1108 const ab_zero_limb_count = std.math.min(a_zero_limb_count, b_zero_limb_count);
1109
1110 if (b.len() - ab_zero_limb_count == 1) {
1111 try quo.ensureCapacity(a.len());
1112
1113 lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.len()], b.limbs[b.len() - 1]);
1114 quo.normalize(a.len() - ab_zero_limb_count);
1115 quo.setSign(a.isPositive() == b.isPositive());
1116
1117 rem.metadata = 1;
1118 } else {
1119 // x and y are modified during division
1120 var x = try Int.initCapacity(quo.allocator.?, a.len());
1121 defer x.deinit();
1122 try x.copy(a);
1123
1124 var y = try Int.initCapacity(quo.allocator.?, b.len());
1125 defer y.deinit();
1126 try y.copy(b);
1127
1128 // x may grow one limb during normalization
1129 try quo.ensureCapacity(a.len() + y.len());
1130
1131 // Shrink x, y such that the trailing zero limbs shared between are removed.
1132 if (ab_zero_limb_count != 0) {
1133 std.mem.copy(Limb, x.limbs[0..], x.limbs[ab_zero_limb_count..]);
1134 std.mem.copy(Limb, y.limbs[0..], y.limbs[ab_zero_limb_count..]);
1135 x.metadata -= ab_zero_limb_count;
1136 y.metadata -= ab_zero_limb_count;
1137 }
1138
1139 try divN(quo.allocator.?, quo, rem, &x, &y);
1140 quo.setSign(a.isPositive() == b.isPositive());
1141 }
1142
1143 if (ab_zero_limb_count != 0) {
1144 try rem.shiftLeft(rem.*, ab_zero_limb_count * Limb.bit_count);
1145 }
1146 }
1147
1148 // Knuth 4.3.1, Exercise 16.
1149 fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
1150 @setRuntimeSafety(false);
1151 debug.assert(a.len > 1 or a[0] >= b);
1152 debug.assert(quo.len >= a.len);
1153
1154 rem.* = 0;
1155 for (a) |_, ri| {
1156 const i = a.len - ri - 1;
1157 const pdiv = ((@as(DoubleLimb, rem.*) << Limb.bit_count) | a[i]);
1158
1159 if (pdiv == 0) {
1160 quo[i] = 0;
1161 rem.* = 0;
1162 } else if (pdiv < b) {
1163 quo[i] = 0;
1164 rem.* = @truncate(Limb, pdiv);
1165 } else if (pdiv == b) {
1166 quo[i] = 1;
1167 rem.* = 0;
1168 } else {
1169 quo[i] = @truncate(Limb, @divTrunc(pdiv, b));
1170 rem.* = @truncate(Limb, pdiv - (quo[i] *% b));
1171 }
1172 }
1173 }
1174
1175 // Handbook of Applied Cryptography, 14.20
1176 //
1177 // x = qy + r where 0 <= r < y
1178 fn divN(allocator: *Allocator, q: *Int, r: *Int, x: *Int, y: *Int) !void {
1179 debug.assert(y.len() >= 2);
1180 debug.assert(x.len() >= y.len());
1181 debug.assert(q.limbs.len >= x.len() + y.len() - 1);
1182 debug.assert(default_capacity >= 3); // see 3.2
1183
1184 var tmp = try Int.init(allocator);
1185 defer tmp.deinit();
1186794
1187 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even795 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even
1188 var norm_shift = @clz(Limb, y.limbs[y.len() - 1]);796 var norm_shift = @clz(Limb, y.limbs[y.len - 1]);
1189 if (norm_shift == 0 and y.isOdd()) {797 if (norm_shift == 0 and y.toConst().isOdd()) {
1190 norm_shift = Limb.bit_count;798 norm_shift = Limb.bit_count;
1191 }799 }
1192 try x.shiftLeft(x.*, norm_shift);800 x.shiftLeft(x.toConst(), norm_shift);
1193 try y.shiftLeft(y.*, norm_shift);801 y.shiftLeft(y.toConst(), norm_shift);
1194802
1195 const n = x.len() - 1;803 const n = x.len - 1;
1196 const t = y.len() - 1;804 const t = y.len - 1;
1197805
1198 // 1.806 // 1.
1199 q.metadata = n - t + 1;807 q.len = n - t + 1;
1200 mem.set(Limb, q.limbs[0..q.len()], 0);808 q.positive = true;
809 mem.set(Limb, q.limbs[0..q.len], 0);
1201810
1202 // 2.811 // 2.
1203 try tmp.shiftLeft(y.*, Limb.bit_count * (n - t));812 tmp.shiftLeft(y.toConst(), Limb.bit_count * (n - t));
1204 while (x.cmp(tmp) != .lt) {813 while (x.toConst().order(tmp.toConst()) != .lt) {
1205 q.limbs[n - t] += 1;814 q.limbs[n - t] += 1;
1206 try x.sub(x.*, tmp);815 x.sub(x.toConst(), tmp.toConst());
1207 }816 }
1208817
1209 // 3.818 // 3.
...@@ -1232,7 +841,7 @@ pub const Int = struct {...@@ -1232,7 +841,7 @@ pub const Int = struct {
1232 r.limbs[2] = carry;841 r.limbs[2] = carry;
1233 r.normalize(3);842 r.normalize(3);
1234843
1235 if (r.cmpAbs(tmp) != .gt) {844 if (r.toConst().orderAbs(tmp.toConst()) != .gt) {
1236 break;845 break;
1237 }846 }
1238847
...@@ -1240,1748 +849,1284 @@ pub const Int = struct {...@@ -1240,1748 +849,1284 @@ pub const Int = struct {
1240 }849 }
1241850
1242 // 3.3851 // 3.3
1243 try tmp.set(q.limbs[i - t - 1]);852 tmp.set(q.limbs[i - t - 1]);
1244 try tmp.mul(tmp, y.*);853 tmp.mul(tmp.toConst(), y.toConst(), mul_limb_buf, allocator);
1245 try tmp.shiftLeft(tmp, Limb.bit_count * (i - t - 1));854 tmp.shiftLeft(tmp.toConst(), Limb.bit_count * (i - t - 1));
1246 try x.sub(x.*, tmp);855 x.sub(x.toConst(), tmp.toConst());
1247856
1248 if (!x.isPositive()) {857 if (!x.positive) {
1249 try tmp.shiftLeft(y.*, Limb.bit_count * (i - t - 1));858 tmp.shiftLeft(y.toConst(), Limb.bit_count * (i - t - 1));
1250 try x.add(x.*, tmp);859 x.add(x.toConst(), tmp.toConst());
1251 q.limbs[i - t - 1] -= 1;860 q.limbs[i - t - 1] -= 1;
1252 }861 }
1253 }862 }
1254863
1255 // Denormalize864 // Denormalize
1256 q.normalize(q.len());865 q.normalize(q.len);
1257866
1258 try r.shiftRight(x.*, norm_shift);867 r.shiftRight(x.toConst(), norm_shift);
1259 r.normalize(r.len());868 r.normalize(r.len);
1260 }869 }
1261870
1262 /// r = a << shift, in other words, r = a * 2^shift871 /// Normalize a possible sequence of leading zeros.
1263 pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void {872 ///
1264 r.assertWritable();873 /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
1265874 /// [1, 2, 0, 0, 0] -> [1, 2]
1266 try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1);875 /// [0, 0, 0, 0, 0] -> [0]
1267 llshl(r.limbs[0..], a.limbs[0..a.len()], shift);876 fn normalize(r: *Mutable, length: usize) void {
1268 r.normalize(a.len() + (shift / Limb.bit_count) + 1);877 r.len = llnormalize(r.limbs[0..length]);
1269 r.setSign(a.isPositive());
1270 }878 }
879};
1271880
1272 fn llshl(r: []Limb, a: []const Limb, shift: usize) void {881/// A arbitrary-precision big integer, with a fixed set of immutable limbs.
1273 @setRuntimeSafety(false);882pub const Const = struct {
1274 debug.assert(a.len >= 1);883 /// Raw digits. These are:
1275 debug.assert(r.len >= a.len + (shift / Limb.bit_count) + 1);884 ///
1276885 /// * Little-endian ordered
1277 const limb_shift = shift / Limb.bit_count + 1;886 /// * limbs.len >= 1
1278 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);887 /// * Zero is represented as limbs.len == 1 with limbs[0] == 0.
1279888 ///
1280 var carry: Limb = 0;889 /// Accessing limbs directly should be avoided.
1281 var i: usize = 0;890 limbs: []const Limb,
1282 while (i < a.len) : (i += 1) {891 positive: bool,
1283 const src_i = a.len - i - 1;892
1284 const dst_i = src_i + limb_shift;893 /// The result is an independent resource which is managed by the caller.
1285894 pub fn toManaged(self: Const, allocator: *Allocator) Allocator.Error!Managed {
1286 const src_digit = a[src_i];895 const limbs = try allocator.alloc(Limb, math.max(Managed.default_capacity, self.limbs.len));
1287 r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{896 mem.copy(Limb, limbs, self.limbs);
1288 Limb,897 return Managed{
1289 src_digit,898 .allocator = allocator,
1290 Limb.bit_count - @intCast(Limb, interior_limb_shift),899 .limbs = limbs,
1291 });900 .metadata = if (self.positive)
1292 carry = (src_digit << interior_limb_shift);901 self.limbs.len & ~Managed.sign_bit
1293 }902 else
1294903 self.limbs.len | Managed.sign_bit,
1295 r[limb_shift - 1] = carry;904 };
1296 mem.set(Limb, r[0 .. limb_shift - 1], 0);
1297 }905 }
1298906
1299 /// r = a >> shift907 /// Asserts `limbs` is big enough to store the value.
1300 pub fn shiftRight(r: *Int, a: Int, shift: usize) !void {908 pub fn toMutable(self: Const, limbs: []Limb) Mutable {
1301 r.assertWritable();909 mem.copy(Limb, limbs, self.limbs[0..self.limbs.len]);
910 return .{
911 .limbs = limbs,
912 .positive = self.positive,
913 .len = self.limbs.len,
914 };
915 }
1302916
1303 if (a.len() <= shift / Limb.bit_count) {917 pub fn dump(self: Const) void {
1304 r.metadata = 1;918 for (self.limbs[0..self.limbs.len]) |limb| {
1305 r.limbs[0] = 0;919 std.debug.warn("{x} ", .{limb});
1306 return;
1307 }920 }
921 std.debug.warn("positive={}\n", .{self.positive});
922 }
1308923
1309 try r.ensureCapacity(a.len() - (shift / Limb.bit_count));924 pub fn abs(self: Const) Const {
1310 const r_len = llshr(r.limbs[0..], a.limbs[0..a.len()], shift);925 return .{
1311 r.metadata = a.len() - (shift / Limb.bit_count);926 .limbs = self.limbs,
1312 r.setSign(a.isPositive());927 .positive = true,
928 };
1313 }929 }
1314930
1315 fn llshr(r: []Limb, a: []const Limb, shift: usize) void {931 pub fn isOdd(self: Const) bool {
1316 @setRuntimeSafety(false);932 return self.limbs[0] & 1 != 0;
1317 debug.assert(a.len >= 1);933 }
1318 debug.assert(r.len >= a.len - (shift / Limb.bit_count));
1319934
1320 const limb_shift = shift / Limb.bit_count;935 pub fn isEven(self: Const) bool {
1321 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);936 return !self.isOdd();
937 }
1322938
1323 var carry: Limb = 0;939 /// Returns the number of bits required to represent the absolute value of an integer.
1324 var i: usize = 0;940 pub fn bitCountAbs(self: Const) usize {
1325 while (i < a.len - limb_shift) : (i += 1) {941 return (self.limbs.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(Limb, self.limbs[self.limbs.len - 1]));
1326 const src_i = a.len - i - 1;
1327 const dst_i = src_i - limb_shift;
1328
1329 const src_digit = a[src_i];
1330 r[dst_i] = carry | (src_digit >> interior_limb_shift);
1331 carry = @call(.{ .modifier = .always_inline }, math.shl, .{
1332 Limb,
1333 src_digit,
1334 Limb.bit_count - @intCast(Limb, interior_limb_shift),
1335 });
1336 }
1337 }942 }
1338943
1339 /// r = a | b944 /// Returns the number of bits required to represent the integer in twos-complement form.
1340 ///945 ///
1341 /// a and b are zero-extended to the longer of a or b.946 /// If the integer is negative the value returned is the number of bits needed by a signed
1342 pub fn bitOr(r: *Int, a: Int, b: Int) !void {947 /// integer to represent the value. If positive the value is the number of bits for an
1343 r.assertWritable();948 /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
949 /// one greater than the returned value.
950 ///
951 /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
952 pub fn bitCountTwosComp(self: Const) usize {
953 var bits = self.bitCountAbs();
1344954
1345 if (a.len() > b.len()) {955 // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
1346 try r.ensureCapacity(a.len());956 // complement requires one less bit.
1347 llor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);957 if (!self.positive) block: {
1348 r.setLen(a.len());958 bits += 1;
1349 } else {959
1350 try r.ensureCapacity(b.len());960 if (@popCount(Limb, self.limbs[self.limbs.len - 1]) == 1) {
1351 llor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);961 for (self.limbs[0 .. self.limbs.len - 1]) |limb| {
1352 r.setLen(b.len());962 if (@popCount(Limb, limb) != 0) {
963 break :block;
964 }
965 }
966
967 bits -= 1;
968 }
1353 }969 }
1354 }
1355970
1356 fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {971 return bits;
1357 @setRuntimeSafety(false);972 }
1358 debug.assert(r.len >= a.len);
1359 debug.assert(a.len >= b.len);
1360973
1361 var i: usize = 0;974 pub fn fitsInTwosComp(self: Const, is_signed: bool, bit_count: usize) bool {
1362 while (i < b.len) : (i += 1) {975 if (self.eqZero()) {
1363 r[i] = a[i] | b[i];976 return true;
1364 }977 }
1365 while (i < a.len) : (i += 1) {978 if (!is_signed and !self.positive) {
1366 r[i] = a[i];979 return false;
1367 }980 }
981
982 const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed);
983 return bit_count >= req_bits;
1368 }984 }
1369985
1370 /// r = a & b986 /// Returns whether self can fit into an integer of the requested type.
1371 pub fn bitAnd(r: *Int, a: Int, b: Int) !void {987 pub fn fits(self: Const, comptime T: type) bool {
1372 r.assertWritable();988 const info = @typeInfo(T).Int;
989 return self.fitsInTwosComp(info.is_signed, info.bits);
990 }
1373991
1374 if (a.len() > b.len()) {992 /// Returns the approximate size of the integer in the given base. Negative values accommodate for
1375 try r.ensureCapacity(b.len());993 /// the minus sign. This is used for determining the number of characters needed to print the
1376 lland(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);994 /// value. It is inexact and may exceed the given value by ~1-2 bytes.
1377 r.normalize(b.len());995 /// TODO See if we can make this exact.
1378 } else {996 pub fn sizeInBaseUpperBound(self: Const, base: usize) usize {
1379 try r.ensureCapacity(a.len());997 const bit_count = @as(usize, @boolToInt(!self.positive)) + self.bitCountAbs();
1380 lland(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);998 return (bit_count / math.log2(base)) + 1;
1381 r.normalize(a.len());
1382 }
1383 }999 }
13841000
1385 fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {1001 pub const ConvertError = error{
1386 @setRuntimeSafety(false);1002 NegativeIntoUnsigned,
1387 debug.assert(r.len >= b.len);1003 TargetTooSmall,
1388 debug.assert(a.len >= b.len);1004 };
13891005
1390 var i: usize = 0;1006 /// Convert self to type T.
1391 while (i < b.len) : (i += 1) {1007 ///
1392 r[i] = a[i] & b[i];1008 /// Returns an error if self cannot be narrowed into the requested type without truncation.
1009 pub fn to(self: Const, comptime T: type) ConvertError!T {
1010 switch (@typeInfo(T)) {
1011 .Int => {
1012 const UT = std.meta.IntType(false, T.bit_count);
1013
1014 if (self.bitCountTwosComp() > T.bit_count) {
1015 return error.TargetTooSmall;
1016 }
1017
1018 var r: UT = 0;
1019
1020 if (@sizeOf(UT) <= @sizeOf(Limb)) {
1021 r = @intCast(UT, self.limbs[0]);
1022 } else {
1023 for (self.limbs[0..self.limbs.len]) |_, ri| {
1024 const limb = self.limbs[self.limbs.len - ri - 1];
1025 r <<= Limb.bit_count;
1026 r |= limb;
1027 }
1028 }
1029
1030 if (!T.is_signed) {
1031 return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;
1032 } else {
1033 if (self.positive) {
1034 return @intCast(T, r);
1035 } else {
1036 if (math.cast(T, r)) |ok| {
1037 return -ok;
1038 } else |_| {
1039 return minInt(T);
1040 }
1041 }
1042 }
1043 },
1044 else => @compileError("cannot convert Const to type " ++ @typeName(T)),
1393 }1045 }
1394 }1046 }
13951047
1396 /// r = a ^ b1048 /// To allow `std.fmt.format` to work with this type.
1397 pub fn bitXor(r: *Int, a: Int, b: Int) !void {1049 /// If the integer is larger than `pow(2, 64 * @sizeOf(usize) * 8), this function will fail
1398 r.assertWritable();1050 /// to print the string, printing "(BigInt)" instead of a number.
1051 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
1052 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
1053 pub fn format(
1054 self: Const,
1055 comptime fmt: []const u8,
1056 options: std.fmt.FormatOptions,
1057 out_stream: var,
1058 ) !void {
1059 comptime var radix = 10;
1060 comptime var uppercase = false;
13991061
1400 if (a.len() > b.len()) {1062 if (fmt.len == 0 or comptime mem.eql(u8, fmt, "d")) {
1401 try r.ensureCapacity(a.len());1063 radix = 10;
1402 llxor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);1064 uppercase = false;
1403 r.normalize(a.len());1065 } else if (comptime mem.eql(u8, fmt, "b")) {
1066 radix = 2;
1067 uppercase = false;
1068 } else if (comptime mem.eql(u8, fmt, "x")) {
1069 radix = 16;
1070 uppercase = false;
1071 } else if (comptime mem.eql(u8, fmt, "X")) {
1072 radix = 16;
1073 uppercase = true;
1404 } else {1074 } else {
1405 try r.ensureCapacity(b.len());1075 @compileError("Unknown format string: '" ++ fmt ++ "'");
1406 llxor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
1407 r.normalize(b.len());
1408 }1076 }
1409 }
14101077
1411 fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {1078 var limbs: [128]Limb = undefined;
1412 @setRuntimeSafety(false);1079 const needed_limbs = calcDivLimbsBufferLen(self.limbs.len, 1);
1413 debug.assert(r.len >= a.len);1080 if (needed_limbs > limbs.len)
1414 debug.assert(a.len >= b.len);1081 return out_stream.writeAll("(BigInt)");
14151082
1416 var i: usize = 0;1083 // This is the inverse of calcDivLimbsBufferLen
1417 while (i < b.len) : (i += 1) {1084 const available_len = (limbs.len / 3) - 2;
1418 r[i] = a[i] ^ b[i];1085
1419 }1086 const biggest: Const = .{
1420 while (i < a.len) : (i += 1) {1087 .limbs = &([1]Limb{math.maxInt(Limb)} ** available_len),
1421 r[i] = a[i];1088 .positive = false,
1422 }1089 };
1090 var buf: [biggest.sizeInBaseUpperBound(radix)]u8 = undefined;
1091 const len = self.toString(&buf, radix, uppercase, &limbs);
1092 return out_stream.writeAll(buf[0..len]);
1423 }1093 }
14241094
1425 pub fn gcd(rma: *Int, x: Int, y: Int) !void {1095 /// Converts self to a string in the requested base.
1426 rma.assertWritable();1096 /// Caller owns returned memory.
1427 var r = rma;1097 /// Asserts that `base` is in the range [2, 16].
1428 var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr;1098 /// See also `toString`, a lower level function than this.
1099 pub fn toStringAlloc(self: Const, allocator: *Allocator, base: u8, uppercase: bool) Allocator.Error![]u8 {
1100 assert(base >= 2);
1101 assert(base <= 16);
14291102
1430 var sr: Int = undefined;1103 if (self.eqZero()) {
1431 if (aliased) {1104 return mem.dupe(allocator, u8, "0");
1432 sr = try Int.initCapacity(rma.allocator.?, math.max(x.len(), y.len()));
1433 r = &sr;
1434 aliased = true;
1435 }1105 }
1436 defer if (aliased) {1106 const string = try allocator.alloc(u8, self.sizeInBaseUpperBound(base));
1437 rma.swap(r);1107 errdefer allocator.free(string);
1438 r.deinit();
1439 };
14401108
1441 try gcdLehmer(r, x, y);1109 const limbs = try allocator.alloc(Limb, calcToStringLimbsBufferLen(self.limbs.len, base));
1442 }1110 defer allocator.free(limbs);
14431111
1444 fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void {1112 return allocator.shrink(string, self.toString(string, base, uppercase, limbs));
1445 var x = try xa.clone();1113 }
1446 x.abs();
1447 defer x.deinit();
14481114
1449 var y = try ya.clone();1115 /// Converts self to a string in the requested base.
1450 y.abs();1116 /// Asserts that `base` is in the range [2, 16].
1451 defer y.deinit();1117 /// `string` is a caller-provided slice of at least `sizeInBaseUpperBound` bytes,
1118 /// where the result is written to.
1119 /// Returns the length of the string.
1120 /// `limbs_buffer` is caller-provided memory for `toString` to use as a working area. It must have
1121 /// length of at least `calcToStringLimbsBufferLen`.
1122 /// In the case of power-of-two base, `limbs_buffer` is ignored.
1123 /// See also `toStringAlloc`, a higher level function than this.
1124 pub fn toString(self: Const, string: []u8, base: u8, uppercase: bool, limbs_buffer: []Limb) usize {
1125 assert(base >= 2);
1126 assert(base <= 16);
14521127
1453 if (x.cmp(y) == .lt) {1128 if (self.eqZero()) {
1454 x.swap(&y);1129 string[0] = '0';
1130 return 1;
1455 }1131 }
14561132
1457 var T = try Int.init(r.allocator.?);1133 var digits_len: usize = 0;
1458 defer T.deinit();
1459
1460 while (y.len() > 1) {
1461 debug.assert(x.isPositive() and y.isPositive());
1462 debug.assert(x.len() >= y.len());
14631134
1464 var xh: SignedDoubleLimb = x.limbs[x.len() - 1];1135 // Power of two: can do a single pass and use masks to extract digits.
1465 var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1];1136 if (math.isPowerOfTwo(base)) {
1137 const base_shift = math.log2_int(Limb, base);
14661138
1467 var A: SignedDoubleLimb = 1;1139 outer: for (self.limbs[0..self.limbs.len]) |limb| {
1468 var B: SignedDoubleLimb = 0;1140 var shift: usize = 0;
1469 var C: SignedDoubleLimb = 0;1141 while (shift < Limb.bit_count) : (shift += base_shift) {
1470 var D: SignedDoubleLimb = 1;1142 const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1));
1143 const ch = std.fmt.digitToChar(r, uppercase);
1144 string[digits_len] = ch;
1145 digits_len += 1;
1146 // If we hit the end, it must be all zeroes from here.
1147 if (digits_len == string.len) break :outer;
1148 }
1149 }
14711150
1472 while (yh + C != 0 and yh + D != 0) {1151 // Always will have a non-zero digit somewhere.
1473 const q = @divFloor(xh + A, yh + C);1152 while (string[digits_len - 1] == '0') {
1474 const qp = @divFloor(xh + B, yh + D);1153 digits_len -= 1;
1475 if (q != qp) {1154 }
1476 break;1155 } else {
1477 }1156 // Non power-of-two: batch divisions per word size.
1157 const digits_per_limb = math.log(Limb, base, maxInt(Limb));
1158 var limb_base: Limb = 1;
1159 var j: usize = 0;
1160 while (j < digits_per_limb) : (j += 1) {
1161 limb_base *= base;
1162 }
1163 const b: Const = .{ .limbs = &[_]Limb{limb_base}, .positive = true };
14781164
1479 var t = A - q * C;1165 var q: Mutable = .{
1480 A = C;1166 .limbs = limbs_buffer[0 .. self.limbs.len + 2],
1481 C = t;1167 .positive = true, // Make absolute by ignoring self.positive.
1482 t = B - q * D;1168 .len = self.limbs.len,
1483 B = D;1169 };
1484 D = t;1170 mem.copy(Limb, q.limbs, self.limbs);
14851171
1486 t = xh - q * yh;1172 var r: Mutable = .{
1487 xh = yh;1173 .limbs = limbs_buffer[q.limbs.len..][0..self.limbs.len],
1488 yh = t;1174 .positive = true,
1489 }1175 .len = 1,
1176 };
1177 r.limbs[0] = 0;
14901178
1491 if (B == 0) {1179 const rest_of_the_limbs_buf = limbs_buffer[q.limbs.len + r.limbs.len ..];
1492 // T = x % y, r is unused
1493 try Int.divTrunc(r, &T, x, y);
1494 debug.assert(T.isPositive());
14951180
1496 x.swap(&y);1181 while (q.len >= 2) {
1497 y.swap(&T);1182 // Passing an allocator here would not be helpful since this division is destroying
1498 } else {1183 // information, not creating it. [TODO citation needed]
1499 var storage: [8]Limb = undefined;1184 q.divTrunc(&r, q.toConst(), b, rest_of_the_limbs_buf, null);
1500 const Ap = FixedIntFromSignedDoubleLimb(A, storage[0..2]);
1501 const Bp = FixedIntFromSignedDoubleLimb(B, storage[2..4]);
1502 const Cp = FixedIntFromSignedDoubleLimb(C, storage[4..6]);
1503 const Dp = FixedIntFromSignedDoubleLimb(D, storage[6..8]);
15041185
1505 // T = Ax + By1186 var r_word = r.limbs[0];
1506 try r.mul(x, Ap);1187 var i: usize = 0;
1507 try T.mul(y, Bp);1188 while (i < digits_per_limb) : (i += 1) {
1508 try T.add(r.*, T);1189 const ch = std.fmt.digitToChar(@intCast(u8, r_word % base), uppercase);
1190 r_word /= base;
1191 string[digits_len] = ch;
1192 digits_len += 1;
1193 }
1194 }
15091195
1510 // u = Cx + Dy, r as u1196 {
1511 try x.mul(x, Cp);1197 assert(q.len == 1);
1512 try r.mul(y, Dp);
1513 try r.add(x, r.*);
15141198
1515 x.swap(&T);1199 var r_word = q.limbs[0];
1516 y.swap(r);1200 while (r_word != 0) {
1201 const ch = std.fmt.digitToChar(@intCast(u8, r_word % base), uppercase);
1202 r_word /= base;
1203 string[digits_len] = ch;
1204 digits_len += 1;
1205 }
1517 }1206 }
1518 }1207 }
15191208
1520 // euclidean algorithm1209 if (!self.positive) {
1521 debug.assert(x.cmp(y) != .lt);1210 string[digits_len] = '-';
15221211 digits_len += 1;
1523 while (!y.eqZero()) {
1524 try Int.divTrunc(&T, r, x, y);
1525 x.swap(&y);
1526 y.swap(r);
1527 }1212 }
15281213
1529 r.swap(&x);1214 const s = string[0..digits_len];
1215 mem.reverse(u8, s);
1216 return s.len;
1530 }1217 }
1531};
15321218
1533// Storage must live for the lifetime of the returned value1219 /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if
1534fn FixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Int {1220 /// `|a| < |b|`, `|a| == |b|`, or `|a| > |b|` respectively.
1535 std.debug.assert(storage.len >= 2);1221 pub fn orderAbs(a: Const, b: Const) math.Order {
15361222 if (a.limbs.len < b.limbs.len) {
1537 var A_is_positive = A >= 0;1223 return .lt;
1538 const Au = @intCast(DoubleLimb, if (A < 0) -A else A);1224 }
1539 storage[0] = @truncate(Limb, Au);1225 if (a.limbs.len > b.limbs.len) {
1540 storage[1] = @truncate(Limb, Au >> Limb.bit_count);1226 return .gt;
1541 var Ap = Int.initFixed(storage[0..2]);1227 }
1542 Ap.setSign(A_is_positive);
1543 return Ap;
1544}
1545
1546// NOTE: All the following tests assume the max machine-word will be 64-bit.
1547//
1548// They will still run on larger than this and should pass, but the multi-limb code-paths
1549// may be untested in some cases.
1550
1551test "big.int comptime_int set" {
1552 comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab;
1553 var a = try Int.initSet(testing.allocator, s);
1554 defer a.deinit();
15551228
1556 const s_limb_count = 128 / Limb.bit_count;1229 var i: usize = a.limbs.len - 1;
1230 while (i != 0) : (i -= 1) {
1231 if (a.limbs[i] != b.limbs[i]) {
1232 break;
1233 }
1234 }
15571235
1558 comptime var i: usize = 0;1236 if (a.limbs[i] < b.limbs[i]) {
1559 inline while (i < s_limb_count) : (i += 1) {1237 return .lt;
1560 const result = @as(Limb, s & maxInt(Limb));1238 } else if (a.limbs[i] > b.limbs[i]) {
1561 s >>= Limb.bit_count / 2;1239 return .gt;
1562 s >>= Limb.bit_count / 2;1240 } else {
1563 testing.expect(a.limbs[i] == result);1241 return .eq;
1242 }
1564 }1243 }
1565}
1566
1567test "big.int comptime_int set negative" {
1568 var a = try Int.initSet(testing.allocator, -10);
1569 defer a.deinit();
1570
1571 testing.expect(a.limbs[0] == 10);
1572 testing.expect(a.isPositive() == false);
1573}
1574
1575test "big.int int set unaligned small" {
1576 var a = try Int.initSet(testing.allocator, @as(u7, 45));
1577 defer a.deinit();
1578
1579 testing.expect(a.limbs[0] == 45);
1580 testing.expect(a.isPositive() == true);
1581}
1582
1583test "big.int comptime_int to" {
1584 const a = try Int.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab);
1585 defer a.deinit();
1586
1587 testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab);
1588}
1589
1590test "big.int sub-limb to" {
1591 const a = try Int.initSet(testing.allocator, 10);
1592 defer a.deinit();
1593
1594 testing.expect((try a.to(u8)) == 10);
1595}
1596
1597test "big.int to target too small error" {
1598 const a = try Int.initSet(testing.allocator, 0xffffffff);
1599 defer a.deinit();
1600
1601 testing.expectError(error.TargetTooSmall, a.to(u8));
1602}
1603
1604test "big.int normalize" {
1605 var a = try Int.init(testing.allocator);
1606 defer a.deinit();
1607 try a.ensureCapacity(8);
1608
1609 a.limbs[0] = 1;
1610 a.limbs[1] = 2;
1611 a.limbs[2] = 3;
1612 a.limbs[3] = 0;
1613 a.normalize(4);
1614 testing.expect(a.len() == 3);
1615
1616 a.limbs[0] = 1;
1617 a.limbs[1] = 2;
1618 a.limbs[2] = 3;
1619 a.normalize(3);
1620 testing.expect(a.len() == 3);
1621
1622 a.limbs[0] = 0;
1623 a.limbs[1] = 0;
1624 a.normalize(2);
1625 testing.expect(a.len() == 1);
1626
1627 a.limbs[0] = 0;
1628 a.normalize(1);
1629 testing.expect(a.len() == 1);
1630}
1631
1632test "big.int normalize multi" {
1633 var a = try Int.init(testing.allocator);
1634 defer a.deinit();
1635 try a.ensureCapacity(8);
1636
1637 a.limbs[0] = 1;
1638 a.limbs[1] = 2;
1639 a.limbs[2] = 0;
1640 a.limbs[3] = 0;
1641 a.normalize(4);
1642 testing.expect(a.len() == 2);
1643
1644 a.limbs[0] = 1;
1645 a.limbs[1] = 2;
1646 a.limbs[2] = 3;
1647 a.normalize(3);
1648 testing.expect(a.len() == 3);
1649
1650 a.limbs[0] = 0;
1651 a.limbs[1] = 0;
1652 a.limbs[2] = 0;
1653 a.limbs[3] = 0;
1654 a.normalize(4);
1655 testing.expect(a.len() == 1);
1656
1657 a.limbs[0] = 0;
1658 a.normalize(1);
1659 testing.expect(a.len() == 1);
1660}
1661
1662test "big.int parity" {
1663 var a = try Int.init(testing.allocator);
1664 defer a.deinit();
1665
1666 try a.set(0);
1667 testing.expect(a.isEven());
1668 testing.expect(!a.isOdd());
1669
1670 try a.set(7);
1671 testing.expect(!a.isEven());
1672 testing.expect(a.isOdd());
1673}
1674
1675test "big.int bitcount + sizeInBase" {
1676 var a = try Int.init(testing.allocator);
1677 defer a.deinit();
1678
1679 try a.set(0b100);
1680 testing.expect(a.bitCountAbs() == 3);
1681 testing.expect(a.sizeInBase(2) >= 3);
1682 testing.expect(a.sizeInBase(10) >= 1);
1683
1684 a.negate();
1685 testing.expect(a.bitCountAbs() == 3);
1686 testing.expect(a.sizeInBase(2) >= 4);
1687 testing.expect(a.sizeInBase(10) >= 2);
1688
1689 try a.set(0xffffffff);
1690 testing.expect(a.bitCountAbs() == 32);
1691 testing.expect(a.sizeInBase(2) >= 32);
1692 testing.expect(a.sizeInBase(10) >= 10);
1693
1694 try a.shiftLeft(a, 5000);
1695 testing.expect(a.bitCountAbs() == 5032);
1696 testing.expect(a.sizeInBase(2) >= 5032);
1697 a.setSign(false);
1698
1699 testing.expect(a.bitCountAbs() == 5032);
1700 testing.expect(a.sizeInBase(2) >= 5033);
1701}
1702
1703test "big.int bitcount/to" {
1704 var a = try Int.init(testing.allocator);
1705 defer a.deinit();
1706
1707 try a.set(0);
1708 testing.expect(a.bitCountTwosComp() == 0);
1709
1710 testing.expect((try a.to(u0)) == 0);
1711 testing.expect((try a.to(i0)) == 0);
1712
1713 try a.set(-1);
1714 testing.expect(a.bitCountTwosComp() == 1);
1715 testing.expect((try a.to(i1)) == -1);
1716
1717 try a.set(-8);
1718 testing.expect(a.bitCountTwosComp() == 4);
1719 testing.expect((try a.to(i4)) == -8);
1720
1721 try a.set(127);
1722 testing.expect(a.bitCountTwosComp() == 7);
1723 testing.expect((try a.to(u7)) == 127);
1724
1725 try a.set(-128);
1726 testing.expect(a.bitCountTwosComp() == 8);
1727 testing.expect((try a.to(i8)) == -128);
1728
1729 try a.set(-129);
1730 testing.expect(a.bitCountTwosComp() == 9);
1731 testing.expect((try a.to(i9)) == -129);
1732}
1733
1734test "big.int fits" {
1735 var a = try Int.init(testing.allocator);
1736 defer a.deinit();
1737
1738 try a.set(0);
1739 testing.expect(a.fits(u0));
1740 testing.expect(a.fits(i0));
1741
1742 try a.set(255);
1743 testing.expect(!a.fits(u0));
1744 testing.expect(!a.fits(u1));
1745 testing.expect(!a.fits(i8));
1746 testing.expect(a.fits(u8));
1747 testing.expect(a.fits(u9));
1748 testing.expect(a.fits(i9));
1749
1750 try a.set(-128);
1751 testing.expect(!a.fits(i7));
1752 testing.expect(a.fits(i8));
1753 testing.expect(a.fits(i9));
1754 testing.expect(!a.fits(u9));
1755
1756 try a.set(0x1ffffffffeeeeeeee);
1757 testing.expect(!a.fits(u32));
1758 testing.expect(!a.fits(u64));
1759 testing.expect(a.fits(u65));
1760}
1761
1762test "big.int string set" {
1763 var a = try Int.init(testing.allocator);
1764 defer a.deinit();
1765
1766 try a.setString(10, "120317241209124781241290847124");
1767 testing.expect((try a.to(u128)) == 120317241209124781241290847124);
1768}
1769
1770test "big.int string negative" {
1771 var a = try Int.init(testing.allocator);
1772 defer a.deinit();
1773
1774 try a.setString(10, "-1023");
1775 testing.expect((try a.to(i32)) == -1023);
1776}
1777
1778test "big.int string set number with underscores" {
1779 var a = try Int.init(testing.allocator);
1780 defer a.deinit();
1781
1782 try a.setString(10, "__1_2_0_3_1_7_2_4_1_2_0_____9_1__2__4_7_8_1_2_4_1_2_9_0_8_4_7_1_2_4___");
1783 testing.expect((try a.to(u128)) == 120317241209124781241290847124);
1784}
1785
1786test "big.int string set case insensitive number" {
1787 var a = try Int.init(testing.allocator);
1788 defer a.deinit();
1789
1790 try a.setString(16, "aB_cD_eF");
1791 testing.expect((try a.to(u32)) == 0xabcdef);
1792}
1793
1794test "big.int string set bad char error" {
1795 var a = try Int.init(testing.allocator);
1796 defer a.deinit();
1797 testing.expectError(error.InvalidCharForDigit, a.setString(10, "x"));
1798}
1799
1800test "big.int string set bad base error" {
1801 var a = try Int.init(testing.allocator);
1802 defer a.deinit();
1803 testing.expectError(error.InvalidBase, a.setString(45, "10"));
1804}
1805
1806test "big.int string to" {
1807 const a = try Int.initSet(testing.allocator, 120317241209124781241290847124);
1808 defer a.deinit();
1809
1810 const as = try a.toString(testing.allocator, 10, false);
1811 defer testing.allocator.free(as);
1812 const es = "120317241209124781241290847124";
1813
1814 testing.expect(mem.eql(u8, as, es));
1815}
1816
1817test "big.int string to base base error" {
1818 const a = try Int.initSet(testing.allocator, 0xffffffff);
1819 defer a.deinit();
1820
1821 testing.expectError(error.InvalidBase, a.toString(testing.allocator, 45, false));
1822}
1823
1824test "big.int string to base 2" {
1825 const a = try Int.initSet(testing.allocator, -0b1011);
1826 defer a.deinit();
1827
1828 const as = try a.toString(testing.allocator, 2, false);
1829 defer testing.allocator.free(as);
1830 const es = "-1011";
1831
1832 testing.expect(mem.eql(u8, as, es));
1833}
1834
1835test "big.int string to base 16" {
1836 const a = try Int.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab);
1837 defer a.deinit();
1838
1839 const as = try a.toString(testing.allocator, 16, false);
1840 defer testing.allocator.free(as);
1841 const es = "efffffff00000001eeeeeeefaaaaaaab";
1842
1843 testing.expect(mem.eql(u8, as, es));
1844}
1845
1846test "big.int neg string to" {
1847 const a = try Int.initSet(testing.allocator, -123907434);
1848 defer a.deinit();
1849
1850 const as = try a.toString(testing.allocator, 10, false);
1851 defer testing.allocator.free(as);
1852 const es = "-123907434";
1853
1854 testing.expect(mem.eql(u8, as, es));
1855}
1856
1857test "big.int zero string to" {
1858 const a = try Int.initSet(testing.allocator, 0);
1859 defer a.deinit();
1860
1861 const as = try a.toString(testing.allocator, 10, false);
1862 defer testing.allocator.free(as);
1863 const es = "0";
1864
1865 testing.expect(mem.eql(u8, as, es));
1866}
1867
1868test "big.int clone" {
1869 var a = try Int.initSet(testing.allocator, 1234);
1870 defer a.deinit();
1871 const b = try a.clone();
1872 defer b.deinit();
1873
1874 testing.expect((try a.to(u32)) == 1234);
1875 testing.expect((try b.to(u32)) == 1234);
1876
1877 try a.set(77);
1878 testing.expect((try a.to(u32)) == 77);
1879 testing.expect((try b.to(u32)) == 1234);
1880}
1881
1882test "big.int swap" {
1883 var a = try Int.initSet(testing.allocator, 1234);
1884 defer a.deinit();
1885 var b = try Int.initSet(testing.allocator, 5678);
1886 defer b.deinit();
1887
1888 testing.expect((try a.to(u32)) == 1234);
1889 testing.expect((try b.to(u32)) == 5678);
1890
1891 a.swap(&b);
1892
1893 testing.expect((try a.to(u32)) == 5678);
1894 testing.expect((try b.to(u32)) == 1234);
1895}
1896
1897test "big.int to negative" {
1898 var a = try Int.initSet(testing.allocator, -10);
1899 defer a.deinit();
1900
1901 testing.expect((try a.to(i32)) == -10);
1902}
1903
1904test "big.int compare" {
1905 var a = try Int.initSet(testing.allocator, -11);
1906 defer a.deinit();
1907 var b = try Int.initSet(testing.allocator, 10);
1908 defer b.deinit();
19091244
1910 testing.expect(a.cmpAbs(b) == .gt);1245 /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if `a < b`, `a == b` or `a > b` respectively.
1911 testing.expect(a.cmp(b) == .lt);1246 pub fn order(a: Const, b: Const) math.Order {
1912}1247 if (a.positive != b.positive) {
19131248 return if (a.positive) .gt else .lt;
1914test "big.int compare similar" {1249 } else {
1915 var a = try Int.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeee);1250 const r = orderAbs(a, b);
1916 defer a.deinit();1251 return if (a.positive) r else switch (r) {
1917 var b = try Int.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeef);1252 .lt => math.Order.gt,
1918 defer b.deinit();1253 .eq => math.Order.eq,
19191254 .gt => math.Order.lt,
1920 testing.expect(a.cmpAbs(b) == .lt);1255 };
1921 testing.expect(b.cmpAbs(a) == .gt);1256 }
1922}1257 }
1923
1924test "big.int compare different limb size" {
1925 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);
1926 defer a.deinit();
1927 var b = try Int.initSet(testing.allocator, 1);
1928 defer b.deinit();
1929
1930 testing.expect(a.cmpAbs(b) == .gt);
1931 testing.expect(b.cmpAbs(a) == .lt);
1932}
1933
1934test "big.int compare multi-limb" {
1935 var a = try Int.initSet(testing.allocator, -0x7777777799999999ffffeeeeffffeeeeffffeeeef);
1936 defer a.deinit();
1937 var b = try Int.initSet(testing.allocator, 0x7777777799999999ffffeeeeffffeeeeffffeeeee);
1938 defer b.deinit();
1939
1940 testing.expect(a.cmpAbs(b) == .gt);
1941 testing.expect(a.cmp(b) == .lt);
1942}
1943
1944test "big.int equality" {
1945 var a = try Int.initSet(testing.allocator, 0xffffffff1);
1946 defer a.deinit();
1947 var b = try Int.initSet(testing.allocator, -0xffffffff1);
1948 defer b.deinit();
1949
1950 testing.expect(a.eqAbs(b));
1951 testing.expect(!a.eq(b));
1952}
1953
1954test "big.int abs" {
1955 var a = try Int.initSet(testing.allocator, -5);
1956 defer a.deinit();
1957
1958 a.abs();
1959 testing.expect((try a.to(u32)) == 5);
1960
1961 a.abs();
1962 testing.expect((try a.to(u32)) == 5);
1963}
1964
1965test "big.int negate" {
1966 var a = try Int.initSet(testing.allocator, 5);
1967 defer a.deinit();
1968
1969 a.negate();
1970 testing.expect((try a.to(i32)) == -5);
1971
1972 a.negate();
1973 testing.expect((try a.to(i32)) == 5);
1974}
1975
1976test "big.int add single-single" {
1977 var a = try Int.initSet(testing.allocator, 50);
1978 defer a.deinit();
1979 var b = try Int.initSet(testing.allocator, 5);
1980 defer b.deinit();
1981
1982 var c = try Int.init(testing.allocator);
1983 defer c.deinit();
1984 try c.add(a, b);
1985
1986 testing.expect((try c.to(u32)) == 55);
1987}
1988
1989test "big.int add multi-single" {
1990 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);
1991 defer a.deinit();
1992 var b = try Int.initSet(testing.allocator, 1);
1993 defer b.deinit();
1994
1995 var c = try Int.init(testing.allocator);
1996 defer c.deinit();
1997
1998 try c.add(a, b);
1999 testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
2000
2001 try c.add(b, a);
2002 testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
2003}
2004
2005test "big.int add multi-multi" {
2006 const op1 = 0xefefefef7f7f7f7f;
2007 const op2 = 0xfefefefe9f9f9f9f;
2008 var a = try Int.initSet(testing.allocator, op1);
2009 defer a.deinit();
2010 var b = try Int.initSet(testing.allocator, op2);
2011 defer b.deinit();
2012
2013 var c = try Int.init(testing.allocator);
2014 defer c.deinit();
2015 try c.add(a, b);
2016
2017 testing.expect((try c.to(u128)) == op1 + op2);
2018}
2019
2020test "big.int add zero-zero" {
2021 var a = try Int.initSet(testing.allocator, 0);
2022 defer a.deinit();
2023 var b = try Int.initSet(testing.allocator, 0);
2024 defer b.deinit();
2025
2026 var c = try Int.init(testing.allocator);
2027 defer c.deinit();
2028 try c.add(a, b);
2029
2030 testing.expect((try c.to(u32)) == 0);
2031}
2032
2033test "big.int add alias multi-limb nonzero-zero" {
2034 const op1 = 0xffffffff777777771;
2035 var a = try Int.initSet(testing.allocator, op1);
2036 defer a.deinit();
2037 var b = try Int.initSet(testing.allocator, 0);
2038 defer b.deinit();
2039
2040 try a.add(a, b);
2041
2042 testing.expect((try a.to(u128)) == op1);
2043}
2044
2045test "big.int add sign" {
2046 var a = try Int.init(testing.allocator);
2047 defer a.deinit();
2048
2049 const one = try Int.initSet(testing.allocator, 1);
2050 defer one.deinit();
2051 const two = try Int.initSet(testing.allocator, 2);
2052 defer two.deinit();
2053 const neg_one = try Int.initSet(testing.allocator, -1);
2054 defer neg_one.deinit();
2055 const neg_two = try Int.initSet(testing.allocator, -2);
2056 defer neg_two.deinit();
2057
2058 try a.add(one, two);
2059 testing.expect((try a.to(i32)) == 3);
2060
2061 try a.add(neg_one, two);
2062 testing.expect((try a.to(i32)) == 1);
2063
2064 try a.add(one, neg_two);
2065 testing.expect((try a.to(i32)) == -1);
2066
2067 try a.add(neg_one, neg_two);
2068 testing.expect((try a.to(i32)) == -3);
2069}
2070
2071test "big.int sub single-single" {
2072 var a = try Int.initSet(testing.allocator, 50);
2073 defer a.deinit();
2074 var b = try Int.initSet(testing.allocator, 5);
2075 defer b.deinit();
2076
2077 var c = try Int.init(testing.allocator);
2078 defer c.deinit();
2079 try c.sub(a, b);
2080
2081 testing.expect((try c.to(u32)) == 45);
2082}
2083
2084test "big.int sub multi-single" {
2085 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);
2086 defer a.deinit();
2087 var b = try Int.initSet(testing.allocator, 1);
2088 defer b.deinit();
2089
2090 var c = try Int.init(testing.allocator);
2091 defer c.deinit();
2092 try c.sub(a, b);
2093
2094 testing.expect((try c.to(Limb)) == maxInt(Limb));
2095}
2096
2097test "big.int sub multi-multi" {
2098 const op1 = 0xefefefefefefefefefefefef;
2099 const op2 = 0xabababababababababababab;
2100
2101 var a = try Int.initSet(testing.allocator, op1);
2102 defer a.deinit();
2103 var b = try Int.initSet(testing.allocator, op2);
2104 defer b.deinit();
2105
2106 var c = try Int.init(testing.allocator);
2107 defer c.deinit();
2108 try c.sub(a, b);
2109
2110 testing.expect((try c.to(u128)) == op1 - op2);
2111}
2112
2113test "big.int sub equal" {
2114 var a = try Int.initSet(testing.allocator, 0x11efefefefefefefefefefefef);
2115 defer a.deinit();
2116 var b = try Int.initSet(testing.allocator, 0x11efefefefefefefefefefefef);
2117 defer b.deinit();
2118
2119 var c = try Int.init(testing.allocator);
2120 defer c.deinit();
2121 try c.sub(a, b);
2122
2123 testing.expect((try c.to(u32)) == 0);
2124}
2125
2126test "big.int sub sign" {
2127 var a = try Int.init(testing.allocator);
2128 defer a.deinit();
2129
2130 const one = try Int.initSet(testing.allocator, 1);
2131 defer one.deinit();
2132 const two = try Int.initSet(testing.allocator, 2);
2133 defer two.deinit();
2134 const neg_one = try Int.initSet(testing.allocator, -1);
2135 defer neg_one.deinit();
2136 const neg_two = try Int.initSet(testing.allocator, -2);
2137 defer neg_two.deinit();
2138
2139 try a.sub(one, two);
2140 testing.expect((try a.to(i32)) == -1);
2141
2142 try a.sub(neg_one, two);
2143 testing.expect((try a.to(i32)) == -3);
2144
2145 try a.sub(one, neg_two);
2146 testing.expect((try a.to(i32)) == 3);
2147
2148 try a.sub(neg_one, neg_two);
2149 testing.expect((try a.to(i32)) == 1);
2150
2151 try a.sub(neg_two, neg_one);
2152 testing.expect((try a.to(i32)) == -1);
2153}
2154
2155test "big.int mul single-single" {
2156 var a = try Int.initSet(testing.allocator, 50);
2157 defer a.deinit();
2158 var b = try Int.initSet(testing.allocator, 5);
2159 defer b.deinit();
2160
2161 var c = try Int.init(testing.allocator);
2162 defer c.deinit();
2163 try c.mul(a, b);
2164
2165 testing.expect((try c.to(u64)) == 250);
2166}
2167
2168test "big.int mul multi-single" {
2169 var a = try Int.initSet(testing.allocator, maxInt(Limb));
2170 defer a.deinit();
2171 var b = try Int.initSet(testing.allocator, 2);
2172 defer b.deinit();
2173
2174 var c = try Int.init(testing.allocator);
2175 defer c.deinit();
2176 try c.mul(a, b);
2177
2178 testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb));
2179}
2180
2181test "big.int mul multi-multi" {
2182 const op1 = 0x998888efefefefefefefef;
2183 const op2 = 0x333000abababababababab;
2184 var a = try Int.initSet(testing.allocator, op1);
2185 defer a.deinit();
2186 var b = try Int.initSet(testing.allocator, op2);
2187 defer b.deinit();
2188
2189 var c = try Int.init(testing.allocator);
2190 defer c.deinit();
2191 try c.mul(a, b);
2192
2193 testing.expect((try c.to(u256)) == op1 * op2);
2194}
2195
2196test "big.int mul alias r with a" {
2197 var a = try Int.initSet(testing.allocator, maxInt(Limb));
2198 defer a.deinit();
2199 var b = try Int.initSet(testing.allocator, 2);
2200 defer b.deinit();
2201
2202 try a.mul(a, b);
2203
2204 testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
2205}
2206
2207test "big.int mul alias r with b" {
2208 var a = try Int.initSet(testing.allocator, maxInt(Limb));
2209 defer a.deinit();
2210 var b = try Int.initSet(testing.allocator, 2);
2211 defer b.deinit();
2212
2213 try a.mul(b, a);
2214
2215 testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
2216}
2217
2218test "big.int mul alias r with a and b" {
2219 var a = try Int.initSet(testing.allocator, maxInt(Limb));
2220 defer a.deinit();
2221
2222 try a.mul(a, a);
2223
2224 testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb));
2225}
2226
2227test "big.int mul a*0" {
2228 var a = try Int.initSet(testing.allocator, 0xefefefefefefefef);
2229 defer a.deinit();
2230 var b = try Int.initSet(testing.allocator, 0);
2231 defer b.deinit();
2232
2233 var c = try Int.init(testing.allocator);
2234 defer c.deinit();
2235 try c.mul(a, b);
2236
2237 testing.expect((try c.to(u32)) == 0);
2238}
2239
2240test "big.int mul 0*0" {
2241 var a = try Int.initSet(testing.allocator, 0);
2242 defer a.deinit();
2243 var b = try Int.initSet(testing.allocator, 0);
2244 defer b.deinit();
2245
2246 var c = try Int.init(testing.allocator);
2247 defer c.deinit();
2248 try c.mul(a, b);
2249
2250 testing.expect((try c.to(u32)) == 0);
2251}
2252
2253test "big.int div single-single no rem" {
2254 var a = try Int.initSet(testing.allocator, 50);
2255 defer a.deinit();
2256 var b = try Int.initSet(testing.allocator, 5);
2257 defer b.deinit();
2258
2259 var q = try Int.init(testing.allocator);
2260 defer q.deinit();
2261 var r = try Int.init(testing.allocator);
2262 defer r.deinit();
2263 try Int.divTrunc(&q, &r, a, b);
2264
2265 testing.expect((try q.to(u32)) == 10);
2266 testing.expect((try r.to(u32)) == 0);
2267}
2268
2269test "big.int div single-single with rem" {
2270 var a = try Int.initSet(testing.allocator, 49);
2271 defer a.deinit();
2272 var b = try Int.initSet(testing.allocator, 5);
2273 defer b.deinit();
2274
2275 var q = try Int.init(testing.allocator);
2276 defer q.deinit();
2277 var r = try Int.init(testing.allocator);
2278 defer r.deinit();
2279 try Int.divTrunc(&q, &r, a, b);
2280
2281 testing.expect((try q.to(u32)) == 9);
2282 testing.expect((try r.to(u32)) == 4);
2283}
2284
2285test "big.int div multi-single no rem" {
2286 const op1 = 0xffffeeeeddddcccc;
2287 const op2 = 34;
2288
2289 var a = try Int.initSet(testing.allocator, op1);
2290 defer a.deinit();
2291 var b = try Int.initSet(testing.allocator, op2);
2292 defer b.deinit();
2293
2294 var q = try Int.init(testing.allocator);
2295 defer q.deinit();
2296 var r = try Int.init(testing.allocator);
2297 defer r.deinit();
2298 try Int.divTrunc(&q, &r, a, b);
2299
2300 testing.expect((try q.to(u64)) == op1 / op2);
2301 testing.expect((try r.to(u64)) == 0);
2302}
2303
2304test "big.int div multi-single with rem" {
2305 const op1 = 0xffffeeeeddddcccf;
2306 const op2 = 34;
2307
2308 var a = try Int.initSet(testing.allocator, op1);
2309 defer a.deinit();
2310 var b = try Int.initSet(testing.allocator, op2);
2311 defer b.deinit();
2312
2313 var q = try Int.init(testing.allocator);
2314 defer q.deinit();
2315 var r = try Int.init(testing.allocator);
2316 defer r.deinit();
2317 try Int.divTrunc(&q, &r, a, b);
2318
2319 testing.expect((try q.to(u64)) == op1 / op2);
2320 testing.expect((try r.to(u64)) == 3);
2321}
2322
2323test "big.int div multi>2-single" {
2324 const op1 = 0xfefefefefefefefefefefefefefefefe;
2325 const op2 = 0xefab8;
2326
2327 var a = try Int.initSet(testing.allocator, op1);
2328 defer a.deinit();
2329 var b = try Int.initSet(testing.allocator, op2);
2330 defer b.deinit();
23311258
2332 var q = try Int.init(testing.allocator);1259 /// Same as `order` but the right-hand operand is a primitive integer.
2333 defer q.deinit();1260 pub fn orderAgainstScalar(lhs: Const, scalar: var) math.Order {
2334 var r = try Int.init(testing.allocator);1261 var limbs: [calcLimbLen(scalar)]Limb = undefined;
2335 defer r.deinit();1262 const rhs = Mutable.init(&limbs, scalar);
2336 try Int.divTrunc(&q, &r, a, b);1263 return order(lhs, rhs.toConst());
1264 }
23371265
2338 testing.expect((try q.to(u128)) == op1 / op2);1266 /// Returns true if `a == 0`.
2339 testing.expect((try r.to(u32)) == 0x3e4e);1267 pub fn eqZero(a: Const) bool {
2340}1268 return a.limbs.len == 1 and a.limbs[0] == 0;
1269 }
23411270
2342test "big.int div single-single q < r" {1271 /// Returns true if `|a| == |b|`.
2343 var a = try Int.initSet(testing.allocator, 0x0078f432);1272 pub fn eqAbs(a: Const, b: Const) bool {
2344 defer a.deinit();1273 return orderAbs(a, b) == .eq;
2345 var b = try Int.initSet(testing.allocator, 0x01000000);1274 }
2346 defer b.deinit();
23471275
2348 var q = try Int.init(testing.allocator);1276 /// Returns true if `a == b`.
2349 defer q.deinit();1277 pub fn eq(a: Const, b: Const) bool {
2350 var r = try Int.init(testing.allocator);1278 return order(a, b) == .eq;
2351 defer r.deinit();1279 }
2352 try Int.divTrunc(&q, &r, a, b);1280};
23531281
2354 testing.expect((try q.to(u64)) == 0);1282/// An arbitrary-precision big integer along with an allocator which manages the memory.
2355 testing.expect((try r.to(u64)) == 0x0078f432);1283///
2356}1284/// Memory is allocated as needed to ensure operations never overflow. The range
1285/// is bounded only by available memory.
1286pub const Managed = struct {
1287 pub const sign_bit: usize = 1 << (usize.bit_count - 1);
23571288
2358test "big.int div single-single q == r" {1289 /// Default number of limbs to allocate on creation of a `Managed`.
2359 var a = try Int.initSet(testing.allocator, 10);1290 pub const default_capacity = 4;
2360 defer a.deinit();
2361 var b = try Int.initSet(testing.allocator, 10);
2362 defer b.deinit();
23631291
2364 var q = try Int.init(testing.allocator);1292 /// Allocator used by the Managed when requesting memory.
2365 defer q.deinit();1293 allocator: *Allocator,
2366 var r = try Int.init(testing.allocator);
2367 defer r.deinit();
2368 try Int.divTrunc(&q, &r, a, b);
23691294
2370 testing.expect((try q.to(u64)) == 1);1295 /// Raw digits. These are:
2371 testing.expect((try r.to(u64)) == 0);1296 ///
2372}1297 /// * Little-endian ordered
1298 /// * limbs.len >= 1
1299 /// * Zero is represent as Managed.len() == 1 with limbs[0] == 0.
1300 ///
1301 /// Accessing limbs directly should be avoided.
1302 limbs: []Limb,
23731303
2374test "big.int div q=0 alias" {1304 /// High bit is the sign bit. If set, Managed is negative, else Managed is positive.
2375 var a = try Int.initSet(testing.allocator, 3);1305 /// The remaining bits represent the number of limbs used by Managed.
2376 defer a.deinit();1306 metadata: usize,
2377 var b = try Int.initSet(testing.allocator, 10);
2378 defer b.deinit();
23791307
2380 try Int.divTrunc(&a, &b, a, b);1308 /// Creates a new `Managed`. `default_capacity` limbs will be allocated immediately.
1309 /// The integer value after initializing is `0`.
1310 pub fn init(allocator: *Allocator) !Managed {
1311 return initCapacity(allocator, default_capacity);
1312 }
23811313
2382 testing.expect((try a.to(u64)) == 0);1314 pub fn toMutable(self: Managed) Mutable {
2383 testing.expect((try b.to(u64)) == 3);1315 return .{
2384}1316 .limbs = self.limbs,
1317 .positive = self.isPositive(),
1318 .len = self.len(),
1319 };
1320 }
23851321
2386test "big.int div multi-multi q < r" {1322 pub fn toConst(self: Managed) Const {
2387 const op1 = 0x1ffffffff0078f432;1323 return .{
2388 const op2 = 0x1ffffffff01000000;1324 .limbs = self.limbs[0..self.len()],
2389 var a = try Int.initSet(testing.allocator, op1);1325 .positive = self.isPositive(),
2390 defer a.deinit();1326 };
2391 var b = try Int.initSet(testing.allocator, op2);1327 }
2392 defer b.deinit();
2393
2394 var q = try Int.init(testing.allocator);
2395 defer q.deinit();
2396 var r = try Int.init(testing.allocator);
2397 defer r.deinit();
2398 try Int.divTrunc(&q, &r, a, b);
2399
2400 testing.expect((try q.to(u128)) == 0);
2401 testing.expect((try r.to(u128)) == op1);
2402}
24031328
2404test "big.int div trunc single-single +/+" {1329 /// Creates a new `Managed` with value `value`.
2405 const u: i32 = 5;1330 ///
2406 const v: i32 = 3;1331 /// This is identical to an `init`, followed by a `set`.
1332 pub fn initSet(allocator: *Allocator, value: var) !Managed {
1333 var s = try Managed.init(allocator);
1334 try s.set(value);
1335 return s;
1336 }
24071337
2408 var a = try Int.initSet(testing.allocator, u);1338 /// Creates a new Managed with a specific capacity. If capacity < default_capacity then the
2409 defer a.deinit();1339 /// default capacity will be used instead.
2410 var b = try Int.initSet(testing.allocator, v);1340 /// The integer value after initializing is `0`.
2411 defer b.deinit();1341 pub fn initCapacity(allocator: *Allocator, capacity: usize) !Managed {
1342 return Managed{
1343 .allocator = allocator,
1344 .metadata = 1,
1345 .limbs = block: {
1346 const limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));
1347 limbs[0] = 0;
1348 break :block limbs;
1349 },
1350 };
1351 }
24121352
2413 var q = try Int.init(testing.allocator);1353 /// Returns the number of limbs currently in use.
2414 defer q.deinit();1354 pub fn len(self: Managed) usize {
2415 var r = try Int.init(testing.allocator);1355 return self.metadata & ~sign_bit;
2416 defer r.deinit();1356 }
2417 try Int.divTrunc(&q, &r, a, b);
24181357
2419 // n = q * d + r1358 /// Returns whether an Managed is positive.
2420 // 5 = 1 * 3 + 21359 pub fn isPositive(self: Managed) bool {
2421 const eq = @divTrunc(u, v);1360 return self.metadata & sign_bit == 0;
2422 const er = @mod(u, v);1361 }
24231362
2424 testing.expect((try q.to(i32)) == eq);1363 /// Sets the sign of an Managed.
2425 testing.expect((try r.to(i32)) == er);1364 pub fn setSign(self: *Managed, positive: bool) void {
2426}1365 if (positive) {
1366 self.metadata &= ~sign_bit;
1367 } else {
1368 self.metadata |= sign_bit;
1369 }
1370 }
24271371
2428test "big.int div trunc single-single -/+" {1372 /// Sets the length of an Managed.
2429 const u: i32 = -5;1373 ///
2430 const v: i32 = 3;1374 /// If setLen is used, then the Managed must be normalized to suit.
1375 pub fn setLen(self: *Managed, new_len: usize) void {
1376 self.metadata &= sign_bit;
1377 self.metadata |= new_len;
1378 }
24311379
2432 var a = try Int.initSet(testing.allocator, u);1380 pub fn setMetadata(self: *Managed, positive: bool, length: usize) void {
2433 defer a.deinit();1381 self.metadata = if (positive) length & ~sign_bit else length | sign_bit;
2434 var b = try Int.initSet(testing.allocator, v);1382 }
2435 defer b.deinit();
24361383
2437 var q = try Int.init(testing.allocator);1384 /// Ensures an Managed has enough space allocated for capacity limbs. If the Managed does not have
2438 defer q.deinit();1385 /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested
2439 var r = try Int.init(testing.allocator);1386 /// capacity is only greater than the current capacity by one limb.
2440 defer r.deinit();1387 pub fn ensureCapacity(self: *Managed, capacity: usize) !void {
2441 try Int.divTrunc(&q, &r, a, b);1388 if (capacity <= self.limbs.len) {
1389 return;
1390 }
1391 self.limbs = try self.allocator.realloc(self.limbs, capacity);
1392 }
24421393
2443 // n = q * d + r1394 /// Frees all associated memory.
2444 // -5 = 1 * -3 - 21395 pub fn deinit(self: *Managed) void {
2445 const eq = -1;1396 self.allocator.free(self.limbs);
2446 const er = -2;1397 self.* = undefined;
1398 }
24471399
2448 testing.expect((try q.to(i32)) == eq);1400 /// Returns a `Managed` with the same value. The returned `Managed` is a deep copy and
2449 testing.expect((try r.to(i32)) == er);1401 /// can be modified separately from the original, and its resources are managed
2450}1402 /// separately from the original.
1403 pub fn clone(other: Managed) !Managed {
1404 return other.cloneWithDifferentAllocator(other.allocator);
1405 }
24511406
2452test "big.int div trunc single-single +/-" {1407 pub fn cloneWithDifferentAllocator(other: Managed, allocator: *Allocator) !Managed {
2453 const u: i32 = 5;1408 return Managed{
2454 const v: i32 = -3;1409 .allocator = allocator,
1410 .metadata = other.metadata,
1411 .limbs = block: {
1412 var limbs = try allocator.alloc(Limb, other.len());
1413 mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]);
1414 break :block limbs;
1415 },
1416 };
1417 }
24551418
2456 var a = try Int.initSet(testing.allocator, u);1419 /// Copies the value of the integer to an existing `Managed` so that they both have the same value.
2457 defer a.deinit();1420 /// Extra memory will be allocated if the receiver does not have enough capacity.
2458 var b = try Int.initSet(testing.allocator, v);1421 pub fn copy(self: *Managed, other: Const) !void {
2459 defer b.deinit();1422 if (self.limbs.ptr == other.limbs.ptr) return;
24601423
2461 var q = try Int.init(testing.allocator);1424 try self.ensureCapacity(other.limbs.len);
2462 defer q.deinit();1425 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.limbs.len]);
2463 var r = try Int.init(testing.allocator);1426 self.setMetadata(other.positive, other.limbs.len);
2464 defer r.deinit();1427 }
2465 try Int.divTrunc(&q, &r, a, b);
24661428
2467 // n = q * d + r1429 /// Efficiently swap a `Managed` with another. This swaps the limb pointers and a full copy is not
2468 // 5 = -1 * -3 + 21430 /// performed. The address of the limbs field will not be the same after this function.
2469 const eq = -1;1431 pub fn swap(self: *Managed, other: *Managed) void {
2470 const er = 2;1432 mem.swap(Managed, self, other);
1433 }
24711434
2472 testing.expect((try q.to(i32)) == eq);1435 /// Debugging tool: prints the state to stderr.
2473 testing.expect((try r.to(i32)) == er);1436 pub fn dump(self: Managed) void {
2474}1437 for (self.limbs[0..self.len()]) |limb| {
1438 std.debug.warn("{x} ", .{limb});
1439 }
1440 std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.positive });
1441 }
24751442
2476test "big.int div trunc single-single -/-" {1443 /// Negate the sign.
2477 const u: i32 = -5;1444 pub fn negate(self: *Managed) void {
2478 const v: i32 = -3;1445 self.metadata ^= sign_bit;
1446 }
24791447
2480 var a = try Int.initSet(testing.allocator, u);1448 /// Make positive.
2481 defer a.deinit();1449 pub fn abs(self: *Managed) void {
2482 var b = try Int.initSet(testing.allocator, v);1450 self.metadata &= ~sign_bit;
2483 defer b.deinit();1451 }
24841452
2485 var q = try Int.init(testing.allocator);1453 pub fn isOdd(self: Managed) bool {
2486 defer q.deinit();1454 return self.limbs[0] & 1 != 0;
2487 var r = try Int.init(testing.allocator);1455 }
2488 defer r.deinit();
2489 try Int.divTrunc(&q, &r, a, b);
24901456
2491 // n = q * d + r1457 pub fn isEven(self: Managed) bool {
2492 // -5 = 1 * -3 - 21458 return !self.isOdd();
2493 const eq = 1;1459 }
2494 const er = -2;
24951460
2496 testing.expect((try q.to(i32)) == eq);1461 /// Returns the number of bits required to represent the absolute value of an integer.
2497 testing.expect((try r.to(i32)) == er);1462 pub fn bitCountAbs(self: Managed) usize {
2498}1463 return self.toConst().bitCountAbs();
1464 }
24991465
2500test "big.int div floor single-single +/+" {1466 /// Returns the number of bits required to represent the integer in twos-complement form.
2501 const u: i32 = 5;1467 ///
2502 const v: i32 = 3;1468 /// If the integer is negative the value returned is the number of bits needed by a signed
1469 /// integer to represent the value. If positive the value is the number of bits for an
1470 /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
1471 /// one greater than the returned value.
1472 ///
1473 /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
1474 pub fn bitCountTwosComp(self: Managed) usize {
1475 return self.toConst().bitCountTwosComp();
1476 }
25031477
2504 var a = try Int.initSet(testing.allocator, u);1478 pub fn fitsInTwosComp(self: Managed, is_signed: bool, bit_count: usize) bool {
2505 defer a.deinit();1479 return self.toConst().fitsInTwosComp(is_signed, bit_count);
2506 var b = try Int.initSet(testing.allocator, v);1480 }
2507 defer b.deinit();
25081481
2509 var q = try Int.init(testing.allocator);1482 /// Returns whether self can fit into an integer of the requested type.
2510 defer q.deinit();1483 pub fn fits(self: Managed, comptime T: type) bool {
2511 var r = try Int.init(testing.allocator);1484 return self.toConst().fits(T);
2512 defer r.deinit();1485 }
2513 try Int.divFloor(&q, &r, a, b);
25141486
2515 // n = q * d + r1487 /// Returns the approximate size of the integer in the given base. Negative values accommodate for
2516 // 5 = 1 * 3 + 21488 /// the minus sign. This is used for determining the number of characters needed to print the
2517 const eq = 1;1489 /// value. It is inexact and may exceed the given value by ~1-2 bytes.
2518 const er = 2;1490 pub fn sizeInBaseUpperBound(self: Managed, base: usize) usize {
1491 return self.toConst().sizeInBaseUpperBound(base);
1492 }
25191493
2520 testing.expect((try q.to(i32)) == eq);1494 /// Sets an Managed to value. Value must be an primitive integer type.
2521 testing.expect((try r.to(i32)) == er);1495 pub fn set(self: *Managed, value: var) Allocator.Error!void {
2522}1496 try self.ensureCapacity(calcLimbLen(value));
1497 var m = self.toMutable();
1498 m.set(value);
1499 self.setMetadata(m.positive, m.len);
1500 }
25231501
2524test "big.int div floor single-single -/+" {1502 pub const ConvertError = Const.ConvertError;
2525 const u: i32 = -5;
2526 const v: i32 = 3;
25271503
2528 var a = try Int.initSet(testing.allocator, u);1504 /// Convert self to type T.
2529 defer a.deinit();1505 ///
2530 var b = try Int.initSet(testing.allocator, v);1506 /// Returns an error if self cannot be narrowed into the requested type without truncation.
2531 defer b.deinit();1507 pub fn to(self: Managed, comptime T: type) ConvertError!T {
1508 return self.toConst().to(T);
1509 }
25321510
2533 var q = try Int.init(testing.allocator);1511 /// Set self from the string representation `value`.
2534 defer q.deinit();1512 ///
2535 var r = try Int.init(testing.allocator);1513 /// `value` must contain only digits <= `base` and is case insensitive. Base prefixes are
2536 defer r.deinit();1514 /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are
2537 try Int.divFloor(&q, &r, a, b);1515 /// ignored and can be used as digit separators.
1516 ///
1517 /// Returns an error if memory could not be allocated or `value` has invalid digits for the
1518 /// requested base.
1519 ///
1520 /// self's allocator is used for temporary storage to boost multiplication performance.
1521 pub fn setString(self: *Managed, base: u8, value: []const u8) !void {
1522 if (base < 2 or base > 16) return error.InvalidBase;
1523 const den = (@sizeOf(Limb) * 8 / base);
1524 try self.ensureCapacity((value.len + (den - 1)) / den);
1525 const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len));
1526 defer self.allocator.free(limbs_buffer);
1527 var m = self.toMutable();
1528 try m.setString(base, value, limbs_buffer, self.allocator);
1529 self.setMetadata(m.positive, m.len);
1530 }
25381531
2539 // n = q * d + r1532 /// Converts self to a string in the requested base. Memory is allocated from the provided
2540 // -5 = -2 * 3 + 11533 /// allocator and not the one present in self.
2541 const eq = -2;1534 pub fn toString(self: Managed, allocator: *Allocator, base: u8, uppercase: bool) ![]u8 {
2542 const er = 1;1535 if (base < 2 or base > 16) return error.InvalidBase;
1536 return self.toConst().toStringAlloc(self.allocator, base, uppercase);
1537 }
25431538
2544 testing.expect((try q.to(i32)) == eq);1539 /// To allow `std.fmt.format` to work with `Managed`.
2545 testing.expect((try r.to(i32)) == er);1540 /// If the integer is larger than `pow(2, 64 * @sizeOf(usize) * 8), this function will fail
2546}1541 /// to print the string, printing "(BigInt)" instead of a number.
1542 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
1543 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
1544 pub fn format(
1545 self: Managed,
1546 comptime fmt: []const u8,
1547 options: std.fmt.FormatOptions,
1548 out_stream: var,
1549 ) !void {
1550 return self.toConst().format(fmt, options, out_stream);
1551 }
25471552
2548test "big.int div floor single-single +/-" {1553 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
2549 const u: i32 = 5;1554 /// |b| or |a| > |b| respectively.
2550 const v: i32 = -3;1555 pub fn orderAbs(a: Managed, b: Managed) math.Order {
1556 return a.toConst().orderAbs(b.toConst());
1557 }
25511558
2552 var a = try Int.initSet(testing.allocator, u);1559 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
2553 defer a.deinit();1560 /// > b respectively.
2554 var b = try Int.initSet(testing.allocator, v);1561 pub fn order(a: Managed, b: Managed) math.Order {
2555 defer b.deinit();1562 return a.toConst().order(b.toConst());
1563 }
25561564
2557 var q = try Int.init(testing.allocator);1565 /// Returns true if a == 0.
2558 defer q.deinit();1566 pub fn eqZero(a: Managed) bool {
2559 var r = try Int.init(testing.allocator);1567 return a.toConst().eqZero();
2560 defer r.deinit();1568 }
2561 try Int.divFloor(&q, &r, a, b);
25621569
2563 // n = q * d + r1570 /// Returns true if |a| == |b|.
2564 // 5 = -2 * -3 - 11571 pub fn eqAbs(a: Managed, b: Managed) bool {
2565 const eq = -2;1572 return a.toConst().eqAbs(b.toConst());
2566 const er = -1;1573 }
25671574
2568 testing.expect((try q.to(i32)) == eq);1575 /// Returns true if a == b.
2569 testing.expect((try r.to(i32)) == er);1576 pub fn eq(a: Managed, b: Managed) bool {
2570}1577 return a.toConst().eq(b.toConst());
1578 }
25711579
2572test "big.int div floor single-single -/-" {1580 /// Normalize a possible sequence of leading zeros.
2573 const u: i32 = -5;1581 ///
2574 const v: i32 = -3;1582 /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
1583 /// [1, 2, 0, 0, 0] -> [1, 2]
1584 /// [0, 0, 0, 0, 0] -> [0]
1585 pub fn normalize(r: *Managed, length: usize) void {
1586 assert(length > 0);
1587 assert(length <= r.limbs.len);
25751588
2576 var a = try Int.initSet(testing.allocator, u);1589 var j = length;
2577 defer a.deinit();1590 while (j > 0) : (j -= 1) {
2578 var b = try Int.initSet(testing.allocator, v);1591 if (r.limbs[j - 1] != 0) {
2579 defer b.deinit();1592 break;
1593 }
1594 }
25801595
2581 var q = try Int.init(testing.allocator);1596 // Handle zero
2582 defer q.deinit();1597 r.setLen(if (j != 0) j else 1);
2583 var r = try Int.init(testing.allocator);1598 }
2584 defer r.deinit();
2585 try Int.divFloor(&q, &r, a, b);
25861599
2587 // n = q * d + r1600 /// r = a + scalar
2588 // -5 = 2 * -3 + 11601 ///
2589 const eq = 1;1602 /// r and a may be aliases.
2590 const er = -2;1603 /// scalar is a primitive integer type.
1604 ///
1605 /// Returns an error if memory could not be allocated.
1606 pub fn addScalar(r: *Managed, a: Const, scalar: var) Allocator.Error!void {
1607 try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1);
1608 var m = r.toMutable();
1609 m.addScalar(a, scalar);
1610 r.setMetadata(m.positive, m.len);
1611 }
25911612
2592 testing.expect((try q.to(i32)) == eq);1613 /// r = a + b
2593 testing.expect((try r.to(i32)) == er);1614 ///
2594}1615 /// r, a and b may be aliases.
1616 ///
1617 /// Returns an error if memory could not be allocated.
1618 pub fn add(r: *Managed, a: Const, b: Const) Allocator.Error!void {
1619 try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1);
1620 var m = r.toMutable();
1621 m.add(a, b);
1622 r.setMetadata(m.positive, m.len);
1623 }
25951624
2596test "big.int div multi-multi with rem" {1625 /// r = a - b
2597 var a = try Int.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);1626 ///
2598 defer a.deinit();1627 /// r, a and b may be aliases.
2599 var b = try Int.initSet(testing.allocator, 0x99990000111122223333);1628 ///
2600 defer b.deinit();1629 /// Returns an error if memory could not be allocated.
1630 pub fn sub(r: *Managed, a: Const, b: Const) !void {
1631 try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1);
1632 var m = r.toMutable();
1633 m.sub(a, b);
1634 r.setMetadata(m.positive, m.len);
1635 }
26011636
2602 var q = try Int.init(testing.allocator);1637 /// rma = a * b
2603 defer q.deinit();1638 ///
2604 var r = try Int.init(testing.allocator);1639 /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
2605 defer r.deinit();1640 ///
2606 try Int.divTrunc(&q, &r, a, b);1641 /// Returns an error if memory could not be allocated.
1642 ///
1643 /// rma's allocator is used for temporary storage to speed up the multiplication.
1644 pub fn mul(rma: *Managed, a: Const, b: Const) !void {
1645 try rma.ensureCapacity(a.limbs.len + b.limbs.len + 1);
1646 var alias_count: usize = 0;
1647 if (rma.limbs.ptr == a.limbs.ptr)
1648 alias_count += 1;
1649 if (rma.limbs.ptr == b.limbs.ptr)
1650 alias_count += 1;
1651 var m = rma.toMutable();
1652 if (alias_count == 0) {
1653 m.mulNoAlias(a, b, rma.allocator);
1654 } else {
1655 const limb_count = calcMulLimbsBufferLen(a.limbs.len, b.limbs.len, alias_count);
1656 const limbs_buffer = try rma.allocator.alloc(Limb, limb_count);
1657 defer rma.allocator.free(limbs_buffer);
1658 m.mul(a, b, limbs_buffer, rma.allocator);
1659 }
1660 rma.setMetadata(m.positive, m.len);
1661 }
26071662
2608 testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);1663 /// q = a / b (rem r)
2609 testing.expect((try r.to(u128)) == 0x28de0acacd806823638);1664 ///
2610}1665 /// a / b are floored (rounded towards 0).
1666 ///
1667 /// Returns an error if memory could not be allocated.
1668 ///
1669 /// q's allocator is used for temporary storage to speed up the multiplication.
1670 pub fn divFloor(q: *Managed, r: *Managed, a: Const, b: Const) !void {
1671 try q.ensureCapacity(a.limbs.len + b.limbs.len + 1);
1672 try r.ensureCapacity(a.limbs.len);
1673 var mq = q.toMutable();
1674 var mr = r.toMutable();
1675 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len));
1676 defer q.allocator.free(limbs_buffer);
1677 mq.divFloor(&mr, a, b, limbs_buffer, q.allocator);
1678 q.setMetadata(mq.positive, mq.len);
1679 r.setMetadata(mr.positive, mr.len);
1680 }
26111681
2612test "big.int div multi-multi no rem" {1682 /// q = a / b (rem r)
2613 var a = try Int.initSet(testing.allocator, 0x8888999911110000ffffeeeedb4fec200ee3a4286361);1683 ///
2614 defer a.deinit();1684 /// a / b are truncated (rounded towards -inf).
2615 var b = try Int.initSet(testing.allocator, 0x99990000111122223333);1685 ///
2616 defer b.deinit();1686 /// Returns an error if memory could not be allocated.
1687 ///
1688 /// q's allocator is used for temporary storage to speed up the multiplication.
1689 pub fn divTrunc(q: *Managed, r: *Managed, a: Const, b: Const) !void {
1690 try q.ensureCapacity(a.limbs.len + b.limbs.len + 1);
1691 try r.ensureCapacity(a.limbs.len);
1692 var mq = q.toMutable();
1693 var mr = r.toMutable();
1694 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len));
1695 defer q.allocator.free(limbs_buffer);
1696 mq.divTrunc(&mr, a, b, limbs_buffer, q.allocator);
1697 q.setMetadata(mq.positive, mq.len);
1698 r.setMetadata(mr.positive, mr.len);
1699 }
26171700
2618 var q = try Int.init(testing.allocator);1701 /// r = a << shift, in other words, r = a * 2^shift
2619 defer q.deinit();1702 pub fn shiftLeft(r: *Managed, a: Managed, shift: usize) !void {
2620 var r = try Int.init(testing.allocator);1703 try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1);
2621 defer r.deinit();1704 var m = r.toMutable();
2622 try Int.divTrunc(&q, &r, a, b);1705 m.shiftLeft(a.toConst(), shift);
1706 r.setMetadata(m.positive, m.len);
1707 }
26231708
2624 testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);1709 /// r = a >> shift
2625 testing.expect((try r.to(u128)) == 0);1710 pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void {
2626}1711 if (a.len() <= shift / Limb.bit_count) {
1712 r.metadata = 1;
1713 r.limbs[0] = 0;
1714 return;
1715 }
26271716
2628test "big.int div multi-multi (2 branch)" {1717 try r.ensureCapacity(a.len() - (shift / Limb.bit_count));
2629 var a = try Int.initSet(testing.allocator, 0x866666665555555588888887777777761111111111111111);1718 var m = r.toMutable();
2630 defer a.deinit();1719 m.shiftRight(a.toConst(), shift);
2631 var b = try Int.initSet(testing.allocator, 0x86666666555555554444444433333333);1720 r.setMetadata(m.positive, m.len);
2632 defer b.deinit();1721 }
26331722
2634 var q = try Int.init(testing.allocator);1723 /// r = a | b
2635 defer q.deinit();1724 ///
2636 var r = try Int.init(testing.allocator);1725 /// a and b are zero-extended to the longer of a or b.
2637 defer r.deinit();1726 pub fn bitOr(r: *Managed, a: Managed, b: Managed) !void {
2638 try Int.divTrunc(&q, &r, a, b);1727 try r.ensureCapacity(math.max(a.len(), b.len()));
1728 var m = r.toMutable();
1729 m.bitOr(a.toConst(), b.toConst());
1730 r.setMetadata(m.positive, m.len);
1731 }
26391732
2640 testing.expect((try q.to(u128)) == 0x10000000000000000);1733 /// r = a & b
2641 testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111);1734 pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void {
2642}1735 try r.ensureCapacity(math.min(a.len(), b.len()));
1736 var m = r.toMutable();
1737 m.bitAnd(a.toConst(), b.toConst());
1738 r.setMetadata(m.positive, m.len);
1739 }
26431740
2644test "big.int div multi-multi (3.1/3.3 branch)" {1741 /// r = a ^ b
2645 var a = try Int.initSet(testing.allocator, 0x11111111111111111111111111111111111111111111111111111111111111);1742 pub fn bitXor(r: *Managed, a: Managed, b: Managed) !void {
2646 defer a.deinit();1743 try r.ensureCapacity(math.max(a.len(), b.len()));
2647 var b = try Int.initSet(testing.allocator, 0x1111111111111111111111111111111111111111171);1744 var m = r.toMutable();
2648 defer b.deinit();1745 m.bitXor(a.toConst(), b.toConst());
1746 r.setMetadata(m.positive, m.len);
1747 }
26491748
2650 var q = try Int.init(testing.allocator);1749 /// rma may alias x or y.
2651 defer q.deinit();1750 /// x and y may alias each other.
2652 var r = try Int.init(testing.allocator);1751 ///
2653 defer r.deinit();1752 /// rma's allocator is used for temporary storage to boost multiplication performance.
2654 try Int.divTrunc(&q, &r, a, b);1753 pub fn gcd(rma: *Managed, x: Managed, y: Managed) !void {
1754 try rma.ensureCapacity(math.min(x.len(), y.len()));
1755 var m = rma.toMutable();
1756 var limbs_buffer = std.ArrayList(Limb).init(rma.allocator);
1757 defer limbs_buffer.deinit();
1758 try m.gcd(x.toConst(), y.toConst(), &limbs_buffer);
1759 rma.setMetadata(m.positive, m.len);
1760 }
1761};
26551762
2656 testing.expect((try q.to(u128)) == 0xfffffffffffffffffff);1763/// Knuth 4.3.1, Algorithm M.
2657 testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);1764///
2658}1765/// r MUST NOT alias any of a or b.
1766fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const Limb) void {
1767 @setRuntimeSafety(false);
1768
1769 const a_norm = a[0..llnormalize(a)];
1770 const b_norm = b[0..llnormalize(b)];
1771 var x = a_norm;
1772 var y = b_norm;
1773 if (a_norm.len > b_norm.len) {
1774 x = b_norm;
1775 y = a_norm;
1776 }
1777
1778 assert(r.len >= x.len + y.len + 1);
1779
1780 // 48 is a pretty abitrary size chosen based on performance of a factorial program.
1781 if (x.len > 48) {
1782 if (opt_allocator) |allocator| {
1783 llmulacc_karatsuba(allocator, r, x, y) catch |err| switch (err) {
1784 error.OutOfMemory => {}, // handled below
1785 };
1786 }
1787 }
26591788
2660test "big.int div multi-single zero-limb trailing" {1789 // Basecase multiplication
2661 var a = try Int.initSet(testing.allocator, 0x60000000000000000000000000000000000000000000000000000000000000000);1790 var i: usize = 0;
2662 defer a.deinit();1791 while (i < x.len) : (i += 1) {
2663 var b = try Int.initSet(testing.allocator, 0x10000000000000000);1792 llmulDigit(r[i..], y, x[i]);
2664 defer b.deinit();1793 }
2665
2666 var q = try Int.init(testing.allocator);
2667 defer q.deinit();
2668 var r = try Int.init(testing.allocator);
2669 defer r.deinit();
2670 try Int.divTrunc(&q, &r, a, b);
2671
2672 var expected = try Int.initSet(testing.allocator, 0x6000000000000000000000000000000000000000000000000);
2673 defer expected.deinit();
2674 testing.expect(q.eq(expected));
2675 testing.expect(r.eqZero());
2676}1794}
26771795
2678test "big.int div multi-multi zero-limb trailing (with rem)" {1796/// Knuth 4.3.1, Algorithm M.
2679 var a = try Int.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);1797///
2680 defer a.deinit();1798/// r MUST NOT alias any of a or b.
2681 var b = try Int.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);1799fn llmulacc_karatsuba(allocator: *Allocator, r: []Limb, x: []const Limb, y: []const Limb) error{OutOfMemory}!void {
2682 defer b.deinit();1800 @setRuntimeSafety(false);
2683
2684 var q = try Int.init(testing.allocator);
2685 defer q.deinit();
2686 var r = try Int.init(testing.allocator);
2687 defer r.deinit();
2688 try Int.divTrunc(&q, &r, a, b);
2689
2690 testing.expect((try q.to(u128)) == 0x10000000000000000);
26911801
2692 const rs = try r.toString(testing.allocator, 16, false);1802 assert(r.len >= x.len + y.len + 1);
2693 defer testing.allocator.free(rs);
2694 testing.expect(std.mem.eql(u8, rs, "4444444344444443111111111111111100000000000000000000000000000000"));
2695}
26961803
2697test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" {1804 const split = @divFloor(x.len, 2);
2698 var a = try Int.initSet(testing.allocator, 0x8666666655555555888888877777777611111111111111110000000000000000);1805 var x0 = x[0..split];
2699 defer a.deinit();1806 var x1 = x[split..x.len];
2700 var b = try Int.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);1807 var y0 = y[0..split];
2701 defer b.deinit();1808 var y1 = y[split..y.len];
27021809
2703 var q = try Int.init(testing.allocator);1810 var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1);
2704 defer q.deinit();1811 defer allocator.free(tmp);
2705 var r = try Int.init(testing.allocator);1812 mem.set(Limb, tmp, 0);
2706 defer r.deinit();
2707 try Int.divTrunc(&q, &r, a, b);
27081813
2709 testing.expect((try q.to(u128)) == 0x1);1814 llmulacc(allocator, tmp, x1, y1);
27101815
2711 const rs = try r.toString(testing.allocator, 16, false);1816 var length = llnormalize(tmp);
2712 defer testing.allocator.free(rs);1817 _ = llaccum(r[split..], tmp[0..length]);
2713 testing.expect(std.mem.eql(u8, rs, "444444434444444311111111111111110000000000000000"));1818 _ = llaccum(r[split * 2 ..], tmp[0..length]);
2714}
27151819
2716test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" {1820 mem.set(Limb, tmp[0..length], 0);
2717 var a = try Int.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
2718 defer a.deinit();
2719 var b = try Int.initSet(testing.allocator, 0x866666665555555544444444333333330000000000000000);
2720 defer b.deinit();
2721
2722 var q = try Int.init(testing.allocator);
2723 defer q.deinit();
2724 var r = try Int.init(testing.allocator);
2725 defer r.deinit();
2726 try Int.divTrunc(&q, &r, a, b);
2727
2728 const qs = try q.toString(testing.allocator, 16, false);
2729 defer testing.allocator.free(qs);
2730 testing.expect(std.mem.eql(u8, qs, "10000000000000000820820803105186f"));
2731
2732 const rs = try r.toString(testing.allocator, 16, false);
2733 defer testing.allocator.free(rs);
2734 testing.expect(std.mem.eql(u8, rs, "4e11f2baa5896a321d463b543d0104e30000000000000000"));
2735}
27361821
2737test "big.int div multi-multi fuzz case #1" {1822 llmulacc(allocator, tmp, x0, y0);
2738 var a = try Int.init(testing.allocator);
2739 defer a.deinit();
2740 var b = try Int.init(testing.allocator);
2741 defer b.deinit();
27421823
2743 try a.setString(16, "ffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");1824 length = llnormalize(tmp);
2744 try b.setString(16, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffc000000000000000000000000000000007fffffffffff");1825 _ = llaccum(r[0..], tmp[0..length]);
1826 _ = llaccum(r[split..], tmp[0..length]);
27451827
2746 var q = try Int.init(testing.allocator);1828 const x_cmp = llcmp(x1, x0);
2747 defer q.deinit();1829 const y_cmp = llcmp(y1, y0);
2748 var r = try Int.init(testing.allocator);1830 if (x_cmp * y_cmp == 0) {
2749 defer r.deinit();1831 return;
2750 try Int.divTrunc(&q, &r, a, b);1832 }
1833 const x0_len = llnormalize(x0);
1834 const x1_len = llnormalize(x1);
1835 var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len));
1836 defer allocator.free(j0);
1837 if (x_cmp == 1) {
1838 llsub(j0, x1[0..x1_len], x0[0..x0_len]);
1839 } else {
1840 llsub(j0, x0[0..x0_len], x1[0..x1_len]);
1841 }
27511842
2752 const qs = try q.toString(testing.allocator, 16, false);1843 const y0_len = llnormalize(y0);
2753 defer testing.allocator.free(qs);1844 const y1_len = llnormalize(y1);
2754 testing.expect(std.mem.eql(u8, qs, "3ffffffffffffffffffffffffffff0000000000000000000000000000000000001ffffffffffffffffffffffffffff7fffffffe000000000000000000000000000180000000000000000000003fffffbfffffffdfffffffffffffeffff800000100101000000100000000020003fffffdfbfffffe3ffffffffffffeffff7fffc00800a100000017ffe000002000400007efbfff7fe9f00000037ffff3fff7fffa004006100000009ffe00000190038200bf7d2ff7fefe80400060000f7d7f8fbf9401fe38e0403ffc0bdffffa51102c300d7be5ef9df4e5060007b0127ad3fa69f97d0f820b6605ff617ddf7f32ad7a05c0d03f2e7bc78a6000e087a8bbcdc59e07a5a079128a7861f553ddebed7e8e56701756f9ead39b48cd1b0831889ea6ec1fddf643d0565b075ff07e6caea4e2854ec9227fd635ed60a2f5eef2893052ffd54718fa08604acbf6a15e78a467c4a3c53c0278af06c4416573f925491b195e8fd79302cb1aaf7caf4ecfc9aec1254cc969786363ac729f914c6ddcc26738d6b0facd54eba026580aba2eb6482a088b0d224a8852420b91ec1"));1845 var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len));
1846 defer allocator.free(j1);
1847 if (y_cmp == 1) {
1848 llsub(j1, y1[0..y1_len], y0[0..y0_len]);
1849 } else {
1850 llsub(j1, y0[0..y0_len], y1[0..y1_len]);
1851 }
1852 const j0_len = llnormalize(j0);
1853 const j1_len = llnormalize(j1);
1854 if (x_cmp == y_cmp) {
1855 mem.set(Limb, tmp[0..length], 0);
1856 llmulacc(allocator, tmp, j0, j1);
27551857
2756 const rs = try r.toString(testing.allocator, 16, false);1858 length = llnormalize(tmp);
2757 defer testing.allocator.free(rs);1859 llsub(r[split..], r[split..], tmp[0..length]);
2758 testing.expect(std.mem.eql(u8, rs, "310d1d4c414426b4836c2635bad1df3a424e50cbdd167ffccb4dfff57d36b4aae0d6ca0910698220171a0f3373c1060a046c2812f0027e321f72979daa5e7973214170d49e885de0c0ecc167837d44502430674a82522e5df6a0759548052420b91ec1"));1860 } else {
1861 llmulacc(allocator, r[split..], j0, j1);
1862 }
2759}1863}
27601864
2761test "big.int div multi-multi fuzz case #2" {1865// r = r + a
2762 var a = try Int.init(testing.allocator);1866fn llaccum(r: []Limb, a: []const Limb) Limb {
2763 defer a.deinit();1867 @setRuntimeSafety(false);
2764 var b = try Int.init(testing.allocator);1868 assert(r.len != 0 and a.len != 0);
2765 defer b.deinit();1869 assert(r.len >= a.len);
27661870
2767 try a.setString(16, "3ffffffffe00000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000001fffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffc000000000000000000000000000000000000000000000000000000000000000");1871 var i: usize = 0;
2768 try b.setString(16, "ffc0000000000000000000000000000000000000000000000000");1872 var carry: Limb = 0;
27691873
2770 var q = try Int.init(testing.allocator);1874 while (i < a.len) : (i += 1) {
2771 defer q.deinit();1875 var c: Limb = 0;
2772 var r = try Int.init(testing.allocator);1876 c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i]));
2773 defer r.deinit();1877 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
2774 try Int.divTrunc(&q, &r, a, b);1878 carry = c;
1879 }
27751880
2776 const qs = try q.toString(testing.allocator, 16, false);1881 while ((carry != 0) and i < r.len) : (i += 1) {
2777 defer testing.allocator.free(qs);1882 carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
2778 testing.expect(std.mem.eql(u8, qs, "40100400fe3f8fe3f8fe3f8fe3f8fe3f8fe4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f91e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4992649926499264991e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4792e4b92e4b92e4b92e4b92a4a92a4a92a4"));1883 }
27791884
2780 const rs = try r.toString(testing.allocator, 16, false);1885 return carry;
2781 defer testing.allocator.free(rs);
2782 testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000"));
2783}1886}
27841887
2785test "big.int shift-right single" {1888/// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs.
2786 var a = try Int.initSet(testing.allocator, 0xffff0000);1889pub fn llcmp(a: []const Limb, b: []const Limb) i8 {
2787 defer a.deinit();1890 @setRuntimeSafety(false);
2788 try a.shiftRight(a, 16);1891 const a_len = llnormalize(a);
27891892 const b_len = llnormalize(b);
2790 testing.expect((try a.to(u32)) == 0xffff);1893 if (a_len < b_len) {
2791}1894 return -1;
1895 }
1896 if (a_len > b_len) {
1897 return 1;
1898 }
27921899
2793test "big.int shift-right multi" {1900 var i: usize = a_len - 1;
2794 var a = try Int.initSet(testing.allocator, 0xffff0000eeee1111dddd2222cccc3333);1901 while (i != 0) : (i -= 1) {
2795 defer a.deinit();1902 if (a[i] != b[i]) {
2796 try a.shiftRight(a, 67);1903 break;
1904 }
1905 }
27971906
2798 testing.expect((try a.to(u64)) == 0x1fffe0001dddc222);1907 if (a[i] < b[i]) {
1908 return -1;
1909 } else if (a[i] > b[i]) {
1910 return 1;
1911 } else {
1912 return 0;
1913 }
2799}1914}
28001915
2801test "big.int shift-left single" {1916fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void {
2802 var a = try Int.initSet(testing.allocator, 0xffff);1917 @setRuntimeSafety(false);
2803 defer a.deinit();1918 if (xi == 0) {
2804 try a.shiftLeft(a, 16);1919 return;
1920 }
28051921
2806 testing.expect((try a.to(u64)) == 0xffff0000);1922 var carry: usize = 0;
2807}1923 var a_lo = acc[0..y.len];
1924 var a_hi = acc[y.len..];
28081925
2809test "big.int shift-left multi" {1926 var j: usize = 0;
2810 var a = try Int.initSet(testing.allocator, 0x1fffe0001dddc222);1927 while (j < a_lo.len) : (j += 1) {
2811 defer a.deinit();1928 a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry });
2812 try a.shiftLeft(a, 67);1929 }
28131930
2814 testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000);1931 j = 0;
1932 while ((carry != 0) and (j < a_hi.len)) : (j += 1) {
1933 carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));
1934 }
2815}1935}
28161936
2817test "big.int shift-right negative" {1937/// returns the min length the limb could be.
2818 var a = try Int.init(testing.allocator);1938fn llnormalize(a: []const Limb) usize {
2819 defer a.deinit();1939 @setRuntimeSafety(false);
28201940 var j = a.len;
2821 try a.shiftRight(try Int.initSet(testing.allocator, -20), 2);1941 while (j > 0) : (j -= 1) {
2822 defer a.deinit();1942 if (a[j - 1] != 0) {
2823 testing.expect((try a.to(i32)) == -20 >> 2);1943 break;
1944 }
1945 }
28241946
2825 try a.shiftRight(try Int.initSet(testing.allocator, -5), 10);1947 // Handle zero
2826 defer a.deinit();1948 return if (j != 0) j else 1;
2827 testing.expect((try a.to(i32)) == -5 >> 10);
2828}1949}
28291950
2830test "big.int shift-left negative" {1951/// Knuth 4.3.1, Algorithm S.
2831 var a = try Int.init(testing.allocator);1952fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
2832 defer a.deinit();1953 @setRuntimeSafety(false);
1954 assert(a.len != 0 and b.len != 0);
1955 assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));
1956 assert(r.len >= a.len);
28331957
2834 try a.shiftRight(try Int.initSet(testing.allocator, -10), 1232);1958 var i: usize = 0;
2835 defer a.deinit();1959 var borrow: Limb = 0;
2836 testing.expect((try a.to(i32)) == -10 >> 1232);
2837}
28381960
2839test "big.int bitwise and simple" {1961 while (i < b.len) : (i += 1) {
2840 var a = try Int.initSet(testing.allocator, 0xffffffff11111111);1962 var c: Limb = 0;
2841 defer a.deinit();1963 c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i]));
2842 var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222);1964 c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i]));
2843 defer b.deinit();1965 borrow = c;
1966 }
28441967
2845 try a.bitAnd(a, b);1968 while (i < a.len) : (i += 1) {
1969 borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i]));
1970 }
28461971
2847 testing.expect((try a.to(u64)) == 0xeeeeeeee00000000);1972 assert(borrow == 0);
2848}1973}
28491974
2850test "big.int bitwise and multi-limb" {1975/// Knuth 4.3.1, Algorithm A.
2851 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);1976fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
2852 defer a.deinit();1977 @setRuntimeSafety(false);
2853 var b = try Int.initSet(testing.allocator, maxInt(Limb));1978 assert(a.len != 0 and b.len != 0);
2854 defer b.deinit();1979 assert(a.len >= b.len);
28551980 assert(r.len >= a.len + 1);
2856 try a.bitAnd(a, b);
28571981
2858 testing.expect((try a.to(u128)) == 0);1982 var i: usize = 0;
2859}1983 var carry: Limb = 0;
28601984
2861test "big.int bitwise xor simple" {1985 while (i < b.len) : (i += 1) {
2862 var a = try Int.initSet(testing.allocator, 0xffffffff11111111);1986 var c: Limb = 0;
2863 defer a.deinit();1987 c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i]));
2864 var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222);1988 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
2865 defer b.deinit();1989 carry = c;
1990 }
28661991
2867 try a.bitXor(a, b);1992 while (i < a.len) : (i += 1) {
1993 carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i]));
1994 }
28681995
2869 testing.expect((try a.to(u64)) == 0x1111111133333333);1996 r[i] = carry;
2870}1997}
28711998
2872test "big.int bitwise xor multi-limb" {1999/// Knuth 4.3.1, Exercise 16.
2873 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);2000fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
2874 defer a.deinit();2001 @setRuntimeSafety(false);
2875 var b = try Int.initSet(testing.allocator, maxInt(Limb));2002 assert(a.len > 1 or a[0] >= b);
2876 defer b.deinit();2003 assert(quo.len >= a.len);
28772004
2878 try a.bitXor(a, b);2005 rem.* = 0;
2006 for (a) |_, ri| {
2007 const i = a.len - ri - 1;
2008 const pdiv = ((@as(DoubleLimb, rem.*) << Limb.bit_count) | a[i]);
28792009
2880 testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb));2010 if (pdiv == 0) {
2011 quo[i] = 0;
2012 rem.* = 0;
2013 } else if (pdiv < b) {
2014 quo[i] = 0;
2015 rem.* = @truncate(Limb, pdiv);
2016 } else if (pdiv == b) {
2017 quo[i] = 1;
2018 rem.* = 0;
2019 } else {
2020 quo[i] = @truncate(Limb, @divTrunc(pdiv, b));
2021 rem.* = @truncate(Limb, pdiv - (quo[i] *% b));
2022 }
2023 }
2881}2024}
28822025
2883test "big.int bitwise or simple" {2026fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
2884 var a = try Int.initSet(testing.allocator, 0xffffffff11111111);2027 @setRuntimeSafety(false);
2885 defer a.deinit();2028 assert(a.len >= 1);
2886 var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222);2029 assert(r.len >= a.len + (shift / Limb.bit_count) + 1);
2887 defer b.deinit();
28882030
2889 try a.bitOr(a, b);2031 const limb_shift = shift / Limb.bit_count + 1;
2032 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
28902033
2891 testing.expect((try a.to(u64)) == 0xffffffff33333333);2034 var carry: Limb = 0;
2892}2035 var i: usize = 0;
28932036 while (i < a.len) : (i += 1) {
2894test "big.int bitwise or multi-limb" {2037 const src_i = a.len - i - 1;
2895 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);2038 const dst_i = src_i + limb_shift;
2896 defer a.deinit();
2897 var b = try Int.initSet(testing.allocator, maxInt(Limb));
2898 defer b.deinit();
28992039
2900 try a.bitOr(a, b);2040 const src_digit = a[src_i];
2041 r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{
2042 Limb,
2043 src_digit,
2044 Limb.bit_count - @intCast(Limb, interior_limb_shift),
2045 });
2046 carry = (src_digit << interior_limb_shift);
2047 }
29012048
2902 // TODO: big.int.cpp or is wrong on multi-limb.2049 r[limb_shift - 1] = carry;
2903 testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));2050 mem.set(Limb, r[0 .. limb_shift - 1], 0);
2904}2051}
29052052
2906test "big.int var args" {2053fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
2907 var a = try Int.initSet(testing.allocator, 5);2054 @setRuntimeSafety(false);
2908 defer a.deinit();2055 assert(a.len >= 1);
2056 assert(r.len >= a.len - (shift / Limb.bit_count));
29092057
2910 const b = try Int.initSet(testing.allocator, 6);2058 const limb_shift = shift / Limb.bit_count;
2911 defer b.deinit();2059 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
2912 try a.add(a, b);
2913 testing.expect((try a.to(u64)) == 11);
29142060
2915 const c = try Int.initSet(testing.allocator, 11);2061 var carry: Limb = 0;
2916 defer c.deinit();2062 var i: usize = 0;
2917 testing.expect(a.cmp(c) == .eq);2063 while (i < a.len - limb_shift) : (i += 1) {
2064 const src_i = a.len - i - 1;
2065 const dst_i = src_i - limb_shift;
29182066
2919 const d = try Int.initSet(testing.allocator, 14);2067 const src_digit = a[src_i];
2920 defer d.deinit();2068 r[dst_i] = carry | (src_digit >> interior_limb_shift);
2921 testing.expect(a.cmp(d) != .gt);2069 carry = @call(.{ .modifier = .always_inline }, math.shl, .{
2070 Limb,
2071 src_digit,
2072 Limb.bit_count - @intCast(Limb, interior_limb_shift),
2073 });
2074 }
2922}2075}
29232076
2924test "big.int gcd non-one small" {2077fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {
2925 var a = try Int.initSet(testing.allocator, 17);2078 @setRuntimeSafety(false);
2926 defer a.deinit();2079 assert(r.len >= a.len);
2927 var b = try Int.initSet(testing.allocator, 97);2080 assert(a.len >= b.len);
2928 defer b.deinit();
2929 var r = try Int.init(testing.allocator);
2930 defer r.deinit();
29312081
2932 try r.gcd(a, b);2082 var i: usize = 0;
29332083 while (i < b.len) : (i += 1) {
2934 testing.expect((try r.to(u32)) == 1);2084 r[i] = a[i] | b[i];
2085 }
2086 while (i < a.len) : (i += 1) {
2087 r[i] = a[i];
2088 }
2935}2089}
29362090
2937test "big.int gcd non-one small" {2091fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {
2938 var a = try Int.initSet(testing.allocator, 4864);2092 @setRuntimeSafety(false);
2939 defer a.deinit();2093 assert(r.len >= b.len);
2940 var b = try Int.initSet(testing.allocator, 3458);2094 assert(a.len >= b.len);
2941 defer b.deinit();
2942 var r = try Int.init(testing.allocator);
2943 defer r.deinit();
2944
2945 try r.gcd(a, b);
29462095
2947 testing.expect((try r.to(u32)) == 38);2096 var i: usize = 0;
2097 while (i < b.len) : (i += 1) {
2098 r[i] = a[i] & b[i];
2099 }
2948}2100}
29492101
2950test "big.int gcd non-one large" {2102fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
2951 var a = try Int.initSet(testing.allocator, 0xffffffffffffffff);2103 assert(r.len >= a.len);
2952 defer a.deinit();2104 assert(a.len >= b.len);
2953 var b = try Int.initSet(testing.allocator, 0xffffffffffffffff7777);
2954 defer b.deinit();
2955 var r = try Int.init(testing.allocator);
2956 defer r.deinit();
2957
2958 try r.gcd(a, b);
29592105
2960 testing.expect((try r.to(u32)) == 4369);2106 var i: usize = 0;
2107 while (i < b.len) : (i += 1) {
2108 r[i] = a[i] ^ b[i];
2109 }
2110 while (i < a.len) : (i += 1) {
2111 r[i] = a[i];
2112 }
2961}2113}
29622114
2963test "big.int gcd large multi-limb result" {2115// Storage must live for the lifetime of the returned value
2964 var a = try Int.initSet(testing.allocator, 0x12345678123456781234567812345678123456781234567812345678);2116fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable {
2965 defer a.deinit();2117 assert(storage.len >= 2);
2966 var b = try Int.initSet(testing.allocator, 0x12345671234567123456712345671234567123456712345671234567);
2967 defer b.deinit();
2968 var r = try Int.init(testing.allocator);
2969 defer r.deinit();
2970
2971 try r.gcd(a, b);
29722118
2973 testing.expect((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1);2119 const A_is_positive = A >= 0;
2120 const Au = @intCast(DoubleLimb, if (A < 0) -A else A);
2121 storage[0] = @truncate(Limb, Au);
2122 storage[1] = @truncate(Limb, Au >> Limb.bit_count);
2123 return .{
2124 .limbs = storage[0..2],
2125 .positive = A_is_positive,
2126 .len = 2,
2127 };
2974}2128}
29752129
2976test "big.int gcd one large" {2130test "" {
2977 var a = try Int.initSet(testing.allocator, 1897056385327307);2131 _ = @import("int_test.zig");
2978 defer a.deinit();
2979 var b = try Int.initSet(testing.allocator, 2251799813685248);
2980 defer b.deinit();
2981 var r = try Int.init(testing.allocator);
2982 defer r.deinit();
2983
2984 try r.gcd(a, b);
2985
2986 testing.expect((try r.to(u64)) == 1);
2987}2132}
lib/std/math/big/int_test.zig created+1455
...@@ -0,0 +1,1455 @@
1const std = @import("../../std.zig");
2const mem = std.mem;
3const testing = std.testing;
4const Managed = std.math.big.int.Managed;
5const Limb = std.math.big.Limb;
6const DoubleLimb = std.math.big.DoubleLimb;
7const maxInt = std.math.maxInt;
8const minInt = std.math.minInt;
9
10// NOTE: All the following tests assume the max machine-word will be 64-bit.
11//
12// They will still run on larger than this and should pass, but the multi-limb code-paths
13// may be untested in some cases.
14
15test "big.int comptime_int set" {
16 comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab;
17 var a = try Managed.initSet(testing.allocator, s);
18 defer a.deinit();
19
20 const s_limb_count = 128 / Limb.bit_count;
21
22 comptime var i: usize = 0;
23 inline while (i < s_limb_count) : (i += 1) {
24 const result = @as(Limb, s & maxInt(Limb));
25 s >>= Limb.bit_count / 2;
26 s >>= Limb.bit_count / 2;
27 testing.expect(a.limbs[i] == result);
28 }
29}
30
31test "big.int comptime_int set negative" {
32 var a = try Managed.initSet(testing.allocator, -10);
33 defer a.deinit();
34
35 testing.expect(a.limbs[0] == 10);
36 testing.expect(a.isPositive() == false);
37}
38
39test "big.int int set unaligned small" {
40 var a = try Managed.initSet(testing.allocator, @as(u7, 45));
41 defer a.deinit();
42
43 testing.expect(a.limbs[0] == 45);
44 testing.expect(a.isPositive() == true);
45}
46
47test "big.int comptime_int to" {
48 var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab);
49 defer a.deinit();
50
51 testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab);
52}
53
54test "big.int sub-limb to" {
55 var a = try Managed.initSet(testing.allocator, 10);
56 defer a.deinit();
57
58 testing.expect((try a.to(u8)) == 10);
59}
60
61test "big.int to target too small error" {
62 var a = try Managed.initSet(testing.allocator, 0xffffffff);
63 defer a.deinit();
64
65 testing.expectError(error.TargetTooSmall, a.to(u8));
66}
67
68test "big.int normalize" {
69 var a = try Managed.init(testing.allocator);
70 defer a.deinit();
71 try a.ensureCapacity(8);
72
73 a.limbs[0] = 1;
74 a.limbs[1] = 2;
75 a.limbs[2] = 3;
76 a.limbs[3] = 0;
77 a.normalize(4);
78 testing.expect(a.len() == 3);
79
80 a.limbs[0] = 1;
81 a.limbs[1] = 2;
82 a.limbs[2] = 3;
83 a.normalize(3);
84 testing.expect(a.len() == 3);
85
86 a.limbs[0] = 0;
87 a.limbs[1] = 0;
88 a.normalize(2);
89 testing.expect(a.len() == 1);
90
91 a.limbs[0] = 0;
92 a.normalize(1);
93 testing.expect(a.len() == 1);
94}
95
96test "big.int normalize multi" {
97 var a = try Managed.init(testing.allocator);
98 defer a.deinit();
99 try a.ensureCapacity(8);
100
101 a.limbs[0] = 1;
102 a.limbs[1] = 2;
103 a.limbs[2] = 0;
104 a.limbs[3] = 0;
105 a.normalize(4);
106 testing.expect(a.len() == 2);
107
108 a.limbs[0] = 1;
109 a.limbs[1] = 2;
110 a.limbs[2] = 3;
111 a.normalize(3);
112 testing.expect(a.len() == 3);
113
114 a.limbs[0] = 0;
115 a.limbs[1] = 0;
116 a.limbs[2] = 0;
117 a.limbs[3] = 0;
118 a.normalize(4);
119 testing.expect(a.len() == 1);
120
121 a.limbs[0] = 0;
122 a.normalize(1);
123 testing.expect(a.len() == 1);
124}
125
126test "big.int parity" {
127 var a = try Managed.init(testing.allocator);
128 defer a.deinit();
129
130 try a.set(0);
131 testing.expect(a.isEven());
132 testing.expect(!a.isOdd());
133
134 try a.set(7);
135 testing.expect(!a.isEven());
136 testing.expect(a.isOdd());
137}
138
139test "big.int bitcount + sizeInBaseUpperBound" {
140 var a = try Managed.init(testing.allocator);
141 defer a.deinit();
142
143 try a.set(0b100);
144 testing.expect(a.bitCountAbs() == 3);
145 testing.expect(a.sizeInBaseUpperBound(2) >= 3);
146 testing.expect(a.sizeInBaseUpperBound(10) >= 1);
147
148 a.negate();
149 testing.expect(a.bitCountAbs() == 3);
150 testing.expect(a.sizeInBaseUpperBound(2) >= 4);
151 testing.expect(a.sizeInBaseUpperBound(10) >= 2);
152
153 try a.set(0xffffffff);
154 testing.expect(a.bitCountAbs() == 32);
155 testing.expect(a.sizeInBaseUpperBound(2) >= 32);
156 testing.expect(a.sizeInBaseUpperBound(10) >= 10);
157
158 try a.shiftLeft(a, 5000);
159 testing.expect(a.bitCountAbs() == 5032);
160 testing.expect(a.sizeInBaseUpperBound(2) >= 5032);
161 a.setSign(false);
162
163 testing.expect(a.bitCountAbs() == 5032);
164 testing.expect(a.sizeInBaseUpperBound(2) >= 5033);
165}
166
167test "big.int bitcount/to" {
168 var a = try Managed.init(testing.allocator);
169 defer a.deinit();
170
171 try a.set(0);
172 testing.expect(a.bitCountTwosComp() == 0);
173
174 testing.expect((try a.to(u0)) == 0);
175 testing.expect((try a.to(i0)) == 0);
176
177 try a.set(-1);
178 testing.expect(a.bitCountTwosComp() == 1);
179 testing.expect((try a.to(i1)) == -1);
180
181 try a.set(-8);
182 testing.expect(a.bitCountTwosComp() == 4);
183 testing.expect((try a.to(i4)) == -8);
184
185 try a.set(127);
186 testing.expect(a.bitCountTwosComp() == 7);
187 testing.expect((try a.to(u7)) == 127);
188
189 try a.set(-128);
190 testing.expect(a.bitCountTwosComp() == 8);
191 testing.expect((try a.to(i8)) == -128);
192
193 try a.set(-129);
194 testing.expect(a.bitCountTwosComp() == 9);
195 testing.expect((try a.to(i9)) == -129);
196}
197
198test "big.int fits" {
199 var a = try Managed.init(testing.allocator);
200 defer a.deinit();
201
202 try a.set(0);
203 testing.expect(a.fits(u0));
204 testing.expect(a.fits(i0));
205
206 try a.set(255);
207 testing.expect(!a.fits(u0));
208 testing.expect(!a.fits(u1));
209 testing.expect(!a.fits(i8));
210 testing.expect(a.fits(u8));
211 testing.expect(a.fits(u9));
212 testing.expect(a.fits(i9));
213
214 try a.set(-128);
215 testing.expect(!a.fits(i7));
216 testing.expect(a.fits(i8));
217 testing.expect(a.fits(i9));
218 testing.expect(!a.fits(u9));
219
220 try a.set(0x1ffffffffeeeeeeee);
221 testing.expect(!a.fits(u32));
222 testing.expect(!a.fits(u64));
223 testing.expect(a.fits(u65));
224}
225
226test "big.int string set" {
227 var a = try Managed.init(testing.allocator);
228 defer a.deinit();
229
230 try a.setString(10, "120317241209124781241290847124");
231 testing.expect((try a.to(u128)) == 120317241209124781241290847124);
232}
233
234test "big.int string negative" {
235 var a = try Managed.init(testing.allocator);
236 defer a.deinit();
237
238 try a.setString(10, "-1023");
239 testing.expect((try a.to(i32)) == -1023);
240}
241
242test "big.int string set number with underscores" {
243 var a = try Managed.init(testing.allocator);
244 defer a.deinit();
245
246 try a.setString(10, "__1_2_0_3_1_7_2_4_1_2_0_____9_1__2__4_7_8_1_2_4_1_2_9_0_8_4_7_1_2_4___");
247 testing.expect((try a.to(u128)) == 120317241209124781241290847124);
248}
249
250test "big.int string set case insensitive number" {
251 var a = try Managed.init(testing.allocator);
252 defer a.deinit();
253
254 try a.setString(16, "aB_cD_eF");
255 testing.expect((try a.to(u32)) == 0xabcdef);
256}
257
258test "big.int string set bad char error" {
259 var a = try Managed.init(testing.allocator);
260 defer a.deinit();
261 testing.expectError(error.InvalidCharacter, a.setString(10, "x"));
262}
263
264test "big.int string set bad base error" {
265 var a = try Managed.init(testing.allocator);
266 defer a.deinit();
267 testing.expectError(error.InvalidBase, a.setString(45, "10"));
268}
269
270test "big.int string to" {
271 var a = try Managed.initSet(testing.allocator, 120317241209124781241290847124);
272 defer a.deinit();
273
274 const as = try a.toString(testing.allocator, 10, false);
275 defer testing.allocator.free(as);
276 const es = "120317241209124781241290847124";
277
278 testing.expect(mem.eql(u8, as, es));
279}
280
281test "big.int string to base base error" {
282 var a = try Managed.initSet(testing.allocator, 0xffffffff);
283 defer a.deinit();
284
285 testing.expectError(error.InvalidBase, a.toString(testing.allocator, 45, false));
286}
287
288test "big.int string to base 2" {
289 var a = try Managed.initSet(testing.allocator, -0b1011);
290 defer a.deinit();
291
292 const as = try a.toString(testing.allocator, 2, false);
293 defer testing.allocator.free(as);
294 const es = "-1011";
295
296 testing.expect(mem.eql(u8, as, es));
297}
298
299test "big.int string to base 16" {
300 var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab);
301 defer a.deinit();
302
303 const as = try a.toString(testing.allocator, 16, false);
304 defer testing.allocator.free(as);
305 const es = "efffffff00000001eeeeeeefaaaaaaab";
306
307 testing.expect(mem.eql(u8, as, es));
308}
309
310test "big.int neg string to" {
311 var a = try Managed.initSet(testing.allocator, -123907434);
312 defer a.deinit();
313
314 const as = try a.toString(testing.allocator, 10, false);
315 defer testing.allocator.free(as);
316 const es = "-123907434";
317
318 testing.expect(mem.eql(u8, as, es));
319}
320
321test "big.int zero string to" {
322 var a = try Managed.initSet(testing.allocator, 0);
323 defer a.deinit();
324
325 const as = try a.toString(testing.allocator, 10, false);
326 defer testing.allocator.free(as);
327 const es = "0";
328
329 testing.expect(mem.eql(u8, as, es));
330}
331
332test "big.int clone" {
333 var a = try Managed.initSet(testing.allocator, 1234);
334 defer a.deinit();
335 var b = try a.clone();
336 defer b.deinit();
337
338 testing.expect((try a.to(u32)) == 1234);
339 testing.expect((try b.to(u32)) == 1234);
340
341 try a.set(77);
342 testing.expect((try a.to(u32)) == 77);
343 testing.expect((try b.to(u32)) == 1234);
344}
345
346test "big.int swap" {
347 var a = try Managed.initSet(testing.allocator, 1234);
348 defer a.deinit();
349 var b = try Managed.initSet(testing.allocator, 5678);
350 defer b.deinit();
351
352 testing.expect((try a.to(u32)) == 1234);
353 testing.expect((try b.to(u32)) == 5678);
354
355 a.swap(&b);
356
357 testing.expect((try a.to(u32)) == 5678);
358 testing.expect((try b.to(u32)) == 1234);
359}
360
361test "big.int to negative" {
362 var a = try Managed.initSet(testing.allocator, -10);
363 defer a.deinit();
364
365 testing.expect((try a.to(i32)) == -10);
366}
367
368test "big.int compare" {
369 var a = try Managed.initSet(testing.allocator, -11);
370 defer a.deinit();
371 var b = try Managed.initSet(testing.allocator, 10);
372 defer b.deinit();
373
374 testing.expect(a.orderAbs(b) == .gt);
375 testing.expect(a.order(b) == .lt);
376}
377
378test "big.int compare similar" {
379 var a = try Managed.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeee);
380 defer a.deinit();
381 var b = try Managed.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeef);
382 defer b.deinit();
383
384 testing.expect(a.orderAbs(b) == .lt);
385 testing.expect(b.orderAbs(a) == .gt);
386}
387
388test "big.int compare different limb size" {
389 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
390 defer a.deinit();
391 var b = try Managed.initSet(testing.allocator, 1);
392 defer b.deinit();
393
394 testing.expect(a.orderAbs(b) == .gt);
395 testing.expect(b.orderAbs(a) == .lt);
396}
397
398test "big.int compare multi-limb" {
399 var a = try Managed.initSet(testing.allocator, -0x7777777799999999ffffeeeeffffeeeeffffeeeef);
400 defer a.deinit();
401 var b = try Managed.initSet(testing.allocator, 0x7777777799999999ffffeeeeffffeeeeffffeeeee);
402 defer b.deinit();
403
404 testing.expect(a.orderAbs(b) == .gt);
405 testing.expect(a.order(b) == .lt);
406}
407
408test "big.int equality" {
409 var a = try Managed.initSet(testing.allocator, 0xffffffff1);
410 defer a.deinit();
411 var b = try Managed.initSet(testing.allocator, -0xffffffff1);
412 defer b.deinit();
413
414 testing.expect(a.eqAbs(b));
415 testing.expect(!a.eq(b));
416}
417
418test "big.int abs" {
419 var a = try Managed.initSet(testing.allocator, -5);
420 defer a.deinit();
421
422 a.abs();
423 testing.expect((try a.to(u32)) == 5);
424
425 a.abs();
426 testing.expect((try a.to(u32)) == 5);
427}
428
429test "big.int negate" {
430 var a = try Managed.initSet(testing.allocator, 5);
431 defer a.deinit();
432
433 a.negate();
434 testing.expect((try a.to(i32)) == -5);
435
436 a.negate();
437 testing.expect((try a.to(i32)) == 5);
438}
439
440test "big.int add single-single" {
441 var a = try Managed.initSet(testing.allocator, 50);
442 defer a.deinit();
443 var b = try Managed.initSet(testing.allocator, 5);
444 defer b.deinit();
445
446 var c = try Managed.init(testing.allocator);
447 defer c.deinit();
448 try c.add(a.toConst(), b.toConst());
449
450 testing.expect((try c.to(u32)) == 55);
451}
452
453test "big.int add multi-single" {
454 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
455 defer a.deinit();
456 var b = try Managed.initSet(testing.allocator, 1);
457 defer b.deinit();
458
459 var c = try Managed.init(testing.allocator);
460 defer c.deinit();
461
462 try c.add(a.toConst(), b.toConst());
463 testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
464
465 try c.add(b.toConst(), a.toConst());
466 testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
467}
468
469test "big.int add multi-multi" {
470 const op1 = 0xefefefef7f7f7f7f;
471 const op2 = 0xfefefefe9f9f9f9f;
472 var a = try Managed.initSet(testing.allocator, op1);
473 defer a.deinit();
474 var b = try Managed.initSet(testing.allocator, op2);
475 defer b.deinit();
476
477 var c = try Managed.init(testing.allocator);
478 defer c.deinit();
479 try c.add(a.toConst(), b.toConst());
480
481 testing.expect((try c.to(u128)) == op1 + op2);
482}
483
484test "big.int add zero-zero" {
485 var a = try Managed.initSet(testing.allocator, 0);
486 defer a.deinit();
487 var b = try Managed.initSet(testing.allocator, 0);
488 defer b.deinit();
489
490 var c = try Managed.init(testing.allocator);
491 defer c.deinit();
492 try c.add(a.toConst(), b.toConst());
493
494 testing.expect((try c.to(u32)) == 0);
495}
496
497test "big.int add alias multi-limb nonzero-zero" {
498 const op1 = 0xffffffff777777771;
499 var a = try Managed.initSet(testing.allocator, op1);
500 defer a.deinit();
501 var b = try Managed.initSet(testing.allocator, 0);
502 defer b.deinit();
503
504 try a.add(a.toConst(), b.toConst());
505
506 testing.expect((try a.to(u128)) == op1);
507}
508
509test "big.int add sign" {
510 var a = try Managed.init(testing.allocator);
511 defer a.deinit();
512
513 var one = try Managed.initSet(testing.allocator, 1);
514 defer one.deinit();
515 var two = try Managed.initSet(testing.allocator, 2);
516 defer two.deinit();
517 var neg_one = try Managed.initSet(testing.allocator, -1);
518 defer neg_one.deinit();
519 var neg_two = try Managed.initSet(testing.allocator, -2);
520 defer neg_two.deinit();
521
522 try a.add(one.toConst(), two.toConst());
523 testing.expect((try a.to(i32)) == 3);
524
525 try a.add(neg_one.toConst(), two.toConst());
526 testing.expect((try a.to(i32)) == 1);
527
528 try a.add(one.toConst(), neg_two.toConst());
529 testing.expect((try a.to(i32)) == -1);
530
531 try a.add(neg_one.toConst(), neg_two.toConst());
532 testing.expect((try a.to(i32)) == -3);
533}
534
535test "big.int sub single-single" {
536 var a = try Managed.initSet(testing.allocator, 50);
537 defer a.deinit();
538 var b = try Managed.initSet(testing.allocator, 5);
539 defer b.deinit();
540
541 var c = try Managed.init(testing.allocator);
542 defer c.deinit();
543 try c.sub(a.toConst(), b.toConst());
544
545 testing.expect((try c.to(u32)) == 45);
546}
547
548test "big.int sub multi-single" {
549 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
550 defer a.deinit();
551 var b = try Managed.initSet(testing.allocator, 1);
552 defer b.deinit();
553
554 var c = try Managed.init(testing.allocator);
555 defer c.deinit();
556 try c.sub(a.toConst(), b.toConst());
557
558 testing.expect((try c.to(Limb)) == maxInt(Limb));
559}
560
561test "big.int sub multi-multi" {
562 const op1 = 0xefefefefefefefefefefefef;
563 const op2 = 0xabababababababababababab;
564
565 var a = try Managed.initSet(testing.allocator, op1);
566 defer a.deinit();
567 var b = try Managed.initSet(testing.allocator, op2);
568 defer b.deinit();
569
570 var c = try Managed.init(testing.allocator);
571 defer c.deinit();
572 try c.sub(a.toConst(), b.toConst());
573
574 testing.expect((try c.to(u128)) == op1 - op2);
575}
576
577test "big.int sub equal" {
578 var a = try Managed.initSet(testing.allocator, 0x11efefefefefefefefefefefef);
579 defer a.deinit();
580 var b = try Managed.initSet(testing.allocator, 0x11efefefefefefefefefefefef);
581 defer b.deinit();
582
583 var c = try Managed.init(testing.allocator);
584 defer c.deinit();
585 try c.sub(a.toConst(), b.toConst());
586
587 testing.expect((try c.to(u32)) == 0);
588}
589
590test "big.int sub sign" {
591 var a = try Managed.init(testing.allocator);
592 defer a.deinit();
593
594 var one = try Managed.initSet(testing.allocator, 1);
595 defer one.deinit();
596 var two = try Managed.initSet(testing.allocator, 2);
597 defer two.deinit();
598 var neg_one = try Managed.initSet(testing.allocator, -1);
599 defer neg_one.deinit();
600 var neg_two = try Managed.initSet(testing.allocator, -2);
601 defer neg_two.deinit();
602
603 try a.sub(one.toConst(), two.toConst());
604 testing.expect((try a.to(i32)) == -1);
605
606 try a.sub(neg_one.toConst(), two.toConst());
607 testing.expect((try a.to(i32)) == -3);
608
609 try a.sub(one.toConst(), neg_two.toConst());
610 testing.expect((try a.to(i32)) == 3);
611
612 try a.sub(neg_one.toConst(), neg_two.toConst());
613 testing.expect((try a.to(i32)) == 1);
614
615 try a.sub(neg_two.toConst(), neg_one.toConst());
616 testing.expect((try a.to(i32)) == -1);
617}
618
619test "big.int mul single-single" {
620 var a = try Managed.initSet(testing.allocator, 50);
621 defer a.deinit();
622 var b = try Managed.initSet(testing.allocator, 5);
623 defer b.deinit();
624
625 var c = try Managed.init(testing.allocator);
626 defer c.deinit();
627 try c.mul(a.toConst(), b.toConst());
628
629 testing.expect((try c.to(u64)) == 250);
630}
631
632test "big.int mul multi-single" {
633 var a = try Managed.initSet(testing.allocator, maxInt(Limb));
634 defer a.deinit();
635 var b = try Managed.initSet(testing.allocator, 2);
636 defer b.deinit();
637
638 var c = try Managed.init(testing.allocator);
639 defer c.deinit();
640 try c.mul(a.toConst(), b.toConst());
641
642 testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb));
643}
644
645test "big.int mul multi-multi" {
646 const op1 = 0x998888efefefefefefefef;
647 const op2 = 0x333000abababababababab;
648 var a = try Managed.initSet(testing.allocator, op1);
649 defer a.deinit();
650 var b = try Managed.initSet(testing.allocator, op2);
651 defer b.deinit();
652
653 var c = try Managed.init(testing.allocator);
654 defer c.deinit();
655 try c.mul(a.toConst(), b.toConst());
656
657 testing.expect((try c.to(u256)) == op1 * op2);
658}
659
660test "big.int mul alias r with a" {
661 var a = try Managed.initSet(testing.allocator, maxInt(Limb));
662 defer a.deinit();
663 var b = try Managed.initSet(testing.allocator, 2);
664 defer b.deinit();
665
666 try a.mul(a.toConst(), b.toConst());
667
668 testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
669}
670
671test "big.int mul alias r with b" {
672 var a = try Managed.initSet(testing.allocator, maxInt(Limb));
673 defer a.deinit();
674 var b = try Managed.initSet(testing.allocator, 2);
675 defer b.deinit();
676
677 try a.mul(b.toConst(), a.toConst());
678
679 testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
680}
681
682test "big.int mul alias r with a and b" {
683 var a = try Managed.initSet(testing.allocator, maxInt(Limb));
684 defer a.deinit();
685
686 try a.mul(a.toConst(), a.toConst());
687
688 testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb));
689}
690
691test "big.int mul a*0" {
692 var a = try Managed.initSet(testing.allocator, 0xefefefefefefefef);
693 defer a.deinit();
694 var b = try Managed.initSet(testing.allocator, 0);
695 defer b.deinit();
696
697 var c = try Managed.init(testing.allocator);
698 defer c.deinit();
699 try c.mul(a.toConst(), b.toConst());
700
701 testing.expect((try c.to(u32)) == 0);
702}
703
704test "big.int mul 0*0" {
705 var a = try Managed.initSet(testing.allocator, 0);
706 defer a.deinit();
707 var b = try Managed.initSet(testing.allocator, 0);
708 defer b.deinit();
709
710 var c = try Managed.init(testing.allocator);
711 defer c.deinit();
712 try c.mul(a.toConst(), b.toConst());
713
714 testing.expect((try c.to(u32)) == 0);
715}
716
717test "big.int div single-single no rem" {
718 var a = try Managed.initSet(testing.allocator, 50);
719 defer a.deinit();
720 var b = try Managed.initSet(testing.allocator, 5);
721 defer b.deinit();
722
723 var q = try Managed.init(testing.allocator);
724 defer q.deinit();
725 var r = try Managed.init(testing.allocator);
726 defer r.deinit();
727 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
728
729 testing.expect((try q.to(u32)) == 10);
730 testing.expect((try r.to(u32)) == 0);
731}
732
733test "big.int div single-single with rem" {
734 var a = try Managed.initSet(testing.allocator, 49);
735 defer a.deinit();
736 var b = try Managed.initSet(testing.allocator, 5);
737 defer b.deinit();
738
739 var q = try Managed.init(testing.allocator);
740 defer q.deinit();
741 var r = try Managed.init(testing.allocator);
742 defer r.deinit();
743 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
744
745 testing.expect((try q.to(u32)) == 9);
746 testing.expect((try r.to(u32)) == 4);
747}
748
749test "big.int div multi-single no rem" {
750 const op1 = 0xffffeeeeddddcccc;
751 const op2 = 34;
752
753 var a = try Managed.initSet(testing.allocator, op1);
754 defer a.deinit();
755 var b = try Managed.initSet(testing.allocator, op2);
756 defer b.deinit();
757
758 var q = try Managed.init(testing.allocator);
759 defer q.deinit();
760 var r = try Managed.init(testing.allocator);
761 defer r.deinit();
762 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
763
764 testing.expect((try q.to(u64)) == op1 / op2);
765 testing.expect((try r.to(u64)) == 0);
766}
767
768test "big.int div multi-single with rem" {
769 const op1 = 0xffffeeeeddddcccf;
770 const op2 = 34;
771
772 var a = try Managed.initSet(testing.allocator, op1);
773 defer a.deinit();
774 var b = try Managed.initSet(testing.allocator, op2);
775 defer b.deinit();
776
777 var q = try Managed.init(testing.allocator);
778 defer q.deinit();
779 var r = try Managed.init(testing.allocator);
780 defer r.deinit();
781 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
782
783 testing.expect((try q.to(u64)) == op1 / op2);
784 testing.expect((try r.to(u64)) == 3);
785}
786
787test "big.int div multi>2-single" {
788 const op1 = 0xfefefefefefefefefefefefefefefefe;
789 const op2 = 0xefab8;
790
791 var a = try Managed.initSet(testing.allocator, op1);
792 defer a.deinit();
793 var b = try Managed.initSet(testing.allocator, op2);
794 defer b.deinit();
795
796 var q = try Managed.init(testing.allocator);
797 defer q.deinit();
798 var r = try Managed.init(testing.allocator);
799 defer r.deinit();
800 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
801
802 testing.expect((try q.to(u128)) == op1 / op2);
803 testing.expect((try r.to(u32)) == 0x3e4e);
804}
805
806test "big.int div single-single q < r" {
807 var a = try Managed.initSet(testing.allocator, 0x0078f432);
808 defer a.deinit();
809 var b = try Managed.initSet(testing.allocator, 0x01000000);
810 defer b.deinit();
811
812 var q = try Managed.init(testing.allocator);
813 defer q.deinit();
814 var r = try Managed.init(testing.allocator);
815 defer r.deinit();
816 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
817
818 testing.expect((try q.to(u64)) == 0);
819 testing.expect((try r.to(u64)) == 0x0078f432);
820}
821
822test "big.int div single-single q == r" {
823 var a = try Managed.initSet(testing.allocator, 10);
824 defer a.deinit();
825 var b = try Managed.initSet(testing.allocator, 10);
826 defer b.deinit();
827
828 var q = try Managed.init(testing.allocator);
829 defer q.deinit();
830 var r = try Managed.init(testing.allocator);
831 defer r.deinit();
832 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
833
834 testing.expect((try q.to(u64)) == 1);
835 testing.expect((try r.to(u64)) == 0);
836}
837
838test "big.int div q=0 alias" {
839 var a = try Managed.initSet(testing.allocator, 3);
840 defer a.deinit();
841 var b = try Managed.initSet(testing.allocator, 10);
842 defer b.deinit();
843
844 try Managed.divTrunc(&a, &b, a.toConst(), b.toConst());
845
846 testing.expect((try a.to(u64)) == 0);
847 testing.expect((try b.to(u64)) == 3);
848}
849
850test "big.int div multi-multi q < r" {
851 const op1 = 0x1ffffffff0078f432;
852 const op2 = 0x1ffffffff01000000;
853 var a = try Managed.initSet(testing.allocator, op1);
854 defer a.deinit();
855 var b = try Managed.initSet(testing.allocator, op2);
856 defer b.deinit();
857
858 var q = try Managed.init(testing.allocator);
859 defer q.deinit();
860 var r = try Managed.init(testing.allocator);
861 defer r.deinit();
862 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
863
864 testing.expect((try q.to(u128)) == 0);
865 testing.expect((try r.to(u128)) == op1);
866}
867
868test "big.int div trunc single-single +/+" {
869 const u: i32 = 5;
870 const v: i32 = 3;
871
872 var a = try Managed.initSet(testing.allocator, u);
873 defer a.deinit();
874 var b = try Managed.initSet(testing.allocator, v);
875 defer b.deinit();
876
877 var q = try Managed.init(testing.allocator);
878 defer q.deinit();
879 var r = try Managed.init(testing.allocator);
880 defer r.deinit();
881 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
882
883 // n = q * d + r
884 // 5 = 1 * 3 + 2
885 const eq = @divTrunc(u, v);
886 const er = @mod(u, v);
887
888 testing.expect((try q.to(i32)) == eq);
889 testing.expect((try r.to(i32)) == er);
890}
891
892test "big.int div trunc single-single -/+" {
893 const u: i32 = -5;
894 const v: i32 = 3;
895
896 var a = try Managed.initSet(testing.allocator, u);
897 defer a.deinit();
898 var b = try Managed.initSet(testing.allocator, v);
899 defer b.deinit();
900
901 var q = try Managed.init(testing.allocator);
902 defer q.deinit();
903 var r = try Managed.init(testing.allocator);
904 defer r.deinit();
905 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
906
907 // n = q * d + r
908 // -5 = 1 * -3 - 2
909 const eq = -1;
910 const er = -2;
911
912 testing.expect((try q.to(i32)) == eq);
913 testing.expect((try r.to(i32)) == er);
914}
915
916test "big.int div trunc single-single +/-" {
917 const u: i32 = 5;
918 const v: i32 = -3;
919
920 var a = try Managed.initSet(testing.allocator, u);
921 defer a.deinit();
922 var b = try Managed.initSet(testing.allocator, v);
923 defer b.deinit();
924
925 var q = try Managed.init(testing.allocator);
926 defer q.deinit();
927 var r = try Managed.init(testing.allocator);
928 defer r.deinit();
929 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
930
931 // n = q * d + r
932 // 5 = -1 * -3 + 2
933 const eq = -1;
934 const er = 2;
935
936 testing.expect((try q.to(i32)) == eq);
937 testing.expect((try r.to(i32)) == er);
938}
939
940test "big.int div trunc single-single -/-" {
941 const u: i32 = -5;
942 const v: i32 = -3;
943
944 var a = try Managed.initSet(testing.allocator, u);
945 defer a.deinit();
946 var b = try Managed.initSet(testing.allocator, v);
947 defer b.deinit();
948
949 var q = try Managed.init(testing.allocator);
950 defer q.deinit();
951 var r = try Managed.init(testing.allocator);
952 defer r.deinit();
953 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
954
955 // n = q * d + r
956 // -5 = 1 * -3 - 2
957 const eq = 1;
958 const er = -2;
959
960 testing.expect((try q.to(i32)) == eq);
961 testing.expect((try r.to(i32)) == er);
962}
963
964test "big.int div floor single-single +/+" {
965 const u: i32 = 5;
966 const v: i32 = 3;
967
968 var a = try Managed.initSet(testing.allocator, u);
969 defer a.deinit();
970 var b = try Managed.initSet(testing.allocator, v);
971 defer b.deinit();
972
973 var q = try Managed.init(testing.allocator);
974 defer q.deinit();
975 var r = try Managed.init(testing.allocator);
976 defer r.deinit();
977 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
978
979 // n = q * d + r
980 // 5 = 1 * 3 + 2
981 const eq = 1;
982 const er = 2;
983
984 testing.expect((try q.to(i32)) == eq);
985 testing.expect((try r.to(i32)) == er);
986}
987
988test "big.int div floor single-single -/+" {
989 const u: i32 = -5;
990 const v: i32 = 3;
991
992 var a = try Managed.initSet(testing.allocator, u);
993 defer a.deinit();
994 var b = try Managed.initSet(testing.allocator, v);
995 defer b.deinit();
996
997 var q = try Managed.init(testing.allocator);
998 defer q.deinit();
999 var r = try Managed.init(testing.allocator);
1000 defer r.deinit();
1001 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
1002
1003 // n = q * d + r
1004 // -5 = -2 * 3 + 1
1005 const eq = -2;
1006 const er = 1;
1007
1008 testing.expect((try q.to(i32)) == eq);
1009 testing.expect((try r.to(i32)) == er);
1010}
1011
1012test "big.int div floor single-single +/-" {
1013 const u: i32 = 5;
1014 const v: i32 = -3;
1015
1016 var a = try Managed.initSet(testing.allocator, u);
1017 defer a.deinit();
1018 var b = try Managed.initSet(testing.allocator, v);
1019 defer b.deinit();
1020
1021 var q = try Managed.init(testing.allocator);
1022 defer q.deinit();
1023 var r = try Managed.init(testing.allocator);
1024 defer r.deinit();
1025 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
1026
1027 // n = q * d + r
1028 // 5 = -2 * -3 - 1
1029 const eq = -2;
1030 const er = -1;
1031
1032 testing.expect((try q.to(i32)) == eq);
1033 testing.expect((try r.to(i32)) == er);
1034}
1035
1036test "big.int div floor single-single -/-" {
1037 const u: i32 = -5;
1038 const v: i32 = -3;
1039
1040 var a = try Managed.initSet(testing.allocator, u);
1041 defer a.deinit();
1042 var b = try Managed.initSet(testing.allocator, v);
1043 defer b.deinit();
1044
1045 var q = try Managed.init(testing.allocator);
1046 defer q.deinit();
1047 var r = try Managed.init(testing.allocator);
1048 defer r.deinit();
1049 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
1050
1051 // n = q * d + r
1052 // -5 = 2 * -3 + 1
1053 const eq = 1;
1054 const er = -2;
1055
1056 testing.expect((try q.to(i32)) == eq);
1057 testing.expect((try r.to(i32)) == er);
1058}
1059
1060test "big.int div multi-multi with rem" {
1061 var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);
1062 defer a.deinit();
1063 var b = try Managed.initSet(testing.allocator, 0x99990000111122223333);
1064 defer b.deinit();
1065
1066 var q = try Managed.init(testing.allocator);
1067 defer q.deinit();
1068 var r = try Managed.init(testing.allocator);
1069 defer r.deinit();
1070 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1071
1072 testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1073 testing.expect((try r.to(u128)) == 0x28de0acacd806823638);
1074}
1075
1076test "big.int div multi-multi no rem" {
1077 var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeedb4fec200ee3a4286361);
1078 defer a.deinit();
1079 var b = try Managed.initSet(testing.allocator, 0x99990000111122223333);
1080 defer b.deinit();
1081
1082 var q = try Managed.init(testing.allocator);
1083 defer q.deinit();
1084 var r = try Managed.init(testing.allocator);
1085 defer r.deinit();
1086 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1087
1088 testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1089 testing.expect((try r.to(u128)) == 0);
1090}
1091
1092test "big.int div multi-multi (2 branch)" {
1093 var a = try Managed.initSet(testing.allocator, 0x866666665555555588888887777777761111111111111111);
1094 defer a.deinit();
1095 var b = try Managed.initSet(testing.allocator, 0x86666666555555554444444433333333);
1096 defer b.deinit();
1097
1098 var q = try Managed.init(testing.allocator);
1099 defer q.deinit();
1100 var r = try Managed.init(testing.allocator);
1101 defer r.deinit();
1102 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1103
1104 testing.expect((try q.to(u128)) == 0x10000000000000000);
1105 testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111);
1106}
1107
1108test "big.int div multi-multi (3.1/3.3 branch)" {
1109 var a = try Managed.initSet(testing.allocator, 0x11111111111111111111111111111111111111111111111111111111111111);
1110 defer a.deinit();
1111 var b = try Managed.initSet(testing.allocator, 0x1111111111111111111111111111111111111111171);
1112 defer b.deinit();
1113
1114 var q = try Managed.init(testing.allocator);
1115 defer q.deinit();
1116 var r = try Managed.init(testing.allocator);
1117 defer r.deinit();
1118 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1119
1120 testing.expect((try q.to(u128)) == 0xfffffffffffffffffff);
1121 testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);
1122}
1123
1124test "big.int div multi-single zero-limb trailing" {
1125 var a = try Managed.initSet(testing.allocator, 0x60000000000000000000000000000000000000000000000000000000000000000);
1126 defer a.deinit();
1127 var b = try Managed.initSet(testing.allocator, 0x10000000000000000);
1128 defer b.deinit();
1129
1130 var q = try Managed.init(testing.allocator);
1131 defer q.deinit();
1132 var r = try Managed.init(testing.allocator);
1133 defer r.deinit();
1134 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1135
1136 var expected = try Managed.initSet(testing.allocator, 0x6000000000000000000000000000000000000000000000000);
1137 defer expected.deinit();
1138 testing.expect(q.eq(expected));
1139 testing.expect(r.eqZero());
1140}
1141
1142test "big.int div multi-multi zero-limb trailing (with rem)" {
1143 var a = try Managed.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
1144 defer a.deinit();
1145 var b = try Managed.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);
1146 defer b.deinit();
1147
1148 var q = try Managed.init(testing.allocator);
1149 defer q.deinit();
1150 var r = try Managed.init(testing.allocator);
1151 defer r.deinit();
1152 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1153
1154 testing.expect((try q.to(u128)) == 0x10000000000000000);
1155
1156 const rs = try r.toString(testing.allocator, 16, false);
1157 defer testing.allocator.free(rs);
1158 testing.expect(std.mem.eql(u8, rs, "4444444344444443111111111111111100000000000000000000000000000000"));
1159}
1160
1161test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" {
1162 var a = try Managed.initSet(testing.allocator, 0x8666666655555555888888877777777611111111111111110000000000000000);
1163 defer a.deinit();
1164 var b = try Managed.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);
1165 defer b.deinit();
1166
1167 var q = try Managed.init(testing.allocator);
1168 defer q.deinit();
1169 var r = try Managed.init(testing.allocator);
1170 defer r.deinit();
1171 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1172
1173 testing.expect((try q.to(u128)) == 0x1);
1174
1175 const rs = try r.toString(testing.allocator, 16, false);
1176 defer testing.allocator.free(rs);
1177 testing.expect(std.mem.eql(u8, rs, "444444434444444311111111111111110000000000000000"));
1178}
1179
1180test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" {
1181 var a = try Managed.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
1182 defer a.deinit();
1183 var b = try Managed.initSet(testing.allocator, 0x866666665555555544444444333333330000000000000000);
1184 defer b.deinit();
1185
1186 var q = try Managed.init(testing.allocator);
1187 defer q.deinit();
1188 var r = try Managed.init(testing.allocator);
1189 defer r.deinit();
1190 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1191
1192 const qs = try q.toString(testing.allocator, 16, false);
1193 defer testing.allocator.free(qs);
1194 testing.expect(std.mem.eql(u8, qs, "10000000000000000820820803105186f"));
1195
1196 const rs = try r.toString(testing.allocator, 16, false);
1197 defer testing.allocator.free(rs);
1198 testing.expect(std.mem.eql(u8, rs, "4e11f2baa5896a321d463b543d0104e30000000000000000"));
1199}
1200
1201test "big.int div multi-multi fuzz case #1" {
1202 var a = try Managed.init(testing.allocator);
1203 defer a.deinit();
1204 var b = try Managed.init(testing.allocator);
1205 defer b.deinit();
1206
1207 try a.setString(16, "ffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
1208 try b.setString(16, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffc000000000000000000000000000000007fffffffffff");
1209
1210 var q = try Managed.init(testing.allocator);
1211 defer q.deinit();
1212 var r = try Managed.init(testing.allocator);
1213 defer r.deinit();
1214 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1215
1216 const qs = try q.toString(testing.allocator, 16, false);
1217 defer testing.allocator.free(qs);
1218 testing.expect(std.mem.eql(u8, qs, "3ffffffffffffffffffffffffffff0000000000000000000000000000000000001ffffffffffffffffffffffffffff7fffffffe000000000000000000000000000180000000000000000000003fffffbfffffffdfffffffffffffeffff800000100101000000100000000020003fffffdfbfffffe3ffffffffffffeffff7fffc00800a100000017ffe000002000400007efbfff7fe9f00000037ffff3fff7fffa004006100000009ffe00000190038200bf7d2ff7fefe80400060000f7d7f8fbf9401fe38e0403ffc0bdffffa51102c300d7be5ef9df4e5060007b0127ad3fa69f97d0f820b6605ff617ddf7f32ad7a05c0d03f2e7bc78a6000e087a8bbcdc59e07a5a079128a7861f553ddebed7e8e56701756f9ead39b48cd1b0831889ea6ec1fddf643d0565b075ff07e6caea4e2854ec9227fd635ed60a2f5eef2893052ffd54718fa08604acbf6a15e78a467c4a3c53c0278af06c4416573f925491b195e8fd79302cb1aaf7caf4ecfc9aec1254cc969786363ac729f914c6ddcc26738d6b0facd54eba026580aba2eb6482a088b0d224a8852420b91ec1"));
1219
1220 const rs = try r.toString(testing.allocator, 16, false);
1221 defer testing.allocator.free(rs);
1222 testing.expect(std.mem.eql(u8, rs, "310d1d4c414426b4836c2635bad1df3a424e50cbdd167ffccb4dfff57d36b4aae0d6ca0910698220171a0f3373c1060a046c2812f0027e321f72979daa5e7973214170d49e885de0c0ecc167837d44502430674a82522e5df6a0759548052420b91ec1"));
1223}
1224
1225test "big.int div multi-multi fuzz case #2" {
1226 var a = try Managed.init(testing.allocator);
1227 defer a.deinit();
1228 var b = try Managed.init(testing.allocator);
1229 defer b.deinit();
1230
1231 try a.setString(16, "3ffffffffe00000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000001fffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffc000000000000000000000000000000000000000000000000000000000000000");
1232 try b.setString(16, "ffc0000000000000000000000000000000000000000000000000");
1233
1234 var q = try Managed.init(testing.allocator);
1235 defer q.deinit();
1236 var r = try Managed.init(testing.allocator);
1237 defer r.deinit();
1238 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1239
1240 const qs = try q.toString(testing.allocator, 16, false);
1241 defer testing.allocator.free(qs);
1242 testing.expect(std.mem.eql(u8, qs, "40100400fe3f8fe3f8fe3f8fe3f8fe3f8fe4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f91e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4992649926499264991e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4792e4b92e4b92e4b92e4b92a4a92a4a92a4"));
1243
1244 const rs = try r.toString(testing.allocator, 16, false);
1245 defer testing.allocator.free(rs);
1246 testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000"));
1247}
1248
1249test "big.int shift-right single" {
1250 var a = try Managed.initSet(testing.allocator, 0xffff0000);
1251 defer a.deinit();
1252 try a.shiftRight(a, 16);
1253
1254 testing.expect((try a.to(u32)) == 0xffff);
1255}
1256
1257test "big.int shift-right multi" {
1258 var a = try Managed.initSet(testing.allocator, 0xffff0000eeee1111dddd2222cccc3333);
1259 defer a.deinit();
1260 try a.shiftRight(a, 67);
1261
1262 testing.expect((try a.to(u64)) == 0x1fffe0001dddc222);
1263}
1264
1265test "big.int shift-left single" {
1266 var a = try Managed.initSet(testing.allocator, 0xffff);
1267 defer a.deinit();
1268 try a.shiftLeft(a, 16);
1269
1270 testing.expect((try a.to(u64)) == 0xffff0000);
1271}
1272
1273test "big.int shift-left multi" {
1274 var a = try Managed.initSet(testing.allocator, 0x1fffe0001dddc222);
1275 defer a.deinit();
1276 try a.shiftLeft(a, 67);
1277
1278 testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000);
1279}
1280
1281test "big.int shift-right negative" {
1282 var a = try Managed.init(testing.allocator);
1283 defer a.deinit();
1284
1285 var arg = try Managed.initSet(testing.allocator, -20);
1286 defer arg.deinit();
1287 try a.shiftRight(arg, 2);
1288 testing.expect((try a.to(i32)) == -20 >> 2);
1289
1290 var arg2 = try Managed.initSet(testing.allocator, -5);
1291 defer arg2.deinit();
1292 try a.shiftRight(arg2, 10);
1293 testing.expect((try a.to(i32)) == -5 >> 10);
1294}
1295
1296test "big.int shift-left negative" {
1297 var a = try Managed.init(testing.allocator);
1298 defer a.deinit();
1299
1300 var arg = try Managed.initSet(testing.allocator, -10);
1301 defer arg.deinit();
1302 try a.shiftRight(arg, 1232);
1303 testing.expect((try a.to(i32)) == -10 >> 1232);
1304}
1305
1306test "big.int bitwise and simple" {
1307 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1308 defer a.deinit();
1309 var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222);
1310 defer b.deinit();
1311
1312 try a.bitAnd(a, b);
1313
1314 testing.expect((try a.to(u64)) == 0xeeeeeeee00000000);
1315}
1316
1317test "big.int bitwise and multi-limb" {
1318 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
1319 defer a.deinit();
1320 var b = try Managed.initSet(testing.allocator, maxInt(Limb));
1321 defer b.deinit();
1322
1323 try a.bitAnd(a, b);
1324
1325 testing.expect((try a.to(u128)) == 0);
1326}
1327
1328test "big.int bitwise xor simple" {
1329 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1330 defer a.deinit();
1331 var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222);
1332 defer b.deinit();
1333
1334 try a.bitXor(a, b);
1335
1336 testing.expect((try a.to(u64)) == 0x1111111133333333);
1337}
1338
1339test "big.int bitwise xor multi-limb" {
1340 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
1341 defer a.deinit();
1342 var b = try Managed.initSet(testing.allocator, maxInt(Limb));
1343 defer b.deinit();
1344
1345 try a.bitXor(a, b);
1346
1347 testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb));
1348}
1349
1350test "big.int bitwise or simple" {
1351 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1352 defer a.deinit();
1353 var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222);
1354 defer b.deinit();
1355
1356 try a.bitOr(a, b);
1357
1358 testing.expect((try a.to(u64)) == 0xffffffff33333333);
1359}
1360
1361test "big.int bitwise or multi-limb" {
1362 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
1363 defer a.deinit();
1364 var b = try Managed.initSet(testing.allocator, maxInt(Limb));
1365 defer b.deinit();
1366
1367 try a.bitOr(a, b);
1368
1369 // TODO: big.int.cpp or is wrong on multi-limb.
1370 testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));
1371}
1372
1373test "big.int var args" {
1374 var a = try Managed.initSet(testing.allocator, 5);
1375 defer a.deinit();
1376
1377 var b = try Managed.initSet(testing.allocator, 6);
1378 defer b.deinit();
1379 try a.add(a.toConst(), b.toConst());
1380 testing.expect((try a.to(u64)) == 11);
1381
1382 var c = try Managed.initSet(testing.allocator, 11);
1383 defer c.deinit();
1384 testing.expect(a.order(c) == .eq);
1385
1386 var d = try Managed.initSet(testing.allocator, 14);
1387 defer d.deinit();
1388 testing.expect(a.order(d) != .gt);
1389}
1390
1391test "big.int gcd non-one small" {
1392 var a = try Managed.initSet(testing.allocator, 17);
1393 defer a.deinit();
1394 var b = try Managed.initSet(testing.allocator, 97);
1395 defer b.deinit();
1396 var r = try Managed.init(testing.allocator);
1397 defer r.deinit();
1398
1399 try r.gcd(a, b);
1400
1401 testing.expect((try r.to(u32)) == 1);
1402}
1403
1404test "big.int gcd non-one small" {
1405 var a = try Managed.initSet(testing.allocator, 4864);
1406 defer a.deinit();
1407 var b = try Managed.initSet(testing.allocator, 3458);
1408 defer b.deinit();
1409 var r = try Managed.init(testing.allocator);
1410 defer r.deinit();
1411
1412 try r.gcd(a, b);
1413
1414 testing.expect((try r.to(u32)) == 38);
1415}
1416
1417test "big.int gcd non-one large" {
1418 var a = try Managed.initSet(testing.allocator, 0xffffffffffffffff);
1419 defer a.deinit();
1420 var b = try Managed.initSet(testing.allocator, 0xffffffffffffffff7777);
1421 defer b.deinit();
1422 var r = try Managed.init(testing.allocator);
1423 defer r.deinit();
1424
1425 try r.gcd(a, b);
1426
1427 testing.expect((try r.to(u32)) == 4369);
1428}
1429
1430test "big.int gcd large multi-limb result" {
1431 var a = try Managed.initSet(testing.allocator, 0x12345678123456781234567812345678123456781234567812345678);
1432 defer a.deinit();
1433 var b = try Managed.initSet(testing.allocator, 0x12345671234567123456712345671234567123456712345671234567);
1434 defer b.deinit();
1435 var r = try Managed.init(testing.allocator);
1436 defer r.deinit();
1437
1438 try r.gcd(a, b);
1439
1440 const answer = (try r.to(u256));
1441 testing.expect(answer == 0xf000000ff00000fff0000ffff000fffff00ffffff1);
1442}
1443
1444test "big.int gcd one large" {
1445 var a = try Managed.initSet(testing.allocator, 1897056385327307);
1446 defer a.deinit();
1447 var b = try Managed.initSet(testing.allocator, 2251799813685248);
1448 defer b.deinit();
1449 var r = try Managed.init(testing.allocator);
1450 defer r.deinit();
1451
1452 try r.gcd(a, b);
1453
1454 testing.expect((try r.to(u64)) == 1);
1455}
lib/std/math/big/rational.zig+60-57
...@@ -5,10 +5,10 @@ const mem = std.mem;...@@ -5,10 +5,10 @@ const mem = std.mem;
5const testing = std.testing;5const testing = std.testing;
6const Allocator = mem.Allocator;6const Allocator = mem.Allocator;
77
8const bn = @import("int.zig");8const Limb = std.math.big.Limb;
9const Limb = bn.Limb;9const DoubleLimb = std.math.big.DoubleLimb;
10const DoubleLimb = bn.DoubleLimb;10const Int = std.math.big.int.Managed;
11const Int = bn.Int;11const IntConst = std.math.big.int.Const;
1212
13/// An arbitrary-precision rational number.13/// An arbitrary-precision rational number.
14///14///
...@@ -17,6 +17,9 @@ const Int = bn.Int;...@@ -17,6 +17,9 @@ const Int = bn.Int;
17///17///
18/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,18/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,
19/// gcd(p, q) = 1 always.19/// gcd(p, q) = 1 always.
20///
21/// TODO rework this to store its own allocator and use a non-managed big int, to avoid double
22/// allocator storage.
20pub const Rational = struct {23pub const Rational = struct {
21 /// Numerator. Determines the sign of the Rational.24 /// Numerator. Determines the sign of the Rational.
22 p: Int,25 p: Int,
...@@ -98,20 +101,20 @@ pub const Rational = struct {...@@ -98,20 +101,20 @@ pub const Rational = struct {
98 if (point) |i| {101 if (point) |i| {
99 try self.p.setString(10, str[0..i]);102 try self.p.setString(10, str[0..i]);
100103
101 const base = Int.initFixed(([_]Limb{10})[0..]);104 const base = IntConst{ .limbs = &[_]Limb{10}, .positive = true };
102105
103 var j: usize = start;106 var j: usize = start;
104 while (j < str.len - i - 1) : (j += 1) {107 while (j < str.len - i - 1) : (j += 1) {
105 try self.p.mul(self.p, base);108 try self.p.mul(self.p.toConst(), base);
106 }109 }
107110
108 try self.q.setString(10, str[i + 1 ..]);111 try self.q.setString(10, str[i + 1 ..]);
109 try self.p.add(self.p, self.q);112 try self.p.add(self.p.toConst(), self.q.toConst());
110113
111 try self.q.set(1);114 try self.q.set(1);
112 var k: usize = i + 1;115 var k: usize = i + 1;
113 while (k < str.len) : (k += 1) {116 while (k < str.len) : (k += 1) {
114 try self.q.mul(self.q, base);117 try self.q.mul(self.q.toConst(), base);
115 }118 }
116119
117 try self.reduce();120 try self.reduce();
...@@ -218,14 +221,14 @@ pub const Rational = struct {...@@ -218,14 +221,14 @@ pub const Rational = struct {
218 }221 }
219222
220 // 2. compute quotient and remainder223 // 2. compute quotient and remainder
221 var q = try Int.init(self.p.allocator.?);224 var q = try Int.init(self.p.allocator);
222 defer q.deinit();225 defer q.deinit();
223226
224 // unused227 // unused
225 var r = try Int.init(self.p.allocator.?);228 var r = try Int.init(self.p.allocator);
226 defer r.deinit();229 defer r.deinit();
227230
228 try Int.divTrunc(&q, &r, a2, b2);231 try Int.divTrunc(&q, &r, a2.toConst(), b2.toConst());
229232
230 var mantissa = extractLowBits(q, BitReprType);233 var mantissa = extractLowBits(q, BitReprType);
231 var have_rem = r.len() > 0;234 var have_rem = r.len() > 0;
...@@ -293,14 +296,14 @@ pub const Rational = struct {...@@ -293,14 +296,14 @@ pub const Rational = struct {
293296
294 /// Set a Rational directly from an Int.297 /// Set a Rational directly from an Int.
295 pub fn copyInt(self: *Rational, a: Int) !void {298 pub fn copyInt(self: *Rational, a: Int) !void {
296 try self.p.copy(a);299 try self.p.copy(a.toConst());
297 try self.q.set(1);300 try self.q.set(1);
298 }301 }
299302
300 /// Set a Rational directly from a ratio of two Int's.303 /// Set a Rational directly from a ratio of two Int's.
301 pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {304 pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
302 try self.p.copy(a);305 try self.p.copy(a.toConst());
303 try self.q.copy(b);306 try self.q.copy(b.toConst());
304307
305 self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);308 self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
306 self.q.setSign(true);309 self.q.setSign(true);
...@@ -327,13 +330,13 @@ pub const Rational = struct {...@@ -327,13 +330,13 @@ pub const Rational = struct {
327330
328 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a331 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
329 /// > b respectively.332 /// > b respectively.
330 pub fn cmp(a: Rational, b: Rational) !math.Order {333 pub fn order(a: Rational, b: Rational) !math.Order {
331 return cmpInternal(a, b, true);334 return cmpInternal(a, b, true);
332 }335 }
333336
334 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==337 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
335 /// |b| or |a| > |b| respectively.338 /// |b| or |a| > |b| respectively.
336 pub fn cmpAbs(a: Rational, b: Rational) !math.Order {339 pub fn orderAbs(a: Rational, b: Rational) !math.Order {
337 return cmpInternal(a, b, false);340 return cmpInternal(a, b, false);
338 }341 }
339342
...@@ -341,16 +344,16 @@ pub const Rational = struct {...@@ -341,16 +344,16 @@ pub const Rational = struct {
341 fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order {344 fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order {
342 // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid345 // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid
343 // the memory allocations here?346 // the memory allocations here?
344 var q = try Int.init(a.p.allocator.?);347 var q = try Int.init(a.p.allocator);
345 defer q.deinit();348 defer q.deinit();
346349
347 var p = try Int.init(b.p.allocator.?);350 var p = try Int.init(b.p.allocator);
348 defer p.deinit();351 defer p.deinit();
349352
350 try q.mul(a.p, b.q);353 try q.mul(a.p.toConst(), b.q.toConst());
351 try p.mul(b.p, a.q);354 try p.mul(b.p.toConst(), a.q.toConst());
352355
353 return if (is_abs) q.cmpAbs(p) else q.cmp(p);356 return if (is_abs) q.orderAbs(p) else q.order(p);
354 }357 }
355358
356 /// rma = a + b.359 /// rma = a + b.
...@@ -364,7 +367,7 @@ pub const Rational = struct {...@@ -364,7 +367,7 @@ pub const Rational = struct {
364367
365 var sr: Rational = undefined;368 var sr: Rational = undefined;
366 if (aliased) {369 if (aliased) {
367 sr = try Rational.init(rma.p.allocator.?);370 sr = try Rational.init(rma.p.allocator);
368 r = &sr;371 r = &sr;
369 aliased = true;372 aliased = true;
370 }373 }
...@@ -373,11 +376,11 @@ pub const Rational = struct {...@@ -373,11 +376,11 @@ pub const Rational = struct {
373 r.deinit();376 r.deinit();
374 };377 };
375378
376 try r.p.mul(a.p, b.q);379 try r.p.mul(a.p.toConst(), b.q.toConst());
377 try r.q.mul(b.p, a.q);380 try r.q.mul(b.p.toConst(), a.q.toConst());
378 try r.p.add(r.p, r.q);381 try r.p.add(r.p.toConst(), r.q.toConst());
379382
380 try r.q.mul(a.q, b.q);383 try r.q.mul(a.q.toConst(), b.q.toConst());
381 try r.reduce();384 try r.reduce();
382 }385 }
383386
...@@ -392,7 +395,7 @@ pub const Rational = struct {...@@ -392,7 +395,7 @@ pub const Rational = struct {
392395
393 var sr: Rational = undefined;396 var sr: Rational = undefined;
394 if (aliased) {397 if (aliased) {
395 sr = try Rational.init(rma.p.allocator.?);398 sr = try Rational.init(rma.p.allocator);
396 r = &sr;399 r = &sr;
397 aliased = true;400 aliased = true;
398 }401 }
...@@ -401,11 +404,11 @@ pub const Rational = struct {...@@ -401,11 +404,11 @@ pub const Rational = struct {
401 r.deinit();404 r.deinit();
402 };405 };
403406
404 try r.p.mul(a.p, b.q);407 try r.p.mul(a.p.toConst(), b.q.toConst());
405 try r.q.mul(b.p, a.q);408 try r.q.mul(b.p.toConst(), a.q.toConst());
406 try r.p.sub(r.p, r.q);409 try r.p.sub(r.p.toConst(), r.q.toConst());
407410
408 try r.q.mul(a.q, b.q);411 try r.q.mul(a.q.toConst(), b.q.toConst());
409 try r.reduce();412 try r.reduce();
410 }413 }
411414
...@@ -415,8 +418,8 @@ pub const Rational = struct {...@@ -415,8 +418,8 @@ pub const Rational = struct {
415 ///418 ///
416 /// Returns an error if memory could not be allocated.419 /// Returns an error if memory could not be allocated.
417 pub fn mul(r: *Rational, a: Rational, b: Rational) !void {420 pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
418 try r.p.mul(a.p, b.p);421 try r.p.mul(a.p.toConst(), b.p.toConst());
419 try r.q.mul(a.q, b.q);422 try r.q.mul(a.q.toConst(), b.q.toConst());
420 try r.reduce();423 try r.reduce();
421 }424 }
422425
...@@ -430,8 +433,8 @@ pub const Rational = struct {...@@ -430,8 +433,8 @@ pub const Rational = struct {
430 @panic("division by zero");433 @panic("division by zero");
431 }434 }
432435
433 try r.p.mul(a.p, b.q);436 try r.p.mul(a.p.toConst(), b.q.toConst());
434 try r.q.mul(b.p, a.q);437 try r.q.mul(b.p.toConst(), a.q.toConst());
435 try r.reduce();438 try r.reduce();
436 }439 }
437440
...@@ -442,7 +445,7 @@ pub const Rational = struct {...@@ -442,7 +445,7 @@ pub const Rational = struct {
442445
443 // reduce r/q such that gcd(r, q) = 1446 // reduce r/q such that gcd(r, q) = 1
444 fn reduce(r: *Rational) !void {447 fn reduce(r: *Rational) !void {
445 var a = try Int.init(r.p.allocator.?);448 var a = try Int.init(r.p.allocator);
446 defer a.deinit();449 defer a.deinit();
447450
448 const sign = r.p.isPositive();451 const sign = r.p.isPositive();
...@@ -450,15 +453,15 @@ pub const Rational = struct {...@@ -450,15 +453,15 @@ pub const Rational = struct {
450 try a.gcd(r.p, r.q);453 try a.gcd(r.p, r.q);
451 r.p.setSign(sign);454 r.p.setSign(sign);
452455
453 const one = Int.initFixed(([_]Limb{1})[0..]);456 const one = IntConst{ .limbs = &[_]Limb{1}, .positive = true };
454 if (a.cmp(one) != .eq) {457 if (a.toConst().order(one) != .eq) {
455 var unused = try Int.init(r.p.allocator.?);458 var unused = try Int.init(r.p.allocator);
456 defer unused.deinit();459 defer unused.deinit();
457460
458 // TODO: divexact would be useful here461 // TODO: divexact would be useful here
459 // TODO: don't copy r.q for div462 // TODO: don't copy r.q for div
460 try Int.divTrunc(&r.p, &unused, r.p, a);463 try Int.divTrunc(&r.p, &unused, r.p.toConst(), a.toConst());
461 try Int.divTrunc(&r.q, &unused, r.q, a);464 try Int.divTrunc(&r.q, &unused, r.q.toConst(), a.toConst());
462 }465 }
463 }466 }
464};467};
...@@ -596,25 +599,25 @@ test "big.rational copy" {...@@ -596,25 +599,25 @@ test "big.rational copy" {
596 var a = try Rational.init(testing.allocator);599 var a = try Rational.init(testing.allocator);
597 defer a.deinit();600 defer a.deinit();
598601
599 const b = try Int.initSet(testing.allocator, 5);602 var b = try Int.initSet(testing.allocator, 5);
600 defer b.deinit();603 defer b.deinit();
601604
602 try a.copyInt(b);605 try a.copyInt(b);
603 testing.expect((try a.p.to(u32)) == 5);606 testing.expect((try a.p.to(u32)) == 5);
604 testing.expect((try a.q.to(u32)) == 1);607 testing.expect((try a.q.to(u32)) == 1);
605608
606 const c = try Int.initSet(testing.allocator, 7);609 var c = try Int.initSet(testing.allocator, 7);
607 defer c.deinit();610 defer c.deinit();
608 const d = try Int.initSet(testing.allocator, 3);611 var d = try Int.initSet(testing.allocator, 3);
609 defer d.deinit();612 defer d.deinit();
610613
611 try a.copyRatio(c, d);614 try a.copyRatio(c, d);
612 testing.expect((try a.p.to(u32)) == 7);615 testing.expect((try a.p.to(u32)) == 7);
613 testing.expect((try a.q.to(u32)) == 3);616 testing.expect((try a.q.to(u32)) == 3);
614617
615 const e = try Int.initSet(testing.allocator, 9);618 var e = try Int.initSet(testing.allocator, 9);
616 defer e.deinit();619 defer e.deinit();
617 const f = try Int.initSet(testing.allocator, 3);620 var f = try Int.initSet(testing.allocator, 3);
618 defer f.deinit();621 defer f.deinit();
619622
620 try a.copyRatio(e, f);623 try a.copyRatio(e, f);
...@@ -680,7 +683,7 @@ test "big.rational swap" {...@@ -680,7 +683,7 @@ test "big.rational swap" {
680 testing.expect((try b.q.to(u32)) == 23);683 testing.expect((try b.q.to(u32)) == 23);
681}684}
682685
683test "big.rational cmp" {686test "big.rational order" {
684 var a = try Rational.init(testing.allocator);687 var a = try Rational.init(testing.allocator);
685 defer a.deinit();688 defer a.deinit();
686 var b = try Rational.init(testing.allocator);689 var b = try Rational.init(testing.allocator);
...@@ -688,11 +691,11 @@ test "big.rational cmp" {...@@ -688,11 +691,11 @@ test "big.rational cmp" {
688691
689 try a.setRatio(500, 231);692 try a.setRatio(500, 231);
690 try b.setRatio(18903, 8584);693 try b.setRatio(18903, 8584);
691 testing.expect((try a.cmp(b)) == .lt);694 testing.expect((try a.order(b)) == .lt);
692695
693 try a.setRatio(890, 10);696 try a.setRatio(890, 10);
694 try b.setRatio(89, 1);697 try b.setRatio(89, 1);
695 testing.expect((try a.cmp(b)) == .eq);698 testing.expect((try a.order(b)) == .eq);
696}699}
697700
698test "big.rational add single-limb" {701test "big.rational add single-limb" {
...@@ -703,11 +706,11 @@ test "big.rational add single-limb" {...@@ -703,11 +706,11 @@ test "big.rational add single-limb" {
703706
704 try a.setRatio(500, 231);707 try a.setRatio(500, 231);
705 try b.setRatio(18903, 8584);708 try b.setRatio(18903, 8584);
706 testing.expect((try a.cmp(b)) == .lt);709 testing.expect((try a.order(b)) == .lt);
707710
708 try a.setRatio(890, 10);711 try a.setRatio(890, 10);
709 try b.setRatio(89, 1);712 try b.setRatio(89, 1);
710 testing.expect((try a.cmp(b)) == .eq);713 testing.expect((try a.order(b)) == .eq);
711}714}
712715
713test "big.rational add" {716test "big.rational add" {
...@@ -723,7 +726,7 @@ test "big.rational add" {...@@ -723,7 +726,7 @@ test "big.rational add" {
723 try a.add(a, b);726 try a.add(a, b);
724727
725 try r.setRatio(984786924199, 290395044174);728 try r.setRatio(984786924199, 290395044174);
726 testing.expect((try a.cmp(r)) == .eq);729 testing.expect((try a.order(r)) == .eq);
727}730}
728731
729test "big.rational sub" {732test "big.rational sub" {
...@@ -739,7 +742,7 @@ test "big.rational sub" {...@@ -739,7 +742,7 @@ test "big.rational sub" {
739 try a.sub(a, b);742 try a.sub(a, b);
740743
741 try r.setRatio(979040510045, 290395044174);744 try r.setRatio(979040510045, 290395044174);
742 testing.expect((try a.cmp(r)) == .eq);745 testing.expect((try a.order(r)) == .eq);
743}746}
744747
745test "big.rational mul" {748test "big.rational mul" {
...@@ -755,7 +758,7 @@ test "big.rational mul" {...@@ -755,7 +758,7 @@ test "big.rational mul" {
755 try a.mul(a, b);758 try a.mul(a, b);
756759
757 try r.setRatio(571481443, 17082061422);760 try r.setRatio(571481443, 17082061422);
758 testing.expect((try a.cmp(r)) == .eq);761 testing.expect((try a.order(r)) == .eq);
759}762}
760763
761test "big.rational div" {764test "big.rational div" {
...@@ -771,7 +774,7 @@ test "big.rational div" {...@@ -771,7 +774,7 @@ test "big.rational div" {
771 try a.div(a, b);774 try a.div(a, b);
772775
773 try r.setRatio(75531824394, 221015929);776 try r.setRatio(75531824394, 221015929);
774 testing.expect((try a.cmp(r)) == .eq);777 testing.expect((try a.order(r)) == .eq);
775}778}
776779
777test "big.rational div" {780test "big.rational div" {
...@@ -784,11 +787,11 @@ test "big.rational div" {...@@ -784,11 +787,11 @@ test "big.rational div" {
784 a.invert();787 a.invert();
785788
786 try r.setRatio(23341, 78923);789 try r.setRatio(23341, 78923);
787 testing.expect((try a.cmp(r)) == .eq);790 testing.expect((try a.order(r)) == .eq);
788791
789 try a.setRatio(-78923, 23341);792 try a.setRatio(-78923, 23341);
790 a.invert();793 a.invert();
791794
792 try r.setRatio(-23341, 78923);795 try r.setRatio(-23341, 78923);
793 testing.expect((try a.cmp(r)) == .eq);796 testing.expect((try a.order(r)) == .eq);
794}797}
lib/std/testing.zig+1-1
...@@ -12,7 +12,7 @@ pub const failing_allocator = &failing_allocator_instance.allocator;...@@ -12,7 +12,7 @@ pub const failing_allocator = &failing_allocator_instance.allocator;
12pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);12pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);
1313
14pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]);14pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]);
15var allocator_mem: [1024 * 1024]u8 = undefined;15var allocator_mem: [2 * 1024 * 1024]u8 = undefined;
1616
17/// This function is intended to be used only in tests. It prints diagnostics to stderr17/// This function is intended to be used only in tests. It prints diagnostics to stderr
18/// and then aborts when actual_error_union is not expected_error.18/// and then aborts when actual_error_union is not expected_error.
src-self-hosted/ir.zig+40-22
...@@ -4,7 +4,8 @@ const Allocator = std.mem.Allocator;...@@ -4,7 +4,8 @@ const Allocator = std.mem.Allocator;
4const Value = @import("value.zig").Value;4const Value = @import("value.zig").Value;
5const Type = @import("type.zig").Type;5const Type = @import("type.zig").Type;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const BigInt = std.math.big.Int;7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
8const Target = std.Target;9const Target = std.Target;
910
10pub const text = @import("ir/text.zig");11pub const text = @import("ir/text.zig");
...@@ -483,29 +484,32 @@ const Analyze = struct {...@@ -483,29 +484,32 @@ const Analyze = struct {
483 });484 });
484 }485 }
485486
486 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigInt) !*Inst {487 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
487 if (big_int.isPositive()) {488 const val_payload = if (big_int.positive) blk: {
488 if (big_int.to(u64)) |x| {489 if (big_int.to(u64)) |x| {
489 return self.constIntUnsigned(src, ty, x);490 return self.constIntUnsigned(src, ty, x);
490 } else |err| switch (err) {491 } else |err| switch (err) {
491 error.NegativeIntoUnsigned => unreachable,492 error.NegativeIntoUnsigned => unreachable,
492 error.TargetTooSmall => {}, // handled below493 error.TargetTooSmall => {}, // handled below
493 }494 }
494 } else {495 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive);
496 big_int_payload.* = .{ .limbs = big_int.limbs };
497 break :blk &big_int_payload.base;
498 } else blk: {
495 if (big_int.to(i64)) |x| {499 if (big_int.to(i64)) |x| {
496 return self.constIntSigned(src, ty, x);500 return self.constIntSigned(src, ty, x);
497 } else |err| switch (err) {501 } else |err| switch (err) {
498 error.NegativeIntoUnsigned => unreachable,502 error.NegativeIntoUnsigned => unreachable,
499 error.TargetTooSmall => {}, // handled below503 error.TargetTooSmall => {}, // handled below
500 }504 }
501 }505 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative);
502506 big_int_payload.* = .{ .limbs = big_int.limbs };
503 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBig);507 break :blk &big_int_payload.base;
504 big_int_payload.* = .{ .big_int = big_int };508 };
505509
506 return self.constInst(src, .{510 return self.constInst(src, .{
507 .ty = ty,511 .ty = ty,
508 .val = Value.initPayload(&big_int_payload.base),512 .val = Value.initPayload(val_payload),
509 });513 });
510 }514 }
511515
...@@ -745,19 +749,31 @@ const Analyze = struct {...@@ -745,19 +749,31 @@ const Analyze = struct {
745 var rhs_space: Value.BigIntSpace = undefined;749 var rhs_space: Value.BigIntSpace = undefined;
746 const lhs_bigint = lhs_val.toBigInt(&lhs_space);750 const lhs_bigint = lhs_val.toBigInt(&lhs_space);
747 const rhs_bigint = rhs_val.toBigInt(&rhs_space);751 const rhs_bigint = rhs_val.toBigInt(&rhs_space);
748 var result_bigint = try BigInt.init(&self.arena.allocator);752 const limbs = try self.arena.allocator.alloc(
749 try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint);753 std.math.big.Limb,
754 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
755 );
756 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
757 result_bigint.add(lhs_bigint, rhs_bigint);
758 const result_limbs = result_bigint.limbs[0..result_bigint.len];
750759
751 if (!lhs.ty.eql(rhs.ty)) {760 if (!lhs.ty.eql(rhs.ty)) {
752 return self.fail(inst.base.src, "TODO implement peer type resolution", .{});761 return self.fail(inst.base.src, "TODO implement peer type resolution", .{});
753 }762 }
754763
755 const val_payload = try self.arena.allocator.create(Value.Payload.IntBig);764 const val_payload = if (result_bigint.positive) blk: {
756 val_payload.* = .{ .big_int = result_bigint };765 const val_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive);
766 val_payload.* = .{ .limbs = result_limbs };
767 break :blk &val_payload.base;
768 } else blk: {
769 const val_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative);
770 val_payload.* = .{ .limbs = result_limbs };
771 break :blk &val_payload.base;
772 };
757773
758 return self.constInst(inst.base.src, .{774 return self.constInst(inst.base.src, .{
759 .ty = lhs.ty,775 .ty = lhs.ty,
760 .val = Value.initPayload(&val_payload.base),776 .val = Value.initPayload(val_payload),
761 });777 });
762 }778 }
763 }779 }
...@@ -1076,7 +1092,8 @@ const Analyze = struct {...@@ -1076,7 +1092,8 @@ const Analyze = struct {
1076 return self.constUndef(src, Type.initTag(.bool));1092 return self.constUndef(src, Type.initTag(.bool));
1077 const is_unsigned = if (lhs_is_float) x: {1093 const is_unsigned = if (lhs_is_float) x: {
1078 var bigint_space: Value.BigIntSpace = undefined;1094 var bigint_space: Value.BigIntSpace = undefined;
1079 var bigint = lhs_val.toBigInt(&bigint_space);1095 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1096 defer bigint.deinit();
1080 const zcmp = lhs_val.orderAgainstZero();1097 const zcmp = lhs_val.orderAgainstZero();
1081 if (lhs_val.floatHasFraction()) {1098 if (lhs_val.floatHasFraction()) {
1082 switch (op) {1099 switch (op) {
...@@ -1085,12 +1102,12 @@ const Analyze = struct {...@@ -1085,12 +1102,12 @@ const Analyze = struct {
1085 else => {},1102 else => {},
1086 }1103 }
1087 if (zcmp == .lt) {1104 if (zcmp == .lt) {
1088 try bigint.addScalar(bigint, -1);1105 try bigint.addScalar(bigint.toConst(), -1);
1089 } else {1106 } else {
1090 try bigint.addScalar(bigint, 1);1107 try bigint.addScalar(bigint.toConst(), 1);
1091 }1108 }
1092 }1109 }
1093 lhs_bits = bigint.bitCountTwosComp();1110 lhs_bits = bigint.toConst().bitCountTwosComp();
1094 break :x (zcmp != .lt);1111 break :x (zcmp != .lt);
1095 } else x: {1112 } else x: {
1096 lhs_bits = lhs_val.intBitCountTwosComp();1113 lhs_bits = lhs_val.intBitCountTwosComp();
...@@ -1110,7 +1127,8 @@ const Analyze = struct {...@@ -1110,7 +1127,8 @@ const Analyze = struct {
1110 return self.constUndef(src, Type.initTag(.bool));1127 return self.constUndef(src, Type.initTag(.bool));
1111 const is_unsigned = if (rhs_is_float) x: {1128 const is_unsigned = if (rhs_is_float) x: {
1112 var bigint_space: Value.BigIntSpace = undefined;1129 var bigint_space: Value.BigIntSpace = undefined;
1113 var bigint = rhs_val.toBigInt(&bigint_space);1130 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1131 defer bigint.deinit();
1114 const zcmp = rhs_val.orderAgainstZero();1132 const zcmp = rhs_val.orderAgainstZero();
1115 if (rhs_val.floatHasFraction()) {1133 if (rhs_val.floatHasFraction()) {
1116 switch (op) {1134 switch (op) {
...@@ -1119,12 +1137,12 @@ const Analyze = struct {...@@ -1119,12 +1137,12 @@ const Analyze = struct {
1119 else => {},1137 else => {},
1120 }1138 }
1121 if (zcmp == .lt) {1139 if (zcmp == .lt) {
1122 try bigint.addScalar(bigint, -1);1140 try bigint.addScalar(bigint.toConst(), -1);
1123 } else {1141 } else {
1124 try bigint.addScalar(bigint, 1);1142 try bigint.addScalar(bigint.toConst(), 1);
1125 }1143 }
1126 }1144 }
1127 rhs_bits = bigint.bitCountTwosComp();1145 rhs_bits = bigint.toConst().bitCountTwosComp();
1128 break :x (zcmp != .lt);1146 break :x (zcmp != .lt);
1129 } else x: {1147 } else x: {
1130 rhs_bits = rhs_val.intBitCountTwosComp();1148 rhs_bits = rhs_val.intBitCountTwosComp();
src-self-hosted/ir/text.zig+20-15
...@@ -4,7 +4,8 @@ const std = @import("std");...@@ -4,7 +4,8 @@ const std = @import("std");
4const mem = std.mem;4const mem = std.mem;
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const BigInt = std.math.big.Int;7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
8const Type = @import("../type.zig").Type;9const Type = @import("../type.zig").Type;
9const Value = @import("../value.zig").Value;10const Value = @import("../value.zig").Value;
10const ir = @import("../ir.zig");11const ir = @import("../ir.zig");
...@@ -99,7 +100,7 @@ pub const Inst = struct {...@@ -99,7 +100,7 @@ pub const Inst = struct {
99 base: Inst,100 base: Inst,
100101
101 positionals: struct {102 positionals: struct {
102 int: BigInt,103 int: BigIntConst,
103 },104 },
104 kw_args: struct {},105 kw_args: struct {},
105 };106 };
...@@ -521,7 +522,7 @@ pub const Module = struct {...@@ -521,7 +522,7 @@ pub const Module = struct {
521 },522 },
522 bool => return stream.writeByte("01"[@boolToInt(param)]),523 bool => return stream.writeByte("01"[@boolToInt(param)]),
523 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),524 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
524 BigInt => return stream.print("{}", .{param}),525 BigIntConst => return stream.print("{}", .{param}),
525 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),526 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
526 }527 }
527 }528 }
...@@ -644,7 +645,7 @@ const Parser = struct {...@@ -644,7 +645,7 @@ const Parser = struct {
644 };645 };
645 }646 }
646647
647 fn parseIntegerLiteral(self: *Parser) !BigInt {648 fn parseIntegerLiteral(self: *Parser) !BigIntConst {
648 const start = self.i;649 const start = self.i;
649 if (self.source[self.i] == '-') self.i += 1;650 if (self.source[self.i] == '-') self.i += 1;
650 while (true) : (self.i += 1) switch (self.source[self.i]) {651 while (true) : (self.i += 1) switch (self.source[self.i]) {
...@@ -652,17 +653,21 @@ const Parser = struct {...@@ -652,17 +653,21 @@ const Parser = struct {
652 else => break,653 else => break,
653 };654 };
654 const number_text = self.source[start..self.i];655 const number_text = self.source[start..self.i];
655 var result = try BigInt.init(&self.arena.allocator);656 const base = 10;
656 result.setString(10, number_text) catch |err| {657 // TODO reuse the same array list for this
657 self.i = start;658 const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
658 switch (err) {659 const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
659 error.InvalidBase => unreachable,660 defer self.allocator.free(limbs_buffer);
660 error.InvalidCharForDigit => return self.fail("invalid digit in integer literal", .{}),661 const limb_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
661 error.DigitTooLargeForBase => return self.fail("digit too large in integer literal", .{}),662 const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
662 else => |e| return e,663 var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
663 }664 result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
665 error.InvalidCharacter => {
666 self.i = start;
667 return self.fail("invalid digit in integer literal", .{});
668 },
664 };669 };
665 return result;670 return result.toConst();
666 }671 }
667672
668 fn parseRoot(self: *Parser) !void {673 fn parseRoot(self: *Parser) !void {
...@@ -859,7 +864,7 @@ const Parser = struct {...@@ -859,7 +864,7 @@ const Parser = struct {
859 },864 },
860 *Inst => return parseParameterInst(self, body_ctx),865 *Inst => return parseParameterInst(self, body_ctx),
861 []u8, []const u8 => return self.parseStringLiteral(),866 []u8, []const u8 => return self.parseStringLiteral(),
862 BigInt => return self.parseIntegerLiteral(),867 BigIntConst => return self.parseIntegerLiteral(),
863 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),868 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
864 }869 }
865 return self.fail("TODO parse parameter {}", .{@typeName(T)});870 return self.fail("TODO parse parameter {}", .{@typeName(T)});
src-self-hosted/translate_c.zig+16-14
...@@ -3913,18 +3913,20 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {...@@ -3913,18 +3913,20 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
3913 };3913 };
3914 var aps_int = int;3914 var aps_int = int;
3915 const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);3915 const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
3916 if (is_negative)3916 if (is_negative) aps_int = ZigClangAPSInt_negate(aps_int);
3917 aps_int = ZigClangAPSInt_negate(aps_int);3917 defer if (is_negative) {
3918 var big = try math.big.Int.initCapacity(c.a(), num_limbs);3918 ZigClangAPSInt_free(aps_int);
3919 if (is_negative)3919 };
3920 big.negate();3920
3921 defer big.deinit();3921 const limbs = try c.a().alloc(math.big.Limb, num_limbs);
3922 defer c.a().free(limbs);
3923
3922 const data = ZigClangAPSInt_getRawData(aps_int);3924 const data = ZigClangAPSInt_getRawData(aps_int);
3923 switch (@sizeOf(std.math.big.Limb)) {3925 switch (@sizeOf(math.big.Limb)) {
3924 8 => {3926 8 => {
3925 var i: usize = 0;3927 var i: usize = 0;
3926 while (i < num_limbs) : (i += 1) {3928 while (i < num_limbs) : (i += 1) {
3927 big.limbs[i] = data[i];3929 limbs[i] = data[i];
3928 }3930 }
3929 },3931 },
3930 4 => {3932 4 => {
...@@ -3934,23 +3936,23 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {...@@ -3934,23 +3936,23 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
3934 limb_i += 2;3936 limb_i += 2;
3935 data_i += 1;3937 data_i += 1;
3936 }) {3938 }) {
3937 big.limbs[limb_i] = @truncate(u32, data[data_i]);3939 limbs[limb_i] = @truncate(u32, data[data_i]);
3938 big.limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);3940 limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
3939 }3941 }
3940 },3942 },
3941 else => @compileError("unimplemented"),3943 else => @compileError("unimplemented"),
3942 }3944 }
3943 const str = big.toString(c.a(), 10, false) catch |err| switch (err) {3945
3946 const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative };
3947 const str = big.toStringAlloc(c.a(), 10, false) catch |err| switch (err) {
3944 error.OutOfMemory => return error.OutOfMemory,3948 error.OutOfMemory => return error.OutOfMemory,
3945 else => unreachable,
3946 };3949 };
3950 defer c.a().free(str);
3947 const token = try appendToken(c, .IntegerLiteral, str);3951 const token = try appendToken(c, .IntegerLiteral, str);
3948 const node = try c.a().create(ast.Node.IntegerLiteral);3952 const node = try c.a().create(ast.Node.IntegerLiteral);
3949 node.* = .{3953 node.* = .{
3950 .token = token,3954 .token = token,
3951 };3955 };
3952 if (is_negative)
3953 ZigClangAPSInt_free(aps_int);
3954 return &node.base;3956 return &node.base;
3955}3957}
39563958
src-self-hosted/value.zig+55-22
...@@ -2,7 +2,8 @@ const std = @import("std");...@@ -2,7 +2,8 @@ const std = @import("std");
2const Type = @import("type.zig").Type;2const Type = @import("type.zig").Type;
3const log2 = std.math.log2;3const log2 = std.math.log2;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const BigInt = std.math.big.Int;5const BigIntConst = std.math.big.int.Const;
6const BigIntMutable = std.math.big.int.Mutable;
6const Target = std.Target;7const Target = std.Target;
7const Allocator = std.mem.Allocator;8const Allocator = std.mem.Allocator;
89
...@@ -60,7 +61,8 @@ pub const Value = extern union {...@@ -60,7 +61,8 @@ pub const Value = extern union {
60 ty,61 ty,
61 int_u64,62 int_u64,
62 int_i64,63 int_i64,
63 int_big,64 int_big_positive,
65 int_big_negative,
64 function,66 function,
65 ref,67 ref,
66 ref_val,68 ref_val,
...@@ -148,7 +150,8 @@ pub const Value = extern union {...@@ -148,7 +150,8 @@ pub const Value = extern union {
148 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),150 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
149 .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),151 .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
150 .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),152 .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
151 .int_big => return out_stream.print("{}", .{val.cast(Payload.IntBig).?.big_int}),153 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
154 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
152 .function => return out_stream.writeAll("(function)"),155 .function => return out_stream.writeAll("(function)"),
153 .ref => return out_stream.writeAll("(ref)"),156 .ref => return out_stream.writeAll("(ref)"),
154 .ref_val => {157 .ref_val => {
...@@ -216,7 +219,8 @@ pub const Value = extern union {...@@ -216,7 +219,8 @@ pub const Value = extern union {
216 .null_value,219 .null_value,
217 .int_u64,220 .int_u64,
218 .int_i64,221 .int_i64,
219 .int_big,222 .int_big_positive,
223 .int_big_negative,
220 .function,224 .function,
221 .ref,225 .ref,
222 .ref_val,226 .ref_val,
...@@ -227,7 +231,7 @@ pub const Value = extern union {...@@ -227,7 +231,7 @@ pub const Value = extern union {
227 }231 }
228232
229 /// Asserts the value is an integer.233 /// Asserts the value is an integer.
230 pub fn toBigInt(self: Value, space: *BigIntSpace) BigInt {234 pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
231 switch (self.tag()) {235 switch (self.tag()) {
232 .ty,236 .ty,
233 .u8_type,237 .u8_type,
...@@ -272,11 +276,12 @@ pub const Value = extern union {...@@ -272,11 +276,12 @@ pub const Value = extern union {
272276
273 .the_one_possible_value, // An integer with one possible value is always zero.277 .the_one_possible_value, // An integer with one possible value is always zero.
274 .zero,278 .zero,
275 => return BigInt.initSetFixed(&space.limbs, 0),279 => return BigIntMutable.init(&space.limbs, 0).toConst(),
276280
277 .int_u64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_u64).?.int),281 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
278 .int_i64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_i64).?.int),282 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
279 .int_big => return self.cast(Payload.IntBig).?.big_int,283 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
284 .int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(),
280 }285 }
281 }286 }
282287
...@@ -330,7 +335,8 @@ pub const Value = extern union {...@@ -330,7 +335,8 @@ pub const Value = extern union {
330335
331 .int_u64 => return self.cast(Payload.Int_u64).?.int,336 .int_u64 => return self.cast(Payload.Int_u64).?.int,
332 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),337 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
333 .int_big => return self.cast(Payload.IntBig).?.big_int.to(u64) catch unreachable,338 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
339 .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable,
334 }340 }
335 }341 }
336342
...@@ -391,7 +397,8 @@ pub const Value = extern union {...@@ -391,7 +397,8 @@ pub const Value = extern union {
391 .int_i64 => {397 .int_i64 => {
392 @panic("TODO implement i64 intBitCountTwosComp");398 @panic("TODO implement i64 intBitCountTwosComp");
393 },399 },
394 .int_big => return self.cast(Payload.IntBig).?.big_int.bitCountTwosComp(),400 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().bitCountTwosComp(),
401 .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().bitCountTwosComp(),
395 }402 }
396 }403 }
397404
...@@ -466,10 +473,18 @@ pub const Value = extern union {...@@ -466,10 +473,18 @@ pub const Value = extern union {
466 .ComptimeInt => return true,473 .ComptimeInt => return true,
467 else => unreachable,474 else => unreachable,
468 },475 },
469 .int_big => switch (ty.zigTypeTag()) {476 .int_big_positive => switch (ty.zigTypeTag()) {
470 .Int => {477 .Int => {
471 const info = ty.intInfo(target);478 const info = ty.intInfo(target);
472 return self.cast(Payload.IntBig).?.big_int.fitsInTwosComp(info.signed, info.bits);479 return self.cast(Payload.IntBigPositive).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
480 },
481 .ComptimeInt => return true,
482 else => unreachable,
483 },
484 .int_big_negative => switch (ty.zigTypeTag()) {
485 .Int => {
486 const info = ty.intInfo(target);
487 return self.cast(Payload.IntBigNegative).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
473 },488 },
474 .ComptimeInt => return true,489 .ComptimeInt => return true,
475 else => unreachable,490 else => unreachable,
...@@ -521,7 +536,8 @@ pub const Value = extern union {...@@ -521,7 +536,8 @@ pub const Value = extern union {
521 .undef,536 .undef,
522 .int_u64,537 .int_u64,
523 .int_i64,538 .int_i64,
524 .int_big,539 .int_big_positive,
540 .int_big_negative,
525 .the_one_possible_value,541 .the_one_possible_value,
526 => unreachable,542 => unreachable,
527543
...@@ -578,7 +594,8 @@ pub const Value = extern union {...@@ -578,7 +594,8 @@ pub const Value = extern union {
578594
579 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),595 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
580 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),596 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
581 .int_big => return lhs.cast(Payload.IntBig).?.big_int.orderAgainstScalar(0),597 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
598 .int_big_negative => return lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
582 }599 }
583 }600 }
584601
...@@ -597,7 +614,7 @@ pub const Value = extern union {...@@ -597,7 +614,7 @@ pub const Value = extern union {
597 var rhs_bigint_space: BigIntSpace = undefined;614 var rhs_bigint_space: BigIntSpace = undefined;
598 const lhs_bigint = lhs.toBigInt(&lhs_bigint_space);615 const lhs_bigint = lhs.toBigInt(&lhs_bigint_space);
599 const rhs_bigint = rhs.toBigInt(&rhs_bigint_space);616 const rhs_bigint = rhs.toBigInt(&rhs_bigint_space);
600 return BigInt.cmp(lhs_bigint, rhs_bigint);617 return lhs_bigint.order(rhs_bigint);
601 }618 }
602619
603 /// Asserts the value is comparable.620 /// Asserts the value is comparable.
...@@ -658,7 +675,8 @@ pub const Value = extern union {...@@ -658,7 +675,8 @@ pub const Value = extern union {
658 .function,675 .function,
659 .int_u64,676 .int_u64,
660 .int_i64,677 .int_i64,
661 .int_big,678 .int_big_positive,
679 .int_big_negative,
662 .bytes,680 .bytes,
663 .undef,681 .undef,
664 .repeated,682 .repeated,
...@@ -712,7 +730,8 @@ pub const Value = extern union {...@@ -712,7 +730,8 @@ pub const Value = extern union {
712 .function,730 .function,
713 .int_u64,731 .int_u64,
714 .int_i64,732 .int_i64,
715 .int_big,733 .int_big_positive,
734 .int_big_negative,
716 .undef,735 .undef,
717 => unreachable,736 => unreachable,
718737
...@@ -775,7 +794,8 @@ pub const Value = extern union {...@@ -775,7 +794,8 @@ pub const Value = extern union {
775 .function,794 .function,
776 .int_u64,795 .int_u64,
777 .int_i64,796 .int_i64,
778 .int_big,797 .int_big_positive,
798 .int_big_negative,
779 .ref,799 .ref,
780 .ref_val,800 .ref_val,
781 .bytes,801 .bytes,
...@@ -801,9 +821,22 @@ pub const Value = extern union {...@@ -801,9 +821,22 @@ pub const Value = extern union {
801 int: i64,821 int: i64,
802 };822 };
803823
804 pub const IntBig = struct {824 pub const IntBigPositive = struct {
805 base: Payload = Payload{ .tag = .int_big },825 base: Payload = Payload{ .tag = .int_big_positive },
806 big_int: BigInt,826 limbs: []const std.math.big.Limb,
827
828 pub fn asBigInt(self: IntBigPositive) BigIntConst {
829 return BigIntConst{ .limbs = self.limbs, .positive = true };
830 }
831 };
832
833 pub const IntBigNegative = struct {
834 base: Payload = Payload{ .tag = .int_big_negative },
835 limbs: []const std.math.big.Limb,
836
837 pub fn asBigInt(self: IntBigNegative) BigIntConst {
838 return BigIntConst{ .limbs = self.limbs, .positive = false };
839 }
807 };840 };
808841
809 pub const Function = struct {842 pub const Function = struct {