authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-01 14:28:09+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-04 11:25:29+02:00
logbb53f4f15a76fd306c8ec2f51369d7ffcdf4c0ca
treeeac53cf7b042e55357b1a8869dc5730c97107f69
parent16991f920b4af8432ff97f83f02c3a4e614f480f

big ints: implement normal/wrapping/saturating subtraction in terms of addition


1 files changed, 17 insertions(+), 36 deletions(-)

lib/std/math/big/int.zig+17-36
...@@ -566,15 +566,7 @@ pub const Mutable = struct {...@@ -566,15 +566,7 @@ pub const Mutable = struct {
566 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by566 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
567 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.567 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.
568 pub fn sub(r: *Mutable, a: Const, b: Const) void {568 pub fn sub(r: *Mutable, a: Const, b: Const) void {
569 if (r.subCarry(a, b)) {569 r.add(a, b.negate());
570 // Fix up the result. Note that addCarry normalizes by a.limbs.len or b.limbs.len,
571 // so we need to set the length here.
572 const msl = math.max(a.limbs.len, b.limbs.len);
573 // `addCarry` normalizes by `msl`, so we need to fix up the result manually here.
574 // Note, the fact that it normalized means that the intermediary limbs are zero here.
575 r.len = msl + 1;
576 r.limbs[msl] = 1; // If this panics, there wasn't enough space in `r`.
577 }
578 }570 }
579571
580 /// r = a - b with 2s-complement wrapping semantics.572 /// r = a - b with 2s-complement wrapping semantics.
...@@ -583,34 +575,16 @@ pub const Mutable = struct {...@@ -583,34 +575,16 @@ pub const Mutable = struct {
583 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by575 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
584 /// r is `calcTwosCompLimbCount(bit_count)`.576 /// r is `calcTwosCompLimbCount(bit_count)`.
585 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {577 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
586 const req_limbs = calcTwosCompLimbCount(bit_count);578 r.addWrap(a, b.negate(), signedness, bit_count);
587579 }
588 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
589 // if an overflow occured.
590 const x = Const{
591 .positive = a.positive,
592 .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)],
593 };
594
595 const y = Const{
596 .positive = b.positive,
597 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
598 };
599
600 if (r.subCarry(x, y)) {
601 // There are two possibilities here:
602 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
603 // truncate anyway.
604 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
605 // Note: after this we still might need to wrap.
606 const msl = math.max(a.limbs.len, b.limbs.len);
607 if (msl < req_limbs) {
608 r.limbs[msl] = 1;
609 r.len = req_limbs;
610 }
611 }
612580
613 r.truncate(r.toConst(), signedness, bit_count);581 /// r = a - b with 2s-complement saturating semantics.
582 /// r, a and b may be aliases.
583 ///
584 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by
585 /// r is `calcTwosCompLimbCount(bit_count)`.
586 pub fn subSat(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
587 r.addSat(a, b.negate(), signedness, bit_count);
614 }588 }
615589
616 /// rma = a * b590 /// rma = a * b
...@@ -2140,6 +2114,13 @@ pub const Managed = struct {...@@ -2140,6 +2114,13 @@ pub const Managed = struct {
2140 r.setMetadata(m.positive, m.len);2114 r.setMetadata(m.positive, m.len);
2141 }2115 }
21422116
2117 pub fn subSat(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {
2118 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
2119 var m = r.toMutable();
2120 m.subSat(a, b, signedness, bit_count);
2121 r.setMetadata(m.positive, m.len);
2122 }
2123
2143 /// rma = a * b2124 /// rma = a * b
2144 ///2125 ///
2145 /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.2126 /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.