/* commonmath - 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"
#include "osdef.h"
#ifdef ANSI
#include "xlproto.h"
#include "xlsproto.h"
#include "osproto.h"
#else
#include "xlfun.h"
#include "xlsfun.h"
#include "osfun.h"
#endif ANSI
#include "xlvar.h"

typedef struct {
  int mode;
  FIXTYPE val, crval, cival;
  FLOTYPE fval, cfrval, cfival;
} Number; /* (Note: definition differs from statistics.c JKL */

/* forward declarations */
#ifdef ANSI
LVAL readnumber(Number *),lispnumber(Number *),unary(int),predicate(int),
     compare(int);
void setmode(Number *,int),matchmodes(Number *,Number *),
     get_rem_arg(FLOTYPE *,int *),badiop(void),badfop(void),badcop(void);
#else
LVAL readnumber(),lispnumber(),unary(),predicate(),
     compare();
void setmode(),matchmodes(),
     get_rem_arg(),badiop(),badfop(),badcop();
#endif ANSI

#define IN 0
#define FL 1
#define CI 2
#define CF 3

/* added JKL */
#define badarg xlbadtype

/* binary functions */
LVAL xadd()    { return (binary('+')); } /* + */
LVAL xsub()    { return (binary('-')); } /* - */
LVAL xmul()    { return (binary('*')); } /* * */
LVAL xdiv()    { return (binary('/')); } /* / */
LVAL xmin()    { return (binary('m')); } /* min */
LVAL xmax()    { return (binary('M')); } /* max */

LVAL xlogand() { return (logbinary('&')); } /* logand */
LVAL xlogior() { return (logbinary('|')); } /* logior */
LVAL xlogxor() { return (logbinary('^')); } /* logxor */

static LVAL readnumber(num)
	Number *num;
{
  LVAL arg = xlgetarg(), real, imag;
  
  if (fixp(arg)) {
    num->mode = IN;
    num->val = getfixnum(arg);
  }
  else if (floatp(arg)) {
    num->mode = FL;
    num->fval = getflonum(arg);
  }
  else if (complexp(arg)) {
    real = realpart(arg);
    imag = imagpart(arg);
    if (fixp(real)) {
      num->mode = CI;
      num->crval = getfixnum(real);
      num->cival = getfixnum(imag);
    }
    else {
      num->mode = CF;
      num->cfrval = makedouble(real);
      num->cfival = makedouble(imag);
    }
  }
  else xlerror("not a number", arg);
  return(arg);
}

static void setmode(x, mode)
	Number *x;
	int mode;
{
  switch (mode) {
  case FL:
    if (x->mode != IN) return;
    x->mode = mode;
    x->fval = x->val;
    break;
  case CI:
    if (x->mode != IN) return;
    x->mode = mode;
    x->crval = x->val;
    x->cival = 0;
    break;
  case CF:
    switch (x->mode) {
    case IN:
      x->mode = mode;
      x->cfrval = x->val;
      x->cfival = 0.0;
      break;
    case FL:
      x->mode = mode;
      x->cfrval = x->fval;
      x->cfival = 0.0;
      break;
    case CI:
      x->mode = mode;
      x->cfrval = x->crval;
      x->cfival = x->cival;
      break;
    }
    break;
  }
}

static void matchmodes(x, y)
	Number *x, *y;
{
  int mode = x->mode;
  switch (mode) {
  case IN: mode = y->mode; break;
  case FL: if (y->mode == CI || y->mode == CF) mode = CF; break;
  case CI: if (y->mode == FL || y->mode == CF) mode = CF; break;
  case CF: break;
  }
  if (x->mode != mode) setmode(x, mode);
  if (y->mode != mode) setmode(y, mode);
}

