authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2018-06-09 10:24:20+12:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2018-06-10 18:24:34+12:00
logdc8bda7e0203410132e0689b7561d9e8731176e9
tree130a82f97f7b59279c15401a2442df8d1c7d87bc
parent0a95b0f1ffeb8ae5ee317b02626890adebe5ec63

Add arbitrary-precision integer to std

A few notes on the implementation: - Any unsigned power of two integer type less than 64 bits in size is supported as a Limb type. - The algorithms used are kept simple for the moment. More complicated algorithms are generally only more useful as integer sizes increase a lot and I don't expect our current usage to be used for this purpose just yet. - All branches (practically) have been covered by tests. See https://github.com/tiehuis/zig-bn/tree/986a2b3243d0454b8430a6adf4ad48611850c1b8/bench for rough performance comparison numbers. Closes #364.

4 files changed, 2033 insertions(+), 0 deletions(-)

CMakeLists.txt+1
...@@ -464,6 +464,7 @@ set(ZIG_STD_FILES...@@ -464,6 +464,7 @@ set(ZIG_STD_FILES
464 "math/atan.zig"464 "math/atan.zig"
465 "math/atan2.zig"465 "math/atan2.zig"
466 "math/atanh.zig"466 "math/atanh.zig"
467 "math/big/int.zig"
467 "math/cbrt.zig"468 "math/cbrt.zig"
468 "math/ceil.zig"469 "math/ceil.zig"
469 "math/complex/abs.zig"470 "math/complex/abs.zig"
std/math/big/index.zig created+5
...@@ -0,0 +1,5 @@
1pub use @import("int.zig");
2
3test "math.big" {
4 _ = @import("int.zig");
5}
std/math/big/int.zig created+2023
...@@ -0,0 +1,2023 @@
1const std = @import("../../index.zig");
2const builtin = @import("builtin");
3const debug = std.debug;
4const math = std.math;
5const mem = std.mem;
6const Allocator = mem.Allocator;
7const ArrayList = std.ArrayList;
8
9const TypeId = builtin.TypeId;
10
11pub const Limb = usize;
12pub const DoubleLimb = @IntType(false, 2 * Limb.bit_count);
13pub const Log2Limb = math.Log2Int(Limb);
14
15comptime {
16 debug.assert(math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);
17 debug.assert(Limb.bit_count <= 64); // u128 set is unsupported
18 debug.assert(Limb.is_signed == false);
19}
20
21const wrapped_buffer_size = 512;
22
23// Converts primitive integer values onto a stack-based big integer, or passes through existing
24// Int types with no modifications. This can fail at runtime if using a very large dynamic
25// integer but it is very unlikely and is considered a user error.
26fn wrapInt(allocator: *Allocator, bn: var) *const Int {
27 const T = @typeOf(bn);
28 switch (@typeInfo(T)) {
29 TypeId.Pointer => |info| {
30 if (info.child == Int) {
31 return bn;
32 } else {
33 @compileError("cannot set Int using type " ++ @typeName(T));
34 }
35 },
36 else => {
37 var s = allocator.create(Int) catch unreachable;
38 s.* = Int{
39 .allocator = allocator,
40 .positive = false,
41 .limbs = block: {
42 var limbs = allocator.alloc(Limb, Int.default_capacity) catch unreachable;
43 limbs[0] = 0;
44 break :block limbs;
45 },
46 .len = 1,
47 };
48 s.set(bn) catch unreachable;
49 return s;
50 },
51 }
52}
53
54pub const Int = struct {
55 allocator: *Allocator,
56 positive: bool,
57 // - little-endian ordered
58 // - len >= 1 always
59 // - zero value -> len == 1 with limbs[0] == 0
60 limbs: []Limb,
61 len: usize,
62
63 const default_capacity = 4;
64
65 pub fn init(allocator: *Allocator) !Int {
66 return try Int.initCapacity(allocator, default_capacity);
67 }
68
69 pub fn initSet(allocator: *Allocator, value: var) !Int {
70 var s = try Int.init(allocator);
71 try s.set(value);
72 return s;
73 }
74
75 pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int {
76 return Int{
77 .allocator = allocator,
78 .positive = true,
79 .limbs = block: {
80 var limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));
81 limbs[0] = 0;
82 break :block limbs;
83 },
84 .len = 1,
85 };
86 }
87
88 pub fn ensureCapacity(self: *Int, capacity: usize) !void {
89 if (capacity <= self.limbs.len) {
90 return;
91 }
92
93 self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);
94 }
95
96 pub fn deinit(self: *const Int) void {
97 self.allocator.free(self.limbs);
98 }
99
100 pub fn clone(other: *const Int) !Int {
101 return Int{
102 .allocator = other.allocator,
103 .positive = other.positive,
104 .limbs = block: {
105 var limbs = try other.allocator.alloc(Limb, other.len);
106 mem.copy(Limb, limbs[0..], other.limbs[0..other.len]);
107 break :block limbs;
108 },
109 .len = other.len,
110 };
111 }
112
113 pub fn copy(self: *Int, other: *const Int) !void {
114 if (self == other) {
115 return;
116 }
117
118 self.positive = other.positive;
119 try self.ensureCapacity(other.len);
120 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.len]);
121 self.len = other.len;
122 }
123
124 pub fn swap(self: *Int, other: *Int) void {
125 mem.swap(Int, self, other);
126 }
127
128 pub fn dump(self: *const Int) void {
129 for (self.limbs) |limb| {
130 debug.warn("{x} ", limb);
131 }
132 debug.warn("\n");
133 }
134
135 pub fn negate(r: *Int) void {
136 r.positive = !r.positive;
137 }
138
139 pub fn abs(r: *Int) void {
140 r.positive = true;
141 }
142
143 pub fn isOdd(r: *const Int) bool {
144 return r.limbs[0] & 1 != 0;
145 }
146
147 pub fn isEven(r: *const Int) bool {
148 return !r.isOdd();
149 }
150
151 fn bitcount(self: *const Int) usize {
152 const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
153 return usize(!self.positive) + u_bit_count;
154 }
155
156 pub fn sizeInBase(self: *const Int, base: usize) usize {
157 return (self.bitcount() / math.log2(base)) + 1;
158 }
159
160 pub fn set(self: *Int, value: var) Allocator.Error!void {
161 const T = @typeOf(value);
162
163 switch (@typeInfo(T)) {
164 TypeId.Int => |info| {
165 const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T;
166
167 try self.ensureCapacity(@sizeOf(UT) / @sizeOf(Limb));
168 self.positive = value >= 0;
169 self.len = 0;
170
171 var w_value: UT = if (value < 0) UT(-value) else UT(value);
172
173 if (info.bits <= Limb.bit_count) {
174 self.limbs[0] = Limb(w_value);
175 self.len = 1;
176 } else {
177 var i: usize = 0;
178 while (w_value != 0) : (i += 1) {
179 self.limbs[i] = @truncate(Limb, w_value);
180 self.len += 1;
181
182 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
183 w_value >>= Limb.bit_count / 2;
184 w_value >>= Limb.bit_count / 2;
185 }
186 }
187 },
188 TypeId.ComptimeInt => {
189 comptime var w_value = if (value < 0) -value else value;
190
191 const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;
192 try self.ensureCapacity(req_limbs);
193
194 self.positive = value >= 0;
195 self.len = req_limbs;
196
197 if (w_value <= @maxValue(Limb)) {
198 self.limbs[0] = w_value;
199 } else {
200 const mask = (1 << Limb.bit_count) - 1;
201
202 comptime var i = 0;
203 inline while (w_value != 0) : (i += 1) {
204 self.limbs[i] = w_value & mask;
205
206 w_value >>= Limb.bit_count / 2;
207 w_value >>= Limb.bit_count / 2;
208 }
209 }
210 },
211 else => {
212 @compileError("cannot set Int using type " ++ @typeName(T));
213 },
214 }
215 }
216
217 pub const ConvertError = error{
218 NegativeIntoUnsigned,
219 TargetTooSmall,
220 };
221
222 pub fn to(self: *const Int, comptime T: type) ConvertError!T {
223 switch (@typeId(T)) {
224 TypeId.Int => {
225 const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T;
226
227 if (self.bitcount() > 8 * @sizeOf(UT)) {
228 return error.TargetTooSmall;
229 }
230
231 var r: UT = 0;
232
233 if (@sizeOf(UT) <= @sizeOf(Limb)) {
234 r = UT(self.limbs[0]);
235 } else {
236 for (self.limbs[0..self.len]) |_, ri| {
237 const limb = self.limbs[self.len - ri - 1];
238 r <<= Limb.bit_count;
239 r |= limb;
240 }
241 }
242
243 if (!T.is_signed) {
244 return if (self.positive) r else error.NegativeIntoUnsigned;
245 } else {
246 return if (self.positive) T(r) else -T(r);
247 }
248 },
249 else => {
250 @compileError("cannot convert Int to type " ++ @typeName(T));
251 },
252 }
253 }
254
255 fn charToDigit(ch: u8, base: u8) !u8 {
256 const d = switch (ch) {
257 '0'...'9' => ch - '0',
258 'a'...'f' => (ch - 'a') + 0xa,
259 else => return error.InvalidCharForDigit,
260 };
261
262 return if (d < base) d else return error.DigitTooLargeForBase;
263 }
264
265 fn digitToChar(d: u8, base: u8) !u8 {
266 if (d >= base) {
267 return error.DigitTooLargeForBase;
268 }
269
270 return switch (d) {
271 0...9 => '0' + d,
272 0xa...0xf => ('a' - 0xa) + d,
273 else => unreachable,
274 };
275 }
276
277 pub fn setString(self: *Int, base: u8, value: []const u8) !void {
278 if (base < 2 or base > 16) {
279 return error.InvalidBase;
280 }
281
282 var i: usize = 0;
283 var positive = true;
284 if (value.len > 0 and value[0] == '-') {
285 positive = false;
286 i += 1;
287 }
288
289 try self.set(0);
290 for (value[i..]) |ch| {
291 const d = try charToDigit(ch, base);
292 try self.mul(self, base);
293 try self.add(self, d);
294 }
295 self.positive = positive;
296 }
297
298 pub fn toString(self: *const Int, allocator: *Allocator, base: u8) ![]const u8 {
299 if (base < 2 or base > 16) {
300 return error.InvalidBase;
301 }
302
303 var digits = ArrayList(u8).init(allocator);
304 try digits.ensureCapacity(self.sizeInBase(base) + 1);
305 defer digits.deinit();
306
307 if (self.eqZero()) {
308 try digits.append('0');
309 return digits.toOwnedSlice();
310 }
311
312 // Power of two: can do a single pass and use masks to extract digits.
313 if (base & (base - 1) == 0) {
314 const base_shift = math.log2_int(Limb, base);
315
316 for (self.limbs[0..self.len]) |limb| {
317 var shift: usize = 0;
318 while (shift < Limb.bit_count) : (shift += base_shift) {
319 const r = u8((limb >> Log2Limb(shift)) & Limb(base - 1));
320 const ch = try digitToChar(r, base);
321 try digits.append(ch);
322 }
323 }
324
325 while (true) {
326 // always will have a non-zero digit somewhere
327 const c = digits.pop();
328 if (c != '0') {
329 digits.append(c) catch unreachable;
330 break;
331 }
332 }
333 } // Non power-of-two: batch divisions per word size.
334 else {
335 const digits_per_limb = math.log(Limb, base, @maxValue(Limb));
336 var limb_base: Limb = 1;
337 var j: usize = 0;
338 while (j < digits_per_limb) : (j += 1) {
339 limb_base *= base;
340 }
341
342 var q = try self.clone();
343 q.positive = true;
344 var r = try Int.init(allocator);
345 var b = try Int.initSet(allocator, limb_base);
346
347 while (q.len >= 2) {
348 try Int.divTrunc(&q, &r, &q, &b);
349
350 var r_word = r.limbs[0];
351 var i: usize = 0;
352 while (i < digits_per_limb) : (i += 1) {
353 const ch = try digitToChar(u8(r_word % base), base);
354 r_word /= base;
355 try digits.append(ch);
356 }
357 }
358
359 {
360 debug.assert(q.len == 1);
361
362 var r_word = q.limbs[0];
363 while (r_word != 0) {
364 const ch = try digitToChar(u8(r_word % base), base);
365 r_word /= base;
366 try digits.append(ch);
367 }
368 }
369 }
370
371 if (!self.positive) {
372 try digits.append('-');
373 }
374
375 var s = digits.toOwnedSlice();
376 mem.reverse(u8, s);
377 return s;
378 }
379
380 // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
381 pub fn cmpAbs(a: *const Int, bv: var) i8 {
382 // TODO: Thread-local buffer.
383 var buffer: [wrapped_buffer_size]u8 = undefined;
384 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
385 var b = wrapInt(&stack.allocator, bv);
386
387 if (a.len < b.len) {
388 return -1;
389 }
390 if (a.len > b.len) {
391 return 1;
392 }
393
394 var i: usize = a.len - 1;
395 while (i != 0) : (i -= 1) {
396 if (a.limbs[i] != b.limbs[i]) {
397 break;
398 }
399 }
400
401 if (a.limbs[i] < b.limbs[i]) {
402 return -1;
403 } else if (a.limbs[i] > b.limbs[i]) {
404 return 1;
405 } else {
406 return 0;
407 }
408 }
409
410 // returns -1, 0, 1 if a < b, a == b or a > b respectively.
411 pub fn cmp(a: *const Int, bv: var) i8 {
412 var buffer: [wrapped_buffer_size]u8 = undefined;
413 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
414 var b = wrapInt(&stack.allocator, bv);
415
416 if (a.positive != b.positive) {
417 return if (a.positive) i8(1) else -1;
418 } else {
419 const r = cmpAbs(a, b);
420 return if (a.positive) r else -r;
421 }
422 }
423
424 // if a == 0
425 pub fn eqZero(a: *const Int) bool {
426 return a.len == 1 and a.limbs[0] == 0;
427 }
428
429 // if |a| == |b|
430 pub fn eqAbs(a: *const Int, b: var) bool {
431 return cmpAbs(a, b) == 0;
432 }
433
434 // if a == b
435 pub fn eq(a: *const Int, b: var) bool {
436 return cmp(a, b) == 0;
437 }
438
439 // Normalize for a possible single carry digit.
440 //
441 // [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
442 // [1, 2, 3, 4, 5] -> [1, 2, 3, 4, 5]
443 // [0] -> [0]
444 fn norm1(r: *Int, length: usize) void {
445 debug.assert(length > 0);
446 debug.assert(length <= r.limbs.len);
447
448 if (r.limbs[length - 1] == 0) {
449 r.len = if (length > 1) length - 1 else 1;
450 } else {
451 r.len = length;
452 }
453 }
454
455 // Normalize a possible sequence of leading zeros.
456 //
457 // [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
458 // [1, 2, 0, 0, 0] -> [1, 2]
459 // [0, 0, 0, 0, 0] -> [0]
460 fn normN(r: *Int, length: usize) void {
461 debug.assert(length > 0);
462 debug.assert(length <= r.limbs.len);
463
464 var j = length;
465 while (j > 0) : (j -= 1) {
466 if (r.limbs[j - 1] != 0) {
467 break;
468 }
469 }
470
471 // Handle zero
472 r.len = if (j != 0) j else 1;
473 }
474
475 // r = a + b
476 pub fn add(r: *Int, av: var, bv: var) Allocator.Error!void {
477 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
478 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
479 var a = wrapInt(&stack.allocator, av);
480 var b = wrapInt(&stack.allocator, bv);
481
482 if (a.eqZero()) {
483 try r.copy(b);
484 return;
485 } else if (b.eqZero()) {
486 try r.copy(a);
487 return;
488 }
489
490 if (a.positive != b.positive) {
491 if (a.positive) {
492 // (a) + (-b) => a - b
493 const bp = Int{
494 .allocator = undefined,
495 .positive = true,
496 .limbs = b.limbs,
497 .len = b.len,
498 };
499 try r.sub(a, bp);
500 } else {
501 // (-a) + (b) => b - a
502 const ap = Int{
503 .allocator = undefined,
504 .positive = true,
505 .limbs = a.limbs,
506 .len = a.len,
507 };
508 try r.sub(b, ap);
509 }
510 } else {
511 if (a.len >= b.len) {
512 try r.ensureCapacity(a.len + 1);
513 lladd(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
514 r.norm1(a.len + 1);
515 } else {
516 try r.ensureCapacity(b.len + 1);
517 lladd(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
518 r.norm1(b.len + 1);
519 }
520
521 r.positive = a.positive;
522 }
523 }
524
525 // Knuth 4.3.1, Algorithm A.
526 fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
527 @setRuntimeSafety(false);
528 debug.assert(a.len != 0 and b.len != 0);
529 debug.assert(a.len >= b.len);
530 debug.assert(r.len >= a.len + 1);
531
532 var i: usize = 0;
533 var carry: Limb = 0;
534
535 while (i < b.len) : (i += 1) {
536 var c: Limb = 0;
537 c += Limb(@addWithOverflow(Limb, a[i], b[i], &r[i]));
538 c += Limb(@addWithOverflow(Limb, r[i], carry, &r[i]));
539 carry = c;
540 }
541
542 while (i < a.len) : (i += 1) {
543 carry = Limb(@addWithOverflow(Limb, a[i], carry, &r[i]));
544 }
545
546 r[i] = carry;
547 }
548
549 // r = a - b
550 pub fn sub(r: *Int, av: var, bv: var) !void {
551 var buffer: [wrapped_buffer_size]u8 = undefined;
552 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
553 var a = wrapInt(&stack.allocator, av);
554 var b = wrapInt(&stack.allocator, bv);
555
556 if (a.positive != b.positive) {
557 if (a.positive) {
558 // (a) - (-b) => a + b
559 const bp = Int{
560 .allocator = undefined,
561 .positive = true,
562 .limbs = b.limbs,
563 .len = b.len,
564 };
565 try r.add(a, bp);
566 } else {
567 // (-a) - (b) => -(a + b)
568 const ap = Int{
569 .allocator = undefined,
570 .positive = true,
571 .limbs = a.limbs,
572 .len = a.len,
573 };
574 try r.add(ap, b);
575 r.positive = false;
576 }
577 } else {
578 if (a.positive) {
579 // (a) - (b) => a - b
580 if (a.cmp(b) >= 0) {
581 try r.ensureCapacity(a.len + 1);
582 llsub(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
583 r.normN(a.len);
584 r.positive = true;
585 } else {
586 try r.ensureCapacity(b.len + 1);
587 llsub(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
588 r.normN(b.len);
589 r.positive = false;
590 }
591 } else {
592 // (-a) - (-b) => -(a - b)
593 if (a.cmp(b) < 0) {
594 try r.ensureCapacity(a.len + 1);
595 llsub(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
596 r.normN(a.len);
597 r.positive = false;
598 } else {
599 try r.ensureCapacity(b.len + 1);
600 llsub(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
601 r.normN(b.len);
602 r.positive = true;
603 }
604 }
605 }
606 }
607
608 // Knuth 4.3.1, Algorithm S.
609 fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
610 @setRuntimeSafety(false);
611 debug.assert(a.len != 0 and b.len != 0);
612 debug.assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));
613 debug.assert(r.len >= a.len);
614
615 var i: usize = 0;
616 var borrow: Limb = 0;
617
618 while (i < b.len) : (i += 1) {
619 var c: Limb = 0;
620 c += Limb(@subWithOverflow(Limb, a[i], b[i], &r[i]));
621 c += Limb(@subWithOverflow(Limb, r[i], borrow, &r[i]));
622 borrow = c;
623 }
624
625 while (i < a.len) : (i += 1) {
626 borrow = Limb(@subWithOverflow(Limb, a[i], borrow, &r[i]));
627 }
628
629 debug.assert(borrow == 0);
630 }
631
632 // rma = a * b
633 //
634 // For greatest efficiency, ensure rma does not alias a or b.
635 pub fn mul(rma: *Int, av: var, bv: var) !void {
636 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
637 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
638 var a = wrapInt(&stack.allocator, av);
639 var b = wrapInt(&stack.allocator, bv);
640
641 var r = rma;
642 var aliased = rma == a or rma == b;
643
644 var sr: Int = undefined;
645 if (aliased) {
646 sr = try Int.initCapacity(rma.allocator, a.len + b.len);
647 r = &sr;
648 aliased = true;
649 }
650 defer if (aliased) {
651 rma.swap(r);
652 r.deinit();
653 };
654
655 try r.ensureCapacity(a.len + b.len);
656
657 if (a.len >= b.len) {
658 llmul(r.limbs, a.limbs[0..a.len], b.limbs[0..b.len]);
659 } else {
660 llmul(r.limbs, b.limbs[0..b.len], a.limbs[0..a.len]);
661 }
662
663 r.positive = a.positive == b.positive;
664 r.normN(a.len + b.len);
665 }
666
667 // a + b * c + *carry, sets carry to the overflow bits
668 pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
669 var r1: Limb = undefined;
670
671 // r1 = a + *carry
672 const c1 = Limb(@addWithOverflow(Limb, a, carry.*, &r1));
673
674 // r2 = b * c
675 //
676 // We still use a DoubleLimb here since the @mulWithOverflow builtin does not
677 // return the carry and lower bits separately so we would need to perform this
678 // anyway to get the carry bits. The branch on the overflow case costs more than
679 // just computing them unconditionally and splitting.
680 //
681 // This could be a single x86 mul instruction, which stores the carry/lower in rdx:rax.
682 const bc = DoubleLimb(b) * DoubleLimb(c);
683 const r2 = @truncate(Limb, bc);
684 const c2 = @truncate(Limb, bc >> Limb.bit_count);
685
686 // r1 = r1 + r2
687 const c3 = Limb(@addWithOverflow(Limb, r1, r2, &r1));
688
689 // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then
690 // c2 is at least <= @maxValue(Limb) - 2.
691 carry.* = c1 + c2 + c3;
692
693 return r1;
694 }
695
696 // Knuth 4.3.1, Algorithm M.
697 //
698 // r MUST NOT alias any of a or b.
699 fn llmul(r: []Limb, a: []const Limb, b: []const Limb) void {
700 @setRuntimeSafety(false);
701 debug.assert(a.len >= b.len);
702 debug.assert(r.len >= a.len + b.len);
703
704 mem.set(Limb, r[0 .. a.len + b.len], 0);
705
706 var i: usize = 0;
707 while (i < a.len) : (i += 1) {
708 var carry: Limb = 0;
709 var j: usize = 0;
710 while (j < b.len) : (j += 1) {
711 r[i + j] = @inlineCall(addMulLimbWithCarry, r[i + j], a[i], b[j], &carry);
712 }
713 r[i + j] = carry;
714 }
715 }
716
717 pub fn divFloor(q: *Int, r: *Int, a: var, b: var) !void {
718 try div(q, r, a, b);
719
720 // Trunc -> Floor.
721 if (!q.positive) {
722 try q.sub(q, 1);
723 try r.add(q, 1);
724 }
725 r.positive = b.positive;
726 }
727
728 pub fn divTrunc(q: *Int, r: *Int, a: var, b: var) !void {
729 try div(q, r, a, b);
730 r.positive = a.positive;
731 }
732
733 // Truncates by default.
734 fn div(quo: *Int, rem: *Int, av: var, bv: var) !void {
735 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
736 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
737 var a = wrapInt(&stack.allocator, av);
738 var b = wrapInt(&stack.allocator, bv);
739
740 if (b.eqZero()) {
741 @panic("division by zero");
742 }
743 if (quo == rem) {
744 @panic("quo and rem cannot be same variable");
745 }
746
747 if (a.cmpAbs(b) < 0) {
748 // quo may alias a so handle rem first
749 try rem.copy(a);
750 rem.positive = a.positive == b.positive;
751
752 quo.positive = true;
753 quo.len = 1;
754 quo.limbs[0] = 0;
755 return;
756 }
757
758 if (b.len == 1) {
759 try quo.ensureCapacity(a.len);
760
761 lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[0..a.len], b.limbs[0]);
762 quo.norm1(a.len);
763 quo.positive = a.positive == b.positive;
764
765 rem.len = 1;
766 rem.positive = true;
767 } else {
768 // x and y are modified during division
769 var x = try a.clone();
770 defer x.deinit();
771
772 var y = try b.clone();
773 defer y.deinit();
774
775 // x may grow one limb during normalization
776 try quo.ensureCapacity(a.len + y.len);
777 try divN(quo.allocator, quo, rem, &x, &y);
778
779 quo.positive = a.positive == b.positive;
780 }
781 }
782
783 // Knuth 4.3.1, Exercise 16.
784 fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
785 @setRuntimeSafety(false);
786 debug.assert(a.len > 1 or a[0] >= b);
787 debug.assert(quo.len >= a.len);
788
789 rem.* = 0;
790 for (a) |_, ri| {
791 const i = a.len - ri - 1;
792 const pdiv = ((DoubleLimb(rem.*) << Limb.bit_count) | a[i]);
793
794 if (pdiv == 0) {
795 quo[i] = 0;
796 rem.* = 0;
797 } else if (pdiv < b) {
798 quo[i] = 0;
799 rem.* = @truncate(Limb, pdiv);
800 } else if (pdiv == b) {
801 quo[i] = 1;
802 rem.* = 0;
803 } else {
804 quo[i] = @truncate(Limb, @divTrunc(pdiv, b));
805 rem.* = @truncate(Limb, pdiv - (quo[i] *% b));
806 }
807 }
808 }
809
810 // Handbook of Applied Cryptography, 14.20
811 //
812 // x = qy + r where 0 <= r < y
813 fn divN(allocator: *Allocator, q: *Int, r: *Int, x: *Int, y: *Int) !void {
814 debug.assert(y.len >= 2);
815 debug.assert(x.len >= y.len);
816 debug.assert(q.limbs.len >= x.len + y.len - 1);
817 debug.assert(default_capacity >= 3); // see 3.2
818
819 var tmp = try Int.init(allocator);
820 defer tmp.deinit();
821
822 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set)
823 const norm_shift = @clz(y.limbs[y.len - 1]);
824 try x.shiftLeft(x, norm_shift);
825 try y.shiftLeft(y, norm_shift);
826
827 const n = x.len - 1;
828 const t = y.len - 1;
829
830 // 1.
831 q.len = n - t + 1;
832 mem.set(Limb, q.limbs[0..q.len], 0);
833
834 // 2.
835 try tmp.shiftLeft(y, Limb.bit_count * (n - t));
836 while (x.cmp(&tmp) >= 0) {
837 q.limbs[n - t] += 1;
838 try x.sub(x, tmp);
839 }
840
841 // 3.
842 var i = n;
843 while (i > t) : (i -= 1) {
844 // 3.1
845 if (x.limbs[i] == y.limbs[t]) {
846 q.limbs[i - t - 1] = @maxValue(Limb);
847 } else {
848 const num = (DoubleLimb(x.limbs[i]) << Limb.bit_count) | DoubleLimb(x.limbs[i - 1]);
849 const z = Limb(num / DoubleLimb(y.limbs[t]));
850 q.limbs[i - t - 1] = if (z > @maxValue(Limb)) @maxValue(Limb) else Limb(z);
851 }
852
853 // 3.2
854 tmp.limbs[0] = if (i >= 2) x.limbs[i - 2] else 0;
855 tmp.limbs[1] = if (i >= 1) x.limbs[i - 1] else 0;
856 tmp.limbs[2] = x.limbs[i];
857 tmp.normN(3);
858
859 while (true) {
860 // 2x1 limb multiplication unrolled against single-limb q[i-t-1]
861 var carry: Limb = 0;
862 r.limbs[0] = addMulLimbWithCarry(0, if (t >= 1) y.limbs[t - 1] else 0, q.limbs[i - t - 1], &carry);
863 r.limbs[1] = addMulLimbWithCarry(0, y.limbs[t], q.limbs[i - t - 1], &carry);
864 r.limbs[2] = carry;
865 r.normN(3);
866
867 if (r.cmpAbs(&tmp) <= 0) {
868 break;
869 }
870
871 q.limbs[i - t - 1] -= 1;
872 }
873
874 // 3.3
875 try tmp.set(q.limbs[i - t - 1]);
876 try tmp.mul(&tmp, y);
877 try tmp.shiftLeft(&tmp, Limb.bit_count * (i - t - 1));
878 try x.sub(x, &tmp);
879
880 if (!x.positive) {
881 try tmp.shiftLeft(y, Limb.bit_count * (i - t - 1));
882 try x.add(x, &tmp);
883 q.limbs[i - t - 1] -= 1;
884 }
885 }
886
887 // Denormalize
888 q.normN(q.len);
889
890 try r.shiftRight(x, norm_shift);
891 r.normN(r.len);
892 }
893
894 // r = a << shift, in other words, r = a * 2^shift
895 pub fn shiftLeft(r: *Int, av: var, shift: usize) !void {
896 var buffer: [wrapped_buffer_size]u8 = undefined;
897 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
898 var a = wrapInt(&stack.allocator, av);
899
900 try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1);
901 llshl(r.limbs[0..], a.limbs[0..a.len], shift);
902 r.norm1(a.len + (shift / Limb.bit_count) + 1);
903 r.positive = a.positive;
904 }
905
906 fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
907 @setRuntimeSafety(false);
908 debug.assert(a.len >= 1);
909 debug.assert(r.len >= a.len + (shift / Limb.bit_count) + 1);
910
911 const limb_shift = shift / Limb.bit_count + 1;
912 const interior_limb_shift = Log2Limb(shift % Limb.bit_count);
913
914 var carry: Limb = 0;
915 var i: usize = 0;
916 while (i < a.len) : (i += 1) {
917 const src_i = a.len - i - 1;
918 const dst_i = src_i + limb_shift;
919
920 const src_digit = a[src_i];
921 r[dst_i] = carry | @inlineCall(math.shr, Limb, src_digit, Limb.bit_count - Limb(interior_limb_shift));
922 carry = (src_digit << interior_limb_shift);
923 }
924
925 r[limb_shift - 1] = carry;
926 mem.set(Limb, r[0 .. limb_shift - 1], 0);
927 }
928
929 // r = a >> shift
930 pub fn shiftRight(r: *Int, av: var, shift: usize) !void {
931 var buffer: [wrapped_buffer_size]u8 = undefined;
932 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
933 var a = wrapInt(&stack.allocator, av);
934
935 if (a.len <= shift / Limb.bit_count) {
936 r.len = 1;
937 r.limbs[0] = 0;
938 r.positive = true;
939 return;
940 }
941
942 try r.ensureCapacity(a.len - (shift / Limb.bit_count));
943 const r_len = llshr(r.limbs[0..], a.limbs[0..a.len], shift);
944 r.len = a.len - (shift / Limb.bit_count);
945 r.positive = a.positive;
946 }
947
948 fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
949 @setRuntimeSafety(false);
950 debug.assert(a.len >= 1);
951 debug.assert(r.len >= a.len - (shift / Limb.bit_count));
952
953 const limb_shift = shift / Limb.bit_count;
954 const interior_limb_shift = Log2Limb(shift % Limb.bit_count);
955
956 var carry: Limb = 0;
957 var i: usize = 0;
958 while (i < a.len - limb_shift) : (i += 1) {
959 const src_i = a.len - i - 1;
960 const dst_i = src_i - limb_shift;
961
962 const src_digit = a[src_i];
963 r[dst_i] = carry | (src_digit >> interior_limb_shift);
964 carry = @inlineCall(math.shl, Limb, src_digit, Limb.bit_count - Limb(interior_limb_shift));
965 }
966 }
967
968 // r = a | b
969 pub fn bitOr(r: *Int, av: var, bv: var) !void {
970 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
971 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
972 var a = wrapInt(&stack.allocator, av);
973 var b = wrapInt(&stack.allocator, bv);
974
975 if (a.len > b.len) {
976 try r.ensureCapacity(a.len);
977 llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
978 r.len = a.len;
979 } else {
980 try r.ensureCapacity(b.len);
981 llor(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
982 r.len = b.len;
983 }
984 }
985
986 fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {
987 @setRuntimeSafety(false);
988 debug.assert(r.len >= a.len);
989 debug.assert(a.len >= b.len);
990
991 var i: usize = 0;
992 while (i < b.len) : (i += 1) {
993 r[i] = a[i] | b[i];
994 }
995 while (i < a.len) : (i += 1) {
996 r[i] = a[i];
997 }
998 }
999
1000 // r = a & b
1001 pub fn bitAnd(r: *Int, av: var, bv: var) !void {
1002 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
1003 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
1004 var a = wrapInt(&stack.allocator, av);
1005 var b = wrapInt(&stack.allocator, bv);
1006
1007 if (a.len > b.len) {
1008 try r.ensureCapacity(b.len);
1009 lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
1010 r.normN(b.len);
1011 } else {
1012 try r.ensureCapacity(a.len);
1013 lland(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
1014 r.normN(a.len);
1015 }
1016 }
1017
1018 fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {
1019 @setRuntimeSafety(false);
1020 debug.assert(r.len >= b.len);
1021 debug.assert(a.len >= b.len);
1022
1023 var i: usize = 0;
1024 while (i < b.len) : (i += 1) {
1025 r[i] = a[i] & b[i];
1026 }
1027 }
1028
1029 // r = a ^ b
1030 pub fn bitXor(r: *Int, av: var, bv: var) !void {
1031 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
1032 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
1033 var a = wrapInt(&stack.allocator, av);
1034 var b = wrapInt(&stack.allocator, bv);
1035
1036 if (a.len > b.len) {
1037 try r.ensureCapacity(a.len);
1038 llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
1039 r.normN(a.len);
1040 } else {
1041 try r.ensureCapacity(b.len);
1042 llxor(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
1043 r.normN(b.len);
1044 }
1045 }
1046
1047 fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
1048 @setRuntimeSafety(false);
1049 debug.assert(r.len >= a.len);
1050 debug.assert(a.len >= b.len);
1051
1052 var i: usize = 0;
1053 while (i < b.len) : (i += 1) {
1054 r[i] = a[i] ^ b[i];
1055 }
1056 while (i < a.len) : (i += 1) {
1057 r[i] = a[i];
1058 }
1059 }
1060};
1061
1062// NOTE: All the following tests assume the max machine-word will be 64-bit.
1063//
1064// They will still run on larger than this and should pass, but the multi-limb code-paths
1065// may be untested in some cases.
1066
1067const u256 = @IntType(false, 256);
1068var al = debug.global_allocator;
1069
1070test "big.int comptime_int set" {
1071 comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab;
1072 var a = try Int.initSet(al, s);
1073
1074 const s_limb_count = 128 / Limb.bit_count;
1075
1076 comptime var i: usize = 0;
1077 inline while (i < s_limb_count) : (i += 1) {
1078 const result = Limb(s & @maxValue(Limb));
1079 s >>= Limb.bit_count / 2;
1080 s >>= Limb.bit_count / 2;
1081 debug.assert(a.limbs[i] == result);
1082 }
1083}
1084
1085test "big.int comptime_int set negative" {
1086 var a = try Int.initSet(al, -10);
1087
1088 debug.assert(a.limbs[0] == 10);
1089 debug.assert(a.positive == false);
1090}
1091
1092test "big.int int set unaligned small" {
1093 var a = try Int.initSet(al, u7(45));
1094
1095 debug.assert(a.limbs[0] == 45);
1096 debug.assert(a.positive == true);
1097}
1098
1099test "big.int comptime_int to" {
1100 const a = try Int.initSet(al, 0xefffffff00000001eeeeeeefaaaaaaab);
1101
1102 debug.assert((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab);
1103}
1104
1105test "big.int sub-limb to" {
1106 const a = try Int.initSet(al, 10);
1107
1108 debug.assert((try a.to(u8)) == 10);
1109}
1110
1111test "big.int to target too small error" {
1112 const a = try Int.initSet(al, 0xffffffff);
1113
1114 if (a.to(u8)) |_| {
1115 unreachable;
1116 } else |err| {
1117 debug.assert(err == error.TargetTooSmall);
1118 }
1119}
1120
1121test "big.int norm1" {
1122 var a = try Int.init(al);
1123 try a.ensureCapacity(8);
1124
1125 a.limbs[0] = 1;
1126 a.limbs[1] = 2;
1127 a.limbs[2] = 3;
1128 a.limbs[3] = 0;
1129 a.norm1(4);
1130 debug.assert(a.len == 3);
1131
1132 a.limbs[0] = 1;
1133 a.limbs[1] = 2;
1134 a.limbs[2] = 3;
1135 a.norm1(3);
1136 debug.assert(a.len == 3);
1137
1138 a.limbs[0] = 0;
1139 a.limbs[1] = 0;
1140 a.norm1(2);
1141 debug.assert(a.len == 1);
1142
1143 a.limbs[0] = 0;
1144 a.norm1(1);
1145 debug.assert(a.len == 1);
1146}
1147
1148test "big.int normN" {
1149 var a = try Int.init(al);
1150 try a.ensureCapacity(8);
1151
1152 a.limbs[0] = 1;
1153 a.limbs[1] = 2;
1154 a.limbs[2] = 0;
1155 a.limbs[3] = 0;
1156 a.normN(4);
1157 debug.assert(a.len == 2);
1158
1159 a.limbs[0] = 1;
1160 a.limbs[1] = 2;
1161 a.limbs[2] = 3;
1162 a.normN(3);
1163 debug.assert(a.len == 3);
1164
1165 a.limbs[0] = 0;
1166 a.limbs[1] = 0;
1167 a.limbs[2] = 0;
1168 a.limbs[3] = 0;
1169 a.normN(4);
1170 debug.assert(a.len == 1);
1171
1172 a.limbs[0] = 0;
1173 a.normN(1);
1174 debug.assert(a.len == 1);
1175}
1176
1177test "big.int parity" {
1178 var a = try Int.init(al);
1179 try a.set(0);
1180 debug.assert(a.isEven());
1181 debug.assert(!a.isOdd());
1182
1183 try a.set(7);
1184 debug.assert(!a.isEven());
1185 debug.assert(a.isOdd());
1186}
1187
1188test "big.int bitcount + sizeInBase" {
1189 var a = try Int.init(al);
1190
1191 try a.set(0b100);
1192 debug.assert(a.bitcount() == 3);
1193 debug.assert(a.sizeInBase(2) >= 3);
1194 debug.assert(a.sizeInBase(10) >= 1);
1195
1196 try a.set(0xffffffff);
1197 debug.assert(a.bitcount() == 32);
1198 debug.assert(a.sizeInBase(2) >= 32);
1199 debug.assert(a.sizeInBase(10) >= 10);
1200
1201 try a.shiftLeft(&a, 5000);
1202 debug.assert(a.bitcount() == 5032);
1203 debug.assert(a.sizeInBase(2) >= 5032);
1204 a.positive = false;
1205
1206 debug.assert(a.bitcount() == 5033);
1207 debug.assert(a.sizeInBase(2) >= 5033);
1208}
1209
1210test "big.int string set" {
1211 var a = try Int.init(al);
1212 try a.setString(10, "120317241209124781241290847124");
1213
1214 debug.assert((try a.to(u128)) == 120317241209124781241290847124);
1215}
1216
1217test "big.int string negative" {
1218 var a = try Int.init(al);
1219 try a.setString(10, "-1023");
1220 debug.assert((try a.to(i32)) == -1023);
1221}
1222
1223test "big.int string set bad char error" {
1224 var a = try Int.init(al);
1225 a.setString(10, "x") catch |err| debug.assert(err == error.InvalidCharForDigit);
1226}
1227
1228test "big.int string set bad base error" {
1229 var a = try Int.init(al);
1230 a.setString(45, "10") catch |err| debug.assert(err == error.InvalidBase);
1231}
1232
1233test "big.int string to" {
1234 const a = try Int.initSet(al, 120317241209124781241290847124);
1235
1236 const as = try a.toString(al, 10);
1237 const es = "120317241209124781241290847124";
1238
1239 debug.assert(mem.eql(u8, as, es));
1240}
1241
1242test "big.int string to base base error" {
1243 const a = try Int.initSet(al, 0xffffffff);
1244
1245 if (a.toString(al, 45)) |_| {
1246 unreachable;
1247 } else |err| {
1248 debug.assert(err == error.InvalidBase);
1249 }
1250}
1251
1252test "big.int string to base 2" {
1253 const a = try Int.initSet(al, -0b1011);
1254
1255 const as = try a.toString(al, 2);
1256 const es = "-1011";
1257
1258 debug.assert(mem.eql(u8, as, es));
1259}
1260
1261test "big.int string to base 16" {
1262 const a = try Int.initSet(al, 0xefffffff00000001eeeeeeefaaaaaaab);
1263
1264 const as = try a.toString(al, 16);
1265 const es = "efffffff00000001eeeeeeefaaaaaaab";
1266
1267 debug.assert(mem.eql(u8, as, es));
1268}
1269
1270test "big.int neg string to" {
1271 const a = try Int.initSet(al, -123907434);
1272
1273 const as = try a.toString(al, 10);
1274 const es = "-123907434";
1275
1276 debug.assert(mem.eql(u8, as, es));
1277}
1278
1279test "big.int zero string to" {
1280 const a = try Int.initSet(al, 0);
1281
1282 const as = try a.toString(al, 10);
1283 const es = "0";
1284
1285 debug.assert(mem.eql(u8, as, es));
1286}
1287
1288test "big.int clone" {
1289 var a = try Int.initSet(al, 1234);
1290 const b = try a.clone();
1291
1292 debug.assert((try a.to(u32)) == 1234);
1293 debug.assert((try b.to(u32)) == 1234);
1294
1295 try a.set(77);
1296 debug.assert((try a.to(u32)) == 77);
1297 debug.assert((try b.to(u32)) == 1234);
1298}
1299
1300test "big.int swap" {
1301 var a = try Int.initSet(al, 1234);
1302 var b = try Int.initSet(al, 5678);
1303
1304 debug.assert((try a.to(u32)) == 1234);
1305 debug.assert((try b.to(u32)) == 5678);
1306
1307 a.swap(&b);
1308
1309 debug.assert((try a.to(u32)) == 5678);
1310 debug.assert((try b.to(u32)) == 1234);
1311}
1312
1313test "big.int to negative" {
1314 var a = try Int.initSet(al, -10);
1315
1316 debug.assert((try a.to(i32)) == -10);
1317}
1318
1319test "big.int compare" {
1320 var a = try Int.initSet(al, -11);
1321 var b = try Int.initSet(al, 10);
1322
1323 debug.assert(a.cmpAbs(&b) == 1);
1324 debug.assert(a.cmp(&b) == -1);
1325}
1326
1327test "big.int compare similar" {
1328 var a = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeee);
1329 var b = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeef);
1330
1331 debug.assert(a.cmpAbs(&b) == -1);
1332 debug.assert(b.cmpAbs(&a) == 1);
1333}
1334
1335test "big.int compare different limb size" {
1336 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1337 var b = try Int.initSet(al, 1);
1338
1339 debug.assert(a.cmpAbs(&b) == 1);
1340 debug.assert(b.cmpAbs(&a) == -1);
1341}
1342
1343test "big.int compare multi-limb" {
1344 var a = try Int.initSet(al, -0x7777777799999999ffffeeeeffffeeeeffffeeeef);
1345 var b = try Int.initSet(al, 0x7777777799999999ffffeeeeffffeeeeffffeeeee);
1346
1347 debug.assert(a.cmpAbs(&b) == 1);
1348 debug.assert(a.cmp(&b) == -1);
1349}
1350
1351test "big.int equality" {
1352 var a = try Int.initSet(al, 0xffffffff1);
1353 var b = try Int.initSet(al, -0xffffffff1);
1354
1355 debug.assert(a.eqAbs(&b));
1356 debug.assert(!a.eq(&b));
1357}
1358
1359test "big.int abs" {
1360 var a = try Int.initSet(al, -5);
1361
1362 a.abs();
1363 debug.assert((try a.to(u32)) == 5);
1364
1365 a.abs();
1366 debug.assert((try a.to(u32)) == 5);
1367}
1368
1369test "big.int negate" {
1370 var a = try Int.initSet(al, 5);
1371
1372 a.negate();
1373 debug.assert((try a.to(i32)) == -5);
1374
1375 a.negate();
1376 debug.assert((try a.to(i32)) == 5);
1377}
1378
1379test "big.int add single-single" {
1380 var a = try Int.initSet(al, 50);
1381 var b = try Int.initSet(al, 5);
1382
1383 var c = try Int.init(al);
1384 try c.add(&a, &b);
1385
1386 debug.assert((try c.to(u32)) == 55);
1387}
1388
1389test "big.int add multi-single" {
1390 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1391 var b = try Int.initSet(al, 1);
1392
1393 var c = try Int.init(al);
1394
1395 try c.add(&a, &b);
1396 debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2);
1397
1398 try c.add(&b, &a);
1399 debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2);
1400}
1401
1402test "big.int add multi-multi" {
1403 const op1 = 0xefefefef7f7f7f7f;
1404 const op2 = 0xfefefefe9f9f9f9f;
1405 var a = try Int.initSet(al, op1);
1406 var b = try Int.initSet(al, op2);
1407
1408 var c = try Int.init(al);
1409 try c.add(&a, &b);
1410
1411 debug.assert((try c.to(u128)) == op1 + op2);
1412}
1413
1414test "big.int add zero-zero" {
1415 var a = try Int.initSet(al, 0);
1416 var b = try Int.initSet(al, 0);
1417
1418 var c = try Int.init(al);
1419 try c.add(&a, &b);
1420
1421 debug.assert((try c.to(u32)) == 0);
1422}
1423
1424test "big.int add alias multi-limb nonzero-zero" {
1425 const op1 = 0xffffffff777777771;
1426 var a = try Int.initSet(al, op1);
1427 var b = try Int.initSet(al, 0);
1428
1429 try a.add(&a, &b);
1430
1431 debug.assert((try a.to(u128)) == op1);
1432}
1433
1434test "big.int add sign" {
1435 var a = try Int.init(al);
1436
1437 try a.add(1, 2);
1438 debug.assert((try a.to(i32)) == 3);
1439
1440 try a.add(-1, 2);
1441 debug.assert((try a.to(i32)) == 1);
1442
1443 try a.add(1, -2);
1444 debug.assert((try a.to(i32)) == -1);
1445
1446 try a.add(-1, -2);
1447 debug.assert((try a.to(i32)) == -3);
1448}
1449
1450test "big.int sub single-single" {
1451 var a = try Int.initSet(al, 50);
1452 var b = try Int.initSet(al, 5);
1453
1454 var c = try Int.init(al);
1455 try c.sub(&a, &b);
1456
1457 debug.assert((try c.to(u32)) == 45);
1458}
1459
1460test "big.int sub multi-single" {
1461 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1462 var b = try Int.initSet(al, 1);
1463
1464 var c = try Int.init(al);
1465 try c.sub(&a, &b);
1466
1467 debug.assert((try c.to(Limb)) == @maxValue(Limb));
1468}
1469
1470test "big.int sub multi-multi" {
1471 const op1 = 0xefefefefefefefefefefefef;
1472 const op2 = 0xabababababababababababab;
1473
1474 var a = try Int.initSet(al, op1);
1475 var b = try Int.initSet(al, op2);
1476
1477 var c = try Int.init(al);
1478 try c.sub(&a, &b);
1479
1480 debug.assert((try c.to(u128)) == op1 - op2);
1481}
1482
1483test "big.int sub equal" {
1484 var a = try Int.initSet(al, 0x11efefefefefefefefefefefef);
1485 var b = try Int.initSet(al, 0x11efefefefefefefefefefefef);
1486
1487 var c = try Int.init(al);
1488 try c.sub(&a, &b);
1489
1490 debug.assert((try c.to(u32)) == 0);
1491}
1492
1493test "big.int sub sign" {
1494 var a = try Int.init(al);
1495
1496 try a.sub(1, 2);
1497 debug.assert((try a.to(i32)) == -1);
1498
1499 try a.sub(-1, 2);
1500 debug.assert((try a.to(i32)) == -3);
1501
1502 try a.sub(1, -2);
1503 debug.assert((try a.to(i32)) == 3);
1504
1505 try a.sub(-1, -2);
1506 debug.assert((try a.to(i32)) == 1);
1507
1508 try a.sub(-2, -1);
1509 debug.assert((try a.to(i32)) == -1);
1510}
1511
1512test "big.int mul single-single" {
1513 var a = try Int.initSet(al, 50);
1514 var b = try Int.initSet(al, 5);
1515
1516 var c = try Int.init(al);
1517 try c.mul(&a, &b);
1518
1519 debug.assert((try c.to(u64)) == 250);
1520}
1521
1522test "big.int mul multi-single" {
1523 var a = try Int.initSet(al, @maxValue(Limb));
1524 var b = try Int.initSet(al, 2);
1525
1526 var c = try Int.init(al);
1527 try c.mul(&a, &b);
1528
1529 debug.assert((try c.to(DoubleLimb)) == 2 * @maxValue(Limb));
1530}
1531
1532test "big.int mul multi-multi" {
1533 const op1 = 0x998888efefefefefefefef;
1534 const op2 = 0x333000abababababababab;
1535 var a = try Int.initSet(al, op1);
1536 var b = try Int.initSet(al, op2);
1537
1538 var c = try Int.init(al);
1539 try c.mul(&a, &b);
1540
1541 debug.assert((try c.to(u256)) == op1 * op2);
1542}
1543
1544test "big.int mul alias r with a" {
1545 var a = try Int.initSet(al, @maxValue(Limb));
1546 var b = try Int.initSet(al, 2);
1547
1548 try a.mul(&a, &b);
1549
1550 debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb));
1551}
1552
1553test "big.int mul alias r with b" {
1554 var a = try Int.initSet(al, @maxValue(Limb));
1555 var b = try Int.initSet(al, 2);
1556
1557 try a.mul(&b, &a);
1558
1559 debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb));
1560}
1561
1562test "big.int mul alias r with a and b" {
1563 var a = try Int.initSet(al, @maxValue(Limb));
1564
1565 try a.mul(&a, &a);
1566
1567 debug.assert((try a.to(DoubleLimb)) == @maxValue(Limb) * @maxValue(Limb));
1568}
1569
1570test "big.int mul a*0" {
1571 var a = try Int.initSet(al, 0xefefefefefefefef);
1572 var b = try Int.initSet(al, 0);
1573
1574 var c = try Int.init(al);
1575 try c.mul(&a, &b);
1576
1577 debug.assert((try c.to(u32)) == 0);
1578}
1579
1580test "big.int mul 0*0" {
1581 var a = try Int.initSet(al, 0);
1582 var b = try Int.initSet(al, 0);
1583
1584 var c = try Int.init(al);
1585 try c.mul(&a, &b);
1586
1587 debug.assert((try c.to(u32)) == 0);
1588}
1589
1590test "big.int div single-single no rem" {
1591 var a = try Int.initSet(al, 50);
1592 var b = try Int.initSet(al, 5);
1593
1594 var q = try Int.init(al);
1595 var r = try Int.init(al);
1596 try Int.divTrunc(&q, &r, &a, &b);
1597
1598 debug.assert((try q.to(u32)) == 10);
1599 debug.assert((try r.to(u32)) == 0);
1600}
1601
1602test "big.int div single-single with rem" {
1603 var a = try Int.initSet(al, 49);
1604 var b = try Int.initSet(al, 5);
1605
1606 var q = try Int.init(al);
1607 var r = try Int.init(al);
1608 try Int.divTrunc(&q, &r, &a, &b);
1609
1610 debug.assert((try q.to(u32)) == 9);
1611 debug.assert((try r.to(u32)) == 4);
1612}
1613
1614test "big.int div multi-single no rem" {
1615 const op1 = 0xffffeeeeddddcccc;
1616 const op2 = 34;
1617
1618 var a = try Int.initSet(al, op1);
1619 var b = try Int.initSet(al, op2);
1620
1621 var q = try Int.init(al);
1622 var r = try Int.init(al);
1623 try Int.divTrunc(&q, &r, &a, &b);
1624
1625 debug.assert((try q.to(u64)) == op1 / op2);
1626 debug.assert((try r.to(u64)) == 0);
1627}
1628
1629test "big.int div multi-single with rem" {
1630 const op1 = 0xffffeeeeddddcccf;
1631 const op2 = 34;
1632
1633 var a = try Int.initSet(al, op1);
1634 var b = try Int.initSet(al, op2);
1635
1636 var q = try Int.init(al);
1637 var r = try Int.init(al);
1638 try Int.divTrunc(&q, &r, &a, &b);
1639
1640 debug.assert((try q.to(u64)) == op1 / op2);
1641 debug.assert((try r.to(u64)) == 3);
1642}
1643
1644test "big.int div multi>2-single" {
1645 const op1 = 0xfefefefefefefefefefefefefefefefe;
1646 const op2 = 0xefab8;
1647
1648 var a = try Int.initSet(al, op1);
1649 var b = try Int.initSet(al, op2);
1650
1651 var q = try Int.init(al);
1652 var r = try Int.init(al);
1653 try Int.divTrunc(&q, &r, &a, &b);
1654
1655 debug.assert((try q.to(u128)) == op1 / op2);
1656 debug.assert((try r.to(u32)) == 0x3e4e);
1657}
1658
1659test "big.int div single-single q < r" {
1660 var a = try Int.initSet(al, 0x0078f432);
1661 var b = try Int.initSet(al, 0x01000000);
1662
1663 var q = try Int.init(al);
1664 var r = try Int.init(al);
1665 try Int.divTrunc(&q, &r, &a, &b);
1666
1667 debug.assert((try q.to(u64)) == 0);
1668 debug.assert((try r.to(u64)) == 0x0078f432);
1669}
1670
1671test "big.int div single-single q == r" {
1672 var a = try Int.initSet(al, 10);
1673 var b = try Int.initSet(al, 10);
1674
1675 var q = try Int.init(al);
1676 var r = try Int.init(al);
1677 try Int.divTrunc(&q, &r, &a, &b);
1678
1679 debug.assert((try q.to(u64)) == 1);
1680 debug.assert((try r.to(u64)) == 0);
1681}
1682
1683test "big.int div q=0 alias" {
1684 var a = try Int.initSet(al, 3);
1685 var b = try Int.initSet(al, 10);
1686
1687 try Int.divTrunc(&a, &b, &a, &b);
1688
1689 debug.assert((try a.to(u64)) == 0);
1690 debug.assert((try b.to(u64)) == 3);
1691}
1692
1693test "big.int div multi-multi q < r" {
1694 const op1 = 0x1ffffffff0078f432;
1695 const op2 = 0x1ffffffff01000000;
1696 var a = try Int.initSet(al, op1);
1697 var b = try Int.initSet(al, op2);
1698
1699 var q = try Int.init(al);
1700 var r = try Int.init(al);
1701 try Int.divTrunc(&q, &r, &a, &b);
1702
1703 debug.assert((try q.to(u128)) == 0);
1704 debug.assert((try r.to(u128)) == op1);
1705}
1706
1707test "big.int div trunc single-single +/+" {
1708 const u: i32 = 5;
1709 const v: i32 = 3;
1710
1711 var a = try Int.initSet(al, u);
1712 var b = try Int.initSet(al, v);
1713
1714 var q = try Int.init(al);
1715 var r = try Int.init(al);
1716 try Int.divTrunc(&q, &r, &a, &b);
1717
1718 // n = q * d + r
1719 // 5 = 1 * 3 + 2
1720 const eq = @divTrunc(u, v);
1721 const er = @mod(u, v);
1722
1723 debug.assert((try q.to(i32)) == eq);
1724 debug.assert((try r.to(i32)) == er);
1725}
1726
1727test "big.int div trunc single-single -/+" {
1728 const u: i32 = -5;
1729 const v: i32 = 3;
1730
1731 var a = try Int.initSet(al, u);
1732 var b = try Int.initSet(al, v);
1733
1734 var q = try Int.init(al);
1735 var r = try Int.init(al);
1736 try Int.divTrunc(&q, &r, &a, &b);
1737
1738 // n = q * d + r
1739 // -5 = 1 * -3 - 2
1740 const eq = -1;
1741 const er = -2;
1742
1743 debug.assert((try q.to(i32)) == eq);
1744 debug.assert((try r.to(i32)) == er);
1745}
1746
1747test "big.int div trunc single-single +/-" {
1748 const u: i32 = 5;
1749 const v: i32 = -3;
1750
1751 var a = try Int.initSet(al, u);
1752 var b = try Int.initSet(al, v);
1753
1754 var q = try Int.init(al);
1755 var r = try Int.init(al);
1756 try Int.divTrunc(&q, &r, &a, &b);
1757
1758 // n = q * d + r
1759 // 5 = -1 * -3 + 2
1760 const eq = -1;
1761 const er = 2;
1762
1763 debug.assert((try q.to(i32)) == eq);
1764 debug.assert((try r.to(i32)) == er);
1765}
1766
1767test "big.int div trunc single-single -/-" {
1768 const u: i32 = -5;
1769 const v: i32 = -3;
1770
1771 var a = try Int.initSet(al, u);
1772 var b = try Int.initSet(al, v);
1773
1774 var q = try Int.init(al);
1775 var r = try Int.init(al);
1776 try Int.divTrunc(&q, &r, &a, &b);
1777
1778 // n = q * d + r
1779 // -5 = 1 * -3 - 2
1780 const eq = 1;
1781 const er = -2;
1782
1783 debug.assert((try q.to(i32)) == eq);
1784 debug.assert((try r.to(i32)) == er);
1785}
1786
1787test "big.int div floor single-single +/+" {
1788 const u: i32 = 5;
1789 const v: i32 = 3;
1790
1791 var a = try Int.initSet(al, u);
1792 var b = try Int.initSet(al, v);
1793
1794 var q = try Int.init(al);
1795 var r = try Int.init(al);
1796 try Int.divFloor(&q, &r, &a, &b);
1797
1798 // n = q * d + r
1799 // 5 = 1 * 3 + 2
1800 const eq = 1;
1801 const er = 2;
1802
1803 debug.assert((try q.to(i32)) == eq);
1804 debug.assert((try r.to(i32)) == er);
1805}
1806
1807test "big.int div floor single-single -/+" {
1808 const u: i32 = -5;
1809 const v: i32 = 3;
1810
1811 var a = try Int.initSet(al, u);
1812 var b = try Int.initSet(al, v);
1813
1814 var q = try Int.init(al);
1815 var r = try Int.init(al);
1816 try Int.divFloor(&q, &r, &a, &b);
1817
1818 // n = q * d + r
1819 // -5 = -2 * 3 + 1
1820 const eq = -2;
1821 const er = 1;
1822
1823 debug.assert((try q.to(i32)) == eq);
1824 debug.assert((try r.to(i32)) == er);
1825}
1826
1827test "big.int div floor single-single +/-" {
1828 const u: i32 = 5;
1829 const v: i32 = -3;
1830
1831 var a = try Int.initSet(al, u);
1832 var b = try Int.initSet(al, v);
1833
1834 var q = try Int.init(al);
1835 var r = try Int.init(al);
1836 try Int.divFloor(&q, &r, &a, &b);
1837
1838 // n = q * d + r
1839 // 5 = -2 * -3 - 1
1840 const eq = -2;
1841 const er = -1;
1842
1843 debug.assert((try q.to(i32)) == eq);
1844 debug.assert((try r.to(i32)) == er);
1845}
1846
1847test "big.int div floor single-single -/-" {
1848 const u: i32 = -5;
1849 const v: i32 = -3;
1850
1851 var a = try Int.initSet(al, u);
1852 var b = try Int.initSet(al, v);
1853
1854 var q = try Int.init(al);
1855 var r = try Int.init(al);
1856 try Int.divFloor(&q, &r, &a, &b);
1857
1858 // n = q * d + r
1859 // -5 = 2 * -3 + 1
1860 const eq = 1;
1861 const er = -2;
1862
1863 debug.assert((try q.to(i32)) == eq);
1864 debug.assert((try r.to(i32)) == er);
1865}
1866
1867test "big.int div multi-multi with rem" {
1868 var a = try Int.initSet(al, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);
1869 var b = try Int.initSet(al, 0x99990000111122223333);
1870
1871 var q = try Int.init(al);
1872 var r = try Int.init(al);
1873 try Int.divTrunc(&q, &r, &a, &b);
1874
1875 debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1876 debug.assert((try r.to(u128)) == 0x28de0acacd806823638);
1877}
1878
1879test "big.int div multi-multi no rem" {
1880 var a = try Int.initSet(al, 0x8888999911110000ffffeeeedb4fec200ee3a4286361);
1881 var b = try Int.initSet(al, 0x99990000111122223333);
1882
1883 var q = try Int.init(al);
1884 var r = try Int.init(al);
1885 try Int.divTrunc(&q, &r, &a, &b);
1886
1887 debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1888 debug.assert((try r.to(u128)) == 0);
1889}
1890
1891test "big.int div multi-multi (2 branch)" {
1892 var a = try Int.initSet(al, 0x866666665555555588888887777777761111111111111111);
1893 var b = try Int.initSet(al, 0x86666666555555554444444433333333);
1894
1895 var q = try Int.init(al);
1896 var r = try Int.init(al);
1897 try Int.divTrunc(&q, &r, &a, &b);
1898
1899 debug.assert((try q.to(u128)) == 0x10000000000000000);
1900 debug.assert((try r.to(u128)) == 0x44444443444444431111111111111111);
1901}
1902
1903test "big.int div multi-multi (3.1/3.3 branch)" {
1904 var a = try Int.initSet(al, 0x11111111111111111111111111111111111111111111111111111111111111);
1905 var b = try Int.initSet(al, 0x1111111111111111111111111111111111111111171);
1906
1907 var q = try Int.init(al);
1908 var r = try Int.init(al);
1909 try Int.divTrunc(&q, &r, &a, &b);
1910
1911 debug.assert((try q.to(u128)) == 0xfffffffffffffffffff);
1912 debug.assert((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);
1913}
1914
1915test "big.int shift-right single" {
1916 var a = try Int.initSet(al, 0xffff0000);
1917 try a.shiftRight(a, 16);
1918
1919 debug.assert((try a.to(u32)) == 0xffff);
1920}
1921
1922test "big.int shift-right multi" {
1923 var a = try Int.initSet(al, 0xffff0000eeee1111dddd2222cccc3333);
1924 try a.shiftRight(a, 67);
1925
1926 debug.assert((try a.to(u64)) == 0x1fffe0001dddc222);
1927}
1928
1929test "big.int shift-left single" {
1930 var a = try Int.initSet(al, 0xffff);
1931 try a.shiftLeft(a, 16);
1932
1933 debug.assert((try a.to(u64)) == 0xffff0000);
1934}
1935
1936test "big.int shift-left multi" {
1937 var a = try Int.initSet(al, 0x1fffe0001dddc222);
1938 try a.shiftLeft(a, 67);
1939
1940 debug.assert((try a.to(u128)) == 0xffff0000eeee11100000000000000000);
1941}
1942
1943test "big.int shift-right negative" {
1944 var a = try Int.init(al);
1945
1946 try a.shiftRight(-20, 2);
1947 debug.assert((try a.to(i32)) == -20 >> 2);
1948
1949 try a.shiftRight(-5, 10);
1950 debug.assert((try a.to(i32)) == -5 >> 10);
1951}
1952
1953test "big.int shift-left negative" {
1954 var a = try Int.init(al);
1955
1956 try a.shiftRight(-10, 1232);
1957 debug.assert((try a.to(i32)) == -10 >> 1232);
1958}
1959
1960test "big.int bitwise and simple" {
1961 var a = try Int.initSet(al, 0xffffffff11111111);
1962 var b = try Int.initSet(al, 0xeeeeeeee22222222);
1963
1964 try a.bitAnd(&a, &b);
1965
1966 debug.assert((try a.to(u64)) == 0xeeeeeeee00000000);
1967}
1968
1969test "big.int bitwise and multi-limb" {
1970 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1971 var b = try Int.initSet(al, @maxValue(Limb));
1972
1973 try a.bitAnd(&a, &b);
1974
1975 debug.assert((try a.to(u128)) == 0);
1976}
1977
1978test "big.int bitwise xor simple" {
1979 var a = try Int.initSet(al, 0xffffffff11111111);
1980 var b = try Int.initSet(al, 0xeeeeeeee22222222);
1981
1982 try a.bitXor(&a, &b);
1983
1984 debug.assert((try a.to(u64)) == 0x1111111133333333);
1985}
1986
1987test "big.int bitwise xor multi-limb" {
1988 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1989 var b = try Int.initSet(al, @maxValue(Limb));
1990
1991 try a.bitXor(&a, &b);
1992
1993 debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) ^ @maxValue(Limb));
1994}
1995
1996test "big.int bitwise or simple" {
1997 var a = try Int.initSet(al, 0xffffffff11111111);
1998 var b = try Int.initSet(al, 0xeeeeeeee22222222);
1999
2000 try a.bitOr(&a, &b);
2001
2002 debug.assert((try a.to(u64)) == 0xffffffff33333333);
2003}
2004
2005test "big.int bitwise or multi-limb" {
2006 var a = try Int.initSet(al, @maxValue(Limb) + 1);
2007 var b = try Int.initSet(al, @maxValue(Limb));
2008
2009 try a.bitOr(&a, &b);
2010
2011 // TODO: big.int.cpp or is wrong on multi-limb.
2012 debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) + @maxValue(Limb));
2013}
2014
2015test "big.int var args" {
2016 var a = try Int.initSet(al, 5);
2017
2018 try a.add(&a, 6);
2019 debug.assert((try a.to(u64)) == 11);
2020
2021 debug.assert(a.cmp(11) == 0);
2022 debug.assert(a.cmp(14) <= 0);
2023}
std/math/index.zig+4
...@@ -132,6 +132,8 @@ pub const tan = @import("tan.zig").tan;...@@ -132,6 +132,8 @@ pub const tan = @import("tan.zig").tan;
132pub const complex = @import("complex/index.zig");132pub const complex = @import("complex/index.zig");
133pub const Complex = complex.Complex;133pub const Complex = complex.Complex;
134134
135pub const big = @import("big/index.zig");
136
135test "math" {137test "math" {
136 _ = @import("nan.zig");138 _ = @import("nan.zig");
137 _ = @import("isnan.zig");139 _ = @import("isnan.zig");
...@@ -177,6 +179,8 @@ test "math" {...@@ -177,6 +179,8 @@ test "math" {
177 _ = @import("tan.zig");179 _ = @import("tan.zig");
178180
179 _ = @import("complex/index.zig");181 _ = @import("complex/index.zig");
182
183 _ = @import("big/index.zig");
180}184}
181185
182pub fn min(x: var, y: var) @typeOf(x + y) {186pub fn min(x: var, y: var) @typeOf(x + y) {