mirror of
https://github.com/lcn2/calc.git
synced 2025-08-16 01:03:29 +03:00
Some folks might think: “you still use RCS”?!? And we will say, hey, at least we switched from SCCS to RCS back in … I think it was around 1994 ... at least we are keeping up! :-) :-) :-) Logs say that SCCS version 18 became RCS version 19 on 1994 March 18. RCS served us well. But now it is time to move on. And so we are switching to git. Calc releases produce a lot of file changes. In the 125 releases of calc since 1996, when I started managing calc releases, there have been 15473 file mods!
613 lines
13 KiB
Plaintext
613 lines
13 KiB
Plaintext
/*
|
|
* test2600 - 2600 series of the regress.cal test suite
|
|
*
|
|
* Copyright (C) 1999 Ernest Bowen and Landon Curt Noll
|
|
*
|
|
* Primary author: Ernest Bowen
|
|
*
|
|
* Calc is open software; you can redistribute it and/or modify it under
|
|
* the terms of the version 2.1 of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* Calc is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
|
|
* Public License for more details.
|
|
*
|
|
* A copy of version 2.1 of the GNU Lesser General Public License is
|
|
* distributed with calc under the filename COPYING-LGPL. You should have
|
|
* received a copy with calc; if not, write to Free Software Foundation, Inc.
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Under source code control: 1995/10/13 00:13:14
|
|
* File existed as early as: 1995
|
|
*
|
|
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
|
|
*/
|
|
|
|
/*
|
|
* Stringent tests of some of calc's builtin functions.
|
|
* Most of the tests are concerned with the accuracy of the value
|
|
* returned for a function; usually it is expected that
|
|
* remainder (true value - calculated value) will be less in
|
|
* absolute value than "epsilon", where this is either a specified
|
|
* argument eps, or if this is omitted, the current value of epsilon().
|
|
* In some cases the remainder is to have a particular sign, or to
|
|
* have absolute value not exceeding eps/2, or in some cases 3 * eps/4.
|
|
*
|
|
* Typical of these tests is testpower("power", n, b, eps, verbose).
|
|
* Here n is the number of numbers a for which power(a, b, eps) is to
|
|
* be evaluated; the ratio c = (true value - calculated value)/eps
|
|
* is calculated and if this is not less in absolute value than
|
|
* 0.75, a "failure" is recorded and the value of a displayed.
|
|
* On completion of the tests, the minimum and maximum values of
|
|
* c are displayed.
|
|
*
|
|
* The numbers a are usually large "random" integers or sometimes
|
|
* ratios of such integers. In some cases the formulae used to
|
|
* calculate c assume eps is small compared with the value of the
|
|
* function. If eps is very small, say 1e-1000, or if the denominator
|
|
* of b in power(a, b, eps) is large, the computation required for
|
|
* a test may be very heavy.
|
|
*
|
|
* Test funcations are called as:
|
|
*
|
|
* testabc(str, ..., verbose)
|
|
*
|
|
* where str is a string that names the test. This string is printed
|
|
* without a newline (if verbose > 0), near the beginning of the function.
|
|
* The verbose parameter controls how verbose the test will be:
|
|
*
|
|
* 0 - print nothing
|
|
* 1 - print str and the error count
|
|
* 2 - print min and max errors as well
|
|
* 3 - print everything including individual loop counts
|
|
*
|
|
* All functions return the number of errors that they detected.
|
|
*/
|
|
|
|
|
|
global defaultverbose = 1; /* default verbose value */
|
|
global err;
|
|
|
|
define testismult(str, n, verbose)
|
|
{
|
|
local a, b, c, i, m;
|
|
|
|
if (isnull(verbose)) verbose = defaultverbose;
|
|
if (verbose > 0) {
|
|
print str:":",:;
|
|
}
|
|
m = 0;
|
|
for (i = 0; i < n; i++) {
|
|
if (verbose > 2) print i,:;
|
|
a = scale(rand(1,1e1000), rand(100));
|
|
b = scale(rand(1,1e1000), rand(100));
|
|
c = a * b;
|
|
if (!ismult(c,a)) {
|
|
m++;
|
|
if (verbose > 1) {
|
|
printf("*** Failure with:\na = %d\nb = %d\n",
|
|
a,b);
|
|
}
|
|
}
|
|
}
|
|
if (verbose > 0) {
|
|
if (m) {
|
|
printf("*** %d error(s)\n", m);
|
|
} else {
|
|
printf("no errors\n");
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
|
|
define testsqrt(str, n, eps, verbose)
|
|
{
|
|
local a, c, i, x, m, min, max;
|
|
|
|
if (isnull(verbose)) verbose = 2;
|
|
if (verbose > 0) {
|
|
print str:":",:;
|
|
}
|
|
m = 0;
|
|
min = 1000;
|
|
max = -1000;
|
|
if (isnull(eps))
|
|
eps = epsilon();
|
|
for (i = 1; i <= n; i++) {
|
|
if (verbose > 2) print i,:;
|
|
a = scale(rand(1,1000), rand(100));
|
|
x = sqrt(a, eps);
|
|
if (x)
|
|
c = (a/x - x)/2/eps;
|
|
else
|
|
c = a/eps; /* ??? */
|
|
if (c < min)
|
|
min = c;
|
|
if (c > max)
|
|
max = c;
|
|
if (abs(c) > 1) {
|
|
m++;
|
|
if (verbose > 1) {
|
|
printf("*** Failure with:\na = %d\neps = %d\n",
|
|
a,eps);
|
|
}
|
|
}
|
|
}
|
|
if (verbose > 0) {
|
|
if (m) {
|
|
printf("*** %d error(s)\n", m);
|
|
printf(" %s: rem/eps min=%d, max=%d\n",
|
|
str, min, max);
|
|
} else {
|
|
printf("no errors\n");
|
|
}
|
|
}
|
|
if (verbose > 1) {
|
|
printf(" %s: rem/eps min=%0.4d, max=%0.4d\n", str, min, max);
|
|
}
|
|
return m;
|
|
}
|
|
|
|
|
|
define testexp(str, n, eps, verbose)
|
|
{
|
|
local i, a, c, m, min, max;
|
|
|
|
if (isnull(verbose)) verbose = 2;
|
|
if (verbose > 0) {
|
|
print str:":",:;
|
|
}
|
|
if (isnull(eps))
|
|
eps = epsilon();
|
|
min = 1000;
|
|
max = -1000;
|
|
for (i = 1; i <= n; i++) {
|
|
if (verbose > 2) print i,:;
|
|
a = rand(1,1e20)/rand(1,1e20) + rand(50);
|
|
if (rand(1))
|
|
a = -a;
|
|
c = cexp(a, eps);
|
|
if (c < min)
|
|
min = c;
|
|
if (c > max)
|
|
max = c;
|
|
if (abs(c) > 0.02) {
|
|
m++;
|
|
if (verbose > 1) {
|
|
printf("*** Failure with:\na = %d\neps = %d\n",
|
|
a,eps);
|
|
}
|
|
}
|
|
}
|
|
if (verbose > 0) {
|
|
if (m) {
|
|
printf("*** %d error(s)\n", m);
|
|
printf(" %s: rem/eps min=%d, max=%d\n",
|
|
str, min, max);
|
|
} else {
|
|
printf("no errors\n");
|
|
}
|
|
}
|
|
if (verbose > 1) {
|
|
printf(" %s: rem/eps min=%0.4d, max=%0.4d\n", str, min, max);
|
|
}
|
|
return m;
|
|
}
|
|
|
|
|
|
define cexp(x,eps) /* Find relative rem/eps for exp(x, eps) */
|
|
{
|
|
local eps1, v, v1, c;
|
|
|
|
if (isnull(eps))
|
|
eps = epsilon();
|
|
eps1 = eps * 1e-6;
|
|
v = exp(x, eps);
|
|
v1 = exp(x, eps1);
|
|
c = round((v1 - v)/v1/eps, 6, 24);
|
|
return c;
|
|
}
|
|
|
|
|
|
define testln(str, n, eps, verbose)
|
|
{
|
|
local i, a, c, m, min, max;
|
|
|
|
if (isnull(verbose)) verbose = 2;
|
|
if (verbose > 0) {
|
|
print str:":",:;
|
|
}
|
|
if (isnull(eps))
|
|
eps = epsilon();
|
|
min = 1000;
|
|
max = -1000;
|
|
for (i = 1; i <= n; i++) {
|
|
if (verbose > 2) print i,:;
|
|
a = rand(1,1e20)/rand(1,1e20) + rand(50);
|
|
c = cln(a, eps);
|
|
if (c < min)
|
|
min = c;
|
|
if (c > max)
|
|
max = c;
|
|
if (abs(c) > 0.5) {
|
|
m++;
|
|
if (verbose > 1) {
|
|
printf("*** Failure with:\na = %d\neps = %d\n",
|
|
a,eps);
|
|
}
|
|
}
|
|
}
|
|
if (verbose > 0) {
|
|
if (m) {
|
|
printf("*** %d error(s)\n", m);
|
|
printf(" %s: rem/eps min=%d, max=%d\n",
|
|
str, min, max);
|
|
} else {
|
|
printf("no errors\n");
|
|
}
|
|
}
|
|
if (verbose > 1) {
|
|
printf(" %s: rem/eps min=%0.4d, max=%0.4d\n", str, min, max);
|
|
}
|
|
return m;
|
|
}
|
|
|
|
define cln(a, eps)
|
|
{
|
|
local eps1, v, v1, c;
|
|
|
|
if (isnull(eps))
|
|
eps = epsilon();
|
|
eps1 = eps/1e6;
|
|
v = ln(a, eps);
|
|
v1 = ln(a, eps1);
|
|
c = round((v1 - v)/eps, 6, 24);
|
|
return c;
|
|
}
|
|
|
|
|
|
define testpower(str, n, b, eps, verbose)
|
|
{
|
|
local i, a, c, m, min, max;
|
|
|
|
if (isnull(verbose)) verbose = 2;
|
|
if (verbose > 0) {
|
|
print str:":",:;
|
|
}
|
|
if (isnull(eps))
|
|
eps = epsilon();
|
|
if (!isnum(b))
|
|
quit "Second argument (exponent) to be a number";
|
|
min = 1000;
|
|
max = -1000;
|
|
for (i = 1; i <= n; i++) {
|
|
if (verbose > 2) print i,:;
|
|
a = rand(1,1e20)/rand(1,1e20);
|
|
c = cpow(a, b, eps);
|
|
if (abs(c) > .75) {
|
|
m++;
|
|
if (verbose > 1) {
|
|
printf("*** Failure for a = %d\n", a);
|
|
}
|
|
}
|
|
if (c < min)
|
|
min = c;
|
|
if (c > max)
|
|
max = c;
|
|
}
|
|
if (verbose > 0) {
|
|
if (m) {
|
|
printf("*** %d error(s)\n", m);
|
|
printf(" %s: rem/eps min=%d, max=%d\n",
|
|
str, min, max);
|
|
} else {
|
|
printf("no errors\n");
|
|
}
|
|
}
|
|
if (verbose > 1) {
|
|
printf(" %s: rem/eps min=%0.4d, max=%0.4d\n", str, min, max);
|
|
}
|
|
return m;
|
|
}
|
|
|
|
|
|
define testpower2(str, n, eps, verbose)
|
|
{
|
|
local i, a, c, m, min, max;
|
|
local b;
|
|
local num;
|
|
local c2;
|
|
local oldeps;
|
|
|
|
if (isnull(verbose)) verbose = 2;
|
|
if (verbose > 0) {
|
|
print str:":",:;
|
|
}
|
|
if (isnull(eps))
|
|
eps = epsilon();
|
|
oldeps = epsilon(eps);
|
|
epsilon(eps),;
|
|
if (!isnum(b))
|
|
quit "Second argument (exponent) to be a number";
|
|
min = 1000;
|
|
max = -1000;
|
|
for (i = 1; i <= n; i++) {
|
|
if (verbose > 2) print i,:;
|
|
|
|
/* real ^ real */
|
|
a = rand(1,1e20);
|
|
a = a / (int(a/2)+rand(1,1e20));
|
|
b = rand(1,1e20);
|
|
b = b / (int(b/2)+rand(1,1e20));
|
|
c = a ^ b;
|
|
c2 = power(a, b);
|
|
if (c != c2) {
|
|
m++;
|
|
if (verbose > 1) {
|
|
printf("*** real^real failure for a = %d\n", a);
|
|
}
|
|
}
|
|
|
|
/* complex ^ real */
|
|
a = rand(1,1e20);
|
|
a = a / (int(a/2)+rand(1,1e20));
|
|
b = rand(1,1e20);
|
|
b = b / (int(b/2)+rand(1,1e20));
|
|
c = (a*1i) ^ b;
|
|
c2 = power(a*1i, b);
|
|
if (c != c2) {
|
|
m++;
|
|
if (verbose > 1) {
|
|
printf("*** comp^real failure for a = %d\n", a);
|
|
}
|
|
}
|
|
|
|
/* real ^ complex */
|
|
a = rand(1,1e20);
|
|
a = a / (int(a/2)+rand(1,1e20));
|
|
b = rand(1,1e20);
|
|
b = b / (int(b/2)+rand(1,1e20));
|
|
c = a ^ (b*1i);
|
|
c2 = power(a, b*1i);
|
|
if (c != c2) {
|
|
m++;
|
|
if (verbose > 1) {
|
|
printf("*** real^comp failure for a = %d\n", a);
|
|
}
|
|
}
|
|
|
|
/* complex ^ complex */
|
|
a = rand(1,1e20);
|
|
a = a / (int(a/2)+rand(1,1e20));
|
|
b = rand(1,1e20);
|
|
b = b / (int(b/2)+rand(1,1e20));
|
|
c = (a*1i) ^ (b*1i);
|
|
c2 = power(a*1i, b*1i);
|
|
if (c != c2) {
|
|
m++;
|
|
if (verbose > 1) {
|
|
printf("*** comp^comp failure for a = %d\n", a);
|
|
}
|
|
}
|
|
}
|
|
epsilon(oldeps),;
|
|
if (verbose > 0) {
|
|
if (m) {
|
|
printf("*** %d error(s)\n", m);
|
|
printf(" %s: rem/eps min=%d, max=%d\n",
|
|
str, min, max);
|
|
} else {
|
|
printf("no errors\n");
|
|
}
|
|
}
|
|
if (verbose > 1) {
|
|
printf(" %s: rem/eps min=%0.4d, max=%0.4d\n", str, min, max);
|
|
}
|
|
return m;
|
|
}
|
|
|
|
|
|
define cpow(a, b, eps) /* Find rem/eps for power(a,b,eps) */
|
|
{
|
|
local v, v1, c, n, d, h;
|
|
|
|
if (isnull(eps))
|
|
eps = epsilon();
|
|
n = num(b);
|
|
d = den(b);
|
|
|
|
v = power(a, b, eps);
|
|
h = (a^n/v^d - 1) * v/d;
|
|
c = round(h/eps, 6, 24);
|
|
return c;
|
|
}
|
|
|
|
define testgcd(str, n, verbose)
|
|
{
|
|
local i, a, b, g, m;
|
|
|
|
if (isnull(verbose)) verbose = 2;
|
|
if (verbose > 0) {
|
|
print str:":",:;
|
|
}
|
|
m = 0;
|
|
for (i = 1; i <= n; i++) {
|
|
if (verbose > 2) print i,:;
|
|
a = rand(1,1e1000);
|
|
b = rand(1,1e1000);
|
|
g = gcd(a,b);
|
|
if (!ismult(a,g) || !ismult(b,g) || !g || !isrel(a/g, b/g)) {
|
|
m++;
|
|
printf("*** Failure for a = %d, b = %d\n", a, b);
|
|
}
|
|
}
|
|
if (verbose > 0) {
|
|
if (m) {
|
|
printf("*** %d error(s)\n", m);
|
|
} else {
|
|
printf("no errors\n");
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
|
|
define mkreal() = scale(rand(-1000,1001)/rand(1,1000), rand(-100, 101));
|
|
|
|
define mkcomplex() = mkreal() + 1i * mkreal();
|
|
|
|
define mkbigreal()
|
|
{
|
|
local x;
|
|
|
|
x = rand(100, 1000)/rand(1,10);
|
|
if (rand(2))
|
|
x = -x;
|
|
return x;
|
|
}
|
|
|
|
define mksmallreal() = rand(-10, 11)/rand(100,1000);
|
|
|
|
define testappr(str, n, verbose)
|
|
{
|
|
local x, y, z, m, i, p;
|
|
|
|
if (isnull(verbose))
|
|
verbose = defaultverbose;
|
|
if (verbose > 0) {
|
|
print str:":",:;
|
|
}
|
|
m = 0;
|
|
for (i = 1; i <= n; i++) {
|
|
x = rand(3) ? mkreal(): mkcomplex();
|
|
y = mkreal();
|
|
if (verbose > 2)
|
|
printf(" %d: x = %d, y = %d\n", i, x, y);
|
|
|
|
for (z = 0; z < 32; z++) {
|
|
p = checkappr(x,y,z,verbose);
|
|
if (p) {
|
|
printf("*** Failure for x=%d, y=%d, z=%d\n",
|
|
x, y, z);
|
|
m++;
|
|
}
|
|
}
|
|
}
|
|
if (verbose > 0) {
|
|
if (m) {
|
|
printf("*** %d error(s)\n", m);
|
|
} else {
|
|
printf("no errors\n");
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
|
|
|
|
define checkappr(x,y,z,verbose) /* Returns 1 if an error is detected */
|
|
{
|
|
local a;
|
|
|
|
a = appr(x,y,z);
|
|
if (verbose > 1)
|
|
printf("\ta = %d\n", a);
|
|
if (isreal(x))
|
|
return checkresult(x,y,z,a);
|
|
if (isnum(x))
|
|
return checkresult(re(x), y, z, re(a))
|
|
| checkresult(im(x), y, z, im(a));
|
|
|
|
quit "Bad first argument for checkappr()";
|
|
}
|
|
|
|
define checkresult(x,y,z,a) /* tests correctness of a = appr(x,y,z) */
|
|
{
|
|
local r, n, s, v;
|
|
|
|
if (y == 0)
|
|
return (a != x);
|
|
r = x - a;
|
|
n = a/y;
|
|
|
|
if (!isint(n))
|
|
return 1;
|
|
if (abs(r) >= abs(y))
|
|
return 1;
|
|
if (r == 0)
|
|
return 0;
|
|
if (z & 16) {
|
|
if (abs(r) > abs(y)/2)
|
|
return 1;
|
|
if (abs(r) < abs(y)/2)
|
|
return 0;
|
|
z &= 15;
|
|
}
|
|
s = sgn(r);
|
|
switch (z) {
|
|
case 0: v = (s == sgn(y)); break;
|
|
case 1: v = (s == -sgn(y)); break;
|
|
case 2: v = (s == sgn(x)); break;
|
|
case 3: v = (s == -sgn(x)); break;
|
|
case 4: v = (s > 0); break;
|
|
case 5: v = (s < 0); break;
|
|
case 6: v = (s == sgn(x/y)); break;
|
|
case 7: v = (s == -sgn(x/y)); break;
|
|
case 8: v = iseven(n); break;
|
|
case 9: v = isodd(n); break;
|
|
case 10: v = (x/y > 0) ? iseven(n) : isodd(n); break;
|
|
case 11: v = (x/y > 0) ? isodd(n) : iseven(n); break;
|
|
case 12: v = (y > 0) ? iseven(n) : isodd(n); break;
|
|
case 13: v = (y > 0) ? isodd(n) : iseven(n); break;
|
|
case 14: v = (x > 0) ? iseven(n) : isodd(n); break;
|
|
case 15: v = (x > 0) ? isodd(n) : iseven(n); break;
|
|
}
|
|
return !v;
|
|
}
|
|
|
|
/*
|
|
* test2600 - perform all of the above tests a bunch of times
|
|
*/
|
|
define test2600(verbose, tnum)
|
|
{
|
|
local n; /* test parameter */
|
|
local ep; /* test parameter */
|
|
local i;
|
|
|
|
/* set test parameters */
|
|
n = 5; /* internal test loop count */
|
|
if (isnull(verbose)) {
|
|
verbose = defaultverbose;
|
|
}
|
|
if (isnull(tnum)) {
|
|
tnum = 1; /* initial test number */
|
|
}
|
|
|
|
/*
|
|
* test a lot of stuff
|
|
*/
|
|
srand(2600e2600);
|
|
ep = 1e-250;
|
|
err += testismult(strcat(str(tnum++), ": mult"), n*20, verbose);
|
|
err += testappr(strcat(str(tnum++), ": appr"), n*40, verbose);
|
|
err += testexp(strcat(str(tnum++),": exp"), n, ep, verbose);
|
|
err += testln(strcat(str(tnum++),": ln"), n, ep, verbose);
|
|
err += testpower(strcat(str(tnum++),": power"), n,
|
|
rand(2,10), ep, verbose);
|
|
err += testgcd(strcat(str(tnum++),": gcd"), n, ep, verbose);
|
|
for (i=0; i < 32; ++i) {
|
|
config("sqrt", i);
|
|
err += testsqrt(strcat(str(tnum++),": sqrt",str(i)), n*10,
|
|
ep, verbose);
|
|
}
|
|
err += testpower2(strcat(str(tnum++),": power"), n*4, ep, verbose);
|
|
if (verbose > 1) {
|
|
if (err) {
|
|
print "***", err, "error(s) found in test2600";
|
|
} else {
|
|
print "no errors in test2600";
|
|
}
|
|
}
|
|
return tnum;
|
|
}
|