aboutsummaryrefslogtreecommitdiff
path: root/scripts/randmath.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/randmath.py')
-rwxr-xr-xscripts/randmath.py421
1 files changed, 0 insertions, 421 deletions
diff --git a/scripts/randmath.py b/scripts/randmath.py
deleted file mode 100755
index 896f0e46c97f..000000000000
--- a/scripts/randmath.py
+++ /dev/null
@@ -1,421 +0,0 @@
-#! /usr/bin/python3 -B
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2018-2021 Gavin D. Howard and contributors.
-#
-# 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
-
-# I want line length to *not* affect differences between the two, so I set it
-# as high as possible.
-env = {
- "BC_LINE_LENGTH": "65535",
- "DC_LINE_LENGTH": "65535"
-}
-
-
-# Generate a random integer between 0 and 2^limit.
-# @param limit The power of two for the upper limit.
-def gen(limit=4):
- return random.randint(0, 2 ** (8 * limit))
-
-
-# Returns a random boolean for whether a number should be negative or not.
-def negative():
- return random.randint(0, 1) == 1
-
-
-# Returns a random boolean for whether a number should be 0 or not. I decided to
-# have it be 0 every 2^4 times since sometimes it is used to make a number less
-# than 1.
-def zero():
- return random.randint(0, 2 ** (4) - 1) == 0
-
-
-# Generate a real portion of a number.
-def gen_real():
-
- # Figure out if we should have a real portion. If so generate it.
- if negative():
- n = str(gen(25))
- length = gen(7 / 8)
- if len(n) < length:
- n = ("0" * (length - len(n))) + n
- else:
- n = "0"
-
- return n
-
-
-# Generates a number (as a string) based on the parameters.
-# @param op The operation under test.
-# @param neg Whether the number can be negative.
-# @param real Whether the number can be a non-integer.
-# @param z Whether the number can be zero.
-# @param limit The power of 2 upper limit for the number.
-def num(op, neg, real, z, limit=4):
-
- # Handle zero first.
- if z:
- z = zero()
- else:
- z = False
-
- if z:
- # Generate a real portion maybe
- if real:
- n = gen_real()
- if n != "0":
- return "0." + n
- return "0"
-
- # Figure out if we should be negative.
- if neg:
- neg = negative()
-
- # Generate the integer portion.
- g = gen(limit)
-
- # Figure out if we should have a real number. negative() is used to give a
- # 50/50 chance of getting a negative number.
- if real:
- n = gen_real()
- else:
- n = "0"
-
- # Generate the string.
- g = str(g)
- if n != "0":
- g = g + "." + n
-
- # Make sure to use the right negative sign.
- if neg and g != "0":
- if op != modexp:
- g = "-" + g
- else:
- g = "_" + g
-
- return g
-
-
-# Add a failed test to the list.
-# @param test The test that failed.
-# @param op The operation for the test.
-def add(test, op):
- tests.append(test)
- gen_ops.append(op)
-
-
-# Compare the output between the two.
-# @param exe The executable under test.
-# @param options The command-line options.
-# @param p The object returned from subprocess.run() for the calculator
-# under test.
-# @param test The test.
-# @param halt The halt string for the calculator under test.
-# @param expected The expected result.
-# @param op The operation under test.
-# @param do_add If true, add a failing test to the list, otherwise, don't.
-def compare(exe, options, p, test, halt, expected, op, do_add=True):
-
- # Check for error from the calculator under test.
- 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()
-
- # Check for a difference in output.
- if actual != expected:
-
- if op >= exponent:
-
- # This is here because GNU bc, like mine can be flaky on the
- # functions in the math library. This is basically testing if adding
- # 10 to the scale works to make them match. If so, the difference is
- # only because of that.
- indata = "scale += 10; {}; {}".format(test, halt)
- args = [ exe, options ]
- p2 = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
- expected = p2.stdout[:-10].decode()
-
- if actual == expected:
- print(" failed because of bug in other {}".format(exe))
- print(" continuing...")
- return
-
- # Do the correct output for the situation.
- 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))
-
-
-# Generates a test for op. I made sure that there was no clashing between
-# calculators. Each calculator is responsible for certain ops.
-# @param op The operation to test.
-def gen_test(op):
-
- # First, figure out how big the scale should be.
- scale = num(op, False, False, True, 5 / 8)
-
- # Do the right thing for each op. Generate the test based on the format
- # string and the constraints of each op. For example, some ops can't accept
- # 0 in some arguments, and some must have integers in some arguments.
- 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
-
-
-# Runs a test with number t.
-# @param t The number of the test.
-def run_test(t):
-
- # Randomly select the operation.
- op = random.randrange(bessel + 1)
-
- # Select the right calculator.
- if op != modexp:
- exe = "bc"
- halt = "halt"
- options = "-lq"
- else:
- exe = "dc"
- halt = "q"
- options = ""
-
- # Generate the test.
- test = gen_test(op)
-
- # These don't work very well for some reason.
- if "c(0)" in test or "scale = 4; j(4" in test:
- return
-
- # Make sure the calculator will halt.
- bcexe = exedir + "/" + exe
- indata = test + "\n" + halt
-
- print("Test {}: {}".format(t, test))
-
- # Only bc has options.
- if exe == "bc":
- args = [ exe, options ]
- else:
- args = [ exe ]
-
- # Run the GNU bc.
- p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
-
- output1 = p.stdout.decode()
-
- # Error checking for GNU.
- 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
-
- # Don't know why GNU has this problem...
- if output1 == "-0\n":
- output1 = "0\n"
- elif output1 == "-0":
- output1 = "0"
-
- args = [ bcexe, options ]
-
- # Run this bc/dc and compare.
- p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
- compare(exe, options, p, test, halt, output1, op)
-
-
-# This script must be run by itself.
-if __name__ != "__main__":
- sys.exit(1)
-
-script = sys.argv[0]
-testdir = os.path.dirname(script)
-
-exedir = testdir + "/../bin"
-
-# The following are tables used to generate numbers.
-
-# The operations to test.
-ops = [ '+', '-', '*', '/', '%', '^', '|' ]
-
-# The functions that can be tested.
-funcs = [ "sqrt", "e", "l", "a", "s", "c", "j" ]
-
-# The files (corresponding to the operations with the functions appended) to add
-# tests to if they fail.
-files = [ "add", "subtract", "multiply", "divide", "modulus", "power", "modexp",
- "sqrt", "exponent", "log", "arctangent", "sine", "cosine", "bessel" ]
-
-# The format strings corresponding to each operation and then each function.
-fmts = [ "scale = {}; {} + {}", "scale = {}; {} - {}", "scale = {}; {} * {}",
- "scale = {}; {} / {}", "scale = {}; {} % {}", "scale = {}; {} ^ {}",
- "{}k {} {} {}|pR", "scale = {}; sqrt({})", "scale = {}; e({})",
- "scale = {}; l({})", "scale = {}; a({})", "scale = {}; s({})",
- "scale = {}; c({})", "scale = {}; j({}, {})" ]
-
-# Constants to make some code easier later.
-div = 3
-mod = 4
-power = 5
-modexp = 6
-sqrt = 7
-exponent = 8
-bessel = 13
-
-gen_ops = []
-tests = []
-
-# Infinite loop until the user sends SIGINT.
-try:
- i = 0
- while True:
- run_test(i)
- i = i + 1
-except KeyboardInterrupt:
- pass
-
-# This is where we start processing the checklist of possible failures. Why only
-# possible failures? Because some operations, specifically the functions in the
-# math library, are not guaranteed to be exactly correct. Because of that, we
-# need to present every failed test to the user for a final check before we
-# add them as test cases.
-
-# No items, just exit.
-if len(tests) == 0:
- print("\nNo items in checklist.")
- print("Exiting")
- sys.exit(0)
-
-print("\nGoing through the checklist...\n")
-
-# Just do some error checking. If this fails here, it's a bug in this script.
-if len(tests) != len(gen_ops):
- print("Corrupted checklist!")
- print("Exiting...")
- sys.exit(1)
-
-# Go through each item in the checklist.
-for i in range(0, len(tests)):
-
- # Yes, there's some code duplication. Sue me.
-
- 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 = ""
-
- # We want to run the test again to show the user the difference.
- indata = tests[i] + "\n" + halt
-
- args = [ exe, options ]
-
- p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
-
- expected = p.stdout.decode()
-
- bcexe = exedir + "/" + exe
- args = [ bcexe, options ]
-
- p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
-
- compare(exe, options, p, tests[i], halt, expected, op, False)
-
- # Ask the user to make a decision on the failed test.
- answer = input("\nAdd test ({}/{}) to test suite? [y/N]: ".format(i + 1, len(tests)))
-
- # Quick and dirty answer parsing.
- if 'Y' in answer or 'y' in answer:
-
- print("Yes")
-
- name = testdir + "/" + exe + "/" + files[op]
-
- # Write the test to the test file and the expected result to the
- # results file.
- 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!")