/*
* mf_arith.c -- optimized arithmetic routines for UNIX METAFONT 84
*
* These functions account for a significant percentage of the processing
* time used by METAFONT, and have been recoded in assembly language for
* some classes of machines.
*
* As yet I don't have code for SPARC, so I'm just using a C version
* by richards, in hopes that cc does better than pc on the brute-force
* algorithm found in mf.web.
*
* function makefraction(p, q: integer): fraction;
* { Calculate the function floor( (p * 2^28) / q + 0.5 ) }
* { if both p and q are positive. If not, then the value }
* { of makefraction is calculated as though both *were* }
* { positive, then the result sign adjusted. }
* { (e.g. makefraction ALWAYS rounds away from zero) }
* { In case of an overflow, return the largest possible }
* { value (2^32-1) with the correct sign, and set global }
* { variable "aritherror" to 1. Note that -2^32 is }
* { considered to be an illegal product for this type of }
* { arithmetic! }
*
* function takefraction(f: fraction; q: integer): integer;
* { Calculate the function floor( (p * q) / 2^28 + 0.5 ) }
* { takefraction() rounds in a similar manner as }
* { makefraction() above. }
*
*/
#include "h00vars.h"
extern bool aritherror; /* to be set on overflow */
n = (n-1)*FRACTION_ONE;
f = 1;
do {
be_careful = p - q;
p = be_careful + p;
if (p >= 0) {
f = f + f + 1;
} else {
f <<= 1;
p += q;
}
} while (f < FRACTION_ONE);
be_careful = p - q;
if ((be_careful + p) >= 0)
f += 1;
return (negative? -(f+n) : (f+n));
}
int takefraction(q, f)
register int q;
register int f;
{
int n, negative;
register int p, be_careful;
if (f < 0) {
negative = 1;
f = -f;
} else negative = 0;
if (q < 0) {
negative = !negative;
q = -q;
}
if (f < FRACTION_ONE)
n = 0;
else {
n = f / FRACTION_ONE;
f = f % FRACTION_ONE;
if (q < (EL_GORDO / n))
n = n * q;
else {
aritherror = TRUE;
n = EL_GORDO;
}
}
f += FRACTION_ONE;
p = FRACTION_HALF;
if (q < FRACTION_FOUR)
do {
if (f & 0x1)
p = (p+q) >> 1;
else
p >>= 1;
f >>= 1;
} while (f > 1);
else
do {
if (f & 0x1)
p = p + ((q-p) >> 1);
else
p >>= 1;
f >>= 1;
} while (f > 1);
be_careful = n - EL_GORDO;
if ((be_careful + p) > 0) {
aritherror = TRUE;
n = EL_GORDO - p;
}