tinymush-3.1p2/game/backups/
tinymush-3.1p2/game/bin/
tinymush-3.1p2/game/data/
tinymush-3.1p2/game/modules/
tinymush-3.1p2/game/modules/old/
tinymush-3.1p2/src/modules/comsys/
tinymush-3.1p2/src/modules/hello/
tinymush-3.1p2/src/modules/mail/
tinymush-3.1p2/src/tools/
/* funmath.c - math and logic functions */
/* $Id: funmath.c,v 1.27 2004/02/23 04:35:14 rmg Exp $ */

#include "copyright.h"
#include "autoconf.h"
#include "config.h"

#include <math.h>

#include "alloc.h"	/* required by mudconf */
#include "flags.h"	/* required by mudconf */
#include "htab.h"	/* required by mudconf */
#include "mudconf.h"	/* required by code */

#include "db.h"		/* required by externs */
#include "externs.h"	/* required by code */

#include "functions.h"	/* required by code */

/* ---------------------------------------------------------------------------
 * fval: copy the floating point value into a buffer and make it presentable
 */

#ifdef FLOATING_POINTS
#define FP_SIZE ((sizeof(double) + sizeof(unsigned int) - 1) / sizeof(unsigned int))
#define FP_EXP_WEIRD	0x1
#define FP_EXP_ZERO	0x2

typedef union {
    double d;
    unsigned int u[FP_SIZE];
} fp_union_uint;

static unsigned int fp_check_weird(buff, bufc, result)
char *buff, **bufc;
double result;
{
	static fp_union_uint fp_sign_mask, fp_exp_mask, fp_mant_mask, fp_val;
	static const double d_zero = 0.0;
	static int fp_initted = 0;
	unsigned int fp_sign, fp_exp, fp_mant;
	int i;

	if (!fp_initted) {
		memset(fp_sign_mask.u, 0, sizeof(fp_sign_mask));
		memset(fp_exp_mask.u, 0, sizeof(fp_exp_mask));
		memset(fp_val.u, 0, sizeof(fp_val));
		fp_exp_mask.d = 1.0 / d_zero;
		fp_sign_mask.d = -1.0 / fp_exp_mask.d;
		for (i = 0; i < FP_SIZE; i++) {
			fp_mant_mask.u[i] = ~(fp_exp_mask.u[i] | fp_sign_mask.u[i]);
		}
		fp_initted = 1;
	}

	fp_val.d = result;

	fp_sign = fp_mant = 0;
	fp_exp = FP_EXP_ZERO | FP_EXP_WEIRD;

	for (i = 0; (i < FP_SIZE) && fp_exp; i++) {
		if (fp_exp_mask.u[i]) {
			unsigned int x = (fp_exp_mask.u[i] & fp_val.u[i]);
			if (x == fp_exp_mask.u[i]) {
				/* these bits are all set. can't be zero
				 * exponent, but could still be max (weird)
				 * exponent.
				 */
				fp_exp &= ~FP_EXP_ZERO;
			} else if (x == 0) {
				/* none of these bits are set. can't be max
				 * exponent, but could still be zero exponent.
				 */
				fp_exp &= ~FP_EXP_WEIRD;
			} else {
				/* some bits were set but not others. can't
				 * be either zero exponent or max exponent.
				 */
				fp_exp = 0;
			}
		}

		fp_sign |= (fp_sign_mask.u[i] & fp_val.u[i]);
		fp_mant |= (fp_mant_mask.u[i] & fp_val.u[i]);
	}
	if (fp_exp == FP_EXP_WEIRD) {
		if (fp_sign) {
			safe_chr('-', buff, bufc);
		}
		if (fp_mant) {
			safe_known_str("NaN", 3, buff, bufc);
	  	} else {
			safe_known_str("Inf", 3, buff, bufc);
		}
	}
	return fp_exp;
}

static void fval(buff, bufc, result)
char *buff, **bufc;
double result;
{
	char *p, *buf1;

	switch (fp_check_weird(buff, bufc, result)) {
	case FP_EXP_WEIRD:	return;
	case FP_EXP_ZERO:	result = 0.0; /* FALLTHRU */
	default:		break;
	}

	buf1 = *bufc;
	safe_tprintf_str(buff, bufc, "%.6f", result);	/* get double val
							 * into buffer 
							 */
	**bufc = '\0';
	p = strrchr(buf1, '0');
	if (p == NULL) {	/* remove useless trailing 0's */
		return;
	} else if (*(p + 1) == '\0') {
		while (*p == '0') {
			*p-- = '\0';
		}
		*bufc = p + 1;
	}
	p = strrchr(buf1, '.');	/* take care of dangling '.' */

	if ((p != NULL) && (*(p + 1) == '\0')) {
			*p = '\0';
		*bufc = p;
	}
	/* Handle bogus result of "-0" from sprintf.  Yay, cclib. */

	if (!strcmp(buf1, "-0")) {
		*buf1 = '0';
		*bufc = buf1 + 1;
	}
}
#else
#define fval(b,p,n)  safe_ltos(b,p,n)
#endif

/* ---------------------------------------------------------------------------
 * Constant math funcs: PI, E
 */

FUNCTION(fun_pi)
{
	safe_known_str("3.141592654", 11, buff, bufc);
}

FUNCTION(fun_e)
{
	safe_known_str("2.718281828", 11, buff, bufc);
}

/* ---------------------------------------------------------------------------
 * Math operations on one number: SIGN, ABS, FLOOR, CEIL, ROUND, TRUNC,
 *    INC, DEC, SQRT, EXP, LN, [A][SIN,COS,TAN][D]
 */

FUNCTION(fun_sign)
{
	NVAL num;

	num = aton(fargs[0]);
	if (num < 0) {
	    safe_known_str("-1", 2, buff, bufc);
	} else {
	    safe_bool(buff, bufc, (num > 0));
	}
}

FUNCTION(fun_abs)
{
	NVAL num;

	num = aton(fargs[0]);
	if (num == 0) {
		safe_chr('0', buff, bufc);
	} else if (num < 0) {
		fval(buff, bufc, -num);
	} else {
		fval(buff, bufc, num);
	}
}

FUNCTION(fun_floor)
{
#ifdef FLOATING_POINTS
	char *oldp;
	NVAL x;

	oldp = *bufc;
	x = floor(aton(fargs[0]));
	switch (fp_check_weird(buff, bufc, x)) {
	case FP_EXP_WEIRD:	return;
	case FP_EXP_ZERO:	x = 0.0; /* FALLTHRU */
	default:		break;
	}
	safe_tprintf_str(buff, bufc, "%.0f", x);
	/* Handle bogus result of "-0" from sprintf.  Yay, cclib. */

	if (!strcmp(oldp, "-0")) {
		*oldp = '0';
		*bufc = oldp + 1;
	}
#else
	fval(buff, bufc, aton(fargs[0]));
#endif
}

FUNCTION(fun_ceil)
{
#ifdef FLOATING_POINTS
	char *oldp;
	NVAL x;

	oldp = *bufc;
	x = ceil(aton(fargs[0]));
	switch (fp_check_weird(buff, bufc, x)) {
	case FP_EXP_WEIRD:	return;
	case FP_EXP_ZERO:	x = 0.0; /* FALLTHRU */
	default:		break;
	}
	safe_tprintf_str(buff, bufc, "%.0f", x);
	/* Handle bogus result of "-0" from sprintf.  Yay, cclib. */

	if (!strcmp(oldp, "-0")) {
		*oldp = '0';
		*bufc = oldp + 1;
	}
#else
	fval(buff, bufc, aton(fargs[0]));
#endif
}

FUNCTION(fun_round)
{
#ifdef FLOATING_POINTS
	const char *fstr;
	char *oldp;
	NVAL x;

	oldp = *bufc;
	
	switch (atoi(fargs[1])) {
	case 1:
		fstr = "%.1f";
		break;
	case 2:
		fstr = "%.2f";
		break;
	case 3:
		fstr = "%.3f";
		break;
	case 4:
		fstr = "%.4f";
		break;
	case 5:
		fstr = "%.5f";
		break;
	case 6:
		fstr = "%.6f";
		break;
	default:
		fstr = "%.0f";
		break;
	}
	x = aton(fargs[0]);
	switch (fp_check_weird(buff, bufc, x)) {
	case FP_EXP_WEIRD:	return;
	case FP_EXP_ZERO:	x = 0.0; /* FALLTHRU */
	default:		break;
	}
	safe_tprintf_str(buff, bufc, (char *)fstr, x);

	/* Handle bogus result of "-0" from sprintf.  Yay, cclib. */

	if (!strcmp(oldp, "-0")) {
		*oldp = '0';
		*bufc = oldp + 1;
	}
#else
	fval(buff, bufc, aton(fargs[0]));
#endif
}