static LVAL lispnumber(x)
	Number *x;
{
  switch (x->mode) {
  case IN: return(cvfixnum(x->val));
  case FL: return(cvflonum(x->fval));
  case CI: return(newicomplex(x->crval, x->cival));
  case CF: return(newdcomplex(x->cfrval, x->cfival));
  }
}
/* see define above JKL
static void badarg(arg)
	LVAL arg;
{
  xlerror("bad argument type", arg);
}
*/
LVAL binary(which)
	int which;
{
  LVAL larg;
  Number val, arg;
  FIXTYPE rtemp, itemp;
  FLOTYPE frtemp, fitemp, magn;

  if (xlargc == 1 && (which == '-' || which == '/')) {
    val.mode = IN;
    switch (which) {
    case '-': val.val = 0; break;
    case '/': val.val = 1; break;
    }
  }
  else /*larg =*/ readnumber(&val); /* not used JKL */
  while (moreargs()) {
    larg = readnumber(&arg);
    matchmodes(&val, &arg);
    switch (which) {
    case '+':
      switch (val.mode) {
      case IN: val.val   += arg.val;  break;
      case FL: val.fval  += arg.fval; break;
      case CI: val.crval += arg.crval;   val.cival += arg.cival;   break;
      case CF: val.cfrval += arg.cfrval; val.cfival += arg.cfival; break;
      }
      break;
    case '-':
      switch (val.mode) {
      case IN: val.val   -= arg.val;  break;
      case FL: val.fval  -= arg.fval; break;
      case CI: val.crval -= arg.crval;   val.cival -= arg.cival;   break;
      case CF: val.cfrval -= arg.cfrval; val.cfival -= arg.cfival; break;
      }
      break;
    case '*':
      switch (val.mode) {
      case IN: val.val   *= arg.val;  break;
      case FL: val.fval  *= arg.fval; break;
      case CI:
        rtemp = val.crval * arg.crval - val.cival * arg.cival;
        itemp = val.cival * arg.crval + val.crval * arg.cival;
        val.crval = rtemp; val.cival = itemp;
        break;
      case CF:
        frtemp = val.cfrval * arg.cfrval - val.cfival * arg.cfival;
        fitemp = val.cfival * arg.cfrval + val.cfrval * arg.cfival;
        val.cfrval = frtemp; val.cfival = fitemp;
        break;
      }
      break;
    case '/':
      switch (val.mode) {
      case IN:
        checkizero(arg.val);
        if (val.val % arg.val == 0) {
          val.val /= arg.val;
          break;
        }
        else {
          setmode(&val, FL);
          setmode(&arg, FL);
        }
        /* drop through */
      case FL:
        checkfzero(arg.fval);
        val.fval /= arg.fval;
        break;
      case CI:
        setmode(&val, CF);
        setmode(&arg, CF);
        /* drop through */
      case CF:
        magn = arg.cfrval * arg.cfrval + arg.cfival * arg.cfival;
        checkfzero(magn);
        frtemp = (val.cfrval * arg.cfrval + val.cfival * arg.cfival) / magn;
        fitemp = (val.cfival * arg.cfrval - val.cfrval * arg.cfival) / magn;
        val.cfrval = frtemp; val.cfival = fitemp;
        break;
      }
      break;
    case 'M':
      switch (val.mode) {
      case IN: val.val  = (val.val > arg.val)   ? val.val  : arg.val;  break;
      case FL: val.fval = (val.fval > arg.fval) ? val.fval : arg.fval; break;
      default: badarg(larg);
      }
      break;
    case 'm':
      switch (val.mode) {
      case IN: val.val  = (val.val < arg.val)   ? val.val  : arg.val;  break;
      case FL: val.fval = (val.fval < arg.fval) ? val.fval : arg.fval; break;
      default: badarg(larg);
      }
      break;
    }
  }
  return(lispnumber(&val));
}

static void get_rem_arg(fval,mode)
	FLOTYPE *fval;
	int *mode;
{
  LVAL arg;
  
  arg = xlgetarg();
  if (fixp(arg)) {
    *fval = getfixnum(arg);
    *mode = IN;
  }
  else if (floatp(arg)) {
    *fval = getflonum(arg);
    *mode = FL;
  }
  else badarg(arg);
}

