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