| ... | @@ -570,26 +570,36 @@ pub const Mutable = struct { | ... | @@ -570,26 +570,36 @@ pub const Mutable = struct { |
| 570 | | 570 | |
| 571 | if (a.limbs.len >= b.limbs.len) { | 571 | if (a.limbs.len >= b.limbs.len) { |
| 572 | r.positive = llsignedor(r.limbs, a.limbs, a.positive, b.limbs, b.positive); | 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 | } else { | 574 | } else { |
| 575 | r.positive = llsignedor(r.limbs, b.limbs, b.positive, a.limbs, a.positive); | 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 | /// r may alias with a or b. | 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 | pub fn bitAnd(r: *Mutable, a: Const, b: Const) void { | 586 | pub fn bitAnd(r: *Mutable, a: Const, b: Const) void { |
| 585 | if (a.limbs.len > b.limbs.len) { | 587 | // Trivial cases, llsignedand does not support zero. |
| 586 | lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | 588 | if (a.eqZero()) { |
| 587 | 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); |
| 588 | } else { | 599 | } else { |
| 589 | 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); |
| 590 | r.normalize(a.limbs.len); | 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 | /// r = a ^ b under 2s complement semantics. | 605 | /// r = a ^ b under 2s complement semantics. |
| ... | @@ -1855,7 +1865,11 @@ pub const Managed = struct { | ... | @@ -1855,7 +1865,11 @@ pub const Managed = struct { |
| 1855 | | 1865 | |
| 1856 | /// r = a & b | 1866 | /// r = a & b |
| 1857 | pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void { | 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 | var m = r.toMutable(); | 1873 | var m = r.toMutable(); |
| 1860 | m.bitAnd(a.toConst(), b.toConst()); | 1874 | m.bitAnd(a.toConst(), b.toConst()); |
| 1861 | r.setMetadata(m.positive, m.len); | 1875 | r.setMetadata(m.positive, m.len); |
| ... | @@ -2244,13 +2258,19 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { | ... | @@ -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 | fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { | 2267 | fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { |
| 2248 | @setRuntimeSafety(debug_safety); | 2268 | @setRuntimeSafety(debug_safety); |
| 2249 | assert(r.len >= a.len); | 2269 | assert(r.len >= a.len); |
| 2250 | assert(a.len >= b.len); | 2270 | assert(a.len >= b.len); |
| 2251 | | 2271 | |
| 2252 | if (a_positive and b_positive) { | 2272 | if (a_positive and b_positive) { |
| 2253 | // Trivial case, result is positive., | 2273 | // Trivial case, result is positive. |
| 2254 | var i: usize = 0; | 2274 | var i: usize = 0; |
| 2255 | while (i < b.len) : (i += 1) { | 2275 | while (i < b.len) : (i += 1) { |
| 2256 | r[i] = a[i] | b[i]; | 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,12 +2281,12 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p |
| 2261 | | 2281 | |
| 2262 | return true; | 2282 | return true; |
| 2263 | } else if (!a_positive and b_positive) { | 2283 | } else if (!a_positive and b_positive) { |
| | 2284 | // Result is negative. |
| 2264 | // r = (--a) | b | 2285 | // r = (--a) | b |
| 2265 | // = ~(-a - 1) | b | 2286 | // = ~(-a - 1) | b |
| 2266 | // = ~(-a - 1) | ~~b | 2287 | // = ~(-a - 1) | ~~b |
| 2267 | // = ~((-a - 1) & ~b) | 2288 | // = ~((-a - 1) & ~b) |
| 2268 | // = -(((-a - 1) & ~b) + 1) | 2289 | // = -(((-a - 1) & ~b) + 1) |
| 2269 | // result is negative. | | |
| 2270 | | 2290 | |
| 2271 | var i: usize = 0; | 2291 | var i: usize = 0; |
| 2272 | var a_borrow: u1 = 1; | 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,25 +2300,29 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p |
| 2280 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); | 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 | // With b = 0, we get (-a - 1) & ~0 = -a - 1. | 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 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); | 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 | assert(a_borrow == 0); // a was 0. | 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 | return false; | 2318 | return false; |
| 2295 | } else if (a_positive and !b_positive) { | 2319 | } else if (a_positive and !b_positive) { |
| | 2320 | // Result is negative. |
| 2296 | // r = a | (--b) | 2321 | // r = a | (--b) |
| 2297 | // = a | ~(-b - 1) | 2322 | // = a | ~(-b - 1) |
| 2298 | // = ~~a | ~(-b - 1) | 2323 | // = ~~a | ~(-b - 1) |
| 2299 | // = ~(~a & (-b - 1)) | 2324 | // = ~(~a & (-b - 1)) |
| 2300 | // = -((~a & (-b - 1)) + 1) | 2325 | // = -((~a & (-b - 1)) + 1) |
| 2301 | // result is negative. | | |
| 2302 | | 2326 | |
| 2303 | var i: usize = 0; | 2327 | var i: usize = 0; |
| 2304 | var b_borrow: u1 = 1; | 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,21 +2339,20 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p |
| 2315 | // b is at least 1, so this should never underflow. | 2339 | // b is at least 1, so this should never underflow. |
| 2316 | assert(b_borrow == 0); // b was 0 | 2340 | assert(b_borrow == 0); // b was 0 |
| 2317 | | 2341 | |
| 2318 | // Can never overflow because in order for b_limb to be maxInt(Limb), | 2342 | // x & ~a can only clear bits, so (x & ~a) <= x, meaning (-b - 1) + 1 never overflows. |
| 2319 | // b_borrow would need to equal 1. | | |
| 2320 | assert(r_carry == 0); | 2343 | assert(r_carry == 0); |
| 2321 | | 2344 | |
| 2322 | // With b = 0 and b_borrow = 0, we get ~a & (-0 - 0) = ~a & 0 = 0. | 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 | return false; | 2348 | return false; |
| 2326 | } else { | 2349 | } else { |
| | 2350 | // Result is negative. |
| 2327 | // r = (--a) | (--b) | 2351 | // r = (--a) | (--b) |
| 2328 | // = ~(-a - 1) | ~(-b - 1) | 2352 | // = ~(-a - 1) | ~(-b - 1) |
| 2329 | // = ~((-a - 1) & (-b - 1)) | 2353 | // = ~((-a - 1) & (-b - 1)) |
| 2330 | // = -(~(~((-a - 1) & (-b - 1))) + 1) | 2354 | // = -(~(~((-a - 1) & (-b - 1))) + 1) |
| 2331 | // = -((-a - 1) & (-b - 1) + 1) | 2355 | // = -((-a - 1) & (-b - 1) + 1) |
| 2332 | // result is negative. | | |
| 2333 | | 2356 | |
| 2334 | var i: usize = 0; | 2357 | var i: usize = 0; |
| 2335 | var a_borrow: u1 = 1; | 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,22 +2375,119 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p |
| 2352 | | 2375 | |
| 2353 | // Can never overflow because in order for b_limb to be maxInt(Limb), | 2376 | // Can never overflow because in order for b_limb to be maxInt(Limb), |
| 2354 | // b_borrow would need to equal 1. | 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 | assert(r_carry == 0); | 2381 | assert(r_carry == 0); |
| 2356 | | 2382 | |
| 2357 | // With b = 0 and b_borrow = 0 we get (-a - 1) & (-0 - 0) = (-a - 1) & 0 = 0. | 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 | return false; | 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 | @setRuntimeSafety(debug_safety); | 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 | assert(a.len >= b.len); | 2399 | assert(a.len >= b.len); |
| 2367 | | 2400 | |
| 2368 | var i: usize = 0; | 2401 | if (a_positive and b_positive) { |
| 2369 | while (i < b.len) : (i += 1) { | 2402 | // Trivial case, result is positive. |
| 2370 | 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; |
| 2371 | } | 2491 | } |
| 2372 | } | 2492 | } |
| 2373 | | 2493 | |