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

📄 zmod.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Copyright (c) 1994 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * 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 "zmath.h"#define	POWBITS	4		/* bits for power chunks (must divide BASEB) */#define	POWNUMS	(1<<POWBITS)	/* number of powers needed in table */LEN _pow2_ = POW_ALG2;		/* modulo size to use REDC for powers */LEN _redc2_ = REDC_ALG2;	/* modulo size to use second REDC algorithm */static REDC *powermodredc = NULL;	/* REDC info for raising to power */#if 0extern void zaddmod MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE z3, ZVALUE *res));extern void znegmod MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));/* * Multiply two numbers together and then mod the result with a third number. * The two numbers to be multiplied can be negative or out of modulo range. * The result will be in the range 0 to the modulus - 1. */voidzmulmod(z1, z2, z3, res)	ZVALUE z1;		/* first number to be multiplied */	ZVALUE z2;		/* second number to be multiplied */	ZVALUE z3;		/* number to take mod with */	ZVALUE *res;		/* result */{	ZVALUE tmp;	FULL prod;	FULL digit;	BOOL neg;	if (ziszero(z3) || zisneg(z3))		math_error("Mod of non-positive integer");	if (ziszero(z1) || ziszero(z2) || zisunit(z3)) {		*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(z3)) {		neg = (z1.sign != z2.sign);		digit = z3.v[0];		if ((digit & -digit) == digit) {	/* NEEDS 2'S COMP */			prod = ((FULL) z1.v[0]) * ((FULL) z2.v[0]);			prod &= (digit - 1);		} else {			z1.sign = 0;			z2.sign = 0;			prod = (FULL) zmodi(z1, (long) digit);			prod *= (FULL) zmodi(z2, (long) digit);			prod %= digit;		}		if (neg && prod)			prod = digit - prod;		itoz((long) prod, res);		return;	}	/*	 * The modulus is more than one digit.	 * Actually do the multiply and divide if necessary.	 */	zmul(z1, z2, &tmp);	if (zispos(tmp) && ((tmp.len < z3.len) || ((tmp.len == z3.len) &&		(tmp.v[tmp.len-1] < z2.v[z3.len-1]))))	{		*res = tmp;		return;	}	zmod(tmp, z3, res);	zfree(tmp);}/* * 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. */voidzsquaremod(z1, z2, res)	ZVALUE z1;		/* number to be squared */	ZVALUE z2;		/* number to take mod with */	ZVALUE *res;		/* result */{	ZVALUE tmp;	FULL prod;	FULL digit;	if (ziszero(z2) || zisneg(z2))		math_error("Mod of non-positive integer");	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);	zfree(tmp);}/* * Add two numbers together and then mod the result with a third number. * The two numbers to be added can be negative or out of modulo range. * The result will be in the range 0 to the modulus - 1. */static voidzaddmod(z1, z2, z3, res)	ZVALUE z1;		/* first number to be added */	ZVALUE z2;		/* second number to be added */	ZVALUE z3;		/* number to take mod with */	ZVALUE *res;		/* result */{	ZVALUE tmp;	FULL sumdigit;	FULL moddigit;	if (ziszero(z3) || zisneg(z3))		math_error("Mod of non-positive integer");	if ((ziszero(z1) && ziszero(z2)) || zisunit(z3)) {		*res = _zero_;		return;	}	if (zistwo(z2)) {		if ((z1.v[0] + z2.v[0]) & 0x1)			*res = _one_;		else			*res = _zero_;		return;	}	zadd(z1, z2, &tmp);	if (zisneg(tmp) || (tmp.len > z3.len)) {		zmod(tmp, z3, res);		zfree(tmp);		return;	}	sumdigit = tmp.v[tmp.len - 1];	moddigit = z3.v[z3.len - 1];	if ((tmp.len < z3.len) || (sumdigit < moddigit)) {		*res = tmp;		return;	}	if (sumdigit < 2 * moddigit) {		zsub(tmp, z3, res);		zfree(tmp);		return;	}	zmod(tmp, z2, res);	zfree(tmp);}/* * Subtract two numbers together and then mod the result with a third number. * The two numbers to be subtract can be negative or out of modulo range. * The result will be in the range 0 to the modulus - 1. */voidzsubmod(z1, z2, z3, res)	ZVALUE z1;		/* number to be subtracted from */	ZVALUE z2;		/* number to be subtracted */	ZVALUE z3;		/* number to take mod with */	ZVALUE *res;		/* result */{	if (ziszero(z3) || zisneg(z3))		math_error("Mod of non-positive integer");	if (ziszero(z2)) {		zmod(z1, z3, res);		return;	}	if (ziszero(z1)) {		znegmod(z2, z3, res);		return;	}	if ((z1.sign == z2.sign) && (z1.len == z2.len) &&		(z1.v[0] == z2.v[0]) && (zcmp(z1, z2) == 0)) {			*res = _zero_;			return;	}	z2.sign = !z2.sign;	zaddmod(z1, z2, z3, res);}/* * Calculate the negative of a number modulo another number. * The number to be negated can be negative or out of modulo range. * The result will be in the range 0 to the modulus - 1. */static voidznegmod(z1, z2, res)	ZVALUE z1;		/* number to take negative of */	ZVALUE z2;		/* number to take mod with */	ZVALUE *res;		/* result */{	int sign;	int cv;	if (ziszero(z2) || zisneg(z2))		math_error("Mod of non-positive integer");	if (ziszero(z1) || zisunit(z2)) {		*res = _zero_;		return;	}	if (zistwo(z2)) {		if (z1.v[0] & 0x1)			*res = _one_;		else			*res = _zero_;		return;	}	/*	 * If the absolute value of the number is within the modulo range,	 * then the result is just a copy or a subtraction.  Otherwise go	 * ahead and negate and reduce the result.	 */	sign = z1.sign;	z1.sign = 0;	cv = zrel(z1, z2);	if (cv == 0) {		*res = _zero_;		return;	}	if (cv < 0) {		if (sign)			zcopy(z1, res);		else			zsub(z2, z1, res);		return;	}	z1.sign = !sign;	zmod(z1, z2, res);}#endif/* * 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]. */voidzminmod(z1, z2, res)	ZVALUE z1;		/* number to find minimum congruence of */	ZVALUE z2;		/* number to take mod with */	ZVALUE *res;		/* result */{	ZVALUE tmp1, tmp2;	int sign;	int cv;	if (ziszero(z2) || zisneg(z2))		math_error("Mod of non-positive integer");	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);		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. */BOOLzcmpmod(z1, z2, z3)	ZVALUE z1;		/* first number to be compared */	ZVALUE z2;		/* second number to be compared */	ZVALUE z3;		/* modulus */{	ZVALUE tmp1, tmp2, tmp3;	FULL digit;	LEN len;	int cv;	if (zisneg(z3) || ziszero(z3))		math_error("Non-positive modulus in zcmpmod");	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)))	{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -