+/* --- @mp_neg@ --- *
+ *
+ * Arguments: @mp *d@ = destination
+ * @mp *a@ = argument
+ *
+ * Returns: The negation of the argument.
+ *
+ * Use: Negates its argument.
+ */
+
+mp *mp_neg(mp *d, mp *a)
+{
+ /* --- Surprising amounts of messing about required --- */
+
+ MP_SHRINK(a);
+ MP_COPY(a);
+ if (d) MP_DROP(d);
+ if (a->v == a->vl) {
+ return (a);
+ }
+ MP_DEST(a, MP_LEN(a), a->f);
+ a->f ^= MP_NEG;
+ return (a);
+}
+
+/* --- @mp_bitop@ --- *
+ *
+ * Arguments: @mp *d@ = destination
+ * @mp *a, *b@ = sources
+ *
+ * Returns: The result of the given bitwise operation. These functions
+ * don't handle negative numbers at all sensibly. For that, use
+ * the @...2c@ variants. The functions are named after the
+ * truth tables they generate:
+ *
+ * a: 0011
+ * b: 0101
+ * @mpx_bitXXXX@
+ */
+
+#define MP_BITBINOP(string) \
+ \
+mp *mp_bit##string(mp *d, mp *a, mp *b) \
+{ \
+ MP_DEST(d, MAX(MP_LEN(a), MP_LEN(b)), (a->f | b->f) & ~MP_NEG); \
+ mpx_bit##string(d->v, d->vl, a->v, a->vl, b->v, b->vl); \
+ d->f = (a->f | b->f) & MP_BURN; \
+ MP_SHRINK(d); \
+ return (d); \
+}
+
+MPX_DOBIN(MP_BITBINOP)
+
+/* --- @mp_not@ --- *
+ *
+ * Arguments: @mp *d@ = destination
+ * @mp *a@ = source
+ *
+ * Returns: The bitwise complement of the source.
+ */
+
+mp *mp_not(mp *d, mp *a)
+{
+ MP_DEST(d, MP_LEN(a), a->f);
+ mpx_not(d->v, d->vl, a->v, a->vl);
+ d->f = a->f & MP_BURN;
+ MP_SHRINK(d);
+ return (d);
+}
+
+/* --- @mp_bitop2c@ --- *
+ *
+ * Arguments: @mp *d@ = destination
+ * @mp *a, *b@ = sources
+ *
+ * Returns: The result of the given bitwise operation. Negative numbers
+ * are treated as two's complement, sign-extended infinitely to
+ * the left. The functions are named after the truth tables
+ * they generate:
+ *
+ * a: 0011
+ * b: 0101
+ * @mpx_bitXXXX@
+ */
+
+/* --- How this actually works --- *
+ *
+ * The two arguments are inverted (with a sign-swap) if they're currently
+ * negative. This means that we end up using a different function (one which
+ * reinverts as we go) for the main operation. Also, if the sign would be
+ * negative at the end, we preinvert the output and then invert again with a
+ * sign-swap.
+ *
+ * Start with: wxyz WXYZ
+ * If @a@ negative: yzwx or YZWX
+ * If @b@ negative: xwzy XWZY
+ * If both negative: zyxw ZYXW
+ */
+
+#define MP_BIT2CBINOP(n, base, an, bn, abn, p_base, p_an, p_bn, p_abn) \
+ \
+mp *mp_bit##n##2c(mp *d, mp *a, mp *b) \
+{ \
+ if (!((a->f | b->f) & MP_NEG)) { /* Both positive */ \
+ d = mp_bit##base(d, a, b); \
+ p_base \
+ } else if (!(b->f & MP_NEG)) { /* Only @b@ positive */ \
+ MP_COPY(b); \
+ d = mp_not2c(d, a); \
+ d = mp_bit##an(d, d, b); \
+ MP_DROP(b); \
+ p_an \
+ } else if (!(a->f & MP_NEG)) { /* Only @a@ positive */ \
+ MP_COPY(a); \
+ d = mp_not2c(d, b); \
+ d = mp_bit##bn(d, a, d); \
+ MP_DROP(a); \
+ p_bn \
+ } else { /* Both negative */ \
+ mp *t = mp_not2c(MP_NEW, a); \
+ mp *d = mp_not2c(d, b); \
+ d = mp_bit##abn(d, t, d); \
+ MP_DROP(t); \
+ p_abn \
+ } \
+ return (d); \
+} \
+
+#define NEG d = mp_not2c(d, d);
+#define POS
+MP_BIT2CBINOP(0000, 0000, 0000, 0000, 0000, POS, POS, POS, POS)
+MP_BIT2CBINOP(0001, 0001, 0100, 0010, 0111, POS, POS, POS, NEG)
+MP_BIT2CBINOP(0010, 0010, 0111, 0001, 0100, POS, NEG, POS, POS)
+MP_BIT2CBINOP(0011, 0011, 0011, 0011, 0011, POS, NEG, POS, NEG)
+MP_BIT2CBINOP(0100, 0100, 0001, 0111, 0010, POS, POS, NEG, POS)
+MP_BIT2CBINOP(0101, 0101, 0101, 0101, 0101, POS, POS, NEG, NEG)
+MP_BIT2CBINOP(0110, 0110, 0110, 0110, 0110, POS, NEG, NEG, POS)
+MP_BIT2CBINOP(0111, 0111, 0010, 0100, 0001, POS, NEG, NEG, NEG)
+MP_BIT2CBINOP(1000, 0111, 0010, 0100, 0001, NEG, POS, POS, POS)
+MP_BIT2CBINOP(1001, 0110, 0110, 0110, 0110, NEG, POS, POS, NEG)
+MP_BIT2CBINOP(1010, 0101, 0101, 0101, 0101, NEG, NEG, POS, POS)
+MP_BIT2CBINOP(1011, 0100, 0001, 0111, 0010, NEG, NEG, POS, NEG)
+MP_BIT2CBINOP(1100, 0011, 0011, 0011, 0011, NEG, POS, NEG, POS)
+MP_BIT2CBINOP(1101, 0010, 0111, 0001, 0100, NEG, POS, NEG, NEG)
+MP_BIT2CBINOP(1110, 0001, 0100, 0010, 0111, NEG, NEG, NEG, POS)
+MP_BIT2CBINOP(1111, 0000, 0000, 0000, 0000, NEG, NEG, NEG, NEG)
+#undef NEG
+#undef POS
+
+/* --- @mp_not2c@ --- *
+ *
+ * Arguments: @mp *d@ = destination
+ * @mp *a@ = source
+ *
+ * Returns: The sign-extended complement of the argument.
+ */
+
+mp *mp_not2c(mp *d, mp *a)
+{
+ mpw one = 1;
+
+ MP_DEST(d, MP_LEN(a) + 1, a->f);
+ if (d == a) {
+ if (a->f & MP_NEG)
+ MPX_USUBN(d->v, d->vl, 1);
+ else
+ MPX_UADDN(d->v, d->vl, 1);
+ } else {
+ if (a->f & MP_NEG)
+ mpx_usub(d->v, d->vl, a->v, a->vl, &one, &one + 1);
+ else
+ mpx_uadd(d->v, d->vl, a->v, a->vl, &one, &one + 1);
+ }
+ d->f = (a->f & (MP_NEG | MP_BURN)) ^ MP_NEG;
+ MP_SHRINK(d);
+ return (d);
+}
+