authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-09-29 00:11:42+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-04 11:25:29+02:00
logdc1f69854504c4dd7fbd18564c4a6811e3cc87e3
tree276369eafca5517f63d5b33a435f968d1ce14df4
parenta36ef84deb1d52e513fbf311c71eaa2a9d48de00

big ints: unify add/sub with their wrapping variants


1 files changed, 124 insertions(+), 117 deletions(-)

lib/std/math/big/int.zig+124-117
...@@ -300,49 +300,55 @@ pub const Mutable = struct {...@@ -300,49 +300,55 @@ pub const Mutable = struct {
300 return add(r, a, operand);300 return add(r, a, operand);
301 }301 }
302302
303 /// r = a + b303 /// Base implementation for addition. Adds `max(a.limbs.len, b.limbs.len)` elements from a and b,
304 ///304 /// and returns whether any overflow occured.
305 /// r, a and b may be aliases.305 /// r, a and b may be aliases.
306 ///306 ///
307 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by307 /// Asserts r has enough elements to hold the result. The upper bound is `max(a.limbs.len, b.limbs.len)`.
308 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`.308 fn addCarry(r: *Mutable, a: Const, b: Const) bool {
309 pub fn add(r: *Mutable, a: Const, b: Const) void {
310 if (a.eqZero()) {309 if (a.eqZero()) {
311 r.copy(b);310 r.copy(b);
312 return;311 return false;
313 } else if (b.eqZero()) {312 } else if (b.eqZero()) {
314 r.copy(a);313 r.copy(a);
315 return;314 return false;
316 }315 } else if (a.positive != b.positive) {
317
318 if (a.limbs.len == 1 and b.limbs.len == 1 and a.positive == b.positive) {
319 var o: Limb = undefined;
320 if (!@addWithOverflow(Limb, a.limbs[0], b.limbs[0], &o)) {
321 r.limbs[0] = o;
322 r.len = 1;
323 r.positive = a.positive;
324 return;
325 }
326 }
327
328 if (a.positive != b.positive) {
329 if (a.positive) {316 if (a.positive) {
330 // (a) + (-b) => a - b317 // (a) + (-b) => a - b
331 r.sub(a, b.abs());318 return r.subCarry(a, b.abs());
332 } else {319 } else {
333 // (-a) + (b) => b - a320 // (-a) + (b) => b - a
334 r.sub(b, a.abs());321 return r.subCarry(b, a.abs());
335 }322 }
336 } else {323 } else {
324 r.positive = a.positive;
337 if (a.limbs.len >= b.limbs.len) {325 if (a.limbs.len >= b.limbs.len) {
338 lladd(r.limbs[0..], a.limbs, b.limbs);326 const c = lladdcarry(r.limbs, a.limbs, b.limbs);
339 r.normalize(a.limbs.len + 1);327 r.normalize(a.limbs.len);
328 return c != 0;
340 } else {329 } else {
341 lladd(r.limbs[0..], b.limbs, a.limbs);330 const c = lladdcarry(r.limbs, b.limbs, a.limbs);
342 r.normalize(b.limbs.len + 1);331 r.normalize(b.limbs.len);
332 return c != 0;
343 }333 }
334 }
335 }
344336
345 r.positive = a.positive;337 /// r = a + b
338 ///
339 /// r, a and b may be aliases.
340 ///
341 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
342 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`.
343 pub fn add(r: *Mutable, a: Const, b: Const) void {
344 if (r.addCarry(a, b)) {
345 // Fix up the result. Note that addCarry normalizes by a.limbs.len or b.limbs.len,
346 // so we need to set the length here.
347 const msl = math.max(a.limbs.len, b.limbs.len);
348 // `[add|sub]Carry` normalizes by `msl`, so we need to fix up the result manually here.
349 // Note, the fact that it normalized means that the intermediary limbs are zero here.
350 r.len = msl + 1;
351 r.limbs[msl] = 1; // If this panics, there wasn't enough space in `r`.
346 }352 }
347 }353 }
348354
...@@ -354,37 +360,82 @@ pub const Mutable = struct {...@@ -354,37 +360,82 @@ pub const Mutable = struct {
354 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {360 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
355 const req_limbs = calcTwosCompLimbCount(bit_count);361 const req_limbs = calcTwosCompLimbCount(bit_count);
356362
357 // We can ignore the upper bits here, those results will be discarded anyway.363 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
358 const a_limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)];364 // if an overflow occured.
359 const b_limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)];365 const x = Const{
366 .positive = a.positive,
367 .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)],
368 };
369
370 const y = Const{
371 .positive = b.positive,
372 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
373 };
360374
375 if (r.addCarry(x, y)) {
376 // There are two possibilities here:
377 // - We overflowed req_limbs. In this case, the carry is ignored.
378 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
379 const msl = math.max(a.limbs.len, b.limbs.len);
380 if (msl < req_limbs) {
381 r.limbs[msl] = 1;
382 r.len = req_limbs;
383 }
384 }
385
386 r.truncate(r.toConst(), signedness, bit_count);
387 }
388
389 /// Base implementation for subtraction. Subtracts `max(a.limbs.len, b.limbs.len)` elements from a and b,
390 /// and returns whether any overflow occured.
391 /// r, a and b may be aliases.
392 ///
393 /// Asserts r has enough elements to hold the result. The upper bound is `max(a.limbs.len, b.limbs.len)`.
394 fn subCarry(r: *Mutable, a: Const, b: Const) bool {
361 if (a.eqZero()) {395 if (a.eqZero()) {
362 r.copy(b);396 r.copy(b);
397 r.positive = !b.positive;
398 return false;
363 } else if (b.eqZero()) {399 } else if (b.eqZero()) {
364 r.copy(a);400 r.copy(a);
365 } else if (a.positive != b.positive) {401 return false;
402 } if (a.positive != b.positive) {
366 if (a.positive) {403 if (a.positive) {
367 // (a) + (-b) => a - b404 // (a) - (-b) => a + b
368 r.subWrap(a, b.abs(), signedness, bit_count);405 return r.addCarry(a, b.abs());
369 } else {406 } else {
370 // (-a) + (b) => b - a407 // (-a) - (b) => -a + -b
371 r.subWrap(b, a.abs(), signedness, bit_count);408 return r.addCarry(a, b.negate());
409 }
410 } else if (a.positive) {
411 if (a.order(b) != .lt) {
412 // (a) - (b) => a - b
413 const c = llsubcarry(r.limbs, a.limbs, b.limbs);
414 r.normalize(a.limbs.len);
415 r.positive = true;
416 return c != 0;
417 } else {
418 // (a) - (b) => -b + a => -(b - a)
419 const c = llsubcarry(r.limbs, b.limbs, a.limbs);
420 r.normalize(b.limbs.len);
421 r.positive = false;
422 return c != 0;
372 }423 }
373 // Don't need to truncate, subWrap does that for us.
374 return;
375 } else {424 } else {
376 if (a_limbs.len >= b_limbs.len) {425 if (a.order(b) == .lt) {
377 _ = lladdcarry(r.limbs, a_limbs, b_limbs);426 // (-a) - (-b) => -(a - b)
378 r.normalize(a_limbs.len);427 const c = llsubcarry(r.limbs, a.limbs, b.limbs);
428 r.normalize(a.limbs.len);
429 r.positive = false;
430 return c != 0;
379 } else {431 } else {
380 _ = lladdcarry(r.limbs, b_limbs, b_limbs);432 // (-a) - (-b) => --b + -a => b - a
381 r.normalize(b_limbs.len);433 const c = llsubcarry(r.limbs, b.limbs, a.limbs);
434 r.normalize(b.limbs.len);
435 r.positive = true;
436 return c != 0;
382 }437 }
383
384 r.positive = a.positive;
385 }438 }
386
387 r.truncate(r.toConst(), signedness, bit_count);
388 }439 }
389440
390 /// r = a - b441 /// r = a - b
...@@ -394,39 +445,14 @@ pub const Mutable = struct {...@@ -394,39 +445,14 @@ pub const Mutable = struct {
394 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by445 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
395 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.446 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.
396 pub fn sub(r: *Mutable, a: Const, b: Const) void {447 pub fn sub(r: *Mutable, a: Const, b: Const) void {
397 if (a.positive != b.positive) {448 if (r.subCarry(a, b)) {
398 if (a.positive) {449 // Fix up the result. Note that addCarry normalizes by a.limbs.len or b.limbs.len,
399 // (a) - (-b) => a + b450 // so we need to set the length here.
400 r.add(a, b.abs());451 const msl = math.max(a.limbs.len, b.limbs.len);
401 } else {452 // `addCarry` normalizes by `msl`, so we need to fix up the result manually here.
402 // (-a) - (b) => -(a + b)453 // Note, the fact that it normalized means that the intermediary limbs are zero here.
403 r.add(a.abs(), b);454 r.len = msl + 1;
404 r.positive = false;455 r.limbs[msl] = 1; // If this panics, there wasn't enough space in `r`.
405 }
406 } else {
407 if (a.positive) {
408 // (a) - (b) => a - b
409 if (a.order(b) != .lt) {
410 llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
411 r.normalize(a.limbs.len);
412 r.positive = true;
413 } else {
414 llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
415 r.normalize(b.limbs.len);
416 r.positive = false;
417 }
418 } else {
419 // (-a) - (-b) => -(a - b)
420 if (a.order(b) == .lt) {
421 llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
422 r.normalize(a.limbs.len);
423 r.positive = false;
424 } else {
425 llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
426 r.normalize(b.limbs.len);
427 r.positive = true;
428 }
429 }
430 }456 }
431 }457 }
432458
...@@ -438,45 +464,26 @@ pub const Mutable = struct {...@@ -438,45 +464,26 @@ pub const Mutable = struct {
438 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {464 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
439 const req_limbs = calcTwosCompLimbCount(bit_count);465 const req_limbs = calcTwosCompLimbCount(bit_count);
440466
441 // We can ignore the upper bits here, those results will be discarded anyway.467 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
442 // We also don't need to mind order here. Again, overflow is ignored here.468 // if an overflow occured.
443 const a_limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)];469 const x = Const{
444 const b_limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)];470 .positive = a.positive,
471 .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)],
472 };
445473
446 if (a.positive != b.positive) {474 const y = Const{
447 if (a.positive) {475 .positive = b.positive,
448 // (a) - (-b) => a + b476 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
449 r.addWrap(a, b.abs(), signedness, bit_count);477 };
450 } else {478
451 // (-a) - (b) => -a + -b479 if (r.subCarry(x, y)) {
452 // Note, we don't do -(a + b) here to avoid a second truncate.480 // There are two possibilities here:
453 r.addWrap(a, b.negate(), signedness, bit_count);481 // - We overflowed req_limbs. In this case, the carry is ignored.
454 }482 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
455 // Don't need to truncate, addWrap does that for us.483 const msl = math.max(a.limbs.len, b.limbs.len);
456 return;484 if (msl < req_limbs) {
457 } else if (a.positive) {485 r.limbs[msl] = 1;
458 if (a_limbs.len >= b_limbs.len) {486 r.len = req_limbs;
459 // (a) - (b) => a - b
460 _ = llsubcarry(r.limbs, a_limbs, b_limbs);
461 r.normalize(a_limbs.len);
462 r.positive = true;
463 } else {
464 // (a) - (b) => -b + a => -(b - a)
465 _ = llsubcarry(r.limbs, b_limbs, a_limbs);
466 r.normalize(b_limbs.len);
467 r.positive = false;
468 }
469 } else {
470 if (a_limbs.len >= b_limbs.len) {
471 // (-a) - (-b) => -(a - b)
472 _ = llsubcarry(r.limbs, a_limbs, b_limbs);
473 r.normalize(a_limbs.len);
474 r.positive = false;
475 } else {
476 // (-a) - (-b) => --b + -a => b - a
477 _ = llsubcarry(r.limbs, b_limbs, a_limbs);
478 r.normalize(b_limbs.len);
479 r.positive = true;
480 }487 }
481 }488 }
482489