LVAL xrem()
{
  int mode1, mode2;
  FLOTYPE fval1, fval2, fres;
  
  get_rem_arg(&fval1, &mode1);
  get_rem_arg(&fval2, &mode2);
  xllastarg();

  fres = fval1 - fval2 * floor(fval1 / fval2);
  return((mode1 == IN && mode2 == IN) ? cvfixnum((FIXTYPE) fres)
                                      : cvflonum((FLOTYPE) fres));
}

LVAL logbinary(which)
	int which;
{
  int val, arg;
  
  switch (which) {
  case '&': val = -1; break;
  case '|': val =  0; break;
  case '^': val =  0; break;
  }
  while (moreargs()) {
    arg = getfixnum(xlgafixnum());
    switch (which) {
    case '&': val &= arg; break;
    case '|': val |= arg; break;
    case '^': val ^= arg; break;
    }
  }
  return(cvfixnum((FIXTYPE) val));
}

LVAL xexpt()
{
  LVAL base, power;
  int bsign, psign;
  FIXTYPE b, p, val;
  FLOTYPE fb, fp, fval;
  
  base = xlgetarg();
  power = xlgetarg();
  xllastarg();
  
  if (fixp(base) && fixp(power)) {
    b = getfixnum(base);
    p = getfixnum(power);
    if (p == 0) return(cvfixnum((FIXTYPE) 1));
    if (b == 0 && p > 0) return(cvfixnum((FIXTYPE) 0));
    checkizero(b);
    bsign = (b > 0) ? 1 : -1;
    psign = (p > 0) ? 1 : -1;
    b = (b > 0) ? b : -b;
    p = (p > 0) ? p : -p;
    fval = floor(f_expt((double) b, (double) p) + 0.1); /* to get integer right */
    if (bsign == -1 && p % 2 == 1) fval = -fval;
    if (psign == 1) {
      val = fval;
      if (val == fval) return(cvfixnum((FIXTYPE) val));
      else return(cvflonum((FLOTYPE) fval));    /* to handle precision for large results */
    }
    else {
      checkfzero(fval);
      return(cvflonum((FLOTYPE) 1.0 / fval));
    }
  } 
  else if (floatp(base) && fixp(power)) {
    fb = getflonum(base);
    p = getfixnum(power);
    if (p == 0) return(cvfixnum((FIXTYPE) 1));
    if (fb == 0.0 && p > 0) return(cvflonum((FLOTYPE) 0.0));
    checkfzero(fb);
    bsign = (fb > 0) ? 1 : -1;
    psign = (p > 0) ? 1 : -1;
    fb = (fb > 0) ? fb : -fb;
    p = (p > 0) ? p : -p;
    fval = f_expt((double) fb, (double) p);
    if (bsign == -1 && p % 2 == 1) fval = -fval;
    if (psign == 1) return(cvflonum((FLOTYPE) fval));
    else {
      checkfzero(fval);
      return(cvflonum((FLOTYPE) 1.0 / fval));
    }
  }
  else if ((fixp(base) || floatp(base)) && floatp(power)) {
    fb = makedouble(base);
    fp = getflonum(power);
    if (fp == 0.0) return(cvflonum((FLOTYPE) 1.0));
    if (fb == 0.0 && fp > 0.0) return(cvflonum((FLOTYPE) 0.0));
    if (fb < 0.0)
      return(cvcomplex(cexpt(makecomplex(base), makecomplex(power))));
    psign = (fp > 0) ? 1 : -1;
    fb = (fb > 0) ? fb : -fb;
    fp = (fp > 0) ? fp : -fp;
    fval = f_expt((double) fb, (double) fp);
    if (psign == 1) return(cvflonum((FLOTYPE) fval));
    else {
      checkfzero(fval);
      return(cvflonum((FLOTYPE) 1.0 / fval));
    }    
  }
  else if (complexp(base) || complexp(power))
    return(cvcomplex(cexpt(makecomplex(base), makecomplex(power))));
  else xlfail("bad argument type(s)");
}

