/*
 * zmod - modulo arithmetic routines
 *
 * Copyright (C) 1999-2007,2021-2023,2026  David I. Bell, Landon Curt Noll and Ernest Bowen
 *
 * Primary author:  David I. Bell
 *
 * 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:   1991/05/22 23:03:55
 * File existed as early as:    1991
 *
 * Share and enjoy!  :-)        http://www.isthe.com/chongo/tech/comp/calc/
 */

/*
 * Routines to do modulo arithmetic both normally and also using the REDC
 * algorithm given by Peter L. Montgomery in Mathematics of Computation,
 * volume 44, number 170 (April, 1985).  For multiple multiplies using
 * the same large modulus, the REDC algorithm avoids the usual division
 * by the modulus, instead replacing it with two multiplies or else a
 * special algorithm.  When these two multiplies or the special algorithm
 * are faster then the division, then the REDC algorithm is better.
 */

/*
 * important <system> header includes
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>

/*
 * calc local src includes
 */
#include "zmath.h"
#include "qmath.h"
#include "config.h"
#include "attribute.h"
#include "errtbl.h"

#include "banned.h" /* include after all other includes */

#define POWBITS 4              /* bits for power chunks (must divide BASEB) */
#define POWNUMS (1 << POWBITS) /* number of powers needed in table */

static void zmod5(ZVALUE *zp);
static void zmod6(ZVALUE z1, ZVALUE *res);
static void zredcmodinv(ZVALUE z1, ZVALUE *res);

static REDC *powermodredc = NULL; /* REDC info for raising to power */

bool havelastmod = false;
static ZVALUE lastmod[1];
static ZVALUE lastmodinv[1];

/*
 * Square a number and then mod the result with a second number.
 * The number to be squared can be negative or out of modulo range.
 * The result will be in the range 0 to the modulus - 1.
 *
 * given:
 *      z1              number to be squared
 *      z2              number to take mod with
 *      res             result
 */
void
zsquaremod(ZVALUE z1, ZVALUE z2, ZVALUE *res)
{
    ZVALUE tmp;
    FULL prod;
    FULL digit;

    /* firewall */
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    if (ziszero(z2) || zisneg(z2)) {
        math_error("Mod of non-positive integer");
        not_reached();
    }
    if (ziszero(z1) || zisunit(z2)) {
        *res = _zero_;
        return;
    }

    /*
     * If the modulus is a single digit number, then do the result
     * cheaply.  Check especially for a small power of two.
     */
    if (zistiny(z2)) {
        digit = z2.v[0];
        if ((digit & -digit) == digit) { /* NEEDS 2'S COMP */
            prod = (FULL)z1.v[0];
            prod = (prod * prod) & (digit - 1);
        } else {
            z1.sign = 0;
            prod = (FULL)zmodi(z1, (long)digit);
            prod = (prod * prod) % digit;
        }
        itoz((long)prod, res);
        return;
    }

    /*
     * The modulus is more than one digit.
     * Actually do the square and divide if necessary.
     */
    zsquare(z1, &tmp);
    if ((tmp.len < z2.len) || ((tmp.len == z2.len) && (tmp.v[tmp.len - 1] < z2.v[z2.len - 1]))) {
        *res = tmp;
        return;
    }
    zmod(tmp, z2, res, 0);
    zfree(tmp);
}

/*
 * Calculate the number congruent to the given number whose absolute
 * value is minimal.  The number to be reduced can be negative or out of
 * modulo range.  The result will be within the range -int((modulus-1)/2)
 * to int(modulus/2) inclusive.  For example, for modulus 7, numbers are
 * reduced to the range [-3, 3], and for modulus 8, numbers are reduced to
 * the range [-3, 4].
 *
 * given:
 *      z1              number to find minimum congruence of
 *      z2              number to take mod with
 *      res             result
 */
void
zminmod(ZVALUE z1, ZVALUE z2, ZVALUE *res)
{
    ZVALUE tmp1, tmp2;
    int sign;
    int cv;

    /* firewall */
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    if (ziszero(z2) || zisneg(z2)) {
        math_error("Mod of non-positive integer");
        not_reached();
    }
    if (ziszero(z1) || zisunit(z2)) {
        *res = _zero_;
        return;
    }
    if (zistwo(z2)) {
        if (zisodd(z1)) {
            *res = _one_;
        } else {
            *res = _zero_;
        }
        return;
    }

    /*
     * Do a quick check to see if the number is very small compared
     * to the modulus.  If so, then the result is obvious.
     */
    if (z1.len < z2.len - 1) {
        zcopy(z1, res);
        return;
    }

    /*
     * Now make sure the input number is within the modulo range.
     * If not, then reduce it to be within range and make the
     * quick check again.
     */
    sign = z1.sign;
    z1.sign = 0;
    cv = zrel(z1, z2);
    if (cv == 0) {
        *res = _zero_;
        return;
    }
    tmp1 = z1;
    if (cv > 0) {
        z1.sign = (bool)sign;
        zmod(z1, z2, &tmp1, 0);
        if (tmp1.len < z2.len - 1) {
            *res = tmp1;
            return;
        }
        sign = 0;
    }

    /*
     * Now calculate the difference of the modulus and the absolute
     * value of the original number.  Compare the original number with
     * the difference, and return the one with the smallest absolute
     * value, with the correct sign.  If the two values are equal, then
     * return the positive result.
     */
    zsub(z2, tmp1, &tmp2);
    cv = zrel(tmp1, tmp2);
    if (cv < 0) {
        zfree(tmp2);
        tmp1.sign = (bool)sign;
        if (tmp1.v == z1.v) {
            zcopy(tmp1, res);
        } else {
            *res = tmp1;
        }
    } else {
        if (cv) {
            tmp2.sign = !sign;
        }
        if (tmp1.v != z1.v) {
            zfree(tmp1);
        }
        *res = tmp2;
    }
}

/*
 * Compare two numbers for equality modulo a third number.
 * The two numbers to be compared can be negative or out of modulo range.
 * Returns true if the numbers are not congruent, and false if they are
 * congruent.
 *
 * given:
 *      z1              first number to be compared
 *      z2              second number to be compared
 *      z3              modulus
 */
bool
zcmpmod(ZVALUE z1, ZVALUE z2, ZVALUE z3)
{
    ZVALUE tmp1, tmp2, tmp3;
    FULL digit;
    LEN len;
    int cv;

    if (zisneg(z3) || ziszero(z3)) {
        math_error("Non-positive modulus in zcmpmod");
        not_reached();
    }
    if (zistwo(z3)) {
        return (((z1.v[0] + z2.v[0]) & 0x1) != 0);
    }

    /*
     * If the two numbers are equal, then their mods are equal.
     */
    if ((z1.sign == z2.sign) && (z1.len == z2.len) && (z1.v[0] == z2.v[0]) && (zcmp(z1, z2) == 0)) {
        return false;
    }

    /*
     * If both numbers are negative, then we can make them positive.
     */
    if (zisneg(z1) && zisneg(z2)) {
        z1.sign = 0;
        z2.sign = 0;
    }

    /*
     * For small negative numbers, make them positive before comparing.
     * In any case, the resulting numbers are in tmp1 and tmp2.
     */
    tmp1 = z1;
    tmp2 = z2;
    len = z3.len;
    digit = z3.v[len - 1];

    if (zisneg(z1) && ((z1.len < len) || ((z1.len == len) && (z1.v[z1.len - 1] < digit)))) {
        zadd(z1, z3, &tmp1);
    }

    if (zisneg(z2) && ((z2.len < len) || ((z2.len == len) && (z2.v[z2.len - 1] < digit)))) {
        zadd(z2, z3, &tmp2);
    }

    /*
     * Now compare the two numbers for equality.
     * If they are equal we are all done.
     */
    if (zcmp(tmp1, tmp2) == 0) {
        if (tmp1.v != z1.v) {
            zfree(tmp1);
        }
        if (tmp2.v != z2.v) {
            zfree(tmp2);
        }
        return false;
    }

    /*
     * They are not identical.  Now if both numbers are positive
     * and less than the modulus, then they are definitely not equal.
     */
    if ((tmp1.sign == tmp2.sign) && ((tmp1.len < len) || (zrel(tmp1, z3) < 0)) && ((tmp2.len < len) || (zrel(tmp2, z3) < 0))) {
        if (tmp1.v != z1.v) {
            zfree(tmp1);
        }
        if (tmp2.v != z2.v) {
            zfree(tmp2);
        }
        return true;
    }

    /*
     * Either one of the numbers is negative or is large.
     * So do the standard thing and subtract the two numbers.
     * Then they are equal if the result is 0 (mod z3).
     */
    zsub(tmp1, tmp2, &tmp3);
    if (tmp1.v != z1.v) {
        zfree(tmp1);
    }
    if (tmp2.v != z2.v) {
        zfree(tmp2);
    }

    /*
     * Compare the result with the modulus to see if it is equal to
     * or less than the modulus.  If so, we know the mod result.
     */
    tmp3.sign = 0;
    cv = zrel(tmp3, z3);
    if (cv == 0) {
        zfree(tmp3);
        return false;
    }
    if (cv < 0) {
        zfree(tmp3);
        return true;
    }

    /*
     * We are forced to actually do the division.
     * The numbers are congruent if the result is zero.
     */
    zmod(tmp3, z3, &tmp1, 0);
    zfree(tmp3);
    if (ziszero(tmp1)) {
        zfree(tmp1);
        return false;
    } else {
        zfree(tmp1);
        return true;
    }
}

/*
 * Given the address of a positive integer whose word count does not
 * exceed twice that of the modulus stored at lastmod, to evaluate and store
 * at that address the value of the integer modulo the modulus.
 */
static void
zmod5(ZVALUE *zp)
{
    LEN len, modlen, j;
    ZVALUE tmp1, tmp2;
    ZVALUE z1, z2, z3;
    HALF *a, *b;
    FULL f;
    HALF u;

    /* firewall */
    if (zp == NULL) {
        math_error("%s: zp NULL", __func__);
        not_reached();
    }

    int subcount = 0;

    if (zrel(*zp, *lastmod) < 0) {
        return;
    }
    modlen = lastmod->len;
    len = zp->len;
    z1.v = zp->v + modlen - 1;
    z1.len = len - modlen + 1;
    z1.sign = z2.sign = z3.sign = 0;
    if (z1.len > modlen + 1) {
        math_error("Bad call to zmod5!!!");
        not_reached();
    }
    z2.v = lastmodinv->v + modlen + 1 - z1.len;
    z2.len = lastmodinv->len - modlen - 1 + z1.len;
    zmul(z1, z2, &tmp1);
    z3.v = tmp1.v + z1.len;
    z3.len = tmp1.len - z1.len;
    if (z3.len > 0) {
        zmul(z3, *lastmod, &tmp2);
        j = modlen;
        a = zp->v;
        b = tmp2.v;
        u = 0;
        len = modlen;
        while (j-- > 0) {
            f = (FULL)*a - (FULL)*b++ - (FULL)u;
            *a++ = (HALF)f;
            u = -(HALF)(f >> BASEB);
        }
        if (z1.len > 1) {
            len++;
            if (tmp2.len > modlen) {
                f = (FULL)*a - (FULL)*b - (FULL)u;
            } else {
                f = (FULL)*a - (FULL)u;
            }
            *a++ = (HALF)f;
        }
        while (len > 0 && *--a == 0) {
            len--;
        }
        zp->len = len;
        zfree(tmp2);
    }
    zfree(tmp1);
    while (len > 0 && zrel(*zp, *lastmod) >= 0) {
        subcount++;
        if (subcount > 2) {
            math_error("Too many subtractions in zmod5");
            not_reached();
        }
        j = modlen;
        a = zp->v;
        b = lastmod->v;
        u = 0;
        while (j-- > 0) {
            f = (FULL)*a - (FULL)*b++ - (FULL)u;
            *a++ = (HALF)f;
            u = -(HALF)(f >> BASEB);
        }
        if (len > modlen) {
            f = (FULL)*a - (FULL)u;
            *a++ = (HALF)f;
        }
        while (len > 0 && *--a == 0) {
            len--;
        }
        zp->len = len;
    }
    if (len == 0) {
        zp->len = 1;
    }
}

static void
zmod6(ZVALUE z1, ZVALUE *res)
{
    LEN len, modlen, len0;
    int sign;
    ZVALUE zp0, ztmp;

    /* firewall */
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    if (ziszero(z1) || zisone(*lastmod)) {
        *res = _zero_;
        return;
    }
    sign = z1.sign;
    z1.sign = 0;
    zcopy(z1, &ztmp);
    modlen = lastmod->len;
    zp0.sign = 0;
    while (zrel(ztmp, *lastmod) >= 0) {
        len = ztmp.len;
        zp0.len = len;
        len0 = 0;
        if (len > 2 * modlen) {
            zp0.len = 2 * modlen;
            len0 = len - 2 * modlen;
        }
        zp0.v = ztmp.v + len - zp0.len;
        zmod5(&zp0);
        len = len0 + zp0.len;
        while (len > 0 && ztmp.v[len - 1] == 0) {
            len--;
        }
        if (len == 0) {
            zfree(ztmp);
            *res = _zero_;
            return;
        }
        ztmp.len = len;
    }
    if (sign) {
        zsub(*lastmod, ztmp, res);
    } else {
        zcopy(ztmp, res);
    }
    zfree(ztmp);
}

/*
 * Compute the result of raising one number to a power modulo another number.
 * That is, this computes:  a^b (modulo c).
 * This calculates the result by examining the power POWBITS bits at a time,
 * using a small table of POWNUMS low powers to calculate powers for those bits,
 * and repeated squaring and multiplying by the partial powers to generate
 * the complete power.  If the power being raised to is high enough, then
 * this uses the REDC algorithm to avoid doing many divisions.  When using
 * REDC, multiple calls to this routine using the same modulus will be
 * slightly faster.
 */
void
zpowermod(ZVALUE z1, ZVALUE z2, ZVALUE z3, ZVALUE *res)
{
    HALF *hp;                  /* pointer to current word of the power */
    REDC *rp;                  /* REDC information to be used */
    ZVALUE *pp;                /* pointer to low power table */
    ZVALUE ans, temp;          /* calculation values */
    ZVALUE modpow;             /* current small power */
    ZVALUE lowpowers[POWNUMS]; /* low powers */
    ZVALUE ztmp;
    int curshift;        /* shift value for word of power */
    HALF curhalf;        /* current word of power */
    unsigned int curpow; /* current low power */
    unsigned int curbit; /* current bit of low power */
    bool free_z1;        /* true => need to free z1 */
    int i;

    /* firewall */
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    if (zisneg(z3) || ziszero(z3)) {
        math_error("Non-positive modulus in zpowermod");
        not_reached();
    }
    if (zisneg(z2)) {
        math_error("Negative power in zpowermod");
        not_reached();
    }

    /*
     * Check easy cases first.
     */
    if ((ziszero(z1) && !ziszero(z2)) || zisunit(z3)) {
        /* 0^(non_zero) or x^y mod 1 always produces zero */
        *res = _zero_;
        return;
    }
    if (ziszero(z2)) { /* x^0 == 1 */
        *res = _one_;
        return;
    }
    if (zistwo(z3)) { /* mod 2 */
        if (zisodd(z1)) {
            *res = _one_;
        } else {
            *res = _zero_;
        }
        return;
    }
    if (zisunit(z1) && (!z1.sign || ziseven(z2))) {
        /* 1^x or (-1)^(2x) */
        *res = _one_;
        return;
    }

    /*
     * Normalize the number being raised to be non-negative and to lie
     * within the modulo range.  Then check for zero or one specially.
     */
    ztmp.len = 0;
    free_z1 = false;
    if (zisneg(z1) || zrel(z1, z3) >= 0) {
        zmod(z1, z3, &ztmp, 0);
        zfree(z1);
        z1 = ztmp;
        free_z1 = true;
    }
    if (ziszero(z1)) {
        zfree(z1);
        if (ztmp.len) {
            zfree(ztmp);
        }
        *res = _zero_;
        return;
    }
    if (zisone(z1)) {
        zfree(z1);
        if (ztmp.len) {
            zfree(ztmp);
        }
        *res = _one_;
        return;
    }

    /*
     * If modulus is large enough use zmod5
     */
    if (z3.len >= conf->pow2) {
        if (havelastmod && zcmp(z3, *lastmod)) {
            zfree(*lastmod);
            zfree(*lastmodinv);
            havelastmod = false;
        }
        if (!havelastmod) {
            zcopy(z3, lastmod);
            zbitvalue(2 * z3.len * BASEB, &temp);
            zquo(temp, z3, lastmodinv, 0);
            zfree(temp);
            havelastmod = true;
        }

        /* zzz */
        for (pp = &lowpowers[2]; pp <= &lowpowers[POWNUMS - 1]; pp++) {
            pp->len = 0;
            pp->v = NULL;
        }
        lowpowers[0] = _one_;
        lowpowers[1] = z1;
        ans = _one_;

        hp = &z2.v[z2.len - 1];
        curhalf = *hp;
        curshift = BASEB - POWBITS;
        while (curshift && ((curhalf >> curshift) == 0)) {
            curshift -= POWBITS;
        }

        /*
         * Calculate the result by examining the power POWBITS bits at
         * a time, and use the table of low powers at each iteration.
         */
        for (;;) {
            curpow = (curhalf >> curshift) & (POWNUMS - 1);
            pp = &lowpowers[curpow];

            /*
             * If the small power is not yet saved in the table,
             * then calculate it and remember it in the table for
             * future use.
             */
            if (pp->v == NULL) {
                if (curpow & 0x1) {
                    zcopy(z1, &modpow);
                    free_z1 = false;
                } else {
                    modpow = _one_;
                }

                for (curbit = 0x2; curbit <= curpow; curbit *= 2) {
                    pp = &lowpowers[curbit];
                    if (pp->v == NULL) {
                        zsquare(lowpowers[curbit / 2], &temp);
                        zmod5(&temp);
                        zcopy(temp, pp);
                        zfree(temp);
                    }
                    if (curbit & curpow) {
                        zmul(*pp, modpow, &temp);
                        zfree(modpow);
                        zmod5(&temp);
                        zcopy(temp, &modpow);
                        zfree(temp);
                    }
                }
                pp = &lowpowers[curpow];
                if (pp->v != NULL) {
                    zfree(*pp);
                }
                *pp = modpow;
            }

            /*
             * If the power is nonzero, then accumulate the small
             * power into the result.
             */
            if (curpow) {
                zmul(ans, *pp, &temp);
                zfree(ans);
                zmod5(&temp);
                zcopy(temp, &ans);
                zfree(temp);
            }

            /*
             * Select the next POWBITS bits of the power, if
             * there is any more to generate.
             */
            curshift -= POWBITS;
            if (curshift < 0) {
                if (hp == z2.v) {
                    break;
                }
                curhalf = *--hp;
                curshift = BASEB - POWBITS;
            }

            /*
             * Square the result POWBITS times to make room for
             * the next chunk of bits.
             */
            for (i = 0; i < POWBITS; i++) {
                zsquare(ans, &temp);
                zfree(ans);
                zmod5(&temp);
                zcopy(temp, &ans);
                zfree(temp);
            }
        }

        for (pp = &lowpowers[2]; pp <= &lowpowers[POWNUMS - 1]; pp++) {
            zfree(*pp);
        }
        *res = ans;
        if (ztmp.len) {
            zfree(ztmp);
        }
        return;
    }

    /*
     * If the modulus is odd and small enough then use
     * the REDC algorithm.  The size where this is done is configurable.
     */
    if (z3.len < conf->redc2 && zisodd(z3)) {
        if (powermodredc && zcmp(powermodredc->mod, z3)) {
            zredcfree(powermodredc);
            powermodredc = NULL;
        }
        if (powermodredc == NULL) {
            powermodredc = zredcalloc(z3);
        }
        rp = powermodredc;
        zredcencode(rp, z1, &temp);
        if (free_z1 == true) {
            zfree(z1);
        }
        zredcpower(rp, temp, z2, &z1);
        zfree(temp);
        zredcdecode(rp, z1, res);
        zfree(z1);
        return;
    }

    /*
     * Modulus or power is small enough to perform the power raising
     * directly.  Initialize the table of powers.
     */
    for (pp = &lowpowers[2]; pp <= &lowpowers[POWNUMS - 1]; pp++) {
        pp->len = 0;
        pp->v = NULL;
    }
    lowpowers[0] = _one_;
    lowpowers[1] = z1;
    ans = _one_;

    hp = &z2.v[z2.len - 1];
    curhalf = *hp;
    curshift = BASEB - POWBITS;
    while (curshift && ((curhalf >> curshift) == 0)) {
        curshift -= POWBITS;
    }

    /*
     * Calculate the result by examining the power POWBITS bits at a time,
     * and use the table of low powers at each iteration.
     */
    for (;;) {
        curpow = (curhalf >> curshift) & (POWNUMS - 1);
        pp = &lowpowers[curpow];

        /*
         * If the small power is not yet saved in the table, then
         * calculate it and remember it in the table for future use.
         */
        if (pp->v == NULL) {
            if (curpow & 0x1) {
                zcopy(z1, &modpow);
                free_z1 = false;
            } else {
                modpow = _one_;
            }

            for (curbit = 0x2; curbit <= curpow; curbit *= 2) {
                pp = &lowpowers[curbit];
                if (pp->v == NULL) {
                    zsquare(lowpowers[curbit / 2], &temp);
                    zmod(temp, z3, pp, 0);
                    zfree(temp);
                }
                if (curbit & curpow) {
                    zmul(*pp, modpow, &temp);
                    zfree(modpow);
                    zmod(temp, z3, &modpow, 0);
                    zfree(temp);
                }
            }
            pp = &lowpowers[curpow];
            if (pp->v != NULL) {
                zfree(*pp);
            }
            *pp = modpow;
        }

        /*
         * If the power is nonzero, then accumulate the small power
         * into the result.
         */
        if (curpow) {
            zmul(ans, *pp, &temp);
            zfree(ans);
            zmod(temp, z3, &ans, 0);
            zfree(temp);
        }

        /*
         * Select the next POWBITS bits of the power, if there is
         * any more to generate.
         */
        curshift -= POWBITS;
        if (curshift < 0) {
            if (hp-- == z2.v) {
                break;
            }
            curhalf = *hp;
            curshift = BASEB - POWBITS;
        }

        /*
         * Square the result POWBITS times to make room for the next
         * chunk of bits.
         */
        for (i = 0; i < POWBITS; i++) {
            zsquare(ans, &temp);
            zfree(ans);
            zmod(temp, z3, &ans, 0);
            zfree(temp);
        }
    }

    for (pp = &lowpowers[2]; pp <= &lowpowers[POWNUMS - 1]; pp++) {
        zfree(*pp);
    }
    *res = ans;
    if (ztmp.len) {
        zfree(ztmp);
    }
    if (free_z1 == true) {
        zfree(z1);
    }
}

/*
 * Given a positive odd N-word integer z, evaluate minv(-z, BASEB^N)
 */
static void
zredcmodinv(ZVALUE z, ZVALUE *res)
{
    ZVALUE tmp;
    HALF *a0, *a, *b;
    HALF bit, h, inv, v;
    FULL f;
    LEN N, i, j, len;

    /* firewall */
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    N = z.len;
    tmp.sign = 0;
    tmp.len = N;
    tmp.v = alloc(N);
    zclearval(tmp);
    *tmp.v = 1;
    h = 1 + *z.v;
    bit = 1;
    inv = 1;
    while (h) {
        bit <<= 1;
        if (bit & h) {
            inv |= bit;
            h += bit * *z.v;
        }
    }

    j = N;
    a0 = tmp.v;
    while (j-- > 0) {
        v = inv * *a0;
        i = j;
        a = a0;
        b = z.v;
        f = (FULL)v * (FULL)*b++ + (FULL)*a++;
        *a0 = v;
        while (i-- > 0) {
            f = (FULL)v * (FULL)*b++ + (FULL)*a + (f >> BASEB);
            *a++ = (HALF)f;
        }
        while (j > 0 && *++a0 == 0) {
            j--;
        }
    }
    a = tmp.v + N;
    len = N;
    while (*--a == 0) {
        len--;
    }
    tmp.len = len;
    zcopy(tmp, res);
    zfree(tmp);
}

