xref: /llvm-project/polly/lib/External/isl/imath/imtest.c (revision 658eb9e14264d48888ade0e3daf0b648f76c3f0e)
1*658eb9e1SMichael Kruse /*
2*658eb9e1SMichael Kruse   Name:     imtest.c
3*658eb9e1SMichael Kruse   Purpose:  Test driver for imath library.
4*658eb9e1SMichael Kruse   Author:   M. J. Fromberger
5*658eb9e1SMichael Kruse 
6*658eb9e1SMichael Kruse   Copyright (C) 2002-2008 Michael J. Fromberger, All Rights Reserved.
7*658eb9e1SMichael Kruse 
8*658eb9e1SMichael Kruse   Permission is hereby granted, free of charge, to any person obtaining a copy
9*658eb9e1SMichael Kruse   of this software and associated documentation files (the "Software"), to deal
10*658eb9e1SMichael Kruse   in the Software without restriction, including without limitation the rights
11*658eb9e1SMichael Kruse   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12*658eb9e1SMichael Kruse   copies of the Software, and to permit persons to whom the Software is
13*658eb9e1SMichael Kruse   furnished to do so, subject to the following conditions:
14*658eb9e1SMichael Kruse 
15*658eb9e1SMichael Kruse   The above copyright notice and this permission notice shall be included in
16*658eb9e1SMichael Kruse   all copies or substantial portions of the Software.
17*658eb9e1SMichael Kruse 
18*658eb9e1SMichael Kruse   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19*658eb9e1SMichael Kruse   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20*658eb9e1SMichael Kruse   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
21*658eb9e1SMichael Kruse   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22*658eb9e1SMichael Kruse   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23*658eb9e1SMichael Kruse   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24*658eb9e1SMichael Kruse   SOFTWARE.
25*658eb9e1SMichael Kruse 
26*658eb9e1SMichael Kruse   Reads tests from input files or standard input, and runs them.  Tests have
27*658eb9e1SMichael Kruse   the form:
28*658eb9e1SMichael Kruse 
29*658eb9e1SMichael Kruse   code:inputs:outputs
30*658eb9e1SMichael Kruse 
31*658eb9e1SMichael Kruse   The 'code' is a string identifying the test to be performed.  The inputs and
32*658eb9e1SMichael Kruse   outputs are comma-separated sequences of values.  The format of each input
33*658eb9e1SMichael Kruse   is:
34*658eb9e1SMichael Kruse 
35*658eb9e1SMichael Kruse     1005    number in decimal notation (signs ok)
36*658eb9e1SMichael Kruse     #x-C0E  number in hexadecimal notation
37*658eb9e1SMichael Kruse     #b1011  number in binary notation
38*658eb9e1SMichael Kruse     #o37750 number in octal notation
39*658eb9e1SMichael Kruse     =k      use register k for this input
40*658eb9e1SMichael Kruse 
41*658eb9e1SMichael Kruse   For rational tests, the following syntax is also legal:
42*658eb9e1SMichael Kruse     @5.33   use decimal notation (for rationals only)
43*658eb9e1SMichael Kruse             may be combined with radix notation, e.g. #x@A0.5C
44*658eb9e1SMichael Kruse 
45*658eb9e1SMichael Kruse   Each output is a string representing the value to which the corresponding
46*658eb9e1SMichael Kruse   result is compared in order to pass the test.  By default, tests are expected
47*658eb9e1SMichael Kruse   to succeed (i.e., return MP_OK).  To specify an alternate return value, use
48*658eb9e1SMichael Kruse   the notation $RESULT, where RESULT is the name of an error (e.g., MP_MEMORY,
49*658eb9e1SMichael Kruse   MP_UNDEF, etc.) or a numeric result denoted $#number (e.g., $#-5).
50*658eb9e1SMichael Kruse 
51*658eb9e1SMichael Kruse   Results are written to standard output in the following formats:
52*658eb9e1SMichael Kruse 
53*658eb9e1SMichael Kruse   filename<tab>line<tab>number<tab>result<eoln>
54*658eb9e1SMichael Kruse   filename<tab>line<tab>number<tab>result<tab>message<eoln>
55*658eb9e1SMichael Kruse 
56*658eb9e1SMichael Kruse   The filename and line give the offset of the test in its input file, the
57*658eb9e1SMichael Kruse   number is the numbet of the test among all inputs, starting from 1.
58*658eb9e1SMichael Kruse   The result is a textual description of the result code returned by the
59*658eb9e1SMichael Kruse   operation being tested.
60*658eb9e1SMichael Kruse 
61*658eb9e1SMichael Kruse   The exit status is 0 if all tests passed, 1 if one or more tests failed or
62*658eb9e1SMichael Kruse   had errors.
63*658eb9e1SMichael Kruse 
64*658eb9e1SMichael Kruse   Note:  There is currently a fixed limit on the length of lines by this test
65*658eb9e1SMichael Kruse   ----   driver.  You can increase it if you wish, but the code doesn't check;
66*658eb9e1SMichael Kruse          lines over the length are truncated (split).
67*658eb9e1SMichael Kruse  */
68*658eb9e1SMichael Kruse 
69*658eb9e1SMichael Kruse #include <assert.h>
70*658eb9e1SMichael Kruse #include <ctype.h>
71*658eb9e1SMichael Kruse #include <errno.h>
72*658eb9e1SMichael Kruse #include <limits.h>
73*658eb9e1SMichael Kruse #include <stdio.h>
74*658eb9e1SMichael Kruse #include <stdlib.h>
75*658eb9e1SMichael Kruse #include <string.h>
76*658eb9e1SMichael Kruse #include <time.h>
77*658eb9e1SMichael Kruse 
78*658eb9e1SMichael Kruse #include "imath.h"
79*658eb9e1SMichael Kruse #include "imdrover.h"
80*658eb9e1SMichael Kruse 
81*658eb9e1SMichael Kruse #ifdef LINE_MAX
82*658eb9e1SMichael Kruse #undef LINE_MAX
83*658eb9e1SMichael Kruse #endif
84*658eb9e1SMichael Kruse 
85*658eb9e1SMichael Kruse #define LINE_MAX 4096
86*658eb9e1SMichael Kruse 
87*658eb9e1SMichael Kruse typedef struct {
88*658eb9e1SMichael Kruse   char *code;
89*658eb9e1SMichael Kruse   int num_inputs;
90*658eb9e1SMichael Kruse   int num_outputs;
91*658eb9e1SMichael Kruse   test_f call;
92*658eb9e1SMichael Kruse } test_t;
93*658eb9e1SMichael Kruse 
94*658eb9e1SMichael Kruse test_t g_tests[] = {
95*658eb9e1SMichael Kruse     /* What it does...    */
96*658eb9e1SMichael Kruse     {"initu", 2, 1, test_init},        /* r0 = uv(r1)        */
97*658eb9e1SMichael Kruse     {"initv", 2, 1, test_init},        /* r0 = v(r1)         */
98*658eb9e1SMichael Kruse     {"setu", 2, 1, test_set},          /* r0 = uv(r1)        */
99*658eb9e1SMichael Kruse     {"setv", 2, 1, test_set},          /* r0 = v(r1)         */
100*658eb9e1SMichael Kruse     {"neg", 2, 1, test_neg},           /* r1 = -r0           */
101*658eb9e1SMichael Kruse     {"abs", 2, 1, test_abs},           /* r1 = |r0|          */
102*658eb9e1SMichael Kruse     {"add", 3, 1, test_add},           /* r3 = r1 + r2       */
103*658eb9e1SMichael Kruse     {"addv", 3, 1, test_add},          /* r3 = r1 + v(r2)    */
104*658eb9e1SMichael Kruse     {"sub", 3, 1, test_sub},           /* r3 = r1 - r2       */
105*658eb9e1SMichael Kruse     {"subv", 3, 1, test_sub},          /* r3 = r1 - v(r2)    */
106*658eb9e1SMichael Kruse     {"mul", 3, 1, test_mul},           /* r3 = r1 * r2       */
107*658eb9e1SMichael Kruse     {"mulp2", 3, 1, test_mulp2},       /* r3 = r1 * 2^v(r2)  */
108*658eb9e1SMichael Kruse     {"mulv", 3, 1, test_mulv},         /* r3 = r1 * v(r2)    */
109*658eb9e1SMichael Kruse     {"sqr", 2, 1, test_sqr},           /* r2 = r1 * r1       */
110*658eb9e1SMichael Kruse     {"div", 4, 2, test_div},           /* r2 = r1 / r2, r3 = r1 % r2         */
111*658eb9e1SMichael Kruse     {"divp2", 4, 2, test_divp2},       /* r2 = r1 / 2^v(r2),r3 = r1 % 2^v(r2)*/
112*658eb9e1SMichael Kruse     {"divv", 3, 2, test_divv},         /* r2 = r1 / v(r2), r3 = r1 % v(r2)   */
113*658eb9e1SMichael Kruse     {"expt", 3, 1, test_expt},         /* r3 = r1 ^ v(r2) */
114*658eb9e1SMichael Kruse     {"exptv", 3, 1, test_exptv},       /* r3 = v(r1) ^ v(r2) */
115*658eb9e1SMichael Kruse     {"exptf", 3, 1, test_exptf},       /* r3 = r1 ^ r2 */
116*658eb9e1SMichael Kruse     {"mod", 3, 1, test_mod},           /* r3 = r1 % r2       */
117*658eb9e1SMichael Kruse     {"gcd", 3, 1, test_gcd},           /* r3 = gcd(r1, r2)   */
118*658eb9e1SMichael Kruse     {"egcd", 5, 3, test_egcd},         /* r3 = gcd(r1, r2) = r1*r4 + r2*r5   */
119*658eb9e1SMichael Kruse     {"lcm", 3, 1, test_lcm},           /* r3 = lcm(r1, r2)   */
120*658eb9e1SMichael Kruse     {"sqrt", 2, 1, test_sqrt},         /* r2 = sqrt(r1)      */
121*658eb9e1SMichael Kruse     {"root", 3, 1, test_root},         /* r3 = r1^(1/v(r2))  */
122*658eb9e1SMichael Kruse     {"invmod", 3, 1, test_invmod},     /* r3 = r1^-1 mod r2  */
123*658eb9e1SMichael Kruse     {"emod", 4, 1, test_exptmod},      /* r4 = r1^r2 mod r3  */
124*658eb9e1SMichael Kruse     {"emodev", 4, 1, test_exptmod_ev}, /* r4 = r1^v(r2) mod r3  */
125*658eb9e1SMichael Kruse     {"emodbv", 4, 1, test_exptmod_bv}, /* r4 = v(r1)^r2 mod r3  */
126*658eb9e1SMichael Kruse     {"cmp", 2, 1, test_comp},          /* rtn = compare(r1, r2) */
127*658eb9e1SMichael Kruse     {"cmpu", 2, 1, test_ucomp},        /* rtn = compare(|r1|, |r2|) */
128*658eb9e1SMichael Kruse     {"cmpz", 1, 1, test_zcomp},        /* rtn = compare(r1, 0)      */
129*658eb9e1SMichael Kruse     {"cmpv", 2, 1, test_vcomp},        /* rtn = compare(r1, v(r2))  */
130*658eb9e1SMichael Kruse     {"cmpuv", 2, 1, test_uvcomp},      /* rtn = compare(r1, v(r2))  */
131*658eb9e1SMichael Kruse     {"tostr", 2, 1, test_tostr},       /* r1: value, r2: radix, o1: result */
132*658eb9e1SMichael Kruse     {"tobin", 1, 1, test_tobin},       /* r1: value, o1: result binary     */
133*658eb9e1SMichael Kruse     {"readbin", 1, 1, test_read_binary}, /* r1: 2's comp, o1: result value   */
134*658eb9e1SMichael Kruse     {"to-uns", 1, 1, test_to_uns},       /* r1: value, o1: result binary     */
135*658eb9e1SMichael Kruse     {"readuns", 1, 1, test_read_uns},    /* r1: unsigned, o1: result value   */
136*658eb9e1SMichael Kruse     {"to-int", 1, 1, test_to_int},       /* r1: value, o1: result */
137*658eb9e1SMichael Kruse     {"to-uint", 1, 1, test_to_uint},     /* r1: value, o1: result */
138*658eb9e1SMichael Kruse     {"meta", -1, -1, test_meta},
139*658eb9e1SMichael Kruse     {"qneg", 2, 1, test_qneg},        /* r2 = -r1        */
140*658eb9e1SMichael Kruse     {"qrecip", 2, 1, test_qrecip},    /* r2 = 1 / r1     */
141*658eb9e1SMichael Kruse     {"qabs", 2, 1, test_qabs},        /* r2 = |r1|       */
142*658eb9e1SMichael Kruse     {"qadd", 3, 1, test_qadd},        /* r3 = r1 + r2    */
143*658eb9e1SMichael Kruse     {"qsub", 3, 1, test_qsub},        /* r3 = r1 - r2    */
144*658eb9e1SMichael Kruse     {"qmul", 3, 1, test_qmul},        /* r3 = r1 * r2    */
145*658eb9e1SMichael Kruse     {"qdiv", 3, 1, test_qdiv},        /* r3 = r1 / r2    */
146*658eb9e1SMichael Kruse     {"qaddz", 3, 1, test_qaddz},      /* r3 = r1 + r2    */
147*658eb9e1SMichael Kruse     {"qsubz", 3, 1, test_qsubz},      /* r3 = r1 - r2    */
148*658eb9e1SMichael Kruse     {"qmulz", 3, 1, test_qmulz},      /* r3 = r1 * r2    */
149*658eb9e1SMichael Kruse     {"qdivz", 3, 1, test_qdivz},      /* r3 = r1 / r2    */
150*658eb9e1SMichael Kruse     {"qexpt", 3, 1, test_qexpt},      /* r3 = r1 ^ v(r2) */
151*658eb9e1SMichael Kruse     {"qtostr", 2, 1, test_qtostr},    /* r1: value, r2: radix; o1: result */
152*658eb9e1SMichael Kruse     {"qtodec", 4, 1, test_qtodec},    /* r1: val, r2: rdx, r3: prec,
153*658eb9e1SMichael Kruse                                          r4: rounding mode; o1: res */
154*658eb9e1SMichael Kruse     {"qrdec", 2, 1, test_qrdec},      /* r1: dec, r2: rdx; o1: result value  */
155*658eb9e1SMichael Kruse     {"isprime", 1, 1, test_is_prime}, /* rtn = prime(r1) ? MP_TRUE : MP_FALSE */
156*658eb9e1SMichael Kruse     {NULL, 0, 0, NULL}                /* end of list marker */
157*658eb9e1SMichael Kruse };
158*658eb9e1SMichael Kruse 
159*658eb9e1SMichael Kruse char g_line[LINE_MAX];
160*658eb9e1SMichael Kruse 
161*658eb9e1SMichael Kruse extern mp_result imath_errno;
162*658eb9e1SMichael Kruse extern char *imath_errmsg;
163*658eb9e1SMichael Kruse 
164*658eb9e1SMichael Kruse const char *g_imath_strerr[] = {"MP_OK",    "MP_TRUE",  "MP_MEMORY", "MP_RANGE",
165*658eb9e1SMichael Kruse                                 "MP_UNDEF", "MP_TRUNC", "MP_BADARG"};
166*658eb9e1SMichael Kruse 
167*658eb9e1SMichael Kruse bool process_file(char *file_name, FILE *ifp, FILE *ofp);
168*658eb9e1SMichael Kruse int read_line(FILE *ifp, char *line, int limit);
169*658eb9e1SMichael Kruse void trim_line(char *line);
170*658eb9e1SMichael Kruse int is_blank(char *line);
171*658eb9e1SMichael Kruse int parse_line(char *line, testspec_t *t);
172*658eb9e1SMichael Kruse int count_fields(char *line, int delim);
173*658eb9e1SMichael Kruse void parse_fields(char *line, int delim, char **start);
174*658eb9e1SMichael Kruse int run_test(int test_num, testspec_t *t, FILE *ofp);
175*658eb9e1SMichael Kruse void free_test(testspec_t *t);
176*658eb9e1SMichael Kruse int find_test(char *code, test_t *info);
177*658eb9e1SMichael Kruse char *error_string(mp_result res);
178*658eb9e1SMichael Kruse 
main(int argc,char * argv[])179*658eb9e1SMichael Kruse int main(int argc, char *argv[]) {
180*658eb9e1SMichael Kruse   int exit_status = 0;
181*658eb9e1SMichael Kruse 
182*658eb9e1SMichael Kruse   init_testing();
183*658eb9e1SMichael Kruse 
184*658eb9e1SMichael Kruse   if (argc == 1) {
185*658eb9e1SMichael Kruse     fprintf(stderr, "[reading from stdin]\n");
186*658eb9e1SMichael Kruse     if (!process_file("-", stdin, stdout)) exit_status = 1;
187*658eb9e1SMichael Kruse   } else {
188*658eb9e1SMichael Kruse     FILE *ifp;
189*658eb9e1SMichael Kruse     int i;
190*658eb9e1SMichael Kruse 
191*658eb9e1SMichael Kruse     for (i = 1; i < argc; ++i) {
192*658eb9e1SMichael Kruse       if (strcmp(argv[i], "-") == 0) {
193*658eb9e1SMichael Kruse         ifp = stdin;
194*658eb9e1SMichael Kruse       } else if ((ifp = fopen(argv[i], "r")) == NULL) {
195*658eb9e1SMichael Kruse         fprintf(stderr, "Cannot open '%s': %s\n", argv[i], strerror(errno));
196*658eb9e1SMichael Kruse         return 1;
197*658eb9e1SMichael Kruse       }
198*658eb9e1SMichael Kruse       if (!process_file(argv[i], ifp, stdout)) exit_status = 1;
199*658eb9e1SMichael Kruse 
200*658eb9e1SMichael Kruse       fclose(ifp);
201*658eb9e1SMichael Kruse     }
202*658eb9e1SMichael Kruse   }
203*658eb9e1SMichael Kruse   return exit_status;
204*658eb9e1SMichael Kruse }
205*658eb9e1SMichael Kruse 
206*658eb9e1SMichael Kruse /** Reads and runs test cases from `ifp` and writes test results to `ofp`. The
207*658eb9e1SMichael Kruse     given `file_name` is used for cosmetic attribution. The return value is
208*658eb9e1SMichael Kruse     true if all tests passed, false if any tests failed or had errors. */
process_file(char * file_name,FILE * ifp,FILE * ofp)209*658eb9e1SMichael Kruse bool process_file(char *file_name, FILE *ifp, FILE *ofp) {
210*658eb9e1SMichael Kruse   int res, line_num, test_num = 0, num_failed = 0, num_bogus = 0;
211*658eb9e1SMichael Kruse   clock_t start, finish;
212*658eb9e1SMichael Kruse 
213*658eb9e1SMichael Kruse   start = clock();
214*658eb9e1SMichael Kruse   while ((line_num = read_line(ifp, g_line, LINE_MAX)) != 0) {
215*658eb9e1SMichael Kruse     testspec_t t;
216*658eb9e1SMichael Kruse     t.line = line_num;
217*658eb9e1SMichael Kruse     t.file = file_name;
218*658eb9e1SMichael Kruse     if (parse_line(g_line, &t)) {
219*658eb9e1SMichael Kruse       if ((res = run_test(++test_num, &t, ofp)) < 0) {
220*658eb9e1SMichael Kruse         ++num_bogus;
221*658eb9e1SMichael Kruse       } else if (res == 0) {
222*658eb9e1SMichael Kruse         ++num_failed;
223*658eb9e1SMichael Kruse       }
224*658eb9e1SMichael Kruse       free_test(&t);
225*658eb9e1SMichael Kruse     } else {
226*658eb9e1SMichael Kruse       fprintf(stderr, "Line %d: Incorrect input syntax.\n", line_num);
227*658eb9e1SMichael Kruse       ++num_bogus;
228*658eb9e1SMichael Kruse     }
229*658eb9e1SMichael Kruse   }
230*658eb9e1SMichael Kruse   finish = clock();
231*658eb9e1SMichael Kruse 
232*658eb9e1SMichael Kruse   fprintf(ofp,
233*658eb9e1SMichael Kruse           "# %s %d tests: %d passed, %d failed, %d errors. (%.2f seconds)\n",
234*658eb9e1SMichael Kruse           file_name, test_num, (test_num - num_failed - num_bogus), num_failed,
235*658eb9e1SMichael Kruse           num_bogus, ((double)(finish - start) / CLOCKS_PER_SEC));
236*658eb9e1SMichael Kruse 
237*658eb9e1SMichael Kruse   return num_failed == 0 && num_bogus == 0;
238*658eb9e1SMichael Kruse }
239*658eb9e1SMichael Kruse 
read_line(FILE * ifp,char * line,int limit)240*658eb9e1SMichael Kruse int read_line(FILE *ifp, char *line, int limit) {
241*658eb9e1SMichael Kruse   static FILE *current_fp = NULL;
242*658eb9e1SMichael Kruse   static int current_line = 0;
243*658eb9e1SMichael Kruse 
244*658eb9e1SMichael Kruse   if (ifp != current_fp) {
245*658eb9e1SMichael Kruse     current_fp = ifp;
246*658eb9e1SMichael Kruse     current_line = 0;
247*658eb9e1SMichael Kruse   }
248*658eb9e1SMichael Kruse 
249*658eb9e1SMichael Kruse   do {
250*658eb9e1SMichael Kruse     if (fgets(line, limit, ifp) == NULL) return 0;
251*658eb9e1SMichael Kruse 
252*658eb9e1SMichael Kruse     ++current_line;
253*658eb9e1SMichael Kruse   } while (is_blank(line));
254*658eb9e1SMichael Kruse 
255*658eb9e1SMichael Kruse   trim_line(line);
256*658eb9e1SMichael Kruse   return current_line;
257*658eb9e1SMichael Kruse }
258*658eb9e1SMichael Kruse 
259*658eb9e1SMichael Kruse /** Removes leading and trailing whitespace from a zero-terminated `line`. */
trim_line(char * line)260*658eb9e1SMichael Kruse void trim_line(char *line) {
261*658eb9e1SMichael Kruse   int len;
262*658eb9e1SMichael Kruse   char *fnw = line;
263*658eb9e1SMichael Kruse 
264*658eb9e1SMichael Kruse   /* Remove leading whitespace */
265*658eb9e1SMichael Kruse   while (isspace((unsigned char)*fnw)) ++fnw;
266*658eb9e1SMichael Kruse 
267*658eb9e1SMichael Kruse   len = strlen(fnw);
268*658eb9e1SMichael Kruse   memmove(line, fnw, len);
269*658eb9e1SMichael Kruse 
270*658eb9e1SMichael Kruse   /* Remove trailing whitespace (including linefeeds) */
271*658eb9e1SMichael Kruse   fnw = line + len - 1;
272*658eb9e1SMichael Kruse   while (fnw >= line && isspace((unsigned char)*fnw)) *fnw-- = '\0';
273*658eb9e1SMichael Kruse }
274*658eb9e1SMichael Kruse 
275*658eb9e1SMichael Kruse /** Reports whether a zero-terminated `line` contains only whitespace after a
276*658eb9e1SMichael Kruse     line-trailing comment (`# ...`) is removed. */
is_blank(char * line)277*658eb9e1SMichael Kruse int is_blank(char *line) {
278*658eb9e1SMichael Kruse   while (*line && *line != '#' && isspace((unsigned char)*line)) ++line;
279*658eb9e1SMichael Kruse 
280*658eb9e1SMichael Kruse   return *line == '\0' || *line == '#';
281*658eb9e1SMichael Kruse }
282*658eb9e1SMichael Kruse 
parse_line(char * line,testspec_t * t)283*658eb9e1SMichael Kruse int parse_line(char *line, testspec_t *t) {
284*658eb9e1SMichael Kruse   char *code_brk, *in_brk;
285*658eb9e1SMichael Kruse   int num_fields;
286*658eb9e1SMichael Kruse 
287*658eb9e1SMichael Kruse   if ((code_brk = strchr(line, ':')) == NULL) return 0;
288*658eb9e1SMichael Kruse   if ((in_brk = strchr(code_brk + 1, ':')) == NULL) return 0;
289*658eb9e1SMichael Kruse 
290*658eb9e1SMichael Kruse   *code_brk = '\0';
291*658eb9e1SMichael Kruse   t->code = line;
292*658eb9e1SMichael Kruse   *in_brk = '\0';
293*658eb9e1SMichael Kruse 
294*658eb9e1SMichael Kruse   num_fields = count_fields(code_brk + 1, ',');
295*658eb9e1SMichael Kruse   t->num_inputs = num_fields;
296*658eb9e1SMichael Kruse   t->input = NULL;
297*658eb9e1SMichael Kruse 
298*658eb9e1SMichael Kruse   num_fields = count_fields(in_brk + 1, ',');
299*658eb9e1SMichael Kruse   t->num_outputs = num_fields;
300*658eb9e1SMichael Kruse   t->output = NULL;
301*658eb9e1SMichael Kruse 
302*658eb9e1SMichael Kruse   if (t->num_inputs > 0) {
303*658eb9e1SMichael Kruse     t->input = calloc(t->num_inputs, sizeof(char *));
304*658eb9e1SMichael Kruse     parse_fields(code_brk + 1, ',', t->input);
305*658eb9e1SMichael Kruse   }
306*658eb9e1SMichael Kruse   if (t->num_outputs > 0) {
307*658eb9e1SMichael Kruse     t->output = calloc(t->num_outputs, sizeof(char *));
308*658eb9e1SMichael Kruse     parse_fields(in_brk + 1, ',', t->output);
309*658eb9e1SMichael Kruse   }
310*658eb9e1SMichael Kruse   return 1;
311*658eb9e1SMichael Kruse }
312*658eb9e1SMichael Kruse 
313*658eb9e1SMichael Kruse /** Returns the number of `delim` separated fields occur in `line`. */
count_fields(char * line,int delim)314*658eb9e1SMichael Kruse int count_fields(char *line, int delim) {
315*658eb9e1SMichael Kruse   int count = 1;
316*658eb9e1SMichael Kruse 
317*658eb9e1SMichael Kruse   if (*line == '\0') return 0;
318*658eb9e1SMichael Kruse 
319*658eb9e1SMichael Kruse   while (*line) {
320*658eb9e1SMichael Kruse     if (*line == (char)delim && *(line + 1) != '\0') ++count;
321*658eb9e1SMichael Kruse     ++line;
322*658eb9e1SMichael Kruse   }
323*658eb9e1SMichael Kruse   return count;
324*658eb9e1SMichael Kruse }
325*658eb9e1SMichael Kruse 
parse_fields(char * line,int delim,char ** start)326*658eb9e1SMichael Kruse void parse_fields(char *line, int delim, char **start) {
327*658eb9e1SMichael Kruse   int pos = 0;
328*658eb9e1SMichael Kruse 
329*658eb9e1SMichael Kruse   start[pos++] = line;
330*658eb9e1SMichael Kruse   while ((line = strchr(line, delim)) != NULL) {
331*658eb9e1SMichael Kruse     *line++ = '\0';
332*658eb9e1SMichael Kruse     start[pos++] = line;
333*658eb9e1SMichael Kruse   }
334*658eb9e1SMichael Kruse }
335*658eb9e1SMichael Kruse 
336*658eb9e1SMichael Kruse /** Runs the test cases specified by `t`, and writes its results to `ofp`. The
337*658eb9e1SMichael Kruse     `test_num` is used in log output and should reflect the global ordering of
338*658eb9e1SMichael Kruse     tests, but is not otherwise interpreted by this function.
339*658eb9e1SMichael Kruse 
340*658eb9e1SMichael Kruse     This function returns 0 if the test succeeds, 1 if the test fails, and -1
341*658eb9e1SMichael Kruse     if the test is broken (e.g., its code is unknown). */
run_test(int test_num,testspec_t * t,FILE * ofp)342*658eb9e1SMichael Kruse int run_test(int test_num, testspec_t *t, FILE *ofp) {
343*658eb9e1SMichael Kruse   test_t info;
344*658eb9e1SMichael Kruse 
345*658eb9e1SMichael Kruse   /* Look up and reality check test parameters */
346*658eb9e1SMichael Kruse   if (find_test(t->code, &info) < 0) {
347*658eb9e1SMichael Kruse     fprintf(stderr, "Line %d: Test code '%s' is unknown.\n", t->line, t->code);
348*658eb9e1SMichael Kruse     return -1;
349*658eb9e1SMichael Kruse   } else {
350*658eb9e1SMichael Kruse     int errs = 0;
351*658eb9e1SMichael Kruse 
352*658eb9e1SMichael Kruse     if (info.num_inputs >= 0 && t->num_inputs != info.num_inputs) {
353*658eb9e1SMichael Kruse       fprintf(stderr,
354*658eb9e1SMichael Kruse               "Line %d: Wrong number of inputs to %s (want %d, have %d)\n",
355*658eb9e1SMichael Kruse               t->line, t->code, info.num_inputs, t->num_inputs);
356*658eb9e1SMichael Kruse       ++errs;
357*658eb9e1SMichael Kruse     }
358*658eb9e1SMichael Kruse     if (info.num_outputs >= 0 && t->num_outputs != info.num_outputs) {
359*658eb9e1SMichael Kruse       fprintf(stderr,
360*658eb9e1SMichael Kruse               "Line %d: Wrong number of outputs to %s (want %d, have %d)\n",
361*658eb9e1SMichael Kruse               t->line, t->code, info.num_outputs, t->num_outputs);
362*658eb9e1SMichael Kruse       ++errs;
363*658eb9e1SMichael Kruse     }
364*658eb9e1SMichael Kruse     if (errs) {
365*658eb9e1SMichael Kruse       fprintf(stderr, "Line %d: %d error(s), skipping this test.\n", t->line,
366*658eb9e1SMichael Kruse               errs);
367*658eb9e1SMichael Kruse       return -1;
368*658eb9e1SMichael Kruse     }
369*658eb9e1SMichael Kruse   }
370*658eb9e1SMichael Kruse 
371*658eb9e1SMichael Kruse   /* If return value is true, just print a generic OK message;
372*658eb9e1SMichael Kruse      otherwise, it is assumed that imath_errno has been set to
373*658eb9e1SMichael Kruse      a value indicating the problem. */
374*658eb9e1SMichael Kruse   if ((info.call)(t, ofp)) {
375*658eb9e1SMichael Kruse     fprintf(ofp, "%s\t%d\t%d\tOK\n", t->file, t->line, test_num);
376*658eb9e1SMichael Kruse     return 1;
377*658eb9e1SMichael Kruse   } else if (imath_errno >= MP_BADARG) {
378*658eb9e1SMichael Kruse     fprintf(ofp, "%s\t%d\t%d\t%s\n", t->file, t->line, test_num,
379*658eb9e1SMichael Kruse             error_string(imath_errno));
380*658eb9e1SMichael Kruse   } else {
381*658eb9e1SMichael Kruse     fprintf(ofp, "%s\t%d\t%d\tFAILED\t%s\n", t->file, t->line, test_num,
382*658eb9e1SMichael Kruse             imath_errmsg);
383*658eb9e1SMichael Kruse   }
384*658eb9e1SMichael Kruse   return 0;
385*658eb9e1SMichael Kruse }
386*658eb9e1SMichael Kruse 
387*658eb9e1SMichael Kruse /** Locates the run instructions for the specified test `code`, and if they are
388*658eb9e1SMichael Kruse     found populates `*info` with a copy. It returns -1 if `code` is unknown. */
find_test(char * code,test_t * info)389*658eb9e1SMichael Kruse int find_test(char *code, test_t *info) {
390*658eb9e1SMichael Kruse   int i = 0;
391*658eb9e1SMichael Kruse 
392*658eb9e1SMichael Kruse   while (g_tests[i].code != NULL) {
393*658eb9e1SMichael Kruse     if (strcmp(g_tests[i].code, code) == 0) {
394*658eb9e1SMichael Kruse       *info = g_tests[i];
395*658eb9e1SMichael Kruse       return i;
396*658eb9e1SMichael Kruse     }
397*658eb9e1SMichael Kruse     ++i;
398*658eb9e1SMichael Kruse   }
399*658eb9e1SMichael Kruse   return -1;
400*658eb9e1SMichael Kruse }
401*658eb9e1SMichael Kruse 
402*658eb9e1SMichael Kruse /** Releases the memory occupied by a test case invocation. */
free_test(testspec_t * t)403*658eb9e1SMichael Kruse void free_test(testspec_t *t) {
404*658eb9e1SMichael Kruse   assert(t != NULL);
405*658eb9e1SMichael Kruse 
406*658eb9e1SMichael Kruse   if (t->input != NULL) {
407*658eb9e1SMichael Kruse     free(t->input);
408*658eb9e1SMichael Kruse     t->input = NULL;
409*658eb9e1SMichael Kruse   }
410*658eb9e1SMichael Kruse   if (t->output != NULL) {
411*658eb9e1SMichael Kruse     free(t->output);
412*658eb9e1SMichael Kruse     t->output = NULL;
413*658eb9e1SMichael Kruse   }
414*658eb9e1SMichael Kruse }
415*658eb9e1SMichael Kruse 
416*658eb9e1SMichael Kruse /** Returns a static label string describing `res`. Note that this is not the
417*658eb9e1SMichael Kruse     same as the error string returned by `mp_error_string`, but corresponds to
418*658eb9e1SMichael Kruse     the spelling of the constant for its value. */
error_string(mp_result res)419*658eb9e1SMichael Kruse char *error_string(mp_result res) {
420*658eb9e1SMichael Kruse   int v = abs(res);
421*658eb9e1SMichael Kruse 
422*658eb9e1SMichael Kruse   return (char *)g_imath_strerr[v];
423*658eb9e1SMichael Kruse }
424*658eb9e1SMichael Kruse 
425*658eb9e1SMichael Kruse /* Here there be dragons */
426