# define INCLUDE_CTYPE
# include "dgd.h"
# include "xfloat.h"

typedef struct {
    unsigned short sign;	/* 0: positive, 0x8000: negative */
    unsigned short exp;		/* bias: 32767 */
    unsigned short high;	/* 0 / 1 / 14 bits */
    Uint low;			/* 0 / 29 bits / 0 / 0 */
} flt;

# define NBITS		44
# define BIAS		0x7fff


/* constants */

xfloat sixty =		{ 0x404e, 0x00000000L };	/* 60 */
xfloat thousand =	{ 0x408f, 0x40000000L };	/* 1e3 */
xfloat thousandth =	{ 0x3f50, 0x624dd2f2L };	/* 1e-3 */

static flt half =	{ 0x0000, 0x7ffe, 0x4000, 0x00000000L };
static flt one =	{ 0x0000, 0x7fff, 0x4000, 0x00000000L };
static flt maxlog =	{ 0x0000, 0x8008, 0x588c, 0x57baf578L };
static flt minlog =	{ 0x8000, 0x8008, 0x588c, 0x57baf578L };
static flt sqrth =	{ 0x0000, 0x7ffe, 0x5a82, 0x3cccfe78L };
static flt pi =		{ 0x0000, 0x8000, 0x6487, 0x76a8885cL };
static flt pio2 =	{ 0x0000, 0x7fff, 0x6487, 0x76a8885cL };
static flt pio4 =	{ 0x0000, 0x7ffe, 0x6487, 0x76a8885cL };


/*
 * NAME:	f_edom()
 * DESCRIPTION:	Domain error
 */
static void f_edom()
{
    error("Math argument");
}

/*
 * NAME:	f_erange()
 * DESCRIPTION:	Out of range
 */
static void f_erange()
{
    error("Result too large");
}

static void f_sub P((flt*, flt*));

/*
 * NAME:	f_add()
 * DESCRIPTION:	a = a + b.  The result is normalized, but not guaranteed to 
 *		be in range.
 */
static void f_add(a, b)
register flt *a, *b;
{
    register unsigned short h, n;
    register Uint l;
    flt tmp;

    if (b->exp == 0) {
	/* b is 0 */
	return;
    }
    if (a->exp == 0) {
	/* a is 0 */
	*a = *b;
	return;
    }
    if (a->sign != b->sign) {
	a->sign ^= 0x8000;
	f_sub(a, b);
	a->sign ^= 0x8000;
	return;
    }
    if (a->exp < b->exp) {
	/* b is the largest; exchange a and b */
	tmp = *a;
	*a = *b;
	b = &tmp;
    }

    n = a->exp - b->exp;
    if (n <= NBITS) {
	h = a->high;
	l = a->low;

	/*
	 * perform addition
	 */
	if (n < 31) {
	    h += (Uint) b->high >> n;
	    l += (((Uint) b->high << (31 - n)) & 0x7fffffffL) | (b->low >> n);
	} else {
	    l += b->high >> (n - 31);
	}
	if ((Int) l < 0) {
	    /* carry */
	    l &= 0x7fffffffL;
	    h++;
	}
	if ((short) h < 0) {
	    /* too large */
	    l |= (Uint) h << 31;
	    l >>= 1;
	    h >>= 1;
	    a->exp++;
	}

	/*
	 * rounding off
	 */
	if ((Int) (l += 2) < 0 && (short) ++h < 0) {
	    h >>= 1;
	    a->exp++;
	}
	l &= 0x7ffffffcL;

	a->high = h;
	a->low = l;
    }
}

/*
 * NAME:	f_sub()
 * DESCRIPTION:	a = a - b.  The result is normalized, but not guaranteed to be
 *		in range.
 */
static void f_sub(a, b)
register flt *a, *b;
{
    register unsigned short h, n;
    register Uint l;
    flt tmp;

    if (b->exp == 0) {
	/* b is 0 */
	return;
    }
    if (a->exp == 0) {
	*a = *b;
	a->sign ^= 0x8000;
	return;
    }
    if (a->sign != b->sign) {
	a->sign ^= 0x8000;
	f_add(a, b);
	a->sign ^= 0x8000;
	return;
    }
    if (a->exp <= b->exp &&
	(a->exp < b->exp || (a->high <= b->high &&
			     (a->high < b->high || a->low < b->low)))) {
	/* b is the largest; exchange a and b */
	tmp = *a;
	*a = *b;
	b = &tmp;
	a->sign ^= 0x8000;
    }

    n = a->exp - b->exp;
    if (n <= NBITS) {
	h = a->high;
	l = a->low;

	/*
	 * perform subtraction
	 */
	if (n < 31) {
	    h -= (Uint) b->high >> n;
	    l -= (((Uint) b->high << (31 - n)) & 0x7fffffffL) | (b->low >> n);
	    if (b->low & ((1 << n) - 1)) {
		--l;
	    }
	} else {
	    n -= 31;
	    l -= b->high >> n;
	    if (b->low != 0 || (b->high & ((1 << n) - 1))) {
		--l;
	    }
	}
	if ((Int) l < 0) {
	    /* borrow */
	    l &= 0x7fffffffL;
	    --h;
	}

	/*
	 * normalize
	 */
	if (h == 0) {
	    if (l == 0) {
		a->exp = 0;
		return;
	    }
	    n = 15;
	    if ((l & 0xffff0000L) == 0) {
		l <<= 15;
		n += 15;
	    }
	    h = l >> 16;
	    l <<= 15;
	    l &= 0x7fffffffL;
	    a->exp -= n;
	}
	if (h < 0x4000) {
	    n = 0;
	    if ((h & 0xff00) == 0) {
		h <<= 7;
		n += 7;
	    }
	    while (h < 0x4000) {
		h <<= 1;
		n++;
	    }
	    h |= l >> (31 - n);
	    l <<= n;
	    l &= 0x7fffffffL;
	    a->exp -= n;
	}

	/*
	 * rounding off
	 */
	if ((Int) (l += 2) < 0 && (short) ++h < 0) {
	    h >>= 1;
	    a->exp++;
	}
	l &= 0x7ffffffcL;

	a->high = h;
	a->low = l;
    }
}

/*
 * NAME:	f_mult()
 * DESCRIPTION:	a = a * b.  The result is normalized, but may be out of range.
 */
static void f_mult(a, b)
register flt *a, *b;
{
    register Uint m, l;
    register unsigned short al, am, ah, bl, bm, bh;

    if (a->exp == 0) {
	/* a is 0 */
	return;
    }
    if (b->exp == 0) {
	/* b is 0 */
	a->exp = 0;
	return;
    }

    al = ((unsigned short) a->low) >> 1;
    bl = ((unsigned short) b->low) >> 1;
    am = a->low >> 16;
    bm = b->low >> 16;
    ah = a->high;
    bh = b->high;

    m = (Uint) al * bl;
    m >>= 15;
    m += (Uint) al * bm + (Uint) am * bl;
    m >>= 15;
    m += (Uint) al * bh + (Uint) am * bm + (Uint) ah * bl;
    m >>= 13;
    l = m & 0x03;
    m >>= 2;
    m += (Uint) am * bh + (Uint) ah * bm;
    l |= (m & 0x7fff) << 2;
    m >>= 15;
    m += (Uint) ah * bh;
    l |= m << 17;
    ah = m >> 14;

    a->sign ^= b->sign;
    a->exp += b->exp - BIAS;
    if ((short) ah < 0) {
	ah >>= 1;
	l >>= 1;
	a->exp++;
    }
    l &= 0x7fffffffL;

    /*
     * rounding off
     */
    if ((Int) (l += 2) < 0 && (short) ++ah < 0) {
	ah >>= 1;
	a->exp++;
    }
    l &= 0x7ffffffcL;

    a->high = ah;
    a->low = l;
}

/*
 * NAME:	f_div()
 * DESCRIPTION:	a = a / b.  b must be non-zero.  The result is normalized,
 *		but may be out of range.
 */
