xref: /plan9/sys/src/libmp/port/mpdiv.c (revision 59cc4ca53493a3c6d2349fe2b7f7c40f7dce7294)
17dd7cddfSDavid du Colombier #include "os.h"
27dd7cddfSDavid du Colombier #include <mp.h>
37dd7cddfSDavid du Colombier #include "dat.h"
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier // division ala knuth, seminumerical algorithms, pp 237-238
67dd7cddfSDavid du Colombier // the numbers are stored backwards to what knuth expects so j
77dd7cddfSDavid du Colombier // counts down rather than up.
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier void
mpdiv(mpint * dividend,mpint * divisor,mpint * quotient,mpint * remainder)107dd7cddfSDavid du Colombier mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder)
117dd7cddfSDavid du Colombier {
127dd7cddfSDavid du Colombier 	int j, s, vn, sign;
137dd7cddfSDavid du Colombier 	mpdigit qd, *up, *vp, *qp;
147dd7cddfSDavid du Colombier 	mpint *u, *v, *t;
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier 	// divide bv zero
177dd7cddfSDavid du Colombier 	if(divisor->top == 0)
187dd7cddfSDavid du Colombier 		abort();
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier 	// quick check
217dd7cddfSDavid du Colombier 	if(mpmagcmp(dividend, divisor) < 0){
227dd7cddfSDavid du Colombier 		if(remainder != nil)
237dd7cddfSDavid du Colombier 			mpassign(dividend, remainder);
247dd7cddfSDavid du Colombier 		if(quotient != nil)
257dd7cddfSDavid du Colombier 			mpassign(mpzero, quotient);
267dd7cddfSDavid du Colombier 		return;
277dd7cddfSDavid du Colombier 	}
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier 	// D1: shift until divisor, v, has hi bit set (needed to make trial
307dd7cddfSDavid du Colombier 	//     divisor accurate)
317dd7cddfSDavid du Colombier 	qd = divisor->p[divisor->top-1];
327dd7cddfSDavid du Colombier 	for(s = 0; (qd & mpdighi) == 0; s++)
337dd7cddfSDavid du Colombier 		qd <<= 1;
347dd7cddfSDavid du Colombier 	u = mpnew((dividend->top+2)*Dbits + s);
357dd7cddfSDavid du Colombier 	if(s == 0 && divisor != quotient && divisor != remainder) {
367dd7cddfSDavid du Colombier 		mpassign(dividend, u);
377dd7cddfSDavid du Colombier 		v = divisor;
387dd7cddfSDavid du Colombier 	} else {
397dd7cddfSDavid du Colombier 		mpleft(dividend, s, u);
407dd7cddfSDavid du Colombier 		v = mpnew(divisor->top*Dbits);
417dd7cddfSDavid du Colombier 		mpleft(divisor, s, v);
427dd7cddfSDavid du Colombier 	}
437dd7cddfSDavid du Colombier 	up = u->p+u->top-1;
447dd7cddfSDavid du Colombier 	vp = v->p+v->top-1;
457dd7cddfSDavid du Colombier 	vn = v->top;
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier 	// D1a: make sure high digit of dividend is less than high digit of divisor
487dd7cddfSDavid du Colombier 	if(*up >= *vp){
497dd7cddfSDavid du Colombier 		*++up = 0;
507dd7cddfSDavid du Colombier 		u->top++;
517dd7cddfSDavid du Colombier 	}
527dd7cddfSDavid du Colombier 
537dd7cddfSDavid du Colombier 	// storage for multiplies
547dd7cddfSDavid du Colombier 	t = mpnew(4*Dbits);
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier 	qp = nil;
577dd7cddfSDavid du Colombier 	if(quotient != nil){
587dd7cddfSDavid du Colombier 		mpbits(quotient, (u->top - v->top)*Dbits);
59*59cc4ca5SDavid du Colombier 		quotient->top = u->top - v->top;
607dd7cddfSDavid du Colombier 		qp = quotient->p+quotient->top-1;
617dd7cddfSDavid du Colombier 	}
627dd7cddfSDavid du Colombier 
637dd7cddfSDavid du Colombier 	// D2, D7: loop on length of dividend
647dd7cddfSDavid du Colombier 	for(j = u->top; j > vn; j--){
657dd7cddfSDavid du Colombier 
667dd7cddfSDavid du Colombier 		// D3: calculate trial divisor
677dd7cddfSDavid du Colombier 		mpdigdiv(up-1, *vp, &qd);
687dd7cddfSDavid du Colombier 
697dd7cddfSDavid du Colombier 		// D3a: rule out trial divisors 2 greater than real divisor
707dd7cddfSDavid du Colombier 		if(vn > 1) for(;;){
717dd7cddfSDavid du Colombier 			memset(t->p, 0, 3*Dbytes);	// mpvecdigmuladd adds to what's there
727dd7cddfSDavid du Colombier 			mpvecdigmuladd(vp-1, 2, qd, t->p);
737dd7cddfSDavid du Colombier 			if(mpveccmp(t->p, 3, up-2, 3) > 0)
747dd7cddfSDavid du Colombier 				qd--;
757dd7cddfSDavid du Colombier 			else
767dd7cddfSDavid du Colombier 				break;
777dd7cddfSDavid du Colombier 		}
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier 		// D4: u -= v*qd << j*Dbits
807dd7cddfSDavid du Colombier 		sign = mpvecdigmulsub(v->p, vn, qd, up-vn);
817dd7cddfSDavid du Colombier 		if(sign < 0){
827dd7cddfSDavid du Colombier 
837dd7cddfSDavid du Colombier 			// D6: trial divisor was too high, add back borrowed
847dd7cddfSDavid du Colombier 			//     value and decrease divisor
857dd7cddfSDavid du Colombier 			mpvecadd(up-vn, vn+1, v->p, vn, up-vn);
867dd7cddfSDavid du Colombier 			qd--;
877dd7cddfSDavid du Colombier 		}
887dd7cddfSDavid du Colombier 
897dd7cddfSDavid du Colombier 		// D5: save quotient digit
907dd7cddfSDavid du Colombier 		if(qp != nil)
917dd7cddfSDavid du Colombier 			*qp-- = qd;
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 		// push top of u down one
947dd7cddfSDavid du Colombier 		u->top--;
957dd7cddfSDavid du Colombier 		*up-- = 0;
967dd7cddfSDavid du Colombier 	}
977dd7cddfSDavid du Colombier 	if(qp != nil){
987dd7cddfSDavid du Colombier 		mpnorm(quotient);
997dd7cddfSDavid du Colombier 		if(dividend->sign != divisor->sign)
1007dd7cddfSDavid du Colombier 			quotient->sign = -1;
1017dd7cddfSDavid du Colombier 	}
1027dd7cddfSDavid du Colombier 
1037dd7cddfSDavid du Colombier 	if(remainder != nil){
1047dd7cddfSDavid du Colombier 		mpright(u, s, remainder);	// u is the remainder shifted
1057dd7cddfSDavid du Colombier 		remainder->sign = dividend->sign;
1067dd7cddfSDavid du Colombier 	}
1077dd7cddfSDavid du Colombier 
1087dd7cddfSDavid du Colombier 	mpfree(t);
1097dd7cddfSDavid du Colombier 	mpfree(u);
1107dd7cddfSDavid du Colombier 	if(v != divisor)
1117dd7cddfSDavid du Colombier 		mpfree(v);
1127dd7cddfSDavid du Colombier }
113