New multiprecision integer arithmetic suite.
[u/mdw/catacomb] / mp.h
diff --git a/mp.h b/mp.h
index fcccee2..72aa675 100644 (file)
--- a/mp.h
+++ b/mp.h
@@ -1,8 +1,8 @@
 /* -*-c-*-
  *
- * $Id: mp.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ * $Id: mp.h,v 1.2 1999/11/17 18:02:16 mdw Exp $
  *
- * Multiprecision arithmetic
+ * Simple multiprecision arithmetic
  *
  * (c) 1999 Straylight/Edgeware
  */
@@ -30,8 +30,8 @@
 /*----- Revision history --------------------------------------------------* 
  *
  * $Log: mp.h,v $
- * Revision 1.1  1999/09/03 08:41:12  mdw
- * Initial import.
+ * Revision 1.2  1999/11/17 18:02:16  mdw
+ * New multiprecision integer arithmetic suite.
  *
  */
 
 
 /*----- Header files ------------------------------------------------------*/
 
-#include <stdlib.h>
+#include <assert.h>
 #include <string.h>
 
-#ifndef MPTYPES_H
-#  include "mptypes.h"
+#include <mLib/sub.h>
+
+#ifndef MPW_H
+#  include "mpw.h"
 #endif
 
 #ifndef MPX_H
 #  include "mpx.h"
 #endif
 
-#ifndef MPSCAN_H
-#  include "mpscan.h"
-#endif
-
 /*----- Data structures ---------------------------------------------------*/
 
 typedef struct mp {
-  mpw *v;                              /* Vector of words */
-  unsigned f;                          /* Various flags */
-  size_t sz;                           /* Size allocated to word vector */
-  size_t len;                          /* Length of current word vector */
+  mpw *v, *vl;
+  size_t sz;
+  unsigned f;
+  unsigned ref;
 } mp;
 
-enum {
-  MPF_SIGN = 1,                                /* Sign bit */
-  MPF_BURN = 2                         /* Burn word vector after use */
-};
+#define MP_NEG 1u
+#define MP_BURN 2u
+#define MP_CONST 4u
+#define MP_UNDEF 8u
 
-typedef struct mp_bitscan {
-  const mp *x;                         /* Pointer to target MP */
-  size_t i;                            /* Index into MP vector */
-  mpw w;                               /* Current value being examined */
-  unsigned bits;                       /* Number of bits left in @w@ */
-} mp_bitscan;
+/*----- Useful constants --------------------------------------------------*/
 
-/*----- External variables ------------------------------------------------*/
+extern mp mp_const[];
 
-extern mp mp_std;
+#define MP_ZERO         (&mp_const[0])
+#define MP_ONE   (&mp_const[1])
+#define MP_TWO   (&mp_const[2])
+#define MP_THREE (&mp_const[3])
+#define MP_FOUR  (&mp_const[4])
+#define MP_FIVE  (&mp_const[5])
+#define MP_TEN   (&mp_const[6])
+#define MP_MONE  (&mp_const[7])
 
-#define MP_ZERO (mp_std + 0)
-#define MP_ONE (mp_std + 1)
-#define MP_TWO (mp_std + 2)
-#define MP_THREE (mp_std + 3)
-#define MP_FOUR (mp_std + 4)
-#define MP_FIVE (mp_std + 5)
-#define MP_TEN (mp_std + 6)
-#define MP_MONE (mp_std + 7)
+#define MP_NEW ((mp *)0)
 
-/*----- Memory allocation and low-level fiddling --------------------------*/
+/*----- Memory allocation hooks -------------------------------------------*/
 
-/* --- @mp_create@ --- *
+#ifndef MPARENA_H
+#  include "mparena.h"
+#endif
+
+/* --- @MP_ARENA@ --- *
  *
- * Arguments   @mp *x@ = pointer to MP head
+ * This selects where memory is allocated from.  Tweak to use more fancy
+ * things like custom arenas.
+ */
+
+#ifndef MP_ARENA
+#  define MP_ARENA MPARENA_GLOBAL
+#endif
+
+/* --- @MP_ALLOC@ --- *
  *
- * Returns:    ---
+ * Arguments:  @size_t sz@ = size required
+ *
+ * Returns:    Pointer to an allocated vector of the requested size.
  *
- * Use:                Initializes an MP ready for use.  The initial value is zero.
+ * Use:                Hook for vector allocation.
  */
 
-#define MP_INIT { 0, 0, 0, 0 }
-
-extern void mp_create(mp */*x*/);
+#ifndef MP_ALLOC
+#  define MP_ALLOC(sz) mpalloc(MP_ARENA, (sz))
+#endif
 
-/* --- @MP_BURN@ --- *
+/* --- @MP_FREE@ --- *
+ *
+ * Arguments:  @mpw *v@ = pointer to vector
  *
- * Arguments:  @x@ = pointer to MP head
+ * Returns:    ---
  *
- * Use:                Burns the contents of the MP, if it contains sensitive data.
+ * Use:                Hook for vector deallocation.
  */
 
-#define MP_BURN(x) do {                                                        \
-  mp *_y = (x)                                                         \
-  if (_y->v && _y->f & mpf_burn) {                                     \
-    memset(_y->v, 0, _y->sz * sizeof(mpw));                            \
-    _y->f &= ~MPF_BURN;                                                        \
-  }                                                                    \
-} while (0)
+#ifndef MP_FREE
+#  define MP_FREE(v) mpfree(MP_ARENA, (v))
+#endif
 
-/* --- @mp_free@ --- *
+/*----- Paranoia management -----------------------------------------------*/
+
+/* --- @mp_burn@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
  *
  * Returns:    ---
  *
- * Use:                Releases the memory used by an MP.
+ * Use:                Marks the integer as `burn-after-use'.  When the integer's
+ *             memory is deallocated, it is deleted so that traces can't
+ *             remain in the swap file.  In theory.
  */
 
-#define MP_DESTROY(x) do {                                             \
-  mp *_x = (x);                                                                \
-  MP_BURN(_x);                                                         \
-  if (_x->v)                                                           \
-    free(_x->v);                                                       \
-  _x->v = 0;                                                           \
-  _x->f = 0;                                                           \
-  _x->sz = 0;                                                          \
-  _x->len = 0;                                                         \
-} while (0)  
-                  
-extern void mp_free(mp */*x*/);
+extern void mp_burn(mp */*m*/);
 
-/* --- @MP_ENSURE@ --- *
+/*----- Trivial macros ----------------------------------------------------*/
+
+/* --- @MP_LEN@ --- *
  *
- * Arguments:  @x@ = pointer to MP head
- *             @len@ = length required (in words)
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
  *
- * Use:                Ensures that the MP has enough memory to store a @len@-word
- *             value.
+ * Returns:    Length of the integer, in words.
  */
 
-#define MP_ENSURE(x, len) do {                                         \
-  mp *_x = (x);                                                                \
-  size_t _len = (len);                                                 \
-  if (_x->sz < _len)                                                   \
-    mp_resize(_x, _len);                                               \
-} while (0)
+#define MP_LEN(m) ((m)->vl - ((m)->v))
 
-/* --- @mp_resize@ --- *
+/*----- Memory management and reference counting --------------------------*/
+
+/* --- @mp_create@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
- *             @size_t sz@ = size required (in words)
+ * Arguments:  @size_t sz@ = size of vector required
  *
- * Returns:    ---
+ * Returns:    Pointer to pristine new MP structure with enough memory
+ *             bolted onto it.
  *
- * Use:                Resizes the MP so that its word vector has space for
- *             exactly @sz@ words.
+ * Use:                Creates a new multiprecision integer with indeterminate
+ *             contents.  The integer has a single reference.
  */
 
