| ... | ... | @@ -584,19 +584,29 @@ pub const Mutable = struct { |
| 584 | 584 | r.positive = a.positive and b.positive; |
| 585 | 585 | } |
| 586 | 586 | |
| 587 | | /// r = a ^ b |
| 587 | /// r = a ^ b under 2s complement semantics. |
| 588 | 588 | /// r may alias with a or b. |
| 589 | 589 | /// |
| 590 | | /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`. |
| 590 | /// Asserts that r has enough limbs to store the result. If a and b share the same signedness, the |
| 591 | /// upper bound is `math.max(a.limbs.len, b.limbs.len)`. Otherwise, if either a or b is negative |
| 592 | /// but not both, the upper bound is `math.max(a.limbs.len, b.limbs.len) + 1`. |
| 591 | 593 | pub fn bitXor(r: *Mutable, a: Const, b: Const) void { |
| 594 | // Trivial cases, because llsignedxor does not support negative zero. |
| 595 | if (a.eqZero()) { |
| 596 | r.copy(b); |
| 597 | return; |
| 598 | } else if (b.eqZero()) { |
| 599 | r.copy(a); |
| 600 | return; |
| 601 | } |
| 602 | |
| 592 | 603 | if (a.limbs.len > b.limbs.len) { |
| 593 | | llxor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); |
| 594 | | r.normalize(a.limbs.len); |
| 604 | r.positive = llsignedxor(r.limbs, a.limbs, a.positive, b.limbs, b.positive); |
| 605 | r.normalize(a.limbs.len + @boolToInt(a.positive != b.positive)); |
| 595 | 606 | } else { |
| 596 | | llxor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); |
| 597 | | r.normalize(b.limbs.len); |
| 607 | r.positive = llsignedxor(r.limbs, b.limbs, b.positive, a.limbs, a.positive); |
| 608 | r.normalize(b.limbs.len + @boolToInt(a.positive != b.positive)); |
| 598 | 609 | } |
| 599 | | r.positive = a.positive or b.positive; |
| 600 | 610 | } |
| 601 | 611 | |
| 602 | 612 | /// rma may alias x or y. |
| ... | ... | @@ -1845,7 +1855,9 @@ pub const Managed = struct { |
| 1845 | 1855 | |
| 1846 | 1856 | /// r = a ^ b |
| 1847 | 1857 | pub fn bitXor(r: *Managed, a: Managed, b: Managed) !void { |
| 1848 | | try r.ensureCapacity(math.max(a.len(), b.len())); |
| 1858 | var cap = math.max(a.len(), b.len()) + @boolToInt(a.isPositive() != b.isPositive()); |
| 1859 | try r.ensureCapacity(cap); |
| 1860 | |
| 1849 | 1861 | var m = r.toMutable(); |
| 1850 | 1862 | m.bitXor(a.toConst(), b.toConst()); |
| 1851 | 1863 | r.setMetadata(m.positive, m.len); |
| ... | ... | @@ -2249,17 +2261,59 @@ fn lland(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2249 | 2261 | } |
| 2250 | 2262 | } |
| 2251 | 2263 | |
| 2252 | | fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2264 | // r = a ^ b with 2s complement semantics. |
| 2265 | // r may alias. |
| 2266 | // a and b must not be -0. |
| 2267 | // Returns `true` when the result is positive. |
| 2268 | fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { |
| 2269 | @setRuntimeSafety(debug_safety); |
| 2270 | assert(a.len != 0 and b.len != 0); |
| 2253 | 2271 | assert(r.len >= a.len); |
| 2254 | 2272 | assert(a.len >= b.len); |
| 2255 | 2273 | |
| 2274 | // If a and b are positive, the result is positive and r = a ^ b. |
| 2275 | // If a negative, b positive, result is negative and we have |
| 2276 | // r = --(--a ^ b) |
| 2277 | // = --(~(-a - 1) ^ b) |
| 2278 | // = -(~(~(-a - 1) ^ b) + 1) |
| 2279 | // = -(((-a - 1) ^ b) + 1) |
| 2280 | // Same if a is positive and b is negative, sides switched. |
| 2281 | // If both a and b are negative, the result is positive and we have |
| 2282 | // r = (--a) ^ (--b) |
| 2283 | // = ~(-a - 1) ^ ~(-b - 1) |
| 2284 | // = (-a - 1) ^ (-b - 1) |
| 2285 | // These operations can be made more generic as follows: |
| 2286 | // - If a is negative, subtract 1 from |a| before the xor. |
| 2287 | // - If b is negative, subtract 1 from |b| before the xor. |
| 2288 | // - if the result is supposed to be negative, add 1. |
| 2289 | |
| 2256 | 2290 | var i: usize = 0; |
| 2291 | var a_borrow = @boolToInt(!a_positive); |
| 2292 | var b_borrow = @boolToInt(!b_positive); |
| 2293 | var r_carry = @boolToInt(a_positive != b_positive); |
| 2294 | |
| 2257 | 2295 | while (i < b.len) : (i += 1) { |
| 2258 | | r[i] = a[i] ^ b[i]; |
| 2296 | var a_limb: Limb = undefined; |
| 2297 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); |
| 2298 | |
| 2299 | var b_limb: Limb = undefined; |
| 2300 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); |
| 2301 | |
| 2302 | r[i] = a_limb ^ b_limb; |
| 2303 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| 2259 | 2304 | } |
| 2305 | |
| 2260 | 2306 | while (i < a.len) : (i += 1) { |
| 2261 | | r[i] = a[i]; |
| 2307 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); |
| 2308 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); |
| 2262 | 2309 | } |
| 2310 | |
| 2311 | r[i] = r_carry; |
| 2312 | |
| 2313 | assert(a_borrow == 0); |
| 2314 | assert(b_borrow == 0); |
| 2315 | |
| 2316 | return a_positive == b_positive; |
| 2263 | 2317 | } |
| 2264 | 2318 | |
| 2265 | 2319 | /// r MUST NOT alias x. |