| ... | ... | @@ -552,36 +552,54 @@ pub const Mutable = struct { |
| 552 | 552 | r.positive = a.positive; |
| 553 | 553 | } |
| 554 | 554 | |
| 555 | | /// r = a | b |
| 555 | /// r = a | b under 2s complement semantics. |
| 556 | 556 | /// r may alias with a or b. |
| 557 | 557 | /// |
| 558 | 558 | /// a and b are zero-extended to the longer of a or b. |
| 559 | 559 | /// |
| 560 | 560 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`. |
| 561 | 561 | pub fn bitOr(r: *Mutable, a: Const, b: Const) void { |
| 562 | | if (a.limbs.len > b.limbs.len) { |
| 563 | | llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); |
| 564 | | r.len = a.limbs.len; |
| 562 | // Trivial cases, llsignedor does not support zero. |
| 563 | if (a.eqZero()) { |
| 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 | 574 | } else { |
| 566 | | llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); |
| 567 | | r.len = b.limbs.len; |
| 575 | r.positive = llsignedor(r.limbs, b.limbs, b.positive, a.limbs, a.positive); |
| 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 | 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 | 586 | pub fn bitAnd(r: *Mutable, a: Const, b: Const) void { |
| 577 | | if (a.limbs.len > b.limbs.len) { |
| 578 | | lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); |
| 579 | | r.normalize(b.limbs.len); |
| 587 | // Trivial cases, llsignedand does not support zero. |
| 588 | if (a.eqZero()) { |
| 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 | 599 | } else { |
| 581 | | lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); |
| 582 | | r.normalize(a.limbs.len); |
| 600 | r.positive = llsignedand(r.limbs, b.limbs, b.positive, a.limbs, a.positive); |
| 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 | 605 | /// r = a ^ b under 2s complement semantics. |
| ... | ... | @@ -1847,7 +1865,11 @@ pub const Managed = struct { |
| 1847 | 1865 | |
| 1848 | 1866 | /// r = a & b |
| 1849 | 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 | 1873 | var m = r.toMutable(); |
| 1852 | 1874 | m.bitAnd(a.toConst(), b.toConst()); |
| 1853 | 1875 | r.setMetadata(m.positive, m.len); |
| ... | ... | @@ -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 | 2268 | @setRuntimeSafety(debug_safety); |
| 2241 | 2269 | assert(r.len >= a.len); |
| 2242 | 2270 | assert(a.len >= b.len); |
| 2243 | 2271 | |
| 2244 | | var i: usize = 0; |
| 2245 | | while (i < b.len) : (i += 1) { |
| 2246 | | r[i] = a[i] | b[i]; |
| 2247 | | } |
| 2248 | | while (i < a.len) : (i += 1) { |
| 2249 | | r[i] = a[i]; |
| 2272 | if (a_positive and b_positive) { |
| 2273 | // Trivial case, result is positive. |
| 2274 | var i: usize = 0; |
| 2275 | while (i < b.len) : (i += 1) { |
| 2276 | r[i] = a[i] | b[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 | 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 | 2399 | assert(a.len >= b.len); |
| 2257 | 2400 | |
| 2258 | | var i: usize = 0; |
| 2259 | | while (i < b.len) : (i += 1) { |
| 2260 | | r[i] = a[i] & b[i]; |
| 2401 | if (a_positive and b_positive) { |
| 2402 | // Trivial case, result is positive. |
| 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 | 2495 | // r may alias. |
| 2266 | 2496 | // a and b must not be -0. |
| 2267 | 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 | 2500 | fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { |
| 2269 | 2501 | @setRuntimeSafety(debug_safety); |
| 2270 | 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 | 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 | 2550 | assert(a_borrow == 0); |
| 2314 | 2551 | assert(b_borrow == 0); |