-extern void mp_resize(mp */*x*/, size_t /*sz*/);
+extern mp *mp_create(size_t /*sz*/);
 
-/* --- @mp_norm@ --- *
+/* --- @mp_build@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
+ * Arguments:  @mp *m@ = pointer to an MP block to fill in
+ *             @mpw *v@ = pointer to a word array
+ *             @mpw *vl@ = pointer just past end of array
  *
  * Returns:    ---
  *
- * Use:                `Normalizes' an MP.  Fixes the @len@ field so that it's
- *             correct.  Assumes that @len@ is either already correct or
- *             too large.
+ * Use:                Creates a multiprecision integer representing some smallish
+ *             number.  You must provide storage for the number and dispose
+ *             of it when you've finished with it.  The number is marked as
+ *             constant while it exists.
  */
 
-#define MP_NORM(x) do {                                                        \
-  mp *_y = (x);                                                                \
-  MPX_LEN(_y->len, _y->v, _y->len);                                    \
-} while (0)
+extern void mp_build(mp */*m*/, mpw */*v*/, mpw */*vl*/);
 
-extern void mp_norm(mp */*x*/);
-
-/* --- @mp_dump@ --- *
+/* --- @mp_destroy@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
- *             @FILE *fp@ = pointer to stream to write on
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
  *
  * Returns:    ---
  *
- * Use:                Dumps an MP to a stream.
+ * Use:                Destroys a multiprecision integer. The reference count isn't
+ *             checked.  Don't use this function if you don't know what
+ *             you're doing: use @mp_drop@ instead.
  */
 
-extern void mp_dump(mp */*x*/, FILE */*fp*/);
+extern void mp_destroy(mp */*m*/);
 
-/* --- @MP_PARANOID@ --- *
+/* --- @mp_copy@ --- *
  *
- * Arguments:  @x@ = pointer to MP head
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
  *
- * Use:                Marks the MP as containing sensitive data which should be
- *             burnt when no longer required.
+ * Returns:    A copy of the given multiprecision integer.
+ *
+ * Use:                Copies the given integer.  In fact you just get another
+ *             reference to the same old one again.
  */
 
-#define MP_PARANOID(x) ((x)->f |= MPF_BURN)
+extern mp *mp_copy(mp */*m*/);
 
-/* --- @mp_copy@ --- *
+#define MP_COPY(m) ((m)->ref++, (m))
+
+/* --- @mp_drop@ --- *
  *
- * Arguments:  @mp *d@ = pointer to MP head for destination
- *             @const mp *s@ = pointer to MP head for source
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
  *
  * Returns:    ---
  *
- * Use:                Copies an MP.
+ * Use:                Drops a reference to an integer which isn't wanted any more.
+ *             If there are no more references, the integer is destroyed.
  */
 
-extern void mp_copy(mp */*d*/, const mp */*s*/);
+extern void mp_drop(mp */*m*/);
 
-/* --- @mp_bits@ --- *
+#define MP_DROP(m) do {                                                        \
+  mp *_mm = (m);                                                       \
+  if (_mm->ref > 1)                                                    \
+    _mm->ref--;                                                                \
+  else if (!(_mm->f & MP_CONST))                                       \
+    mp_destroy(_mm);                                                   \
+} while (0)
+                  
+/* --- @mp_split@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
  *
- * Returns:    Length of the number in bits.
+ * Returns:    A reference to the same integer, possibly with a different
+ *             address.
  *
- * Use:                Calculates the number of bits required to represent a number.
- *             The number must be normalized.
+ * Use:                Splits off a modifiable version of the integer referred to.
  */
 