FUNCTION(fun_trunc)
{
#ifdef FLOATING_POINTS
	NVAL x;

	x = aton(fargs[0]);
	x = (x >= 0) ? floor(x) : ceil(x);
	switch (fp_check_weird(buff, bufc, x)) {
	case FP_EXP_WEIRD:	return;
	case FP_EXP_ZERO:	x = 0.0; /* FALLTHRU */
	default:		break;
	}
	fval(buff, bufc, x);
#else
	fval(buff, bufc, aton(fargs[0]));
#endif
}

FUNCTION(fun_inc)
{
	safe_ltos(buff, bufc, atoi(fargs[0]) + 1);
}

FUNCTION(fun_dec)
{
	safe_ltos(buff, bufc, atoi(fargs[0]) - 1);
}

FUNCTION(fun_sqrt)
{
	NVAL val;

	val = aton(fargs[0]);
	if (val < 0) {
		safe_str("#-1 SQUARE ROOT OF NEGATIVE", buff, bufc);
	} else if (val == 0) {
		safe_chr('0', buff, bufc);
	} else {
		fval(buff, bufc, sqrt((double)val));
	}
}

FUNCTION(fun_exp)
{
	fval(buff, bufc, exp((double)aton(fargs[0])));
}

FUNCTION(fun_ln)
{
	NVAL val;

	val = aton(fargs[0]);
	if (val > 0)
		fval(buff, bufc, log((double)val));
	else
		safe_str("#-1 LN OF NEGATIVE OR ZERO", buff, bufc);
}

FUNCTION(handle_trig)
{
	NVAL val;
	int oper, flag;
	static double (* const trig_funcs[8])(double) = {
		sin,  cos,  tan, NULL,	/* XXX no cotangent function */
		asin, acos, atan, NULL
	};

	flag = Func_Flags(fargs);
	oper = flag & TRIG_OPER;

	val = aton(fargs[0]);
	if ((flag & TRIG_ARC) && !(flag & TRIG_TAN) &&
	    ((val < -1) || (val > 1))) {
		safe_tprintf_str(buff, bufc, "#-1 %s ARGUMENT OUT OF RANGE",
				 ((FUN *)fargs[-1])->name);
		return;
	}
	if ((flag & TRIG_DEG) && !(flag & TRIG_ARC))
		val = val * (M_PI / 180);

	val = (trig_funcs[oper])((double)val);

	if ((flag & TRIG_DEG) && (flag & TRIG_ARC))
		val = (val * 180) / M_PI;

	fval(buff, bufc, val);
}

/* ---------------------------------------------------------------------------
 * Math comparison funcs: GT, GTE, LT, LTE, EQ, NEQ, NCOMP
 */

FUNCTION(fun_gt)
{
	safe_bool(buff, bufc, (aton(fargs[0]) > aton(fargs[1])));
}

FUNCTION(fun_gte)
{
	safe_bool(buff, bufc, (aton(fargs[0]) >= aton(fargs[1])));
}

FUNCTION(fun_lt)
{
	safe_bool(buff, bufc, (aton(fargs[0]) < aton(fargs[1])));
}

FUNCTION(fun_lte)
{
	safe_bool(buff, bufc, (aton(fargs[0]) <= aton(fargs[1])));
}

FUNCTION(fun_eq)
{
	safe_bool(buff, bufc, (aton(fargs[0]) == aton(fargs[1])));
}

FUNCTION(fun_neq)
{
	safe_bool(buff, bufc, (aton(fargs[0]) != aton(fargs[1])));
}

FUNCTION(fun_ncomp)
{
	NVAL x, y;

	x = aton(fargs[0]);
	y = aton(fargs[1]);

	if (x == y) {
		safe_chr('0', buff, bufc);
	} else if (x < y) {
		safe_str("-1", buff, bufc);
	} else {
		safe_chr('1', buff, bufc);
	}
}

/* ---------------------------------------------------------------------------
 * Two-argument math functions: SUB, DIV, FLOORDIV, FDIV, MODULO, REMAINDER,
 *    POWER, LOG
 */

