|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - download
Length: 5988 (0x1764) Types: TextFile Notes: UNIX file Names: »mdiv.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code └─⟦f4b8d8c84⟧ UNIX Filesystem └─ ⟦this⟧ »libmp/mdiv.c«
#include "mprec.h" #include <assert.h> #include <mdata.h> /* * Mdiv sets the mints pointed to by "q" and "r" to the quotient * and remainder (respectively) of dividing "a" by "b". If "b" * is zero, then it calls mperr with the appropriate message. * The division is performed such that the following two properties * hold : * 1. "r" + "q" * "b" = "a". * 2. The sign of "r" = the sign of "q". * 3. The abs. value of "r" < abs. value of "b". * Note that no assumption is made as to the distinctness of "a", * "b", "q" and "r". */ void mdiv(a, b, q, r) register mint *a, *b, *q, *r; { int apos, bpos; mint al, bl; int ispos(); void shortd(), longd(); /* take care of small denominators */ if (b->len <= 2 && ( b->len == 1 /* 0 to BASE - 2 */ || b->val[1] == NEFL /* -BASE to -2 */ || b->val[1] == 0 /* BASE - 1 */ || (b->val[1] == 1 && b->val[0] == 0) /* BASE */ ) ) { shortd(a, b, q, r); return; } /* make denominator negative */ if (bpos = ispos(b)) { minit(&bl); mneg(b, &bl); b = &bl; } assert(b->len > 2); /* copy numerator, makeing it positive */ minit(&al); if (apos = ispos(a)) mcopy(a, &al); else mneg(a, &al); a = &al; /* * BASE ^ (a->len - 1) <= a < BASE ^ a->len * BASE ^ (b->len - 2) < -b <= BASE ^ (b->len - 1) * * do divide. */ if (a->len <= b->len - 2) mcopy(mzero, q); else longd(a, b, q); /* adjust signs */ if (apos ? !bpos : bpos) { mneg(q, q); mneg(a, r); } else mcopy(a, r); /* throw away garbage */ mpfree(al.val); if (bpos) mpfree(bl.val); } /* * Shortd divides the mints pointed to by "a" and "b". * It sets the mints pointed to by "q" and "r" to the quotient * and remainder respectively. It assumes that "b" is between * -BASE and BASE. */ static void shortd(a, b, q, r) register mint *a, *b, *q, *r; { int rem; int denom; assert(b->len <= 2); if (ispos(b)) { denom = b->val[0]; if (b->len != 1 && denom == 0) denom = BASE; } else denom = b->val[0] - BASE; assert(-BASE <= denom && denom <= BASE); sdiv(a, denom, q, &rem); mitom(rem, r); } /* * Longd does the actual long divide. It sets "q" and "a" to * the quotient and remainder (respectively) of "a" / (- "b"). * It assumes the following: * 1. "b" is negative * 2. "a" is positive * 3. "a" is a destroyable copy * 4. ("a"->len + 2) > ("b"->len) >= 3 */ static void longd(a, b, q) register mint *a, *b; mint *q; { register char *rp; unsigned shift; int tden; unsigned tbias; mint res; /* * For "a" we have the range * (BASE - 1) * BASE ^ (a->len - 1) > a * a >= (BASE - 1) * BASE ^ (a->len - 2) * and for "b" we want * tden * BASE ^ tbias >= -b > (tden - 1) * BASE ^ tbias */ tden = est(b); tbias = b->len - 3; assert(a->len >= tbias + 2); res.len = a->len - tbias; res.val = (char *)mpalc(res.len); rp = &res.val[res.len - 1]; *rp-- = 0; shift = a->len - (tbias + 2); do { *rp = guess(tden, tbias + shift, a); msma(b, *rp, shift, a); if (snc(b, shift, a)) { ++*rp; msma(b, 1, shift, a); } --rp; } while (shift-- != 0); norm(a); norm(&res); mpfree(q->val); *q = res; } /* * Est estimates the mint pointed to by "a". Its result * satisfies the following: * 1. result * BASE ^ (a->len - 3) >= -a * && -a > (result - 1) * BASE ^ (a->len - 3) * 2. BASE ^ 2 >= mant > BASE * Note that "a" is assumed to be negative and have length atleast * three. */ static int est(a) register mint *a; { register int result; assert(!ispos(a) && a->len >= 3); result = (NEFL - a->val[a->len - 2]) << L2BASE; result += NEFL - a->val[a->len - 3] + 1; assert(BASE * BASE >= result && result > BASE); return (result); } /* * Guess returns an integer which is the integral part of * "a" / ("den" * BASE ^ "shift") * Note that it is assumed that the following hold: * 1. "a" is positive. * 2. BASE ^ 2 >= "den" > BASE * 3. the quotient is a single "digit" (ie. NEFL > quotient >= 0) */ static int guess(den, shift, a) int den; unsigned shift; mint *a; { register char *ap = &a->val[a->len - 1]; long result; if (*ap == 0) --ap; assert(ap + 1 - a->val <= shift + 3); if (ap + 1 - a->val < shift + 2) return (0); result = (ap[0] << L2BASE) + ap[-1]; if (ap + 1 - a->val == shift + 3) result = (result << L2BASE) + ap[-2]; assert(result < den * (long)BASE); return (result / den); } /* * Snc does a shift-negate-compare. Specifically, it assumes that * "b" is negative, "a" is positive and it returns wether or not * a >= -b * BASE ^ shift * (ie. a + b * BASE ^ shift is positive (ie. >= 0).) */ static snc(b, shift, a) mint *a, *b; unsigned shift; { register char *ap; register char *bp; register char *limit; int aleng, bleng; int temp; /* * correct length of a */ limit = a->val; for (ap = &limit[a->len - 1]; *ap == 0 && ap > limit; --ap) ; a->len = ap - limit + 1; if (*ap == NEFL) ++a->len; aleng = ap - limit + 1; bleng = b->len + shift - 1; /* * BASE ^ aleng > a >= BASE ^ (aleng - 1) * BASE ^ (b->len - 1) >= -b > BASE ^ (b->len - 2) * so multiplying by BASE ^ shift gives * BASE ^ bleng >= -b * BASE ^ shift > BASE ^ (bleng - 1) * This means that * IF bleng > aleng * THEN bleng - 1 >= aleng * SO -b * BASE ^ shift > BASE ^ (bleng - 1) * >= BASE ^ aleng * > a * and thus we return FALSE. * Also we have * IF aleng > bleng * THEN aleng - 1 >= bleng * SO a >= BASE ^ (aleng - 1) * >= BASE ^ bleng * >= -b * BASE ^ shift * and thus we return TRUE. * Finally * If aleng = bleng then the two numbers line up and * it is just a matter of returning wether or not their * sum would cause a carry. */ if (aleng != bleng) return (aleng > bleng); limit = b->val; for (bp = &limit[b->len - 2]; bp >= limit; --bp, --ap) { temp = *ap + *bp; if (temp >= BASE) return (TRUE); if (temp < NEFL) return (FALSE); } return (FALSE); }