static void f_div(a, b)
register flt *a, *b;
{
    unsigned short n[3];
    register Uint numh, numl, divl, high, low, q;
    register unsigned short divh, i;

    if (b->exp == 0) {
	error("Division by zero");
    }
    if (a->exp == 0) {
	/* a is 0 */
	return;
    }

    numh = ((Uint) a->high << 16) | (a->low >> 15);
    numl = a->low << 17;

    divh = (b->high << 1) | (b->low >> 30);
    divl = b->low << 2;

    n[0] = 0;
    n[1] = 0;
    i = 3;
    do {
	/* estimate the high word of the quotient */
	q = numh / divh;
	/* highlow = div * q */
	low = (unsigned short) (high = q * (unsigned short) divl);
	high >>= 16;
	high += q * (divl >> 16);
	low |= high << 16;
	high >>= 16;
	high += q * divh;

	/* the estimated quotient may be 2 off; correct it if needed */
	if (high >= numh && (high > numh || low > numl)) {
	    high -= divh;
	    if (low < divl) {
		--high;
	    }
	    low -= divl;
	    --q;
	    if (high >= numh && (high > numh || low > numl)) {
		high -= divh;
		if (low < divl) {
		    --high;
		}
		low -= divl;
		--q;
	    }
	}

	n[--i] = q;
	if (i == 0) {
	    break;
	}

	/* subtract highlow */
	numh -= high;
	if (numl < low) {
	    --numh;
	}
	numl -= low;
	numh <<= 16;
	numh |= numl >> 16;
	numl <<= 16;

    } while (numh != 0 || numl != 0);

    a->sign ^= b->sign;
    a->exp -= b->exp - BIAS + 1;
    high = n[2];
    low = ((Uint) n[1] << 15) | (n[0] >> 1);
    if ((short) high < 0) {
	low |= high << 31;
	low >>= 1;
	high >>= 1;
	a->exp++;
    }

    /*
     * rounding off
     */
    if ((Int) (low += 2) < 0 && (short) ++high < 0) {
	high >>= 1;
	a->exp++;
    }
    low &= 0x7ffffffcL;

    a->high = high;
    a->low = low;
}

/*
 * NAME:	f_trunc()
 * DESCRIPTION:	truncate a flt
 */
static void f_trunc(a)
register flt *a;
{
    static unsigned short maskh[] = {
		0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x7f00,
	0x7f80, 0x7fc0, 0x7fe0, 0x7ff0, 0x7ff8, 0x7ffc, 0x7ffe, 0x7fff,
		0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff
    };
    static Uint maskl[] = {
		     0x00000000L, 0x00000000L, 0x00000000L,
	0x00000000L, 0x00000000L, 0x00000000L, 0x00000000L,
	0x00000000L, 0x00000000L, 0x00000000L, 0x00000000L,
	0x00000000L, 0x00000000L, 0x00000000L, 0x00000000L,
		     0x40000000L, 0x60000000L, 0x70000000L,
	0x78000000L, 0x7c000000L, 0x7e000000L, 0x7f000000L,
	0x7f800000L, 0x7fc00000L, 0x7fe00000L, 0x7ff00000L,
	0x7ff80000L, 0x7ffc0000L, 0x7ffe0000L, 0x7fff0000L,
	0x7fff8000L, 0x7fffc000L, 0x7fffe000L, 0x7ffff000L,
	0x7ffff800L, 0x7ffffc00L, 0x7ffffe00L, 0x7fffff00L,
	0x7fffff80L, 0x7fffffc0L, 0x7fffffe0L, 0x7ffffff0L,
	0x7ffffff8L
    };

    if (a->exp < BIAS) {
	a->exp = 0;
    } else if (a->exp < BIAS + NBITS) {
	a->high &= maskh[a->exp - BIAS];
	a->low &= maskl[a->exp - BIAS];
    }
}

/*
 * NAME:	f_37bits()
 * DESCRIPTION:	round a flt to 37 binary digits of precision
 */
static void f_37bits(a)
register flt *a;
{
    if ((Int) (a->low += 0x100) < 0) {
	a->low = 0;
	if ((short) ++(a->high) < 0) {
	    a->high >>= 1;
	    a->exp++;
	}
    } else {
	a->low &= 0xfffffe00L;
    }
}

/*
 * NAME:	f_round()
 * DESCRIPTION:	round off a flt
 */
static void f_round(a)
register flt *a;
{
    static flt half = { 0, 0x7ffe, 0x4000, 0x00000000L };

    half.sign = a->sign;
    f_add(a, &half);
    f_trunc(a);
}

/*
 * NAME:	f_cmp()
 * DESCRIPTION:	compate two flts
 */
static int f_cmp(a, b)
register flt *a, *b;
{
    if (a->exp == 0) {
	if (b->exp == 0) {
	    return 0;
	}
	return (b->sign == 0) ? -1 : 1;
    } else if (b->exp == 0) {
	if (a->exp == 0) {
	    return 0;
	}
	return (a->sign == 0) ? 1 : -1;
    } else if (a->sign != b->sign) {
	return (a->sign == 0) ? 1 : -1;
    } else {
	if (a->exp == b->exp && a->high == b->high && a->low == b->low) {
	    return 0;
	}
	if (a->exp <= b->exp &&
	    (a->exp < b->exp || (a->high <= b->high &&
				 (a->high < b->high || a->low < b->low)))) {
	    return (a->sign == 0) ? -1 : 1;
	}
	return (a->sign == 0) ? 1 : -1;
    }
}

/*
 * NAME:	f_itof()
 * DESCRIPTION:	convert an integer to a flt
 */
static void f_itof(i, a)
Int i;
register flt *a;
{
    register Uint n;
    register unsigned short shift;

    /* deal with zero and sign */
    if (i == 0) {
	a->exp = 0;
	return;
    } else if (i < 0) {
	a->sign = 0x8000;
	n = -i;
    } else {
	a->sign = 0;
	n = i;
    }

    shift = 0;
    while ((n & 0xff000000L) == 0) {
	n <<= 8;
	shift += 8;
    }
    while ((Int) n >= 0) {
	n <<= 1;
	shift++;
    }
    a->exp = BIAS + 31 - shift;
    a->high = n >> 17;
    a->low = (n << 14) & 0x7fffffff;
}

/*
 * NAME:	f_ftoi()
 * DESCRIPTION:	convert a flt to an integer, discarding the fractional part
 */
static Int f_ftoi(a)
register flt *a;
{
    Uint i;

    if (a->exp < BIAS) {
	return 0;
    }
    if (a->exp > BIAS + 30 &&
	(a->sign == 0 || a->exp != BIAS + 31 || a->high != 0x4000 ||
	 a->low != 0)) {
	f_erange();
    }

    i = (((Uint) a->high << 17) | (a->low >> 14)) >> (BIAS + 31 - a->exp);

    return (a->sign == 0) ? i : -i;
}

/*
 * NAME:	f_ftoxf()
 * DESCRIPTION:	convert flt to xfloat
 */
static void f_ftoxf(a, f)
register flt *a;
register xfloat *f;
{
    register unsigned short exp;
    register unsigned short high;
    register Uint low;

    exp = a->exp;
    if (exp == 0) {
	/* zero */
	f->high = 0;
	f->low = 0;
	return;
    }
    high = a->high;
    low = a->low;

    /* mantissa */
    if ((Int) (low += 0x100) < 0) {
	low = 0;
	if ((short) ++high < 0) {
	    high >>= 1;
	    exp++;
	}
    }

    /* exponent */
    if (exp > BIAS + 1023) {
	f_erange();
    }
    if (exp < BIAS - 1022) {
	/* underflow */
	f->high = 0;
	f->low = 0;
	return;
    }

    f->high = a->sign | ((exp - BIAS + 1023) << 4) | ((high >> 10) & 0x000f);
    f->low = ((Uint) high << 22) | (low >> 9);
}

/*
 * NAME:	f_xftof()
 * DESCRIPTION:	convert xfloat to flt
 */
static void f_xftof(f, a)
register xfloat *f;
register flt *a;
{
    register unsigned short exp;

    a->sign = f->high & 0x8000;
    exp = (f->high >> 4) & 0x07ff;
    if (exp == 0) {
	/* zero */
	a->exp = 0;
	return;
    }
    a->exp = exp + BIAS - 1023;
    a->high = 0x4000 | ((f->high & 0x0f) << 10) | (f->low >> 22);
    a->low = (f->low << 9) & 0x7fffffffL;
}


static flt tens[] = {
    { 0, 0x8002, 0x5000, 0x00000000L },	/* 10 ** 1 */
    { 0, 0x8005, 0x6400, 0x00000000L },	/* 10 ** 2 */
    { 0, 0x800C, 0x4E20, 0x00000000L },	/* 10 ** 4 */
    { 0, 0x8019, 0x5F5E, 0x08000000L },	/* 10 ** 8 */
    { 0, 0x8034, 0x470D, 0x726FC100L },	/* 10 ** 16 */
    { 0, 0x8069, 0x4EE2, 0x6B6A0ADCL },	/* 10 ** 32 */
    { 0, 0x80D3, 0x613C, 0x07D27FF4L },	/* 10 ** 64 */
    { 0, 0x81A8, 0x49DD, 0x11F2603CL },	/* 10 ** 128 */
    { 0, 0x8351, 0x553F, 0x3AFEE780L },	/* 10 ** 256 */
    { 0, 0x86A3, 0x718C, 0x682BA984L },	/* 10 ** 512 */
};

static flt tenths[] = {
    { 0, 0x7FFB, 0x6666, 0x33333334L },	/* 10 ** -1 */
    { 0, 0x7FF8, 0x51EB, 0x428F5C28L },	/* 10 ** -2 */
    { 0, 0x7FF1, 0x68DB, 0x45D63888L },	/* 10 ** -4 */
    { 0, 0x7FE4, 0x55E6, 0x1DC46118L },	/* 10 ** -8 */
    { 0, 0x7FC9, 0x734A, 0x652FB114L },	/* 10 ** -16 */
    { 0, 0x7F94, 0x67D8, 0x47AB5150L },	/* 10 ** -32 */
    { 0, 0x7F2A, 0x543F, 0x7A89E950L },	/* 10 ** -64 */
    { 0, 0x7E55, 0x6EE8, 0x119F1930L },	/* 10 ** -128 */
    { 0, 0x7CAC, 0x6018, 0x50C958E0L },	/* 10 ** -256 */
    { 0, 0x795A, 0x4824, 0x7B8CB6C8L },	/* 10 ** -512 */
};

/*
 * NAME:	float->atof()
 * DESCRIPTION:	Convert a string to a float.  The string must be in the
 *		proper format.  Return TRUE if the operation was successful,
 *		FALSE otherwise.
 */
bool flt_atof(s, f)
char **s;
xfloat *f;
{
    flt a, b, c, *t;
    register unsigned short e, h;
    register char *p;

    p = *s;

    /* sign */
    if (*p == '-') {
	a.sign = b.sign = 0x8000;
	p++;
    } else {
	a.sign = b.sign = 0;
    }

    a.exp = 0;
    b.low = 0;

    /* digits before . */
    while (isdigit(*p)) {
	f_mult(&a, &tens[0]);
	h = (*p++ - '0') << 12;
	if (h != 0) {
	    e = BIAS + 3;
	    while ((short) h >= 0) {
		h <<= 1;
		--e;
	    }
	    b.exp = e;
	    b.high = h >> 1;
	    f_add(&a, &b);
	}
	if (a.exp > 0xffff - 10) {
	    return FALSE;
	}
    }

    /* digits after . */
    if (*p == '.') {
	c = tenths[0];
	while (isdigit(*++p)) {
	    if (c.exp > 10) {
		h = (*p - '0') << 12;
		if (h != 0) {
		    e = BIAS + 3;
		    while ((short) h >= 0) {
			h <<= 1;
			--e;
		    }
		    b.exp = e;
		    b.high = h >> 1;
		    b.low = 0;
		    f_mult(&b, &c);
		    f_add(&a, &b);
		}
		f_mult(&c, &tenths[0]);
	    }
	}
    }

    /* exponent */
    if (*p == 'e' || *p == 'E') {
	/* sign of exponent */
	if (*++p == '-') {
	    t = tenths;
	    p++;
	} else {
	    t = tens;
	    if (*p == '+') {
		p++;
	    }
	}

	/* get exponent */
	e = 0;
	do {
	    e *= 10;
	    e += *p++ - '0';
	    if (e >= 1024) {
		return FALSE;
	    }
	} while (isdigit(*p));

	/* adjust number */
	while (e != 0) {
	    if ((e & 1) != 0) {
		f_mult(&a, t);
		if (a.exp < 0x1000 || a.exp > 0xf000) {
		    break;
		}
	    }
	    e >>= 1;
	    t++;
	}
    }
    if (a.exp >= BIAS + 1023 &&
	(a.exp > BIAS + 1023 || (a.high == 0x7fff && a.low >= 0x7fffff00L))) {
	return FALSE;
    }

    f_ftoxf(&a, f);
    *s = p;
    return TRUE;
}

/*
 * NAME:	float->ftoa()
 * DESCRIPTION:	convert a float to a string
 */
void flt_ftoa(f, buffer)
xfloat *f;
char *buffer;
{
    static flt tenmillion =	{ 0, 0x8016, 0x4c4b, 0x20000000L };
    register unsigned short i;
    register short e;
    register Uint n;
    register char *p;
    register flt *t, *t2;
    char digits[9];
    flt a;

    f_xftof(f, &a);
    if (a.exp == 0) {
	strcpy(buffer, "0");
	return;
    }

    if (a.sign != 0) {
	*buffer++ = '-';
	a.sign = 0;
    }

    /* reduce the float to range 1 .. 9.999999999, and extract exponent */
    e = 0;
    if (a.exp >= BIAS) {
	/* >= 1 */
	for (i = 10, t = &tens[9], t2 = &tenths[9]; i > 0; --i, --t, --t2) {
	    e <<= 1;
	    if (f_cmp(&a, t) >= 0) {
		e |= 1;
		f_mult(&a, t2);
	    }
	}
    } else {
	/* < 1 */
	for (i = 10, t = &tenths[9], t2 = &tens[9]; i > 0; --i, --t, --t2) {
	    e <<= 1;
	    if (f_cmp(&a, t) <= 0) {
		e |= 1;
		f_mult(&a, t2);
	    }
	}
	if (a.exp < BIAS) {
	    /* still < 1 */
	    f_mult(&a, &tens[0]);
	    e++;
	}
	e = -e;
    }
    f_mult(&a, &tenmillion);
    f_37bits(&a);

    /*
     * obtain digits
     */
    f_add(&a, &half);
    i = a.exp - BIAS + 1 - 15;
    n = ((Uint) a.high << i) | (a.low >> (31 - i));
    if (n == 100000000L) {
	p = digits + 7;
	p[0] = '1';
	p[1] = '\0';
	i = 1;
	e++;
    } else {
	while (n != 0 && n % 10 == 0) {
	    n /= 10;
	}
	p = digits + 8;
	*p = '\0';
	i = 0;
	do {
	    i++;
	    *--p = '0' + n % 10;
	    n /= 10;
	} while (n != 0);
    }

    if (e >= 8 || (e < -3 && i - e > 8)) {
	buffer[0] = *p;
	if (i != 1) {
	    buffer[1] = '.';
	    memcpy(buffer + 2, p + 1, i - 1);
	    i++;
	}
	buffer[i++] = 'e';
	if (e >= 0) {
	    buffer[i] = '+';
	} else {
	    buffer[i] = '-';
	    e = -e;
	}
	p = digits + 8;
	do {
	    *--p = '0' + e % 10;
	    e /= 10;
	} while (e != 0);
	strcpy(buffer + i + 1, p);
    } else if (e < 0) {
	e = 1 - e;
	memcpy(buffer, "0.000000", e);
	strcpy(buffer + e, p);
    } else {
	while (e >= 0) {
	    *buffer++ = (*p == '\0') ? '0' : *p++;
	    --e;
	}
	if (*p != '\0') {
	    *buffer = '.';
	    strcpy(buffer + 1, p);
	} else {
	    *buffer = '\0';
	}
    }
}

/*
 * NAME:	float->itof()
 * DESCRIPTION:	convert an integer to a float
 */
void flt_itof(i, f)
Int i;
xfloat *f;
{
    flt a;

    f_itof(i, &a);
    f_ftoxf(&a, f);
}

/*
 * NAME:	float->ftoi()
 * DESCRIPTION:	convert a float to an integer
 */
Int flt_ftoi(f)
xfloat *f;
{
    flt a;

    f_xftof(f, &a);
    f_round(&a);
    return f_ftoi(&a);
}

/*
 * NAME:	float->add()
 * DESCRIPTION:	add two floats
 */
void flt_add(f1, f2)
xfloat *f1, *f2;
{
    flt a, b;

    f_xftof(f2, &b);
    f_xftof(f1, &a);
    f_add(&a, &b);
    f_ftoxf(&a, f1);
}

/*
 * NAME:	float->sub()
 * DESCRIPTION:	subtract a float from a float
 */
void flt_sub(f1, f2)
xfloat *f1, *f2;
{
    flt a, b;

    f_xftof(f2, &b);
    f_xftof(f1, &a);
    f_sub(&a, &b);
    f_ftoxf(&a, f1);
}

/*
 * NAME:	float->mult()
 * DESCRIPTION:	multiply two floats
 */
void flt_mult(f1, f2)
xfloat *f1, *f2;
{
    flt a, b;

    f_xftof(f1, &a);
    f_xftof(f2, &b);
    f_mult(&a, &b);
    f_ftoxf(&a, f1);
}

/*
 * NAME:	float->div()
 * DESCRIPTION:	divide a float by a float
 */
void flt_div(f1, f2)
xfloat *f1, *f2;
{
    flt a, b;

    f_xftof(f2, &b);
    f_xftof(f1, &a);
    f_div(&a, &b);
    f_ftoxf(&a, f1);
}

/*
 * NAME:	float->cmp()
 * DESCRIPTION:	compare two xfloats
 */
int flt_cmp(f1, f2)
register xfloat *f1, *f2;
{
    if ((short) (f1->high ^ f2->high) < 0) {
	return ((short) f1->high < 0) ? -1 : 1;
    }

    if (f1->high == f2->high && f1->low == f2->low) {
	return 0;
    }
    if (f1->high <= f2->high && (f1->high < f2->high || f1->low < f2->low)) {
	return ((short) f1->high < 0) ? 1 : -1;
    }
    return ((short) f1->high < 0) ? -1 : 1;
}

/*
 * NAME:	float->floor()
 * DESCRIPTION:	round a float downwards
 */
void flt_floor(f)
xfloat *f;
{
    flt a, b;

    f_xftof(f, &a);
    b = a;
    f_trunc(&b);
    if (b.sign != 0 && f_cmp(&a, &b) != 0) {
	f_sub(&b, &one);
    }
    f_ftoxf(&b, f);
}

/*
 * NAME:	float->ceil()
 * DESCRIPTION:	round a float upwards
 */
void flt_ceil(f)
xfloat *f;
{
    flt a, b;

    f_xftof(f, &a);
    b = a;
    f_trunc(&b);
    if (b.sign == 0 && f_cmp(&a, &b) != 0) {
	f_add(&b, &one);
    }
    f_ftoxf(&b, f);
}

/*
 * NAME:	float->fmod()
 * DESCRIPTION:	perform fmod
 */
void flt_fmod(f1, f2)
xfloat *f1, *f2;
{
    flt a, b, c;

    f_xftof(f2, &b);
    if (b.exp == 0) {
	f_edom();
    }
    f_xftof(f1, &a);
    if (a.exp == 0) {
	return;
    }

    c.sign = a.sign;
    c.high = b.high;
    c.low = b.low;
    while (f_cmp(&a, &b) >= 0) {
	c.exp = a.exp;
	if (f_cmp(&a, &c) < 0) {
	    c.exp--;
	}
	f_sub(&a, &c);
    }

    f_ftoxf(&a, f1);
}

/*
 * NAME:	float->frexp()
 * DESCRIPTION:	split a float into a fraction and an exponent
 */
Int flt_frexp(f)
register xfloat *f;
{
    short e;

    if (f->high == 0) {
	return 0;
    }
    e = ((f->high & 0x7ff0) >> 4) - 1022;
    f->high = (f->high & 0x800f) | (1022 << 4);
    return e;
}

/*
 * NAME:	float->ldexp()
 * DESCRIPTION:	make a float from a fraction and an exponent
 */
void flt_ldexp(f, exp)
register xfloat *f;
register Int exp;
{
    if (f->high == 0) {
	return;
    }
    exp += (f->high & 0x7ff0) >> 4;
    if (exp <= 0) {
	f->high = 0;
	f->low = 0;
	return;
    }
    if (exp > 1023 + 1023) {
	f_erange();
    }
    f->high = (f->high & 0x800f) | (exp << 4);
}

/*
 * NAME:	float->modf()
 * DESCRIPTION:	split float into fraction and integer part
 */
void flt_modf(f1, f2)
xfloat *f1, *f2;
{
    flt a, b;

    f_xftof(f1, &a);
    if (a.exp < BIAS) {
	b.exp = 0;
    } else {
	b = a;
	f_trunc(&b);
	f_sub(&a, &b);
    }
    f_ftoxf(&a, f1);
    f_ftoxf(&b, f2);
}


/*
 * The algorithms for much of the following are taken from the Cephes Math
 * Library 2.1, by Stephen L. Moshier.
 */

/*
 * NAME:	f_poly()
 * DESCRIPTION:	evaluate polynomial
 */
static void f_poly(x, coef, n)
register flt *x, *coef;
register int n;
{
    flt result;

    result = *coef++;
    do {
	f_mult(&result, x);
	f_add(&result, coef++);
    } while (--n != 0);

    *x = result;
}

/*
 * NAME:	f_poly1()
 * DESCRIPTION:	evaluate polynomial with coefficient of x ** (n + 1) == 1.0.
 */
static void f_poly1(x, coef, n)
register flt *x, *coef;
register int n;
{
    flt result;

    result = *x;
    f_add(&result, coef++);
    do {
	f_mult(&result, x);
	f_add(&result, coef++);
    } while (--n != 0);

    *x = result;
}

/*
 * NAME:	f_exp()
 * DESCRIPTION:	internal version of exp(f)
 */
static void f_exp(a)
register flt *a;
{
    static flt p[] = {
	{ 0x0000, 0x7ff2, 0x4228, 0x01073370L },
	{ 0x0000, 0x7ff9, 0x7c1b, 0x4362a050L },
	{ 0x0000, 0x7fff, 0x4000, 0x00000000L }
    };
    static flt q[] = {
	{ 0x0000, 0x7fec, 0x64bd, 0x3130af58L },
	{ 0x0000, 0x7ff6, 0x52b9, 0x2c76e408L },
	{ 0x0000, 0x7ffc, 0x745c, 0x1b8352c0L },
	{ 0x0000, 0x8000, 0x4000, 0x00000000L }
    };
    static flt log2e = { 0x0000, 0x7fff, 0x5c55, 0x0eca5704L };
    static flt c1 = { 0x0000, 0x7ffe, 0x58c0, 0x00000000L };
    static flt c2 = { 0x0000, 0x7ff2, 0x6f40, 0x20b8c218L };
    flt b, c;
    register short n;

    b = *a;
    f_mult(&b, &log2e);
    f_round(&b);
    n = f_ftoi(&b);
    c = b;
    f_mult(&c, &c1);
    f_sub(a, &c);
    f_mult(&b, &c2);
    f_add(a, &b);

    b = *a;
    f_mult(&b, a);
    c = b;
    f_poly(&c, p, 2);
    f_mult(a, &c);
    f_poly(&b, q, 3);
    f_sub(&b, a);
    f_div(a, &b);

    if (a->exp != 0) {
	a->exp++;
    }
    f_add(a, &one);
    if (a->exp != 0) {
	a->exp += n;
    }
}

/*
 * NAME:	float->exp()
 * DESCRIPTION:	exp(f)
 */
void flt_exp(f)
xfloat *f;
{
    flt a;

    f_xftof(f, &a);
    if (f_cmp(&a, &maxlog) > 0) {
	/* overflow */
	f_erange();
    }
    if (f_cmp(&a, &minlog) < 0) {
	/* underflow */
	a.exp = 0;
    } else {
	f_exp(&a);
    }

    f_ftoxf(&a, f);
}

static flt logp[] = {
    { 0x0000, 0x7ff0, 0x6026, 0x4ed4bf30L },
    { 0x0000, 0x7ffd, 0x7f9f, 0x5db2f2b4L },
    { 0x0000, 0x8001, 0x6902, 0x458cd8e8L },
    { 0x0000, 0x8003, 0x7726, 0x52fc7a84L },
    { 0x0000, 0x8004, 0x7939, 0x5ac9d7b8L },
    { 0x0000, 0x8004, 0x7178, 0x244a33a8L },
    { 0x0000, 0x8003, 0x4f8e, 0x4b136264L }
};
static flt logq[] = {
    { 0x0000, 0x8002, 0x7840, 0x2c1bf7a0L },
    { 0x0000, 0x8005, 0x52bd, 0x5a8f5cf4L },
    { 0x0000, 0x8006, 0x6e55, 0x0548968cL },
    { 0x0000, 0x8007, 0x4cd0, 0x22530620L },
    { 0x0000, 0x8006, 0x6b7a, 0x28551a68L },
    { 0x0000, 0x8004, 0x7755, 0x709d1394L }
};

