xref: /aosp_15_r20/external/bc/scripts/randmath.py (revision 5a6e848804d15c18a0125914844ee4eb0bda4fcf)
1*5a6e8488SAndroid Build Coastguard Worker#! /usr/bin/python3 -B
2*5a6e8488SAndroid Build Coastguard Worker#
3*5a6e8488SAndroid Build Coastguard Worker# SPDX-License-Identifier: BSD-2-Clause
4*5a6e8488SAndroid Build Coastguard Worker#
5*5a6e8488SAndroid Build Coastguard Worker# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
6*5a6e8488SAndroid Build Coastguard Worker#
7*5a6e8488SAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without
8*5a6e8488SAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are met:
9*5a6e8488SAndroid Build Coastguard Worker#
10*5a6e8488SAndroid Build Coastguard Worker# * Redistributions of source code must retain the above copyright notice, this
11*5a6e8488SAndroid Build Coastguard Worker#   list of conditions and the following disclaimer.
12*5a6e8488SAndroid Build Coastguard Worker#
13*5a6e8488SAndroid Build Coastguard Worker# * Redistributions in binary form must reproduce the above copyright notice,
14*5a6e8488SAndroid Build Coastguard Worker#   this list of conditions and the following disclaimer in the documentation
15*5a6e8488SAndroid Build Coastguard Worker#   and/or other materials provided with the distribution.
16*5a6e8488SAndroid Build Coastguard Worker#
17*5a6e8488SAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18*5a6e8488SAndroid Build Coastguard Worker# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*5a6e8488SAndroid Build Coastguard Worker# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*5a6e8488SAndroid Build Coastguard Worker# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21*5a6e8488SAndroid Build Coastguard Worker# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22*5a6e8488SAndroid Build Coastguard Worker# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23*5a6e8488SAndroid Build Coastguard Worker# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*5a6e8488SAndroid Build Coastguard Worker# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25*5a6e8488SAndroid Build Coastguard Worker# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26*5a6e8488SAndroid Build Coastguard Worker# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27*5a6e8488SAndroid Build Coastguard Worker# POSSIBILITY OF SUCH DAMAGE.
28*5a6e8488SAndroid Build Coastguard Worker#
29*5a6e8488SAndroid Build Coastguard Worker
30*5a6e8488SAndroid Build Coastguard Workerimport os, errno
31*5a6e8488SAndroid Build Coastguard Workerimport random
32*5a6e8488SAndroid Build Coastguard Workerimport sys
33*5a6e8488SAndroid Build Coastguard Workerimport subprocess
34*5a6e8488SAndroid Build Coastguard Worker
35*5a6e8488SAndroid Build Coastguard Worker# I want line length to *not* affect differences between the two, so I set it
36*5a6e8488SAndroid Build Coastguard Worker# as high as possible.
37*5a6e8488SAndroid Build Coastguard Workerenv = {
38*5a6e8488SAndroid Build Coastguard Worker	"BC_LINE_LENGTH": "65535",
39*5a6e8488SAndroid Build Coastguard Worker	"DC_LINE_LENGTH": "65535"
40*5a6e8488SAndroid Build Coastguard Worker}
41*5a6e8488SAndroid Build Coastguard Worker
42*5a6e8488SAndroid Build Coastguard Worker
43*5a6e8488SAndroid Build Coastguard Worker# Generate a random integer between 0 and 2^limit.
44*5a6e8488SAndroid Build Coastguard Worker# @param limit  The power of two for the upper limit.
45*5a6e8488SAndroid Build Coastguard Workerdef gen(limit=4):
46*5a6e8488SAndroid Build Coastguard Worker	return random.randint(0, 2 ** (8 * limit))
47*5a6e8488SAndroid Build Coastguard Worker
48*5a6e8488SAndroid Build Coastguard Worker
49*5a6e8488SAndroid Build Coastguard Worker# Returns a random boolean for whether a number should be negative or not.
50*5a6e8488SAndroid Build Coastguard Workerdef negative():
51*5a6e8488SAndroid Build Coastguard Worker	return random.randint(0, 1) == 1
52*5a6e8488SAndroid Build Coastguard Worker
53*5a6e8488SAndroid Build Coastguard Worker
54*5a6e8488SAndroid Build Coastguard Worker# Returns a random boolean for whether a number should be 0 or not. I decided to
55*5a6e8488SAndroid Build Coastguard Worker# have it be 0 every 2^4 times since sometimes it is used to make a number less
56*5a6e8488SAndroid Build Coastguard Worker# than 1.
57*5a6e8488SAndroid Build Coastguard Workerdef zero():
58*5a6e8488SAndroid Build Coastguard Worker	return random.randint(0, 2 ** (4) - 1) == 0
59*5a6e8488SAndroid Build Coastguard Worker
60*5a6e8488SAndroid Build Coastguard Worker
61*5a6e8488SAndroid Build Coastguard Worker# Generate a real portion of a number.
62*5a6e8488SAndroid Build Coastguard Workerdef gen_real():
63*5a6e8488SAndroid Build Coastguard Worker
64*5a6e8488SAndroid Build Coastguard Worker	# Figure out if we should have a real portion. If so generate it.
65*5a6e8488SAndroid Build Coastguard Worker	if negative():
66*5a6e8488SAndroid Build Coastguard Worker		n = str(gen(25))
67*5a6e8488SAndroid Build Coastguard Worker		length = gen(7 / 8)
68*5a6e8488SAndroid Build Coastguard Worker		if len(n) < length:
69*5a6e8488SAndroid Build Coastguard Worker			n = ("0" * (length - len(n))) + n
70*5a6e8488SAndroid Build Coastguard Worker	else:
71*5a6e8488SAndroid Build Coastguard Worker		n = "0"
72*5a6e8488SAndroid Build Coastguard Worker
73*5a6e8488SAndroid Build Coastguard Worker	return n
74*5a6e8488SAndroid Build Coastguard Worker
75*5a6e8488SAndroid Build Coastguard Worker
76*5a6e8488SAndroid Build Coastguard Worker# Generates a number (as a string) based on the parameters.
77*5a6e8488SAndroid Build Coastguard Worker# @param op     The operation under test.
78*5a6e8488SAndroid Build Coastguard Worker# @param neg    Whether the number can be negative.
79*5a6e8488SAndroid Build Coastguard Worker# @param real   Whether the number can be a non-integer.
80*5a6e8488SAndroid Build Coastguard Worker# @param z      Whether the number can be zero.
81*5a6e8488SAndroid Build Coastguard Worker# @param limit  The power of 2 upper limit for the number.
82*5a6e8488SAndroid Build Coastguard Workerdef num(op, neg, real, z, limit=4):
83*5a6e8488SAndroid Build Coastguard Worker
84*5a6e8488SAndroid Build Coastguard Worker	# Handle zero first.
85*5a6e8488SAndroid Build Coastguard Worker	if z:
86*5a6e8488SAndroid Build Coastguard Worker		z = zero()
87*5a6e8488SAndroid Build Coastguard Worker	else:
88*5a6e8488SAndroid Build Coastguard Worker		z = False
89*5a6e8488SAndroid Build Coastguard Worker
90*5a6e8488SAndroid Build Coastguard Worker	if z:
91*5a6e8488SAndroid Build Coastguard Worker		# Generate a real portion maybe
92*5a6e8488SAndroid Build Coastguard Worker		if real:
93*5a6e8488SAndroid Build Coastguard Worker			n = gen_real()
94*5a6e8488SAndroid Build Coastguard Worker			if n != "0":
95*5a6e8488SAndroid Build Coastguard Worker				return "0." + n
96*5a6e8488SAndroid Build Coastguard Worker		return "0"
97*5a6e8488SAndroid Build Coastguard Worker
98*5a6e8488SAndroid Build Coastguard Worker	# Figure out if we should be negative.
99*5a6e8488SAndroid Build Coastguard Worker	if neg:
100*5a6e8488SAndroid Build Coastguard Worker		neg = negative()
101*5a6e8488SAndroid Build Coastguard Worker
102*5a6e8488SAndroid Build Coastguard Worker	# Generate the integer portion.
103*5a6e8488SAndroid Build Coastguard Worker	g = gen(limit)
104*5a6e8488SAndroid Build Coastguard Worker
105*5a6e8488SAndroid Build Coastguard Worker	# Figure out if we should have a real number. negative() is used to give a
106*5a6e8488SAndroid Build Coastguard Worker	# 50/50 chance of getting a negative number.
107*5a6e8488SAndroid Build Coastguard Worker	if real:
108*5a6e8488SAndroid Build Coastguard Worker		n = gen_real()
109*5a6e8488SAndroid Build Coastguard Worker	else:
110*5a6e8488SAndroid Build Coastguard Worker		n = "0"
111*5a6e8488SAndroid Build Coastguard Worker
112*5a6e8488SAndroid Build Coastguard Worker	# Generate the string.
113*5a6e8488SAndroid Build Coastguard Worker	g = str(g)
114*5a6e8488SAndroid Build Coastguard Worker	if n != "0":
115*5a6e8488SAndroid Build Coastguard Worker		g = g + "." + n
116*5a6e8488SAndroid Build Coastguard Worker
117*5a6e8488SAndroid Build Coastguard Worker	# Make sure to use the right negative sign.
118*5a6e8488SAndroid Build Coastguard Worker	if neg and g != "0":
119*5a6e8488SAndroid Build Coastguard Worker		if op != modexp:
120*5a6e8488SAndroid Build Coastguard Worker			g = "-" + g
121*5a6e8488SAndroid Build Coastguard Worker		else:
122*5a6e8488SAndroid Build Coastguard Worker			g = "_" + g
123*5a6e8488SAndroid Build Coastguard Worker
124*5a6e8488SAndroid Build Coastguard Worker	return g
125*5a6e8488SAndroid Build Coastguard Worker
126*5a6e8488SAndroid Build Coastguard Worker
127*5a6e8488SAndroid Build Coastguard Worker# Add a failed test to the list.
128*5a6e8488SAndroid Build Coastguard Worker# @param test  The test that failed.
129*5a6e8488SAndroid Build Coastguard Worker# @param op    The operation for the test.
130*5a6e8488SAndroid Build Coastguard Workerdef add(test, op):
131*5a6e8488SAndroid Build Coastguard Worker	tests.append(test)
132*5a6e8488SAndroid Build Coastguard Worker	gen_ops.append(op)
133*5a6e8488SAndroid Build Coastguard Worker
134*5a6e8488SAndroid Build Coastguard Worker
135*5a6e8488SAndroid Build Coastguard Worker# Compare the output between the two.
136*5a6e8488SAndroid Build Coastguard Worker# @param exe       The executable under test.
137*5a6e8488SAndroid Build Coastguard Worker# @param options   The command-line options.
138*5a6e8488SAndroid Build Coastguard Worker# @param p         The object returned from subprocess.run() for the calculator
139*5a6e8488SAndroid Build Coastguard Worker#                  under test.
140*5a6e8488SAndroid Build Coastguard Worker# @param test      The test.
141*5a6e8488SAndroid Build Coastguard Worker# @param halt      The halt string for the calculator under test.
142*5a6e8488SAndroid Build Coastguard Worker# @param expected  The expected result.
143*5a6e8488SAndroid Build Coastguard Worker# @param op        The operation under test.
144*5a6e8488SAndroid Build Coastguard Worker# @param do_add    If true, add a failing test to the list, otherwise, don't.
145*5a6e8488SAndroid Build Coastguard Workerdef compare(exe, options, p, test, halt, expected, op, do_add=True):
146*5a6e8488SAndroid Build Coastguard Worker
147*5a6e8488SAndroid Build Coastguard Worker	# Check for error from the calculator under test.
148*5a6e8488SAndroid Build Coastguard Worker	if p.returncode != 0:
149*5a6e8488SAndroid Build Coastguard Worker
150*5a6e8488SAndroid Build Coastguard Worker		print("    {} returned an error ({})".format(exe, p.returncode))
151*5a6e8488SAndroid Build Coastguard Worker
152*5a6e8488SAndroid Build Coastguard Worker		if do_add:
153*5a6e8488SAndroid Build Coastguard Worker			print("    adding to checklist...")
154*5a6e8488SAndroid Build Coastguard Worker			add(test, op)
155*5a6e8488SAndroid Build Coastguard Worker
156*5a6e8488SAndroid Build Coastguard Worker		return
157*5a6e8488SAndroid Build Coastguard Worker
158*5a6e8488SAndroid Build Coastguard Worker	actual = p.stdout.decode()
159*5a6e8488SAndroid Build Coastguard Worker
160*5a6e8488SAndroid Build Coastguard Worker	# Check for a difference in output.
161*5a6e8488SAndroid Build Coastguard Worker	if actual != expected:
162*5a6e8488SAndroid Build Coastguard Worker
163*5a6e8488SAndroid Build Coastguard Worker		if op >= exponent:
164*5a6e8488SAndroid Build Coastguard Worker
165*5a6e8488SAndroid Build Coastguard Worker			# This is here because GNU bc, like mine can be flaky on the
166*5a6e8488SAndroid Build Coastguard Worker			# functions in the math library. This is basically testing if adding
167*5a6e8488SAndroid Build Coastguard Worker			# 10 to the scale works to make them match. If so, the difference is
168*5a6e8488SAndroid Build Coastguard Worker			# only because of that.
169*5a6e8488SAndroid Build Coastguard Worker			indata = "scale += 10; {}; {}".format(test, halt)
170*5a6e8488SAndroid Build Coastguard Worker			args = [ exe, options ]
171*5a6e8488SAndroid Build Coastguard Worker			p2 = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
172*5a6e8488SAndroid Build Coastguard Worker			expected = p2.stdout[:-10].decode()
173*5a6e8488SAndroid Build Coastguard Worker
174*5a6e8488SAndroid Build Coastguard Worker			if actual == expected:
175*5a6e8488SAndroid Build Coastguard Worker				print("    failed because of bug in other {}".format(exe))
176*5a6e8488SAndroid Build Coastguard Worker				print("    continuing...")
177*5a6e8488SAndroid Build Coastguard Worker				return
178*5a6e8488SAndroid Build Coastguard Worker
179*5a6e8488SAndroid Build Coastguard Worker		# Do the correct output for the situation.
180*5a6e8488SAndroid Build Coastguard Worker		if do_add:
181*5a6e8488SAndroid Build Coastguard Worker			print("   failed; adding to checklist...")
182*5a6e8488SAndroid Build Coastguard Worker			add(test, op)
183*5a6e8488SAndroid Build Coastguard Worker		else:
184*5a6e8488SAndroid Build Coastguard Worker			print("   failed {}".format(test))
185*5a6e8488SAndroid Build Coastguard Worker			print("    expected:")
186*5a6e8488SAndroid Build Coastguard Worker			print("        {}".format(expected))
187*5a6e8488SAndroid Build Coastguard Worker			print("    actual:")
188*5a6e8488SAndroid Build Coastguard Worker			print("        {}".format(actual))
189*5a6e8488SAndroid Build Coastguard Worker
190*5a6e8488SAndroid Build Coastguard Worker
191*5a6e8488SAndroid Build Coastguard Worker# Generates a test for op. I made sure that there was no clashing between
192*5a6e8488SAndroid Build Coastguard Worker# calculators. Each calculator is responsible for certain ops.
193*5a6e8488SAndroid Build Coastguard Worker# @param op  The operation to test.
194*5a6e8488SAndroid Build Coastguard Workerdef gen_test(op):
195*5a6e8488SAndroid Build Coastguard Worker
196*5a6e8488SAndroid Build Coastguard Worker	# First, figure out how big the scale should be.
197*5a6e8488SAndroid Build Coastguard Worker	scale = num(op, False, False, True, 5 / 8)
198*5a6e8488SAndroid Build Coastguard Worker
199*5a6e8488SAndroid Build Coastguard Worker	# Do the right thing for each op. Generate the test based on the format
200*5a6e8488SAndroid Build Coastguard Worker	# string and the constraints of each op. For example, some ops can't accept
201*5a6e8488SAndroid Build Coastguard Worker	# 0 in some arguments, and some must have integers in some arguments.
202*5a6e8488SAndroid Build Coastguard Worker	if op < div:
203*5a6e8488SAndroid Build Coastguard Worker		s = fmts[op].format(scale, num(op, True, True, True), num(op, True, True, True))
204*5a6e8488SAndroid Build Coastguard Worker	elif op == div or op == mod:
205*5a6e8488SAndroid Build Coastguard Worker		s = fmts[op].format(scale, num(op, True, True, True), num(op, True, True, False))
206*5a6e8488SAndroid Build Coastguard Worker	elif op == power:
207*5a6e8488SAndroid Build Coastguard Worker		s = fmts[op].format(scale, num(op, True, True, True, 7 / 8), num(op, True, False, True, 6 / 8))
208*5a6e8488SAndroid Build Coastguard Worker	elif op == modexp:
209*5a6e8488SAndroid Build Coastguard Worker		s = fmts[op].format(scale, num(op, True, False, True), num(op, True, False, True),
210*5a6e8488SAndroid Build Coastguard Worker		                    num(op, True, False, False))
211*5a6e8488SAndroid Build Coastguard Worker	elif op == sqrt:
212*5a6e8488SAndroid Build Coastguard Worker		s = "1"
213*5a6e8488SAndroid Build Coastguard Worker		while s == "1":
214*5a6e8488SAndroid Build Coastguard Worker			s = num(op, False, True, True, 1)
215*5a6e8488SAndroid Build Coastguard Worker		s = fmts[op].format(scale, s)
216*5a6e8488SAndroid Build Coastguard Worker	else:
217*5a6e8488SAndroid Build Coastguard Worker
218*5a6e8488SAndroid Build Coastguard Worker		if op == exponent:
219*5a6e8488SAndroid Build Coastguard Worker			first = num(op, True, True, True, 6 / 8)
220*5a6e8488SAndroid Build Coastguard Worker		elif op == bessel:
221*5a6e8488SAndroid Build Coastguard Worker			first = num(op, False, True, True, 6 / 8)
222*5a6e8488SAndroid Build Coastguard Worker		else:
223*5a6e8488SAndroid Build Coastguard Worker			first = num(op, True, True, True)
224*5a6e8488SAndroid Build Coastguard Worker
225*5a6e8488SAndroid Build Coastguard Worker		if op != bessel:
226*5a6e8488SAndroid Build Coastguard Worker			s = fmts[op].format(scale, first)
227*5a6e8488SAndroid Build Coastguard Worker		else:
228*5a6e8488SAndroid Build Coastguard Worker			s = fmts[op].format(scale, first, 6 / 8)
229*5a6e8488SAndroid Build Coastguard Worker
230*5a6e8488SAndroid Build Coastguard Worker	return s
231*5a6e8488SAndroid Build Coastguard Worker
232*5a6e8488SAndroid Build Coastguard Worker
233*5a6e8488SAndroid Build Coastguard Worker# Runs a test with number t.
234*5a6e8488SAndroid Build Coastguard Worker# @param t  The number of the test.
235*5a6e8488SAndroid Build Coastguard Workerdef run_test(t):
236*5a6e8488SAndroid Build Coastguard Worker
237*5a6e8488SAndroid Build Coastguard Worker	# Randomly select the operation.
238*5a6e8488SAndroid Build Coastguard Worker	op = random.randrange(bessel + 1)
239*5a6e8488SAndroid Build Coastguard Worker
240*5a6e8488SAndroid Build Coastguard Worker	# Select the right calculator.
241*5a6e8488SAndroid Build Coastguard Worker	if op != modexp:
242*5a6e8488SAndroid Build Coastguard Worker		exe = "bc"
243*5a6e8488SAndroid Build Coastguard Worker		halt = "halt"
244*5a6e8488SAndroid Build Coastguard Worker		options = "-lq"
245*5a6e8488SAndroid Build Coastguard Worker	else:
246*5a6e8488SAndroid Build Coastguard Worker		exe = "dc"
247*5a6e8488SAndroid Build Coastguard Worker		halt = "q"
248*5a6e8488SAndroid Build Coastguard Worker		options = ""
249*5a6e8488SAndroid Build Coastguard Worker
250*5a6e8488SAndroid Build Coastguard Worker	# Generate the test.
251*5a6e8488SAndroid Build Coastguard Worker	test = gen_test(op)
252*5a6e8488SAndroid Build Coastguard Worker
253*5a6e8488SAndroid Build Coastguard Worker	# These don't work very well for some reason.
254*5a6e8488SAndroid Build Coastguard Worker	if "c(0)" in test or "scale = 4; j(4" in test:
255*5a6e8488SAndroid Build Coastguard Worker		return
256*5a6e8488SAndroid Build Coastguard Worker
257*5a6e8488SAndroid Build Coastguard Worker	# Make sure the calculator will halt.
258*5a6e8488SAndroid Build Coastguard Worker	bcexe = exedir + "/" + exe
259*5a6e8488SAndroid Build Coastguard Worker	indata = test + "\n" + halt
260*5a6e8488SAndroid Build Coastguard Worker
261*5a6e8488SAndroid Build Coastguard Worker	print("Test {}: {}".format(t, test))
262*5a6e8488SAndroid Build Coastguard Worker
263*5a6e8488SAndroid Build Coastguard Worker	# Only bc has options.
264*5a6e8488SAndroid Build Coastguard Worker	if exe == "bc":
265*5a6e8488SAndroid Build Coastguard Worker		args = [ exe, options ]
266*5a6e8488SAndroid Build Coastguard Worker	else:
267*5a6e8488SAndroid Build Coastguard Worker		args = [ exe ]
268*5a6e8488SAndroid Build Coastguard Worker
269*5a6e8488SAndroid Build Coastguard Worker	# Run the GNU bc.
270*5a6e8488SAndroid Build Coastguard Worker	p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
271*5a6e8488SAndroid Build Coastguard Worker
272*5a6e8488SAndroid Build Coastguard Worker	output1 = p.stdout.decode()
273*5a6e8488SAndroid Build Coastguard Worker
274*5a6e8488SAndroid Build Coastguard Worker	# Error checking for GNU.
275*5a6e8488SAndroid Build Coastguard Worker	if p.returncode != 0 or output1 == "":
276*5a6e8488SAndroid Build Coastguard Worker		print("    other {} returned an error ({}); continuing...".format(exe, p.returncode))
277*5a6e8488SAndroid Build Coastguard Worker		return
278*5a6e8488SAndroid Build Coastguard Worker
279*5a6e8488SAndroid Build Coastguard Worker	if output1 == "\n":
280*5a6e8488SAndroid Build Coastguard Worker		print("   other {} has a bug; continuing...".format(exe))
281*5a6e8488SAndroid Build Coastguard Worker		return
282*5a6e8488SAndroid Build Coastguard Worker
283*5a6e8488SAndroid Build Coastguard Worker	# Don't know why GNU has this problem...
284*5a6e8488SAndroid Build Coastguard Worker	if output1 == "-0\n":
285*5a6e8488SAndroid Build Coastguard Worker		output1 = "0\n"
286*5a6e8488SAndroid Build Coastguard Worker	elif output1 == "-0":
287*5a6e8488SAndroid Build Coastguard Worker		output1 = "0"
288*5a6e8488SAndroid Build Coastguard Worker
289*5a6e8488SAndroid Build Coastguard Worker	args = [ bcexe, options ]
290*5a6e8488SAndroid Build Coastguard Worker
291*5a6e8488SAndroid Build Coastguard Worker	# Run this bc/dc and compare.
292*5a6e8488SAndroid Build Coastguard Worker	p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
293*5a6e8488SAndroid Build Coastguard Worker	compare(exe, options, p, test, halt, output1, op)
294*5a6e8488SAndroid Build Coastguard Worker
295*5a6e8488SAndroid Build Coastguard Worker
296*5a6e8488SAndroid Build Coastguard Worker# This script must be run by itself.
297*5a6e8488SAndroid Build Coastguard Workerif __name__ != "__main__":
298*5a6e8488SAndroid Build Coastguard Worker	sys.exit(1)
299*5a6e8488SAndroid Build Coastguard Worker
300*5a6e8488SAndroid Build Coastguard Workerscript = sys.argv[0]
301*5a6e8488SAndroid Build Coastguard Workertestdir = os.path.dirname(script)
302*5a6e8488SAndroid Build Coastguard Worker
303*5a6e8488SAndroid Build Coastguard Workerexedir = testdir + "/../bin"
304*5a6e8488SAndroid Build Coastguard Worker
305*5a6e8488SAndroid Build Coastguard Worker# The following are tables used to generate numbers.
306*5a6e8488SAndroid Build Coastguard Worker
307*5a6e8488SAndroid Build Coastguard Worker# The operations to test.
308*5a6e8488SAndroid Build Coastguard Workerops = [ '+', '-', '*', '/', '%', '^', '|' ]
309*5a6e8488SAndroid Build Coastguard Worker
310*5a6e8488SAndroid Build Coastguard Worker# The functions that can be tested.
311*5a6e8488SAndroid Build Coastguard Workerfuncs = [ "sqrt", "e", "l", "a", "s", "c", "j" ]
312*5a6e8488SAndroid Build Coastguard Worker
313*5a6e8488SAndroid Build Coastguard Worker# The files (corresponding to the operations with the functions appended) to add
314*5a6e8488SAndroid Build Coastguard Worker# tests to if they fail.
315*5a6e8488SAndroid Build Coastguard Workerfiles = [ "add", "subtract", "multiply", "divide", "modulus", "power", "modexp",
316*5a6e8488SAndroid Build Coastguard Worker          "sqrt", "exponent", "log", "arctangent", "sine", "cosine", "bessel" ]
317*5a6e8488SAndroid Build Coastguard Worker
318*5a6e8488SAndroid Build Coastguard Worker# The format strings corresponding to each operation and then each function.
319*5a6e8488SAndroid Build Coastguard Workerfmts = [ "scale = {}; {} + {}", "scale = {}; {} - {}", "scale = {}; {} * {}",
320*5a6e8488SAndroid Build Coastguard Worker         "scale = {}; {} / {}", "scale = {}; {} % {}", "scale = {}; {} ^ {}",
321*5a6e8488SAndroid Build Coastguard Worker         "{}k {} {} {}|pR", "scale = {}; sqrt({})", "scale = {}; e({})",
322*5a6e8488SAndroid Build Coastguard Worker         "scale = {}; l({})", "scale = {}; a({})", "scale = {}; s({})",
323*5a6e8488SAndroid Build Coastguard Worker         "scale = {}; c({})", "scale = {}; j({}, {})" ]
324*5a6e8488SAndroid Build Coastguard Worker
325*5a6e8488SAndroid Build Coastguard Worker# Constants to make some code easier later.
326*5a6e8488SAndroid Build Coastguard Workerdiv = 3
327*5a6e8488SAndroid Build Coastguard Workermod = 4
328*5a6e8488SAndroid Build Coastguard Workerpower = 5
329*5a6e8488SAndroid Build Coastguard Workermodexp = 6
330*5a6e8488SAndroid Build Coastguard Workersqrt = 7
331*5a6e8488SAndroid Build Coastguard Workerexponent = 8
332*5a6e8488SAndroid Build Coastguard Workerbessel = 13
333*5a6e8488SAndroid Build Coastguard Worker
334*5a6e8488SAndroid Build Coastguard Workergen_ops = []
335*5a6e8488SAndroid Build Coastguard Workertests = []
336*5a6e8488SAndroid Build Coastguard Worker
337*5a6e8488SAndroid Build Coastguard Worker# Infinite loop until the user sends SIGINT.
338*5a6e8488SAndroid Build Coastguard Workertry:
339*5a6e8488SAndroid Build Coastguard Worker	i = 0
340*5a6e8488SAndroid Build Coastguard Worker	while True:
341*5a6e8488SAndroid Build Coastguard Worker		run_test(i)
342*5a6e8488SAndroid Build Coastguard Worker		i = i + 1
343*5a6e8488SAndroid Build Coastguard Workerexcept KeyboardInterrupt:
344*5a6e8488SAndroid Build Coastguard Worker	pass
345*5a6e8488SAndroid Build Coastguard Worker
346*5a6e8488SAndroid Build Coastguard Worker# This is where we start processing the checklist of possible failures. Why only
347*5a6e8488SAndroid Build Coastguard Worker# possible failures? Because some operations, specifically the functions in the
348*5a6e8488SAndroid Build Coastguard Worker# math library, are not guaranteed to be exactly correct. Because of that, we
349*5a6e8488SAndroid Build Coastguard Worker# need to present every failed test to the user for a final check before we
350*5a6e8488SAndroid Build Coastguard Worker# add them as test cases.
351*5a6e8488SAndroid Build Coastguard Worker
352*5a6e8488SAndroid Build Coastguard Worker# No items, just exit.
353*5a6e8488SAndroid Build Coastguard Workerif len(tests) == 0:
354*5a6e8488SAndroid Build Coastguard Worker	print("\nNo items in checklist.")
355*5a6e8488SAndroid Build Coastguard Worker	print("Exiting")
356*5a6e8488SAndroid Build Coastguard Worker	sys.exit(0)
357*5a6e8488SAndroid Build Coastguard Worker
358*5a6e8488SAndroid Build Coastguard Workerprint("\nGoing through the checklist...\n")
359*5a6e8488SAndroid Build Coastguard Worker
360*5a6e8488SAndroid Build Coastguard Worker# Just do some error checking. If this fails here, it's a bug in this script.
361*5a6e8488SAndroid Build Coastguard Workerif len(tests) != len(gen_ops):
362*5a6e8488SAndroid Build Coastguard Worker	print("Corrupted checklist!")
363*5a6e8488SAndroid Build Coastguard Worker	print("Exiting...")
364*5a6e8488SAndroid Build Coastguard Worker	sys.exit(1)
365*5a6e8488SAndroid Build Coastguard Worker
366*5a6e8488SAndroid Build Coastguard Worker# Go through each item in the checklist.
367*5a6e8488SAndroid Build Coastguard Workerfor i in range(0, len(tests)):
368*5a6e8488SAndroid Build Coastguard Worker
369*5a6e8488SAndroid Build Coastguard Worker	# Yes, there's some code duplication. Sue me.
370*5a6e8488SAndroid Build Coastguard Worker
371*5a6e8488SAndroid Build Coastguard Worker	print("\n{}".format(tests[i]))
372*5a6e8488SAndroid Build Coastguard Worker
373*5a6e8488SAndroid Build Coastguard Worker	op = int(gen_ops[i])
374*5a6e8488SAndroid Build Coastguard Worker
375*5a6e8488SAndroid Build Coastguard Worker	if op != modexp:
376*5a6e8488SAndroid Build Coastguard Worker		exe = "bc"
377*5a6e8488SAndroid Build Coastguard Worker		halt = "halt"
378*5a6e8488SAndroid Build Coastguard Worker		options = "-lq"
379*5a6e8488SAndroid Build Coastguard Worker	else:
380*5a6e8488SAndroid Build Coastguard Worker		exe = "dc"
381*5a6e8488SAndroid Build Coastguard Worker		halt = "q"
382*5a6e8488SAndroid Build Coastguard Worker		options = ""
383*5a6e8488SAndroid Build Coastguard Worker
384*5a6e8488SAndroid Build Coastguard Worker	# We want to run the test again to show the user the difference.
385*5a6e8488SAndroid Build Coastguard Worker	indata = tests[i] + "\n" + halt
386*5a6e8488SAndroid Build Coastguard Worker
387*5a6e8488SAndroid Build Coastguard Worker	args = [ exe, options ]
388*5a6e8488SAndroid Build Coastguard Worker
389*5a6e8488SAndroid Build Coastguard Worker	p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
390*5a6e8488SAndroid Build Coastguard Worker
391*5a6e8488SAndroid Build Coastguard Worker	expected = p.stdout.decode()
392*5a6e8488SAndroid Build Coastguard Worker
393*5a6e8488SAndroid Build Coastguard Worker	bcexe = exedir + "/" + exe
394*5a6e8488SAndroid Build Coastguard Worker	args = [ bcexe, options ]
395*5a6e8488SAndroid Build Coastguard Worker
396*5a6e8488SAndroid Build Coastguard Worker	p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
397*5a6e8488SAndroid Build Coastguard Worker
398*5a6e8488SAndroid Build Coastguard Worker	compare(exe, options, p, tests[i], halt, expected, op, False)
399*5a6e8488SAndroid Build Coastguard Worker
400*5a6e8488SAndroid Build Coastguard Worker	# Ask the user to make a decision on the failed test.
401*5a6e8488SAndroid Build Coastguard Worker	answer = input("\nAdd test ({}/{}) to test suite? [y/N]: ".format(i + 1, len(tests)))
402*5a6e8488SAndroid Build Coastguard Worker
403*5a6e8488SAndroid Build Coastguard Worker	# Quick and dirty answer parsing.
404*5a6e8488SAndroid Build Coastguard Worker	if 'Y' in answer or 'y' in answer:
405*5a6e8488SAndroid Build Coastguard Worker
406*5a6e8488SAndroid Build Coastguard Worker		print("Yes")
407*5a6e8488SAndroid Build Coastguard Worker
408*5a6e8488SAndroid Build Coastguard Worker		name = testdir + "/" + exe + "/" + files[op]
409*5a6e8488SAndroid Build Coastguard Worker
410*5a6e8488SAndroid Build Coastguard Worker		# Write the test to the test file and the expected result to the
411*5a6e8488SAndroid Build Coastguard Worker		# results file.
412*5a6e8488SAndroid Build Coastguard Worker		with open(name + ".txt", "a") as f:
413*5a6e8488SAndroid Build Coastguard Worker			f.write(tests[i] + "\n")
414*5a6e8488SAndroid Build Coastguard Worker
415*5a6e8488SAndroid Build Coastguard Worker		with open(name + "_results.txt", "a") as f:
416*5a6e8488SAndroid Build Coastguard Worker			f.write(expected)
417*5a6e8488SAndroid Build Coastguard Worker
418*5a6e8488SAndroid Build Coastguard Worker	else:
419*5a6e8488SAndroid Build Coastguard Worker		print("No")
420*5a6e8488SAndroid Build Coastguard Worker
421*5a6e8488SAndroid Build Coastguard Workerprint("Done!")
422