Release 2.1.3.
[u/mdw/catacomb] / mptext.c
index fcdac51..8c00e34 100644 (file)
--- a/mptext.c
+++ b/mptext.c
@@ -1,13 +1,13 @@
 /* -*-c-*-
  *
- * $Id: mptext.c,v 1.16 2002/10/15 22:57:43 mdw Exp $
+ * $Id$
  *
  * Textual representation of multiprecision numbers
  *
  * (c) 1999 Straylight/Edgeware
  */
 
-/*----- Licensing notice --------------------------------------------------* 
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of Catacomb.
  *
  * 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.
  */
 
-/*----- Revision history --------------------------------------------------* 
- *
- * $Log: mptext.c,v $
- * Revision 1.16  2002/10/15 22:57:43  mdw
- * Bug fix: prevent negative zero.
- *
- * Revision 1.15  2002/10/15 19:18:15  mdw
- * Fix fencepost bugs in binary radix writing.
- *
- * Revision 1.14  2002/10/09 00:33:44  mdw
- * Allow `0o' and `0b' prefixes for octal and binary (from Haskell)
- *
- * Revision 1.13  2002/10/09 00:21:06  mdw
- * Allow user-specified `r_xx' bases to be up to 62.
- *
- * Revision 1.12  2002/01/13 19:51:18  mdw
- * Extend the textual format to bases up to 62 by distinguishing case.
- *
- * Revision 1.11  2001/06/16 23:42:17  mdw
- * Typesetting fixes.
- *
- * Revision 1.10  2001/06/16 13:22:39  mdw
- * Added fast-track code for binary output bases, and tests.
- *
- * Revision 1.9  2001/02/03 16:05:17  mdw
- * Make flags be unsigned.  Improve the write algorithm: recurse until the
- * parts are one word long and use single-precision arithmetic from there.
- * Fix off-by-one bug when breaking the number apart.
- *
- * Revision 1.8  2000/12/06 20:32:42  mdw
- * Reduce binary bytes (to allow marker bits to be ignored).  Fix error
- * message string a bit.  Allow leading `+' signs.
- *
- * Revision 1.7  2000/07/15 10:01:08  mdw
- * Bug fix in binary input.
- *
- * Revision 1.6  2000/06/25 12:58:23  mdw
- * Fix the derivation of `depth' commentary.
- *
- * Revision 1.5  2000/06/17 11:46:19  mdw
- * New and much faster stack-based algorithm for reading integers.  Support
- * reading and writing binary integers in bases between 2 and 256.
- *
- * Revision 1.4  1999/12/22 15:56:56  mdw
- * Use clever recursive algorithm for writing numbers out.
- *
- * Revision 1.3  1999/12/10 23:23:26  mdw
- * Allocate slightly less memory.
- *
- * Revision 1.2  1999/11/20 22:24:15  mdw
- * Use function versions of MPX_UMULN and MPX_UADDN.
- *
- * Revision 1.1  1999/11/17 18:02:16  mdw
- * New multiprecision integer arithmetic suite.
- *
- */
-
 /*----- Header files ------------------------------------------------------*/
 
 #include <ctype.h>
@@ -98,7 +41,7 @@
 
 /* --- Maximum recursion depth --- *
  *
- * This is the number of bits in a @size_t@ object.  Why? 
+ * This is the number of bits in a @size_t@ object.  Why?
  *
  * To see this, let %$b = \textit{MPW\_MAX} + 1$% and let %$Z$% be the
  * largest @size_t@ value.  Then the largest possible @mp@ is %$M - 1$% where
@@ -180,7 +123,7 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
   /* --- Initialize the stacks --- */
 
   mp_build(&rr, &rd, &rd + 1);
-  pow[0] = &rr;  
+  pow[0] = &rr;
   pows = 1;
 
   sp = 0;
@@ -193,8 +136,10 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
   /* --- Read an initial character --- */
 
   ch = ops->get(p);
-  while (isspace(ch))
-    ch = ops->get(p);
+  if (radix >= 0) {
+    while (isspace(ch))
+      ch = ops->get(p);
+  }
 
   /* --- Handle an initial sign --- */
 
@@ -212,7 +157,7 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
     r = -1;
   } else if (radix < 0) {
     rd = -radix;
-    assert(((void)"binary radix must fit in a byte", rd < UCHAR_MAX));
+    assert(((void)"binary radix must fit in a byte", rd <= UCHAR_MAX));
     r = -1;
   } else if (ch != '0') {
     rd = 10;
@@ -319,7 +264,7 @@ restart:
        a |= MPW(x) << b;
       } else {
        a |= MPW(x) >> (bit - b);
-       b += MPW_BITS - bit;
+       b += MPW_BITS - bit;
        *--v = MPW(a);
        n--;
        if (!n) {
@@ -348,6 +293,7 @@ restart:
       m->f &= ~MP_UNDEF;
       m = mp_lsr(m, m, (unsigned long)n * MPW_BITS + b);
     }
+    ops->unget(ch, p);
     goto done;
   }}
 
