authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-04 14:29:09-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-10-04 14:29:09-04:00
loga28f2e0dd2d7b78464115bca4968f7c0befa6b28
tree1953c90e0c488588495abce1e76d35e3d98557b8
parent2454459ef5435081abe82724e873a74bd33a79af
parent95fe86e3dbd3826ca0971853b275a409ac215c83
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9885 from Snektron/big-int-wrapping

Big int wrapping/saturating

6 files changed, 1402 insertions(+), 222 deletions(-)

lib/std/math/big.zig+2
......@@ -5,6 +5,7 @@ pub const Rational = @import("big/rational.zig").Rational;
55pub const int = @import("big/int.zig");
66pub const Limb = usize;
77const limb_info = @typeInfo(Limb).Int;
8pub const SignedLimb = std.meta.Int(.signed, limb_info.bits);
89pub const DoubleLimb = std.meta.Int(.unsigned, 2 * limb_info.bits);
910pub const SignedDoubleLimb = std.meta.Int(.signed, 2 * limb_info.bits);
1011pub const Log2Limb = std.math.Log2Int(Limb);
......@@ -19,6 +20,7 @@ test {
1920 _ = int;
2021 _ = Rational;
2122 _ = Limb;
23 _ = SignedLimb;
2224 _ = DoubleLimb;
2325 _ = SignedDoubleLimb;
2426 _ = Log2Limb;
lib/std/math/big/int.zig+832-144
......@@ -44,6 +44,11 @@ pub fn calcMulLimbsBufferLen(a_len: usize, b_len: usize, aliases: usize) usize {
4444 return aliases * math.max(a_len, b_len);
4545}
4646
47pub fn calcMulWrapLimbsBufferLen(bit_count: usize, a_len: usize, b_len: usize, aliases: usize) usize {
48 const req_limbs = calcTwosCompLimbCount(bit_count);
49 return aliases * math.min(req_limbs, math.max(a_len, b_len));
50}
51
4752pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {
4853 const limb_count = calcSetStringLimbCount(base, string_len);
4954 return calcMulLimbsBufferLen(limb_count, limb_count, 2);
......@@ -58,6 +63,11 @@ pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize {
5863 return 2 + (a_bit_count * y + (limb_bits - 1)) / limb_bits;
5964}
6065
66// Compute the number of limbs required to store a 2s-complement number of `bit_count` bits.
67pub fn calcTwosCompLimbCount(bit_count: usize) usize {
68 return std.math.divCeil(usize, bit_count, @bitSizeOf(Limb)) catch unreachable;
69}
70
6171/// a + b * c + *carry, sets carry to the overflow bits
6272pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
6373 @setRuntimeSafety(debug_safety);
......@@ -81,6 +91,33 @@ pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
8191 return r1;
8292}
8393
94/// a - b * c - *carry, sets carry to the overflow bits
95fn subMulLimbWithBorrow(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
96 // r1 = a - *carry
97 var r1: Limb = undefined;
98 const c1: Limb = @boolToInt(@subWithOverflow(Limb, a, carry.*, &r1));
99
100 // r2 = b * c
101 const bc = @as(DoubleLimb, std.math.mulWide(Limb, b, c));
102 const r2 = @truncate(Limb, bc);
103 const c2 = @truncate(Limb, bc >> limb_bits);
104
105 // r1 = r1 - r2
106 const c3: Limb = @boolToInt(@subWithOverflow(Limb, r1, r2, &r1));
107 carry.* = c1 + c2 + c3;
108
109 return r1;
110}
111
112/// Used to indicate either limit of a 2s-complement integer.
113pub const TwosCompIntLimit = enum {
114 // The low limit, either 0x00 (unsigned) or (-)0x80 (signed) for an 8-bit integer.
115 min,
116
117 // The high limit, either 0xFF (unsigned) or 0x7F (signed) for an 8-bit integer.
118 max,
119};
120
84121/// A arbitrary-precision big integer, with a fixed set of mutable limbs.
85122pub const Mutable = struct {
86123 /// Raw digits. These are:
......@@ -282,6 +319,75 @@ pub const Mutable = struct {
282319 self.positive = positive;
283320 }
284321
322 /// Set self to either bound of a 2s-complement integer.
323 /// Note: The result is still sign-magnitude, not twos complement! In order to convert the
324 /// result to twos complement, it is sufficient to take the absolute value.
325 ///
326 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
327 /// r is `calcTwosCompLimbCount(bit_count)`.
328 pub fn setTwosCompIntLimit(
329 r: *Mutable,
330 limit: TwosCompIntLimit,
331 signedness: std.builtin.Signedness,
332 bit_count: usize,
333 ) void {
334 // Handle zero-bit types.
335 if (bit_count == 0) {
336 r.set(0);
337 return;
338 }
339
340 const req_limbs = calcTwosCompLimbCount(bit_count);
341 const bit = @truncate(Log2Limb, bit_count - 1);
342 const signmask = @as(Limb, 1) << bit; // 0b0..010..0 where 1 is the sign bit.
343 const mask = (signmask << 1) -% 1; // 0b0..011..1 where the leftmost 1 is the sign bit.
344
345 r.positive = true;
346
347 switch (signedness) {
348 .signed => switch (limit) {
349 .min => {
350 // Negative bound, signed = -0x80.
351 r.len = req_limbs;
352 mem.set(Limb, r.limbs[0 .. r.len - 1], 0);
353 r.limbs[r.len - 1] = signmask;
354 r.positive = false;
355 },
356 .max => {
357 // Positive bound, signed = 0x7F
358 // Note, in this branch we need to normalize because the first bit is
359 // supposed to be 0.
360
361 // Special case for 1-bit integers.
362 if (bit_count == 1) {
363 r.set(0);
364 } else {
365 const new_req_limbs = calcTwosCompLimbCount(bit_count - 1);
366 const msb = @truncate(Log2Limb, bit_count - 2);
367 const new_signmask = @as(Limb, 1) << msb; // 0b0..010..0 where 1 is the sign bit.
368 const new_mask = (new_signmask << 1) -% 1; // 0b0..001..1 where the rightmost 0 is the sign bit.
369
370 r.len = new_req_limbs;
371 std.mem.set(Limb, r.limbs[0 .. r.len - 1], maxInt(Limb));
372 r.limbs[r.len - 1] = new_mask;
373 }
374 },
375 },
376 .unsigned => switch (limit) {
377 .min => {
378 // Min bound, unsigned = 0x00
379 r.set(0);
380 },
381 .max => {
382 // Max bound, unsigned = 0xFF
383 r.len = req_limbs;
384 std.mem.set(Limb, r.limbs[0 .. r.len - 1], maxInt(Limb));
385 r.limbs[r.len - 1] = mask;
386 },
387 },
388 }
389 }
390
285391 /// r = a + scalar
286392 ///
287393 /// r and a may be aliases.
......@@ -295,102 +401,220 @@ pub const Mutable = struct {
295401 return add(r, a, operand);
296402 }
297403
298 /// r = a + b
299 ///
404 /// Base implementation for addition. Adds `max(a.limbs.len, b.limbs.len)` elements from a and b,
405 /// and returns whether any overflow occured.
300406 /// r, a and b may be aliases.
301407 ///
302 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
303 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`.
304 pub fn add(r: *Mutable, a: Const, b: Const) void {
408 /// Asserts r has enough elements to hold the result. The upper bound is `max(a.limbs.len, b.limbs.len)`.
409 fn addCarry(r: *Mutable, a: Const, b: Const) bool {
305410 if (a.eqZero()) {
306411 r.copy(b);
307 return;
412 return false;
308413 } else if (b.eqZero()) {
309414 r.copy(a);
310 return;
311 }
312
313 if (a.limbs.len == 1 and b.limbs.len == 1 and a.positive == b.positive) {
314 var o: Limb = undefined;
315 if (!@addWithOverflow(Limb, a.limbs[0], b.limbs[0], &o)) {
316 r.limbs[0] = o;
317 r.len = 1;
318 r.positive = a.positive;
319 return;
320 }
321 }
322
323 if (a.positive != b.positive) {
415 return false;
416 } else if (a.positive != b.positive) {
324417 if (a.positive) {
325418 // (a) + (-b) => a - b
326 r.sub(a, b.abs());
419 return r.subCarry(a, b.abs());
327420 } else {
328421 // (-a) + (b) => b - a
329 r.sub(b, a.abs());
422 return r.subCarry(b, a.abs());
330423 }
331424 } else {
425 r.positive = a.positive;
332426 if (a.limbs.len >= b.limbs.len) {
333 lladd(r.limbs[0..], a.limbs, b.limbs);
334 r.normalize(a.limbs.len + 1);
427 const c = lladdcarry(r.limbs, a.limbs, b.limbs);
428 r.normalize(a.limbs.len);
429 return c != 0;
335430 } else {
336 lladd(r.limbs[0..], b.limbs, a.limbs);
337 r.normalize(b.limbs.len + 1);
431 const c = lladdcarry(r.limbs, b.limbs, a.limbs);
432 r.normalize(b.limbs.len);
433 return c != 0;
338434 }
435 }
436 }
339437
340 r.positive = a.positive;
438 /// r = a + b
439 /// r, a and b may be aliases.
440 ///
441 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
442 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`.
443 pub fn add(r: *Mutable, a: Const, b: Const) void {
444 if (r.addCarry(a, b)) {
445 // Fix up the result. Note that addCarry normalizes by a.limbs.len or b.limbs.len,
446 // so we need to set the length here.
447 const msl = math.max(a.limbs.len, b.limbs.len);
448 // `[add|sub]Carry` normalizes by `msl`, so we need to fix up the result manually here.
449 // Note, the fact that it normalized means that the intermediary limbs are zero here.
450 r.len = msl + 1;
451 r.limbs[msl] = 1; // If this panics, there wasn't enough space in `r`.
341452 }
342453 }
343454
344 /// r = a - b
455 /// r = a + b with 2s-complement wrapping semantics.
456 /// r, a and b may be aliases
345457 ///
458 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
459 /// r is `calcTwosCompLimbCount(bit_count)`.
460 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
461 const req_limbs = calcTwosCompLimbCount(bit_count);
462
463 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
464 // if an overflow occured.
465 const x = Const{
466 .positive = a.positive,
467 .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)],
468 };
469
470 const y = Const{
471 .positive = b.positive,
472 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
473 };
474
475 if (r.addCarry(x, y)) {
476 // There are two possibilities here:
477 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
478 // truncate anyway.
479 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
480 // Note: after this we still might need to wrap.
481 const msl = math.max(a.limbs.len, b.limbs.len);
482 if (msl < req_limbs) {
483 r.limbs[msl] = 1;
484 r.len = req_limbs;
485 }
486 }
487
488 r.truncate(r.toConst(), signedness, bit_count);
489 }
490
491 /// r = a + b with 2s-complement saturating semantics.
346492 /// r, a and b may be aliases.
347493 ///
348 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
349 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.
350 pub fn sub(r: *Mutable, a: Const, b: Const) void {
351 if (a.positive != b.positive) {
494 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by
495 /// r is `calcTwosCompLimbCount(bit_count)`.
496 pub fn addSat(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
497 const req_limbs = calcTwosCompLimbCount(bit_count);
498
499 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
500 // if an overflow occured.
501 const x = Const{
502 .positive = a.positive,
503 .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)],
504 };
505
506 const y = Const{
507 .positive = b.positive,
508 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
509 };
510
511 if (r.addCarry(x, y)) {
512 // There are two possibilities here:
513 // - We overflowed req_limbs, in which case we need to saturate.
514 // - a and b had less elements than req_limbs, and those were overflowed.
515 // Note: In this case, might _also_ need to saturate.
516 const msl = math.max(a.limbs.len, b.limbs.len);
517 if (msl < req_limbs) {
518 r.limbs[msl] = 1;
519 r.len = req_limbs;
520 // Note: Saturation may still be required if msl == req_limbs - 1
521 } else {
522 // Overflowed req_limbs, definitely saturate.
523 r.setTwosCompIntLimit(if (r.positive) .max else .min, signedness, bit_count);
524 }
525 }
526
527 // Saturate if the result didn't fit.
528 r.saturate(r.toConst(), signedness, bit_count);
529 }
530
531 /// Base implementation for subtraction. Subtracts `max(a.limbs.len, b.limbs.len)` elements from a and b,
532 /// and returns whether any overflow occured.
533 /// r, a and b may be aliases.
534 ///
535 /// Asserts r has enough elements to hold the result. The upper bound is `max(a.limbs.len, b.limbs.len)`.
536 fn subCarry(r: *Mutable, a: Const, b: Const) bool {
537 if (a.eqZero()) {
538 r.copy(b);
539 r.positive = !b.positive;
540 return false;
541 } else if (b.eqZero()) {
542 r.copy(a);
543 return false;
544 } else if (a.positive != b.positive) {
352545 if (a.positive) {
353546 // (a) - (-b) => a + b
354 r.add(a, b.abs());
547 return r.addCarry(a, b.abs());
355548 } else {
356 // (-a) - (b) => -(a + b)
357 r.add(a.abs(), b);
358 r.positive = false;
549 // (-a) - (b) => -a + -b
550 return r.addCarry(a, b.negate());
359551 }
360 } else {
361 if (a.positive) {
552 } else if (a.positive) {
553 if (a.order(b) != .lt) {
362554 // (a) - (b) => a - b
363 if (a.order(b) != .lt) {
364 llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
365 r.normalize(a.limbs.len);
366 r.positive = true;
367 } else {
368 llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
369 r.normalize(b.limbs.len);
370 r.positive = false;
371 }
555 const c = llsubcarry(r.limbs, a.limbs, b.limbs);
556 r.normalize(a.limbs.len);
557 r.positive = true;
558 return c != 0;
372559 } else {
560 // (a) - (b) => -b + a => -(b - a)
561 const c = llsubcarry(r.limbs, b.limbs, a.limbs);
562 r.normalize(b.limbs.len);
563 r.positive = false;
564 return c != 0;
565 }
566 } else {
567 if (a.order(b) == .lt) {
373568 // (-a) - (-b) => -(a - b)
374 if (a.order(b) == .lt) {
375 llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
376 r.normalize(a.limbs.len);
377 r.positive = false;
378 } else {
379 llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
380 r.normalize(b.limbs.len);
381 r.positive = true;
382 }
569 const c = llsubcarry(r.limbs, a.limbs, b.limbs);
570 r.normalize(a.limbs.len);
571 r.positive = false;
572 return c != 0;
573 } else {
574 // (-a) - (-b) => --b + -a => b - a
575 const c = llsubcarry(r.limbs, b.limbs, a.limbs);
576 r.normalize(b.limbs.len);
577 r.positive = true;
578 return c != 0;
383579 }
384580 }
385581 }
386582
583 /// r = a - b
584 ///
585 /// r, a and b may be aliases.
586 ///
587 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
588 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.
589 pub fn sub(r: *Mutable, a: Const, b: Const) void {
590 r.add(a, b.negate());
591 }
592
593 /// r = a - b with 2s-complement wrapping semantics.
594 ///
595 /// r, a and b may be aliases
596 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
597 /// r is `calcTwosCompLimbCount(bit_count)`.
598 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
599 r.addWrap(a, b.negate(), signedness, bit_count);
600 }
601
602 /// r = a - b with 2s-complement saturating semantics.
603 /// r, a and b may be aliases.
604 ///
605 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by
606 /// r is `calcTwosCompLimbCount(bit_count)`.
607 pub fn subSat(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
608 r.addSat(a, b.negate(), signedness, bit_count);
609 }
610
387611 /// rma = a * b
388612 ///
389613 /// `rma` may alias with `a` or `b`.
390614 /// `a` and `b` may alias with each other.
391615 ///
392616 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
393 /// rma is given by `a.limbs.len + b.limbs.len + 1`.
617 /// rma is given by `a.limbs.len + b.limbs.len`.
394618 ///
395619 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcMulLimbsBufferLen`.
396620 pub fn mul(rma: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void {
......@@ -419,7 +643,7 @@ pub const Mutable = struct {
419643 /// `a` and `b` may alias with each other.
420644 ///
421645 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
422 /// rma is given by `a.limbs.len + b.limbs.len + 1`.
646 /// rma is given by `a.limbs.len + b.limbs.len`.
423647 ///
424648 /// If `allocator` is provided, it will be used for temporary storage to improve
425649 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
......@@ -435,14 +659,89 @@ pub const Mutable = struct {
435659 }
436660 }
437661
438 mem.set(Limb, rma.limbs[0 .. a.limbs.len + b.limbs.len + 1], 0);
662 mem.set(Limb, rma.limbs[0 .. a.limbs.len + b.limbs.len], 0);
439663
440 llmulacc(allocator, rma.limbs, a.limbs, b.limbs);
664 llmulacc(.add, allocator, rma.limbs, a.limbs, b.limbs);
441665
442666 rma.normalize(a.limbs.len + b.limbs.len);
443667 rma.positive = (a.positive == b.positive);
444668 }
445669
670 /// rma = a * b with 2s-complement wrapping semantics.
671 ///
672 /// `rma` may alias with `a` or `b`.
673 /// `a` and `b` may alias with each other.
674 ///
675 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
676 /// rma is given by `a.limbs.len + b.limbs.len`.
677 ///
678 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcMulWrapLimbsBufferLen`.
679 pub fn mulWrap(
680 rma: *Mutable,
681 a: Const,
682 b: Const,
683 signedness: std.builtin.Signedness,
684 bit_count: usize,
685 limbs_buffer: []Limb,
686 allocator: ?*Allocator,
687 ) void {
688 var buf_index: usize = 0;
689 const req_limbs = calcTwosCompLimbCount(bit_count);
690
691 const a_copy = if (rma.limbs.ptr == a.limbs.ptr) blk: {
692 const start = buf_index;
693 const a_len = math.min(req_limbs, a.limbs.len);
694 mem.copy(Limb, limbs_buffer[buf_index..], a.limbs[0..a_len]);
695 buf_index += a_len;
696 break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst();
697 } else a;
698
699 const b_copy = if (rma.limbs.ptr == b.limbs.ptr) blk: {
700 const start = buf_index;
701 const b_len = math.min(req_limbs, b.limbs.len);
702 mem.copy(Limb, limbs_buffer[buf_index..], b.limbs[0..b_len]);
703 buf_index += b_len;
704 break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst();
705 } else b;
706
707 return rma.mulWrapNoAlias(a_copy, b_copy, signedness, bit_count, allocator);
708 }
709
710 /// rma = a * b with 2s-complement wrapping semantics.
711 ///
712 /// `rma` may not alias with `a` or `b`.
713 /// `a` and `b` may alias with each other.
714 ///
715 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
716 /// rma is given by `a.limbs.len + b.limbs.len`.
717 ///
718 /// If `allocator` is provided, it will be used for temporary storage to improve
719 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
720 pub fn mulWrapNoAlias(
721 rma: *Mutable,
722 a: Const,
723 b: Const,
724 signedness: std.builtin.Signedness,
725 bit_count: usize,
726 allocator: ?*Allocator,
727 ) void {
728 assert(rma.limbs.ptr != a.limbs.ptr); // illegal aliasing
729 assert(rma.limbs.ptr != b.limbs.ptr); // illegal aliasing
730
731 const req_limbs = calcTwosCompLimbCount(bit_count);
732
733 // We can ignore the upper bits here, those results will be discarded anyway.
734 const a_limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)];
735 const b_limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)];
736
737 mem.set(Limb, rma.limbs[0..req_limbs], 0);
738
739 llmulacc(.add, allocator, rma.limbs, a_limbs, b_limbs);
740 rma.normalize(math.min(req_limbs, a.limbs.len + b.limbs.len));
741 rma.positive = (a.positive == b.positive);
742 rma.truncate(rma.toConst(), signedness, bit_count);
743 }
744
446745 /// rma = a * a
447746 ///
448747 /// `rma` may not alias with `a`.
......@@ -458,7 +757,7 @@ pub const Mutable = struct {
458757
459758 mem.set(Limb, rma.limbs, 0);
460759
461 llsquare_basecase(rma.limbs, a.limbs);
760 llsquareBasecase(rma.limbs, a.limbs);
462761
463762 rma.normalize(2 * a.limbs.len + 1);
464763 rma.positive = true;
......@@ -980,6 +1279,102 @@ pub const Mutable = struct {
9801279 r.normalize(r.len);
9811280 }
9821281
1282 /// Truncate an integer to a number of bits, following 2s-complement semantics.
1283 /// r may alias a.
1284 ///
1285 /// Asserts `r` has enough storage to store the result.
1286 /// The upper bound is `calcTwosCompLimbCount(a.len)`.
1287 pub fn truncate(r: *Mutable, a: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
1288 const req_limbs = calcTwosCompLimbCount(bit_count);
1289
1290 // Handle 0-bit integers.
1291 if (req_limbs == 0 or a.eqZero()) {
1292 r.set(0);
1293 return;
1294 }
1295
1296 const bit = @truncate(Log2Limb, bit_count - 1);
1297 const signmask = @as(Limb, 1) << bit; // 0b0..010...0 where 1 is the sign bit.
1298 const mask = (signmask << 1) -% 1; // 0b0..01..1 where the leftmost 1 is the sign bit.
1299
1300 if (!a.positive) {
1301 // Convert the integer from sign-magnitude into twos-complement.
1302 // -x = ~(x - 1)
1303 // Note, we simply take req_limbs * @bitSizeOf(Limb) as the
1304 // target bit count.
1305
1306 r.addScalar(a.abs(), -1);
1307
1308 // Zero-extend the result
1309 if (req_limbs > r.len) {
1310 mem.set(Limb, r.limbs[r.len..req_limbs], 0);
1311 }
1312
1313 // Truncate to required number of limbs.
1314 assert(r.limbs.len >= req_limbs);
1315 r.len = req_limbs;
1316
1317 // Without truncating, we can already peek at the sign bit of the result here.
1318 // Note that it will be 0 if the result is negative, as we did not apply the flip here.
1319 // If the result is negative, we have
1320 // -(-x & mask)
1321 // = ~(~(x - 1) & mask) + 1
1322 // = ~(~((x - 1) | ~mask)) + 1
1323 // = ((x - 1) | ~mask)) + 1
1324 // Note, this is only valid for the target bits and not the upper bits
1325 // of the most significant limb. Those still need to be cleared.
1326 // Also note that `mask` is zero for all other bits, reducing to the identity.
1327 // This means that we still need to use & mask to clear off the upper bits.
1328
1329 if (signedness == .signed and r.limbs[r.len - 1] & signmask == 0) {
1330 // Re-add the one and negate to get the result.
1331 r.limbs[r.len - 1] &= mask;
1332 // Note, addition cannot require extra limbs here as we did a subtraction before.
1333 r.addScalar(r.toConst(), 1);
1334 r.normalize(r.len);
1335 r.positive = false;
1336 } else {
1337 llnot(r.limbs[0..r.len]);
1338 r.limbs[r.len - 1] &= mask;
1339 r.normalize(r.len);
1340 }
1341 } else {
1342 r.copy(a);
1343 if (r.len < req_limbs) {
1344 // Integer fits within target bits, no wrapping required.
1345 return;
1346 }
1347
1348 r.len = req_limbs;
1349 r.limbs[r.len - 1] &= mask;
1350 r.normalize(r.len);
1351
1352 if (signedness == .signed and r.limbs[r.len - 1] & signmask != 0) {
1353 // Convert 2s-complement back to sign-magnitude.
1354 // Sign-extend the upper bits so that they are inverted correctly.
1355 r.limbs[r.len - 1] |= ~mask;
1356 llnot(r.limbs[0..r.len]);
1357
1358 // Note, can only overflow if r holds 0xFFF...F which can only happen if
1359 // a holds 0.
1360 r.addScalar(r.toConst(), 1);
1361
1362 r.positive = false;
1363 }
1364 }
1365 }
1366
1367 /// Saturate an integer to a number of bits, following 2s-complement semantics.
1368 /// r may alias a.
1369 ///
1370 /// Asserts `r` has enough storage to store the result.
1371 /// The upper bound is `calcTwosCompLimbCount(a.len)`.
1372 pub fn saturate(r: *Mutable, a: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
1373 if (!a.fitsInTwosComp(signedness, bit_count)) {
1374 r.setTwosCompIntLimit(if (r.positive) .max else .min, signedness, bit_count);
1375 }
1376 }
1377
9831378 /// Normalize a possible sequence of leading zeros.
9841379 ///
9851380 /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
......@@ -1040,6 +1435,13 @@ pub const Const = struct {
10401435 };
10411436 }
10421437
1438 pub fn negate(self: Const) Const {
1439 return .{
1440 .limbs = self.limbs,
1441 .positive = !self.positive,
1442 };
1443 }
1444
10431445 pub fn isOdd(self: Const) bool {
10441446 return self.limbs[0] & 1 != 0;
10451447 }
......@@ -1643,6 +2045,21 @@ pub const Managed = struct {
16432045 self.setMetadata(m.positive, m.len);
16442046 }
16452047
2048 /// Set self to either bound of a 2s-complement integer.
2049 /// Note: The result is still sign-magnitude, not twos complement! In order to convert the
2050 /// result to twos complement, it is sufficient to take the absolute value.
2051 pub fn setTwosCompIntLimit(
2052 r: *Managed,
2053 limit: TwosCompIntLimit,
2054 signedness: std.builtin.Signedness,
2055 bit_count: usize,
2056 ) !void {
2057 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
2058 var m = r.toMutable();
2059 m.setTwosCompIntLimit(limit, signedness, bit_count);
2060 r.setMetadata(m.positive, m.len);
2061 }
2062
16462063 /// Converts self to a string in the requested base. Memory is allocated from the provided
16472064 /// allocator and not the one present in self.
16482065 pub fn toString(self: Managed, allocator: *Allocator, base: u8, case: std.fmt.Case) ![]u8 {
......@@ -1741,6 +2158,32 @@ pub const Managed = struct {
17412158 r.setMetadata(m.positive, m.len);
17422159 }
17432160
2161 /// r = a + b with 2s-complement wrapping semantics.
2162 ///
2163 /// r, a and b may be aliases. If r aliases a or b, then caller must call
2164 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2165 ///
2166 /// Returns an error if memory could not be allocated.
2167 pub fn addWrap(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {
2168 try r.ensureTwosCompCapacity(bit_count);
2169 var m = r.toMutable();
2170 m.addWrap(a, b, signedness, bit_count);
2171 r.setMetadata(m.positive, m.len);
2172 }
2173
2174 /// r = a + b with 2s-complement saturating semantics.
2175 ///
2176 /// r, a and b may be aliases. If r aliases a or b, then caller must call
2177 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2178 ///
2179 /// Returns an error if memory could not be allocated.
2180 pub fn addSat(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {
2181 try r.ensureTwosCompCapacity(bit_count);
2182 var m = r.toMutable();
2183 m.addSat(a, b, signedness, bit_count);
2184 r.setMetadata(m.positive, m.len);
2185 }
2186
17442187 /// r = a - b
17452188 ///
17462189 /// r, a and b may be aliases.
......@@ -1753,6 +2196,32 @@ pub const Managed = struct {
17532196 r.setMetadata(m.positive, m.len);
17542197 }
17552198
2199 /// r = a - b with 2s-complement wrapping semantics.
2200 ///
2201 /// r, a and b may be aliases. If r aliases a or b, then caller must call
2202 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2203 ///
2204 /// Returns an error if memory could not be allocated.
2205 pub fn subWrap(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {
2206 try r.ensureTwosCompCapacity(bit_count);
2207 var m = r.toMutable();
2208 m.subWrap(a, b, signedness, bit_count);
2209 r.setMetadata(m.positive, m.len);
2210 }
2211
2212 /// r = a - b with 2s-complement saturating semantics.
2213 ///
2214 /// r, a and b may be aliases. If r aliases a or b, then caller must call
2215 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2216 ///
2217 /// Returns an error if memory could not be allocated.
2218 pub fn subSat(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {
2219 try r.ensureTwosCompCapacity(bit_count);
2220 var m = r.toMutable();
2221 m.subSat(a, b, signedness, bit_count);
2222 r.setMetadata(m.positive, m.len);
2223 }
2224
17562225 /// rma = a * b
17572226 ///
17582227 /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
......@@ -1781,6 +2250,39 @@ pub const Managed = struct {
17812250 rma.setMetadata(m.positive, m.len);
17822251 }
17832252
2253 /// rma = a * b with 2s-complement wrapping semantics.
2254 ///
2255 /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
2256 /// If rma aliases a or b, then caller must call `ensureTwosCompCapacity`
2257 /// prior to calling `mul`.
2258 ///
2259 /// Returns an error if memory could not be allocated.
2260 ///
2261 /// rma's allocator is used for temporary storage to speed up the multiplication.
2262 pub fn mulWrap(rma: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) !void {
2263 var alias_count: usize = 0;
2264 if (rma.limbs.ptr == a.limbs.ptr)
2265 alias_count += 1;
2266 if (rma.limbs.ptr == b.limbs.ptr)
2267 alias_count += 1;
2268
2269 try rma.ensureTwosCompCapacity(bit_count);
2270 var m = rma.toMutable();
2271 if (alias_count == 0) {
2272 m.mulWrapNoAlias(a, b, signedness, bit_count, rma.allocator);
2273 } else {
2274 const limb_count = calcMulWrapLimbsBufferLen(bit_count, a.limbs.len, b.limbs.len, alias_count);
2275 const limbs_buffer = try rma.allocator.alloc(Limb, limb_count);
2276 defer rma.allocator.free(limbs_buffer);
2277 m.mulWrap(a, b, signedness, bit_count, limbs_buffer, rma.allocator);
2278 }
2279 rma.setMetadata(m.positive, m.len);
2280 }
2281
2282 pub fn ensureTwosCompCapacity(r: *Managed, bit_count: usize) !void {
2283 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
2284 }
2285
17842286 pub fn ensureAddScalarCapacity(r: *Managed, a: Const, scalar: anytype) !void {
17852287 try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1);
17862288 }
......@@ -1941,30 +2443,58 @@ pub const Managed = struct {
19412443 rma.setMetadata(rma_mut.positive, rma_mut.len);
19422444 }
19432445 }
2446
2447 /// r = truncate(Int(signedness, bit_count), a)
2448 pub fn truncate(r: *Managed, a: Const, signedness: std.builtin.Signedness, bit_count: usize) !void {
2449 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
2450 var m = r.toMutable();
2451 m.truncate(a, signedness, bit_count);
2452 r.setMetadata(m.positive, m.len);
2453 }
2454
2455 /// r = saturate(Int(signedness, bit_count), a)
2456 pub fn saturate(r: *Managed, a: Const, signedness: std.builtin.Signedness, bit_count: usize) !void {
2457 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
2458 var m = r.toMutable();
2459 m.saturate(a, signedness, bit_count);
2460 r.setMetadata(m.positive, m.len);
2461 }
2462};
2463
2464/// Different operators which can be used in accumulation style functions
2465/// (llmulacc, llmulaccKaratsuba, llmulaccLong, llmulLimb). In all these functions,
2466/// a computed value is accumulated with an existing result.
2467const AccOp = enum {
2468 /// The computed value is added to the result.
2469 add,
2470
2471 /// The computed value is subtracted from the result.
2472 sub,
19442473};
19452474
19462475/// Knuth 4.3.1, Algorithm M.
19472476///
2477/// r = r (op) a * b
19482478/// r MUST NOT alias any of a or b.
1949fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const Limb) void {
2479///
2480/// The result is computed modulo `r.len`. When `r.len >= a.len + b.len`, no overflow occurs.
2481fn llmulacc(comptime op: AccOp, opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const Limb) void {
19502482 @setRuntimeSafety(debug_safety);
2483 assert(r.len >= a.len);
2484 assert(r.len >= b.len);
19512485
1952 const a_norm = a[0..llnormalize(a)];
1953 const b_norm = b[0..llnormalize(b)];
1954 var x = a_norm;
1955 var y = b_norm;
1956 if (a_norm.len > b_norm.len) {
1957 x = b_norm;
1958 y = a_norm;
2486 // Order greatest first.
2487 var x = a;
2488 var y = b;
2489 if (a.len < b.len) {
2490 x = b;
2491 y = a;
19592492 }
19602493
1961 assert(r.len >= x.len + y.len + 1);
1962
1963 // 48 is a pretty abitrary size chosen based on performance of a factorial program.
19642494 k_mul: {
1965 if (x.len > 48) {
2495 if (y.len > 48) {
19662496 if (opt_allocator) |allocator| {
1967 llmulacc_karatsuba(allocator, r, x, y) catch |err| switch (err) {
2497 llmulaccKaratsuba(op, allocator, r, x, y) catch |err| switch (err) {
19682498 error.OutOfMemory => break :k_mul, // handled below
19692499 };
19702500 return;
......@@ -1972,83 +2502,191 @@ fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const L
19722502 }
19732503 }
19742504
1975 // Basecase multiplication
1976 var i: usize = 0;
1977 while (i < x.len) : (i += 1) {
1978 llmulDigit(r[i..], y, x[i]);
1979 }
2505 llmulaccLong(op, r, x, y);
19802506}
19812507
19822508/// Knuth 4.3.1, Algorithm M.
19832509///
2510/// r = r (op) a * b
19842511/// r MUST NOT alias any of a or b.
1985fn llmulacc_karatsuba(allocator: *Allocator, r: []Limb, x: []const Limb, y: []const Limb) error{OutOfMemory}!void {
2512///
2513/// The result is computed modulo `r.len`. When `r.len >= a.len + b.len`, no overflow occurs.
2514fn llmulaccKaratsuba(
2515 comptime op: AccOp,
2516 allocator: *Allocator,
2517 r: []Limb,
2518 a: []const Limb,
2519 b: []const Limb,
2520) error{OutOfMemory}!void {
19862521 @setRuntimeSafety(debug_safety);
2522 assert(r.len >= a.len);
2523 assert(a.len >= b.len);
19872524
1988 assert(r.len >= x.len + y.len + 1);
2525 // Classical karatsuba algorithm:
2526 // a = a1 * B + a0
2527 // b = b1 * B + b0
2528 // Where a0, b0 < B
2529 //
2530 // We then have:
2531 // ab = a * b
2532 // = (a1 * B + a0) * (b1 * B + b0)
2533 // = a1 * b1 * B * B + a1 * B * b0 + a0 * b1 * B + a0 * b0
2534 // = a1 * b1 * B * B + (a1 * b0 + a0 * b1) * B + a0 * b0
2535 //
2536 // Note that:
2537 // a1 * b0 + a0 * b1
2538 // = (a1 + a0)(b1 + b0) - a1 * b1 - a0 * b0
2539 // = (a0 - a1)(b1 - b0) + a1 * b1 + a0 * b0
2540 //
2541 // This yields:
2542 // ab = p2 * B^2 + (p0 + p1 + p2) * B + p0
2543 //
2544 // Where:
2545 // p0 = a0 * b0
2546 // p1 = (a0 - a1)(b1 - b0)
2547 // p2 = a1 * b1
2548 //
2549 // Note, (a0 - a1) and (b1 - b0) produce values -B < x < B, and so we need to mind the sign here.
2550 // We also have:
2551 // 0 <= p0 <= 2B
2552 // -2B <= p1 <= 2B
2553 //
2554 // Note, when B is a multiple of the limb size, multiplies by B amount to shifts or
2555 // slices of a limbs array.
2556 //
2557 // This function computes the result of the multiplication modulo r.len. This means:
2558 // - p2 and p1 only need to be computed modulo r.len - B.
2559 // - In the case of p2, p2 * B^2 needs to be added modulo r.len - 2 * B.
2560
2561 const split = b.len / 2; // B
19892562
1990 const split = @divFloor(x.len, 2);
1991 var x0 = x[0..split];
1992 var x1 = x[split..x.len];
1993 var y0 = y[0..split];
1994 var y1 = y[split..y.len];
2563 const limbs_after_split = r.len - split; // Limbs to compute for p1 and p2.
2564 const limbs_after_split2 = r.len - split * 2; // Limbs to add for p2 * B^2.
2565
2566 // For a0 and b0 we need the full range.
2567 const a0 = a[0..llnormalize(a[0..split])];
2568 const b0 = b[0..llnormalize(b[0..split])];
2569
2570 // For a1 and b1 we only need `limbs_after_split` limbs.
2571 const a1 = blk: {
2572 var a1 = a[split..];
2573 a1.len = math.min(llnormalize(a1), limbs_after_split);
2574 break :blk a1;
2575 };
19952576
1996 var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1);
2577 const b1 = blk: {
2578 var b1 = b[split..];
2579 b1.len = math.min(llnormalize(b1), limbs_after_split);
2580 break :blk b1;
2581 };
2582
2583 // Note that the above slices relative to `split` work because we have a.len > b.len.
2584
2585 // We need some temporary memory to store intermediate results.
2586 // Note, we can reduce the amount of temporaries we need by reordering the computation here:
2587 // ab = p2 * B^2 + (p0 + p1 + p2) * B + p0
2588 // = p2 * B^2 + (p0 * B + p1 * B + p2 * B) + p0
2589 // = (p2 * B^2 + p2 * B) + (p0 * B + p0) + p1 * B
2590
2591 // Allocate at least enough memory to be able to multiply the upper two segments of a and b, assuming
2592 // no overflow.
2593 const tmp = try allocator.alloc(Limb, a.len - split + b.len - split);
19972594 defer allocator.free(tmp);
1998 mem.set(Limb, tmp, 0);
19992595
2000 llmulacc(allocator, tmp, x1, y1);
2596 // Compute p2.
2597 // Note, we don't need to compute all of p2, just enough limbs to satisfy r.
2598 const p2_limbs = math.min(limbs_after_split, a1.len + b1.len);
20012599
2002 var length = llnormalize(tmp);
2003 _ = llaccum(r[split..], tmp[0..length]);
2004 _ = llaccum(r[split * 2 ..], tmp[0..length]);
2600 mem.set(Limb, tmp[0..p2_limbs], 0);
2601 llmulacc(.add, allocator, tmp[0..p2_limbs], a1[0..math.min(a1.len, p2_limbs)], b1[0..math.min(b1.len, p2_limbs)]);
2602 const p2 = tmp[0..llnormalize(tmp[0..p2_limbs])];
20052603
2006 mem.set(Limb, tmp[0..length], 0);
2604 // Add p2 * B to the result.
2605 llaccum(op, r[split..], p2);
20072606
2008 llmulacc(allocator, tmp, x0, y0);
2607 // Add p2 * B^2 to the result if required.
2608 if (limbs_after_split2 > 0) {
2609 llaccum(op, r[split * 2 ..], p2[0..math.min(p2.len, limbs_after_split2)]);
2610 }
2611
2612 // Compute p0.
2613 // Since a0.len, b0.len <= split and r.len >= split * 2, the full width of p0 needs to be computed.
2614 const p0_limbs = a0.len + b0.len;
2615 mem.set(Limb, tmp[0..p0_limbs], 0);
2616 llmulacc(.add, allocator, tmp[0..p0_limbs], a0, b0);
2617 const p0 = tmp[0..llnormalize(tmp[0..p0_limbs])];
2618
2619 // Add p0 to the result.
2620 llaccum(op, r, p0);
20092621
2010 length = llnormalize(tmp);
2011 _ = llaccum(r[0..], tmp[0..length]);
2012 _ = llaccum(r[split..], tmp[0..length]);
2622 // Add p0 * B to the result. In this case, we may not need all of it.
2623 llaccum(op, r[split..], p0[0..math.min(limbs_after_split, p0.len)]);
20132624
2014 const x_cmp = llcmp(x1, x0);
2015 const y_cmp = llcmp(y1, y0);
2016 if (x_cmp * y_cmp == 0) {
2625 // Finally, compute and add p1.
2626 // From now on we only need `limbs_after_split` limbs for a0 and b0, since the result of the
2627 // following computation will be added * B.
2628 const a0x = a0[0..std.math.min(a0.len, limbs_after_split)];
2629 const b0x = b0[0..std.math.min(b0.len, limbs_after_split)];
2630
2631 const j0_sign = llcmp(a0x, a1);
2632 const j1_sign = llcmp(b1, b0x);
2633
2634 if (j0_sign * j1_sign == 0) {
2635 // p1 is zero, we don't need to do any computation at all.
20172636 return;
20182637 }
2019 const x0_len = llnormalize(x0);
2020 const x1_len = llnormalize(x1);
2021 var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len));
2022 defer allocator.free(j0);
2023 if (x_cmp == 1) {
2024 llsub(j0, x1[0..x1_len], x0[0..x0_len]);
2638
2639 mem.set(Limb, tmp, 0);
2640
2641 // p1 is nonzero, so compute the intermediary terms j0 = a0 - a1 and j1 = b1 - b0.
2642 // Note that in this case, we again need some storage for intermediary results
2643 // j0 and j1. Since we have tmp.len >= 2B, we can store both
2644 // intermediaries in the already allocated array.
2645 const j0 = tmp[0 .. a.len - split];
2646 const j1 = tmp[a.len - split ..];
2647
2648 // Ensure that no subtraction overflows.
2649 if (j0_sign == 1) {
2650 // a0 > a1.
2651 _ = llsubcarry(j0, a0x, a1);
20252652 } else {
2026 llsub(j0, x0[0..x0_len], x1[0..x1_len]);
2653 // a0 < a1.
2654 _ = llsubcarry(j0, a1, a0x);
20272655 }
20282656
2029 const y0_len = llnormalize(y0);
2030 const y1_len = llnormalize(y1);
2031 var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len));
2032 defer allocator.free(j1);
2033 if (y_cmp == 1) {
2034 llsub(j1, y1[0..y1_len], y0[0..y0_len]);
2657 if (j1_sign == 1) {
2658 // b1 > b0.
2659 _ = llsubcarry(j1, b1, b0x);
20352660 } else {
2036 llsub(j1, y0[0..y0_len], y1[0..y1_len]);
2661 // b1 > b0.
2662 _ = llsubcarry(j1, b0x, b1);
20372663 }
2038 if (x_cmp == y_cmp) {
2039 mem.set(Limb, tmp[0..length], 0);
2040 llmulacc(allocator, tmp, j0, j1);
20412664
2042 length = llnormalize(tmp);
2043 llsub(r[split..], r[split..], tmp[0..length]);
2665 if (j0_sign * j1_sign == 1) {
2666 // If j0 and j1 are both positive, we now have:
2667 // p1 = j0 * j1
2668 // If j0 and j1 are both negative, we now have:
2669 // p1 = -j0 * -j1 = j0 * j1
2670 // In this case we can add p1 to the result using llmulacc.
2671 llmulacc(op, allocator, r[split..], j0[0..llnormalize(j0)], j1[0..llnormalize(j1)]);
20442672 } else {
2045 llmulacc(allocator, r[split..], j0, j1);
2673 // In this case either j0 or j1 is negative, an we have:
2674 // p1 = -(j0 * j1)
2675 // Now we need to subtract instead of accumulate.
2676 const inverted_op = if (op == .add) .sub else .add;
2677 llmulacc(inverted_op, allocator, r[split..], j0[0..llnormalize(j0)], j1[0..llnormalize(j1)]);
20462678 }
20472679}
20482680
2049// r = r + a
2050fn llaccum(r: []Limb, a: []const Limb) Limb {
2681/// r = r (op) a.
2682/// The result is computed modulo `r.len`.
2683fn llaccum(comptime op: AccOp, r: []Limb, a: []const Limb) void {
20512684 @setRuntimeSafety(debug_safety);
2685 if (op == .sub) {
2686 _ = llsubcarry(r, r, a);
2687 return;
2688 }
2689
20522690 assert(r.len != 0 and a.len != 0);
20532691 assert(r.len >= a.len);
20542692
......@@ -2065,8 +2703,6 @@ fn llaccum(r: []Limb, a: []const Limb) Limb {
20652703 while ((carry != 0) and i < r.len) : (i += 1) {
20662704 carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
20672705 }
2068
2069 return carry;
20702706}
20712707
20722708/// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs.
......@@ -2097,24 +2733,55 @@ pub fn llcmp(a: []const Limb, b: []const Limb) i8 {
20972733 }
20982734}
20992735
2100fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void {
2736/// r = r (op) y * xi
2737/// The result is computed modulo `r.len`. When `r.len >= a.len + b.len`, no overflow occurs.
2738fn llmulaccLong(comptime op: AccOp, r: []Limb, a: []const Limb, b: []const Limb) void {
2739 @setRuntimeSafety(debug_safety);
2740 assert(r.len >= a.len);
2741 assert(a.len >= b.len);
2742
2743 var i: usize = 0;
2744 while (i < b.len) : (i += 1) {
2745 llmulLimb(op, r[i..], a, b[i]);
2746 }
2747}
2748
2749/// r = r (op) y * xi
2750/// The result is computed modulo `r.len`.
2751fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) void {
21012752 @setRuntimeSafety(debug_safety);
21022753 if (xi == 0) {
21032754 return;
21042755 }
21052756
2106 var carry: Limb = 0;
21072757 var a_lo = acc[0..y.len];
21082758 var a_hi = acc[y.len..];
21092759
2110 var j: usize = 0;
2111 while (j < a_lo.len) : (j += 1) {
2112 a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry });
2113 }
2760 switch (op) {
2761 .add => {
2762 var carry: Limb = 0;
2763 var j: usize = 0;
2764 while (j < a_lo.len) : (j += 1) {
2765 a_lo[j] = addMulLimbWithCarry(a_lo[j], y[j], xi, &carry);
2766 }
21142767
2115 j = 0;
2116 while ((carry != 0) and (j < a_hi.len)) : (j += 1) {
2117 carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));
2768 j = 0;
2769 while ((carry != 0) and (j < a_hi.len)) : (j += 1) {
2770 carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));
2771 }
2772 },
2773 .sub => {
2774 var borrow: Limb = 0;
2775 var j: usize = 0;
2776 while (j < a_lo.len) : (j += 1) {
2777 a_lo[j] = subMulLimbWithBorrow(a_lo[j], y[j], xi, &borrow);
2778 }
2779
2780 j = 0;
2781 while ((borrow != 0) and (j < a_hi.len)) : (j += 1) {
2782 borrow = @boolToInt(@subWithOverflow(Limb, a_hi[j], borrow, &a_hi[j]));
2783 }
2784 },
21182785 }
21192786}
21202787
......@@ -2133,10 +2800,10 @@ fn llnormalize(a: []const Limb) usize {
21332800}
21342801
21352802/// Knuth 4.3.1, Algorithm S.
2136fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
2803fn llsubcarry(r: []Limb, a: []const Limb, b: []const Limb) Limb {
21372804 @setRuntimeSafety(debug_safety);
21382805 assert(a.len != 0 and b.len != 0);
2139 assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));
2806 assert(a.len >= b.len);
21402807 assert(r.len >= a.len);
21412808
21422809 var i: usize = 0;
......@@ -2153,15 +2820,21 @@ fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
21532820 borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i]));
21542821 }
21552822
2156 assert(borrow == 0);
2823 return borrow;
2824}
2825
2826fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
2827 @setRuntimeSafety(debug_safety);
2828 assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));
2829 assert(llsubcarry(r, a, b) == 0);
21572830}
21582831
21592832/// Knuth 4.3.1, Algorithm A.
2160fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
2833fn lladdcarry(r: []Limb, a: []const Limb, b: []const Limb) Limb {
21612834 @setRuntimeSafety(debug_safety);
21622835 assert(a.len != 0 and b.len != 0);
21632836 assert(a.len >= b.len);
2164 assert(r.len >= a.len + 1);
2837 assert(r.len >= a.len);
21652838
21662839 var i: usize = 0;
21672840 var carry: Limb = 0;
......@@ -2177,7 +2850,13 @@ fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
21772850 carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i]));
21782851 }
21792852
2180 r[i] = carry;
2853 return carry;
2854}
2855
2856fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
2857 @setRuntimeSafety(debug_safety);
2858 assert(r.len >= a.len + 1);
2859 r[a.len] = lladdcarry(r, a, b);
21812860}
21822861
21832862/// Knuth 4.3.1, Exercise 16.
......@@ -2258,6 +2937,15 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
22582937 }
22592938}
22602939
2940// r = ~r
2941fn llnot(r: []Limb) void {
2942 @setRuntimeSafety(debug_safety);
2943
2944 for (r) |*elem| {
2945 elem.* = ~elem.*;
2946 }
2947}
2948
22612949// r = a | b with 2s complement semantics.
22622950// r may alias.
22632951// a and b must not be 0.
......@@ -2554,7 +3242,7 @@ fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_
25543242}
25553243
25563244/// r MUST NOT alias x.
2557fn llsquare_basecase(r: []Limb, x: []const Limb) void {
3245fn llsquareBasecase(r: []Limb, x: []const Limb) void {
25583246 @setRuntimeSafety(debug_safety);
25593247
25603248 const x_norm = x;
......@@ -2577,7 +3265,7 @@ fn llsquare_basecase(r: []Limb, x: []const Limb) void {
25773265
25783266 for (x_norm) |v, i| {
25793267 // Accumulate all the x[i]*x[j] (with x!=j) products
2580 llmulDigit(r[2 * i + 1 ..], x_norm[i + 1 ..], v);
3268 llmulLimb(.add, r[2 * i + 1 ..], x_norm[i + 1 ..], v);
25813269 }
25823270
25833271 // Each product appears twice, multiply by 2
......@@ -2585,7 +3273,7 @@ fn llsquare_basecase(r: []Limb, x: []const Limb) void {
25853273
25863274 for (x_norm) |v, i| {
25873275 // Compute and add the squares
2588 llmulDigit(r[2 * i ..], x[i .. i + 1], v);
3276 llmulLimb(.add, r[2 * i ..], x[i .. i + 1], v);
25893277 }
25903278}
25913279
......@@ -2624,12 +3312,12 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
26243312 while (i < exp_bits) : (i += 1) {
26253313 // Square
26263314 mem.set(Limb, tmp2, 0);
2627 llsquare_basecase(tmp2, tmp1[0..llnormalize(tmp1)]);
3315 llsquareBasecase(tmp2, tmp1[0..llnormalize(tmp1)]);
26283316 mem.swap([]Limb, &tmp1, &tmp2);
26293317 // Multiply by a
26303318 if (@shlWithOverflow(u32, exp, 1, &exp)) {
26313319 mem.set(Limb, tmp2, 0);
2632 llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a);
3320 llmulacc(.add, null, tmp2, tmp1[0..llnormalize(tmp1)], a);
26333321 mem.swap([]Limb, &tmp1, &tmp2);
26343322 }
26353323 }
lib/std/math/big/int_test.zig+430
......@@ -4,6 +4,7 @@ const testing = std.testing;
44const Managed = std.math.big.int.Managed;
55const Mutable = std.math.big.int.Mutable;
66const Limb = std.math.big.Limb;
7const SignedLimb = std.math.big.SignedLimb;
78const DoubleLimb = std.math.big.DoubleLimb;
89const SignedDoubleLimb = std.math.big.SignedDoubleLimb;
910const maxInt = std.math.maxInt;
......@@ -269,6 +270,36 @@ test "big.int string set bad base error" {
269270 try testing.expectError(error.InvalidBase, a.setString(45, "10"));
270271}
271272
273test "big.int twos complement limit set" {
274 const test_types = [_]type{
275 u64,
276 i64,
277 u1,
278 i1,
279 u0,
280 i0,
281 u65,
282 i65,
283 };
284
285 inline for (test_types) |T| {
286 // To work around 'control flow attempts to use compile-time variable at runtime'
287 const U = T;
288 const int_info = @typeInfo(U).Int;
289
290 var a = try Managed.init(testing.allocator);
291 defer a.deinit();
292
293 try a.setTwosCompIntLimit(.max, int_info.signedness, int_info.bits);
294 var max: U = maxInt(U);
295 try testing.expect(max == try a.to(U));
296
297 try a.setTwosCompIntLimit(.min, int_info.signedness, int_info.bits);
298 var min: U = minInt(U);
299 try testing.expect(min == try a.to(U));
300 }
301}
302
272303test "big.int string to" {
273304 var a = try Managed.initSet(testing.allocator, 120317241209124781241290847124);
274305 defer a.deinit();
......@@ -545,6 +576,198 @@ test "big.int add scalar" {
545576 try testing.expect((try b.to(u32)) == 55);
546577}
547578
579test "big.int addWrap single-single, unsigned" {
580 var a = try Managed.initSet(testing.allocator, maxInt(u17));
581 defer a.deinit();
582
583 var b = try Managed.initSet(testing.allocator, 10);
584 defer b.deinit();
585
586 try a.addWrap(a.toConst(), b.toConst(), .unsigned, 17);
587
588 try testing.expect((try a.to(u17)) == 9);
589}
590
591test "big.int subWrap single-single, unsigned" {
592 var a = try Managed.initSet(testing.allocator, 0);
593 defer a.deinit();
594
595 var b = try Managed.initSet(testing.allocator, maxInt(u17));
596 defer b.deinit();
597
598 try a.subWrap(a.toConst(), b.toConst(), .unsigned, 17);
599
600 try testing.expect((try a.to(u17)) == 1);
601}
602
603test "big.int addWrap multi-multi, unsigned, limb aligned" {
604 var a = try Managed.initSet(testing.allocator, maxInt(DoubleLimb));
605 defer a.deinit();
606
607 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb));
608 defer b.deinit();
609
610 try a.addWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
611
612 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 1);
613}
614
615test "big.int subWrap single-multi, unsigned, limb aligned" {
616 var a = try Managed.initSet(testing.allocator, 10);
617 defer a.deinit();
618
619 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb) + 100);
620 defer b.deinit();
621
622 try a.subWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
623
624 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 88);
625}
626
627test "big.int addWrap single-single, signed" {
628 var a = try Managed.initSet(testing.allocator, maxInt(i21));
629 defer a.deinit();
630
631 var b = try Managed.initSet(testing.allocator, 1 + 1 + maxInt(u21));
632 defer b.deinit();
633
634 try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));
635
636 try testing.expect((try a.to(i21)) == minInt(i21));
637}
638
639test "big.int subWrap single-single, signed" {
640 var a = try Managed.initSet(testing.allocator, minInt(i21));
641 defer a.deinit();
642
643 var b = try Managed.initSet(testing.allocator, 1);
644 defer b.deinit();
645
646 try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));
647
648 try testing.expect((try a.to(i21)) == maxInt(i21));
649}
650
651test "big.int addWrap multi-multi, signed, limb aligned" {
652 var a = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb));
653 defer a.deinit();
654
655 var b = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb));
656 defer b.deinit();
657
658 try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
659
660 try testing.expect((try a.to(SignedDoubleLimb)) == -2);
661}
662
663test "big.int subWrap single-multi, signed, limb aligned" {
664 var a = try Managed.initSet(testing.allocator, minInt(SignedDoubleLimb));
665 defer a.deinit();
666
667 var b = try Managed.initSet(testing.allocator, 1);
668 defer b.deinit();
669
670 try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
671
672 try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
673}
674
675test "big.int addSat single-single, unsigned" {
676 var a = try Managed.initSet(testing.allocator, maxInt(u17) - 5);
677 defer a.deinit();
678
679 var b = try Managed.initSet(testing.allocator, 10);
680 defer b.deinit();
681
682 try a.addSat(a.toConst(), b.toConst(), .unsigned, 17);
683
684 try testing.expect((try a.to(u17)) == maxInt(u17));
685}
686
687test "big.int subSat single-single, unsigned" {
688 var a = try Managed.initSet(testing.allocator, 123);
689 defer a.deinit();
690
691 var b = try Managed.initSet(testing.allocator, 4000);
692 defer b.deinit();
693
694 try a.subSat(a.toConst(), b.toConst(), .unsigned, 17);
695
696 try testing.expect((try a.to(u17)) == 0);
697}
698
699test "big.int addSat multi-multi, unsigned, limb aligned" {
700 var a = try Managed.initSet(testing.allocator, maxInt(DoubleLimb));
701 defer a.deinit();
702
703 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb));
704 defer b.deinit();
705
706 try a.addSat(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
707
708 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb));
709}
710
711test "big.int subSat single-multi, unsigned, limb aligned" {
712 var a = try Managed.initSet(testing.allocator, 10);
713 defer a.deinit();
714
715 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb) + 100);
716 defer b.deinit();
717
718 try a.subSat(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
719
720 try testing.expect((try a.to(DoubleLimb)) == 0);
721}
722
723test "big.int addSat single-single, signed" {
724 var a = try Managed.initSet(testing.allocator, maxInt(i14));
725 defer a.deinit();
726
727 var b = try Managed.initSet(testing.allocator, 1);
728 defer b.deinit();
729
730 try a.addSat(a.toConst(), b.toConst(), .signed, @bitSizeOf(i14));
731
732 try testing.expect((try a.to(i14)) == maxInt(i14));
733}
734
735test "big.int subSat single-single, signed" {
736 var a = try Managed.initSet(testing.allocator, minInt(i21));
737 defer a.deinit();
738
739 var b = try Managed.initSet(testing.allocator, 1);
740 defer b.deinit();
741
742 try a.subSat(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));
743
744 try testing.expect((try a.to(i21)) == minInt(i21));
745}
746
747test "big.int addSat multi-multi, signed, limb aligned" {
748 var a = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb));
749 defer a.deinit();
750
751 var b = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb));
752 defer b.deinit();
753
754 try a.addSat(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
755
756 try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
757}
758
759test "big.int subSat single-multi, signed, limb aligned" {
760 var a = try Managed.initSet(testing.allocator, minInt(SignedDoubleLimb));
761 defer a.deinit();
762
763 var b = try Managed.initSet(testing.allocator, 1);
764 defer b.deinit();
765
766 try a.subSat(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
767
768 try testing.expect((try a.to(SignedDoubleLimb)) == minInt(SignedDoubleLimb));
769}
770
548771test "big.int sub single-single" {
549772 var a = try Managed.initSet(testing.allocator, 50);
550773 defer a.deinit();
......@@ -748,6 +971,84 @@ test "big.int mul large" {
748971 try testing.expect(b.eq(c));
749972}
750973
974test "big.int mulWrap single-single unsigned" {
975 var a = try Managed.initSet(testing.allocator, 1234);
976 defer a.deinit();
977 var b = try Managed.initSet(testing.allocator, 5678);
978 defer b.deinit();
979
980 var c = try Managed.init(testing.allocator);
981 defer c.deinit();
982 try c.mulWrap(a.toConst(), b.toConst(), .unsigned, 17);
983
984 try testing.expect((try c.to(u17)) == 59836);
985}
986
987test "big.int mulWrap single-single signed" {
988 var a = try Managed.initSet(testing.allocator, 1234);
989 defer a.deinit();
990 var b = try Managed.initSet(testing.allocator, -5678);
991 defer b.deinit();
992
993 var c = try Managed.init(testing.allocator);
994 defer c.deinit();
995 try c.mulWrap(a.toConst(), b.toConst(), .signed, 17);
996
997 try testing.expect((try c.to(i17)) == -59836);
998}
999
1000test "big.int mulWrap multi-multi unsigned" {
1001 const op1 = 0x998888efefefefefefefef;
1002 const op2 = 0x333000abababababababab;
1003 var a = try Managed.initSet(testing.allocator, op1);
1004 defer a.deinit();
1005 var b = try Managed.initSet(testing.allocator, op2);
1006 defer b.deinit();
1007
1008 var c = try Managed.init(testing.allocator);
1009 defer c.deinit();
1010 try c.mulWrap(a.toConst(), b.toConst(), .unsigned, 65);
1011
1012 try testing.expect((try c.to(u256)) == (op1 * op2) & ((1 << 65) - 1));
1013}
1014
1015test "big.int mulWrap multi-multi signed" {
1016 var a = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb) - 1);
1017 defer a.deinit();
1018 var b = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb));
1019 defer b.deinit();
1020
1021 var c = try Managed.init(testing.allocator);
1022 defer c.deinit();
1023 try c.mulWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
1024
1025 try testing.expect((try c.to(SignedDoubleLimb)) == minInt(SignedDoubleLimb) + 2);
1026}
1027
1028test "big.int mulWrap large" {
1029 var a = try Managed.initCapacity(testing.allocator, 50);
1030 defer a.deinit();
1031 var b = try Managed.initCapacity(testing.allocator, 100);
1032 defer b.deinit();
1033 var c = try Managed.initCapacity(testing.allocator, 100);
1034 defer c.deinit();
1035
1036 // Generate a number that's large enough to cross the thresholds for the use
1037 // of subquadratic algorithms
1038 for (a.limbs) |*p| {
1039 p.* = std.math.maxInt(Limb);
1040 }
1041 a.setMetadata(true, 50);
1042
1043 const testbits = @bitSizeOf(Limb) * 64 + 45;
1044
1045 try b.mulWrap(a.toConst(), a.toConst(), .signed, testbits);
1046 try c.sqr(a.toConst());
1047 try c.truncate(c.toConst(), .signed, testbits);
1048
1049 try testing.expect(b.eq(c));
1050}
1051
7511052test "big.int div single-single no rem" {
7521053 var a = try Managed.initSet(testing.allocator, 50);
7531054 defer a.deinit();
......@@ -1280,6 +1581,135 @@ test "big.int div multi-multi fuzz case #2" {
12801581 try testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000"));
12811582}
12821583
1584test "big.int truncate single unsigned" {
1585 var a = try Managed.initSet(testing.allocator, maxInt(u47));
1586 defer a.deinit();
1587
1588 try a.truncate(a.toConst(), .unsigned, 17);
1589
1590 try testing.expect((try a.to(u17)) == maxInt(u17));
1591}
1592
1593test "big.int truncate single signed" {
1594 var a = try Managed.initSet(testing.allocator, 0x1_0000);
1595 defer a.deinit();
1596
1597 try a.truncate(a.toConst(), .signed, 17);
1598
1599 try testing.expect((try a.to(i17)) == minInt(i17));
1600}
1601
1602test "big.int truncate multi to single unsigned" {
1603 var a = try Managed.initSet(testing.allocator, (maxInt(Limb) + 1) | 0x1234_5678_9ABC_DEF0);
1604 defer a.deinit();
1605
1606 try a.truncate(a.toConst(), .unsigned, 27);
1607
1608 try testing.expect((try a.to(u27)) == 0x2BC_DEF0);
1609}
1610
1611test "big.int truncate multi to single signed" {
1612 var a = try Managed.initSet(testing.allocator, maxInt(Limb) << 10);
1613 defer a.deinit();
1614
1615 try a.truncate(a.toConst(), .signed, @bitSizeOf(i11));
1616
1617 try testing.expect((try a.to(i11)) == minInt(i11));
1618}
1619
1620test "big.int truncate multi to multi unsigned" {
1621 const bits = @typeInfo(SignedDoubleLimb).Int.bits;
1622 const Int = std.meta.Int(.unsigned, bits - 1);
1623
1624 var a = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb));
1625 defer a.deinit();
1626
1627 try a.truncate(a.toConst(), .unsigned, bits - 1);
1628
1629 try testing.expect((try a.to(Int)) == maxInt(Int));
1630}
1631
1632test "big.int truncate multi to multi signed" {
1633 var a = try Managed.initSet(testing.allocator, 3 << @bitSizeOf(Limb));
1634 defer a.deinit();
1635
1636 try a.truncate(a.toConst(), .signed, @bitSizeOf(Limb) + 1);
1637
1638 try testing.expect((try a.to(std.meta.Int(.signed, @bitSizeOf(Limb) + 1))) == -1 << @bitSizeOf(Limb));
1639}
1640
1641test "big.int truncate negative multi to single" {
1642 var a = try Managed.initSet(testing.allocator, -@as(SignedDoubleLimb, maxInt(Limb) + 1));
1643 defer a.deinit();
1644
1645 try a.truncate(a.toConst(), .signed, @bitSizeOf(i17));
1646
1647 try testing.expect((try a.to(i17)) == 0);
1648}
1649
1650test "big.int saturate single signed positive" {
1651 var a = try Managed.initSet(testing.allocator, 0xBBBB_BBBB);
1652 defer a.deinit();
1653
1654 try a.saturate(a.toConst(), .signed, 17);
1655
1656 try testing.expect((try a.to(i17)) == maxInt(i17));
1657}
1658
1659test "big.int saturate single signed negative" {
1660 var a = try Managed.initSet(testing.allocator, -1_234_567);
1661 defer a.deinit();
1662
1663 try a.saturate(a.toConst(), .signed, 17);
1664
1665 try testing.expect((try a.to(i17)) == minInt(i17));
1666}
1667
1668test "big.int saturate single signed" {
1669 var a = try Managed.initSet(testing.allocator, maxInt(i17) - 1);
1670 defer a.deinit();
1671
1672 try a.saturate(a.toConst(), .signed, 17);
1673
1674 try testing.expect((try a.to(i17)) == maxInt(i17) - 1);
1675}
1676
1677test "big.int saturate multi signed" {
1678 var a = try Managed.initSet(testing.allocator, maxInt(Limb) << @bitSizeOf(SignedDoubleLimb));
1679 defer a.deinit();
1680
1681 try a.saturate(a.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
1682
1683 try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
1684}
1685
1686test "big.int saturate single unsigned" {
1687 var a = try Managed.initSet(testing.allocator, 0xFEFE_FEFE);
1688 defer a.deinit();
1689
1690 try a.saturate(a.toConst(), .unsigned, 23);
1691
1692 try testing.expect((try a.to(u23)) == maxInt(u23));
1693}
1694
1695test "big.int saturate multi unsigned zero" {
1696 var a = try Managed.initSet(testing.allocator, -1);
1697 defer a.deinit();
1698
1699 try a.saturate(a.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
1700
1701 try testing.expect(a.eqZero());
1702}
1703
1704test "big.int saturate multi unsigned" {
1705 var a = try Managed.initSet(testing.allocator, maxInt(Limb) << @bitSizeOf(DoubleLimb));
1706 defer a.deinit();
1707
1708 try a.saturate(a.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
1709
1710 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb));
1711}
1712
12831713test "big.int shift-right single" {
12841714 var a = try Managed.initSet(testing.allocator, 0xffff0000);
12851715 defer a.deinit();
src/Sema.zig+1-1
......@@ -9017,7 +9017,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
90179017
90189018 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
90199019 if (val.isUndef()) return sema.addConstUndef(dest_ty);
9020 return sema.addConstant(dest_ty, try val.intTrunc(sema.arena, dest_info.bits));
9020 return sema.addConstant(dest_ty, try val.intTrunc(sema.arena, dest_info.signedness, dest_info.bits));
90219021 }
90229022
90239023 try sema.requireRuntimeBlock(block, src);
src/type.zig+4-10
......@@ -3101,9 +3101,8 @@ pub const Type = extern union {
31013101 return Value.Tag.int_i64.create(arena, n);
31023102 }
31033103
3104 var res = try std.math.big.int.Managed.initSet(arena, 1);
3105 try res.shiftLeft(res, info.bits - 1);
3106 res.negate();
3104 var res = try std.math.big.int.Managed.init(arena);
3105 try res.setTwosCompIntLimit(.min, info.signedness, info.bits);
31073106
31083107 const res_const = res.toConst();
31093108 if (res_const.positive) {
......@@ -3126,13 +3125,8 @@ pub const Type = extern union {
31263125 return Value.Tag.int_u64.create(arena, n);
31273126 }
31283127
3129 var res = try std.math.big.int.Managed.initSet(arena, 1);
3130 try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed));
3131 const one = std.math.big.int.Const{
3132 .limbs = &[_]std.math.big.Limb{1},
3133 .positive = true,
3134 };
3135 res.sub(res.toConst(), one) catch unreachable;
3128 var res = try std.math.big.int.Managed.init(arena);
3129 try res.setTwosCompIntLimit(.max, info.signedness, info.bits);
31363130
31373131 const res_const = res.toConst();
31383132 if (res_const.positive) {
src/value.zig+133-67
......@@ -1660,19 +1660,26 @@ pub const Value = extern union {
16601660 if (ty.isAnyFloat()) {
16611661 return floatAdd(lhs, rhs, ty, arena);
16621662 }
1663 const result = try intAdd(lhs, rhs, arena);
16641663
1665 const max = try ty.maxInt(arena, target);
1666 if (compare(result, .gt, max, ty)) {
1667 @panic("TODO comptime wrapping integer addition");
1668 }
1664 const info = ty.intInfo(target);
16691665
1670 const min = try ty.minInt(arena, target);
1671 if (compare(result, .lt, min, ty)) {
1672 @panic("TODO comptime wrapping integer addition");
1673 }
1666 var lhs_space: Value.BigIntSpace = undefined;
1667 var rhs_space: Value.BigIntSpace = undefined;
1668 const lhs_bigint = lhs.toBigInt(&lhs_space);
1669 const rhs_bigint = rhs.toBigInt(&rhs_space);
1670 const limbs = try arena.alloc(
1671 std.math.big.Limb,
1672 std.math.big.int.calcTwosCompLimbCount(info.bits),
1673 );
1674 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1675 result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1676 const result_limbs = result_bigint.limbs[0..result_bigint.len];
16741677
1675 return result;
1678 if (result_bigint.positive) {
1679 return Value.Tag.int_big_positive.create(arena, result_limbs);
1680 } else {
1681 return Value.Tag.int_big_negative.create(arena, result_limbs);
1682 }
16761683 }
16771684
16781685 /// Supports integers only; asserts neither operand is undefined.
......@@ -1686,19 +1693,25 @@ pub const Value = extern union {
16861693 assert(!lhs.isUndef());
16871694 assert(!rhs.isUndef());
16881695
1689 const result = try intAdd(lhs, rhs, arena);
1696 const info = ty.intInfo(target);
16901697
1691 const max = try ty.maxInt(arena, target);
1692 if (compare(result, .gt, max, ty)) {
1693 return max;
1694 }
1698 var lhs_space: Value.BigIntSpace = undefined;
1699 var rhs_space: Value.BigIntSpace = undefined;
1700 const lhs_bigint = lhs.toBigInt(&lhs_space);
1701 const rhs_bigint = rhs.toBigInt(&rhs_space);
1702 const limbs = try arena.alloc(
1703 std.math.big.Limb,
1704 std.math.big.int.calcTwosCompLimbCount(info.bits),
1705 );
1706 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1707 result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1708 const result_limbs = result_bigint.limbs[0..result_bigint.len];
16951709
1696 const min = try ty.minInt(arena, target);
1697 if (compare(result, .lt, min, ty)) {
1698 return min;
1710 if (result_bigint.positive) {
1711 return Value.Tag.int_big_positive.create(arena, result_limbs);
1712 } else {
1713 return Value.Tag.int_big_negative.create(arena, result_limbs);
16991714 }
1700
1701 return result;
17021715 }
17031716
17041717 /// Supports both floats and ints; handles undefined.
......@@ -1714,19 +1727,26 @@ pub const Value = extern union {
17141727 if (ty.isAnyFloat()) {
17151728 return floatSub(lhs, rhs, ty, arena);
17161729 }
1717 const result = try intSub(lhs, rhs, arena);
17181730
1719 const max = try ty.maxInt(arena, target);
1720 if (compare(result, .gt, max, ty)) {
1721 @panic("TODO comptime wrapping integer subtraction");
1722 }
1731 const info = ty.intInfo(target);
17231732
1724 const min = try ty.minInt(arena, target);
1725 if (compare(result, .lt, min, ty)) {
1726 @panic("TODO comptime wrapping integer subtraction");
1727 }
1733 var lhs_space: Value.BigIntSpace = undefined;
1734 var rhs_space: Value.BigIntSpace = undefined;
1735 const lhs_bigint = lhs.toBigInt(&lhs_space);
1736 const rhs_bigint = rhs.toBigInt(&rhs_space);
1737 const limbs = try arena.alloc(
1738 std.math.big.Limb,
1739 std.math.big.int.calcTwosCompLimbCount(info.bits),
1740 );
1741 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1742 result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1743 const result_limbs = result_bigint.limbs[0..result_bigint.len];
17281744
1729 return result;
1745 if (result_bigint.positive) {
1746 return Value.Tag.int_big_positive.create(arena, result_limbs);
1747 } else {
1748 return Value.Tag.int_big_negative.create(arena, result_limbs);
1749 }
17301750 }
17311751
17321752 /// Supports integers only; asserts neither operand is undefined.
......@@ -1740,19 +1760,25 @@ pub const Value = extern union {
17401760 assert(!lhs.isUndef());
17411761 assert(!rhs.isUndef());
17421762
1743 const result = try intSub(lhs, rhs, arena);
1763 const info = ty.intInfo(target);
17441764
1745 const max = try ty.maxInt(arena, target);
1746 if (compare(result, .gt, max, ty)) {
1747 return max;
1748 }
1765 var lhs_space: Value.BigIntSpace = undefined;
1766 var rhs_space: Value.BigIntSpace = undefined;
1767 const lhs_bigint = lhs.toBigInt(&lhs_space);
1768 const rhs_bigint = rhs.toBigInt(&rhs_space);
1769 const limbs = try arena.alloc(
1770 std.math.big.Limb,
1771 std.math.big.int.calcTwosCompLimbCount(info.bits),
1772 );
1773 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1774 result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1775 const result_limbs = result_bigint.limbs[0..result_bigint.len];
17491776
1750 const min = try ty.minInt(arena, target);
1751 if (compare(result, .lt, min, ty)) {
1752 return min;
1777 if (result_bigint.positive) {
1778 return Value.Tag.int_big_positive.create(arena, result_limbs);
1779 } else {
1780 return Value.Tag.int_big_negative.create(arena, result_limbs);
17531781 }
1754
1755 return result;
17561782 }
17571783
17581784 /// Supports both floats and ints; handles undefined.
......@@ -1768,19 +1794,31 @@ pub const Value = extern union {
17681794 if (ty.isAnyFloat()) {
17691795 return floatMul(lhs, rhs, ty, arena);
17701796 }
1771 const result = try intMul(lhs, rhs, arena);
17721797
1773 const max = try ty.maxInt(arena, target);
1774 if (compare(result, .gt, max, ty)) {
1775 @panic("TODO comptime wrapping integer multiplication");
1776 }
1798 const info = ty.intInfo(target);
17771799
1778 const min = try ty.minInt(arena, target);
1779 if (compare(result, .lt, min, ty)) {
1780 @panic("TODO comptime wrapping integer multiplication");
1781 }
1800 var lhs_space: Value.BigIntSpace = undefined;
1801 var rhs_space: Value.BigIntSpace = undefined;
1802 const lhs_bigint = lhs.toBigInt(&lhs_space);
1803 const rhs_bigint = rhs.toBigInt(&rhs_space);
1804 const limbs = try arena.alloc(
1805 std.math.big.Limb,
1806 std.math.big.int.calcTwosCompLimbCount(info.bits),
1807 );
1808 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1809 var limbs_buffer = try arena.alloc(
1810 std.math.big.Limb,
1811 std.math.big.int.calcMulWrapLimbsBufferLen(info.bits, lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
1812 );
1813 defer arena.free(limbs_buffer);
1814 result_bigint.mulWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits, limbs_buffer, arena);
1815 const result_limbs = result_bigint.limbs[0..result_bigint.len];
17821816
1783 return result;
1817 if (result_bigint.positive) {
1818 return Value.Tag.int_big_positive.create(arena, result_limbs);
1819 } else {
1820 return Value.Tag.int_big_negative.create(arena, result_limbs);
1821 }
17841822 }
17851823
17861824 /// Supports integers only; asserts neither operand is undefined.
......@@ -1794,19 +1832,35 @@ pub const Value = extern union {
17941832 assert(!lhs.isUndef());
17951833 assert(!rhs.isUndef());
17961834
1797 const result = try intMul(lhs, rhs, arena);
1835 const info = ty.intInfo(target);
17981836
1799 const max = try ty.maxInt(arena, target);
1800 if (compare(result, .gt, max, ty)) {
1801 return max;
1802 }
1837 var lhs_space: Value.BigIntSpace = undefined;
1838 var rhs_space: Value.BigIntSpace = undefined;
1839 const lhs_bigint = lhs.toBigInt(&lhs_space);
1840 const rhs_bigint = rhs.toBigInt(&rhs_space);
1841 const limbs = try arena.alloc(
1842 std.math.big.Limb,
1843 std.math.max(
1844 // For the saturate
1845 std.math.big.int.calcTwosCompLimbCount(info.bits),
1846 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
1847 ),
1848 );
1849 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1850 var limbs_buffer = try arena.alloc(
1851 std.math.big.Limb,
1852 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
1853 );
1854 defer arena.free(limbs_buffer);
1855 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
1856 result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
1857 const result_limbs = result_bigint.limbs[0..result_bigint.len];
18031858
1804 const min = try ty.minInt(arena, target);
1805 if (compare(result, .lt, min, ty)) {
1806 return min;
1859 if (result_bigint.positive) {
1860 return Value.Tag.int_big_positive.create(arena, result_limbs);
1861 } else {
1862 return Value.Tag.int_big_negative.create(arena, result_limbs);
18071863 }
1808
1809 return result;
18101864 }
18111865
18121866 /// Supports both floats and ints; handles undefined.
......@@ -2118,7 +2172,7 @@ pub const Value = extern union {
21182172 const rhs_bigint = rhs.toBigInt(&rhs_space);
21192173 const limbs = try allocator.alloc(
21202174 std.math.big.Limb,
2121 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
2175 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
21222176 );
21232177 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
21242178 var limbs_buffer = try allocator.alloc(
......@@ -2136,12 +2190,24 @@ pub const Value = extern union {
21362190 }
21372191 }
21382192
2139 pub fn intTrunc(val: Value, arena: *Allocator, bits: u16) !Value {
2140 const x = val.toUnsignedInt(); // TODO: implement comptime truncate on big ints
2141 if (bits == 64) return val;
2142 const mask = (@as(u64, 1) << @intCast(u6, bits)) - 1;
2143 const truncated = x & mask;
2144 return Tag.int_u64.create(arena, truncated);
2193 pub fn intTrunc(val: Value, allocator: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Value {
2194 var val_space: Value.BigIntSpace = undefined;
2195 const val_bigint = val.toBigInt(&val_space);
2196
2197 const limbs = try allocator.alloc(
2198 std.math.big.Limb,
2199 std.math.big.int.calcTwosCompLimbCount(bits),
2200 );
2201 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2202
2203 result_bigint.truncate(val_bigint, signedness, bits);
2204 const result_limbs = result_bigint.limbs[0..result_bigint.len];
2205
2206 if (result_bigint.positive) {
2207 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2208 } else {
2209 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2210 }
21452211 }
21462212
21472213 pub fn shl(lhs: Value, rhs: Value, allocator: *Allocator) !Value {