| #include "os.h" |
| #include <mp.h> |
| #include "dat.h" |
| |
| // division ala knuth, seminumerical algorithms, pp 237-238 |
| // the numbers are stored backwards to what knuth expects so j |
| // counts down rather than up. |
| |
| void |
| mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder) |
| { |
| int j, s, vn, sign; |
| mpdigit qd, *up, *vp, *qp; |
| mpint *u, *v, *t; |
| |
| // divide bv zero |
| if(divisor->top == 0) |
| abort(); |
| |
| // quick check |
| if(mpmagcmp(dividend, divisor) < 0){ |
| if(remainder != nil) |
| mpassign(dividend, remainder); |
| if(quotient != nil) |
| mpassign(mpzero, quotient); |
| return; |
| } |
| |
| // D1: shift until divisor, v, has hi bit set (needed to make trial |
| // divisor accurate) |
| qd = divisor->p[divisor->top-1]; |
| for(s = 0; (qd & mpdighi) == 0; s++) |
| qd <<= 1; |
| u = mpnew((dividend->top+2)*Dbits + s); |
| if(s == 0 && divisor != quotient && divisor != remainder) { |
| mpassign(dividend, u); |
| v = divisor; |
| } else { |
| mpleft(dividend, s, u); |
| v = mpnew(divisor->top*Dbits); |
| mpleft(divisor, s, v); |
| } |
| up = u->p+u->top-1; |
| vp = v->p+v->top-1; |
| vn = v->top; |
| |
| // D1a: make sure high digit of dividend is less than high digit of divisor |
| if(*up >= *vp){ |
| *++up = 0; |
| u->top++; |
| } |
| |
| // storage for multiplies |
| t = mpnew(4*Dbits); |
| |
| qp = nil; |
| if(quotient != nil){ |
| mpbits(quotient, (u->top - v->top)*Dbits); |
| quotient->top = u->top - v->top; |
| qp = quotient->p+quotient->top-1; |
| } |
| |
| // D2, D7: loop on length of dividend |
| for(j = u->top; j > vn; j--){ |
| |
| // D3: calculate trial divisor |
| mpdigdiv(up-1, *vp, &qd); |
| |
| // D3a: rule out trial divisors 2 greater than real divisor |
| if(vn > 1) for(;;){ |
| memset(t->p, 0, 3*Dbytes); // mpvecdigmuladd adds to what's there |
| mpvecdigmuladd(vp-1, 2, qd, t->p); |
| if(mpveccmp(t->p, 3, up-2, 3) > 0) |
| qd--; |
| else |
| break; |
| } |
| |
| // D4: u -= v*qd << j*Dbits |
| sign = mpvecdigmulsub(v->p, vn, qd, up-vn); |
| if(sign < 0){ |
| |
| // D6: trial divisor was too high, add back borrowed |
| // value and decrease divisor |
| mpvecadd(up-vn, vn+1, v->p, vn, up-vn); |
| qd--; |
| } |
| |
| // D5: save quotient digit |
| if(qp != nil) |
| *qp-- = qd; |
| |
| // push top of u down one |
| u->top--; |
| *up-- = 0; |
| } |
| if(qp != nil){ |
| mpnorm(quotient); |
| if(dividend->sign != divisor->sign) |
| quotient->sign = -1; |
| } |
| |
| if(remainder != nil){ |
| mpright(u, s, remainder); // u is the remainder shifted |
| remainder->sign = dividend->sign; |
| } |
| |
| mpfree(t); |
| mpfree(u); |
| if(v != divisor) |
| mpfree(v); |
| } |