brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.6 KiB · 67677c9 Raw
426 lines · c
1/*2  Name:     imtest.c3  Purpose:  Test driver for imath library.4  Author:   M. J. Fromberger5 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 copy9  of this software and associated documentation files (the "Software"), to deal10  in the Software without restriction, including without limitation the rights11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell12  copies of the Software, and to permit persons to whom the Software is13  furnished to do so, subject to the following conditions:14 15  The above copyright notice and this permission notice shall be included in16  all copies or substantial portions of the Software.17 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER22  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 THE24  SOFTWARE.25 26  Reads tests from input files or standard input, and runs them.  Tests have27  the form:28 29  code:inputs:outputs30 31  The 'code' is a string identifying the test to be performed.  The inputs and32  outputs are comma-separated sequences of values.  The format of each input33  is:34 35    1005    number in decimal notation (signs ok)36    #x-C0E  number in hexadecimal notation37    #b1011  number in binary notation38    #o37750 number in octal notation39    =k      use register k for this input40 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.5C44 45  Each output is a string representing the value to which the corresponding46  result is compared in order to pass the test.  By default, tests are expected47  to succeed (i.e., return MP_OK).  To specify an alternate return value, use48  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, the57  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 the59  operation being tested.60 61  The exit status is 0 if all tests passed, 1 if one or more tests failed or62  had errors.63 64  Note:  There is currently a fixed limit on the length of lines by this test65  ----   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_MAX82#undef LINE_MAX83#endif84 85#define LINE_MAX 409686 87typedef struct {88  char *code;89  int num_inputs;90  int num_outputs;91  test_f call;92} test_t;93 94test_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 159char g_line[LINE_MAX];160 161extern mp_result imath_errno;162extern char *imath_errmsg;163 164const char *g_imath_strerr[] = {"MP_OK",    "MP_TRUE",  "MP_MEMORY", "MP_RANGE",165                                "MP_UNDEF", "MP_TRUNC", "MP_BADARG"};166 167bool process_file(char *file_name, FILE *ifp, FILE *ofp);168int read_line(FILE *ifp, char *line, int limit);169void trim_line(char *line);170int is_blank(char *line);171int parse_line(char *line, testspec_t *t);172int count_fields(char *line, int delim);173void parse_fields(char *line, int delim, char **start);174int run_test(int test_num, testspec_t *t, FILE *ofp);175void free_test(testspec_t *t);176int find_test(char *code, test_t *info);177char *error_string(mp_result res);178 179int 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`. The207    given `file_name` is used for cosmetic attribution. The return value is208    true if all tests passed, false if any tests failed or had errors. */209bool 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 240int 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`. */260void 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 a276    line-trailing comment (`# ...`) is removed. */277int is_blank(char *line) {278  while (*line && *line != '#' && isspace((unsigned char)*line)) ++line;279 280  return *line == '\0' || *line == '#';281}282 283int 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`. */314int 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 326void 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`. The337    `test_num` is used in log output and should reflect the global ordering of338    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 -1341    if the test is broken (e.g., its code is unknown). */342int 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 to373     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 are388    found populates `*info` with a copy. It returns -1 if `code` is unknown. */389int 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. */403void 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 the417    same as the error string returned by `mp_error_string`, but corresponds to418    the spelling of the constant for its value. */419char *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