FUNCTION(fun_sub)
{
	fval(buff, bufc, aton(fargs[0]) - aton(fargs[1]));
}

FUNCTION(fun_div)
{
	int top, bot;

	/* The C / operator is only fully specified for non-negative
	 * operands, so we try not to give it negative operands here
	 */

	top = atoi(fargs[0]);
	bot = atoi(fargs[1]);
	if (bot == 0) {
		safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
		return;
	}

	if (top < 0) {
		if (bot < 0)
			top = (-top) / (-bot);
		else
			top = -((-top) / bot);
	} else {
		if (bot < 0)
			top = -(top / (-bot));
		else
			top = top / bot;
	}
	safe_ltos(buff, bufc, top);
}

FUNCTION(fun_floordiv)
{
	int top, bot, res;

	/* The C / operator is only fully specified for non-negative
	 * operands, so we try not to give it negative operands here
	 */

	top = atoi(fargs[0]);
	bot = atoi(fargs[1]);
	if (bot == 0) {
		safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
		return;
	}

	if (top < 0) {
		if (bot < 0) {
			res = (-top) / (-bot);
		} else {
			res = -((-top) / bot);
			if (top % bot)
				res--;
		}
	} else {
		if (bot < 0) {
			res = -(top / (-bot));
			if (top % bot)
				res--;
		} else {
			res = top / bot;
		}
	}
	safe_ltos(buff, bufc, res);
}

FUNCTION(fun_fdiv)
{
	NVAL bot;

	bot = aton(fargs[1]);
	if (bot == 0) {
		safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
	} else {
		fval(buff, bufc, (aton(fargs[0]) / bot));
	}
}

FUNCTION(fun_modulo)
{
	int top, bot;

	/* The C % operator is only fully specified for non-negative
	 * operands, so we try not to give it negative operands here
	 */

	top = atoi(fargs[0]);
	bot = atoi(fargs[1]);
	if (bot == 0)
		bot = 1;
	if (top < 0) {
		if (bot < 0)
			top = -((-top) % (-bot));
		else
			top = (bot - ((-top) % bot)) % bot;
	} else {
		if (bot < 0)
			top = -(((-bot) - (top % (-bot))) % (-bot));
		else
			top = top % bot;
	}
	safe_ltos(buff, bufc, top); 
}

FUNCTION(fun_remainder)
{
	int top, bot;

	/* The C % operator is only fully specified for non-negative
	 * operands, so we try not to give it negative operands here
	 */

	top = atoi(fargs[0]);
	bot = atoi(fargs[1]);
	if (bot == 0)
		bot = 1;
	if (top < 0) {
		if (bot < 0)
			top = -((-top) % (-bot));
		else
			top = -((-top) % bot);
	} else {
		if (bot < 0)
			top = top % (-bot);
		else
			top = top % bot;
	}
	safe_ltos(buff, bufc, top); 
}

FUNCTION(fun_power)
{
	NVAL val1, val2;

	val1 = aton(fargs[0]);
	val2 = aton(fargs[1]);
	if (val1 < 0) {
		safe_str("#-1 POWER OF NEGATIVE", buff, bufc);
	} else {
		fval(buff, bufc, pow((double)val1, (double)val2));
	}
}

FUNCTION(fun_log)
{
	NVAL val, base;

	VaChk_Range(1, 2);

	val = aton(fargs[0]);

	if (nfargs == 2)
	    base = aton(fargs[1]);
	else
	    base = 10;

	if ((val <= 0) || (base <= 0))
	    safe_str("#-1 LOG OF NEGATIVE OR ZERO", buff, bufc);
	else if (base == 1)
	    safe_str("#-1 DIVISION BY ZERO", buff, bufc);
	else
	    fval(buff, bufc, log((double)val) / log((double)base));
}

/* ------------------------------------------------------------------------
 * Bitwise two-argument integer math functions: SHL, SHR, BAND, BOR, BNAND
 */

FUNCTION(fun_shl)
{
	safe_ltos(buff, bufc, atoi(fargs[0]) << atoi(fargs[1]));
}

FUNCTION(fun_shr)
{
	safe_ltos(buff, bufc, atoi(fargs[0]) >> atoi(fargs[1]));
}

