tommath/bn_fast_s_mp_sqr.c

134 lines
3.9 KiB
C
Raw Normal View History

2003-02-28 11:08:34 -05:00
/* LibTomMath, multiple-precision integer library -- Tom St Denis
*
* LibTomMath is library that provides for multiple-precision
* integer arithmetic as well as number theoretic functionality.
*
* The library is designed directly after the MPI library by
* Michael Fromberger but has been written from scratch with
* additional optimizations in place.
*
* The library is free for all purposes without any express
* guarantee it works.
*
2003-03-12 21:11:11 -05:00
* Tom St Denis, tomstdenis@iahu.ca, http://math.libtomcrypt.org
2003-02-28 11:08:34 -05:00
*/
#include <tommath.h>
/* fast squaring
*
* This is the comba method where the columns of the product are computed first
* then the carries are computed. This has the effect of making a very simple
* inner loop that is executed the most
*
2003-05-17 08:33:54 -04:00
* W2 represents the outer products and W the inner.
2003-02-28 11:08:34 -05:00
*
* A further optimizations is made because the inner products are of the form
* "A * B * 2". The *2 part does not need to be computed until the end which is
* good because 64-bit shifts are slow!
*
2003-02-28 11:09:08 -05:00
* Based on Algorithm 14.16 on pp.597 of HAC.
2003-02-28 11:08:34 -05:00
*
*/
int
fast_s_mp_sqr (mp_int * a, mp_int * b)
{
2003-02-28 11:09:08 -05:00
int olduse, newused, res, ix, pa;
2003-05-17 08:33:54 -04:00
mp_word W2[MP_WARRAY], W[MP_WARRAY];
2003-02-28 11:08:34 -05:00
2003-02-28 11:09:08 -05:00
/* calculate size of product and allocate as required */
2003-02-28 11:08:34 -05:00
pa = a->used;
newused = pa + pa + 1;
if (b->alloc < newused) {
if ((res = mp_grow (b, newused)) != MP_OKAY) {
return res;
}
}
2003-05-17 08:33:54 -04:00
/* zero temp buffer (columns)
2003-02-28 11:08:34 -05:00
* Note that there are two buffers. Since squaring requires
2003-05-17 08:33:54 -04:00
* a outter and inner product and the inner product requires
2003-02-28 11:08:34 -05:00
* computing a product and doubling it (a relatively expensive
* op to perform n^2 times if you don't have to) the inner and
* outer products are computed in different buffers. This way
* the inner product can be doubled using n doublings instead of
* n^2
*/
2003-02-28 11:09:08 -05:00
memset (W, 0, newused * sizeof (mp_word));
2003-02-28 11:08:34 -05:00
memset (W2, 0, newused * sizeof (mp_word));
2003-02-28 11:09:08 -05:00
/* note optimization
* values in W2 are only written in even locations which means
* we can collapse the array to 256 words [and fixup the memset above]
* provided we also fix up the summations below. Ideally
2003-05-17 08:33:54 -04:00
* the fixup loop should be unrolled twice to handle the even/odd
2003-02-28 11:09:08 -05:00
* cases, and then a final step to handle odd cases [e.g. newused == odd]
*
* This will not only save ~8*256 = 2KB of stack but lower the number of
* operations required to finally fix up the columns
*/
2003-02-28 11:08:34 -05:00
/* This computes the inner product. To simplify the inner N^2 loop
* the multiplication by two is done afterwards in the N loop.
*/
for (ix = 0; ix < pa; ix++) {
2003-05-17 08:33:54 -04:00
/* compute the outer product
2003-02-28 11:09:08 -05:00
*
2003-05-17 08:33:54 -04:00
* Note that every outer product is computed
* for a particular column only once which means that
2003-02-28 11:09:08 -05:00
* there is no need todo a double precision addition
*/
W2[ix + ix] = ((mp_word) a->dp[ix]) * ((mp_word) a->dp[ix]);
2003-02-28 11:08:34 -05:00
{
register mp_digit tmpx, *tmpy;
register mp_word *_W;
register int iy;
/* copy of left side */
tmpx = a->dp[ix];
/* alias for right side */
tmpy = a->dp + (ix + 1);
/* the column to store the result in */
_W = W + (ix + ix + 1);
/* inner products */
for (iy = ix + 1; iy < pa; iy++) {
2003-05-17 08:33:54 -04:00
*_W++ += ((mp_word) tmpx) * ((mp_word) * tmpy++);
2003-02-28 11:08:34 -05:00
}
}
}
/* setup dest */
olduse = b->used;
b->used = newused;
/* double first value, since the inner products are half of what they should be */
W[0] += W[0] + W2[0];
/* now compute digits */
2003-02-28 11:09:08 -05:00
{
register mp_digit *tmpb;
2003-02-28 11:08:34 -05:00
2003-02-28 11:09:08 -05:00
tmpb = b->dp;
2003-02-28 11:08:34 -05:00
2003-02-28 11:09:08 -05:00
for (ix = 1; ix < newused; ix++) {
/* double/add next digit */
W[ix] += W[ix] + W2[ix];
2003-02-28 11:08:34 -05:00
2003-02-28 11:09:08 -05:00
W[ix] = W[ix] + (W[ix - 1] >> ((mp_word) DIGIT_BIT));
*tmpb++ = (mp_digit) (W[ix - 1] & ((mp_word) MP_MASK));
}
*tmpb++ = (mp_digit) (W[(newused) - 1] & ((mp_word) MP_MASK));
/* clear high */
for (; ix < olduse; ix++) {
*tmpb++ = 0;
}
}
2003-02-28 11:08:34 -05:00
mp_clamp (b);
return MP_OKAY;
}