/*
 * Initialize the REDC algorithm for a particular modulus,
 * returning a pointer to a structure that is used for other
 * REDC calls.  An error is generated if the structure cannot
 * be allocated.  The modulus must be odd and positive.
 *
 * given:
 *      z1              modulus to initialize for
 */
REDC *
zredcalloc(ZVALUE z1)
{
    REDC *rp; /* REDC information */
    ZVALUE tmp;
    long bit;

    if (ziseven(z1) || zisneg(z1)) {
        math_error("REDC requires positive odd modulus");
        not_reached();
    }

    rp = (REDC *)calloc(1, sizeof(REDC));
    if (rp == NULL) {
        math_error("Cannot allocate REDC structure");
        not_reached();
    }

    /*
     * Round up the binary modulus to the next power of two
     * which is at a word boundary.  Then the shift and modulo
     * operations mod the binary modulus can be done very cheaply.
     * Calculate the REDC format for the number 1 for future use.
     */
    zcopy(z1, &rp->mod);
    zredcmodinv(z1, &rp->inv);
    bit = zhighbit(z1) + 1;
    if (bit % BASEB) {
        bit += (BASEB - (bit % BASEB));
    }
    zbitvalue(bit, &tmp);
    zmod(tmp, rp->mod, &rp->one, 0);
    zfree(tmp);
    rp->len = (LEN)(bit / BASEB);
    return rp;
}

/*
 * Free any numbers associated with the specified REDC structure,
 * and then the REDC structure itself.
 *
 * given:
 *      rp              REDC information to be cleared
 */
void
zredcfree(REDC *rp)
{
    /* firewall */
    if (rp == NULL) {
        math_error("%s: rp NULL", __func__);
        not_reached();
    }

    zfree(rp->mod);
    zfree(rp->inv);
    zfree(rp->one);
    free(rp);
}

/*
 * Convert a normal number into the specified REDC format.
 * The number to be converted can be negative or out of modulo range.
 * The resulting number can be used for multiplying, adding, subtracting,
 * or comparing with any other such converted numbers, as if the numbers
 * were being calculated modulo the number which initialized the REDC
 * information.  When the final value is not converted, the result is the
 * same as if the usual operations were done with the original numbers.
 *
 * given:
 *      rp              REDC information
 *      z1              number to be converted
 *      res             returned converted number
 */
void
zredcencode(REDC *rp, ZVALUE z1, ZVALUE *res)
{
    ZVALUE tmp1;

    /* firewall */
    if (rp == NULL) {
        math_error("%s: rp NULL", __func__);
        not_reached();
    }
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    /*
     * Confirm or initialize lastmod information when modulus is a
     * big number.
     */

    if (rp->len >= conf->pow2) {
        if (havelastmod && zcmp(rp->mod, *lastmod)) {
            zfree(*lastmod);
            zfree(*lastmodinv);
            havelastmod = false;
        }
        if (!havelastmod) {
            zcopy(rp->mod, lastmod);
            zbitvalue(2 * rp->len * BASEB, &tmp1);
            zquo(tmp1, rp->mod, lastmodinv, 0);
            zfree(tmp1);
            havelastmod = true;
        }
    }
    /*
     * Handle the cases 0, 1, -1, and 2 specially since these are
     * easy to calculate.  Zero transforms to zero, and the others
     * can be obtained from the precomputed REDC format for 1 since
     * addition and subtraction act normally for REDC format numbers.
     */
    if (ziszero(z1)) {
        *res = _zero_;
        return;
    }
    if (zisone(z1)) {
        zcopy(rp->one, res);
        return;
    }
    if (zisunit(z1)) {
        zsub(rp->mod, rp->one, res);
        return;
    }
    if (zistwo(z1)) {
        zadd(rp->one, rp->one, &tmp1);
        if (zrel(tmp1, rp->mod) < 0) {
            *res = tmp1;
            return;
        }
        zsub(tmp1, rp->mod, res);
        zfree(tmp1);
        return;
    }

    /*
     * Not a trivial number to convert, so do the full transformation.
     */
    zshift(z1, rp->len * BASEB, &tmp1);
    if (rp->len < conf->pow2) {
        zmod(tmp1, rp->mod, res, 0);
    } else {
        zmod6(tmp1, res);
    }
    zfree(tmp1);
}

/*
 * The REDC algorithm used to convert numbers out of REDC format and also
 * used after multiplication of two REDC numbers.  Using this routine
 * avoids any divides, replacing the divide by two multiplications.
 * If the numbers are very large, then these two multiplies will be
 * quicker than the divide, since dividing is harder than multiplying.
 *
 * given:
 *      rp              REDC information
 *      z1              number to be transformed
 *      res             returned transformed number
 */
void
zredcdecode(REDC *rp, ZVALUE z1, ZVALUE *res)
{
    ZVALUE tmp1, tmp2;
    ZVALUE ztmp;
    ZVALUE ztop;
    ZVALUE zp1;
    FULL muln;
    HALF *h1;
    HALF *h3;
    HALF *hd = NULL;
    HALF Ninv;
    LEN modlen;
    LEN len;
    FULL f;
    int sign;

    /* firewall */
    if (rp == NULL) {
        math_error("%s: rp NULL", __func__);
        not_reached();
    }
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }
    int i, j;

    /*
     * Check first for the special values for 0 and 1 that are easy.
     */
    if (ziszero(z1)) {
        *res = _zero_;
        return;
    }
    if ((z1.len == rp->one.len) && (z1.v[0] == rp->one.v[0]) && (zcmp(z1, rp->one) == 0)) {
        *res = _one_;
        return;
    }
    ztop.len = 0;
    ztmp.len = 0;
    modlen = rp->len;
    sign = z1.sign;
    z1.sign = 0;
    if (z1.len > modlen) {
        ztop.v = z1.v + modlen;
        ztop.len = z1.len - modlen;
        ztop.sign = 0;
        if (zrel(ztop, rp->mod) >= 0) {
            zmod(ztop, rp->mod, &ztmp, 0);
            ztop = ztmp;
        }
        len = modlen;
        h1 = z1.v + len;
        while (len > 0 && *--h1 == 0) {
            len--;
        }
        if (len == 0) {
            if (ztmp.len) {
                *res = ztmp;
            } else {
                zcopy(ztop, res);
            }
            return;
        }
        z1.len = len;
    }
    if (rp->mod.len < conf->pow2) {
        Ninv = rp->inv.v[0];
        res->sign = 0;
        res->len = modlen;
        res->v = alloc(modlen);
        zclearval(*res);
        h1 = z1.v;
        for (i = 0; i < modlen; i++) {
            h3 = rp->mod.v;
            hd = res->v;
            f = (FULL)*hd++;
            if (i < z1.len) {
                f += (FULL)*h1++;
            }
            muln = (HALF)((f & BASE1) * Ninv);
            f = ((muln * (FULL)*h3++) + f) >> BASEB;
            j = modlen;
            while (--j > 0) {
                f += (muln * (FULL)*h3++) + (FULL)*hd;
                hd[-1] = (HALF)f;
                f >>= BASEB;
                hd++;
            }
            hd[-1] = (HALF)f;
        }
        len = modlen;
        while (*--hd == 0 && len > 1) {
            len--;
        }
        if (len == 0) {
            len = 1;
        }
        res->len = len;
    } else {
        /* Here 0 < z1 < 2^bitnum */

        /*
         * First calculate the following:
         *      tmp2 = ((z1 * inv) % 2^bitnum.
         * The mod operations can be done with no work since the bit
         * number was selected as a multiple of the word size.  Just
         * reduce the sizes of the numbers as required.
         */
        zmul(z1, rp->inv, &tmp2);
        if (tmp2.len > modlen) {
            h1 = tmp2.v + modlen;
            len = modlen;
            while (len > 0 && *--h1 == 0) {
                len--;
            }
            tmp2.len = len;
        }

        /*
         * Next calculate the following:
         *      res = (z1 + tmp2 * modulus) / 2^bitnum
         * Since 0 < z1 < 2^bitnum and the division is always exact,
         * the quotient can be evaluated by rounding up
         * (tmp2 * modulus)/2^bitnum.  This can be achieved by defining
         * zp1 by an appropriate shift and then adding one.
         */
        zmul(tmp2, rp->mod, &tmp1);
        zfree(tmp2);
        if (tmp1.len > modlen) {
            zp1.v = tmp1.v + modlen;
            zp1.len = tmp1.len - modlen;
            zp1.sign = 0;
            zadd(zp1, _one_, res);
        } else {
            *res = _one_;
        }
        zfree(tmp1);
    }
    if (ztop.len) {
        zadd(*res, ztop, &tmp1);
        zfree(*res);
        if (ztmp.len) {
            zfree(ztmp);
        }
        *res = tmp1;
    }

    /*
     * Finally do a final modulo by a simple subtraction if necessary.
     * This is all that is needed because the previous calculation is
     * guaranteed to always be less than twice the modulus.
     */

    if (zrel(*res, rp->mod) >= 0) {
        zsub(*res, rp->mod, &tmp1);
        zfree(*res);
        *res = tmp1;
    }
    if (sign && !ziszero(*res)) {
        zsub(rp->mod, *res, &tmp1);
        zfree(*res);
        *res = tmp1;
    }
    return;
}

