summaryrefslogtreecommitdiff
path: root/tests/randmath.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/randmath.py')
-rwxr-xr-xtests/randmath.py306
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!")