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