📄 zmod.c
字号:
/* * 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. */voidzredcmul(rp, z1, z2, res) REDC *rp; /* REDC information */ ZVALUE z1; /* first REDC number to be multiplied */ ZVALUE z2; /* second REDC number to be multiplied */ ZVALUE *res; /* resulting REDC number */{ 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 sival3; SIUNION carry; ZVALUE tmp; if (zisneg(z1) || (z1.len > rp->mod.len) || zisneg(z2) || (z2.len > rp->mod.len)) math_error("Negative or too large number in zredcmul"); /* * Check for special values which we easily know the answer. */ if (ziszero(z1) || ziszero(z2)) { *res = _zero_; return; } if ((z1.len == rp->one.len) && (z1.v[0] == rp->one.v[0]) && (zcmp(z1, rp->one) == 0)) { zcopy(z2, res); return; } if ((z2.len == rp->one.len) && (z2.v[0] == rp->one.v[0]) && (zcmp(z2, rp->one) == 0)) { zcopy(z1, res); 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 >= _redc2_) { zmul(z1, z2, &tmp); zredcdecode(rp, tmp, res); zfree(tmp); 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++); sival3.ivalue = ((FULL) sival1.silow) + ((FULL) sival2.silow); carry.ivalue = ((FULL) sival1.sihigh) + ((FULL) sival2.sihigh) + ((FULL) sival3.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++); sival2.ivalue = muln * ((FULL) *h3++); sival3.ivalue = ((FULL) sival1.silow) + ((FULL) sival2.silow) + ((FULL) *hd) + ((FULL) carry.silow); carry.ivalue = ((FULL) sival1.sihigh) + ((FULL) sival2.sihigh) + ((FULL) sival3.sihigh) + ((FULL) carry.sihigh); hd[-1] = sival3.silow; hd++; } /* * Now continue the loop as necessary so the total number * of interations 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++); sival3.ivalue = ((FULL) sival2.silow) + ((FULL) *hd) + ((FULL) carry.silow); carry.ivalue = ((FULL) sival2.sihigh) + ((FULL) sival3.sihigh) + ((FULL) carry.sihigh); hd[-1] = sival3.silow; hd++; } res->v[modlen - 1] = carry.silow; topdigit = carry.sihigh; } /* * Now continue the loop as necessary so the total number * of interations 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++); sival3.ivalue = ((FULL) *hd++) + ((FULL) sival2.silow); carry.ivalue = ((FULL) sival2.sihigh) + ((FULL) sival3.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++); sival3.ivalue = ((FULL) sival2.silow) + ((FULL) *hd) + ((FULL) carry.silow); carry.ivalue = ((FULL) sival2.sihigh) + ((FULL) sival3.sihigh) + ((FULL) carry.sihigh); hd[-1] = sival3.silow; hd++; } res->v[modlen - 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; hd = &res->v[len - 1]; while ((*hd == 0) && (len > 1)) { hd--; len--; } res->len = len; } /* * Compare the result with the modulus. * If it is less than the modulus, then the calculation is complete. */ if ((topdigit == 0) && ((len < modlen) || (res->v[len - 1] < rp->mod.v[len - 1]) || (zrel(*res, rp->mod) < 0))) 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++ = 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;}/* * Square a number in REDC format producing a result also in REDC format. */voidzredcsquare(rp, z1, res) REDC *rp; /* REDC information */ ZVALUE z1; /* REDC number to be squared */ ZVALUE *res; /* resulting REDC number */{ ZVALUE tmp; if (zisneg(z1)) math_error("Negative number in zredcsquare"); if (ziszero(z1)) { *res = _zero_; return; } if ((z1.len == rp->one.len) && (z1.v[0] == rp->one.v[0]) && (zcmp(z1, rp->one) == 0)) { zcopy(z1, res); 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 < _redc2_) { zredcmul(rp, z1, z1, res); return; } zsquare(z1, &tmp); zredcdecode(rp, tmp, res); zfree(tmp);}/* * 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. */voidzredcpower(rp, z1, z2, res) REDC *rp; /* REDC information */ ZVALUE z1; /* REDC number to be raised */ ZVALUE z2; /* normal number to raise number to */ ZVALUE *res; /* result */{ HALF *hp; /* pointer to current word of the power */ ZVALUE *pp; /* pointer to low power table */ ZVALUE ans, temp; /* calculation values */ 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 i; if (zisneg(z1)) math_error("Negative number in zredcpower"); if (zisneg(z2)) math_error("Negative power in zredcpower"); /* * Check for zero or the REDC format for one. */ if (ziszero(z1) || zisunit(rp->mod)) { *res = _zero_; return; } if (zcmp(z1, rp->one) == 0) { zcopy(rp->one, res); 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)) { *res = temp; return; } zfree(temp); zcopy(rp->one, res); 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]; *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++) { if (pp->len) freeh(pp->v); } *res = ans;}/* END CODE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -