chiark / gitweb /
New multiprecision integer arithmetic suite.
[catacomb] / mptext.c
1 /* -*-c-*-
2  *
3  * $Id: mptext.c,v 1.1 1999/11/17 18:02:16 mdw Exp $
4  *
5  * Textual representation of multiprecision numbers
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Catacomb.
13  *
14  * Catacomb is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * Catacomb is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with Catacomb; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: mptext.c,v $
33  * Revision 1.1  1999/11/17 18:02:16  mdw
34  * New multiprecision integer arithmetic suite.
35  *
36  */
37
38 /*----- Header files ------------------------------------------------------*/
39
40 #include <ctype.h>
41 #include <stdio.h>
42
43 #include <mLib/darray.h>
44
45 #include "mp.h"
46 #include "mptext.h"
47
48 /*----- Data structures ---------------------------------------------------*/
49
50 #ifndef CHARV
51    DA_DECL(charv, char);
52 #  define CHARV
53 #endif
54
55 /*----- Main code ---------------------------------------------------------*/
56
57 /* --- @mp_read@ --- *
58  *
59  * Arguments:   @mp *m@ = destination multiprecision number
60  *              @int radix@ = base to assume for data (or zero to guess)
61  *              @const mptext_ops *ops@ = pointer to operations block
62  *              @void *p@ = data for the operations block
63  *
64  * Returns:     The integer read, or zero if it didn't work.
65  *
66  * Use:         Reads an integer from some source.  If the @radix@ is
67  *              specified, the number is assumed to be given in that radix,
68  *              with the letters `a' (either upper- or lower-case) upwards
69  *              standing for digits greater than 9.  Otherwise, base 10 is
70  *              assumed unless the number starts with `0' (octal), `0x' (hex)
71  *              or `nnn_' (base `nnn').  An arbitrary amount of whitespace
72  *              before the number is ignored.
73  */
74
75 mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
76 {
77   int r;
78   int ch;
79   unsigned f = 0;
80
81   enum {
82     f_neg = 1u,
83     f_ok = 2u
84   };
85
86   /* --- Initialize the destination number --- */
87
88   MP_MODIFY(m, 16);
89   m->vl = m->v;
90   m->f &= ~MP_UNDEF;
91
92   /* --- Read an initial character --- */
93
94   ch = ops->get(p);
95   while (isspace(ch))
96     ch = ops->get(p);
97
98   /* --- Handle an initial sign --- */
99
100   if (ch == '-') {
101     f |= f_neg;
102     ch = ops->get(p);
103     while (isspace(ch))
104       ch = ops->get(p);
105   }
106
107   /* --- If the radix is zero, look for leading zeros --- */
108
109   if (radix)
110     r = -1;
111   else if (ch != '0') {
112     radix = 10;
113     r = 0;
114   } else {
115     ch = ops->get(p);
116     if (ch == 'x') {
117       ch = ops->get(p);
118       radix = 16;
119     } else {
120       radix = 8;
121       f |= f_ok;
122     }
123     r = -1;
124   }
125
126   /* --- Time to start --- */
127
128   for (;; ch = ops->get(p)) {
129     int x;
130
131     /* --- An underscore indicates a numbered base --- */
132
133     if (ch == '_' && r > 0 && r <= 36) {
134       radix = r;
135       m->vl = m->v;
136       r = -1;
137       f &= ~f_ok;
138       continue;
139     }
140
141     /* --- Check that the character is a digit and in range --- */
142
143     if (!isalnum(ch))
144       break;
145     if (ch >= '0' && ch <= '9')
146       x = ch - '0';
147     else {
148       ch = tolower(ch);
149       if (ch >= 'a' && ch <= 'z')       /* ASCII dependent! */
150         x = ch - 'a' + 10;
151       else
152         break;
153     }
154
155     /* --- Sort out what to do with the character --- */
156
157     if (x >= 10 && r >= 0)
158       r = -1;
159     if (x >= radix)
160       break;
161
162     if (r >= 0)
163       r = r * 10 + x;
164
165     /* --- Stick the character on the end of my integer --- */
166
167     MP_ENSURE(m, MP_LEN(m) + 1);
168     MPX_UMULN(m->v, m->vl, m->v, m->vl - 1, radix);
169     MPX_UADDN(m->v, m->vl, x);
170     MP_SHRINK(m);
171     f |= f_ok;
172   }
173
174   ops->unget(ch, p);
175
176   /* --- Bail out if the number was bad --- */
177
178   if (!(f & f_ok)) {
179     MP_DROP(m);
180     return (0);
181   }
182
183   /* --- Set the sign and return --- */
184
185   m->f = 0;
186   if (f & f_neg)
187     m->f |= MP_NEG;
188   return (m);
189 }
190
191 /* --- @mp_write@ --- *
192  *
193  * Arguments:   @mp *m@ = pointer to a multi-precision integer
194  *              @int radix@ = radix to use when writing the number out
195  *              @const mptext_ops *ops@ = pointer to an operations block
196  *              @void *p@ = data for the operations block
197  *
198  * Returns:     Zero if it worked, nonzero otherwise.
199  *
200  * Use:         Writes a large integer in textual form.
201  */
202
203 int mp_write(mp *m, int radix, const mptext_ops *ops, void *p)
204 {
205   charv v = DA_INIT;
206   mpw b = radix;
207   mp bb;
208
209   /* --- Set various things up --- */
210
211   m = MP_COPY(m);
212   mp_build(&bb, &b, &b + 1);
213
214   /* --- If the number is negative, sort that out --- */
215
216   if (m->f & MP_NEG) {
217     if (ops->put("-", 1, p))
218       return (EOF);
219     MP_SPLIT(m);
220     m->f &= ~MP_NEG;
221   }
222
223   /* --- Write digits to a temporary array --- */
224
225   do {
226     mp *q = MP_NEW, *r = MP_NEW;
227     int ch;
228     mpw x;
229
230     mp_div(&q, &r, m, &bb);
231     x = r->v[0];
232     if (x < 10)
233       ch = '0' + x;
234     else
235       ch = 'a' + x - 10;
236     DA_UNSHIFT(&v, ch);
237     MP_DROP(m);
238     MP_DROP(r);
239     m = q;
240   } while (MP_CMP(m, !=, MP_ZERO));
241
242   /* --- Finished that --- */
243
244   MP_DROP(m);
245
246   {
247     int rc = ops->put(DA(&v), DA_LEN(&v), p);
248     DA_DESTROY(&v);
249     return (rc ? EOF : 0);
250   }
251 }
252
253 /*----- Test rig ----------------------------------------------------------*/
254
255 #ifdef TEST_RIG
256
257 #include <mLib/testrig.h>
258
259 static int verify(dstr *v)
260 {
261   int ok = 1;
262   int ib = *(int *)v[0].buf, ob = *(int *)v[2].buf;
263   dstr d = DSTR_INIT;
264   mp *m = mp_readdstr(MP_NEW, &v[1], 0, ib);
265   if (m) {
266     if (!ob) {
267       fprintf(stderr, "*** unexpected successful parse\n"
268                       "*** input [%i] = %s\n",
269               ib, v[1].buf);
270       mp_writedstr(m, &d, 10);
271       fprintf(stderr, "*** (value = %s)\n", d.buf);
272       ok = 0;
273     } else {
274       mp_writedstr(m, &d, ob);
275       if (d.len != v[3].len || memcmp(d.buf, v[3].buf, d.len) != 0) {
276         fprintf(stderr, "*** failed read or write\n"
277                         "*** input [%i]    = %s\n"
278                         "*** output [%i]   = %s\n"
279                         "*** expected [%i] = %s\n",
280                 ib, v[1].buf, ob, d.buf, ob, v[3].buf);
281         ok = 0;
282       }
283     }
284     mp_drop(m);
285   } else {
286     if (ob) {
287       fprintf(stderr, "*** unexpected parse failure\n"
288                       "*** input [%i] = %s\n"
289                       "*** expected [%i] = %s\n",
290               ib, v[1].buf, ob, v[3].buf);
291       ok = 0;
292     }
293   }
294
295   dstr_destroy(&d);
296   return (ok);
297 }
298
299 static test_chunk tests[] = {
300   { "mptext", verify,
301     { &type_int, &type_string, &type_int, &type_string, 0 } },
302   { 0, 0, { 0 } }
303 };
304
305 int main(int argc, char *argv[])
306 {
307   sub_init();
308   test_run(argc, argv, tests, SRCDIR "/tests/mptext");
309   return (0);
310 }
311
312 #endif
313
314 /*----- That's all, folks -------------------------------------------------*/