-unsigned long mp_bits(mp */*x*/);
+extern mp *mp_split(mp */*m*/);
+
+#define MP_SPLIT(m) do {                                               \
+  mp *_mm = (m);                                                       \
+  if ((_mm->f & MP_CONST) || _mm->ref != 1) {                          \
+    mp *_dd = mp_create(_mm->sz);                                      \
+    _dd->vl = _dd->v + MP_LEN(_mm);                                    \
+    _dd->f = _mm->f & (MP_NEG | MP_BURN);                              \
+    memcpy(_dd->v, _mm->v, MPWS(MP_LEN(_mm)));                         \
+    _dd->ref = 1;                                                      \
+    _mm->ref--;                                                                \
+    (m) = _dd;                                                         \
+  }                                                                    \
+} while (0)
 
-/* --- @mp_octets@ --- *
+/* --- @mp_resize@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
+ *             @size_t sz@ = new size
  *
- * Returns:    Length of number in octets.
+ * Returns:    ---
  *
- * Use:                Calculates the number of octets required to represent a
- *             number.  The number must be normalized.
+ * Use:                Resizes the vector containing the integer's digits.  The new
+ *             size must be at least as large as the current integer's
+ *             length.  The integer's length is increased and new digits are
+ *             filled with zeroes.  This isn't really intended for client
+ *             use.
  */
 
-extern size_t mp_octets(mp */*x*/);
-
-/*----- Loading and storing as binary data --------------------------------*/
+extern void mp_resize(mp */*m*/, size_t /*sz*/);
+
+#define MP_RESIZE(m, ssz) do {                                         \
+  mp *_m = (m);                                                                \
+  size_t _sz = (ssz);                                                  \
+  size_t _len = MP_LEN(_m);                                            \
+  mpw *_v = MP_ALLOC(_sz);                                             \
+  memcpy(_v, _m->v, MPWS(_len));                                       \
+  if (_m->f & MP_BURN)                                                 \
+    memset(_m->v, 0, MPWS(_m->sz));                                    \
+  MP_FREE(_m->v);                                                      \
+  _m->v = _v;                                                          \
+  _m->vl = _v + _len;                                                  \
+  _m->sz = _sz;                                                                \
+} while (0)
 
-/* --- @mp_storel@ --- *
+/* --- @mp_ensure@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
- *             @octet *p@ = pointer to octet array
- *             @size_t sz@ = size of octet array
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
+ *             @size_t sz@ = required size
  *
  * Returns:    ---
  *
- * Use:                Stores an MP in an octet array, least significant octet
- *             first.  High-end octets are silently discarded if there
- *             isn't enough space for them.
+ * Use:                Ensures that the integer has enough space for @sz@ digits.
+ *             The value is not changed.
  */
 
-extern void mp_storel(mp */*x*/, octet */*p*/, size_t /*sz*/);
+extern void mp_ensure(mp */*m*/, size_t /*sz*/);
+
+#define MP_ENSURE(m, ssz) do {                                         \
+  mp *_mm = (m);                                                       \
+  size_t _ssz = (ssz);                                                 \
+  size_t _len = MP_LEN(_mm);                                           \
+  if (_ssz > _mm->sz)                                                  \
+    MP_RESIZE(_mm, _ssz);                                              \
+  if (!(_mm->f & MP_UNDEF) && _ssz > _len) {                           \
+    memset(_mm->vl, 0, MPWS(_ssz - _len));                             \
+    _mm->vl = _mm->v + _ssz;                                           \
+  }                                                                    \
+} while (0)
 
-/* --- @mp_loadl@ --- *
+/* --- @mp_modify@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
- *             @const octet *p@ = pointer to octet array
- *             @size_t sz@ = size of octet array
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
+ *             @size_t sz@ = size required
  *
- * Returns:    ---
+ * Returns:    Pointer to the integer (possibly different).
  *
- * Use:                Loads an MP in an octet array, least significant octet
- *             first.
+ * Use:                Prepares an integer to be overwritten.  It's split off from
+ *             other references to the same integer, and sufficient space is
+ *             allocated.
  */
 
-extern void mp_loadl(mp */*x*/, const octet */*p*/, size_t /*sz*/);
+extern mp *mp_modify(mp */*m*/, size_t /*sz*/);
 
