/*
 * test4000.ptest - test ptest, nextcand, prevcand for test 40dd
 *
 * Copyright (C) 1999,2023  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:   1996/03/13 02:38:45
 * File existed as early as:    1996
 *
 * Share and enjoy!  :-)        http://www.isthe.com/chongo/tech/comp/calc/
 */

/*
 * Functions for testing and timing ptest, nextcand, prevcand.
 *
 * rlen(N) for N > 0 generates a random N-word positive integer.
 *
 * plen(N) for N > 0 generates an almost certainly prime positive
 *      integer whose word-count is about N.
 *
 * clen(N) for N > 0 generates a composite odd N-word integer.
 *
 * ptimes(str, N [, n [, count [, skip, [, verbose]]]])
 *      tests, and finds the runtime, for
 *      ptest(x, count, skip) for n random almost certainly prime integers x
 *      with word-count about N; n defaults to ceil(K1/abs(count)/(H1 + N^3)),
 *      count to COUNT, skip to SKIP.
 *
 * ctimes(str, N [, n [, count [, skip, [, verbose]]]])
 *      tests, and finds the runtime, for
 *      ptest(x, count, skip) for n random composite integers x with word-count
 *      about N; n defaults to ceil(K2/(H2 + N^3)), count to COUNT, skip
 *      to SKIP.
 *
 * crtimes(str,a,b,n, [,count [, skip, [, verbose]]])
 *      tests, and finds the runtime,
 *      for ptest(x, count, skip) for n random integers x between a and b;
 *      count defaults to COUNT, skip to SKIP.
 *
 * ntimes (str, N [,n, [, count [, skip [, residue [, modulus[,verb]]]]]]) tests
 *      and finds the runtime for nextcand(...) and prevcand (...) for
 *      n integers x with word-count about N, etc. n defaults to
 *      ceil(K3/(H3 + N^3));
 *
 * testnextcand(str, N [, n [, count [, skip [, residue [, modulus [, verb]]]]])
 *      performs tests of nextcand(x, count, skip, residue, modulus)
 *      for n values of x with word-count N; n defaults to
 *      ceil(K3/(H3 + N^3)), count to COUNT, skip to SKIP, residue to 0,
 *      modulus to 1.
 *
 * testprevcand(str, N [, n [, count [, skip [, residue [, modulus [, verb]]]]])
 *      performs tests of prevcand(x, count, skip, residue, modulus)
 *      for n values of x with word-count N; n defaults to
 *      ceil(K3/(H3 + N^3)), count to COUNT, skip to SKIP, residue to 0,
 *      modulus to 1.
 */


defaultverbose = 1;     /* default verbose value */

/*
 * test defaults
 */
global BASEB = 32;
global BASE = 2^BASEB;
global COUNT = 5;
global SKIP = 0;
global RESIDUE = 0;
global MODULUS = 1;

/*
 * internal test constants
 */
global K1 = 2^15;
global H1 = 40;
global K2 = 2^17;
global H2 = 40;
global K3 = 2^10;
global H3 = 10;


define rlen(N)
{

        if (!isint(N) || N <= 0)
                quit "Bad argument for rlen";
        return rand(BASE^(N-1), BASE^N);
}

define plen(N) = nextcand(rlen(N), 10, 0);

define clen(N)
{
        local n, v;

        do {
                v = rlen(N);
                if (iseven(v))
                        v++;
        }
        while
                (ptest(v, 10, 0));
        return v;
}

define ptimes(str, N, n, count, skip, verbose)
{
        local A, i, t, p, m;

        if (isnull(verbose))
                verbose = defaultverbose;
        if (verbose > 0) {
                print str:":",:;
        }
        m = 0;
        if (isnull(count))
                count = COUNT;
        if (isnull(n)) {
                n = ceil(K1/abs(count)/(H1 + N^3));
                if (verbose > 1) {
                        print "n =",n;
                }
        }
        if (isnull(skip))
                skip = SKIP;
        mat A[n];
        for (i = 0; i < n; i++)
                A[i] = plen(N);
        t = usertime();
        for (i = 0; i < n; i++) {
                p = ptest(A[i], count, skip);
                if (!p) {
                        if (verbose > 0) {
                                printf("*** Error for x = %d\n", A[i]);
                                m++;
                        }
                }
        }
        if (verbose > 0) {
                if (m) {
                        printf("*** %d error(s)\n", m);
                } else {
                        t = round(usertime() - t, 4);
                        if (verbose > 1) {
                                printf("%d probable primes: time = %d\n", n, t);
                        } else {
                                printf("%d probable primes: passed\n", n);
                        }
                }
        }
        return m;
}

