/* xlmath - xlisp builtin arithmetic functions */

#include "xlisp.h"

/* external variables */
extern NODE *xlstack;
extern NODE *true;

/* forward declarations */
FORWARD NODE *unary();
FORWARD NODE *binary();
FORWARD NODE *predicate();
FORWARD NODE *compare();

/* xadd - builtin function for addition */
NODE *xadd(args)
  NODE *args;
{
    return (binary(args,'+'));
}

/* xsub - builtin function for subtraction */
NODE *xsub(args)
  NODE *args;
{
    return (binary(args,'-'));
}

/* xmul - builtin function for multiplication */
NODE *xmul(args)
  NODE *args;
{
    return (binary(args,'*'));
}

/* xdiv - builtin function for division */
NODE *xdiv(args)
  NODE *args;
{
    return (binary(args,'/'));
}

/* xrem - builtin function for remainder */
NODE *xrem(args)
  NODE *args;
{
    return (binary(args,'%'));
}

/* xmin - builtin function for minimum */
NODE *xmin(args)
  NODE *args;
{
    return (binary(args,'m'));
}

/* xmax - builtin function for maximum */
NODE *xmax(args)
  NODE *args;
{
    return (binary(args,'M'));
}

/* xbitand - builtin function for bitwise and */
NODE *xbitand(args)
  NODE *args;
{
    return (binary(args,'&'));
}

/* xbitior - builtin function for bitwise inclusive or */
NODE *xbitior(args)
  NODE *args;
{
    return (binary(args,'|'));
}

/* xbitxor - builtin function for bitwise exclusive or */
NODE *xbitxor(args)
  NODE *args;
{
    return (binary(args,'^'));
}

/* binary - handle binary operations */
LOCAL NODE *binary(args,fcn)
  NODE *args; int fcn;
{
    int ival,iarg;
    NODE *val;

    /* get the first argument */
    ival = xlmatch(INT,&args)->n_int;

    /* treat '-' with a single argument as a special case */
    if (fcn == '-' && args == NULL)
	ival = -ival;

    /* handle each remaining argument */
    while (args) {

	/* get the next argument */
	iarg = xlmatch(INT,&args)->n_int;

	/* accumulate the result value */
	switch (fcn) {
	case '+':	ival += iarg; break;
	case '-':	ival -= iarg; break;
	case '*':	ival *= iarg; break;
	case '/':	ival /= iarg; break;
	case '%':	ival %= iarg; break;
	case 'M':	if (iarg > ival) ival = iarg; break;
	case 'm':	if (iarg < ival) ival = iarg; break;
	case '&':	ival &= iarg; break;
	case '|':	ival |= iarg; break;
	case '^':	ival ^= iarg; break;
	}
    }

    /* initialize value */
    val = newnode(INT);
    val->n_int = ival;

    /* return the result value */
    return (val);
}

/* xbitnot - bitwise not */
NODE *xbitnot(args)
  NODE *args;
{
    return (unary(args,'~'));
}

/* xabs - builtin function for absolute value */
NODE *xabs(args)
  NODE *args;
{
    return (unary(args,'A'));
}

/* xadd1 - builtin function for adding one */
NODE *xadd1(args)
  NODE *args;
{
    return (unary(args,'+'));
}

/* xsub1 - builtin function for subtracting one */
NODE *xsub1(args)
  NODE *args;
{
    return (unary(args,'-'));
}

/* unary - handle unary operations */
LOCAL NODE *unary(args,fcn)
  NODE *args; int fcn;
{
    NODE *val;
    int ival;

    /* get the argument */
    ival = xlmatch(INT,&args)->n_int;
    xllastarg(args);

    /* compute the result */
    switch (fcn) {
    case '~':	ival = ~ival; break;
    case 'A':	if (ival < 0) ival = -ival; break;
    case '+':	ival++; break;
    case '-':	ival--; break;
    }

    /* convert the value  */
    val = newnode(INT);
    val->n_int = ival;

    /* return the result value */
    return (val);
}

/* xminusp - is this number negative? */
NODE *xminusp(args)
  NODE *args;
{
    return (predicate(args,'-'));
}

/* xzerop - is this number zero? */
NODE *xzerop(args)
  NODE *args;
{
    return (predicate(args,'Z'));
}

/* xplusp - is this number positive? */
NODE *xplusp(args)
  NODE *args;
{
    return (predicate(args,'+'));
}

/* xevenp - is this number even? */
NODE *xevenp(args)
  NODE *args;
{
    return (predicate(args,'E'));
}

/* xoddp - is this number odd? */
NODE *xoddp(args)
  NODE *args;
{
    return (predicate(args,'O'));
}

/* predicate - handle a predicate function */
LOCAL NODE *predicate(args,fcn)
  NODE *args; int fcn;
{
    NODE *val;
    int ival;

    /* get the argument */
    ival = xlmatch(INT,&args)->n_int;
    xllastarg(args);

    /* compute the result */
    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;
    }

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

/* xlss - builtin function for < */
NODE *xlss(args)
  NODE *args;
{
    return (compare(args,'<'));
}

/* xleq - builtin function for <= */
NODE *xleq(args)
  NODE *args;
{
    return (compare(args,'L'));
}

/* equ - builtin function for = */
NODE *xequ(args)
  NODE *args;
{
    return (compare(args,'='));
}

/* xneq - builtin function for /= */
NODE *xneq(args)
  NODE *args;
{
    return (compare(args,'#'));
}

/* xgeq - builtin function for >= */
NODE *xgeq(args)
  NODE *args;
{
    return (compare(args,'G'));
}

/* xgtr - builtin function for > */
NODE *xgtr(args)
  NODE *args;
{
    return (compare(args,'>'));
}

/* compare - common compare function */
LOCAL NODE *compare(args,fcn)
  NODE *args; int fcn;
{
    NODE *arg1,*arg2;
    int cmp;

    /* get the two arguments */
    arg1 = xlarg(&args);
    arg2 = xlarg(&args);
    xllastarg(args);

    /* do the compare */
    if (stringp(arg1) && stringp(arg2))
	cmp = strcmp(arg1->n_str,arg2->n_str);
    else if (fixp(arg1) && fixp(arg2))
	cmp = arg1->n_int - arg2->n_int;
    else
	cmp = arg1 - arg2;

    /* compute result of the compare */
    switch (fcn) {
    case '<':	cmp = (cmp < 0); break;
    case 'L':	cmp = (cmp <= 0); break;
    case '=':	cmp = (cmp == 0); break;
    case '#':	cmp = (cmp != 0); break;
    case 'G':	cmp = (cmp >= 0); break;
    case '>':	cmp = (cmp > 0); break;
    }

    /* return the result */
    return (cmp ? true : NULL);
}
