math/fgoldi.c: Add support for Hamburg's `Goldilocks' field.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 26 Apr 2017 10:54:29 +0000 (11:54 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 29 Apr 2017 11:29:22 +0000 (12:29 +0100)
GF(2^448 - 2^224 - 1).

math/Makefile.am
math/fgoldi.c [new file with mode: 0644]
math/fgoldi.h [new file with mode: 0644]
math/t/fgoldi [new file with mode: 0644]

index 49d8b33..1eb0534 100644 (file)
@@ -431,6 +431,19 @@ f25519_p10_t_CPPFLAGS      += -DF25519_IMPL=10
 f25519_p10_t_LDADD      = $(TEST_LIBS) $(top_builddir)/libcatacomb.la
 f25519_p10_t_LDADD     += $(mLib_LIBS) $(CATACOMB_LIBS) $(LIBS)
 
+pkginclude_HEADERS     += fgoldi.h
+libmath_la_SOURCES     += fgoldi.c
+TESTS                  += fgoldi.t$(EXEEXT)
+TESTS                  += fgoldi-p12.t$(EXEEXT)
+EXTRA_DIST             += t/fgoldi
+
+check_PROGRAMS         += fgoldi-p12.t
+fgoldi_p12_t_SOURCES    = fgoldi.c
+fgoldi_p12_t_CPPFLAGS   = $(AM_CPPFLAGS) -DTEST_RIG -DSRCDIR="\"$(srcdir)\""
+fgoldi_p12_t_CPPFLAGS  += -DFGOLDI_IMPL=12
+fgoldi_p12_t_LDADD      = $(TEST_LIBS) $(top_builddir)/libcatacomb.la
+fgoldi_p12_t_LDADD     += $(mLib_LIBS) $(CATACOMB_LIBS) $(LIBS)
+
 pkginclude_HEADERS     += scaf.h
 libmath_la_SOURCES     += scaf.c
 
diff --git a/math/fgoldi.c b/math/fgoldi.c
new file mode 100644 (file)
index 0000000..5b2889f
--- /dev/null
@@ -0,0 +1,988 @@
+/* -*-c-*-
+ *
+ * Arithmetic in the Goldilocks field GF(2^448 - 2^224 - 1)
+ *
+ * (c) 2017 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include "fgoldi.h"
+
+/*----- Basic setup -------------------------------------------------------*
+ *
+ * Let φ = 2^224; then p = φ^2 - φ - 1, and, in GF(p), we have φ^2 = φ + 1
+ * (hence the name).
+ */
+
+#if FGOLDI_IMPL == 28
+/* We represent an element of GF(p) as 16 28-bit signed integer pieces x_i:
+ * x = SUM_{0<=i<16} x_i 2^(28i).
+ */
+
+typedef  int32  piece; typedef  int64  dblpiece;
+typedef uint32 upiece; typedef uint64 udblpiece;
+#define PIECEWD(i) 28
+#define NPIECE 16
+#define P p28
+
+#define B28 0x10000000u
+#define B27 0x08000000u
+#define M28 0x0fffffffu
+#define M27 0x07ffffffu
+#define M32 0xffffffffu
+
+#elif FGOLDI_IMPL == 12
+/* We represent an element of GF(p) as 40 signed integer pieces x_i: x =
+ * SUM_{0<=i<40} x_i 2^ceil(224i/20).  Pieces i with i == 0 (mod 5) are 12
+ * bits wide; the others are 11 bits wide, so they form eight groups of 56
+ * bits.
+ */
+
+typedef  int16  piece; typedef  int32  dblpiece;
+typedef uint16 upiece; typedef uint32 udblpiece;
+#define PIECEWD(i) ((i)%5 ? 11 : 12)
+#define NPIECE 40
+#define P p12
+
+#define B12 0x1000u
+#define B11 0x0800u
+#define B10 0x0400u
+#define M12 0xfffu
+#define M11 0x7ffu
+#define M10 0x3ffu
+#define M8 0xffu
+
+#endif
+
+/*----- Debugging machinery -----------------------------------------------*/
+
+#if defined(FGOLDI_DEBUG) || defined(TEST_RIG)
+
+#include <stdio.h>
+
+#include "mp.h"
+#include "mptext.h"
+
+static mp *get_pgoldi(void)
+{
+  mp *p = MP_NEW, *t = MP_NEW;
+
+  p = mp_setbit(p, MP_ZERO, 448);
+  t = mp_setbit(t, MP_ZERO, 224);
+  p = mp_sub(p, p, t);
+  p = mp_sub(p, p, MP_ONE);
+  mp_drop(t);
+  return (p);
+}
+
+DEF_FDUMP(fdump, piece, PIECEWD, NPIECE, 56, get_pgoldi())
+
+#endif
+
+/*----- Loading and storing -----------------------------------------------*/
+
+/* --- @fgoldi_load@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to store the result
+ *             @const octet xv[56]@ = source to read
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads an element of %$\gf{2^{448} - 2^{224} - 19}$% in
+ *             external representation from @xv@ and stores it in @z@.
+ *
+ *             External representation is little-endian base-256.  Some
+ *             elements have multiple encodings, which are not produced by
+ *             correct software; use of noncanonical encodings is not an
+ *             error, and toleration of them is considered a performance
+ *             feature.
+ */
+
+void fgoldi_load(fgoldi *z, const octet xv[56])
+{
+#if FGOLDI_IMPL == 28
+
+  unsigned i;
+  uint32 xw[14];
+  piece b, c;
+
+  /* First, read the input value as words. */
+  for (i = 0; i < 14; i++) xw[i] = LOAD32_L(xv + 4*i);
+
+  /* Extract unsigned 28-bit pieces from the words. */
+  z->P[ 0] = (xw[ 0] >> 0)&M28;
+  z->P[ 7] = (xw[ 6] >> 4)&M28;
+  z->P[ 8] = (xw[ 7] >> 0)&M28;
+  z->P[15] = (xw[13] >> 4)&M28;
+  for (i = 1; i < 7; i++) {
+    z->P[i + 0] = ((xw[i + 0] << (4*i)) | (xw[i - 1] >> (32 - 4*i)))&M28;
+    z->P[i + 8] = ((xw[i + 7] << (4*i)) | (xw[i + 6] >> (32 - 4*i)))&M28;
+  }
+
+  /* Convert the nonnegative pieces into a balanced signed representation, so
+   * each piece ends up in the interval |z_i| <= 2^27.  For each piece, if
+   * its top bit is set, lend a bit leftwards; in the case of z_15, reduce
+   * this bit by adding it onto z_0 and z_8, since this is the φ^2 bit, and
+   * φ^2 = φ + 1.  We delay this carry until after all of the pieces have
+   * been balanced.  If we don't do this, then we have to do a more expensive
+   * test for nonzeroness to decide whether to lend a bit leftwards rather
+   * than just testing a single bit.
+   *
+   * Note that we don't try for a canonical representation here: both upper
+   * and lower bounds are achievable.
+   */
+  b = z->P[15]&B27; z->P[15] -= b << 1; c = b >> 27;
+  for (i = NPIECE - 1; i--; )
+    { b = z->P[i]&B27; z->P[i] -= b << 1; z->P[i + 1] += b >> 27; }
+  z->P[0] += c; z->P[8] += c;
+
+#elif FGOLDI_IMPL == 12
+
+  unsigned i, j, n, w, b;
+  uint32 a;
+  int c;
+
+  /* First, convert the bytes into nonnegative pieces. */
+  for (i = j = a = n = 0, w = PIECEWD(0); i < 56; i++) {
+    a |= (uint32)xv[i] << n; n += 8;
+    if (n >= w) {
+      z->P[j++] = a&MASK(w);
+      a >>= w; n -= w; w = PIECEWD(j);
+    }
+  }
+
+  /* Convert the nonnegative pieces into a balanced signed representation, so
+   * each piece ends up in the interval |z_i| <= 2^11 + 1.
+   */
+  b = z->P[39]&B10; z->P[39] -= b << 1; c = b >> 10;
+  for (i = NPIECE - 1; i--; ) {
+    w = PIECEWD(i) - 1;
+    b = z->P[i]&BIT(w);
+    z->P[i] -= b << 1;
+    z->P[i + 1] += b >> w;
+  }
+  z->P[0] += c; z->P[20] += c;
+
+#endif
+}
+
+/* --- @fgoldi_store@ --- *
+ *
+ * Arguments:  @octet zv[56]@ = where to write the result
+ *             @const fgoldi *x@ = the field element to write
+ *
+ * Returns:    ---
+ *
+ * Use:                Stores a field element in the given octet vector in external
+ *             representation.  A canonical encoding is always stored.
+ */
+
+void fgoldi_store(octet zv[56], const fgoldi *x)
+{
+#if FGOLDI_IMPL == 28
+
+  piece y[NPIECE], yy[NPIECE], c, d;
+  uint32 u, v;
+  mask32 m;
+  unsigned i;
+
+  for (i = 0; i < NPIECE; i++) y[i] = x->P[i];
+
+  /* First, propagate the carries.  By the end of this, we'll have all of the
+   * the pieces canonically sized and positive, and maybe there'll be
+   * (signed) carry out.  The carry c is in { -1, 0, +1 }, and the remaining
+   * value will be in the half-open interval [0, φ^2).  The whole represented
+   * value is then y + φ^2 c.
+   *
+   * Assume that we start out with |y_i| <= 2^30.  We start off by cutting
+   * off and reducing the carry c_15 from the topmost piece, y_15.  This
+   * leaves 0 <= y_15 < 2^28; and we'll have |c_15| <= 4.  We'll add this
+   * onto y_0 and y_8, and propagate the carries.  It's very clear that we'll
+   * end up with |y + (φ + 1) c_15 - φ^2/2| << φ^2.
+   *
+   * Here, the y_i are signed, so we must be cautious about bithacking them.
+   */
+  c = ASR(piece, y[15], 28); y[15] = (upiece)y[15]&M28; y[8] += c;
+  for (i = 0; i < NPIECE; i++)
+    { y[i] += c; c = ASR(piece, y[i], 28); y[i] = (upiece)y[i]&M28; }
+
+  /* Now we have a slightly fiddly job to do.  If c = +1, or if c = 0 and
+   * y >= p, then we should subtract p from the whole value; if c = -1 then
+   * we should add p; and otherwise we should do nothing.
+   *
+   * But conditional behaviour is bad, m'kay.  So here's what we do instead.
+   *
+   * The first job is to sort out what we wanted to do.  If c = -1 then we
+   * want to (a) invert the constant addend and (b) feed in a carry-in;
+   * otherwise, we don't.
+   */
+  m = SIGN(c)&M28;
+  d = m&1;
+
+  /* Now do the addition/subtraction.  Remember that all of the y_i are
+   * nonnegative, so shifting and masking are safe and easy.
+   */
+      d += y[0] + (1 ^ m); yy[0] = d&M28; d >>= 28;
+  for (i = 1; i < 8; i++)
+    { d += y[i] +      m;  yy[i] = d&M28; d >>= 28; }
+      d += y[8] + (1 ^ m); yy[8] = d&M28; d >>= 28;
+  for (i = 9; i < 16; i++)
+    { d += y[i] +      m;  yy[i] = d&M28; d >>= 28; }
+
+  /* The final carry-out is in d; since we only did addition, and the y_i are
+   * nonnegative, then d is in { 0, 1 }.  We want to keep y', rather than y,
+   * if (a) c /= 0 (in which case we know that the old value was
+   * unsatisfactory), or (b) if d = 1 (in which case, if c = 0, we know that
+   * the subtraction didn't cause a borrow, so we must be in the case where
+   * p <= y < φ^2.
+   */
+  m = NONZEROP(c) | ~NONZEROP(d - 1);
+  for (i = 0; i < NPIECE; i++) y[i] = (yy[i]&m) | (y[i]&~m);
+
+  /* Extract 32-bit words from the value. */
+  for (i = 0; i < 7; i++) {
+    u = ((y[i + 0] >> (4*i)) | ((uint32)y[i + 1] << (28 - 4*i)))&M32;
+    v = ((y[i + 8] >> (4*i)) | ((uint32)y[i + 9] << (28 - 4*i)))&M32;
+    STORE32_L(zv + 4*i,             u);
+    STORE32_L(zv + 4*i + 28, v);
+  }
+
+#elif FGOLDI_IMPL == 12
+
+  piece y[NPIECE], yy[NPIECE], c, d;
+  uint32 a;
+  mask32 m, mm;
+  unsigned i, j, n, w;
+
+  for (i = 0; i < NPIECE; i++) y[i] = x->P[i];
+
+  /* First, propagate the carries.  By the end of this, we'll have all of the
+   * the pieces canonically sized and positive, and maybe there'll be
+   * (signed) carry out.  The carry c is in { -1, 0, +1 }, and the remaining
+   * value will be in the half-open interval [0, φ^2).  The whole represented
+   * value is then y + φ^2 c.
+   *
+   * Assume that we start out with |y_i| <= 2^14.  We start off by cutting
+   * off and reducing the carry c_39 from the topmost piece, y_39.  This
+   * leaves 0 <= y_39 < 2^11; and we'll have |c_39| <= 16.  We'll add this
+   * onto y_0 and y_20, and propagate the carries.  It's very clear that
+   * we'll end up with |y + (φ + 1) c_39 - φ^2/2| << φ^2.
+   *
+   * Here, the y_i are signed, so we must be cautious about bithacking them.
+   */
+  c = ASR(piece, y[39], 11); y[39] = (piece)y[39]&M11; y[20] += c;
+  for (i = 0; i < NPIECE; i++) {
+    w = PIECEWD(i); m = (1 << w) - 1;
+    y[i] += c; c = ASR(piece, y[i], w); y[i] = (upiece)y[i]&m;
+  }
+
+  /* Now we have a slightly fiddly job to do.  If c = +1, or if c = 0 and
+   * y >= p, then we should subtract p from the whole value; if c = -1 then
+   * we should add p; and otherwise we should do nothing.
+   *
+   * But conditional behaviour is bad, m'kay.  So here's what we do instead.
+   *
+   * The first job is to sort out what we wanted to do.  If c = -1 then we
+   * want to (a) invert the constant addend and (b) feed in a carry-in;
+   * otherwise, we don't.
+   */
+  mm = SIGN(c);
+  d = m&1;
+
+  /* Now do the addition/subtraction.  Remember that all of the y_i are
+   * nonnegative, so shifting and masking are safe and easy.
+   */
+    d += y[ 0] + (1 ^ (mm&M12)); yy[ 0] = d&M12; d >>= 12;
+  for (i = 1; i < 20; i++) {
+    w = PIECEWD(i); m = MASK(w);
+    d += y[ i] +      (mm&m);    yy[ i] = d&m;   d >>= w;
+  }
+    d += y[20] + (1 ^ (mm&M12)); yy[20] = d&M12; d >>= 12;
+  for (i = 21; i < 40; i++) {
+    w = PIECEWD(i); m = MASK(w);
+    d += y[ i] +      (mm&m);    yy[ i] = d&m;   d >>= w;
+  }
+
+  /* The final carry-out is in d; since we only did addition, and the y_i are
+   * nonnegative, then d is in { 0, 1 }.  We want to keep y', rather than y,
+   * if (a) c /= 0 (in which case we know that the old value was
+   * unsatisfactory), or (b) if d = 1 (in which case, if c = 0, we know that
+   * the subtraction didn't cause a borrow, so we must be in the case where
+   * p <= y < φ^2.
+   */
+  m = NONZEROP(c) | ~NONZEROP(d - 1);
+  for (i = 0; i < NPIECE; i++) y[i] = (yy[i]&m) | (y[i]&~m);
+
+  /* Convert that back into octets. */
+  for (i = j = a = n = 0; i < NPIECE; i++) {
+    a |= (uint32)y[i] << n; n += PIECEWD(i);
+    while (n >= 8) { zv[j++] = a&M8; a >>= 8; n -= 8; }
+  }
+
+#endif
+}
+
+/* --- @fgoldi_set@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to write the result
+ *             @int a@ = a small-ish constant
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets @z@ to equal @a@.
+ */
+
+void fgoldi_set(fgoldi *x, int a)
+{
+  unsigned i;
+
+  x->P[0] = a;
+  for (i = 1; i < NPIECE; i++) x->P[i] = 0;
+}
+
+/*----- Basic arithmetic --------------------------------------------------*/
+
+/* --- @fgoldi_add@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@ or @y@)
+ *             @const fgoldi *x, *y@ = two operands
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the sum %$x + y$%.
+ */
+
+void fgoldi_add(fgoldi *z, const fgoldi *x, const fgoldi *y)
+{
+  unsigned i;
+  for (i = 0; i < NPIECE; i++) z->P[i] = x->P[i] + y->P[i];
+}
+
+/* --- @fgoldi_sub@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@ or @y@)
+ *             @const fgoldi *x, *y@ = two operands
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the difference %$x - y$%.
+ */
+
+void fgoldi_sub(fgoldi *z, const fgoldi *x, const fgoldi *y)
+{
+  unsigned i;
+  for (i = 0; i < NPIECE; i++) z->P[i] = x->P[i] - y->P[i];
+}
+
+/*----- Constant-time utilities -------------------------------------------*/
+
+/* --- @fgoldi_condswap@ --- *
+ *
+ * Arguments:  @fgoldi *x, *y@ = two operands
+ *             @uint32 m@ = a mask
+ *
+ * Returns:    ---
+ *
+ * Use:                If @m@ is zero, do nothing; if @m@ is all-bits-set, then
+ *             exchange @x@ and @y@.  If @m@ has some other value, then
+ *             scramble @x@ and @y@ in an unhelpful way.
+ */
+
+void fgoldi_condswap(fgoldi *x, fgoldi *y, uint32 m)
+{
+  unsigned i;
+  mask32 mm = FIX_MASK32(m);
+
+  for (i = 0; i < NPIECE; i++) CONDSWAP(x->P[i], y->P[i], mm);
+}
+
+/*----- Multiplication ----------------------------------------------------*/
+
+#if FGOLDI_IMPL == 28
+
+/* Let B = 2^63 - 1 be the largest value such that +B and -B can be
+ * represented in a double-precision piece.  On entry, it must be the case
+ * that |X_i| <= M <= B - 2^27 for some M.  If this is the case, then, on
+ * exit, we will have |Z_i| <= 2^27 + M/2^27.
+ */
+#define CARRY_REDUCE(z, x) do {                                                \
+  dblpiece _t[NPIECE], _c;                                             \
+  unsigned _i;                                                         \
+                                                                       \
+  /* Bias the input pieces.  This keeps the carries and so on centred  \
+   * around zero rather than biased positive.                          \
+   */                                                                  \
+  for (_i = 0; _i < NPIECE; _i++) _t[_i] = (x)[_i] + B27;              \
+                                                                       \
+  /* Calculate the reduced pieces.  Careful with the bithacking. */    \
+  _c = ASR(dblpiece, _t[15], 28);                                      \
+  (z)[0] = (dblpiece)((udblpiece)_t[0]&M28) - B27 + _c;                        \
+  for (_i = 1; _i < NPIECE; _i++) {                                    \
+    (z)[_i] = (dblpiece)((udblpiece)_t[_i]&M28) - B27 +                        \
+      ASR(dblpiece, _t[_i - 1], 28);                                   \
+  }                                                                    \
+  (z)[8] += _c;                                                                \
+} while (0)
+
+#elif FGOLDI_IMPL == 12
+
+static void carry_reduce(dblpiece x[NPIECE])
+{
+  /* Initial bounds: we assume |x_i| < 2^31 - 2^27. */
+
+  unsigned i, j;
+  dblpiece c;
+
+  /* The result is nearly canonical, because we do sequential carry
+   * propagation, because smaller processors are more likely to prefer the
+   * smaller working set than the instruction-level parallelism.
+   *
+   * Start at x_37; truncate it to 10 bits, and propagate the carry to x_38.
+   * Truncate x_38 to 10 bits, and add the carry onto x_39.  Truncate x_39 to
+   * 10 bits, and add the carry onto x_0 and x_20.  And so on.
+   *
+   * Once we reach x_37 for the second time, we start with |x_37| <= 2^10.
+   * The carry into x_37 is at most 2^21; so the carry out into x_38 has
+   * magnitude at most 2^10.  In turn, |x_38| <= 2^10 before the carry, so is
+   * now no more than 2^11 in magnitude, and the carry out into x_39 is at
+   * most 1.  This leaves |x_39| <= 2^10 + 1 after carry propagation.
+   *
+   * Be careful with the bit hacking because the quantities involved are
+   * signed.
+   */
+
+  /* For each piece, we bias it so that floor division (as done by an
+   * arithmetic right shift) and modulus (as done by bitwise-AND) does the
+   * right thing.
+   */
+#define CARRY(i, wd, b, m) do {                                                \
+  x[i] += (b);                                                         \
+  c = ASR(dblpiece, x[i], (wd));                                       \
+  x[i] = (dblpiece)((udblpiece)x[i]&(m)) - (b);                                \
+} while (0)
+
+                            {             CARRY(37, 11, B10, M11);      }
+                            { x[38] += c; CARRY(38, 11, B10, M11);      }
+                            { x[39] += c; CARRY(39, 11, B10, M11);      }
+                              x[20] += c;
+  for (i = 0; i < 35; ) {
+                            {  x[i] += c; CARRY( i, 12, B11, M12); i++; }
+    for (j = i + 4; i < j; ) {  x[i] += c; CARRY( i, 11, B10, M11); i++; }
+  }
+                            {  x[i] += c; CARRY( i, 12, B11, M12); i++; }
+  while (i < 39)            {  x[i] += c; CARRY( i, 11, B10, M11); i++; }
+  x[39] += c;
+}
+
+#endif
+
+/* --- @fgoldi_mulconst@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@)
+ *             @const fgoldi *x@ = an operand
+ *             @long a@ = a small-ish constant; %$|a| < 2^{20}$%.
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the product %$a x$%.
+ */
+
+void fgoldi_mulconst(fgoldi *z, const fgoldi *x, long a)
+{
+  unsigned i;
+  dblpiece zz[NPIECE], aa = a;
+
+  for (i = 0; i < NPIECE; i++) zz[i] = aa*x->P[i];
+#if FGOLDI_IMPL == 28
+  CARRY_REDUCE(z->P, zz);
+#elif FGOLDI_IMPL == 12
+  carry_reduce(zz);
+  for (i = 0; i < NPIECE; i++) z->P[i] = zz[i];
+#endif
+}
+
+/* --- @fgoldi_mul@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@ or @y@)
+ *             @const fgoldi *x, *y@ = two operands
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the product %$x y$%.
+ */
+
+void fgoldi_mul(fgoldi *z, const fgoldi *x, const fgoldi *y)
+{
+  dblpiece zz[NPIECE], u[NPIECE];
+  piece ab[NPIECE/2], cd[NPIECE/2];
+  const piece
+    *a = x->P + NPIECE/2, *b = x->P,
+    *c = y->P + NPIECE/2, *d = y->P;
+  unsigned i, j;
+
+#if FGOLDI_IMPL == 28
+
+#  define M(x,i, y,j) ((dblpiece)(x)[i]*(y)[j])
+
+#elif FGOLDI_IMPL == 12
+
+  static const unsigned short off[39] = {
+      0,  12,  23,  34,  45,  56,  68,  79,  90, 101,
+    112, 124, 135, 146, 157, 168, 180, 191, 202, 213,
+    224, 236, 247, 258, 269, 280, 292, 303, 314, 325,
+    336, 348, 359, 370, 381, 392, 404, 415, 426
+  };
+
+#define M(x,i, y,j)                                                    \
+  (((dblpiece)(x)[i]*(y)[j]) << (off[i] + off[j] - off[(i) + (j)]))
+
+#endif
+
+  /* Behold the magic.
+   *
+   * Write x = a φ + b, and y = c φ + d.  Then x y = a c φ^2 +
+   * (a d + b c) φ + b d.  Karatsuba and Ofman observed that a d + b c =
+   * (a + b) (c + d) - a c - b d, saving a multiplication, and Hamburg chose
+   * the prime p so that φ^2 = φ + 1.  So
+   *
+   *   x y = ((a + b) (c + d) - b d) φ + a c + b d
+   */
+
+  for (i = 0; i < NPIECE; i++) zz[i] = 0;
+
+  /* Our first job will be to calculate (1 - φ) b d, and write the result
+   * into z.  As we do this, an interesting thing will happen.  Write
+   * b d = u φ + v; then (1 - φ) b d = u φ + v - u φ^2 - v φ = (1 - φ) v - u.
+   * So, what we do is to write the product end-swapped and negated, and then
+   * we'll subtract the (negated, remember) high half from the low half.
+   */
+  for (i = 0; i < NPIECE/2; i++) {
+    for (j = 0; j < NPIECE/2 - i; j++)
+      zz[i + j + NPIECE/2] -= M(b,i, d,j);
+    for (; j < NPIECE/2; j++)
+      zz[i + j - NPIECE/2] -= M(b,i, d,j);
+  }
+  for (i = 0; i < NPIECE/2; i++)
+    zz[i] -= zz[i + NPIECE/2];
+
+  /* Next, we add on a c.  There are no surprises here. */
+  for (i = 0; i < NPIECE/2; i++)
+    for (j = 0; j < NPIECE/2; j++)
+      zz[i + j] += M(a,i, c,j);
+
+  /* Now, calculate a + b and c + d. */
+  for (i = 0; i < NPIECE/2; i++)
+    { ab[i] = a[i] + b[i]; cd[i] = c[i] + d[i]; }
+
+  /* Finally (for the multiplication) we must add on (a + b) (c + d) φ.
+   * Write (a + b) (c + d) as u φ + v; then we actually want u φ^2 + v φ =
+   * v φ + (1 + φ) u.  We'll store u in a temporary place and add it on
+   * twice.
+   */
+  for (i = 0; i < NPIECE; i++) u[i] = 0;
+  for (i = 0; i < NPIECE/2; i++) {
+    for (j = 0; j < NPIECE/2 - i; j++)
+      zz[i + j + NPIECE/2] += M(ab,i, cd,j);
+    for (; j < NPIECE/2; j++)
+      u[i + j - NPIECE/2] += M(ab,i, cd,j);
+  }
+  for (i = 0; i < NPIECE/2; i++)
+    { zz[i] += u[i]; zz[i + NPIECE/2] += u[i]; }
+
+#undef M
+
+#if FGOLDI_IMPL == 28
+  /* That wraps it up for the multiplication.  Let's figure out some bounds.
+   * Fortunately, Karatsuba is a polynomial identity, so all of the pieces
+   * end up the way they'd be if we'd done the thing the easy way, which
+   * simplifies the analysis.  Suppose we started with |x_i|, |y_i| <= 9/5
+   * 2^28.  The overheads in the result are given by the coefficients of
+   *
+   *   ((u^16 - 1)/(u - 1))^2 mod u^16 - u^8 - 1
+   *
+   * the greatest of which is 38.  So |z_i| <= 38*81/25*2^56 < 2^63.
+   *
+   * Anyway, a round of `CARRY_REDUCE' will leave us with |z_i| < 2^27 +
+   * 2^36; and a second round will leave us with |z_i| < 2^27 + 512.
+   */
+  for (i = 0; i < 2; i++) CARRY_REDUCE(zz, zz);
+  for (i = 0; i < NPIECE; i++) z->P[i] = zz[i];
+#elif FGOLDI_IMPL == 12
+  carry_reduce(zz);
+  for (i = 0; i < NPIECE; i++) z->P[i] = zz[i];
+#endif
+}
+
+/* --- @fgoldi_sqr@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@ or @y@)
+ *             @const fgoldi *x@ = an operand
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the square %$x^2$%.
+ */
+
+void fgoldi_sqr(fgoldi *z, const fgoldi *x)
+{
+#if FGOLDI_IMPL == 28
+
+  dblpiece zz[NPIECE], u[NPIECE];
+  piece ab[NPIECE];
+  const piece *a = x->P + NPIECE/2, *b = x->P;
+  unsigned i, j;
+
+#  define M(x,i, y,j) ((dblpiece)(x)[i]*(y)[j])
+
+  /* The magic is basically the same as `fgoldi_mul' above.  We write
+   * x = a φ + b and use Karatsuba and the special prime shape.  This time,
+   * we have
+   *
+   *   x^2 = ((a + b)^2 - b^2) φ + a^2 + b^2
+   */
+
+  for (i = 0; i < NPIECE; i++) zz[i] = 0;
+
+  /* Our first job will be to calculate (1 - φ) b^2, and write the result
+   * into z.  Again, this interacts pleasantly with the prime shape.
+   */
+  for (i = 0; i < NPIECE/4; i++) {
+    zz[2*i + NPIECE/2] -= M(b,i, b,i);
+    for (j = i + 1; j < NPIECE/2 - i; j++)
+      zz[i + j + NPIECE/2] -= 2*M(b,i, b,j);
+    for (; j < NPIECE/2; j++)
+      zz[i + j - NPIECE/2] -= 2*M(b,i, b,j);
+  }
+  for (; i < NPIECE/2; i++) {
+    zz[2*i - NPIECE/2] -= M(b,i, b,i);
+    for (j = i + 1; j < NPIECE/2; j++)
+      zz[i + j - NPIECE/2] -= 2*M(b,i, b,j);
+  }
+  for (i = 0; i < NPIECE/2; i++)
+    zz[i] -= zz[i + NPIECE/2];
+
+  /* Next, we add on a^2.  There are no surprises here. */
+  for (i = 0; i < NPIECE/2; i++) {
+    zz[2*i] += M(a,i, a,i);
+    for (j = i + 1; j < NPIECE/2; j++)
+      zz[i + j] += 2*M(a,i, a,j);
+  }
+
+  /* Now, calculate a + b. */
+  for (i = 0; i < NPIECE/2; i++)
+    ab[i] = a[i] + b[i];
+
+  /* Finally (for the multiplication) we must add on (a + b)^2 φ.
+   * Write (a + b)^2 as u φ + v; then we actually want (u + v) φ + u.  We'll
+   * store u in a temporary place and add it on twice.
+   */
+  for (i = 0; i < NPIECE; i++) u[i] = 0;
+  for (i = 0; i < NPIECE/4; i++) {
+    zz[2*i + NPIECE/2] += M(ab,i, ab,i);
+    for (j = i + 1; j < NPIECE/2 - i; j++)
+      zz[i + j + NPIECE/2] += 2*M(ab,i, ab,j);
+    for (; j < NPIECE/2; j++)
+      u[i + j - NPIECE/2] += 2*M(ab,i, ab,j);
+  }
+  for (; i < NPIECE/2; i++) {
+    u[2*i - NPIECE/2] += M(ab,i, ab,i);
+    for (j = i + 1; j < NPIECE/2; j++)
+      u[i + j - NPIECE/2] += 2*M(ab,i, ab,j);
+  }
+  for (i = 0; i < NPIECE/2; i++)
+    { zz[i] += u[i]; zz[i + NPIECE/2] += u[i]; }
+
+#undef M
+
+  /* Finally, carrying. */
+  for (i = 0; i < 2; i++) CARRY_REDUCE(zz, zz);
+  for (i = 0; i < NPIECE; i++) z->P[i] = zz[i];
+
+#elif FGOLDI_IMPL == 12
+  fgoldi_mul(z, x, x);
+#endif
+}
+
+/*----- More advanced operations ------------------------------------------*/
+
+/* --- @fgoldi_inv@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@)
+ *             @const fgoldi *x@ = an operand
+ *
+ * Returns:    ---
+ *
+ * Use:                Stores in @z@ the multiplicative inverse %$x^{-1}$%.  If
+ *             %$x = 0$% then @z@ is set to zero.  This is considered a
+ *             feature.
+ */
+
+void fgoldi_inv(fgoldi *z, const fgoldi *x)
+{
+  fgoldi t, u;
+  unsigned i;
+
+#define SQRN(z, x, n) do {                                             \
+  fgoldi_sqr((z), (x));                                                        \
+  for (i = 1; i < (n); i++) fgoldi_sqr((z), (z));                      \
+} while (0)
+
+  /* Calculate x^-1 = x^(p - 2) = x^(2^448 - 2^224 - 3), which also handles
+   * x = 0 as intended.  The addition chain is home-made.
+   */                                  /* step | value */
+  fgoldi_sqr(&u, x);                   /*    1 | 2 */
+  fgoldi_mul(&t, &u, x);               /*    2 | 3 */
+  SQRN(&u, &t, 2);                     /*    4 | 12 */
+  fgoldi_mul(&t, &u, &t);              /*    5 | 15 */
+  SQRN(&u, &t, 4);                     /*    9 | 240 */
+  fgoldi_mul(&u, &u, &t);              /*   10 | 255 = 2^8 - 1 */
+  SQRN(&u, &u, 4);                     /*   14 | 2^12 - 16 */
+  fgoldi_mul(&t, &u, &t);              /*   15 | 2^12 - 1 */
+  SQRN(&u, &t, 12);                    /*   27 | 2^24 - 2^12 */
+  fgoldi_mul(&u, &u, &t);              /*   28 | 2^24 - 1 */
+  SQRN(&u, &u, 12);                    /*   40 | 2^36 - 2^12 */
+  fgoldi_mul(&t, &u, &t);              /*   41 | 2^36 - 1 */
+  fgoldi_sqr(&t, &t);                  /*   42 | 2^37 - 2 */
+  fgoldi_mul(&t, &t, x);               /*   43 | 2^37 - 1 */
+  SQRN(&u, &t, 37);                    /*   80 | 2^74 - 2^37 */
+  fgoldi_mul(&u, &u, &t);              /*   81 | 2^74 - 1 */
+  SQRN(&u, &u, 37);                    /*  118 | 2^111 - 2^37 */
+  fgoldi_mul(&t, &u, &t);              /*  119 | 2^111 - 1 */
+  SQRN(&u, &t, 111);                   /*  230 | 2^222 - 2^111 */
+  fgoldi_mul(&t, &u, &t);              /*  231 | 2^222 - 1 */
+  fgoldi_sqr(&u, &t);                  /*  232 | 2^223 - 2 */
+  fgoldi_mul(&u, &u, x);               /*  233 | 2^223 - 1 */
+  SQRN(&u, &u, 223);                   /*  456 | 2^446 - 2^223 */
+  fgoldi_mul(&t, &u, &t);              /*  457 | 2^446 - 2^222 - 1 */
+  SQRN(&t, &t, 2);                     /*  459 | 2^448 - 2^224 - 4 */
+  fgoldi_mul(z, &t, x);                        /*  460 | 2^448 - 2^224 - 3 */
+
+#undef SQRN
+}
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <mLib/report.h>
+#include <mLib/str.h>
+#include <mLib/testrig.h>
+
+static void fixdstr(dstr *d)
+{
+  if (d->len > 56)
+    die(1, "invalid length for fgoldi");
+  else if (d->len < 56) {
+    dstr_ensure(d, 56);
+    memset(d->buf + d->len, 0, 56 - d->len);
+    d->len = 56;
+  }
+}
+
+static void cvt_fgoldi(const char *buf, dstr *d)
+{
+  dstr dd = DSTR_INIT;
+
+  type_hex.cvt(buf, &dd); fixdstr(&dd);
+  dstr_ensure(d, sizeof(fgoldi)); d->len = sizeof(fgoldi);
+  fgoldi_load((fgoldi *)d->buf, (const octet *)dd.buf);
+  dstr_destroy(&dd);
+}
+
+static void dump_fgoldi(dstr *d, FILE *fp)
+  { fdump(stderr, "???", (const piece *)d->buf); }
+
+static void cvt_fgoldi_ref(const char *buf, dstr *d)
+  { type_hex.cvt(buf, d); fixdstr(d); }
+
+static void dump_fgoldi_ref(dstr *d, FILE *fp)
+{
+  fgoldi x;
+
+  fgoldi_load(&x, (const octet *)d->buf);
+  fdump(stderr, "???", x.P);
+}
+
+static int eq(const fgoldi *x, dstr *d)
+  { octet b[56]; fgoldi_store(b, x); return (memcmp(b, d->buf, 56) == 0); }
+
+static const test_type
+  type_fgoldi = { cvt_fgoldi, dump_fgoldi },
+  type_fgoldi_ref = { cvt_fgoldi_ref, dump_fgoldi_ref };
+
+#define TEST_UNOP(op)                                                  \
+  static int vrf_##op(dstr dv[])                                       \
+  {                                                                    \
+    fgoldi *x = (fgoldi *)dv[0].buf;                                   \
+    fgoldi z, zz;                                                      \
+    int ok = 1;                                                                \
+                                                                       \
+    fgoldi_##op(&z, x);                                                        \
+    if (!eq(&z, &dv[1])) {                                             \
+      ok = 0;                                                          \
+      fprintf(stderr, "failed!\n");                                    \
+      fdump(stderr, "x", x->P);                                                \
+      fdump(stderr, "calc", z.P);                                      \
+      fgoldi_load(&zz, (const octet *)dv[1].buf);                      \
+      fdump(stderr, "z", zz.P);                                                \
+    }                                                                  \
+                                                                       \
+    return (ok);                                                       \
+  }
+
+TEST_UNOP(sqr)
+TEST_UNOP(inv)
+
+#define TEST_BINOP(op)                                                 \
+  static int vrf_##op(dstr dv[])                                       \
+  {                                                                    \
+    fgoldi *x = (fgoldi *)dv[0].buf, *y = (fgoldi *)dv[1].buf;         \
+    fgoldi z, zz;                                                      \
+    int ok = 1;                                                                \
+                                                                       \
+    fgoldi_##op(&z, x, y);                                             \
+    if (!eq(&z, &dv[2])) {                                             \
+      ok = 0;                                                          \
+      fprintf(stderr, "failed!\n");                                    \
+      fdump(stderr, "x", x->P);                                                \
+      fdump(stderr, "y", y->P);                                                \
+      fdump(stderr, "calc", z.P);                                      \
+      fgoldi_load(&zz, (const octet *)dv[2].buf);                      \
+      fdump(stderr, "z", zz.P);                                                \
+    }                                                                  \
+                                                                       \
+    return (ok);                                                       \
+  }
+
+TEST_BINOP(add)
+TEST_BINOP(sub)
+TEST_BINOP(mul)
+
+static int vrf_mulc(dstr dv[])
+{
+  fgoldi *x = (fgoldi *)dv[0].buf;
+  long a = *(const long *)dv[1].buf;
+  fgoldi z, zz;
+  int ok = 1;
+
+  fgoldi_mulconst(&z, x, a);
+  if (!eq(&z, &dv[2])) {
+    ok = 0;
+    fprintf(stderr, "failed!\n");
+    fdump(stderr, "x", x->P);
+    fprintf(stderr, "a = %ld\n", a);
+    fdump(stderr, "calc", z.P);
+    fgoldi_load(&zz, (const octet *)dv[2].buf);
+    fdump(stderr, "z", zz.P);
+  }
+
+  return (ok);
+}
+
+static int vrf_condswap(dstr dv[])
+{
+  fgoldi *x = (fgoldi *)dv[0].buf, *y = (fgoldi *)dv[1].buf;
+  fgoldi xx = *x, yy = *y;
+  uint32 m = *(uint32 *)dv[2].buf;
+  int ok = 1;
+
+  fgoldi_condswap(&xx, &yy, m);
+  if (!eq(&xx, &dv[3]) || !eq(&yy, &dv[4])) {
+    ok = 0;
+    fprintf(stderr, "failed!\n");
+    fdump(stderr, "x", x->P);
+    fdump(stderr, "y", y->P);
+    fprintf(stderr, "m = 0x%08lx\n", (unsigned long)m);
+    fdump(stderr, "calc xx", xx.P);
+    fdump(stderr, "calc yy", yy.P);
+    fgoldi_load(&xx, (const octet *)dv[3].buf);
+    fgoldi_load(&yy, (const octet *)dv[4].buf);
+    fdump(stderr, "want xx", xx.P);
+    fdump(stderr, "want yy", yy.P);
+  }
+
+  return (ok);
+}
+
+static int vrf_sub_mulc_add_sub_mul(dstr dv[])
+{
+  fgoldi *u = (fgoldi *)dv[0].buf, *v = (fgoldi *)dv[1].buf,
+    *w = (fgoldi *)dv[3].buf, *x = (fgoldi *)dv[4].buf,
+    *y = (fgoldi *)dv[5].buf;
+  long a = *(const long *)dv[2].buf;
+  fgoldi umv, aumv, wpaumv, xmy, z, zz;
+  int ok = 1;
+
+  fgoldi_sub(&umv, u, v);
+  fgoldi_mulconst(&aumv, &umv, a);
+  fgoldi_add(&wpaumv, w, &aumv);
+  fgoldi_sub(&xmy, x, y);
+  fgoldi_mul(&z, &wpaumv, &xmy);
+
+  if (!eq(&z, &dv[6])) {
+    ok = 0;
+    fprintf(stderr, "failed!\n");
+    fdump(stderr, "u", u->P);
+    fdump(stderr, "v", v->P);
+    fdump(stderr, "u - v", umv.P);
+    fprintf(stderr, "a = %ld\n", a);
+    fdump(stderr, "a (u - v)", aumv.P);
+    fdump(stderr, "w + a (u - v)", wpaumv.P);
+    fdump(stderr, "x", x->P);
+    fdump(stderr, "y", y->P);
+    fdump(stderr, "x - y", xmy.P);
+    fdump(stderr, "(x - y) (w + a (u - v))", z.P);
+    fgoldi_load(&zz, (const octet *)dv[6].buf); fdump(stderr, "z", zz.P);
+  }
+
+  return (ok);
+}
+
+static test_chunk tests[] = {
+  { "add", vrf_add, { &type_fgoldi, &type_fgoldi, &type_fgoldi_ref } },
+  { "sub", vrf_sub, { &type_fgoldi, &type_fgoldi, &type_fgoldi_ref } },
+  { "mul", vrf_mul, { &type_fgoldi, &type_fgoldi, &type_fgoldi_ref } },
+  { "mulconst", vrf_mulc, { &type_fgoldi, &type_long, &type_fgoldi_ref } },
+  { "condswap", vrf_condswap,
+    { &type_fgoldi, &type_fgoldi, &type_uint32,
+      &type_fgoldi_ref, &type_fgoldi_ref } },
+  { "sqr", vrf_sqr, { &type_fgoldi, &type_fgoldi_ref } },
+  { "inv", vrf_inv, { &type_fgoldi, &type_fgoldi_ref } },
+  { "sub-mulc-add-sub-mul", vrf_sub_mulc_add_sub_mul,
+    { &type_fgoldi, &type_fgoldi, &type_long, &type_fgoldi,
+      &type_fgoldi, &type_fgoldi, &type_fgoldi_ref } },
+  { 0, 0, { 0 } }
+};
+
+int main(int argc, char *argv[])
+{
+  test_run(argc, argv, tests, SRCDIR "/t/fgoldi");
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/math/fgoldi.h b/math/fgoldi.h
new file mode 100644 (file)
index 0000000..b05fd77
--- /dev/null
@@ -0,0 +1,202 @@
+/* -*-c-*-
+ *
+ * Arithmetic in the Goldilocks field GF(2^448 - 2^224 - 1)
+ *
+ * (c) 2017 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef CATACOMB_FGOLDI_H
+#define CATACOMB_FGOLDI_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+#ifndef CATACOMB_QFARITH_H
+#  include "qfarith.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef union {
+  int32 p28[16];
+  int16 p12[40];
+} fgoldi;
+
+#if !defined(FGOLDI_IMPL) && defined(HAVE_INT64)
+#  define FGOLDI_IMPL 28
+#endif
+
+#ifndef FGOLDI_IMPL
+#  define FGOLDI_IMPL 12
+#endif
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @fgoldi_load@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to store the result
+ *             @const octet xv[56]@ = source to read
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads an element of %$\gf{2^{448} - 2^{224} - 19}$% in
+ *             external representation from @xv@ and stores it in @z@.
+ *
+ *             External representation is little-endian base-256.  Some
+ *             elements have multiple encodings, which are not produced by
+ *             correct software; use of noncanonical encodings is not an
+ *             error, and toleration of them is considered a performance
+ *             feature.
+ */
+
+extern void fgoldi_load(fgoldi */*z*/, const octet /*xv*/[56]);
+
+/* --- @fgoldi_store@ --- *
+ *
+ * Arguments:  @octet zv[56]@ = where to write the result
+ *             @const fgoldi *x@ = the field element to write
+ *
+ * Returns:    ---
+ *
+ * Use:                Stores a field element in the given octet vector in external
+ *             representation.  A canonical encoding is always stored.
+ */
+
+extern void fgoldi_store(octet /*zv*/[56], const fgoldi */*x*/);
+
+/* --- @fgoldi_set@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to write the result
+ *             @int a@ = a small-ish constant
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets @z@ to equal @a@.
+ */
+
+extern void fgoldi_set(fgoldi */*x*/, int /*a*/);
+
+/* --- @fgoldi_add@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@ or @y@)
+ *             @const fgoldi *x, *y@ = two operands
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the sum %$x + y$%.
+ */
+
+extern void fgoldi_add(fgoldi */*z*/,
+                      const fgoldi */*x*/, const fgoldi */*y*/);
+
+/* --- @fgoldi_sub@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@ or @y@)
+ *             @const fgoldi *x, *y@ = two operands
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the difference %$x - y$%.
+ */
+
+extern void fgoldi_sub(fgoldi */*z*/,
+                      const fgoldi */*x*/, const fgoldi */*y*/);
+
+/* --- @fgoldi_condswap@ --- *
+ *
+ * Arguments:  @fgoldi *x, *y@ = two operands
+ *             @uint32 m@ = a mask
+ *
+ * Returns:    ---
+ *
+ * Use:                If @m@ is zero, do nothing; if @m@ is all-bits-set, then
+ *             exchange @x@ and @y@.  If @m@ has some other value, then
+ *             scramble @x@ and @y@ in an unhelpful way.
+ */
+
+extern void fgoldi_condswap(fgoldi */*x*/, fgoldi */*y*/, uint32 /*m*/);
+
+/* --- @fgoldi_mulconst@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@)
+ *             @const fgoldi *x@ = an operand
+ *             @long a@ = a small-ish constant; %$|a| < 2^{20}$%.
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the product %$a x$%.
+ */
+
+extern void fgoldi_mulconst(fgoldi */*z*/, const fgoldi */*x*/, long /*a*/);
+
+/* --- @fgoldi_mul@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@ or @y@)
+ *             @const fgoldi *x, *y@ = two operands
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the product %$x y$%.
+ */
+
+extern void fgoldi_mul(fgoldi */*z*/,
+                      const fgoldi */*x*/, const fgoldi */*y*/);
+
+/* --- @fgoldi_sqr@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@ or @y@)
+ *             @const fgoldi *x@ = an operand
+ *
+ * Returns:    ---
+ *
+ * Use:                Set @z@ to the square %$x^2$%.
+ */
+
+extern void fgoldi_sqr(fgoldi */*z*/, const fgoldi */*x*/);
+
+/* --- @fgoldi_inv@ --- *
+ *
+ * Arguments:  @fgoldi *z@ = where to put the result (may alias @x@)
+ *             @const fgoldi *x@ = an operand
+ *
+ * Returns:    ---
+ *
+ * Use:                Stores in @z@ the multiplicative inverse %$x^{-1}$%.  If
+ *             %$x = 0$% then @z@ is set to zero.  This is considered a
+ *             feature.
+ */
+
+extern void fgoldi_inv(fgoldi */*z*/, const fgoldi */*x*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/math/t/fgoldi b/math/t/fgoldi
new file mode 100644 (file)
index 0000000..1f635c7
--- /dev/null
@@ -0,0 +1,572 @@
+### Test cases for arithmetic mod 2^448 - 2^224 - 1.
+
+add {
+  ## Some easy ones.
+  ffffffffffffff01000030000000ffffff2f00000002000030000000ffffff5f000000fbfffffffffffffcffff4f000000ffffffefffffff
+    00
+    ffffffffffffff01000030000000ffffff2f00000002000030000000ffffff5f000000fbfffffffffffffcffff4f000000ffffffefffffff;
+  fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff
+    0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+    fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff;
+  fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff
+    01 00;
+
+  ## Random tests.
+  6b3944398448a0a5f6c4e1be9abfcbb082b8f68f1c831d51ef2244f6e1a4c4baaf0ada769c1261b8e14b4856de381d69961078ea1949d39c
+    62e933c5f93e57e042d66b89d3c72ee85441b1207732d888b3d6bc35f3bcef91a6f4564cb085fd3239191c2fe427dcdb0e81164d30ca8145
+    cd2278fe7d87f785399b4d486e87fa98d7f9a7b093b5f5d9a2f9002cd561b44c56ff30c34c985eeb1a656485c260f944a5918e374a1355e2;
+  34938a406030a285024e9ff0a45987b53accc9d5d59828de8b8e07ba693b64d1dd2c7a190e708253a9ae57572e44fa458c8f8ac87cd96037
+    9d05863131328886c7203fe70924761e56244d40a04d4b5592f1c6a457b9d94fe950fb8a865f9091ba9223479f3e46680fb78ccddc776bf2
+    d298107291622a0cca6eded7ae7dfdd390f0161676e673331e80ce5ec2f43d21c77d75a494cf12e563417b9ecd8240ae9b4617965951cc29;
+  4e9cb6823482233daf59662d00212f2afe42183f8ce949b2e0ca5473e32ab647fe9bc1c472b2288715c0d5b118043fbc9aec782654363cdd
+    6c62329ec8eb576b6866cb6af702b891dd2cd72d21c354ada34221141381acca066fb46a8fbce28df44f0ac8062e38847067b8639be867f8
+    bbfee820fd6d7ba817c03198f723e7bbdb6fef6cadac9e5f840d7687f7ab6212050b762f026f0b150a10e0791f3277400b54318aef1ea4d5;
+  b46d33f24a69c2288bed34eb054d1fcd6244606cffca7d53ff4e68f113174655a9a72898ad2699fe49d069a0a84e077bf48205008dee04aa
+    9abc68911a75a6a376837cc6fc0e18c63cfb81747a59ba1314412f032f8b2a2fc5dde29176d141e71dbc11aeb2ecb9a336c8467882a175ea
+    4f2a9c8365de68cc0171b1b1025c37939f3fe2e079243867139097f443a270846e850b2a24f8dae5678c7b4e5b3bc11e2b4b4c780f907a94;
+  1195eae69f2bad43b7b8330395e41c811bae2f753f0a6727b9decd2e553dce743dbb568b45140c3a464c8646db12b3af18df328a95640706
+    8fc020ba693b4014fbf4e9c64223a8c3234e1361b8e2a745e3897f582537e20417831c159c107451fec49b88c4cde774330d8986430b39e0
+    a0550ba10967ed57b2ad1dcad707c5443ffc42d6f7ec0e6d9c684d877a74b079543e73a0e124808b441122cf9fe09a244cecbb10d96f40e6;
+  4b25a323cba12b5d042369a065a3696c7613898205f7defd3ab15ec351ec9fe1ba453c8b9abf254ff793c92b1f77d5f23f7629c04ec5c70d
+    b12ee8f930b029e2e4b79b808566b88b8087a510e5f80751ffc8f5426709bc2b40608e3e769dbd97d37dfe8a36fd25fbc1efb2cd9eda09f2
+    fc538b1dfc51553fe9da0421eb0922f8f69a2e93eaefe64e3a7a5406b9f55b0dfba5cac9105de3e6ca11c8b65574fbed0166dc8ded9fd1ff;
+  c441e6cebc634bd7de7de356e855528d52f4affae7cdd585352d94218ca171929b17b8f49ea294f5151a277c116519de5ff92ce4e2f6ce0a
+    809617d4e40d53223f01887bfab5a7d2dc1eb4f680ae59981b33e11fe71345b3155f567cc867cd9e48c8b534870c7df49dff883c0900d8cd
+    44d8fda2a1719ef91d7f6bd2e20bfa5f2f1364f1687c2f1e5160754173b5b645b1760e71670a62945ee2dcb0987196d2fdf8b520ecf6a6d8;
+  a92361d9c458605877a6d9229128b1a52f7bab378d0d389568aae10d09d419b0e6436684b438abcaa3e1710efee5ecec1af06cf0c82b339d
+    74bc6cd43025234b1a6e15fe2d4fab520c874af6f896b82ecb096b0ec3cf73ecf04ef1d13ca612ef0981b8aca0a769ae60202eca36410b0e
+    1de0cdadf57d83a39114ef20bf775cf83b02f62d86a4f0c333b44c1ccca38d9cd7925756f1debdb9ad622abb9e8d569b7b109bbaff6c3eab;
+  f0ed25db758767b83b1d5ab6f8bdbe8e263b18341ea296b2a5d27c0d928004d6c48695ac88af863ed12be6629d8a1b58ca83ddb4ff9c778b
+    44998d5a87e29ace239af04334451efca0d8bb88b441140caab3e79da9c73f1354df0c4e2a8148141bb44c33eb63f1c0025ec2b289390e06
+    3487b335fd6902875fb74afa2c03dd8ac713d4bcd2e3aabe4f8664ab3b4844e91866a2fab230cf52ecdf329688ee0c19cde19f6789d68591;
+  64429139cb6dd44a64984ac73cdf306354a32772da0724c647164d89218906de465c0c47f2841564fc69933d140088a02584bbeb7a138e7a
+    391d7bf04dd8082b6b196651c251737ba319720b66075f2e65a06023e1dc9697ee4c540b6fae20bdc333c9a95e69266b5ebeccf0b2aea18d
+    9e5f0c2a1946dd75cfb1b018ff30a4def7bc997d400f83f4acb6adac03669d7535a9605261333621c09d5ce77269ae0b844288dc2dc22f08;
+  0bb7ca441d108e3d7a4c43025798eb1f1036e2d2200e2457aa93a631c068bf9e99805e8c69344d6f5fc063865f2e94bb954c20645f8b7003
+    3f47bc6701604867acc3cf534d62c9847617954d860c34ac2bcacbfb1c24c99a7442a6170ea6539df0c98afaf6fc8f45dcaa77afebcc8290
+    4afe86ac1e70d6a426101356a4fab4a4864d7720a71a5803d65d722ddd8c88390ec304a477daa00c508aee80562b240172f797134b58f393;
+  93205ac6e814337d0d12f8e55f0b62a32ba948cbc6df03293d51e67a7aa7e1457d201991f3392f1cc4a4a9b6b2ed27407ac4e24a5b0e2664
+    c2d648607a99e6dccb9f9940ffed57de39928d9c3fcbabd79876ecf1a5476ff5a637d0052f04d82a2794bf0e31268fd6ff640b4aa1ac921f
+    55f7a22663ae195ad9b191265ff9b981653bd66706abaf00d6c7d26c20ef503b2458e996223e0747eb3869c5e313b7167a29ee94fcbab883;
+  42bb562d81bac7f606d2157697f29f1799b3e5172f003f44785b80006fb56681b832ff6e7875d6a146b09a9a95f9f3cfb210f5b9aa545e0c
+    e7cc109d2ea008a1c938c66e296911c0d7503b1a961f23edce9532c718e53518729dcc6f7e921f831c56d1d8ee7c5b4919f4030ce6703e68
+    298867caaf5ad097d00adce4c05bb1d770042132c51f623147f1b2c7879a9c992ad0cbdef607f62463066c7384764f19cc04f9c590c59c74;
+  4cfc133cf2b3e22f569340c3a59299087f9caf8b36562d418f2e18494769278c16a22641e4de8f675e9540d6fc0f8c210a64dd63c8d01453
+    f33b0a3beaad1fb12ba48d69ef999bb7a5af3bd2b977aaf518bc65a13caaa9d61cacbb7c5ce6173a282d341cf83c86a458c8d2d10a1c1ac2
+    40381e77dc6102e18137ce2c952c35c0244ceb5df0cdd736a8ea7dea8413d162334ee2bd40c5a7a186c274f2f44c12c6622cb035d3ec2e15;
+  fc64ef657d34477c17b0674b697cc5a4bc8e665ba1dee02e5afd0d2f57e0a64abb2bc428215d9e8ece95c5283e4233d483201c286e4997cb
+    ed4af51625762548e95f32e7042f07bb260f734fb9ba43301daddff9a863990d79791d5db1bbbdbad75c09ce313d93638c219c989702c5ed
+    eaafe47ca2aa6cc400109a326eabcc5fe39dd9aa5a99245f77aaed280144405834a5e185d2185c49a6f2cef66f7fc6371042b8c0054c5cb9;
+  2fa4cdcae0b01f3f3aa28d4ff311054c73683c09749eb312cd4ff7cf018fa647a66b44477459cab757b9149623ecf9064dba16d5601eafb7
+    b378045a6652eea3cc9a8530dea6361daa86866f038e579670f2d50e072417342f1887827335a0a041d1411b24a68d8f190edd8a251ac3c7
+    e31cd22447030ee3063d1380d1b83b691defc278772c0ba93d42cdde09b3bd7bd583cbc9e78e6a58998a56b14792879666c8f35f8638727f;
+  5107c636bcd6bc59ec9c7fcf04efbf90fdde936bff7e221be660d542f74fcccb8b49220b93744203bdb7b026f1bb9fd9a64c0187fd3d99a7
+    ee48bc6d5c231bd7be253ed69171d2525157d0d8fe12fdafa6e7a3285f0d966fd4c570928f61850138489fca5cb540444c24b602d9025899
+    405082a418fad730abc2bda5966092e34e366444fe911fcb8c48796b575d623b600f939d22d6c704f5ff4ff14d71e01df370b789d640f140;
+  475e593758fa8506baa94e1b6070aeaff278d905af67f9e5a5273ae0737825d220c02ae7a89f39998f98895c83e51e7bd0cc0c9126273792
+    3f724d7bbb27bf8b2f4e328bd865e412ed6207802703def28b9b64567b15f7648c1f96ca5092400e32427f9880c95fa0a83f7fbdd6b2d2b7
+    87d0a6b213224592e9f780a638d692c2dfdbe085d66ad7d831c39e36f08d1c37addfc0b1f9317aa7c1da08f503af7e1b790c8c4efdd9094a;
+  fb7923d1a8666b372b41792749ae312903e1b1a8198970a5da6d85dc19a4326bcde206ee841a2bf106c95e99b42fa7eb3fa559b4b88d31c2
+    45e467dafaee144bbd614028c0ea4d3a37b4ef99ded5c9e44b7526916218202401233364bffadac9424143bac99b4c2d89ceea71c3fd0ea4
+    415e8baba3558082e8a2b94f09997f633a95a142f85e3a8a26e3ab6d7dbc528fce053a52441506bb490aa2537ecbf318c97344267c8b4066;
+  2212850af1b191227fc4875e981ee15968832cb6898243af481b62d91ae5cb26fdcd72e45a54cbbbd9efc5e1739f8683d8ab318586b51e7c
+    6b7b3294bfd31a11644924c988b0b19cfc1db66d8e674040ee21537b54aed4e1e217f0b3427d7d4d9dc7d9dcbcda8c2c47c5348b9eaa7ddb
+    8e8db79eb085ac33e30dac2721cf92f664a1e22318ea83ef363db5547093a008e0e562989dd1480977b79fbe307a13b01f71661025609c57;
+}
+
+sub {
+  890e734c6f4af5b1c62cf6fdd0facf76af535bdc9781396c59a6cd8c37cc96aac97fa61bef07c60c69dfb633049014868473d28481385ba1
+    0a06a622804bfe9041259a435be8c40aa56d1be10dd3c4261157c16c0d0d99d11b0f5dce0b164a2e6d4c53813242954830a39f2c894fb315
+    7f08cd29effef62085075cba75120b6c0ae63ffb89ae7445484f0c202abffdd8ad70494de3f17bdefb9263b2d14d7f3d54d03258f8e8a78b;
+  17935b9768cf9ecdc3b3a4c3c87ee20f766bca820f620c1852a48d680f445f1a28ee6a9a18fbdcdb9f35846bc6f0bbb1687b227bcadd4c3a
+    9f913b62023a48e577ac9aaa6d06bb5cf46ea3acc2946a80f66fba02859837491a31b6740a5ed1178c8b6faf4198ac836419deb8aa439c0d
+    78012035669556e84b070a195b7827b381fc26d64ccda1975b34d3658aab27d10dbdb4250e9d0bc413aa14bc84580f2e046244c21f9ab02c;
+  b1689e72355c7305d5cab07e2d672e7b92872c115f03f9b45fe7c9d3354ea0d3702f4f915900085ec44e816dcd731086c999c0e97ce52b40
+    a85e9b5cbe9f628049f91b9adcc1db63f19e8f7c37b92e43015d0f76f188c1cfe89c86910afb82c2c6f1f7197562b919380a1b32b2439006
+    090a031677bc10858bd194e450a55217a1e89c94274aca715e8aba5d44c5de038892c8ff4e05859bfd5c89535811576c918fa5b7caa19b39;
+  8f952f2233cdba45ccaafcb8e6d17b38529a44ff49e8ac8dc6378710d5e5af052c460e2758e3ee28f5866e71737c9b6e7731c03d11d21cd6
+    5bc41fd436b58032dd4f50cd0c57bda1127a3033c2183d8133cb9c40760ea41bb42f48f36b6a2f3bcd0dbc60b5bef5e4b712308ca0f2b443
+    34d10f4efc173a13ef5aacebd97abe963f2014cc87cf6f0c936ceacf5ed70bea7716c633ec78bfed2779b210bebda589bf1e90b170df6792;
+  708939d3a13fe996dd90b1128231b7da3593c12d9ab880a4da9febe7f12bfefe72aab1608145911fdfa1e3f1f59b426a06dee253d4d3eeb6
+    008929e61e53becf9bc6dcbf6414f3a71e9f134fcab293cd42e096fef3d87a472a79ed1ce623708aee31e12f5bef9bc6d0aa470ce2a52f80
+    700010ed82ec2ac741cad4521d1dc43217f4addecf05edd697bf54e9fd5283b74831c4439b212195f06f02c29aaca6a335339b47f22dbf36;
+  a7996b0ae1d8ad5856ae5cf3abc8fc61c4b3579d951b632b9af67dce957365759fb5a6f54965a86b1dcccc9439eebdeb04f616c3fb4db382
+    2df8e6cf52e985d0e20f4f5e9bbaf1b8bd7d7b982da05568b7ea82fd2a5c0302d0ce9feec5e135eca886fd30827905eae204a51e498a173d
+    7aa1843a8eef2788739e0d95100e0ba90636dc04687b0dc3e20bfbd06a176273cfe606078483727f7445cf63b774b80122f171a4b2c39b45;
+  74179531aaf2ee4e7a309350982fc9ce501ad18c650f0adacce5da9b520ce27c04ca69dca0e719713d4d10db532401bee26b7ff9819bf532
+    6a3554898f2951ed005bc4ac74dfd0ef124cffbb52301109d8414719473dc17a602a3b530195c383efa22dd6da87414395886e4b9dd5b5f3
+    09e240a81ac99d6179d5cea32350f8de3dced1d012dff8d0f4a393820acf2002a49f2e899f5256ed4daae204799cbf7a4de310aee4c53f3f;
+  e7c145e38ca66fd86991538ab11231abb60af8b0ab7460e047f7b5b69bbe4a3a56c28c14131c35621f4014d541ffabd642c774bc1a5c66c7
+    9ca047c2251cc6012bd734198e4b2c6ca0d50a85db601659b8380510fc6c1d08a27fa5fb8eb3c9e5622185f83fba6ed128d153cc9563e1d6
+    4a21fe20678aa9d63eba1e7123c7043f1635ed2bd0134a878fbeb0a69e512d32b442e71884686b7cbc1e8fdc01453d051af620f084f884f0;
+  8f01ab3bbedf7ac443423793a422910a54dd73f121ce6f52270d7953386bf2b0eb228b2560b7103aa94f3190e80038505f2b21a9f138c414
+    44a6b845b4b3b6d9c55478664fa2134df50e7ff4c5184aa57bcc8d01da42cdbb78da3898fd92287b604b6a369c06d695241c48605b57808f
+    4a5bf2f5092cc4ea7dedbe2c55807dbd5ecef4fc5bb525adab40eb515d2825f57248528d6224e8be4804c7594cfa61ba3a0fd94896e14385;
+  1b6f84c1ad9ec3d008f345a19305aa35e5b55efeb535024b0fc9aee4be1c031bb070655fc5e672ea75b5db98cff0dcfb5427e21e5ffc4fa9
+    ad5d4e308c69186e87ac97551e9ec34f2d1746d5f6712b126c3415346ac2271450aca96fc6b9e82ef7e2f3fad60df4f184e34dff8fb53b02
+    6e1136912135ab628146ae4b7567e6e5b79e1829bfc3d638a39499b0545adb0660c4bbeffe2c8abb7ed2e79df8e2e809d043941fcf4614a7;
+  25ce194230be078d7f35b85d68706d768fec7994654fcd3f63f3d32eaf59f17bc87a617d41f76b7d0c78b63377a203e43194f0b056d26b0b
+    76266bea72a15cdc355867a490e775c909b7b6cfc833107e7c6b7dbaed2c8491c6d000b2d7d2beb72d72937285aa1bceff18aa35d0c52158
+    aea7ae57bd1cabb049dd50b9d788f7ac8535c3c49c1bbdc1e6875674c02c6dea01aa60cb6924adc5de0523c1f1f7e715327b467b860c4ab3;
+  c010bfa3d130f868f2b975318139c2a847c69014b76164192b437fd7e26b892a5a2425670fe3e8fd9efdfa250f57579d1af2d24f8eb6ce92
+    bfe65616e278b6ce46b295600131bf5ea092451a87f6bc3adf1c86f2fe717351b073062ced1394a14051bb33f3874cca569aae8ba97605f9
+    002a688defb7419aab07e0d07f08034aa7334bfa2f6ba7de4b26f9e4e2f915d9a9b01e3b22cf545c5eac3ff21bcf0ad3c35724c4e43fc999;
+  b2f21992b1f2155b7afc3a1d731b52c03cf14208e634b7da123888d652afcd919e9f5f0bea8b9986c71a7ba08fefe58ee0402d9a23b95f2c
+    ae241fa6292c63f3c09301255a866825b39c9645512ea123486f9a873cacab567cd169c463fb1cf0417633a7055d831d23fd1d58dd42e726
+    04cefaeb87c6b267b96839f81895e99a8954acc2940616b7cac8ed4e1603223b22cef54686907c9685a447f989926271bd430f4246767805;
+  b714b60b2614ab6ce63bf8eae621357f2e921f79de94982fcd5f524015db3faa52b15ff042733958fa202786f61bfed0b9fb5d5873619aec
+    0c99e1da4c747ad1a625321cf9359b1d823544d1eacfddc38e4bfd729ed2f9fb37760242340f7986843fb8fa0435bcb5cffdf11453caebcf
+    ab7bd430d99f309b3f16c6ceedeb9961ac5cdba7f3c4ba6b3e1455cd760846ae1a3b5dae0e64c0d175e16e8bf1e6411beafd6b432097ae1c;
+  c5425daf8f7089def2ff910e256d9496fc109d2698cf15dec1ce3f8116231969c5ed327626362b0658c60e74023a3cae5ef128fb5d27cad7
+    bc4e49fbf095f73894c4d623a210475cb7ba78ebbcbee8ab55a851162f789fb21e72de95afe4ddd45b90289f94f6d02b5f5a4b90c603ea31
+    09f413b49eda91a55e3bbbea825c4d3a4556243bdb102d326c26ee6ae7aa79b6a67b54e076514d31fc35e6d46d436b82ff96dd6a9723e0a5;
+  9aa20b4259efd3a701a63347181fa63f56f42a97ef836bf3aab88d30bbf28a91bbf713a0170024e6057e2281ac113f9b58cb13d63e850ea7
+    09e7104367d8c0441d811ecc619af04def9236752c2d47bc508b0de3c3b29c74533d8eb9fee13279aed8d58247e4010206c697d9b72bab43
+    91bbfafef1161363e424157bb684b5f16661f421c35624375a2d804df73fee1c68ba85e6181ef16c57a54cfe642d3d9952057cfc86596363;
+  e8ead9316aeb8f56d43418cd3bb1a71234c9e20b4a6dc1604ffdfdbcaacebe73b71d18c2c167544304d895d67e2daa2538a4ad0708cd9acf
+    ff3603f0960bb7a92f2b7fbfe242b467164e0ca72e89bcab6c467b5391166d05c601b38de40643f04d5b50ff8ec62b18c69e9df74f01babe
+    e9b3d641d3dfd8aca409990d596ef3aa1d7bd6641be404b5e2b6826919b8516ef11b6534dd601153b67c45d7ef667e0d72051010b8cbe010;
+  f37b41d9950036d560f02b8befd6f5c73096b8995850b9fc563291fba59fd994916744fbd4c1dd5d17a9e5ecd4ec4a658dfdae1ba7024bc6
+    eeda58ba400f16a946381fb11f0ac0db895957236bcc629ce9d1ea425eef8412ea73af488b97f1afa950736f26c3cace887417b699cc3410
+    05a1e81e55f11f2c1ab80cdacfcc35eca63c6176ed8356606d60a6b847b05482a7f394b2492aecad6d58727dae298096048997650d3616b6;
+  2bdb71a053047c4173718ce104e5b8671fbd61c59bfe8b20b890ceda15a69d24eec3775f4ce8a9c16d36e50626bc334e0fc1be492d800c06
+    b1ccd0dac33ac46b2709f9626ed70136f98dfbc705ad93ea8f1b325d644daf2c0180cb83bfdbb7398d3f58c0f6600056a174850b9db91ed4
+    790ea1c58fc9b7d54b68937e960db731262f66fd9551f83528759c7db058eef7ec43acdb8c0cf287e0f68c462f5b33f86d4c393e90c6ed31;
+  8f3fd6346c7f2dacf0d7cdb88a5bca3b222a895a07b169d0bcd0af0a9aa634876ae1a939262dd75bd12b5a97127155a015e00acac7d5cbc8
+    0e9c03c5a06accdddd0d8613e41a00f2b586e297e366987e02e7536e0ca1b4c2c75fed619655acddffe8585c7a828432d90575731a4c8519
+    81a3d26fcb1461ce12ca47a5a640ca496ca3a6c2234ad151bae95b9c8d0580c4a281bcd78fd72a7ed142013b98eed06d3cda9556ad8946af;
+}
+
+condswap {
+  db0be3ccfda1e2f8492bffb5d3344f1856ff1896b6a067d2fa064423ac5b05b669db1c9b51899c44a7f470be4173856d9aa3d1cd10cb8c9b
+    3005560f89232a97cebf6808e7493dd0f17e572556b53723b57faee51d8a0a45c7fb51aeb916f59c74ea6041494483b2fdce72189d1bd031
+    0xffffffff
+    3005560f89232a97cebf6808e7493dd0f17e572556b53723b57faee51d8a0a45c7fb51aeb916f59c74ea6041494483b2fdce72189d1bd031
+    db0be3ccfda1e2f8492bffb5d3344f1856ff1896b6a067d2fa064423ac5b05b669db1c9b51899c44a7f470be4173856d9aa3d1cd10cb8c9b;
+  ea009751f6a7045ec7d6d46b16cbe663f5a78fca3835626c79864bec4030443bb9349654d52b2edab92b8f4aa876a97e868aa6267a54e6b1
+    8c6f864ef7f664f4985ed8c1ea0c63bd7ab355b5321c2712791b610a2dadddc90a65ca8d5c25ff199b011193796b08f96420934615103b7b
+    0x00000000
+    ea009751f6a7045ec7d6d46b16cbe663f5a78fca3835626c79864bec4030443bb9349654d52b2edab92b8f4aa876a97e868aa6267a54e6b1
+    8c6f864ef7f664f4985ed8c1ea0c63bd7ab355b5321c2712791b610a2dadddc90a65ca8d5c25ff199b011193796b08f96420934615103b7b;
+  dc3aa7d363058993cbdc54e95f3c972a2cea17cdf9370fccdfedfa1ba939ca35f762c5c6283f84348282fd56d83539804427eabe3bd6170d
+    8c47a0990eccf7300986232dbc8ee26e8996c3bb4b73663ec5525312bea8975221c9f49039840c181d042654a8c1806bb86702ac7d0b80f4
+    0x00000000
+    dc3aa7d363058993cbdc54e95f3c972a2cea17cdf9370fccdfedfa1ba939ca35f762c5c6283f84348282fd56d83539804427eabe3bd6170d
+    8c47a0990eccf7300986232dbc8ee26e8996c3bb4b73663ec5525312bea8975221c9f49039840c181d042654a8c1806bb86702ac7d0b80f4;
+  69b45538513af2148e0a2903539bc5e20fca4da447278bf5967091d2e781121080c14a41989f9ee5218c444d3a42989ba890c7be7faae5d3
+    935eadbde0c571c29deb4d8404dd39a8cca177ced36c359abfa7a6cafe6075422e1d79cba8d81cdbdfa4a5144faf37b19ca22b2b40fe1a0b
+    0x00000000
+    69b45538513af2148e0a2903539bc5e20fca4da447278bf5967091d2e781121080c14a41989f9ee5218c444d3a42989ba890c7be7faae5d3
+    935eadbde0c571c29deb4d8404dd39a8cca177ced36c359abfa7a6cafe6075422e1d79cba8d81cdbdfa4a5144faf37b19ca22b2b40fe1a0b;
+  de30072f175127ad4200a08099f4d0df0edfa40eb02e21a03c5628dcb2f70da8bce9ebbb1ebed2aca391e73a4114c06bcdbf762bf28777cf
+    5eaf3aecf1f7d1fd3b43be8fa450163c79f162b0c6eaa4b1ad39baaf3a4c36564e001c935fdce38e90789151beff0f3cc4fdfc2d86dcb587
+    0x00000000
+    de30072f175127ad4200a08099f4d0df0edfa40eb02e21a03c5628dcb2f70da8bce9ebbb1ebed2aca391e73a4114c06bcdbf762bf28777cf
+    5eaf3aecf1f7d1fd3b43be8fa450163c79f162b0c6eaa4b1ad39baaf3a4c36564e001c935fdce38e90789151beff0f3cc4fdfc2d86dcb587;
+  1bb20a5a05b190737ad6b2bc5bc2b9259fa7e36eac8f3032a7c56ed59c1510baaea32bba898b329b0c64fadbe454095b9f82b366bfae3d8b
+    cf0a87c8befed020caf82f37b4c5f17d2aa9ff8ab75baa156cfe2716ac1eaabf9bc760c2ddcd66a9d02a8384ad1db9e8f857b151d126e0ef
+    0x00000000
+    1bb20a5a05b190737ad6b2bc5bc2b9259fa7e36eac8f3032a7c56ed59c1510baaea32bba898b329b0c64fadbe454095b9f82b366bfae3d8b
+    cf0a87c8befed020caf82f37b4c5f17d2aa9ff8ab75baa156cfe2716ac1eaabf9bc760c2ddcd66a9d02a8384ad1db9e8f857b151d126e0ef;
+  c186531a219344dc485cd5dad6500616ac8e0653693168573cb9be590b02dfb1ddc5d87d27f2d9fa46ffeda955a6285b7ef2b9bf84825d06
+    91f3be7eb2e0be38d92974d81be5126ef40d358f242d23aa4863fb16ba3e8844e02edc87c0206e211587886ed532da0ab148cfcf64e9b58d
+    0x00000000
+    c186531a219344dc485cd5dad6500616ac8e0653693168573cb9be590b02dfb1ddc5d87d27f2d9fa46ffeda955a6285b7ef2b9bf84825d06
+    91f3be7eb2e0be38d92974d81be5126ef40d358f242d23aa4863fb16ba3e8844e02edc87c0206e211587886ed532da0ab148cfcf64e9b58d;
+  9f2476d6db424d19ee0cba4ed091da5b44ca63caf2f711eaeb7edcc75f49e0d0c367c1f2817b6bb39460f897432901064cabed7f6a03b1f0
+    66e7dcd34d76a19e3285685112b7619f9a3557764a918922faf1370bb97c2b2fa845dd34662cfe7299ab702c68fd43b5f6f15a0af6625f2e
+    0xffffffff
+    66e7dcd34d76a19e3285685112b7619f9a3557764a918922faf1370bb97c2b2fa845dd34662cfe7299ab702c68fd43b5f6f15a0af6625f2e
+    9f2476d6db424d19ee0cba4ed091da5b44ca63caf2f711eaeb7edcc75f49e0d0c367c1f2817b6bb39460f897432901064cabed7f6a03b1f0;
+  f124ac31d304a29cdd94bdc0611c61064a7c5f09cf6847bccd52fc7a7b77ede73b42b1ef75f000de4361028e0fb63896f4ef5ad3bc1b9c90
+    6629084a230c793086e469b9a01e052ad7a5680d6955667e0dfb19a250f447d9e238695400dc4ecaeefcebed44d374e57ec4e414f5629e63
+    0x00000000
+    f124ac31d304a29cdd94bdc0611c61064a7c5f09cf6847bccd52fc7a7b77ede73b42b1ef75f000de4361028e0fb63896f4ef5ad3bc1b9c90
+    6629084a230c793086e469b9a01e052ad7a5680d6955667e0dfb19a250f447d9e238695400dc4ecaeefcebed44d374e57ec4e414f5629e63;
+  b64f2952510827bef3d32ef6347251274c7241d9913268a7a6a5c360a4b29e7ed04d09a9ee5aa83235eaf8c0582b076b21d78b7062952773
+    77897028a9a81fd54362a6085aa2c2fd6fdd44589f447c7310b9b3b5a29b54ed15c57e2c9fbff16f20129a4dbd43640a0d1037e277ca5c93
+    0xffffffff
+    77897028a9a81fd54362a6085aa2c2fd6fdd44589f447c7310b9b3b5a29b54ed15c57e2c9fbff16f20129a4dbd43640a0d1037e277ca5c93
+    b64f2952510827bef3d32ef6347251274c7241d9913268a7a6a5c360a4b29e7ed04d09a9ee5aa83235eaf8c0582b076b21d78b7062952773;
+  4d75efd204a66aed323456e6f55cb891ee7a31d739ae89726e0ea84532f9c740d1b647b117094fb85da2c62da04f3b9cb13db6b840046b7c
+    0afd07bb073a150e7b82711e1f42f7b0789af8192ea6b4e1b24a3d48de7f8b802875aca5a02a9d24deca0b58c895992a449d01026495b96c
+    0x00000000
+    4d75efd204a66aed323456e6f55cb891ee7a31d739ae89726e0ea84532f9c740d1b647b117094fb85da2c62da04f3b9cb13db6b840046b7c
+    0afd07bb073a150e7b82711e1f42f7b0789af8192ea6b4e1b24a3d48de7f8b802875aca5a02a9d24deca0b58c895992a449d01026495b96c;
+  1ff4eb5f0280744f0c909cede8be29cccb8ae43e4626018a9a1d352a97d987678863ba34bdeb183bb5e9347e96760665f5881570778a2e19
+    49cd390a9edc7fad6c84c2e44ce09e490161094d2dc6cad30759fbef10aa30e5aa6474bc46571188418f3e4a2b19ecfc379a1f94e3a44e48
+    0x00000000
+    1ff4eb5f0280744f0c909cede8be29cccb8ae43e4626018a9a1d352a97d987678863ba34bdeb183bb5e9347e96760665f5881570778a2e19
+    49cd390a9edc7fad6c84c2e44ce09e490161094d2dc6cad30759fbef10aa30e5aa6474bc46571188418f3e4a2b19ecfc379a1f94e3a44e48;
+  e96c0316baf01c25ef8a41c02e99aa84c82f8eecd3249be268834afb5f7e95f0ad10bfad4160bb335c30ffd6a1090c91c57eb6ac7a7a5e66
+    115e6f115bc4e19af60710f3dd821fb58cb680a32d6973eae9ea3e48f1928dc1d9f684a8c8de80affc59bc0fb342bb040f89d2d46b8ff8fc
+    0x00000000
+    e96c0316baf01c25ef8a41c02e99aa84c82f8eecd3249be268834afb5f7e95f0ad10bfad4160bb335c30ffd6a1090c91c57eb6ac7a7a5e66
+    115e6f115bc4e19af60710f3dd821fb58cb680a32d6973eae9ea3e48f1928dc1d9f684a8c8de80affc59bc0fb342bb040f89d2d46b8ff8fc;
+  71bd8f5036da4c33f6940348ea448c2f9bccb152dab40b410bd695923a9e255b33aaf361055be4bb9a570e201dce784cc1ccb0f89faca85f
+    971b2f6569ddd9099a87ec4086d8218abcfbe81579f25f03634e471170a5088f48a6cfd09ed9e389ca37b780165c00b206304229135b6044
+    0xffffffff
+    971b2f6569ddd9099a87ec4086d8218abcfbe81579f25f03634e471170a5088f48a6cfd09ed9e389ca37b780165c00b206304229135b6044
+    71bd8f5036da4c33f6940348ea448c2f9bccb152dab40b410bd695923a9e255b33aaf361055be4bb9a570e201dce784cc1ccb0f89faca85f;
+  0f1165e19968f3e6159e9f7f843c1b849e5c3917af4e2798869112c6efca9993ed339c8495eec8c54b286820bf7befdc8ae344e4d309c077
+    7ce5a2ae66b9bddd30c98d9aa9fcdd6ef90d244d10115d15cf5d81e9e389f6852b153a42c642447043fcfd278166b92c624508973449b0e8
+    0xffffffff
+    7ce5a2ae66b9bddd30c98d9aa9fcdd6ef90d244d10115d15cf5d81e9e389f6852b153a42c642447043fcfd278166b92c624508973449b0e8
+    0f1165e19968f3e6159e9f7f843c1b849e5c3917af4e2798869112c6efca9993ed339c8495eec8c54b286820bf7befdc8ae344e4d309c077;
+  6dd07b4891d8f45ad1ee64da38383ec50d6f29634767607eca19a547cadc3c157ebe7d86a28d4d2fa2d40f2c5402d8b338cfd8d30aeadb6a
+    63da9a5a0cd51e58168ddb305c0e70a2f898279dae6394e1b3e1ef3a0763847d5d2f15be493114360204d037d7d3c3a9e746d27dcb544ded
+    0xffffffff
+    63da9a5a0cd51e58168ddb305c0e70a2f898279dae6394e1b3e1ef3a0763847d5d2f15be493114360204d037d7d3c3a9e746d27dcb544ded
+    6dd07b4891d8f45ad1ee64da38383ec50d6f29634767607eca19a547cadc3c157ebe7d86a28d4d2fa2d40f2c5402d8b338cfd8d30aeadb6a;
+  d91228b648f75350d775432af35be9aa283e9729fcd3b689f0c7ee02ea67efd33493980ae5eec92ad6ab42ccd253a7aab5a62c26be275278
+    07c5d1b4f9b8db88cd4187b1ae5e175bf703af12b26d066c1d55dd367569d3e23e3d0470f3c47e6761746ce7de39cf563500d026a6791980
+    0xffffffff
+    07c5d1b4f9b8db88cd4187b1ae5e175bf703af12b26d066c1d55dd367569d3e23e3d0470f3c47e6761746ce7de39cf563500d026a6791980
+    d91228b648f75350d775432af35be9aa283e9729fcd3b689f0c7ee02ea67efd33493980ae5eec92ad6ab42ccd253a7aab5a62c26be275278;
+  f9e764e20657319802b53ee29df43b0d83d994d9049b9820fc0c31b631265c49f6613923a042eadce5902fb81753784ff5a9c0de2c4f6b12
+    6ab2f8279e82aa65e8326e5c60bfa067e4967b3ea67d0369853282e5429736529a43eeee3fdf24ebbe5ca85b59647994fc99c90da83c6229
+    0xffffffff
+    6ab2f8279e82aa65e8326e5c60bfa067e4967b3ea67d0369853282e5429736529a43eeee3fdf24ebbe5ca85b59647994fc99c90da83c6229
+    f9e764e20657319802b53ee29df43b0d83d994d9049b9820fc0c31b631265c49f6613923a042eadce5902fb81753784ff5a9c0de2c4f6b12;
+  772289865ac905fac203e4654b258890ddb91cac71c6d2af3826e3acfebfa222c937b40069ce3a757e2ffd960b5821ed1ad54e4ad2e3c6fd
+    58dbf838ab81a754533c7f80fea48cbc981f724e4c150ddbb0afb5c711a8e21079446f77c427f7eb1892c096fa3eae5e2b070413611cd184
+    0xffffffff
+    58dbf838ab81a754533c7f80fea48cbc981f724e4c150ddbb0afb5c711a8e21079446f77c427f7eb1892c096fa3eae5e2b070413611cd184
+    772289865ac905fac203e4654b258890ddb91cac71c6d2af3826e3acfebfa222c937b40069ce3a757e2ffd960b5821ed1ad54e4ad2e3c6fd;
+  0bd6656d68c41bfc5389db274367f69191754919435ba58d2256017b5bc595d102c6f4f745b3ceb0c32bd0a6cdadded3a3dc925ef2b5c73c
+    1d903009c74d3d135a192bd42466cd28303f482626a7d10832f1f6eae72c3b051b0373e62300a71226011475034f0bfc6c9a65601bb83993
+    0xffffffff
+    1d903009c74d3d135a192bd42466cd28303f482626a7d10832f1f6eae72c3b051b0373e62300a71226011475034f0bfc6c9a65601bb83993
+    0bd6656d68c41bfc5389db274367f69191754919435ba58d2256017b5bc595d102c6f4f745b3ceb0c32bd0a6cdadded3a3dc925ef2b5c73c;
+}
+
+mulconst {
+  cb908747a1af81f52715ef8440aed06aaacbcd7903c99cdecef5ec54e7845bedaad6475028b0cbdb0d53b6eeb71843f900fefd398a083d5a -417895
+    e95e64fd1b2e2e79d1bfef5c96942eaf7ca5d40b1af774ed9c4ea2351c8ae81013f896bafd1f0b0e3a3c2ca3347c6cf88f988877af5e009b;
+  46b23d83c0848e79af947191cb76b087483beec6224af749e59c0c5ae27ff8abada31da895f8132aae0457f5ddc30d18a8d52046fbe6ef55 384370
+    31c9bf2f3bdfd3c1dc115db96d7a1c15e2a9ae67dfebccddc9408a0dcd147d9aa62ce4a55a2d6ad54542317756a08d6be3b959bf132624e2;
+  53e19751de62909506bebf74411d359014ec4b6afbb8ce14eeed8a3938013f0fee6897e3cc3adcbc90d7df321c4caa1941d0e5909551335d -49682
+    82fb902a8992ec1032a6837cec56189c45d1c1fdc47748e17dd9ecabc807a52eb537d6411f99f4d3b328e7ca76475924f90506a8adeb908e;
+  7ff3b9b3d8ee248a48d0c8d308d8c3658374acc92303143061a46656ae2b35094ed7b5b27d23366c0e42ee0a9471a4355d81cda0e4390348 131908
+    adbb3dd21e634dfe2c41593c65249f03a048338037d4a107892df36f10e3ff68402d6f48e74bc7a7858f2c3650fd2a10acdfd4213850529e;
+  f532f4f5ab5447b083ee4225246aaa4448696e4fc8bcf5b0ec1b52df88354e5327d7a3cf6e15db9945205968c275755ea3906a27437d29ed 383911
+    1f42b3585a2553ab19b14639c3e3aca636d88053c70fb758962713783bcd824b729fb4d01dd56a344d991bf2d32a2967acf470b39bee88a6;
+  ef0e2a829097e50575eae6b1ad3fa10b8e417b0363654e0cb07a563a905c02e367c6f1ad7be1469b9f5295e3db0726853adffb820715f7d1 -226220
+    a7c5387af1b9f61f09d628feac1a050173021386775c9f534557b8841b812155325e3632eee7a2302ef8af0b4746575d6c2bcf3b29f674b0;
+  3b4655b1c22542ca413f76bc24751a01cdab6b385b50301d67fd62ba31ad132150e38e12a849405112c4742e93b92849bd8b872c29edb2f9 -89924
+    b51c0dfa2407a89435fedbcfd7835b6e0849d8626995fd00974f44c49d794d3c5dc16028e9fdee45bbcd269035e8bdba695a6653028d4d59;
+  48bdc2073bff25ae9bf7fcc368d65602a71e48c343bc7181c361331f108867d56c588f698aa5b91222f5dbbc859bce656f2607b053222fc7 66432
+    e845dfe65d563884074ffeecaa325706df4a129b73b056cab6ad999588216d72525222ce00ce4d2ffb09fcf6f010a6eeb2eb753fefd44b47;
+  98a26f23f83673a480076b2fc7ccfef07fbbe70c4160d5af638c345f7d17b00931efff543fd47b7b8b362a70657a458566ada4aefd1c5eaf 492192
+    0ef483482255b5ca0f8338317f6eaf9c4fbf8bde8b8b04e26d2cdc0632186f1e62a9c3a1be23e8dc444ce50783135aeca614c884bdb77a30;
+  6ebf7c4e06837120cc852a077945656046e64c2d88ce440bdfc8d38736a4d510a722bc8b0d225f11df0e704e4b04c48bf481da68fd1e150e -7098
+    8d4e2cd12323b5744442fe4e4fc21748b344d7f94794398ec187f0f986ee743cd733a7a16bd4495818ab033103f020c794c988c5d2c1628a;
+  94e49f2bb473a7273abed94363c4dc3d13db159e3c3698a62ce5f321252aa03c7f63dd6eff9529c985f43abc7283d11140cb44ff6fd0f3d3 -392951
+    58a606cdc1bf1d5646fc08c7a6b3336fbe532c823c70242ea7536cc6dcdde7c4f492306e39ac0fb88367e41d63a8305d69fab57a52c8c284;
+  cd262298ce3b73294bfb523c30206fc3771144890a0ebcef19e02e5085973a6aeefabdf8cb7186d57be38ee84b114b9a7a22556c0cf88df1 -237681
+    7553420c53b255fb909d6e9008f043e7af25a2a9c29b177b830ff1ae6f6fdcb6aefb8386465f24634fb04c6078322ac691586f91cf7928bd;
+  cc9bd2e72ce15dd50877f95812b65c65cf09b14e2817784a5b296c123e498c532362180a932a455b2ddb75e26335dbcb21eb94a90ebb6940 246794
+    8838e720b6e497d9cde9fbddc2333c13cb5bdbbbe5afe41ffd61f5035c8c4987fddb8036c25b0505765bf1a5aa369c6bf7723c65278584a8;
+  d8ab6b8f3523e98f49140e051c0b744bb0068d20f499c0763aa28ec1d1dd7e86356f357149c317376be96cd520b3ae04a2c4d468d74745e8 -360296
+    4c0bc5f6fc52c6302b74c790d57ccc8fae59f2aeebb59c3393c56e70060f4d170ea41354f5040804a6e8dcb4a7c585ee71a6d90454e701de;
+  6036fbfb9fcc223a1776ec67c9424788aabe553a7d6a0472159edc747ebaf97e99f7263ece635ef0b702a3610aef32e17f59fae503e18d74 -5433
+    f6fa9a49b55077330fcda976a19ba7cfd98e44fa54044840df0ae9e19710193e684703f7fadbcbbc534e7be3adec0cae0d95ea414494f368;
+  a3433dfc89ec4d6d858bb5b018e2d474b7de7604ed4491957fe16453d3793740ef88136814f59b19ed3610252e74cbc3a36571d02e57913b 237641
+    7a01a802b7a5aca44675887d9cf94d04d4893e2dc5d7474090cb32798fa370690255f57d18dce2a57126bb5883672b5a74a1c780f48f7bd8;
+  c17ecc6099e9630a4ec20f4199abe1c10d414f6d734014ada1e6f097090a2bb4e638fb52d9bf56080d77c2b535f52cb091cbaad3c65072e8 517226
+    708adeac4ff99cac4c3eb649d55276f58a7d21a0507af176a53893c567050d352af501a9c242baf53121e8b56f5679b175dc01538350b644;
+  134263fd765e5f1da64900df5643c5241d4fa8a9e96ee59f01667b64f9122e85e995b90d4bf5d5fe9f68825dfa6edd19d8ce009270ae2984 390674
+    2ffbda7ccbeecd8dc4600965679146753c496d35bfa546e13edbbaa259065b024c8aaa3a17a3384f4f14d5ee01371ac55fb50a153b4ac1c0;
+  54326fffef9f0fe1207a07c8ac063d64b3fe81528e005f857319a74da2c3423a552b05f8c465908b028a2dcf15cb7158c99a1d2bdfd890da -255467
+    e554e875e480c68c4fd780425ff8fb50f18bbafd0536a09489bc7cf197fdcb80dd4c7e31447a1a862066b6761ea39ea3b8c3002039c86b40;
+  5c30f73ec3addb2d27e0e79579063f2d30fa9fb47c2b6a3a32553120a047cad83abc901e5a5b4a65f8e51144f7eb0abec9a4d105236b2618 -466309
+    5ec894f6abe2d88971da11b982c3322d1f533a326057816f6273585bfa186a7f569ddf93c27e8e74719ddb515689d5935c56e6b86936f22b;
+}
+
+mul {
+  ## Easy tests.
+  03 02 06;
+  f7ffffdfffffff00000050000000000000a0ffffff03000090fffffffbffffafffffff00000020000000fdffff9ffffffff9ffff7fffffff
+    ffffffffffffff01000030000000ffffff2f00000002000030000000ffffff5f000000fbfffffffffffffcffff4f000000ffffffefffffff
+    bfffff6f02000083ffff5f010000a1ffff9ffcffffc0ffff6ffdffffc4ffffff02000082ffff8f03000089ffff6ffaffff7cffffff000000;
+  f8ff7ffeff5f00001000000800000300000100a0ffffafffff130000fbffff0100000000e0ffffebffffffff7ffffffffeff1f0000080000
+    f7ff7f0200e0ffffefffff070000faff7ffefffffefffffffff7ffff040080ffff1fffff8ffffff7ffff0500800200000000f0fffffbffff
+    0a0100240020f5ff7ffdff4fffff4100003a00201400400300a8feff030180e6fffff9ff8f02003001002c0000f0ff9f0600d0f8ff670200;
+
+  ## Random tests.
+  94aa99eaba90c5eb2efd7101c8efeffc1e44c8fcd84ccb8ef27b9792ae12534c0a250e5400407177bd655e51c343f776a3fd26474da4dc74
+    284a0f7df2d7d7c760cc57b1606625884c1643020230a9eecca7de6098f5338ddf9297daecae8a07e199494cb6db91c2c683566bf79545d2
+    213a31ed7e494f470736ded872f2c4f3675562a7b42bf01ea246be0ecaae2a61ae500690df9f99ea9fa8e5e1b9ccf3614bc34148b7693c91;
+  a434abefb0de3c7f5e33f9230ead871bb3f06888c0d568b9a74fa32b676dc600fed2d54a042e1b97340852c4d6ef796a18561884d760d4ad
+    99d4c85f047d3dd8e530c50ae7d80815a30ec6ba820316f5da922cfa7a51ab45290ac2ab8a53082716a6d7f77ac5a285fac298eca4fd9c52
+    97b2991fcc858d5667d3674484db9435ba71ab0c836c534efb823c123f9e94d0d3445257c55c73e8bd62f1d7acf1d75fea5eea669bfb9912;
+  fe9911abbe57f1ddd61acd062d59637b129a1571af65fb70169db0d8b12b498a66d1eefcf9c8cbea01179f113afef0ae192550828885689c
+    e371e9a11f662e38caff28cf4cf5ca8cdfe4a8b99fa12a33765a22ae25506874fdcd85388ed406a955d293320e01957f119027ec4803c2a9
+    171b49441f9891d927d2f6436eee1dc59e7872730be496638017b42151cdf00e9abb212bafc47c05a4e185bf7cf21ccd97b0c8ea426e4381;
+  1c300df44d28406d070b10c1920e01ea8b9056d9e1d6f9704dbda3f6328c26a28e9e4aa07dc79e03c09c5639cf8b8c783c8b430b9d8c21a8
+    c565b0357a1e8aff3b3a6164ff34c9598a1c15a1551224fb38d25dfa3b0627c9d992dbd215910236aeeed23f4efd99640054b6648946346c
+    f7871e06efb6f737cb4e04f708827092e94e3c70faee57f70096b9d674d0ac5b228824924bb705de49de9f9c0e834462093a9a5551fc7d5d;
+  9014dd22854f1f21fa1475d74eba669101c871c2a21ffd5d10f0792e3cc5d34f15b4735759575dda169e2dd868a5ae64dbeaa2a654cfde5c
+    c6f59ecf6add5ff92813cd6f37de9c6f8d3ae9cd33e4f3236ff43127f463f297c3067493868adf7823bc285a3d609a57d9b0b841a9b628a1
+    cbebe67e30ed82acb0de699a5d874121257b78c2b0642583713b58fdcd089a6277a5c5e571538e4af9025dfc63cd78931be51780055dfb2f;
+  c8fc23a431d744e45e8bcd2dd2ca612db40b26287970aa878a5cf6283c51b2bd4edb76f705f29bc3911a9aee8fb7272009520f3590ac4c4a
+    c1015900ea58d6305b8b558c992bff50a8614843cd3d11274830dff6e1db405f8b268100fbc5e44c477cc2a9e83f825c7840e560fc2b2920
+    6ecf49d92efb60a15a9de38ff37d3b23b339d889ad8b11cb332df260b9c49fd28b287a559fa5a5edf732b3bd1014711fb7d088a37cf6ef72;
+  daaea5c9549776d866be36a032027a65eb8325b1684d3c37cba068e69aad7d4f104ff47c14f669883cdef5dae9a0c7840379631ef97e6bc3
+    f8dc00891a9c55481a224309a8024ea257850b2bc62ab10bb5ea5b836776cd46994900d6656b7f7e0d9092fd824b16d20ea14528a0de7729
+    83d3eb62429c2ece396cd7069dfa2985630da2fb0949b52f1df9231c82f32022543d4a2302241d0146b4db97f44ec5342b6444f463ad056f;
+  786f4ebb20161c96f93ffe3ee2b0cae50fc454d910b3fae4fbd8454b8ab7975c6cab8f6a1b593996b24b22603137a52ee732f9d19f9a6e43
+    d78d3df4f455c8f5d98baaa17200d4c94056e21768c4ca783188bf15c28dc86c581fd9c150f923f905becba19361ca94f7434d8a74744669
+    e857a2020e1877b50151772bcf06763285c1d8b2b5ffdb63e13ef4985ccc3b965fdb053305f16feea4a3ac0edad21423c80ef847181bdff7;
+  6b48bf18ec4557e745cad3e106511bd5a9bbe136c77cf639745f891f54bb16c11e19770eb5112df8bb6755b97cb1e23c9c7efe589693764e
+    95c2a38f889c451f792c3d5d8b3237eed3f99e2b4857bfc48f75d7c0de3f293d2cfa7f97d65d75e0e3df633d84931973ac542ce07ba974b1
+    2fdfb1146b569ac98b0811e600e2d8e578023024ed704f97c7f7d56a2b4a33ac6ec68d14f0789441a904d175fbf8b91603c532ecdffd27e4;
+  1fbba059c333843e3cf0cff19bec99bd0cfc89c3b390200165680b742fe540b32042959022e7393c9e13ef7fa3f1dd5ac46595803210c536
+    c723bb3649b1d95647636abffd325e363931782f02002ad6d75d2082c980a7a2c5e1d51045880b36d4f62c16250faffcb2acb824d8d3d5d4
+    97041eef8892ecaaa97c4effb8c69160b3878fd302bc5f0862bfe4894bf23193738d8dd95c9eee9539e23fff67243372b97f02c3a3934555;
+  26f5bc0c2c49899690fa423478ab1cf7000be27609bf683896a6ce3f619510b061ca04debb6c64540e724d113cd4b8ff3485337851367700
+    b8540188b9ca77a82899c7fd26412dcbd6a3c9b45be595f63bce7426d7c16eb20c3a2dc86afe9064edc19840f75f05de828c276e98451ee5
+    d973580e88abb6c83defc009607082ffbdcae6ce9d577218580459711682f8708a15de9c62e3ca3b50e91a7bf5781deee152ac801a64fe81;
+  6c044d6556abb8efdf5b180695293464a8bc4b9cab201822053c7295973ea0a8866425629cfff1d372a48f51dbc6a7f828691194c32c2c5c
+    da28b84dfd48f206972fe282449991597bc7f0734916b6f7a253897fca2ccd6b80aa477cfd08549b6d25bd18932128e59889e2dcfaf76157
+    a599fabf997468487b9376500b58a51e0e00c86d9eac6f50157aa1017d99ac8b95e7842b36071a192784ef72433c06a1dd042990b71ffe8f;
+  50378c6ac56816962d428ea523a0cf87121fb3c53b37bac6d3a05fcbdfb8a5fb20aa014f1e1b01a3963534d403baeb7c436f72cf3cc5db58
+    78901fa015c6f0d3e1072aa87d865579fa405615691a3f27945f9b1431ee8bce8ac9965a1f9b0f42ff10bc77c43cc93566eee9a9b79866ed
+    00127644003ed4b518f04abbd058a46015fb328883b92070bc5cf0fd98d7307dfabbc65f3e8551c622792efabe55f03953e498e360410790;
+  5db2f6db35af2e1c84ab6832b967bf987cd03adea7113fb8806ce8fb3ffa426dc10997e13afcd39cf5cd65f65f8a09e650fad8e5f6ef2648
+    b063695bc97a18d6ccb0785ee4588951dcbee420ce8e37b25cce325352968015c94f9bb784f3a2bfa34c6a1edf710b63f139fff6d4f7b2f3
+    999284a309482f67ab6460e73ad1ad267052b59fe50cd58a245e22079e2b50b119e312ff60a65037cf9faeaa259d22f689eaedd7924f1093;
+  d5baefaae7fc9000e0d55f287f740dfb1899ba6e9e36338959a16ac5e5f45442a37086b5519a0b439f38b129328aebf468dec4b59c89a249
+    4febc8666dcf2b150faec9902a5f57e97275e7b122724c2b77d9b78655b455d424db93f859b8de6b09c4bad6d5ba68349c65c41caca67f8b
+    19e6ed86dc819e6b4368d196679328710f63bf1fcba6652265508b509ecd0e3e8b8bf17f8f1eeb361af107ef684a729a3635cb55de3f8605;
+  9974be322a0f471cfe0855456104219d8037ca1cb5aacb9ff5da86a047ce159430cfd92f67e764244996668ac44d8d9787dc1b5754d215c1
+    78c00f878fb41fdb0cd22c026881d5022fc470755e6261945b9bfcc3868f591f08288e4d64a65c9f3d0b05d3ab7f27d801aef29e5e98caf8
+    fd51b84f21b06377054dc879b8a176a7799951d5669486b210e4528b9f52bd60f88c2a068f722d85fa12761a4caf2f05bcdeecb7ed62646e;
+  f4048d4a1602eeec43cdf20c3ac9f7b359935bae3648070276faf07a5c58fdf79771ba6c077ddeae47617971e2145f4960587637e7028822
+    32635c7425108f5d19e35e4d91a4bdf3310bc884c9061d0697556ae25e17cd611e090c1179ca9a68225b283c8ab98a7b1e97508592c60c66
+    8d6a91d016aaa7a0da03b06f5876ce8e8a71d1e5e5eb430d6c5ae16e1d6d9b61e77179b4f6fe43ce37cae27689983d41923f3b30e9d9f930;
+  9023c019bf7ef6df45e0e44111903c622220bd3dde046679540302451d001e47a153b0ac7dcfb9831e2d262d8e27002197d9640900e4c1cc
+    1f5e2ca612ec6f14df4b528cc35001e763b9414a81381a1a3c7008c0cc525bdb00aea94ce2348f380dda43b5edf4fec7c8cf5ea2f5fec4b8
+    bf2c241efc180988232b83cda91e12dd0aec0d22ab68353c9841c4ea0547fd1b377fbca125f8cf92c426f8eb859f85a3853516ec104d9b6e;
+  bbdecfafa9284e3067081b7d1bd7ce0e488829859628a8f6a38bc1a3ca6ec3130dbc1345b69abb7224b047fd3f2dadcf9cc11a8ec46a6436
+    49a956ee69cdfecf29440d60fde9db1edd94619af97f625d45dc83c64994fe6c1ac858150cb526ca3c58a9fdc09d409ee3ed0b0cbcdb711c
+    7abfd47fdfcceb278c917afd059e335110bec63fea48dedf7f8275da8922201ea06be39e3d1bccea2d38f5eb7d7aa5ef4e6fe6792a9f0ce0;
+  a9483d4f0c94eac273adc392c3f425be610206683cbb6806dca47be3b54ad142fa2c54a7a473675556bcc8cfd50649af3c58d7a91e84b428
+    b986365aa08f088fe04aaaf2e00468af3fb41cda961b56ece5bedfd83cf9a0fb959ae70c23d9b27cd1eaf8b611d9e578aa0b9d899b81d5f1
+    4af529627637ad3f3f1269319f4fd426d31e276287ee525d0c64974e55fca1cd58694eadf47e44dc3e843e01c36b22f9717067a907840ca1;
+}
+
+sqr {
+  ## Easy tests.
+  03 09;
+  f7ffffdfffffff00000050000000000000a0ffffff03000090fffffffbffffafffffff00000020000000fdffff9ffffffff9ffff7fffffff
+    8c000040010000f3ffffff040000f4000040120000ff0000800a000099000060060000030000000a0000e7010000180000290200800b0000;
+
+  ## Random tests.
+  776cf7ee39df23538f76e6be87ecbb1ed78477b858e3520f24b2b36f09a7dd61daf7bd5990af13c11b1419d8215eb69c30efc3917097797b
+    870001f8ff173e02ce682526a513ff99c6e9688cbec13839ecbf0fbc11d8f84f6cf53041b5bdcbc624f9787db9babe61cd477cb9395590d1;
+  1e5ef8dd68207b3c3d3c670acee7ecb277ee6bc10be9fa3f3d800e1414bf402298ca443de6fd57ca899188c884a93d4b9c9d1b00a7c9ce44
+    4a6d6da8002b6a0f3f5c3a9631254ccc62cf66c93279c4b77dda55d32514c8b388b543002f8055321c7195ce1fd26f51f48732e664cdc62f;
+  e02eba6cd5ec1cc39ab92db439896520e50a5f9e9ed6d31ad37d00ebb2a54cb68f1c0ae537006ec2ffb14aaad2de16ff06194929b702b95a
+    95632fd74045a49782f6f9d0c86d554cf6efd07f9eef57d669828c77537d4c3a69138d755855640064f5e9bc8fbe0eca96fb04053f565431;
+  bad2a1348fc0b54791f9f2875009c8be2d30f14526fbbc22c439c1ae2b865436cd74f428766e4e676b795fe9333312f6334d4e4ec2378e26
+    d87a8f7ca45620b4646fad2c22792538bd16f37879dbc4d87c21cafa60c282f6415fde2fb6293a8b8a6e5b3901f88d75f9243023b68f41d3;
+  d6d221f2bf71eba62e49db570e13ca69f4a8a33543298619cda7c8d2524c9446b11ae7b7d301579f047565b7044b5286c2478896800ad992
+    fdb163312b884bc6d74c4c269011311388ead9c069884f48487f9ac0d62b39aa175e81976fb599716aaa68db4c02985a6f0ffe522b0fb21b;
+  20cc05120202e684981bce1db74af493d218ed8e0f5e76fad46bfeb98c69e504113b0eb91771c602cb422ae09846fbfef7c47afd09689277
+    b07d84ff34dc5f0cbcc60037400ee04ba09f34d1d7a0c6a80672a3f6c1da630681d4207ce1325d301b36f0acaafd7579a108646f5a26b755;
+  72c4c3e4fcfd36389f558e0d4016f3f088145be47b2de85ee7ab963fcd8c7adf449d5b7c37ad8a32cf97ea9bde5c220fc896cf1b91633e19
+    1e8f68736c3b259b661c0c1ec065a642e717f9e4d86003411b8e15ff89537d8e129c116d2f8a9e7e1b46879deedd66fa6ce2b60ab680cfb0;
+  8c524f059e7ebcf3af3072dbd2f76ebc530adba6d26240c8675b5451d5558db648e7a61fefb1369cf2939ba1fadcf8e40385db5d57cacace
+    a19188e6821f5cc2c53b7e59ae6f70e95fb929c0f282627ccfa2c6d9cb59dc871e9dcf3e2a8d6d66b7a6907d4d36c9de03c3d74325d8c1f1;
+  dd47eb6f4f68cd3a9abd6af401bd6f3c795d89474dd41a89025b24c8349a0de417c3676bb6ee36002d7c8ff6b4fb4491e693cfa617e079f8
+    871f46c93dea48a0d937b3f1f724f66615daa9200f0b48cee01c3b20e4a05dd6ce01bd7b2713e096cc2bd1c5ce7ca0c075aed0fc1ef6fa51;
+  577870fdb83454eed0711eb51d7bd91f9843769737566823e41d0668fad45c82fe069347769244af7230e6fbc40f5a71151279ae65e9893a
+    efabd464218df4a490b9870d541c45e9b7ab29bae25d9e976539a553995ded0edc5610916883642a032eae4e96eb9e4e1443f747dec98135;
+  8ba84194db2aad7b46005423b1251555ea931b933e0083fee76b9863fb528927f27a4a0617b8d75de306103b592a0fbd51b72bfbb051ee2d
+    3a29e940174d846df65d51070295f535a03e988fc401ef62f5dd2456cf52668390e61e7b6912186d6d5260f6d6977004003bb106abbdaf02;
+  9f17e0943b4e39051abfd8f4512ef177fd5c368ebdf4b0af49df2e1d89e30c4e68ec474d81203964bfe9c1fecc1279e5e4f598ac97b48cb0
+    d794adb47cff3f7666dd0a934896015e59f278d9b1d6dc7a06328da96d6e6520670598129b7e1b26f9537216364199cf5c0010f23692ec90;
+  a6b51625066e4cccb7d45dff5b182e2a6bf4b1cce7090a187d38dd39f08afad6f868c0277bf70d5fb88b6e67432526e4feb39ae707e34990
+    67315efb85e16a1e97130092cc351759d9c18261845329f63eba1e77d91ad2c7df51ffa50b655de81bc807ea2b3e137214a2b08b400f2b7a;
+  28a0fe53cf73a0419fdb4cb493c6425be8aa812c3693411816c90621097a9d5687feb16ebf874a30ec849297742b7aab5921427d7b474190
+    69300770a5bc2c9c599431e5f879ef3f3ec065e3a2cfe82e3499018add58acada581e9e81d40799fc74674b9721da018a64138519abdb108;
+  fad58259395fd5bbcfca35b095253a3a121b62f1694e14cd9e2f85bd08e8a15d1dff98e3b81eeec678821a1ee9d6192c8351bde45ce0581f
+    bee7e0d6d4de419981ba2aa3964adfc7545e21b72b65c774b2029c22e06f251cebf04cdffd23e2051b57f986cbf7e56f047050714a275e72;
+  91e548171a8136d2d26e6bea02cb16ec9591e9cd5b913184b22b43d1029bdb214ef737f6e45f473f7d95b8fde075645c3b0c2dd2bd224278
+    abe2936ecf2ac3012455dcac906f691f2d96490539c9621658fd7f66790bbfe0d2df07165ea80e0c3282642d45b63b7db476b53e4f94f1c5;
+  a52c855daa9ede16ea8de7c681436d5d31eda6ca5b441f972e5cf5639a1c56f689fcb411965271853de2893f22b81d7d0facafdb49292e39
+    382480e92d44199fe5f7c357ad2e5a7277beab1a3055d48e679516151ca85ee3cb498b172087d588738daac2ec1da9638552d76be97626ad;
+  c65888cdc1a575fc9a06dce8800607f56fe88839948b7e4e2713aae44b0ae6b7c6b68786b11b430d22766e9e4d76af118a972729c67c06b7
+    31311e4ff9600089b533cdac6c05de3ddfe6a450ee7fc90cde1bb297e2deb750336ac3ed2f6fa9b88ece846be6f6aeb392f6753eb6fa475e;
+  a61c81d621df61ece4b7274f63ae4e9f2e2df1d603fc9303ee361962b2d70bd783ff1b5d8f3ed27f17e7773b2b1c85af7fbd70387bdef5d3
+    a014ef20adc291c0238be3759ceb9f9645524b961761c8a991ce67efbddda568359fe36e762980d75a58a7ba10b803c9f883fe4d2239a08b;
+  96ced952366c324c5fccd48ac5d475cc1944e7e455dc093f0d9680d6d6abf1e45c76a286ac46acb4de061b9eb673a2d3124e6c3b921ad71f
+    6debaeff6a7d2bb2c1389cb3338fc87b2a32e651ecb8d43fc012efc9b9c59c79487b742f0b30d0b64ac283af0d3b877102c6a7ff0f606eb9;
+}
+
+inv {
+  d665678f17043846f953fb09c88295d7fd0a53bc70ec7d396aff56e7dfa376f3a338f0da0af9e07b66d3ff7443e3829b541bd667201999d4
+    6839ab730bf666c6ca987d254d83bf32b981b466c147f04b823981da75e12c9ded469a0a462861a987c13f05739f47596401fec25a5eee5c;
+  bb8d567f6163a92603a388405121bd58abfcf05c596bf0624bf65b6a8fe03e6940648382fe01e5ab58e37ce235f4acbfb9e02624001c49da
+    3f60d0fa0036da2db915e232bd841d09f006d4ba018c1d268af86d36990579d669ee63393b95c202645dcb1225fe9e64d14270905e63eb79;
+  d456692d8e00bf9cd86d22a9f4ad9e9879e6298274854ae82729ebbdb0cc46877caeb381506dbb097b10c180d615661189da65d431e3003b
+    6e0f5a173fb60be882a2b570200515dc7ad473e2192470ba89e6dda6cfdd6ff77026a1d76acdded7b5881a54d68605140a4ffceae70906d5;
+  359ab073b7465de1358a5772aef79db09118541c062421cb30a972525a351f16b5545daed5f9ce8e91a283818df6cdbf443831fbe5f3e848
+    2b0ea74e6f4533498eb3f34247a9674f3a2cf2ab0e4fde65ff11b0d1e756430bc5452ca4b85bd3a1f10f6e4e3b1874df8d19f390a9021757;
+  3509fb8b7688f354738d4a8ca4091f66c880db794aacec6115e675090394ca77e1c83b955aaf4d8e75c6e4e9200be1ce5693532a561d970d
+    cafa18317193054cff7884bada249422ba889c19693d66610fb774fb4bdfeac0e67a5ba6f84ace126c125c46b4cfd627ee777e231a6ffc1f;
+  42e2babc095d88b61caa8883e91fd045106db9c4eeb4d945703c1dee3229c58869e21f73909ae2f3c4437d301a2a3724d67f6ad310f63835
+    28c43a594b6471412f504dc10fd79d2c04ec029a7723cfa0a90aef036991f1b56cf156e2cbc7533671086edb54036e9f4ddc9bfe2e3ffcda;
+  9c01d006213bcfda413517ec815e8cebc06707f5649398a9d14a5d64754375c1ded8aaefd838e8eba84746de07a0f9b32caa2325c4c8e540
+    0a3b64374ac2149d602a9f0ee4fcf6d814c476f451531a086b04b0dd598287b1758f77780b7de08573326a9767e816309d871fb1962bb1a4;
+  7bbd8a2de4bd2ffee4e93d0dd2fa9c5d4abcf9ab86c097921fe7d603b7ffdd07006c7d79d1a10267edce9b3069b134bed727c8f0ed1aca13
+    c909ffafbcc09a96723b9afba8ec0d00d5ce0c9d85de3c1e4de24cf44fb934bd0d2397b380885b7419cbbeac339fc85e2161f08c2f3515d7;
+  6a64650ccf103604c3b030415f7114c88c7094fbd0fd2f2b6b907a27fe104ef4e0684c17ff9f26f15eee5e7442e679ecad3e49a0629cb5c3
+    9db77e47433997640b4c0916c5123e8a4d9c7c0bcd12b2dafccbababd6c9dbc3f4ec7991ea68b3ad4ffc6f0be4920764c65d98bdeba8a811;
+  c3823c71f6c0f3679cb9c776e437e718f856c8fde038418942d062423113352aeb60ab84138f2bf73f268386cab0f2a1419660e966c24187
+    558bff4fffa85d6f67ad44938f5b4473d250a87a120af9ff645a7db37a43808abe30c19c90bac94adb190840078214518e189d6c9fc5bef2;
+  44bd920b0eaf47695f93d063cd85c43c58470c23796f4a106b298d5f2b091210d103e9575c369bb0be66b2323d89cc29602a8071697931c6
+    67464f2dab3a95c9ac7c783017105df7d561d7ae499ad89c4698da2c0710a1654c89966a7bd30d2d5c0511ed9f020309cde84cecd66be8d0;
+  a6b12fc35e62532ea7c1fd49dd834bb7957da914ec3daf2e4f446b54916d016054cb59ffd4e3d9eecd4a18b08033ee8d9ff84db072cc4f86
+    cc70960f0bb743da1b23d11eb87530a820484a861abd68d2ed7079b487d88481d915b1b34c29d9938b5fa54d05c75cb1aebf0b722a6770b6;
+  1773dc978cd9f94186b83fd5e10a456b48058acd9e3bbacbecee2cfd0f8303c64721ca64c2913d57764336ea055700c2fcafc60d9f4b817e
+    49e443096eb28c167ee62c63bfe4079418fa8e7f9e58757e13327680890b02ce7c9e5bbbc51366e0b3f5315edc456b6f65a09ea1b1a24438;
+  552158406db36accc32edde2f0ce46e23a1d406f60e54ca6a789386df36a57434c826c9586c4b371df3179f675e5956fe7dc3022f785d9e0
+    e0025a1007d3dff5e44947a0292bee8db5abbd1a0fca1d3ab0a64a37925396cd96c21af54d36656eb0b60c63fd5651622ec5487d53f976e4;
+  6b006d36f60750bb5c59f6eb31e2efe6098ab67de7a7a8a27201652380a295c408ec78351c2a799d62227dffd4731b7605e5c462dce2c1e8
+    21785aae5f73da711f6144894728627824b685fac3d95068a9d0c4e0ee058301a8953322a5ee5be75452164a55477f1a987103a718bf98ca;
+  700cb76911e718ce8829f8d767c53e333dd2f7f20d2c020d1da3cc9ce96b9f6c7aad67450d5aab8f8c60e8ad77d4b5fdb86fae92a0ac30fe
+    257f202b457f19f27ceed047e6778204c2b71646fcc96e19c6f07518780bb5b1c456fd6e93ce2f1cb0aea1a225137511dc71e446c1fe22c3;
+  0c909e0326649d4d375452c5b233a86e90b0fa88b20aa54e02685edd02b70aa022ed8af31e994b70b20101a5cfd8e16ba53c702840b4a16b
+    98c41073ea843b4b5ad6b0589699f48a1576e3d834f8e595b8859069b71bf5e16126aa93b20259ff0a03976ff6d7f78239ec1059f8cf1193;
+  4529761361781e67037eae69c5ca9977b1e21468834eb83cd369471e5092249015cd263563bff9348a0f51e4a8517e27236ed1ecfe30a780
+    e20d5896be18bb273bb0ba84f6c070de45378bac6d793685e654764e5aa847d48403a68560b48dcb12573ac737366ae701072300615b4659;
+  6fae83f7eadeaca22b6781f400b01f9de186b12e31fdf56e0ebe98a8420baacf5069ffdf26d0215c16b64027dd69ff29709a64cfe8f52dcb
+    daa266a02c4500d989670eb63ab78219892d9c1ca2e17f4b5200cf1d920cfece5e80050be77f81a616de840bf19c23835ed17d57aeeea0f5;
+  23ca16e8b4c15f8f2b216441d851368d185a7f8587f398b0cf775875678241094538708da0cb2260c5e056b59c003c30c8f84f5088d0c99c
+    9de1360b4718fc762e6ffab46827c5e4672a77d797aa608b31c132d1ad8138e7a64ba1f4278a07a6088f8cec9a01ea9bc1427c55241433c5;
+}
+
+sub-mulc-add-sub-mul {
+  07ebfe324f64b54e938951a1e437bab4d35c2bbf5f210c5d78ca2b4eb7ef9c3dab61724be4fa7c2e43086b58db7e961f62a44b029f9901d5
+    d44920bba642b8162d1c3e956e060bbe6f15484298c2ec55575ee378556b0e8826f627bebda963b7d4d9f1c768a49cd408957105d3c6008a -41997
+    e3647583d41ee4e813045d4f7a8b53af084c1dfeab8db93472810e8c54d253a2c100a6fc341bd92223341150d363f00f1c2bbb9ff9f8e7ca
+    d61c770618adfc42ef3ac4272dfae86a674f8ae4c273d26ad7b9a29943c4740e0c9535812b9ef0d17fb4bfcc77bf60646aa96a11c29035a3
+    76c496c2c798e23c657eb80c44940229ba88f21bcacb21cb6b4cdc8b93d10832f3c4d31819fbeacb6c5a516c7889f0e639abc29ec4d7b604
+    5a94d8487539381061d764a3806e31e1b2add772d2f9cb9c4fb96336f7b36534a9a0f7caf30b7e086c875670a0d568f895502983b1dec2ef;
+  e1792f9e9c7b70293e041a9059df95bcac07b4112f25d35375ade1794660a0a10c520adf06fd477c79a170ab88108e4b74bc47ede8ddc0a4
+    6cffa5fd3f07f203fcf08c399690b793a152e65aa99c6f9b465d39861c3c46691b635cd02c9e77083ecdee73a54aa1aab5ce6044cb2cb8d8 23228
+    31cd1c1937619b8fe711d22912104fd03fcfa3f14f81f3bc7ec9839c5440836d7c9b92158c818933aa614f9a085f9a1c18d71dd11d09a6db
+    2386136ccd81814300b5f3fa148c4025e6d7a2d9da344e56b42811deaa3d4ecbd5365f91d941d7bab20b305ac6dd0457a4b81cc611bb5f11
+    16bf2b1e10c77597990088a8df91150a6e7b6c3008e6584909b2f7184358abf6b59674e210aeec3219a5202440139aecde4a4b6e0f46dbaa
+    dd373d5908b408e51e3c6f8da1c439f845d40467057a34a96cd53680661b329f073d0bfe6af98543d5d38c3d0476c19d20c54226e7bf8d7c;
+  fe6d00268bafd5878e0a4ddebad8dff25399a1d18e69f94b471029058e9712b98263d88b4dea5d08d8fe70b3a62ba2d136c2c2713ca809ea
+    12be184341bbd7ab5a668f3c3e4b13a60c70cd4e1f229f90901be24a285f3ff0b7391aee82529076c14232f61a11b6a8edffd56af603a25b -252008
+    178e5309ab3a578b8b09149da9539d93309c5214b61e3c045034847354f59d58fee27e31dfcf5d5daa24b5a605686b468a3db727583709d4
+    8da5b2f4c39e442c729a09cf04405136c55cf7b76fbe949ec3b34fb17b2edabcac44f4b4a19afb58c9d86eb87a0c58cff2cf196e6cac7da2
+    3b0be5158a7c13a6c71455025ea5b4466eaa321956fa785f9b74872532e3767d796da94ac47e6d25a368ea1ac79c44fda8d2e13edae5c219
+    7ad75999f1bd89b130fa03274247b36b2c2684163097d46deb852083b5fab0e8755d2150dcd2ee563757f05eb1d132df284ccf818cd56b9c;
+  e4ef0519fc7af554a4a348cfcea95c6c18752f4a35a946b7d2ad1e6aa77d64ec4012eea4faead2d91c66b0d57198b19317a79e136a98caf9
+    008c72ff456f0f941781020ce6387d8065772af7c68955668a7cb05b53f00960f15fc463029a2e2a353a2fd3bc43790a847ea35c75d6cd26 287411
+    5ee722a21955b3bec9ed5f151686af9da9d3f5e6491e3d9a8d82237b1e3dddb58b827264ab5a1aca3eb8c1de7922b59744567e0467a4357f
+    9381ebf793f7b3f523f4621e934217ba463f96279ce2e7d165fc00ea037b20254948132dd2f0481ad9816e1f66da64d081e4d24866094af9
+    62e4648d4716c6e0f99357c8c099c4988cfec4fa21873b7928350f174679f59629a54d78bc8d239800709a585d4890fb2b634f68ec13cd78
+    850e54cf9388bdaf3de63c3e73823c0f39f0b9bf15a9f0262f24c828d2fc8cd7d1ac939193804fac3d92467e26ba5e57d1dc4f8664a270af;
+  5f3811d6b8277fd93a197bf4f3dec4bf4dd5fc6a0ac92ee3d0e64ae48fa9e1373cfd63da945db924bbac41a2b78f6819ccdf2820b1a3c4ff
+    ee669edf4430678ff1190be8e39c06dd00cd9982ab5b625e5a6814ec456f68f9467f1f38519f4bcb392772479ba553060391752ab4fc70aa -254559
+    3d11820f867d39b2b1169030c24a9307eb387f44ee0fc8d53a946e56065e8cf9af6069c9c8099de1565f3d491c33248965ebf941987ade48
+    9582a93732bb52eba39d81c161dd0a996b50dadb13b4eb54085ecb9e86117ed25cb7c78d1a5cc3807a97d740710c2fbf65f97e8de3591163
+    501c2ca79204d22e6c52d32d405310853e7e29da884c0f14782e9029929751821f81a9493c8dd76e582bb48465ce5b40d7c5e398322ceada
+    dcdc428d085f7548c99d963af7b8af997c1503307d75e298efbfc042d425d385a6b7321d86d5af13809ed3ec6bc5d96afcd026d08caeaa30;
+  d17c198598d3b5188e5807c445976b769d458ca952a7b99da4b19ffa3b35b6100bd3605136143912640e402a7cb91675e61527244844409b
+    9cac226e58f5ffbc41bb82b769ed12d061eef4d2d0369eb12724bcd4db6a1ff85f22d6b25003cbea0308a1b7dcce1450283d4e96ac8d4b8c 516083
+    9f92e7a68af5c7737f3f3e959d2b55230ca11e0dd467bd52a20b91816ce61ac7fa7059934c7a952e43058ff4f277170ec5ecc4cab51baef5
+    d4fb3628a45e2290c7fd201ca434ef83911ac2687218605782b3cdca577e692a389756e6b9a0891311727d623fe8ea601fb13efe238adee0
+    ce572b3cdd011749634a577c727aa4516d00c4236cdf3b4664c2be35d3efe7e0b140b5ef2d4a0339a8a29eb26025e60e7d166913251966be
+    790d7ef8990c44fd271d5a41c55a6d552f60fd8980c1c3b91919fdd5e291c75b0a0cbc4224dfd2ce93de2eef53249f3d9d0955f05b4cefaa;
+  396fe89631a66fcf4368600d3a8cbfa6fc903cc8d157f13ac0e4a569436c3230c0a9319a55ebb7e61d428e74b8ce740e973ee5d2b0a99a61
+    f644b35fe8d9bd57a0e6837ef03d889df2248a06a0e52a9cdc80307cf68c496f34aa82f246b8d6e040d166ffa1920b7d921938bb33f8e8a0 -339102
+    716c673c21cd0d92d86f8060beba37c65b7d9eb8b1d4f8aa5665478278ea5c3a2a2c4f606aa179bc8b38f4f37b3e077959ac7c8c81a5fba2
+    6379d7843800b16ad06ff70511c7ff656fc93ff95a05617b4adf7e2b6810cf2cf81dfd6b0c7d478d52f2f4989a13a3ccbc3a4e30f85a3279
+    5b982eda7d33ef68c0d94400610744b91572443614a128bc90011a953bc9422910640047809049a513c413b4390a09395da3de6958c44b64
+    91cf825bde194c05cadbb9dcee5f08f152d2bacabc144b4ee498b82ddf4074846ccc75d366184ee29bd322deaf99d6ae81563cc2e7b2e98d;
+  b8bb334fbe75e81a417ef59e14b52898e34dcc8bca4bb2caa987dc31950a9afa4dd356735dd163eba9073056a2e7f259f1485ef37acf8c24
+    0e872ffa891f664be01868170c452c25cbd41dd0e4df0a66c0ed67dfd3d007c25c50e4b0615450906c69788080546b9a4ad425402c422580 287059
+    52356b0443301992c676f6cfa7eeca79d586324bbb5f8aa71163b89d863ffc7879e55c363ce260fd6ab160e18ef0921d81a4d634799fe816
+    35be6d14d3dc696bb20c90de43c7e08b2427ffc9c63a5c37767ee6756894b6d94c748e9d2c4d8519cbdc2efaa215874bb1923cf7aa5a826c
+    8db5fd04e45f9f93c7a2a2e238c7f001fd7c059d02fce693340c106ebb98d7f6f3029d3e820e2e3e912901c33c3dfce56d68a7c42a2f2e0a
+    29ff4f6533b4292d0e8f2a7abbd1269d1455d8ec34cadca9df875d544ba10395c36d1e750fb66ff0a9d97a92538a63ab48db8583fa6ebd5f;
+  74dfa3166dee585d6d4c5f310259a6f9a407e5c520559f22a3f290733d2f0ba8a276d46820bc73f9231fe25dcc8e9c2e2542eda5024180ed
+    50881f8d2fac3278cadb483b7db6e179306b6e967f8fbdb53f70f443ba9f1b1073921a0a1f46c8833856e6a0ead7ab6f445abf833d2550f7 -430161
+    10b16716f590c13b435ce912fd4adb5dcb4982d8e0302dfccff5f9d388efaae716e8d9e2915de30d365a04468f4bec0f4489d81c0e159bd0
+    b4e67998a7579734cf8f0fc281bb29e1c3dd4dd4d9c1434e830d4d093ea2b908137dad647ab349c5858d1b3ce20f7378546033114dc0fa68
+    76a10a58f6136ec6b73d96ed1d506b758ddf6f54326f7b2a12d28dec9ed880dac2d16f2f57004c7df846ffb4c5c7de814a0245668df0e804
+    33216663bc217e49c81d64e3957f3a16452193b677c68f3df80315c7496995a752c7f42807d22e6a23ad2f5568ada163ba168cd1ff3631c0;
+  c047f2b5077afcb0c246abb9104309be15f2c2da8e703e74459d34281992fbbfb95b1012067415c5b17650d10f5d2d12b84c3c825068b881
+    831adf8c6481178176f11f1d7010fa4b51c6e30ae8a9aec3a748a47bad643ff69cc8e1ba47bf9d33183de1c1220e989c58d6b23e8a602ddd -445266
+    db32546436b5d585cd4fefcdf8affd7c467c31699787dedd133f205641fcf74ee246a5f62dee25eefb54091b5dd0cb4dc7cfad9cc68bb9cb
+    9ea3da4650bbc07d7cefbff97a9bebe5aa932772ce0e47a4bdfaab1be5457f652f9882d8e4ca286b7bce092a4fe93b1c0473a2d5d33d92d3
+    a8eeaf02c9e281f2522def4792beff7382ae509316b80ca3e70b36c080706f9cc8589518dd9b27e61c3f3e28c8608524bc201076d6ae2fcb
+    463c925f11987399440da6a502b87ee798b2dfc37f012951af57dda87651de7bb96c2c30890e6b65771e70ee08bf41e2bde696dfcb177e83;
+  63428356e8b4c5147870a0e653866093d5ca20a691524e3adba1c95af70fc706fdb420391c230dcb70cbad21f028b03e7554efac7bc8bed6
+    e99ca8f8dfe11c545b49031f68a605229adac19568b5cfb9b02b9721416c225179968b5583d0588ead12790c5df7c13bae466f934e997b4e -445574
+    7ace55b2ba3365a01a872ef5882a8d47518fec37bd816d809d4716301a7b6811926eb5aa593fb2c852ae2455fa42e92be1038d96ebf31966
+    db9244d18540fb524ec9455f493b570aba16ee4f4491247fa18efe63a43c70e533ad6660f64e836675bd788113993836c43a3c5868efe068
+    8cc4affd9631c4ff75313f43588d977f24b58a0ef1c3275b7c142e6113f20d72fe974f11c9f7cf3dc176e1799dcb4230d5bb138ddee2eb34
+    179440042f537fcd021678f32a1346547ac1a4aa5dc3e5bed329c543ec35058efa16af2ca066ffb7087a3daf68345aa3a0a21c6133fd3a59;
+  993bb02c2ae6974f6988293ce6eae32f520758329e80ae242801f45937baed48cb9bbc111731aea52883ca30286b7593746e6522fc3ccd54
+    2bbbb81c8e51356355a1e49ebc1c6e160119eca0df8c07d171783665c9d96af7164e6bd7384002ed5c19f531bda705b90e06130bfcb2ad17 -159409
+    6b3eb1b9dac2b8f6d86a0d983bd2b36a98c760ecf5f4aa30e22726e8048cc2e633a7a7bdc862d6bb02fcb0d9e854ac9dce8cb9982645ba44
+    fba6ac4753539d9dea1190d160994937af74be3f73856e56dd7ef4fffd1f6e2d99f1466fff7ec4bacd115f4e1f8708adb8c9afa7b17ee887
+    7f28c3d4526328a9f03cbfb3a0b04fc2aac564effcdbcedfd61a160d0499c83742255e86764c344887a93f7f1851903372e40d7034b16f03
+    2bec5989a2f2fc934dc0e673f07f0ea7ebade4d4bdf7bf472f7e779a716dbc4aa01771bd1dcc634780b0da41389c108a76fc67235e028142;
+  96ab5a1f5b6cffe08fbe3118d7bf2d09e6da8dfdb0ce69cae1c2d3fe71864db59befd7e252410d04a858ef680a754b2d42f7f4ab7cd38e0d
+    673d469ae9193bb79aace5d641041c94b81f6f9a9d12e5a05db86674899ed5650b8d4d30bb00f2b51b0261245dd3a2e18a10f8109cae1535 197264
+    83f7aaef3c944d9d6ef3c329224067a204f738f3625488ba01845e31fa07dda26e08a4e395687b322f594fed860bf1597ab33536c5f24c53
+    9bdc4e2e4e412b65af636a24480a40ab73df53fb209619044317f90df9bba10e5892aa502ee86e1039a8547a4e119caa359e8095ef0f5827
+    659ea4924522edb2aa50430f9bf5ba3112c60af504dd290215a52c8bc275fa8fe0042348811a7cacd0d8bbf95255a547ae9b82db70326b25
+    a28bd6727521f46a3ab3ac32a3452c03b7ceca3c06c4e5f260c1868935269b8cbeb3d303cc6df3faee2595788af6f24a52ef8237763793a1;
+  a0de95d94e3506ed563b974d07454fd347f7b37aa1e9453e0145d23e1867f2aea78434ace37877022343cad84d9100c7683b33c115014903
+    ceeac84a960af7e749645352ddbd4a9f7fd77e96268ed8548e4247b6084e13a193233ba02c49da24c89f2a62ed50029ab38eb6566167aaab -5830
+    8568f4c8837fbef756a3c03ee6a59f7ce0b5ffe370afb5c18545e4fe88eae49c65c642494e1cba91c4eb8b2fa94df6ca22b21d3e7b717f3a
+    70170f374d567a159bd8e354eceee6aacbedfcf9a2be6b530808152a96e93a301518f0042baecaf90170bdc4cc8c2e1003bfe737cd258929
+    cfa4cd159f5793a1833bfdb97261c204d3a80a01e1488ce8c628e68225bbcffa785093267eb3f75702e70b6abef59bc0a223ecfa09aac356
+    7302a30f89bf3163baf814605772b0ca175aec6a030e53a2677c49351d88f1c095a1974e1dabcf5859c206f4bd3e932f7973766e81e0c278;
+  c297bf2bf6e64d4446a2e5d1c6448f5881c5973ccf6e75730dcf2bb74096e603a354461e02e818a94ace1f47053d23710719135cdd366d92
+    8966ea0a08b7f43706340232c68a264c54b4e609ad8c901d0c0768c2aa5660434e5d4ce7510883238b17a8c92f8013d2488730d3a28ca291 -266784
+    c52a9e11dce1938ffb862357a9a696ee72c9fe18f7163d29052c949b4ac0fa151052dffa3546cdefdef598c28ebcc620d064783e8ee20413
+    04f0bdf761a2cfebb822a12bb44b2def0906ae99cf84e7bff6d033e4082c2fed1961942288b0e2beb11a0e96db8a5037fa61631cd89896e4
+    add0f3d532987482db8148a8ef3724e27ad497bdcd804686114acb3a86e4e561001a7af6420bbeef57b77bf8444c4b978e5cd136083d9118
+    efda70e401d9e5beea539fb3ce7a145bfc90422e049387f222db53edae81078046fb1e2db538ebd7ba36ff5509691c83ef9c51f3581edc2e;
+  06768f69843008554828e6665685231b32e60fa878cdc11536819fb04874df4c6f9023b356f1cfb03a1706d04f472f1f1cba2148e161f936
+    61d686b96bd02a99efe67123f3962e318ce9836b1ed0b7a39ae98483f38efa78a89063badbf97325b4743b2342647ed8a44a164e76328781 -301528
+    f8987e1314d3a3fa7ed1784fd04e4b381e9bbee6a785400e51d66402bd95c13de03e4a8213fbcbcec493f0ab8f3aef54794d8baeb9579b57
+    dee30a4b1359ae1459b790e8bedbce16eef2e299082dd2a07e46063c79af11524e438740af78d161afbd5b410888ab9f64eb4129dd1caadf
+    cc52f548a25b964b2556be59c5243b63a323701d696d8d3f866be512554227b8aa3567960aec684ba50701be5528f188a9bcba490517949f
+    bf88a0bbfcf8d048525ba4b7f64988bb8db5ab4abe40c20530b512f130dd54ed8f0bf9c36f51c20735415e25905d4047d896bf6eef7325ec;
+  b2fd5cba9d6d5487c5257954d1d170a99cd27c2e990aa7b20006d8615b929f9024c5b3a0b063dc9c364a668cde34b4a26ec4e374d38d2a32
+    e4796d1ed6d6cd3443a095e6f91e80db3ea043b1f9026e4015a4abff5b2ecde79848dd0b856db0f47a6f29e1759fbaeb2ac82d2d8a939d89 -338517
+    65c169eec5df306b2d17d58f7c1f833bea184fadbbc8924803cc0ff2804d24531e69004059169f29423b5692d54c92fec7936933eac92317
+    ab7c743547424e08a733876339fc4434d1c0ede96dbcfc7a92acbb6ec4e1f6e330d13cee90e4378863022bb49b57b6b4617b37fb5e6f7d0a
+    5fb79ffb10f7c3bc4cc98025f7c7f71bdb4041a5161e7fdee2a701692d16ae77d23680becc76f14e66a7e961d8841adad5485043e13b7694
+    365da4af25fe7f21edf0706495f44475b2d3a9b157b3008dd745cb8e756e3da662108d6c05d573aedfbaa0fd0c8420b6b7ab8d418d273be2;
+  0b1fe73972fe3400b1a9563b759cc65dc7f685031405e14ecbf1ab977c7c9afceafa91f51d1127f09058b637c9cdc39fa665be7c2a4d55d6
+    349cebcab0d98aea0beb0d38dde902e4f6caa23cbb066e68ef8d384613b4ecf6d21780ae1844c4b3eb9598cfd62fc001505d3ce4e66746ae -242599
+    7c7018b38125bf7415723b398f888458a647f4a2f2ff0b7dcbd6eb7ae873c44068429efde0be1f2f28ae1c8e01db9f812442fabaf51ffadb
+    c125c39bcf8ae742747cc2f711e470b8386403e275a14e5651f1dae89231cb3ee186a3d2c1b1a74002ac70e1fc1a5adc89a17734aa231650
+    feb78a43f6e633e7b92ffb2789f6486cc7597ce0ce9ce2486828af900d97c9ee8fcd0f52d43d01e141f9987ad194dfaa8a31b9d649e487ac
+    32318ade75655d7aacba10759063893db2482b2083a924915afefc63891f9951b58bf5525f5b330f16422c65bdf77a5508f1830849c2a941;
+  080f596d6be6f4554477689f6ac45ff60a880924f6e609701bd6d1fe32eb4f1be13edf39399eb170fa5b5b9801e38301e7ed768383cab6cf
+    f14535e25853aa97a8bc8128610eaab77977d07d3a89608a837b16fb2c2878f8edf11a2276f3c42ee21ba275dd4e6059574f887abcf6480f 273443
+    f273139e62664cb2b5f9c268c37abfcddc054c0f30ed8f0fd643017a989512cf115467ca704b3442b1c92496e378f4df6434b27e9c9fbacb
+    8af391a0b26881d67fa19196d5dded3e00cc3dc60247781ec1d000e635b6f39f7186e948bbb5b03894f0a000b27838dbda844836c5c29d99
+    56513a9d50e48e2464df2069404f09c21d4bf112d2223ea316e78cb4ebbca403327ab344af7e15b17b9865ea8d801cc7d4f6f7283687c3bd
+    9c3043751d7889855a337637b51e1fb9c7bb47d75ef529e4e8545822cbb2f535ef1579b9c660813438e2e9f16bb35f4a7843d3e7820e1406;
+  a5d6bc11f9251ec7d03e87c267b2eb05d51fdbeb24970e2c87a7d8923f7adbf4bff904e5ae498f5984072d7a21c703ba6765bc3292e43bfe
+    2be24a92df5b92545f72e710d4a5c71f881d544305e891dc9fcf2b39a24a5e80fe44f856411fc886566f428b5443af8140543e549d092170 -140922
+    629534899a55d04d6e0c879ea02b9eb05675cf6074ce400d8caa25b2adc59eedcd88f203f9440db9fb06c5bfcd65512324d01f871b485650
+    867e9ed2dbef2b0d485a7d5dc71266eae2b6053c2f1e09dfba6192f015419e06868aaaefefdc3b03cb26e50cc4861f38eac76ac69e5ab1fd
+    61d009f31c5be4eaca1f44ab6800f0116dadb7923e427db6e525f03c17b380eaa6d0c8f06440674c80f0040c272b8b956366a82ca28dccbf
+    a7628d76da5a4a8dcabfcd28252b9fb1f780e2d32f72a0f1f8da7ff7d354c6d35a72efb2b75e87c332f93557c1a5e1bd51813e18ee1fe241;
+}