LVAL xlog()
{
  LVAL arg, base;
  int base_supplied = FALSE;
  double fx, fb;
  
  arg = xlgetarg();
  if (moreargs()) {
    base_supplied = TRUE;
    base = xlgetarg();
  }
  if (base_supplied) {
    if (numberp(arg) && numberp(base)) {
      fx = makedouble(arg);
      fb = makedouble(base);
      if (fx <= 0.0 || fb <= 0.0)
        return(cvcomplex(cdiv(clog(makecomplex(arg)), clog(makecomplex(base)))));
      else return(cvflonum((FLOTYPE) logarithm(fx, fb, TRUE)));
    }
    else if ((numberp(arg) && complexp(base))
             || (complexp(arg) && numberp(base))
             || (complexp(arg) && complexp(base)))
      return(cvcomplex(cdiv(clog(makecomplex(arg)), clog(makecomplex(base)))));
    else xlfail("bad argument type(s)");
  }
  else {
    if (numberp(arg)) {
      fx = makedouble(arg);
      if (fx <= 0.0) return(cvcomplex(clog(makecomplex(arg))));
      else return(cvflonum((FLOTYPE) logarithm(fx, 0.0, FALSE)));
    }
    else if (complexp(arg)) 
      return(cvcomplex(clog(makecomplex(arg))));
    else xlfail("bad argument type(s)");
  }
}

/* xgcd - greatest common divisor */
LVAL xgcd()
{
  FIXTYPE m,n,r;
  LVAL arg;

  if (!moreargs())                  /* check for identity case */
    return (cvfixnum((FIXTYPE)0));
  arg = xlgafixnum();
  n = getfixnum(arg);
  if (n < (FIXTYPE)0) n = -n;		/* absolute value */
  while (moreargs()) {
    arg = xlgafixnum();
    m = getfixnum(arg);
    if (m == 0 || n == 0) xlfail("zero argument");
    if (m < (FIXTYPE)0) m = -m;	    /* absolute value */
    for (;;) {                      /* euclid's algorithm */
      r = m % n;
      if (r == (FIXTYPE) 0)
        break;
      m = n;
      n = r;
    }
  }
  return (cvfixnum(n));
}

/* checkizero - check for integer division by zero */
void checkizero(iarg)
  FIXTYPE iarg;
{
  if (iarg == 0)
  xlfail("illegal zero argument");
}

/* checkfzero - check for floating point division by zero or log of zero */
void checkfzero(farg)
  FLOTYPE farg;
{
  if (farg == 0.0)
  xlfail("illegal zero argument");
}

/* unary functions */
LVAL xlognot() { return (unary('~')); } /* lognot */
LVAL xabs()    { return (unary('A')); } /* abs */
LVAL xadd1()   { return (unary('+')); } /* 1+ */
LVAL xsub1()   { return (unary('-')); } /* 1- */
LVAL xsin()    { return (unary('S')); } /* sin */
LVAL xcos()    { return (unary('C')); } /* cos */
LVAL xtan()    { return (unary('T')); } /* tan */
LVAL xexp()    { return (unary('E')); } /* exp */
LVAL xsqrt()   { return (unary('R')); } /* sqrt */
LVAL xfix()    { return (unary('I')); } /* truncate */
LVAL xfloat()  { return (unary('F')); } /* float */
LVAL xrand()   { return (unary('?')); } /* random */
LVAL xfloor()  { return (unary('_')); } /* floor */
LVAL xceil()   { return (unary('^')); } /* ceiling */
LVAL xround()  { return (unary('r')); } /* round */
LVAL xasin()   { return (unary('s')); } /* asin */
LVAL xacos()   { return (unary('c')); } /* acos */
LVAL xatan()   { return (unary('t')); } /* atan */
LVAL xphase()  { return (unary('P')); } /* phase */

