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