📄 zmod.c
字号:
/* * zmod - modulo arithmetic routines * * Copyright (C) 1999 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. * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * @(#) $Revision: 29.4 $ * @(#) $Id: zmod.c,v 29.4 2006/06/11 00:08:56 chongo Exp $ * @(#) $Source: /usr/local/src/cmd/calc/RCS/zmod.c,v $ * * 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. */#include "config.h"#include "zmath.h"#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 */voidzsquaremod(ZVALUE z1, ZVALUE z2, ZVALUE *res){ ZVALUE tmp; FULL prod; FULL digit; if (ziszero(z2) || zisneg(z2)) math_error("Mod of non-positive integer"); /*NOTREACHED*/ 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 */voidzminmod(ZVALUE z1, ZVALUE z2, ZVALUE *res){ ZVALUE tmp1, tmp2; int sign; int cv; if (ziszero(z2) || zisneg(z2)) { math_error("Mod of non-positive integer"); /*NOTREACHED*/ } 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 */BOOLzcmpmod(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"); /*NOTREACHED*/ } 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 voidzmod5(ZVALUE *zp){ LEN len, modlen, j; ZVALUE tmp1, tmp2; ZVALUE z1, z2, z3; HALF *a, *b; FULL f; HALF u; 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!!!"); /*NOTREACHED*/ } 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"); /*NOTREACHED*/ } 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 voidzmod6(ZVALUE z1, ZVALUE *res){ LEN len, modlen, len0; int sign; ZVALUE zp0, ztmp; 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. */voidzpowermod(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 */ int i; if (zisneg(z3) || ziszero(z3)) { math_error("Non-positive modulus in zpowermod"); /*NOTREACHED*/ } if (zisneg(z2)) { math_error("Negative power in zpowermod"); /*NOTREACHED*/ } /* * 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; if (zisneg(z1) || zrel(z1, z3) >= 0) { zmod(z1, z3, &ztmp, 0); z1 = ztmp; } if (ziszero(z1)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -