]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bc/tests/randmath.py
MFC: 362681, 362697, 362914, 362984, 362986, 362987, 363091, 363172, 363809,
[FreeBSD/FreeBSD.git] / contrib / bc / tests / randmath.py
1 #! /usr/bin/python3 -B
2 #
3 # SPDX-License-Identifier: BSD-2-Clause
4 #
5 # Copyright (c) 2018-2020 Gavin D. Howard and contributors.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 #   list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice,
14 #   this list of conditions and the following disclaimer in the documentation
15 #   and/or other materials provided with the distribution.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29
30 import os, errno
31 import random
32 import sys
33 import subprocess
34
35 def gen(limit=4):
36         return random.randint(0, 2 ** (8 * limit))
37
38 def negative():
39         return random.randint(0, 1) == 1
40
41 def zero():
42         return random.randint(0, 2 ** (8) - 1) == 0
43
44 def num(op, neg, real, z, limit=4):
45
46         if z:
47                 z = zero()
48         else:
49                 z = False
50
51         if z:
52                 return 0
53
54         if neg:
55                 neg = negative()
56
57         g = gen(limit)
58
59         if real and negative():
60                 n = str(gen(25))
61                 length = gen(7 / 8)
62                 if len(n) < length:
63                         n = ("0" * (length - len(n))) + n
64         else:
65                 n = "0"
66
67         g = str(g)
68         if n != "0":
69                 g = g + "." + n
70
71         if neg and g != "0":
72                 if op != modexp:
73                         g = "-" + g
74                 else:
75                         g = "_" + g
76
77         return g
78
79
80 def add(test, op):
81
82         tests.append(test)
83         gen_ops.append(op)
84
85 def compare(exe, options, p, test, halt, expected, op, do_add=True):
86
87         if p.returncode != 0:
88
89                 print("    {} returned an error ({})".format(exe, p.returncode))
90
91                 if do_add:
92                         print("    adding to checklist...")
93                         add(test, op)
94
95                 return
96
97         actual = p.stdout.decode()
98
99         if actual != expected:
100
101                 if op >= exponent:
102
103                         indata = "scale += 10; {}; {}".format(test, halt)
104                         args = [ exe, options ]
105                         p2 = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
106                         expected = p2.stdout[:-10].decode()
107
108                         if actual == expected:
109                                 print("    failed because of bug in other {}".format(exe))
110                                 print("    continuing...")
111                                 return
112
113                 if do_add:
114                         print("   failed; adding to checklist...")
115                         add(test, op)
116                 else:
117                         print("   failed {}".format(test))
118                         print("    expected:")
119                         print("        {}".format(expected))
120                         print("    actual:")
121                         print("        {}".format(actual))
122
123
124 def gen_test(op):
125
126         scale = num(op, False, False, True, 5 / 8)
127
128         if op < div:
129                 s = fmts[op].format(scale, num(op, True, True, True), num(op, True, True, True))
130         elif op == div or op == mod:
131                 s = fmts[op].format(scale, num(op, True, True, True), num(op, True, True, False))
132         elif op == power:
133                 s = fmts[op].format(scale, num(op, True, True, True, 7 / 8), num(op, True, False, True, 6 / 8))
134         elif op == modexp:
135                 s = fmts[op].format(scale, num(op, True, False, True), num(op, True, False, True),
136                                     num(op, True, False, False))
137         elif op == sqrt:
138                 s = "1"
139                 while s == "1":
140                         s = num(op, False, True, True, 1)
141                 s = fmts[op].format(scale, s)
142         else:
143
144                 if op == exponent:
145                         first = num(op, True, True, True, 6 / 8)
146                 elif op == bessel:
147                         first = num(op, False, True, True, 6 / 8)
148                 else:
149                         first = num(op, True, True, True)
150
151                 if op != bessel:
152                         s = fmts[op].format(scale, first)
153                 else:
154                         s = fmts[op].format(scale, first, 6 / 8)
155
156         return s
157
158 def run_test(t):
159
160         op = random.randrange(bessel + 1)
161
162         if op != modexp:
163                 exe = "bc"
164                 halt = "halt"
165                 options = "-lq"
166         else:
167                 exe = "dc"
168                 halt = "q"
169                 options = ""
170
171         test = gen_test(op)
172
173         if "c(0)" in test or "scale = 4; j(4" in test:
174                 return
175
176         bcexe = exedir + "/" + exe
177         indata = test + "\n" + halt
178
179         print("Test {}: {}".format(t, test))
180
181         if exe == "bc":
182                 args = [ exe, options ]
183         else:
184                 args = [ exe ]
185
186         p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
187
188         output1 = p.stdout.decode()
189
190         if p.returncode != 0 or output1 == "":
191                 print("    other {} returned an error ({}); continuing...".format(exe, p.returncode))
192                 return
193
194         if output1 == "\n":
195                 print("   other {} has a bug; continuing...".format(exe))
196                 return
197
198         if output1 == "-0\n":
199                 output1 = "0\n"
200         elif output1 == "-0":
201                 output1 = "0"
202
203         args = [ bcexe, options ]
204
205         p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
206         compare(exe, options, p, test, halt, output1, op)
207
208
209 if __name__ != "__main__":
210         sys.exit(1)
211
212 script = sys.argv[0]
213 testdir = os.path.dirname(script)
214
215 exedir = testdir + "/../bin"
216
217 ops = [ '+', '-', '*', '/', '%', '^', '|' ]
218 files = [ "add", "subtract", "multiply", "divide", "modulus", "power", "modexp",
219           "sqrt", "exponent", "log", "arctangent", "sine", "cosine", "bessel" ]
220 funcs = [ "sqrt", "e", "l", "a", "s", "c", "j" ]
221
222 fmts = [ "scale = {}; {} + {}", "scale = {}; {} - {}", "scale = {}; {} * {}",
223          "scale = {}; {} / {}", "scale = {}; {} % {}", "scale = {}; {} ^ {}",
224          "{}k {} {} {}|pR", "scale = {}; sqrt({})", "scale = {}; e({})",
225          "scale = {}; l({})", "scale = {}; a({})", "scale = {}; s({})",
226          "scale = {}; c({})", "scale = {}; j({}, {})" ]
227
228 div = 3
229 mod = 4
230 power = 5
231 modexp = 6
232 sqrt = 7
233 exponent = 8
234 bessel = 13
235
236 gen_ops = []
237 tests = []
238
239 try:
240         i = 0
241         while True:
242                 run_test(i)
243                 i = i + 1
244 except KeyboardInterrupt:
245         pass
246
247 if len(tests) == 0:
248         print("\nNo items in checklist.")
249         print("Exiting")
250         sys.exit(0)
251
252 print("\nGoing through the checklist...\n")
253
254 if len(tests) != len(gen_ops):
255         print("Corrupted checklist!")
256         print("Exiting...")
257         sys.exit(1)
258
259 for i in range(0, len(tests)):
260
261         print("\n{}".format(tests[i]))
262
263         op = int(gen_ops[i])
264
265         if op != modexp:
266                 exe = "bc"
267                 halt = "halt"
268                 options = "-lq"
269         else:
270                 exe = "dc"
271                 halt = "q"
272                 options = ""
273
274         indata = tests[i] + "\n" + halt
275
276         args = [ exe, options ]
277
278         p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
279
280         expected = p.stdout.decode()
281
282         bcexe = exedir + "/" + exe
283         args = [ bcexe, options ]
284
285         p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
286
287         compare(exe, options, p, tests[i], halt, expected, op, False)
288
289         answer = input("\nAdd test ({}/{}) to test suite? [y/N]: ".format(i + 1, len(tests)))
290
291         if 'Y' in answer or 'y' in answer:
292
293                 print("Yes")
294
295                 name = testdir + "/" + exe + "/" + files[op]
296
297                 with open(name + ".txt", "a") as f:
298                         f.write(tests[i] + "\n")
299
300                 with open(name + "_results.txt", "a") as f:
301                         f.write(expected)
302
303         else:
304                 print("No")
305
306 print("Done!")