xref: /plan9/sys/src/libmp/port/mpmul.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
17dd7cddfSDavid du Colombier #include "os.h"
27dd7cddfSDavid du Colombier #include <mp.h>
37dd7cddfSDavid du Colombier #include "dat.h"
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier //
67dd7cddfSDavid du Colombier //  from knuth's 1969 seminumberical algorithms, pp 233-235 and pp 258-260
77dd7cddfSDavid du Colombier //
87dd7cddfSDavid du Colombier //  mpvecmul is an assembly language routine that performs the inner
97dd7cddfSDavid du Colombier //  loop.
107dd7cddfSDavid du Colombier //
117dd7cddfSDavid du Colombier //  the karatsuba trade off is set empiricly by measuring the algs on
127dd7cddfSDavid du Colombier //  a 400 MHz Pentium II.
137dd7cddfSDavid du Colombier //
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier // karatsuba like (see knuth pg 258)
167dd7cddfSDavid du Colombier // prereq: p is already zeroed
177dd7cddfSDavid du Colombier static void
mpkaratsuba(mpdigit * a,int alen,mpdigit * b,int blen,mpdigit * p)187dd7cddfSDavid du Colombier mpkaratsuba(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
197dd7cddfSDavid du Colombier {
207dd7cddfSDavid du Colombier 	mpdigit *t, *u0, *u1, *v0, *v1, *u0v0, *u1v1, *res, *diffprod;
217dd7cddfSDavid du Colombier 	int u0len, u1len, v0len, v1len, reslen;
227dd7cddfSDavid du Colombier 	int sign, n;
237dd7cddfSDavid du Colombier 
247dd7cddfSDavid du Colombier 	// divide each piece in half
257dd7cddfSDavid du Colombier 	n = alen/2;
267dd7cddfSDavid du Colombier 	if(alen&1)
277dd7cddfSDavid du Colombier 		n++;
287dd7cddfSDavid du Colombier 	u0len = n;
297dd7cddfSDavid du Colombier 	u1len = alen-n;
307dd7cddfSDavid du Colombier 	if(blen > n){
317dd7cddfSDavid du Colombier 		v0len = n;
327dd7cddfSDavid du Colombier 		v1len = blen-n;
337dd7cddfSDavid du Colombier 	} else {
347dd7cddfSDavid du Colombier 		v0len = blen;
357dd7cddfSDavid du Colombier 		v1len = 0;
367dd7cddfSDavid du Colombier 	}
377dd7cddfSDavid du Colombier 	u0 = a;
387dd7cddfSDavid du Colombier 	u1 = a + u0len;
397dd7cddfSDavid du Colombier 	v0 = b;
407dd7cddfSDavid du Colombier 	v1 = b + v0len;
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier 	// room for the partial products
437dd7cddfSDavid du Colombier 	t = mallocz(Dbytes*5*(2*n+1), 1);
447dd7cddfSDavid du Colombier 	if(t == nil)
45*9a747e4fSDavid du Colombier 		sysfatal("mpkaratsuba: %r");
467dd7cddfSDavid du Colombier 	u0v0 = t;
477dd7cddfSDavid du Colombier 	u1v1 = t + (2*n+1);
487dd7cddfSDavid du Colombier 	diffprod = t + 2*(2*n+1);
497dd7cddfSDavid du Colombier 	res = t + 3*(2*n+1);
507dd7cddfSDavid du Colombier 	reslen = 4*n+1;
517dd7cddfSDavid du Colombier 
527dd7cddfSDavid du Colombier 	// t[0] = (u1-u0)
537dd7cddfSDavid du Colombier 	sign = 1;
547dd7cddfSDavid du Colombier 	if(mpveccmp(u1, u1len, u0, u0len) < 0){
557dd7cddfSDavid du Colombier 		sign = -1;
567dd7cddfSDavid du Colombier 		mpvecsub(u0, u0len, u1, u1len, u0v0);
577dd7cddfSDavid du Colombier 	} else
587dd7cddfSDavid du Colombier 		mpvecsub(u1, u1len, u0, u1len, u0v0);
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier 	// t[1] = (v0-v1)
617dd7cddfSDavid du Colombier 	if(mpveccmp(v0, v0len, v1, v1len) < 0){
627dd7cddfSDavid du Colombier 		sign *= -1;
637dd7cddfSDavid du Colombier 		mpvecsub(v1, v1len, v0, v1len, u1v1);
647dd7cddfSDavid du Colombier 	} else
657dd7cddfSDavid du Colombier 		mpvecsub(v0, v0len, v1, v1len, u1v1);
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier 	// t[4:5] = (u1-u0)*(v0-v1)
687dd7cddfSDavid du Colombier 	mpvecmul(u0v0, u0len, u1v1, v0len, diffprod);
697dd7cddfSDavid du Colombier 
707dd7cddfSDavid du Colombier 	// t[0:1] = u1*v1
717dd7cddfSDavid du Colombier 	memset(t, 0, 2*(2*n+1)*Dbytes);
727dd7cddfSDavid du Colombier 	if(v1len > 0)
737dd7cddfSDavid du Colombier 		mpvecmul(u1, u1len, v1, v1len, u1v1);
747dd7cddfSDavid du Colombier 
757dd7cddfSDavid du Colombier 	// t[2:3] = u0v0
767dd7cddfSDavid du Colombier 	mpvecmul(u0, u0len, v0, v0len, u0v0);
777dd7cddfSDavid du Colombier 
787dd7cddfSDavid du Colombier 	// res = u0*v0<<n + u0*v0
797dd7cddfSDavid du Colombier 	mpvecadd(res, reslen, u0v0, u0len+v0len, res);
807dd7cddfSDavid du Colombier 	mpvecadd(res+n, reslen-n, u0v0, u0len+v0len, res+n);
817dd7cddfSDavid du Colombier 
827dd7cddfSDavid du Colombier 	// res += u1*v1<<n + u1*v1<<2*n
837dd7cddfSDavid du Colombier 	if(v1len > 0){
847dd7cddfSDavid du Colombier 		mpvecadd(res+n, reslen-n, u1v1, u1len+v1len, res+n);
857dd7cddfSDavid du Colombier 		mpvecadd(res+2*n, reslen-2*n, u1v1, u1len+v1len, res+2*n);
867dd7cddfSDavid du Colombier 	}
877dd7cddfSDavid du Colombier 
887dd7cddfSDavid du Colombier 	// res += (u1-u0)*(v0-v1)<<n
897dd7cddfSDavid du Colombier 	if(sign < 0)
907dd7cddfSDavid du Colombier 		mpvecsub(res+n, reslen-n, diffprod, u0len+v0len, res+n);
917dd7cddfSDavid du Colombier 	else
927dd7cddfSDavid du Colombier 		mpvecadd(res+n, reslen-n, diffprod, u0len+v0len, res+n);
937dd7cddfSDavid du Colombier 	memmove(p, res, (alen+blen)*Dbytes);
947dd7cddfSDavid du Colombier 
957dd7cddfSDavid du Colombier 	free(t);
967dd7cddfSDavid du Colombier }
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier #define KARATSUBAMIN 32
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier void
mpvecmul(mpdigit * a,int alen,mpdigit * b,int blen,mpdigit * p)1017dd7cddfSDavid du Colombier mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
1027dd7cddfSDavid du Colombier {
1037dd7cddfSDavid du Colombier 	int i;
1047dd7cddfSDavid du Colombier 	mpdigit d;
1057dd7cddfSDavid du Colombier 	mpdigit *t;
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier 	// both mpvecdigmuladd and karatsuba are fastest when a is the longer vector
1087dd7cddfSDavid du Colombier 	if(alen < blen){
1097dd7cddfSDavid du Colombier 		i = alen;
1107dd7cddfSDavid du Colombier 		alen = blen;
1117dd7cddfSDavid du Colombier 		blen = i;
1127dd7cddfSDavid du Colombier 		t = a;
1137dd7cddfSDavid du Colombier 		a = b;
1147dd7cddfSDavid du Colombier 		b = t;
1157dd7cddfSDavid du Colombier 	}
1167dd7cddfSDavid du Colombier 	if(blen == 0){
1177dd7cddfSDavid du Colombier 		memset(p, 0, Dbytes*(alen+blen));
1187dd7cddfSDavid du Colombier 		return;
1197dd7cddfSDavid du Colombier 	}
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier 	if(alen >= KARATSUBAMIN && blen > 1){
1227dd7cddfSDavid du Colombier 		// O(n^1.585)
1237dd7cddfSDavid du Colombier 		mpkaratsuba(a, alen, b, blen, p);
1247dd7cddfSDavid du Colombier 	} else {
1257dd7cddfSDavid du Colombier 		// O(n^2)
1267dd7cddfSDavid du Colombier 		for(i = 0; i < blen; i++){
1277dd7cddfSDavid du Colombier 			d = b[i];
1287dd7cddfSDavid du Colombier 			if(d != 0)
1297dd7cddfSDavid du Colombier 				mpvecdigmuladd(a, alen, d, &p[i]);
1307dd7cddfSDavid du Colombier 		}
1317dd7cddfSDavid du Colombier 	}
1327dd7cddfSDavid du Colombier }
1337dd7cddfSDavid du Colombier 
1347dd7cddfSDavid du Colombier void
mpmul(mpint * b1,mpint * b2,mpint * prod)1357dd7cddfSDavid du Colombier mpmul(mpint *b1, mpint *b2, mpint *prod)
1367dd7cddfSDavid du Colombier {
1377dd7cddfSDavid du Colombier 	mpint *oprod;
1387dd7cddfSDavid du Colombier 
1397dd7cddfSDavid du Colombier 	oprod = nil;
1407dd7cddfSDavid du Colombier 	if(prod == b1 || prod == b2){
1417dd7cddfSDavid du Colombier 		oprod = prod;
1427dd7cddfSDavid du Colombier 		prod = mpnew(0);
1437dd7cddfSDavid du Colombier 	}
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier 	prod->top = 0;
1467dd7cddfSDavid du Colombier 	mpbits(prod, (b1->top+b2->top+1)*Dbits);
1477dd7cddfSDavid du Colombier 	mpvecmul(b1->p, b1->top, b2->p, b2->top, prod->p);
14859cc4ca5SDavid du Colombier 	prod->top = b1->top+b2->top+1;
1497dd7cddfSDavid du Colombier 	prod->sign = b1->sign*b2->sign;
1507dd7cddfSDavid du Colombier 	mpnorm(prod);
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	if(oprod != nil){
1537dd7cddfSDavid du Colombier 		mpassign(prod, oprod);
1547dd7cddfSDavid du Colombier 		mpfree(prod);
1557dd7cddfSDavid du Colombier 	}
1567dd7cddfSDavid du Colombier }
157