define ctimes(str, N, n, count, skip, verbose)
{
        local A, i, r, t, p, m;


        if (isnull(verbose))
                verbose = defaultverbose;
        if (verbose > 0) {
                print str:":",:;
        }
        m = 0;
        if (isnull(count))
                count = COUNT;
        if (isnull(n)) {
                n = ceil(K2/(H2 + N^3));
                if (verbose > 1) {
                        print "n =",n;
                }
        }
        if (isnull(skip))
                skip = SKIP;
        mat A[n];
        for (i = 0; i < n; i++)
                A[i] = clen(N);
        t = usertime();
        for (i = 0; i < n; i++) {
                p = ptest(A[i], count, skip);
                if (p) {
                        if (verbose > 0) {
                                printf("*** Error, what should be rare "
                                       "has occurred for x = %d \n", A[i]);
                                m++;
                        }
                }
        }
        if (verbose > 0) {
                if (m) {
                        printf("*** %d error(s)\n", m);
                } else {
                        t = round(usertime() - t, 4);
                        if (verbose > 1) {
                                printf("%d probable primes: time = %d\n", n, t);
                        } else {
                                printf("%d probable primes: passed\n", n);
                        }
                }
        }
        return m;
}

define crtimes(str, a, b, n, count, skip, verbose)
{
        local A, P, i, t, p, m;

        if (isnull(verbose))
                verbose = defaultverbose;
        if (verbose > 0) {
                print str:":",:;
        }
        m = 0;
        if (b < a)
                swap(a,b);
        b++;
        if (isnull(count))
                count = COUNT;
        if (isnull(skip))
                skip = SKIP;
        mat A[n];
        mat P[n];
        for (i = 0; i < n; i++) {
                A[i] = rand(a,b);
                P[i] = ptest(A[i], 20, 0);
        }
        t = usertime();
        for (i = 0; i < n; i++) {
                p = ptest(A[i], count, skip);
                if (p != P[i]) {
                        if (verbose > 0) {
                                printf("*** Apparent error for %s x = %d\n",
                                        P[i] ? "prime" : "composite", A[i]);
                                ++m;
                        }
                }
        }
        if (verbose > 0) {
                if (m) {
                        printf("*** %d error(s)?\n", m);
                } else {
                        t = round(usertime() - t, 4);
                        if (verbose > 1) {
                                printf("%d probable primes: time = %d\n", n, t);
                        } else {
                                printf("%d probable primes: passed\n", n);
                        }
                }
        }
        return m;
}

define ntimes(str, N, n, count, skip, residue, modulus, verbose)
{
        local A, i, t, p, tnext, tprev;

        if (isnull(verbose))
                verbose = defaultverbose;
        if (verbose > 0) {
                print str:":",:;
        }
        if (isnull(count))
                count = COUNT;
        if (isnull(n)) {
                n = ceil(K3/(H3 + N^3));
                if (verbose > 1) {
                        print "n =",n;
                }
        }
        if (isnull(skip))
                skip = SKIP;
        if (isnull(residue))
                residue = RESIDUE;
        if (isnull(modulus))
                modulus = MODULUS;

        mat A[n];
        for (i = 0; i < n; i++)
                A[i] = rlen(N);
        t = usertime();
        for (i = 0; i < n; i++) {
                p = nextcand(A[i], count, skip, residue, modulus);
        }
        tnext = round(usertime() - t, 4);
        t = usertime();
        for (i = 0; i < n; i++) {
                p = prevcand(A[i], count, skip, residue, modulus);
        }
        tprev = round(usertime() - t, 4);
        if (verbose > 0) {
                printf("%d evaluations, nextcand: %d, "
                       "prevcand: %d\n", n, tnext, tprev);
        }
}

