| ... | @@ -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 | } |
| 302 | | 302 | |
| 303 | /// r = a + b | 303 | /// 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 by | 307 | /// 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 - b | 317 | // (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 - a | 320 | // (-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 | } |
| 344 | | 336 | |
| 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 | } |
| 348 | | 354 | |
| ... | @@ -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); |
| 356 | | 362 | |
| 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 | }; |
| 360 | | 374 | |
| | 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 - b | 404 | // (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 - a | 407 | // (-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 | } |
| 389 | | 440 | |
| 390 | /// r = a - b | 441 | /// 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 by | 445 | /// 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 + b | 450 | // 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 | } |
| 432 | | 458 | |
| ... | @@ -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); |
| 440 | | 466 | |
| 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 | }; |
| 445 | | 473 | |
| 446 | if (a.positive != b.positive) { | 474 | const y = Const{ |
| 447 | if (a.positive) { | 475 | .positive = b.positive, |
| 448 | // (a) - (-b) => a + b | 476 | .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 + -b | 479 | 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 | } |
| 482 | | 489 | |