FUNCTION(fun_band)
{
	safe_ltos(buff, bufc, atoi(fargs[0]) & atoi(fargs[1]));
}

FUNCTION(fun_bor)
{
	safe_ltos(buff, bufc, atoi(fargs[0]) | atoi(fargs[1]));
}

FUNCTION(fun_bnand)
{
	safe_ltos(buff, bufc, atoi(fargs[0]) & ~(atoi(fargs[1])));
}

/* ---------------------------------------------------------------------------
 * Multi-argument math functions: ADD, MUL, MAX, MIN
 */

FUNCTION(fun_add)
{
	NVAL sum;
	int i;

	if (nfargs < 2) {
		safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
	} else {
		sum = aton(fargs[0]);
		for (i = 1; i < nfargs; i++) {
			sum += aton(fargs[i]);
		}
		fval(buff, bufc, sum);
	}
	return;
}

FUNCTION(fun_mul)
{
    NVAL prod;
    int i;

    if (nfargs < 2) {
	safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
    } else {
	prod = aton(fargs[0]);
	for (i = 1; i < nfargs; i++) {
	    prod *= aton(fargs[i]);
	}
	fval(buff, bufc, prod);
    }
    return;
}

FUNCTION(fun_max)
{
    int i;
    NVAL max, val;

    if (nfargs < 1) {
	safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
    } else {
	max = aton(fargs[0]);
	for (i = 1; i < nfargs; i++) {
	    val = aton(fargs[i]);
	    max = (max < val) ? val : max;
	}
	fval(buff, bufc, max);
    }
}

FUNCTION(fun_min)
{
    int i;
    NVAL min, val;

    if (nfargs < 1) {
	safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
    } else {
	min = aton(fargs[0]);
	for (i = 1; i < nfargs; i++) {
	    val = aton(fargs[i]);
	    min = (min > val) ? val : min;
	}
	fval(buff, bufc, min);
    }
}

/* ---------------------------------------------------------------------------
 * Integer point distance functions: DIST2D, DIST3D
 */

FUNCTION(fun_dist2d)
{
	int d;
	double r;

	d = atoi(fargs[0]) - atoi(fargs[2]);
	r = (double)(d * d);
	d = atoi(fargs[1]) - atoi(fargs[3]);
	r += (double)(d * d);
	d = (int)(sqrt(r) + 0.5);
	safe_ltos(buff, bufc, d);
}

FUNCTION(fun_dist3d)
{
	int d;
	double r;

	d = atoi(fargs[0]) - atoi(fargs[3]);
	r = (double)(d * d);
	d = atoi(fargs[1]) - atoi(fargs[4]);
	r += (double)(d * d);
	d = atoi(fargs[2]) - atoi(fargs[5]);
	r += (double)(d * d);
	d = (int)(sqrt(r) + 0.5);
	safe_ltos(buff, bufc, d);
}

/* ---------------------------------------------------------------------------
 * Math "accumulator" operations on a list: LADD, LMAX, LMIN
 */

FUNCTION(fun_ladd)
{
    NVAL sum;
    char *cp, *curr;
    Delim isep;

    if (nfargs == 0) {
	safe_chr('0', buff, bufc);
	return;
    }

    VaChk_Only_In(2);

    sum = 0;
    cp = trim_space_sep(fargs[0], &isep);
    while (cp) {
	curr = split_token(&cp, &isep);
	sum += aton(curr);
    }
    fval(buff, bufc, sum);
}

FUNCTION(fun_lmax)
{
    NVAL max, val;
    char *cp, *curr;
    Delim isep;

    VaChk_Only_In(2);

    cp = trim_space_sep(fargs[0], &isep);
    if (cp) {
	curr = split_token(&cp, &isep);
	max = aton(curr);
	while (cp) {
	    curr = split_token(&cp, &isep);
	    val = aton(curr);
	    if (max < val)
		max = val;
	}
	fval(buff, bufc, max);
    }
}

FUNCTION(fun_lmin)
{
    NVAL min, val;
    char *cp, *curr;
    Delim isep;

    VaChk_Only_In(2);

    cp = trim_space_sep(fargs[0], &isep);
    if (cp) {
	curr = split_token(&cp, &isep);
	min = aton(curr);
	while (cp) {
	    curr = split_token(&cp, &isep);
	    val = aton(curr);
	    if (min > val)
		min = val;
	}
	fval(buff, bufc, min);
    }
}

