| #include "pdefs.h" |
| #include "precision.h" |
| |
| #ifdef DEBUG |
| #include <stdio.h> |
| #endif |
| |
| #ifdef ASM_16BIT |
| #include "asm16bit.h" |
| #endif |
| |
| /* |
| * Divide u (dividend) by v (divisor); If non-null, qp and rp are set to |
| * quotient and remainder. The result returned will be *qp, unless qp is |
| * NULL, then *rp will be returned if non-null, otherwise pUndef is returned. |
| * |
| * Produce: |
| * |
| * q (quotient) = u div v (v != 0) |
| * truncation is toward zero |
| * |
| * r (remainder) = u mod v |
| * = u - u div v * v (v != 0) |
| * = u (v == 0) |
| * ( e.g. u == q*v + r ) |
| * remainder has same sign and dividend |
| * |
| * Note: this has opposite convention than the C standard div fuction, |
| * but the same convention of the typical C "/" operator |
| * It is also inconvienient for the mod function. |
| */ |
| /* |
| * This algorithm is taken almost verbatum from Knuth Vol 2. |
| * Please note the following trivial(?) array index |
| * transformations (since MSD to LSD order is reversed): |
| * |
| * q[0..m] to Q[0..m] thus q[i] == Q[m-i] |
| * r[1..n] R[0..n-1] r[i] == R[n+1-i] |
| * u[0..m+n] w[0..m+n] u[i] == w[m+n-i] |
| * v[1..n] x[0..n-1] v[i] == x[n-i] |
| * |
| * let N == n - 1 so that n == N + 1 thus: |
| * |
| * q[0..m] to Q[0..m] thus q[i] == Q[m-i] |
| * r[1..n] R[0..N] r[i] == R[N+2-i] |
| * u[0..m+n] w[0..m+N+1] u[i] == w[m+N+1-i] |
| * v[1..n] x[0..N] v[i] == x[N+1-i] |
| */ |
| |
| /* |
| * Note: Be very observent of the usage of uPtr, and vPtr. |
| * They are used to point to u, v, w, q or r as necessary. |
| */ |
| precision pdivmod(u, v, qp, rp) |
| precision u, v, *qp, *rp; |
| { |
| register digitPtr uPtr, vPtr, qPtr, LastPtr; |
| |
| register accumulator temp; /* 0 <= temp < base^2 */ |
| register digit carry; /* 0 <= carry < 2 */ |
| register digit hi; /* 0 <= hi < base */ |
| |
| register posit n, m; |
| digit d; /* 0 <= d < base */ |
| digit qd; /* 0 <= qd < base */ |
| #ifdef DEBUG |
| int i; |
| #endif |
| |
| precision q, r, w; /* quotient, remainder, temporary */ |
| |
| n = v->size; /* size of v and r */ |
| |
| (void) pparm(u); |
| (void) pparm(v); |
| if (u->size < n) { |
| q = pUndef; |
| r = pUndef; |
| pset(&q, pzero); |
| pset(&r, u); |
| goto done; |
| } |
| |
| m = u->size - n; |
| |
| uPtr = u->value + m + n; |
| vPtr = v->value + n; |
| |
| q = palloc(m + 1); |
| if (q == pUndef) return q; |
| |
| q->sign = (u->sign != v->sign); /* can generate -0 */ |
| |
| r = palloc(n); |
| if (r == pUndef) { |
| pdestroy(q); |
| return r; |
| } |
| r->sign = u->sign; |
| /* |
| * watch out! does this function return: q=floor(a/b) or trunc(a/b)? |
| * it's currently the latter, but every mathmaticion I have talked to |
| * prefers the former so that a % b returns between 0 to b-1. The |
| * problem is that this is slower and disagrees with C common practice. |
| */ |
| qPtr = q->value + m + 1; |
| |
| if (n == 1) { |
| d = *--vPtr; /* d is only digit of v */ |
| if (d == 0) { /* divide by zero? */ |
| q = pnew(errorp(PDOMAIN, "pdivmod", "divide by zero")); |
| } else { /* single digit divide */ |
| #ifndef ASM_16BIT |
| vPtr = r->value + n; |
| hi = 0; /* hi is current remainder */ |
| do { |
| temp = mulBase(hi); /* 0 <= temp <= (base-1)^2 */ |
| temp += *--uPtr; /* 0 <= temp <= base(base-1) */ |
| hi = uModDiv(temp, d, --qPtr); /* 0 <= hi < base */ |
| } while (uPtr > u->value); |
| *--vPtr = hi; |
| #else |
| qPtr -= m + 1; |
| *(r->value) = memdivw1(qPtr, u->value, m + 1, d); |
| #endif |
| } |
| } else { /* muti digit divide */ |
| /* |
| * normalize: multiply u and v by d so hi digit of v > b/2 |
| */ |
| d = BASE / (*--vPtr+1); /* high digit of v */ |
| |
| w = palloc(n); /* size of v */ |
| if (w == pUndef) return w; |
| |
| #ifndef ASM_16BIT |
| vPtr = v->value; |
| uPtr = w->value; /* very confusing. just a temp */ |
| LastPtr = vPtr + n; |
| hi = 0; |
| do { /* single digit multiply */ |
| temp = uMul(*vPtr++, d); /* 0<= temp <= base(base-1)/2 */ |
| temp += hi; /* 0 <= temp <= (base^2-1)/2 */ |
| hi = divBase(temp); /* 0 <= hi < base / 2 */ |
| *uPtr++ = modBase(temp); /* 0 <= hi < base / 2 */ |
| } while (vPtr < LastPtr); /* on exit hi == 0 */ |
| #else |
| hi = memmulw1(w->value, v->value, n, d); |
| #endif |
| |
| pset(&v, w); |
| pdestroy(w); |
| |
| w = palloc(m + n + 1); |
| if (w == pUndef) return w; |
| |
| #ifndef ASM_16BIT |
| uPtr = u->value; |
| vPtr = w->value; /* very confusing. just a temp */ |
| LastPtr = uPtr + m + n; |
| do { /* single digit multiply */ |
| temp = uMul(*uPtr++, d); |
| temp += hi; |
| hi = divBase(temp); |
| *vPtr++ = modBase(temp); |
| } while (uPtr < LastPtr); |
| *vPtr = hi; /* note extra digit */ |
| #else |
| hi = memmulw1(w->value, u->value, m + n, d); |
| w->value[m + n] = hi; |
| #endif |
| |
| pset(&u, w); |
| pdestroy(w); |
| |
| #ifdef DEBUG |
| printf("m = %d n = %d\nd = %d\n", m, n, d); |
| printf("norm u = "); pshow(u); |
| printf("norm v = "); pshow(v); |
| #endif |
| |
| uPtr = u->value + m + 1; /* current least significant digit */ |
| do { |
| --uPtr; |
| #ifdef DEBUG |
| printf(" u = "); |
| for (i = n; i >= 0; --i) printf("%.*x ", sizeof(digit) * 2, uPtr[i]); |
| putchar('\n'); |
| printf(" v = "); |
| for (i = 1; i < 3; i++) printf("%.*x ", sizeof(digit) * 2, |
| v->value[n-i]); |
| putchar('\n'); |
| #endif |
| #ifndef ASM_16BIT |
| vPtr = v->value + n; |
| LastPtr = uPtr + n; |
| if (*LastPtr == *--vPtr) { /* guess next digit */ |
| qd = BASE - 1; |
| } else { |
| temp = mulBase(*LastPtr); |
| temp += *--LastPtr; /* 0 <= temp< base^2 */ |
| temp = uModDiv(temp, *vPtr, &qd); |
| --vPtr; |
| --LastPtr; |
| while (uMul(*vPtr, qd) > mulBase(temp) + *LastPtr) { |
| --qd; |
| temp += vPtr[1]; |
| if (temp >= BASE) break; /* if so, vPtr*qd <= temp*base */ |
| } |
| LastPtr += 2; |
| } |
| /* |
| * Single digit Multiply then Subtract |
| */ |
| vPtr = v->value; |
| carry = 1; /* noborrow bit */ |
| hi = 0; /* hi digit of multiply */ |
| do { |
| /* multiply */ |
| temp = uMul(qd, *vPtr++); /* 0 <= temp <= (base-1)^2 */ |
| temp += hi; /* 0 <= temp <= base(base-1) */ |
| hi = divBase(temp); |
| temp = modBase(temp); |
| /* subtract */ |
| temp = (BASE-1) - temp; /* 0 <= temp < base */ |
| temp += *uPtr + carry; /* 0 <= temp < 2*base */ |
| carry = divBase(temp); |
| *uPtr++ = modBase(temp); /* 0 <= carry < 2 */ |
| } while (uPtr < LastPtr); |
| temp = (BASE-1) - hi; |
| temp += *uPtr + carry; |
| carry = divBase(temp); |
| *uPtr = modBase(temp); |
| uPtr -= n; |
| #else |
| #if 0 |
| carry = !memmulsubw(uPtr, v->value, n, qd); /* 1 if noborrow */ |
| #endif |
| carry = !memdivw(uPtr, v->value, n, &qd); /* 1 if noborrow */ |
| #endif |
| #ifdef DEBUG |
| printf(" qhat = %.*x\n", sizeof(digit) * 2, qd); |
| printf(" new u = "); |
| for (i = n; i >= 0; --i) printf("%.*x ", sizeof(digit) * 2, uPtr[i]); |
| putchar('\n'); |
| #endif |
| if (carry == 0) { /* Test remainder, add back */ |
| vPtr = v->value; |
| LastPtr = uPtr + n; |
| do { |
| temp = *uPtr + *vPtr++; |
| temp += carry; |
| carry = divBase(temp); |
| *uPtr++ = modBase(temp); |
| } while (uPtr < LastPtr); |
| *uPtr += carry - BASE; /* real strange but works */ |
| uPtr -= n; |
| --qd; |
| #ifdef DEBUG |
| printf(" decrementing q...adding back\n"); |
| printf(" fixed u = "); |
| for (i = n; i >= 0; --i) printf("%.*x ", sizeof(digit) * 2, uPtr[i]); |
| putchar('\n'); |
| printf(" newq = %.*x\n", sizeof(digit) * 2, qd); |
| #endif |
| } |
| *--qPtr = qd; /* one leading zero possible */ |
| #ifdef DEBUG |
| putchar('\n'); |
| #endif |
| } while (uPtr > u->value); |
| |
| /* |
| * Un-normalize to get remainder |
| */ |
| #ifndef ASM_16BIT |
| uPtr = u->value + n; /* skip hi digit (it's zero) */ |
| vPtr = r->value + n; |
| hi = 0; /* hi is current remainder */ |
| do { /* single digit divide */ |
| temp = mulBase(hi); /* 0<=temp < base^2-(base-1) */ |
| temp += *--uPtr; /* 0 <= temp < base^2 */ |
| hi = uModDiv(temp, d, --vPtr); |
| } while (uPtr > u->value); /* carry will be zero */ |
| #else |
| carry = memdivw1(r->value, u->value, n, d); /* always 0 */ |
| #endif |
| pnorm(r); /* remainder may have many leading 0's */ |
| } |
| |
| if (m > 0 && qPtr[m] == 0) { |
| --(q->size); /* normalize */ |
| } |
| if (q->size == 1 && *qPtr == 0) q->sign = false; |
| |
| done: |
| |
| pdestroy(u); |
| pdestroy(v); |
| |
| if (rp == (precision *) -1) { |
| if (qp != pNull) pset(qp, q); |
| pdestroy(q); |
| return presult(r); |
| } else if (qp == (precision *) -1) { |
| if (rp != pNull) pset(rp, r); |
| pdestroy(r); |
| return presult(q); |
| } |
| if (qp != pNull) pset(qp, q); |
| if (rp != pNull) pset(rp, r); |
| pdestroy(q); |
| pdestroy(r); |
| return pUndef; |
| } |