define testnextcand(str, N, n, count, skip, residue, modulus, verbose)
{
        local p, x, y, i, m;

        if (isnull(verbose))
                verbose = defaultverbose;
        if (verbose > 0) {
                print str:":",:;
        }
        m = 0;
        if (isnull(count))
                count = COUNT;
        if (isnull(n)) {
                n = ceil(K3/(H3 + N^3));
                print "n =",n;
        }
        if (isnull(skip))
                skip = SKIP;
        if (isnull(residue))
                residue = RESIDUE;
        if (isnull(modulus))
                modulus = MODULUS;
        for (i = 0; i < n; i++) {
                x = rlen(N);
                y = nextcand(x, count, skip, residue, modulus);
                p = testnext1(x, y, count, skip, residue, modulus);
                if (p) {
                        m++;
                        if (verbose > 1) {
                                printf("*** Failure %d for x = %d\n", p, x);
                        }
                }
        }
        if (verbose > 0) {
                if (m) {
                        printf("*** %d error(s)?\n", m);
                } else {
                        printf("%d successful tests\n", n);
                }
        }
        return m;
}

define testnext1(x, y, count, skip, residue, modulus)
{
        if (y <= x)
                return 1;
        if (!ptest(y, count, skip))
                return 2;
        if (mne(y, residue, modulus))
                return 3;
        return 0;
}

define testprevcand(str, N, n, count, skip, residue, modulus, verbose)
{
        local p, x, y, i, m;

        if (isnull(verbose))
                verbose = defaultverbose;
        if (verbose > 0) {
                print str:":",:;
        }
        m = 0;
        if (isnull(count))
                count = COUNT;
        if (isnull(n)) {
                n = ceil(K3/(H3 + N^3));
                print "n =",n;
        }
        if (isnull(skip))
                skip = SKIP;
        if (isnull(residue))
                residue = RESIDUE;
        if (isnull(modulus))
                modulus = MODULUS;
        for (i = 0; i < n; i++) {
                x = rlen(N);
                y = prevcand(x, count, skip, residue, modulus);
                p = testprev1(x, y, count, skip, residue, modulus);
                if (p) {
                        m++;
                        if (verbose > 1) {
                                printf("*** Failure %d for x = %d\n", p, x);
                        }
                }
        }
        if (verbose > 0) {
                if (m) {
                        printf("*** %d error(s)?\n", m);
                } else {
                        printf("%d successful tests\n", n);
                }
        }
        return m;
}


define testprev1(x, y, count, skip, residue, modulus)
{
        if (y >= x)
                return 1;
        if (!ptest(y, count, skip))
                return 2;
        if (mne(y, residue, modulus))
                return 3;
        return 0;
}

/*
 * test4000 - perform all of the above tests a bunch of times
 */
define test4000(v, tnum)
{
        local n;        /* test parameter */

        /*
         * set test parameters
         */
        srand(4000e4000);

        /*
         * test a lot of stuff
         */
        err += ptimes(strcat(str(tnum++),": ptimes(1,250)"), 1, 250,,,v);
        err += ptimes(strcat(str(tnum++),": ptimes(3,50)"), 3, 50,,,v);
        err += ptimes(strcat(str(tnum++),": ptimes(5,20)"), 5, 20,,,v);

        err += ctimes(strcat(str(tnum++),": ctimes(1,7500)"), 1, 7500,,,v);
        err += ctimes(strcat(str(tnum++),": ctimes(3,500)"), 3, 500,,,v);
        err += ctimes(strcat(str(tnum++),": ctimes(5,200)"), 5, 200,,,v);

        err += crtimes(strcat(str(tnum++),": crtimes(2^30,2^31,2500)"),
                2^30, 2^31, 2500,,,v);
        err += crtimes(strcat(str(tnum++),": crtimes(2^300,2^301,75)"),
                2^300, 2^301, 75,,,v);

        err += testprevcand(strcat(str(tnum++),": testprevcand(1,250)"),
                1, 250, ,,,,v);
        err += testprevcand(strcat(str(tnum++),": testprevcand(3,25)"),
                3, 25, ,,,,v);
        err += testprevcand(strcat(str(tnum++),": testprevcand(5,10)"),
                5, 10, ,,,,v);

        err += testnextcand(strcat(str(tnum++),": testnextcand(1,250)"),
                1, 250, ,,,,v);
        err += testnextcand(strcat(str(tnum++),": testnextcand(3,25)"),
                3, 25, ,,,,v);
        err += testnextcand(strcat(str(tnum++),": testnextcand(5,10)"),
                5, 10, ,,,,v);

        /*
         * report results
         */
        if (v > 1) {
                if (err) {
                        print "***", err, "error(s) found in testall";
                } else {
                        print "no errors in testall";
                }
        }
        return tnum;
}