/* unary - handle unary operations */
LOCAL LVAL unary(which)
	int which;
{
  FLOTYPE fval;
  FIXTYPE ival;
  Complex cval;
  LVAL arg, real, imag;
  int mode;
  
  /* get the argument */
  arg = xlgetarg();
  if (which == 'F' && moreargs()) xlgaflonum();
  xllastarg();

  /* check its type */
  if (fixp(arg)) {
    ival = getfixnum(arg);
    mode = IN;
  }
  else if (floatp(arg)) {
    fval = getflonum(arg);
    mode = FL;
  }
  else if (complexp(arg)) {
    cval = makecomplex(arg);
    real = realpart(arg);
    imag = imagpart(arg);
    if (fixp(realpart(arg))) mode = CI;
    else mode = CF;
  }
  else xlerror("not a number", arg);

  switch (which) {
  case '~':
    if (mode == IN) return(cvfixnum((FIXTYPE) ~ival));    
    else badiop();
    break;
  case 'A':
    switch (mode) {
    case IN: return(cvfixnum((FIXTYPE) (ival < 0   ? -ival : ival)));
    case FL: return(cvflonum((FLOTYPE) (fval < 0.0 ? -fval : fval)));
    case CI:
    case CF: return(cvflonum((FLOTYPE) modulus(cval)));
    }
    break;
  case '+':
    switch (mode) {
    case IN: return(cvfixnum((FIXTYPE) ival + 1));
    case FL: return(cvflonum((FLOTYPE) fval + 1.0));
    case CI: return(newicomplex(getfixnum(real) + 1, getfixnum(imag)));
    case CF: return(newdcomplex(getflonum(real) + 1.0, getflonum(imag)));
    }
    break;
  case '-':
    switch (mode) {
    case IN: return(cvfixnum((FIXTYPE) ival - 1));
    case FL: return(cvflonum((FLOTYPE) fval - 1.0));
    case CI: return(newicomplex(getfixnum(real) - 1, getfixnum(imag)));
    case CF: return(newdcomplex(getflonum(real) - 1.0, getflonum(imag)));
    }
    break;
  case 'S':
    switch (mode) {
    case IN: return(cvflonum((FLOTYPE) sin((double) ival)));
    case FL: return(cvflonum((FLOTYPE) sin((double) fval)));
    case CI:
    case CF: return(cvcomplex(csin(cval)));
    }
  case 'C':
    switch (mode) {
    case IN: return(cvflonum((FLOTYPE) cos((double) ival)));
    case FL: return(cvflonum((FLOTYPE) cos((double) fval)));
    case CI:
    case CF: return(cvcomplex(ccos(cval)));
    }
  case 'T': 
    switch (mode) {
    case IN: return(cvflonum((FLOTYPE) tan((double) ival)));
    case FL: return(cvflonum((FLOTYPE) tan((double) fval)));
    case CI:
    case CF: return(cvcomplex(ctan(cval)));
    }
  case 'E':
    switch (mode) {
    case IN: return(cvflonum((FLOTYPE) f_exp((double) ival)));
    case FL: return(cvflonum((FLOTYPE) f_exp((double) fval)));
	case CI: 
	case CF: return(cvcomplex(cexp(cval)));
	}
	break;
  case 'R':
    switch (mode) {
    case IN:
      if (ival < 0) return(cvcomplex(csqrt(makecomplex(arg))));
	  else return(cvflonum((FLOTYPE) f_sqrt((double) ival)));
	case FL:
      if (fval < 0) return(cvcomplex(csqrt(makecomplex(arg))));
      else return(cvflonum((FLOTYPE) f_sqrt(fval)));
    case CI:
    case CF: return(cvcomplex(csqrt(cval)));
    }
    break;
  case 'I':
    switch (mode) {
    case IN: return (cvfixnum((FIXTYPE) ival));
    case FL: return (cvfixnum((FIXTYPE) fval));
	default: badcop();
	}
	break;
  case 'F': 
    switch (mode) {
    case IN: return (cvflonum((FLOTYPE) ival));
    case FL: return (cvflonum((FLOTYPE) fval));
	default: badcop();
	}
	break;
  case '?':
    switch (mode) {
    case IN: 
	  if (ival <= 0) badiop();
      return (cvfixnum((FIXTYPE) osrand((int) ival)));
    case FL: 
	  if (fval <= 0.0) badfop();
	  return (cvflonum((FLOTYPE) fval * uni()));
	default: badcop();
	}
	break;
  case '_':
    switch (mode) {
    case IN: return (cvfixnum((FIXTYPE) ival));
    case FL: return (cvfixnum((FIXTYPE) floor(fval)));
	default: badcop();
	}
	break;
  case '^':
    switch (mode) {
    case IN: return (cvfixnum((FIXTYPE) ival));
    case FL: return (cvfixnum((FIXTYPE) ceil(fval)));
	default: badcop();
	}
	break;
  case 'r':
    switch (mode) {
    case IN: return (cvfixnum((FIXTYPE) ival));
    case FL: return (cvfixnum((FIXTYPE) floor(fval + 0.5)));
	default: badcop();
	}
	break;
  case 's':
    switch (mode) {
    case IN:
      fval = ival;
      /* drop through */
    case FL:
      if (fval > 1.0 || fval < -1.0) 
        return(cvcomplex(casin(makecomplex(arg))));
      else return(cvflonum((FLOTYPE) asin(fval)));
	case CI:
	case CF: return(cvcomplex(casin(cval)));
	}
	break;
  case 'c':
    switch (mode) {
    case IN:
      fval = ival;
      /* drop through */
    case FL:
      if (fval > 1.0 || fval < -1.0) 
        return(cvcomplex(cacos(makecomplex(arg))));
      else return(cvflonum((FLOTYPE) acos(fval)));
	case CI:
	case CF: return(cvcomplex(cacos(cval)));
	}
	break;
  case 't':
    switch (mode) {
    case IN: fval = ival; /* drop through */
    case FL: return(cvflonum((FLOTYPE) atan(fval)));
	case CI:
	case CF: return(cvcomplex(catan(cval)));
	}
	break;
  case 'P':
    switch (mode) {
    case IN: return(cvflonum((FLOTYPE) (ival >= 0) ? 0.0 : PI));
	case FL: return(cvflonum((FLOTYPE) (fval >= 0.0) ? 0.0 : PI));
	case CI:
	case CF: return(cvflonum((FLOTYPE) phase(cval)));
	}
	break;
  default: xlfail("unsupported operation");
  }
}

