xref: /inferno-os/libmp/port/mpdiv.c (revision d9bd3181330830c49e714609e86eaa3e39a884ca)
137da2899SCharles.Forsyth #include "os.h"
237da2899SCharles.Forsyth #include <mp.h>
337da2899SCharles.Forsyth #include "dat.h"
437da2899SCharles.Forsyth 
537da2899SCharles.Forsyth // division ala knuth, seminumerical algorithms, pp 237-238
637da2899SCharles.Forsyth // the numbers are stored backwards to what knuth expects so j
737da2899SCharles.Forsyth // counts down rather than up.
837da2899SCharles.Forsyth 
937da2899SCharles.Forsyth void
mpdiv(mpint * dividend,mpint * divisor,mpint * quotient,mpint * remainder)1037da2899SCharles.Forsyth mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder)
1137da2899SCharles.Forsyth {
1237da2899SCharles.Forsyth 	int j, s, vn, sign;
1337da2899SCharles.Forsyth 	mpdigit qd, *up, *vp, *qp;
1437da2899SCharles.Forsyth 	mpint *u, *v, *t;
1537da2899SCharles.Forsyth 
1637da2899SCharles.Forsyth 	// divide bv zero
1737da2899SCharles.Forsyth 	if(divisor->top == 0)
18*d9bd3181SCharles.Forsyth 		sysfatal("mpdiv: divide by zero");
1937da2899SCharles.Forsyth 
2037da2899SCharles.Forsyth 	// quick check
2137da2899SCharles.Forsyth 	if(mpmagcmp(dividend, divisor) < 0){
2237da2899SCharles.Forsyth 		if(remainder != nil)
2337da2899SCharles.Forsyth 			mpassign(dividend, remainder);
2437da2899SCharles.Forsyth 		if(quotient != nil)
2537da2899SCharles.Forsyth 			mpassign(mpzero, quotient);
2637da2899SCharles.Forsyth 		return;
2737da2899SCharles.Forsyth 	}
2837da2899SCharles.Forsyth 
2937da2899SCharles.Forsyth 	// D1: shift until divisor, v, has hi bit set (needed to make trial
3037da2899SCharles.Forsyth 	//     divisor accurate)
3137da2899SCharles.Forsyth 	qd = divisor->p[divisor->top-1];
3237da2899SCharles.Forsyth 	for(s = 0; (qd & mpdighi) == 0; s++)
3337da2899SCharles.Forsyth 		qd <<= 1;
3437da2899SCharles.Forsyth 	u = mpnew((dividend->top+2)*Dbits + s);
3537da2899SCharles.Forsyth 	if(s == 0 && divisor != quotient && divisor != remainder) {
3637da2899SCharles.Forsyth 		mpassign(dividend, u);
3737da2899SCharles.Forsyth 		v = divisor;
3837da2899SCharles.Forsyth 	} else {
3937da2899SCharles.Forsyth 		mpleft(dividend, s, u);
4037da2899SCharles.Forsyth 		v = mpnew(divisor->top*Dbits);
4137da2899SCharles.Forsyth 		mpleft(divisor, s, v);
4237da2899SCharles.Forsyth 	}
4337da2899SCharles.Forsyth 	up = u->p+u->top-1;
4437da2899SCharles.Forsyth 	vp = v->p+v->top-1;
4537da2899SCharles.Forsyth 	vn = v->top;
4637da2899SCharles.Forsyth 
4737da2899SCharles.Forsyth 	// D1a: make sure high digit of dividend is less than high digit of divisor
4837da2899SCharles.Forsyth 	if(*up >= *vp){
4937da2899SCharles.Forsyth 		*++up = 0;
5037da2899SCharles.Forsyth 		u->top++;
5137da2899SCharles.Forsyth 	}
5237da2899SCharles.Forsyth 
5337da2899SCharles.Forsyth 	// storage for multiplies
5437da2899SCharles.Forsyth 	t = mpnew(4*Dbits);
5537da2899SCharles.Forsyth 
5637da2899SCharles.Forsyth 	qp = nil;
5737da2899SCharles.Forsyth 	if(quotient != nil){
5837da2899SCharles.Forsyth 		mpbits(quotient, (u->top - v->top)*Dbits);
5937da2899SCharles.Forsyth 		quotient->top = u->top - v->top;
6037da2899SCharles.Forsyth 		qp = quotient->p+quotient->top-1;
6137da2899SCharles.Forsyth 	}
6237da2899SCharles.Forsyth 
6337da2899SCharles.Forsyth 	// D2, D7: loop on length of dividend
6437da2899SCharles.Forsyth 	for(j = u->top; j > vn; j--){
6537da2899SCharles.Forsyth 
6637da2899SCharles.Forsyth 		// D3: calculate trial divisor
6737da2899SCharles.Forsyth 		mpdigdiv(up-1, *vp, &qd);
6837da2899SCharles.Forsyth 
6937da2899SCharles.Forsyth 		// D3a: rule out trial divisors 2 greater than real divisor
7037da2899SCharles.Forsyth 		if(vn > 1) for(;;){
7137da2899SCharles.Forsyth 			memset(t->p, 0, 3*Dbytes);	// mpvecdigmuladd adds to what's there
7237da2899SCharles.Forsyth 			mpvecdigmuladd(vp-1, 2, qd, t->p);
7337da2899SCharles.Forsyth 			if(mpveccmp(t->p, 3, up-2, 3) > 0)
7437da2899SCharles.Forsyth 				qd--;
7537da2899SCharles.Forsyth 			else
7637da2899SCharles.Forsyth 				break;
7737da2899SCharles.Forsyth 		}
7837da2899SCharles.Forsyth 
7937da2899SCharles.Forsyth 		// D4: u -= v*qd << j*Dbits
8037da2899SCharles.Forsyth 		sign = mpvecdigmulsub(v->p, vn, qd, up-vn);
8137da2899SCharles.Forsyth 		if(sign < 0){
8237da2899SCharles.Forsyth 
8337da2899SCharles.Forsyth 			// D6: trial divisor was too high, add back borrowed
8437da2899SCharles.Forsyth 			//     value and decrease divisor
8537da2899SCharles.Forsyth 			mpvecadd(up-vn, vn+1, v->p, vn, up-vn);
8637da2899SCharles.Forsyth 			qd--;
8737da2899SCharles.Forsyth 		}
8837da2899SCharles.Forsyth 
8937da2899SCharles.Forsyth 		// D5: save quotient digit
9037da2899SCharles.Forsyth 		if(qp != nil)
9137da2899SCharles.Forsyth 			*qp-- = qd;
9237da2899SCharles.Forsyth 
9337da2899SCharles.Forsyth 		// push top of u down one
9437da2899SCharles.Forsyth 		u->top--;
9537da2899SCharles.Forsyth 		*up-- = 0;
9637da2899SCharles.Forsyth 	}
9737da2899SCharles.Forsyth 	if(qp != nil){
9837da2899SCharles.Forsyth 		mpnorm(quotient);
9937da2899SCharles.Forsyth 		if(dividend->sign != divisor->sign)
10037da2899SCharles.Forsyth 			quotient->sign = -1;
10137da2899SCharles.Forsyth 	}
10237da2899SCharles.Forsyth 
10337da2899SCharles.Forsyth 	if(remainder != nil){
10437da2899SCharles.Forsyth 		mpright(u, s, remainder);	// u is the remainder shifted
10537da2899SCharles.Forsyth 		remainder->sign = dividend->sign;
10637da2899SCharles.Forsyth 	}
10737da2899SCharles.Forsyth 
10837da2899SCharles.Forsyth 	mpfree(t);
10937da2899SCharles.Forsyth 	mpfree(u);
11037da2899SCharles.Forsyth 	if(v != divisor)
11137da2899SCharles.Forsyth 		mpfree(v);
11237da2899SCharles.Forsyth }
113