| ... | @@ -552,36 +552,54 @@ pub const Mutable = struct { | ... | @@ -552,36 +552,54 @@ pub const Mutable = struct { |
| 552 | r.positive = a.positive; | 552 | r.positive = a.positive; |
| 553 | } | 553 | } |
| 554 | | 554 | |
| 555 | /// r = a | b | 555 | /// r = a | b under 2s complement semantics. |
| 556 | /// r may alias with a or b. | 556 | /// r may alias with a or b. |
| 557 | /// | 557 | /// |
| 558 | /// a and b are zero-extended to the longer of a or b. | 558 | /// a and b are zero-extended to the longer of a or b. |
| 559 | /// | 559 | /// |
| 560 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`. | 560 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`. |
| 561 | pub fn bitOr(r: *Mutable, a: Const, b: Const) void { | 561 | pub fn bitOr(r: *Mutable, a: Const, b: Const) void { |
| 562 | if (a.limbs.len > b.limbs.len) { | 562 | // Trivial cases, llsignedor does not support zero. |
| 563 | llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | 563 | if (a.eqZero()) { |
| 564 | r.len = a.limbs.len; | 564 | r.copy(b); |
| | 565 | return; |
| | 566 | } else if (b.eqZero()) { |
| | 567 | r.copy(a); |
| | 568 | return; |
| | 569 | } |
| | 570 | |
| | 571 | if (a.limbs.len >= b.limbs.len) { |
| | 572 | r.positive = llsignedor(r.limbs, a.limbs, a.positive, b.limbs, b.positive); |
| | 573 | r.normalize(if (b.positive) a.limbs.len else b.limbs.len); |
| 565 | } else { | 574 | } else { |
| 566 | llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | 575 | r.positive = llsignedor(r.limbs, b.limbs, b.positive, a.limbs, a.positive); |
| 567 | r.len = b.limbs.len; | 576 | r.normalize(if (a.positive) b.limbs.len else a.limbs.len); |
| 568 | } | 577 | } |
| 569 | r.positive = a.positive or b.positive; | | |
| 570 | } | 578 | } |
| 571 | | 579 | |
| 572 | /// r = a & b | 580 | /// r = a & b under 2s complement semantics. |
| 573 | /// r may alias with a or b. | 581 | /// r may alias with a or b. |
| 574 | /// | 582 | /// |
| 575 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.min(a.limbs.len, b.limbs.len)`. | 583 | /// Asserts that r has enough limbs to store the result. |
| | 584 | /// If a or b is positive, the upper bound is `math.min(a.limbs.len, b.limbs.len)`. |
| | 585 | /// If a and b are negative, the upper bound is `math.max(a.limbs.len, b.limbs.len) + 1`. |
| 576 | pub fn bitAnd(r: *Mutable, a: Const, b: Const) void { | 586 | pub fn bitAnd(r: *Mutable, a: Const, b: Const) void { |
| 577 | if (a.limbs.len > b.limbs.len) { | 587 | // Trivial cases, llsignedand does not support zero. |
| 578 | lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | 588 | if (a.eqZero()) { |
| 579 | r.normalize(b.limbs.len); | 589 | r.copy(a); |
| | 590 | return; |
| | 591 | } else if (b.eqZero()) { |
| | 592 | r.copy(b); |
| | 593 | return; |
| | 594 | } |
| | 595 | |
| | 596 | if (a.limbs.len >= b.limbs.len) { |
| | 597 | r.positive = llsignedand(r.limbs, a.limbs, a.positive, b.limbs, b.positive); |
| | 598 | r.normalize(if (a.positive or b.positive) b.limbs.len else a.limbs.len + 1); |
| 580 | } else { | 599 | } else { |
| 581 | lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | 600 | r.positive = llsignedand(r.limbs, b.limbs, b.positive, a.limbs, a.positive); |
| 582 | r.normalize(a.limbs.len); | 601 | r.normalize(if (a.positive or b.positive) a.limbs.len else b.limbs.len + 1); |
| 583 | } | 602 | } |
| 584 | r.positive = a.positive and b.positive; | | |
| 585 | } | 603 | } |
| 586 | | 604 | |
| 587 | /// r = a ^ b under 2s complement semantics. | 605 | /// r = a ^ b under 2s complement semantics. |
| ... | @@ -1847,7 +1865,11 @@ pub const Managed = struct { | ... | @@ -1847,7 +1865,11 @@ pub const Managed = struct { |
| 1847 | | 1865 | |
| 1848 | /// r = a & b | 1866 | /// r = a & b |
| 1849 | pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void { | 1867 | pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void { |
| 1850 | try r.ensureCapacity(math.min(a.len(), b.len())); | 1868 | const cap = if (a.isPositive() or b.isPositive()) |
| | 1869 | math.min(a.len(), b.len()) |
| | 1870 | else |
| | 1871 | math.max(a.len(), b.len()) + 1; |
| | 1872 | try r.ensureCapacity(cap); |
| 1851 | var m = r.toMutable(); | 1873 | var m = r.toMutable(); |
| 1852 | m.bitAnd(a.toConst(), b.toConst()); | 1874 | m.bitAnd(a.toConst(), b.toConst()); |
| 1853 | r.setMetadata(m.positive, m.len); | 1875 | r.setMetadata(m.positive, m.len); |
| ... | @@ -2236,28 +2258,236 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { | ... | @@ -2236,28 +2258,236 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { |
| 2236 | } | 2258 | } |
| 2237 | } | 2259 | } |
| 2238 | | 2260 | |
| 2239 | fn llor(r: []Limb, a: []const Limb, b: []const Limb) void { | 2261 | // r = a | b with 2s complement semantics. |
| | 2262 | // r may alias. |
| | 2263 | // a and b must not be 0. |
| | 2264 | // Returns `true` when the result is positive. |
| | 2265 | // When b is positive, r requires at least `a.len` limbs of storage. |
| | 2266 | // When b is negative, r requires at least `b.len` limbs of storage. |
| | 2267 | fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { |
| 2240 | @setRuntimeSafety(debug_safety); | 2268 | @setRuntimeSafety(debug_safety); |
| 2241 | assert(r.len >= a.len); | 2269 | assert(r.len >= a.len); |
| 2242 | assert(a.len >= b.len); | 2270 | assert(a.len >= b.len); |
| 2243 | | 2271 | |
| 2244 | var i: usize = 0; | 2272 | if (a_positive and b_positive) { |
| 2245 | while (i < b.len) : (i += 1) { | 2273 | // Trivial case, result is positive. |
| 2246 | r[i] = a[i] | b[i]; | 2274 | var i: usize = 0; |
| 2247 | } | 2275 | while (i < b.len) : (i += 1) { |
| 2248 | while (i < a.len) : (i += 1) { | 2276 | r[i] = a[i] | b[i]; |
| 2249 | r[i] = a[i]; | 2277 | } |
| | 2278 | while (i < a.len) : (i += 1) { |
| | 2279 | r[i] = a[i]; |
| | 2280 | } |
| | 2281 | |
| | 2282 | return true; |
| | 2283 | } else if (!a_positive and b_positive) { |
| | 2284 | // Result is negative. |
| | 2285 | // r = (--a) | b |
| | 2286 | // = ~(-a - 1) | b |
| | 2287 | // = ~(-a - 1) | ~~b |
| | 2288 | // = ~((-a - 1) & ~b) |
| | 2289 | // = -(((-a - 1) & ~b) + 1) |
| | 2290 | |
| | 2291 | var i: usize = 0; |
| | 2292 | var a_borrow: u1 = 1; |
| | 2293 | var r_carry: u1 = 1; |
| | 2294 | |
| | 2295 | while (i < b.len) : (i += 1) { |
| | 2296 | var a_limb: Limb = undefined; |
| | 2297 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); |
| | 2298 | |
| | 2299 | r[i] = a_limb & ~b[i]; |
| | 2300 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| | 2301 | } |
| | 2302 | |
| | 2303 | // In order for r_carry to be nonzero at this point, ~b[i] would need to be |
| | 2304 | // all ones, which would require b[i] to be zero. This cannot be when |
| | 2305 | // b is normalized, so there cannot be a carry here. |
| | 2306 | // Also, x & ~b can only clear bits, so (x & ~b) <= x, meaning (-a - 1) + 1 never overflows. |
| | 2307 | assert(r_carry == 0); |
| | 2308 | |
| | 2309 | // With b = 0, we get (-a - 1) & ~0 = -a - 1. |
| | 2310 | // Note, if a_borrow is zero we do not need to compute anything for |
| | 2311 | // the higher limbs so we can early return here. |
| | 2312 | while (i < a.len and a_borrow == 1) : (i += 1) { |
| | 2313 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); |
| | 2314 | } |
| | 2315 | |
| | 2316 | assert(a_borrow == 0); // a was 0. |
| | 2317 | |
| | 2318 | return false; |
| | 2319 | } else if (a_positive and !b_positive) { |
| | 2320 | // Result is negative. |
| | 2321 | // r = a | (--b) |
| | 2322 | // = a | ~(-b - 1) |
| | 2323 | // = ~~a | ~(-b - 1) |
| | 2324 | // = ~(~a & (-b - 1)) |
| | 2325 | // = -((~a & (-b - 1)) + 1) |
| | 2326 | |
| | 2327 | var i: usize = 0; |
| | 2328 | var b_borrow: u1 = 1; |
| | 2329 | var r_carry: u1 = 1; |
| | 2330 | |
| | 2331 | while (i < b.len) : (i += 1) { |
| | 2332 | var b_limb: Limb = undefined; |
| | 2333 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); |
| | 2334 | |
| | 2335 | r[i] = ~a[i] & b_limb; |
| | 2336 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| | 2337 | } |
| | 2338 | |
| | 2339 | // b is at least 1, so this should never underflow. |
| | 2340 | assert(b_borrow == 0); // b was 0 |
| | 2341 | |
| | 2342 | // x & ~a can only clear bits, so (x & ~a) <= x, meaning (-b - 1) + 1 never overflows. |
| | 2343 | assert(r_carry == 0); |
| | 2344 | |
| | 2345 | // With b = 0 and b_borrow = 0, we get ~a & (-0 - 0) = ~a & 0 = 0. |
| | 2346 | // Omit setting the upper bytes, just deal with those when calling llsignedor. |
| | 2347 | |
| | 2348 | return false; |
| | 2349 | } else { |
| | 2350 | // Result is negative. |
| | 2351 | // r = (--a) | (--b) |
| | 2352 | // = ~(-a - 1) | ~(-b - 1) |
| | 2353 | // = ~((-a - 1) & (-b - 1)) |
| | 2354 | // = -(~(~((-a - 1) & (-b - 1))) + 1) |
| | 2355 | // = -((-a - 1) & (-b - 1) + 1) |
| | 2356 | |
| | 2357 | var i: usize = 0; |
| | 2358 | var a_borrow: u1 = 1; |
| | 2359 | var b_borrow: u1 = 1; |
| | 2360 | var r_carry: u1 = 1; |
| | 2361 | |
| | 2362 | while (i < b.len) : (i += 1) { |
| | 2363 | var a_limb: Limb = undefined; |
| | 2364 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); |
| | 2365 | |
| | 2366 | var b_limb: Limb = undefined; |
| | 2367 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); |
| | 2368 | |
| | 2369 | r[i] = a_limb & b_limb; |
| | 2370 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| | 2371 | } |
| | 2372 | |
| | 2373 | // b is at least 1, so this should never underflow. |
| | 2374 | assert(b_borrow == 0); // b was 0 |
| | 2375 | |
| | 2376 | // Can never overflow because in order for b_limb to be maxInt(Limb), |
| | 2377 | // b_borrow would need to equal 1. |
| | 2378 | |
| | 2379 | // x & y can only clear bits, meaning x & y <= x and x & y <= y. This implies that |
| | 2380 | // for x = a - 1 and y = b - 1, the +1 term would never cause an overflow. |
| | 2381 | assert(r_carry == 0); |
| | 2382 | |
| | 2383 | // With b = 0 and b_borrow = 0 we get (-a - 1) & (-0 - 0) = (-a - 1) & 0 = 0. |
| | 2384 | // Omit setting the upper bytes, just deal with those when calling llsignedor. |
| | 2385 | return false; |
| 2250 | } | 2386 | } |
| 2251 | } | 2387 | } |
| 2252 | | 2388 | |
| 2253 | fn lland(r: []Limb, a: []const Limb, b: []const Limb) void { | 2389 | // r = a & b with 2s complement semantics. |
| | 2390 | // r may alias. |
| | 2391 | // a and b must not be 0. |
| | 2392 | // Returns `true` when the result is positive. |
| | 2393 | // When either or both of a and b are positive, r requires at least `b.len` limbs of storage. |
| | 2394 | // When both a and b are negative, r requires at least `a.limbs.len + 1` limbs of storage. |
| | 2395 | fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { |
| 2254 | @setRuntimeSafety(debug_safety); | 2396 | @setRuntimeSafety(debug_safety); |
| 2255 | assert(r.len >= b.len); | 2397 | assert(a.len != 0 and b.len != 0); |
| | 2398 | assert(r.len >= a.len); |
| 2256 | assert(a.len >= b.len); | 2399 | assert(a.len >= b.len); |
| 2257 | | 2400 | |
| 2258 | var i: usize = 0; | 2401 | if (a_positive and b_positive) { |
| 2259 | while (i < b.len) : (i += 1) { | 2402 | // Trivial case, result is positive. |
| 2260 | r[i] = a[i] & b[i]; | 2403 | var i: usize = 0; |
| | 2404 | while (i < b.len) : (i += 1) { |
| | 2405 | r[i] = a[i] & b[i]; |
| | 2406 | } |
| | 2407 | |
| | 2408 | // With b = 0 we have a & 0 = 0, so the upper bytes are zero. |
| | 2409 | // Omit setting them here and simply discard them whenever |
| | 2410 | // llsignedand is called. |
| | 2411 | |
| | 2412 | return true; |
| | 2413 | } else if (!a_positive and b_positive) { |
| | 2414 | // Result is positive. |
| | 2415 | // r = (--a) & b |
| | 2416 | // = ~(-a - 1) & b |
| | 2417 | |
| | 2418 | var i: usize = 0; |
| | 2419 | var a_borrow: u1 = 1; |
| | 2420 | |
| | 2421 | while (i < b.len) : (i += 1) { |
| | 2422 | var a_limb: Limb = undefined; |
| | 2423 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); |
| | 2424 | r[i] = ~a_limb & b[i]; |
| | 2425 | } |
| | 2426 | |
| | 2427 | // With b = 0 we have ~(a - 1) & 0 = 0, so the upper bytes are zero. |
| | 2428 | // Omit setting them here and simply discard them whenever |
| | 2429 | // llsignedand is called. |
| | 2430 | |
| | 2431 | return true; |
| | 2432 | } else if (a_positive and !b_positive) { |
| | 2433 | // Result is positive. |
| | 2434 | // r = a & (--b) |
| | 2435 | // = a & ~(-b - 1) |
| | 2436 | |
| | 2437 | var i: usize = 0; |
| | 2438 | var b_borrow: u1 = 1; |
| | 2439 | |
| | 2440 | while (i < b.len) : (i += 1) { |
| | 2441 | var a_limb: Limb = undefined; |
| | 2442 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &a_limb)); |
| | 2443 | r[i] = a[i] & ~a_limb; |
| | 2444 | } |
| | 2445 | |
| | 2446 | assert(b_borrow == 0); // b was 0 |
| | 2447 | |
| | 2448 | // With b = 0 and b_borrow = 0 we have a & ~(-0 - 0) = a & 0 = 0, so |
| | 2449 | // the upper bytes are zero. Omit setting them here and simply discard |
| | 2450 | // them whenever llsignedand is called. |
| | 2451 | |
| | 2452 | return true; |
| | 2453 | } else { |
| | 2454 | // Result is negative. |
| | 2455 | // r = (--a) & (--b) |
| | 2456 | // = ~(-a - 1) & ~(-b - 1) |
| | 2457 | // = ~((-a - 1) | (-b - 1)) |
| | 2458 | // = -(((-a - 1) | (-b - 1)) + 1) |
| | 2459 | |
| | 2460 | var i: usize = 0; |
| | 2461 | var a_borrow: u1 = 1; |
| | 2462 | var b_borrow: u1 = 1; |
| | 2463 | var r_carry: u1 = 1; |
| | 2464 | |
| | 2465 | while (i < b.len) : (i += 1) { |
| | 2466 | var a_limb: Limb = undefined; |
| | 2467 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); |
| | 2468 | |
| | 2469 | var b_limb: Limb = undefined; |
| | 2470 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); |
| | 2471 | |
| | 2472 | r[i] = a_limb | b_limb; |
| | 2473 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| | 2474 | } |
| | 2475 | |
| | 2476 | // b is at least 1, so this should never underflow. |
| | 2477 | assert(b_borrow == 0); // b was 0 |
| | 2478 | |
| | 2479 | // With b = 0 and b_borrow = 0 we get (-a - 1) | (-0 - 0) = (-a - 1) | 0 = -a - 1. |
| | 2480 | while (i < a.len) : (i += 1) { |
| | 2481 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); |
| | 2482 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| | 2483 | } |
| | 2484 | |
| | 2485 | assert(a_borrow == 0); // a was 0. |
| | 2486 | |
| | 2487 | // The final addition can overflow here, so we need to keep that in mind. |
| | 2488 | r[i] = r_carry; |
| | 2489 | |
| | 2490 | return false; |
| 2261 | } | 2491 | } |
| 2262 | } | 2492 | } |
| 2263 | | 2493 | |
| ... | @@ -2265,6 +2495,8 @@ fn lland(r: []Limb, a: []const Limb, b: []const Limb) void { | ... | @@ -2265,6 +2495,8 @@ fn lland(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2265 | // r may alias. | 2495 | // r may alias. |
| 2266 | // a and b must not be -0. | 2496 | // a and b must not be -0. |
| 2267 | // Returns `true` when the result is positive. | 2497 | // Returns `true` when the result is positive. |
| | 2498 | // If the sign of a and b is equal, then r requires at least `max(a.len, b.len)` limbs are required. |
| | 2499 | // Otherwise, r requires at least `max(a.len, b.len) + 1` limbs. |
| 2268 | fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { | 2500 | fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { |
| 2269 | @setRuntimeSafety(debug_safety); | 2501 | @setRuntimeSafety(debug_safety); |
| 2270 | assert(a.len != 0 and b.len != 0); | 2502 | assert(a.len != 0 and b.len != 0); |
| ... | @@ -2308,7 +2540,12 @@ fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_ | ... | @@ -2308,7 +2540,12 @@ fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_ |
| 2308 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); | 2540 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| 2309 | } | 2541 | } |
| 2310 | | 2542 | |
| 2311 | r[i] = r_carry; | 2543 | // If both inputs don't share the same sign, an extra limb is required. |
| | 2544 | if (a_positive != b_positive) { |
| | 2545 | r[i] = r_carry; |
| | 2546 | } else { |
| | 2547 | assert(r_carry == 0); |
| | 2548 | } |
| 2312 | | 2549 | |
| 2313 | assert(a_borrow == 0); | 2550 | assert(a_borrow == 0); |
| 2314 | assert(b_borrow == 0); | 2551 | assert(b_borrow == 0); |