/*
 * Multiply two numbers in REDC format together producing a result also
 * in REDC format.  If the result is converted back to a normal number,
 * then the result is the same as the modulo'd multiplication of the
 * original numbers before they were converted to REDC format.  This
 * calculation is done in one of two ways, depending on the size of the
 * modulus.  For large numbers, the REDC definition is used directly
 * which involves three multiplies overall.  For small numbers, a
 * complicated routine is used which does the indicated multiplication
 * and the REDC algorithm at the same time to produce the result.
 *
 * given:
 *      rp              REDC information
 *      z1              first REDC number to be multiplied
 *      z2              second REDC number to be multiplied
 *      res             resulting REDC number
 */
void
zredcmul(REDC *rp, ZVALUE z1, ZVALUE z2, ZVALUE *res)
{
    FULL mulb;
    FULL muln;
    HALF *h1;
    HALF *h2;
    HALF *h3;
    HALF *hd;
    HALF Ninv;
    HALF topdigit = 0;
    LEN modlen;
    LEN len;
    LEN len2;
    SIUNION sival1;
    SIUNION sival2;
    SIUNION carry;
    ZVALUE tmp;
    ZVALUE z1tmp, z2tmp;
    int sign;

    /* firewall */
    if (rp == NULL) {
        math_error("%s: rp NULL", __func__);
        not_reached();
    }
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    sign = z1.sign ^ z2.sign;
    z1.sign = 0;
    z2.sign = 0;
    z1tmp.len = 0;
    if (zrel(z1, rp->mod) >= 0) {
        zmod(z1, rp->mod, &z1tmp, 0);
        z1 = z1tmp;
    }
    z2tmp.len = 0;
    if (zrel(z2, rp->mod) >= 0) {
        zmod(z2, rp->mod, &z2tmp, 0);
        z2 = z2tmp;
    }

    /*
     * Check for special values which we easily know the answer.
     */
    if (ziszero(z1) || ziszero(z2)) {
        *res = _zero_;
        if (z1tmp.len) {
            zfree(z1tmp);
        }
        if (z2tmp.len) {
            zfree(z2tmp);
        }
        return;
    }

    if ((z1.len == rp->one.len) && (z1.v[0] == rp->one.v[0]) && (zcmp(z1, rp->one) == 0)) {
        if (sign) {
            zsub(rp->mod, z2, res);
        } else {
            zcopy(z2, res);
        }
        if (z1tmp.len) {
            zfree(z1tmp);
        }
        if (z2tmp.len) {
            zfree(z2tmp);
        }
        return;
    }

    if ((z2.len == rp->one.len) && (z2.v[0] == rp->one.v[0]) && (zcmp(z2, rp->one) == 0)) {
        if (sign) {
            zsub(rp->mod, z1, res);
        } else {
            zcopy(z1, res);
        }
        if (z1tmp.len) {
            zfree(z1tmp);
        }
        if (z2tmp.len) {
            zfree(z2tmp);
        }
        return;
    }

    /*
     * If the size of the modulus is large, then just do the multiply,
     * followed by the two multiplies contained in the REDC routine.
     * This will be quicker than directly doing the REDC calculation
     * because of the O(N^1.585) speed of the multiplies.  The size
     * of the number which this is done is configurable.
     */
    if (rp->mod.len >= conf->redc2) {
        zmul(z1, z2, &tmp);
        zredcdecode(rp, tmp, res);
        zfree(tmp);
        if (sign && !ziszero(*res)) {
            zsub(rp->mod, *res, &tmp);
            zfree(*res);
            *res = tmp;
        }
        if (z1tmp.len) {
            zfree(z1tmp);
        }
        if (z2tmp.len) {
            zfree(z2tmp);
        }
        return;
    }

    /*
     * The number is small enough to calculate by doing the O(N^2) REDC
     * algorithm directly.  This algorithm performs the multiplication and
     * the reduction at the same time.  Notice the obscure facts that
     * only the lowest word of the inverse value is used, and that
     * there is no shifting of the partial products as there is in a
     * normal multiply.
     */
    modlen = rp->mod.len;
    Ninv = rp->inv.v[0];

    /*
     * Allocate the result and clear it.
     * The size of the result will be equal to or smaller than
     * the modulus size.
     */
    res->sign = 0;
    res->len = modlen;
    res->v = alloc(modlen);

    hd = res->v;
    len = modlen;
    zclearval(*res);

    /*
     * Do this outermost loop over all the digits of z1.
     */
    h1 = z1.v;
    len = z1.len;
    while (len--) {
        /*
         * Start off with the next digit of z1, the first
         * digit of z2, and the first digit of the modulus.
         */
        mulb = (FULL)*h1++;
        h2 = z2.v;
        h3 = rp->mod.v;
        hd = res->v;
        sival1.ivalue = mulb * ((FULL)*h2++) + ((FULL)*hd++);
        muln = ((HALF)(sival1.silow * Ninv));
        sival2.ivalue = muln * ((FULL)*h3++) + ((FULL)sival1.silow);
        carry.ivalue = ((FULL)sival1.sihigh) + ((FULL)sival2.sihigh);

        /*
         * Do this innermost loop for each digit of z2, except
         * for the first digit which was just done above.
         */
        len2 = z2.len;
        while (--len2 > 0) {
            sival1.ivalue = mulb * ((FULL)*h2++) + ((FULL)*hd) + ((FULL)carry.silow);
            sival2.ivalue = muln * ((FULL)*h3++) + ((FULL)sival1.silow);
            carry.ivalue = ((FULL)sival1.sihigh) + ((FULL)sival2.sihigh) + ((FULL)carry.sihigh);

            hd[-1] = sival2.silow;
            hd++;
        }

        /*
         * Now continue the loop as necessary so the total number
         * of iterations is equal to the size of the modulus.
         * This acts as if the innermost loop was repeated for
         * high digits of z2 that are zero.
         */
        len2 = modlen - z2.len;
        while (len2--) {
            sival2.ivalue = muln * ((FULL)*h3++) + ((FULL)*hd) + ((FULL)carry.silow);
            carry.ivalue = ((FULL)sival2.sihigh) + ((FULL)carry.sihigh);

            hd[-1] = sival2.silow;
            hd++;
        }

        carry.ivalue += topdigit;
        hd[-1] = carry.silow;
        topdigit = carry.sihigh;
    }

    /*
     * Now continue the loop as necessary so the total number
     * of iterations is equal to the size of the modulus.
     * This acts as if the outermost loop was repeated for high
     * digits of z1 that are zero.
     */
    len = modlen - z1.len;
    while (len--) {
        /*
         * Start off with the first digit of the modulus.
         */
        h3 = rp->mod.v;
        hd = res->v;
        muln = ((HALF)(*hd * Ninv));
        sival2.ivalue = muln * ((FULL)*h3++) + (FULL)*hd++;
        carry.ivalue = ((FULL)sival2.sihigh);

        /*
         * Do this innermost loop for each digit of the modulus,
         * except for the first digit which was just done above.
         */
        len2 = modlen;
        while (--len2 > 0) {
            sival2.ivalue = muln * ((FULL)*h3++) + ((FULL)*hd) + ((FULL)carry.silow);
            carry.ivalue = ((FULL)sival2.sihigh) + ((FULL)carry.sihigh);

            hd[-1] = sival2.silow;
            hd++;
        }
        carry.ivalue += topdigit;
        hd[-1] = carry.silow;
        topdigit = carry.sihigh;
    }

    /*
     * Determine the true size of the result, taking the top digit of
     * the current result into account.  The top digit is not stored in
     * the number because it is temporary and would become zero anyway
     * after the final subtraction is done.
     */
    if (topdigit == 0) {
        len = modlen;
        while (*--hd == 0 && len > 1) {
            len--;
        }
        res->len = len;

        /*
         * Compare the result with the modulus.
         * If it is less than the modulus, then the calculation is complete.
         */

        if (zrel(*res, rp->mod) < 0) {
            if (z1tmp.len) {
                zfree(z1tmp);
            }
            if (z2tmp.len) {
                zfree(z2tmp);
            }
            if (sign && !ziszero(*res)) {
                zsub(rp->mod, *res, &tmp);
                zfree(*res);
                *res = tmp;
            }
            return;
        }
    }

    /*
     * Do a subtraction to reduce the result to a value less than
     * the modulus.  The REDC algorithm guarantees that a single subtract
     * is all that is needed.  Ignore any borrowing from the possible
     * highest word of the current result because that would affect
     * only the top digit value that was not stored and would become
     * zero anyway.
     */
    carry.ivalue = 0;
    h1 = rp->mod.v;
    hd = res->v;
    len = modlen;
    while (len--) {
        carry.ivalue = BASE1 - ((FULL)*hd) + ((FULL)*h1++) + ((FULL)carry.silow);
        *hd++ = (HALF)(BASE1 - carry.silow);
        carry.silow = carry.sihigh;
    }

    /*
     * Now finally recompute the size of the result.
     */
    len = modlen;
    hd = &res->v[len - 1];
    while ((*hd == 0) && (len > 1)) {
        hd--;
        len--;
    }
    res->len = len;
    if (z1tmp.len) {
        zfree(z1tmp);
    }
    if (z2tmp.len) {
        zfree(z2tmp);
    }
    if (sign && !ziszero(*res)) {
        zsub(rp->mod, *res, &tmp);
        zfree(*res);
        *res = tmp;
    }
}

