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 {
300300 return add(r, a, operand);
301301 }
302302
303 /// r = a + b
304 ///
303 /// Base implementation for addition. Adds `max(a.limbs.len, b.limbs.len)` elements from a and b,
304 /// and returns whether any overflow occured.
305305 /// r, a and b may be aliases.
306306 ///
307 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
308 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`.
309 pub fn add(r: *Mutable, a: Const, b: Const) void {
307 /// Asserts r has enough elements to hold the result. The upper bound is `max(a.limbs.len, b.limbs.len)`.
308 fn addCarry(r: *Mutable, a: Const, b: Const) bool {
310309 if (a.eqZero()) {
311310 r.copy(b);
312 return;
311 return false;
313312 } else if (b.eqZero()) {
314313 r.copy(a);
315 return;
316 }
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) {
314 return false;
315 } else if (a.positive != b.positive) {
329316 if (a.positive) {
330317 // (a) + (-b) => a - b
331 r.sub(a, b.abs());
318 return r.subCarry(a, b.abs());
332319 } else {
333320 // (-a) + (b) => b - a
334 r.sub(b, a.abs());
321 return r.subCarry(b, a.abs());
335322 }
336323 } else {
324 r.positive = a.positive;
337325 if (a.limbs.len >= b.limbs.len) {
338 lladd(r.limbs[0..], a.limbs, b.limbs);
339 r.normalize(a.limbs.len + 1);
326 const c = lladdcarry(r.limbs, a.limbs, b.limbs);
327 r.normalize(a.limbs.len);
328 return c != 0;
340329 } else {
341 lladd(r.limbs[0..], b.limbs, a.limbs);
342 r.normalize(b.limbs.len + 1);
330 const c = lladdcarry(r.limbs, b.limbs, a.limbs);
331 r.normalize(b.limbs.len);
332 return c != 0;
343333 }
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`.
346352 }
347353 }
348354
......@@ -354,37 +360,82 @@ pub const Mutable = struct {
354360 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
355361 const req_limbs = calcTwosCompLimbCount(bit_count);
356362
357 // We can ignore the upper bits here, those results will be discarded anyway.
358 const a_limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)];
359 const b_limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)];
363 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
364 // if an overflow occured.
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 {
361395 if (a.eqZero()) {
362396 r.copy(b);
397 r.positive = !b.positive;
398 return false;
363399 } else if (b.eqZero()) {
364400 r.copy(a);
365 } else if (a.positive != b.positive) {
401 return false;
402 } if (a.positive != b.positive) {
366403 if (a.positive) {
367 // (a) + (-b) => a - b
368 r.subWrap(a, b.abs(), signedness, bit_count);
404 // (a) - (-b) => a + b
405 return r.addCarry(a, b.abs());
369406 } else {
370 // (-a) + (b) => b - a
371 r.subWrap(b, a.abs(), signedness, bit_count);
407 // (-a) - (b) => -a + -b
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;
372423 }
373 // Don't need to truncate, subWrap does that for us.
374 return;
375424 } else {
376 if (a_limbs.len >= b_limbs.len) {
377 _ = lladdcarry(r.limbs, a_limbs, b_limbs);
378 r.normalize(a_limbs.len);
425 if (a.order(b) == .lt) {
426 // (-a) - (-b) => -(a - b)
427 const c = llsubcarry(r.limbs, a.limbs, b.limbs);
428 r.normalize(a.limbs.len);
429 r.positive = false;
430 return c != 0;
379431 } else {
380 _ = lladdcarry(r.limbs, b_limbs, b_limbs);
381 r.normalize(b_limbs.len);
432 // (-a) - (-b) => --b + -a => b - a
433 const c = llsubcarry(r.limbs, b.limbs, a.limbs);
434 r.normalize(b.limbs.len);
435 r.positive = true;
436 return c != 0;
382437 }
383
384 r.positive = a.positive;
385438 }
386
387 r.truncate(r.toConst(), signedness, bit_count);
388439 }
389440
390441 /// r = a - b
......@@ -394,39 +445,14 @@ pub const Mutable = struct {
394445 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
395446 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.
396447 pub fn sub(r: *Mutable, a: Const, b: Const) void {
397 if (a.positive != b.positive) {
398 if (a.positive) {
399 // (a) - (-b) => a + b
400 r.add(a, b.abs());
401 } else {
402 // (-a) - (b) => -(a + b)
403 r.add(a.abs(), b);
404 r.positive = false;
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 }
448 if (r.subCarry(a, b)) {
449 // Fix up the result. Note that addCarry normalizes by a.limbs.len or b.limbs.len,
450 // so we need to set the length here.
451 const msl = math.max(a.limbs.len, b.limbs.len);
452 // `addCarry` normalizes by `msl`, so we need to fix up the result manually here.
453 // Note, the fact that it normalized means that the intermediary limbs are zero here.
454 r.len = msl + 1;
455 r.limbs[msl] = 1; // If this panics, there wasn't enough space in `r`.
430456 }
431457 }
432458
......@@ -438,45 +464,26 @@ pub const Mutable = struct {
438464 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
439465 const req_limbs = calcTwosCompLimbCount(bit_count);
440466
441 // We can ignore the upper bits here, those results will be discarded anyway.
442 // We also don't need to mind order here. Again, overflow is ignored here.
443 const a_limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)];
444 const b_limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)];
467 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
468 // if an overflow occured.
469 const x = Const{
470 .positive = a.positive,
471 .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)],
472 };
445473
446 if (a.positive != b.positive) {
447 if (a.positive) {
448 // (a) - (-b) => a + b
449 r.addWrap(a, b.abs(), signedness, bit_count);
450 } else {
451 // (-a) - (b) => -a + -b
452 // Note, we don't do -(a + b) here to avoid a second truncate.
453 r.addWrap(a, b.negate(), signedness, bit_count);
454 }
455 // Don't need to truncate, addWrap does that for us.
456 return;
457 } else if (a.positive) {
458 if (a_limbs.len >= b_limbs.len) {
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;
474 const y = Const{
475 .positive = b.positive,
476 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
477 };
478
479 if (r.subCarry(x, y)) {
480 // There are two possibilities here:
481 // - We overflowed req_limbs. In this case, the carry is ignored.
482 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
483 const msl = math.max(a.limbs.len, b.limbs.len);
484 if (msl < req_limbs) {
485 r.limbs[msl] = 1;
486 r.len = req_limbs;
480487 }
481488 }
482489