Merge branch '2.5.x' into HEAD
authorMark Wooding <mdw@distorted.org.uk>
Sat, 23 Dec 2023 14:33:00 +0000 (14:33 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 23 Dec 2023 14:33:00 +0000 (14:33 +0000)
* 2.5.x:
  rand/rand.c (rand_gate): Evolve r->ibits in a more sensible manner.
  rand/rand.c (rand_getgood): Stretch the output buffer if necessary.

154 files changed:
base/Makefile.am
base/asm-common.h
base/dispatch-x86ish.S [new file with mode: 0644]
base/dispatch.c
base/dispatch.h
base/keysz-conv.c
base/keysz.h
base/regdump-arm.S
base/regdump-arm64.S
base/regdump-x86ish.S
base/regdump.c
base/regdump.h
base/rsvr.c
base/test-regdump-x86ish.S
configure.ac
debian/.gitignore
debian/catacomb2.symbols
debian/changelog
debian/control
key/key-attr.c
key/key-binary.c
key/key-flags.c
key/key-io.c
key/key-misc.c
key/key-pass.c
key/key-text.c
key/key.h
key/passphrase.c
key/pixie-common.c
m4/mdw-uint-bits.m4
math/Makefile.am
math/bittest.c
math/ec-test.c
math/f25519.c
math/fgoldi.c
math/g-ec.c
math/genprimes.c
math/genwheel.c
math/gfreduce.c
math/group-dstr.c
math/group-test.c
math/mp-arith.c
math/mp-nthrt.c [new file with mode: 0644]
math/mp.h
math/mpgen
math/mpint.c
math/mpmont-exp.c
math/mpmont.c
math/mptext.c
math/mpx-mul4-amd64-sse2.S
math/mpx-mul4-arm-neon.S [new file with mode: 0644]
math/mpx-mul4-arm64-simd.S [new file with mode: 0644]
math/mpx-mul4-test.c
math/mpx-mul4-x86-sse2.S
math/mpx.c
math/qdparse.c
math/t/mp
math/t/mpx-mul4
misc/gfshare.c
progs/catcrypt.c
progs/catsign.c
progs/cc-enc.c
progs/cc-hash.c
progs/cc-kem.c
progs/cc-list.c
progs/cc-sig.c
progs/cc-subcmd.c
progs/cookie.c
progs/dsig.c
progs/factorial.c
progs/fibonacci.c
progs/hashsum.c
progs/key.1
progs/key.c
progs/mkphrase.c
progs/perftest.c
progs/pixie.c
progs/rspit.c
pub/bbs-rand.c
pub/dh-kcdsa.c
pub/dh-param.c
pub/dsa-gen.c
pub/dsa-sign.c
pub/ed25519.c
pub/ed448.c
pub/gkcdsa.c
pub/pkcs1.c
pub/pss.c
pub/rsa-pub.c
pub/rsa-test.c
pub/x25519.c
pub/x25519.h
pub/x448.c
pub/x448.h
rand/Makefile.am
rand/dsarand.c
rand/lcrand.c
rand/rand-x86ish.S [new file with mode: 0644]
rand/rand.c
rand/sslprf.c
rand/tlsprf.c
symm/Makefile.am
symm/blkc.h
symm/ccm-def.h
symm/ccm.c
symm/chacha-arm-neon.S
symm/chacha-arm64.S
symm/chacha-x86ish-sse2.S
symm/chacha.c
symm/cmac-def.h
symm/eax-def.h
symm/gaead.c
symm/gcm-def.h
symm/gcm-x86ish-pclmul.S
symm/gcm.c
symm/gthingtab.c.in
symm/hash.h
symm/hmac-def.h
symm/keccak1600.c
symm/keccak1600.h
symm/latinpoly-def.h
symm/latinpoly-test.c
symm/modes-test.c
symm/multigen
symm/ocb1-def.h
symm/ocb3-def.h
symm/pmac1-def.h
symm/poly1305.c
symm/rc2.c
symm/rc4.c
symm/rijndael-arm64-crypto.S
symm/rijndael-x86ish-aesni.S
symm/rijndael.c
symm/rijndael192.c
symm/rijndael256.c
symm/salsa20-arm-neon.S
symm/salsa20-arm64.S
symm/salsa20-x86ish-sse2.S
symm/salsa20.c
symm/seal.c
symm/serpent-check.c
symm/sha3.c
symm/square-mktab.c
symm/square.c
symm/strobe.c [new file with mode: 0644]
symm/strobe.h [new file with mode: 0644]
symm/t/poly1305
symm/t/poly1305.slow [new file with mode: 0644]
symm/t/square
symm/t/strobe [new file with mode: 0644]
utils/advmodes
utils/ecptdecompress.c
utils/gcm-ref
utils/strobe [new file with mode: 0644]

index 145f9c3..8b7c0fc 100644 (file)
@@ -45,6 +45,12 @@ libbase_la_SOURCES   += ct.c ct-test.c
 ## CPU-specific dispatch.
 pkginclude_HEADERS     += dispatch.h
 libbase_la_SOURCES     += dispatch.c
+if CPUFAM_X86
+libbase_la_SOURCES     += dispatch-x86ish.S
+endif
+if CPUFAM_AMD64
+libbase_la_SOURCES     += dispatch-x86ish.S
+endif
 
 ## Acceptable key-size descriptions.
 pkginclude_HEADERS     += keysz.h
index 44c223d..b4d4a90 100644 (file)
 #define _ENDLIT .text .L$_subsec
 #define _LTORG .L$_subsec = .L$_subsec + 2; .text .L$_subsec
 
-// ELF section types.
-#if __ELF__
-#  if CPUFAM_ARMEL
-#    define _SECTTY(ty) %ty
-#  else
-#    define _SECTTY(ty) @ty
-#  endif
-#endif
-
-// Section selection.
-#define TEXT .text .L$_subsec
-#if ABI_WIN
-#  define RODATA .section .rdata, "dr"
-#elif __ELF__
-#  define RODATA .section .rodata, "a", _SECTTY(progbits)
-#else
-#  define RODATA TEXT
-#endif
-#define DATA .data
-
 // Announcing an internal function.
 #define INTFUNC(name)                                                  \
        TYPE_FUNC(name);                                                \
@@ -106,14 +86,32 @@ name:
 
 #if __ELF__
 
-#if __PIC__ || __PIE__
-#  define WANT_PIC 1
+// Section types.
+#if CPUFAM_ARMEL
+#  define _SECTTY(ty) %ty
+#else
+#  define _SECTTY(ty) @ty
 #endif
 
-#define TYPE_FUNC(name) .type name, STT_FUNC
+// Section selection.
+#define RODATA .section .rodata, "a", _SECTTY(progbits)
 
+// Additional symbol metadata.
+#define TYPE_FUNC(name) .type name, STT_FUNC
+#define TYPE_OBJ(name) .type name, STT_OBJECT
 #define SIZE_OBJ(name) .size name, . - name
 
+// Special arrangements for position-independent code.
+#if __PIC__ || __PIE__
+#  define WANT_PIC 1
+#endif
+
+// Don't make the stack executable by default.
+#ifndef FORCE_EXECUTABLE_STACK
+       .pushsection .note.GNU-stack, "", _SECTTY(progbits)
+       .popsection
+#endif
+
 #endif
 
 ///--------------------------------------------------------------------------
@@ -121,10 +119,14 @@ name:
 
 #if ABI_WIN
 
+// Function names need decorating on 32-bit i386.
 #if CPUFAM_X86
 #  define F(name) _##name
 #endif
 
+// Section selection.
+#define RODATA .section .rdata, "dr"
+
 #endif
 
 ///--------------------------------------------------------------------------
@@ -311,6 +313,7 @@ name:
 // R_r(decor) applies decoration decor to register r, which is an internal
 // register name.  The internal register names are: `ip', `a', `b', `c', `d',
 // `si', `di', `bp', `sp', `r8'--`r15'.
+#define R_nil(decor) nil
 #define R_ip(decor) _DECOR(ip, decor, ip)
 #define R_a(decor) _DECOR(abcd, decor, a)
 #define R_b(decor) _DECOR(abcd, decor, b)
@@ -345,6 +348,8 @@ name:
 // assembler-level register name, in place of any decoration that register
 // name has already.
 
+#define _REGFORM_nil(decor) R_nil(decor)
+
 #define _REGFORM_ip(decor) R_ip(decor)
 #define _REGFORM_eip(decor) R_ip(decor)
 
@@ -452,10 +457,20 @@ name:
 #endif
 #define WHOLE(reg) _REGFORM(reg, r)
 
+// Macros for some common registers.
+#define AX R_a(r)
+#define BX R_b(r)
+#define CX R_c(r)
+#define DX R_d(r)
+#define SI R_si(r)
+#define DI R_di(r)
+#define BP R_bp(r)
+#define SP R_sp(r)
+
 // Stack management and unwinding.
-.macro setfp   fp=R_bp(r), offset=0
+.macro setfp   fp=BP, offset=0
   .if \offset == 0
-       mov     \fp, R_sp(r)
+       mov     \fp, SP
 #if __ELF__
          .cfi_def_cfa_register \fp
 #endif
@@ -463,7 +478,7 @@ name:
          .seh_setframe \fp, 0
 #endif
   .else
-       lea     \fp, [R_sp(r) + \offset]
+       lea     \fp, [SP + \offset]
 #if __ELF__
          .cfi_def_cfa_register \fp
          .cfi_adjust_cfa_offset -\offset
@@ -478,14 +493,14 @@ name:
 
 .macro _dropfp fp, offset=0
   .if \offset == 0
-       mov     R_sp(r), \fp
+       mov     SP, \fp
 #if __ELF__
-         .cfi_def_cfa_register R_sp(r)
+         .cfi_def_cfa_register SP
 #endif
   .else
-       lea     R_sp(r), [\fp - \offset]
+       lea     SP, [\fp - \offset]
 #if __ELF__
-         .cfi_def_cfa_register R_sp(r)
+         .cfi_def_cfa_register SP
          .cfi_adjust_cfa_offset +\offset
 #endif
   .endif
@@ -494,7 +509,7 @@ name:
 .endm
 
 .macro stalloc n
-       sub     R_sp(r), \n
+       sub     SP, \n
 #if __ELF__
          .cfi_adjust_cfa_offset +\n
 #endif
@@ -504,7 +519,7 @@ name:
 .endm
 
 .macro stfree  n
-       add     R_sp(r), \n
+       add     SP, \n
 #if __ELF__
          .cfi_adjust_cfa_offset -\n
 #endif
@@ -530,14 +545,14 @@ name:
 .endm
 
 .macro savexmm r, offset
-       movdqa  [R_sp(r) + \offset], \r
+       movdqa  [SP + \offset], \r
 #if ABI_WIN && CPUFAM_AMD64
          .seh_savexmm \r, \offset
 #endif
 .endm
 
 .macro rstrxmm r, offset
-       movdqa  \r, [R_sp(r) + \offset]
+       movdqa  \r, [SP + \offset]
 .endm
 
 .macro endprologue
@@ -657,6 +672,8 @@ name:
 // Internal macros: `_REGFORM_r(decor)' applies decoration decor to register
 // name r.
 
+#define _REGFORM_nil(decor) nil
+
 #define _REGFORM_s0(decor) _DECOR(s, decor, 0)
 #define _REGFORM_s1(decor) _DECOR(s, decor, 1)
 #define _REGFORM_s2(decor) _DECOR(s, decor, 2)
@@ -1099,6 +1116,59 @@ name:
 .macro endprologue
 .endm
 
+// cmov RD, RN, CC: set RD to RN if CC is satisfied, otherwise do nothing
+.macro cmov    rd, rn, cc
+       csel    \rd, \rn, \rd, \cc
+.endm
+
+// Notational improvement: write `csel.CC' etc., rather than `csel ..., CC'.
+#define _COND(_)                                                       \
+       _(eq) _(ne) _(cs) _(cc) _(vs) _(vc) _(mi) _(pl)                 \
+       _(ge) _(lt) _(gt) _(le) _(hi) _(ls) _(al) _(nv)                 \
+       _(hs) _(lo)
+#define _INST(_)                                                       \
+       _(ccmp) _(ccmn)                                                 \
+       _(csel) _(cmov)                                                 \
+       _(csinc) _(cinc) _(cset)                                        \
+       _(csneg) _(cneg)                                                \
+       _(csinv) _(cinv) _(csetm)
+#define _CONDVAR(cc) _definstvar cc;
+#define _INSTVARS(inst)                                                        \
+       .macro _definstvar cc;                                          \
+         .macro inst.\cc args:vararg; inst \args, \cc; .endm;          \
+       .endm;                                                          \
+       _COND(_CONDVAR);                                                \
+       .purgem _definstvar;
+       _INST(_INSTVARS)
+#undef _COND
+#undef _INST
+#undef _CONDVAR
+#undef _INSTVARS
+
+// Flag bits for `ccmp' and friends.
+#define CCMP_N 8
+#define CCMP_Z 4
+#define CCMP_C 2
+#define CCMP_V 1
+
+// Flag settings for satisfying conditions.
+#define CCMP_MI CCMP_N
+#define CCMP_PL 0
+#define CCMP_EQ CCMP_Z
+#define CCMP_NE 0
+#define CCMP_CS CCMP_C
+#define CCMP_HS CCMP_C
+#define CCMP_CC 0
+#define CCMP_LO 0
+#define CCMP_VS CCMP_V
+#define CCMP_VC 0
+#define CCMP_HI CCMP_C
+#define CCMP_LS 0
+#define CCMP_LT CCMP_N
+#define CCMP_GE 0
+#define CCMP_LE CCMP_N
+#define CCMP_GT 0
+
 #endif
 
 ///--------------------------------------------------------------------------
@@ -1115,6 +1185,18 @@ name:
 #  define ENDFUNC_HOOK(_)
 #endif
 
+// Section selection.
+#ifndef TEXT
+#  define TEXT .text .L$_subsec
+#endif
+#ifndef RODATA
+#  define RODATA TEXT
+#endif
+#ifndef DATA
+#  define DATA .data
+#endif
+
+// Symbol decoration.
 #ifndef F
 #  ifdef SYM_USCORE
 #    define F(name) _##name
@@ -1126,16 +1208,13 @@ name:
 #ifndef TYPE_FUNC
 #  define TYPE_FUNC(name)
 #endif
-
+#ifndef TYPE_OBJ
+#  define TYPE_OBJ(name)
+#endif
 #ifndef SIZE_OBJ
 #  define SIZE_OBJ(name)
 #endif
 
-#if __ELF__ && !defined(WANT_EXECUTABLE_STACK)
-       .pushsection .note.GNU-stack, "", _SECTTY(progbits)
-       .popsection
-#endif
-
 ///----- That's all, folks --------------------------------------------------
 
 #endif
diff --git a/base/dispatch-x86ish.S b/base/dispatch-x86ish.S
new file mode 100644 (file)
index 0000000..57d8d32
--- /dev/null
@@ -0,0 +1,216 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// CPU dispatch support for x86
+///
+/// (c) 2019 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.
+
+///--------------------------------------------------------------------------
+/// Preliminaries.
+
+#include "config.h"
+#include "asm-common.h"
+
+       EFLAGS_ID = 1 << 21
+
+       .text
+
+///--------------------------------------------------------------------------
+/// Probing for CPUID.
+
+FUNC(dispatch_x86ish_cpuid)
+       // Enter with a pointer to 16 bytes of storage for the output A, B,
+       // C, D values in the first argument, and input A and C values in the
+       // second and third.  Fill the output buffer with `cpuid' results and
+       // return zero if we can; otherwise fill with zero and return -1.
+
+#if CPUFAM_X86
+       pushreg ebx
+       pushreg edi
+       mov     edi, [SP + 12]
+       mov     eax, [SP + 16]
+       mov     ecx, [SP + 20]
+#  define OUT edi
+#endif
+#if CPUFAM_AMD64 && ABI_SYSV
+       pushreg rbx
+       mov     eax, esi
+       mov     ecx, edx
+#  define OUT rdi
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+       pushreg rbx
+       mov     r9, rcx
+       mov     eax, edx
+       mov     ecx, r8d
+#  define OUT r9
+#endif
+  endprologue
+
+       // First, check that this is even a thing, using the complicated
+       // dance with the flags register.
+       pushf
+       pop     DX                      // current flags in d
+
+       or      DX, EFLAGS_ID           // force the id bit on and check it
+       push    DX
+       popf
+       pushf
+       pop     DX
+       test    edx, EFLAGS_ID
+       jz      8f
+
+       and     DX, ~EFLAGS_ID          // force the id bit off and check it
+       push    DX
+       popf
+       pushf
+       pop     DX
+       test    edx, EFLAGS_ID
+       jnz     8f
+
+       // OK, that seemed to work.
+       cpuid
+
+       mov     [OUT + 0], eax
+       mov     [OUT + 4], ebx
+       mov     [OUT + 8], ecx
+       mov     [OUT + 12], edx
+       xor     eax, eax
+
+       // We're done.
+9:
+#if CPUFAM_X86
+       popreg  edi
+       popreg  ebx
+#endif
+#if CPUFAM_AMD64
+       popreg  rbx
+#endif
+       ret
+
+       // Failed.
+8:     xor     eax, eax
+       mov     [OUT + 0], eax
+       mov     [OUT + 4], eax
+       mov     [OUT + 8], eax
+       mov     [OUT + 12], eax
+       mov     eax, -1
+       jmp     9b
+ENDFUNC
+
+///--------------------------------------------------------------------------
+/// Probing for XMM register availability.
+
+FUNC(dispatch_x86ish_xmmregisters_p)
+       // Enter with no arguments.  Return nonzero if the XMM registers are
+       // usable.
+
+       pushreg BP
+       setfp
+       stalloc 512
+       and     SP, ~15
+  endprologue
+
+       // Save the floating point and SIMD registers, and try to clobber
+       // xmm0.
+       lea     DX, [SP + 160]
+       fxsave  [SP]
+       mov     eax, [DX]
+       xor     dword ptr [DX], 0xaaaa5555
+       fxrstor [SP]
+
+       // Save them again, and read back the low word of xmm0.  Undo the
+       // clobbering and restore.
+       fxsave  [SP]
+       mov     ecx, [DX]
+       mov     [DX], eax
+       fxrstor [SP]
+
+       // The register are live if we read different things.
+       xor     eax, ecx
+
+       // Done.
+       dropfp
+       popreg  BP
+       ret
+ENDFUNC
+
+///--------------------------------------------------------------------------
+/// Checking `rdrand'.
+
+FUNC(dispatch_x86ish_rdrand)
+       // Enter with two arguments: a code OP requesting either `rdrand' (0)
+       // or `rdseed' (1), and a pointer X_OUT to a 32-bit word.  Try to
+       // generate a random word using the requested instruction'.  If
+       // successful, set *X_OUT to the generated word, and return zero;
+       // otherwise, return -1.
+
+#if CPUFAM_X86
+#  define OP eax
+#  define X_OUT edx
+#  define COUNT ecx
+       mov     OP, [SP + 4]
+       mov     X_OUT, [SP + 8]
+#endif
+#if CPUFAM_AMD64 && ABI_SYSV
+#  define OP edi
+#  define X_OUT rsi
+#  define COUNT ecx
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+#  define OP rcx
+#  define X_OUT rdx
+#  define COUNT r8d
+#endif
+  endprologue
+
+       cmp     OP, 0
+       mov     COUNT, 16               // fairly persistent
+       jne     1f
+
+0:     rdrand  eax
+       jc      9f
+       dec     COUNT
+       jnz     0b
+       jmp     8f
+
+1:     rdseed  eax
+       jc      9f
+       dec     COUNT
+       jnz     1b
+       jmp     8f
+
+       // Failed to come up with a random value.
+8:     mov     eax, -1
+       ret
+
+       // Success.
+9:     mov     [X_OUT], eax
+       xor     eax, eax
+       ret
+
+#undef X_OUT
+#undef COUNT
+
+ENDFUNC
+
+///----- That's all, folks --------------------------------------------------
index 131e3fd..4ce6015 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "config.h"
 
+#include <assert.h>
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #if CPUFAM_X86 || CPUFAM_AMD64
 
-#  define EFLAGS_ID (1u << 21)
+enum {
+  CPUID_1_D,                           /* eax = 1 => edx&?? */
 #  define CPUID1D_SSE2 (1u << 26)
 #  define CPUID1D_FXSR (1u << 24)
+
+  CPUID_1_C,                           /* eax = 1 => ecx&?? */
 #  define CPUID1C_PCLMUL (1u << 1)
 #  define CPUID1C_SSSE3 (1u << 9)
 #  define CPUID1C_AESNI (1u << 25)
 #  define CPUID1C_AVX (1u << 28)
 #  define CPUID1C_RDRAND (1u << 30)
 
-struct cpuid { unsigned a, b, c, d; };
-
-/* --- @cpuid@ --- *
- *
- * Arguments:  @struct cpuid *cc@ = where to write the result
- *             @unsigned a, c@ = EAX and ECX registers to set
- *
- * Returns:    ---
- *
- * Use:                Minimal C wrapper around the x86 `CPUID' instruction.  Checks
- *             that the instruction is actually available before invoking
- *             it; fills the output structure with zero if it's not going to
- *             work.
- */
+  CPUID_7_0_B,                         /* eax = 7, ecx = 0 => ebx&?? */
+#  define CPUID70B_RDSEED (1u << 18)
+};
 
-#ifdef __GNUC__
-#  if CPUFAM_X86
-static __inline__ unsigned getflags(void)
-  { unsigned f; __asm__ ("pushf; popl %0" : "=g" (f)); return (f); }
-static __inline__ unsigned setflags(unsigned f)
-{
-  unsigned ff;
-  __asm__ ("pushf; pushl %1; popf; pushf; popl %0; popf"
-          : "=r" (ff)
-          : "r" (f));
-  return (ff);
-}
-#  else
-static __inline__ unsigned long getflags(void)
-  { unsigned long f; __asm__ ("pushf; popq %0" : "=g" (f)); return (f); }
-static __inline__ unsigned long long setflags(unsigned long f)
-{
-  unsigned long ff;
-  __asm__ ("pushf; pushq %1; popf; pushf; popq %0; popf"
-          : "=r" (ff)
-          : "r" (f));
-  return (ff);
-}
-#  endif
-#endif
+struct cpuid { unsigned a, b, c, d; };
+extern int dispatch_x86ish_cpuid(struct cpuid *, unsigned a, unsigned c);
+extern int dispatch_x86ish_xmmregisters_p(void);
+extern int dispatch_x86ish_rdrand(unsigned op, unsigned *);
 
 static void cpuid(struct cpuid *cc, unsigned a, unsigned c)
 {
-#ifdef __GNUC__
-  unsigned f;
-#endif
-
-  cc->a = cc->b = cc->c = cc->d = 0;
-
-#ifdef __GNUC__
-  /* Stupid dance to detect whether the CPUID instruction is available. */
-  f = getflags();
-  if (!(setflags(f |  EFLAGS_ID) & EFLAGS_ID) ||
-       setflags(f & ~EFLAGS_ID) & EFLAGS_ID) {
+  int rc = dispatch_x86ish_cpuid(cc, a, c);
+  if (rc)
     dispatch_debug("CPUID instruction not available");
-    return;
-  }
-  setflags(f);
-
-  /* Alas, EBX is magical in PIC code, so abuse ESI instead.  This isn't
-   * pretty, but it works.
-   */
-#  if CPUFAM_X86
-  __asm__ ("pushl %%ebx; cpuid; movl %%ebx, %%esi; popl %%ebx"
-          : "=a" (cc->a), "=S" (cc->b), "=c" (cc->c), "=d" (cc->d)
-          : "a" (a) , "c" (c));
-#  elif CPUFAM_AMD64
-  __asm__ ("pushq %%rbx; cpuid; movl %%ebx, %%esi; popq %%rbx"
-          : "=a" (cc->a), "=S" (cc->b), "=c" (cc->c), "=d" (cc->d)
-          : "a" (a) , "c" (c));
-#  else
-#    error "I'm confused."
-#  endif
-  dispatch_debug("CPUID(%08x, %08x) -> %08x, %08x, %08x, %08x",
-                a, c, cc->a, cc->b, cc->c, cc->d);
-#else
-  dispatch_debug("GNU inline assembler not available; can't CPUID");
-#endif
+  else
+    dispatch_debug("CPUID(%08x, %08x) -> %08x, %08x, %08x, %08x",
+                  a, c, cc->a, cc->b, cc->c, cc->d);
 }
 
 static unsigned cpuid_maxleaf(void)
   { struct cpuid c; cpuid(&c, 0, 0); return (c.a); }
 
-/* --- @cpuid_features_p@ --- *
+/* --- @cpuid_feature_p@ --- *
  *
- * Arguments:  @unsigned dbits@ = bits to check in EDX
- *             @unsigned cbits@ = bits to check in ECX
+ * Arguments:  @unsigned leaf@ = leaf to look up
+ *             @unsigned bits@ = bits to check
  *
- * Returns:    Nonzero if all the requested bits are set in the CPUID result
- *             on leaf 1.
+ * Returns:    Nonzero if all the requested bits are set in the requested
+ *             CPUID result.
  */
 
-static int cpuid_features_p(unsigned dbits, unsigned cbits)
+static int cpuid_feature_p(unsigned leaf, unsigned bits)
 {
   struct cpuid c;
-  if (cpuid_maxleaf() < 1) return (0);
-  cpuid(&c, 1, 0);
-  return ((c.d & dbits) == dbits && (c.c & cbits) == cbits);
+  unsigned r;
+
+  switch (leaf) {
+    case CPUID_1_D:
+      if (cpuid_maxleaf() < 1) return (0);
+      cpuid(&c, 1, 0); r = c.d;
+      break;
+    case CPUID_1_C:
+      if (cpuid_maxleaf() < 1) return (0);
+      cpuid(&c, 1, 0); r = c.c;
+      break;
+    case CPUID_7_0_B:
+      if (cpuid_maxleaf() < 7) return (0);
+      cpuid(&c, 7, 0); r = c.b;
+      break;
+    default:
+      assert(!"unknown cpuid leaf");
+  }
+  return ((r&bits) == bits);
 }
 
 /* --- @xmm_registers_available_p@ --- *
@@ -162,43 +121,10 @@ static int cpuid_features_p(unsigned dbits, unsigned cbits)
 
 static int xmm_registers_available_p(void)
 {
-#ifdef __GNUC__
-  unsigned f;
-  /* This hack is by Agner Fog.  Use FXSAVE/FXRSTOR to figure out whether the
-   * XMM registers are actually alive.
-   */
-  if (!cpuid_features_p(CPUID1D_FXSR, 0)) return (0);
-#  if CPUFAM_X86
-  __asm__ ("movl %%esp, %%edx; subl $512, %%esp; andl $~15, %%esp\n"
-          "fxsave (%%esp)\n"
-          "movl 160(%%esp), %%eax; xorl $0xaaaa5555, 160(%%esp)\n"
-          "fxrstor (%%esp); fxsave (%%esp)\n"
-          "movl 160(%%esp), %%ecx; movl %%eax, 160(%%esp)\n"
-          "fxrstor (%%esp); movl %%edx, %%esp\n"
-          "xorl %%ecx, %%eax"
-          : "=a" (f)
-          : /* no inputs */
-          : "%ecx", "%edx");
-#  elif CPUFAM_AMD64
-  __asm__ ("movq %%rsp, %%rdx; subq $512, %%rsp; andq $~15, %%rsp\n"
-          "fxsave (%%rsp)\n"
-          "movl 160(%%rsp), %%eax; xorl $0xaaaa5555, 160(%%rsp)\n"
-          "fxrstor (%%rsp); fxsave (%%rsp)\n"
-          "movl 160(%%rsp), %%ecx; movl %%eax, 160(%%rsp)\n"
-          "fxrstor (%%rsp); movq %%rdx, %%rsp\n"
-          "xorl %%ecx, %%eax"
-          : "=a" (f)
-          : /* no inputs */
-          : "%ecx", "%rdx");
-#  else
-#    error "I'm confused."
-#  endif
+  int f = dispatch_x86ish_xmmregisters_p();
+
   dispatch_debug("XMM registers %savailable", f ? "" : "not ");
   return (f);
-#else
-  dispatch_debug("GNU inline assembler not available; can't check for XMM");
-  return (0);
-#endif
 }
 
 /* --- @rdrand_works_p@ --- *
@@ -210,47 +136,37 @@ static int xmm_registers_available_p(void)
  *             that it's already been verified to be safe to issue.
  */
 
-#ifdef __GNUC__
-static int rdrand(unsigned *x)
-{
-  int i, rc;
-  unsigned _t;
-
-  i = 16;
-  __asm__ ("" : "=g" (_t));
-  __asm__ ("0: rdrand %2; jc 1f; decl %1; jnz 0b\n"
-          "mov $-1, %0; jmp 9f\n"
-          "1: movl %2, (%3); xorl %0, %0\n"
-          "9:"
-          : "=r" (rc), "+r" (i), "+r" (_t)
-          : "r" (x)
-          : "cc");
-  return (rc);
-}
-#endif
+enum { OP_RDRAND, OP_RDSEED };
 
-static int rdrand_works_p(void)
+static int rdrand_works_p(unsigned op)
 {
   unsigned ref, x, i;
+  const char *what;
+
+  switch (op) {
+    case OP_RDRAND: what = "RDRAND"; break;
+    case OP_RDSEED: what = "RDSEED"; break;
+    default: assert(!"unexpected op");
+  }
 
   /* Check that it doesn't always give the same answer.  Try four times: this
    * will fail with probability %$2^{-128}$% with a truly random generator,
    * which seems fair enough.
    */
-  if (rdrand(&ref)) goto fail;
+  if (dispatch_x86ish_rdrand(op, &ref)) goto fail;
   for (i = 0; i < 4; i++) {
-    if (rdrand(&x)) goto fail;
+    if (dispatch_x86ish_rdrand(op, &x)) goto fail;
     if (x != ref) goto not_stuck;
   }
-  dispatch_debug("RDRAND always returns 0x%08x!", ref);
+  dispatch_debug("%s always returns 0x%08x!", what, ref);
   return (0);
 
 not_stuck:
-  dispatch_debug("RDRAND instruction looks plausible");
+  dispatch_debug("%s instruction looks plausible", what);
   return (1);
 
 fail:
-  dispatch_debug("RDRAND instruction fails too often");
+  dispatch_debug("%s instruction fails too often", what);
   return (0);
 }
 
@@ -497,7 +413,7 @@ static unsigned get_hwcaps(void)
   unsigned hw;
 
   DISPATCH_LOAD(hwcaps, hw);
-  if (!(hwcaps & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); }
+  if (!(hw & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); }
   return (hw);
 }
 
@@ -552,14 +468,14 @@ static int IGNORABLE check_env(const char *ftok)
   if (!p) return (-1);
 
   for (;;) {
-    while (isspace((unsigned char)*p)) p++;
+    while (ISSPACE(*p)) p++;
     if (!*p) return (-1);
     switch (*p) {
       case '+': d = +1; p++; break;
       case '-': d =  0; p++; break;
       default:  d = -1;      break;
     }
-    for (q = p; *q && !isspace((unsigned char)*q); q++);
+    for (q = p; *q && !ISSPACE(*q); q++);
     if (d >= 0) {
       for (pp = ftok; p < q && *pp && *p == *pp; p++, pp++);
       if ((p == q && !*pp) || (*p == '*' && p + 1 == q)) return (d);
@@ -593,8 +509,7 @@ int cpu_feature_p(int feat)
   int IGNORABLE f;
   IGNORE(f);
 #define CASE_CPUFEAT(feat, ftok, cond) case CPUFEAT_##feat:            \
-  if ((f = feat_debug(ftok, "environment override",                    \
-                     check_env(ftok))) >= 0)                           \
+  if ((f = feat_debug(ftok, "environment override", check_env(ftok))) >= 0) \
     return (f);                                                                \
   else                                                                 \
     return (feat_debug(ftok, "runtime probe", cond));
@@ -602,22 +517,26 @@ int cpu_feature_p(int feat)
   switch (feat) {
 #if CPUFAM_X86 || CPUFAM_AMD64
     CASE_CPUFEAT(X86_SSE2, "x86:sse2",
-                cpuid_features_p(CPUID1D_SSE2, 0) &&
+                cpuid_feature_p(CPUID_1_D, CPUID1D_SSE2) &&
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_AESNI, "x86:aesni",
-                cpuid_features_p(CPUID1D_SSE2, CPUID1C_AESNI) &&
+                cpuid_feature_p(CPUID_1_C, CPUID1C_AESNI) &&
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_RDRAND, "x86:rdrand",
-                cpuid_features_p(0, CPUID1C_RDRAND) && rdrand_works_p());
+                cpuid_feature_p(CPUID_1_C, CPUID1C_RDRAND) &&
+                rdrand_works_p(OP_RDRAND));
     CASE_CPUFEAT(X86_AVX, "x86:avx",
-                cpuid_features_p(0, CPUID1C_AVX) &&
+                cpuid_feature_p(CPUID_1_C, CPUID1C_AVX) &&
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_SSSE3, "x86:ssse3",
-                cpuid_features_p(0, CPUID1C_SSSE3) &&
+                cpuid_feature_p(CPUID_1_C, CPUID1C_SSSE3) &&
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_PCLMUL, "x86:pclmul",
-                cpuid_features_p(0, CPUID1C_PCLMUL) &&
+                cpuid_feature_p(CPUID_1_C, CPUID1C_PCLMUL) &&
                 xmm_registers_available_p());
+    CASE_CPUFEAT(X86_RDSEED, "x86:rdseed",
+                cpuid_feature_p(CPUID_7_0_B, CPUID70B_RDSEED) &&
+                rdrand_works_p(OP_RDSEED));
 #endif
 #ifdef CAPMAP
 #  define FEATP__CASE(feat, tok)                                       \
index 7c08382..2c78d92 100644 (file)
@@ -180,12 +180,13 @@ enum {
   CPUFEAT_ARM_NEON,                    /* Advanced SIMD (v1 or v2) */
   CPUFEAT_ARM_V4,                      /* VFPv4 and/or SIMD v2 */
   CPUFEAT_ARM_D32,                     /* 32 double registers, not 16 */
-  CPUFEAT_X86_RDRAND,                  /* Built-in entropy source */
+  CPUFEAT_X86_RDRAND,                  /* Built-in cooked entropy source */
   CPUFEAT_ARM_AES,                     /* AES instructions */
   CPUFEAT_X86_AVX,                     /* AVX 1 (i.e., 256-bit YMM regs) */
   CPUFEAT_X86_SSSE3,                   /* Supplementary SSE 3 */
   CPUFEAT_X86_PCLMUL,                  /* Carry-less multiplication */
-  CPUFEAT_ARM_PMULL                    /* Polynomial multiplication */
+  CPUFEAT_ARM_PMULL,                   /* Polynomial multiplication */
+  CPUFEAT_X86_RDSEED                   /* Built-in raw entropy source */
 };
 
 extern int cpu_feature_p(int /*feat*/);
index 2d40558..9ed9f58 100644 (file)
@@ -140,6 +140,8 @@ CONVERSIONS(DEFINE_TO)
 #include <stdlib.h>
 #include <string.h>
 
+#include <mLib/macros.h>
+
 static const struct entry {
   const char *name;
   double (*func)(double);
@@ -162,7 +164,7 @@ int main(int argc, char *argv[])
     putc('\n', stderr);
     return (1);
   }
-  for (e = tab; e->name && strcmp(e->name, argv[1]); e++);
+  for (e = tab; e->name && STRCMP(e->name, !=, argv[1]); e++);
   if (!e) {
     fprintf(stderr, "unknown conversion `%s'\n", argv[1]);
     return (1);
index 4ad772a..2986d61 100644 (file)
@@ -59,7 +59,7 @@
 enum {
   KSZ_ANY,                             /* Allows any key at all */
   KSZ_RANGE,                           /* Allows keys within a range */
-  KSZ_SET,                             /* Allows specific sizes of keys */
+  KSZ_SET                              /* Allows specific sizes of keys */
 };
 
 #define KSZ_16BIT 0x20                 /* Arguments are 16 bits long */
index 963a60e..6adcbdd 100644 (file)
@@ -60,15 +60,8 @@ FUNC(regdump_gpsave)
        add     r0, r4, #REGDUMP_GPSIZE
        str     r0, [r4, #13*4]
 
-       // Capture the status flags and return address.  If the return
-       // address has its low bit set, then the caller was in Thumb state:
-       // clear the bit from the reconstructed PC, and set the corresponding
-       // CPSR bit.
-       mrs     r0, cpsr
-       tst     r14, #1
+       // Clear the magic Thumb-state bit from the return address.
        bic     r1, r14, #1
-       orrne   r0, r0, #0x00000020
-       str     r0, [r13, #4*REGIX_CPSR]
        str     r1, [r13, #15*4]
 
        // Load the focus address and save it as r6.
index 81c9f8e..183d38f 100644 (file)
@@ -56,10 +56,6 @@ FUNC(regdump_gpsave)
        stp     x2, x3, [sp, #16]
        stp     x4, x5, [sp, #32]
        stp     x6, x7, [sp, #48]
-       stp     x8, x9, [sp, #64]
-       stp     x10, x11, [sp, #80]
-       stp     x12, x13, [sp, #96]
-       stp     x14, x15, [sp, #112]
        stp     x18, x19, [sp, #144]
        stp     x20, x21, [sp, #160]
        stp     x22, x23, [sp, #176]
@@ -73,10 +69,6 @@ FUNC(regdump_gpsave)
        add     x0, x20, #REGDUMP_GPSIZE
        str     x0, [x20, #31*8]
 
-       // Capture the status flags.
-       mrs     x0, nzcv
-       str     x0, [x20, #8*REGIX_NZCV]
-
        // Set the return address as our PC.
        str     x30, [x20, #8*REGIX_PC]
 
index e4dd8e8..67a4ae0 100644 (file)
@@ -56,48 +56,48 @@ FUNC(regdump_gpsave)
        cld
 
        // Save r/ebp and establish it pointing to the save area.
-       mov     [R_sp(r) + WORDSZ + REGIX_BP*WORDSZ], R_bp(r)
-       lea     R_bp(r), [R_sp(r) + WORDSZ]
+       mov     [SP + WORDSZ + REGIX_BP*WORDSZ], BP
+       lea     BP, [SP + WORDSZ]
 
        // Save the other easy general-purpose registers.
 #if !CPUFAM_X86
-       mov     [R_bp(r) + REGIX_BX*WORDSZ], R_b(r)
+       mov     [BP + REGIX_BX*WORDSZ], BX
 #endif
-       mov     [R_bp(r) + REGIX_CX*WORDSZ], R_c(r)
-       mov     [R_bp(r) + REGIX_DX*WORDSZ], R_d(r)
-       mov     [R_bp(r) + REGIX_SI*WORDSZ], R_si(r)
-       mov     [R_bp(r) + REGIX_DI*WORDSZ], R_di(r)
+       mov     [BP + REGIX_CX*WORDSZ], CX
+       mov     [BP + REGIX_DX*WORDSZ], DX
+       mov     [BP + REGIX_SI*WORDSZ], SI
+       mov     [BP + REGIX_DI*WORDSZ], DI
 #if CPUFAM_AMD64
-       mov     [R_bp(r) + REGIX_R8*WORDSZ], R_r8(r)
-       mov     [R_bp(r) + REGIX_R9*WORDSZ], R_r9(r)
-       mov     [R_bp(r) + REGIX_R10*WORDSZ], R_r10(r)
-       mov     [R_bp(r) + REGIX_R11*WORDSZ], R_r11(r)
-       mov     [R_bp(r) + REGIX_R12*WORDSZ], R_r12(r)
-       mov     [R_bp(r) + REGIX_R13*WORDSZ], R_r13(r)
-       mov     [R_bp(r) + REGIX_R14*WORDSZ], R_r14(r)
-       mov     [R_bp(r) + REGIX_R15*WORDSZ], R_r15(r)
+       mov     [BP + REGIX_R8*WORDSZ], r8
+       mov     [BP + REGIX_R9*WORDSZ], r9
+       mov     [BP + REGIX_R10*WORDSZ], r10
+       mov     [BP + REGIX_R11*WORDSZ], r11
+       mov     [BP + REGIX_R12*WORDSZ], r12
+       mov     [BP + REGIX_R13*WORDSZ], r13
+       mov     [BP + REGIX_R14*WORDSZ], r14
+       mov     [BP + REGIX_R15*WORDSZ], r15
 #endif
 
        // Determine the previous stack pointer and save it.
 #if CPUFAM_AMD64 && ABI_SYSV
-       lea     R_a(r), [R_bp(r) + 128 + REGDUMP_GPSIZE]
+       lea     AX, [BP + 128 + REGDUMP_GPSIZE]
 #else
-       lea     R_a(r), [R_bp(r) + REGDUMP_GPSIZE]
+       lea     AX, [BP + REGDUMP_GPSIZE]
 #endif
-       mov     [R_bp(r) + REGIX_SP*WORDSZ], R_a(r)
+       mov     [BP + REGIX_SP*WORDSZ], AX
 
        // Collect the return address and save it as r/eip.
-       mov     R_a(r), [R_sp(r)]
-       mov     [R_bp(r) + REGIX_IP*WORDSZ], R_a(r)
+       mov     AX, [SP]
+       mov     [BP + REGIX_IP*WORDSZ], AX
 
        // Save the segment registers.
-       lea     R_a(r), [R_bp(r) + REGIX_GPLIM*WORDSZ]
-       mov     [R_a(r) + 2*REGIX_CS], cs
-       mov     [R_a(r) + 2*REGIX_DS], ds
-       mov     [R_a(r) + 2*REGIX_SS], ss
-       mov     [R_a(r) + 2*REGIX_ES], es
-       mov     [R_a(r) + 2*REGIX_FS], fs
-       mov     [R_a(r) + 2*REGIX_GS], gs
+       lea     AX, [BP + REGIX_GPLIM*WORDSZ]
+       mov     [AX + 2*REGIX_CS], cs
+       mov     [AX + 2*REGIX_DS], ds
+       mov     [AX + 2*REGIX_SS], ss
+       mov     [AX + 2*REGIX_ES], es
+       mov     [AX + 2*REGIX_FS], fs
+       mov     [AX + 2*REGIX_GS], gs
 
        // Determine the extended save area size.  Preserve ebx on 32-bit x86
        // here, because the caller needs it for PLT-indirect calls.
@@ -135,23 +135,23 @@ FUNC(regdump_gprstr)
 
        // We assume nobody actually fiddled with the segment registers.  So
        // just the actual integer registers to do.
-       mov     R_a(r), [R_bp(r) + REGIX_AX*WORDSZ]
-       mov     R_b(r), [R_bp(r) + REGIX_BX*WORDSZ]
-       mov     R_c(r), [R_bp(r) + REGIX_CX*WORDSZ]
-       mov     R_d(r), [R_bp(r) + REGIX_DX*WORDSZ]
-       mov     R_si(r), [R_bp(r) + REGIX_SI*WORDSZ]
-       mov     R_di(r), [R_bp(r) + REGIX_DI*WORDSZ]
+       mov     AX, [BP + REGIX_AX*WORDSZ]
+       mov     BX, [BP + REGIX_BX*WORDSZ]
+       mov     CX, [BP + REGIX_CX*WORDSZ]
+       mov     DX, [BP + REGIX_DX*WORDSZ]
+       mov     SI, [BP + REGIX_SI*WORDSZ]
+       mov     DI, [BP + REGIX_DI*WORDSZ]
 #if CPUFAM_AMD64
-       mov     R_r8(r), [R_bp(r) + REGIX_R8*WORDSZ]
-       mov     R_r9(r), [R_bp(r) + REGIX_R9*WORDSZ]
-       mov     R_r10(r), [R_bp(r) + REGIX_R10*WORDSZ]
-       mov     R_r11(r), [R_bp(r) + REGIX_R11*WORDSZ]
-       mov     R_r12(r), [R_bp(r) + REGIX_R12*WORDSZ]
-       mov     R_r13(r), [R_bp(r) + REGIX_R13*WORDSZ]
-       mov     R_r14(r), [R_bp(r) + REGIX_R14*WORDSZ]
-       mov     R_r15(r), [R_bp(r) + REGIX_R15*WORDSZ]
+       mov     r8, [BP + REGIX_R8*WORDSZ]
+       mov     r9, [BP + REGIX_R9*WORDSZ]
+       mov     r10, [BP + REGIX_R10*WORDSZ]
+       mov     r11, [BP + REGIX_R11*WORDSZ]
+       mov     r12, [BP + REGIX_R12*WORDSZ]
+       mov     r13, [BP + REGIX_R13*WORDSZ]
+       mov     r14, [BP + REGIX_R14*WORDSZ]
+       mov     r15, [BP + REGIX_R15*WORDSZ]
 #endif
-       mov     R_bp(r), [R_bp(r) + REGIX_BP*WORDSZ]
+       mov     BP, [BP + REGIX_BP*WORDSZ]
 
        // Done.
        ret
@@ -175,11 +175,11 @@ FUNC(regdump_xtsave)
        // general registers are clobbered.
 
        // Start by filling in the easy parts of the map.
-       mov     [R_sp(r) + WORDSZ + regmap_gp], R_bp(r)
-       lea     R_bp(r), [R_sp(r) + WORDSZ]
+       mov     [SP + WORDSZ + regmap_gp], BP
+       lea     BP, [SP + WORDSZ]
 
        xor     eax, eax                // clears rax too on amd64
-       mov     [R_bp(r) + regmap_avx], R_a(r)
+       mov     [BP + regmap_avx], AX
 
        // Find out whether we use `xsave'.  (Preserve ebx.)
 #if CPUFAM_X86
@@ -191,40 +191,40 @@ FUNC(regdump_xtsave)
        je      5f
 
        // We have the `xsave' machinery.  Select the base address.
-       lea     R_si(r), [R_sp(r) + WORDSZ + regmap_size + 63]
-       and     R_si(r), ~63
-       mov     [R_bp(r) + regmap_fx], R_si(r)
+       lea     SI, [SP + WORDSZ + regmap_size + 63]
+       and     SI, ~63
+       mov     [BP + regmap_fx], SI
 
        // Clear out the header area.
        xor     eax, eax
-       lea     R_di(r), [R_si(r) + 512]
+       lea     DI, [SI + 512]
        mov     ecx, 16
        rep stosd
 
        // Save the registers.
        mov     eax, 0x00000007
        xor     edx, edx
-       xsave   [R_si(r)]
+       xsave   [SI]
 
        // Establish the AVX pointer, if available.
-       test    dword ptr [R_si(r) + 512], 4 // = xstate_bv
+       test    dword ptr [SI + 512], 4 // = xstate_bv
        je      8f
 
        mov     eax, 13
        mov     ecx, 2
        cpuid
-       add     R_b(r), R_si(r)
-       mov     [R_bp(r) + regmap_avx], R_b(r)
+       add     BX, SI
+       mov     [BP + regmap_avx], BX
 
        jmp     8f
 
        // We have only `fxsave'.  Set the base address.
-5:     lea     R_si(r), [R_sp(r) + WORDSZ + regmap_size + 15]
-       and     R_si(r), ~15
-       mov     [R_bp(r) + regmap_fx], R_si(r)
+5:     lea     SI, [SP + WORDSZ + regmap_size + 15]
+       and     SI, ~15
+       mov     [BP + regmap_fx], SI
 
        // Save the registers.
-       fxsave  [R_si(r)]
+       fxsave  [SI]
 
        // Clear the x87 state; otherwise it can cause trouble later.
 8:     fninit
@@ -245,7 +245,7 @@ FUNC(regdump_xtrstr)
        // 32-bit x86, and the other general registers are clobbered.
 
        // Find the extended register dump.
-       mov     R_si(r), [R_bp(r) + regmap_fx]
+       mov     SI, [BP + regmap_fx]
 
        // Probe to find out whether we have `xsave'.
 #if CPUFAM_X86
@@ -259,14 +259,14 @@ FUNC(regdump_xtrstr)
        // We have the `xsave' machinery.
        mov     eax, 0x00000007
        xor     edx, edx
-       xrstor  [R_si(r)]
+       xrstor  [SI]
        jmp     8f
 
        // We must fake it up.
-1:     fxrstor [R_si(r)]
+1:     fxrstor [SI]
 
        // Done.
-8:     mov     R_bp(r), [R_bp(r) + regmap_gp]
+8:     mov     BP, [BP + regmap_gp]
 #if CPUFAM_X86
        pop     ebx
 #endif
index c591fd5..0fa76df 100644 (file)
@@ -213,6 +213,7 @@ static const char *regname(char *buf, uint32 f)
 
   switch (src) {
 
+    case REGSRC_NONE:
     case REGSRC_ABS:
       return (0);
 
@@ -374,6 +375,14 @@ static const char *regname(char *buf, uint32 f)
 #  define GP(gp) (gp).u64
 #endif
 
+#define CF (1 <<  0)
+#define PF (1 <<  2)
+#define AF (1 <<  4)
+#define ZF (1 <<  6)
+#define SF (1 <<  7)
+#define DF (1 << 10)
+#define OF (1 << 11)
+
 void regdump_init(void) { ; }
 
 static void dump_flags(const char *lbl, const char *reg, gpreg f)
@@ -390,6 +399,17 @@ static void dump_flags(const char *lbl, const char *reg, gpreg f)
         (GP(f) >>  7)&1u ? '+' : '-',
         (GP(f) >> 10)&1u ? '+' : '-',
         (GP(f) >> 11)&1u ? '+' : '-');
+  printf(";;\t\tcond:");
+  if (GP(f)&CF) printf(" c/b/nae"); else printf(" nc/ae/nb");
+  if (GP(f)&ZF) printf(" e/z"); else printf(" ne/nz");
+  if (GP(f)&SF) printf(" s"); else printf(" ns");
+  if (GP(f)&OF) printf(" o"); else printf(" no");
+  if (GP(f)&PF) printf(" p"); else printf(" np");
+  if ((GP(f)&CF) || (GP(f)&ZF)) printf(" be/na"); else printf(" a/nbe");
+  if (!(GP(f)&OF) == !(GP(f)&SF)) printf(" ge/nl"); else printf(" l/nge");
+  if (!(GP(f)&OF) == !(GP(f)&SF) && !(GP(f)&ZF))
+    printf(" g/nle"); else printf(" le/ng");
+  putchar('\n');
   printf(";;\t\tsystem: %ctf %cif iopl=%d %cnt "
                        "%crf %cvm %cac %cvif %cvip %cid\n",
         (GP(f) >>  8)&1u ? '+' : '-',
@@ -548,6 +568,11 @@ void regdump_simd(const struct regmap *map)
 
 #if CPUFAM_ARMEL
 
+#define NF (1u << 31)
+#define ZF (1u << 30)
+#define CF (1u << 29)
+#define VF (1u << 28)
+
 unsigned regdump__flags = 0;
 
 void regdump_init(void)
@@ -556,6 +581,17 @@ void regdump_init(void)
   if (cpu_feature_p(CPUFEAT_ARM_D32)) regdump__flags |= REGF_D32;
 }
 
+static void dump_conditions(unsigned f)
+{
+  if (f&NF) printf(" mi"); else printf(" pl");
+  if (f&ZF) printf(" eq"); else printf(" ne");
+  if (f&CF) printf(" cs/hs"); else printf(" cc/lo");
+  if (f&VF) printf(" vs"); else printf(" vc");
+  if ((f&CF) && !(f&ZF)) printf(" hi"); else printf(" ls");
+  if (!(f&VF) == !(f&NF)) printf(" ge"); else printf(" lt");
+  if (!(f&VF) == !(f&NF) && !(f&ZF)) printf(" gt"); else printf(" le");
+}
+
 static void dump_flags(const char *lbl, unsigned f)
 {
   static const char
@@ -569,7 +605,7 @@ static void dump_flags(const char *lbl, unsigned f)
   printf(";; ");
   if (lbl) printf("%s: ", lbl);
   printf("   cpsr = 0x%08x\n", f);
-  printf(";;\t\tuser: %cn %cz %cc %cv %cq ge=%c%c%c%c\n",
+  printf(";;\t\tuser: %cn %cz %cc %cv %cq ge=%c%c%c%c;",
         (f >> 31)&1u ? '+' : '-',
         (f >> 30)&1u ? '+' : '-',
         (f >> 29)&1u ? '+' : '-',
@@ -579,6 +615,7 @@ static void dump_flags(const char *lbl, unsigned f)
         (f >> 18)&1u ? '1' : '0',
         (f >> 17)&1u ? '1' : '0',
         (f >> 16)&1u ? '1' : '0');
+  dump_conditions(f); putchar('\n');
   printf(";;\t\tsystem: %cj it=%s:%c%c%c%c %ce %ca %ci %cf %ct m=%s\n",
         (f >> 24)&1u ? '+' : '-',
         condtab[(f >> 12)&15u],
@@ -601,12 +638,13 @@ static void dump_fpflags(const char *lbl, unsigned f)
   printf(";; ");
   if (lbl) printf("%s: ", lbl);
   printf("  fpscr = 0x%08x\n", f);
-  printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n",
+  printf(";;\t\tcond: %cn %cz %cc %cv %cqc;",
         (f >> 31)&1u ? '+' : '-',
         (f >> 30)&1u ? '+' : '-',
         (f >> 29)&1u ? '+' : '-',
         (f >> 28)&1u ? '+' : '-',
         (f >> 27)&1u ? '+' : '-');
+  dump_conditions(f); putchar('\n');
   printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n",
         (f >> 15)&1u ? '+' : '-',
         (f >> 12)&1u ? '+' : '-',
@@ -673,18 +711,35 @@ void regdump_simd(const struct regmap *map) { ; }
 
 #if CPUFAM_ARM64
 
+#define NF (1u << 31)
+#define ZF (1u << 30)
+#define CF (1u << 29)
+#define VF (1u << 28)
+
 void regdump_init(void) { ; }
 
+static void dump_conditions(unsigned f)
+{
+  if (f&NF) printf(" mi"); else printf(" pl");
+  if (f&ZF) printf(" eq"); else printf(" ne");
+  if (f&CF) printf(" cs/hs"); else printf(" cc/lo");
+  if (f&VF) printf(" vs"); else printf(" vc");
+  if ((f&CF) && !(f&ZF)) printf(" hi"); else printf(" ls");
+  if (!(f&VF) == !(f&NF)) printf(" ge"); else printf(" lt");
+  if (!(f&VF) == !(f&NF) && !(f&ZF)) printf(" gt"); else printf(" le");
+}
+
 static void dump_flags(const char *lbl, unsigned f)
 {
   printf(";; ");
   if (lbl) printf("%s: ", lbl);
   printf("   nzcv = 0x%08x\n", f);
-  printf(";;\t\tuser: %cn %cz %cc %cv\n",
+  printf(";;\t\tuser: %cn %cz %cc %cv;",
         (f >> 31)&1u ? '+' : '-',
         (f >> 30)&1u ? '+' : '-',
         (f >> 29)&1u ? '+' : '-',
         (f >> 28)&1u ? '+' : '-');
+  dump_conditions(f); putchar('\n');
 }
 
 static void dump_fpflags(const char *lbl, const struct fpsave *fp)
@@ -695,12 +750,13 @@ static void dump_fpflags(const char *lbl, const struct fpsave *fp)
   printf(";; ");
   if (lbl) printf("%s: ", lbl);
   printf("   fpsr = 0x%08x\n", fp->fpsr);
-  printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n",
+  printf(";;\t\tcond_a32: %cn %cz %cc %cv %cqc;",
         (fp->fpsr >> 31)&1u ? '+' : '-',
         (fp->fpsr >> 30)&1u ? '+' : '-',
         (fp->fpsr >> 29)&1u ? '+' : '-',
         (fp->fpsr >> 28)&1u ? '+' : '-',
         (fp->fpsr >> 27)&1u ? '+' : '-');
+  dump_conditions(fp->fpsr); putchar('\n');
   printf(";;\t\terror:  %cidc %cixc %cufc %cofc %cdzc %cioc\n",
         (fp->fpsr >>  7)&1u ? '+' : '-',
         (fp->fpsr >>  4)&1u ? '+' : '-',
@@ -803,6 +859,11 @@ void regdump(const void *base, const char *lbl, uint32 f)
   }
 
   switch (f&REGF_SRCMASK) {
+
+    case REGSRC_NONE:
+      printf(";; %s\n", lbl);
+      return;
+
     case REGSRC_ABS:
       p = base;
       break;
index f5b3306..3a6d59c 100644 (file)
@@ -135,6 +135,7 @@ union gp64 { uint64 u64; int64 i64; PTR64 };
 #define   REGSRC_SIMD  0x04000000      /* SIMD vector register */
 #define   REGSRC_STMMX 0x05000000      /* x86-specific: x87/MMX register */
 #define   REGSRC_SEG   0x06000000      /* x86-specific: segment register */
+#define   REGSRC_NONE  0x0f000000      /* just a message */
 /* Where to find the values. */
 
 #define REGF_WDMASK    0xf0000000
@@ -271,11 +272,11 @@ struct regmap {
        regmap_avx = 2*WORDSZ
        regmap_size = 3*WORDSZ
 
-#define REGDEF_GPX86_COMMON(rn, RN)                                    \
-       regsrc.e##rn = REGSRC_GP | REGIX_##RN;                          \
+#define REGDEF_GPX86_COMMON(rn, ix)                                    \
+       regsrc.e##rn = REGSRC_GP | ix;                                  \
        regty.e##rn = REGF_32;                                          \
        regfmt.e##rn = REGF_HEX;                                        \
-       regsrc.r##rn = REGSRC_GP | REGIX_##RN;                          \
+       regsrc.r##rn = REGSRC_GP | ix;                                  \
        regty.r##rn = REGF_64;                                          \
        regfmt.r##rn = REGF_HEX
 
@@ -289,7 +290,7 @@ struct regmap {
        regsrc.rn##x = REGSRC_GP | REGIX_##RN##X;                       \
        regty.rn##x = REGF_16;                                          \
        regfmt.rn##x = REGF_HEX;                                        \
-       REGDEF_GPX86_COMMON(rn##x, RN##X)
+       REGDEF_GPX86_COMMON(rn##x, REGIX_##RN##X)
 REGDEF_GPX86_ABCD(a, A)
 REGDEF_GPX86_ABCD(b, B)
 REGDEF_GPX86_ABCD(c, C)
@@ -312,7 +313,7 @@ REGDEF_GPX86_ABCD(d, D)
        regsrc.rn = REGSRC_GP | REGIX_##RN;                             \
        regty.rn = REGF_16;                                             \
        regfmt.rn = REGF_HEX;                                           \
-       REGDEF_GPX86_COMMON(rn, RN)
+       REGDEF_GPX86_COMMON(rn, REGIX_##RN)
 REGDEF_GPX86_XP(ip, IP)
 REGDEF_GPX86_XP(si, SI)
 REGDEF_GPX86_XP(di, DI)
@@ -381,32 +382,32 @@ DO8(REGDEF_SIMD)
 
        // Stash r/eax.  This is bletcherous: hope we don't get a signal in
        // the next few instructions.
-       mov     [R_sp(r) - REGDUMP_SPADJ + (REGIX_AX - 1)*WORDSZ], R_a(r)
+       mov     [SP - REGDUMP_SPADJ + (REGIX_AX - 1)*WORDSZ], AX
 
   .ifnes "\addr", "nil"
        // Collect the effective address for the following dump, leaving it
        // in the `addr' slot of the dump.
-       lea     R_a(r), \addr
-       mov     [R_sp(r) - REGDUMP_SPADJ + (REGIX_ADDR - 1)*WORDSZ], R_a(r)
+       lea     AX, \addr
+       mov     [SP - REGDUMP_SPADJ + (REGIX_ADDR - 1)*WORDSZ], AX
   .endif
 
        // Make space for the register save area.  On AMD64 with System/V
        // ABI, also skip the red zone.  Use `lea' here to preserve the
        // flags.
-       lea     R_sp(r), [R_sp(r) - REGDUMP_SPADJ]
+       lea     SP, [SP - REGDUMP_SPADJ]
 
        // Save flags and general-purpose registers.  On 32-bit x86, we save
        // ebx here and establish a GOT pointer here for the benefit of the
        // PLT-indirect calls made later on.
        pushf
 #  if CPUFAM_X86
-       mov     [esp + 4*REGIX_BX], ebx
+       mov     [SP + 4*REGIX_BX], ebx
        ldgot
 #  endif
        callext F(regdump_gpsave)
 
        // Make space for the extended registers.
-       sub     R_sp(r), R_c(r)
+       sub     SP, CX
        callext F(regdump_xtsave)
 
        // Prepare for calling back into C.  On 32-bit x86, leave space for
@@ -414,11 +415,11 @@ DO8(REGDEF_SIMD)
        // the `shadow space' for the called-function's arguments.  Also,
        // forcibly align the stack pointer to a 16-byte boundary.
 #  if CPUFAM_X86
-       sub     esp, 16
+       sub     SP, 16
 #  elif ABI_WIN
-       sub     rsp, 32
+       sub     SP, 32
 #  endif
-       and     R_sp(r), ~15
+       and     SP, ~15
 .endm
 
 .macro _rstrregs
@@ -426,27 +427,38 @@ DO8(REGDEF_SIMD)
 
        // We assume r/ebp still points to the register map.
        callext F(regdump_xtrstr)
-       mov     R_sp(r), R_bp(r)
+       mov     SP, BP
        callext F(regdump_gprstr)
        popf
-       lea     R_sp(r), [R_sp(r) + REGDUMP_SPADJ]
+       lea     SP, [SP + REGDUMP_SPADJ]
+.endm
+
+.macro _nilbase
+#  if CPUFAM_X86
+       xor     eax, eax
+       mov     [SP + 0], eax
+#  elif ABI_SYSV
+       xor     edi, edi
+#  elif ABI_WIN
+       xor     ecx, ecx
+#  endif
 .endm
 
 .macro _regbase
 #  if CPUFAM_X86
-       mov     [esp + 0], ebp
+       mov     [SP + 0], BP
 #  elif ABI_SYSV
-       mov     rdi, rbp
+       mov     rdi, BP
 #  elif ABI_WIN
-       mov     rcx, rbp
+       mov     rcx, BP
 #  endif
 .endm
 
 .macro _membase
-       mov     R_a(r), [R_bp(r) + regmap_gp]
+       mov     AX, [BP + regmap_gp]
 #  if CPUFAM_X86
        mov     eax, [eax + REGIX_ADDR*WORDSZ]
-       mov     [esp + 0], eax
+       mov     [SP + 0], eax
 #  elif ABI_SYSV
        mov     rdi, [rax + REGIX_ADDR*WORDSZ]
 #  elif ABI_WIN
@@ -457,7 +469,7 @@ DO8(REGDEF_SIMD)
 .macro _reglbl msg
   .ifeqs "\msg", ""
 #  if CPUFAM_X86
-       mov     dword ptr [esp + 4], 0
+       mov     dword ptr [SP + 4], 0
 #  elif ABI_SYSV
        xor     esi, esi
 #  elif ABI_WIN
@@ -466,7 +478,7 @@ DO8(REGDEF_SIMD)
   .else
 #  if CPUFAM_X86
        lea     eax, [INTADDR(.L$_reglbl$\@)]
-       mov     [esp + 4], eax
+       mov     [SP + 4], eax
 #  elif ABI_SYSV
        lea     rsi, [INTADDR(.L$_reglbl$\@)]
 #  elif ABI_WIN
@@ -481,7 +493,7 @@ DO8(REGDEF_SIMD)
 
 .macro _regfmt arg
 #  if CPUFAM_X86
-       mov     dword ptr [esp + 8], \arg
+       mov     dword ptr [SP + 8], \arg
 #  elif ABI_SYSV
        mov     edx, \arg
 #  elif ABI_WIN
@@ -600,6 +612,8 @@ DO16(REGDEF_NEONQ)
        sub     r13, r13, #REGDUMP_GPSIZE
 
        // Save flags and general-purpose registers.
+       mrs     r14, cpsr
+       str     r14, [r13, #4*REGIX_CPSR]
        str     r12, [r13, #4*12]
        bl      regdump_gpsave
 
@@ -625,6 +639,10 @@ DO16(REGDEF_NEONQ)
        add     r13, r13, #REGDUMP_GPSIZE
 .endm
 
+.macro _nilbase
+       mov     r0, #0
+.endm
+
 .macro _regbase
        mov     r0, r5
 .endm
@@ -756,8 +774,15 @@ DO32(REGDEF_FP)
        // Make space for the register save area.
        sub     sp, sp, #REGDUMP_GPSIZE
 
-       // Save flags and general-purpose registers.
-       stp     x16, x17, [sp, #8*16]
+       // Save flags and general-purpose registers.  The PLT linkage code
+       // makes free with x8--x17, so we must save those here.
+       mrs     x30, nzcv
+       str     x30, [sp, #8*REGIX_NZCV]
+       stp     x8, x9, [sp, #64]
+       stp     x10, x11, [sp, #80]
+       stp     x12, x13, [sp, #96]
+       stp     x14, x15, [sp, #112]
+       stp     x16, x17, [sp, #128]
        bl      regdump_gpsave
 
        // Make space for the extended registers.
@@ -776,6 +801,10 @@ DO32(REGDEF_FP)
        add     sp, sp, #REGDUMP_GPSIZE
 .endm
 
+.macro _nilbase
+       mov     x0, #0
+.endm
+
 .macro _regbase
        mov     x0, x21
 .endm
@@ -889,6 +918,15 @@ extern void regdump_freshline(void);
        _rstrregs
 .endm
 
+.macro msg     lbl
+       _saveregs
+       _nilbase
+       _reglbl "\lbl"
+       _regfmt REGSRC_NONE | (1 << REGF_WDSHIFT)
+       callext F(regdump)
+       _rstrregs
+.endm
+
 .macro reg     lbl, rn, fmt=0
        _saveregs
        _regbase
index a468db7..3be9ed1 100644 (file)
@@ -231,6 +231,7 @@ int rsvr_done(rsvr_state *st)
 #include <mLib/alloc.h>
 #include <mLib/bits.h>
 #include <mLib/darray.h>
+#include <mLib/macros.h>
 #include <mLib/report.h>
 #include <mLib/testrig.h>
 
@@ -305,12 +306,12 @@ static void parse_intlist(uint_v *v, const char *p)
   int e = errno;
 
   for (;;) {
-    while (isspace((unsigned char)*p)) p++;
+    while (ISSPACE(*p)) p++;
     if (!*p) break;
     if (*p == ',') p++;
-    while (isspace((unsigned char)*p)) p++;
+    while (ISSPACE(*p)) p++;
     errno = 0; n = strtoul(p, &q, 0);
-    if (errno || (*q && *q != ',' && !isspace((unsigned char)*q)))
+    if (errno || (*q && *q != ',' && !ISSPACE(*q)))
       die(1, "invalid int list");
     p = q; DA_PUSH(v, n);
   }
index a8c8d43..41ba77f 100644 (file)
@@ -10,9 +10,9 @@ vec:
 
 FUNC(main)
 
-       pushreg R_bp(r)
+       pushreg BP
        setfp
-       and     R_sp(r), ~15
+       and     SP, ~15
   endprologue
 
        fldz
@@ -32,7 +32,7 @@ FUNC(main)
 
        xor     eax, eax
        dropfp
-       popreg  R_bp(r)
+       popreg  BP
        ret
 
 ENDFUNC
index 5335a2e..4e8f489 100644 (file)
@@ -128,7 +128,7 @@ AC_CACHE_CHECK(
        .intel_syntax noprefix
        .globl  foo
 foo:
-       adcd    var, 0
+       adc     dword ptr var, 0
        ret
        .data
 var:   .long   1
@@ -430,7 +430,7 @@ dnl Set the master libraries we need.
 AC_SUBST([CATACOMB_LIBS])
 
 dnl Necessary support libraries.
-PKG_CHECK_MODULES([mLib], [mLib >= 2.3.0])
+PKG_CHECK_MODULES([mLib], [mLib >= 2.4.1])
 AM_CFLAGS="$AM_CFLAGS $mLib_CFLAGS"
 
 dnl--------------------------------------------------------------------------
index b97ed0c..4b961cd 100644 (file)
@@ -5,5 +5,6 @@ substvars
 *.debhelper
 catacomb
 catacomb-bin
+catacomb-data
 catacomb-dev
 catacomb2
index 66f19be..a8237d8 100644 (file)
@@ -24,6 +24,7 @@ libcatacomb.so.2 catacomb2 #MINVER#
        cpu_feature_p@Base 2.2.3
        dispatch_debug@Base 2.2.3
        (optional|arch=i386 amd64)dispatch_x86ish_cpuid@Base 2.5.0
+       (optional|arch=i386 amd64)dispatch_x86ish_rdrand@Base 2.6.0
        (optional|arch=i386 amd64)dispatch_x86ish_xmmregisters_p@Base 2.5.0
 
 ## regdump (available with `--enable-asm-debug')
@@ -127,14 +128,20 @@ libcatacomb.so.2 catacomb2 #MINVER#
        (optional|arch=i386)mpx_umul4_x86_avx@Base 2.3.0
        (optional|arch=amd64)mpx_umul4_amd64_sse2@Base 2.3.0
        (optional|arch=amd64)mpx_umul4_amd64_avx@Base 2.3.0
+       (optional|arch=armel armhf)mpx_umul4_arm_neon@Base 2.6.0
+       (optional|arch=arm64)mpx_umul4_arm64_simd@Base 2.6.0
        (optional|arch=i386)mpxmont_mul4_x86_sse2@Base 2.3.0
        (optional|arch=i386)mpxmont_mul4_x86_avx@Base 2.3.0
        (optional|arch=amd64)mpxmont_mul4_amd64_sse2@Base 2.3.0
        (optional|arch=amd64)mpxmont_mul4_amd64_avx@Base 2.3.0
+       (optional|arch=armel armhf)mpxmont_mul4_arm_neon@Base 2.6.0
+       (optional|arch=arm64)mpxmont_mul4_arm64_simd@Base 2.6.0
        (optional|arch=i386)mpxmont_redc4_x86_sse2@Base 2.3.0
        (optional|arch=i386)mpxmont_redc4_x86_avx@Base 2.3.0
        (optional|arch=amd64)mpxmont_redc4_amd64_sse2@Base 2.3.0
        (optional|arch=amd64)mpxmont_redc4_amd64_avx@Base 2.3.0
+       (optional|arch=armel armhf)mpxmont_redc4_arm_neon@Base 2.6.0
+       (optional|arch=arm64)mpxmont_redc4_arm64_simd@Base 2.6.0
 
 ## mparena
        mparena_create@Base 2.0.0
@@ -314,6 +321,10 @@ libcatacomb.so.2 catacomb2 #MINVER#
 ## mp-sqrt
        mp_sqrt@Base 2.1.1
 
+## mp-nthrt
+       mp_nthrt@Base 2.6.99~
+       mp_perfect_power_p@Base 2.6.99~
+
 ## mptext
        mp_read@Base 2.1.2
        mp_readdstr@Base 2.1.2
@@ -646,6 +657,7 @@ libcatacomb.so.2 catacomb2 #MINVER#
        rand_seed@Base 2.2.3
        rand_quick@Base 2.5.2
        (optional|arch=i386 amd64)rand_quick_x86ish_rdrand@Base 2.5.0
+       (optional|arch=i386 amd64)rand_quick_x86ish_rdseed@Base 2.6.0
        rand_key@Base 2.5.2
        rand_add@Base 2.2.3
        rand_goodbits@Base 2.2.3
@@ -4041,6 +4053,7 @@ libcatacomb.so.2 catacomb2 #MINVER#
        keccak1600_init@Base 2.4.0
        keccak1600_p@Base 2.4.0
        keccak1600_mix@Base 2.4.0
+       keccak1600_set@Base 2.6.0
        keccak1600_extract@Base 2.4.0
 
 ## sha3 common
@@ -4179,6 +4192,22 @@ libcatacomb.so.2 catacomb2 #MINVER#
        kmac128_rand@Base 2.0.0
        kmac256_rand@Base 2.0.0
 
+## strobe
+       strobe_init@Base 2.6.0
+       strobe_begin@Base 2.6.0
+       strobe_process@Base 2.6.0
+       strobe_done@Base 2.6.0
+       strobe_key@Base 2.6.0
+       strobe_ad@Base 2.6.0
+       strobe_prf@Base 2.6.0
+       strobe_clrout@Base 2.6.0
+       strobe_clrin@Base 2.6.0
+       strobe_encout@Base 2.6.0
+       strobe_encin@Base 2.6.0
+       strobe_macout@Base 2.6.0
+       strobe_macin@Base 2.6.0
+       strobe_ratchet@Base 2.6.0
+
 ## poly1305
        poly1305_keyinit@Base 2.4.0
        poly1305_keysz@Base 2.4.0
@@ -4409,7 +4438,9 @@ libcatacomb.so.2 catacomb2 #MINVER#
        key_close@Base 2.1.1
        key_discard@Base 2.1.1
        key_extract@Base 2.3.1
+       key_extractline@Base 2.6.0
        key_merge@Base 2.1.1
+       key_mergeline@Base 2.6.0
 
 ## key-misc
        key_byid@Base 2.1.1
index 3e08f89..19115e9 100644 (file)
@@ -1,3 +1,50 @@
+catacomb (2.6.99~) experimental; urgency=medium
+
+  * (placeholder for next minor version)
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sat, 29 Aug 2020 00:42:11 +0100
+
+catacomb (2.6.2) experimental; urgency=medium
+
+  * catacomb: Fix incorrect feature test for AESNI on Intel processors.
+    (This was introduced in 2.6.0.  Workaround for affected processors on
+    2.6.0 and 2.6.1: set `CATACOMB_CPUFEAT' to `-x86:aesni'.)
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sat, 13 Jun 2020 18:10:53 +0100
+
+catacomb (2.6.1) experimental; urgency=medium
+
+  * catacomb: Fix segfault from `rand_quick' on i386.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Mon, 25 May 2020 17:45:02 +0100
+
+catacomb (2.6.0) experimental; urgency=medium
+
+  * catacomb: Introduce Mike Hamburg's `STROBE' syymetric encryption
+    framework, based on Keccak.
+  * catacomb: Fix KCDSA prime generation so that it makes primes of
+    exactly the right length.  I think this is the last of the prime-
+    generation algorthms that needs fixing.
+  * catacomb: Inttroduce low-level key-file functions to accommodate
+    Python 3 bindings.
+  * catacomb: Support `tag:', `id:' and `type:' prefixes in `bytag' key
+    queries.
+  * catacomb-bin: Be consistent about metasyntax used to denote hash
+    function names.
+  * catacomb: Introduce fast SIMD multiplication for ARM32 and ARM64
+    platforms.  I think this finally means that X86 and ARM have similar
+    levels of optimization.
+  * catacomb: Check SIMD feature bit on ARM64 before using the optimized
+    code.  I don't know of any ARM64 implementations which lack SIMD
+    instructions, but the bit must be there for a reason, so I might as
+    well use it.
+  * catacomb-dev: Allow reading the current number of passes from a
+    `dsarand' object.
+  * catacomb: Prefer X84 `rdseed' instruction for quick entropy over
+    `rdrand' if it's available.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sat, 09 May 2020 17:38:45 +0100
+
 catacomb (2.5.2) experimental; urgency=medium
 
   * Merge changes from 2.4.5.
index c22c255..89903a1 100644 (file)
@@ -2,7 +2,7 @@ Source: catacomb
 Section: libs
 Priority: extra
 Build-Depends: debhelper (>= 10), python, valgrind [!armel], pkg-config,
- mlib-dev (>= 2.3.0)
+ mlib-dev (>= 2.4.1)
 Maintainer: Mark Wooding <mdw@distorted.org.uk>
 Standards-Version: 3.1.1
 
index e884d69..567fb92 100644 (file)
@@ -34,6 +34,7 @@
 #include <time.h>
 
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/sym.h>
 
 #include "key.h"
@@ -54,7 +55,7 @@ int key_chkident(const char *p)
   if (!p || !*p || strlen(p) > 255)
     return (-1);
   while (*p) {
-    if (*p == ':' || *p == '.' || isspace((unsigned char)*p))
+    if (*p == ':' || *p == '.' || ISSPACE(*p))
       return (-1);
     p++;
   }
@@ -252,8 +253,7 @@ int key_settag(key_file *f, key *k, const char *tag)
 
   /* --- See if the new tag is the same as the old one --- */
 
-  if ((!tag && !k->tag) ||
-      (tag && k->tag && strcmp(tag, k->tag) == 0))
+  if ((!tag && !k->tag) || (tag && k->tag && STRCMP(tag, ==, k->tag)))
     return (0);
 
   /* --- Allocate an entry for the new tag --- */
index 046819a..718dfb6 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <mLib/bits.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/sub.h>
 #include <mLib/sym.h>
 
index 169de2c..7cedb85 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <mLib/bits.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 
 #include "key-data.h"
 
@@ -104,10 +105,10 @@ int key_readflags(const char *p, char **pp, unsigned *ff, unsigned *mm)
 
     /* --- Look up the string in the flags table --- */
 
-    if (sz == 4 && strncmp(p, "none", 4) == 0)
+    if (sz == 4 && STRNCMP(p, ==, "none", 4))
       goto next;
     for (e = flagtab; e->name; e++) {
-      if (strncmp(e->name, p, sz) == 0) {
+      if (STRNCMP(e->name, ==, p, sz)) {
        if (e->name[sz] == 0) {
          ee = e;
          break;
index 5a7ce5c..3bb8e1d 100644 (file)
@@ -38,6 +38,7 @@
 #include <mLib/crc32.h>
 #include <mLib/dstr.h>
 #include <mLib/hash.h>
+#include <mLib/macros.h>
 #include <mLib/str.h>
 #include <mLib/sub.h>
 #include <mLib/sym.h>
@@ -142,234 +143,294 @@ static int insert(key_file *f, key *k)
 static time_t exptime(const char *p)
 {
   size_t sz = strlen(p);
-  if (strncmp(p, "expired", sz) == 0)
+  if (STRNCMP(p, ==, "expired", sz))
     return (KEXP_EXPIRE);
-  else if (strncmp(p, "forever", sz) == 0)
+  else if (STRNCMP(p, ==, "forever", sz))
     return (KEXP_FOREVER);
   else
     return (atol(p));
 }
 
-/* --- @key_merge@ --- *
+/* --- @merge_core@ --- *
  *
  * Arguments:  @key_file *f@ = pointer to file structure
  *             @const char *file@ = name of file (for error messages)
- *             @FILE *fp@ = file handle to read from
+ *             @int lno@ = line number
+ *             @char *p@ = pointer into the line buffer (which will be
+ *                     clobbered)
  *             @key_reporter *rep@ = error reporting function
  *             @void *arg@ = argument for function
+ *             @dstr *n, *v@ = scratch dynamic strings, which must be empty
+ *                     on entry and are left empty on exit
  *
- * Returns:    Error code (one of the @KERR@ constants).
+ * Returns:    ---
+ *
+ * Use:                This is the common core of @key_merge@ and @key_mergeline@.
  *
- * Use:                Reads keys from a file, and inserts them into the file.
+ *             It is assumed that the caller has already verified that the
+ *             keyring is writable.
  */
 
-int key_merge(key_file *f, const char *file, FILE *fp,
-             key_reporter *rep, void *arg)
+static void merge_core(key_file *f, const char *file, int lno, char *p,
+                      key_reporter *rep, void *arg, dstr *n, dstr *v)
 {
-  int line = 0;
-  dstr l = DSTR_INIT;
-  dstr n = DSTR_INIT, v = DSTR_INIT;
+  char *vf[6];
+  key *k = 0;
 
-  if (!(f->f & KF_WRITE))
-    return (KERR_READONLY);
+  /* --- Skip blank lines and comments --- *
+   *
+   * Quite what they're doing in what ought to be an automatically-
+   * maintained file I don't know.
+   */
+
+  while (ISSPACE(*p))
+    p++;
+  if (!*p || *p == '#')
+    goto skip;
+
+  /* --- Break the line into fields --- *
+   *
+   * There are currently six fields of interest:
+   *
+   *    * The key's identification (id, tag and type).
+   *    * The actual key data itself.
+   *    * The key expiry time.
+   *    * The key deletion time.
+   *    * The attributes field.
+   *    * Any further comments.
+   *
+   * All but the last field can contain no spaces.
+   */
 
-  for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) {
-    char *vf[6];
-    char *p = l.buf;
-    key *k;
-
-    /* --- Skip blank lines and comments --- *
-     *
-     * Quite what they're doing in what ought to be an automatically-
-     * maintained file I don't know.
-     */
-
-    line++;
-    while (isspace((unsigned char)*p))
-      p++;
-    if (!*p || *p == '#')
-      continue;
-
-    /* --- Break the line into fields --- *
-     *
-     * There are currently six fields of interest:
-     *
-     *  * The key's identification (id, tag and type).
-     *  * The actual key data itself.
-     *  * The key expiry time.
-     *  * The key deletion time.
-     *  * The attributes field.
-     *  * Any further comments.
-     *
-     * All but the last field can contain no spaces.
-     */
-
-    {
-      int n = str_split(p, vf, 5, &vf[5]);
-      if (n < 4) {
-       if (rep)
-         rep(file, line, "too few fields", arg);
-       goto skip_0;
-      }
+  {
+    int n = str_split(p, vf, 5, &vf[5]);
+    if (n < 4) {
+      if (rep)
+       rep(file, lno, "too few fields", arg);
+      goto skip;
     }
+  }
 
-    /* --- Allocate a new key block --- */
+  /* --- Allocate a new key block --- */
 
-    k = CREATE(key);
+  k = CREATE(key);
+  k->k = 0;
+  k->tag = 0;
+  k->type = 0;
 
-    /* --- Extract the key data into the block --- */
+  /* --- Extract the key data into the block --- */
 
-    if ((k->k = key_read(vf[1], 0)) == 0) {
-      if (rep)
-       rep(file, line, "bad key data", arg);
-      goto skip_1;
-    }
+  if ((k->k = key_read(vf[1], 0)) == 0) {
+    if (rep)
+      rep(file, lno, "bad key data", arg);
+    goto skip;
+  }
 
-    /* --- Decode the identification field --- *
-     *
-     * For compatibility, derive a keyid from the key data.  This can only be
-     * done if the key encoding is binary (and presumably old-encoding binary
-     * at that).
-     */
-
-    {
-      char *q = strchr(vf[0], ':');
-      char *qq;
-
-      if (!q) {
-       if (k->k->e != KENC_BINARY) {
-         if (rep)
-           rep(file, line, "new-style key encoding but no keyid", arg);
-         goto skip_2;
-       }
-       k->id = crc32(0, k->k->u.k.k, k->k->u.k.sz);
-       k->type = xstrdup(vf[0]);
+  /* --- Decode the identification field --- *
+   *
+   * For compatibility, derive a keyid from the key data.  This can only be
+   * done if the key encoding is binary (and presumably old-encoding binary
+   * at that).
+   */
+
+  {
+    char *q = strchr(vf[0], ':');
+    char *qq;
+
+    if (!q) {
+      if (k->k->e != KENC_BINARY) {
+       if (rep)
+         rep(file, lno, "new-style key encoding but no keyid", arg);
+       goto skip;
+      }
+      k->id = crc32(0, k->k->u.k.k, k->k->u.k.sz);
+      k->type = xstrdup(vf[0]);
+      k->tag = 0;
+    } else {
+      *q++ = 0;
+      k->id = strtoul(p, 0, 16);
+      if ((qq = strchr(q, ':')) == 0 || !qq[1]) {
+       if (qq)
+         *qq = 0;
        k->tag = 0;
       } else {
-       *q++ = 0;
-       k->id = strtoul(p, 0, 16);
-       if ((qq = strchr(q, ':')) == 0 || !qq[1]) {
-         if (qq)
-           *qq = 0;
-         k->tag = 0;
-       } else {
-         *qq++ = 0;
-         k->tag = xstrdup(qq);
-       }
-       k->type = xstrdup(q);
+       *qq++ = 0;
+       k->tag = xstrdup(qq);
       }
+      k->type = xstrdup(q);
     }
+  }
 
-    /* --- Get a key block for the new key --- */
+  /* --- Get a key block for the new key --- */
 
-    k->exp = exptime(vf[2]);
-    k->del = exptime(vf[3]);
+  k->exp = exptime(vf[2]);
+  k->del = exptime(vf[3]);
 
-    /* --- Insert the key block into the table --- */
+  /* --- Insert the key block into the table --- */
 
-    {
-      int err;
+  {
+    int err;
 
-    again:
-      if ((err = insert(f, k)) < 0) {
-       if (err == KERR_DUPTAG) {
-         if (rep)
-           rep(file, line, "duplicate key tag stripped", arg);
-         xfree(k->tag);
-         k->tag = 0;
-         goto again;
-       }
+  again:
+    if ((err = insert(f, k)) < 0) {
+      if (err == KERR_DUPTAG) {
        if (rep)
-         rep(file, line, key_strerror(err), arg);
-       goto skip_3;
+         rep(file, lno, "duplicate key tag stripped", arg);
+       xfree(k->tag);
+       k->tag = 0;
+       goto again;
       }
+      if (rep)
+       rep(file, lno, key_strerror(err), arg);
+      goto skip;
     }
+  }
 
-    /* --- Parse up the attributes, if specified --- */
+  /* --- Parse up the attributes, if specified --- */
 
     sym_create(&k->a);
-    if (vf[4] && strcmp(vf[4], "-") != 0) {
+    if (vf[4] && STRCMP(vf[4], !=, "-")) {
       url_dctx uc;
-      for (url_initdec(&uc, vf[4]); url_dec(&uc, &n, &v); ) {
-       key_putattr(f, k, n.buf, v.buf);
-       DRESET(&n); DRESET(&v);
-      }
+    for (url_initdec(&uc, vf[4]); url_dec(&uc, n, v); ) {
+      key_putattr(f, k, n->buf, v->buf);
+      DRESET(n); DRESET(v);
     }
+  }
 
-    /* --- Insert the comment --- */
+  /* --- Insert the comment --- */
 
-    if (vf[5])
-      k->c = xstrdup(vf[5]);
-    else
-      k->c = 0;
-    continue;
+  if (vf[5])
+    k->c = xstrdup(vf[5]);
+  else
+    k->c = 0;
 
-    /* --- Tidy up after something going wrong --- */
+  /* --- Done --- */
 
-  skip_3:
-    if (k->tag)
-      xfree(k->tag);
-    xfree(k->type);
-  skip_2:
-    key_drop(k->k);
-  skip_1:
+  f->f |= KF_MODIFIED;
+  return;
+
+  /* --- Tidy up after something going wrong --- */
+
+skip:
+  if (k) {
+    if (k->tag) xfree(k->tag);
+    if (k->type) xfree(k->type);
+    if (k->k) key_drop(k->k);
     DESTROY(k);
-  skip_0:;
   }
+}
+
+/* --- @key_merge@, @key_mergeline@ --- *
+ *
+ * Arguments:  @key_file *f@ = pointer to file structure
+ *             @const char *file@ = name of file (for error messages)
+ *             @int lno@ = line number (for error messages, @key_mergeline@)
+ *             @FILE *fp@ = file handle to read from (@key_merge@)
+ *             @const char *line@ = line from the input (@key_mergeline@)
+ *             @key_reporter *rep@ = error reporting function
+ *             @void *arg@ = argument for function
+ *
+ * Returns:    Error code (one of the @KERR@ constants).
+ *
+ * Use:                The @key_merge@ function reads keys from a file, and inserts
+ *             them into the keyring.
+ *
+ *             The @key_mergeline@ function reads a key from a single input
+ *             line (which may, but need not, have a final newline), and
+ *             adds it to the keyring.
+ *
+ *             The @key_mergeline@ function is intended to help with
+ *             interfacing Catacomb to runtimes which don't use C's @stdio@
+ *             streams, rather than as a general-purpose service, though if
+ *             it turns out to be useful in other ways then so much the
+ *             better.
+ */
 
-  /* --- Extensive tidying up now required --- */
+int key_merge(key_file *f, const char *file, FILE *fp,
+             key_reporter *rep, void *arg)
+{
+  dstr n = DSTR_INIT, v = DSTR_INIT;
+  dstr l = DSTR_INIT;
+  int lno = 1;
+
+  if (!(f->f & KF_WRITE))
+    return (KERR_READONLY);
+
+  for (; dstr_putline(&l, fp) != EOF; DRESET(&l))
+    merge_core(f, file, lno++, l.buf, rep, arg, &n, &v);
 
   dstr_destroy(&l);
-  dstr_destroy(&n);
-  dstr_destroy(&v);
-  f->f |= KF_MODIFIED;
+  dstr_destroy(&n); dstr_destroy(&v);
   return (0);
 }
 
-/* --- @key_extract@ --- *
+int key_mergeline(key_file *f, const char *file, int lno, const char *line,
+                 key_reporter *rep, void *arg)
+{
+  dstr n = DSTR_INIT, v = DSTR_INIT;
+  size_t len = strlen(line);
+  char *p;
+
+  if (!(f->f & KF_WRITE)) return (KERR_READONLY);
+
+  if (len && line[len - 1] == '\n') len--;
+  p = xmalloc(len); memcpy(p, line, len); p[len] = 0;
+  merge_core(f, file, lno, p, rep, arg, &n, &v);
+  xfree(p); dstr_destroy(&n); dstr_destroy(&v);
+  return (0);
+}
+
+/* --- @key_extract@, @key_extractline@ --- *
  *
  * Arguments:  @key_file *f@ = pointer to file structure
  *             @key *k@ = key to extract
- *             @FILE *fp@ = file to write on
+ *             @FILE *fp@ = file to write on (@key_extract@)
+ *             @dstr *d@ = string to write on (@key_extractline@)
  *             @const key_filter *kf@ = pointer to key selection block
  *
- * Returns:    Zero if OK, EOF on error.
+ * Returns:    @key_extract@ returns zero if OK, EOF on error.
+ *             @key_extractline@ does not return a value.
+ *
+ * Use:                Extracts a key to an ouptut file or buffer.
+ *
+ *             The @key_extractline@ includes a final newline in its output.
  *
- * Use:                Extracts a key to an ouptut file.
+ *             The @key_extractline@ function is intended to help with
+ *             interfacing Catacomb to runtimes which don't use C's @stdio@
+ *             streams, rather than as a general-purpose service, though if
+ *             it turns out to be useful in other ways then so much the
+ *             better.
  */
 
-int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf)
+void key_extractline(key_file *f, key *k, dstr *d, const key_filter *kf)
 {
-  dstr d = DSTR_INIT;
   time_t t = time(0);
 
   /* --- Skip the key if it's deleted or unselected--- */
 
   if (KEY_EXPIRED(t, k->del) || !key_match(k->k, kf))
-    return (0);
+    return;
 
   /* --- Encode the key and write the easy stuff --- */
 
-  key_fulltag(k, &d);
-  DPUTC(&d, ' ');
-  if (!key_write(k->k, &d, kf)) dstr_puts(&d, "struct:[]");
-  DPUTC(&d, ' ');
-  dstr_write(&d, fp);
-  DRESET(&d);
+  key_fulltag(k, d);
+  DPUTC(d, ' ');
+  if (!key_write(k->k, d, kf)) dstr_puts(d, "struct:[]");
+  DPUTC(d, ' ');
 
   /* --- Write out the expiry and deletion times --- */
 
   if (KEY_EXPIRED(t, k->exp))
-    fputs("expired ", fp);
+    dstr_puts(d, "expired ");
   else if (k->exp == KEXP_FOREVER)
-    fputs("forever ", fp);
+    dstr_puts(d, "forever ");
   else
-    fprintf(fp, "%li ", (long)k->exp);
+    dstr_putf(d, "%li ", (long)k->exp);
 
   if (k->del == KEXP_FOREVER)
-    fputs("forever ", fp);
+    dstr_puts(d, "forever ");
   else
-    fprintf(fp, "%li ", (long)k->del);
+    dstr_putf(d, "%li ", (long)k->del);
 
   /* --- Output the attributes --- */
 
@@ -382,19 +443,24 @@ int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf)
     url_initenc(&uc);
     for (sym_mkiter(&i, &k->a); (a = sym_next(&i)) != 0; ) {
       none = 0;
-      url_enc(&uc, &d, SYM_NAME(a), a->p);
+      url_enc(&uc, d, SYM_NAME(a), a->p);
     }
     if (none)
-      DPUTS(&d, "-");
-    DWRITE(&d, fp);
+      DPUTC(d, '-');
   }
 
-  dstr_destroy(&d);
-  if (k->c) {
-    putc(' ', fp);
-    fputs(k->c, fp);
-  }
-  putc('\n', fp);
+  if (k->c)
+    dstr_putf(d, " %s", k->c);
+
+  DPUTC(d, '\n'); DPUTZ(d);
+}
+
+int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf)
+{
+  dstr d = DSTR_INIT;
+
+  key_extractline(f, k, &d, kf);
+  dstr_write(&d, fp);
   return (ferror(fp) ? EOF : 0);
 }
 
index 0d969a8..0afad76 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <mLib/bits.h>
 #include <mLib/hash.h>
+#include <mLib/macros.h>
 #include <mLib/sub.h>
 #include <mLib/sym.h>
 
@@ -159,14 +160,29 @@ key *key_bytag(key_file *f, const char *tag)
   time_t t = time(0);
   char *p;
   uint32 id;
-  key_ref *kr = sym_find(&f->bytag, tag, -1, 0, 0);
+  enum { GUESS = -1, TAG, ID, TYPE }; int lookup;
+  key_ref *kr;
   key *k;
 
-  if (kr && !(KEY_EXPIRED(t, kr->k->exp) && KEY_EXPIRED(t, kr->k->del)))
+  if (STRNCMP(tag, ==, "tag:", 4)) { lookup = TAG; tag += 4; }
+  else if (STRNCMP(tag, ==, "id:", 3)) { lookup = ID; tag += 3; }
+  else if (STRNCMP(tag, ==, "type:", 5)) { lookup = TYPE; tag += 5; }
+  else lookup = GUESS;
+
+  if ((lookup == GUESS || lookup == TAG) &&
+      (kr = sym_find(&f->bytag, tag, -1, 0, 0)) != 0 &&
+      !(KEY_EXPIRED(t, kr->k->exp) && KEY_EXPIRED(t, kr->k->del)))
     return (kr->k);
-  id = strtoul(tag, &p, 16);
-  if (!*p && (k = key_byid(f, id)) != 0) return (k);
-  return (key_bytype(f, tag));
+
+  if (lookup == GUESS || lookup == ID) {
+    id = strtoul(tag, &p, 16);
+    if (!*p && (k = key_byid(f, id)) != 0) return (k);
+  }
+
+  if ((lookup == GUESS || lookup == TYPE) && (k = key_bytype(f, tag)) != 0)
+    return (k);
+
+  return (0);
 }
 
 /* --- @key_qtag@ --- *
index 665030e..223486e 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <mLib/dstr.h>
 
+#include "ct.h"
 #include "key-data.h"
 #include "paranoia.h"
 #include "passphrase.h"
@@ -172,8 +173,8 @@ int key_unlock(key_data **kt, key_data *k, const void *e, size_t esz)
   rmd160_macinit(&mc, &mk);
   rmd160_machash(&mc, k->u.k.k + RMD160_HASHSZ * 2, sz);
   rmd160_macdone(&mc, b + RMD160_HASHSZ);
-  if (memcmp(b + RMD160_HASHSZ, k->u.k.k + RMD160_HASHSZ,
-            RMD160_HASHSZ) != 0) {
+  if (!ct_memeq(b + RMD160_HASHSZ,
+               k->u.k.k + RMD160_HASHSZ, RMD160_HASHSZ)) {
     rc = KERR_BADPASS;
     goto fail;
   }
index 98c1fba..ace69ce 100644 (file)
@@ -34,6 +34,7 @@
 #include <mLib/base64.h>
 #include <mLib/bits.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/sub.h>
 #include <mLib/sym.h>
 #include <mLib/url.h>
@@ -298,7 +299,7 @@ int key_write(key_data *k, dstr *d, const key_filter *kf)
       DPUTC(d, ':');
       while (*p) {
        if (*p == ' ') DPUTC(d, '+');
-       else if (!isalnum((unsigned char)*p)) dstr_putf(d, "%%%02x", *p);
+       else if (!ISALNUM(*p)) dstr_putf(d, "%%%02x", *p);
        else DPUTC(d, *p);
        p++;
       }
index bed3632..96a2b5a 100644 (file)
--- a/key/key.h
+++ b/key/key.h
@@ -177,36 +177,64 @@ typedef void key_reporter(const char */*file*/, int /*line*/,
 
 /*----- Reading and writing keys and files --------------------------------*/
 
-/* --- @key_merge@ --- *
+/* --- @key_merge@, @key_mergeline@ --- *
  *
  * Arguments:  @key_file *f@ = pointer to file structure
  *             @const char *file@ = name of file (for error messages)
- *             @FILE *fp@ = file handle to read from
+ *             @int lno@ = line number (for error messages, @key_mergeline@)
+ *             @FILE *fp@ = file handle to read from (@key_merge@)
+ *             @const char *line@ = line from the input (@key_mergeline@)
  *             @key_reporter *rep@ = error reporting function
  *             @void *arg@ = argument for function
  *
  * Returns:    Error code (one of the @KERR@ constants).
  *
- * Use:                Reads keys from a file, and inserts them into the file.
+ * Use:                The @key_merge@ function reads keys from a file, and inserts
+ *             them into the keyring.
+ *
+ *             The @key_mergeline@ function reads a key from a single input
+ *             line (which may, but need not, have a final newline), and
+ *             adds it to the keyring.
+ *
+ *             The @key_mergeline@ function is intended to help with
+ *             interfacing Catacomb to runtimes which don't use C's @stdio@
+ *             streams, rather than as a general-purpose service, though if
+ *             it turns out to be useful in other ways then so much the
+ *             better.
  */
 
 extern int key_merge(key_file */*f*/, const char */*file*/, FILE */*fp*/,
                     key_reporter */*rep*/, void */*arg*/);
+extern int key_mergeline(key_file */*f*/, const char */*file*/, int /*lno*/,
+                        const char */*line*/,
+                        key_reporter */*rep*/, void */*arg*/);
 
-/* --- @key_extract@ --- *
+/* --- @key_extract@, @key_extractline@ --- *
  *
  * Arguments:  @key_file *f@ = pointer to file structure
  *             @key *k@ = key to extract
- *             @FILE *fp@ = file to write on
+ *             @FILE *fp@ = file to write on (@key_extract@)
+ *             @dstr *d@ = string to write on (@key_extractline@)
  *             @const key_filter *kf@ = pointer to key selection block
  *
- * Returns:    Zero if OK, EOF on error.
+ * Returns:    @key_extract@ returns zero if OK, EOF on error.
+ *             @key_extractline@ does not return a value.
+ *
+ * Use:                Extracts a key to an ouptut file or buffer.
+ *
+ *             The @key_extractline@ includes a final newline in its output.
  *
- * Use:                Extracts a key to an ouptut file.
+ *             The @key_extractline@ function is intended to help with
+ *             interfacing Catacomb to runtimes which don't use C's @stdio@
+ *             streams, rather than as a general-purpose service, though if
+ *             it turns out to be useful in other ways then so much the
+ *             better.
  */
 
 extern int key_extract(key_file */*f*/, key */*k*/, FILE */*fp*/,
                       const key_filter */*kf*/);
+extern void key_extractline(key_file */*f*/, key */*k*/,
+                           dstr */*d*/, const key_filter */*kf*/);
 
 /* --- @key_open@ --- *
  *
index 6f0780f..3a287bd 100644 (file)
@@ -35,6 +35,7 @@
 #include <unistd.h>
 
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 
 #include "passphrase.h"
 #include "pixie.h"
@@ -122,7 +123,7 @@ int passphrase_read(const char *tag, unsigned mode, char *buf, size_t sz)
     char b[1024];
     DRESET(&d);
     dstr_putf(&d, "Verify passphrase %s: ", tag);
-    if (pixie_getpass(d.buf, b, sizeof(b)) || strcmp(b, buf) != 0) {
+    if (pixie_getpass(d.buf, b, sizeof(b)) || STRCMP(b, !=, buf)) {
       memset(b, 0, sizeof(b));
       goto fail;
     }
index 2975c20..d3348bc 100644 (file)
@@ -44,6 +44,7 @@
 
 #include <mLib/alloc.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/str.h>
 
 #include "pixie.h"
@@ -283,11 +284,11 @@ again:
   p = buf;
   if ((q = str_getword(&p)) == 0)
     return (-1);
-  if (strcmp(q, "INFO") == 0)
+  if (STRCMP(q, ==, "INFO"))
     goto again;
-  else if (strcmp(q, "MISSING") == 0)
+  else if (STRCMP(q, ==, "MISSING"))
     return (+1);
-  else if (strcmp(q, "OK") != 0)
+  else if (STRCMP(q, !=, "OK"))
     return (-1);
 
   /* --- Return the final answer --- */
@@ -336,7 +337,7 @@ void pixie_set(int fd, const char *tag, const char *phrase)
 again:
   pixie_fdline(fd, buf, sizeof(buf));
   p = buf;
-  if ((q = str_getword(&p)) != 0 && strcmp(q, "INFO") == 0)
+  if ((q = str_getword(&p)) != 0 && STRCMP(q, ==, "INFO"))
     goto again;
 }
 
@@ -367,7 +368,7 @@ void pixie_cancel(int fd, const char *tag)
 again:
   pixie_fdline(fd, buf, sizeof(buf));
   p = buf;
-  if ((q = str_getword(&p)) != 0 && strcmp(q, "INFO") == 0)
+  if ((q = str_getword(&p)) != 0 && STRCMP(q, ==, "INFO"))
     goto again;
 }
 
index d5e678f..31de01a 100644 (file)
@@ -2,7 +2,7 @@ dnl -*-autoconf-*-
 
 ### SYNOPSIS
 ###
-###   dnl mdw_UINT_BITS(TYPE, ABBREV)
+###   mdw_UINT_BITS(TYPE, ABBREV)
 ###
 ### DESCRIPTION
 ###
index 7c4ffed..8c53940 100644 (file)
@@ -100,6 +100,8 @@ libmath_la_SOURCES  += mp-modsqrt.c
 TESTS                  += mp-modsqrt.t$(EXEEXT)
 libmath_la_SOURCES     += mp-sqrt.c
 TESTS                  += mp-sqrt.t$(EXEEXT)
+libmath_la_SOURCES     += mp-nthrt.c
+TESTS                  += mp-nthrt.t$(EXEEXT)
 libmath_la_SOURCES     += mp-test.c
 EXTRA_DIST             += t/mp
 
@@ -192,6 +194,16 @@ MPX_MUL4_SOURCES    = mpx-mul4-amd64-sse2.S
 check_PROGRAMS         += mpx-mul4.t
 TESTS                  += mpx-mul4.t$(EXEEXT)
 endif
+if CPUFAM_ARMEL
+MPX_MUL4_SOURCES        = mpx-mul4-arm-neon.S
+check_PROGRAMS         += mpx-mul4.t
+TESTS                  += mpx-mul4.t$(EXEEXT)
+endif
+if CPUFAM_ARM64
+MPX_MUL4_SOURCES        = mpx-mul4-arm64-simd.S
+check_PROGRAMS         += mpx-mul4.t
+TESTS                  += mpx-mul4.t$(EXEEXT)
+endif
 libmath_la_SOURCES     += $(MPX_MUL4_SOURCES)
 mpx_mul4_t_SOURCES      = mpx-mul4-test.c $(MPX_MUL4_SOURCES)
 mpx_mul4_t_CPPFLAGS     = \
index 5f7b1d7..e335286 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <mLib/macros.h>
 #include "bitops.h"
 #include "mpx.h"
 
@@ -45,7 +46,7 @@ int main(void)
   buf[2] = B##string(1u, 0u) & 1u? '1' : '0';                          \
   buf[3] = B##string(1u, 1u) & 1u? '1' : '0';                          \
   buf[4] = 0;                                                          \
-  if (strcmp(buf, ref) != 0) {                                         \
+  if (STRCMP(buf, !=, ref)) {                                          \
     fprintf(stderr, "mismatch ref `%s' != buf `%s'\n", ref, buf);      \
     rc = 1;                                                            \
   }                                                                    \
index d9cda6f..ff01a2e 100644 (file)
@@ -215,6 +215,8 @@ const test_type type_ec = { eccvt, ecdump };
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
+
 static void ecdestroy(ec_curve *c)
 {
   field *f = c->f;
@@ -354,7 +356,7 @@ static int vec2osp(dstr v[])
   int ok = 1;
   int win, wantwin;
 
-  if (strcmp(v[3].buf, "FAIL") == 0) wantwin = 0;
+  if (STRCMP(v[3].buf, ==, "FAIL")) wantwin = 0;
   else { wantwin = 1; type_hex.cvt(v[3].buf, &d); }
 
   dstr_ensure(&dd, n); buf_init(&b, dd.buf, n);
@@ -362,7 +364,7 @@ static int vec2osp(dstr v[])
   win = !ec_ec2osp(e, f, &b, p);
   if (!win != !wantwin ||
       (win && (BLEN(&b) != d.len ||
-              memcmp(BBASE(&b), d.buf, BLEN(&b)) != 0))) {
+              MEMCMP(BBASE(&b), !=, d.buf, BLEN(&b))))) {
     ok = 0;
     fprintf(stderr, "ec2osp failed");
     fprintf(stderr, "\ncurve = "); type_ecurve.dump(v, stderr);
@@ -393,7 +395,7 @@ static int vos2ecp(dstr v[])
   int ok = 1;
   int win;
 
-  if (strcmp(v[3].buf, "FAIL") == 0) p = 0;
+  if (STRCMP(v[3].buf, ==, "FAIL")) p = 0;
   else { type_ec.cvt(v[3].buf, &d); p = (ec *)d.buf; }
 
   buf_init(&b, v[2].buf, v[2].len);
index a886465..a0f40fa 100644 (file)
@@ -183,18 +183,18 @@ void f25519_load(f25519 *z, const octet xv[32])
    * and lower bounds are achievable.
    *
    * All of the x_i at this point are positive, so we don't need to do
-   * anything wierd when masking them.
+   * anything weird when masking them.
    */
-  b = x9&B24; c   = 19&((b >> 19) - (b >> 24)); x9 -= b << 1;
-  b = x8&B25; x9 +=      b >> 25;              x8 -= b << 1;
-  b = x7&B24; x8 +=     b >> 24;               x7 -= b << 1;
-  b = x6&B25; x7 +=     b >> 25;               x6 -= b << 1;
-  b = x5&B24; x6 +=     b >> 24;               x5 -= b << 1;
-  b = x4&B25; x5 +=     b >> 25;               x4 -= b << 1;
-  b = x3&B24; x4 +=     b >> 24;               x3 -= b << 1;
-  b = x2&B25; x3 +=     b >> 25;               x2 -= b << 1;
-  b = x1&B24; x2 +=     b >> 24;               x1 -= b << 1;
-  b = x0&B25; x1 +=     (b >> 25) + (x0 >> 26); x0 = (x0&M26) - (b << 1);
+  b = x9&B24; c   = 19&((b >> 19) - (b >> 24)); x9 -=            b << 1;
+  b = x8&B25; x9 +=     b >> 25;               x8 -=             b << 1;
+  b = x7&B24; x8 +=     b >> 24;               x7 -=             b << 1;
+  b = x6&B25; x7 +=     b >> 25;               x6 -=             b << 1;
+  b = x5&B24; x6 +=     b >> 24;               x5 -=             b << 1;
+  b = x4&B25; x5 +=     b >> 25;               x4 -=             b << 1;
+  b = x3&B24; x4 +=     b >> 24;               x3 -=             b << 1;
+  b = x2&B25; x3 +=     b >> 25;               x2 -=             b << 1;
+  b = x1&B24; x2 +=     b >> 24;               x1 -=             b << 1;
+  b = x0&B25; x1 +=     (b >> 25) + (x0 >> 26); x0  = (x0&M26) - (b << 1);
              x0 +=      c;
 
   /* And with that, we're done. */
@@ -756,7 +756,7 @@ static void carry_reduce(dblpiece x[NPIECE])
    * signed.
    */
 
-  /*For each piece, we bias it so that floor division (as done by an
+  /* 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.
    */
@@ -1178,6 +1178,7 @@ int f25519_quosqrt(f25519 *z, const f25519 *x, const f25519 *y)
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
 #include <mLib/report.h>
 #include <mLib/str.h>
 #include <mLib/testrig.h>
@@ -1218,7 +1219,7 @@ static void dump_f25519_ref(dstr *d, FILE *fp)
 }
 
 static int eq(const f25519 *x, dstr *d)
-  { octet b[32]; f25519_store(b, x); return (memcmp(b, d->buf, 32) == 0); }
+  { octet b[32]; f25519_store(b, x); return (MEMCMP(b, ==, d->buf, 32)); }
 
 static const test_type
   type_f25519 = { cvt_f25519, dump_f25519 },
index 1b09b58..43309dc 100644 (file)
@@ -962,6 +962,7 @@ int fgoldi_quosqrt(fgoldi *z, const fgoldi *x, const fgoldi *y)
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
 #include <mLib/report.h>
 #include <mLib/str.h>
 #include <mLib/testrig.h>
@@ -1002,7 +1003,7 @@ static void dump_fgoldi_ref(dstr *d, FILE *fp)
 }
 
 static int eq(const fgoldi *x, dstr *d)
-  { octet b[56]; fgoldi_store(b, x); return (memcmp(b, d->buf, 56) == 0); }
+  { octet b[56]; fgoldi_store(b, x); return (MEMCMP(b, ==, d->buf, 56)); }
 
 static const test_type
   type_fgoldi = { cvt_fgoldi, dump_fgoldi },
index 1b6c16e..5d922dd 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <ctype.h>
 
+#include <mLib/macros.h>
 #include <mLib/sub.h>
 
 #define ge ec
@@ -99,15 +100,15 @@ static int gread(group *gg, ec *d, const mptext_ops *ops, void *p) {
   int ch;
 
   ch = ops->get(p);
-  if (tolower(ch) == 'i') {
-    if (tolower(ops->get(p)) != 'n' || tolower(ops->get(p)) != 'f')
+  if (TOLOWER(ch) == 'i') {
+    if (TOLOWER(ops->get(p)) != 'n' || TOLOWER(ops->get(p)) != 'f')
       return (-1);
     EC_SETINF(d);
     return (0);
   }
   ops->unget(ch, p);
   if ((t.x = mp_read(MP_NEW, 0, ops, p)) == 0) goto done;
-  do ch = ops->get(p); while (ch == ',' || isspace(ch)); ops->unget(ch, p);
+  do ch = ops->get(p); while (ch == ',' || ISSPACE(ch)); ops->unget(ch, p);
   if ((t.y = mp_read(MP_NEW, 0, ops, p)) == 0) goto done;
   EC_IN(g->ei.c, &t, &t);
   if (EC_CHECK(g->ei.c, &t)) goto done;
index 8b47ac9..1ed7b4d 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <mLib/darray.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -123,8 +124,8 @@ int main(int argc, char *argv[])
     if (!sym) {
       for (q = header; *q; q++) {
        int ch = (unsigned char)*q;
-       if (isalnum(ch))
-         ch = toupper(ch);
+       if (ISALNUM(ch))
+         ch = TOUPPER(ch);
        else
          ch = '_';
        DPUTC(&d, ch);
index 9bf3cbe..0354cc2 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <mLib/darray.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -127,8 +128,8 @@ int main(int argc, char *argv[])
     if (!sym) {
       for (q = header; *q; q++) {
        int ch = (unsigned char)*q;
-       if (isalnum(ch))
-         ch = toupper(ch);
+       if (ISALNUM(ch))
+         ch = TOUPPER(ch);
        else
          ch = '_';
        DPUTC(&d, ch);
index 99f3bea..f29c104 100644 (file)
@@ -554,12 +554,16 @@ mp *gfreduce_quadsolve(const gfreduce *r, mp *d, mp *x)
      *
      * Choose %$\rho \inr \gf{2^m}$% with %$\Tr(\rho) = 1$%.  Let
      * %$z = \sum_{0\le i<m} \rho^{2^i} \sum_{0\le j<i} x^{2^j} = {}$%
+     * %$\sum_{1\le i<m} \rho^{2^i} (x + \sum_{1\le j<i} x^{2^j} = {}$%
      * %$\rho^2 x + \rho^4 (x + x^2) + \rho^8 (x + x^2 + x^4) + \cdots + {}$%
      * %$\rho^{2^{m-1}} (x + x^2 + x^{2^{m-2}})$%.  Then %$z^2 = {}$%
      * %$\sum_{0\le i<m} \rho^{2^{i+1}} \sum_{0\le j<i} x^{2^{j+1}} = {}$%
-     * %$\sum_{1\le i\le m} \rho^{2^i} \sum_{1\le j\le i} x^{2^j}$% and,
-     * somewhat miraculously, %$z^2 + z = \sum_{0\le i<m} \rho^{2^i} x + {}$%
-     * %$\rho \sum_{1\le i<m} x^{2^i} = x \Tr(\rho) + \rho \Tr(x)$%.  Again,
+     * %$\sum_{1\le i\le m} \rho^{2^i} \sum_{1\le j<i} x^{2^j} = {}$%
+     * %$\sum_{1\le i<m} \rho^{2^i} \sum_{1\le j<i} x^{2^j} + {}$%
+     * %$\rho^{2^m} \sum_{1\le j<m} x^{2^j}$%; and, somewhat miraculously,
+     * %$z^2 + z = \sum_{1\le i<m} \rho^{2^i} x + {}$%
+     * %$\rho \sum_{1\le i<m} x^{2^i} = x (\Tr(\rho) + \rho) + {}$%
+     * %$\rho (\Tr(x) + x) = x \Tr(\rho) + \rho \Tr(x)$%.  Again,
      * this gives us the root we want whenever %$\Tr(x) = 0$%.
      *
      * The loop below calculates %$w = \Tr(\rho)$% and %$z$% simultaneously,
index 4714183..4c7ce8e 100644 (file)
@@ -78,6 +78,4 @@ int group_writedstr(group *g, ge *x, dstr *d)
   return (0);
 }
 
-
-
 /*----- That's all, folks -------------------------------------------------*/
index 493ee2f..dfbc37f 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <stdarg.h>
 
+#include <mLib/macros.h>
 #include <mLib/testrig.h>
 
 #include "group.h"
@@ -108,7 +109,7 @@ static int vcheck(dstr *v)
   gr->ops->destroy(gr);
   if (!e) e = "ok";
   G_DESTROYGROUP(g);
-  if (strcmp(e, v[1].buf)) {
+  if (STRCMP(e, !=, v[1].buf)) {
     ok = 0;
     fprintf(stderr, "*** check failed\n");
     fprintf(stderr, "*** group: %s\n", v[0].buf);
@@ -390,7 +391,7 @@ static int vtobuf(dstr *v)
   ic = G_TOBUF(g, &b, x);
   c.len = BLEN(&b);
   if (ic != ir || (!ic && (c.len != v[3].len ||
-                          memcmp(c.buf, v[3].buf, c.len)))) {
+                          MEMCMP(c.buf, !=, v[3].buf, c.len)))) {
     ok = 0;
     fprintf(stderr, "*** tobuf failed\n");
     fprintf(stderr, "*** group: %s\n", v[0].buf);
@@ -455,7 +456,7 @@ static int vtoraw(dstr *v)
   ic = G_TORAW(g, &b, x);
   c.len = BLEN(&b);
   if (ic != ir || (!ic && (c.len != v[3].len ||
-                          memcmp(c.buf, v[3].buf, c.len)))) {
+                          MEMCMP(c.buf, !=, v[3].buf, c.len)))) {
     ok = 0;
     fprintf(stderr, "*** toraw failed\n");
     fprintf(stderr, "*** group: %s\n", v[0].buf);
index 470620e..048f4c6 100644 (file)
@@ -700,6 +700,8 @@ mp *mp_leastcongruent(mp *d, mp *b, mp *r, mp *m)
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
+
 static int verify(const char *op, mp *expect, mp *result, mp *a, mp *b)
 {
   if (!MP_EQ(expect, result)) {
@@ -787,11 +789,11 @@ MPX_DOBIN(DO)
   mp *r = *(mp **)v[3].buf;
   mp *c;
 
-  if (strcmp(v[0].buf, "and") == 0) op = 1;
-  else if (strcmp(v[0].buf, "or") == 0) op = 7;
-  else if (strcmp(v[0].buf, "nand") == 0) op = 14;
-  else if (strcmp(v[0].buf, "nor") == 0) op = 8;
-  else if (strcmp(v[0].buf, "xor") == 0) op = 6;
+  if (STRCMP(v[0].buf, ==, "and")) op = 1;
+  else if (STRCMP(v[0].buf, ==, "or")) op = 7;
+  else if (STRCMP(v[0].buf, ==, "nand")) op = 14;
+  else if (STRCMP(v[0].buf, ==, "nor")) op = 8;
+  else if (STRCMP(v[0].buf, ==, "xor")) op = 6;
   else {
     char *p = v[0].buf;
     while (*p) {
diff --git a/math/mp-nthrt.c b/math/mp-nthrt.c
new file mode 100644 (file)
index 0000000..b253d2f
--- /dev/null
@@ -0,0 +1,315 @@
+/* -*-c-*-
+ *
+ * Calculating %$n$%th roots
+ *
+ * (c) 2020 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 "mp.h"
+#include "mpint.h"
+#include "mplimits.h"
+#include "primeiter.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @mp_nthrt@ --- *
+ *
+ * Arguments:  @mp *d@ = fake destination
+ *             @mp *a@ = an integer
+ *             @mp *n@ = a strictly positive integer
+ *             @int *exectp_out@ = set nonzero if an exact solution is found
+ *
+ * Returns:    The integer %$\bigl\lfloor \sqrt[n]{a} \bigr\rfloor$%.
+ *
+ * Use:                Return an approximation to the %$n$%th root of %$a$%.
+ *             Specifically, it returns the largest integer %$x$% such that
+ *             %$x^n \le a$%.  If %$x^n = a$% then @*exactp_out@ is set
+ *             nonzero; otherwise it is set zero.  (If @exactp_out@ is null
+ *             then this information is discarded.)
+ *
+ *             The exponent %$n$% must be strictly positive: it's not clear
+ *             to me what the right answer is for %$n \le 0$%.  If %$a$% is
+ *             negative then %$n$% must be odd; otherwise there is no real
+ *             solution.
+ */
+
+mp *mp_nthrt(mp *d, mp *a, mp *n, int *exactp_out)
+{
+  /* We want to find %$x$% such that %$x^n = a$%.  Newton--Raphson says we
+   * start with a guess %$x_i$%, and refine it to a better guess %$x_{i+1}$%
+   * which is where the tangent to the curve %$y = x^n - a$% at %$x = x_i$%
+   * meets the %$x$%-axis.  The tangent meets the curve at
+   * %($x_i, x_i^n - a)$%, and has gradient %$n x_i^{n-1}$%, and hence meets
+   * the %$x$%-axis at %$x_{i+1} = x_i - (x_i^n - a)/(n x_i^{n-1})$%.
+   *
+   * We're working with plain integers here, and we actually want
+   * %$\lfloor x \rfloor$%.  We can check that we're close enough because
+   * %$x^n$% is monotonic for positive %$x$%: if %$x_i^n > a$% then we're too
+   * large; if %$(xi + i)^n \le a$% then we're too small; otherwise we're
+   * done.
+   */
+
+  mp *ai = MP_NEW, *bi = MP_NEW, *xi = MP_NEW,
+    *nm1 = MP_NEW, *delta = MP_NEW;
+  int cmp;
+  unsigned f = 0;
+#define f_neg 1u
+
+  /* Firstly, deal with a zero or negative %$xa%. */
+  if (!MP_NEGP(a))
+    MP_COPY(a);
+  else {
+    assert(MP_ODDP(n));
+    f |= f_neg;
+    a = mp_neg(MP_NEW, a);
+  }
+
+  /* Secondly, reject %$n \le 0$%.
+   *
+   * Clearly %$x^0 = 1$% for nonzero %$x$%, so if %$a$% is zero then we could
+   * return anything, but maybe %$1$% for concreteness; but what do we return
+   * for %$a \ne 1$%?  For %$n < 0$% there's just no hope.
+   */
+  assert(MP_POSP(n));
+
+  /* Pick a starting point.  This is rather important to get right.  In
+   * particular, for large %$n$%, if our initial guess is too small, then the
+   * next iteration is a wild overestimate and it takes a long time to
+   * converge back.
+   */
+  nm1 = mp_sub(nm1, n, MP_ONE);
+  xi = mp_fromulong(xi, mp_bits(a));
+  xi = mp_add(xi, xi, nm1); mp_div(&xi, 0, xi, n);
+  assert(MP_CMP(xi, <=, MP_ULONG_MAX));
+  xi = mp_setbit(xi, MP_ZERO, mp_toulong(xi));
+
+  /* The main iteration. */
+  for (;;) {
+    ai = mp_exp(ai, xi, n);
+    bi = mp_add(bi, xi, MP_ONE); bi = mp_exp(bi, bi, n);
+    cmp = mp_cmp(ai, a);
+    if (cmp <= 0 && MP_CMP(a, <, bi)) break;
+    ai = mp_sub(ai, ai, a);
+    bi = mp_exp(bi, xi, nm1); bi = mp_mul(bi, bi, n);
+    mp_div(&delta, 0, ai, bi);
+    if (MP_ZEROP(delta) && cmp > 0) xi = mp_sub(xi, xi, MP_ONE);
+    else xi = mp_sub(xi, xi, delta);
+  }
+
+  /* Final sorting out of negative inputs.
+   *
+   * If the input %$a$% is not an exact %$n$%th root, then we must round
+   * %%\emph{up}%% so that, after negation, we implement the correct floor
+   * behaviour.
+   */
+  if (f&f_neg) {
+    if (cmp) xi = mp_add(xi, xi, MP_ONE);
+    xi = mp_neg(xi, xi);
+  }
+
+  /* Free up the temporary things. */
+  MP_DROP(a); MP_DROP(ai); MP_DROP(bi);
+  MP_DROP(nm1); MP_DROP(delta); mp_drop(d);
+
+  /* Done. */
+  if (exactp_out) *exactp_out = (cmp == 0);
+  return (xi);
+
+#undef f_neg
+}
+
+/* --- @mp_perfect_power_p@ --- *
+ *
+ * Arguments:  @mp **x@ = where to write the base
+ *             @mp **n@ = where to write the exponent
+ *             @mp *a@ = an integer
+ *
+ * Returns:    Nonzero if %$a$% is a perfect power.
+ *
+ * Use:                Returns whether an integer %$a$% is a perfect power, i.e.,
+ *             whether it can be written in the form %$a = x^n$% where
+ *             %$|x| > 1$% and %$n > 1$% are integers.  If this is possible,
+ *             then (a) store %$x$% and the largest such %$n$% in @*x@ and
+ *             @*n@, and return nonzero; otherwise, store %$x = a$% and
+ *             %$n = 1$% and return zero.  (Either @x@ or @n@, or both, may
+ *             be null to discard these outputs.)
+ *
+ *             Note that %$-1$%, %$0$% and %$1$% are not considered perfect
+ *             powers by this definition.  (The exponent is not well-defined
+ *             in these cases, but it seemed better to implement a function
+ *             which worked for all integers.)  Note also that %$-4$% is not
+ *             a perfect power since it has no real square root.
+ */
+
+int mp_perfect_power_p(mp **x, mp **n, mp *a)
+{
+  mp *r = MP_ONE, *p = MP_NEW, *t = MP_NEW;
+  int exactp, rc = 0;
+  primeiter pi;
+  unsigned f = 0;
+#define f_neg 1u
+
+  /* Initial setup.  We'll modify @a@ as we go, so we have to destroy it when
+   * we're done; therefore we should take a copy now.
+   */
+  MP_COPY(a);
+
+  /* According to the definition above, @0@ is %%\emph{not}%% a perfect
+   * power.  Dispose of this special case early.
+   */
+  if (MP_ZEROP(a)) goto done;
+
+  /* Start by initializing a prime iterator.  If %$a$% is negative, then it
+   * can't be an even power so start iterating at 3; otherwise we must start
+   * with 2.
+   */
+  primeiter_create(&pi, MP_NEGP(a) ? MP_THREE : MP_TWO);
+  if (MP_NEGP(a)) { a = mp_neg(a, a); f |= f_neg; }
+
+  /* Prime the pump for the main loop. */
+  p = primeiter_next(&pi, p);
+
+  /* Here we go. */
+  for (;;) {
+
+    /* See if %$a$% is a perfect %$p$%th power. */
+    t = mp_nthrt(t, a, p, &exactp);
+    if (MP_EQ(t, MP_ONE))
+      /* No, and moreover %$2^p > a$%.  We won't get anywhere by examining
+       * larger primes, so stop here.
+       */
+      break;
+
+    else if (!exactp)
+      /* No.  Oh, well: let's try the next prime, then. */
+
+      p = primeiter_next(&pi, p);
+    else {
+      /* Yes!  We've found that %$a = a'^p$% for some %$a'$%.  We know
+       * (induction) that %$a'$% is not a perfect %$q$%th power for any prime
+       * %$q < p$%, so we can write %$a = x^n$% if and only if we can write
+       * %$a' = x'^{n'}$%, in which case we have %$a = a'^p = x'^{p n'}$%.
+       */
+      r = mp_mul(r, r, p);
+      MP_DROP(a); a = t; t = MP_NEW;
+      rc = 1;
+    }
+  }
+
+  /* We're all done. */
+  primeiter_destroy(&pi);
+
+done:
+  /* Return the resulting decomposition. */
+  if (x) { mp_drop(*x); *x = f&f_neg ? mp_neg(a, a) : a; a = 0; }
+  if (n) { mp_drop(*n); *n = r; r = 0; }
+
+  /* Clean everything up. */
+  mp_drop(a); mp_drop(p); mp_drop(r); mp_drop(t);
+
+  /* All done. */
+  return (rc);
+
+#undef f_neg
+}
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <mLib/testrig.h>
+
+static int vrf_nthrt(dstr *v)
+{
+  mp *a = *(mp **)v[0].buf;
+  mp *n = *(mp **)v[1].buf;
+  mp *r0 = *(mp **)v[2].buf;
+  int exactp0 = *(int *)v[3].buf, exactp;
+  mp *r = mp_nthrt(MP_NEW, a, n, &exactp);
+  int ok = 1;
+
+  if (!MP_EQ(r, r0) || exactp != exactp0) {
+    ok = 0;
+    fputs("\n*** nthrt failed", stderr);
+    fputs("\n***       a = ", stderr); mp_writefile(a, stderr, 10);
+    fputs("\n***       n = ", stderr); mp_writefile(n, stderr, 10);
+    fputs("\n***  r calc = ", stderr); mp_writefile(r, stderr, 10);
+    fprintf(stderr, "\n*** ex calc = %d", exactp);
+    fputs("\n***   r exp = ", stderr); mp_writefile(r0, stderr, 10);
+    fprintf(stderr, "\n***  ex exp = %d", exactp0);
+    fputc('\n', stderr);
+  }
+
+  mp_drop(a); mp_drop(n); mp_drop(r); mp_drop(r0);
+  assert(mparena_count(MPARENA_GLOBAL) == 0);
+
+  return (ok);
+}
+
+static int vrf_ppp(dstr *v)
+{
+  mp *a = *(mp **)v[0].buf;
+  int ret0 = *(int *)v[1].buf;
+  mp *x0 = *(mp **)v[2].buf;
+  mp *n0 = *(mp **)v[3].buf;
+  mp *x = MP_NEW, *n = MP_NEW;
+  int ret = mp_perfect_power_p(&x, &n, a);
+  int ok = 1;
+
+  if (ret != ret0 || !MP_EQ(x, x0) || !MP_EQ(n, n0)) {
+    ok = 0;
+    fputs("\n*** perfect_power_p failed", stderr);
+    fputs("\n***       a = ", stderr); mp_writefile(a, stderr, 10);
+    fprintf(stderr, "\n***  r calc = %d", ret);
+    fputs("\n***  x calc = ", stderr); mp_writefile(x, stderr, 10);
+    fputs("\n***  n calc = ", stderr); mp_writefile(n, stderr, 10);
+    fprintf(stderr, "\n***   r exp = %d", ret0);
+    fputs("\n***   x exp = ", stderr); mp_writefile(x0, stderr, 10);
+    fputs("\n***   n exp = ", stderr); mp_writefile(n0, stderr, 10);
+    fputc('\n', stderr);
+  }
+
+  mp_drop(a); mp_drop(x); mp_drop(n); mp_drop(x0); mp_drop(n0);
+  assert(mparena_count(MPARENA_GLOBAL) == 0);
+
+  return (ok);
+}
+
+static test_chunk tests[] = {
+  { "nthrt", vrf_nthrt, { &type_mp, &type_mp, &type_mp, &type_int, 0 } },
+  { "perfect-power-p", vrf_ppp, { &type_mp, &type_int, &type_mp, &type_mp, 0 } },
+  { 0, 0, { 0 } },
+};
+
+int main(int argc, char *argv[])
+{
+  sub_init();
+  test_run(argc, argv, tests, SRCDIR "/t/mp");
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
index 0434c28..23e71e3 100644 (file)
--- a/math/mp.h
+++ b/math/mp.h
@@ -923,6 +923,54 @@ extern mp *mp_leastcongruent(mp */*d*/, mp */*b*/, mp */*r*/, mp */*m*/);
 
 extern mp *mp_sqrt(mp */*d*/, mp */*a*/);
 
+/* --- @mp_nthrt@ --- *
+ *
+ * Arguments:  @mp *d@ = fake destination
+ *             @mp *a@ = an integer
+ *             @mp *n@ = a strictly positive integer
+ *             @int *exectp_out@ = set nonzero if an exact solution is found
+ *
+ * Returns:    The integer %$\bigl\lfloor \sqrt[n]{a} \bigr\rfloor$%.
+ *
+ * Use:                Return an approximation to the %$n$%th root of %$a$%.
+ *             Specifically, it returns the largest integer %$x$% such that
+ *             %$x^n \le a$%.  If %$x^n = a$% then @*exactp_out@ is set
+ *             nonzero; otherwise it is set zero.  (If @exactp_out@ is null
+ *             then this information is discarded.)
+ *
+ *             The exponent %$n$% must be strictly positive: it's not clear
+ *             to me what the right answer is for %$n \le 0$%.  If %$a$% is
+ *             negative then %$n$% must be odd; otherwise there is no real
+ *             solution.
+ */
+
+extern mp *mp_nthrt(mp */*d*/, mp */*a*/, mp */*n*/, int */*exactp_out*/);
+
+/* --- @mp_perfect_power_p@ --- *
+ *
+ * Arguments:  @mp **x@ = where to write the base
+ *             @mp **n@ = where to write the exponent
+ *             @mp *a@ = an integer
+ *
+ * Returns:    Nonzero if %$a$% is a perfect power.
+ *
+ * Use:                Returns whether an integer %$a$% is a perfect power, i.e.,
+ *             whether it can be written in the form %$a = x^n$% where
+ *             %$|x| > 1$% and %$n > 1$% are integers.  If this is possible,
+ *             then (a) store %$x$% and the largest such %$n$% in @*x@ and
+ *             @*n@, and return nonzero; otherwise, store %$x = a$% and
+ *             %$n = 1$% and return zero.  (Either @x@ or @n@, or both, may
+ *             be null to discard these outputs.)
+ *
+ *             Note that %$-1$%, %$0$% and %$1$% are not considered perfect
+ *             powers by this definition.  (The exponent is not well-defined
+ *             in these cases, but it seemed better to implement a function
+ *             which worked for all integers.)  Note also that %$-4$% is not
+ *             a perfect power since it has no real square root.
+ */
+
+extern int mp_perfect_power_p(mp **/*x*/, mp **/*n*/, mp */*a*/);
+
 /* --- @mp_gcd@ --- *
  *
  * Arguments:  @mp **gcd, **xx, **yy@ = where to write the results
index 7d11bfd..c7dbf88 100644 (file)
@@ -28,10 +28,9 @@ from __future__ import with_statement
 
 import re as RX
 import optparse as OP
+import sys as SYS; stdout = SYS.stdout
 import types as TY
 
-from sys import stdout
-
 ###--------------------------------------------------------------------------
 ### Random utilities.
 
@@ -65,6 +64,22 @@ def fix_name(name):
   """Replace non-alphanumeric characters in NAME with underscores."""
   return R_IDBAD.sub('_', name)
 
+if SYS.version_info >= (3,):
+  def func_name(func): return func.__name__
+  xrange = range
+  long = int
+else:
+  def func_name(func): return func.func_name
+
+def with_metaclass(meta, *supers):
+  return meta('#<anonymous base %s>' % meta.__name__,
+              supers or (object,), dict())
+
+def load(file, mod):
+  with open(file) as f: text = f.read()
+  code = compile(text, file, 'exec')
+  exec(code, mod)
+
 ###--------------------------------------------------------------------------
 ### Determining the appropriate types.
 
@@ -83,7 +98,7 @@ class IntClass (type):
     except AttributeError: pass
     return c
 
-class BasicIntType (object):
+class BasicIntType (with_metaclass(IntClass)):
   """
   A base class for integer-type classes, providing defaults and protocol.
 
@@ -195,7 +210,7 @@ class TypeChoice (object):
 
     ## Load the captured type information.
     me.ti = TY.ModuleType('typeinfo')
-    execfile(opts.typeinfo, me.ti.__dict__)
+    load(opts.typeinfo, me.ti.__dict__)
 
     ## Build a map of the available types.
     tymap = {}
@@ -226,14 +241,14 @@ class TypeChoice (object):
 
     ## Make sure we've not ended up somewhere really silly.
     if mpwbits < 16:
-      raise Exception, "`mpw' type is too small: your C environment is weird"
+      raise Exception("`mpw' type is too small: your C environment is weird")
 
     ## Now figure out suitable types for `mpw' and `mpd'.
     def find_type(bits, what):
       for ty in byrank:
         if ty.bits >= bits: return ty
-      raise Exception, \
-          "failed to find suitable %d-bit type, for %s" % (bits, what)
+      raise Exception \
+        ("failed to find suitable %d-bit type, for %s" % (bits, what))
 
     ## Store our decisions.
     me.mpwbits = mpwbits
@@ -327,7 +342,7 @@ def defmode(func):
   arguments from the command and are expected to write their output to
   stdout.
   """
-  name = func.func_name
+  name = func_name(func)
   if name.startswith('m_'): name = name[2:]
   MODEMAP[name] = func
   return func
@@ -483,7 +498,7 @@ class GroupTableClass (type):
     else: MODEMAP[c.mode] = c.run
     return c
 
-class GroupTable (object):
+class GroupTable (with_metaclass(GroupTableClass)):
   """
   Base class for group tables objects.
 
@@ -568,8 +583,6 @@ class GroupTable (object):
   ## _slotmap           A dictionary mapping slot names to their
   ##                    descriptions.
 
-  __metaclass__ = GroupTableClass
-
   ## Default values.
   keyword = 'group'
   slots = []
@@ -644,7 +657,7 @@ class GroupTable (object):
 
         if ff[0] == 'alias':
           ## An alias.  Just remember this.
-          if len(ff) != 3: raise Exception, "wrong number of alias arguments"
+          if len(ff) != 3: raise Exception("wrong number of alias arguments")
           me._flush()
           me._names.append((ff[1], ff[2]))
 
@@ -654,7 +667,7 @@ class GroupTable (object):
           ## Check the headline syntax.  Headline slots may be set here, or
           ## later by name.
           if len(ff) < 2 or len(ff) > 2 + len(me._headslots):
-            raise Exception, "bad number of headline arguments"
+            raise Exception("bad number of headline arguments")
 
           ## Flush out the previous stanza.
           me._flush()
@@ -670,14 +683,14 @@ class GroupTable (object):
         elif ff[0] in me._slotmap:
           ## A slot assignment.  Get the slot to store a value.
           if me.st.name is None:
-            raise Exception, "no group currently being defined"
+            raise Exception("no group currently being defined")
           if len(ff) != 2:
-            raise Exception, "bad number of values for slot `%s'" % ff[0]
+            raise Exception("bad number of values for slot `%s'" % ff[0])
           me._slotmap[ff[0]].set(me.st, ff[1])
 
         else:
           ## Something incomprehensible.
-          raise Exception, "unknown keyword `%s'" % ff[0]
+          raise Exception("unknown keyword `%s'" % ff[0])
 
     ## End of the input.  Write out the final stanza.
     me._flush()
@@ -687,7 +700,7 @@ class GroupTable (object):
     stdout.write("\nconst %s %s[] = {\n" % (me.entry_t, me.tabname))
     for a, n in me._names:
       if n not in me._defs:
-        raise Exception, "alias `%s' refers to unknown group `%s'" % (a, n)
+        raise Exception("alias `%s' refers to unknown group `%s'" % (a, n))
       stdout.write('  { "%s", &c_%s },\n' % (a, fix_name(n)))
     stdout.write("  { 0, 0 }\n};\n\n")
 
@@ -747,7 +760,7 @@ class BaseSlot (object):
     Store a VALUE for the slot.
     """
     if me._allowp and not me._allowp(st, value):
-      raise Exception, "slot `%s' not allowed here" % me.name
+      raise Exception("slot `%s' not allowed here" % me.name)
     st.d[me] = value
 
   def setup(me, st):
@@ -755,7 +768,7 @@ class BaseSlot (object):
     Prepare the slot for output, checking its value and so on.
     """
     if me not in st.d and (not me._omitp or not me._omitp(st)):
-      raise Exception, "missing slot `%s'" % me.name
+      raise Exception("missing slot `%s'" % me.name)
 
 class EnumSlot (BaseSlot):
   """
@@ -781,7 +794,7 @@ class EnumSlot (BaseSlot):
     Check that the VALUE is one of the ones we know.
     """
     if value not in me._values:
-      raise Exception, "invalid %s value `%s'" % (me.name, value)
+      raise Exception("invalid %s value `%s'" % (me.name, value))
     super(EnumSlot, me).set(st, value)
 
   def write(me, st):
index 3511f1e..971edab 100644 (file)
@@ -57,6 +57,7 @@ MPINT_CONVERSIONS(TO)
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
 #include <mLib/testrig.h>
 
 static int fromuint(dstr *v)
index 6b5c8c4..ff15b2d 100644 (file)
@@ -93,6 +93,10 @@ mp *mpmont_exp(const mpmont *mm, mp *d, mp *a, mp *e)
 
 #ifdef TEST_RIG
 
+#ifdef ENABLE_ASM_DEBUG
+#  include "regdump.h"
+#endif
+
 static int texp(dstr *v)
 {
   mp *m = *(mp **)v[0].buf;
@@ -136,6 +140,9 @@ static test_chunk tests[] = {
 int main(int argc, char *argv[])
 {
   sub_init();
+#ifdef ENABLE_ASM_DEBUG
+  regdump_init();
+#endif
   test_run(argc, argv, tests, SRCDIR "/t/mpmont");
   return (0);
 }
index 094ac40..b4bf982 100644 (file)
@@ -98,6 +98,14 @@ static void simple_redccore(mpw *dv, mpw *dvl, const mpw *mv,
   MAYBE_REDC4(amd64_avx)
 #endif
 
+#if CPUFAM_ARMEL
+  MAYBE_REDC4(arm_neon)
+#endif
+
+#if CPUFAM_ARM64
+  MAYBE_REDC4(arm64_simd)
+#endif
+
 static redccore__functype *pick_redccore(void)
 {
 #if CPUFAM_X86
@@ -112,10 +120,18 @@ static redccore__functype *pick_redccore(void)
   DISPATCH_PICK_COND(mpmont_reduce, maybe_redc4_amd64_sse2,
                     cpu_feature_p(CPUFEAT_X86_SSE2));
 #endif
+#if CPUFAM_ARMEL
+  DISPATCH_PICK_COND(mpmont_reduce, maybe_redc4_arm_neon,
+                    cpu_feature_p(CPUFEAT_ARM_NEON));
+#endif
+#if CPUFAM_ARM64
+  DISPATCH_PICK_COND(mpmont_reduce, maybe_redc4_arm64_simd,
+                    cpu_feature_p(CPUFEAT_ARM_NEON));
+#endif
   DISPATCH_PICK_FALLBACK(mpmont_reduce, simple_redccore);
 }
 
-/* --- @redccore@ --- *
+/* --- @mulcore@ --- *
  *
  * Arguments:  @mpw *dv, *dvl@ = base and limit of source/destination
  *             @const mpw *av, *avl@ = base and limit of first multiplicand
@@ -204,6 +220,14 @@ static void simple_mulcore(mpw *dv, mpw *dvl,
   MAYBE_MUL4(amd64_avx)
 #endif
 
+#if CPUFAM_ARMEL
+  MAYBE_MUL4(arm_neon)
+#endif
+
+#if CPUFAM_ARM64
+  MAYBE_MUL4(arm64_simd)
+#endif
+
 static mulcore__functype *pick_mulcore(void)
 {
 #if CPUFAM_X86
@@ -218,6 +242,14 @@ static mulcore__functype *pick_mulcore(void)
   DISPATCH_PICK_COND(mpmont_mul, maybe_mul4_amd64_sse2,
                     cpu_feature_p(CPUFEAT_X86_SSE2));
 #endif
+#if CPUFAM_ARMEL
+  DISPATCH_PICK_COND(mpmont_mul, maybe_mul4_arm_neon,
+                    cpu_feature_p(CPUFEAT_ARM_NEON));
+#endif
+#if CPUFAM_ARM64
+  DISPATCH_PICK_COND(mpmont_mul, maybe_mul4_arm64_simd,
+                    cpu_feature_p(CPUFEAT_ARM_NEON));
+#endif
   DISPATCH_PICK_FALLBACK(mpmont_mul, simple_mulcore);
 }
 
@@ -444,6 +476,10 @@ mp *mpmont_mul(const mpmont *mm, mp *d, mp *a, mp *b)
 
 #ifdef TEST_RIG
 
+#ifdef ENABLE_ASM_DEBUG
+#  include "regdump.h"
+#endif
+
 static int tcreate(dstr *v)
 {
   mp *m = *(mp **)v[0].buf;
@@ -539,7 +575,6 @@ static int tmul(dstr *v)
     mp_drop(mr);
   }
 
-
   MP_DROP(m);
   MP_DROP(a);
   MP_DROP(b);
@@ -558,6 +593,9 @@ static test_chunk tests[] = {
 int main(int argc, char *argv[])
 {
   sub_init();
+#ifdef ENABLE_ASM_DEBUG
+  regdump_init();
+#endif
   test_run(argc, argv, tests, SRCDIR "/t/mpmont");
   return (0);
 }
index c902264..a5c77db 100644 (file)
@@ -31,6 +31,8 @@
 #include <limits.h>
 #include <stdio.h>
 
+#include <mLib/macros.h>
+
 #include "mp.h"
 #include "mptext.h"
 #include "paranoia.h"
@@ -340,10 +342,10 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
   /* --- If we're reading text, skip leading space, and maybe a sign --- */
 
   if (radix >= 0) {
-    while (isspace(ch)) ch = ops->get(p);
+    while (ISSPACE(ch)) ch = ops->get(p);
     switch (ch) {
       case '-': f |= f_neg; /* and on */
-      case '+': do ch = ops->get(p); while (isspace(ch));
+      case '+': do ch = ops->get(p); while (ISSPACE(ch));
     }
   }
 
@@ -705,7 +707,7 @@ static int verify(dstr *v)
       ok = 0;
     } else {
       mp_writedstr(m, &d, ob);
-      if (d.len != v[3].len || memcmp(d.buf, v[3].buf, d.len) != 0) {
+      if (d.len != v[3].len || MEMCMP(d.buf, !=, v[3].buf, d.len)) {
        fprintf(stderr, "*** failed read or write\n"
                        "*** input [%2i]      = ", ib);
        if (ib < 0)
@@ -746,7 +748,7 @@ static int verify(dstr *v)
   }
 
   if (v[1].len - off != v[4].len ||
-      memcmp(v[1].buf + off, v[4].buf, v[4].len) != 0) {
+      MEMCMP(v[1].buf + off, !=, v[4].buf, v[4].len)) {
     fprintf(stderr, "*** leftovers incorrect\n"
                    "*** input [%2i]    = ", ib);
     if (ib < 0)
index d313765..5a748c6 100644 (file)
 /// construct more general variable-length multipliers.
 ///
 /// The basic trick is the same throughout.  In an operand-scanning
-/// multiplication, the inner multiplication loop multiplies a
-/// multiple-precision operand by a single precision factor, and adds the
-/// result, appropriately shifted, to the result.  A `finely integrated
-/// operand scanning' implementation of Montgomery multiplication also adds
-/// the product of a single-precision `Montgomery factor' and the modulus,
+/// multiplication, the inner multiplication loop multiplies a multiple-
+/// precision operand by a single precision factor, and adds the result,
+/// appropriately shifted, to the result.  A `finely integrated operand
+/// scanning' implementation of Montgomery multiplication also adds the
+/// product of a single-precision `Montgomery factor' and the modulus,
 /// calculated in the same pass.  The more common `coarsely integrated
 /// operand scanning' alternates main multiplication and Montgomery passes,
 /// which requires additional carry propagation.
 /// many products together before we must deal with carrying; it also allows
 /// for some calculations to be performed on the above expanded form.
 ///
-/// ...
+/// We maintain four `carry' registers XMM12--XMM15 accumulating intermediate
+/// results.  The registers' precise roles rotate during the computation; we
+/// name them `c0', `c1', `c2', and `c3'.  Each carry register holds two
+/// 64-bit halves: the register c0, for example, holds c'_0 (low half) and
+/// c''_0 (high half), and represents the value c_0 = c'_0 + c''_0 b; the
+/// carry registers collectively represent the value c_0 + c_1 B + c_2 B^2 +
+/// c_3 B^3.  The `pmuluqdq' instruction acting on a scalar operand
+/// (broadcast across all lanes of its vector) and an operand in the expanded
+/// form above produces a result which can be added directly to the
+/// appropriate carry register.  Following a pass of four multiplications, we
+/// perform some limited carry propagation: let t = c''_0 mod B, and let d =
+/// c'_0 + t b; then we output z = d mod B, add (floor(d/B), floor(c''_0/B))
+/// to c1, and cycle the carry registers around, so that c1 becomes c0, and
+/// the old (implicitly) zeroed c0 becomes c3.
 ///
-/// We maintain four `carry' registers accumulating intermediate results.
-/// The registers' precise roles rotate during the computation; we name them
-/// `c0', `c1', `c2', and `c3'.  Each carry register holds two 64-bit halves:
-/// the register c0, for example, holds c'_0 (low half) and c''_0 (high
-/// half), and represents the value c_0 = c'_0 + c''_0 b; the carry registers
-/// collectively represent the value c_0 + c_1 B + c_2 B^2 + c_3 B^3.  The
-/// `pmuluqdq' instruction acting on a scalar operand (broadcast across all
-/// lanes of its vector) and an operand in the expanded form above produces a
-/// result which can be added directly to the appropriate carry register.
-/// Following a pass of four multiplications, we perform some limited carry
-/// propagation: let t = c''_0 mod B, and let d = c'_0 + t b; then we output
-/// z = d mod B, add (floor(d/B), floor(c''_0/B)) to c1, and cycle the carry
-/// registers around, so that c1 becomes c0, and the old c0 is (implicitly)
-/// zeroed becomes c3.
+/// On 64-bit AMD64, we have a reasonable number of registers: the expanded
+/// operands are kept in registers.  The packed operands are read from memory
+/// into working registers XMM4 and XMM5; XMM0--XMM3 are used for the actual
+/// multiplications; and XMM6 and XMM7 are used for combining the results.
+/// The following conventional argument names and locations are used
+/// throughout.
+///
+/// Arg        Format      Location    Notes
+///
+/// U  packed      [RAX]
+/// X  packed      [RBX]       In Montgomery multiplication, X = N
+/// V  expanded    XMM8/XMM9
+/// Y  expanded    XMM10/XMM11 In Montgomery multiplication, Y = (A + U V) M
+/// M  expanded    (see below) Montgomery factor, M = -N^{-1} (mod B^4)
+/// N                          Modulus, for Montgomery multiplication
+/// A  packed      [RDI]       Destination/accumulator
+/// C  carry       XMM12--XMM15
+///
+/// The calculation is some variant of
+///
+///    A' + C' B^4 <- U V + X Y + A + C
+///
+/// The low-level functions fit into a fairly traditional (finely-integrated)
+/// operand scanning loop over operand pairs (U, X) (indexed by j) and (V, Y)
+/// (indexed by i).
+///
+/// The variants are as follows.
+///
+/// Function   Variant                 Use             i       j
+///
+/// mmul4      A = C = 0, Y = M        Montgomery      0       0
+/// dmul4      A = 0                   Montgomery      0       +
+/// mmla4      C = 0, Y = M            Montgomery      +       0
+/// dmla4      exactly as shown        Montgomery      +       +
+/// mont4      U = C = 0, V = M        Montgomery      any     0
+///
+/// mul4zc     U = V = A = C = 0       Plain           0       0
+/// mul4       U = V = A = 0           Plain           0       +
+/// mla4zc     U = V = C = 0           Plain           +       0
+/// mla4       U = V = 0               Plain           +       +
+///
+/// The `mmul4' and `mmla4' functions are also responsible for calculating
+/// the Montgomery reduction factor Y = (A + U V) M used by the rest of the
+/// inner loop.
 
 ///--------------------------------------------------------------------------
 /// Macro definitions.
@@ -319,7 +362,6 @@ INTFUNC(carryprop)
        movdqu  [rdi], xmm0
 
        ret
-
 ENDFUNC
 
 INTFUNC(dmul4)
@@ -357,7 +399,6 @@ INTFUNC(dmul4)
        movdqu  [rdi], xmm6
 
        ret
-
 ENDFUNC
 
 INTFUNC(dmla4)
@@ -398,7 +439,6 @@ INTFUNC(dmla4)
        movdqu  [rdi], xmm6
 
        ret
-
 ENDFUNC
 
 INTFUNC(mul4zc)
@@ -429,7 +469,6 @@ INTFUNC(mul4zc)
        movdqu  [rdi], xmm6
 
        ret
-
 ENDFUNC
 
 INTFUNC(mul4)
@@ -462,7 +501,6 @@ INTFUNC(mul4)
        movdqu  [rdi], xmm6
 
        ret
-
 ENDFUNC
 
 INTFUNC(mla4zc)
@@ -498,7 +536,6 @@ INTFUNC(mla4zc)
        movdqu  [rdi], xmm6
 
        ret
-
 ENDFUNC
 
 INTFUNC(mla4)
@@ -533,14 +570,13 @@ INTFUNC(mla4)
        movdqu  [rdi], xmm6
 
        ret
-
 ENDFUNC
 
 INTFUNC(mmul4)
        // On entry, RDI points to the destination buffer; RAX and RBX point
        // to the packed operands U and N; and XMM8/XMM9 and XMM10/XMM11 hold
-       // the expanded operands V and M.  The stack pointer must be 8 modulo 16
-       // (as usual for AMD64 ABIs).
+       // the expanded operands V and M.  The stack pointer must be 8 modulo
+       // 16 (as usual for AMD64 ABIs).
        //
        // On exit, we store Y = U V M mod B in XMM10/XMM11, and write the
        // low 128 bits of the sum U V + N Y to [RDI], leaving the remaining
@@ -557,7 +593,6 @@ INTFUNC(mmul4)
        mulcore xmm4, 0,   xmm8,  xmm9,  xmm12, xmm13, xmm14, xmm15
        propout xmm7, lo,                xmm12, xmm13
        jmp     5f
-
 ENDFUNC
 
 INTFUNC(mmla4)
@@ -575,10 +610,10 @@ INTFUNC(mmla4)
        movdqu  xmm4, [rax]
 #if ABI_WIN
        stalloc 48 + 8                  // space for the carries
-#  define STKTMP(i) [rsp + i]
+#  define STKTMP(i) [SP + i]
 #endif
 #if ABI_SYSV
-#  define STKTMP(i) [rsp + i - 48 - 8] // use red zone
+#  define STKTMP(i) [SP + i - 48 - 8]  // use red zone
 #endif
   endprologue
 
@@ -744,7 +779,6 @@ INTFUNC(mont4)
        // And, with that, we're done.
        movdqu  [rdi], xmm6
        ret
-
 ENDFUNC
 
 ///--------------------------------------------------------------------------
@@ -759,7 +793,7 @@ ENDFUNC
 
 FUNC(mpx_umul4_amd64_sse2)
        // void mpx_umul4_amd64_sse2(mpw *dv, const mpw *av, const mpw *avl,
-       //                         const mpw *bv, const mpw *bvl);
+       //                           const mpw *bv, const mpw *bvl);
 
        // Establish the arguments and do initial setup.
        //
@@ -783,7 +817,6 @@ FUNC(mpx_umul4_amd64_sse2)
   endprologue
 
        mov     DV, rdi
-
 #endif
 
 #if ABI_WIN
@@ -811,8 +844,7 @@ FUNC(mpx_umul4_amd64_sse2)
   endprologue
 
        mov     rdi, DV
-       mov     BVL, [rsp + 224]
-
+       mov     BVL, [SP + 224]
 #endif
 
        // Prepare for the first iteration.
@@ -878,7 +910,6 @@ FUNC(mpx_umul4_amd64_sse2)
 #endif
 
 #if ABI_WIN
-
        rstrxmm xmm6,    0
        rstrxmm xmm7,   16
        rstrxmm xmm8,   32
@@ -893,7 +924,6 @@ FUNC(mpx_umul4_amd64_sse2)
        stfree  160 + 8
        popreg  rdi
        popreg  rbx
-
 #endif
 
        ret
@@ -946,7 +976,6 @@ FUNC(mpxmont_mul4_amd64_sse2)
   endprologue
 
        mov     DV, rdi
-
 #endif
 
 #if ABI_WIN
@@ -978,9 +1007,8 @@ FUNC(mpxmont_mul4_amd64_sse2)
   endprologue
 
        mov     rdi, DV
-       mov     N, [rsp + 224]
-       mov     MI, [rsp + 232]
-
+       mov     N, [SP + 224]
+       mov     MI, [SP + 232]
 #endif
 
        // Establish the expanded operands.
@@ -1062,7 +1090,6 @@ FUNC(mpxmont_mul4_amd64_sse2)
 #endif
 
 #if ABI_WIN
-
        rstrxmm xmm6,    0
        rstrxmm xmm7,   16
        rstrxmm xmm8,   32
@@ -1078,7 +1105,6 @@ FUNC(mpxmont_mul4_amd64_sse2)
        popreg  r12
        popreg  rdi
        popreg  rbx
-
 #endif
 
        ret
@@ -1129,12 +1155,11 @@ FUNC(mpxmont_redc4_amd64_sse2)
        // outer loop dv        r10     rcx
        // outer loop dv limit  r11     r11
        // nv base              rdx     r8
-       // nv limit             r9      r12*
+       // nv limit             r9      r10*
        // n                    rcx     r9
        // c                    rcx     r9
 
 #if ABI_SYSV
-
 #  define DVL rax
 #  define DVL4 rsi
 #  define MI r8
@@ -1149,25 +1174,22 @@ FUNC(mpxmont_redc4_amd64_sse2)
   endprologue
 
        mov     DV, rdi
-
 #endif
 
 #if ABI_WIN
-
 #  define DVL rax
 #  define DVL4 rdx
 #  define MI r10
 #  define DV rcx
 #  define DVLO r11
 #  define NV r8
-#  define NVL r12
+#  define NVL r10
 #  define N r9
 #  define C r9d
 
        pushreg rbx
        pushreg rdi
-       pushreg r12
-       stalloc 160
+       stalloc 168
 
        savexmm xmm6,    0
        savexmm xmm7,   16
@@ -1183,8 +1205,7 @@ FUNC(mpxmont_redc4_amd64_sse2)
   endprologue
 
        mov     rdi, DV
-       mov     MI, [rsp + 224]
-
+       mov     MI, [SP + 224]
 #endif
 
        // Establish the expanded operands and the blocks-of-4 dv limit.
@@ -1230,29 +1251,29 @@ FUNC(mpxmont_redc4_amd64_sse2)
        // Continue carry propagation until the end of the buffer.
 0:     add     [rdi], C
        mov     C, 0                    // preserves flags
-       adcd    [rdi + 4], 0
-       adcd    [rdi + 8], 0
-       adcd    [rdi + 12], 0
+       adc     dword ptr [rdi + 4], 0
+       adc     dword ptr [rdi + 8], 0
+       adc     dword ptr [rdi + 12], 0
        adc     C, 0
        add     rdi, 16
        cmp     rdi, DVL4
        jb      0b
 
-       // Deal with the tail end.
+       // Deal with the tail end.  Note that the actual destination length
+       // won't be an exacty number of blocks of four, so it's safe to just
+       // drop through here.
 7:     add     [rdi], C
-       mov     C, 0                    // preserves flags
+       mov     C, 0
        add     rdi, 4
        adc     C, 0
        cmp     rdi, DVL
        jb      7b
 
-       // All done for this iteration.  Start the next.  (This must have at
-       // least one follow-on iteration, or we'd not have started this outer
-       // loop.)
-8:     mov     rdi, DV                 // -> Z = dv[i]
-       mov     rbx, NV                 // -> X = nv[0]
-       cmp     rdi, DVLO               // all done yet?
+       // All done for this iteration.  Start the next.
+       cmp     DV, DVLO                // all done yet?
        jae     9f
+       mov     rdi, DV                 // -> Z = dv[i]
+       mov     rbx, NV                 // -> X = nv[0]
        add     DV, 16
        call    mont4
        add     rdi, 16
@@ -1267,7 +1288,6 @@ FUNC(mpxmont_redc4_amd64_sse2)
 #endif
 
 #if ABI_WIN
-
        rstrxmm xmm6,    0
        rstrxmm xmm7,   16
        rstrxmm xmm8,   32
@@ -1279,11 +1299,9 @@ FUNC(mpxmont_redc4_amd64_sse2)
        rstrxmm xmm14, 128
        rstrxmm xmm15, 144
 
-       stfree  160
-       popreg  r12
+       stfree  168
        popreg  rdi
        popreg  rbx
-
 #endif
 
        ret
@@ -1329,7 +1347,7 @@ ENDFUNC
 #  define ARG8 STKARG(4)
 #  define STKARG_OFFSET 224
 #endif
-#define STKARG(i) [rsp + STKARG_OFFSET + 8*(i)]
+#define STKARG(i) [SP + STKARG_OFFSET + 8*(i)]
 
 //               sysv                          win
 //               dmul  smul  mmul  mont        dmul  smul  mmul  mont
@@ -1583,6 +1601,8 @@ FUNC(test_mmul4)
        testtop r11
        call    mmul4
        testtail
+       pshufd  xmm10, xmm10, SHUF(0, 2, 1, 3)
+       pshufd  xmm11, xmm11, SHUF(0, 2, 1, 3)
        movdqu  [r10 +  0], xmm10
        movdqu  [r10 + 16], xmm11
        testcarryout
@@ -1594,6 +1614,8 @@ FUNC(test_mmla4)
        testtop r11
        call    mmla4
        testtail
+       pshufd  xmm10, xmm10, SHUF(0, 2, 1, 3)
+       pshufd  xmm11, xmm11, SHUF(0, 2, 1, 3)
        movdqu  [r10 +  0], xmm10
        movdqu  [r10 + 16], xmm11
        testcarryout
@@ -1605,6 +1627,8 @@ FUNC(test_mont4)
        testtop
        call    mont4
        testtail
+       pshufd  xmm10, xmm10, SHUF(0, 2, 1, 3)
+       pshufd  xmm11, xmm11, SHUF(0, 2, 1, 3)
        movdqu  [r10 +  0], xmm10
        movdqu  [r10 + 16], xmm11
        testcarryout
diff --git a/math/mpx-mul4-arm-neon.S b/math/mpx-mul4-arm-neon.S
new file mode 100644 (file)
index 0000000..efca790
--- /dev/null
@@ -0,0 +1,1189 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Large SIMD-based multiplications
+///
+/// (c) 2019 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.
+
+///--------------------------------------------------------------------------
+/// Preliminaries.
+
+#include "config.h"
+#include "asm-common.h"
+
+       .arch   armv7-a
+       .fpu    neon
+
+       .text
+
+///--------------------------------------------------------------------------
+/// Theory.
+///
+/// We define a number of primitive fixed-size multipliers from which we can
+/// construct more general variable-length multipliers.
+///
+/// The basic trick is the same throughout.  In an operand-scanning
+/// multiplication, the inner multiplication loop multiplies a multiple-
+/// precision operand by a single precision factor, and adds the result,
+/// appropriately shifted, to the result.  A `finely integrated operand
+/// scanning' implementation of Montgomery multiplication also adds the
+/// product of a single-precision `Montgomery factor' and the modulus,
+/// calculated in the same pass.  The more common `coarsely integrated
+/// operand scanning' alternates main multiplication and Montgomery passes,
+/// which requires additional carry propagation.
+///
+/// Throughout both plain-multiplication and Montgomery stages, then, one of
+/// the factors remains constant throughout the operation, so we can afford
+/// to take a little time to preprocess it.  The transformation we perform is
+/// as follows.  Let b = 2^16, and B = b^2 = 2^32.  Suppose we're given a
+/// 128-bit factor v = v_0 + v_1 B + v_2 B^2 + v_3 B^3.  Split each v_i into
+/// two sixteen-bit pieces, so v_i = v'_i + v''_i b.  These eight 16-bit
+/// pieces are placed into 32-bit cells, and arranged as two 128-bit NEON
+/// operands, as follows.
+///
+///    Offset     0       4        8      12
+///       0    v'_0   v''_0     v'_1   v''_1
+///      16    v'_2   v''_2     v'_3   v''_3
+///
+/// The `vmull' and `vmlal' instructions can multiply a vector of two 32-bit
+/// values by a 32-bit scalar, giving two 64-bit results; thus, it will act
+/// on (say) v'_0 and v''_0 in a single instruction, to produce two 48-bit
+/// results in 64-bit fields.  The sixteen bits of headroom allows us to add
+/// many products together before we must deal with carrying; it also allows
+/// for some calculations to be performed on the above expanded form.
+///
+/// We maintain three `carry' registers, q12--q14, accumulating intermediate
+/// results; we name them `c0', `c1', and `c2'.  Each carry register holds
+/// two 64-bit halves: the register c0, for example, holds c'_0 (low half)
+/// and c''_0 (high half), and represents the value c_0 = c'_0 + c''_0 b; the
+/// carry registers collectively represent the value c_0 + c_1 B + c_2 B^2.
+/// The `vmull' or `vmlal' instruction acting on a scalar operand and an
+/// operand in the expanded form above produces a result which can be added
+/// directly to the appropriate carry register.
+///
+/// Multiplication is performed in product-scanning order, since ARM
+/// processors commonly implement result forwarding for consecutive multiply-
+/// and-accumulate instructions specifying the same destination.
+/// Experimentally, this runs faster than operand-scanning in an attempt to
+/// hide instruction latencies.
+///
+/// On 32-bit ARM, we have a reasonable number of registers: the expanded
+/// operands are kept in registers.  The packed operands are read from memory
+/// into working registers q0 and q1.  The following conventional argument
+/// names and locations are used throughout.
+///
+/// Arg        Format      Location    Notes
+///
+/// U  packed      [r1]
+/// X  packed      [r2]        In Montgomery multiplication, X = N
+/// V  expanded    q2/q3
+/// Y  expanded    q4/q5       In Montgomery multiplication, Y = (A + U V) M
+/// M  expanded    q4/q5       -N^{-1} (mod B^4)
+/// N                          Modulus, for Montgomery multiplication
+/// A  packed      [r0]        Destination/accumulator
+/// C  carry       q13--q15
+///
+/// The calculation is some variant of
+///
+///    A' + C' B^4 <- U V + X Y + A + C
+///
+/// The low-level functions fit into a fairly traditional (finely-integrated)
+/// operand scanning loop over operand pairs (U, X) (indexed by j) and (V, Y)
+/// (indexed by i).
+///
+/// The variants are as follows.
+///
+/// Function   Variant                 Use             i       j
+///
+/// mmul4      A = C = 0               Montgomery      0       0
+/// dmul4      A = 0                   Montgomery      0       +
+/// mmla4      C = 0                   Montgomery      +       0
+/// dmla4      exactly as shown        Montgomery      +       +
+///
+/// mul4zc     U = V = A = C = 0       Plain           0       0
+/// mul4       U = V = A = 0           Plain           0       +
+/// mla4zc     U = V = C = 0           Plain           +       0
+/// mla4       U = V = 0               Plain           +       +
+///
+/// The `mmul4' and `mmla4' functions are also responsible for calculating
+/// the Montgomery reduction factor Y = (A + U V) M used by the rest of the
+/// inner loop.
+
+///--------------------------------------------------------------------------
+/// Macro definitions.
+
+.macro mulacc  z, u, v, x=nil, y=nil
+       // Set Z = Z + U V + X Y.  X may be `nil' to omit the second
+       // operand.  Z should be a 128-bit `qN' register; V and Y should be
+       // 64-bit `dN' registers; and U and X should be 32-bit `dN[I]'
+       // scalars; the multiplications produce two 64-bit elementwise
+       // products, which are added elementwise to Z.
+
+       vmlal.u32 \z, \v, \u
+    .ifnes "\x", "nil"
+       vmlal.u32 \z, \y, \x
+    .endif
+.endm
+
+.macro mulinit z, zinitp, u, v, x, y
+       // If ZINITP then set Z = Z + U V + X Y, as for `mulacc'; otherwise,
+       // set Z = U V + X Y.  Operand requirements and detailed operation
+       // are as for `mulacc'.
+
+  .ifeqs "\zinitp", "t"
+       mulacc  \z, \u, \v, \x, \y
+  .else
+       vmull.u32 \z, \v, \u
+    .ifnes "\x", "nil"
+       vmlal.u32 \z, \y, \x
+    .endif
+  .endif
+.endm
+
+// `MULI': accumulate the B^I and b B^i terms of the polynomial product sum U
+// V + X Y, given that U = u_0 + B u_1 + B^2 u_2 + B^3 u_3 (and similarly for
+// x), and V = v'_0 + b v''_0 + B (v'_1 + b v''_1) + B^2 (v'_2 + b v''_2) +
+// B^3 (v'_3 + b v''_3) (and similarly for Y).  The 64-bit coefficients are
+// added into the low and high halves of the 128-bit register Z (if ZINIT is
+// `nil' then simply set Z, as if it were initially zero).
+#define MUL0(z, zinitp, u, v0, v1, x, y0, y1)                          \
+       mulinit z, zinitp, QW(u, 0), D0(v0), QW(x, 0), D0(y0)
+#define MUL1(z, zinitp, u, v0, v1, x, y0, y1)                          \
+       mulinit z, zinitp, QW(u, 0), D1(v0), QW(x, 0), D1(y0);          \
+       mulacc  z,         QW(u, 1), D0(v0), QW(x, 1), D0(y0)
+#define MUL2(z, zinitp, u, v0, v1, x, y0, y1)                          \
+       mulinit z, zinitp, QW(u, 0), D0(v1), QW(x, 0), D0(y1);          \
+       mulacc  z,         QW(u, 1), D1(v0), QW(x, 1), D1(y0);          \
+       mulacc  z,         QW(u, 2), D0(v0), QW(x, 2), D0(y0)
+#define MUL3(z, zinitp, u, v0, v1, x, y0, y1)                          \
+       mulinit z, zinitp, QW(u, 0), D1(v1), QW(x, 0), D1(y1);          \
+       mulacc  z,         QW(u, 1), D0(v1), QW(x, 1), D0(y1);          \
+       mulacc  z,         QW(u, 2), D1(v0), QW(x, 2), D1(y0);          \
+       mulacc  z,         QW(u, 3), D0(v0), QW(x, 3), D0(y0)
+#define MUL4(z, zinitp, u, v0, v1, x, y0, y1)                          \
+       mulinit z, zinitp, QW(u, 1), D1(v1), QW(x, 1), D1(y1);          \
+       mulacc  z,         QW(u, 2), D0(v1), QW(x, 2), D0(y1);          \
+       mulacc  z,         QW(u, 3), D1(v0), QW(x, 3), D1(y0)
+#define MUL5(z, zinitp, u, v0, v1, x, y0, y1)                          \
+       mulinit z, zinitp, QW(u, 2), D1(v1), QW(x, 2), D1(y1);          \
+       mulacc  z,         QW(u, 3), D0(v1), QW(x, 3), D0(y1)
+#define MUL6(z, zinitp, u, v0, v1, x, y0, y1)                          \
+       mulinit z, zinitp, QW(u, 3), D1(v1), QW(x, 3), D1(y1)
+
+// Steps in the process of propagating carry bits from ZLO to ZHI (both
+// 128-bit `qN' registers).  Here, T is a 128-bit `qN' temporary register.
+// Set the low 32 bits of the 64-bit `dN' register ZOUT to the completed
+// coefficient z_i.
+//
+// In detail, what happens is as follows.  Suppose initially that ZLO =
+// (z'_i; z''_i) and ZHI = (z'_{i+1}; z''_{i+1}).  Let t = z'_i + b z''_i;
+// observe that floor(t/b) = floor(z'_i/b) + z''_i.  Let z_i = t mod B, and
+// add floor(t/B) = floor((floor(z'_i/b) + z''_i)/b) onto z'_{i+1}.  This has
+// a circuit depth of 4; I don't know how to do better.
+.macro _carry0 zout, zlo0, zlo1, t0, t1
+       // ZLO0 and ZLO1 are the low and high halves of a carry register.
+       // Extract a 32-bit output, in the bottom 32 bits of ZOUT, and set T1
+       // so as to continue processing using `_carry1'.  All operands are
+       // 64-bit `dN' registers.  If ZOUT is `nil' then no output is
+       // produced; if T1 is `nil' then no further processing will be
+       // possible.
+  .ifnes "\zout", "nil"
+       vshl.i64 \t0, \zlo1, #16
+  .endif
+  .ifnes "\t1", "nil"
+       vshr.u64 \t1, \zlo0, #16
+  .endif
+  .ifnes "\zout", "nil"
+       vadd.i64 \zout, \zlo0, \t0
+  .endif
+  .ifnes "\t1", "nil"
+       vadd.i64 \t1, \t1, \zlo1
+  .endif
+.endm
+.macro _carry1 u, zhi0, t1
+       // ZHI0 is the low half of a carry register, and T1 is the result of
+       // applying `_carry0' to the previous carry register.  Set U to the
+       // result of propagating the carry into ZHI0.
+       vshr.u64 \t1, \t1, #16
+       vadd.i64 \u, \zhi0, \t1
+.endm
+
+// More convenient wrappers for `_carry0' and `_carry1'.
+//
+// CARRY0(ZOUT, ZLO, T)
+//     Store a 32-bit output in ZOUT from carry ZLO, using T as a
+//     temporary.  ZOUT is a 64-bit `dN' register; ZLO and T are 128-bit
+//     `qN' registers.
+//
+// CARRY1(ZHI, T)
+//     Propagate carry from T into ZHI.  Both are 128-bit `qN' registers;
+//     ZHI is updated.
+#define CARRY0(zout, zlo, t)                                           \
+       CASIDE0(zout, D0(zlo), zlo, t)
+#define CARRY1(zhi, t)                                                 \
+       CASIDE1(D0(zhi), zhi, t)
+
+// Versions of `CARRY0' and `CARRY1' which don't mutate their operands.
+//
+// CASIDE0(ZOUT, U, ZLO, T)
+//     As for `CARRY0', but the low half of ZLO is actually in U (a 64-bit
+//     `dN' register).
+//
+// CASIDE0E(ZOUT, U, ZLO, T)
+//     As for `CASIDE0', but only calculate the output word, and no
+//     carry-out.
+//
+// CASIDE1(U, ZHI, T)
+//     As for `CARRY1', but write the updated low half of ZHI to U.
+#define CASIDE0(zout, u, zlo, t)                                       \
+       _carry0 zout, u, D1(zlo), D0(t), D1(t)
+#define CASIDE0E(zout, u, zlo, t)                                      \
+       _carry0 zout, u, D1(zlo), D0(t), nil
+#define CASIDE1(u, zhi, t)                                             \
+       _carry1 u, D0(zhi), D1(t)
+
+// Steps in spreading a packed 128-bit operand in A0 across A0, A1, A2, A3 in
+// carry format.
+#define SPREADACC0(a0, a1, a2, a3)                                     \
+       vmov.i32 a1, #0;                                                \
+       vmov.i32 a2, #0;                                                \
+       vmov.i32 a3, #0
+#define SPREADACC1(a0, a1, a2, a3)                                     \
+       vswp    D1(a0), D0(a2);                                         \
+       vtrn.32 D0(a0), D0(a1);                                         \
+       vtrn.32 D0(a2), D0(a3)
+
+// Add the carry-format values A0, A1, A2 into the existing carries C0, C1,
+// C2 (leaving A3 where it is).
+#define CARRYACC(a0, a1, a2, a3, c0, c1, c2)                           \
+       vadd.i64 c0, c0, a0;                                            \
+       vadd.i64 c1, c1, a1;                                            \
+       vadd.i64 c2, c2, a2
+
+///--------------------------------------------------------------------------
+/// Primitive multipliers and related utilities.
+
+INTFUNC(carryprop)
+       // On entry, r0 points to a destination, and q13--q15 hold incoming
+       // carries c0--c2.  On exit, the low 128 bits of the carry value are
+       // stored at [r0]; the remaining 16 bits of carry are left in d30; r0
+       // is advanced by 16; and q10--q14 are clobbered.
+  endprologue
+
+       CARRY0(D0(q10), q13, q12)
+       CARRY1(q14, q12)
+       CARRY0(D0(q11), q14, q12)
+       CARRY1(q15, q12)
+       CARRY0(D1(q10), q15, q12)
+       vshr.u64 D1(q11), D1(q12), #16
+       vshr.u64 D0(q15), D1(q12), #48
+       vtrn.32 q10, q11
+       vst1.32 {q10}, [r0]!
+       bx      r14
+ENDFUNC
+
+INTFUNC(dmul4)
+       // On entry, r0 points to the destination; r1 and r2 point to packed
+       // operands U and X; q2/q3 and q4/q5 hold expanded operands V and Y;
+       // and q13--q15 hold incoming carries c0--c2.  On exit, the
+       // destination and carries are updated; r0, r1, r2 are each advanced
+       // by 16; q2--q5 are preserved; and the other NEON registers are
+       // clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       vld1.32 {q0}, [r1]!
+       vld1.32 {q1}, [r2]!
+
+       // Do the multiplication.
+       MUL0(q13, t,      q0,  q2, q3,    q1,  q4, q5)
+       MUL1(q14, t,      q0,  q2, q3,    q1,  q4, q5)
+        CARRY0(D0(q8), q13, q6)
+       MUL2(q15, t,      q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q14, q6)
+        CARRY0(D0(q9), q14, q6)
+       MUL3(q12, nil,    q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q15, q6)
+        CARRY0(D1(q8), q15, q6)
+       MUL4(q13, nil,    q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q12, q6)
+        CARRY0(D1(q9), q12, q6)
+       MUL5(q14, nil,    q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q13, q6)
+       MUL6(q15, nil,    q0,  q2, q3,    q1,  q4, q5)
+
+       // Finish up and store the result.
+       vtrn.32 q8, q9
+       vst1.32 {q8}, [r0]!
+
+       // All done.
+       bx      r14
+ENDFUNC
+
+INTFUNC(dmla4)
+       // On entry, r0 points to the destination/accumulator; r1 and r2
+       // point to packed operands U and X; q2/q3 and q4/q5 hold expanded
+       // operands V and Y; and q13--q15 hold incoming carries c0--c2.  On
+       // exit, the accumulator and carries are updated; r0, r1, r2 are each
+       // advanced by 16; q2--q5 are preserved; and the other NEON registers
+       // are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       vld1.32 {q9}, [r0]
+       SPREADACC0(q9, q10, q11, q12)
+       vld1.32 {q0}, [r1]!
+       vld1.32 {q1}, [r2]!
+
+       // Add the accumulator input to the incoming carries.  Split the
+       // accumulator into four pieces and add the carries onto them.
+       SPREADACC1(q9, q10, q11, q12)
+       CARRYACC(q9, q10, q11, q12, q13, q14, q15)
+
+       // Do the multiplication.
+       MUL0(q13, t,      q0,  q2, q3,    q1,  q4, q5)
+       MUL1(q14, t,      q0,  q2, q3,    q1,  q4, q5)
+        CARRY0(D0(q8), q13, q6)
+       MUL2(q15, t,      q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q14, q6)
+        CARRY0(D0(q9), q14, q6)
+       MUL3(q12, t,      q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q15, q6)
+        CARRY0(D1(q8), q15, q6)
+       MUL4(q13, nil,    q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q12, q6)
+        CARRY0(D1(q9), q12, q6)
+       MUL5(q14, nil,    q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q13, q6)
+       MUL6(q15, nil,    q0,  q2, q3,    q1,  q4, q5)
+
+       // Finish up and store the result.
+       vtrn.32 q8, q9
+       vst1.32 {q8}, [r0]!
+
+       // All done.
+       bx      r14
+ENDFUNC
+
+INTFUNC(mul4)
+       // On entry, r0 points to the destination; r2 points to a packed
+       // operand X; q4/q5 holds an expanded operand Y; and q13--q15 hold
+       // incoming carries c0--c2.  On exit, the destination and carries are
+       // updated; r0 and r2 are each advanced by 16; q4 and q5 are
+       // preserved; and the other NEON registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       vld1.32 {q1}, [r2]!
+
+       // Do the multiplication.
+       MUL0(q13, t,      q1,  q4, q5,    nil,  nil, nil)
+       MUL1(q14, t,      q1,  q4, q5,    nil,  nil, nil)
+        CARRY0(D0(q8), q13, q6)
+       MUL2(q15, t,      q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q14, q6)
+        CARRY0(D0(q9), q14, q6)
+       MUL3(q12, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q15, q6)
+        CARRY0(D1(q8), q15, q6)
+       MUL4(q13, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q12, q6)
+        CARRY0(D1(q9), q12, q6)
+       MUL5(q14, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q13, q6)
+       MUL6(q15, nil,    q1,  q4, q5,    nil,  nil, nil)
+
+       // Finish up and store the result.
+       vtrn.32 q8, q9
+       vst1.32 {q8}, [r0]!
+
+       // All done.
+       bx      r14
+ENDFUNC
+
+INTFUNC(mul4zc)
+       // On entry, r0 points to the destination; r2 points to a packed
+       // operand X; and q4/q5 holds an expanded operand Y.  On exit, the
+       // destination is updated; q13--q15 hold outgoing carries c0--c2; r0
+       // and r2 are each advanced by 16; q4 and q5 are preserved; and the
+       // other NEON registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       vld1.32 {q1}, [r2]!
+
+       // Do the multiplication.
+       MUL0(q13, nil,    q1,  q4, q5,    nil,  nil, nil)
+       MUL1(q14, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY0(D0(q8), q13, q6)
+       MUL2(q15, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q14, q6)
+        CARRY0(D0(q9), q14, q6)
+       MUL3(q12, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q15, q6)
+        CARRY0(D1(q8), q15, q6)
+       MUL4(q13, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q12, q6)
+        CARRY0(D1(q9), q12, q6)
+       MUL5(q14, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q13, q6)
+       MUL6(q15, nil,    q1,  q4, q5,    nil,  nil, nil)
+
+       // Finish up and store the result.
+       vtrn.32 q8, q9
+       vst1.32 {q8}, [r0]!
+
+       // All done.
+       bx      r14
+ENDFUNC
+
+INTFUNC(mla4)
+       // On entry, r0 points to the destination/accumulator; r2 points to a
+       // packed operand X; q4/q5 holds an expanded operand Y; and q13--q15
+       // hold incoming carries c0--c2.  On exit, the accumulator and
+       // carries are updated; r0 and r2 are each advanced by 16; q4 and q5
+       // are preserved; and the other NEON registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       vld1.32 {q9}, [r0]
+       SPREADACC0(q9, q10, q11, q12)
+       vld1.32 {q1}, [r2]!
+
+       // Add the accumulator input to the incoming carries.  Split the
+       // accumulator into four pieces and add the carries onto them.
+       SPREADACC1(q9, q10, q11, q12)
+       CARRYACC(q9, q10, q11, q12, q13, q14, q15)
+
+       // Do the multiplication.
+mla4_common:
+       MUL0(q13, t,      q1,  q4, q5,    nil,  nil, nil)
+       MUL1(q14, t,      q1,  q4, q5,    nil,  nil, nil)
+        CARRY0(D0(q8), q13, q6)
+       MUL2(q15, t,      q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q14, q6)
+        CARRY0(D0(q9), q14, q6)
+       MUL3(q12, t,      q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q15, q6)
+        CARRY0(D1(q8), q15, q6)
+       MUL4(q13, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q12, q6)
+        CARRY0(D1(q9), q12, q6)
+       MUL5(q14, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q13, q6)
+       MUL6(q15, nil,    q1,  q4, q5,    nil,  nil, nil)
+
+       // Finish up and store the result.
+       vtrn.32 q8, q9
+       vst1.32 {q8}, [r0]!
+
+       // All done.
+       bx      r14
+ENDFUNC
+
+INTFUNC(mla4zc)
+       // On entry, r0 points to the destination/accumulator; r2 points to a
+       // packed operand X; and q4/q5 holds an expanded operand Y.  On exit,
+       // the accumulator is updated; q13--q15 hold outgoing carries c0--c2;
+       // r0 and r2 are each advanced by 16; q4 and q5 are preserved; and
+       // the other NEON registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       vld1.32 {q13}, [r0]
+       SPREADACC0(q13, q14, q15, q12)
+       vld1.32 {q1}, [r2]!
+
+       // Move the accumulator input to the incoming carry slots.  Split the
+       // accumulator into four pieces.
+       SPREADACC1(q13, q14, q15, q12)
+
+       b       mla4_common
+ENDFUNC
+
+INTFUNC(mmul4)
+       // On entry, r0 points to the destination; r1 points to a packed
+       // operand U; r2 points to a packed operand X (the modulus); q2/q3
+       // holds an expanded operand V; and q4/q5 holds an expanded operand M
+       // (the Montgomery factor -N^{-1} (mod B)).  On exit, the destination
+       // is updated (to zero); q4/q5 hold an expanded factor Y = U V M (mod
+       // B); q13--q15 hold outgoing carries c0--c2; r0, r1, and r2 are each
+       // advanced by 16; q2 and q3 are preserved; and the other NEON
+       // registers are clobbered.
+
+       // Start by loading the operand words from memory.
+       vld1.32 {q0}, [r1]!
+
+       // Calculate the low half of W = A + U V, being careful to leave the
+       // carries in place.
+       MUL0(q13, nil,    q0,  q2, q3,    nil,  nil, nil)
+       MUL1(q14, nil,    q0,  q2, q3,    nil,  nil, nil)
+        CARRY0(D0(q6), q13, q8)
+       MUL2(q15, nil,    q0,  q2, q3,    nil,  nil, nil)
+        CASIDE1(D0(q9), q14, q8)
+        CASIDE0(D0(q7), D0(q9), q14, q8)
+       MUL3(q12, nil,    q0,  q2, q3,    nil,  nil, nil)
+       b mmla4_common
+ENDFUNC
+
+INTFUNC(mmla4)
+       // On entry, r0 points to the destination/accumulator A; r1 points to
+       // a packed operand U; r2 points to a packed operand X (the modulus);
+       // q2/q3 holds an expanded operand V; and q4/q5 holds an expanded
+       // operand M (the Montgomery factor -N^{-1} (mod B)).  On exit, the
+       // accumulator is updated (to zero); q4/q5 hold an expanded factor Y
+       // = (A + U V) M (mod B); q13--q15 hold outgoing carries c0--c2; r0,
+       // r1, and r2 are each advanced by 16; q2 and q3 are preserved; and
+       // the other NEON registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       vld1.32 {q13}, [r0]
+       SPREADACC0(q13, q14, q15, q12)
+       vld1.32 {q0}, [r1]!
+
+       // Move the accumulator input to the incoming carry slots.  Split the
+       // accumulator into four pieces.
+       SPREADACC1(q13, q14, q15, q12)
+
+       // Calculate the low half of W = A + U V, being careful to leave the
+       // carries in place.
+       MUL0(q13, t,      q0,  q2, q3,    nil,  nil, nil)
+       MUL1(q14, t,      q0,  q2, q3,    nil,  nil, nil)
+        CARRY0(D0(q6), q13, q8)
+       MUL2(q15, t,      q0,  q2, q3,    nil,  nil, nil)
+        CASIDE1(D0(q9), q14, q8)
+        CASIDE0(D0(q7), D0(q9), q14, q8)
+       MUL3(q12, t,      q0,  q2, q3,    nil,  nil, nil)
+mmla4_common:
+        CASIDE1(D0(q9), q15, q8)
+        CASIDE0(D1(q6), D0(q9), q15, q8)
+        CASIDE1(D0(q9), q12, q8)
+        CASIDE0E(D1(q7), D0(q9), q12, q8)
+       vtrn.32 q6, q7
+
+       // Calculate the low half of the Montgomery factor Y = W M.  At this
+       // point, registers are a little tight.
+       MUL0( q8, nil,    q6,  q4, q5,    nil,  nil, nil)
+       MUL1( q9, nil,    q6,  q4, q5,    nil,  nil, nil)
+        CARRY0(D0(q8), q8, q1)
+       MUL2(q10, nil,    q6,  q4, q5,    nil,  nil, nil)
+        CARRY1(q9, q1)
+        CARRY0(D0(q9), q9, q1)
+       MUL3(q11, nil,    q6,  q4, q5,    nil,  nil, nil)
+        CARRY1(q10, q1)
+        CARRY0(D1(q8), q10, q1)
+         vmov.i32 q5, #0
+        CARRY1(q11, q1)
+        CARRY0(D1(q9), q11, q1)
+         vld1.32 {q1}, [r2]!
+       vtrn.32 q8, q9
+
+       // Expand Y.  We'll put it in its proper place a bit later.
+       vzip.16 q8, q5
+
+       // Build up the product X Y in the carry slots.
+       MUL0(q13, t,      q1,  q8, q5,    nil,  nil, nil)
+       MUL1(q14, t,      q1,  q8, q5,    nil,  nil, nil)
+        CARRY0(nil, q13, q9)
+       MUL2(q15, t,      q1,  q8, q5,    nil,  nil, nil)
+        CARRY1(q14, q9)
+         vmov  q4, q8
+        CARRY0(nil, q14, q9)
+       MUL3(q12, t,      q1,  q8, q5,    nil,  nil, nil)
+        CARRY1(q15, q9)
+        CARRY0(nil, q15, q9)
+       vmov.u32 q6, #0
+
+       // And complete the calculation.
+       MUL4(q13, nil,    q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q12, q9)
+        CARRY0(nil, q12, q9)
+       MUL5(q14, nil,    q0,  q2, q3,    q1,  q4, q5)
+        CARRY1(q13, q9)
+       MUL6(q15, nil,    q0,  q2, q3,    q1,  q4, q5)
+
+       // Finish up and store the result.
+       vst1.32 {q6}, [r0]!
+
+       // All done.
+       bx      r14
+ENDFUNC
+
+INTFUNC(mont4)
+       // On entry, r0 points to the destination/accumulator A; r2 points to
+       // a packed operand X (the modulus); and q2/q3 holds an expanded
+       // operand M (the Montgomery factor -N^{-1} (mod B)).  On exit, the
+       // accumulator is updated (to zero); q4/q5 hold an expanded factor Y
+       // = A M (mod B); q13--q15 hold outgoing carries c0--c2; r0 and r2
+       // are each advanced by 16; q2 and q3 are preserved; and the other
+       // NEON registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       vld1.32 {q0}, [r0]
+       vld1.32 {q1}, [r2]!
+
+       // Calculate Y = A M (mod B).
+       MUL0(q8, nil,     q0,  q2, q3,    nil,  nil, nil)
+       MUL1(q9, nil,     q0,  q2, q3,    nil,  nil, nil)
+        CARRY0(D0(q4), q8, q6)
+       MUL2(q10, nil,    q0,  q2, q3,    nil,  nil, nil)
+        CARRY1(q9, q6)
+         vmov  q13, q0
+        CARRY0(D0(q7), q9, q6)
+       MUL3(q11, nil,    q0,  q2, q3,    nil,  nil, nil)
+        CARRY1(q10, q6)
+        CARRY0(D1(q4), q10, q6)
+         SPREADACC0(q13, q14, q15, q12)
+        CARRY1(q11, q6)
+        CARRY0(D1(q7), q11, q6)
+         SPREADACC1(q13, q14, q15, q12)
+       vmov.i32 q5, #0
+       vtrn.32 q4, q7
+       vzip.16 q4, q5
+
+       // Calculate the actual result.  Well, the carries, at least.
+       vmov.i32 q8, #0
+       MUL0(q13, t,      q1,  q4, q5,    nil,  nil, nil)
+       MUL1(q14, t,      q1,  q4, q5,    nil,  nil, nil)
+        CARRY0(nil, q13, q6)
+       MUL2(q15, t,      q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q14, q6)
+        CARRY0(nil, q14, q6)
+       MUL3(q12, t,      q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q15, q6)
+        CARRY0(nil, q15, q6)
+       MUL4(q13, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q12, q6)
+        CARRY0(nil, q12, q6)
+       MUL5(q14, nil,    q1,  q4, q5,    nil,  nil, nil)
+        CARRY1(q13, q6)
+       MUL6(q15, nil,    q1,  q4, q5,    nil,  nil, nil)
+
+       // Finish up and store the result.
+       vst1.32 {q8}, [r0]!
+
+       // All done.
+       bx      r14
+ENDFUNC
+
+///--------------------------------------------------------------------------
+/// Bulk multipliers.
+
+FUNC(mpx_umul4_arm_neon)
+       // void mpx_umul4_arm_neon(mpw *dv, const mpw *av, const mpw *avl,
+       //                         const mpw *bv, const mpw *bvl);
+
+       // Establish the arguments and do initial setup.
+       //
+       // inner loop dv        r0
+       // inner loop av        r2
+       // outer loop dv        r5
+       // outer loop bv        r3
+       // av base              r1
+       // av limit             r12
+       // bv limit             r4
+       pushreg r4, r5, r14
+       pushvfp QQ(q4, q7)
+  endprologue
+
+       // Prepare for the first iteration.
+       vld1.32 {q4}, [r3]!             // = Y = bv[0]
+       vmov.i32 q5, #0
+       // r0                           // = dv for inner loop
+       // r1                           // = av base
+       // r3                           // = bv for outer loop
+       ldr     r4, [sp, #76]           // = bv limit
+       mov     r12, r2                 // = av limit
+       mov     r2, r1                  // = av for inner loop
+       add     r5, r0, #16             // = dv for outer loop
+       vzip.16 q4, q5                  // expand Y
+       bl      mul4zc
+       cmp     r2, r12                 // all done?
+       bhs     8f
+
+       // Continue with the first iteration.
+0:     bl      mul4
+       cmp     r2, r12                 // all done?
+       blo     0b
+
+       // Write out the leftover carry.  There can be no tail here.
+8:     bl      carryprop
+       cmp     r3, r4                  // more passes to do?
+       bhs     9f
+
+       // Set up for the next pass.
+1:     vld1.32 {q4}, [r3]!             // = Y = bv[i]
+       vmov.i32 q5, #0
+       mov     r0, r5                  // -> dv[i]
+       mov     r2, r1                  // -> av[0]
+       add     r5, r5, #16
+       vzip.16 q4, q5                  // expand Y
+       bl      mla4zc
+       cmp     r2, r12                 // done yet?
+       bhs     8f
+
+       // Continue...
+0:     bl      mla4
+       cmp     r2, r12
+       blo     0b
+
+       // Finish off this pass.  There was no tail on the previous pass, and
+       // there can be done on this pass.
+8:     bl      carryprop
+       cmp     r3, r4
+       blo     1b
+
+       // All over.
+9:     popvfp  QQ(q4, q7)
+       popreg  r4, r5, r14
+       bx      r14
+ENDFUNC
+
+FUNC(mpxmont_mul4_arm_neon)
+       // void mpxmont_mul4_arm_neon(mpw *dv, const mpw *av, const mpw *bv,
+       //                           const mpw *nv, size_t n, const mpw *mi);
+
+       // Establish the arguments and do initial setup.
+       //
+       // inner loop dv        r0
+       // inner loop av        r1
+       // inner loop nv        r2
+       // mi                   r5
+       // outer loop dv        r6
+       // outer loop bv        r7
+       // av base              r8
+       // av limit             r9
+       // bv limit             r4
+       // nv base              r3
+       // n                    r4
+       // c                    r10
+       // 0                    r12
+
+       pushreg r4-r10, r14
+       pushvfp QQ(q4, q7)
+  endprologue
+
+       // Establish the expanded operands.
+       ldrd    r4, r5, [sp, #96]       // r4 = n; r5 -> mi
+       vld1.32 {q2}, [r2]              // = V = bv[0]
+       vmov.i32 q3, #0
+       vmov.i32 q5, #0
+       vld1.32 {q4}, [r5]              // = M
+
+       // Set up the outer loop state and prepare for the first iteration.
+       // r0                           // -> dv for inner loop
+       // r1                           // -> av for inner loop
+       add     r7, r2, #16             // -> bv
+       // r3                           // -> nv
+       add     r6, r0, #16             // -> dv
+       mov     r8, r1                  // -> av
+       add     r9, r1, r4, lsl #2      // -> av limit
+       add     r4, r2, r4, lsl #2      // -> bv limit
+       mov     r2, r3                  // -> nv for inner loop
+       mov     r12, #0                 // = 0
+
+       vzip.16 q2, q3                  // expand V
+       vzip.16 q4, q5                  // expand M
+       bl      mmul4
+       cmp     r1, r9                  // done already?
+       bhs     8f
+
+       // Complete the first inner loop.
+0:     bl      dmul4
+       cmp     r1, r9                  // done yet?
+       blo     0b
+
+       // Still have carries left to propagate.  Rather than store the tail
+       // end in memory, keep it in a general-purpose register for later.
+       bl      carryprop
+       vmov.32 r10, QW(q15, 0)
+
+       // Embark on the next iteration.  (There must be one.  If n = 1 then
+       // we would have bailed above, to label 8.  Similarly, the subsequent
+       // iterations can fall into the inner loop immediately.)
+1:     vld1.32 {q2}, [r7]!             // = V = bv[i]
+       vld1.32 {q4}, [r5]              // = M
+       vmov.i32 q3, #0
+       vmov.i32 q5, #0
+       mov     r0, r6                  // -> dv[i]
+       add     r6, r6, #16
+       mov     r1, r8                  // -> av[0]
+       mov     r2, r3                  // -> nv[0]
+       vzip.16 q2, q3                  // expand V
+       vzip.16 q4, q5                  // expand M
+       bl      mmla4
+
+       // Complete the next inner loop.
+0:     bl      dmla4
+       cmp     r1, r9                  // done yet?
+       blo     0b
+
+       // Still have carries left to propagate, and they overlap the
+       // previous iteration's final tail, so read that and add it.
+       cmp     r7, r4
+       vmov.32 D0(q12), r10, r12
+       vadd.i64 D0(q13), D0(q13), D0(q12)
+       bl      carryprop
+       vmov.32 r10, QW(q15, 0)
+
+       // Back again, maybe.
+       blo     1b
+
+       // All done, almost.
+       str     r10, [r0], #4
+       popvfp  QQ(q4, q7)
+       popreg  r4-r10, r14
+       bx      r14
+
+       // First iteration was short.  Write out the carries and we're done.
+       // (This could be folded into the main loop structure, but that would
+       // penalize small numbers more.)
+8:     bl      carryprop
+       vst1.32 {QW(q15, 0)}, [r0]!
+       popvfp  QQ(q4, q7)
+       popreg  r4-r10, r14
+       bx      r14
+ENDFUNC
+
+FUNC(mpxmont_redc4_arm_neon)
+       // void mpxmont_redc4_arm_neon(mpw *dv, mpw *dvl, const mpw *nv,
+       //                             size_t n, const mpw *mi);
+
+       // Establish the arguments and do initial setup.
+       //
+       // inner loop dv        r0
+       // dv limit             r1
+       // inner loop nv        r2
+       // blocks-of-4 dv limit r3
+       // mi                   (r14)
+       // outer loop dv        r4
+       // outer loop dv limit  r5
+       // nv base              r6
+       // nv limit             r7
+       // n                    r3
+       // c                    (r14)
+       // t0, t1, t2, t3       r2, r8, r9, r10
+       // 0                    r12
+
+       pushreg r4-r10, r14
+       pushvfp QQ(q4, q7)
+  endprologue
+
+       // Set up the outer loop state and prepare for the first iteration.
+       ldr     r14, [sp, #96]          // -> mi
+       vmov.i32 q3, #0
+        sub    r12, r1, r0             // total dv bytes
+       // r0                           // -> dv for inner loop
+       // r1                           // -> overall dv limit
+       // r2                           // -> nv for inner loop
+       // r3                           // = n (for now)
+       add     r4, r0, #16             // -> dv for outer loop
+       add     r5, r0, r3, lsl #2      // -> dv limit
+        bic    r12, r12, #15           // dv blocks of 4
+        vld1.32 {q2}, [r14]            // = M
+       mov     r6, r2                  // -> nv
+       add     r7, r2, r3, lsl #2      // -> nv limit
+       add     r3, r0, r12             // -> dv blocks-of-4 limit
+        vzip.16 q2, q3                 // expand M
+       mov     r12, #0                 // = 0
+       bl      mont4
+       cmp     r2, r7                  // done already?
+       bhs     8f
+
+5:     bl      mla4
+       cmp     r2, r7                  // done yet?
+       blo     5b
+
+       // Still have carries left to propagate.  Adding the accumulator
+       // block into the carries is a little different this time, because
+       // all four accumulator limbs have to be squished into the three
+       // carry registers for `carryprop' to do its thing.
+8:     vld1.32 {q9}, [r0]
+       SPREADACC0(q9, q10, q11, q12)
+       SPREADACC1(q9, q10, q11, q12)
+       vshl.u64 D0(q12), D0(q12), #16
+       CARRYACC(q9, q10, q11, q12, q13, q14, q15)
+       vadd.u64 D1(q15), D1(q15), D0(q12)
+
+       bl      carryprop
+       vmov.32 r14, QW(q15, 0)
+       cmp     r0, r3
+       bhs     7f
+
+       // Propagate the first group of carries.
+       ldmia   r0, {r2, r8-r10}
+       adds    r2, r2, r14
+       adcs    r8, r8, #0
+       adcs    r9, r9, #0
+       adcs    r10, r10, #0
+       stmia   r0!, {r2, r8-r10}
+       teq     r0, r3
+       beq     6f
+
+       // Continue carry propagation until the end of the buffer.
+0:     ldmia   r0, {r2, r8-r10}
+       adcs    r2, r2, #0
+       adcs    r8, r8, #0
+       adcs    r9, r9, #0
+       adcs    r10, r10, #0
+       stmia   r0!, {r2, r8-r10}
+       teq     r0, r3
+       bne     0b
+
+       // Deal with the tail end.  Note that the actual destination length
+       // won't be an exacty number of blocks of four, so it's safe to just
+       // drop through here.
+6:     adc     r14, r12, #0
+7:     ldr     r2, [r0]
+       adds    r2, r2, r14
+       str     r2, [r0], #4
+       teq     r0, r1
+       beq     8f
+0:     ldr     r2, [r0]
+       adcs    r2, r2, #0
+       str     r2, [r0], #4
+       teq     r0, r1
+       bne     0b
+
+       // All done for this iteration.  Start the next.
+8:     cmp     r4, r5
+       bhs     9f
+       mov     r0, r4
+       add     r4, r4, #16
+       mov     r2, r6
+       bl      mont4
+       b       5b
+
+       // All over.
+9:     popvfp  QQ(q4, q7)
+       popreg  r4-r10, r14
+       bx      r14
+ENDFUNC
+
+///--------------------------------------------------------------------------
+/// Testing and performance measurement.
+
+#ifdef TEST_MUL4
+
+//               dmul  smul  mmul  mont
+// z   r0        r0    r0    r0     r0
+// c   r4        r1    r1    r1     r1
+// y   r3        --    --    r2     r2
+// u   r1        r2    --    r3     --
+// x   r2        r3    r2    stk0   r3
+// vv  q2/q3     stk0  --    stk1   stk0
+// yy  q4/q5     stk1  r3    stk2   --
+// n   r5        stk2  stk0  stk3   stk1
+// cyv r6        stk3  stk1  stk4   stk2
+
+#define STKARG(i) sp, #80 + 4*(i)
+
+.macro testprologue mode
+       pushreg r4-r6, r14
+       pushvfp QQ(q4, q7)
+  endprologue
+
+  .ifeqs "\mode", "dmul"
+       mov     r4, r1                  // -> c
+       mov     r1, r2                  // -> u
+       mov     r2, r3                  // -> x
+
+       ldr     r14, [STKARG(0)]        // -> vv
+       vld1.32 {q2}, [r14]
+       vmov.i32 q3, #0
+       vzip.16 q2, q3                  // (v'_0, v''_0; v'_1, v''_1)
+
+       ldr     r14, [STKARG(1)]        // -> yy
+       vld1.32 {q4}, [r14]
+       vmov.i32 q5, #0
+       vzip.16 q4, q5                  // (y'_0, y''_0; y'_1, y''_1)
+
+       ldr     r5, [STKARG(2)]         // = n
+       ldr     r6, [STKARG(3)]         // -> cyv
+  .endif
+
+  .ifeqs "\mode", "smul"
+       mov     r4, r1                  // -> c
+       // r2                           // -> x
+
+       vld1.32 {q4}, [r3]
+       vmov.i32 q5, #0
+       vzip.16 q4, q5                  // (y'_0, y''_0; y'_1, y''_1)
+
+       ldr     r5, [STKARG(0)]         // = n
+       ldr     r6, [STKARG(1)]         // -> cyv
+  .endif
+
+  .ifeqs "\mode", "mmul"
+       mov     r4, r1                  // -> c
+       mov     r1, r3                  // -> u
+       mov     r3, r2                  // -> y
+       ldr     r2, [STKARG(0)]         // -> x
+
+       ldr     r14, [STKARG(1)]        // -> vv
+       vld1.32 {q2}, [r14]
+       vmov.i32 q3, #0
+       vzip.16 q2, q3                  // (v'_0, v''_0; v'_1, v''_1)
+
+       ldr     r14, [STKARG(2)]        // -> yy
+       vld1.32 {q4}, [r14]
+       vmov.i32 q5, #0
+       vzip.16 q4, q5                  // (y'_0, y''_0; y'_1, y''_1)
+
+       ldr     r5, [STKARG(3)]         // = n
+       ldr     r6, [STKARG(4)]         // -> cyv
+  .endif
+
+  .ifeqs "\mode", "mont"
+       mov     r4, r1                  // -> c
+       mov     r1, r3                  // -> u
+       mov     r14, r2
+       mov     r2, r3                  // -> x
+       mov     r3, r14                 // -> y
+
+       ldr     r14, [STKARG(0)]        // -> vv
+       vld1.32 {q2}, [r14]
+       vmov.i32 q3, #0
+       vzip.16 q2, q3                  // (v'_0, v''_0; v'_1, v''_1)
+
+       ldr     r5, [STKARG(1)]         // = n
+       ldr     r6, [STKARG(2)]         // -> cyv
+  .endif
+.endm
+
+.macro testldcarry
+       vldmia  r4, {QQ(q13, q15)}      // c0, c1, c2
+.endm
+
+.macro testtop
+0:     subs    r5, r5, #1
+.endm
+
+.macro testtail
+       bne     0b
+.endm
+
+.macro testcarryout
+       vstmia  r4, {QQ(q13, q15)}
+.endm
+
+.macro testepilogue
+       popvfp  QQ(q4, q7)
+       popreg  r4-r6, r14
+       bx      r14
+.endm
+
+FUNC(test_dmul4)
+       testprologue dmul
+       testldcarry
+       testtop
+       bl      dmul4
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_dmla4)
+       testprologue dmul
+       testldcarry
+       testtop
+       bl      dmla4
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mul4)
+       testprologue smul
+       testldcarry
+       testtop
+       bl      mul4
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mul4zc)
+       testprologue smul
+       testldcarry
+       testtop
+       bl      mul4zc
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mla4)
+       testprologue smul
+       testldcarry
+       testtop
+       bl      mla4
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mla4zc)
+       testprologue smul
+       testldcarry
+       testtop
+       bl      mla4zc
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mmul4)
+       testprologue mmul
+       testtop
+       bl      mmul4
+       testtail
+       vst1.32 {q4, q5}, [r3]
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mmla4)
+       testprologue mmul
+       testtop
+       bl      mmla4
+       testtail
+       vst1.32 {q4, q5}, [r3]
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mont4)
+       testprologue mont
+       testtop
+       bl      mont4
+       testtail
+       vst1.32 {q4, q5}, [r3]
+       testcarryout
+       testepilogue
+ENDFUNC
+
+#endif
+
+///----- That's all, folks --------------------------------------------------
diff --git a/math/mpx-mul4-arm64-simd.S b/math/mpx-mul4-arm64-simd.S
new file mode 100644 (file)
index 0000000..60eed20
--- /dev/null
@@ -0,0 +1,1217 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Large SIMD-based multiplications
+///
+/// (c) 2019 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.
+
+///--------------------------------------------------------------------------
+/// Preliminaries.
+
+#include "config.h"
+#include "asm-common.h"
+
+       .text
+
+///--------------------------------------------------------------------------
+/// Theory.
+///
+/// We define a number of primitive fixed-size multipliers from which we can
+/// construct more general variable-length multipliers.
+///
+/// The basic trick is the same throughout.  In an operand-scanning
+/// multiplication, the inner multiplication loop multiplies a multiple-
+/// precision operand by a single precision factor, and adds the result,
+/// appropriately shifted, to the result.  A `finely integrated operand
+/// scanning' implementation of Montgomery multiplication also adds the
+/// product of a single-precision `Montgomery factor' and the modulus,
+/// calculated in the same pass.  The more common `coarsely integrated
+/// operand scanning' alternates main multiplication and Montgomery passes,
+/// which requires additional carry propagation.
+///
+/// Throughout both plain-multiplication and Montgomery stages, then, one of
+/// the factors remains constant throughout the operation, so we can afford
+/// to take a little time to preprocess it.  The transformation we perform is
+/// as follows.  Let b = 2^16, and B = b^2 = 2^32.  Suppose we're given a
+/// 128-bit factor v = v_0 + v_1 B + v_2 B^2 + v_3 B^3.  Split each v_i into
+/// two sixteen-bit pieces, so v_i = v'_i + v''_i b.  These eight 16-bit
+/// pieces are placed into 32-bit cells, and arranged as two 128-bit SIMD
+/// operands, as follows.
+///
+///    Offset     0       4        8      12
+///       0    v'_0   v''_0     v'_1   v''_1
+///      16    v'_2   v''_2     v'_3   v''_3
+///
+/// The `umull' and `umlal' instructions can multiply a vector of two 32-bit
+/// values by a 32-bit scalar, giving two 64-bit results; thus, it will act
+/// on (say) v'_0 and v''_0 in a single instruction, to produce two 48-bit
+/// results in 64-bit fields.  The sixteen bits of headroom allows us to add
+/// many products together before we must deal with carrying; it also allows
+/// for some calculations to be performed on the above expanded form.
+///
+/// We maintain three `carry' registers, v28--v30, accumulating intermediate
+/// results; we name them `c0', `c1', and `c2'.  Each carry register holds
+/// two 64-bit halves: the register c0, for example, holds c'_0 (low half)
+/// and c''_0 (high half), and represents the value c_0 = c'_0 + c''_0 b; the
+/// carry registers collectively represent the value c_0 + c_1 B + c_2 B^2.
+/// The `umull' or `umlal' instruction acting on a scalar operand and an
+/// operand in the expanded form above produces a result which can be added
+/// directly to the appropriate carry register.
+///
+/// An unusual feature of this code, as compared to the other `mul4'
+/// implementations, is that it makes extensive use of the ARM64
+/// general-purpose registers for carry resolution and output construction.
+/// As a result, an additional general-purpose register (typically x15) is
+/// used as an additional carry, with the carry value in bits 16--63.
+///
+/// Multiplication is performed in product-scanning order, since ARM
+/// processors commonly implement result forwarding for consecutive multiply-
+/// and-accumulate instructions specifying the same destination.
+/// Experimentally, this runs faster than operand-scanning in an attempt to
+/// hide instruction latencies.
+///
+/// On 64-bit ARM, we have a vast supply number of registers: the expanded
+/// operands are kept in registers.  The packed operands are read from memory
+/// into working registers v0 and v1.  The following conventional argument
+/// names and locations are used throughout.
+///
+/// Arg        Format      Location    Notes
+///
+/// U  packed      [x1]
+/// X  packed      [x2]        In Montgomery multiplication, X = N
+/// V  expanded    v2/v3
+/// Y  expanded    v4/v5       In Montgomery multiplication, Y = (A + U V) M
+/// M  expanded    v6/v7       -N^{-1} (mod B^4)
+/// N                          Modulus, for Montgomery multiplication
+/// A  packed      [x0]        Destination/accumulator
+/// C  carry       v28--v30
+/// 0              v31         128-bit zero
+///
+/// The calculation is some variant of
+///
+///    A' + C' B^4 <- U V + X Y + A + C
+///
+/// The low-level functions fit into a fairly traditional (finely-integrated)
+/// operand scanning loop over operand pairs (U, X) (indexed by j) and (V, Y)
+/// (indexed by i).
+///
+/// The variants are as follows.
+///
+/// Function   Variant                 Use             i       j
+///
+/// mmul4      A = C = 0               Montgomery      0       0
+/// dmul4      A = 0                   Montgomery      0       +
+/// mmla4      C = 0                   Montgomery      +       0
+/// dmla4      exactly as shown        Montgomery      +       +
+///
+/// mul4zc     U = V = A = C = 0       Plain           0       0
+/// mul4       U = V = A = 0           Plain           0       +
+/// mla4zc     U = V = C = 0           Plain           +       0
+/// mla4       U = V = 0               Plain           +       +
+///
+/// The `mmul4' and `mmla4' functions are also responsible for calculating
+/// the Montgomery reduction factor Y = (A + U V) M used by the rest of the
+/// inner loop.
+
+///--------------------------------------------------------------------------
+/// Macro definitions.
+
+.macro mulacc  z, u, v, x=nil, y=nil
+       // Set Z = Z + U V + X Y, using the low halves of V and Y.  Y may be
+       // `nil' to omit the second operand.  Z, V, and Y should be 128-bit
+       // `vN' registers; and U and X should be 32-bit `vN.s[I]' scalars;
+       // the multiplications produce two 64-bit elementwise products, which
+       // are added elementwise to Z.
+
+       umlal   \z\().2d, \v\().2s, \u
+    .ifnes "\y", "nil"
+       umlal   \z\().2d, \y\().2s, \x
+    .endif
+.endm
+
+.macro mulacc2 z, u, v, x=nil, y=nil
+       // Set Z = Z + U V + X Y, using the high halves of V and Y; see
+       // `mulacc'.
+
+       umlal2  \z\().2d, \v\().4s, \u
+    .ifnes "\y", "nil"
+       umlal2  \z\().2d, \y\().4s, \x
+    .endif
+.endm
+
+.macro mulinit z, zinitp, u, v=nil, x=nil, y=nil
+       // If ZINITP then set Z = Z + U V + X Y, as for `mulacc'; otherwise,
+       // set Z = U V + X Y.  Operand requirements and detailed operation
+       // are as for `mulacc'.
+
+  .ifeqs "\zinitp", "t"
+       mulacc  \z, \u, \v, \x, \y
+  .else
+       umull   \z\().2d, \v\().2s, \u
+    .ifnes "\y", "nil"
+       umlal   \z\().2d, \y\().2s, \x
+    .endif
+  .endif
+.endm
+
+.macro mulini2 z, zinitp, u, v=nil, x=nil, y=nil
+       // As `mulinit', but with the high halves of V and Y.
+
+  .ifeqs "\zinitp", "t"
+       mulacc2 \z, \u, \v, \x, \y
+  .else
+       umull2  \z\().2d, \v\().4s, \u
+    .ifnes "\y", "nil"
+       umlal2  \z\().2d, \y\().4s, \x
+    .endif
+  .endif
+.endm
+
+// `mulI': accumulate the B^I and b B^I terms of the polynomial product sum
+// U V + X Y, given that U = u_0 + B u_1 + B^2 u_2 + B^3 u_3 (and similarly
+// for x), and V = v'_0 + b v''_0 + B (v'_1 + b v''_1) + B^2 (v'_2 + b v''_2)
+// + B^3 (v'_3 + b v''_3) (and similarly for Y).  The 64-bit coefficients are
+// added into the low and high halves of the 128-bit register Z (if ZINIT is
+// `nil' then simply set Z, as if it were initially zero).
+.macro mul0    z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil
+       mulinit \z, \zinitp, \u\().s[0], \v0, \x\().s[0], \y0
+.endm
+.macro mul1    z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil
+       mulini2 \z, \zinitp, \u\().s[0], \v0, \x\().s[0], \y0
+       mulacc  \z,          \u\().s[1], \v0, \x\().s[1], \y0
+.endm
+.macro mul2    z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil
+       mulinit \z, \zinitp, \u\().s[0], \v1, \x\().s[0], \y1
+       mulacc2 \z,          \u\().s[1], \v0, \x\().s[1], \y0
+       mulacc  \z,          \u\().s[2], \v0, \x\().s[2], \y0
+.endm
+.macro mul3    z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil
+       mulini2 \z, \zinitp, \u\().s[0], \v1, \x\().s[0], \y1
+       mulacc  \z,          \u\().s[1], \v1, \x\().s[1], \y1
+       mulacc2 \z,          \u\().s[2], \v0, \x\().s[2], \y0
+       mulacc  \z,          \u\().s[3], \v0, \x\().s[3], \y0
+.endm
+.macro mul4    z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil
+       mulini2 \z, \zinitp, \u\().s[1], \v1, \x\().s[1], \y1
+       mulacc  \z,          \u\().s[2], \v1, \x\().s[2], \y1
+       mulacc2 \z,          \u\().s[3], \v0, \x\().s[3], \y0
+.endm
+.macro mul5    z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil
+       mulini2 \z, \zinitp, \u\().s[2], \v1, \x\().s[2], \y1
+       mulacc  \z,          \u\().s[3], \v1, \x\().s[3], \y1
+.endm
+.macro mul6    z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil
+       mulini2 \z, \zinitp, \u\().s[3], \v1, \x\().s[3], \y1
+.endm
+
+// Steps in the process of propagating carry bits upwards from ZLO (a 128-bit
+// `vN' register).  Here, T0, T1, and CG are 64-bit `xN' general-purpose
+// registers clobbered in the process.  Set the low 32 bits of the 64-bit
+// `xN' general-purpose register ZOUT to the completed coefficient z_1,
+// leaving a carry in CG.
+//
+// In detail, what happens is as follows.  Suppose initially that ZLO =
+// (z'_i; z''_i) and ZHI = (z'_{i+1}; z''_{i+1}).  Let t = z'_i + b z''_i;
+// observe that floor(t/b) = floor(z'_i/b) + z''_i.  Let z_i = t mod B, and
+// add floor(t/B) = floor((floor(z'_i/b) + z''_i)/b) onto z'_{i+1}.  This has
+// a circuit depth of 3; I don't know how to do better.
+//
+// Output words are left in the low half of a 64-bit register, with rubbish
+// in the high half.  Two such results can be combined using the `bfi'
+// instruction.
+.macro carry0  zlo, cg=x15, t0=x16, t1=x17
+       // Capture the values of carry-register ZLO and CG (if not `nil') in
+       // general-purpose registers T0 and T1, suitable for use in `carry1'.
+       mov     \t0, \zlo\().d[0]
+       mov     \t1, \zlo\().d[1]
+  .ifnes "\cg", "nil"
+       add     \t0, \t0, \cg, lsr #16
+  .endif
+.endm
+.macro carry1  zout, cg=x15, t0=x16, t1=x17
+       // Collect a 32-bit output word in the low 32 bits of ZOUT (leaving
+       // rubbish in the high 32 bits), and update CG suitably to continue
+       // processing with the next carry register.
+  .ifnes "\zout", "nil"
+       add     \zout, \t0, \t1, lsl #16
+  .endif
+  .ifnes "\cg", "nil"
+       add     \cg, \t1, \t0, lsr #16
+  .endif
+.endm
+
+.macro expand  vlo, vhi, vz=v31
+       // Expand the packed 128-bit operand in VLO to an expanded operand in
+       // VLO and VHI, assuming that VZ is all-bits-zero.  All three are
+       // `vN' 128-bit SIMD registers.
+       zip2    \vhi\().8h, \vlo\().8h, \vz\().8h
+       zip1    \vlo\().8h, \vlo\().8h, \vz\().8h
+.endm
+
+.macro sprdacc a0, a1, a2, a3=nil
+       // Spread the packed 128-bit operand in A0 into carry-format values
+       // in A0, A1, A2, A3.  If A3 is `nil', then spread the same value
+       // into A0, A1, A2 only, clobbering x16.
+  .ifeqs "\a3", "nil"
+       mov     w16, \a0\().s[3]
+  .endif
+       trn2    \a2\().2d, \a0\().2d, v31.2d
+       trn2    \a1\().2s, \a0\().2s, v31.2s
+  .ifeqs "\a3", "nil"
+       lsl     x16, x16, #16
+  .endif
+       trn1    \a0\().2s, \a0\().2s, v31.2s
+  .ifeqs "\a3", "nil"
+       mov     \a2\().d[1], x16
+  .else
+       trn2    \a3\().2s, \a2\().2s, v31.2s
+  .endif
+       mov     \a2\().s[1], wzr
+.endm
+
+.macro crryacc a0, a1, a2, a3, c0, c1, c2
+       // Add the carry-format values A0, A1, A2 into the existing carries
+       // C0, C1, C2 (leaving A3 where it is).
+       add     \c0\().2d, \c0\().2d, \a0\().2d
+       add     \c1\().2d, \c1\().2d, \a1\().2d
+       add     \c2\().2d, \c2\().2d, \a2\().2d
+.endm
+
+///--------------------------------------------------------------------------
+/// Primitive multipliers and related utilities.
+
+INTFUNC(carryprop)
+       // On entry, x0 points to a destination, and v28--v30 and x15 hold
+       // incoming carries c0--c2 and cg.  On exit, the low 128 bits of the
+       // carry value are stored at [x0]; the remaining 16 bits of carry are
+       // left in x10; x0 is advanced by 16; and x11--x17 are clobbered.
+  endprologue
+
+       carry0  v28
+       carry1  x11
+       carry0  v29
+       carry1  x13
+       carry0  v30
+       carry1  x12
+       bfi     x11, x13, #32, #32
+       lsr     x14, x15, #16
+       lsr     x10, x15, #48
+       bfi     x12, x14, #32, #32
+       stp     x11, x12, [x0], #16
+       ret
+ENDFUNC
+
+INTFUNC(dmul4)
+       // On entry, x0 points to the destination; x1 and x2 point to packed
+       // operands U and X; v2/v3 and v4/v5 hold expanded operands V and Y;
+       // v28--v30 and x15 hold incoming carries c0--c2 and cg; and v31 is
+       // zero.  On exit, the destination and carries are updated; x0, x1,
+       // x2 are each advanced by 16; v2--v5 and v8--v15 are preserved; and
+       // x11--x14, x16, x17 and the other SIMD registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       ldr     q0, [x1], #16
+       ldr     q1, [x2], #16
+
+       // Do the multiplication.
+       mul0    v28, t,      v0,  v2, v3,    v1,  v4, v5
+       mul1    v29, t,      v0,  v2, v3,    v1,  v4, v5
+        carry0 v28
+       mul2    v30, t,      v0,  v2, v3,    v1,  v4, v5
+        carry1 x11
+        carry0 v29
+       mul3    v27, nil,    v0,  v2, v3,    v1,  v4, v5
+        carry1 x13
+        carry0 v30
+       mul4    v28, nil,    v0,  v2, v3,    v1,  v4, v5
+        carry1 x12
+        carry0 v27
+       mul5    v29, nil,    v0,  v2, v3,    v1,  v4, v5
+        carry1 x14
+       mul6    v30, nil,    v0,  v2, v3,    v1,  v4, v5
+
+       // Finish up and store the result.
+       bfi     x11, x13, #32, #32
+       bfi     x12, x14, #32, #32
+       stp     x11, x12, [x0], #16
+
+       // All done.
+       ret
+ENDFUNC
+
+INTFUNC(dmla4)
+       // On entry, x0 points to the destination/accumulator A; x1 and x2
+       // point to packed operands U and X; v2/v3 and v4/v5 hold expanded
+       // operands V and Y; v28--v30 and x15 hold incoming carries c0--c2
+       // and cg; and v31 is zero.  On exit, the accumulator and carries are
+       // updated; x0, x1, x2 are each advanced by 16; v2--v5 and v8--v15
+       // are preserved; and x11--x14, x16, x17 and the other SIMD registers
+       // are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       ldr     q24, [x0]
+       ldr     q0, [x1], #16
+       ldr     q1, [x2], #16
+       sprdacc v24, v25, v26, v27
+       crryacc v24, v25, v26, v27, v28, v29, v30
+
+       // Do the multiplication.
+       mul0    v28, t,      v0,  v2, v3,    v1,  v4, v5
+       mul1    v29, t,      v0,  v2, v3,    v1,  v4, v5
+        carry0 v28
+       mul2    v30, t,      v0,  v2, v3,    v1,  v4, v5
+        carry1 x11
+        carry0 v29
+       mul3    v27, t,      v0,  v2, v3,    v1,  v4, v5
+        carry1 x13
+        carry0 v30
+       mul4    v28, nil,    v0,  v2, v3,    v1,  v4, v5
+        carry1 x12
+        carry0 v27
+       mul5    v29, nil,    v0,  v2, v3,    v1,  v4, v5
+        carry1 x14
+       mul6    v30, nil,    v0,  v2, v3,    v1,  v4, v5
+
+       // Finish up and store the result.
+       bfi     x11, x13, #32, #32
+       bfi     x12, x14, #32, #32
+       stp     x11, x12, [x0], #16
+
+       // All done.
+       ret
+ENDFUNC
+
+INTFUNC(mul4)
+       // On entry, x0 points to the destination; x2 points to a packed
+       // operand X; v4/v5 holds an expanded operand Y; v13--v15 and x15
+       // hold incoming carries c0--c2 and cg; and v31 is zero.  On exit,
+       // the destination and carries are updated; x0 and x2 are each
+       // advanced by 16; v4 and v5 and v8--v15 are preserved; and x11--x14,
+       // x16, x17 and the other SIMD registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       ldr     q1, [x2], #16
+
+       // Do the multiplication.
+       mul0    v28, t,      v1,  v4, v5
+       mul1    v29, t,      v1,  v4, v5
+        carry0 v28
+       mul2    v30, t,      v1,  v4, v5
+        carry1 x11
+        carry0 v29
+       mul3    v27, nil,    v1,  v4, v5
+        carry1 x13
+        carry0 v30
+       mul4    v28, nil,    v1,  v4, v5
+        carry1 x12
+        carry0 v27
+       mul5    v29, nil,    v1,  v4, v5
+        carry1 x14
+       mul6    v30, nil,    v1,  v4, v5
+
+       // Finish up and store the result.
+       bfi     x11, x13, #32, #32
+       bfi     x12, x14, #32, #32
+       stp     x11, x12, [x0], #16
+
+       // All done.
+       ret
+ENDFUNC
+
+INTFUNC(mul4zc)
+       // On entry, x0 points to the destination; x2 points to a packed
+       // operand X; v4/v5 holds an expanded operand Y; and v31 is zero.  On
+       // exit, the destination is updated; v28--v30 and x15 hold outgoing
+       // carries c0--c2 and cg; x0 and x2 are each advanced by 16; v4 and
+       // v5 and v8--v15 are preserved; and x11--x14, x16, x17 and the other
+       // SIMD registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       ldr     q1, [x2], #16
+
+       // Do the multiplication.
+       mul0    v28, nil,    v1,  v4, v5
+       mul1    v29, nil,    v1,  v4, v5
+        carry0 v28, nil
+       mul2    v30, nil,    v1,  v4, v5
+        carry1 x11
+        carry0 v29
+       mul3    v27, nil,    v1,  v4, v5
+        carry1 x13
+        carry0 v30
+       mul4    v28, nil,    v1,  v4, v5
+        carry1 x12
+        carry0 v27
+       mul5    v29, nil,    v1,  v4, v5
+        carry1 x14
+       mul6    v30, nil,    v1,  v4, v5
+
+       // Finish up and store the result.
+       bfi     x11, x13, #32, #32
+       bfi     x12, x14, #32, #32
+       stp     x11, x12, [x0], #16
+
+       // All done.
+       ret
+ENDFUNC
+
+INTFUNC(mla4)
+       // On entry, x0 points to the destination/accumulator A; x2 points to
+       // a packed operand X; v4/v5 holds an expanded operand Y; v13--v15
+       // and x15 hold incoming carries c0--c2 and cg; and v31 is zero.  On
+       // exit, the accumulator and carries are updated; x0 and x2 are each
+       // advanced by 16; v4 and v5 and v8--v15 are preserved; and x11--x14,
+       // x16, x17 and the other SIMD registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       ldr     q24, [x0]
+       ldr     q1, [x2], #16
+       sprdacc v24, v25, v26, v27
+       crryacc v24, v25, v26, v27, v28, v29, v30
+
+       // Do the multiplication.
+       mul0    v28, t,      v1,  v4, v5
+       mul1    v29, t,      v1,  v4, v5
+        carry0 v28
+       mul2    v30, t,      v1,  v4, v5
+        carry1 x11
+        carry0 v29
+       mul3    v27, t,      v1,  v4, v5
+        carry1 x13
+        carry0 v30
+       mul4    v28, nil,    v1,  v4, v5
+        carry1 x12
+        carry0 v27
+       mul5    v29, nil,    v1,  v4, v5
+        carry1 x14
+       mul6    v30, nil,    v1,  v4, v5
+
+       // Finish up and store the result.
+       bfi     x11, x13, #32, #32
+       bfi     x12, x14, #32, #32
+       stp     x11, x12, [x0], #16
+
+       // All done.
+       ret
+ENDFUNC
+
+INTFUNC(mla4zc)
+       // On entry, x0 points to the destination/accumulator A; x2 points to
+       // a packed operand X; v4/v5 holds an expanded operand Y; and v31 is
+       // zero.  On exit, the accumulator is updated; v28--v30 and x15 hold
+       // outgoing carries c0--c2 and cg; x0 and x2 are each advanced by 16;
+       // v4, v5, and v8--v15 are preserved; and x11--x14, x16, x17 and the
+       // other SIMD registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       ldr     q28, [x0]
+       ldr     q1, [x2], #16
+       sprdacc v28, v29, v30, v27
+
+       // Do the multiplication.
+       mul0    v28, t,      v1,  v4, v5
+       mul1    v29, t,      v1,  v4, v5
+        carry0 v28, nil
+       mul2    v30, t,      v1,  v4, v5
+        carry1 x11
+        carry0 v29
+       mul3    v27, t,      v1,  v4, v5
+        carry1 x13
+        carry0 v30
+       mul4    v28, nil,    v1,  v4, v5
+        carry1 x12
+        carry0 v27
+       mul5    v29, nil,    v1,  v4, v5
+        carry1 x14
+       mul6    v30, nil,    v1,  v4, v5
+
+       // Finish up and store the result.
+       bfi     x11, x13, #32, #32
+       bfi     x12, x14, #32, #32
+       stp     x11, x12, [x0], #16
+
+       // All done.
+       ret
+ENDFUNC
+
+INTFUNC(mmul4)
+       // On entry, x0 points to the destination; x1 points to a packed
+       // operand U; x2 points to a packed operand X (the modulus); v2/v3
+       // holds an expanded operand V; and v6/v7 holds an expanded operand M
+       // (the Montgomery factor -N^{-1} (mod B)).  On exit, the destination
+       // is updated (to zero); v4/v5 hold an expanded factor Y = U V M (mod
+       // B); v28--v30 and x15 hold outgoing carries c0--c2 and cg; x0, x1,
+       // and x2 are each advanced by 16; v2, v3, and v8--v15 are preserved;
+       // and x11--x14, x16, x17 and the other SIMD registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       ldr     q0, [x1], #16
+       ldr     q1, [x2], #16
+
+       // Calculate the low half of W = A + U V, being careful to leave the
+       // carries in place.
+       mul0    v28, nil,  v0,  v2, v3
+       mul1    v29, nil,  v0,  v2, v3
+        carry0 v28, nil
+       mul2    v30, nil,  v0,  v2, v3
+        carry1 x11
+        carry0 v29
+       mul3    v27, nil,  v0,  v2, v3
+       b       mmla4_common
+ENDFUNC
+
+INTFUNC(mmla4)
+       // On entry, x0 points to the destination/accumulator A; x1 points to
+       // a packed operand U; x2 points to a packed operand X (the modulus);
+       // v2/v3 holds an expanded operand V; and v6/v7 holds an expanded
+       // operand M (the Montgomery factor -N^{-1} (mod B)).  On exit, the
+       // accumulator is updated (to zero); v4/v5 hold an expanded factor Y
+       // = (A + U V) M (mod B); v28--v30 and x15 hold outgoing carries
+       // c0--c2 and cg; x0, x1, and x2 are each advanced by 16; v2, v3, v6,
+       // v7, and v8--v15 are preserved; and x11--x14, x16, x17 and the
+       // other SIMD registers are clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       ldr     q28, [x0]
+       ldr     q0, [x1], #16
+       ldr     q1, [x2], #16
+       sprdacc v28, v29, v30, v27
+
+       // Calculate the low half of W = A + U V, being careful to leave the
+       // carries in place.
+       mul0    v28, t,    v0,  v2, v3
+       mul1    v29, t,    v0,  v2, v3
+        carry0 v28, nil
+       mul2    v30, t,    v0,  v2, v3
+        carry1 x11
+        carry0 v29
+       mul3    v27, t,    v0,  v2, v3
+mmla4_common:
+        carry1 x13
+        carry0 v30
+        carry1 x12
+        carry0 v27
+        carry1 x14, nil
+
+       // Piece the result together and ship it back.
+       bfi     x11, x13, #32, #32
+       bfi     x12, x14, #32, #32
+       mov     v16.d[0], x11
+       mov     v16.d[1], x12
+
+       // Calculate the low half of the Montgomery factor Y = W M.
+       mul0    v18, nil,    v16,  v6, v7
+       mul1    v19, nil,    v16,  v6, v7
+        carry0 v18, nil
+       mul2    v20, nil,    v16,  v6, v7
+        carry1 x11
+        carry0 v19
+       mul3    v21, nil,    v16,  v6, v7
+        carry1 x13
+        carry0 v20
+        carry1 x12
+        carry0 v21
+        carry1 x14, nil
+
+       // Piece the result together, ship it back, and expand.
+       bfi     x11, x13, #32, #32
+       bfi     x12, x14, #32, #32
+       mov     v4.d[0], x11
+       mov     v4.d[1], x12
+       expand  v4, v5
+
+       // Build up the product X Y in the carry slots.
+       mul0    v28, t,                      v1,  v4, v5
+       mul1    v29, t,                      v1,  v4, v5
+        carry0 v28, nil
+       mul2    v30, t,                      v1,  v4, v5
+        carry1 nil
+        carry0 v29
+       mul3    v27, t,                      v1,  v4, v5
+        carry1 nil
+        carry0 v30
+
+       // And complete the calculation.
+       mul4    v28, nil,    v0,  v2, v3,    v1,  v4, v5
+        carry1 nil
+        carry0 v27
+       mul5    v29, nil,    v0,  v2, v3,    v1,  v4, v5
+        carry1 nil
+       mul6    v30, nil,    v0,  v2, v3,    v1,  v4, v5
+
+       // Finish up and store the result.
+       stp     xzr, xzr, [x0], #16
+
+       // All done.
+       ret
+ENDFUNC
+
+INTFUNC(mont4)
+       // On entry, x0 points to the destination/accumulator A; x2 points to
+       // a packed operand X (the modulus); and v6/v7 holds an expanded
+       // operand M (the Montgomery factor -N^{-1} (mod B)).  On exit, the
+       // accumulator is updated (to zero); v4/v5 hold an expanded factor Y
+       // = A M (mod B); v28--v30 and x15 hold outgoing carries c0--c2 and
+       // cg; x0 and x2 are each advanced by 16; v6, v7, and v8--v15 are
+       // preserved; and x11--x14, x16, x17 and the other SIMD registers are
+       // clobbered.
+  endprologue
+
+       // Start by loading the operand words from memory.
+       ldr     q28, [x0]
+       ldr     q1, [x2], #16
+
+       // Calculate Y = A M (mod B).
+       mul0    v18, nil,    v28,  v6, v7
+       mul1    v19, nil,    v28,  v6, v7
+        carry0 v18, nil
+       mul2    v20, nil,    v28,  v6, v7
+        carry1 x11
+        carry0 v19
+       mul3    v21, nil,    v28,  v6, v7
+        carry1 x13
+        carry0 v20
+         sprdacc v28, v29, v30, v27
+        carry1 x12
+        carry0 v21
+        carry1 x14, nil
+
+       // Piece the result together, ship it back, and expand.
+       bfi     x11, x13, #32, #32
+       bfi     x12, x14, #32, #32
+       mov     v4.d[0], x11
+       mov     v4.d[1], x12
+       expand  v4, v5
+
+       // Calculate the actual result.  Well, the carries, at least.
+       mul0    v28, t,      v1,  v4, v5
+       mul1    v29, t,      v1,  v4, v5
+        carry0 v28, nil
+       mul2    v30, t,      v1,  v4, v5
+        carry1 nil
+        carry0 v29
+       mul3    v27, t,      v1,  v4, v5
+        carry1 nil
+        carry0 v30
+
+       // And complete the calculation.
+       mul4    v28, nil,    v1,  v4, v5
+        carry1 nil
+        carry0 v27
+       mul5    v29, nil,    v1,  v4, v5
+        carry1 nil
+       mul6    v30, nil,    v1,  v4, v5
+
+       // Finish up and store the result.
+       stp     xzr, xzr, [x0], #16
+
+       // All done.
+       ret
+ENDFUNC
+
+///--------------------------------------------------------------------------
+/// Bulk multipliers.
+
+FUNC(mpx_umul4_arm64_simd)
+       // void mpx_umul4_arm64_simd(mpw *dv, const mpw *av, const mpw *avl,
+       //                           const mpw *bv, const mpw *bvl);
+
+       // Establish the arguments and do initial setup.
+       //
+       // inner loop dv        x0
+       // inner loop av        x2
+       // outer loop dv        x5
+       // outer loop bv        x3
+       // av base              x1
+       // inner n              x6
+       // n base               x7
+       // outer n              x4
+       pushreg x29, x30
+       setfp
+  endprologue
+
+       // Prepare for the first iteration.
+       ldr     q4, [x3], #16           // Y = bv[0]
+       movi    v31.4s, #0
+       sub     x7, x2, x1              // = inner loop count base
+       // x0                           // = dv for inner loop
+       // x1                           // = av base
+       // x3                           // = bv for outer loop
+       sub     x4, x4, x3              // = outer loop count (decremented)
+       sub     x6, x7, #16             // = inner loop count (decremented)
+       mov     x2, x1                  // = av for inner loop
+       add     x5, x0, #16             // = dv for outer loop
+       expand  v4, v5                  // expand Y
+       bl      mul4zc
+       cbz     x6, 8f                  // all done?
+
+       // Continue with the first iteration.
+0:     sub     x6, x6, #16
+       bl      mul4
+       cbnz    x6, 0b
+
+       // Write out the leftover carry.  There can be no tail here.
+8:     bl      carryprop
+       cbz     x4, 9f
+
+       // Set up for the next pass.
+1:     ldr     q4, [x3], #16           // Y = bv[i]
+       mov     x0, x5                  // -> dv[i]
+       mov     x2, x1                  // -> av[0]
+       add     x5, x5, #16
+       sub     x6, x7, #16             // = inner loop count (decremented)
+       sub     x4, x4, #16             // outer loop count
+       expand  v4, v5                  // expand Y
+       bl      mla4zc
+       cbz     x6, 8f
+
+       // Continue...
+0:     sub     x6, x6, #16
+       bl      mla4
+       cbnz    x6, 0b
+
+       // Finish off this pass.  There was no tail on the previous pass, and
+       // there can be done on this pass.
+8:     bl      carryprop
+       cbnz    x4, 1b
+
+       // All over.
+9:     popreg  x29, x30
+       ret
+ENDFUNC
+
+FUNC(mpxmont_mul4_arm64_simd)
+       // void mpxmont_mul4_arm64_simd(mpw *dv,
+       //                              const mpw *av, const mpw *bv,
+       //                              const mpw *nv, size_t n,
+       //                              const mpw *mi);
+
+       // Establish the arguments and do initial setup.
+       //
+       // inner loop dv        x0
+       // inner loop av        x1
+       // inner loop nv        x2
+       // nv base              x3
+       // base n               x4
+       // mi                   (x5)
+       // outer loop dv        x5
+       // outer loop bv        x6
+       // av base              x7
+       // inner n              x8
+       // outer n              x9
+       // c                    x10
+       pushreg x29, x30
+       setfp
+  endprologue
+
+       // Set up the outer loop state and prepare for the first iteration.
+       ldr     q2, [x2]                // = V = bv[0]
+       ldr     q6, [x5]                // = M
+       movi    v31.4s, #0
+       // x0                           // -> dv for inner loop
+       // x1                           // -> av for inner loop
+       // x3                           // -> nv base
+       // x4                           // = n base
+       add     x5, x0, #16             // -> dv
+       add     x6, x2, #16             // -> bv
+       mov     x2, x3                  // -> nv[0]
+       mov     x7, x1                  // -> av base
+       sub     x8, x4, #4              // = inner n (decremented)
+       sub     x9, x4, #4              // = outer n (decremented)
+       expand  v2, v3                  // expand V
+       expand  v6, v7                  // expand M
+       bl      mmul4
+       cbz     x8, 8f                  // done already?
+
+       // Complete the first inner loop.
+0:     sub     x8, x8, #4
+       bl      dmul4
+       cbnz    x8, 0b                  // done yet?
+
+       // Still have carries left to propagate.  Rather than store the tail
+       // end in memory, keep it in x10 for later.
+       bl      carryprop
+
+       // Embark on the next iteration.  (There must be one.  If n = 1 then
+       // we would have bailed above, to label 8.  Similarly, the subsequent
+       // iterations can fall into the inner loop immediately.)
+1:     ldr     q2, [x6], #16           // = Y = bv[i]
+       mov     x0, x5                  // -> dv[i]
+       mov     x1, x7                  // -> av[0]
+       mov     x2, x3                  // -> nv[0]
+       add     x5, x5, #16
+       sub     x8, x4, #4
+       sub     x9, x9, #4
+       expand  v2, v3
+       bl      mmla4
+
+       // Complete the next inner loop.
+0:     sub     x8, x8, #4
+       bl      dmla4
+       cbnz    x8, 0b
+
+       // Still have carries left to propagate, and they overlap the
+       // previous iteration's final tail, so read that and add it.
+       add     x15, x15, x10, lsl #16
+       bl      carryprop
+
+       // Back again, maybe.
+       cbnz    x9, 1b
+
+       // All done, almost.
+       str     w10, [x0], #4
+       popreg  x29, x30
+       ret
+
+       // First iteration was short.  Write out the carries and we're done.
+       // (This could be folded into the main loop structure, but that would
+       // penalize small numbers more.)
+8:     bl      carryprop
+       str     w10, [x0], #4
+       popreg  x29, x30
+       ret
+ENDFUNC
+
+FUNC(mpxmont_redc4_arm64_simd)
+       // void mpxmont_redc4_arm64_simd(mpw *dv, mpw *dvl, const mpw *nv,
+       //                               size_t n, const mpw *mi);
+
+       // Establish the arguments and do initial setup.
+       //
+       // inner loop dv        x0
+       // inner loop nv        x2
+       // blocks-of-4 count    x6
+       // tail count           x7
+       // mi                   (x4)
+       // outer loop dv        x4
+       // outer loop count     x8
+       // nv base              x5
+       // inner loop count     x1
+       // n                    x3
+       // c                    x10
+       // t0, t1               x11, x12
+
+       pushreg x29, x30
+       setfp
+  endprologue
+
+       // Set up the outer loop state and prepare for the first iteration.
+       ldr     q6, [x4]                // = M
+       movi    v31.4s, #0
+       // x0                           // -> dv for inner loop
+        sub    x6, x1, x0              // total dv bytes
+       sub     x1, x3, #4              // inner loop counter
+       // x2                           // -> nv for inner loop
+       // x3                           // = n
+       add     x4, x0, #16             // -> dv for outer loop
+       mov     x5, x2                  // -> nv base
+        sub    x6, x6, x3, lsl #2      // dv carry range bytes
+       sub     x8, x3, #4              // outer loop counter
+        sub    x6, x6, #16             // dv steam-powered carry bytes
+       expand  v6, v7                  // expand M
+       and     x7, x6, #15             // dv tail length in bytes
+       bic     x6, x6, #15             // dv blocks-of-four length in bytes
+
+       bl      mont4
+       cbz     x1, 8f                  // done already?
+
+5:     sub     x1, x1, #4
+       bl      mla4
+       cbnz    x1, 5b                  // done yet?
+
+       // Still have carries left to propagate.  Adding the accumulator
+       // block into the carries is a little different this time, because
+       // all four accumulator limbs have to be squished into the three
+       // carry registers for `carryprop' to do its thing.
+8:     ldr     q24, [x0]
+       sprdacc v24, v25, v26
+       add     v28.2d, v28.2d, v24.2d
+       add     v29.2d, v29.2d, v25.2d
+       add     v30.2d, v30.2d, v26.2d
+       bl      carryprop
+       cbz     x6, 7f
+
+       // Propagate the first group of carries.
+       ldp     x16, x17, [x0]
+       sub     x1, x6, #16
+       adds    x16, x16, x10
+       adcs    x17, x17, xzr
+       stp     x16, x17, [x0], #16
+       cbz     x1, 6f
+
+       // Continue carry propagation until the end of the buffer.
+0:     ldp     x16, x17, [x0]
+       sub     x1, x1, #16
+       adcs    x16, x16, xzr
+       adcs    x17, x17, xzr
+       stp     x16, x17, [x0], #16
+       cbnz    x1, 0b
+
+       // Deal with the tail end.  Note that the actual destination length
+       // won't be an exacty number of blocks of four, so it's safe to just
+       // drop through here.
+6:     adc     w10, wzr, wzr
+7:     ldr     w16, [x0]
+       sub     x1, x7, #4
+       adds    w16, w16, w10
+       str     w16, [x0], #4
+       cbz     x1, 8f
+0:     ldr     w16, [x0]
+       sub     x1, x1, #4
+       adcs    w16, w16, wzr
+       str     w16, [x0], #4
+       cbnz    x1, 0b
+
+       // All done for this iteration.  Start the next.
+8:     cbz     x8, 9f
+       mov     x0, x4
+       add     x4, x4, #16
+       sub     x1, x3, #4
+       mov     x2, x5
+       sub     x8, x8, #4
+       sub     x6, x6, #16
+       bl      mont4
+       b       5b
+
+       // All over.
+9:     popreg  x29, x30
+       ret
+ENDFUNC
+
+///--------------------------------------------------------------------------
+/// Testing and performance measurement.
+
+#ifdef TEST_MUL4
+
+//               dmul  smul  mmul  mont
+// z   x0        x0    x0    x0     x0
+// c   x3        x1    x1    x1     x1
+// y   x4        --    --    x2     x2
+// u   x1        x2    --    x3     --
+// x   x2        x3    x2    x4     x3
+// vv  v2/v3     x4    --    x5     --
+// yy  v4/v5     x5    x3    x6     --
+// mm  v6/v7     --    --    --     x4
+// n   x5        x6    x4    x7     x5
+// cyv x6        x7    x5    stk0   x6
+
+#define STKARG(i) sp, #16 + i
+
+.macro testprologue mode
+       pushreg x29, x30
+       setfp
+  endprologue
+       movi    v31.4s, #0
+
+  .ifeqs "\mode", "dmul"
+       ldr     q2, [x4]
+       zip2    v3.8h, v2.8h, v31.8h    // (v'_2, v''_2; v'_3, v''_3)
+       zip1    v2.8h, v2.8h, v31.8h    // (v'_0, v''_0; v'_1, v''_1)
+
+       ldr     q4, [x5]
+       zip2    v5.8h, v4.8h, v31.8h    // (y'_2, y''_2; y'_3, y''_3)
+       zip1    v4.8h, v4.8h, v31.8h    // (y'_0, y''_0; y'_1, y''_1)
+
+       mov     x16, x1
+       mov     x1, x2                  // -> u
+       mov     x2, x3                  // -> x
+       mov     x3, x16                 // -> c
+
+       mov     x5, x6                  // = n
+       mov     x6, x7                  // -> cyv
+  .endif
+
+  .ifeqs "\mode", "smul"
+       ldr     q4, [x3]
+       zip2    v5.8h, v4.8h, v31.8h    // (y'_2, y''_2; y'_3, y''_3)
+       zip1    v4.8h, v4.8h, v31.8h    // (y'_0, y''_0; y'_1, y''_1)
+
+       // x2                           // -> x
+       mov     x3, x1                  // -> c
+       mov     x6, x5                  // -> cyv
+       mov     x5, x4                  // = n
+  .endif
+
+  .ifeqs "\mode", "mmul"
+       ldr     q2, [x5]
+       zip2    v3.8h, v2.8h, v31.8h    // (v'_2, v''_2; v'_3, v''_3)
+       zip1    v2.8h, v2.8h, v31.8h    // (v'_0, v''_0; v'_1, v''_1)
+
+       ldr     q6, [x6]
+       zip2    v7.8h, v6.8h, v31.8h    // (y'_2, y''_2; y'_3, y''_3)
+       zip1    v6.8h, v6.8h, v31.8h    // (y'_0, y''_0; y'_1, y''_1)
+
+       mov     x16, x1
+       mov     x1, x3                  // -> u
+       mov     x3, x16                 // -> c
+
+       mov     x16, x2
+       mov     x2, x4                  // -> x
+       mov     x4, x16                 // -> y
+
+       mov     x5, x7                  // = n
+       ldr     x6, [STKARG(0)]         // -> cyv
+  .endif
+
+  .ifeqs "\mode", "mont"
+       ldr     q6, [x4]
+       zip2    v7.8h, v6.8h, v31.8h    // (m'_2, m''_2; m'_3, m''_3)
+       zip1    v6.8h, v6.8h, v31.8h    // (m'_0, m''_0; m'_1, m''_1)
+
+       mov     x4, x2                  // -> y
+       mov     x2, x3                  // -> x
+       mov     x3, x1                  // -> c
+
+       // x5                           // = n
+       // x6                           // -> cyv
+  .endif
+.endm
+
+.macro testldcarry
+       ld1     {v28.2d-v30.2d}, [x3]
+       mov     x15, #0
+.endm
+
+.macro testtop
+0:     sub     x5, x5, #1
+.endm
+
+.macro testtail
+       cbnz    x5, 0b
+.endm
+
+.macro testcarryout
+       // More complicated than usual because we must mix the general-
+       // purpose carry back in.
+       lsr     x15, x15, #16
+       mov     v0.d[0], x15
+       mov     v0.d[1], xzr
+       add     v28.2d, v28.2d, v0.2d
+       st1     {v28.2d-v30.2d}, [x3]
+.endm
+
+.macro testepilogue
+       popreg  x29, x30
+       ret
+.endm
+
+FUNC(test_dmul4)
+       testprologue dmul
+       testldcarry
+       testtop
+       bl      dmul4
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_dmla4)
+       testprologue dmul
+       testldcarry
+       testtop
+       bl      dmla4
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mul4)
+       testprologue smul
+       testldcarry
+       testtop
+       bl      mul4
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mul4zc)
+       testprologue smul
+       testldcarry
+       testtop
+       bl      mul4zc
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mla4)
+       testprologue smul
+       testldcarry
+       testtop
+       bl      mla4
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mla4zc)
+       testprologue smul
+       testldcarry
+       testtop
+       bl      mla4zc
+       testtail
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mmul4)
+       testprologue mmul
+       testtop
+       bl      mmul4
+       testtail
+       stp     q4, q5, [x4]
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mmla4)
+       testprologue mmul
+       testtop
+       bl      mmla4
+       testtail
+       stp     q4, q5, [x4]
+       testcarryout
+       testepilogue
+ENDFUNC
+
+FUNC(test_mont4)
+       testprologue mont
+       testtop
+       bl      mont4
+       testtail
+       stp     q4, q5, [x4]
+       testcarryout
+       testepilogue
+ENDFUNC
+
+#endif
+
+///----- That's all, folks --------------------------------------------------
index 414974b..5325db0 100644 (file)
 
 #include "config.h"
 
+#ifdef ENABLE_ASM_DEBUG
+#  include "regdump.h"
+#endif
+
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -57,6 +61,18 @@ static int cpu_features_p(void) { return (cpu_feature_p(CPUFEAT_X86_SSE2)); }
 static int cpu_features_p(void) { return (cpu_feature_p(CPUFEAT_X86_SSE2)); }
 #endif
 
+#if CPUFAM_ARMEL
+#  define VARIANT _arm_neon
+#  define REPR_32
+static int cpu_features_p(void) { return (cpu_feature_p(CPUFEAT_ARM_NEON)); }
+#endif
+
+#if CPUFAM_ARM64
+#  define VARIANT _arm64_simd
+#  define REPR_32
+static int cpu_features_p(void) { return (1); }
+#endif
+
 #ifndef VARIANT
 #  error "Unsupported CPU family."
 #endif
@@ -107,7 +123,37 @@ TESTOPS(DECLSTUB)
 
 /*----- Conversion functions ----------------------------------------------*/
 
-#define DEFTYPE(ty, ld, st, nby)                                       \
+static mp *combine_mpw(mp *d, const mpw *v, size_t n, unsigned off)
+{
+  size_t i;
+  unsigned o;
+  mp m, *t = d;
+  mpw w[1];
+
+  d = MP_ZERO;
+  for (i = 0, o = 0; i < n; i++, o += off) {
+    w[0] = v[i]; mp_build(&m, w, w + 1);
+    t = mp_lsl(t, &m, o); d = mp_add(d, d, t);
+  }
+  mp_drop(t); return (d);
+}
+
+static mp *combine_mpd(mp *d, const mpd *v, size_t n, unsigned off)
+{
+  size_t i;
+  unsigned o;
+  mp m, *t = d;
+  mpw w[2];
+
+  d = MP_ZERO;
+  for (i = 0, o = 0; i < n; i++, o += off) {
+    w[0] = MPW(v[i]); w[1] = MPW(v[i] >> MPW_BITS); mp_build(&m, w, w + 2);
+    t = mp_lsl(t, &m, o); d = mp_add(d, d, t);
+  }
+  mp_drop(t); return (d);
+}
+
+#define DEFTYPE(ty, ld, st, nby, combfn, off)                          \
                                                                        \
   static void cvt_##ty(const char *buf, dstr *d)                       \
   {                                                                    \
@@ -129,6 +175,7 @@ TESTOPS(DECLSTUB)
     dstr dd = DSTR_INIT;                                               \
     int i;                                                             \
     const ty *x = (const ty *)d->buf;                                  \
+    mp *xx = combfn(MP_NEW, x->w, N(x->w), off);                       \
     octet *p;                                                          \
                                                                        \
     dstr_ensure(&dd, N(x->w)*nby); p = (octet *)dd.buf;                        \
@@ -136,22 +183,26 @@ TESTOPS(DECLSTUB)
     dd.len = N(x->w)*nby;                                              \
     type_hex.dump(&dd, fp);                                            \
     dstr_destroy(&dd);                                                 \
+                                                                       \
+    fputs(" = 0x", fp); mp_writefile(xx, fp, 16);                      \
+    fputs(" = ", fp); mp_writefile(xx, fp, 10);                                \
+    MP_DROP(xx);                                                       \
   }                                                                    \
                                                                        \
   static int eq_##ty(const ty *x, const ty *y)                         \
   {                                                                    \
-    int i;                                                             \
-                                                                       \
-    for (i = 0; i < N(x->w); i++)                                      \
-      if (x->w[i] != y->w[i]) return (0);                              \
-    return (1);                                                                \
+    mp *xx = combfn(MP_NEW, x->w, N(x->w), off),                       \
+      *yy = combfn(MP_NEW, y->w, N(y->w), off);                                \
+    int rc = MP_EQ(xx, yy);                                            \
+    MP_DROP(xx); MP_DROP(yy);                                          \
+    return (rc);                                                       \
   }                                                                    \
                                                                        \
   static const struct test_type type_##ty = { cvt_##ty, dump_##ty };
 
-DEFTYPE(p128, LDW, STW, NWBY)
-DEFTYPE(x128, LDW, STW, NWBY)
-DEFTYPE(carry, LDD, STD, NDBY)
+DEFTYPE(p128, LDW, STW, NWBY, combine_mpw, MPW_BITS)
+DEFTYPE(x128, LDW, STW, NWBY, combine_mpw, MPW_BITS/2)
+DEFTYPE(carry, LDD, STD, NDBY, combine_mpd, MPW_BITS/2)
 
 /*----- Test functions ----------------------------------------------------*/
 
@@ -284,6 +335,9 @@ static test_chunk tests[] = {
 int main(int argc, char *argv[])
 {
   sub_init();
+#ifdef ENABLE_ASM_DEBUG
+  regdump_init();
+#endif
   if (!cpu_features_p())
     { fprintf(stderr, "required cpu feature not available\n"); exit(77); }
   test_run(argc, argv, tests, SRCDIR "/t/mpx-mul4");
index 904c0d0..916adef 100644 (file)
 /// construct more general variable-length multipliers.
 ///
 /// The basic trick is the same throughout.  In an operand-scanning
-/// multiplication, the inner multiplication loop multiplies a
-/// multiple-precision operand by a single precision factor, and adds the
-/// result, appropriately shifted, to the result.  A `finely integrated
-/// operand scanning' implementation of Montgomery multiplication also adds
-/// the product of a single-precision `Montgomery factor' and the modulus,
+/// multiplication, the inner multiplication loop multiplies a multiple-
+/// precision operand by a single precision factor, and adds the result,
+/// appropriately shifted, to the result.  A `finely integrated operand
+/// scanning' implementation of Montgomery multiplication also adds the
+/// product of a single-precision `Montgomery factor' and the modulus,
 /// calculated in the same pass.  The more common `coarsely integrated
 /// operand scanning' alternates main multiplication and Montgomery passes,
 /// which requires additional carry propagation.
 /// many products together before we must deal with carrying; it also allows
 /// for some calculations to be performed on the above expanded form.
 ///
+/// We maintain four `carry' registers XMM4--XMM7 accumulating intermediate
+/// results.  The registers' precise roles rotate during the computation; we
+/// name them `c0', `c1', `c2', and `c3'.  Each carry register holds two
+/// 64-bit halves: the register c0, for example, holds c'_0 (low half) and
+/// c''_0 (high half), and represents the value c_0 = c'_0 + c''_0 b; the
+/// carry registers collectively represent the value c_0 + c_1 B + c_2 B^2 +
+/// c_3 B^3.  The `pmuluqd' instruction acting on a scalar operand (broadcast
+/// across all lanes of its vector) and an operand in the expanded form above
+/// produces a result which can be added directly to the appropriate carry
+/// register.  Following a pass of four multiplications, we perform some
+/// limited carry propagation: let t = c''_0 mod B, and let d = c'_0 + t b;
+/// then we output z = d mod B, add (floor(d/B), floor(c''_0/B)) to c1, and
+/// cycle the carry registers around, so that c1 becomes c0, and the old
+/// (implicitly) zeroed c0 becomes c3.
+///
 /// On 32-bit x86, we are register starved: the expanded operands are kept in
-/// memory, typically in warm L1 cache.
+/// memory, typically in warm L1 cache.  The packed operands are read from
+/// memory into working registers XMM0--XMM3 and processed immediately.
+/// The following conventional argument names and locations are used
+/// throughout.
+///
+/// Arg        Format      Location    Notes
+///
+/// U  packed      [EAX]
+/// X  packed      [EBX]       In Montgomery multiplication, X = N
+/// V  expanded    [ECX]
+/// Y  expanded    [EDX]       In Montgomery multiplication, Y = (A + U V) M
+/// M  expanded    [ESI]       -N^{-1} (mod B^4)
+/// N                          Modulus, for Montgomery multiplication
+/// A  packed      [EDI]       Destination/accumulator
+/// C  carry       XMM4--XMM7
+///
+/// The calculation is some variant of
+///
+///    A' + C' B^4 <- U V + X Y + A + C
+///
+/// The low-level functions fit into a fairly traditional (finely-integrated)
+/// operand scanning loop over operand pairs (U, X) (indexed by j) and (V, Y)
+/// (indexed by i).
 ///
-/// We maintain four `carry' registers accumulating intermediate results.
-/// The registers' precise roles rotate during the computation; we name them
-/// `c0', `c1', `c2', and `c3'.  Each carry register holds two 64-bit halves:
-/// the register c0, for example, holds c'_0 (low half) and c''_0 (high
-/// half), and represents the value c_0 = c'_0 + c''_0 b; the carry registers
-/// collectively represent the value c_0 + c_1 B + c_2 B^2 + c_3 B^3.  The
-/// `pmuluqd' instruction acting on a scalar operand (broadcast across all
-/// lanes of its vector) and an operand in the expanded form above produces a
-/// result which can be added directly to the appropriate carry register.
-/// Following a pass of four multiplications, we perform some limited carry
-/// propagation: let t = c''_0 mod B, and let d = c'_0 + t b; then we output
-/// z = d mod B, add (floor(d/B), floor(c''_0/B)) to c1, and cycle the carry
-/// registers around, so that c1 becomes c0, and the old c0 is (implicitly)
-/// zeroed becomes c3.
+/// The variants are as follows.
+///
+/// Function   Variant                 Use             i       j
+///
+/// mmul4      A = C = 0               Montgomery      0       0
+/// dmul4      A = 0                   Montgomery      0       +
+/// mmla4      C = 0                   Montgomery      +       0
+/// dmla4      exactly as shown        Montgomery      +       +
+/// mont4      U = V = C = 0           Montgomery      any     0
+///
+/// mul4zc     U = V = A = C = 0       Plain           0       0
+/// mul4       U = V = A = 0           Plain           0       +
+/// mla4zc     U = V = C = 0           Plain           +       0
+/// mla4       U = V = 0               Plain           +       +
+///
+/// The `mmul4' and `mmla4' functions are also responsible for calculating
+/// the Montgomery reduction factor Y = (A + U V) M used by the rest of the
+/// inner loop.
 
 ///--------------------------------------------------------------------------
 /// Macro definitions.
@@ -316,7 +357,6 @@ INTFUNC(carryprop)
        propout [edi +  8], xmm6, nil
        endprop [edi + 12], xmm6, xmm4
        ret
-
 ENDFUNC
 
 INTFUNC(dmul4)
@@ -348,7 +388,6 @@ INTFUNC(dmul4)
        propout [edi + 12],      xmm7, xmm4
 
        ret
-
 ENDFUNC
 
 INTFUNC(dmla4)
@@ -384,7 +423,6 @@ INTFUNC(dmla4)
        propout [edi + 12],      xmm7, xmm4
 
        ret
-
 ENDFUNC
 
 INTFUNC(mul4zc)
@@ -410,7 +448,6 @@ INTFUNC(mul4zc)
        propout [edi + 12],      xmm7, xmm4
 
        ret
-
 ENDFUNC
 
 INTFUNC(mul4)
@@ -438,7 +475,6 @@ INTFUNC(mul4)
        propout [edi + 12],      xmm7, xmm4
 
        ret
-
 ENDFUNC
 
 INTFUNC(mla4zc)
@@ -470,7 +506,6 @@ INTFUNC(mla4zc)
        propout [edi + 12],      xmm7, xmm4
 
        ret
-
 ENDFUNC
 
 INTFUNC(mla4)
@@ -501,7 +536,6 @@ INTFUNC(mla4)
        propout [edi + 12],      xmm7, xmm4
 
        ret
-
 ENDFUNC
 
 INTFUNC(mmul4)
@@ -523,7 +557,6 @@ INTFUNC(mmul4)
        mulcore [eax +  0], ecx, xmm4, xmm5, xmm6, xmm7
        propout [edi +  0],      xmm4, xmm5
        jmp     5f
-
 ENDFUNC
 
 INTFUNC(mmla4)
@@ -561,9 +594,9 @@ INTFUNC(mmla4)
        mulacc  [eax + 12], ecx, xmm7, xmm4, xmm5, xmm6, t
        propout [edi + 12],      xmm7, xmm4
 
-       movdqa  [esp +  0], xmm4
-       movdqa  [esp + 16], xmm5
-       movdqa  [esp + 32], xmm6
+       movdqa  [SP +  0], xmm4
+       movdqa  [SP + 16], xmm5
+       movdqa  [SP + 32], xmm6
 
        // Calculate Y = W M.
        mulcore [edi +  0], esi, xmm4, xmm5, xmm6, xmm7
@@ -606,14 +639,13 @@ INTFUNC(mmla4)
        propout [edi + 12],      xmm7, xmm4
 
        // Add add on the carry we calculated earlier.
-       paddq   xmm4, [esp +  0]
-       paddq   xmm5, [esp + 16]
-       paddq   xmm6, [esp + 32]
+       paddq   xmm4, [SP +  0]
+       paddq   xmm5, [SP + 16]
+       paddq   xmm6, [SP + 32]
 
        // And, with that, we're done.
        stfree  48 + 12
        ret
-
 ENDFUNC
 
 INTFUNC(mont4)
@@ -670,7 +702,6 @@ INTFUNC(mont4)
 
        // And, with that, we're done.
        ret
-
 ENDFUNC
 
 ///--------------------------------------------------------------------------
@@ -688,40 +719,40 @@ FUNC(mpx_umul4_x86_sse2)
        // void mpx_umul4_x86_sse2(mpw *dv, const mpw *av, const mpw *avl,
        //                         const mpw *bv, const mpw *bvl);
 
-       // Build a stack frame.  Arguments will be relative to EBP, as
+       // Build a stack frame.  Arguments will be relative to BP, as
        // follows.
        //
-       //      ebp + 20        dv
-       //      ebp + 24        av
-       //      ebp + 28        avl
-       //      ebp + 32        bv
-       //      ebp + 36        bvl
+       //      BP + 20 dv
+       //      BP + 24 av
+       //      BP + 28 avl
+       //      BP + 32 bv
+       //      BP + 36 bvl
        //
-       // Locals are relative to ESP, as follows.
+       // Locals are relative to SP, as follows.
        //
-       //      esp +  0        expanded Y (32 bytes)
-       //      esp + 32        (top of locals)
-       pushreg ebp
+       //      SP +  0 expanded Y (32 bytes)
+       //      SP + 32 (top of locals)
+       pushreg BP
        pushreg ebx
        pushreg esi
        pushreg edi
        setfp
-       and     esp, ~15
-       sub     esp, 32
+       stalloc 32
+       and     SP, ~15
   endprologue
 
        // Prepare for the first iteration.
-       mov     esi, [ebp + 32]         // -> bv[0]
+       mov     esi, [BP + 32]          // -> bv[0]
        pxor    xmm7, xmm7
        movdqu  xmm0, [esi]             // bv[0]
-       mov     edi, [ebp + 20]         // -> dv[0]
+       mov     edi, [BP + 20]          // -> dv[0]
        mov     ecx, edi                // outer loop dv cursor
        expand  xmm7, xmm0, xmm1
-       mov     ebx, [ebp + 24]         // -> av[0]
-       mov     eax, [ebp + 28]         // -> av[m] = av limit
-       mov     edx, esp                // -> expanded Y = bv[0]
-       movdqa  [esp + 0], xmm0         // bv[0] expanded low
-       movdqa  [esp + 16], xmm1        // bv[0] expanded high
+       mov     ebx, [BP + 24]          // -> av[0]
+       mov     eax, [BP + 28]          // -> av[m] = av limit
+       mov     edx, SP                 // -> expanded Y = bv[0]
+       movdqa  [SP + 0], xmm0          // bv[0] expanded low
+       movdqa  [SP + 16], xmm1         // bv[0] expanded high
        call    mul4zc
        add     ebx, 16
        add     edi, 16
@@ -740,7 +771,7 @@ FUNC(mpx_umul4_x86_sse2)
 
        // Write out the leftover carry.  There can be no tail here.
 8:     call    carryprop
-       cmp     esi, [ebp + 36]         // more passes to do?
+       cmp     esi, [BP + 36]          // more passes to do?
        jae     9f
 
        .p2align 4
@@ -749,9 +780,9 @@ FUNC(mpx_umul4_x86_sse2)
        mov     edi, ecx                // -> dv[i]
        pxor    xmm7, xmm7
        expand  xmm7, xmm0, xmm1
-       mov     ebx, [ebp + 24]         // -> av[0]
-       movdqa  [esp + 0], xmm0         // bv[i] expanded low
-       movdqa  [esp + 16], xmm1        // bv[i] expanded high
+       mov     ebx, [BP + 24]          // -> av[0]
+       movdqa  [SP + 0], xmm0          // bv[i] expanded low
+       movdqa  [SP + 16], xmm1         // bv[i] expanded high
        call    mla4zc
        add     edi, 16
        add     ebx, 16
@@ -771,7 +802,7 @@ FUNC(mpx_umul4_x86_sse2)
        // Finish off this pass.  There was no tail on the previous pass, and
        // there can be none on this pass.
 8:     call    carryprop
-       cmp     esi, [ebp + 36]
+       cmp     esi, [BP + 36]
        jb      1b
 
        // All over.
@@ -779,9 +810,8 @@ FUNC(mpx_umul4_x86_sse2)
        pop     edi
        pop     esi
        pop     ebx
-       pop     ebp
+       pop     BP
        ret
-
 ENDFUNC
 
 FUNC(mpxmont_mul4_x86_avx)
@@ -796,69 +826,69 @@ FUNC(mpxmont_mul4_x86_sse2)
        // void mpxmont_mul4_x86_sse2(mpw *dv, const mpw *av, const mpw *bv,
        //                           const mpw *nv, size_t n, const mpw *mi);
 
-       // Build a stack frame.  Arguments will be relative to EBP, as
+       // Build a stack frame.  Arguments will be relative to BP, as
        // follows.
        //
-       //      ebp + 20        dv
-       //      ebp + 24        av
-       //      ebp + 28        bv
-       //      ebp + 32        nv
-       //      ebp + 36        n (nonzero multiple of 4)
-       //      ebp + 40        mi
+       //      BP + 20 dv
+       //      BP + 24 av
+       //      BP + 28 bv
+       //      BP + 32 nv
+       //      BP + 36 n (nonzero multiple of 4)
+       //      BP + 40 mi
        //
-       // Locals are relative to ESP, which 16-byte aligned, as follows.
+       // Locals are relative to SP, which 16-byte aligned, as follows.
        //
-       //      esp +   0       expanded V (32 bytes)
-       //      esp +  32       expanded M (32 bytes)
-       //      esp +  64       expanded Y (32 bytes)
-       //      esp +  96       outer loop dv
-       //      esp + 100       outer loop bv
-       //      esp + 104       av limit (mostly in ESI)
-       //      esp + 108       bv limit
-       //      esp + 112       (top of locals)
-       pushreg ebp
+       //      SP +   0        expanded V (32 bytes)
+       //      SP +  32        expanded M (32 bytes)
+       //      SP +  64        expanded Y (32 bytes)
+       //      SP +  96        outer loop dv
+       //      SP + 100        outer loop bv
+       //      SP + 104        av limit (mostly in ESI)
+       //      SP + 108        bv limit
+       //      SP + 112        (top of locals)
+       pushreg BP
        pushreg ebx
        pushreg esi
        pushreg edi
        setfp
-       and     esp, ~15
-       sub     esp, 112
+       stalloc 112
+       and     SP, ~15
   endprologue
 
        // Establish the expanded operands.
        pxor    xmm7, xmm7
-       mov     ecx, [ebp + 28]         // -> bv
-       mov     edx, [ebp + 40]         // -> mi
+       mov     ecx, [BP + 28]          // -> bv
+       mov     edx, [BP + 40]          // -> mi
        movdqu  xmm0, [ecx]             // bv[0]
        movdqu  xmm2, [edx]             // mi
        expand  xmm7, xmm0, xmm1, xmm2, xmm3
-       movdqa  [esp +  0], xmm0        // bv[0] expanded low
-       movdqa  [esp + 16], xmm1        // bv[0] expanded high
-       movdqa  [esp + 32], xmm2        // mi expanded low
-       movdqa  [esp + 48], xmm3        // mi expanded high
+       movdqa  [SP +  0], xmm0         // bv[0] expanded low
+       movdqa  [SP + 16], xmm1         // bv[0] expanded high
+       movdqa  [SP + 32], xmm2         // mi expanded low
+       movdqa  [SP + 48], xmm3         // mi expanded high
 
        // Set up the outer loop state and prepare for the first iteration.
-       mov     edx, [ebp + 36]         // n
-       mov     eax, [ebp + 24]         // -> U = av[0]
-       mov     ebx, [ebp + 32]         // -> X = nv[0]
-       mov     edi, [ebp + 20]         // -> Z = dv[0]
-       mov     [esp + 100], ecx
+       mov     edx, [BP + 36]          // n
+       mov     eax, [BP + 24]          // -> U = av[0]
+       mov     ebx, [BP + 32]          // -> X = nv[0]
+       mov     edi, [BP + 20]          // -> Z = dv[0]
+       mov     [SP + 100], ecx
        lea     ecx, [ecx + 4*edx]      // -> bv[n/4] = bv limit
        lea     edx, [eax + 4*edx]      // -> av[n/4] = av limit
-       mov     [esp + 96], edi
-       mov     [esp + 104], edx
-       mov     [esp + 108], ecx
-       lea     ecx, [esp + 0]          // -> expanded V = bv[0]
-       lea     esi, [esp + 32]         // -> expanded M = mi
-       lea     edx, [esp + 64]         // -> space for Y
+       mov     [SP + 96], edi
+       mov     [SP + 104], edx
+       mov     [SP + 108], ecx
+       lea     ecx, [SP + 0]           // -> expanded V = bv[0]
+       lea     esi, [SP + 32]          // -> expanded M = mi
+       lea     edx, [SP + 64]          // -> space for Y
        call    mmul4
-       mov     esi, [esp + 104]        // recover av limit
+       mov     esi, [SP + 104]         // recover av limit
        add     edi, 16
        add     eax, 16
        add     ebx, 16
        cmp     eax, esi                // done already?
        jae     8f
-       mov     [esp + 96], edi
+       mov     [SP + 96], edi
 
        .p2align 4
        // Complete the first inner loop.
@@ -877,26 +907,26 @@ FUNC(mpxmont_mul4_x86_sse2)
        // Embark on the next iteration.  (There must be one.  If n = 1, then
        // we would have bailed above, to label 8.  Similarly, the subsequent
        // iterations can fall into the inner loop immediately.)
-1:     mov     eax, [esp + 100]        // -> bv[i - 1]
-       mov     edi, [esp + 96]         // -> Z = dv[i]
+1:     mov     eax, [SP + 100]         // -> bv[i - 1]
+       mov     edi, [SP + 96]          // -> Z = dv[i]
        add     eax, 16                 // -> bv[i]
        pxor    xmm7, xmm7
-       mov     [esp + 100], eax
-       cmp     eax, [esp + 108]        // done yet?
+       mov     [SP + 100], eax
+       cmp     eax, [SP + 108]         // done yet?
        jae     9f
        movdqu  xmm0, [eax]             // bv[i]
-       mov     ebx, [ebp + 32]         // -> X = nv[0]
-       lea     esi, [esp + 32]         // -> expanded M = mi
-       mov     eax, [ebp + 24]         // -> U = av[0]
+       mov     ebx, [BP + 32]          // -> X = nv[0]
+       lea     esi, [SP + 32]          // -> expanded M = mi
+       mov     eax, [BP + 24]          // -> U = av[0]
        expand  xmm7, xmm0, xmm1
-       movdqa  [esp + 0], xmm0         // bv[i] expanded low
-       movdqa  [esp + 16], xmm1        // bv[i] expanded high
+       movdqa  [SP + 0], xmm0          // bv[i] expanded low
+       movdqa  [SP + 16], xmm1         // bv[i] expanded high
        call    mmla4
-       mov     esi, [esp + 104]        // recover av limit
+       mov     esi, [SP + 104]         // recover av limit
        add     edi, 16
        add     eax, 16
        add     ebx, 16
-       mov     [esp + 96], edi
+       mov     [SP + 96], edi
 
        .p2align 4
        // Complete the next inner loop.
@@ -928,9 +958,8 @@ FUNC(mpxmont_mul4_x86_sse2)
        popreg  edi
        popreg  esi
        popreg  ebx
-       popreg  ebp
+       popreg  BP
        ret
-
 ENDFUNC
 
 FUNC(mpxmont_redc4_x86_avx)
@@ -945,55 +974,55 @@ FUNC(mpxmont_redc4_x86_sse2)
        // void mpxmont_redc4_x86_sse2(mpw *dv, mpw *dvl, const mpw *nv,
        //                             size_t n, const mpw *mi);
 
-       // Build a stack frame.  Arguments will be relative to EBP, as
+       // Build a stack frame.  Arguments will be relative to BP, as
        // follows.
        //
-       //      ebp + 20        dv
-       //      ebp + 24        dvl
-       //      ebp + 28        nv
-       //      ebp + 32        n (nonzero multiple of 4)
-       //      ebp + 36        mi
+       //      BP + 20 dv
+       //      BP + 24 dvl
+       //      BP + 28 nv
+       //      BP + 32 n (nonzero multiple of 4)
+       //      BP + 36 mi
        //
-       // Locals are relative to ESP, as follows.
+       // Locals are relative to SP, as follows.
        //
-       //      esp +  0        outer loop dv
-       //      esp +  4        outer dv limit
-       //      esp +  8        blocks-of-4 dv limit
-       //      esp + 12        expanded M (32 bytes)
-       //      esp + 44        expanded Y (32 bytes)
-       //      esp + 76        (top of locals)
-       pushreg ebp
+       //      SP +  0 outer loop dv
+       //      SP +  4 outer dv limit
+       //      SP +  8 blocks-of-4 dv limit
+       //      SP + 12 expanded M (32 bytes)
+       //      SP + 44 expanded Y (32 bytes)
+       //      SP + 76 (top of locals)
+       pushreg BP
        pushreg ebx
        pushreg esi
        pushreg edi
        setfp
-       and     esp, ~15
-       sub     esp, 76
+       and     SP, ~15
+       stalloc 76
   endprologue
 
        // Establish the expanded operands and the blocks-of-4 dv limit.
-       mov     edi, [ebp + 20]         // -> Z = dv[0]
+       mov     edi, [BP + 20]          // -> Z = dv[0]
        pxor    xmm7, xmm7
-       mov     eax, [ebp + 24]         // -> dv[n] = dv limit
+       mov     eax, [BP + 24]          // -> dv[n] = dv limit
        sub     eax, edi                // length of dv in bytes
-       mov     edx, [ebp + 36]         // -> mi
+       mov     edx, [BP + 36]          // -> mi
        movdqu  xmm0, [edx]             // mi
        and     eax, ~15                // mask off the tail end
        expand  xmm7, xmm0, xmm1
        add     eax, edi                // find limit
-       movdqa  [esp + 12], xmm0        // mi expanded low
-       movdqa  [esp + 28], xmm1        // mi expanded high
-       mov     [esp + 8], eax
+       movdqa  [SP + 12], xmm0         // mi expanded low
+       movdqa  [SP + 28], xmm1         // mi expanded high
+       mov     [SP + 8], eax
 
        // Set up the outer loop state and prepare for the first iteration.
-       mov     ecx, [ebp + 32]         // n
-       mov     ebx, [ebp + 28]         // -> X = nv[0]
+       mov     ecx, [BP + 32]          // n
+       mov     ebx, [BP + 28]          // -> X = nv[0]
        lea     edx, [edi + 4*ecx]      // -> dv[n/4] = outer dv limit
        lea     ecx, [ebx + 4*ecx]      // -> nv[n/4] = nv limit
-       mov     [esp + 0], edi
-       mov     [esp + 4], edx
-       lea     esi, [esp + 12]         // -> expanded M = mi
-       lea     edx, [esp + 44]         // -> space for Y
+       mov     [SP + 0], edi
+       mov     [SP + 4], edx
+       lea     esi, [SP + 12]          // -> expanded M = mi
+       lea     edx, [SP + 44]          // -> space for Y
        call    mont4
        add     ebx, 16
        add     edi, 16
@@ -1010,8 +1039,8 @@ FUNC(mpxmont_redc4_x86_sse2)
 
        // Still have carries left to propagate.
 8:     carryadd
-       mov     esi, [esp + 8]          // -> dv blocks limit
-       mov     edx, [ebp + 24]         // dv limit
+       mov     esi, [SP + 8]           // -> dv blocks limit
+       mov     edx, [BP + 24]          // dv limit
        psllq   xmm7, 16
        pslldq  xmm7, 8
        paddq   xmm6, xmm7
@@ -1025,33 +1054,33 @@ FUNC(mpxmont_redc4_x86_sse2)
        // Continue carry propagation until the end of the buffer.
 0:     add     [edi], eax
        mov     eax, 0                  // preserves flags
-       adcd    [edi + 4], 0
-       adcd    [edi + 8], 0
-       adcd    [edi + 12], 0
+       adc     dword ptr [edi + 4], 0
+       adc     dword ptr [edi + 8], 0
+       adc     dword ptr [edi + 12], 0
        adc     eax, 0
        add     edi, 16
        cmp     edi, esi
        jb      0b
 
-       // Deal with the tail end.
+       // Deal with the tail end.  Note that the actual destination length
+       // won't be an exact number of blocks of four, so it's safe to just
+       // drop through here.
 7:     add     [edi], eax
-       mov     eax, 0                  // preserves flags
+       mov     eax, 0
        add     edi, 4
        adc     eax, 0
        cmp     edi, edx
        jb      7b
 
-       // All done for this iteration.  Start the next.  (This must have at
-       // least one follow-on iteration, or we'd not have started this outer
-       // loop.)
-8:     mov     edi, [esp + 0]          // -> dv[i - 1]
-       mov     ebx, [ebp + 28]         // -> X = nv[0]
-       lea     edx, [esp + 44]         // -> space for Y
-       lea     esi, [esp + 12]         // -> expanded M = mi
+       // All done for this iteration.  Start the next.
+8:     mov     edi, [SP + 0]           // -> dv[i - 1]
+       mov     ebx, [BP + 28]          // -> X = nv[0]
+       lea     edx, [SP + 44]          // -> space for Y
+       lea     esi, [SP + 12]          // -> expanded M = mi
        add     edi, 16                 // -> Z = dv[i]
-       cmp     edi, [esp + 4]          // all done yet?
+       cmp     edi, [SP + 4]           // all done yet?
        jae     9f
-       mov     [esp + 0], edi
+       mov     [SP + 0], edi
        call    mont4
        add     edi, 16
        add     ebx, 16
@@ -1062,9 +1091,8 @@ FUNC(mpxmont_redc4_x86_sse2)
        popreg  edi
        popreg  esi
        popreg  ebx
-       popreg  ebp
+       popreg  BP
        ret
-
 ENDFUNC
 
 ///--------------------------------------------------------------------------
@@ -1091,22 +1119,22 @@ ENDFUNC
 .endm
 
 .macro testprologue n
-       pushreg ebp
+       pushreg BP
        pushreg ebx
        pushreg esi
        pushreg edi
        setfp
-       and     esp, ~15
-       sub     esp, 3*32 + 4*4
+       stalloc 3*32 + 4*4
+       and     SP, ~15
   endprologue
        mov     eax, \n
-       mov     [esp + 104], eax
+       mov     [SP + 104], eax
        // vars:
-       //      esp +   0 = v expanded
-       //      esp +  32 = y expanded
-       //      esp +  64 = ? expanded
-       //      esp +  96 = cycles
-       //      esp + 104 = count
+       //      SP +   0 = v expanded
+       //      SP +  32 = y expanded
+       //      SP +  64 = ? expanded
+       //      SP +  96 = cycles
+       //      SP + 104 = count
 .endm
 
 .macro testepilogue
@@ -1114,7 +1142,7 @@ ENDFUNC
        popreg  edi
        popreg  esi
        popreg  ebx
-       popreg  ebp
+       popreg  BP
        ret
 .endm
 
@@ -1131,15 +1159,15 @@ ENDFUNC
        mov     ecx, \v
        movdqu  xmm0, [ecx]
        expand  xmm7, xmm0, xmm1
-       movdqa  [esp +  0], xmm0
-       movdqa  [esp + 16], xmm1
+       movdqa  [SP +  0], xmm0
+       movdqa  [SP + 16], xmm1
   .endif
   .ifnes "\y", "nil"
        mov     edx, \y
        movdqu  xmm2, [edx]
        expand  xmm7, xmm2, xmm3
-       movdqa  [esp + 32], xmm2
-       movdqa  [esp + 48], xmm3
+       movdqa  [SP + 32], xmm2
+       movdqa  [SP + 48], xmm3
   .endif
 .endm
 
@@ -1147,25 +1175,25 @@ ENDFUNC
        .p2align 4
 0:
   .ifnes "\u", "nil"
-       lea     ecx, [esp + 0]
+       lea     ecx, [SP + 0]
   .endif
        mov     ebx, \x
   .ifeqs "\mode", "mont"
-       lea     esi, [esp + 32]
+       lea     esi, [SP + 32]
   .endif
-       cysetup esp + 96
+       cysetup SP + 96
   .ifnes "\u", "nil"
        mov     eax, \u
   .endif
   .ifeqs "\mode", "mont"
-       lea     edx, [esp + 64]
+       lea     edx, [SP + 64]
   .else
-       lea     edx, [esp + 32]
+       lea     edx, [SP + 32]
   .endif
 .endm
 
 .macro testtail cyv
-       cystore esp + 96, \cyv, esp + 104
+       cystore SP + 96, \cyv, SP + 104
        jnz     0b
 .endm
 
@@ -1177,122 +1205,128 @@ ENDFUNC
 .endm
 
 FUNC(test_dmul4)
-       testprologue [ebp + 44]
-       testldcarry [ebp + 24]
-       testexpand [ebp + 36], [ebp + 40]
-       mov     edi, [ebp + 20]
-       testtop [ebp + 28], [ebp + 32]
+       testprologue [BP + 44]
+       testldcarry [BP + 24]
+       testexpand [BP + 36], [BP + 40]
+       mov     edi, [BP + 20]
+       testtop [BP + 28], [BP + 32]
        call    dmul4
-       testtail [ebp + 48]
-       testcarryout [ebp + 24]
+       testtail [BP + 48]
+       testcarryout [BP + 24]
        testepilogue
 ENDFUNC
 
 FUNC(test_dmla4)
-       testprologue [ebp + 44]
-       testldcarry [ebp + 24]
-       testexpand [ebp + 36], [ebp + 40]
-       mov     edi, [ebp + 20]
-       testtop [ebp + 28], [ebp + 32]
+       testprologue [BP + 44]
+       testldcarry [BP + 24]
+       testexpand [BP + 36], [BP + 40]
+       mov     edi, [BP + 20]
+       testtop [BP + 28], [BP + 32]
        call    dmla4
-       testtail [ebp + 48]
-       testcarryout [ebp + 24]
+       testtail [BP + 48]
+       testcarryout [BP + 24]
        testepilogue
 ENDFUNC
 
 FUNC(test_mul4)
-       testprologue [ebp + 36]
-       testldcarry [ebp + 24]
-       testexpand nil, [ebp + 32]
-       mov     edi, [ebp + 20]
-       testtop nil, [ebp + 28]
+       testprologue [BP + 36]
+       testldcarry [BP + 24]
+       testexpand nil, [BP + 32]
+       mov     edi, [BP + 20]
+       testtop nil, [BP + 28]
        call    mul4
-       testtail [ebp + 40]
-       testcarryout [ebp + 24]
+       testtail [BP + 40]
+       testcarryout [BP + 24]
        testepilogue
 ENDFUNC
 
 FUNC(test_mul4zc)
-       testprologue [ebp + 36]
-       testldcarry [ebp + 24]
-       testexpand nil, [ebp + 32]
-       mov     edi, [ebp + 20]
-       testtop nil, [ebp + 28]
+       testprologue [BP + 36]
+       testldcarry [BP + 24]
+       testexpand nil, [BP + 32]
+       mov     edi, [BP + 20]
+       testtop nil, [BP + 28]
        call    mul4zc
-       testtail [ebp + 40]
-       testcarryout [ebp + 24]
+       testtail [BP + 40]
+       testcarryout [BP + 24]
        testepilogue
 ENDFUNC
 
 FUNC(test_mla4)
-       testprologue [ebp + 36]
-       testldcarry [ebp + 24]
-       testexpand nil, [ebp + 32]
-       mov     edi, [ebp + 20]
-       testtop nil, [ebp + 28]
+       testprologue [BP + 36]
+       testldcarry [BP + 24]
+       testexpand nil, [BP + 32]
+       mov     edi, [BP + 20]
+       testtop nil, [BP + 28]
        call    mla4
-       testtail [ebp + 40]
-       testcarryout [ebp + 24]
+       testtail [BP + 40]
+       testcarryout [BP + 24]
        testepilogue
 ENDFUNC
 
 FUNC(test_mla4zc)
-       testprologue [ebp + 36]
-       testldcarry [ebp + 24]
-       testexpand nil, [ebp + 32]
-       mov     edi, [ebp + 20]
-       testtop nil, [ebp + 28]
+       testprologue [BP + 36]
+       testldcarry [BP + 24]
+       testexpand nil, [BP + 32]
+       mov     edi, [BP + 20]
+       testtop nil, [BP + 28]
        call    mla4zc
-       testtail [ebp + 40]
-       testcarryout [ebp + 24]
+       testtail [BP + 40]
+       testcarryout [BP + 24]
        testepilogue
 ENDFUNC
 
 FUNC(test_mmul4)
-       testprologue [ebp + 48]
-       testexpand [ebp + 40], [ebp + 44]
-       mov     edi, [ebp + 20]
-       testtop [ebp + 32], [ebp + 36], mont
+       testprologue [BP + 48]
+       testexpand [BP + 40], [BP + 44]
+       mov     edi, [BP + 20]
+       testtop [BP + 32], [BP + 36], mont
        call    mmul4
-       testtail [ebp + 52]
-       mov     edi, [ebp + 28]
-       movdqa  xmm0, [esp + 64]
-       movdqa  xmm1, [esp + 80]
+       testtail [BP + 52]
+       mov     edi, [BP + 28]
+       movdqa  xmm0, [SP + 64]
+       movdqa  xmm1, [SP + 80]
+       pshufd  xmm0, xmm0, SHUF(0, 2, 1, 3)
+       pshufd  xmm1, xmm1, SHUF(0, 2, 1, 3)
        movdqu  [edi], xmm0
        movdqu  [edi + 16], xmm1
-       testcarryout [ebp + 24]
+       testcarryout [BP + 24]
        testepilogue
 ENDFUNC
 
 FUNC(test_mmla4)
-       testprologue [ebp + 48]
-       testexpand [ebp + 40], [ebp + 44]
-       mov     edi, [ebp + 20]
-       testtop [ebp + 32], [ebp + 36], mont
+       testprologue [BP + 48]
+       testexpand [BP + 40], [BP + 44]
+       mov     edi, [BP + 20]
+       testtop [BP + 32], [BP + 36], mont
        call    mmla4
-       testtail [ebp + 52]
-       mov     edi, [ebp + 28]
-       movdqa  xmm0, [esp + 64]
-       movdqa  xmm1, [esp + 80]
+       testtail [BP + 52]
+       mov     edi, [BP + 28]
+       movdqa  xmm0, [SP + 64]
+       movdqa  xmm1, [SP + 80]
+       pshufd  xmm0, xmm0, SHUF(0, 2, 1, 3)
+       pshufd  xmm1, xmm1, SHUF(0, 2, 1, 3)
        movdqu  [edi], xmm0
        movdqu  [edi + 16], xmm1
-       testcarryout [ebp + 24]
+       testcarryout [BP + 24]
        testepilogue
 ENDFUNC
 
 FUNC(test_mont4)
-       testprologue [ebp + 40]
-       testexpand nil, [ebp + 36]
-       mov     edi, [ebp + 20]
-       testtop nil, [ebp + 32], mont
+       testprologue [BP + 40]
+       testexpand nil, [BP + 36]
+       mov     edi, [BP + 20]
+       testtop nil, [BP + 32], mont
        call    mont4
-       testtail [ebp + 44]
-       mov     edi, [ebp + 28]
-       movdqa  xmm0, [esp + 64]
-       movdqa  xmm1, [esp + 80]
+       testtail [BP + 44]
+       mov     edi, [BP + 28]
+       movdqa  xmm0, [SP + 64]
+       movdqa  xmm1, [SP + 80]
+       pshufd  xmm0, xmm0, SHUF(0, 2, 1, 3)
+       pshufd  xmm1, xmm1, SHUF(0, 2, 1, 3)
        movdqu  [edi], xmm0
        movdqu  [edi + 16], xmm1
-       testcarryout [ebp + 24]
+       testcarryout [BP + 24]
        testepilogue
 ENDFUNC
 
index 4294845..ac82688 100644 (file)
@@ -818,7 +818,7 @@ void mpx_usub(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl,
 
 void mpx_usubn(mpw *dv, mpw *dvl, mpw n) { MPX_USUBN(dv, dvl, n); }
 
-/* --- @mpx_uaddnlsl@ --- *
+/* --- @mpx_usubnlsl@ --- *
  *
  * Arguments:  @mpw *dv, *dvl@ = destination and first argument vector
  *             @mpw a@ = second argument
@@ -931,6 +931,14 @@ static void simple_umul(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl,
   MAYBE_UMUL4(amd64_avx)
 #endif
 
+#if CPUFAM_ARMEL
+  MAYBE_UMUL4(arm_neon)
+#endif
+
+#if CPUFAM_ARM64
+  MAYBE_UMUL4(arm64_simd)
+#endif
+
 static mpx_umul__functype *pick_umul(void)
 {
 #if CPUFAM_X86
@@ -945,6 +953,14 @@ static mpx_umul__functype *pick_umul(void)
   DISPATCH_PICK_COND(mpx_umul, maybe_umul4_amd64_sse2,
                     cpu_feature_p(CPUFEAT_X86_SSE2));
 #endif
+#if CPUFAM_ARMEL
+  DISPATCH_PICK_COND(mpx_umul, maybe_umul4_arm_neon,
+                    cpu_feature_p(CPUFEAT_ARM_NEON));
+#endif
+#if CPUFAM_ARM64
+  DISPATCH_PICK_COND(mpx_umul, maybe_umul4_arm64_simd,
+                    cpu_feature_p(CPUFEAT_ARM_NEON));
+#endif
   DISPATCH_PICK_FALLBACK(mpx_umul, simple_umul);
 }
 
@@ -1274,9 +1290,14 @@ mpw mpx_udivn(mpw *qv, mpw *qvl, const mpw *rv, const mpw *rvl, mpw d)
 
 #include <mLib/alloc.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
+#ifdef ENABLE_ASM_DEBUG
+#  include "regdump.h"
+#endif
+
 #include "mpscan.h"
 
 #define ALLOC(v, vl, sz) do {                                          \
@@ -1366,7 +1387,7 @@ static int loadstore(dstr *v)
       ok = 0;
     MPX_OCTETS(oct, m, ml);
     mpx_storel(m, ml, d.buf, d.sz);
-    if (memcmp(d.buf, v->buf, oct) != 0) {
+    if (MEMCMP(d.buf, !=, v->buf, oct)) {
       dumpbits("\n*** storel failed", d.buf, d.sz);
       ok = 0;
     }
@@ -1376,7 +1397,7 @@ static int loadstore(dstr *v)
       ok = 0;
     MPX_OCTETS(oct, m, ml);
     mpx_storeb(m, ml, d.buf, d.sz);
-    if (memcmp(d.buf + d.sz - oct, v->buf + v->len - oct, oct) != 0) {
+    if (MEMCMP(d.buf + d.sz - oct, !=, v->buf + v->len - oct, oct)) {
       dumpbits("\n*** storeb failed", d.buf, d.sz);
       ok = 0;
     }
@@ -1410,14 +1431,14 @@ static int twocl(dstr *v)
 
     mpx_loadl(m, ml0, v[0].buf, v[0].len);
     mpx_storel2cn(m, ml0, d.buf, v[1].len);
-    if (memcmp(d.buf, v[1].buf, v[1].len)) {
+    if (MEMCMP(d.buf, !=, v[1].buf, v[1].len)) {
       dumpbits("\n*** storel2cn failed", d.buf, v[1].len);
       ok = 0;
     }
 
     mpx_loadl2cn(m, ml1, v[1].buf, v[1].len);
     mpx_storel(m, ml1, d.buf, v[0].len);
-    if (memcmp(d.buf, v[0].buf, v[0].len)) {
+    if (MEMCMP(d.buf, !=, v[0].buf, v[0].len)) {
       dumpbits("\n*** loadl2cn failed", d.buf, v[0].len);
       ok = 0;
     }
@@ -1454,14 +1475,14 @@ static int twocb(dstr *v)
 
     mpx_loadb(m, ml0, v[0].buf, v[0].len);
     mpx_storeb2cn(m, ml0, d.buf, v[1].len);
-    if (memcmp(d.buf, v[1].buf, v[1].len)) {
+    if (MEMCMP(d.buf, !=, v[1].buf, v[1].len)) {
       dumpbits("\n*** storeb2cn failed", d.buf, v[1].len);
       ok = 0;
     }
 
     mpx_loadb2cn(m, ml1, v[1].buf, v[1].len);
     mpx_storeb(m, ml1, d.buf, v[0].len);
-    if (memcmp(d.buf, v[0].buf, v[0].len)) {
+    if (MEMCMP(d.buf, !=, v[0].buf, v[0].len)) {
       dumpbits("\n*** loadb2cn failed", d.buf, v[0].len);
       ok = 0;
     }
@@ -1708,6 +1729,9 @@ static test_chunk defs[] = {
 
 int main(int argc, char *argv[])
 {
+#ifdef ENABLE_ASM_DEBUG
+  regdump_init();
+#endif
   test_run(argc, argv, defs, SRCDIR"/t/mpx");
   return (0);
 }
index 217cc62..9be9742 100644 (file)
@@ -30,6 +30,8 @@
 #include <ctype.h>
 #include <string.h>
 
+#include <mLib/macros.h>
+
 #include "qdparse.h"
 
 /*----- Main code ---------------------------------------------------------*/
@@ -45,7 +47,7 @@
 
 void qd_skipspc(qd_parse *qd)
 {
-  while (isspace((unsigned char)*qd->p))
+  while (ISSPACE(*qd->p))
     qd->p++;
 }
 
@@ -90,7 +92,7 @@ int qd_enum(qd_parse *qd, const char *e)
     e += strspn(e, ", ");
     if (!*e) break;
     n = strcspn(e, ", ");
-    if (strncmp(qd->p, e, n) == 0 && !isalnum((unsigned char)qd->p[n])) {
+    if (STRNCMP(qd->p, ==, e, n) && !ISALNUM(qd->p[n])) {
       qd->p += n;
       return (i);
     }
@@ -116,7 +118,7 @@ mp *qd_getmp(qd_parse *qd)
 
   qd_skipspc(qd);
   m = mp_readstring(MP_NEW, qd->p, &q, 0);
-  if (m && !isalnum((unsigned char)*q))
+  if (m && !ISALNUM(*q))
     qd->p = q;
   else {
     mp_drop(m);
index b50ea8d..8d0942d 100644 (file)
--- a/math/t/mp
+++ b/math/t/mp
@@ -115,6 +115,39 @@ sqrt {
 14565040310136678240 3816417208;
 }
 
+nthrt {
+  0 27 0 1;
+  1 1 1 1;
+  99 2 9 0;
+  100 2 10 1;
+  101 2 10 0;
+
+  2432442434617858985744608211960343276041892998697958012044143077567778256072696563501333460622383626492631158845093813667916645390906408185968436731121086804986194010729874783817632607960227495980162127756247771205609001938726 37 1234566 0;
+  2432442434617858985744608211960343276041892998697958012044143077567778256072696563501333460622383626492631158845093813667916645390906408185968436731121086804986194010729874783817632607960227495980162127756247771205609001938727 37 1234567 1;
+  2432442434617858985744608211960343276041892998697958012044143077567778256072696563501333460622383626492631158845093813667916645390906408185968436731121086804986194010729874783817632607960227495980162127756247771205609001938728 37 1234567 0;
+
+  -26 3 -3 0;
+  -27 3 -3 1;
+  -28 3 -4 0;
+}
+
+perfect-power-p {
+  0 0 0 1;
+  1 0 1 1;
+  -1 0 -1 1;
+
+  -4 0 -4 1;
+  -8 1 -2 3;
+  -64 1 -4 3;
+
+  80 0 80 1;
+  81 1 3 4;
+  82 0 82 1;
+
+  2432442434617858985744608211960343276041892998697958012044143077567778256072696563501333460622383626492631158845093813667916645390906408185968436731121086804986194010729874783817632607960227495980162127756247771205609001938727 1 1234567 37;
+  42467986438630307821661186973460619303572935864570185492440237295438188325895624954701633272891586038903309915601221633039963682793644006385615600911359153716020597273608200491915551536581527267184634993651215467730190125236224 1 12 210;
+}
+
 gcd {
   # --- Simple tests ---
 
index 69ea238..1d8a741 100644 (file)
@@ -7,7 +7,7 @@ dmul4 {
     d3d2d1d0d7d6d5d4dbdad9d8dfdedddc # v
     f3f2f1f0f7f6f5f4fbfaf9f8fffefdfc # y
     d4356fa018c7f681e0be24efecdaf6e0 # zz
-    0004bb142333e4e00004c56cb3ac322d000335ca0eb0310000033cbfe475dfd00001a2236db667a00001a5a668a94f10; ## cc
+    0004bb142333e4e00004c56cb3ac322d000335ca0eb0310000033cbfe475dfd00001a2236db667a00001a5a668a94f10; # cc
 }
 
 dmla4 {
@@ -26,7 +26,7 @@ mul4 {
     e3e2e1e0e7e6e5e4ebeae9e8efeeedec # x
     f3f2f1f0f7f6f5f4fbfaf9f8fffefdfc # y
     964a43a0b812545cd3c4a34a69e3ec23 # zz
-    0002b2f3db03f8310002b880e3fffed70001d457394991000001d812a4ace8a80000ee0b505470500000efed0e0e2428; ## cc
+    0002b2f3db03f8310002b880e3fffed70001d457394991000001d812a4ace8a80000ee0b505470500000efed0e0e2428; # cc
 }
 
 mul4zc {
@@ -42,7 +42,7 @@ mla4 {
     e3e2e1e0e7e6e5e4ebeae9e8efeeedec # x
     f3f2f1f0f7f6f5f4fbfaf9f8fffefdfc # y
     49fcf5506fc90a118f7f5d0329a2a9e0 # zz
-    0002b2f3db03f8320002b880e3fffed70001d457394991000001d812a4ace8a80000ee0b505470500000efed0e0e2428; ## cc
+    0002b2f3db03f8320002b880e3fffed70001d457394991000001d812a4ace8a80000ee0b505470500000efed0e0e2428; # cc
 }
 
 mla4zc {
@@ -59,7 +59,7 @@ mmul4 {
     d3d2d1d0d7d6d5d4dbdad9d8dfdedddc # v
     546f97b132b6ca1d10d519b5ca6ab8a9 # m
     00000000000000000000000000000000 # zz
-    00006c00000012ad00009a8d0000630c0000f0840000979d000077a400000caa # yy
+    00006c0000009a8d000012ad0000630c0000f084000077a40000979d00000caa # yy
     0003126be83bdbf40002a05c4867918e000259dfe01b01770001b7e463bf6b7a00011339f770da470000bdab9990cf26; # cc
 }
 
@@ -70,7 +70,7 @@ mmla4 {
     d3d2d1d0d7d6d5d4dbdad9d8dfdedddc # v
     546f97b132b6ca1d10d519b5ca6ab8a9 # m
     00000000000000000000000000000000 # zz
-    000016b00000d85500000b390000507000008de20000754b000057700000c5db # yy
+    000016b000000b390000d8550000507000008de2000057700000754b0000c5db # yy
     000338658ad352110002f9fbc6cd85d5000205e99c5e20d300021acac7b997550000fdb10c111c11000131df2708bb59; # cc
 }
 
@@ -79,6 +79,6 @@ mont4 {
     acadaeafa8a9aaaba4a5a6a7a0a1a2a3 # n
     546f97b132b6ca1d10d519b5ca6ab8a9 # m
     00000000000000000000000000000000 # zz
-    0000aab00000c5a7000070ab0000ed6400009d5d0000ddad0000dfcb0000b930 # yy
+    0000aab0000070ab0000c5a70000ed6400009d5d0000dfcb0000ddad0000b930 # yy
     0001734705fa761d00019ee57a6290e40000f14fc045d61200010386c155e29100008b1816a19f2700007432ecd64990; # cc
 }
index 80a6ef6..09acb40 100644 (file)
@@ -258,6 +258,8 @@ void gfshare_combine(gfshare *s, void *buf)
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
+
 #include "fibrand.h"
 
 static int verify(grand *r)
@@ -298,7 +300,7 @@ static int verify(grand *r)
   gfshare_combine(&s, sbuf);
   gfshare_destroy(&s);
 
-  if (memcmp(sec, sbuf, len) != 0) {
+  if (MEMCMP(sec, !=, sbuf, len)) {
     ok = 0;
     fprintf(stderr, "\nbad recombination of shares\n");
   };
index c31a3d9..e4d974a 100644 (file)
@@ -38,6 +38,7 @@
 
 #include <mLib/base64.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -194,14 +195,14 @@ static int encrypt(int argc, char *argv[])
     die(EXIT_FAILURE, "encoding `%s' not found", ef);
 
   fn = optind < argc ? argv[optind++] : "-";
-  if (strcmp(fn, "-") == 0)
+  if (STRCMP(fn, ==, "-"))
     fp = stdin;
   else if ((fp = fopen(fn, "rb")) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s': %s",
        fn, strerror(errno));
   }
 
-  if (!of || strcmp(of, "-") == 0)
+  if (!of || STRCMP(of, ==, "-"))
     ofp = stdout;
   else if ((ofp = fopen(of, eo->wmode)) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
@@ -366,7 +367,7 @@ static int decrypt(int argc, char *argv[])
     die(EXIT_FAILURE, "encoding `%s' not found", ef);
 
   fn = optind < argc ? argv[optind++] : "-";
-  if (strcmp(fn, "-") == 0)
+  if (STRCMP(fn, ==, "-"))
     fp = stdin;
   else if ((fp = fopen(fn, eo->rmode)) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s': %s",
@@ -472,7 +473,7 @@ static int decrypt(int argc, char *argv[])
 
   /* --- Now decrypt the main body --- */
 
-  if (!of || strcmp(of, "-") == 0) {
+  if (!of || STRCMP(of, ==, "-")) {
     ofp = stdout;
     f |= f_buffer;
   }
index 3cce9c9..d27016f 100644 (file)
@@ -161,7 +161,7 @@ static size_t textread(msgcanon *m, void *bp)
       if (n >= MSGBUFTHRESH) goto full;
       b[n++] = ch;
       f &= ~F_MIDLINE;
-    } else if (isspace(ch)) {
+    } else if (ISSPACE(ch)) {
       f |= F_MIDLINE;
       if (n >= MSGBUFSZ) goto full;
       b[n++] = ch; nsp++;
@@ -222,7 +222,7 @@ static void nullwrite(msgcanon *m, const void *bp, size_t n) { ; }
 static void mcsetup_readfile(msgcanon *m, unsigned f, const char *fn)
 {
   m->f = F_DETACH | (f & (F_BINARY | F_PROGRESS));
-  if (!fn || strcmp(fn, "-") == 0) {
+  if (!fn || STRCMP(fn, ==, "-")) {
     m->fp = stdin;
     m->f |= F_NOCLOSE;
   } else if ((m->fp = fopen(fn, (f & F_BINARY) ? "rb" : "r")) == 0)
@@ -260,7 +260,7 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee,
       }
     } else {
       m->read = binreaddetach;
-      if (!dfn || strcmp(dfn, "-") == 0)
+      if (!dfn || STRCMP(dfn, ==, "-"))
        m->fp = stdin;
       else if ((m->fp = fopen(dfn, "rb")) == 0)
        die(EXIT_FAILURE, "can't open `%s': %s", dfn, strerror(errno));
@@ -288,7 +288,7 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee,
        }
       }
     } else {
-      if (!dfn || strcmp(dfn, "-") == 0)
+      if (!dfn || STRCMP(dfn, ==, "-"))
        m->fp = stdin;
       else if ((m->fp = fopen(dfn, "r")) == 0)
        die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno));
@@ -533,7 +533,7 @@ static int sign(int argc, char *argv[])
 
   fn = (optind >= argc) ? 0 : argv[optind++];
 
-  if (!of || strcmp(of, "-") == 0)
+  if (!of || STRCMP(of, ==, "-"))
     ofp = stdout;
   else if ((ofp = fopen(of, eo->wmode)) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
@@ -599,14 +599,14 @@ static int vrfbdry(const char *b, void *p)
 {
   vrfctx *v = p;
 
-  if (strcmp(b, "CATSIGN MESSAGE") == 0) {
+  if (STRCMP(b, ==, "CATSIGN MESSAGE")) {
     v->f |= F_BINARY;
     v->m |= F_BINARY | F_DETACH;
     return (1);
-  } else if (strcmp(b, "CATSIGN MESSAGE HEADER") == 0) {
+  } else if (STRCMP(b, ==, "CATSIGN MESSAGE HEADER")) {
     v->m |= F_BINARY | F_DETACH;
     return (1);
-  } else if (strcmp(b, "CATSIGN SIGNATURE") == 0) {
+  } else if (STRCMP(b, ==, "CATSIGN SIGNATURE")) {
     v->f |= F_DETACH;
     v->m |= F_DETACH;
     return (1);
@@ -668,7 +668,7 @@ static int verify(int argc, char *argv[])
       case 'u': v.f |= F_UTC; break;
       case 'C': v.f |= F_NOCHECK; break;
       case 't':
-       if (strcmp(optarg, "always") == 0) t_fresh = 0;
+       if (STRCMP(optarg, ==, "always")) t_fresh = 0;
        else if ((t_fresh = get_date(optarg, 0)) < 0)
          die(EXIT_FAILURE, "bad freshness time");
        break;
@@ -685,7 +685,7 @@ static int verify(int argc, char *argv[])
     die(EXIT_FAILURE, "encoding `%s' not found", ef);
 
   fn = optind < argc ? argv[optind++] : "-";
-  if (strcmp(fn, "-") == 0)
+  if (STRCMP(fn, ==, "-"))
     fp = stdin;
   else if ((fp = fopen(fn, eo->rmode)) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s': %s",
@@ -730,7 +730,7 @@ static int verify(int argc, char *argv[])
     printf("WARN verification key %s fails check: %s\n", d.buf, err);
 
   dstr_reset(&dd); keyhash(k, s.s, &dd);
-  if (dd.len != s.kh.len || memcmp(dd.buf, s.kh.buf, dd.len) != 0) {
+  if (dd.len != s.kh.len || MEMCMP(dd.buf, !=, s.kh.buf, dd.len)) {
     if (v.verb) printf("FAIL key hash mismatch\n");
     exit(EXIT_FAILURE);
   }
@@ -746,7 +746,7 @@ static int verify(int argc, char *argv[])
   if (!of && (v.f & F_DETACH)) {
     rfp = ofp = 0;
     v.f &= ~F_BUFFER;
-  } else if (!of || strcmp(of, "-") == 0) {
+  } else if (!of || STRCMP(of, ==, "-")) {
     v.f |= F_BUFFER;
     ofp = stdout;
   } else if (of && !(v.f & F_BUFFER)) {
@@ -893,7 +893,7 @@ static int format(int argc, char *argv[])
     die(EXIT_FAILURE, "encoding `%s' not found", oef);
 
   fn = optind < argc ? argv[optind++] : "-";
-  if (strcmp(fn, "-") == 0)
+  if (STRCMP(fn, ==, "-"))
     fp = stdin;
   else if ((fp = fopen(fn, ieo->rmode)) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s': %s",
@@ -927,7 +927,7 @@ static int format(int argc, char *argv[])
   if (!of)
     mcsetup_writenull(&mc_out);
   else {
-    if (strcmp(of, "-") == 0)
+    if (STRCMP(of, ==, "-"))
       ofp = stdout;
     else if ((ofp = fopen(of, oeo->wmode)) == 0) {
       die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
@@ -942,7 +942,7 @@ static int format(int argc, char *argv[])
   }
 
   if (mf) {
-    if (strcmp(mf, "-") == 0)
+    if (STRCMP(mf, ==, "-"))
       mfp = stdout;
     else if ((mfp = fopen(mf, (f & F_BINARY) ? "wb" : "w")) == 0) {
       die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
@@ -1037,7 +1037,7 @@ static int info(int argc, char *argv[])
 
   if (optind >= argc)
     fp = stdin;
-  else if (strcmp(argv[optind], "-") == 0) {
+  else if (STRCMP(argv[optind], ==, "-")) {
     fp = stdin;
     optind++;
   } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) {
index f4b6119..a008fbb 100644 (file)
@@ -35,6 +35,7 @@
 #include <mLib/alloc.h>
 #include <mLib/base64.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/report.h>
 #include <mLib/sub.h>
@@ -94,7 +95,7 @@ static enc *pem_encinit(FILE *fp, const char *msg)
   return (&pe->e);
 }
 
-int checkbdry(const char *b, void *p) { return (!p || strcmp(b, p) == 0); }
+int checkbdry(const char *b, void *p) { return (!p || STRCMP(b, ==, p)); }
 
 static enc *pem_decinit(FILE *fp, encbdryp *func, void *p)
 {
@@ -133,7 +134,7 @@ banner:
   /* --- Check we have the right framing --- */
 
   if (d != 5) goto top;
-  if (strncmp(buf, "BEGIN ", 6) != 0 || (func && !func(buf + 6, p)))
+  if (STRNCMP(buf, !=, "BEGIN ", 6) || (func && !func(buf + 6, p)))
     goto top;
 
   /* --- Ready --- */
@@ -248,7 +249,7 @@ static int pem_decdone(enc *e)
   }
   if (d != 5) goto fail;
   buf[i] = 0;
-  if (strncmp(buf, "END ", 4) != 0 || strcmp(buf + 4, pe->msg) != 0)
+  if (STRNCMP(buf, !=, "END ", 4) || STRCMP(buf + 4, !=, pe->msg))
     goto fail;
   return (0);
 
@@ -295,7 +296,7 @@ const encops *getenc(const char *enc)
   const encops *eo;
 
   for (eo = enctab; eo->name; eo++) {
-    if (strcmp(eo->name, enc) == 0)
+    if (STRCMP(eo->name, ==, enc))
       goto e_found;
   }
   die(EXIT_FAILURE, "couldn't find encoding `%s'", enc);
@@ -399,14 +400,14 @@ int cmd_encode(int argc, char *argv[])
     die(EXIT_FAILURE, "encoding `%s' not found", ef);
 
   fn = optind < argc ? argv[optind++] : "-";
-  if (strcmp(fn, "-") == 0)
+  if (STRCMP(fn, ==, "-"))
     fp = stdin;
   else if ((fp = fopen(fn, "rb")) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s': %s",
        fn, strerror(errno));
   }
 
-  if (!of || strcmp(of, "-") == 0)
+  if (!of || STRCMP(of, ==, "-"))
     ofp = stdout;
   else if ((ofp = fopen(of, eo->wmode)) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
@@ -481,14 +482,14 @@ int cmd_decode(int argc, char *argv[])
     die(EXIT_FAILURE, "encoding `%s' not found", ef);
 
   fn = optind < argc ? argv[optind++] : "-";
-  if (strcmp(fn, "-") == 0)
+  if (STRCMP(fn, ==, "-"))
     fp = stdin;
   else if ((fp = fopen(fn, eo->rmode)) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s': %s",
        fn, strerror(errno));
   }
 
-  if (!of || strcmp(of, "-") == 0)
+  if (!of || STRCMP(of, ==, "-"))
     ofp = stdout;
   else if ((ofp = fopen(of, "wb")) == 0) {
     die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
index 64fea0e..6d65f5b 100644 (file)
@@ -42,6 +42,7 @@
 
 #include <mLib/alloc.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/report.h>
 #include <mLib/str.h>
 
@@ -67,9 +68,7 @@ static void puthex(const octet *buf, size_t sz, FILE *fp)
 static size_t gethex(const char *p, octet *q, size_t sz, char **pp)
 {
   size_t i = 0;
-  while (sz > 0 &&
-        isxdigit((unsigned char)p[0]) &&
-        isxdigit((unsigned char)p[1])) {
+  while (sz > 0 && ISXDIGIT(p[0]) && ISXDIGIT(p[1])) {
     char buf[3];
     buf[0] = p[0];
     buf[1] = p[1];
@@ -164,7 +163,7 @@ const encodeops *getencoding(const char *ename)
   const encodeops *e;
 
   for (e = encodingtab; e->name; e++) {
-    if (strcmp(ename, e->name) == 0)
+    if (STRCMP(ename, ==, e->name))
       return (e);
   }
   return (0);
@@ -198,7 +197,7 @@ const gchash *gethash(const char *name)
   const gchash *const *g, *gg = 0;
   size_t sz = strlen(name);
   for (g = ghashtab; *g; g++) {
-    if (strncmp(name, (*g)->name, sz) == 0) {
+    if (STRNCMP(name, ==, (*g)->name, sz)) {
       if ((*g)->name[sz] == 0) {
        gg = *g;
        break;
@@ -292,7 +291,7 @@ int fhash(fhashstate *fh, const char *file, void *buf)
   size_t n;
   fprogress ff;
 
-  if (!file || strcmp(file, "-") == 0)
+  if (!file || STRCMP(file, ==, "-"))
     fp = stdin;
   else if ((fp = fopen(file, fh->f & FHF_BINARY ? "rb" : "r")) == 0)
     return (-1);
@@ -303,7 +302,7 @@ int fhash(fhashstate *fh, const char *file, void *buf)
 
   if (fh->f & FHF_JUNK) {
     p = file;
-    if (strncmp(p, "./", 2) == 0) p += 2;
+    if (STRNCMP(p, ==, "./", 2)) p += 2;
     q = p;
     ffhe = &fh->ents;
     for (;;) {
@@ -311,7 +310,7 @@ int fhash(fhashstate *fh, const char *file, void *buf)
        n = q - p;
        for (; *ffhe; ffhe = &(*ffhe)->next) {
          fhe = *ffhe;
-         if (strncmp(p, fhe->name, n) == 0 && fhe->name[n] == 0)
+         if (STRNCMP(p, ==, fhe->name, n) && fhe->name[n] == 0)
            goto found;
        }
        fhe = xmalloc(offsetof(struct fhent, name) + n + 1);
@@ -383,10 +382,10 @@ static int fhjunk(struct fhjunk *fhj, struct fhent *ents)
     n++;
   }
   while (errno = 0, (d = readdir(dp)) != 0) {
-    if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
+    if (STRCMP(d->d_name, ==, ".") || STRCMP(d->d_name, ==, ".."))
       continue;
     for (fhe = ents; fhe; fhe = fhe->next) {
-      if (strcmp(d->d_name, fhe->name) == 0) goto found;
+      if (STRCMP(d->d_name, ==, fhe->name)) goto found;
     }
     fhj->d->len = n;
     dstr_puts(fhj->d, d->d_name);
@@ -464,17 +463,17 @@ int hfparse(hfpctx *hfp)
   if (*p == '#') {
     p++;
     if ((q = str_getword(&p)) == 0) return (HF_BAD);
-    if (strcmp(q, "hash") == 0) {
+    if (STRCMP(q, ==, "hash")) {
       if ((q = str_getword(&p)) == 0) return (HF_BAD);
       if ((gch = gethash(q)) == 0) return (HF_BAD);
       hfp->gch = gch;
       return (HF_HASH);
-    } else if (strcmp(q, "encoding") == 0) {
+    } else if (STRCMP(q, ==, "encoding")) {
       if ((q = str_getword(&p)) == 0) return (HF_BAD);
       if ((ee = getencoding(q)) == 0) return (HF_BAD);
       hfp->ee = ee;
       return (HF_ENC);
-    } else if (strcmp(q, "escape") == 0) {
+    } else if (STRCMP(q, ==, "escape")) {
       hfp->f |= HFF_ESCAPE;
       return (HF_ESC);
     }
@@ -556,7 +555,7 @@ int getstring(void *in, dstr *d, unsigned f)
 
 again:
   ch = nextch(in);
-  while (isspace(ch))
+  while (ISSPACE(ch))
     ch = nextch(in);
   if (ch == '#') {
     do ch = nextch(in); while (ch != '\n' && ch != eofch);
@@ -605,7 +604,7 @@ again:
 
     if (ch == q)
       break;
-    if (!q && isspace(ch))
+    if (!q && ISSPACE(ch))
       break;
 
     /* --- Otherwise contribute and continue --- */
@@ -649,7 +648,7 @@ void putstring(FILE *fp, const char *p, unsigned f)
 
   qq = 0;
   for (q = p; *q; q++) {
-    if (isspace((unsigned char)*q)) {
+    if (ISSPACE(*q)) {
       qq = '\"';
       break;
     }
index 9159ade..36dfdb3 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <mLib/alloc.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/report.h>
 #include <mLib/sub.h>
 
@@ -210,12 +211,12 @@ static bulk *naclbox_init(key *k, const char *calg, const char *halg)
   key_fulltag(k, &t);
 
   if ((q = key_getattr(0, k, "cipher")) != 0) calg = q;
-  if (!calg || strcmp(calg, "salsa20") == 0) aec = &salsa20_naclbox;
-  else if (strcmp(calg, "salsa20/12") == 0) aec = &salsa2012_naclbox;
-  else if (strcmp(calg, "salsa20/8") == 0) aec = &salsa208_naclbox;
-  else if (strcmp(calg, "chacha20") == 0) aec = &chacha20_naclbox;
-  else if (strcmp(calg, "chacha12") == 0) aec = &chacha12_naclbox;
-  else if (strcmp(calg, "chacha8") == 0) aec = &chacha8_naclbox;
+  if (!calg || STRCMP(calg, ==, "salsa20")) aec = &salsa20_naclbox;
+  else if (STRCMP(calg, ==, "salsa20/12")) aec = &salsa2012_naclbox;
+  else if (STRCMP(calg, ==, "salsa20/8")) aec = &salsa208_naclbox;
+  else if (STRCMP(calg, ==, "chacha20")) aec = &chacha20_naclbox;
+  else if (STRCMP(calg, ==, "chacha12")) aec = &chacha12_naclbox;
+  else if (STRCMP(calg, ==, "chacha8")) aec = &chacha8_naclbox;
   else {
     die(EXIT_FAILURE,
        "unknown or inappropriate encryption scheme `%s' in key `%s'",
@@ -866,7 +867,7 @@ kem *getkem(key *k, const char *app, int wantpriv, bulk **bc)
   if ((q = key_getattr(0, k, "kem")) != 0) {
     dstr_puts(&d, q);
     p = d.buf;
-  } else if (strncmp(k->type, app, n) == 0 && k->type[n] == '-') {
+  } else if (STRNCMP(k->type, ==, app, n) && k->type[n] == '-') {
     dstr_puts(&d, k->type);
     p = d.buf + n + 1;
   } else
@@ -897,7 +898,7 @@ kem *getkem(key *k, const char *app, int wantpriv, bulk **bc)
   /* --- Instantiate the KEM --- */
 
   for (kt = kemtab; kt->name; kt++) {
-    if (strcmp(kt->name, kalg) == 0)
+    if (STRCMP(kt->name, ==, kalg))
       goto k_found;
   }
   die(EXIT_FAILURE, "key encapsulation mechanism `%s' not found in key `%s'",
@@ -934,10 +935,10 @@ k_found:;
     bt = bulktab;
   else {
     for (bt = bulktab, bo = 0; bt->name; bt++) {
-      if (strcmp(balg, bt->name) == 0)
+      if (STRCMP(balg, ==, bt->name))
        { balg = 0; goto b_found; }
       n = strlen(bt->name);
-      if (strncmp(balg, bt->name, n) == 0 && balg[n] == '-')
+      if (STRNCMP(balg, ==, bt->name, n) && balg[n] == '-')
        { balg += n + 1; goto b_found; }
     }
     bt = bulktab;
index 8bc2219..19ea7d5 100644 (file)
@@ -29,6 +29,7 @@
 
 #define _FILE_OFFSET_BITS 64
 
+#include <mLib/macros.h>
 #include <mLib/report.h>
 
 #include "cc.h"
@@ -57,7 +58,7 @@ int displaylists(const struct listent *listtab, char *const argv[])
   } else {
     for (i = 0; argv[i]; i++) {
       for (li = listtab; li->name; li++) {
-       if (strcmp(li->name, argv[i]) == 0) {
+       if (STRCMP(li->name, ==, argv[i])) {
          li->list();
          goto cool;
        }
index a416c6e..4c1defc 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <stdlib.h>
 
+#include <mLib/macros.h>
 #include <mLib/report.h>
 
 #include "rand.h"
@@ -809,7 +810,7 @@ sig *getsig(key *k, const char *app, int wantpriv)
   if ((q = key_getattr(0, k, "sig")) != 0) {
     dstr_puts(&d, q);
     p = d.buf;
-  } else if (strncmp(k->type, app, n) == 0 && k->type[n] == '-') {
+  } else if (STRNCMP(k->type, ==, app, n) && k->type[n] == '-') {
     dstr_puts(&d, k->type);
     p = d.buf + n + 1;
   } else
@@ -832,7 +833,7 @@ sig *getsig(key *k, const char *app, int wantpriv)
   /* --- Look up the algorithms in the table --- */
 
   for (st = sigtab; st->name; st++) {
-    if (strcmp(st->name, salg) == 0)
+    if (STRCMP(st->name, ==, salg))
       goto s_found;
   }
   die(EXIT_FAILURE, "signature algorithm `%s' not found in key `%s'",
index 83c4a78..d593a1a 100644 (file)
@@ -29,6 +29,7 @@
 
 #define _FILE_OFFSET_BITS 64
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
 
@@ -53,7 +54,7 @@ const cmd *findcmd(const cmd *cmds, const char *name)
   size_t sz = strlen(name);
 
   for (c = cmds; c->name; c++) {
-    if (strncmp(name, c->name, sz) == 0) {
+    if (STRNCMP(name, ==, c->name, sz)) {
       if (c->name[sz] == 0) {
        chosen = c;
        break;
index c6912ff..62e23b7 100644 (file)
@@ -39,6 +39,7 @@
 #include <mLib/base64.h>
 #include <mLib/bits.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -178,7 +179,7 @@ static gmac *getmac(key *k, const char *app)
   if ((q = key_getattr(0, k, "mac")) != 0) {
     dstr_puts(&d, q);
     p = d.buf;
-  } else if (strncmp(k->type, app, n) == 0 && k->type[n] == '-') {
+  } else if (STRNCMP(k->type, ==, app, n) && k->type[n] == '-') {
     dstr_puts(&d, k->type);
     p = d.buf + n + 1;
   } else
@@ -258,7 +259,7 @@ static int cmd_gen(int argc, char *argv[])
       /* --- Fetch an expiry time --- */
 
       case 'e':
-       if (strcmp(optarg, "forever") == 0)
+       if (STRCMP(optarg, ==, "forever"))
          c.exp = KEXP_FOREVER;
        else if ((c.exp = get_date(optarg, 0)) == -1)
          die(EXIT_FAILURE, "bad expiry date: `%s'", optarg);
index 1377bca..3f217da 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <mLib/alloc.h>
 #include <mLib/base64.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -204,7 +205,7 @@ static int bget(block *b, FILE *fp, unsigned bin)
     if (getstring(fp, &d, GSF_FILE))
       return (E_EOF);
     for (tag = 0; tagtab[tag]; tag++) {
-      if (strcmp(tagtab[tag], d.buf) == 0)
+      if (STRCMP(tagtab[tag], ==, d.buf))
        goto done;
     }
     return (E_TAG);
@@ -239,7 +240,7 @@ static int bget(block *b, FILE *fp, unsigned bin)
       } else {
        if (getstring(fp, &b->d, GSF_FILE))
          return (E_EOF);
-       if (strcmp(b->d.buf, "forever") == 0)
+       if (STRCMP(b->d.buf, ==, "forever"))
          b->t = KEXP_FOREVER;
        else if ((b->t = get_date(b->d.buf, 0)) == -1)
          return (E_DATE);
@@ -536,7 +537,7 @@ static int sign(int argc, char *argv[])
        ki = optarg;
        break;
       case 'e':
-       if (strcmp(optarg, "forever") == 0)
+       if (STRCMP(optarg, ==, "forever"))
          exp = KEXP_FOREVER;
        else if ((exp = get_date(optarg, 0)) == -1)
          die(EXIT_FAILURE, "bad expiry time");
@@ -572,14 +573,14 @@ static int sign(int argc, char *argv[])
   /* --- Open files --- */
 
   if (hfile) ifile = hfile;
-  if (!ifile || strcmp(ifile, "-") == 0)
+  if (!ifile || STRCMP(ifile, ==, "-"))
     ifp = stdin;
   else if ((ifp = fopen(ifile, (f & f_bin) ? "rb" : "r")) == 0) {
     die(EXIT_FAILURE, "couldn't open input file `%s': %s",
        ifile, strerror(errno));
   }
 
-  if (!ofile || strcmp(ofile, "-") == 0)
+  if (!ofile || STRCMP(ofile, ==, "-"))
     ofp = stdout;
   else if ((ofp = fopen(ofile, (f & f_bin) ? "wb" : "w")) == 0) {
     die(EXIT_FAILURE, "couldn't open output file `%s': %s",
@@ -908,7 +909,7 @@ static int verify(int argc, char *argv[])
          }
          f |= f_bogus;
        } else if (b.b.len != GH_CLASS(s->h)->hashsz ||
-                  memcmp(d.buf, b.b.buf, b.b.len) != 0) {
+                  MEMCMP(d.buf, !=, b.b.buf, b.b.len)) {
          if (verb > 1)
            printf("BAD file `%s' has incorrect hash\n", b.d.buf);
          f |= f_bogus;
index 975c698..ece0b30 100644 (file)
@@ -34,6 +34,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -130,10 +131,10 @@ int main(int argc, char *argv[])
   }
   ulmax = mp_fromulong(MP_NEW, ULONG_MAX);
   p = argv[optind];
-  while (isspace((unsigned char)*p))
+  while (ISSPACE(*p))
     p++;
   xx = mp_readstring(MP_NEW, argv[optind], &p, 0);
-  while (isspace((unsigned char)*p))
+  while (ISSPACE(*p))
     p++;
   if (!xx || *p || MP_CMP(xx, <, MP_ZERO) || MP_CMP(xx, >, ulmax))
     die(EXIT_FAILURE, "bad integer `%s'", argv[optind]);
index 4b5a958..a494f62 100644 (file)
@@ -34,6 +34,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -128,9 +129,9 @@ int main(int argc, char *argv[])
   lmin = mp_fromlong(MP_NEW, LONG_MIN);
   lmax = mp_fromlong(MP_NEW, LONG_MAX);
   p = argv[optind];
-  while (isspace((unsigned char)*p)) p++;
+  while (ISSPACE(*p)) p++;
   nn = mp_readstring(MP_NEW, argv[optind], &p, 0);
-  while (isspace((unsigned char)*p)) p++;
+  while (ISSPACE(*p)) p++;
   if (!nn || *p || MP_CMP(lmin, >, nn) || MP_CMP(nn, >, lmax))
     die(EXIT_FAILURE, "bad integer `%s'", argv[optind]);
   n = mp_tolong(nn);
index df1fdae..deb27d5 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <mLib/alloc.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -97,7 +98,7 @@ static int checkhash(fhashstate *fh, const char *file, const encodeops *e)
   unsigned long n = 0, nfail = 0;
   int hf;
 
-  if (!file || strcmp(file, "-") == 0)
+  if (!file || STRCMP(file, ==, "-"))
     hfp.fp = stdin;
   else if ((hfp.fp = fopen(file, fh->f & GSF_RAW ? "r" : "rb")) == 0) {
     moan("couldn't open `%s': %s", file, strerror(errno));
@@ -124,8 +125,8 @@ static int checkhash(fhashstate *fh, const char *file, const encodeops *e)
          rc = EXIT_FAILURE;
          continue;
        }
-       if (memcmp(hfp.hbuf, hfp.hbuf + hfp.gch->hashsz,
-                  hfp.gch->hashsz) != 0) {
+       if (MEMCMP(hfp.hbuf, !=, hfp.hbuf + hfp.gch->hashsz,
+                  hfp.gch->hashsz)) {
          if (hfp.f & f_verbose)
            fprintf(stderr, "FAIL %s\n", df.buf);
          else
@@ -190,7 +191,7 @@ static int hashfiles(fhashstate *fh, const char *file, const encodeops *e)
   int rc = 0;
   int rrc;
 
-  if (!file || strcmp(file, "-") == 0)
+  if (!file || STRCMP(file, ==, "-"))
     fp = stdin;
   else if ((fp = fopen(file, fh->f & GSF_RAW ? "r" : "rb")) == 0) {
     moan("couldn't open `%s': %s", file, strerror(errno));
@@ -278,7 +279,7 @@ int main(int argc, char *argv[])
   {
     char *q = xstrdup(QUIS);
     size_t len = strlen(q);
-    if (len > 3 && strcmp(q + len - 3, "sum") == 0) {
+    if (len > 3 && STRCMP(q + len - 3, ==, "sum")) {
       q[len - 3] = 0;
       fh.gch = gethash(q);
     }
index 15552fd..f5b6445 100644 (file)
@@ -966,9 +966,9 @@ keyids, types, expiry and deletion dates, and comments.  Additional
 .RB ` \-v '
 options show more information, such as the exact time of day for expiry
 and deletion, key attributes, and a dump of the actual key data.  If the
-verbosity level is sufficiently high, passphrases are requested to
-decrypt locked keys.  Make sure nobody is looking over your shoulder
-when you do this!
+verbosity level is sufficiently high, secret parts of keys are printed,
+and passphrases are requested to decrypt locked keys.  Make sure nobody
+is looking over your shoulder when you do this!
 .SS "fingerprint"
 Reports a fingerprint (secure hash) on components of requested keys.
 The following options are supported:
index 0c817d4..c38ed5d 100644 (file)
@@ -42,6 +42,7 @@
 #include <mLib/base32.h>
 #include <mLib/base64.h>
 #include <mLib/hex.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -602,7 +603,7 @@ static void alg_dhparam(keyopts *k)
       group *g;
       const char *e;
 
-      if (strcmp(k->curve, "list") == 0) {
+      if (STRCMP(k->curve, ==, "list")) {
        unsigned i, w;
        LIST("Built-in prime fields", stdout, ptab[i].name, ptab[i].name);
        exit(0);
@@ -770,7 +771,7 @@ static void alg_binparam(keyopts *k)
     /* --- Decide on a field --- */
 
     if (!k->bits) k->bits = 128;
-    if (k->curve && strcmp(k->curve, "list") == 0) {
+    if (k->curve && STRCMP(k->curve, ==, "list")) {
       unsigned i, w;
       LIST("Built-in binary fields", stdout,
           bintab[i].name, bintab[i].name);
@@ -869,7 +870,7 @@ static void alg_ecparam(keyopts *k)
     /* --- Decide on a curve --- */
 
     if (!k->bits) k->bits = 256;
-    if (k->curve && strcmp(k->curve, "list") == 0) {
+    if (k->curve && STRCMP(k->curve, ==, "list")) {
       unsigned i, w;
       LIST("Built-in elliptic curves", stdout,
           ectab[i].name, ectab[i].name);
@@ -1093,7 +1094,7 @@ static int cmd_add(int argc, char *argv[])
        keyalg *a;
        size_t sz = strlen(optarg);
 
-       if (strcmp(optarg, "list") == 0) {
+       if (STRCMP(optarg, ==, "list")) {
          for (a = algtab; a->name; a++)
            printf("%-10s %s\n", a->name, a->help);
          return (0);
@@ -1101,7 +1102,7 @@ static int cmd_add(int argc, char *argv[])
 
        alg = 0;
        for (a = algtab; a->name; a++) {
-         if (strncmp(optarg, a->name, sz) == 0) {
+         if (STRNCMP(optarg, ==, a->name, sz)) {
            if (a->name[sz] == 0) {
              alg = a;
              break;
@@ -1140,7 +1141,7 @@ static int cmd_add(int argc, char *argv[])
       /* --- Expiry dates get passed to @get_date@ for parsing --- */
 
       case 'e':
-       if (strcmp(optarg, "forever") == 0)
+       if (STRCMP(optarg, ==, "forever"))
          exp = KEXP_FOREVER;
        else {
          exp = get_date(optarg, 0);
@@ -1178,7 +1179,7 @@ static int cmd_add(int argc, char *argv[])
 
       case 'A': {
        const struct seedalg *ss;
-       if (strcmp(optarg, "list") == 0) {
+       if (STRCMP(optarg, ==, "list")) {
          printf("Seed algorithms:\n");
          for (ss = seedtab; ss->p; ss++)
            printf("  %s\n", ss->p);
@@ -1187,7 +1188,7 @@ static int cmd_add(int argc, char *argv[])
        if (seed) die(EXIT_FAILURE, "seed already set -- put -A first");
        sa = 0;
        for (ss = seedtab; ss->p; ss++) {
-         if (strcmp(optarg, ss->p) == 0)
+         if (STRCMP(optarg, ==, ss->p))
            sa = ss;
        }
        if (!sa)
@@ -1320,7 +1321,7 @@ static int cmd_add(int argc, char *argv[])
     if (k.f & f_retag) {
       if ((kk = key_bytag(&f, tag)) != 0 &&
          kk->tag &&
-         strcmp(kk->tag, tag) == 0)
+         STRCMP(kk->tag, ==, tag))
        key_settag(&f, kk, 0);
     }
     if ((err = key_settag(&f, k.k, tag)) != 0)
@@ -1396,6 +1397,11 @@ static void showkeydata(key_data *k, int ind, listopts *o, dstr *d)
   }                                                                    \
 } while (0)
 
+  if ((k->e&KF_ENCMASK) == KENC_ENCRYPT && o->v <= 4)
+    { fputs(" encrypted\n", stdout); return; }
+  if ((k->e&KF_ENCMASK) != KENC_STRUCT && !(k->e&KF_NONSECRET) && o->v <= 3)
+    { fputs(" secret\n", stdout); return; }
+
   switch (k->e & KF_ENCMASK) {
 
     /* --- Binary key data --- *
@@ -1431,20 +1437,16 @@ static void showkeydata(key_data *k, int ind, listopts *o, dstr *d)
      * key.  Otherwise just say that it's encrypted and move on.
      */
 
-    case KENC_ENCRYPT:
-      if (o->v <= 3)
-       fputs(" encrypted\n", stdout);
+    case KENC_ENCRYPT: {
+      key_data *kd;
+      if (key_punlock(&kd, k, d->buf))
+       printf(" <failed to unlock %s>\n", d->buf);
       else {
-       key_data *kd;
-       if (key_punlock(&kd, k, d->buf))
-         printf(" <failed to unlock %s>\n", d->buf);
-       else {
-         fputs(" encrypted", stdout);
-         showkeydata(kd, ind, o, d);
-         key_drop(kd);
-       }
+       fputs(" encrypted", stdout);
+       showkeydata(kd, ind, o, d);
+       key_drop(kd);
       }
-      break;
+    } break;
 
     /* --- Integer keys --- *
      *
@@ -1825,7 +1827,7 @@ static const struct fpres *lookup_fpres(const char *name)
 {
   const struct fpres *fpres;
   for (fpres = fprestab; fpres->name; fpres++)
-    if (strcmp(fpres->name, name) == 0) return (fpres);
+    if (STRCMP(fpres->name, ==, name)) return (fpres);
   die(EXIT_FAILURE, "unknown presentation syle `%s'", name);
 }
 
@@ -1870,7 +1872,7 @@ static int cmd_finger(int argc, char *argv[])
   argv += optind; argc -= optind;
   if (rc) {
     die(EXIT_FAILURE,
-       "Usage: fingerprint [-a HASHALG] [-p STYLE] [-f FILTER] [TAG...]");
+       "Usage: fingerprint [-a HASH] [-p STYLE] [-f FILTER] [TAG...]");
   }
 
   doopen(&f, KOPEN_READ);
@@ -1944,7 +1946,7 @@ static int cmd_verify(int argc, char *argv[])
   argv += optind; argc -= optind;
   if (rc || argc != 2) {
     die(EXIT_FAILURE,
-       "Usage: verify [-a HASHALG] [-p STYLE] [-f FILTER] TAG FINGERPRINT");
+       "Usage: verify [-a HASH] [-p STYLE] [-f FILTER] TAG FINGERPRINT");
   }
 
   doopen(&f, KOPEN_READ);
@@ -1969,7 +1971,7 @@ static int cmd_verify(int argc, char *argv[])
   if (!key_fingerprint(k, h, &kf))
     die(EXIT_FAILURE, "key has no fingerprintable components (as filtered)");
   fpr = GH_DONE(h, 0);
-  if (memcmp(fpr, d.buf, ch->hashsz) != 0)
+  if (MEMCMP(fpr, !=, d.buf, ch->hashsz))
     die(EXIT_FAILURE, "key fingerprint mismatch");
   dstr_destroy(&d); dstr_destroy(&dd);
   doclose(&f);
@@ -2028,7 +2030,7 @@ static int cmd_tag(int argc, char *argv[])
     die(EXIT_FAILURE, "Usage: tag [-r] TAG [NEW-TAG]");
   doopen(&f, KOPEN_WRITE);
   if (flags & f_retag) {
-    if ((k = key_bytag(&f, argv[1])) != 0 && strcmp(k->tag, argv[1]) == 0)
+    if ((k = key_bytag(&f, argv[1])) != 0 && STRCMP(k->tag, ==, argv[1]))
       key_settag(&f, k, 0);
   }
   if ((k = key_bytag(&f, argv[0])) == 0)
@@ -2122,7 +2124,7 @@ static int cmd_extract(int argc, char *argv[])
   argv += optind; argc -= optind;
   if (rc || argc < 1)
     die(EXIT_FAILURE, "Usage: extract [-f FILTER] FILE [TAG...]");
-  if (strcmp(*argv, "-") == 0)
+  if (STRCMP(*argv, ==, "-"))
     fp = stdout;
   else {
     outfile = *argv;
@@ -2178,7 +2180,7 @@ static int cmd_merge(int argc, char *argv[])
 
   if (argc != 2)
     die(EXIT_FAILURE, "Usage: merge FILE");
-  if (strcmp(argv[1], "-") == 0)
+  if (STRCMP(argv[1], ==, "-"))
     fp = stdin;
   else if (!(fp = fopen(argv[1], "r"))) {
     die(EXIT_FAILURE, "couldn't open `%s' for reading: %s",
@@ -2233,7 +2235,7 @@ Options:\n\
 -v, --verbose          Show more information.\n\
 " },
   { "fingerprint", cmd_finger,
-    "fingerprint [-a HASHALG] [-p STYLE] [-f FILTER] [TAG...]", "\
+    "fingerprint [-a HASH] [-p STYLE] [-f FILTER] [TAG...]", "\
 Options:\n\
 \n\
 -f, --filter=FILT      Only hash key components matching FILT.\n\
index b84d850..1b36cb2 100644 (file)
@@ -40,6 +40,7 @@
 #include <mLib/bits.h>
 #include <mLib/darray.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -99,7 +100,7 @@ static void wordlist_scan(FILE *fp, void *p)
 
   for (;;) {
     int ch = getc(fp);
-    if (ch == EOF || isspace(ch)) {
+    if (ch == EOF || ISSPACE(ch)) {
       DPUTZ(&d);
       if (f && d.len >= min && d.len <= max)
        sym_find(&w->tab, d.buf, d.len + 1, sizeof(sym_base), 0);
@@ -109,7 +110,7 @@ static void wordlist_scan(FILE *fp, void *p)
        break;
       continue;
     }
-    ch = tolower(ch);
+    ch = TOLOWER(ch);
     if (strchr(wchars, ch)) {
       DPUTC(&d, ch);
       f = 1;
@@ -204,7 +205,7 @@ static void markov_scan(FILE *fp, void *p)
     const char *q;
     node *n = &(*model)[i][j][k];
 
-    if (ch == EOF || isspace(ch)) {
+    if (ch == EOF || ISSPACE(ch)) {
       if (l != C_END) {
        l = C_END;
        n->count++;
@@ -216,7 +217,7 @@ static void markov_scan(FILE *fp, void *p)
       continue;
     }
 
-    if ((q = strchr(wchars, tolower(ch))) == 0)
+    if ((q = strchr(wchars, TOLOWER(ch))) == 0)
       continue;
     l = q - wchars;
     n->count++;
@@ -275,7 +276,7 @@ static void usage(FILE *fp)
 {
   pquis(fp, "\
 Usage: $ [-p] [-b MIN[-MAX]] [-g GEN] [-n COUNT]\n\
-\t[-r [MIN-]MAX] WORDLIST...\n                   \
+\t[-r [MIN-]MAX] WORDLIST...\n\
 ");
 }
 
@@ -362,7 +363,7 @@ int main(int argc, char *argv[])
        size_t n = strlen(optarg);
        ops = 0;
        for (p = ppgentab; *p; p++) {
-         if (strncmp(optarg, (*p)->name, n) == 0) {
+         if (STRNCMP(optarg, ==, (*p)->name, n)) {
            if (!(*p)->name[n]) {
              ops = *p;
              break;
@@ -414,7 +415,7 @@ int main(int argc, char *argv[])
 
   ctx = ops->init();
   while (*argv) {
-    if (strcmp(*argv, "-") == 0)
+    if (STRCMP(*argv, ==, "-"))
       ops->scan(stdin, ctx);
     else {
       FILE *fp = fopen(*argv, "r");
index 6af4d51..b4a88ca 100644 (file)
@@ -51,6 +51,7 @@
 #include <mLib/alloc.h>
 #include <mLib/bits.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -979,7 +980,7 @@ int main(int argc, char *argv[])
   if (optind + 1 != argc) { usage(stderr); exit(1); }
 
   for (j = jobtab; j->name; j++)
-    if (strcmp(j->name, argv[optind]) == 0) break;
+    if (STRCMP(j->name, ==, argv[optind])) break;
   if (!j->name) die(1, "unknown job type `%s'", argv[optind]);
   p = j->init(&o);
 
index 3fd3208..f827c20 100644 (file)
@@ -57,6 +57,7 @@
 #include <mLib/alloc.h>
 #include <mLib/dstr.h>
 #include <mLib/fdflags.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -229,7 +230,7 @@ static phrase *p_find(const char *tag)
   phrase *p;
 
   for (p = p_head; p; p = p->next) {
-    if (strcmp(p->tag, tag) == 0) {
+    if (STRCMP(p->tag, ==, tag)) {
       if (p->t) {
        struct timeval tv;
        sel_rmtimer(&p->timer);
@@ -311,7 +312,7 @@ static void p_flush(const char *tag)
     phrase *pp = p->next;
     if (!tag)
       p_free(p);
-    else if (strcmp(p->tag, tag) == 0) {
+    else if (STRCMP(p->tag, ==, tag)) {
       if (verbose > 1)
        pxlog("flushing passphrase `%s'", tag);
       p_free(p);
@@ -445,7 +446,7 @@ static int p_request(const char *msg, const char *tag, char *buf, size_t sz)
 ok: {
     char *p = buf;
     size_t len;
-    while (isspace((unsigned char)*p))
+    while (ISSPACE(*p))
       p++;
     len = strlen(p);
     memmove(buf, p, len);
@@ -517,7 +518,7 @@ static int p_get(const char **q, const char *tag, unsigned mode, time_t exp)
       goto fail;
     if (p_request("Verify passphrase", tag, pp, LBUFSZ) < 0)
       goto fail;
-    if (strcmp(pp, p->p) != 0) {
+    if (STRCMP(pp, !=, p->p)) {
       if (verbose)
        pxlog("passphrases for `%s' don't match", tag);
       p_free(p);
@@ -677,11 +678,11 @@ static void pixserv_line(char *s, size_t len, void *p)
   if ((q = str_getword(&s)) == 0)
     return;
   for (qq = q; *qq; qq++)
-    *qq = tolower((unsigned char)*qq);
+    *qq = TOLOWER(*qq);
 
   /* --- Handle a help request --- */
 
-  if (strcmp(q, "help") == 0) {
+  if (STRCMP(q, ==, "help")) {
     if (pixserv_write(px, "\
 INFO Commands supported:\n\
 INFO flush [TAG]\n\
@@ -698,7 +699,7 @@ OK\n\
 
   /* --- List the passphrases --- */
 
-  else if (strcmp(q, "list") == 0) {
+  else if (STRCMP(q, ==, "list")) {
     phrase *p;
 
     for (p = p_head; p; p = p->next) {
@@ -718,8 +719,8 @@ OK\n\
 
   /* --- Request a passphrase --- */
 
-  else if ((mode = PMODE_READ, strcmp(q, "pass") == 0) ||
-          (mode = PMODE_VERIFY, strcmp(q, "verify") == 0)) {
+  else if ((mode = PMODE_READ, STRCMP(q, ==, "pass")) ||
+          (mode = PMODE_VERIFY, STRCMP(q, ==, "verify"))) {
     unsigned long t;
     const char *p;
     int rc;
@@ -747,7 +748,7 @@ OK\n\
 
   /* --- Flush existing passphrases --- */
 
-  else if (strcmp(q, "flush") == 0) {
+  else if (STRCMP(q, ==, "flush")) {
     q = str_getword(&s);
     p_flush(q);
     if (pixserv_write(px, "OK\n")) goto close;
@@ -755,7 +756,7 @@ OK\n\
 
   /* --- Set a passphrase --- */
 
-  else if (strcmp(q, "set") == 0) {
+  else if (STRCMP(q, ==, "set")) {
     char *tag;
     unsigned long t;
     if ((tag = str_getword(&s)) == 0) {
@@ -763,14 +764,14 @@ OK\n\
     } else if ((q = str_getword(&s)) == 0) {
       if (pixserv_write(px, "FAIL no passphrase\n")) goto close;
     } else {
-      if (strcmp(q, "--") != 0) {
+      if (STRCMP(q, !=, "--")) {
        t = pixserv_timeout(q);
        q = str_getword(&s);
       } else
        t = pixserv_timeout(0);
       if (!q) {
        if (pixserv_write(px, "FAIL no passphrase\n")) goto close;
-      } else if (strcmp(q, "--") != 0) {
+      } else if (STRCMP(q, !=, "--")) {
        if (pixserv_write(px, "FAIL rubbish found before passphrase\n"))
          goto close;
       } else {
@@ -783,7 +784,7 @@ OK\n\
 
   /* --- Shut the server down --- */
 
-  else if (strcmp(q, "quit") == 0) {
+  else if (STRCMP(q, ==, "quit")) {
     if (verbose) {
       pxlog("%s client requested shutdown",
            px->f & px_stdin ? "local" : "remote");
@@ -1076,14 +1077,14 @@ static void c_sline(char *s, size_t len, void *p)
     puts(s);
   else {
     char *q = str_getword(&s);
-    if (strcmp(q, "FAIL") == 0)
+    if (STRCMP(q, ==, "FAIL"))
       die(1, "%s", s);
-    else if (strcmp(q, "INFO") == 0 ||
-            strcmp(q, "ITEM") == 0)
+    else if (STRCMP(q, ==, "INFO") ||
+            STRCMP(q, ==, "ITEM"))
       puts(s);
-    else if (strcmp(q, "OK") == 0) {
+    else if (STRCMP(q, ==, "OK")) {
       if (s && *s) puts(s);
-    } else if (strcmp(q, "MISSING") == 0)
+    } else if (STRCMP(q, ==, "MISSING"))
       ;
     else
       moan("unexpected output: %s %s", q, s);
index 66a0538..812525f 100644 (file)
@@ -47,6 +47,7 @@
 
 #include <mLib/darray.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -435,7 +436,7 @@ static int opt(void)
       case 'o':
        if (flags & f_file)
          die(EXIT_FAILURE, "already set an output file");
-       if (strcmp(optarg, "-") == 0)
+       if (STRCMP(optarg, ==, "-"))
          outfp = stdout;
        else {
          outfp = fopen(optarg, "w");
@@ -1591,7 +1592,7 @@ int main(int ac, char *av[])
 
     g = 0;
     for (gg = generators; gg->name; gg++) {
-      if (strncmp(arg, gg->name, sz) == 0) {
+      if (STRNCMP(arg, ==, gg->name, sz)) {
        if (gg->name[sz] == 0) {
          g = gg;
          break;
index c7dc86e..c3ae268 100644 (file)
@@ -374,6 +374,8 @@ grand *bbs_rand(mp *m, mp *x)
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
+
 static int verify(dstr *v)
 {
   mp *n = *(mp **)v[0].buf;
@@ -385,7 +387,7 @@ static int verify(dstr *v)
   dstr_ensure(&d, v[2].len);
   b->ops->fill(b, d.buf, v[2].len);
   d.len = v[2].len;
-  if (memcmp(d.buf, v[2].buf, v[2].len) != 0) {
+  if (MEMCMP(d.buf, !=, v[2].buf, v[2].len)) {
     fputs("\n*** bbs failure\n", stderr);
     fputs("n = ", stderr); mp_writefile(n, stderr, 10); fputc('\n', stderr);
     fputs("x = ", stderr); mp_writefile(x, stderr, 10); fputc('\n', stderr);
index 5feda92..d27bc7d 100644 (file)
@@ -66,32 +66,42 @@ int dh_kcdsagen(dh_param *dp, unsigned ql, unsigned pl,
   rabin rb;
   int rc = PGEN_ABORT;
   int i;
-  mp *x;
+  mp *x = MP_NEW, *t = MP_NEW;
 
-  /* --- First trick: find %$q$% --- */
+  /* --- First trick: find %$v$% --- */
 
+retry:
   pf.step = 2;
-  x = mprand(MP_NEW, pl - ql, r, 1);
+  x = mprand(x, pl - ql - 1, r, 1);
   x = pgen("v", x, x, ev, ec,
           steps, pgen_filter, &pf,
           rabin_iters(pl - ql), pgen_test, &rb);
   if (!x)
     goto fail_0;
 
-  /* --- Second trick: find %$p$% and %$v$% --- */
+  /* --- Second trick: find %$p$% and %$q$% --- */
 
   x = mp_lsl(x, x, 1);
   sp[0].add = MP_ZERO; sp[0].mul = MP_ONE; sp[0].f = 0;
-  sp[1].add = MP_ONE; sp[1].mul = x; sp[1].f = PGENF_KEEP;
+  sp[1].add = MP_ONE; sp[1].mul = x; sp[1].f = PGENF_KEEP; x = MP_NEW;
   ss.step = MP_TWO; ss.v = sp; ss.n = N(sp);
-  x = mprand(MP_NEW, ql, r, 1);
+  do {
+    x = mprand(x, ql, r, 1);
+    t = mp_mul(t, x, sp[1].mul);
+  } while (mp_bits(t) != pl);
   dp->q = pgen("p", MP_NEW, x, ev, ec,
               steps, pgen_simulstep, &ss,
               rabin_iters(ql), pgen_simultest, &ss);
   mp_drop(sp[1].mul);
+  dp->p = sp[1].u.x;
   if (!dp->q)
     goto fail_1;
-  dp->p = sp[1].u.x;
+  if (mp_bits(dp->q) != ql || mp_bits(dp->p) != pl) {
+    if (steps) goto fail_1;
+    MP_DROP(dp->p);
+    MP_DROP(dp->q);
+    goto retry;
+  }
 
   /* --- Third trick: find a generator --- */
 
@@ -104,19 +114,20 @@ int dh_kcdsagen(dh_param *dp, unsigned ql, unsigned pl,
               0, prim_step, &i, 1, prim_test, &pc);
   mpmont_destroy(&pc.mm);
   if (!dp->g)
-    goto fail_2;
+    goto fail_1;
 
   rc = PGEN_DONE;
   goto done;
 
   /* --- Tidying up and going home --- */
 
-fail_2:
-  mp_drop(dp->p);
 fail_1:
+  mp_drop(dp->p);
+  mp_drop(dp->q);
 fail_0:
 done:
   mp_drop(x);
+  mp_drop(t);
   return (rc);
 }
 
index 69e2437..5b5e403 100644 (file)
@@ -108,6 +108,8 @@ found:
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
+
 #include "fibrand.h"
 
 int main(int argc, char *argv[])
@@ -127,7 +129,7 @@ int main(int argc, char *argv[])
     dh_infofromdata(&dp, pe->data);
     g = group_prime(&dp);
     if (mp_bits(dp.p) > 3072 &&
-       (!argv[1] || strcmp(argv[1], "keen") != 0)) {
+       (!argv[1] || STRCMP(argv[1], !=, "keen"))) {
       printf(" [%s skipped]", pe->name);
       fflush(stdout);
       continue;
index 7c6c3ba..8802003 100644 (file)
@@ -215,6 +215,8 @@ fail_q:
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
+
 static int verify(dstr *v)
 {
   mp *q = *(mp **)v[4].buf;
@@ -231,7 +233,7 @@ static int verify(dstr *v)
 
   rc = dsa_gen(&dp, 160, l, 16, v[0].buf, v[0].len, &ds, pgen_evspin, 0);
   if (rc || ds.count != n || ds.sz != v[2].len ||
-      memcmp(ds.p, v[2].buf, v[2].len) != 0 ||
+      MEMCMP(ds.p, !=, v[2].buf, v[2].len) ||
       !MP_EQ(q, dp.q) || !MP_EQ(p, dp.p) || !MP_EQ(g, dp.g)) {
     fputs("\n*** gen failed", stderr);
     fputs("\nseed_in = ", stderr); type_hex.dump(&v[0], stderr);
index 7593a74..88054eb 100644 (file)
@@ -119,6 +119,7 @@ void dsa_sign(dsa_param *dp, mp *a,
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
 #include <mLib/testrig.h>
 
 #include "sha.h"
@@ -145,8 +146,8 @@ static int verify(dstr *v)
           s.r, sizeof(s.r), s.s, sizeof(s.s));
 
   if (v[6].len != sizeof(s.r) || v[7].len != sizeof(s.s) ||
-      memcmp(s.r, v[6].buf, sizeof(s.r)) != 0 ||
-      memcmp(s.s, v[7].buf, sizeof(s.s)) != 0) {
+      MEMCMP(s.r, !=, v[6].buf, sizeof(s.r)) ||
+      MEMCMP(s.s, !=, v[7].buf, sizeof(s.s))) {
     fputs("\n*** signature failed", stderr);
     fputs("\nq = ", stderr); mp_writefile(dp.q, stderr, 16);
     fputs("\np = ", stderr); mp_writefile(dp.p, stderr, 16);
index 676fe8c..8374109 100644 (file)
@@ -29,6 +29,8 @@
 
 #include <string.h>
 
+#include <mLib/macros.h>
+
 #include "f25519.h"
 #include "ed25519.h"
 #include "scaf.h"
@@ -406,7 +408,7 @@ int ed25519ctx_verify(const octet K[ED25519_PUBSZ],
   scaf_loaddbl(tt, sig + 32, 32, 2*NPIECE, PIECEWD);
   scaf_reduce(s, tt, l, mu, NPIECE, PIECEWD, scratch);
   scaf_store(b, 32, s, NPIECE, PIECEWD);
-  if (memcmp(b, sig + 32, 32) != 0) return (-1);
+  if (MEMCMP(b, !=, sig + 32, 32)) return (-1);
 
   /* Check the signature. */
   psz = prefix(b, phflag, p, psz);
@@ -420,7 +422,7 @@ int ed25519ctx_verify(const octet K[ED25519_PUBSZ],
   scaf_reduce(t, tt, l, mu, NPIECE, PIECEWD, scratch);
   ptsimmul(&RX, &RY, &RZ, s, BX, BY, BZ, t, &AX, &AY, &AZ);
   ptencode(b, &RX, &RY, &RZ);
-  if (memcmp(b, sig, 32) != 0) return (-1);
+  if (MEMCMP(b, !=, sig, 32)) return (-1);
 
   /* All is good. */
   return (0);
@@ -454,7 +456,7 @@ static int vrf_pubkey(dstr dv[])
   dstr_ensure(&dpub, ED25519_PUBSZ); dpub.len = ED25519_PUBSZ;
   ed25519_pubkey((octet *)dpub.buf, dv[0].buf, dv[0].len);
   ct_remedy(dpub.buf, dpub.len);
-  if (memcmp(dpub.buf, dv[1].buf, ED25519_PUBSZ) != 0) {
+  if (MEMCMP(dpub.buf, !=, dv[1].buf, ED25519_PUBSZ)) {
     ok = 0;
     fprintf(stderr, "failed!");
     fprintf(stderr, "\n\tpriv = "); type_hex.dump(&dv[0], stderr);
@@ -493,7 +495,7 @@ static int vrf_sign(dstr *priv, int phflag, dstr *perso,
                  phflag, perso ? perso->buf : 0, perso ? perso->len : 0,
                  m->buf, m->len);
   ct_remedy(dsig.buf, dsig.len);
-  if (memcmp(dsig.buf, want->buf, ED25519_SIGSZ) != 0) {
+  if (MEMCMP(dsig.buf, !=, want->buf, ED25519_SIGSZ)) {
     ok = 0;
     fprintf(stderr, "failed!");
     fprintf(stderr, "\n\tpriv = "); type_hex.dump(priv, stderr);
index 6032802..681a3ca 100644 (file)
@@ -29,6 +29,8 @@
 
 #include <string.h>
 
+#include <mLib/macros.h>
+
 #include "fgoldi.h"
 #include "ed448.h"
 #include "scaf.h"
@@ -394,7 +396,7 @@ int ed448_verify(const octet K[ED448_PUBSZ],
   scaf_loaddbl(tt, sig + 57, 57, 2*NPIECE, PIECEWD);
   scaf_reduce(s, tt, l, mu, NPIECE, PIECEWD, scratch);
   scaf_store(b, 57, s, NPIECE, PIECEWD);
-  if (memcmp(b, sig + 57, 57) != 0) return (-1);
+  if (MEMCMP(b, !=, sig + 57, 57)) return (-1);
 
   /* Check the signature. */
   psz = prefix(b, phflag, p, psz);
@@ -408,7 +410,7 @@ int ed448_verify(const octet K[ED448_PUBSZ],
   scaf_reduce(t, tt, l, mu, NPIECE, PIECEWD, scratch);
   ptsimmul(&RX, &RY, &RZ, s, BX, BY, BZ, t, &AX, &AY, &AZ);
   ptencode(b, &RX, &RY, &RZ);
-  if (memcmp(b, sig, 57) != 0) return (-1);
+  if (MEMCMP(b, !=, sig, 57)) return (-1);
 
   /* All is good. */
   return (0);
@@ -437,7 +439,7 @@ static int vrf_pubkey(dstr dv[])
   dstr_ensure(&dpub, ED448_PUBSZ); dpub.len = ED448_PUBSZ;
   ed448_pubkey((octet *)dpub.buf, dv[0].buf, dv[0].len);
   ct_remedy(dpub.buf, dpub.len);
-  if (memcmp(dpub.buf, dv[1].buf, ED448_PUBSZ) != 0) {
+  if (MEMCMP(dpub.buf, !=, dv[1].buf, ED448_PUBSZ)) {
     ok = 0;
     fprintf(stderr, "failed!");
     fprintf(stderr, "\n\tpriv = "); type_hex.dump(&dv[0], stderr);
@@ -476,7 +478,7 @@ static int vrf_sign(dstr *priv, int phflag, dstr *perso,
             phflag, perso ? perso->buf : 0, perso ? perso->len : 0,
             m->buf, m->len);
   ct_remedy(dsig.buf, dsig.len);
-  if (memcmp(dsig.buf, want->buf, ED448_SIGSZ) != 0) {
+  if (MEMCMP(dsig.buf, !=, want->buf, ED448_SIGSZ)) {
     ok = 0;
     fprintf(stderr, "failed!");
     fprintf(stderr, "\n\tpriv = "); type_hex.dump(priv, stderr);
index 94f22aa..2399425 100644 (file)
@@ -27,6 +27,8 @@
 
 /*----- Header files ------------------------------------------------------*/
 
+#include <mLib/macros.h>
+
 #include "dsa.h"
 #include "gkcdsa.h"
 #include "group.h"
@@ -175,7 +177,7 @@ int gkcdsa_verify(const gkcdsa *c, const gkcdsa_sig *s, const void *m)
   e[1].base = g->g; e[1].exp = x;
   z = G_CREATE(g); G_MEXP(g, z, e, 2);
   h = hashge(g, c->h, z); p = GH_DONE(h, 0);
-  if (memcmp(p, s->r, hsz) == 0) rc = 0;
+  if (MEMCMP(p, ==, s->r, hsz)) rc = 0;
   mp_drop(x); mp_drop(y); G_DESTROY(g, z); GH_DESTROY(h);
   return (rc);
 }
@@ -241,7 +243,7 @@ static int tsign(dstr *v)
   gkcdsa_endhash(&c, h);
   gkcdsa_sign(&c, &ss, GH_DONE(h, 0), k);
   GH_DESTROY(h);
-  if (memcmp(s.r, ss.r, c.h->hashsz) || !MP_EQ(s.s, ss.s)) {
+  if (MEMCMP(s.r, !=, ss.r, c.h->hashsz) || !MP_EQ(s.s, ss.s)) {
     ok = 0;
     fprintf(stderr, "*** sign failed!\n");
     fprintf(stderr, "*** group: %s\n", v[0].buf);
index 590c05e..bb33351 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <mLib/bits.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 
 #include "ct.h"
 #include "grand.h"
@@ -245,7 +246,7 @@ int pkcs1_sigdecode(mp *s, const void *m, size_t msz, octet *b, size_t sz,
 
   /* --- Check the encoding parameters --- */
 
-  if (pp->ep && memcmp(q, pp->ep, pp->epsz) != 0)
+  if (pp->ep && MEMCMP(q, !=, pp->ep, pp->epsz))
     return (-1);
   q += pp->epsz;
 
index 8e77b78..137b876 100644 (file)
--- a/pub/pss.c
+++ b/pub/pss.c
@@ -32,6 +32,7 @@
 #include <mLib/alloc.h>
 #include <mLib/bits.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 
 #include "gcipher.h"
 #include "ghash.h"
@@ -174,7 +175,7 @@ int pss_decode(mp *mi, const void *m, size_t msz, octet *b, size_t sz,
   GH_HASH(h, m, msz);
   GH_HASH(h, s, pp->ssz);
   s = GH_DONE(h, 0);
-  rc = !memcmp(s, r, hsz);
+  rc = MEMCMP(s, ==, r, hsz);
   GH_DESTROY(h);
   if (!rc) return (-1);
 
index 5e13182..bb402a3 100644 (file)
@@ -29,6 +29,7 @@
 #include <mLib/alloc.h>
 #include <mLib/bits.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 
 #include "mp.h"
 #include "mpmont.h"
@@ -200,7 +201,7 @@ int rsa_verify(rsa_pubctx *rp, mp *s, const void *m, size_t msz,
   dstr_ensure(d, n);
   rc = e(p, m, msz, (octet *)d->buf + d->len, n, nb, earg);
   if (rc > 0 && m) {
-    if (rc != msz || memcmp(d->buf + d->len, m, msz) != 0)
+    if (rc != msz || MEMCMP(d->buf + d->len, !=, m, msz))
       rc = -1;
     else
       rc = 0;
index 34b2a1f..2ee6bbc 100644 (file)
@@ -27,6 +27,8 @@
 
 /*----- Header files ------------------------------------------------------*/
 
+#include <mLib/macros.h>
+
 #include "fibrand.h"
 #include "rsa.h"
 
@@ -69,7 +71,7 @@ static int tencpad(int nbits,
 #define tsigpad tencpad
 
 #define DSTR_EQ(x, y)                                                  \
-  ((x)->len == (y)->len && !memcmp((x)->buf, (y)->buf, (x)->len))
+  ((x)->len == (y)->len && MEMCMP((x)->buf, ==, (y)->buf, (x)->len))
 
 static int tdecpad(int nbits,
                   mp *c, int rc, dstr *p,
index f897129..cfdfea6 100644 (file)
@@ -111,6 +111,7 @@ void x25519(octet zz[X25519_OUTSZ],
 #include <stdio.h>
 #include <string.h>
 
+#include <mLib/macros.h>
 #include <mLib/report.h>
 #include <mLib/testrig.h>
 
@@ -131,7 +132,7 @@ static int vrf_x25519(dstr dv[])
         (const octet *)dv[0].buf,
         (const octet *)dv[1].buf);
   ct_remedy(dz.buf, dz.len);
-  if (memcmp(dz.buf, dv[2].buf, X25519_OUTSZ) != 0) {
+  if (MEMCMP(dz.buf, !=, dv[2].buf, X25519_OUTSZ)) {
     ok = 0;
     fprintf(stderr, "failed!");
     fprintf(stderr, "\n\t   k = "); type_hex.dump(&dv[0], stderr);
@@ -167,7 +168,7 @@ static int vrf_mct(dstr dv[])
   }
   memcpy(d.buf, k, d.len);
 
-  if (memcmp(d.buf, dv[3].buf, d.len) != 0) {
+  if (MEMCMP(d.buf, !=, dv[3].buf, d.len)) {
     ok = 0;
     fprintf(stderr, "failed...");
     fprintf(stderr, "\n\tinitial k = "); type_hex.dump(&dv[0], stderr);
index 56008df..192f8db 100644 (file)
  * Since then, the name `Curve25519' has shifted somewhat, to refer to the
  * specific elliptic curve used, and the x-coordinate Diffie--Hellman
  * operation is now named `X25519'.
+ *
+ * The @x25519@ function essentially performs incompatible cofactor
+ * multiplication on the elliptic curve %$E(k)$% containing points %$(x, y)$%
+ * in %$\proj^2(k)$% satisfying the Montgomery-form equation
+ *
+ *     %$y^3 = x^3 + 486662 x^2 + x$% ,
+ *
+ * where $k = \gf{p}$, with $p = 2^{255} - 19$%.  The curve has
+ * %$n = (p + 1) + 221938542218978828286815502327069187962$% points; this is
+ * eight times a prime %$\ell$%.  The points with %$x$%-coordinate 9 have
+ * order %$\ell$%.
  */
 
 /*----- Header files ------------------------------------------------------*/
index 6bef9dd..70ec10f 100644 (file)
@@ -97,8 +97,8 @@ void x448(octet zz[X448_OUTSZ],
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
 #include <mLib/report.h>
-#include <mLib/str.h>
 #include <mLib/testrig.h>
 
 #include "ct.h"
@@ -118,7 +118,7 @@ static int vrf_x448(dstr dv[])
        (const octet *)dv[0].buf,
        (const octet *)dv[1].buf);
   ct_remedy(dz.buf, dz.len);
-  if (memcmp(dz.buf, dv[2].buf, X448_OUTSZ) != 0) {
+  if (MEMCMP(dz.buf, !=, dv[2].buf, X448_OUTSZ)) {
     ok = 0;
     fprintf(stderr, "failed!");
     fprintf(stderr, "\n\t   k = "); type_hex.dump(&dv[0], stderr);
@@ -153,7 +153,7 @@ static int vrf_mct(dstr dv[])
   }
   memcpy(d.buf, k, d.len);
 
-  if (memcmp(d.buf, dv[3].buf, d.len) != 0) {
+  if (MEMCMP(d.buf, !=, dv[3].buf, d.len)) {
     ok = 0;
     fprintf(stderr, "failed...");
     fprintf(stderr, "\n\tinitial k = "); type_hex.dump(&dv[0], stderr);
index 4561d41..42c9fb9 100644 (file)
  * described in Hamburg's paper, since it doesn't involve the `Decaf'
  * cofactor elimination procedure.  Indeed, it looks very much like X25519
  * with Hamburg's curve slotted in in place of Bernstein's.
+ *
+ * The @x448@ function essentially performs incompatible cofactor
+ * multiplication on the elliptic curve %$E(k)$% containing points %$(x, y)$%
+ * in %$\proj^2(k)$% satisfying the Montgomery-form equation
+ *
+ *     %$y^3 = x^3 + 156326 x^2 + x$% ,
+ *
+ * where $k = \gf{p}$, with $p = \phi^2 - \phi - 1$%, where
+ * %$\phi = 2^{224}$%.  The curve has %$n = (p + 1) + {}$%
+ * %$28312320572429821613362531907042076847709625476988141958474579766324$%
+ * points; this is four times a prime %$\ell$%.  The points with
+ * %$x$%-coordinate 5 have order %$\ell$%.
  */
 
 /*----- Header files ------------------------------------------------------*/
index d97749e..e02ccc5 100644 (file)
@@ -67,6 +67,12 @@ librand_la_SOURCES   += noise.c
 ## Cryptographic laundering for true random data generation.
 pkginclude_HEADERS     += rand.h
 librand_la_SOURCES     += rand.c
+if CPUFAM_X86
+librand_la_SOURCES     += rand-x86ish.S
+endif
+if CPUFAM_AMD64
+librand_la_SOURCES     += rand-x86ish.S
+endif
 librand_la_SOURCES     += randgen.c
 
 ## The SSL v3 pseudorandom function.
index 70ffaf8..a4328aa 100644 (file)
@@ -282,9 +282,11 @@ static int gmisc(grand *r, unsigned op, ...)
       grand *rr = va_arg(ap, grand *);
       rr->ops->fill(rr, g->d.p, g->d.sz);
     } break;
-    case DSARAND_PASSES:
-      g->d.passes = va_arg(ap, unsigned);
-      break;
+    case DSARAND_PASSES: {
+      unsigned n = va_arg(ap, unsigned);
+      rc = g->d.passes;
+      if (n > 0) g->d.passes = n;
+    } break;
     case DSARAND_SEEDSZ:
       rc = g->d.sz;
       break;
index 1c76f65..f916c9c 100644 (file)
@@ -33,6 +33,7 @@
 #include <string.h>
 
 #include <mLib/bits.h>
+#include <mLib/macros.h>
 #include <mLib/sub.h>
 
 #include "grand.h"
@@ -106,15 +107,11 @@ uint32 lcrand(uint32 x)
 
   /* --- Now reduce mod p --- *
    *
-   * I'm using shifts and adds to do the multiply step here.  This needs to
-   * be changed if @D@ ever becomes something other than 5.
+   * I'm using shifts and adds to do the multiply step here.
    */
 
-#if D != 5
-#  error "Change shift sequence!"
-#endif
-
   {
+    STATIC_ASSERT(D == 5, "Shift sequence doesn't match prime");
     uint32 q;
 
     q = yy[1];
diff --git a/rand/rand-x86ish.S b/rand/rand-x86ish.S
new file mode 100644 (file)
index 0000000..399ccb1
--- /dev/null
@@ -0,0 +1,151 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Random-number support for x86
+///
+/// (c) 2019 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.
+
+///--------------------------------------------------------------------------
+/// Preliminaries.
+
+#include "config.h"
+#include "asm-common.h"
+
+       .extern F(rand_add)
+
+       .text
+
+///--------------------------------------------------------------------------
+/// Quick random generation.
+
+// Common register allocation.
+#if CPUFAM_X86
+#  define COUNT ecx
+#endif
+#if CPUFAM_AMD64 && ABI_SYSV
+#  define COUNT ecx
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+#  define COUNT r8d
+#endif
+
+FUNC(rand_quick_x86ish_rdrand)
+       // Enter with a pointer to the random context in the first argument.
+       // Return zero on success, or -1 on error.
+
+#if CPUFAM_X86
+       mov     edx, [SP + 4]
+       push    ebx
+       stalloc 24
+#endif
+#if CPUFAM_AMD64 && ABI_SYSV
+       stalloc 8
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+       stalloc 40
+#endif
+  endprologue
+
+       // Try to fetch a random number.
+       mov     COUNT, 16
+0:     rdrand  AX
+       jc      1f
+       dec     COUNT
+       jnz     0b
+
+       // Failed.
+       mov     eax, -1
+       jmp     9f
+ENDFUNC
+
+FUNC(rand_quick_x86ish_rdseed)
+       // Enter with a pointer to the random context in the first argument.
+       // Return zero on success, or -1 on error.
+
+#if CPUFAM_X86
+       mov     edx, [SP + 4]
+       push    ebx
+       stalloc 24
+#endif
+#if CPUFAM_AMD64 && ABI_SYSV
+       stalloc 8
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+       stalloc 40
+#endif
+  endprologue
+
+       // Try to fetch a random number.
+       mov     COUNT, 16
+0:     rdseed  AX
+       jc      1f
+       dec     COUNT
+       jnz     0b
+
+       // Failed.
+       mov     eax, -1
+       jmp     9f
+
+       // Success.
+1:
+#if CPUFAM_X86
+       ldgot   ebx
+       mov     [SP + 16], AX
+       lea     ecx, [SP + 16]
+       mov     dword ptr [SP + 12], 32
+       mov     dword ptr [SP + 8], 4
+       mov     [SP + 4], ecx
+       mov     [SP + 0], edx
+#endif
+#if CPUFAM_AMD64 && ABI_SYSV
+       mov     [SP + 0], AX
+       mov     rsi, SP
+       mov     edx, 8
+       mov     ecx, 64
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+       mov     [SP + 32], AX
+       lea     rdx, [SP + 32]
+       mov     r8d, 8
+       mov     r9d, 64
+#endif
+       callext F(rand_add)
+       xor     eax, eax
+
+       // Done.
+9:
+#if CPUFAM_X86
+       stfree  24
+       pop     ebx
+#endif
+#if CPUFAM_AMD64 && ABI_SYSV
+       stfree  8
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+       stfree  40
+#endif
+       ret
+ENDFUNC
+
+#undef COUNT
+
+///----- That's all, folks --------------------------------------------------
index 01e6422..90e8193 100644 (file)
@@ -161,24 +161,17 @@ CPU_DISPATCH(static, return, int, quick, (rand_pool *r), (r),
 
 static int trivial_quick(rand_pool *r) { return (-1); }
 
-#if __GNUC__ && (CPUFAM_X86 || CPUFAM_AMD64)
-static int rdrand_quick(rand_pool *r)
-{
-  unsigned long rr;
-  int i = 16;
-
-  __asm__ ("0: rdrand %0; jc 9f; dec %1; jnz 0b; 9:"
-          : "=r" (rr), "=r" (i) : "1" (i) : "cc");
-  if (!i) return (-1);
-  rand_add(r, &rr, sizeof(rr), 8*sizeof(rr));
-  return (0);
-}
+#if CPUFAM_X86 || CPUFAM_AMD64
+extern int rand_quick_x86ish_rdrand(rand_pool */*r*/);
+extern int rand_quick_x86ish_rdseed(rand_pool */*r*/);
 #endif
 
 static quick__functype *pick_quick(void)
 {
-#if __GNUC__ && (CPUFAM_X86 || CPUFAM_AMD64)
-  DISPATCH_PICK_COND(rand_quick, rdrand_quick,
+#if CPUFAM_X86 || CPUFAM_AMD64
+  DISPATCH_PICK_COND(rand_quick, rand_quick_x86ish_rdseed,
+                    cpu_feature_p(CPUFEAT_X86_RDSEED));
+  DISPATCH_PICK_COND(rand_quick, rand_quick_x86ish_rdrand,
                     cpu_feature_p(CPUFEAT_X86_RDRAND));
 #endif
   DISPATCH_PICK_FALLBACK(rand_quick, trivial_quick);
@@ -261,9 +254,7 @@ void rand_add(rand_pool *r, const void *p, size_t sz, unsigned goodbits)
   const octet *c = p;
   int i, rot;
 
-#if RAND_POOLSZ != 128
-#  error Polynomial in rand_add is out of date.  Fix it.
-#endif
+  STATIC_ASSERT(RAND_POOLSZ == 128, "Polynomial doesn't match pool size");
 
   RAND_RESOLVE(r);
 
@@ -316,6 +307,8 @@ void rand_gate(rand_pool *r)
   HASH_CTX hc;
   CIPHER_CTX cc;
 
+  STATIC_ASSERT(CIPHER_KEYSZ <= HASH_SZ, "rand cipher keysize too long");
+
   RAND_RESOLVE(r);
   QUICK(r);
 
@@ -331,7 +324,6 @@ void rand_gate(rand_pool *r)
 
   /* --- Now mangle all of the data based on the hash --- */
 
-  assert(CIPHER_KEYSZ <= HASH_SZ);
   CIPHER_INIT(&cc, h, CIPHER_KEYSZ, 0);
   CIPHER_ENCRYPT(&cc, r->pool, r->pool, RAND_POOLSZ);
   CIPHER_ENCRYPT(&cc, r->buf, r->buf, RAND_BUFSZ);
@@ -367,6 +359,8 @@ void rand_stretch(rand_pool *r)
   HASH_CTX hc;
   CIPHER_CTX cc;
 
+  STATIC_ASSERT(CIPHER_KEYSZ <= HASH_SZ, "rand cipher keysize too long");
+
   RAND_RESOLVE(r);
   QUICK(r);
 
@@ -382,7 +376,6 @@ void rand_stretch(rand_pool *r)
 
   /* --- Now mangle the buffer based on the hash --- */
 
-  assert(CIPHER_KEYSZ <= HASH_SZ);
   CIPHER_INIT(&cc, h, CIPHER_KEYSZ, 0);
   CIPHER_ENCRYPT(&cc, r->buf, r->buf, RAND_BUFSZ);
   BURN(cc);
index 06187b3..9601f4e 100644 (file)
@@ -310,6 +310,7 @@ grand *sslprf_rand(const gchash *hco, const gchash *hci,
 #include <stdio.h>
 #include <string.h>
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -327,7 +328,7 @@ static int v_generate(dstr *v)
   d.len = v[2].len;
   g->ops->fill(g, d.buf, d.len);
   g->ops->destroy(g);
-  if (memcmp(v[2].buf, d.buf, d.len) != 0) {
+  if (MEMCMP(v[2].buf, !=, d.buf, d.len)) {
     ok = 0;
     printf("\nfail sslprf:"
           "\n\tkey        = ");
index fa28faf..95f5b94 100644 (file)
@@ -493,6 +493,7 @@ grand *tlsprf_rand(const gcmac *mcx, const gcmac *mcy,
 #include <stdio.h>
 #include <string.h>
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -511,7 +512,7 @@ static int v_generate(dstr *v)
   d.len = v[2].len;
   g->ops->fill(g, d.buf, d.len);
   g->ops->destroy(g);
-  if (memcmp(v[2].buf, d.buf, d.len) != 0) {
+  if (MEMCMP(v[2].buf, !=, d.buf, d.len)) {
     ok = 0;
     printf("\nfail tlsprf:"
           "\n\tkey        = ");
index 68d9267..e87d741 100644 (file)
@@ -476,6 +476,12 @@ $(srcdir)/t/sha3: $(SHA3_TESTS) sha3-trans t/sha3.local
                  cat t/sha3.local; } >t/sha3.new && \
                mv t/sha3.new t/sha3
 
+## Mike Hamburg's STROBE protocol framework.
+pkginclude_HEADERS     += strobe.h
+libsymm_la_SOURCES     += strobe.c
+TESTS                  += strobe.t$(EXEEXT)
+EXTRA_DIST             += t/strobe
+
 ## Bellare, Canetti and Krawczyk's `HMAC' mode for message authentication.
 HASHMACMODES           += hmac
 
@@ -587,7 +593,7 @@ pkginclude_HEADERS  += poly1305.h
 libsymm_la_SOURCES     += poly1305.c
 TESTS                  += poly1305.t$(EXEEXT)
 TESTS                  += poly1305-p11.t$(EXEEXT)
-EXTRA_DIST             += t/poly1305
+EXTRA_DIST             += t/poly1305 t/poly1305.slow
 
 check_PROGRAMS         += poly1305-p11.t
 poly1305_p11_t_SOURCES  = poly1305.c
index bbba763..444ec8d 100644 (file)
 
 #include <string.h>
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
+#ifndef BLKC_TESTHOOK
+#  define BLKC_TESTHOOK do ; while (0)
+#endif
+
 #define BLKC_VERIFY(PRE, pre) BLKC_VERIFYX(PRE, pre, #pre)
 
 #define BLKC_VERIFYX(PRE, pre, name)                                   \
@@ -429,7 +434,7 @@ static int pre##_verify(dstr *v)                                    \
   BLKC_MOVE(PRE, d, p);                                                        \
   pre##_eblk(&k, d, d);                                                        \
   BLKC_STORE(PRE, b.buf, d);                                           \
-  if (memcmp(b.buf, v[2].buf, PRE##_BLKSZ)) {                          \
+  if (MEMCMP(b.buf, !=, v[2].buf, PRE##_BLKSZ)) {                      \
     ok = 0;                                                            \
     printf("\nfail encryption:"                                                \
           "\n\tkey        = ");                                        \
@@ -445,7 +450,7 @@ static int pre##_verify(dstr *v)                                    \
   BLKC_MOVE(PRE, d, c);                                                        \
   pre##_dblk(&k, d, d);                                                        \
   BLKC_STORE(PRE, b.buf, d);                                           \
-  if (memcmp(b.buf, v[1].buf, PRE##_BLKSZ)) {                          \
+  if (MEMCMP(b.buf, !=, v[1].buf, PRE##_BLKSZ)) {                      \
     ok = 0;                                                            \
     printf("\nfail decryption:"                                                \
           "\n\tkey        = ");                                        \
@@ -477,6 +482,7 @@ static const test_chunk defs[] = {                                  \
                                                                        \
 int main(int argc, char *argv[])                                       \
 {                                                                      \
+  BLKC_TESTHOOK;                                                       \
   test_run(argc, argv, defs, SRCDIR"/t/" fname);                       \
   return (0);                                                          \
 }
index 2d864fa..7c6bd2e 100644 (file)
@@ -338,9 +338,9 @@ int pre##_ccmencrypt(pre##_ccmctx *ctx,                                     \
   /* Determine the buffering plan.  Our buffer is going to do double-  \
    * duty here.  The end portion is going to contain mask from the     \
    * encrypted counter which we mix into the plaintext to encrypt it;  \
-   * the start portion, which originally mask bytes we've already used,        \
-   * will hold the input plaintext, which will eventually be           \
-   * collected into the CBC-MAC state.                                 \
+   * the start portion, which originally contained mask bytes we've    \
+   * already used, will hold the input plaintext, which will           \
+   * eventually be collected into the CBC-MAC state.                   \
    */                                                                  \
   rsvr_mkplan(&plan, &pre##_ccmpolicy, ctx->off, sz);                  \
                                                                        \
@@ -427,9 +427,9 @@ int pre##_ccmdecrypt(pre##_ccmctx *ctx,                                     \
   /* Determine the buffering plan.  Our buffer is going to do double-  \
    * duty here.  The end portion is going to contain mask from the     \
    * encrypted counter which we mix into the plaintext to encrypt it;  \
-   * the start portion, which originally mask bytes we've already used,        \
-   * will hold the recovered plaintext, which will eventually be       \
-   * collected into the CBC-MAC state.                                 \
+   * the start portion, which originally mask contained bytes we've    \
+   * already used, will hold the recovered plaintext, which will       \
+   * eventually be collected into the CBC-MAC state.                   \
    */                                                                  \
   rsvr_mkplan(&plan, &pre##_ccmpolicy, ctx->off, sz);                  \
                                                                        \
@@ -715,13 +715,23 @@ static gaead_key *gckey(const void *k, size_t ksz)                        \
   return (&key->k);                                                    \
 }                                                                      \
                                                                        \
+static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz)      \
+{                                                                      \
+  ccm_params p;                                                                \
+                                                                       \
+  if (!gaead_szokcommon(&pre##_ccm, nsz, hsz, msz, tsz)) return (0);   \
+  p.hsz = hsz; p.msz = msz; p.bsz = PRE##_BLKSZ; p.nsz = nsz; p.tsz = tsz; \
+  if (!ccm_check(&p)) return (0);                                      \
+  return (1);                                                          \
+}                                                                      \
+                                                                       \
 const gcaead pre##_ccm = {                                             \
   name "-ccm",                                                         \
   pre##_keysz, pre##_ccmnoncesz, pre##_ccmtagsz,                       \
   PRE##_BLKSZ, 0, 0,                                                   \
   AEADF_PCHSZ | AEADF_PCMSZ | AEADF_PCTSZ |                            \
   AEADF_AADNDEP | AEADF_AADFIRST,                                      \
-  gckey                                                                        \
+  gckey, gcszok                                                                \
 };                                                                     \
                                                                        \
 CCM_TESTX(PRE, pre, name, fname)
@@ -742,6 +752,7 @@ CCM_TESTX(PRE, pre, name, fname)
 #include <stdio.h>
 
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -802,8 +813,8 @@ static int ccmverify(dstr *v)                                               \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[4].len ||                                           \
-       memcmp(d.buf, v[4].buf, v[4].len) != 0 ||                       \
-       memcmp(t.buf, v[5].buf, v[5].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[4].buf, v[4].len) ||                        \
+       MEMCMP(t.buf, !=, v[5].buf, v[5].len)) {                        \
     fail_enc:                                                          \
       printf("\nfail encrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
@@ -855,7 +866,7 @@ static int ccmverify(dstr *v)                                               \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[3].len || !win ||                                   \
-       memcmp(d.buf, v[3].buf, v[3].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[3].buf, v[3].len)) {                        \
     fail_dec:                                                          \
       printf("\nfail decrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
index d65e049..939335c 100644 (file)
@@ -81,7 +81,7 @@
  * Then H = F || N || Q and C_i = 0^9 || [q - 1]_7 || N || [i]_{8q}.
  */
 
-/* --- @ccm_paramsok@ --- *
+/* --- @ccm_check@ --- *
  *
  * Arguments:  @const ccm_params *p@ = pointer to parameters
  *
index a900db7..2160def 100644 (file)
@@ -36,7 +36,7 @@
        .text
 
 ///--------------------------------------------------------------------------
-/// Main.code.
+/// Main code.
 
 FUNC(chacha_core_arm_neon)
 
index 61ac51a..9fe39c9 100644 (file)
@@ -35,7 +35,7 @@
        .text
 
 ///--------------------------------------------------------------------------
-/// Main.code.
+/// Main code.
 
 FUNC(chacha_core_arm64)
 
index 3fb623a..974ec5b 100644 (file)
@@ -66,15 +66,15 @@ FUNC(chacha_core_x86ish_sse2)
 #  define SAVE0 xmm5
 #  define SAVE1 xmm6
 #  define SAVE2 xmm7
-#  define SAVE3 [esp]
+#  define SAVE3 [SP]
 
-       pushreg ebp
+       pushreg BP
        setfp
-       sub     esp, 16
-       mov     IN, [ebp + 12]
-       mov     OUT, [ebp + 16]
-       and     esp, ~15
-       mov     NR, [ebp + 8]
+       stalloc 16
+       mov     IN, [BP + 12]
+       mov     OUT, [BP + 16]
+       and     SP, ~15
+       mov     NR, [BP + 8]
 #endif
 
 #if CPUFAM_AMD64 && ABI_SYSV
@@ -105,9 +105,9 @@ FUNC(chacha_core_x86ish_sse2)
 #  define IN rdx
 #  define OUT r8
 #  define SAVE0 xmm5
-#  define SAVE1 [rsp +  0]
-#  define SAVE2 [rsp + 16]
-#  define SAVE3 [rsp + 32]
+#  define SAVE1 [SP +  0]
+#  define SAVE2 [SP + 16]
+#  define SAVE3 [SP + 32]
 
        stalloc 48 + 8
 #endif
@@ -248,7 +248,7 @@ FUNC(chacha_core_x86ish_sse2)
        // Tidy things up.
 #if CPUFAM_X86
        dropfp
-       popreg  ebp
+       popreg  BP
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
        stfree  48 + 8
index 90a4c67..8cc16b8 100644 (file)
@@ -849,9 +849,14 @@ CHACHA_VARS(DEFXGRAND)
 #include <stdio.h>
 #include <string.h>
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
+#ifdef ENABLE_ASM_DEBUG
+#  include "regdump.h"
+#endif
+
 #define DEFVCORE(r)                                                    \
   static int v_core_##r(dstr *v)                                       \
   {                                                                    \
@@ -870,7 +875,7 @@ CHACHA_VARS(DEFXGRAND)
     }                                                                  \
     for (i = 0; i < CHACHA_OUTSZ/4; i++) STORE32_L(d.buf + 4*i, a[i]); \
                                                                        \
-    if (d.len != v[2].len || memcmp(d.buf, v[2].buf, v[2].len) != 0) { \
+    if (d.len != v[2].len || MEMCMP(d.buf, !=, v[2].buf, v[2].len)) {  \
       ok = 0;                                                          \
       printf("\nfail core:"                                            \
             "\n\titerations = %d"                                      \
@@ -940,7 +945,7 @@ CHACHA_VARS(DEFVCORE)
       }                                                                        \
       if (sz) BASE##_ENCRYPT(r, &ctx, p, q, sz);                       \
                                                                        \
-      if (d.len != v[5].len || memcmp(d.buf, v[5].buf, v[5].len) != 0) { \
+      if (d.len != v[5].len || MEMCMP(d.buf, !=, v[5].buf, v[5].len)) {        \
        ok = 0;                                                         \
        printf("\nfail encrypt:"                                        \
               "\n\tstep           = %lu"                               \
@@ -986,6 +991,9 @@ CHACHA_VARS(DEFXTAB)
 
 int main(int argc, char *argv[])
 {
+#ifdef ENABLE_ASM_DEBUG
+  regdump_init();
+#endif
   test_run(argc, argv, defs, SRCDIR"/t/chacha");
   return (0);
 }
index 701d3b5..ee8040a 100644 (file)
@@ -323,6 +323,7 @@ CMAC_TESTX(PRE, pre, name, fname)
 #include <stdio.h>
 
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -362,7 +363,7 @@ static int macverify(dstr *v)                                               \
       csz -= i;                                                                \
     }                                                                  \
     pre##_cmacdone(&cctx, d.buf);                                      \
-    if (memcmp(d.buf, v[2].buf, PRE##_BLKSZ) != 0) {                   \
+    if (MEMCMP(d.buf, !=, v[2].buf, PRE##_BLKSZ)) {                    \
       printf("\nfail:\n\tstep = %i\n\tkey = ", *ip);                   \
       type_hex.dump(&v[0], stdout);                                    \
       fputs("\n\tinput = ", stdout);                                   \
index 6e1c7ca..8daf2d8 100644 (file)
@@ -123,7 +123,7 @@ void pre##_eaxsetkey(pre##_eaxkey *key, const void *k, size_t ksz)  \
    *                                                                   \
    *   * %$z_i = E_K(t_0 \xor m_0)$% is the tweak with the `full final \
    *    buffer' mask applied, which is the final tag for a final empty \
-   *    message. \
+   *    message.                                                       \
    */                                                                  \
   BLKC_BSET(PRE, t, 0); pre##_eblk(&key->ctx, t, key->v0);             \
   BLKC_XMOVE(PRE, t, key->m0); pre##_eblk(&key->ctx, t, key->z0);      \
@@ -275,9 +275,9 @@ int pre##_eaxencrypt(pre##_eaxctx *ctx,                                     \
   /* Determine the buffering plan.  Our buffer is going to do double-  \
    * duty here.  The end portion is going to contain mask from the     \
    * encrypted counter which we mix into the plaintext to encrypt it;  \
-   * the start portion, which originally mask bytes we've already used,        \
-   * will hold the output ciphertext, which will eventually be         \
-   * collected into the OMAC state.                                    \
+   * the start portion, which originally contained mask bytes we've    \
+   * already used, will hold the output ciphertext, which will         \
+   * eventually be collected into the OMAC state.                      \
    */                                                                  \
   rsvr_mkplan(&plan, &pre##_omacpolicy, ctx->off, sz);                 \
                                                                        \
@@ -362,9 +362,9 @@ int pre##_eaxdecrypt(pre##_eaxctx *ctx,                                     \
   /* Determine the buffering plan.  Our buffer is going to do double-  \
    * duty here.  The end portion is going to contain mask from the     \
    * encrypted counter which we mix into the plaintext to encrypt it;  \
-   * the start portion, which originally mask bytes we've already used,        \
-   * will hold the input ciphertext, which will eventually be          \
-   * collected into the OMAC state.                                    \
+   * the start portion, which originally contained mask bytes we've    \
+   * already used, will hold the input ciphertext, which will          \
+   * eventually be collected into the OMAC state.                      \
    */                                                                  \
   rsvr_mkplan(&plan, &pre##_omacpolicy, ctx->off, sz);                 \
                                                                        \
@@ -675,11 +675,14 @@ static gaead_key *gckey(const void *k, size_t ksz)                        \
   return (&key->k);                                                    \
 }                                                                      \
                                                                        \
+static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz)      \
+  { return (gaead_szokcommon(&pre##_eax, nsz, hsz, msz, tsz)); }       \
+                                                                       \
 const gcaead pre##_eax = {                                             \
   name "-eax",                                                         \
   pre##_keysz, pre##_eaxnoncesz, pre##_eaxtagsz,                       \
   PRE##_BLKSZ, 0, 0, 0,                                                        \
-  gckey                                                                        \
+  gckey, gcszok                                                                \
 };                                                                     \
                                                                        \
 EAX_TESTX(PRE, pre, name, fname)
@@ -700,6 +703,7 @@ EAX_TESTX(PRE, pre, name, fname)
 #include <stdio.h>
 
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -761,8 +765,8 @@ static int eaxverify(dstr *v)                                               \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[4].len ||                                           \
-       memcmp(d.buf, v[4].buf, v[4].len) != 0 ||                       \
-       memcmp(t.buf, v[5].buf, v[5].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[4].buf, v[4].len) ||                        \
+       MEMCMP(t.buf, !=, v[5].buf, v[5].len)) {                        \
     fail_enc:                                                          \
       printf("\nfail encrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
@@ -803,7 +807,7 @@ static int eaxverify(dstr *v)                                               \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[3].len || !win ||                                   \
-       memcmp(d.buf, v[3].buf, v[3].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[3].buf, v[3].len)) {                        \
     fail_dec:                                                          \
       printf("\nfail decrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
index 45aeb61..d513069 100644 (file)
 
 /*----- Main code ---------------------------------------------------------*/
 
+/* --- @gaead_szokcommon@ --- *
+ *
+ * Arguments:  @const gcaead *aec@ = pointer to AEAD class
+ *             @size_t nsz@, @size_t hsz@, @size_t msz@, @size_t tsz@ =
+ *                     nonce, header, message, and tag sizes
+ *
+ * Returns:    Nonzero if the sizes are acceptable to the AEAD scheme in
+ *             combination.
+ *
+ * Use:                Generic implementation for sensible AEAD schemes.
+ */
+
+int gaead_szokcommon(const gcaead *aec,
+                    size_t nsz, size_t hsz, size_t msz, size_t tsz)
+{
+  if (keysz(nsz, aec->noncesz) != nsz) return (0);
+  if (keysz(tsz, aec->tagsz) != tsz) return (0);
+  if (hsz && (aec->f&AEADF_NOAAD)) return (0);
+  return (1);
+}
+
 /* --- @gaead_encrypt@ --- *
  *
  * Arguments:  @const gaead_key *k@ = the AEAD key, already prepared
index 34f95aa..ffa008c 100644 (file)
@@ -392,9 +392,9 @@ int pre##_gcmencrypt(pre##_gcmctx *ctx,                                     \
   /* Determine the buffering plan.  Our buffer is going to do double-  \
    * duty here.  The end portion is going to contain mask from the     \
    * encrypted counter which we mix into the plaintext to encrypt it;  \
-   * the start portion, which originally mask bytes we've already used,        \
-   * will hold the output ciphertext, which will eventually be         \
-   * collected into the GHASH state.                                   \
+   * the start portion, which originally contained mask bytes we've    \
+   * already used, will hold the output ciphertext, which will         \
+   * eventually be collected into the GHASH state.                     \
    */                                                                  \
   rsvr_mkplan(&plan, &pre##_gcmpolicy, ctx->off, sz);                  \
                                                                        \
@@ -477,9 +477,9 @@ int pre##_gcmdecrypt(pre##_gcmctx *ctx,                                     \
   /* Determine the buffering plan.  Our buffer is going to do double-  \
    * duty here.  The end portion is going to contain mask from the     \
    * encrypted counter which we mix into the plaintext to encrypt it;  \
-   * the start portion, which originally mask bytes we've already used,        \
-   * will hold the input ciphertext, which will eventually be          \
-   * collected into the GHASH state.                                   \
+   * the start portion, which originally contained mask bytes we've    \
+   * already used, will hold the input ciphertext, which will          \
+   * eventually be collected into the GHASH state.                     \
    */                                                                  \
   rsvr_mkplan(&plan, &pre##_gcmpolicy, ctx->off, sz);                  \
                                                                        \
@@ -790,11 +790,14 @@ static gaead_key *gckey(const void *k, size_t ksz)                        \
   return (&key->k);                                                    \
 }                                                                      \
                                                                        \
+static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz)      \
+  { return (gaead_szokcommon(&pre##_gcm, nsz, hsz, msz, tsz)); }       \
+                                                                       \
 const gcaead pre##_gcm = {                                             \
   name "-gcm",                                                         \
   pre##_keysz, pre##_gcmnoncesz, pre##_gcmtagsz,                       \
   PRE##_BLKSZ, 0, 0, 0,                                                        \
-  gckey                                                                        \
+  gckey, gcszok                                                                \
 };                                                                     \
                                                                        \
 GCM_TESTX(PRE, pre, name, fname)
@@ -815,6 +818,7 @@ GCM_TESTX(PRE, pre, name, fname)
 #include <stdio.h>
 
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -876,8 +880,8 @@ static int gcmverify(dstr *v)                                               \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[4].len ||                                           \
-       memcmp(d.buf, v[4].buf, v[4].len) != 0 ||                       \
-       memcmp(t.buf, v[5].buf, v[5].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[4].buf, v[4].len) ||                        \
+       MEMCMP(t.buf, !=, v[5].buf, v[5].len)) {                        \
     fail_enc:                                                          \
       printf("\nfail encrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
@@ -918,7 +922,7 @@ static int gcmverify(dstr *v)                                               \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[3].len || !win ||                                   \
-       memcmp(d.buf, v[3].buf, v[3].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[3].buf, v[3].len)) {                        \
     fail_dec:                                                          \
       printf("\nfail decrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
index e60b7ca..5edf56e 100644 (file)
        // xmm3 =                       // v_0 = (v_01; v_00)
        movdqa  xmm4, xmm0              // u_1 again
 #if CPUFAM_X86
-       movdqa  [esp + 0], xmm3
+       movdqa  [SP + 0], xmm3
 #elif CPUFAM_AMD64
        movdqa  xmm8, xmm3
 #  define V0 xmm8
        pclmullqlqdq xmm4, xmm2         // u_11 v_11
        pclmulhqhqdq xmm7, xmm2         // u_10 v_10
 #if CPUFAM_X86
-       movdqa  xmm2, [esp + 0]
+       movdqa  xmm2, [SP + 0]
 #  define V0 xmm2
 #endif
        pxor    xmm0, xmm3              // u_10 v_11 + u_11 v_10
@@ -771,8 +771,8 @@ SSEFUNC(gcm_mulk_128b_x86ish_pclmul)
        // A is updated with the product A K.
 
 #if CPUFAM_X86
-       mov     A, [esp + 4]
-       mov     K, [esp + 8]
+       mov     A, [SP + 4]
+       mov     K, [SP + 8]
 #endif
   endprologue
        movdqu  xmm0, [A]
@@ -790,8 +790,8 @@ SSEFUNC(gcm_mulk_128l_x86ish_pclmul)
        // exit, A is updated with the product A K.
 
 #if CPUFAM_X86
-       mov     A, [esp + 4]
-       mov     K, [esp + 8]
+       mov     A, [SP + 4]
+       mov     K, [SP + 8]
        ldgot   ecx
 #endif
   endprologue
@@ -811,8 +811,8 @@ SSEFUNC(gcm_mulk_64b_x86ish_pclmul)
        // A is updated with the product A K.
 
 #if CPUFAM_X86
-       mov     A, [esp + 4]
-       mov     K, [esp + 8]
+       mov     A, [SP + 4]
+       mov     K, [SP + 8]
 #endif
   endprologue
        movq    xmm0, [A]
@@ -830,8 +830,8 @@ SSEFUNC(gcm_mulk_64l_x86ish_pclmul)
        // exit, A is updated with the product A K.
 
 #if CPUFAM_X86
-       mov     A, [esp + 4]
-       mov     K, [esp + 8]
+       mov     A, [SP + 4]
+       mov     K, [SP + 8]
        ldgot   ecx
 #endif
   endprologue
@@ -852,8 +852,8 @@ SSEFUNC(gcm_mulk_96b_x86ish_pclmul)
        // with the product A K.
 
 #if CPUFAM_X86
-       mov     A, [esp + 4]
-       mov     K, [esp + 8]
+       mov     A, [SP + 4]
+       mov     K, [SP + 8]
 #endif
   endprologue
        movq    xmm0, [A + 0]
@@ -876,8 +876,8 @@ SSEFUNC(gcm_mulk_96l_x86ish_pclmul)
        // updated with the product A K.
 
 #if CPUFAM_X86
-       mov     A, [esp + 4]
-       mov     K, [esp + 8]
+       mov     A, [SP + 4]
+       mov     K, [SP + 8]
        ldgot   ecx
 #endif
   endprologue
@@ -901,8 +901,8 @@ SSEFUNC(gcm_mulk_192b_x86ish_pclmul)
        // A is updated with the product A K.
 
 #if CPUFAM_X86
-       mov     A, [esp + 4]
-       mov     K, [esp + 8]
+       mov     A, [SP + 4]
+       mov     K, [SP + 8]
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
        stalloc 2*16 + 8
@@ -935,8 +935,8 @@ SSEFUNC(gcm_mulk_192l_x86ish_pclmul)
        // exit, A is updated with the product A K.
 
 #if CPUFAM_X86
-       mov     A, [esp + 4]
-       mov     K, [esp + 8]
+       mov     A, [SP + 4]
+       mov     K, [SP + 8]
        ldgot   ecx
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
@@ -970,12 +970,12 @@ SSEFUNC(gcm_mulk_256b_x86ish_pclmul)
        // A is updated with the product A K.
 
 #if CPUFAM_X86
-       pushreg ebp
+       pushreg BP
        setfp
-       mov     A, [esp + 8]
-       mov     K, [esp + 12]
-       and     esp, ~15
-       sub     esp, 16
+       mov     A, [SP + 8]
+       mov     K, [SP + 12]
+       stalloc 16
+       and     SP, ~15
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
        stalloc 3*16 + 8
@@ -997,7 +997,7 @@ SSEFUNC(gcm_mulk_256b_x86ish_pclmul)
        movdqu  [A + 0], xmm1
 #if CPUFAM_X86
        dropfp
-       popreg  ebp
+       popreg  BP
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
        rstrxmm xmm6, 0
@@ -1014,13 +1014,13 @@ SSEFUNC(gcm_mulk_256l_x86ish_pclmul)
        // exit, A is updated with the product A K.
 
 #if CPUFAM_X86
-       pushreg ebp
+       pushreg BP
        setfp
-       mov     A, [esp + 8]
-       mov     K, [esp + 12]
-       and     esp, ~15
+       mov     A, [SP + 8]
+       mov     K, [SP + 12]
+       stalloc 16
        ldgot   ecx
-       sub     esp, 16
+       and     SP, ~15
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
        stalloc 3*16 + 8
@@ -1044,7 +1044,7 @@ SSEFUNC(gcm_mulk_256l_x86ish_pclmul)
        movdqu  [A + 0], xmm1
 #if CPUFAM_X86
        dropfp
-       popreg  ebp
+       popreg  BP
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
        rstrxmm xmm6, 0
index 73b2851..b438415 100644 (file)
@@ -778,9 +778,14 @@ void gcm_concat(const gcm_params *p, uint32 *z, const uint32 *x,
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
+#ifdef ENABLE_ASM_DEBUG
+#  include "regdump.h"
+#endif
+
 static void report_failure(const char *test, unsigned nbits,
                           const char *ref, dstr v[], dstr *d)
 {
@@ -820,7 +825,7 @@ static int test_mul(uint32 poly, dstr v[])
 
 #define CHECK(E, what, ref) do {                                       \
   for (i = 0; i < nbits/32; i++) STORE32_##E(d.buf + 4*i, z[i]);       \
-  if (memcmp(d.buf, v[I_##ref].buf, nbits/8) != 0)                     \
+  if (MEMCMP(d.buf, !=, v[I_##ref].buf, nbits/8))                      \
     { ok = 0; report_failure(what, nbits, #ref, v, &d); }              \
 } while (0)
 
@@ -873,6 +878,9 @@ GCM_WIDTHS(TEST)
 int main(int argc,  char *argv[])
 {
   ego(argv[0]);
+#ifdef ENABLE_ASM_DEBUG
+  regdump_init();
+#endif
   test_run(argc, argv, defs, SRCDIR"/t/gcm");
   return (0);
 }
index 29b733b..92fe792 100644 (file)
@@ -9,6 +9,8 @@
 
 #include <string.h>
 
+#include <mLib/macros.h>
+
 #include "@what.h"
 
 %repeat
@@ -27,6 +29,6 @@ const @cls *@{what}_byname(const char *p)
   const @cls *const *c;
 
   for (c = @{what}tab; *c; c++)
-    if (strcmp(p, (*c)->name) == 0) return (*c);
+    if (STRCMP(p, ==, (*c)->name)) return (*c);
   return (0);
 }
index 3d0c117..5b2e7fb 100644 (file)
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -178,7 +179,7 @@ static int vrf_##pre(dstr *v, const test_type *msgty)                       \
       sz -= i;                                                         \
     }                                                                  \
     pre##_done(&ctx, d.buf);                                           \
-    if (memcmp(d.buf, v[1].buf, PRE##_HASHSZ) != 0) {                  \
+    if (MEMCMP(d.buf, !=, v[1].buf, PRE##_HASHSZ)) {                   \
       printf("\nfail:\n\tstep = %i\n\tinput = ", *ip);                 \
       msgty->dump(&v[0], stdout);                                      \
       printf("\n\texpected = ");                                       \
@@ -222,7 +223,7 @@ static int vrf_##pre##_rep(dstr *v)                                 \
   while (n--) pre##_hash(&ctx, p, len);                                        \
   pre##_done(&ctx, d.buf);                                             \
                                                                        \
-  if (memcmp(d.buf, v[2].buf, PRE##_HASHSZ) != 0) {                    \
+  if (MEMCMP(d.buf, !=, v[2].buf, PRE##_HASHSZ)) {                     \
     printf("\nfail:\n\tinput = `%s'\n\treps = `%i'\n\texpected = ",    \
             v[0].buf, *(int *)v[1].buf);                               \
     type_hex.dump(&v[2], stdout);                                      \
index e3f12ce..76d578d 100644 (file)
 /* --- Useful constants --- */                                         \
                                                                        \
 const octet pre##_hmackeysz[] =                                                \
-  { KSZ_ANY | KSZ_16BIT, PRE##_STATESZ/256, PRE##_STATESZ%256 };       \
+  { KSZ_ANY | KSZ_16BIT, PRE##_HASHSZ/256, PRE##_HASHSZ%256 };         \
 const octet pre##_sslmackeysz[] =                                      \
-  { KSZ_ANY | KSZ_16BIT, PRE##_STATESZ/256, PRE##_STATESZ%256 };       \
+  { KSZ_ANY | KSZ_16BIT, PRE##_HASHSZ/256, PRE##_HASHSZ%256 };         \
 const octet pre##_nmackeysz[] =                                                \
-  { KSZ_SET | KSZ_16BIT,                                               \
-    2*PRE##_STATESZ/256, 2*PRE##_STATESZ%256, 0, 0 };                  \
+  { KSZ_SET | KSZ_16BIT, 2*PRE##_HASHSZ/256, 2*PRE##_HASHSZ%256, 0, 0 }; \
                                                                        \
 /* --- @pre_nmacinit@ --- *                                            \
  *                                                                     \
@@ -360,6 +359,7 @@ HMAC_TESTX(PRE, pre, name, fname)
 
 #include <stdio.h>
 
+#include <mLib/macros.h>
 #include <mLib/dstr.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
@@ -400,7 +400,7 @@ static int macverify(dstr *v)                                               \
       csz -= i;                                                                \
     }                                                                  \
     pre##_macdone(&cctx, d.buf);                                       \
-    if (memcmp(d.buf, v[2].buf, PRE##_HASHSZ) != 0) {                  \
+    if (MEMCMP(d.buf, !=, v[2].buf, PRE##_HASHSZ)) {                   \
       printf("\nfail:\n\tstep = %i\n\tinput = `%s'\n\tkey = ",         \
             *ip, v[0].buf);                                            \
       type_hex.dump(&v[1], stdout);                                    \
index d58bc6f..cfbfdef 100644 (file)
@@ -221,6 +221,8 @@ static const lane rcon[24] = {
  * `keccak1600_round' below for the details.
  */
 
+#define COMPL_MASK 0x00121106u
+
 #define STATE_INIT(z) do {                                             \
   lane cmpl = LANE_CMPL;                                               \
   (z)->S[I(1, 0)] = cmpl; (z)->S[I(2, 0)] = cmpl;                      \
@@ -240,6 +242,8 @@ static const lane rcon[24] = {
 #else
 /* A target with fused and/not (`bic', `andc2').  Everything is simple. */
 
+#define COMPL_MASK 0u
+
 #define STATE_INIT(z) do ; while (0)
 #define STATE_OUT(z) do ; while (0)
 
@@ -585,6 +589,35 @@ void keccak1600_mix(keccak1600_state *s, const kludge64 *p, size_t n)
     { a = TO_LANE(p[i]); XOR_LANE(s->S[i], s->S[i], a); }
 }
 
+/* --- @keccak1600_set@ --- *
+ *
+ * Arguments:  @keccak1600_state *s@ = a state to update
+ *             @const kludge64 *p@ = pointer to 64-bit words to mix in
+ *             @size_t n@ = size of the input, in 64-bit words
+ *
+ * Returns:    ---
+ *
+ * Use:                Stores data into a %$\Keccak[r, 1600 - r]$% state.  Note that
+ *             it's the caller's responsibility to pass in no more than
+ *             %$r$% bits of data.
+ *
+ *             This is not the operation you wanted for ordinary hashing.
+ *             It's provided for the use of higher-level protocols which use
+ *             duplexing and other fancy sponge features.
+ */
+
+void keccak1600_set(keccak1600_state *s, const kludge64 *p, size_t n)
+{
+  uint32 m = COMPL_MASK;
+  unsigned i;
+  lane a;
+
+  for (i = 0; i < n; i++) {
+    a = TO_LANE(p[i]); if (m&1) NOT_LANE(a, a);
+    s->S[i] = a; m >>= 1;
+  }
+}
+
 /* --- @keccak1600_extract@ --- *
  *
  * Arguments:  @const keccak1600_state *s@ = a state to extract output from
@@ -600,11 +633,14 @@ void keccak1600_mix(keccak1600_state *s, const kludge64 *p, size_t n)
 
 void keccak1600_extract(const keccak1600_state *s, kludge64 *p, size_t n)
 {
+  uint32 m = COMPL_MASK;
   unsigned i;
-  keccak1600_state t;
+  lane t;
 
-  t = *s; STATE_OUT(&t);
-  for (i = 0; i < n; i++) p[i] = FROM_LANE(t.S[i]);
+  for (i = 0; i < n; i++) {
+    t = s->S[i]; if (m&1) NOT_LANE(t, t);
+    *p++ = FROM_LANE(t); m >>= 1;
+  }
 }
 
 /*----- Test rig ----------------------------------------------------------*/
@@ -613,6 +649,7 @@ void keccak1600_extract(const keccak1600_state *s, kludge64 *p, size_t n)
 
 #include <stdio.h>
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
 #include <mLib/testrig.h>
@@ -637,7 +674,7 @@ static int vrf_p(dstr v[])
   keccak1600_p(&u, &u, n);
   keccak1600_extract(&u, t, 25);
   for (i = 0; i < 25; i++) STORE64_L_(d.buf + 8*i, t[i]);
-  if (memcmp(d.buf, v[2].buf, 200) != 0) {
+  if (MEMCMP(d.buf, !=, v[2].buf, 200)) {
     ok = 0;
     fprintf(stderr, "failed!");
     fprintf(stderr, "\n\t     input = "); type_hex.dump(&v[0], stderr);
index 2867be9..f5aad98 100644 (file)
@@ -112,6 +112,26 @@ extern void keccak1600_init(keccak1600_state */*s*/);
 extern void keccak1600_mix(keccak1600_state */*s*/,
                           const kludge64 */*p*/, size_t /*n*/);
 
+/* --- @keccak1600_set@ --- *
+ *
+ * Arguments:  @keccak1600_state *s@ = a state to update
+ *             @const kludge64 *p@ = pointer to 64-bit words to mix in
+ *             @size_t n@ = size of the input, in 64-bit words
+ *
+ * Returns:    ---
+ *
+ * Use:                Stores data into a %$\Keccak[r, 1600 - r]$% state.  Note that
+ *             it's the caller's responsibility to pass in no more than
+ *             %$r$% bits of data.
+ *
+ *             This is not the operation you wanted for ordinary hashing.
+ *             It's provided for the use of higher-level protocols which use
+ *             duplexing and other fancy sponge features.
+ */
+
+extern void keccak1600_set(keccak1600_state */*s*/,
+                          const kludge64 */*p*/, size_t /*n*/);
+
 /* --- @keccak1600_extract@ --- *
  *
  * Arguments:  @const keccak1600_state *s@ = a state to extract output from
index af917fa..885b748 100644 (file)
@@ -445,19 +445,27 @@ static gaead_key *gkey_##latin##_common(const gaead_keyops *ops,  \
 static gaead_key *gkey_##latin##_poly1305(const void *k, size_t ksz)   \
   { return (gkey_##latin##_common(&gkops_##latin##_poly1305, k, ksz)); } \
                                                                        \
+static int gszok_##latin##_poly1305(size_t nsz, size_t hsz,            \
+                                   size_t msz, size_t tsz)             \
+  { return (gaead_szokcommon(&latin##_poly1305, nsz, hsz, msz, tsz)); }        \
+                                                                       \
 static gaead_key *gkey_##latin##_naclbox(const void *k, size_t ksz)    \
   { return (gkey_##latin##_common(&gkops_##latin##_naclbox, k, ksz)); }        \
                                                                        \
+static int gszok_##latin##_naclbox(size_t nsz, size_t hsz,             \
+                                  size_t msz, size_t tsz)              \
+  { return (gaead_szokcommon(&latin##_poly1305, nsz, hsz, msz, tsz)); } \
+                                                                       \
 const gcaead latin##_poly1305 = {                                      \
   name "-poly1305", latin##_keysz, latinpoly_noncesz, latinpoly_tagsz, \
   64, 0, 0, AEADF_AADNDEP,                                             \
-  gkey_##latin##_poly1305                                              \
+  gkey_##latin##_poly1305, gszok_##latin##_poly1305                    \
 };                                                                     \
                                                                        \
 const gcaead latin##_naclbox = {                                       \
   name "-naclbox", latin##_keysz, latinpoly_noncesz, latinpoly_tagsz,  \
   64, 0, 0, AEADF_AADNDEP | AEADF_NOAAD,                               \
-  gkey_##latin##_naclbox                                               \
+  gkey_##latin##_naclbox, gszok_##latin##_naclbox                      \
 };
 
 /*----- That's all, folks -------------------------------------------------*/
index 5a914e5..f7a2076 100644 (file)
@@ -27,6 +27,8 @@
 
 /*----- Header files ------------------------------------------------------*/
 
+#include <mLib/macros.h>
+
 #include "latinpoly-def.h"
 
 /*----- Main code ---------------------------------------------------------*/
@@ -62,8 +64,9 @@ int latinpoly_test(const gcaead *aec, dstr *v)
   if (rc) { printf("!! encryptdone reports failure\n"); goto encfail; }
 
   out.len = BLEN(&b); tag.len = POLY1305_TAGSZ;
-  if (out.len != v[4].len || memcmp(out.buf, v[4].buf, v[4].len) ||
-      memcmp(tag.buf, v[5].buf, v[5].len)) {
+  if (out.len != v[4].len ||
+      MEMCMP(out.buf, !=, v[4].buf, v[4].len) ||
+      MEMCMP(tag.buf, !=, v[5].buf, v[5].len)) {
   encfail:
     ok = 0;
     printf("\n%s encrypt FAILED", aec->name);
@@ -91,7 +94,8 @@ int latinpoly_test(const gcaead *aec, dstr *v)
   if (rc < 0) { printf("!! decryptdone reports failure\n"); goto decfail; }
 
   out.len = BLEN(&b); tag.len = POLY1305_TAGSZ;
-  if (out.len != v[3].len || memcmp(out.buf, v[3].buf, v[3].len) || !rc) {
+  if (out.len != v[3].len || MEMCMP(out.buf, !=, v[3].buf, v[3].len) ||
+      !rc) {
   decfail:
     ok = 0;
     printf("\ndecrypt FAILED");
index b06d230..a6c28ba 100644 (file)
@@ -38,6 +38,7 @@
 #include <mLib/bits.h>
 #include <mLib/dstr.h>
 #include <mLib/quis.h>
+#include <mLib/macros.h>
 #include <mLib/report.h>
 
 #include "modes-test.h"
@@ -164,7 +165,7 @@ static int regress_data(int rmode, FILE *fp, const char *what,
       if (!fread(t, sz, 1, fp))
        die(1, "failed to read %s: %s", what,
            ferror(fp) ? strerror(errno) : "unexpected eof");
-      if (memcmp(p, t, sz) != 0) return (-1);
+      if (MEMCMP(p, !=, t, sz)) return (-1);
       return (0);
     default:
       abort();
@@ -372,7 +373,7 @@ int test_encmode(const char *name,
     if (!p || !*p) {
       if (i >= argc) break;
       p = argv[i++];
-      if (strcmp(p, "--") == 0) break;
+      if (STRCMP(p, ==, "--")) break;
       if (p[0] != '-' || p[1] == 0) { i--; break; }
       p++;
     }
@@ -478,7 +479,7 @@ int test_encmode(const char *name,
     regr = 0;
     if ((f&TEMF_REFALIGN) && (sz0%blksz || sz1%blksz)) regr = 1;
     else if (!refp) { memcpy(ref, ct, TEXTSZ); regr = 1; refp = 1; }
-    else if (memcmp(ref, ct, TEXTSZ) != 0) {
+    else if (MEMCMP(ref, !=, ct, TEXTSZ)) {
       ok = 0;
       printf("\nConsistency failure (split = %lu/%lu/%lu)\n",
             (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2);
@@ -509,7 +510,7 @@ int test_encmode(const char *name,
     }
     if (sz2)
       dec(ct + sz0 + sz1, pt + sz0 + sz1, sz2);
-    if (memcmp(text, pt, TEXTSZ) != 0) {
+    if (MEMCMP(text, !=, pt, TEXTSZ)) {
       ok = 0;
       printf("\nRound-trip failure (split = %lu/%lu/%lu)\n",
             (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2);
index 4c1921e..8af0f10 100755 (executable)
@@ -30,7 +30,9 @@ import itertools as IT
 import optparse as OP
 import os as OS
 import re as RX
-from cStringIO import StringIO
+import sys as SYS
+if SYS.version_info >= (3,): from io import StringIO
+else: from cStringIO import StringIO
 from sys import argv, exit, stderr
 
 ###--------------------------------------------------------------------------
@@ -49,6 +51,16 @@ def indexed(seq):
   """
   return IT.izip(IT.count(), seq)
 
+if SYS.version_info >= (3,):
+  def func_name(func): return func.__name__
+  IT.izip = zip
+else:
+  def func_name(func): return func.func_name
+
+try: next
+except NameError:
+  def next(obj): return obj.next()
+
 ###--------------------------------------------------------------------------
 ### Reading the input values.
 
@@ -363,7 +375,7 @@ class SequenceTemplate (BasicTemplate):
     if len(seq) == 1:
       return seq[0]
     else:
-      return super(SequenceTemplate, cls).__new__(cls, seq = seq, **kw)
+      return super(SequenceTemplate, cls).__new__(cls, **kw)
 
   def __init__(me, seq, **kw):
     """
@@ -461,7 +473,7 @@ class ParseState (object):
 
     Sets `curr' to the next line, or to None if the input is exhausted.
     """
-    try: me.curr = me._it.next()
+    try: me.curr = next(me._it)
     except StopIteration: me.curr = None
     else: me._i += 1
 
@@ -504,7 +516,7 @@ def defop(func):
   An operator function is given the raw value as an argument and should
   return the transformed value.
   """
-  name = func.func_name
+  name = func_name(func)
   if name.startswith('op_'): name = name[3:]
   OPMAP[name] = func
   return func
@@ -576,7 +588,7 @@ def parse_text(ps):
     ## object.
     l = lit.getvalue()
     if l: tt.append(LiteralTemplate(l))
-    lit.reset()
+    lit.seek(0)
     lit.truncate()
 
   ## Iterate over the lines of input.
@@ -786,7 +798,7 @@ def filenames(filetempl):
 
 ## Main dispatch.
 if opts.mode == 'list':
-  for file, cs in filenames(filetempl): print file
+  for file, cs in filenames(filetempl): print(file)
 elif opts.mode == 'gen':
   with open(opts.input) as f:
     templ = RepeatTemplate(compile_template(opts.input, f.read()))
index 7c7eb29..09b3824 100644 (file)
@@ -495,11 +495,14 @@ static gaead_key *gckey(const void *k, size_t ksz)                        \
   return (&key->k);                                                    \
 }                                                                      \
                                                                        \
+static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz)      \
+  { return (gaead_szokcommon(&pre##_ocb1, nsz, hsz, msz, tsz)); }      \
+                                                                       \
 const gcaead pre##_ocb1 = {                                            \
   name "-ocb1",                                                                \
   pre##_keysz, pre##_ocb1noncesz, pre##_ocb1tagsz,                     \
   PRE##_BLKSZ, PRE##_BLKSZ, 0, 0,                                      \
-  gckey                                                                        \
+  gckey, gcszok                                                                \
 };                                                                     \
                                                                        \
 OCB1_TESTX(PRE, pre, name, fname)
@@ -520,6 +523,7 @@ OCB1_TESTX(PRE, pre, name, fname)
 #include <stdio.h>
 
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -581,8 +585,8 @@ static int ocb1verify(dstr *v)                                              \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[4].len ||                                           \
-       memcmp(d.buf, v[4].buf, v[4].len) != 0 ||                       \
-       memcmp(t.buf, v[5].buf, v[5].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[4].buf, v[4].len) ||                        \
+       MEMCMP(t.buf, !=, v[5].buf, v[5].len)) {                        \
     fail_enc:                                                          \
       printf("\nfail encrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
@@ -623,7 +627,7 @@ static int ocb1verify(dstr *v)                                              \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[3].len || !win ||                                   \
-       memcmp(d.buf, v[3].buf, v[3].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[3].buf, v[3].len)) {                        \
     fail_dec:                                                          \
       printf("\nfail decrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
index 14a22f6..a5ee46e 100644 (file)
@@ -768,11 +768,14 @@ static gaead_key *gckey(const void *k, size_t ksz)                        \
   return (&key->k);                                                    \
 }                                                                      \
                                                                        \
+static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz)      \
+  { return (gaead_szokcommon(&pre##_ocb3, nsz, hsz, msz, tsz)); }      \
+                                                                       \
 const gcaead pre##_ocb3 = {                                            \
   name "-ocb3",                                                                \
   pre##_keysz, pre##_ocb3noncesz, pre##_ocb3tagsz,                     \
   PRE##_BLKSZ, PRE##_BLKSZ - 1, 0, AEADF_PCTSZ,                                \
-  gckey                                                                        \
+  gckey, gcszok                                                                \
 };                                                                     \
                                                                        \
 OCB3_TESTX(PRE, pre, name, fname)
@@ -854,8 +857,8 @@ static int ocb3verify(dstr *v)                                              \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[4].len ||                                           \
-       memcmp(d.buf, v[4].buf, v[4].len) != 0 ||                       \
-       memcmp(t.buf, v[5].buf, v[5].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[4].buf, v[4].len) ||                        \
+       MEMCMP(t.buf, !=, v[5].buf, v[5].len)) {                        \
     fail_enc:                                                          \
       printf("\nfail encrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
@@ -896,7 +899,7 @@ static int ocb3verify(dstr *v)                                              \
     d.len = BLEN(&b);                                                  \
                                                                        \
     if (d.len != v[3].len || !win ||                                   \
-       memcmp(d.buf, v[3].buf, v[3].len) != 0) {                       \
+       MEMCMP(d.buf, !=, v[3].buf, v[3].len)) {                        \
     fail_dec:                                                          \
       printf("\nfail decrypt:\n\tstep = %i", *ip);                     \
       fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout);       \
@@ -965,7 +968,7 @@ static int ocb3mct(dstr *v)                                         \
   if (rc) goto fail;                                                   \
                                                                        \
   d.len = tsz;                                                         \
-  if (memcmp(d.buf, v[1].buf, tsz) != 0) {                             \
+  if (MEMCMP(d.buf, !=, v[1].buf, tsz)) {                              \
   fail:                                                                        \
     printf("\nfail mct: ksz = %u, tsz = %u", ksz, tsz);                        \
     fputs("\n\texp tag = ", stdout); type_hex.dump(&v[1], stdout);     \
index 4b0da04..5df9338 100644 (file)
@@ -288,6 +288,7 @@ PMAC1_TESTX(PRE, pre, name, fname)
 #include <stdio.h>
 
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -327,7 +328,7 @@ static int macverify(dstr *v)                                               \
       csz -= i;                                                                \
     }                                                                  \
     pre##_pmac1done(&cctx, d.buf);                                     \
-    if (memcmp(d.buf, v[2].buf, PRE##_BLKSZ) != 0) {                   \
+    if (MEMCMP(d.buf, !=, v[2].buf, PRE##_BLKSZ)) {                    \
       printf("\nfail:\n\tstep = %i\n\tkey = ", *ip);                   \
       type_hex.dump(&v[0], stdout);                                    \
       fputs("\n\tinput = ", stdout);                                   \
index c4a88a8..39c832f 100644 (file)
@@ -679,9 +679,9 @@ void poly1305_concat(poly1305_ctx *ctx,
 #define BIT (1ul << (ULONG_BITS - 1))
   if (n) {
     i = ULONG_BITS;
-    while (!(n & BIT)) { n <<= 1; i--; }
+    while (!(n&BIT)) { n <<= 1; i--; }
     mul_r(prefix, x, x); n <<= 1; i--;
-    while (i--) { sqr(x, x); if (n & BIT) mul_r(prefix, x, x); n <<= 1; }
+    while (i--) { sqr(x, x); if (n&BIT) mul_r(prefix, x, x); n <<= 1; }
   }
 #undef BIT
   mul(x, prefix->u.P.h, x);
@@ -789,10 +789,10 @@ void poly1305_done(poly1305_ctx *ctx, void *h)
   /* Convert this mess back into 32-bit words.  We lose the top two bits,
    * but that's fine.
    */
-  h0 = (h0 >>  0) | ((h1 & 0x0000003f) << 26);
-  h1 = (h1 >>  6) | ((h2 & 0x00000fff) << 20);
-  h2 = (h2 >> 12) | ((h3 & 0x0003ffff) << 14);
-  h3 = (h3 >> 18) | ((h4 & 0x00ffffff) <<  8);
+  h0 = (h0 >>  0) | ((h1&0x0000003f) << 26);
+  h1 = (h1 >>  6) | ((h2&0x00000fff) << 20);
+  h2 = (h2 >> 12) | ((h3&0x0003ffff) << 14);
+  h3 = (h3 >> 18) | ((h4&0x00ffffff) <<  8);
 
   /* All done. */
   STORE32_L(p +  0, h0); STORE32_L(p +  4, h1);
@@ -861,6 +861,7 @@ void poly1305_done(poly1305_ctx *ctx, void *h)
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
 #include <mLib/testrig.h>
 
 #include "ct.h"
@@ -888,7 +889,7 @@ static int vrf_hash(dstr v[])
       poly1305_hash(&ctx, v[2].buf + j, v[2].len - j);
       poly1305_done(&ctx, t.buf);
       ct_remedy(t.buf, t.len);
-      if (memcmp(t.buf, v[3].buf, 16) != 0) {
+      if (MEMCMP(t.buf, !=, v[3].buf, 16)) {
        fprintf(stderr, "failed...");
        fprintf(stderr, "\n\tkey    = "); type_hex.dump(&v[0], stderr);
        fprintf(stderr, "\n\tmask   = "); type_hex.dump(&v[1], stderr);
@@ -932,7 +933,7 @@ static int vrf_cat(dstr v[])
       poly1305_concat(&ctx, &ctx, &cc[2]);
     }
     poly1305_done(&ctx, t.buf);
-    if (memcmp(t.buf, v[5].buf, 16) != 0) {
+    if (MEMCMP(t.buf, !=, v[5].buf, 16)) {
       fprintf(stderr, "failed...");
       fprintf(stderr, "\n\tkey    = "); type_hex.dump(&v[0], stderr);
       fprintf(stderr, "\n\tmask   = "); type_hex.dump(&v[1], stderr);
@@ -955,27 +956,38 @@ static int vrf_cat(dstr v[])
 static int vrf_mct(dstr v[])
 {
   unsigned j, msz;
-  unsigned long i, niter;
+  unsigned long i, start_iter, end_iter;
   rijndael_ecbctx rij;
   poly1305_key key;
   poly1305_ctx mac;
-  dstr d = DSTR_INIT;
-  octet k[16], r[16], n[16], s[16], *t, m[MSZMAX] = { 0 };
+  dstr dk = DSTR_INIT, dr = DSTR_INIT, dn = DSTR_INIT,
+    dt = DSTR_INIT, dm = DSTR_INIT;
+  octet *k, *r, s[16], *n, *t, *m;
   int ok = 1;
 
-  if (v[0].len != sizeof(k)) { fprintf(stderr, "AES key len\n"); exit(2); }
-  if (v[1].len != sizeof(r)) { fprintf(stderr, "poly key len\n"); exit(2); }
-  if (v[2].len != sizeof(n)) { fprintf(stderr, "nonce len\n"); exit(2); }
-  if (v[4].len != sizeof(n)) { fprintf(stderr, "result len\n"); exit(2); }
-  memcpy(k, v[0].buf, sizeof(k));
-  memcpy(r, v[1].buf, sizeof(k));
-  memcpy(n, v[2].buf, sizeof(k));
-  niter = *(unsigned long *)v[3].buf;
-  dstr_ensure(&d, 16); d.len = 16; t = (octet *)d.buf;
-
-  rijndael_ecbinit(&rij, k, sizeof(k), 0);
-  poly1305_keyinit(&key, r, sizeof(r));
-  for (i = 0; i < niter; i++) {
+  DENSURE(&dk, 16);     k = (octet *)dk.buf; dk.len = 16;
+  DENSURE(&dr, 16);     r = (octet *)dr.buf; dr.len = 16;
+  DENSURE(&dn, 16);     n = (octet *)dn.buf; dn.len = 16;
+  DENSURE(&dt, 16);     t = (octet *)dt.buf; dt.len = 16;
+  DENSURE(&dm, MSZMAX); m = (octet *)dm.buf; dm.len = MSZMAX;
+  memset(m, 0, MSZMAX);
+
+  if (v[0].len != 16)     { fprintf(stderr, "AES key len\n"); exit(2); }
+  if (v[1].len != 16)     { fprintf(stderr, "poly key len\n"); exit(2); }
+  if (v[2].len != 16)     { fprintf(stderr, "nonce len\n"); exit(2); }
+  if (v[3].len != MSZMAX) { fprintf(stderr, "msgbuf len\n"); exit(2); }
+  if (v[6].len != 16)     { fprintf(stderr, "result len\n"); exit(2); }
+  memcpy(k, v[0].buf, 16);
+  memcpy(r, v[1].buf, 16);
+  memcpy(n, v[2].buf, 16);
+  memcpy(m, v[3].buf, MSZMAX);
+  start_iter = *(unsigned long *)v[4].buf;
+  end_iter = *(unsigned long *)v[5].buf;
+  if (end_iter < start_iter) { fprintf(stderr, "iter bounds\n"); exit(2); }
+
+  rijndael_ecbinit(&rij, k, 16, 0);
+  poly1305_keyinit(&key, r, 16);
+  for (i = start_iter; i < end_iter; i++) {
     msz = 0;
     for (;;) {
       rijndael_ecbencrypt(&rij, n, s, 16);
@@ -987,29 +999,39 @@ static int vrf_mct(dstr v[])
       for (j = 0; j < 16; j++) n[j] ^= t[j];
       if (msz%2) {
        for (j = 0; j < 16; j++) k[j] ^= t[j];
-       rijndael_ecbinit(&rij, k, sizeof(k), 0);
+       rijndael_ecbinit(&rij, k, 16, 0);
       }
       if (msz%3) {
        for (j = 0; j < 16; j++) r[j] ^= t[j];
-       poly1305_keyinit(&key, r, sizeof(r));
+       poly1305_keyinit(&key, r, 16);
       }
       m[msz++] ^= t[0];
     }
   }
 
-  if (memcmp(t, v[4].buf, 16) != 0) {
+  if (MEMCMP(t, !=, v[6].buf, 16)) {
     ok = 0;
     fprintf(stderr, "failed...");
     fprintf(stderr, "\n\tinitial k = "); type_hex.dump(&v[0], stderr);
     fprintf(stderr, "\n\tinitial r = "); type_hex.dump(&v[1], stderr);
     fprintf(stderr, "\n\tinitial n = "); type_hex.dump(&v[2], stderr);
-    fprintf(stderr, "\n\titerations = %lu", niter);
-    fprintf(stderr, "\n\texpected = "); type_hex.dump(&v[4], stderr);
-    fprintf(stderr, "\n\tcalculated = "); type_hex.dump(&d, stderr);
+    fprintf(stderr, "\n\tinitial m = "); type_hex.dump(&v[3], stderr);
+    fprintf(stderr, "\n\tstart iter = %lu", start_iter);
+    fprintf(stderr, "\n\tend iter = %lu", end_iter);
+    fprintf(stderr, "\n\tfinal k = "); type_hex.dump(&dk, stderr);
+    fprintf(stderr, "\n\tfinal r = "); type_hex.dump(&dr, stderr);
+    fprintf(stderr, "\n\tfinal n = "); type_hex.dump(&dn, stderr);
+    fprintf(stderr, "\n\tfinal m = "); type_hex.dump(&dm, stderr);
+    fprintf(stderr, "\n\texpected = "); type_hex.dump(&v[6], stderr);
+    fprintf(stderr, "\n\tcalculated = "); type_hex.dump(&dt, stderr);
     fputc('\n', stderr);
   }
 
-  dstr_destroy(&d);
+  dstr_destroy(&dk);
+  dstr_destroy(&dr);
+  dstr_destroy(&dn);
+  dstr_destroy(&dt);
+  dstr_destroy(&dm);
   return (ok);
 }
 
@@ -1019,7 +1041,8 @@ static const struct test_chunk tests[] = {
   { "poly1305-cat", vrf_cat,
     { &type_hex, &type_hex, &type_hex, &type_hex, &type_hex, &type_hex } },
   { "poly1305-mct", vrf_mct,
-    { &type_hex, &type_hex, &type_hex, &type_ulong, &type_hex } },
+    { &type_hex, &type_hex, &type_hex, &type_hex,
+      &type_ulong, &type_ulong, &type_hex } },
   { 0, 0, { 0 } }
 };
 
index df59425..97963fe 100644 (file)
@@ -250,6 +250,7 @@ void rc2_dblk(const rc2_ctx *k, const uint32 *s, uint32 *dst)
 
 #ifdef TEST_RIG
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -276,7 +277,7 @@ static int verify(dstr *v)
   BLKC_MOVE(RC2, d, p);
   rc2_eblk(&k, d, d);
   BLKC_STORE(RC2, b.buf, d);
-  if (memcmp(b.buf, v[3].buf, RC2_BLKSZ)) {
+  if (MEMCMP(b.buf, !=, v[3].buf, RC2_BLKSZ)) {
     ok = 0;
     printf("\nfail encryption:"
           "\n\tkey        = ");
@@ -293,7 +294,7 @@ static int verify(dstr *v)
   BLKC_MOVE(RC2, d, c);
   rc2_dblk(&k, d, d);
   BLKC_STORE(RC2, b.buf, d);
-  if (memcmp(b.buf, v[2].buf, RC2_BLKSZ)) {
+  if (MEMCMP(b.buf, !=, v[2].buf, RC2_BLKSZ)) {
     ok = 0;
     printf("\nfail decryption:"
           "\n\tkey        = ");
index 214dbc1..4cb0e87 100644 (file)
@@ -296,6 +296,7 @@ grand *rc4_rand(const void *k, size_t sz)
 #include <stdio.h>
 #include <string.h>
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
@@ -310,7 +311,7 @@ static int v_encrypt(dstr *v)
   d.len = v[1].len;
   rc4_encrypt(&ctx, v[1].buf, d.buf, d.len);
 
-  if (memcmp(v[2].buf, d.buf, d.len) != 0) {
+  if (MEMCMP(v[2].buf, !=, d.buf, d.len)) {
     ok = 0;
     printf("\nfail encryption:"
           "\n\tkey        = ");
@@ -336,7 +337,7 @@ static int v_generate(dstr *v)
   d.len = v[2].len;
   rc4_encrypt(&ctx, 0, d.buf, d.len);
 
-  if (memcmp(v[2].buf, d.buf, d.len) != 0) {
+  if (MEMCMP(v[2].buf, !=, d.buf, d.len)) {
     ok = 0;
     printf("\nfail generation:"
           "\n\tkey        = ");
index 98f6173..df0bb9d 100644 (file)
@@ -131,7 +131,7 @@ FUNC(rijndael_setup_arm64_crypto)
        sub     x6, x6, #1
        cmp     x8, x3
        cbz     x6, 9f
-       csel    x8, x8, xzr, cc
+       cmov.cs x8, xzr
        b       0b
 
        // Next job is to construct the decryption keys.  The keys for the
index 6d9b3b2..f5e5cc9 100644 (file)
@@ -70,15 +70,12 @@ ENDFUNC
 
 FUNC(rijndael_setup_x86ish_aesni)
 
-#define SI WHOLE(si)
-#define DI WHOLE(di)
-
 #if CPUFAM_X86
        // Arguments are on the stack.  We'll need to stack the caller's
        // register veriables, but we'll manage.
 
-#  define CTX ebp                      // context pointer
-#  define BLKSZ [esp + 24]             // block size
+#  define CTX BP                       // context pointer
+#  define BLKSZ [SP + 24]              // block size
 
 #  define KSZ ebx                      // key size
 #  define NKW edx                      // total number of key words
@@ -92,15 +89,15 @@ FUNC(rijndael_setup_x86ish_aesni)
 #  define BLKOFF edx                   // block size in bytes
 
        // Stack the caller's registers.
-       pushreg ebp
+       pushreg BP
        pushreg ebx
        pushreg esi
        pushreg edi
 
        // Set up our own variables.
-       mov     CTX, [esp + 20]         // context base pointer
-       mov     SI, [esp + 28]          // key material
-       mov     KSZ, [esp + 32]         // key size, in words
+       mov     CTX, [SP + 20]          // context base pointer
+       mov     SI, [SP + 28]           // key material
+       mov     KSZ, [SP + 32]          // key size, in words
 #endif
 
 #if CPUFAM_AMD64 && ABI_SYSV
@@ -330,7 +327,7 @@ FUNC(rijndael_setup_x86ish_aesni)
        popreg  edi
        popreg  esi
        popreg  ebx
-       popreg  ebp
+       popreg  BP
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
        popreg  rdi
@@ -389,8 +386,8 @@ ENDFUNC
 #  define DST edx
 #  define NR ecx
 
-       mov     K, [esp + 4]
-       mov     SRC, [esp + 8]
+       mov     K, [SP + 4]
+       mov     SRC, [SP + 8]
 #endif
 
 #if CPUFAM_AMD64 && ABI_SYSV
@@ -428,7 +425,7 @@ ENDFUNC
        add     K, 16
        pxor    xmm0, xmm1
 #if CPUFAM_X86
-       mov     DST, [esp + 12]
+       mov     DST, [SP + 12]
 #endif
 
        // Dispatch to the correct code.
index 7db9e01..597ae98 100644 (file)
 
 #include <mLib/bits.h>
 
+#if defined(TEST_RIG) && defined(ENABLE_ASM_DEBUG)
+#  include "regdump.h"
+#  define BLKC_TESTHOOK do { regdump_init(); } while (0)
+#endif
+
 #include "blkc.h"
 #include "dispatch.h"
 #include "gcipher.h"
index 424f8f9..46562fe 100644 (file)
 
 #include <mLib/bits.h>
 
+#if defined(TEST_RIG) && defined(ENABLE_ASM_DEBUG)
+#  include "regdump.h"
+#  define BLKC_TESTHOOK do { regdump_init(); } while (0)
+#endif
+
 #include "blkc.h"
 #include "gcipher.h"
 #include "rijndael.h"
index 9fb7298..4030ab2 100644 (file)
 
 #include <mLib/bits.h>
 
+#if defined(TEST_RIG) && defined(ENABLE_ASM_DEBUG)
+#  include "regdump.h"
+#  define BLKC_TESTHOOK do { regdump_init(); } while (0)
+#endif
+
 #include "blkc.h"
 #include "gcipher.h"
 #include "rijndael.h"
index 3b6beb0..9725e8a 100644 (file)
@@ -36,7 +36,7 @@
        .text
 
 ///--------------------------------------------------------------------------
-/// Main.code.
+/// Main code.
 
 FUNC(salsa20_core_arm_neon)
 
index 864c63c..a71e53b 100644 (file)
@@ -35,7 +35,7 @@
        .text
 
 ///--------------------------------------------------------------------------
-/// Main.code.
+/// Main code.
 
 FUNC(salsa20_core_arm64)
 
index 5dc9c17..26bab89 100644 (file)
@@ -65,16 +65,16 @@ FUNC(salsa20_core_x86ish_sse2)
 #  define OUT edx
 #  define SAVE0 xmm6
 #  define SAVE1 xmm7
-#  define SAVE2 [esp + 0]
-#  define SAVE3 [esp + 16]
+#  define SAVE2 [SP + 0]
+#  define SAVE3 [SP + 16]
 
-       pushreg ebp
+       pushreg BP
        setfp
-       sub     esp, 32
-       mov     IN, [ebp + 12]
-       mov     OUT, [ebp + 16]
-       and     esp, ~15
-       mov     NR, [ebp + 8]
+       stalloc 32
+       mov     IN, [BP + 12]
+       mov     OUT, [BP + 16]
+       and     SP, ~15
+       mov     NR, [BP + 8]
 #endif
 
 #if CPUFAM_AMD64 && ABI_SYSV
@@ -107,8 +107,8 @@ FUNC(salsa20_core_x86ish_sse2)
 #  define OUT r8
 #  define SAVE0 xmm6
 #  define SAVE1 xmm7
-#  define SAVE2 [rsp + 32]
-#  define SAVE3 [rsp + 48]
+#  define SAVE2 [SP + 32]
+#  define SAVE3 [SP + 48]
 
        stalloc 64 + 8
        savexmm xmm6, 0
@@ -301,7 +301,7 @@ FUNC(salsa20_core_x86ish_sse2)
        // Tidy things up.
 #if CPUFAM_X86
        dropfp
-       popreg  ebp
+       popreg  BP
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
        rstrxmm xmm6, 0
index e7c35f4..f0fe3d7 100644 (file)
@@ -871,9 +871,14 @@ SALSA20_VARS(DEFXGRAND)
 #include <stdio.h>
 #include <string.h>
 
+#include <mLib/macros.h>
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
+#ifdef ENABLE_ASM_DEBUG
+#  include "regdump.h"
+#endif
+
 static const int perm[] = {
    0, 13, 10,  7,
    4,  1, 14, 11,
@@ -900,7 +905,7 @@ static const int perm[] = {
     }                                                                  \
     for (i = 0; i < SALSA20_OUTSZ/4; i++) STORE32_L(d.buf + 4*i, b[i]);        \
                                                                        \
-    if (d.len != v[2].len || memcmp(d.buf, v[2].buf, v[2].len) != 0) { \
+    if (d.len != v[2].len || MEMCMP(d.buf, !=, v[2].buf, v[2].len)) {  \
       ok = 0;                                                          \
       printf("\nfail core:"                                            \
             "\n\titerations = %d"                                      \
@@ -970,7 +975,7 @@ SALSA20_VARS(DEFVCORE)
       }                                                                        \
       if (sz) BASE##_ENCRYPT(r, &ctx, p, q, sz);                       \
                                                                        \
-      if (d.len != v[5].len || memcmp(d.buf, v[5].buf, v[5].len) != 0) { \
+      if (d.len != v[5].len || MEMCMP(d.buf, !=, v[5].buf, v[5].len)) {        \
        ok = 0;                                                         \
        printf("\nfail encrypt:"                                        \
               "\n\tstep           = %lu"                               \
@@ -1016,6 +1021,9 @@ SALSA20_VARS(DEFXTAB)
 
 int main(int argc, char *argv[])
 {
+#ifdef ENABLE_ASM_DEBUG
+  regdump_init();
+#endif
   test_run(argc, argv, defs, SRCDIR"/t/salsa20");
   return (0);
 }
index a4a0a3f..070550d 100644 (file)
@@ -521,6 +521,7 @@ grand *seal_rand(const void *k, size_t sz, uint32 n)
 
 #include <string.h>
 
+#include <mLib/macros.h>
 #include <mLib/testrig.h>
 
 static int verify(dstr *v)
@@ -543,7 +544,7 @@ static int verify(dstr *v)
     seal_initctx(&c, &k, n);
     seal_encrypt(&c, 0, d.buf, i);
     seal_encrypt(&c, z.buf, d.buf + i, d.len - i);
-    if (memcmp(d.buf, v[2].buf, d.len) != 0) {
+    if (MEMCMP(d.buf, !=, v[2].buf, d.len)) {
       ok = 0;
       printf("*** seal failure\n");
       printf("*** k = "); type_hex.dump(&v[0], stdout); putchar('\n');
index 7f95ffc..190096f 100644 (file)
@@ -32,6 +32,7 @@
 #include <string.h>
 
 #include <mLib/bits.h>
+#include <mLib/macros.h>
 
 #include "serpent-sbox.h"
 
@@ -71,7 +72,7 @@ static int check(unsigned a, unsigned b, unsigned c, unsigned d,
     *q++ = (a & 1) | ((b & 1) << 1) | ((c & 1) << 2) | ((d & 1) << 3);
     a >>= 1; b >>= 1; c >>= 1; d >>= 1;
   }
-  return (memcmp(buf, p, sizeof(buf)));
+  return (MEMCMP(buf, ==, p, sizeof(buf)) ? 0 : -1);
 }
 
 #define CHECK(i) do {                                                  \
index 1696762..36bd430 100644 (file)
@@ -957,6 +957,7 @@ grand *kmac256_rand(const void *perso, size_t psz, const void *k, size_t sz)
 
 #include <stdio.h>
 
+#include <mLib/macros.h>
 #include <mLib/report.h>
 #include <mLib/testrig.h>
 
@@ -979,7 +980,7 @@ static int vrf_sha3_mct(void (*initfn)(sha3_ctx *),
     sha3_done(&ctx, d.buf);
   }
 
-  if (memcmp(d.buf, out->buf, out->len) != 0) {
+  if (MEMCMP(d.buf, !=, out->buf, out->len)) {
     ok = 0;
     printf("\nfail\n\tsteps = %d\n\tinput = ", n);
     type_hex.dump(in, stdout);
@@ -1037,7 +1038,7 @@ static int vrf_shaky(void (*initfn)(shake_ctx *,
       p += i; sz -= i;
     }
 
-    if (memcmp(d.buf, want->buf, want->len) != 0) {
+    if (MEMCMP(d.buf, !=, want->buf, want->len)) {
       ok = 0;
       printf("\nfail (get):\n\tstep = %i\n\tinput = ", *ip);
       type_hex.dump(m, stdout);
@@ -1071,7 +1072,7 @@ static int vrf_shaky(void (*initfn)(shake_ctx *,
       p += i; sz -= i;
     }
 
-    if (memcmp(d.buf, want->buf, want->len) != 0) {
+    if (MEMCMP(d.buf, !=, want->buf, want->len)) {
       ok = 0;
       printf("\nfail (mask):\n\tstep = %i\n\tinput = ", *ip);
       type_hex.dump(m, stdout);
@@ -1131,7 +1132,7 @@ static int vrf_kmac(void (*initfn)(kmac_ctx *, const void *, size_t,
   if (tsz) kmac_done(&ctx, d.buf, tsz);
   else { kmac_xof(&ctx); kmac_get(&ctx, d.buf, d.len); }
 
-  if (memcmp(d.buf, want->buf, want->len) != 0) {
+  if (MEMCMP(d.buf, !=, want->buf, want->len)) {
     ok = 0;
     printf("\nfail");
     printf("\n\tperso = `%s'", perso->buf);
index cc26f58..70d7e04 100644 (file)
@@ -38,7 +38,7 @@
 static octet s[256], si[256];
 static uint32 t[4][256], ti[4][256];
 static uint32 u[4][256];
-static octet rc[32];
+static octet rc[35];
 
 /*----- Main code ---------------------------------------------------------*/
 
@@ -333,7 +333,7 @@ const uint32 square_u[4][256] = {\n\
   fputs("\
 /* --- The round constants --- */\n\
 \n\
-const octet square_rcon[32] = {\n\
+const octet square_rcon[35] = {\n\
   ", stdout);
   for (i = 0; i < sizeof(rc); i++) {
     printf("0x%02x", rc[i]);
index 888d495..34d313d 100644 (file)
@@ -47,7 +47,7 @@ const octet square_keysz[] = { KSZ_RANGE, SQUARE_KEYSZ, 4, 16, 4 };
 extern const octet square_s[256], square_si[256];
 extern const uint32 square_t[4][256], square_ti[4][256];
 extern const uint32 square_u[4][256];
-extern const octet square_rcon[32];
+extern const octet square_rcon[35];
 
 #define S square_s
 #define SI square_si
diff --git a/symm/strobe.c b/symm/strobe.c
new file mode 100644 (file)
index 0000000..de4dbb2
--- /dev/null
@@ -0,0 +1,695 @@
+/* -*-c-*-
+ *
+ * The STROBE protocol framework
+ *
+ * (c) 2018 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 <assert.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <mLib/buf.h>
+
+#include "keccak1600.h"
+#include "strobe.h"
+
+/*----- Magic constants ---------------------------------------------------*/
+
+#define DDATA 0x04
+#define DRATE 0x80
+
+/*----- Utilities ---------------------------------------------------------*/
+
+/* --- @crank@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:    ---
+ *
+ * Use:                Cycle the Keccak-p[1600, n] duplex function.
+ */
+
+static void crank(strobe_ctx *ctx)
+{
+  kludge64 t[25];
+  octet *p;
+  unsigned i;
+
+  /* Ensure that we've not overstepped the rate bound. */
+  assert(ctx->n <= ctx->r - 2);
+
+  /* Apply the cSHAKE and rate padding. */
+  ctx->buf[ctx->n] ^= ctx->n0;
+  ctx->buf[ctx->n + 1] ^= DDATA;
+  ctx->buf[ctx->r - 1] ^= DRATE;
+
+  /* Cycle the sponge. */
+  for (i = 0, p = ctx->buf; i < ctx->r/8; i++)
+    { LOAD64_L_(t[i], p); p += 8; }
+  keccak1600_set(&ctx->k, t, ctx->r/8);
+  keccak1600_p(&ctx->k, &ctx->k, 24);
+  keccak1600_extract(&ctx->k, t, ctx->r/8);
+  for (i = 0, p = ctx->buf; i < ctx->r/8; i++)
+    { STORE64_L_(p, t[i]); p += 8; }
+
+  /* Restart at the beginning of the buffer, and note this as a
+   * continuation.
+   */
+  ctx->n = ctx->n0 = 0;
+}
+
+/* --- @xorbuf@ --- *
+ *
+ * Arguments:  @octet *z@ = pointer to output buffer
+ *             @const octet *x, *y@ = pointer to input buffers
+ *             @size_t sz@ = common buffer length
+ *
+ * Returns:    ---
+ *
+ * Use:                Store the bytewise XOR of the buffers @x@ and @y@ in @z@.
+ *             The @x@ and @y@ may be equal, but otherwise the buffers must
+ *             not overlap.
+ */
+
+static void xorbuf(octet *z, const octet *x, const octet *y, size_t sz)
+  { size_t i; for (i = 0; i < sz; i++) *z++ = *x++ ^ *y++; }
+
+/* --- @nonzerop@ --- *
+ *
+ * Arguments:  @const octet *x@ = pointer to input buffer
+ *             @size_t sz@ = buffer length
+ *
+ * Returns:    ---
+ *
+ * Use:                If any byte of @x@ is nonzero, then return a nonzero value
+ *             between 1 and 255 inclusive; otherwise return zero.
+ */
+
+static unsigned nonzerop(const octet *x, size_t sz)
+{
+  unsigned z = 0;
+  size_t i;
+
+  for (i = 0; i < sz; i++) z |= *x++;
+  return (z);
+}
+
+/* --- @unequalp@ --- *
+ *
+ * Arguments:  @const octet *x, *y@ = pointer to input buffers
+ *             @size_t sz@ = common buffer length
+ *
+ * Returns:    ---
+ *
+ * Use:                If any respective bytes of @x@ and @y@ are unequal, then
+ *             return a nonzero value between 1 and 255 inclusive; otherwise
+ *             return zero.
+ */
+
+static unsigned unequalp(const octet *x, const octet *y, size_t sz)
+{
+  unsigned z = 0;
+  size_t i;
+
+  for (i = 0; i < sz; i++) z |= *x++ ^ *y++;
+  return (z);
+}
+
+/* --- @process_buffer@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block
+ *             @const octet *p@ = pointer to input buffer
+ *             @octet *q@ = pointer to output buffer
+ *             @size_t sz@ = common buffer length
+ *
+ * Returns:    ---
+ *
+ * Use:                Process a portion of a STROBE input small enough to be
+ *             satisfied from the internal buffer.
+ */
+
+static void process_buffer(strobe_ctx *ctx,
+                          const octet *p, octet *q, size_t sz)
+{
+  octet *b = ctx->buf + ctx->n;
+  unsigned z = 0;
+
+  if (!(ctx->f&STRBF_CRYPTO)) {
+    /* No crypto to do.  The `output' would be equal to the input, so that's
+     * rather uninteresting (and, indeed, forbidden).  If there's input, then
+     * mix it into the state.
+     */
+
+    if (p && (ctx->f&STRBF_VRFOUT)) z |= nonzerop(p, sz);
+    if (p) xorbuf(b, b, p, sz);
+  } else if (!(ctx->f&STRBF_MIXOUT)) {
+    /* Mix the input into the sponge state.  That means that the new state
+     * will be equal to the output.
+     */
+
+    if (p) xorbuf(b, b, p, sz);
+    if (ctx->f&STRBF_VRFOUT) z |= nonzerop(b, sz);
+    if (q) memcpy(q, b, sz);
+  } else if (p) {
+    /* Mix the output into the sponge state, so the new state will in fact be
+     * equal to the input.  If the input and output buffers are equal then we
+     * have a dance to do.
+     */
+
+    if (!q) {
+      if (ctx->f&STRBF_VRFOUT) z |= unequalp(p, b, sz);
+      memcpy(b, p, sz);
+    } else {
+      xorbuf(q, p, b, sz);
+      if (q != p) memcpy(b, p, sz);
+      else xorbuf(b, b, q, sz);
+      if (ctx->f&STRBF_VRFOUT) z |= nonzerop(q, sz);
+    }
+  } else {
+    /* As above, only the input is hardwired to zero.  That means that we
+     * copy state bytes to the output (if any), and just clobber the state
+     * when we're done.
+     */
+
+    if (q) memcpy(q, b, sz);
+    memset(b, 0, sz);
+  }
+
+  /* Set the @STRBF_NZERO@ flag if @z@ is nonzero.  If @z@ is zero then
+   * subtracting one will set all of its bits, so, in particular, bits
+   * 8--15.  Otherwise, @z@ is between 1 and 255, so bits 8--15 are clear and
+   * will remain so when we subtract one.
+   */
+  if (ctx->f&STRBF_VRFOUT) ctx->f |= ((z - 1)&STRBF_NZERO) ^ STRBF_NZERO;
+
+  /* Update the buffer cursor. */
+  ctx->n += sz;
+}
+
+/*----- Interface ---------------------------------------------------------*/
+
+/* --- @strobe_init@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block to initialize
+ *             @unsigned lambda@ = security parameter, in bits (must be a
+ *                     multiple of 32)
+ *
+ * Returns:    ---
+ *
+ * Use:                Initialize a STROBE context for use.
+ */
+
+void strobe_init(strobe_ctx *ctx, unsigned lambda)
+{
+  const char v[] = "STROBEv1.0.2";
+  kludge64 t[25];
+  octet *p;
+  buf b;
+  unsigned n, i;
+
+  /* Check the security parameter. */
+  assert(lambda%32 == 0); assert(lambda <= 704);
+  ctx->r = (1600 - 2*lambda)/8;
+
+  /* Set up the initial cSHAKE framing. */
+  buf_init(&b, ctx->buf, ctx->r);
+  buf_putu8(&b, 1); buf_putu8(&b, ctx->r);
+  buf_putu8(&b, 1); buf_putu8(&b, 0);
+  buf_putu8(&b, 1); buf_putu8(&b, 8*(sizeof(v) - 1));
+  buf_put(&b, v, sizeof(v) - 1);
+  assert(BOK(&b));
+  n = BLEN(&b); if (n%8) memset(ctx->buf + n, 0, 8 - n%8);
+
+  /* Cycle the sponge once initially, and get the first output buffer. */
+  keccak1600_init(&ctx->k);
+  for (i = 0, p = ctx->buf; i < (n + 7)/8; i++)
+    { LOAD64_L_(t[i], p); p += 8; }
+  keccak1600_set(&ctx->k, t, (n + 7)/8);
+  keccak1600_p(&ctx->k, &ctx->k, 24);
+  keccak1600_extract(&ctx->k, t, ctx->r/8);
+  for (i = 0, p = ctx->buf; i < ctx->r/8; i++)
+    { STORE64_L_(p, t[i]); p += 8; }
+
+  /* Initialize the other parts of the state. */
+  ctx->n = ctx->n0 = 0; ctx->f = 0;
+}
+
+/* --- @strobe_begin@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block
+ *             @unsigned op@ = bitmask of flags
+ *
+ * Returns:    ---
+ *
+ * Use:                Begin a STROBE operation.  The flags determine the behaviour
+ *             of the @strobe_process@ and @strobe_done@ functions.
+ *
+ *               * The @I@ bit determines the primary direction of data
+ *                 movement.  If it's clear, data comes from the application
+ *                 into STROBE.  If it's set, data comes from STROBE towards
+ *                 the application.
+ *
+ *               * The @C@ bit activates cryptographic processing.  If it's
+ *                 clear, then the input and output data would be equal, so
+ *                 @dest@ must be null.  If it's set, then input data is
+ *                 XORed with the keystream on its way to the output.
+ *
+ *               * The @A@ bit determines whether the application is
+ *                 engaged.  If it's set, then the input or output buffer
+ *                 (according to whether @I@ is clear or set, respectively)
+ *                 holds the application data.  If it's clear, and @I@ is
+ *                 clear, then zero bytes are fed in; if @I@ is set, then
+ *                 the output is compared with zero, and @strobe_done@
+ *                 reports the outcome of this comparison.
+ *
+ *               * The @T@ bit determines whether the transport is engaged.
+ *                 If it's set, then the input or output buffer (according
+ *                 to whether @I@ is set or clear, respectively) holds
+ *                 transport data.  If it's clear, and @I@ is set, then zero
+ *                 bytes are fed in; if @I@ is clear, then the output is
+ *                 discarded.
+ *
+ *               * The @M@ bit marks the data as metadata, but has no other
+ *                 effect.
+ */
+
+void strobe_begin(strobe_ctx *ctx, unsigned op)
+{
+  /* Preliminary checking.  We shouldn't have an operation underway, and the
+   * operation shouldn't have reserved bits set.
+   */
+  assert(!(ctx->f&STRBF_ACTIVE)); assert(!(op&~STRBF_VALIDMASK));
+
+  /* Reset our operation state. */
+  ctx->f &= STRBF_STMASK;
+
+  /* Operation framing.  Chain back to the start of the previous frame and
+   * write the new operation code.  Set the sticky asymmetry bit here if
+   * necessary.
+   */
+  ctx->buf[ctx->n++] ^= ctx->n0; ctx->n0 = ctx->n;
+  if (ctx->n >= ctx->r - 2) crank(ctx);
+  if (!(op&STRBF_T))
+    ctx->buf[ctx->n++] ^= U8(op);
+  else {
+    if (!(ctx->f&STRBF_INIT)) ctx->f |= STRBF_INIT | (op&STRBF_I);
+    ctx->buf[ctx->n++] ^= U8(op ^ ctx->f);
+  }
+  if (ctx->n >= ctx->r - 2 || (op&STRBF_C)) crank(ctx);
+
+  /* The operation is now underway. */
+  ctx->f |= STRBF_ACTIVE;
+
+  /* Determine whether we expect input and/or output. */
+  if (op&(op&STRBF_I ? STRBF_T : STRBF_A))
+    ctx->f |= STRBF_WANTIN;
+  if ((op&STRBF_C) && op&(op&STRBF_I ? STRBF_A : STRBF_T))
+    ctx->f |= STRBF_WANTOUT;
+
+  /* Determine whether the keystream is engaged, and how it fits in. */
+  if (op&STRBF_C) {
+    ctx->f |= STRBF_CRYPTO;
+    if ((op&(STRBF_I | STRBF_T)) != STRBF_T) ctx->f |= STRBF_MIXOUT;
+  }
+
+  /* Determine whether the output is supposed to be all-bytes-zero. */
+  if ((op&(STRBF_I | STRBF_A | STRBF_T)) == (STRBF_I | STRBF_T))
+    ctx->f |= STRBF_VRFOUT;
+
+  /* The operation is now underway. */
+  ctx->f |= STRBF_ACTIVE;
+}
+
+/* --- @strobe_process@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block
+ *             @const void *src@ = pointer to input data, or null
+ *             @void *dest@ = pointer to output data, or null
+ *             @size_t sz@ = common buffer length
+ *
+ * Returns:    ---
+ *
+ * Use:                Process data through the active STROBE operation.  The exact
+ *             behaviour depends on the flags passed to @strobe_begin@; see
+ *             that function for details.  If @src@ is null, then the
+ *             behaviour is as if the input consists of @sz@ zero bytes.  If
+ *             @dest@ in null, then the output is discarded.
+ */
+
+void strobe_process(strobe_ctx *ctx, const void *src, void *dest, size_t sz)
+{
+  const octet *p = src; octet *q = dest;
+  unsigned spare;
+
+  /* Make sure that things are set up properly. */
+  assert(ctx->f&STRBF_ACTIVE);
+  if (!(ctx->f&STRBF_WANTIN)) assert(!src);
+  if (!(ctx->f&STRBF_WANTOUT)) assert(!dest);
+
+  /* Work through the input. */
+  spare = ctx->r - ctx->n - 2;
+  if (sz < spare)
+    { process_buffer(ctx, p, q, sz); return; }
+  if (ctx->n) {
+    process_buffer(ctx, p, q, spare); crank(ctx);
+    if (p) { p += spare; }
+    if (q) { q += spare; }
+    sz -= spare;
+  }
+
+  while (sz >= ctx->r - 2) {
+    process_buffer(ctx, p, q, ctx->r - 2); crank(ctx);
+    if (p) { p += ctx->r - 2; }
+    if (q) { q += ctx->r - 2; }
+    sz -= ctx->r - 2;
+  }
+  if (sz) process_buffer(ctx, p, q, sz);
+}
+
+/* --- @strobe_done@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block
+ *
+ * Returns:    Zero on success; @-1@ on verification failure (if @I@ and @T@
+ *                     are set and @A@ is clear)
+ *
+ * Use:                Concludes a STROBE operation, returning the result.
+ */
+
+int strobe_done(strobe_ctx *ctx)
+{
+  assert(ctx->f&STRBF_ACTIVE); ctx->f &= ~STRBF_ACTIVE;
+  if (ctx->f&STRBF_VRFOUT) return (-(int)((ctx->f/STRBF_NZERO)&1u));
+  else return (0);
+}
+
+/* --- @strobe_key@, @strobe_ad@, @strobe_@prf@, @strobe_clrout@,
+ *     @strobe_clrin@, @strobe_encout@, @strobe_encin@, @strobe_macout@,
+ *     @strobe_macin@, @strobe_ratchet@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block
+ *
+ * Returns:    @strobe_macin@ returns zero on success, or @-1@ on
+ *                     verification failure
+ *
+ * Use:                Perform a STROBE operation on a single buffer.
+ */
+
+static int op(strobe_ctx *ctx, unsigned f0, unsigned f1,
+             const void *src, void *dest, size_t sz)
+{
+  assert(!(f1&~STRBF_M));
+
+  strobe_begin(ctx, f0 | f1);
+  strobe_process(ctx, src, dest, sz);
+  return (strobe_done(ctx));
+}
+
+void strobe_key(strobe_ctx *ctx, unsigned f, const void *k, size_t sz)
+  { op(ctx, STROBE_KEY, f, k, 0, sz); }
+
+void strobe_ad(strobe_ctx *ctx, unsigned f, const void *h, size_t sz)
+  { op(ctx, STROBE_AD, f, h, 0, sz); }
+
+void strobe_prf(strobe_ctx *ctx, unsigned f, void *t, size_t sz)
+  { op(ctx, STROBE_PRF, f, 0, t, sz); }
+
+void strobe_clrout(strobe_ctx *ctx, unsigned f, const void *m, size_t sz)
+  { op(ctx, STROBE_CLROUT, f, m, 0, sz); }
+
+void strobe_clrin(strobe_ctx *ctx, unsigned f, const void *m, size_t sz)
+  { op(ctx, STROBE_CLRIN, f, m, 0, sz); }
+
+void strobe_encout(strobe_ctx *ctx, unsigned f,
+                  const void *m, void *c, size_t sz)
+  { op(ctx, STROBE_ENCOUT, f, m, c, sz); }
+
+void strobe_encin(strobe_ctx *ctx, unsigned f,
+                 const void *c, void *m, size_t sz)
+  { op(ctx, STROBE_ENCIN, f, c, m, sz); }
+
+void strobe_macout(strobe_ctx *ctx, unsigned f, void *t, size_t sz)
+  { op(ctx, STROBE_MACOUT, f, 0, t, sz); }
+
+int strobe_macin(strobe_ctx *ctx, unsigned f, const void *t, size_t sz)
+  { return (op(ctx, STROBE_MACIN, f, t, 0, sz)); }
+
+void strobe_ratchet(strobe_ctx *ctx, unsigned f, size_t sz)
+  { op(ctx, STROBE_RATCHET, f, 0, 0, sz); }
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/hex.h>
+#include <mLib/macros.h>
+#include <mLib/testrig.h>
+
+#define NSTATE 16
+
+static strobe_ctx states[NSTATE];
+
+static void dump(int rc, char win, const void *p, size_t sz)
+{
+  dstr d = DSTR_INIT;
+  const char *q = p;
+  size_t i;
+  codec *hex;
+  int printable;
+
+  if (!p) {
+    if (!rc) putchar(win);
+    else putchar('-');
+  } else {
+    for (i = 0, printable = 1; i < sz; i++)
+      if (!ISPRINT(q[i])) { printable = 0; break; }
+    if (printable)
+      printf("`%s'", q);
+    else {
+      hex = hex_class.encoder(CDCF_LOWERC, 0, 0);
+      hex->ops->code(hex, p, sz, &d);
+      dstr_write(&d, stdout);
+      hex->ops->destroy(hex);
+    }
+  }
+  dstr_destroy(&d);
+  putchar('\n');
+}
+
+typedef int opfunc(strobe_ctx *, unsigned, const void *, void *, size_t);
+
+static int op_init(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_init(ctx, sz); return (0); }
+
+static int op_copy(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { *ctx = states[sz]; return (0); }
+
+static int op_begin(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_begin(ctx, f); return (0); }
+
+static int op_process(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_process(ctx, p, q, sz); return (0); }
+
+static int op_done(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { return (strobe_done(ctx)); }
+
+static int op_key(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_key(ctx, f, p, sz); return (0); }
+
+static int op_ad(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_ad(ctx, f, p, sz); return (0); }
+
+static int op_prf(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_prf(ctx, f, q, sz); return (0); }
+
+static int op_clrout(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_clrout(ctx, f, p, sz); return (0); }
+
+static int op_clrin(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_clrin(ctx, f, p, sz); return (0); }
+
+static int op_encout(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_encout(ctx, f, p, q, sz); return (0); }
+
+static int op_encin(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_encin(ctx, f, p, q, sz); return (0); }
+
+static int op_macout(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { strobe_macout(ctx, f, q, sz); return (0); }
+
+static int op_macin(strobe_ctx *ctx, unsigned f,
+                  const void *p, void *q, size_t sz)
+  { return (strobe_macin(ctx, f, p, sz)); }
+
+static int op_ratchet(strobe_ctx *ctx, unsigned f,
+                     const void *p, void *q, size_t sz)
+  { strobe_ratchet(ctx, f, sz); return (0); }
+
+static const struct optab {
+  const char *name;
+  opfunc *op;
+} optab[] = {
+#define OP(op) { #op, op_##op }
+  OP(init), OP(copy),
+  OP(begin), OP(process), OP(done),
+  OP(key), OP(ad), OP(prf),
+  OP(clrout), OP(clrin),
+  OP(encout), OP(encin),
+  OP(macout), OP(macin),
+  OP(ratchet),
+  { 0 }
+#undef OP
+};
+
+static int verify(dstr v[])
+{
+  int r;
+  strobe_ctx *ctx;
+  const char *p;
+  char *q;
+  const struct optab *op;
+  dstr d0 = DSTR_INIT, d1 = DSTR_INIT;
+  codec *hex;
+  unsigned f;
+  const void *src, *destref;
+  void *dest;
+  size_t sz;
+  int rc, rcref;
+  int ok;
+
+  /* First, get the register number. */
+  r = *(int *)v[0].buf; ctx = &states[r];
+
+  /* Next job is to parse the command and flags. */
+  q = v[1].buf; p = q; q += strcspn(q, "/"); if (*q) *q++ = 0;
+  for (op = optab; op->name; op++)
+    if (STRCMP(op->name, ==, p)) goto found_op;
+  abort();
+found_op:
+
+  f = 0;
+  for (p = q; *p; p++) {
+    switch (*p) {
+      case 'I': f |= STRBF_I; break;
+      case 'C': f |= STRBF_C; break;
+      case 'A': f |= STRBF_A; break;
+      case 'T': f |= STRBF_T; break;
+      case 'M': f |= STRBF_M; break;
+      default: abort();
+    }
+  }
+
+  /* Convert the source parameter. */
+  p = v[2].buf;
+  if (*p == '*')
+    { src = 0; sz = strtoul(p + 1, 0, 0); }
+  else if (*p == '=')
+    { src = p + 1; sz = v[2].len - 1; }
+  else if (*p == '!') {
+    hex = hex_class.decoder(CDCF_IGNCASE);
+    rc = hex->ops->code(hex, p + 1, v[2].len - 1, &d0); assert(!rc);
+    src = d0.buf; sz = d0.len;
+    hex->ops->destroy(hex);
+  } else
+    abort();
+
+  /* Convert the destination parameter. */
+  p = v[3].buf;
+  if (*p == '+')
+    { destref = 0; rcref = 0; assert(v[3].len == 1); }
+  else if (*p == '-')
+    { destref = 0; rcref = -1; assert(v[3].len == 1); }
+  else if (*p == '=')
+    { destref = p + 1; assert(sz == v[3].len - 1); rcref = 0; }
+  else if (*p == '!') {
+    hex = hex_class.decoder(CDCF_IGNCASE);
+    rc = hex->ops->code(hex, p + 1, v[3].len - 1, &d1); assert(!rc);
+    destref = d1.buf; assert(sz == d1.len);
+    hex->ops->destroy(hex);
+    rcref = 0;
+  } else
+    abort();
+  if (!destref) dest = 0;
+  else dest = xmalloc(sz);
+
+  /* Do the operation. */
+  rc = op->op(ctx, f, src, dest, sz);
+
+  /* Check we got the right answer. */
+  ok = (rc == rcref && (!destref || MEMCMP(dest, ==, destref, sz)));
+  if (!ok) {
+    printf("failed test\n");
+    printf("       state = %d\n", r);
+    printf("   operation = %s%s%s%s%s%s%s\n",
+          op->name,
+          f ? "/" : "",
+          f&STRBF_I ? "I" : "",
+          f&STRBF_A ? "A" : "",
+          f&STRBF_C ? "C" : "",
+          f&STRBF_T ? "T" : "",
+          f&STRBF_M ? "M" : "");
+    printf("       input = "); dump(0, '*', src, sz);
+    printf("    computed = "); dump(rc, '+', dest, sz);
+    printf("    expected = "); dump(rcref, '+', destref, sz);
+  }
+
+  dstr_destroy(&d0);
+  dstr_destroy(&d1);
+  free(dest);
+  return (ok);
+}
+
+static test_chunk tests[] = {
+  { "strobe", verify,
+    { &type_int, &type_string, &type_string, &type_string, 0 } },
+  { 0, 0, { 0 } }
+};
+
+int main(int argc, char *argv[])
+{
+  test_run(argc, argv, tests, SRCDIR "/t/strobe");
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/symm/strobe.h b/symm/strobe.h
new file mode 100644 (file)
index 0000000..c53fc39
--- /dev/null
@@ -0,0 +1,233 @@
+/* -*-c-*-
+ *
+ * The STROBE protocol framework
+ *
+ * (c) 2018 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.
+ */
+
+/*----- Notes on the STROBE framework -------------------------------------*
+ *
+ * Mike Hamburg's STROBE framework is an attempt to do pretty much all the
+ * obvious parts of symmetric cryptography using a single primitive: the
+ * Keccak-p[1600, 24] sponge function which underlies the SHA3.  This works
+ * excellently on tiny devices, because it's possible to implement
+ * Keccak-p[1600, 24] with a very small amount of code, and STROBE requires
+ * minimal additional state beyond the 200-byte Keccak-p[1600, n] state,
+ * which can be updated more-or-less in place.
+ *
+ * STROBE is stateful.  The idea is that the two parties' STROBE states
+ * should evolve in lockstep with each other.  This will obviously go wrong
+ * if messages cross over, or are reordered or duplicated.
+ *
+ * The model for a single STROBE-based endpoint consists of the application,
+ * and a transport for communicating with a peer.  All data gets mixed into
+ * the STROBE state.  Some operations mix input data with a keystream
+ * generated by the sponge function (used in duplexing mode), which can then
+ * be used to encrypt and authenticate messages.
+ */
+
+#ifndef CATACOMB_STROBE_H
+#define CATACOMB_STROBE_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+#ifndef CATACOMB_KECCAK1600_H
+#  include "keccak1600.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+#define STRBF_I 0x01                   /* inbound */
+#define STRBF_A 0x02                   /* application */
+#define STRBF_C 0x04                   /* cipher */
+#define STRBF_T 0x08                   /* transport */
+#define STRBF_M 0x10                   /* metadata */
+#define STRBF_K 0x20                   /* key-tree (not implemented) */
+#define STRBF_VALIDMASK 0x1f           /* valid bits */
+#define STRBF_OPMASK 0xff              /* observable operation bits */
+
+typedef struct strobe_ctx {
+  keccak1600_state k;
+  octet buf[200];
+  unsigned f, r, n0, n;
+#define STRBF_I0 0x0001                        /* initiator/responder role */
+#define STRBF_INIT 0x0100              /* initiator/responder role set? */
+#define STRBF_STMASK 0x01ff            /* mask for persistent state bits */
+#define STRBF_ACTIVE 0x0200            /* operation in progress */
+#define STRBF_WANTIN 0x0400            /* expect input buffer */
+#define STRBF_WANTOUT 0x0800           /* expect output buffer */
+#define STRBF_VRFOUT 0x1000            /* verify that output is nonzero */
+#define STRBF_NZERO 0x2000             /* nonzero output from update */
+#define STRBF_MIXOUT 0x4000            /* mix output into state */
+#define STRBF_CRYPTO 0x8000            /* mix sponge state into output */
+} strobe_ctx;
+
+#define STROBE_KEY               (STRBF_A | STRBF_C)
+#define STROBE_AD                (STRBF_A)
+#define STROBE_PRF     (STRBF_I | STRBF_A | STRBF_C)
+#define STROBE_CLROUT            (STRBF_A |           STRBF_T)
+#define STROBE_CLRIN   (STRBF_I | STRBF_A |           STRBF_T)
+#define STROBE_ENCOUT            (STRBF_A | STRBF_C | STRBF_T)
+#define STROBE_ENCIN   (STRBF_I | STRBF_A | STRBF_C | STRBF_T)
+#define STROBE_MACOUT                      (STRBF_C | STRBF_T)
+#define STROBE_MACIN   (STRBF_I |           STRBF_C | STRBF_T)
+#define STROBE_RATCHET                     (STRBF_C)
+
+#define STROBE_ROLEMASK        (STRBF_I0 | STRBF_INIT)
+#define STRBRL_UNDCD   (0)                     /* undecided */
+#define STRBRL_INIT               (STRBF_INIT) /* initiator */
+#define STRBRL_RESP    (STRBF_I0 | STRBF_INIT) /* responder */
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @strobe_init@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block to initialize
+ *             @unsigned lambda@ = security parameter, in bits (must be a
+ *                     multiple of 32)
+ *
+ * Returns:    ---
+ *
+ * Use:                Initialize a STROBE context for use.
+ */
+
+extern void strobe_init(strobe_ctx */*ctx*/, unsigned /*lambda*/);
+
+/* --- @strobe_begin@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block
+ *             @unsigned op@ = bitmask of flags
+ *
+ * Returns:    ---
+ *
+ * Use:                Begin a STROBE operation.  The flags determine the behaviour
+ *             of the @strobe_process@ and @strobe_done@ functions.
+ *
+ *               * The @I@ bit determines the primary direction of data
+ *                 movement.  If it's clear, data comes from the application
+ *                 into STROBE.  If it's set, data comes from STROBE towards
+ *                 the application.
+ *
+ *               * The @C@ bit activates cryptographic processing.  If it's
+ *                 clear, then the input and output data would be equal, so
+ *                 @dest@ must be null.  If it's set, then input data is
+ *                 XORed with the keystream on its way to the output.
+ *
+ *               * The @A@ bit determines whether the application is
+ *                 engaged.  If it's set, then the input or output buffer
+ *                 (according to whether @I@ is clear or set, respectively)
+ *                 holds the application data.  If it's clear, and @I@ is
+ *                 clear, then zero bytes are fed in; if @I@ is set, then
+ *                 the output is compared with zero, and @strobe_done@
+ *                 reports the outcome of this comparison.
+ *
+ *               * The @T@ bit determines whether the transport is engaged.
+ *                 If it's set, then the input or output buffer (according
+ *                 to whether @I@ is set or clear, respectively) holds
+ *                 transport data.  If it's clear, and @I@ is set, then zero
+ *                 bytes are fed in; if @I@ is clear, then the output is
+ *                 discarded.
+ *
+ *               * The @M@ bit marks the data as metadata, but has no other
+ *                 effect.
+ */
+
+extern void strobe_begin(strobe_ctx */*ctx*/, unsigned /*op*/);
+
+/* --- @strobe_process@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block
+ *             @const void *src@ = pointer to input data, or null
+ *             @void *dest@ = pointer to output data, or null
+ *             @size_t sz@ = common buffer length
+ *
+ * Returns:    ---
+ *
+ * Use:                Process data through the active STROBE operation.  The exact
+ *             behaviour depends on the flags passed to @strobe_begin@; see
+ *             that function for details.  If @src@ is null, then the
+ *             behaviour is as if the input consists of @sz@ zero bytes.  If
+ *             @dest@ in null, then the output is discarded.
+ */
+
+extern void strobe_process(strobe_ctx */*ctx*/, const void */*src*/,
+                          void */*dest*/, size_t /*sz*/);
+
+/* --- @strobe_done@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block
+ *
+ * Returns:    Zero on success; @-1@ on verification failure (if @I@ and @T@
+ *                     are set and @A@ is clear)
+ *
+ * Use:                Concludes a STROBE operation, returning the result.
+ */
+
+extern int strobe_done(strobe_ctx */*ctx*/);
+
+/* --- @strobe_key@, @strobe_ad@, @strobe_@prf@, @strobe_clrout@,
+ *     @strobe_clrin@, @strobe_encout@, @strobe_encin@, @strobe_macout@,
+ *     @strobe_macin@, @strobe_ratchet@ --- *
+ *
+ * Arguments:  @strobe_ctx *ctx@ = pointer to context block
+ *
+ * Returns:    @strobe_macin@ returns zero on success, or @-1@ on
+ *                     verification failure
+ *
+ * Use:                Perform a STROBE operation on a single buffer.
+ */
+
+extern void strobe_key(strobe_ctx */*ctx*/, unsigned /*f*/,
+                      const void */*k*/, size_t /*sz*/);
+extern void strobe_ad(strobe_ctx */*ctx*/, unsigned /*f*/,
+                     const void */*h*/, size_t /*sz*/);
+extern void strobe_prf(strobe_ctx */*ctx*/, unsigned /*f*/,
+                      void */*t*/, size_t /*sz*/);
+extern void strobe_clrout(strobe_ctx */*ctx*/, unsigned /*f*/,
+                         const void */*m*/, size_t /*sz*/);
+extern void strobe_clrin(strobe_ctx */*ctx*/, unsigned /*f*/,
+                        const void */*m*/, size_t /*sz*/);
+extern void strobe_encout(strobe_ctx */*ctx*/, unsigned /*f*/,
+                         const void */*m*/, void */*c*/, size_t /*sz*/);
+extern void strobe_encin(strobe_ctx */*ctx*/, unsigned /*f*/,
+                        const void */*c*/, void */*m*/, size_t /*sz*/);
+extern void strobe_macout(strobe_ctx */*ctx*/, unsigned /*f*/,
+                         void */*t*/, size_t /*sz*/);
+extern int strobe_macin(strobe_ctx */*ctx*/, unsigned /*f*/,
+                       const void */*t*/, size_t /*sz*/);
+extern void strobe_ratchet(strobe_ctx */*ctx*/, unsigned /*f*/,
+                          size_t /*sz*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
index b03a0a8..4d23ac6 100644 (file)
@@ -1,5 +1,7 @@
+### Poly1305 tests.
+
 poly1305-hash {
-  ## The tests from Danial J. Bernstein, `The Poly1305-AES message-
+  ## The tests from Daniel J. Bernstein, `The Poly1305-AES message-
   ## authentication code', 2005-03-29, Appendix B,
   ## https://cr.yp.to/mac/poly1305-20050329.pdf
   851fc40c3467ac0be05cc20404f3f700 580b3b0f9447bb1e69d095b5928b6dbc
@@ -90,23 +92,21 @@ poly1305-mct {
   00000000000000000000000000000000
     00000000000000000000000000000000
     00000000000000000000000000000000
-    1 596382b2c34704b87e291250fcb927fd;
-  00000000000000000000000000000000
-    00000000000000000000000000000000
-    00000000000000000000000000000000
-    10 7f1d971da577bdd6fb24437aaac845f5;
-  00000000000000000000000000000000
-    00000000000000000000000000000000
-    00000000000000000000000000000000
-    100 e1cb88ba2c498ade2091ab06cefa24fd;
-  00000000000000000000000000000000
-    00000000000000000000000000000000
-    00000000000000000000000000000000
-    1000 f7064b7217e8a6b74b381c58175d9ff2;
-
-  ## The full test.  This takes aaaaages.
-  ##00000000000000000000000000000000
-  ##  00000000000000000000000000000000
-  ##  00000000000000000000000000000000
-  ##  1000000 df62013a9d388ea6e82cb7295fa706ec;
+    00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+    0 1 596382b2c34704b87e291250fcb927fd;
+  87d0ce793cb4cb966c8dd6ad0e6c6799
+    18bf6ee80c6d2d8d11ffe4f5b7214cf2
+    2a3dbb3acef3b4172e5f7045a33bd038
+    66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055defc3e0677d956b4c62664bac15962ab15d93ccbbc03aafdbde779162ed93b55361f0f8acaa41d50ef5175927fe79ea316186516eef15001cd04d3524a55e4fa3c5ca479d3aaa8a897c21807f721b6270ffc68b6889d81a116799f6aaa35d8e04c7a7dd5e6da2519e8759f54e906696f5772fee093283bcef7b930aed50323bcbc8c820c67422c1e16bdc022a9c0277c9d95fef0ea4ee11e2b27276da811523c5acb80154989f8a67ee9e3fa30b73b0c1c34bf46e3464d977cd7fcd0ac3b82721080bb0d9b982ee2c77feee983d7ba35da88ce86955002940652ab63bc56fb16f994da2b01d74356509d7d1b6d7956b0e5a557757bd1ced2eef8650bc5b6d426108c1518abcbd0befb6a0d5fd57a3e2dbf31458eab63df66613653d4beae73f5c40eb438fbcfdcf4a4ba46320184b9ca0da4dfae77de7ccc910356caea3243f33a3c81b064b3b7cedc7435c223f664227215715980e6e0bb570d459ba80d7512dbe458c8f0f3f52d659b6e8eef19ee71aea2ced85c7a42ffca6522a62db49a2a46eff72bd7f7e0883acd087183f0627f3537a4d558754ed63358e8182bee196735b361dc9bd64d5e34e1074a855655d2974cc6fa1653754cf40f561d8c7dc526aab2908ec2d2b977cde1a1fb1071e32f40e049ea20f30368ba1592b4fe57fb51595d23acbdace324cdd78060a17187c662368854e915402d9b52fb21e984663e41c26a109437e162cfaf071b53f77e50000a5388ff183b82ce7a1af476c416d7d204157b3633b2f4ec077b699b032816997e37bceded8d4a04976fd7d0c0b029f290794c3be504c5242287ea2f831f11ed5690d92775cd6e863d7731fd4da687ebfb13df4c41dc0fb8aec3d45ef6076ec131cdd55e724bae2931260484b73b12fdf2546d852cf089b64f622809c6ff963010d8aebc14e0cf4d5d06fc92e8e022a2a97dd822329d7bf26baf95f2f25fd2274cb4e10a596b570a97176273053be918c9a0a12ab3f61d83229e403e512b9f1fd89824ec9942ee782ee6972faf4cb5898e4a123c5e9e3f3fb71397d435b4de0f83627475ee6170f55646fc8aa52375fe4aae3711138b550c5f5654106b4ba7bc268ae7f63cf8a2401b2ba6c0894177e3f6696f9086760c3ce4e16844d1780c10b7785dcb3641fb488f077eacb89b8cf5b627a4449224923378dab62b90ef1c2dc7f1f7a196304e1805ee1a13f8cf12aed5526b82f62b5759ff12a573f68b8ccbe2d01d2e5615802edc3ef0a7778a1c565d9a836d770d1f5812e5698af213e398e0357834fffcde35d1cb77c821d21377401341f898475a4df74c58
+    1 10 7f1d971da577bdd6fb24437aaac845f5;
+  9eff9b802b9d43922975aea18a62834a
+    1f22e8534c98f80321384c88d00972d7
+    2b2f5dfbc2aeaead12f8dfaad90d4ec9
+    b84d332e02eda38a6cf2763928ba014bc632a0f2c0266f9093edc731eb2ce1e61c9a10bcb4bbd2b4b69e3f84da37dcc011142fbf8222c68a31f9bc227ead19f629ff4d6d414e95d3e9a3b2998d8e6db6379112f2d1e5edb32294729ad7d1c087b87d1c26a7e3b491f9d4452a2da50df87027d0f52c75e281091e86c86cc15091b0a6459a94e74597f86cd4801b4413f6185a7dd87f5d6bd3dfc8717afabd833c08160428a0fd0c2f9934921e9a7e40d9bf8a4d61781985aa6691cec4f466ab0bd8fe4cce660b31aeedec1cdc8ae76d3b9a01e5b5ca1e5d6932096bef253f427da7e974eab8aca5c0f22c9f0b5f1598e857f28c4a0b313890c718130e6bd18a150e5aa18e2f19bec1cb88adaeade2122ee13193287bbab77b2a334ffd9ba26210d170b6e7c7edd2fb6808d03acac8fe38b87a7523926e04028bc8df5753ced4b1bdf0b8a8d2315d210433417509dc685c660bd97678ef2d470c5845497d7d726e5ffbcc6e4db02d5d1a6a8b4629580ca3fae7392b33a992e73afd9e07c1cdc3576aedf6d2330f8dcf5d148873799e76cd4b8dda4eba698178d5edd6b260ef84af4517fbb68810571cfa7df52e241d8c43d3e035e23684e386c5d57cd0c4755614a6b8fb3529ee54ae2b2393dba3638795ec5c30c84dd3651ad75f96a4675df0b2d7e67329c5eefd62c2b4c378e2212ffcd788690201ba9eadb62395e8b3ca8bdacddc3a96f36456d0f95edb90dd975dd685348ada9658dc0d452cc99288460e12749a433770560281e6cc60a0cc654c664e53ebfa5bbc71c67993418c32ce1eb2643f64e9357b1ecca7805de5b75a3b8b918d719a229662fa8f0100bf6d717823b41b72fb9e91edcc75eaaabb066f7a714bcb0dfecf8d6944295cd40e3e53b041ac373e4dfe54336c9ce2417f297b64b1a5f45223a3b135c2faa6dda33a8128a545fca0dafeec4c1e6d187b336712802abdc8f658e5b3cbfc8961c0324e138cfafe635e3d47537890ccb5557d71e77419da3f7f513d22c6300cd51397c2e9f07d4584cd54081ec5339a43d64d6607159b9227f32e112b48e6644c7543be64d2015bddb8e50093143761a48cacb3376f16a34b86f82f066c9087d74663082549784b44444ee744cf5b62a5efc25aea49238df87dbecb07d9977901b70db24f8d851cb78b50d814be55d91c0775b5b7acb6a5651b260427057f6c86cd9fa709205f1d40431cfef5e3f90bc124637bcf35cdf343e3f52ce4236db25ac29aba0a8c69a9b0f1ae180f5be6e9c2a79fa522078604855624d5ec85a113738ee0a81f2d6ca9be1caa26ba628b0b1429c06b371f5e2801fec0599588417c02d21b6412ba01b904cae803c61e83ddf17e083a9b42564255c49ca91c2f079f8fc9439fe93ae322de7480df086018
+    10 100 e1cb88ba2c498ade2091ab06cefa24fd;
+  0c4ac91a4cbcd2ddec777ed12feaee3b
+    0411d5def6fe88aff66611870f7b83d8
+    5ba282f23cef22af42c21616603b8f25
+    1d630211fdf32fb5c4432d71b34bfd6daa6aac7ef09bd1656e7b710a70166f0e926d61116e29486f407724d581acf140d70b1352195971ed43c0857c2fe1018cb570317da3f5eae2b83e795e3375106bad876f2fd2cec8b972681cd3b0f4f75442694294f3659b0d452c8e39728e73bf402783d9c9ba3bc913473c747f212aa29d5303614869abce329d8c872a0da70f7d3e0d48a99f076117e32f313584d8531de33d8f99fc5379cc4f0dc2fb785bbdc372868d7598d7c2773ec7c6717f45c45a0d1db3f9fe342c809c407b56af8bc5896ecbd27c7c2517884018142a37cc50ad0a754dc5f2fbbcfdc45a8a0f4815c1ab75ea15fb1585e475c6ecd452a75fe4fad2719e5915b044833b67174cd6eaf4106d6e3512c9986e34442da5d46700c68b391f6dd0beb025f0ec3b719cfb643313223dc859c824025dddfb3cef7084c5ae0b139930c51f8f925d4c5cf7e9c8b6af2e43eed9fb2bbead8999e9d629d1e346244f5ef6034a018517954508ea83a1f32a682b927b5814a67bf581980d620e6f2102344121c74f774721d2e24a7fe426fd216ffcd1d7a3abeec214e21e6db6d17b5237ece8ad09e19643e0cbdc1a25e72d1e58c85810900e965e2f42ee1a5d947ea20815c46019cafefbf808264cd151a551607d57295b08b130dcc936c32b9d9c586ecf2d2ae62f353b0fb9c0a9b2eb4dc50a7ac12cb1c782415adfe6a2ef7d58f4c893d2cb501785e5d5fc4439847018ffa0142707474dcb40736a379a408f92bf83e719e9599e35969f0f3ec47c1a1a78b2663468609f818f1b3fe267f2acd2ddcda6068f9b3d2e021f9d63b9c6d922f6a879f763f17c9fa4fc64c18ba9fee1ad1a383e9384b6978595bfddd54a08ec4a1b56fb7dbf7fe9de9b60cf4d8d3082b238864ef23f0336756f4b0e00e30872600f87b76112d6c4b2ec1cd5c234cd4e3062f886040b8e4a6d267edecad7966ee43e92428c44ef294c18434f4ee0317ab1856e8b01acc9a821b7594462da20b7cbca013b9e6ef07434dfba97cf4b41fdd36aadc8ffdfc95bec624b227af1ea06af10cae4fea72ada570f2df547c3e9fd8ddd84283cebb8de338d5e83e454faa25394dad66457f66a3813993888606876d2bec156e29755d251e88af6c1603c3ae0427db9de04bb2e83a85eab1d6180553f35d9d0ee1dca7a3083f47ea6e1c7686fd89910b4f0bb5a768ea7ee97d55ce24c0bb733239074f79f7ccb25ccdcddd0c2d2693e37546cf8237451828b4147a688f2a2a891e2e23108bafd413e512c4a50dfd101c44ec1b27acbf4d68b566a6a544c81d5f67ab7ed5d7b42dcd389d367539b360fbc70eee289699a9000cddff14bc212ec8a6e18ab58c19727c403b99a7b982fc660f3d8f54e096dad2a281308d4f5d50fc6e1
+    100 1000 f7064b7217e8a6b74b381c58175d9ff2;
 }
diff --git a/symm/t/poly1305.slow b/symm/t/poly1305.slow
new file mode 100644 (file)
index 0000000..c2443c0
--- /dev/null
@@ -0,0 +1,46 @@
+### Very slow tests for Poly1305.
+
+poly1305-mct {
+  ## Monte-Carlo test from https://cr.yp.to/mac/test.html, converted from
+  ## the original hash-the-transcript form; see `t/poly1305' for detais.
+
+  ## These are duplicated from the basic `poly1305' set, as a simple smoke
+  ## test.
+  00000000000000000000000000000000
+    00000000000000000000000000000000
+    00000000000000000000000000000000
+    00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+    0 1 596382b2c34704b87e291250fcb927fd;
+  87d0ce793cb4cb966c8dd6ad0e6c6799
+    18bf6ee80c6d2d8d11ffe4f5b7214cf2
+    2a3dbb3acef3b4172e5f7045a33bd038
+    66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055defc3e0677d956b4c62664bac15962ab15d93ccbbc03aafdbde779162ed93b55361f0f8acaa41d50ef5175927fe79ea316186516eef15001cd04d3524a55e4fa3c5ca479d3aaa8a897c21807f721b6270ffc68b6889d81a116799f6aaa35d8e04c7a7dd5e6da2519e8759f54e906696f5772fee093283bcef7b930aed50323bcbc8c820c67422c1e16bdc022a9c0277c9d95fef0ea4ee11e2b27276da811523c5acb80154989f8a67ee9e3fa30b73b0c1c34bf46e3464d977cd7fcd0ac3b82721080bb0d9b982ee2c77feee983d7ba35da88ce86955002940652ab63bc56fb16f994da2b01d74356509d7d1b6d7956b0e5a557757bd1ced2eef8650bc5b6d426108c1518abcbd0befb6a0d5fd57a3e2dbf31458eab63df66613653d4beae73f5c40eb438fbcfdcf4a4ba46320184b9ca0da4dfae77de7ccc910356caea3243f33a3c81b064b3b7cedc7435c223f664227215715980e6e0bb570d459ba80d7512dbe458c8f0f3f52d659b6e8eef19ee71aea2ced85c7a42ffca6522a62db49a2a46eff72bd7f7e0883acd087183f0627f3537a4d558754ed63358e8182bee196735b361dc9bd64d5e34e1074a855655d2974cc6fa1653754cf40f561d8c7dc526aab2908ec2d2b977cde1a1fb1071e32f40e049ea20f30368ba1592b4fe57fb51595d23acbdace324cdd78060a17187c662368854e915402d9b52fb21e984663e41c26a109437e162cfaf071b53f77e50000a5388ff183b82ce7a1af476c416d7d204157b3633b2f4ec077b699b032816997e37bceded8d4a04976fd7d0c0b029f290794c3be504c5242287ea2f831f11ed5690d92775cd6e863d7731fd4da687ebfb13df4c41dc0fb8aec3d45ef6076ec131cdd55e724bae2931260484b73b12fdf2546d852cf089b64f622809c6ff963010d8aebc14e0cf4d5d06fc92e8e022a2a97dd822329d7bf26baf95f2f25fd2274cb4e10a596b570a97176273053be918c9a0a12ab3f61d83229e403e512b9f1fd89824ec9942ee782ee6972faf4cb5898e4a123c5e9e3f3fb71397d435b4de0f83627475ee6170f55646fc8aa52375fe4aae3711138b550c5f5654106b4ba7bc268ae7f63cf8a2401b2ba6c0894177e3f6696f9086760c3ce4e16844d1780c10b7785dcb3641fb488f077eacb89b8cf5b627a4449224923378dab62b90ef1c2dc7f1f7a196304e1805ee1a13f8cf12aed5526b82f62b5759ff12a573f68b8ccbe2d01d2e5615802edc3ef0a7778a1c565d9a836d770d1f5812e5698af213e398e0357834fffcde35d1cb77c821d21377401341f898475a4df74c58
+    1 10 7f1d971da577bdd6fb24437aaac845f5;
+  9eff9b802b9d43922975aea18a62834a
+    1f22e8534c98f80321384c88d00972d7
+    2b2f5dfbc2aeaead12f8dfaad90d4ec9
+    b84d332e02eda38a6cf2763928ba014bc632a0f2c0266f9093edc731eb2ce1e61c9a10bcb4bbd2b4b69e3f84da37dcc011142fbf8222c68a31f9bc227ead19f629ff4d6d414e95d3e9a3b2998d8e6db6379112f2d1e5edb32294729ad7d1c087b87d1c26a7e3b491f9d4452a2da50df87027d0f52c75e281091e86c86cc15091b0a6459a94e74597f86cd4801b4413f6185a7dd87f5d6bd3dfc8717afabd833c08160428a0fd0c2f9934921e9a7e40d9bf8a4d61781985aa6691cec4f466ab0bd8fe4cce660b31aeedec1cdc8ae76d3b9a01e5b5ca1e5d6932096bef253f427da7e974eab8aca5c0f22c9f0b5f1598e857f28c4a0b313890c718130e6bd18a150e5aa18e2f19bec1cb88adaeade2122ee13193287bbab77b2a334ffd9ba26210d170b6e7c7edd2fb6808d03acac8fe38b87a7523926e04028bc8df5753ced4b1bdf0b8a8d2315d210433417509dc685c660bd97678ef2d470c5845497d7d726e5ffbcc6e4db02d5d1a6a8b4629580ca3fae7392b33a992e73afd9e07c1cdc3576aedf6d2330f8dcf5d148873799e76cd4b8dda4eba698178d5edd6b260ef84af4517fbb68810571cfa7df52e241d8c43d3e035e23684e386c5d57cd0c4755614a6b8fb3529ee54ae2b2393dba3638795ec5c30c84dd3651ad75f96a4675df0b2d7e67329c5eefd62c2b4c378e2212ffcd788690201ba9eadb62395e8b3ca8bdacddc3a96f36456d0f95edb90dd975dd685348ada9658dc0d452cc99288460e12749a433770560281e6cc60a0cc654c664e53ebfa5bbc71c67993418c32ce1eb2643f64e9357b1ecca7805de5b75a3b8b918d719a229662fa8f0100bf6d717823b41b72fb9e91edcc75eaaabb066f7a714bcb0dfecf8d6944295cd40e3e53b041ac373e4dfe54336c9ce2417f297b64b1a5f45223a3b135c2faa6dda33a8128a545fca0dafeec4c1e6d187b336712802abdc8f658e5b3cbfc8961c0324e138cfafe635e3d47537890ccb5557d71e77419da3f7f513d22c6300cd51397c2e9f07d4584cd54081ec5339a43d64d6607159b9227f32e112b48e6644c7543be64d2015bddb8e50093143761a48cacb3376f16a34b86f82f066c9087d74663082549784b44444ee744cf5b62a5efc25aea49238df87dbecb07d9977901b70db24f8d851cb78b50d814be55d91c0775b5b7acb6a5651b260427057f6c86cd9fa709205f1d40431cfef5e3f90bc124637bcf35cdf343e3f52ce4236db25ac29aba0a8c69a9b0f1ae180f5be6e9c2a79fa522078604855624d5ec85a113738ee0a81f2d6ca9be1caa26ba628b0b1429c06b371f5e2801fec0599588417c02d21b6412ba01b904cae803c61e83ddf17e083a9b42564255c49ca91c2f079f8fc9439fe93ae322de7480df086018
+    10 100 e1cb88ba2c498ade2091ab06cefa24fd;
+  0c4ac91a4cbcd2ddec777ed12feaee3b
+    0411d5def6fe88aff66611870f7b83d8
+    5ba282f23cef22af42c21616603b8f25
+    1d630211fdf32fb5c4432d71b34bfd6daa6aac7ef09bd1656e7b710a70166f0e926d61116e29486f407724d581acf140d70b1352195971ed43c0857c2fe1018cb570317da3f5eae2b83e795e3375106bad876f2fd2cec8b972681cd3b0f4f75442694294f3659b0d452c8e39728e73bf402783d9c9ba3bc913473c747f212aa29d5303614869abce329d8c872a0da70f7d3e0d48a99f076117e32f313584d8531de33d8f99fc5379cc4f0dc2fb785bbdc372868d7598d7c2773ec7c6717f45c45a0d1db3f9fe342c809c407b56af8bc5896ecbd27c7c2517884018142a37cc50ad0a754dc5f2fbbcfdc45a8a0f4815c1ab75ea15fb1585e475c6ecd452a75fe4fad2719e5915b044833b67174cd6eaf4106d6e3512c9986e34442da5d46700c68b391f6dd0beb025f0ec3b719cfb643313223dc859c824025dddfb3cef7084c5ae0b139930c51f8f925d4c5cf7e9c8b6af2e43eed9fb2bbead8999e9d629d1e346244f5ef6034a018517954508ea83a1f32a682b927b5814a67bf581980d620e6f2102344121c74f774721d2e24a7fe426fd216ffcd1d7a3abeec214e21e6db6d17b5237ece8ad09e19643e0cbdc1a25e72d1e58c85810900e965e2f42ee1a5d947ea20815c46019cafefbf808264cd151a551607d57295b08b130dcc936c32b9d9c586ecf2d2ae62f353b0fb9c0a9b2eb4dc50a7ac12cb1c782415adfe6a2ef7d58f4c893d2cb501785e5d5fc4439847018ffa0142707474dcb40736a379a408f92bf83e719e9599e35969f0f3ec47c1a1a78b2663468609f818f1b3fe267f2acd2ddcda6068f9b3d2e021f9d63b9c6d922f6a879f763f17c9fa4fc64c18ba9fee1ad1a383e9384b6978595bfddd54a08ec4a1b56fb7dbf7fe9de9b60cf4d8d3082b238864ef23f0336756f4b0e00e30872600f87b76112d6c4b2ec1cd5c234cd4e3062f886040b8e4a6d267edecad7966ee43e92428c44ef294c18434f4ee0317ab1856e8b01acc9a821b7594462da20b7cbca013b9e6ef07434dfba97cf4b41fdd36aadc8ffdfc95bec624b227af1ea06af10cae4fea72ada570f2df547c3e9fd8ddd84283cebb8de338d5e83e454faa25394dad66457f66a3813993888606876d2bec156e29755d251e88af6c1603c3ae0427db9de04bb2e83a85eab1d6180553f35d9d0ee1dca7a3083f47ea6e1c7686fd89910b4f0bb5a768ea7ee97d55ce24c0bb733239074f79f7ccb25ccdcddd0c2d2693e37546cf8237451828b4147a688f2a2a891e2e23108bafd413e512c4a50dfd101c44ec1b27acbf4d68b566a6a544c81d5f67ab7ed5d7b42dcd389d367539b360fbc70eee289699a9000cddff14bc212ec8a6e18ab58c19727c403b99a7b982fc660f3d8f54e096dad2a281308d4f5d50fc6e1
+    100 1000 f7064b7217e8a6b74b381c58175d9ff2;
+
+  ## And the ones which take a long time...
+  e67ee956b65e3e7a221eafc7f0ea01eb
+    0ffbe8b89a03187d5f7214b8d288b467
+    cce953773f0420b28340a8faf43dcebe
+    97cba51fcc9a9854abe0d283981461d28bcbd2864cb1ffbcfee66dbf40e7a45824f0b7557bb3e2b42792aea3a8faecba712922c05fecc559b6ac2297ab5b1d2e308199c205b5a2e5ebcfdc27843e03d391110e79470308d67b03cdfd023493cc946bd635876f42935c89076a577557af4bdba7f1cd397c8b05f159d8207d7a5a8435f1ce996eeef6dd059bac6ea521125a8b972fae3be55d35a7536c441cba26013d9a21fab4c9c94241c7d862146386951d4955fac4916178dff83af45252341659aa2714b710adc9d7953dc06584fb9f34480b6b834e1ab10b138db634130d3cd607ce93722070b16b83aa71559e2d515c3b582b7a630d5b825c5df4faecefd9cd905043a85c4caec7502d238b25c06b4231b8af96aae7f58446c0b21b37b2a8c7af43406d4002eef6e0311358f3dd27d8123032a9bfed32aa916f59e286739760e58bf9e6675a3353b2678c0aa858596199b5116071b0f7f8b6e8683f46cd78f0e69c5879da781dddfa1b92ef3dd187add8deb65c22ffc302866738cb62e38c4c0320cc55a9533067ec6fcdf5d432bbae9d79e1b1c7fe84a52f603ecb4352f5d99bdecbfde786ff8e98b1b4997b28fd5515ce14198a8678595c0b457b174a816989f2df949a21a5b70adef9c04c6b0371c80b2b50d70c7b8fbf122abde9d815927050734b29973038c1413d23a1d2cd282ee6a6c701595f6900728aad1f0a1ff9e00a2e5412c7da0bc20b86e7e5fbe4137838c0dcc8d32b02359b45a85f98fe069b4a32061110d0dc0f6a15eadd409e292b26b9f250b6c6f4e9315320d8f36601fbcbe01163f59a3641e0ccd29f037b93fa6242696f68aaace3fc2882270a7e90dad52a265ca9b26071feafa347fbb79e1cd64141c7eca85e53c4c0b8b51f228596de801f181860bdf0ae48f025152611d92b7f440ca3efd072ddb1daf16bab4ed3b3be38a2b898a0c7e2c691f48ef88ee31711df2e2a2b739367412e44eb21cd56daba191f4b92500e2cc60e9ed77dfe8eacd89b35804070879326a3d732a73850c6ce90d68652be417fd37200888ee0b6f946050d4057613c9dd1e0c4b6923e01bde22735505ffa967311d72931a58be47157a09404ff67116b44c11efb021c39987f1ab9bbba3ae7b20cff80b058de38335f338bf8b2b3abd08d8b21a83c9713e8187ef98a43f811142d2b16c1c9919e242662350069c7dc59f70f499544f4eedea9ce55f8a6cd637d983191960eac0510f6013b21342a76571fcba18c9374d6554009fcbaee3db509f404edd4bc9d6fbaf9cad1b8007beadbe864a7c58eddd25c039dbcf43ed3eb03295d144f9f2186ea704fb065a5e3594364354f5c6720ee4c6f8731235d1f98851f586fee0952ed0e1d201f673e25377111a5212537fb4ff2f69dfcc7
+    1000 10000 526fbba33c160146160ab9abc3514d70;
+  de4b1444f9b3e24182a8f893069985ad
+    662f8dcf37c2537fa4acda7f8588afca
+    0d03c71cccec848e7b613408b41c1ac8
+    37b9a507c7e12a61a6e9248ba6b2c9b09373551a15bf70dee6a24059a9f32cd24519fba4fccb04d91dad2ac4b3f58850d6fc3e3fe7ecc43d2e6a61c4e7f31c3ec0bf91e272c7500e7697695893cdff766e1acfb858d7e78d899c50eb73ed4d7c27e54c1e3f26316d943cbb70ead8605b7fb1f9314f910cd058671069cda3e83085c9e145d1f7834cbf6d3688d9bd485e165567c39b45f70d865be3730b46f4673348250aa0984ba91ae35b911e0ddcb6536c94abc68ce1d040808cda931f0e327ec06face8eff8a84b587b57cead5be8082571cdf60c26c79b6004113659de81e6e59815c0ccbbe4fbff6f451851f406135806292a21ea803c1a64ebaf4274649f07533e6835c4aa8831c30410d05f398f2c6fbe1eff8795cd517bf5d2d96c1e752bf0f8372a4adf538fdcfce1da5d96ecb5aeb1475eead5207d12506f496c9f6c23b87d5dc8499a044a1c667123e1e041c0c821bbb851d049e5f30a37f9578576eb48a663566381793da0df295a127c6492b1eff9592274a38a24816cf6b15cc890465d11e8206dcf1d53bb467d788b277d855008699d8b49ea8019137838de83fed0cce28825e541a02020f426eeff1709370bcabbf99e341a038722dcfb8021914b20df5aae68c36137337ba2467ee5c8fb603c79eda88063215a59097a85eede7b8991aa7dde58c8046f62458cdfab00d648401c124ddd60ca160d8ba15ba9cc44d5077c73a73ace9ccf008e00a17b2d618ed4f935db3a452d37b6736900f017066f8797850c625ff6943ac5969030fe3c53d3126d8f0d8c43b2a229f077e57b4892378bb2aae0501d3c48be841d8daa6ac55a86e901ddc56fcb33c8dc06593719e60f3a54467ad502df248ebed9992bf85e6c61f50cbdd0a9100a9a09e11bf3f422318574dc655ba404650163e9a01f5b70fa789167656211b3a2c844d7ae13580f610b723e99f3efbd6c1c3646468101772d8f9a46e845bde3c4220c53051cdb7181459ced3e0f17091a4d6c3f9adef6f0d4d4bb219d37c6ef8821d1f5bceadffadb2f75d5b0ac1f7bee9be5974c84d6796a5d3f6ea4907808f562e53e9c83565c557eb9556f13955c905c3893c893db2e6ad4cadb79aff4355fb0ae54c80ae4a04820dda1b34d7afd9e324d8a9796ecc581e396f98c6c98188a358e9266b1a3781f9a863771288860c29426d0f17bce970fd39fa43d845f93b574a8b0b267d3c1589b79227e9b95ee0e78705e95ac5758db2c712de4a7b96d60e53b158e378a0f475a3e1c22974c4a90ac222e0f0098cbffcf35777813936764ef9c2574f5edeeeba642163ea08b83a14a1c8dfaaad46b93547c2486addcd7e51178b987b4d6ff365f12785bc7465aacb16d749a823ef81b259c2624addc7d863e42091fb76f5642167e27
+    10000 100000 1beef5f2e126cc3c549276415c17e51b;
+  8404ffcc46b4d0898a8de08bf5eae9b6
+    7bd305dadd4f291f6700c7b48446988b
+    c18d7e08835beed76ae9e82396af49c6
+    bacc8b39ade64cc59369ff39dfd2522c31a603edacdb057ead0d96e9d5ec1662e6d065bc2414852f9e8c67275ce0e82f12645fd458f4e5833461f69dfa92dc2fc77e488539617ec8857707e60fde8dd27364183373bb45b292495b89e74f730fc8bedd6b8e4ad23c464b069f4fbda078584ff1cd06acb21436dfbd3488cd7098981f1c7ef494da47f8367a4d0992a5f08a2d4193770d295cd5fce729c114ed16d7c68f5412b91189182ef8a43462637247e5c269cdf4e4e03334827157c72679617fbbf71bd5c59dc898c937f9a342c0c1a5fa69122b07f396b89ccbc51e17e7062278adb6c7f5b3a023cbfa5dbf076b6bd35cfd65cf9c2e6fa905a597e56fe81639966a0848e7cf1f9690342d5bd25e1cff78d510e407f49d42f9031921fa7757632a2ca344912b646376b67ddba85e47939fbc9f99cf148c57df560a4e84cdd86d7adef2a9d7c9f7862084ac393d92cae3ed1812b1596d4b594be1c7cad3fe7715484ff838285f9c78dfd08e41aac6820ddf4a443202a4ceb0c10225766dc4694ae3c97f9c37d96971c9fabde40d453a50f030ccd633596fea1326a6944952b5d1a7be5afe6c1b79be0a4737abb7771c1b5951fa8a4f2030560ad4756171d6dc91b0d052f39ffc58edb14e89bee46442ad6b0bae8b9a3e9be2af7a10b8136cf832d1335921ed325dca3fe10164888d0969a941206f3a2671186b3aa08ad120a040267548e2975bbd7aee77f9ea0eaed42f3e5f8b6051822fef1461db72a5bb3929719a2b1ca72d350c9986bd3aaef8219c260803258ad97f4cf69b435bf229ecfdda161ddf6efe1f37818301c1e06296555a87f51c340fe02b4eb24ff03f8d0a5d7699f99be34eeaa29d4b0a97971a0f8f5a3834b00031a58d94192b944d9ee460961086eb35d0de5a9dada27022e1c7dfcc5ed6c615c72bc5cdf02f9a98e6f49ed8558ca8cdaa9f610ce93f62568596c913c0f96d55ea593571a9b41e2bd4a48d0be558feaa1c8e30f49fb57be63fbe55c81723e1c4402c2ff085959318079d3c531cd69d2599d2cb321e02a0a850419932a3b522c33b8343653b30ea1a53909fe5384ed977ab3c2c20b60055d1eeb3bc47e7a79489ee69a5d42984f73864f048309c4f7ab0e02d5bba5e7c5fa6ead2f92f456e67878269bc8efd4c398791ffd2e005f9563e17a0872478326ea0a8c0f1277c345e19bee04d23fb1bc5af0f39d3a1b8c2ecc92324ae85e15cdc2315600c456efd341a4ceb1731b3963e3b8dc0d67cc486651915f40668c62b46b93d359d869a0daac341f1ce370372194d4c7636f1b370d3890f69a684bd43641e0541b451e89f780d442ec940c1b573211afaca11dd4294e310e341cd396e711afb00676b6afbebe67a3b5f4eac5b41afef4aed2c370e1ca0bd
+    100000 1000000 df62013a9d388ea6e82cb7295fa706ec;
+}
index 9d0cbf9..4adf562 100644 (file)
@@ -525,16 +525,16 @@ square {
 square-cmac {
   f260d7bc
     ""
-    29984a33657f622352c1ee581eab804e;
+    a6c67fdb36ee9d8552f4112e08d64d76;
   da163547
     d3
-    cdc5e3485007695cdeb53ce1e8cec39e;
+    df74a9ab4bb73ba0904abebbc8aafd31;
   48b75511
     95e77022907dd1dff7dac5c9941d26d0c6eb14ad568f86edd1dc9268eeee533285a6ed810c9b689daaa9060d2d4b6003
-    7fdd1689e5a004d05b0bc92e3ecc8f16;
+    93b8e5f20003a16bbc160e0010c09ad2;
   062365b0
     a54364c76c160f11896c4794846ecfa14a7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd
-    95f37b226b5f157fb68c9b235085799a;
+    fc6bc3e49f756681f782dabfaecabdab;
   739a3d1f337f29549e6b0d27
     ""
     c48de9e2768ea55c731b65c22052aebd;
@@ -573,25 +573,25 @@ square-ccm {
     eb
     ""
     ""
-    bba9ea11;
+    0b77d435;
   14ad568f
     86edd1dc9268eeee533285
     ""
     a6
     0a
-    507bd8ea;
+    a1d8e1c8;
   ed810c9b
     689daaa9060d2d4b60
     03062365b0a54364c76c160f11896c4794846ecfa14a7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd
     739a3d1f337f29549e6b0d27a4ba234085406a6136512061f7080cc07df0591d8fa21f2dd88374d8cde8e160ad10997a
-    2de5299d997f5a95d32115f84b8cbc072043580e27bb6ace6654173512e0d464ce4358899c43ac2befe5cbe68155797b
-    217a8c66edf6aad2f17f555c8936028b;
+    2de5299de77f5a9551211586c48cc2fb2043580e59bb6acee454174b9de0aa98ce435889e243ac2b6de5cb980e550787
+    890c514f7759ae037140eef723181c48;
   21635c6d
     62c9269029df3e6057
     acc87638f508046733d9ff61cdbda3b3e9878731ebfedd4705e505da1435dceaa7b1cc49ae1d50c38201a8
     94476b3f102b752eb9529533966f27043eb621b7f65b000961040ef2f9b2fc5fa450727a9b542cde52ebfda19d0ccc520f215eb57b
-    f88c7d34053cc7278c3e3b09715ab3dec3aa050aa70aa6dcd560a9ea58ddd10857b9bcffb98e2af8a3c851f60f540ab8872af0df1d
-    a65b843ead57238ecd6bf0eea46d6fbe;
+    f88c7d347b3cc7270e3e3b77fe5acd22c3aa050ad90aa6dc5760a994d7ddaff457b9bcffc78e2af821c8518880547444872af0df63
+    06f53d976392e9e86ac221394b5fb07c;
   b3a4f3ebbbb18ac6c95a97a4
     8030370c33d090c54215ab
     ""
@@ -660,37 +660,37 @@ square-eax {
     ""
     ""
     ""
-    66848361a1d85500e3efc4889f3c2bd1;
+    68950a6ce18037350d23d4b89f253b27;
   da163547
     d3
     ""
     ""
     ""
-    3e14f9c0f9b208db9a52730d00525dc6;
+    aff75e00b2b5ec870ee51a74b87f658b;
   48b75511
     ""
     95
     ""
     ""
-    1b81e7698ff5e258ea2e8f75b7bcfd88;
+    d62f0eac68b965fbb852442a082b390e;
   e7702290
     ""
     ""
     7d
     d5
-    c256dd01d1ee4db1387ef0f0b3d43b57;
+    3b4a842254e17ddd4906899551af908c;
   d1dff7da
     c5c9941d26d0c6eb14ad568f86edd1dc
     9268eeee533285a6ed810c9b689daaa9060d2d4b6003062365b0a54364c76c160f11896c4794846ecfa14a7130c9f137
     120634c9519848a877ff77bf79192a5b50ade5d9cd739a3d1f337f29549e6b0d27a4ba234085406a6136512061f7080c
-    cd9fbedf0befc513ec208493902b99f4e304654e0460d29920804d69dc150fa3e31d86a755ba930d0211fee9b7ca5f9f
-    f01b0b0089bf48fedc2c21556a1b644a;
+    792f3471e561f7f3c2da13d0ea08d8b4299c8514c61fc51896804fe26089afebe792f88719672f75b3656f491e894635
+    0515fe199fc47f8f105e5ce90fa181ea;
   c07df059
     1d8fa21f2dd88374d8cde8e160ad10
     997a21635c6d62c9269029df3e6057acc87638f508046733d9ff61cdbda3b3e9878731ebfedd4705e505da
     1435dceaa7b1cc49ae1d50c38201a894476b3f102b752eb9529533966f27043eb621b7f65b000961040ef2f9b2fc5fa450727a9b54
-    b88f48021b69e18d9457120fc11ebdd467c0b1119b5ab0bd16a5c3aa70e263d3ab841cbe6bba4fd02c94690dd143746294f825c26f
-    e0efd3bd220277de49de30272939c21c;
+    19a55efbd17a2244fe3c555e336fc60c40e085e44dde6f28ea6e3194127b46e45cb6245c82a396f1f29f784edcb0055d3f04e30eb0
+    074a5996cc3961dfceed8fe3d2863452;
   2cde52ebfda19d0ccc520f21
     ""
     ""
@@ -771,43 +771,43 @@ square-gcm {
     ""
     ""
     ""
-    66ac17c939ebd05ca617e77ba83201e9;
+    66ac17c947ebd05c2417e70527327f15;
   da163547
     d3
     ""
     ""
     ""
-    8361e8e723233ce477ef9132624bfb9e;
+    a793a26107dd3c18ba0431c52f2708af;
   48b75511
     ""
     95
     ""
     ""
-    29a7e874d6054c42dcc1ada2e6f38cf8;
+    19d3533dcde0922c2d3ffc53bb921da8;
   e7702290
     ""
     ""
     7d
     7d
-    d439d2be5e01a0d767739abd80dbc293;
+    d51f111bc7c1b32ab76788e279cd208c;
   d1dff7da
     c5c9941d26d0c6eb14ad568f86edd1dc
     9268eeee533285a6ed810c9b689daaa9060d2d4b6003062365b0a54364c76c160f11896c4794846ecfa14a7130c9f137
     120634c9519848a877ff77bf79192a5b50ade5d9cd739a3d1f337f29549e6b0d27a4ba234085406a6136512061f7080c
-    f36d7d44b8637a2a560a8e89a7a5a5c754de68194a47bb2befdf2384e91eb38cc70807a42a687eb3113c9c223e1c8763
-    e13b8f67d62247d887180b4cfb90eb6b;
+    3c011823aab23337e5a5c1d3bc7422b6216ffbea8135a4e2ffb11dfb68c2a14e746ffc5b48f0551c9f22642faf9835d8
+    35aaf9095f76e809798699ffbe1c0b0a;
   c07df059
     1d8fa21f2dd88374d8cde8e1
     60ad10997a21635c6d62c9269029df3e6057ac
     c87638f508046733d9ff61cdbda3b3e9878731ebfedd4705e505da1435dceaa7b1cc49ae1d50c38201a894476b3f102b752eb9529533966f27
-    863f1602c0fb45b1a3264e8140b5addd0a82feb9b5e632fc539b3ed83667eecbf32c65dcacec865ea475c3506d61d00db1ba24288ba1a13ef1
-    58de876618ed18d0aab09ef9acd06bae;
+    863f1602befb45b121264effcfb5d3210a82feb9cbe632fcd19b3ea6b9679037f32c65dcd2ec865e2675c32ee261aef1b1ba2428f5a1a13e73
+    80d044d5151c9bd4360850268cf9b6f6;
   043eb621
     b7f65b000961040ef2f9b2fc5fa450
     727a9b542cde52ebfda19d0ccc520f215eb57bb3a4f3ebbbb18ac6c95a97a48030370c33d090c54215abd6
     b3ad54efc9a38378c5b93bf4f2aad2605faee2b03fb648e27fff63102758fe2b69ac26afa3349829b94586306fed54154f8f28523c
-    1e559d1bc26402ae885ebcb0946a573d44df5d59476510247197bc816467f5386887c5543f3a2256e26f44a91b31afcb7bcc85b2dc
-    83783d53918eff6b310540ac8c1c5d85;
+    8eade4d2955ca654b8aab9e25092f2ec8885f08960d1692a8c09bec546d8fd396dc75e52f6331a96391fa8e4a7d18b25bc86ae6aab
+    cca26d676181ec28242129be174ef96c;
   03d4de1600157846b710ee72
     ""
     ""
@@ -900,43 +900,43 @@ square-ocb1 {
     ""
     ""
     ""
-    4ec5c312526c730e41458aca324ae0a0;
+    9517fb39f874f5eb9492ab804cac53a0;
   dff7dac5
     c9941d26d0c6eb14ad568f86edd1dc92
     68
     ""
     ""
-    d0cc24f9084261cdaf4393e7b250701b;
+    f250f3dd82723801fae0678035528efe;
   eeee5332
     85a6ed810c9b689daaa9060d2d4b6003
     ""
     06
-    72
-    1e1671f47f5bd8b32ebb09c019105281;
+    7e
+    9d05909f6a5484b677be057af1ea2a8a;
   2365b0a5
     4364c76c160f11896c4794846ecfa14a
     ""
     7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd739a3d1f337f29549e6b0d27a4ba234085406a613651
-    11e134a63441fc0951b37098764822d6eaa47187fe96a2f3658563cc612c857f0dd5d4e056c820a3670828dd7f5a9d1c
-    86893621402b0a64fded1b34d47504fb;
+    90236cc04b3ce7ab2c782d22fb93b02ae0583bb026f837c28a6e4ae22b533878255b39b158962faf8814c99423e5fa64
+    29a472eb0282c912127b000174003c50;
   2061f708
     0cc07df0591d8fa21f2dd88374d8cde8
     e160ad10997a21635c6d62c9269029df3e6057acc87638f508046733d9ff61cdbda3b3e9878731ebfedd4705e505da14
     35dceaa7b1cc49ae1d50c38201a894476b3f102b752eb9529533966f27043eb621b7f65b000961040ef2f9b2fc5fa450
-    1599b31480519c999bbc9fd4fb1a24676071040c16ff4a6834cdaf46c51ccbb9522cfde0e2df2867871cabd4aa73c33a
-    e8e411a4ac803610a4f2a8e1b6aabba9;
+    f16cf340bff7a524dd6b369039ef6eb7f1e0bae5814c79864964a2cb624b56041fe2c7f997b5d3760324b40197e72a0d
+    fab36fdf2f2c4ded8846e9d22e6ac9a8;
   727a9b54
     2cde52ebfda19d0ccc520f215eb57bb3
     ""
     a4f3ebbbb18ac6c95a97a48030370c33d090c54215abd6b3ad54efc9a38378c5b93bf4f2aad2605faee2b03fb648e27fff63102758
-    384781d5a43f9e0d692787efd9cbbff3f8cc6e154544695653e495e63b3da9730b781b214f5c8f6d940542c76c24f0acacfcf099c0
-    5fa2cade7fa22e8df91b9f91593f9d3a;
+    2469ec52037cd3e95b25de4c6ef7be38887e49d789f649a91cb0301fc2a94d80377a623f24afb58e9428b5c089cb6eecd6a7408678
+    357e06fe7770804cccb09d3f80cad777;
   fe2b69ac
     26afa3349829b94586306fed54154f8f
     28523c03d4de1600157846b710ee72807a2219bfb474fd71d891f24bb65d1563259f9eb53b571ea629c54d
     57dd2d42f70800df9fcbaca48b77dba189196d1ebba10b0467cb9fc2712a199e533fa9156308cdec3f768281e040a9b9a222bd689a
-    4c8c3874f73c8504b64bbe612589c103a462720d244c3092221b6c7b31a162aebefaea934d2ac8eb9e03f8f480aa1407d58a8a8e0f
-    03fa1ede942984e0ae4f30946f7e0201;
+    d0a3845096db507a5f0fd48124160df0b71c43dcbcdb4383629901895b30f400b59087532c0357816645f6d972c8ec143524b1b628
+    6ef130cca6a884d3a8a04d06ae1ad5fc;
   ef66f5306ceb0c6b08ac8b0a
     22260c571b4a42bb8fdb233bfa6a5cfb
     ""
@@ -1026,16 +1026,16 @@ square-ocb1 {
 square-pmac1 {
   f260d7bc
     ""
-    aec43cacfa9e76ed9f6c0176974d1111;
+    aec43cac849e76ed1d6c0108184d6fed;
   da163547
     d3
-    38e2ce3d69f8fb21ed28599d77607fe8;
+    38e2ce3d17f8fb216f2859e3f8600114;
   48b75511
     95e77022907dd1dff7dac5c9941d26d0c6eb14ad568f86edd1dc9268eeee533285a6ed810c9b689daaa9060d2d4b6003
-    7c8785ab3dcb7d71ba3d6fcf7ba32376;
+    65c61b1f4a19d8c1a15e7f7f77fed761;
   062365b0
     a54364c76c160f11896c4794846ecfa14a7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd
-    b21eb39143554c29a104eb7ca8d10ca8;
+    0df810432393db82d08f39204917e303;
   739a3d1f337f29549e6b0d27
     ""
     8a5016e4834fa9fe8ef64746a89189ce;
@@ -1068,43 +1068,43 @@ square-ocb3 {
     ""
     ""
     ""
-    b1669cbf9b59f73b4288eb0761332190;
+    43ecf932f2a50f9a85f7dedbee4a09f2;
   7dd1dff7
     dac5c9941d26d0c6eb14ad568f86
     ed
     ""
     ""
-    780716f09513ecb8dd6f322f3901fd27;
+    4e23472f7776db3c194a95e9523bfb69;
   d1dc9268
     eeee533285a6ed810c9b689daaa9
     ""
     06
-    b8
-    1fcf843450fa4d3da50d8de6525119eb;
+    06
+    80f022fdba9a783af386696a7468a7d0;
   0d2d4b60
     03062365b0a54364c76c16
     ""
     0f11896c4794846ecfa14a7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd739a3d1f337f29549e6b0d
-    33f91dd536f3667b220ac4fa9ef6bec830fc052968f5c3c78141ad56254dbe7fff02e85ea5a278bce0c9a5037caa31f0
-    11621f557ad6c52a82e8d9495bb284e2;
+    ec00fcf09bc64fb96c5630b05426c0f3e11c4e5e278f64dd5391ee47cb3765af3407824e449c2f3ad2e53b1d7a754391
+    838c24cc6117a97999b82d8a6f49927f;
   27a4ba23
     4085406a6136512061f7080cc0
     7df0591d8fa21f2dd88374d8cde8e160ad10997a21635c6d62c9269029df3e6057acc87638f508046733d9ff61cdbda3
     b3e9878731ebfedd4705e505da1435dceaa7b1cc49ae1d50c38201a894476b3f102b752eb9529533966f27043eb621b7
-    1859d78e9ad0fbc8bf76a97f08e6de088994238974dce458f7dc145948b8ba9f8b8f52c82a1ff60046350eeec8936379
-    350f84d218392b1282daa7db5ebc4e5e;
+    688100f96e36393c78db3753d70228a939eb45ddb3692356fad4b74ade71fea04546dccc6896115975a92e7d715e329b
+    a00f680ac9305870d77686138a56be0d;
   f65b0009
     61040ef2f9b2fc5fa450727a9b54
     ""
     2cde52ebfda19d0ccc520f215eb57bb3a4f3ebbbb18ac6c95a97a48030370c33d090c54215abd6b3ad54efc9a38378c5b93bf4f2aa
-    664cabfb69a25342a0bcc17049c316c78c412df3efbf75cc192b936dccf87b3c094d51468c20911a477cd6a214c403ff99abec313a
-    09b989eae5e6973b675f06ab4b9ad1e7;
+    47cba1883ecfcb1c960a4af93ca6a30d74cd55635368c2523b675910d02e63ec2f6255396d7a9325d8bce6913293860c952f3755c3
+    1c31d84f0f6360eb2dd47910f1375c61;
   d2605fae
     e2b03fb648e27fff63102758fe2b
     69ac26afa3349829b94586306fed54154f8f28523c03d4de1600157846b710ee72807a2219bfb474fd71d8
     91f24bb65d1563259f9eb53b571ea629c54d57dd2d42f70800df9fcbaca48b77dba189196d1ebba10b0467cb9fc2712a199e533fa9
-    9f53984003391daa1fe81c1e9385d1d902f330b930b225a8164f06adeaff820d08dfceeba9ae1a9639d556bba8ad6563186d022240
-    64d960c38db07c9ede67bd56b122544e;
+    d8a4afa64f98f4dc2a74ac259523c9201f96b07b7521a3c6a25b40b62c52fb1e65a993715cc737afc6e84b8ad7eb31386ee8bc8941
+    05c0bdead7d29c226d28e8d257555b3e;
   156308cdec3f768281e040a9
     b9a222bd689aef66f5306ceb0c6b
     ""
@@ -1195,13 +1195,13 @@ square-ocb3-mct {
   16 47b0a29f45c1b000f1a0c27eee7f42e7;
   12 7a80e7d6e556778f77a327928ff4dc35;
    8 886bfcb71cd24995911ed011b231d99e;
-   4 d0b2597b73f284932af5a5db0fd1fb3d;
+   4 6554bcd7716933559def79025d3c06ba;
   16 5c3d0dfbee0d8977a7cee660;
   12 6057212ee2684bb36520d44b;
    8 5f0f721b8171461d242bb883;
-   4 127b569653ace9af8aac7f47;
+   4 bd7b3db643c64178da4896a5;
   16 4184125b37fbd3fc;
   12 258e75d36b5359b3;
    8 bd8c4a18f64be7cf;
-   4 55a5c5e40c4de0e9;
+   4 4f39f80256368cc0;
 }
diff --git a/symm/t/strobe b/symm/t/strobe
new file mode 100644 (file)
index 0000000..1d05675
--- /dev/null
@@ -0,0 +1,1288 @@
+### Test vectors for STROBE.
+
+strobe {
+  ## This is the `Concurrence' test from Hamburg's reference implementation.
+
+  ## Initial conversation.
+  0 init *128 +;                       1 init *128 +;
+  0 ad/M =testing +;                   1 ad/M =testing +;
+  0 prf *10 !8a13a189683bf5678170;     1 prf *10 !8a13a189683bf5678170;
+  0 ad =Hello +;                       1 ad =Hello +;
+  0 encout =World !123bfbee34;         1 encin !123bfbee34 =World;
+  0 clrout =foo +;                     1 clrin =foo +;
+  0 clrin =bar +;                      1 clrout =bar +;
+  0 encin =baz !15e518;                        1 encout !15e518 =baz;
+
+  ## Lots of `X's.
+  0 encout
+       =
+       !;
+  1 encin
+       !
+       =;
+  0 encout
+       =X
+       !c6;
+  1 encin
+       !c6
+       =X;
+  0 encout
+       =XX
+       !1016;
+  1 encin
+       !1016
+       =XX;
+  0 encout
+       =XXX
+       !8ba0e8;
+  1 encin
+       !8ba0e8
+       =XXX;
+  0 encout
+       =XXXX
+       !4990e91f;
+  1 encin
+       !4990e91f
+       =XXXX;
+  0 encout
+       =XXXXX
+       !5eec19edd5;
+  1 encin
+       !5eec19edd5
+       =XXXXX;
+  0 encout
+       =XXXXXX
+       !dc06c47954a4;
+  1 encin
+       !dc06c47954a4
+       =XXXXXX;
+  0 encout
+       =XXXXXXX
+       !9a4b7ade0aa89e;
+  1 encin
+       !9a4b7ade0aa89e
+       =XXXXXXX;
+  0 encout
+       =XXXXXXXX
+       !0d1b71cf776af3a9;
+  1 encin
+       !0d1b71cf776af3a9
+       =XXXXXXXX;
+  0 encout
+       =XXXXXXXXX
+       !fa037bf1c020190bbc;
+  1 encin
+       !fa037bf1c020190bbc
+       =XXXXXXXXX;
+  0 encout
+       =XXXXXXXXXX
+       !a66e9933c1cdcf5fc084;
+  1 encin
+       !a66e9933c1cdcf5fc084
+       =XXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXX
+       !561f708ef060b436728811;
+  1 encin
+       !561f708ef060b436728811
+       =XXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXX
+       !12fe9d8b24b91b434ec114ab;
+  1 encin
+       !12fe9d8b24b91b434ec114ab
+       =XXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXX
+       !9a516cf12e38944e370864e0f8;
+  1 encin
+       !9a516cf12e38944e370864e0f8
+       =XXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXX
+       !9155393fc1155502250c839e3758;
+  1 encin
+       !9155393fc1155502250c839e3758
+       =XXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXX
+       !9731d438dd00d5de2a3bb21a6a14e3;
+  1 encin
+       !9731d438dd00d5de2a3bb21a6a14e3
+       =XXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXX
+       !41c74574fe9efce9225aa766a83d4f42;
+  1 encin
+       !41c74574fe9efce9225aa766a83d4f42
+       =XXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXX
+       !65d720eafbb3261f0d960d70c35dcaea20;
+  1 encin
+       !65d720eafbb3261f0d960d70c35dcaea20
+       =XXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXX
+       !703a1db79e5b68de3760c58f90fa6775f455;
+  1 encin
+       !703a1db79e5b68de3760c58f90fa6775f455
+       =XXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXX
+       !b47759d2263e7d97760a4461a0d04e7fd56f6e;
+  1 encin
+       !b47759d2263e7d97760a4461a0d04e7fd56f6e
+       =XXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXX
+       !3869204c64230931d7c2e5647dd67e2800db2039;
+  1 encin
+       !3869204c64230931d7c2e5647dd67e2800db2039
+       =XXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXX
+       !4aa5d694e8006d83f87cfc62408b6127b77e47bdd7;
+  1 encin
+       !4aa5d694e8006d83f87cfc62408b6127b77e47bdd7
+       =XXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXX
+       !1f1e7cee02468524a853c1c1b53d561443d89b19d536;
+  1 encin
+       !1f1e7cee02468524a853c1c1b53d561443d89b19d536
+       =XXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXX
+       !7c3fc5d277be9e4525d80442d49d21536fc460a7317f0e;
+  1 encin
+       !7c3fc5d277be9e4525d80442d49d21536fc460a7317f0e
+       =XXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXX
+       !fd2f4cf48128e3a4d20d1ffedb7e1629ba80e89e55b1d343;
+  1 encin
+       !fd2f4cf48128e3a4d20d1ffedb7e1629ba80e89e55b1d343
+       =XXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXX
+       !c85c1eb7626b48b96ebb245822a54dc95a6d96cb6a2af76c3c;
+  1 encin
+       !c85c1eb7626b48b96ebb245822a54dc95a6d96cb6a2af76c3c
+       =XXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXX
+       !1b3a6f13c1741e91fad9a060ba3f144e356a7c8e5bf6ea982d84;
+  1 encin
+       !1b3a6f13c1741e91fad9a060ba3f144e356a7c8e5bf6ea982d84
+       =XXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a979bb3b94687d0d60c55dbc071668da261bfa6f37f91a2d194115;
+  1 encin
+       !a979bb3b94687d0d60c55dbc071668da261bfa6f37f91a2d194115
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !1e2a52e3220403c77bbb0e6e2b9456fcb918d5a0dfc04eef76fc9278;
+  1 encin
+       !1e2a52e3220403c77bbb0e6e2b9456fcb918d5a0dfc04eef76fc9278
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !1f34cc349d503817d28fc1a67b8dd54a4082ae73d8d4f004279bd7ce63;
+  1 encin
+       !1f34cc349d503817d28fc1a67b8dd54a4082ae73d8d4f004279bd7ce63
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !f7c6bdf8689f0cd920c8ff6745b3fc285decc5a73180841baec2008b72ae;
+  1 encin
+       !f7c6bdf8689f0cd920c8ff6745b3fc285decc5a73180841baec2008b72ae
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !89bca3b3fe374d7776fca1568c67972791638e6004e37fc3b792388fbf4d94;
+  1 encin
+       !89bca3b3fe374d7776fca1568c67972791638e6004e37fc3b792388fbf4d94
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d752309a5e5ebe2c591c55b9b42c1aebc3a5d17bb696bd5e5fece6d70cd4a076;
+  1 encin
+       !d752309a5e5ebe2c591c55b9b42c1aebc3a5d17bb696bd5e5fece6d70cd4a076
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !e6f0b8800dbee31e5a9b1487e186ae7e5839becfbe8572afe8a907803b6298ce47;
+  1 encin
+       !e6f0b8800dbee31e5a9b1487e186ae7e5839becfbe8572afe8a907803b6298ce47
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !ea5e143b8a616211700347b46bda10ebd200ebad8cb747bee6ebbd2044bcbbe88767;
+  1 encin
+       !ea5e143b8a616211700347b46bda10ebd200ebad8cb747bee6ebbd2044bcbbe88767
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !c88d7a83b618b4a3bbaf5cdcd9bfb77b8593163e29b656de06aad9dcba67f1090894a8;
+  1 encin
+       !c88d7a83b618b4a3bbaf5cdcd9bfb77b8593163e29b656de06aad9dcba67f1090894a8
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !13343f85d5c33de49ebab48d9ea012ec6bee78fe997b9d465220b267ce527df63963920c;
+  1 encin
+       !13343f85d5c33de49ebab48d9ea012ec6bee78fe997b9d465220b267ce527df63963920c
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !0772dfb3e97ffe9897e254e183ed6a339e14b6f6ce98579cbd74b0dd12705f0e8fc83a22f1;
+  1 encin
+       !0772dfb3e97ffe9897e254e183ed6a339e14b6f6ce98579cbd74b0dd12705f0e8fc83a22f1
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !e666998f161d9d5cd8a4b84b8f69c7f242843a7d70a368e0012aebb31f3455c8db0a2762d56e;
+  1 encin
+       !e666998f161d9d5cd8a4b84b8f69c7f242843a7d70a368e0012aebb31f3455c8db0a2762d56e
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a617e710a24d8ec6e8d0643913558d03c979dff5adb4807a1bb676c54abc271e478e3cd26441f5;
+  1 encin
+       !a617e710a24d8ec6e8d0643913558d03c979dff5adb4807a1bb676c54abc271e478e3cd26441f5
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !4ac10a6382eb48c30b8f3532c5d2bd6aa01258713c8048843f2f01aeb07ec9673ee23c12ebbc496c;
+  1 encin
+       !4ac10a6382eb48c30b8f3532c5d2bd6aa01258713c8048843f2f01aeb07ec9673ee23c12ebbc496c
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !3a14815a68d3156dbb049088b1c3cac707e1fe98d6f6c2ef702507b73d36341539f5bf3ec179c459fd;
+  1 encin
+       !3a14815a68d3156dbb049088b1c3cac707e1fe98d6f6c2ef702507b73d36341539f5bf3ec179c459fd
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !c5e4de01206e6d22e1b688ee16cf70722800c67acdf9b33ea0e4c4bb9abf57c29212a4b5037cead2d56b;
+  1 encin
+       !c5e4de01206e6d22e1b688ee16cf70722800c67acdf9b33ea0e4c4bb9abf57c29212a4b5037cead2d56b
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !ef4c23d94151559c878ed774c21e343cb401494331bb90471146f6b9dfa754f29220fe979712c045f279cf;
+  1 encin
+       !ef4c23d94151559c878ed774c21e343cb401494331bb90471146f6b9dfa754f29220fe979712c045f279cf
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !1d5e809ca195882a69bf0a3ceb7cab559cdaa31f10bbfb4c52350d921a6d00cc6432730fe329ba59fb40ee9b;
+  1 encin
+       !1d5e809ca195882a69bf0a3ceb7cab559cdaa31f10bbfb4c52350d921a6d00cc6432730fe329ba59fb40ee9b
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !0ce3268ba95b6a6ff86e8c9f2258ac6464b6c1dd6ada24a3003cfd91f54c930c08a440fae376197d5b021cbe2e;
+  1 encin
+       !0ce3268ba95b6a6ff86e8c9f2258ac6464b6c1dd6ada24a3003cfd91f54c930c08a440fae376197d5b021cbe2e
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !522fb018a04f385658fd390f4c2543454c872ce4ba8d6fed4aee361e896875ca34f393a9a5472464a3bcf01e9853;
+  1 encin
+       !522fb018a04f385658fd390f4c2543454c872ce4ba8d6fed4aee361e896875ca34f393a9a5472464a3bcf01e9853
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !7f25149deb31470c801e70671e08ed3a370342fb15bb99d17b58943c5bf9c1e4eeebf0b8aaa4f30679be9a06bb8923;
+  1 encin
+       !7f25149deb31470c801e70671e08ed3a370342fb15bb99d17b58943c5bf9c1e4eeebf0b8aaa4f30679be9a06bb8923
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !3f4a8395e2d690bb34b1bdcfb36c2c52079d5d66b4fcb6548da98d492da04db0d90500a4ef5c4f9e9feb55ee42154ad9;
+  1 encin
+       !3f4a8395e2d690bb34b1bdcfb36c2c52079d5d66b4fcb6548da98d492da04db0d90500a4ef5c4f9e9feb55ee42154ad9
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !b0f391bda49d977c52f80f8675e84e9b014f7503bb3825c25da1b5169af498e96dc9440cae6b516325efdee35689abbad9;
+  1 encin
+       !b0f391bda49d977c52f80f8675e84e9b014f7503bb3825c25da1b5169af498e96dc9440cae6b516325efdee35689abbad9
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !2812c84a8d5c2fb938292308e711d64bff30ea0c6419e3433eb43be48646fe8b29f3864cb06cd0a3cc70b38cdea4fa6d099d;
+  1 encin
+       !2812c84a8d5c2fb938292308e711d64bff30ea0c6419e3433eb43be48646fe8b29f3864cb06cd0a3cc70b38cdea4fa6d099d
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !9f2d627361db4aab97458fda8cdc703ed90dad1f3d6d524c6dae99729194effd0a1f394c30271d9ddf28c04cca1b391b0bb660;
+  1 encin
+       !9f2d627361db4aab97458fda8cdc703ed90dad1f3d6d524c6dae99729194effd0a1f394c30271d9ddf28c04cca1b391b0bb660
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d90fd5180555d5694fb8d37ae3b14a16522f0975dfedf194d7fb982f78739e5c18ec96683d36e7ea87e95f9b6032af286cfc87b9;
+  1 encin
+       !d90fd5180555d5694fb8d37ae3b14a16522f0975dfedf194d7fb982f78739e5c18ec96683d36e7ea87e95f9b6032af286cfc87b9
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !9f68b8e0fbabaa3ba7e89f01ce4ee4a303a0d5b70a4cf86e248fd0818b197e2a11fa48eacbac441e555f9c334ad08d01826e832362;
+  1 encin
+       !9f68b8e0fbabaa3ba7e89f01ce4ee4a303a0d5b70a4cf86e248fd0818b197e2a11fa48eacbac441e555f9c334ad08d01826e832362
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !5eda3e402fb41bcd18f967777257f3bc57df8a46899110accff5cd918faaa0e6d50879e32dd185978ff379120fccfda4d4a8fdd4af64;
+  1 encin
+       !5eda3e402fb41bcd18f967777257f3bc57df8a46899110accff5cd918faaa0e6d50879e32dd185978ff379120fccfda4d4a8fdd4af64
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !217f1002e3606276bc24ef3441adba9b103aa61c009157ff65c88925d7bc6ffa76860af8ca8e809de0650f560f5fef195697086774a509;
+  1 encin
+       !217f1002e3606276bc24ef3441adba9b103aa61c009157ff65c88925d7bc6ffa76860af8ca8e809de0650f560f5fef195697086774a509
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !1a27632ac0e6dc87d3d9b752e104fae2e97962129829c68d921a01880ddae68eaf1c4cf514cfc1be573acb9704483c8052f263daba49c5c1;
+  1 encin
+       !1a27632ac0e6dc87d3d9b752e104fae2e97962129829c68d921a01880ddae68eaf1c4cf514cfc1be573acb9704483c8052f263daba49c5c1
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !63ee36be37b62458e10f5a1014bec7168ee9925e290f232484aedee47e99c55168eb25fd677e83b2dfd8b74fbea276c17efe71cef67932e7fa;
+  1 encin
+       !63ee36be37b62458e10f5a1014bec7168ee9925e290f232484aedee47e99c55168eb25fd677e83b2dfd8b74fbea276c17efe71cef67932e7fa
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !aa17985c6558bbb41d8bdcfbcdbfedb0c495c2f1795be87902defeb3eaf329cca4210047084f9664f7bba72dfafc3ac277d465ccad618af29090;
+  1 encin
+       !aa17985c6558bbb41d8bdcfbcdbfedb0c495c2f1795be87902defeb3eaf329cca4210047084f9664f7bba72dfafc3ac277d465ccad618af29090
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !cb6c34e2718c39a14da69b7edcb2b9eb341036cf37b74109b920b02426d216bd137387cf79c904233da683b0e2d95497e62b0e5456095de8b7adb1;
+  1 encin
+       !cb6c34e2718c39a14da69b7edcb2b9eb341036cf37b74109b920b02426d216bd137387cf79c904233da683b0e2d95497e62b0e5456095de8b7adb1
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !8920633abb5e90a51bd6da58a196e932647695b2ac3fa83d5a4a156f95c1cd506aab3c655f2b2b11c9434292529a0dfb6c5f1b5db33de9e3019f98aa;
+  1 encin
+       !8920633abb5e90a51bd6da58a196e932647695b2ac3fa83d5a4a156f95c1cd506aab3c655f2b2b11c9434292529a0dfb6c5f1b5db33de9e3019f98aa
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !87df0d097ff3bcb1c875d39bb45138c7bec2b033209dadbb205655b28bcce6353f802b15b81642919f40f7601c36703266fbd07163e7da933d4f2bb9d4;
+  1 encin
+       !87df0d097ff3bcb1c875d39bb45138c7bec2b033209dadbb205655b28bcce6353f802b15b81642919f40f7601c36703266fbd07163e7da933d4f2bb9d4
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !dbbb5a6d715a0504eb85597f07a74c7df63a2f497a45f523ba37a01134993fcbd08b4c3ee88c540c640728864f79b3011fb8f07c20da16453d3d7bc0ad62;
+  1 encin
+       !dbbb5a6d715a0504eb85597f07a74c7df63a2f497a45f523ba37a01134993fcbd08b4c3ee88c540c640728864f79b3011fb8f07c20da16453d3d7bc0ad62
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !bba81430187038b681b56b17148f96ac219a2b474c29ea1d362fb97fa3177c6b3aae2a8b35bdf16d4166f2ff35319e60b0770436ed42fd4c72caf90b2e4047;
+  1 encin
+       !bba81430187038b681b56b17148f96ac219a2b474c29ea1d362fb97fa3177c6b3aae2a8b35bdf16d4166f2ff35319e60b0770436ed42fd4c72caf90b2e4047
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !3b341925aa4a4b5930a4280f5c7ac0d8e7aa047e1a55f5ba7803cc57d3685df6d460fc0ab526c5f776a0ebe100907080f9448cf473668c06a4d8724f5bf34aa5;
+  1 encin
+       !3b341925aa4a4b5930a4280f5c7ac0d8e7aa047e1a55f5ba7803cc57d3685df6d460fc0ab526c5f776a0ebe100907080f9448cf473668c06a4d8724f5bf34aa5
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !572c5a300ac0105071ed9d80d268a7ff9e71df5e4b7bd4734089368500159222711532393589a776e48f3809e03fa0af9895909220d3c1e477605546bf82388742;
+  1 encin
+       !572c5a300ac0105071ed9d80d268a7ff9e71df5e4b7bd4734089368500159222711532393589a776e48f3809e03fa0af9895909220d3c1e477605546bf82388742
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !ad6a3713688e3c8d05bb38ab2115819c2a7a6044e6b532bdb5f5afc199eeeec5530ed464014e82ebbc5ae8cb230438831dd90233cf679e3cd0aa621e82749e00bcbf;
+  1 encin
+       !ad6a3713688e3c8d05bb38ab2115819c2a7a6044e6b532bdb5f5afc199eeeec5530ed464014e82ebbc5ae8cb230438831dd90233cf679e3cd0aa621e82749e00bcbf
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !9ad3396f7edfd8cb0c67701033b2941e32867e9a34288cfbf0b575f4981d96738d7d58f95131652da0947405e6210eca92d4787a9604cedb5113e7820bf2b1f60b338a;
+  1 encin
+       !9ad3396f7edfd8cb0c67701033b2941e32867e9a34288cfbf0b575f4981d96738d7d58f95131652da0947405e6210eca92d4787a9604cedb5113e7820bf2b1f60b338a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !fd7ab45351a569c2215e8645840b3f62cdd02d348d984f848a6aa1971854fe5feaf4253ace1f3a5cd8f9e5caf2ad65572a389f9e09fb09059fa66f37aad370ff6d068a88;
+  1 encin
+       !fd7ab45351a569c2215e8645840b3f62cdd02d348d984f848a6aa1971854fe5feaf4253ace1f3a5cd8f9e5caf2ad65572a389f9e09fb09059fa66f37aad370ff6d068a88
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !38e597770fde2538e66147b517e204e827674c1fc47d51d835fc5f8e9c0de980f6995ab39272791d075ac26d3ef13a4e87ed8491e4610c64ae8102310623cde0ebcc78ba30;
+  1 encin
+       !38e597770fde2538e66147b517e204e827674c1fc47d51d835fc5f8e9c0de980f6995ab39272791d075ac26d3ef13a4e87ed8491e4610c64ae8102310623cde0ebcc78ba30
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !86e78b7d6e0c059f0895d5cb7cfc130523a1738b2d0efb4dacac3a8a10ef768d39f46c18206496cbe0a07ae07bd9ce0e7439f2952ed2ff25ae7d7c9242b7de77515d3a6a0bb3;
+  1 encin
+       !86e78b7d6e0c059f0895d5cb7cfc130523a1738b2d0efb4dacac3a8a10ef768d39f46c18206496cbe0a07ae07bd9ce0e7439f2952ed2ff25ae7d7c9242b7de77515d3a6a0bb3
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !49f36a3342f69941f0c2fbf927b19eb768e8fcbd4dc1050e70146db69e16155964b4da24ec9f5b677ced64cccdb2d50623a2b85bc7f9e2e40f340993ba4d238fe47d4401a345b6;
+  1 encin
+       !49f36a3342f69941f0c2fbf927b19eb768e8fcbd4dc1050e70146db69e16155964b4da24ec9f5b677ced64cccdb2d50623a2b85bc7f9e2e40f340993ba4d238fe47d4401a345b6
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !37256a8d6113eba18fa7e67a2eb2a7d9007099c0f8dad252a7450b2199a9031ff2318140668f623b161877eedd5342e324edb960a9584a680d45ca633adf08b90430c531b132478b;
+  1 encin
+       !37256a8d6113eba18fa7e67a2eb2a7d9007099c0f8dad252a7450b2199a9031ff2318140668f623b161877eedd5342e324edb960a9584a680d45ca633adf08b90430c531b132478b
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !32dca3f2af35c92213a9af62b8c9206742f6c66a3df5148d7b72b967e9ae90d1ba3dc5b5743fce415a57deb51c46f5a133ffe2f83b8acb0f1495f16a702bc644c15377255a75a44039;
+  1 encin
+       !32dca3f2af35c92213a9af62b8c9206742f6c66a3df5148d7b72b967e9ae90d1ba3dc5b5743fce415a57deb51c46f5a133ffe2f83b8acb0f1495f16a702bc644c15377255a75a44039
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !5bc91312769a0b0f43b13813583b56041d376cc7c059f0ba9693287df303d2271f9a81f8467f1b9d2a929ad508b0c08568d2988aa330395660c5a3e10828cb6c06b0a300a3a4789ff756;
+  1 encin
+       !5bc91312769a0b0f43b13813583b56041d376cc7c059f0ba9693287df303d2271f9a81f8467f1b9d2a929ad508b0c08568d2988aa330395660c5a3e10828cb6c06b0a300a3a4789ff756
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !cfcef59edf00d6721c558676ad694b2bc7cdda43564d29da7f58e399a47fe0ad544a2a5c85086a37239b5f2ccabd7472e8a7936d9de9f2252b67d5953b21144f329565b9a4770c8ff4a227;
+  1 encin
+       !cfcef59edf00d6721c558676ad694b2bc7cdda43564d29da7f58e399a47fe0ad544a2a5c85086a37239b5f2ccabd7472e8a7936d9de9f2252b67d5953b21144f329565b9a4770c8ff4a227
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !ef849e65f395e85b4396f22602b1aa9fe3726161e6e1d582ebc585c2e6790fce1c0e5f0b616e35e875b9ed5eccef0c945cc8e68be2374d8859576405979f300a18931d9806e6a8a1e99eb66b;
+  1 encin
+       !ef849e65f395e85b4396f22602b1aa9fe3726161e6e1d582ebc585c2e6790fce1c0e5f0b616e35e875b9ed5eccef0c945cc8e68be2374d8859576405979f300a18931d9806e6a8a1e99eb66b
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !bd964b885a27668afe2e0738502b2ba3f11a63181e5cc26fb55223b320bae68e2c04f069d076b0ff5209f6a5b453c059177af54d28ba29bb1dd62edfb8a77dbad75c5b19397062408dd42ea499;
+  1 encin
+       !bd964b885a27668afe2e0738502b2ba3f11a63181e5cc26fb55223b320bae68e2c04f069d076b0ff5209f6a5b453c059177af54d28ba29bb1dd62edfb8a77dbad75c5b19397062408dd42ea499
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !463042317a2a27ba58aaea708b0bfe915a2a2433f89ef1193e04082f06ad3cd7a5bf8920adfc155fcbb8dc91d0473c28ff17b2202baccf8d7ba72c7d622a95a5eaf3a5de9e7b1a55e3d76c98a745;
+  1 encin
+       !463042317a2a27ba58aaea708b0bfe915a2a2433f89ef1193e04082f06ad3cd7a5bf8920adfc155fcbb8dc91d0473c28ff17b2202baccf8d7ba72c7d622a95a5eaf3a5de9e7b1a55e3d76c98a745
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d91972d9c34b73ca5adfdf28c276903d020d856a39569272483a1d333f7c6b72ae1fc94e00e42d62d0c6f93d5d2ded327e19ff8c07e586e5ed9eebd00c55239446500cea3a4ea22c38f4d0e32d8dec;
+  1 encin
+       !d91972d9c34b73ca5adfdf28c276903d020d856a39569272483a1d333f7c6b72ae1fc94e00e42d62d0c6f93d5d2ded327e19ff8c07e586e5ed9eebd00c55239446500cea3a4ea22c38f4d0e32d8dec
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !4e9afeacf87b309b35d87b488162b56421f477ce928add35f0f31a6824e0a5f5c31f3bf762ac50e6aef8c7d0e98eb01d4950d0dd67261f8c06dee7e034788e686318884285b1ddce6f64dc9aae9c5479;
+  1 encin
+       !4e9afeacf87b309b35d87b488162b56421f477ce928add35f0f31a6824e0a5f5c31f3bf762ac50e6aef8c7d0e98eb01d4950d0dd67261f8c06dee7e034788e686318884285b1ddce6f64dc9aae9c5479
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !e15bb7f491654b63e1ed476efda5b4ed6a86c7c5193cc79da0650b7848e325b392f8321ca8463539f1e1e60777192732c64507162ddcad9fb563b6a34a35af66f6bfad0f7c9d22828c70cb718c40ddfff6;
+  1 encin
+       !e15bb7f491654b63e1ed476efda5b4ed6a86c7c5193cc79da0650b7848e325b392f8321ca8463539f1e1e60777192732c64507162ddcad9fb563b6a34a35af66f6bfad0f7c9d22828c70cb718c40ddfff6
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !12643264e4261ecb9bf62fb88fd3d23fb26e017c16455c888acc5ae6e3bde3689b15659b1ec5942408a90146fbecc56c33136c0e691784ecce9ed0b0181b654f08eea34646ff8db13dc89a2e653164b306be;
+  1 encin
+       !12643264e4261ecb9bf62fb88fd3d23fb26e017c16455c888acc5ae6e3bde3689b15659b1ec5942408a90146fbecc56c33136c0e691784ecce9ed0b0181b654f08eea34646ff8db13dc89a2e653164b306be
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a66947e171591261bc4b41aaf24dc588bcdcbbec645dc4ddfdf3857e799ca30e8e65ac1ff2081dae5f3bc859c3a2f89f644aec1c239f4d498a22be827ef23e48a4771da5f9972cca6378616a8960a5f7c72076;
+  1 encin
+       !a66947e171591261bc4b41aaf24dc588bcdcbbec645dc4ddfdf3857e799ca30e8e65ac1ff2081dae5f3bc859c3a2f89f644aec1c239f4d498a22be827ef23e48a4771da5f9972cca6378616a8960a5f7c72076
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !6f4bacbcfacfd24c2e17bda9e259edb6c02f949f78029a6990b53a5c51e4c103b938556a0f0a67f372c459f04281e1390db713ba8434837de3631e290f0ae637f8423cacad173e7952a021dc0c380ace81f9be89;
+  1 encin
+       !6f4bacbcfacfd24c2e17bda9e259edb6c02f949f78029a6990b53a5c51e4c103b938556a0f0a67f372c459f04281e1390db713ba8434837de3631e290f0ae637f8423cacad173e7952a021dc0c380ace81f9be89
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a4b2340dec02cabb3531362a815759930e2f09b6a466d44afd33959bb753638ce63b8050b365c5e9e00290114c8e8847d909eaca13581366170e6adf31347a9946f7573538d8921239f16b7ea403313a1d0fa218a7;
+  1 encin
+       !a4b2340dec02cabb3531362a815759930e2f09b6a466d44afd33959bb753638ce63b8050b365c5e9e00290114c8e8847d909eaca13581366170e6adf31347a9946f7573538d8921239f16b7ea403313a1d0fa218a7
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !9df08e36f23ceb212520f4461e8df919d09d50e6818fa7571c2139268129ce9310c79e671d4442c330fdc336e3c9ed30f7b0ce90127d2d9d288e4b10078f4f580c0b127791ee53a8a9876f05c64c884fd009dcf9d1ad;
+  1 encin
+       !9df08e36f23ceb212520f4461e8df919d09d50e6818fa7571c2139268129ce9310c79e671d4442c330fdc336e3c9ed30f7b0ce90127d2d9d288e4b10078f4f580c0b127791ee53a8a9876f05c64c884fd009dcf9d1ad
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !dedfd0487fd61a2d9c54495f96a2b5f6c46682d7411c5d9a6e73cb1ab5296353d7c6f0c2fc7b759b233495fdaaf532c2b6c0a1a10252c1f34e1eec567650ccaca1e891c74744d60ca6e132240f56b775fa84618fb9a97e;
+  1 encin
+       !dedfd0487fd61a2d9c54495f96a2b5f6c46682d7411c5d9a6e73cb1ab5296353d7c6f0c2fc7b759b233495fdaaf532c2b6c0a1a10252c1f34e1eec567650ccaca1e891c74744d60ca6e132240f56b775fa84618fb9a97e
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !c6e3226aa258e7c492e730f5c43e2e39f664a7c9a3a6a038a1ccc077d307c2d2a9e21091f222b9dbc66e9fd462e38e59546cf2706dc02301afa34beb1894c334ce44daddd002250eec388f2e4aa819ac4a943c297292626a;
+  1 encin
+       !c6e3226aa258e7c492e730f5c43e2e39f664a7c9a3a6a038a1ccc077d307c2d2a9e21091f222b9dbc66e9fd462e38e59546cf2706dc02301afa34beb1894c334ce44daddd002250eec388f2e4aa819ac4a943c297292626a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !95fa84145d9a295647c55aac1f55e67ad2b91f7048a066c8314cb1c1b1e5a090c8b4862da9e29e270009f73cf2d7d18206d6a732b46bf85a1a4e44e4188a02a7e26716453df7962c5604195c47d7cc2d5850e8387cc641781f;
+  1 encin
+       !95fa84145d9a295647c55aac1f55e67ad2b91f7048a066c8314cb1c1b1e5a090c8b4862da9e29e270009f73cf2d7d18206d6a732b46bf85a1a4e44e4188a02a7e26716453df7962c5604195c47d7cc2d5850e8387cc641781f
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !960bc94f8c562c75ae7a863757295bda90586db18e675f5b8e93e5897a534a369cf60383f636a81af4a0d48bc9fa11a2d5ce6f56efe48d55fe0202d13a24b64b9b3c6ff8bb27e4eccc3d5260fe5f22bb173d57837d078f11a38d;
+  1 encin
+       !960bc94f8c562c75ae7a863757295bda90586db18e675f5b8e93e5897a534a369cf60383f636a81af4a0d48bc9fa11a2d5ce6f56efe48d55fe0202d13a24b64b9b3c6ff8bb27e4eccc3d5260fe5f22bb173d57837d078f11a38d
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !7f437b3d6edcec8b85a6b5a755f0718a63a10d2dbbcf842362c5e32822de6da58deae222ada40efae07b9148b9db97680decfc9c134cacbefbb89a79d685cfd53a33f28a24012d9e6d277350bf5ab87d61d7ef9c322c0c292481e5;
+  1 encin
+       !7f437b3d6edcec8b85a6b5a755f0718a63a10d2dbbcf842362c5e32822de6da58deae222ada40efae07b9148b9db97680decfc9c134cacbefbb89a79d685cfd53a33f28a24012d9e6d277350bf5ab87d61d7ef9c322c0c292481e5
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d7078e462a43b0805ea87ad33842b2418bce472ec2a3e2391701e8bb637518aa71bb1dfe34d9f1cd7c5e17858b6614105460e27342493515b90a16a0beb7964fde79f3828f979bf58487cd505f9b56da1b35b2dbaf4a79fc6eea7104;
+  1 encin
+       !d7078e462a43b0805ea87ad33842b2418bce472ec2a3e2391701e8bb637518aa71bb1dfe34d9f1cd7c5e17858b6614105460e27342493515b90a16a0beb7964fde79f3828f979bf58487cd505f9b56da1b35b2dbaf4a79fc6eea7104
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !2cb0637be1b04080b43b84fb5275269a25cfc672897886337978163d9e73da4be2bab5036eecc91d19b5c8b542bde6ed4514bb22f3e0a6ea0792ea062c60a96dd266d7e470cf4407463286c5cd0397a8ea0011b3bc6f99be9e78631c65;
+  1 encin
+       !2cb0637be1b04080b43b84fb5275269a25cfc672897886337978163d9e73da4be2bab5036eecc91d19b5c8b542bde6ed4514bb22f3e0a6ea0792ea062c60a96dd266d7e470cf4407463286c5cd0397a8ea0011b3bc6f99be9e78631c65
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !5d7358063a8200c777e45389670e232d4188d628376180c5032dd7f2c11d372eaff574f40a1b58ee0e0af0242bf7c24d5d70b2f7b088c052111006fad011ec35456990459fd16b7db74439c5e9e5f53ad39055adc2786ac65b9c2faed47c;
+  1 encin
+       !5d7358063a8200c777e45389670e232d4188d628376180c5032dd7f2c11d372eaff574f40a1b58ee0e0af0242bf7c24d5d70b2f7b088c052111006fad011ec35456990459fd16b7db74439c5e9e5f53ad39055adc2786ac65b9c2faed47c
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !63d76039f6f5138bd4b61472da6cb3493ae0928323e84244dfc79d832e1f3f93919b619a1526969365b2622ad4fa0f5d6d8302ecc2e5dddb15d22978f52b78f21bb79f6fd0c587cf36a6d38671ad474d4bcbdad048f79e34aeeafa936a4fde;
+  1 encin
+       !63d76039f6f5138bd4b61472da6cb3493ae0928323e84244dfc79d832e1f3f93919b619a1526969365b2622ad4fa0f5d6d8302ecc2e5dddb15d22978f52b78f21bb79f6fd0c587cf36a6d38671ad474d4bcbdad048f79e34aeeafa936a4fde
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !4fbb7e5d6f71ab847942a0a0581532679ad7b9a544e42c1a337144c0cc1e779db8172d5fd172e1957d51b3e70fe4fb0930997384efb7f1b17d16b58075f866effa13b13e5104ab8c110c6b9a7c89ff8ad1bebb27a31c22e6e2b49aec205e04b9;
+  1 encin
+       !4fbb7e5d6f71ab847942a0a0581532679ad7b9a544e42c1a337144c0cc1e779db8172d5fd172e1957d51b3e70fe4fb0930997384efb7f1b17d16b58075f866effa13b13e5104ab8c110c6b9a7c89ff8ad1bebb27a31c22e6e2b49aec205e04b9
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !805395735a1a2f301dcfbb22cb101078d52fbf686c8998a6c0b2c5f3dee68976dbeef15796df545f345725e72e4b5d48c4b9102932f5db2574d88b91da298421d3f0be76efaafefde2aee208b8ee2e336d33f55047e42f16f87a164f56f3a5f4a0;
+  1 encin
+       !805395735a1a2f301dcfbb22cb101078d52fbf686c8998a6c0b2c5f3dee68976dbeef15796df545f345725e72e4b5d48c4b9102932f5db2574d88b91da298421d3f0be76efaafefde2aee208b8ee2e336d33f55047e42f16f87a164f56f3a5f4a0
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !8b53700554352c4e5b1f7907cb52e061b48cf3e459f7db87fc14df1935072944bdf81f088624fe2b7bbd2a4a0e15ffd69017d685b06f138d8bd5f5999e9a1a9592a7774de43014b98cecea6e798eec14b2737239028d70aed2a6e6806bca6f691eaa;
+  1 encin
+       !8b53700554352c4e5b1f7907cb52e061b48cf3e459f7db87fc14df1935072944bdf81f088624fe2b7bbd2a4a0e15ffd69017d685b06f138d8bd5f5999e9a1a9592a7774de43014b98cecea6e798eec14b2737239028d70aed2a6e6806bca6f691eaa
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d6d4465829dfd8dc49547f835882ada15a406833a83ca2f6fbb4bb7c0db0d64ed12884e2d32abc05f0a429287746399e9262e2f304c0ad6ae36c1a5cd530fd929dfb0163017ed471b52fe10c1062c22f046a7fd375cd113d26b38b35504a8f32aefb5a;
+  1 encin
+       !d6d4465829dfd8dc49547f835882ada15a406833a83ca2f6fbb4bb7c0db0d64ed12884e2d32abc05f0a429287746399e9262e2f304c0ad6ae36c1a5cd530fd929dfb0163017ed471b52fe10c1062c22f046a7fd375cd113d26b38b35504a8f32aefb5a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a6b1200dd06a7e2a036e971949474aaa154f524b4215ff691706a0e12498ff335792866ae19a48d8e4335bfb6d85d96126ed7b8b581b7f7042a0c7e5d6901ad8f32b7f29c04463f70aa8d5de527d19df1ad6d48a192109afc58eaa22c515ae0a10fde60c;
+  1 encin
+       !a6b1200dd06a7e2a036e971949474aaa154f524b4215ff691706a0e12498ff335792866ae19a48d8e4335bfb6d85d96126ed7b8b581b7f7042a0c7e5d6901ad8f32b7f29c04463f70aa8d5de527d19df1ad6d48a192109afc58eaa22c515ae0a10fde60c
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !27be9a888de11c9b544d1c8e65b9453737464138e4a25b81ba946034da90d61dbd46cb298f1fecf60b6f9c6351ca9193f1db7f340e876a0ff812a0e4064332f91ac684d31b242229fd19d58c0626a6a43a5d9e8641cf6d9fd072f6e2a15ee1ecfa508e54dd;
+  1 encin
+       !27be9a888de11c9b544d1c8e65b9453737464138e4a25b81ba946034da90d61dbd46cb298f1fecf60b6f9c6351ca9193f1db7f340e876a0ff812a0e4064332f91ac684d31b242229fd19d58c0626a6a43a5d9e8641cf6d9fd072f6e2a15ee1ecfa508e54dd
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !578538e9c435f10544df84cadc023d8513a0e51c73a512764d817d4ceab7f7ceb522031a9f94f0d73aae136f1e0d8468f99ba24f40803ead8beed12fac2385235887cb680ce3aa93f882f62061ff7a6411d4fa0ae395172ccceb4b4ee66c821e6d6b2d979bf2;
+  1 encin
+       !578538e9c435f10544df84cadc023d8513a0e51c73a512764d817d4ceab7f7ceb522031a9f94f0d73aae136f1e0d8468f99ba24f40803ead8beed12fac2385235887cb680ce3aa93f882f62061ff7a6411d4fa0ae395172ccceb4b4ee66c821e6d6b2d979bf2
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !2a564c70ffaf1f6c9b4bde77e13b803a92603b1d853555a334d41eb38fca095fee9cf4d47b6dc34226f735c0471bb4ee20ab690a72d4bf6d9e9057fc44082334d00440e6264184e1f2140eea5f795b7b688959f00028d2b2620a1b1e085286ad8d9bce2cdccae7;
+  1 encin
+       !2a564c70ffaf1f6c9b4bde77e13b803a92603b1d853555a334d41eb38fca095fee9cf4d47b6dc34226f735c0471bb4ee20ab690a72d4bf6d9e9057fc44082334d00440e6264184e1f2140eea5f795b7b688959f00028d2b2620a1b1e085286ad8d9bce2cdccae7
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !c3a497949629704ca9b40657503c581ba1cebb20b4f7bd3dd1e71f4a7e3cf67e5609cd60b35b2c6fa9df996d7c4609b90dc8195e55b3fcbc90184538c18b1307e5161f85005b1f41b043577548ca9104912ac7aab9c3993169bd66af217efd52d59c0b2217a9595f;
+  1 encin
+       !c3a497949629704ca9b40657503c581ba1cebb20b4f7bd3dd1e71f4a7e3cf67e5609cd60b35b2c6fa9df996d7c4609b90dc8195e55b3fcbc90184538c18b1307e5161f85005b1f41b043577548ca9104912ac7aab9c3993169bd66af217efd52d59c0b2217a9595f
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !fffa26f4dbd0fa61db80afadaec090c5a55a4c3a1394d7aa64b7d144705c3864c5f296ced71fe5e75f7ad7805abc8113417681659a53dff7b6209db2de89412fb3ad47d6eaf6bf4a6b3b10d8f1272dafa114b2ad3f3f1ff7bd72dcb282027879d42d128f197dbf67b2;
+  1 encin
+       !fffa26f4dbd0fa61db80afadaec090c5a55a4c3a1394d7aa64b7d144705c3864c5f296ced71fe5e75f7ad7805abc8113417681659a53dff7b6209db2de89412fb3ad47d6eaf6bf4a6b3b10d8f1272dafa114b2ad3f3f1ff7bd72dcb282027879d42d128f197dbf67b2
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !86cba2f603aae0dcbfbb3ac33c846452e4d0ba8891b11cee14f2117e97b48fc780e19fddd82131f310138a8d806525714a34b1ad02ce1ed1745572c031c64ddfe16b6f90b45242c4522fa8c050a4e392881de0d7d5c36d11774d5d0e83a6614a80bad1e7968d402b8e64;
+  1 encin
+       !86cba2f603aae0dcbfbb3ac33c846452e4d0ba8891b11cee14f2117e97b48fc780e19fddd82131f310138a8d806525714a34b1ad02ce1ed1745572c031c64ddfe16b6f90b45242c4522fa8c050a4e392881de0d7d5c36d11774d5d0e83a6614a80bad1e7968d402b8e64
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !f0334f7e4a7acda9077684729f1740afdd50931e8814f3308a736faeb67d7c70432d076df140034313bece815f7060eca9911359681b6647feda952b8701ecc1996426eb7951df47bb2550700dac2da57643865ab6a3e59598094f72d646d39103d3251f6b2da9663a2ef5;
+  1 encin
+       !f0334f7e4a7acda9077684729f1740afdd50931e8814f3308a736faeb67d7c70432d076df140034313bece815f7060eca9911359681b6647feda952b8701ecc1996426eb7951df47bb2550700dac2da57643865ab6a3e59598094f72d646d39103d3251f6b2da9663a2ef5
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !087b6a424f1c2a083940fb4d7c9772eb2fbd9dbbba59054f76c3c0687a78dab3bd6124da5960c9b9618c5b5ac00b6899dbc1d91a75a74d4824c6ab5647b3783ca0da89f2cbcbf2274b1e9529fede6439d7ecaf2ffc08e9ad1903af418590810abf8618668b5dad8ee1b21b18;
+  1 encin
+       !087b6a424f1c2a083940fb4d7c9772eb2fbd9dbbba59054f76c3c0687a78dab3bd6124da5960c9b9618c5b5ac00b6899dbc1d91a75a74d4824c6ab5647b3783ca0da89f2cbcbf2274b1e9529fede6439d7ecaf2ffc08e9ad1903af418590810abf8618668b5dad8ee1b21b18
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !75bd4f8ff0d109a3425322a55977727a53cd81735fe798dea31a1e308bbe6d19cdd4fd309b63c5f0f3891374715ad385f57e780974aaf64c73cf3b183d50755e3c753dd7a080da9da863233619defebe67a3059dc125c707f873ec8acc1334d0226240edd4aab093480598ec21;
+  1 encin
+       !75bd4f8ff0d109a3425322a55977727a53cd81735fe798dea31a1e308bbe6d19cdd4fd309b63c5f0f3891374715ad385f57e780974aaf64c73cf3b183d50755e3c753dd7a080da9da863233619defebe67a3059dc125c707f873ec8acc1334d0226240edd4aab093480598ec21
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !5e86f82dc7b83b01a9d4362e7ad0871c53e8f8c96ff7b78746d0162257fa81771bb8bae3a6f34bccc42951fbc1c06fb1dd518ac733ecc253357c93892946944f815cba634f1f85178f744ac95ce478b2661a740b45a422bf6cec31cfe20d78a3ee978dd1d7e79910a5903bd1c782;
+  1 encin
+       !5e86f82dc7b83b01a9d4362e7ad0871c53e8f8c96ff7b78746d0162257fa81771bb8bae3a6f34bccc42951fbc1c06fb1dd518ac733ecc253357c93892946944f815cba634f1f85178f744ac95ce478b2661a740b45a422bf6cec31cfe20d78a3ee978dd1d7e79910a5903bd1c782
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !3d906019d05fe688c8b215aa6a765e4ca03745195109c46c53f84b3b54dc8e4c2ded2a13de8e8678d94b8dcf9a936783ce726344199e73f82f182459e4bd88fdb6047e7e3bd16d2ff501f62e991e0bda2026be06e661313c532e84396cf6f56afd2ca9f3cc79cf1cc3e52a1d893eb6;
+  1 encin
+       !3d906019d05fe688c8b215aa6a765e4ca03745195109c46c53f84b3b54dc8e4c2ded2a13de8e8678d94b8dcf9a936783ce726344199e73f82f182459e4bd88fdb6047e7e3bd16d2ff501f62e991e0bda2026be06e661313c532e84396cf6f56afd2ca9f3cc79cf1cc3e52a1d893eb6
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !6a65bc3cf9bcad98c0a733fafd599fbc9553584eba486ef3fb197ed4a12ff5da33d7b4bddb9ad153b890f870801b45c1e0115ffa220c24d8d60c5529c81a9141808a4d8ab91f2f93356247bb1c951aaf7d23328562150265130f607d54289500020099a247034a75ee58fd73af740e16;
+  1 encin
+       !6a65bc3cf9bcad98c0a733fafd599fbc9553584eba486ef3fb197ed4a12ff5da33d7b4bddb9ad153b890f870801b45c1e0115ffa220c24d8d60c5529c81a9141808a4d8ab91f2f93356247bb1c951aaf7d23328562150265130f607d54289500020099a247034a75ee58fd73af740e16
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a0648c74ee2872b422b64290354ce4f38de2fb62fbbb99ae2a46a7233b9f4a9ea08ec253aa377042855099edca949834b90091dcf7d946baa658f0ef2906c906f70c151a19a1a73d9ae83176364533cd9b4de078329dd07ea96cdf2f6264446d5b9386a3d4a3f49d6db97e05ec518950aa;
+  1 encin
+       !a0648c74ee2872b422b64290354ce4f38de2fb62fbbb99ae2a46a7233b9f4a9ea08ec253aa377042855099edca949834b90091dcf7d946baa658f0ef2906c906f70c151a19a1a73d9ae83176364533cd9b4de078329dd07ea96cdf2f6264446d5b9386a3d4a3f49d6db97e05ec518950aa
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !2c887d8300686768cac139577901999d57371ff152ec09e5cb979cf360a9704669b79a9e9252c6c90ba9f9e44bd5e77224081c3e3aebdc5d8f59a10794dfa167f8c2f6a25b3386eae4dd580ad86ebaa1df9bf5169f09698277f52f6877d3724c05bc07d3975865bc61b19a13174c2a750c96;
+  1 encin
+       !2c887d8300686768cac139577901999d57371ff152ec09e5cb979cf360a9704669b79a9e9252c6c90ba9f9e44bd5e77224081c3e3aebdc5d8f59a10794dfa167f8c2f6a25b3386eae4dd580ad86ebaa1df9bf5169f09698277f52f6877d3724c05bc07d3975865bc61b19a13174c2a750c96
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !63c6df75230c4afab23cf2dc6f92138b9fde21cf5cd48865f3cb44642fb0f36a8212d07e4374351fee51566be0a0a70bfe1fb03b87cac717a3c667fc9190ba053a471428b2cc1329f49976a026150952bd8438220edfc60d9c3cd07dcbb6bff3ff0ea3353afeea85ab194db96161146f5e9e89;
+  1 encin
+       !63c6df75230c4afab23cf2dc6f92138b9fde21cf5cd48865f3cb44642fb0f36a8212d07e4374351fee51566be0a0a70bfe1fb03b87cac717a3c667fc9190ba053a471428b2cc1329f49976a026150952bd8438220edfc60d9c3cd07dcbb6bff3ff0ea3353afeea85ab194db96161146f5e9e89
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !f980b6dd51a09fe70b93e752ec22a082e7a8fc5ffacaa9d53dd68c5688400355e52588087e91e4ee2185d54f998ab1fc1d263bdd5ae9cc1223414113bbc31f4e5eeeeab7764c60319b55beae12c1e02f92d5aa938c10b706027066cebde0269b572ba23f6e4972b127905faad5eaef8c5bc12115;
+  1 encin
+       !f980b6dd51a09fe70b93e752ec22a082e7a8fc5ffacaa9d53dd68c5688400355e52588087e91e4ee2185d54f998ab1fc1d263bdd5ae9cc1223414113bbc31f4e5eeeeab7764c60319b55beae12c1e02f92d5aa938c10b706027066cebde0269b572ba23f6e4972b127905faad5eaef8c5bc12115
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !dee0d26ce7b9d85fefcef72ed7e3c995c06f60de485ce04b0531279d22eb6553bb58fc807e119f98e71220ca51f4242b8ae59228afa4756fd98e3c1f12efea0cd44f1bcdc6ccd6e77e0fff4375e65f0dd1c1a0c79aa17f3c79b09cde2ec364ee122e37a54bd1fdf4d97fa50a40b7eef07f781f9d1a;
+  1 encin
+       !dee0d26ce7b9d85fefcef72ed7e3c995c06f60de485ce04b0531279d22eb6553bb58fc807e119f98e71220ca51f4242b8ae59228afa4756fd98e3c1f12efea0cd44f1bcdc6ccd6e77e0fff4375e65f0dd1c1a0c79aa17f3c79b09cde2ec364ee122e37a54bd1fdf4d97fa50a40b7eef07f781f9d1a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !5fe15102695168abca87356479efd9a734ccbd1ebcc2742f470aec0931a24379d225c9f5617b02415e3db7fd8e741d359a4059830dd9c83c2152370b5b89fae3d6c89ec9445bd339c689741bc08c1ba7392ee4f9cb5d06612dc766919144add4a7659222c8bc3c63b08353e7f24ecfc4b459a27e9bf4;
+  1 encin
+       !5fe15102695168abca87356479efd9a734ccbd1ebcc2742f470aec0931a24379d225c9f5617b02415e3db7fd8e741d359a4059830dd9c83c2152370b5b89fae3d6c89ec9445bd339c689741bc08c1ba7392ee4f9cb5d06612dc766919144add4a7659222c8bc3c63b08353e7f24ecfc4b459a27e9bf4
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !4a9ab68772ead5aaa6c1e40e7ce22e1647070b5185a4260cebddb7c7bbf2ab88bf8c81e361cccfac2e96e971cef18e0f16607f04f01fc5131542f3de46ab6192bfcce55fdd37c32fe86423222b6dfa1c443b6e4a1b7039fe07e80f390f6bc99ca5ced16148c39f25f0644ed58fad44bc44fe6fe8ff87c9;
+  1 encin
+       !4a9ab68772ead5aaa6c1e40e7ce22e1647070b5185a4260cebddb7c7bbf2ab88bf8c81e361cccfac2e96e971cef18e0f16607f04f01fc5131542f3de46ab6192bfcce55fdd37c32fe86423222b6dfa1c443b6e4a1b7039fe07e80f390f6bc99ca5ced16148c39f25f0644ed58fad44bc44fe6fe8ff87c9
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !eaf08edd13b54042c0b2513d87f8fbe40eeeaba7395f20bfba998f23d19a88753f86588a21d1f47ad76733423bdf27ac253040608d12d7a931495a0572dd025230dc8992423e87639b5948cf792bf205bfc010d6e1edeb7a0244af19e38fe696771a1ba7f50fa7df7e20e2d514b73fa4db9668749f53ae91;
+  1 encin
+       !eaf08edd13b54042c0b2513d87f8fbe40eeeaba7395f20bfba998f23d19a88753f86588a21d1f47ad76733423bdf27ac253040608d12d7a931495a0572dd025230dc8992423e87639b5948cf792bf205bfc010d6e1edeb7a0244af19e38fe696771a1ba7f50fa7df7e20e2d514b73fa4db9668749f53ae91
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !b741f0d5ddf4fd12c35b263c809648be6696576310c2982b2dd2ebf259df191cdbc4792fde17eec1ab0d470c9814ef62248ea816f8cb3a0be431179c0d44762f94014aaefb3227d70eda04f2ab460dd59bff4adced323d673b91d2128fbaed95fc9ccbd68195bf3dc50f382ce3987f7bef692647e8dfbee4d5;
+  1 encin
+       !b741f0d5ddf4fd12c35b263c809648be6696576310c2982b2dd2ebf259df191cdbc4792fde17eec1ab0d470c9814ef62248ea816f8cb3a0be431179c0d44762f94014aaefb3227d70eda04f2ab460dd59bff4adced323d673b91d2128fbaed95fc9ccbd68195bf3dc50f382ce3987f7bef692647e8dfbee4d5
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !03b0931db86e38f25ad44cfaafeedfed18b06a97f92eff0d64011cb0733b4e514e59531569fd97b4309801b8671405068159bc1a5ffaf2cb177235ed52fa285d24309f7ab065eee2b69bb7c2c7c20211829883bb2250bce0d233c203748661cdbf9007dacf93746176e2bf4f67fd90a01bfc55723e783729b887;
+  1 encin
+       !03b0931db86e38f25ad44cfaafeedfed18b06a97f92eff0d64011cb0733b4e514e59531569fd97b4309801b8671405068159bc1a5ffaf2cb177235ed52fa285d24309f7ab065eee2b69bb7c2c7c20211829883bb2250bce0d233c203748661cdbf9007dacf93746176e2bf4f67fd90a01bfc55723e783729b887
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !989906fd9f5f75949d1a37e67787ee1ef6e9a363200079e05becff5d47ad954753e5573aa490a193560dc32bdfeacad0e1ba94762967d31a87c3a5ba4ca89eb00db5f3ed51c5f6aa987bd68b8d7d6faccb4c3ee81a9d2d2f1ad36af8717765c076632448b4dea1c7e9cff8ebcb4a56cf6232aff243cdc94d5e3cb1;
+  1 encin
+       !989906fd9f5f75949d1a37e67787ee1ef6e9a363200079e05becff5d47ad954753e5573aa490a193560dc32bdfeacad0e1ba94762967d31a87c3a5ba4ca89eb00db5f3ed51c5f6aa987bd68b8d7d6faccb4c3ee81a9d2d2f1ad36af8717765c076632448b4dea1c7e9cff8ebcb4a56cf6232aff243cdc94d5e3cb1
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !4a372dac2c861d6198141bbe14c36939fe30c5766fe2aa1c6c97c236e131f0fc77255636552d9a201c598c69d72482886db1ce216c50cfb870e622ad866518f6ce0d69d4bb6af0d53105a024ac3af6a684ce751dd2ce22b4aedf869f4c4ac8153e4c6c11188a1a7efa9ad48f0c219447bb0b2db7863495224cfb92f9;
+  1 encin
+       !4a372dac2c861d6198141bbe14c36939fe30c5766fe2aa1c6c97c236e131f0fc77255636552d9a201c598c69d72482886db1ce216c50cfb870e622ad866518f6ce0d69d4bb6af0d53105a024ac3af6a684ce751dd2ce22b4aedf869f4c4ac8153e4c6c11188a1a7efa9ad48f0c219447bb0b2db7863495224cfb92f9
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !49cdb3599b611bcb646931ea2cd3c1060ee0a9929da09814766ec5a2b21c40b45f538d495a5222dedcc132cbb1ec9fac86413e51968c986a461376b32d9ef7e46d6eb78d30c1de2cdbe962406299f415b71bf161b2b281f86bc78d4c607d1998ddd54605a5c320da45733d83f6025cbfd8040f238be9e599d2c75b8966;
+  1 encin
+       !49cdb3599b611bcb646931ea2cd3c1060ee0a9929da09814766ec5a2b21c40b45f538d495a5222dedcc132cbb1ec9fac86413e51968c986a461376b32d9ef7e46d6eb78d30c1de2cdbe962406299f415b71bf161b2b281f86bc78d4c607d1998ddd54605a5c320da45733d83f6025cbfd8040f238be9e599d2c75b8966
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !ea38bad18ac44276bd7a3f160166311eb27e5290267e2f9532d3780bc31d47ff1a3b14f4dc4ca025f5f62735205fdbe6c26f96cbcdef43b6a00f22ea30cf76068decad0d9bea2c943f112cc0233d862f9f729e1a8563c3aaa8d9dd7ea6060c4d080be791865b38d54517e01a7bc06b8172347dfcfa58a4af0cb726ec0615;
+  1 encin
+       !ea38bad18ac44276bd7a3f160166311eb27e5290267e2f9532d3780bc31d47ff1a3b14f4dc4ca025f5f62735205fdbe6c26f96cbcdef43b6a00f22ea30cf76068decad0d9bea2c943f112cc0233d862f9f729e1a8563c3aaa8d9dd7ea6060c4d080be791865b38d54517e01a7bc06b8172347dfcfa58a4af0cb726ec0615
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !cb799d41fdeca9a8edf3be872ef7ae761b03f63503d4d9e7296c386dbc4032396844e9949a77dff11174a74d0f0d90a6cd54f8f32188756639949478dc73a386d79dff19238a2ea6d5595e5015ac55f5bba55390a8d1c0eeaf908068167c7b86b4633b632cb0e271c35506baad8f79a63e04face56d157101c7bfa2f6aaadb;
+  1 encin
+       !cb799d41fdeca9a8edf3be872ef7ae761b03f63503d4d9e7296c386dbc4032396844e9949a77dff11174a74d0f0d90a6cd54f8f32188756639949478dc73a386d79dff19238a2ea6d5595e5015ac55f5bba55390a8d1c0eeaf908068167c7b86b4633b632cb0e271c35506baad8f79a63e04face56d157101c7bfa2f6aaadb
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !1730946f2b7514c84c2bb6ac6e268d42627d50a468c466a6fb8e7b6a27062df5b9bd67c858bd2b174a8ae43e506177fceaafde77296c5febf48fb91b6fbd38bd476bc5f4b1b246516ac7cb536887dad9e7a058ccff5733c443fdcb4ec7fccec491f204e07e801ada05af9df8b267ec7669b1d213d74fdb4c996ac67ff7b14d79;
+  1 encin
+       !1730946f2b7514c84c2bb6ac6e268d42627d50a468c466a6fb8e7b6a27062df5b9bd67c858bd2b174a8ae43e506177fceaafde77296c5febf48fb91b6fbd38bd476bc5f4b1b246516ac7cb536887dad9e7a058ccff5733c443fdcb4ec7fccec491f204e07e801ada05af9df8b267ec7669b1d213d74fdb4c996ac67ff7b14d79
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !9e8b99a3af0f1e8b6316b1c0efcd812160803e80b9652d059bf2f85ec2e91581c6128e2d2afbfae23accfda5acfab80baad8466e97f3f73899ccbd9de6048dd85aeb5ab78a39fdca62af5a4c556feddbf97a50f7fc376c3a41c46bb599d6a44334dcb17a03fa93262aae1041322dcc17d7e08a655985fe6574db79f951f336bda7;
+  1 encin
+       !9e8b99a3af0f1e8b6316b1c0efcd812160803e80b9652d059bf2f85ec2e91581c6128e2d2afbfae23accfda5acfab80baad8466e97f3f73899ccbd9de6048dd85aeb5ab78a39fdca62af5a4c556feddbf97a50f7fc376c3a41c46bb599d6a44334dcb17a03fa93262aae1041322dcc17d7e08a655985fe6574db79f951f336bda7
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a21883c5d37094d553045637551fd4993ee73471e6fd2152111a3c5336622130199e981dce4ec30a8f151d6b1b3b070a343196c8624146f6247bc79bee489e883d2a6e720995b684c5b30f9aa734b6f1891ccaf05f464dcd9bcfbdcbac372e28845de339f8920716ba6efe914c166817c74f2df2e5331997426548a92be3a2dd5679;
+  1 encin
+       !a21883c5d37094d553045637551fd4993ee73471e6fd2152111a3c5336622130199e981dce4ec30a8f151d6b1b3b070a343196c8624146f6247bc79bee489e883d2a6e720995b684c5b30f9aa734b6f1891ccaf05f464dcd9bcfbdcbac372e28845de339f8920716ba6efe914c166817c74f2df2e5331997426548a92be3a2dd5679
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !8d5a18f2b27043ec61d95990211be55d3901870f9ef30b2403e59bd09b30617f5e5043089ebd916a3f13591d72d7394ad4b2dc66ebebb7c1c5e6329525d8bf1c2cfe5f657363c180bd37f39f8e0fe67c16918d02a7dbc30d4436441d8383b7b50e8ce8d414506c20917b29d4b0f249fefef11cac8af387d27d0d45149860685143cbc0;
+  1 encin
+       !8d5a18f2b27043ec61d95990211be55d3901870f9ef30b2403e59bd09b30617f5e5043089ebd916a3f13591d72d7394ad4b2dc66ebebb7c1c5e6329525d8bf1c2cfe5f657363c180bd37f39f8e0fe67c16918d02a7dbc30d4436441d8383b7b50e8ce8d414506c20917b29d4b0f249fefef11cac8af387d27d0d45149860685143cbc0
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !c512f89967db78a64284f9e8975327fe142cce566d2adad60dc3ec81d462877fea4fe06a32428b9a148dd9a8d4a661be6c56582e30a65d6e3289c08dcadcc717e4e9f785a7a887d652174f586286d9d2104171f938b084ed61dc5df1158c328e361c75b9901807dd2563d1be9c1507b4dbd1bafefb837ff8a2538c131425a27d29d59938;
+  1 encin
+       !c512f89967db78a64284f9e8975327fe142cce566d2adad60dc3ec81d462877fea4fe06a32428b9a148dd9a8d4a661be6c56582e30a65d6e3289c08dcadcc717e4e9f785a7a887d652174f586286d9d2104171f938b084ed61dc5df1158c328e361c75b9901807dd2563d1be9c1507b4dbd1bafefb837ff8a2538c131425a27d29d59938
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !467c5e146445701168ce494b307cd12c729189b1c1176ff06ef8f93ff1b0690f311e284b68380395224cdea9e382a69b8d5fe027a5f79ccab42f523a1749362873aed701acbede76da4614cc538310027a37bcb6518e19c8ce3ab8b7c98f486217e3263887566cc8be7447cd5b14bfd27ed2336496ee9b4542603456d0511bbcebe3d1a2f8;
+  1 encin
+       !467c5e146445701168ce494b307cd12c729189b1c1176ff06ef8f93ff1b0690f311e284b68380395224cdea9e382a69b8d5fe027a5f79ccab42f523a1749362873aed701acbede76da4614cc538310027a37bcb6518e19c8ce3ab8b7c98f486217e3263887566cc8be7447cd5b14bfd27ed2336496ee9b4542603456d0511bbcebe3d1a2f8
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !0fb913af1109a3ba37d5085ea5540b1ee92c5aed16f00fad7aad185e67c6071be4d496df368c7bf8e4acabd275754a6f1e8f59dd5c590dd4f43e86f4f150dd5a6f2d8f79852ab4df1274b660720409993effaff1da293fdf035acee7a60ad46a5f8d308aeeb97124f2429c4f19354faa8a2ef7dbfc7a44685601dbff5561dcd7d69e8d6b9e87;
+  1 encin
+       !0fb913af1109a3ba37d5085ea5540b1ee92c5aed16f00fad7aad185e67c6071be4d496df368c7bf8e4acabd275754a6f1e8f59dd5c590dd4f43e86f4f150dd5a6f2d8f79852ab4df1274b660720409993effaff1da293fdf035acee7a60ad46a5f8d308aeeb97124f2429c4f19354faa8a2ef7dbfc7a44685601dbff5561dcd7d69e8d6b9e87
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !26d41ee387956931db354efe7fb8bc6a517e5418806986d8f758934f40f61d71788296cd7e3fee5f8c2ccd32375527c39c4de49b53c6c71726145fb173bd091230842edfc5f54c9ec2012c06decd95881d9da61d4c93ba6c7fff662073969fd74eb12a3b8ed255b01fc2c138f45033c03a77b127d0a17d4a00c02599fea636e1afbb257b9afb92;
+  1 encin
+       !26d41ee387956931db354efe7fb8bc6a517e5418806986d8f758934f40f61d71788296cd7e3fee5f8c2ccd32375527c39c4de49b53c6c71726145fb173bd091230842edfc5f54c9ec2012c06decd95881d9da61d4c93ba6c7fff662073969fd74eb12a3b8ed255b01fc2c138f45033c03a77b127d0a17d4a00c02599fea636e1afbb257b9afb92
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !67f8a44aaff6a6854b93913b91acf1ff4279196ead3237bb06ec6186ed2ce0d0b89adaed048d0fd098e2192fd3ea2a61657f05d488a9ed67078d93ae8b90d45b2498a368cdb1da1c90aca04f6d1109bbbe219b6dc9f8a9c74aaf30ad127d50286409d9ee829a20c25b365bef91582c0261d6b34adbe0cdc638b90c17edf99f8b1464986012542dba;
+  1 encin
+       !67f8a44aaff6a6854b93913b91acf1ff4279196ead3237bb06ec6186ed2ce0d0b89adaed048d0fd098e2192fd3ea2a61657f05d488a9ed67078d93ae8b90d45b2498a368cdb1da1c90aca04f6d1109bbbe219b6dc9f8a9c74aaf30ad127d50286409d9ee829a20c25b365bef91582c0261d6b34adbe0cdc638b90c17edf99f8b1464986012542dba
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !cdda0d5dc2e67d6068e0489111a8a7b04beb7acd74f69fc6ee867ad5da4fdf93267b2085186a8c243b61bf93b9ecaa3f4288597cf18ced9dab52efa3cb52b01211527a90872755862c765d659baa936a5eb74d2c5de82206ae1b170183ea10f49de534928879d813b4f450c3c1b8f5e80de7282b010a3c1d20214b0127fd0ab1fa34818275cc959540;
+  1 encin
+       !cdda0d5dc2e67d6068e0489111a8a7b04beb7acd74f69fc6ee867ad5da4fdf93267b2085186a8c243b61bf93b9ecaa3f4288597cf18ced9dab52efa3cb52b01211527a90872755862c765d659baa936a5eb74d2c5de82206ae1b170183ea10f49de534928879d813b4f450c3c1b8f5e80de7282b010a3c1d20214b0127fd0ab1fa34818275cc959540
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !ff7cf9c75c7604cbb436d1b77819520b3fba90750073a66ed6df1533aa012bae59b17d5dc60832695394f3f9893857169ae23d961c96c6ec35054e55d600a62c9f0313780a7b8b3e6c55b3d268fdc25bcd379971fd82639c375ff0ca93b116bb992b137758c8ccfb1379406d50f64d58095b90076561706ab302db92d47c4ebf54ca9b2ba5594c1cd67f;
+  1 encin
+       !ff7cf9c75c7604cbb436d1b77819520b3fba90750073a66ed6df1533aa012bae59b17d5dc60832695394f3f9893857169ae23d961c96c6ec35054e55d600a62c9f0313780a7b8b3e6c55b3d268fdc25bcd379971fd82639c375ff0ca93b116bb992b137758c8ccfb1379406d50f64d58095b90076561706ab302db92d47c4ebf54ca9b2ba5594c1cd67f
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !57b2bfaec0af01a35c50b5b0d8d6dd4ca5d9cb51ecaa601e31f845cc6772b6522112c913ef6f3b875ff364f91d60b340b687e1d54163e05086b69518c6697faa995a4cbbe7b467ec7d49b6cdd074d3458de3bf45eb468b3b6e4949725bb6d67e2e69788d35bb36817791f2d55b1289281f614d48f6d74f20856be25523e285df3febfb775289b4491635b2;
+  1 encin
+       !57b2bfaec0af01a35c50b5b0d8d6dd4ca5d9cb51ecaa601e31f845cc6772b6522112c913ef6f3b875ff364f91d60b340b687e1d54163e05086b69518c6697faa995a4cbbe7b467ec7d49b6cdd074d3458de3bf45eb468b3b6e4949725bb6d67e2e69788d35bb36817791f2d55b1289281f614d48f6d74f20856be25523e285df3febfb775289b4491635b2
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !55af1f6bbdc6cb39b760e9f2b0bb10078cac8178deed373a5593730cf99c67356a352dcd3c6cbbd046cbb0c4f1e0c4658e982f4052a908c95e53d18704eb50d1d53a4ac371a96eecbca72a017ad9e91a34842233e48450b191ac8bd18d0e55d8ce893f690d50ebba44c7c85f1cce6e99b6eb7f626ba32729c4871222e893592df5904ac3a102d139ebb8ad6a;
+  1 encin
+       !55af1f6bbdc6cb39b760e9f2b0bb10078cac8178deed373a5593730cf99c67356a352dcd3c6cbbd046cbb0c4f1e0c4658e982f4052a908c95e53d18704eb50d1d53a4ac371a96eecbca72a017ad9e91a34842233e48450b191ac8bd18d0e55d8ce893f690d50ebba44c7c85f1cce6e99b6eb7f626ba32729c4871222e893592df5904ac3a102d139ebb8ad6a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !817f9d166e2324ca704127c3235a0201526f6cae694aa8a8a8cfab625d28f75a639c816562ac0f520b8ce5f075be45cd939a1978224d9b7a5cc703a623cf7c1c255825f246b6ff608bd90a859d6e4119b27e48018c8acf6fb82ea093d2c926a3f4a5f0d7f91953c0610858d063f2d28fb13c12d8f02a0c078707408c323ba8be17ce2a8df6862095adce0ead83;
+  1 encin
+       !817f9d166e2324ca704127c3235a0201526f6cae694aa8a8a8cfab625d28f75a639c816562ac0f520b8ce5f075be45cd939a1978224d9b7a5cc703a623cf7c1c255825f246b6ff608bd90a859d6e4119b27e48018c8acf6fb82ea093d2c926a3f4a5f0d7f91953c0610858d063f2d28fb13c12d8f02a0c078707408c323ba8be17ce2a8df6862095adce0ead83
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !7298ea337f59a018915318a4d0db523f1df3d0be7c3da956e040361f40e48eb7e0bc2dfa76e840cd47e3d62c6345be06a0b05cbb17633c1892f0c4d5d9929bde4ddf116b6114e10ed97c625702f60dfcf2c9bbf89f4602490c35f9098d6f345fcd2c4988ff1a67033ed4eea7949432ff1e01ac43a0f134d221e6aea1b3c8d48c137a28da8b1f27b87fa698732cb0;
+  1 encin
+       !7298ea337f59a018915318a4d0db523f1df3d0be7c3da956e040361f40e48eb7e0bc2dfa76e840cd47e3d62c6345be06a0b05cbb17633c1892f0c4d5d9929bde4ddf116b6114e10ed97c625702f60dfcf2c9bbf89f4602490c35f9098d6f345fcd2c4988ff1a67033ed4eea7949432ff1e01ac43a0f134d221e6aea1b3c8d48c137a28da8b1f27b87fa698732cb0
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !7cdf8bf7ca3c72b62bbf780fb64e525aed5941c5b427df53214e667aad02e66214082074bf5289412f065e28b5519d9e72990e7da6b821939b7003ed9d14be5d8e40e5b108163143032cf3ec8de24223ba39a7b31d3758a26992c0282bc8750768b9d7dc4873ea35a09dbfe21e87bf8dac2864cd7a6f9f233945a6d90a34b82c75b9cf3041d026a56f67c00b78dcc4;
+  1 encin
+       !7cdf8bf7ca3c72b62bbf780fb64e525aed5941c5b427df53214e667aad02e66214082074bf5289412f065e28b5519d9e72990e7da6b821939b7003ed9d14be5d8e40e5b108163143032cf3ec8de24223ba39a7b31d3758a26992c0282bc8750768b9d7dc4873ea35a09dbfe21e87bf8dac2864cd7a6f9f233945a6d90a34b82c75b9cf3041d026a56f67c00b78dcc4
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !bfd7ed1fb1d60722deed37eee14269fa78711a348e141f74f1dc099369785bdf7ca057e1a9b8b8c312d780c714d0573a2d3f470c095f860eaa12f69178ea50e69c0c328cdcbd5e224c303f58ed28063c2bfb6ee0744de16664d3fbf3a865e5a4d24535d2ba07033f20ffdb85b3c7877aff32b8d221108b3ae453df3d83741b4d78b55b2541eae332e38083d56e9bacf9;
+  1 encin
+       !bfd7ed1fb1d60722deed37eee14269fa78711a348e141f74f1dc099369785bdf7ca057e1a9b8b8c312d780c714d0573a2d3f470c095f860eaa12f69178ea50e69c0c328cdcbd5e224c303f58ed28063c2bfb6ee0744de16664d3fbf3a865e5a4d24535d2ba07033f20ffdb85b3c7877aff32b8d221108b3ae453df3d83741b4d78b55b2541eae332e38083d56e9bacf9
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !e7c484b50b855361c92113e093248397d429ad660b492d7cee162225be9be63c73a97d70202e511222075a6d456af4a1db02e9db54a120cc34088813c8c9ff466382147470a64c9376508d5198051dc5956a1ecca55da430a897922cf9feb2a02dd1230b015bc7693ba20a159dc2bb33df14b9a07d84815b51aa87b2a95fd3c6e641de935958e8bd39c0d4fb9f1bd8e090;
+  1 encin
+       !e7c484b50b855361c92113e093248397d429ad660b492d7cee162225be9be63c73a97d70202e511222075a6d456af4a1db02e9db54a120cc34088813c8c9ff466382147470a64c9376508d5198051dc5956a1ecca55da430a897922cf9feb2a02dd1230b015bc7693ba20a159dc2bb33df14b9a07d84815b51aa87b2a95fd3c6e641de935958e8bd39c0d4fb9f1bd8e090
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d074a37adcf01819fc6a0d9147bf55b3c4ef2f14f900ae750241648c134428dad5138117bc92b9ea4c0bb8838c8e578e909effb483d63b59500bce9e6ead001d8d21aef7cad4bf92ce2b7ae99885ea1444d065fc58026682d4e7d6f25fbb4952e2ce5fc50796a0332fabaa3a7cc3521d3a1d3994ef0cc3324ee0c04c928b3054326fff80bdbbe2c0ca251cefa84ff86e3e6e;
+  1 encin
+       !d074a37adcf01819fc6a0d9147bf55b3c4ef2f14f900ae750241648c134428dad5138117bc92b9ea4c0bb8838c8e578e909effb483d63b59500bce9e6ead001d8d21aef7cad4bf92ce2b7ae99885ea1444d065fc58026682d4e7d6f25fbb4952e2ce5fc50796a0332fabaa3a7cc3521d3a1d3994ef0cc3324ee0c04c928b3054326fff80bdbbe2c0ca251cefa84ff86e3e6e
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !990e4ae01929cd998c3568ec423b4bd62a07a635453bb8a75c68c7db0ed42661294b8f7b0ba120885bcde030fadd1f8ef4f72650789da415ca81dc28b215d981909d6f67d3c03742028538a679b3f20ddae154b4d050748da40c93c2a6dd38287838118245ce3005f653aaf3af86e4b52159fd43984bca37f5c16b880c64154330134903dae9cddc6606f67acb5efb98eaf9ee;
+  1 encin
+       !990e4ae01929cd998c3568ec423b4bd62a07a635453bb8a75c68c7db0ed42661294b8f7b0ba120885bcde030fadd1f8ef4f72650789da415ca81dc28b215d981909d6f67d3c03742028538a679b3f20ddae154b4d050748da40c93c2a6dd38287838118245ce3005f653aaf3af86e4b52159fd43984bca37f5c16b880c64154330134903dae9cddc6606f67acb5efb98eaf9ee
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !ad097be738311d2e3d2a39e3ee1b4bfe04bec56469d598b4b4e8834fc676df55cc9c84dbfaef232c595d15a89aabd037e9dc42227222e73aba94237615e296bc0f74e5cb363f16ddfb688c03fdfa10de2a24b5363241830d90dabfcd2fcd5759728ed2a8e65698780dc4851868791d86984ea5950f84e23e02ead6128cd93e68139b87f4ff50b53aef621ffc0bb6f8c645081765;
+  1 encin
+       !ad097be738311d2e3d2a39e3ee1b4bfe04bec56469d598b4b4e8834fc676df55cc9c84dbfaef232c595d15a89aabd037e9dc42227222e73aba94237615e296bc0f74e5cb363f16ddfb688c03fdfa10de2a24b5363241830d90dabfcd2fcd5759728ed2a8e65698780dc4851868791d86984ea5950f84e23e02ead6128cd93e68139b87f4ff50b53aef621ffc0bb6f8c645081765
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a292c951bf6914dc7215b8cc0643713e537adad6c5277ae3ffbab54cd6b6ba66b26e09b793af29e1b076e4c42a604385a1cb10e7e62b1b80a5b629305d343ef63a259c2b80e08b3c9f81dd5f9d81cce72ebd43b819efb12538f6a4d2ecfd20cb792106d4f830f47e17b1868514ce494c5394701bc4f0c7e6f151631953527b78b21f5127e128984d2b97cdb8e36e8c38fb084f9e86;
+  1 encin
+       !a292c951bf6914dc7215b8cc0643713e537adad6c5277ae3ffbab54cd6b6ba66b26e09b793af29e1b076e4c42a604385a1cb10e7e62b1b80a5b629305d343ef63a259c2b80e08b3c9f81dd5f9d81cce72ebd43b819efb12538f6a4d2ecfd20cb792106d4f830f47e17b1868514ce494c5394701bc4f0c7e6f151631953527b78b21f5127e128984d2b97cdb8e36e8c38fb084f9e86
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d78adbbbaf2c814d5ece2c7d946606951b1801c95be9594398aa6c1121bd48892f5e53b27a364a4d4d60a5ffdbf430b9e0058594e9a18bd0f875e0d3525b326e629f5a2a8552eef0c09188364ed7a1c27b59cd5b29155d77db49051eee69b6f01f49d23dcb5fb00e4c649a2cab73b34ca2ce7f7e307e9ae50ce9ff6b718a6165a5b0053d1fa94ee35ee2052d19215d6f92801c6263c0;
+  1 encin
+       !d78adbbbaf2c814d5ece2c7d946606951b1801c95be9594398aa6c1121bd48892f5e53b27a364a4d4d60a5ffdbf430b9e0058594e9a18bd0f875e0d3525b326e629f5a2a8552eef0c09188364ed7a1c27b59cd5b29155d77db49051eee69b6f01f49d23dcb5fb00e4c649a2cab73b34ca2ce7f7e307e9ae50ce9ff6b718a6165a5b0053d1fa94ee35ee2052d19215d6f92801c6263c0
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !91553d2f6c335608f38b20fef4372d877d851d9f15f3088ea772a77880a9761784912d9863ab94984e7a047241b048d4fbf9dd3ae6f69be65b3a9fac8da0a3fc83968d9cde57f062eb67404b86eee957263b1bbd6d73d76bef122c5262fd33d2a84bccb5f39743b47f23d73850e68796a603ecf1957025ee90608b66c6137f08d56c4e2d5866265943dd1d5057a2495add3db8093d9359;
+  1 encin
+       !91553d2f6c335608f38b20fef4372d877d851d9f15f3088ea772a77880a9761784912d9863ab94984e7a047241b048d4fbf9dd3ae6f69be65b3a9fac8da0a3fc83968d9cde57f062eb67404b86eee957263b1bbd6d73d76bef122c5262fd33d2a84bccb5f39743b47f23d73850e68796a603ecf1957025ee90608b66c6137f08d56c4e2d5866265943dd1d5057a2495add3db8093d9359
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !38a6a59d061f04f11ba59e1a39bdeaf5394895f788738eff0def07797d99371f02ad9f1f5e624308c1356730f1561008e0ae46ee68439715107ab75bdf960f5d2970c3899735fe9ae934afeb67a862c54eef175cbeaf856e84ca623f1e33084c21f9e5d7e68808b26edff9b08584537453f60ddbd52affd973423a3e671f5010f84908a9d76ee8e3a63b7d70f79e26c90f865a265184ba64;
+  1 encin
+       !38a6a59d061f04f11ba59e1a39bdeaf5394895f788738eff0def07797d99371f02ad9f1f5e624308c1356730f1561008e0ae46ee68439715107ab75bdf960f5d2970c3899735fe9ae934afeb67a862c54eef175cbeaf856e84ca623f1e33084c21f9e5d7e68808b26edff9b08584537453f60ddbd52affd973423a3e671f5010f84908a9d76ee8e3a63b7d70f79e26c90f865a265184ba64
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !88368e2008e4625020bdc44e0ce3a7931a4e8ae20d109ff1ea15dadc5f5f20a20c397c08defe92c08bb3c9616af246a1081e79b3fab37030580c1184cc7b0e10ec37c7e0ca79e8cb2dc356fbdc920162407dbf70f16e44cddd14d96159e8ea3b90231f35356976c508ee854085bca1cd2601cdc4aeff90aa1e341a64f164acd6e75fdb4344bee0950c4c93a66cd73b58c9ad4969d704e4bef8;
+  1 encin
+       !88368e2008e4625020bdc44e0ce3a7931a4e8ae20d109ff1ea15dadc5f5f20a20c397c08defe92c08bb3c9616af246a1081e79b3fab37030580c1184cc7b0e10ec37c7e0ca79e8cb2dc356fbdc920162407dbf70f16e44cddd14d96159e8ea3b90231f35356976c508ee854085bca1cd2601cdc4aeff90aa1e341a64f164acd6e75fdb4344bee0950c4c93a66cd73b58c9ad4969d704e4bef8
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !61bfce8ba6e1b8c43b1cb6d42068b9a05732db6d7ab6c6bf0eb5646952dfdedaaab8c098b68ff62bd5afefcdb8b9efa817db9a2c73287c80770cccaa904f978608cd084d64dfcff4694cba64d197f4d51d7bf727cb55c6218470ac2c4cf0097dc5af9e1aba3853f935c90627e8520c6c2dab7f49f8614c6aa2d732ae04483446925d6a24ae292ec5a5b5c614f3570f2cd4b2e7b43e3895708926;
+  1 encin
+       !61bfce8ba6e1b8c43b1cb6d42068b9a05732db6d7ab6c6bf0eb5646952dfdedaaab8c098b68ff62bd5afefcdb8b9efa817db9a2c73287c80770cccaa904f978608cd084d64dfcff4694cba64d197f4d51d7bf727cb55c6218470ac2c4cf0097dc5af9e1aba3853f935c90627e8520c6c2dab7f49f8614c6aa2d732ae04483446925d6a24ae292ec5a5b5c614f3570f2cd4b2e7b43e3895708926
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !7a2ede61e2fde8e1745851495017a161de04c347153f7ec572b4c60b22e1c10509a5767dd664f2df7d1d8e86f2262396d0fcb7e7339fe53f4c80882b4d2b43add0736694a7829bda043cc744f236f1f5b9f61f7fd77b38819257f1e7c632d84e23d40e921b65b04439afa64c053cc9a3dc42e95c089495b571bc7493ded7c9c6be32e1a5288d3ca20861c9bbaf1261909acfa6a66a1ee3dce159de;
+  1 encin
+       !7a2ede61e2fde8e1745851495017a161de04c347153f7ec572b4c60b22e1c10509a5767dd664f2df7d1d8e86f2262396d0fcb7e7339fe53f4c80882b4d2b43add0736694a7829bda043cc744f236f1f5b9f61f7fd77b38819257f1e7c632d84e23d40e921b65b04439afa64c053cc9a3dc42e95c089495b571bc7493ded7c9c6be32e1a5288d3ca20861c9bbaf1261909acfa6a66a1ee3dce159de
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d7caceec9406ce456325d8c2d72a56fc3c648e949eb2e69f815f6a4607a3dcc2d980d15672325f3aa714f7c3560aaa7076912cb9a9073d382730a4d2f08636f5c8a75252a4517355845f2bf956c1768694aa523b94440e01d526ce4f1d289c413f02ad16b5de0de49602ec3469edd588c06a8f40eb25ef5440ec4ea5d9ff3c35aa75468b05026b6850895e64fc6ff718ef4f6988972d978a679c7550;
+  1 encin
+       !d7caceec9406ce456325d8c2d72a56fc3c648e949eb2e69f815f6a4607a3dcc2d980d15672325f3aa714f7c3560aaa7076912cb9a9073d382730a4d2f08636f5c8a75252a4517355845f2bf956c1768694aa523b94440e01d526ce4f1d289c413f02ad16b5de0de49602ec3469edd588c06a8f40eb25ef5440ec4ea5d9ff3c35aa75468b05026b6850895e64fc6ff718ef4f6988972d978a679c7550
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !2d651c94a7ed4d02d256b4039661e0dc59e6a15b582e2aaa4931ad644d9765ffea394266410efa7502bd4a86a9f45f1c337c18510bbfa4f5bd40d9d1d0ba37f216d0980647266f382e448f8911c02f4840f9aa5c7dfc1a60bfbe90822358d46b7ef3bce139112101dc85ad5153233429cee3cfb9399c028400bdf7010083ab5defc0a2e3be79f95f76995e5e466641743d410533a5ca6e2b398da11bb5;
+  1 encin
+       !2d651c94a7ed4d02d256b4039661e0dc59e6a15b582e2aaa4931ad644d9765ffea394266410efa7502bd4a86a9f45f1c337c18510bbfa4f5bd40d9d1d0ba37f216d0980647266f382e448f8911c02f4840f9aa5c7dfc1a60bfbe90822358d46b7ef3bce139112101dc85ad5153233429cee3cfb9399c028400bdf7010083ab5defc0a2e3be79f95f76995e5e466641743d410533a5ca6e2b398da11bb5
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !0976f8129dd12c3f8dea983af67ae3aadfbdafd1fc4070454e5f48a69491e6fb34abb1aba463547b8ac75b036c8acbf71760d560453da42b4d6eb9d648a9e3305329fcf206291d3ce91da0a00f3d7d9f56f5e828b5403c1ea451d731c8751a07943cccade4d76bc4ad467a08a35f3d5f2412ea96ff4affad059e5b13d8aac4160e40b13406cdf643df1a70d9f9a564c3c104f66b1e341cb70b0e9768c903;
+  1 encin
+       !0976f8129dd12c3f8dea983af67ae3aadfbdafd1fc4070454e5f48a69491e6fb34abb1aba463547b8ac75b036c8acbf71760d560453da42b4d6eb9d648a9e3305329fcf206291d3ce91da0a00f3d7d9f56f5e828b5403c1ea451d731c8751a07943cccade4d76bc4ad467a08a35f3d5f2412ea96ff4affad059e5b13d8aac4160e40b13406cdf643df1a70d9f9a564c3c104f66b1e341cb70b0e9768c903
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !976ef493928a00c12152f71ee7f0471090b83d2443131f0f5906e825dd28a42601dee4a590eb773e9accb1c9651fe70d12ebad1ed6e48534a2c532dbb8da858ad46612cec9a3a1189643f3321b7cb1b450b1a2b536d4b80be7809693ebbd3a621e13d3fa245fe32cb36b38e42f83db93b5318caadc6ca85f74f4a9f15b01e1524a2f63e713b28fa8fd9f42ae5b46c1734f6d93b3bf7845e44275af3051d8d4;
+  1 encin
+       !976ef493928a00c12152f71ee7f0471090b83d2443131f0f5906e825dd28a42601dee4a590eb773e9accb1c9651fe70d12ebad1ed6e48534a2c532dbb8da858ad46612cec9a3a1189643f3321b7cb1b450b1a2b536d4b80be7809693ebbd3a621e13d3fa245fe32cb36b38e42f83db93b5318caadc6ca85f74f4a9f15b01e1524a2f63e713b28fa8fd9f42ae5b46c1734f6d93b3bf7845e44275af3051d8d4
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !8ac2ef579459419d2d55a05cc2b1b38f2c214f917b6803c414487e168415010103ec052e2d34110a9400993c3db3ae2811153428a4bd102f7002d253db77da3bb8f19dd64cb2c9889e777c0e91d6935815f46f69ca20c7ce9ce987f1a8f53d2d827dd7966c7e3f678e6ae080db3b3139c6ac83f821d17c45e4a385e47b5257387f2bdcc8d95cc465b240fb0149499035a5dd93f428eb8a42793fbb9e4ae88d67;
+  1 encin
+       !8ac2ef579459419d2d55a05cc2b1b38f2c214f917b6803c414487e168415010103ec052e2d34110a9400993c3db3ae2811153428a4bd102f7002d253db77da3bb8f19dd64cb2c9889e777c0e91d6935815f46f69ca20c7ce9ce987f1a8f53d2d827dd7966c7e3f678e6ae080db3b3139c6ac83f821d17c45e4a385e47b5257387f2bdcc8d95cc465b240fb0149499035a5dd93f428eb8a42793fbb9e4ae88d67
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !930bc40db6824175fec1a6ac155183afb11bdbb2ff1c32a4425f7667171e1271ac4d436c10c839bc85a1172a4095501e62a53a0c5933a7fee45b5415dc26bb68fbb4768b4bee89b14bce1459a8f46d84d2ac9f5fa4b43f66a719a1d74837da85de979ea0ff975441f8ec34225a17a4a326fc6273614d4643c672da8d8a22a8cac265adaf916b7bd11f30e8282a268e4abb16fed4677dc963bcf5e360b03d862adb;
+  1 encin
+       !930bc40db6824175fec1a6ac155183afb11bdbb2ff1c32a4425f7667171e1271ac4d436c10c839bc85a1172a4095501e62a53a0c5933a7fee45b5415dc26bb68fbb4768b4bee89b14bce1459a8f46d84d2ac9f5fa4b43f66a719a1d74837da85de979ea0ff975441f8ec34225a17a4a326fc6273614d4643c672da8d8a22a8cac265adaf916b7bd11f30e8282a268e4abb16fed4677dc963bcf5e360b03d862adb
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d18d19dc4a66a1994dd58caa14ae538160d710af67597889f5c5d7d1a6c2a7d076ac4fb070200e0e1deecd1d0f1189dea5f2d542b8375317fe734a52cda4b40b414816201de095841a89db465cbbc198b4e3d2239afd4b07add7cf5e4ba5c934c65a485e8c2d682546d28f435234b6208ad0fe9f02192bb9b2c1b73e20716e39525f586f12f5f9fe8e0c58381b8f26651794a3156261b94795e98cb494b8402f0034;
+  1 encin
+       !d18d19dc4a66a1994dd58caa14ae538160d710af67597889f5c5d7d1a6c2a7d076ac4fb070200e0e1deecd1d0f1189dea5f2d542b8375317fe734a52cda4b40b414816201de095841a89db465cbbc198b4e3d2239afd4b07add7cf5e4ba5c934c65a485e8c2d682546d28f435234b6208ad0fe9f02192bb9b2c1b73e20716e39525f586f12f5f9fe8e0c58381b8f26651794a3156261b94795e98cb494b8402f0034
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !8f6ed81d1b3df9d227a6c0d1d7fcbd0dac459e92a6c8e7ede2f3f58767722026d6308129bb753818e0ec585a5e3e56dab714ae1aae8b7bc2e0625e0ef8294d1d7a048bd25bef0dfe581c08356ea0198430698d2499f0b833f6e3d22d8cdf7abfb3178365c082d274014fe6afcdd0e8e024c89f75f9ee6a12357684612d557ed1544931a04ab1ebedc73450f9126f805f62660e46298a930af945d5e737365b8eed349e;
+  1 encin
+       !8f6ed81d1b3df9d227a6c0d1d7fcbd0dac459e92a6c8e7ede2f3f58767722026d6308129bb753818e0ec585a5e3e56dab714ae1aae8b7bc2e0625e0ef8294d1d7a048bd25bef0dfe581c08356ea0198430698d2499f0b833f6e3d22d8cdf7abfb3178365c082d274014fe6afcdd0e8e024c89f75f9ee6a12357684612d557ed1544931a04ab1ebedc73450f9126f805f62660e46298a930af945d5e737365b8eed349e
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !394cc6c775f2c9d0f178ccab1f32a9beb6f19f33bc82aaf734f30b4499a70ea6cad8e3c074766a466c7f541a460453b99bde9a5119a83f6360aeced4dfea527178008c38ea08a27352f731e8e5f2698e1fc030e26378bb597a6cb1153e2b2c6c2ed827efa4d941e3caecbbbbc16036cf614f0f8ebb1d355003f854c90b8e1ad325ba6269bbfc1807a8e067a546523c6e0b0a034affc0a4c8f8d10bd9d7f926e6c383c6d8;
+  1 encin
+       !394cc6c775f2c9d0f178ccab1f32a9beb6f19f33bc82aaf734f30b4499a70ea6cad8e3c074766a466c7f541a460453b99bde9a5119a83f6360aeced4dfea527178008c38ea08a27352f731e8e5f2698e1fc030e26378bb597a6cb1153e2b2c6c2ed827efa4d941e3caecbbbbc16036cf614f0f8ebb1d355003f854c90b8e1ad325ba6269bbfc1807a8e067a546523c6e0b0a034affc0a4c8f8d10bd9d7f926e6c383c6d8
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !503e331b0acc00f2fc35dd9d8f145b2eb5c2e1ed3204dfdcc0a624eabd913ffac3dd87cd30a972df89ec6ccf43cfaadf5deca3c0448d510600068033201099f1c8e3f08eadb73ee63bc06e63e7df525458abf9b54113aebf3825127b1a0bdc12c96665e1cdd1ef08a66d147e727dadf9172076215a53f21b1201112cf2b57b33e10c8bed75afa629afe4e9f6510c77b853d64c218af57edcb68281594afc61f926a659bafd;
+  1 encin
+       !503e331b0acc00f2fc35dd9d8f145b2eb5c2e1ed3204dfdcc0a624eabd913ffac3dd87cd30a972df89ec6ccf43cfaadf5deca3c0448d510600068033201099f1c8e3f08eadb73ee63bc06e63e7df525458abf9b54113aebf3825127b1a0bdc12c96665e1cdd1ef08a66d147e727dadf9172076215a53f21b1201112cf2b57b33e10c8bed75afa629afe4e9f6510c77b853d64c218af57edcb68281594afc61f926a659bafd
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !794a8b86dcef1e3409d82b9e460403e67a7719c399dbab43e23610347bb0c275bf7ca6babe885231398c04b6492b6bec7ec678f6ca6ab5b23e1466dfd253bf270f08cc6a467cc4bedec91bce70e95b87be5509501260ab8edd3c1a2ccac8722e3cfc6687c962aa9ccb1f7fca535d38b0c930ebe9a63407a0308aa6198b90fa0ed3d4918f962be7391c7ab9444beb642f6fb06f3a2df0bba77fdb29bd7ac2420aeeb9bcfecc0a;
+  1 encin
+       !794a8b86dcef1e3409d82b9e460403e67a7719c399dbab43e23610347bb0c275bf7ca6babe885231398c04b6492b6bec7ec678f6ca6ab5b23e1466dfd253bf270f08cc6a467cc4bedec91bce70e95b87be5509501260ab8edd3c1a2ccac8722e3cfc6687c962aa9ccb1f7fca535d38b0c930ebe9a63407a0308aa6198b90fa0ed3d4918f962be7391c7ab9444beb642f6fb06f3a2df0bba77fdb29bd7ac2420aeeb9bcfecc0a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !7e3f2d928ed2d2e4e1cf6c534b57d52235d437d9377b00760467bba709f113b0b562e52c1a077ffeff2622ea38f7f787ac7dbbc4b29aa7fc5d9e8d58977a2bf94172e00b3fc7f63875f08f1e5c67e2737259faf012e4362177affa06f24859b476a8ab55798bba9702980f57d78b0f1306b4dd7be7118ace1bbbe1a23340226fb91499d938f10573d720c4c72e3d116dd6d72fd7920a7af69b1f600b5cfbba5538645ce50aae1e;
+  1 encin
+       !7e3f2d928ed2d2e4e1cf6c534b57d52235d437d9377b00760467bba709f113b0b562e52c1a077ffeff2622ea38f7f787ac7dbbc4b29aa7fc5d9e8d58977a2bf94172e00b3fc7f63875f08f1e5c67e2737259faf012e4362177affa06f24859b476a8ab55798bba9702980f57d78b0f1306b4dd7be7118ace1bbbe1a23340226fb91499d938f10573d720c4c72e3d116dd6d72fd7920a7af69b1f600b5cfbba5538645ce50aae1e
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !30ad0da493ebd63c8fc042db11b23a2d203bae61cffd6194337d4ad838589b693211b89a25f845d15c8dcbe61442b1a2f44fd4ec5a6bfdb43ecd0bcc5fb8da34d68f2c20ef1433422d658c917a25fc440b4c2b151a62b949e184f58f501ff0c6d77335ac8db3127e38313fe96c4b109e9c05233e300fd9c9fdf45b700e8ee0553c55bdaedc0d43856cbf8679f53525e722f3e3435eb4f154137195c27590c8e6eb7455f64d11c546;
+  1 encin
+       !30ad0da493ebd63c8fc042db11b23a2d203bae61cffd6194337d4ad838589b693211b89a25f845d15c8dcbe61442b1a2f44fd4ec5a6bfdb43ecd0bcc5fb8da34d68f2c20ef1433422d658c917a25fc440b4c2b151a62b949e184f58f501ff0c6d77335ac8db3127e38313fe96c4b109e9c05233e300fd9c9fdf45b700e8ee0553c55bdaedc0d43856cbf8679f53525e722f3e3435eb4f154137195c27590c8e6eb7455f64d11c546
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !e3177d315779299d808bd2c34eca11dfb2efd5f0c11c354d59b9bc2d29eab2767cce65797e2d2cd94328dda9e700eaadd66dd23a7bcce40c978e5fa0081056d393cd1612ae248ad340dd33e0605cfba4c899dd7f78e4fdeec95d53b0fdac26217fe1a6f0104ddc0f2929feb54db9b07a6b8801c99978a6db91cbefb9f3001bef853424018d5911c2c22fa4d2939b5ead3457274332a2b223f2c8eccaec8a632ada3bb746e55adbf77f;
+  1 encin
+       !e3177d315779299d808bd2c34eca11dfb2efd5f0c11c354d59b9bc2d29eab2767cce65797e2d2cd94328dda9e700eaadd66dd23a7bcce40c978e5fa0081056d393cd1612ae248ad340dd33e0605cfba4c899dd7f78e4fdeec95d53b0fdac26217fe1a6f0104ddc0f2929feb54db9b07a6b8801c99978a6db91cbefb9f3001bef853424018d5911c2c22fa4d2939b5ead3457274332a2b223f2c8eccaec8a632ada3bb746e55adbf77f
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !584cee6d2cbdcde833d8d786bc1939a8a3da9ee634703bf690532eff66665478b7c2478a2df42e0326cca1fd6f891c81a261489e8c37471f031874970bd556c6747626b6fdd05d4c4f4531da9a938030d38626f661ccec2faea96affc1273fc48245cd1828b346bb6ea7e107026171c06db53e08a66286a9b945a7142236ea16133e217aa104a2920eb401f4404d6dcb9589d694b0d6ef8c61e9a2a9f584a4a3af56449f2aaea8d06534;
+  1 encin
+       !584cee6d2cbdcde833d8d786bc1939a8a3da9ee634703bf690532eff66665478b7c2478a2df42e0326cca1fd6f891c81a261489e8c37471f031874970bd556c6747626b6fdd05d4c4f4531da9a938030d38626f661ccec2faea96affc1273fc48245cd1828b346bb6ea7e107026171c06db53e08a66286a9b945a7142236ea16133e217aa104a2920eb401f4404d6dcb9589d694b0d6ef8c61e9a2a9f584a4a3af56449f2aaea8d06534
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !1e11e7edfa768149f9969b5863f1a2240c90f528593d0bd47ca6636a564aa7218cefe194a1d57c557b1429779336b8bd2cacfe63e15b4e8161f584ab860069ff5d3bccfd6b3c8be24789f2988fe8c1bd23fdf2a870fc502e8e25d2fd90ab4c95dcd929a050d99d3e2ef0a4e0fd8e42b5a73300becd80d6571ba8ebfcef4aa5a152843ac09f6c102b765c7e422f619563b18c5fc18bbdb279c6463b84116373adaf8c776c68b85221fe0abd;
+  1 encin
+       !1e11e7edfa768149f9969b5863f1a2240c90f528593d0bd47ca6636a564aa7218cefe194a1d57c557b1429779336b8bd2cacfe63e15b4e8161f584ab860069ff5d3bccfd6b3c8be24789f2988fe8c1bd23fdf2a870fc502e8e25d2fd90ab4c95dcd929a050d99d3e2ef0a4e0fd8e42b5a73300becd80d6571ba8ebfcef4aa5a152843ac09f6c102b765c7e422f619563b18c5fc18bbdb279c6463b84116373adaf8c776c68b85221fe0abd
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !2219a18f102a95f8e186531b43cfc32c33b502b82f8327224423866a8f107e14dbac95eaffdac32deb461ca94008c4285478d2e294972cedc61af9a02c6a650261b3fe05d96e6e5de9f88c996a9fdba504b762e5e662f59b918a054bd44dcda3c093b49726d361a7c9efa020f7c5548ee1f671e17d1add2a5f802b2b661e8b4ac6ff9c4463776a8cc1d0db851dee9f53ac32c54f4f93641be604bb671db1be856fa228a41c6cd3bbb947cfb0;
+  1 encin
+       !2219a18f102a95f8e186531b43cfc32c33b502b82f8327224423866a8f107e14dbac95eaffdac32deb461ca94008c4285478d2e294972cedc61af9a02c6a650261b3fe05d96e6e5de9f88c996a9fdba504b762e5e662f59b918a054bd44dcda3c093b49726d361a7c9efa020f7c5548ee1f671e17d1add2a5f802b2b661e8b4ac6ff9c4463776a8cc1d0db851dee9f53ac32c54f4f93641be604bb671db1be856fa228a41c6cd3bbb947cfb0
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !ae249337097bb506049440d2c68004517c467292742e4153c5f76bbfaf608b092e681b8eb012163166ef2be88b704d859ec57405bb3a3b15bcd9167d9c007f3865250cc614b8dda4864868243303c31749f2a4f32f967f95c28b4b80ff9f089d670af32b62641dd213d6c3d9553d16a1e398985a304c0ce4b535b8d2ec7b8328039883d55afe35cfb3cde5b59d21b5c85d14de7498404b25470f931e77bfa9658d92171cc3ea7b31406b1d2b03;
+  1 encin
+       !ae249337097bb506049440d2c68004517c467292742e4153c5f76bbfaf608b092e681b8eb012163166ef2be88b704d859ec57405bb3a3b15bcd9167d9c007f3865250cc614b8dda4864868243303c31749f2a4f32f967f95c28b4b80ff9f089d670af32b62641dd213d6c3d9553d16a1e398985a304c0ce4b535b8d2ec7b8328039883d55afe35cfb3cde5b59d21b5c85d14de7498404b25470f931e77bfa9658d92171cc3ea7b31406b1d2b03
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d3001684fb506a9e086187174ac74f28c7d78412658329bde0ffb2c70b47fbb071ff32f79b1386e88f6050969da71e1f44524a09c601d52dac9884e21869c7321a3a1a70e714723024f368889a7eb52ec19b4356962a9e38f7b8a03d092b218e2b175d691555b300a02ec585f5504b8d68a908ff6c62e38c5dd590fc783e751fe425e9af00b0c743cb48be38de4300b42609d2bbb3956ddd771865ed365790d5455253799a2df6a08e0bfee50595;
+  1 encin
+       !d3001684fb506a9e086187174ac74f28c7d78412658329bde0ffb2c70b47fbb071ff32f79b1386e88f6050969da71e1f44524a09c601d52dac9884e21869c7321a3a1a70e714723024f368889a7eb52ec19b4356962a9e38f7b8a03d092b218e2b175d691555b300a02ec585f5504b8d68a908ff6c62e38c5dd590fc783e751fe425e9af00b0c743cb48be38de4300b42609d2bbb3956ddd771865ed365790d5455253799a2df6a08e0bfee50595
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !4f78ed9d5c890216da2099901998bf64bd90a0426a3d31dd0e8789a37659d9ddfd1154f7407c3cb6a2000a87fe1fb050f84715bb5dcd02e61e51cfbe67d95504b9c83a3e988cb77408d468ffb869aec42291d414be67049af20b9e10c59ef5ef39f3945c146c4ab941cec0043afb9460878a2c513080df8c757fbccb2269512c08136dcda6cfe8b0a6dbd3fe2754cf4551cf8a5a95a6215e677bded62ea4487ac6304b028154eba7e98e024112f28a;
+  1 encin
+       !4f78ed9d5c890216da2099901998bf64bd90a0426a3d31dd0e8789a37659d9ddfd1154f7407c3cb6a2000a87fe1fb050f84715bb5dcd02e61e51cfbe67d95504b9c83a3e988cb77408d468ffb869aec42291d414be67049af20b9e10c59ef5ef39f3945c146c4ab941cec0043afb9460878a2c513080df8c757fbccb2269512c08136dcda6cfe8b0a6dbd3fe2754cf4551cf8a5a95a6215e677bded62ea4487ac6304b028154eba7e98e024112f28a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !49a411020d58068cf8d35e23b4155598b348a9f91b92dfd0e831f7c040b7d4b696fbc5b4ca0e40cf9c57843dd440aa6c85e84e7c0091443baa205bfc668883380124d84fb042b92fd982099567b2f6048a0ec215975114990b3c72a09d684a21c006bae1dff5010d84330203c64f2afe2127ecac142760f3732f382658bb6b3ee3e58afbe7f231851ffbbc5220906a8acbb7ff61bb797996b590aeaeea7f1cd5769703fc182959c6082fbf1678f314ec;
+  1 encin
+       !49a411020d58068cf8d35e23b4155598b348a9f91b92dfd0e831f7c040b7d4b696fbc5b4ca0e40cf9c57843dd440aa6c85e84e7c0091443baa205bfc668883380124d84fb042b92fd982099567b2f6048a0ec215975114990b3c72a09d684a21c006bae1dff5010d84330203c64f2afe2127ecac142760f3732f382658bb6b3ee3e58afbe7f231851ffbbc5220906a8acbb7ff61bb797996b590aeaeea7f1cd5769703fc182959c6082fbf1678f314ec
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !e439fc3b2bc7687dbf83956f3b2174e9c3b137e33d42661129c9fd00b7b6514608b03b68361d950b8c49f493eba04fa2c3f1b66bc4484ad07b81f65f7755609755adbeec725db22c9115cd1d2010eacb784a5b88b06a1e0e7c0ad63fdbbedbbd407d94df8740b014706acd1a27effd43581e9b218837512feb0f60141371ab6910098ff0e457c5df82779947197e46213e8b5973d4cef2e41b326a203f4bba820c18127e7aabe7abf06a12b8f31660c223;
+  1 encin
+       !e439fc3b2bc7687dbf83956f3b2174e9c3b137e33d42661129c9fd00b7b6514608b03b68361d950b8c49f493eba04fa2c3f1b66bc4484ad07b81f65f7755609755adbeec725db22c9115cd1d2010eacb784a5b88b06a1e0e7c0ad63fdbbedbbd407d94df8740b014706acd1a27effd43581e9b218837512feb0f60141371ab6910098ff0e457c5df82779947197e46213e8b5973d4cef2e41b326a203f4bba820c18127e7aabe7abf06a12b8f31660c223
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !13e0220a51a95659729e38e45e6f189923e230e75ba60282ee7c1064bcf5fdabc446a44f16e36a1d3d0dee3933359f8c2ec41595494918e8ac5a69b131775febb8d2d0510e2a78932f084eb875ba8a22da5033123debd857a16811a2fe87997d59eebd6832371e7f893bc182b019fb750bca23ee7c80335e90f72ce6bdf1bdaf2b4597684bc22c43be120452f4cd8f8f1c9e5ca0a8fbd434ed889ba8fea8ec5a3e101b0185a661e261620166b534b59dd3b9;
+  1 encin
+       !13e0220a51a95659729e38e45e6f189923e230e75ba60282ee7c1064bcf5fdabc446a44f16e36a1d3d0dee3933359f8c2ec41595494918e8ac5a69b131775febb8d2d0510e2a78932f084eb875ba8a22da5033123debd857a16811a2fe87997d59eebd6832371e7f893bc182b019fb750bca23ee7c80335e90f72ce6bdf1bdaf2b4597684bc22c43be120452f4cd8f8f1c9e5ca0a8fbd434ed889ba8fea8ec5a3e101b0185a661e261620166b534b59dd3b9
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !1bb9929153b43eba74014f0a3e9232b6b09c3264f7b51321dfdef0d31a83ac116f742fd8af086514605e91e04eac8a19463f156863ba14440c4c85aedfc741a1d6208bf6fb8c40e63da89216a7548b53def99de221ed4cb5bf0a9bc450763c21ed9345acc1a4f64b73a2ae652508f5ce7b8dd3f6e9df8674e6c987f8436a2c2418425008b1b75995cba10d2e0a871c694de76bbb0e21c1c24d1503fbb46e1e693ddb21c5876fbaca82e479f660fe67a7686a7a;
+  1 encin
+       !1bb9929153b43eba74014f0a3e9232b6b09c3264f7b51321dfdef0d31a83ac116f742fd8af086514605e91e04eac8a19463f156863ba14440c4c85aedfc741a1d6208bf6fb8c40e63da89216a7548b53def99de221ed4cb5bf0a9bc450763c21ed9345acc1a4f64b73a2ae652508f5ce7b8dd3f6e9df8674e6c987f8436a2c2418425008b1b75995cba10d2e0a871c694de76bbb0e21c1c24d1503fbb46e1e693ddb21c5876fbaca82e479f660fe67a7686a7a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !d0302df57b17f2f3188b63b200d2e9ec3915ce60514c9b6941f9a29031cd1f68e2c07af6400bda66945b1753a33b26ff6c2dabe6ffdc1fa915b3422ef0c1444aa71522d656dc225981f345771dec1dfd304733be682194b89d3c90ae3a490a309709909998a1edb6dd6f65f9e7ee49bb80b0d5ed399dab5a18573fa6df6e986ea4a53fd062638472f9162e2abe43de24fbb1ec1461ff86a498ee0877100c907b0158638ea75fae34ddaa6ce9963636af55f7e0c0;
+  1 encin
+       !d0302df57b17f2f3188b63b200d2e9ec3915ce60514c9b6941f9a29031cd1f68e2c07af6400bda66945b1753a33b26ff6c2dabe6ffdc1fa915b3422ef0c1444aa71522d656dc225981f345771dec1dfd304733be682194b89d3c90ae3a490a309709909998a1edb6dd6f65f9e7ee49bb80b0d5ed399dab5a18573fa6df6e986ea4a53fd062638472f9162e2abe43de24fbb1ec1461ff86a498ee0877100c907b0158638ea75fae34ddaa6ce9963636af55f7e0c0
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !35b23065c41551d21bca567870fcac995aabf14c1f9f32d6638cb75964c5924e03902a5ca638abe78af8af70b448c113498a000289efd5e0a9c5b5ede89445e42dd3140fe0d73089f3d8223d9d662111e67809a539f034a07971ab886121a8bddcd45bdafdc249b15b1556ea2bdd0221a75fb575a76a56ff2557bc6b1773b4ac0317f141f35dbd032e32236039a3d881e15951e3e54c4bcfd364a9b9696f281fc750f1153c3fa2e3b87fe0cc3a73249daab30e15c6;
+  1 encin
+       !35b23065c41551d21bca567870fcac995aabf14c1f9f32d6638cb75964c5924e03902a5ca638abe78af8af70b448c113498a000289efd5e0a9c5b5ede89445e42dd3140fe0d73089f3d8223d9d662111e67809a539f034a07971ab886121a8bddcd45bdafdc249b15b1556ea2bdd0221a75fb575a76a56ff2557bc6b1773b4ac0317f141f35dbd032e32236039a3d881e15951e3e54c4bcfd364a9b9696f281fc750f1153c3fa2e3b87fe0cc3a73249daab30e15c6
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !6aa7cbfd617d2dc854b2e65e4fe5077fb991987bbe6a2e274f8dfe06bc62b2c9539bddb7db1a6a76adde9b78d6b73776700e30dfa42cd4d96dfa359d8857ae3a952ceab3bc7b266d33dff9a75da46aa4ade9031eac27428f49dfd30dfe4894f1b7d474e50dbdb06987aa29b280eb0bca726e1246e8fbaa0d623cc4df1bd9d551e67d0aea9337c63446124ddafe3f942f5b05897f49c902a8f67c31a0a51455c6b2b7e845c68f2f82f6c86e6f5bbc37f78622bb067693;
+  1 encin
+       !6aa7cbfd617d2dc854b2e65e4fe5077fb991987bbe6a2e274f8dfe06bc62b2c9539bddb7db1a6a76adde9b78d6b73776700e30dfa42cd4d96dfa359d8857ae3a952ceab3bc7b266d33dff9a75da46aa4ade9031eac27428f49dfd30dfe4894f1b7d474e50dbdb06987aa29b280eb0bca726e1246e8fbaa0d623cc4df1bd9d551e67d0aea9337c63446124ddafe3f942f5b05897f49c902a8f67c31a0a51455c6b2b7e845c68f2f82f6c86e6f5bbc37f78622bb067693
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !3d4f01c3f52db6eadd975fb99c579dd8ed921c67d577e3ad51f2eee94c643d31cd4e27636234478d2aa718b85656c9ffa3f8a4874e465f3b8007776b0124949eaa7a2f4199d9203cfc46e154bf62c36fa8f31d07efd9fe2333d42fcba6438a2022ee8e37b67489c9595efc02c7488eb05c9d691d2da641043610d800ffcf497ba5dfb1be9263fa7b05dd27ded3bdf7a463443c5cc11d2506f2e221cc13446a04e5e8a42941e55a52c135fb139b97cdd9dab1b22f9f4c40;
+  1 encin
+       !3d4f01c3f52db6eadd975fb99c579dd8ed921c67d577e3ad51f2eee94c643d31cd4e27636234478d2aa718b85656c9ffa3f8a4874e465f3b8007776b0124949eaa7a2f4199d9203cfc46e154bf62c36fa8f31d07efd9fe2333d42fcba6438a2022ee8e37b67489c9595efc02c7488eb05c9d691d2da641043610d800ffcf497ba5dfb1be9263fa7b05dd27ded3bdf7a463443c5cc11d2506f2e221cc13446a04e5e8a42941e55a52c135fb139b97cdd9dab1b22f9f4c40
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !82ba5683a6b7b686523481e90adce794dbe49e5e80217d798272ae66f5827fe7bd0900d6bd0cb3101916a4284420298e8826afd5ec0e9a9c78ead1f48c8007f8cb4e20364adde10ec5f843e92e717106498cbc7ed7741f5e0db2a9ef48f5c07db71d4f31be38bcb1532c16fdbf3001300861ee0d9b2b06be8779cf13d8436b28af7e87ba69634bbdbccbee94201c93273089dd9fb8a6acb7fa6f1238effce72ee5130ac3930d947ba7f8bc4cd91e129a7cbafbeb4c32d01a;
+  1 encin
+       !82ba5683a6b7b686523481e90adce794dbe49e5e80217d798272ae66f5827fe7bd0900d6bd0cb3101916a4284420298e8826afd5ec0e9a9c78ead1f48c8007f8cb4e20364adde10ec5f843e92e717106498cbc7ed7741f5e0db2a9ef48f5c07db71d4f31be38bcb1532c16fdbf3001300861ee0d9b2b06be8779cf13d8436b28af7e87ba69634bbdbccbee94201c93273089dd9fb8a6acb7fa6f1238effce72ee5130ac3930d947ba7f8bc4cd91e129a7cbafbeb4c32d01a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !11b58ac73a99abf28873aaa1f820ba419659265dd34df84986f5437b6a9a353438e4f4d0134bad123e648ee670ee7eccb75c4a47f768006645020483a8e4e3f538b761a427763a2389d6b00b4b75226b21ee8b22d799c15df53fc786000a2e42d687b84419d4cd709b82c1b79313c642686a221194cdbdce50f52a678273182309dc50ff1b71d73c398937a090ac47e36d1abbe0ab5ee526a19e64c34ca1b54d67b23b002ba33ae1538f7a4cfdeb897bd930fc0cae0bf99470;
+  1 encin
+       !11b58ac73a99abf28873aaa1f820ba419659265dd34df84986f5437b6a9a353438e4f4d0134bad123e648ee670ee7eccb75c4a47f768006645020483a8e4e3f538b761a427763a2389d6b00b4b75226b21ee8b22d799c15df53fc786000a2e42d687b84419d4cd709b82c1b79313c642686a221194cdbdce50f52a678273182309dc50ff1b71d73c398937a090ac47e36d1abbe0ab5ee526a19e64c34ca1b54d67b23b002ba33ae1538f7a4cfdeb897bd930fc0cae0bf99470
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a6e6e7cd064b56599f350078aa8009f5c496f976525b4543ce8b5aed07b97b2d9ed2a6ec53b175740bc0a709b21b90a775ac216c925c4d2c8662bbf6839bafda30657c11e688cafd61779fbf345bd662f0253cd292a65bfa69d235c2ddd31f82bd2b72c354ba2345848e417cbdcb15b8bd18dac3f490c2bbca39a5fe839a22a5bc9d588971b7c6765b6367bae62fcad0e88246e515d47f3dfd706f5d2b2ad2fd678b3ba13c589ba7a9be4d140f77553ba09dc01e7b6eab91bbe8;
+  1 encin
+       !a6e6e7cd064b56599f350078aa8009f5c496f976525b4543ce8b5aed07b97b2d9ed2a6ec53b175740bc0a709b21b90a775ac216c925c4d2c8662bbf6839bafda30657c11e688cafd61779fbf345bd662f0253cd292a65bfa69d235c2ddd31f82bd2b72c354ba2345848e417cbdcb15b8bd18dac3f490c2bbca39a5fe839a22a5bc9d588971b7c6765b6367bae62fcad0e88246e515d47f3dfd706f5d2b2ad2fd678b3ba13c589ba7a9be4d140f77553ba09dc01e7b6eab91bbe8
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !46ae65281aa759e1e8a46a5041503ebdcea654a9e36e593f37feed335172d4bc6b6774fd4c3a0965f04a69ee35dc96eb3cd4d16f62a5996b1c9040f4b5b5bc0230286c1c9cdb9ceeb96f8297466d1bbfc8ced321e11af6a1582672c8e82f54d8474c05310f67313bbb074d78fc938e5dbf18eac026370010e20d968a15b1acc9d7d4f83c8608b84570d999c151b1ee55fbc15c7cbf8235d95bbb710cbf3d606133dfe910824606f2a1c55accb3de74b67c963cf0077d778bd68d8a;
+  1 encin
+       !46ae65281aa759e1e8a46a5041503ebdcea654a9e36e593f37feed335172d4bc6b6774fd4c3a0965f04a69ee35dc96eb3cd4d16f62a5996b1c9040f4b5b5bc0230286c1c9cdb9ceeb96f8297466d1bbfc8ced321e11af6a1582672c8e82f54d8474c05310f67313bbb074d78fc938e5dbf18eac026370010e20d968a15b1acc9d7d4f83c8608b84570d999c151b1ee55fbc15c7cbf8235d95bbb710cbf3d606133dfe910824606f2a1c55accb3de74b67c963cf0077d778bd68d8a
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !beccfb6865d93fc79df6863c229892181813ad92f2b7ae1069a0d359cab9ceb4b2bd78b5a77cd3557b3100709aee138c152663f8f01d2872c38133a1c2ca375379eeaadf41c5291e73e580a6b43d763a6d88b14be8f6898e636f9b57f3020ab4f83c6d36397d9e1fe63f62bc37bf5449eba8621febd9679b6d314e2992d2e02580b471c12deaadcf674c05db4a7e4ae2b29827f7744e2f08d6c4a9e1732b2cf24b036bc5e9b6d39be223117c2f6a4581868cfe14bd6ff5aeb583b848;
+  1 encin
+       !beccfb6865d93fc79df6863c229892181813ad92f2b7ae1069a0d359cab9ceb4b2bd78b5a77cd3557b3100709aee138c152663f8f01d2872c38133a1c2ca375379eeaadf41c5291e73e580a6b43d763a6d88b14be8f6898e636f9b57f3020ab4f83c6d36397d9e1fe63f62bc37bf5449eba8621febd9679b6d314e2992d2e02580b471c12deaadcf674c05db4a7e4ae2b29827f7744e2f08d6c4a9e1732b2cf24b036bc5e9b6d39be223117c2f6a4581868cfe14bd6ff5aeb583b848
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !95ca5c74d1ee38d0fb0c75d8890ab98f8b9219aaccb500ee6257efbf26412c6f8909069a43f66ed0a4452ff21a7b8b995f2bce9b636a1363f582397133900410714fec452c95f109ef21a5ac227ae925c2e78baf70ff404f2447d802fa680820fa1686f8dd8dd9c163a2451ee9587cdccb288b4cc4bbb2f9e8950f5d8744ec5d5d9f1074e33ae98359c68d0ec1fc665c7e55efd68e97283257a4f3adf46276d480b66ae8cf4dc43c1152ea087717250d888ced63f6aed38f220c86116f;
+  1 encin
+       !95ca5c74d1ee38d0fb0c75d8890ab98f8b9219aaccb500ee6257efbf26412c6f8909069a43f66ed0a4452ff21a7b8b995f2bce9b636a1363f582397133900410714fec452c95f109ef21a5ac227ae925c2e78baf70ff404f2447d802fa680820fa1686f8dd8dd9c163a2451ee9587cdccb288b4cc4bbb2f9e8950f5d8744ec5d5d9f1074e33ae98359c68d0ec1fc665c7e55efd68e97283257a4f3adf46276d480b66ae8cf4dc43c1152ea087717250d888ced63f6aed38f220c86116f
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !0f7e862cc4b8330d51d29e038100afc60afee12133bc2005549a46bb45fab3bcdfb9bb25039a066029f4b293b6bfbe128dc9ad86e365b67cd7f6a368620a0d20f23d3fe368f5d75bc96949706674272bab848e63ce53e0d18fa1f5945a396a67af66f9399c01103fc768471f6b146ae82c1624f6aaa9a41edb27ef3feade42034c7d7278c1e3dc2fdcc9ded319dd0548a0d14dc6bc94a821c6cfa351e8b105f65208afc56fc0fdc6c1307767a3ebe6df1533b8ee53be66f39be14f311b57;
+  1 encin
+       !0f7e862cc4b8330d51d29e038100afc60afee12133bc2005549a46bb45fab3bcdfb9bb25039a066029f4b293b6bfbe128dc9ad86e365b67cd7f6a368620a0d20f23d3fe368f5d75bc96949706674272bab848e63ce53e0d18fa1f5945a396a67af66f9399c01103fc768471f6b146ae82c1624f6aaa9a41edb27ef3feade42034c7d7278c1e3dc2fdcc9ded319dd0548a0d14dc6bc94a821c6cfa351e8b105f65208afc56fc0fdc6c1307767a3ebe6df1533b8ee53be66f39be14f311b57
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !99e949b44e949bcbaed6b35d39f94327764bcee5268f6634e4fb0c8a7b8e5c5cdfa9524a7f1fe38c59c65a281093b6ccbfe5e87b3661f5757aa50465b70d65090a1c16563ea62af36e20d1885dcbd100a9bce0f96158b0dc4d0fcbc2ab65658c6d9d2fe7b3abe31f25eba6179e6da61e82418ebd0094eb2890723671d8a43eaf3a53e5cbaadea5ba3eaa065d5b3c60939a7feb4993b0e6000cca8e125f5e40961681cd5cd12fef3c53bff1ac2872f6d835faa92de1729a1f6c9ca5ba887a10;
+  1 encin
+       !99e949b44e949bcbaed6b35d39f94327764bcee5268f6634e4fb0c8a7b8e5c5cdfa9524a7f1fe38c59c65a281093b6ccbfe5e87b3661f5757aa50465b70d65090a1c16563ea62af36e20d1885dcbd100a9bce0f96158b0dc4d0fcbc2ab65658c6d9d2fe7b3abe31f25eba6179e6da61e82418ebd0094eb2890723671d8a43eaf3a53e5cbaadea5ba3eaa065d5b3c60939a7feb4993b0e6000cca8e125f5e40961681cd5cd12fef3c53bff1ac2872f6d835faa92de1729a1f6c9ca5ba887a10
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !28919843d9fd8f617aa87186155b9542fde5014d857c172fb3562e448780d1004f98f5cbbbf3f8d0605f7dec2e73eb48fa3c23d3f191cf82af45363dc364c7dbba5304601a91663171b46e73fb0e48f9a4ea3092091f581d642fb40fa4469227bf7956ad90363e4550df4d73945386c0278a3433d10f4f0381bab8fdc62dd911e3130ef1c68e85d551c9c51be9d8a9ab15237b4ee914e7c04bfa95a0db8db7a9947ab7244d1d371c16d0d31ec354012ae4fd6485fcb590622e2e7e6e49383f92;
+  1 encin
+       !28919843d9fd8f617aa87186155b9542fde5014d857c172fb3562e448780d1004f98f5cbbbf3f8d0605f7dec2e73eb48fa3c23d3f191cf82af45363dc364c7dbba5304601a91663171b46e73fb0e48f9a4ea3092091f581d642fb40fa4469227bf7956ad90363e4550df4d73945386c0278a3433d10f4f0381bab8fdc62dd911e3130ef1c68e85d551c9c51be9d8a9ab15237b4ee914e7c04bfa95a0db8db7a9947ab7244d1d371c16d0d31ec354012ae4fd6485fcb590622e2e7e6e49383f92
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !f521bf3504ddd72a7deb4fbbed98dc1b2793c9eb42593f4ba38724da05396801e219f45c7ce3f98f108ee002166df1206b3625e68c0997cca92c6b46f1f4966fe4be41db01594f936680d9e28f287363969730c9d1f6bff9782a6e5a3d05f3bb756032bb668698b934b50f94b8937602dad9617aaab85799647f96ab6069b06376350b34842a4aa8a874400f61c867cfc49945e964a1fc3da8d924ec800a65b0f83dac752f23eed134fa325586a0780b0988ed827805097fb993af5f25aedfefde;
+  1 encin
+       !f521bf3504ddd72a7deb4fbbed98dc1b2793c9eb42593f4ba38724da05396801e219f45c7ce3f98f108ee002166df1206b3625e68c0997cca92c6b46f1f4966fe4be41db01594f936680d9e28f287363969730c9d1f6bff9782a6e5a3d05f3bb756032bb668698b934b50f94b8937602dad9617aaab85799647f96ab6069b06376350b34842a4aa8a874400f61c867cfc49945e964a1fc3da8d924ec800a65b0f83dac752f23eed134fa325586a0780b0988ed827805097fb993af5f25aedfefde
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !ee153185d4b6438313761f9dd042f1b16af08876cd2ee02a6fa3a89a8910d74b8fe53bf1877483476754556e2b0b2e9f13c483c3daeb2bbb294fb00816eba4a70e93a67aea8b3297684940b5e2ef22724f427334aaa77fd32516b9b888d775178f754e6cd3228ca9190675287abe9e98061868a2ba491b97c3c561f02f1988632be69ace7c37f01f7ef74399d9ba0c9dc8679f8c82823155553c2678a64bba1433b29e6476b2e480873fa512fcfcf3d04b94901cb3e8086671c76cd4168727f0b3fe;
+  1 encin
+       !ee153185d4b6438313761f9dd042f1b16af08876cd2ee02a6fa3a89a8910d74b8fe53bf1877483476754556e2b0b2e9f13c483c3daeb2bbb294fb00816eba4a70e93a67aea8b3297684940b5e2ef22724f427334aaa77fd32516b9b888d775178f754e6cd3228ca9190675287abe9e98061868a2ba491b97c3c561f02f1988632be69ace7c37f01f7ef74399d9ba0c9dc8679f8c82823155553c2678a64bba1433b29e6476b2e480873fa512fcfcf3d04b94901cb3e8086671c76cd4168727f0b3fe
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !188778ec99fa6b7cdbb54ba457d08ec97b0afbccfac8f3adfa0f9d16e1332ebc8a7f826f7f017f5daeff368ec657208ce487b8f42d5e0c002476d71ad4119847082770f95815b07bd779af99cb8c3ad9384fc5513688d0636481bb28306385e63b29576622c017313a7d4e5914c50494b7ebcb1ae9185900437993bf6d0a7777beabc71245301bffa5c99031c67146904c9bb1d8e674562b4cccb6e12f9671cf06efbaaa32e5cc9249ecdefbe8804b88ee86437da83d4e16facccde46a3ce04f5209c1;
+  1 encin
+       !188778ec99fa6b7cdbb54ba457d08ec97b0afbccfac8f3adfa0f9d16e1332ebc8a7f826f7f017f5daeff368ec657208ce487b8f42d5e0c002476d71ad4119847082770f95815b07bd779af99cb8c3ad9384fc5513688d0636481bb28306385e63b29576622c017313a7d4e5914c50494b7ebcb1ae9185900437993bf6d0a7777beabc71245301bffa5c99031c67146904c9bb1d8e674562b4cccb6e12f9671cf06efbaaa32e5cc9249ecdefbe8804b88ee86437da83d4e16facccde46a3ce04f5209c1
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !544bd24c1e8c7658f1c0c577ac0f06bac973fdf62c03ea6e8d83d811a2a91f0fea27f9a3bc62dc7fb8db7f03e37e7d35ace19024207a9ebfe3e5e083fc8b0a992e779db4a3c9844f5f7494bec07450cce78224f69c87ad0dde624a286831f3d4485c397cfc8217a517c8705ebd9d758358bc100b90ab18829bd8cec8a1a49b5b91167cafc7eedeac90cd74610c2f281692ade9f3f442b1f618f8c5ba32cd0380478c05281965301c9b3185094aeeb6bfe071d177fcbaef27812aa978b4b21ae66ee546de;
+  1 encin
+       !544bd24c1e8c7658f1c0c577ac0f06bac973fdf62c03ea6e8d83d811a2a91f0fea27f9a3bc62dc7fb8db7f03e37e7d35ace19024207a9ebfe3e5e083fc8b0a992e779db4a3c9844f5f7494bec07450cce78224f69c87ad0dde624a286831f3d4485c397cfc8217a517c8705ebd9d758358bc100b90ab18829bd8cec8a1a49b5b91167cafc7eedeac90cd74610c2f281692ade9f3f442b1f618f8c5ba32cd0380478c05281965301c9b3185094aeeb6bfe071d177fcbaef27812aa978b4b21ae66ee546de
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !956b768925fd4ddf7a44aac550783d04b77bdc179a635c40e818bf2a5c3a450846ea931ec6ed539e50a586fb52ba11611e44a44c224bcbc757d6828757101788c093dfb0cca10d451de2c87fa50662b2e5272e96f89c6d06ec967f61265d6a06381d5b7b14d669336d28b74bc41b56c03a994901cb80a47470b2fcd2e2ea883c7f83271f3f92ed0e9614d90818440cf44c13ebb84df180e7c281ed6660e6d15d12a9776052b802c3ffaa55ec456bc7fc24130ee00ca84fc140f26a47450475467271fe7270;
+  1 encin
+       !956b768925fd4ddf7a44aac550783d04b77bdc179a635c40e818bf2a5c3a450846ea931ec6ed539e50a586fb52ba11611e44a44c224bcbc757d6828757101788c093dfb0cca10d451de2c87fa50662b2e5272e96f89c6d06ec967f61265d6a06381d5b7b14d669336d28b74bc41b56c03a994901cb80a47470b2fcd2e2ea883c7f83271f3f92ed0e9614d90818440cf44c13ebb84df180e7c281ed6660e6d15d12a9776052b802c3ffaa55ec456bc7fc24130ee00ca84fc140f26a47450475467271fe7270
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !c17edf88f543f4f8f358ef7bc02f696d4afca3b561c9fe4b0110aadcd7d4f63cda8269fd39a69f5d4237644608391df0a336ddbb1f58d7e06d6f9e104287ec366cd3a36eeecafbaf56d565cc7b637f3841db2131d3b386094b8acde4cce3a321474134440781b7c3be99cbf38dbbb149521b510034567ef4f555ca5b4d0ab5f3e9370971b785f863547f600edf2031efaa6e31d4dcafa182a122e63d42e4375bb485fefa4faf9f3796529ad44f77e901f56cfb4f9bc960fb4e210f2425d891cf468a3b297fc5;
+  1 encin
+       !c17edf88f543f4f8f358ef7bc02f696d4afca3b561c9fe4b0110aadcd7d4f63cda8269fd39a69f5d4237644608391df0a336ddbb1f58d7e06d6f9e104287ec366cd3a36eeecafbaf56d565cc7b637f3841db2131d3b386094b8acde4cce3a321474134440781b7c3be99cbf38dbbb149521b510034567ef4f555ca5b4d0ab5f3e9370971b785f863547f600edf2031efaa6e31d4dcafa182a122e63d42e4375bb485fefa4faf9f3796529ad44f77e901f56cfb4f9bc960fb4e210f2425d891cf468a3b297fc5
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  2 copy *0 +;
+  0 encout
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !a9174d658536fdd35fe59babd9680f137e003592e3f53e75019ef8147c4ed9e8a68fb125bdfe4b0fb2b583a0dd3d7523b84cc6010b7f694687a51b2aa2d2d48968ce6e07d2c8e831718892a06216a4c2e4038c1b33d1ed75ad212398846dc0119543e8ebaf236725adbef7b9444cc98af397536cd494b60d75d4fe93463f0676e8e116cd5d20d6fca42d2e1bb3e524bf50cb44fe7d141147b9239223717e2fcc7baae339cd0e23502bef19345782cffaa5b6d4cad1936f2dbee4257c07e8456a8efc84e58171e2;
+  3 copy *1 +;
+  1 encin
+       !a9174d658536fdd35fe59babd9680f137e003592e3f53e75019ef8147c4ed9e8a68fb125bdfe4b0fb2b583a0dd3d7523b84cc6010b7f694687a51b2aa2d2d48968ce6e07d2c8e831718892a06216a4c2e4038c1b33d1ed75ad212398846dc0119543e8ebaf236725adbef7b9444cc98af397536cd494b60d75d4fe93463f0676e8e116cd5d20d6fca42d2e1bb3e524bf50cb44fe7d141147b9239223717e2fcc7baae339cd0e23502bef19345782cffaa5b6d4cad1936f2dbee4257c07e8456a8efc84e58171e2
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+
+  ## Some buffering tests.
+  2 begin/ACT *0 +;
+  2 process
+       =XXXXXXXXXXXXXXXXX
+       !a9174d658536fdd35fe59babd9680f137e;
+  2 process
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !003592e3f53e75019ef8147c4ed9e8a68fb125bdfe4b0fb2b583a0dd3d7523b84cc6010b7f694687a51b2aa2d2d48968ce6e07d2c8e831718892a06216a4c2e4038c1b33d1ed75ad212398846dc0119543e8ebaf23;
+  2 process
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+       !6725adbef7b9444cc98af397536cd494b60d75d4fe93463f0676e8e116cd5d20d6fca42d2e1bb3e524bf50cb44fe7d141147b9239223717e2fcc7baae339cd0e23502bef19345782cffaa5b6d4cad1936f2dbee4257c07e8456a8efc84e58171;
+  2 process
+       =X
+       !e2;
+  2 done *0 +;
+
+  3 begin/IACT *0 +;
+  3 process
+       !a9174d658536fdd35fe59babd9680f137e
+       =XXXXXXXXXXXXXXXXX;
+  3 process
+       !003592e3f53e75019ef8147c4ed9e8a68fb125bdfe4b0fb2b583a0dd3d7523b84cc6010b7f694687a51b2aa2d2d48968ce6e07d2c8e831718892a06216a4c2e4038c1b33d1ed75ad212398846dc0119543e8ebaf23
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  3 process
+       !6725adbef7b9444cc98af397536cd494b60d75d4fe93463f0676e8e116cd5d20d6fca42d2e1bb3e524bf50cb44fe7d141147b9239223717e2fcc7baae339cd0e23502bef19345782cffaa5b6d4cad1936f2dbee4257c07e8456a8efc84e58171
+       =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;
+  3 process
+       !e2
+       =X;
+  3 done *0 +;
+
+  ## Back to legible tests.
+  0 prf *123 !45476fc0806aee35e864c4f18e6ba62bd3eb1b1e8bef9042b30b0f15d00c3e9f5d5904ab789d4c67eaed582473c15aa4424f11d52b21a296b36db3392e2ecbb2dc6963bafba3b23882d061f1d335e86e470e8d819591bf0c223e24b925751d04f789fc73bc55f7d2b3ed4881c625aa6321d31511b13f6d5e4ce54a;
+  1 prf *123 !45476fc0806aee35e864c4f18e6ba62bd3eb1b1e8bef9042b30b0f15d00c3e9f5d5904ab789d4c67eaed582473c15aa4424f11d52b21a296b36db3392e2ecbb2dc6963bafba3b23882d061f1d335e86e470e8d819591bf0c223e24b925751d04f789fc73bc55f7d2b3ed4881c625aa6321d31511b13f6d5e4ce54a;
+
+  ## MAC verification tests, positive and negative.
+  2 copy *1 +;
+  2 macin !171419608e11e7c907d493209e17f26b +;
+
+  2 copy *1 +;
+  2 begin/ICT *0 +;
+  2 process !171419608e11e7c907d493209e17f26b +;
+  2 done *0 +;
+
+  2 copy *1 +;
+  2 begin/ICT *0 +;
+  2 process !171419608e11e7c970d493209e17f26b +;
+  2 done *0 -;
+
+  2 copy *1 +;
+  2 ad =spong +;
+  2 macin !171419608e11e7c907d493209e17f26b -;
+
+  ## Resume Hamburg's `Concurrence' test.
+  0 macout *16 !171419608e11e7c907d493209e17f26b;
+  1 macin !171419608e11e7c907d493209e17f26b +;
+
+  ## Additional home-grown tests (see `utils/strobe').
+  0 key "=this is my super-secret key" +;
+  1 key "=this is my super-secret key" +;
+
+  0 prf *32 !5418e0f0ee7f982bbbdc2b4bbf49425d0088abfa98ee21d8ad8a3610d15ebb68;
+  1 prf *32 !5418e0f0ee7f982bbbdc2b4bbf49425d0088abfa98ee21d8ad8a3610d15ebb68;
+
+  0 ratchet *32 +;                     1 ratchet *32 +;
+
+  0 macin !b2084ebdfabd50768c91eebc190132cc +;
+  1 macout *16 !b2084ebdfabd50768c91eebc190132cc;
+}
index 834771c..c4344b3 100755 (executable)
@@ -34,7 +34,7 @@ def poly(nbits):
   base = C.GF(0).setbit(nbits).setbit(0)
   for k in xrange(1, nbits, 2):
     for cc in combs(range(1, nbits), k):
-      p = base + sum(C.GF(0).setbit(c) for c in cc)
+      p = base + sum((C.GF(0).setbit(c) for c in cc), C.GF(0))
       if p.irreduciblep(): POLYMAP[nbits] = p; return p
   raise ValueError, nbits
 
@@ -242,8 +242,8 @@ def omac_masks(E):
   p = poly(8*blksz)
   z = Z(blksz)
   L = E.encrypt(z)
-  m0 = mul_blk_gf(L, 2, p)
-  m1 = mul_blk_gf(m0, 2, p)
+  m0 = mul_blk_gf(L, C.GF(2), p)
+  m1 = mul_blk_gf(m0, C.GF(2), p)
   return m0, m1
 
 def dump_omac(E):
@@ -586,14 +586,14 @@ def pmac2(E, m):
   blksz = E.__class__.blksz
   p = prim(8*blksz)
   L = E.encrypt(Z(blksz))
-  o = mul_blk_gf(L, 10, p)
+  o = mul_blk_gf(L, C.GF(10), p)
   a = Z(blksz)
   v, tl = blocks(m, blksz)
   for x in v:
     a ^= E.encrypt(x ^ o)
-    o = mul_blk_gf(o, 2, p)
-  if len(tl) == blksz: a ^= tl ^ mul_blk_gf(o, 3, p)
-  else: a ^= pad10star(tl, blksz) ^ mul_blk_gf(o, 5, p)
+    o = mul_blk_gf(o, C.GF(2), p)
+  if len(tl) == blksz: a ^= tl ^ mul_blk_gf(o, C.GF(3), p)
+  else: a ^= pad10star(tl, blksz) ^ mul_blk_gf(o, C.GF(5), p)
   return E.encrypt(a)
 
 def ocb3_masks(E):
@@ -740,18 +740,18 @@ def ocb2enc(E, n, h, m, tsz = None):
   if tsz is None: tsz = blksz
   p = prim(8*blksz)
   L = E.encrypt(n)
-  o = mul_blk_gf(L, 2, p)
+  o = mul_blk_gf(L, C.GF(2), p)
   a = Z(blksz)
   v, tl = blocks(m, blksz)
   y = C.WriteBuffer()
   for x in v:
     a ^= x
     y.put(E.encrypt(x ^ o) ^ o)
-    o = mul_blk_gf(o, 2, p)
+    o = mul_blk_gf(o, C.GF(2), p)
   n = len(tl)
   yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ o)
   cfinal = tl ^ yfinal[:n]
-  a ^= (tl + yfinal[n:]) ^ mul_blk_gf(o, 3, p)
+  a ^= (tl + yfinal[n:]) ^ mul_blk_gf(o, C.GF(3), p)
   y.put(cfinal)
   t = E.encrypt(a)
   if h: t ^= pmac2(E, h)
@@ -761,7 +761,7 @@ def ocb2dec(E, n, h, y, t):
   blksz = E.__class__.blksz
   p = prim(8*blksz)
   L = E.encrypt(n)
-  o = mul_blk_gf(L, 2, p)
+  o = mul_blk_gf(L, C.GF(2), p)
   a = Z(blksz)
   v, tl = blocks(y, blksz)
   m = C.WriteBuffer()
@@ -769,11 +769,11 @@ def ocb2dec(E, n, h, y, t):
     u = E.encrypt(x ^ o) ^ o
     y.put(u)
     a ^= u
-    o = mul_blk_gf(o, 2, p)
+    o = mul_blk_gf(o, C.GF(2), p)
   n = len(tl)
   yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ o)
   mfinal = tl ^ yfinal[:n]
-  a ^= (mfinal + yfinal[n:]) ^ mul_blk_gf(o, 3, p)
+  a ^= (mfinal + yfinal[n:]) ^ mul_blk_gf(o, C.GF(3), p)
   m.put(mfinal)
   u = E.encrypt(a)
   if h: u ^= pmac2(E, h)
index e438696..61fb79f 100644 (file)
@@ -5,6 +5,7 @@
 #include <mLib/alloc.h>
 #include <mLib/hex.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 
 #include "ec.h"
 #include "mp.h"
@@ -129,7 +130,7 @@ int main(int argc, char *argv[])
   if ((err = ec_checkinfo(&ei, &rand_global)) != 0)
     fprintf(stderr, "bad curve: %s\n", err);
   puthex("p", ei.c->f->m, 0);
-  if (strcmp(F_NAME(ei.c->f), "binnorm") == 0) {
+  if (STRCMP(F_NAME(ei.c->f), ==, "binnorm")) {
     fctx_binnorm *fc = (fctx_binnorm *)ei.c->f;
     puthex("beta", fc->ntop.r[fc->ntop.n - 1], c->f->noctets);
   }
index ccbf432..4a53737 100755 (executable)
@@ -54,7 +54,7 @@ def poly(nbits):
   base = C.GF(0).setbit(nbits).setbit(0)
   for k in xrange(1, nbits, 2):
     for cc in combs(range(1, nbits), k):
-      p = base + sum(C.GF(0).setbit(c) for c in cc)
+      p = base + sum((C.GF(0).setbit(c) for c in cc), C.GF(0))
       if p.irreduciblep(): POLYMAP[nbits] = p; return p
   raise ValueError, nbits
 
diff --git a/utils/strobe b/utils/strobe
new file mode 100644 (file)
index 0000000..fd0fe57
--- /dev/null
@@ -0,0 +1,147 @@
+#! /usr/bin/python
+
+import catacomb as CAT
+
+I = 1
+A = 2
+C = 4
+T = 8
+M = 16
+INIT = 256
+
+def dump_keccak_state(what, state):
+  buf = CAT.ReadBuffer(state + CAT.ByteString.zero(200 - len(state)))
+  print ";; %s [round 0]" % what
+  print ";; uncomplemented state..."
+  for i in xrange(5):
+    print ";;\t%s" % " ".join(["%016x" % buf.getu64l() for j in xrange(5)])
+  print
+
+class Strobe (object):
+  def __init__(me, sec = 128, _copy = None):
+    if _copy is None:
+      me.k = CAT.Keccak1600()
+      me.r = (1600 - 2*sec)/8
+      me.f = 0
+      me.p0 = 0
+      buf = CAT.WriteBuffer()
+      buf.putu8(1).putu8(me.r)
+      buf.putu8(1).putu8(0)
+      buf.putu8(1).putu8(12*8).put('STROBEv1.0.2')
+      ##dump_keccak_state("init", buf.contents)
+      me.k.mix(buf.contents).step()
+      me.buf = CAT.WriteBuffer()
+      me.mask = me.k.extract(me.r - 2)
+      ##dump_keccak_state("final", me.mask)
+    else:
+      me.k = _copy.k.dup()
+      me.r = _copy.r
+      me.f = _copy.f
+      me.p0 = _copy.p0
+      me.buf = CAT.WriteBuffer().put(_copy.buf.contents)
+      me.mask = me._copy.mask
+
+  def _crank(me):
+    me.buf.putu8(me.p0); me.p0 = 0
+    if me.buf.size == me.r - 1: me.buf.putu8(0x84)
+    else: me.buf.putu8(0x04).zero(me.r - me.buf.size - 1).putu8(0x80)
+    ##dump_keccak_state("buffer", me.buf.contents)
+    ##dump_keccak_state("init", me.buf.contents ^ me.k.extract(me.r))
+    me.k.mix(me.buf.contents).step()
+    me.mask = me.k.extract(me.r - 2)
+    ##dump_keccak_state("final", me.mask)
+    me.buf = CAT.WriteBuffer()
+
+  def process(me, op, input):
+    if not op&T:
+      opf = op
+    else:
+      if not me.f&INIT: me.f |= INIT | (op&I)
+      opf = op ^ (me.f&255)
+    me.buf.putu8(me.p0); me.p0 = me.buf.size
+    if me.buf.size >= me.r - 2: me._crank()
+    me.buf.putu8(opf)
+    if op&C or me.buf.size >= me.r - 2: me._crank()
+
+    if op&(I | T) == I or op&(I | A) == 0: buf = CAT.ByteString.zero(input)
+    else: buf = CAT.ByteString(input)
+    out = CAT.WriteBuffer()
+    i, j, sz = 0, me.buf.size, len(buf)
+
+    while i < sz:
+      spare = me.r - j - 2
+      n = min(spare, sz - i); x = buf[i:i + n]; i += n
+      if not op&C: out.put(x); me.buf.put(x)
+      else:
+        y = x ^ me.mask[j:j + n]; out.put(y)
+        if op&I or not op&T: me.buf.put(y)
+        else: me.buf.put(x)
+      if n == spare: me._crank(); j = 0
+
+    if op&(I | A | C | T) != (I | C | T): return out.contents
+    elif out.contents == CAT.ByteString.zero(out.size): return me
+    else: raise ValueError, 'verify failed'
+
+st0 = Strobe()
+st1 = Strobe()
+
+st0.process(A | M, 'testing')
+st1.process(A | M, 'testing')
+
+print 'prf(10) -> %s' % hex(st0.process(I | A | C, 10))
+st1.process(I | A | C, 10)
+
+print 'ad("Hello")'
+st0.process(A, 'Hello')
+st1.process(A, 'Hello')
+
+c0 = st0.process(A | C | T, 'World');
+st1.process(I | A | C | T, c0)
+print 'encout("World") -> %s' %  hex(c0)
+
+print 'clrout("foo")'
+st0.process(A | T, 'foo');
+st1.process(I | A | T, 'foo')
+
+print 'clrin("bar")'
+st1.process(A | T, 'bar')
+st0.process(I | A | T, 'bar')
+
+m1 = st0.process(I | A | C | T, 'baz')
+st1.process(A | C | T, m1) #  backwards in time
+print 'encin("baz") -> %s' % hex(m1)
+
+for i in xrange(200):
+  c = st0.process(A | C | T, i*'X')
+  m = st1.process(I | A | C | T, c)
+  print "  0 encout\n\t=%s\n\t!%s;" % (i*'X', hex(c))
+  print "  1 encin\n\t!%s\n\t=%s;" % (hex(c), i*'X')
+
+r0 = st0.process(I | A | C, 123)
+r1 = st1.process(I | A | C, 123)
+print 'prf(123) -> %s' % hex(r0)
+assert r0 == r1
+
+t = st0.process(C | T, 16)
+print 'macout(16) -> %s' % hex(t)
+st1.process(I | C | T, t)
+
+k = 'this is my super-secret key'
+print 'key("%s")' % k
+st0.process(A | C, k)
+st1.process(A | C, k)
+
+r0 = st0.process(I | A | C, 32)
+r1 = st1.process(I | A | C, 32)
+print 'prf(32) -> %s' % hex(r0)
+assert r0 == r1
+
+print 'ratchet(32)'
+st0.process(C, 32)
+st1.process(C, 32)
+
+t0 = CAT.bytes('b2084ebdfabd50768c91eebc190132cc')
+st0.process(I | C | T, t0)
+t1 = st1.process(C | T, 16)
+assert t1 == t0
+print 'macin(%s)' % hex(t0)