⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 zmod.c

📁 Calc Software Package for Number Calc
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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 + -