a
This commit is contained in:
156
libmp/mpmul.c
Normal file
156
libmp/mpmul.c
Normal file
@ -0,0 +1,156 @@
|
||||
#include "os.h"
|
||||
#include <mp.h>
|
||||
#include "dat.h"
|
||||
|
||||
//
|
||||
// from knuth's 1969 seminumberical algorithms, pp 233-235 and pp 258-260
|
||||
//
|
||||
// mpvecmul is an assembly language routine that performs the inner
|
||||
// loop.
|
||||
//
|
||||
// the karatsuba trade off is set empiricly by measuring the algs on
|
||||
// a 400 MHz Pentium II.
|
||||
//
|
||||
|
||||
// karatsuba like (see knuth pg 258)
|
||||
// prereq: p is already zeroed
|
||||
static void
|
||||
mpkaratsuba(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
|
||||
{
|
||||
mpdigit *t, *u0, *u1, *v0, *v1, *u0v0, *u1v1, *res, *diffprod;
|
||||
int u0len, u1len, v0len, v1len, reslen;
|
||||
int sign, n;
|
||||
|
||||
// divide each piece in half
|
||||
n = alen/2;
|
||||
if(alen&1)
|
||||
n++;
|
||||
u0len = n;
|
||||
u1len = alen-n;
|
||||
if(blen > n){
|
||||
v0len = n;
|
||||
v1len = blen-n;
|
||||
} else {
|
||||
v0len = blen;
|
||||
v1len = 0;
|
||||
}
|
||||
u0 = a;
|
||||
u1 = a + u0len;
|
||||
v0 = b;
|
||||
v1 = b + v0len;
|
||||
|
||||
// room for the partial products
|
||||
t = mallocz(Dbytes*5*(2*n+1), 1);
|
||||
if(t == nil)
|
||||
sysfatal("mpkaratsuba: %r");
|
||||
u0v0 = t;
|
||||
u1v1 = t + (2*n+1);
|
||||
diffprod = t + 2*(2*n+1);
|
||||
res = t + 3*(2*n+1);
|
||||
reslen = 4*n+1;
|
||||
|
||||
// t[0] = (u1-u0)
|
||||
sign = 1;
|
||||
if(mpveccmp(u1, u1len, u0, u0len) < 0){
|
||||
sign = -1;
|
||||
mpvecsub(u0, u0len, u1, u1len, u0v0);
|
||||
} else
|
||||
mpvecsub(u1, u1len, u0, u1len, u0v0);
|
||||
|
||||
// t[1] = (v0-v1)
|
||||
if(mpveccmp(v0, v0len, v1, v1len) < 0){
|
||||
sign *= -1;
|
||||
mpvecsub(v1, v1len, v0, v1len, u1v1);
|
||||
} else
|
||||
mpvecsub(v0, v0len, v1, v1len, u1v1);
|
||||
|
||||
// t[4:5] = (u1-u0)*(v0-v1)
|
||||
mpvecmul(u0v0, u0len, u1v1, v0len, diffprod);
|
||||
|
||||
// t[0:1] = u1*v1
|
||||
memset(t, 0, 2*(2*n+1)*Dbytes);
|
||||
if(v1len > 0)
|
||||
mpvecmul(u1, u1len, v1, v1len, u1v1);
|
||||
|
||||
// t[2:3] = u0v0
|
||||
mpvecmul(u0, u0len, v0, v0len, u0v0);
|
||||
|
||||
// res = u0*v0<<n + u0*v0
|
||||
mpvecadd(res, reslen, u0v0, u0len+v0len, res);
|
||||
mpvecadd(res+n, reslen-n, u0v0, u0len+v0len, res+n);
|
||||
|
||||
// res += u1*v1<<n + u1*v1<<2*n
|
||||
if(v1len > 0){
|
||||
mpvecadd(res+n, reslen-n, u1v1, u1len+v1len, res+n);
|
||||
mpvecadd(res+2*n, reslen-2*n, u1v1, u1len+v1len, res+2*n);
|
||||
}
|
||||
|
||||
// res += (u1-u0)*(v0-v1)<<n
|
||||
if(sign < 0)
|
||||
mpvecsub(res+n, reslen-n, diffprod, u0len+v0len, res+n);
|
||||
else
|
||||
mpvecadd(res+n, reslen-n, diffprod, u0len+v0len, res+n);
|
||||
memmove(p, res, (alen+blen)*Dbytes);
|
||||
|
||||
free(t);
|
||||
}
|
||||
|
||||
#define KARATSUBAMIN 32
|
||||
|
||||
void
|
||||
mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
|
||||
{
|
||||
int i;
|
||||
mpdigit d;
|
||||
mpdigit *t;
|
||||
|
||||
// both mpvecdigmuladd and karatsuba are fastest when a is the longer vector
|
||||
if(alen < blen){
|
||||
i = alen;
|
||||
alen = blen;
|
||||
blen = i;
|
||||
t = a;
|
||||
a = b;
|
||||
b = t;
|
||||
}
|
||||
if(blen == 0){
|
||||
memset(p, 0, Dbytes*(alen+blen));
|
||||
return;
|
||||
}
|
||||
|
||||
if(alen >= KARATSUBAMIN && blen > 1){
|
||||
// O(n^1.585)
|
||||
mpkaratsuba(a, alen, b, blen, p);
|
||||
} else {
|
||||
// O(n^2)
|
||||
for(i = 0; i < blen; i++){
|
||||
d = b[i];
|
||||
if(d != 0)
|
||||
mpvecdigmuladd(a, alen, d, &p[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mpmul(mpint *b1, mpint *b2, mpint *prod)
|
||||
{
|
||||
mpint *oprod;
|
||||
|
||||
oprod = nil;
|
||||
if(prod == b1 || prod == b2){
|
||||
oprod = prod;
|
||||
prod = mpnew(0);
|
||||
}
|
||||
|
||||
prod->top = 0;
|
||||
mpbits(prod, (b1->top+b2->top+1)*Dbits);
|
||||
mpvecmul(b1->p, b1->top, b2->p, b2->top, prod->p);
|
||||
prod->top = b1->top+b2->top+1;
|
||||
prod->sign = b1->sign*b2->sign;
|
||||
mpnorm(prod);
|
||||
|
||||
if(oprod != nil){
|
||||
mpassign(prod, oprod);
|
||||
mpfree(prod);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user