-/* --- @mp_storeb@ --- *
+#define MP_MODIFY(m, sz) do {                                          \
+  size_t _rq = (sz);                                                   \
+  mp *_m = (m);                                                                \
+  if (_m == MP_NEW)                                                    \
+    _m = mp_create(_rq);                                               \
+  else {                                                               \
+    MP_SPLIT(_m);                                                      \
+    MP_ENSURE(_m, _rq);                                                        \
+  }                                                                    \
+  _m->vl = _m->v + _rq;                                                        \
+  (m) = _m;                                                            \
+} while (0)
+
+/*----- Size manipulation -------------------------------------------------*/
+
+/* --- @mp_shrink@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
- *             @octet *p@ = pointer to octet array
- *             @size_t sz@ = size of octet array
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
  *
  * Returns:    ---
  *
- * Use:                Stores an MP in an octet array, most significant octet
- *             first.  High-end octets are silently discarded if there
- *             isn't enough space for them.
+ * Use:                Reduces the recorded length of an integer.  This doesn't
+ *             reduce the amount of memory used, although it can improve
+ *             performance a bit.  To reduce memory, use @mp_minimize@
+ *             instead.  This can't change the value of an integer, and is
+ *             therefore safe to use even when there are multiple
+ *             references.
  */
 
-extern void mp_storeb(mp */*x*/, octet */*p*/, size_t /*sz*/);
+extern void mp_shrink(mp */*m*/);
 
-/* --- @mp_loadb@ --- *
+#define MP_SHRINK(m) do {                                              \
+  mp *_mm = (m);                                                       \
+  MPX_SHRINK(_mm->v, _mm->vl);                                         \
+  if (!MP_LEN(_mm))                                                    \
+    _mm->f &= ~MP_NEG;                                                 \
+} while (0)
+
+/* --- @mp_minimize@ --- *
  *
- * Arguments:  @mp *x@ = pointer to MP head
- *             @const octet *p@ = pointer to octet array
- *             @size_t sz@ = size of octet array
+ * Arguments:  @mp *m@ = pointer to a multiprecision integer
  *
  * Returns:    ---
  *
- * Use:                Loads an MP in an octet array, most significant octet
- *             first.
+ * Use:                Reduces the amount of memory an integer uses.  It's best to
+ *             do this to numbers which aren't going to change in the
+ *             future.
  */
 
-extern void mp_loadb(mp */*x*/, const octet */*p*/, size_t /*sz*/);
+extern void mp_minimize(mp */*m*/);
 
-/*----- Iterating through bits --------------------------------------------*/
+/*----- Bit scanning ------------------------------------------------------*/
 
-/* --- @mp_mkbitscan@ --- *
+#ifndef MPSCAN_H
+#  include "mpscan.h"
+#endif
+
+/* --- @mp_scan@ --- *
  *
- * Arguments:  @mp_bitscan *sc@ = pointer to bitscan object
- *             @const mp *x@ = pointer to MP head
+ * Arguments:  @mpscan *sc@ = pointer to bitscanner block
+ *             @const mp *m@ = pointer to a multiprecision integer
  *
  * Returns:    ---
  *
- * Use:                Initializes a bitscan object.
+ * Use:                Initializes a bitscanner on a multiprecision integer.
  */
 
-extern void mp_mkbitscan(mp_bitscan */*sc*/, const mp */*x*/);
+extern void mp_scan(mpscan */*sc*/, const mp */*m*/);
+
+#define MP_SCAN(sc, m) do {                                            \
+  mp *_mm = (m);                                                       \
+  mpscan *_sc = (sc);                                                  \
+  MPSCAN_INITX(_sc, _mm->v, _mm->vl);                                  \
+} while (0)
+
+/* --- Other bitscanning aliases --- */
+
+#define mp_step mpscan_step
+#define mp_bit mpscan_bit
+
+#define MP_STEP MPSCAN_STEP
+#define MP_BIT MPSCAN_BIT
+
+/*----- Loading and storing -----------------------------------------------*/
 