/*
 * Square a number in REDC format producing a result also in REDC format.
 *
 * given:
 *      rp              REDC information
 *      z1              REDC number to be squared
 *      res             resulting REDC number
 */
void
zredcsquare(REDC *rp, ZVALUE z1, ZVALUE *res)
{
    FULL mulb;
    FULL muln;
    HALF *h1;
    HALF *h2;
    HALF *h3;
    HALF *hd = NULL;
    HALF Ninv;
    HALF topdigit = 0;
    LEN modlen;
    LEN len;
    SIUNION sival1;
    SIUNION sival2;
    SIUNION sival3;
    SIUNION carry;
    ZVALUE tmp, ztmp;
    FULL f;
    int i, j;

    /* firewall */
    if (rp == NULL) {
        math_error("%s: rp NULL", __func__);
        not_reached();
    }
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    ztmp.len = 0;
    z1.sign = 0;
    if (zrel(z1, rp->mod) >= 0) {
        zmod(z1, rp->mod, &ztmp, 0);
        z1 = ztmp;
    }
    if (ziszero(z1)) {
        *res = _zero_;
        if (ztmp.len) {
            zfree(ztmp);
        }
        return;
    }
    if ((z1.len == rp->one.len) && (z1.v[0] == rp->one.v[0]) && (zcmp(z1, rp->one) == 0)) {
        zcopy(z1, res);
        if (ztmp.len) {
            zfree(ztmp);
        }
        return;
    }

    /*
     * If the modulus is small enough, then call the multiply
     * routine to produce the result.  Otherwise call the O(N^1.585)
     * routines to get the answer.
     */
    if (rp->mod.len >= conf->redc2 || 3 * z1.len < 2 * rp->mod.len) {
        zsquare(z1, &tmp);
        zredcdecode(rp, tmp, res);
        zfree(tmp);
        if (ztmp.len) {
            zfree(ztmp);
        }
        return;
    }
    modlen = rp->mod.len;
    Ninv = rp->inv.v[0];

    res->sign = 0;
    res->len = modlen;
    res->v = alloc(modlen);

    zclearval(*res);

    h1 = z1.v;

    for (i = 0; i < z1.len; i++) {
        mulb = (FULL)*h1++;
        h2 = h1;
        h3 = rp->mod.v;
        hd = res->v;
        if (i == 0) {
            sival1.ivalue = mulb * mulb;
            muln = (HALF)(sival1.silow * Ninv);
            sival2.ivalue = muln * ((FULL)*h3++) + (FULL)sival1.silow;
            carry.ivalue = (FULL)sival1.sihigh + (FULL)sival2.sihigh;
            hd++;
        } else {
            muln = (HALF)(*hd * Ninv);
            f = (muln * ((FULL)*h3++) + (FULL)*hd++) >> BASEB;
            j = i;
            while (--j > 0) {
                f += muln * ((FULL)*h3++) + *hd;
                hd[-1] = (HALF)f;
                f >>= BASEB;
                hd++;
            }
            carry.ivalue = f;
            sival1.ivalue = mulb * mulb + (FULL)carry.silow;
            sival2.ivalue = muln * ((FULL)*h3++) + (FULL)*hd + (FULL)sival1.silow;
            carry.ivalue = (FULL)sival1.sihigh + (FULL)sival2.sihigh + (FULL)carry.sihigh;
            hd[-1] = sival2.silow;
            hd++;
        }
        j = z1.len - i;
        while (--j > 0) {
            sival1.ivalue = mulb * ((FULL)*h2++);
            sival2.ivalue = ((FULL)sival1.silow << 1) + muln * ((FULL)*h3++);
            sival3.ivalue = (FULL)sival2.silow + (FULL)*hd + (FULL)carry.silow;
            carry.ivalue = ((FULL)sival1.sihigh << 1) + (FULL)sival2.sihigh + (FULL)sival3.sihigh + (FULL)carry.sihigh;
            hd[-1] = sival3.silow;
            hd++;
        }
        j = modlen - z1.len;
        while (j-- > 0) {
            sival1.ivalue = muln * ((FULL)*h3++) + (FULL)*hd + (FULL)carry.silow;
            carry.ivalue = (FULL)sival1.sihigh + (FULL)carry.sihigh;
            hd[-1] = sival1.silow;
            hd++;
        }
        carry.ivalue += (FULL)topdigit;
        hd[-1] = carry.silow;
        topdigit = carry.sihigh;
    }
    i = modlen - z1.len;
    while (i-- > 0) {
        h3 = rp->mod.v;
        hd = res->v;
        muln = (HALF)(*hd * Ninv);
        sival1.ivalue = muln * ((FULL)*h3++) + (FULL)*hd++;
        carry.ivalue = (FULL)sival1.sihigh;
        j = modlen;
        while (--j > 0) {
            sival1.ivalue = muln * ((FULL)*h3++) + (FULL)*hd + (FULL)carry.silow;
            carry.ivalue = (FULL)sival1.sihigh + (FULL)carry.sihigh;
            hd[-1] = sival1.silow;
            hd++;
        }
        carry.ivalue += (FULL)topdigit;
        hd[-1] = carry.silow;
        topdigit = carry.sihigh;
    }
    if (topdigit == 0) {
        len = modlen;
        while (*--hd == 0 && len > 1) {
            len--;
        }
        res->len = len;
        if (zrel(*res, rp->mod) < 0) {
            if (ztmp.len) {
                zfree(ztmp);
            }
            return;
        }
    }

    carry.ivalue = 0;
    h1 = rp->mod.v;
    hd = res->v;
    len = modlen;
    while (len--) {
        carry.ivalue = BASE1 - ((FULL)*hd) + ((FULL)*h1++) + ((FULL)carry.silow);
        *hd++ = (HALF)(BASE1 - carry.silow);
        carry.silow = carry.sihigh;
    }

    len = modlen;
    hd = &res->v[len - 1];
    while ((*hd == 0) && (len > 1)) {
        hd--;
        len--;
    }
    res->len = len;
    if (ztmp.len) {
        zfree(ztmp);
    }
}

