xref: /plan9/sys/src/libmp/port/mpmul.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier #include "os.h"
2*7dd7cddfSDavid du Colombier #include <mp.h>
3*7dd7cddfSDavid du Colombier #include "dat.h"
4*7dd7cddfSDavid du Colombier 
5*7dd7cddfSDavid du Colombier //
6*7dd7cddfSDavid du Colombier //  from knuth's 1969 seminumberical algorithms, pp 233-235 and pp 258-260
7*7dd7cddfSDavid du Colombier //
8*7dd7cddfSDavid du Colombier //  mpvecmul is an assembly language routine that performs the inner
9*7dd7cddfSDavid du Colombier //  loop.
10*7dd7cddfSDavid du Colombier //
11*7dd7cddfSDavid du Colombier //  the karatsuba trade off is set empiricly by measuring the algs on
12*7dd7cddfSDavid du Colombier //  a 400 MHz Pentium II.
13*7dd7cddfSDavid du Colombier //
14*7dd7cddfSDavid du Colombier 
15*7dd7cddfSDavid du Colombier // karatsuba like (see knuth pg 258)
16*7dd7cddfSDavid du Colombier // prereq: p is already zeroed
17*7dd7cddfSDavid du Colombier static void
18*7dd7cddfSDavid du Colombier mpkaratsuba(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
19*7dd7cddfSDavid du Colombier {
20*7dd7cddfSDavid du Colombier 	mpdigit *t, *u0, *u1, *v0, *v1, *u0v0, *u1v1, *res, *diffprod;
21*7dd7cddfSDavid du Colombier 	int u0len, u1len, v0len, v1len, reslen;
22*7dd7cddfSDavid du Colombier 	int sign, n;
23*7dd7cddfSDavid du Colombier 
24*7dd7cddfSDavid du Colombier 	// divide each piece in half
25*7dd7cddfSDavid du Colombier 	n = alen/2;
26*7dd7cddfSDavid du Colombier 	if(alen&1)
27*7dd7cddfSDavid du Colombier 		n++;
28*7dd7cddfSDavid du Colombier 	u0len = n;
29*7dd7cddfSDavid du Colombier 	u1len = alen-n;
30*7dd7cddfSDavid du Colombier 	if(blen > n){
31*7dd7cddfSDavid du Colombier 		v0len = n;
32*7dd7cddfSDavid du Colombier 		v1len = blen-n;
33*7dd7cddfSDavid du Colombier 	} else {
34*7dd7cddfSDavid du Colombier 		v0len = blen;
35*7dd7cddfSDavid du Colombier 		v1len = 0;
36*7dd7cddfSDavid du Colombier 	}
37*7dd7cddfSDavid du Colombier 	u0 = a;
38*7dd7cddfSDavid du Colombier 	u1 = a + u0len;
39*7dd7cddfSDavid du Colombier 	v0 = b;
40*7dd7cddfSDavid du Colombier 	v1 = b + v0len;
41*7dd7cddfSDavid du Colombier 
42*7dd7cddfSDavid du Colombier 	// room for the partial products
43*7dd7cddfSDavid du Colombier 	t = mallocz(Dbytes*5*(2*n+1), 1);
44*7dd7cddfSDavid du Colombier 	if(t == nil)
45*7dd7cddfSDavid du Colombier 		sysfatal("mpkaratsuba");
46*7dd7cddfSDavid du Colombier 	u0v0 = t;
47*7dd7cddfSDavid du Colombier 	u1v1 = t + (2*n+1);
48*7dd7cddfSDavid du Colombier 	diffprod = t + 2*(2*n+1);
49*7dd7cddfSDavid du Colombier 	res = t + 3*(2*n+1);
50*7dd7cddfSDavid du Colombier 	reslen = 4*n+1;
51*7dd7cddfSDavid du Colombier 
52*7dd7cddfSDavid du Colombier 	// t[0] = (u1-u0)
53*7dd7cddfSDavid du Colombier 	sign = 1;
54*7dd7cddfSDavid du Colombier 	if(mpveccmp(u1, u1len, u0, u0len) < 0){
55*7dd7cddfSDavid du Colombier 		sign = -1;
56*7dd7cddfSDavid du Colombier 		mpvecsub(u0, u0len, u1, u1len, u0v0);
57*7dd7cddfSDavid du Colombier 	} else
58*7dd7cddfSDavid du Colombier 		mpvecsub(u1, u1len, u0, u1len, u0v0);
59*7dd7cddfSDavid du Colombier 
60*7dd7cddfSDavid du Colombier 	// t[1] = (v0-v1)
61*7dd7cddfSDavid du Colombier 	if(mpveccmp(v0, v0len, v1, v1len) < 0){
62*7dd7cddfSDavid du Colombier 		sign *= -1;
63*7dd7cddfSDavid du Colombier 		mpvecsub(v1, v1len, v0, v1len, u1v1);
64*7dd7cddfSDavid du Colombier 	} else
65*7dd7cddfSDavid du Colombier 		mpvecsub(v0, v0len, v1, v1len, u1v1);
66*7dd7cddfSDavid du Colombier 
67*7dd7cddfSDavid du Colombier 	// t[4:5] = (u1-u0)*(v0-v1)
68*7dd7cddfSDavid du Colombier 	mpvecmul(u0v0, u0len, u1v1, v0len, diffprod);
69*7dd7cddfSDavid du Colombier 
70*7dd7cddfSDavid du Colombier 	// t[0:1] = u1*v1
71*7dd7cddfSDavid du Colombier 	memset(t, 0, 2*(2*n+1)*Dbytes);
72*7dd7cddfSDavid du Colombier 	if(v1len > 0)
73*7dd7cddfSDavid du Colombier 		mpvecmul(u1, u1len, v1, v1len, u1v1);
74*7dd7cddfSDavid du Colombier 
75*7dd7cddfSDavid du Colombier 	// t[2:3] = u0v0
76*7dd7cddfSDavid du Colombier 	mpvecmul(u0, u0len, v0, v0len, u0v0);
77*7dd7cddfSDavid du Colombier 
78*7dd7cddfSDavid du Colombier 	// res = u0*v0<<n + u0*v0
79*7dd7cddfSDavid du Colombier 	mpvecadd(res, reslen, u0v0, u0len+v0len, res);
80*7dd7cddfSDavid du Colombier 	mpvecadd(res+n, reslen-n, u0v0, u0len+v0len, res+n);
81*7dd7cddfSDavid du Colombier 
82*7dd7cddfSDavid du Colombier 	// res += u1*v1<<n + u1*v1<<2*n
83*7dd7cddfSDavid du Colombier 	if(v1len > 0){
84*7dd7cddfSDavid du Colombier 		mpvecadd(res+n, reslen-n, u1v1, u1len+v1len, res+n);
85*7dd7cddfSDavid du Colombier 		mpvecadd(res+2*n, reslen-2*n, u1v1, u1len+v1len, res+2*n);
86*7dd7cddfSDavid du Colombier 	}
87*7dd7cddfSDavid du Colombier 
88*7dd7cddfSDavid du Colombier 	// res += (u1-u0)*(v0-v1)<<n
89*7dd7cddfSDavid du Colombier 	if(sign < 0)
90*7dd7cddfSDavid du Colombier 		mpvecsub(res+n, reslen-n, diffprod, u0len+v0len, res+n);
91*7dd7cddfSDavid du Colombier 	else
92*7dd7cddfSDavid du Colombier 		mpvecadd(res+n, reslen-n, diffprod, u0len+v0len, res+n);
93*7dd7cddfSDavid du Colombier 	memmove(p, res, (alen+blen)*Dbytes);
94*7dd7cddfSDavid du Colombier 
95*7dd7cddfSDavid du Colombier 	free(t);
96*7dd7cddfSDavid du Colombier }
97*7dd7cddfSDavid du Colombier 
98*7dd7cddfSDavid du Colombier #define KARATSUBAMIN 32
99*7dd7cddfSDavid du Colombier 
100*7dd7cddfSDavid du Colombier void
101*7dd7cddfSDavid du Colombier mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p)
102*7dd7cddfSDavid du Colombier {
103*7dd7cddfSDavid du Colombier 	int i;
104*7dd7cddfSDavid du Colombier 	mpdigit d;
105*7dd7cddfSDavid du Colombier 	mpdigit *t;
106*7dd7cddfSDavid du Colombier 
107*7dd7cddfSDavid du Colombier 	// both mpvecdigmuladd and karatsuba are fastest when a is the longer vector
108*7dd7cddfSDavid du Colombier 	if(alen < blen){
109*7dd7cddfSDavid du Colombier 		i = alen;
110*7dd7cddfSDavid du Colombier 		alen = blen;
111*7dd7cddfSDavid du Colombier 		blen = i;
112*7dd7cddfSDavid du Colombier 		t = a;
113*7dd7cddfSDavid du Colombier 		a = b;
114*7dd7cddfSDavid du Colombier 		b = t;
115*7dd7cddfSDavid du Colombier 	}
116*7dd7cddfSDavid du Colombier 	if(blen == 0){
117*7dd7cddfSDavid du Colombier 		memset(p, 0, Dbytes*(alen+blen));
118*7dd7cddfSDavid du Colombier 		return;
119*7dd7cddfSDavid du Colombier 	}
120*7dd7cddfSDavid du Colombier 
121*7dd7cddfSDavid du Colombier 	if(alen >= KARATSUBAMIN && blen > 1){
122*7dd7cddfSDavid du Colombier 		// O(n^1.585)
123*7dd7cddfSDavid du Colombier 		mpkaratsuba(a, alen, b, blen, p);
124*7dd7cddfSDavid du Colombier 	} else {
125*7dd7cddfSDavid du Colombier 		// O(n^2)
126*7dd7cddfSDavid du Colombier 		for(i = 0; i < blen; i++){
127*7dd7cddfSDavid du Colombier 			d = b[i];
128*7dd7cddfSDavid du Colombier 			if(d != 0)
129*7dd7cddfSDavid du Colombier 				mpvecdigmuladd(a, alen, d, &p[i]);
130*7dd7cddfSDavid du Colombier 		}
131*7dd7cddfSDavid du Colombier 	}
132*7dd7cddfSDavid du Colombier }
133*7dd7cddfSDavid du Colombier 
134*7dd7cddfSDavid du Colombier void
135*7dd7cddfSDavid du Colombier mpmul(mpint *b1, mpint *b2, mpint *prod)
136*7dd7cddfSDavid du Colombier {
137*7dd7cddfSDavid du Colombier 	mpint *oprod;
138*7dd7cddfSDavid du Colombier 
139*7dd7cddfSDavid du Colombier 	oprod = nil;
140*7dd7cddfSDavid du Colombier 	if(prod == b1 || prod == b2){
141*7dd7cddfSDavid du Colombier 		oprod = prod;
142*7dd7cddfSDavid du Colombier 		prod = mpnew(0);
143*7dd7cddfSDavid du Colombier 	}
144*7dd7cddfSDavid du Colombier 
145*7dd7cddfSDavid du Colombier 	prod->top = 0;
146*7dd7cddfSDavid du Colombier 	mpbits(prod, (b1->top+b2->top+1)*Dbits);
147*7dd7cddfSDavid du Colombier 	mpvecmul(b1->p, b1->top, b2->p, b2->top, prod->p);
148*7dd7cddfSDavid du Colombier 	prod->sign = b1->sign*b2->sign;
149*7dd7cddfSDavid du Colombier 	mpnorm(prod);
150*7dd7cddfSDavid du Colombier 
151*7dd7cddfSDavid du Colombier 	if(oprod != nil){
152*7dd7cddfSDavid du Colombier 		mpassign(prod, oprod);
153*7dd7cddfSDavid du Colombier 		mpfree(prod);
154*7dd7cddfSDavid du Colombier 	}
155*7dd7cddfSDavid du Colombier }
156