/*
 * NAME:	float->log()
 * DESCRIPTION:	log(f)
 */
void flt_log(f)
xfloat *f;
{
    static flt r[] = {
	{ 0x8000, 0x7ffe, 0x6510, 0x7bb8d81cL },
	{ 0x0000, 0x8003, 0x418b, 0x78e604f8L },
	{ 0x8000, 0x8005, 0x4024, 0x0c224454L }
    };
    static flt s[] = {
	{ 0x8000, 0x8004, 0x4758, 0x1a87d8dcL },
	{ 0x0000, 0x8007, 0x4e06, 0x002255c8L },
	{ 0x8000, 0x8008, 0x6036, 0x12336680L }
    };
    static flt c1 = { 0x0000, 0x7ff2, 0x6f40, 0x20b8c218L };
    static flt c2 = { 0x0000, 0x7ffe, 0x58c0, 0x00000000L };
    flt a, b, c, d;
    register short n;

    f_xftof(f, &a);
    if (a.sign != 0 || a.exp == 0) {
	/* <= 0.0 */
	f_edom();
    }

    n = a.exp - BIAS + 1;
    a.exp = BIAS - 1;

    if (n > 2 || n < -2) {
	if (f_cmp(&a, &sqrth) < 0) {
	    --n;
	    f_sub(&a, &half);
	    b = a;
	} else {
	    b = a;
	    f_sub(&a, &half);
	    f_sub(&a, &half);
	}
	if (b.exp != 0) {
	    --b.exp;
	}
	f_add(&b, &half);

	f_div(&a, &b);
	b = a;
	f_mult(&b, &b);
	c = b;
	f_poly(&b, r, 2);
	f_mult(&b, &c);
	f_poly1(&c, s, 2);
	f_div(&b, &c);
	f_mult(&b, &a);
	f_add(&a, &b);
    } else {
	if (f_cmp(&a, &sqrth) < 0) {
	    --n;
	    a.exp++;
	}
	f_sub(&a, &one);

	b = a;
	f_mult(&b, &a);
	c = a;
	f_poly(&c, logp, 6);
	f_mult(&c, &b);
	d = a;
	f_poly1(&d, logq, 5);
	f_div(&c, &d);
	f_mult(&c, &a);
	if (b.exp != 0) {
	    --b.exp;
	}
	f_sub(&c, &b);
	f_add(&a, &c);
    }

    if (n != 0) {
	f_itof((Int) n, &b);
	c = b;
	f_mult(&c, &c1);
	f_sub(&a, &c);
	f_mult(&b, &c2);
	f_add(&a, &b);
    }

    f_ftoxf(&a, f);
}

/*
 * NAME:	float->log10()
 * DESCRIPTION:	log10(f)
 */
void flt_log10(f)
xfloat *f;
{
    static flt l102a = { 0x0000, 0x7ffd, 0x4d00, 0x00000000L };
    static flt l102b = { 0x0000, 0x7ff3, 0x4135, 0x04fbcff8L };
    static flt l10ea = { 0x0000, 0x7ffd, 0x6f00, 0x00000000L };
    static flt l10eb = { 0x0000, 0x7ff4, 0x5bd8, 0x549b9438L };
    flt a, b, c, d;
    register short n;

    f_xftof(f, &a);
    if (a.sign != 0 || a.exp == 0) {
	/* <= 0.0 */
	f_edom();
    }

    n = a.exp - BIAS + 1;
    a.exp = BIAS - 1;

    if (f_cmp(&a, &sqrth) < 0) {
	--n;
	a.exp++;
    }
    f_sub(&a, &one);

    b = a;
    f_mult(&b, &a);
    c = a;
    f_poly(&c, logp, 6);
    f_mult(&c, &b);
    d = a;
    f_poly1(&d, logq, 5);
    f_div(&c, &d);
    f_mult(&c, &a);
    if (b.exp != 0) {
	--b.exp;
    }
    f_sub(&c, &b);

    b = a;
    f_add(&b, &c);
    f_mult(&b, &l10eb);
    f_mult(&a, &l10ea);
    f_add(&a, &b);
    f_mult(&c, &l10ea);
    f_add(&a, &c);
    f_itof((Int) n, &b);
    c = b;
    f_mult(&b, &l102b);
    f_add(&a, &b);
    f_mult(&c, &l102a);
    f_add(&a, &c);

    f_ftoxf(&a, f);
}

/*
 * NAME:	f_powi()
 * DESCRIPTION:	take a number to an integer power
 */
static void f_powi(a, n)
register flt *a;
register int n;
{
    flt b;
    unsigned short sign;
    bool neg;

    if (n == 0) {
	/* pow(x, 0.0) == 1.0 */
	*a = one;
	return;
    }

    if (a->exp == 0) {
	if (n < 0) {
	    /* negative power of 0.0 */
	    f_edom();
	}
	/* pow(0.0, y) == 0.0 */
	return;
    }

    sign = a->sign;
    a->sign = 0;

    if (n < 0) {
	neg = TRUE;
	n = -n;
    } else {
	neg = FALSE;
    }

    if (n & 1) {
	b = *a;
    } else {
	b = one;
	sign = 0;
    }
    while ((n >>= 1) != 0) {
	f_mult(a, a);
	if (a->exp > BIAS + 1023) {
	    f_erange();
	}
	if (n & 1) {
	    f_mult(&b, a);
	}
    }
    /* range of b is checked when converting back to xfloat */

    b.sign = sign;
    if (neg) {
	*a = one;
	f_div(a, &b);
    } else {
	*a = b;
    }
}

/*
 * NAME:	float->pow()
 * DESCRIPTION:	pow(f1, f2)
 */
