/* xlmath3 - xlisp math functions modified and augmented to correspond */ /* more closely to Common Lisp standard */ /* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney */ /* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz */ /* You may give out copies of this software; for conditions see the */ /* file COPYING included with this distribution. */ #include "xlisp.h" #ifdef BIGNUMS /* This code supports rational, bignum and complex arithmetic. It is * based on the following assumptions: * * 1) Fixnums use the full range of a FIXTYPE in 2's complement, so * that overflow of addition and subtraction can be detected by * checking signs. * * 2) The range of a FIXTYPE is at least [-2^31,2^31-1]. Floating * point products of integers with results in this range are exact and * are coerced exactly to the right integer by an assignment. For * systems with larger FITYPE ranges, BIGNUM arithmetic is used for * larger products and the results are demoted to FIXNUM's * * 3) SFIXMIN <= -1 and SFIXMAX >= 2 so that FIXZERO, FIXONE, FIXTWO, * and FIXNEGTWO can be computed withot allocation or GC. Eventually * they should be pre-computed in xlinit.c. * * The general approach used in this implementation is to code fixnum, * flonum and complex flonum operations for efficiency. Other * operations are coded for simplicity, with recursion to simpler * types used wherever possible. * * Internal operations like add2 assume arguments are protected. */ #define IN 0 #define BG 1 #define RT 2 #define FL 3 #define CR 4 #define CF 5 #ifndef MAXFLOFIX #define MAXFLOFIX 2147483647.0 #endif #ifndef MINFLOFIX #define MINFLOFIX -2147483648.0 #endif #define goodisum(x, y, z) (((x) > 0) ? ((y) < (z)) : ! ((y) < (z))) #define goodidiff(x, y, z) (!(((x < 0) ^ (y < 0)) && ((z < 0) ^ (x < 0)))) #define infixnumrange(x) (MINFLOFIX <= (x) && (x) <= MAXFLOFIX) #define FIXZERO cvfixnum((FIXTYPE) 0) #define FIXONE cvfixnum((FIXTYPE) 1) #define FIXTWO cvfixnum((FIXTYPE) 2) #define FIXNEGTWO cvfixnum((FIXTYPE) -2) #define fixzerop(x) (fixp(x) && getfixnum(x) == 0) #define fixonep(x) (fixp(x) && getfixnum(x) == 1) #define realpart(x) (complexp(x) ? getreal(x) : x) #define imagpart(x) (complexp(x) ? getimag(x) : FIXZERO) #define numerator(x) (ratiop(x) ? getnumer(x) : x) #define denominator(x) (ratiop(x) ? getdenom(x) : FIXONE) #define as_flotype(x) (floatp(x) ? getflonum(x) : makefloat(x)) #define as_bignum(x) \ (bignump(x) ? x : fixp(x) ? cvtfixbignum(getfixnum(x)) : xlbadtype(x)) #define cvcomplex(c) newdcomplex((c).r,(c).i) #define lss2(x,y) (compare2(x,y) < 0) #define leq2(x,y) (compare2(x,y) <= 0) #define geq2(x,y) (compare2(x,y) >= 0) #define gtr2(x,y) (compare2(x,y) > 0) #define neq2(x,y) (! equ2(x, y)) /* Function prototypes */ LOCAL VOID badzero(V); LOCAL double logarithm P3H(FLOTYPE, FLOTYPE, int); LOCAL int zerop P1H(LVAL); LOCAL int plusp P1H(LVAL); LOCAL int minusp P1H(LVAL); LOCAL int evenp P1H(LVAL); LOCAL int oddp P1H(LVAL); LOCAL int commontype P2H(LVAL, LVAL); LOCAL int compare2 P2H(LVAL, LVAL); LOCAL int equ2 P2H(LVAL, LVAL); LOCAL LVAL uminus P1H(LVAL); LOCAL LVAL add2 P2H(LVAL, LVAL); LOCAL LVAL sub2 P2H(LVAL, LVAL); LOCAL LVAL mul2 P2H(LVAL, LVAL); LOCAL LVAL div2 P2H(LVAL, LVAL); #ifdef XLISP_STAT LOCAL LVAL fdiv2 P2H(LVAL, LVAL); #endif /* XLISP_STAT */ LOCAL LVAL min2 P2H(LVAL, LVAL); LOCAL LVAL max2 P2H(LVAL, LVAL); LOCAL VOID rational_idiv2 P5H(LVAL, LVAL, LVAL *, LVAL *, int); LOCAL VOID flonum_idiv2 P5H(FLOTYPE, FLOTYPE, FLOTYPE *, FLOTYPE *, int); LOCAL VOID idiv2 P5H(LVAL, LVAL, LVAL *, LVAL *, int); LOCAL LVAL add1 P1H(LVAL); LOCAL LVAL sub1 P1H(LVAL); LOCAL LVAL gcd2 P2H(LVAL, LVAL); #ifdef applec /* a gross hack */ VOID MODF(double x, double *y) { extended value; modf(x, &value); *y = value; } #else #define MODF modf #endif /* applec */ /* Error checking for illegal zero argument */ LOCAL VOID badzero(V) { xlfail("illegal zero argument"); } #define checkzero(x) if (zerop(x)) badzero(); #define checkizero(x) if (x == 0) badzero(); #define checkfzero(x) if (x == 0.0) badzero(); /* Complex number functions */ double d_sign P2C(double *, a, double *, b) { /* from the f2c distribution */ double x; x = *a >= 0 ? *a : - *a; return *b >= 0 ? x : -x; } double z_abs P1C(dcomplex *, z) { /* from the f2c distribution */ double real, imag; double temp; real = z->r; imag = z->i; if(real < 0) real = -real; if(imag < 0) imag = -imag; if(imag > real){ temp = real; real = imag; imag = temp; } if((real+imag) == real) return(real); temp = imag/real; temp = real*sqrt(1.0 + temp*temp); /*overflow!!*/ return(temp); } VOID z_div P3C(dcomplex *, c, dcomplex *, a, dcomplex *, b) { /* from the f2c distribution */ double ratio, den; double abr, abi; dcomplex r; if( (abr = b->r) < 0.) abr = - abr; if( (abi = b->i) < 0.) abi = - abi; if( abr <= abi ) { if(abi == 0) xlfail("complex division by zero"); ratio = b->r / b->i ; den = b->i * (1 + ratio*ratio); r.r = (a->r*ratio + a->i) / den; r.i = (a->i*ratio - a->r) / den; } else { ratio = b->i / b->r ; den = b->r * (1 + ratio*ratio); r.r = (a->r + a->i*ratio) / den; r.i = (a->i - a->r*ratio) / den; } c->r = r.r, c->i = r.i; } VOID z_sqrt P2C(dcomplex *, r, dcomplex *, z) { /* from the f2c distribution */ double mag; dcomplex v; if((mag = z_abs(z)) == 0.0) v.r = v.i = 0.0; else if(z->r > 0) { v.r = sqrt(0.5 * (mag + z->r)); v.i = z->i / v.r / 2; } else { v.i = sqrt(0.5 * (mag - z->r)); if(z->i < 0) v.i = - v.i; v.r = z->i / v.i / 2; } r->r = v.r, r->i = v.i; } LOCAL VOID makecomplex P2C(dcomplex *, c, LVAL, x) { if (realp(x)) { c->r = makefloat(x); c->i = 0.0; } else if (complexp(x)) { c->r = makefloat(getreal(x)); c->i = makefloat(getimag(x)); } else xlerror("not a number", x); } LOCAL double z_phase P1C(dcomplex *, c) { double phi; if (c->r == 0.0) { if (c->i > 0.0) phi = PI / 2; else if (c->i == 0.0) phi = 0.0; else phi = -PI / 2; } else phi = atan2(c->i, c->r); return(phi); } LOCAL VOID z_exp P2C(dcomplex *, r, dcomplex *, z) { /* from the f2c distribution */ double expx; expx = exp(z->r); r->r = expx * cos(z->i); r->i = expx * sin(z->i); } LOCAL VOID z_log P2C(dcomplex *, r, dcomplex *, z) { /* adapted from the f2c distribution */ double mod; dcomplex v; mod = z_abs(z); checkfzero(mod); v.r = log(mod); v.i = atan2(z->i, z->r); r->r = v.r, r->i = v.i; } LOCAL VOID z_expt P3C(dcomplex *, r, dcomplex *, a, dcomplex *, b) { /* adapted from pow_zz.c in the f2c distribution */ if (b->r == 0.0 && b->i == 0.0) r->r = 1.0, r->i = 0.0; else if (a->r == 0.0 && a->i == 0.0) r->r = 0.0, r->i = 0.0; else { double logr, logi, x, y; logr = log(z_abs(a)); logi = atan2(a->i, a->r); x = exp(logr * b->r - logi * b->i); y = logr * b->i + logi * b->r; r->r = x * cos(y); r->i = x * sin(y); } } LOCAL VOID z_sin P2C(dcomplex *, r, dcomplex *, c) { /* Better code by Hume Smith, 11/92 */ dcomplex val; val.r = sin(c->r) * cosh(c->i); val.i = cos(c->r) * sinh(c->i); r->r = val.r, r->i = val.i; } LOCAL VOID z_cos P2C(dcomplex *, r, dcomplex *, c) { /* Better code by Hume Smith, 11/92 */ dcomplex val; val.r = cos(c->r) * cosh(c->i); val.i = -sin(c->r) * sinh(c->i); r->r = val.r, r->i = val.i; } LOCAL VOID z_tan P2C(dcomplex *, ret_val, dcomplex *, z) { /* Adapted from ctan.f in FN from NETLIB. */ /* This function does not attempt to signal loss of precision. */ double x2, y2; double den, sn2x; x2 = z->r * 2.0; y2 = z->i * 2.0; sn2x = sin(x2); den = cos(x2) + cosh(y2); checkfzero(den); ret_val->r = sn2x / den, ret_val->i = sinh(y2) / den; return; } LOCAL VOID z_asin P2C(dcomplex *, r, dcomplex *, c) { /* Improved code by Hume Smith 11/92 */ dcomplex sx, val; /* compute sx = 1 - c*c */ sx.r = 1.0 - c->r*c->r + c->i*c->i; sx.i = -2.0*c->r*c->i; /* compute sx = i*c + sqrt(sx) */ z_sqrt(&sx, &sx); sx.r -= c->i; sx.i += c->r; /* compute val = -i ln(sx) */ z_log(&sx, &sx); val.r = sx.i; val.i = -sx.r; r->r = val.r, r->i = val.i; } LOCAL VOID z_acos P2C(dcomplex *, r, dcomplex *, c) { /* Improved code by Hume Smith 11/92 */ dcomplex sx, val; /* compute val = 1 - c*c */ sx.r = 1.0 - c->r*c->r + c->i*c->i; sx.i = -2.0*c->r*c->i; z_sqrt(&val, &sx); /* compute sx = c + i sqrt(val) */ sx.r = c->r - val.i; sx.i = c->i + val.r; z_log(&sx, &sx); /* compute val = -i ln(sx)) */ val.r = sx.i; val.i = -sx.r; r->r = val.r, r->i = val.i; } LOCAL VOID z_atan P2C(dcomplex *, val, dcomplex *, c) { /* This has been redefined in Jan 1989 */ dcomplex sx, ix, v; sx.r = 1.0 - c->i, sx.i = c->r; ix.r = 1.0 + c->i, ix.i = -c->r; z_log(&sx, &sx); z_log(&ix, &ix); sx.r -= ix.r, sx.i -= ix.i; ix.r = 0.0, ix.i = 2.0; z_div(&v, &sx, &ix); val->r = v.r, val->i = v.i; } LOCAL VOID z_atan2 P3C(dcomplex *, val, dcomplex *, n, dcomplex *, d) { dcomplex v; /* adapted from catan2.f in FN from NETLIB */ if (z_abs(d) != 0.0) { z_div(&v, n, d); z_atan(&v, &v); } else { v.i = 0.0; if (z_abs(n) != 0.0) { double d__1, d__2; d__1 = PI * 0.5; d__2 = n->r; v.r = d_sign(&d__1, &d__2), v.i = 0.0; } else v.r = 0.0; } val->r = v.r, val->i = v.i; } /* Internal generic arithmetic functions */ LOCAL int zerop P1C(LVAL, x) { switch (ntype(x)) { case FIXNUM: return (getfixnum(x) == 0); case RATIO: return FALSE; case BIGNUM: return zeropbignum(x); case FLONUM: return (getflonum(x) == 0.0); case COMPLEX: return (zerop(getreal(x)) && zerop(getimag(x))); default: return FALSE; } } LOCAL int plusp P1C(LVAL, x) { switch (ntype(x)) { case FIXNUM: return (getfixnum(x) > 0); case RATIO: return plusp(getnumer(x)); case BIGNUM: return ! getbignumsign(x); case FLONUM: return (getflonum(x) > 0.0); default: return FALSE; } } LOCAL int minusp P1C(LVAL, x) { switch (ntype(x)) { case FIXNUM: return (getfixnum(x) < 0); case RATIO: return minusp(getnumer(x)); case BIGNUM: return getbignumsign(x); case FLONUM: return (getflonum(x) < 0.0); default: return FALSE; } } LOCAL int evenp P1C(LVAL, x) { switch (ntype(x)) { case FIXNUM: return (getfixnum(x) % 2 == 0); case BIGNUM: { FIXTYPE temp; LVAL bigtwo, r; xlsave1(bigtwo); bigtwo = cvtfixbignum((FIXTYPE) 2); divbignum(x, bigtwo, &r); xlpop(); return (cvtbigfixnum(r, &temp) && temp == 0); } default: return FALSE; } } LOCAL int oddp P1C(LVAL, x) { return integerp(x) && ! evenp(x); } LOCAL int commontype P2C(LVAL, x, LVAL, y) { switch (ntype(x)) { case FIXNUM: switch (ntype(y)) { case FIXNUM: return IN; case BIGNUM: return BG; case RATIO: return RT; case FLONUM: return FL; case COMPLEX: return floatp(getreal(y)) ? CF : CR; default: xlbadtype(y); } case BIGNUM: switch (ntype(y)) { case FIXNUM: case BIGNUM: return BG; case RATIO: return RT; case FLONUM: return FL; case COMPLEX: return floatp(getreal(y)) ? CF : CR; default: xlbadtype(y); } case RATIO: switch (ntype(y)) { case FIXNUM: case BIGNUM: case RATIO: return RT; case FLONUM: return FL; case COMPLEX: return floatp(getreal(y)) ? CF : CR; default: xlbadtype(y); } case FLONUM: switch (ntype(y)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return FL; case COMPLEX: return CF; default: xlbadtype(y); } case COMPLEX: switch (ntype(y)) { case FIXNUM: case BIGNUM: case RATIO: return floatp(getreal(x)) ? CF : CR; case FLONUM: return CF; case COMPLEX: return (floatp(getreal(x)) || floatp(getreal(y))) ? CF : CR; default: xlbadtype(y); } default: xlbadtype(x); } return 0; /* to keep compilers happy */ } LOCAL int compare2 P2C(LVAL, x, LVAL, y) { switch (commontype(x, y)) { case IN: { FIXTYPE xi, yi; xi = getfixnum(x); yi = getfixnum(y); return xi < yi ? -1 : xi > yi ? 1 : 0; } case BG: { int val; xlstkcheck(2); xlprotect(x); xlprotect(y); x = as_bignum(x); y = as_bignum(y); val = comparebignum(x, y); xlpopn(2); return val; } case RT: { LVAL nx, dx, ny, dy, a, b; int val; xlstkcheck(2); xlsave(a); xlsave(b); nx = numerator(x); dx = denominator(x); ny = numerator(y); dy = denominator(y); a = mul2(nx, dy); b = mul2(ny, dx); val = compare2(a, b); xlpopn(2); return val; } case FL: { FLOTYPE xd, yd; xd = as_flotype(x); yd = as_flotype(y); return xd < yd ? -1 : xd > yd ? 1 : 0; } default: xlbadtype(complexp(x) ? x : y); return 0; /* keep compilers happy */ } } LOCAL int equ2 P2C(LVAL, x, LVAL, y) { switch (commontype(x, y)) { case IN: case BG: case RT: case FL: return compare2(x, y) == 0; case CR: return equ2(realpart(x),realpart(y)) && equ2(imagpart(x),imagpart(y)); case CF: { LVAL rx, ix, ry, iy; FLOTYPE rxd, ixd, ryd, iyd; rx = realpart(x); rxd = as_flotype(rx); ix = imagpart(x); ixd = as_flotype(ix); ry = realpart(y); ryd = as_flotype(ry); iy = imagpart(y); iyd = as_flotype(iy); return rxd == ryd && ixd == iyd; } default: return 0; /* keep compilers happy */ } } LOCAL LVAL uminus P1C(LVAL, x) { switch (ntype(x)) { case FIXNUM: { FIXTYPE ix; ix = getfixnum(x); if (ix > MINFIX) return cvfixnum(-ix); /* else fall through */ } case BIGNUM: { FIXTYPE temp; xlprot1(x); x = as_bignum(x); x = copybignum(x, ! getbignumsign(x)); xlpop(); return cvtbigfixnum(x, &temp) ? cvfixnum(temp) : x; } case RATIO: { LVAL n, y; xlsave1(n); n = getnumer(x); n = uminus(n); y = div2(n, getdenom(x)); xlpop(); return y; } case FLONUM: return cvflonum(-getflonum(x)); case COMPLEX: if (floatp(getreal(x))) { return newdcomplex(-getflonum(getreal(x)), -getflonum(getimag(x))); } else { LVAL r, i, y; xlstkcheck(2); xlsave(r); xlsave(i); r = getreal(x); i = getimag(x); r = uminus(r); i = uminus(i); y = newcomplex(r, i); xlpopn(2); return y; } default: return xlbadtype(x); } } LOCAL LVAL add2 P2C(LVAL, x, LVAL, y) { /* try for quick return */ if (fixzerop(x)) return y; if (fixzerop(y)) return x; /* general case */ switch (commontype(x, y)) { case IN: { FIXTYPE xi, yi, zi; xi = getfixnum(x); yi = getfixnum(y); zi = xi + yi; if (goodisum(xi, yi, zi)) return cvfixnum(zi); /* else drop through */ } case BG: { LVAL z; FIXTYPE temp; xlstkcheck(2); xlprotect(x); xlprotect(y); x = as_bignum(x); y = as_bignum(y); z = addsubbignum(x, y, FALSE); xlpopn(2); return cvtbigfixnum(z, &temp) ? cvfixnum(temp) : z; } case RT: { LVAL nx, dx, ny, dy, n, d, z; xlstkcheck(4); xlsave(nx); xlsave(ny); xlsave(n); xlsave(d); nx = numerator(x); dx = denominator(x); ny = numerator(y); dy = denominator(y); nx = mul2(nx, dy); ny = mul2(ny, dx); n = add2(nx, ny); d = mul2(dx, dy); z = div2(n, d); xlpopn(4); return z; } case FL: { FLOTYPE xd, yd; xd = as_flotype(x); yd = as_flotype(y); return cvflonum(xd + yd); } case CR: { LVAL rx, ix, ry, iy, rz, iz, z; xlstkcheck(2); xlsave(rz); xlsave(iz); rx = realpart(x); ry = realpart(y); rz = add2(rx, ry); ix = imagpart(x); iy = imagpart(y); iz = add2(ix, iy); z = newcomplex(rz, iz); xlpopn(2); return z; } case CF: { LVAL rx, ix, ry, iy; FLOTYPE rxd, ixd, ryd, iyd; rx = realpart(x); rxd = as_flotype(rx); ix = imagpart(x); ixd = as_flotype(ix); ry = realpart(y); ryd = as_flotype(ry); iy = imagpart(y); iyd = as_flotype(iy); return newdcomplex(rxd + ryd, ixd + iyd); } default: return NIL; /* to keep compiler happy */ } } LOCAL LVAL sub2 P2C(LVAL, x, LVAL, y) { /* try for quick exit */ if (fixzerop(y)) return x; if (fixzerop(x)) return uminus(y); /* general case */ switch (commontype(x, y)) { case IN: { FIXTYPE xi, yi, zi; xi = getfixnum(x); yi = getfixnum(y); zi = xi - yi; if (goodidiff(xi, yi, zi)) return cvfixnum(zi); /* else drop through */ } case BG: { LVAL z; FIXTYPE temp; xlstkcheck(2); xlprotect(x); xlprotect(y); x = as_bignum(x); y = as_bignum(y); z = addsubbignum(x, y, TRUE); xlpopn(2); return cvtbigfixnum(z, &temp) ? cvfixnum(temp) : z; } case RT: { LVAL nx, dx, ny, dy, n, d, z; xlstkcheck(4); xlsave(nx); xlsave(ny); xlsave(n); xlsave(d); nx = numerator(x); dx = denominator(x); ny = numerator(y); dy = denominator(y); nx = mul2(nx, dy); ny = mul2(ny, dx); n = sub2(nx, ny); d = mul2(dx, dy); z = div2(n, d); xlpopn(4); return z; } case FL: { FLOTYPE xd, yd; xd = as_flotype(x); yd = as_flotype(y); return cvflonum(xd - yd); } case CR: { LVAL rx, ix, ry, iy, rz, iz, z; xlstkcheck(2); xlsave(rz); xlsave(iz); rx = realpart(x); ry = realpart(y); rz = sub2(rx, ry); ix = imagpart(x); iy = imagpart(y); iz = sub2(ix, iy); z = newcomplex(rz, iz); xlpopn(2); return z; } case CF: { LVAL rx, ix, ry, iy; FLOTYPE rxd, ixd, ryd, iyd; rx = realpart(x); rxd = as_flotype(rx); ix = imagpart(x); ixd = as_flotype(ix); ry = realpart(y); ryd = as_flotype(ry); iy = imagpart(y); iyd = as_flotype(iy); return newdcomplex(rxd - ryd, ixd - iyd); } default: return NIL; /* to keep compiler happy */ } } LOCAL LVAL mul2 P2C(LVAL, x, LVAL, y) { /* try for quick exit */ if (fixonep(x)) return y; if (fixonep(y)) return x; if (fixzerop(x) && (rationalp(y) || (complexp(y) && rationalp(getreal(y))))) return FIXZERO; if (fixzerop(y) && (rationalp(x) || (complexp(x) && rationalp(getreal(x))))) return FIXZERO; /* general case */ switch (commontype(x, y)) { case IN: { double xd, yd, zd; xd = (double) getfixnum(x); yd = (double) getfixnum(y); zd = xd * yd; if (infixnumrange(zd)) return cvfixnum((FIXTYPE) zd); /* else drop through */ } case BG: { LVAL z; FIXTYPE temp; xlstkcheck(2); xlprotect(x); xlprotect(y); x = as_bignum(x); y = as_bignum(y); z = multbignum(x, y); xlpopn(2); return cvtbigfixnum(z, &temp) ? cvfixnum(temp) : z; } case RT: { LVAL nx, dx, ny, dy, n, d, z; xlstkcheck(2); xlsave(n); xlsave(d); nx = numerator(x); dx = denominator(x); ny = numerator(y); dy = denominator(y); n = mul2(nx, ny); d = mul2(dx, dy); z = div2(n, d); xlpopn(2); return z; } case FL: { FLOTYPE xd, yd; xd = as_flotype(x); yd = as_flotype(y); return cvflonum(xd * yd); } case CR: { LVAL rx, ix, ry, iy, tmp1, tmp2, rz, iz, z; xlstkcheck(4); xlsave(tmp1); xlsave(tmp2); xlsave(rz); xlsave(iz); rx = realpart(x); ix = imagpart(x); ry = realpart(y); iy = imagpart(y); tmp1 = mul2(rx, ry); tmp2 = mul2(ix, iy); rz = sub2(tmp1, tmp2); tmp1 = mul2(ix, ry); tmp2 = mul2(rx, iy); iz = add2(tmp1, tmp2); z = newcomplex(rz, iz); xlpopn(4); return z; } case CF: { LVAL rx, ix, ry, iy; FLOTYPE rxd, ixd, ryd, iyd; rx = realpart(x); rxd = as_flotype(rx); ix = imagpart(x); ixd = as_flotype(ix); ry = realpart(y); ryd = as_flotype(ry); iy = imagpart(y); iyd = as_flotype(iy); return newdcomplex(rxd * ryd - ixd * iyd, ixd * ryd + rxd * iyd); } default: return NIL; /* to keep compiler happy */ } } LOCAL LVAL div2 P2C(LVAL, x, LVAL, y) { checkzero(y); /* try quick exit */ if (fixonep(y)) return x; if (fixzerop(x) && (rationalp(y) || (complexp(y) && rationalp(getreal(y))))) return FIXZERO; /* general case */ switch (commontype(x, y)) { case IN: return cvratio(getfixnum(x), getfixnum(y)); case BG: { LVAL z; xlstkcheck(2); xlprotect(x); xlprotect(y); x = as_bignum(x); y = as_bignum(y); z = cvbratio(x, y); xlpopn(2); return z; } case RT: { LVAL nx, dx, ny, dy, n, d, z; xlstkcheck(2); xlsave(n); xlsave(d); nx = numerator(x); dx = denominator(x); ny = numerator(y); dy = denominator(y); n = mul2(nx, dy); d = mul2(dx, ny); z = div2(n, d); xlpopn(2); return z; } case FL: { FLOTYPE xd, yd; xd = as_flotype(x); yd = as_flotype(y); return cvflonum(xd / yd); } case CR: { LVAL rx, ix, ry, iy, tmp1, tmp2, rz, iz, d, z; xlstkcheck(5); xlsave(tmp1); xlsave(tmp2); xlsave(d); xlsave(rz); xlsave(iz); rx = realpart(x); ix = imagpart(x); ry = realpart(y); iy = imagpart(y); tmp1 = mul2(ry, ry); tmp2 = mul2(iy, iy); d = add2(tmp1, tmp2); tmp1 = mul2(rx, ry); tmp2 = mul2(ix, iy); tmp1 = add2(tmp1, tmp2); rz = div2(tmp1, d); tmp1 = mul2(ix, ry); tmp2 = mul2(rx, iy); tmp1 = sub2(tmp1, tmp2); iz = div2(tmp1, d); z = newcomplex(rz, iz); xlpopn(5); return z; } case CF: { LVAL rx, ix, ry, iy; dcomplex cx, cy, cz; rx = realpart(x); cx.r = as_flotype(rx); ix = imagpart(x); cx.i = as_flotype(ix); ry = realpart(y); cy.r = as_flotype(ry); iy = imagpart(y); cy.i = as_flotype(iy); z_div(&cz, &cx, &cy); return newdcomplex(cz.r, cz.i); } default: return NIL; /* to keep compiler happy */ } } #ifdef XLISP_STAT LOCAL LVAL fdiv2 P2C(LVAL, x, LVAL, y) { switch (commontype(x, y)) { case IN: { FIXTYPE xi, yi; xi = getfixnum(x); yi = getfixnum(y); checkizero(yi); if (yi == -1 && xi == MINFIX) return cvratio(xi, yi); else if (xi % yi == 0) return cvfixnum(xi / yi); else return cvflonum(xi / (FLOTYPE) yi); } case BG: case RT: { LVAL z; z = div2(x, y); if (ratiop(z)) z = cvflonum(makefloat(z)); return z; } case FL: { FLOTYPE xd, yd; xd = as_flotype(x); yd = as_flotype(y); checkfzero(yd); return cvflonum(xd / yd); } case CR: { LVAL z, rz, iz; z = div2(x,y); rz = realpart(z); iz = imagpart(z); if (ratiop(rz) || ratiop(iz)) z = newdcomplex(makefloat(rz), makefloat(iz)); return z; } case CF: { LVAL rx, ix, ry, iy; dcomplex cx, cy, cz; rx = realpart(x); cx.r = as_flotype(rx); ix = imagpart(x); cx.i = as_flotype(ix); ry = realpart(y); cy.r = as_flotype(ry); iy = imagpart(y); cy.i = as_flotype(iy); z_div(&cz, &cx, &cy); return newdcomplex(cz.r, cz.i); } default: return NIL; /* to keep compiler happy */ } } #endif /* XLISP_STAT */ LOCAL LVAL min2 P2C(LVAL, x, LVAL, y) { switch (commontype(x, y)) { case IN: return (getfixnum(x) <= getfixnum(y)) ? x : y; case FL: return (as_flotype(x) <= as_flotype(y)) ? x : y; default: return leq2(x, y) ? x : y; } } LOCAL LVAL max2 P2C(LVAL, x, LVAL, y) { switch (commontype(x, y)) { case IN: return (getfixnum(x) >= getfixnum(y)) ? x : y; case FL: return (as_flotype(x) >= as_flotype(y)) ? x : y; default: return geq2(x, y) ? x : y; } } LOCAL VOID rational_idiv2 P5C(LVAL, x, LVAL, y, LVAL *, pn, LVAL *, pr, int, which) { LVAL n, r; checkzero(y); if (fixonep(y) && integerp(x)) { /* check for quick exit */ n = x; r = FIXZERO; } else { xlstkcheck(2); xlsave(n); xlsave(r); if (integerp(x) && integerp(y)) { /* base case */ LVAL xb, yb; FIXTYPE temp; xlstkcheck(2); xlsave(xb); xlsave(yb); xb = as_bignum(x); yb = as_bignum(y); n = divbignum(xb, yb, &r); xlpopn(2); if (cvtbigfixnum(n, &temp)) n = cvfixnum(temp); if (cvtbigfixnum(r, &temp)) r = cvfixnum(temp); } else { LVAL xn, xd, yn, yd, tmp1, tmp2; xn = numerator(x); xd = denominator(x); yn = numerator(y); yd = denominator(y); xlstkcheck(2); xlsave(tmp1); xlsave(tmp2); tmp1 = mul2(xn, yd); tmp2 = mul2(yn, xd); rational_idiv2(tmp1, tmp2, &n, &r, 'I'); tmp1 = mul2(xd, yd); r = div2(r, tmp1); xlpopn(2); } /* adjust ratio and remainder if necessary */ if (which != 'I' && ! zerop(r)) { switch (which) { case '^': if ((plusp(r) && plusp(y)) || (minusp(r) && minusp(y))) { n = add1(n); r = sub2(r, y); } break; case '_': if ((minusp(r) && plusp(y)) || (plusp(r) && minusp(y))) { n = sub1(n); r = add2(r, y); } break; case 'r': /* rounds away from zero on ties, not towards even number */ { LVAL cut; xlsave1(cut); if (plusp(y)) { if (plusp(r)) { cut = div2(y, FIXTWO); if (geq2(r, cut)) { n = add1(n); r = sub2(r, y); } } else { cut = div2(y, FIXNEGTWO); if (leq2(r, cut)) { n = sub1(n); r = add2(r, y); } } } else { if (plusp(r)) { cut = div2(y, FIXNEGTWO); if (geq2(r, cut)) { n = sub1(n); r = add2(r, y); } } else { cut = div2(y, FIXTWO); if (leq2(r, cut)) { n = add1(n); r = sub2(r, y); } } } xlpop(); } break; } } xlpopn(2); } *pn = n; *pr = r; } LOCAL VOID flonum_idiv2 P5C(FLOTYPE, xd, FLOTYPE, yd, FLOTYPE *, pnd, FLOTYPE *, prd, int, which) { FLOTYPE nd = *pnd, rd = *prd; checkfzero(yd); MODF(xd / yd, &nd); rd = xd - nd * yd; if (rd != 0.0) { switch (which) { case '^': if ((rd > 0.0 && yd > 0.0) || (rd < 0.0 && yd < 0.0)) { nd++; rd = rd - yd; } break; case '_': if ((rd < 0.0 && yd > 0.0) || (rd > 0.0 && yd < 0.0)) { nd--; rd = rd + yd; } break; case 'r': /* rounds away from zero on ties, not towards even number */ if (yd > 0.0) { if (rd > 0.0) { if (rd >= 0.5 * yd) { nd++; rd -= yd; } } else { if (rd <= -0.5 * yd) { nd--; rd += yd; } } } else { if (rd > 0.0) { if (rd >= -0.5 * yd) { nd--; rd += yd; } } else { if (rd <= 0.5 * yd) { nd++; rd -= yd; } } } break; } } *pnd = nd; *prd = rd; } LOCAL VOID idiv2 P5C(LVAL, x, LVAL, y, LVAL *, pn, LVAL *, pr, int, which) { LVAL n, r; switch (commontype(x, y)) { case IN: { FIXTYPE xi, yi; xi = getfixnum(x); yi = getfixnum(y); if (yi == 1) { /* check for quick exit */ n = x; r = FIXZERO; break; } else if (xi > MINFIX && xi < MAXFIX && yi > MINFIX && yi < MAXFIX) { FIXTYPE ni, ri; checkizero(yi); ni = xi / yi; ri = xi % yi; /* adjust ratio and remainder if necessary */ if (ri != 0) { switch (which) { case '^': if ((ri > 0 && yi > 0) || (ri < 0 && yi < 0)) { ni++; ri = ri - yi; } break; case '_': if ((ri < 0 && yi > 0) || (ri > 0 && yi < 0)) { ni--; ri = ri + yi; } break; case 'r':/* rounds away from zero on ties, not towards even number */ if (yi > 0) { if (ri > 0) { if (ri >= (yi + 1) / 2) { ni++; ri -= yi; } } else { if (ri <= (-yi - 1) / 2) { ni--; ri += yi; } } } else { if (ri > 0) { if (ri >= (-yi + 1) / 2) { ni--; ri += yi; } } else { if (ri <= (yi - 1) / 2) { ni++; ri -= yi; } } } break; } } /* only allocate if actually needed */ if (pn) { xlsave1(n); /* protect n while allocating r */ n = cvfixnum(ni); r = cvfixnum(ri); xlpop(); } else { n = NIL; r = cvfixnum(ri); } break; } /* else drop through */ } case BG: case RT: rational_idiv2(x, y, &n, &r, which); break; case FL: { FLOTYPE xd, yd, nd, rd; xd = as_flotype(x); yd = as_flotype(y); flonum_idiv2(xd, yd, &nd, &rd, which); /* only allocate n if actually needed */ if (pn) { xlsave1(n); /* protect n while allocating r */ if (infixnumrange(nd)) n = cvfixnum((FIXTYPE) nd); else { FIXTYPE temp; n = cvtflobignum(nd); if (cvtbigfixnum(n, &temp)) n = cvfixnum(temp); /* not needed for 32-big FIXTYPE's */ } r = cvflonum(rd); xlpop(); } else { n = NIL; r = cvflonum(rd); } break; } default: xlbadtype(complexp(x) ? x : y); r = n = NIL; /* fool compiler into not giving warning */ } if (pn) *pn = n; if (pr) *pr = r; } LOCAL LVAL add1 P1C(LVAL, x) { switch (ntype(x)) { case FIXNUM: { FIXTYPE xi, yi; xi = getfixnum(x); yi = xi + 1; if (goodisum(xi, 1, yi)) return cvfixnum(yi); /* else drop through */ } case BIGNUM: { LVAL y; FIXTYPE temp; xlprot1(x); x = as_bignum(x); y = addsubbignum(x, n_bigmone, TRUE); y = cvtbigfixnum(y, &temp) ? cvfixnum(temp) : y; xlpop(); return y; } case RATIO: { LVAL nx, dx, y; xlsave1(nx); nx = numerator(x); dx = denominator(x); nx = add2(nx, dx); y = div2(nx, dx); xlpop(); return y; } case FLONUM: return cvflonum(as_flotype(x) + 1.0); case COMPLEX: if (rationalp(getreal(x)) && rationalp(getimag(x))) { LVAL ry, y; xlsave1(ry); ry = add2(realpart(x), FIXONE); y = newcomplex(ry, imagpart(x)); xlpop(); return y; } else { LVAL rx, ix; FLOTYPE ixd, rxd; rx = realpart(x); rxd = as_flotype(rx); ix = imagpart(x); ixd = as_flotype(ix); return newdcomplex(rxd + 1.0, ixd); } default: return NIL; /* to keep compiler happy */ } } LOCAL LVAL sub1 P1C(LVAL, x) { switch (ntype(x)) { case FIXNUM: { FIXTYPE xi, yi; xi = getfixnum(x); yi = xi - 1; if (goodidiff(xi, 1, yi)) return cvfixnum(yi); /* else drop through */ } case BIGNUM: { LVAL y; FIXTYPE temp; xlprot1(x); x = as_bignum(x); y = addsubbignum(x, n_bigmone, FALSE); y = cvtbigfixnum(y, &temp) ? cvfixnum(temp) : y; xlpop(); return y; } case RATIO: { LVAL nx, dx, y; xlsave1(nx); nx = numerator(x); dx = denominator(x); nx = sub2(nx, dx); y = div2(nx, dx); xlpop(); return y; } case FLONUM: return cvflonum(as_flotype(x) - 1.0); case COMPLEX: if (rationalp(getreal(x)) && rationalp(getimag(x))) { LVAL ry, y; xlsave1(ry); ry = sub2(realpart(x), FIXONE); y = newcomplex(ry, imagpart(x)); xlpop(); return y; } else { LVAL rx, ix; FLOTYPE ixd, rxd; rx = realpart(x); rxd = as_flotype(rx); ix = imagpart(x); ixd = as_flotype(ix); return newdcomplex(rxd - 1.0, ixd); } default: return NIL; /* to keep compiler happy */ } } LOCAL LVAL gcd2 P2C(LVAL, n, LVAL, m) /* euclid's algorith */ { LVAL r; xlstkcheck(3); xlprotect(n); xlprotect(m); xlsave(r); for (;;) { idiv2(m, n, NULL, &r, 'I'); if (zerop(r)) break; m = n; n = r; } xlpopn(3); return (n); } LVAL xadd(V) { LVAL x, y; xlsave1(x); x = moreargs() ? xlganumber() : FIXZERO; while (moreargs()) { y = xlgetarg(); x = add2(x, y); } xlpop(); return x; } LVAL xsub(V) { LVAL x, y; x = xlgetarg(); if (moreargs()) { xlprot1(x); do { y = xlgetarg(); x = sub2(x, y); } while (moreargs()); xlpop(); } else x = uminus(x); return x; } LVAL xmul(V) { LVAL x, y; xlsave1(x); x = moreargs() ? xlganumber() : FIXONE; while (moreargs()) { y = xlgetarg(); x = mul2(x, y); } xlpop(); return x; } LVAL xdiv(V) { LVAL x, y; x = xlgetarg(); if (moreargs()) { xlprot1(x); do { y = xlgetarg(); x = div2(x, y); } while (moreargs()); xlpop(); } else x = div2(FIXONE, x); return x; } #ifdef XLISP_STAT LVAL xfdiv(V) { LVAL x, y; x = xlgetarg(); if (moreargs()) { xlprot1(x); do { y = xlgetarg(); x = fdiv2(x, y); } while (moreargs()); xlpop(); } else x = div2(FIXONE, x); return x; } #endif /* XLISP_STAT */ LVAL xmin(V) { LVAL x, y; x = xlganumber(); while (moreargs()) { y = xlgetarg(); x = min2(x, y); } return x; } LVAL xmax(V) { LVAL x, y; x = xlganumber(); while (moreargs()) { y = xlgetarg(); x = max2(x, y); } return x; } #define DEFCMPFUN(name,fun) \ LVAL name(V) { \ LVAL x, y; \ x = xlganumber(); \ while (moreargs()) { \ y = xlgetarg(); \ if (fun(x, y)) x = y; \ else return NIL; \ } \ return s_true; \ } DEFCMPFUN(xlss, lss2) DEFCMPFUN(xleq, leq2) DEFCMPFUN(xequ, equ2) DEFCMPFUN(xgeq, geq2) DEFCMPFUN(xgtr, gtr2) LVAL xneq(V) { LVAL x; int i; x = xlganumber(); while (moreargs()) { for (i = 0; i < xlargc; i++) if (equ2(x, xlargv[i])) return NIL; x = xlgetarg(); } return s_true; } #define DEFUNARYFUN(name,fun) \ LVAL name(V) { \ LVAL x = xlgetarg(); \ xllastarg(); \ return fun(x); \ } DEFUNARYFUN(xadd1, add1) DEFUNARYFUN(xsub1, sub1) LVAL xrem(V) { LVAL x, y, r; x = xlgetarg(); y = xlgetarg(); xllastarg(); idiv2(x, y, NULL, &r, 'I'); return r; } LVAL xmod(V) { LVAL x, y, r; x = xlgetarg(); y = xlgetarg(); xllastarg(); idiv2(x, y, NULL, &r, '_'); return r; } #ifdef MULVALS #define DEFIDIVFUN(name,w) \ LVAL name (V) { \ LVAL x, y, n, r; \ x = xlgetarg(); \ y = moreargs() ? xlgetarg() : FIXONE; \ xllastarg(); \ idiv2(x, y, &n, &r, w); \ xlresults[0] = n; \ xlresults[1] = r; \ xlnumresults = 2; \ return n; \ } #define DEFFDIVFUN(name,w) \ LVAL name(V) { \ FLOTYPE xd, yd, nd, rd; \ xd = makefloat(xlgetarg()); \ yd = moreargs() ? makefloat(xlgetarg()) : 1.0; \ xllastarg(); \ flonum_idiv2(xd, yd, &nd, &rd, w); \ xlnumresults = 0; \ xlresults[xlnumresults++] = cvflonum(nd); \ xlresults[xlnumresults++] = cvflonum(rd); \ return xlresults[0]; \ } #else #define DEFIDIVFUN(name,w) \ LVAL name(V) { \ LVAL x, y, n, r; \ x = xlgetarg(); \ y = moreargs() ? xlgetarg() : FIXONE; \ xllastarg(); \ idiv2(x, y, &n, &r, w); \ return n; \ } #define DEFFDIVFUN(name,w) \ LVAL name (V) { \ FLOTYPE xd, yd, nd, rd; \ xd = makefloat(xlgetarg()); \ yd = moreargs() ? makefloat(xlgetarg()) : 1.0; \ xllastarg(); \ flonum_idiv2(xd, yd, &nd, &rd, w); \ return cvflonum(nd); \ } #endif /* MULVALS */ DEFIDIVFUN(xfix, 'I') DEFIDIVFUN(xceil, '^') DEFIDIVFUN(xfloor, '_') DEFIDIVFUN(xround, 'r') DEFFDIVFUN(xffix, 'I') DEFFDIVFUN(xfceil, '^') DEFFDIVFUN(xffloor, '_') DEFFDIVFUN(xfround, 'r') LVAL xexpt(V) { LVAL base, power; base = xlganumber(); power = xlganumber(); xllastarg(); if (fixzerop(power)) { switch (ntype(base)) { case FIXNUM: case BIGNUM: case RATIO: return FIXONE; case FLONUM: return cvflonum((FLOTYPE) 1.0); case COMPLEX: return floatp(getreal(base)) ? newdcomplex(1.0, 0.0) : FIXONE; } } else if (zerop(base) && (zerop(power) || minusp(power))) badzero(); /* complex float base or complex power */ if ((complexp(base) && floatp(getreal(base))) || complexp(power)) { dcomplex zb, zp, zv; makecomplex(&zb, base); makecomplex(&zp, power); z_expt(&zv, &zb, &zp); return(cvcomplex(zv)); } /* real or complex rational base, integer power */ else if (integerp(power)) { LVAL val, r; if (fixp(base) && fixp(power)) { double fb, fp, fv; fb = (double) getfixnum(base); fp = (double) getfixnum(power); fv = floor(pow(fb, fp > 0.0 ? fp : -fp) + 0.5); if (infixnumrange(fv)) { if (fp > 0.0) return cvfixnum((FIXTYPE) fv); else return cvratio(1, (FIXTYPE) fv); } } else if (floatp(base) && fixp(power)) { double fb, fp, fv; fb = (double) getflonum(base); fp = (double) getfixnum(power); /* test range in case FIXNUMS aren't all exactly represented */ if (infixnumrange(fp)) { fv = pow(fb, fp); return cvflonum((FLOTYPE) fv); } /* else fall through */ } if (zerop(base)) return FIXZERO; xlstkcheck(3); xlprotect(base); xlprotect(power); xlsave(val); val = FIXONE; if (minusp(power)) { base = div2(FIXONE, base); power = uminus(power); } while (TRUE) { idiv2(power, FIXTWO, &power, &r, 'I'); if (! zerop(r)) { val = mul2(val, base); if (zerop(power)) break; } base = mul2(base, base); } xlpopn(3); return val; } /* real power and float base or rational power and real base */ else { double fb, fp; fb = makefloat(base); fp = makefloat(power); if (fb == 0.0 && fp > 0.0) return(cvflonum((FLOTYPE) 0.0)); else if (fb < 0.0) { dcomplex zb, zp, zv; makecomplex(&zb, base); makecomplex(&zp, power); z_expt(&zv, &zb, &zp); return(cvcomplex(zv)); } else { double fval; if (fp < 0.0) { fb = 1.0 / fb; fp = -fp; } fval = pow(fb, fp); return(cvflonum((FLOTYPE) fval)); } } } #ifdef XLISP_STAT LVAL xfexpt(V) { /* this is a hack but it should do */ if (xlargc == 2 && integerp(xlargv[1]) && minusp(xlargv[1])) { LVAL b = xlargv[0]; if (rationalp(b)) xlargv[0] = cvflonum((FLOTYPE) makefloat(b)); else if (complexp(b) && rationalp(getreal(b))) xlargv[0] = newdcomplex(makefloat(getreal(b)), makefloat(getimag(b))); } return xexpt(); } #endif /* XLISP_STAT */ /* arc tangent -- Tom Almy */ LVAL xatan(V) { LVAL lnum, ldenom; dcomplex cnum, cdenom, cval; lnum = xlgetarg(); if (moreargs()) { ldenom = xlgetarg(); xllastarg(); switch (commontype(lnum, ldenom)) { case IN: case FL: case RT: return cvflonum((FLOTYPE)atan2(makefloat(lnum), makefloat(ldenom))); default: /* complex */ makecomplex(&cnum, lnum); makecomplex(&cdenom, ldenom); z_atan2(&cval, &cnum, &cdenom); return (cvcomplex(cval)); } } else { switch (ntype(lnum)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return cvflonum((FLOTYPE)atan(makefloat(lnum))); default: /* complex */ makecomplex(&cnum, lnum); z_atan(&cnum, &cnum); return (cvcomplex(cnum)); } } } /* two argument logarithm */ LOCAL double logarithm P3C(FLOTYPE, x, FLOTYPE, base, int, base_supplied) { double lbase; if (x <= 0.0) xlfail("logarithm of a nonpositive number"); if (base_supplied) { if (base <= 0.0) xlfail("logarithm to a nonpositive base"); else { lbase = log(base); if (lbase == 0.0) xlfail("logarith to a unit base"); else return((log(x)/lbase)); } } return (log(x)); } LVAL xlog(V) { LVAL arg, base; double fx, fb; dcomplex zx, zb, zv; arg = xlgetarg(); if (moreargs()) { base = xlgetarg(); xllastarg(); if (realp(arg) && realp(base)) { fx = makefloat(arg); fb = makefloat(base); if (fx <= 0.0 || fb <= 0.0) { makecomplex(&zx, arg); makecomplex(&zb, base); z_log(&zx, &zx); z_log(&zb, &zb); z_div(&zv, &zx, &zb); return(cvcomplex(zv)); } else return(cvflonum((FLOTYPE) logarithm(fx, fb, TRUE))); } else if ((realp(arg) && complexp(base)) || (complexp(arg) && realp(base)) || (complexp(arg) && complexp(base))) { makecomplex(&zx, arg); makecomplex(&zb, base); z_log(&zx, &zx); z_log(&zb, &zb); z_div(&zv, &zx, &zb); return(cvcomplex(zv)); } else xlfail("bad argument type(s)"); } else { xllastarg(); if (realp(arg)) { fx = makefloat(arg); if (fx <= 0.0) { makecomplex(&zx, arg); z_log(&zx, &zx); return(cvcomplex(zx)); } else return(cvflonum((FLOTYPE) logarithm(fx, 0.0, FALSE))); } else if (complexp(arg)) { makecomplex(&zx, arg); z_log(&zx, &zx); return(cvcomplex(zx)); } else xlfail("bad argument type(s)"); } return NIL; /* avoid compiler warnings */ } /* xgcd - greatest common divisor */ LVAL xgcd(V) { LVAL m, n; if (!moreargs()) /* check for identity case */ return FIXZERO; xlstkcheck(2); xlsave(m); xlsave(n); n = xlgainteger(); if (minusp(n)) n = uminus(n); /* absolute value */ checkzero(n); while (moreargs()) { m = xlgainteger(); if (minusp(m)) m = uminus(m); /* absolute value */ checkzero(m); n = gcd2(n,m); } xlpopn(2); return n; } LVAL xlcm(V) { LVAL n, m, t, g; xlstkcheck(4); xlsave(n); xlsave(m); xlsave(t); xlsave(g); n = xlgainteger(); while (moreargs()) { m = xlgainteger(); if (zerop(n) || zerop(m)) return FIXZERO; t = mul2(n, m); g = gcd2(n, m); n = div2(t, g); } if (minusp(n)) n = uminus(n); xlpopn(4); return n; } LVAL xabs(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return minusp(x) ? uminus(x) : x; case COMPLEX: { dcomplex zx; makecomplex(&zx, x); return cvflonum((FLOTYPE) z_abs(&zx)); } default: return xlbadtype(x); } } LVAL xsin(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return cvflonum((FLOTYPE) sin(makefloat(x))); case COMPLEX: { dcomplex zx; makecomplex(&zx, x); z_sin(&zx, &zx); return cvcomplex(zx); } default: return xlbadtype(x); } } LVAL xcos(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return cvflonum((FLOTYPE) cos(makefloat(x))); case COMPLEX: { dcomplex zx; makecomplex(&zx, x); z_cos(&zx, &zx); return cvcomplex(zx); } default: return xlbadtype(x); } } LVAL xtan(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return cvflonum((FLOTYPE) tan(makefloat(x))); case COMPLEX: { dcomplex zx; makecomplex(&zx, x); z_tan(&zx, &zx); return cvcomplex(zx); } default: return xlbadtype(x); } } LVAL xexp(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return cvflonum((FLOTYPE) exp(makefloat(x))); case COMPLEX: { dcomplex zx; makecomplex(&zx, x); z_exp(&zx, &zx); return cvcomplex(zx); } default: return xlbadtype(x); } } LVAL xsqrt(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: { double dx = makefloat(x); if (dx >= 0.0) return cvflonum((FLOTYPE) sqrt(dx)); else return newdcomplex(0.0, sqrt(-dx)); } case COMPLEX: { dcomplex zx; makecomplex(&zx, x); z_sqrt(&zx, &zx); return cvcomplex(zx); } default: return xlbadtype(x); } } LVAL xfloat(V) { LVAL x; x = xlgetarg(); if (moreargs()) xlgaflonum(); xllastarg(); return floatp(x) ? x : cvflonum((FLOTYPE) makefloat(x)); } LVAL xasin(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: { double dx = makefloat(x); if (dx <= 1.0 && dx >= -1.0) return cvflonum((FLOTYPE) asin(dx)); /* else drop through */ } case COMPLEX: { dcomplex zx; makecomplex(&zx, x); z_asin(&zx, &zx); return cvcomplex(zx); } default: return xlbadtype(x); } } LVAL xacos(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: { double dx = makefloat(x); if (dx <= 1.0 && dx >= -1.0) return cvflonum((FLOTYPE) acos(dx)); /* else drop through */ } case COMPLEX: { dcomplex zx; makecomplex(&zx, x); z_acos(&zx, &zx); return cvcomplex(zx); } default: return xlbadtype(x); } } LVAL xphase(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return cvflonum((FLOTYPE) minusp(x) ? PI : 0.0); case COMPLEX: { dcomplex zx; makecomplex(&zx, x); return cvflonum((FLOTYPE) z_phase(&zx)); } default: return xlbadtype(x); } } LVAL xrational(V) { LVAL x; x = xlgetarg(); xllastarg(); switch (ntype(x)) { case FIXNUM: case BIGNUM: case RATIO: return x; case FLONUM: { double dummy, original, dx; int e, sign; dx = original = getflonum(x); if (dx < 0.0) { sign = TRUE; dx = -dx; } else sign = FALSE; dx = frexp(dx, &e); while (MODF(dx, &dummy) != 0.0 && dx <= DBL_MAX / 2.0) { e--; dx *= 2.0; } if (e >= 0) return cvtflobignum(original); else { LVAL num, denom, val; xlstkcheck(2); xlsave(num); xlsave(denom); num = cvtflobignum(sign ? -dummy : dummy); denom = cvtflobignum(ldexp(1.0, -e)); val = cvbratio(num, denom); xlpopn(2); return val; } } default: return xlbadtype(x); } } /* unary predicates */ #define DEFPREDFUN(name,fun) \ LVAL name(V) { \ LVAL x; \ x = xlganumber(); \ xllastarg(); \ return fun(x) ? s_true : NIL; \ } DEFPREDFUN(xminusp, minusp) DEFPREDFUN(xzerop, zerop) DEFPREDFUN(xplusp, plusp) DEFPREDFUN(xevenp, evenp) DEFPREDFUN(xoddp, oddp) /***********************************************************************/ /** **/ /** Complex Number Functions **/ /** **/ /***********************************************************************/ LVAL xcomplex(V) /* TAA rewrite so (complex 12.0) => #c(12.0 0.0) as required by CL. */ { LVAL real, imag; real = xlgetarg(); if (moreargs()) { imag = xlgetarg(); xllastarg(); return newcomplex(real, imag); } if (rationalp(real)) return real; return newdcomplex(makefloat(real), 0.0); } LVAL xconjugate(V) { LVAL arg, nimag, val; arg = xlgetarg(); xllastarg(); switch (ntype(arg)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return arg; case COMPLEX: xlsave1(nimag); nimag = uminus(getimag(arg)); val = newcomplex(getreal(arg), nimag); xlpop(); return val; default: return xlbadtype(arg); } } LVAL xrealpart(V) { LVAL arg = xlgetarg(); xllastarg(); switch (ntype(arg)) { case FIXNUM: case BIGNUM: case RATIO: case FLONUM: return arg; case COMPLEX: return getreal(arg); default: xlbadtype(arg); return(NIL); } } LVAL ximagpart(V) { LVAL arg = xlgetarg(); xllastarg(); switch (ntype(arg)) { case FIXNUM: case BIGNUM: case RATIO: return FIXZERO; case FLONUM: return cvflonum((FLOTYPE) 0.0); case COMPLEX: return(getimag(arg)); default: xlbadtype(arg); return(NIL); } } #endif