diff options
Diffstat (limited to 'tests/randmath.py')
-rwxr-xr-x | tests/randmath.py | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/tests/randmath.py b/tests/randmath.py new file mode 100755 index 000000000000..f350ef357f6b --- /dev/null +++ b/tests/randmath.py @@ -0,0 +1,306 @@ +#! /usr/bin/python3 -B +# +# Copyright (c) 2018-2020 Gavin D. Howard and contributors. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import os, errno +import random +import sys +import subprocess + +def gen(limit=4): + return random.randint(0, 2 ** (8 * limit)) + +def negative(): + return random.randint(0, 1) == 1 + +def zero(): + return random.randint(0, 2 ** (8) - 1) == 0 + +def num(op, neg, real, z, limit=4): + + if z: + z = zero() + else: + z = False + + if z: + return 0 + + if neg: + neg = negative() + + g = gen(limit) + + if real and negative(): + n = str(gen(25)) + length = gen(7 / 8) + if len(n) < length: + n = ("0" * (length - len(n))) + n + else: + n = "0" + + g = str(g) + if n != "0": + g = g + "." + n + + if neg and g != "0": + if op != modexp: + g = "-" + g + else: + g = "_" + g + + return g + + +def add(test, op): + + tests.append(test) + gen_ops.append(op) + +def compare(exe, options, p, test, halt, expected, op, do_add=True): + + if p.returncode != 0: + + print(" {} returned an error ({})".format(exe, p.returncode)) + + if do_add: + print(" adding to checklist...") + add(test, op) + + return + + actual = p.stdout.decode() + + if actual != expected: + + if op >= exponent: + + indata = "scale += 10; {}; {}".format(test, halt) + args = [ exe, options ] + p2 = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + expected = p2.stdout[:-10].decode() + + if actual == expected: + print(" failed because of bug in other {}".format(exe)) + print(" continuing...") + return + + if do_add: + print(" failed; adding to checklist...") + add(test, op) + else: + print(" failed {}".format(test)) + print(" expected:") + print(" {}".format(expected)) + print(" actual:") + print(" {}".format(actual)) + + +def gen_test(op): + + scale = num(op, False, False, True, 5 / 8) + + if op < div: + s = fmts[op].format(scale, num(op, True, True, True), num(op, True, True, True)) + elif op == div or op == mod: + s = fmts[op].format(scale, num(op, True, True, True), num(op, True, True, False)) + elif op == power: + s = fmts[op].format(scale, num(op, True, True, True, 7 / 8), num(op, True, False, True, 6 / 8)) + elif op == modexp: + s = fmts[op].format(scale, num(op, True, False, True), num(op, True, False, True), + num(op, True, False, False)) + elif op == sqrt: + s = "1" + while s == "1": + s = num(op, False, True, True, 1) + s = fmts[op].format(scale, s) + else: + + if op == exponent: + first = num(op, True, True, True, 6 / 8) + elif op == bessel: + first = num(op, False, True, True, 6 / 8) + else: + first = num(op, True, True, True) + + if op != bessel: + s = fmts[op].format(scale, first) + else: + s = fmts[op].format(scale, first, 6 / 8) + + return s + +def run_test(t): + + op = random.randrange(bessel + 1) + + if op != modexp: + exe = "bc" + halt = "halt" + options = "-lq" + else: + exe = "dc" + halt = "q" + options = "" + + test = gen_test(op) + + if "c(0)" in test or "scale = 4; j(4" in test: + return + + bcexe = exedir + "/" + exe + indata = test + "\n" + halt + + print("Test {}: {}".format(t, test)) + + if exe == "bc": + args = [ exe, options ] + else: + args = [ exe ] + + p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + output1 = p.stdout.decode() + + if p.returncode != 0 or output1 == "": + print(" other {} returned an error ({}); continuing...".format(exe, p.returncode)) + return + + if output1 == "\n": + print(" other {} has a bug; continuing...".format(exe)) + return + + if output1 == "-0\n": + output1 = "0\n" + elif output1 == "-0": + output1 = "0" + + args = [ bcexe, options ] + + p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + compare(exe, options, p, test, halt, output1, op) + + +if __name__ != "__main__": + sys.exit(1) + +script = sys.argv[0] +testdir = os.path.dirname(script) + +exedir = testdir + "/../bin" + +ops = [ '+', '-', '*', '/', '%', '^', '|' ] +files = [ "add", "subtract", "multiply", "divide", "modulus", "power", "modexp", + "sqrt", "exponent", "log", "arctangent", "sine", "cosine", "bessel" ] +funcs = [ "sqrt", "e", "l", "a", "s", "c", "j" ] + +fmts = [ "scale = {}; {} + {}", "scale = {}; {} - {}", "scale = {}; {} * {}", + "scale = {}; {} / {}", "scale = {}; {} % {}", "scale = {}; {} ^ {}", + "{}k {} {} {}|pR", "scale = {}; sqrt({})", "scale = {}; e({})", + "scale = {}; l({})", "scale = {}; a({})", "scale = {}; s({})", + "scale = {}; c({})", "scale = {}; j({}, {})" ] + +div = 3 +mod = 4 +power = 5 +modexp = 6 +sqrt = 7 +exponent = 8 +bessel = 13 + +gen_ops = [] +tests = [] + +try: + i = 0 + while True: + run_test(i) + i = i + 1 +except KeyboardInterrupt: + pass + +if len(tests) == 0: + print("\nNo items in checklist.") + print("Exiting") + sys.exit(0) + +print("\nGoing through the checklist...\n") + +if len(tests) != len(gen_ops): + print("Corrupted checklist!") + print("Exiting...") + sys.exit(1) + +for i in range(0, len(tests)): + + print("\n{}".format(tests[i])) + + op = int(gen_ops[i]) + + if op != modexp: + exe = "bc" + halt = "halt" + options = "-lq" + else: + exe = "dc" + halt = "q" + options = "" + + indata = tests[i] + "\n" + halt + + args = [ exe, options ] + + p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + expected = p.stdout.decode() + + bcexe = exedir + "/" + exe + args = [ bcexe, options ] + + p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + compare(exe, options, p, tests[i], halt, expected, op, False) + + answer = input("\nAdd test ({}/{}) to test suite? [y/N]: ".format(i + 1, len(tests))) + + if 'Y' in answer or 'y' in answer: + + print("Yes") + + name = testdir + "/" + exe + "/" + files[op] + + with open(name + ".txt", "a") as f: + f.write(tests[i] + "\n") + + with open(name + "_results.txt", "a") as f: + f.write(expected) + + else: + print("No") + +print("Done!") |