/* unary predicates */
LVAL xminusp() { return (predicate('-')); } /* minusp */
LVAL xzerop()  { return (predicate('Z')); } /* zerop */
LVAL xplusp()  { return (predicate('+')); } /* plusp */
LVAL xevenp()  { return (predicate('E')); } /* evenp */
LVAL xoddp()   { return (predicate('O')); } /* oddp */


/* predicate - handle a predicate function */
LOCAL LVAL predicate(fcn)
  int fcn;
{
  FLOTYPE fval;
  FIXTYPE ival;
  LVAL arg;

  /* get the argument */
  arg = xlgetarg();
  xllastarg();

  /* check the argument type */
  if (fixp(arg)) {
    ival = getfixnum(arg);
    switch (fcn) {
    case '-': ival = (ival < 0); break;
    case 'Z': ival = (ival == 0); break;
    case '+': ival = (ival > 0); break;
    case 'E': ival = ((ival & 1) == 0); break;
    case 'O': ival = ((ival & 1) != 0); break;
    default:  badiop();
    }
  }
  else if (floatp(arg)) {
    fval = getflonum(arg);
    switch (fcn) {
    case '-': ival = (fval < 0); break;
    case 'Z': ival = (fval == 0); break;
    case '+': ival = (fval > 0); break;
    default:  badfop();
    }
  }
  else
    badarg(arg);

  /* return the result value */
  return (ival ? true : NIL);
}

/* comparison functions */
LVAL xlss() { return (compare('<'));  } /* < */
LVAL xleq() { return (compare('L'));  } /* <= */
LVAL xequ() { return (ccompare('=')); } /* = */
LVAL xneq() { return (ccompare('#')); } /* /= */
LVAL xgeq() { return (compare('G'));  } /* >= */
LVAL xgtr() { return (compare('>'));  } /* > */

