| ... | @@ -584,19 +584,29 @@ pub const Mutable = struct { | ... | @@ -584,19 +584,29 @@ pub const Mutable = struct { |
| 584 | r.positive = a.positive and b.positive; | 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 | /// r may alias with a or b. | 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 | pub fn bitXor(r: *Mutable, a: Const, b: Const) void { | 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 | if (a.limbs.len > b.limbs.len) { | 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]); | 604 | r.positive = llsignedxor(r.limbs, a.limbs, a.positive, b.limbs, b.positive); |
| 594 | r.normalize(a.limbs.len); | 605 | r.normalize(a.limbs.len + @boolToInt(a.positive != b.positive)); |
| 595 | } else { | 606 | } else { |
| 596 | llxor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | 607 | r.positive = llsignedxor(r.limbs, b.limbs, b.positive, a.limbs, a.positive); |
| 597 | r.normalize(b.limbs.len); | 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 | /// rma may alias x or y. | 612 | /// rma may alias x or y. |
| ... | @@ -1845,7 +1855,9 @@ pub const Managed = struct { | ... | @@ -1845,7 +1855,9 @@ pub const Managed = struct { |
| 1845 | | 1855 | |
| 1846 | /// r = a ^ b | 1856 | /// r = a ^ b |
| 1847 | pub fn bitXor(r: *Managed, a: Managed, b: Managed) !void { | 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 | var m = r.toMutable(); | 1861 | var m = r.toMutable(); |
| 1850 | m.bitXor(a.toConst(), b.toConst()); | 1862 | m.bitXor(a.toConst(), b.toConst()); |
| 1851 | r.setMetadata(m.positive, m.len); | 1863 | r.setMetadata(m.positive, m.len); |
| ... | @@ -2249,17 +2261,59 @@ fn lland(r: []Limb, a: []const Limb, b: []const Limb) void { | ... | @@ -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 | assert(r.len >= a.len); | 2271 | assert(r.len >= a.len); |
| 2254 | assert(a.len >= b.len); | 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 | var i: usize = 0; | 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 | while (i < b.len) : (i += 1) { | 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 | while (i < a.len) : (i += 1) { | 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 | /// r MUST NOT alias x. | 2319 | /// r MUST NOT alias x. |