-/* --- @mp_bstep@ --- *
+/* --- @mp_octets@ --- *
  *
- * Arguments:  @mp_bitscan *sc@ = pointer to bitscanner object
+ * Arguments:  @const mp *m@ = a multiprecision integer
  *
- * Returns:    Nonzero if there is another bit to read.
+ * Returns:    The number of octets required to represent @m@.
  *
- * Use:                Steps on to the next bit, and tells the caller whether one
- *             exists.
+ * Use:                Calculates the external storage required for a multiprecision
+ *             integer.
  */
 
-extern int mp_bstep(mp_bitscan */*sc*/);
+extern size_t mp_octets(const mp *m);
 
-/* --- @mp_bit@ --- *
+/* --- @mp_loadl@ --- *
  *
- * Arguments:  @const mp_bitscan *sc@ = pointer to bitscanner
+ * Arguments:  @mp *d@ = destination
+ *             @const void *pv@ = pointer to source data
+ *             @size_t sz@ = size of the source data
  *
- * Returns:    Current bit value.
+ * Returns:    Resulting multiprecision number.
  *
- * Use:                Returns the value of the current bit.
+ * Use:                Loads a multiprecision number from an array of octets.  The
+ *             first byte in the array is the least significant.  More
+ *             formally, if the bytes are %$b_0, b_1, \ldots, b_{n-1}$%
+ *             then the result is %$N = \sum_{0 \le i < n} b_i 2^{8i}$%.
  */
 
-#define MP_BIT(sc) ((sc)->w & 1)
+extern mp *mp_loadl(mp */*d*/, const void */*pv*/, size_t /*sz*/);
 
-extern int mp_bit(const mp_bitscan */*sc*/);
+/* --- @mp_storel@ --- *
+ *
+ * Arguments:  @const mp *m@ = source
+ *             @void *pv@ = pointer to output array
+ *             @size_t sz@ = size of the output array
+ *
+ * Returns:    ---
+ *
+ * Use:                Stores a multiprecision number in an array of octets.  The
+ *             first byte in the array is the least significant.  If the
+ *             array is too small to represent the number, high-order bits
+ *             are truncated; if the array is too large, high order bytes
+ *             are filled with zeros.  More formally, if the number is
+ *             %$N = \sum{0 \le i} b_i 2^{8i}$% where %$0 \le b_i < 256$%,
+ *             then the array is %$b_0, b_1, \ldots, b_{n-1}$%.
+ */
 
-/*----- Shifting ----------------------------------------------------------*/
+extern void mp_storel(const mp */*m*/, void */*pv*/, size_t /*sz*/);
 
-/* --- @mp_lsl@ --- *
+/* --- @mp_loadb@ --- *
  *
- * Arguments:  @mp *d@ = pointer to MP head of destination
- *             @const mp *x@ = pointer to MP head of source
- *             @size_t n@ = number of bits to shift
+ * Arguments:  @mp *d@ = destination
+ *             @const void *pv@ = pointer to source data
+ *             @size_t sz@ = size of the source data
  *
- * Returns:    ---
+ * Returns:    Resulting multiprecision number.
  *
- * Use:                Shifts a number left by a given number of bit positions.
+ * Use:                Loads a multiprecision number from an array of octets.  The
+ *             last byte in the array is the least significant.  More
+ *             formally, if the bytes are %$b_{n-1}, b_{n-2}, \ldots, b_0$%
+ *             then the result is %$N = \sum_{0 \le i < n} b_i 2^{8i}$%.
  */
 
-extern void mp_lsl(mp */*d*/, const mp */*x*/, size_t /*n*/);
+extern mp *mp_loadb(mp */*d*/, const void */*pv*/, size_t /*sz*/);
 