void flt_pow(f1, f2)
xfloat *f1, *f2;
{
    static flt p[] = {
	{ 0x0000, 0x7ffd, 0x7f6e, 0x32feb6b8L },
	{ 0x0000, 0x8000, 0x7777, 0x5fd53dc0L },
	{ 0x0000, 0x8001, 0x7b32, 0x7afef1d8L },
	{ 0x0000, 0x8001, 0x4aaa, 0x076cb938L }
    };
    static flt q[] = {
	{ 0x0000, 0x8002, 0x4aaa, 0x69364124L },
	{ 0x0000, 0x8003, 0x6fff, 0x7e838394L },
	{ 0x0000, 0x8004, 0x4332, 0x78362ec8L },
	{ 0x0000, 0x8002, 0x6fff, 0x0b2315d4L }
    };
    static flt aloga[] = {
	{ 0x0000, 0x7fff, 0x4000, 0x00000000L },
	{ 0x0000, 0x7ffe, 0x7a92, 0x5f454920L },
	{ 0x0000, 0x7ffe, 0x7560, 0x31b9f748L },
	{ 0x0000, 0x7ffe, 0x7066, 0x37bb0aa4L },
	{ 0x0000, 0x7ffe, 0x6ba2, 0x3f32b5a8L },
	{ 0x0000, 0x7ffe, 0x6712, 0x230547e0L },
	{ 0x0000, 0x7ffe, 0x62b3, 0x4a845540L },
	{ 0x0000, 0x7ffe, 0x5e84, 0x28e7d604L },
	{ 0x0000, 0x7ffe, 0x5a82, 0x3cccfe78L },
	{ 0x0000, 0x7ffe, 0x56ac, 0x0fba90a8L },
	{ 0x0000, 0x7ffe, 0x52ff, 0x35aa6c54L },
	{ 0x0000, 0x7ffe, 0x4f7a, 0x4c982468L },
	{ 0x0000, 0x7ffe, 0x4c1b, 0x7c146370L },
	{ 0x0000, 0x7ffe, 0x48e1, 0x74dceac4L },
	{ 0x0000, 0x7ffe, 0x45ca, 0x7078faa4L },
	{ 0x0000, 0x7ffe, 0x42d5, 0x30d9f314L },
	{ 0x0000, 0x7ffe, 0x4000, 0x00000000L }
    };
    static flt alogb[] = {
	{ 0x0000, 0x0000, 0x0000, 0x00000000L },
	{ 0x0000, 0x7fc7, 0x4bb4, 0x05aeb670L },
	{ 0x0000, 0x7fc8, 0x5e87, 0x1a68bb98L },
	{ 0x0000, 0x7fc8, 0x5ba7, 0x62ad0c98L },
	{ 0x8000, 0x7fc8, 0x6f74, 0x682764c8L },
	{ 0x0000, 0x7fc6, 0x750e, 0x2f5fd884L },
	{ 0x0000, 0x7fc7, 0x5bd1, 0x55a46304L },
	{ 0x8000, 0x7fc7, 0x4641, 0x0373af14L },
	{ 0x0000, 0x0000, 0x0000, 0x00000000L }
    };
    static flt r[] = {
	{ 0x0000, 0x7fee, 0x7d8c, 0x0fafe528L },
	{ 0x0000, 0x7ff2, 0x50be, 0x7cc1f924L },
	{ 0x0000, 0x7ff5, 0x5761, 0x7d9095e0L },
	{ 0x0000, 0x7ff8, 0x4eca, 0x56dde268L },
	{ 0x0000, 0x7ffa, 0x71ac, 0x11ae0834L },
	{ 0x0000, 0x7ffc, 0x7afe, 0x7bff058cL },
	{ 0x0000, 0x7ffe, 0x58b9, 0x05fdf474L }
    };
    static flt log2ea = { 0x0000, 0x7ffd, 0x7154, 0x3b295c18L };
    static flt sixteenth = { 0x0000, 0x7ffb, 0x4000, 0x00000000L };
    flt a, b, c, d, e;
    register int n, i;
    unsigned short sign;

    f_xftof(f1, &a);
    f_xftof(f2, &b);

    c = b;
    f_trunc(&c);
    if (f_cmp(&b, &c) == 0 && b.exp < 0x800e) {
	/* integer power < 32768 */
	f_powi(&a, (int) f_ftoi(&c));
	f_ftoxf(&a, f1);
	return;
    }

    sign = a.sign;
    if (sign != 0) {
	if (f_cmp(&b, &c) != 0) {
	    /* non-integer power of negative number */
	    f_edom();
	}
	a.sign = 0;
	--c.exp;
	f_trunc(&c);
	if (f_cmp(&b, &c) == 0) {
	    /* even power of negative number */
	    sign = 0;
	}
    }
    if (a.exp == 0) {
	if (b.sign != 0) {
	    /* negative power of 0.0 */
	    f_edom();
	}
	/* pow(0.0, y) == 0.0 */
	return;
    }

    n = a.exp - BIAS + 1;
    a.exp = BIAS - 1;

    if (f_cmp(&a, &aloga[1]) >= 0) {
	i = 0;
    } else {
	i = 1;
	if (f_cmp(&a, &aloga[9]) <= 0) {
	    i = 9;
	}
	if (f_cmp(&a, &aloga[i + 4]) <= 0) {
	    i += 4;
	}
	if (f_cmp(&a, &aloga[i + 2]) <= 0) {
	    i += 2;
	}
	i++;
    }
    f_sub(&a, &aloga[i]);
    f_sub(&a, &alogb[i >> 1]);
    f_div(&a, &aloga[i]);

    c = a;
    f_mult(&c, &a);
    d = a;
    f_poly(&d, p, 3);
    f_mult(&d, &c);
    e = a;
    f_poly1(&e, q, 3);
    f_div(&d, &e);
    f_mult(&d, &a);
    if (c.exp != 0) {
	--c.exp;
    }
    f_sub(&d, &c);

    c = d;
    f_mult(&d, &log2ea);
    f_add(&c, &d);
    d = a;
    f_mult(&d, &log2ea);
    f_add(&c, &d);
    f_add(&c, &a);
    f_mult(&c, &b);

    f_itof((Int) -i, &d);
    if (d.exp != 0) {
	d.exp -= 4;
    }
    f_itof((Int) n, &e);
    f_add(&d, &e);

    e = b;
    e.exp += 4;
    f_trunc(&e);
    if (e.exp != 0) {
	e.exp -= 4;
    }
    f_sub(&b, &e);

    f_mult(&b, &d);
    f_add(&c, &b);
    b = c;
    if (b.exp != 0) {
	b.exp += 4;
	f_trunc(&b);
	if (b.exp != 0) {
	    b.exp -= 4;
	}
    }
    f_sub(&c, &b);

    f_mult(&d, &e);
    f_add(&b, &d);
    d = b;
    if (d.exp != 0) {
	d.exp += 4;
	f_trunc(&d);
	if (d.exp != 0) {
	    d.exp -= 4;
	}
    }
    f_sub(&b, &d);

    f_add(&b, &c);
    c = b;
    if (c.exp != 0) {
	c.exp += 4;
	f_trunc(&c);
	if (c.exp != 0) {
	    c.exp -= 4;
	}
    }
    f_add(&d, &c);
    if (d.exp != 0) {
	d.exp += 4;
    }

    if (d.exp >= 0x800d) {
	/* exponent >= 16384 */
	if (d.sign == 0) {
	    /* overflow */
	    f_erange();
	}
	/* underflow */
	a.exp = 0;
	f_ftoxf(&a, f1);
	return;
    }
    n = f_ftoi(&d);
    f_sub(&b, &c);
    if (b.sign == 0 && b.exp != 0) {
	n++;
	f_sub(&b, &sixteenth);
    }

    a = b;
    f_poly(&a, r, 6);
    f_mult(&a, &b);

    i = n / 16 + ((n < 0) ? 0 : 1);
    n = i * 16 - n;
    f_mult(&a, &aloga[n]);
    f_add(&a, &aloga[n]);
    if (a.exp != 0) {
	a.exp += i;
    }
    a.sign = sign;

    f_ftoxf(&a, f1);
}

/*
 * NAME:	f_sqrt()
 * DESCRIPTION:	internal version of sqrt(f)
 */
static void f_sqrt(a)
register flt *a;
{
    static flt c1 = { 0x0000, 0x7ffe, 0x4b8a, 0x371e5fa0L };
    static flt c2 = { 0x0000, 0x7ffd, 0x6ad4, 0x55de691cL };
    static flt sqrt2 = { 0x0000, 0x7fff, 0x5a82, 0x3cccfe78L };
    flt b, c;
    register int n;

    if (a->exp == 0) {
	return;
    }

    b = *a;
    n = a->exp - BIAS + 1;
    a->exp = BIAS - 1;
    f_mult(a, &c1);
    f_add(a, &c2);
    if (n & 1) {
	f_mult(a, &sqrt2);
    }
    a->exp += n >> 1;

    c = b;
    f_div(&c, a);
    f_add(a, &c);
    --a->exp;
    c = b;
    f_div(&c, a);
    f_add(a, &c);
    --a->exp;
    f_div(&b, a);
    f_add(a, &b);
    --a->exp;
}

/*
 * NAME:	float->sqrt()
 * DESCRIPTION:	sqrt(f)
 */
void flt_sqrt(f)
xfloat *f;
{
    flt a;

    f_xftof(f, &a);
    if (a.sign != 0) {
	f_edom();
    }
    f_sqrt(&a);
    f_ftoxf(&a, f);
}

static flt sincof[] = {
    { 0x0000, 0x7fde, 0x5763, 0x7a3fa338L },
    { 0x8000, 0x7fe5, 0x6b97, 0x4b525240L },
    { 0x0000, 0x7fec, 0x5c77, 0x46acfa90L },
    { 0x8000, 0x7ff2, 0x6806, 0x40337fc0L },
    { 0x0000, 0x7ff8, 0x4444, 0x222221f0L },
    { 0x8000, 0x7ffc, 0x5555, 0x2aaaaaacL }
};
static flt coscof[] = {
    { 0x8000, 0x7fda, 0x63e9, 0x13410c34L },
    { 0x0000, 0x7fe2, 0x47ba, 0x3af69c80L },
    { 0x8000, 0x7fe9, 0x49f9, 0x1efd5898L },
    { 0x0000, 0x7fef, 0x6806, 0x40339088L },
    { 0x8000, 0x7ff5, 0x5b05, 0x582d82a0L },
    { 0x0000, 0x7ffa, 0x5555, 0x2aaaaaacL }
};
static flt sc1 = { 0x0000, 0x7ffe, 0x6487, 0x76800000L };
static flt sc2 = { 0x0000, 0x7fe6, 0x5110, 0x5a000000L };
static flt sc3 = { 0x0000, 0x7fce, 0x611a, 0x313198a4L };