/* ---------------------------------------------------------------------------
 * Operations on a single vector: VMAG, VUNIT
 * (VDIM is implemented by fun_words)
 */

FUNCTION(handle_vector)
{
    char **v1;
    int n, i, oper;
    NVAL tmp, res = 0;
    Delim isep, osep;

    oper = Func_Mask(VEC_OPER);

    if (oper == VEC_UNIT) {
	VaChk_Only_In_Out(3);
    } else {
	VaChk_Only_In(2);
    }

    /* split the list up, or return if the list is empty */
    if (!fargs[0] || !*fargs[0]) {
	return;
    }
    n = list2arr(&v1, LBUF_SIZE, fargs[0], &isep);

    /* calculate the magnitude */
    for (i = 0; i < n; i++) {
	tmp = aton(v1[i]);
	res += tmp * tmp;
    }

    /* if we're just calculating the magnitude, return it */
    if (oper == VEC_MAG) {
	if (res > 0) {
	    fval(buff, bufc, sqrt(res));
	} else {
	    safe_chr('0', buff, bufc);
	}
	XFREE(v1, "handle_vector.v1");
	return;
    }

    if (res <= 0) {
	safe_str("#-1 CAN'T MAKE UNIT VECTOR FROM ZERO-LENGTH VECTOR",
		 buff, bufc);
	XFREE(v1, "handle_vector.v1");
	return;
    }
    res = sqrt(res);
    fval(buff, bufc, aton(v1[0]) / res);
    for (i = 1; i < n; i++) {
	print_sep(&osep, buff, bufc);
	fval(buff, bufc, aton(v1[i]) / res);
    }
    XFREE(v1, "handle_vector.v1");
}

/* ---------------------------------------------------------------------------
 * Operations on a pair of vectors: VADD, VSUB, VMUL, VDOT
 */

FUNCTION(handle_vectors)
{
    Delim isep, osep;
    int oper;
    char **v1, **v2;
    NVAL scalar;
    int n, m, i;

    oper = Func_Mask(VEC_OPER);

    if (oper != VEC_DOT) {
	VaChk_Only_In_Out(4);
    } else {
	/* dot product returns a scalar, so no output delim */
	VaChk_Only_In(3);
    }

    /*
     * split the list up, or return if the list is empty 
     */
    if (!fargs[0] || !*fargs[0] || !fargs[1] || !*fargs[1]) {
	return;
    }
    n = list2arr(&v1, LBUF_SIZE, fargs[0], &isep);
    m = list2arr(&v2, LBUF_SIZE, fargs[1], &isep);

    /* It's okay to have vmul() be passed a scalar first or second arg,
     * but everything else has to be same-dimensional.
     */
    if ((n != m) &&
	!((oper == VEC_MUL) && ((n == 1) || (m == 1)))) {
	safe_str("#-1 VECTORS MUST BE SAME DIMENSIONS", buff, bufc);
	XFREE(v1, "handle_vectors.v1");
	XFREE(v2, "handle_vectors.v2");
	return;
    }

    switch (oper) {
	case VEC_ADD:
	    fval(buff, bufc, aton(v1[0]) + aton(v2[0]));
	    for (i = 1; i < n; i++) {
		print_sep(&osep, buff, bufc);
		fval(buff, bufc, aton(v1[i]) + aton(v2[i]));
	    }
	    break;
	case VEC_SUB:
	    fval(buff, bufc, aton(v1[0]) - aton(v2[0]));
	    for (i = 1; i < n; i++) {
		print_sep(&osep, buff, bufc);
		fval(buff, bufc, aton(v1[i]) - aton(v2[i]));
	    }
	    break;
	case VEC_MUL:
	    /* if n or m is 1, this is scalar multiplication.
	     * otherwise, multiply elementwise.
	     */
	    if (n == 1) {
		scalar = aton(v1[0]);
		fval(buff, bufc, aton(v2[0]) * scalar);
		for (i = 1; i < m; i++) {
		    print_sep(&osep, buff, bufc);
		    fval(buff, bufc, aton(v2[i]) * scalar);
		}
	    } else if (m == 1) {
		scalar = aton(v2[0]);
		fval(buff, bufc, aton(v1[0]) * scalar);
		for (i = 1; i < n; i++) {
		    print_sep(&osep, buff, bufc);
		    fval(buff, bufc, aton(v1[i]) * scalar);
		}
	    } else {
		/* vector elementwise product.
		 *
		 * Note this is a departure from TinyMUX, but an imitation
		 * of the PennMUSH behavior: the documentation in Penn
		 * claims it's a dot product, but the actual behavior
		 * isn't. We implement dot product separately!
		 */
		fval(buff, bufc, aton(v1[0]) * aton(v2[0]));
		for (i = 1; i < n; i++) {
		    print_sep(&osep, buff, bufc);
		    fval(buff, bufc, aton(v1[i]) * aton(v2[i]));
		}
	    }
	    break;
	case VEC_DOT:
	    /* dot product: (a,b,c) . (d,e,f) = ad + be + cf
	     * 
	     * no cross product implementation yet: it would be
	     * (a,b,c) x (d,e,f) = (bf - ce, cd - af, ae - bd)
	     */
	    scalar = 0;
	    for (i = 0; i < n; i++) {
		scalar += aton(v1[i]) * aton(v2[i]);
	    }
	    fval(buff, bufc, scalar);
	    break;
	default:
	    /* If we reached this, we're in trouble. */
	    safe_str("#-1 UNIMPLEMENTED", buff, bufc);
	    break;
    }
    XFREE(v1, "handle_vectors.v1");
    XFREE(v2, "handle_vectors.v2");
}

