|
@@ -194,12 +194,36 @@ FLATTEN SignedBigInteger SignedBigInteger::bitwise_or(const SignedBigInteger& ot
|
|
|
|
|
|
FLATTEN SignedBigInteger SignedBigInteger::bitwise_and(const SignedBigInteger& other) const
|
|
|
{
|
|
|
- auto result = bitwise_and(other.unsigned_value());
|
|
|
-
|
|
|
- // The sign bit will have to be AND'd manually.
|
|
|
- result.m_sign = is_negative() && other.is_negative();
|
|
|
-
|
|
|
- return result;
|
|
|
+ if (!is_negative() && !other.is_negative())
|
|
|
+ return { unsigned_value().bitwise_and(other.unsigned_value()), false };
|
|
|
+
|
|
|
+ // These two just use that -x == ~x + 1 (see below).
|
|
|
+
|
|
|
+ // -A & B == (~A + 1) & B.
|
|
|
+ if (is_negative() && !other.is_negative())
|
|
|
+ return { unsigned_value().bitwise_not_fill_to_size(other.trimmed_length()).plus(1).bitwise_and(other.unsigned_value()), false };
|
|
|
+
|
|
|
+ // A & -B == A & (~B + 1).
|
|
|
+ if (!is_negative() && other.is_negative())
|
|
|
+ return { unsigned_value().bitwise_and(other.unsigned_value().bitwise_not_fill_to_size(trimmed_length()).plus(1)), false };
|
|
|
+
|
|
|
+ // Both numbers are negative.
|
|
|
+ // x + ~x == 0xff...ff, up to however many bits x is wide.
|
|
|
+ // In two's complement, x + ~x + 1 == 0 since the 1 in the overflowing bit position is masked out.
|
|
|
+ // Rearranging terms, ~x = -x - 1 (eq1).
|
|
|
+ // Substituting x = y - 1, ~(y - 1) == -(y - 1) - 1 == -y +1 -1 == -y, or ~(y - 1) == -y (eq2).
|
|
|
+ // Since both numbers are negative, we want to compute -A & -B.
|
|
|
+ // Per (eq2):
|
|
|
+ // -A & -B == ~(A - 1) & ~(B - 1)
|
|
|
+ // Inverting both sides:
|
|
|
+ // ~(-A & -B) == ~(~(A - 1) & ~(B - 1)) == ~~(A - 1) | ~~(B - 1) == (A - 1) | (B - 1).
|
|
|
+ // Applying (q1) on the LHS:
|
|
|
+ // -(-A & -B) - 1 == (A - 1) | (B - 1)
|
|
|
+ // Adding 1 on both sides and then multiplying both sides by -1:
|
|
|
+ // -A & -B == -( (A - 1) | (B - 1) + 1)
|
|
|
+ // So we can compute the bitwise and by returning a negative number with magnitude (A - 1) | (B - 1) + 1.
|
|
|
+ // This is better than the naive (~A + 1) & (~B + 1) because it needs just one O(n) scan for the or instead of 2 for the ~s.
|
|
|
+ return { unsigned_value().minus(1).bitwise_or(other.unsigned_value().minus(1)).plus(1), true };
|
|
|
}
|
|
|
|
|
|
FLATTEN SignedBigInteger SignedBigInteger::bitwise_xor(const SignedBigInteger& other) const
|