/*
 * Compute the result of raising a REDC format number to a power.
 * The result is within the range 0 to the modulus - 1.
 * This calculates the result by examining the power POWBITS bits at a time,
 * using a small table of POWNUMS low powers to calculate powers for those bits,
 * and repeated squaring and multiplying by the partial powers to generate
 * the complete power.
 *
 * given:
 *      rp              REDC information
 *      z1              REDC number to be raised
 *      z2              normal number to raise number to
 *      res             result
 */
void
zredcpower(REDC *rp, ZVALUE z1, ZVALUE z2, ZVALUE *res)
{
    HALF *hp;         /* pointer to current word of the power */
    ZVALUE *pp;       /* pointer to low power table */
    ZVALUE ans, temp; /* calculation values */
    ZVALUE ztmp;
    ZVALUE modpow;             /* current small power */
    ZVALUE lowpowers[POWNUMS]; /* low powers */
    int curshift;              /* shift value for word of power */
    HALF curhalf;              /* current word of power */
    unsigned int curpow;       /* current low power */
    unsigned int curbit;       /* current bit of low power */
    int sign;
    int i;

    /* firewall */
    if (rp == NULL) {
        math_error("%s: rp NULL", __func__);
        not_reached();
    }
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    if (zisneg(z2)) {
        math_error("Negative power in zredcpower");
        not_reached();
    }

    if (zisunit(rp->mod)) {
        *res = _zero_;
        return;
    }

    sign = zisodd(z2) ? z1.sign : 0;
    z1.sign = 0;
    ztmp.len = 0;
    if (zrel(z1, rp->mod) >= 0) {
        zmod(z1, rp->mod, &ztmp, 0);
        z1 = ztmp;
    }
    /*
     * Check for zero or the REDC format for one.
     */
    if (ziszero(z1)) {
        if (ziszero(z2)) {
            *res = _one_;
        } else {
            *res = _zero_;
        }
        if (ztmp.len) {
            zfree(ztmp);
        }
        return;
    }
    if (zcmp(z1, rp->one) == 0) {
        if (sign) {
            zsub(rp->mod, rp->one, res);
        } else {
            zcopy(rp->one, res);
        }
        if (ztmp.len) {
            zfree(ztmp);
        }
        return;
    }

    /*
     * See if the number being raised is the REDC format for -1.
     * If so, then the answer is the REDC format for one or minus one.
     * To do this check, calculate the REDC format for -1.
     */
    if (((HALF)(z1.v[0] + rp->one.v[0])) == rp->mod.v[0]) {
        zsub(rp->mod, rp->one, &temp);
        if (zcmp(z1, temp) == 0) {
            if (zisodd(z2) ^ sign) {
                *res = temp;
                if (ztmp.len) {
                    zfree(ztmp);
                }
                return;
            }
            zfree(temp);
            zcopy(rp->one, res);
            if (ztmp.len) {
                zfree(ztmp);
            }
            return;
        }
        zfree(temp);
    }

    for (pp = &lowpowers[2]; pp < &lowpowers[POWNUMS]; pp++) {
        pp->len = 0;
    }
    zcopy(rp->one, &lowpowers[0]);
    zcopy(z1, &lowpowers[1]);
    zcopy(rp->one, &ans);

    hp = &z2.v[z2.len - 1];
    curhalf = *hp;
    curshift = BASEB - POWBITS;
    while (curshift && ((curhalf >> curshift) == 0)) {
        curshift -= POWBITS;
    }

    /*
     * Calculate the result by examining the power POWBITS bits at a time,
     * and use the table of low powers at each iteration.
     */
    for (;;) {
        curpow = (curhalf >> curshift) & (POWNUMS - 1);
        pp = &lowpowers[curpow];

        /*
         * If the small power is not yet saved in the table, then
         * calculate it and remember it in the table for future use.
         */
        if (pp->len == 0) {
            if (curpow & 0x1) {
                zcopy(z1, &modpow);
            } else {
                zcopy(rp->one, &modpow);
            }

            for (curbit = 0x2; curbit <= curpow; curbit *= 2) {
                pp = &lowpowers[curbit];
                if (pp->len == 0) {
                    zredcsquare(rp, lowpowers[curbit / 2], pp);
                }
                if (curbit & curpow) {
                    zredcmul(rp, *pp, modpow, &temp);
                    zfree(modpow);
                    modpow = temp;
                }
            }
            pp = &lowpowers[curpow];
            if (pp->len > 0) {
                zfree(*pp);
            }
            *pp = modpow;
        }

        /*
         * If the power is nonzero, then accumulate the small power
         * into the result.
         */
        if (curpow) {
            zredcmul(rp, ans, *pp, &temp);
            zfree(ans);
            ans = temp;
        }

        /*
         * Select the next POWBITS bits of the power, if there is
         * any more to generate.
         */
        curshift -= POWBITS;
        if (curshift < 0) {
            if (hp-- == z2.v) {
                break;
            }
            curhalf = *hp;
            curshift = BASEB - POWBITS;
        }

        /*
         * Square the result POWBITS times to make room for the next
         * chunk of bits.
         */
        for (i = 0; i < POWBITS; i++) {
            zredcsquare(rp, ans, &temp);
            zfree(ans);
            ans = temp;
        }
    }

    for (pp = lowpowers; pp < &lowpowers[POWNUMS]; pp++) {
        zfree(*pp);
    }
    if (sign && !ziszero(ans)) {
        zsub(rp->mod, ans, res);
        zfree(ans);
    } else {
        *res = ans;
    }
    if (ztmp.len) {
        zfree(ztmp);
    }
}

