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