-/* --- @mp_lsr@ --- *
+/* --- @mp_storeb@ --- *
  *
- * Arguments:  @mp *d@ = pointer to MP head of destination
- *             @const mp *x@ = pointer to MP head of source
- *             @size_t n@ = number of bits to shift
+ * Arguments:  @const mp *m@ = source
+ *             @void *pv@ = pointer to output array
+ *             @size_t sz@ = size of the output array
  *
  * Returns:    ---
  *
- * Use:                Shifts a number right by a given number of bit positions.
+ * Use:                Stores a multiprecision number in an array of octets.  The
+ *             last byte in the array is the least significant.  If the
+ *             array is too small to represent the number, high-order bits
+ *             are truncated; if the array is too large, high order bytes
+ *             are filled with zeros.  More formally, if the number is
+ *             %$N = \sum{0 \le i} b_i 2^{8i}$% where %$0 \le b_i < 256$%,
+ *             then the array is %$b_{n-1}, b_{n-2}, \ldots, b_0$%.
  */
 
-extern void mp_lsr(mp */*d*/, const mp */*x*/, size_t /*n*/);
+extern void mp_storeb(const mp */*m*/, void */*pv*/, size_t /*sz*/);
 
-/*----- Unsigned arithmetic -----------------------------------------------*/
+/*----- Simple arithmetic -------------------------------------------------*/
 
-/* --- @mp_uadd@ --- *
+/* --- @mp_2c@ --- *
  *
- * Arguments:  @const mp *d@ = pointers to MP head of destination
- *             @const mp *x, *y@ = pointers to MP heads of operands
+ * Arguments:  @mp *d@ = destination
+ *             @mp *a@ = source
  *
- * Returns:    ---
+ * Returns:    Result, @a@ converted to two's complement notation.
+ */
+
+extern mp *mp_2c(mp */*d*/, mp */*a*/);
+
+/* --- @mp_sm@ --- *
+ *
+ * Arguments:  @mp *d@ = destination
+ *             @mp *a@ = source
  *
- * Use:                Performs unsigned MP addition.
+ * Returns:    Result, @a@ converted to the native signed-magnitude
+ *             notation.
  */
 
-extern void mp_uadd(mp */*d*/, const mp */*x*/, const mp */*y*/);
+extern mp *mp_sm(mp */*d*/, mp */*a*/);
 
-/* --- @mp_usub@ --- *
+/* --- @mp_lsl@ --- *
  *
- * Arguments:  @const mp *d@ = pointers to MP head of destination
- *             @const mp *x, *y@ = pointers to MP heads of operands
+ * Arguments:  @mp *d@ = destination
+ *             @const mp *a@ = source
+ *             @size_t n@ = number of bits to move
  *
- * Returns:    ---
+ * Returns:    Result, @a@ shifted left by @n@.
+ */
+
+extern mp *mp_lsl(mp */*d*/, const mp */*a*/, size_t /*n*/);
+
+/* --- @mp_lsr@ --- *
+ *
+ * Arguments:  @mp *d@ = destination
+ *             @const mp *a@ = source
+ *             @size_t n@ = number of bits to move
  *
- * Use:                Performs unsigned MP subtraction.
+ * Returns:    Result, @a@ shifted left by @n@.
  */
 
-extern void mp_usub(mp */*d*/, const mp */*x*/, const mp */*y*/);
+extern mp *mp_lsr(mp */*d*/, const mp */*a*/, size_t /*n*/);
 
-/* --- @mp_ucmp@ --- *
+/* --- @mp_cmp@ --- *
  *
- * Arguments:  @const mp *x, *y@ = pointers to MP heads of operands
+ * Arguments:  @const mp *a, *b@ = two numbers
  *
- * Returns:    Less than, equal to, or greater than zero.
+ * Returns:    Less than, equal to or greater than zero, according to
+ *             whether @a@ is less than, equal to or greater than @b@.
+ */
+
+extern int mp_cmp(const mp */*a*/, const mp */*b*/);
+
+#define MP_CMP(a, op, b) (mp_cmp((a), (b)) op 0)
+
+/* --- @mp_add@ --- *
  *
- * Use:                Performs unsigned MP comparison.
+ * Arguments:  @mp *d@ = destination
+ *             @const mp *a, *b@ = sources
+ *
+ * Returns:    Result, @a@ added to @b@.
  */
 