/*
 * zhnrmod - compute z mod h*2^n+r
 *
 * We compute v mod h*2^n+r, where h>0, n>0, abs(r) <= 1, as follows:
 *
 *      Let v = b*2^n + a, where 0 <= a < 2^n
 *
 *      Now v mod h*2^n+r == b*2^n + a mod h*2^n+r,
 *      and thus v mod h*2^n+r == b*2^n mod h*2^n+r + a mod h*2^n+r.
 *
 *      Because 0 <= a < 2^n < h*2^n+r, a mod h*2^n+r == a.
 *      Thus v mod h*2^n+r == b*2^n mod h*2^n+r + a.
 *
 *      It can be shown that b*2^n mod h*2^n == 2^n * (b mod h).
 *
 *      Thus for r == 0, v mod h*2^n+r == (2^n)*(b mod h) + a.
 *
 *      It can be shown that v mod 2^n-1 == a+b mod 2^n-1.
 *
 *      Thus for r == -1, v mod h*2^n+r == (2^n)*(b mod h) + a + int(b/h).
 *
 *      It can be shown that v mod 2^n+1 == a-b mod 2^n+1.
 *
 *      Thus for r == +1, v mod h*2^n+r == (2^n)*(b mod h) + a - int(b/h).
 *
 *      Therefore, v mod h*2^n+r == (2^n)*(b mod h) + a - r*int(b/h).
 *
 * The above proof leads to the following calc resource file which computes
 * the value z mod h*2^n+r:
 *
 *    define hnrmod(v,h,n,r)
 *    {
 *      local a,b,modulus,tquo,tmod,lbit,ret;
 *
 *      if (!isint(h) || h < 1) {
 *          quit "h must be an integer be > 0";
 *      }
 *      if (!isint(n) || n < 1) {
 *          quit "n must be an integer be > 0";
 *      }
 *      if (r != 1 && r != 0 && r != -1) {
 *          quit "r must be -1, 0 or 1";
 *      }
 *
 *      lbit = lowbit(h);
 *      if (lbit > 0) {
 *          n += lbit;
 *          h >>= lbit;
 *      }
 *
 *      modulus = h<<n+r;
 *      if (modulus <= 2^31-1) {
 *          return v % modulus;
 *      }
 *      ret = v;
 *
 *      do {
 *          if (highbit(ret) < n) {
 *              break;
 *          }
 *          b = ret>>n;
 *          a = ret - (b<<n);
 *
 *          switch (r) {
 *          case -1:
 *              if (h == 1) {
 *                  ret = a + b;
 *              } else {
 *                  quomod(b, h, tquo, tmod);
 *                  ret = tmod<<n + a + tquo;
 *              }
 *              break;
 *          case 0:
 *              if (h == 1) {
 *                  ret = a;
 *              } else {
 *                  ret = (b%h)<<n + a;
 *              }
 *              break;
 *          case 1:
 *              if (h == 1) {
 *                  ret = ((a > b) ? a-b : modulus+a-b);
 *              } else {
 *                  quomod(b, h, tquo, tmod);
 *                  tmod = tmod<<n + a;
 *                  ret = ((tmod >= tquo) ? tmod-tquo : modulus+tmod-tquo);
 *              }
 *              break;
 *          }
 *      } while (ret > modulus);
 *      ret = ((ret < 0) ? ret+modulus : ((ret == modulus) ? 0 : ret));
 *
 *      return ret;
 *    }
 *
 * This function implements the above calc resource file.
 *
 * given:
 *      v               take mod of this value, v >= 0
 *      zh              h from modulus h*2^n+r, h > 0
 *      zn              n from modulus h*2^n+r, n > 0
 *      zr              r from modulus h*2^n+r, abs(r) <= 1
 *      res             v mod h*2^n+r
 */
void
zhnrmod(ZVALUE v, ZVALUE zh, ZVALUE zn, ZVALUE zr, ZVALUE *res)
{
    ZVALUE a;       /* lower n bits of v */
    ZVALUE b;       /* bits above the lower n bits of v */
    ZVALUE h;       /* working zh value */
    ZVALUE modulus; /* h^2^n + r */
    ZVALUE tquo;    /* b // h */
    ZVALUE tmod;    /* b % h or (b%h)<<n + a */
    ZVALUE t;       /* temp ZVALUE */
    ZVALUE t2;      /* temp ZVALUE */
    ZVALUE ret;     /* return value, what *res is set to */
    long n;         /* integer value of zn */
    long r;         /* integer value of zr */
    long hbit;      /* highbit(res) */
    long lbit;      /* lowbit(h) */
    int zrelval;    /* return value of zrel() */
    int hisone;     /* 1 => h == 1, 0 => h != 1 */

    /* firewall */
    if (res == NULL) {
        math_error("%s: res NULL", __func__);
        not_reached();
    }

    /*
     * firewall
     */
    if (zisneg(zh) || ziszero(zh)) {
        math_error("h must be > 0");
        not_reached();
    }
    if (zisneg(zn) || ziszero(zn)) {
        math_error("n must be > 0");
        not_reached();
    }
    if (zge31b(zn)) {
        math_error("n must be < 2^31");
        not_reached();
    }
    if (!zisabsleone(zr)) {
        math_error("r must be -1, 0 or 1");
        not_reached();
    }

    /*
     * setup for loop
     */
    n = ztolong(zn);
    r = ztolong(zr);
    if (zisneg(zr)) {
        r = -r;
    }
    /* lbit = lowbit(h); */
    lbit = zlowbit(zh);
    /* if (lbit > 0) { n += lbit; h >>= lbit; } */
    if (lbit > 0) {
        n += lbit;
        zshift(zh, -lbit, &h);
    } else {
        h = zh;
    }
    /* modulus = h<<n+r; */
    zshift(h, n, &t);
    switch (r) {
    case 1:
        zadd(t, _one_, &modulus);
        zfree(t);
        break;
    case 0:
        modulus = t;
        break;
    case -1:
        zsub(t, _one_, &modulus);
        zfree(t);
        break;
    }
    /* if (modulus <= MAXLONG) { return v % modulus; } */
    if (!zgtmaxlong(modulus)) {
        itoz(zmodi(v, ztolong(modulus)), res);
        zfree(modulus);
        if (lbit > 0) {
            zfree(h);
        }
        return;
    }
    /* ret = v; */
    zcopy(v, &ret);

    /*
     * shift-add modulus loop
     */
    hisone = zisone(h);
    do {

        /*
         * split ret into to chunks, the lower n bits
         * and everything above the lower n bits
         */
        /* if (highbit(ret) < n) { break; } */
        hbit = (long)zhighbit(ret);
        if (hbit < n) {
            zrelval = (zcmp(ret, modulus) ? -1 : 0);
            break;
        }
        /* b = ret>>n; */
        zshift(ret, -n, &b);
        b.sign = ret.sign;
        /* a = ret - (b<<n); */
        a.sign = ret.sign;
        a.len = (n + BASEB - 1) / BASEB;
        a.v = alloc(a.len);
        memcpy(a.v, ret.v, a.len * sizeof(HALF));
        if (n % BASEB) {
            a.v[a.len - 1] &= lowhalf[n % BASEB];
        }
        ztrim(&a);

        /*
         * switch depending on r == -1, 0 or 1
         */
        switch (r) {
        case -1: /* v mod h*2^h-1 */
            /* if (h == 1) ... */
            if (hisone) {
                /* ret = a + b; */
                zfree(ret);
                zadd(a, b, &ret);

                /* ... else ... */
            } else {
                /* quomod(b, h, tquo, tmod); */
                (void)zdiv(b, h, &tquo, &tmod, 0);
                /* ret = tmod<<n + a + tquo; */
                zshift(tmod, n, &t);
                zfree(tmod);
                zadd(a, tquo, &t2);
                zfree(tquo);
                zfree(ret);
                zadd(t, t2, &ret);
                zfree(t);
                zfree(t2);
            }
            break;

        case 0: /* v mod h*2^h-1 */
            /* if (h == 1) ... */
            if (hisone) {
                /* ret = a; */
                zfree(ret);
                zcopy(a, &ret);

                /* ... else ... */
            } else {
                /* ret = (b%h)<<n + a; */
                (void)zmod(b, h, &tmod, 0);
                zshift(tmod, n, &t);
                zfree(tmod);
                zfree(ret);
                zadd(t, a, &ret);
                zfree(t);
            }
            break;

        case 1: /* v mod h*2^h-1 */
            /* if (h == 1) ... */
            if (hisone) {
                /* ret = a-b; */
                zfree(ret);
                zsub(a, b, &ret);

                /* ... else ... */
            } else {
                /* quomod(b, h, tquo, tmod); */
                (void)zdiv(b, h, &tquo, &tmod, 0);
                /* tmod = tmod<<n + a; */
                zshift(tmod, n, &t);
                zfree(tmod);
                zadd(t, a, &tmod);
                zfree(t);
                /* ret = tmod-tquo; */
                zfree(ret);
                zsub(tmod, tquo, &ret);
                zfree(tquo);
                zfree(tmod);
            }
            break;
        }
        zfree(a);
        zfree(b);

        /* ... while (abs(ret) > modulus); */
    } while ((zrelval = zabsrel(ret, modulus)) > 0);
    /* ret = ((ret < 0) ? ret+modulus : ((ret == modulus) ? 0 : ret)); */
    if (ret.sign) {
        zadd(ret, modulus, &t);
        zfree(ret);
        ret = t;
    } else if (zrelval == 0) {
        zfree(ret);
        ret = _zero_;
    }
    zfree(modulus);
    if (lbit > 0) {
        zfree(h);
    }

    /*
     * return ret
     */
    *res = ret;
    return;
}
