mirror of
https://github.com/0intro/conterm
synced 2024-11-22 13:51:34 +03:00
113 lines
2.4 KiB
C
113 lines
2.4 KiB
C
#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);
|
|
}
|