/* ---------------------------------------------------------------------------
 * Simple boolean funcs: NOT, NOTBOOL, T
 */

FUNCTION(fun_not)
{
	safe_bool(buff, bufc, !atoi(fargs[0]));
}

FUNCTION(fun_notbool)
{
	safe_bool(buff, bufc, !xlate(fargs[0]));
}

FUNCTION(fun_t)
{
	safe_bool(buff, bufc, xlate(fargs[0]));
}

/* ---------------------------------------------------------------------------
 * Multi-argument boolean funcs: various combinations of
 *    [L,C][AND,OR,XOR][BOOL]
 */

FUNCTION(handle_logic)
{
	Delim isep;
	int flag, oper, i, val;
	char *str, *tbuf, *bp;
	int (*cvtfun)(char *);

	flag = Func_Flags(fargs);
	cvtfun = (flag & LOGIC_BOOL) ? xlate : (int (*)(char *))atoi;
	oper = (flag & LOGIC_OPER);

	/* most logic operations on an empty string should be false */
	val = 0;

	if (flag & LOGIC_LIST) {
		/* the arguments come in a pre-evaluated list */
		VaChk_Only_In(2);

		bp = trim_space_sep(fargs[0], &isep);
		while (bp) {
	  		tbuf = split_token(&bp, &isep);
			val = ((oper == LOGIC_XOR) && val) ? !cvtfun(tbuf) : cvtfun(tbuf);
			if (((oper == LOGIC_AND) && !val) ||
			    ((oper == LOGIC_OR) && val))
				break;
		}

	} else if (nfargs < 2) {
		/* separate arguments, but not enough of them */
		safe_known_str("#-1 TOO FEW ARGUMENTS", 21, buff, bufc);
		return;

	} else if (flag & FN_NO_EVAL) {
		/* separate, unevaluated arguments */
		tbuf = alloc_lbuf("handle_logic");
		for (i = 0; i < nfargs; i++) { 
			str = fargs[i];
			bp = tbuf;
			exec(tbuf, &bp, player, caller, cause,
			     EV_EVAL | EV_STRIP | EV_FCHECK, &str, cargs, ncargs);
			val = ((oper == LOGIC_XOR) && val) ? !cvtfun(tbuf) : cvtfun(tbuf);
			if (((oper == LOGIC_AND) && !val) ||
			    ((oper == LOGIC_OR) && val))
				break;
		}
		free_lbuf(tbuf);

	} else {
		/* separate, pre-evaluated arguments */
		for (i = 0; i < nfargs; i++) { 
			val = ((oper == LOGIC_XOR) && val) ? !cvtfun(fargs[i]) : cvtfun(fargs[i]);
			if (((oper == LOGIC_AND) && !val) ||
			    ((oper == LOGIC_OR) && val))
				break;
		}
	}

	safe_bool(buff, bufc, val);
}