| ... | ... | @@ -570,26 +570,36 @@ pub const Mutable = struct { |
| 570 | 570 | |
| 571 | 571 | if (a.limbs.len >= b.limbs.len) { |
| 572 | 572 | r.positive = llsignedor(r.limbs, a.limbs, a.positive, b.limbs, b.positive); |
| 573 | | r.normalize(a.limbs.len); |
| 573 | r.normalize(if (b.positive) a.limbs.len else b.limbs.len); |
| 574 | 574 | } else { |
| 575 | 575 | r.positive = llsignedor(r.limbs, b.limbs, b.positive, a.limbs, a.positive); |
| 576 | | r.normalize(b.limbs.len); |
| 576 | r.normalize(if (a.positive) b.limbs.len else a.limbs.len); |
| 577 | 577 | } |
| 578 | 578 | } |
| 579 | 579 | |
| 580 | | /// r = a & b |
| 580 | /// r = a & b under 2s complement semantics. |
| 581 | 581 | /// r may alias with a or b. |
| 582 | 582 | /// |
| 583 | | /// 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`. |
| 584 | 586 | pub fn bitAnd(r: *Mutable, a: Const, b: Const) void { |
| 585 | | if (a.limbs.len > b.limbs.len) { |
| 586 | | lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); |
| 587 | | 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); |
| 588 | 599 | } else { |
| 589 | | lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); |
| 590 | | 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); |
| 591 | 602 | } |
| 592 | | r.positive = a.positive and b.positive; |
| 593 | 603 | } |
| 594 | 604 | |
| 595 | 605 | /// r = a ^ b under 2s complement semantics. |
| ... | ... | @@ -1855,7 +1865,11 @@ pub const Managed = struct { |
| 1855 | 1865 | |
| 1856 | 1866 | /// r = a & b |
| 1857 | 1867 | pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void { |
| 1858 | | 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); |
| 1859 | 1873 | var m = r.toMutable(); |
| 1860 | 1874 | m.bitAnd(a.toConst(), b.toConst()); |
| 1861 | 1875 | r.setMetadata(m.positive, m.len); |
| ... | ... | @@ -2244,13 +2258,19 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { |
| 2244 | 2258 | } |
| 2245 | 2259 | } |
| 2246 | 2260 | |
| 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. |
| 2247 | 2267 | fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { |
| 2248 | 2268 | @setRuntimeSafety(debug_safety); |
| 2249 | 2269 | assert(r.len >= a.len); |
| 2250 | 2270 | assert(a.len >= b.len); |
| 2251 | 2271 | |
| 2252 | 2272 | if (a_positive and b_positive) { |
| 2253 | | // Trivial case, result is positive., |
| 2273 | // Trivial case, result is positive. |
| 2254 | 2274 | var i: usize = 0; |
| 2255 | 2275 | while (i < b.len) : (i += 1) { |
| 2256 | 2276 | r[i] = a[i] | b[i]; |
| ... | ... | @@ -2261,12 +2281,12 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p |
| 2261 | 2281 | |
| 2262 | 2282 | return true; |
| 2263 | 2283 | } else if (!a_positive and b_positive) { |
| 2284 | // Result is negative. |
| 2264 | 2285 | // r = (--a) | b |
| 2265 | 2286 | // = ~(-a - 1) | b |
| 2266 | 2287 | // = ~(-a - 1) | ~~b |
| 2267 | 2288 | // = ~((-a - 1) & ~b) |
| 2268 | 2289 | // = -(((-a - 1) & ~b) + 1) |
| 2269 | | // result is negative. |
| 2270 | 2290 | |
| 2271 | 2291 | var i: usize = 0; |
| 2272 | 2292 | var a_borrow: u1 = 1; |
| ... | ... | @@ -2280,25 +2300,29 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p |
| 2280 | 2300 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| 2281 | 2301 | } |
| 2282 | 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 | |
| 2283 | 2309 | // With b = 0, we get (-a - 1) & ~0 = -a - 1. |
| 2284 | | while (i < a.len) : (i += 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) { |
| 2285 | 2313 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); |
| 2286 | | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| 2287 | 2314 | } |
| 2288 | 2315 | |
| 2289 | 2316 | assert(a_borrow == 0); // a was 0. |
| 2290 | 2317 | |
| 2291 | | // Can never overflow because a_borrow would need to equal 1. |
| 2292 | | assert(r_carry == 0); |
| 2293 | | |
| 2294 | 2318 | return false; |
| 2295 | 2319 | } else if (a_positive and !b_positive) { |
| 2320 | // Result is negative. |
| 2296 | 2321 | // r = a | (--b) |
| 2297 | 2322 | // = a | ~(-b - 1) |
| 2298 | 2323 | // = ~~a | ~(-b - 1) |
| 2299 | 2324 | // = ~(~a & (-b - 1)) |
| 2300 | 2325 | // = -((~a & (-b - 1)) + 1) |
| 2301 | | // result is negative. |
| 2302 | 2326 | |
| 2303 | 2327 | var i: usize = 0; |
| 2304 | 2328 | var b_borrow: u1 = 1; |
| ... | ... | @@ -2315,21 +2339,20 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p |
| 2315 | 2339 | // b is at least 1, so this should never underflow. |
| 2316 | 2340 | assert(b_borrow == 0); // b was 0 |
| 2317 | 2341 | |
| 2318 | | // Can never overflow because in order for b_limb to be maxInt(Limb), |
| 2319 | | // b_borrow would need to equal 1. |
| 2342 | // x & ~a can only clear bits, so (x & ~a) <= x, meaning (-b - 1) + 1 never overflows. |
| 2320 | 2343 | assert(r_carry == 0); |
| 2321 | 2344 | |
| 2322 | 2345 | // With b = 0 and b_borrow = 0, we get ~a & (-0 - 0) = ~a & 0 = 0. |
| 2323 | | mem.set(Limb, r[i..a.len], 0); |
| 2346 | // Omit setting the upper bytes, just deal with those when calling llsignedor. |
| 2324 | 2347 | |
| 2325 | 2348 | return false; |
| 2326 | 2349 | } else { |
| 2350 | // Result is negative. |
| 2327 | 2351 | // r = (--a) | (--b) |
| 2328 | 2352 | // = ~(-a - 1) | ~(-b - 1) |
| 2329 | 2353 | // = ~((-a - 1) & (-b - 1)) |
| 2330 | 2354 | // = -(~(~((-a - 1) & (-b - 1))) + 1) |
| 2331 | 2355 | // = -((-a - 1) & (-b - 1) + 1) |
| 2332 | | // result is negative. |
| 2333 | 2356 | |
| 2334 | 2357 | var i: usize = 0; |
| 2335 | 2358 | var a_borrow: u1 = 1; |
| ... | ... | @@ -2352,22 +2375,119 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p |
| 2352 | 2375 | |
| 2353 | 2376 | // Can never overflow because in order for b_limb to be maxInt(Limb), |
| 2354 | 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. |
| 2355 | 2381 | assert(r_carry == 0); |
| 2356 | 2382 | |
| 2357 | 2383 | // With b = 0 and b_borrow = 0 we get (-a - 1) & (-0 - 0) = (-a - 1) & 0 = 0. |
| 2358 | | mem.set(Limb, r[i..a.len], 0); |
| 2384 | // Omit setting the upper bytes, just deal with those when calling llsignedor. |
| 2359 | 2385 | return false; |
| 2360 | 2386 | } |
| 2361 | 2387 | } |
| 2362 | 2388 | |
| 2363 | | 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 { |
| 2364 | 2396 | @setRuntimeSafety(debug_safety); |
| 2365 | | assert(r.len >= b.len); |
| 2397 | assert(a.len != 0 and b.len != 0); |
| 2398 | assert(r.len >= a.len); |
| 2366 | 2399 | assert(a.len >= b.len); |
| 2367 | 2400 | |
| 2368 | | var i: usize = 0; |
| 2369 | | while (i < b.len) : (i += 1) { |
| 2370 | | 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; |
| 2371 | 2491 | } |
| 2372 | 2492 | } |
| 2373 | 2493 | |