/*
 * NAME:	float->cos()
 * DESCRIPTION:	cos(f)
 */
void flt_cos(f)
xfloat *f;
{
    flt a, b, c;
    register int n;
    unsigned short sign;

    f_xftof(f, &a);
    if (a.exp >= 0x801d) {
	f_edom();
    }

    a.sign = sign = 0;
    b = a;
    f_div(&b, &pio4);
    f_trunc(&b);
    n = f_ftoi(&b);
    if (n & 1) {
	n++;
	f_add(&b, &one);
    }
    n &= 7;
    if (n > 3) {
	sign = 0x8000;
	n -= 4;
    }
    if (n > 1) {
	sign ^= 0x8000;
    }

    c = b;
    f_mult(&c, &sc1);
    f_sub(&a, &c);
    c = b;
    f_mult(&c, &sc2);
    f_sub(&a, &c);
    f_mult(&b, &sc3);
    f_sub(&a, &b);

    b = a;
    f_mult(&b, &a);
    if (n == 1 || n == 2) {
	c = b;
	f_mult(&b, &a);
	f_poly(&c, sincof, 5);
    } else {
	a = one;
	c = b;
	if (c.exp != 0) {
	    --c.exp;
	}
	f_sub(&a, &c);
	c = b;
	f_mult(&b, &b);
	f_poly(&c, coscof, 5);
    }
    f_mult(&b, &c);
    f_add(&a, &b);
    a.sign ^= sign;

    f_ftoxf(&a, f);
}

/*
 * NAME:	float->sin()
 * DESCRIPTION:	sin(f)
 */
void flt_sin(f)
xfloat *f;
{
    flt a, b, c;
    register int n;
    unsigned short sign;

    f_xftof(f, &a);
    if (a.exp >= 0x801d) {
	f_edom();
    }

    sign = a.sign;
    a.sign = 0;
    b = a;
    f_div(&b, &pio4);
    f_trunc(&b);
    n = f_ftoi(&b);
    if (n & 1) {
	n++;
	f_add(&b, &one);
    }
    n &= 7;
    if (n > 3) {
	sign ^= 0x8000;
	n -= 4;
    }

    c = b;
    f_mult(&c, &sc1);
    f_sub(&a, &c);
    c = b;
    f_mult(&c, &sc2);
    f_sub(&a, &c);
    f_mult(&b, &sc3);
    f_sub(&a, &b);

    b = a;
    f_mult(&b, &a);
    if (n == 1 || n == 2) {
	a = one;
	c = b;
	if (c.exp != 0) {
	    --c.exp;
	}
	f_sub(&a, &c);
	c = b;
	f_mult(&b, &b);
	f_poly(&c, coscof, 5);
    } else {
	c = b;
	f_mult(&b, &a);
	f_poly(&c, sincof, 5);
    }
    f_mult(&b, &c);
    f_add(&a, &b);
    a.sign ^= sign;

    f_ftoxf(&a, f);
}

/*
 * NAME:	float->tan()
 * DESCRIPTION:	float(f)
 */
void flt_tan(f)
xfloat *f;
{
    static flt p[] = {
	{ 0x8000, 0x800c, 0x664b, 0x31a49e80L },
	{ 0x0000, 0x8013, 0x4667, 0x594bf93cL },
	{ 0x8000, 0x8017, 0x447f, 0x55a65324L }
    };
    static flt q[] = {
	{ 0x0000, 0x800c, 0x6ae2, 0x4bdd66ccL },
	{ 0x8000, 0x8013, 0x509e, 0x78b05578L },
	{ 0x0000, 0x8017, 0x5f66, 0x1f85d5b0L },
	{ 0x8000, 0x8018, 0x66bf, 0x40797cb4L }
    };
    static flt p1 = { 0x0000, 0x7ffe, 0x6487, 0x76800000L };
    static flt p2 = { 0x0000, 0x7fe6, 0x5110, 0x5a000000L };
    static flt p3 = { 0x0000, 0x7fce, 0x611a, 0x313198a4L };
    flt a, b, c;
    register int n;
    unsigned short sign;

    f_xftof(f, &a);
    if (a.exp >= 0x801d) {
	f_edom();
    }

    sign = a.sign;
    a.sign = 0;
    b = a;
    f_div(&b, &pio4);
    f_trunc(&b);
    n = f_ftoi(&b);
    if (n & 1) {
	n++;
	f_add(&b, &one);
    }

    c = b;
    f_mult(&c, &p1);
    f_sub(&a, &c);
    c = b;
    f_mult(&c, &p2);
    f_sub(&a, &c);
    f_mult(&b, &p3);
    f_sub(&a, &b);

    b = a;
    f_mult(&b, &a);
    if (b.exp > 0x7fd0) {	/* ~1e-14 */
	c = b;
	f_poly(&b, p, 2);
	f_mult(&b, &c);
	f_poly1(&c, q, 3);
	f_div(&b, &c);
	f_mult(&b, &a);
	f_add(&a, &b);
    }

    if (n & 2) {
	b = one;
	f_div(&b, &a);
	a = b;
	a.sign ^= 0x8000;
    }
    a.sign ^= sign;

    f_ftoxf(&a, f);
}

static flt ascp[] = {
    { 0x8000, 0x7ffe, 0x5931, 0x3dd1792cL },
    { 0x0000, 0x8002, 0x5147, 0x2a1c6244L },
    { 0x8000, 0x8004, 0x4f77, 0x6c7ab96cL },
    { 0x0000, 0x8004, 0x7295, 0x0d081500L },
    { 0x8000, 0x8003, 0x6da8, 0x634b0bb0L }
};
static flt ascq[] = {
    { 0x8000, 0x8003, 0x5f58, 0x7442cc70L },
    { 0x0000, 0x8006, 0x4b8c, 0x15abd1acL },
    { 0x8000, 0x8007, 0x5f95, 0x630cc2e0L },
    { 0x0000, 0x8007, 0x6871, 0x0dbab9b8L },
    { 0x8000, 0x8006, 0x523e, 0x4a7848c4L }
};

/*
 * NAME:	float->acos()
 * DESCRIPTION:	acos(f)
 */
void flt_acos(f)
xfloat *f;
{
    flt a, b, c;
    unsigned short sign;
    bool flag;

    f_xftof(f, &a);
    sign = a.sign;
    a.sign = 0;
    if (f_cmp(&a, &one) > 0) {
	f_edom();
    }

    if (f_cmp(&a, &half) > 0) {
	b = half;
	f_sub(&b, &a);
	f_add(&b, &half);
	if (b.exp != 0) {
	    --b.exp;
	}
	a = b;
	f_sqrt(&a);
	flag = TRUE;
    } else {
	b = a;
	f_mult(&b, &a);
	flag = FALSE;
    }

    if (a.exp >= 0x7fe7) {	/* ~1e-7 */
	c = b;
	f_poly(&c, ascp, 4);
	f_mult(&c, &b);
	f_poly1(&b, ascq, 4);
	f_div(&c, &b);
	f_mult(&c, &a);
	f_add(&a, &c);
    }

    if (flag) {
	if (a.exp != 0) {
	    a.exp++;
	}
	if (sign != 0) {
	    b = pi;
	    f_sub(&b, &a);
	    a = b;
	}
    } else {
	if (sign != 0) {
	    f_add(&a, &pio2);
	} else {
	    b = pio2;
	    f_sub(&b, &a);
	    a = b;
	}
    }

    f_ftoxf(&a, f);
}

/*
 * NAME:	float->asin()
 * DESCRIPTION:	asin(f)
 */