-#define MP_UCMP(x, op, y) (mp_ucmp((x), (y)) op 0)
+extern mp *mp_add(mp */*d*/, const mp */*a*/, const mp */*b*/);
 
-extern int mp_ucmp(const mp */*x*/, const mp */*y*/);
+/* --- @mp_sub@ --- *
+ *
+ * Arguments:  @mp *d@ = destination
+ *             @const mp *a, *b@ = sources
+ *
+ * Returns:    Result, @b@ subtracted from @a@.
+ */
 
-/* --- @mp_umul@ --- *
+extern mp *mp_sub(mp */*d*/, const mp */*a*/, const mp */*b*/);
+
+/* --- @mp_mul@ --- *
  *
- * Arguments:  @mp *d@ = pointer to MP head of destination
- *             @const mp *x, *y@ = pointes to MP heads of operands
+ * Arguments:  @mp *d@ = destination
+ *             @const mp *a, *b@ = sources
  *
- * Returns:    ---
+ * Returns:    Result, @a@ multiplied by @b@.
+ */
+
+extern mp *mp_mul(mp */*d*/, const mp */*a*/, const mp */*b*/);
+
+/* --- @mp_sqr@ --- *
+ *
+ * Arguments:  @mp *d@ = destination
+ *             @const mp *a@ = source
+ *
+ * Returns:    Result, @a@ squared.
+ */
+
+extern mp *mp_sqr(mp */*d*/, const mp */*a*/);
+
+/* --- @mp_div@ --- *
  *
- * Use:                Performs unsigned MP multiplication.
+ * Arguments:  @mp **qq, **rr@ = destination, quotient and remainder
+ *             @const mp *a, *b@ = sources
+ *
+ * Use:                Calculates the quotient and remainder when @a@ is divided by
+ *             @b@.
  */
 
-extern void mp_umul(mp */*d*/, const mp */*x*/, const mp */*y*/);
+extern void mp_div(mp **/*qq*/, mp **/*rr*/,
+                  const mp */*a*/, const mp */*b*/);
+
+/*----- More advanced algorithms ------------------------------------------*/
 
-/* --- @mp_udiv@ --- *
+/* --- @mp_gcd@ --- *
  *
- * Arguments:  @mp *q, *r@ = pointers to MP heads for quotient, remainder
- *             @const mp *x, *y@ = pointers to MP heads for operands
+ * Arguments:  @mp **gcd, **xx, **yy@ = where to write the results
+ *             @mp *a, *b@ = sources (must be nonzero)
  *
  * Returns:    ---
  *
- * Use:                Performs unsigned MP division.
+ * Use:                Calculates @gcd(a, b)@, and two numbers @x@ and @y@ such that
+ *             @ax + by = gcd(a, b)@.  This is useful for computing modular
+ *             inverses.  Neither @a@ nor @b@ may be zero.  Note that,
+ *             unlike @mp_div@ for example, it is not possible to specify
+ *             explicit destinations -- new MPs are always allocated.
  */
 
-extern void mp_udiv(mp */*q*/, mp */*r*/, const mp */*x*/, const mp */*y*/);
+extern void mp_gcd(mp **/*gcd*/, mp **/*xx*/, mp **/*yy*/,
+                  mp */*a*/, mp */*b*/);
+
+/*----- Test harness support ----------------------------------------------*/
+
+#include <mLib/testrig.h>
+
+#ifndef MPTEXT_H
+#  include "mptext.h"
+#endif
+
+extern const test_type type_mp;
 
 /*----- That's all, folks -------------------------------------------------*/