/* compare - common compare function */
LOCAL LVAL compare(fcn)
  int fcn;
{
    FIXTYPE icmp,ival,iarg;
    FLOTYPE fcmp,fval,farg;
    LVAL arg;
    int mode;

    /* get the first argument */
    arg = xlgetarg();

    /* set the type of the first argument */
    if (fixp(arg)) {
	ival = getfixnum(arg);
	mode = 'I';
    }
    else if (floatp(arg)) {
	fval = getflonum(arg);
	mode = 'F';
    }
    else
      badarg(arg);

    /* handle each remaining argument */
    for (icmp = TRUE; icmp && moreargs(); ival = iarg, fval = farg) {

	/* get the next argument */
	arg = xlgetarg();

	/* check its type */
	if (fixp(arg)) {
	    switch (mode) {
	    case 'I':
	        iarg = getfixnum(arg);
	        break;
	    case 'F':
	        farg = (FLOTYPE)getfixnum(arg);
		break;
	    }
	}
	else if (floatp(arg)) {
	    switch (mode) {
	    case 'I':
	        fval = (FLOTYPE)ival;
		farg = getflonum(arg);
		mode = 'F';
		break;
	    case 'F':
	        farg = getflonum(arg);
		break;
	    }
	}
	else
      badarg(arg);

	/* compute result of the compare */
	switch (mode) {
	case 'I':
	    icmp = ival - iarg;
	    switch (fcn) {
	    case '<':	icmp = (icmp < 0); break;
	    case 'L':	icmp = (icmp <= 0); break;
	    case '=':	icmp = (icmp == 0); break;
	    case '#':	icmp = (icmp != 0); break;
	    case 'G':	icmp = (icmp >= 0); break;
	    case '>':	icmp = (icmp > 0); break;
	    }
	    break;
	case 'F':
	    fcmp = fval - farg;
	    switch (fcn) {
	    case '<':	icmp = (fcmp < 0.0); break;
	    case 'L':	icmp = (fcmp <= 0.0); break;
	    case '=':	icmp = (fcmp == 0.0); break;
	    case '#':	icmp = (fcmp != 0.0); break;
	    case 'G':	icmp = (fcmp >= 0.0); break;
	    case '>':	icmp = (fcmp > 0.0); break;
	    }
	    break;
	}
    }

    /* return the result */
    return (icmp ? true : NIL);
}

LVAL ccompare(which)
	int which;
{
/*LVAL larg; */
  Number val, arg;
  int icmp;
  
  switch (which) {
  case '=': icmp = TRUE;  break;
  case '#': icmp = FALSE; break;
  }
/*larg = */ readnumber(&val); /* not used JKL */
  while (moreargs()) {
/*  larg = */ readnumber(&arg); /* not used JKL */
    matchmodes(&val, &arg);
    switch (which) {
    case '=':
      switch (val.mode) {
      case IN: icmp = icmp && val.val  == arg.val;  break;
      case FL: icmp = icmp && val.fval == arg.fval; break;
      case CI: icmp = icmp && val.crval == arg.crval && val.cival == arg.cival; break;
      case CF: icmp = icmp && val.cfrval == arg.cfrval && val.cfival == arg.cfival; break;
      }
      break;
    case '#':
      switch (val.mode) {
      case IN: icmp = icmp || val.val  != arg.val;  break;
      case FL: icmp = icmp || val.fval != arg.fval; break;
      case CI: icmp = icmp || val.crval != arg.crval || val.cival != arg.cival; break;
      case CF: icmp = icmp || val.cfrval != arg.cfrval || val.cfival != arg.cfival; break;
      }
      break;
    }
  }
  return((icmp) ? true : NIL);
}

/* badiop - bad integer operation */
LOCAL void badiop()
{
  xlfail("bad integer operation");
}

/* badfop - bad floating point operation */
LOCAL void badfop()
{
  xlfail("bad floating point operation");
}

/* badcop - bad complex number operation */
LOCAL void badcop()
{
  xlfail("bad complex number operation");
}

/* two argument logarithm */
double logarithm(x, base, base_supplied)
     FLOTYPE x, 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 = f_log(base);
      if (lbase == 0.0) xlfail("logarith to a unit base");
      else return((f_log(x)/lbase));
    }
  }
  else return (f_log(x));
}