@@ -393,7 +339,7 @@ restart:
        x = ch - '0';
       else {
        if (rd <= 36)
-         ch = tolower(ch);       
+         ch = tolower(ch);
        if (ch >= 'a' && ch <= 'z')     /* ASCII dependent! */
          x = ch - 'a' + 10;
        else if (ch >= 'A' && ch <= 'Z')
@@ -497,7 +443,7 @@ restart:
   /* --- Bail out if the number was bad --- */
 
 done:
-  if (!(f & f_ok))    
+  if (!(f & f_ok))
     return (0);
 
   /* --- Set the sign and return --- */
@@ -598,7 +544,7 @@ static int complicated(mp *m, int radix, mp **pr, unsigned i, unsigned z,
 
   assert(i);
   mp_div(&q, &m, m, pr[i]);
-  if (!MP_LEN(q))
+  if (MP_ZEROP(q))
     d = z;
   else {
     if (z > d)
@@ -713,7 +659,7 @@ int mp_write(mp *m, int radix, const mptext_ops *ops, void *p)
   int rc;
 
   if (MP_EQ(m, MP_ZERO))
-    return (ops->put("0", 1, p));
+    return (ops->put(radix > 0 ? "0" : "\0", 1, p));
 
   /* --- Set various things up --- */
 
@@ -725,13 +671,14 @@ int mp_write(mp *m, int radix, const mptext_ops *ops, void *p)
   if (radix > 0)
     assert(((void)"ascii radix must be <= 62", radix <= 62));
   else if (radix < 0)
-    assert(((void)"binary radix must fit in a byte", -radix < UCHAR_MAX));
+    assert(((void)"binary radix must fit in a byte", -radix <= UCHAR_MAX));
   else
     assert(((void)"radix can't be zero in mp_write", 0));
 
   /* --- If the number is negative, sort that out --- */
 
-  if (m->f & MP_NEG) {
+  if (MP_NEGP(m)) {
+    assert(radix > 0);
     if (ops->put("-", 1, p))
       return (EOF);
     m->f &= ~MP_NEG;
@@ -745,8 +692,8 @@ int mp_write(mp *m, int radix, const mptext_ops *ops, void *p)
     case   8: case   -8: return (binary(m, 3, radix, ops, p));
     case  16: case  -16: return (binary(m, 4, radix, ops, p));
     case  32: case  -32: return (binary(m, 5, radix, ops, p));
-              case  -64: return (binary(m, 6, radix, ops, p));
-              case -128: return (binary(m, 7, radix, ops, p));
+             case  -64: return (binary(m, 6, radix, ops, p));
+             case -128: return (binary(m, 7, radix, ops, p));
   }
 
   /* --- If the number is small, do it the easy way --- */
@@ -808,11 +755,12 @@ static int verify(dstr *v)
   int ok = 1;
   int ib = *(int *)v[0].buf, ob = *(int *)v[2].buf;
   dstr d = DSTR_INIT;
-  mp *m = mp_readdstr(MP_NEW, &v[1], 0, ib);
+  size_t off = 0;
+  mp *m = mp_readdstr(MP_NEW, &v[1], &off, ib);
   if (m) {
     if (!ob) {
       fprintf(stderr, "*** unexpected successful parse\n"
-                     "*** input [%2i] =     ", ib);
+                     "*** input [%2i] =     ", ib);
       if (ib < 0)
        type_hex.dump(&v[1], stderr);
       else
@@ -824,17 +772,17 @@ static int verify(dstr *v)
       mp_writedstr(m, &d, ob);
       if (d.len != v[3].len || memcmp(d.buf, v[3].buf, d.len) != 0) {
        fprintf(stderr, "*** failed read or write\n"
-                       "*** input [%2i]      = ", ib);
+                       "*** input [%2i]      = ", ib);
        if (ib < 0)
          type_hex.dump(&v[1], stderr);
        else
          fputs(v[1].buf, stderr);
-       fprintf(stderr, "\n*** output [%2i]     = ", ob);
+       fprintf(stderr, "\n*** output [%2i]     = ", ob);
        if (ob < 0)
          type_hex.dump(&d, stderr);
        else
          fputs(d.buf, stderr);
-       fprintf(stderr, "\n*** expected [%2i]   = ", ob);
+       fprintf(stderr, "\n*** expected [%2i]   = ", ob);
        if (ob < 0)
          type_hex.dump(&v[3], stderr);
        else
@@ -847,12 +795,12 @@ static int verify(dstr *v)
   } else {
     if (ob) {
       fprintf(stderr, "*** unexpected parse failure\n"
-                     "*** input [%i]    = ", ib);
+                     "*** input [%2i]    = ", ib);
       if (ib < 0)
        type_hex.dump(&v[1], stderr);
       else
        fputs(v[1].buf, stderr);
-      fprintf(stderr, "\n*** expected [%i]   = ", ob);
+      fprintf(stderr, "\n*** expected [%2i]   = ", ob);
       if (ob < 0)
        type_hex.dump(&v[3], stderr);
       else
@@ -862,6 +810,20 @@ 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) {
+    fprintf(stderr, "*** leftovers incorrect\n"
+                   "*** input [%2i]    = ", ib);
+    if (ib < 0)
+      type_hex.dump(&v[1], stderr);
+    else
+      fputs(v[1].buf, stderr);
+    fprintf(stderr, "\n*** expected `%s'\n"
+                   "*** found `%s'\n",
+           v[4].buf, v[1].buf + off);
+    ok = 0;
+  }
+
   dstr_destroy(&d);
   assert(mparena_count(MPARENA_GLOBAL) == 0);
   return (ok);
@@ -869,11 +831,11 @@ static int verify(dstr *v)
 
 static test_chunk tests[] = {
   { "mptext-ascii", verify,
-    { &type_int, &type_string, &type_int, &type_string, 0 } },
+    { &type_int, &type_string, &type_int, &type_string, &type_string, 0 } },
   { "mptext-bin-in", verify,
-    { &type_int, &type_hex, &type_int, &type_string, 0 } },
+    { &type_int, &type_hex, &type_int, &type_string, &type_string, 0 } },
   { "mptext-bin-out", verify,
-    { &type_int, &type_string, &type_int, &type_hex, 0 } },
+    { &type_int, &type_string, &type_int, &type_hex, &type_string, 0 } },
   { 0, 0, { 0 } }
 };