void flt_asin(f)
xfloat *f;
{
    flt a, b, c;
    unsigned short sign;
    bool flag;

    f_xftof(f, &a);
    sign = a.sign;
    a.sign = 0;
    if (f_cmp(&a, &one) > 0) {
	f_edom();
    }

    if (f_cmp(&a, &half) > 0) {
	b = half;
	f_sub(&b, &a);
	f_add(&b, &half);
	if (b.exp != 0) {
	    --b.exp;
	}
	a = b;
	f_sqrt(&a);
	flag = TRUE;
    } else {
	b = a;
	f_mult(&b, &a);
	flag = FALSE;
    }

    if (a.exp >= 0x7fe7) {	/* ~1e-7 */
	c = b;
	f_poly(&c, ascp, 4);
	f_mult(&c, &b);
	f_poly1(&b, ascq, 4);
	f_div(&c, &b);
	f_mult(&c, &a);
	f_add(&a, &c);
    }

    if (flag) {
	if (a.exp != 0) {
	    a.exp++;
	}
	b = pio2;
	f_sub(&b, &a);
	a = b;
    }
    a.sign ^= sign;

    f_ftoxf(&a, f);
}

static flt atp[] = {
    { 0x8000, 0x7ffe, 0x6ba5, 0x2175f64cL },
    { 0x8000, 0x8002, 0x46b5, 0x3c27114cL },
    { 0x8000, 0x8003, 0x5763, 0x7b6b8ba4L },
    { 0x8000, 0x8002, 0x76a5, 0x2457275cL }
};
static flt atq[] = {
    { 0x0000, 0x8002, 0x7bfa, 0x59b1a2acL },
    { 0x0000, 0x8004, 0x7d94, 0x68676274L },
    { 0x0000, 0x8005, 0x5c3c, 0x7b2444acL },
    { 0x0000, 0x8004, 0x58fb, 0x7b415d88L }
};
static flt t3p8 = { 0x0000, 0x8000, 0x4d41, 0x1e667f3cL };
static flt tp8 = { 0x0000, 0x7ffd, 0x6a09, 0x7333f9e0L };

/*
 * NAME:	float->atan()
 * DESCRIPTION:	atan(f)
 */
void flt_atan(f)
xfloat *f;
{
    flt a, b, c, d, e;
    unsigned short sign;

    f_xftof(f, &a);
    sign = a.sign;
    a.sign = 0;

    if (f_cmp(&a, &t3p8) > 0) {
	b = pio2;
	c = one;
	f_div(&c, &a);
	a = c;
	a.sign = 0x8000;
    } else if (f_cmp(&a, &tp8) > 0) {
	b = pio4;
	c = a;
	f_sub(&a, &one);
	f_add(&c, &one);
	f_div(&a, &c);
    } else {
	b.exp = 0;
    }

    c = a;
    f_mult(&c, &a);
    d = e = c;
    f_poly(&c, atp, 3);
    f_poly1(&d, atq, 3);
    f_div(&c, &d);
    f_mult(&c, &e);
    f_mult(&c, &a);
    f_add(&c, &b);
    f_add(&a, &c);
    a.sign ^= sign;

    f_ftoxf(&a, f);
}

/*
 * NAME:	float->atan2()
 * DESCRIPTION:	atan2(f)
 */
void flt_atan2(f1, f2)
xfloat *f1, *f2;
{
    flt a, b, c, d, e;
    unsigned short asign, bsign;

    f_xftof(f1, &a);
    f_xftof(f2, &b);

    if (b.exp == 0) {
	if (a.exp == 0) {
	    /* atan2(0.0, 0.0); */
	    return;
	}
	a.exp = pio2.exp;
	a.high = pio2.high;
	a.low = pio2.low;
	f_ftoxf(&a, f1);
	return;
    }
    if (a.exp == 0) {
	if (b.sign != 0) {
	    a = pi;
	}
	f_ftoxf(&a, f1);
	return;
    }

    asign = a.sign;
    bsign = b.sign;
    f_div(&a, &b);
    a.sign = 0;

    if (f_cmp(&a, &t3p8) > 0) {
	b = pio2;
	c = one;
	f_div(&c, &a);
	a = c;
	a.sign = 0x8000;
    } else if (f_cmp(&a, &tp8) > 0) {
	b = pio4;
	c = a;
	f_sub(&a, &one);
	f_add(&c, &one);
	f_div(&a, &c);
    } else {
	b.exp = 0;
    }

    c = a;
    f_mult(&c, &a);
    d = e = c;
    f_poly(&c, atp, 3);
    f_poly1(&d, atq, 3);
    f_div(&c, &d);
    f_mult(&c, &e);
    f_mult(&c, &a);
    f_add(&c, &b);
    f_add(&a, &c);
    a.sign ^= asign ^ bsign;

    if (bsign != 0) {
	if (asign == 0) {
	    f_add(&a, &pi);
	} else {
	    f_sub(&a, &pi);
	}
    }

    f_ftoxf(&a, f1);
}

/*
 * NAME:	float->cosh()
 * DESCRIPTION:	cosh(f)
 */
void flt_cosh(f)
xfloat *f;
{
    flt a, b;

    f_xftof(f, &a);
    a.sign = 0;
    if (f_cmp(&a, &maxlog) > 0) {
	f_erange();
    }

    f_exp(&a);
    b = one;
    f_div(&b, &a);
    f_add(&a, &b);
    --a.exp;

    f_ftoxf(&a, f);
}

/*
 * NAME:	float->sinh()
 * DESCRIPTION:	sinh(f)
 */
void flt_sinh(f)
xfloat *f;
{
    static flt p[] = {
	{ 0x8000, 0x7ffe, 0x650d, 0x3fd17678L },
	{ 0x8000, 0x8006, 0x51dc, 0x74731fe8L },
	{ 0x8000, 0x800c, 0x5a52, 0x718e3ac4L },
	{ 0x8000, 0x8011, 0x55e0, 0x57b7ed58L }
    };
    static flt q[] = {
	{ 0x8000, 0x8007, 0x456d, 0x412dd2c8L },
	{ 0x0000, 0x800e, 0x469e, 0x74fdae44L },
	{ 0x8000, 0x8014, 0x4068, 0x41c9f200L }
    };
    flt a, b, c, d;
    unsigned short sign;

    f_xftof(f, &a);
    if (f_cmp(&a, &maxlog) > 0 || f_cmp(&a, &minlog) < 0) {
	f_erange();
    }

    sign = a.sign;
    a.sign = 0;

    if (f_cmp(&a, &one) > 0) {
	f_exp(&a);
	b = half;
	f_div(&b, &a);
	--a.exp;
	f_sub(&a, &b);
	a.sign ^= sign;
    } else {
	b = a;
	f_mult(&b, &a);
	c = d = b;
	f_poly(&c, p, 3);
	f_poly1(&d, q, 2);
	f_div(&c, &d);
	f_mult(&b, &a);
	f_mult(&b, &c);
	f_add(&a, &b);
    }

    f_ftoxf(&a, f);
}

/*
 * NAME:	float->tanh()
 * DESCRIPTION:	tanh(f)
 */
void flt_tanh(f)
xfloat *f;
{
    static flt p[] = {
	{ 0x8000, 0x7ffe, 0x7b71, 0x3755fae0L },
	{ 0x8000, 0x8005, 0x6349, 0x541c4cd0L },
	{ 0x8000, 0x8009, 0x64eb, 0x0060b00cL }
    };
    static flt q[] = {
	{ 0x0000, 0x8005, 0x70cf, 0x6514b038L },
	{ 0x0000, 0x800a, 0x45db, 0x741caa6cL },
	{ 0x0000, 0x800b, 0x4bb0, 0x20488408L }
    };
    static flt mlog2 = { 0x0000, 0x8007, 0x588c, 0x57baf578L };
    static flt d625 = { 0x0000, 0x7ffe, 0x5000, 0x00000000L };
    static flt two = { 0x0000, 0x8000, 0x4000, 0x00000000L };
    flt a, b, c, d;
    unsigned short sign;

    f_xftof(f, &a);
    sign = a.sign;
    a.sign = 0;

    if (f_cmp(&a, &mlog2) > 0) {
	a.exp = one.exp;
	a.high = one.high;
	a.low = one.low;
    } else if (f_cmp(&a, &d625) >= 0) {
	a.exp++;
	f_exp(&a);
	f_add(&a, &one);
	b = two;
	f_div(&b, &a);
	a = one;
	f_sub(&a, &b);
    } else {
	b = a;
	f_mult(&b, &a);
	c = d = b;
	f_poly(&c, p, 2);
	f_poly1(&d, q, 2);
	f_div(&c, &d);
	f_mult(&b, &c);
	f_mult(&b, &a);
	f_add(&a, &b);
    }
    a.sign = sign;

    f_ftoxf(&a, f);
}
