1570af302Sopenharmony_ci/*
2570af302Sopenharmony_ci./gen can generate testcases using an mp lib
3570af302Sopenharmony_ci./check can test an mp lib compared to the input
4570af302Sopenharmony_ci
5570af302Sopenharmony_ciinput format:
6570af302Sopenharmony_ciT.<rounding>.<inputs>.<outputs>.<outputerr>.<exceptflags>.
7570af302Sopenharmony_ciwhere . is a sequence of separators: " \t,(){}"
8570af302Sopenharmony_cithe T prefix and rounding mode are optional (default is RN),
9570af302Sopenharmony_ciso the following are all ok and equivalent input:
10570af302Sopenharmony_ci
11570af302Sopenharmony_ci 1 2.0 0.1 INEXACT
12570af302Sopenharmony_ci {RN, 1, 2.0, 0.1, INEXACT},
13570af302Sopenharmony_ci T(RN, 1, 2.0, 0.1, INEXACT)
14570af302Sopenharmony_ci
15570af302Sopenharmony_cifor gen only rounding and inputs are required (the rest is discarded)
16570af302Sopenharmony_ci
17570af302Sopenharmony_cigen:
18570af302Sopenharmony_ci	s = getline()
19570af302Sopenharmony_ci	x = scan(s)
20570af302Sopenharmony_ci	xy = mpfunc(x)
21570af302Sopenharmony_ci	print(xy)
22570af302Sopenharmony_cicheck:
23570af302Sopenharmony_ci	s = getline()
24570af302Sopenharmony_ci	xy = scan(s)
25570af302Sopenharmony_ci	xy' = mpfunc(x)
26570af302Sopenharmony_ci	check(xy, xy')
27570af302Sopenharmony_ci*/
28570af302Sopenharmony_ci
29570af302Sopenharmony_ci#include <stdlib.h>
30570af302Sopenharmony_ci#include <stdio.h>
31570af302Sopenharmony_ci#include <string.h>
32570af302Sopenharmony_ci#include "gen.h"
33570af302Sopenharmony_ci
34570af302Sopenharmony_cistatic int scan(const char *fmt, struct t *t, char *buf);
35570af302Sopenharmony_cistatic int print(const char *fmt, struct t *t, char *buf, int n);
36570af302Sopenharmony_ci
37570af302Sopenharmony_ci// TODO: many output, fmt->ulp
38570af302Sopenharmony_cistruct fun;
39570af302Sopenharmony_cistatic int check(struct t *want, struct t *got, struct fun *f, float ulpthres, float *abserr);
40570af302Sopenharmony_ci
41570af302Sopenharmony_cistruct fun {
42570af302Sopenharmony_ci	char *name;
43570af302Sopenharmony_ci	int (*mpf)(struct t*);
44570af302Sopenharmony_ci	char *fmt;
45570af302Sopenharmony_ci} fun[] = {
46570af302Sopenharmony_ci#define T(f,t) {#f, mp##f, #t},
47570af302Sopenharmony_ci#include "functions.h"
48570af302Sopenharmony_ci#undef T
49570af302Sopenharmony_ci};
50570af302Sopenharmony_ci
51570af302Sopenharmony_ciint main(int argc, char *argv[])
52570af302Sopenharmony_ci{
53570af302Sopenharmony_ci	char buf[512];
54570af302Sopenharmony_ci	char *p;
55570af302Sopenharmony_ci	int checkmode;
56570af302Sopenharmony_ci	int i;
57570af302Sopenharmony_ci	struct t t;
58570af302Sopenharmony_ci	struct t tread;
59570af302Sopenharmony_ci	struct fun *f = 0;
60570af302Sopenharmony_ci	double ulpthres = 1.0;
61570af302Sopenharmony_ci	float maxerr = 0;
62570af302Sopenharmony_ci	float abserr;
63570af302Sopenharmony_ci	struct t terr;
64570af302Sopenharmony_ci
65570af302Sopenharmony_ci	p = strrchr(argv[0], '/');
66570af302Sopenharmony_ci	if (!p)
67570af302Sopenharmony_ci		p = argv[0];
68570af302Sopenharmony_ci	else
69570af302Sopenharmony_ci		p++;
70570af302Sopenharmony_ci	checkmode = strcmp(p, "check") == 0;
71570af302Sopenharmony_ci	if (argc < 2) {
72570af302Sopenharmony_ci		fprintf(stderr, "%s func%s\n", argv[0], checkmode ? " ulpthres" : "");
73570af302Sopenharmony_ci		return 1;
74570af302Sopenharmony_ci	}
75570af302Sopenharmony_ci	if (argc > 2 && checkmode) {
76570af302Sopenharmony_ci		ulpthres = strtod(argv[2], &p);
77570af302Sopenharmony_ci		if (*p) {
78570af302Sopenharmony_ci			fprintf(stderr, "invalid ulperr %s\n", argv[2]);
79570af302Sopenharmony_ci			return 1;
80570af302Sopenharmony_ci		}
81570af302Sopenharmony_ci	}
82570af302Sopenharmony_ci	for (i = 0; i < sizeof fun/sizeof *fun; i++)
83570af302Sopenharmony_ci		if (strcmp(fun[i].name, argv[1]) == 0) {
84570af302Sopenharmony_ci			f = fun + i;
85570af302Sopenharmony_ci			break;
86570af302Sopenharmony_ci		}
87570af302Sopenharmony_ci	if (f == 0) {
88570af302Sopenharmony_ci		fprintf(stderr, "unknown func: %s\n", argv[1]);
89570af302Sopenharmony_ci		return 1;
90570af302Sopenharmony_ci	}
91570af302Sopenharmony_ci	for (i = 1; fgets(buf, sizeof buf, stdin); i++) {
92570af302Sopenharmony_ci		dropcomm(buf);
93570af302Sopenharmony_ci		if (*buf == 0 || *buf == '\n')
94570af302Sopenharmony_ci			continue;
95570af302Sopenharmony_ci		memset(&t, 0, sizeof t);
96570af302Sopenharmony_ci		if (scan(f->fmt, &t, buf))
97570af302Sopenharmony_ci			fprintf(stderr, "error scan %s, line %d\n", f->name, i);
98570af302Sopenharmony_ci		tread = t;
99570af302Sopenharmony_ci		if (f->mpf(&t))
100570af302Sopenharmony_ci			fprintf(stderr, "error mpf %s, line %d\n", f->name, i);
101570af302Sopenharmony_ci		if (checkmode) {
102570af302Sopenharmony_ci			if (check(&tread, &t, f, ulpthres, &abserr)) {
103570af302Sopenharmony_ci				print(f->fmt, &tread, buf, sizeof buf);
104570af302Sopenharmony_ci				fputs(buf, stdout);
105570af302Sopenharmony_ci//				print(f->fmt, &t, buf, sizeof buf);
106570af302Sopenharmony_ci//				fputs(buf, stdout);
107570af302Sopenharmony_ci			}
108570af302Sopenharmony_ci			if (abserr > maxerr) {
109570af302Sopenharmony_ci				maxerr = abserr;
110570af302Sopenharmony_ci				terr = tread;
111570af302Sopenharmony_ci			}
112570af302Sopenharmony_ci		} else {
113570af302Sopenharmony_ci			if (print(f->fmt, &t, buf, sizeof buf))
114570af302Sopenharmony_ci				fprintf(stderr, "error fmt %s, line %d\n", f->name, i);
115570af302Sopenharmony_ci			fputs(buf, stdout);
116570af302Sopenharmony_ci		}
117570af302Sopenharmony_ci	}
118570af302Sopenharmony_ci	if (checkmode && maxerr) {
119570af302Sopenharmony_ci		printf("// maxerr: %f, ", maxerr);
120570af302Sopenharmony_ci		print(f->fmt, &terr, buf, sizeof buf);
121570af302Sopenharmony_ci		fputs(buf, stdout);
122570af302Sopenharmony_ci	}
123570af302Sopenharmony_ci	return 0;
124570af302Sopenharmony_ci}
125570af302Sopenharmony_ci
126570af302Sopenharmony_cistatic int check(struct t *want, struct t *got, struct fun *f, float ulpthres, float *abserr)
127570af302Sopenharmony_ci{
128570af302Sopenharmony_ci	int err = 0;
129570af302Sopenharmony_ci	int m = INEXACT|UNDERFLOW; // TODO: dont check inexact and underflow for now
130570af302Sopenharmony_ci
131570af302Sopenharmony_ci	if ((got->e|m) != (want->e|m)) {
132570af302Sopenharmony_ci		fprintf(stdout, "//%s %s(%La,%La)==%La except: want %s",
133570af302Sopenharmony_ci			rstr(want->r), f->name, want->x, want->x2, want->y, estr(want->e));
134570af302Sopenharmony_ci		fprintf(stdout, " got %s\n", estr(got->e));
135570af302Sopenharmony_ci		err++;
136570af302Sopenharmony_ci	}
137570af302Sopenharmony_ci	if (isnan(got->y) && isnan(want->y))
138570af302Sopenharmony_ci		return err;
139570af302Sopenharmony_ci	if (got->y != want->y || signbit(got->y) != signbit(want->y)) {
140570af302Sopenharmony_ci		char *p;
141570af302Sopenharmony_ci		int n;
142570af302Sopenharmony_ci		float d;
143570af302Sopenharmony_ci
144570af302Sopenharmony_ci		p = strchr(f->fmt, '_');
145570af302Sopenharmony_ci		if (!p)
146570af302Sopenharmony_ci			return -1;
147570af302Sopenharmony_ci		p++;
148570af302Sopenharmony_ci		if (*p == 'd')
149570af302Sopenharmony_ci			n = eulp(want->y);
150570af302Sopenharmony_ci		else if (*p == 'f')
151570af302Sopenharmony_ci			n = eulpf(want->y);
152570af302Sopenharmony_ci		else if (*p == 'l')
153570af302Sopenharmony_ci			n = eulpl(want->y);
154570af302Sopenharmony_ci		else
155570af302Sopenharmony_ci			return -1;
156570af302Sopenharmony_ci
157570af302Sopenharmony_ci		d = scalbnl(got->y - want->y, -n);
158570af302Sopenharmony_ci		*abserr = fabsf(d + want->dy);
159570af302Sopenharmony_ci		if (*abserr <= ulpthres)
160570af302Sopenharmony_ci			return err;
161570af302Sopenharmony_ci		fprintf(stdout, "//%s %s(%La,%La) want %La got %La ulperr %.3f = %a + %a\n",
162570af302Sopenharmony_ci			rstr(want->r), f->name, want->x, want->x2, want->y, got->y, d + want->dy, d, want->dy);
163570af302Sopenharmony_ci		err++;
164570af302Sopenharmony_ci	}
165570af302Sopenharmony_ci	return err;
166570af302Sopenharmony_ci}
167570af302Sopenharmony_ci
168570af302Sopenharmony_ci// scan discards suffixes, this may cause rounding issues (eg scanning 0.1f as long double)
169570af302Sopenharmony_cistatic int scan1(long double *x, char *s, int fmt)
170570af302Sopenharmony_ci{
171570af302Sopenharmony_ci	double d;
172570af302Sopenharmony_ci	float f;
173570af302Sopenharmony_ci
174570af302Sopenharmony_ci	if (fmt == 'd') {
175570af302Sopenharmony_ci		if (sscanf(s, "%lf", &d) != 1)
176570af302Sopenharmony_ci			return -1;
177570af302Sopenharmony_ci		*x = d;
178570af302Sopenharmony_ci	} else if (fmt == 'f') {
179570af302Sopenharmony_ci		if (sscanf(s, "%f", &f) != 1)
180570af302Sopenharmony_ci			return -1;
181570af302Sopenharmony_ci		*x = f;
182570af302Sopenharmony_ci	} else if (fmt == 'l') {
183570af302Sopenharmony_ci		return sscanf(s, "%Lf", x) != 1;
184570af302Sopenharmony_ci	} else
185570af302Sopenharmony_ci		return -1;
186570af302Sopenharmony_ci	return 0;
187570af302Sopenharmony_ci}
188570af302Sopenharmony_ci
189570af302Sopenharmony_cistatic int scan(const char *fmt, struct t *t, char *buf)
190570af302Sopenharmony_ci{
191570af302Sopenharmony_ci	char *a[20];
192570af302Sopenharmony_ci	long double *b[4];
193570af302Sopenharmony_ci	long double dy, dy2;
194570af302Sopenharmony_ci	char *end;
195570af302Sopenharmony_ci	int n, i=0, j=0;
196570af302Sopenharmony_ci
197570af302Sopenharmony_ci	buf = skipstr(buf, "T \t\r\n,(){}");
198570af302Sopenharmony_ci	n = splitstr(a, sizeof a/sizeof *a, buf, " \t\r\n,(){}");
199570af302Sopenharmony_ci	if (n <= 0)
200570af302Sopenharmony_ci		return -1;
201570af302Sopenharmony_ci	if (a[0][0] == 'R') {
202570af302Sopenharmony_ci		if (rconv(&t->r, a[i++]))
203570af302Sopenharmony_ci			return -1;
204570af302Sopenharmony_ci	} else
205570af302Sopenharmony_ci		t->r = RN;
206570af302Sopenharmony_ci
207570af302Sopenharmony_ci	b[0] = &t->x;
208570af302Sopenharmony_ci	b[1] = &t->x2;
209570af302Sopenharmony_ci	b[2] = &t->x3;
210570af302Sopenharmony_ci	b[3] = 0;
211570af302Sopenharmony_ci	for (; *fmt && *fmt != '_'; fmt++) {
212570af302Sopenharmony_ci		if (i >= n)
213570af302Sopenharmony_ci			return -1;
214570af302Sopenharmony_ci		if (*fmt == 'i') {
215570af302Sopenharmony_ci			t->i = strtoll(a[i++], &end, 0);
216570af302Sopenharmony_ci			if (*end)
217570af302Sopenharmony_ci				return -1;
218570af302Sopenharmony_ci		} else if (*fmt == 'd' || *fmt == 'f' || *fmt == 'l') {
219570af302Sopenharmony_ci			if (scan1(b[j++], a[i++], *fmt))
220570af302Sopenharmony_ci				return -1;
221570af302Sopenharmony_ci		} else
222570af302Sopenharmony_ci			return -1;
223570af302Sopenharmony_ci	}
224570af302Sopenharmony_ci
225570af302Sopenharmony_ci	b[0] = &t->y;
226570af302Sopenharmony_ci	b[1] = &dy;
227570af302Sopenharmony_ci	b[2] = &t->y2;
228570af302Sopenharmony_ci	b[3] = &dy2;
229570af302Sopenharmony_ci	j = 0;
230570af302Sopenharmony_ci	fmt++;
231570af302Sopenharmony_ci	for (; *fmt && i < n && j < sizeof b/sizeof *b; fmt++) {
232570af302Sopenharmony_ci		if (*fmt == 'i') {
233570af302Sopenharmony_ci			t->i = strtoll(a[i++], &end, 0);
234570af302Sopenharmony_ci			if (*end)
235570af302Sopenharmony_ci				return -1;
236570af302Sopenharmony_ci		} else if (*fmt == 'd' || *fmt == 'f' || *fmt == 'l') {
237570af302Sopenharmony_ci			if (scan1(b[j++], a[i++], *fmt))
238570af302Sopenharmony_ci				return -1;
239570af302Sopenharmony_ci			if (i < n && scan1(b[j++], a[i++], 'f'))
240570af302Sopenharmony_ci				return -1;
241570af302Sopenharmony_ci		} else
242570af302Sopenharmony_ci			return -1;
243570af302Sopenharmony_ci	}
244570af302Sopenharmony_ci	t->dy = dy;
245570af302Sopenharmony_ci	t->dy2 = dy2;
246570af302Sopenharmony_ci	if (i < n)
247570af302Sopenharmony_ci		econv(&t->e, a[i]);
248570af302Sopenharmony_ci	return 0;
249570af302Sopenharmony_ci}
250570af302Sopenharmony_ci
251570af302Sopenharmony_ci/* assume strlen(old) == strlen(new) */
252570af302Sopenharmony_cistatic void replace(char *buf, char *old, char *new)
253570af302Sopenharmony_ci{
254570af302Sopenharmony_ci	int n = strlen(new);
255570af302Sopenharmony_ci	char *p = buf;
256570af302Sopenharmony_ci
257570af302Sopenharmony_ci	while ((p = strstr(p, old)))
258570af302Sopenharmony_ci		memcpy(p, new, n);
259570af302Sopenharmony_ci}
260570af302Sopenharmony_ci
261570af302Sopenharmony_cistatic void fixl(char *buf)
262570af302Sopenharmony_ci{
263570af302Sopenharmony_ci	replace(buf, "-infL", " -inf");
264570af302Sopenharmony_ci	replace(buf, "infL", " inf");
265570af302Sopenharmony_ci	replace(buf, "-nanL", " -nan");
266570af302Sopenharmony_ci	replace(buf, "nanL", " nan");
267570af302Sopenharmony_ci}
268570af302Sopenharmony_ci
269570af302Sopenharmony_cistatic int print1(char *buf, int n, long double x, int fmt)
270570af302Sopenharmony_ci{
271570af302Sopenharmony_ci	int k;
272570af302Sopenharmony_ci
273570af302Sopenharmony_ci	if (fmt == 'd')
274570af302Sopenharmony_ci		k = snprintf(buf, n, ",%24a", (double)x);
275570af302Sopenharmony_ci	else if (fmt == 'f')
276570af302Sopenharmony_ci		k = snprintf(buf, n, ",%16a", (double)x);
277570af302Sopenharmony_ci	else if (fmt == 'l') {
278570af302Sopenharmony_ci#if LDBL_MANT_DIG == 53
279570af302Sopenharmony_ci		k = snprintf(buf, n, ",%24a", (double)x);
280570af302Sopenharmony_ci#elif LDBL_MANT_DIG == 64
281570af302Sopenharmony_ci		k = snprintf(buf, n, ",%30LaL", x);
282570af302Sopenharmony_ci		fixl(buf);
283570af302Sopenharmony_ci#endif
284570af302Sopenharmony_ci	} else
285570af302Sopenharmony_ci		k = -1;
286570af302Sopenharmony_ci	return k;
287570af302Sopenharmony_ci}
288570af302Sopenharmony_ci
289570af302Sopenharmony_cistatic int print(const char *fmt, struct t *t, char *buf, int n)
290570af302Sopenharmony_ci{
291570af302Sopenharmony_ci	long double a[4];
292570af302Sopenharmony_ci	int k, i=0, out=0;
293570af302Sopenharmony_ci
294570af302Sopenharmony_ci	k = snprintf(buf, n, "T(%s", rstr(t->r));
295570af302Sopenharmony_ci	if (k < 0 || k >= n)
296570af302Sopenharmony_ci		return -1;
297570af302Sopenharmony_ci	n -= k;
298570af302Sopenharmony_ci	buf += k;
299570af302Sopenharmony_ci
300570af302Sopenharmony_ci	a[0] = t->x;
301570af302Sopenharmony_ci	a[1] = t->x2;
302570af302Sopenharmony_ci	a[2] = t->x3;
303570af302Sopenharmony_ci	for (; *fmt; fmt++) {
304570af302Sopenharmony_ci		if (*fmt == '_') {
305570af302Sopenharmony_ci			a[0] = t->y;
306570af302Sopenharmony_ci			a[1] = t->dy;
307570af302Sopenharmony_ci			a[2] = t->y2;
308570af302Sopenharmony_ci			a[3] = t->dy2;
309570af302Sopenharmony_ci			i = 0;
310570af302Sopenharmony_ci			out = 1;
311570af302Sopenharmony_ci			continue;
312570af302Sopenharmony_ci		}
313570af302Sopenharmony_ci		if (*fmt == 'i') {
314570af302Sopenharmony_ci			k = snprintf(buf, n, ", %11lld", t->i);
315570af302Sopenharmony_ci			if (k < 0 || k >= n)
316570af302Sopenharmony_ci				return -1;
317570af302Sopenharmony_ci			n -= k;
318570af302Sopenharmony_ci			buf += k;
319570af302Sopenharmony_ci		} else {
320570af302Sopenharmony_ci			if (i >= sizeof a/sizeof *a)
321570af302Sopenharmony_ci				return -1;
322570af302Sopenharmony_ci			k = print1(buf, n, a[i++], *fmt);
323570af302Sopenharmony_ci			if (k < 0 || k >= n)
324570af302Sopenharmony_ci				return -1;
325570af302Sopenharmony_ci			n -= k;
326570af302Sopenharmony_ci			buf += k;
327570af302Sopenharmony_ci			if (out) {
328570af302Sopenharmony_ci				k = print1(buf, n, a[i++], 'f');
329570af302Sopenharmony_ci				if (k < 0 || k >= n)
330570af302Sopenharmony_ci					return -1;
331570af302Sopenharmony_ci				n -= k;
332570af302Sopenharmony_ci				buf += k;
333570af302Sopenharmony_ci			}
334570af302Sopenharmony_ci		}
335570af302Sopenharmony_ci	}
336570af302Sopenharmony_ci	k = snprintf(buf, n, ", %s)\n", estr(t->e));
337570af302Sopenharmony_ci	if (k < 0 || k >= n)
338570af302Sopenharmony_ci		return -1;
339570af302Sopenharmony_ci	return 0;
340570af302Sopenharmony_ci}
341